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