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