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