1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 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 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 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. 15 */ 16 17 18 package com.android.server.power; 19 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; 58 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; 64 65 import java.io.File; 66 import java.io.FileOutputStream; 67 import java.io.IOException; 68 import java.nio.charset.StandardCharsets; 69 70 public final class ShutdownThread extends Thread { 71 // constants 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; 85 86 // length of vibration before shutting down 87 private static final int SHUTDOWN_VIBRATE_MS = 500; 88 89 // state tracking 90 private static final Object sIsStartedGuard = new Object(); 91 private static boolean sIsStarted = false; 92 93 private static boolean mReboot; 94 private static boolean mRebootSafeMode; 95 private static boolean mRebootHasProgressBar; 96 private static String mReason; 97 98 // Provides shutdown assurance in case the system_server is killed 99 public static final String SHUTDOWN_ACTION_PROPERTY = "sys.shutdown.requested"; 100 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"; 104 105 // static instance of this thread 106 private static final ShutdownThread sInstance = new ShutdownThread(); 107 108 private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder() 109 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) 110 .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) 111 .build(); 112 113 // Metrics that will be reported to tron after reboot 114 private static final ArrayMap<String, Long> TRON_METRICS = new ArrayMap<>(); 115 116 // File to use for save metrics 117 private static final String METRICS_FILE_BASENAME = "/data/system/shutdown-metrics"; 118 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"; 129 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; 137 138 private static AlertDialog sConfirmDialog; 139 private ProgressDialog mProgressDialog; 140 141 private ShutdownThread() { 142 } 143 144 /** 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 147 * is shown. 148 * 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. 153 */ 154 public static void shutdown(final Context context, String reason, boolean confirm) { 155 mReboot = false; 156 mRebootSafeMode = false; 157 mReason = reason; 158 shutdownInner(context, confirm); 159 } 160 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 163 // in is themed. 164 context.assertRuntimeOverlayThemable(); 165 166 // ensure that only one thread is trying to power down. 167 // any additional calls are just returned 168 synchronized (sIsStartedGuard) { 169 if (sIsStarted) { 170 Log.d(TAG, "Request to shutdown already running, returning."); 171 return; 172 } 173 } 174 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); 182 183 Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior); 184 185 if (confirm) { 186 final CloseDialogReceiver closer = new CloseDialogReceiver(context); 187 if (sConfirmDialog != null) { 188 sConfirmDialog.dismiss(); 189 } 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); 198 } 199 }) 200 .setNegativeButton(com.android.internal.R.string.no, null) 201 .create(); 202 closer.dialog = sConfirmDialog; 203 sConfirmDialog.setOnDismissListener(closer); 204 sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); 205 sConfirmDialog.show(); 206 } else { 207 beginShutdownSequence(context); 208 } 209 } 210 211 private static class CloseDialogReceiver extends BroadcastReceiver 212 implements DialogInterface.OnDismissListener { 213 private Context mContext; 214 public Dialog dialog; 215 216 CloseDialogReceiver(Context context) { 217 mContext = context; 218 IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); 219 context.registerReceiver(this, filter); 220 } 221 222 @Override 223 public void onReceive(Context context, Intent intent) { 224 dialog.cancel(); 225 } 226 227 public void onDismiss(DialogInterface unused) { 228 mContext.unregisterReceiver(this); 229 } 230 } 231 232 /** 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 235 * is shown. 236 * 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. 241 */ 242 public static void reboot(final Context context, String reason, boolean confirm) { 243 mReboot = true; 244 mRebootSafeMode = false; 245 mRebootHasProgressBar = false; 246 mReason = reason; 247 shutdownInner(context, confirm); 248 } 249 250 /** 251 * Request a reboot into safe mode. Must be called from a Looper thread in which its UI 252 * is shown. 253 * 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. 257 */ 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)) { 261 return; 262 } 263 264 mReboot = true; 265 mRebootSafeMode = true; 266 mRebootHasProgressBar = false; 267 mReason = null; 268 shutdownInner(context, confirm); 269 } 270 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); 274 275 // Path 1: Reboot to recovery for update 276 // Condition: mReason startswith REBOOT_RECOVERY_UPDATE 277 // 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) 282 // 283 // * Path 1a is expected to be removed once the GmsCore shipped on 284 // device always calls uncrypt prior to reboot. 285 // 286 // Path 1b: uncrypt already done 287 // UI: spinning circle only (no progress bar) 288 // 289 // Path 2: Reboot to recovery for factory reset 290 // Condition: mReason == REBOOT_RECOVERY 291 // UI: spinning circle only (no progress bar) 292 // 293 // Path 3: Regular reboot / shutdown 294 // Condition: Otherwise 295 // UI: spinning circle only (no progress bar) 296 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) { 305 pd.setMax(100); 306 pd.setProgress(0); 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)); 312 } else { 313 if (showSysuiReboot()) { 314 return null; 315 } 316 pd.setIndeterminate(true); 317 pd.setMessage(context.getText( 318 com.android.internal.R.string.reboot_to_update_reboot)); 319 } 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); 328 } else { 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); 334 } 335 } else { 336 if (showSysuiReboot()) { 337 return null; 338 } 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); 342 } 343 pd.setCancelable(false); 344 pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); 345 346 pd.show(); 347 return pd; 348 } 349 350 private static boolean showSysuiReboot() { 351 Log.d(TAG, "Attempting to use SysUI shutdown UI"); 352 try { 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"); 358 return true; 359 } 360 } catch (Exception e) { 361 // If anything went wrong, ignore it and use fallback ui 362 } 363 Log.d(TAG, "SysUI is unavailable"); 364 return false; 365 } 366 367 private static void beginShutdownSequence(Context context) { 368 synchronized (sIsStartedGuard) { 369 if (sIsStarted) { 370 Log.d(TAG, "Shutdown sequence already running, returning."); 371 return; 372 } 373 sIsStarted = true; 374 } 375 376 sInstance.mProgressDialog = showShutdownDialog(context); 377 sInstance.mContext = context; 378 sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE); 379 380 // make sure we never fall asleep again 381 sInstance.mCpuWakeLock = null; 382 try { 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; 390 } 391 392 // also make sure the screen stays on for better user experience 393 sInstance.mScreenWakeLock = null; 394 if (sInstance.mPowerManager.isScreenOn()) { 395 try { 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; 403 } 404 } 405 406 // start the thread that initiates shutdown 407 sInstance.mHandler = new Handler() { 408 }; 409 sInstance.start(); 410 } 411 412 void actionDone() { 413 synchronized (mActionDoneSync) { 414 mActionDone = true; 415 mActionDoneSync.notifyAll(); 416 } 417 } 418 419 /** 420 * Makes sure we handle the shutdown gracefully. 421 * Shuts off power regardless of radio and bluetooth state if the alloted time has passed. 422 */ 423 public void run() { 424 TimingsTraceLog shutdownTimingLog = newTimingsLog(); 425 shutdownTimingLog.traceBegin("SystemServerShutdown"); 426 metricStarted(METRIC_SYSTEM_SERVER); 427 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. 431 actionDone(); 432 } 433 }; 434 435 /* 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. 439 */ 440 { 441 String reason = (mReboot ? "1" : "0") + (mReason != null ? mReason : ""); 442 SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason); 443 } 444 445 /* 446 * If we are rebooting into safe mode, write a system property 447 * indicating so. 448 */ 449 if (mRebootSafeMode) { 450 SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1"); 451 } 452 453 metricStarted(METRIC_SEND_BROADCAST); 454 shutdownTimingLog.traceBegin("SendShutdownBroadcast"); 455 Log.i(TAG, "Sending shutdown broadcast..."); 456 457 // First send the high-level shut down broadcast. 458 mActionDone = false; 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); 464 465 final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME; 466 synchronized (mActionDoneSync) { 467 while (!mActionDone) { 468 long delay = endTime - SystemClock.elapsedRealtime(); 469 if (delay <= 0) { 470 Log.w(TAG, "Shutdown broadcast timed out"); 471 break; 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); 476 } 477 try { 478 mActionDoneSync.wait(Math.min(delay, PHONE_STATE_POLL_SLEEP_MSEC)); 479 } catch (InterruptedException e) { 480 } 481 } 482 } 483 if (mRebootHasProgressBar) { 484 sInstance.setRebootProgress(BROADCAST_STOP_PERCENT, null); 485 } 486 shutdownTimingLog.traceEnd(); // SendShutdownBroadcast 487 metricEnded(METRIC_SEND_BROADCAST); 488 489 Log.i(TAG, "Shutting down activity manager..."); 490 shutdownTimingLog.traceBegin("ShutdownActivityManager"); 491 metricStarted(METRIC_AM); 492 493 final IActivityManager am = 494 IActivityManager.Stub.asInterface(ServiceManager.checkService("activity")); 495 if (am != null) { 496 try { 497 am.shutdown(MAX_BROADCAST_TIME); 498 } catch (RemoteException e) { 499 } 500 } 501 if (mRebootHasProgressBar) { 502 sInstance.setRebootProgress(ACTIVITY_MANAGER_STOP_PERCENT, null); 503 } 504 shutdownTimingLog.traceEnd();// ShutdownActivityManager 505 metricEnded(METRIC_AM); 506 507 Log.i(TAG, "Shutting down package manager..."); 508 shutdownTimingLog.traceBegin("ShutdownPackageManager"); 509 metricStarted(METRIC_PM); 510 511 final PackageManagerService pm = (PackageManagerService) 512 ServiceManager.getService("package"); 513 if (pm != null) { 514 pm.shutdown(); 515 } 516 if (mRebootHasProgressBar) { 517 sInstance.setRebootProgress(PACKAGE_MANAGER_STOP_PERCENT, null); 518 } 519 shutdownTimingLog.traceEnd(); // ShutdownPackageManager 520 metricEnded(METRIC_PM); 521 522 // Shutdown radios. 523 shutdownTimingLog.traceBegin("ShutdownRadios"); 524 metricStarted(METRIC_RADIOS); 525 shutdownRadios(MAX_RADIO_WAIT_TIME); 526 if (mRebootHasProgressBar) { 527 sInstance.setRebootProgress(RADIO_STOP_PERCENT, null); 528 } 529 shutdownTimingLog.traceEnd(); // ShutdownRadios 530 metricEnded(METRIC_RADIOS); 531 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"); 536 actionDone(); 537 } 538 }; 539 540 Log.i(TAG, "Shutting down StorageManagerService"); 541 shutdownTimingLog.traceBegin("ShutdownStorageManager"); 542 metricStarted(METRIC_SM); 543 544 // Set initial variables and time out time. 545 mActionDone = false; 546 final long endShutTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_WAIT_TIME; 547 synchronized (mActionDoneSync) { 548 try { 549 final IStorageManager storageManager = IStorageManager.Stub.asInterface( 550 ServiceManager.checkService("mount")); 551 if (storageManager != null) { 552 storageManager.shutdown(observer); 553 } else { 554 Log.w(TAG, "StorageManagerService unavailable for shutdown"); 555 } 556 } catch (Exception e) { 557 Log.e(TAG, "Exception during StorageManagerService shutdown", e); 558 } 559 while (!mActionDone) { 560 long delay = endShutTime - SystemClock.elapsedRealtime(); 561 if (delay <= 0) { 562 Log.w(TAG, "StorageManager shutdown wait timed out"); 563 break; 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); 570 } 571 try { 572 mActionDoneSync.wait(Math.min(delay, PHONE_STATE_POLL_SLEEP_MSEC)); 573 } catch (InterruptedException e) { 574 } 575 } 576 } 577 shutdownTimingLog.traceEnd(); // ShutdownStorageManager 578 metricEnded(METRIC_SM); 579 580 if (mRebootHasProgressBar) { 581 sInstance.setRebootProgress(MOUNT_SERVICE_STOP_PERCENT, null); 582 583 // If it's to reboot to install an update and uncrypt hasn't been 584 // done yet, trigger it now. 585 uncrypt(); 586 } 587 588 shutdownTimingLog.traceEnd(); // SystemServerShutdown 589 metricEnded(METRIC_SYSTEM_SERVER); 590 saveMetrics(mReboot); 591 rebootOrShutdown(mContext, mReboot, mReason); 592 } 593 594 private static TimingsTraceLog newTimingsLog() { 595 return new TimingsTraceLog("ShutdownTiming", Trace.TRACE_TAG_SYSTEM_SERVER); 596 } 597 598 private static void metricStarted(String metricKey) { 599 synchronized (TRON_METRICS) { 600 TRON_METRICS.put(metricKey, -1 * SystemClock.elapsedRealtime()); 601 } 602 } 603 604 private static void metricEnded(String metricKey) { 605 synchronized (TRON_METRICS) { 606 TRON_METRICS 607 .put(metricKey, SystemClock.elapsedRealtime() + TRON_METRICS.get(metricKey)); 608 } 609 } 610 611 private void setRebootProgress(final int progress, final CharSequence message) { 612 mHandler.post(new Runnable() { 613 @Override 614 public void run() { 615 if (mProgressDialog != null) { 616 mProgressDialog.setProgress(progress); 617 if (message != null) { 618 mProgressDialog.setMessage(message); 619 } 620 } 621 } 622 }); 623 } 624 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, 627 // just in case. 628 final long endTime = SystemClock.elapsedRealtime() + timeout; 629 final boolean[] done = new boolean[1]; 630 Thread t = new Thread() { 631 public void run() { 632 TimingsTraceLog shutdownTimingsTraceLog = newTimingsLog(); 633 boolean nfcOff; 634 boolean bluetoothReadyForShutdown; 635 boolean radioOff; 636 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)); 644 try { 645 nfcOff = nfc == null || 646 nfc.getState() == NfcAdapter.STATE_OFF; 647 if (!nfcOff) { 648 Log.w(TAG, "Turning off NFC..."); 649 metricStarted(METRIC_NFC); 650 nfc.disable(false); // Don't persist new state 651 } 652 } catch (RemoteException ex) { 653 Log.e(TAG, "RemoteException during NFC shutdown", ex); 654 nfcOff = true; 655 } 656 657 try { 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 664 } 665 } catch (RemoteException ex) { 666 Log.e(TAG, "RemoteException during bluetooth shutdown", ex); 667 bluetoothReadyForShutdown = true; 668 } 669 670 try { 671 radioOff = phone == null || !phone.needMobileRadioShutdown(); 672 if (!radioOff) { 673 Log.w(TAG, "Turning off cellular radios..."); 674 metricStarted(METRIC_RADIO); 675 phone.shutdownMobileRadios(); 676 } 677 } catch (RemoteException ex) { 678 Log.e(TAG, "RemoteException during radio shutdown", ex); 679 radioOff = true; 680 } 681 682 Log.i(TAG, "Waiting for NFC, Bluetooth and Radio..."); 683 684 long delay = endTime - SystemClock.elapsedRealtime(); 685 while (delay > 0) { 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); 691 } 692 693 if (!bluetoothReadyForShutdown) { 694 try { 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; 704 } 705 if (bluetoothReadyForShutdown) { 706 Log.i(TAG, "Bluetooth turned off."); 707 metricEnded(METRIC_BT); 708 shutdownTimingsTraceLog 709 .logDuration("ShutdownBt", TRON_METRICS.get(METRIC_BT)); 710 } 711 } 712 if (!radioOff) { 713 try { 714 radioOff = !phone.needMobileRadioShutdown(); 715 } catch (RemoteException ex) { 716 Log.e(TAG, "RemoteException during radio shutdown", ex); 717 radioOff = true; 718 } 719 if (radioOff) { 720 Log.i(TAG, "Radio turned off."); 721 metricEnded(METRIC_RADIO); 722 shutdownTimingsTraceLog 723 .logDuration("ShutdownRadio", TRON_METRICS.get(METRIC_RADIO)); 724 } 725 } 726 if (!nfcOff) { 727 try { 728 nfcOff = nfc.getState() == NfcAdapter.STATE_OFF; 729 } catch (RemoteException ex) { 730 Log.e(TAG, "RemoteException during NFC shutdown", ex); 731 nfcOff = true; 732 } 733 if (nfcOff) { 734 Log.i(TAG, "NFC turned off."); 735 metricEnded(METRIC_NFC); 736 shutdownTimingsTraceLog 737 .logDuration("ShutdownNfc", TRON_METRICS.get(METRIC_NFC)); 738 } 739 } 740 741 if (radioOff && bluetoothReadyForShutdown && nfcOff) { 742 Log.i(TAG, "NFC, Radio and Bluetooth shutdown complete."); 743 done[0] = true; 744 break; 745 } 746 SystemClock.sleep(PHONE_STATE_POLL_SLEEP_MSEC); 747 748 delay = endTime - SystemClock.elapsedRealtime(); 749 } 750 } 751 }; 752 753 t.start(); 754 try { 755 t.join(timeout); 756 } catch (InterruptedException ex) { 757 } 758 if (!done[0]) { 759 Log.w(TAG, "Timed out waiting for NFC, Radio and Bluetooth shutdown."); 760 } 761 } 762 763 /** 764 * Do not call this directly. Use {@link #reboot(Context, String, boolean)} 765 * or {@link #shutdown(Context, String, boolean)} instead. 766 * 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 770 */ 771 public static void rebootOrShutdown(final Context context, boolean reboot, String reason) { 772 if (reboot) { 773 Log.i(TAG, "Rebooting, reason: " + reason); 774 PowerManagerService.lowLevelReboot(reason); 775 Log.e(TAG, "Reboot failed, will attempt shutdown instead"); 776 reason = null; 777 } else if (SHUTDOWN_VIBRATE_MS > 0 && context != null) { 778 // vibrate before shutting down 779 Vibrator vibrator = new SystemVibrator(context); 780 try { 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); 785 } 786 787 // vibrator is asynchronous so we need to wait to avoid shutting down too soon. 788 try { 789 Thread.sleep(SHUTDOWN_VIBRATE_MS); 790 } catch (InterruptedException unused) { 791 } 792 } 793 // Shutdown power 794 Log.i(TAG, "Performing low-level shutdown..."); 795 PowerManagerService.lowLevelShutdown(reason); 796 } 797 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); 806 if (value < 0) { 807 Log.e(TAG, "metricEnded wasn't called for " + name); 808 continue; 809 } 810 metricValue.append(',').append(name).append(':').append(value); 811 } 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)); 816 saved = true; 817 } catch (IOException e) { 818 Log.e(TAG,"Cannot save shutdown metrics", e); 819 } 820 if (saved) { 821 tmp.renameTo(new File(METRICS_FILE_BASENAME + ".txt")); 822 } 823 } 824 825 private void uncrypt() { 826 Log.i(TAG, "Calling uncrypt and monitoring the progress..."); 827 828 final RecoverySystem.ProgressListener progressListener = 829 new RecoverySystem.ProgressListener() { 830 @Override 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); 843 } else { 844 // Ignored 845 } 846 } 847 }; 848 849 final boolean[] done = new boolean[1]; 850 done[0] = false; 851 Thread t = new Thread() { 852 @Override 853 public void run() { 854 RecoverySystem rs = (RecoverySystem) mContext.getSystemService( 855 Context.RECOVERY_SERVICE); 856 String filename = null; 857 try { 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); 862 } 863 done[0] = true; 864 } 865 }; 866 t.start(); 867 868 try { 869 t.join(MAX_UNCRYPT_WAIT_TIME); 870 } catch (InterruptedException unused) { 871 } 872 if (!done[0]) { 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); 877 try { 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); 881 } 882 } 883 } 884 } 885