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