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

Commit a122d867 authored by Steven Moreland's avatar Steven Moreland Committed by android-build-merger
Browse files

servicemanager: notifications am: 27cfab02

am: 567ff3ba

Change-Id: I391f97fffd882c81d9d278e67809277876db43b8
parents 6bb165f2 567ff3ba
Loading
Loading
Loading
Loading
+97 −0
Original line number Diff line number Diff line
@@ -28,6 +28,13 @@ ServiceManager::ServiceManager(std::unique_ptr<Access>&& access) : mAccess(std::
ServiceManager::~ServiceManager() {
    // this should only happen in tests

    for (const auto& [name, callbacks] : mNameToCallback) {
        CHECK(!callbacks.empty()) << name;
        for (const auto& callback : callbacks) {
            CHECK(callback != nullptr) << name;
        }
    }

    for (const auto& [name, service] : mNameToService) {
        CHECK(service.binder != nullptr) << name;
    }
@@ -117,6 +124,14 @@ Status ServiceManager::addService(const std::string& name, const sp<IBinder>& bi
        .dumpPriority = dumpPriority,
    };

    auto it = mNameToCallback.find(name);
    if (it != mNameToCallback.end()) {
        for (const sp<IServiceCallback>& cb : it->second) {
            // permission checked in registerForNotifications
            cb->onRegistration(name, binder);
        }
    }

    return Status::ok();
}

@@ -146,6 +161,84 @@ Status ServiceManager::listServices(int32_t dumpPriority, std::vector<std::strin
    return Status::ok();
}

Status ServiceManager::registerForNotifications(
        const std::string& name, const sp<IServiceCallback>& callback) {
    auto ctx = mAccess->getCallingContext();

    if (!mAccess->canFind(ctx, name)) {
        return Status::fromExceptionCode(Status::EX_SECURITY);
    }

    if (!isValidServiceName(name)) {
        LOG(ERROR) << "Invalid service name: " << name;
        return Status::fromExceptionCode(Status::EX_ILLEGAL_ARGUMENT);
    }

    if (callback == nullptr) {
        return Status::fromExceptionCode(Status::EX_NULL_POINTER);
    }

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

    mNameToCallback[name].push_back(callback);

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

        // never null if an entry exists
        CHECK(binder != nullptr) << name;
        callback->onRegistration(name, binder);
    }

    return Status::ok();
}
Status ServiceManager::unregisterForNotifications(
        const std::string& name, const sp<IServiceCallback>& callback) {
    auto ctx = mAccess->getCallingContext();

    if (!mAccess->canFind(ctx, name)) {
        return Status::fromExceptionCode(Status::EX_SECURITY);
    }

    bool found = false;

    auto it = mNameToCallback.find(name);
    if (it != mNameToCallback.end()) {
        removeCallback(IInterface::asBinder(callback), &it, &found);
    }

    if (!found) {
        LOG(ERROR) << "Trying to unregister callback, but none exists " << name;
        return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
    }

    return Status::ok();
}

void ServiceManager::removeCallback(const wp<IBinder>& who,
                                    CallbackMap::iterator* it,
                                    bool* found) {
    std::vector<sp<IServiceCallback>>& listeners = (*it)->second;

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

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

void ServiceManager::binderDied(const wp<IBinder>& who) {
    for (auto it = mNameToService.begin(); it != mNameToService.end();) {
        if (who == it->second.binder) {
@@ -154,6 +247,10 @@ void ServiceManager::binderDied(const wp<IBinder>& who) {
            ++it;
        }
    }

    for (auto it = mNameToCallback.begin(); it != mNameToCallback.end();) {
        removeCallback(who, &it, nullptr /*found*/);
    }
}

}  // namespace android
+22 −3
Original line number Diff line number Diff line
@@ -17,11 +17,14 @@
#pragma once

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

#include "Access.h"

namespace android {

using os::IServiceCallback;

class ServiceManager : public os::BnServiceManager, public IBinder::DeathRecipient {
public:
    ServiceManager(std::unique_ptr<Access>&& access);
@@ -29,19 +32,35 @@ public:

    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, bool allowIsolated, int32_t dumpPriority) override;
    binder::Status addService(const std::string& name, const sp<IBinder>& binder,
                              bool allowIsolated, int32_t dumpPriority) override;
    binder::Status listServices(int32_t dumpPriority, std::vector<std::string>* outList) override;
    binder::Status registerForNotifications(const std::string& name,
                                            const sp<IServiceCallback>& callback) override;
    binder::Status unregisterForNotifications(const std::string& name,
                                              const sp<IServiceCallback>& callback) override;

    void binderDied(const wp<IBinder>& who) override;

private:
    struct Service {
        sp<IBinder> binder;
        sp<IBinder> binder; // not null
        bool allowIsolated;
        int32_t dumpPriority;
    };

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

    // removes a callback from mNameToCallback, removing it if the vector is empty
    // this updates iterator to the next location
    void removeCallback(const wp<IBinder>& who,
                        CallbackMap::iterator* it,
                        bool* found);

    CallbackMap mNameToCallback;
    ServiceMap mNameToService;

    std::unique_ptr<Access> mAccess;
};

+163 −7
Original line number Diff line number Diff line
@@ -14,7 +14,10 @@
 * limitations under the License.
 */

#include <android/os/BnServiceCallback.h>
#include <binder/Binder.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include <cutils/android_filesystem_config.h>
#include <gtest/gtest.h>
#include <gmock/gmock.h>
@@ -24,8 +27,11 @@

using android::sp;
using android::Access;
using android::BBinder;
using android::IBinder;
using android::ServiceManager;
using android::binder::Status;
using android::os::BnServiceCallback;
using android::os::IServiceManager;
using testing::_;
using testing::ElementsAre;
@@ -33,9 +39,14 @@ using testing::NiceMock;
using testing::Return;

static sp<IBinder> getBinder() {
    // It doesn't matter what remote binder it is, we just need one so that linkToDeath will work.
    // The context manager (servicemanager) is easy to get and is in another process.
    return android::ProcessState::self()->getContextObject(nullptr);
    class LinkableBinder : public BBinder {
        android::status_t linkToDeath(const sp<DeathRecipient>&, void*, uint32_t) override {
            // let SM linkToDeath
            return android::OK;
        }
    };

    return new LinkableBinder;
}

class MockAccess : public Access {
@@ -132,12 +143,14 @@ TEST(AddService, NoPermissions) {

TEST(GetService, HappyHappy) {
    auto sm = getPermissiveServiceManager();
    EXPECT_TRUE(sm->addService("foo", getBinder(), false /*allowIsolated*/,
    sp<IBinder> service = getBinder();

    EXPECT_TRUE(sm->addService("foo", service, false /*allowIsolated*/,
        IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());

    sp<IBinder> out;
    EXPECT_TRUE(sm->getService("foo", &out).isOk());
    EXPECT_EQ(getBinder(), out);
    EXPECT_EQ(service, out);
}

TEST(GetService, NonExistant) {
@@ -181,12 +194,13 @@ TEST(GetService, AllowedFromIsolated) {

    sp<ServiceManager> sm = new ServiceManager(std::move(access));

    EXPECT_TRUE(sm->addService("foo", getBinder(), true /*allowIsolated*/,
    sp<IBinder> service = getBinder();
    EXPECT_TRUE(sm->addService("foo", service, true /*allowIsolated*/,
        IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());

    sp<IBinder> out;
    EXPECT_TRUE(sm->getService("foo", &out).isOk());
    EXPECT_EQ(getBinder(), out.get());
    EXPECT_EQ(service, out.get());
}

TEST(GetService, NotAllowedFromIsolated) {
@@ -265,3 +279,145 @@ TEST(ListServices, CriticalServices) {
    // all there and in the right order
    EXPECT_THAT(out, ElementsAre("sa"));
}

class CallbackHistorian : public BnServiceCallback {
    Status onRegistration(const std::string& name, const sp<IBinder>& binder) override {
        registrations.push_back(name);
        binders.push_back(binder);
        return Status::ok();
    }

    android::status_t linkToDeath(const sp<DeathRecipient>&, void*, uint32_t) override {
        // let SM linkToDeath
        return android::OK;
    }

public:
    std::vector<std::string> registrations;
    std::vector<sp<IBinder>> binders;
};

TEST(ServiceNotifications, NoPermissionsRegister) {
    std::unique_ptr<MockAccess> access = std::make_unique<NiceMock<MockAccess>>();

    EXPECT_CALL(*access, getCallingContext()).WillOnce(Return(Access::CallingContext{}));
    EXPECT_CALL(*access, canFind(_,_)).WillOnce(Return(false));

    sp<ServiceManager> sm = new ServiceManager(std::move(access));

    sp<CallbackHistorian> cb = new CallbackHistorian;

    EXPECT_EQ(sm->registerForNotifications("foofoo", cb).exceptionCode(),
        Status::EX_SECURITY);
}

TEST(ServiceNotifications, NoPermissionsUnregister) {
    std::unique_ptr<MockAccess> access = std::make_unique<NiceMock<MockAccess>>();

    EXPECT_CALL(*access, getCallingContext()).WillOnce(Return(Access::CallingContext{}));
    EXPECT_CALL(*access, canFind(_,_)).WillOnce(Return(false));

    sp<ServiceManager> sm = new ServiceManager(std::move(access));

    sp<CallbackHistorian> cb = new CallbackHistorian;

    // should always hit security error first
    EXPECT_EQ(sm->unregisterForNotifications("foofoo", cb).exceptionCode(),
        Status::EX_SECURITY);
}

TEST(ServiceNotifications, InvalidName) {
    auto sm = getPermissiveServiceManager();

    sp<CallbackHistorian> cb = new CallbackHistorian;

    EXPECT_EQ(sm->registerForNotifications("foo@foo", cb).exceptionCode(),
        Status::EX_ILLEGAL_ARGUMENT);
}

TEST(ServiceNotifications, NullCallback) {
    auto sm = getPermissiveServiceManager();

    EXPECT_EQ(sm->registerForNotifications("foofoo", nullptr).exceptionCode(),
        Status::EX_NULL_POINTER);
}

TEST(ServiceNotifications, Unregister) {
    auto sm = getPermissiveServiceManager();

    sp<CallbackHistorian> cb = new CallbackHistorian;

    EXPECT_TRUE(sm->registerForNotifications("foofoo", cb).isOk());
    EXPECT_EQ(sm->unregisterForNotifications("foofoo", cb).exceptionCode(), 0);
}

TEST(ServiceNotifications, UnregisterWhenNoRegistrationExists) {
    auto sm = getPermissiveServiceManager();

    sp<CallbackHistorian> cb = new CallbackHistorian;

    EXPECT_EQ(sm->unregisterForNotifications("foofoo", cb).exceptionCode(),
        Status::EX_ILLEGAL_STATE);
}

TEST(ServiceNotifications, NoNotification) {
    auto sm = getPermissiveServiceManager();

    sp<CallbackHistorian> cb = new CallbackHistorian;

    EXPECT_TRUE(sm->registerForNotifications("foofoo", cb).isOk());
    EXPECT_TRUE(sm->addService("otherservice", getBinder(),
        false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());

    EXPECT_THAT(cb->registrations, ElementsAre());
    EXPECT_THAT(cb->binders, ElementsAre());
}

TEST(ServiceNotifications, GetNotification) {
    auto sm = getPermissiveServiceManager();

    sp<CallbackHistorian> cb = new CallbackHistorian;

    sp<IBinder> service = getBinder();

    EXPECT_TRUE(sm->registerForNotifications("asdfasdf", cb).isOk());
    EXPECT_TRUE(sm->addService("asdfasdf", service,
        false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());

    EXPECT_THAT(cb->registrations, ElementsAre("asdfasdf"));
    EXPECT_THAT(cb->binders, ElementsAre(service));
}

TEST(ServiceNotifications, GetNotificationForAlreadyRegisteredService) {
    auto sm = getPermissiveServiceManager();

    sp<CallbackHistorian> cb = new CallbackHistorian;

    sp<IBinder> service = getBinder();

    EXPECT_TRUE(sm->addService("asdfasdf", service,
        false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());

    EXPECT_TRUE(sm->registerForNotifications("asdfasdf", cb).isOk());

    EXPECT_THAT(cb->registrations, ElementsAre("asdfasdf"));
    EXPECT_THAT(cb->binders, ElementsAre(service));
}

TEST(ServiceNotifications, GetMultipleNotification) {
    auto sm = getPermissiveServiceManager();

    sp<CallbackHistorian> cb = new CallbackHistorian;

    sp<IBinder> binder1 = getBinder();
    sp<IBinder> binder2 = getBinder();

    EXPECT_TRUE(sm->registerForNotifications("asdfasdf", cb).isOk());
    EXPECT_TRUE(sm->addService("asdfasdf", binder1,
        false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());
    EXPECT_TRUE(sm->addService("asdfasdf", binder2,
        false /*allowIsolated*/, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT).isOk());

    EXPECT_THAT(cb->registrations, ElementsAre("asdfasdf", "asdfasdf"));
    EXPECT_THAT(cb->registrations, ElementsAre("asdfasdf", "asdfasdf"));
}
+1 −0
Original line number Diff line number Diff line
@@ -145,6 +145,7 @@ filegroup {
    name: "libbinder_aidl",
    srcs: [
        "aidl/android/content/pm/IPackageManagerNative.aidl",
        "aidl/android/os/IServiceCallback.aidl",
        "aidl/android/os/IServiceManager.aidl",
    ],
    path: "aidl",
+30 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.os;

/**
 * @hide
 */
oneway interface IServiceCallback {
    /**
     * Called when a service is registered.
     *
     * @param name the service name that has been registered with
     * @param binder the binder that is registered
     */
    void onRegistration(@utf8InCpp String name, IBinder binder);
}
Loading