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