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