xref: /aosp_15_r20/frameworks/av/media/utils/include/mediautils/ServiceSingleton.h (revision ec779b8e0859a360c3d303172224686826e6e0e1)
1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #pragma once
18 
19 #include "BinderGenericUtils.h"
20 
21 #include <android-base/thread_annotations.h>
22 #include <audio_utils/mutex.h>
23 #include <chrono>
24 #include <map>
25 #include <mutex>
26 #include <utils/Log.h>
27 #include <utils/Timers.h>
28 
29 /**
30  * ServiceSingleton provides a non-blocking NDK/CPP compatible service cache.
31  *
32  * This is a specialized cache that allows per-service configuration.
33  *
34  * Features:
35  *
36  * 1) Seamless compatibility with NDK and CPP based interfaces.
37  * 2) Time-out based service acquisition.
38  *    Set the maximum time to wait for any service.
39  * 3) Service prefetch:
40  *    Reduce start-up by prefetching service in advance (not on demand).
41  *    Prefetch is automatically installed by getService().
42  * 4) Manual interface setting for test and non-service manager acquisition support.
43  *
44  * If both NDK and CPP interfaces are available, we prefer the CPP version
45  * for the following reasons:
46  * 1) Established sp<> reference counting avoids mistakes. NDK tends to be error-prone.
47  * 2) Possible reduced binder object clutter by a singleton notification binder object.
48  *    Fewer binder objects are more efficient for the binder driver and ServiceManager.
49  *    For example, fewer binder deaths means less ServiceManager (linear time) cleanup.
50  *    A single binder object also offers binder access serialization.
51  * 3) CPP offers slightly better efficiency as it is closer to the
52  *    actual implementation, a minor detail and effect.
53  *
54  * We use a per-service ServiceHandler object to collect methods and implementation details.
55  * Currently this is separate for NDK and CPP interfaces to the same service;
56  * unification is possible by using ibinder_internals.h.
57  */
58 namespace android::mediautils {
59 
60 enum class ServiceOptions {
61     kNone = 0,
62     kNonNull = (1 << 0),  // don't return a null interface unless disabled.
63                           // partially implemented and experimental.
64 };
65 
66 enum class SkipMode {
67     kNone = 0,       // do not skip the cache (normal behavior for caching services).
68     kImmediate = 1,  // do not cache or find the service, return null to the caller immediately,
69                      // which is the normal behavior for skipping the service cache.
70     kWait = 2,       // do not cache or find the service, but block the caller;
71                      // this is used for cases where a local service override is desired.
72 };
73 
74 // Traits may come through a constexpr static function collection.
75 // This participates in small buffer optimization SBO in std::function impl.
76 template <typename Service>
77 struct DefaultServiceTraits {
78     // getServiceName() returns the name associated with Service.
79     //
80     // If name is empty, it returns the name from the Service descriptor.
81     // If name starts with '/', it appends the name as a version to the Service descriptor,
82     // e.g. "/default".
83     // Otherwise the name is assumed to be the Service name.
getServiceNameDefaultServiceTraits84     static constexpr const char* getServiceName() { return "/default"; }
85 
86     // This callback is called when a new service is received.
87     // The callback requires at least one thread in the Binder threadpool.
onNewServiceDefaultServiceTraits88     static constexpr void onNewService(const InterfaceType<Service>&) {}
89 
90     // This callback is called if the service has died.
91     // The callback requires at least one thread in the Binder threadpool.
onServiceDiedDefaultServiceTraits92     static constexpr void onServiceDied(const InterfaceType<Service>&) {}
93 
94     // ServiceOptions configured for the Service.
optionsDefaultServiceTraits95     static constexpr ServiceOptions options() { return ServiceOptions::kNone; }
96 };
97 
98 // We store the traits as functors.
99 template <typename Service>
100 struct FunctionalServiceTraits {
101     template <typename ServiceTraits>
FunctionalServiceTraitsFunctionalServiceTraits102     explicit FunctionalServiceTraits(const ServiceTraits& serviceTraits)
103         : getServiceName{serviceTraits.getServiceName}
104         , onNewService{serviceTraits.onNewService}
105         , onServiceDied{serviceTraits.onServiceDied}
106         , options{serviceTraits.options} {
107     }
108     std::function<const char*()> getServiceName;
109     std::function<void(const InterfaceType<Service>& service)> onNewService;
110     std::function<void(const InterfaceType<Service>& service)> onServiceDied;
111     std::function<ServiceOptions()> options;
112 };
113 
114 namespace details {
115 
116 class ServiceHandler
117 {
118 public:
119     /**
120      * Returns a ServiceHandler, templated type T is String16 for the native type
121      * of the CPP service descriptors and const char* for the native type of the NDK
122      * service descriptors.
123      */
124     template<typename T>
125     requires (std::is_same_v<T, const char*> || std::is_same_v<T, String16>)
126     static std::shared_ptr<ServiceHandler> getInstance(const T& name);
127 
128     /**
129      * Initializes the service handler with new service traits
130      * (methods that are triggered on service events).
131      *
132      * This is optional.  Default construction of traits is allowed for
133      * services that do not require special handling.
134      *
135      * @param serviceTraits
136      * @return true if the service handler had been previously initialized.
137      */
138     template<typename Service, typename ServiceTraits>
init(const ServiceTraits & serviceTraits)139     bool init(const ServiceTraits& serviceTraits) {
140         auto traits = std::make_shared<FunctionalServiceTraits<Service>>(serviceTraits);
141         std::shared_ptr<void> oldTraits;
142         std::lock_guard l(mMutex);
143         std::swap(oldTraits, mTraits);
144         const bool existing = oldTraits != nullptr;
145         mTraits = std::move(traits);
146         mSkipMode = SkipMode::kNone;
147         return existing;
148     }
149 
150     /**
151      * Returns the service based on a timeout.
152      *
153      * @param waitNs the time to wait, internally clamped to (0, INT64_MAX / 2) to
154      *       avoid numeric overflow.
155      * @param useCallback installs a callback instead of polling.
156      *       the Callback persists if the call timeouts.  A Callback requires
157      *       at least one thread in the threadpool.
158      * @return Service interface.
159      */
160     template <typename Service>
get(std::chrono::nanoseconds waitNs,bool useCallback)161     auto get(std::chrono::nanoseconds waitNs, bool useCallback) {
162         audio_utils::unique_lock ul(mMutex);
163         auto& service = std::get<BaseInterfaceType<Service>>(mService);
164 
165         // early check.
166         if (mSkipMode == SkipMode::kImmediate || (service && mValid)) return service;
167 
168         // clamp to avoid numeric overflow.  INT64_MAX / 2 is effectively forever for a device.
169         std::chrono::nanoseconds kWaitLimitNs(
170                 std::numeric_limits<decltype(waitNs.count())>::max() / 2);
171         waitNs = std::clamp(waitNs, decltype(waitNs)(0), kWaitLimitNs);
172         const auto end = std::chrono::steady_clock::now() + waitNs;
173 
174         for (bool first = true; true; first = false) {
175             // we may have released mMutex, so see if service has been obtained.
176             if (mSkipMode == SkipMode::kImmediate || (service && mValid))  return service;
177 
178             int options = 0;
179             if (mSkipMode == SkipMode::kNone) {
180                 const auto traits = getTraits_l<Service>();
181 
182                 // first time or not using callback, check the service.
183                 if (first || !useCallback) {
184                     auto service_new = checkServicePassThrough<Service>(
185                             traits->getServiceName());
186                     if (service_new) {
187                         mValid = true;
188                         service = std::move(service_new);
189                         // service is a reference, so we copy to service_fixed as
190                         // we're releasing the mutex.
191                         const auto service_fixed = service;
192                         ul.unlock();
193                         traits->onNewService(interfaceFromBase<Service>(service_fixed));
194                         ul.lock();
195                         setDeathNotifier_l<Service>(service_fixed);
196                         ul.unlock();
197                         mCv.notify_all();
198                         return service_fixed;
199                     }
200                 }
201                 // install service callback if needed.
202                 if (useCallback && !mServiceNotificationHandle) {
203                     setServiceNotifier_l<Service>();
204                 }
205                 options = static_cast<int>(traits->options());
206             }
207 
208             // check time expiration.
209             const auto now = std::chrono::steady_clock::now();
210             if (now >= end &&
211                     (service
212                     || mSkipMode != SkipMode::kNone  // skip is set.
213                     || !(options & static_cast<int>(ServiceOptions::kNonNull)))) { // null allowed
214                 return service;
215             }
216 
217             // compute time to wait, then wait.
218             if (mServiceNotificationHandle) {
219                 mCv.wait_until(ul, end);
220             } else {
221                 const auto target = now + kPollTime;
222                 mCv.wait_until(ul, std::min(target, end));
223             }
224             // loop back to see if we have any state change.
225         }
226     }
227 
228     /**
229      * Sets an externally provided service override.
230      *
231      * @param Service
232      * @param service_new
233      */
234     template<typename Service>
set(const InterfaceType<Service> & service_new)235     void set(const InterfaceType<Service>& service_new) {
236         audio_utils::unique_lock ul(mMutex);
237         auto& service = std::get<BaseInterfaceType<Service>>(mService);
238         const auto traits = getTraits_l<Service>();
239         if (service) {
240             auto orig_service = service;
241             invalidateService_l<Service>();
242             ul.unlock();
243             traits->onServiceDied(interfaceFromBase<Service>(orig_service));
244         }
245         service = service_new;
246         ul.unlock();
247         // should we set the death notifier?  It could be a local service.
248         if (service_new) traits->onNewService(service_new);
249         mCv.notify_all();
250     }
251 
252     /**
253      * Disables cache management in the ServiceHandler.  init() needs to be
254      * called to restart.
255      *
256      * All notifiers removed.
257      * Service pointer is released.
258      *
259      * If skipMode is kNone,      then cache management is immediately reenabled.
260      * If skipMode is kImmediate, then any new waiters will return null immediately.
261      * If skipMode is kWait,      then any new waiters will be blocked until an update occurs
262      *                            or the timeout expires.
263      */
264     template<typename Service>
skip(SkipMode skipMode)265     void skip(SkipMode skipMode) {
266         audio_utils::unique_lock ul(mMutex);
267         mSkipMode = skipMode;
268         // remove notifiers.  OK to hold lock as presuming notifications one-way
269         // or manually triggered outside of lock.
270         mDeathNotificationHandle.reset();
271         mServiceNotificationHandle.reset();
272         auto& service = std::get<BaseInterfaceType<Service>>(mService);
273         const auto traits = getTraits_l<Service>();
274         std::shared_ptr<void> oldTraits;
275         std::swap(oldTraits, mTraits);  // destroyed outside of lock.
276         if (service) {
277             auto orig_service = service;  // keep reference to service to manually notify death.
278             invalidateService_l<Service>();  // sets service to nullptr
279             ul.unlock();
280             traits->onServiceDied(interfaceFromBase<Service>(orig_service));
281         } else {
282             ul.unlock();
283         }
284         mCv.notify_all();
285     }
286 
287 private:
288 
289     // invalidateService_l is called to remove the old death notifier,
290     // invalidate the service, and optionally clear the service pointer.
291     template <typename Service>
invalidateService_l()292     void invalidateService_l() REQUIRES(mMutex) {
293         mDeathNotificationHandle.reset();
294         const auto traits = getTraits_l<Service>();
295         mValid = false;
296         if (!(static_cast<int>(traits->options()) & static_cast<int>(ServiceOptions::kNonNull))
297                 || mSkipMode != SkipMode::kNone) {
298             auto &service = std::get<BaseInterfaceType<Service>>(mService);
299             service = nullptr;
300         }
301     }
302 
303     // gets the traits set by init(), initializes with default if init() not called.
304     template <typename Service>
getTraits_l()305     std::shared_ptr<FunctionalServiceTraits<Service>> getTraits_l() REQUIRES(mMutex) {
306         if (!mTraits) {
307             mTraits = std::make_shared<FunctionalServiceTraits<Service>>(
308                     DefaultServiceTraits<Service>{});
309         }
310         return std::static_pointer_cast<FunctionalServiceTraits<Service>>(mTraits);
311     }
312 
313     // sets the service notification
314     template <typename Service>
setServiceNotifier_l()315     void setServiceNotifier_l() REQUIRES(mMutex) {
316         const auto traits = getTraits_l<Service>();
317         mServiceNotificationHandle = requestServiceNotification<Service>(
318                 [traits, this](const InterfaceType<Service>& service) {
319                     audio_utils::unique_lock ul(mMutex);
320                     auto originalService = std::get<BaseInterfaceType<Service>>(mService);
321                     if (originalService != service) {
322                         if (originalService != nullptr) {
323                             invalidateService_l<Service>();
324                         }
325                         mService = service;
326                         mValid = true;
327                         ul.unlock();
328                         if (originalService != nullptr) {
329                             traits->onServiceDied(interfaceFromBase<Service>(originalService));
330                         }
331                         traits->onNewService(service);
332                         ul.lock();
333                         setDeathNotifier_l<Service>(service);
334                     }
335                     ul.unlock();
336                     mCv.notify_all();
337                 }, traits->getServiceName());
338         ALOGW_IF(!mServiceNotificationHandle, "%s: cannot register service notification %s"
339                                               " (do we have permission?)",
340                 __func__, toString(Service::descriptor).c_str());
341     }
342 
343     // sets the death notifier for mService (mService must be non-null).
344     template <typename Service>
setDeathNotifier_l(const BaseInterfaceType<Service> & base)345     void setDeathNotifier_l(const BaseInterfaceType<Service>& base) REQUIRES(mMutex) {
346         if (base != std::get<BaseInterfaceType<Service>>(mService)) {
347             ALOGW("%s: service has changed for %s, skipping death notification registration",
348                     __func__, toString(Service::descriptor).c_str());
349             return;
350         }
351         auto service = interfaceFromBase<Service>(base);
352         const auto binder = binderFromInterface(service);
353         if (binder.get()) {
354             auto traits = getTraits_l<Service>();
355             mDeathNotificationHandle = requestDeathNotification(
356                     base, [traits, service, this]() {
357                         // as only one death notification is dispatched,
358                         // we do not need to generation count.
359                         {
360                             std::lock_guard l(mMutex);
361                             invalidateService_l<Service>();
362                         }
363                         traits->onServiceDied(service);
364                     });
365             // Implementation detail: if the service has already died,
366             // we do not call the death notification, but log the issue here.
367             ALOGW_IF(!mDeathNotificationHandle, "%s: cannot register death notification %s"
368                                                 " (already died?)",
369                     __func__, toString(Service::descriptor).c_str());
370         }
371     }
372 
373     // initializes the variant for NDK use (called on first creation in the cache map).
init_ndk()374     void init_ndk() EXCLUDES(mMutex) {
375         std::lock_guard l(mMutex);
376         mService = std::shared_ptr<::ndk::ICInterface>{};
377     }
378 
379     // initializes the variant for CPP use (called on first creation in the cache map).
init_cpp()380     void init_cpp() EXCLUDES(mMutex) {
381         std::lock_guard l(mMutex);
382         mService = sp<::android::IInterface>{};
383     }
384 
toString(const std::string & s)385     static std::string toString(const std::string& s) { return s; }
toString(const String16 & s)386     static std::string toString(const String16& s) { return String8(s).c_str(); }
387 
388     mutable std::mutex mMutex;
389     std::condition_variable mCv;
390     static constexpr auto kPollTime = std::chrono::seconds(1);
391 
392     std::variant<std::shared_ptr<::ndk::ICInterface>,
393             sp<::android::IInterface>> mService GUARDED_BY(mMutex);
394     // aesthetically we place these last, but a ServiceHandler is never deleted in
395     // current operation, so there is no deadlock on destruction.
396     std::shared_ptr<void> mDeathNotificationHandle GUARDED_BY(mMutex);
397     std::shared_ptr<void> mServiceNotificationHandle GUARDED_BY(mMutex);
398     std::shared_ptr<void> mTraits GUARDED_BY(mMutex);
399 
400     // mValid is true iff the service is non-null and alive.
401     bool mValid GUARDED_BY(mMutex) = false;
402 
403     // mSkipMode indicates the service cache state:
404     //
405     // one may either wait (blocked) until the service is reinitialized.
406     SkipMode mSkipMode GUARDED_BY(mMutex) = SkipMode::kNone;
407 };
408 
409 } // details
410 
411 //----------------------------------
412 // ServiceSingleton API
413 //
414 
415 /*
416  * Implementation detail:
417  *
418  * Each CPP or NDK service interface has a unique ServiceHandler that
419  * is stored in a singleton cache.  The cache key is based on the service descriptor string
420  * so only one version can be chosen.  (The particular version may be changed using
421  * ServiceTraits.getName()).
422  */
423 
424 /**
425  * Sets the service trait parameters for acquiring the Service interface.
426  *
427  * If this is not set before the first service fetch, then default service traits are used.
428  *
429  * @return true if there is a preexisting (including prior default set) traits.
430  */
431 template<typename Service, typename ServiceTraits>
432 bool initService(const ServiceTraits& serviceTraits = {}) {
433     const auto serviceHandler = details::ServiceHandler::getInstance(Service::descriptor);
434     return serviceHandler->template init<Service>(serviceTraits);
435 }
436 
437 /**
438  * Returns either a std::shared_ptr<Interface> or sp<Interface>
439  * for the AIDL service.  If the service is not available within waitNs,
440  * the method will return nullptr
441  * (or the previous invalidated service if Service.options() & kNonNull).
442  *
443  * This method installs a callback to obtain the service, so with waitNs == 0, it may be used to
444  * prefetch the service before it is actually needed.
445  *
446  * @param waitNs wait time for the service to become available.
447  * @return
448  *    a sp<> for a CPP interface
449  *    a std::shared_ptr<> for a NDK interface
450  *
451  */
452 template<typename Service>
453 auto getService(std::chrono::nanoseconds waitNs = {}) {
454     const auto serviceHandler = details::ServiceHandler::getInstance(Service::descriptor);
455     return interfaceFromBase<Service>(serviceHandler->template get<Service>(
456             waitNs, true /* useCallback */));
457 }
458 
459 /**
460  * Returns either a std::shared_ptr<Interface> or sp<Interface>
461  * for the AIDL service.  If the service is not available within waitNs,
462  * the method will return nullptr
463  * (or the previous invalidated service if Service.options() & kNonNull).
464  *
465  * This method polls to obtain the service, which
466  * is useful if the service is restricted due to permissions or
467  * one is concerned about ThreadPool starvation.
468  *
469  * @param waitNs wait time for the service to become available.
470  * @return
471  *    a sp<> for a CPP interface
472  *    a std::shared_ptr<> for a NDK interface
473  */
474 template<typename Service>
475 auto checkService(std::chrono::nanoseconds waitNs = {}) {
476     const auto serviceHandler = details::ServiceHandler::getInstance(Service::descriptor);
477     return interfaceFromBase<Service>(serviceHandler->template get<Service>(
478             waitNs, false /* useCallback */));
479 }
480 
481 /**
482  * Sets a service implementation override, replacing any fetched service from ServiceManager.
483  *
484  * An empty service clears the cache.
485  */
486 template<typename Service>
setService(const InterfaceType<Service> & service)487 void setService(const InterfaceType<Service>& service) {
488     const auto serviceHandler = details::ServiceHandler::getInstance(Service::descriptor);
489     serviceHandler->template set<Service>(service);
490 }
491 
492 /**
493  * Disables the service cache.
494  *
495  * This releases any service and notification callbacks.  After this,
496  * another initService() can be called seamlessly.
497  */
498 template<typename Service>
499 void skipService(SkipMode skipMode = SkipMode::kImmediate) {
500     const auto serviceHandler = details::ServiceHandler::getInstance(Service::descriptor);
501     serviceHandler->template skip<Service>(skipMode);
502 }
503 
504 } // namespace android::mediautils
505