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
diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h
index fcc5124..006e519 100644
--- a/cmds/servicemanager/ServiceManager.h
+++ b/cmds/servicemanager/ServiceManager.h
@@ -30,6 +30,7 @@
ServiceManager(std::unique_ptr<Access>&& access);
~ServiceManager();
+ // getService will try to start any services it cannot find
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,
@@ -42,6 +43,9 @@
void binderDied(const wp<IBinder>& who) override;
+protected:
+ virtual void tryStartService(const std::string& name);
+
private:
struct Service {
sp<IBinder> binder; // not null
@@ -57,6 +61,7 @@
void removeCallback(const wp<IBinder>& who,
CallbackMap::iterator* it,
bool* found);
+ sp<IBinder> tryGetService(const std::string& name, bool startIfNotFound);
CallbackMap mNameToCallback;
ServiceMap mNameToService;
diff --git a/cmds/servicemanager/test_sm.cpp b/cmds/servicemanager/test_sm.cpp
index 3c211d2..25245be 100644
--- a/cmds/servicemanager/test_sm.cpp
+++ b/cmds/servicemanager/test_sm.cpp
@@ -57,6 +57,12 @@
MOCK_METHOD1(canList, bool(const CallingContext&));
};
+class MockServiceManager : public ServiceManager {
+ public:
+ MockServiceManager(std::unique_ptr<Access>&& access) : ServiceManager(std::move(access)) {}
+ MOCK_METHOD1(tryStartService, void(const std::string& name));
+};
+
static sp<ServiceManager> getPermissiveServiceManager() {
std::unique_ptr<MockAccess> access = std::make_unique<NiceMock<MockAccess>>();
@@ -65,7 +71,7 @@
ON_CALL(*access, canFind(_, _)).WillByDefault(Return(true));
ON_CALL(*access, canList(_)).WillByDefault(Return(true));
- sp<ServiceManager> sm = new ServiceManager(std::move(access));
+ sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access));
return sm;
}
@@ -113,7 +119,7 @@
.uid = uid,
}));
EXPECT_CALL(*access, canAdd(_, _)).Times(0);
- sp<ServiceManager> sm = new ServiceManager(std::move(access));
+ sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access));
EXPECT_FALSE(sm->addService("foo", getBinder(), false /*allowIsolated*/,
IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
@@ -135,7 +141,7 @@
EXPECT_CALL(*access, getCallingContext()).WillOnce(Return(Access::CallingContext{}));
EXPECT_CALL(*access, canAdd(_, _)).WillOnce(Return(false));
- sp<ServiceManager> sm = new ServiceManager(std::move(access));
+ sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access));
EXPECT_FALSE(sm->addService("foo", getBinder(), false /*allowIsolated*/,
IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
@@ -168,7 +174,7 @@
EXPECT_CALL(*access, canAdd(_, _)).WillOnce(Return(true));
EXPECT_CALL(*access, canFind(_, _)).WillOnce(Return(false));
- sp<ServiceManager> sm = new ServiceManager(std::move(access));
+ sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access));
EXPECT_TRUE(sm->addService("foo", getBinder(), false /*allowIsolated*/,
IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
@@ -192,7 +198,7 @@
EXPECT_CALL(*access, canAdd(_, _)).WillOnce(Return(true));
EXPECT_CALL(*access, canFind(_, _)).WillOnce(Return(true));
- sp<ServiceManager> sm = new ServiceManager(std::move(access));
+ sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access));
sp<IBinder> service = getBinder();
EXPECT_TRUE(sm->addService("foo", service, true /*allowIsolated*/,
@@ -218,7 +224,7 @@
// TODO(b/136023468): when security check is first, this should be called first
// EXPECT_CALL(*access, canFind(_, _)).WillOnce(Return(true));
- sp<ServiceManager> sm = new ServiceManager(std::move(access));
+ sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access));
EXPECT_TRUE(sm->addService("foo", getBinder(), false /*allowIsolated*/,
IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
@@ -235,7 +241,7 @@
EXPECT_CALL(*access, getCallingContext()).WillOnce(Return(Access::CallingContext{}));
EXPECT_CALL(*access, canList(_)).WillOnce(Return(false));
- sp<ServiceManager> sm = new ServiceManager(std::move(access));
+ sp<ServiceManager> sm = new NiceMock<MockServiceManager>(std::move(access));
std::vector<std::string> out;
EXPECT_FALSE(sm->listServices(IServiceManager::DUMP_FLAG_PRIORITY_ALL, &out).isOk());
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 715a460..eefc5b1 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -232,7 +232,7 @@
const std::string name = String8(name16).c_str();
sp<IBinder> out;
- if(!mTheRealServiceManager->checkService(name, &out).isOk()) {
+ if (!mTheRealServiceManager->getService(name, &out).isOk()) {
return nullptr;
}
if(out != nullptr) return out;
@@ -256,13 +256,13 @@
// Handle race condition for lazy services. Here is what can happen:
// - the service dies (not processed by init yet).
// - sm processes death notification.
- // - sm gets checkService and calls init to start service.
+ // - sm gets getService and calls init to start service.
// - init gets the start signal, but the service already appears
// started, so it does nothing.
// - init gets death signal, but doesn't know it needs to restart
// the service
// - we need to request service again to get it to start
- if(!mTheRealServiceManager->checkService(name, &out).isOk()) {
+ if (!mTheRealServiceManager->getService(name, &out).isOk()) {
return nullptr;
}
if(out != nullptr) return out;