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