summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Dianne Hackborn <hackbod@google.com> 2009-05-07 15:53:46 -0700
committer Dianne Hackborn <hackbod@google.com> 2009-05-08 12:59:21 -0700
commit55280a91884b9256e8db6af6a09f28b3feeaa9d8 (patch)
treeac29de79b8ecec41f2fde6014a1834418a82d2fd
parent672f1e2b07d985526bfd5606e6a888005fdcb70c (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.xml22
-rw-r--r--core/java/android/app/ActivityManagerNative.java22
-rw-r--r--core/java/android/app/IActivityManager.java5
-rw-r--r--core/java/android/content/Intent.java10
-rw-r--r--core/java/android/content/SyncManager.java12
-rw-r--r--core/java/android/content/SyncStorageEngine.java19
-rw-r--r--core/java/android/os/Power.java7
-rw-r--r--core/java/com/android/internal/app/ShutdownThread.java241
-rw-r--r--core/res/AndroidManifest.xml8
-rw-r--r--core/res/res/values/strings.xml6
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java58
-rw-r--r--services/java/com/android/server/am/BatteryStatsService.java10
-rwxr-xr-xservices/java/com/android/server/am/UsageStatsService.java6
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="&quot;android.permission.SHUTDOWN&quot;"
+ 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="&quot;android.intent.action.ACTION_SHUTDOWN&quot;"
+ 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;