summaryrefslogtreecommitdiffstats
path: root/core/java/com
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/com')
-rw-r--r--core/java/com/android/internal/app/ShutdownThread.java241
1 files changed, 241 insertions, 0 deletions
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 0000000..77d6e20
--- /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();
+ }
+}