Implement linkToDeath, use binder as client ID.
Implement linkToDeath for binders. Delete allocated resources for a
binder when it died or unlinked.
This CL also uses 'const AIBinder*' as client id type instead of
the callback because the Binder object corresponds to the remote
proxy and is guaranteed to be unique per client.
Bug: 204943359
Test: atest DefaultVehicleHalTest
Change-Id: If2e0c58e86a041a78b8ca69597aef4733ce1826c
diff --git a/automotive/vehicle/aidl/impl/vhal/include/ConnectedClient.h b/automotive/vehicle/aidl/impl/vhal/include/ConnectedClient.h
index 4f0b74a..833707a 100644
--- a/automotive/vehicle/aidl/impl/vhal/include/ConnectedClient.h
+++ b/automotive/vehicle/aidl/impl/vhal/include/ConnectedClient.h
@@ -42,10 +42,10 @@
// This class is thread-safe.
class ConnectedClient {
public:
- ConnectedClient(
- std::shared_ptr<PendingRequestPool> requestPool,
- std::shared_ptr<::aidl::android::hardware::automotive::vehicle::IVehicleCallback>
- callback);
+ using CallbackType =
+ std::shared_ptr<::aidl::android::hardware::automotive::vehicle::IVehicleCallback>;
+
+ ConnectedClient(std::shared_ptr<PendingRequestPool> requestPool, CallbackType callback);
virtual ~ConnectedClient() = default;
@@ -68,8 +68,7 @@
virtual std::shared_ptr<const PendingRequestPool::TimeoutCallbackFunc> getTimeoutCallback() = 0;
const std::shared_ptr<PendingRequestPool> mRequestPool;
- const std::shared_ptr<::aidl::android::hardware::automotive::vehicle::IVehicleCallback>
- mCallback;
+ const CallbackType mCallback;
};
// A class to represent a client that calls {@code IVehicle.setValues} or {@code
@@ -77,10 +76,7 @@
template <class ResultType, class ResultsType>
class GetSetValuesClient final : public ConnectedClient {
public:
- GetSetValuesClient(
- std::shared_ptr<PendingRequestPool> requestPool,
- std::shared_ptr<::aidl::android::hardware::automotive::vehicle::IVehicleCallback>
- callback);
+ GetSetValuesClient(std::shared_ptr<PendingRequestPool> requestPool, CallbackType callback);
// Sends the results to this client.
void sendResults(const std::vector<ResultType>& results);
@@ -105,10 +101,7 @@
// A class to represent a client that calls {@code IVehicle.subscribe}.
class SubscriptionClient final : public ConnectedClient {
public:
- SubscriptionClient(
- std::shared_ptr<PendingRequestPool> requestPool,
- std::shared_ptr<::aidl::android::hardware::automotive::vehicle::IVehicleCallback>
- callback);
+ SubscriptionClient(std::shared_ptr<PendingRequestPool> requestPool, CallbackType callback);
// Gets the callback to be called when the request for this client has finished.
std::shared_ptr<const IVehicleHardware::GetValuesCallback> getResultCallback();
@@ -116,8 +109,7 @@
// Marshals the updated values into largeParcelable and sents it through {@code onPropertyEvent}
// callback.
static void sendUpdatedValues(
- std::shared_ptr<::aidl::android::hardware::automotive::vehicle::IVehicleCallback>
- callback,
+ CallbackType callback,
std::vector<::aidl::android::hardware::automotive::vehicle::VehiclePropValue>&&
updatedValues);
@@ -132,9 +124,7 @@
std::shared_ptr<const IVehicleHardware::PropertyChangeCallback> mPropertyChangeCallback;
static void onGetValueResults(
- const void* clientId,
- std::shared_ptr<::aidl::android::hardware::automotive::vehicle::IVehicleCallback>
- callback,
+ const void* clientId, CallbackType callback,
std::shared_ptr<PendingRequestPool> requestPool,
std::vector<::aidl::android::hardware::automotive::vehicle::GetValueResult> results);
};
diff --git a/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h b/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h
index b9975bc..62b2627 100644
--- a/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h
+++ b/automotive/vehicle/aidl/impl/vhal/include/DefaultVehicleHal.h
@@ -53,7 +53,7 @@
explicit DefaultVehicleHal(std::unique_ptr<IVehicleHardware> hardware);
- ~DefaultVehicleHal() = default;
+ ~DefaultVehicleHal();
::ndk::ScopedAStatus getAllPropConfigs(
::aidl::android::hardware::automotive::vehicle::VehiclePropConfigs* returnConfigs)
@@ -101,7 +101,7 @@
private:
std::mutex mLock;
- std::unordered_map<CallbackType, int64_t> mIds GUARDED_BY(mLock);
+ std::unordered_map<const AIBinder*, int64_t> mIds GUARDED_BY(mLock);
};
// A thread safe class to store all subscribe clients. This class is safe to pass to async
@@ -112,16 +112,42 @@
std::shared_ptr<SubscriptionClient> getClient(const CallbackType& callback);
+ void removeClient(const AIBinder* clientId);
+
size_t countClients();
private:
std::mutex mLock;
- std::unordered_map<CallbackType, std::shared_ptr<SubscriptionClient>> mClients
+ std::unordered_map<const AIBinder*, std::shared_ptr<SubscriptionClient>> mClients
GUARDED_BY(mLock);
// PendingRequestPool is thread-safe.
std::shared_ptr<PendingRequestPool> mPendingRequestPool;
};
+ // A wrapper for linkToDeath to enable stubbing for test.
+ class ILinkToDeath {
+ public:
+ virtual ~ILinkToDeath() = default;
+
+ virtual binder_status_t linkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient,
+ void* cookie) = 0;
+ };
+
+ // A real implementation for ILinkToDeath.
+ class AIBinderLinkToDeathImpl final : public ILinkToDeath {
+ public:
+ binder_status_t linkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient,
+ void* cookie) override;
+ };
+
+ // OnBinderDiedContext is a type used as a cookie passed deathRecipient. The deathRecipient's
+ // onBinderDied function takes only a cookie as input and we have to store all the contexts
+ // as the cookie.
+ struct OnBinderDiedContext {
+ DefaultVehicleHal* vhal;
+ const AIBinder* clientId;
+ };
+
// The default timeout of get or set value requests is 30s.
// TODO(b/214605968): define TIMEOUT_IN_NANO in IVehicle and allow getValues/setValues/subscribe
// to specify custom timeouts.
@@ -142,15 +168,22 @@
std::shared_ptr<SubscriptionManager> mSubscriptionManager;
std::mutex mLock;
- std::unordered_map<CallbackType, std::shared_ptr<GetValuesClient>> mGetValuesClients
+ std::unordered_map<const AIBinder*, std::unique_ptr<OnBinderDiedContext>> mOnBinderDiedContexts
GUARDED_BY(mLock);
- std::unordered_map<CallbackType, std::shared_ptr<SetValuesClient>> mSetValuesClients
+ std::unordered_map<const AIBinder*, std::shared_ptr<GetValuesClient>> mGetValuesClients
+ GUARDED_BY(mLock);
+ std::unordered_map<const AIBinder*, std::shared_ptr<SetValuesClient>> mSetValuesClients
GUARDED_BY(mLock);
// SubscriptionClients is thread-safe.
std::shared_ptr<SubscriptionClients> mSubscriptionClients;
+ // mLinkToDeathImpl is only going to be changed in test.
+ std::unique_ptr<ILinkToDeath> mLinkToDeathImpl;
+
// RecurrentTimer is thread-safe.
RecurrentTimer mRecurrentTimer;
+ ::ndk::ScopedAIBinder_DeathRecipient mDeathRecipient;
+
::android::base::Result<void> checkProperty(
const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue& propValue);
@@ -176,9 +209,15 @@
const ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig*>
getConfig(int32_t propId) const;
+ void onBinderDiedWithContext(const AIBinder* clientId);
+
+ void onBinderUnlinkedWithContext(const AIBinder* clientId);
+
+ void monitorBinderLifeCycle(const CallbackType& callback);
+
template <class T>
static std::shared_ptr<T> getOrCreateClient(
- std::unordered_map<CallbackType, std::shared_ptr<T>>* clients,
+ std::unordered_map<const AIBinder*, std::shared_ptr<T>>* clients,
const CallbackType& callback, std::shared_ptr<PendingRequestPool> pendingRequestPool);
static void getValueFromHardwareCallCallback(
@@ -195,9 +234,16 @@
static void checkHealth(std::weak_ptr<IVehicleHardware> hardware,
std::weak_ptr<SubscriptionManager> subscriptionManager);
+ static void onBinderDied(void* cookie);
+
+ static void onBinderUnlinked(void* cookie);
+
// Test-only
// Set the default timeout for pending requests.
void setTimeout(int64_t timeoutInNano);
+
+ // Test-only
+ void setLinkToDeathImpl(std::unique_ptr<ILinkToDeath> impl);
};
} // namespace vehicle
diff --git a/automotive/vehicle/aidl/impl/vhal/include/SubscriptionManager.h b/automotive/vehicle/aidl/impl/vhal/include/SubscriptionManager.h
index 28809c6..e739c8c 100644
--- a/automotive/vehicle/aidl/impl/vhal/include/SubscriptionManager.h
+++ b/automotive/vehicle/aidl/impl/vhal/include/SubscriptionManager.h
@@ -38,6 +38,7 @@
// A thread-safe subscription manager that manages all VHAL subscriptions.
class SubscriptionManager final {
public:
+ using ClientIdType = const AIBinder*;
using CallbackType =
std::shared_ptr<::aidl::android::hardware::automotive::vehicle::IVehicleCallback>;
using GetValueFunc = std::function<void(
@@ -59,24 +60,24 @@
options,
bool isContinuousProperty);
- // Unsubscribes from the properties for the callback.
- // Returns error if the callback was not subscribed before or one of the given property was not
+ // Unsubscribes from the properties for the client.
+ // Returns error if the client was not subscribed before or one of the given property was not
// subscribed. If error is returned, no property would be unsubscribed.
- // Returns ok if all the requested properties for the callback are unsubscribed.
- ::android::base::Result<void> unsubscribe(const CallbackType& callback,
+ // Returns ok if all the requested properties for the client are unsubscribed.
+ ::android::base::Result<void> unsubscribe(ClientIdType client,
const std::vector<int32_t>& propIds);
- // Unsubscribes to all the properties for the callback.
- // Returns error if the callback was not subscribed before. If error is returned, no property
+ // Unsubscribes from all the properties for the client.
+ // Returns error if the client was not subscribed before. If error is returned, no property
// would be unsubscribed.
- // Returns ok if all the properties for the callback are unsubscribed.
- ::android::base::Result<void> unsubscribe(const CallbackType& callback);
+ // Returns ok if all the properties for the client are unsubscribed.
+ ::android::base::Result<void> unsubscribe(ClientIdType client);
// For a list of updated properties, returns a map that maps clients subscribing to
// the updated properties to a list of updated values. This would only return on-change property
// clients that should be informed for the given updated values.
std::unordered_map<
- std::shared_ptr<::aidl::android::hardware::automotive::vehicle::IVehicleCallback>,
+ CallbackType,
std::vector<const ::aidl::android::hardware::automotive::vehicle::VehiclePropValue*>>
getSubscribedClients(
const std::vector<::aidl::android::hardware::automotive::vehicle::VehiclePropValue>&
@@ -86,6 +87,9 @@
static bool checkSampleRate(float sampleRate);
private:
+ // Friend class for testing.
+ friend class DefaultVehicleHalTest;
+
struct PropIdAreaId {
int32_t propId;
int32_t areaId;
@@ -131,9 +135,10 @@
};
mutable std::mutex mLock;
- std::unordered_map<PropIdAreaId, std::unordered_set<CallbackType>, PropIdAreaIdHash>
+ std::unordered_map<PropIdAreaId, std::unordered_map<ClientIdType, CallbackType>,
+ PropIdAreaIdHash>
mClientsByPropIdArea GUARDED_BY(mLock);
- std::unordered_map<CallbackType, std::unordered_map<PropIdAreaId, std::unique_ptr<Subscription>,
+ std::unordered_map<ClientIdType, std::unordered_map<PropIdAreaId, std::unique_ptr<Subscription>,
PropIdAreaIdHash>>
mSubscriptionsByClient GUARDED_BY(mLock);
// RecurrentTimer is thread-safe.
@@ -141,6 +146,9 @@
const GetValueFunc mGetValue;
static ::android::base::Result<int64_t> getInterval(float sampleRate);
+
+ // Checks whether the manager is empty. For testing purpose.
+ bool isEmpty();
};
} // namespace vehicle
diff --git a/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp b/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp
index c4cbc68..3e088c5 100644
--- a/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/src/DefaultVehicleHal.cpp
@@ -42,7 +42,6 @@
using ::aidl::android::hardware::automotive::vehicle::GetValueRequests;
using ::aidl::android::hardware::automotive::vehicle::GetValueResult;
using ::aidl::android::hardware::automotive::vehicle::GetValueResults;
-using ::aidl::android::hardware::automotive::vehicle::IVehicleCallback;
using ::aidl::android::hardware::automotive::vehicle::SetValueRequest;
using ::aidl::android::hardware::automotive::vehicle::SetValueRequests;
using ::aidl::android::hardware::automotive::vehicle::SetValueResult;
@@ -62,6 +61,8 @@
using ::android::base::expected;
using ::android::base::Result;
using ::android::base::StringPrintf;
+
+using ::ndk::ScopedAIBinder_DeathRecipient;
using ::ndk::ScopedAStatus;
std::string toString(const std::unordered_set<int64_t>& values) {
@@ -86,10 +87,15 @@
int64_t DefaultVehicleHal::SubscribeIdByClient::getId(const CallbackType& callback) {
std::scoped_lock<std::mutex> lockGuard(mLock);
// This would be initialized to 0 if callback does not exist in the map.
- int64_t subscribeId = (mIds[callback])++;
+ int64_t subscribeId = (mIds[callback->asBinder().get()])++;
return subscribeId;
}
+void DefaultVehicleHal::SubscriptionClients::removeClient(const AIBinder* clientId) {
+ std::scoped_lock<std::mutex> lockGuard(mLock);
+ mClients.erase(clientId);
+}
+
size_t DefaultVehicleHal::SubscriptionClients::countClients() {
std::scoped_lock<std::mutex> lockGuard(mLock);
return mClients.size();
@@ -139,6 +145,17 @@
std::make_shared<std::function<void()>>([hardwareCopy, subscriptionManagerCopy]() {
checkHealth(hardwareCopy, subscriptionManagerCopy);
}));
+
+ mLinkToDeathImpl = std::make_unique<AIBinderLinkToDeathImpl>();
+ mDeathRecipient = ScopedAIBinder_DeathRecipient(
+ AIBinder_DeathRecipient_new(&DefaultVehicleHal::onBinderDied));
+ AIBinder_DeathRecipient_setOnUnlinked(mDeathRecipient.get(),
+ &DefaultVehicleHal::onBinderUnlinked);
+}
+
+DefaultVehicleHal::~DefaultVehicleHal() {
+ // Delete the deathRecipient so that onBinderDied would not be called to reference 'this'.
+ mDeathRecipient = ScopedAIBinder_DeathRecipient();
}
void DefaultVehicleHal::onPropertyChangeEvent(
@@ -161,26 +178,73 @@
template <class T>
std::shared_ptr<T> DefaultVehicleHal::getOrCreateClient(
- std::unordered_map<CallbackType, std::shared_ptr<T>>* clients, const CallbackType& callback,
- std::shared_ptr<PendingRequestPool> pendingRequestPool) {
- if (clients->find(callback) == clients->end()) {
- // TODO(b/204943359): Remove client from clients when linkToDeath is implemented.
- (*clients)[callback] = std::make_shared<T>(pendingRequestPool, callback);
+ std::unordered_map<const AIBinder*, std::shared_ptr<T>>* clients,
+ const CallbackType& callback, std::shared_ptr<PendingRequestPool> pendingRequestPool) {
+ const AIBinder* clientId = callback->asBinder().get();
+ if (clients->find(clientId) == clients->end()) {
+ (*clients)[clientId] = std::make_shared<T>(pendingRequestPool, callback);
}
- return (*clients)[callback];
+ return (*clients)[clientId];
+}
+
+void DefaultVehicleHal::monitorBinderLifeCycle(const CallbackType& callback) {
+ AIBinder* clientId = callback->asBinder().get();
+ {
+ std::scoped_lock<std::mutex> lockGuard(mLock);
+ if (mOnBinderDiedContexts.find(clientId) != mOnBinderDiedContexts.end()) {
+ // Already registered.
+ return;
+ }
+ }
+
+ std::unique_ptr<OnBinderDiedContext> context = std::make_unique<OnBinderDiedContext>(
+ OnBinderDiedContext{.vhal = this, .clientId = clientId});
+ binder_status_t status = mLinkToDeathImpl->linkToDeath(clientId, mDeathRecipient.get(),
+ static_cast<void*>(context.get()));
+ if (status == STATUS_OK) {
+ std::scoped_lock<std::mutex> lockGuard(mLock);
+ // Insert into a map to keep the context object alive.
+ mOnBinderDiedContexts[clientId] = std::move(context);
+ } else {
+ ALOGE("failed to call linkToDeath on client binder, status: %d", static_cast<int>(status));
+ }
+}
+
+void DefaultVehicleHal::onBinderDied(void* cookie) {
+ OnBinderDiedContext* context = reinterpret_cast<OnBinderDiedContext*>(cookie);
+ context->vhal->onBinderDiedWithContext(context->clientId);
+}
+
+void DefaultVehicleHal::onBinderDiedWithContext(const AIBinder* clientId) {
+ std::scoped_lock<std::mutex> lockGuard(mLock);
+ mSetValuesClients.erase(clientId);
+ mGetValuesClients.erase(clientId);
+ mSubscriptionClients->removeClient(clientId);
+ mSubscriptionManager->unsubscribe(clientId);
+}
+
+void DefaultVehicleHal::onBinderUnlinked(void* cookie) {
+ // Delete the context associated with this cookie.
+ OnBinderDiedContext* context = reinterpret_cast<OnBinderDiedContext*>(cookie);
+ context->vhal->onBinderUnlinkedWithContext(context->clientId);
+}
+
+void DefaultVehicleHal::onBinderUnlinkedWithContext(const AIBinder* clientId) {
+ std::scoped_lock<std::mutex> lockGuard(mLock);
+ mOnBinderDiedContexts.erase(clientId);
}
template std::shared_ptr<DefaultVehicleHal::GetValuesClient>
DefaultVehicleHal::getOrCreateClient<DefaultVehicleHal::GetValuesClient>(
- std::unordered_map<CallbackType, std::shared_ptr<GetValuesClient>>* clients,
+ std::unordered_map<const AIBinder*, std::shared_ptr<GetValuesClient>>* clients,
const CallbackType& callback, std::shared_ptr<PendingRequestPool> pendingRequestPool);
template std::shared_ptr<DefaultVehicleHal::SetValuesClient>
DefaultVehicleHal::getOrCreateClient<DefaultVehicleHal::SetValuesClient>(
- std::unordered_map<CallbackType, std::shared_ptr<SetValuesClient>>* clients,
+ std::unordered_map<const AIBinder*, std::shared_ptr<SetValuesClient>>* clients,
const CallbackType& callback, std::shared_ptr<PendingRequestPool> pendingRequestPool);
template std::shared_ptr<SubscriptionClient>
DefaultVehicleHal::getOrCreateClient<SubscriptionClient>(
- std::unordered_map<CallbackType, std::shared_ptr<SubscriptionClient>>* clients,
+ std::unordered_map<const AIBinder*, std::shared_ptr<SubscriptionClient>>* clients,
const CallbackType& callback, std::shared_ptr<PendingRequestPool> pendingRequestPool);
void DefaultVehicleHal::getValueFromHardwareCallCallback(
@@ -268,6 +332,8 @@
ScopedAStatus DefaultVehicleHal::getValues(const CallbackType& callback,
const GetValueRequests& requests) {
+ monitorBinderLifeCycle(callback);
+
expected<LargeParcelableBase::BorrowedOwnedObject<GetValueRequests>, ScopedAStatus>
deserializedResults = fromStableLargeParcelable(requests);
if (!deserializedResults.ok()) {
@@ -344,6 +410,8 @@
ScopedAStatus DefaultVehicleHal::setValues(const CallbackType& callback,
const SetValueRequests& requests) {
+ monitorBinderLifeCycle(callback);
+
expected<LargeParcelableBase::BorrowedOwnedObject<SetValueRequests>, ScopedAStatus>
deserializedResults = fromStableLargeParcelable(requests);
if (!deserializedResults.ok()) {
@@ -526,6 +594,8 @@
ScopedAStatus DefaultVehicleHal::subscribe(const CallbackType& callback,
const std::vector<SubscribeOptions>& options,
[[maybe_unused]] int32_t maxSharedMemoryFileCount) {
+ monitorBinderLifeCycle(callback);
+
// TODO(b/205189110): Use shared memory file count.
if (auto result = checkSubscribeOptions(options); !result.ok()) {
ALOGE("subscribe: invalid subscribe options: %s", getErrorMsg(result).c_str());
@@ -571,7 +641,7 @@
ScopedAStatus DefaultVehicleHal::unsubscribe(const CallbackType& callback,
const std::vector<int32_t>& propIds) {
- return toScopedAStatus(mSubscriptionManager->unsubscribe(callback, propIds),
+ return toScopedAStatus(mSubscriptionManager->unsubscribe(callback->asBinder().get(), propIds),
StatusCode::INVALID_ARG);
}
@@ -639,6 +709,15 @@
return;
}
+binder_status_t DefaultVehicleHal::AIBinderLinkToDeathImpl::linkToDeath(
+ AIBinder* binder, AIBinder_DeathRecipient* recipient, void* cookie) {
+ return AIBinder_linkToDeath(binder, recipient, cookie);
+}
+
+void DefaultVehicleHal::setLinkToDeathImpl(std::unique_ptr<ILinkToDeath> impl) {
+ mLinkToDeathImpl = std::move(impl);
+}
+
} // namespace vehicle
} // namespace automotive
} // namespace hardware
diff --git a/automotive/vehicle/aidl/impl/vhal/src/SubscriptionManager.cpp b/automotive/vehicle/aidl/impl/vhal/src/SubscriptionManager.cpp
index ff996fe..21bfba6 100644
--- a/automotive/vehicle/aidl/impl/vhal/src/SubscriptionManager.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/src/SubscriptionManager.cpp
@@ -26,7 +26,7 @@
namespace {
-constexpr float ONE_SECOND_IN_NANO = 1000000000.;
+constexpr float ONE_SECOND_IN_NANO = 1'000'000'000.;
} // namespace
@@ -100,6 +100,7 @@
}
size_t intervalIndex = 0;
+ ClientIdType clientId = callback->asBinder().get();
for (const auto& option : options) {
int32_t propId = option.propId;
const std::vector<int32_t>& areaIds = option.areaIds;
@@ -118,7 +119,7 @@
.prop = propId,
.areaId = areaId,
};
- mSubscriptionsByClient[callback][propIdAreaId] =
+ mSubscriptionsByClient[clientId][propIdAreaId] =
std::make_unique<RecurrentSubscription>(
mTimer,
[this, callback, propValueRequest] {
@@ -126,24 +127,24 @@
},
interval);
} else {
- mSubscriptionsByClient[callback][propIdAreaId] =
+ mSubscriptionsByClient[clientId][propIdAreaId] =
std::make_unique<OnChangeSubscription>();
}
- mClientsByPropIdArea[propIdAreaId].insert(callback);
+ mClientsByPropIdArea[propIdAreaId][clientId] = callback;
}
}
return {};
}
-Result<void> SubscriptionManager::unsubscribe(const std::shared_ptr<IVehicleCallback>& callback,
+Result<void> SubscriptionManager::unsubscribe(SubscriptionManager::ClientIdType clientId,
const std::vector<int32_t>& propIds) {
std::scoped_lock<std::mutex> lockGuard(mLock);
- if (mSubscriptionsByClient.find(callback) == mSubscriptionsByClient.end()) {
+ if (mSubscriptionsByClient.find(clientId) == mSubscriptionsByClient.end()) {
return Error() << "No property was subscribed for the callback";
}
std::unordered_set<int32_t> subscribedPropIds;
- for (auto const& [propIdAreaId, _] : mSubscriptionsByClient[callback]) {
+ for (auto const& [propIdAreaId, _] : mSubscriptionsByClient[clientId]) {
subscribedPropIds.insert(propIdAreaId.propId);
}
@@ -153,13 +154,13 @@
}
}
- auto& subscriptions = mSubscriptionsByClient[callback];
+ auto& subscriptions = mSubscriptionsByClient[clientId];
auto it = subscriptions.begin();
while (it != subscriptions.end()) {
int32_t propId = it->first.propId;
if (std::find(propIds.begin(), propIds.end(), propId) != propIds.end()) {
auto& clients = mClientsByPropIdArea[it->first];
- clients.erase(callback);
+ clients.erase(clientId);
if (clients.empty()) {
mClientsByPropIdArea.erase(it->first);
}
@@ -169,27 +170,27 @@
}
}
if (subscriptions.empty()) {
- mSubscriptionsByClient.erase(callback);
+ mSubscriptionsByClient.erase(clientId);
}
return {};
}
-Result<void> SubscriptionManager::unsubscribe(const std::shared_ptr<IVehicleCallback>& callback) {
+Result<void> SubscriptionManager::unsubscribe(SubscriptionManager::ClientIdType clientId) {
std::scoped_lock<std::mutex> lockGuard(mLock);
- if (mSubscriptionsByClient.find(callback) == mSubscriptionsByClient.end()) {
- return Error() << "No property was subscribed for the callback";
+ if (mSubscriptionsByClient.find(clientId) == mSubscriptionsByClient.end()) {
+ return Error() << "No property was subscribed for this client";
}
- auto& subscriptions = mSubscriptionsByClient[callback];
+ auto& subscriptions = mSubscriptionsByClient[clientId];
for (auto const& [propIdAreaId, _] : subscriptions) {
auto& clients = mClientsByPropIdArea[propIdAreaId];
- clients.erase(callback);
+ clients.erase(clientId);
if (clients.empty()) {
mClientsByPropIdArea.erase(propIdAreaId);
}
}
- mSubscriptionsByClient.erase(callback);
+ mSubscriptionsByClient.erase(clientId);
return {};
}
@@ -207,8 +208,8 @@
if (mClientsByPropIdArea.find(propIdAreaId) == mClientsByPropIdArea.end()) {
continue;
}
- for (const auto& client : mClientsByPropIdArea[propIdAreaId]) {
- if (!mSubscriptionsByClient[client][propIdAreaId]->isOnChange()) {
+ for (const auto& [clientId, client] : mClientsByPropIdArea[propIdAreaId]) {
+ if (!mSubscriptionsByClient[clientId][propIdAreaId]->isOnChange()) {
continue;
}
clients[client].push_back(&value);
@@ -217,6 +218,11 @@
return clients;
}
+bool SubscriptionManager::isEmpty() {
+ std::scoped_lock<std::mutex> lockGuard(mLock);
+ return mSubscriptionsByClient.empty() && mClientsByPropIdArea.empty();
+}
+
SubscriptionManager::RecurrentSubscription::RecurrentSubscription(
std::shared_ptr<RecurrentTimer> timer, std::function<void()>&& action, int64_t interval)
: mAction(std::make_shared<std::function<void()>>(action)), mTimer(timer) {
diff --git a/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp b/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp
index 54fc17d..ff355c3 100644
--- a/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/test/DefaultVehicleHalTest.cpp
@@ -73,6 +73,7 @@
using ::ndk::ScopedAStatus;
using ::ndk::ScopedFileDescriptor;
+using ::ndk::SpAIBinder;
using ::testing::Eq;
using ::testing::UnorderedElementsAre;
@@ -324,7 +325,12 @@
mVhal = ndk::SharedRefBase::make<DefaultVehicleHal>(std::move(hardware));
mVhalClient = IVehicle::fromBinder(mVhal->asBinder());
mCallback = ndk::SharedRefBase::make<MockVehicleCallback>();
- mCallbackClient = IVehicleCallback::fromBinder(mCallback->asBinder());
+ // Keep the local binder alive.
+ mBinder = mCallback->asBinder();
+ mCallbackClient = IVehicleCallback::fromBinder(mBinder);
+
+ // Set the linkToDeath to a fake implementation that always returns OK.
+ setTestLinkToDeathImpl();
}
void TearDown() override {
@@ -342,10 +348,36 @@
void setTimeout(int64_t timeoutInNano) { mVhal->setTimeout(timeoutInNano); }
+ void setTestLinkToDeathImpl() {
+ mVhal->setLinkToDeathImpl(std::make_unique<TestLinkToDeathImpl>());
+ }
+
size_t countPendingRequests() { return mVhal->mPendingRequestPool->countPendingRequests(); }
+ size_t countClients() {
+ std::scoped_lock<std::mutex> lockGuard(mVhal->mLock);
+ return mVhal->mGetValuesClients.size() + mVhal->mSetValuesClients.size() +
+ mVhal->mSubscriptionClients->countClients();
+ }
+
std::shared_ptr<PendingRequestPool> getPool() { return mVhal->mPendingRequestPool; }
+ void onBinderDied(void* cookie) { return mVhal->onBinderDied(cookie); }
+
+ void onBinderUnlinked(void* cookie) { return mVhal->onBinderUnlinked(cookie); }
+
+ void* getOnBinderDiedContexts(AIBinder* clientId) {
+ std::scoped_lock<std::mutex> lockGuard(mVhal->mLock);
+ return mVhal->mOnBinderDiedContexts[clientId].get();
+ }
+
+ bool countOnBinderDiedContexts() {
+ std::scoped_lock<std::mutex> lockGuard(mVhal->mLock);
+ return mVhal->mOnBinderDiedContexts.size();
+ }
+
+ bool hasNoSubscriptions() { return mVhal->mSubscriptionManager->isEmpty(); }
+
static Result<void> getValuesTestCases(size_t size, GetValueRequests& requests,
std::vector<GetValueResult>& expectedResults,
std::vector<GetValueRequest>& expectedHardwareRequests) {
@@ -416,18 +448,20 @@
return {};
}
- size_t countClients() {
- std::scoped_lock<std::mutex> lockGuard(mVhal->mLock);
- return mVhal->mGetValuesClients.size() + mVhal->mSetValuesClients.size() +
- mVhal->mSubscriptionClients->countClients();
- }
-
private:
std::shared_ptr<DefaultVehicleHal> mVhal;
std::shared_ptr<IVehicle> mVhalClient;
MockVehicleHardware* mHardwarePtr;
std::shared_ptr<MockVehicleCallback> mCallback;
std::shared_ptr<IVehicleCallback> mCallbackClient;
+ SpAIBinder mBinder;
+
+ class TestLinkToDeathImpl final : public DefaultVehicleHal::ILinkToDeath {
+ public:
+ binder_status_t linkToDeath(AIBinder*, AIBinder_DeathRecipient*, void*) override {
+ return STATUS_OK;
+ }
+ };
};
TEST_F(DefaultVehicleHalTest, testGetAllPropConfigsSmall) {
@@ -1445,6 +1479,71 @@
<< "expect to get the latest timestamp with the heartbeat event";
}
+TEST_F(DefaultVehicleHalTest, testOnBinderDiedUnlinked) {
+ // First subscribe to a continuous property so that we register a death recipient for our
+ // client.
+ VehiclePropValue testValue{
+ .prop = GLOBAL_CONTINUOUS_PROP,
+ .value.int32Values = {0},
+ };
+ // Set responses for all the hardware getValues requests.
+ getHardware()->setGetValueResponder(
+ [](std::shared_ptr<const IVehicleHardware::GetValuesCallback> callback,
+ const std::vector<GetValueRequest>& requests) {
+ std::vector<GetValueResult> results;
+ for (auto& request : requests) {
+ VehiclePropValue prop = request.prop;
+ prop.value.int32Values = {0};
+ results.push_back({
+ .requestId = request.requestId,
+ .status = StatusCode::OK,
+ .prop = prop,
+ });
+ }
+ (*callback)(results);
+ return StatusCode::OK;
+ });
+ std::vector<SubscribeOptions> options = {
+ {
+ .propId = GLOBAL_CONTINUOUS_PROP,
+ .sampleRate = 20.0,
+ },
+ };
+ auto status = getClient()->subscribe(getCallbackClient(), options, 0);
+ ASSERT_TRUE(status.isOk()) << "subscribe failed: " << status.getMessage();
+ // Sleep for 100ms so that the subscriptionClient gets created because we would at least try to
+ // get value once.
+ std::this_thread::sleep_for(std::chrono::milliseconds(100));
+
+ // Issue another getValue request on the same client.
+ GetValueRequests requests;
+ std::vector<GetValueResult> expectedResults;
+ std::vector<GetValueRequest> expectedHardwareRequests;
+ ASSERT_TRUE(getValuesTestCases(1, requests, expectedResults, expectedHardwareRequests).ok());
+ getHardware()->addGetValueResponses(expectedResults);
+ status = getClient()->getValues(getCallbackClient(), requests);
+ ASSERT_TRUE(status.isOk()) << "getValues failed: " << status.getMessage();
+
+ ASSERT_EQ(countOnBinderDiedContexts(), static_cast<size_t>(1))
+ << "expect one OnBinderDied context when one client is registered";
+
+ // Get the death recipient cookie for our callback that would be used in onBinderDied and
+ // onBinderUnlinked.
+ AIBinder* clientId = getCallbackClient()->asBinder().get();
+ void* context = getOnBinderDiedContexts(clientId);
+
+ onBinderDied(context);
+
+ ASSERT_EQ(countClients(), static_cast<size_t>(0))
+ << "expect all clients to be removed when binder died";
+ ASSERT_TRUE(hasNoSubscriptions()) << "expect no subscriptions when binder died";
+
+ onBinderUnlinked(context);
+
+ ASSERT_EQ(countOnBinderDiedContexts(), static_cast<size_t>(0))
+ << "expect OnBinderDied context to be deleted when binder is unlinked";
+}
+
} // namespace vehicle
} // namespace automotive
} // namespace hardware
diff --git a/automotive/vehicle/aidl/impl/vhal/test/SubscriptionManagerTest.cpp b/automotive/vehicle/aidl/impl/vhal/test/SubscriptionManagerTest.cpp
index c14f4fd..f81b1a2 100644
--- a/automotive/vehicle/aidl/impl/vhal/test/SubscriptionManagerTest.cpp
+++ b/automotive/vehicle/aidl/impl/vhal/test/SubscriptionManagerTest.cpp
@@ -45,6 +45,7 @@
using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropValues;
using ::ndk::ScopedAStatus;
+using ::ndk::SpAIBinder;
using ::testing::ElementsAre;
using ::testing::WhenSorted;
@@ -95,7 +96,9 @@
0);
});
mCallback = ::ndk::SharedRefBase::make<PropertyCallback>();
- mCallbackClient = IVehicleCallback::fromBinder(mCallback->asBinder());
+ // Keep the local binder alive.
+ mBinder = mCallback->asBinder();
+ mCallbackClient = IVehicleCallback::fromBinder(mBinder);
}
SubscriptionManager* getManager() { return mManager.get(); }
@@ -112,6 +115,7 @@
std::unique_ptr<SubscriptionManager> mManager;
std::shared_ptr<PropertyCallback> mCallback;
std::shared_ptr<IVehicleCallback> mCallbackClient;
+ SpAIBinder mBinder;
};
TEST_F(SubscriptionManagerTest, testSubscribeGlobalContinuous) {
@@ -229,7 +233,7 @@
auto result = getManager()->subscribe(getCallbackClient(), options, true);
ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message();
- result = getManager()->unsubscribe(getCallbackClient());
+ result = getManager()->unsubscribe(getCallbackClient()->asBinder().get());
ASSERT_TRUE(result.ok()) << "failed to unsubscribe: " << result.error().message();
clearEvents();
@@ -257,7 +261,8 @@
auto result = getManager()->subscribe(getCallbackClient(), options, true);
ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message();
- result = getManager()->unsubscribe(getCallbackClient(), std::vector<int32_t>({0}));
+ result = getManager()->unsubscribe(getCallbackClient()->asBinder().get(),
+ std::vector<int32_t>({0}));
ASSERT_TRUE(result.ok()) << "failed to unsubscribe: " << result.error().message();
clearEvents();
@@ -289,7 +294,7 @@
auto result = getManager()->subscribe(getCallbackClient(), options, true);
ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message();
- result = getManager()->unsubscribe(getCallbackClient());
+ result = getManager()->unsubscribe(getCallbackClient()->asBinder().get());
ASSERT_TRUE(result.ok()) << "failed to unsubscribe: " << result.error().message();
clearEvents();
@@ -315,12 +320,14 @@
ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message();
// Property ID: 2 was not subscribed.
- result = getManager()->unsubscribe(getCallbackClient(), std::vector<int32_t>({0, 1, 2}));
+ result = getManager()->unsubscribe(getCallbackClient()->asBinder().get(),
+ std::vector<int32_t>({0, 1, 2}));
ASSERT_FALSE(result.ok()) << "unsubscribe an unsubscribed property must fail";
// Since property 0 and property 1 was not unsubscribed successfully, we should be able to
// unsubscribe them again.
- result = getManager()->unsubscribe(getCallbackClient(), std::vector<int32_t>({0, 1}));
+ result = getManager()->unsubscribe(getCallbackClient()->asBinder().get(),
+ std::vector<int32_t>({0, 1}));
ASSERT_TRUE(result.ok()) << "a failed unsubscription must not unsubscribe any properties"
<< result.error().message();
}
@@ -343,10 +350,10 @@
},
};
- std::shared_ptr<IVehicleCallback> client1 = IVehicleCallback::fromBinder(
- ::ndk::SharedRefBase::make<PropertyCallback>()->asBinder());
- std::shared_ptr<IVehicleCallback> client2 = IVehicleCallback::fromBinder(
- ::ndk::SharedRefBase::make<PropertyCallback>()->asBinder());
+ SpAIBinder binder1 = ::ndk::SharedRefBase::make<PropertyCallback>()->asBinder();
+ std::shared_ptr<IVehicleCallback> client1 = IVehicleCallback::fromBinder(binder1);
+ SpAIBinder binder2 = ::ndk::SharedRefBase::make<PropertyCallback>()->asBinder();
+ std::shared_ptr<IVehicleCallback> client2 = IVehicleCallback::fromBinder(binder2);
auto result = getManager()->subscribe(client1, options1, false);
ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message();
result = getManager()->subscribe(client2, options2, false);
@@ -447,7 +454,8 @@
auto result = getManager()->subscribe(getCallbackClient(), options, false);
ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message();
- result = getManager()->unsubscribe(getCallbackClient(), std::vector<int32_t>({0}));
+ result = getManager()->unsubscribe(getCallbackClient()->asBinder().get(),
+ std::vector<int32_t>({0}));
ASSERT_TRUE(result.ok()) << "failed to unsubscribe: " << result.error().message();
std::vector<VehiclePropValue> updatedValues = {