servicemanager: notifications
You can now register and receive notifications for new services.
Eye towards:
- avoiding while(true) getService loops
- need it to make AIDL dynamic HALs
- need it for feature parity w/ HIDL
Bug: 136027762
Test: servicemanager_test
Change-Id: Iffbf984e87214aa9c9b7af8a5ae682e127ba40a5
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index 119e4c3..b3aa342 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -28,6 +28,13 @@
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 @@
.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 @@
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 @@
++it;
}
}
+
+ for (auto it = mNameToCallback.begin(); it != mNameToCallback.end();) {
+ removeCallback(who, &it, nullptr /*found*/);
+ }
}
} // namespace android