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.annotation.UserIdInt;
21 import android.content.Context;
22 import android.content.pm.UserInfo;
23 import android.os.Environment;
24 import android.os.SystemClock;
25 import android.os.Trace;
26 import android.os.UserHandle;
27 import android.os.UserManagerInternal;
28 import android.util.ArrayMap;
29 import android.util.Slog;
30 
31 import com.android.server.SystemService.TargetUser;
32 import com.android.server.utils.TimingsTraceAndSlog;
33 
34 import dalvik.system.PathClassLoader;
35 
36 import java.io.File;
37 import java.lang.reflect.Constructor;
38 import java.lang.reflect.InvocationTargetException;
39 import java.util.ArrayList;
40 
41 /**
42  * Manages creating, starting, and other lifecycle events of
43  * {@link com.android.server.SystemService system services}.
44  *
45  * {@hide}
46  */
47 public class SystemServiceManager {
48     private static final String TAG = "SystemServiceManager";
49     private static final boolean DEBUG = false;
50     private static final int SERVICE_CALL_WARN_TIME_MS = 50;
51 
52     // Constants used on onUser(...)
53     private static final String START = "Start";
54     private static final String UNLOCKING = "Unlocking";
55     private static final String UNLOCKED = "Unlocked";
56     private static final String SWITCH = "Switch";
57     private static final String STOP = "Stop";
58     private static final String CLEANUP = "Cleanup";
59 
60     private static File sSystemDir;
61     private final Context mContext;
62     private boolean mSafeMode;
63     private boolean mRuntimeRestarted;
64     private long mRuntimeStartElapsedTime;
65     private long mRuntimeStartUptime;
66 
67     // Services that should receive lifecycle events.
68     private final ArrayList<SystemService> mServices = new ArrayList<SystemService>();
69 
70     // Map of paths to PathClassLoader, so we don't load the same path multiple times.
71     private final ArrayMap<String, PathClassLoader> mLoadedPaths = new ArrayMap<>();
72 
73     private int mCurrentPhase = -1;
74 
75     private UserManagerInternal mUserManagerInternal;
76 
SystemServiceManager(Context context)77     SystemServiceManager(Context context) {
78         mContext = context;
79     }
80 
81     /**
82      * Starts a service by class name.
83      *
84      * @return The service instance.
85      */
startService(String className)86     public SystemService startService(String className) {
87         final Class<SystemService> serviceClass = loadClassFromLoader(className,
88                 this.getClass().getClassLoader());
89         return startService(serviceClass);
90     }
91 
92     /**
93      * Starts a service by class name and a path that specifies the jar where the service lives.
94      *
95      * @return The service instance.
96      */
startServiceFromJar(String className, String path)97     public SystemService startServiceFromJar(String className, String path) {
98         PathClassLoader pathClassLoader = mLoadedPaths.get(path);
99         if (pathClassLoader == null) {
100             // NB: the parent class loader should always be the system server class loader.
101             // Changing it has implications that require discussion with the mainline team.
102             pathClassLoader = new PathClassLoader(path, this.getClass().getClassLoader());
103             mLoadedPaths.put(path, pathClassLoader);
104         }
105         final Class<SystemService> serviceClass = loadClassFromLoader(className, pathClassLoader);
106         return startService(serviceClass);
107     }
108 
109     /*
110      * Loads and initializes a class from the given classLoader. Returns the class.
111      */
112     @SuppressWarnings("unchecked")
loadClassFromLoader(String className, ClassLoader classLoader)113     private static Class<SystemService> loadClassFromLoader(String className,
114             ClassLoader classLoader) {
115         try {
116             return (Class<SystemService>) Class.forName(className, true, classLoader);
117         } catch (ClassNotFoundException ex) {
118             throw new RuntimeException("Failed to create service " + className
119                     + " from class loader " + classLoader.toString() + ": service class not "
120                     + "found, usually indicates that the caller should "
121                     + "have called PackageManager.hasSystemFeature() to check whether the "
122                     + "feature is available on this device before trying to start the "
123                     + "services that implement it. Also ensure that the correct path for the "
124                     + "classloader is supplied, if applicable.", ex);
125         }
126     }
127 
128     /**
129      * Creates and starts a system service. The class must be a subclass of
130      * {@link com.android.server.SystemService}.
131      *
132      * @param serviceClass A Java class that implements the SystemService interface.
133      * @return The service instance, never null.
134      * @throws RuntimeException if the service fails to start.
135      */
startService(Class<T> serviceClass)136     public <T extends SystemService> T startService(Class<T> serviceClass) {
137         try {
138             final String name = serviceClass.getName();
139             Slog.i(TAG, "Starting " + name);
140             Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "StartService " + name);
141 
142             // Create the service.
143             if (!SystemService.class.isAssignableFrom(serviceClass)) {
144                 throw new RuntimeException("Failed to create " + name
145                         + ": service must extend " + SystemService.class.getName());
146             }
147             final T service;
148             try {
149                 Constructor<T> constructor = serviceClass.getConstructor(Context.class);
150                 service = constructor.newInstance(mContext);
151             } catch (InstantiationException ex) {
152                 throw new RuntimeException("Failed to create service " + name
153                         + ": service could not be instantiated", ex);
154             } catch (IllegalAccessException ex) {
155                 throw new RuntimeException("Failed to create service " + name
156                         + ": service must have a public constructor with a Context argument", ex);
157             } catch (NoSuchMethodException ex) {
158                 throw new RuntimeException("Failed to create service " + name
159                         + ": service must have a public constructor with a Context argument", ex);
160             } catch (InvocationTargetException ex) {
161                 throw new RuntimeException("Failed to create service " + name
162                         + ": service constructor threw an exception", ex);
163             }
164 
165             startService(service);
166             return service;
167         } finally {
168             Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
169         }
170     }
171 
startService(@onNull final SystemService service)172     public void startService(@NonNull final SystemService service) {
173         // Register it.
174         mServices.add(service);
175         // Start it.
176         long time = SystemClock.elapsedRealtime();
177         try {
178             service.onStart();
179         } catch (RuntimeException ex) {
180             throw new RuntimeException("Failed to start service " + service.getClass().getName()
181                     + ": onStart threw an exception", ex);
182         }
183         warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onStart");
184     }
185 
186     /**
187      * Starts the specified boot phase for all system services that have been started up to
188      * this point.
189      *
190      * @param t trace logger
191      * @param phase The boot phase to start.
192      */
startBootPhase(@onNull TimingsTraceAndSlog t, int phase)193     public void startBootPhase(@NonNull TimingsTraceAndSlog t, int phase) {
194         if (phase <= mCurrentPhase) {
195             throw new IllegalArgumentException("Next phase must be larger than previous");
196         }
197         mCurrentPhase = phase;
198 
199         Slog.i(TAG, "Starting phase " + mCurrentPhase);
200         try {
201             t.traceBegin("OnBootPhase_" + phase);
202             final int serviceLen = mServices.size();
203             for (int i = 0; i < serviceLen; i++) {
204                 final SystemService service = mServices.get(i);
205                 long time = SystemClock.elapsedRealtime();
206                 t.traceBegin("OnBootPhase_" + phase + "_" + service.getClass().getName());
207                 try {
208                     service.onBootPhase(mCurrentPhase);
209                 } catch (Exception ex) {
210                     throw new RuntimeException("Failed to boot service "
211                             + service.getClass().getName()
212                             + ": onBootPhase threw an exception during phase "
213                             + mCurrentPhase, ex);
214                 }
215                 warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onBootPhase");
216                 t.traceEnd();
217             }
218         } finally {
219             t.traceEnd();
220         }
221 
222         if (phase == SystemService.PHASE_BOOT_COMPLETED) {
223             final long totalBootTime = SystemClock.uptimeMillis() - mRuntimeStartUptime;
224             t.logDuration("TotalBootTime", totalBootTime);
225             SystemServerInitThreadPool.shutdown();
226         }
227     }
228 
229     /**
230      * @return true if system has completed the boot; false otherwise.
231      */
isBootCompleted()232     public boolean isBootCompleted() {
233         return mCurrentPhase >= SystemService.PHASE_BOOT_COMPLETED;
234     }
235 
236     /**
237      * Called at the beginning of {@code ActivityManagerService.systemReady()}.
238      */
preSystemReady()239     public void preSystemReady() {
240         mUserManagerInternal = LocalServices.getService(UserManagerInternal.class);
241     }
242 
getUserInfo(@serIdInt int userHandle)243     private @NonNull UserInfo getUserInfo(@UserIdInt int userHandle) {
244         if (mUserManagerInternal == null) {
245             throw new IllegalStateException("mUserManagerInternal not set yet");
246         }
247         final UserInfo userInfo = mUserManagerInternal.getUserInfo(userHandle);
248         if (userInfo == null) {
249             throw new IllegalStateException("No UserInfo for " + userHandle);
250         }
251         return userInfo;
252     }
253 
254     /**
255      * Starts the given user.
256      */
startUser(final @NonNull TimingsTraceAndSlog t, final @UserIdInt int userHandle)257     public void startUser(final @NonNull TimingsTraceAndSlog t, final @UserIdInt int userHandle) {
258         onUser(t, START, userHandle);
259     }
260 
261     /**
262      * Unlocks the given user.
263      */
unlockUser(final @UserIdInt int userHandle)264     public void unlockUser(final @UserIdInt int userHandle) {
265         onUser(UNLOCKING, userHandle);
266     }
267 
268     /**
269      * Called after the user was unlocked.
270      */
onUserUnlocked(final @UserIdInt int userHandle)271     public void onUserUnlocked(final @UserIdInt int userHandle) {
272         onUser(UNLOCKED, userHandle);
273     }
274 
275     /**
276      * Switches to the given user.
277      */
switchUser(final @UserIdInt int from, final @UserIdInt int to)278     public void switchUser(final @UserIdInt int from, final @UserIdInt int to) {
279         onUser(TimingsTraceAndSlog.newAsyncLog(), SWITCH, to, from);
280     }
281 
282     /**
283      * Stops the given user.
284      */
stopUser(final @UserIdInt int userHandle)285     public void stopUser(final @UserIdInt int userHandle) {
286         onUser(STOP, userHandle);
287     }
288 
289     /**
290      * Cleans up the given user.
291      */
cleanupUser(final @UserIdInt int userHandle)292     public void cleanupUser(final @UserIdInt int userHandle) {
293         onUser(CLEANUP, userHandle);
294     }
295 
onUser(@onNull String onWhat, @UserIdInt int userHandle)296     private void onUser(@NonNull String onWhat, @UserIdInt int userHandle) {
297         onUser(TimingsTraceAndSlog.newAsyncLog(), onWhat, userHandle);
298     }
299 
onUser(@onNull TimingsTraceAndSlog t, @NonNull String onWhat, @UserIdInt int userHandle)300     private void onUser(@NonNull TimingsTraceAndSlog t, @NonNull String onWhat,
301             @UserIdInt int userHandle) {
302         onUser(t, onWhat, userHandle, UserHandle.USER_NULL);
303     }
304 
onUser(@onNull TimingsTraceAndSlog t, @NonNull String onWhat, @UserIdInt int curUserId, @UserIdInt int prevUserId)305     private void onUser(@NonNull TimingsTraceAndSlog t, @NonNull String onWhat,
306             @UserIdInt int curUserId, @UserIdInt int prevUserId) {
307         t.traceBegin("ssm." + onWhat + "User-" + curUserId);
308         Slog.i(TAG, "Calling on" + onWhat + "User " + curUserId);
309         final TargetUser curUser = new TargetUser(getUserInfo(curUserId));
310         final TargetUser prevUser = prevUserId == UserHandle.USER_NULL ? null
311                 : new TargetUser(getUserInfo(prevUserId));
312         final int serviceLen = mServices.size();
313         for (int i = 0; i < serviceLen; i++) {
314             final SystemService service = mServices.get(i);
315             final String serviceName = service.getClass().getName();
316             boolean supported = service.isUserSupported(curUser);
317 
318             // Must check if either curUser or prevUser is supported (for example, if switching from
319             // unsupported to supported, we still need to notify the services)
320             if (!supported && prevUser != null) {
321                 supported = service.isUserSupported(prevUser);
322             }
323 
324             if (!supported) {
325                 if (DEBUG) {
326                     Slog.d(TAG, "Skipping " + onWhat + "User-" + curUserId + " on service "
327                             + serviceName + " because it's not supported (curUser: "
328                             + curUser + ", prevUser:" + prevUser + ")");
329                 } else {
330                     Slog.i(TAG,  "Skipping " + onWhat + "User-" + curUserId + " on "
331                             + serviceName);
332                 }
333                 continue;
334             }
335             t.traceBegin("ssm.on" + onWhat + "User-" + curUserId + "_" + serviceName);
336             long time = SystemClock.elapsedRealtime();
337             try {
338                 switch (onWhat) {
339                     case SWITCH:
340                         service.onUserSwitching(prevUser, curUser);
341                         break;
342                     case START:
343                         service.onUserStarting(curUser);
344                         break;
345                     case UNLOCKING:
346                         service.onUserUnlocking(curUser);
347                         break;
348                     case UNLOCKED:
349                         service.onUserUnlocked(curUser);
350                         break;
351                     case STOP:
352                         service.onUserStopping(curUser);
353                         break;
354                     case CLEANUP:
355                         service.onUserStopped(curUser);
356                         break;
357                     default:
358                         throw new IllegalArgumentException(onWhat + " what?");
359                 }
360             } catch (Exception ex) {
361                 Slog.wtf(TAG, "Failure reporting " + onWhat + " of user " + curUser
362                         + " to service " + serviceName, ex);
363             }
364             warnIfTooLong(SystemClock.elapsedRealtime() - time, service,
365                     "on" + onWhat + "User-" + curUserId);
366             t.traceEnd(); // what on service
367         }
368         t.traceEnd(); // main entry
369     }
370 
371     /** Sets the safe mode flag for services to query. */
setSafeMode(boolean safeMode)372     void setSafeMode(boolean safeMode) {
373         mSafeMode = safeMode;
374     }
375 
376     /**
377      * Returns whether we are booting into safe mode.
378      * @return safe mode flag
379      */
isSafeMode()380     public boolean isSafeMode() {
381         return mSafeMode;
382     }
383 
384     /**
385      * @return true if runtime was restarted, false if it's normal boot
386      */
isRuntimeRestarted()387     public boolean isRuntimeRestarted() {
388         return mRuntimeRestarted;
389     }
390 
391     /**
392      * @return Time when SystemServer was started, in elapsed realtime.
393      */
getRuntimeStartElapsedTime()394     public long getRuntimeStartElapsedTime() {
395         return mRuntimeStartElapsedTime;
396     }
397 
398     /**
399      * @return Time when SystemServer was started, in uptime.
400      */
getRuntimeStartUptime()401     public long getRuntimeStartUptime() {
402         return mRuntimeStartUptime;
403     }
404 
setStartInfo(boolean runtimeRestarted, long runtimeStartElapsedTime, long runtimeStartUptime)405     void setStartInfo(boolean runtimeRestarted,
406             long runtimeStartElapsedTime, long runtimeStartUptime) {
407         mRuntimeRestarted = runtimeRestarted;
408         mRuntimeStartElapsedTime = runtimeStartElapsedTime;
409         mRuntimeStartUptime = runtimeStartUptime;
410     }
411 
warnIfTooLong(long duration, SystemService service, String operation)412     private void warnIfTooLong(long duration, SystemService service, String operation) {
413         if (duration > SERVICE_CALL_WARN_TIME_MS) {
414             Slog.w(TAG, "Service " + service.getClass().getName() + " took " + duration + " ms in "
415                     + operation);
416         }
417     }
418 
419     /**
420      * Ensures that the system directory exist creating one if needed.
421      * @deprecated Use {@link Environment#getDataSystemCeDirectory()}
422      * or {@link Environment#getDataSystemDeDirectory()} instead.
423      * @return The system directory.
424      */
425     @Deprecated
ensureSystemDir()426     public static File ensureSystemDir() {
427         if (sSystemDir == null) {
428             File dataDir = Environment.getDataDirectory();
429             sSystemDir = new File(dataDir, "system");
430             sSystemDir.mkdirs();
431         }
432         return sSystemDir;
433     }
434 
435     /**
436      * Outputs the state of this manager to the System log.
437      */
dump()438     public void dump() {
439         StringBuilder builder = new StringBuilder();
440         builder.append("Current phase: ").append(mCurrentPhase).append("\n");
441         builder.append("Services:\n");
442         final int startedLen = mServices.size();
443         for (int i = 0; i < startedLen; i++) {
444             final SystemService service = mServices.get(i);
445             builder.append("\t")
446                     .append(service.getClass().getSimpleName())
447                     .append("\n");
448         }
449 
450         Slog.e(TAG, builder.toString());
451     }
452 }
453