1 /* 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 package com.android.phone; 18 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; 31 32 import com.android.internal.telephony.Phone; 33 import com.android.internal.telephony.PhoneFactory; 34 35 /** 36 * Helper class used by the InCallScreen to handle certain special 37 * cases when making an emergency call. 38 * 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. 42 * 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. 49 */ 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 53 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"; 56 57 /** count indicating an initial attempt at the call should be made. */ 58 public static final int INITIAL_ATTEMPT = -1; 59 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; 63 64 // constant events 65 private static final int EVENT_SERVICE_STATE_CHANGED = 100; 66 private static final int EVENT_TIMEOUT_EMERGENCY_CALL = 200; 67 68 /** 69 * Package holding information needed for the callback. 70 */ 71 private static class EmergencyCallInfo { 72 public Phone phone; 73 public Intent intent; 74 public ProgressDialog dialog; 75 public Application app; 76 } 77 78 /** 79 * static handler class, used to handle the two relevent events. 80 */ 81 private static EmergencyCallEventHandler sHandler; 82 private static class EmergencyCallEventHandler extends Handler { handleMessage(Message msg)83 public void handleMessage(Message msg) { 84 switch(msg.what) { 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); 94 eci.dialog.dismiss(); 95 96 if (DBG) Log.d(TAG, "About to (re)launch InCallScreen: " + eci.intent); 97 eci.app.startActivity(eci.intent); 98 } 99 } 100 break; 101 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(); 107 108 if (DBG) Log.d(TAG, "About to (re)launch InCallScreen: " + eci.intent); 109 eci.app.startActivity(eci.intent); 110 } 111 break; 112 } 113 } 114 } 115 116 @Override onCreate(Bundle icicle)117 protected void onCreate(Bundle icicle) { 118 Log.i(TAG, "onCreate()... intent = " + getIntent()); 119 super.onCreate(icicle); 120 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. 126 // 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()); 133 } 134 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); 138 139 // create a new message object. 140 EmergencyCallInfo eci = new EmergencyCallInfo(); 141 eci.phone = phone; 142 eci.app = getApplication(); 143 eci.dialog = constructDialog(retryCount); 144 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); 154 155 // create the handler. 156 if (sHandler == null) { 157 sHandler = new EmergencyCallEventHandler(); 158 } 159 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 163 // the call again. 164 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); 172 173 // turn the radio on and listen for it to complete. 174 phone.registerForServiceStateChanged(sHandler, 175 EVENT_SERVICE_STATE_CHANGED, eci); 176 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..."); 182 183 // Change the system setting 184 Settings.System.putInt(getContentResolver(), Settings.System.AIRPLANE_MODE_ON, 0); 185 186 // Post the intent 187 Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); 188 intent.putExtra("state", false); 189 sendBroadcast(intent); 190 191 // Otherwise, for some strange reason the radio is just off, so 192 // we just turn it back on. 193 } else { 194 if (DBG) Log.d(TAG, "Manually powering radio on..."); 195 phone.setRadioPower(true); 196 } 197 198 } else { 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)); 202 203 // get the message and attach the data, then wait the alloted 204 // time and send. 205 Message m = sHandler.obtainMessage(EVENT_TIMEOUT_EMERGENCY_CALL); 206 m.obj = eci; 207 sHandler.sendMessageDelayed(m, TIME_BETWEEN_RETRIES_MS); 208 } 209 finish(); 210 } 211 212 /** 213 * create the dialog and hand it back to caller. 214 */ constructDialog(int retryCount)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; 220 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); 229 230 // show the dialog 231 pd.show(); 232 233 return pd; 234 } 235 236 @Override onNewIntent(Intent intent)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); 242 } 243 } 244