ShutdownThread.java revision 2250d56a0b47b93016018340c8f4040325aa5611
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.ProgressDialog; 24import android.bluetooth.BluetoothAdapter; 25import android.bluetooth.IBluetoothManager; 26import android.media.AudioAttributes; 27import android.nfc.NfcAdapter; 28import android.nfc.INfcAdapter; 29import android.content.BroadcastReceiver; 30import android.content.Context; 31import android.content.DialogInterface; 32import android.content.Intent; 33import android.content.IntentFilter; 34import android.os.FileUtils; 35import android.os.Handler; 36import android.os.PowerManager; 37import android.os.RecoverySystem; 38import android.os.RemoteException; 39import android.os.ServiceManager; 40import android.os.SystemClock; 41import android.os.SystemProperties; 42import android.os.UserHandle; 43import android.os.UserManager; 44import android.os.Vibrator; 45import android.os.SystemVibrator; 46import android.os.storage.IStorageShutdownObserver; 47import android.os.storage.IStorageManager; 48import android.system.ErrnoException; 49import android.system.Os; 50 51import com.android.internal.telephony.ITelephony; 52import com.android.server.pm.PackageManagerService; 53 54import android.util.Log; 55import android.view.WindowManager; 56 57import java.io.BufferedReader; 58import java.io.File; 59import java.io.FileReader; 60import java.io.IOException; 61 62public 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 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 // Indicates whether we should stay in safe mode until ro.build.date.utc is newer than this 98 public static final String AUDIT_SAFEMODE_PROPERTY = "persist.sys.audit_safemode"; 99 100 // static instance of this thread 101 private static final ShutdownThread sInstance = new ShutdownThread(); 102 103 private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder() 104 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) 105 .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION) 106 .build(); 107 108 private final Object mActionDoneSync = new Object(); 109 private boolean mActionDone; 110 private Context mContext; 111 private PowerManager mPowerManager; 112 private PowerManager.WakeLock mCpuWakeLock; 113 private PowerManager.WakeLock mScreenWakeLock; 114 private Handler mHandler; 115 116 private static AlertDialog sConfirmDialog; 117 private ProgressDialog mProgressDialog; 118 119 private ShutdownThread() { 120 } 121 122 /** 123 * Request a clean shutdown, waiting for subsystems to clean up their 124 * state etc. Must be called from a Looper thread in which its UI 125 * is shown. 126 * 127 * @param context Context used to display the shutdown progress dialog. 128 * @param reason code to pass to android_reboot() (e.g. "userrequested"), or null. 129 * @param confirm true if user confirmation is needed before shutting down. 130 */ 131 public static void shutdown(final Context context, String reason, boolean confirm) { 132 mReboot = false; 133 mRebootSafeMode = false; 134 mReason = reason; 135 shutdownInner(context, confirm); 136 } 137 138 static void shutdownInner(final Context context, boolean confirm) { 139 // ensure that only one thread is trying to power down. 140 // any additional calls are just returned 141 synchronized (sIsStartedGuard) { 142 if (sIsStarted) { 143 Log.d(TAG, "Request to shutdown already running, returning."); 144 return; 145 } 146 } 147 148 final int longPressBehavior = context.getResources().getInteger( 149 com.android.internal.R.integer.config_longPressOnPowerBehavior); 150 final int resourceId = mRebootSafeMode 151 ? com.android.internal.R.string.reboot_safemode_confirm 152 : (longPressBehavior == 2 153 ? com.android.internal.R.string.shutdown_confirm_question 154 : com.android.internal.R.string.shutdown_confirm); 155 156 Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior); 157 158 if (confirm) { 159 final CloseDialogReceiver closer = new CloseDialogReceiver(context); 160 if (sConfirmDialog != null) { 161 sConfirmDialog.dismiss(); 162 } 163 sConfirmDialog = new AlertDialog.Builder(context) 164 .setTitle(mRebootSafeMode 165 ? com.android.internal.R.string.reboot_safemode_title 166 : com.android.internal.R.string.power_off) 167 .setMessage(resourceId) 168 .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() { 169 public void onClick(DialogInterface dialog, int which) { 170 beginShutdownSequence(context); 171 } 172 }) 173 .setNegativeButton(com.android.internal.R.string.no, null) 174 .create(); 175 closer.dialog = sConfirmDialog; 176 sConfirmDialog.setOnDismissListener(closer); 177 sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); 178 sConfirmDialog.show(); 179 } else { 180 beginShutdownSequence(context); 181 } 182 } 183 184 private static class CloseDialogReceiver extends BroadcastReceiver 185 implements DialogInterface.OnDismissListener { 186 private Context mContext; 187 public Dialog dialog; 188 189 CloseDialogReceiver(Context context) { 190 mContext = context; 191 IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); 192 context.registerReceiver(this, filter); 193 } 194 195 @Override 196 public void onReceive(Context context, Intent intent) { 197 dialog.cancel(); 198 } 199 200 public void onDismiss(DialogInterface unused) { 201 mContext.unregisterReceiver(this); 202 } 203 } 204 205 /** 206 * Request a clean shutdown, waiting for subsystems to clean up their 207 * state etc. Must be called from a Looper thread in which its UI 208 * is shown. 209 * 210 * @param context Context used to display the shutdown progress dialog. 211 * @param reason code to pass to the kernel (e.g. "recovery"), or null. 212 * @param confirm true if user confirmation is needed before shutting down. 213 */ 214 public static void reboot(final Context context, String reason, boolean confirm) { 215 mReboot = true; 216 mRebootSafeMode = false; 217 mRebootHasProgressBar = false; 218 mReason = reason; 219 shutdownInner(context, confirm); 220 } 221 222 /** 223 * Request a reboot into safe mode. Must be called from a Looper thread in which its UI 224 * is shown. 225 * 226 * @param context Context used to display the shutdown progress dialog. 227 * @param confirm true if user confirmation is needed before shutting down. 228 */ 229 public static void rebootSafeMode(final Context context, boolean confirm) { 230 UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE); 231 if (um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) { 232 return; 233 } 234 235 mReboot = true; 236 mRebootSafeMode = true; 237 mRebootHasProgressBar = false; 238 mReason = null; 239 shutdownInner(context, confirm); 240 } 241 242 private static void beginShutdownSequence(Context context) { 243 synchronized (sIsStartedGuard) { 244 if (sIsStarted) { 245 Log.d(TAG, "Shutdown sequence already running, returning."); 246 return; 247 } 248 sIsStarted = true; 249 } 250 251 // Throw up a system dialog to indicate the device is rebooting / shutting down. 252 ProgressDialog pd = new ProgressDialog(context); 253 254 // Path 1: Reboot to recovery for update 255 // Condition: mReason == REBOOT_RECOVERY_UPDATE 256 // 257 // Path 1a: uncrypt needed 258 // Condition: if /cache/recovery/uncrypt_file exists but 259 // /cache/recovery/block.map doesn't. 260 // UI: determinate progress bar (mRebootHasProgressBar == True) 261 // 262 // * Path 1a is expected to be removed once the GmsCore shipped on 263 // device always calls uncrypt prior to reboot. 264 // 265 // Path 1b: uncrypt already done 266 // UI: spinning circle only (no progress bar) 267 // 268 // Path 2: Reboot to recovery for factory reset 269 // Condition: mReason == REBOOT_RECOVERY 270 // UI: spinning circle only (no progress bar) 271 // 272 // Path 3: Regular reboot / shutdown 273 // Condition: Otherwise 274 // UI: spinning circle only (no progress bar) 275 if (PowerManager.REBOOT_RECOVERY_UPDATE.equals(mReason)) { 276 // We need the progress bar if uncrypt will be invoked during the 277 // reboot, which might be time-consuming. 278 mRebootHasProgressBar = RecoverySystem.UNCRYPT_PACKAGE_FILE.exists() 279 && !(RecoverySystem.BLOCK_MAP_FILE.exists()); 280 pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_update_title)); 281 if (mRebootHasProgressBar) { 282 pd.setMax(100); 283 pd.setProgress(0); 284 pd.setIndeterminate(false); 285 pd.setProgressNumberFormat(null); 286 pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); 287 pd.setMessage(context.getText( 288 com.android.internal.R.string.reboot_to_update_prepare)); 289 } else { 290 pd.setIndeterminate(true); 291 pd.setMessage(context.getText( 292 com.android.internal.R.string.reboot_to_update_reboot)); 293 } 294 } else if (PowerManager.REBOOT_RECOVERY.equals(mReason)) { 295 // Factory reset path. Set the dialog message accordingly. 296 pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_reset_title)); 297 pd.setMessage(context.getText( 298 com.android.internal.R.string.reboot_to_reset_message)); 299 pd.setIndeterminate(true); 300 } else { 301 pd.setTitle(context.getText(com.android.internal.R.string.power_off)); 302 pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress)); 303 pd.setIndeterminate(true); 304 } 305 pd.setCancelable(false); 306 pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); 307 308 pd.show(); 309 310 sInstance.mProgressDialog = pd; 311 sInstance.mContext = context; 312 sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE); 313 314 // make sure we never fall asleep again 315 sInstance.mCpuWakeLock = null; 316 try { 317 sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock( 318 PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu"); 319 sInstance.mCpuWakeLock.setReferenceCounted(false); 320 sInstance.mCpuWakeLock.acquire(); 321 } catch (SecurityException e) { 322 Log.w(TAG, "No permission to acquire wake lock", e); 323 sInstance.mCpuWakeLock = null; 324 } 325 326 // also make sure the screen stays on for better user experience 327 sInstance.mScreenWakeLock = null; 328 if (sInstance.mPowerManager.isScreenOn()) { 329 try { 330 sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock( 331 PowerManager.FULL_WAKE_LOCK, TAG + "-screen"); 332 sInstance.mScreenWakeLock.setReferenceCounted(false); 333 sInstance.mScreenWakeLock.acquire(); 334 } catch (SecurityException e) { 335 Log.w(TAG, "No permission to acquire wake lock", e); 336 sInstance.mScreenWakeLock = null; 337 } 338 } 339 340 // start the thread that initiates shutdown 341 sInstance.mHandler = new Handler() { 342 }; 343 sInstance.start(); 344 } 345 346 void actionDone() { 347 synchronized (mActionDoneSync) { 348 mActionDone = true; 349 mActionDoneSync.notifyAll(); 350 } 351 } 352 353 /** 354 * Makes sure we handle the shutdown gracefully. 355 * Shuts off power regardless of radio and bluetooth state if the alloted time has passed. 356 */ 357 public void run() { 358 BroadcastReceiver br = new BroadcastReceiver() { 359 @Override public void onReceive(Context context, Intent intent) { 360 // We don't allow apps to cancel this, so ignore the result. 361 actionDone(); 362 } 363 }; 364 365 /* 366 * Write a system property in case the system_server reboots before we 367 * get to the actual hardware restart. If that happens, we'll retry at 368 * the beginning of the SystemServer startup. 369 */ 370 { 371 String reason = (mReboot ? "1" : "0") + (mReason != null ? mReason : ""); 372 SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason); 373 } 374 375 /* 376 * If we are rebooting into safe mode, write a system property 377 * indicating so. 378 */ 379 if (mRebootSafeMode) { 380 SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1"); 381 } 382 383 Log.i(TAG, "Sending shutdown broadcast..."); 384 385 // First send the high-level shut down broadcast. 386 mActionDone = false; 387 Intent intent = new Intent(Intent.ACTION_SHUTDOWN); 388 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 389 mContext.sendOrderedBroadcastAsUser(intent, 390 UserHandle.ALL, null, br, mHandler, 0, null, null); 391 392 final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME; 393 synchronized (mActionDoneSync) { 394 while (!mActionDone) { 395 long delay = endTime - SystemClock.elapsedRealtime(); 396 if (delay <= 0) { 397 Log.w(TAG, "Shutdown broadcast timed out"); 398 break; 399 } else if (mRebootHasProgressBar) { 400 int status = (int)((MAX_BROADCAST_TIME - delay) * 1.0 * 401 BROADCAST_STOP_PERCENT / MAX_BROADCAST_TIME); 402 sInstance.setRebootProgress(status, null); 403 } 404 try { 405 mActionDoneSync.wait(Math.min(delay, PHONE_STATE_POLL_SLEEP_MSEC)); 406 } catch (InterruptedException e) { 407 } 408 } 409 } 410 if (mRebootHasProgressBar) { 411 sInstance.setRebootProgress(BROADCAST_STOP_PERCENT, null); 412 } 413 414 Log.i(TAG, "Shutting down activity manager..."); 415 416 final IActivityManager am = 417 IActivityManager.Stub.asInterface(ServiceManager.checkService("activity")); 418 if (am != null) { 419 try { 420 am.shutdown(MAX_BROADCAST_TIME); 421 } catch (RemoteException e) { 422 } 423 } 424 if (mRebootHasProgressBar) { 425 sInstance.setRebootProgress(ACTIVITY_MANAGER_STOP_PERCENT, null); 426 } 427 428 Log.i(TAG, "Shutting down package manager..."); 429 430 final PackageManagerService pm = (PackageManagerService) 431 ServiceManager.getService("package"); 432 if (pm != null) { 433 pm.shutdown(); 434 } 435 if (mRebootHasProgressBar) { 436 sInstance.setRebootProgress(PACKAGE_MANAGER_STOP_PERCENT, null); 437 } 438 439 // Shutdown radios. 440 shutdownRadios(MAX_RADIO_WAIT_TIME); 441 if (mRebootHasProgressBar) { 442 sInstance.setRebootProgress(RADIO_STOP_PERCENT, null); 443 } 444 445 // Shutdown StorageManagerService to ensure media is in a safe state 446 IStorageShutdownObserver observer = new IStorageShutdownObserver.Stub() { 447 public void onShutDownComplete(int statusCode) throws RemoteException { 448 Log.w(TAG, "Result code " + statusCode + " from StorageManagerService.shutdown"); 449 actionDone(); 450 } 451 }; 452 453 Log.i(TAG, "Shutting down StorageManagerService"); 454 455 // Set initial variables and time out time. 456 mActionDone = false; 457 final long endShutTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_WAIT_TIME; 458 synchronized (mActionDoneSync) { 459 try { 460 final IStorageManager storageManager = IStorageManager.Stub.asInterface( 461 ServiceManager.checkService("mount")); 462 if (storageManager != null) { 463 storageManager.shutdown(observer); 464 } else { 465 Log.w(TAG, "StorageManagerService unavailable for shutdown"); 466 } 467 } catch (Exception e) { 468 Log.e(TAG, "Exception during StorageManagerService shutdown", e); 469 } 470 while (!mActionDone) { 471 long delay = endShutTime - SystemClock.elapsedRealtime(); 472 if (delay <= 0) { 473 Log.w(TAG, "Shutdown wait timed out"); 474 break; 475 } else if (mRebootHasProgressBar) { 476 int status = (int)((MAX_SHUTDOWN_WAIT_TIME - delay) * 1.0 * 477 (MOUNT_SERVICE_STOP_PERCENT - RADIO_STOP_PERCENT) / 478 MAX_SHUTDOWN_WAIT_TIME); 479 status += RADIO_STOP_PERCENT; 480 sInstance.setRebootProgress(status, null); 481 } 482 try { 483 mActionDoneSync.wait(Math.min(delay, PHONE_STATE_POLL_SLEEP_MSEC)); 484 } catch (InterruptedException e) { 485 } 486 } 487 } 488 if (mRebootHasProgressBar) { 489 sInstance.setRebootProgress(MOUNT_SERVICE_STOP_PERCENT, null); 490 491 // If it's to reboot to install an update and uncrypt hasn't been 492 // done yet, trigger it now. 493 uncrypt(); 494 } 495 496 rebootOrShutdown(mContext, mReboot, mReason); 497 } 498 499 private void setRebootProgress(final int progress, final CharSequence message) { 500 mHandler.post(new Runnable() { 501 @Override 502 public void run() { 503 if (mProgressDialog != null) { 504 mProgressDialog.setProgress(progress); 505 if (message != null) { 506 mProgressDialog.setMessage(message); 507 } 508 } 509 } 510 }); 511 } 512 513 private void shutdownRadios(final int timeout) { 514 // If a radio is wedged, disabling it may hang so we do this work in another thread, 515 // just in case. 516 final long endTime = SystemClock.elapsedRealtime() + timeout; 517 final boolean[] done = new boolean[1]; 518 Thread t = new Thread() { 519 public void run() { 520 boolean nfcOff; 521 boolean bluetoothOff; 522 boolean radioOff; 523 524 final INfcAdapter nfc = 525 INfcAdapter.Stub.asInterface(ServiceManager.checkService("nfc")); 526 final ITelephony phone = 527 ITelephony.Stub.asInterface(ServiceManager.checkService("phone")); 528 final IBluetoothManager bluetooth = 529 IBluetoothManager.Stub.asInterface(ServiceManager.checkService( 530 BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE)); 531 532 try { 533 nfcOff = nfc == null || 534 nfc.getState() == NfcAdapter.STATE_OFF; 535 if (!nfcOff) { 536 Log.w(TAG, "Turning off NFC..."); 537 nfc.disable(false); // Don't persist new state 538 } 539 } catch (RemoteException ex) { 540 Log.e(TAG, "RemoteException during NFC shutdown", ex); 541 nfcOff = true; 542 } 543 544 try { 545 bluetoothOff = bluetooth == null || 546 bluetooth.getState() == BluetoothAdapter.STATE_OFF; 547 if (!bluetoothOff) { 548 Log.w(TAG, "Disabling Bluetooth..."); 549 bluetooth.disable(mContext.getPackageName(), 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.getState() == BluetoothAdapter.STATE_OFF; 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 */ 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 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 final int uncryptTimeoutError = 100; 720 String timeoutMessage = String.format("uncrypt_time: %d\n" + "uncrypt_error: %d\n", 721 MAX_UNCRYPT_WAIT_TIME / 1000, uncryptTimeoutError); 722 try { 723 FileUtils.stringToFile(RecoverySystem.UNCRYPT_STATUS_FILE, timeoutMessage); 724 } catch (IOException e) { 725 Log.e(TAG, "Failed to write timeout message to uncrypt status", e); 726 } 727 } 728 } 729} 730