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