2 * Copyright (C) 2008 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 package com.android.server.power;
20 import android.app.ActivityManagerNative;
21 import android.app.AlertDialog;
22 import android.app.Dialog;
23 import android.app.IActivityManager;
24 import android.app.ProgressDialog;
25 import android.bluetooth.BluetoothAdapter;
26 import android.bluetooth.IBluetoothManager;
27 import android.media.AudioAttributes;
28 import android.nfc.NfcAdapter;
29 import android.nfc.INfcAdapter;
30 import android.content.BroadcastReceiver;
31 import android.content.Context;
32 import android.content.DialogInterface;
33 import android.content.Intent;
34 import android.content.IntentFilter;
35 import android.os.Handler;
36 import android.os.PowerManager;
37 import android.os.RemoteException;
38 import android.os.ServiceManager;
39 import android.os.SystemClock;
40 import android.os.SystemProperties;
41 import android.os.UserHandle;
42 import android.os.UserManager;
43 import android.os.Vibrator;
44 import android.os.SystemVibrator;
45 import android.os.storage.IMountService;
46 import android.os.storage.IMountShutdownObserver;
48 import com.android.internal.telephony.ITelephony;
49 import com.android.server.pm.PackageManagerService;
51 import android.util.Log;
52 import android.view.WindowManager;
54 public final class ShutdownThread extends Thread {
56 private static final String TAG = "ShutdownThread";
57 private static final int PHONE_STATE_POLL_SLEEP_MSEC = 500;
58 // maximum time we wait for the shutdown broadcast before going on.
59 private static final int MAX_BROADCAST_TIME = 10*1000;
60 private static final int MAX_SHUTDOWN_WAIT_TIME = 20*1000;
61 private static final int MAX_RADIO_WAIT_TIME = 12*1000;
63 // length of vibration before shutting down
64 private static final int SHUTDOWN_VIBRATE_MS = 500;
67 private static Object sIsStartedGuard = new Object();
68 private static boolean sIsStarted = false;
70 private static boolean mReboot;
71 private static boolean mRebootSafeMode;
72 private static String mRebootReason;
74 // Provides shutdown assurance in case the system_server is killed
75 public static final String SHUTDOWN_ACTION_PROPERTY = "sys.shutdown.requested";
77 // Indicates whether we are rebooting into safe mode
78 public static final String REBOOT_SAFEMODE_PROPERTY = "persist.sys.safemode";
80 // static instance of this thread
81 private static final ShutdownThread sInstance = new ShutdownThread();
83 private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
84 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
85 .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
88 private final Object mActionDoneSync = new Object();
89 private boolean mActionDone;
90 private Context mContext;
91 private PowerManager mPowerManager;
92 private PowerManager.WakeLock mCpuWakeLock;
93 private PowerManager.WakeLock mScreenWakeLock;
94 private Handler mHandler;
96 private static AlertDialog sConfirmDialog;
98 private ShutdownThread() {
102 * Request a clean shutdown, waiting for subsystems to clean up their
103 * state etc. Must be called from a Looper thread in which its UI
106 * @param context Context used to display the shutdown progress dialog.
107 * @param confirm true if user confirmation is needed before shutting down.
109 public static void shutdown(final Context context, boolean confirm) {
111 mRebootSafeMode = false;
112 shutdownInner(context, confirm);
115 static void shutdownInner(final Context context, boolean confirm) {
116 // ensure that only one thread is trying to power down.
117 // any additional calls are just returned
118 synchronized (sIsStartedGuard) {
120 Log.d(TAG, "Request to shutdown already running, returning.");
125 final int longPressBehavior = context.getResources().getInteger(
126 com.android.internal.R.integer.config_longPressOnPowerBehavior);
127 final int resourceId = mRebootSafeMode
128 ? com.android.internal.R.string.reboot_safemode_confirm
129 : (longPressBehavior == 2
130 ? com.android.internal.R.string.shutdown_confirm_question
131 : com.android.internal.R.string.shutdown_confirm);
133 Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior);
136 final CloseDialogReceiver closer = new CloseDialogReceiver(context);
137 if (sConfirmDialog != null) {
138 sConfirmDialog.dismiss();
140 sConfirmDialog = new AlertDialog.Builder(context)
141 .setTitle(mRebootSafeMode
142 ? com.android.internal.R.string.reboot_safemode_title
143 : com.android.internal.R.string.power_off)
144 .setMessage(resourceId)
145 .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {
146 public void onClick(DialogInterface dialog, int which) {
147 beginShutdownSequence(context);
150 .setNegativeButton(com.android.internal.R.string.no, null)
152 closer.dialog = sConfirmDialog;
153 sConfirmDialog.setOnDismissListener(closer);
154 sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
155 sConfirmDialog.show();
157 beginShutdownSequence(context);
161 private static class CloseDialogReceiver extends BroadcastReceiver
162 implements DialogInterface.OnDismissListener {
163 private Context mContext;
164 public Dialog dialog;
166 CloseDialogReceiver(Context context) {
168 IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
169 context.registerReceiver(this, filter);
173 public void onReceive(Context context, Intent intent) {
177 public void onDismiss(DialogInterface unused) {
178 mContext.unregisterReceiver(this);
183 * Request a clean shutdown, waiting for subsystems to clean up their
184 * state etc. Must be called from a Looper thread in which its UI
187 * @param context Context used to display the shutdown progress dialog.
188 * @param reason code to pass to the kernel (e.g. "recovery"), or null.
189 * @param confirm true if user confirmation is needed before shutting down.
191 public static void reboot(final Context context, String reason, boolean confirm) {
193 mRebootSafeMode = false;
194 mRebootReason = reason;
195 shutdownInner(context, confirm);
199 * Request a reboot into safe mode. Must be called from a Looper thread in which its UI
202 * @param context Context used to display the shutdown progress dialog.
203 * @param confirm true if user confirmation is needed before shutting down.
205 public static void rebootSafeMode(final Context context, boolean confirm) {
206 UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
207 if (um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
212 mRebootSafeMode = true;
213 mRebootReason = null;
214 shutdownInner(context, confirm);
217 private static void beginShutdownSequence(Context context) {
218 synchronized (sIsStartedGuard) {
220 Log.d(TAG, "Shutdown sequence already running, returning.");
226 // throw up an indeterminate system dialog to indicate radio is
228 ProgressDialog pd = new ProgressDialog(context);
229 pd.setTitle(context.getText(com.android.internal.R.string.power_off));
230 pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
231 pd.setIndeterminate(true);
232 pd.setCancelable(false);
233 pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
237 sInstance.mContext = context;
238 sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
240 // make sure we never fall asleep again
241 sInstance.mCpuWakeLock = null;
243 sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock(
244 PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu");
245 sInstance.mCpuWakeLock.setReferenceCounted(false);
246 sInstance.mCpuWakeLock.acquire();
247 } catch (SecurityException e) {
248 Log.w(TAG, "No permission to acquire wake lock", e);
249 sInstance.mCpuWakeLock = null;
252 // also make sure the screen stays on for better user experience
253 sInstance.mScreenWakeLock = null;
254 if (sInstance.mPowerManager.isScreenOn()) {
256 sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock(
257 PowerManager.FULL_WAKE_LOCK, TAG + "-screen");
258 sInstance.mScreenWakeLock.setReferenceCounted(false);
259 sInstance.mScreenWakeLock.acquire();
260 } catch (SecurityException e) {
261 Log.w(TAG, "No permission to acquire wake lock", e);
262 sInstance.mScreenWakeLock = null;
266 // start the thread that initiates shutdown
267 sInstance.mHandler = new Handler() {
273 synchronized (mActionDoneSync) {
275 mActionDoneSync.notifyAll();
280 * Makes sure we handle the shutdown gracefully.
281 * Shuts off power regardless of radio and bluetooth state if the alloted time has passed.
284 BroadcastReceiver br = new BroadcastReceiver() {
285 @Override public void onReceive(Context context, Intent intent) {
286 // We don't allow apps to cancel this, so ignore the result.
292 * Write a system property in case the system_server reboots before we
293 * get to the actual hardware restart. If that happens, we'll retry at
294 * the beginning of the SystemServer startup.
297 String reason = (mReboot ? "1" : "0") + (mRebootReason != null ? mRebootReason : "");
298 SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);
302 * If we are rebooting into safe mode, write a system property
305 if (mRebootSafeMode) {
306 SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");
309 Log.i(TAG, "Sending shutdown broadcast...");
311 // First send the high-level shut down broadcast.
313 Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
314 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
315 mContext.sendOrderedBroadcastAsUser(intent,
316 UserHandle.ALL, null, br, mHandler, 0, null, null);
318 final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
319 synchronized (mActionDoneSync) {
320 while (!mActionDone) {
321 long delay = endTime - SystemClock.elapsedRealtime();
323 Log.w(TAG, "Shutdown broadcast timed out");
327 mActionDoneSync.wait(delay);
328 } catch (InterruptedException e) {
333 Log.i(TAG, "Shutting down activity manager...");
335 final IActivityManager am =
336 ActivityManagerNative.asInterface(ServiceManager.checkService("activity"));
339 am.shutdown(MAX_BROADCAST_TIME);
340 } catch (RemoteException e) {
344 Log.i(TAG, "Shutting down package manager...");
346 final PackageManagerService pm = (PackageManagerService)
347 ServiceManager.getService("package");
353 shutdownRadios(MAX_RADIO_WAIT_TIME);
355 // Shutdown MountService to ensure media is in a safe state
356 IMountShutdownObserver observer = new IMountShutdownObserver.Stub() {
357 public void onShutDownComplete(int statusCode) throws RemoteException {
358 Log.w(TAG, "Result code " + statusCode + " from MountService.shutdown");
363 Log.i(TAG, "Shutting down MountService");
365 // Set initial variables and time out time.
367 final long endShutTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_WAIT_TIME;
368 synchronized (mActionDoneSync) {
370 final IMountService mount = IMountService.Stub.asInterface(
371 ServiceManager.checkService("mount"));
373 mount.shutdown(observer);
375 Log.w(TAG, "MountService unavailable for shutdown");
377 } catch (Exception e) {
378 Log.e(TAG, "Exception during MountService shutdown", e);
380 while (!mActionDone) {
381 long delay = endShutTime - SystemClock.elapsedRealtime();
383 Log.w(TAG, "Shutdown wait timed out");
387 mActionDoneSync.wait(delay);
388 } catch (InterruptedException e) {
393 rebootOrShutdown(mReboot, mRebootReason);
396 private void shutdownRadios(int timeout) {
397 // If a radio is wedged, disabling it may hang so we do this work in another thread,
399 final long endTime = SystemClock.elapsedRealtime() + timeout;
400 final boolean[] done = new boolean[1];
401 Thread t = new Thread() {
404 boolean bluetoothOff;
407 final INfcAdapter nfc =
408 INfcAdapter.Stub.asInterface(ServiceManager.checkService("nfc"));
409 final ITelephony phone =
410 ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
411 final IBluetoothManager bluetooth =
412 IBluetoothManager.Stub.asInterface(ServiceManager.checkService(
413 BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE));
416 nfcOff = nfc == null ||
417 nfc.getState() == NfcAdapter.STATE_OFF;
419 Log.w(TAG, "Turning off NFC...");
420 nfc.disable(false); // Don't persist new state
422 } catch (RemoteException ex) {
423 Log.e(TAG, "RemoteException during NFC shutdown", ex);
428 bluetoothOff = bluetooth == null || !bluetooth.isEnabled();
430 Log.w(TAG, "Disabling Bluetooth...");
431 bluetooth.disable(false); // disable but don't persist new state
433 } catch (RemoteException ex) {
434 Log.e(TAG, "RemoteException during bluetooth shutdown", ex);
439 radioOff = phone == null || !phone.needMobileRadioShutdown();
441 Log.w(TAG, "Turning off cellular radios...");
442 phone.shutdownMobileRadios();
444 } catch (RemoteException ex) {
445 Log.e(TAG, "RemoteException during radio shutdown", ex);
449 Log.i(TAG, "Waiting for NFC, Bluetooth and Radio...");
451 while (SystemClock.elapsedRealtime() < endTime) {
454 bluetoothOff = !bluetooth.isEnabled();
455 } catch (RemoteException ex) {
456 Log.e(TAG, "RemoteException during bluetooth shutdown", ex);
460 Log.i(TAG, "Bluetooth turned off.");
465 radioOff = !phone.needMobileRadioShutdown();
466 } catch (RemoteException ex) {
467 Log.e(TAG, "RemoteException during radio shutdown", ex);
471 Log.i(TAG, "Radio turned off.");
476 nfcOff = nfc.getState() == NfcAdapter.STATE_OFF;
477 } catch (RemoteException ex) {
478 Log.e(TAG, "RemoteException during NFC shutdown", ex);
482 Log.i(TAG, "NFC turned off.");
486 if (radioOff && bluetoothOff && nfcOff) {
487 Log.i(TAG, "NFC, Radio and Bluetooth shutdown complete.");
491 SystemClock.sleep(PHONE_STATE_POLL_SLEEP_MSEC);
499 } catch (InterruptedException ex) {
502 Log.w(TAG, "Timed out waiting for NFC, Radio and Bluetooth shutdown.");
507 * Do not call this directly. Use {@link #reboot(Context, String, boolean)}
508 * or {@link #shutdown(Context, boolean)} instead.
510 * @param reboot true to reboot or false to shutdown
511 * @param reason reason for reboot
513 public static void rebootOrShutdown(boolean reboot, String reason) {
515 Log.i(TAG, "Rebooting, reason: " + reason);
516 PowerManagerService.lowLevelReboot(reason);
517 Log.e(TAG, "Reboot failed, will attempt shutdown instead");
518 } else if (SHUTDOWN_VIBRATE_MS > 0) {
519 // vibrate before shutting down
520 Vibrator vibrator = new SystemVibrator();
522 vibrator.vibrate(SHUTDOWN_VIBRATE_MS, VIBRATION_ATTRIBUTES);
523 } catch (Exception e) {
524 // Failure to vibrate shouldn't interrupt shutdown. Just log it.
525 Log.w(TAG, "Failed to vibrate during shutdown.", e);
528 // vibrator is asynchronous so we need to wait to avoid shutting down too soon.
530 Thread.sleep(SHUTDOWN_VIBRATE_MS);
531 } catch (InterruptedException unused) {
536 Log.i(TAG, "Performing low-level shutdown...");
537 PowerManagerService.lowLevelShutdown();