/* * Copyright (C) 2024 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. */ #pragma once #include #include #include #include namespace android::mediautils { // General Template Binder Utilities. // // In order to write generic Template methods, we need to have utility methods // that provide seamless template overload resolution between NDK and CPP variants. // // Returns true or false based on whether the Interface is a NDK Interface. template inline constexpr bool is_ndk = std::derived_from; // Returns the Interface ptr type (shared_ptr or sp) based on the Interface. template using InterfaceType = std::conditional_t , std::shared_ptr, sp>; template using BaseInterfaceType = std::conditional_t , std::shared_ptr<::ndk::ICInterface>, sp<::android::IInterface>>; /** * Returns either a sp or an SpAIBinder object * for the AIDL interface given. * * A -cpp interface will return sp. * A -ndk interface will return SpAIBinder */ template sp binderFromInterface(const sp &interface) { return IInterface::asBinder(interface); } template ::ndk::SpAIBinder binderFromInterface(const std::shared_ptr &interface) { return interface->asBinder(); } /** * Returns either a sp or a std::shared_ptr from a Binder object. * * A -cpp interface will return sp. * A -ndk interface will return std::shared_ptr */ template sp interfaceFromBinder(const sp &binder) { return interface_cast(binder); } template std::shared_ptr interfaceFromBinder(const ::ndk::SpAIBinder &binder) { return Interface::fromBinder(binder); } /** * Returns either a sp or a std::shared_ptr from * the NDK/CPP base interface class. */ template sp interfaceFromBase(const sp<::android::IInterface> &interface) { // this is unvalidated, though could verify getInterfaceDescriptor() == Interface::descriptor return sp::cast(interface); } template std::shared_ptr interfaceFromBase( const std::shared_ptr<::ndk::ICInterface> &interface) { // this is unvalidated, though could verify // !strcmp(AIBinder_Class_getDescriptor(AIBinder_getClass(...), Interface::descriptor) return std::static_pointer_cast(interface); } /** * Returns a fully qualified service name. * * @param name * If name is empty, it returns the name from the Service descriptor. * If name starts with '/', it appends the name as a version to the Service descriptor, * e.g. "/default". * Otherwise the name is assumed to be the full Service name, overriding the * Service descriptor. */ template auto fullyQualifiedServiceName(const char* const name) { using StringType = std::conditional_t, std::string, String16>; return name == nullptr ? StringType(Service::descriptor) : name[0] != 0 && name[0] != '/' ? StringType(name) : StringType(Service::descriptor) + StringType(name); } /** * Returns either a std::shared_ptr or sp * for the AIDL interface given. * * A -cpp interface will return sp. * A -ndk interface will return std::shared_ptr * * @param name if non-empty should contain either a suffix if it starts * with a '/' such as "/default", or the full service name. */ template auto checkServicePassThrough(const char *const name = "") { if constexpr(is_ndk) { const auto serviceName = fullyQualifiedServiceName(name); return Service::fromBinder( ::ndk::SpAIBinder(AServiceManager_checkService(serviceName.c_str()))); } else /* constexpr */ { const auto serviceName = fullyQualifiedServiceName(name); auto binder = defaultServiceManager()->checkService(serviceName); return interface_cast(binder); } } template void addService(const std::shared_ptr &service) { AServiceManager_addService(binderFromInterface(service), Service::descriptor); } template void addService(const sp &service) { defaultServiceManager()->addService(Service::descriptor, binderFromInterface(service)); } namespace details { // Use the APIs below, not the details here. /** * RequestServiceManagerCallback(Cpp|Ndk) is a RAII class that * requests a ServiceManager callback. * * Note the ServiceManager is a single threaded "apartment" and only one * transaction is active, hence: * * 1) After the RequestServiceManagerCallback object is destroyed no * calls to the onBinder function is pending or will occur. * 2) To prevent deadlock, do not construct or destroy the class with * a lock held that the onService function also requires. */ template class RequestServiceManagerCallbackCpp { public: explicit RequestServiceManagerCallbackCpp( std::function &)> &&onService, const char *const serviceName = "" ) : mServiceName{fullyQualifiedServiceName(serviceName)}, mWaiter{sp::make(std::move(onService))}, mStatus{defaultServiceManager()->registerForNotifications(mServiceName, mWaiter)} { } ~RequestServiceManagerCallbackCpp() { if (mStatus == OK) { defaultServiceManager()->unregisterForNotifications(mServiceName, mWaiter); } } status_t getStatus() const { return mStatus; } private: const String16 mServiceName; const sp mWaiter; const status_t mStatus; // With some work here, we could make this a singleton to improve // performance and reduce binder clutter. class Waiter : public IServiceManager::LocalRegistrationCallback { public: explicit Waiter(std::function &)> &&onService) : mOnService{std::move(onService)} {} private: void onServiceRegistration( const String16 & /*name*/, const sp &binder) final { mOnService(interface_cast(binder)); } const std::function &)> mOnService; }; }; template class RequestServiceManagerCallbackNdk { public: explicit RequestServiceManagerCallbackNdk( std::function &)> &&onService, const char *const serviceName = "" ) : mServiceName{fullyQualifiedServiceName(serviceName)}, mOnService{std::move(onService)}, mWaiter{AServiceManager_registerForServiceNotifications( mServiceName.c_str(), onRegister, this)} // must be registered after mOnService. {} ~RequestServiceManagerCallbackNdk() { if (mWaiter) { AServiceManager_NotificationRegistration_delete(mWaiter); } } status_t getStatus() const { return mWaiter != nullptr ? OK : INVALID_OPERATION; } private: const std::string mServiceName; // must keep a local copy. const std::function &)> mOnService; AServiceManager_NotificationRegistration *const mWaiter; // last. static void onRegister(const char *instance, AIBinder *registered, void *cookie) { (void) instance; auto *callbackHandler = static_cast *>(cookie); callbackHandler->mOnService(Service::fromBinder(::ndk::SpAIBinder(registered))); } }; /** * RequestDeathNotification(Cpp|Ndk) is a RAII class that * requests a death notification. * * Note the ServiceManager is a single threaded "apartment" and only one * transaction is active, hence: * * 1) After the RequestDeathNotification object is destroyed no * calls to the onBinder function is pending or will occur. * 2) To prevent deadlock, do not construct or destroy the class with * a lock held that the onBinderDied function also requires. */ class RequestDeathNotificationCpp { class DeathRecipientHelper : public IBinder::DeathRecipient { public: explicit DeathRecipientHelper(std::function &&onBinderDied) : mOnBinderDied{std::move(onBinderDied)} { } void binderDied(const wp &weakBinder) final { (void) weakBinder; mOnBinderDied(); } private: const std::function mOnBinderDied; }; public: RequestDeathNotificationCpp(const sp &binder, std::function &&onBinderDied) : mHelper{sp::make(std::move(onBinderDied))}, mWeakBinder{binder}, mStatus{binder->linkToDeath(mHelper)} { ALOGW_IF(mStatus != OK, "%s: linkToDeath status:%d", __func__, mStatus); } ~RequestDeathNotificationCpp() { if (mStatus == OK) { const auto binder = mWeakBinder.promote(); if (binder) binder->unlinkToDeath(mHelper); } } status_t getStatus() const { return mStatus; } private: const sp mHelper; const wp mWeakBinder; const status_t mStatus; }; class RequestDeathNotificationNdk { public: RequestDeathNotificationNdk( const ::ndk::SpAIBinder &binder, std::function&& onBinderDied) : mRecipient(::AIBinder_DeathRecipient_new(OnBinderDiedStatic), &AIBinder_DeathRecipient_delete), mStatus{(AIBinder_DeathRecipient_setOnUnlinked( // sets cookie deleter mRecipient.get(), OnBinderDiedUnlinkedStatic), AIBinder_linkToDeath( // registers callback binder.get(), mRecipient.get(), // we create functional cookie ptr which may outlive this object. new std::function(std::move(onBinderDied))))} { ALOGW_IF(mStatus != OK, "%s: AIBinder_linkToDeath status:%d", __func__, mStatus); } ~RequestDeathNotificationNdk() { // mRecipient's unique_ptr calls AIBinder_DeathRecipient_delete to unlink the recipient. // Then OnBinderDiedUnlinkedStatic eventually deletes the cookie. } status_t getStatus() const { return mStatus; } private: static void OnBinderDiedUnlinkedStatic(void* cookie) { delete reinterpret_cast*>(cookie); } static void OnBinderDiedStatic(void* cookie) { (*reinterpret_cast*>(cookie))(); } const std::unique_ptr mRecipient; const status_t mStatus; // binder_status_t is a limited subset of status_t }; } // details /** * Requests a notification that service is available. * * An opaque handle is returned - after clearing it is guaranteed that * no callback will occur. * * The callback will be of form: * onService(const sp& service); * onService(const std::shared_ptr& service); */ template std::shared_ptr requestServiceNotification( F onService, const char *const serviceName = "") { // the following are used for callbacks but placed here for invalidate. using RequestServiceManagerCallback = std::conditional_t, details::RequestServiceManagerCallbackNdk, details::RequestServiceManagerCallbackCpp>; const auto ptr = std::make_shared( onService, serviceName); const auto status = ptr->getStatus(); return status == OK ? ptr : nullptr; } /** * Requests a death notification. * * An opaque handle is returned. If the service is already dead, the * handle will be null. * * Implementation detail: A callback may occur after the handle is released * if a death notification is in progress. * * The callback will be of form void onBinderDied(); */ template std::shared_ptr requestDeathNotification( const sp &service, std::function &&onBinderDied) { const auto ptr = std::make_shared( binderFromInterface(service), std::move(onBinderDied)); const auto status = ptr->getStatus(); return status == OK ? ptr : nullptr; } template std::shared_ptr requestDeathNotification( const std::shared_ptr &service, std::function &&onBinderDied) { const auto ptr = std::make_shared( binderFromInterface(service), std::move(onBinderDied)); const auto status = ptr->getStatus(); return status == OK ? ptr : nullptr; } } // namespace android::mediautils