diff options
| author | 2019-08-30 12:08:49 -0700 | |
|---|---|---|
| committer | 2019-08-30 12:08:49 -0700 | |
| commit | 1aed07f3295a34e2e1c41a841d0b92b6176b84c6 (patch) | |
| tree | 9d514c5ceae89c2fbbfeae06d485e651a3118703 | |
| parent | 2b6dd0ecb324e3a113dacb81b0beedfb25c9d97e (diff) | |
| parent | 7576e3a7e9dedb1926d9b1437d5ed059bdfbbb18 (diff) | |
servicemanager: notifications am: 27cfab02a0 am: 567ff3bae5 am: a122d86743
am: 7576e3a7e9
Change-Id: I599c12d24c821aec06c956f19e0ff8ebdbcfabb8
| -rw-r--r-- | cmds/servicemanager/ServiceManager.cpp | 97 | ||||
| -rw-r--r-- | cmds/servicemanager/ServiceManager.h | 25 | ||||
| -rw-r--r-- | cmds/servicemanager/test_sm.cpp | 170 | ||||
| -rw-r--r-- | libs/binder/Android.bp | 1 | ||||
| -rw-r--r-- | libs/binder/aidl/android/os/IServiceCallback.aidl | 30 | ||||
| -rw-r--r-- | libs/binder/aidl/android/os/IServiceManager.aidl | 12 |
6 files changed, 325 insertions, 10 deletions
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp index 119e4c387c..b3aa342a19 100644 --- a/cmds/servicemanager/ServiceManager.cpp +++ b/cmds/servicemanager/ServiceManager.cpp @@ -28,6 +28,13 @@ ServiceManager::ServiceManager(std::unique_ptr<Access>&& access) : mAccess(std:: 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; } @@ -117,6 +124,14 @@ Status ServiceManager::addService(const std::string& name, const sp<IBinder>& bi .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(); } @@ -146,6 +161,84 @@ Status ServiceManager::listServices(int32_t dumpPriority, std::vector<std::strin 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) { @@ -154,6 +247,10 @@ void ServiceManager::binderDied(const wp<IBinder>& who) { ++it; } } + + for (auto it = mNameToCallback.begin(); it != mNameToCallback.end();) { + removeCallback(who, &it, nullptr /*found*/); + } } } // namespace android diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h index 43723c52c7..fcc5124fa2 100644 --- a/cmds/servicemanager/ServiceManager.h +++ b/cmds/servicemanager/ServiceManager.h @@ -17,11 +17,14 @@ #pragma once #include <android/os/BnServiceManager.h> +#include <android/os/IServiceCallback.h> #include "Access.h" namespace android { +using os::IServiceCallback; + class ServiceManager : public os::BnServiceManager, public IBinder::DeathRecipient { public: ServiceManager(std::unique_ptr<Access>&& access); @@ -29,19 +32,35 @@ public: binder::Status getService(const std::string& name, sp<IBinder>* outBinder) override; binder::Status checkService(const std::string& name, sp<IBinder>* outBinder) override; - binder::Status addService(const std::string& name, const sp<IBinder>& binder, bool allowIsolated, int32_t dumpPriority) override; + binder::Status addService(const std::string& name, const sp<IBinder>& binder, + bool allowIsolated, int32_t dumpPriority) override; binder::Status listServices(int32_t dumpPriority, std::vector<std::string>* outList) override; + binder::Status registerForNotifications(const std::string& name, + const sp<IServiceCallback>& callback) override; + binder::Status unregisterForNotifications(const std::string& name, + const sp<IServiceCallback>& callback) override; void binderDied(const wp<IBinder>& who) override; private: struct Service { - sp<IBinder> binder; + sp<IBinder> binder; // not null bool allowIsolated; int32_t dumpPriority; }; - std::map<std::string, Service> mNameToService; + using CallbackMap = std::map<std::string, std::vector<sp<IServiceCallback>>>; + using ServiceMap = std::map<std::string, Service>; + + // removes a callback from mNameToCallback, removing it if the vector is empty + // this updates iterator to the next location + void removeCallback(const wp<IBinder>& who, + CallbackMap::iterator* it, + bool* found); + + CallbackMap mNameToCallback; + ServiceMap mNameToService; + std::unique_ptr<Access> mAccess; }; diff --git a/cmds/servicemanager/test_sm.cpp b/cmds/servicemanager/test_sm.cpp index 91485e456b..3c211d285f 100644 --- a/cmds/servicemanager/test_sm.cpp +++ b/cmds/servicemanager/test_sm.cpp @@ -14,7 +14,10 @@ * limitations under the License. */ +#include <android/os/BnServiceCallback.h> +#include <binder/Binder.h> #include <binder/ProcessState.h> +#include <binder/IServiceManager.h> #include <cutils/android_filesystem_config.h> #include <gtest/gtest.h> #include <gmock/gmock.h> @@ -24,8 +27,11 @@ using android::sp; using android::Access; +using android::BBinder; using android::IBinder; using android::ServiceManager; +using android::binder::Status; +using android::os::BnServiceCallback; using android::os::IServiceManager; using testing::_; using testing::ElementsAre; @@ -33,9 +39,14 @@ using testing::NiceMock; using testing::Return; static sp<IBinder> getBinder() { - // It doesn't matter what remote binder it is, we just need one so that linkToDeath will work. - // The context manager (servicemanager) is easy to get and is in another process. - return android::ProcessState::self()->getContextObject(nullptr); + class LinkableBinder : public BBinder { + android::status_t linkToDeath(const sp<DeathRecipient>&, void*, uint32_t) override { + // let SM linkToDeath + return android::OK; + } + }; + + return new LinkableBinder; } class MockAccess : public Access { @@ -132,12 +143,14 @@ TEST(AddService, NoPermissions) { TEST(GetService, HappyHappy) { auto sm = getPermissiveServiceManager(); - EXPECT_TRUE(sm->addService("foo", getBinder(), false /*allowIsolated*/, + sp<IBinder> service = getBinder(); + + EXPECT_TRUE(sm->addService("foo", service, false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()); sp<IBinder> out; EXPECT_TRUE(sm->getService("foo", &out).isOk()); - EXPECT_EQ(getBinder(), out); + EXPECT_EQ(service, out); } TEST(GetService, NonExistant) { @@ -181,12 +194,13 @@ TEST(GetService, AllowedFromIsolated) { sp<ServiceManager> sm = new ServiceManager(std::move(access)); - EXPECT_TRUE(sm->addService("foo", getBinder(), true /*allowIsolated*/, + sp<IBinder> service = getBinder(); + EXPECT_TRUE(sm->addService("foo", service, true /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()); sp<IBinder> out; EXPECT_TRUE(sm->getService("foo", &out).isOk()); - EXPECT_EQ(getBinder(), out.get()); + EXPECT_EQ(service, out.get()); } TEST(GetService, NotAllowedFromIsolated) { @@ -265,3 +279,145 @@ TEST(ListServices, CriticalServices) { // all there and in the right order EXPECT_THAT(out, ElementsAre("sa")); } + +class CallbackHistorian : public BnServiceCallback { + Status onRegistration(const std::string& name, const sp<IBinder>& binder) override { + registrations.push_back(name); + binders.push_back(binder); + return Status::ok(); + } + + android::status_t linkToDeath(const sp<DeathRecipient>&, void*, uint32_t) override { + // let SM linkToDeath + return android::OK; + } + +public: + std::vector<std::string> registrations; + std::vector<sp<IBinder>> binders; +}; + +TEST(ServiceNotifications, NoPermissionsRegister) { + std::unique_ptr<MockAccess> access = std::make_unique<NiceMock<MockAccess>>(); + + EXPECT_CALL(*access, getCallingContext()).WillOnce(Return(Access::CallingContext{})); + EXPECT_CALL(*access, canFind(_,_)).WillOnce(Return(false)); + + sp<ServiceManager> sm = new ServiceManager(std::move(access)); + + sp<CallbackHistorian> cb = new CallbackHistorian; + + EXPECT_EQ(sm->registerForNotifications("foofoo", cb).exceptionCode(), + Status::EX_SECURITY); +} + +TEST(ServiceNotifications, NoPermissionsUnregister) { + std::unique_ptr<MockAccess> access = std::make_unique<NiceMock<MockAccess>>(); + + EXPECT_CALL(*access, getCallingContext()).WillOnce(Return(Access::CallingContext{})); + EXPECT_CALL(*access, canFind(_,_)).WillOnce(Return(false)); + + sp<ServiceManager> sm = new ServiceManager(std::move(access)); + + sp<CallbackHistorian> cb = new CallbackHistorian; + + // should always hit security error first + EXPECT_EQ(sm->unregisterForNotifications("foofoo", cb).exceptionCode(), + Status::EX_SECURITY); +} + +TEST(ServiceNotifications, InvalidName) { + auto sm = getPermissiveServiceManager(); + + sp<CallbackHistorian> cb = new CallbackHistorian; + + EXPECT_EQ(sm->registerForNotifications("foo@foo", cb).exceptionCode(), + Status::EX_ILLEGAL_ARGUMENT); +} + +TEST(ServiceNotifications, NullCallback) { + auto sm = getPermissiveServiceManager(); + + EXPECT_EQ(sm->registerForNotifications("foofoo", nullptr).exceptionCode(), + Status::EX_NULL_POINTER); +} + +TEST(ServiceNotifications, Unregister) { + auto sm = getPermissiveServiceManager(); + + sp<CallbackHistorian> cb = new CallbackHistorian; + + EXPECT_TRUE(sm->registerForNotifications("foofoo", cb).isOk()); + EXPECT_EQ(sm->unregisterForNotifications("foofoo", cb).exceptionCode(), 0); +} + +TEST(ServiceNotifications, UnregisterWhenNoRegistrationExists) { + auto sm = getPermissiveServiceManager(); + + sp<CallbackHistorian> cb = new CallbackHistorian; + + EXPECT_EQ(sm->unregisterForNotifications("foofoo", cb).exceptionCode(), + Status::EX_ILLEGAL_STATE); +} + +TEST(ServiceNotifications, NoNotification) { + auto sm = getPermissiveServiceManager(); + + sp<CallbackHistorian> cb = new CallbackHistorian; + + EXPECT_TRUE(sm->registerForNotifications("foofoo", cb).isOk()); + EXPECT_TRUE(sm->addService("otherservice", getBinder(), + false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()); + + EXPECT_THAT(cb->registrations, ElementsAre()); + EXPECT_THAT(cb->binders, ElementsAre()); +} + +TEST(ServiceNotifications, GetNotification) { + auto sm = getPermissiveServiceManager(); + + sp<CallbackHistorian> cb = new CallbackHistorian; + + sp<IBinder> service = getBinder(); + + EXPECT_TRUE(sm->registerForNotifications("asdfasdf", cb).isOk()); + EXPECT_TRUE(sm->addService("asdfasdf", service, + false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()); + + EXPECT_THAT(cb->registrations, ElementsAre("asdfasdf")); + EXPECT_THAT(cb->binders, ElementsAre(service)); +} + +TEST(ServiceNotifications, GetNotificationForAlreadyRegisteredService) { + auto sm = getPermissiveServiceManager(); + + sp<CallbackHistorian> cb = new CallbackHistorian; + + sp<IBinder> service = getBinder(); + + EXPECT_TRUE(sm->addService("asdfasdf", service, + false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()); + + EXPECT_TRUE(sm->registerForNotifications("asdfasdf", cb).isOk()); + + EXPECT_THAT(cb->registrations, ElementsAre("asdfasdf")); + EXPECT_THAT(cb->binders, ElementsAre(service)); +} + +TEST(ServiceNotifications, GetMultipleNotification) { + auto sm = getPermissiveServiceManager(); + + sp<CallbackHistorian> cb = new CallbackHistorian; + + sp<IBinder> binder1 = getBinder(); + sp<IBinder> binder2 = getBinder(); + + EXPECT_TRUE(sm->registerForNotifications("asdfasdf", cb).isOk()); + EXPECT_TRUE(sm->addService("asdfasdf", binder1, + false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()); + EXPECT_TRUE(sm->addService("asdfasdf", binder2, + false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk()); + + EXPECT_THAT(cb->registrations, ElementsAre("asdfasdf", "asdfasdf")); + EXPECT_THAT(cb->registrations, ElementsAre("asdfasdf", "asdfasdf")); +} diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index 0b71d1f60c..6ece4a2162 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -143,6 +143,7 @@ filegroup { name: "libbinder_aidl", srcs: [ "aidl/android/content/pm/IPackageManagerNative.aidl", + "aidl/android/os/IServiceCallback.aidl", "aidl/android/os/IServiceManager.aidl", ], path: "aidl", diff --git a/libs/binder/aidl/android/os/IServiceCallback.aidl b/libs/binder/aidl/android/os/IServiceCallback.aidl new file mode 100644 index 0000000000..b29dfedf70 --- /dev/null +++ b/libs/binder/aidl/android/os/IServiceCallback.aidl @@ -0,0 +1,30 @@ +/* + * 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. + */ + +package android.os; + +/** + * @hide + */ +oneway interface IServiceCallback { + /** + * Called when a service is registered. + * + * @param name the service name that has been registered with + * @param binder the binder that is registered + */ + void onRegistration(@utf8InCpp String name, IBinder binder); +} diff --git a/libs/binder/aidl/android/os/IServiceManager.aidl b/libs/binder/aidl/android/os/IServiceManager.aidl index 50a72aa9e4..60c2cceaf2 100644 --- a/libs/binder/aidl/android/os/IServiceManager.aidl +++ b/libs/binder/aidl/android/os/IServiceManager.aidl @@ -16,6 +16,8 @@ package android.os; +import android.os.IServiceCallback; + /** * Basic interface for finding and publishing system services. * @@ -77,4 +79,14 @@ interface IServiceManager { * Return a list of all currently running services. */ @utf8InCpp String[] listServices(int dumpPriority); + + /** + * Request a callback when a service is registered. + */ + void registerForNotifications(@utf8InCpp String name, IServiceCallback callback); + + /** + * Unregisters all requests for notifications for a specific callback. + */ + void unregisterForNotifications(@utf8InCpp String name, IServiceCallback callback); } |