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