13426afaf85d33d454fad8d341a1a895fd7e21c10David Brown/*
23426afaf85d33d454fad8d341a1a895fd7e21c10David Brown * Copyright (C) 2011 The Android Open Source Project
33426afaf85d33d454fad8d341a1a895fd7e21c10David Brown *
43426afaf85d33d454fad8d341a1a895fd7e21c10David Brown * Licensed under the Apache License, Version 2.0 (the "License");
53426afaf85d33d454fad8d341a1a895fd7e21c10David Brown * you may not use this file except in compliance with the License.
63426afaf85d33d454fad8d341a1a895fd7e21c10David Brown * You may obtain a copy of the License at
73426afaf85d33d454fad8d341a1a895fd7e21c10David Brown *
83426afaf85d33d454fad8d341a1a895fd7e21c10David Brown *      http://www.apache.org/licenses/LICENSE-2.0
93426afaf85d33d454fad8d341a1a895fd7e21c10David Brown *
103426afaf85d33d454fad8d341a1a895fd7e21c10David Brown * Unless required by applicable law or agreed to in writing, software
113426afaf85d33d454fad8d341a1a895fd7e21c10David Brown * distributed under the License is distributed on an "AS IS" BASIS,
123426afaf85d33d454fad8d341a1a895fd7e21c10David Brown * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133426afaf85d33d454fad8d341a1a895fd7e21c10David Brown * See the License for the specific language governing permissions and
143426afaf85d33d454fad8d341a1a895fd7e21c10David Brown * limitations under the License.
153426afaf85d33d454fad8d341a1a895fd7e21c10David Brown */
163426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
173426afaf85d33d454fad8d341a1a895fd7e21c10David Brownpackage com.android.phone;
183426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
193426afaf85d33d454fad8d341a1a895fd7e21c10David Brownimport com.android.internal.telephony.CallManager;
203426afaf85d33d454fad8d341a1a895fd7e21c10David Brownimport com.android.internal.telephony.Connection;
213426afaf85d33d454fad8d341a1a895fd7e21c10David Brownimport com.android.internal.telephony.Phone;
223426afaf85d33d454fad8d341a1a895fd7e21c10David Brownimport com.android.phone.Constants.CallStatusCode;
233426afaf85d33d454fad8d341a1a895fd7e21c10David Brownimport com.android.phone.InCallUiState.ProgressIndicationType;
243426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
253426afaf85d33d454fad8d341a1a895fd7e21c10David Brownimport android.content.Context;
263426afaf85d33d454fad8d341a1a895fd7e21c10David Brownimport android.content.Intent;
273426afaf85d33d454fad8d341a1a895fd7e21c10David Brownimport android.os.AsyncResult;
283426afaf85d33d454fad8d341a1a895fd7e21c10David Brownimport android.os.Handler;
293426afaf85d33d454fad8d341a1a895fd7e21c10David Brownimport android.os.Message;
303426afaf85d33d454fad8d341a1a895fd7e21c10David Brownimport android.os.PowerManager;
313426afaf85d33d454fad8d341a1a895fd7e21c10David Brownimport android.provider.Settings;
323426afaf85d33d454fad8d341a1a895fd7e21c10David Brownimport android.telephony.ServiceState;
333426afaf85d33d454fad8d341a1a895fd7e21c10David Brownimport android.util.Log;
343426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
353426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
363426afaf85d33d454fad8d341a1a895fd7e21c10David Brown/**
373426afaf85d33d454fad8d341a1a895fd7e21c10David Brown * Helper class for the {@link CallController} that implements special
383426afaf85d33d454fad8d341a1a895fd7e21c10David Brown * behavior related to emergency calls.  Specifically, this class handles
393426afaf85d33d454fad8d341a1a895fd7e21c10David Brown * the case of the user trying to dial an emergency number while the radio
403426afaf85d33d454fad8d341a1a895fd7e21c10David Brown * is off (i.e. the device is in airplane mode), by forcibly turning the
413426afaf85d33d454fad8d341a1a895fd7e21c10David Brown * radio back on, waiting for it to come up, and then retrying the
423426afaf85d33d454fad8d341a1a895fd7e21c10David Brown * emergency call.
433426afaf85d33d454fad8d341a1a895fd7e21c10David Brown *
443426afaf85d33d454fad8d341a1a895fd7e21c10David Brown * This class is instantiated lazily (the first time the user attempts to
453426afaf85d33d454fad8d341a1a895fd7e21c10David Brown * make an emergency call from airplane mode) by the the
463426afaf85d33d454fad8d341a1a895fd7e21c10David Brown * {@link CallController} singleton.
473426afaf85d33d454fad8d341a1a895fd7e21c10David Brown */
483426afaf85d33d454fad8d341a1a895fd7e21c10David Brownpublic class EmergencyCallHelper extends Handler {
493426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    private static final String TAG = "EmergencyCallHelper";
503426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    private static final boolean DBG = true;
513426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
523426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    // Number of times to retry the call, and time between retry attempts.
533426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    public static final int MAX_NUM_RETRIES = 6;
543426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    public static final long TIME_BETWEEN_RETRIES = 5000;  // msec
553426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
563426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    // Timeout used with our wake lock (just as a safety valve to make
573426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    // sure we don't hold it forever).
583426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    public static final long WAKE_LOCK_TIMEOUT = 5 * 60 * 1000;  // 5 minutes in msec
593426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
603426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    // Handler message codes; see handleMessage()
613426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    private static final int START_SEQUENCE = 1;
623426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    private static final int SERVICE_STATE_CHANGED = 2;
633426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    private static final int DISCONNECT = 3;
643426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    private static final int RETRY_TIMEOUT = 4;
653426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
663426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    private CallController mCallController;
673426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    private PhoneApp mApp;
683426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    private CallManager mCM;
693426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    private Phone mPhone;
703426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    private String mNumber;  // The emergency number we're trying to dial
713426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    private int mNumRetriesSoFar;
723426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
733426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    // Wake lock we hold while running the whole sequence
743426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    private PowerManager.WakeLock mPartialWakeLock;
753426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
763426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    public EmergencyCallHelper(CallController callController) {
773426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        if (DBG) log("EmergencyCallHelper constructor...");
783426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        mCallController = callController;
793426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        mApp = PhoneApp.getInstance();
803426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        mCM =  mApp.mCM;
813426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    }
823426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
833426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    @Override
843426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    public void handleMessage(Message msg) {
853426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        switch (msg.what) {
863426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            case START_SEQUENCE:
873426afaf85d33d454fad8d341a1a895fd7e21c10David Brown                startSequenceInternal(msg);
883426afaf85d33d454fad8d341a1a895fd7e21c10David Brown                break;
893426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            case SERVICE_STATE_CHANGED:
903426afaf85d33d454fad8d341a1a895fd7e21c10David Brown                onServiceStateChanged(msg);
913426afaf85d33d454fad8d341a1a895fd7e21c10David Brown                break;
923426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            case DISCONNECT:
933426afaf85d33d454fad8d341a1a895fd7e21c10David Brown                onDisconnect(msg);
943426afaf85d33d454fad8d341a1a895fd7e21c10David Brown                break;
953426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            case RETRY_TIMEOUT:
963426afaf85d33d454fad8d341a1a895fd7e21c10David Brown                onRetryTimeout();
973426afaf85d33d454fad8d341a1a895fd7e21c10David Brown                break;
983426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            default:
993426afaf85d33d454fad8d341a1a895fd7e21c10David Brown                Log.wtf(TAG, "handleMessage: unexpected message: " + msg);
1003426afaf85d33d454fad8d341a1a895fd7e21c10David Brown                break;
1013426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        }
1023426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    }
1033426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
1043426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    /**
1053426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * Starts the "emergency call from airplane mode" sequence.
1063426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     *
1073426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * This is the (single) external API of the EmergencyCallHelper class.
1083426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * This method is called from the CallController placeCall() sequence
1093426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * if the user dials a valid emergency number, but the radio is
1103426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * powered-off (presumably due to airplane mode.)
1113426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     *
1123426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * This method kicks off the following sequence:
1133426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * - Power on the radio
1143426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * - Listen for the service state change event telling us the radio has come up
1153426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * - Then launch the emergency call
1163426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * - Retry if the call fails with an OUT_OF_SERVICE error
1173426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * - Retry if we've gone 5 seconds without any response from the radio
1183426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * - Finally, clean up any leftover state (progress UI, wake locks, etc.)
1193426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     *
1203426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * This method is safe to call from any thread, since it simply posts
1213426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * a message to the EmergencyCallHelper's handler (thus ensuring that
1223426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * the rest of the sequence is entirely serialized, and runs only on
1233426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * the handler thread.)
1243426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     *
1253426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * This method does *not* force the in-call UI to come up; our caller
1263426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * is responsible for doing that (presumably by calling
1273426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * PhoneApp.displayCallScreen().)
1283426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     */
1293426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    public void startEmergencyCallFromAirplaneModeSequence(String number) {
1303426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        if (DBG) log("startEmergencyCallFromAirplaneModeSequence('" + number + "')...");
1313426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        Message msg = obtainMessage(START_SEQUENCE, number);
1323426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        sendMessage(msg);
1333426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    }
1343426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
1353426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    /**
1363426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * Actual implementation of startEmergencyCallFromAirplaneModeSequence(),
1373426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * guaranteed to run on the handler thread.
1383426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * @see startEmergencyCallFromAirplaneModeSequence()
1393426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     */
1403426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    private void startSequenceInternal(Message msg) {
1413426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        if (DBG) log("startSequenceInternal(): msg = " + msg);
1423426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
1433426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // First of all, clean up any state (including mPartialWakeLock!)
1443426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // left over from a prior emergency call sequence.
1453426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // This ensures that we'll behave sanely if another
1463426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // startEmergencyCallFromAirplaneModeSequence() comes in while
1473426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // we're already in the middle of the sequence.
1483426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        cleanup();
1493426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
1503426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        mNumber = (String) msg.obj;
1513426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        if (DBG) log("- startSequenceInternal: Got mNumber: '" + mNumber + "'");
1523426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
1533426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        mNumRetriesSoFar = 0;
1543426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
1553426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // Reset mPhone to whatever the current default phone is right now.
1563426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        mPhone = mApp.mCM.getDefaultPhone();
1573426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
1583426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // Wake lock to make sure the processor doesn't go to sleep midway
1593426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // through the emergency call sequence.
1603426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        PowerManager pm = (PowerManager) mApp.getSystemService(Context.POWER_SERVICE);
1613426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
1623426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // Acquire with a timeout, just to be sure we won't hold the wake
1633426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // lock forever even if a logic bug (in this class) causes us to
1643426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // somehow never call cleanup().
1653426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        if (DBG) log("- startSequenceInternal: acquiring wake lock");
1663426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        mPartialWakeLock.acquire(WAKE_LOCK_TIMEOUT);
1673426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
1683426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // No need to check the current service state here, since the only
1693426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // reason the CallController would call this method in the first
1703426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // place is if the radio is powered-off.
1713426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        //
1723426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // So just go ahead and turn the radio on.
1733426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
1743426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        powerOnRadio();  // We'll get an onServiceStateChanged() callback
1753426afaf85d33d454fad8d341a1a895fd7e21c10David Brown                         // when the radio successfully comes up.
1763426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
1773426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // Next step: when the SERVICE_STATE_CHANGED event comes in,
1783426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // we'll retry the call; see placeEmergencyCall();
1793426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // But also, just in case, start a timer to make sure we'll retry
1803426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // the call even if the SERVICE_STATE_CHANGED event never comes in
1813426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // for some reason.
1823426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        startRetryTimer();
1833426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
1843426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // And finally, let the in-call UI know that we need to
1853426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // display the "Turning on radio..." progress indication.
1863426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        mApp.inCallUiState.setProgressIndication(ProgressIndicationType.TURNING_ON_RADIO);
1873426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
1883426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // (Our caller is responsible for calling mApp.displayCallScreen().)
1893426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    }
1903426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
1913426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    /**
1923426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * Handles the SERVICE_STATE_CHANGED event.
1933426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     *
1943426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * (Normally this event tells us that the radio has finally come
1953426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * up.  In that case, it's now safe to actually place the
1963426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * emergency call.)
1973426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     */
1983426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    private void onServiceStateChanged(Message msg) {
1993426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        ServiceState state = (ServiceState) ((AsyncResult) msg.obj).result;
2003426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        if (DBG) log("onServiceStateChanged()...  new state = " + state);
2013426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
2023426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // Possible service states:
2033426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // - STATE_IN_SERVICE        // Normal operation
2043426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // - STATE_OUT_OF_SERVICE    // Still searching for an operator to register to,
2053426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        //                           // or no radio signal
2063426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // - STATE_EMERGENCY_ONLY    // Phone is locked; only emergency numbers are allowed
2073426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // - STATE_POWER_OFF         // Radio is explicitly powered off (airplane mode)
2083426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
2093426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // Once we reach either STATE_IN_SERVICE or STATE_EMERGENCY_ONLY,
2103426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // it's finally OK to place the emergency call.
2113426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        boolean okToCall = (state.getState() == ServiceState.STATE_IN_SERVICE)
2123426afaf85d33d454fad8d341a1a895fd7e21c10David Brown                || (state.getState() == ServiceState.STATE_EMERGENCY_ONLY);
2133426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
2143426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        if (okToCall) {
2153426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // Woo hoo!  It's OK to actually place the call.
2163426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            if (DBG) log("onServiceStateChanged: ok to call!");
2173426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
2183426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // Deregister for the service state change events.
2193426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            unregisterForServiceStateChanged();
2203426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
2213426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // Take down the "Turning on radio..." indication.
2223426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            mApp.inCallUiState.clearProgressIndication();
2233426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
2243426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            placeEmergencyCall();
2253426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
2263426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // The in-call UI is probably still up at this point,
2273426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // but make sure of that:
2283426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            mApp.displayCallScreen();
2293426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        } else {
2303426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // The service state changed, but we're still not ready to call yet.
2313426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // (This probably was the transition from STATE_POWER_OFF to
2323426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // STATE_OUT_OF_SERVICE, which happens immediately after powering-on
2333426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // the radio.)
2343426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            //
2353426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // So just keep waiting; we'll probably get to either
2363426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // STATE_IN_SERVICE or STATE_EMERGENCY_ONLY very shortly.
2373426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // (Or even if that doesn't happen, we'll at least do another retry
2383426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // when the RETRY_TIMEOUT event fires.)
2393426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            if (DBG) log("onServiceStateChanged: not ready to call yet, keep waiting...");
2403426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        }
2413426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    }
2423426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
2433426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    /**
2443426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * Handles a DISCONNECT event from the telephony layer.
2453426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     *
2463426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * Even after we successfully place an emergency call (after powering
2473426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * on the radio), it's still possible for the call to fail with the
2483426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * disconnect cause OUT_OF_SERVICE.  If so, schedule a retry.
2493426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     */
2503426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    private void onDisconnect(Message msg) {
2513426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        Connection conn = (Connection) ((AsyncResult) msg.obj).result;
2523426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        Connection.DisconnectCause cause = conn.getDisconnectCause();
2533426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        if (DBG) log("onDisconnect: connection '" + conn
2543426afaf85d33d454fad8d341a1a895fd7e21c10David Brown                     + "', addr '" + conn.getAddress() + "', cause = " + cause);
2553426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
2563426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        if (cause == Connection.DisconnectCause.OUT_OF_SERVICE) {
2573426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // Wait a bit more and try again (or just bail out totally if
2583426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // we've had too many failures.)
2593426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            if (DBG) log("- onDisconnect: OUT_OF_SERVICE, need to retry...");
2603426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            scheduleRetryOrBailOut();
2613426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        } else {
2623426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // Any other disconnect cause means we're done.
2633426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // Either the emergency call succeeded *and* ended normally,
2643426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // or else there was some error that we can't retry.  In either
2653426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // case, just clean up our internal state.)
2663426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
2673426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            if (DBG) log("==> Disconnect event; clean up...");
2683426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            cleanup();
2693426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
2703426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // Nothing else to do here.  If the InCallScreen was visible,
2713426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // it would have received this disconnect event too (so it'll
2723426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // show the "Call ended" state and finish itself without any
2733426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // help from us.)
2743426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        }
2753426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    }
2763426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
2773426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    /**
2783426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * Handles the retry timer expiring.
2793426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     */
2803426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    private void onRetryTimeout() {
2813426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        Phone.State phoneState = mCM.getState();
2823426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        int serviceState = mPhone.getServiceState().getState();
2833426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        if (DBG) log("onRetryTimeout():  phone state " + phoneState
2843426afaf85d33d454fad8d341a1a895fd7e21c10David Brown                     + ", service state " + serviceState
2853426afaf85d33d454fad8d341a1a895fd7e21c10David Brown                     + ", mNumRetriesSoFar = " + mNumRetriesSoFar);
2863426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
2873426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // - If we're actually in a call, we've succeeded.
2883426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        //
2893426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // - Otherwise, if the radio is now on, that means we successfully got
2903426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        //   out of airplane mode but somehow didn't get the service state
2913426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        //   change event.  In that case, try to place the call.
2923426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        //
2933426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // - If the radio is still powered off, try powering it on again.
2943426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
2953426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        if (phoneState == Phone.State.OFFHOOK) {
2963426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            if (DBG) log("- onRetryTimeout: Call is active!  Cleaning up...");
2973426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            cleanup();
2983426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            return;
2993426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        }
3003426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
3013426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        if (serviceState != ServiceState.STATE_POWER_OFF) {
3023426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // Woo hoo -- we successfully got out of airplane mode.
3033426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
3043426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // Deregister for the service state change events; we don't need
3053426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // these any more now that the radio is powered-on.
3063426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            unregisterForServiceStateChanged();
3073426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
3083426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // Take down the "Turning on radio..." indication.
3093426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            mApp.inCallUiState.clearProgressIndication();
3103426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
3113426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            placeEmergencyCall();  // If the call fails, placeEmergencyCall()
3123426afaf85d33d454fad8d341a1a895fd7e21c10David Brown                                   // will schedule a retry.
3133426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        } else {
3143426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // Uh oh; we've waited the full TIME_BETWEEN_RETRIES and the
3153426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // radio is still not powered-on.  Try again...
3163426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
3173426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            if (DBG) log("- Trying (again) to turn on the radio...");
3183426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            powerOnRadio();  // Again, we'll (hopefully) get an onServiceStateChanged()
3193426afaf85d33d454fad8d341a1a895fd7e21c10David Brown                             // callback when the radio successfully comes up.
3203426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
3213426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // ...and also set a fresh retry timer (or just bail out
3223426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // totally if we've had too many failures.)
3233426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            scheduleRetryOrBailOut();
3243426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        }
3253426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
3263426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // Finally, the in-call UI is probably still up at this point,
3273426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // but make sure of that:
3283426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        mApp.displayCallScreen();
3293426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    }
3303426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
3313426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    /**
3323426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * Attempt to power on the radio (i.e. take the device out
3333426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * of airplane mode.)
3343426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     *
3353426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * Additionally, start listening for service state changes;
3363426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * we'll eventually get an onServiceStateChanged() callback
3373426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * when the radio successfully comes up.
3383426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     */
3393426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    private void powerOnRadio() {
3403426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        if (DBG) log("- powerOnRadio()...");
3413426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
3423426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // We're about to turn on the radio, so arrange to be notified
3433426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // when the sequence is complete.
3443426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        registerForServiceStateChanged();
3453426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
3463426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // If airplane mode is on, we turn it off the same way that the
3473426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // Settings activity turns it off.
3483426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        if (Settings.System.getInt(mApp.getContentResolver(),
3493426afaf85d33d454fad8d341a1a895fd7e21c10David Brown                                   Settings.System.AIRPLANE_MODE_ON, 0) > 0) {
3503426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            if (DBG) log("==> Turning off airplane mode...");
3513426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
3523426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // Change the system setting
3533426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            Settings.System.putInt(mApp.getContentResolver(),
3543426afaf85d33d454fad8d341a1a895fd7e21c10David Brown                                   Settings.System.AIRPLANE_MODE_ON, 0);
3553426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
3563426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // Post the intent
3573426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
3583426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            intent.putExtra("state", false);
3593426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            mApp.sendBroadcast(intent);
3603426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        } else {
3613426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // Otherwise, for some strange reason the radio is off
3623426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // (even though the Settings database doesn't think we're
3633426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // in airplane mode.)  In this case just turn the radio
3643426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // back on.
3653426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            if (DBG) log("==> (Apparently) not in airplane mode; manually powering radio on...");
3663426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            mPhone.setRadioPower(true);
3673426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        }
3683426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    }
3693426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
3703426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    /**
3713426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * Actually initiate the outgoing emergency call.
3723426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * (We do this once the radio has successfully been powered-up.)
3733426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     *
3743426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * If the call succeeds, we're done.
3753426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * If the call fails, schedule a retry of the whole sequence.
3763426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     */
3773426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    private void placeEmergencyCall() {
3783426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        if (DBG) log("placeEmergencyCall()...");
3793426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
3803426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // Place an outgoing call to mNumber.
3813426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // Note we call PhoneUtils.placeCall() directly; we don't want any
3823426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // of the behavior from CallController.placeCallInternal() here.
3833426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // (Specifically, we don't want to start the "emergency call from
3843426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // airplane mode" sequence from the beginning again!)
3853426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
3863426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        registerForDisconnect();  // Get notified when this call disconnects
3873426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
3883426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        if (DBG) log("- placing call to '" + mNumber + "'...");
3893426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        int callStatus = PhoneUtils.placeCall(mApp,
3903426afaf85d33d454fad8d341a1a895fd7e21c10David Brown                                              mPhone,
3913426afaf85d33d454fad8d341a1a895fd7e21c10David Brown                                              mNumber,
3923426afaf85d33d454fad8d341a1a895fd7e21c10David Brown                                              null,  // contactUri
3933426afaf85d33d454fad8d341a1a895fd7e21c10David Brown                                              true,  // isEmergencyCall
3943426afaf85d33d454fad8d341a1a895fd7e21c10David Brown                                              null);  // gatewayUri
3953426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        if (DBG) log("- PhoneUtils.placeCall() returned status = " + callStatus);
3963426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
3973426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        boolean success;
3983426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // Note PhoneUtils.placeCall() returns one of the CALL_STATUS_*
3993426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // constants, not a CallStatusCode enum value.
4003426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        switch (callStatus) {
4013426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            case PhoneUtils.CALL_STATUS_DIALED:
4023426afaf85d33d454fad8d341a1a895fd7e21c10David Brown                success = true;
4033426afaf85d33d454fad8d341a1a895fd7e21c10David Brown                break;
4043426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
4053426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            case PhoneUtils.CALL_STATUS_DIALED_MMI:
4063426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            case PhoneUtils.CALL_STATUS_FAILED:
4073426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            default:
4083426afaf85d33d454fad8d341a1a895fd7e21c10David Brown                // Anything else is a failure, and we'll need to retry.
4093426afaf85d33d454fad8d341a1a895fd7e21c10David Brown                Log.w(TAG, "placeEmergencyCall(): placeCall() failed: callStatus = " + callStatus);
4103426afaf85d33d454fad8d341a1a895fd7e21c10David Brown                success = false;
4113426afaf85d33d454fad8d341a1a895fd7e21c10David Brown                break;
4123426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        }
4133426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
4143426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        if (success) {
4153426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            if (DBG) log("==> Success from PhoneUtils.placeCall()!");
4163426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // Ok, the emergency call is (hopefully) under way.
4173426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
4183426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // We're not done yet, though, so don't call cleanup() here.
4193426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // (It's still possible that this call will fail, and disconnect
4203426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // with cause==OUT_OF_SERVICE.  If so, that will trigger a retry
4213426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // from the onDisconnect() method.)
4223426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        } else {
4233426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            if (DBG) log("==> Failure.");
4243426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // Wait a bit more and try again (or just bail out totally if
4253426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // we've had too many failures.)
4263426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            scheduleRetryOrBailOut();
4273426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        }
4283426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    }
4293426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
4303426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    /**
4313426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * Schedules a retry in response to some failure (either the radio
4323426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * failing to power on, or a failure when trying to place the call.)
4333426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * Or, if we've hit the retry limit, bail out of this whole sequence
4343426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * and display a failure message to the user.
4353426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     */
4363426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    private void scheduleRetryOrBailOut() {
4373426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        mNumRetriesSoFar++;
4383426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        if (DBG) log("scheduleRetryOrBailOut()...  mNumRetriesSoFar is now " + mNumRetriesSoFar);
4393426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
4403426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        if (mNumRetriesSoFar > MAX_NUM_RETRIES) {
4413426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            Log.w(TAG, "scheduleRetryOrBailOut: hit MAX_NUM_RETRIES; giving up...");
4423426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            cleanup();
4433426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // ...and have the InCallScreen display a generic failure
4443426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            // message.
4453426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            mApp.inCallUiState.setPendingCallStatusCode(CallStatusCode.CALL_FAILED);
4463426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        } else {
4473426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            if (DBG) log("- Scheduling another retry...");
4483426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            startRetryTimer();
4493426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            mApp.inCallUiState.setProgressIndication(ProgressIndicationType.RETRYING);
4503426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        }
4513426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    }
4523426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
4533426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    /**
4543426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * Clean up when done with the whole sequence: either after
4553426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * successfully placing *and* ending the emergency call, or after
4563426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * bailing out because of too many failures.
4573426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     *
4583426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * The exact cleanup steps are:
4593426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * - Take down any progress UI (and also ask the in-call UI to refresh itself,
4603426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     *   if it's still visible)
4613426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * - Double-check that we're not still registered for any telephony events
4623426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * - Clean up any extraneous handler messages (like retry timeouts) still in the queue
4633426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * - Make sure we're not still holding any wake locks
4643426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     *
4653426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * Basically this method guarantees that there will be no more
4663426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * activity from the EmergencyCallHelper until the CallController
4673426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * kicks off the whole sequence again with another call to
4683426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * startEmergencyCallFromAirplaneModeSequence().
4693426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     *
4703426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * Note we don't call this method simply after a successful call to
4713426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * placeCall(), since it's still possible the call will disconnect
4723426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     * very quickly with an OUT_OF_SERVICE error.
4733426afaf85d33d454fad8d341a1a895fd7e21c10David Brown     */
4743426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    private void cleanup() {
4753426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        if (DBG) log("cleanup()...");
4763426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
4773426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // Take down the "Turning on radio..." indication.
4783426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        mApp.inCallUiState.clearProgressIndication();
4793426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
4803426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        unregisterForServiceStateChanged();
4813426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        unregisterForDisconnect();
4823426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        cancelRetryTimer();
4833426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
4843426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // Release / clean up the wake lock
4853426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        if (mPartialWakeLock != null) {
4863426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            if (mPartialWakeLock.isHeld()) {
4873426afaf85d33d454fad8d341a1a895fd7e21c10David Brown                if (DBG) log("- releasing wake lock");
4883426afaf85d33d454fad8d341a1a895fd7e21c10David Brown                mPartialWakeLock.release();
4893426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            }
4903426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            mPartialWakeLock = null;
4913426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        }
4923426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
4933426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // And finally, ask the in-call UI to refresh itself (to clean up the
4943426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // progress indication if necessary), if it's currently visible.
4953426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        mApp.updateInCallScreen();
4963426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    }
4973426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
4983426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    private void startRetryTimer() {
4993426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        removeMessages(RETRY_TIMEOUT);
5003426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        sendEmptyMessageDelayed(RETRY_TIMEOUT, TIME_BETWEEN_RETRIES);
5013426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    }
5023426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
5033426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    private void cancelRetryTimer() {
5043426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        removeMessages(RETRY_TIMEOUT);
5053426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    }
5063426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
5073426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    private void registerForServiceStateChanged() {
5083426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // Unregister first, just to make sure we never register ourselves
5093426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // twice.  (We need this because Phone.registerForServiceStateChanged()
5103426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // does not prevent multiple registration of the same handler.)
5113426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        mPhone.unregisterForServiceStateChanged(this);  // Safe even if not currently registered
5123426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        mPhone.registerForServiceStateChanged(this, SERVICE_STATE_CHANGED, null);
5133426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    }
5143426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
5153426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    private void unregisterForServiceStateChanged() {
5163426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // This method is safe to call even if we haven't set mPhone yet.
5173426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        if (mPhone != null) {
5183426afaf85d33d454fad8d341a1a895fd7e21c10David Brown            mPhone.unregisterForServiceStateChanged(this);  // Safe even if unnecessary
5193426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        }
5203426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        removeMessages(SERVICE_STATE_CHANGED);  // Clean up any pending messages too
5213426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    }
5223426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
5233426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    private void registerForDisconnect() {
5243426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // Note: no need to unregister first, since
5253426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // CallManager.registerForDisconnect() automatically prevents
5263426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        // multiple registration of the same handler.
5273426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        mCM.registerForDisconnect(this, DISCONNECT, null);
5283426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    }
5293426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
5303426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    private void unregisterForDisconnect() {
5313426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        mCM.unregisterForDisconnect(this);  // Safe even if not currently registered
5323426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        removeMessages(DISCONNECT);  // Clean up any pending messages too
5333426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    }
5343426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
5353426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
5363426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    //
5373426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    // Debugging
5383426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    //
5393426afaf85d33d454fad8d341a1a895fd7e21c10David Brown
5403426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    private static void log(String msg) {
5413426afaf85d33d454fad8d341a1a895fd7e21c10David Brown        Log.d(TAG, msg);
5423426afaf85d33d454fad8d341a1a895fd7e21c10David Brown    }
5433426afaf85d33d454fad8d341a1a895fd7e21c10David Brown}
544