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