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