/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.os; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.compat.annotation.UnsupportedAppUsage; import android.util.ArrayMap; import android.util.Log; import com.android.internal.annotations.GuardedBy; import com.android.internal.os.BinderInternal; import com.android.internal.util.Preconditions; import com.android.internal.util.StatLogger; import java.util.Map; /** * Manage binder services as registered with the binder context manager. These services must be * declared statically on an Android device (SELinux access_vector service_manager, w/ service * names in service_contexts files), and they do not follow the activity lifecycle. When * building applications, android.app.Service should be preferred. * * @hide **/ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) @android.ravenwood.annotation.RavenwoodKeepPartialClass public final class ServiceManager { private static final String TAG = "ServiceManager"; private static final Object sLock = new Object(); @UnsupportedAppUsage private static IServiceManager sServiceManager; /** * Cache for the "well known" services, such as WM and AM. */ // NOTE: this cache is designed to be populated exactly once at process // start to avoid any overhead from locking @UnsupportedAppUsage private static Map sCache = new ArrayMap(); @GuardedBy("ServiceManager.class") // NOTE: this cache is designed to support mutation by tests, so we require // a lock to be held for all accesses private static Map sCache$ravenwood; /** * We do the "slow log" at most once every this interval. */ private static final int SLOW_LOG_INTERVAL_MS = 5000; /** * We do the "stats log" at most once every this interval. */ private static final int STATS_LOG_INTERVAL_MS = 5000; /** * Threshold in uS for a "slow" call, used on core UIDs. We use a more relax value to * avoid logspam. */ private static final long GET_SERVICE_SLOW_THRESHOLD_US_CORE = SystemProperties.getInt("debug.servicemanager.slow_call_core_ms", 10) * 1000; /** * Threshold in uS for a "slow" call, used on non-core UIDs. We use a more relax value to * avoid logspam. */ private static final long GET_SERVICE_SLOW_THRESHOLD_US_NON_CORE = SystemProperties.getInt("debug.servicemanager.slow_call_ms", 50) * 1000; /** * We log stats logging ever this many getService() calls. */ private static final int GET_SERVICE_LOG_EVERY_CALLS_CORE = SystemProperties.getInt("debug.servicemanager.log_calls_core", 100); /** * We log stats logging ever this many getService() calls. */ private static final int GET_SERVICE_LOG_EVERY_CALLS_NON_CORE = SystemProperties.getInt("debug.servicemanager.log_calls", 200); @GuardedBy("sLock") private static int sGetServiceAccumulatedUs; @GuardedBy("sLock") private static int sGetServiceAccumulatedCallCount; @GuardedBy("sLock") private static long sLastStatsLogUptime; @GuardedBy("sLock") private static long sLastSlowLogUptime; @GuardedBy("sLock") private static long sLastSlowLogActualTime; interface Stats { int GET_SERVICE = 0; int COUNT = GET_SERVICE + 1; } /** @hide */ public static final StatLogger sStatLogger = new StatLogger(new String[] { "getService()", }); /** @hide */ @UnsupportedAppUsage @android.ravenwood.annotation.RavenwoodKeep public ServiceManager() { } /** @hide */ @android.ravenwood.annotation.RavenwoodKeep public static void init$ravenwood() { synchronized (ServiceManager.class) { sCache$ravenwood = new ArrayMap<>(); } } /** @hide */ @android.ravenwood.annotation.RavenwoodKeep public static void reset$ravenwood() { synchronized (ServiceManager.class) { sCache$ravenwood.clear(); sCache$ravenwood = null; } } @UnsupportedAppUsage private static IServiceManager getIServiceManager() { if (sServiceManager != null) { return sServiceManager; } // Find the service manager sServiceManager = ServiceManagerNative .asInterface(Binder.allowBlocking(BinderInternal.getContextObject())); return sServiceManager; } /** * Returns a reference to a service with the given name. * * @param name the name of the service to get * @return a reference to the service, or null if the service doesn't exist * @hide */ @UnsupportedAppUsage @android.ravenwood.annotation.RavenwoodReplace public static IBinder getService(String name) { try { IBinder service = sCache.get(name); if (service != null) { return service; } else { return Binder.allowBlocking(rawGetService(name)); } } catch (RemoteException e) { Log.e(TAG, "error in getService", e); } return null; } /** @hide */ public static IBinder getService$ravenwood(String name) { synchronized (ServiceManager.class) { // Ravenwood is a single-process environment, so it only needs to store locally return Preconditions.requireNonNullViaRavenwoodRule(sCache$ravenwood).get(name); } } /** * Returns a reference to a service with the given name, or throws * {@link ServiceNotFoundException} if none is found. * * @hide */ @android.ravenwood.annotation.RavenwoodKeep public static IBinder getServiceOrThrow(String name) throws ServiceNotFoundException { final IBinder binder = getService(name); if (binder != null) { return binder; } else { throw new ServiceNotFoundException(name); } } /** * Place a new @a service called @a name into the service * manager. * * @param name the name of the new service * @param service the service object * @hide */ @UnsupportedAppUsage @android.ravenwood.annotation.RavenwoodKeep public static void addService(String name, IBinder service) { addService(name, service, false, IServiceManager.DUMP_FLAG_PRIORITY_DEFAULT); } /** * Place a new @a service called @a name into the service * manager. * * @param name the name of the new service * @param service the service object * @param allowIsolated set to true to allow isolated sandboxed processes * to access this service * @hide */ @UnsupportedAppUsage @android.ravenwood.annotation.RavenwoodKeep public static void addService(String name, IBinder service, boolean allowIsolated) { addService(name, service, allowIsolated, IServiceManager.DUMP_FLAG_PRIORITY_DEFAULT); } /** * Place a new @a service called @a name into the service * manager. * * @param name the name of the new service * @param service the service object * @param allowIsolated set to true to allow isolated sandboxed processes * @param dumpPriority supported dump priority levels as a bitmask * to access this service * @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @android.ravenwood.annotation.RavenwoodReplace public static void addService(String name, IBinder service, boolean allowIsolated, int dumpPriority) { try { getIServiceManager().addService(name, service, allowIsolated, dumpPriority); } catch (RemoteException e) { Log.e(TAG, "error in addService", e); } } /** @hide */ public static void addService$ravenwood(String name, IBinder service, boolean allowIsolated, int dumpPriority) { synchronized (ServiceManager.class) { // Ravenwood is a single-process environment, so it only needs to store locally Preconditions.requireNonNullViaRavenwoodRule(sCache$ravenwood).put(name, service); } } /** * Retrieve an existing service called @a name from the * service manager. Non-blocking. * @hide */ @UnsupportedAppUsage public static IBinder checkService(String name) { try { IBinder service = sCache.get(name); if (service != null) { return service; } else { return Binder.allowBlocking(getIServiceManager().checkService(name)); } } catch (RemoteException e) { Log.e(TAG, "error in checkService", e); return null; } } /** * Returns whether the specified service is declared. * * @return true if the service is declared somewhere (eg. VINTF manifest) and * waitForService should always be able to return the service. */ public static boolean isDeclared(@NonNull String name) { try { return getIServiceManager().isDeclared(name); } catch (RemoteException | SecurityException e) { Log.e(TAG, "error in isDeclared", e); return false; } } /** * Returns an array of all declared instances for a particular interface. * * For instance, if 'android.foo.IFoo/foo' is declared (e.g. in VINTF * manifest), and 'android.foo.IFoo' is passed here, then ["foo"] would be * returned. * * @hide */ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) @NonNull public static String[] getDeclaredInstances(@NonNull String iface) { try { return getIServiceManager().getDeclaredInstances(iface); } catch (RemoteException e) { Log.e(TAG, "error in getDeclaredInstances", e); throw e.rethrowFromSystemServer(); } } /** * Returns the specified service from the service manager. * * If the service is not running, servicemanager will attempt to start it, and this function * will wait for it to be ready. * * @return {@code null} only if there are permission problems or fatal errors. * @hide */ public static IBinder waitForService(@NonNull String name) { return Binder.allowBlocking(waitForServiceNative(name)); } private static native IBinder waitForServiceNative(@NonNull String name); /** * Returns the specified service from the service manager, if declared. * * If the service is not running, servicemanager will attempt to start it, and this function * will wait for it to be ready. * * @throws SecurityException if the process does not have the permissions to check * isDeclared() for the service. * @return {@code null} if the service is not declared in the manifest, or if there * are fatal errors. * @hide */ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) @Nullable public static IBinder waitForDeclaredService(@NonNull String name) { return isDeclared(name) ? waitForService(name) : null; } /** * Register callback for service registration notifications. * * @throws RemoteException for underlying error. * @hide */ public static void registerForNotifications( @NonNull String name, @NonNull IServiceCallback callback) throws RemoteException { getIServiceManager().registerForNotifications(name, callback); } /** * Return a list of all currently running services. * @return an array of all currently running services, or null in * case of an exception * @hide */ @UnsupportedAppUsage public static String[] listServices() { try { return getIServiceManager().listServices(IServiceManager.DUMP_FLAG_PRIORITY_ALL); } catch (RemoteException e) { Log.e(TAG, "error in listServices", e); return null; } } /** * Get service debug info. * @return an array of information for each service (like listServices, but with PIDs) * @hide */ public static ServiceDebugInfo[] getServiceDebugInfo() { try { return getIServiceManager().getServiceDebugInfo(); } catch (RemoteException e) { Log.e(TAG, "error in getServiceDebugInfo", e); return null; } } /** * This is only intended to be called when the process is first being brought * up and bound by the activity manager. There is only one thread in the process * at that time, so no locking is done. * * @param cache the cache of service references * @hide */ public static void initServiceCache(Map cache) { if (sCache.size() != 0) { throw new IllegalStateException("setServiceCache may only be called once"); } sCache.putAll(cache); } /** * Exception thrown when no service published for given name. This might be * thrown early during boot before certain services have published * themselves. * * @hide */ @android.ravenwood.annotation.RavenwoodKeepWholeClass public static class ServiceNotFoundException extends Exception { public ServiceNotFoundException(String name) { super("No service published for: " + name); } } private static IBinder rawGetService(String name) throws RemoteException { final long start = sStatLogger.getTime(); final IBinder binder = getIServiceManager().getService(name); final int time = (int) sStatLogger.logDurationStat(Stats.GET_SERVICE, start); final int myUid = Process.myUid(); final boolean isCore = UserHandle.isCore(myUid); final long slowThreshold = isCore ? GET_SERVICE_SLOW_THRESHOLD_US_CORE : GET_SERVICE_SLOW_THRESHOLD_US_NON_CORE; synchronized (sLock) { sGetServiceAccumulatedUs += time; sGetServiceAccumulatedCallCount++; final long nowUptime = SystemClock.uptimeMillis(); // Was a slow call? if (time >= slowThreshold) { // We do a slow log: // - At most once in every SLOW_LOG_INTERVAL_MS // - OR it was slower than the previously logged slow call. if ((nowUptime > (sLastSlowLogUptime + SLOW_LOG_INTERVAL_MS)) || (sLastSlowLogActualTime < time)) { EventLogTags.writeServiceManagerSlow(time / 1000, name); sLastSlowLogUptime = nowUptime; sLastSlowLogActualTime = time; } } // Every GET_SERVICE_LOG_EVERY_CALLS calls, log the total time spent in getService(). final int logInterval = isCore ? GET_SERVICE_LOG_EVERY_CALLS_CORE : GET_SERVICE_LOG_EVERY_CALLS_NON_CORE; if ((sGetServiceAccumulatedCallCount >= logInterval) && (nowUptime >= (sLastStatsLogUptime + STATS_LOG_INTERVAL_MS))) { EventLogTags.writeServiceManagerStats( sGetServiceAccumulatedCallCount, // Total # of getService() calls. sGetServiceAccumulatedUs / 1000, // Total time spent in getService() calls. (int) (nowUptime - sLastStatsLogUptime)); // Uptime duration since last log. sGetServiceAccumulatedCallCount = 0; sGetServiceAccumulatedUs = 0; sLastStatsLogUptime = nowUptime; } } return binder; } }