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
This commit is contained in:
Yu Shan
2021-12-03 19:07:41 -08:00
parent d110eda701
commit 14829be269
7 changed files with 320 additions and 84 deletions

View File

@@ -42,10 +42,10 @@ namespace vehicle {
// 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 @@ class ConnectedClient {
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 @@ class ConnectedClient {
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 @@ class GetSetValuesClient final : public ConnectedClient {
// 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 @@ class SubscriptionClient final : public ConnectedClient {
// 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 @@ class SubscriptionClient final : public ConnectedClient {
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);
};

View File

@@ -53,7 +53,7 @@ class DefaultVehicleHal final : public ::aidl::android::hardware::automotive::ve
explicit DefaultVehicleHal(std::unique_ptr<IVehicleHardware> hardware);
~DefaultVehicleHal() = default;
~DefaultVehicleHal();
::ndk::ScopedAStatus getAllPropConfigs(
::aidl::android::hardware::automotive::vehicle::VehiclePropConfigs* returnConfigs)
@@ -101,7 +101,7 @@ class DefaultVehicleHal final : public ::aidl::android::hardware::automotive::ve
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 @@ class DefaultVehicleHal final : public ::aidl::android::hardware::automotive::ve
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 @@ class DefaultVehicleHal final : public ::aidl::android::hardware::automotive::ve
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 @@ class DefaultVehicleHal final : public ::aidl::android::hardware::automotive::ve
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 @@ class DefaultVehicleHal final : public ::aidl::android::hardware::automotive::ve
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

View File

@@ -38,6 +38,7 @@ namespace vehicle {
// 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 @@ class SubscriptionManager final {
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 @@ class SubscriptionManager final {
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 @@ class SubscriptionManager final {
};
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 @@ class SubscriptionManager final {
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

View File

@@ -42,7 +42,6 @@ using ::aidl::android::hardware::automotive::vehicle::GetValueRequest;
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::Error;
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 @@ std::shared_ptr<SubscriptionClient> DefaultVehicleHal::SubscriptionClients::getC
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 @@ DefaultVehicleHal::DefaultVehicleHal(std::unique_ptr<IVehicleHardware> hardware)
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 @@ void DefaultVehicleHal::onPropertyChangeEvent(
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 @@ Result<void> DefaultVehicleHal::checkProperty(const VehiclePropValue& propValue)
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::getValues(const CallbackType& callback,
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 @@ Result<void> DefaultVehicleHal::checkSubscribeOptions(
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::subscribe(const CallbackType& callback,
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 @@ void DefaultVehicleHal::checkHealth(std::weak_ptr<IVehicleHardware> hardware,
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

View File

@@ -26,7 +26,7 @@ namespace vehicle {
namespace {
constexpr float ONE_SECOND_IN_NANO = 1000000000.;
constexpr float ONE_SECOND_IN_NANO = 1'000'000'000.;
} // namespace
@@ -100,6 +100,7 @@ Result<void> SubscriptionManager::subscribe(const std::shared_ptr<IVehicleCallba
}
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 @@ Result<void> SubscriptionManager::subscribe(const std::shared_ptr<IVehicleCallba
.prop = propId,
.areaId = areaId,
};
mSubscriptionsByClient[callback][propIdAreaId] =
mSubscriptionsByClient[clientId][propIdAreaId] =
std::make_unique<RecurrentSubscription>(
mTimer,
[this, callback, propValueRequest] {
@@ -126,24 +127,24 @@ Result<void> SubscriptionManager::subscribe(const std::shared_ptr<IVehicleCallba
},
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 @@ Result<void> SubscriptionManager::unsubscribe(const std::shared_ptr<IVehicleCall
}
}
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 @@ Result<void> SubscriptionManager::unsubscribe(const std::shared_ptr<IVehicleCall
}
}
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 @@ SubscriptionManager::getSubscribedClients(const std::vector<VehiclePropValue>& u
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 @@ SubscriptionManager::getSubscribedClients(const std::vector<VehiclePropValue>& u
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) {

View File

@@ -73,6 +73,7 @@ using ::android::base::Result;
using ::ndk::ScopedAStatus;
using ::ndk::ScopedFileDescriptor;
using ::ndk::SpAIBinder;
using ::testing::Eq;
using ::testing::UnorderedElementsAre;
@@ -324,7 +325,12 @@ class DefaultVehicleHalTest : public ::testing::Test {
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 @@ class DefaultVehicleHalTest : public ::testing::Test {
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 @@ class DefaultVehicleHalTest : public ::testing::Test {
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 @@ TEST_F(DefaultVehicleHalTest, testHeartbeatEvent) {
<< "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

View File

@@ -45,6 +45,7 @@ using ::aidl::android::hardware::automotive::vehicle::VehiclePropErrors;
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 @@ class SubscriptionManagerTest : public ::testing::Test {
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 @@ class SubscriptionManagerTest : public ::testing::Test {
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 @@ TEST_F(SubscriptionManagerTest, testUnsubscribeGlobalContinuous) {
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 @@ TEST_F(SubscriptionManagerTest, testUnsubscribeMultipleAreas) {
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 @@ TEST_F(SubscriptionManagerTest, testUnsubscribeByCallback) {
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 @@ TEST_F(SubscriptionManagerTest, testUnsubscribeFailure) {
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 @@ TEST_F(SubscriptionManagerTest, testSubscribeOnchange) {
},
};
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 @@ TEST_F(SubscriptionManagerTest, testUnsubscribeOnchange) {
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 = {