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