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 (showSysuiReboot()) { 312 return null; 313 } else if (RescueParty.isAttemptingFactoryReset()) { 314 // We're not actually doing a factory reset yet; we're rebooting 315 // to ask the user if they'd like to reset, so give them a less 316 // scary dialog message. 317 pd.setTitle(context.getText(com.android.internal.R.string.power_off)); 318 pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress)); 319 pd.setIndeterminate(true); 320 } else { 321 // Factory reset path. Set the dialog message accordingly. 322 pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_reset_title)); 323 pd.setMessage(context.getText( 324 com.android.internal.R.string.reboot_to_reset_message)); 325 pd.setIndeterminate(true); 326 } 327 } else { 328 if (showSysuiReboot()) { 329 return null; 330 } 331 pd.setTitle(context.getText(com.android.internal.R.string.power_off)); 332 pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress)); 333 pd.setIndeterminate(true); 334 } 335 pd.setCancelable(false); 336 pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); 337 338 pd.show(); 339 return pd; 340 } 341 showSysuiReboot()342 private static boolean showSysuiReboot() { 343 Log.d(TAG, "Attempting to use SysUI shutdown UI"); 344 try { 345 StatusBarManagerInternal service = LocalServices.getService( 346 StatusBarManagerInternal.class); 347 if (service.showShutdownUi(mReboot, mReason)) { 348 // Sysui will handle shutdown UI. 349 Log.d(TAG, "SysUI handling shutdown UI"); 350 return true; 351 } 352 } catch (Exception e) { 353 // If anything went wrong, ignore it and use fallback ui 354 } 355 Log.d(TAG, "SysUI is unavailable"); 356 return false; 357 } 358 beginShutdownSequence(Context context)359 private static void beginShutdownSequence(Context context) { 360 synchronized (sIsStartedGuard) { 361 if (sIsStarted) { 362 Log.d(TAG, "Shutdown sequence already running, returning."); 363 return; 364 } 365 sIsStarted = true; 366 } 367 368 sInstance.mProgressDialog = showShutdownDialog(context); 369 sInstance.mContext = context; 370 sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE); 371 372 // make sure we never fall asleep again 373 sInstance.mCpuWakeLock = null; 374 try { 375 sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock( 376 PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu"); 377 sInstance.mCpuWakeLock.setReferenceCounted(false); 378 sInstance.mCpuWakeLock.acquire(); 379 } catch (SecurityException e) { 380 Log.w(TAG, "No permission to acquire wake lock", e); 381 sInstance.mCpuWakeLock = null; 382 } 383 384 // also make sure the screen stays on for better user experience 385 sInstance.mScreenWakeLock = null; 386 if (sInstance.mPowerManager.isScreenOn()) { 387 try { 388 sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock( 389 PowerManager.FULL_WAKE_LOCK, TAG + "-screen"); 390 sInstance.mScreenWakeLock.setReferenceCounted(false); 391 sInstance.mScreenWakeLock.acquire(); 392 } catch (SecurityException e) { 393 Log.w(TAG, "No permission to acquire wake lock", e); 394 sInstance.mScreenWakeLock = null; 395 } 396 } 397 398 if (SecurityLog.isLoggingEnabled()) { 399 SecurityLog.writeEvent(SecurityLog.TAG_OS_SHUTDOWN); 400 } 401 402 // start the thread that initiates shutdown 403 sInstance.mHandler = new Handler() { 404 }; 405 sInstance.start(); 406 } 407 actionDone()408 void actionDone() { 409 synchronized (mActionDoneSync) { 410 mActionDone = true; 411 mActionDoneSync.notifyAll(); 412 } 413 } 414 415 /** 416 * Makes sure we handle the shutdown gracefully. 417 * Shuts off power regardless of radio state if the allotted time has passed. 418 */ run()419 public void run() { 420 TimingsTraceLog shutdownTimingLog = newTimingsLog(); 421 shutdownTimingLog.traceBegin("SystemServerShutdown"); 422 metricShutdownStart(); 423 metricStarted(METRIC_SYSTEM_SERVER); 424 425 BroadcastReceiver br = new BroadcastReceiver() { 426 @Override public void onReceive(Context context, Intent intent) { 427 // We don't allow apps to cancel this, so ignore the result. 428 actionDone(); 429 } 430 }; 431 432 /* 433 * Write a system property in case the system_server reboots before we 434 * get to the actual hardware restart. If that happens, we'll retry at 435 * the beginning of the SystemServer startup. 436 */ 437 { 438 String reason = (mReboot ? "1" : "0") + (mReason != null ? mReason : ""); 439 SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason); 440 } 441 442 /* 443 * If we are rebooting into safe mode, write a system property 444 * indicating so. 445 */ 446 if (mRebootSafeMode) { 447 SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1"); 448 } 449 450 shutdownTimingLog.traceBegin("DumpPreRebootInfo"); 451 try { 452 Slog.i(TAG, "Logging pre-reboot information..."); 453 PreRebootLogger.log(mContext); 454 } catch (Exception e) { 455 Slog.e(TAG, "Failed to log pre-reboot information", e); 456 } 457 shutdownTimingLog.traceEnd(); // DumpPreRebootInfo 458 459 metricStarted(METRIC_SEND_BROADCAST); 460 shutdownTimingLog.traceBegin("SendShutdownBroadcast"); 461 Log.i(TAG, "Sending shutdown broadcast..."); 462 463 // First send the high-level shut down broadcast. 464 mActionDone = false; 465 Intent intent = new Intent(Intent.ACTION_SHUTDOWN); 466 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND | Intent.FLAG_RECEIVER_REGISTERED_ONLY); 467 mContext.sendOrderedBroadcastAsUser(intent, 468 UserHandle.ALL, null, br, mHandler, 0, null, null); 469 470 final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME; 471 synchronized (mActionDoneSync) { 472 while (!mActionDone) { 473 long delay = endTime - SystemClock.elapsedRealtime(); 474 if (delay <= 0) { 475 Log.w(TAG, "Shutdown broadcast timed out"); 476 break; 477 } else if (mRebootHasProgressBar) { 478 int status = (int)((MAX_BROADCAST_TIME - delay) * 1.0 * 479 BROADCAST_STOP_PERCENT / MAX_BROADCAST_TIME); 480 sInstance.setRebootProgress(status, null); 481 } 482 try { 483 mActionDoneSync.wait(Math.min(delay, ACTION_DONE_POLL_WAIT_MS)); 484 } catch (InterruptedException e) { 485 } 486 } 487 } 488 if (mRebootHasProgressBar) { 489 sInstance.setRebootProgress(BROADCAST_STOP_PERCENT, null); 490 } 491 shutdownTimingLog.traceEnd(); // SendShutdownBroadcast 492 metricEnded(METRIC_SEND_BROADCAST); 493 494 Log.i(TAG, "Shutting down activity manager..."); 495 shutdownTimingLog.traceBegin("ShutdownActivityManager"); 496 metricStarted(METRIC_AM); 497 498 final IActivityManager am = 499 IActivityManager.Stub.asInterface(ServiceManager.checkService("activity")); 500 if (am != null) { 501 try { 502 am.shutdown(MAX_BROADCAST_TIME); 503 } catch (RemoteException e) { 504 } 505 } 506 if (mRebootHasProgressBar) { 507 sInstance.setRebootProgress(ACTIVITY_MANAGER_STOP_PERCENT, null); 508 } 509 shutdownTimingLog.traceEnd();// ShutdownActivityManager 510 metricEnded(METRIC_AM); 511 512 Log.i(TAG, "Shutting down package manager..."); 513 shutdownTimingLog.traceBegin("ShutdownPackageManager"); 514 metricStarted(METRIC_PM); 515 516 final PackageManagerService pm = (PackageManagerService) 517 ServiceManager.getService("package"); 518 if (pm != null) { 519 pm.shutdown(); 520 } 521 if (mRebootHasProgressBar) { 522 sInstance.setRebootProgress(PACKAGE_MANAGER_STOP_PERCENT, null); 523 } 524 shutdownTimingLog.traceEnd(); // ShutdownPackageManager 525 metricEnded(METRIC_PM); 526 527 // Shutdown radios. 528 shutdownTimingLog.traceBegin("ShutdownRadios"); 529 metricStarted(METRIC_RADIOS); 530 shutdownRadios(MAX_RADIO_WAIT_TIME); 531 if (mRebootHasProgressBar) { 532 sInstance.setRebootProgress(RADIO_STOP_PERCENT, null); 533 } 534 shutdownTimingLog.traceEnd(); // ShutdownRadios 535 metricEnded(METRIC_RADIOS); 536 537 if (mRebootHasProgressBar) { 538 sInstance.setRebootProgress(MOUNT_SERVICE_STOP_PERCENT, null); 539 540 // If it's to reboot to install an update and uncrypt hasn't been 541 // done yet, trigger it now. 542 uncrypt(); 543 } 544 545 shutdownTimingLog.traceEnd(); // SystemServerShutdown 546 metricEnded(METRIC_SYSTEM_SERVER); 547 saveMetrics(mReboot, mReason); 548 // Remaining work will be done by init, including vold shutdown 549 rebootOrShutdown(mContext, mReboot, mReason); 550 } 551 newTimingsLog()552 private static TimingsTraceLog newTimingsLog() { 553 return new TimingsTraceLog("ShutdownTiming", Trace.TRACE_TAG_SYSTEM_SERVER); 554 } 555 metricStarted(String metricKey)556 private static void metricStarted(String metricKey) { 557 synchronized (TRON_METRICS) { 558 TRON_METRICS.put(metricKey, -1 * SystemClock.elapsedRealtime()); 559 } 560 } 561 metricEnded(String metricKey)562 private static void metricEnded(String metricKey) { 563 synchronized (TRON_METRICS) { 564 TRON_METRICS 565 .put(metricKey, SystemClock.elapsedRealtime() + TRON_METRICS.get(metricKey)); 566 } 567 } 568 metricShutdownStart()569 private static void metricShutdownStart() { 570 synchronized (TRON_METRICS) { 571 TRON_METRICS.put(METRIC_SHUTDOWN_TIME_START, System.currentTimeMillis()); 572 } 573 } 574 setRebootProgress(final int progress, final CharSequence message)575 private void setRebootProgress(final int progress, final CharSequence message) { 576 mHandler.post(new Runnable() { 577 @Override 578 public void run() { 579 if (mProgressDialog != null) { 580 mProgressDialog.setProgress(progress); 581 if (message != null) { 582 mProgressDialog.setMessage(message); 583 } 584 } 585 } 586 }); 587 } 588 shutdownRadios(final int timeout)589 private void shutdownRadios(final int timeout) { 590 // If a radio is wedged, disabling it may hang so we do this work in another thread, 591 // just in case. 592 final long endTime = SystemClock.elapsedRealtime() + timeout; 593 final boolean[] done = new boolean[1]; 594 Thread t = new Thread() { 595 public void run() { 596 TimingsTraceLog shutdownTimingsTraceLog = newTimingsLog(); 597 boolean radioOff; 598 599 TelephonyManager telephonyManager = mContext.getSystemService( 600 TelephonyManager.class); 601 602 radioOff = telephonyManager == null 603 || !telephonyManager.isAnyRadioPoweredOn(); 604 if (!radioOff) { 605 Log.w(TAG, "Turning off cellular radios..."); 606 metricStarted(METRIC_RADIO); 607 telephonyManager.shutdownAllRadios(); 608 } 609 610 Log.i(TAG, "Waiting for Radio..."); 611 612 long delay = endTime - SystemClock.elapsedRealtime(); 613 while (delay > 0) { 614 if (mRebootHasProgressBar) { 615 int status = (int)((timeout - delay) * 1.0 * 616 (RADIO_STOP_PERCENT - PACKAGE_MANAGER_STOP_PERCENT) / timeout); 617 status += PACKAGE_MANAGER_STOP_PERCENT; 618 sInstance.setRebootProgress(status, null); 619 } 620 621 if (!radioOff) { 622 radioOff = !telephonyManager.isAnyRadioPoweredOn(); 623 if (radioOff) { 624 Log.i(TAG, "Radio turned off."); 625 metricEnded(METRIC_RADIO); 626 shutdownTimingsTraceLog 627 .logDuration("ShutdownRadio", TRON_METRICS.get(METRIC_RADIO)); 628 } 629 } 630 631 if (radioOff) { 632 Log.i(TAG, "Radio shutdown complete."); 633 done[0] = true; 634 break; 635 } 636 SystemClock.sleep(RADIOS_STATE_POLL_SLEEP_MS); 637 delay = endTime - SystemClock.elapsedRealtime(); 638 } 639 } 640 }; 641 642 t.start(); 643 try { 644 t.join(timeout); 645 } catch (InterruptedException ex) { 646 } 647 if (!done[0]) { 648 Log.w(TAG, "Timed out waiting for Radio shutdown."); 649 } 650 } 651 652 /** 653 * Do not call this directly. Use {@link #reboot(Context, String, boolean)} 654 * or {@link #shutdown(Context, String, boolean)} instead. 655 * 656 * @param context Context used to vibrate or null without vibration 657 * @param reboot true to reboot or false to shutdown 658 * @param reason reason for reboot/shutdown 659 */ rebootOrShutdown(final Context context, boolean reboot, String reason)660 public static void rebootOrShutdown(final Context context, boolean reboot, String reason) { 661 if (reboot) { 662 Log.i(TAG, "Rebooting, reason: " + reason); 663 PowerManagerService.lowLevelReboot(reason); 664 Log.e(TAG, "Reboot failed, will attempt shutdown instead"); 665 reason = null; 666 } else if (SHUTDOWN_VIBRATE_MS > 0 && context != null) { 667 // vibrate before shutting down 668 Vibrator vibrator = new SystemVibrator(context); 669 try { 670 vibrator.vibrate(SHUTDOWN_VIBRATE_MS, VIBRATION_ATTRIBUTES); 671 } catch (Exception e) { 672 // Failure to vibrate shouldn't interrupt shutdown. Just log it. 673 Log.w(TAG, "Failed to vibrate during shutdown.", e); 674 } 675 676 // vibrator is asynchronous so we need to wait to avoid shutting down too soon. 677 try { 678 Thread.sleep(SHUTDOWN_VIBRATE_MS); 679 } catch (InterruptedException unused) { 680 } 681 } 682 // Shutdown power 683 Log.i(TAG, "Performing low-level shutdown..."); 684 PowerManagerService.lowLevelShutdown(reason); 685 } 686 saveMetrics(boolean reboot, String reason)687 private static void saveMetrics(boolean reboot, String reason) { 688 StringBuilder metricValue = new StringBuilder(); 689 metricValue.append("reboot:"); 690 metricValue.append(reboot ? "y" : "n"); 691 metricValue.append(",").append("reason:").append(reason); 692 final int metricsSize = TRON_METRICS.size(); 693 for (int i = 0; i < metricsSize; i++) { 694 final String name = TRON_METRICS.keyAt(i); 695 final long value = TRON_METRICS.valueAt(i); 696 if (value < 0) { 697 Log.e(TAG, "metricEnded wasn't called for " + name); 698 continue; 699 } 700 metricValue.append(',').append(name).append(':').append(value); 701 } 702 File tmp = new File(METRICS_FILE_BASENAME + ".tmp"); 703 boolean saved = false; 704 try (FileOutputStream fos = new FileOutputStream(tmp)) { 705 fos.write(metricValue.toString().getBytes(StandardCharsets.UTF_8)); 706 saved = true; 707 } catch (IOException e) { 708 Log.e(TAG,"Cannot save shutdown metrics", e); 709 } 710 if (saved) { 711 tmp.renameTo(new File(METRICS_FILE_BASENAME + ".txt")); 712 } 713 } 714 uncrypt()715 private void uncrypt() { 716 Log.i(TAG, "Calling uncrypt and monitoring the progress..."); 717 718 final RecoverySystem.ProgressListener progressListener = 719 new RecoverySystem.ProgressListener() { 720 @Override 721 public void onProgress(int status) { 722 if (status >= 0 && status < 100) { 723 // Scale down to [MOUNT_SERVICE_STOP_PERCENT, 100). 724 status = (int)(status * (100.0 - MOUNT_SERVICE_STOP_PERCENT) / 100); 725 status += MOUNT_SERVICE_STOP_PERCENT; 726 CharSequence msg = mContext.getText( 727 com.android.internal.R.string.reboot_to_update_package); 728 sInstance.setRebootProgress(status, msg); 729 } else if (status == 100) { 730 CharSequence msg = mContext.getText( 731 com.android.internal.R.string.reboot_to_update_reboot); 732 sInstance.setRebootProgress(status, msg); 733 } else { 734 // Ignored 735 } 736 } 737 }; 738 739 final boolean[] done = new boolean[1]; 740 done[0] = false; 741 Thread t = new Thread() { 742 @Override 743 public void run() { 744 RecoverySystem rs = (RecoverySystem) mContext.getSystemService( 745 Context.RECOVERY_SERVICE); 746 String filename = null; 747 try { 748 filename = FileUtils.readTextFile(RecoverySystem.UNCRYPT_PACKAGE_FILE, 0, null); 749 rs.processPackage(mContext, new File(filename), progressListener); 750 } catch (IOException e) { 751 Log.e(TAG, "Error uncrypting file", e); 752 } 753 done[0] = true; 754 } 755 }; 756 t.start(); 757 758 try { 759 t.join(MAX_UNCRYPT_WAIT_TIME); 760 } catch (InterruptedException unused) { 761 } 762 if (!done[0]) { 763 Log.w(TAG, "Timed out waiting for uncrypt."); 764 final int uncryptTimeoutError = 100; 765 String timeoutMessage = String.format("uncrypt_time: %d\n" + "uncrypt_error: %d\n", 766 MAX_UNCRYPT_WAIT_TIME / 1000, uncryptTimeoutError); 767 try { 768 FileUtils.stringToFile(RecoverySystem.UNCRYPT_STATUS_FILE, timeoutMessage); 769 } catch (IOException e) { 770 Log.e(TAG, "Failed to write timeout message to uncrypt status", e); 771 } 772 } 773 } 774 } 775