diff options
| author | 2009-05-07 15:53:46 -0700 | |
|---|---|---|
| committer | 2009-05-08 12:59:21 -0700 | |
| commit | 55280a91884b9256e8db6af6a09f28b3feeaa9d8 (patch) | |
| tree | ac29de79b8ecec41f2fde6014a1834418a82d2fd | |
| parent | 672f1e2b07d985526bfd5606e6a888005fdcb70c (diff) | |
Improve shutdown process to send broadcast for applications.
This introduces a new class in the base platform for performing a clean
shutdown (which was copied from the classes in the policies). It
includes new features to send a shutdown broadcast for applications
to do cleanup, and ot have the activity manager pause the current
activity before proceeding with the shutdown. These facilities are
also use to write at the most recent stat files for sync, battery
and user activity.
| -rw-r--r-- | api/current.xml | 22 | ||||
| -rw-r--r-- | core/java/android/app/ActivityManagerNative.java | 22 | ||||
| -rw-r--r-- | core/java/android/app/IActivityManager.java | 5 | ||||
| -rw-r--r-- | core/java/android/content/Intent.java | 10 | ||||
| -rw-r--r-- | core/java/android/content/SyncManager.java | 12 | ||||
| -rw-r--r-- | core/java/android/content/SyncStorageEngine.java | 19 | ||||
| -rw-r--r-- | core/java/android/os/Power.java | 7 | ||||
| -rw-r--r-- | core/java/com/android/internal/app/ShutdownThread.java | 241 | ||||
| -rw-r--r-- | core/res/AndroidManifest.xml | 8 | ||||
| -rw-r--r-- | core/res/res/values/strings.xml | 6 | ||||
| -rw-r--r-- | services/java/com/android/server/am/ActivityManagerService.java | 58 | ||||
| -rw-r--r-- | services/java/com/android/server/am/BatteryStatsService.java | 10 | ||||
| -rwxr-xr-x | services/java/com/android/server/am/UsageStatsService.java | 6 |
13 files changed, 410 insertions, 16 deletions
diff --git a/api/current.xml b/api/current.xml index 7c5e6675482f..f809c09b00a0 100644 --- a/api/current.xml +++ b/api/current.xml @@ -1013,6 +1013,17 @@ visibility="public" > </field> +<field name="SHUTDOWN" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.permission.SHUTDOWN"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="SIGNAL_PERSISTENT_PROCESSES" type="java.lang.String" transient="false" @@ -28790,6 +28801,17 @@ visibility="public" > </field> +<field name="ACTION_SHUTDOWN" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.intent.action.ACTION_SHUTDOWN"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="ACTION_SYNC" type="java.lang.String" transient="false" diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 53e6f34a6915..541f413676c8 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -990,6 +990,14 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case SHUTDOWN_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + boolean res = shutdown(data.readInt()); + reply.writeNoException(); + reply.writeInt(res ? 1 : 0); + return true; + } + case PEEK_SERVICE_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); Intent service = Intent.CREATOR.createFromParcel(data); @@ -2160,5 +2168,19 @@ class ActivityManagerProxy implements IActivityManager return res; } + public boolean shutdown(int timeout) throws RemoteException + { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeInt(timeout); + mRemote.transact(SHUTDOWN_TRANSACTION, data, reply, 0); + reply.readException(); + boolean res = reply.readInt() != 0; + reply.recycle(); + data.recycle(); + return res; + } + private IBinder mRemote; } diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 2ac6160cf83e..56b29c1c941f 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -16,7 +16,6 @@ package android.app; -import android.app.ActivityManager.MemoryInfo; import android.content.ComponentName; import android.content.ContentProviderNative; import android.content.IContentProvider; @@ -34,7 +33,6 @@ import android.os.IInterface; import android.os.Parcel; import android.os.Parcelable; import android.os.ParcelFileDescriptor; -import android.text.TextUtils; import android.os.Bundle; import java.util.List; @@ -225,6 +223,8 @@ public interface IActivityManager extends IInterface { public boolean profileControl(String process, boolean start, String path) throws RemoteException; + public boolean shutdown(int timeout) throws RemoteException; + /* * Private non-Binder interfaces */ @@ -370,4 +370,5 @@ public interface IActivityManager extends IInterface { int GET_DEVICE_CONFIGURATION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+83; int PEEK_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+84; int PROFILE_CONTROL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+85; + int SHUTDOWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+86; } diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index b3e81d7e1f0a..3ebf81639a90 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -510,6 +510,7 @@ import java.util.Set; * <li> {@link #ACTION_BATTERY_CHANGED} * <li> {@link #ACTION_POWER_CONNECTED} * <li> {@link #ACTION_POWER_DISCONNECTED} + * <li> {@link #ACTION_SHUTDOWN} * </ul> * * <h3>Standard Categories</h3> @@ -1270,6 +1271,15 @@ public class Intent implements Parcelable { @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_POWER_DISCONNECTED = "android.intent.action.ACTION_POWER_DISCONNECTED"; /** + * Broadcast Action: Device is shutting down. + * This is broadcast when the device is being shut down (completely turned + * off, not sleeping). Once the broadcast is complete, the final shutdown + * will proceed and all unsaved data lost. Apps will not normally need + * to handle this, since the forground activity will be paused as well. + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String ACTION_SHUTDOWN = "android.intent.action.ACTION_SHUTDOWN"; + /** * Broadcast Action: Indicates low memory condition on the device */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java index ade5f3d3e04e..4d2cce881897 100644 --- a/core/java/android/content/SyncManager.java +++ b/core/java/android/content/SyncManager.java @@ -212,6 +212,14 @@ class SyncManager { } }; + private BroadcastReceiver mShutdownIntentReceiver = + new BroadcastReceiver() { + public void onReceive(Context context, Intent intent) { + Log.w(TAG, "Writing sync state before shutdown..."); + getSyncStorageEngine().writeAllState(); + } + }; + private static final String ACTION_SYNC_ALARM = "android.content.syncmanager.SYNC_ALARM"; private static final String SYNC_POLL_ALARM = "android.content.syncmanager.SYNC_POLL_ALARM"; private final SyncHandler mSyncHandler; @@ -249,6 +257,10 @@ class SyncManager { intentFilter.addAction(Intent.ACTION_DEVICE_STORAGE_OK); context.registerReceiver(mStorageIntentReceiver, intentFilter); + intentFilter = new IntentFilter(Intent.ACTION_SHUTDOWN); + intentFilter.setPriority(100); + context.registerReceiver(mShutdownIntentReceiver, intentFilter); + if (!factoryTest) { mNotificationMgr = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java index 1587462ec0e4..38278496411a 100644 --- a/core/java/android/content/SyncStorageEngine.java +++ b/core/java/android/content/SyncStorageEngine.java @@ -736,7 +736,7 @@ public class SyncStorageEngine extends Handler { } boolean writeStatisticsNow = false; - int day = getCurrentDay(); + int day = getCurrentDayLocked(); if (mDayStats[0] == null) { mDayStats[0] = new DayStats(day); } else if (day != mDayStats[0].day) { @@ -925,7 +925,7 @@ public class SyncStorageEngine extends Handler { } } - private int getCurrentDay() { + private int getCurrentDayLocked() { mCal.setTimeInMillis(System.currentTimeMillis()); final int dayOfYear = mCal.get(Calendar.DAY_OF_YEAR); if (mYear != mCal.get(Calendar.YEAR)) { @@ -1005,6 +1005,21 @@ public class SyncStorageEngine extends Handler { return status; } + public void writeAllState() { + synchronized (mAuthorities) { + // Account info is always written so no need to do it here. + + if (mNumPendingFinished > 0) { + // Only write these if they are out of date. + writePendingOperationsLocked(); + } + + // Just always write these... they are likely out of date. + writeStatusLocked(); + writeStatisticsLocked(); + } + } + /** * Read all account information back in to the initial engine state. */ diff --git a/core/java/android/os/Power.java b/core/java/android/os/Power.java index 47497e547ff1..3679e478126e 100644 --- a/core/java/android/os/Power.java +++ b/core/java/android/os/Power.java @@ -80,10 +80,9 @@ public class Power public static native int setLastUserActivityTimeout(long ms); /** - * Turn the device off. - * - * This method is considered deprecated in favor of - * {@link android.policy.ShutdownThread.shutdownAfterDisablingRadio()}. + * Low-level function turn the device off immediately, without trying + * to be clean. Most people should use + * {@link android.internal.app.ShutdownThread} for a clean shutdown. * * @deprecated * @hide diff --git a/core/java/com/android/internal/app/ShutdownThread.java b/core/java/com/android/internal/app/ShutdownThread.java new file mode 100644 index 000000000000..77d6e20a0fa5 --- /dev/null +++ b/core/java/com/android/internal/app/ShutdownThread.java @@ -0,0 +1,241 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package com.android.internal.app; + +import android.app.ActivityManagerNative; +import android.app.IActivityManager; +import android.app.ProgressDialog; +import android.app.AlertDialog; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.IBluetoothDevice; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Handler; +import android.os.RemoteException; +import android.os.Power; +import android.os.ServiceManager; +import android.os.SystemClock; +import com.android.internal.telephony.ITelephony; +import android.util.Log; +import android.view.WindowManager; + +public final class ShutdownThread extends Thread { + // constants + private static final String TAG = "ShutdownThread"; + private static final int MAX_NUM_PHONE_STATE_READS = 16; + private static final int PHONE_STATE_POLL_SLEEP_MSEC = 500; + // maximum time we wait for the shutdown broadcast before going on. + private static final int MAX_BROADCAST_TIME = 10*1000; + + // state tracking + private static Object sIsStartedGuard = new Object(); + private static boolean sIsStarted = false; + + // static instance of this thread + private static final ShutdownThread sInstance = new ShutdownThread(); + + private final Object mBroadcastDoneSync = new Object(); + private boolean mBroadcastDone; + private Context mContext; + private Handler mHandler; + + private ShutdownThread() { + } + + /** + * Request a clean shutdown, waiting for subsystems to clean up their + * state etc. Must be called from a Looper thread in which its UI + * is shown. + * + * @param context Context used to display the shutdown progress dialog. + */ + public static void shutdown(final Context context, boolean confirm) { + // ensure that only one thread is trying to power down. + // any additional calls are just returned + synchronized (sIsStartedGuard){ + if (sIsStarted) { + Log.d(TAG, "Request to shutdown already running, returning."); + return; + } + } + + Log.d(TAG, "Notifying thread to start radio shutdown"); + + if (confirm) { + final AlertDialog dialog = new AlertDialog.Builder(context) + .setIcon(android.R.drawable.ic_dialog_alert) + .setTitle(com.android.internal.R.string.power_off) + .setMessage(com.android.internal.R.string.shutdown_confirm) + .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + beginShutdownSequence(context); + } + }) + .setNegativeButton(com.android.internal.R.string.no, null) + .create(); + dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); + dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND); + dialog.show(); + } else { + beginShutdownSequence(context); + } + } + + private static void beginShutdownSequence(Context context) { + synchronized (sIsStartedGuard) { + sIsStarted = true; + } + + // throw up an indeterminate system dialog to indicate radio is + // shutting down. + ProgressDialog pd = new ProgressDialog(context); + pd.setTitle(context.getText(com.android.internal.R.string.power_off)); + pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress)); + pd.setIndeterminate(true); + pd.setCancelable(false); + pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); + pd.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND); + + pd.show(); + + // start the thread that initiates shutdown + sInstance.mContext = context; + sInstance.mHandler = new Handler() { + }; + sInstance.start(); + } + + void broadcastDone() { + synchronized (mBroadcastDoneSync) { + mBroadcastDone = true; + mBroadcastDoneSync.notifyAll(); + } + } + + /** + * Makes sure we handle the shutdown gracefully. + * Shuts off power regardless of radio and bluetooth state if the alloted time has passed. + */ + public void run() { + boolean bluetoothOff; + boolean radioOff; + + BroadcastReceiver br = new BroadcastReceiver() { + @Override public void onReceive(Context context, Intent intent) { + // We don't allow apps to cancel this, so ignore the result. + broadcastDone(); + } + }; + + Log.i(TAG, "Sending shutdown broadcast..."); + + // First send the high-level shut down broadcast. + mBroadcastDone = false; + mContext.sendOrderedBroadcast(new Intent(Intent.ACTION_SHUTDOWN), null, + br, mHandler, 0, null, null); + + final long endTime = System.currentTimeMillis() + MAX_BROADCAST_TIME; + synchronized (mBroadcastDoneSync) { + while (!mBroadcastDone) { + long delay = endTime - System.currentTimeMillis(); + if (delay <= 0) { + Log.w(TAG, "Shutdown broadcast timed out"); + break; + } + try { + mBroadcastDoneSync.wait(delay); + } catch (InterruptedException e) { + } + } + } + + Log.i(TAG, "Shutting down activity manager..."); + + final IActivityManager am = + ActivityManagerNative.asInterface(ServiceManager.checkService("activity")); + if (am != null) { + try { + am.shutdown(MAX_BROADCAST_TIME); + } catch (RemoteException e) { + } + } + + final ITelephony phone = + ITelephony.Stub.asInterface(ServiceManager.checkService("phone")); + final IBluetoothDevice bluetooth = + IBluetoothDevice.Stub.asInterface(ServiceManager.checkService( + Context.BLUETOOTH_SERVICE)); + + try { + bluetoothOff = bluetooth == null || + bluetooth.getBluetoothState() == BluetoothDevice.BLUETOOTH_STATE_OFF; + if (!bluetoothOff) { + Log.w(TAG, "Disabling Bluetooth..."); + bluetooth.disable(false); // disable but don't persist new state + } + } catch (RemoteException ex) { + Log.e(TAG, "RemoteException during bluetooth shutdown", ex); + bluetoothOff = true; + } + + try { + radioOff = phone == null || !phone.isRadioOn(); + if (!radioOff) { + Log.w(TAG, "Turning off radio..."); + phone.setRadio(false); + } + } catch (RemoteException ex) { + Log.e(TAG, "RemoteException during radio shutdown", ex); + radioOff = true; + } + + Log.i(TAG, "Waiting for Bluetooth and Radio..."); + + // Wait a max of 32 seconds for clean shutdown + for (int i = 0; i < MAX_NUM_PHONE_STATE_READS; i++) { + if (!bluetoothOff) { + try { + bluetoothOff = + bluetooth.getBluetoothState() == BluetoothDevice.BLUETOOTH_STATE_OFF; + } catch (RemoteException ex) { + Log.e(TAG, "RemoteException during bluetooth shutdown", ex); + bluetoothOff = true; + } + } + if (!radioOff) { + try { + radioOff = !phone.isRadioOn(); + } catch (RemoteException ex) { + Log.e(TAG, "RemoteException during radio shutdown", ex); + radioOff = true; + } + } + if (radioOff && bluetoothOff) { + Log.i(TAG, "Radio and Bluetooth shutdown complete."); + break; + } + SystemClock.sleep(PHONE_STATE_POLL_SLEEP_MSEC); + } + + //shutdown power + Log.i(TAG, "Performing low-level shutdown..."); + Power.shutdown(); + } +} diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 7ebc896059bf..bff6b9dfda5e 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -803,6 +803,14 @@ android:description="@string/permdesc_runSetActivityWatcher" android:protectionLevel="signature" /> + <!-- Allows an application to watch and control how activities are + started globally in the system. Only for is in debugging + (usually the monkey command). --> + <permission android:name="android.permission.SHUTDOWN" + android:label="@string/permlab_shutdown" + android:description="@string/permdesc_shutdown" + android:protectionLevel="signature" /> + <!-- Allows an application to retrieve the current state of keys and switches. This is only for use by the system.--> <permission android:name="android.permission.READ_INPUT_STATE" diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 6e2da4b84cf8..331ef1a0feaa 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -467,6 +467,12 @@ the system, and steal or corrupt any data on it.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_shutdown">partial shutdown</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_shutdown">Puts the activity manager into a shutdown + state. Does not perform a complete shutdown.</string> + + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_runSetActivityWatcher">monitor and control all application launching</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permdesc_runSetActivityWatcher">Allows an application to diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index b04f5a8f4fb0..2be497528ed9 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -707,6 +707,11 @@ public final class ActivityManagerService extends ActivityManagerNative implemen boolean mSleeping = false; /** + * Set if we are shutting down the system, similar to sleeping. + */ + boolean mShuttingDown = false; + + /** * Set when the system is going to sleep, until we have * successfully paused the current activity and released our wake lock. * At that point the system is allowed to actually sleep. @@ -868,7 +873,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return; } AppErrorResult res = (AppErrorResult) data.get("result"); - if (!mSleeping) { + if (!mSleeping && !mShuttingDown) { Dialog d = new AppErrorDialog( mContext, res, proc, (Integer)data.get("flags"), @@ -1893,7 +1898,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // If we are not going to sleep, we want to ensure the device is // awake until the next activity is started. - if (!mSleeping) { + if (!mSleeping && !mShuttingDown) { mLaunchingActivity.acquire(); if (!mHandler.hasMessages(LAUNCH_TIMEOUT_MSG)) { // To be safe, don't allow the wake lock to be held for too long. @@ -1972,12 +1977,15 @@ public final class ActivityManagerService extends ActivityManagerNative implemen mPausingActivity = null; } - if (!mSleeping) { + if (!mSleeping && !mShuttingDown) { resumeTopActivityLocked(prev); } else { if (mGoingToSleep.isHeld()) { mGoingToSleep.release(); } + if (mShuttingDown) { + notifyAll(); + } } if (prev != null) { @@ -2243,7 +2251,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // If we are sleeping, and there is no resumed activity, and the top // activity is paused, well that is the state we want. - if (mSleeping && mLastPausedActivity == next && next.state == ActivityState.PAUSED) { + if ((mSleeping || mShuttingDown) + && mLastPausedActivity == next && next.state == ActivityState.PAUSED) { // Make sure we have executed any pending transitions, since there // should be nothing left to do at this point. mWindowManager.executeAppTransition(); @@ -7098,8 +7107,45 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } + public boolean shutdown(int timeout) { + if (checkCallingPermission(android.Manifest.permission.SHUTDOWN) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Requires permission " + + android.Manifest.permission.SHUTDOWN); + } + + boolean timedout = false; + + synchronized(this) { + mShuttingDown = true; + mWindowManager.setEventDispatching(false); + + if (mResumedActivity != null) { + pauseIfSleepingLocked(); + final long endTime = System.currentTimeMillis() + timeout; + while (mResumedActivity != null || mPausingActivity != null) { + long delay = endTime - System.currentTimeMillis(); + if (delay <= 0) { + Log.w(TAG, "Activity manager shutdown timed out"); + timedout = true; + break; + } + try { + this.wait(); + } catch (InterruptedException e) { + } + } + } + } + + mUsageStatsService.shutdown(); + mBatteryStatsService.shutdown(); + + return timedout; + } + void pauseIfSleepingLocked() { - if (mSleeping) { + if (mSleeping || mShuttingDown) { if (!mGoingToSleep.isHeld()) { mGoingToSleep.acquire(); if (mLaunchingActivity.isHeld()) { @@ -8064,7 +8110,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen + " mBooting=" + mBooting + " mBooted=" + mBooted + " mFactoryTest=" + mFactoryTest); - pw.println(" mSleeping=" + mSleeping); + pw.println(" mSleeping=" + mSleeping + " mShuttingDown=" + mShuttingDown); pw.println(" mGoingToSleep=" + mGoingToSleep); pw.println(" mLaunchingActivity=" + mLaunchingActivity); pw.println(" mDebugApp=" + mDebugApp + "/orig=" + mOrigDebugApp diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java index ddc3e6829de2..a21893bbb44c 100644 --- a/services/java/com/android/server/am/BatteryStatsService.java +++ b/services/java/com/android/server/am/BatteryStatsService.java @@ -25,8 +25,7 @@ import android.os.IBinder; import android.os.Parcel; import android.os.Process; import android.os.ServiceManager; -import android.telephony.TelephonyManager; -import android.util.PrintWriterPrinter; +import android.util.Log; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -50,6 +49,13 @@ public final class BatteryStatsService extends IBatteryStats.Stub { ServiceManager.addService("batteryinfo", asBinder()); } + public void shutdown() { + Log.w("BatteryStats", "Writing battery stats before shutdown..."); + synchronized (mStats) { + mStats.writeLocked(); + } + } + public static IBatteryStats getService() { if (sService != null) { return sService; diff --git a/services/java/com/android/server/am/UsageStatsService.java b/services/java/com/android/server/am/UsageStatsService.java index b6f91589fae6..866334b2d28e 100755 --- a/services/java/com/android/server/am/UsageStatsService.java +++ b/services/java/com/android/server/am/UsageStatsService.java @@ -17,6 +17,7 @@ package com.android.server.am; import com.android.internal.app.IUsageStats; + import android.content.ComponentName; import android.content.Context; import android.os.Binder; @@ -418,6 +419,11 @@ public final class UsageStatsService extends IUsageStats.Stub { ServiceManager.addService(SERVICE_NAME, asBinder()); } + public void shutdown() { + Log.w(TAG, "Writing usage stats before shutdown..."); + writeStatsToFile(true); + } + public static IUsageStats getService() { if (sService != null) { return sService; |