ServiceManager signals init to start lazy services

This allows services to be disabled at boot and dynamically enabled as
they are needed. When servicemanager receives a getService request,
it will check whether the service is running. If it is not,
servicemanager will attempt to start the service by signaling init with
the ctl.interface_start control message.

Bug: 138756857
Test: Manual (using mediaextractor as a test service), test_sm
Change-Id: Ic2d47d21769b936381e3fae2f2cf739d3b7501a4
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index b3aa342..463d67f 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -17,8 +17,10 @@
 #include "ServiceManager.h"
 
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <cutils/android_filesystem_config.h>
 #include <cutils/multiuser.h>
+#include <thread>
 
 using ::android::binder::Status;
 
@@ -41,39 +43,44 @@
 }
 
 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);
+    *outBinder = tryGetService(name, true);
+    // returns ok regardless of result for legacy reasons
+    return Status::ok();
 }
 
 Status ServiceManager::checkService(const std::string& name, sp<IBinder>* outBinder) {
+    *outBinder = tryGetService(name, false);
+    // returns ok regardless of result for legacy reasons
+    return Status::ok();
+}
+
+sp<IBinder> ServiceManager::tryGetService(const std::string& name, bool startIfNotFound) {
     auto ctx = mAccess->getCallingContext();
 
-    auto it = mNameToService.find(name);
-    if (it == mNameToService.end()) {
-        *outBinder = nullptr;
-        return Status::ok();
-    }
+    sp<IBinder> out;
+    if (auto it = mNameToService.find(name); it != mNameToService.end()) {
+        const Service& service = it->second;
 
-    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 (!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 (isIsolated) {
+                return nullptr;
+            }
         }
+        out = service.binder;
     }
 
     if (!mAccess->canFind(ctx, name)) {
-        // returns ok and null for legacy reasons
-        *outBinder = nullptr;
-        return Status::ok();
+        return nullptr;
     }
 
-    *outBinder = service.binder;
-    return Status::ok();
+    if (!out && startIfNotFound) {
+        tryStartService(name);
+    }
+
+    return out;
 }
 
 bool isValidServiceName(const std::string& name) {
@@ -253,4 +260,13 @@
     }
 }
 
+void ServiceManager::tryStartService(const std::string& name) {
+    ALOGI("Since '%s' could not be found, trying to start it as a lazy AIDL service",
+          name.c_str());
+
+    std::thread([=] {
+        (void)base::SetProperty("ctl.interface_start", "aidl/" + name);
+    }).detach();
+}
+
 }  // namespace android