OSDN Git Service

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