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

Commit c3f00716 authored by Jon Spivack's avatar Jon Spivack Committed by Gerrit Code Review
Browse files

Merge "Dynamically stop lazy services"

parents 0be2047b 9f503a42
Loading
Loading
Loading
Loading
+201 −6
Original line number Original line Diff line number Diff line
@@ -18,6 +18,9 @@


#include <android-base/logging.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/properties.h>
#include <binder/BpBinder.h>
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <binder/Stability.h>
#include <binder/Stability.h>
#include <cutils/android_filesystem_config.h>
#include <cutils/android_filesystem_config.h>
#include <cutils/multiuser.h>
#include <cutils/multiuser.h>
@@ -108,10 +111,11 @@ sp<IBinder> ServiceManager::tryGetService(const std::string& name, bool startIfN
    auto ctx = mAccess->getCallingContext();
    auto ctx = mAccess->getCallingContext();


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


        if (!service.allowIsolated) {
        if (!service->allowIsolated) {
            uid_t appid = multiuser_get_app_id(ctx.uid);
            uid_t appid = multiuser_get_app_id(ctx.uid);
            bool isIsolated = appid >= AID_ISOLATED_START && appid <= AID_ISOLATED_END;
            bool isIsolated = appid >= AID_ISOLATED_START && appid <= AID_ISOLATED_END;


@@ -119,7 +123,7 @@ sp<IBinder> ServiceManager::tryGetService(const std::string& name, bool startIfN
                return nullptr;
                return nullptr;
            }
            }
        }
        }
        out = service.binder;
        out = service->binder;
    }
    }


    if (!mAccess->canFind(ctx, name)) {
    if (!mAccess->canFind(ctx, name)) {
@@ -130,6 +134,12 @@ sp<IBinder> ServiceManager::tryGetService(const std::string& name, bool startIfN
        tryStartService(name);
        tryStartService(name);
    }
    }


    if (out) {
        // Setting this guarantee each time we hand out a binder ensures that the client-checking
        // loop knows about the event even if the client immediately drops the service
        service->guaranteeClient = true;
    }

    return out;
    return out;
}
}


@@ -182,15 +192,17 @@ Status ServiceManager::addService(const std::string& name, const sp<IBinder>& bi
        return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
        return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
    }
    }


    mNameToService[name] = Service {
    auto entry = mNameToService.emplace(name, Service {
        .binder = binder,
        .binder = binder,
        .allowIsolated = allowIsolated,
        .allowIsolated = allowIsolated,
        .dumpPriority = dumpPriority,
        .dumpPriority = dumpPriority,
    };
        .debugPid = ctx.debugPid,
    });


    auto it = mNameToCallback.find(name);
    auto it = mNameToCallback.find(name);
    if (it != mNameToCallback.end()) {
    if (it != mNameToCallback.end()) {
        for (const sp<IServiceCallback>& cb : it->second) {
        for (const sp<IServiceCallback>& cb : it->second) {
            entry.first->second.guaranteeClient = true;
            // permission checked in registerForNotifications
            // permission checked in registerForNotifications
            cb->onRegistration(name, binder);
            cb->onRegistration(name, binder);
        }
        }
@@ -330,6 +342,10 @@ void ServiceManager::binderDied(const wp<IBinder>& who) {
    for (auto it = mNameToCallback.begin(); it != mNameToCallback.end();) {
    for (auto it = mNameToCallback.begin(); it != mNameToCallback.end();) {
        removeCallback(who, &it, nullptr /*found*/);
        removeCallback(who, &it, nullptr /*found*/);
    }
    }

    for (auto it = mNameToClientCallback.begin(); it != mNameToClientCallback.end();) {
        removeClientCallback(who, &it);
    }
}
}


void ServiceManager::tryStartService(const std::string& name) {
void ServiceManager::tryStartService(const std::string& name) {
@@ -341,4 +357,183 @@ void ServiceManager::tryStartService(const std::string& name) {
    }).detach();
    }).detach();
}
}


Status ServiceManager::registerClientCallback(const std::string& name, const sp<IBinder>& service,
                                              const sp<IClientCallback>& cb) {
    if (cb == nullptr) {
        return Status::fromExceptionCode(Status::EX_NULL_POINTER);
    }

    auto ctx = mAccess->getCallingContext();
    if (!mAccess->canAdd(ctx, name)) {
        return Status::fromExceptionCode(Status::EX_SECURITY);
    }

    auto serviceIt = mNameToService.find(name);
    if (serviceIt == mNameToService.end()) {
        LOG(ERROR) << "Could not add callback for nonexistent service: " << name;
        return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);
    }

    if (serviceIt->second.debugPid != IPCThreadState::self()->getCallingPid()) {
        LOG(WARNING) << "Only a server can register for client callbacks (for " << name << ")";
        return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION);
    }

    if (serviceIt->second.binder != service) {
        LOG(WARNING) << "Tried to register client callback for " << name
            << " but a different service is registered under this name.";
        return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);
    }

    if (OK != IInterface::asBinder(cb)->linkToDeath(this)) {
        LOG(ERROR) << "Could not linkToDeath when adding client callback for " << name;
        return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
    }

    mNameToClientCallback[name].push_back(cb);

    return Status::ok();
}

void ServiceManager::removeClientCallback(const wp<IBinder>& who,
                                          ClientCallbackMap::iterator* it) {
    std::vector<sp<IClientCallback>>& listeners = (*it)->second;

    for (auto lit = listeners.begin(); lit != listeners.end();) {
        if (IInterface::asBinder(*lit) == who) {
            lit = listeners.erase(lit);
        } else {
            ++lit;
        }
    }

    if (listeners.empty()) {
        *it = mNameToClientCallback.erase(*it);
    } else {
        (*it)++;
    }
}

ssize_t ServiceManager::Service::getNodeStrongRefCount() {
    sp<BpBinder> bpBinder = binder->remoteBinder();
    if (bpBinder == nullptr) return -1;

    return ProcessState::self()->getStrongRefCountForNodeByHandle(bpBinder->handle());
}

void ServiceManager::handleClientCallbacks() {
    for (const auto& [name, service] : mNameToService) {
        handleServiceClientCallback(name);
    }
}

ssize_t ServiceManager::handleServiceClientCallback(const std::string& serviceName) {
    auto serviceIt = mNameToService.find(serviceName);
    if (serviceIt == mNameToService.end() || mNameToClientCallback.count(serviceName) < 1) {
        return -1;
    }

    Service& service = serviceIt->second;
    ssize_t count = service.getNodeStrongRefCount();

    // binder driver doesn't support this feature
    if (count == -1) return count;

    bool hasClients = count > 1; // this process holds a strong count

    if (service.guaranteeClient) {
        // we have no record of this client
        if (!service.hasClients && !hasClients) {
            sendClientCallbackNotifications(serviceName, true);
        }

        // guarantee is temporary
        service.guaranteeClient = false;
    }

    if (hasClients && !service.hasClients) {
        // client was retrieved in some other way
        sendClientCallbackNotifications(serviceName, true);
    }

    // there are no more clients, but the callback has not been called yet
    if (!hasClients && service.hasClients) {
        sendClientCallbackNotifications(serviceName, false);
    }

    return count;
}

void ServiceManager::sendClientCallbackNotifications(const std::string& serviceName, bool hasClients) {
    auto serviceIt = mNameToService.find(serviceName);
    if (serviceIt == mNameToService.end()) {
        LOG(WARNING) << "sendClientCallbackNotifications could not find service " << serviceName;
        return;
    }
    Service& service = serviceIt->second;

    CHECK(hasClients != service.hasClients) << "Record shows: " << service.hasClients
        << " so we can't tell clients again that we have client: " << hasClients;

    LOG(INFO) << "Notifying " << serviceName << " they have clients: " << hasClients;

    auto ccIt = mNameToClientCallback.find(serviceName);
    CHECK(ccIt != mNameToClientCallback.end())
        << "sendClientCallbackNotifications could not find callbacks for service ";

    for (const auto& callback : ccIt->second) {
        callback->onClients(service.binder, hasClients);
    }

    service.hasClients = hasClients;
}

Status ServiceManager::tryUnregisterService(const std::string& name, const sp<IBinder>& binder) {
    if (binder == nullptr) {
        return Status::fromExceptionCode(Status::EX_NULL_POINTER);
    }

    auto ctx = mAccess->getCallingContext();
    if (!mAccess->canAdd(ctx, name)) {
        return Status::fromExceptionCode(Status::EX_SECURITY);
    }

    auto serviceIt = mNameToService.find(name);
    if (serviceIt == mNameToService.end()) {
        LOG(WARNING) << "Tried to unregister " << name
            << ", but that service wasn't registered to begin with.";
        return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
    }

    if (serviceIt->second.debugPid != IPCThreadState::self()->getCallingPid()) {
        LOG(WARNING) << "Only a server can unregister itself (for " << name << ")";
        return Status::fromExceptionCode(Status::EX_UNSUPPORTED_OPERATION);
    }

    sp<IBinder> storedBinder = serviceIt->second.binder;

    if (binder != storedBinder) {
        LOG(WARNING) << "Tried to unregister " << name
            << ", but a different service is registered under this name.";
        return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
    }

    int clients = handleServiceClientCallback(name);

    // clients < 0: feature not implemented or other error. Assume clients.
    // Otherwise:
    // - kernel driver will hold onto one refcount (during this transaction)
    // - servicemanager has a refcount (guaranteed by this transaction)
    // So, if clients > 2, then at least one other service on the system must hold a refcount.
    if (clients < 0 || clients > 2) {
        // client callbacks are either disabled or there are other clients
        LOG(INFO) << "Tried to unregister " << name << " but there are clients: " << clients;
        return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
    }

    mNameToService.erase(name);

    return Status::ok();
}

}  // namespace android
}  // namespace android
 No newline at end of file
+22 −1
Original line number Original line Diff line number Diff line
@@ -17,12 +17,14 @@
#pragma once
#pragma once


#include <android/os/BnServiceManager.h>
#include <android/os/BnServiceManager.h>
#include <android/os/IClientCallback.h>
#include <android/os/IServiceCallback.h>
#include <android/os/IServiceCallback.h>


#include "Access.h"
#include "Access.h"


namespace android {
namespace android {


using os::IClientCallback;
using os::IServiceCallback;
using os::IServiceCallback;


class ServiceManager : public os::BnServiceManager, public IBinder::DeathRecipient {
class ServiceManager : public os::BnServiceManager, public IBinder::DeathRecipient {
@@ -40,9 +42,13 @@ public:
                                            const sp<IServiceCallback>& callback) override;
                                            const sp<IServiceCallback>& callback) override;
    binder::Status unregisterForNotifications(const std::string& name,
    binder::Status unregisterForNotifications(const std::string& name,
                                              const sp<IServiceCallback>& callback) override;
                                              const sp<IServiceCallback>& callback) override;
    binder::Status isDeclared(const std::string& name, bool* outReturn) override;


    binder::Status isDeclared(const std::string& name, bool* outReturn) override;
    binder::Status registerClientCallback(const std::string& name, const sp<IBinder>& service,
                                          const sp<IClientCallback>& cb) override;
    binder::Status tryUnregisterService(const std::string& name, const sp<IBinder>& binder) override;
    void binderDied(const wp<IBinder>& who) override;
    void binderDied(const wp<IBinder>& who) override;
    void handleClientCallbacks();


protected:
protected:
    virtual void tryStartService(const std::string& name);
    virtual void tryStartService(const std::string& name);
@@ -52,9 +58,16 @@ private:
        sp<IBinder> binder; // not null
        sp<IBinder> binder; // not null
        bool allowIsolated;
        bool allowIsolated;
        int32_t dumpPriority;
        int32_t dumpPriority;
        bool hasClients = false; // notifications sent on true -> false.
        bool guaranteeClient = false; // forces the client check to true
        pid_t debugPid = 0; // the process in which this service runs

        // the number of clients of the service, including servicemanager itself
        ssize_t getNodeStrongRefCount();
    };
    };


    using CallbackMap = std::map<std::string, std::vector<sp<IServiceCallback>>>;
    using CallbackMap = std::map<std::string, std::vector<sp<IServiceCallback>>>;
    using ClientCallbackMap = std::map<std::string, std::vector<sp<IClientCallback>>>;
    using ServiceMap = std::map<std::string, Service>;
    using ServiceMap = std::map<std::string, Service>;


    // removes a callback from mNameToCallback, removing it if the vector is empty
    // removes a callback from mNameToCallback, removing it if the vector is empty
@@ -62,10 +75,18 @@ private:
    void removeCallback(const wp<IBinder>& who,
    void removeCallback(const wp<IBinder>& who,
                        CallbackMap::iterator* it,
                        CallbackMap::iterator* it,
                        bool* found);
                        bool* found);
    ssize_t handleServiceClientCallback(const std::string& serviceName);
     // Also updates mHasClients (of what the last callback was)
    void sendClientCallbackNotifications(const std::string& serviceName, bool hasClients);
    // removes a callback from mNameToClientCallback, deleting the entry if the vector is empty
    // this updates the iterator to the next location
    void removeClientCallback(const wp<IBinder>& who, ClientCallbackMap::iterator* it);

    sp<IBinder> tryGetService(const std::string& name, bool startIfNotFound);
    sp<IBinder> tryGetService(const std::string& name, bool startIfNotFound);


    CallbackMap mNameToCallback;
    CallbackMap mNameToCallback;
    ServiceMap mNameToService;
    ServiceMap mNameToService;
    ClientCallbackMap mNameToClientCallback;


    std::unique_ptr<Access> mAccess;
    std::unique_ptr<Access> mAccess;
};
};
+91 −1
Original line number Original line Diff line number Diff line
@@ -18,18 +18,101 @@
#include <binder/IPCThreadState.h>
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <binder/ProcessState.h>
#include <binder/Status.h>
#include <binder/Status.h>
#include <sys/timerfd.h>
#include <utils/Looper.h>
#include <utils/StrongPointer.h>
#include <utils/StrongPointer.h>


#include "Access.h"
#include "Access.h"
#include "ServiceManager.h"
#include "ServiceManager.h"


using ::android::Access;
using ::android::Access;
using ::android::sp;
using ::android::Looper;
using ::android::LooperCallback;
using ::android::ProcessState;
using ::android::IPCThreadState;
using ::android::IPCThreadState;
using ::android::ProcessState;
using ::android::ProcessState;
using ::android::ServiceManager;
using ::android::ServiceManager;
using ::android::os::IServiceManager;
using ::android::os::IServiceManager;
using ::android::sp;
using ::android::sp;


class BinderCallback : public LooperCallback {
public:
    static sp<BinderCallback> setupTo(const sp<Looper>& looper) {
        sp<BinderCallback> cb = new BinderCallback;

        int binder_fd = -1;
        IPCThreadState::self()->setupPolling(&binder_fd);
        LOG_ALWAYS_FATAL_IF(binder_fd < 0, "Failed to setupPolling: %d", binder_fd);

        // Flush after setupPolling(), to make sure the binder driver
        // knows about this thread handling commands.
        IPCThreadState::self()->flushCommands();

        int ret = looper->addFd(binder_fd,
                                Looper::POLL_CALLBACK,
                                Looper::EVENT_INPUT,
                                cb,
                                nullptr /*data*/);
        LOG_ALWAYS_FATAL_IF(ret != 1, "Failed to add binder FD to Looper");

        return cb;
    }

    int handleEvent(int /* fd */, int /* events */, void* /* data */) override {
        IPCThreadState::self()->handlePolledCommands();
        return 1;  // Continue receiving callbacks.
    }
};

// LooperCallback for IClientCallback
class ClientCallbackCallback : public LooperCallback {
public:
    static sp<ClientCallbackCallback> setupTo(const sp<Looper>& looper, const sp<ServiceManager>& manager) {
        sp<ClientCallbackCallback> cb = new ClientCallbackCallback(manager);

        int fdTimer = timerfd_create(CLOCK_MONOTONIC, 0 /*flags*/);
        LOG_ALWAYS_FATAL_IF(fdTimer < 0, "Failed to timerfd_create: fd: %d err: %d", fdTimer, errno);

        itimerspec timespec {
            .it_interval = {
                .tv_sec = 5,
                .tv_nsec = 0,
            },
            .it_value = {
                .tv_sec = 5,
                .tv_nsec = 0,
            },
        };

        int timeRes = timerfd_settime(fdTimer, 0 /*flags*/, &timespec, nullptr);
        LOG_ALWAYS_FATAL_IF(timeRes < 0, "Failed to timerfd_settime: res: %d err: %d", timeRes, errno);

        int addRes = looper->addFd(fdTimer,
                                   Looper::POLL_CALLBACK,
                                   Looper::EVENT_INPUT,
                                   cb,
                                   nullptr);
        LOG_ALWAYS_FATAL_IF(addRes != 1, "Failed to add client callback FD to Looper");

        return cb;
    }

    int handleEvent(int fd, int /*events*/, void* /*data*/) override {
        uint64_t expirations;
        int ret = read(fd, &expirations, sizeof(expirations));
        if (ret != sizeof(expirations)) {
            ALOGE("Read failed to callback FD: ret: %d err: %d", ret, errno);
        }

        mManager->handleClientCallbacks();
        return 1;  // Continue receiving callbacks.
    }
private:
    ClientCallbackCallback(const sp<ServiceManager>& manager) : mManager(manager) {}
    sp<ServiceManager> mManager;
};

int main(int argc, char** argv) {
int main(int argc, char** argv) {
    if (argc > 2) {
    if (argc > 2) {
        LOG(FATAL) << "usage: " << argv[0] << " [binder driver]";
        LOG(FATAL) << "usage: " << argv[0] << " [binder driver]";
@@ -49,7 +132,14 @@ int main(int argc, char** argv) {
    IPCThreadState::self()->setTheContextObject(manager);
    IPCThreadState::self()->setTheContextObject(manager);
    ps->becomeContextManager(nullptr, nullptr);
    ps->becomeContextManager(nullptr, nullptr);


    IPCThreadState::self()->joinThreadPool();
    sp<Looper> looper = Looper::prepare(false /*allowNonCallbacks*/);

    BinderCallback::setupTo(looper);
    ClientCallbackCallback::setupTo(looper, manager);

    while(true) {
        looper->pollAll(-1);
    }


    // should not be reached
    // should not be reached
    return EXIT_FAILURE;
    return EXIT_FAILURE;
+2 −0
Original line number Original line Diff line number Diff line
@@ -90,6 +90,7 @@ cc_library {
        "IResultReceiver.cpp",
        "IResultReceiver.cpp",
        "IServiceManager.cpp",
        "IServiceManager.cpp",
        "IShellCallback.cpp",
        "IShellCallback.cpp",
        "LazyServiceRegistrar.cpp",
        "MemoryBase.cpp",
        "MemoryBase.cpp",
        "MemoryDealer.cpp",
        "MemoryDealer.cpp",
        "MemoryHeapBase.cpp",
        "MemoryHeapBase.cpp",
@@ -160,6 +161,7 @@ filegroup {
    name: "libbinder_aidl",
    name: "libbinder_aidl",
    srcs: [
    srcs: [
        "aidl/android/content/pm/IPackageManagerNative.aidl",
        "aidl/android/content/pm/IPackageManagerNative.aidl",
        "aidl/android/os/IClientCallback.aidl",
        "aidl/android/os/IServiceCallback.aidl",
        "aidl/android/os/IServiceCallback.aidl",
        "aidl/android/os/IServiceManager.aidl",
        "aidl/android/os/IServiceManager.aidl",
    ],
    ],
+2 −0
Original line number Original line Diff line number Diff line
@@ -271,6 +271,8 @@ sp<IBinder> ServiceManagerShim::waitForService(const String16& name16)
            std::unique_lock<std::mutex> lock(mMutex);
            std::unique_lock<std::mutex> lock(mMutex);
            mBinder = binder;
            mBinder = binder;
            lock.unlock();
            lock.unlock();
            // Flushing here helps ensure the service's ref count remains accurate
            IPCThreadState::self()->flushCommands();
            mCv.notify_one();
            mCv.notify_one();
            return Status::ok();
            return Status::ok();
        }
        }
Loading