From 1c47b58f7c2d896b6953e3001e8d3818f6eb7731 Mon Sep 17 00:00:00 2001 From: Steven Moreland Date: Tue, 27 Aug 2019 18:05:27 -0700 Subject: Add waitForService to AIDL. Eye towards: - avoiding while(true) getService loops - need it to make AIDL dynamic HALs - need it for feature parity w/ HIDL Bug: 136027762 Test: use in AIDL unit test Change-Id: I2981eb4ebecff8c4519e09b7dcca51892dfd528d --- cmds/dumpsys/tests/dumpsys_test.cpp | 2 +- libs/binder/IServiceManager.cpp | 59 ++++++++++++++++++++++++++++ libs/binder/include/binder/IServiceManager.h | 13 ++++++ 3 files changed, 73 insertions(+), 1 deletion(-) diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp index 3ada15398c..cbac839e6f 100644 --- a/cmds/dumpsys/tests/dumpsys_test.cpp +++ b/cmds/dumpsys/tests/dumpsys_test.cpp @@ -53,7 +53,7 @@ class ServiceManagerMock : public IServiceManager { MOCK_CONST_METHOD1(checkService, sp(const String16&)); MOCK_METHOD4(addService, status_t(const String16&, const sp&, bool, int)); MOCK_METHOD1(listServices, Vector(int)); - + MOCK_METHOD1(waitForService, sp(const String16&)); protected: MOCK_METHOD0(onAsBinder, IBinder*()); }; diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp index 74f1f47c7a..715a4609d6 100644 --- a/libs/binder/IServiceManager.cpp +++ b/libs/binder/IServiceManager.cpp @@ -18,6 +18,7 @@ #include +#include #include #include #include @@ -212,6 +213,64 @@ public: return res; } + sp waitForService(const String16& name16) override { + class Waiter : public android::os::BnServiceCallback { + Status onRegistration(const std::string& /*name*/, + const sp& binder) override { + std::unique_lock lock(mMutex); + mBinder = binder; + lock.unlock(); + mCv.notify_one(); + return Status::ok(); + } + public: + sp mBinder; + std::mutex mMutex; + std::condition_variable mCv; + }; + + const std::string name = String8(name16).c_str(); + + sp out; + if(!mTheRealServiceManager->checkService(name, &out).isOk()) { + return nullptr; + } + if(out != nullptr) return out; + + sp waiter = new Waiter; + if (!mTheRealServiceManager->registerForNotifications( + name, waiter).isOk()) { + return nullptr; + } + + while(true) { + { + std::unique_lock lock(waiter->mMutex); + using std::literals::chrono_literals::operator""s; + waiter->mCv.wait_for(lock, 1s, [&] { + return waiter->mBinder != nullptr; + }); + if (waiter->mBinder != nullptr) return waiter->mBinder; + } + + // Handle race condition for lazy services. Here is what can happen: + // - the service dies (not processed by init yet). + // - sm processes death notification. + // - sm gets checkService and calls init to start service. + // - init gets the start signal, but the service already appears + // started, so it does nothing. + // - init gets death signal, but doesn't know it needs to restart + // the service + // - we need to request service again to get it to start + if(!mTheRealServiceManager->checkService(name, &out).isOk()) { + return nullptr; + } + if(out != nullptr) return out; + + ALOGW("Waited one second for %s", name.c_str()); + } + } + private: sp mTheRealServiceManager; }; diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h index 30786fa0cb..8ae860df38 100644 --- a/libs/binder/include/binder/IServiceManager.h +++ b/libs/binder/include/binder/IServiceManager.h @@ -71,10 +71,23 @@ public: */ // NOLINTNEXTLINE(google-default-arguments) virtual Vector listServices(int dumpsysFlags = DUMP_FLAG_PRIORITY_ALL) = 0; + + /** + * Efficiently wait for a service. + * + * Returns nullptr only for permission problem or fatal error. + */ + virtual sp waitForService(const String16& name) = 0; }; sp defaultServiceManager(); +template +sp waitForService(const String16& name) { + const sp sm = defaultServiceManager(); + return interface_cast(sm->waitForService(name)); +} + template status_t getService(const String16& name, sp* outService) { -- cgit v1.2.3-59-g8ed1b