From a45f94d60d688e02bc9fa5ed416e944dbe8f0979 Mon Sep 17 00:00:00 2001 From: Yu Shan Date: Mon, 10 Apr 2023 12:05:57 -0700 Subject: [PATCH] Add reference ivn HAL impl. Add reference ivn HAL impl. The reference implementation reads a JSON config file and provides static information. Test: atest IvnAndroidDeviceServiceUnitTest Bug: 274139217 Change-Id: Idc55d3ac8573bd1eeff096c4f12d8bfd935fb859 --- .../impl/default/Android.bp | 58 ++++++ .../impl/default/config/Android.bp | 32 +++ .../impl/default/config/DefaultConfig.json | 61 ++++++ .../default/include/IvnAndroidDeviceService.h | 72 +++++++ .../impl/default/ivn-default-service.rc | 4 + .../impl/default/ivn-default-service.xml | 7 + .../impl/default/src/IvnAndroidDeviceImpl.cpp | 58 ++++++ .../default/src/IvnAndroidDeviceService.cpp | 196 ++++++++++++++++++ .../impl/default/test/Android.bp | 42 ++++ .../test/IvnAndroidDeviceServiceUnittest.cpp | 110 ++++++++++ 10 files changed, 640 insertions(+) create mode 100644 automotive/ivn_android_device/impl/default/Android.bp create mode 100644 automotive/ivn_android_device/impl/default/config/Android.bp create mode 100644 automotive/ivn_android_device/impl/default/config/DefaultConfig.json create mode 100644 automotive/ivn_android_device/impl/default/include/IvnAndroidDeviceService.h create mode 100644 automotive/ivn_android_device/impl/default/ivn-default-service.rc create mode 100644 automotive/ivn_android_device/impl/default/ivn-default-service.xml create mode 100644 automotive/ivn_android_device/impl/default/src/IvnAndroidDeviceImpl.cpp create mode 100644 automotive/ivn_android_device/impl/default/src/IvnAndroidDeviceService.cpp create mode 100644 automotive/ivn_android_device/impl/default/test/Android.bp create mode 100644 automotive/ivn_android_device/impl/default/test/IvnAndroidDeviceServiceUnittest.cpp diff --git a/automotive/ivn_android_device/impl/default/Android.bp b/automotive/ivn_android_device/impl/default/Android.bp new file mode 100644 index 0000000000..98c1f58ee5 --- /dev/null +++ b/automotive/ivn_android_device/impl/default/Android.bp @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +cc_library { + name: "IvnAndroidDeviceService", + vendor_available: true, + local_include_dirs: ["include"], + export_include_dirs: ["include"], + srcs: [ + "src/IvnAndroidDeviceService.cpp", + ], + whole_static_libs: [ + "android.hardware.automotive.ivn-V1-ndk", + ], + shared_libs: [ + "libbase", + "libbinder_ndk", + "libjsoncpp", + "liblog", + "libutils", + ], +} + +cc_binary { + name: "android.hardware.automotive.ivn@V1-default-service", + vendor: true, + relative_install_path: "hw", + local_include_dirs: ["include"], + srcs: ["src/IvnAndroidDeviceImpl.cpp"], + whole_static_libs: ["IvnAndroidDeviceService"], + shared_libs: [ + "libbase", + "libbinder_ndk", + "libjsoncpp", + "liblog", + "libutils", + ], + required: ["Prebuilt_IvnAndroidDeviceServiceDefaultConfig_JSON"], + vintf_fragments: ["ivn-default-service.xml"], + init_rc: ["ivn-default-service.rc"], +} diff --git a/automotive/ivn_android_device/impl/default/config/Android.bp b/automotive/ivn_android_device/impl/default/config/Android.bp new file mode 100644 index 0000000000..03c4d1b215 --- /dev/null +++ b/automotive/ivn_android_device/impl/default/config/Android.bp @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +filegroup { + name: "IvnAndroidDeviceServiceDefaultConfig_Json", + srcs: ["DefaultConfig.json"], +} + +prebuilt_etc { + name: "Prebuilt_IvnAndroidDeviceServiceDefaultConfig_JSON", + filename_from_src: true, + src: "DefaultConfig.json", + sub_dir: "automotive/IvnConfig/", + vendor: true, +} diff --git a/automotive/ivn_android_device/impl/default/config/DefaultConfig.json b/automotive/ivn_android_device/impl/default/config/DefaultConfig.json new file mode 100644 index 0000000000..45663e2645 --- /dev/null +++ b/automotive/ivn_android_device/impl/default/config/DefaultConfig.json @@ -0,0 +1,61 @@ +{ + "MyDeviceId": 0, + "Devices": [ + { + "DeviceId": 0, + "OccupantZones": [ + { + "ZoneId": 0, + "OccupantType": "DRIVER", + "Seat": 1, + "Comments": "Occupant zone for driver and FRONT_LEFT seat" + }, + { + "ZoneId": 1, + "OccupantType": "FRONT_PASSENGER", + "Seat": 4, + "Comments": "Occupant zone for FRONT_RIGHT passenger" + } + ], + "EndpointInfo": { + "IpAddress": "10.10.10.1", + "PortNumber": 1234, + "BrandName": "MyBrand", + "DeviceName": "MyDevice", + "ProductName": "MyProduct", + "ManufacturerName": "MyCompany", + "ModelName": "MyModel", + "SerialNumber": "Serial1234" + }, + "Comments": "Device for front row" + }, + { + "DeviceId": 1, + "OccupantZones": [ + { + "ZoneId": 2, + "OccupantType": "REAR_PASSENGER", + "Seat": 16 + }, + { + "ZoneId": 3, + "OccupantType": "REAR_PASSENGER", + "Seat": 64 + } + ], + "EndpointInfo": { + "IpAddress": "10.10.10.2", + "PortNumber": 2345, + "BrandName": "MyBrand", + "DeviceName": "MyDevice", + "ProductName": "MyProduct", + "ManufacturerName": "MyCompany", + "ModelName": "MyModel", + "SerialNumber": "Serial2345" + }, + "Comments": "Device for back row" + } + ], + "Comment": + "This simulates a vehicle with two Android devices, one for front row, one for back row" +} diff --git a/automotive/ivn_android_device/impl/default/include/IvnAndroidDeviceService.h b/automotive/ivn_android_device/impl/default/include/IvnAndroidDeviceService.h new file mode 100644 index 0000000000..c0cc9fea2d --- /dev/null +++ b/automotive/ivn_android_device/impl/default/include/IvnAndroidDeviceService.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include + +namespace android { +namespace hardware { +namespace automotive { +namespace ivn { + +struct DeviceInfo { + std::vector occupantZones; + aidl::android::hardware::automotive::ivn::EndpointInfo endpointInfo; +}; + +class IvnAndroidDeviceService + : public aidl::android::hardware::automotive::ivn::BnIvnAndroidDevice { + public: + explicit IvnAndroidDeviceService(std::string_view configPath); + + // Initialize the service, returns true on success. + bool init(); + + ndk::ScopedAStatus getMyDeviceId(int* deviceId) override; + + ndk::ScopedAStatus getOtherDeviceIds(std::vector* deviceIds) override; + + ndk::ScopedAStatus getDeviceIdForOccupantZone(int zoneId, int* deviceId) override; + + ndk::ScopedAStatus getOccupantZonesForDevice( + int androidDeviceId, + std::vector* occupantZones) + override; + + ndk::ScopedAStatus getMyEndpointInfo( + aidl::android::hardware::automotive::ivn::EndpointInfo* endpointInfo) override; + + ndk::ScopedAStatus getEndpointInfoForDevice( + int androidDeviceId, + aidl::android::hardware::automotive::ivn::EndpointInfo* endpointInfo) override; + + private: + int mMyDeviceId; + std::unordered_map mDeviceInfoById; + std::string_view mConfigPath; +}; + +} // namespace ivn +} // namespace automotive +} // namespace hardware +} // namespace android diff --git a/automotive/ivn_android_device/impl/default/ivn-default-service.rc b/automotive/ivn_android_device/impl/default/ivn-default-service.rc new file mode 100644 index 0000000000..070b2591be --- /dev/null +++ b/automotive/ivn_android_device/impl/default/ivn-default-service.rc @@ -0,0 +1,4 @@ +service vendor.ivn-default /vendor/bin/hw/android.hardware.automotive.ivn@V1-default-service + class hal + user vehicle_network + group system inet diff --git a/automotive/ivn_android_device/impl/default/ivn-default-service.xml b/automotive/ivn_android_device/impl/default/ivn-default-service.xml new file mode 100644 index 0000000000..481bc0a08f --- /dev/null +++ b/automotive/ivn_android_device/impl/default/ivn-default-service.xml @@ -0,0 +1,7 @@ + + + android.hardware.automotive.ivn + 1 + IIvnAndroidDevice/default + + diff --git a/automotive/ivn_android_device/impl/default/src/IvnAndroidDeviceImpl.cpp b/automotive/ivn_android_device/impl/default/src/IvnAndroidDeviceImpl.cpp new file mode 100644 index 0000000000..fdf677661a --- /dev/null +++ b/automotive/ivn_android_device/impl/default/src/IvnAndroidDeviceImpl.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "IvnAndroidDeviceImpl" + +#include "IvnAndroidDeviceService.h" + +#include +#include +#include +#include + +constexpr char SERVICE_NAME[] = "android.hardware.automotive.ivn.IIvnAndroidDevice/default"; +constexpr char DEFAULT_CONFIG_DIR[] = "/vendor/etc/automotive/IvnConfig/DefaultConfig.json"; + +int main(int /* argc */, char* /* argv */[]) { + LOG(INFO) << "Registering IvnAndroidDeviceService as service..."; + auto service = + ndk::SharedRefBase::make( + DEFAULT_CONFIG_DIR); + if (!service->init()) { + LOG(ERROR) << "Failed to init IvnAndroidDeviceService"; + exit(1); + } + + binder_exception_t err = AServiceManager_addService(service->asBinder().get(), SERVICE_NAME); + if (err != EX_NONE) { + LOG(ERROR) << "Failed to register IvnAndroidDeviceService service, exception: " << err; + exit(1); + } + + if (!ABinderProcess_setThreadPoolMaxThreadCount(1)) { + LOG(ERROR) << "Failed to set thread pool max thread count"; + exit(1); + } + ABinderProcess_startThreadPool(); + + LOG(INFO) << "IvnAndroidDeviceService Ready"; + + ABinderProcess_joinThreadPool(); + + LOG(ERROR) << "IvnAndroidDeviceService init failed! Should not reach here"; + + return 0; +} diff --git a/automotive/ivn_android_device/impl/default/src/IvnAndroidDeviceService.cpp b/automotive/ivn_android_device/impl/default/src/IvnAndroidDeviceService.cpp new file mode 100644 index 0000000000..71454d5d40 --- /dev/null +++ b/automotive/ivn_android_device/impl/default/src/IvnAndroidDeviceService.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "IvnAndroidDeviceService.h" + +#include +#include +#include +#include +#include +#include + +#include + +namespace android { +namespace hardware { +namespace automotive { +namespace ivn { + +namespace { + +using ::aidl::android::hardware::automotive::ivn::ConnectProtocol; +using ::aidl::android::hardware::automotive::ivn::EndpointInfo; +using ::aidl::android::hardware::automotive::ivn::HardwareIdentifiers; +using ::aidl::android::hardware::automotive::ivn::OccupantType; +using ::aidl::android::hardware::automotive::ivn::OccupantZoneInfo; +using ::ndk::ScopedAStatus; + +constexpr int IVN_ERROR_GENERIC = -1; + +} // namespace + +IvnAndroidDeviceService::IvnAndroidDeviceService(std::string_view configPath) { + mConfigPath = configPath; +} + +bool IvnAndroidDeviceService::init() { + std::ifstream configStream(mConfigPath); + if (!configStream) { + LOG(ERROR) << "couldn't open " << mConfigPath << " for parsing."; + return false; + } + Json::CharReaderBuilder builder; + Json::Value root; + std::string errs; + if (!Json::parseFromStream(builder, configStream, &root, &errs)) { + LOG(ERROR) << "Failed to parse config JSON stream, error: " << errs; + return false; + } + if (!root.isObject()) { + LOG(ERROR) << "Root must be an object"; + return false; + } + if (!root.isMember("MyDeviceId")) { + LOG(ERROR) << "Must contain 'MyDeviceId' field"; + return false; + } + mMyDeviceId = root["MyDeviceId"].asInt(); + if (!root.isMember("Devices") || !root["Devices"].isArray()) { + LOG(ERROR) << "Must contain 'Devices' field as array"; + return false; + } + Json::Value& devices = root["Devices"]; + for (unsigned int i = 0; i < devices.size(); i++) { + Json::Value& device = devices[i]; + int deviceId = device["DeviceId"].asInt(); + DeviceInfo deviceInfo = {}; + Json::Value& occupantZones = device["OccupantZones"]; + for (unsigned int j = 0; j < occupantZones.size(); j++) { + Json::Value& occupantZone = occupantZones[j]; + int zoneId = occupantZone["ZoneId"].asInt(); + std::string occupantTypeStr = occupantZone["OccupantType"].asString(); + int seat = occupantZone["Seat"].asInt(); + OccupantType occupantType; + if (occupantTypeStr == "DRIVER") { + occupantType = OccupantType::DRIVER; + } else if (occupantTypeStr == "FRONT_PASSENGER") { + occupantType = OccupantType::FRONT_PASSENGER; + } else if (occupantTypeStr == "REAR_PASSENGER") { + occupantType = OccupantType::REAR_PASSENGER; + } else { + LOG(ERROR) << "Unknown occupant type: " << occupantTypeStr; + return false; + } + OccupantZoneInfo occupantZoneInfo = { + .zoneId = zoneId, .occupantType = occupantType, .seat = seat}; + deviceInfo.occupantZones.push_back(std::move(occupantZoneInfo)); + } + Json::Value& ep = device["EndpointInfo"]; + EndpointInfo endpointInfo = {}; + endpointInfo.connectProtocol = ConnectProtocol::TCP_IP; + endpointInfo.ipAddress = ep["IpAddress"].asString(); + endpointInfo.portNumber = ep["PortNumber"].asInt(); + HardwareIdentifiers hardwareId = {}; + if (ep.isMember("BrandName")) { + hardwareId.brandName = ep["BrandName"].asString(); + } + if (ep.isMember("DeviceName")) { + hardwareId.deviceName = ep["DeviceName"].asString(); + } + if (ep.isMember("ProductName")) { + hardwareId.productName = ep["ProductName"].asString(); + } + if (ep.isMember("ManufacturerName")) { + hardwareId.manufacturerName = ep["ManufacturerName"].asString(); + } + if (ep.isMember("ModelName")) { + hardwareId.modelName = ep["ModelName"].asString(); + } + if (ep.isMember("SerialNumber")) { + hardwareId.serialNumber = ep["SerialNumber"].asString(); + } + endpointInfo.hardwareId = hardwareId; + deviceInfo.endpointInfo = endpointInfo; + mDeviceInfoById[deviceId] = deviceInfo; + } + if (mDeviceInfoById.find(mMyDeviceId) == mDeviceInfoById.end()) { + LOG(ERROR) << "My device ID is not in the device info list"; + return false; + } + return true; +} + +ScopedAStatus IvnAndroidDeviceService::getMyDeviceId(int* deviceId) { + *deviceId = mMyDeviceId; + return ScopedAStatus::ok(); +} + +ScopedAStatus IvnAndroidDeviceService::getOtherDeviceIds(std::vector* deviceIds) { + deviceIds->clear(); + for (const auto& [deviceId, _] : mDeviceInfoById) { + if (deviceId == mMyDeviceId) { + continue; + } + deviceIds->push_back(deviceId); + } + return ScopedAStatus::ok(); +} + +ScopedAStatus IvnAndroidDeviceService::getDeviceIdForOccupantZone(int zoneId, int* outDeviceId) { + for (const auto& [deviceId, deviceInfo] : mDeviceInfoById) { + for (const auto& occupantZoneInfo : deviceInfo.occupantZones) { + if (occupantZoneInfo.zoneId == zoneId) { + *outDeviceId = deviceId; + return ScopedAStatus::ok(); + } + } + } + return ScopedAStatus::fromServiceSpecificErrorWithMessage(IVN_ERROR_GENERIC, + "Occupant zone not found"); +} + +ScopedAStatus IvnAndroidDeviceService::getOccupantZonesForDevice( + int androidDeviceId, std::vector* occupantZones) { + if (mDeviceInfoById.find(androidDeviceId) == mDeviceInfoById.end()) { + return ScopedAStatus::fromServiceSpecificErrorWithMessage(IVN_ERROR_GENERIC, + "Android device ID not found"); + } + for (const auto& occupantZoneInfo : mDeviceInfoById[androidDeviceId].occupantZones) { + occupantZones->push_back(occupantZoneInfo); + } + return ScopedAStatus::ok(); +} + +ScopedAStatus IvnAndroidDeviceService::getMyEndpointInfo(EndpointInfo* endpointInfo) { + *endpointInfo = mDeviceInfoById[mMyDeviceId].endpointInfo; + return ScopedAStatus::ok(); +} + +ScopedAStatus IvnAndroidDeviceService::getEndpointInfoForDevice(int androidDeviceId, + EndpointInfo* endpointInfo) { + if (mDeviceInfoById.find(androidDeviceId) == mDeviceInfoById.end()) { + return ScopedAStatus::fromServiceSpecificErrorWithMessage(IVN_ERROR_GENERIC, + "Android device ID not found"); + } + *endpointInfo = mDeviceInfoById[androidDeviceId].endpointInfo; + return ScopedAStatus::ok(); +} + +} // namespace ivn +} // namespace automotive +} // namespace hardware +} // namespace android diff --git a/automotive/ivn_android_device/impl/default/test/Android.bp b/automotive/ivn_android_device/impl/default/test/Android.bp new file mode 100644 index 0000000000..a100575de3 --- /dev/null +++ b/automotive/ivn_android_device/impl/default/test/Android.bp @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +cc_test { + name: "IvnAndroidDeviceServiceUnitTest", + vendor: true, + srcs: ["*.cpp"], + whole_static_libs: [ + "IvnAndroidDeviceService", + ], + shared_libs: [ + "libbase", + "libbinder_ndk", + "libjsoncpp", + "liblog", + "libutils", + ], + static_libs: [ + "libgtest", + ], + data: [ + ":IvnAndroidDeviceServiceDefaultConfig_Json", + ], + test_suites: ["device-tests"], +} diff --git a/automotive/ivn_android_device/impl/default/test/IvnAndroidDeviceServiceUnittest.cpp b/automotive/ivn_android_device/impl/default/test/IvnAndroidDeviceServiceUnittest.cpp new file mode 100644 index 0000000000..bc8e69fd8f --- /dev/null +++ b/automotive/ivn_android_device/impl/default/test/IvnAndroidDeviceServiceUnittest.cpp @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "IvnAndroidDeviceService.h" + +#include +#include +#include +#include + +namespace android { +namespace hardware { +namespace automotive { +namespace ivn { + +using ::aidl::android::hardware::automotive::ivn::OccupantType; +using ::aidl::android::hardware::automotive::ivn::OccupantZoneInfo; +using ::ndk::ScopedAStatus; + +class IvnAndroidDeviceServiceUnitTest : public ::testing::Test { + public: + virtual void SetUp() override { + mService = ndk::SharedRefBase::make( + android::base::GetExecutableDirectory() + "/DefaultConfig.json"); + mService->init(); + } + + std::shared_ptr mService; +}; + +TEST_F(IvnAndroidDeviceServiceUnitTest, TestGetMyDeviceId) { + int deviceId = -1; + + ScopedAStatus status = mService->getMyDeviceId(&deviceId); + + ASSERT_TRUE(status.isOk()); + ASSERT_EQ(deviceId, 0); +} + +TEST_F(IvnAndroidDeviceServiceUnitTest, TestGetOtherDeviceIds) { + std::vector deviceIds; + + ScopedAStatus status = mService->getOtherDeviceIds(&deviceIds); + + ASSERT_TRUE(status.isOk()); + ASSERT_EQ(deviceIds, std::vector({1})); +} + +TEST_F(IvnAndroidDeviceServiceUnitTest, TestGetDeviceIdForOccupantZone) { + int deviceId = -1; + + ScopedAStatus status = mService->getDeviceIdForOccupantZone(/*zoneId=*/0, &deviceId); + + ASSERT_TRUE(status.isOk()); + EXPECT_EQ(deviceId, 0); + + status = mService->getDeviceIdForOccupantZone(/*zoneId=*/1, &deviceId); + + ASSERT_TRUE(status.isOk()); + EXPECT_EQ(deviceId, 0); + + status = mService->getDeviceIdForOccupantZone(/*zoneId=*/2, &deviceId); + + ASSERT_TRUE(status.isOk()); + EXPECT_EQ(deviceId, 1); + + status = mService->getDeviceIdForOccupantZone(/*zoneId=*/3, &deviceId); + + ASSERT_TRUE(status.isOk()); + EXPECT_EQ(deviceId, 1); + + status = mService->getDeviceIdForOccupantZone(/*zoneId=*/4, &deviceId); + + ASSERT_FALSE(status.isOk()); +} + +TEST_F(IvnAndroidDeviceServiceUnitTest, TestGetOccupantZonesForDevice) { + std::vector occupantZones; + + ScopedAStatus status = + mService->getOccupantZonesForDevice(/*androidDeviceId=*/0, &occupantZones); + ASSERT_TRUE(status.isOk()); + EXPECT_EQ(occupantZones.size(), 2); + if (occupantZones.size() == 2) { + EXPECT_EQ(occupantZones[0].zoneId, 0); + EXPECT_EQ(occupantZones[0].occupantType, OccupantType::DRIVER); + EXPECT_EQ(occupantZones[0].seat, 1); + EXPECT_EQ(occupantZones[1].zoneId, 1); + EXPECT_EQ(occupantZones[1].occupantType, OccupantType::FRONT_PASSENGER); + EXPECT_EQ(occupantZones[1].seat, 4); + } +} + +} // namespace ivn +} // namespace automotive +} // namespace hardware +} // namespace android