| /* | 
 |  * 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 |