Implement reg/unregSupportedValueChange.
Flag: EXEMPT hal
Bug: 382563296
Test: atest DefaultVehicleHalTest
Change-Id: I29fc11a0ec519f1185ac3f310f871b8e377e89b9
diff --git a/automotive/vehicle/aidl/impl/current/hardware/include/IVehicleHardware.h b/automotive/vehicle/aidl/impl/current/hardware/include/IVehicleHardware.h
index f46a1c8..9122955 100644
--- a/automotive/vehicle/aidl/impl/current/hardware/include/IVehicleHardware.h
+++ b/automotive/vehicle/aidl/impl/current/hardware/include/IVehicleHardware.h
@@ -303,6 +303,8 @@
//
// If the propertyId's supported values are static, then must do nothing.
//
+ // If some of the [propId, areaId]s are already subscribed, then do nothing.
+ //
// This is only called for [propId, areaId] that has non-null {@code HasSupportedValueInfo}.
//
// Client must implement (override) this function if at least one [propId, areaId]'s
diff --git a/automotive/vehicle/aidl/impl/current/vhal/include/SubscriptionManager.h b/automotive/vehicle/aidl/impl/current/vhal/include/SubscriptionManager.h
index cac1901..59c21aa 100644
--- a/automotive/vehicle/aidl/impl/current/vhal/include/SubscriptionManager.h
+++ b/automotive/vehicle/aidl/impl/current/vhal/include/SubscriptionManager.h
@@ -118,8 +118,19 @@
std::vector<aidl::android::hardware::automotive::vehicle::VehiclePropError>>
getSubscribedClientsForErrorEvents(const std::vector<SetValueErrorEvent>& errorEvents);
- // Returns the number of subscribed clients.
- size_t countClients();
+ // Subscribes to supported values change.
+ VhalResult<void> subscribeSupportedValueChange(const CallbackType& callback,
+ const std::vector<PropIdAreaId>& propIdAreaIds);
+
+ // Unsubscribes to supported values change.
+ VhalResult<void> unsubscribeSupportedValueChange(
+ ClientIdType client, const std::vector<PropIdAreaId>& propIdAreaIds);
+
+ // Returns the number of subscribed property change clients.
+ size_t countPropertyChangeClients();
+
+ // Returns the number of subscribed supported value change clients.
+ size_t countSupportedValueChangeClients();
// Checks whether the sample rate is valid.
static bool checkSampleRateHz(float sampleRateHz);
@@ -160,6 +171,11 @@
std::unordered_set<VehiclePropValue, VehiclePropValueHashPropIdAreaId,
VehiclePropValueEqualPropIdAreaId>>
mContSubValuesByCallback GUARDED_BY(mLock);
+ std::unordered_map<PropIdAreaId, std::unordered_map<ClientIdType, CallbackType>,
+ PropIdAreaIdHash>
+ mSupportedValueChangeClientsByPropIdAreaId GUARDED_BY(mLock);
+ std::unordered_map<ClientIdType, std::unordered_set<PropIdAreaId, PropIdAreaIdHash>>
+ mSupportedValueChangePropIdAreaIdsByClient GUARDED_BY(mLock);
VhalResult<void> addContinuousSubscriberLocked(const ClientIdType& clientId,
const PropIdAreaId& propIdAreaId,
@@ -180,6 +196,9 @@
VhalResult<void> unsubscribePropIdAreaIdLocked(SubscriptionManager::ClientIdType clientId,
const PropIdAreaId& propIdAreaId)
REQUIRES(mLock);
+ VhalResult<void> unsubscribeSupportedValueChangeLocked(
+ SubscriptionManager::ClientIdType clientId,
+ const std::vector<PropIdAreaId>& propIdAreaIds) REQUIRES(mLock);
// Checks whether the manager is empty. For testing purpose.
bool isEmpty();
diff --git a/automotive/vehicle/aidl/impl/current/vhal/src/DefaultVehicleHal.cpp b/automotive/vehicle/aidl/impl/current/vhal/src/DefaultVehicleHal.cpp
index 509d1c3..9073d85 100644
--- a/automotive/vehicle/aidl/impl/current/vhal/src/DefaultVehicleHal.cpp
+++ b/automotive/vehicle/aidl/impl/current/vhal/src/DefaultVehicleHal.cpp
@@ -82,9 +82,6 @@
using VhalPropIdAreaId = ::aidl::android::hardware::automotive::vehicle::PropIdAreaId;
-#define propIdtoString(PROP_ID) \
- aidl::android::hardware::automotive::vehicle::toString(static_cast<VehicleProperty>(PROP_ID))
-
std::string toString(const std::unordered_set<int64_t>& values) {
std::string str = "";
for (auto it = values.begin(); it != values.end(); it++) {
@@ -976,13 +973,13 @@
int32_t propId, int32_t areaId) const {
auto result = getConfig(propId);
if (!result.ok()) {
- return Error() << "Failed to get property config for propertyId: " << propIdtoString(propId)
+ return Error() << "Failed to get property config for propertyId: " << propIdToString(propId)
<< ", error: " << result.error();
}
const VehiclePropConfig& config = result.value();
const VehicleAreaConfig* areaConfig = getAreaConfig(propId, areaId, config);
if (areaConfig == nullptr) {
- return Error() << "AreaId config not found for propertyId: " << propIdtoString(propId)
+ return Error() << "AreaId config not found for propertyId: " << propIdToString(propId)
<< ", areaId: " << areaId;
}
return areaConfig;
@@ -1002,7 +999,7 @@
return &(areaConfig->hasSupportedValueInfo.value());
}
}
- return Error() << "property: " << propIdtoString(propId) << ", areaId: " << areaId
+ return Error() << "property: " << propIdToString(propId) << ", areaId: " << areaId
<< " does not support this operation because hasSupportedValueInfo is null";
}
@@ -1130,15 +1127,65 @@
}
ScopedAStatus DefaultVehicleHal::registerSupportedValueChangeCallback(
- const std::shared_ptr<IVehicleCallback>&, const std::vector<VhalPropIdAreaId>&) {
- // TODO(b/381020465): Add relevant implementation.
- return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ const std::shared_ptr<IVehicleCallback>& callback,
+ const std::vector<VhalPropIdAreaId>& vhalPropIdAreaIds) {
+ std::vector<PropIdAreaId> propIdAreaIdsToSubscribe;
+ for (size_t i = 0; i < vhalPropIdAreaIds.size(); i++) {
+ const auto& vhalPropIdAreaId = vhalPropIdAreaIds.at(i);
+ int32_t propId = vhalPropIdAreaId.propId;
+ int32_t areaId = vhalPropIdAreaId.areaId;
+ auto hasSupportedValueInfoResult = getHasSupportedValueInfo(propId, areaId);
+ if (!hasSupportedValueInfoResult.ok()) {
+ ALOGE("registerSupportedValueChangeCallback not supported: %s",
+ hasSupportedValueInfoResult.error().message().c_str());
+ return toScopedAStatus(hasSupportedValueInfoResult, StatusCode::INVALID_ARG);
+ }
+ const auto& hasSupportedValueInfo = *(hasSupportedValueInfoResult.value());
+ if (!hasSupportedValueInfo.hasMinSupportedValue &&
+ !hasSupportedValueInfo.hasMaxSupportedValue &&
+ !hasSupportedValueInfo.hasSupportedValuesList) {
+ ALOGW("registerSupportedValueChangeCallback: do nothing for property: %s, "
+ "areaId: %" PRId32
+ ", no min/max supported values or supported values list"
+ " specified",
+ propIdToString(propId).c_str(), areaId);
+ continue;
+ }
+ propIdAreaIdsToSubscribe.push_back(PropIdAreaId{.propId = propId, .areaId = areaId});
+ }
+ if (propIdAreaIdsToSubscribe.empty()) {
+ return ScopedAStatus::ok();
+ }
+ auto result =
+ mSubscriptionManager->subscribeSupportedValueChange(callback, propIdAreaIdsToSubscribe);
+ if (!result.ok()) {
+ ALOGW("registerSupportedValueChangeCallback: failed to subscribe supported value change"
+ " for %s, error: %s",
+ fmt::format("{}", propIdAreaIdsToSubscribe).c_str(),
+ result.error().message().c_str());
+ return toScopedAStatus(result);
+ }
+ return ScopedAStatus::ok();
}
ScopedAStatus DefaultVehicleHal::unregisterSupportedValueChangeCallback(
- const std::shared_ptr<IVehicleCallback>&, const std::vector<VhalPropIdAreaId>&) {
- // TODO(b/381020465): Add relevant implementation.
- return ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+ const std::shared_ptr<IVehicleCallback>& callback,
+ const std::vector<VhalPropIdAreaId>& vhalPropIdAreaIds) {
+ std::vector<PropIdAreaId> propIdAreaIds;
+ for (const auto& vhalPropIdAreaId : vhalPropIdAreaIds) {
+ propIdAreaIds.push_back(
+ PropIdAreaId{.propId = vhalPropIdAreaId.propId, .areaId = vhalPropIdAreaId.areaId});
+ }
+
+ auto result = mSubscriptionManager->unsubscribeSupportedValueChange(callback->asBinder().get(),
+ propIdAreaIds);
+ if (!result.ok()) {
+ ALOGW("unregisterSupportedValueChangeCallback: failed to unsubscribe supported value change"
+ " for %s, error: %s",
+ fmt::format("{}", propIdAreaIds).c_str(), result.error().message().c_str());
+ return toScopedAStatus(result);
+ }
+ return ScopedAStatus::ok();
}
IVehicleHardware* DefaultVehicleHal::getHardware() {
@@ -1256,12 +1303,14 @@
dprintf(fd, "Currently have %zu getValues clients\n", mGetValuesClients.size());
dprintf(fd, "Currently have %zu setValues clients\n", mSetValuesClients.size());
dprintf(fd, "Currently have %zu subscribe clients\n", countSubscribeClients());
+ dprintf(fd, "Currently have %zu supported values change subscribe clients\n",
+ mSubscriptionManager->countSupportedValueChangeClients());
}
return STATUS_OK;
}
size_t DefaultVehicleHal::countSubscribeClients() {
- return mSubscriptionManager->countClients();
+ return mSubscriptionManager->countPropertyChangeClients();
}
} // namespace vehicle
diff --git a/automotive/vehicle/aidl/impl/current/vhal/src/SubscriptionManager.cpp b/automotive/vehicle/aidl/impl/current/vhal/src/SubscriptionManager.cpp
index 2d09e02..f790033 100644
--- a/automotive/vehicle/aidl/impl/current/vhal/src/SubscriptionManager.cpp
+++ b/automotive/vehicle/aidl/impl/current/vhal/src/SubscriptionManager.cpp
@@ -17,6 +17,7 @@
#include "SubscriptionManager.h"
#include <VehicleUtils.h>
+#include <android-base/format.h>
#include <android-base/stringprintf.h>
#include <utils/Log.h>
#include <utils/SystemClock.h>
@@ -65,6 +66,8 @@
mClientsByPropIdAreaId.clear();
mSubscribedPropsByClient.clear();
+ mSupportedValueChangeClientsByPropIdAreaId.clear();
+ mSupportedValueChangePropIdAreaIdsByClient.clear();
}
bool SubscriptionManager::checkSampleRateHz(float sampleRateHz) {
@@ -166,8 +169,7 @@
/*enableVur*/ false));
status != StatusCode::OK) {
return StatusError(status)
- << StringPrintf("failed subscribe for prop: %s, areaId: %" PRId32,
- propIdToString(propId).c_str(), areaId);
+ << fmt::format("failed subscribe for propIdAreaId: {}", propIdAreaId);
}
return {};
}
@@ -379,16 +381,111 @@
if (mSubscribedPropsByClient.find(clientId) == mSubscribedPropsByClient.end()) {
ALOGW("No property was subscribed for this client, unsubscribe does nothing");
- return {};
+ } else {
+ auto& propIdAreaIds = mSubscribedPropsByClient[clientId];
+ for (auto const& propIdAreaId : propIdAreaIds) {
+ if (auto result = unsubscribePropIdAreaIdLocked(clientId, propIdAreaId); !result.ok()) {
+ return result;
+ }
+ }
+ mSubscribedPropsByClient.erase(clientId);
}
- auto& subscriptions = mSubscribedPropsByClient[clientId];
- for (auto const& propIdAreaId : subscriptions) {
- if (auto result = unsubscribePropIdAreaIdLocked(clientId, propIdAreaId); !result.ok()) {
+ if (mSupportedValueChangePropIdAreaIdsByClient.find(clientId) ==
+ mSupportedValueChangePropIdAreaIdsByClient.end()) {
+ ALOGW("No supported value change was subscribed for this client, unsubscribe does nothing");
+ } else {
+ const auto& propIdAreaIds = mSupportedValueChangePropIdAreaIdsByClient[clientId];
+ if (auto result = unsubscribeSupportedValueChangeLocked(
+ clientId,
+ std::vector<PropIdAreaId>(propIdAreaIds.begin(), propIdAreaIds.end()));
+ !result.ok()) {
return result;
}
}
- mSubscribedPropsByClient.erase(clientId);
+ return {};
+}
+
+VhalResult<void> SubscriptionManager::subscribeSupportedValueChange(
+ const std::shared_ptr<IVehicleCallback>& callback,
+ const std::vector<PropIdAreaId>& propIdAreaIds) {
+ // Need to make sure this whole operation is guarded by a lock so that our internal state is
+ // consistent with IVehicleHardware state.
+ std::scoped_lock<std::mutex> lockGuard(mLock);
+
+ ClientIdType clientId = callback->asBinder().get();
+
+ // It is possible that some of the [propId, areaId]s are already subscribed, IVehicleHardware
+ // will ignore them.
+ if (auto status = mVehicleHardware->subscribeSupportedValueChange(propIdAreaIds);
+ status != StatusCode::OK) {
+ return StatusError(status)
+ << fmt::format("failed to call subscribeSupportedValueChange for propIdAreaIds: {}",
+ propIdAreaIds);
+ }
+ for (const auto& propIdAreaId : propIdAreaIds) {
+ mSupportedValueChangeClientsByPropIdAreaId[propIdAreaId][clientId] = callback;
+ // mSupportedValueChangePropIdAreaIdsByClient[clientId] is a set so this will ignore
+ // duplicate [propId, areaId].
+ mSupportedValueChangePropIdAreaIdsByClient[clientId].insert(propIdAreaId);
+ }
+ return {};
+}
+
+VhalResult<void> SubscriptionManager::unsubscribeSupportedValueChange(
+ SubscriptionManager::ClientIdType clientId,
+ const std::vector<PropIdAreaId>& propIdAreaIds) {
+ // Need to make sure this whole operation is guarded by a lock so that our internal state is
+ // consistent with IVehicleHardware state.
+ std::scoped_lock<std::mutex> lockGuard(mLock);
+
+ return unsubscribeSupportedValueChangeLocked(clientId, propIdAreaIds);
+}
+
+VhalResult<void> SubscriptionManager::unsubscribeSupportedValueChangeLocked(
+ SubscriptionManager::ClientIdType clientId,
+ const std::vector<PropIdAreaId>& propIdAreaIds) {
+ std::vector<PropIdAreaId> propIdAreaIdsToUnsubscribe;
+
+ // Check which [propId, areaId] needs to be unsubscribed from the hardware.
+ for (const auto& propIdAreaId : propIdAreaIds) {
+ auto it = mSupportedValueChangeClientsByPropIdAreaId.find(propIdAreaId);
+ if (it != mSupportedValueChangeClientsByPropIdAreaId.end()) {
+ const auto& clients = it->second;
+ if (clients.size() == 1 && clients.find(clientId) != clients.end()) {
+ // This callback is the only client registered for [propId, areaId].
+ // Unregister it should unregister the [propId, areaId].
+ propIdAreaIdsToUnsubscribe.push_back(propIdAreaId);
+ }
+ }
+ }
+
+ // Send the unsubscribe request.
+ if (!propIdAreaIdsToUnsubscribe.empty()) {
+ if (auto status =
+ mVehicleHardware->unsubscribeSupportedValueChange(propIdAreaIdsToUnsubscribe);
+ status != StatusCode::OK) {
+ return StatusError(status) << fmt::format(
+ "failed to call unsubscribeSupportedValueChange for "
+ "propIdAreaIds: {}",
+ propIdAreaIdsToUnsubscribe);
+ }
+ }
+
+ // Remove internal book-keeping.
+ for (const auto& propIdAreaId : propIdAreaIds) {
+ if (mSupportedValueChangeClientsByPropIdAreaId.find(propIdAreaId) !=
+ mSupportedValueChangeClientsByPropIdAreaId.end()) {
+ mSupportedValueChangeClientsByPropIdAreaId[propIdAreaId].erase(clientId);
+ }
+ if (mSupportedValueChangeClientsByPropIdAreaId.empty()) {
+ mSupportedValueChangeClientsByPropIdAreaId.erase(propIdAreaId);
+ }
+ mSupportedValueChangePropIdAreaIdsByClient[clientId].erase(propIdAreaId);
+ if (mSupportedValueChangePropIdAreaIdsByClient[clientId].empty()) {
+ mSupportedValueChangePropIdAreaIdsByClient.erase(clientId);
+ }
+ }
return {};
}
@@ -486,14 +583,21 @@
bool SubscriptionManager::isEmpty() {
std::scoped_lock<std::mutex> lockGuard(mLock);
- return mSubscribedPropsByClient.empty() && mClientsByPropIdAreaId.empty();
+ return mSubscribedPropsByClient.empty() && mClientsByPropIdAreaId.empty() &&
+ mSupportedValueChangeClientsByPropIdAreaId.empty() &&
+ mSupportedValueChangePropIdAreaIdsByClient.empty();
}
-size_t SubscriptionManager::countClients() {
+size_t SubscriptionManager::countPropertyChangeClients() {
std::scoped_lock<std::mutex> lockGuard(mLock);
return mSubscribedPropsByClient.size();
}
+size_t SubscriptionManager::countSupportedValueChangeClients() {
+ std::scoped_lock<std::mutex> lockGuard(mLock);
+ return mSupportedValueChangePropIdAreaIdsByClient.size();
+}
+
} // namespace vehicle
} // namespace automotive
} // namespace hardware
diff --git a/automotive/vehicle/aidl/impl/current/vhal/test/DefaultVehicleHalTest.cpp b/automotive/vehicle/aidl/impl/current/vhal/test/DefaultVehicleHalTest.cpp
index 4df5ba3..0526f7d 100644
--- a/automotive/vehicle/aidl/impl/current/vhal/test/DefaultVehicleHalTest.cpp
+++ b/automotive/vehicle/aidl/impl/current/vhal/test/DefaultVehicleHalTest.cpp
@@ -2418,6 +2418,205 @@
ASSERT_EQ(result.status, StatusCode::INVALID_ARG);
}
+TEST_F(DefaultVehicleHalTest, testRegisterSupportedValueChangeCallback) {
+ auto testConfigs = std::vector<VehiclePropConfig>(
+ {VehiclePropConfig{
+ .prop = testInt32VecProp(1),
+ .areaConfigs =
+ {
+ {.areaId = 0,
+ .hasSupportedValueInfo =
+ HasSupportedValueInfo{
+ .hasMinSupportedValue = false,
+ .hasMaxSupportedValue = false,
+ .hasSupportedValuesList = false,
+ }},
+ },
+ },
+ VehiclePropConfig{
+ .prop = testInt32VecWindowProp(2),
+ .areaConfigs =
+ {
+ {.areaId = 2,
+ .hasSupportedValueInfo =
+ HasSupportedValueInfo{
+ .hasMinSupportedValue = true,
+ .hasMaxSupportedValue = false,
+ .hasSupportedValuesList = false,
+ }},
+ },
+ }});
+
+ auto hardware = std::make_unique<MockVehicleHardware>();
+ MockVehicleHardware* hardwarePtr = hardware.get();
+ hardware->setPropertyConfigs(testConfigs);
+
+ auto vhal = ndk::SharedRefBase::make<DefaultVehicleHal>(std::move(hardware));
+ std::shared_ptr<IVehicle> client = IVehicle::fromBinder(vhal->asBinder());
+
+ // This request is ignored because it does not have supported value info.
+ auto propIdAreaId1 = VhalPropIdAreaId{.propId = testInt32VecProp(1), .areaId = 0};
+ auto propIdAreaId2 = VhalPropIdAreaId{.propId = testInt32VecWindowProp(2), .areaId = 2};
+ auto status = client->registerSupportedValueChangeCallback(
+ getCallbackClient(), std::vector<VhalPropIdAreaId>{propIdAreaId1, propIdAreaId2});
+
+ ASSERT_TRUE(status.isOk()) << "Get non-okay status from registerSupportedValueChangeCallback"
+ << status.getMessage();
+ ASSERT_THAT(hardwarePtr->getSubscribedSupportedValueChangePropIdAreaIds(),
+ ElementsAre(PropIdAreaId{.propId = testInt32VecWindowProp(2), .areaId = 2}));
+}
+
+TEST_F(DefaultVehicleHalTest, testRegisterSupportedValueChangeCallback_invalidRequest) {
+ auto testConfigs = std::vector<VehiclePropConfig>({VehiclePropConfig{
+ .prop = testInt32VecProp(1),
+ .areaConfigs =
+ {
+ {.areaId = 0, .hasSupportedValueInfo = std::nullopt},
+ },
+ }});
+ auto hardware = std::make_unique<MockVehicleHardware>();
+ hardware->setPropertyConfigs(testConfigs);
+
+ auto vhal = ndk::SharedRefBase::make<DefaultVehicleHal>(std::move(hardware));
+ std::shared_ptr<IVehicle> client = IVehicle::fromBinder(vhal->asBinder());
+
+ auto propIdAreaId1 = VhalPropIdAreaId{.propId = testInt32VecProp(1), .areaId = 0};
+ auto status = client->registerSupportedValueChangeCallback(
+ getCallbackClient(), std::vector<VhalPropIdAreaId>{propIdAreaId1});
+
+ ASSERT_FALSE(status.isOk()) << "registerSupportedValueChangeCallback must return error if one "
+ "of the requested [propId, areaId]"
+ " does not have supportedValueInfo";
+}
+
+TEST_F(DefaultVehicleHalTest, testRegisterSupportedValueChangeCallback_errorStatusFromHardware) {
+ auto testConfigs = std::vector<VehiclePropConfig>({VehiclePropConfig{
+ .prop = testInt32VecWindowProp(2),
+ .areaConfigs =
+ {
+ {.areaId = 2,
+ .hasSupportedValueInfo =
+ HasSupportedValueInfo{
+ .hasMinSupportedValue = true,
+ .hasMaxSupportedValue = false,
+ .hasSupportedValuesList = false,
+ }},
+ },
+ }});
+
+ auto hardware = std::make_unique<MockVehicleHardware>();
+ hardware->setStatus("subscribeSupportedValueChange", StatusCode::INTERNAL_ERROR);
+ hardware->setPropertyConfigs(testConfigs);
+
+ auto vhal = ndk::SharedRefBase::make<DefaultVehicleHal>(std::move(hardware));
+ std::shared_ptr<IVehicle> client = IVehicle::fromBinder(vhal->asBinder());
+
+ auto propIdAreaId = VhalPropIdAreaId{.propId = testInt32VecWindowProp(2), .areaId = 2};
+ auto status = client->registerSupportedValueChangeCallback(
+ getCallbackClient(), std::vector<VhalPropIdAreaId>{propIdAreaId});
+
+ ASSERT_FALSE(status.isOk()) << "registerSupportedValueChangeCallback must return error if "
+ "VehicleHardware returns error";
+}
+
+TEST_F(DefaultVehicleHalTest, testUnregisterSupportedValueChangeCallback) {
+ auto testConfigs = std::vector<VehiclePropConfig>(
+ {VehiclePropConfig{
+ .prop = testInt32VecProp(1),
+ .areaConfigs =
+ {
+ {.areaId = 0,
+ .hasSupportedValueInfo =
+ HasSupportedValueInfo{
+ .hasMinSupportedValue = false,
+ .hasMaxSupportedValue = false,
+ .hasSupportedValuesList = true,
+ }},
+ },
+ },
+ VehiclePropConfig{
+ .prop = testInt32VecWindowProp(2),
+ .areaConfigs =
+ {
+ {.areaId = 2,
+ .hasSupportedValueInfo =
+ HasSupportedValueInfo{
+ .hasMinSupportedValue = true,
+ .hasMaxSupportedValue = false,
+ .hasSupportedValuesList = false,
+ }},
+ },
+ }});
+
+ auto hardware = std::make_unique<MockVehicleHardware>();
+ MockVehicleHardware* hardwarePtr = hardware.get();
+ hardware->setPropertyConfigs(testConfigs);
+
+ auto vhal = ndk::SharedRefBase::make<DefaultVehicleHal>(std::move(hardware));
+ std::shared_ptr<IVehicle> client = IVehicle::fromBinder(vhal->asBinder());
+
+ auto propIdAreaId1 = VhalPropIdAreaId{.propId = testInt32VecProp(1), .areaId = 0};
+ auto propIdAreaId2 = VhalPropIdAreaId{.propId = testInt32VecWindowProp(2), .areaId = 2};
+ auto status = client->registerSupportedValueChangeCallback(
+ getCallbackClient(), std::vector<VhalPropIdAreaId>{propIdAreaId1, propIdAreaId2});
+
+ ASSERT_TRUE(status.isOk()) << "Get non-okay status from registerSupportedValueChangeCallback"
+ << status.getMessage();
+
+ status = client->unregisterSupportedValueChangeCallback(
+ getCallbackClient(), std::vector<VhalPropIdAreaId>{propIdAreaId1, propIdAreaId2});
+
+ ASSERT_TRUE(status.isOk()) << "Get non-okay status from unregisterSupportedValueChangeCallback"
+ << status.getMessage();
+
+ ASSERT_TRUE(hardwarePtr->getSubscribedSupportedValueChangePropIdAreaIds().empty())
+ << "All registered [propId, areaId]s must be unregistered";
+}
+
+TEST_F(DefaultVehicleHalTest, testUnregisterSupportedValueChangeCallback_ignoreUnregistered) {
+ auto testConfigs = std::vector<VehiclePropConfig>(
+ {VehiclePropConfig{
+ .prop = testInt32VecProp(1),
+ .areaConfigs =
+ {
+ {.areaId = 0,
+ .hasSupportedValueInfo =
+ HasSupportedValueInfo{
+ .hasMinSupportedValue = false,
+ .hasMaxSupportedValue = false,
+ .hasSupportedValuesList = true,
+ }},
+ },
+ },
+ VehiclePropConfig{
+ .prop = testInt32VecWindowProp(2),
+ .areaConfigs =
+ {
+ {.areaId = 2,
+ .hasSupportedValueInfo =
+ HasSupportedValueInfo{
+ .hasMinSupportedValue = true,
+ .hasMaxSupportedValue = false,
+ .hasSupportedValuesList = false,
+ }},
+ },
+ }});
+
+ auto hardware = std::make_unique<MockVehicleHardware>();
+ MockVehicleHardware* hardwarePtr = hardware.get();
+ hardware->setPropertyConfigs(testConfigs);
+
+ auto vhal = ndk::SharedRefBase::make<DefaultVehicleHal>(std::move(hardware));
+ std::shared_ptr<IVehicle> client = IVehicle::fromBinder(vhal->asBinder());
+
+ auto propIdAreaId1 = VhalPropIdAreaId{.propId = testInt32VecProp(1), .areaId = 0};
+ auto propIdAreaId2 = VhalPropIdAreaId{.propId = testInt32VecWindowProp(2), .areaId = 2};
+ auto status = client->unregisterSupportedValueChangeCallback(
+ getCallbackClient(), std::vector<VhalPropIdAreaId>{propIdAreaId1, propIdAreaId2});
+
+ ASSERT_TRUE(status.isOk());
+}
+
} // namespace vehicle
} // namespace automotive
} // namespace hardware
diff --git a/automotive/vehicle/aidl/impl/current/vhal/test/MockVehicleHardware.cpp b/automotive/vehicle/aidl/impl/current/vhal/test/MockVehicleHardware.cpp
index ae2b5a2..11f1835 100644
--- a/automotive/vehicle/aidl/impl/current/vhal/test/MockVehicleHardware.cpp
+++ b/automotive/vehicle/aidl/impl/current/vhal/test/MockVehicleHardware.cpp
@@ -310,6 +310,40 @@
return mEventBatchingWindow;
}
+StatusCode MockVehicleHardware::subscribeSupportedValueChange(
+ const std::vector<PropIdAreaId>& propIdAreaIds) {
+ std::scoped_lock<std::mutex> lockGuard(mLock);
+ if (auto it = mStatusByFunctions.find(__func__); it != mStatusByFunctions.end()) {
+ if (StatusCode status = it->second; status != StatusCode::OK) {
+ return status;
+ }
+ }
+ for (const auto& propIdAreaId : propIdAreaIds) {
+ mSubscribedSupportedValueChangePropIdAreaIds.insert(propIdAreaId);
+ }
+ return StatusCode::OK;
+}
+
+StatusCode MockVehicleHardware::unsubscribeSupportedValueChange(
+ const std::vector<PropIdAreaId>& propIdAreaIds) {
+ std::scoped_lock<std::mutex> lockGuard(mLock);
+ if (auto it = mStatusByFunctions.find(__func__); it != mStatusByFunctions.end()) {
+ if (StatusCode status = it->second; status != StatusCode::OK) {
+ return status;
+ }
+ }
+ for (const auto& propIdAreaId : propIdAreaIds) {
+ mSubscribedSupportedValueChangePropIdAreaIds.erase(propIdAreaId);
+ }
+ return StatusCode::OK;
+}
+
+std::unordered_set<PropIdAreaId, PropIdAreaIdHash>
+MockVehicleHardware::getSubscribedSupportedValueChangePropIdAreaIds() {
+ std::scoped_lock<std::mutex> lockGuard(mLock);
+ return mSubscribedSupportedValueChangePropIdAreaIds;
+}
+
template <class ResultType>
StatusCode MockVehicleHardware::returnResponse(
std::shared_ptr<const std::function<void(std::vector<ResultType>)>> callback,
diff --git a/automotive/vehicle/aidl/impl/current/vhal/test/MockVehicleHardware.h b/automotive/vehicle/aidl/impl/current/vhal/test/MockVehicleHardware.h
index d1e9771..e7359db 100644
--- a/automotive/vehicle/aidl/impl/current/vhal/test/MockVehicleHardware.h
+++ b/automotive/vehicle/aidl/impl/current/vhal/test/MockVehicleHardware.h
@@ -71,6 +71,10 @@
getSupportedValuesLists(const std::vector<PropIdAreaId>& propIdAreaIds) override;
std::vector<aidl::android::hardware::automotive::vehicle::MinMaxSupportedValueResult>
getMinMaxSupportedValues(const std::vector<PropIdAreaId>& propIdAreaIds) override;
+ aidl::android::hardware::automotive::vehicle::StatusCode subscribeSupportedValueChange(
+ const std::vector<PropIdAreaId>& propIdAreaIds) override;
+ aidl::android::hardware::automotive::vehicle::StatusCode unsubscribeSupportedValueChange(
+ const std::vector<PropIdAreaId>& propIdAreaIds) override;
// Test functions.
void setPropertyConfigs(
@@ -114,10 +118,13 @@
std::vector<aidl::android::hardware::automotive::vehicle::SubscribeOptions>
getSubscribeOptions();
void clearSubscribeOptions();
- // Whether getAllPropertyConfigs() has been called, which blocks all all property configs
+ // Whether getAllPropertyConfigs() has been called, which blocks on all property configs
// being ready.
bool getAllPropertyConfigsCalled();
+ std::unordered_set<PropIdAreaId, PropIdAreaIdHash>
+ getSubscribedSupportedValueChangePropIdAreaIds();
+
private:
mutable std::mutex mLock;
mutable std::condition_variable mCv;
@@ -174,6 +181,9 @@
std::shared_ptr<RecurrentTimer> mRecurrentTimer;
std::unordered_map<int32_t, std::unordered_map<int32_t, std::shared_ptr<std::function<void()>>>>
mRecurrentActions GUARDED_BY(mLock);
+
+ std::unordered_set<PropIdAreaId, PropIdAreaIdHash> mSubscribedSupportedValueChangePropIdAreaIds
+ GUARDED_BY(mLock);
};
} // namespace vehicle