1 /*
2  * Copyright (C) 2013 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 com.android.server;
18 
19 import android.annotation.NonNull;
20 import android.content.Context;
21 import android.os.Environment;
22 import android.os.SystemClock;
23 import android.os.Trace;
24 import android.util.Slog;
25 import android.util.TimingsTraceLog;
26 
27 import java.io.File;
28 import java.lang.reflect.Constructor;
29 import java.lang.reflect.InvocationTargetException;
30 import java.util.ArrayList;
31 
32 /**
33  * Manages creating, starting, and other lifecycle events of
34  * {@link com.android.server.SystemService system services}.
35  *
36  * {@hide}
37  */
38 public class SystemServiceManager {
39     private static final String TAG = "SystemServiceManager";
40     private static final int SERVICE_CALL_WARN_TIME_MS = 50;
41 
42     private static File sSystemDir;
43     private final Context mContext;
44     private boolean mSafeMode;
45     private boolean mRuntimeRestarted;
46     private long mRuntimeStartElapsedTime;
47     private long mRuntimeStartUptime;
48 
49     // Services that should receive lifecycle events.
50     private final ArrayList<SystemService> mServices = new ArrayList<SystemService>();
51 
52     private int mCurrentPhase = -1;
53 
SystemServiceManager(Context context)54     SystemServiceManager(Context context) {
55         mContext = context;
56     }
57 
58     /**
59      * Starts a service by class name.
60      *
61      * @return The service instance.
62      */
63     @SuppressWarnings("unchecked")
startService(String className)64     public SystemService startService(String className) {
65         final Class<SystemService> serviceClass;
66         try {
67             serviceClass = (Class<SystemService>)Class.forName(className);
68         } catch (ClassNotFoundException ex) {
69             Slog.i(TAG, "Starting " + className);
70             throw new RuntimeException("Failed to create service " + className
71                     + ": service class not found, usually indicates that the caller should "
72                     + "have called PackageManager.hasSystemFeature() to check whether the "
73                     + "feature is available on this device before trying to start the "
74                     + "services that implement it", ex);
75         }
76         return startService(serviceClass);
77     }
78 
79     /**
80      * Creates and starts a system service. The class must be a subclass of
81      * {@link com.android.server.SystemService}.
82      *
83      * @param serviceClass A Java class that implements the SystemService interface.
84      * @return The service instance, never null.
85      * @throws RuntimeException if the service fails to start.
86      */
87     @SuppressWarnings("unchecked")
startService(Class<T> serviceClass)88     public <T extends SystemService> T startService(Class<T> serviceClass) {
89         try {
90             final String name = serviceClass.getName();
91             Slog.i(TAG, "Starting " + name);
92             Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "StartService " + name);
93 
94             // Create the service.
95             if (!SystemService.class.isAssignableFrom(serviceClass)) {
96                 throw new RuntimeException("Failed to create " + name
97                         + ": service must extend " + SystemService.class.getName());
98             }
99             final T service;
100             try {
101                 Constructor<T> constructor = serviceClass.getConstructor(Context.class);
102                 service = constructor.newInstance(mContext);
103             } catch (InstantiationException ex) {
104                 throw new RuntimeException("Failed to create service " + name
105                         + ": service could not be instantiated", ex);
106             } catch (IllegalAccessException ex) {
107                 throw new RuntimeException("Failed to create service " + name
108                         + ": service must have a public constructor with a Context argument", ex);
109             } catch (NoSuchMethodException ex) {
110                 throw new RuntimeException("Failed to create service " + name
111                         + ": service must have a public constructor with a Context argument", ex);
112             } catch (InvocationTargetException ex) {
113                 throw new RuntimeException("Failed to create service " + name
114                         + ": service constructor threw an exception", ex);
115             }
116 
117             startService(service);
118             return service;
119         } finally {
120             Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
121         }
122     }
123 
startService(@onNull final SystemService service)124     public void startService(@NonNull final SystemService service) {
125         // Register it.
126         mServices.add(service);
127         // Start it.
128         long time = SystemClock.elapsedRealtime();
129         try {
130             service.onStart();
131         } catch (RuntimeException ex) {
132             throw new RuntimeException("Failed to start service " + service.getClass().getName()
133                     + ": onStart threw an exception", ex);
134         }
135         warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onStart");
136     }
137 
138     /**
139      * Starts the specified boot phase for all system services that have been started up to
140      * this point.
141      *
142      * @param phase The boot phase to start.
143      */
startBootPhase(final int phase)144     public void startBootPhase(final int phase) {
145         if (phase <= mCurrentPhase) {
146             throw new IllegalArgumentException("Next phase must be larger than previous");
147         }
148         mCurrentPhase = phase;
149 
150         Slog.i(TAG, "Starting phase " + mCurrentPhase);
151         try {
152             Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "OnBootPhase " + phase);
153             final int serviceLen = mServices.size();
154             for (int i = 0; i < serviceLen; i++) {
155                 final SystemService service = mServices.get(i);
156                 long time = SystemClock.elapsedRealtime();
157                 Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, service.getClass().getName());
158                 try {
159                     service.onBootPhase(mCurrentPhase);
160                 } catch (Exception ex) {
161                     throw new RuntimeException("Failed to boot service "
162                             + service.getClass().getName()
163                             + ": onBootPhase threw an exception during phase "
164                             + mCurrentPhase, ex);
165                 }
166                 warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onBootPhase");
167                 Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
168             }
169         } finally {
170             Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
171         }
172     }
173 
174     /**
175      * @return true if system has completed the boot; false otherwise.
176      */
isBootCompleted()177     public boolean isBootCompleted() {
178         return mCurrentPhase >= SystemService.PHASE_BOOT_COMPLETED;
179     }
180 
startUser(final int userHandle)181     public void startUser(final int userHandle) {
182         final TimingsTraceLog t = new TimingsTraceLog(TAG, Trace.TRACE_TAG_SYSTEM_SERVER);
183         t.traceBegin("ssm.startUser-" + userHandle);
184         Slog.i(TAG, "Calling onStartUser u" + userHandle);
185         final int serviceLen = mServices.size();
186         for (int i = 0; i < serviceLen; i++) {
187             final SystemService service = mServices.get(i);
188             t.traceBegin("onStartUser-" + userHandle + " " + service.getClass().getName());
189             long time = SystemClock.elapsedRealtime();
190             try {
191                 service.onStartUser(userHandle);
192             } catch (Exception ex) {
193                 Slog.wtf(TAG, "Failure reporting start of user " + userHandle
194                         + " to service " + service.getClass().getName(), ex);
195             }
196             warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onStartUser ");
197             t.traceEnd();
198         }
199         t.traceEnd();
200     }
201 
unlockUser(final int userHandle)202     public void unlockUser(final int userHandle) {
203         final TimingsTraceLog t = new TimingsTraceLog(TAG, Trace.TRACE_TAG_SYSTEM_SERVER);
204         t.traceBegin("ssm.unlockUser-" + userHandle);
205         Slog.i(TAG, "Calling onUnlockUser u" + userHandle);
206         final int serviceLen = mServices.size();
207         for (int i = 0; i < serviceLen; i++) {
208             final SystemService service = mServices.get(i);
209             t.traceBegin("onUnlockUser-" + userHandle + " " + service.getClass().getName());
210             long time = SystemClock.elapsedRealtime();
211             try {
212                 service.onUnlockUser(userHandle);
213             } catch (Exception ex) {
214                 Slog.wtf(TAG, "Failure reporting unlock of user " + userHandle
215                         + " to service " + service.getClass().getName(), ex);
216             }
217             warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onUnlockUser ");
218             t.traceEnd();
219         }
220         t.traceEnd();
221     }
222 
switchUser(final int userHandle)223     public void switchUser(final int userHandle) {
224         final TimingsTraceLog t = new TimingsTraceLog(TAG, Trace.TRACE_TAG_SYSTEM_SERVER);
225         t.traceBegin("ssm.switchUser-" + userHandle);
226         Slog.i(TAG, "Calling switchUser u" + userHandle);
227         final int serviceLen = mServices.size();
228         for (int i = 0; i < serviceLen; i++) {
229             final SystemService service = mServices.get(i);
230             t.traceBegin("onSwitchUser-" + userHandle + " " + service.getClass().getName());
231             long time = SystemClock.elapsedRealtime();
232             try {
233                 service.onSwitchUser(userHandle);
234             } catch (Exception ex) {
235                 Slog.wtf(TAG, "Failure reporting switch of user " + userHandle
236                         + " to service " + service.getClass().getName(), ex);
237             }
238             warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onSwitchUser");
239             t.traceEnd();
240         }
241         t.traceEnd();
242     }
243 
stopUser(final int userHandle)244     public void stopUser(final int userHandle) {
245         final TimingsTraceLog t = new TimingsTraceLog(TAG, Trace.TRACE_TAG_SYSTEM_SERVER);
246         t.traceBegin("ssm.stopUser-" + userHandle);
247         Slog.i(TAG, "Calling onStopUser u" + userHandle);
248         final int serviceLen = mServices.size();
249         for (int i = 0; i < serviceLen; i++) {
250             final SystemService service = mServices.get(i);
251             t.traceBegin("onStopUser-" + userHandle + " " + service.getClass().getName());
252             long time = SystemClock.elapsedRealtime();
253             try {
254                 service.onStopUser(userHandle);
255             } catch (Exception ex) {
256                 Slog.wtf(TAG, "Failure reporting stop of user " + userHandle
257                         + " to service " + service.getClass().getName(), ex);
258             }
259             warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onStopUser");
260             t.traceEnd();
261         }
262         t.traceEnd();
263     }
264 
cleanupUser(final int userHandle)265     public void cleanupUser(final int userHandle) {
266         final TimingsTraceLog t = new TimingsTraceLog(TAG, Trace.TRACE_TAG_SYSTEM_SERVER);
267         t.traceBegin("ssm.cleanupUser-" + userHandle);
268         Slog.i(TAG, "Calling onCleanupUser u" + userHandle);
269         final int serviceLen = mServices.size();
270         for (int i = 0; i < serviceLen; i++) {
271             final SystemService service = mServices.get(i);
272             t.traceBegin("onCleanupUser-" + userHandle + " " + service.getClass().getName());
273             long time = SystemClock.elapsedRealtime();
274             try {
275                 service.onCleanupUser(userHandle);
276             } catch (Exception ex) {
277                 Slog.wtf(TAG, "Failure reporting cleanup of user " + userHandle
278                         + " to service " + service.getClass().getName(), ex);
279             }
280             warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onCleanupUser");
281             t.traceEnd();
282         }
283         t.traceEnd();
284     }
285 
286     /** Sets the safe mode flag for services to query. */
setSafeMode(boolean safeMode)287     void setSafeMode(boolean safeMode) {
288         mSafeMode = safeMode;
289     }
290 
291     /**
292      * Returns whether we are booting into safe mode.
293      * @return safe mode flag
294      */
isSafeMode()295     public boolean isSafeMode() {
296         return mSafeMode;
297     }
298 
299     /**
300      * @return true if runtime was restarted, false if it's normal boot
301      */
isRuntimeRestarted()302     public boolean isRuntimeRestarted() {
303         return mRuntimeRestarted;
304     }
305 
306     /**
307      * @return Time when SystemServer was started, in elapsed realtime.
308      */
getRuntimeStartElapsedTime()309     public long getRuntimeStartElapsedTime() {
310         return mRuntimeStartElapsedTime;
311     }
312 
313     /**
314      * @return Time when SystemServer was started, in uptime.
315      */
getRuntimeStartUptime()316     public long getRuntimeStartUptime() {
317         return mRuntimeStartUptime;
318     }
319 
setStartInfo(boolean runtimeRestarted, long runtimeStartElapsedTime, long runtimeStartUptime)320     void setStartInfo(boolean runtimeRestarted,
321             long runtimeStartElapsedTime, long runtimeStartUptime) {
322         mRuntimeRestarted = runtimeRestarted;
323         mRuntimeStartElapsedTime = runtimeStartElapsedTime;
324         mRuntimeStartUptime = runtimeStartUptime;
325     }
326 
warnIfTooLong(long duration, SystemService service, String operation)327     private void warnIfTooLong(long duration, SystemService service, String operation) {
328         if (duration > SERVICE_CALL_WARN_TIME_MS) {
329             Slog.w(TAG, "Service " + service.getClass().getName() + " took " + duration + " ms in "
330                     + operation);
331         }
332     }
333 
334     /**
335      * Ensures that the system directory exist creating one if needed.
336      * @deprecated Use {@link Environment#getDataSystemCeDirectory()}
337      * or {@link Environment#getDataSystemDeDirectory()} instead.
338      * @return The system directory.
339      */
340     @Deprecated
ensureSystemDir()341     public static File ensureSystemDir() {
342         if (sSystemDir == null) {
343             File dataDir = Environment.getDataDirectory();
344             sSystemDir = new File(dataDir, "system");
345             sSystemDir.mkdirs();
346         }
347         return sSystemDir;
348     }
349 
350     /**
351      * Outputs the state of this manager to the System log.
352      */
dump()353     public void dump() {
354         StringBuilder builder = new StringBuilder();
355         builder.append("Current phase: ").append(mCurrentPhase).append("\n");
356         builder.append("Services:\n");
357         final int startedLen = mServices.size();
358         for (int i = 0; i < startedLen; i++) {
359             final SystemService service = mServices.get(i);
360             builder.append("\t")
361                     .append(service.getClass().getSimpleName())
362                     .append("\n");
363         }
364 
365         Slog.e(TAG, builder.toString());
366     }
367 }
368