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