blob: 37898a1da3d7c6b86a6d9cdfda0a72dba47efe0c [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;
Dianne Hackbornf99ae762010-03-08 12:43:51 -080031import android.os.PowerManager;
Dianne Hackborn55280a92009-05-07 15:53:46 -070032import android.os.RemoteException;
33import android.os.Power;
34import android.os.ServiceManager;
35import android.os.SystemClock;
San Mehatb1043402010-02-05 08:26:50 -080036import android.os.storage.IMountService;
Dianne Hackborn568cae52009-10-07 16:13:39 -070037
Dianne Hackborn55280a92009-05-07 15:53:46 -070038import com.android.internal.telephony.ITelephony;
39import android.util.Log;
40import android.view.WindowManager;
41
42public final class ShutdownThread extends Thread {
43 // constants
44 private static final String TAG = "ShutdownThread";
45 private static final int MAX_NUM_PHONE_STATE_READS = 16;
46 private static final int PHONE_STATE_POLL_SLEEP_MSEC = 500;
47 // maximum time we wait for the shutdown broadcast before going on.
48 private static final int MAX_BROADCAST_TIME = 10*1000;
49
50 // state tracking
51 private static Object sIsStartedGuard = new Object();
52 private static boolean sIsStarted = false;
53
54 // static instance of this thread
55 private static final ShutdownThread sInstance = new ShutdownThread();
56
57 private final Object mBroadcastDoneSync = new Object();
58 private boolean mBroadcastDone;
59 private Context mContext;
Dianne Hackbornf99ae762010-03-08 12:43:51 -080060 private PowerManager mPowerManager;
61 private PowerManager.WakeLock mWakeLock;
Dianne Hackborn55280a92009-05-07 15:53:46 -070062 private Handler mHandler;
63
64 private ShutdownThread() {
65 }
66
67 /**
68 * Request a clean shutdown, waiting for subsystems to clean up their
69 * state etc. Must be called from a Looper thread in which its UI
70 * is shown.
71 *
72 * @param context Context used to display the shutdown progress dialog.
73 */
74 public static void shutdown(final Context context, boolean confirm) {
75 // ensure that only one thread is trying to power down.
76 // any additional calls are just returned
77 synchronized (sIsStartedGuard){
78 if (sIsStarted) {
79 Log.d(TAG, "Request to shutdown already running, returning.");
80 return;
81 }
82 }
83
84 Log.d(TAG, "Notifying thread to start radio shutdown");
85
86 if (confirm) {
87 final AlertDialog dialog = new AlertDialog.Builder(context)
88 .setIcon(android.R.drawable.ic_dialog_alert)
89 .setTitle(com.android.internal.R.string.power_off)
90 .setMessage(com.android.internal.R.string.shutdown_confirm)
91 .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {
92 public void onClick(DialogInterface dialog, int which) {
93 beginShutdownSequence(context);
94 }
95 })
96 .setNegativeButton(com.android.internal.R.string.no, null)
97 .create();
98 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
Dianne Hackborn568cae52009-10-07 16:13:39 -070099 if (!context.getResources().getBoolean(
100 com.android.internal.R.bool.config_sf_slowBlur)) {
101 dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
102 }
Dianne Hackborn55280a92009-05-07 15:53:46 -0700103 dialog.show();
104 } else {
105 beginShutdownSequence(context);
106 }
107 }
108
109 private static void beginShutdownSequence(Context context) {
110 synchronized (sIsStartedGuard) {
111 sIsStarted = true;
112 }
113
114 // throw up an indeterminate system dialog to indicate radio is
115 // shutting down.
116 ProgressDialog pd = new ProgressDialog(context);
117 pd.setTitle(context.getText(com.android.internal.R.string.power_off));
118 pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
119 pd.setIndeterminate(true);
120 pd.setCancelable(false);
121 pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
Dianne Hackborn568cae52009-10-07 16:13:39 -0700122 if (!context.getResources().getBoolean(
123 com.android.internal.R.bool.config_sf_slowBlur)) {
124 pd.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
125 }
Dianne Hackborn55280a92009-05-07 15:53:46 -0700126
127 pd.show();
128
129 // start the thread that initiates shutdown
130 sInstance.mContext = context;
Dianne Hackbornf99ae762010-03-08 12:43:51 -0800131 sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
132 sInstance.mWakeLock = null;
133 if (sInstance.mPowerManager.isScreenOn()) {
134 try {
135 sInstance.mWakeLock = sInstance.mPowerManager.newWakeLock(
136 PowerManager.FULL_WAKE_LOCK, "Shutdown");
137 sInstance.mWakeLock.acquire();
138 } catch (SecurityException e) {
139 Log.w(TAG, "No permission to acquire wake lock", e);
140 sInstance.mWakeLock = null;
141 }
142 }
Dianne Hackborn55280a92009-05-07 15:53:46 -0700143 sInstance.mHandler = new Handler() {
144 };
145 sInstance.start();
146 }
147
148 void broadcastDone() {
149 synchronized (mBroadcastDoneSync) {
150 mBroadcastDone = true;
151 mBroadcastDoneSync.notifyAll();
152 }
153 }
154
155 /**
156 * Makes sure we handle the shutdown gracefully.
157 * Shuts off power regardless of radio and bluetooth state if the alloted time has passed.
158 */
159 public void run() {
160 boolean bluetoothOff;
161 boolean radioOff;
162
163 BroadcastReceiver br = new BroadcastReceiver() {
164 @Override public void onReceive(Context context, Intent intent) {
165 // We don't allow apps to cancel this, so ignore the result.
166 broadcastDone();
167 }
168 };
169
170 Log.i(TAG, "Sending shutdown broadcast...");
171
172 // First send the high-level shut down broadcast.
173 mBroadcastDone = false;
174 mContext.sendOrderedBroadcast(new Intent(Intent.ACTION_SHUTDOWN), null,
175 br, mHandler, 0, null, null);
176
177 final long endTime = System.currentTimeMillis() + MAX_BROADCAST_TIME;
178 synchronized (mBroadcastDoneSync) {
179 while (!mBroadcastDone) {
180 long delay = endTime - System.currentTimeMillis();
181 if (delay <= 0) {
182 Log.w(TAG, "Shutdown broadcast timed out");
183 break;
184 }
185 try {
186 mBroadcastDoneSync.wait(delay);
187 } catch (InterruptedException e) {
188 }
189 }
190 }
191
192 Log.i(TAG, "Shutting down activity manager...");
193
194 final IActivityManager am =
195 ActivityManagerNative.asInterface(ServiceManager.checkService("activity"));
196 if (am != null) {
197 try {
198 am.shutdown(MAX_BROADCAST_TIME);
199 } catch (RemoteException e) {
200 }
201 }
202
203 final ITelephony phone =
204 ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
Nick Pellybd022f42009-08-14 18:33:38 -0700205 final IBluetooth bluetooth =
206 IBluetooth.Stub.asInterface(ServiceManager.checkService(
Nick Pellyf242b7b2009-10-08 00:12:45 +0200207 BluetoothAdapter.BLUETOOTH_SERVICE));
San Mehat9f7f7ca2010-01-07 11:34:59 -0800208
209 final IMountService mount =
210 IMountService.Stub.asInterface(
211 ServiceManager.checkService("mount"));
Dianne Hackborn55280a92009-05-07 15:53:46 -0700212
213 try {
214 bluetoothOff = bluetooth == null ||
Nick Pellyde893f52009-09-08 13:15:33 -0700215 bluetooth.getBluetoothState() == BluetoothAdapter.STATE_OFF;
Dianne Hackborn55280a92009-05-07 15:53:46 -0700216 if (!bluetoothOff) {
217 Log.w(TAG, "Disabling Bluetooth...");
218 bluetooth.disable(false); // disable but don't persist new state
219 }
220 } catch (RemoteException ex) {
221 Log.e(TAG, "RemoteException during bluetooth shutdown", ex);
222 bluetoothOff = true;
223 }
224
225 try {
226 radioOff = phone == null || !phone.isRadioOn();
227 if (!radioOff) {
228 Log.w(TAG, "Turning off radio...");
229 phone.setRadio(false);
230 }
231 } catch (RemoteException ex) {
232 Log.e(TAG, "RemoteException during radio shutdown", ex);
233 radioOff = true;
234 }
235
236 Log.i(TAG, "Waiting for Bluetooth and Radio...");
237
238 // Wait a max of 32 seconds for clean shutdown
239 for (int i = 0; i < MAX_NUM_PHONE_STATE_READS; i++) {
240 if (!bluetoothOff) {
241 try {
242 bluetoothOff =
Nick Pellyde893f52009-09-08 13:15:33 -0700243 bluetooth.getBluetoothState() == BluetoothAdapter.STATE_OFF;
Dianne Hackborn55280a92009-05-07 15:53:46 -0700244 } catch (RemoteException ex) {
245 Log.e(TAG, "RemoteException during bluetooth shutdown", ex);
246 bluetoothOff = true;
247 }
248 }
249 if (!radioOff) {
250 try {
251 radioOff = !phone.isRadioOn();
252 } catch (RemoteException ex) {
253 Log.e(TAG, "RemoteException during radio shutdown", ex);
254 radioOff = true;
255 }
256 }
257 if (radioOff && bluetoothOff) {
258 Log.i(TAG, "Radio and Bluetooth shutdown complete.");
259 break;
260 }
261 SystemClock.sleep(PHONE_STATE_POLL_SLEEP_MSEC);
262 }
263
San Mehat9f7f7ca2010-01-07 11:34:59 -0800264 // Shutdown MountService to ensure media is in a safe state
265 try {
266 if (mount != null) {
267 mount.shutdown();
268 } else {
269 Log.w(TAG, "MountService unavailable for shutdown");
270 }
271 } catch (Exception e) {
272 Log.e(TAG, "Exception during MountService shutdown", e);
273 }
274
Dianne Hackborn55280a92009-05-07 15:53:46 -0700275 //shutdown power
276 Log.i(TAG, "Performing low-level shutdown...");
277 Power.shutdown();
278 }
279}