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 Diff line number Diff line
@@ -18,6 +18,9 @@

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

    sp<IBinder> out;
    Service* service = nullptr;
    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);
            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;
            }
        }
        out = service.binder;
        out = service->binder;
    }

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

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

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

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

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

void ServiceManager::tryStartService(const std::string& name) {
@@ -341,4 +357,183 @@ void ServiceManager::tryStartService(const std::string& name) {
    }).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
 No newline at end of file
+22 −1
Original line number Diff line number Diff line
@@ -17,12 +17,14 @@
#pragma once

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

#include "Access.h"

namespace android {

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

class ServiceManager : public os::BnServiceManager, public IBinder::DeathRecipient {
@@ -40,9 +42,13 @@ public:
                                            const sp<IServiceCallback>& callback) override;
    binder::Status unregisterForNotifications(const std::string& name,
                                              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 handleClientCallbacks();

protected:
    virtual void tryStartService(const std::string& name);
@@ -52,9 +58,16 @@ private:
        sp<IBinder> binder; // not null
        bool allowIsolated;
        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 ClientCallbackMap = std::map<std::string, std::vector<sp<IClientCallback>>>;
    using ServiceMap = std::map<std::string, Service>;

    // removes a callback from mNameToCallback, removing it if the vector is empty
@@ -62,10 +75,18 @@ private:
    void removeCallback(const wp<IBinder>& who,
                        CallbackMap::iterator* it,
                        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);

    CallbackMap mNameToCallback;
    ServiceMap mNameToService;
    ClientCallbackMap mNameToClientCallback;

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

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

using ::android::Access;
using ::android::sp;
using ::android::Looper;
using ::android::LooperCallback;
using ::android::ProcessState;
using ::android::IPCThreadState;
using ::android::ProcessState;
using ::android::ServiceManager;
using ::android::os::IServiceManager;
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) {
    if (argc > 2) {
        LOG(FATAL) << "usage: " << argv[0] << " [binder driver]";
@@ -49,7 +132,14 @@ int main(int argc, char** argv) {
    IPCThreadState::self()->setTheContextObject(manager);
    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
    return EXIT_FAILURE;
+2 −0
Original line number Diff line number Diff line
@@ -90,6 +90,7 @@ cc_library {
        "IResultReceiver.cpp",
        "IServiceManager.cpp",
        "IShellCallback.cpp",
        "LazyServiceRegistrar.cpp",
        "MemoryBase.cpp",
        "MemoryDealer.cpp",
        "MemoryHeapBase.cpp",
@@ -160,6 +161,7 @@ filegroup {
    name: "libbinder_aidl",
    srcs: [
        "aidl/android/content/pm/IPackageManagerNative.aidl",
        "aidl/android/os/IClientCallback.aidl",
        "aidl/android/os/IServiceCallback.aidl",
        "aidl/android/os/IServiceManager.aidl",
    ],
+2 −0
Original line number Diff line number Diff line
@@ -271,6 +271,8 @@ sp<IBinder> ServiceManagerShim::waitForService(const String16& name16)
            std::unique_lock<std::mutex> lock(mMutex);
            mBinder = binder;
            lock.unlock();
            // Flushing here helps ensure the service's ref count remains accurate
            IPCThreadState::self()->flushCommands();
            mCv.notify_one();
            return Status::ok();
        }
Loading