Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2011 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | package com.android.phone; |
| 18 | |
| 19 | import com.android.internal.telephony.CallManager; |
| 20 | import com.android.internal.telephony.Connection; |
| 21 | import com.android.internal.telephony.Phone; |
| 22 | import com.android.internal.telephony.PhoneConstants; |
Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 23 | |
| 24 | import android.content.Context; |
| 25 | import android.content.Intent; |
| 26 | import android.os.AsyncResult; |
| 27 | import android.os.Handler; |
| 28 | import android.os.Message; |
| 29 | import android.os.PowerManager; |
| 30 | import android.os.UserHandle; |
| 31 | import android.provider.Settings; |
| 32 | import android.telephony.ServiceState; |
| 33 | import android.util.Log; |
| 34 | |
| 35 | |
| 36 | /** |
| 37 | * Helper class for the {@link CallController} that implements special |
| 38 | * behavior related to emergency calls. Specifically, this class handles |
| 39 | * the case of the user trying to dial an emergency number while the radio |
| 40 | * is off (i.e. the device is in airplane mode), by forcibly turning the |
| 41 | * radio back on, waiting for it to come up, and then retrying the |
| 42 | * emergency call. |
| 43 | * |
| 44 | * This class is instantiated lazily (the first time the user attempts to |
| 45 | * make an emergency call from airplane mode) by the the |
| 46 | * {@link CallController} singleton. |
| 47 | */ |
| 48 | public class EmergencyCallHelper extends Handler { |
| 49 | private static final String TAG = "EmergencyCallHelper"; |
| 50 | private static final boolean DBG = false; |
| 51 | |
| 52 | // Number of times to retry the call, and time between retry attempts. |
| 53 | public static final int MAX_NUM_RETRIES = 6; |
| 54 | public static final long TIME_BETWEEN_RETRIES = 5000; // msec |
| 55 | |
| 56 | // Timeout used with our wake lock (just as a safety valve to make |
| 57 | // sure we don't hold it forever). |
| 58 | public static final long WAKE_LOCK_TIMEOUT = 5 * 60 * 1000; // 5 minutes in msec |
| 59 | |
| 60 | // Handler message codes; see handleMessage() |
| 61 | private static final int START_SEQUENCE = 1; |
| 62 | private static final int SERVICE_STATE_CHANGED = 2; |
| 63 | private static final int DISCONNECT = 3; |
| 64 | private static final int RETRY_TIMEOUT = 4; |
| 65 | |
| 66 | private CallController mCallController; |
| 67 | private PhoneGlobals mApp; |
| 68 | private CallManager mCM; |
Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 69 | private String mNumber; // The emergency number we're trying to dial |
| 70 | private int mNumRetriesSoFar; |
| 71 | |
| 72 | // Wake lock we hold while running the whole sequence |
| 73 | private PowerManager.WakeLock mPartialWakeLock; |
| 74 | |
| 75 | public EmergencyCallHelper(CallController callController) { |
| 76 | if (DBG) log("EmergencyCallHelper constructor..."); |
| 77 | mCallController = callController; |
| 78 | mApp = PhoneGlobals.getInstance(); |
| 79 | mCM = mApp.mCM; |
| 80 | } |
| 81 | |
| 82 | @Override |
| 83 | public void handleMessage(Message msg) { |
| 84 | switch (msg.what) { |
| 85 | case START_SEQUENCE: |
| 86 | startSequenceInternal(msg); |
| 87 | break; |
| 88 | case SERVICE_STATE_CHANGED: |
| 89 | onServiceStateChanged(msg); |
| 90 | break; |
| 91 | case DISCONNECT: |
| 92 | onDisconnect(msg); |
| 93 | break; |
| 94 | case RETRY_TIMEOUT: |
| 95 | onRetryTimeout(); |
| 96 | break; |
| 97 | default: |
| 98 | Log.wtf(TAG, "handleMessage: unexpected message: " + msg); |
| 99 | break; |
| 100 | } |
| 101 | } |
| 102 | |
| 103 | /** |
| 104 | * Starts the "emergency call from airplane mode" sequence. |
| 105 | * |
| 106 | * This is the (single) external API of the EmergencyCallHelper class. |
| 107 | * This method is called from the CallController placeCall() sequence |
| 108 | * if the user dials a valid emergency number, but the radio is |
| 109 | * powered-off (presumably due to airplane mode.) |
| 110 | * |
| 111 | * This method kicks off the following sequence: |
| 112 | * - Power on the radio |
| 113 | * - Listen for the service state change event telling us the radio has come up |
| 114 | * - Then launch the emergency call |
| 115 | * - Retry if the call fails with an OUT_OF_SERVICE error |
| 116 | * - Retry if we've gone 5 seconds without any response from the radio |
| 117 | * - Finally, clean up any leftover state (progress UI, wake locks, etc.) |
| 118 | * |
| 119 | * This method is safe to call from any thread, since it simply posts |
| 120 | * a message to the EmergencyCallHelper's handler (thus ensuring that |
| 121 | * the rest of the sequence is entirely serialized, and runs only on |
| 122 | * the handler thread.) |
| 123 | * |
| 124 | * This method does *not* force the in-call UI to come up; our caller |
| 125 | * is responsible for doing that (presumably by calling |
| 126 | * PhoneApp.displayCallScreen().) |
| 127 | */ |
| 128 | public void startEmergencyCallFromAirplaneModeSequence(String number) { |
| 129 | if (DBG) log("startEmergencyCallFromAirplaneModeSequence('" + number + "')..."); |
| 130 | Message msg = obtainMessage(START_SEQUENCE, number); |
| 131 | sendMessage(msg); |
| 132 | } |
| 133 | |
| 134 | /** |
| 135 | * Actual implementation of startEmergencyCallFromAirplaneModeSequence(), |
| 136 | * guaranteed to run on the handler thread. |
Jake Hamby | 3a2daab | 2013-11-08 12:35:57 -0800 | [diff] [blame^] | 137 | * @see #startEmergencyCallFromAirplaneModeSequence |
Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 138 | */ |
| 139 | private void startSequenceInternal(Message msg) { |
| 140 | if (DBG) log("startSequenceInternal(): msg = " + msg); |
| 141 | |
| 142 | // First of all, clean up any state (including mPartialWakeLock!) |
| 143 | // left over from a prior emergency call sequence. |
| 144 | // This ensures that we'll behave sanely if another |
| 145 | // startEmergencyCallFromAirplaneModeSequence() comes in while |
| 146 | // we're already in the middle of the sequence. |
| 147 | cleanup(); |
| 148 | |
| 149 | mNumber = (String) msg.obj; |
| 150 | if (DBG) log("- startSequenceInternal: Got mNumber: '" + mNumber + "'"); |
| 151 | |
| 152 | mNumRetriesSoFar = 0; |
| 153 | |
Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 154 | // Wake lock to make sure the processor doesn't go to sleep midway |
| 155 | // through the emergency call sequence. |
| 156 | PowerManager pm = (PowerManager) mApp.getSystemService(Context.POWER_SERVICE); |
| 157 | mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); |
| 158 | // Acquire with a timeout, just to be sure we won't hold the wake |
| 159 | // lock forever even if a logic bug (in this class) causes us to |
| 160 | // somehow never call cleanup(). |
| 161 | if (DBG) log("- startSequenceInternal: acquiring wake lock"); |
| 162 | mPartialWakeLock.acquire(WAKE_LOCK_TIMEOUT); |
| 163 | |
| 164 | // No need to check the current service state here, since the only |
| 165 | // reason the CallController would call this method in the first |
| 166 | // place is if the radio is powered-off. |
| 167 | // |
| 168 | // So just go ahead and turn the radio on. |
| 169 | |
| 170 | powerOnRadio(); // We'll get an onServiceStateChanged() callback |
| 171 | // when the radio successfully comes up. |
| 172 | |
| 173 | // Next step: when the SERVICE_STATE_CHANGED event comes in, |
| 174 | // we'll retry the call; see placeEmergencyCall(); |
| 175 | // But also, just in case, start a timer to make sure we'll retry |
| 176 | // the call even if the SERVICE_STATE_CHANGED event never comes in |
| 177 | // for some reason. |
| 178 | startRetryTimer(); |
| 179 | |
Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 180 | // (Our caller is responsible for calling mApp.displayCallScreen().) |
| 181 | } |
| 182 | |
| 183 | /** |
| 184 | * Handles the SERVICE_STATE_CHANGED event. |
| 185 | * |
| 186 | * (Normally this event tells us that the radio has finally come |
| 187 | * up. In that case, it's now safe to actually place the |
| 188 | * emergency call.) |
| 189 | */ |
| 190 | private void onServiceStateChanged(Message msg) { |
| 191 | ServiceState state = (ServiceState) ((AsyncResult) msg.obj).result; |
| 192 | if (DBG) log("onServiceStateChanged()... new state = " + state); |
| 193 | |
| 194 | // Possible service states: |
| 195 | // - STATE_IN_SERVICE // Normal operation |
| 196 | // - STATE_OUT_OF_SERVICE // Still searching for an operator to register to, |
| 197 | // // or no radio signal |
| 198 | // - STATE_EMERGENCY_ONLY // Phone is locked; only emergency numbers are allowed |
| 199 | // - STATE_POWER_OFF // Radio is explicitly powered off (airplane mode) |
| 200 | |
| 201 | // Once we reach either STATE_IN_SERVICE or STATE_EMERGENCY_ONLY, |
| 202 | // it's finally OK to place the emergency call. |
| 203 | boolean okToCall = (state.getState() == ServiceState.STATE_IN_SERVICE) |
| 204 | || (state.getState() == ServiceState.STATE_EMERGENCY_ONLY); |
| 205 | |
| 206 | if (okToCall) { |
| 207 | // Woo hoo! It's OK to actually place the call. |
| 208 | if (DBG) log("onServiceStateChanged: ok to call!"); |
| 209 | |
| 210 | // Deregister for the service state change events. |
| 211 | unregisterForServiceStateChanged(); |
| 212 | |
Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 213 | placeEmergencyCall(); |
Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 214 | } else { |
| 215 | // The service state changed, but we're still not ready to call yet. |
| 216 | // (This probably was the transition from STATE_POWER_OFF to |
| 217 | // STATE_OUT_OF_SERVICE, which happens immediately after powering-on |
| 218 | // the radio.) |
| 219 | // |
| 220 | // So just keep waiting; we'll probably get to either |
| 221 | // STATE_IN_SERVICE or STATE_EMERGENCY_ONLY very shortly. |
| 222 | // (Or even if that doesn't happen, we'll at least do another retry |
| 223 | // when the RETRY_TIMEOUT event fires.) |
| 224 | if (DBG) log("onServiceStateChanged: not ready to call yet, keep waiting..."); |
| 225 | } |
| 226 | } |
| 227 | |
| 228 | /** |
| 229 | * Handles a DISCONNECT event from the telephony layer. |
| 230 | * |
| 231 | * Even after we successfully place an emergency call (after powering |
| 232 | * on the radio), it's still possible for the call to fail with the |
| 233 | * disconnect cause OUT_OF_SERVICE. If so, schedule a retry. |
| 234 | */ |
| 235 | private void onDisconnect(Message msg) { |
| 236 | Connection conn = (Connection) ((AsyncResult) msg.obj).result; |
| 237 | Connection.DisconnectCause cause = conn.getDisconnectCause(); |
| 238 | if (DBG) log("onDisconnect: connection '" + conn |
| 239 | + "', addr '" + conn.getAddress() + "', cause = " + cause); |
| 240 | |
| 241 | if (cause == Connection.DisconnectCause.OUT_OF_SERVICE) { |
| 242 | // Wait a bit more and try again (or just bail out totally if |
| 243 | // we've had too many failures.) |
| 244 | if (DBG) log("- onDisconnect: OUT_OF_SERVICE, need to retry..."); |
| 245 | scheduleRetryOrBailOut(); |
| 246 | } else { |
| 247 | // Any other disconnect cause means we're done. |
| 248 | // Either the emergency call succeeded *and* ended normally, |
| 249 | // or else there was some error that we can't retry. In either |
| 250 | // case, just clean up our internal state.) |
| 251 | |
| 252 | if (DBG) log("==> Disconnect event; clean up..."); |
| 253 | cleanup(); |
| 254 | |
| 255 | // Nothing else to do here. If the InCallScreen was visible, |
| 256 | // it would have received this disconnect event too (so it'll |
| 257 | // show the "Call ended" state and finish itself without any |
| 258 | // help from us.) |
| 259 | } |
| 260 | } |
| 261 | |
| 262 | /** |
| 263 | * Handles the retry timer expiring. |
| 264 | */ |
| 265 | private void onRetryTimeout() { |
| 266 | PhoneConstants.State phoneState = mCM.getState(); |
Jake Hamby | 3a2daab | 2013-11-08 12:35:57 -0800 | [diff] [blame^] | 267 | int serviceState = mCM.getDefaultPhone().getServiceState().getState(); |
Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 268 | if (DBG) log("onRetryTimeout(): phone state " + phoneState |
| 269 | + ", service state " + serviceState |
| 270 | + ", mNumRetriesSoFar = " + mNumRetriesSoFar); |
| 271 | |
| 272 | // - If we're actually in a call, we've succeeded. |
| 273 | // |
| 274 | // - Otherwise, if the radio is now on, that means we successfully got |
| 275 | // out of airplane mode but somehow didn't get the service state |
| 276 | // change event. In that case, try to place the call. |
| 277 | // |
| 278 | // - If the radio is still powered off, try powering it on again. |
| 279 | |
| 280 | if (phoneState == PhoneConstants.State.OFFHOOK) { |
| 281 | if (DBG) log("- onRetryTimeout: Call is active! Cleaning up..."); |
| 282 | cleanup(); |
| 283 | return; |
| 284 | } |
| 285 | |
| 286 | if (serviceState != ServiceState.STATE_POWER_OFF) { |
| 287 | // Woo hoo -- we successfully got out of airplane mode. |
| 288 | |
| 289 | // Deregister for the service state change events; we don't need |
| 290 | // these any more now that the radio is powered-on. |
| 291 | unregisterForServiceStateChanged(); |
| 292 | |
Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 293 | placeEmergencyCall(); // If the call fails, placeEmergencyCall() |
| 294 | // will schedule a retry. |
| 295 | } else { |
| 296 | // Uh oh; we've waited the full TIME_BETWEEN_RETRIES and the |
| 297 | // radio is still not powered-on. Try again... |
| 298 | |
| 299 | if (DBG) log("- Trying (again) to turn on the radio..."); |
| 300 | powerOnRadio(); // Again, we'll (hopefully) get an onServiceStateChanged() |
| 301 | // callback when the radio successfully comes up. |
| 302 | |
| 303 | // ...and also set a fresh retry timer (or just bail out |
| 304 | // totally if we've had too many failures.) |
| 305 | scheduleRetryOrBailOut(); |
| 306 | } |
Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 307 | } |
| 308 | |
| 309 | /** |
| 310 | * Attempt to power on the radio (i.e. take the device out |
| 311 | * of airplane mode.) |
| 312 | * |
| 313 | * Additionally, start listening for service state changes; |
| 314 | * we'll eventually get an onServiceStateChanged() callback |
| 315 | * when the radio successfully comes up. |
| 316 | */ |
| 317 | private void powerOnRadio() { |
| 318 | if (DBG) log("- powerOnRadio()..."); |
| 319 | |
| 320 | // We're about to turn on the radio, so arrange to be notified |
| 321 | // when the sequence is complete. |
| 322 | registerForServiceStateChanged(); |
| 323 | |
| 324 | // If airplane mode is on, we turn it off the same way that the |
| 325 | // Settings activity turns it off. |
| 326 | if (Settings.Global.getInt(mApp.getContentResolver(), |
| 327 | Settings.Global.AIRPLANE_MODE_ON, 0) > 0) { |
| 328 | if (DBG) log("==> Turning off airplane mode..."); |
| 329 | |
| 330 | // Change the system setting |
| 331 | Settings.Global.putInt(mApp.getContentResolver(), |
| 332 | Settings.Global.AIRPLANE_MODE_ON, 0); |
| 333 | |
| 334 | // Post the intent |
| 335 | Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED); |
| 336 | intent.putExtra("state", false); |
| 337 | mApp.sendBroadcastAsUser(intent, UserHandle.ALL); |
| 338 | } else { |
| 339 | // Otherwise, for some strange reason the radio is off |
| 340 | // (even though the Settings database doesn't think we're |
| 341 | // in airplane mode.) In this case just turn the radio |
| 342 | // back on. |
| 343 | if (DBG) log("==> (Apparently) not in airplane mode; manually powering radio on..."); |
Jake Hamby | 3a2daab | 2013-11-08 12:35:57 -0800 | [diff] [blame^] | 344 | mCM.getDefaultPhone().setRadioPower(true); |
Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 345 | } |
| 346 | } |
| 347 | |
| 348 | /** |
| 349 | * Actually initiate the outgoing emergency call. |
| 350 | * (We do this once the radio has successfully been powered-up.) |
| 351 | * |
| 352 | * If the call succeeds, we're done. |
| 353 | * If the call fails, schedule a retry of the whole sequence. |
| 354 | */ |
| 355 | private void placeEmergencyCall() { |
| 356 | if (DBG) log("placeEmergencyCall()..."); |
| 357 | |
| 358 | // Place an outgoing call to mNumber. |
| 359 | // Note we call PhoneUtils.placeCall() directly; we don't want any |
| 360 | // of the behavior from CallController.placeCallInternal() here. |
| 361 | // (Specifically, we don't want to start the "emergency call from |
| 362 | // airplane mode" sequence from the beginning again!) |
| 363 | |
| 364 | registerForDisconnect(); // Get notified when this call disconnects |
| 365 | |
| 366 | if (DBG) log("- placing call to '" + mNumber + "'..."); |
| 367 | int callStatus = PhoneUtils.placeCall(mApp, |
Jake Hamby | 3a2daab | 2013-11-08 12:35:57 -0800 | [diff] [blame^] | 368 | mCM.getDefaultPhone(), |
Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 369 | mNumber, |
| 370 | null, // contactUri |
Santos Cordon | 69a6919 | 2013-08-22 14:25:42 -0700 | [diff] [blame] | 371 | true); // isEmergencyCall |
Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 372 | if (DBG) log("- PhoneUtils.placeCall() returned status = " + callStatus); |
| 373 | |
| 374 | boolean success; |
| 375 | // Note PhoneUtils.placeCall() returns one of the CALL_STATUS_* |
| 376 | // constants, not a CallStatusCode enum value. |
| 377 | switch (callStatus) { |
| 378 | case PhoneUtils.CALL_STATUS_DIALED: |
| 379 | success = true; |
| 380 | break; |
| 381 | |
| 382 | case PhoneUtils.CALL_STATUS_DIALED_MMI: |
| 383 | case PhoneUtils.CALL_STATUS_FAILED: |
| 384 | default: |
| 385 | // Anything else is a failure, and we'll need to retry. |
| 386 | Log.w(TAG, "placeEmergencyCall(): placeCall() failed: callStatus = " + callStatus); |
| 387 | success = false; |
| 388 | break; |
| 389 | } |
| 390 | |
| 391 | if (success) { |
| 392 | if (DBG) log("==> Success from PhoneUtils.placeCall()!"); |
| 393 | // Ok, the emergency call is (hopefully) under way. |
| 394 | |
| 395 | // We're not done yet, though, so don't call cleanup() here. |
| 396 | // (It's still possible that this call will fail, and disconnect |
| 397 | // with cause==OUT_OF_SERVICE. If so, that will trigger a retry |
| 398 | // from the onDisconnect() method.) |
| 399 | } else { |
| 400 | if (DBG) log("==> Failure."); |
| 401 | // Wait a bit more and try again (or just bail out totally if |
| 402 | // we've had too many failures.) |
| 403 | scheduleRetryOrBailOut(); |
| 404 | } |
| 405 | } |
| 406 | |
| 407 | /** |
| 408 | * Schedules a retry in response to some failure (either the radio |
| 409 | * failing to power on, or a failure when trying to place the call.) |
| 410 | * Or, if we've hit the retry limit, bail out of this whole sequence |
| 411 | * and display a failure message to the user. |
| 412 | */ |
| 413 | private void scheduleRetryOrBailOut() { |
| 414 | mNumRetriesSoFar++; |
| 415 | if (DBG) log("scheduleRetryOrBailOut()... mNumRetriesSoFar is now " + mNumRetriesSoFar); |
| 416 | |
| 417 | if (mNumRetriesSoFar > MAX_NUM_RETRIES) { |
| 418 | Log.w(TAG, "scheduleRetryOrBailOut: hit MAX_NUM_RETRIES; giving up..."); |
| 419 | cleanup(); |
Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 420 | } else { |
| 421 | if (DBG) log("- Scheduling another retry..."); |
| 422 | startRetryTimer(); |
Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 423 | } |
| 424 | } |
| 425 | |
| 426 | /** |
| 427 | * Clean up when done with the whole sequence: either after |
| 428 | * successfully placing *and* ending the emergency call, or after |
| 429 | * bailing out because of too many failures. |
| 430 | * |
| 431 | * The exact cleanup steps are: |
| 432 | * - Take down any progress UI (and also ask the in-call UI to refresh itself, |
| 433 | * if it's still visible) |
| 434 | * - Double-check that we're not still registered for any telephony events |
| 435 | * - Clean up any extraneous handler messages (like retry timeouts) still in the queue |
| 436 | * - Make sure we're not still holding any wake locks |
| 437 | * |
| 438 | * Basically this method guarantees that there will be no more |
| 439 | * activity from the EmergencyCallHelper until the CallController |
| 440 | * kicks off the whole sequence again with another call to |
| 441 | * startEmergencyCallFromAirplaneModeSequence(). |
| 442 | * |
| 443 | * Note we don't call this method simply after a successful call to |
| 444 | * placeCall(), since it's still possible the call will disconnect |
| 445 | * very quickly with an OUT_OF_SERVICE error. |
| 446 | */ |
| 447 | private void cleanup() { |
| 448 | if (DBG) log("cleanup()..."); |
| 449 | |
Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 450 | unregisterForServiceStateChanged(); |
| 451 | unregisterForDisconnect(); |
| 452 | cancelRetryTimer(); |
| 453 | |
| 454 | // Release / clean up the wake lock |
| 455 | if (mPartialWakeLock != null) { |
| 456 | if (mPartialWakeLock.isHeld()) { |
| 457 | if (DBG) log("- releasing wake lock"); |
| 458 | mPartialWakeLock.release(); |
| 459 | } |
| 460 | mPartialWakeLock = null; |
| 461 | } |
Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 462 | } |
| 463 | |
| 464 | private void startRetryTimer() { |
| 465 | removeMessages(RETRY_TIMEOUT); |
| 466 | sendEmptyMessageDelayed(RETRY_TIMEOUT, TIME_BETWEEN_RETRIES); |
| 467 | } |
| 468 | |
| 469 | private void cancelRetryTimer() { |
| 470 | removeMessages(RETRY_TIMEOUT); |
| 471 | } |
| 472 | |
| 473 | private void registerForServiceStateChanged() { |
| 474 | // Unregister first, just to make sure we never register ourselves |
| 475 | // twice. (We need this because Phone.registerForServiceStateChanged() |
| 476 | // does not prevent multiple registration of the same handler.) |
Jake Hamby | 3a2daab | 2013-11-08 12:35:57 -0800 | [diff] [blame^] | 477 | Phone phone = mCM.getDefaultPhone(); |
| 478 | phone.unregisterForServiceStateChanged(this); // Safe even if not currently registered |
| 479 | phone.registerForServiceStateChanged(this, SERVICE_STATE_CHANGED, null); |
Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 480 | } |
| 481 | |
| 482 | private void unregisterForServiceStateChanged() { |
| 483 | // This method is safe to call even if we haven't set mPhone yet. |
Jake Hamby | 3a2daab | 2013-11-08 12:35:57 -0800 | [diff] [blame^] | 484 | Phone phone = mCM.getDefaultPhone(); |
| 485 | if (phone != null) { |
| 486 | phone.unregisterForServiceStateChanged(this); // Safe even if unnecessary |
Santos Cordon | 7d4ddf6 | 2013-07-10 11:58:08 -0700 | [diff] [blame] | 487 | } |
| 488 | removeMessages(SERVICE_STATE_CHANGED); // Clean up any pending messages too |
| 489 | } |
| 490 | |
| 491 | private void registerForDisconnect() { |
| 492 | // Note: no need to unregister first, since |
| 493 | // CallManager.registerForDisconnect() automatically prevents |
| 494 | // multiple registration of the same handler. |
| 495 | mCM.registerForDisconnect(this, DISCONNECT, null); |
| 496 | } |
| 497 | |
| 498 | private void unregisterForDisconnect() { |
| 499 | mCM.unregisterForDisconnect(this); // Safe even if not currently registered |
| 500 | removeMessages(DISCONNECT); // Clean up any pending messages too |
| 501 | } |
| 502 | |
| 503 | |
| 504 | // |
| 505 | // Debugging |
| 506 | // |
| 507 | |
| 508 | private static void log(String msg) { |
| 509 | Log.d(TAG, msg); |
| 510 | } |
| 511 | } |