1 /* 2 * Copyright (C) 2006 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.Call; 20 import com.android.internal.telephony.CallManager; 21 import com.android.internal.telephony.CallerInfo; 22 import com.android.internal.telephony.CallerInfoAsyncQuery; 23 import com.android.internal.telephony.Connection; 24 import com.android.internal.telephony.Phone; 25 import com.android.internal.telephony.PhoneConstants; 26 import com.android.internal.telephony.PhoneBase; 27 import com.android.internal.telephony.TelephonyCapabilities; 28 import com.android.internal.telephony.cdma.CdmaCallWaitingNotification; 29 import com.android.internal.telephony.cdma.CdmaInformationRecords.CdmaDisplayInfoRec; 30 import com.android.internal.telephony.cdma.CdmaInformationRecords.CdmaSignalInfoRec; 31 import com.android.internal.telephony.cdma.SignalToneUtil; 32 33 import android.app.ActivityManagerNative; 34 import android.bluetooth.BluetoothAdapter; 35 import android.bluetooth.BluetoothHeadset; 36 import android.bluetooth.BluetoothProfile; 37 import android.content.Context; 38 import android.media.AudioManager; 39 import android.media.ToneGenerator; 40 import android.net.Uri; 41 import android.os.AsyncResult; 42 import android.os.Handler; 43 import android.os.Message; 44 import android.os.SystemProperties; 45 import android.os.SystemVibrator; 46 import android.os.Vibrator; 47 import android.provider.CallLog.Calls; 48 import android.provider.Settings; 49 import android.telephony.PhoneNumberUtils; 50 import android.telephony.PhoneStateListener; 51 import android.telephony.TelephonyManager; 52 import android.util.EventLog; 53 import android.util.Log; 54 55 /** 56 * Phone app module that listens for phone state changes and various other 57 * events from the telephony layer, and triggers any resulting UI behavior 58 * (like starting the Ringer and Incoming Call UI, playing in-call tones, 59 * updating notifications, writing call log entries, etc.) 60 */ 61 public class CallNotifier extends Handler 62 implements CallerInfoAsyncQuery.OnQueryCompleteListener { 63 private static final String LOG_TAG = "CallNotifier"; 64 private static final boolean DBG = 65 (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1); 66 private static final boolean VDBG = (PhoneGlobals.DBG_LEVEL >= 2); 67 68 // Maximum time we allow the CallerInfo query to run, 69 // before giving up and falling back to the default ringtone. 70 private static final int RINGTONE_QUERY_WAIT_TIME = 500; // msec 71 72 // Timers related to CDMA Call Waiting 73 // 1) For displaying Caller Info 74 // 2) For disabling "Add Call" menu option once User selects Ignore or CW Timeout occures 75 private static final int CALLWAITING_CALLERINFO_DISPLAY_TIME = 20000; // msec 76 private static final int CALLWAITING_ADDCALL_DISABLE_TIME = 30000; // msec 77 78 // Time to display the DisplayInfo Record sent by CDMA network 79 private static final int DISPLAYINFO_NOTIFICATION_TIME = 2000; // msec 80 81 /** The singleton instance. */ 82 private static CallNotifier sInstance; 83 84 // Boolean to keep track of whether or not a CDMA Call Waiting call timed out. 85 // 86 // This is CDMA-specific, because with CDMA we *don't* get explicit 87 // notification from the telephony layer that a call-waiting call has 88 // stopped ringing. Instead, when a call-waiting call first comes in we 89 // start a 20-second timer (see CALLWAITING_CALLERINFO_DISPLAY_DONE), and 90 // if the timer expires we clean up the call and treat it as a missed call. 91 // 92 // If this field is true, that means that the current Call Waiting call 93 // "timed out" and should be logged in Call Log as a missed call. If it's 94 // false when we reach onCdmaCallWaitingReject(), we can assume the user 95 // explicitly rejected this call-waiting call. 96 // 97 // This field is reset to false any time a call-waiting call first comes 98 // in, and after cleaning up a missed call-waiting call. It's only ever 99 // set to true when the CALLWAITING_CALLERINFO_DISPLAY_DONE timer fires. 100 // 101 // TODO: do we really need a member variable for this? Don't we always 102 // know at the moment we call onCdmaCallWaitingReject() whether this is an 103 // explicit rejection or not? 104 // (Specifically: when we call onCdmaCallWaitingReject() from 105 // PhoneUtils.hangupRingingCall() that means the user deliberately rejected 106 // the call, and if we call onCdmaCallWaitingReject() because of a 107 // CALLWAITING_CALLERINFO_DISPLAY_DONE event that means that it timed 108 // out...) 109 private boolean mCallWaitingTimeOut = false; 110 111 // values used to track the query state 112 private static final int CALLERINFO_QUERY_READY = 0; 113 private static final int CALLERINFO_QUERYING = -1; 114 115 // the state of the CallerInfo Query. 116 private int mCallerInfoQueryState; 117 118 // object used to synchronize access to mCallerInfoQueryState 119 private Object mCallerInfoQueryStateGuard = new Object(); 120 121 // Event used to indicate a query timeout. 122 private static final int RINGER_CUSTOM_RINGTONE_QUERY_TIMEOUT = 100; 123 124 // Events generated internally: 125 private static final int PHONE_MWI_CHANGED = 21; 126 private static final int CALLWAITING_CALLERINFO_DISPLAY_DONE = 22; 127 private static final int CALLWAITING_ADDCALL_DISABLE_TIMEOUT = 23; 128 private static final int DISPLAYINFO_NOTIFICATION_DONE = 24; 129 private static final int CDMA_CALL_WAITING_REJECT = 26; 130 private static final int UPDATE_IN_CALL_NOTIFICATION = 27; 131 132 // Emergency call related defines: 133 private static final int EMERGENCY_TONE_OFF = 0; 134 private static final int EMERGENCY_TONE_ALERT = 1; 135 private static final int EMERGENCY_TONE_VIBRATE = 2; 136 137 private PhoneGlobals mApplication; 138 private CallManager mCM; 139 private Ringer mRinger; 140 private BluetoothHeadset mBluetoothHeadset; 141 private CallLogger mCallLogger; 142 private CallModeler mCallModeler; 143 private boolean mSilentRingerRequested; 144 145 // ToneGenerator instance for playing SignalInfo tones 146 private ToneGenerator mSignalInfoToneGenerator; 147 148 // The tone volume relative to other sounds in the stream SignalInfo 149 private static final int TONE_RELATIVE_VOLUME_SIGNALINFO = 80; 150 151 private Call.State mPreviousCdmaCallState; 152 private boolean mVoicePrivacyState = false; 153 private boolean mIsCdmaRedialCall = false; 154 155 // Emergency call tone and vibrate: 156 private int mIsEmergencyToneOn; 157 private int mCurrentEmergencyToneState = EMERGENCY_TONE_OFF; 158 private EmergencyTonePlayerVibrator mEmergencyTonePlayerVibrator; 159 160 // Ringback tone player 161 private InCallTonePlayer mInCallRingbackTonePlayer; 162 163 // Call waiting tone player 164 private InCallTonePlayer mCallWaitingTonePlayer; 165 166 // Cached AudioManager 167 private AudioManager mAudioManager; 168 169 private final BluetoothManager mBluetoothManager; 170 171 /** 172 * Initialize the singleton CallNotifier instance. 173 * This is only done once, at startup, from PhoneApp.onCreate(). 174 */ init(PhoneGlobals app, Phone phone, Ringer ringer, CallLogger callLogger, CallStateMonitor callStateMonitor, BluetoothManager bluetoothManager, CallModeler callModeler)175 /* package */ static CallNotifier init(PhoneGlobals app, Phone phone, Ringer ringer, 176 CallLogger callLogger, CallStateMonitor callStateMonitor, 177 BluetoothManager bluetoothManager, CallModeler callModeler) { 178 synchronized (CallNotifier.class) { 179 if (sInstance == null) { 180 sInstance = new CallNotifier(app, phone, ringer, callLogger, callStateMonitor, 181 bluetoothManager, callModeler); 182 } else { 183 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance); 184 } 185 return sInstance; 186 } 187 } 188 189 /** Private constructor; @see init() */ CallNotifier(PhoneGlobals app, Phone phone, Ringer ringer, CallLogger callLogger, CallStateMonitor callStateMonitor, BluetoothManager bluetoothManager, CallModeler callModeler)190 private CallNotifier(PhoneGlobals app, Phone phone, Ringer ringer, CallLogger callLogger, 191 CallStateMonitor callStateMonitor, BluetoothManager bluetoothManager, 192 CallModeler callModeler) { 193 mApplication = app; 194 mCM = app.mCM; 195 mCallLogger = callLogger; 196 mBluetoothManager = bluetoothManager; 197 mCallModeler = callModeler; 198 199 mAudioManager = (AudioManager) mApplication.getSystemService(Context.AUDIO_SERVICE); 200 201 callStateMonitor.addListener(this); 202 203 createSignalInfoToneGenerator(); 204 205 mRinger = ringer; 206 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 207 if (adapter != null) { 208 adapter.getProfileProxy(mApplication.getApplicationContext(), 209 mBluetoothProfileServiceListener, 210 BluetoothProfile.HEADSET); 211 } 212 213 TelephonyManager telephonyManager = (TelephonyManager)app.getSystemService( 214 Context.TELEPHONY_SERVICE); 215 telephonyManager.listen(mPhoneStateListener, 216 PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR 217 | PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR); 218 } 219 createSignalInfoToneGenerator()220 private void createSignalInfoToneGenerator() { 221 // Instantiate the ToneGenerator for SignalInfo and CallWaiting 222 // TODO: We probably don't need the mSignalInfoToneGenerator instance 223 // around forever. Need to change it so as to create a ToneGenerator instance only 224 // when a tone is being played and releases it after its done playing. 225 if (mSignalInfoToneGenerator == null) { 226 try { 227 mSignalInfoToneGenerator = new ToneGenerator(AudioManager.STREAM_VOICE_CALL, 228 TONE_RELATIVE_VOLUME_SIGNALINFO); 229 Log.d(LOG_TAG, "CallNotifier: mSignalInfoToneGenerator created when toneplay"); 230 } catch (RuntimeException e) { 231 Log.w(LOG_TAG, "CallNotifier: Exception caught while creating " + 232 "mSignalInfoToneGenerator: " + e); 233 mSignalInfoToneGenerator = null; 234 } 235 } else { 236 Log.d(LOG_TAG, "mSignalInfoToneGenerator created already, hence skipping"); 237 } 238 } 239 240 @Override handleMessage(Message msg)241 public void handleMessage(Message msg) { 242 switch (msg.what) { 243 case CallStateMonitor.PHONE_NEW_RINGING_CONNECTION: 244 log("RINGING... (new)"); 245 onNewRingingConnection((AsyncResult) msg.obj); 246 mSilentRingerRequested = false; 247 break; 248 249 case CallStateMonitor.PHONE_INCOMING_RING: 250 // repeat the ring when requested by the RIL, and when the user has NOT 251 // specifically requested silence. 252 if (msg.obj != null && ((AsyncResult) msg.obj).result != null) { 253 PhoneBase pb = (PhoneBase)((AsyncResult)msg.obj).result; 254 255 if ((pb.getState() == PhoneConstants.State.RINGING) 256 && (mSilentRingerRequested == false)) { 257 if (DBG) log("RINGING... (PHONE_INCOMING_RING event)"); 258 mRinger.ring(); 259 } else { 260 if (DBG) log("RING before NEW_RING, skipping"); 261 } 262 } 263 break; 264 265 case CallStateMonitor.PHONE_STATE_CHANGED: 266 onPhoneStateChanged((AsyncResult) msg.obj); 267 break; 268 269 case CallStateMonitor.PHONE_DISCONNECT: 270 if (DBG) log("DISCONNECT"); 271 onDisconnect((AsyncResult) msg.obj); 272 break; 273 274 case CallStateMonitor.PHONE_UNKNOWN_CONNECTION_APPEARED: 275 onUnknownConnectionAppeared((AsyncResult) msg.obj); 276 break; 277 278 case RINGER_CUSTOM_RINGTONE_QUERY_TIMEOUT: 279 onCustomRingtoneQueryTimeout((Connection) msg.obj); 280 break; 281 282 case PHONE_MWI_CHANGED: 283 onMwiChanged(mApplication.phone.getMessageWaitingIndicator()); 284 break; 285 286 case CallStateMonitor.PHONE_CDMA_CALL_WAITING: 287 if (DBG) log("Received PHONE_CDMA_CALL_WAITING event"); 288 onCdmaCallWaiting((AsyncResult) msg.obj); 289 break; 290 291 case CDMA_CALL_WAITING_REJECT: 292 Log.i(LOG_TAG, "Received CDMA_CALL_WAITING_REJECT event"); 293 onCdmaCallWaitingReject(); 294 break; 295 296 case CALLWAITING_CALLERINFO_DISPLAY_DONE: 297 Log.i(LOG_TAG, "Received CALLWAITING_CALLERINFO_DISPLAY_DONE event"); 298 mCallWaitingTimeOut = true; 299 onCdmaCallWaitingReject(); 300 break; 301 302 case CALLWAITING_ADDCALL_DISABLE_TIMEOUT: 303 if (DBG) log("Received CALLWAITING_ADDCALL_DISABLE_TIMEOUT event ..."); 304 // Set the mAddCallMenuStateAfterCW state to true 305 mApplication.cdmaPhoneCallState.setAddCallMenuStateAfterCallWaiting(true); 306 break; 307 308 case CallStateMonitor.PHONE_STATE_DISPLAYINFO: 309 if (DBG) log("Received PHONE_STATE_DISPLAYINFO event"); 310 onDisplayInfo((AsyncResult) msg.obj); 311 break; 312 313 case CallStateMonitor.PHONE_STATE_SIGNALINFO: 314 if (DBG) log("Received PHONE_STATE_SIGNALINFO event"); 315 onSignalInfo((AsyncResult) msg.obj); 316 break; 317 318 case DISPLAYINFO_NOTIFICATION_DONE: 319 if (DBG) log("Received Display Info notification done event ..."); 320 CdmaDisplayInfo.dismissDisplayInfoRecord(); 321 break; 322 323 case CallStateMonitor.EVENT_OTA_PROVISION_CHANGE: 324 if (DBG) log("EVENT_OTA_PROVISION_CHANGE..."); 325 mApplication.handleOtaspEvent(msg); 326 break; 327 328 case CallStateMonitor.PHONE_ENHANCED_VP_ON: 329 if (DBG) log("PHONE_ENHANCED_VP_ON..."); 330 if (!mVoicePrivacyState) { 331 int toneToPlay = InCallTonePlayer.TONE_VOICE_PRIVACY; 332 new InCallTonePlayer(toneToPlay).start(); 333 mVoicePrivacyState = true; 334 } 335 break; 336 337 case CallStateMonitor.PHONE_ENHANCED_VP_OFF: 338 if (DBG) log("PHONE_ENHANCED_VP_OFF..."); 339 if (mVoicePrivacyState) { 340 int toneToPlay = InCallTonePlayer.TONE_VOICE_PRIVACY; 341 new InCallTonePlayer(toneToPlay).start(); 342 mVoicePrivacyState = false; 343 } 344 break; 345 346 case CallStateMonitor.PHONE_RINGBACK_TONE: 347 onRingbackTone((AsyncResult) msg.obj); 348 break; 349 350 case CallStateMonitor.PHONE_RESEND_MUTE: 351 onResendMute(); 352 break; 353 354 default: 355 // super.handleMessage(msg); 356 } 357 } 358 359 PhoneStateListener mPhoneStateListener = new PhoneStateListener() { 360 @Override 361 public void onMessageWaitingIndicatorChanged(boolean mwi) { 362 onMwiChanged(mwi); 363 } 364 365 @Override 366 public void onCallForwardingIndicatorChanged(boolean cfi) { 367 onCfiChanged(cfi); 368 } 369 }; 370 371 /** 372 * Handles a "new ringing connection" event from the telephony layer. 373 */ onNewRingingConnection(AsyncResult r)374 private void onNewRingingConnection(AsyncResult r) { 375 Connection c = (Connection) r.result; 376 log("onNewRingingConnection(): state = " + mCM.getState() + ", conn = { " + c + " }"); 377 Call ringing = c.getCall(); 378 Phone phone = ringing.getPhone(); 379 380 // Check for a few cases where we totally ignore incoming calls. 381 if (ignoreAllIncomingCalls(phone)) { 382 // Immediately reject the call, without even indicating to the user 383 // that an incoming call occurred. (This will generally send the 384 // caller straight to voicemail, just as if we *had* shown the 385 // incoming-call UI and the user had declined the call.) 386 PhoneUtils.hangupRingingCall(ringing); 387 return; 388 } 389 390 if (!c.isRinging()) { 391 Log.i(LOG_TAG, "CallNotifier.onNewRingingConnection(): connection not ringing!"); 392 // This is a very strange case: an incoming call that stopped 393 // ringing almost instantly after the onNewRingingConnection() 394 // event. There's nothing we can do here, so just bail out 395 // without doing anything. (But presumably we'll log it in 396 // the call log when the disconnect event comes in...) 397 return; 398 } 399 400 // Stop any signalInfo tone being played on receiving a Call 401 stopSignalInfoTone(); 402 403 Call.State state = c.getState(); 404 // State will be either INCOMING or WAITING. 405 if (VDBG) log("- connection is ringing! state = " + state); 406 // if (DBG) PhoneUtils.dumpCallState(mPhone); 407 408 // No need to do any service state checks here (like for 409 // "emergency mode"), since in those states the SIM won't let 410 // us get incoming connections in the first place. 411 412 // TODO: Consider sending out a serialized broadcast Intent here 413 // (maybe "ACTION_NEW_INCOMING_CALL"), *before* starting the 414 // ringer and going to the in-call UI. The intent should contain 415 // the caller-id info for the current connection, and say whether 416 // it would be a "call waiting" call or a regular ringing call. 417 // If anybody consumed the broadcast, we'd bail out without 418 // ringing or bringing up the in-call UI. 419 // 420 // This would give 3rd party apps a chance to listen for (and 421 // intercept) new ringing connections. An app could reject the 422 // incoming call by consuming the broadcast and doing nothing, or 423 // it could "pick up" the call (without any action by the user!) 424 // via some future TelephonyManager API. 425 // 426 // See bug 1312336 for more details. 427 // We'd need to protect this with a new "intercept incoming calls" 428 // system permission. 429 430 // Obtain a partial wake lock to make sure the CPU doesn't go to 431 // sleep before we finish bringing up the InCallScreen. 432 // (This will be upgraded soon to a full wake lock; see 433 // showIncomingCall().) 434 if (VDBG) log("Holding wake lock on new incoming connection."); 435 mApplication.requestWakeState(PhoneGlobals.WakeState.PARTIAL); 436 437 // - don't ring for call waiting connections 438 // - do this before showing the incoming call panel 439 startIncomingCallQuery(c); 440 441 442 443 // Note we *don't* post a status bar notification here, since 444 // we're not necessarily ready to actually show the incoming call 445 // to the user. (For calls in the INCOMING state, at least, we 446 // still need to run a caller-id query, and we may not even ring 447 // at all if the "send directly to voicemail" flag is set.) 448 // 449 // Instead, we update the notification (and potentially launch the 450 // InCallScreen) from the showIncomingCall() method, which runs 451 // when the caller-id query completes or times out. 452 453 if (VDBG) log("- onNewRingingConnection() done."); 454 } 455 456 /** 457 * Determines whether or not we're allowed to present incoming calls to the 458 * user, based on the capabilities and/or current state of the device. 459 * 460 * If this method returns true, that means we should immediately reject the 461 * current incoming call, without even indicating to the user that an 462 * incoming call occurred. 463 * 464 * (We only reject incoming calls in a few cases, like during an OTASP call 465 * when we can't interrupt the user, or if the device hasn't completed the 466 * SetupWizard yet. We also don't allow incoming calls on non-voice-capable 467 * devices. But note that we *always* allow incoming calls while in ECM.) 468 * 469 * @return true if we're *not* allowed to present an incoming call to 470 * the user. 471 */ ignoreAllIncomingCalls(Phone phone)472 private boolean ignoreAllIncomingCalls(Phone phone) { 473 // Incoming calls are totally ignored on non-voice-capable devices. 474 if (!PhoneGlobals.sVoiceCapable) { 475 // ...but still log a warning, since we shouldn't have gotten this 476 // event in the first place! (Incoming calls *should* be blocked at 477 // the telephony layer on non-voice-capable capable devices.) 478 Log.w(LOG_TAG, "Got onNewRingingConnection() on non-voice-capable device! Ignoring..."); 479 return true; 480 } 481 482 // In ECM (emergency callback mode), we ALWAYS allow incoming calls 483 // to get through to the user. (Note that ECM is applicable only to 484 // voice-capable CDMA devices). 485 if (PhoneUtils.isPhoneInEcm(phone)) { 486 if (DBG) log("Incoming call while in ECM: always allow..."); 487 return false; 488 } 489 490 // Incoming calls are totally ignored if the device isn't provisioned yet. 491 boolean provisioned = Settings.Global.getInt(mApplication.getContentResolver(), 492 Settings.Global.DEVICE_PROVISIONED, 0) != 0; 493 if (!provisioned) { 494 Log.i(LOG_TAG, "Ignoring incoming call: not provisioned"); 495 return true; 496 } 497 498 // Incoming calls are totally ignored if an OTASP call is active. 499 if (TelephonyCapabilities.supportsOtasp(phone)) { 500 boolean activateState = (mApplication.cdmaOtaScreenState.otaScreenState 501 == OtaUtils.CdmaOtaScreenState.OtaScreenState.OTA_STATUS_ACTIVATION); 502 boolean dialogState = (mApplication.cdmaOtaScreenState.otaScreenState 503 == OtaUtils.CdmaOtaScreenState.OtaScreenState.OTA_STATUS_SUCCESS_FAILURE_DLG); 504 boolean spcState = mApplication.cdmaOtaProvisionData.inOtaSpcState; 505 506 if (spcState) { 507 Log.i(LOG_TAG, "Ignoring incoming call: OTA call is active"); 508 return true; 509 } else if (activateState || dialogState) { 510 // We *are* allowed to receive incoming calls at this point. 511 // But clear out any residual OTASP UI first. 512 // TODO: It's an MVC violation to twiddle the OTA UI state here; 513 // we should instead provide a higher-level API via OtaUtils. 514 if (dialogState) mApplication.dismissOtaDialogs(); 515 mApplication.clearOtaState(); 516 return false; 517 } 518 } 519 520 // Normal case: allow this call to be presented to the user. 521 return false; 522 } 523 524 /** 525 * Helper method to manage the start of incoming call queries 526 */ startIncomingCallQuery(Connection c)527 private void startIncomingCallQuery(Connection c) { 528 // TODO: cache the custom ringer object so that subsequent 529 // calls will not need to do this query work. We can keep 530 // the MRU ringtones in memory. We'll still need to hit 531 // the database to get the callerinfo to act as a key, 532 // but at least we can save the time required for the 533 // Media player setup. The only issue with this is that 534 // we may need to keep an eye on the resources the Media 535 // player uses to keep these ringtones around. 536 537 // make sure we're in a state where we can be ready to 538 // query a ringtone uri. 539 boolean shouldStartQuery = false; 540 synchronized (mCallerInfoQueryStateGuard) { 541 if (mCallerInfoQueryState == CALLERINFO_QUERY_READY) { 542 mCallerInfoQueryState = CALLERINFO_QUERYING; 543 shouldStartQuery = true; 544 } 545 } 546 if (shouldStartQuery) { 547 // Reset the ringtone to the default first. 548 mRinger.setCustomRingtoneUri(Settings.System.DEFAULT_RINGTONE_URI); 549 550 // query the callerinfo to try to get the ringer. 551 PhoneUtils.CallerInfoToken cit = PhoneUtils.startGetCallerInfo( 552 mApplication, c, this, c); 553 554 // if this has already been queried then just ring, otherwise 555 // we wait for the alloted time before ringing. 556 if (cit.isFinal) { 557 if (VDBG) log("- CallerInfo already up to date, using available data"); 558 onQueryComplete(0, c, cit.currentInfo); 559 } else { 560 if (VDBG) log("- Starting query, posting timeout message."); 561 562 // Phone number (via getAddress()) is stored in the message to remember which 563 // number is actually used for the look up. 564 sendMessageDelayed( 565 Message.obtain(this, RINGER_CUSTOM_RINGTONE_QUERY_TIMEOUT, c), 566 RINGTONE_QUERY_WAIT_TIME); 567 } 568 // The call to showIncomingCall() will happen after the 569 // queries are complete (or time out). 570 } else { 571 // This should never happen; its the case where an incoming call 572 // arrives at the same time that the query is still being run, 573 // and before the timeout window has closed. 574 EventLog.writeEvent(EventLogTags.PHONE_UI_MULTIPLE_QUERY); 575 576 ringAndNotifyOfIncomingCall(c); 577 } 578 } 579 580 /** 581 * Performs the final steps of the onNewRingingConnection sequence: 582 * starts the ringer, and brings up the "incoming call" UI. 583 * 584 * Normally, this is called when the CallerInfo query completes (see 585 * onQueryComplete()). In this case, onQueryComplete() has already 586 * configured the Ringer object to use the custom ringtone (if there 587 * is one) for this caller. So we just tell the Ringer to start, and 588 * proceed to the InCallScreen. 589 * 590 * But this method can *also* be called if the 591 * RINGTONE_QUERY_WAIT_TIME timeout expires, which means that the 592 * CallerInfo query is taking too long. In that case, we log a 593 * warning but otherwise we behave the same as in the normal case. 594 * (We still tell the Ringer to start, but it's going to use the 595 * default ringtone.) 596 */ onCustomRingQueryComplete(Connection c)597 private void onCustomRingQueryComplete(Connection c) { 598 boolean isQueryExecutionTimeExpired = false; 599 synchronized (mCallerInfoQueryStateGuard) { 600 if (mCallerInfoQueryState == CALLERINFO_QUERYING) { 601 mCallerInfoQueryState = CALLERINFO_QUERY_READY; 602 isQueryExecutionTimeExpired = true; 603 } 604 } 605 if (isQueryExecutionTimeExpired) { 606 // There may be a problem with the query here, since the 607 // default ringtone is playing instead of the custom one. 608 Log.w(LOG_TAG, "CallerInfo query took too long; falling back to default ringtone"); 609 EventLog.writeEvent(EventLogTags.PHONE_UI_RINGER_QUERY_ELAPSED); 610 } 611 612 // Make sure we still have an incoming call! 613 // 614 // (It's possible for the incoming call to have been disconnected 615 // while we were running the query. In that case we better not 616 // start the ringer here, since there won't be any future 617 // DISCONNECT event to stop it!) 618 // 619 // Note we don't have to worry about the incoming call going away 620 // *after* this check but before we call mRinger.ring() below, 621 // since in that case we *will* still get a DISCONNECT message sent 622 // to our handler. (And we will correctly stop the ringer when we 623 // process that event.) 624 if (mCM.getState() != PhoneConstants.State.RINGING) { 625 Log.i(LOG_TAG, "onCustomRingQueryComplete: No incoming call! Bailing out..."); 626 // Don't start the ringer *or* bring up the "incoming call" UI. 627 // Just bail out. 628 return; 629 } 630 631 // If the ringing call still does not have any connection anymore, do not send the 632 // notification to the CallModeler. 633 final Call ringingCall = mCM.getFirstActiveRingingCall(); 634 635 if (ringingCall != null && ringingCall.getLatestConnection() == c) { 636 ringAndNotifyOfIncomingCall(c); 637 } 638 } 639 onUnknownConnectionAppeared(AsyncResult r)640 private void onUnknownConnectionAppeared(AsyncResult r) { 641 PhoneConstants.State state = mCM.getState(); 642 643 if (state == PhoneConstants.State.OFFHOOK) { 644 if (DBG) log("unknown connection appeared..."); 645 646 onPhoneStateChanged(r); 647 } 648 } 649 650 /** 651 * Notifies the Call Modeler that there is a new ringing connection. 652 * If it is not a waiting call (there is no other active call in foreground), we will ring the 653 * ringtone. Otherwise we will play the call waiting tone instead. 654 * @param c The new ringing connection. 655 */ ringAndNotifyOfIncomingCall(Connection c)656 private void ringAndNotifyOfIncomingCall(Connection c) { 657 if (PhoneUtils.isRealIncomingCall(c.getState())) { 658 mRinger.ring(); 659 } else { 660 if (VDBG) log("- starting call waiting tone..."); 661 if (mCallWaitingTonePlayer == null) { 662 mCallWaitingTonePlayer = new InCallTonePlayer(InCallTonePlayer.TONE_CALL_WAITING); 663 mCallWaitingTonePlayer.start(); 664 } 665 } 666 mCallModeler.onNewRingingConnection(c); 667 } 668 669 /** 670 * Updates the phone UI in response to phone state changes. 671 * 672 * Watch out: certain state changes are actually handled by their own 673 * specific methods: 674 * - see onNewRingingConnection() for new incoming calls 675 * - see onDisconnect() for calls being hung up or disconnected 676 */ onPhoneStateChanged(AsyncResult r)677 private void onPhoneStateChanged(AsyncResult r) { 678 PhoneConstants.State state = mCM.getState(); 679 if (VDBG) log("onPhoneStateChanged: state = " + state); 680 681 // Turn status bar notifications on or off depending upon the state 682 // of the phone. Notification Alerts (audible or vibrating) should 683 // be on if and only if the phone is IDLE. 684 mApplication.notificationMgr.statusBarHelper 685 .enableNotificationAlerts(state == PhoneConstants.State.IDLE); 686 687 Phone fgPhone = mCM.getFgPhone(); 688 if (fgPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) { 689 if ((fgPhone.getForegroundCall().getState() == Call.State.ACTIVE) 690 && ((mPreviousCdmaCallState == Call.State.DIALING) 691 || (mPreviousCdmaCallState == Call.State.ALERTING))) { 692 if (mIsCdmaRedialCall) { 693 int toneToPlay = InCallTonePlayer.TONE_REDIAL; 694 new InCallTonePlayer(toneToPlay).start(); 695 } 696 // Stop any signal info tone when call moves to ACTIVE state 697 stopSignalInfoTone(); 698 } 699 mPreviousCdmaCallState = fgPhone.getForegroundCall().getState(); 700 } 701 702 // Have the PhoneApp recompute its mShowBluetoothIndication 703 // flag based on the (new) telephony state. 704 // There's no need to force a UI update since we update the 705 // in-call notification ourselves (below), and the InCallScreen 706 // listens for phone state changes itself. 707 // TODO: Have BluetoothManager listen to CallModeler instead of relying on 708 // CallNotifier 709 mBluetoothManager.updateBluetoothIndication(); 710 711 712 // Update the phone state and other sensor/lock. 713 mApplication.updatePhoneState(state); 714 715 if (state == PhoneConstants.State.OFFHOOK) { 716 // stop call waiting tone if needed when answering 717 if (mCallWaitingTonePlayer != null) { 718 mCallWaitingTonePlayer.stopTone(); 719 mCallWaitingTonePlayer = null; 720 } 721 722 if (VDBG) log("onPhoneStateChanged: OFF HOOK"); 723 // make sure audio is in in-call mode now 724 PhoneUtils.setAudioMode(mCM); 725 726 // Since we're now in-call, the Ringer should definitely *not* 727 // be ringing any more. (This is just a sanity-check; we 728 // already stopped the ringer explicitly back in 729 // PhoneUtils.answerCall(), before the call to phone.acceptCall().) 730 // TODO: Confirm that this call really *is* unnecessary, and if so, 731 // remove it! 732 if (DBG) log("stopRing()... (OFFHOOK state)"); 733 mRinger.stopRing(); 734 } 735 736 if (fgPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) { 737 Connection c = fgPhone.getForegroundCall().getLatestConnection(); 738 if ((c != null) && (PhoneNumberUtils.isLocalEmergencyNumber(c.getAddress(), 739 mApplication))) { 740 if (VDBG) log("onPhoneStateChanged: it is an emergency call."); 741 Call.State callState = fgPhone.getForegroundCall().getState(); 742 if (mEmergencyTonePlayerVibrator == null) { 743 mEmergencyTonePlayerVibrator = new EmergencyTonePlayerVibrator(); 744 } 745 746 if (callState == Call.State.DIALING || callState == Call.State.ALERTING) { 747 mIsEmergencyToneOn = Settings.Global.getInt( 748 mApplication.getContentResolver(), 749 Settings.Global.EMERGENCY_TONE, EMERGENCY_TONE_OFF); 750 if (mIsEmergencyToneOn != EMERGENCY_TONE_OFF && 751 mCurrentEmergencyToneState == EMERGENCY_TONE_OFF) { 752 if (mEmergencyTonePlayerVibrator != null) { 753 mEmergencyTonePlayerVibrator.start(); 754 } 755 } 756 } else if (callState == Call.State.ACTIVE) { 757 if (mCurrentEmergencyToneState != EMERGENCY_TONE_OFF) { 758 if (mEmergencyTonePlayerVibrator != null) { 759 mEmergencyTonePlayerVibrator.stop(); 760 } 761 } 762 } 763 } 764 } 765 766 if ((fgPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM) 767 || (fgPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_SIP)) { 768 Call.State callState = mCM.getActiveFgCallState(); 769 if (!callState.isDialing()) { 770 // If call get activated or disconnected before the ringback 771 // tone stops, we have to stop it to prevent disturbing. 772 if (mInCallRingbackTonePlayer != null) { 773 mInCallRingbackTonePlayer.stopTone(); 774 mInCallRingbackTonePlayer = null; 775 } 776 } 777 } 778 } 779 updateCallNotifierRegistrationsAfterRadioTechnologyChange()780 void updateCallNotifierRegistrationsAfterRadioTechnologyChange() { 781 if (DBG) Log.d(LOG_TAG, "updateCallNotifierRegistrationsAfterRadioTechnologyChange..."); 782 783 // Clear ringback tone player 784 mInCallRingbackTonePlayer = null; 785 786 // Clear call waiting tone player 787 mCallWaitingTonePlayer = null; 788 789 // Instantiate mSignalInfoToneGenerator 790 createSignalInfoToneGenerator(); 791 } 792 793 /** 794 * Implemented for CallerInfoAsyncQuery.OnQueryCompleteListener interface. 795 * refreshes the CallCard data when it called. If called with this 796 * class itself, it is assumed that we have been waiting for the ringtone 797 * and direct to voicemail settings to update. 798 */ 799 @Override onQueryComplete(int token, Object cookie, CallerInfo ci)800 public void onQueryComplete(int token, Object cookie, CallerInfo ci) { 801 if (cookie instanceof Long) { 802 if (VDBG) log("CallerInfo query complete, posting missed call notification"); 803 804 mApplication.notificationMgr.notifyMissedCall(ci.name, ci.phoneNumber, 805 ci.numberPresentation, ci.phoneLabel, ci.cachedPhoto, ci.cachedPhotoIcon, 806 ((Long) cookie).longValue()); 807 } else if (cookie instanceof Connection) { 808 final Connection c = (Connection) cookie; 809 if (VDBG) log("CallerInfo query complete (for CallNotifier), " 810 + "updating state for incoming call.."); 811 812 // get rid of the timeout messages 813 removeMessages(RINGER_CUSTOM_RINGTONE_QUERY_TIMEOUT); 814 815 boolean isQueryExecutionTimeOK = false; 816 synchronized (mCallerInfoQueryStateGuard) { 817 if (mCallerInfoQueryState == CALLERINFO_QUERYING) { 818 mCallerInfoQueryState = CALLERINFO_QUERY_READY; 819 isQueryExecutionTimeOK = true; 820 } 821 } 822 //if we're in the right state 823 if (isQueryExecutionTimeOK) { 824 825 // send directly to voicemail. 826 if (ci.shouldSendToVoicemail) { 827 if (DBG) log("send to voicemail flag detected. hanging up."); 828 final Call ringingCall = mCM.getFirstActiveRingingCall(); 829 if (ringingCall != null && ringingCall.getLatestConnection() == c) { 830 PhoneUtils.hangupRingingCall(ringingCall); 831 return; 832 } 833 } 834 835 // set the ringtone uri to prepare for the ring. 836 if (ci.contactRingtoneUri != null) { 837 if (DBG) log("custom ringtone found, setting up ringer."); 838 Ringer r = mRinger; 839 r.setCustomRingtoneUri(ci.contactRingtoneUri); 840 } 841 // ring, and other post-ring actions. 842 onCustomRingQueryComplete(c); 843 } 844 } 845 } 846 847 /** 848 * Called when asynchronous CallerInfo query is taking too long (more than 849 * {@link #RINGTONE_QUERY_WAIT_TIME} msec), but we cannot wait any more. 850 * 851 * This looks up in-memory fallback cache and use it when available. If not, it just calls 852 * {@link #onCustomRingQueryComplete()} with default ringtone ("Send to voicemail" flag will 853 * be just ignored). 854 * 855 * @param number The phone number used for the async query. This method will take care of 856 * formatting or normalization of the number. 857 */ onCustomRingtoneQueryTimeout(Connection c)858 private void onCustomRingtoneQueryTimeout(Connection c) { 859 // First of all, this case itself should be rare enough, though we cannot avoid it in 860 // some situations (e.g. IPC is slow due to system overload, database is in sync, etc.) 861 Log.w(LOG_TAG, "CallerInfo query took too long; look up local fallback cache."); 862 863 // This method is intentionally verbose for now to detect possible bad side-effect for it. 864 // TODO: Remove the verbose log when it looks stable and reliable enough. 865 866 867 if (c != null) { 868 final CallerInfoCache.CacheEntry entry = 869 mApplication.callerInfoCache.getCacheEntry(c.getAddress()); 870 if (entry != null) { 871 if (entry.sendToVoicemail) { 872 log("send to voicemail flag detected (in fallback cache). hanging up."); 873 if (mCM.getFirstActiveRingingCall().getLatestConnection() == c) { 874 PhoneUtils.hangupRingingCall(mCM.getFirstActiveRingingCall()); 875 return; 876 } 877 } 878 879 if (entry.customRingtone != null) { 880 log("custom ringtone found (in fallback cache), setting up ringer: " 881 + entry.customRingtone); 882 this.mRinger.setCustomRingtoneUri(Uri.parse(entry.customRingtone)); 883 } 884 } else { 885 // In this case we call onCustomRingQueryComplete(), just 886 // like if the query had completed normally. (But we're 887 // going to get the default ringtone, since we never got 888 // the chance to call Ringer.setCustomRingtoneUri()). 889 log("Failed to find fallback cache. Use default ringer tone."); 890 } 891 } 892 893 onCustomRingQueryComplete(c); 894 } 895 onDisconnect(AsyncResult r)896 private void onDisconnect(AsyncResult r) { 897 if (VDBG) log("onDisconnect()... CallManager state: " + mCM.getState()); 898 899 mVoicePrivacyState = false; 900 Connection c = (Connection) r.result; 901 if (c != null) { 902 log("onDisconnect: cause = " + c.getDisconnectCause() 903 + ", incoming = " + c.isIncoming() 904 + ", date = " + c.getCreateTime()); 905 } else { 906 Log.w(LOG_TAG, "onDisconnect: null connection"); 907 } 908 909 int autoretrySetting = 0; 910 if ((c != null) && (c.getCall().getPhone().getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA)) { 911 autoretrySetting = android.provider.Settings.Global.getInt(mApplication. 912 getContentResolver(),android.provider.Settings.Global.CALL_AUTO_RETRY, 0); 913 } 914 915 // Stop any signalInfo tone being played when a call gets ended 916 stopSignalInfoTone(); 917 918 if ((c != null) && (c.getCall().getPhone().getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA)) { 919 // Resetting the CdmaPhoneCallState members 920 mApplication.cdmaPhoneCallState.resetCdmaPhoneCallState(); 921 922 // Remove Call waiting timers 923 removeMessages(CALLWAITING_CALLERINFO_DISPLAY_DONE); 924 removeMessages(CALLWAITING_ADDCALL_DISABLE_TIMEOUT); 925 } 926 927 // Stop the ringer if it was ringing (for an incoming call that 928 // either disconnected by itself, or was rejected by the user.) 929 // 930 // TODO: We technically *shouldn't* stop the ringer if the 931 // foreground or background call disconnects while an incoming call 932 // is still ringing, but that's a really rare corner case. 933 // It's safest to just unconditionally stop the ringer here. 934 935 // CDMA: For Call collision cases i.e. when the user makes an out going call 936 // and at the same time receives an Incoming Call, the Incoming Call is given 937 // higher preference. At this time framework sends a disconnect for the Out going 938 // call connection hence we should *not* be stopping the ringer being played for 939 // the Incoming Call 940 Call ringingCall = mCM.getFirstActiveRingingCall(); 941 if (ringingCall.getPhone().getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) { 942 if (PhoneUtils.isRealIncomingCall(ringingCall.getState())) { 943 // Also we need to take off the "In Call" icon from the Notification 944 // area as the Out going Call never got connected 945 if (DBG) log("cancelCallInProgressNotifications()... (onDisconnect)"); 946 mApplication.notificationMgr.cancelCallInProgressNotifications(); 947 } else { 948 if (DBG) log("stopRing()... (onDisconnect)"); 949 mRinger.stopRing(); 950 } 951 } else { // GSM 952 if (DBG) log("stopRing()... (onDisconnect)"); 953 mRinger.stopRing(); 954 } 955 956 // stop call waiting tone if needed when disconnecting 957 if (mCallWaitingTonePlayer != null) { 958 mCallWaitingTonePlayer.stopTone(); 959 mCallWaitingTonePlayer = null; 960 } 961 962 // If this is the end of an OTASP call, pass it on to the PhoneApp. 963 if (c != null && TelephonyCapabilities.supportsOtasp(c.getCall().getPhone())) { 964 final String number = c.getAddress(); 965 if (c.getCall().getPhone().isOtaSpNumber(number)) { 966 if (DBG) log("onDisconnect: this was an OTASP call!"); 967 mApplication.handleOtaspDisconnect(); 968 } 969 } 970 971 // Check for the various tones we might need to play (thru the 972 // earpiece) after a call disconnects. 973 int toneToPlay = InCallTonePlayer.TONE_NONE; 974 975 // The "Busy" or "Congestion" tone is the highest priority: 976 if (c != null) { 977 Connection.DisconnectCause cause = c.getDisconnectCause(); 978 if (cause == Connection.DisconnectCause.BUSY) { 979 if (DBG) log("- need to play BUSY tone!"); 980 toneToPlay = InCallTonePlayer.TONE_BUSY; 981 } else if (cause == Connection.DisconnectCause.CONGESTION) { 982 if (DBG) log("- need to play CONGESTION tone!"); 983 toneToPlay = InCallTonePlayer.TONE_CONGESTION; 984 } else if (((cause == Connection.DisconnectCause.NORMAL) 985 || (cause == Connection.DisconnectCause.LOCAL)) 986 && (mApplication.isOtaCallInActiveState())) { 987 if (DBG) log("- need to play OTA_CALL_END tone!"); 988 toneToPlay = InCallTonePlayer.TONE_OTA_CALL_END; 989 } else if (cause == Connection.DisconnectCause.CDMA_REORDER) { 990 if (DBG) log("- need to play CDMA_REORDER tone!"); 991 toneToPlay = InCallTonePlayer.TONE_REORDER; 992 } else if (cause == Connection.DisconnectCause.CDMA_INTERCEPT) { 993 if (DBG) log("- need to play CDMA_INTERCEPT tone!"); 994 toneToPlay = InCallTonePlayer.TONE_INTERCEPT; 995 } else if (cause == Connection.DisconnectCause.CDMA_DROP) { 996 if (DBG) log("- need to play CDMA_DROP tone!"); 997 toneToPlay = InCallTonePlayer.TONE_CDMA_DROP; 998 } else if (cause == Connection.DisconnectCause.OUT_OF_SERVICE) { 999 if (DBG) log("- need to play OUT OF SERVICE tone!"); 1000 toneToPlay = InCallTonePlayer.TONE_OUT_OF_SERVICE; 1001 } else if (cause == Connection.DisconnectCause.UNOBTAINABLE_NUMBER) { 1002 if (DBG) log("- need to play TONE_UNOBTAINABLE_NUMBER tone!"); 1003 toneToPlay = InCallTonePlayer.TONE_UNOBTAINABLE_NUMBER; 1004 } else if (cause == Connection.DisconnectCause.ERROR_UNSPECIFIED) { 1005 if (DBG) log("- DisconnectCause is ERROR_UNSPECIFIED: play TONE_CALL_ENDED!"); 1006 toneToPlay = InCallTonePlayer.TONE_CALL_ENDED; 1007 } 1008 } 1009 1010 // If we don't need to play BUSY or CONGESTION, then play the 1011 // "call ended" tone if this was a "regular disconnect" (i.e. a 1012 // normal call where one end or the other hung up) *and* this 1013 // disconnect event caused the phone to become idle. (In other 1014 // words, we *don't* play the sound if one call hangs up but 1015 // there's still an active call on the other line.) 1016 // TODO: We may eventually want to disable this via a preference. 1017 if ((toneToPlay == InCallTonePlayer.TONE_NONE) 1018 && (mCM.getState() == PhoneConstants.State.IDLE) 1019 && (c != null)) { 1020 Connection.DisconnectCause cause = c.getDisconnectCause(); 1021 if ((cause == Connection.DisconnectCause.NORMAL) // remote hangup 1022 || (cause == Connection.DisconnectCause.LOCAL)) { // local hangup 1023 if (VDBG) log("- need to play CALL_ENDED tone!"); 1024 toneToPlay = InCallTonePlayer.TONE_CALL_ENDED; 1025 mIsCdmaRedialCall = false; 1026 } 1027 } 1028 1029 // All phone calls are disconnected. 1030 if (mCM.getState() == PhoneConstants.State.IDLE) { 1031 // Don't reset the audio mode or bluetooth/speakerphone state 1032 // if we still need to let the user hear a tone through the earpiece. 1033 if (toneToPlay == InCallTonePlayer.TONE_NONE) { 1034 resetAudioStateAfterDisconnect(); 1035 } 1036 1037 mApplication.notificationMgr.cancelCallInProgressNotifications(); 1038 } 1039 1040 if (c != null) { 1041 mCallLogger.logCall(c); 1042 1043 final String number = c.getAddress(); 1044 final Phone phone = c.getCall().getPhone(); 1045 final boolean isEmergencyNumber = 1046 PhoneNumberUtils.isLocalEmergencyNumber(number, mApplication); 1047 1048 if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) { 1049 if ((isEmergencyNumber) 1050 && (mCurrentEmergencyToneState != EMERGENCY_TONE_OFF)) { 1051 if (mEmergencyTonePlayerVibrator != null) { 1052 mEmergencyTonePlayerVibrator.stop(); 1053 } 1054 } 1055 } 1056 1057 final long date = c.getCreateTime(); 1058 final Connection.DisconnectCause cause = c.getDisconnectCause(); 1059 final boolean missedCall = c.isIncoming() && 1060 (cause == Connection.DisconnectCause.INCOMING_MISSED); 1061 if (missedCall) { 1062 // Show the "Missed call" notification. 1063 // (Note we *don't* do this if this was an incoming call that 1064 // the user deliberately rejected.) 1065 showMissedCallNotification(c, date); 1066 } 1067 1068 // Possibly play a "post-disconnect tone" thru the earpiece. 1069 // We do this here, rather than from the InCallScreen 1070 // activity, since we need to do this even if you're not in 1071 // the Phone UI at the moment the connection ends. 1072 if (toneToPlay != InCallTonePlayer.TONE_NONE) { 1073 if (VDBG) log("- starting post-disconnect tone (" + toneToPlay + ")..."); 1074 new InCallTonePlayer(toneToPlay).start(); 1075 1076 // TODO: alternatively, we could start an InCallTonePlayer 1077 // here with an "unlimited" tone length, 1078 // and manually stop it later when this connection truly goes 1079 // away. (The real connection over the network was closed as soon 1080 // as we got the BUSY message. But our telephony layer keeps the 1081 // connection open for a few extra seconds so we can show the 1082 // "busy" indication to the user. We could stop the busy tone 1083 // when *that* connection's "disconnect" event comes in.) 1084 } 1085 1086 if (((mPreviousCdmaCallState == Call.State.DIALING) 1087 || (mPreviousCdmaCallState == Call.State.ALERTING)) 1088 && (!isEmergencyNumber) 1089 && (cause != Connection.DisconnectCause.INCOMING_MISSED ) 1090 && (cause != Connection.DisconnectCause.NORMAL) 1091 && (cause != Connection.DisconnectCause.LOCAL) 1092 && (cause != Connection.DisconnectCause.INCOMING_REJECTED)) { 1093 if (!mIsCdmaRedialCall) { 1094 if (autoretrySetting == InCallScreen.AUTO_RETRY_ON) { 1095 // TODO: (Moto): The contact reference data may need to be stored and use 1096 // here when redialing a call. For now, pass in NULL as the URI parameter. 1097 final int status = 1098 PhoneUtils.placeCall(mApplication, phone, number, null, false); 1099 if (status != PhoneUtils.CALL_STATUS_FAILED) { 1100 mIsCdmaRedialCall = true; 1101 } 1102 } else { 1103 mIsCdmaRedialCall = false; 1104 } 1105 } else { 1106 mIsCdmaRedialCall = false; 1107 } 1108 } 1109 } 1110 } 1111 1112 /** 1113 * Resets the audio mode and speaker state when a call ends. 1114 */ resetAudioStateAfterDisconnect()1115 private void resetAudioStateAfterDisconnect() { 1116 if (VDBG) log("resetAudioStateAfterDisconnect()..."); 1117 1118 if (mBluetoothHeadset != null) { 1119 mBluetoothHeadset.disconnectAudio(); 1120 } 1121 1122 // call turnOnSpeaker() with state=false and store=true even if speaker 1123 // is already off to reset user requested speaker state. 1124 PhoneUtils.turnOnSpeaker(mApplication, false, true); 1125 1126 PhoneUtils.setAudioMode(mCM); 1127 } 1128 onMwiChanged(boolean visible)1129 private void onMwiChanged(boolean visible) { 1130 if (VDBG) log("onMwiChanged(): " + visible); 1131 1132 // "Voicemail" is meaningless on non-voice-capable devices, 1133 // so ignore MWI events. 1134 if (!PhoneGlobals.sVoiceCapable) { 1135 // ...but still log a warning, since we shouldn't have gotten this 1136 // event in the first place! 1137 // (PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR events 1138 // *should* be blocked at the telephony layer on non-voice-capable 1139 // capable devices.) 1140 Log.w(LOG_TAG, "Got onMwiChanged() on non-voice-capable device! Ignoring..."); 1141 return; 1142 } 1143 1144 mApplication.notificationMgr.updateMwi(visible); 1145 } 1146 1147 /** 1148 * Posts a delayed PHONE_MWI_CHANGED event, to schedule a "retry" for a 1149 * failed NotificationMgr.updateMwi() call. 1150 */ sendMwiChangedDelayed(long delayMillis)1151 /* package */ void sendMwiChangedDelayed(long delayMillis) { 1152 Message message = Message.obtain(this, PHONE_MWI_CHANGED); 1153 sendMessageDelayed(message, delayMillis); 1154 } 1155 onCfiChanged(boolean visible)1156 private void onCfiChanged(boolean visible) { 1157 if (VDBG) log("onCfiChanged(): " + visible); 1158 mApplication.notificationMgr.updateCfi(visible); 1159 } 1160 1161 /** 1162 * Indicates whether or not this ringer is ringing. 1163 */ isRinging()1164 boolean isRinging() { 1165 return mRinger.isRinging(); 1166 } 1167 1168 /** 1169 * Stops the current ring, and tells the notifier that future 1170 * ring requests should be ignored. 1171 */ silenceRinger()1172 void silenceRinger() { 1173 mSilentRingerRequested = true; 1174 if (DBG) log("stopRing()... (silenceRinger)"); 1175 mRinger.stopRing(); 1176 } 1177 1178 /** 1179 * Restarts the ringer after having previously silenced it. 1180 * 1181 * (This is a no-op if the ringer is actually still ringing, or if the 1182 * incoming ringing call no longer exists.) 1183 */ restartRinger()1184 /* package */ void restartRinger() { 1185 if (DBG) log("restartRinger()..."); 1186 // Already ringing or Silent requested; no need to restart. 1187 if (isRinging() || mSilentRingerRequested) return; 1188 1189 final Call ringingCall = mCM.getFirstActiveRingingCall(); 1190 // Don't check ringingCall.isRinging() here, since that'll be true 1191 // for the WAITING state also. We only allow the ringer for 1192 // regular INCOMING calls. 1193 if (DBG) log("- ringingCall state: " + ringingCall.getState()); 1194 if (ringingCall.getState() == Call.State.INCOMING) { 1195 mRinger.ring(); 1196 } 1197 } 1198 1199 /** 1200 * Helper class to play tones through the earpiece (or speaker / BT) 1201 * during a call, using the ToneGenerator. 1202 * 1203 * To use, just instantiate a new InCallTonePlayer 1204 * (passing in the TONE_* constant for the tone you want) 1205 * and start() it. 1206 * 1207 * When we're done playing the tone, if the phone is idle at that 1208 * point, we'll reset the audio routing and speaker state. 1209 * (That means that for tones that get played *after* a call 1210 * disconnects, like "busy" or "congestion" or "call ended", you 1211 * should NOT call resetAudioStateAfterDisconnect() yourself. 1212 * Instead, just start the InCallTonePlayer, which will automatically 1213 * defer the resetAudioStateAfterDisconnect() call until the tone 1214 * finishes playing.) 1215 */ 1216 private class InCallTonePlayer extends Thread { 1217 private int mToneId; 1218 private int mState; 1219 // The possible tones we can play. 1220 public static final int TONE_NONE = 0; 1221 public static final int TONE_CALL_WAITING = 1; 1222 public static final int TONE_BUSY = 2; 1223 public static final int TONE_CONGESTION = 3; 1224 public static final int TONE_CALL_ENDED = 4; 1225 public static final int TONE_VOICE_PRIVACY = 5; 1226 public static final int TONE_REORDER = 6; 1227 public static final int TONE_INTERCEPT = 7; 1228 public static final int TONE_CDMA_DROP = 8; 1229 public static final int TONE_OUT_OF_SERVICE = 9; 1230 public static final int TONE_REDIAL = 10; 1231 public static final int TONE_OTA_CALL_END = 11; 1232 public static final int TONE_RING_BACK = 12; 1233 public static final int TONE_UNOBTAINABLE_NUMBER = 13; 1234 1235 // The tone volume relative to other sounds in the stream 1236 static final int TONE_RELATIVE_VOLUME_EMERGENCY = 100; 1237 static final int TONE_RELATIVE_VOLUME_HIPRI = 80; 1238 static final int TONE_RELATIVE_VOLUME_LOPRI = 50; 1239 1240 // Buffer time (in msec) to add on to tone timeout value. 1241 // Needed mainly when the timeout value for a tone is the 1242 // exact duration of the tone itself. 1243 static final int TONE_TIMEOUT_BUFFER = 20; 1244 1245 // The tone state 1246 static final int TONE_OFF = 0; 1247 static final int TONE_ON = 1; 1248 static final int TONE_STOPPED = 2; 1249 InCallTonePlayer(int toneId)1250 InCallTonePlayer(int toneId) { 1251 super(); 1252 mToneId = toneId; 1253 mState = TONE_OFF; 1254 } 1255 1256 @Override run()1257 public void run() { 1258 log("InCallTonePlayer.run(toneId = " + mToneId + ")..."); 1259 1260 int toneType = 0; // passed to ToneGenerator.startTone() 1261 int toneVolume; // passed to the ToneGenerator constructor 1262 int toneLengthMillis; 1263 int phoneType = mCM.getFgPhone().getPhoneType(); 1264 1265 switch (mToneId) { 1266 case TONE_CALL_WAITING: 1267 toneType = ToneGenerator.TONE_SUP_CALL_WAITING; 1268 toneVolume = TONE_RELATIVE_VOLUME_HIPRI; 1269 // Call waiting tone is stopped by stopTone() method 1270 toneLengthMillis = Integer.MAX_VALUE - TONE_TIMEOUT_BUFFER; 1271 break; 1272 case TONE_BUSY: 1273 if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) { 1274 toneType = ToneGenerator.TONE_CDMA_NETWORK_BUSY_ONE_SHOT; 1275 toneVolume = TONE_RELATIVE_VOLUME_LOPRI; 1276 toneLengthMillis = 1000; 1277 } else if ((phoneType == PhoneConstants.PHONE_TYPE_GSM) 1278 || (phoneType == PhoneConstants.PHONE_TYPE_SIP)) { 1279 toneType = ToneGenerator.TONE_SUP_BUSY; 1280 toneVolume = TONE_RELATIVE_VOLUME_HIPRI; 1281 toneLengthMillis = 4000; 1282 } else { 1283 throw new IllegalStateException("Unexpected phone type: " + phoneType); 1284 } 1285 break; 1286 case TONE_CONGESTION: 1287 toneType = ToneGenerator.TONE_SUP_CONGESTION; 1288 toneVolume = TONE_RELATIVE_VOLUME_HIPRI; 1289 toneLengthMillis = 4000; 1290 break; 1291 1292 case TONE_CALL_ENDED: 1293 toneType = ToneGenerator.TONE_PROP_PROMPT; 1294 toneVolume = TONE_RELATIVE_VOLUME_HIPRI; 1295 toneLengthMillis = 200; 1296 break; 1297 case TONE_OTA_CALL_END: 1298 if (mApplication.cdmaOtaConfigData.otaPlaySuccessFailureTone == 1299 OtaUtils.OTA_PLAY_SUCCESS_FAILURE_TONE_ON) { 1300 toneType = ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD; 1301 toneVolume = TONE_RELATIVE_VOLUME_HIPRI; 1302 toneLengthMillis = 750; 1303 } else { 1304 toneType = ToneGenerator.TONE_PROP_PROMPT; 1305 toneVolume = TONE_RELATIVE_VOLUME_HIPRI; 1306 toneLengthMillis = 200; 1307 } 1308 break; 1309 case TONE_VOICE_PRIVACY: 1310 toneType = ToneGenerator.TONE_CDMA_ALERT_NETWORK_LITE; 1311 toneVolume = TONE_RELATIVE_VOLUME_HIPRI; 1312 toneLengthMillis = 5000; 1313 break; 1314 case TONE_REORDER: 1315 toneType = ToneGenerator.TONE_CDMA_REORDER; 1316 toneVolume = TONE_RELATIVE_VOLUME_HIPRI; 1317 toneLengthMillis = 4000; 1318 break; 1319 case TONE_INTERCEPT: 1320 toneType = ToneGenerator.TONE_CDMA_ABBR_INTERCEPT; 1321 toneVolume = TONE_RELATIVE_VOLUME_LOPRI; 1322 toneLengthMillis = 500; 1323 break; 1324 case TONE_CDMA_DROP: 1325 case TONE_OUT_OF_SERVICE: 1326 toneType = ToneGenerator.TONE_CDMA_CALLDROP_LITE; 1327 toneVolume = TONE_RELATIVE_VOLUME_LOPRI; 1328 toneLengthMillis = 375; 1329 break; 1330 case TONE_REDIAL: 1331 toneType = ToneGenerator.TONE_CDMA_ALERT_AUTOREDIAL_LITE; 1332 toneVolume = TONE_RELATIVE_VOLUME_LOPRI; 1333 toneLengthMillis = 5000; 1334 break; 1335 case TONE_RING_BACK: 1336 toneType = ToneGenerator.TONE_SUP_RINGTONE; 1337 toneVolume = TONE_RELATIVE_VOLUME_HIPRI; 1338 // Call ring back tone is stopped by stopTone() method 1339 toneLengthMillis = Integer.MAX_VALUE - TONE_TIMEOUT_BUFFER; 1340 break; 1341 case TONE_UNOBTAINABLE_NUMBER: 1342 toneType = ToneGenerator.TONE_SUP_ERROR; 1343 toneVolume = TONE_RELATIVE_VOLUME_HIPRI; 1344 toneLengthMillis = 4000; 1345 break; 1346 default: 1347 throw new IllegalArgumentException("Bad toneId: " + mToneId); 1348 } 1349 1350 // If the mToneGenerator creation fails, just continue without it. It is 1351 // a local audio signal, and is not as important. 1352 ToneGenerator toneGenerator; 1353 try { 1354 int stream; 1355 if (mBluetoothHeadset != null) { 1356 stream = mBluetoothHeadset.isAudioOn() ? AudioManager.STREAM_BLUETOOTH_SCO: 1357 AudioManager.STREAM_VOICE_CALL; 1358 } else { 1359 stream = AudioManager.STREAM_VOICE_CALL; 1360 } 1361 toneGenerator = new ToneGenerator(stream, toneVolume); 1362 // if (DBG) log("- created toneGenerator: " + toneGenerator); 1363 } catch (RuntimeException e) { 1364 Log.w(LOG_TAG, 1365 "InCallTonePlayer: Exception caught while creating ToneGenerator: " + e); 1366 toneGenerator = null; 1367 } 1368 1369 // Using the ToneGenerator (with the CALL_WAITING / BUSY / 1370 // CONGESTION tones at least), the ToneGenerator itself knows 1371 // the right pattern of tones to play; we do NOT need to 1372 // manually start/stop each individual tone, or manually 1373 // insert the correct delay between tones. (We just start it 1374 // and let it run for however long we want the tone pattern to 1375 // continue.) 1376 // 1377 // TODO: When we stop the ToneGenerator in the middle of a 1378 // "tone pattern", it sounds bad if we cut if off while the 1379 // tone is actually playing. Consider adding API to the 1380 // ToneGenerator to say "stop at the next silent part of the 1381 // pattern", or simply "play the pattern N times and then 1382 // stop." 1383 boolean needToStopTone = true; 1384 boolean okToPlayTone = false; 1385 1386 if (toneGenerator != null) { 1387 int ringerMode = mAudioManager.getRingerMode(); 1388 if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) { 1389 if (toneType == ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD) { 1390 if ((ringerMode != AudioManager.RINGER_MODE_SILENT) && 1391 (ringerMode != AudioManager.RINGER_MODE_VIBRATE)) { 1392 if (DBG) log("- InCallTonePlayer: start playing call tone=" + toneType); 1393 okToPlayTone = true; 1394 needToStopTone = false; 1395 } 1396 } else if ((toneType == ToneGenerator.TONE_CDMA_NETWORK_BUSY_ONE_SHOT) || 1397 (toneType == ToneGenerator.TONE_CDMA_REORDER) || 1398 (toneType == ToneGenerator.TONE_CDMA_ABBR_REORDER) || 1399 (toneType == ToneGenerator.TONE_CDMA_ABBR_INTERCEPT) || 1400 (toneType == ToneGenerator.TONE_CDMA_CALLDROP_LITE)) { 1401 if (ringerMode != AudioManager.RINGER_MODE_SILENT) { 1402 if (DBG) log("InCallTonePlayer:playing call fail tone:" + toneType); 1403 okToPlayTone = true; 1404 needToStopTone = false; 1405 } 1406 } else if ((toneType == ToneGenerator.TONE_CDMA_ALERT_AUTOREDIAL_LITE) || 1407 (toneType == ToneGenerator.TONE_CDMA_ALERT_NETWORK_LITE)) { 1408 if ((ringerMode != AudioManager.RINGER_MODE_SILENT) && 1409 (ringerMode != AudioManager.RINGER_MODE_VIBRATE)) { 1410 if (DBG) log("InCallTonePlayer:playing tone for toneType=" + toneType); 1411 okToPlayTone = true; 1412 needToStopTone = false; 1413 } 1414 } else { // For the rest of the tones, always OK to play. 1415 okToPlayTone = true; 1416 } 1417 } else { // Not "CDMA" 1418 okToPlayTone = true; 1419 } 1420 1421 synchronized (this) { 1422 if (okToPlayTone && mState != TONE_STOPPED) { 1423 mState = TONE_ON; 1424 toneGenerator.startTone(toneType); 1425 try { 1426 wait(toneLengthMillis + TONE_TIMEOUT_BUFFER); 1427 } catch (InterruptedException e) { 1428 Log.w(LOG_TAG, 1429 "InCallTonePlayer stopped: " + e); 1430 } 1431 if (needToStopTone) { 1432 toneGenerator.stopTone(); 1433 } 1434 } 1435 // if (DBG) log("- InCallTonePlayer: done playing."); 1436 toneGenerator.release(); 1437 mState = TONE_OFF; 1438 } 1439 } 1440 1441 // Finally, do the same cleanup we otherwise would have done 1442 // in onDisconnect(). 1443 // 1444 // (But watch out: do NOT do this if the phone is in use, 1445 // since some of our tones get played *during* a call (like 1446 // CALL_WAITING) and we definitely *don't* 1447 // want to reset the audio mode / speaker / bluetooth after 1448 // playing those! 1449 // This call is really here for use with tones that get played 1450 // *after* a call disconnects, like "busy" or "congestion" or 1451 // "call ended", where the phone has already become idle but 1452 // we need to defer the resetAudioStateAfterDisconnect() call 1453 // till the tone finishes playing.) 1454 if (mCM.getState() == PhoneConstants.State.IDLE) { 1455 resetAudioStateAfterDisconnect(); 1456 } 1457 } 1458 stopTone()1459 public void stopTone() { 1460 synchronized (this) { 1461 if (mState == TONE_ON) { 1462 notify(); 1463 } 1464 mState = TONE_STOPPED; 1465 } 1466 } 1467 } 1468 1469 /** 1470 * Displays a notification when the phone receives a DisplayInfo record. 1471 */ onDisplayInfo(AsyncResult r)1472 private void onDisplayInfo(AsyncResult r) { 1473 // Extract the DisplayInfo String from the message 1474 CdmaDisplayInfoRec displayInfoRec = (CdmaDisplayInfoRec)(r.result); 1475 1476 if (displayInfoRec != null) { 1477 String displayInfo = displayInfoRec.alpha; 1478 if (DBG) log("onDisplayInfo: displayInfo=" + displayInfo); 1479 CdmaDisplayInfo.displayInfoRecord(mApplication, displayInfo); 1480 1481 // start a 2 second timer 1482 sendEmptyMessageDelayed(DISPLAYINFO_NOTIFICATION_DONE, 1483 DISPLAYINFO_NOTIFICATION_TIME); 1484 } 1485 } 1486 1487 /** 1488 * Helper class to play SignalInfo tones using the ToneGenerator. 1489 * 1490 * To use, just instantiate a new SignalInfoTonePlayer 1491 * (passing in the ToneID constant for the tone you want) 1492 * and start() it. 1493 */ 1494 private class SignalInfoTonePlayer extends Thread { 1495 private int mToneId; 1496 SignalInfoTonePlayer(int toneId)1497 SignalInfoTonePlayer(int toneId) { 1498 super(); 1499 mToneId = toneId; 1500 } 1501 1502 @Override run()1503 public void run() { 1504 log("SignalInfoTonePlayer.run(toneId = " + mToneId + ")..."); 1505 1506 if (mSignalInfoToneGenerator != null) { 1507 //First stop any ongoing SignalInfo tone 1508 mSignalInfoToneGenerator.stopTone(); 1509 1510 //Start playing the new tone if its a valid tone 1511 mSignalInfoToneGenerator.startTone(mToneId); 1512 } 1513 } 1514 } 1515 1516 /** 1517 * Plays a tone when the phone receives a SignalInfo record. 1518 */ onSignalInfo(AsyncResult r)1519 private void onSignalInfo(AsyncResult r) { 1520 // Signal Info are totally ignored on non-voice-capable devices. 1521 if (!PhoneGlobals.sVoiceCapable) { 1522 Log.w(LOG_TAG, "Got onSignalInfo() on non-voice-capable device! Ignoring..."); 1523 return; 1524 } 1525 1526 if (PhoneUtils.isRealIncomingCall(mCM.getFirstActiveRingingCall().getState())) { 1527 // Do not start any new SignalInfo tone when Call state is INCOMING 1528 // and stop any previous SignalInfo tone which is being played 1529 stopSignalInfoTone(); 1530 } else { 1531 // Extract the SignalInfo String from the message 1532 CdmaSignalInfoRec signalInfoRec = (CdmaSignalInfoRec)(r.result); 1533 // Only proceed if a Signal info is present. 1534 if (signalInfoRec != null) { 1535 boolean isPresent = signalInfoRec.isPresent; 1536 if (DBG) log("onSignalInfo: isPresent=" + isPresent); 1537 if (isPresent) {// if tone is valid 1538 int uSignalType = signalInfoRec.signalType; 1539 int uAlertPitch = signalInfoRec.alertPitch; 1540 int uSignal = signalInfoRec.signal; 1541 1542 if (DBG) log("onSignalInfo: uSignalType=" + uSignalType + ", uAlertPitch=" + 1543 uAlertPitch + ", uSignal=" + uSignal); 1544 //Map the Signal to a ToneGenerator ToneID only if Signal info is present 1545 int toneID = SignalToneUtil.getAudioToneFromSignalInfo 1546 (uSignalType, uAlertPitch, uSignal); 1547 1548 //Create the SignalInfo tone player and pass the ToneID 1549 new SignalInfoTonePlayer(toneID).start(); 1550 } 1551 } 1552 } 1553 } 1554 1555 /** 1556 * Stops a SignalInfo tone in the following condition 1557 * 1 - On receiving a New Ringing Call 1558 * 2 - On disconnecting a call 1559 * 3 - On answering a Call Waiting Call 1560 */ stopSignalInfoTone()1561 /* package */ void stopSignalInfoTone() { 1562 if (DBG) log("stopSignalInfoTone: Stopping SignalInfo tone player"); 1563 new SignalInfoTonePlayer(ToneGenerator.TONE_CDMA_SIGNAL_OFF).start(); 1564 } 1565 1566 /** 1567 * Plays a Call waiting tone if it is present in the second incoming call. 1568 */ onCdmaCallWaiting(AsyncResult r)1569 private void onCdmaCallWaiting(AsyncResult r) { 1570 // Remove any previous Call waiting timers in the queue 1571 removeMessages(CALLWAITING_CALLERINFO_DISPLAY_DONE); 1572 removeMessages(CALLWAITING_ADDCALL_DISABLE_TIMEOUT); 1573 1574 // Set the Phone Call State to SINGLE_ACTIVE as there is only one connection 1575 // else we would not have received Call waiting 1576 mApplication.cdmaPhoneCallState.setCurrentCallState( 1577 CdmaPhoneCallState.PhoneCallState.SINGLE_ACTIVE); 1578 1579 // Start timer for CW display 1580 mCallWaitingTimeOut = false; 1581 sendEmptyMessageDelayed(CALLWAITING_CALLERINFO_DISPLAY_DONE, 1582 CALLWAITING_CALLERINFO_DISPLAY_TIME); 1583 1584 // Set the mAddCallMenuStateAfterCW state to false 1585 mApplication.cdmaPhoneCallState.setAddCallMenuStateAfterCallWaiting(false); 1586 1587 // Start the timer for disabling "Add Call" menu option 1588 sendEmptyMessageDelayed(CALLWAITING_ADDCALL_DISABLE_TIMEOUT, 1589 CALLWAITING_ADDCALL_DISABLE_TIME); 1590 1591 // Extract the Call waiting information 1592 CdmaCallWaitingNotification infoCW = (CdmaCallWaitingNotification) r.result; 1593 int isPresent = infoCW.isPresent; 1594 if (DBG) log("onCdmaCallWaiting: isPresent=" + isPresent); 1595 if (isPresent == 1 ) {//'1' if tone is valid 1596 int uSignalType = infoCW.signalType; 1597 int uAlertPitch = infoCW.alertPitch; 1598 int uSignal = infoCW.signal; 1599 if (DBG) log("onCdmaCallWaiting: uSignalType=" + uSignalType + ", uAlertPitch=" 1600 + uAlertPitch + ", uSignal=" + uSignal); 1601 //Map the Signal to a ToneGenerator ToneID only if Signal info is present 1602 int toneID = 1603 SignalToneUtil.getAudioToneFromSignalInfo(uSignalType, uAlertPitch, uSignal); 1604 1605 //Create the SignalInfo tone player and pass the ToneID 1606 new SignalInfoTonePlayer(toneID).start(); 1607 } 1608 1609 mCallModeler.onCdmaCallWaiting(infoCW); 1610 } 1611 1612 /** 1613 * Posts a event causing us to clean up after rejecting (or timing-out) a 1614 * CDMA call-waiting call. 1615 * 1616 * This method is safe to call from any thread. 1617 * @see #onCdmaCallWaitingReject() 1618 */ sendCdmaCallWaitingReject()1619 /* package */ void sendCdmaCallWaitingReject() { 1620 sendEmptyMessage(CDMA_CALL_WAITING_REJECT); 1621 } 1622 1623 /** 1624 * Performs Call logging based on Timeout or Ignore Call Waiting Call for CDMA, 1625 * and finally calls Hangup on the Call Waiting connection. 1626 * 1627 * This method should be called only from the UI thread. 1628 * @see #sendCdmaCallWaitingReject() 1629 */ onCdmaCallWaitingReject()1630 private void onCdmaCallWaitingReject() { 1631 final Call ringingCall = mCM.getFirstActiveRingingCall(); 1632 1633 // Call waiting timeout scenario 1634 if (ringingCall.getState() == Call.State.WAITING) { 1635 // Code for perform Call logging and missed call notification 1636 Connection c = ringingCall.getLatestConnection(); 1637 1638 if (c != null) { 1639 final int callLogType = mCallWaitingTimeOut ? 1640 Calls.MISSED_TYPE : Calls.INCOMING_TYPE; 1641 1642 // TODO: This callLogType override is not ideal. Connection should be astracted away 1643 // at a telephony-phone layer that can understand and edit the callTypes within 1644 // the abstraction for CDMA devices. 1645 mCallLogger.logCall(c, callLogType); 1646 1647 final long date = c.getCreateTime(); 1648 if (callLogType == Calls.MISSED_TYPE) { 1649 // Add missed call notification 1650 showMissedCallNotification(c, date); 1651 } else { 1652 // Remove Call waiting 20 second display timer in the queue 1653 removeMessages(CALLWAITING_CALLERINFO_DISPLAY_DONE); 1654 } 1655 1656 // Hangup the RingingCall connection for CW 1657 PhoneUtils.hangup(c); 1658 } 1659 1660 //Reset the mCallWaitingTimeOut boolean 1661 mCallWaitingTimeOut = false; 1662 } 1663 1664 // Call modeler needs to know about this event regardless of the 1665 // state conditionals in the previous code. 1666 mCallModeler.onCdmaCallWaitingReject(); 1667 } 1668 1669 /** 1670 * Return the private variable mPreviousCdmaCallState. 1671 */ getPreviousCdmaCallState()1672 /* package */ Call.State getPreviousCdmaCallState() { 1673 return mPreviousCdmaCallState; 1674 } 1675 1676 /** 1677 * Return the private variable mVoicePrivacyState. 1678 */ getVoicePrivacyState()1679 /* package */ boolean getVoicePrivacyState() { 1680 return mVoicePrivacyState; 1681 } 1682 1683 /** 1684 * Return the private variable mIsCdmaRedialCall. 1685 */ getIsCdmaRedialCall()1686 /* package */ boolean getIsCdmaRedialCall() { 1687 return mIsCdmaRedialCall; 1688 } 1689 1690 /** 1691 * Helper function used to show a missed call notification. 1692 */ showMissedCallNotification(Connection c, final long date)1693 private void showMissedCallNotification(Connection c, final long date) { 1694 PhoneUtils.CallerInfoToken info = 1695 PhoneUtils.startGetCallerInfo(mApplication, c, this, Long.valueOf(date)); 1696 if (info != null) { 1697 // at this point, we've requested to start a query, but it makes no 1698 // sense to log this missed call until the query comes back. 1699 if (VDBG) log("showMissedCallNotification: Querying for CallerInfo on missed call..."); 1700 if (info.isFinal) { 1701 // it seems that the query we have actually is up to date. 1702 // send the notification then. 1703 CallerInfo ci = info.currentInfo; 1704 1705 // Check number presentation value; if we have a non-allowed presentation, 1706 // then display an appropriate presentation string instead as the missed 1707 // call. 1708 String name = ci.name; 1709 String number = ci.phoneNumber; 1710 if (ci.numberPresentation == PhoneConstants.PRESENTATION_RESTRICTED) { 1711 name = mApplication.getString(R.string.private_num); 1712 } else if (ci.numberPresentation != PhoneConstants.PRESENTATION_ALLOWED) { 1713 name = mApplication.getString(R.string.unknown); 1714 } else { 1715 number = PhoneUtils.modifyForSpecialCnapCases(mApplication, 1716 ci, number, ci.numberPresentation); 1717 } 1718 mApplication.notificationMgr.notifyMissedCall(name, number, ci.numberPresentation, 1719 ci.phoneLabel, ci.cachedPhoto, ci.cachedPhotoIcon, date); 1720 } 1721 } else { 1722 // getCallerInfo() can return null in rare cases, like if we weren't 1723 // able to get a valid phone number out of the specified Connection. 1724 Log.w(LOG_TAG, "showMissedCallNotification: got null CallerInfo for Connection " + c); 1725 } 1726 } 1727 1728 /** 1729 * Inner class to handle emergency call tone and vibrator 1730 */ 1731 private class EmergencyTonePlayerVibrator { 1732 private final int EMG_VIBRATE_LENGTH = 1000; // ms. 1733 private final int EMG_VIBRATE_PAUSE = 1000; // ms. 1734 private final long[] mVibratePattern = 1735 new long[] { EMG_VIBRATE_LENGTH, EMG_VIBRATE_PAUSE }; 1736 1737 private ToneGenerator mToneGenerator; 1738 // We don't rely on getSystemService(Context.VIBRATOR_SERVICE) to make sure this vibrator 1739 // object will be isolated from others. 1740 private Vibrator mEmgVibrator = new SystemVibrator(); 1741 private int mInCallVolume; 1742 1743 /** 1744 * constructor 1745 */ EmergencyTonePlayerVibrator()1746 public EmergencyTonePlayerVibrator() { 1747 } 1748 1749 /** 1750 * Start the emergency tone or vibrator. 1751 */ start()1752 private void start() { 1753 if (VDBG) log("call startEmergencyToneOrVibrate."); 1754 int ringerMode = mAudioManager.getRingerMode(); 1755 1756 if ((mIsEmergencyToneOn == EMERGENCY_TONE_ALERT) && 1757 (ringerMode == AudioManager.RINGER_MODE_NORMAL)) { 1758 log("EmergencyTonePlayerVibrator.start(): emergency tone..."); 1759 mToneGenerator = new ToneGenerator (AudioManager.STREAM_VOICE_CALL, 1760 InCallTonePlayer.TONE_RELATIVE_VOLUME_EMERGENCY); 1761 if (mToneGenerator != null) { 1762 mInCallVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_VOICE_CALL); 1763 mAudioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL, 1764 mAudioManager.getStreamMaxVolume(AudioManager.STREAM_VOICE_CALL), 1765 0); 1766 mToneGenerator.startTone(ToneGenerator.TONE_CDMA_EMERGENCY_RINGBACK); 1767 mCurrentEmergencyToneState = EMERGENCY_TONE_ALERT; 1768 } 1769 } else if (mIsEmergencyToneOn == EMERGENCY_TONE_VIBRATE) { 1770 log("EmergencyTonePlayerVibrator.start(): emergency vibrate..."); 1771 if (mEmgVibrator != null) { 1772 mEmgVibrator.vibrate(mVibratePattern, 0); 1773 mCurrentEmergencyToneState = EMERGENCY_TONE_VIBRATE; 1774 } 1775 } 1776 } 1777 1778 /** 1779 * If the emergency tone is active, stop the tone or vibrator accordingly. 1780 */ stop()1781 private void stop() { 1782 if (VDBG) log("call stopEmergencyToneOrVibrate."); 1783 1784 if ((mCurrentEmergencyToneState == EMERGENCY_TONE_ALERT) 1785 && (mToneGenerator != null)) { 1786 mToneGenerator.stopTone(); 1787 mToneGenerator.release(); 1788 mAudioManager.setStreamVolume(AudioManager.STREAM_VOICE_CALL, 1789 mInCallVolume, 1790 0); 1791 } else if ((mCurrentEmergencyToneState == EMERGENCY_TONE_VIBRATE) 1792 && (mEmgVibrator != null)) { 1793 mEmgVibrator.cancel(); 1794 } 1795 mCurrentEmergencyToneState = EMERGENCY_TONE_OFF; 1796 } 1797 } 1798 1799 private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener = 1800 new BluetoothProfile.ServiceListener() { 1801 public void onServiceConnected(int profile, BluetoothProfile proxy) { 1802 mBluetoothHeadset = (BluetoothHeadset) proxy; 1803 if (VDBG) log("- Got BluetoothHeadset: " + mBluetoothHeadset); 1804 } 1805 1806 public void onServiceDisconnected(int profile) { 1807 mBluetoothHeadset = null; 1808 } 1809 }; 1810 onRingbackTone(AsyncResult r)1811 private void onRingbackTone(AsyncResult r) { 1812 boolean playTone = (Boolean)(r.result); 1813 1814 if (playTone == true) { 1815 // Only play when foreground call is in DIALING or ALERTING. 1816 // to prevent a late coming playtone after ALERTING. 1817 // Don't play ringback tone if it is in play, otherwise it will cut 1818 // the current tone and replay it 1819 if (mCM.getActiveFgCallState().isDialing() && 1820 mInCallRingbackTonePlayer == null) { 1821 mInCallRingbackTonePlayer = new InCallTonePlayer(InCallTonePlayer.TONE_RING_BACK); 1822 mInCallRingbackTonePlayer.start(); 1823 } 1824 } else { 1825 if (mInCallRingbackTonePlayer != null) { 1826 mInCallRingbackTonePlayer.stopTone(); 1827 mInCallRingbackTonePlayer = null; 1828 } 1829 } 1830 } 1831 1832 /** 1833 * Toggle mute and unmute requests while keeping the same mute state 1834 */ onResendMute()1835 private void onResendMute() { 1836 boolean muteState = PhoneUtils.getMute(); 1837 PhoneUtils.setMute(!muteState); 1838 PhoneUtils.setMute(muteState); 1839 } 1840 log(String msg)1841 private void log(String msg) { 1842 Log.d(LOG_TAG, msg); 1843 } 1844 } 1845