OSDN Git Service

am ac7927a8: Merge "LayoutInflater should always try to generate LayoutParams for...
[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.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.Handler;
36 import android.os.PowerManager;
37 import android.os.RemoteException;
38 import android.os.ServiceManager;
39 import android.os.SystemClock;
40 import android.os.SystemProperties;
41 import android.os.UserHandle;
42 import android.os.UserManager;
43 import android.os.Vibrator;
44 import android.os.SystemVibrator;
45 import android.os.storage.IMountService;
46 import android.os.storage.IMountShutdownObserver;
47
48 import com.android.internal.telephony.ITelephony;
49 import com.android.server.pm.PackageManagerService;
50
51 import android.util.Log;
52 import android.view.WindowManager;
53
54 public final class ShutdownThread extends Thread {
55     // constants
56     private static final String TAG = "ShutdownThread";
57     private static final int PHONE_STATE_POLL_SLEEP_MSEC = 500;
58     // maximum time we wait for the shutdown broadcast before going on.
59     private static final int MAX_BROADCAST_TIME = 10*1000;
60     private static final int MAX_SHUTDOWN_WAIT_TIME = 20*1000;
61     private static final int MAX_RADIO_WAIT_TIME = 12*1000;
62
63     // length of vibration before shutting down
64     private static final int SHUTDOWN_VIBRATE_MS = 500;
65     
66     // state tracking
67     private static Object sIsStartedGuard = new Object();
68     private static boolean sIsStarted = false;
69     
70     private static boolean mReboot;
71     private static boolean mRebootSafeMode;
72     private static String mRebootReason;
73
74     // Provides shutdown assurance in case the system_server is killed
75     public static final String SHUTDOWN_ACTION_PROPERTY = "sys.shutdown.requested";
76
77     // Indicates whether we are rebooting into safe mode
78     public static final String REBOOT_SAFEMODE_PROPERTY = "persist.sys.safemode";
79
80     // static instance of this thread
81     private static final ShutdownThread sInstance = new ShutdownThread();
82
83     private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
84             .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
85             .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
86             .build();
87
88     private final Object mActionDoneSync = new Object();
89     private boolean mActionDone;
90     private Context mContext;
91     private PowerManager mPowerManager;
92     private PowerManager.WakeLock mCpuWakeLock;
93     private PowerManager.WakeLock mScreenWakeLock;
94     private Handler mHandler;
95
96     private static AlertDialog sConfirmDialog;
97     
98     private ShutdownThread() {
99     }
100  
101     /**
102      * Request a clean shutdown, waiting for subsystems to clean up their
103      * state etc.  Must be called from a Looper thread in which its UI
104      * is shown.
105      *
106      * @param context Context used to display the shutdown progress dialog.
107      * @param confirm true if user confirmation is needed before shutting down.
108      */
109     public static void shutdown(final Context context, boolean confirm) {
110         mReboot = false;
111         mRebootSafeMode = false;
112         shutdownInner(context, confirm);
113     }
114
115     static void shutdownInner(final Context context, boolean confirm) {
116         // ensure that only one thread is trying to power down.
117         // any additional calls are just returned
118         synchronized (sIsStartedGuard) {
119             if (sIsStarted) {
120                 Log.d(TAG, "Request to shutdown already running, returning.");
121                 return;
122             }
123         }
124
125         final int longPressBehavior = context.getResources().getInteger(
126                         com.android.internal.R.integer.config_longPressOnPowerBehavior);
127         final int resourceId = mRebootSafeMode
128                 ? com.android.internal.R.string.reboot_safemode_confirm
129                 : (longPressBehavior == 2
130                         ? com.android.internal.R.string.shutdown_confirm_question
131                         : com.android.internal.R.string.shutdown_confirm);
132
133         Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior);
134
135         if (confirm) {
136             final CloseDialogReceiver closer = new CloseDialogReceiver(context);
137             if (sConfirmDialog != null) {
138                 sConfirmDialog.dismiss();
139             }
140             sConfirmDialog = new AlertDialog.Builder(context)
141                     .setTitle(mRebootSafeMode
142                             ? com.android.internal.R.string.reboot_safemode_title
143                             : com.android.internal.R.string.power_off)
144                     .setMessage(resourceId)
145                     .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {
146                         public void onClick(DialogInterface dialog, int which) {
147                             beginShutdownSequence(context);
148                         }
149                     })
150                     .setNegativeButton(com.android.internal.R.string.no, null)
151                     .create();
152             closer.dialog = sConfirmDialog;
153             sConfirmDialog.setOnDismissListener(closer);
154             sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
155             sConfirmDialog.show();
156         } else {
157             beginShutdownSequence(context);
158         }
159     }
160
161     private static class CloseDialogReceiver extends BroadcastReceiver
162             implements DialogInterface.OnDismissListener {
163         private Context mContext;
164         public Dialog dialog;
165
166         CloseDialogReceiver(Context context) {
167             mContext = context;
168             IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
169             context.registerReceiver(this, filter);
170         }
171
172         @Override
173         public void onReceive(Context context, Intent intent) {
174             dialog.cancel();
175         }
176
177         public void onDismiss(DialogInterface unused) {
178             mContext.unregisterReceiver(this);
179         }
180     }
181
182     /**
183      * Request a clean shutdown, waiting for subsystems to clean up their
184      * state etc.  Must be called from a Looper thread in which its UI
185      * is shown.
186      *
187      * @param context Context used to display the shutdown progress dialog.
188      * @param reason code to pass to the kernel (e.g. "recovery"), or null.
189      * @param confirm true if user confirmation is needed before shutting down.
190      */
191     public static void reboot(final Context context, String reason, boolean confirm) {
192         mReboot = true;
193         mRebootSafeMode = false;
194         mRebootReason = reason;
195         shutdownInner(context, confirm);
196     }
197
198     /**
199      * Request a reboot into safe mode.  Must be called from a Looper thread in which its UI
200      * is shown.
201      *
202      * @param context Context used to display the shutdown progress dialog.
203      * @param confirm true if user confirmation is needed before shutting down.
204      */
205     public static void rebootSafeMode(final Context context, boolean confirm) {
206         UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
207         if (um.hasUserRestriction(UserManager.DISALLOW_SAFE_BOOT)) {
208             return;
209         }
210
211         mReboot = true;
212         mRebootSafeMode = true;
213         mRebootReason = null;
214         shutdownInner(context, confirm);
215     }
216
217     private static void beginShutdownSequence(Context context) {
218         synchronized (sIsStartedGuard) {
219             if (sIsStarted) {
220                 Log.d(TAG, "Shutdown sequence already running, returning.");
221                 return;
222             }
223             sIsStarted = true;
224         }
225
226         // throw up an indeterminate system dialog to indicate radio is
227         // shutting down.
228         ProgressDialog pd = new ProgressDialog(context);
229         pd.setTitle(context.getText(com.android.internal.R.string.power_off));
230         pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
231         pd.setIndeterminate(true);
232         pd.setCancelable(false);
233         pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
234
235         pd.show();
236
237         sInstance.mContext = context;
238         sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
239
240         // make sure we never fall asleep again
241         sInstance.mCpuWakeLock = null;
242         try {
243             sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock(
244                     PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu");
245             sInstance.mCpuWakeLock.setReferenceCounted(false);
246             sInstance.mCpuWakeLock.acquire();
247         } catch (SecurityException e) {
248             Log.w(TAG, "No permission to acquire wake lock", e);
249             sInstance.mCpuWakeLock = null;
250         }
251
252         // also make sure the screen stays on for better user experience
253         sInstance.mScreenWakeLock = null;
254         if (sInstance.mPowerManager.isScreenOn()) {
255             try {
256                 sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock(
257                         PowerManager.FULL_WAKE_LOCK, TAG + "-screen");
258                 sInstance.mScreenWakeLock.setReferenceCounted(false);
259                 sInstance.mScreenWakeLock.acquire();
260             } catch (SecurityException e) {
261                 Log.w(TAG, "No permission to acquire wake lock", e);
262                 sInstance.mScreenWakeLock = null;
263             }
264         }
265
266         // start the thread that initiates shutdown
267         sInstance.mHandler = new Handler() {
268         };
269         sInstance.start();
270     }
271
272     void actionDone() {
273         synchronized (mActionDoneSync) {
274             mActionDone = true;
275             mActionDoneSync.notifyAll();
276         }
277     }
278
279     /**
280      * Makes sure we handle the shutdown gracefully.
281      * Shuts off power regardless of radio and bluetooth state if the alloted time has passed.
282      */
283     public void run() {
284         BroadcastReceiver br = new BroadcastReceiver() {
285             @Override public void onReceive(Context context, Intent intent) {
286                 // We don't allow apps to cancel this, so ignore the result.
287                 actionDone();
288             }
289         };
290
291         /*
292          * Write a system property in case the system_server reboots before we
293          * get to the actual hardware restart. If that happens, we'll retry at
294          * the beginning of the SystemServer startup.
295          */
296         {
297             String reason = (mReboot ? "1" : "0") + (mRebootReason != null ? mRebootReason : "");
298             SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);
299         }
300
301         /*
302          * If we are rebooting into safe mode, write a system property
303          * indicating so.
304          */
305         if (mRebootSafeMode) {
306             SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");
307         }
308
309         Log.i(TAG, "Sending shutdown broadcast...");
310         
311         // First send the high-level shut down broadcast.
312         mActionDone = false;
313         Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
314         intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
315         mContext.sendOrderedBroadcastAsUser(intent,
316                 UserHandle.ALL, null, br, mHandler, 0, null, null);
317         
318         final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
319         synchronized (mActionDoneSync) {
320             while (!mActionDone) {
321                 long delay = endTime - SystemClock.elapsedRealtime();
322                 if (delay <= 0) {
323                     Log.w(TAG, "Shutdown broadcast timed out");
324                     break;
325                 }
326                 try {
327                     mActionDoneSync.wait(delay);
328                 } catch (InterruptedException e) {
329                 }
330             }
331         }
332         
333         Log.i(TAG, "Shutting down activity manager...");
334         
335         final IActivityManager am =
336             ActivityManagerNative.asInterface(ServiceManager.checkService("activity"));
337         if (am != null) {
338             try {
339                 am.shutdown(MAX_BROADCAST_TIME);
340             } catch (RemoteException e) {
341             }
342         }
343
344         Log.i(TAG, "Shutting down package manager...");
345
346         final PackageManagerService pm = (PackageManagerService)
347             ServiceManager.getService("package");
348         if (pm != null) {
349             pm.shutdown();
350         }
351
352         // Shutdown radios.
353         shutdownRadios(MAX_RADIO_WAIT_TIME);
354
355         // Shutdown MountService to ensure media is in a safe state
356         IMountShutdownObserver observer = new IMountShutdownObserver.Stub() {
357             public void onShutDownComplete(int statusCode) throws RemoteException {
358                 Log.w(TAG, "Result code " + statusCode + " from MountService.shutdown");
359                 actionDone();
360             }
361         };
362
363         Log.i(TAG, "Shutting down MountService");
364
365         // Set initial variables and time out time.
366         mActionDone = false;
367         final long endShutTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_WAIT_TIME;
368         synchronized (mActionDoneSync) {
369             try {
370                 final IMountService mount = IMountService.Stub.asInterface(
371                         ServiceManager.checkService("mount"));
372                 if (mount != null) {
373                     mount.shutdown(observer);
374                 } else {
375                     Log.w(TAG, "MountService unavailable for shutdown");
376                 }
377             } catch (Exception e) {
378                 Log.e(TAG, "Exception during MountService shutdown", e);
379             }
380             while (!mActionDone) {
381                 long delay = endShutTime - SystemClock.elapsedRealtime();
382                 if (delay <= 0) {
383                     Log.w(TAG, "Shutdown wait timed out");
384                     break;
385                 }
386                 try {
387                     mActionDoneSync.wait(delay);
388                 } catch (InterruptedException e) {
389                 }
390             }
391         }
392
393         rebootOrShutdown(mReboot, mRebootReason);
394     }
395
396     private void shutdownRadios(int timeout) {
397         // If a radio is wedged, disabling it may hang so we do this work in another thread,
398         // just in case.
399         final long endTime = SystemClock.elapsedRealtime() + timeout;
400         final boolean[] done = new boolean[1];
401         Thread t = new Thread() {
402             public void run() {
403                 boolean nfcOff;
404                 boolean bluetoothOff;
405                 boolean radioOff;
406
407                 final INfcAdapter nfc =
408                         INfcAdapter.Stub.asInterface(ServiceManager.checkService("nfc"));
409                 final ITelephony phone =
410                         ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
411                 final IBluetoothManager bluetooth =
412                         IBluetoothManager.Stub.asInterface(ServiceManager.checkService(
413                                 BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE));
414
415                 try {
416                     nfcOff = nfc == null ||
417                              nfc.getState() == NfcAdapter.STATE_OFF;
418                     if (!nfcOff) {
419                         Log.w(TAG, "Turning off NFC...");
420                         nfc.disable(false); // Don't persist new state
421                     }
422                 } catch (RemoteException ex) {
423                 Log.e(TAG, "RemoteException during NFC shutdown", ex);
424                     nfcOff = true;
425                 }
426
427                 try {
428                     bluetoothOff = bluetooth == null || !bluetooth.isEnabled();
429                     if (!bluetoothOff) {
430                         Log.w(TAG, "Disabling Bluetooth...");
431                         bluetooth.disable(false);  // disable but don't persist new state
432                     }
433                 } catch (RemoteException ex) {
434                     Log.e(TAG, "RemoteException during bluetooth shutdown", ex);
435                     bluetoothOff = true;
436                 }
437
438                 try {
439                     radioOff = phone == null || !phone.needMobileRadioShutdown();
440                     if (!radioOff) {
441                         Log.w(TAG, "Turning off cellular radios...");
442                         phone.shutdownMobileRadios();
443                     }
444                 } catch (RemoteException ex) {
445                     Log.e(TAG, "RemoteException during radio shutdown", ex);
446                     radioOff = true;
447                 }
448
449                 Log.i(TAG, "Waiting for NFC, Bluetooth and Radio...");
450
451                 while (SystemClock.elapsedRealtime() < endTime) {
452                     if (!bluetoothOff) {
453                         try {
454                             bluetoothOff = !bluetooth.isEnabled();
455                         } catch (RemoteException ex) {
456                             Log.e(TAG, "RemoteException during bluetooth shutdown", ex);
457                             bluetoothOff = true;
458                         }
459                         if (bluetoothOff) {
460                             Log.i(TAG, "Bluetooth turned off.");
461                         }
462                     }
463                     if (!radioOff) {
464                         try {
465                             radioOff = !phone.needMobileRadioShutdown();
466                         } catch (RemoteException ex) {
467                             Log.e(TAG, "RemoteException during radio shutdown", ex);
468                             radioOff = true;
469                         }
470                         if (radioOff) {
471                             Log.i(TAG, "Radio turned off.");
472                         }
473                     }
474                     if (!nfcOff) {
475                         try {
476                             nfcOff = nfc.getState() == NfcAdapter.STATE_OFF;
477                         } catch (RemoteException ex) {
478                             Log.e(TAG, "RemoteException during NFC shutdown", ex);
479                             nfcOff = true;
480                         }
481                         if (nfcOff) {
482                             Log.i(TAG, "NFC turned off.");
483                         }
484                     }
485
486                     if (radioOff && bluetoothOff && nfcOff) {
487                         Log.i(TAG, "NFC, Radio and Bluetooth shutdown complete.");
488                         done[0] = true;
489                         break;
490                     }
491                     SystemClock.sleep(PHONE_STATE_POLL_SLEEP_MSEC);
492                 }
493             }
494         };
495
496         t.start();
497         try {
498             t.join(timeout);
499         } catch (InterruptedException ex) {
500         }
501         if (!done[0]) {
502             Log.w(TAG, "Timed out waiting for NFC, Radio and Bluetooth shutdown.");
503         }
504     }
505
506     /**
507      * Do not call this directly. Use {@link #reboot(Context, String, boolean)}
508      * or {@link #shutdown(Context, boolean)} instead.
509      *
510      * @param reboot true to reboot or false to shutdown
511      * @param reason reason for reboot
512      */
513     public static void rebootOrShutdown(boolean reboot, String reason) {
514         if (reboot) {
515             Log.i(TAG, "Rebooting, reason: " + reason);
516             PowerManagerService.lowLevelReboot(reason);
517             Log.e(TAG, "Reboot failed, will attempt shutdown instead");
518         } else if (SHUTDOWN_VIBRATE_MS > 0) {
519             // vibrate before shutting down
520             Vibrator vibrator = new SystemVibrator();
521             try {
522                 vibrator.vibrate(SHUTDOWN_VIBRATE_MS, VIBRATION_ATTRIBUTES);
523             } catch (Exception e) {
524                 // Failure to vibrate shouldn't interrupt shutdown.  Just log it.
525                 Log.w(TAG, "Failed to vibrate during shutdown.", e);
526             }
527
528             // vibrator is asynchronous so we need to wait to avoid shutting down too soon.
529             try {
530                 Thread.sleep(SHUTDOWN_VIBRATE_MS);
531             } catch (InterruptedException unused) {
532             }
533         }
534
535         // Shutdown power
536         Log.i(TAG, "Performing low-level shutdown...");
537         PowerManagerService.lowLevelShutdown();
538     }
539 }