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.content.Context;
20 import android.util.Slog;
21 
22 import java.lang.reflect.Constructor;
23 import java.lang.reflect.InvocationTargetException;
24 import java.util.ArrayList;
25 
26 /**
27  * Manages creating, starting, and other lifecycle events of
28  * {@link com.android.server.SystemService system services}.
29  *
30  * {@hide}
31  */
32 public class SystemServiceManager {
33     private static final String TAG = "SystemServiceManager";
34 
35     private final Context mContext;
36     private boolean mSafeMode;
37 
38     // Services that should receive lifecycle events.
39     private final ArrayList<SystemService> mServices = new ArrayList<SystemService>();
40 
41     private int mCurrentPhase = -1;
42 
SystemServiceManager(Context context)43     public SystemServiceManager(Context context) {
44         mContext = context;
45     }
46 
47     /**
48      * Starts a service by class name.
49      *
50      * @return The service instance.
51      */
52     @SuppressWarnings("unchecked")
startService(String className)53     public SystemService startService(String className) {
54         final Class<SystemService> serviceClass;
55         try {
56             serviceClass = (Class<SystemService>)Class.forName(className);
57         } catch (ClassNotFoundException ex) {
58             Slog.i(TAG, "Starting " + className);
59             throw new RuntimeException("Failed to create service " + className
60                     + ": service class not found, usually indicates that the caller should "
61                     + "have called PackageManager.hasSystemFeature() to check whether the "
62                     + "feature is available on this device before trying to start the "
63                     + "services that implement it", ex);
64         }
65         return startService(serviceClass);
66     }
67 
68     /**
69      * Creates and starts a system service. The class must be a subclass of
70      * {@link com.android.server.SystemService}.
71      *
72      * @param serviceClass A Java class that implements the SystemService interface.
73      * @return The service instance, never null.
74      * @throws RuntimeException if the service fails to start.
75      */
76     @SuppressWarnings("unchecked")
startService(Class<T> serviceClass)77     public <T extends SystemService> T startService(Class<T> serviceClass) {
78         final String name = serviceClass.getName();
79         Slog.i(TAG, "Starting " + name);
80 
81         // Create the service.
82         if (!SystemService.class.isAssignableFrom(serviceClass)) {
83             throw new RuntimeException("Failed to create " + name
84                     + ": service must extend " + SystemService.class.getName());
85         }
86         final T service;
87         try {
88             Constructor<T> constructor = serviceClass.getConstructor(Context.class);
89             service = constructor.newInstance(mContext);
90         } catch (InstantiationException ex) {
91             throw new RuntimeException("Failed to create service " + name
92                     + ": service could not be instantiated", ex);
93         } catch (IllegalAccessException ex) {
94             throw new RuntimeException("Failed to create service " + name
95                     + ": service must have a public constructor with a Context argument", ex);
96         } catch (NoSuchMethodException ex) {
97             throw new RuntimeException("Failed to create service " + name
98                     + ": service must have a public constructor with a Context argument", ex);
99         } catch (InvocationTargetException ex) {
100             throw new RuntimeException("Failed to create service " + name
101                     + ": service constructor threw an exception", ex);
102         }
103 
104         // Register it.
105         mServices.add(service);
106 
107         // Start it.
108         try {
109             service.onStart();
110         } catch (RuntimeException ex) {
111             throw new RuntimeException("Failed to start service " + name
112                     + ": onStart threw an exception", ex);
113         }
114         return service;
115     }
116 
117     /**
118      * Starts the specified boot phase for all system services that have been started up to
119      * this point.
120      *
121      * @param phase The boot phase to start.
122      */
startBootPhase(final int phase)123     public void startBootPhase(final int phase) {
124         if (phase <= mCurrentPhase) {
125             throw new IllegalArgumentException("Next phase must be larger than previous");
126         }
127         mCurrentPhase = phase;
128 
129         Slog.i(TAG, "Starting phase " + mCurrentPhase);
130 
131         final int serviceLen = mServices.size();
132         for (int i = 0; i < serviceLen; i++) {
133             final SystemService service = mServices.get(i);
134             try {
135                 service.onBootPhase(mCurrentPhase);
136             } catch (Exception ex) {
137                 throw new RuntimeException("Failed to boot service "
138                         + service.getClass().getName()
139                         + ": onBootPhase threw an exception during phase "
140                         + mCurrentPhase, ex);
141             }
142         }
143     }
144 
startUser(final int userHandle)145     public void startUser(final int userHandle) {
146         final int serviceLen = mServices.size();
147         for (int i = 0; i < serviceLen; i++) {
148             final SystemService service = mServices.get(i);
149             try {
150                 service.onStartUser(userHandle);
151             } catch (Exception ex) {
152                 Slog.wtf(TAG, "Failure reporting start of user " + userHandle
153                         + " to service " + service.getClass().getName(), ex);
154             }
155         }
156     }
157 
switchUser(final int userHandle)158     public void switchUser(final int userHandle) {
159         final int serviceLen = mServices.size();
160         for (int i = 0; i < serviceLen; i++) {
161             final SystemService service = mServices.get(i);
162             try {
163                 service.onSwitchUser(userHandle);
164             } catch (Exception ex) {
165                 Slog.wtf(TAG, "Failure reporting switch of user " + userHandle
166                         + " to service " + service.getClass().getName(), ex);
167             }
168         }
169     }
170 
stopUser(final int userHandle)171     public void stopUser(final int userHandle) {
172         final int serviceLen = mServices.size();
173         for (int i = 0; i < serviceLen; i++) {
174             final SystemService service = mServices.get(i);
175             try {
176                 service.onStopUser(userHandle);
177             } catch (Exception ex) {
178                 Slog.wtf(TAG, "Failure reporting stop of user " + userHandle
179                         + " to service " + service.getClass().getName(), ex);
180             }
181         }
182     }
183 
cleanupUser(final int userHandle)184     public void cleanupUser(final int userHandle) {
185         final int serviceLen = mServices.size();
186         for (int i = 0; i < serviceLen; i++) {
187             final SystemService service = mServices.get(i);
188             try {
189                 service.onCleanupUser(userHandle);
190             } catch (Exception ex) {
191                 Slog.wtf(TAG, "Failure reporting cleanup of user " + userHandle
192                         + " to service " + service.getClass().getName(), ex);
193             }
194         }
195     }
196 
197     /** Sets the safe mode flag for services to query. */
setSafeMode(boolean safeMode)198     public void setSafeMode(boolean safeMode) {
199         mSafeMode = safeMode;
200     }
201 
202     /**
203      * Returns whether we are booting into safe mode.
204      * @return safe mode flag
205      */
isSafeMode()206     public boolean isSafeMode() {
207         return mSafeMode;
208     }
209 
210     /**
211      * Outputs the state of this manager to the System log.
212      */
dump()213     public void dump() {
214         StringBuilder builder = new StringBuilder();
215         builder.append("Current phase: ").append(mCurrentPhase).append("\n");
216         builder.append("Services:\n");
217         final int startedLen = mServices.size();
218         for (int i = 0; i < startedLen; i++) {
219             final SystemService service = mServices.get(i);
220             builder.append("\t")
221                     .append(service.getClass().getSimpleName())
222                     .append("\n");
223         }
224 
225         Slog.e(TAG, builder.toString());
226     }
227 }
228