Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0d84430b authored by Jon Spivack's avatar Jon Spivack
Browse files

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
parent e380e5d8
Loading
Loading
Loading
Loading
+36 −20
Original line number Diff line number Diff line
@@ -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,19 +43,22 @@ ServiceManager::~ServiceManager() {
}

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) {
    auto ctx = mAccess->getCallingContext();

    auto it = mNameToService.find(name);
    if (it == mNameToService.end()) {
        *outBinder = nullptr;
    *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();

    sp<IBinder> out;
    if (auto it = mNameToService.find(name); it != mNameToService.end()) {
        const Service& service = it->second;

        if (!service.allowIsolated) {
@@ -61,19 +66,21 @@ Status ServiceManager::checkService(const std::string& name, sp<IBinder>* outBin
            bool isIsolated = appid >= AID_ISOLATED_START && appid <= AID_ISOLATED_END;

            if (isIsolated) {
            *outBinder = nullptr;
            return Status::ok();
                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::binderDied(const wp<IBinder>& who) {
    }
}

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
+5 −0
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ public:
    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 @@ public:

    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 @@ private:
    void removeCallback(const wp<IBinder>& who,
                        CallbackMap::iterator* it,
                        bool* found);
    sp<IBinder> tryGetService(const std::string& name, bool startIfNotFound);

    CallbackMap mNameToCallback;
    ServiceMap mNameToService;
+13 −7
Original line number Diff line number Diff line
@@ -57,6 +57,12 @@ public:
    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 @@ static sp<ServiceManager> getPermissiveServiceManager() {
    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 @@ TEST(AddService, AddDisallowedFromApp) {
            .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 @@ TEST(AddService, NoPermissions) {
    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 @@ TEST(GetService, NoPermissionsForGettingService) {
    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 @@ TEST(GetService, AllowedFromIsolated) {
    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 @@ TEST(GetService, NotAllowedFromIsolated) {
    // 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 @@ TEST(ListServices, NoPermissions) {
    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());
+3 −3
Original line number Diff line number Diff line
@@ -232,7 +232,7 @@ public:
        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 @@ public:
            // 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;