1 /*
2  * Copyright (C) 2007 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 package android.os;
18 
19 import android.compat.annotation.UnsupportedAppUsage;
20 import android.util.ArrayMap;
21 import android.util.Log;
22 
23 import com.android.internal.annotations.GuardedBy;
24 import com.android.internal.os.BinderInternal;
25 import com.android.internal.util.StatLogger;
26 
27 import java.util.Map;
28 
29 /** @hide */
30 public final class ServiceManager {
31     private static final String TAG = "ServiceManager";
32     private static final Object sLock = new Object();
33 
34     @UnsupportedAppUsage
35     private static IServiceManager sServiceManager;
36 
37     /**
38      * Cache for the "well known" services, such as WM and AM.
39      */
40     @UnsupportedAppUsage
41     private static Map<String, IBinder> sCache = new ArrayMap<String, IBinder>();
42 
43     /**
44      * We do the "slow log" at most once every this interval.
45      */
46     private static final int SLOW_LOG_INTERVAL_MS = 5000;
47 
48     /**
49      * We do the "stats log" at most once every this interval.
50      */
51     private static final int STATS_LOG_INTERVAL_MS = 5000;
52 
53     /**
54      * Threshold in uS for a "slow" call, used on core UIDs. We use a more relax value to
55      * avoid logspam.
56      */
57     private static final long GET_SERVICE_SLOW_THRESHOLD_US_CORE =
58             SystemProperties.getInt("debug.servicemanager.slow_call_core_ms", 10) * 1000;
59 
60     /**
61      * Threshold in uS for a "slow" call, used on non-core UIDs. We use a more relax value to
62      * avoid logspam.
63      */
64     private static final long GET_SERVICE_SLOW_THRESHOLD_US_NON_CORE =
65             SystemProperties.getInt("debug.servicemanager.slow_call_ms", 50) * 1000;
66 
67     /**
68      * We log stats logging ever this many getService() calls.
69      */
70     private static final int GET_SERVICE_LOG_EVERY_CALLS_CORE =
71             SystemProperties.getInt("debug.servicemanager.log_calls_core", 100);
72 
73     /**
74      * We log stats logging ever this many getService() calls.
75      */
76     private static final int GET_SERVICE_LOG_EVERY_CALLS_NON_CORE =
77             SystemProperties.getInt("debug.servicemanager.log_calls", 200);
78 
79     @GuardedBy("sLock")
80     private static int sGetServiceAccumulatedUs;
81 
82     @GuardedBy("sLock")
83     private static int sGetServiceAccumulatedCallCount;
84 
85     @GuardedBy("sLock")
86     private static long sLastStatsLogUptime;
87 
88     @GuardedBy("sLock")
89     private static long sLastSlowLogUptime;
90 
91     @GuardedBy("sLock")
92     private static long sLastSlowLogActualTime;
93 
94     interface Stats {
95         int GET_SERVICE = 0;
96 
97         int COUNT = GET_SERVICE + 1;
98     }
99 
100     public static final StatLogger sStatLogger = new StatLogger(new String[] {
101             "getService()",
102     });
103 
104     @UnsupportedAppUsage
ServiceManager()105     public ServiceManager() {
106     }
107 
108     @UnsupportedAppUsage
getIServiceManager()109     private static IServiceManager getIServiceManager() {
110         if (sServiceManager != null) {
111             return sServiceManager;
112         }
113 
114         // Find the service manager
115         sServiceManager = ServiceManagerNative
116                 .asInterface(Binder.allowBlocking(BinderInternal.getContextObject()));
117         return sServiceManager;
118     }
119 
120     /**
121      * Returns a reference to a service with the given name.
122      *
123      * @param name the name of the service to get
124      * @return a reference to the service, or <code>null</code> if the service doesn't exist
125      */
126     @UnsupportedAppUsage
getService(String name)127     public static IBinder getService(String name) {
128         try {
129             IBinder service = sCache.get(name);
130             if (service != null) {
131                 return service;
132             } else {
133                 return Binder.allowBlocking(rawGetService(name));
134             }
135         } catch (RemoteException e) {
136             Log.e(TAG, "error in getService", e);
137         }
138         return null;
139     }
140 
141     /**
142      * Returns a reference to a service with the given name, or throws
143      * {@link NullPointerException} if none is found.
144      *
145      * @hide
146      */
getServiceOrThrow(String name)147     public static IBinder getServiceOrThrow(String name) throws ServiceNotFoundException {
148         final IBinder binder = getService(name);
149         if (binder != null) {
150             return binder;
151         } else {
152             throw new ServiceNotFoundException(name);
153         }
154     }
155 
156     /**
157      * Place a new @a service called @a name into the service
158      * manager.
159      *
160      * @param name the name of the new service
161      * @param service the service object
162      */
163     @UnsupportedAppUsage
addService(String name, IBinder service)164     public static void addService(String name, IBinder service) {
165         addService(name, service, false, IServiceManager.DUMP_FLAG_PRIORITY_DEFAULT);
166     }
167 
168     /**
169      * Place a new @a service called @a name into the service
170      * manager.
171      *
172      * @param name the name of the new service
173      * @param service the service object
174      * @param allowIsolated set to true to allow isolated sandboxed processes
175      * to access this service
176      */
177     @UnsupportedAppUsage
addService(String name, IBinder service, boolean allowIsolated)178     public static void addService(String name, IBinder service, boolean allowIsolated) {
179         addService(name, service, allowIsolated, IServiceManager.DUMP_FLAG_PRIORITY_DEFAULT);
180     }
181 
182     /**
183      * Place a new @a service called @a name into the service
184      * manager.
185      *
186      * @param name the name of the new service
187      * @param service the service object
188      * @param allowIsolated set to true to allow isolated sandboxed processes
189      * @param dumpPriority supported dump priority levels as a bitmask
190      * to access this service
191      */
192     @UnsupportedAppUsage
addService(String name, IBinder service, boolean allowIsolated, int dumpPriority)193     public static void addService(String name, IBinder service, boolean allowIsolated,
194             int dumpPriority) {
195         try {
196             getIServiceManager().addService(name, service, allowIsolated, dumpPriority);
197         } catch (RemoteException e) {
198             Log.e(TAG, "error in addService", e);
199         }
200     }
201 
202     /**
203      * Retrieve an existing service called @a name from the
204      * service manager.  Non-blocking.
205      */
206     @UnsupportedAppUsage
checkService(String name)207     public static IBinder checkService(String name) {
208         try {
209             IBinder service = sCache.get(name);
210             if (service != null) {
211                 return service;
212             } else {
213                 return Binder.allowBlocking(getIServiceManager().checkService(name));
214             }
215         } catch (RemoteException e) {
216             Log.e(TAG, "error in checkService", e);
217             return null;
218         }
219     }
220 
221     /**
222      * Return a list of all currently running services.
223      * @return an array of all currently running services, or <code>null</code> in
224      * case of an exception
225      */
226     @UnsupportedAppUsage
listServices()227     public static String[] listServices() {
228         try {
229             return getIServiceManager().listServices(IServiceManager.DUMP_FLAG_PRIORITY_ALL);
230         } catch (RemoteException e) {
231             Log.e(TAG, "error in listServices", e);
232             return null;
233         }
234     }
235 
236     /**
237      * This is only intended to be called when the process is first being brought
238      * up and bound by the activity manager. There is only one thread in the process
239      * at that time, so no locking is done.
240      *
241      * @param cache the cache of service references
242      * @hide
243      */
initServiceCache(Map<String, IBinder> cache)244     public static void initServiceCache(Map<String, IBinder> cache) {
245         if (sCache.size() != 0) {
246             throw new IllegalStateException("setServiceCache may only be called once");
247         }
248         sCache.putAll(cache);
249     }
250 
251     /**
252      * Exception thrown when no service published for given name. This might be
253      * thrown early during boot before certain services have published
254      * themselves.
255      *
256      * @hide
257      */
258     public static class ServiceNotFoundException extends Exception {
ServiceNotFoundException(String name)259         public ServiceNotFoundException(String name) {
260             super("No service published for: " + name);
261         }
262     }
263 
rawGetService(String name)264     private static IBinder rawGetService(String name) throws RemoteException {
265         final long start = sStatLogger.getTime();
266 
267         final IBinder binder = getIServiceManager().getService(name);
268 
269         final int time = (int) sStatLogger.logDurationStat(Stats.GET_SERVICE, start);
270 
271         final int myUid = Process.myUid();
272         final boolean isCore = UserHandle.isCore(myUid);
273 
274         final long slowThreshold = isCore
275                 ? GET_SERVICE_SLOW_THRESHOLD_US_CORE
276                 : GET_SERVICE_SLOW_THRESHOLD_US_NON_CORE;
277 
278         synchronized (sLock) {
279             sGetServiceAccumulatedUs += time;
280             sGetServiceAccumulatedCallCount++;
281 
282             final long nowUptime = SystemClock.uptimeMillis();
283 
284             // Was a slow call?
285             if (time >= slowThreshold) {
286                 // We do a slow log:
287                 // - At most once in every SLOW_LOG_INTERVAL_MS
288                 // - OR it was slower than the previously logged slow call.
289                 if ((nowUptime > (sLastSlowLogUptime + SLOW_LOG_INTERVAL_MS))
290                         || (sLastSlowLogActualTime < time)) {
291                     EventLogTags.writeServiceManagerSlow(time / 1000, name);
292 
293                     sLastSlowLogUptime = nowUptime;
294                     sLastSlowLogActualTime = time;
295                 }
296             }
297 
298             // Every GET_SERVICE_LOG_EVERY_CALLS calls, log the total time spent in getService().
299 
300             final int logInterval = isCore
301                     ? GET_SERVICE_LOG_EVERY_CALLS_CORE
302                     : GET_SERVICE_LOG_EVERY_CALLS_NON_CORE;
303 
304             if ((sGetServiceAccumulatedCallCount >= logInterval)
305                     && (nowUptime >= (sLastStatsLogUptime + STATS_LOG_INTERVAL_MS))) {
306 
307                 EventLogTags.writeServiceManagerStats(
308                         sGetServiceAccumulatedCallCount, // Total # of getService() calls.
309                         sGetServiceAccumulatedUs / 1000, // Total time spent in getService() calls.
310                         (int) (nowUptime - sLastStatsLogUptime)); // Uptime duration since last log.
311                 sGetServiceAccumulatedCallCount = 0;
312                 sGetServiceAccumulatedUs = 0;
313                 sLastStatsLogUptime = nowUptime;
314             }
315         }
316         return binder;
317     }
318 }
319