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