You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
507 lines
15 KiB
507 lines
15 KiB
#define LOG_TAG "hwservicemanager" |
|
|
|
#include "ServiceManager.h" |
|
#include "Vintf.h" |
|
|
|
#include <android-base/logging.h> |
|
#include <hwbinder/IPCThreadState.h> |
|
#include <hidl/HidlSupport.h> |
|
#include <hidl/HidlTransportSupport.h> |
|
#include <regex> |
|
#include <sstream> |
|
|
|
using android::hardware::IPCThreadState; |
|
|
|
namespace android { |
|
namespace hidl { |
|
namespace manager { |
|
namespace implementation { |
|
|
|
static constexpr uint64_t kServiceDiedCookie = 0; |
|
static constexpr uint64_t kPackageListenerDiedCookie = 1; |
|
static constexpr uint64_t kServiceListenerDiedCookie = 2; |
|
|
|
size_t ServiceManager::countExistingService() const { |
|
size_t total = 0; |
|
forEachExistingService([&] (const HidlService *) { |
|
++total; |
|
}); |
|
return total; |
|
} |
|
|
|
void ServiceManager::forEachExistingService(std::function<void(const HidlService *)> f) const { |
|
forEachServiceEntry([f] (const HidlService *service) { |
|
if (service->getService() == nullptr) { |
|
return; |
|
} |
|
f(service); |
|
}); |
|
} |
|
|
|
void ServiceManager::forEachServiceEntry(std::function<void(const HidlService *)> f) const { |
|
for (const auto &interfaceMapping : mServiceMap) { |
|
const auto &instanceMap = interfaceMapping.second.getInstanceMap(); |
|
|
|
for (const auto &instanceMapping : instanceMap) { |
|
f(instanceMapping.second.get()); |
|
} |
|
} |
|
} |
|
|
|
void ServiceManager::serviceDied(uint64_t cookie, const wp<IBase>& who) { |
|
switch (cookie) { |
|
case kServiceDiedCookie: |
|
removeService(who); |
|
break; |
|
case kPackageListenerDiedCookie: |
|
removePackageListener(who); |
|
break; |
|
case kServiceListenerDiedCookie: |
|
removeServiceListener(who); |
|
break; |
|
} |
|
} |
|
|
|
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) { |
|
using ::android::hardware::interfacesEqual; |
|
|
|
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) { |
|
using ::android::hardware::interfacesEqual; |
|
|
|
bool found = false; |
|
|
|
for (auto &servicePair : getInstanceMap()) { |
|
const std::unique_ptr<HidlService> &service = servicePair.second; |
|
found |= service->removeListener(who); |
|
} |
|
|
|
return found; |
|
} |
|
|
|
// Methods from ::android::hidl::manager::V1_0::IServiceManager follow. |
|
Return<sp<IBase>> ServiceManager::get(const hidl_string& fqName, |
|
const hidl_string& name) { |
|
pid_t pid = IPCThreadState::self()->getCallingPid(); |
|
if (!mAcl.canGet(fqName, pid)) { |
|
return nullptr; |
|
} |
|
|
|
auto ifaceIt = mServiceMap.find(fqName); |
|
if (ifaceIt == mServiceMap.end()) { |
|
return nullptr; |
|
} |
|
|
|
const PackageInterfaceMap &ifaceMap = ifaceIt->second; |
|
const HidlService *hidlService = ifaceMap.lookup(name); |
|
|
|
if (hidlService == nullptr) { |
|
return nullptr; |
|
} |
|
|
|
return hidlService->getService(); |
|
} |
|
|
|
Return<bool> ServiceManager::add(const hidl_string& name, const sp<IBase>& service) { |
|
bool isValidService = false; |
|
|
|
if (service == nullptr) { |
|
return false; |
|
} |
|
|
|
// TODO(b/34235311): use HIDL way to determine this |
|
// also, this assumes that the PID that is registering is the pid that is the service |
|
pid_t pid = IPCThreadState::self()->getCallingPid(); |
|
auto context = mAcl.getContext(pid); |
|
|
|
auto ret = service->interfaceChain([&](const auto &interfaceChain) { |
|
if (interfaceChain.size() == 0) { |
|
return; |
|
} |
|
|
|
// First, verify you're allowed to add() the whole interface hierarchy |
|
for(size_t i = 0; i < interfaceChain.size(); i++) { |
|
std::string fqName = interfaceChain[i]; |
|
|
|
if (!mAcl.canAdd(fqName, context, pid)) { |
|
return; |
|
} |
|
} |
|
|
|
for(size_t i = 0; i < interfaceChain.size(); i++) { |
|
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, pid)); |
|
} else { |
|
if (hidlService->getService() != nullptr) { |
|
auto ret = hidlService->getService()->unlinkToDeath(this); |
|
ret.isOk(); // ignore |
|
} |
|
hidlService->setService(service, pid); |
|
} |
|
|
|
ifaceMap.sendPackageRegistrationNotification(fqName, name); |
|
} |
|
|
|
auto linkRet = service->linkToDeath(this, 0 /*cookie*/); |
|
linkRet.isOk(); // ignore |
|
|
|
isValidService = true; |
|
}); |
|
|
|
if (!ret.isOk()) { |
|
LOG(ERROR) << "Failed to retrieve interface chain."; |
|
return false; |
|
} |
|
|
|
return isValidService; |
|
} |
|
|
|
Return<ServiceManager::Transport> ServiceManager::getTransport(const hidl_string& fqName, |
|
const hidl_string& name) { |
|
using ::android::hardware::getTransport; |
|
|
|
pid_t pid = IPCThreadState::self()->getCallingPid(); |
|
if (!mAcl.canGet(fqName, pid)) { |
|
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) { |
|
pid_t pid = IPCThreadState::self()->getCallingPid(); |
|
if (!mAcl.canList(pid)) { |
|
_hidl_cb({}); |
|
return Void(); |
|
} |
|
|
|
hidl_vec<hidl_string> list; |
|
|
|
list.resize(countExistingService()); |
|
|
|
size_t idx = 0; |
|
forEachExistingService([&] (const HidlService *service) { |
|
list[idx++] = service->string(); |
|
}); |
|
|
|
_hidl_cb(list); |
|
return Void(); |
|
} |
|
|
|
Return<void> ServiceManager::listByInterface(const hidl_string& fqName, |
|
listByInterface_cb _hidl_cb) { |
|
pid_t pid = IPCThreadState::self()->getCallingPid(); |
|
if (!mAcl.canGet(fqName, pid)) { |
|
_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; |
|
} |
|
|
|
pid_t pid = IPCThreadState::self()->getCallingPid(); |
|
if (!mAcl.canGet(fqName, pid)) { |
|
return false; |
|
} |
|
|
|
PackageInterfaceMap &ifaceMap = mServiceMap[fqName]; |
|
|
|
if (name.empty()) { |
|
auto ret = callback->linkToDeath(this, kPackageListenerDiedCookie /*cookie*/); |
|
if (!ret.isOk()) { |
|
LOG(ERROR) << "Failed to register death recipient for " << fqName << "/" << name; |
|
return false; |
|
} |
|
ifaceMap.addPackageListener(callback); |
|
return true; |
|
} |
|
|
|
HidlService *service = ifaceMap.lookup(name); |
|
|
|
auto ret = callback->linkToDeath(this, kServiceListenerDiedCookie); |
|
if (!ret.isOk()) { |
|
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<void> ServiceManager::debugDump(debugDump_cb _cb) { |
|
pid_t pid = IPCThreadState::self()->getCallingPid(); |
|
if (!mAcl.canList(pid)) { |
|
_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({ |
|
.pid = service->getPid(), |
|
.interfaceName = service->getInterfaceName(), |
|
.instanceName = service->getInstanceName(), |
|
.clientPids = clientPids, |
|
.arch = ::android::hidl::base::V1_0::DebugInfo::Architecture::UNKNOWN |
|
}); |
|
}); |
|
|
|
_cb(list); |
|
return Void(); |
|
} |
|
|
|
|
|
Return<void> ServiceManager::registerPassthroughClient(const hidl_string &fqName, |
|
const hidl_string &name) { |
|
pid_t pid = IPCThreadState::self()->getCallingPid(); |
|
if (!mAcl.canGet(fqName, pid)) { |
|
/* 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(pid); |
|
ifaceMap.insertService(std::move(adding)); |
|
} else { |
|
service->registerPassthroughClient(pid); |
|
} |
|
return Void(); |
|
} |
|
|
|
bool ServiceManager::removeService(const wp<IBase>& who) { |
|
using ::android::hardware::interfacesEqual; |
|
|
|
bool found = false; |
|
for (auto &interfaceMapping : mServiceMap) { |
|
auto &instanceMap = interfaceMapping.second.getInstanceMap(); |
|
|
|
for (auto &servicePair : instanceMap) { |
|
const std::unique_ptr<HidlService> &service = servicePair.second; |
|
if (interfacesEqual(service->getService(), who.promote())) { |
|
service->setService(nullptr, static_cast<pid_t>(IServiceManager::PidConstant::NO_PID)); |
|
found = true; |
|
} |
|
} |
|
} |
|
return found; |
|
} |
|
|
|
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
|
|
|