OSDN Git Service

Merge "Auto dim the nav bar to help prevent diff aging" into oc-mr1-dev
[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.KeyguardManager;
24 import android.app.ProgressDialog;
25 import android.app.WallpaperColors;
26 import android.app.WallpaperManager;
27 import android.bluetooth.BluetoothAdapter;
28 import android.bluetooth.IBluetoothManager;
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.graphics.Color;
35 import android.graphics.drawable.ColorDrawable;
36 import android.media.AudioAttributes;
37 import android.nfc.INfcAdapter;
38 import android.nfc.NfcAdapter;
39 import android.os.FileUtils;
40 import android.os.Handler;
41 import android.os.PowerManager;
42 import android.os.RecoverySystem;
43 import android.os.RemoteException;
44 import android.os.ServiceManager;
45 import android.os.SystemClock;
46 import android.os.SystemProperties;
47 import android.os.SystemVibrator;
48 import android.os.Trace;
49 import android.os.UserHandle;
50 import android.os.UserManager;
51 import android.os.Vibrator;
52 import android.os.storage.IStorageManager;
53 import android.os.storage.IStorageShutdownObserver;
54 import android.util.ArrayMap;
55 import android.util.Log;
56 import android.util.TimingsTraceLog;
57 import android.view.WindowManager;
58
59 import com.android.internal.telephony.ITelephony;
60 import com.android.server.RescueParty;
61 import com.android.server.LocalServices;
62 import com.android.server.pm.PackageManagerService;
63 import com.android.server.statusbar.StatusBarManagerInternal;
64
65 import java.io.File;
66 import java.io.FileOutputStream;
67 import java.io.IOException;
68 import java.nio.charset.StandardCharsets;
69
70 public final class ShutdownThread extends Thread {
71     // constants
72     private static final String TAG = "ShutdownThread";
73     private static final int PHONE_STATE_POLL_SLEEP_MSEC = 500;
74     // maximum time we wait for the shutdown broadcast before going on.
75     private static final int MAX_BROADCAST_TIME = 10*1000;
76     private static final int MAX_SHUTDOWN_WAIT_TIME = 20*1000;
77     private static final int MAX_RADIO_WAIT_TIME = 12*1000;
78     private static final int MAX_UNCRYPT_WAIT_TIME = 15*60*1000;
79     // constants for progress bar. the values are roughly estimated based on timeout.
80     private static final int BROADCAST_STOP_PERCENT = 2;
81     private static final int ACTIVITY_MANAGER_STOP_PERCENT = 4;
82     private static final int PACKAGE_MANAGER_STOP_PERCENT = 6;
83     private static final int RADIO_STOP_PERCENT = 18;
84     private static final int MOUNT_SERVICE_STOP_PERCENT = 20;
85
86     // length of vibration before shutting down
87     private static final int SHUTDOWN_VIBRATE_MS = 500;
88
89     // state tracking
90     private static final Object sIsStartedGuard = new Object();
91     private static boolean sIsStarted = false;
92
93     private static boolean mReboot;
94     private static boolean mRebootSafeMode;
95     private static boolean mRebootHasProgressBar;
96     private static String mReason;
97
98     // Provides shutdown assurance in case the system_server is killed
99     public static final String SHUTDOWN_ACTION_PROPERTY = "sys.shutdown.requested";
100
101     // Indicates whether we are rebooting into safe mode
102     public static final String REBOOT_SAFEMODE_PROPERTY = "persist.sys.safemode";
103     public static final String RO_SAFEMODE_PROPERTY = "ro.sys.safemode";
104
105     // static instance of this thread
106     private static final ShutdownThread sInstance = new ShutdownThread();
107
108     private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
109             .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
110             .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
111             .build();
112
113     // Metrics that will be reported to tron after reboot
114     private static final ArrayMap<String, Long> TRON_METRICS = new ArrayMap<>();
115
116     // File to use for save metrics
117     private static final String METRICS_FILE_BASENAME = "/data/system/shutdown-metrics";
118
119     // Metrics names to be persisted in shutdown-metrics file
120     private static String METRIC_SYSTEM_SERVER = "shutdown_system_server";
121     private static String METRIC_SEND_BROADCAST = "shutdown_send_shutdown_broadcast";
122     private static String METRIC_AM = "shutdown_activity_manager";
123     private static String METRIC_PM = "shutdown_package_manager";
124     private static String METRIC_RADIOS = "shutdown_radios";
125     private static String METRIC_BT = "shutdown_bt";
126     private static String METRIC_RADIO = "shutdown_radio";
127     private static String METRIC_NFC = "shutdown_nfc";
128     private static String METRIC_SM = "shutdown_storage_manager";
129
130     private final Object mActionDoneSync = new Object();
131     private boolean mActionDone;
132     private Context mContext;
133     private PowerManager mPowerManager;
134     private PowerManager.WakeLock mCpuWakeLock;
135     private PowerManager.WakeLock mScreenWakeLock;
136     private Handler mHandler;
137
138     private static AlertDialog sConfirmDialog;
139     private ProgressDialog mProgressDialog;
140
141     private ShutdownThread() {
142     }
143
144     /**
145      * Request a clean shutdown, waiting for subsystems to clean up their
146      * state etc.  Must be called from a Looper thread in which its UI
147      * is shown.
148      *
149      * @param context Context used to display the shutdown progress dialog. This must be a context
150      *                suitable for displaying UI (aka Themable).
151      * @param reason code to pass to android_reboot() (e.g. "userrequested"), or null.
152      * @param confirm true if user confirmation is needed before shutting down.
153      */
154     public static void shutdown(final Context context, String reason, boolean confirm) {
155         mReboot = false;
156         mRebootSafeMode = false;
157         mReason = reason;
158         shutdownInner(context, confirm);
159     }
160
161     private static void shutdownInner(final Context context, boolean confirm) {
162         // ShutdownThread is called from many places, so best to verify here that the context passed
163         // in is themed.
164         context.assertRuntimeOverlayThemable();
165
166         // ensure that only one thread is trying to power down.
167         // any additional calls are just returned
168         synchronized (sIsStartedGuard) {
169             if (sIsStarted) {
170                 Log.d(TAG, "Request to shutdown already running, returning.");
171                 return;
172             }
173         }
174
175         final int longPressBehavior = context.getResources().getInteger(
176                         com.android.internal.R.integer.config_longPressOnPowerBehavior);
177         final int resourceId = mRebootSafeMode
178                 ? com.android.internal.R.string.reboot_safemode_confirm
179                 : (longPressBehavior == 2
180                         ? com.android.internal.R.string.shutdown_confirm_question
181                         : com.android.internal.R.string.shutdown_confirm);
182
183         Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior);
184
185         if (confirm) {
186             final CloseDialogReceiver closer = new CloseDialogReceiver(context);
187             if (sConfirmDialog != null) {
188                 sConfirmDialog.dismiss();
189             }
190             sConfirmDialog = new AlertDialog.Builder(context)
191                     .setTitle(mRebootSafeMode
192                             ? com.android.internal.R.string.reboot_safemode_title
193                             : com.android.internal.R.string.power_off)
194                     .setMessage(resourceId)
195                     .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {
196                         public void onClick(DialogInterface dialog, int which) {
197                             beginShutdownSequence(context);
198                         }
199                     })
200                     .setNegativeButton(com.android.internal.R.string.no, null)
201                     .create();
202             closer.dialog = sConfirmDialog;
203             sConfirmDialog.setOnDismissListener(closer);
204             sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
205             sConfirmDialog.show();
206         } else {
207             beginShutdownSequence(context);
208         }
209     }
210
211     private static class CloseDialogReceiver extends BroadcastReceiver
212             implements DialogInterface.OnDismissListener {
213         private Context mContext;
214         public Dialog dialog;
215
216         CloseDialogReceiver(Context context) {
217             mContext = context;
218             IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
219             context.registerReceiver(this, filter);
220         }
221
222         @Override
223         public void onReceive(Context context, Intent intent) {
224             dialog.cancel();
225         }
226
227         public void onDismiss(DialogInterface unused) {
228             mContext.unregisterReceiver(this);
229         }
230     }
231
232     /**
233      * Request a clean shutdown, waiting for subsystems to clean up their
234      * state etc.  Must be called from a Looper thread in which its UI
235      * is shown.
236      *
237      * @param context Context used to display the shutdown progress dialog. This must be a context
238      *                suitable for displaying UI (aka Themable).
239      * @param reason code to pass to the kernel (e.g. "recovery"), or null.
240      * @param confirm true if user confirmation is needed before shutting down.
241      */
242     public static void reboot(final Context context, String reason, boolean confirm) {
243         mReboot = true;
244         mRebootSafeMode = false;
245         mRebootHasProgressBar = false;
246         mReason = reason;
247         shutdownInner(context, confirm);
248     }
249
250     /**
251      * Request a reboot into safe mode.  Must be called from a Looper thread in which its UI
252      * is shown.
253      *
254      * @param context Context used to display the shutdown progress dialog. This must be a context
255      *                suitable for displaying UI (aka Themable).
256      * @param confirm true if user confirmation is needed before shutting down.
257      */
258     public static void rebootSafeMode(final Context context, boolean confirm) {
259         UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
260         if (um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
261             return;
262         }
263
264         mReboot = true;
265         mRebootSafeMode = true;
266         mRebootHasProgressBar = false;
267         mReason = null;
268         shutdownInner(context, confirm);
269     }
270
271     private static ProgressDialog showShutdownDialog(Context context) {
272         // Throw up a system dialog to indicate the device is rebooting / shutting down.
273         ProgressDialog pd = new ProgressDialog(context);
274
275         // Path 1: Reboot to recovery for update
276         //   Condition: mReason startswith REBOOT_RECOVERY_UPDATE
277         //
278         //  Path 1a: uncrypt needed
279         //   Condition: if /cache/recovery/uncrypt_file exists but
280         //              /cache/recovery/block.map doesn't.
281         //   UI: determinate progress bar (mRebootHasProgressBar == True)
282         //
283         // * Path 1a is expected to be removed once the GmsCore shipped on
284         //   device always calls uncrypt prior to reboot.
285         //
286         //  Path 1b: uncrypt already done
287         //   UI: spinning circle only (no progress bar)
288         //
289         // Path 2: Reboot to recovery for factory reset
290         //   Condition: mReason == REBOOT_RECOVERY
291         //   UI: spinning circle only (no progress bar)
292         //
293         // Path 3: Regular reboot / shutdown
294         //   Condition: Otherwise
295         //   UI: spinning circle only (no progress bar)
296
297         // mReason could be "recovery-update" or "recovery-update,quiescent".
298         if (mReason != null && mReason.startsWith(PowerManager.REBOOT_RECOVERY_UPDATE)) {
299             // We need the progress bar if uncrypt will be invoked during the
300             // reboot, which might be time-consuming.
301             mRebootHasProgressBar = RecoverySystem.UNCRYPT_PACKAGE_FILE.exists()
302                     && !(RecoverySystem.BLOCK_MAP_FILE.exists());
303             pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_update_title));
304             if (mRebootHasProgressBar) {
305                 pd.setMax(100);
306                 pd.setProgress(0);
307                 pd.setIndeterminate(false);
308                 pd.setProgressNumberFormat(null);
309                 pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
310                 pd.setMessage(context.getText(
311                             com.android.internal.R.string.reboot_to_update_prepare));
312             } else {
313                 if (showSysuiReboot()) {
314                     return null;
315                 }
316                 pd.setIndeterminate(true);
317                 pd.setMessage(context.getText(
318                             com.android.internal.R.string.reboot_to_update_reboot));
319             }
320         } else if (mReason != null && mReason.equals(PowerManager.REBOOT_RECOVERY)) {
321             if (RescueParty.isAttemptingFactoryReset()) {
322                 // We're not actually doing a factory reset yet; we're rebooting
323                 // to ask the user if they'd like to reset, so give them a less
324                 // scary dialog message.
325                 pd.setTitle(context.getText(com.android.internal.R.string.power_off));
326                 pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
327                 pd.setIndeterminate(true);
328             } else {
329                 // Factory reset path. Set the dialog message accordingly.
330                 pd.setTitle(context.getText(com.android.internal.R.string.reboot_to_reset_title));
331                 pd.setMessage(context.getText(
332                             com.android.internal.R.string.reboot_to_reset_message));
333                 pd.setIndeterminate(true);
334             }
335         } else {
336             if (showSysuiReboot()) {
337                 return null;
338             }
339             pd.setTitle(context.getText(com.android.internal.R.string.power_off));
340             pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
341             pd.setIndeterminate(true);
342         }
343         pd.setCancelable(false);
344         pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
345
346         pd.show();
347         return pd;
348     }
349
350     private static boolean showSysuiReboot() {
351         Log.d(TAG, "Attempting to use SysUI shutdown UI");
352         try {
353             StatusBarManagerInternal service = LocalServices.getService(
354                     StatusBarManagerInternal.class);
355             if (service.showShutdownUi(mReboot, mReason)) {
356                 // Sysui will handle shutdown UI.
357                 Log.d(TAG, "SysUI handling shutdown UI");
358                 return true;
359             }
360         } catch (Exception e) {
361             // If anything went wrong, ignore it and use fallback ui
362         }
363         Log.d(TAG, "SysUI is unavailable");
364         return false;
365     }
366
367     private static void beginShutdownSequence(Context context) {
368         synchronized (sIsStartedGuard) {
369             if (sIsStarted) {
370                 Log.d(TAG, "Shutdown sequence already running, returning.");
371                 return;
372             }
373             sIsStarted = true;
374         }
375
376         sInstance.mProgressDialog = showShutdownDialog(context);
377         sInstance.mContext = context;
378         sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
379
380         // make sure we never fall asleep again
381         sInstance.mCpuWakeLock = null;
382         try {
383             sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock(
384                     PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu");
385             sInstance.mCpuWakeLock.setReferenceCounted(false);
386             sInstance.mCpuWakeLock.acquire();
387         } catch (SecurityException e) {
388             Log.w(TAG, "No permission to acquire wake lock", e);
389             sInstance.mCpuWakeLock = null;
390         }
391
392         // also make sure the screen stays on for better user experience
393         sInstance.mScreenWakeLock = null;
394         if (sInstance.mPowerManager.isScreenOn()) {
395             try {
396                 sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock(
397                         PowerManager.FULL_WAKE_LOCK, TAG + "-screen");
398                 sInstance.mScreenWakeLock.setReferenceCounted(false);
399                 sInstance.mScreenWakeLock.acquire();
400             } catch (SecurityException e) {
401                 Log.w(TAG, "No permission to acquire wake lock", e);
402                 sInstance.mScreenWakeLock = null;
403             }
404         }
405
406         // start the thread that initiates shutdown
407         sInstance.mHandler = new Handler() {
408         };
409         sInstance.start();
410     }
411
412     void actionDone() {
413         synchronized (mActionDoneSync) {
414             mActionDone = true;
415             mActionDoneSync.notifyAll();
416         }
417     }
418
419     /**
420      * Makes sure we handle the shutdown gracefully.
421      * Shuts off power regardless of radio and bluetooth state if the alloted time has passed.
422      */
423     public void run() {
424         TimingsTraceLog shutdownTimingLog = newTimingsLog();
425         shutdownTimingLog.traceBegin("SystemServerShutdown");
426         metricStarted(METRIC_SYSTEM_SERVER);
427
428         BroadcastReceiver br = new BroadcastReceiver() {
429             @Override public void onReceive(Context context, Intent intent) {
430                 // We don't allow apps to cancel this, so ignore the result.
431                 actionDone();
432             }
433         };
434
435         /*
436          * Write a system property in case the system_server reboots before we
437          * get to the actual hardware restart. If that happens, we'll retry at
438          * the beginning of the SystemServer startup.
439          */
440         {
441             String reason = (mReboot ? "1" : "0") + (mReason != null ? mReason : "");
442             SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);
443         }
444
445         /*
446          * If we are rebooting into safe mode, write a system property
447          * indicating so.
448          */
449         if (mRebootSafeMode) {
450             SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");
451         }
452
453         metricStarted(METRIC_SEND_BROADCAST);
454         shutdownTimingLog.traceBegin("SendShutdownBroadcast");
455         Log.i(TAG, "Sending shutdown broadcast...");
456
457         // First send the high-level shut down broadcast.
458         mActionDone = false;
459         Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
460         intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND
461                 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
462         mContext.sendOrderedBroadcastAsUser(intent,
463                 UserHandle.ALL, null, br, mHandler, 0, null, null);
464
465         final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
466         synchronized (mActionDoneSync) {
467             while (!mActionDone) {
468                 long delay = endTime - SystemClock.elapsedRealtime();
469                 if (delay <= 0) {
470                     Log.w(TAG, "Shutdown broadcast timed out");
471                     break;
472                 } else if (mRebootHasProgressBar) {
473                     int status = (int)((MAX_BROADCAST_TIME - delay) * 1.0 *
474                             BROADCAST_STOP_PERCENT / MAX_BROADCAST_TIME);
475                     sInstance.setRebootProgress(status, null);
476                 }
477                 try {
478                     mActionDoneSync.wait(Math.min(delay, PHONE_STATE_POLL_SLEEP_MSEC));
479                 } catch (InterruptedException e) {
480                 }
481             }
482         }
483         if (mRebootHasProgressBar) {
484             sInstance.setRebootProgress(BROADCAST_STOP_PERCENT, null);
485         }
486         shutdownTimingLog.traceEnd(); // SendShutdownBroadcast
487         metricEnded(METRIC_SEND_BROADCAST);
488
489         Log.i(TAG, "Shutting down activity manager...");
490         shutdownTimingLog.traceBegin("ShutdownActivityManager");
491         metricStarted(METRIC_AM);
492
493         final IActivityManager am =
494                 IActivityManager.Stub.asInterface(ServiceManager.checkService("activity"));
495         if (am != null) {
496             try {
497                 am.shutdown(MAX_BROADCAST_TIME);
498             } catch (RemoteException e) {
499             }
500         }
501         if (mRebootHasProgressBar) {
502             sInstance.setRebootProgress(ACTIVITY_MANAGER_STOP_PERCENT, null);
503         }
504         shutdownTimingLog.traceEnd();// ShutdownActivityManager
505         metricEnded(METRIC_AM);
506
507         Log.i(TAG, "Shutting down package manager...");
508         shutdownTimingLog.traceBegin("ShutdownPackageManager");
509         metricStarted(METRIC_PM);
510
511         final PackageManagerService pm = (PackageManagerService)
512             ServiceManager.getService("package");
513         if (pm != null) {
514             pm.shutdown();
515         }
516         if (mRebootHasProgressBar) {
517             sInstance.setRebootProgress(PACKAGE_MANAGER_STOP_PERCENT, null);
518         }
519         shutdownTimingLog.traceEnd(); // ShutdownPackageManager
520         metricEnded(METRIC_PM);
521
522         // Shutdown radios.
523         shutdownTimingLog.traceBegin("ShutdownRadios");
524         metricStarted(METRIC_RADIOS);
525         shutdownRadios(MAX_RADIO_WAIT_TIME);
526         if (mRebootHasProgressBar) {
527             sInstance.setRebootProgress(RADIO_STOP_PERCENT, null);
528         }
529         shutdownTimingLog.traceEnd(); // ShutdownRadios
530         metricEnded(METRIC_RADIOS);
531
532         // Shutdown StorageManagerService to ensure media is in a safe state
533         IStorageShutdownObserver observer = new IStorageShutdownObserver.Stub() {
534             public void onShutDownComplete(int statusCode) throws RemoteException {
535                 Log.w(TAG, "Result code " + statusCode + " from StorageManagerService.shutdown");
536                 actionDone();
537             }
538         };
539
540         Log.i(TAG, "Shutting down StorageManagerService");
541         shutdownTimingLog.traceBegin("ShutdownStorageManager");
542         metricStarted(METRIC_SM);
543
544         // Set initial variables and time out time.
545         mActionDone = false;
546         final long endShutTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_WAIT_TIME;
547         synchronized (mActionDoneSync) {
548             try {
549                 final IStorageManager storageManager = IStorageManager.Stub.asInterface(
550                         ServiceManager.checkService("mount"));
551                 if (storageManager != null) {
552                     storageManager.shutdown(observer);
553                 } else {
554                     Log.w(TAG, "StorageManagerService unavailable for shutdown");
555                 }
556             } catch (Exception e) {
557                 Log.e(TAG, "Exception during StorageManagerService shutdown", e);
558             }
559             while (!mActionDone) {
560                 long delay = endShutTime - SystemClock.elapsedRealtime();
561                 if (delay <= 0) {
562                     Log.w(TAG, "StorageManager shutdown wait timed out");
563                     break;
564                 } else if (mRebootHasProgressBar) {
565                     int status = (int)((MAX_SHUTDOWN_WAIT_TIME - delay) * 1.0 *
566                             (MOUNT_SERVICE_STOP_PERCENT - RADIO_STOP_PERCENT) /
567                             MAX_SHUTDOWN_WAIT_TIME);
568                     status += RADIO_STOP_PERCENT;
569                     sInstance.setRebootProgress(status, null);
570                 }
571                 try {
572                     mActionDoneSync.wait(Math.min(delay, PHONE_STATE_POLL_SLEEP_MSEC));
573                 } catch (InterruptedException e) {
574                 }
575             }
576         }
577         shutdownTimingLog.traceEnd(); // ShutdownStorageManager
578         metricEnded(METRIC_SM);
579
580         if (mRebootHasProgressBar) {
581             sInstance.setRebootProgress(MOUNT_SERVICE_STOP_PERCENT, null);
582
583             // If it's to reboot to install an update and uncrypt hasn't been
584             // done yet, trigger it now.
585             uncrypt();
586         }
587
588         shutdownTimingLog.traceEnd(); // SystemServerShutdown
589         metricEnded(METRIC_SYSTEM_SERVER);
590         saveMetrics(mReboot);
591         rebootOrShutdown(mContext, mReboot, mReason);
592     }
593
594     private static TimingsTraceLog newTimingsLog() {
595         return new TimingsTraceLog("ShutdownTiming", Trace.TRACE_TAG_SYSTEM_SERVER);
596     }
597
598     private static void metricStarted(String metricKey) {
599         synchronized (TRON_METRICS) {
600             TRON_METRICS.put(metricKey, -1 * SystemClock.elapsedRealtime());
601         }
602     }
603
604     private static void metricEnded(String metricKey) {
605         synchronized (TRON_METRICS) {
606             TRON_METRICS
607                     .put(metricKey, SystemClock.elapsedRealtime() + TRON_METRICS.get(metricKey));
608         }
609     }
610
611     private void setRebootProgress(final int progress, final CharSequence message) {
612         mHandler.post(new Runnable() {
613             @Override
614             public void run() {
615                 if (mProgressDialog != null) {
616                     mProgressDialog.setProgress(progress);
617                     if (message != null) {
618                         mProgressDialog.setMessage(message);
619                     }
620                 }
621             }
622         });
623     }
624
625     private void shutdownRadios(final int timeout) {
626         // If a radio is wedged, disabling it may hang so we do this work in another thread,
627         // just in case.
628         final long endTime = SystemClock.elapsedRealtime() + timeout;
629         final boolean[] done = new boolean[1];
630         Thread t = new Thread() {
631             public void run() {
632                 TimingsTraceLog shutdownTimingsTraceLog = newTimingsLog();
633                 boolean nfcOff;
634                 boolean bluetoothReadyForShutdown;
635                 boolean radioOff;
636
637                 final INfcAdapter nfc =
638                         INfcAdapter.Stub.asInterface(ServiceManager.checkService("nfc"));
639                 final ITelephony phone =
640                         ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
641                 final IBluetoothManager bluetooth =
642                         IBluetoothManager.Stub.asInterface(ServiceManager.checkService(
643                                 BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE));
644                 try {
645                     nfcOff = nfc == null ||
646                              nfc.getState() == NfcAdapter.STATE_OFF;
647                     if (!nfcOff) {
648                         Log.w(TAG, "Turning off NFC...");
649                         metricStarted(METRIC_NFC);
650                         nfc.disable(false); // Don't persist new state
651                     }
652                 } catch (RemoteException ex) {
653                 Log.e(TAG, "RemoteException during NFC shutdown", ex);
654                     nfcOff = true;
655                 }
656
657                 try {
658                     bluetoothReadyForShutdown = bluetooth == null ||
659                             bluetooth.getState() == BluetoothAdapter.STATE_OFF;
660                     if (!bluetoothReadyForShutdown) {
661                         Log.w(TAG, "Disabling Bluetooth...");
662                         metricStarted(METRIC_BT);
663                         bluetooth.disable(mContext.getPackageName(), false);  // disable but don't persist new state
664                     }
665                 } catch (RemoteException ex) {
666                     Log.e(TAG, "RemoteException during bluetooth shutdown", ex);
667                     bluetoothReadyForShutdown = true;
668                 }
669
670                 try {
671                     radioOff = phone == null || !phone.needMobileRadioShutdown();
672                     if (!radioOff) {
673                         Log.w(TAG, "Turning off cellular radios...");
674                         metricStarted(METRIC_RADIO);
675                         phone.shutdownMobileRadios();
676                     }
677                 } catch (RemoteException ex) {
678                     Log.e(TAG, "RemoteException during radio shutdown", ex);
679                     radioOff = true;
680                 }
681
682                 Log.i(TAG, "Waiting for NFC, Bluetooth and Radio...");
683
684                 long delay = endTime - SystemClock.elapsedRealtime();
685                 while (delay > 0) {
686                     if (mRebootHasProgressBar) {
687                         int status = (int)((timeout - delay) * 1.0 *
688                                 (RADIO_STOP_PERCENT - PACKAGE_MANAGER_STOP_PERCENT) / timeout);
689                         status += PACKAGE_MANAGER_STOP_PERCENT;
690                         sInstance.setRebootProgress(status, null);
691                     }
692
693                     if (!bluetoothReadyForShutdown) {
694                         try {
695                           // BLE only mode can happen when BT is turned off
696                           // We will continue shutting down in such case
697                           bluetoothReadyForShutdown =
698                                   bluetooth.getState() == BluetoothAdapter.STATE_OFF ||
699                                   bluetooth.getState() == BluetoothAdapter.STATE_BLE_TURNING_OFF ||
700                                   bluetooth.getState() == BluetoothAdapter.STATE_BLE_ON;
701                         } catch (RemoteException ex) {
702                             Log.e(TAG, "RemoteException during bluetooth shutdown", ex);
703                             bluetoothReadyForShutdown = true;
704                         }
705                         if (bluetoothReadyForShutdown) {
706                             Log.i(TAG, "Bluetooth turned off.");
707                             metricEnded(METRIC_BT);
708                             shutdownTimingsTraceLog
709                                     .logDuration("ShutdownBt", TRON_METRICS.get(METRIC_BT));
710                         }
711                     }
712                     if (!radioOff) {
713                         try {
714                             radioOff = !phone.needMobileRadioShutdown();
715                         } catch (RemoteException ex) {
716                             Log.e(TAG, "RemoteException during radio shutdown", ex);
717                             radioOff = true;
718                         }
719                         if (radioOff) {
720                             Log.i(TAG, "Radio turned off.");
721                             metricEnded(METRIC_RADIO);
722                             shutdownTimingsTraceLog
723                                     .logDuration("ShutdownRadio", TRON_METRICS.get(METRIC_RADIO));
724                         }
725                     }
726                     if (!nfcOff) {
727                         try {
728                             nfcOff = nfc.getState() == NfcAdapter.STATE_OFF;
729                         } catch (RemoteException ex) {
730                             Log.e(TAG, "RemoteException during NFC shutdown", ex);
731                             nfcOff = true;
732                         }
733                         if (nfcOff) {
734                             Log.i(TAG, "NFC turned off.");
735                             metricEnded(METRIC_NFC);
736                             shutdownTimingsTraceLog
737                                     .logDuration("ShutdownNfc", TRON_METRICS.get(METRIC_NFC));
738                         }
739                     }
740
741                     if (radioOff && bluetoothReadyForShutdown && nfcOff) {
742                         Log.i(TAG, "NFC, Radio and Bluetooth shutdown complete.");
743                         done[0] = true;
744                         break;
745                     }
746                     SystemClock.sleep(PHONE_STATE_POLL_SLEEP_MSEC);
747
748                     delay = endTime - SystemClock.elapsedRealtime();
749                 }
750             }
751         };
752
753         t.start();
754         try {
755             t.join(timeout);
756         } catch (InterruptedException ex) {
757         }
758         if (!done[0]) {
759             Log.w(TAG, "Timed out waiting for NFC, Radio and Bluetooth shutdown.");
760         }
761     }
762
763     /**
764      * Do not call this directly. Use {@link #reboot(Context, String, boolean)}
765      * or {@link #shutdown(Context, String, boolean)} instead.
766      *
767      * @param context Context used to vibrate or null without vibration
768      * @param reboot true to reboot or false to shutdown
769      * @param reason reason for reboot/shutdown
770      */
771     public static void rebootOrShutdown(final Context context, boolean reboot, String reason) {
772         if (reboot) {
773             Log.i(TAG, "Rebooting, reason: " + reason);
774             PowerManagerService.lowLevelReboot(reason);
775             Log.e(TAG, "Reboot failed, will attempt shutdown instead");
776             reason = null;
777         } else if (SHUTDOWN_VIBRATE_MS > 0 && context != null) {
778             // vibrate before shutting down
779             Vibrator vibrator = new SystemVibrator(context);
780             try {
781                 vibrator.vibrate(SHUTDOWN_VIBRATE_MS, VIBRATION_ATTRIBUTES);
782             } catch (Exception e) {
783                 // Failure to vibrate shouldn't interrupt shutdown.  Just log it.
784                 Log.w(TAG, "Failed to vibrate during shutdown.", e);
785             }
786
787             // vibrator is asynchronous so we need to wait to avoid shutting down too soon.
788             try {
789                 Thread.sleep(SHUTDOWN_VIBRATE_MS);
790             } catch (InterruptedException unused) {
791             }
792         }
793         // Shutdown power
794         Log.i(TAG, "Performing low-level shutdown...");
795         PowerManagerService.lowLevelShutdown(reason);
796     }
797
798     private static void saveMetrics(boolean reboot) {
799         StringBuilder metricValue = new StringBuilder();
800         metricValue.append("reboot:");
801         metricValue.append(reboot ? "y" : "n");
802         final int metricsSize = TRON_METRICS.size();
803         for (int i = 0; i < metricsSize; i++) {
804             final String name = TRON_METRICS.keyAt(i);
805             final long value = TRON_METRICS.valueAt(i);
806             if (value < 0) {
807                 Log.e(TAG, "metricEnded wasn't called for " + name);
808                 continue;
809             }
810             metricValue.append(',').append(name).append(':').append(value);
811         }
812         File tmp = new File(METRICS_FILE_BASENAME + ".tmp");
813         boolean saved = false;
814         try (FileOutputStream fos = new FileOutputStream(tmp)) {
815             fos.write(metricValue.toString().getBytes(StandardCharsets.UTF_8));
816             saved = true;
817         } catch (IOException e) {
818             Log.e(TAG,"Cannot save shutdown metrics", e);
819         }
820         if (saved) {
821             tmp.renameTo(new File(METRICS_FILE_BASENAME + ".txt"));
822         }
823     }
824
825     private void uncrypt() {
826         Log.i(TAG, "Calling uncrypt and monitoring the progress...");
827
828         final RecoverySystem.ProgressListener progressListener =
829                 new RecoverySystem.ProgressListener() {
830             @Override
831             public void onProgress(int status) {
832                 if (status >= 0 && status < 100) {
833                     // Scale down to [MOUNT_SERVICE_STOP_PERCENT, 100).
834                     status = (int)(status * (100.0 - MOUNT_SERVICE_STOP_PERCENT) / 100);
835                     status += MOUNT_SERVICE_STOP_PERCENT;
836                     CharSequence msg = mContext.getText(
837                             com.android.internal.R.string.reboot_to_update_package);
838                     sInstance.setRebootProgress(status, msg);
839                 } else if (status == 100) {
840                     CharSequence msg = mContext.getText(
841                             com.android.internal.R.string.reboot_to_update_reboot);
842                     sInstance.setRebootProgress(status, msg);
843                 } else {
844                     // Ignored
845                 }
846             }
847         };
848
849         final boolean[] done = new boolean[1];
850         done[0] = false;
851         Thread t = new Thread() {
852             @Override
853             public void run() {
854                 RecoverySystem rs = (RecoverySystem) mContext.getSystemService(
855                         Context.RECOVERY_SERVICE);
856                 String filename = null;
857                 try {
858                     filename = FileUtils.readTextFile(RecoverySystem.UNCRYPT_PACKAGE_FILE, 0, null);
859                     rs.processPackage(mContext, new File(filename), progressListener);
860                 } catch (IOException e) {
861                     Log.e(TAG, "Error uncrypting file", e);
862                 }
863                 done[0] = true;
864             }
865         };
866         t.start();
867
868         try {
869             t.join(MAX_UNCRYPT_WAIT_TIME);
870         } catch (InterruptedException unused) {
871         }
872         if (!done[0]) {
873             Log.w(TAG, "Timed out waiting for uncrypt.");
874             final int uncryptTimeoutError = 100;
875             String timeoutMessage = String.format("uncrypt_time: %d\n" + "uncrypt_error: %d\n",
876                     MAX_UNCRYPT_WAIT_TIME / 1000, uncryptTimeoutError);
877             try {
878                 FileUtils.stringToFile(RecoverySystem.UNCRYPT_STATUS_FILE, timeoutMessage);
879             } catch (IOException e) {
880                 Log.e(TAG, "Failed to write timeout message to uncrypt status", e);
881             }
882         }
883     }
884 }