diff options
| author | 2022-11-18 21:15:35 +0000 | |
|---|---|---|
| committer | 2022-11-22 02:03:33 +0000 | |
| commit | 09b9d160951805c536a179083868fb32e14bc95b (patch) | |
| tree | e640a3618064e1a3498515fc062623446f9c2e61 /libs | |
| parent | a3793499691e3bcb52964cdfdad3a84124839b32 (diff) | |
libbinder_ndk: service notifications
Hey - this API looks shiny. You probably want to use
waitForService instead - because 99% of the time, guaranteeing the
code works is more important than controlling the boot order. For
the cases we really really really need to be impatient, here we
go! :)
Bug: 232878913
Test: libbinder_ndk_unit_test
Change-Id: Iae090a8cc48e968faec1d75cf304e2995b9553e0
Diffstat (limited to 'libs')
| -rw-r--r-- | libs/binder/ndk/include_platform/android/binder_manager.h | 61 | ||||
| -rw-r--r-- | libs/binder/ndk/libbinder_ndk.map.txt | 2 | ||||
| -rw-r--r-- | libs/binder/ndk/service_manager.cpp | 62 | ||||
| -rw-r--r-- | libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp | 41 |
4 files changed, 166 insertions, 0 deletions
diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h index c23427012a..1f1defef52 100644 --- a/libs/binder/ndk/include_platform/android/binder_manager.h +++ b/libs/binder/ndk/include_platform/android/binder_manager.h @@ -108,6 +108,67 @@ __attribute__((warn_unused_result)) AIBinder* AServiceManager_waitForService(con __INTRODUCED_IN(31); /** + * Function to call when a service is registered. The instance is passed as well as + * ownership of the binder named 'registered'. + * + * WARNING: a lock is held when this method is called in order to prevent races with + * AServiceManager_NotificationRegistration_delete. Do not make synchronous binder calls when + * implementing this method to avoid deadlocks. + * + * \param instance instance name of service registered + * \param registered ownership-passed instance of service registered + * \param cookie data passed during registration for notifications + */ +typedef void (*AServiceManager_onRegister)(const char* instance, AIBinder* registered, + void* cookie); + +/** + * Represents a registration to servicemanager which can be cleared anytime. + */ +struct AServiceManager_NotificationRegistration; + +/** + * Get notifications when a service is registered. If the service is already registered, + * you will immediately get a notification. + * + * WARNING: it is strongly recommended to use AServiceManager_waitForService API instead. + * That API will wait synchronously, which is what you usually want in cases, including + * using some feature or during boot up. There is a history of bugs where waiting for + * notifications like this races with service startup. Also, when this API is used, a service + * bug will result in silent failure (rather than a debuggable deadlock). Furthermore, there + * is a history of this API being used to know when a service is up as a proxy for whethre + * that service should be started. This should only be used if you are intending to get + * ahold of the service as a client. For lazy services, whether a service is registered + * should not be used as a proxy for when it should be registered, which is only known + * by the real client. + * + * WARNING: if you use this API, you must also ensure that you check missing services are + * started and crash otherwise. If service failures are ignored, the system rots. + * + * \param instance name of service to wait for notifications about + * \param onRegister callback for when service is registered + * \param cookie data associated with this callback + * + * \return the token for this registration. Deleting this token will unregister. + */ +__attribute__((warn_unused_result)) AServiceManager_NotificationRegistration* +AServiceManager_registerForServiceNotifications(const char* instance, + AServiceManager_onRegister onRegister, void* cookie) + __INTRODUCED_IN(34); + +/** + * Unregister for notifications and delete the object. + * + * After this method is called, the callback is guaranteed to no longer be invoked. This will block + * until any in-progress onRegister callbacks have completed. It is therefore safe to immediately + * destroy the void* cookie that was registered when this method returns. + * + * \param notification object to dismiss + */ +void AServiceManager_NotificationRegistration_delete( + AServiceManager_NotificationRegistration* notification) __INTRODUCED_IN(34); + +/** * Check if a service is declared (e.g. VINTF manifest). * * \param instance identifier of the service. diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt index 32ca5649dd..5c7005ceb7 100644 --- a/libs/binder/ndk/libbinder_ndk.map.txt +++ b/libs/binder/ndk/libbinder_ndk.map.txt @@ -155,6 +155,8 @@ LIBBINDER_NDK33 { # introduced=33 LIBBINDER_NDK34 { # introduced=UpsideDownCake global: AServiceManager_getUpdatableApexName; # systemapi + AServiceManager_registerForServiceNotifications; # systemapi llndk + AServiceManager_NotificationRegistration_delete; # systemapi llndk }; LIBBINDER_NDK_PLATFORM { diff --git a/libs/binder/ndk/service_manager.cpp b/libs/binder/ndk/service_manager.cpp index a12d0e9e8d..e107c83d14 100644 --- a/libs/binder/ndk/service_manager.cpp +++ b/libs/binder/ndk/service_manager.cpp @@ -28,6 +28,7 @@ using ::android::IBinder; using ::android::IServiceManager; using ::android::sp; using ::android::status_t; +using ::android::statusToString; using ::android::String16; using ::android::String8; @@ -86,6 +87,67 @@ AIBinder* AServiceManager_waitForService(const char* instance) { AIBinder_incStrong(ret.get()); return ret.get(); } +typedef void (*AServiceManager_onRegister)(const char* instance, AIBinder* registered, + void* cookie); + +struct AServiceManager_NotificationRegistration + : public IServiceManager::LocalRegistrationCallback { + std::mutex m; + const char* instance = nullptr; + void* cookie = nullptr; + AServiceManager_onRegister onRegister = nullptr; + + virtual void onServiceRegistration(const String16& smInstance, const sp<IBinder>& binder) { + std::lock_guard<std::mutex> l(m); + if (onRegister == nullptr) return; + + CHECK_EQ(String8(smInstance), instance); + + sp<AIBinder> ret = ABpBinder::lookupOrCreateFromBinder(binder); + AIBinder_incStrong(ret.get()); + + onRegister(instance, ret.get(), cookie); + } + + void clear() { + std::lock_guard<std::mutex> l(m); + instance = nullptr; + cookie = nullptr; + onRegister = nullptr; + } +}; + +__attribute__((warn_unused_result)) AServiceManager_NotificationRegistration* +AServiceManager_registerForServiceNotifications(const char* instance, + AServiceManager_onRegister onRegister, + void* cookie) { + CHECK_NE(instance, nullptr); + CHECK_NE(onRegister, nullptr) << instance; + // cookie can be nullptr + + auto cb = sp<AServiceManager_NotificationRegistration>::make(); + cb->instance = instance; + cb->onRegister = onRegister; + cb->cookie = cookie; + + sp<IServiceManager> sm = defaultServiceManager(); + if (status_t res = sm->registerForNotifications(String16(instance), cb); res != STATUS_OK) { + LOG(ERROR) << "Failed to register for service notifications for " << instance << ": " + << statusToString(res); + return nullptr; + } + + cb->incStrong(nullptr); + return cb.get(); +} + +void AServiceManager_NotificationRegistration_delete( + AServiceManager_NotificationRegistration* notification) { + CHECK_NE(notification, nullptr); + notification->clear(); + notification->decStrong(nullptr); +} + bool AServiceManager_isDeclared(const char* instance) { if (instance == nullptr) { return false; diff --git a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp index e221e4c2bb..9d5ef6805a 100644 --- a/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp +++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp @@ -254,6 +254,47 @@ TEST(NdkBinder, CheckServiceThatDoesExist) { AIBinder_decStrong(binder); } +struct ServiceData { + std::string instance; + ndk::SpAIBinder binder; + + static void fillOnRegister(const char* instance, AIBinder* binder, void* cookie) { + ServiceData* d = reinterpret_cast<ServiceData*>(cookie); + d->instance = instance; + d->binder = ndk::SpAIBinder(binder); + } +}; + +TEST(NdkBinder, RegisterForServiceNotificationsNonExisting) { + ServiceData data; + auto* notif = AServiceManager_registerForServiceNotifications( + "DOES_NOT_EXIST", ServiceData::fillOnRegister, (void*)&data); + ASSERT_NE(notif, nullptr); + + sleep(1); // give us a chance to fail + AServiceManager_NotificationRegistration_delete(notif); + + // checking after deleting to avoid needing a mutex over the data - otherwise + // in an environment w/ multiple threads, you would need to guard access + EXPECT_EQ(data.instance, ""); + EXPECT_EQ(data.binder, nullptr); +} + +TEST(NdkBinder, RegisterForServiceNotificationsExisting) { + ServiceData data; + auto* notif = AServiceManager_registerForServiceNotifications( + kExistingNonNdkService, ServiceData::fillOnRegister, (void*)&data); + ASSERT_NE(notif, nullptr); + + sleep(1); // give us a chance to fail + AServiceManager_NotificationRegistration_delete(notif); + + // checking after deleting to avoid needing a mutex over the data - otherwise + // in an environment w/ multiple threads, you would need to guard access + EXPECT_EQ(data.instance, kExistingNonNdkService); + EXPECT_EQ(data.binder, ndk::SpAIBinder(AServiceManager_checkService(kExistingNonNdkService))); +} + TEST(NdkBinder, UnimplementedDump) { sp<IFoo> foo = IFoo::getService(IFoo::kSomeInstanceName); ASSERT_NE(foo, nullptr); |