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