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