blob: b7255bb1914c32ca9cce1ad168637179a2ac8104 [file] [log] [blame]
Dianne Hackborn55280a92009-05-07 15:53:46 -07001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17
18package com.android.internal.app;
19
20import android.app.ActivityManagerNative;
21import android.app.IActivityManager;
22import android.app.ProgressDialog;
23import android.app.AlertDialog;
Nick Pellybd022f42009-08-14 18:33:38 -070024import android.bluetooth.BluetoothAdapter;
25import android.bluetooth.IBluetooth;
Dianne Hackborn55280a92009-05-07 15:53:46 -070026import android.content.BroadcastReceiver;
27import android.content.Context;
28import android.content.DialogInterface;
29import android.content.Intent;
30import android.os.Handler;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -080031import android.os.Power;
Dianne Hackbornf99ae762010-03-08 12:43:51 -080032import android.os.PowerManager;
Dianne Hackborn55280a92009-05-07 15:53:46 -070033import android.os.RemoteException;
Dianne Hackborn55280a92009-05-07 15:53:46 -070034import android.os.ServiceManager;
35import android.os.SystemClock;
Kenny Rootf547d672010-09-22 10:36:48 -070036import android.os.SystemProperties;
Mike Lockwooda717f642010-04-01 20:01:44 -070037import android.os.Vibrator;
San Mehatb1043402010-02-05 08:26:50 -080038import android.os.storage.IMountService;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -080039import android.os.storage.IMountShutdownObserver;
Dianne Hackborn568cae52009-10-07 16:13:39 -070040
Dianne Hackborn55280a92009-05-07 15:53:46 -070041import com.android.internal.telephony.ITelephony;
42import android.util.Log;
43import android.view.WindowManager;
44
45public final class ShutdownThread extends Thread {
46 // constants
47 private static final String TAG = "ShutdownThread";
48 private static final int MAX_NUM_PHONE_STATE_READS = 16;
49 private static final int PHONE_STATE_POLL_SLEEP_MSEC = 500;
50 // maximum time we wait for the shutdown broadcast before going on.
51 private static final int MAX_BROADCAST_TIME = 10*1000;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -080052 private static final int MAX_SHUTDOWN_WAIT_TIME = 20*1000;
Mike Lockwooda717f642010-04-01 20:01:44 -070053
54 // length of vibration before shutting down
55 private static final int SHUTDOWN_VIBRATE_MS = 500;
Dianne Hackborn55280a92009-05-07 15:53:46 -070056
57 // state tracking
58 private static Object sIsStartedGuard = new Object();
59 private static boolean sIsStarted = false;
60
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -080061 private static boolean mReboot;
62 private static String mRebootReason;
63
Kenny Rootf547d672010-09-22 10:36:48 -070064 // Provides shutdown assurance in case the system_server is killed
65 public static final String SHUTDOWN_ACTION_PROPERTY = "sys.shutdown.requested";
66
Dianne Hackborn55280a92009-05-07 15:53:46 -070067 // static instance of this thread
68 private static final ShutdownThread sInstance = new ShutdownThread();
69
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -080070 private final Object mActionDoneSync = new Object();
71 private boolean mActionDone;
Dianne Hackborn55280a92009-05-07 15:53:46 -070072 private Context mContext;
Dianne Hackbornf99ae762010-03-08 12:43:51 -080073 private PowerManager mPowerManager;
74 private PowerManager.WakeLock mWakeLock;
Dianne Hackborn55280a92009-05-07 15:53:46 -070075 private Handler mHandler;
76
77 private ShutdownThread() {
78 }
79
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -080080 /**
Dianne Hackborn55280a92009-05-07 15:53:46 -070081 * Request a clean shutdown, waiting for subsystems to clean up their
82 * state etc. Must be called from a Looper thread in which its UI
83 * is shown.
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -080084 *
Dianne Hackborn55280a92009-05-07 15:53:46 -070085 * @param context Context used to display the shutdown progress dialog.
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -080086 * @param confirm true if user confirmation is needed before shutting down.
Dianne Hackborn55280a92009-05-07 15:53:46 -070087 */
88 public static void shutdown(final Context context, boolean confirm) {
89 // ensure that only one thread is trying to power down.
90 // any additional calls are just returned
Mike Lockwoodd67b2362010-07-26 07:18:21 -040091 synchronized (sIsStartedGuard) {
Dianne Hackborn55280a92009-05-07 15:53:46 -070092 if (sIsStarted) {
93 Log.d(TAG, "Request to shutdown already running, returning.");
94 return;
95 }
96 }
97
98 Log.d(TAG, "Notifying thread to start radio shutdown");
99
100 if (confirm) {
101 final AlertDialog dialog = new AlertDialog.Builder(context)
102 .setIcon(android.R.drawable.ic_dialog_alert)
103 .setTitle(com.android.internal.R.string.power_off)
104 .setMessage(com.android.internal.R.string.shutdown_confirm)
105 .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {
106 public void onClick(DialogInterface dialog, int which) {
107 beginShutdownSequence(context);
108 }
109 })
110 .setNegativeButton(com.android.internal.R.string.no, null)
111 .create();
112 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
Dianne Hackborn568cae52009-10-07 16:13:39 -0700113 if (!context.getResources().getBoolean(
114 com.android.internal.R.bool.config_sf_slowBlur)) {
115 dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
116 }
Dianne Hackborn55280a92009-05-07 15:53:46 -0700117 dialog.show();
118 } else {
119 beginShutdownSequence(context);
120 }
121 }
122
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800123 /**
124 * Request a clean shutdown, waiting for subsystems to clean up their
125 * state etc. Must be called from a Looper thread in which its UI
126 * is shown.
127 *
128 * @param context Context used to display the shutdown progress dialog.
129 * @param reason code to pass to the kernel (e.g. "recovery"), or null.
130 * @param confirm true if user confirmation is needed before shutting down.
131 */
132 public static void reboot(final Context context, String reason, boolean confirm) {
133 mReboot = true;
134 mRebootReason = reason;
135 shutdown(context, confirm);
136 }
137
Dianne Hackborn55280a92009-05-07 15:53:46 -0700138 private static void beginShutdownSequence(Context context) {
139 synchronized (sIsStartedGuard) {
Mike Lockwoodd67b2362010-07-26 07:18:21 -0400140 if (sIsStarted) {
141 Log.d(TAG, "Request to shutdown already running, returning.");
142 return;
143 }
Dianne Hackborn55280a92009-05-07 15:53:46 -0700144 sIsStarted = true;
145 }
146
147 // throw up an indeterminate system dialog to indicate radio is
148 // shutting down.
149 ProgressDialog pd = new ProgressDialog(context);
150 pd.setTitle(context.getText(com.android.internal.R.string.power_off));
151 pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
152 pd.setIndeterminate(true);
153 pd.setCancelable(false);
154 pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
Dianne Hackborn568cae52009-10-07 16:13:39 -0700155 if (!context.getResources().getBoolean(
156 com.android.internal.R.bool.config_sf_slowBlur)) {
157 pd.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
158 }
Dianne Hackborn55280a92009-05-07 15:53:46 -0700159
160 pd.show();
161
162 // start the thread that initiates shutdown
163 sInstance.mContext = context;
Dianne Hackbornf99ae762010-03-08 12:43:51 -0800164 sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
165 sInstance.mWakeLock = null;
166 if (sInstance.mPowerManager.isScreenOn()) {
167 try {
168 sInstance.mWakeLock = sInstance.mPowerManager.newWakeLock(
169 PowerManager.FULL_WAKE_LOCK, "Shutdown");
170 sInstance.mWakeLock.acquire();
171 } catch (SecurityException e) {
172 Log.w(TAG, "No permission to acquire wake lock", e);
173 sInstance.mWakeLock = null;
174 }
175 }
Dianne Hackborn55280a92009-05-07 15:53:46 -0700176 sInstance.mHandler = new Handler() {
177 };
178 sInstance.start();
179 }
180
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800181 void actionDone() {
182 synchronized (mActionDoneSync) {
183 mActionDone = true;
184 mActionDoneSync.notifyAll();
Dianne Hackborn55280a92009-05-07 15:53:46 -0700185 }
186 }
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800187
Dianne Hackborn55280a92009-05-07 15:53:46 -0700188 /**
189 * Makes sure we handle the shutdown gracefully.
190 * Shuts off power regardless of radio and bluetooth state if the alloted time has passed.
191 */
192 public void run() {
193 boolean bluetoothOff;
194 boolean radioOff;
195
196 BroadcastReceiver br = new BroadcastReceiver() {
197 @Override public void onReceive(Context context, Intent intent) {
198 // We don't allow apps to cancel this, so ignore the result.
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800199 actionDone();
Dianne Hackborn55280a92009-05-07 15:53:46 -0700200 }
201 };
Kenny Rootf547d672010-09-22 10:36:48 -0700202
203 /*
204 * Write a system property in case the system_server reboots before we
205 * get to the actual hardware restart. If that happens, we'll retry at
206 * the beginning of the SystemServer startup.
207 */
208 {
209 String reason = (mReboot ? "1" : "0") + (mRebootReason != null ? mRebootReason : "");
210 SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);
211 }
212
Dianne Hackborn55280a92009-05-07 15:53:46 -0700213 Log.i(TAG, "Sending shutdown broadcast...");
214
215 // First send the high-level shut down broadcast.
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800216 mActionDone = false;
Dianne Hackborn55280a92009-05-07 15:53:46 -0700217 mContext.sendOrderedBroadcast(new Intent(Intent.ACTION_SHUTDOWN), null,
218 br, mHandler, 0, null, null);
219
Mike Lockwood098e58d2010-05-13 16:29:49 -0400220 final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800221 synchronized (mActionDoneSync) {
222 while (!mActionDone) {
Mike Lockwood098e58d2010-05-13 16:29:49 -0400223 long delay = endTime - SystemClock.elapsedRealtime();
Dianne Hackborn55280a92009-05-07 15:53:46 -0700224 if (delay <= 0) {
225 Log.w(TAG, "Shutdown broadcast timed out");
226 break;
227 }
228 try {
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800229 mActionDoneSync.wait(delay);
Dianne Hackborn55280a92009-05-07 15:53:46 -0700230 } catch (InterruptedException e) {
231 }
232 }
233 }
234
235 Log.i(TAG, "Shutting down activity manager...");
236
237 final IActivityManager am =
238 ActivityManagerNative.asInterface(ServiceManager.checkService("activity"));
239 if (am != null) {
240 try {
241 am.shutdown(MAX_BROADCAST_TIME);
242 } catch (RemoteException e) {
243 }
244 }
245
246 final ITelephony phone =
247 ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
Nick Pellybd022f42009-08-14 18:33:38 -0700248 final IBluetooth bluetooth =
249 IBluetooth.Stub.asInterface(ServiceManager.checkService(
Nick Pellyf242b7b2009-10-08 00:12:45 +0200250 BluetoothAdapter.BLUETOOTH_SERVICE));
San Mehat9f7f7ca2010-01-07 11:34:59 -0800251
252 final IMountService mount =
253 IMountService.Stub.asInterface(
254 ServiceManager.checkService("mount"));
Dianne Hackborn55280a92009-05-07 15:53:46 -0700255
256 try {
257 bluetoothOff = bluetooth == null ||
Nick Pellyde893f52009-09-08 13:15:33 -0700258 bluetooth.getBluetoothState() == BluetoothAdapter.STATE_OFF;
Dianne Hackborn55280a92009-05-07 15:53:46 -0700259 if (!bluetoothOff) {
260 Log.w(TAG, "Disabling Bluetooth...");
261 bluetooth.disable(false); // disable but don't persist new state
262 }
263 } catch (RemoteException ex) {
264 Log.e(TAG, "RemoteException during bluetooth shutdown", ex);
265 bluetoothOff = true;
266 }
267
268 try {
269 radioOff = phone == null || !phone.isRadioOn();
270 if (!radioOff) {
271 Log.w(TAG, "Turning off radio...");
272 phone.setRadio(false);
273 }
274 } catch (RemoteException ex) {
275 Log.e(TAG, "RemoteException during radio shutdown", ex);
276 radioOff = true;
277 }
278
279 Log.i(TAG, "Waiting for Bluetooth and Radio...");
280
281 // Wait a max of 32 seconds for clean shutdown
282 for (int i = 0; i < MAX_NUM_PHONE_STATE_READS; i++) {
283 if (!bluetoothOff) {
284 try {
285 bluetoothOff =
Nick Pellyde893f52009-09-08 13:15:33 -0700286 bluetooth.getBluetoothState() == BluetoothAdapter.STATE_OFF;
Dianne Hackborn55280a92009-05-07 15:53:46 -0700287 } catch (RemoteException ex) {
288 Log.e(TAG, "RemoteException during bluetooth shutdown", ex);
289 bluetoothOff = true;
290 }
291 }
292 if (!radioOff) {
293 try {
294 radioOff = !phone.isRadioOn();
295 } catch (RemoteException ex) {
296 Log.e(TAG, "RemoteException during radio shutdown", ex);
297 radioOff = true;
298 }
299 }
300 if (radioOff && bluetoothOff) {
301 Log.i(TAG, "Radio and Bluetooth shutdown complete.");
302 break;
303 }
304 SystemClock.sleep(PHONE_STATE_POLL_SLEEP_MSEC);
305 }
306
San Mehat9f7f7ca2010-01-07 11:34:59 -0800307 // Shutdown MountService to ensure media is in a safe state
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800308 IMountShutdownObserver observer = new IMountShutdownObserver.Stub() {
309 public void onShutDownComplete(int statusCode) throws RemoteException {
310 Log.w(TAG, "Result code " + statusCode + " from MountService.shutdown");
311 actionDone();
San Mehat9f7f7ca2010-01-07 11:34:59 -0800312 }
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800313 };
314
315 Log.i(TAG, "Shutting down MountService");
316 // Set initial variables and time out time.
317 mActionDone = false;
Mike Lockwood098e58d2010-05-13 16:29:49 -0400318 final long endShutTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_WAIT_TIME;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800319 synchronized (mActionDoneSync) {
320 try {
321 if (mount != null) {
322 mount.shutdown(observer);
323 } else {
324 Log.w(TAG, "MountService unavailable for shutdown");
325 }
326 } catch (Exception e) {
327 Log.e(TAG, "Exception during MountService shutdown", e);
328 }
329 while (!mActionDone) {
Mike Lockwood098e58d2010-05-13 16:29:49 -0400330 long delay = endShutTime - SystemClock.elapsedRealtime();
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800331 if (delay <= 0) {
332 Log.w(TAG, "Shutdown wait timed out");
333 break;
334 }
335 try {
336 mActionDoneSync.wait(delay);
337 } catch (InterruptedException e) {
338 }
339 }
San Mehat9f7f7ca2010-01-07 11:34:59 -0800340 }
341
Kenny Rootf547d672010-09-22 10:36:48 -0700342 rebootOrShutdown(mReboot, mRebootReason);
343 }
344
345 /**
346 * Do not call this directly. Use {@link #reboot(Context, String, boolean)}
347 * or {@link #shutdown(Context, boolean)} instead.
348 *
349 * @param reboot true to reboot or false to shutdown
350 * @param reason reason for reboot
351 */
352 public static void rebootOrShutdown(boolean reboot, String reason) {
353 if (reboot) {
354 Log.i(TAG, "Rebooting, reason: " + reason);
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800355 try {
Kenny Rootf547d672010-09-22 10:36:48 -0700356 Power.reboot(reason);
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800357 } catch (Exception e) {
358 Log.e(TAG, "Reboot failed, will attempt shutdown instead", e);
359 }
Mike Lockwooda717f642010-04-01 20:01:44 -0700360 } else if (SHUTDOWN_VIBRATE_MS > 0) {
361 // vibrate before shutting down
362 Vibrator vibrator = new Vibrator();
Brad Fitzpatrick26e9cf32010-10-19 09:33:09 -0700363 try {
364 vibrator.vibrate(SHUTDOWN_VIBRATE_MS);
365 } catch (Exception e) {
366 // Failure to vibrate shouldn't interrupt shutdown. Just log it.
367 Log.w(TAG, "Failed to vibrate during shutdown.", e);
368 }
369
Mike Lockwooda717f642010-04-01 20:01:44 -0700370 // vibrator is asynchronous so we need to wait to avoid shutting down too soon.
371 try {
372 Thread.sleep(SHUTDOWN_VIBRATE_MS);
Brad Fitzpatricke3316442010-10-14 19:40:56 -0700373 } catch (InterruptedException unused) {
Mike Lockwooda717f642010-04-01 20:01:44 -0700374 }
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800375 }
376
377 // Shutdown power
Dianne Hackborn55280a92009-05-07 15:53:46 -0700378 Log.i(TAG, "Performing low-level shutdown...");
379 Power.shutdown();
380 }
381}