2 * Copyright (C) 2008 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.phone;
19 import android.app.Activity;
20 import android.app.Application;
21 import android.app.ProgressDialog;
22 import android.content.Intent;
23 import android.os.AsyncResult;
24 import android.os.Bundle;
25 import android.os.Handler;
26 import android.os.Message;
27 import android.provider.Settings;
28 import android.telephony.ServiceState;
29 import android.util.Log;
30 import android.view.WindowManager;
32 import com.android.internal.telephony.Phone;
33 import com.android.internal.telephony.PhoneFactory;
36 * Helper class used by the InCallScreen to handle certain special
37 * cases when making an emergency call.
39 * Specifically, if the user tries to dial an emergency number but the
40 * radio is off, e.g. if the device is in airplane mode, this class is
41 * responsible for turning the radio back on and retrying the call.
43 * This class is initially launched using the same intent originally
44 * passed to the InCallScreen (presumably an ACTION_CALL_EMERGENCY intent)
45 * but with this class explicitly set as the className/component. Later,
46 * we retry the emergency call by firing off that same intent, with the
47 * component cleared, and using an integer extra called
48 * EMERGENCY_CALL_RETRY_KEY to convey information about the current state.
50 public class EmergencyCallHandler extends Activity {
51 private static final String TAG = "EmergencyCallHandler";
52 private static final boolean DBG = true; // OK to have this on by default
54 /** the key used to get the count from our Intent's extra(s) */
55 public static final String EMERGENCY_CALL_RETRY_KEY = "emergency_call_retry_count";
57 /** count indicating an initial attempt at the call should be made. */
58 public static final int INITIAL_ATTEMPT = -1;
60 /** number of times to retry the call and the time spent in between attempts*/
61 public static final int NUMBER_OF_RETRIES = 6;
62 public static final int TIME_BETWEEN_RETRIES_MS = 5000;
65 private static final int EVENT_SERVICE_STATE_CHANGED = 100;
66 private static final int EVENT_TIMEOUT_EMERGENCY_CALL = 200;
69 * Package holding information needed for the callback.
71 private static class EmergencyCallInfo {
74 public ProgressDialog dialog;
75 public Application app;
79 * static handler class, used to handle the two relevent events.
81 private static EmergencyCallEventHandler sHandler;
82 private static class EmergencyCallEventHandler extends Handler {
83 public void handleMessage(Message msg) {
85 case EVENT_SERVICE_STATE_CHANGED: {
86 // make the initial call attempt after the radio is turned on.
87 ServiceState state = (ServiceState) ((AsyncResult) msg.obj).result;
88 if (DBG) Log.d(TAG, "EVENT_SERVICE_STATE_CHANGED: state = " + state);
89 if (state.getState() != ServiceState.STATE_POWER_OFF) {
90 EmergencyCallInfo eci =
91 (EmergencyCallInfo) ((AsyncResult) msg.obj).userObj;
92 // deregister for the service state change events.
93 eci.phone.unregisterForServiceStateChanged(this);
96 if (DBG) Log.d(TAG, "About to (re)launch InCallScreen: " + eci.intent);
97 eci.app.startActivity(eci.intent);
102 case EVENT_TIMEOUT_EMERGENCY_CALL: {
103 if (DBG) Log.d(TAG, "EVENT_TIMEOUT_EMERGENCY_CALL...");
104 // repeated call after the timeout period.
105 EmergencyCallInfo eci = (EmergencyCallInfo) msg.obj;
106 eci.dialog.dismiss();
108 if (DBG) Log.d(TAG, "About to (re)launch InCallScreen: " + eci.intent);
109 eci.app.startActivity(eci.intent);
117 protected void onCreate(Bundle icicle) {
118 Log.i(TAG, "onCreate()... intent = " + getIntent());
119 super.onCreate(icicle);
121 // Watch out: the intent action we get here should always be
122 // ACTION_CALL_EMERGENCY, since the whole point of this activity
123 // is for it to be launched using the same intent originally
124 // passed to the InCallScreen, which will always be
125 // ACTION_CALL_EMERGENCY when making an emergency call.
127 // If we ever get launched with any other action, especially if it's
128 // "com.android.phone.InCallScreen.UNDEFINED" (as in bug 3094858), that
129 // almost certainly indicates a logic bug in the InCallScreen.
130 if (!Intent.ACTION_CALL_EMERGENCY.equals(getIntent().getAction())) {
131 Log.w(TAG, "Unexpected intent action! Should be ACTION_CALL_EMERGENCY, "
132 + "but instead got: " + getIntent().getAction());
135 // setup the phone and get the retry count embedded in the intent.
136 Phone phone = PhoneFactory.getDefaultPhone();
137 int retryCount = getIntent().getIntExtra(EMERGENCY_CALL_RETRY_KEY, INITIAL_ATTEMPT);
139 // create a new message object.
140 EmergencyCallInfo eci = new EmergencyCallInfo();
142 eci.app = getApplication();
143 eci.dialog = constructDialog(retryCount);
145 // The Intent we're going to fire off to retry the call is the
146 // same one that got us here (except that we *don't* explicitly
147 // specify this class as the component!)
148 eci.intent = getIntent().setComponent(null);
149 // And we'll be firing this Intent from the PhoneApp's context
150 // (see the startActivity() calls above) so the
151 // FLAG_ACTIVITY_NEW_TASK flag is required.
152 eci.intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
153 if (DBG) Log.d(TAG, "- initial eci.intent: " + eci.intent);
155 // create the handler.
156 if (sHandler == null) {
157 sHandler = new EmergencyCallEventHandler();
160 // If this is the initial attempt, we need to register for a radio state
161 // change and turn the radio on. Otherwise, this is just a retry, and
162 // we simply wait the alloted time before sending the request to try
165 // Note: The radio logic ITSELF will try its best to put the emergency
166 // call through once the radio is turned on. The retry we have here
167 // is in case it fails; the current constants we have include making
168 // 6 attempts, with a 5 second delay between each.
169 if (retryCount == INITIAL_ATTEMPT) {
170 // place the number of pending retries in the intent.
171 eci.intent.putExtra(EMERGENCY_CALL_RETRY_KEY, NUMBER_OF_RETRIES);
173 // turn the radio on and listen for it to complete.
174 phone.registerForServiceStateChanged(sHandler,
175 EVENT_SERVICE_STATE_CHANGED, eci);
177 // If airplane mode is on, we turn it off the same way that the
178 // Settings activity turns it off.
179 if (Settings.System.getInt(getContentResolver(),
180 Settings.System.AIRPLANE_MODE_ON, 0) > 0) {
181 if (DBG) Log.d(TAG, "Turning off airplane mode...");
183 // Change the system setting
184 Settings.System.putInt(getContentResolver(), Settings.System.AIRPLANE_MODE_ON, 0);
187 Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
188 intent.putExtra("state", false);
189 sendBroadcast(intent);
191 // Otherwise, for some strange reason the radio is just off, so
192 // we just turn it back on.
194 if (DBG) Log.d(TAG, "Manually powering radio on...");
195 phone.setRadioPower(true);
199 // decrement and store the number of retries.
200 if (DBG) Log.d(TAG, "Retry attempt... retryCount = " + retryCount);
201 eci.intent.putExtra(EMERGENCY_CALL_RETRY_KEY, (retryCount - 1));
203 // get the message and attach the data, then wait the alloted
205 Message m = sHandler.obtainMessage(EVENT_TIMEOUT_EMERGENCY_CALL);
207 sHandler.sendMessageDelayed(m, TIME_BETWEEN_RETRIES_MS);
213 * create the dialog and hand it back to caller.
215 private ProgressDialog constructDialog(int retryCount) {
216 // figure out the message to display.
217 int msgId = (retryCount == INITIAL_ATTEMPT) ?
218 R.string.emergency_enable_radio_dialog_message :
219 R.string.emergency_enable_radio_dialog_retry;
221 // create a system dialog that will persist outside this activity.
222 ProgressDialog pd = new ProgressDialog(getApplication());
223 pd.setTitle(getText(R.string.emergency_enable_radio_dialog_title));
224 pd.setMessage(getText(msgId));
225 pd.setIndeterminate(true);
226 pd.setCancelable(false);
227 pd.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
228 pd.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
237 protected void onNewIntent(Intent intent) {
238 // We shouldn't ever get here, since we should never be launched in
239 // "singleTop" mode in the first place.
240 Log.w(TAG, "Unexpected call to onNewIntent(): intent=" + intent);
241 super.onNewIntent(intent);