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.ActivityManagerInternal; 21 import android.app.AlertDialog; 22 import android.app.BroadcastOptions; 23 import android.app.Dialog; 24 import android.app.IActivityManager; 25 import android.app.ProgressDialog; 26 import android.app.admin.SecurityLog; 27 import android.content.BroadcastReceiver; 28 import android.content.Context; 29 import android.content.DialogInterface; 30 import android.content.IIntentReceiver; 31 import android.content.Intent; 32 import android.content.IntentFilter; 33 import android.content.pm.PackageManagerInternal; 34 import android.os.Bundle; 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.SystemVibrator; 44 import android.os.Trace; 45 import android.os.UserHandle; 46 import android.os.UserManager; 47 import android.os.VibrationAttributes; 48 import android.os.VibrationEffect; 49 import android.os.Vibrator; 50 import android.os.vibrator.persistence.VibrationXmlParser; 51 import android.telephony.TelephonyManager; 52 import android.text.TextUtils; 53 import android.util.ArrayMap; 54 import android.util.Log; 55 import android.util.Slog; 56 import android.util.TimingsTraceLog; 57 import android.view.SurfaceControl; 58 import android.view.WindowManager; 59 60 import com.android.internal.annotations.VisibleForTesting; 61 import com.android.server.LocalServices; 62 import com.android.server.RescueParty; 63 import com.android.server.statusbar.StatusBarManagerInternal; 64 65 import java.io.File; 66 import java.io.FileOutputStream; 67 import java.io.FileReader; 68 import java.io.IOException; 69 import java.nio.charset.StandardCharsets; 70 71 public final class ShutdownThread extends Thread { 72 // constants 73 private static final boolean DEBUG = false; 74 private static final String TAG = "ShutdownThread"; 75 private static final int ACTION_DONE_POLL_WAIT_MS = 500; 76 private static final int RADIOS_STATE_POLL_SLEEP_MS = 100; 77 // maximum time we wait for the shutdown broadcast before going on. 78 private static final int MAX_BROADCAST_TIME = 10 * 1000; 79 private static final int MAX_CHECK_POINTS_DUMP_WAIT_TIME = 10 * 1000; 80 private static final int MAX_RADIO_WAIT_TIME = 12 * 1000; 81 private static final int MAX_UNCRYPT_WAIT_TIME = 15 * 60 * 1000; 82 // constants for progress bar. the values are roughly estimated based on timeout. 83 private static final int BROADCAST_STOP_PERCENT = 2; 84 private static final int ACTIVITY_MANAGER_STOP_PERCENT = 4; 85 private static final int PACKAGE_MANAGER_STOP_PERCENT = 6; 86 private static final int RADIO_STOP_PERCENT = 18; 87 private static final int MOUNT_SERVICE_STOP_PERCENT = 20; 88 89 // length of vibration before shutting down 90 @VisibleForTesting static final int DEFAULT_SHUTDOWN_VIBRATE_MS = 500; 91 92 // state tracking 93 private static final Object sIsStartedGuard = new Object(); 94 private static boolean sIsStarted = false; 95 96 private static boolean mReboot; 97 private static boolean mRebootSafeMode; 98 private static boolean mRebootHasProgressBar; 99 private static String mReason; 100 101 // Provides shutdown assurance in case the system_server is killed 102 public static final String SHUTDOWN_ACTION_PROPERTY = "sys.shutdown.requested"; 103 104 // Indicates whether we are rebooting into safe mode 105 public static final String REBOOT_SAFEMODE_PROPERTY = "persist.sys.safemode"; 106 public static final String RO_SAFEMODE_PROPERTY = "ro.sys.safemode"; 107 108 // static instance of this thread 109 private static final ShutdownThread sInstance = new ShutdownThread(); 110 111 // Metrics that will be reported to tron after reboot 112 private static final ArrayMap<String, Long> TRON_METRICS = new ArrayMap<>(); 113 114 // File to use for saving shutdown metrics 115 private static final String METRICS_FILE_BASENAME = "/data/system/shutdown-metrics"; 116 // File to use for saving shutdown check points 117 private static final String CHECK_POINTS_FILE_BASENAME = 118 "/data/system/shutdown-checkpoints/checkpoints"; 119 120 // Metrics names to be persisted in shutdown-metrics file 121 private static String METRIC_SYSTEM_SERVER = "shutdown_system_server"; 122 private static String METRIC_SEND_BROADCAST = "shutdown_send_shutdown_broadcast"; 123 private static String METRIC_AM = "shutdown_activity_manager"; 124 private static String METRIC_PM = "shutdown_package_manager"; 125 private static String METRIC_RADIOS = "shutdown_radios"; 126 private static String METRIC_RADIO = "shutdown_radio"; 127 private static String METRIC_SHUTDOWN_TIME_START = "begin_shutdown"; 128 129 private final Injector mInjector; 130 131 private final Object mActionDoneSync = new Object(); 132 private boolean mActionDone; 133 private Context mContext; 134 private PowerManager mPowerManager; 135 private PowerManager.WakeLock mCpuWakeLock; 136 private PowerManager.WakeLock mScreenWakeLock; 137 private Handler mHandler; 138 139 private static AlertDialog sConfirmDialog; 140 private ProgressDialog mProgressDialog; 141 ShutdownThread()142 private ShutdownThread() { 143 this(new Injector()); 144 } 145 146 @VisibleForTesting ShutdownThread(Injector injector)147 ShutdownThread(Injector injector) { 148 mInjector = injector; 149 } 150 151 /** 152 * Request a clean shutdown, waiting for subsystems to clean up their 153 * state etc. Must be called from a Looper thread in which its UI 154 * is shown. 155 * 156 * @param context Context used to display the shutdown progress dialog. This must be a context 157 * suitable for displaying UI (aka Themable). 158 * @param reason code to pass to android_reboot() (e.g. "userrequested"), or null. 159 * @param confirm true if user confirmation is needed before shutting down. 160 */ shutdown(final Context context, String reason, boolean confirm)161 public static void shutdown(final Context context, String reason, boolean confirm) { 162 mReboot = false; 163 mRebootSafeMode = false; 164 mReason = reason; 165 shutdownInner(context, confirm); 166 } 167 shutdownInner(final Context context, boolean confirm)168 private static void shutdownInner(final Context context, boolean confirm) { 169 // ShutdownThread is called from many places, so best to verify here that the context passed 170 // in is themed. 171 context.assertRuntimeOverlayThemable(); 172 173 // ensure that only one thread is trying to power down. 174 // any additional calls are just returned 175 synchronized (sIsStartedGuard) { 176 if (sIsStarted) { 177 if (DEBUG) { 178 Log.d(TAG, "Request to shutdown already running, returning."); 179 } 180 return; 181 } 182 } 183 184 // Add checkpoint for this shutdown attempt. The user might still cancel the dialog, but 185 // this point preserves the system trace of the trigger point of the ShutdownThread. 186 ShutdownCheckPoints.recordCheckPoint(/* reason= */ null); 187 188 final int longPressBehavior = context.getResources().getInteger( 189 com.android.internal.R.integer.config_longPressOnPowerBehavior); 190 final int resourceId = mRebootSafeMode 191 ? com.android.internal.R.string.reboot_safemode_confirm 192 : (longPressBehavior == 2 193 ? com.android.internal.R.string.shutdown_confirm_question 194 : com.android.internal.R.string.shutdown_confirm); 195 196 if (DEBUG) { 197 Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior); 198 } 199 200 if (confirm) { 201 final CloseDialogReceiver closer = new CloseDialogReceiver(context); 202 if (sConfirmDialog != null) { 203 sConfirmDialog.dismiss(); 204 } 205 sConfirmDialog = new AlertDialog.Builder(context) 206 .setTitle(mRebootSafeMode 207 ? com.android.internal.R.string.reboot_safemode_title 208 : com.android.internal.R.string.power_off) 209 .setMessage(resourceId) 210 .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() { 211 public void onClick(DialogInterface dialog, int which) { 212 beginShutdownSequence(context); 213 } 214 }) 215 .setNegativeButton(com.android.internal.R.string.no, null) 216 .create(); 217 closer.dialog = sConfirmDialog; 218 sConfirmDialog.setOnDismissListener(closer); 219 sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); 220 sConfirmDialog.show(); 221 } else { 222 beginShutdownSequence(context); 223 } 224 } 225 226 private static class CloseDialogReceiver extends BroadcastReceiver 227 implements DialogInterface.OnDismissListener { 228 private Context mContext; 229 public Dialog dialog; 230 CloseDialogReceiver(Context context)231 CloseDialogReceiver(Context context) { 232 mContext = context; 233 IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); 234 context.registerReceiver(this, filter, Context.RECEIVER_EXPORTED); 235 } 236 237 @Override onReceive(Context context, Intent intent)238 public void onReceive(Context context, Intent intent) { 239 dialog.cancel(); 240 } 241 onDismiss(DialogInterface unused)242 public void onDismiss(DialogInterface unused) { 243 mContext.unregisterReceiver(this); 244 } 245 } 246 247 /** 248 * Request a clean shutdown, waiting for subsystems to clean up their 249 * state etc. Must be called from a Looper thread in which its UI 250 * is shown. 251 * 252 * @param context Context used to display the shutdown progress dialog. This must be a context 253 * suitable for displaying UI (aka Themable). 254 * @param reason code to pass to the kernel (e.g. "recovery"), or null. 255 * @param confirm true if user confirmation is needed before shutting down. 256 */ reboot(final Context context, String reason, boolean confirm)257 public static void reboot(final Context context, String reason, boolean confirm) { 258 mReboot = true; 259 mRebootSafeMode = false; 260 mRebootHasProgressBar = false; 261 mReason = reason; 262 shutdownInner(context, confirm); 263 } 264 265 /** 266 * Request a reboot into safe mode. Must be called from a Looper thread in which its UI 267 * is shown. 268 * 269 * @param context Context used to display the shutdown progress dialog. This must be a context 270 * suitable for displaying UI (aka Themable). 271 * @param confirm true if user confirmation is needed before shutting down. 272 */ rebootSafeMode(final Context context, boolean confirm)273 public static void rebootSafeMode(final Context context, boolean confirm) { 274 UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE); 275 if (um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) { 276 return; 277 } 278 279 mReboot = true; 280 mRebootSafeMode = true; 281 mRebootHasProgressBar = false; 282 mReason = null; 283 shutdownInner(context, confirm); 284 } 285 showShutdownDialog(Context context)286 private static ProgressDialog showShutdownDialog(Context context) { 287 // Throw up a system dialog to indicate the device is rebooting / shutting down. 288 ProgressDialog pd = new ProgressDialog(context); 289 290 // Path 1: Reboot to recovery for update 291 // Condition: mReason startswith REBOOT_RECOVERY_UPDATE 292 // 293 // Path 1a: uncrypt needed 294 // Condition: if /cache/recovery/uncrypt_file exists but 295 // /cache/recovery/block.map doesn't. 296 // UI: determinate progress bar (mRebootHasProgressBar == True) 297 // 298 // * Path 1a is expected to be removed once the GmsCore shipped on 299 // device always calls uncrypt prior to reboot. 300 // 301 // Path 1b: uncrypt already done 302 // UI: spinning circle only (no progress bar) 303 // 304 // Path 2: Reboot to recovery for factory reset 305 // Condition: mReason == REBOOT_RECOVERY 306 // UI: spinning circle only (no progress bar) 307 // 308 // Path 3: Regular reboot / shutdown 309 // Condition: Otherwise 310 // UI: spinning circle only (no progress bar) 311 312 // mReason could be "recovery-update" or "recovery-update,quiescent". 313 if (mReason != null && mReason.startsWith(PowerManager.REBOOT_RECOVERY_UPDATE)) { 314 // We need the progress bar if uncrypt will be invoked during the 315 // reboot, which might be time-consuming. 316 mRebootHasProgressBar = RecoverySystem.UNCRYPT_PACKAGE_FILE.exists() 317 && !(RecoverySystem.BLOCK_MAP_FILE.exists()); 318 pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_update_title)); 319 if (mRebootHasProgressBar) { 320 pd.setMax(100); 321 pd.setProgress(0); 322 pd.setIndeterminate(false); 323 boolean showPercent = context.getResources().getBoolean( 324 com.android.internal.R.bool.config_showPercentageTextDuringRebootToUpdate); 325 if (!showPercent) { 326 pd.setProgressPercentFormat(null); 327 } 328 pd.setProgressNumberFormat(null); 329 pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); 330 pd.setMessage(context.getText( 331 com.android.internal.R.string.reboot_to_update_prepare)); 332 } else { 333 if (showSysuiReboot()) { 334 return null; 335 } 336 pd.setIndeterminate(true); 337 pd.setMessage(context.getText( 338 com.android.internal.R.string.reboot_to_update_reboot)); 339 } 340 } else if (mReason != null && mReason.equals(PowerManager.REBOOT_RECOVERY)) { 341 if (RescueParty.isRecoveryTriggeredReboot()) { 342 // We're not actually doing a factory reset yet; we're rebooting 343 // to ask the user if they'd like to reset, so give them a less 344 // scary dialog message. 345 pd.setTitle(context.getText(com.android.internal.R.string.power_off)); 346 pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress)); 347 pd.setIndeterminate(true); 348 } else if (showSysuiReboot()) { 349 return null; 350 } else { 351 // Factory reset path. Set the dialog message accordingly. 352 pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_reset_title)); 353 pd.setMessage(context.getText( 354 com.android.internal.R.string.reboot_to_reset_message)); 355 pd.setIndeterminate(true); 356 } 357 } else { 358 if (showSysuiReboot()) { 359 return null; 360 } 361 pd.setTitle(context.getText(com.android.internal.R.string.power_off)); 362 pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress)); 363 pd.setIndeterminate(true); 364 } 365 pd.setCancelable(false); 366 pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); 367 368 pd.show(); 369 return pd; 370 } 371 showSysuiReboot()372 private static boolean showSysuiReboot() { 373 if (DEBUG) { 374 Log.d(TAG, "Attempting to use SysUI shutdown UI"); 375 } 376 try { 377 StatusBarManagerInternal service = LocalServices.getService( 378 StatusBarManagerInternal.class); 379 if (service.showShutdownUi(mReboot, mReason)) { 380 // Sysui will handle shutdown UI. 381 if (DEBUG) { 382 Log.d(TAG, "SysUI handling shutdown UI"); 383 } 384 return true; 385 } 386 } catch (Exception e) { 387 // If anything went wrong, ignore it and use fallback ui 388 } 389 if (DEBUG) { 390 Log.d(TAG, "SysUI is unavailable"); 391 } 392 return false; 393 } 394 beginShutdownSequence(Context context)395 private static void beginShutdownSequence(Context context) { 396 synchronized (sIsStartedGuard) { 397 if (sIsStarted) { 398 if (DEBUG) { 399 Log.d(TAG, "Shutdown sequence already running, returning."); 400 } 401 return; 402 } 403 sIsStarted = true; 404 } 405 406 sInstance.mProgressDialog = showShutdownDialog(context); 407 sInstance.mContext = context; 408 sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE); 409 410 // make sure we never fall asleep again 411 sInstance.mCpuWakeLock = null; 412 try { 413 sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock( 414 PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu"); 415 sInstance.mCpuWakeLock.setReferenceCounted(false); 416 sInstance.mCpuWakeLock.acquire(); 417 } catch (SecurityException e) { 418 Log.w(TAG, "No permission to acquire wake lock", e); 419 sInstance.mCpuWakeLock = null; 420 } 421 422 // also make sure the screen stays on for better user experience 423 sInstance.mScreenWakeLock = null; 424 if (sInstance.mPowerManager.isScreenOn()) { 425 try { 426 sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock( 427 PowerManager.FULL_WAKE_LOCK, TAG + "-screen"); 428 sInstance.mScreenWakeLock.setReferenceCounted(false); 429 sInstance.mScreenWakeLock.acquire(); 430 } catch (SecurityException e) { 431 Log.w(TAG, "No permission to acquire wake lock", e); 432 sInstance.mScreenWakeLock = null; 433 } 434 } 435 436 if (SecurityLog.isLoggingEnabled()) { 437 SecurityLog.writeEvent(SecurityLog.TAG_OS_SHUTDOWN); 438 } 439 440 // start the thread that initiates shutdown 441 sInstance.mHandler = new Handler() { 442 }; 443 sInstance.start(); 444 } 445 actionDone()446 void actionDone() { 447 synchronized (mActionDoneSync) { 448 mActionDone = true; 449 mActionDoneSync.notifyAll(); 450 } 451 } 452 453 /** 454 * Makes sure we handle the shutdown gracefully. 455 * Shuts off power regardless of radio state if the allotted time has passed. 456 */ run()457 public void run() { 458 TimingsTraceLog shutdownTimingLog = newTimingsLog(); 459 shutdownTimingLog.traceBegin("SystemServerShutdown"); 460 metricShutdownStart(); 461 metricStarted(METRIC_SYSTEM_SERVER); 462 463 // Notify SurfaceFlinger that the device is shutting down. 464 // Transaction traces should be captured at this stage. 465 SurfaceControl.notifyShutdown(); 466 467 // Start dumping check points for this shutdown in a separate thread. 468 Thread dumpCheckPointsThread = ShutdownCheckPoints.newDumpThread( 469 new File(CHECK_POINTS_FILE_BASENAME)); 470 dumpCheckPointsThread.start(); 471 472 /* 473 * Write a system property in case the system_server reboots before we 474 * get to the actual hardware restart. If that happens, we'll retry at 475 * the beginning of the SystemServer startup. 476 */ 477 { 478 String reason = (mReboot ? "1" : "0") + (mReason != null ? mReason : ""); 479 SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason); 480 } 481 482 /* 483 * If we are rebooting into safe mode, write a system property 484 * indicating so. 485 */ 486 if (mRebootSafeMode) { 487 SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1"); 488 } 489 490 shutdownTimingLog.traceBegin("DumpPreRebootInfo"); 491 try { 492 Slog.i(TAG, "Logging pre-reboot information..."); 493 PreRebootLogger.log(mContext); 494 } catch (Exception e) { 495 Slog.e(TAG, "Failed to log pre-reboot information", e); 496 } 497 shutdownTimingLog.traceEnd(); // DumpPreRebootInfo 498 499 metricStarted(METRIC_SEND_BROADCAST); 500 shutdownTimingLog.traceBegin("SendShutdownBroadcast"); 501 Log.i(TAG, "Sending shutdown broadcast..."); 502 503 // First send the high-level shut down broadcast. 504 mActionDone = false; 505 Intent intent = new Intent(Intent.ACTION_SHUTDOWN); 506 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_REGISTERED_ONLY); 507 final Bundle opts = BroadcastOptions.makeBasic() 508 .setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_UNTIL_ACTIVE) 509 .toBundle(); 510 final ActivityManagerInternal activityManagerInternal = LocalServices.getService( 511 ActivityManagerInternal.class); 512 activityManagerInternal.broadcastIntentWithCallback(intent, 513 new IIntentReceiver.Stub() { 514 @Override 515 public void performReceive(Intent intent, int resultCode, String data, 516 Bundle extras, boolean ordered, boolean sticky, int sendingUser) { 517 mHandler.post(ShutdownThread.this::actionDone); 518 } 519 }, null, UserHandle.USER_ALL, null, null, opts); 520 521 final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME; 522 synchronized (mActionDoneSync) { 523 while (!mActionDone) { 524 long delay = endTime - SystemClock.elapsedRealtime(); 525 if (delay <= 0) { 526 Log.w(TAG, "Shutdown broadcast timed out"); 527 break; 528 } else if (mRebootHasProgressBar) { 529 int status = (int)((MAX_BROADCAST_TIME - delay) * 1.0 * 530 BROADCAST_STOP_PERCENT / MAX_BROADCAST_TIME); 531 sInstance.setRebootProgress(status, null); 532 } 533 try { 534 mActionDoneSync.wait(Math.min(delay, ACTION_DONE_POLL_WAIT_MS)); 535 } catch (InterruptedException e) { 536 } 537 } 538 } 539 if (mRebootHasProgressBar) { 540 sInstance.setRebootProgress(BROADCAST_STOP_PERCENT, null); 541 } 542 shutdownTimingLog.traceEnd(); // SendShutdownBroadcast 543 metricEnded(METRIC_SEND_BROADCAST); 544 545 Log.i(TAG, "Shutting down activity manager..."); 546 shutdownTimingLog.traceBegin("ShutdownActivityManager"); 547 metricStarted(METRIC_AM); 548 549 final IActivityManager am = 550 IActivityManager.Stub.asInterface(ServiceManager.checkService("activity")); 551 if (am != null) { 552 try { 553 am.shutdown(MAX_BROADCAST_TIME); 554 } catch (RemoteException e) { 555 } 556 } 557 if (mRebootHasProgressBar) { 558 sInstance.setRebootProgress(ACTIVITY_MANAGER_STOP_PERCENT, null); 559 } 560 shutdownTimingLog.traceEnd();// ShutdownActivityManager 561 metricEnded(METRIC_AM); 562 563 Log.i(TAG, "Shutting down package manager..."); 564 shutdownTimingLog.traceBegin("ShutdownPackageManager"); 565 metricStarted(METRIC_PM); 566 567 final PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class); 568 if (pm != null) { 569 pm.shutdown(); 570 } 571 if (mRebootHasProgressBar) { 572 sInstance.setRebootProgress(PACKAGE_MANAGER_STOP_PERCENT, null); 573 } 574 shutdownTimingLog.traceEnd(); // ShutdownPackageManager 575 metricEnded(METRIC_PM); 576 577 // Shutdown radios. 578 shutdownTimingLog.traceBegin("ShutdownRadios"); 579 metricStarted(METRIC_RADIOS); 580 shutdownRadios(MAX_RADIO_WAIT_TIME); 581 if (mRebootHasProgressBar) { 582 sInstance.setRebootProgress(RADIO_STOP_PERCENT, null); 583 } 584 shutdownTimingLog.traceEnd(); // ShutdownRadios 585 metricEnded(METRIC_RADIOS); 586 587 if (mRebootHasProgressBar) { 588 sInstance.setRebootProgress(MOUNT_SERVICE_STOP_PERCENT, null); 589 590 // If it's to reboot to install an update and uncrypt hasn't been 591 // done yet, trigger it now. 592 uncrypt(); 593 } 594 595 // Wait for the check points dump thread to finish, or kill it if not finished in time. 596 shutdownTimingLog.traceBegin("ShutdownCheckPointsDumpWait"); 597 try { 598 dumpCheckPointsThread.join(MAX_CHECK_POINTS_DUMP_WAIT_TIME); 599 } catch (InterruptedException ex) { 600 } 601 shutdownTimingLog.traceEnd(); // ShutdownCheckPointsDumpWait 602 603 shutdownTimingLog.traceEnd(); // SystemServerShutdown 604 metricEnded(METRIC_SYSTEM_SERVER); 605 saveMetrics(mReboot, mReason); 606 // Remaining work will be done by init, including vold shutdown 607 rebootOrShutdown(mContext, mReboot, mReason); 608 } 609 newTimingsLog()610 private static TimingsTraceLog newTimingsLog() { 611 return new TimingsTraceLog("ShutdownTiming", Trace.TRACE_TAG_SYSTEM_SERVER); 612 } 613 metricStarted(String metricKey)614 private static void metricStarted(String metricKey) { 615 synchronized (TRON_METRICS) { 616 TRON_METRICS.put(metricKey, -1 * SystemClock.elapsedRealtime()); 617 } 618 } 619 metricEnded(String metricKey)620 private static void metricEnded(String metricKey) { 621 synchronized (TRON_METRICS) { 622 TRON_METRICS 623 .put(metricKey, SystemClock.elapsedRealtime() + TRON_METRICS.get(metricKey)); 624 } 625 } 626 metricShutdownStart()627 private static void metricShutdownStart() { 628 synchronized (TRON_METRICS) { 629 TRON_METRICS.put(METRIC_SHUTDOWN_TIME_START, System.currentTimeMillis()); 630 } 631 } 632 setRebootProgress(final int progress, final CharSequence message)633 private void setRebootProgress(final int progress, final CharSequence message) { 634 mHandler.post(new Runnable() { 635 @Override 636 public void run() { 637 if (mProgressDialog != null) { 638 mProgressDialog.setProgress(progress); 639 if (message != null) { 640 mProgressDialog.setMessage(message); 641 } 642 } 643 } 644 }); 645 } 646 shutdownRadios(final int timeout)647 private void shutdownRadios(final int timeout) { 648 // If a radio is wedged, disabling it may hang so we do this work in another thread, 649 // just in case. 650 final long endTime = SystemClock.elapsedRealtime() + timeout; 651 final boolean[] done = new boolean[1]; 652 Thread t = new Thread() { 653 public void run() { 654 TimingsTraceLog shutdownTimingsTraceLog = newTimingsLog(); 655 boolean radioOff; 656 657 TelephonyManager telephonyManager = mContext.getSystemService( 658 TelephonyManager.class); 659 660 radioOff = telephonyManager == null 661 || !telephonyManager.isAnyRadioPoweredOn(); 662 if (!radioOff) { 663 Log.w(TAG, "Turning off cellular radios..."); 664 metricStarted(METRIC_RADIO); 665 telephonyManager.shutdownAllRadios(); 666 } 667 668 Log.i(TAG, "Waiting for Radio..."); 669 670 long delay = endTime - SystemClock.elapsedRealtime(); 671 while (delay > 0) { 672 if (mRebootHasProgressBar) { 673 int status = (int)((timeout - delay) * 1.0 * 674 (RADIO_STOP_PERCENT - PACKAGE_MANAGER_STOP_PERCENT) / timeout); 675 status += PACKAGE_MANAGER_STOP_PERCENT; 676 sInstance.setRebootProgress(status, null); 677 } 678 679 if (!radioOff) { 680 radioOff = !telephonyManager.isAnyRadioPoweredOn(); 681 if (radioOff) { 682 Log.i(TAG, "Radio turned off."); 683 metricEnded(METRIC_RADIO); 684 shutdownTimingsTraceLog 685 .logDuration("ShutdownRadio", TRON_METRICS.get(METRIC_RADIO)); 686 } 687 } 688 689 if (radioOff) { 690 Log.i(TAG, "Radio shutdown complete."); 691 done[0] = true; 692 break; 693 } 694 SystemClock.sleep(RADIOS_STATE_POLL_SLEEP_MS); 695 delay = endTime - SystemClock.elapsedRealtime(); 696 } 697 } 698 }; 699 700 t.start(); 701 try { 702 t.join(timeout); 703 } catch (InterruptedException ex) { 704 } 705 if (!done[0]) { 706 Log.w(TAG, "Timed out waiting for Radio shutdown."); 707 } 708 } 709 710 /** 711 * Do not call this directly. Use {@link #reboot(Context, String, boolean)} 712 * or {@link #shutdown(Context, String, boolean)} instead. 713 * 714 * @param context Context used to vibrate or null without vibration 715 * @param reboot true to reboot or false to shutdown 716 * @param reason reason for reboot/shutdown 717 */ rebootOrShutdown(final Context context, boolean reboot, String reason)718 public static void rebootOrShutdown(final Context context, boolean reboot, String reason) { 719 if (reboot) { 720 Log.i(TAG, "Rebooting, reason: " + reason); 721 PowerManagerService.lowLevelReboot(reason); 722 Log.e(TAG, "Reboot failed, will attempt shutdown instead"); 723 reason = null; 724 } else if (context != null) { 725 // vibrate before shutting down 726 try { 727 sInstance.playShutdownVibration(context); 728 } catch (Exception e) { 729 // Failure to vibrate shouldn't interrupt shutdown. Just log it. 730 Log.w(TAG, "Failed to vibrate during shutdown.", e); 731 } 732 733 } 734 // Shutdown power 735 Log.i(TAG, "Performing low-level shutdown..."); 736 PowerManagerService.lowLevelShutdown(reason); 737 } 738 739 /** 740 * Plays a vibration for shutdown. Along with playing a shutdown vibration, this method also 741 * sleeps the current Thread for some time, to allow the vibration to finish before the device 742 * shuts down. 743 */ 744 @VisibleForTesting // For testing vibrations without shutting down device playShutdownVibration(Context context)745 void playShutdownVibration(Context context) { 746 Vibrator vibrator = mInjector.getVibrator(context); 747 if (!vibrator.hasVibrator()) { 748 return; 749 } 750 751 VibrationEffect vibrationEffect = getValidShutdownVibration(context, vibrator); 752 vibrator.vibrate( 753 vibrationEffect, 754 VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH)); 755 756 // vibrator is asynchronous so we have to wait to avoid shutting down too soon. 757 long vibrationDuration = vibrationEffect.getDuration(); 758 // A negative vibration duration may indicate a vibration effect whose duration is not 759 // known by the system (e.g. pre-baked effects). In that case, use the default shutdown 760 // vibration duration. 761 mInjector.sleep(vibrationDuration < 0 ? DEFAULT_SHUTDOWN_VIBRATE_MS : vibrationDuration); 762 } 763 764 private static void saveMetrics(boolean reboot, String reason) { 765 StringBuilder metricValue = new StringBuilder(); 766 metricValue.append("reboot:"); 767 metricValue.append(reboot ? "y" : "n"); 768 metricValue.append(",").append("reason:").append(reason); 769 final int metricsSize = TRON_METRICS.size(); 770 for (int i = 0; i < metricsSize; i++) { 771 final String name = TRON_METRICS.keyAt(i); 772 final long value = TRON_METRICS.valueAt(i); 773 if (value < 0) { 774 Log.e(TAG, "metricEnded wasn't called for " + name); 775 continue; 776 } 777 metricValue.append(',').append(name).append(':').append(value); 778 } 779 File tmp = new File(METRICS_FILE_BASENAME + ".tmp"); 780 boolean saved = false; 781 try (FileOutputStream fos = new FileOutputStream(tmp)) { 782 fos.write(metricValue.toString().getBytes(StandardCharsets.UTF_8)); 783 saved = true; 784 } catch (IOException e) { 785 Log.e(TAG,"Cannot save shutdown metrics", e); 786 } 787 if (saved) { 788 tmp.renameTo(new File(METRICS_FILE_BASENAME + ".txt")); 789 } 790 } 791 792 private void uncrypt() { 793 Log.i(TAG, "Calling uncrypt and monitoring the progress..."); 794 795 final RecoverySystem.ProgressListener progressListener = 796 new RecoverySystem.ProgressListener() { 797 @Override 798 public void onProgress(int status) { 799 if (status >= 0 && status < 100) { 800 // Scale down to [MOUNT_SERVICE_STOP_PERCENT, 100). 801 status = (int)(status * (100.0 - MOUNT_SERVICE_STOP_PERCENT) / 100); 802 status += MOUNT_SERVICE_STOP_PERCENT; 803 CharSequence msg = mContext.getText( 804 com.android.internal.R.string.reboot_to_update_package); 805 sInstance.setRebootProgress(status, msg); 806 } else if (status == 100) { 807 CharSequence msg = mContext.getText( 808 com.android.internal.R.string.reboot_to_update_reboot); 809 sInstance.setRebootProgress(status, msg); 810 } else { 811 // Ignored 812 } 813 } 814 }; 815 816 final boolean[] done = new boolean[1]; 817 done[0] = false; 818 Thread t = new Thread() { 819 @Override 820 public void run() { 821 RecoverySystem rs = (RecoverySystem) mContext.getSystemService( 822 Context.RECOVERY_SERVICE); 823 String filename = null; 824 try { 825 filename = FileUtils.readTextFile(RecoverySystem.UNCRYPT_PACKAGE_FILE, 0, null); 826 rs.processPackage(mContext, new File(filename), progressListener); 827 } catch (IOException e) { 828 Log.e(TAG, "Error uncrypting file", e); 829 } 830 done[0] = true; 831 } 832 }; 833 t.start(); 834 835 try { 836 t.join(MAX_UNCRYPT_WAIT_TIME); 837 } catch (InterruptedException unused) { 838 } 839 if (!done[0]) { 840 Log.w(TAG, "Timed out waiting for uncrypt."); 841 final int uncryptTimeoutError = 100; 842 String timeoutMessage = String.format("uncrypt_time: %d\n" + "uncrypt_error: %d\n", 843 MAX_UNCRYPT_WAIT_TIME / 1000, uncryptTimeoutError); 844 try { 845 FileUtils.stringToFile(RecoverySystem.UNCRYPT_STATUS_FILE, timeoutMessage); 846 } catch (IOException e) { 847 Log.e(TAG, "Failed to write timeout message to uncrypt status", e); 848 } 849 } 850 } 851 852 /** 853 * Provides a {@link VibrationEffect} to be used for shutdown. 854 * 855 * <p>The vibration to be played is derived from the shutdown vibration file (which the device 856 * should specify at `com.android.internal.R.string.config_defaultShutdownVibrationFile`). A 857 * fallback vibration maybe used in one of these conditions: 858 * <ul> 859 * <li>A vibration file has not been specified, or if the specified file does not exist 860 * <li>If the content of the file does not represent a valid serialization of a 861 * {@link VibrationEffect} 862 * <li>If the {@link VibrationEffect} specified in the file is not suitable for 863 * a shutdown vibration (such as indefinite vibrations) 864 * </ul> 865 */ 866 private VibrationEffect getValidShutdownVibration(Context context, Vibrator vibrator) { 867 VibrationEffect parsedEffect = parseVibrationEffectFromFile( 868 mInjector.getDefaultShutdownVibrationEffectFilePath(context), 869 vibrator); 870 871 if (parsedEffect == null) { 872 return createDefaultVibrationEffect(); 873 } 874 875 long parsedEffectDuration = parsedEffect.getDuration(); 876 if (parsedEffectDuration == Long.MAX_VALUE) { 877 // This means that the effect does not have a defined end. 878 // Since we don't want to vibrate forever while trying to shutdown, we ignore this 879 // parsed effect and use the default one instead. 880 Log.w(TAG, "The parsed shutdown vibration is indefinite."); 881 return createDefaultVibrationEffect(); 882 } 883 884 return parsedEffect; 885 } 886 887 private static VibrationEffect parseVibrationEffectFromFile( 888 String filePath, Vibrator vibrator) { 889 if (!TextUtils.isEmpty(filePath)) { 890 try { 891 return VibrationXmlParser.parseDocument(new FileReader(filePath)).resolve(vibrator); 892 } catch (Exception e) { 893 Log.e(TAG, "Error parsing default shutdown vibration effect.", e); 894 } 895 } 896 return null; 897 } 898 899 private static VibrationEffect createDefaultVibrationEffect() { 900 return VibrationEffect.createOneShot( 901 DEFAULT_SHUTDOWN_VIBRATE_MS, VibrationEffect.DEFAULT_AMPLITUDE); 902 } 903 904 /** Utility class to inject instances, for easy testing. */ 905 @VisibleForTesting 906 static class Injector { 907 public Vibrator getVibrator(Context context) { 908 return new SystemVibrator(context); 909 } 910 911 public void sleep(long durationMs) { 912 try { 913 Thread.sleep(durationMs); 914 } catch (InterruptedException unused) { 915 // this is not critical and does not require logging. 916 } 917 } 918 919 public String getDefaultShutdownVibrationEffectFilePath(Context context) { 920 return context.getResources().getString( 921 com.android.internal.R.string.config_defaultShutdownVibrationFile); 922 } 923 } 924 } 925