mirror of
https://github.com/Evolution-X/hardware_interfaces
synced 2026-02-02 20:24:19 +00:00
Merge changes from topic "default_vhal_emulated_user"
* changes: Move emulatedUserHal to default VHAL. Add fake OBD2 frame logic to default VHAL. Support vendor override in default VHAL.
This commit is contained in:
committed by
Android (Google) Code Review
commit
34422ab4b7
@@ -92,10 +92,12 @@ cc_library_static {
|
||||
"impl/vhal_v2_0/LinearFakeValueGenerator.cpp",
|
||||
"impl/vhal_v2_0/JsonFakeValueGenerator.cpp",
|
||||
"impl/vhal_v2_0/GeneratorHub.cpp",
|
||||
"impl/vhal_v2_0/FakeObd2Frame.cpp",
|
||||
],
|
||||
local_include_dirs: ["common/include/vhal_v2_0"],
|
||||
export_include_dirs: ["impl"],
|
||||
whole_static_libs: [
|
||||
"android.hardware.automotive.vehicle@2.0-emulated-user-hal-lib",
|
||||
"android.hardware.automotive.vehicle@2.0-manager-lib",
|
||||
],
|
||||
shared_libs: [
|
||||
@@ -141,10 +143,10 @@ cc_library_static {
|
||||
"impl/vhal_v2_0/JsonFakeValueGenerator.cpp",
|
||||
"impl/vhal_v2_0/LinearFakeValueGenerator.cpp",
|
||||
"impl/vhal_v2_0/DefaultVehicleHalServer.cpp",
|
||||
"impl/vhal_v2_0/FakeObd2Frame.cpp",
|
||||
],
|
||||
whole_static_libs: [
|
||||
"android.hardware.automotive.vehicle@2.0-server-common-lib",
|
||||
"android.hardware.automotive.vehicle@2.0-user-hal-helper-lib",
|
||||
],
|
||||
static_libs: [
|
||||
"android.hardware.automotive.vehicle@2.0-libproto-native",
|
||||
@@ -213,7 +215,10 @@ cc_test {
|
||||
"android.hardware.automotive.vehicle@2.0-default-impl-lib",
|
||||
"android.hardware.automotive.vehicle@2.0-libproto-native",
|
||||
],
|
||||
data: [":vhal_test_json"],
|
||||
data: [
|
||||
":vhal_test_json",
|
||||
":vhal_test_override_json",
|
||||
],
|
||||
test_suites: ["general-tests"],
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <EmulatedUserHal.h>
|
||||
#include <EmulatedVehicleConnector.h>
|
||||
#include <EmulatedVehicleHal.h>
|
||||
#include <vhal_v2_0/VehicleHalManager.h>
|
||||
@@ -32,8 +31,7 @@ using namespace android::hardware::automotive::vehicle::V2_0;
|
||||
int main(int /* argc */, char* /* argv */ []) {
|
||||
auto store = std::make_unique<VehiclePropertyStore>();
|
||||
auto connector = std::make_unique<impl::EmulatedVehicleConnector>();
|
||||
auto userHal = connector->getEmulatedUserHal();
|
||||
auto hal = std::make_unique<impl::EmulatedVehicleHal>(store.get(), connector.get(), userHal);
|
||||
auto hal = std::make_unique<impl::EmulatedVehicleHal>(store.get(), connector.get());
|
||||
auto emulator = connector->getEmulator();
|
||||
auto service = std::make_unique<VehicleHalManager>(hal.get());
|
||||
connector->setValuePool(hal->getValuePool());
|
||||
|
||||
@@ -17,10 +17,14 @@
|
||||
|
||||
#include <android-base/chrono_utils.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/SystemClock.h>
|
||||
#include <vhal_v2_0/RecurrentTimer.h>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "FakeObd2Frame.h"
|
||||
#include "PropertyUtils.h"
|
||||
#include "VehicleUtils.h"
|
||||
|
||||
#include "DefaultVehicleHal.h"
|
||||
@@ -52,8 +56,30 @@ const VehicleAreaConfig* getAreaConfig(const VehiclePropValue& propValue,
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
VehicleHal::VehiclePropValuePtr addTimestamp(VehicleHal::VehiclePropValuePtr v) {
|
||||
if (v.get()) {
|
||||
v->timestamp = elapsedRealtimeNano();
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
bool isDebugProperty(int propId) {
|
||||
return (propId == kGenerateFakeDataControllingProperty ||
|
||||
propId == kSetIntPropertyFromVehicleForTest ||
|
||||
propId == kSetFloatPropertyFromVehicleForTest ||
|
||||
propId == kSetBooleanPropertyFromVehicleForTest);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
VehicleHal::VehiclePropValuePtr DefaultVehicleHal::createVhalHeartBeatProp() {
|
||||
VehicleHal::VehiclePropValuePtr v = getValuePool()->obtainInt64(uptimeMillis());
|
||||
v->prop = static_cast<int32_t>(VehicleProperty::VHAL_HEARTBEAT);
|
||||
v->areaId = 0;
|
||||
v->status = VehiclePropertyStatus::AVAILABLE;
|
||||
return v;
|
||||
}
|
||||
|
||||
DefaultVehicleHal::DefaultVehicleHal(VehiclePropertyStore* propStore, VehicleHalClient* client)
|
||||
: mPropStore(propStore), mRecurrentTimer(getTimerAction()), mVehicleClient(client) {
|
||||
initStaticConfig();
|
||||
@@ -63,12 +89,51 @@ DefaultVehicleHal::DefaultVehicleHal(VehiclePropertyStore* propStore, VehicleHal
|
||||
});
|
||||
}
|
||||
|
||||
VehicleHal::VehiclePropValuePtr DefaultVehicleHal::getUserHalProp(
|
||||
const VehiclePropValue& requestedPropValue, StatusCode* outStatus) {
|
||||
auto propId = requestedPropValue.prop;
|
||||
ALOGI("get(): getting value for prop %d from User HAL", propId);
|
||||
const auto& ret = mEmulatedUserHal.onGetProperty(requestedPropValue);
|
||||
VehicleHal::VehiclePropValuePtr v = nullptr;
|
||||
if (!ret.ok()) {
|
||||
ALOGE("get(): User HAL returned error: %s", ret.error().message().c_str());
|
||||
*outStatus = StatusCode(ret.error().code());
|
||||
} else {
|
||||
auto value = ret.value().get();
|
||||
if (value != nullptr) {
|
||||
ALOGI("get(): User HAL returned value: %s", toString(*value).c_str());
|
||||
v = getValuePool()->obtain(*value);
|
||||
*outStatus = StatusCode::OK;
|
||||
} else {
|
||||
ALOGE("get(): User HAL returned null value");
|
||||
*outStatus = StatusCode::INTERNAL_ERROR;
|
||||
}
|
||||
}
|
||||
return addTimestamp(std::move(v));
|
||||
}
|
||||
|
||||
VehicleHal::VehiclePropValuePtr DefaultVehicleHal::get(const VehiclePropValue& requestedPropValue,
|
||||
StatusCode* outStatus) {
|
||||
auto propId = requestedPropValue.prop;
|
||||
ALOGV("get(%d)", propId);
|
||||
|
||||
if (mEmulatedUserHal.isSupported(propId)) {
|
||||
return getUserHalProp(requestedPropValue, outStatus);
|
||||
}
|
||||
|
||||
VehiclePropValuePtr v = nullptr;
|
||||
if (propId == OBD2_FREEZE_FRAME) {
|
||||
v = getValuePool()->obtainComplex();
|
||||
*outStatus = fillObd2FreezeFrame(mPropStore, requestedPropValue, v.get());
|
||||
return addTimestamp(std::move(v));
|
||||
}
|
||||
|
||||
if (propId == OBD2_FREEZE_FRAME_INFO) {
|
||||
v = getValuePool()->obtainComplex();
|
||||
*outStatus = fillObd2DtcInfo(mPropStore, v.get());
|
||||
return addTimestamp(std::move(v));
|
||||
}
|
||||
|
||||
auto internalPropValue = mPropStore->readValueOrNull(requestedPropValue);
|
||||
if (internalPropValue != nullptr) {
|
||||
v = getValuePool()->obtain(*internalPropValue);
|
||||
@@ -81,10 +146,7 @@ VehicleHal::VehiclePropValuePtr DefaultVehicleHal::get(const VehiclePropValue& r
|
||||
} else {
|
||||
*outStatus = StatusCode::TRY_AGAIN;
|
||||
}
|
||||
if (v.get()) {
|
||||
v->timestamp = elapsedRealtimeNano();
|
||||
}
|
||||
return v;
|
||||
return addTimestamp(std::move(v));
|
||||
}
|
||||
|
||||
std::vector<VehiclePropConfig> DefaultVehicleHal::listProperties() {
|
||||
@@ -92,6 +154,36 @@ std::vector<VehiclePropConfig> DefaultVehicleHal::listProperties() {
|
||||
}
|
||||
|
||||
bool DefaultVehicleHal::dump(const hidl_handle& fd, const hidl_vec<hidl_string>& options) {
|
||||
int nativeFd = fd->data[0];
|
||||
if (nativeFd < 0) {
|
||||
ALOGW("Invalid fd from HIDL handle: %d", nativeFd);
|
||||
return false;
|
||||
}
|
||||
if (options.size() > 0) {
|
||||
if (options[0] == "--help") {
|
||||
std::string buffer;
|
||||
buffer += "Emulated user hal usage:\n";
|
||||
buffer += mEmulatedUserHal.showDumpHelp();
|
||||
buffer += "\n";
|
||||
buffer += "VHAL server debug usage:\n";
|
||||
buffer += "--debughal: send debug command to VHAL server, see '--debughal --help'\n";
|
||||
buffer += "\n";
|
||||
dprintf(nativeFd, "%s", buffer.c_str());
|
||||
return false;
|
||||
} else if (options[0] == kUserHalDumpOption) {
|
||||
dprintf(nativeFd, "%s", mEmulatedUserHal.dump("").c_str());
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// No options, dump the emulated user hal state first and then send command to VHAL server
|
||||
// to dump its state.
|
||||
std::string buffer;
|
||||
buffer += "Emulator user hal state:\n";
|
||||
buffer += mEmulatedUserHal.dump(" ");
|
||||
buffer += "\n";
|
||||
dprintf(nativeFd, "%s", buffer.c_str());
|
||||
}
|
||||
|
||||
return mVehicleClient->dump(fd, options);
|
||||
}
|
||||
|
||||
@@ -250,6 +342,23 @@ StatusCode DefaultVehicleHal::checkValueRange(const VehiclePropValue& value,
|
||||
return StatusCode::OK;
|
||||
}
|
||||
|
||||
StatusCode DefaultVehicleHal::setUserHalProp(const VehiclePropValue& propValue) {
|
||||
ALOGI("onSetProperty(): property %d will be handled by UserHal", propValue.prop);
|
||||
|
||||
const auto& ret = mEmulatedUserHal.onSetProperty(propValue);
|
||||
if (!ret.ok()) {
|
||||
ALOGE("onSetProperty(): HAL returned error: %s", ret.error().message().c_str());
|
||||
return StatusCode(ret.error().code());
|
||||
}
|
||||
auto updatedValue = ret.value().get();
|
||||
if (updatedValue != nullptr) {
|
||||
ALOGI("onSetProperty(): updating property returned by HAL: %s",
|
||||
toString(*updatedValue).c_str());
|
||||
onPropertyValue(*updatedValue, true);
|
||||
}
|
||||
return StatusCode::OK;
|
||||
}
|
||||
|
||||
StatusCode DefaultVehicleHal::set(const VehiclePropValue& propValue) {
|
||||
if (propValue.status != VehiclePropertyStatus::AVAILABLE) {
|
||||
// Android side cannot set property status - this value is the
|
||||
@@ -258,6 +367,39 @@ StatusCode DefaultVehicleHal::set(const VehiclePropValue& propValue) {
|
||||
return StatusCode::INVALID_ARG;
|
||||
}
|
||||
|
||||
if (mEmulatedUserHal.isSupported(propValue.prop)) {
|
||||
return setUserHalProp(propValue);
|
||||
}
|
||||
|
||||
std::unordered_set<int32_t> powerProps(std::begin(kHvacPowerProperties),
|
||||
std::end(kHvacPowerProperties));
|
||||
if (powerProps.count(propValue.prop)) {
|
||||
auto hvacPowerOn = mPropStore->readValueOrNull(
|
||||
toInt(VehicleProperty::HVAC_POWER_ON),
|
||||
(VehicleAreaSeat::ROW_1_LEFT | VehicleAreaSeat::ROW_1_RIGHT |
|
||||
VehicleAreaSeat::ROW_2_LEFT | VehicleAreaSeat::ROW_2_CENTER |
|
||||
VehicleAreaSeat::ROW_2_RIGHT));
|
||||
|
||||
if (hvacPowerOn && hvacPowerOn->value.int32Values.size() == 1 &&
|
||||
hvacPowerOn->value.int32Values[0] == 0) {
|
||||
return StatusCode::NOT_AVAILABLE;
|
||||
}
|
||||
}
|
||||
|
||||
if (propValue.prop == OBD2_FREEZE_FRAME_CLEAR) {
|
||||
return clearObd2FreezeFrames(mPropStore, propValue);
|
||||
}
|
||||
if (propValue.prop == VEHICLE_MAP_SERVICE) {
|
||||
// Placeholder for future implementation of VMS property in the default hal. For
|
||||
// now, just returns OK; otherwise, hal clients crash with property not supported.
|
||||
return StatusCode::OK;
|
||||
}
|
||||
if (isDebugProperty(propValue.prop)) {
|
||||
// These are special debug properties and do not need a config or check.
|
||||
// TODO(shanyu): Remove this after we remove debug properties.
|
||||
return mVehicleClient->setProperty(propValue, /*updateStatus=*/false);
|
||||
}
|
||||
|
||||
int32_t property = propValue.prop;
|
||||
const VehiclePropConfig* config = mPropStore->getConfigOrNull(property);
|
||||
if (config == nullptr) {
|
||||
@@ -298,7 +440,13 @@ void DefaultVehicleHal::onCreate() {
|
||||
auto configs = mVehicleClient->getAllPropertyConfig();
|
||||
|
||||
for (const auto& cfg : configs) {
|
||||
int32_t numAreas = isGlobalProp(cfg.prop) ? 0 : cfg.areaConfigs.size();
|
||||
if (isDiagnosticProperty(cfg)) {
|
||||
// do not write an initial empty value for the diagnostic properties
|
||||
// as we will initialize those separately.
|
||||
continue;
|
||||
}
|
||||
|
||||
int32_t numAreas = isGlobalProp(cfg.prop) ? 1 : cfg.areaConfigs.size();
|
||||
|
||||
for (int i = 0; i < numAreas; i++) {
|
||||
int32_t curArea = isGlobalProp(cfg.prop) ? 0 : cfg.areaConfigs[i].areaId;
|
||||
@@ -315,6 +463,10 @@ void DefaultVehicleHal::onCreate() {
|
||||
}
|
||||
|
||||
mVehicleClient->triggerSendAllValues();
|
||||
|
||||
initObd2LiveFrame(mPropStore, *mPropStore->getConfigOrDie(OBD2_LIVE_FRAME));
|
||||
initObd2FreezeFrame(mPropStore, *mPropStore->getConfigOrDie(OBD2_FREEZE_FRAME));
|
||||
|
||||
registerHeartBeatEvent();
|
||||
}
|
||||
|
||||
@@ -344,14 +496,6 @@ VehicleHal::VehiclePropValuePtr DefaultVehicleHal::doInternalHealthCheck() {
|
||||
return v;
|
||||
}
|
||||
|
||||
VehicleHal::VehiclePropValuePtr DefaultVehicleHal::createVhalHeartBeatProp() {
|
||||
VehicleHal::VehiclePropValuePtr v = getValuePool()->obtainInt64(uptimeMillis());
|
||||
v->prop = static_cast<int32_t>(VehicleProperty::VHAL_HEARTBEAT);
|
||||
v->areaId = 0;
|
||||
v->status = VehiclePropertyStatus::AVAILABLE;
|
||||
return v;
|
||||
}
|
||||
|
||||
void DefaultVehicleHal::onContinuousPropertyTimer(const std::vector<int32_t>& properties) {
|
||||
auto& pool = *getValuePool();
|
||||
|
||||
@@ -432,7 +576,21 @@ void DefaultVehicleHal::onPropertyValue(const VehiclePropValue& value, bool upda
|
||||
void DefaultVehicleHal::initStaticConfig() {
|
||||
auto configs = mVehicleClient->getAllPropertyConfig();
|
||||
for (auto&& cfg : configs) {
|
||||
mPropStore->registerProperty(cfg, nullptr);
|
||||
VehiclePropertyStore::TokenFunction tokenFunction = nullptr;
|
||||
|
||||
switch (cfg.prop) {
|
||||
case OBD2_FREEZE_FRAME: {
|
||||
// We use timestamp as token for OBD2_FREEZE_FRAME
|
||||
tokenFunction = [](const VehiclePropValue& propValue) {
|
||||
return propValue.timestamp;
|
||||
};
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
mPropStore->registerProperty(cfg, tokenFunction);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,8 +19,9 @@
|
||||
|
||||
#include <vhal_v2_0/RecurrentTimer.h>
|
||||
#include <vhal_v2_0/VehicleHal.h>
|
||||
#include "vhal_v2_0/VehiclePropertyStore.h"
|
||||
#include <vhal_v2_0/VehiclePropertyStore.h>
|
||||
|
||||
#include "EmulatedUserHal.h"
|
||||
#include "VehicleHalClient.h"
|
||||
|
||||
namespace android {
|
||||
@@ -55,10 +56,21 @@ class DefaultVehicleHal : public VehicleHal {
|
||||
VehiclePropertyStore* mPropStore;
|
||||
RecurrentTimer mRecurrentTimer;
|
||||
VehicleHalClient* mVehicleClient;
|
||||
virtual bool isContinuousProperty(int32_t propId) const;
|
||||
virtual void initStaticConfig();
|
||||
virtual void onContinuousPropertyTimer(const std::vector<int32_t>& properties);
|
||||
EmulatedUserHal mEmulatedUserHal;
|
||||
|
||||
// The callback that would be called when a property value is updated. This function could
|
||||
// be extended to handle specific property update event.
|
||||
virtual void onPropertyValue(const VehiclePropValue& value, bool updateStatus);
|
||||
// Do an internal health check, vendor should add health check logic in this function.
|
||||
virtual VehicleHal::VehiclePropValuePtr doInternalHealthCheck();
|
||||
|
||||
// The callback that would be called for every event generated by 'subscribe' or heartbeat.
|
||||
// Properties contains a list of properties that need to be handled.
|
||||
void onContinuousPropertyTimer(const std::vector<int32_t>& properties);
|
||||
// Initiate config for all properties, would be called during onCreate().
|
||||
void initStaticConfig();
|
||||
// Whether the property is a continuous property.
|
||||
bool isContinuousProperty(int32_t propId) const;
|
||||
// Returns a lambda that could be used in mRecurrentTimer.
|
||||
RecurrentTimer::Action getTimerAction();
|
||||
// Check whether a propValue is valid according to its type.
|
||||
@@ -69,8 +81,12 @@ class DefaultVehicleHal : public VehicleHal {
|
||||
// Register the heart beat event to be sent every 3s. This is required to inform watch dog that
|
||||
// VHAL is alive. Subclasses should always calls this function during onCreate.
|
||||
void registerHeartBeatEvent();
|
||||
|
||||
VehicleHal::VehiclePropValuePtr doInternalHealthCheck();
|
||||
// Get a user HAL property.
|
||||
VehiclePropValuePtr getUserHalProp(const VehiclePropValue& requestedPropValue,
|
||||
StatusCode* outStatus);
|
||||
// Set a user HAL property.
|
||||
StatusCode setUserHalProp(const VehiclePropValue& propValue);
|
||||
// Create a VHAL heart beat property.
|
||||
VehicleHal::VehiclePropValuePtr createVhalHeartBeatProp();
|
||||
|
||||
private:
|
||||
@@ -78,6 +94,8 @@ class DefaultVehicleHal : public VehicleHal {
|
||||
// See 'VehiclePropertyType' documentation in 'types.hal' for detail.
|
||||
StatusCode checkVendorMixedPropValue(const VehiclePropValue& value,
|
||||
const VehiclePropConfig* config);
|
||||
// Read the override properties from a config file.
|
||||
void getAllPropertiesOverride();
|
||||
};
|
||||
|
||||
} // namespace impl
|
||||
|
||||
@@ -17,14 +17,17 @@
|
||||
#define LOG_TAG "DefaultVehicleHalServer"
|
||||
|
||||
#include <fstream>
|
||||
#include <regex>
|
||||
|
||||
#include <android-base/format.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/parsedouble.h>
|
||||
#include <android-base/parseint.h>
|
||||
#include <android-base/properties.h>
|
||||
#include <utils/SystemClock.h>
|
||||
|
||||
#include "DefaultConfig.h"
|
||||
#include "FakeObd2Frame.h"
|
||||
#include "JsonFakeValueGenerator.h"
|
||||
#include "LinearFakeValueGenerator.h"
|
||||
|
||||
@@ -38,6 +41,10 @@ namespace V2_0 {
|
||||
|
||||
namespace impl {
|
||||
|
||||
namespace {
|
||||
const char* VENDOR_OVERRIDE_DIR = "/vendor/etc/vhaloverride/";
|
||||
} // namespace
|
||||
|
||||
void DefaultVehicleHalServer::storePropInitialValue(const ConfigDeclaration& config) {
|
||||
VehiclePropConfig cfg = config.config;
|
||||
|
||||
@@ -72,8 +79,13 @@ DefaultVehicleHalServer::DefaultVehicleHalServer() {
|
||||
for (auto& it : kVehicleProperties) {
|
||||
VehiclePropConfig cfg = it.config;
|
||||
mServerSidePropStore.registerProperty(cfg);
|
||||
// Skip diagnostic properties since there is special logic to handle those.
|
||||
if (isDiagnosticProperty(cfg)) {
|
||||
continue;
|
||||
}
|
||||
storePropInitialValue(it);
|
||||
}
|
||||
maybeOverrideProperties(VENDOR_OVERRIDE_DIR);
|
||||
}
|
||||
|
||||
void DefaultVehicleHalServer::sendAllValuesToClient() {
|
||||
@@ -559,6 +571,32 @@ IVehicleServer::DumpResult DefaultVehicleHalServer::genFakeData(
|
||||
return result;
|
||||
}
|
||||
|
||||
void DefaultVehicleHalServer::maybeOverrideProperties(const char* overrideDir) {
|
||||
if (android::base::GetBoolProperty("persist.vendor.vhal_init_value_override", false)) {
|
||||
overrideProperties(overrideDir);
|
||||
}
|
||||
}
|
||||
|
||||
void DefaultVehicleHalServer::overrideProperties(const char* overrideDir) {
|
||||
LOG(INFO) << "loading vendor override properties from " << overrideDir;
|
||||
if (auto dir = opendir(overrideDir)) {
|
||||
std::regex reg_json(".*[.]json", std::regex::icase);
|
||||
while (auto f = readdir(dir)) {
|
||||
if (!regex_match(f->d_name, reg_json)) {
|
||||
continue;
|
||||
}
|
||||
std::string file = overrideDir + std::string(f->d_name);
|
||||
JsonFakeValueGenerator tmpGenerator(file);
|
||||
|
||||
std::vector<VehiclePropValue> propValues = tmpGenerator.getAllEvents();
|
||||
for (const VehiclePropValue& prop : propValues) {
|
||||
mServerSidePropStore.writeValue(prop, true);
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace impl
|
||||
|
||||
} // namespace V2_0
|
||||
|
||||
@@ -72,6 +72,10 @@ class DefaultVehicleHalServer : public IVehicleServer {
|
||||
std::string getHelpInfo();
|
||||
|
||||
DumpResult genFakeData(const std::vector<std::string>& options);
|
||||
// If "persist.vendor.vhal_init_value_override" is true, try to override the properties default
|
||||
// values according to JSON files in 'overrideDir'. Would be called in constructor using
|
||||
// VENDOR_OVERRIDE_DIR as overrideDir.
|
||||
void maybeOverrideProperties(const char* overrideDir);
|
||||
|
||||
protected:
|
||||
GeneratorHub mGeneratorHub{
|
||||
@@ -79,6 +83,12 @@ class DefaultVehicleHalServer : public IVehicleServer {
|
||||
|
||||
VehiclePropValuePool* mValuePool{nullptr};
|
||||
VehiclePropertyStore mServerSidePropStore;
|
||||
|
||||
private:
|
||||
// Expose protected methods to unit test.
|
||||
friend class DefaultVhalImplTestHelper;
|
||||
// Override the properties using config files in 'overrideDir'.
|
||||
void overrideProperties(const char* overrideDir);
|
||||
};
|
||||
|
||||
} // namespace impl
|
||||
|
||||
184
automotive/vehicle/2.0/default/impl/vhal_v2_0/FakeObd2Frame.cpp
Normal file
184
automotive/vehicle/2.0/default/impl/vhal_v2_0/FakeObd2Frame.cpp
Normal file
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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 <android/hardware/automotive/vehicle/2.0/types.h>
|
||||
#include <utils/Log.h>
|
||||
#include <vhal_v2_0/Obd2SensorStore.h>
|
||||
#include <vhal_v2_0/PropertyUtils.h>
|
||||
#include <vhal_v2_0/VehiclePropertyStore.h>
|
||||
#include <vhal_v2_0/VehicleUtils.h>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace automotive {
|
||||
namespace vehicle {
|
||||
namespace V2_0 {
|
||||
|
||||
namespace impl {
|
||||
|
||||
namespace {
|
||||
|
||||
std::unique_ptr<Obd2SensorStore> fillDefaultObd2Frame(size_t numVendorIntegerSensors,
|
||||
size_t numVendorFloatSensors) {
|
||||
std::unique_ptr<Obd2SensorStore> sensorStore(
|
||||
new Obd2SensorStore(numVendorIntegerSensors, numVendorFloatSensors));
|
||||
|
||||
sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::FUEL_SYSTEM_STATUS,
|
||||
toInt(Obd2FuelSystemStatus::CLOSED_LOOP));
|
||||
sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::MALFUNCTION_INDICATOR_LIGHT_ON, 0);
|
||||
sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::IGNITION_MONITORS_SUPPORTED,
|
||||
toInt(Obd2IgnitionMonitorKind::SPARK));
|
||||
sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::IGNITION_SPECIFIC_MONITORS,
|
||||
Obd2CommonIgnitionMonitors::COMPONENTS_AVAILABLE |
|
||||
Obd2CommonIgnitionMonitors::MISFIRE_AVAILABLE |
|
||||
Obd2SparkIgnitionMonitors::AC_REFRIGERANT_AVAILABLE |
|
||||
Obd2SparkIgnitionMonitors::EVAPORATIVE_SYSTEM_AVAILABLE);
|
||||
sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::INTAKE_AIR_TEMPERATURE, 35);
|
||||
sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::COMMANDED_SECONDARY_AIR_STATUS,
|
||||
toInt(Obd2SecondaryAirStatus::FROM_OUTSIDE_OR_OFF));
|
||||
sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::NUM_OXYGEN_SENSORS_PRESENT, 1);
|
||||
sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::RUNTIME_SINCE_ENGINE_START, 500);
|
||||
sensorStore->setIntegerSensor(
|
||||
DiagnosticIntegerSensorIndex::DISTANCE_TRAVELED_WITH_MALFUNCTION_INDICATOR_LIGHT_ON, 0);
|
||||
sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::WARMUPS_SINCE_CODES_CLEARED, 51);
|
||||
sensorStore->setIntegerSensor(
|
||||
DiagnosticIntegerSensorIndex::DISTANCE_TRAVELED_SINCE_CODES_CLEARED, 365);
|
||||
sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::ABSOLUTE_BAROMETRIC_PRESSURE, 30);
|
||||
sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::CONTROL_MODULE_VOLTAGE, 12);
|
||||
sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::AMBIENT_AIR_TEMPERATURE, 18);
|
||||
sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::MAX_FUEL_AIR_EQUIVALENCE_RATIO, 1);
|
||||
sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::FUEL_TYPE,
|
||||
toInt(Obd2FuelType::GASOLINE));
|
||||
sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::CALCULATED_ENGINE_LOAD, 0.153);
|
||||
sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::SHORT_TERM_FUEL_TRIM_BANK1, -0.16);
|
||||
sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::LONG_TERM_FUEL_TRIM_BANK1, -0.16);
|
||||
sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::SHORT_TERM_FUEL_TRIM_BANK2, -0.16);
|
||||
sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::LONG_TERM_FUEL_TRIM_BANK2, -0.16);
|
||||
sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::INTAKE_MANIFOLD_ABSOLUTE_PRESSURE, 7.5);
|
||||
sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::ENGINE_RPM, 1250.);
|
||||
sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::VEHICLE_SPEED, 40.);
|
||||
sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::TIMING_ADVANCE, 2.5);
|
||||
sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::THROTTLE_POSITION, 19.75);
|
||||
sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::OXYGEN_SENSOR1_VOLTAGE, 0.265);
|
||||
sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::FUEL_TANK_LEVEL_INPUT, 0.824);
|
||||
sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::EVAPORATION_SYSTEM_VAPOR_PRESSURE,
|
||||
-0.373);
|
||||
sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::CATALYST_TEMPERATURE_BANK1_SENSOR1,
|
||||
190.);
|
||||
sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::RELATIVE_THROTTLE_POSITION, 3.);
|
||||
sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::ABSOLUTE_THROTTLE_POSITION_B, 0.306);
|
||||
sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::ACCELERATOR_PEDAL_POSITION_D, 0.188);
|
||||
sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::ACCELERATOR_PEDAL_POSITION_E, 0.094);
|
||||
sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::COMMANDED_THROTTLE_ACTUATOR, 0.024);
|
||||
|
||||
return sensorStore;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void initObd2LiveFrame(VehiclePropertyStore* propStore, const VehiclePropConfig& propConfig) {
|
||||
auto liveObd2Frame = createVehiclePropValue(VehiclePropertyType::MIXED, 0);
|
||||
auto sensorStore = fillDefaultObd2Frame(static_cast<size_t>(propConfig.configArray[0]),
|
||||
static_cast<size_t>(propConfig.configArray[1]));
|
||||
sensorStore->fillPropValue("", liveObd2Frame.get());
|
||||
liveObd2Frame->prop = OBD2_LIVE_FRAME;
|
||||
|
||||
propStore->writeValue(*liveObd2Frame, true);
|
||||
}
|
||||
|
||||
void initObd2FreezeFrame(VehiclePropertyStore* propStore, const VehiclePropConfig& propConfig) {
|
||||
auto sensorStore = fillDefaultObd2Frame(static_cast<size_t>(propConfig.configArray[0]),
|
||||
static_cast<size_t>(propConfig.configArray[1]));
|
||||
|
||||
static std::vector<std::string> sampleDtcs = {"P0070", "P0102", "P0123"};
|
||||
for (auto&& dtc : sampleDtcs) {
|
||||
auto freezeFrame = createVehiclePropValue(VehiclePropertyType::MIXED, 0);
|
||||
sensorStore->fillPropValue(dtc, freezeFrame.get());
|
||||
freezeFrame->prop = OBD2_FREEZE_FRAME;
|
||||
ALOGE("freeze frame: %lld", (long long)freezeFrame->timestamp);
|
||||
|
||||
propStore->writeValue(*freezeFrame, true);
|
||||
}
|
||||
}
|
||||
|
||||
StatusCode fillObd2FreezeFrame(VehiclePropertyStore* propStore,
|
||||
const VehiclePropValue& requestedPropValue,
|
||||
VehiclePropValue* outValue) {
|
||||
if (requestedPropValue.value.int64Values.size() != 1) {
|
||||
ALOGE("asked for OBD2_FREEZE_FRAME without valid timestamp");
|
||||
return StatusCode::INVALID_ARG;
|
||||
}
|
||||
if (propStore->readValuesForProperty(OBD2_FREEZE_FRAME).size() == 0) {
|
||||
// Should no freeze frame be available at the given timestamp, a response of NOT_AVAILABLE
|
||||
// must be returned by the implementation
|
||||
return StatusCode::NOT_AVAILABLE;
|
||||
}
|
||||
auto timestamp = requestedPropValue.value.int64Values[0];
|
||||
auto freezeFrame = propStore->readValueOrNull(OBD2_FREEZE_FRAME, 0, timestamp);
|
||||
if (freezeFrame == nullptr) {
|
||||
ALOGE("asked for OBD2_FREEZE_FRAME at invalid timestamp");
|
||||
return StatusCode::INVALID_ARG;
|
||||
}
|
||||
outValue->prop = OBD2_FREEZE_FRAME;
|
||||
outValue->value.int32Values = freezeFrame->value.int32Values;
|
||||
outValue->value.floatValues = freezeFrame->value.floatValues;
|
||||
outValue->value.bytes = freezeFrame->value.bytes;
|
||||
outValue->value.stringValue = freezeFrame->value.stringValue;
|
||||
outValue->timestamp = freezeFrame->timestamp;
|
||||
return StatusCode::OK;
|
||||
}
|
||||
|
||||
StatusCode fillObd2DtcInfo(VehiclePropertyStore* propStore, VehiclePropValue* outValue) {
|
||||
std::vector<int64_t> timestamps;
|
||||
for (const auto& freezeFrame : propStore->readValuesForProperty(OBD2_FREEZE_FRAME)) {
|
||||
timestamps.push_back(freezeFrame.timestamp);
|
||||
}
|
||||
outValue->value.int64Values = timestamps;
|
||||
outValue->prop = OBD2_FREEZE_FRAME_INFO;
|
||||
return StatusCode::OK;
|
||||
}
|
||||
|
||||
StatusCode clearObd2FreezeFrames(VehiclePropertyStore* propStore,
|
||||
const VehiclePropValue& propValue) {
|
||||
if (propValue.value.int64Values.size() == 0) {
|
||||
propStore->removeValuesForProperty(OBD2_FREEZE_FRAME);
|
||||
return StatusCode::OK;
|
||||
} else {
|
||||
for (int64_t timestamp : propValue.value.int64Values) {
|
||||
auto freezeFrame = propStore->readValueOrNull(OBD2_FREEZE_FRAME, 0, timestamp);
|
||||
if (freezeFrame == nullptr) {
|
||||
ALOGE("asked for OBD2_FREEZE_FRAME at invalid timestamp");
|
||||
return StatusCode::INVALID_ARG;
|
||||
}
|
||||
propStore->removeValue(*freezeFrame);
|
||||
}
|
||||
}
|
||||
return StatusCode::OK;
|
||||
}
|
||||
|
||||
bool isDiagnosticProperty(const VehiclePropConfig& propConfig) {
|
||||
return (propConfig.prop == OBD2_LIVE_FRAME || propConfig.prop == OBD2_FREEZE_FRAME ||
|
||||
propConfig.prop == OBD2_FREEZE_FRAME_CLEAR ||
|
||||
propConfig.prop == OBD2_FREEZE_FRAME_INFO);
|
||||
}
|
||||
|
||||
} // namespace impl
|
||||
|
||||
} // namespace V2_0
|
||||
} // namespace vehicle
|
||||
} // namespace automotive
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.
|
||||
*/
|
||||
#ifndef android_hardware_automotive_vehicle_V2_0_impl_FakeObd2Frame_H_
|
||||
#define android_hardware_automotive_vehicle_V2_0_impl_FakeObd2Frame_H_
|
||||
|
||||
#include <android/hardware/automotive/vehicle/2.0/types.h>
|
||||
#include <vhal_v2_0/VehiclePropertyStore.h>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace automotive {
|
||||
namespace vehicle {
|
||||
namespace V2_0 {
|
||||
|
||||
namespace impl {
|
||||
|
||||
void initObd2LiveFrame(VehiclePropertyStore* propStore, const VehiclePropConfig& propConfig);
|
||||
void initObd2FreezeFrame(VehiclePropertyStore* propStore, const VehiclePropConfig& propConfig);
|
||||
StatusCode fillObd2FreezeFrame(VehiclePropertyStore* propStore,
|
||||
const VehiclePropValue& requestedPropValue,
|
||||
VehiclePropValue* outValue);
|
||||
StatusCode fillObd2DtcInfo(VehiclePropertyStore* propStore, VehiclePropValue* outValue);
|
||||
StatusCode clearObd2FreezeFrames(VehiclePropertyStore* propStore,
|
||||
const VehiclePropValue& propValue);
|
||||
bool isDiagnosticProperty(const VehiclePropConfig& propConfig);
|
||||
|
||||
} // namespace impl
|
||||
|
||||
} // namespace V2_0
|
||||
} // namespace vehicle
|
||||
} // namespace automotive
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
|
||||
#endif // android_hardware_automotive_vehicle_V2_0_impl_FakeObd2Frame_H_
|
||||
@@ -16,3 +16,8 @@ filegroup {
|
||||
name: "vhal_test_json",
|
||||
srcs: ["prop.json"],
|
||||
}
|
||||
|
||||
filegroup {
|
||||
name: "vhal_test_override_json",
|
||||
srcs: ["override/*"],
|
||||
}
|
||||
|
||||
@@ -27,6 +27,32 @@
|
||||
#include <vhal_v2_0/VehicleObjectPool.h>
|
||||
#include <vhal_v2_0/VehiclePropertyStore.h>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace automotive {
|
||||
namespace vehicle {
|
||||
namespace V2_0 {
|
||||
namespace impl {
|
||||
|
||||
class DefaultVhalImplTestHelper {
|
||||
public:
|
||||
DefaultVhalImplTestHelper(DefaultVehicleHalServer* server) { mServer = server; }
|
||||
|
||||
void overrideProperties(const char* overrideDir) {
|
||||
mServer->overrideProperties(overrideDir);
|
||||
}
|
||||
|
||||
private:
|
||||
DefaultVehicleHalServer* mServer;
|
||||
};
|
||||
|
||||
} // namespace impl
|
||||
} // namespace V2_0
|
||||
} // namespace vehicle
|
||||
} // namespace automotive
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
|
||||
namespace {
|
||||
|
||||
using ::android::hardware::hidl_handle;
|
||||
@@ -44,9 +70,15 @@ using ::android::hardware::automotive::vehicle::V2_0::VehiclePropValue;
|
||||
using ::android::hardware::automotive::vehicle::V2_0::VehiclePropValuePool;
|
||||
using ::android::hardware::automotive::vehicle::V2_0::impl::DefaultVehicleConnector;
|
||||
using ::android::hardware::automotive::vehicle::V2_0::impl::DefaultVehicleHal;
|
||||
using ::android::hardware::automotive::vehicle::V2_0::impl::DefaultVhalImplTestHelper;
|
||||
using ::android::hardware::automotive::vehicle::V2_0::impl::HVAC_ALL;
|
||||
using ::android::hardware::automotive::vehicle::V2_0::impl::HVAC_LEFT;
|
||||
using ::android::hardware::automotive::vehicle::V2_0::impl::HVAC_RIGHT;
|
||||
using ::android::hardware::automotive::vehicle::V2_0::impl::kMixedTypePropertyForTest;
|
||||
using ::android::hardware::automotive::vehicle::V2_0::impl::OBD2_FREEZE_FRAME;
|
||||
using ::android::hardware::automotive::vehicle::V2_0::impl::OBD2_FREEZE_FRAME_CLEAR;
|
||||
using ::android::hardware::automotive::vehicle::V2_0::impl::OBD2_FREEZE_FRAME_INFO;
|
||||
using ::android::hardware::automotive::vehicle::V2_0::impl::OBD2_LIVE_FRAME;
|
||||
|
||||
using ::testing::HasSubstr;
|
||||
|
||||
@@ -72,12 +104,24 @@ class DefaultVhalImplTest : public ::testing::Test {
|
||||
mConnector.reset(new DefaultVehicleConnector);
|
||||
mConnector->setValuePool(&mValueObjectPool);
|
||||
mHal.reset(new DefaultVehicleHal(mPropStore.get(), mConnector.get()));
|
||||
initHal();
|
||||
}
|
||||
|
||||
void initHal() {
|
||||
mHal->init(&mValueObjectPool,
|
||||
std::bind(&DefaultVhalImplTest::onHalEvent, this, std::placeholders::_1),
|
||||
std::bind(&DefaultVhalImplTest::onHalPropertySetError, this,
|
||||
std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));
|
||||
}
|
||||
|
||||
protected:
|
||||
std::unique_ptr<DefaultVehicleHal> mHal;
|
||||
std::unique_ptr<DefaultVehicleConnector> mConnector;
|
||||
std::unique_ptr<VehiclePropertyStore> mPropStore;
|
||||
VehiclePropValuePool mValueObjectPool;
|
||||
android::ConcurrentQueue<VehiclePropValuePtr> mEventQueue;
|
||||
android::ConcurrentQueue<VehiclePropValuePtr> mHeartBeatQueue;
|
||||
|
||||
private:
|
||||
void onHalEvent(VehiclePropValuePtr v) {
|
||||
if (v->prop != toInt(VehicleProperty::VHAL_HEARTBEAT)) {
|
||||
@@ -90,14 +134,6 @@ class DefaultVhalImplTest : public ::testing::Test {
|
||||
|
||||
void onHalPropertySetError(StatusCode /*errorCode*/, int32_t /*property*/, int32_t /*areaId*/) {
|
||||
}
|
||||
|
||||
protected:
|
||||
std::unique_ptr<DefaultVehicleHal> mHal;
|
||||
std::unique_ptr<DefaultVehicleConnector> mConnector;
|
||||
std::unique_ptr<VehiclePropertyStore> mPropStore;
|
||||
VehiclePropValuePool mValueObjectPool;
|
||||
android::ConcurrentQueue<VehiclePropValuePtr> mEventQueue;
|
||||
android::ConcurrentQueue<VehiclePropValuePtr> mHeartBeatQueue;
|
||||
};
|
||||
|
||||
TEST_F(DefaultVhalImplTest, testListProperties) {
|
||||
@@ -721,12 +757,12 @@ TEST_F(DefaultVhalImplTest, testDebugGenFakeDataJson) {
|
||||
EXPECT_EQ((size_t)1, events[0]->value.int32Values.size());
|
||||
EXPECT_EQ(8, events[0]->value.int32Values[0]);
|
||||
EXPECT_EQ((size_t)1, events[3]->value.int32Values.size());
|
||||
EXPECT_EQ(4, events[3]->value.int32Values[0]);
|
||||
EXPECT_EQ(10, events[3]->value.int32Values[0]);
|
||||
// Second set of the same events.
|
||||
EXPECT_EQ((size_t)1, events[4]->value.int32Values.size());
|
||||
EXPECT_EQ(8, events[4]->value.int32Values[0]);
|
||||
EXPECT_EQ((size_t)1, events[7]->value.int32Values.size());
|
||||
EXPECT_EQ(4, events[7]->value.int32Values[0]);
|
||||
EXPECT_EQ(10, events[7]->value.int32Values[0]);
|
||||
}
|
||||
|
||||
TEST_F(DefaultVhalImplTest, testDebugGenFakeDataKeyPress) {
|
||||
@@ -768,4 +804,463 @@ TEST_F(DefaultVhalImplTest, testHeartBeatEvent) {
|
||||
ASSERT_EQ(toInt(VehicleProperty::VHAL_HEARTBEAT), events[0]->prop);
|
||||
}
|
||||
|
||||
TEST_F(DefaultVhalImplTest, testVendorOverrideProperties) {
|
||||
// Destroy the existing VHAL first to prevent it using destroyed connector or propstore.
|
||||
mHal.reset();
|
||||
// Create a new Default VHAL and reinitialize it to load the override properties.
|
||||
std::string overrideDir = android::base::GetExecutableDirectory() + "/override/";
|
||||
mPropStore.reset(new VehiclePropertyStore);
|
||||
mConnector.reset(new DefaultVehicleConnector);
|
||||
mConnector->setValuePool(&mValueObjectPool);
|
||||
mHal.reset(new DefaultVehicleHal(mPropStore.get(), mConnector.get()));
|
||||
// Set vendor override directory.
|
||||
DefaultVhalImplTestHelper helper(mConnector.get());
|
||||
helper.overrideProperties(overrideDir.c_str());
|
||||
|
||||
initHal();
|
||||
|
||||
VehiclePropValue value;
|
||||
StatusCode status;
|
||||
// This is the same as the prop in 'gear_selection.json'.
|
||||
value.prop = toInt(VehicleProperty::GEAR_SELECTION);
|
||||
|
||||
auto gotValue = mHal->get(value, &status);
|
||||
|
||||
ASSERT_EQ(StatusCode::OK, status);
|
||||
ASSERT_EQ((size_t)1, gotValue->value.int32Values.size());
|
||||
ASSERT_EQ(8, gotValue->value.int32Values[0]);
|
||||
|
||||
// If we set the value, it should update despite the override.
|
||||
value.prop = toInt(VehicleProperty::GEAR_SELECTION);
|
||||
value.value.int32Values.resize(1);
|
||||
value.value.int32Values[0] = 5;
|
||||
|
||||
status = mHal->set(value);
|
||||
ASSERT_EQ(StatusCode::OK, status);
|
||||
|
||||
gotValue = mHal->get(value, &status);
|
||||
ASSERT_EQ(StatusCode::OK, status);
|
||||
ASSERT_EQ((size_t)1, gotValue->value.int32Values.size());
|
||||
ASSERT_EQ(5, gotValue->value.int32Values[0]);
|
||||
}
|
||||
|
||||
TEST_F(DefaultVhalImplTest, testVendorOverridePropertiesMultipleAreas) {
|
||||
// Destroy the existing VHAL first to prevent it using destroyed connector or propstore.
|
||||
mHal.reset();
|
||||
// Create a new Default VHAL and reinitialize it to load the override properties.
|
||||
std::string overrideDir = android::base::GetExecutableDirectory() + "/override/";
|
||||
mPropStore.reset(new VehiclePropertyStore);
|
||||
mConnector.reset(new DefaultVehicleConnector);
|
||||
mConnector->setValuePool(&mValueObjectPool);
|
||||
mHal.reset(new DefaultVehicleHal(mPropStore.get(), mConnector.get()));
|
||||
// Set vendor override directory.
|
||||
DefaultVhalImplTestHelper helper(mConnector.get());
|
||||
helper.overrideProperties(overrideDir.c_str());
|
||||
|
||||
initHal();
|
||||
|
||||
VehiclePropValue value;
|
||||
StatusCode status;
|
||||
// This is the same as the prop in 'hvac_temperature_set.json'.
|
||||
value.prop = toInt(VehicleProperty::HVAC_TEMPERATURE_SET);
|
||||
value.areaId = HVAC_LEFT;
|
||||
|
||||
auto gotValue = mHal->get(value, &status);
|
||||
|
||||
ASSERT_EQ(StatusCode::OK, status);
|
||||
ASSERT_EQ((size_t)1, gotValue->value.floatValues.size());
|
||||
ASSERT_EQ(30.0f, gotValue->value.floatValues[0]);
|
||||
|
||||
// HVAC_RIGHT should not be affected and return the default value.
|
||||
value.areaId = HVAC_RIGHT;
|
||||
|
||||
gotValue = mHal->get(value, &status);
|
||||
|
||||
ASSERT_EQ(StatusCode::OK, status);
|
||||
ASSERT_EQ((size_t)1, gotValue->value.floatValues.size());
|
||||
ASSERT_EQ(20.0f, gotValue->value.floatValues[0]);
|
||||
}
|
||||
|
||||
TEST_F(DefaultVhalImplTest, testVendorOverridePropertiesDirDoesNotExist) {
|
||||
// Destroy the existing VHAL first to prevent it using destroyed connector or propstore.
|
||||
mHal.reset();
|
||||
// Create a new Default VHAL and reinitialize it to load the override properties.
|
||||
mPropStore.reset(new VehiclePropertyStore);
|
||||
mConnector.reset(new DefaultVehicleConnector);
|
||||
mConnector->setValuePool(&mValueObjectPool);
|
||||
mHal.reset(new DefaultVehicleHal(mPropStore.get(), mConnector.get()));
|
||||
// Set vendor override directory to a non-existing dir
|
||||
DefaultVhalImplTestHelper helper(mConnector.get());
|
||||
helper.overrideProperties("123");
|
||||
initHal();
|
||||
|
||||
VehiclePropValue value;
|
||||
StatusCode status;
|
||||
value.prop = toInt(VehicleProperty::GEAR_SELECTION);
|
||||
|
||||
auto gotValue = mHal->get(value, &status);
|
||||
|
||||
// We should get the default value.
|
||||
ASSERT_EQ(StatusCode::OK, status);
|
||||
ASSERT_EQ((size_t)1, gotValue->value.int32Values.size());
|
||||
ASSERT_EQ(4, gotValue->value.int32Values[0]);
|
||||
}
|
||||
|
||||
TEST_F(DefaultVhalImplTest, testGetObd2FreezeFrameNoTimestamp) {
|
||||
VehiclePropValue value;
|
||||
value.prop = OBD2_FREEZE_FRAME;
|
||||
StatusCode status;
|
||||
|
||||
auto gotValue = mHal->get(value, &status);
|
||||
|
||||
ASSERT_EQ(StatusCode::INVALID_ARG, status);
|
||||
}
|
||||
|
||||
TEST_F(DefaultVhalImplTest, testGetObd2FreezeFrameInvalidTimestamp) {
|
||||
VehiclePropValue value;
|
||||
value.prop = OBD2_FREEZE_FRAME;
|
||||
value.value.int64Values.resize(1);
|
||||
value.value.int64Values[0] = 0;
|
||||
StatusCode status;
|
||||
|
||||
auto gotValue = mHal->get(value, &status);
|
||||
|
||||
ASSERT_EQ(StatusCode::INVALID_ARG, status);
|
||||
}
|
||||
|
||||
TEST_F(DefaultVhalImplTest, testGetObd2FreezeFrameInfoGetObd2FreezeFrame) {
|
||||
VehiclePropValue value;
|
||||
value.prop = OBD2_FREEZE_FRAME_INFO;
|
||||
StatusCode status;
|
||||
|
||||
auto gotValue = mHal->get(value, &status);
|
||||
|
||||
ASSERT_EQ(StatusCode::OK, status);
|
||||
ASSERT_EQ((size_t)3, gotValue->value.int64Values.size());
|
||||
|
||||
std::vector<std::string> dtcs;
|
||||
std::vector<std::string> sampleDtcs = {"P0070", "P0102", "P0123"};
|
||||
for (int64_t timestamp : gotValue->value.int64Values) {
|
||||
VehiclePropValue freezeFrameRequest;
|
||||
freezeFrameRequest.prop = OBD2_FREEZE_FRAME;
|
||||
freezeFrameRequest.value.int64Values.resize(1);
|
||||
freezeFrameRequest.value.int64Values[0] = timestamp;
|
||||
|
||||
auto freezeFrameValue = mHal->get(freezeFrameRequest, &status);
|
||||
|
||||
ASSERT_EQ(StatusCode::OK, status);
|
||||
// Obd2IntegerSensorIndex.LAST_SYSTEM_INDEX + 1
|
||||
EXPECT_EQ((size_t)32, freezeFrameValue->value.int32Values.size());
|
||||
// Obd2FloatSensorIndex.LAST_SYSTEM_INDEX + 1
|
||||
EXPECT_EQ((size_t)71, freezeFrameValue->value.floatValues.size());
|
||||
// (intValues.size() + floatValues.size()) / 8
|
||||
EXPECT_EQ((size_t)13, freezeFrameValue->value.bytes.size());
|
||||
|
||||
dtcs.push_back(freezeFrameValue->value.stringValue);
|
||||
}
|
||||
|
||||
for (std::string expectDtc : sampleDtcs) {
|
||||
EXPECT_NE(std::find(dtcs.begin(), dtcs.end(), expectDtc), dtcs.end());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DefaultVhalImplTest, testGetObd2LiveFrame) {
|
||||
VehiclePropValue value;
|
||||
value.prop = OBD2_LIVE_FRAME;
|
||||
StatusCode status;
|
||||
|
||||
auto gotValue = mHal->get(value, &status);
|
||||
|
||||
ASSERT_EQ(StatusCode::OK, status);
|
||||
// Obd2IntegerSensorIndex.LAST_SYSTEM_INDEX + 1
|
||||
EXPECT_EQ((size_t)32, gotValue->value.int32Values.size());
|
||||
// Obd2FloatSensorIndex.LAST_SYSTEM_INDEX + 1
|
||||
EXPECT_EQ((size_t)71, gotValue->value.floatValues.size());
|
||||
// (intValues.size() + floatValues.size()) / 8
|
||||
EXPECT_EQ((size_t)13, gotValue->value.bytes.size());
|
||||
}
|
||||
|
||||
TEST_F(DefaultVhalImplTest, testClearObd2FreezeFrameAll) {
|
||||
VehiclePropValue value;
|
||||
value.prop = OBD2_FREEZE_FRAME_CLEAR;
|
||||
// No int64Values is to clear all frames.
|
||||
|
||||
auto status = mHal->set(value);
|
||||
|
||||
EXPECT_EQ(StatusCode::OK, status);
|
||||
|
||||
VehiclePropValue freezeFrameRequest;
|
||||
freezeFrameRequest.prop = OBD2_FREEZE_FRAME;
|
||||
freezeFrameRequest.value.int64Values.resize(1);
|
||||
|
||||
auto gotValue = mHal->get(freezeFrameRequest, &status);
|
||||
|
||||
EXPECT_EQ(StatusCode::NOT_AVAILABLE, status);
|
||||
|
||||
VehiclePropValue freezeFrameInfoRequest;
|
||||
freezeFrameInfoRequest.prop = OBD2_FREEZE_FRAME_INFO;
|
||||
|
||||
gotValue = mHal->get(freezeFrameInfoRequest, &status);
|
||||
|
||||
EXPECT_EQ(StatusCode::OK, status);
|
||||
EXPECT_EQ((size_t)0, gotValue->value.int64Values.size());
|
||||
}
|
||||
|
||||
TEST_F(DefaultVhalImplTest, testClearObd2FreezeFrameOneFrame) {
|
||||
// Get existing freeze frame info first.
|
||||
VehiclePropValue frameInfoRequest;
|
||||
frameInfoRequest.prop = OBD2_FREEZE_FRAME_INFO;
|
||||
StatusCode status;
|
||||
auto gotValue = mHal->get(frameInfoRequest, &status);
|
||||
ASSERT_EQ(StatusCode::OK, status);
|
||||
ASSERT_EQ((size_t)3, gotValue->value.int64Values.size());
|
||||
|
||||
VehiclePropValue clearRequest;
|
||||
int64_t timestamp = gotValue->value.int64Values[0];
|
||||
clearRequest.prop = OBD2_FREEZE_FRAME_CLEAR;
|
||||
clearRequest.value.int64Values.resize(1);
|
||||
clearRequest.value.int64Values[0] = timestamp;
|
||||
|
||||
// Try to clear the first frame.
|
||||
status = mHal->set(clearRequest);
|
||||
|
||||
// Get freeze frame info again.
|
||||
gotValue = mHal->get(frameInfoRequest, &status);
|
||||
|
||||
ASSERT_EQ(StatusCode::OK, status);
|
||||
// Now we should only have 2 frames.
|
||||
ASSERT_EQ((size_t)2, gotValue->value.int64Values.size());
|
||||
|
||||
// Try to get the deleted frame, should fail.
|
||||
VehiclePropValue frameRequest;
|
||||
frameRequest.prop = OBD2_FREEZE_FRAME;
|
||||
frameRequest.value.int64Values.resize(1);
|
||||
frameRequest.value.int64Values[0] = timestamp;
|
||||
|
||||
gotValue = mHal->get(frameRequest, &status);
|
||||
|
||||
ASSERT_EQ(StatusCode::INVALID_ARG, status);
|
||||
|
||||
// Clear the same frame again should fail.
|
||||
status = mHal->set(clearRequest);
|
||||
|
||||
ASSERT_EQ(StatusCode::INVALID_ARG, status);
|
||||
}
|
||||
|
||||
TEST_F(DefaultVhalImplTest, testGetUserPropertySetOnly) {
|
||||
VehiclePropValue value;
|
||||
value.prop = toInt(VehicleProperty::INITIAL_USER_INFO);
|
||||
StatusCode status;
|
||||
|
||||
mHal->get(value, &status);
|
||||
|
||||
ASSERT_EQ(StatusCode::INVALID_ARG, status);
|
||||
|
||||
value.prop = toInt(VehicleProperty::SWITCH_USER);
|
||||
|
||||
mHal->get(value, &status);
|
||||
|
||||
ASSERT_EQ(StatusCode::INVALID_ARG, status);
|
||||
|
||||
value.prop = toInt(VehicleProperty::CREATE_USER);
|
||||
|
||||
mHal->get(value, &status);
|
||||
|
||||
ASSERT_EQ(StatusCode::INVALID_ARG, status);
|
||||
|
||||
value.prop = toInt(VehicleProperty::REMOVE_USER);
|
||||
|
||||
mHal->get(value, &status);
|
||||
|
||||
ASSERT_EQ(StatusCode::INVALID_ARG, status);
|
||||
}
|
||||
|
||||
TEST_F(DefaultVhalImplTest, testGetUserIdAssoc) {
|
||||
VehiclePropValue value;
|
||||
value.prop = toInt(VehicleProperty::USER_IDENTIFICATION_ASSOCIATION);
|
||||
StatusCode status;
|
||||
|
||||
mHal->get(value, &status);
|
||||
|
||||
// Default returns NOT_AVAILABLE.
|
||||
ASSERT_EQ(StatusCode::NOT_AVAILABLE, status);
|
||||
|
||||
// This is the same example as used in User HAL Emulation doc.
|
||||
VehiclePropValue setValue = {
|
||||
.prop = toInt(VehicleProperty::USER_IDENTIFICATION_ASSOCIATION),
|
||||
.areaId = 1,
|
||||
.value.int32Values = {666, 1, 1, 2},
|
||||
};
|
||||
|
||||
status = mHal->set(setValue);
|
||||
|
||||
ASSERT_EQ(StatusCode::OK, status);
|
||||
|
||||
auto gotValue = mHal->get(value, &status);
|
||||
|
||||
ASSERT_EQ(StatusCode::OK, status);
|
||||
ASSERT_EQ((size_t)4, gotValue->value.int32Values.size());
|
||||
EXPECT_EQ(1, gotValue->areaId);
|
||||
EXPECT_EQ(666, gotValue->value.int32Values[0]);
|
||||
EXPECT_EQ(1, gotValue->value.int32Values[1]);
|
||||
EXPECT_EQ(1, gotValue->value.int32Values[2]);
|
||||
EXPECT_EQ(2, gotValue->value.int32Values[3]);
|
||||
EXPECT_EQ(toInt(VehicleProperty::USER_IDENTIFICATION_ASSOCIATION), gotValue->prop);
|
||||
}
|
||||
|
||||
TEST_F(DefaultVhalImplTest, testSwitchUser) {
|
||||
// This is the same example as used in User HAL Emulation doc.
|
||||
VehiclePropValue setValue = {
|
||||
.prop = toInt(VehicleProperty::SWITCH_USER),
|
||||
.areaId = 1,
|
||||
.value.int32Values = {666, 3, 2},
|
||||
};
|
||||
|
||||
auto status = mHal->set(setValue);
|
||||
|
||||
ASSERT_EQ(StatusCode::OK, status);
|
||||
|
||||
// Simulate a request from Android side.
|
||||
setValue = {
|
||||
.prop = toInt(VehicleProperty::SWITCH_USER),
|
||||
.areaId = 0,
|
||||
.value.int32Values = {666, 3},
|
||||
};
|
||||
// Clear existing events.
|
||||
mEventQueue.flush();
|
||||
|
||||
status = mHal->set(setValue);
|
||||
|
||||
ASSERT_EQ(StatusCode::OK, status);
|
||||
|
||||
// Should generate an event for user hal response.
|
||||
auto events = mEventQueue.flush();
|
||||
ASSERT_EQ((size_t)1, events.size());
|
||||
EXPECT_EQ(1, events[0]->areaId);
|
||||
EXPECT_EQ(toInt(VehicleProperty::SWITCH_USER), events[0]->prop);
|
||||
ASSERT_EQ((size_t)3, events[0]->value.int32Values.size());
|
||||
EXPECT_EQ(666, events[0]->value.int32Values[0]);
|
||||
EXPECT_EQ(3, events[0]->value.int32Values[1]);
|
||||
EXPECT_EQ(2, events[0]->value.int32Values[2]);
|
||||
|
||||
// Try to get switch_user again, should return default value.
|
||||
status = mHal->set(setValue);
|
||||
ASSERT_EQ(StatusCode::OK, status);
|
||||
|
||||
events = mEventQueue.flush();
|
||||
ASSERT_EQ((size_t)1, events.size());
|
||||
EXPECT_EQ(0, events[0]->areaId);
|
||||
EXPECT_EQ(toInt(VehicleProperty::SWITCH_USER), events[0]->prop);
|
||||
ASSERT_EQ((size_t)3, events[0]->value.int32Values.size());
|
||||
// Request ID
|
||||
EXPECT_EQ(666, events[0]->value.int32Values[0]);
|
||||
// VEHICLE_RESPONSE
|
||||
EXPECT_EQ(3, events[0]->value.int32Values[1]);
|
||||
// SUCCESS
|
||||
EXPECT_EQ(1, events[0]->value.int32Values[2]);
|
||||
}
|
||||
|
||||
TEST_F(DefaultVhalImplTest, testCreateUser) {
|
||||
// This is the same example as used in User HAL Emulation doc.
|
||||
VehiclePropValue setValue = {
|
||||
.prop = toInt(VehicleProperty::CREATE_USER),
|
||||
.areaId = 1,
|
||||
.value.int32Values = {666, 2},
|
||||
};
|
||||
|
||||
auto status = mHal->set(setValue);
|
||||
|
||||
ASSERT_EQ(StatusCode::OK, status);
|
||||
|
||||
// Simulate a request from Android side.
|
||||
setValue = {
|
||||
.prop = toInt(VehicleProperty::CREATE_USER),
|
||||
.areaId = 0,
|
||||
.value.int32Values = {666},
|
||||
};
|
||||
// Clear existing events.
|
||||
mEventQueue.flush();
|
||||
|
||||
status = mHal->set(setValue);
|
||||
|
||||
ASSERT_EQ(StatusCode::OK, status);
|
||||
|
||||
// Should generate an event for user hal response.
|
||||
auto events = mEventQueue.flush();
|
||||
ASSERT_EQ((size_t)1, events.size());
|
||||
EXPECT_EQ(1, events[0]->areaId);
|
||||
EXPECT_EQ(toInt(VehicleProperty::CREATE_USER), events[0]->prop);
|
||||
ASSERT_EQ((size_t)2, events[0]->value.int32Values.size());
|
||||
EXPECT_EQ(666, events[0]->value.int32Values[0]);
|
||||
EXPECT_EQ(2, events[0]->value.int32Values[1]);
|
||||
|
||||
// Try to get create_user again, should return default value.
|
||||
status = mHal->set(setValue);
|
||||
ASSERT_EQ(StatusCode::OK, status);
|
||||
|
||||
events = mEventQueue.flush();
|
||||
ASSERT_EQ((size_t)1, events.size());
|
||||
EXPECT_EQ(0, events[0]->areaId);
|
||||
EXPECT_EQ(toInt(VehicleProperty::CREATE_USER), events[0]->prop);
|
||||
ASSERT_EQ((size_t)2, events[0]->value.int32Values.size());
|
||||
// Request ID
|
||||
EXPECT_EQ(666, events[0]->value.int32Values[0]);
|
||||
// SUCCESS
|
||||
EXPECT_EQ(1, events[0]->value.int32Values[1]);
|
||||
}
|
||||
|
||||
TEST_F(DefaultVhalImplTest, testInitialUserInfo) {
|
||||
// This is the same example as used in User HAL Emulation doc.
|
||||
VehiclePropValue setValue = {
|
||||
.prop = toInt(VehicleProperty::INITIAL_USER_INFO),
|
||||
.areaId = 1,
|
||||
.value.int32Values = {666, 1, 11},
|
||||
};
|
||||
|
||||
auto status = mHal->set(setValue);
|
||||
|
||||
ASSERT_EQ(StatusCode::OK, status);
|
||||
|
||||
// Simulate a request from Android side.
|
||||
setValue = {
|
||||
.prop = toInt(VehicleProperty::INITIAL_USER_INFO),
|
||||
.areaId = 0,
|
||||
.value.int32Values = {3},
|
||||
};
|
||||
// Clear existing events.
|
||||
mEventQueue.flush();
|
||||
|
||||
status = mHal->set(setValue);
|
||||
|
||||
ASSERT_EQ(StatusCode::OK, status);
|
||||
|
||||
// Should generate an event for user hal response.
|
||||
auto events = mEventQueue.flush();
|
||||
ASSERT_EQ((size_t)1, events.size());
|
||||
EXPECT_EQ(1, events[0]->areaId);
|
||||
EXPECT_EQ(toInt(VehicleProperty::INITIAL_USER_INFO), events[0]->prop);
|
||||
ASSERT_EQ((size_t)3, events[0]->value.int32Values.size());
|
||||
EXPECT_EQ(3, events[0]->value.int32Values[0]);
|
||||
EXPECT_EQ(1, events[0]->value.int32Values[1]);
|
||||
EXPECT_EQ(11, events[0]->value.int32Values[2]);
|
||||
|
||||
// Try to get create_user again, should return default value.
|
||||
status = mHal->set(setValue);
|
||||
ASSERT_EQ(StatusCode::OK, status);
|
||||
|
||||
events = mEventQueue.flush();
|
||||
ASSERT_EQ((size_t)1, events.size());
|
||||
EXPECT_EQ(0, events[0]->areaId);
|
||||
EXPECT_EQ(toInt(VehicleProperty::INITIAL_USER_INFO), events[0]->prop);
|
||||
ASSERT_EQ((size_t)4, events[0]->value.int32Values.size());
|
||||
// Request ID
|
||||
EXPECT_EQ(3, events[0]->value.int32Values[0]);
|
||||
// ACTION: DEFAULT
|
||||
EXPECT_EQ(0, events[0]->value.int32Values[1]);
|
||||
// User id: 0
|
||||
EXPECT_EQ(0, events[0]->value.int32Values[2]);
|
||||
// Flags: 0
|
||||
EXPECT_EQ(0, events[0]->value.int32Values[3]);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1000000,
|
||||
"areaId": 0,
|
||||
"value": 8,
|
||||
// GEAR_SELECTION
|
||||
"prop": 289408000
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,10 @@
|
||||
[
|
||||
{
|
||||
"timestamp": 1000000,
|
||||
// HVAC_LEFT
|
||||
"areaId": 49,
|
||||
"value": 30,
|
||||
// HVAC_TEMPERATURE_SET
|
||||
"prop": 358614275
|
||||
}
|
||||
]
|
||||
@@ -20,7 +20,7 @@
|
||||
{
|
||||
"timestamp": 4000000,
|
||||
"areaId": 0,
|
||||
"value": 4,
|
||||
"value": 10,
|
||||
"prop": 289408000
|
||||
}
|
||||
]
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
// Copyright (C) 2021 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.
|
||||
|
||||
// Library used to emulate User HAL behavior through lshal debug requests.
|
||||
cc_library {
|
||||
name: "android.hardware.automotive.vehicle@2.0-emulated-user-hal-lib",
|
||||
vendor: true,
|
||||
defaults: ["vhal_v2_0_target_defaults"],
|
||||
srcs: ["EmulatedUserHal.cpp"],
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libutils",
|
||||
"libcutils",
|
||||
],
|
||||
local_include_dirs: ["include"],
|
||||
export_include_dirs: ["include"],
|
||||
whole_static_libs: [
|
||||
"android.hardware.automotive.vehicle@2.0-user-hal-helper-lib",
|
||||
],
|
||||
}
|
||||
@@ -0,0 +1,353 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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 "EmulatedUserHal"
|
||||
|
||||
#include <cutils/log.h>
|
||||
#include <utils/SystemClock.h>
|
||||
|
||||
#include "UserHalHelper.h"
|
||||
|
||||
#include "EmulatedUserHal.h"
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace automotive {
|
||||
namespace vehicle {
|
||||
namespace V2_0 {
|
||||
|
||||
namespace impl {
|
||||
|
||||
namespace {
|
||||
|
||||
using android::base::Error;
|
||||
using android::base::Result;
|
||||
|
||||
constexpr int32_t INITIAL_USER_INFO = static_cast<int32_t>(VehicleProperty::INITIAL_USER_INFO);
|
||||
constexpr int32_t SWITCH_USER = static_cast<int32_t>(VehicleProperty::SWITCH_USER);
|
||||
constexpr int32_t CREATE_USER = static_cast<int32_t>(VehicleProperty::CREATE_USER);
|
||||
constexpr int32_t REMOVE_USER = static_cast<int32_t>(VehicleProperty::REMOVE_USER);
|
||||
constexpr int32_t USER_IDENTIFICATION_ASSOCIATION =
|
||||
static_cast<int32_t>(VehicleProperty::USER_IDENTIFICATION_ASSOCIATION);
|
||||
|
||||
Result<int32_t> getRequestId(const VehiclePropValue& value) {
|
||||
if (value.value.int32Values.size() < 1) {
|
||||
return Error(static_cast<int>(StatusCode::INVALID_ARG))
|
||||
<< "no int32values on " << toString(value);
|
||||
}
|
||||
return value.value.int32Values[0];
|
||||
}
|
||||
|
||||
Result<SwitchUserMessageType> getSwitchUserMessageType(const VehiclePropValue& value) {
|
||||
if (value.value.int32Values.size() < 2) {
|
||||
return Error(static_cast<int>(StatusCode::INVALID_ARG))
|
||||
<< "missing switch user message type " << toString(value);
|
||||
}
|
||||
return user_hal_helper::verifyAndCast<SwitchUserMessageType>(value.value.int32Values[1]);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool EmulatedUserHal::isSupported(int32_t prop) {
|
||||
switch (prop) {
|
||||
case INITIAL_USER_INFO:
|
||||
case SWITCH_USER:
|
||||
case CREATE_USER:
|
||||
case REMOVE_USER:
|
||||
case USER_IDENTIFICATION_ASSOCIATION:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Result<std::unique_ptr<VehiclePropValue>> EmulatedUserHal::onSetProperty(
|
||||
const VehiclePropValue& value) {
|
||||
ALOGV("onSetProperty(): %s", toString(value).c_str());
|
||||
|
||||
switch (value.prop) {
|
||||
case INITIAL_USER_INFO:
|
||||
return onSetInitialUserInfoResponse(value);
|
||||
case SWITCH_USER:
|
||||
return onSetSwitchUserResponse(value);
|
||||
case CREATE_USER:
|
||||
return onSetCreateUserResponse(value);
|
||||
case REMOVE_USER:
|
||||
ALOGI("REMOVE_USER is FYI only, nothing to do...");
|
||||
return {};
|
||||
case USER_IDENTIFICATION_ASSOCIATION:
|
||||
return onSetUserIdentificationAssociation(value);
|
||||
default:
|
||||
return Error(static_cast<int>(StatusCode::INVALID_ARG))
|
||||
<< "Unsupported property: " << toString(value);
|
||||
}
|
||||
}
|
||||
|
||||
Result<std::unique_ptr<VehiclePropValue>> EmulatedUserHal::onGetProperty(
|
||||
const VehiclePropValue& value) {
|
||||
ALOGV("onGetProperty(%s)", toString(value).c_str());
|
||||
switch (value.prop) {
|
||||
case INITIAL_USER_INFO:
|
||||
case SWITCH_USER:
|
||||
case CREATE_USER:
|
||||
case REMOVE_USER:
|
||||
ALOGE("onGetProperty(): %d is only supported on SET", value.prop);
|
||||
return Error(static_cast<int>(StatusCode::INVALID_ARG)) << "only supported on SET";
|
||||
case USER_IDENTIFICATION_ASSOCIATION:
|
||||
return onGetUserIdentificationAssociation(value);
|
||||
default:
|
||||
ALOGE("onGetProperty(): %d is not supported", value.prop);
|
||||
return Error(static_cast<int>(StatusCode::INVALID_ARG)) << "not supported by User HAL";
|
||||
}
|
||||
}
|
||||
|
||||
Result<std::unique_ptr<VehiclePropValue>> EmulatedUserHal::onGetUserIdentificationAssociation(
|
||||
const VehiclePropValue& value) {
|
||||
if (mSetUserIdentificationAssociationResponseFromCmd == nullptr) {
|
||||
return defaultUserIdentificationAssociation(value);
|
||||
}
|
||||
ALOGI("get(USER_IDENTIFICATION_ASSOCIATION): returning %s",
|
||||
toString(*mSetUserIdentificationAssociationResponseFromCmd).c_str());
|
||||
auto newValue = std::unique_ptr<VehiclePropValue>(
|
||||
new VehiclePropValue(*mSetUserIdentificationAssociationResponseFromCmd));
|
||||
auto requestId = getRequestId(value);
|
||||
if (requestId.ok()) {
|
||||
// Must use the same requestId
|
||||
newValue->value.int32Values[0] = *requestId;
|
||||
} else {
|
||||
ALOGE("get(USER_IDENTIFICATION_ASSOCIATION): no requestId on %s", toString(value).c_str());
|
||||
}
|
||||
return newValue;
|
||||
}
|
||||
|
||||
Result<std::unique_ptr<VehiclePropValue>> EmulatedUserHal::onSetInitialUserInfoResponse(
|
||||
const VehiclePropValue& value) {
|
||||
auto requestId = getRequestId(value);
|
||||
if (!requestId.ok()) {
|
||||
ALOGE("Failed to get requestId on set(INITIAL_USER_INFO): %s",
|
||||
requestId.error().message().c_str());
|
||||
return requestId.error();
|
||||
}
|
||||
|
||||
if (value.areaId != 0) {
|
||||
ALOGD("set(INITIAL_USER_INFO) called from lshal; storing it: %s", toString(value).c_str());
|
||||
mInitialUserResponseFromCmd.reset(new VehiclePropValue(value));
|
||||
return {};
|
||||
}
|
||||
|
||||
ALOGD("set(INITIAL_USER_INFO) called from Android: %s", toString(value).c_str());
|
||||
if (mInitialUserResponseFromCmd != nullptr) {
|
||||
ALOGI("replying INITIAL_USER_INFO with lshal value: %s",
|
||||
toString(*mInitialUserResponseFromCmd).c_str());
|
||||
return sendUserHalResponse(std::move(mInitialUserResponseFromCmd), *requestId);
|
||||
}
|
||||
|
||||
// Returns default response
|
||||
auto updatedValue = user_hal_helper::toVehiclePropValue(InitialUserInfoResponse{
|
||||
.requestId = *requestId,
|
||||
.action = InitialUserInfoResponseAction::DEFAULT,
|
||||
});
|
||||
ALOGI("no lshal response; replying with InitialUserInfoResponseAction::DEFAULT: %s",
|
||||
toString(*updatedValue).c_str());
|
||||
return updatedValue;
|
||||
}
|
||||
|
||||
Result<std::unique_ptr<VehiclePropValue>> EmulatedUserHal::onSetSwitchUserResponse(
|
||||
const VehiclePropValue& value) {
|
||||
auto requestId = getRequestId(value);
|
||||
if (!requestId.ok()) {
|
||||
ALOGE("Failed to get requestId on set(SWITCH_USER): %s",
|
||||
requestId.error().message().c_str());
|
||||
return requestId.error();
|
||||
}
|
||||
|
||||
auto messageType = getSwitchUserMessageType(value);
|
||||
if (!messageType.ok()) {
|
||||
ALOGE("Failed to get messageType on set(SWITCH_USER): %s",
|
||||
messageType.error().message().c_str());
|
||||
return messageType.error();
|
||||
}
|
||||
|
||||
if (value.areaId != 0) {
|
||||
if (*messageType == SwitchUserMessageType::VEHICLE_REQUEST) {
|
||||
// User HAL can also request a user switch, so we need to check it first
|
||||
ALOGD("set(SWITCH_USER) called from lshal to emulate a vehicle request: %s",
|
||||
toString(value).c_str());
|
||||
return std::unique_ptr<VehiclePropValue>(new VehiclePropValue(value));
|
||||
}
|
||||
// Otherwise, we store it
|
||||
ALOGD("set(SWITCH_USER) called from lshal; storing it: %s", toString(value).c_str());
|
||||
mSwitchUserResponseFromCmd.reset(new VehiclePropValue(value));
|
||||
return {};
|
||||
}
|
||||
ALOGD("set(SWITCH_USER) called from Android: %s", toString(value).c_str());
|
||||
|
||||
if (mSwitchUserResponseFromCmd != nullptr) {
|
||||
ALOGI("replying SWITCH_USER with lshal value: %s",
|
||||
toString(*mSwitchUserResponseFromCmd).c_str());
|
||||
return sendUserHalResponse(std::move(mSwitchUserResponseFromCmd), *requestId);
|
||||
}
|
||||
|
||||
if (*messageType == SwitchUserMessageType::LEGACY_ANDROID_SWITCH ||
|
||||
*messageType == SwitchUserMessageType::ANDROID_POST_SWITCH) {
|
||||
ALOGI("request is %s; ignoring it", toString(*messageType).c_str());
|
||||
return {};
|
||||
}
|
||||
|
||||
// Returns default response
|
||||
auto updatedValue = user_hal_helper::toVehiclePropValue(SwitchUserResponse{
|
||||
.requestId = *requestId,
|
||||
.messageType = SwitchUserMessageType::VEHICLE_RESPONSE,
|
||||
.status = SwitchUserStatus::SUCCESS,
|
||||
});
|
||||
ALOGI("no lshal response; replying with VEHICLE_RESPONSE / SUCCESS: %s",
|
||||
toString(*updatedValue).c_str());
|
||||
return updatedValue;
|
||||
}
|
||||
|
||||
Result<std::unique_ptr<VehiclePropValue>> EmulatedUserHal::onSetCreateUserResponse(
|
||||
const VehiclePropValue& value) {
|
||||
auto requestId = getRequestId(value);
|
||||
if (!requestId.ok()) {
|
||||
ALOGE("Failed to get requestId on set(CREATE_USER): %s",
|
||||
requestId.error().message().c_str());
|
||||
return requestId.error();
|
||||
}
|
||||
|
||||
if (value.areaId != 0) {
|
||||
ALOGD("set(CREATE_USER) called from lshal; storing it: %s", toString(value).c_str());
|
||||
mCreateUserResponseFromCmd.reset(new VehiclePropValue(value));
|
||||
return {};
|
||||
}
|
||||
ALOGD("set(CREATE_USER) called from Android: %s", toString(value).c_str());
|
||||
|
||||
if (mCreateUserResponseFromCmd != nullptr) {
|
||||
ALOGI("replying CREATE_USER with lshal value: %s",
|
||||
toString(*mCreateUserResponseFromCmd).c_str());
|
||||
return sendUserHalResponse(std::move(mCreateUserResponseFromCmd), *requestId);
|
||||
}
|
||||
|
||||
// Returns default response
|
||||
auto updatedValue = user_hal_helper::toVehiclePropValue(CreateUserResponse{
|
||||
.requestId = *requestId,
|
||||
.status = CreateUserStatus::SUCCESS,
|
||||
});
|
||||
ALOGI("no lshal response; replying with SUCCESS: %s", toString(*updatedValue).c_str());
|
||||
return updatedValue;
|
||||
}
|
||||
|
||||
Result<std::unique_ptr<VehiclePropValue>> EmulatedUserHal::onSetUserIdentificationAssociation(
|
||||
const VehiclePropValue& value) {
|
||||
auto requestId = getRequestId(value);
|
||||
if (!requestId.ok()) {
|
||||
ALOGE("Failed to get requestId on set(USER_IDENTIFICATION_ASSOCIATION): %s",
|
||||
requestId.error().message().c_str());
|
||||
return requestId.error();
|
||||
}
|
||||
|
||||
if (value.areaId != 0) {
|
||||
ALOGD("set(USER_IDENTIFICATION_ASSOCIATION) called from lshal; storing it: %s",
|
||||
toString(value).c_str());
|
||||
mSetUserIdentificationAssociationResponseFromCmd.reset(new VehiclePropValue(value));
|
||||
return {};
|
||||
}
|
||||
ALOGD("set(USER_IDENTIFICATION_ASSOCIATION) called from Android: %s", toString(value).c_str());
|
||||
|
||||
if (mSetUserIdentificationAssociationResponseFromCmd != nullptr) {
|
||||
ALOGI("replying USER_IDENTIFICATION_ASSOCIATION with lshal value: %s",
|
||||
toString(*mSetUserIdentificationAssociationResponseFromCmd).c_str());
|
||||
// Not moving response so it can be used on GET requests
|
||||
auto copy = std::unique_ptr<VehiclePropValue>(
|
||||
new VehiclePropValue(*mSetUserIdentificationAssociationResponseFromCmd));
|
||||
return sendUserHalResponse(std::move(copy), *requestId);
|
||||
}
|
||||
// Returns default response
|
||||
return defaultUserIdentificationAssociation(value);
|
||||
}
|
||||
|
||||
Result<std::unique_ptr<VehiclePropValue>> EmulatedUserHal::defaultUserIdentificationAssociation(
|
||||
const VehiclePropValue& request) {
|
||||
// TODO(b/159498909): return a response with NOT_ASSOCIATED_ANY_USER for all requested types
|
||||
ALOGE("no lshal response for %s; replying with NOT_AVAILABLE", toString(request).c_str());
|
||||
return Error(static_cast<int>(StatusCode::NOT_AVAILABLE)) << "not set by lshal";
|
||||
}
|
||||
|
||||
Result<std::unique_ptr<VehiclePropValue>> EmulatedUserHal::sendUserHalResponse(
|
||||
std::unique_ptr<VehiclePropValue> response, int32_t requestId) {
|
||||
switch (response->areaId) {
|
||||
case 1:
|
||||
ALOGD("returning response with right request id");
|
||||
response->value.int32Values[0] = requestId;
|
||||
break;
|
||||
case 2:
|
||||
ALOGD("returning response with wrong request id");
|
||||
response->value.int32Values[0] = -requestId;
|
||||
break;
|
||||
case 3:
|
||||
ALOGD("not generating a property change event because of lshal prop: %s",
|
||||
toString(*response).c_str());
|
||||
return Error(static_cast<int>(StatusCode::NOT_AVAILABLE))
|
||||
<< "not generating a property change event because of lshal prop: "
|
||||
<< toString(*response);
|
||||
default:
|
||||
ALOGE("invalid action on lshal response: %s", toString(*response).c_str());
|
||||
return Error(static_cast<int>(StatusCode::INTERNAL_ERROR))
|
||||
<< "invalid action on lshal response: " << toString(*response);
|
||||
}
|
||||
|
||||
ALOGD("updating property to: %s", toString(*response).c_str());
|
||||
return response;
|
||||
}
|
||||
|
||||
std::string EmulatedUserHal::showDumpHelp() {
|
||||
return fmt::format("{}: dumps state used for user management\n", kUserHalDumpOption);
|
||||
}
|
||||
|
||||
std::string EmulatedUserHal::dump(std::string indent) {
|
||||
std::string info;
|
||||
if (mInitialUserResponseFromCmd != nullptr) {
|
||||
info += fmt::format("{}InitialUserInfo response: {}\n", indent.c_str(),
|
||||
toString(*mInitialUserResponseFromCmd).c_str());
|
||||
} else {
|
||||
info += fmt::format("{}No InitialUserInfo response\n", indent.c_str());
|
||||
}
|
||||
if (mSwitchUserResponseFromCmd != nullptr) {
|
||||
info += fmt::format("{}SwitchUser response: {}\n", indent.c_str(),
|
||||
toString(*mSwitchUserResponseFromCmd).c_str());
|
||||
} else {
|
||||
info += fmt::format("{}No SwitchUser response\n", indent.c_str());
|
||||
}
|
||||
if (mCreateUserResponseFromCmd != nullptr) {
|
||||
info += fmt::format("{}CreateUser response: {}\n", indent.c_str(),
|
||||
toString(*mCreateUserResponseFromCmd).c_str());
|
||||
} else {
|
||||
info += fmt::format("{}No CreateUser response\n", indent.c_str());
|
||||
}
|
||||
if (mSetUserIdentificationAssociationResponseFromCmd != nullptr) {
|
||||
info += fmt::format("{}SetUserIdentificationAssociation response: {}\n", indent.c_str(),
|
||||
toString(*mSetUserIdentificationAssociationResponseFromCmd).c_str());
|
||||
} else {
|
||||
info += fmt::format("{}No SetUserIdentificationAssociation response\n", indent.c_str());
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
} // namespace impl
|
||||
|
||||
} // namespace V2_0
|
||||
} // namespace vehicle
|
||||
} // namespace automotive
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright (C) 2020 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.
|
||||
*/
|
||||
|
||||
#ifndef android_hardware_automotive_vehicle_V2_0_impl_EmulatedUserHal_H_
|
||||
#define android_hardware_automotive_vehicle_V2_0_impl_EmulatedUserHal_H_
|
||||
|
||||
#include <android-base/format.h>
|
||||
#include <android-base/result.h>
|
||||
|
||||
#include <android/hardware/automotive/vehicle/2.0/types.h>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace automotive {
|
||||
namespace vehicle {
|
||||
namespace V2_0 {
|
||||
|
||||
namespace impl {
|
||||
|
||||
constexpr char kUserHalDumpOption[] = "--user-hal";
|
||||
|
||||
/**
|
||||
* Class used to emulate a real User HAL behavior through lshal debug requests.
|
||||
*/
|
||||
class EmulatedUserHal {
|
||||
public:
|
||||
EmulatedUserHal() {}
|
||||
|
||||
~EmulatedUserHal() = default;
|
||||
|
||||
/**
|
||||
* Checks if the emulator can handle the property.
|
||||
*/
|
||||
bool isSupported(int32_t prop);
|
||||
|
||||
/**
|
||||
* Lets the emulator set the property.
|
||||
*
|
||||
* @return updated property and StatusCode
|
||||
*/
|
||||
android::base::Result<std::unique_ptr<VehiclePropValue>> onSetProperty(
|
||||
const VehiclePropValue& value);
|
||||
|
||||
/**
|
||||
* Gets the property value from the emulator.
|
||||
*
|
||||
* @return property value and StatusCode
|
||||
*/
|
||||
android::base::Result<std::unique_ptr<VehiclePropValue>> onGetProperty(
|
||||
const VehiclePropValue& value);
|
||||
|
||||
/**
|
||||
* Shows the User HAL emulation help.
|
||||
*/
|
||||
std::string showDumpHelp();
|
||||
|
||||
/**
|
||||
* Dump its contents.
|
||||
*/
|
||||
std::string dump(std::string indent);
|
||||
|
||||
private:
|
||||
/**
|
||||
* INITIAL_USER_INFO is called by Android when it starts, and it's expecting a property change
|
||||
* indicating what the initial user should be.
|
||||
*
|
||||
* During normal circumstances, the emulator will reply right away, passing a response if
|
||||
* InitialUserInfoResponseAction::DEFAULT (so Android could use its own logic to decide which
|
||||
* user to boot).
|
||||
*
|
||||
* But during development / testing, the behavior can be changed using lshal dump, which must
|
||||
* use the areaId to indicate what should happen next.
|
||||
*
|
||||
* So, the behavior of set(INITIAL_USER_INFO) is:
|
||||
*
|
||||
* - if it has an areaId, store the property into mInitialUserResponseFromCmd (as it was called
|
||||
* by lshal).
|
||||
* - else if mInitialUserResponseFromCmd is not set, return a response with the same request id
|
||||
* and InitialUserInfoResponseAction::DEFAULT
|
||||
* - else the behavior is defined by the areaId on mInitialUserResponseFromCmd:
|
||||
* - if it's 1, reply with mInitialUserResponseFromCmd and the right request id
|
||||
* - if it's 2, reply with mInitialUserResponseFromCmd but a wrong request id (so Android can
|
||||
* test this error scenario)
|
||||
* - if it's 3, then don't send a property change (so Android can emulate a timeout)
|
||||
*
|
||||
*/
|
||||
android::base::Result<std::unique_ptr<VehiclePropValue>> onSetInitialUserInfoResponse(
|
||||
const VehiclePropValue& value);
|
||||
|
||||
/**
|
||||
* Used to emulate SWITCH_USER - see onSetInitialUserInfoResponse() for usage.
|
||||
*/
|
||||
android::base::Result<std::unique_ptr<VehiclePropValue>> onSetSwitchUserResponse(
|
||||
const VehiclePropValue& value);
|
||||
|
||||
/**
|
||||
* Used to emulate CREATE_USER - see onSetInitialUserInfoResponse() for usage.
|
||||
*/
|
||||
android::base::Result<std::unique_ptr<VehiclePropValue>> onSetCreateUserResponse(
|
||||
const VehiclePropValue& value);
|
||||
|
||||
/**
|
||||
* Used to emulate set USER_IDENTIFICATION_ASSOCIATION - see onSetInitialUserInfoResponse() for
|
||||
* usage.
|
||||
*/
|
||||
android::base::Result<std::unique_ptr<VehiclePropValue>> onSetUserIdentificationAssociation(
|
||||
const VehiclePropValue& value);
|
||||
|
||||
/**
|
||||
* Used to emulate get USER_IDENTIFICATION_ASSOCIATION - see onSetInitialUserInfoResponse() for
|
||||
* usage.
|
||||
*/
|
||||
android::base::Result<std::unique_ptr<VehiclePropValue>> onGetUserIdentificationAssociation(
|
||||
const VehiclePropValue& value);
|
||||
|
||||
/**
|
||||
* Creates a default USER_IDENTIFICATION_ASSOCIATION when it was not set by lshal.
|
||||
*/
|
||||
android::base::Result<std::unique_ptr<VehiclePropValue>> defaultUserIdentificationAssociation(
|
||||
const VehiclePropValue& request);
|
||||
|
||||
android::base::Result<std::unique_ptr<VehiclePropValue>> sendUserHalResponse(
|
||||
std::unique_ptr<VehiclePropValue> response, int32_t requestId);
|
||||
|
||||
std::unique_ptr<VehiclePropValue> mInitialUserResponseFromCmd;
|
||||
std::unique_ptr<VehiclePropValue> mSwitchUserResponseFromCmd;
|
||||
std::unique_ptr<VehiclePropValue> mCreateUserResponseFromCmd;
|
||||
std::unique_ptr<VehiclePropValue> mSetUserIdentificationAssociationResponseFromCmd;
|
||||
};
|
||||
|
||||
} // namespace impl
|
||||
|
||||
} // namespace V2_0
|
||||
} // namespace vehicle
|
||||
} // namespace automotive
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
|
||||
#endif // android_hardware_automotive_vehicle_V2_0_impl_EmulatedUserHal_H_
|
||||
Reference in New Issue
Block a user