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.AlertDialog;
21 import android.app.Dialog;
22 import android.app.IActivityManager;
23 import android.app.KeyguardManager;
24 import android.app.ProgressDialog;
25 import android.app.WallpaperColors;
26 import android.app.WallpaperManager;
27 import android.bluetooth.BluetoothAdapter;
28 import android.bluetooth.IBluetoothManager;
29 import android.content.BroadcastReceiver;
30 import android.content.Context;
31 import android.content.DialogInterface;
32 import android.content.Intent;
33 import android.content.IntentFilter;
34 import android.graphics.Color;
35 import android.graphics.drawable.ColorDrawable;
36 import android.media.AudioAttributes;
37 import android.nfc.INfcAdapter;
38 import android.nfc.NfcAdapter;
39 import android.os.FileUtils;
40 import android.os.Handler;
41 import android.os.PowerManager;
42 import android.os.RecoverySystem;
43 import android.os.RemoteException;
44 import android.os.ServiceManager;
45 import android.os.SystemClock;
46 import android.os.SystemProperties;
47 import android.os.SystemVibrator;
48 import android.os.Trace;
49 import android.os.UserHandle;
50 import android.os.UserManager;
51 import android.os.Vibrator;
52 import android.os.storage.IStorageManager;
53 import android.os.storage.IStorageShutdownObserver;
54 import android.util.ArrayMap;
55 import android.util.Log;
56 import android.util.TimingsTraceLog;
57 import android.view.WindowManager;
59 import com.android.internal.telephony.ITelephony;
60 import com.android.server.RescueParty;
61 import com.android.server.LocalServices;
62 import com.android.server.pm.PackageManagerService;
63 import com.android.server.statusbar.StatusBarManagerInternal;
66 import java.io.FileOutputStream;
67 import java.io.IOException;
68 import java.nio.charset.StandardCharsets;
70 public final class ShutdownThread extends Thread {
72 private static final String TAG = "ShutdownThread";
73 private static final int PHONE_STATE_POLL_SLEEP_MSEC = 500;
74 // maximum time we wait for the shutdown broadcast before going on.
75 private static final int MAX_BROADCAST_TIME = 10*1000;
76 private static final int MAX_SHUTDOWN_WAIT_TIME = 20*1000;
77 private static final int MAX_RADIO_WAIT_TIME = 12*1000;
78 private static final int MAX_UNCRYPT_WAIT_TIME = 15*60*1000;
79 // constants for progress bar. the values are roughly estimated based on timeout.
80 private static final int BROADCAST_STOP_PERCENT = 2;
81 private static final int ACTIVITY_MANAGER_STOP_PERCENT = 4;
82 private static final int PACKAGE_MANAGER_STOP_PERCENT = 6;
83 private static final int RADIO_STOP_PERCENT = 18;
84 private static final int MOUNT_SERVICE_STOP_PERCENT = 20;
86 // length of vibration before shutting down
87 private static final int SHUTDOWN_VIBRATE_MS = 500;
90 private static final Object sIsStartedGuard = new Object();
91 private static boolean sIsStarted = false;
93 private static boolean mReboot;
94 private static boolean mRebootSafeMode;
95 private static boolean mRebootHasProgressBar;
96 private static String mReason;
98 // Provides shutdown assurance in case the system_server is killed
99 public static final String SHUTDOWN_ACTION_PROPERTY = "sys.shutdown.requested";
101 // Indicates whether we are rebooting into safe mode
102 public static final String REBOOT_SAFEMODE_PROPERTY = "persist.sys.safemode";
103 public static final String RO_SAFEMODE_PROPERTY = "ro.sys.safemode";
105 // static instance of this thread
106 private static final ShutdownThread sInstance = new ShutdownThread();
108 private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
109 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
110 .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
113 // Metrics that will be reported to tron after reboot
114 private static final ArrayMap<String, Long> TRON_METRICS = new ArrayMap<>();
116 // File to use for save metrics
117 private static final String METRICS_FILE_BASENAME = "/data/system/shutdown-metrics";
119 // Metrics names to be persisted in shutdown-metrics file
120 private static String METRIC_SYSTEM_SERVER = "shutdown_system_server";
121 private static String METRIC_SEND_BROADCAST = "shutdown_send_shutdown_broadcast";
122 private static String METRIC_AM = "shutdown_activity_manager";
123 private static String METRIC_PM = "shutdown_package_manager";
124 private static String METRIC_RADIOS = "shutdown_radios";
125 private static String METRIC_BT = "shutdown_bt";
126 private static String METRIC_RADIO = "shutdown_radio";
127 private static String METRIC_NFC = "shutdown_nfc";
128 private static String METRIC_SM = "shutdown_storage_manager";
130 private final Object mActionDoneSync = new Object();
131 private boolean mActionDone;
132 private Context mContext;
133 private PowerManager mPowerManager;
134 private PowerManager.WakeLock mCpuWakeLock;
135 private PowerManager.WakeLock mScreenWakeLock;
136 private Handler mHandler;
138 private static AlertDialog sConfirmDialog;
139 private ProgressDialog mProgressDialog;
141 private ShutdownThread() {
145 * Request a clean shutdown, waiting for subsystems to clean up their
146 * state etc. Must be called from a Looper thread in which its UI
149 * @param context Context used to display the shutdown progress dialog. This must be a context
150 * suitable for displaying UI (aka Themable).
151 * @param reason code to pass to android_reboot() (e.g. "userrequested"), or null.
152 * @param confirm true if user confirmation is needed before shutting down.
154 public static void shutdown(final Context context, String reason, boolean confirm) {
156 mRebootSafeMode = false;
158 shutdownInner(context, confirm);
161 private static void shutdownInner(final Context context, boolean confirm) {
162 // ShutdownThread is called from many places, so best to verify here that the context passed
164 context.assertRuntimeOverlayThemable();
166 // ensure that only one thread is trying to power down.
167 // any additional calls are just returned
168 synchronized (sIsStartedGuard) {
170 Log.d(TAG, "Request to shutdown already running, returning.");
175 final int longPressBehavior = context.getResources().getInteger(
176 com.android.internal.R.integer.config_longPressOnPowerBehavior);
177 final int resourceId = mRebootSafeMode
178 ? com.android.internal.R.string.reboot_safemode_confirm
179 : (longPressBehavior == 2
180 ? com.android.internal.R.string.shutdown_confirm_question
181 : com.android.internal.R.string.shutdown_confirm);
183 Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior);
186 final CloseDialogReceiver closer = new CloseDialogReceiver(context);
187 if (sConfirmDialog != null) {
188 sConfirmDialog.dismiss();
190 sConfirmDialog = new AlertDialog.Builder(context)
191 .setTitle(mRebootSafeMode
192 ? com.android.internal.R.string.reboot_safemode_title
193 : com.android.internal.R.string.power_off)
194 .setMessage(resourceId)
195 .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {
196 public void onClick(DialogInterface dialog, int which) {
197 beginShutdownSequence(context);
200 .setNegativeButton(com.android.internal.R.string.no, null)
202 closer.dialog = sConfirmDialog;
203 sConfirmDialog.setOnDismissListener(closer);
204 sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
205 sConfirmDialog.show();
207 beginShutdownSequence(context);
211 private static class CloseDialogReceiver extends BroadcastReceiver
212 implements DialogInterface.OnDismissListener {
213 private Context mContext;
214 public Dialog dialog;
216 CloseDialogReceiver(Context context) {
218 IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
219 context.registerReceiver(this, filter);
223 public void onReceive(Context context, Intent intent) {
227 public void onDismiss(DialogInterface unused) {
228 mContext.unregisterReceiver(this);
233 * Request a clean shutdown, waiting for subsystems to clean up their
234 * state etc. Must be called from a Looper thread in which its UI
237 * @param context Context used to display the shutdown progress dialog. This must be a context
238 * suitable for displaying UI (aka Themable).
239 * @param reason code to pass to the kernel (e.g. "recovery"), or null.
240 * @param confirm true if user confirmation is needed before shutting down.
242 public static void reboot(final Context context, String reason, boolean confirm) {
244 mRebootSafeMode = false;
245 mRebootHasProgressBar = false;
247 shutdownInner(context, confirm);
251 * Request a reboot into safe mode. Must be called from a Looper thread in which its UI
254 * @param context Context used to display the shutdown progress dialog. This must be a context
255 * suitable for displaying UI (aka Themable).
256 * @param confirm true if user confirmation is needed before shutting down.
258 public static void rebootSafeMode(final Context context, boolean confirm) {
259 UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
260 if (um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
265 mRebootSafeMode = true;
266 mRebootHasProgressBar = false;
268 shutdownInner(context, confirm);
271 private static ProgressDialog showShutdownDialog(Context context) {
272 // Throw up a system dialog to indicate the device is rebooting / shutting down.
273 ProgressDialog pd = new ProgressDialog(context);
275 // Path 1: Reboot to recovery for update
276 // Condition: mReason startswith REBOOT_RECOVERY_UPDATE
278 // Path 1a: uncrypt needed
279 // Condition: if /cache/recovery/uncrypt_file exists but
280 // /cache/recovery/block.map doesn't.
281 // UI: determinate progress bar (mRebootHasProgressBar == True)
283 // * Path 1a is expected to be removed once the GmsCore shipped on
284 // device always calls uncrypt prior to reboot.
286 // Path 1b: uncrypt already done
287 // UI: spinning circle only (no progress bar)
289 // Path 2: Reboot to recovery for factory reset
290 // Condition: mReason == REBOOT_RECOVERY
291 // UI: spinning circle only (no progress bar)
293 // Path 3: Regular reboot / shutdown
294 // Condition: Otherwise
295 // UI: spinning circle only (no progress bar)
297 // mReason could be "recovery-update" or "recovery-update,quiescent".
298 if (mReason != null && mReason.startsWith(PowerManager.REBOOT_RECOVERY_UPDATE)) {
299 // We need the progress bar if uncrypt will be invoked during the
300 // reboot, which might be time-consuming.
301 mRebootHasProgressBar = RecoverySystem.UNCRYPT_PACKAGE_FILE.exists()
302 && !(RecoverySystem.BLOCK_MAP_FILE.exists());
303 pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_update_title));
304 if (mRebootHasProgressBar) {
307 pd.setIndeterminate(false);
308 pd.setProgressNumberFormat(null);
309 pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
310 pd.setMessage(context.getText(
311 com.android.internal.R.string.reboot_to_update_prepare));
313 if (showSysuiReboot()) {
316 pd.setIndeterminate(true);
317 pd.setMessage(context.getText(
318 com.android.internal.R.string.reboot_to_update_reboot));
320 } else if (mReason != null && mReason.equals(PowerManager.REBOOT_RECOVERY)) {
321 if (RescueParty.isAttemptingFactoryReset()) {
322 // We're not actually doing a factory reset yet; we're rebooting
323 // to ask the user if they'd like to reset, so give them a less
324 // scary dialog message.
325 pd.setTitle(context.getText(com.android.internal.R.string.power_off));
326 pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
327 pd.setIndeterminate(true);
329 // Factory reset path. Set the dialog message accordingly.
330 pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_reset_title));
331 pd.setMessage(context.getText(
332 com.android.internal.R.string.reboot_to_reset_message));
333 pd.setIndeterminate(true);
336 if (showSysuiReboot()) {
339 pd.setTitle(context.getText(com.android.internal.R.string.power_off));
340 pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
341 pd.setIndeterminate(true);
343 pd.setCancelable(false);
344 pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
350 private static boolean showSysuiReboot() {
351 Log.d(TAG, "Attempting to use SysUI shutdown UI");
353 StatusBarManagerInternal service = LocalServices.getService(
354 StatusBarManagerInternal.class);
355 if (service.showShutdownUi(mReboot, mReason)) {
356 // Sysui will handle shutdown UI.
357 Log.d(TAG, "SysUI handling shutdown UI");
360 } catch (Exception e) {
361 // If anything went wrong, ignore it and use fallback ui
363 Log.d(TAG, "SysUI is unavailable");
367 private static void beginShutdownSequence(Context context) {
368 synchronized (sIsStartedGuard) {
370 Log.d(TAG, "Shutdown sequence already running, returning.");
376 sInstance.mProgressDialog = showShutdownDialog(context);
377 sInstance.mContext = context;
378 sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
380 // make sure we never fall asleep again
381 sInstance.mCpuWakeLock = null;
383 sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock(
384 PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu");
385 sInstance.mCpuWakeLock.setReferenceCounted(false);
386 sInstance.mCpuWakeLock.acquire();
387 } catch (SecurityException e) {
388 Log.w(TAG, "No permission to acquire wake lock", e);
389 sInstance.mCpuWakeLock = null;
392 // also make sure the screen stays on for better user experience
393 sInstance.mScreenWakeLock = null;
394 if (sInstance.mPowerManager.isScreenOn()) {
396 sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock(
397 PowerManager.FULL_WAKE_LOCK, TAG + "-screen");
398 sInstance.mScreenWakeLock.setReferenceCounted(false);
399 sInstance.mScreenWakeLock.acquire();
400 } catch (SecurityException e) {
401 Log.w(TAG, "No permission to acquire wake lock", e);
402 sInstance.mScreenWakeLock = null;
406 // start the thread that initiates shutdown
407 sInstance.mHandler = new Handler() {
413 synchronized (mActionDoneSync) {
415 mActionDoneSync.notifyAll();
420 * Makes sure we handle the shutdown gracefully.
421 * Shuts off power regardless of radio and bluetooth state if the alloted time has passed.
424 TimingsTraceLog shutdownTimingLog = newTimingsLog();
425 shutdownTimingLog.traceBegin("SystemServerShutdown");
426 metricStarted(METRIC_SYSTEM_SERVER);
428 BroadcastReceiver br = new BroadcastReceiver() {
429 @Override public void onReceive(Context context, Intent intent) {
430 // We don't allow apps to cancel this, so ignore the result.
436 * Write a system property in case the system_server reboots before we
437 * get to the actual hardware restart. If that happens, we'll retry at
438 * the beginning of the SystemServer startup.
441 String reason = (mReboot ? "1" : "0") + (mReason != null ? mReason : "");
442 SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);
446 * If we are rebooting into safe mode, write a system property
449 if (mRebootSafeMode) {
450 SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");
453 metricStarted(METRIC_SEND_BROADCAST);
454 shutdownTimingLog.traceBegin("SendShutdownBroadcast");
455 Log.i(TAG, "Sending shutdown broadcast...");
457 // First send the high-level shut down broadcast.
459 Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
460 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND
461 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
462 mContext.sendOrderedBroadcastAsUser(intent,
463 UserHandle.ALL, null, br, mHandler, 0, null, null);
465 final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
466 synchronized (mActionDoneSync) {
467 while (!mActionDone) {
468 long delay = endTime - SystemClock.elapsedRealtime();
470 Log.w(TAG, "Shutdown broadcast timed out");
472 } else if (mRebootHasProgressBar) {
473 int status = (int)((MAX_BROADCAST_TIME - delay) * 1.0 *
474 BROADCAST_STOP_PERCENT / MAX_BROADCAST_TIME);
475 sInstance.setRebootProgress(status, null);
478 mActionDoneSync.wait(Math.min(delay, PHONE_STATE_POLL_SLEEP_MSEC));
479 } catch (InterruptedException e) {
483 if (mRebootHasProgressBar) {
484 sInstance.setRebootProgress(BROADCAST_STOP_PERCENT, null);
486 shutdownTimingLog.traceEnd(); // SendShutdownBroadcast
487 metricEnded(METRIC_SEND_BROADCAST);
489 Log.i(TAG, "Shutting down activity manager...");
490 shutdownTimingLog.traceBegin("ShutdownActivityManager");
491 metricStarted(METRIC_AM);
493 final IActivityManager am =
494 IActivityManager.Stub.asInterface(ServiceManager.checkService("activity"));
497 am.shutdown(MAX_BROADCAST_TIME);
498 } catch (RemoteException e) {
501 if (mRebootHasProgressBar) {
502 sInstance.setRebootProgress(ACTIVITY_MANAGER_STOP_PERCENT, null);
504 shutdownTimingLog.traceEnd();// ShutdownActivityManager
505 metricEnded(METRIC_AM);
507 Log.i(TAG, "Shutting down package manager...");
508 shutdownTimingLog.traceBegin("ShutdownPackageManager");
509 metricStarted(METRIC_PM);
511 final PackageManagerService pm = (PackageManagerService)
512 ServiceManager.getService("package");
516 if (mRebootHasProgressBar) {
517 sInstance.setRebootProgress(PACKAGE_MANAGER_STOP_PERCENT, null);
519 shutdownTimingLog.traceEnd(); // ShutdownPackageManager
520 metricEnded(METRIC_PM);
523 shutdownTimingLog.traceBegin("ShutdownRadios");
524 metricStarted(METRIC_RADIOS);
525 shutdownRadios(MAX_RADIO_WAIT_TIME);
526 if (mRebootHasProgressBar) {
527 sInstance.setRebootProgress(RADIO_STOP_PERCENT, null);
529 shutdownTimingLog.traceEnd(); // ShutdownRadios
530 metricEnded(METRIC_RADIOS);
532 // Shutdown StorageManagerService to ensure media is in a safe state
533 IStorageShutdownObserver observer = new IStorageShutdownObserver.Stub() {
534 public void onShutDownComplete(int statusCode) throws RemoteException {
535 Log.w(TAG, "Result code " + statusCode + " from StorageManagerService.shutdown");
540 Log.i(TAG, "Shutting down StorageManagerService");
541 shutdownTimingLog.traceBegin("ShutdownStorageManager");
542 metricStarted(METRIC_SM);
544 // Set initial variables and time out time.
546 final long endShutTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_WAIT_TIME;
547 synchronized (mActionDoneSync) {
549 final IStorageManager storageManager = IStorageManager.Stub.asInterface(
550 ServiceManager.checkService("mount"));
551 if (storageManager != null) {
552 storageManager.shutdown(observer);
554 Log.w(TAG, "StorageManagerService unavailable for shutdown");
556 } catch (Exception e) {
557 Log.e(TAG, "Exception during StorageManagerService shutdown", e);
559 while (!mActionDone) {
560 long delay = endShutTime - SystemClock.elapsedRealtime();
562 Log.w(TAG, "StorageManager shutdown wait timed out");
564 } else if (mRebootHasProgressBar) {
565 int status = (int)((MAX_SHUTDOWN_WAIT_TIME - delay) * 1.0 *
566 (MOUNT_SERVICE_STOP_PERCENT - RADIO_STOP_PERCENT) /
567 MAX_SHUTDOWN_WAIT_TIME);
568 status += RADIO_STOP_PERCENT;
569 sInstance.setRebootProgress(status, null);
572 mActionDoneSync.wait(Math.min(delay, PHONE_STATE_POLL_SLEEP_MSEC));
573 } catch (InterruptedException e) {
577 shutdownTimingLog.traceEnd(); // ShutdownStorageManager
578 metricEnded(METRIC_SM);
580 if (mRebootHasProgressBar) {
581 sInstance.setRebootProgress(MOUNT_SERVICE_STOP_PERCENT, null);
583 // If it's to reboot to install an update and uncrypt hasn't been
584 // done yet, trigger it now.
588 shutdownTimingLog.traceEnd(); // SystemServerShutdown
589 metricEnded(METRIC_SYSTEM_SERVER);
590 saveMetrics(mReboot);
591 rebootOrShutdown(mContext, mReboot, mReason);
594 private static TimingsTraceLog newTimingsLog() {
595 return new TimingsTraceLog("ShutdownTiming", Trace.TRACE_TAG_SYSTEM_SERVER);
598 private static void metricStarted(String metricKey) {
599 synchronized (TRON_METRICS) {
600 TRON_METRICS.put(metricKey, -1 * SystemClock.elapsedRealtime());
604 private static void metricEnded(String metricKey) {
605 synchronized (TRON_METRICS) {
607 .put(metricKey, SystemClock.elapsedRealtime() + TRON_METRICS.get(metricKey));
611 private void setRebootProgress(final int progress, final CharSequence message) {
612 mHandler.post(new Runnable() {
615 if (mProgressDialog != null) {
616 mProgressDialog.setProgress(progress);
617 if (message != null) {
618 mProgressDialog.setMessage(message);
625 private void shutdownRadios(final int timeout) {
626 // If a radio is wedged, disabling it may hang so we do this work in another thread,
628 final long endTime = SystemClock.elapsedRealtime() + timeout;
629 final boolean[] done = new boolean[1];
630 Thread t = new Thread() {
632 TimingsTraceLog shutdownTimingsTraceLog = newTimingsLog();
634 boolean bluetoothReadyForShutdown;
637 final INfcAdapter nfc =
638 INfcAdapter.Stub.asInterface(ServiceManager.checkService("nfc"));
639 final ITelephony phone =
640 ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
641 final IBluetoothManager bluetooth =
642 IBluetoothManager.Stub.asInterface(ServiceManager.checkService(
643 BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE));
645 nfcOff = nfc == null ||
646 nfc.getState() == NfcAdapter.STATE_OFF;
648 Log.w(TAG, "Turning off NFC...");
649 metricStarted(METRIC_NFC);
650 nfc.disable(false); // Don't persist new state
652 } catch (RemoteException ex) {
653 Log.e(TAG, "RemoteException during NFC shutdown", ex);
658 bluetoothReadyForShutdown = bluetooth == null ||
659 bluetooth.getState() == BluetoothAdapter.STATE_OFF;
660 if (!bluetoothReadyForShutdown) {
661 Log.w(TAG, "Disabling Bluetooth...");
662 metricStarted(METRIC_BT);
663 bluetooth.disable(mContext.getPackageName(), false); // disable but don't persist new state
665 } catch (RemoteException ex) {
666 Log.e(TAG, "RemoteException during bluetooth shutdown", ex);
667 bluetoothReadyForShutdown = true;
671 radioOff = phone == null || !phone.needMobileRadioShutdown();
673 Log.w(TAG, "Turning off cellular radios...");
674 metricStarted(METRIC_RADIO);
675 phone.shutdownMobileRadios();
677 } catch (RemoteException ex) {
678 Log.e(TAG, "RemoteException during radio shutdown", ex);
682 Log.i(TAG, "Waiting for NFC, Bluetooth and Radio...");
684 long delay = endTime - SystemClock.elapsedRealtime();
686 if (mRebootHasProgressBar) {
687 int status = (int)((timeout - delay) * 1.0 *
688 (RADIO_STOP_PERCENT - PACKAGE_MANAGER_STOP_PERCENT) / timeout);
689 status += PACKAGE_MANAGER_STOP_PERCENT;
690 sInstance.setRebootProgress(status, null);
693 if (!bluetoothReadyForShutdown) {
695 // BLE only mode can happen when BT is turned off
696 // We will continue shutting down in such case
697 bluetoothReadyForShutdown =
698 bluetooth.getState() == BluetoothAdapter.STATE_OFF ||
699 bluetooth.getState() == BluetoothAdapter.STATE_BLE_TURNING_OFF ||
700 bluetooth.getState() == BluetoothAdapter.STATE_BLE_ON;
701 } catch (RemoteException ex) {
702 Log.e(TAG, "RemoteException during bluetooth shutdown", ex);
703 bluetoothReadyForShutdown = true;
705 if (bluetoothReadyForShutdown) {
706 Log.i(TAG, "Bluetooth turned off.");
707 metricEnded(METRIC_BT);
708 shutdownTimingsTraceLog
709 .logDuration("ShutdownBt", TRON_METRICS.get(METRIC_BT));
714 radioOff = !phone.needMobileRadioShutdown();
715 } catch (RemoteException ex) {
716 Log.e(TAG, "RemoteException during radio shutdown", ex);
720 Log.i(TAG, "Radio turned off.");
721 metricEnded(METRIC_RADIO);
722 shutdownTimingsTraceLog
723 .logDuration("ShutdownRadio", TRON_METRICS.get(METRIC_RADIO));
728 nfcOff = nfc.getState() == NfcAdapter.STATE_OFF;
729 } catch (RemoteException ex) {
730 Log.e(TAG, "RemoteException during NFC shutdown", ex);
734 Log.i(TAG, "NFC turned off.");
735 metricEnded(METRIC_NFC);
736 shutdownTimingsTraceLog
737 .logDuration("ShutdownNfc", TRON_METRICS.get(METRIC_NFC));
741 if (radioOff && bluetoothReadyForShutdown && nfcOff) {
742 Log.i(TAG, "NFC, Radio and Bluetooth shutdown complete.");
746 SystemClock.sleep(PHONE_STATE_POLL_SLEEP_MSEC);
748 delay = endTime - SystemClock.elapsedRealtime();
756 } catch (InterruptedException ex) {
759 Log.w(TAG, "Timed out waiting for NFC, Radio and Bluetooth shutdown.");
764 * Do not call this directly. Use {@link #reboot(Context, String, boolean)}
765 * or {@link #shutdown(Context, String, boolean)} instead.
767 * @param context Context used to vibrate or null without vibration
768 * @param reboot true to reboot or false to shutdown
769 * @param reason reason for reboot/shutdown
771 public static void rebootOrShutdown(final Context context, boolean reboot, String reason) {
773 Log.i(TAG, "Rebooting, reason: " + reason);
774 PowerManagerService.lowLevelReboot(reason);
775 Log.e(TAG, "Reboot failed, will attempt shutdown instead");
777 } else if (SHUTDOWN_VIBRATE_MS > 0 && context != null) {
778 // vibrate before shutting down
779 Vibrator vibrator = new SystemVibrator(context);
781 vibrator.vibrate(SHUTDOWN_VIBRATE_MS, VIBRATION_ATTRIBUTES);
782 } catch (Exception e) {
783 // Failure to vibrate shouldn't interrupt shutdown. Just log it.
784 Log.w(TAG, "Failed to vibrate during shutdown.", e);
787 // vibrator is asynchronous so we need to wait to avoid shutting down too soon.
789 Thread.sleep(SHUTDOWN_VIBRATE_MS);
790 } catch (InterruptedException unused) {
794 Log.i(TAG, "Performing low-level shutdown...");
795 PowerManagerService.lowLevelShutdown(reason);
798 private static void saveMetrics(boolean reboot) {
799 StringBuilder metricValue = new StringBuilder();
800 metricValue.append("reboot:");
801 metricValue.append(reboot ? "y" : "n");
802 final int metricsSize = TRON_METRICS.size();
803 for (int i = 0; i < metricsSize; i++) {
804 final String name = TRON_METRICS.keyAt(i);
805 final long value = TRON_METRICS.valueAt(i);
807 Log.e(TAG, "metricEnded wasn't called for " + name);
810 metricValue.append(',').append(name).append(':').append(value);
812 File tmp = new File(METRICS_FILE_BASENAME + ".tmp");
813 boolean saved = false;
814 try (FileOutputStream fos = new FileOutputStream(tmp)) {
815 fos.write(metricValue.toString().getBytes(StandardCharsets.UTF_8));
817 } catch (IOException e) {
818 Log.e(TAG,"Cannot save shutdown metrics", e);
821 tmp.renameTo(new File(METRICS_FILE_BASENAME + ".txt"));
825 private void uncrypt() {
826 Log.i(TAG, "Calling uncrypt and monitoring the progress...");
828 final RecoverySystem.ProgressListener progressListener =
829 new RecoverySystem.ProgressListener() {
831 public void onProgress(int status) {
832 if (status >= 0 && status < 100) {
833 // Scale down to [MOUNT_SERVICE_STOP_PERCENT, 100).
834 status = (int)(status * (100.0 - MOUNT_SERVICE_STOP_PERCENT) / 100);
835 status += MOUNT_SERVICE_STOP_PERCENT;
836 CharSequence msg = mContext.getText(
837 com.android.internal.R.string.reboot_to_update_package);
838 sInstance.setRebootProgress(status, msg);
839 } else if (status == 100) {
840 CharSequence msg = mContext.getText(
841 com.android.internal.R.string.reboot_to_update_reboot);
842 sInstance.setRebootProgress(status, msg);
849 final boolean[] done = new boolean[1];
851 Thread t = new Thread() {
854 RecoverySystem rs = (RecoverySystem) mContext.getSystemService(
855 Context.RECOVERY_SERVICE);
856 String filename = null;
858 filename = FileUtils.readTextFile(RecoverySystem.UNCRYPT_PACKAGE_FILE, 0, null);
859 rs.processPackage(mContext, new File(filename), progressListener);
860 } catch (IOException e) {
861 Log.e(TAG, "Error uncrypting file", e);
869 t.join(MAX_UNCRYPT_WAIT_TIME);
870 } catch (InterruptedException unused) {
873 Log.w(TAG, "Timed out waiting for uncrypt.");
874 final int uncryptTimeoutError = 100;
875 String timeoutMessage = String.format("uncrypt_time: %d\n" + "uncrypt_error: %d\n",
876 MAX_UNCRYPT_WAIT_TIME / 1000, uncryptTimeoutError);
878 FileUtils.stringToFile(RecoverySystem.UNCRYPT_STATUS_FILE, timeoutMessage);
879 } catch (IOException e) {
880 Log.e(TAG, "Failed to write timeout message to uncrypt status", e);