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