servicemanager: notifications

You can now register and receive notifications for new services.

Eye towards:
- avoiding while(true) getService loops
- need it to make AIDL dynamic HALs
- need it for feature parity w/ HIDL

Bug: 136027762
Test: servicemanager_test
Change-Id: Iffbf984e87214aa9c9b7af8a5ae682e127ba40a5
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 119e4c3..b3aa342 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -28,6 +28,13 @@
 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 @@
         .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 @@
     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 @@
             ++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 43723c5..fcc5124 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 @@
 
     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 91485e4..3c211d2 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::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(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 @@
 
     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 @@
     // 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 027418a..86f19c5 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -145,6 +145,7 @@
     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 0000000..b29dfed
--- /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 50a72aa..60c2cce 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 @@
      * 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);
 }