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