Loading media/libaudioclient/AudioSystem.cpp +17 −6 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ #include <media/TypeConverter.h> #include <mediautils/ServiceSingleton.h> #include <math.h> #include <private/android_filesystem_config.h> #include <system/audio.h> #include <android/media/GetInputForAttrResponse.h> Loading Loading @@ -169,9 +170,14 @@ public: if (!mDisableThreadPoolStart) { ProcessState::self()->startThreadPool(); } if (multiuser_get_app_id(getuid()) == AID_AUDIOSERVER) { mediautils::skipService<media::IAudioFlingerService>(mediautils::SkipMode::kWait); mWaitMs = std::chrono::milliseconds(INT32_MAX); } else { mediautils::initService<media::IAudioFlingerService, AudioFlingerServiceTraits>(); mWaitMs = std::chrono::milliseconds( property_get_int32(kServiceWaitProperty, kServiceClientWaitMs)); } init = true; } if (mValid) return mService; Loading Loading @@ -1015,9 +1021,14 @@ public: if (!mDisableThreadPoolStart) { ProcessState::self()->startThreadPool(); } if (multiuser_get_app_id(getuid()) == AID_AUDIOSERVER) { mediautils::skipService<IAudioPolicyService>(mediautils::SkipMode::kWait); mWaitMs = std::chrono::milliseconds(INT32_MAX); } else { mediautils::initService<IAudioPolicyService, AudioPolicyServiceTraits>(); mWaitMs = std::chrono::milliseconds( property_get_int32(kServiceWaitProperty, kServiceClientWaitMs)); } init = true; } if (mValid) return mService; Loading media/utils/include/mediautils/ServiceSingleton.h +60 −38 Original line number Diff line number Diff line Loading @@ -57,12 +57,20 @@ */ namespace android::mediautils { enum ServiceOptions { enum class ServiceOptions { kNone = 0, kNonNull = (1 << 0), // don't return a null interface unless disabled. // partially implemented and experimental. }; enum class SkipMode { kNone = 0, // do not skip the cache (normal behavior for caching services). kImmediate = 1, // do not cache or find the service, return null to the caller immediately, // which is the normal behavior for skipping the service cache. kWait = 2, // do not cache or find the service, but block the caller; // this is used for cases where a local service override is desired. }; // Traits may come through a constexpr static function collection. // This participates in small buffer optimization SBO in std::function impl. template <typename Service> Loading Loading @@ -135,7 +143,7 @@ public: std::swap(oldTraits, mTraits); const bool existing = oldTraits != nullptr; mTraits = std::move(traits); mSkip = false; mSkipMode = SkipMode::kNone; return existing; } Loading @@ -154,7 +162,8 @@ public: audio_utils::unique_lock ul(mMutex); auto& service = std::get<BaseInterfaceType<Service>>(mService); if (mSkip || (service && mValid)) return service; // early check. // early check. if (mSkipMode == SkipMode::kImmediate || (service && mValid)) return service; // clamp to avoid numeric overflow. INT64_MAX / 2 is effectively forever for a device. std::chrono::nanoseconds kWaitLimitNs( Loading @@ -164,8 +173,10 @@ public: for (bool first = true; true; first = false) { // we may have released mMutex, so see if service has been obtained. if (mSkip || (service && mValid)) return service; if (mSkipMode == SkipMode::kImmediate || (service && mValid)) return service; int options = 0; if (mSkipMode == SkipMode::kNone) { const auto traits = getTraits_l<Service>(); // first time or not using callback, check the service. Loading @@ -187,16 +198,19 @@ public: return service_fixed; } } // install service callback if needed. if (useCallback && !mServiceNotificationHandle) { setServiceNotifier_l<Service>(); } options = static_cast<int>(traits->options()); } // check time expiration. const auto now = std::chrono::steady_clock::now(); if (now >= end && (service || !(traits->options() & ServiceOptions::kNonNull))) { if (now >= end && (service || mSkipMode != SkipMode::kNone // skip is set. || !(options & static_cast<int>(ServiceOptions::kNonNull)))) { // null allowed return service; } Loading Loading @@ -241,11 +255,16 @@ public: * * All notifiers removed. * Service pointer is released. * * If skipMode is kNone, then cache management is immediately reenabled. * If skipMode is kImmediate, then any new waiters will return null immediately. * If skipMode is kWait, then any new waiters will be blocked until an update occurs * or the timeout expires. */ template<typename Service> void skip() { void skip(SkipMode skipMode) { audio_utils::unique_lock ul(mMutex); mSkip = true; mSkipMode = skipMode; // remove notifiers. OK to hold lock as presuming notifications one-way // or manually triggered outside of lock. mDeathNotificationHandle.reset(); Loading Loading @@ -274,7 +293,8 @@ private: mDeathNotificationHandle.reset(); const auto traits = getTraits_l<Service>(); mValid = false; if (!(traits->options() & ServiceOptions::kNonNull) || mSkip) { if (!(static_cast<int>(traits->options()) & static_cast<int>(ServiceOptions::kNonNull)) || mSkipMode != SkipMode::kNone) { auto &service = std::get<BaseInterfaceType<Service>>(mService); service = nullptr; } Loading Loading @@ -380,8 +400,10 @@ private: // mValid is true iff the service is non-null and alive. bool mValid GUARDED_BY(mMutex) = false; // mSkip indicates that the service is not cached. bool mSkip GUARDED_BY(mMutex) = false; // mSkipMode indicates the service cache state: // // one may either wait (blocked) until the service is reinitialized. SkipMode mSkipMode GUARDED_BY(mMutex) = SkipMode::kNone; }; } // details Loading Loading @@ -474,9 +496,9 @@ void setService(const InterfaceType<Service>& service) { * another initService() can be called seamlessly. */ template<typename Service> void skipService() { void skipService(SkipMode skipMode = SkipMode::kImmediate) { const auto serviceHandler = details::ServiceHandler::getInstance(Service::descriptor); serviceHandler->template skip<Service>(); serviceHandler->template skip<Service>(skipMode); } } // namespace android::mediautils media/utils/tests/service_singleton_tests.cpp +36 −0 Original line number Diff line number Diff line Loading @@ -299,6 +299,42 @@ TEST(service_singleton_tests, one_and_only) { EXPECT_EQ(4, sServiceDied); EXPECT_EQ(4, listenerServiceCreated); // our listener picks it up. { // in default mode (kNull) a null is returned when the service is skipped and // wait time is ignored. const auto ref1 = std::chrono::steady_clock::now(); auto service = mediautils::getService<IServiceSingletonTest>(std::chrono::seconds(2)); EXPECT_FALSE(service); const auto ref2 = std::chrono::steady_clock::now(); EXPECT_LT(ref2 - ref1, std::chrono::seconds(1)); auto service2 = mediautils::getService<aidl::IServiceSingletonTest>( std::chrono::seconds(2)); EXPECT_FALSE(service2); const auto ref3 = std::chrono::steady_clock::now(); EXPECT_LT(ref3 - ref2, std::chrono::seconds(1)); } // Cancel the singleton cache but use wait mode. mediautils::skipService<IServiceSingletonTest>(mediautils::SkipMode::kWait); mediautils::skipService<aidl::IServiceSingletonTest>(mediautils::SkipMode::kWait); { // in wait mode, the timeouts are respected const auto ref1 = std::chrono::steady_clock::now(); auto service = mediautils::getService<IServiceSingletonTest>(std::chrono::seconds(1)); EXPECT_FALSE(service); const auto ref2 = std::chrono::steady_clock::now(); EXPECT_GT(ref2 - ref1, std::chrono::seconds(1)); auto service2 = mediautils::getService<aidl::IServiceSingletonTest>( std::chrono::seconds(1)); EXPECT_FALSE(service2); const auto ref3 = std::chrono::steady_clock::now(); EXPECT_GT(ref3 - ref2, std::chrono::seconds(1)); } // remove service remoteWorker->putc('b'); EXPECT_EQ('b', remoteWorker->getc()); Loading Loading
media/libaudioclient/AudioSystem.cpp +17 −6 Original line number Diff line number Diff line Loading @@ -35,6 +35,7 @@ #include <media/TypeConverter.h> #include <mediautils/ServiceSingleton.h> #include <math.h> #include <private/android_filesystem_config.h> #include <system/audio.h> #include <android/media/GetInputForAttrResponse.h> Loading Loading @@ -169,9 +170,14 @@ public: if (!mDisableThreadPoolStart) { ProcessState::self()->startThreadPool(); } if (multiuser_get_app_id(getuid()) == AID_AUDIOSERVER) { mediautils::skipService<media::IAudioFlingerService>(mediautils::SkipMode::kWait); mWaitMs = std::chrono::milliseconds(INT32_MAX); } else { mediautils::initService<media::IAudioFlingerService, AudioFlingerServiceTraits>(); mWaitMs = std::chrono::milliseconds( property_get_int32(kServiceWaitProperty, kServiceClientWaitMs)); } init = true; } if (mValid) return mService; Loading Loading @@ -1015,9 +1021,14 @@ public: if (!mDisableThreadPoolStart) { ProcessState::self()->startThreadPool(); } if (multiuser_get_app_id(getuid()) == AID_AUDIOSERVER) { mediautils::skipService<IAudioPolicyService>(mediautils::SkipMode::kWait); mWaitMs = std::chrono::milliseconds(INT32_MAX); } else { mediautils::initService<IAudioPolicyService, AudioPolicyServiceTraits>(); mWaitMs = std::chrono::milliseconds( property_get_int32(kServiceWaitProperty, kServiceClientWaitMs)); } init = true; } if (mValid) return mService; Loading
media/utils/include/mediautils/ServiceSingleton.h +60 −38 Original line number Diff line number Diff line Loading @@ -57,12 +57,20 @@ */ namespace android::mediautils { enum ServiceOptions { enum class ServiceOptions { kNone = 0, kNonNull = (1 << 0), // don't return a null interface unless disabled. // partially implemented and experimental. }; enum class SkipMode { kNone = 0, // do not skip the cache (normal behavior for caching services). kImmediate = 1, // do not cache or find the service, return null to the caller immediately, // which is the normal behavior for skipping the service cache. kWait = 2, // do not cache or find the service, but block the caller; // this is used for cases where a local service override is desired. }; // Traits may come through a constexpr static function collection. // This participates in small buffer optimization SBO in std::function impl. template <typename Service> Loading Loading @@ -135,7 +143,7 @@ public: std::swap(oldTraits, mTraits); const bool existing = oldTraits != nullptr; mTraits = std::move(traits); mSkip = false; mSkipMode = SkipMode::kNone; return existing; } Loading @@ -154,7 +162,8 @@ public: audio_utils::unique_lock ul(mMutex); auto& service = std::get<BaseInterfaceType<Service>>(mService); if (mSkip || (service && mValid)) return service; // early check. // early check. if (mSkipMode == SkipMode::kImmediate || (service && mValid)) return service; // clamp to avoid numeric overflow. INT64_MAX / 2 is effectively forever for a device. std::chrono::nanoseconds kWaitLimitNs( Loading @@ -164,8 +173,10 @@ public: for (bool first = true; true; first = false) { // we may have released mMutex, so see if service has been obtained. if (mSkip || (service && mValid)) return service; if (mSkipMode == SkipMode::kImmediate || (service && mValid)) return service; int options = 0; if (mSkipMode == SkipMode::kNone) { const auto traits = getTraits_l<Service>(); // first time or not using callback, check the service. Loading @@ -187,16 +198,19 @@ public: return service_fixed; } } // install service callback if needed. if (useCallback && !mServiceNotificationHandle) { setServiceNotifier_l<Service>(); } options = static_cast<int>(traits->options()); } // check time expiration. const auto now = std::chrono::steady_clock::now(); if (now >= end && (service || !(traits->options() & ServiceOptions::kNonNull))) { if (now >= end && (service || mSkipMode != SkipMode::kNone // skip is set. || !(options & static_cast<int>(ServiceOptions::kNonNull)))) { // null allowed return service; } Loading Loading @@ -241,11 +255,16 @@ public: * * All notifiers removed. * Service pointer is released. * * If skipMode is kNone, then cache management is immediately reenabled. * If skipMode is kImmediate, then any new waiters will return null immediately. * If skipMode is kWait, then any new waiters will be blocked until an update occurs * or the timeout expires. */ template<typename Service> void skip() { void skip(SkipMode skipMode) { audio_utils::unique_lock ul(mMutex); mSkip = true; mSkipMode = skipMode; // remove notifiers. OK to hold lock as presuming notifications one-way // or manually triggered outside of lock. mDeathNotificationHandle.reset(); Loading Loading @@ -274,7 +293,8 @@ private: mDeathNotificationHandle.reset(); const auto traits = getTraits_l<Service>(); mValid = false; if (!(traits->options() & ServiceOptions::kNonNull) || mSkip) { if (!(static_cast<int>(traits->options()) & static_cast<int>(ServiceOptions::kNonNull)) || mSkipMode != SkipMode::kNone) { auto &service = std::get<BaseInterfaceType<Service>>(mService); service = nullptr; } Loading Loading @@ -380,8 +400,10 @@ private: // mValid is true iff the service is non-null and alive. bool mValid GUARDED_BY(mMutex) = false; // mSkip indicates that the service is not cached. bool mSkip GUARDED_BY(mMutex) = false; // mSkipMode indicates the service cache state: // // one may either wait (blocked) until the service is reinitialized. SkipMode mSkipMode GUARDED_BY(mMutex) = SkipMode::kNone; }; } // details Loading Loading @@ -474,9 +496,9 @@ void setService(const InterfaceType<Service>& service) { * another initService() can be called seamlessly. */ template<typename Service> void skipService() { void skipService(SkipMode skipMode = SkipMode::kImmediate) { const auto serviceHandler = details::ServiceHandler::getInstance(Service::descriptor); serviceHandler->template skip<Service>(); serviceHandler->template skip<Service>(skipMode); } } // namespace android::mediautils
media/utils/tests/service_singleton_tests.cpp +36 −0 Original line number Diff line number Diff line Loading @@ -299,6 +299,42 @@ TEST(service_singleton_tests, one_and_only) { EXPECT_EQ(4, sServiceDied); EXPECT_EQ(4, listenerServiceCreated); // our listener picks it up. { // in default mode (kNull) a null is returned when the service is skipped and // wait time is ignored. const auto ref1 = std::chrono::steady_clock::now(); auto service = mediautils::getService<IServiceSingletonTest>(std::chrono::seconds(2)); EXPECT_FALSE(service); const auto ref2 = std::chrono::steady_clock::now(); EXPECT_LT(ref2 - ref1, std::chrono::seconds(1)); auto service2 = mediautils::getService<aidl::IServiceSingletonTest>( std::chrono::seconds(2)); EXPECT_FALSE(service2); const auto ref3 = std::chrono::steady_clock::now(); EXPECT_LT(ref3 - ref2, std::chrono::seconds(1)); } // Cancel the singleton cache but use wait mode. mediautils::skipService<IServiceSingletonTest>(mediautils::SkipMode::kWait); mediautils::skipService<aidl::IServiceSingletonTest>(mediautils::SkipMode::kWait); { // in wait mode, the timeouts are respected const auto ref1 = std::chrono::steady_clock::now(); auto service = mediautils::getService<IServiceSingletonTest>(std::chrono::seconds(1)); EXPECT_FALSE(service); const auto ref2 = std::chrono::steady_clock::now(); EXPECT_GT(ref2 - ref1, std::chrono::seconds(1)); auto service2 = mediautils::getService<aidl::IServiceSingletonTest>( std::chrono::seconds(1)); EXPECT_FALSE(service2); const auto ref3 = std::chrono::steady_clock::now(); EXPECT_GT(ref3 - ref2, std::chrono::seconds(1)); } // remove service remoteWorker->putc('b'); EXPECT_EQ('b', remoteWorker->getc()); Loading