17d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon/* 27d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * Copyright (C) 2011 The Android Open Source Project 37d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * 47d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * Licensed under the Apache License, Version 2.0 (the "License"); 57d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * you may not use this file except in compliance with the License. 67d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * You may obtain a copy of the License at 77d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * 87d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * http://www.apache.org/licenses/LICENSE-2.0 97d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * 107d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * Unless required by applicable law or agreed to in writing, software 117d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * distributed under the License is distributed on an "AS IS" BASIS, 127d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 137d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * See the License for the specific language governing permissions and 147d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * limitations under the License. 157d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon */ 167d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 177d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordonpackage com.android.phone; 187d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 197d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordonimport com.android.internal.telephony.CallManager; 207d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordonimport com.android.internal.telephony.Connection; 217d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordonimport com.android.internal.telephony.Phone; 227d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordonimport com.android.internal.telephony.PhoneConstants; 237d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 247d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordonimport android.content.Context; 257d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordonimport android.content.Intent; 267d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordonimport android.os.AsyncResult; 277d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordonimport android.os.Handler; 287d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordonimport android.os.Message; 297d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordonimport android.os.PowerManager; 307d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordonimport android.os.UserHandle; 317d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordonimport android.provider.Settings; 327d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordonimport android.telephony.ServiceState; 337d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordonimport android.util.Log; 347d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 357d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 367d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon/** 377d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * Helper class for the {@link CallController} that implements special 387d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * behavior related to emergency calls. Specifically, this class handles 397d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * the case of the user trying to dial an emergency number while the radio 407d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * is off (i.e. the device is in airplane mode), by forcibly turning the 417d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * radio back on, waiting for it to come up, and then retrying the 427d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * emergency call. 437d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * 447d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * This class is instantiated lazily (the first time the user attempts to 457d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * make an emergency call from airplane mode) by the the 467d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * {@link CallController} singleton. 477d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon */ 487d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordonpublic class EmergencyCallHelper extends Handler { 497d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon private static final String TAG = "EmergencyCallHelper"; 507d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon private static final boolean DBG = false; 517d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 527d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Number of times to retry the call, and time between retry attempts. 537d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon public static final int MAX_NUM_RETRIES = 6; 547d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon public static final long TIME_BETWEEN_RETRIES = 5000; // msec 557d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 567d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Timeout used with our wake lock (just as a safety valve to make 577d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // sure we don't hold it forever). 587d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon public static final long WAKE_LOCK_TIMEOUT = 5 * 60 * 1000; // 5 minutes in msec 597d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 607d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Handler message codes; see handleMessage() 617d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon private static final int START_SEQUENCE = 1; 627d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon private static final int SERVICE_STATE_CHANGED = 2; 637d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon private static final int DISCONNECT = 3; 647d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon private static final int RETRY_TIMEOUT = 4; 657d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 667d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon private CallController mCallController; 677d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon private PhoneGlobals mApp; 687d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon private CallManager mCM; 697d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon private String mNumber; // The emergency number we're trying to dial 707d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon private int mNumRetriesSoFar; 717d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 727d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Wake lock we hold while running the whole sequence 737d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon private PowerManager.WakeLock mPartialWakeLock; 747d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 757d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon public EmergencyCallHelper(CallController callController) { 767d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (DBG) log("EmergencyCallHelper constructor..."); 777d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon mCallController = callController; 787d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon mApp = PhoneGlobals.getInstance(); 797d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon mCM = mApp.mCM; 807d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 817d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 827d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon @Override 837d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon public void handleMessage(Message msg) { 847d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon switch (msg.what) { 857d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon case START_SEQUENCE: 867d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon startSequenceInternal(msg); 877d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon break; 887d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon case SERVICE_STATE_CHANGED: 897d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon onServiceStateChanged(msg); 907d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon break; 917d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon case DISCONNECT: 927d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon onDisconnect(msg); 937d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon break; 947d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon case RETRY_TIMEOUT: 957d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon onRetryTimeout(); 967d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon break; 977d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon default: 987d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Log.wtf(TAG, "handleMessage: unexpected message: " + msg); 997d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon break; 1007d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 1017d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 1027d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 1037d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon /** 1047d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * Starts the "emergency call from airplane mode" sequence. 1057d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * 1067d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * This is the (single) external API of the EmergencyCallHelper class. 1077d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * This method is called from the CallController placeCall() sequence 1087d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * if the user dials a valid emergency number, but the radio is 1097d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * powered-off (presumably due to airplane mode.) 1107d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * 1117d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * This method kicks off the following sequence: 1127d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * - Power on the radio 1137d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * - Listen for the service state change event telling us the radio has come up 1147d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * - Then launch the emergency call 1157d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * - Retry if the call fails with an OUT_OF_SERVICE error 1167d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * - Retry if we've gone 5 seconds without any response from the radio 1177d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * - Finally, clean up any leftover state (progress UI, wake locks, etc.) 1187d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * 1197d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * This method is safe to call from any thread, since it simply posts 1207d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * a message to the EmergencyCallHelper's handler (thus ensuring that 1217d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * the rest of the sequence is entirely serialized, and runs only on 1227d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * the handler thread.) 1237d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * 1247d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * This method does *not* force the in-call UI to come up; our caller 1257d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * is responsible for doing that (presumably by calling 1267d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * PhoneApp.displayCallScreen().) 1277d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon */ 1287d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon public void startEmergencyCallFromAirplaneModeSequence(String number) { 1297d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (DBG) log("startEmergencyCallFromAirplaneModeSequence('" + number + "')..."); 1307d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Message msg = obtainMessage(START_SEQUENCE, number); 1317d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon sendMessage(msg); 1327d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 1337d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 1347d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon /** 1357d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * Actual implementation of startEmergencyCallFromAirplaneModeSequence(), 1367d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * guaranteed to run on the handler thread. 1373a2daabaaffe0e056ec930c248b00f1dbac98f8bJake Hamby * @see #startEmergencyCallFromAirplaneModeSequence 1387d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon */ 1397d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon private void startSequenceInternal(Message msg) { 1407d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (DBG) log("startSequenceInternal(): msg = " + msg); 1417d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 1427d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // First of all, clean up any state (including mPartialWakeLock!) 1437d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // left over from a prior emergency call sequence. 1447d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // This ensures that we'll behave sanely if another 1457d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // startEmergencyCallFromAirplaneModeSequence() comes in while 1467d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // we're already in the middle of the sequence. 1477d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon cleanup(); 1487d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 1497d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon mNumber = (String) msg.obj; 1507d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (DBG) log("- startSequenceInternal: Got mNumber: '" + mNumber + "'"); 1517d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 1527d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon mNumRetriesSoFar = 0; 1537d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 1547d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Wake lock to make sure the processor doesn't go to sleep midway 1557d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // through the emergency call sequence. 1567d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon PowerManager pm = (PowerManager) mApp.getSystemService(Context.POWER_SERVICE); 1577d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); 1587d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Acquire with a timeout, just to be sure we won't hold the wake 1597d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // lock forever even if a logic bug (in this class) causes us to 1607d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // somehow never call cleanup(). 1617d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (DBG) log("- startSequenceInternal: acquiring wake lock"); 1627d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon mPartialWakeLock.acquire(WAKE_LOCK_TIMEOUT); 1637d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 1647d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // No need to check the current service state here, since the only 1657d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // reason the CallController would call this method in the first 1667d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // place is if the radio is powered-off. 1677d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // 1687d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // So just go ahead and turn the radio on. 1697d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 1707d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon powerOnRadio(); // We'll get an onServiceStateChanged() callback 1717d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // when the radio successfully comes up. 1727d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 1737d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Next step: when the SERVICE_STATE_CHANGED event comes in, 1747d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // we'll retry the call; see placeEmergencyCall(); 1757d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // But also, just in case, start a timer to make sure we'll retry 1767d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // the call even if the SERVICE_STATE_CHANGED event never comes in 1777d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // for some reason. 1787d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon startRetryTimer(); 1797d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 1807d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // (Our caller is responsible for calling mApp.displayCallScreen().) 1817d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 1827d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 1837d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon /** 1847d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * Handles the SERVICE_STATE_CHANGED event. 1857d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * 1867d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * (Normally this event tells us that the radio has finally come 1877d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * up. In that case, it's now safe to actually place the 1887d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * emergency call.) 1897d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon */ 1907d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon private void onServiceStateChanged(Message msg) { 1917d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon ServiceState state = (ServiceState) ((AsyncResult) msg.obj).result; 1927d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (DBG) log("onServiceStateChanged()... new state = " + state); 1937d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 1947d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Possible service states: 1957d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // - STATE_IN_SERVICE // Normal operation 1967d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // - STATE_OUT_OF_SERVICE // Still searching for an operator to register to, 1977d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // // or no radio signal 1987d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // - STATE_EMERGENCY_ONLY // Phone is locked; only emergency numbers are allowed 1997d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // - STATE_POWER_OFF // Radio is explicitly powered off (airplane mode) 2007d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 2017d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Once we reach either STATE_IN_SERVICE or STATE_EMERGENCY_ONLY, 2027d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // it's finally OK to place the emergency call. 2037d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon boolean okToCall = (state.getState() == ServiceState.STATE_IN_SERVICE) 2047d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon || (state.getState() == ServiceState.STATE_EMERGENCY_ONLY); 2057d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 2067d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (okToCall) { 2077d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Woo hoo! It's OK to actually place the call. 2087d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (DBG) log("onServiceStateChanged: ok to call!"); 2097d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 2107d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Deregister for the service state change events. 2117d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon unregisterForServiceStateChanged(); 2127d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 2137d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon placeEmergencyCall(); 2147d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } else { 2157d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // The service state changed, but we're still not ready to call yet. 2167d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // (This probably was the transition from STATE_POWER_OFF to 2177d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // STATE_OUT_OF_SERVICE, which happens immediately after powering-on 2187d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // the radio.) 2197d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // 2207d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // So just keep waiting; we'll probably get to either 2217d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // STATE_IN_SERVICE or STATE_EMERGENCY_ONLY very shortly. 2227d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // (Or even if that doesn't happen, we'll at least do another retry 2237d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // when the RETRY_TIMEOUT event fires.) 2247d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (DBG) log("onServiceStateChanged: not ready to call yet, keep waiting..."); 2257d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 2267d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 2277d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 2287d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon /** 2297d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * Handles a DISCONNECT event from the telephony layer. 2307d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * 2317d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * Even after we successfully place an emergency call (after powering 2327d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * on the radio), it's still possible for the call to fail with the 2337d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * disconnect cause OUT_OF_SERVICE. If so, schedule a retry. 2347d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon */ 2357d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon private void onDisconnect(Message msg) { 2367d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Connection conn = (Connection) ((AsyncResult) msg.obj).result; 2377d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Connection.DisconnectCause cause = conn.getDisconnectCause(); 2387d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (DBG) log("onDisconnect: connection '" + conn 2397d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon + "', addr '" + conn.getAddress() + "', cause = " + cause); 2407d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 2417d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (cause == Connection.DisconnectCause.OUT_OF_SERVICE) { 2427d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Wait a bit more and try again (or just bail out totally if 2437d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // we've had too many failures.) 2447d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (DBG) log("- onDisconnect: OUT_OF_SERVICE, need to retry..."); 2457d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon scheduleRetryOrBailOut(); 2467d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } else { 2477d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Any other disconnect cause means we're done. 2487d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Either the emergency call succeeded *and* ended normally, 2497d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // or else there was some error that we can't retry. In either 2507d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // case, just clean up our internal state.) 2517d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 2527d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (DBG) log("==> Disconnect event; clean up..."); 2537d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon cleanup(); 2547d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 2557d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Nothing else to do here. If the InCallScreen was visible, 2567d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // it would have received this disconnect event too (so it'll 2577d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // show the "Call ended" state and finish itself without any 2587d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // help from us.) 2597d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 2607d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 2617d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 2627d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon /** 2637d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * Handles the retry timer expiring. 2647d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon */ 2657d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon private void onRetryTimeout() { 2667d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon PhoneConstants.State phoneState = mCM.getState(); 2673a2daabaaffe0e056ec930c248b00f1dbac98f8bJake Hamby int serviceState = mCM.getDefaultPhone().getServiceState().getState(); 2687d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (DBG) log("onRetryTimeout(): phone state " + phoneState 2697d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon + ", service state " + serviceState 2707d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon + ", mNumRetriesSoFar = " + mNumRetriesSoFar); 2717d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 2727d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // - If we're actually in a call, we've succeeded. 2737d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // 2747d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // - Otherwise, if the radio is now on, that means we successfully got 2757d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // out of airplane mode but somehow didn't get the service state 2767d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // change event. In that case, try to place the call. 2777d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // 2787d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // - If the radio is still powered off, try powering it on again. 2797d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 2807d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (phoneState == PhoneConstants.State.OFFHOOK) { 2817d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (DBG) log("- onRetryTimeout: Call is active! Cleaning up..."); 2827d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon cleanup(); 2837d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon return; 2847d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 2857d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 2867d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (serviceState != ServiceState.STATE_POWER_OFF) { 2877d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Woo hoo -- we successfully got out of airplane mode. 2887d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 2897d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Deregister for the service state change events; we don't need 2907d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // these any more now that the radio is powered-on. 2917d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon unregisterForServiceStateChanged(); 2927d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 2937d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon placeEmergencyCall(); // If the call fails, placeEmergencyCall() 2947d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // will schedule a retry. 2957d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } else { 2967d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Uh oh; we've waited the full TIME_BETWEEN_RETRIES and the 2977d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // radio is still not powered-on. Try again... 2987d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 2997d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (DBG) log("- Trying (again) to turn on the radio..."); 3007d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon powerOnRadio(); // Again, we'll (hopefully) get an onServiceStateChanged() 3017d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // callback when the radio successfully comes up. 3027d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 3037d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // ...and also set a fresh retry timer (or just bail out 3047d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // totally if we've had too many failures.) 3057d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon scheduleRetryOrBailOut(); 3067d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 3077d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 3087d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 3097d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon /** 3107d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * Attempt to power on the radio (i.e. take the device out 3117d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * of airplane mode.) 3127d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * 3137d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * Additionally, start listening for service state changes; 3147d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * we'll eventually get an onServiceStateChanged() callback 3157d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * when the radio successfully comes up. 3167d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon */ 3177d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon private void powerOnRadio() { 3187d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (DBG) log("- powerOnRadio()..."); 3197d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 3207d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // We're about to turn on the radio, so arrange to be notified 3217d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // when the sequence is complete. 3227d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon registerForServiceStateChanged(); 3237d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 3247d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // If airplane mode is on, we turn it off the same way that the 3257d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Settings activity turns it off. 3267d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (Settings.Global.getInt(mApp.getContentResolver(), 3277d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Settings.Global.AIRPLANE_MODE_ON, 0) > 0) { 3287d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (DBG) log("==> Turning off airplane mode..."); 3297d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 3307d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Change the system setting 3317d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Settings.Global.putInt(mApp.getContentResolver(), 3327d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Settings.Global.AIRPLANE_MODE_ON, 0); 3337d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 3347d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Post the intent 3357d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); 3367d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon intent.putExtra("state", false); 3377d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon mApp.sendBroadcastAsUser(intent, UserHandle.ALL); 3387d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } else { 3397d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Otherwise, for some strange reason the radio is off 3407d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // (even though the Settings database doesn't think we're 3417d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // in airplane mode.) In this case just turn the radio 3427d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // back on. 3437d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (DBG) log("==> (Apparently) not in airplane mode; manually powering radio on..."); 3443a2daabaaffe0e056ec930c248b00f1dbac98f8bJake Hamby mCM.getDefaultPhone().setRadioPower(true); 3457d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 3467d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 3477d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 3487d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon /** 3497d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * Actually initiate the outgoing emergency call. 3507d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * (We do this once the radio has successfully been powered-up.) 3517d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * 3527d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * If the call succeeds, we're done. 3537d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * If the call fails, schedule a retry of the whole sequence. 3547d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon */ 3557d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon private void placeEmergencyCall() { 3567d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (DBG) log("placeEmergencyCall()..."); 3577d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 3587d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Place an outgoing call to mNumber. 3597d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Note we call PhoneUtils.placeCall() directly; we don't want any 3607d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // of the behavior from CallController.placeCallInternal() here. 3617d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // (Specifically, we don't want to start the "emergency call from 3627d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // airplane mode" sequence from the beginning again!) 3637d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 3647d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon registerForDisconnect(); // Get notified when this call disconnects 3657d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 3667d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (DBG) log("- placing call to '" + mNumber + "'..."); 3677d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon int callStatus = PhoneUtils.placeCall(mApp, 3683a2daabaaffe0e056ec930c248b00f1dbac98f8bJake Hamby mCM.getDefaultPhone(), 3697d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon mNumber, 3707d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon null, // contactUri 37169a691914e9b013a7ff52c129d8466c152ed7239Santos Cordon true); // isEmergencyCall 3727d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (DBG) log("- PhoneUtils.placeCall() returned status = " + callStatus); 3737d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 3747d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon boolean success; 3757d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Note PhoneUtils.placeCall() returns one of the CALL_STATUS_* 3767d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // constants, not a CallStatusCode enum value. 3777d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon switch (callStatus) { 3787d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon case PhoneUtils.CALL_STATUS_DIALED: 3797d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon success = true; 3807d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon break; 3817d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 3827d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon case PhoneUtils.CALL_STATUS_DIALED_MMI: 3837d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon case PhoneUtils.CALL_STATUS_FAILED: 3847d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon default: 3857d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Anything else is a failure, and we'll need to retry. 3867d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Log.w(TAG, "placeEmergencyCall(): placeCall() failed: callStatus = " + callStatus); 3877d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon success = false; 3887d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon break; 3897d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 3907d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 3917d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (success) { 3927d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (DBG) log("==> Success from PhoneUtils.placeCall()!"); 3937d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Ok, the emergency call is (hopefully) under way. 3947d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 3957d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // We're not done yet, though, so don't call cleanup() here. 3967d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // (It's still possible that this call will fail, and disconnect 3977d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // with cause==OUT_OF_SERVICE. If so, that will trigger a retry 3987d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // from the onDisconnect() method.) 3997d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } else { 4007d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (DBG) log("==> Failure."); 4017d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Wait a bit more and try again (or just bail out totally if 4027d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // we've had too many failures.) 4037d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon scheduleRetryOrBailOut(); 4047d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 4057d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 4067d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 4077d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon /** 4087d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * Schedules a retry in response to some failure (either the radio 4097d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * failing to power on, or a failure when trying to place the call.) 4107d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * Or, if we've hit the retry limit, bail out of this whole sequence 4117d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * and display a failure message to the user. 4127d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon */ 4137d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon private void scheduleRetryOrBailOut() { 4147d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon mNumRetriesSoFar++; 4157d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (DBG) log("scheduleRetryOrBailOut()... mNumRetriesSoFar is now " + mNumRetriesSoFar); 4167d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 4177d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (mNumRetriesSoFar > MAX_NUM_RETRIES) { 4187d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Log.w(TAG, "scheduleRetryOrBailOut: hit MAX_NUM_RETRIES; giving up..."); 4197d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon cleanup(); 4207d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } else { 4217d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (DBG) log("- Scheduling another retry..."); 4227d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon startRetryTimer(); 4237d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 4247d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 4257d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 4267d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon /** 4277d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * Clean up when done with the whole sequence: either after 4287d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * successfully placing *and* ending the emergency call, or after 4297d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * bailing out because of too many failures. 4307d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * 4317d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * The exact cleanup steps are: 4327d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * - Take down any progress UI (and also ask the in-call UI to refresh itself, 4337d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * if it's still visible) 4347d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * - Double-check that we're not still registered for any telephony events 4357d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * - Clean up any extraneous handler messages (like retry timeouts) still in the queue 4367d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * - Make sure we're not still holding any wake locks 4377d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * 4387d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * Basically this method guarantees that there will be no more 4397d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * activity from the EmergencyCallHelper until the CallController 4407d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * kicks off the whole sequence again with another call to 4417d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * startEmergencyCallFromAirplaneModeSequence(). 4427d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * 4437d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * Note we don't call this method simply after a successful call to 4447d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * placeCall(), since it's still possible the call will disconnect 4457d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon * very quickly with an OUT_OF_SERVICE error. 4467d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon */ 4477d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon private void cleanup() { 4487d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (DBG) log("cleanup()..."); 4497d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 4507d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon unregisterForServiceStateChanged(); 4517d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon unregisterForDisconnect(); 4527d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon cancelRetryTimer(); 4537d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 4547d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Release / clean up the wake lock 4557d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (mPartialWakeLock != null) { 4567d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (mPartialWakeLock.isHeld()) { 4577d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon if (DBG) log("- releasing wake lock"); 4587d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon mPartialWakeLock.release(); 4597d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 4607d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon mPartialWakeLock = null; 4617d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 4627d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 4637d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 4647d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon private void startRetryTimer() { 4657d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon removeMessages(RETRY_TIMEOUT); 4667d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon sendEmptyMessageDelayed(RETRY_TIMEOUT, TIME_BETWEEN_RETRIES); 4677d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 4687d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 4697d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon private void cancelRetryTimer() { 4707d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon removeMessages(RETRY_TIMEOUT); 4717d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 4727d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 4737d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon private void registerForServiceStateChanged() { 4747d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Unregister first, just to make sure we never register ourselves 4757d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // twice. (We need this because Phone.registerForServiceStateChanged() 4767d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // does not prevent multiple registration of the same handler.) 4773a2daabaaffe0e056ec930c248b00f1dbac98f8bJake Hamby Phone phone = mCM.getDefaultPhone(); 4783a2daabaaffe0e056ec930c248b00f1dbac98f8bJake Hamby phone.unregisterForServiceStateChanged(this); // Safe even if not currently registered 4793a2daabaaffe0e056ec930c248b00f1dbac98f8bJake Hamby phone.registerForServiceStateChanged(this, SERVICE_STATE_CHANGED, null); 4807d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 4817d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 4827d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon private void unregisterForServiceStateChanged() { 4837d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // This method is safe to call even if we haven't set mPhone yet. 4843a2daabaaffe0e056ec930c248b00f1dbac98f8bJake Hamby Phone phone = mCM.getDefaultPhone(); 4853a2daabaaffe0e056ec930c248b00f1dbac98f8bJake Hamby if (phone != null) { 4863a2daabaaffe0e056ec930c248b00f1dbac98f8bJake Hamby phone.unregisterForServiceStateChanged(this); // Safe even if unnecessary 4877d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 4887d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon removeMessages(SERVICE_STATE_CHANGED); // Clean up any pending messages too 4897d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 4907d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 4917d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon private void registerForDisconnect() { 4927d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Note: no need to unregister first, since 4937d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // CallManager.registerForDisconnect() automatically prevents 4947d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // multiple registration of the same handler. 4957d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon mCM.registerForDisconnect(this, DISCONNECT, null); 4967d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 4977d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 4987d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon private void unregisterForDisconnect() { 4997d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon mCM.unregisterForDisconnect(this); // Safe even if not currently registered 5007d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon removeMessages(DISCONNECT); // Clean up any pending messages too 5017d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 5027d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 5037d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 5047d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // 5057d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // Debugging 5067d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon // 5077d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon 5087d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon private static void log(String msg) { 5097d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon Log.d(TAG, msg); 5107d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon } 5117d4ddf6dc0d7c8158bac3a5dec7936e837e95bddSantos Cordon} 512