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