blob: 82f72f74115348e7adb3a1527bb293fc560fb3c4 [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 Brown4f8ecd82012-06-18 18:29:13 -070018package com.android.server.power;
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 -070046
Dianne Hackborn55280a92009-05-07 15:53:46 -070047import android.util.Log;
48import android.view.WindowManager;
49
50public final class ShutdownThread extends Thread {
51 // constants
52 private static final String TAG = "ShutdownThread";
Dianne Hackborn55280a92009-05-07 15:53:46 -070053 private static final int PHONE_STATE_POLL_SLEEP_MSEC = 500;
54 // maximum time we wait for the shutdown broadcast before going on.
55 private static final int MAX_BROADCAST_TIME = 10*1000;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -080056 private static final int MAX_SHUTDOWN_WAIT_TIME = 20*1000;
Jeff Brownb8203712012-05-31 17:39:13 -070057 private static final int MAX_RADIO_WAIT_TIME = 12*1000;
Mike Lockwooda717f642010-04-01 20:01:44 -070058
59 // length of vibration before shutting down
60 private static final int SHUTDOWN_VIBRATE_MS = 500;
Dianne Hackborn55280a92009-05-07 15:53:46 -070061
62 // state tracking
63 private static Object sIsStartedGuard = new Object();
64 private static boolean sIsStarted = false;
65
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -080066 private static boolean mReboot;
Dianne Hackborn19caadc2012-04-20 17:49:10 -070067 private static boolean mRebootSafeMode;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -080068 private static String mRebootReason;
69
Kenny Rootf547d672010-09-22 10:36:48 -070070 // Provides shutdown assurance in case the system_server is killed
71 public static final String SHUTDOWN_ACTION_PROPERTY = "sys.shutdown.requested";
72
Dianne Hackborn19caadc2012-04-20 17:49:10 -070073 // Indicates whether we are rebooting into safe mode
74 public static final String REBOOT_SAFEMODE_PROPERTY = "persist.sys.safemode";
75
Dianne Hackborn55280a92009-05-07 15:53:46 -070076 // static instance of this thread
77 private static final ShutdownThread sInstance = new ShutdownThread();
78
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -080079 private final Object mActionDoneSync = new Object();
80 private boolean mActionDone;
Dianne Hackborn55280a92009-05-07 15:53:46 -070081 private Context mContext;
Dianne Hackbornf99ae762010-03-08 12:43:51 -080082 private PowerManager mPowerManager;
Mattias Larssoncd4e4272010-09-28 14:34:15 +020083 private PowerManager.WakeLock mCpuWakeLock;
84 private PowerManager.WakeLock mScreenWakeLock;
Dianne Hackborn55280a92009-05-07 15:53:46 -070085 private Handler mHandler;
Jean-Baptiste Queru6e85ad72012-06-11 12:20:36 -070086
87 private static AlertDialog sConfirmDialog;
Dianne Hackborn55280a92009-05-07 15:53:46 -070088
89 private ShutdownThread() {
90 }
91
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -080092 /**
Dianne Hackborn55280a92009-05-07 15:53:46 -070093 * Request a clean shutdown, waiting for subsystems to clean up their
94 * state etc. Must be called from a Looper thread in which its UI
95 * is shown.
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -080096 *
Dianne Hackborn55280a92009-05-07 15:53:46 -070097 * @param context Context used to display the shutdown progress dialog.
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -080098 * @param confirm true if user confirmation is needed before shutting down.
Dianne Hackborn55280a92009-05-07 15:53:46 -070099 */
100 public static void shutdown(final Context context, boolean confirm) {
Dianne Hackborn19caadc2012-04-20 17:49:10 -0700101 mReboot = false;
102 mRebootSafeMode = false;
103 shutdownInner(context, confirm);
104 }
105
106 static void shutdownInner(final Context context, boolean confirm) {
Dianne Hackborn55280a92009-05-07 15:53:46 -0700107 // ensure that only one thread is trying to power down.
108 // any additional calls are just returned
Mike Lockwoodd67b2362010-07-26 07:18:21 -0400109 synchronized (sIsStartedGuard) {
Dianne Hackborn55280a92009-05-07 15:53:46 -0700110 if (sIsStarted) {
111 Log.d(TAG, "Request to shutdown already running, returning.");
112 return;
113 }
114 }
115
Joe Onoratod208e702010-10-08 16:22:43 -0400116 final int longPressBehavior = context.getResources().getInteger(
117 com.android.internal.R.integer.config_longPressOnPowerBehavior);
Dianne Hackborn19caadc2012-04-20 17:49:10 -0700118 final int resourceId = mRebootSafeMode
119 ? com.android.internal.R.string.reboot_safemode_confirm
120 : (longPressBehavior == 2
121 ? com.android.internal.R.string.shutdown_confirm_question
122 : com.android.internal.R.string.shutdown_confirm);
Joe Onoratod208e702010-10-08 16:22:43 -0400123
124 Log.d(TAG, "Notifying thread to start shutdown longPressBehavior=" + longPressBehavior);
Dianne Hackborn55280a92009-05-07 15:53:46 -0700125
126 if (confirm) {
Joe Onoratod208e702010-10-08 16:22:43 -0400127 final CloseDialogReceiver closer = new CloseDialogReceiver(context);
Jean-Baptiste Queru6e85ad72012-06-11 12:20:36 -0700128 if (sConfirmDialog != null) {
129 sConfirmDialog.dismiss();
130 }
131 sConfirmDialog = new AlertDialog.Builder(context)
Dianne Hackborn19caadc2012-04-20 17:49:10 -0700132 .setTitle(mRebootSafeMode
133 ? com.android.internal.R.string.reboot_safemode_title
134 : com.android.internal.R.string.power_off)
Joe Onoratod208e702010-10-08 16:22:43 -0400135 .setMessage(resourceId)
Dianne Hackborn55280a92009-05-07 15:53:46 -0700136 .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {
137 public void onClick(DialogInterface dialog, int which) {
138 beginShutdownSequence(context);
139 }
140 })
141 .setNegativeButton(com.android.internal.R.string.no, null)
142 .create();
Jean-Baptiste Queru6e85ad72012-06-11 12:20:36 -0700143 closer.dialog = sConfirmDialog;
144 sConfirmDialog.setOnDismissListener(closer);
145 sConfirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
146 sConfirmDialog.show();
Dianne Hackborn55280a92009-05-07 15:53:46 -0700147 } else {
148 beginShutdownSequence(context);
149 }
150 }
151
Joe Onoratod208e702010-10-08 16:22:43 -0400152 private static class CloseDialogReceiver extends BroadcastReceiver
153 implements DialogInterface.OnDismissListener {
154 private Context mContext;
155 public Dialog dialog;
156
157 CloseDialogReceiver(Context context) {
158 mContext = context;
159 IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
160 context.registerReceiver(this, filter);
161 }
162
163 @Override
164 public void onReceive(Context context, Intent intent) {
165 dialog.cancel();
166 }
167
168 public void onDismiss(DialogInterface unused) {
169 mContext.unregisterReceiver(this);
170 }
171 }
172
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800173 /**
174 * Request a clean shutdown, waiting for subsystems to clean up their
175 * state etc. Must be called from a Looper thread in which its UI
176 * is shown.
177 *
178 * @param context Context used to display the shutdown progress dialog.
179 * @param reason code to pass to the kernel (e.g. "recovery"), or null.
180 * @param confirm true if user confirmation is needed before shutting down.
181 */
182 public static void reboot(final Context context, String reason, boolean confirm) {
183 mReboot = true;
Dianne Hackborn19caadc2012-04-20 17:49:10 -0700184 mRebootSafeMode = false;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800185 mRebootReason = reason;
Dianne Hackborn19caadc2012-04-20 17:49:10 -0700186 shutdownInner(context, confirm);
187 }
188
189 /**
190 * Request a reboot into safe mode. Must be called from a Looper thread in which its UI
191 * is shown.
192 *
193 * @param context Context used to display the shutdown progress dialog.
194 * @param confirm true if user confirmation is needed before shutting down.
195 */
196 public static void rebootSafeMode(final Context context, boolean confirm) {
197 mReboot = true;
198 mRebootSafeMode = true;
199 mRebootReason = null;
200 shutdownInner(context, confirm);
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800201 }
202
Dianne Hackborn55280a92009-05-07 15:53:46 -0700203 private static void beginShutdownSequence(Context context) {
204 synchronized (sIsStartedGuard) {
Mike Lockwoodd67b2362010-07-26 07:18:21 -0400205 if (sIsStarted) {
Mathias Jeppsson8534a8e2010-08-17 13:33:09 +0200206 Log.d(TAG, "Shutdown sequence already running, returning.");
Mike Lockwoodd67b2362010-07-26 07:18:21 -0400207 return;
208 }
Dianne Hackborn55280a92009-05-07 15:53:46 -0700209 sIsStarted = true;
210 }
211
212 // throw up an indeterminate system dialog to indicate radio is
213 // shutting down.
214 ProgressDialog pd = new ProgressDialog(context);
215 pd.setTitle(context.getText(com.android.internal.R.string.power_off));
216 pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
217 pd.setIndeterminate(true);
218 pd.setCancelable(false);
219 pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
Dianne Hackborn55280a92009-05-07 15:53:46 -0700220
221 pd.show();
222
Dianne Hackborn55280a92009-05-07 15:53:46 -0700223 sInstance.mContext = context;
Dianne Hackbornf99ae762010-03-08 12:43:51 -0800224 sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
Mattias Larssoncd4e4272010-09-28 14:34:15 +0200225
226 // make sure we never fall asleep again
227 sInstance.mCpuWakeLock = null;
228 try {
229 sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock(
230 PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu");
231 sInstance.mCpuWakeLock.setReferenceCounted(false);
232 sInstance.mCpuWakeLock.acquire();
233 } catch (SecurityException e) {
234 Log.w(TAG, "No permission to acquire wake lock", e);
235 sInstance.mCpuWakeLock = null;
236 }
237
238 // also make sure the screen stays on for better user experience
239 sInstance.mScreenWakeLock = null;
Dianne Hackbornf99ae762010-03-08 12:43:51 -0800240 if (sInstance.mPowerManager.isScreenOn()) {
241 try {
Mattias Larssoncd4e4272010-09-28 14:34:15 +0200242 sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock(
243 PowerManager.FULL_WAKE_LOCK, TAG + "-screen");
244 sInstance.mScreenWakeLock.setReferenceCounted(false);
245 sInstance.mScreenWakeLock.acquire();
Dianne Hackbornf99ae762010-03-08 12:43:51 -0800246 } catch (SecurityException e) {
247 Log.w(TAG, "No permission to acquire wake lock", e);
Mattias Larssoncd4e4272010-09-28 14:34:15 +0200248 sInstance.mScreenWakeLock = null;
Dianne Hackbornf99ae762010-03-08 12:43:51 -0800249 }
250 }
Mattias Larssoncd4e4272010-09-28 14:34:15 +0200251
252 // start the thread that initiates shutdown
Dianne Hackborn55280a92009-05-07 15:53:46 -0700253 sInstance.mHandler = new Handler() {
254 };
255 sInstance.start();
256 }
257
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800258 void actionDone() {
259 synchronized (mActionDoneSync) {
260 mActionDone = true;
261 mActionDoneSync.notifyAll();
Dianne Hackborn55280a92009-05-07 15:53:46 -0700262 }
263 }
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800264
Dianne Hackborn55280a92009-05-07 15:53:46 -0700265 /**
266 * Makes sure we handle the shutdown gracefully.
267 * Shuts off power regardless of radio and bluetooth state if the alloted time has passed.
268 */
269 public void run() {
Dianne Hackborn55280a92009-05-07 15:53:46 -0700270 BroadcastReceiver br = new BroadcastReceiver() {
271 @Override public void onReceive(Context context, Intent intent) {
272 // We don't allow apps to cancel this, so ignore the result.
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800273 actionDone();
Dianne Hackborn55280a92009-05-07 15:53:46 -0700274 }
275 };
Kenny Rootf547d672010-09-22 10:36:48 -0700276
277 /*
278 * Write a system property in case the system_server reboots before we
279 * get to the actual hardware restart. If that happens, we'll retry at
280 * the beginning of the SystemServer startup.
281 */
282 {
283 String reason = (mReboot ? "1" : "0") + (mRebootReason != null ? mRebootReason : "");
284 SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);
285 }
286
Dianne Hackborn19caadc2012-04-20 17:49:10 -0700287 /*
288 * If we are rebooting into safe mode, write a system property
289 * indicating so.
290 */
291 if (mRebootSafeMode) {
292 SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");
293 }
294
Dianne Hackborn55280a92009-05-07 15:53:46 -0700295 Log.i(TAG, "Sending shutdown broadcast...");
296
297 // First send the high-level shut down broadcast.
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800298 mActionDone = false;
Dianne Hackborn55280a92009-05-07 15:53:46 -0700299 mContext.sendOrderedBroadcast(new Intent(Intent.ACTION_SHUTDOWN), null,
300 br, mHandler, 0, null, null);
301
Mike Lockwood098e58d2010-05-13 16:29:49 -0400302 final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800303 synchronized (mActionDoneSync) {
304 while (!mActionDone) {
Mike Lockwood098e58d2010-05-13 16:29:49 -0400305 long delay = endTime - SystemClock.elapsedRealtime();
Dianne Hackborn55280a92009-05-07 15:53:46 -0700306 if (delay <= 0) {
307 Log.w(TAG, "Shutdown broadcast timed out");
308 break;
309 }
310 try {
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800311 mActionDoneSync.wait(delay);
Dianne Hackborn55280a92009-05-07 15:53:46 -0700312 } catch (InterruptedException e) {
313 }
314 }
315 }
316
317 Log.i(TAG, "Shutting down activity manager...");
318
319 final IActivityManager am =
320 ActivityManagerNative.asInterface(ServiceManager.checkService("activity"));
321 if (am != null) {
322 try {
323 am.shutdown(MAX_BROADCAST_TIME);
324 } catch (RemoteException e) {
325 }
326 }
Jaikumar Ganesh1abb1cb2012-01-25 16:14:50 -0800327
328 final ITelephony phone =
329 ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
330 final IBluetooth bluetooth =
331 IBluetooth.Stub.asInterface(ServiceManager.checkService(
332 BluetoothAdapter.BLUETOOTH_SERVICE));
Sunil Jogi3bba8d02012-04-10 13:12:26 -0700333
Jaikumar Ganesh1abb1cb2012-01-25 16:14:50 -0800334 final IMountService mount =
335 IMountService.Stub.asInterface(
336 ServiceManager.checkService("mount"));
337
338 try {
339 bluetoothOff = bluetooth == null ||
340 bluetooth.getState() == BluetoothAdapter.STATE_OFF;
341 if (!bluetoothOff) {
342 Log.w(TAG, "Disabling Bluetooth...");
343 //TODO(BT)
344 bluetooth.disable(); // disable but don't persist new state
345 }
346 } catch (RemoteException ex) {
347 Log.e(TAG, "RemoteException during bluetooth shutdown", ex);
348 bluetoothOff = true;
349 }
350
351 try {
352 radioOff = phone == null || !phone.isRadioOn();
353 if (!radioOff) {
354 Log.w(TAG, "Turning off radio...");
355 phone.setRadio(false);
356 }
357 } catch (RemoteException ex) {
358 Log.e(TAG, "RemoteException during radio shutdown", ex);
359 radioOff = true;
360 }
361
362 Log.i(TAG, "Waiting for Bluetooth and Radio...");
363
364 // Wait a max of 32 seconds for clean shutdown
365 for (int i = 0; i < MAX_NUM_PHONE_STATE_READS; i++) {
366 if (!bluetoothOff) {
367 try {
368 bluetoothOff =
369 bluetooth.getState() == BluetoothAdapter.STATE_OFF;
370 } catch (RemoteException ex) {
371 Log.e(TAG, "RemoteException during bluetooth shutdown", ex);
372 bluetoothOff = true;
373 }
374 }
375 if (!radioOff) {
376 try {
377 radioOff = !phone.isRadioOn();
378 } catch (RemoteException ex) {
379 Log.e(TAG, "RemoteException during radio shutdown", ex);
380 radioOff = true;
381 }
382 }
383 if (radioOff && bluetoothOff) {
384 Log.i(TAG, "Radio and Bluetooth shutdown complete.");
385 break;
386 }
387 SystemClock.sleep(PHONE_STATE_POLL_SLEEP_MSEC);
388 }
Dianne Hackborn55280a92009-05-07 15:53:46 -0700389
San Mehat9f7f7ca2010-01-07 11:34:59 -0800390 // Shutdown MountService to ensure media is in a safe state
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800391 IMountShutdownObserver observer = new IMountShutdownObserver.Stub() {
392 public void onShutDownComplete(int statusCode) throws RemoteException {
393 Log.w(TAG, "Result code " + statusCode + " from MountService.shutdown");
394 actionDone();
San Mehat9f7f7ca2010-01-07 11:34:59 -0800395 }
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800396 };
397
398 Log.i(TAG, "Shutting down MountService");
Jeff Brownb8203712012-05-31 17:39:13 -0700399
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800400 // Set initial variables and time out time.
401 mActionDone = false;
Mike Lockwood098e58d2010-05-13 16:29:49 -0400402 final long endShutTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_WAIT_TIME;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800403 synchronized (mActionDoneSync) {
404 try {
Jeff Brownb8203712012-05-31 17:39:13 -0700405 final IMountService mount = IMountService.Stub.asInterface(
406 ServiceManager.checkService("mount"));
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800407 if (mount != null) {
408 mount.shutdown(observer);
409 } else {
410 Log.w(TAG, "MountService unavailable for shutdown");
411 }
412 } catch (Exception e) {
413 Log.e(TAG, "Exception during MountService shutdown", e);
414 }
415 while (!mActionDone) {
Mike Lockwood098e58d2010-05-13 16:29:49 -0400416 long delay = endShutTime - SystemClock.elapsedRealtime();
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800417 if (delay <= 0) {
418 Log.w(TAG, "Shutdown wait timed out");
419 break;
420 }
421 try {
422 mActionDoneSync.wait(delay);
423 } catch (InterruptedException e) {
424 }
425 }
San Mehat9f7f7ca2010-01-07 11:34:59 -0800426 }
427
Kenny Rootf547d672010-09-22 10:36:48 -0700428 rebootOrShutdown(mReboot, mRebootReason);
429 }
430
Jeff Brownb8203712012-05-31 17:39:13 -0700431 private void shutdownRadios(int timeout) {
432 // If a radio is wedged, disabling it may hang so we do this work in another thread,
433 // just in case.
434 final long endTime = SystemClock.elapsedRealtime() + timeout;
435 final boolean[] done = new boolean[1];
436 Thread t = new Thread() {
437 public void run() {
438 boolean nfcOff;
439 boolean bluetoothOff;
440 boolean radioOff;
441
442 final INfcAdapter nfc =
443 INfcAdapter.Stub.asInterface(ServiceManager.checkService("nfc"));
444 final ITelephony phone =
445 ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
446 final IBluetooth bluetooth =
447 IBluetooth.Stub.asInterface(ServiceManager.checkService(
448 BluetoothAdapter.BLUETOOTH_SERVICE));
449
450 try {
451 nfcOff = nfc == null ||
452 nfc.getState() == NfcAdapter.STATE_OFF;
453 if (!nfcOff) {
454 Log.w(TAG, "Turning off NFC...");
455 nfc.disable(false); // Don't persist new state
456 }
457 } catch (RemoteException ex) {
458 Log.e(TAG, "RemoteException during NFC shutdown", ex);
459 nfcOff = true;
460 }
461
462 try {
463 bluetoothOff = bluetooth == null ||
464 bluetooth.getBluetoothState() == BluetoothAdapter.STATE_OFF;
465 if (!bluetoothOff) {
466 Log.w(TAG, "Disabling Bluetooth...");
467 bluetooth.disable(false); // disable but don't persist new state
468 }
469 } catch (RemoteException ex) {
470 Log.e(TAG, "RemoteException during bluetooth shutdown", ex);
471 bluetoothOff = true;
472 }
473
474 try {
475 radioOff = phone == null || !phone.isRadioOn();
476 if (!radioOff) {
477 Log.w(TAG, "Turning off radio...");
478 phone.setRadio(false);
479 }
480 } catch (RemoteException ex) {
481 Log.e(TAG, "RemoteException during radio shutdown", ex);
482 radioOff = true;
483 }
484
485 Log.i(TAG, "Waiting for NFC, Bluetooth and Radio...");
486
487 while (SystemClock.elapsedRealtime() < endTime) {
488 if (!bluetoothOff) {
489 try {
490 bluetoothOff =
491 bluetooth.getBluetoothState() == BluetoothAdapter.STATE_OFF;
492 } catch (RemoteException ex) {
493 Log.e(TAG, "RemoteException during bluetooth shutdown", ex);
494 bluetoothOff = true;
495 }
496 if (bluetoothOff) {
497 Log.i(TAG, "Bluetooth turned off.");
498 }
499 }
500 if (!radioOff) {
501 try {
502 radioOff = !phone.isRadioOn();
503 } catch (RemoteException ex) {
504 Log.e(TAG, "RemoteException during radio shutdown", ex);
505 radioOff = true;
506 }
507 if (radioOff) {
508 Log.i(TAG, "Radio turned off.");
509 }
510 }
511 if (!nfcOff) {
512 try {
513 nfcOff = nfc.getState() == NfcAdapter.STATE_OFF;
514 } catch (RemoteException ex) {
515 Log.e(TAG, "RemoteException during NFC shutdown", ex);
516 nfcOff = true;
517 }
518 if (radioOff) {
519 Log.i(TAG, "NFC turned off.");
520 }
521 }
522
523 if (radioOff && bluetoothOff && nfcOff) {
524 Log.i(TAG, "NFC, Radio and Bluetooth shutdown complete.");
525 done[0] = true;
526 break;
527 }
528 SystemClock.sleep(PHONE_STATE_POLL_SLEEP_MSEC);
529 }
530 }
531 };
532
533 t.start();
534 try {
535 t.join(timeout);
536 } catch (InterruptedException ex) {
537 }
538 if (!done[0]) {
539 Log.w(TAG, "Timed out waiting for NFC, Radio and Bluetooth shutdown.");
540 }
541 }
542
Kenny Rootf547d672010-09-22 10:36:48 -0700543 /**
544 * Do not call this directly. Use {@link #reboot(Context, String, boolean)}
545 * or {@link #shutdown(Context, boolean)} instead.
546 *
547 * @param reboot true to reboot or false to shutdown
548 * @param reason reason for reboot
549 */
550 public static void rebootOrShutdown(boolean reboot, String reason) {
551 if (reboot) {
552 Log.i(TAG, "Rebooting, reason: " + reason);
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800553 try {
Jeff Brown7304c342012-05-11 18:42:42 -0700554 PowerManagerService.lowLevelReboot(reason);
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800555 } catch (Exception e) {
556 Log.e(TAG, "Reboot failed, will attempt shutdown instead", e);
557 }
Mike Lockwooda717f642010-04-01 20:01:44 -0700558 } else if (SHUTDOWN_VIBRATE_MS > 0) {
559 // vibrate before shutting down
Jeff Brownc2346132012-04-13 01:55:38 -0700560 Vibrator vibrator = new SystemVibrator();
Brad Fitzpatrick26e9cf32010-10-19 09:33:09 -0700561 try {
562 vibrator.vibrate(SHUTDOWN_VIBRATE_MS);
563 } catch (Exception e) {
564 // Failure to vibrate shouldn't interrupt shutdown. Just log it.
565 Log.w(TAG, "Failed to vibrate during shutdown.", e);
566 }
567
Mike Lockwooda717f642010-04-01 20:01:44 -0700568 // vibrator is asynchronous so we need to wait to avoid shutting down too soon.
569 try {
570 Thread.sleep(SHUTDOWN_VIBRATE_MS);
Brad Fitzpatricke3316442010-10-14 19:40:56 -0700571 } catch (InterruptedException unused) {
Mike Lockwooda717f642010-04-01 20:01:44 -0700572 }
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800573 }
574
575 // Shutdown power
Dianne Hackborn55280a92009-05-07 15:53:46 -0700576 Log.i(TAG, "Performing low-level shutdown...");
Jeff Brown7304c342012-05-11 18:42:42 -0700577 PowerManagerService.lowLevelShutdown();
Dianne Hackborn55280a92009-05-07 15:53:46 -0700578 }
579}