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