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

Commit 18f63759 authored by Devin Moore's avatar Devin Moore
Browse files

Add support for injecting RPC binder accessors to libbinder

This allows libbinder to set up client connections to binder RPC
services underneath the libbinder service manager APIs.

It requires callbacks to be added to the local process to get connection
information that the client is responsible for obtaining.
Once these callbacks are added to libbinder, any client elswhere in the
process using the service manager APIs will be able to get and use a
binder for the service in the same way they do for kernel binder
services.

Test: atest binderRpcTest vm_accessor_test
Bug: 358427181
Change-Id: Iec27d30a669e0673bd66c99fded1edc335f7dff1
parent 9d04fe2c
Loading
Loading
Loading
Loading
+38 −8
Original line number Diff line number Diff line
@@ -46,7 +46,9 @@ binder::Status BackendUnifiedServiceManager::getService2(const ::std::string& na
                                                         os::Service* _out) {
    os::Service service;
    binder::Status status = mTheRealServiceManager->getService2(name, &service);
    toBinderService(service, _out);
    if (status.isOk()) {
        return toBinderService(name, service, _out);
    }
    return status;
}

@@ -54,15 +56,38 @@ binder::Status BackendUnifiedServiceManager::checkService(const ::std::string& n
                                                          os::Service* _out) {
    os::Service service;
    binder::Status status = mTheRealServiceManager->checkService(name, &service);
    toBinderService(service, _out);
    if (status.isOk()) {
        return toBinderService(name, service, _out);
    }
    return status;
}

void BackendUnifiedServiceManager::toBinderService(const os::Service& in, os::Service* _out) {
binder::Status BackendUnifiedServiceManager::toBinderService(const ::std::string& name,
                                                             const os::Service& in,
                                                             os::Service* _out) {
    switch (in.getTag()) {
        case os::Service::Tag::binder: {
            if (in.get<os::Service::Tag::binder>() == nullptr) {
                // failed to find a service. Check to see if we have any local
                // injected Accessors for this service.
                os::Service accessor;
                binder::Status status = getInjectedAccessor(name, &accessor);
                if (!status.isOk()) {
                    *_out = os::Service::make<os::Service::Tag::binder>(nullptr);
                    return status;
                }
                if (accessor.getTag() == os::Service::Tag::accessor &&
                    accessor.get<os::Service::Tag::accessor>() != nullptr) {
                    ALOGI("Found local injected service for %s, will attempt to create connection",
                          name.c_str());
                    // Call this again using the accessor Service to get the real
                    // service's binder into _out
                    return toBinderService(name, accessor, _out);
                }
            }

            *_out = in;
            break;
            return binder::Status::ok();
        }
        case os::Service::Tag::accessor: {
            sp<IBinder> accessorBinder = in.get<os::Service::Tag::accessor>();
@@ -70,7 +95,7 @@ void BackendUnifiedServiceManager::toBinderService(const os::Service& in, os::Se
            if (accessor == nullptr) {
                ALOGE("Service#accessor doesn't have accessor. VM is maybe starting...");
                *_out = os::Service::make<os::Service::Tag::binder>(nullptr);
                break;
                return binder::Status::ok();
            }
            auto request = [=] {
                os::ParcelFileDescriptor fd;
@@ -83,10 +108,15 @@ void BackendUnifiedServiceManager::toBinderService(const os::Service& in, os::Se
                }
            };
            auto session = RpcSession::make();
            session->setupPreconnectedClient(base::unique_fd{}, request);
            status_t status = session->setupPreconnectedClient(base::unique_fd{}, request);
            if (status != OK) {
                ALOGE("Failed to set up preconnected binder RPC client: %s",
                      statusToString(status).c_str());
                return binder::Status::fromStatusT(status);
            }
            session->setSessionSpecificRoot(accessorBinder);
            *_out = os::Service::make<os::Service::Tag::binder>(session->getRootObject());
            break;
            return binder::Status::ok();
        }
        default: {
            LOG_ALWAYS_FATAL("Unknown service type: %d", in.getTag());
+5 −2
Original line number Diff line number Diff line
@@ -61,9 +61,12 @@ public:

private:
    sp<os::IServiceManager> mTheRealServiceManager;
    void toBinderService(const os::Service& in, os::Service* _out);
    binder::Status toBinderService(const ::std::string& name, const os::Service& in,
                                   os::Service* _out);
};

sp<BackendUnifiedServiceManager> getBackendUnifiedServiceManager();

android::binder::Status getInjectedAccessor(const std::string& name, android::os::Service* service);

} // namespace android
+209 −1
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
 * limitations under the License.
 */

#include <sys/socket.h>
#define LOG_TAG "ServiceManagerCppClient"

#include <binder/IServiceManager.h>
@@ -24,14 +25,19 @@
#include <chrono>
#include <condition_variable>

#include <FdTrigger.h>
#include <RpcSocketAddress.h>
#include <android-base/properties.h>
#include <android/os/BnAccessor.h>
#include <android/os/BnServiceCallback.h>
#include <android/os/BnServiceManager.h>
#include <android/os/IAccessor.h>
#include <android/os/IServiceManager.h>
#include <binder/IPCThreadState.h>
#include <binder/Parcel.h>
#include <binder/RpcSession.h>
#include <utils/String8.h>

#include <variant>
#ifndef __ANDROID_VNDK__
#include <binder/IPermissionController.h>
#endif
@@ -149,8 +155,141 @@ protected:
    }
};

class AccessorProvider {
public:
    AccessorProvider(RpcAccessorProvider&& provider) : mProvider(provider) {}
    sp<IBinder> provide(const String16& name) { return mProvider(name); }

private:
    AccessorProvider() = delete;

    RpcAccessorProvider mProvider;
};

class AccessorProviderEntry {
public:
    AccessorProviderEntry(std::shared_ptr<AccessorProvider>&& provider) : mProvider(provider) {}
    std::shared_ptr<AccessorProvider> mProvider;

private:
    AccessorProviderEntry() = delete;
};

[[clang::no_destroy]] static std::once_flag gSmOnce;
[[clang::no_destroy]] static sp<IServiceManager> gDefaultServiceManager;
[[clang::no_destroy]] static std::mutex gAccessorProvidersMutex;
[[clang::no_destroy]] static std::vector<AccessorProviderEntry> gAccessorProviders;

class LocalAccessor : public android::os::BnAccessor {
public:
    LocalAccessor(const String16& instance, RpcSocketAddressProvider&& connectionInfoProvider)
          : mInstance(instance), mConnectionInfoProvider(connectionInfoProvider) {
        LOG_ALWAYS_FATAL_IF(!mConnectionInfoProvider,
                            "LocalAccessor object needs a valid connection info provider");
    }

    ~LocalAccessor() {
        if (mOnDelete) mOnDelete();
    }

    ::android::binder::Status addConnection(::android::os::ParcelFileDescriptor* outFd) {
        using android::os::IAccessor;
        sockaddr_storage addrStorage;
        std::unique_ptr<FdTrigger> trigger = FdTrigger::make();
        RpcTransportFd fd;
        status_t status =
                mConnectionInfoProvider(mInstance, reinterpret_cast<sockaddr*>(&addrStorage),
                                        sizeof(addrStorage));
        if (status != OK) {
            const std::string error = "The connection info provider was unable to provide "
                                      "connection info for instance " +
                    std::string(String8(mInstance).c_str()) +
                    " with status: " + statusToString(status);
            ALOGE("%s", error.c_str());
            return Status::fromServiceSpecificError(IAccessor::ERROR_CONNECTION_INFO_NOT_FOUND,
                                                    error.c_str());
        }
        if (addrStorage.ss_family == AF_VSOCK) {
            sockaddr_vm* addr = reinterpret_cast<sockaddr_vm*>(&addrStorage);
            status = singleSocketConnection(VsockSocketAddress(addr->svm_cid, addr->svm_port),
                                            trigger, &fd);
        } else if (addrStorage.ss_family == AF_UNIX) {
            sockaddr_un* addr = reinterpret_cast<sockaddr_un*>(&addrStorage);
            status = singleSocketConnection(UnixSocketAddress(addr->sun_path), trigger, &fd);
        } else if (addrStorage.ss_family == AF_INET) {
            sockaddr_in* addr = reinterpret_cast<sockaddr_in*>(&addrStorage);
            status = singleSocketConnection(InetSocketAddress(reinterpret_cast<sockaddr*>(addr),
                                                              sizeof(sockaddr_in),
                                                              inet_ntoa(addr->sin_addr),
                                                              ntohs(addr->sin_port)),
                                            trigger, &fd);
        } else {
            const std::string error =
                    "Unsupported socket family type or the ConnectionInfoProvider failed to find a "
                    "valid address. Family type: " +
                    std::to_string(addrStorage.ss_family);
            ALOGE("%s", error.c_str());
            return Status::fromServiceSpecificError(IAccessor::ERROR_UNSUPPORTED_SOCKET_FAMILY,
                                                    error.c_str());
        }
        if (status != OK) {
            const std::string error = "Failed to connect to socket for " +
                    std::string(String8(mInstance).c_str()) +
                    " with status: " + statusToString(status);
            ALOGE("%s", error.c_str());
            int err = 0;
            if (status == -EACCES) {
                err = IAccessor::ERROR_FAILED_TO_CONNECT_EACCES;
            } else {
                err = IAccessor::ERROR_FAILED_TO_CONNECT_TO_SOCKET;
            }
            return Status::fromServiceSpecificError(err, error.c_str());
        }
        *outFd = os::ParcelFileDescriptor(std::move(fd.fd));
        return Status::ok();
    }

    ::android::binder::Status getInstanceName(String16* instance) {
        *instance = mInstance;
        return Status::ok();
    }

private:
    LocalAccessor() = delete;
    String16 mInstance;
    RpcSocketAddressProvider mConnectionInfoProvider;
    std::function<void()> mOnDelete;
};

android::binder::Status getInjectedAccessor(const std::string& name,
                                            android::os::Service* service) {
    std::vector<AccessorProviderEntry> copiedProviders;
    {
        std::lock_guard<std::mutex> lock(gAccessorProvidersMutex);
        copiedProviders.insert(copiedProviders.begin(), gAccessorProviders.begin(),
                               gAccessorProviders.end());
    }

    // Unlocked to call the providers. This requires the providers to be
    // threadsafe and not contain any references to objects that could be
    // deleted.
    for (const auto& provider : copiedProviders) {
        sp<IBinder> binder = provider.mProvider->provide(String16(name.c_str()));
        if (binder == nullptr) continue;
        status_t status = validateAccessor(String16(name.c_str()), binder);
        if (status != OK) {
            ALOGE("A provider returned a binder that is not an IAccessor for instance %s. Status: "
                  "%s",
                  name.c_str(), statusToString(status).c_str());
            return android::binder::Status::fromStatusT(android::INVALID_OPERATION);
        }
        *service = os::Service::make<os::Service::Tag::accessor>(binder);
        return android::binder::Status::ok();
    }

    *service = os::Service::make<os::Service::Tag::accessor>(nullptr);
    return android::binder::Status::ok();
}

sp<IServiceManager> defaultServiceManager()
{
@@ -173,6 +312,75 @@ void setDefaultServiceManager(const sp<IServiceManager>& sm) {
    }
}

std::weak_ptr<AccessorProvider> addAccessorProvider(RpcAccessorProvider&& providerCallback) {
    std::lock_guard<std::mutex> lock(gAccessorProvidersMutex);
    std::shared_ptr<AccessorProvider> provider =
            std::make_shared<AccessorProvider>(std::move(providerCallback));
    gAccessorProviders.push_back(AccessorProviderEntry(std::move(provider)));

    return provider;
}

status_t removeAccessorProvider(std::weak_ptr<AccessorProvider> wProvider) {
    std::shared_ptr<AccessorProvider> provider = wProvider.lock();
    if (provider == nullptr) {
        ALOGE("The provider supplied to removeAccessorProvider has already been removed.");
        return NAME_NOT_FOUND;
    }
    std::lock_guard<std::mutex> lock(gAccessorProvidersMutex);
    size_t sizeBefore = gAccessorProviders.size();
    gAccessorProviders.erase(std::remove_if(gAccessorProviders.begin(), gAccessorProviders.end(),
                                            [&](AccessorProviderEntry entry) {
                                                return entry.mProvider == provider;
                                            }),
                             gAccessorProviders.end());
    if (sizeBefore == gAccessorProviders.size()) {
        ALOGE("Failed to find an AccessorProvider for removeAccessorProvider");
        return NAME_NOT_FOUND;
    }

    return OK;
}

status_t validateAccessor(const String16& instance, const sp<IBinder>& binder) {
    if (binder == nullptr) {
        ALOGE("Binder is null");
        return BAD_VALUE;
    }
    sp<IAccessor> accessor = interface_cast<IAccessor>(binder);
    if (accessor == nullptr) {
        ALOGE("This binder for %s is not an IAccessor binder", String8(instance).c_str());
        return BAD_TYPE;
    }
    String16 reportedInstance;
    Status status = accessor->getInstanceName(&reportedInstance);
    if (!status.isOk()) {
        ALOGE("Failed to validate the binder being used to create a new ARpc_Accessor for %s with "
              "status: %s",
              String8(instance).c_str(), status.toString8().c_str());
        return NAME_NOT_FOUND;
    }
    if (reportedInstance != instance) {
        ALOGE("Instance %s doesn't match the Accessor's instance of %s", String8(instance).c_str(),
              String8(reportedInstance).c_str());
        return NAME_NOT_FOUND;
    }
    return OK;
}

sp<IBinder> createAccessor(const String16& instance,
                           RpcSocketAddressProvider&& connectionInfoProvider) {
    // Try to create a new accessor
    if (!connectionInfoProvider) {
        ALOGE("Could not find an Accessor for %s and no ConnectionInfoProvider provided to "
              "create a new one",
              String8(instance).c_str());
        return nullptr;
    }
    sp<IBinder> binder = sp<LocalAccessor>::make(instance, std::move(connectionInfoProvider));
    return binder;
}

#if !defined(__ANDROID_VNDK__)
// IPermissionController is not accessible to vendors

+18 −2
Original line number Diff line number Diff line
@@ -589,6 +589,21 @@ status_t RpcSession::setupSocketClient(const RpcSocketAddress& addr) {
status_t RpcSession::setupOneSocketConnection(const RpcSocketAddress& addr,
                                              const std::vector<uint8_t>& sessionId,
                                              bool incoming) {
    RpcTransportFd transportFd;
    status_t status = singleSocketConnection(addr, mShutdownTrigger, &transportFd);
    if (status != OK) return status;

    return initAndAddConnection(std::move(transportFd), sessionId, incoming);
}

status_t singleSocketConnection(const RpcSocketAddress& addr,
                                const std::unique_ptr<FdTrigger>& shutdownTrigger,
                                RpcTransportFd* outFd) {
    LOG_ALWAYS_FATAL_IF(outFd == nullptr,
                        "There is no reason to call this function without an outFd");
    LOG_ALWAYS_FATAL_IF(shutdownTrigger == nullptr,
                        "FdTrigger argument is required so we don't get stuck in the connect call "
                        "if the server process shuts down.");
    for (size_t tries = 0; tries < 5; tries++) {
        if (tries > 0) usleep(10000);

@@ -620,7 +635,7 @@ status_t RpcSession::setupOneSocketConnection(const RpcSocketAddress& addr,
            if (connErrno == EAGAIN || connErrno == EINPROGRESS) {
                // For non-blocking sockets, connect() may return EAGAIN (for unix domain socket) or
                // EINPROGRESS (for others). Call poll() and getsockopt() to get the error.
                status_t pollStatus = mShutdownTrigger->triggerablePoll(transportFd, POLLOUT);
                status_t pollStatus = shutdownTrigger->triggerablePoll(transportFd, POLLOUT);
                if (pollStatus != OK) {
                    ALOGE("Could not POLLOUT after connect() on non-blocking socket: %s",
                          statusToString(pollStatus).c_str());
@@ -654,7 +669,8 @@ status_t RpcSession::setupOneSocketConnection(const RpcSocketAddress& addr,
        LOG_RPC_DETAIL("Socket at %s client with fd %d", addr.toString().c_str(),
                       transportFd.fd.get());

        return initAndAddConnection(std::move(transportFd), sessionId, incoming);
        *outFd = std::move(transportFd);
        return OK;
    }

    ALOGE("Ran out of retries to connect to %s", addr.toString().c_str());
+7 −0
Original line number Diff line number Diff line
@@ -113,4 +113,11 @@ private:
    unsigned int mPort;
};

/**
 * Connects to a single socket and produces a RpcTransportFd.
 */
status_t singleSocketConnection(const RpcSocketAddress& address,
                                const std::unique_ptr<FdTrigger>& shutdownTrigger,
                                RpcTransportFd* outFd);

} // namespace android
Loading