Loading automotive/vehicle/aidl/android/hardware/automotive/vehicle/IVehicle.aidl +42 −4 Original line number Diff line number Diff line Loading @@ -90,6 +90,14 @@ interface IVehicle { * area ID) are not allowed in a single call. This function must return * {@link StatusCode#INVALID_ARG} for duplicate properties. * * The {@link VehiclePropValue#timestamp} field in request is ignored. The * {@link VehiclePropValue#timestamp} field in {@link GetValueResult} must * be the system uptime since boot when the value changes for * ON_CHANGE property or when the value is checked according to polling rate * for CONTINUOUS property. Note that for CONTINUOUS property, VHAL client * reading the property multiple times between the polling interval will get * the same timestamp. * * @param callback A callback interface, whose 'onGetValues' would be called * after the value is fetched. Caller should use * {@code android-automotive-large-parcelable} library to parse the Loading @@ -104,7 +112,7 @@ interface IVehicle { * Set vehicle property values. * * The {@link IVehicleCallback#onSetValues} function would be called after * the values set request are sent through vehicle bus or are failed to set. * the values set request are sent through vehicle bus or failed to set. * If the bus protocol supports confirmation, the callback would be called * after getting the confirmation. * Loading Loading @@ -152,11 +160,36 @@ interface IVehicle { * Clients must be able to subscribe to multiple properties at a time * depending on data provided in options argument. * * For one callback, the is only one subscription for one property. * For one callback, there is only one subscription for one property. * A new subscription with a different sample rate would override the old * subscription. One property could be subscribed multiple times for * different callbacks. * * If error is returned, some of the properties failed to subscribe. * Caller is safe to try again, since subscribing to an already subscribed * property is okay. * * The specified sample rate is just a guidance. It is not guaranteed that * the sample rate is achievable depending on how the polling refresh rate * is. The actual property event rate might be higher/lower than the * specified sampleRate, for example, if the polling rate can be 5 times/s * or 10 times/s, subscribing to a sample rate of 7 might use the 5 times/s * polling rate, thus generating 5 events/s. We only require that on * average, the {@code minSampleRate} and {@code maxSampleRate} can be * achieved, all the sampleRate within min and max would on average * generates events with rate >= {@code minSampleRate} and <= * {@code maxSampleRate}. * * The {@link VehiclePropValue#timestamp} field for each property event must * be the system uptime since boot when the value changes for * ON_CHANGE property or when the value is checked according to polling rate * for CONTINUOUS property. Note that for CONTINUOUS property, VHAL client * reading the property multiple times between the polling interval will get * the same timestamp. * For example, if the polling rate for a property is 10 times/s, no matter * what the sampleRate specified in {@code options}, the timestamp for * the timestamp is updated 10 times/s. * * @param callback The subscription callbacks. * {@link IVehicleCallback#onPropertyEvent} would be called when a new * property event arrives. Loading Loading @@ -189,8 +222,13 @@ interface IVehicle { /** * Unsubscribes from property events. * * If 'callback' is not valid or 'propIds' were not subscribed for this * 'callback', this method must return {@link StatusCode#INVALID_ARG}. * If 'callback' is not valid this method must return * {@link StatusCode#INVALID_ARG}. If a specified propId was not subscribed * before, this method must ignore that propId. * * If error is returned, some of the properties failed to unsubscribe. * Caller is safe to try again, since unsubscribing an already unsubscribed * property is okay. * * @param callback The callback used in the previous subscription. * @param propIds The IDs for the properties to unsubscribe. Loading automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h +12 −5 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ #include <FakeObd2Frame.h> #include <FakeUserHal.h> #include <IVehicleHardware.h> #include <RecurrentTimer.h> #include <VehicleHalTypes.h> #include <VehiclePropertyStore.h> #include <android-base/parseint.h> Loading Loading @@ -82,6 +83,10 @@ class FakeVehicleHardware : public IVehicleHardware { void registerOnPropertySetErrorEvent( std::unique_ptr<const PropertySetErrorCallback> 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; protected: // mValuePool is also used in mServerSidePropStore. const std::shared_ptr<VehiclePropValuePool> mValuePool; Loading @@ -99,11 +104,13 @@ class FakeVehicleHardware : public IVehicleHardware { const std::unique_ptr<obd2frame::FakeObd2Frame> mFakeObd2Frame; const std::unique_ptr<FakeUserHal> mFakeUserHal; std::mutex mCallbackLock; std::unique_ptr<const PropertyChangeCallback> mOnPropertyChangeCallback GUARDED_BY(mCallbackLock); std::unique_ptr<const PropertySetErrorCallback> mOnPropertySetErrorCallback GUARDED_BY(mCallbackLock); // RecurrentTimer is thread-safe. std::unique_ptr<RecurrentTimer> mRecurrentTimer; std::mutex mLock; std::unique_ptr<const PropertyChangeCallback> mOnPropertyChangeCallback GUARDED_BY(mLock); std::unique_ptr<const PropertySetErrorCallback> mOnPropertySetErrorCallback GUARDED_BY(mLock); std::unordered_map<PropIdAreaId, std::shared_ptr<RecurrentTimer::Callback>, PropIdAreaIdHash> mRecurrentActions GUARDED_BY(mLock); void init(); // Stores the initial value to property store. Loading automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp +46 −10 Original line number Diff line number Diff line Loading @@ -66,6 +66,7 @@ using ::android::base::EqualsIgnoreCase; using ::android::base::Error; using ::android::base::ParseFloat; using ::android::base::Result; using ::android::base::ScopedLockAssertion; using ::android::base::StartsWith; using ::android::base::StringPrintf; Loading Loading @@ -131,18 +132,14 @@ void FakeVehicleHardware::storePropInitialValue(const defaultconfig::ConfigDecla } FakeVehicleHardware::FakeVehicleHardware() : mValuePool(new VehiclePropValuePool), mServerSidePropStore(new VehiclePropertyStore(mValuePool)), mFakeObd2Frame(new obd2frame::FakeObd2Frame(mServerSidePropStore)), mFakeUserHal(new FakeUserHal(mValuePool)) { init(); } : FakeVehicleHardware(std::make_unique<VehiclePropValuePool>()) {} FakeVehicleHardware::FakeVehicleHardware(std::unique_ptr<VehiclePropValuePool> valuePool) : mValuePool(std::move(valuePool)), mServerSidePropStore(new VehiclePropertyStore(mValuePool)), mFakeObd2Frame(new obd2frame::FakeObd2Frame(mServerSidePropStore)), mFakeUserHal(new FakeUserHal(mValuePool)) { mFakeUserHal(new FakeUserHal(mValuePool)), mRecurrentTimer(new RecurrentTimer()) { init(); } Loading Loading @@ -837,18 +834,57 @@ StatusCode FakeVehicleHardware::checkHealth() { void FakeVehicleHardware::registerOnPropertyChangeEvent( std::unique_ptr<const PropertyChangeCallback> callback) { std::scoped_lock<std::mutex> lockGuard(mCallbackLock); std::scoped_lock<std::mutex> lockGuard(mLock); mOnPropertyChangeCallback = std::move(callback); } void FakeVehicleHardware::registerOnPropertySetErrorEvent( std::unique_ptr<const PropertySetErrorCallback> callback) { std::scoped_lock<std::mutex> lockGuard(mCallbackLock); std::scoped_lock<std::mutex> lockGuard(mLock); 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<std::mutex> lockGuard(mLock); PropIdAreaId propIdAreaId{ .propId = propId, .areaId = areaId, }; if (mRecurrentActions.find(propIdAreaId) != mRecurrentActions.end()) { mRecurrentTimer->unregisterTimerCallback(mRecurrentActions[propIdAreaId]); } if (sampleRate == 0) { return StatusCode::OK; } int64_t interval = static_cast<int64_t>(1'000'000'000. / sampleRate); auto action = std::make_shared<RecurrentTimer::Callback>([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{ .prop = propId, .areaId = areaId, }); if (!result.ok()) { // Failed to read current value, skip refreshing. return; } result.value()->timestamp = elapsedRealtimeNano(); // Must remove the value before writing, otherwise, we would generate no update event since // the value is the same. mServerSidePropStore->removeValue(*result.value()); mServerSidePropStore->writeValue(std::move(result.value())); }); mRecurrentTimer->registerTimerCallback(interval, action); mRecurrentActions[propIdAreaId] = action; return StatusCode::OK; } void FakeVehicleHardware::onValueChangeCallback(const VehiclePropValue& value) { std::scoped_lock<std::mutex> lockGuard(mCallbackLock); std::scoped_lock<std::mutex> lockGuard(mLock); if (mOnPropertyChangeCallback == nullptr) { return; Loading automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp +80 −7 Original line number Diff line number Diff line Loading @@ -25,12 +25,15 @@ #include <android-base/expected.h> #include <android-base/file.h> #include <android-base/stringprintf.h> #include <android-base/thread_annotations.h> #include <gmock/gmock.h> #include <gtest/gtest.h> #include <utils/Log.h> #include <utils/SystemClock.h> #include <inttypes.h> #include <chrono> #include <condition_variable> #include <vector> namespace android { Loading @@ -53,6 +56,7 @@ using ::aidl::android::hardware::automotive::vehicle::VehicleProperty; using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyStatus; using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue; using ::android::base::expected; using ::android::base::ScopedLockAssertion; using ::android::base::StringPrintf; using ::android::base::unexpected; using ::testing::ContainerEq; Loading @@ -60,6 +64,8 @@ using ::testing::ContainsRegex; using ::testing::Eq; using ::testing::WhenSortedBy; using std::chrono::milliseconds; constexpr int INVALID_PROP_ID = 0; constexpr char CAR_MAKE[] = "Default Car"; Loading Loading @@ -158,30 +164,65 @@ class FakeVehicleHardwareTest : public ::testing::Test { } void onSetValues(std::vector<SetValueResult> results) { std::scoped_lock<std::mutex> lockGuard(mLock); for (auto& result : results) { mSetValueResults.push_back(result); } } const std::vector<SetValueResult>& getSetValueResults() { return mSetValueResults; } const std::vector<SetValueResult>& getSetValueResults() { std::scoped_lock<std::mutex> lockGuard(mLock); return mSetValueResults; } void onGetValues(std::vector<GetValueResult> results) { std::scoped_lock<std::mutex> lockGuard(mLock); for (auto& result : results) { mGetValueResults.push_back(result); } } const std::vector<GetValueResult>& getGetValueResults() { return mGetValueResults; } const std::vector<GetValueResult>& getGetValueResults() { std::scoped_lock<std::mutex> lockGuard(mLock); return mGetValueResults; } void onPropertyChangeEvent(std::vector<VehiclePropValue> values) { std::scoped_lock<std::mutex> lockGuard(mLock); for (auto& value : values) { mChangedProperties.push_back(value); PropIdAreaId propIdAreaId{ .propId = value.prop, .areaId = value.areaId, }; mEventCount[propIdAreaId]++; } mCv.notify_one(); } const std::vector<VehiclePropValue>& getChangedProperties() { std::scoped_lock<std::mutex> lockGuard(mLock); return mChangedProperties; } const std::vector<VehiclePropValue>& getChangedProperties() { return mChangedProperties; } bool waitForChangedProperties(int32_t propId, int32_t areaId, size_t count, milliseconds timeout) { PropIdAreaId propIdAreaId{ .propId = propId, .areaId = areaId, }; std::unique_lock<std::mutex> lk(mLock); return mCv.wait_for(lk, timeout, [this, propIdAreaId, count] { ScopedLockAssertion lockAssertion(mLock); return mEventCount[propIdAreaId] >= count; }); } void clearChangedProperties() { mChangedProperties.clear(); } void clearChangedProperties() { std::scoped_lock<std::mutex> lockGuard(mLock); mEventCount.clear(); mChangedProperties.clear(); } static void addSetValueRequest(std::vector<SetValueRequest>& requests, std::vector<SetValueResult>& expectedResults, int64_t requestId, Loading Loading @@ -246,11 +287,14 @@ class FakeVehicleHardwareTest : public ::testing::Test { private: FakeVehicleHardware mHardware; std::vector<SetValueResult> mSetValueResults; std::vector<GetValueResult> mGetValueResults; std::vector<VehiclePropValue> mChangedProperties; std::shared_ptr<IVehicleHardware::SetValuesCallback> mSetValuesCallback; std::shared_ptr<IVehicleHardware::GetValuesCallback> mGetValuesCallback; std::condition_variable mCv; std::mutex mLock; std::unordered_map<PropIdAreaId, size_t, PropIdAreaIdHash> mEventCount GUARDED_BY(mLock); std::vector<SetValueResult> mSetValueResults GUARDED_BY(mLock); std::vector<GetValueResult> mGetValueResults GUARDED_BY(mLock); std::vector<VehiclePropValue> mChangedProperties GUARDED_BY(mLock); }; TEST_F(FakeVehicleHardwareTest, testGetAllPropertyConfigs) { Loading Loading @@ -1510,6 +1554,35 @@ TEST_F(FakeVehicleHardwareTest, testGetEchoReverseBytes) { ASSERT_EQ(result.value().value.byteValues, std::vector<uint8_t>({0x04, 0x03, 0x02, 0x01})); } TEST_F(FakeVehicleHardwareTest, testUpdateSampleRate) { 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); ASSERT_TRUE(waitForChangedProperties(propSpeed, areaId, /*count=*/5, milliseconds(1500))) << "not enough events generated for speed"; getHardware()->updateSampleRate(propSteering, areaId, 10); 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); clearChangedProperties(); ASSERT_TRUE(waitForChangedProperties(propSteering, areaId, /*count=*/5, milliseconds(1500))) << "should still receive steering events after disable polling for speed"; auto updatedValues = getChangedProperties(); for (auto& value : updatedValues) { ASSERT_GE(value.timestamp, timestamp); ASSERT_EQ(value.prop, propSteering); ASSERT_EQ(value.areaId, areaId); } } } // namespace fake } // namespace vehicle } // namespace automotive Loading automotive/vehicle/aidl/impl/hardware/include/IVehicleHardware.h +29 −0 Original line number Diff line number Diff line Loading @@ -80,6 +80,35 @@ class IVehicleHardware { const std::vector<aidl::android::hardware::automotive::vehicle::GetValueRequest>& requests) const = 0; // Update the sampling rate for the specified property and the specified areaId (0 for global // property) if server supports it. The property must be a continuous property. // {@code sampleRate} means that for this specific property, the server must generate at least // this many OnPropertyChange events per seconds. // A sampleRate of 0 means the property is no longer subscribed and server does not need to // generate any onPropertyEvent for this property. // This would be called if sample rate is updated for a subscriber, a new subscriber is added // or an existing subscriber is removed. For example: // 1. We have no subscriber for speed. // 2. A new subscriber is subscribing speed for 10 times/s, updsateSampleRate would be called // with sampleRate as 10. The impl is now polling vehicle speed from bus 10 times/s. // 3. A new subscriber is subscribing speed for 5 times/s, because it is less than 10 // times/sec, updateSampleRate would not be called. // 4. The initial subscriber is removed, updateSampleRate would be called with sampleRate as // 5, because now it only needs to report event 5times/sec. The impl can now poll vehicle // speed 5 times/s. If the impl is still polling at 10 times/s, that is okay as long as // the polling rate is larger than 5times/s. DefaultVehicleHal would ignore the additional // events. // 5. The second subscriber is removed, updateSampleRate would be called with sampleRate as 0. // The impl can optionally disable the polling for vehicle speed. // // If the impl is always polling at {@code maxSampleRate} as specified in config, then this // function can be a no-op. virtual aidl::android::hardware::automotive::vehicle::StatusCode updateSampleRate( [[maybe_unused]] int32_t propId, [[maybe_unused]] int32_t areaId, [[maybe_unused]] float sampleRate) { return aidl::android::hardware::automotive::vehicle::StatusCode::OK; } // Dump debug information in the server. virtual DumpResult dump(const std::vector<std::string>& options) = 0; Loading Loading
automotive/vehicle/aidl/android/hardware/automotive/vehicle/IVehicle.aidl +42 −4 Original line number Diff line number Diff line Loading @@ -90,6 +90,14 @@ interface IVehicle { * area ID) are not allowed in a single call. This function must return * {@link StatusCode#INVALID_ARG} for duplicate properties. * * The {@link VehiclePropValue#timestamp} field in request is ignored. The * {@link VehiclePropValue#timestamp} field in {@link GetValueResult} must * be the system uptime since boot when the value changes for * ON_CHANGE property or when the value is checked according to polling rate * for CONTINUOUS property. Note that for CONTINUOUS property, VHAL client * reading the property multiple times between the polling interval will get * the same timestamp. * * @param callback A callback interface, whose 'onGetValues' would be called * after the value is fetched. Caller should use * {@code android-automotive-large-parcelable} library to parse the Loading @@ -104,7 +112,7 @@ interface IVehicle { * Set vehicle property values. * * The {@link IVehicleCallback#onSetValues} function would be called after * the values set request are sent through vehicle bus or are failed to set. * the values set request are sent through vehicle bus or failed to set. * If the bus protocol supports confirmation, the callback would be called * after getting the confirmation. * Loading Loading @@ -152,11 +160,36 @@ interface IVehicle { * Clients must be able to subscribe to multiple properties at a time * depending on data provided in options argument. * * For one callback, the is only one subscription for one property. * For one callback, there is only one subscription for one property. * A new subscription with a different sample rate would override the old * subscription. One property could be subscribed multiple times for * different callbacks. * * If error is returned, some of the properties failed to subscribe. * Caller is safe to try again, since subscribing to an already subscribed * property is okay. * * The specified sample rate is just a guidance. It is not guaranteed that * the sample rate is achievable depending on how the polling refresh rate * is. The actual property event rate might be higher/lower than the * specified sampleRate, for example, if the polling rate can be 5 times/s * or 10 times/s, subscribing to a sample rate of 7 might use the 5 times/s * polling rate, thus generating 5 events/s. We only require that on * average, the {@code minSampleRate} and {@code maxSampleRate} can be * achieved, all the sampleRate within min and max would on average * generates events with rate >= {@code minSampleRate} and <= * {@code maxSampleRate}. * * The {@link VehiclePropValue#timestamp} field for each property event must * be the system uptime since boot when the value changes for * ON_CHANGE property or when the value is checked according to polling rate * for CONTINUOUS property. Note that for CONTINUOUS property, VHAL client * reading the property multiple times between the polling interval will get * the same timestamp. * For example, if the polling rate for a property is 10 times/s, no matter * what the sampleRate specified in {@code options}, the timestamp for * the timestamp is updated 10 times/s. * * @param callback The subscription callbacks. * {@link IVehicleCallback#onPropertyEvent} would be called when a new * property event arrives. Loading Loading @@ -189,8 +222,13 @@ interface IVehicle { /** * Unsubscribes from property events. * * If 'callback' is not valid or 'propIds' were not subscribed for this * 'callback', this method must return {@link StatusCode#INVALID_ARG}. * If 'callback' is not valid this method must return * {@link StatusCode#INVALID_ARG}. If a specified propId was not subscribed * before, this method must ignore that propId. * * If error is returned, some of the properties failed to unsubscribe. * Caller is safe to try again, since unsubscribing an already unsubscribed * property is okay. * * @param callback The callback used in the previous subscription. * @param propIds The IDs for the properties to unsubscribe. Loading
automotive/vehicle/aidl/impl/fake_impl/hardware/include/FakeVehicleHardware.h +12 −5 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ #include <FakeObd2Frame.h> #include <FakeUserHal.h> #include <IVehicleHardware.h> #include <RecurrentTimer.h> #include <VehicleHalTypes.h> #include <VehiclePropertyStore.h> #include <android-base/parseint.h> Loading Loading @@ -82,6 +83,10 @@ class FakeVehicleHardware : public IVehicleHardware { void registerOnPropertySetErrorEvent( std::unique_ptr<const PropertySetErrorCallback> 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; protected: // mValuePool is also used in mServerSidePropStore. const std::shared_ptr<VehiclePropValuePool> mValuePool; Loading @@ -99,11 +104,13 @@ class FakeVehicleHardware : public IVehicleHardware { const std::unique_ptr<obd2frame::FakeObd2Frame> mFakeObd2Frame; const std::unique_ptr<FakeUserHal> mFakeUserHal; std::mutex mCallbackLock; std::unique_ptr<const PropertyChangeCallback> mOnPropertyChangeCallback GUARDED_BY(mCallbackLock); std::unique_ptr<const PropertySetErrorCallback> mOnPropertySetErrorCallback GUARDED_BY(mCallbackLock); // RecurrentTimer is thread-safe. std::unique_ptr<RecurrentTimer> mRecurrentTimer; std::mutex mLock; std::unique_ptr<const PropertyChangeCallback> mOnPropertyChangeCallback GUARDED_BY(mLock); std::unique_ptr<const PropertySetErrorCallback> mOnPropertySetErrorCallback GUARDED_BY(mLock); std::unordered_map<PropIdAreaId, std::shared_ptr<RecurrentTimer::Callback>, PropIdAreaIdHash> mRecurrentActions GUARDED_BY(mLock); void init(); // Stores the initial value to property store. Loading
automotive/vehicle/aidl/impl/fake_impl/hardware/src/FakeVehicleHardware.cpp +46 −10 Original line number Diff line number Diff line Loading @@ -66,6 +66,7 @@ using ::android::base::EqualsIgnoreCase; using ::android::base::Error; using ::android::base::ParseFloat; using ::android::base::Result; using ::android::base::ScopedLockAssertion; using ::android::base::StartsWith; using ::android::base::StringPrintf; Loading Loading @@ -131,18 +132,14 @@ void FakeVehicleHardware::storePropInitialValue(const defaultconfig::ConfigDecla } FakeVehicleHardware::FakeVehicleHardware() : mValuePool(new VehiclePropValuePool), mServerSidePropStore(new VehiclePropertyStore(mValuePool)), mFakeObd2Frame(new obd2frame::FakeObd2Frame(mServerSidePropStore)), mFakeUserHal(new FakeUserHal(mValuePool)) { init(); } : FakeVehicleHardware(std::make_unique<VehiclePropValuePool>()) {} FakeVehicleHardware::FakeVehicleHardware(std::unique_ptr<VehiclePropValuePool> valuePool) : mValuePool(std::move(valuePool)), mServerSidePropStore(new VehiclePropertyStore(mValuePool)), mFakeObd2Frame(new obd2frame::FakeObd2Frame(mServerSidePropStore)), mFakeUserHal(new FakeUserHal(mValuePool)) { mFakeUserHal(new FakeUserHal(mValuePool)), mRecurrentTimer(new RecurrentTimer()) { init(); } Loading Loading @@ -837,18 +834,57 @@ StatusCode FakeVehicleHardware::checkHealth() { void FakeVehicleHardware::registerOnPropertyChangeEvent( std::unique_ptr<const PropertyChangeCallback> callback) { std::scoped_lock<std::mutex> lockGuard(mCallbackLock); std::scoped_lock<std::mutex> lockGuard(mLock); mOnPropertyChangeCallback = std::move(callback); } void FakeVehicleHardware::registerOnPropertySetErrorEvent( std::unique_ptr<const PropertySetErrorCallback> callback) { std::scoped_lock<std::mutex> lockGuard(mCallbackLock); std::scoped_lock<std::mutex> lockGuard(mLock); 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<std::mutex> lockGuard(mLock); PropIdAreaId propIdAreaId{ .propId = propId, .areaId = areaId, }; if (mRecurrentActions.find(propIdAreaId) != mRecurrentActions.end()) { mRecurrentTimer->unregisterTimerCallback(mRecurrentActions[propIdAreaId]); } if (sampleRate == 0) { return StatusCode::OK; } int64_t interval = static_cast<int64_t>(1'000'000'000. / sampleRate); auto action = std::make_shared<RecurrentTimer::Callback>([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{ .prop = propId, .areaId = areaId, }); if (!result.ok()) { // Failed to read current value, skip refreshing. return; } result.value()->timestamp = elapsedRealtimeNano(); // Must remove the value before writing, otherwise, we would generate no update event since // the value is the same. mServerSidePropStore->removeValue(*result.value()); mServerSidePropStore->writeValue(std::move(result.value())); }); mRecurrentTimer->registerTimerCallback(interval, action); mRecurrentActions[propIdAreaId] = action; return StatusCode::OK; } void FakeVehicleHardware::onValueChangeCallback(const VehiclePropValue& value) { std::scoped_lock<std::mutex> lockGuard(mCallbackLock); std::scoped_lock<std::mutex> lockGuard(mLock); if (mOnPropertyChangeCallback == nullptr) { return; Loading
automotive/vehicle/aidl/impl/fake_impl/hardware/test/FakeVehicleHardwareTest.cpp +80 −7 Original line number Diff line number Diff line Loading @@ -25,12 +25,15 @@ #include <android-base/expected.h> #include <android-base/file.h> #include <android-base/stringprintf.h> #include <android-base/thread_annotations.h> #include <gmock/gmock.h> #include <gtest/gtest.h> #include <utils/Log.h> #include <utils/SystemClock.h> #include <inttypes.h> #include <chrono> #include <condition_variable> #include <vector> namespace android { Loading @@ -53,6 +56,7 @@ using ::aidl::android::hardware::automotive::vehicle::VehicleProperty; using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyStatus; using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue; using ::android::base::expected; using ::android::base::ScopedLockAssertion; using ::android::base::StringPrintf; using ::android::base::unexpected; using ::testing::ContainerEq; Loading @@ -60,6 +64,8 @@ using ::testing::ContainsRegex; using ::testing::Eq; using ::testing::WhenSortedBy; using std::chrono::milliseconds; constexpr int INVALID_PROP_ID = 0; constexpr char CAR_MAKE[] = "Default Car"; Loading Loading @@ -158,30 +164,65 @@ class FakeVehicleHardwareTest : public ::testing::Test { } void onSetValues(std::vector<SetValueResult> results) { std::scoped_lock<std::mutex> lockGuard(mLock); for (auto& result : results) { mSetValueResults.push_back(result); } } const std::vector<SetValueResult>& getSetValueResults() { return mSetValueResults; } const std::vector<SetValueResult>& getSetValueResults() { std::scoped_lock<std::mutex> lockGuard(mLock); return mSetValueResults; } void onGetValues(std::vector<GetValueResult> results) { std::scoped_lock<std::mutex> lockGuard(mLock); for (auto& result : results) { mGetValueResults.push_back(result); } } const std::vector<GetValueResult>& getGetValueResults() { return mGetValueResults; } const std::vector<GetValueResult>& getGetValueResults() { std::scoped_lock<std::mutex> lockGuard(mLock); return mGetValueResults; } void onPropertyChangeEvent(std::vector<VehiclePropValue> values) { std::scoped_lock<std::mutex> lockGuard(mLock); for (auto& value : values) { mChangedProperties.push_back(value); PropIdAreaId propIdAreaId{ .propId = value.prop, .areaId = value.areaId, }; mEventCount[propIdAreaId]++; } mCv.notify_one(); } const std::vector<VehiclePropValue>& getChangedProperties() { std::scoped_lock<std::mutex> lockGuard(mLock); return mChangedProperties; } const std::vector<VehiclePropValue>& getChangedProperties() { return mChangedProperties; } bool waitForChangedProperties(int32_t propId, int32_t areaId, size_t count, milliseconds timeout) { PropIdAreaId propIdAreaId{ .propId = propId, .areaId = areaId, }; std::unique_lock<std::mutex> lk(mLock); return mCv.wait_for(lk, timeout, [this, propIdAreaId, count] { ScopedLockAssertion lockAssertion(mLock); return mEventCount[propIdAreaId] >= count; }); } void clearChangedProperties() { mChangedProperties.clear(); } void clearChangedProperties() { std::scoped_lock<std::mutex> lockGuard(mLock); mEventCount.clear(); mChangedProperties.clear(); } static void addSetValueRequest(std::vector<SetValueRequest>& requests, std::vector<SetValueResult>& expectedResults, int64_t requestId, Loading Loading @@ -246,11 +287,14 @@ class FakeVehicleHardwareTest : public ::testing::Test { private: FakeVehicleHardware mHardware; std::vector<SetValueResult> mSetValueResults; std::vector<GetValueResult> mGetValueResults; std::vector<VehiclePropValue> mChangedProperties; std::shared_ptr<IVehicleHardware::SetValuesCallback> mSetValuesCallback; std::shared_ptr<IVehicleHardware::GetValuesCallback> mGetValuesCallback; std::condition_variable mCv; std::mutex mLock; std::unordered_map<PropIdAreaId, size_t, PropIdAreaIdHash> mEventCount GUARDED_BY(mLock); std::vector<SetValueResult> mSetValueResults GUARDED_BY(mLock); std::vector<GetValueResult> mGetValueResults GUARDED_BY(mLock); std::vector<VehiclePropValue> mChangedProperties GUARDED_BY(mLock); }; TEST_F(FakeVehicleHardwareTest, testGetAllPropertyConfigs) { Loading Loading @@ -1510,6 +1554,35 @@ TEST_F(FakeVehicleHardwareTest, testGetEchoReverseBytes) { ASSERT_EQ(result.value().value.byteValues, std::vector<uint8_t>({0x04, 0x03, 0x02, 0x01})); } TEST_F(FakeVehicleHardwareTest, testUpdateSampleRate) { 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); ASSERT_TRUE(waitForChangedProperties(propSpeed, areaId, /*count=*/5, milliseconds(1500))) << "not enough events generated for speed"; getHardware()->updateSampleRate(propSteering, areaId, 10); 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); clearChangedProperties(); ASSERT_TRUE(waitForChangedProperties(propSteering, areaId, /*count=*/5, milliseconds(1500))) << "should still receive steering events after disable polling for speed"; auto updatedValues = getChangedProperties(); for (auto& value : updatedValues) { ASSERT_GE(value.timestamp, timestamp); ASSERT_EQ(value.prop, propSteering); ASSERT_EQ(value.areaId, areaId); } } } // namespace fake } // namespace vehicle } // namespace automotive Loading
automotive/vehicle/aidl/impl/hardware/include/IVehicleHardware.h +29 −0 Original line number Diff line number Diff line Loading @@ -80,6 +80,35 @@ class IVehicleHardware { const std::vector<aidl::android::hardware::automotive::vehicle::GetValueRequest>& requests) const = 0; // Update the sampling rate for the specified property and the specified areaId (0 for global // property) if server supports it. The property must be a continuous property. // {@code sampleRate} means that for this specific property, the server must generate at least // this many OnPropertyChange events per seconds. // A sampleRate of 0 means the property is no longer subscribed and server does not need to // generate any onPropertyEvent for this property. // This would be called if sample rate is updated for a subscriber, a new subscriber is added // or an existing subscriber is removed. For example: // 1. We have no subscriber for speed. // 2. A new subscriber is subscribing speed for 10 times/s, updsateSampleRate would be called // with sampleRate as 10. The impl is now polling vehicle speed from bus 10 times/s. // 3. A new subscriber is subscribing speed for 5 times/s, because it is less than 10 // times/sec, updateSampleRate would not be called. // 4. The initial subscriber is removed, updateSampleRate would be called with sampleRate as // 5, because now it only needs to report event 5times/sec. The impl can now poll vehicle // speed 5 times/s. If the impl is still polling at 10 times/s, that is okay as long as // the polling rate is larger than 5times/s. DefaultVehicleHal would ignore the additional // events. // 5. The second subscriber is removed, updateSampleRate would be called with sampleRate as 0. // The impl can optionally disable the polling for vehicle speed. // // If the impl is always polling at {@code maxSampleRate} as specified in config, then this // function can be a no-op. virtual aidl::android::hardware::automotive::vehicle::StatusCode updateSampleRate( [[maybe_unused]] int32_t propId, [[maybe_unused]] int32_t areaId, [[maybe_unused]] float sampleRate) { return aidl::android::hardware::automotive::vehicle::StatusCode::OK; } // Dump debug information in the server. virtual DumpResult dump(const std::vector<std::string>& options) = 0; Loading