| #define LOG_TAG "hwservicemanager" |
| |
| #include "ServiceManager.h" |
| #include "Vintf.h" |
| |
| #include <android-base/logging.h> |
| #include <hwbinder/IPCThreadState.h> |
| #include <hidl/HidlSupport.h> |
| #include <regex> |
| #include <sstream> |
| |
| using android::hardware::IPCThreadState; |
| |
| namespace android { |
| namespace hidl { |
| namespace manager { |
| namespace V1_0 { |
| 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: |
| remove(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) { |
| bool found = false; |
| |
| for (auto it = mPackageListeners.begin(); it != mPackageListeners.end();) { |
| if (*it == who) { |
| it = mPackageListeners.erase(it); |
| found = true; |
| } else { |
| ++it; |
| } |
| } |
| |
| 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 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, 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<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::remove(const wp<IBase>& who) { |
| 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 (service->getService() == who) { |
| 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 &instanceMap = interfaceMapping.second.getInstanceMap(); |
| |
| for (auto &servicePair : instanceMap) { |
| const std::unique_ptr<HidlService> &service = servicePair.second; |
| found |= service->removeListener(who); |
| } |
| } |
| return found; |
| } |
| } // namespace implementation |
| } // namespace V1_0 |
| } // namespace manager |
| } // namespace hidl |
| } // namespace android |