From db4f577dc5e4c7427da7d50d6f13d544f9a2ff39 Mon Sep 17 00:00:00 2001 From: Yu Shan Date: Thu, 19 Oct 2023 18:24:05 -0700 Subject: [PATCH] Override subscribe/unsubscribe. Override subscribe/unsubscribe in FakeVehicleHardware, now it will only generate property change events for subscribed properties. Test: atest DefaultVehicleHalTest android.car.cts.CarPropertyManagerTest Bug: 306262618 Change-Id: Ice39f059820d4ec6039acb4daf9975514f2eb22b --- .../hardware/include/FakeVehicleHardware.h | 19 +- .../hardware/src/FakeVehicleHardware.cpp | 120 +++++-- .../hardware/test/FakeVehicleHardwareTest.cpp | 308 +++++++++++------- 3 files changed, 300 insertions(+), 147 deletions(-) diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h b/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h index 844bea5a81..6115c4917f 100644 --- a/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h +++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h @@ -91,9 +91,13 @@ class FakeVehicleHardware : public IVehicleHardware { void registerOnPropertySetErrorEvent( std::unique_ptr callback) override; - // Update the sample rate for the [propId, areaId] pair. - aidl::android::hardware::automotive::vehicle::StatusCode updateSampleRate( - int32_t propId, int32_t areaId, float sampleRate) override; + // Subscribe to a new [propId, areaId] or change the update rate. + aidl::android::hardware::automotive::vehicle::StatusCode subscribe( + aidl::android::hardware::automotive::vehicle::SubscribeOptions options) override; + + // Unsubscribe to a [propId, areaId]. + aidl::android::hardware::automotive::vehicle::StatusCode unsubscribe(int32_t propId, + int32_t areaId) override; protected: // mValuePool is also used in mServerSidePropStore. @@ -154,6 +158,7 @@ class FakeVehicleHardware : public IVehicleHardware { mRecurrentActions GUARDED_BY(mLock); std::unordered_map mSavedProps GUARDED_BY(mLock); + std::unordered_set mSubOnChangePropIdAreaIds GUARDED_BY(mLock); // PendingRequestHandler is thread-safe. mutable PendingRequestHandler @@ -176,7 +181,8 @@ class FakeVehicleHardware : public IVehicleHardware { void storePropInitialValue(const ConfigDeclaration& config); // The callback that would be called when a vehicle property value change happens. void onValueChangeCallback( - const aidl::android::hardware::automotive::vehicle::VehiclePropValue& value); + const aidl::android::hardware::automotive::vehicle::VehiclePropValue& value) + EXCLUDES(mLock); // Load the config files in format '*.json' from the directory and parse the config files // into a map from property ID to ConfigDeclarations. void loadPropConfigsFromDir(const std::string& dirPath, @@ -262,6 +268,11 @@ class FakeVehicleHardware : public IVehicleHardware { void generateVendorConfigs( std::vector&) const; + aidl::android::hardware::automotive::vehicle::StatusCode subscribePropIdAreaIdLocked( + int32_t propId, int32_t areaId, float sampleRateHz, + aidl::android::hardware::automotive::vehicle::VehiclePropertyChangeMode changeMode) + REQUIRES(mLock); + static aidl::android::hardware::automotive::vehicle::VehiclePropValue createHwInputKeyProp( aidl::android::hardware::automotive::vehicle::VehicleHwKeyInputAction action, int32_t keyCode, int32_t targetDisplay); diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp index ee24fbda9c..bc66f6d902 100644 --- a/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp +++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp @@ -60,6 +60,8 @@ using ::aidl::android::hardware::automotive::vehicle::RawPropValues; using ::aidl::android::hardware::automotive::vehicle::SetValueRequest; using ::aidl::android::hardware::automotive::vehicle::SetValueResult; using ::aidl::android::hardware::automotive::vehicle::StatusCode; +using ::aidl::android::hardware::automotive::vehicle::SubscribeOptions; +using ::aidl::android::hardware::automotive::vehicle::toString; using ::aidl::android::hardware::automotive::vehicle::VehicleApPowerStateReport; using ::aidl::android::hardware::automotive::vehicle::VehicleApPowerStateReq; using ::aidl::android::hardware::automotive::vehicle::VehicleArea; @@ -67,6 +69,7 @@ using ::aidl::android::hardware::automotive::vehicle::VehicleHwKeyInputAction; using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig; using ::aidl::android::hardware::automotive::vehicle::VehicleProperty; using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyAccess; +using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyChangeMode; using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyGroup; using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyStatus; using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType; @@ -1926,43 +1929,85 @@ void FakeVehicleHardware::registerOnPropertySetErrorEvent( mOnPropertySetErrorCallback = std::move(callback); } -StatusCode FakeVehicleHardware::updateSampleRate(int32_t propId, int32_t areaId, float sampleRate) { - // DefaultVehicleHal makes sure that sampleRate must be within minSampleRate and maxSampleRate. - // For fake implementation, we would write the same value with a new timestamp into propStore - // at sample rate. - std::scoped_lock lockGuard(mLock); +StatusCode FakeVehicleHardware::subscribe(SubscribeOptions options) { + int32_t propId = options.propId; + auto configResult = mServerSidePropStore->getConfig(propId); + if (!configResult.ok()) { + ALOGE("subscribe: property: %" PRId32 " is not supported", propId); + return StatusCode::INVALID_ARG; + } + + std::scoped_lock lockGuard(mLock); + for (int areaId : options.areaIds) { + if (StatusCode status = subscribePropIdAreaIdLocked(propId, areaId, options.sampleRate, + configResult.value()->changeMode); + status != StatusCode::OK) { + return status; + } + } + return StatusCode::OK; +} + +StatusCode FakeVehicleHardware::subscribePropIdAreaIdLocked(int32_t propId, int32_t areaId, + float sampleRateHz, + VehiclePropertyChangeMode changeMode) { + PropIdAreaId propIdAreaId{ + .propId = propId, + .areaId = areaId, + }; + switch (changeMode) { + case VehiclePropertyChangeMode::STATIC: + ALOGW("subscribe to a static property, do nothing."); + return StatusCode::OK; + case VehiclePropertyChangeMode::ON_CHANGE: + mSubOnChangePropIdAreaIds.insert(std::move(propIdAreaId)); + return StatusCode::OK; + case VehiclePropertyChangeMode::CONTINUOUS: + if (sampleRateHz == 0.f) { + ALOGE("Must not use sample rate 0 for a continuous property"); + return StatusCode::INTERNAL_ERROR; + } + if (mRecurrentActions.find(propIdAreaId) != mRecurrentActions.end()) { + mRecurrentTimer->unregisterTimerCallback(mRecurrentActions[propIdAreaId]); + } + int64_t intervalInNanos = static_cast(1'000'000'000. / sampleRateHz); + auto action = std::make_shared([this, propId, areaId] { + // Refresh the property value. In real implementation, this should poll the latest + // value from vehicle bus. Here, we are just refreshing the existing value with a + // new timestamp. + auto result = getValue(VehiclePropValue{ + .areaId = areaId, + .prop = propId, + .value = {}, + }); + if (!result.ok()) { + // Failed to read current value, skip refreshing. + return; + } + result.value()->timestamp = elapsedRealtimeNano(); + // For continuous properties, we must generate a new onPropertyChange event + // periodically according to the sample rate. + mServerSidePropStore->writeValue(std::move(result.value()), /*updateStatus=*/true, + VehiclePropertyStore::EventMode::ALWAYS); + }); + mRecurrentTimer->registerTimerCallback(intervalInNanos, action); + mRecurrentActions[propIdAreaId] = action; + return StatusCode::OK; + } +} + +StatusCode FakeVehicleHardware::unsubscribe(int32_t propId, int32_t areaId) { + std::scoped_lock lockGuard(mLock); PropIdAreaId propIdAreaId{ .propId = propId, .areaId = areaId, }; if (mRecurrentActions.find(propIdAreaId) != mRecurrentActions.end()) { mRecurrentTimer->unregisterTimerCallback(mRecurrentActions[propIdAreaId]); + mRecurrentActions.erase(propIdAreaId); } - if (sampleRate == 0) { - return StatusCode::OK; - } - int64_t interval = static_cast(1'000'000'000. / sampleRate); - auto action = std::make_shared([this, propId, areaId] { - // Refresh the property value. In real implementation, this should poll the latest value - // from vehicle bus. Here, we are just refreshing the existing value with a new timestamp. - auto result = getValue(VehiclePropValue{ - .areaId = areaId, - .prop = propId, - .value = {}, - }); - if (!result.ok()) { - // Failed to read current value, skip refreshing. - return; - } - result.value()->timestamp = elapsedRealtimeNano(); - // For continuous properties, we must generate a new onPropertyChange event periodically - // according to the sample rate. - mServerSidePropStore->writeValue(std::move(result.value()), /*updateStatus=*/true, - VehiclePropertyStore::EventMode::ALWAYS); - }); - mRecurrentTimer->registerTimerCallback(interval, action); - mRecurrentActions[propIdAreaId] = action; + mSubOnChangePropIdAreaIds.erase(propIdAreaId); return StatusCode::OK; } @@ -1971,6 +2016,23 @@ void FakeVehicleHardware::onValueChangeCallback(const VehiclePropValue& value) { return; } + PropIdAreaId propIdAreaId{ + .propId = value.prop, + .areaId = value.areaId, + }; + + { + std::scoped_lock lockGuard(mLock); + if (mRecurrentActions.find(propIdAreaId) == mRecurrentActions.end() && + mSubOnChangePropIdAreaIds.find(propIdAreaId) == mSubOnChangePropIdAreaIds.end()) { + if (FAKE_VEHICLEHARDWARE_DEBUG) { + ALOGD("The updated property value: %s is not subscribed, ignore", + value.toString().c_str()); + } + return; + } + } + std::vector updatedValues; updatedValues.push_back(value); (*mOnPropertyChangeCallback)(std::move(updatedValues)); diff --git a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp index cf9beee0fb..85eefa4774 100644 --- a/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp +++ b/automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp @@ -72,6 +72,7 @@ using ::aidl::android::hardware::automotive::vehicle::RawPropValues; using ::aidl::android::hardware::automotive::vehicle::SetValueRequest; using ::aidl::android::hardware::automotive::vehicle::SetValueResult; using ::aidl::android::hardware::automotive::vehicle::StatusCode; +using ::aidl::android::hardware::automotive::vehicle::SubscribeOptions; using ::aidl::android::hardware::automotive::vehicle::VehicleApPowerStateReport; using ::aidl::android::hardware::automotive::vehicle::VehicleApPowerStateReq; using ::aidl::android::hardware::automotive::vehicle::VehicleApPowerStateShutdownParam; @@ -149,6 +150,15 @@ class FakeVehicleHardwareTest : public ::testing::Test { mHardware = std::move(hardware); } + static SubscribeOptions newSubscribeOptions(int32_t propId, int32_t areaId, + float sampleRateHz) { + SubscribeOptions options; + options.areaIds = {areaId}; + options.propId = propId; + options.sampleRate = sampleRateHz; + return options; + } + StatusCode setValues(const std::vector& requests) { { std::scoped_lock lockGuard(mLock); @@ -336,6 +346,13 @@ class FakeVehicleHardwareTest : public ::testing::Test { return mEventCount[propIdAreaId]; } + void subscribe(int32_t propId, int32_t areaId, float sampleRateHz) { + ASSERT_EQ(StatusCode::OK, + getHardware()->subscribe(newSubscribeOptions(propId, areaId, sampleRateHz))) + << "failed to subscribe to propId: " << propId << "areaId: " << areaId + << ", sampleRateHz: " << sampleRateHz; + } + static void addSetValueRequest(std::vector& requests, std::vector& expectedResults, int64_t requestId, const VehiclePropValue& value, StatusCode expectedStatus) { @@ -370,24 +387,24 @@ class FakeVehicleHardwareTest : public ::testing::Test { } std::vector getTestPropValues() { - VehiclePropValue fuelCapacity = { - .prop = toInt(VehicleProperty::INFO_FUEL_CAPACITY), - .value = {.floatValues = {1.0}}, + VehiclePropValue oilLevel = { + .prop = toInt(VehicleProperty::ENGINE_OIL_LEVEL), + .value = {.int32Values = {1}}, }; - VehiclePropValue leftTirePressure = { - .prop = toInt(VehicleProperty::TIRE_PRESSURE), + VehiclePropValue leftHvacTemp = { + .prop = toInt(VehicleProperty::HVAC_TEMPERATURE_CURRENT), .value = {.floatValues = {170.0}}, - .areaId = WHEEL_FRONT_LEFT, + .areaId = SEAT_1_LEFT, }; - VehiclePropValue rightTirePressure = { - .prop = toInt(VehicleProperty::TIRE_PRESSURE), + VehiclePropValue rightHvacTemp = { + .prop = toInt(VehicleProperty::HVAC_TEMPERATURE_CURRENT), .value = {.floatValues = {180.0}}, - .areaId = WHEEL_FRONT_RIGHT, + .areaId = SEAT_1_RIGHT, }; - return {fuelCapacity, leftTirePressure, rightTirePressure}; + return {oilLevel, leftHvacTemp, rightHvacTemp}; } struct PropValueCmp { @@ -559,17 +576,13 @@ TEST_F(FakeVehicleHardwareTest, testSetValuesError) { ASSERT_THAT(getSetValueResults(), ContainerEq(expectedResults)); } -TEST_F(FakeVehicleHardwareTest, testRegisterOnPropertyChangeEvent) { - // We have already registered this callback in Setup, here we are registering again. - auto callback = std::make_unique( - [this](const std::vector& values) { onPropertyChangeEvent(values); }); - getHardware()->registerOnPropertyChangeEvent(std::move(callback)); - +TEST_F(FakeVehicleHardwareTest, testSetValues_getUpdateEvents) { auto testValues = getTestPropValues(); std::vector requests; std::vector expectedResults; int64_t requestId = 1; for (auto& value : testValues) { + subscribe(value.prop, value.areaId, /*sampleRateHz=*/0); addSetValueRequest(requests, expectedResults, requestId++, value, StatusCode::OK); } int64_t timestamp = elapsedRealtimeNano(); @@ -1624,27 +1637,30 @@ INSTANTIATE_TEST_SUITE_P( return info.param.name; }); -TEST_F(FakeVehicleHardwareTest, testSetWaitForVhalAfterCarServiceCrash) { - int32_t propId = toInt(VehicleProperty::AP_POWER_STATE_REPORT); +TEST_F(FakeVehicleHardwareTest, testSetWaitForVhal_alwaysTriggerEvents) { + int32_t powerReq = toInt(VehicleProperty::AP_POWER_STATE_REQ); + subscribe(powerReq, /*areaId*/ 0, /*sampleRateHz*/ 0); + + int32_t powerReport = toInt(VehicleProperty::AP_POWER_STATE_REPORT); VehiclePropValue request = VehiclePropValue{ - .prop = propId, + .prop = powerReport, .value.int32Values = {toInt(VehicleApPowerStateReport::WAIT_FOR_VHAL)}, }; - ASSERT_EQ(setValue(request), StatusCode::OK) << "failed to set property " << propId; + ASSERT_EQ(setValue(request), StatusCode::OK) << "failed to set property " << powerReport; // Clear existing events. clearChangedProperties(); // Simulate a Car Service crash, Car Service would restart and send the message again. - ASSERT_EQ(setValue(request), StatusCode::OK) << "failed to set property " << propId; + ASSERT_EQ(setValue(request), StatusCode::OK) << "failed to set property " << powerReport; std::vector events = getChangedProperties(); // Even though the state is already ON, we should receive another ON event. - ASSERT_EQ(events.size(), 1u); + ASSERT_EQ(events.size(), 1u) << "failed to receive on-change events AP_POWER_STATE_REQ ON"; // Erase the timestamp for comparison. events[0].timestamp = 0; auto expectedValue = VehiclePropValue{ - .prop = toInt(VehicleProperty::AP_POWER_STATE_REQ), + .prop = powerReq, .status = VehiclePropertyStatus::AVAILABLE, .value.int32Values = {toInt(VehicleApPowerStateReq::ON), 0}, }; @@ -2015,6 +2031,22 @@ TEST_F(FakeVehicleHardwareTest, testSendAdasPropertiesState) { }, }, }; + + // First subscribe to all the properties that we will change. + for (auto& enabledToErrorStateProps : adasEnabledPropToAdasPropWithErrorState) { + std::unordered_set expectedChangedPropIds(enabledToErrorStateProps.second.begin(), + enabledToErrorStateProps.second.end()); + expectedChangedPropIds.insert(enabledToErrorStateProps.first); + + for (int32_t propId : expectedChangedPropIds) { + int32_t areaId = 0; + if (propId == toInt(VehicleProperty::BLIND_SPOT_WARNING_STATE)) { + areaId = toInt(VehicleAreaMirror::DRIVER_LEFT); + } + subscribe(propId, areaId, /*sampleRateHz*/ 0); + } + } + for (auto& enabledToErrorStateProps : adasEnabledPropToAdasPropWithErrorState) { int32_t adasEnabledPropertyId = enabledToErrorStateProps.first; StatusCode status = @@ -2095,9 +2127,16 @@ TEST_F(FakeVehicleHardwareTest, testGetUserIdAssoc) { } TEST_F(FakeVehicleHardwareTest, testSwitchUser) { + SubscribeOptions options; + int32_t propSwitchUser = toInt(VehicleProperty::SWITCH_USER); + options.propId = propSwitchUser; + options.areaIds = {0, 1}; + ASSERT_EQ(StatusCode::OK, getHardware()->subscribe(options)) + << "failed to subscribe to propId: " << propSwitchUser; + // This is the same example as used in User HAL Emulation doc. VehiclePropValue valueToSet = { - .prop = toInt(VehicleProperty::SWITCH_USER), + .prop = propSwitchUser, .areaId = 1, .value.int32Values = {666, 3, 2}, }; @@ -2108,7 +2147,7 @@ TEST_F(FakeVehicleHardwareTest, testSwitchUser) { // Simulate a request from Android side. VehiclePropValue switchUserRequest = { - .prop = toInt(VehicleProperty::SWITCH_USER), + .prop = propSwitchUser, .areaId = 0, .value.int32Values = {666, 3}, }; @@ -2138,7 +2177,7 @@ TEST_F(FakeVehicleHardwareTest, testSwitchUser) { events[0].timestamp = 0; auto expectedValue = VehiclePropValue{ .areaId = 0, - .prop = toInt(VehicleProperty::SWITCH_USER), + .prop = propSwitchUser, .value.int32Values = { // Request ID @@ -2153,6 +2192,13 @@ TEST_F(FakeVehicleHardwareTest, testSwitchUser) { } TEST_F(FakeVehicleHardwareTest, testCreateUser) { + SubscribeOptions options; + int32_t propCreateUser = toInt(VehicleProperty::CREATE_USER); + options.propId = propCreateUser; + options.areaIds = {0, 1}; + ASSERT_EQ(StatusCode::OK, getHardware()->subscribe(options)) + << "failed to subscribe to propId: " << propCreateUser; + // This is the same example as used in User HAL Emulation doc. VehiclePropValue valueToSet = { .prop = toInt(VehicleProperty::CREATE_USER), @@ -2166,7 +2212,7 @@ TEST_F(FakeVehicleHardwareTest, testCreateUser) { // Simulate a request from Android side. VehiclePropValue createUserRequest = { - .prop = toInt(VehicleProperty::CREATE_USER), + .prop = propCreateUser, .areaId = 0, .value.int32Values = {666}, }; @@ -2195,7 +2241,7 @@ TEST_F(FakeVehicleHardwareTest, testCreateUser) { events[0].timestamp = 0; auto expectedValue = VehiclePropValue{ .areaId = 0, - .prop = toInt(VehicleProperty::CREATE_USER), + .prop = propCreateUser, .value.int32Values = { // Request ID @@ -2208,9 +2254,16 @@ TEST_F(FakeVehicleHardwareTest, testCreateUser) { } TEST_F(FakeVehicleHardwareTest, testInitialUserInfo) { + SubscribeOptions options; + int32_t propInitialUserInfo = toInt(VehicleProperty::INITIAL_USER_INFO); + options.propId = propInitialUserInfo; + options.areaIds = {0, 1}; + ASSERT_EQ(StatusCode::OK, getHardware()->subscribe(options)) + << "failed to subscribe to propId: " << propInitialUserInfo; + // This is the same example as used in User HAL Emulation doc. VehiclePropValue valueToSet = { - .prop = toInt(VehicleProperty::INITIAL_USER_INFO), + .prop = propInitialUserInfo, .areaId = 1, .value.int32Values = {666, 1, 11}, }; @@ -2221,7 +2274,7 @@ TEST_F(FakeVehicleHardwareTest, testInitialUserInfo) { // Simulate a request from Android side. VehiclePropValue initialUserInfoRequest = { - .prop = toInt(VehicleProperty::INITIAL_USER_INFO), + .prop = propInitialUserInfo, .areaId = 0, .value.int32Values = {3}, }; @@ -2238,7 +2291,7 @@ TEST_F(FakeVehicleHardwareTest, testInitialUserInfo) { events[0].timestamp = 0; auto expectedValue = VehiclePropValue{ .areaId = 0, - .prop = toInt(VehicleProperty::INITIAL_USER_INFO), + .prop = propInitialUserInfo, .value.int32Values = {3, 1, 11}, }; EXPECT_EQ(events[0], expectedValue); @@ -2253,7 +2306,7 @@ TEST_F(FakeVehicleHardwareTest, testInitialUserInfo) { events[0].timestamp = 0; expectedValue = VehiclePropValue{ .areaId = 0, - .prop = toInt(VehicleProperty::INITIAL_USER_INFO), + .prop = propInitialUserInfo, .value.int32Values = { // Request ID @@ -2395,13 +2448,14 @@ TEST_F(FakeVehicleHardwareTest, testSaveRestoreProp) { } TEST_F(FakeVehicleHardwareTest, testDumpInjectEvent) { - int32_t prop = toInt(VehicleProperty::PERF_VEHICLE_SPEED); + int32_t prop = toInt(VehicleProperty::ENGINE_OIL_LEVEL); std::string propIdStr = std::to_string(prop); + subscribe(prop, /*areaId*/ 0, /*sampleRateHz*/ 0); + int64_t timestamp = elapsedRealtimeNano(); - // Inject an event with float value 123.4 and timestamp. DumpResult result = getHardware()->dump( - {"--inject-event", propIdStr, "-f", "123.4", "-t", std::to_string(timestamp)}); + {"--inject-event", propIdStr, "-i", "1234", "-t", std::to_string(timestamp)}); ASSERT_FALSE(result.callerShouldDumpState); ASSERT_THAT(result.buffer, @@ -2412,7 +2466,7 @@ TEST_F(FakeVehicleHardwareTest, testDumpInjectEvent) { ASSERT_EQ(events.size(), 1u); auto event = events[0]; ASSERT_EQ(event.timestamp, timestamp); - ASSERT_EQ(event.value.floatValues, std::vector({123.4})); + ASSERT_EQ(event.value.int32Values, std::vector({1234})); } TEST_F(FakeVehicleHardwareTest, testDumpInvalidOptions) { @@ -2755,9 +2809,13 @@ INSTANTIATE_TEST_SUITE_P( }); TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataLinear) { - // Start a fake linear data generator for vehicle speed at 0.1s interval. + // Start a fake linear data generator for engine oil level at 0.1s interval. // range: 0 - 100, current value: 30, step: 20. - std::string propIdString = StringPrintf("%d", toInt(VehicleProperty::PERF_VEHICLE_SPEED)); + int32_t prop = toInt(VehicleProperty::ENGINE_OIL_LEVEL); + + subscribe(prop, /*areaId*/ 0, /*sampleRateHz*/ 0); + + std::string propIdString = StringPrintf("%d", prop); std::vector options = {"--genfakedata", "--startlinear", propIdString, /*middleValue=*/"50", /*currentValue=*/"30", @@ -2770,15 +2828,14 @@ TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataLinear) { ASSERT_FALSE(result.callerShouldDumpState); ASSERT_THAT(result.buffer, HasSubstr("successfully")); - ASSERT_TRUE(waitForChangedProperties(toInt(VehicleProperty::PERF_VEHICLE_SPEED), 0, /*count=*/5, - milliseconds(1000))) + ASSERT_TRUE(waitForChangedProperties(prop, 0, /*count=*/5, milliseconds(1000))) << "not enough events generated for linear data generator"; int32_t value = 30; auto events = getChangedProperties(); for (size_t i = 0; i < 5; i++) { - ASSERT_EQ(1u, events[i].value.floatValues.size()); - EXPECT_EQ(static_cast(value), events[i].value.floatValues[0]); + ASSERT_EQ(1u, events[i].value.int32Values.size()); + EXPECT_EQ(value, events[i].value.int32Values[0]); value = (value + 20) % 100; } @@ -2794,7 +2851,7 @@ TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataLinear) { std::this_thread::sleep_for(std::chrono::milliseconds(200)); // There should be no new events generated. - EXPECT_EQ(0u, getEventCount(toInt(VehicleProperty::PERF_VEHICLE_SPEED), 0)); + EXPECT_EQ(0u, getEventCount(prop, 0)); } std::string getTestFilePath(const char* filename) { @@ -2803,6 +2860,8 @@ std::string getTestFilePath(const char* filename) { } TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataJson) { + subscribe(toInt(VehicleProperty::GEAR_SELECTION), /*areaId*/ 0, /*sampleRateHz*/ 0); + std::vector options = {"--genfakedata", "--startjson", "--path", getTestFilePath("prop.json"), "2"}; @@ -2829,6 +2888,8 @@ TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataJson) { } TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataJsonByContent) { + subscribe(toInt(VehicleProperty::GEAR_SELECTION), /*areaId*/ 0, /*sampleRateHz*/ 0); + std::vector options = { "--genfakedata", "--startjson", "--content", "[{\"timestamp\":1000000,\"areaId\":0,\"value\":8,\"prop\":289408000}]", "1"}; @@ -2903,8 +2964,11 @@ TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataJsonStopInvalidFile) { } TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataKeyPress) { + int32_t propHwKeyInput = toInt(VehicleProperty::HW_KEY_INPUT); std::vector options = {"--genfakedata", "--keypress", "1", "2"}; + subscribe(propHwKeyInput, /*areaId*/ 0, /*sampleRateHz*/ 0); + DumpResult result = getHardware()->dump(options); ASSERT_FALSE(result.callerShouldDumpState); @@ -2912,8 +2976,8 @@ TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataKeyPress) { auto events = getChangedProperties(); ASSERT_EQ(2u, events.size()); - EXPECT_EQ(toInt(VehicleProperty::HW_KEY_INPUT), events[0].prop); - EXPECT_EQ(toInt(VehicleProperty::HW_KEY_INPUT), events[1].prop); + EXPECT_EQ(propHwKeyInput, events[0].prop); + EXPECT_EQ(propHwKeyInput, events[1].prop); ASSERT_EQ(3u, events[0].value.int32Values.size()); ASSERT_EQ(3u, events[1].value.int32Values.size()); EXPECT_EQ(toInt(VehicleHwKeyInputAction::ACTION_DOWN), events[0].value.int32Values[0]); @@ -2925,8 +2989,11 @@ TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataKeyPress) { } TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataKeyInputV2) { + int32_t propHwKeyInputV2 = toInt(VehicleProperty::HW_KEY_INPUT_V2); std::vector options = {"--genfakedata", "--keyinputv2", "1", "2", "3", "4", "5"}; + subscribe(propHwKeyInputV2, /*areaId*/ 1, /*sampleRateHz*/ 0); + DumpResult result = getHardware()->dump(options); ASSERT_FALSE(result.callerShouldDumpState); @@ -2944,6 +3011,7 @@ TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataKeyInputV2) { } TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataMotionInput) { + int32_t propHwMotionInput = toInt(VehicleProperty::HW_MOTION_INPUT); std::vector options = {"--genfakedata", "--motioninput", "1", @@ -2966,6 +3034,8 @@ TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataMotionInput) { "65.5", "76.6"}; + subscribe(propHwMotionInput, /*areaId*/ 1, /*sampleRateHz*/ 0); + DumpResult result = getHardware()->dump(options); ASSERT_FALSE(result.callerShouldDumpState); @@ -2973,7 +3043,7 @@ TEST_F(FakeVehicleHardwareTest, testDebugGenFakeDataMotionInput) { auto events = getChangedProperties(); ASSERT_EQ(1u, events.size()); - EXPECT_EQ(toInt(VehicleProperty::HW_MOTION_INPUT), events[0].prop); + EXPECT_EQ(propHwMotionInput, events[0].prop); ASSERT_EQ(9u, events[0].value.int32Values.size()); EXPECT_EQ(2, events[0].value.int32Values[0]); EXPECT_EQ(3, events[0].value.int32Values[1]); @@ -3014,23 +3084,27 @@ TEST_F(FakeVehicleHardwareTest, testGetEchoReverseBytes) { ASSERT_EQ(result.value().value.byteValues, std::vector({0x04, 0x03, 0x02, 0x01})); } -TEST_F(FakeVehicleHardwareTest, testUpdateSampleRate) { +TEST_F(FakeVehicleHardwareTest, testSubscribeUnsubscribe_continuous) { int32_t propSpeed = toInt(VehicleProperty::PERF_VEHICLE_SPEED); int32_t propSteering = toInt(VehicleProperty::PERF_STEERING_ANGLE); int32_t areaId = 0; - getHardware()->updateSampleRate(propSpeed, areaId, 5); + + auto status = getHardware()->subscribe(newSubscribeOptions(propSpeed, areaId, 5)); + ASSERT_EQ(status, StatusCode::OK) << "failed to subscribe"; ASSERT_TRUE(waitForChangedProperties(propSpeed, areaId, /*count=*/5, milliseconds(1500))) << "not enough events generated for speed"; - getHardware()->updateSampleRate(propSteering, areaId, 10); + status = getHardware()->subscribe(newSubscribeOptions(propSteering, areaId, 10)); + ASSERT_EQ(status, StatusCode::OK) << "failed to subscribe"; ASSERT_TRUE(waitForChangedProperties(propSteering, areaId, /*count=*/10, milliseconds(1500))) << "not enough events generated for steering"; int64_t timestamp = elapsedRealtimeNano(); // Disable refreshing for propSpeed. - getHardware()->updateSampleRate(propSpeed, areaId, 0); + status = getHardware()->unsubscribe(propSpeed, areaId); + ASSERT_EQ(status, StatusCode::OK) << "failed to unsubscribe"; clearChangedProperties(); ASSERT_TRUE(waitForChangedProperties(propSteering, areaId, /*count=*/5, milliseconds(1500))) @@ -3043,12 +3117,58 @@ TEST_F(FakeVehicleHardwareTest, testUpdateSampleRate) { } } +TEST_F(FakeVehicleHardwareTest, testSubscribeUnusubscribe_onChange) { + int32_t propHvac = toInt(VehicleProperty::HVAC_TEMPERATURE_SET); + int32_t areaId = SEAT_1_LEFT; + + auto status = getHardware()->subscribe(newSubscribeOptions(propHvac, areaId, 0)); + ASSERT_EQ(status, StatusCode::OK) << "failed to subscribe"; + + status = setValue({ + .prop = propHvac, + .areaId = areaId, + .value.floatValues = {20.0f}, + }); + ASSERT_EQ(status, StatusCode::OK) << "failed to set hvac value"; + + ASSERT_TRUE(waitForChangedProperties(propHvac, areaId, /*count=*/1, milliseconds(100))) + << "not enough on change events generated for hvac"; + clearChangedProperties(); + + status = setValue({ + .prop = propHvac, + .areaId = areaId, + .value.floatValues = {21.0f}, + }); + ASSERT_EQ(status, StatusCode::OK) << "failed to set hvac value"; + + ASSERT_TRUE(waitForChangedProperties(propHvac, areaId, /*count=*/1, milliseconds(100))) + << "not enough on change events generated for hvac"; + clearChangedProperties(); + + status = getHardware()->unsubscribe(propHvac, areaId); + ASSERT_EQ(status, StatusCode::OK); + + status = setValue({ + .prop = propHvac, + .areaId = areaId, + .value.floatValues = {22.0f}, + }); + ASSERT_EQ(status, StatusCode::OK) << "failed to set hvac value"; + + ASSERT_FALSE(waitForChangedProperties(propHvac, areaId, /*count=*/1, milliseconds(100))) + << "must not receive on change events if the propId, areaId is unsubscribed"; +} + TEST_F(FakeVehicleHardwareTest, testSetHvacTemperatureValueSuggestion) { float CELSIUS = static_cast(toInt(VehicleUnit::CELSIUS)); float FAHRENHEIT = static_cast(toInt(VehicleUnit::FAHRENHEIT)); + int32_t propHvacTempValueSuggest = toInt(VehicleProperty::HVAC_TEMPERATURE_VALUE_SUGGESTION); + + subscribe(propHvacTempValueSuggest, HVAC_ALL, /*sampleRateHz*/ 0); VehiclePropValue floatArraySizeFour = { - .prop = toInt(VehicleProperty::HVAC_TEMPERATURE_VALUE_SUGGESTION), + .prop = propHvacTempValueSuggest, .areaId = HVAC_ALL, .value.floatValues = {0, CELSIUS, 0, 0}, }; @@ -3056,14 +3176,14 @@ TEST_F(FakeVehicleHardwareTest, testSetHvacTemperatureValueSuggestion) { EXPECT_EQ(status, StatusCode::OK); VehiclePropValue floatArraySizeZero = { - .prop = toInt(VehicleProperty::HVAC_TEMPERATURE_VALUE_SUGGESTION), + .prop = propHvacTempValueSuggest, .areaId = HVAC_ALL, }; status = setValue(floatArraySizeZero); EXPECT_EQ(status, StatusCode::INVALID_ARG); VehiclePropValue floatArraySizeFive = { - .prop = toInt(VehicleProperty::HVAC_TEMPERATURE_VALUE_SUGGESTION), + .prop = propHvacTempValueSuggest, .areaId = HVAC_ALL, .value.floatValues = {0, CELSIUS, 0, 0, 0}, }; @@ -3071,7 +3191,7 @@ TEST_F(FakeVehicleHardwareTest, testSetHvacTemperatureValueSuggestion) { EXPECT_EQ(status, StatusCode::INVALID_ARG); VehiclePropValue invalidUnit = { - .prop = toInt(VehicleProperty::HVAC_TEMPERATURE_VALUE_SUGGESTION), + .prop = propHvacTempValueSuggest, .areaId = HVAC_ALL, .value.floatValues = {0, 0, 0, 0}, }; @@ -3102,9 +3222,7 @@ TEST_F(FakeVehicleHardwareTest, testSetHvacTemperatureValueSuggestion) { .valuesToSet = { VehiclePropValue{ - .prop = toInt( - VehicleProperty:: - HVAC_TEMPERATURE_VALUE_SUGGESTION), + .prop = propHvacTempValueSuggest, .areaId = HVAC_ALL, .value.floatValues = {minTempInCelsius, CELSIUS, 0, 0}, }, @@ -3112,9 +3230,7 @@ TEST_F(FakeVehicleHardwareTest, testSetHvacTemperatureValueSuggestion) { .expectedValuesToGet = { VehiclePropValue{ - .prop = toInt( - VehicleProperty:: - HVAC_TEMPERATURE_VALUE_SUGGESTION), + .prop = propHvacTempValueSuggest, .areaId = HVAC_ALL, .value.floatValues = {minTempInCelsius, CELSIUS, minTempInCelsius, @@ -3127,9 +3243,7 @@ TEST_F(FakeVehicleHardwareTest, testSetHvacTemperatureValueSuggestion) { .valuesToSet = { VehiclePropValue{ - .prop = toInt( - VehicleProperty:: - HVAC_TEMPERATURE_VALUE_SUGGESTION), + .prop = propHvacTempValueSuggest, .areaId = HVAC_ALL, .value.floatValues = {minTempInFahrenheit, FAHRENHEIT, 0, 0}, @@ -3138,9 +3252,7 @@ TEST_F(FakeVehicleHardwareTest, testSetHvacTemperatureValueSuggestion) { .expectedValuesToGet = { VehiclePropValue{ - .prop = toInt( - VehicleProperty:: - HVAC_TEMPERATURE_VALUE_SUGGESTION), + .prop = propHvacTempValueSuggest, .areaId = HVAC_ALL, .value.floatValues = {minTempInFahrenheit, FAHRENHEIT, minTempInCelsius, @@ -3153,9 +3265,7 @@ TEST_F(FakeVehicleHardwareTest, testSetHvacTemperatureValueSuggestion) { .valuesToSet = { VehiclePropValue{ - .prop = toInt( - VehicleProperty:: - HVAC_TEMPERATURE_VALUE_SUGGESTION), + .prop = propHvacTempValueSuggest, .areaId = HVAC_ALL, .value.floatValues = {maxTempInCelsius, CELSIUS, 0, 0}, }, @@ -3163,9 +3273,7 @@ TEST_F(FakeVehicleHardwareTest, testSetHvacTemperatureValueSuggestion) { .expectedValuesToGet = { VehiclePropValue{ - .prop = toInt( - VehicleProperty:: - HVAC_TEMPERATURE_VALUE_SUGGESTION), + .prop = propHvacTempValueSuggest, .areaId = HVAC_ALL, .value.floatValues = {maxTempInCelsius, CELSIUS, maxTempInCelsius, @@ -3178,9 +3286,7 @@ TEST_F(FakeVehicleHardwareTest, testSetHvacTemperatureValueSuggestion) { .valuesToSet = { VehiclePropValue{ - .prop = toInt( - VehicleProperty:: - HVAC_TEMPERATURE_VALUE_SUGGESTION), + .prop = propHvacTempValueSuggest, .areaId = HVAC_ALL, .value.floatValues = {maxTempInFahrenheit, FAHRENHEIT, 0, 0}, @@ -3189,9 +3295,7 @@ TEST_F(FakeVehicleHardwareTest, testSetHvacTemperatureValueSuggestion) { .expectedValuesToGet = { VehiclePropValue{ - .prop = toInt( - VehicleProperty:: - HVAC_TEMPERATURE_VALUE_SUGGESTION), + .prop = propHvacTempValueSuggest, .areaId = HVAC_ALL, .value.floatValues = {maxTempInFahrenheit, FAHRENHEIT, maxTempInCelsius, @@ -3204,9 +3308,7 @@ TEST_F(FakeVehicleHardwareTest, testSetHvacTemperatureValueSuggestion) { .valuesToSet = { VehiclePropValue{ - .prop = toInt( - VehicleProperty:: - HVAC_TEMPERATURE_VALUE_SUGGESTION), + .prop = propHvacTempValueSuggest, .areaId = HVAC_ALL, .value.floatValues = {minTempInCelsius - 1, CELSIUS, 0, 0}, @@ -3215,9 +3317,7 @@ TEST_F(FakeVehicleHardwareTest, testSetHvacTemperatureValueSuggestion) { .expectedValuesToGet = { VehiclePropValue{ - .prop = toInt( - VehicleProperty:: - HVAC_TEMPERATURE_VALUE_SUGGESTION), + .prop = propHvacTempValueSuggest, .areaId = HVAC_ALL, .value.floatValues = {minTempInCelsius - 1, CELSIUS, minTempInCelsius, @@ -3230,9 +3330,7 @@ TEST_F(FakeVehicleHardwareTest, testSetHvacTemperatureValueSuggestion) { .valuesToSet = { VehiclePropValue{ - .prop = toInt( - VehicleProperty:: - HVAC_TEMPERATURE_VALUE_SUGGESTION), + .prop = propHvacTempValueSuggest, .areaId = HVAC_ALL, .value.floatValues = {minTempInFahrenheit - 1, FAHRENHEIT, 0, 0}, @@ -3241,9 +3339,7 @@ TEST_F(FakeVehicleHardwareTest, testSetHvacTemperatureValueSuggestion) { .expectedValuesToGet = { VehiclePropValue{ - .prop = toInt( - VehicleProperty:: - HVAC_TEMPERATURE_VALUE_SUGGESTION), + .prop = propHvacTempValueSuggest, .areaId = HVAC_ALL, .value.floatValues = {minTempInFahrenheit - 1, FAHRENHEIT, minTempInCelsius, @@ -3256,9 +3352,7 @@ TEST_F(FakeVehicleHardwareTest, testSetHvacTemperatureValueSuggestion) { .valuesToSet = { VehiclePropValue{ - .prop = toInt( - VehicleProperty:: - HVAC_TEMPERATURE_VALUE_SUGGESTION), + .prop = propHvacTempValueSuggest, .areaId = HVAC_ALL, .value.floatValues = {maxTempInCelsius + 1, CELSIUS, 0, 0}, @@ -3267,9 +3361,7 @@ TEST_F(FakeVehicleHardwareTest, testSetHvacTemperatureValueSuggestion) { .expectedValuesToGet = { VehiclePropValue{ - .prop = toInt( - VehicleProperty:: - HVAC_TEMPERATURE_VALUE_SUGGESTION), + .prop = propHvacTempValueSuggest, .areaId = HVAC_ALL, .value.floatValues = {maxTempInCelsius + 1, CELSIUS, maxTempInCelsius, @@ -3282,9 +3374,7 @@ TEST_F(FakeVehicleHardwareTest, testSetHvacTemperatureValueSuggestion) { .valuesToSet = { VehiclePropValue{ - .prop = toInt( - VehicleProperty:: - HVAC_TEMPERATURE_VALUE_SUGGESTION), + .prop = propHvacTempValueSuggest, .areaId = HVAC_ALL, .value.floatValues = {maxTempInFahrenheit + 1, FAHRENHEIT, 0, 0}, @@ -3293,9 +3383,7 @@ TEST_F(FakeVehicleHardwareTest, testSetHvacTemperatureValueSuggestion) { .expectedValuesToGet = { VehiclePropValue{ - .prop = toInt( - VehicleProperty:: - HVAC_TEMPERATURE_VALUE_SUGGESTION), + .prop = propHvacTempValueSuggest, .areaId = HVAC_ALL, .value.floatValues = {maxTempInFahrenheit + 1, FAHRENHEIT, maxTempInCelsius, @@ -3308,9 +3396,7 @@ TEST_F(FakeVehicleHardwareTest, testSetHvacTemperatureValueSuggestion) { .valuesToSet = { VehiclePropValue{ - .prop = toInt( - VehicleProperty:: - HVAC_TEMPERATURE_VALUE_SUGGESTION), + .prop = propHvacTempValueSuggest, .areaId = HVAC_ALL, .value.floatValues = {minTempInCelsius + incrementInCelsius * 2.5f, @@ -3320,9 +3406,7 @@ TEST_F(FakeVehicleHardwareTest, testSetHvacTemperatureValueSuggestion) { .expectedValuesToGet = { VehiclePropValue{ - .prop = toInt( - VehicleProperty:: - HVAC_TEMPERATURE_VALUE_SUGGESTION), + .prop = propHvacTempValueSuggest, .areaId = HVAC_ALL, .value.floatValues = {minTempInCelsius + incrementInCelsius * 2.5f, @@ -3338,9 +3422,7 @@ TEST_F(FakeVehicleHardwareTest, testSetHvacTemperatureValueSuggestion) { .valuesToSet = { VehiclePropValue{ - .prop = toInt( - VehicleProperty:: - HVAC_TEMPERATURE_VALUE_SUGGESTION), + .prop = propHvacTempValueSuggest, .areaId = HVAC_ALL, .value.floatValues = {minTempInFahrenheit + incrementInFahrenheit * @@ -3351,9 +3433,7 @@ TEST_F(FakeVehicleHardwareTest, testSetHvacTemperatureValueSuggestion) { .expectedValuesToGet = { VehiclePropValue{ - .prop = toInt( - VehicleProperty:: - HVAC_TEMPERATURE_VALUE_SUGGESTION), + .prop = propHvacTempValueSuggest, .areaId = HVAC_ALL, .value.floatValues = {minTempInFahrenheit +