Merge "Add reference ivn HAL impl." into udc-dev

This commit is contained in:
Treehugger Robot 2023-04-26 02:38:51 +00:00 committed by Android (Google) Code Review
commit 603de4b9cf
10 changed files with 640 additions and 0 deletions

View file

@ -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"],
}

View file

@ -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,
}

View file

@ -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"
}

View file

@ -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 <aidl/android/hardware/automotive/ivn/BnIvnAndroidDevice.h>
#include <aidl/android/hardware/automotive/ivn/EndpointInfo.h>
#include <aidl/android/hardware/automotive/ivn/OccupantZoneInfo.h>
#include <android/binder_auto_utils.h>
#include <vector>
#include <unordered_map>
namespace android {
namespace hardware {
namespace automotive {
namespace ivn {
struct DeviceInfo {
std::vector<aidl::android::hardware::automotive::ivn::OccupantZoneInfo> 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<int>* deviceIds) override;
ndk::ScopedAStatus getDeviceIdForOccupantZone(int zoneId, int* deviceId) override;
ndk::ScopedAStatus getOccupantZonesForDevice(
int androidDeviceId,
std::vector<aidl::android::hardware::automotive::ivn::OccupantZoneInfo>* 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<int, DeviceInfo> mDeviceInfoById;
std::string_view mConfigPath;
};
} // namespace ivn
} // namespace automotive
} // namespace hardware
} // namespace android

View file

@ -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

View file

@ -0,0 +1,7 @@
<manifest version="1.0" type="device">
<hal format="aidl">
<name>android.hardware.automotive.ivn</name>
<version>1</version>
<fqname>IIvnAndroidDevice/default</fqname>
</hal>
</manifest>

View file

@ -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 <android-base/logging.h>
#include <android/binder_manager.h>
#include <android/binder_process.h>
#include <stdlib.h>
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<android::hardware::automotive::ivn::IvnAndroidDeviceService>(
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;
}

View file

@ -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 <aidl/android/hardware/automotive/ivn/ConnectProtocol.h>
#include <aidl/android/hardware/automotive/ivn/HardwareIdentifiers.h>
#include <aidl/android/hardware/automotive/ivn/OccupantType.h>
#include <android-base/logging.h>
#include <android/binder_status.h>
#include <json/json.h>
#include <fstream>
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<int>* 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<OccupantZoneInfo>* 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

View file

@ -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"],
}

View file

@ -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 <aidl/android/hardware/automotive/ivn/OccupantType.h>
#include <aidl/android/hardware/automotive/ivn/OccupantZoneInfo.h>
#include <android-base/file.h>
#include <gtest/gtest.h>
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<IvnAndroidDeviceService>(
android::base::GetExecutableDirectory() + "/DefaultConfig.json");
mService->init();
}
std::shared_ptr<IvnAndroidDeviceService> 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<int> deviceIds;
ScopedAStatus status = mService->getOtherDeviceIds(&deviceIds);
ASSERT_TRUE(status.isOk());
ASSERT_EQ(deviceIds, std::vector<int>({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<OccupantZoneInfo> 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