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 com.android.internal.telephony.Phone; 29 import com.android.internal.telephony.PhoneFactory; 30 import android.telephony.ServiceState; 31 import android.view.WindowManager; 32 33 /** 34 * Helper class used by the InCallScreen to handle certain special 35 * cases when making an emergency call. 36 * 37 * Specifically, if the user tries to dial an emergency number but the 38 * radio is off, e.g. if the device is in airplane mode, this class is 39 * responsible for turning the radio back on and retrying the call. 40 * 41 * This class is initially launched using the same intent originally 42 * passed to the InCallScreen (presumably an ACTION_CALL_EMERGENCY intent) 43 * but with this class explicitly set as the className/component. Later, 44 * we retry the emergency call by firing off that same intent, with the 45 * component cleared, and using an integer extra called 46 * EMERGENCY_CALL_RETRY_KEY to convey information about the current state. 47 */ 48 public class EmergencyCallHandler extends Activity { 49 /** the key used to get the count from our Intent's extra(s) */ 50 public static final String EMERGENCY_CALL_RETRY_KEY = "emergency_call_retry_count"; 51 52 /** count indicating an initial attempt at the call should be made. */ 53 public static final int INITIAL_ATTEMPT = -1; 54 55 /** number of times to retry the call and the time spent in between attempts*/ 56 public static final int NUMBER_OF_RETRIES = 6; 57 public static final int TIME_BETWEEN_RETRIES_MS = 5000; 58 59 // constant events 60 private static final int EVENT_SERVICE_STATE_CHANGED = 100; 61 private static final int EVENT_TIMEOUT_EMERGENCY_CALL = 200; 62 63 /** 64 * Package holding information needed for the callback. 65 */ 66 private static class EmergencyCallInfo { 67 public Phone phone; 68 public Intent intent; 69 public ProgressDialog dialog; 70 public Application app; 71 } 72 73 /** 74 * static handler class, used to handle the two relevent events. 75 */ 76 private static EmergencyCallEventHandler sHandler; 77 private static class EmergencyCallEventHandler extends Handler { handleMessage(Message msg)78 public void handleMessage(Message msg) { 79 switch(msg.what) { 80 case EVENT_SERVICE_STATE_CHANGED: { 81 // make the initial call attempt after the radio is turned on. 82 ServiceState state = (ServiceState) ((AsyncResult) msg.obj).result; 83 if (state.getState() != ServiceState.STATE_POWER_OFF) { 84 EmergencyCallInfo eci = 85 (EmergencyCallInfo) ((AsyncResult) msg.obj).userObj; 86 // deregister for the service state change events. 87 eci.phone.unregisterForServiceStateChanged(this); 88 eci.app.startActivity(eci.intent); 89 eci.dialog.dismiss(); 90 } 91 } 92 break; 93 94 case EVENT_TIMEOUT_EMERGENCY_CALL: { 95 // repeated call after the timeout period. 96 EmergencyCallInfo eci = (EmergencyCallInfo) msg.obj; 97 eci.app.startActivity(eci.intent); 98 eci.dialog.dismiss(); 99 } 100 break; 101 } 102 } 103 } 104 105 @Override onCreate(Bundle icicle)106 protected void onCreate(Bundle icicle) { 107 super.onCreate(icicle); 108 109 // setup the phone and get the retry count embedded in the intent. 110 Phone phone = PhoneFactory.getDefaultPhone(); 111 int retryCount = getIntent().getIntExtra(EMERGENCY_CALL_RETRY_KEY, INITIAL_ATTEMPT); 112 113 // create a new message object. 114 EmergencyCallInfo eci = new EmergencyCallInfo(); 115 eci.phone = phone; 116 eci.app = getApplication(); 117 eci.dialog = constructDialog(retryCount); 118 119 // The Intent we're going to fire off to retry the call is the 120 // same one that got us here (except that we *don't* explicitly 121 // specify this class as the component!) 122 eci.intent = getIntent().setComponent(null); 123 // And we'll be firing this Intent from the PhoneApp's context 124 // (see the startActivity() calls above) so the 125 // FLAG_ACTIVITY_NEW_TASK flag is required. 126 eci.intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 127 128 // create the handler. 129 if (sHandler == null) { 130 sHandler = new EmergencyCallEventHandler(); 131 } 132 133 // If this is the initial attempt, we need to register for a radio state 134 // change and turn the radio on. Otherwise, this is just a retry, and 135 // we simply wait the alloted time before sending the request to try 136 // the call again. 137 138 // Note: The radio logic ITSELF will try its best to put the emergency 139 // call through once the radio is turned on. The retry we have here 140 // is in case it fails; the current constants we have include making 141 // 6 attempts, with a 5 second delay between each. 142 if (retryCount == INITIAL_ATTEMPT) { 143 // place the number of pending retries in the intent. 144 eci.intent.putExtra(EMERGENCY_CALL_RETRY_KEY, NUMBER_OF_RETRIES); 145 146 // turn the radio on and listen for it to complete. 147 phone.registerForServiceStateChanged(sHandler, 148 EVENT_SERVICE_STATE_CHANGED, eci); 149 150 // If airplane mode is on, we turn it off the same way that the 151 // Settings activity turns it off. 152 if (Settings.System.getInt(getContentResolver(), 153 Settings.System.AIRPLANE_MODE_ON, 0) > 0) { 154 // Change the system setting 155 Settings.System.putInt(getContentResolver(), Settings.System.AIRPLANE_MODE_ON, 0); 156 157 // Post the intent 158 Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); 159 intent.putExtra("state", false); 160 sendBroadcast(intent); 161 162 // Otherwise, for some strange reason the radio is just off, so 163 // we just turn it back on. 164 } else { 165 phone.setRadioPower(true); 166 } 167 168 } else { 169 // decrement and store the number of retries. 170 eci.intent.putExtra(EMERGENCY_CALL_RETRY_KEY, (retryCount - 1)); 171 172 // get the message and attach the data, then wait the alloted 173 // time and send. 174 Message m = sHandler.obtainMessage(EVENT_TIMEOUT_EMERGENCY_CALL); 175 m.obj = eci; 176 sHandler.sendMessageDelayed(m, TIME_BETWEEN_RETRIES_MS); 177 } 178 finish(); 179 } 180 181 /** 182 * create the dialog and hand it back to caller. 183 */ constructDialog(int retryCount)184 private ProgressDialog constructDialog(int retryCount) { 185 // figure out the message to display. 186 int msgId = (retryCount == INITIAL_ATTEMPT) ? 187 R.string.emergency_enable_radio_dialog_message : 188 R.string.emergency_enable_radio_dialog_retry; 189 190 // create a system dialog that will persist outside this activity. 191 ProgressDialog pd = new ProgressDialog(getApplication()); 192 pd.setTitle(getText(R.string.emergency_enable_radio_dialog_title)); 193 pd.setMessage(getText(msgId)); 194 pd.setIndeterminate(true); 195 pd.setCancelable(false); 196 pd.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG); 197 pd.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND); 198 199 // show the dialog 200 pd.show(); 201 202 return pd; 203 } 204 } 205