aosp12/system/hwservicemanager/ServiceManager.cpp

880 lines
30 KiB
C++

/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "hwservicemanager"
#include "ServiceManager.h"
#include "Vintf.h"
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <hwbinder/IPCThreadState.h>
#include <hidl/HidlSupport.h>
#include <hidl/HidlTransportSupport.h>
#include <regex>
#include <sstream>
#include <thread>
using android::hardware::IPCThreadState;
using ::android::hardware::interfacesEqual;
namespace android {
namespace hidl {
namespace manager {
namespace implementation {
AccessControl::CallingContext getBinderCallingContext() {
const auto& self = IPCThreadState::self();
pid_t pid = self->getCallingPid();
const char* sid = self->getCallingSid();
if (sid == nullptr) {
if (pid != getpid()) {
android_errorWriteLog(0x534e4554, "121035042");
}
return AccessControl::getCallingContext(pid);
} else {
return { true, sid, pid };
}
}
static constexpr uint64_t kServiceDiedCookie = 0;
static constexpr uint64_t kPackageListenerDiedCookie = 1;
static constexpr uint64_t kServiceListenerDiedCookie = 2;
static constexpr uint64_t kClientCallbackDiedCookie = 3;
size_t ServiceManager::countExistingService() const {
size_t total = 0;
forEachExistingService([&] (const HidlService *) {
++total;
return true; // continue
});
return total;
}
void ServiceManager::forEachExistingService(std::function<bool(const HidlService *)> f) const {
forEachServiceEntry([&] (const HidlService *service) {
if (service->getService() == nullptr) {
return true; // continue
}
return f(service);
});
}
void ServiceManager::forEachExistingService(std::function<bool(HidlService *)> f) {
forEachServiceEntry([&] (HidlService *service) {
if (service->getService() == nullptr) {
return true; // continue
}
return f(service);
});
}
void ServiceManager::forEachServiceEntry(std::function<bool(const HidlService *)> f) const {
for (const auto& interfaceMapping : mServiceMap) {
const auto& instanceMap = interfaceMapping.second.getInstanceMap();
for (const auto& instanceMapping : instanceMap) {
if (!f(instanceMapping.second.get())) {
return;
}
}
}
}
void ServiceManager::forEachServiceEntry(std::function<bool(HidlService *)> f) {
for (auto& interfaceMapping : mServiceMap) {
auto& instanceMap = interfaceMapping.second.getInstanceMap();
for (auto& instanceMapping : instanceMap) {
if (!f(instanceMapping.second.get())) {
return;
}
}
}
}
HidlService* ServiceManager::lookup(const std::string& fqName, const std::string& name) {
auto ifaceIt = mServiceMap.find(fqName);
if (ifaceIt == mServiceMap.end()) {
return nullptr;
}
PackageInterfaceMap &ifaceMap = ifaceIt->second;
HidlService *hidlService = ifaceMap.lookup(name);
return hidlService;
}
void ServiceManager::serviceDied(uint64_t cookie, const wp<IBase>& who) {
bool serviceRemoved = false;
switch (cookie) {
case kServiceDiedCookie:
serviceRemoved = removeService(who, nullptr /* restrictToInstanceName */);
break;
case kPackageListenerDiedCookie:
serviceRemoved = removePackageListener(who);
break;
case kServiceListenerDiedCookie:
serviceRemoved = removeServiceListener(who);
break;
case kClientCallbackDiedCookie: {
sp<IBase> base = who.promote();
IClientCallback* callback = static_cast<IClientCallback*>(base.get());
serviceRemoved = unregisterClientCallback(nullptr /*service*/,
sp<IClientCallback>(callback));
} break;
}
if (!serviceRemoved) {
LOG(ERROR) << "Received death notification but interface instance not removed. Cookie: "
<< cookie << " Service pointer: " << who.promote().get();
}
}
ServiceManager::InstanceMap &ServiceManager::PackageInterfaceMap::getInstanceMap() {
return mInstanceMap;
}
const ServiceManager::InstanceMap &ServiceManager::PackageInterfaceMap::getInstanceMap() const {
return mInstanceMap;
}
const HidlService *ServiceManager::PackageInterfaceMap::lookup(
const std::string &name) const {
auto it = mInstanceMap.find(name);
if (it == mInstanceMap.end()) {
return nullptr;
}
return it->second.get();
}
HidlService *ServiceManager::PackageInterfaceMap::lookup(
const std::string &name) {
return const_cast<HidlService*>(
const_cast<const PackageInterfaceMap*>(this)->lookup(name));
}
void ServiceManager::PackageInterfaceMap::insertService(
std::unique_ptr<HidlService> &&service) {
mInstanceMap.insert({service->getInstanceName(), std::move(service)});
}
void ServiceManager::PackageInterfaceMap::sendPackageRegistrationNotification(
const hidl_string &fqName,
const hidl_string &instanceName) {
for (auto it = mPackageListeners.begin(); it != mPackageListeners.end();) {
auto ret = (*it)->onRegistration(fqName, instanceName, false /* preexisting */);
if (ret.isOk()) {
++it;
} else {
LOG(ERROR) << "Dropping registration callback for " << fqName << "/" << instanceName
<< ": transport error.";
it = mPackageListeners.erase(it);
}
}
}
void ServiceManager::PackageInterfaceMap::addPackageListener(sp<IServiceNotification> listener) {
for (const auto &instanceMapping : mInstanceMap) {
const std::unique_ptr<HidlService> &service = instanceMapping.second;
if (service->getService() == nullptr) {
continue;
}
auto ret = listener->onRegistration(
service->getInterfaceName(),
service->getInstanceName(),
true /* preexisting */);
if (!ret.isOk()) {
LOG(ERROR) << "Not adding package listener for " << service->getInterfaceName()
<< "/" << service->getInstanceName() << ": transport error "
<< "when sending notification for already registered instance.";
return;
}
}
mPackageListeners.push_back(listener);
}
bool ServiceManager::PackageInterfaceMap::removePackageListener(const wp<IBase>& who) {
bool found = false;
for (auto it = mPackageListeners.begin(); it != mPackageListeners.end();) {
if (interfacesEqual(*it, who.promote())) {
it = mPackageListeners.erase(it);
found = true;
} else {
++it;
}
}
return found;
}
bool ServiceManager::PackageInterfaceMap::removeServiceListener(const wp<IBase>& who) {
bool found = false;
for (auto &servicePair : getInstanceMap()) {
const std::unique_ptr<HidlService> &service = servicePair.second;
found |= service->removeListener(who);
}
return found;
}
static void tryStartService(const std::string& fqName, const std::string& name) {
using ::android::base::SetProperty;
// The "happy path" here is starting up a service that is configured as a
// lazy HAL, but we aren't sure that is the case. If the service doesn't
// have an 'interface' entry in its .rc file OR if the service is already
// running, then this will be a no-op. So, for instance, if a service is
// deadlocked during startup, you will see this message repeatedly.
LOG(INFO) << "Since " << fqName << "/" << name
<< " is not registered, trying to start it as a lazy HAL.";
std::thread([=] {
if (!SetProperty("ctl.interface_start", fqName + "/" + name)) {
LOG(INFO) << "Tried to start " << fqName << "/" << name
<< " as a lazy service, but was unable to. Usually this happens when a "
"service is not installed, but if the service is intended to be used as a "
"lazy service, then it may be configured incorrectly.";
}
}).detach();
}
// Methods from ::android::hidl::manager::V1_0::IServiceManager follow.
Return<sp<IBase>> ServiceManager::get(const hidl_string& hidlFqName,
const hidl_string& hidlName) {
const std::string fqName = hidlFqName;
const std::string name = hidlName;
if (!mAcl.canGet(fqName, getBinderCallingContext())) {
return nullptr;
}
HidlService* hidlService = lookup(fqName, name);
if (hidlService == nullptr) {
tryStartService(fqName, name);
return nullptr;
}
sp<IBase> service = hidlService->getService();
if (service == nullptr) {
tryStartService(fqName, name);
return nullptr;
}
// Let HidlService know that we handed out a client. If the client drops the service before the
// next time handleClientCallbacks is called, it will still know that the service had been handed out.
hidlService->guaranteeClient();
forEachExistingService([&] (HidlService *otherService) {
if (otherService != hidlService && interfacesEqual(service, otherService->getService())) {
otherService->guaranteeClient();
}
return true;
});
// This is executed immediately after the binder driver confirms the transaction. The driver
// will update the appropriate data structures to reflect the fact that the client now has the
// service this function is returning. Nothing else can update the HidlService at the same
// time. This will run before anything else can modify the HidlService which is owned by this
// object, so it will be in the same state that it was when this function returns.
hardware::addPostCommandTask([hidlService] {
hidlService->handleClientCallbacks(false /* isCalledOnInterval */, 1 /*knownClientCount*/);
});
return service;
}
Return<bool> ServiceManager::add(const hidl_string& name, const sp<IBase>& service) {
bool addSuccess = false;
if (service == nullptr) {
return false;
}
auto pidcon = getBinderCallingContext();
if (!mAcl.canAdd(IBase::descriptor, pidcon)) {
LOG(ERROR) << "Missing permissions to add IBase";
return false;
}
auto ret = service->interfaceChain([&](const auto &interfaceChain) {
addSuccess = addImpl(name, service, interfaceChain, pidcon);
});
if (!ret.isOk()) {
LOG(ERROR) << "Failed to retrieve interface chain: " << ret.description();
return false;
}
return addSuccess;
}
bool ServiceManager::addImpl(const std::string& name,
const sp<IBase>& service,
const hidl_vec<hidl_string>& interfaceChain,
const AccessControl::CallingContext& callingContext) {
if (interfaceChain.size() == 0) {
LOG(WARNING) << "Empty interface chain for " << name;
return false;
}
// First, verify you're allowed to add() the whole interface hierarchy
for(size_t i = 0; i < interfaceChain.size(); i++) {
const std::string fqName = interfaceChain[i];
if (!mAcl.canAdd(fqName, callingContext)) {
return false;
}
}
const std::string childFqName = interfaceChain[0];
// Detect duplicate registration
if (interfaceChain.size() > 1) {
// second to last entry should be the highest base class other than IBase.
const std::string baseFqName = interfaceChain[interfaceChain.size() - 2];
const HidlService *hidlService = lookup(baseFqName, name);
if (hidlService != nullptr && hidlService->getService() != nullptr) {
// This shouldn't occur during normal operation. Here are some cases where
// it might get hit:
// - bad configuration (service installed on device multiple times)
// - race between death notification and a new service being registered
// (previous logs should indicate a separate problem)
pid_t newServicePid = IPCThreadState::self()->getCallingPid();
pid_t oldServicePid = hidlService->getDebugPid();
LOG(WARNING) << "Detected instance of " << childFqName << " (pid: " << newServicePid
<< ") registering over instance of or with base of " << baseFqName << " (pid: "
<< oldServicePid << ").";
}
}
// Unregister superclass if subclass is registered over it
{
// For IBar extends IFoo if IFoo/default is being registered, remove
// IBar/default. This makes sure the following two things are equivalent
// 1). IBar::castFrom(IFoo::getService(X))
// 2). IBar::getService(X)
// assuming that IBar is declared in the device manifest and there
// is also not an IBaz extends IFoo and there is no race.
const HidlService *hidlService = lookup(childFqName, name);
if (hidlService != nullptr) {
const sp<IBase> remove = hidlService->getService();
if (remove != nullptr) {
const std::string instanceName = name;
removeService(remove, &instanceName /* restrictToInstanceName */);
}
}
}
// Detect missing manifest entries of superclass, when subclass in manifest.
{
// Ideally we could require all HALs registered with hwservicemanager to
// be in the VINTF manifest. However, this would prevent tests from
// running, and we would need another method of registering them (AIDL
// servicemanager gets around this because only certain objects are
// VINTF objects). So, for HIDL, we rely on VTS.
//
// When registering a HAL, in the client process, it checks to make sure
// that the last (leaf) class in the chain is in the VINTF manifest and
// fails. However, this fails to take into account parent classes. If
// parent classes are associated with certain VTS tests, then those VTS
// tests will not run until vts_treble_vintf_vendor_test fails and the
// failures are fixed (namely adding this into a manifest).
//
// So, here we make sure that if something is in the manifest, all of
// its parent classes are.
using ::android::hardware::getTransport;
if (vintf::Transport::EMPTY != getTransport(childFqName, name)) {
bool parentsInManifest = true;
// skip over latest, and check over all interfaces except the base
// interface (android.hidl.base is never in the manifest)
for (size_t i = 1; i + 1 < interfaceChain.size(); i++) {
if (vintf::Transport::EMPTY == getTransport(interfaceChain[i], name)) {
LOG(ERROR) << childFqName << "/" << name
<< " is in the VINTF manifest, but its superclass "
<< interfaceChain[i] << " is not. Refusing to register.";
parentsInManifest = false;
}
}
if (!parentsInManifest) {
return false;
}
}
}
for(size_t i = 0; i < interfaceChain.size(); i++) {
const std::string fqName = interfaceChain[i];
PackageInterfaceMap &ifaceMap = mServiceMap[fqName];
HidlService *hidlService = ifaceMap.lookup(name);
if (hidlService == nullptr) {
ifaceMap.insertService(
std::make_unique<HidlService>(fqName, name, service, callingContext.pid));
} else {
hidlService->setService(service, callingContext.pid);
}
ifaceMap.sendPackageRegistrationNotification(fqName, name);
}
bool linkRet = service->linkToDeath(this, kServiceDiedCookie).withDefault(false);
if (!linkRet) {
LOG(ERROR) << "Could not link to death for " << interfaceChain[0] << "/" << name;
}
return true;
}
Return<ServiceManager::Transport> ServiceManager::getTransport(const hidl_string& fqName,
const hidl_string& name) {
using ::android::hardware::getTransport;
if (!mAcl.canGet(fqName, getBinderCallingContext())) {
return Transport::EMPTY;
}
switch (getTransport(fqName, name)) {
case vintf::Transport::HWBINDER:
return Transport::HWBINDER;
case vintf::Transport::PASSTHROUGH:
return Transport::PASSTHROUGH;
case vintf::Transport::EMPTY:
default:
return Transport::EMPTY;
}
}
Return<void> ServiceManager::list(list_cb _hidl_cb) {
if (!mAcl.canList(getBinderCallingContext())) {
_hidl_cb({});
return Void();
}
hidl_vec<hidl_string> list;
list.resize(countExistingService());
size_t idx = 0;
forEachExistingService([&] (const HidlService *service) {
list[idx++] = service->string();
return true; // continue
});
_hidl_cb(list);
return Void();
}
Return<void> ServiceManager::listByInterface(const hidl_string& fqName,
listByInterface_cb _hidl_cb) {
if (!mAcl.canGet(fqName, getBinderCallingContext())) {
_hidl_cb({});
return Void();
}
auto ifaceIt = mServiceMap.find(fqName);
if (ifaceIt == mServiceMap.end()) {
_hidl_cb(hidl_vec<hidl_string>());
return Void();
}
const auto &instanceMap = ifaceIt->second.getInstanceMap();
hidl_vec<hidl_string> list;
size_t total = 0;
for (const auto &serviceMapping : instanceMap) {
const std::unique_ptr<HidlService> &service = serviceMapping.second;
if (service->getService() == nullptr) continue;
++total;
}
list.resize(total);
size_t idx = 0;
for (const auto &serviceMapping : instanceMap) {
const std::unique_ptr<HidlService> &service = serviceMapping.second;
if (service->getService() == nullptr) continue;
list[idx++] = service->getInstanceName();
}
_hidl_cb(list);
return Void();
}
Return<bool> ServiceManager::registerForNotifications(const hidl_string& fqName,
const hidl_string& name,
const sp<IServiceNotification>& callback) {
if (callback == nullptr) {
return false;
}
if (!mAcl.canGet(fqName, getBinderCallingContext())) {
return false;
}
PackageInterfaceMap &ifaceMap = mServiceMap[fqName];
if (name.empty()) {
bool ret = callback->linkToDeath(this, kPackageListenerDiedCookie).withDefault(false);
if (!ret) {
LOG(ERROR) << "Failed to register death recipient for " << fqName << "/" << name;
return false;
}
ifaceMap.addPackageListener(callback);
return true;
}
HidlService *service = ifaceMap.lookup(name);
bool ret = callback->linkToDeath(this, kServiceListenerDiedCookie).withDefault(false);
if (!ret) {
LOG(ERROR) << "Failed to register death recipient for " << fqName << "/" << name;
return false;
}
if (service == nullptr) {
auto adding = std::make_unique<HidlService>(fqName, name);
adding->addListener(callback);
ifaceMap.insertService(std::move(adding));
} else {
service->addListener(callback);
}
return true;
}
Return<bool> ServiceManager::unregisterForNotifications(const hidl_string& fqName,
const hidl_string& name,
const sp<IServiceNotification>& callback) {
if (callback == nullptr) {
LOG(ERROR) << "Cannot unregister null callback for " << fqName << "/" << name;
return false;
}
// NOTE: don't need ACL since callback is binder token, and if someone has gotten it,
// then they already have access to it.
if (fqName.empty()) {
bool success = false;
success |= removePackageListener(callback);
success |= removeServiceListener(callback);
return success;
}
PackageInterfaceMap &ifaceMap = mServiceMap[fqName];
if (name.empty()) {
bool success = false;
success |= ifaceMap.removePackageListener(callback);
success |= ifaceMap.removeServiceListener(callback);
return success;
}
HidlService *service = ifaceMap.lookup(name);
if (service == nullptr) {
return false;
}
return service->removeListener(callback);
}
Return<bool> ServiceManager::registerClientCallback(const hidl_string& hidlFqName,
const hidl_string& hidlName,
const sp<IBase>& server,
const sp<IClientCallback>& cb) {
if (server == nullptr || cb == nullptr) return false;
const std::string fqName = hidlFqName;
const std::string name = hidlName;
// only the server of the interface can register a client callback
pid_t pid = IPCThreadState::self()->getCallingPid();
if (!mAcl.canAdd(fqName, getBinderCallingContext())) {
return false;
}
HidlService* registered = lookup(fqName, name);
if (registered == nullptr) {
return false;
}
// sanity
if (registered->getDebugPid() != pid) {
LOG(WARNING) << "Only a server can register for client callbacks (for " << fqName
<< "/" << name << ")";
return false;
}
sp<IBase> service = registered->getService();
if (!interfacesEqual(service, server)) {
LOG(WARNING) << "Tried to register client callback for " << fqName << "/" << name
<< " but a different service is registered under this name.";
return false;
}
bool linkRet = cb->linkToDeath(this, kClientCallbackDiedCookie).withDefault(false);
if (!linkRet) {
LOG(ERROR) << "Could not link to death for registerClientCallback";
return false;
}
// knownClientCount
// - one from binder transaction (base here)
// - one from hwservicemanager
registered->addClientCallback(cb, 2 /*knownClientCount*/);
return true;
}
Return<bool> ServiceManager::unregisterClientCallback(const sp<IBase>& server,
const sp<IClientCallback>& cb) {
if (cb == nullptr) return false;
bool removed = false;
forEachExistingService([&] (HidlService *service) {
if (server == nullptr || interfacesEqual(service->getService(), server)) {
removed |= service->removeClientCallback(cb);
}
return true; // continue
});
return removed;
}
void ServiceManager::handleClientCallbacks() {
forEachServiceEntry([&] (HidlService *service) {
// hwservicemanager will hold one reference, so knownClientCount is 1.
service->handleClientCallbacks(true /* isCalledOnInterval */, 1 /*knownClientCount*/);
return true; // continue
});
}
Return<bool> ServiceManager::addWithChain(const hidl_string& name,
const sp<IBase>& service,
const hidl_vec<hidl_string>& chain) {
if (service == nullptr) {
return false;
}
auto callingContext = getBinderCallingContext();
return addImpl(name, service, chain, callingContext);
}
Return<void> ServiceManager::listManifestByInterface(const hidl_string& fqName,
listManifestByInterface_cb _hidl_cb) {
if (!mAcl.canGet(fqName, getBinderCallingContext())) {
_hidl_cb({});
return Void();
}
std::set<std::string> instances = getInstances(fqName);
hidl_vec<hidl_string> ret(instances.begin(), instances.end());
_hidl_cb(ret);
return Void();
}
Return<bool> ServiceManager::tryUnregister(const hidl_string& hidlFqName,
const hidl_string& hidlName,
const sp<IBase>& service) {
const std::string fqName = hidlFqName;
const std::string name = hidlName;
if (service == nullptr) {
return false;
}
if (!mAcl.canAdd(fqName, getBinderCallingContext())) {
return false;
}
HidlService* registered = lookup(fqName, name);
// sanity
pid_t pid = IPCThreadState::self()->getCallingPid();
if (registered->getDebugPid() != pid) {
LOG(WARNING) << "Only a server can unregister itself (for " << fqName
<< "/" << name << ")";
return false;
}
sp<IBase> server = registered->getService();
if (!interfacesEqual(service, server)) {
LOG(WARNING) << "Tried to unregister for " << fqName << "/" << name
<< " but a different service is registered under this name.";
return false;
}
// knownClientCount
// - one from binder transaction (base here)
// - one from hwservicemanager
bool clients = registered->forceHandleClientCallbacks(false /*isCalledOnInterval*/, 2 /*knownClientCount*/);
if (clients) {
// client callbacks are either disabled or there are other clients
LOG(INFO) << "Tried to unregister for " << fqName << "/" << name
<< " but there are clients: " << clients;
return false;
}
// will remove entire parent hierarchy
bool success = removeService(service, &name /*restrictToInstanceName*/);
if (registered->getService() != nullptr) {
LOG(ERROR) << "Bad state. Unregistration failed for " << fqName << "/" << name << ".";
return false;
}
return success;
}
Return<void> ServiceManager::debugDump(debugDump_cb _cb) {
if (!mAcl.canList(getBinderCallingContext())) {
_cb({});
return Void();
}
std::vector<IServiceManager::InstanceDebugInfo> list;
forEachServiceEntry([&] (const HidlService *service) {
hidl_vec<int32_t> clientPids;
clientPids.resize(service->getPassthroughClients().size());
size_t i = 0;
for (pid_t p : service->getPassthroughClients()) {
clientPids[i++] = p;
}
list.push_back({
.interfaceName = service->getInterfaceName(),
.instanceName = service->getInstanceName(),
.pid = service->getDebugPid(),
.clientPids = clientPids,
.arch = ::android::hidl::base::V1_0::DebugInfo::Architecture::UNKNOWN
});
return true; // continue
});
_cb(list);
return Void();
}
Return<void> ServiceManager::registerPassthroughClient(const hidl_string &fqName,
const hidl_string &name) {
auto callingContext = getBinderCallingContext();
if (!mAcl.canGet(fqName, callingContext)) {
/* We guard this function with "get", because it's typically used in
* the getService() path, albeit for a passthrough service in this
* case
*/
return Void();
}
PackageInterfaceMap &ifaceMap = mServiceMap[fqName];
if (name.empty()) {
LOG(WARNING) << "registerPassthroughClient encounters empty instance name for "
<< fqName.c_str();
return Void();
}
HidlService *service = ifaceMap.lookup(name);
if (service == nullptr) {
auto adding = std::make_unique<HidlService>(fqName, name);
adding->registerPassthroughClient(callingContext.pid);
ifaceMap.insertService(std::move(adding));
} else {
service->registerPassthroughClient(callingContext.pid);
}
return Void();
}
bool ServiceManager::removeService(const wp<IBase>& who, const std::string* restrictToInstanceName) {
bool keepInstance = false;
bool removed = false;
for (auto &interfaceMapping : mServiceMap) {
auto &instanceMap = interfaceMapping.second.getInstanceMap();
for (auto &servicePair : instanceMap) {
const std::string &instanceName = servicePair.first;
const std::unique_ptr<HidlService> &service = servicePair.second;
if (interfacesEqual(service->getService(), who.promote())) {
if (restrictToInstanceName != nullptr && *restrictToInstanceName != instanceName) {
// We cannot remove all instances of this service, so we don't return that it
// has been entirely removed.
keepInstance = true;
continue;
}
service->setService(nullptr, static_cast<pid_t>(IServiceManager::PidConstant::NO_PID));
removed = true;
}
}
}
return !keepInstance && removed;
}
bool ServiceManager::removePackageListener(const wp<IBase>& who) {
bool found = false;
for (auto &interfaceMapping : mServiceMap) {
found |= interfaceMapping.second.removePackageListener(who);
}
return found;
}
bool ServiceManager::removeServiceListener(const wp<IBase>& who) {
bool found = false;
for (auto &interfaceMapping : mServiceMap) {
auto &packageInterfaceMap = interfaceMapping.second;
found |= packageInterfaceMap.removeServiceListener(who);
}
return found;
}
} // namespace implementation
} // namespace manager
} // namespace hidl
} // namespace android