1182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski/*
2182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski * Copyright (C) 2013 The Android Open Source Project
3182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski *
4182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski * Licensed under the Apache License, Version 2.0 (the "License");
5182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski * you may not use this file except in compliance with the License.
6182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski * You may obtain a copy of the License at
7182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski *
8182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski *      http://www.apache.org/licenses/LICENSE-2.0
9182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski *
10182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski * Unless required by applicable law or agreed to in writing, software
11182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski * distributed under the License is distributed on an "AS IS" BASIS,
12182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski * See the License for the specific language governing permissions and
14182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski * limitations under the License.
15182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski */
16182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski
17182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinskipackage com.android.server;
18182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski
19182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinskiimport android.content.Context;
20182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinskiimport android.util.Slog;
21182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski
22b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brownimport java.lang.reflect.Constructor;
23b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brownimport java.lang.reflect.InvocationTargetException;
24182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinskiimport java.util.ArrayList;
25182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski
26182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski/**
27b102b2cc73baa20e8cc9e4c081235b031c4e8fe0Adam Lesinski * Manages creating, starting, and other lifecycle events of
28b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown * {@link com.android.server.SystemService system services}.
29b102b2cc73baa20e8cc9e4c081235b031c4e8fe0Adam Lesinski *
30b102b2cc73baa20e8cc9e4c081235b031c4e8fe0Adam Lesinski * {@hide}
31182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski */
32182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinskipublic class SystemServiceManager {
33182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    private static final String TAG = "SystemServiceManager";
34182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski
35182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    private final Context mContext;
369158825f9c41869689d6b1786d7c7aa8bdd524ceAmith Yamasani    private boolean mSafeMode;
37182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski
38182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    // Services that should receive lifecycle events.
39182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    private final ArrayList<SystemService> mServices = new ArrayList<SystemService>();
40182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski
41182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    private int mCurrentPhase = -1;
42182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski
43182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    public SystemServiceManager(Context context) {
44182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        mContext = context;
45182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    }
46182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski
476f357d3284a833cc50a990e14b39f389b8972254Jeff Brown    /**
482cb6c60c0d2de3bc743c043aca963db6fe52662fAdam Lesinski     * Starts a service by class name.
496f357d3284a833cc50a990e14b39f389b8972254Jeff Brown     *
502cb6c60c0d2de3bc743c043aca963db6fe52662fAdam Lesinski     * @return The service instance.
516f357d3284a833cc50a990e14b39f389b8972254Jeff Brown     */
526f357d3284a833cc50a990e14b39f389b8972254Jeff Brown    @SuppressWarnings("unchecked")
532c43c339de5aaf4fef58aa9b5ac3af48609263a8Jeff Brown    public SystemService startService(String className) {
542c43c339de5aaf4fef58aa9b5ac3af48609263a8Jeff Brown        final Class<SystemService> serviceClass;
552c43c339de5aaf4fef58aa9b5ac3af48609263a8Jeff Brown        try {
562c43c339de5aaf4fef58aa9b5ac3af48609263a8Jeff Brown            serviceClass = (Class<SystemService>)Class.forName(className);
572c43c339de5aaf4fef58aa9b5ac3af48609263a8Jeff Brown        } catch (ClassNotFoundException ex) {
582c43c339de5aaf4fef58aa9b5ac3af48609263a8Jeff Brown            Slog.i(TAG, "Starting " + className);
592c43c339de5aaf4fef58aa9b5ac3af48609263a8Jeff Brown            throw new RuntimeException("Failed to create service " + className
602c43c339de5aaf4fef58aa9b5ac3af48609263a8Jeff Brown                    + ": service class not found, usually indicates that the caller should "
612c43c339de5aaf4fef58aa9b5ac3af48609263a8Jeff Brown                    + "have called PackageManager.hasSystemFeature() to check whether the "
622c43c339de5aaf4fef58aa9b5ac3af48609263a8Jeff Brown                    + "feature is available on this device before trying to start the "
632c43c339de5aaf4fef58aa9b5ac3af48609263a8Jeff Brown                    + "services that implement it", ex);
642c43c339de5aaf4fef58aa9b5ac3af48609263a8Jeff Brown        }
652c43c339de5aaf4fef58aa9b5ac3af48609263a8Jeff Brown        return startService(serviceClass);
669158825f9c41869689d6b1786d7c7aa8bdd524ceAmith Yamasani    }
679158825f9c41869689d6b1786d7c7aa8bdd524ceAmith Yamasani
68182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    /**
69182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski     * Creates and starts a system service. The class must be a subclass of
70182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski     * {@link com.android.server.SystemService}.
71182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski     *
72182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski     * @param serviceClass A Java class that implements the SystemService interface.
736f357d3284a833cc50a990e14b39f389b8972254Jeff Brown     * @return The service instance, never null.
74182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski     * @throws RuntimeException if the service fails to start.
75182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski     */
766f357d3284a833cc50a990e14b39f389b8972254Jeff Brown    @SuppressWarnings("unchecked")
776f357d3284a833cc50a990e14b39f389b8972254Jeff Brown    public <T extends SystemService> T startService(Class<T> serviceClass) {
78b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown        final String name = serviceClass.getName();
79b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown        Slog.i(TAG, "Starting " + name);
80b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown
81b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown        // Create the service.
82b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown        if (!SystemService.class.isAssignableFrom(serviceClass)) {
83b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown            throw new RuntimeException("Failed to create " + name
84b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown                    + ": service must extend " + SystemService.class.getName());
85b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown        }
86b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown        final T service;
87182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        try {
88b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown            Constructor<T> constructor = serviceClass.getConstructor(Context.class);
89b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown            service = constructor.newInstance(mContext);
90b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown        } catch (InstantiationException ex) {
91b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown            throw new RuntimeException("Failed to create service " + name
92b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown                    + ": service could not be instantiated", ex);
93b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown        } catch (IllegalAccessException ex) {
94b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown            throw new RuntimeException("Failed to create service " + name
95b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown                    + ": service must have a public constructor with a Context argument", ex);
96b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown        } catch (NoSuchMethodException ex) {
97b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown            throw new RuntimeException("Failed to create service " + name
98b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown                    + ": service must have a public constructor with a Context argument", ex);
99b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown        } catch (InvocationTargetException ex) {
100b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown            throw new RuntimeException("Failed to create service " + name
101b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown                    + ": service constructor threw an exception", ex);
102182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        }
103182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski
104b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown        // Register it.
105b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown        mServices.add(service);
106182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski
107b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown        // Start it.
108182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        try {
109b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown            service.onStart();
110b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown        } catch (RuntimeException ex) {
111b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown            throw new RuntimeException("Failed to start service " + name
112b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown                    + ": onStart threw an exception", ex);
113182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        }
114b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown        return service;
115182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    }
116182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski
117182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    /**
118182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski     * Starts the specified boot phase for all system services that have been started up to
119182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski     * this point.
120182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski     *
121182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski     * @param phase The boot phase to start.
122182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski     */
123182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    public void startBootPhase(final int phase) {
124182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        if (phase <= mCurrentPhase) {
125182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski            throw new IllegalArgumentException("Next phase must be larger than previous");
126182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        }
127182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        mCurrentPhase = phase;
128182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski
129182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        Slog.i(TAG, "Starting phase " + mCurrentPhase);
130182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski
131182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        final int serviceLen = mServices.size();
132182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        for (int i = 0; i < serviceLen; i++) {
133182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski            final SystemService service = mServices.get(i);
134182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski            try {
135182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski                service.onBootPhase(mCurrentPhase);
136b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown            } catch (Exception ex) {
137b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown                throw new RuntimeException("Failed to boot service "
138b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown                        + service.getClass().getName()
139b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown                        + ": onBootPhase threw an exception during phase "
140b880d880c6cd989eacc28c365fc9a41d31900da1Jeff Brown                        + mCurrentPhase, ex);
141182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski            }
142182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        }
143182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    }
144182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski
14591097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn    public void startUser(final int userHandle) {
14691097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn        final int serviceLen = mServices.size();
14791097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn        for (int i = 0; i < serviceLen; i++) {
14891097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn            final SystemService service = mServices.get(i);
14991097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn            try {
15091097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn                service.onStartUser(userHandle);
15191097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn            } catch (Exception ex) {
15291097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn                Slog.wtf(TAG, "Failure reporting start of user " + userHandle
15391097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn                        + " to service " + service.getClass().getName(), ex);
15491097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn            }
15591097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn        }
15691097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn    }
15791097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn
15891097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn    public void switchUser(final int userHandle) {
15991097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn        final int serviceLen = mServices.size();
16091097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn        for (int i = 0; i < serviceLen; i++) {
16191097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn            final SystemService service = mServices.get(i);
16291097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn            try {
16391097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn                service.onSwitchUser(userHandle);
16491097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn            } catch (Exception ex) {
16591097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn                Slog.wtf(TAG, "Failure reporting switch of user " + userHandle
16691097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn                        + " to service " + service.getClass().getName(), ex);
16791097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn            }
16891097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn        }
16991097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn    }
17091097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn
17191097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn    public void stopUser(final int userHandle) {
17291097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn        final int serviceLen = mServices.size();
17391097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn        for (int i = 0; i < serviceLen; i++) {
17491097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn            final SystemService service = mServices.get(i);
17591097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn            try {
17691097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn                service.onStopUser(userHandle);
17791097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn            } catch (Exception ex) {
17891097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn                Slog.wtf(TAG, "Failure reporting stop of user " + userHandle
17991097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn                        + " to service " + service.getClass().getName(), ex);
18091097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn            }
18191097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn        }
18291097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn    }
18391097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn
18491097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn    public void cleanupUser(final int userHandle) {
18591097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn        final int serviceLen = mServices.size();
18691097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn        for (int i = 0; i < serviceLen; i++) {
18791097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn            final SystemService service = mServices.get(i);
18891097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn            try {
18991097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn                service.onCleanupUser(userHandle);
19091097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn            } catch (Exception ex) {
19191097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn                Slog.wtf(TAG, "Failure reporting cleanup of user " + userHandle
19291097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn                        + " to service " + service.getClass().getName(), ex);
19391097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn            }
19491097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn        }
19591097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn    }
19691097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn
1979158825f9c41869689d6b1786d7c7aa8bdd524ceAmith Yamasani    /** Sets the safe mode flag for services to query. */
1989158825f9c41869689d6b1786d7c7aa8bdd524ceAmith Yamasani    public void setSafeMode(boolean safeMode) {
1999158825f9c41869689d6b1786d7c7aa8bdd524ceAmith Yamasani        mSafeMode = safeMode;
2009158825f9c41869689d6b1786d7c7aa8bdd524ceAmith Yamasani    }
2019158825f9c41869689d6b1786d7c7aa8bdd524ceAmith Yamasani
2029158825f9c41869689d6b1786d7c7aa8bdd524ceAmith Yamasani    /**
2039158825f9c41869689d6b1786d7c7aa8bdd524ceAmith Yamasani     * Returns whether we are booting into safe mode.
2049158825f9c41869689d6b1786d7c7aa8bdd524ceAmith Yamasani     * @return safe mode flag
2059158825f9c41869689d6b1786d7c7aa8bdd524ceAmith Yamasani     */
2069158825f9c41869689d6b1786d7c7aa8bdd524ceAmith Yamasani    public boolean isSafeMode() {
2079158825f9c41869689d6b1786d7c7aa8bdd524ceAmith Yamasani        return mSafeMode;
2089158825f9c41869689d6b1786d7c7aa8bdd524ceAmith Yamasani    }
2099158825f9c41869689d6b1786d7c7aa8bdd524ceAmith Yamasani
210182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    /**
211182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski     * Outputs the state of this manager to the System log.
212182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski     */
213182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    public void dump() {
214182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        StringBuilder builder = new StringBuilder();
215182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        builder.append("Current phase: ").append(mCurrentPhase).append("\n");
216182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        builder.append("Services:\n");
217182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        final int startedLen = mServices.size();
218182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        for (int i = 0; i < startedLen; i++) {
219182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski            final SystemService service = mServices.get(i);
220182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski            builder.append("\t")
221182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski                    .append(service.getClass().getSimpleName())
222182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski                    .append("\n");
223182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        }
224182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski
225182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski        Slog.e(TAG, builder.toString());
226182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski    }
227182f73fc4da13a6417e5086ec9ecce80eb8423caAdam Lesinski}
228