Home | History | Annotate | Download | only in power
      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 
    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      */
    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 
    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 
    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
    197         public void onReceive(Context context, Intent intent) {
    198             dialog.cancel();
    199         }
    200 
    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      */
    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      */
    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 
    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 
    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      */
    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 
    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 
    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 ||
    547                             bluetooth.getState() == BluetoothAdapter.STATE_OFF;
    548                     if (!bluetoothOff) {
    549                         Log.w(TAG, "Disabling Bluetooth...");
    550                         bluetooth.disable(false);  // disable but don't persist new state
    551                     }
    552                 } catch (RemoteException ex) {
    553                     Log.e(TAG, "RemoteException during bluetooth shutdown", ex);
    554                     bluetoothOff = true;
    555                 }
    556 
    557                 try {
    558                     radioOff = phone == null || !phone.needMobileRadioShutdown();
    559                     if (!radioOff) {
    560                         Log.w(TAG, "Turning off cellular radios...");
    561                         phone.shutdownMobileRadios();
    562                     }
    563                 } catch (RemoteException ex) {
    564                     Log.e(TAG, "RemoteException during radio shutdown", ex);
    565                     radioOff = true;
    566                 }
    567 
    568                 Log.i(TAG, "Waiting for NFC, Bluetooth and Radio...");
    569 
    570                 long delay = endTime - SystemClock.elapsedRealtime();
    571                 while (delay > 0) {
    572                     if (mRebootHasProgressBar) {
    573                         int status = (int)((timeout - delay) * 1.0 *
    574                                 (RADIO_STOP_PERCENT - PACKAGE_MANAGER_STOP_PERCENT) / timeout);
    575                         status += PACKAGE_MANAGER_STOP_PERCENT;
    576                         sInstance.setRebootProgress(status, null);
    577                     }
    578 
    579                     if (!bluetoothOff) {
    580                         try {
    581                             bluetoothOff = bluetooth.getState() == BluetoothAdapter.STATE_OFF;
    582                         } catch (RemoteException ex) {
    583                             Log.e(TAG, "RemoteException during bluetooth shutdown", ex);
    584                             bluetoothOff = true;
    585                         }
    586                         if (bluetoothOff) {
    587                             Log.i(TAG, "Bluetooth turned off.");
    588                         }
    589                     }
    590                     if (!radioOff) {
    591                         try {
    592                             radioOff = !phone.needMobileRadioShutdown();
    593                         } catch (RemoteException ex) {
    594                             Log.e(TAG, "RemoteException during radio shutdown", ex);
    595                             radioOff = true;
    596                         }
    597                         if (radioOff) {
    598                             Log.i(TAG, "Radio turned off.");
    599                         }
    600                     }
    601                     if (!nfcOff) {
    602                         try {
    603                             nfcOff = nfc.getState() == NfcAdapter.STATE_OFF;
    604                         } catch (RemoteException ex) {
    605                             Log.e(TAG, "RemoteException during NFC shutdown", ex);
    606                             nfcOff = true;
    607                         }
    608                         if (nfcOff) {
    609                             Log.i(TAG, "NFC turned off.");
    610                         }
    611                     }
    612 
    613                     if (radioOff && bluetoothOff && nfcOff) {
    614                         Log.i(TAG, "NFC, Radio and Bluetooth shutdown complete.");
    615                         done[0] = true;
    616                         break;
    617                     }
    618                     SystemClock.sleep(PHONE_STATE_POLL_SLEEP_MSEC);
    619 
    620                     delay = endTime - SystemClock.elapsedRealtime();
    621                 }
    622             }
    623         };
    624 
    625         t.start();
    626         try {
    627             t.join(timeout);
    628         } catch (InterruptedException ex) {
    629         }
    630         if (!done[0]) {
    631             Log.w(TAG, "Timed out waiting for NFC, Radio and Bluetooth shutdown.");
    632         }
    633     }
    634 
    635     /**
    636      * Do not call this directly. Use {@link #reboot(Context, String, boolean)}
    637      * or {@link #shutdown(Context, boolean)} instead.
    638      *
    639      * @param context Context used to vibrate or null without vibration
    640      * @param reboot true to reboot or false to shutdown
    641      * @param reason reason for reboot/shutdown
    642      */
    643     public static void rebootOrShutdown(final Context context, boolean reboot, String reason) {
    644         if (reboot) {
    645             Log.i(TAG, "Rebooting, reason: " + reason);
    646             PowerManagerService.lowLevelReboot(reason);
    647             Log.e(TAG, "Reboot failed, will attempt shutdown instead");
    648             reason = null;
    649         } else if (SHUTDOWN_VIBRATE_MS > 0 && context != null) {
    650             // vibrate before shutting down
    651             Vibrator vibrator = new SystemVibrator(context);
    652             try {
    653                 vibrator.vibrate(SHUTDOWN_VIBRATE_MS, VIBRATION_ATTRIBUTES);
    654             } catch (Exception e) {
    655                 // Failure to vibrate shouldn't interrupt shutdown.  Just log it.
    656                 Log.w(TAG, "Failed to vibrate during shutdown.", e);
    657             }
    658 
    659             // vibrator is asynchronous so we need to wait to avoid shutting down too soon.
    660             try {
    661                 Thread.sleep(SHUTDOWN_VIBRATE_MS);
    662             } catch (InterruptedException unused) {
    663             }
    664         }
    665 
    666         // Shutdown power
    667         Log.i(TAG, "Performing low-level shutdown...");
    668         PowerManagerService.lowLevelShutdown(reason);
    669     }
    670 
    671     private void uncrypt() {
    672         Log.i(TAG, "Calling uncrypt and monitoring the progress...");
    673 
    674         final RecoverySystem.ProgressListener progressListener =
    675                 new RecoverySystem.ProgressListener() {
    676             @Override
    677             public void onProgress(int status) {
    678                 if (status >= 0 && status < 100) {
    679                     // Scale down to [MOUNT_SERVICE_STOP_PERCENT, 100).
    680                     status = (int)(status * (100.0 - MOUNT_SERVICE_STOP_PERCENT) / 100);
    681                     status += MOUNT_SERVICE_STOP_PERCENT;
    682                     CharSequence msg = mContext.getText(
    683                             com.android.internal.R.string.reboot_to_update_package);
    684                     sInstance.setRebootProgress(status, msg);
    685                 } else if (status == 100) {
    686                     CharSequence msg = mContext.getText(
    687                             com.android.internal.R.string.reboot_to_update_reboot);
    688                     sInstance.setRebootProgress(status, msg);
    689                 } else {
    690                     // Ignored
    691                 }
    692             }
    693         };
    694 
    695         final boolean[] done = new boolean[1];
    696         done[0] = false;
    697         Thread t = new Thread() {
    698             @Override
    699             public void run() {
    700                 RecoverySystem rs = (RecoverySystem) mContext.getSystemService(
    701                         Context.RECOVERY_SERVICE);
    702                 String filename = null;
    703                 try {
    704                     filename = FileUtils.readTextFile(RecoverySystem.UNCRYPT_PACKAGE_FILE, 0, null);
    705                     rs.processPackage(mContext, new File(filename), progressListener);
    706                 } catch (IOException e) {
    707                     Log.e(TAG, "Error uncrypting file", e);
    708                 }
    709                 done[0] = true;
    710             }
    711         };
    712         t.start();
    713 
    714         try {
    715             t.join(MAX_UNCRYPT_WAIT_TIME);
    716         } catch (InterruptedException unused) {
    717         }
    718         if (!done[0]) {
    719             Log.w(TAG, "Timed out waiting for uncrypt.");
    720         }
    721     }
    722 }
    723