blob: 3675d41e3a269bcdee0cd35544fcee751fabe50b [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
Jeff Brown7304c342012-05-11 18:42:42 -070018package com.android.server.pm;
Dianne Hackborn55280a92009-05-07 15:53:46 -070019
20import android.app.ActivityManagerNative;
Joe Onoratod208e702010-10-08 16:22:43 -040021import android.app.AlertDialog;
22import android.app.Dialog;
Dianne Hackborn55280a92009-05-07 15:53:46 -070023import android.app.IActivityManager;
24import android.app.ProgressDialog;
Nick Pellybd022f42009-08-14 18:33:38 -070025import android.bluetooth.BluetoothAdapter;
26import android.bluetooth.IBluetooth;
Sunil Jogi3bba8d02012-04-10 13:12:26 -070027import android.nfc.NfcAdapter;
28import android.nfc.INfcAdapter;
Dianne Hackborn55280a92009-05-07 15:53:46 -070029import android.content.BroadcastReceiver;
30import android.content.Context;
31import android.content.DialogInterface;
32import android.content.Intent;
Joe Onoratod208e702010-10-08 16:22:43 -040033import android.content.IntentFilter;
Dianne Hackborn55280a92009-05-07 15:53:46 -070034import android.os.Handler;
Dianne Hackbornf99ae762010-03-08 12:43:51 -080035import android.os.PowerManager;
Dianne Hackborn55280a92009-05-07 15:53:46 -070036import android.os.RemoteException;
Dianne Hackborn55280a92009-05-07 15:53:46 -070037import android.os.ServiceManager;
38import android.os.SystemClock;
Kenny Rootf547d672010-09-22 10:36:48 -070039import android.os.SystemProperties;
Mike Lockwooda717f642010-04-01 20:01:44 -070040import android.os.Vibrator;
Jeff Brownc2346132012-04-13 01:55:38 -070041import android.os.SystemVibrator;
San Mehatb1043402010-02-05 08:26:50 -080042import android.os.storage.IMountService;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -080043import android.os.storage.IMountShutdownObserver;
Dianne Hackborn568cae52009-10-07 16:13:39 -070044
Dianne Hackborn55280a92009-05-07 15:53:46 -070045import com.android.internal.telephony.ITelephony;
Jeff Brown7304c342012-05-11 18:42:42 -070046import com.android.server.PowerManagerService;
47
Dianne Hackborn55280a92009-05-07 15:53:46 -070048import android.util.Log;
49import android.view.WindowManager;
50
51public final class ShutdownThread extends Thread {
52 // constants
53 private static final String TAG = "ShutdownThread";
Dianne Hackborn55280a92009-05-07 15:53:46 -070054 private static final int PHONE_STATE_POLL_SLEEP_MSEC = 500;
55 // maximum time we wait for the shutdown broadcast before going on.
56 private static final int MAX_BROADCAST_TIME = 10*1000;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -080057 private static final int MAX_SHUTDOWN_WAIT_TIME = 20*1000;
Jeff Brownb8203712012-05-31 17:39:13 -070058 private static final int MAX_RADIO_WAIT_TIME = 12*1000;
Mike Lockwooda717f642010-04-01 20:01:44 -070059
60 // length of vibration before shutting down
61 private static final int SHUTDOWN_VIBRATE_MS = 500;
Dianne Hackborn55280a92009-05-07 15:53:46 -070062
63 // state tracking
64 private static Object sIsStartedGuard = new Object();
65 private static boolean sIsStarted = false;
66
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -080067 private static boolean mReboot;
Dianne Hackborn19caadc2012-04-20 17:49:10 -070068 private static boolean mRebootSafeMode;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -080069 private static String mRebootReason;
70
Kenny Rootf547d672010-09-22 10:36:48 -070071 // Provides shutdown assurance in case the system_server is killed
72 public static final String SHUTDOWN_ACTION_PROPERTY = "sys.shutdown.requested";
73
Dianne Hackborn19caadc2012-04-20 17:49:10 -070074 // Indicates whether we are rebooting into safe mode
75 public static final String REBOOT_SAFEMODE_PROPERTY = "persist.sys.safemode";
76
Dianne Hackborn55280a92009-05-07 15:53:46 -070077 // static instance of this thread
78 private static final ShutdownThread sInstance = new ShutdownThread();
79
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -080080 private final Object mActionDoneSync = new Object();
81 private boolean mActionDone;
Dianne Hackborn55280a92009-05-07 15:53:46 -070082 private Context mContext;
Dianne Hackbornf99ae762010-03-08 12:43:51 -080083 private PowerManager mPowerManager;
Mattias Larssoncd4e4272010-09-28 14:34:15 +020084 private PowerManager.WakeLock mCpuWakeLock;
85 private PowerManager.WakeLock mScreenWakeLock;
Dianne Hackborn55280a92009-05-07 15:53:46 -070086 private Handler mHandler;
Jean-Baptiste Queru6e85ad72012-06-11 12:20:36 -070087
88 private static AlertDialog sConfirmDialog;
Dianne Hackborn55280a92009-05-07 15:53:46 -070089
90 private ShutdownThread() {
91 }
92
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -080093 /**
Dianne Hackborn55280a92009-05-07 15:53:46 -070094 * Request a clean shutdown, waiting for subsystems to clean up their
95 * state etc. Must be called from a Looper thread in which its UI
96 * is shown.
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -080097 *
Dianne Hackborn55280a92009-05-07 15:53:46 -070098 * @param context Context used to display the shutdown progress dialog.
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -080099 * @param confirm true if user confirmation is needed before shutting down.
Dianne Hackborn55280a92009-05-07 15:53:46 -0700100 */
101 public static void shutdown(final Context context, boolean confirm) {
Dianne Hackborn19caadc2012-04-20 17:49:10 -0700102 mReboot = false;
103 mRebootSafeMode = false;
104 shutdownInner(context, confirm);
105 }
106
107 static void shutdownInner(final Context context, boolean confirm) {
Dianne Hackborn55280a92009-05-07 15:53:46 -0700108 // ensure that only one thread is trying to power down.
109 // any additional calls are just returned
Mike Lockwoodd67b2362010-07-26 07:18:21 -0400110 synchronized (sIsStartedGuard) {
Dianne Hackborn55280a92009-05-07 15:53:46 -0700111 if (sIsStarted) {
112 Log.d(TAG, "Request to shutdown already running, returning.");
113 return;
114 }
115 }
116
Joe Onoratod208e702010-10-08 16:22:43 -0400117 final int longPressBehavior = context.getResources().getInteger(
118 com.android.internal.R.integer.config_longPressOnPowerBehavior);
Dianne Hackborn19caadc2012-04-20 17:49:10 -0700119 final int resourceId = mRebootSafeMode
120 ? com.android.internal.R.string.reboot_safemode_confirm
121 : (longPressBehavior == 2
122 ? com.android.internal.R.string.shutdown_confirm_question
123 : com.android.internal.R.string.shutdown_confirm);
Joe Onoratod208e702010-10-08 16:22:43 -0400124
125 Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior);
Dianne Hackborn55280a92009-05-07 15:53:46 -0700126
127 if (confirm) {
Joe Onoratod208e702010-10-08 16:22:43 -0400128 final CloseDialogReceiver closer = new CloseDialogReceiver(context);
Jean-Baptiste Queru6e85ad72012-06-11 12:20:36 -0700129 if (sConfirmDialog != null) {
130 sConfirmDialog.dismiss();
131 }
132 sConfirmDialog = new AlertDialog.Builder(context)
Dianne Hackborn19caadc2012-04-20 17:49:10 -0700133 .setTitle(mRebootSafeMode
134 ? com.android.internal.R.string.reboot_safemode_title
135 : com.android.internal.R.string.power_off)
Joe Onoratod208e702010-10-08 16:22:43 -0400136 .setMessage(resourceId)
Dianne Hackborn55280a92009-05-07 15:53:46 -0700137 .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {
138 public void onClick(DialogInterface dialog, int which) {
139 beginShutdownSequence(context);
140 }
141 })
142 .setNegativeButton(com.android.internal.R.string.no, null)
143 .create();
Jean-Baptiste Queru6e85ad72012-06-11 12:20:36 -0700144 closer.dialog = sConfirmDialog;
145 sConfirmDialog.setOnDismissListener(closer);
146 sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
147 sConfirmDialog.show();
Dianne Hackborn55280a92009-05-07 15:53:46 -0700148 } else {
149 beginShutdownSequence(context);
150 }
151 }
152
Joe Onoratod208e702010-10-08 16:22:43 -0400153 private static class CloseDialogReceiver extends BroadcastReceiver
154 implements DialogInterface.OnDismissListener {
155 private Context mContext;
156 public Dialog dialog;
157
158 CloseDialogReceiver(Context context) {
159 mContext = context;
160 IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
161 context.registerReceiver(this, filter);
162 }
163
164 @Override
165 public void onReceive(Context context, Intent intent) {
166 dialog.cancel();
167 }
168
169 public void onDismiss(DialogInterface unused) {
170 mContext.unregisterReceiver(this);
171 }
172 }
173
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800174 /**
175 * Request a clean shutdown, waiting for subsystems to clean up their
176 * state etc. Must be called from a Looper thread in which its UI
177 * is shown.
178 *
179 * @param context Context used to display the shutdown progress dialog.
180 * @param reason code to pass to the kernel (e.g. "recovery"), or null.
181 * @param confirm true if user confirmation is needed before shutting down.
182 */
183 public static void reboot(final Context context, String reason, boolean confirm) {
184 mReboot = true;
Dianne Hackborn19caadc2012-04-20 17:49:10 -0700185 mRebootSafeMode = false;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800186 mRebootReason = reason;
Dianne Hackborn19caadc2012-04-20 17:49:10 -0700187 shutdownInner(context, confirm);
188 }
189
190 /**
191 * Request a reboot into safe mode. Must be called from a Looper thread in which its UI
192 * is shown.
193 *
194 * @param context Context used to display the shutdown progress dialog.
195 * @param confirm true if user confirmation is needed before shutting down.
196 */
197 public static void rebootSafeMode(final Context context, boolean confirm) {
198 mReboot = true;
199 mRebootSafeMode = true;
200 mRebootReason = null;
201 shutdownInner(context, confirm);
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800202 }
203
Dianne Hackborn55280a92009-05-07 15:53:46 -0700204 private static void beginShutdownSequence(Context context) {
205 synchronized (sIsStartedGuard) {
Mike Lockwoodd67b2362010-07-26 07:18:21 -0400206 if (sIsStarted) {
Mathias Jeppsson8534a8e2010-08-17 13:33:09 +0200207 Log.d(TAG, "Shutdown sequence already running, returning.");
Mike Lockwoodd67b2362010-07-26 07:18:21 -0400208 return;
209 }
Dianne Hackborn55280a92009-05-07 15:53:46 -0700210 sIsStarted = true;
211 }
212
213 // throw up an indeterminate system dialog to indicate radio is
214 // shutting down.
215 ProgressDialog pd = new ProgressDialog(context);
216 pd.setTitle(context.getText(com.android.internal.R.string.power_off));
217 pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
218 pd.setIndeterminate(true);
219 pd.setCancelable(false);
220 pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
Dianne Hackborn55280a92009-05-07 15:53:46 -0700221
222 pd.show();
223
Dianne Hackborn55280a92009-05-07 15:53:46 -0700224 sInstance.mContext = context;
Dianne Hackbornf99ae762010-03-08 12:43:51 -0800225 sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
Mattias Larssoncd4e4272010-09-28 14:34:15 +0200226
227 // make sure we never fall asleep again
228 sInstance.mCpuWakeLock = null;
229 try {
230 sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock(
231 PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu");
232 sInstance.mCpuWakeLock.setReferenceCounted(false);
233 sInstance.mCpuWakeLock.acquire();
234 } catch (SecurityException e) {
235 Log.w(TAG, "No permission to acquire wake lock", e);
236 sInstance.mCpuWakeLock = null;
237 }
238
239 // also make sure the screen stays on for better user experience
240 sInstance.mScreenWakeLock = null;
Dianne Hackbornf99ae762010-03-08 12:43:51 -0800241 if (sInstance.mPowerManager.isScreenOn()) {
242 try {
Mattias Larssoncd4e4272010-09-28 14:34:15 +0200243 sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock(
244 PowerManager.FULL_WAKE_LOCK, TAG + "-screen");
245 sInstance.mScreenWakeLock.setReferenceCounted(false);
246 sInstance.mScreenWakeLock.acquire();
Dianne Hackbornf99ae762010-03-08 12:43:51 -0800247 } catch (SecurityException e) {
248 Log.w(TAG, "No permission to acquire wake lock", e);
Mattias Larssoncd4e4272010-09-28 14:34:15 +0200249 sInstance.mScreenWakeLock = null;
Dianne Hackbornf99ae762010-03-08 12:43:51 -0800250 }
251 }
Mattias Larssoncd4e4272010-09-28 14:34:15 +0200252
253 // start the thread that initiates shutdown
Dianne Hackborn55280a92009-05-07 15:53:46 -0700254 sInstance.mHandler = new Handler() {
255 };
256 sInstance.start();
257 }
258
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800259 void actionDone() {
260 synchronized (mActionDoneSync) {
261 mActionDone = true;
262 mActionDoneSync.notifyAll();
Dianne Hackborn55280a92009-05-07 15:53:46 -0700263 }
264 }
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800265
Dianne Hackborn55280a92009-05-07 15:53:46 -0700266 /**
267 * Makes sure we handle the shutdown gracefully.
268 * Shuts off power regardless of radio and bluetooth state if the alloted time has passed.
269 */
270 public void run() {
Dianne Hackborn55280a92009-05-07 15:53:46 -0700271 BroadcastReceiver br = new BroadcastReceiver() {
272 @Override public void onReceive(Context context, Intent intent) {
273 // We don't allow apps to cancel this, so ignore the result.
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800274 actionDone();
Dianne Hackborn55280a92009-05-07 15:53:46 -0700275 }
276 };
Kenny Rootf547d672010-09-22 10:36:48 -0700277
278 /*
279 * Write a system property in case the system_server reboots before we
280 * get to the actual hardware restart. If that happens, we'll retry at
281 * the beginning of the SystemServer startup.
282 */
283 {
284 String reason = (mReboot ? "1" : "0") + (mRebootReason != null ? mRebootReason : "");
285 SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);
286 }
287
Dianne Hackborn19caadc2012-04-20 17:49:10 -0700288 /*
289 * If we are rebooting into safe mode, write a system property
290 * indicating so.
291 */
292 if (mRebootSafeMode) {
293 SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");
294 }
295
Dianne Hackborn55280a92009-05-07 15:53:46 -0700296 Log.i(TAG, "Sending shutdown broadcast...");
297
298 // First send the high-level shut down broadcast.
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800299 mActionDone = false;
Dianne Hackborn55280a92009-05-07 15:53:46 -0700300 mContext.sendOrderedBroadcast(new Intent(Intent.ACTION_SHUTDOWN), null,
301 br, mHandler, 0, null, null);
302
Mike Lockwood098e58d2010-05-13 16:29:49 -0400303 final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800304 synchronized (mActionDoneSync) {
305 while (!mActionDone) {
Mike Lockwood098e58d2010-05-13 16:29:49 -0400306 long delay = endTime - SystemClock.elapsedRealtime();
Dianne Hackborn55280a92009-05-07 15:53:46 -0700307 if (delay <= 0) {
308 Log.w(TAG, "Shutdown broadcast timed out");
309 break;
310 }
311 try {
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800312 mActionDoneSync.wait(delay);
Dianne Hackborn55280a92009-05-07 15:53:46 -0700313 } catch (InterruptedException e) {
314 }
315 }
316 }
317
318 Log.i(TAG, "Shutting down activity manager...");
319
320 final IActivityManager am =
321 ActivityManagerNative.asInterface(ServiceManager.checkService("activity"));
322 if (am != null) {
323 try {
324 am.shutdown(MAX_BROADCAST_TIME);
325 } catch (RemoteException e) {
326 }
327 }
Sunil Jogi3bba8d02012-04-10 13:12:26 -0700328
Jeff Brownb8203712012-05-31 17:39:13 -0700329 // Shutdown radios.
330 shutdownRadios(MAX_RADIO_WAIT_TIME);
Dianne Hackborn55280a92009-05-07 15:53:46 -0700331
San Mehat9f7f7ca2010-01-07 11:34:59 -0800332 // Shutdown MountService to ensure media is in a safe state
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800333 IMountShutdownObserver observer = new IMountShutdownObserver.Stub() {
334 public void onShutDownComplete(int statusCode) throws RemoteException {
335 Log.w(TAG, "Result code " + statusCode + " from MountService.shutdown");
336 actionDone();
San Mehat9f7f7ca2010-01-07 11:34:59 -0800337 }
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800338 };
339
340 Log.i(TAG, "Shutting down MountService");
Jeff Brownb8203712012-05-31 17:39:13 -0700341
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800342 // Set initial variables and time out time.
343 mActionDone = false;
Mike Lockwood098e58d2010-05-13 16:29:49 -0400344 final long endShutTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_WAIT_TIME;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800345 synchronized (mActionDoneSync) {
346 try {
Jeff Brownb8203712012-05-31 17:39:13 -0700347 final IMountService mount = IMountService.Stub.asInterface(
348 ServiceManager.checkService("mount"));
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800349 if (mount != null) {
350 mount.shutdown(observer);
351 } else {
352 Log.w(TAG, "MountService unavailable for shutdown");
353 }
354 } catch (Exception e) {
355 Log.e(TAG, "Exception during MountService shutdown", e);
356 }
357 while (!mActionDone) {
Mike Lockwood098e58d2010-05-13 16:29:49 -0400358 long delay = endShutTime - SystemClock.elapsedRealtime();
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800359 if (delay <= 0) {
360 Log.w(TAG, "Shutdown wait timed out");
361 break;
362 }
363 try {
364 mActionDoneSync.wait(delay);
365 } catch (InterruptedException e) {
366 }
367 }
San Mehat9f7f7ca2010-01-07 11:34:59 -0800368 }
369
Kenny Rootf547d672010-09-22 10:36:48 -0700370 rebootOrShutdown(mReboot, mRebootReason);
371 }
372
Jeff Brownb8203712012-05-31 17:39:13 -0700373 private void shutdownRadios(int timeout) {
374 // If a radio is wedged, disabling it may hang so we do this work in another thread,
375 // just in case.
376 final long endTime = SystemClock.elapsedRealtime() + timeout;
377 final boolean[] done = new boolean[1];
378 Thread t = new Thread() {
379 public void run() {
380 boolean nfcOff;
381 boolean bluetoothOff;
382 boolean radioOff;
383
384 final INfcAdapter nfc =
385 INfcAdapter.Stub.asInterface(ServiceManager.checkService("nfc"));
386 final ITelephony phone =
387 ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
388 final IBluetooth bluetooth =
389 IBluetooth.Stub.asInterface(ServiceManager.checkService(
390 BluetoothAdapter.BLUETOOTH_SERVICE));
391
392 try {
393 nfcOff = nfc == null ||
394 nfc.getState() == NfcAdapter.STATE_OFF;
395 if (!nfcOff) {
396 Log.w(TAG, "Turning off NFC...");
397 nfc.disable(false); // Don't persist new state
398 }
399 } catch (RemoteException ex) {
400 Log.e(TAG, "RemoteException during NFC shutdown", ex);
401 nfcOff = true;
402 }
403
404 try {
405 bluetoothOff = bluetooth == null ||
406 bluetooth.getBluetoothState() == BluetoothAdapter.STATE_OFF;
407 if (!bluetoothOff) {
408 Log.w(TAG, "Disabling Bluetooth...");
409 bluetooth.disable(false); // disable but don't persist new state
410 }
411 } catch (RemoteException ex) {
412 Log.e(TAG, "RemoteException during bluetooth shutdown", ex);
413 bluetoothOff = true;
414 }
415
416 try {
417 radioOff = phone == null || !phone.isRadioOn();
418 if (!radioOff) {
419 Log.w(TAG, "Turning off radio...");
420 phone.setRadio(false);
421 }
422 } catch (RemoteException ex) {
423 Log.e(TAG, "RemoteException during radio shutdown", ex);
424 radioOff = true;
425 }
426
427 Log.i(TAG, "Waiting for NFC, Bluetooth and Radio...");
428
429 while (SystemClock.elapsedRealtime() < endTime) {
430 if (!bluetoothOff) {
431 try {
432 bluetoothOff =
433 bluetooth.getBluetoothState() == BluetoothAdapter.STATE_OFF;
434 } catch (RemoteException ex) {
435 Log.e(TAG, "RemoteException during bluetooth shutdown", ex);
436 bluetoothOff = true;
437 }
438 if (bluetoothOff) {
439 Log.i(TAG, "Bluetooth turned off.");
440 }
441 }
442 if (!radioOff) {
443 try {
444 radioOff = !phone.isRadioOn();
445 } catch (RemoteException ex) {
446 Log.e(TAG, "RemoteException during radio shutdown", ex);
447 radioOff = true;
448 }
449 if (radioOff) {
450 Log.i(TAG, "Radio turned off.");
451 }
452 }
453 if (!nfcOff) {
454 try {
455 nfcOff = nfc.getState() == NfcAdapter.STATE_OFF;
456 } catch (RemoteException ex) {
457 Log.e(TAG, "RemoteException during NFC shutdown", ex);
458 nfcOff = true;
459 }
460 if (radioOff) {
461 Log.i(TAG, "NFC turned off.");
462 }
463 }
464
465 if (radioOff && bluetoothOff && nfcOff) {
466 Log.i(TAG, "NFC, Radio and Bluetooth shutdown complete.");
467 done[0] = true;
468 break;
469 }
470 SystemClock.sleep(PHONE_STATE_POLL_SLEEP_MSEC);
471 }
472 }
473 };
474
475 t.start();
476 try {
477 t.join(timeout);
478 } catch (InterruptedException ex) {
479 }
480 if (!done[0]) {
481 Log.w(TAG, "Timed out waiting for NFC, Radio and Bluetooth shutdown.");
482 }
483 }
484
Kenny Rootf547d672010-09-22 10:36:48 -0700485 /**
486 * Do not call this directly. Use {@link #reboot(Context, String, boolean)}
487 * or {@link #shutdown(Context, boolean)} instead.
488 *
489 * @param reboot true to reboot or false to shutdown
490 * @param reason reason for reboot
491 */
492 public static void rebootOrShutdown(boolean reboot, String reason) {
493 if (reboot) {
494 Log.i(TAG, "Rebooting, reason: " + reason);
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800495 try {
Jeff Brown7304c342012-05-11 18:42:42 -0700496 PowerManagerService.lowLevelReboot(reason);
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800497 } catch (Exception e) {
498 Log.e(TAG, "Reboot failed, will attempt shutdown instead", e);
499 }
Mike Lockwooda717f642010-04-01 20:01:44 -0700500 } else if (SHUTDOWN_VIBRATE_MS > 0) {
501 // vibrate before shutting down
Jeff Brownc2346132012-04-13 01:55:38 -0700502 Vibrator vibrator = new SystemVibrator();
Brad Fitzpatrick26e9cf32010-10-19 09:33:09 -0700503 try {
504 vibrator.vibrate(SHUTDOWN_VIBRATE_MS);
505 } catch (Exception e) {
506 // Failure to vibrate shouldn't interrupt shutdown. Just log it.
507 Log.w(TAG, "Failed to vibrate during shutdown.", e);
508 }
509
Mike Lockwooda717f642010-04-01 20:01:44 -0700510 // vibrator is asynchronous so we need to wait to avoid shutting down too soon.
511 try {
512 Thread.sleep(SHUTDOWN_VIBRATE_MS);
Brad Fitzpatricke3316442010-10-14 19:40:56 -0700513 } catch (InterruptedException unused) {
Mike Lockwooda717f642010-04-01 20:01:44 -0700514 }
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800515 }
516
517 // Shutdown power
Dianne Hackborn55280a92009-05-07 15:53:46 -0700518 Log.i(TAG, "Performing low-level shutdown...");
Jeff Brown7304c342012-05-11 18:42:42 -0700519 PowerManagerService.lowLevelShutdown();
Dianne Hackborn55280a92009-05-07 15:53:46 -0700520 }
521}