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.FileUtils;
36 import android.os.Handler;
37 import android.os.PowerManager;
38 import android.os.RecoverySystem;
39 import android.os.RemoteException;
40 import android.os.ServiceManager;
41 import android.os.SystemClock;
42 import android.os.SystemProperties;
43 import android.os.UserHandle;
44 import android.os.UserManager;
45 import android.os.Vibrator;
46 import android.os.SystemVibrator;
47 import android.os.storage.IMountService;
48 import android.os.storage.IMountShutdownObserver;
49 import android.system.ErrnoException;
50 import android.system.Os;
52 import com.android.internal.telephony.ITelephony;
53 import com.android.server.pm.PackageManagerService;
55 import android.util.Log;
56 import android.view.WindowManager;
58 import java.io.BufferedReader;
60 import java.io.FileReader;
61 import java.io.IOException;
63 public final class ShutdownThread extends Thread {
65 private static final String TAG = "ShutdownThread";
66 private static final int PHONE_STATE_POLL_SLEEP_MSEC = 500;
67 // maximum time we wait for the shutdown broadcast before going on.
68 private static final int MAX_BROADCAST_TIME = 10*1000;
69 private static final int MAX_SHUTDOWN_WAIT_TIME = 20*1000;
70 private static final int MAX_RADIO_WAIT_TIME = 12*1000;
71 private static final int MAX_UNCRYPT_WAIT_TIME = 15*60*1000;
72 // constants for progress bar. the values are roughly estimated based on timeout.
73 private static final int BROADCAST_STOP_PERCENT = 2;
74 private static final int ACTIVITY_MANAGER_STOP_PERCENT = 4;
75 private static final int PACKAGE_MANAGER_STOP_PERCENT = 6;
76 private static final int RADIO_STOP_PERCENT = 18;
77 private static final int MOUNT_SERVICE_STOP_PERCENT = 20;
79 // length of vibration before shutting down
80 private static final int SHUTDOWN_VIBRATE_MS = 500;
83 private static Object sIsStartedGuard = new Object();
84 private static boolean sIsStarted = false;
86 private static boolean mReboot;
87 private static boolean mRebootSafeMode;
88 private static boolean mRebootHasProgressBar;
89 private static String mReason;
91 // Provides shutdown assurance in case the system_server is killed
92 public static final String SHUTDOWN_ACTION_PROPERTY = "sys.shutdown.requested";
94 // Indicates whether we are rebooting into safe mode
95 public static final String REBOOT_SAFEMODE_PROPERTY = "persist.sys.safemode";
96 public static final String RO_SAFEMODE_PROPERTY = "ro.sys.safemode";
98 // Indicates whether we should stay in safe mode until ro.build.date.utc is newer than this
99 public static final String AUDIT_SAFEMODE_PROPERTY = "persist.sys.audit_safemode";
101 // static instance of this thread
102 private static final ShutdownThread sInstance = new ShutdownThread();
104 private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
105 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
106 .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
109 private final Object mActionDoneSync = new Object();
110 private boolean mActionDone;
111 private Context mContext;
112 private PowerManager mPowerManager;
113 private PowerManager.WakeLock mCpuWakeLock;
114 private PowerManager.WakeLock mScreenWakeLock;
115 private Handler mHandler;
117 private static AlertDialog sConfirmDialog;
118 private ProgressDialog mProgressDialog;
120 private ShutdownThread() {
124 * Request a clean shutdown, waiting for subsystems to clean up their
125 * state etc. Must be called from a Looper thread in which its UI
128 * @param context Context used to display the shutdown progress dialog.
129 * @param reason code to pass to android_reboot() (e.g. "userrequested"), or null.
130 * @param confirm true if user confirmation is needed before shutting down.
132 public static void shutdown(final Context context, String reason, boolean confirm) {
134 mRebootSafeMode = false;
136 shutdownInner(context, confirm);
139 static void shutdownInner(final Context context, boolean confirm) {
140 // ensure that only one thread is trying to power down.
141 // any additional calls are just returned
142 synchronized (sIsStartedGuard) {
144 Log.d(TAG, "Request to shutdown already running, returning.");
149 final int longPressBehavior = context.getResources().getInteger(
150 com.android.internal.R.integer.config_longPressOnPowerBehavior);
151 final int resourceId = mRebootSafeMode
152 ? com.android.internal.R.string.reboot_safemode_confirm
153 : (longPressBehavior == 2
154 ? com.android.internal.R.string.shutdown_confirm_question
155 : com.android.internal.R.string.shutdown_confirm);
157 Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior);
160 final CloseDialogReceiver closer = new CloseDialogReceiver(context);
161 if (sConfirmDialog != null) {
162 sConfirmDialog.dismiss();
164 sConfirmDialog = new AlertDialog.Builder(context)
165 .setTitle(mRebootSafeMode
166 ? com.android.internal.R.string.reboot_safemode_title
167 : com.android.internal.R.string.power_off)
168 .setMessage(resourceId)
169 .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {
170 public void onClick(DialogInterface dialog, int which) {
171 beginShutdownSequence(context);
174 .setNegativeButton(com.android.internal.R.string.no, null)
176 closer.dialog = sConfirmDialog;
177 sConfirmDialog.setOnDismissListener(closer);
178 sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
179 sConfirmDialog.show();
181 beginShutdownSequence(context);
185 private static class CloseDialogReceiver extends BroadcastReceiver
186 implements DialogInterface.OnDismissListener {
187 private Context mContext;
188 public Dialog dialog;
190 CloseDialogReceiver(Context context) {
192 IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
193 context.registerReceiver(this, filter);
197 public void onReceive(Context context, Intent intent) {
201 public void onDismiss(DialogInterface unused) {
202 mContext.unregisterReceiver(this);
207 * Request a clean shutdown, waiting for subsystems to clean up their
208 * state etc. Must be called from a Looper thread in which its UI
211 * @param context Context used to display the shutdown progress dialog.
212 * @param reason code to pass to the kernel (e.g. "recovery"), or null.
213 * @param confirm true if user confirmation is needed before shutting down.
215 public static void reboot(final Context context, String reason, boolean confirm) {
217 mRebootSafeMode = false;
218 mRebootHasProgressBar = false;
220 shutdownInner(context, confirm);
224 * Request a reboot into safe mode. Must be called from a Looper thread in which its UI
227 * @param context Context used to display the shutdown progress dialog.
228 * @param confirm true if user confirmation is needed before shutting down.
230 public static void rebootSafeMode(final Context context, boolean confirm) {
231 UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
232 if (um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
237 mRebootSafeMode = true;
238 mRebootHasProgressBar = false;
240 shutdownInner(context, confirm);
243 private static void beginShutdownSequence(Context context) {
244 synchronized (sIsStartedGuard) {
246 Log.d(TAG, "Shutdown sequence already running, returning.");
252 // Throw up a system dialog to indicate the device is rebooting / shutting down.
253 ProgressDialog pd = new ProgressDialog(context);
255 // Path 1: Reboot to recovery for update
256 // Condition: mReason == REBOOT_RECOVERY_UPDATE
258 // Path 1a: uncrypt needed
259 // Condition: if /cache/recovery/uncrypt_file exists but
260 // /cache/recovery/block.map doesn't.
261 // UI: determinate progress bar (mRebootHasProgressBar == True)
263 // * Path 1a is expected to be removed once the GmsCore shipped on
264 // device always calls uncrypt prior to reboot.
266 // Path 1b: uncrypt already done
267 // UI: spinning circle only (no progress bar)
269 // Path 2: Reboot to recovery for factory reset
270 // Condition: mReason == REBOOT_RECOVERY
271 // UI: spinning circle only (no progress bar)
273 // Path 3: Regular reboot / shutdown
274 // Condition: Otherwise
275 // UI: spinning circle only (no progress bar)
276 if (PowerManager.REBOOT_RECOVERY_UPDATE.equals(mReason)) {
277 // We need the progress bar if uncrypt will be invoked during the
278 // reboot, which might be time-consuming.
279 mRebootHasProgressBar = RecoverySystem.UNCRYPT_PACKAGE_FILE.exists()
280 && !(RecoverySystem.BLOCK_MAP_FILE.exists());
281 pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_update_title));
282 if (mRebootHasProgressBar) {
285 pd.setIndeterminate(false);
286 pd.setProgressNumberFormat(null);
287 pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
288 pd.setMessage(context.getText(
289 com.android.internal.R.string.reboot_to_update_prepare));
291 pd.setIndeterminate(true);
292 pd.setMessage(context.getText(
293 com.android.internal.R.string.reboot_to_update_reboot));
295 } else if (PowerManager.REBOOT_RECOVERY.equals(mReason)) {
296 // Factory reset path. Set the dialog message accordingly.
297 pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_reset_title));
298 pd.setMessage(context.getText(
299 com.android.internal.R.string.reboot_to_reset_message));
300 pd.setIndeterminate(true);
302 pd.setTitle(context.getText(com.android.internal.R.string.power_off));
303 pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
304 pd.setIndeterminate(true);
306 pd.setCancelable(false);
307 pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
311 sInstance.mProgressDialog = pd;
312 sInstance.mContext = context;
313 sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
315 // make sure we never fall asleep again
316 sInstance.mCpuWakeLock = null;
318 sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock(
319 PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu");
320 sInstance.mCpuWakeLock.setReferenceCounted(false);
321 sInstance.mCpuWakeLock.acquire();
322 } catch (SecurityException e) {
323 Log.w(TAG, "No permission to acquire wake lock", e);
324 sInstance.mCpuWakeLock = null;
327 // also make sure the screen stays on for better user experience
328 sInstance.mScreenWakeLock = null;
329 if (sInstance.mPowerManager.isScreenOn()) {
331 sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock(
332 PowerManager.FULL_WAKE_LOCK, TAG + "-screen");
333 sInstance.mScreenWakeLock.setReferenceCounted(false);
334 sInstance.mScreenWakeLock.acquire();
335 } catch (SecurityException e) {
336 Log.w(TAG, "No permission to acquire wake lock", e);
337 sInstance.mScreenWakeLock = null;
341 // start the thread that initiates shutdown
342 sInstance.mHandler = new Handler() {
348 synchronized (mActionDoneSync) {
350 mActionDoneSync.notifyAll();
355 * Makes sure we handle the shutdown gracefully.
356 * Shuts off power regardless of radio and bluetooth state if the alloted time has passed.
359 BroadcastReceiver br = new BroadcastReceiver() {
360 @Override public void onReceive(Context context, Intent intent) {
361 // We don't allow apps to cancel this, so ignore the result.
367 * Write a system property in case the system_server reboots before we
368 * get to the actual hardware restart. If that happens, we'll retry at
369 * the beginning of the SystemServer startup.
372 String reason = (mReboot ? "1" : "0") + (mReason != null ? mReason : "");
373 SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);
377 * If we are rebooting into safe mode, write a system property
380 if (mRebootSafeMode) {
381 SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");
384 Log.i(TAG, "Sending shutdown broadcast...");
386 // First send the high-level shut down broadcast.
388 Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
389 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
390 mContext.sendOrderedBroadcastAsUser(intent,
391 UserHandle.ALL, null, br, mHandler, 0, null, null);
393 final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
394 synchronized (mActionDoneSync) {
395 while (!mActionDone) {
396 long delay = endTime - SystemClock.elapsedRealtime();
398 Log.w(TAG, "Shutdown broadcast timed out");
400 } else if (mRebootHasProgressBar) {
401 int status = (int)((MAX_BROADCAST_TIME - delay) * 1.0 *
402 BROADCAST_STOP_PERCENT / MAX_BROADCAST_TIME);
403 sInstance.setRebootProgress(status, null);
406 mActionDoneSync.wait(Math.min(delay, PHONE_STATE_POLL_SLEEP_MSEC));
407 } catch (InterruptedException e) {
411 if (mRebootHasProgressBar) {
412 sInstance.setRebootProgress(BROADCAST_STOP_PERCENT, null);
415 Log.i(TAG, "Shutting down activity manager...");
417 final IActivityManager am =
418 ActivityManagerNative.asInterface(ServiceManager.checkService("activity"));
421 am.shutdown(MAX_BROADCAST_TIME);
422 } catch (RemoteException e) {
425 if (mRebootHasProgressBar) {
426 sInstance.setRebootProgress(ACTIVITY_MANAGER_STOP_PERCENT, null);
429 Log.i(TAG, "Shutting down package manager...");
431 final PackageManagerService pm = (PackageManagerService)
432 ServiceManager.getService("package");
436 if (mRebootHasProgressBar) {
437 sInstance.setRebootProgress(PACKAGE_MANAGER_STOP_PERCENT, null);
441 shutdownRadios(MAX_RADIO_WAIT_TIME);
442 if (mRebootHasProgressBar) {
443 sInstance.setRebootProgress(RADIO_STOP_PERCENT, null);
446 // Shutdown MountService to ensure media is in a safe state
447 IMountShutdownObserver observer = new IMountShutdownObserver.Stub() {
448 public void onShutDownComplete(int statusCode) throws RemoteException {
449 Log.w(TAG, "Result code " + statusCode + " from MountService.shutdown");
454 Log.i(TAG, "Shutting down MountService");
456 // Set initial variables and time out time.
458 final long endShutTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_WAIT_TIME;
459 synchronized (mActionDoneSync) {
461 final IMountService mount = IMountService.Stub.asInterface(
462 ServiceManager.checkService("mount"));
464 mount.shutdown(observer);
466 Log.w(TAG, "MountService unavailable for shutdown");
468 } catch (Exception e) {
469 Log.e(TAG, "Exception during MountService shutdown", e);
471 while (!mActionDone) {
472 long delay = endShutTime - SystemClock.elapsedRealtime();
474 Log.w(TAG, "Shutdown wait timed out");
476 } else if (mRebootHasProgressBar) {
477 int status = (int)((MAX_SHUTDOWN_WAIT_TIME - delay) * 1.0 *
478 (MOUNT_SERVICE_STOP_PERCENT - RADIO_STOP_PERCENT) /
479 MAX_SHUTDOWN_WAIT_TIME);
480 status += RADIO_STOP_PERCENT;
481 sInstance.setRebootProgress(status, null);
484 mActionDoneSync.wait(Math.min(delay, PHONE_STATE_POLL_SLEEP_MSEC));
485 } catch (InterruptedException e) {
489 if (mRebootHasProgressBar) {
490 sInstance.setRebootProgress(MOUNT_SERVICE_STOP_PERCENT, null);
492 // If it's to reboot to install an update and uncrypt hasn't been
493 // done yet, trigger it now.
497 rebootOrShutdown(mContext, mReboot, mReason);
500 private void setRebootProgress(final int progress, final CharSequence message) {
501 mHandler.post(new Runnable() {
504 if (mProgressDialog != null) {
505 mProgressDialog.setProgress(progress);
506 if (message != null) {
507 mProgressDialog.setMessage(message);
514 private void shutdownRadios(final int timeout) {
515 // If a radio is wedged, disabling it may hang so we do this work in another thread,
517 final long endTime = SystemClock.elapsedRealtime() + timeout;
518 final boolean[] done = new boolean[1];
519 Thread t = new Thread() {
522 boolean bluetoothOff;
525 final INfcAdapter nfc =
526 INfcAdapter.Stub.asInterface(ServiceManager.checkService("nfc"));
527 final ITelephony phone =
528 ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
529 final IBluetoothManager bluetooth =
530 IBluetoothManager.Stub.asInterface(ServiceManager.checkService(
531 BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE));
534 nfcOff = nfc == null ||
535 nfc.getState() == NfcAdapter.STATE_OFF;
537 Log.w(TAG, "Turning off NFC...");
538 nfc.disable(false); // Don't persist new state
540 } catch (RemoteException ex) {
541 Log.e(TAG, "RemoteException during NFC shutdown", ex);
546 bluetoothOff = bluetooth == null ||
547 bluetooth.getState() == BluetoothAdapter.STATE_OFF;
549 Log.w(TAG, "Disabling Bluetooth...");
550 bluetooth.disable(false); // disable but don't persist new state
552 } catch (RemoteException ex) {
553 Log.e(TAG, "RemoteException during bluetooth shutdown", ex);
558 radioOff = phone == null || !phone.needMobileRadioShutdown();
560 Log.w(TAG, "Turning off cellular radios...");
561 phone.shutdownMobileRadios();
563 } catch (RemoteException ex) {
564 Log.e(TAG, "RemoteException during radio shutdown", ex);
568 Log.i(TAG, "Waiting for NFC, Bluetooth and Radio...");
570 long delay = endTime - SystemClock.elapsedRealtime();
572 if (mRebootHasProgressBar) {
573 int status = (int)((timeout - delay) * 1.0 *
574 (RADIO_STOP_PERCENT - PACKAGE_MANAGER_STOP_PERCENT) / timeout);
575 status += PACKAGE_MANAGER_STOP_PERCENT;
576 sInstance.setRebootProgress(status, null);
581 bluetoothOff = bluetooth.getState() == BluetoothAdapter.STATE_OFF;
582 } catch (RemoteException ex) {
583 Log.e(TAG, "RemoteException during bluetooth shutdown", ex);
587 Log.i(TAG, "Bluetooth turned off.");
592 radioOff = !phone.needMobileRadioShutdown();
593 } catch (RemoteException ex) {
594 Log.e(TAG, "RemoteException during radio shutdown", ex);
598 Log.i(TAG, "Radio turned off.");
603 nfcOff = nfc.getState() == NfcAdapter.STATE_OFF;
604 } catch (RemoteException ex) {
605 Log.e(TAG, "RemoteException during NFC shutdown", ex);
609 Log.i(TAG, "NFC turned off.");
613 if (radioOff && bluetoothOff && nfcOff) {
614 Log.i(TAG, "NFC, Radio and Bluetooth shutdown complete.");
618 SystemClock.sleep(PHONE_STATE_POLL_SLEEP_MSEC);
620 delay = endTime - SystemClock.elapsedRealtime();
628 } catch (InterruptedException ex) {
631 Log.w(TAG, "Timed out waiting for NFC, Radio and Bluetooth shutdown.");
636 * Do not call this directly. Use {@link #reboot(Context, String, boolean)}
637 * or {@link #shutdown(Context, boolean)} instead.
639 * @param context Context used to vibrate or null without vibration
640 * @param reboot true to reboot or false to shutdown
641 * @param reason reason for reboot/shutdown
643 public static void rebootOrShutdown(final Context context, boolean reboot, String reason) {
645 Log.i(TAG, "Rebooting, reason: " + reason);
646 PowerManagerService.lowLevelReboot(reason);
647 Log.e(TAG, "Reboot failed, will attempt shutdown instead");
649 } else if (SHUTDOWN_VIBRATE_MS > 0 && context != null) {
650 // vibrate before shutting down
651 Vibrator vibrator = new SystemVibrator(context);
653 vibrator.vibrate(SHUTDOWN_VIBRATE_MS, VIBRATION_ATTRIBUTES);
654 } catch (Exception e) {
655 // Failure to vibrate shouldn't interrupt shutdown. Just log it.
656 Log.w(TAG, "Failed to vibrate during shutdown.", e);
659 // vibrator is asynchronous so we need to wait to avoid shutting down too soon.
661 Thread.sleep(SHUTDOWN_VIBRATE_MS);
662 } catch (InterruptedException unused) {
667 Log.i(TAG, "Performing low-level shutdown...");
668 PowerManagerService.lowLevelShutdown(reason);
671 private void uncrypt() {
672 Log.i(TAG, "Calling uncrypt and monitoring the progress...");
674 final RecoverySystem.ProgressListener progressListener =
675 new RecoverySystem.ProgressListener() {
677 public void onProgress(int status) {
678 if (status >= 0 && status < 100) {
679 // Scale down to [MOUNT_SERVICE_STOP_PERCENT, 100).
680 status = (int)(status * (100.0 - MOUNT_SERVICE_STOP_PERCENT) / 100);
681 status += MOUNT_SERVICE_STOP_PERCENT;
682 CharSequence msg = mContext.getText(
683 com.android.internal.R.string.reboot_to_update_package);
684 sInstance.setRebootProgress(status, msg);
685 } else if (status == 100) {
686 CharSequence msg = mContext.getText(
687 com.android.internal.R.string.reboot_to_update_reboot);
688 sInstance.setRebootProgress(status, msg);
695 final boolean[] done = new boolean[1];
697 Thread t = new Thread() {
700 RecoverySystem rs = (RecoverySystem) mContext.getSystemService(
701 Context.RECOVERY_SERVICE);
702 String filename = null;
704 filename = FileUtils.readTextFile(RecoverySystem.UNCRYPT_PACKAGE_FILE, 0, null);
705 rs.processPackage(mContext, new File(filename), progressListener);
706 } catch (IOException e) {
707 Log.e(TAG, "Error uncrypting file", e);
715 t.join(MAX_UNCRYPT_WAIT_TIME);
716 } catch (InterruptedException unused) {
719 Log.w(TAG, "Timed out waiting for uncrypt.");
720 final int uncryptTimeoutError = 100;
721 String timeoutMessage = String.format("uncrypt_time: %d\n" + "uncrypt_error: %d\n",
722 MAX_UNCRYPT_WAIT_TIME / 1000, uncryptTimeoutError);
724 FileUtils.stringToFile(RecoverySystem.UNCRYPT_STATUS_FILE, timeoutMessage);
725 } catch (IOException e) {
726 Log.e(TAG, "Failed to write timeout message to uncrypt status", e);