1e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon/*
2e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon * Copyright (C) 2014 The Android Open Source Project
3e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon *
4e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon * Licensed under the Apache License, Version 2.0 (the "License");
5e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon * you may not use this file except in compliance with the License.
6e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon * You may obtain a copy of the License at
7e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon *
8e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon *      http://www.apache.org/licenses/LICENSE-2.0
9e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon *
10e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon * Unless required by applicable law or agreed to in writing, software
11e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon * distributed under the License is distributed on an "AS IS" BASIS,
12e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon * See the License for the specific language governing permissions and
14e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon * limitations under the License.
15e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon */
16e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
17e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordonpackage com.android.services.telephony;
18e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
19e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordonimport android.content.Context;
20e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
21e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordonimport android.content.Intent;
22e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordonimport android.os.AsyncResult;
23e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordonimport android.os.Handler;
24e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordonimport android.os.Message;
25e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordonimport android.os.UserHandle;
26e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordonimport android.provider.Settings;
27e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordonimport android.telephony.ServiceState;
28e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
29e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordonimport com.android.internal.os.SomeArgs;
30e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordonimport com.android.internal.telephony.Phone;
31e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordonimport com.android.internal.telephony.PhoneConstants;
32e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
33e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon/**
34e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon * Helper class that implements special behavior related to emergency calls. Specifically, this
35e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon * class handles the case of the user trying to dial an emergency number while the radio is off
36e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon * (i.e. the device is in airplane mode), by forcibly turning the radio back on, waiting for it to
37e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon * come up, and then retrying the emergency call.
38e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon */
39e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordonpublic class EmergencyCallHelper {
40e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
41e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    /**
42e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon     * Receives the result of the EmergencyCallHelper's attempt to turn on the radio.
43e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon     */
44e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    interface Callback {
45e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        void onComplete(boolean isRadioReady);
46e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    }
47e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
48e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    // Number of times to retry the call, and time between retry attempts.
49b7795cf75d54752c7d0d1fd59870d5c3d5fe662eSantos Cordon    public static final int MAX_NUM_RETRIES = 5;
50e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    public static final long TIME_BETWEEN_RETRIES_MILLIS = 5000;  // msec
51e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
52e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    // Handler message codes; see handleMessage()
53e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    private static final int MSG_START_SEQUENCE = 1;
54e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    private static final int MSG_SERVICE_STATE_CHANGED = 2;
55e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    private static final int MSG_RETRY_TIMEOUT = 3;
56e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
57e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    private final Context mContext;
58e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
59e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    private final Handler mHandler = new Handler() {
60e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        @Override
61e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        public void handleMessage(Message msg) {
62e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon            switch (msg.what) {
63e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon                case MSG_START_SEQUENCE:
64e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon                    SomeArgs args = (SomeArgs) msg.obj;
65e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon                    Phone phone = (Phone) args.arg1;
66e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon                    EmergencyCallHelper.Callback callback =
67e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon                            (EmergencyCallHelper.Callback) args.arg2;
68e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon                    args.recycle();
69e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
70e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon                    startSequenceInternal(phone, callback);
71e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon                    break;
72e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon                case MSG_SERVICE_STATE_CHANGED:
73e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon                    onServiceStateChanged((ServiceState) ((AsyncResult) msg.obj).result);
74e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon                    break;
75e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon                case MSG_RETRY_TIMEOUT:
76e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon                    onRetryTimeout();
77e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon                    break;
78e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon                default:
79e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon                    Log.wtf(this, "handleMessage: unexpected message: %d.", msg.what);
80e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon                    break;
81e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon            }
82e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        }
83e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    };
84e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
85e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
86e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    private Callback mCallback;  // The callback to notify upon completion.
87e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    private Phone mPhone;  // The phone that will attempt to place the call.
88e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    private int mNumRetriesSoFar;
89e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
90e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    public EmergencyCallHelper(Context context) {
91e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        Log.d(this, "EmergencyCallHelper constructor.");
92e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        mContext = context;
93e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    }
94e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
95e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    /**
96e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon     * Starts the "turn on radio" sequence. This is the (single) external API of the
97e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon     * EmergencyCallHelper class.
98e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon     *
99e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon     * This method kicks off the following sequence:
100e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon     * - Power on the radio.
101e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon     * - Listen for the service state change event telling us the radio has come up.
102e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon     * - Retry if we've gone {@link #TIME_BETWEEN_RETRIES_MILLIS} without any response from the
103e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon     *   radio.
104e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon     * - Finally, clean up any leftover state.
105e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon     *
106e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon     * This method is safe to call from any thread, since it simply posts a message to the
107e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon     * EmergencyCallHelper's handler (thus ensuring that the rest of the sequence is entirely
108e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon     * serialized, and runs only on the handler thread.)
109e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon     */
110e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    public void startTurnOnRadioSequence(Phone phone, Callback callback) {
111e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        Log.d(this, "startTurnOnRadioSequence");
112e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
113e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        SomeArgs args = SomeArgs.obtain();
114e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        args.arg1 = phone;
115e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        args.arg2 = callback;
116e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        mHandler.obtainMessage(MSG_START_SEQUENCE, args).sendToTarget();
117e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    }
118e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
119e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    /**
120e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon     * Actual implementation of startTurnOnRadioSequence(), guaranteed to run on the handler thread.
121e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon     * @see #startTurnOnRadioSequence
122e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon     */
123e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    private void startSequenceInternal(Phone phone, Callback callback) {
124e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        Log.d(this, "startSequenceInternal()");
125e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
126e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        // First of all, clean up any state left over from a prior emergency call sequence. This
127e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        // ensures that we'll behave sanely if another startTurnOnRadioSequence() comes in while
128e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        // we're already in the middle of the sequence.
129e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        cleanup();
130e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
131e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        mPhone = phone;
132e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        mCallback = callback;
133e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
134e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
135e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        // No need to check the current service state here, since the only reason to invoke this
136e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        // method in the first place is if the radio is powered-off. So just go ahead and turn the
137e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        // radio on.
138e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
139e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        powerOnRadio();  // We'll get an onServiceStateChanged() callback
140e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon                         // when the radio successfully comes up.
141e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
142e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        // Next step: when the SERVICE_STATE_CHANGED event comes in, we'll retry the call; see
143e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        // onServiceStateChanged(). But also, just in case, start a timer to make sure we'll retry
144e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        // the call even if the SERVICE_STATE_CHANGED event never comes in for some reason.
145e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        startRetryTimer();
146e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    }
147e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
148e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    /**
149e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon     * Handles the SERVICE_STATE_CHANGED event. Normally this event tells us that the radio has
150e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon     * finally come up. In that case, it's now safe to actually place the emergency call.
151e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon     */
152e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    private void onServiceStateChanged(ServiceState state) {
153e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        Log.d(this, "onServiceStateChanged(), new state = %s.", state);
154e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
155e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        // Possible service states:
156e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        // - STATE_IN_SERVICE        // Normal operation
157e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        // - STATE_OUT_OF_SERVICE    // Still searching for an operator to register to,
158e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        //                           // or no radio signal
159e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        // - STATE_EMERGENCY_ONLY    // Phone is locked; only emergency numbers are allowed
160e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        // - STATE_POWER_OFF         // Radio is explicitly powered off (airplane mode)
161e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
162e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        if (isOkToCall(state.getState(), mPhone.getState())) {
163e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon            // Woo hoo!  It's OK to actually place the call.
164e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon            Log.d(this, "onServiceStateChanged: ok to call!");
165e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
166e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon            onComplete(true);
167e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon            cleanup();
168e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        } else {
169e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon            // The service state changed, but we're still not ready to call yet. (This probably was
170e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon            // the transition from STATE_POWER_OFF to STATE_OUT_OF_SERVICE, which happens
171e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon            // immediately after powering-on the radio.)
172e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon            //
173e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon            // So just keep waiting; we'll probably get to either STATE_IN_SERVICE or
174e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon            // STATE_EMERGENCY_ONLY very shortly. (Or even if that doesn't happen, we'll at least do
175e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon            // another retry when the RETRY_TIMEOUT event fires.)
176e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon            Log.d(this, "onServiceStateChanged: not ready to call yet, keep waiting.");
177e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        }
178e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    }
179e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
180e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    private boolean isOkToCall(int serviceState, PhoneConstants.State phoneState) {
181e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        // Once we reach either STATE_IN_SERVICE or STATE_EMERGENCY_ONLY, it's finally OK to place
182e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        // the emergency call.
183b7795cf75d54752c7d0d1fd59870d5c3d5fe662eSantos Cordon        return ((phoneState == PhoneConstants.State.OFFHOOK)
184e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon                || (serviceState == ServiceState.STATE_IN_SERVICE)
185b7795cf75d54752c7d0d1fd59870d5c3d5fe662eSantos Cordon                || (serviceState == ServiceState.STATE_EMERGENCY_ONLY)) ||
186b7795cf75d54752c7d0d1fd59870d5c3d5fe662eSantos Cordon
187b7795cf75d54752c7d0d1fd59870d5c3d5fe662eSantos Cordon                // Allow STATE_OUT_OF_SERVICE if we are at the max number of retries.
188b7795cf75d54752c7d0d1fd59870d5c3d5fe662eSantos Cordon                (mNumRetriesSoFar == MAX_NUM_RETRIES &&
189b7795cf75d54752c7d0d1fd59870d5c3d5fe662eSantos Cordon                 serviceState == ServiceState.STATE_OUT_OF_SERVICE);
190e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    }
191e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
192e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    /**
193e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon     * Handles the retry timer expiring.
194e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon     */
195e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    private void onRetryTimeout() {
196e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        PhoneConstants.State phoneState = mPhone.getState();
197e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        int serviceState = mPhone.getServiceState().getState();
198e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        Log.d(this, "onRetryTimeout():  phone state = %s, service state = %d, retries = %d.",
199e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon               phoneState, serviceState, mNumRetriesSoFar);
200e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
201e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        // - If we're actually in a call, we've succeeded.
202e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        // - Otherwise, if the radio is now on, that means we successfully got out of airplane mode
203e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        //   but somehow didn't get the service state change event.  In that case, try to place the
204e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        //   call.
205e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        // - If the radio is still powered off, try powering it on again.
206e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
207e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        if (isOkToCall(serviceState, phoneState)) {
208e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon            Log.d(this, "onRetryTimeout: Radio is on. Cleaning up.");
209e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
210e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon            // Woo hoo -- we successfully got out of airplane mode.
211e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon            onComplete(true);
212e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon            cleanup();
213e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        } else {
214e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon            // Uh oh; we've waited the full TIME_BETWEEN_RETRIES_MILLIS and the radio is still not
215e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon            // powered-on.  Try again.
216e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
217e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon            mNumRetriesSoFar++;
218e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon            Log.d(this, "mNumRetriesSoFar is now " + mNumRetriesSoFar);
219e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
220e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon            if (mNumRetriesSoFar > MAX_NUM_RETRIES) {
221e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon                Log.w(this, "Hit MAX_NUM_RETRIES; giving up.");
222e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon                cleanup();
223e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon            } else {
224e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon                Log.d(this, "Trying (again) to turn on the radio.");
225e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon                powerOnRadio();  // Again, we'll (hopefully) get an onServiceStateChanged() callback
226e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon                                 // when the radio successfully comes up.
227e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon                startRetryTimer();
228e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon            }
229e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        }
230e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    }
231e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
232e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    /**
233e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon     * Attempt to power on the radio (i.e. take the device out of airplane mode.)
234e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon     * Additionally, start listening for service state changes; we'll eventually get an
235e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon     * onServiceStateChanged() callback when the radio successfully comes up.
236e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon     */
237e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    private void powerOnRadio() {
238e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        Log.d(this, "powerOnRadio().");
239e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
240e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        // We're about to turn on the radio, so arrange to be notified when the sequence is
241e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        // complete.
242e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        registerForServiceStateChanged();
243e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
244e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        // If airplane mode is on, we turn it off the same way that the Settings activity turns it
245e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        // off.
246e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        if (Settings.Global.getInt(mContext.getContentResolver(),
247e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon                                   Settings.Global.AIRPLANE_MODE_ON, 0) > 0) {
248e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon            Log.d(this, "==> Turning off airplane mode.");
249e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
250e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon            // Change the system setting
251e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon            Settings.Global.putInt(mContext.getContentResolver(),
252e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon                                   Settings.Global.AIRPLANE_MODE_ON, 0);
253e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
254e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon            // Post the broadcast intend for change in airplane mode
255da120f4e3d32ca97c5b4c21d6c505d834a29ab8dSantos Cordon            // TODO: We really should not be in charge of sending this broadcast.
256e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon            //     If changing the setting is sufficent to trigger all of the rest of the logic,
257e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon            //     then that should also trigger the broadcast intent.
258e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon            Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
259e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon            intent.putExtra("state", false);
260e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon            mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
261e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        } else {
262e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon            // Otherwise, for some strange reason the radio is off (even though the Settings
263e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon            // database doesn't think we're in airplane mode.)  In this case just turn the radio
264e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon            // back on.
265e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon            Log.d(this, "==> (Apparently) not in airplane mode; manually powering radio on.");
266e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon            mPhone.setRadioPower(true);
267e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        }
268e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    }
269e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
270e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    /**
271e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon     * Clean up when done with the whole sequence: either after successfully turning on the radio,
272e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon     * or after bailing out because of too many failures.
273e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon     *
274e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon     * The exact cleanup steps are:
275e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon     * - Notify callback if we still hadn't sent it a response.
276e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon     * - Double-check that we're not still registered for any telephony events
277e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon     * - Clean up any extraneous handler messages (like retry timeouts) still in the queue
278e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon     *
279e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon     * Basically this method guarantees that there will be no more activity from the
280e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon     * EmergencyCallHelper until someone kicks off the whole sequence again with another call to
281e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon     * {@link #startTurnOnRadioSequence}
282e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon     *
283da120f4e3d32ca97c5b4c21d6c505d834a29ab8dSantos Cordon     * TODO: Do the work for the comment below:
284e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon     * Note we don't call this method simply after a successful call to placeCall(), since it's
285e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon     * still possible the call will disconnect very quickly with an OUT_OF_SERVICE error.
286e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon     */
287e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    private void cleanup() {
288e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        Log.d(this, "cleanup()");
289e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
290e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        // This will send a failure call back if callback has yet to be invoked.  If the callback
291e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        // was already invoked, it's a no-op.
292e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        onComplete(false);
293e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
294e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        unregisterForServiceStateChanged();
295e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        cancelRetryTimer();
296e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
297e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        // Used for unregisterForServiceStateChanged() so we null it out here instead.
298e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        mPhone = null;
299e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        mNumRetriesSoFar = 0;
300e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    }
301e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
302e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    private void startRetryTimer() {
303e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        cancelRetryTimer();
304e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        mHandler.sendEmptyMessageDelayed(MSG_RETRY_TIMEOUT, TIME_BETWEEN_RETRIES_MILLIS);
305e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    }
306e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
307e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    private void cancelRetryTimer() {
308e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        mHandler.removeMessages(MSG_RETRY_TIMEOUT);
309e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    }
310e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
311e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    private void registerForServiceStateChanged() {
312e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        // Unregister first, just to make sure we never register ourselves twice.  (We need this
313e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        // because Phone.registerForServiceStateChanged() does not prevent multiple registration of
314e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        // the same handler.)
315e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        unregisterForServiceStateChanged();
316e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        mPhone.registerForServiceStateChanged(mHandler, MSG_SERVICE_STATE_CHANGED, null);
317e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    }
318e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
319e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    private void unregisterForServiceStateChanged() {
320e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        // This method is safe to call even if we haven't set mPhone yet.
321e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        if (mPhone != null) {
322e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon            mPhone.unregisterForServiceStateChanged(mHandler);  // Safe even if unnecessary
323e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        }
324e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        mHandler.removeMessages(MSG_SERVICE_STATE_CHANGED);  // Clean up any pending messages too
325e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    }
326e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon
327e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    private void onComplete(boolean isRadioReady) {
328e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        if (mCallback != null) {
329e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon            Callback tempCallback = mCallback;
330e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon            mCallback = null;
331e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon            tempCallback.onComplete(isRadioReady);
332e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon        }
333e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon    }
334e30fc022880a19d326a2c4396af0c4c1f1dd5d01Santos Cordon}
335