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