diff options
Diffstat (limited to 'cmds/servicemanager/ServiceManager.cpp')
-rw-r--r-- | cmds/servicemanager/ServiceManager.cpp | 256 |
1 files changed, 256 insertions, 0 deletions
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp new file mode 100644 index 0000000000..b3aa342a19 --- /dev/null +++ b/cmds/servicemanager/ServiceManager.cpp @@ -0,0 +1,256 @@ +/* + * Copyright (C) 2019 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. + */ + +#include "ServiceManager.h" + +#include <android-base/logging.h> +#include <cutils/android_filesystem_config.h> +#include <cutils/multiuser.h> + +using ::android::binder::Status; + +namespace android { + +ServiceManager::ServiceManager(std::unique_ptr<Access>&& access) : mAccess(std::move(access)) {} +ServiceManager::~ServiceManager() { + // this should only happen in tests + + for (const auto& [name, callbacks] : mNameToCallback) { + CHECK(!callbacks.empty()) << name; + for (const auto& callback : callbacks) { + CHECK(callback != nullptr) << name; + } + } + + for (const auto& [name, service] : mNameToService) { + CHECK(service.binder != nullptr) << name; + } +} + +Status ServiceManager::getService(const std::string& name, sp<IBinder>* outBinder) { + // Servicemanager is single-threaded and cannot block. This method exists for legacy reasons. + return checkService(name, outBinder); +} + +Status ServiceManager::checkService(const std::string& name, sp<IBinder>* outBinder) { + auto ctx = mAccess->getCallingContext(); + + auto it = mNameToService.find(name); + if (it == mNameToService.end()) { + *outBinder = nullptr; + return Status::ok(); + } + + const Service& service = it->second; + + if (!service.allowIsolated) { + uid_t appid = multiuser_get_app_id(ctx.uid); + bool isIsolated = appid >= AID_ISOLATED_START && appid <= AID_ISOLATED_END; + + if (isIsolated) { + *outBinder = nullptr; + return Status::ok(); + } + } + + if (!mAccess->canFind(ctx, name)) { + // returns ok and null for legacy reasons + *outBinder = nullptr; + return Status::ok(); + } + + *outBinder = service.binder; + return Status::ok(); +} + +bool isValidServiceName(const std::string& name) { + if (name.size() == 0) return false; + if (name.size() > 127) return false; + + for (char c : name) { + if (c == '_' || c == '-' || c == '.' || c == '/') continue; + if (c >= 'a' && c <= 'z') continue; + if (c >= 'A' && c <= 'Z') continue; + if (c >= '0' && c <= '9') continue; + return false; + } + + return true; +} + +Status ServiceManager::addService(const std::string& name, const sp<IBinder>& binder, bool allowIsolated, int32_t dumpPriority) { + auto ctx = mAccess->getCallingContext(); + + // apps cannot add services + if (multiuser_get_app_id(ctx.uid) >= AID_APP) { + return Status::fromExceptionCode(Status::EX_SECURITY); + } + + if (!mAccess->canAdd(ctx, name)) { + return Status::fromExceptionCode(Status::EX_SECURITY); + } + + if (binder == nullptr) { + return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT); + } + + if (!isValidServiceName(name)) { + LOG(ERROR) << "Invalid service name: " << name; + return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT); + } + + // implicitly unlinked when the binder is removed + if (OK != binder->linkToDeath(this)) { + LOG(ERROR) << "Could not linkToDeath when adding " << name; + return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE); + } + + mNameToService[name] = Service { + .binder = binder, + .allowIsolated = allowIsolated, + .dumpPriority = dumpPriority, + }; + + auto it = mNameToCallback.find(name); + if (it != mNameToCallback.end()) { + for (const sp<IServiceCallback>& cb : it->second) { + // permission checked in registerForNotifications + cb->onRegistration(name, binder); + } + } + + return Status::ok(); +} + +Status ServiceManager::listServices(int32_t dumpPriority, std::vector<std::string>* outList) { + if (!mAccess->canList(mAccess->getCallingContext())) { + return Status::fromExceptionCode(Status::EX_SECURITY); + } + + size_t toReserve = 0; + for (auto const& [name, service] : mNameToService) { + (void) name; + + if (service.dumpPriority & dumpPriority) ++toReserve; + } + + CHECK(outList->empty()); + + outList->reserve(toReserve); + for (auto const& [name, service] : mNameToService) { + (void) service; + + if (service.dumpPriority & dumpPriority) { + outList->push_back(name); + } + } + + return Status::ok(); +} + +Status ServiceManager::registerForNotifications( + const std::string& name, const sp<IServiceCallback>& callback) { + auto ctx = mAccess->getCallingContext(); + + if (!mAccess->canFind(ctx, name)) { + return Status::fromExceptionCode(Status::EX_SECURITY); + } + + if (!isValidServiceName(name)) { + LOG(ERROR) << "Invalid service name: " << name; + return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT); + } + + if (callback == nullptr) { + return Status::fromExceptionCode(Status::EX_NULL_POINTER); + } + + if (OK != IInterface::asBinder(callback)->linkToDeath(this)) { + LOG(ERROR) << "Could not linkToDeath when adding " << name; + return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE); + } + + mNameToCallback[name].push_back(callback); + + if (auto it = mNameToService.find(name); it != mNameToService.end()) { + const sp<IBinder>& binder = it->second.binder; + + // never null if an entry exists + CHECK(binder != nullptr) << name; + callback->onRegistration(name, binder); + } + + return Status::ok(); +} +Status ServiceManager::unregisterForNotifications( + const std::string& name, const sp<IServiceCallback>& callback) { + auto ctx = mAccess->getCallingContext(); + + if (!mAccess->canFind(ctx, name)) { + return Status::fromExceptionCode(Status::EX_SECURITY); + } + + bool found = false; + + auto it = mNameToCallback.find(name); + if (it != mNameToCallback.end()) { + removeCallback(IInterface::asBinder(callback), &it, &found); + } + + if (!found) { + LOG(ERROR) << "Trying to unregister callback, but none exists " << name; + return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE); + } + + return Status::ok(); +} + +void ServiceManager::removeCallback(const wp<IBinder>& who, + CallbackMap::iterator* it, + bool* found) { + std::vector<sp<IServiceCallback>>& listeners = (*it)->second; + + for (auto lit = listeners.begin(); lit != listeners.end();) { + if (IInterface::asBinder(*lit) == who) { + if(found) *found = true; + lit = listeners.erase(lit); + } else { + ++lit; + } + } + + if (listeners.empty()) { + *it = mNameToCallback.erase(*it); + } else { + it++; + } +} + +void ServiceManager::binderDied(const wp<IBinder>& who) { + for (auto it = mNameToService.begin(); it != mNameToService.end();) { + if (who == it->second.binder) { + it = mNameToService.erase(it); + } else { + ++it; + } + } + + for (auto it = mNameToCallback.begin(); it != mNameToCallback.end();) { + removeCallback(who, &it, nullptr /*found*/); + } +} + +} // namespace android |