From 4a23901e50466d05ed7246e840f983bd4e9fc545 Mon Sep 17 00:00:00 2001 From: Yu Shan Date: Tue, 20 Jul 2021 18:12:53 -0700 Subject: [PATCH 1/3] Support vendor override in default VHAL. Move vendor override property logic from emulated VHAL to default VHAL. If system property "persist.vendor.vhal_init_value_override" is set, then we would try to parse JSON files in "/vendor/etc/vhaloverride/" and use the values there to override the default initial values. Test: unit test Bug: 193831021 Change-Id: I6823aab3de284d5b1d96186bc9f1325d868fd8d6 --- automotive/vehicle/2.0/default/Android.bp | 6 +- .../vehicle/2.0/default/VehicleService.cpp | 4 +- .../impl/vhal_v2_0/DefaultVehicleHal.cpp | 68 ++++++-- .../impl/vhal_v2_0/DefaultVehicleHal.h | 23 ++- .../vhal_v2_0/DefaultVehicleHalServer.cpp | 33 ++++ .../impl/vhal_v2_0/DefaultVehicleHalServer.h | 10 ++ .../default/impl/vhal_v2_0/tests/Android.bp | 5 + .../vhal_v2_0/tests/DefaultVhalImpl_test.cpp | 158 ++++++++++++++++-- .../tests/override/gear_selection.json | 9 + .../tests/override/hvac_temperature_set.json | 10 ++ .../default/impl/vhal_v2_0/tests/prop.json | 2 +- 11 files changed, 293 insertions(+), 35 deletions(-) create mode 100644 automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/override/gear_selection.json create mode 100644 automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/override/hvac_temperature_set.json diff --git a/automotive/vehicle/2.0/default/Android.bp b/automotive/vehicle/2.0/default/Android.bp index 2e8ca66473..3be460c554 100644 --- a/automotive/vehicle/2.0/default/Android.bp +++ b/automotive/vehicle/2.0/default/Android.bp @@ -144,7 +144,6 @@ cc_library_static { ], 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 +212,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_override_json", + ":vhal_test_json", + ], test_suites: ["general-tests"], } diff --git a/automotive/vehicle/2.0/default/VehicleService.cpp b/automotive/vehicle/2.0/default/VehicleService.cpp index ba2a60638a..fae6e43377 100644 --- a/automotive/vehicle/2.0/default/VehicleService.cpp +++ b/automotive/vehicle/2.0/default/VehicleService.cpp @@ -20,7 +20,6 @@ #include -#include #include #include #include @@ -32,8 +31,7 @@ using namespace android::hardware::automotive::vehicle::V2_0; int main(int /* argc */, char* /* argv */ []) { auto store = std::make_unique(); auto connector = std::make_unique(); - auto userHal = connector->getEmulatedUserHal(); - auto hal = std::make_unique(store.get(), connector.get(), userHal); + auto hal = std::make_unique(store.get(), connector.get()); auto emulator = connector->getEmulator(); auto service = std::make_unique(hal.get()); connector->setValuePool(hal->getValuePool()); diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.cpp index 1b37cbd6fd..51ba6fd9c5 100644 --- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.cpp +++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.cpp @@ -17,10 +17,13 @@ #include #include +#include #include #include #include +#include +#include "PropertyUtils.h" #include "VehicleUtils.h" #include "DefaultVehicleHal.h" @@ -52,8 +55,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(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(); @@ -69,6 +94,7 @@ VehicleHal::VehiclePropValuePtr DefaultVehicleHal::get(const VehiclePropValue& r ALOGV("get(%d)", propId); VehiclePropValuePtr v = nullptr; + auto internalPropValue = mPropStore->readValueOrNull(requestedPropValue); if (internalPropValue != nullptr) { v = getValuePool()->obtain(*internalPropValue); @@ -81,10 +107,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 DefaultVehicleHal::listProperties() { @@ -258,6 +281,32 @@ StatusCode DefaultVehicleHal::set(const VehiclePropValue& propValue) { return StatusCode::INVALID_ARG; } + std::unordered_set 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 == 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 +347,7 @@ void DefaultVehicleHal::onCreate() { auto configs = mVehicleClient->getAllPropertyConfig(); for (const auto& cfg : configs) { - int32_t numAreas = isGlobalProp(cfg.prop) ? 0 : cfg.areaConfigs.size(); + 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 +364,7 @@ void DefaultVehicleHal::onCreate() { } mVehicleClient->triggerSendAllValues(); + registerHeartBeatEvent(); } @@ -344,14 +394,6 @@ VehicleHal::VehiclePropValuePtr DefaultVehicleHal::doInternalHealthCheck() { return v; } -VehicleHal::VehiclePropValuePtr DefaultVehicleHal::createVhalHeartBeatProp() { - VehicleHal::VehiclePropValuePtr v = getValuePool()->obtainInt64(uptimeMillis()); - v->prop = static_cast(VehicleProperty::VHAL_HEARTBEAT); - v->areaId = 0; - v->status = VehiclePropertyStatus::AVAILABLE; - return v; -} - void DefaultVehicleHal::onContinuousPropertyTimer(const std::vector& properties) { auto& pool = *getValuePool(); diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.h index af29143bc2..027dc8559a 100644 --- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.h +++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.h @@ -19,7 +19,7 @@ #include #include -#include "vhal_v2_0/VehiclePropertyStore.h" +#include #include "VehicleHalClient.h" @@ -55,10 +55,20 @@ 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& properties); + + // 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& 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 +79,7 @@ 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(); + // Create a VHAL heart beat property. VehicleHal::VehiclePropValuePtr createVhalHeartBeatProp(); private: @@ -78,6 +87,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 diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHalServer.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHalServer.cpp index f61a93b5bf..595a1d1eb1 100644 --- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHalServer.cpp +++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHalServer.cpp @@ -17,11 +17,13 @@ #define LOG_TAG "DefaultVehicleHalServer" #include +#include #include #include #include #include +#include #include #include "DefaultConfig.h" @@ -38,6 +40,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; @@ -74,6 +80,7 @@ DefaultVehicleHalServer::DefaultVehicleHalServer() { mServerSidePropStore.registerProperty(cfg); storePropInitialValue(it); } + maybeOverrideProperties(VENDOR_OVERRIDE_DIR); } void DefaultVehicleHalServer::sendAllValuesToClient() { @@ -559,6 +566,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 propValues = tmpGenerator.getAllEvents(); + for (const VehiclePropValue& prop : propValues) { + mServerSidePropStore.writeValue(prop, true); + } + } + closedir(dir); + } +} + } // namespace impl } // namespace V2_0 diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHalServer.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHalServer.h index aa6cf0bf49..5858325758 100644 --- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHalServer.h +++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHalServer.h @@ -72,6 +72,10 @@ class DefaultVehicleHalServer : public IVehicleServer { std::string getHelpInfo(); DumpResult genFakeData(const std::vector& 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 diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/Android.bp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/Android.bp index 576222380e..a7d5440b39 100644 --- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/Android.bp +++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/Android.bp @@ -16,3 +16,8 @@ filegroup { name: "vhal_test_json", srcs: ["prop.json"], } + +filegroup { + name: "vhal_test_override_json", + srcs: ["override/*"], +} diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultVhalImpl_test.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultVhalImpl_test.cpp index 891d8d60e3..4fb74f6660 100644 --- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultVhalImpl_test.cpp +++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultVhalImpl_test.cpp @@ -27,6 +27,32 @@ #include #include +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 mHal; + std::unique_ptr mConnector; + std::unique_ptr mPropStore; + VehiclePropValuePool mValueObjectPool; + android::ConcurrentQueue mEventQueue; + android::ConcurrentQueue 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 mHal; - std::unique_ptr mConnector; - std::unique_ptr mPropStore; - VehiclePropValuePool mValueObjectPool; - android::ConcurrentQueue mEventQueue; - android::ConcurrentQueue 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,106 @@ 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]); +} + } // namespace diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/override/gear_selection.json b/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/override/gear_selection.json new file mode 100644 index 0000000000..59666b8046 --- /dev/null +++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/override/gear_selection.json @@ -0,0 +1,9 @@ +[ + { + "timestamp": 1000000, + "areaId": 0, + "value": 8, + // GEAR_SELECTION + "prop": 289408000 + } +] diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/override/hvac_temperature_set.json b/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/override/hvac_temperature_set.json new file mode 100644 index 0000000000..93a97ed095 --- /dev/null +++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/override/hvac_temperature_set.json @@ -0,0 +1,10 @@ +[ + { + "timestamp": 1000000, + // HVAC_LEFT + "areaId": 49, + "value": 30, + // HVAC_TEMPERATURE_SET + "prop": 358614275 + } +] diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/prop.json b/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/prop.json index 2e77a44094..b881109237 100644 --- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/prop.json +++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/prop.json @@ -20,7 +20,7 @@ { "timestamp": 4000000, "areaId": 0, - "value": 4, + "value": 10, "prop": 289408000 } ] From 122eea67d3e4a10a125d8d8cc3d6157ceab4bdb6 Mon Sep 17 00:00:00 2001 From: Yu Shan Date: Tue, 20 Jul 2021 18:22:23 -0700 Subject: [PATCH 2/3] Add fake OBD2 frame logic to default VHAL. Test: unit test Bug: 193831021 Change-Id: Ie26cf0c5468984781bd6d7a0bf7edc2e0b2426e3 --- automotive/vehicle/2.0/default/Android.bp | 2 + .../impl/vhal_v2_0/DefaultVehicleHal.cpp | 40 +++- .../vhal_v2_0/DefaultVehicleHalServer.cpp | 5 + .../default/impl/vhal_v2_0/FakeObd2Frame.cpp | 184 ++++++++++++++++++ .../default/impl/vhal_v2_0/FakeObd2Frame.h | 48 +++++ .../vhal_v2_0/tests/DefaultVhalImpl_test.cpp | 141 ++++++++++++++ 6 files changed, 419 insertions(+), 1 deletion(-) create mode 100644 automotive/vehicle/2.0/default/impl/vhal_v2_0/FakeObd2Frame.cpp create mode 100644 automotive/vehicle/2.0/default/impl/vhal_v2_0/FakeObd2Frame.h diff --git a/automotive/vehicle/2.0/default/Android.bp b/automotive/vehicle/2.0/default/Android.bp index 3be460c554..8bf6bbea87 100644 --- a/automotive/vehicle/2.0/default/Android.bp +++ b/automotive/vehicle/2.0/default/Android.bp @@ -92,6 +92,7 @@ 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"], @@ -141,6 +142,7 @@ 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", diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.cpp index 51ba6fd9c5..74337b8b00 100644 --- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.cpp +++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.cpp @@ -23,6 +23,7 @@ #include #include +#include "FakeObd2Frame.h" #include "PropertyUtils.h" #include "VehicleUtils.h" @@ -94,6 +95,17 @@ VehicleHal::VehiclePropValuePtr DefaultVehicleHal::get(const VehiclePropValue& r ALOGV("get(%d)", propId); 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) { @@ -296,6 +308,9 @@ StatusCode DefaultVehicleHal::set(const VehiclePropValue& propValue) { } } + 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. @@ -347,6 +362,12 @@ void DefaultVehicleHal::onCreate() { auto configs = mVehicleClient->getAllPropertyConfig(); for (const auto& cfg : configs) { + 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++) { @@ -365,6 +386,9 @@ void DefaultVehicleHal::onCreate() { mVehicleClient->triggerSendAllValues(); + initObd2LiveFrame(mPropStore, *mPropStore->getConfigOrDie(OBD2_LIVE_FRAME)); + initObd2FreezeFrame(mPropStore, *mPropStore->getConfigOrDie(OBD2_FREEZE_FRAME)); + registerHeartBeatEvent(); } @@ -474,7 +498,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); } } diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHalServer.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHalServer.cpp index 595a1d1eb1..0aaa4c1fc6 100644 --- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHalServer.cpp +++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHalServer.cpp @@ -27,6 +27,7 @@ #include #include "DefaultConfig.h" +#include "FakeObd2Frame.h" #include "JsonFakeValueGenerator.h" #include "LinearFakeValueGenerator.h" @@ -78,6 +79,10 @@ 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); diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/FakeObd2Frame.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/FakeObd2Frame.cpp new file mode 100644 index 0000000000..d95584d2ee --- /dev/null +++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/FakeObd2Frame.cpp @@ -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 +#include +#include +#include +#include +#include + +namespace android { +namespace hardware { +namespace automotive { +namespace vehicle { +namespace V2_0 { + +namespace impl { + +namespace { + +std::unique_ptr fillDefaultObd2Frame(size_t numVendorIntegerSensors, + size_t numVendorFloatSensors) { + std::unique_ptr 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(propConfig.configArray[0]), + static_cast(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(propConfig.configArray[0]), + static_cast(propConfig.configArray[1])); + + static std::vector 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 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 diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/FakeObd2Frame.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/FakeObd2Frame.h new file mode 100644 index 0000000000..704964ca5e --- /dev/null +++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/FakeObd2Frame.h @@ -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 +#include + +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_ diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultVhalImpl_test.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultVhalImpl_test.cpp index 4fb74f6660..27c1f4d45f 100644 --- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultVhalImpl_test.cpp +++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultVhalImpl_test.cpp @@ -906,4 +906,145 @@ TEST_F(DefaultVhalImplTest, testVendorOverridePropertiesDirDoesNotExist) { 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 dtcs; + std::vector 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); +} + } // namespace From 56020bc1c7be8cc6d87e68b65eb0c5cb509c7c26 Mon Sep 17 00:00:00 2001 From: Yu Shan Date: Fri, 16 Jul 2021 15:51:16 -0700 Subject: [PATCH 3/3] Move emulatedUserHal to default VHAL. Test: Run on emulator. Verify user hal logic works. unit test. Bug: 193831021 Change-Id: I9b91205ac11837f31950018f2de12542df85e202 --- automotive/vehicle/2.0/default/Android.bp | 3 +- .../impl/vhal_v2_0/DefaultVehicleHal.cpp | 78 ++++ .../impl/vhal_v2_0/DefaultVehicleHal.h | 7 + .../vhal_v2_0/tests/DefaultVhalImpl_test.cpp | 216 +++++++++++ .../default/impl/vhal_v2_0/userhal/Android.bp | 31 ++ .../vhal_v2_0/userhal/EmulatedUserHal.cpp | 353 ++++++++++++++++++ .../userhal/include/EmulatedUserHal.h | 152 ++++++++ 7 files changed, 839 insertions(+), 1 deletion(-) create mode 100644 automotive/vehicle/2.0/default/impl/vhal_v2_0/userhal/Android.bp create mode 100644 automotive/vehicle/2.0/default/impl/vhal_v2_0/userhal/EmulatedUserHal.cpp create mode 100644 automotive/vehicle/2.0/default/impl/vhal_v2_0/userhal/include/EmulatedUserHal.h diff --git a/automotive/vehicle/2.0/default/Android.bp b/automotive/vehicle/2.0/default/Android.bp index 8bf6bbea87..2c3422e82d 100644 --- a/automotive/vehicle/2.0/default/Android.bp +++ b/automotive/vehicle/2.0/default/Android.bp @@ -97,6 +97,7 @@ cc_library_static { 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: [ @@ -215,8 +216,8 @@ cc_test { "android.hardware.automotive.vehicle@2.0-libproto-native", ], data: [ - ":vhal_test_override_json", ":vhal_test_json", + ":vhal_test_override_json", ], test_suites: ["general-tests"], } diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.cpp index 74337b8b00..25a698b9fc 100644 --- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.cpp +++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.cpp @@ -89,11 +89,38 @@ 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(); @@ -127,6 +154,36 @@ std::vector DefaultVehicleHal::listProperties() { } bool DefaultVehicleHal::dump(const hidl_handle& fd, const hidl_vec& 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); } @@ -285,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 @@ -293,6 +367,10 @@ StatusCode DefaultVehicleHal::set(const VehiclePropValue& propValue) { return StatusCode::INVALID_ARG; } + if (mEmulatedUserHal.isSupported(propValue.prop)) { + return setUserHalProp(propValue); + } + std::unordered_set powerProps(std::begin(kHvacPowerProperties), std::end(kHvacPowerProperties)); if (powerProps.count(propValue.prop)) { diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.h index 027dc8559a..7cd7ac2a38 100644 --- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.h +++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/DefaultVehicleHal.h @@ -21,6 +21,7 @@ #include #include +#include "EmulatedUserHal.h" #include "VehicleHalClient.h" namespace android { @@ -55,6 +56,7 @@ class DefaultVehicleHal : public VehicleHal { VehiclePropertyStore* mPropStore; RecurrentTimer mRecurrentTimer; VehicleHalClient* mVehicleClient; + 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. @@ -79,6 +81,11 @@ 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(); + // 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(); diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultVhalImpl_test.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultVhalImpl_test.cpp index 27c1f4d45f..2268df80a0 100644 --- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultVhalImpl_test.cpp +++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/tests/DefaultVhalImpl_test.cpp @@ -1047,4 +1047,220 @@ TEST_F(DefaultVhalImplTest, testClearObd2FreezeFrameOneFrame) { 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 diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/userhal/Android.bp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/userhal/Android.bp new file mode 100644 index 0000000000..0058d33a6a --- /dev/null +++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/userhal/Android.bp @@ -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", + ], +} diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/userhal/EmulatedUserHal.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/userhal/EmulatedUserHal.cpp new file mode 100644 index 0000000000..55dc01a4e6 --- /dev/null +++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/userhal/EmulatedUserHal.cpp @@ -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 +#include + +#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(VehicleProperty::INITIAL_USER_INFO); +constexpr int32_t SWITCH_USER = static_cast(VehicleProperty::SWITCH_USER); +constexpr int32_t CREATE_USER = static_cast(VehicleProperty::CREATE_USER); +constexpr int32_t REMOVE_USER = static_cast(VehicleProperty::REMOVE_USER); +constexpr int32_t USER_IDENTIFICATION_ASSOCIATION = + static_cast(VehicleProperty::USER_IDENTIFICATION_ASSOCIATION); + +Result getRequestId(const VehiclePropValue& value) { + if (value.value.int32Values.size() < 1) { + return Error(static_cast(StatusCode::INVALID_ARG)) + << "no int32values on " << toString(value); + } + return value.value.int32Values[0]; +} + +Result getSwitchUserMessageType(const VehiclePropValue& value) { + if (value.value.int32Values.size() < 2) { + return Error(static_cast(StatusCode::INVALID_ARG)) + << "missing switch user message type " << toString(value); + } + return user_hal_helper::verifyAndCast(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> 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(StatusCode::INVALID_ARG)) + << "Unsupported property: " << toString(value); + } +} + +Result> 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(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(StatusCode::INVALID_ARG)) << "not supported by User HAL"; + } +} + +Result> 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( + 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> 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> 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(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> 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> 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( + new VehiclePropValue(*mSetUserIdentificationAssociationResponseFromCmd)); + return sendUserHalResponse(std::move(copy), *requestId); + } + // Returns default response + return defaultUserIdentificationAssociation(value); +} + +Result> 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(StatusCode::NOT_AVAILABLE)) << "not set by lshal"; +} + +Result> EmulatedUserHal::sendUserHalResponse( + std::unique_ptr 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(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(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 diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/userhal/include/EmulatedUserHal.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/userhal/include/EmulatedUserHal.h new file mode 100644 index 0000000000..46f6d0f2dc --- /dev/null +++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/userhal/include/EmulatedUserHal.h @@ -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 +#include + +#include + +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> onSetProperty( + const VehiclePropValue& value); + + /** + * Gets the property value from the emulator. + * + * @return property value and StatusCode + */ + android::base::Result> 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> onSetInitialUserInfoResponse( + const VehiclePropValue& value); + + /** + * Used to emulate SWITCH_USER - see onSetInitialUserInfoResponse() for usage. + */ + android::base::Result> onSetSwitchUserResponse( + const VehiclePropValue& value); + + /** + * Used to emulate CREATE_USER - see onSetInitialUserInfoResponse() for usage. + */ + android::base::Result> onSetCreateUserResponse( + const VehiclePropValue& value); + + /** + * Used to emulate set USER_IDENTIFICATION_ASSOCIATION - see onSetInitialUserInfoResponse() for + * usage. + */ + android::base::Result> onSetUserIdentificationAssociation( + const VehiclePropValue& value); + + /** + * Used to emulate get USER_IDENTIFICATION_ASSOCIATION - see onSetInitialUserInfoResponse() for + * usage. + */ + android::base::Result> onGetUserIdentificationAssociation( + const VehiclePropValue& value); + + /** + * Creates a default USER_IDENTIFICATION_ASSOCIATION when it was not set by lshal. + */ + android::base::Result> defaultUserIdentificationAssociation( + const VehiclePropValue& request); + + android::base::Result> sendUserHalResponse( + std::unique_ptr response, int32_t requestId); + + std::unique_ptr mInitialUserResponseFromCmd; + std::unique_ptr mSwitchUserResponseFromCmd; + std::unique_ptr mCreateUserResponseFromCmd; + std::unique_ptr mSetUserIdentificationAssociationResponseFromCmd; +}; + +} // namespace impl + +} // namespace V2_0 +} // namespace vehicle +} // namespace automotive +} // namespace hardware +} // namespace android + +#endif // android_hardware_automotive_vehicle_V2_0_impl_EmulatedUserHal_H_