480 lines
16 KiB
Java
480 lines
16 KiB
Java
/*
|
|
* 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<String, IBinder> sCache = new ArrayMap<String, IBinder>();
|
|
|
|
@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<String, IBinder> 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 <code>null</code> 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 <code>null</code> 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<String, IBinder> 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;
|
|
}
|
|
}
|