ImsPhoneCallTracker.java revision c3ec7d484569eff5005ee31fe3c96dfc00b86096
1/* 2 * Copyright (C) 2013 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 17package com.android.internal.telephony.imsphone; 18 19import java.io.FileDescriptor; 20import java.io.PrintWriter; 21import java.util.ArrayList; 22import java.util.HashMap; 23import java.util.List; 24import java.util.Map; 25 26import android.app.PendingIntent; 27import android.content.BroadcastReceiver; 28import android.content.Context; 29import android.content.Intent; 30import android.content.IntentFilter; 31import android.content.SharedPreferences; 32import android.os.AsyncResult; 33import android.os.Bundle; 34import android.os.Handler; 35import android.os.Message; 36import android.os.PersistableBundle; 37import android.os.Registrant; 38import android.os.RegistrantList; 39import android.os.RemoteException; 40import android.os.SystemProperties; 41import android.provider.Settings; 42import android.telephony.CarrierConfigManager; 43import android.text.TextUtils; 44import android.preference.PreferenceManager; 45import android.telecom.ConferenceParticipant; 46import android.telecom.VideoProfile; 47import android.telephony.DisconnectCause; 48import android.telephony.PhoneNumberUtils; 49import android.telephony.Rlog; 50import android.telephony.ServiceState; 51import android.telephony.SubscriptionManager; 52 53import com.android.ims.ImsCall; 54import com.android.ims.ImsCallProfile; 55import com.android.ims.ImsConfig; 56import com.android.ims.ImsConfigListener; 57import com.android.ims.ImsConnectionStateListener; 58import com.android.ims.ImsEcbm; 59import com.android.ims.ImsException; 60import com.android.ims.ImsManager; 61import com.android.ims.ImsMultiEndpoint; 62import com.android.ims.ImsReasonInfo; 63import com.android.ims.ImsServiceClass; 64import com.android.ims.ImsSuppServiceNotification; 65import com.android.ims.ImsUtInterface; 66import com.android.ims.internal.IImsVideoCallProvider; 67import com.android.ims.internal.ImsVideoCallProviderWrapper; 68import com.android.internal.telephony.Call; 69import com.android.internal.telephony.CallStateException; 70import com.android.internal.telephony.CallTracker; 71import com.android.internal.telephony.CommandException; 72import com.android.internal.telephony.CommandsInterface; 73import com.android.internal.telephony.Connection; 74import com.android.internal.telephony.Phone; 75import com.android.internal.telephony.PhoneConstants; 76import com.android.internal.telephony.TelephonyEventLog; 77import com.android.internal.telephony.TelephonyProperties; 78import com.android.internal.telephony.gsm.SuppServiceNotification; 79 80/** 81 * {@hide} 82 */ 83public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { 84 static final String LOG_TAG = "ImsPhoneCallTracker"; 85 86 private static final boolean DBG = true; 87 88 // When true, dumps the state of ImsPhoneCallTracker after changes to foreground and background 89 // calls. This is helpful for debugging. 90 private static final boolean VERBOSE_STATE_LOGGING = false; /* stopship if true */ 91 92 //Indices map to ImsConfig.FeatureConstants 93 private boolean[] mImsFeatureEnabled = {false, false, false, false, false, false}; 94 private final String[] mImsFeatureStrings = {"VoLTE", "ViLTE", "VoWiFi", "ViWiFi", 95 "UTLTE", "UTWiFi"}; 96 97 private TelephonyEventLog mEventLog; 98 99 private BroadcastReceiver mReceiver = new BroadcastReceiver() { 100 @Override 101 public void onReceive(Context context, Intent intent) { 102 if (intent.getAction().equals(ImsManager.ACTION_IMS_INCOMING_CALL)) { 103 if (DBG) log("onReceive : incoming call intent"); 104 105 if (mImsManager == null) return; 106 107 if (mServiceId < 0) return; 108 109 try { 110 // Network initiated USSD will be treated by mImsUssdListener 111 boolean isUssd = intent.getBooleanExtra(ImsManager.EXTRA_USSD, false); 112 if (isUssd) { 113 if (DBG) log("onReceive : USSD"); 114 mUssdSession = mImsManager.takeCall(mServiceId, intent, mImsUssdListener); 115 if (mUssdSession != null) { 116 mUssdSession.accept(ImsCallProfile.CALL_TYPE_VOICE); 117 } 118 return; 119 } 120 121 boolean isUnknown = intent.getBooleanExtra(ImsManager.EXTRA_IS_UNKNOWN_CALL, 122 false); 123 if (DBG) { 124 log("onReceive : isUnknown = " + isUnknown + 125 " fg = " + mForegroundCall.getState() + 126 " bg = " + mBackgroundCall.getState()); 127 } 128 129 // Normal MT/Unknown call 130 ImsCall imsCall = mImsManager.takeCall(mServiceId, intent, mImsCallListener); 131 ImsPhoneConnection conn = new ImsPhoneConnection(mPhone, imsCall, 132 ImsPhoneCallTracker.this, 133 (isUnknown? mForegroundCall: mRingingCall), isUnknown); 134 135 // If there is an active call. 136 if (mForegroundCall.hasConnections()) { 137 ImsCall activeCall = mForegroundCall.getFirstConnection().getImsCall(); 138 boolean answeringWillDisconnect = 139 shouldDisconnectActiveCallOnAnswer(activeCall, imsCall); 140 conn.setActiveCallDisconnectedOnAnswer(answeringWillDisconnect); 141 } 142 conn.setAllowAddCallDuringVideoCall(mAllowAddCallDuringVideoCall); 143 addConnection(conn); 144 145 setVideoCallProvider(conn, imsCall); 146 147 mEventLog.writeOnImsCallReceive(imsCall.getSession()); 148 149 if (isUnknown) { 150 mPhone.notifyUnknownConnection(conn); 151 } else { 152 if ((mForegroundCall.getState() != ImsPhoneCall.State.IDLE) || 153 (mBackgroundCall.getState() != ImsPhoneCall.State.IDLE)) { 154 conn.update(imsCall, ImsPhoneCall.State.WAITING); 155 } 156 157 mPhone.notifyNewRingingConnection(conn); 158 mPhone.notifyIncomingRing(); 159 } 160 161 updatePhoneState(); 162 mPhone.notifyPreciseCallStateChanged(); 163 } catch (ImsException e) { 164 loge("onReceive : exception " + e); 165 } catch (RemoteException e) { 166 } 167 } else if (intent.getAction().equals( 168 CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) { 169 int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY, 170 SubscriptionManager.INVALID_SUBSCRIPTION_ID); 171 if (subId == mPhone.getSubId()) { 172 cacheCarrierConfiguration(subId); 173 log("onReceive : Updating mAllowEmergencyVideoCalls = " + 174 mAllowEmergencyVideoCalls); 175 } 176 } 177 } 178 }; 179 180 //***** Constants 181 182 static final int MAX_CONNECTIONS = 7; 183 static final int MAX_CONNECTIONS_PER_CALL = 5; 184 185 private static final int EVENT_HANGUP_PENDINGMO = 18; 186 private static final int EVENT_RESUME_BACKGROUND = 19; 187 private static final int EVENT_DIAL_PENDINGMO = 20; 188 private static final int EVENT_EXIT_ECBM_BEFORE_PENDINGMO = 21; 189 private static final int EVENT_VT_DATA_USAGE_UPDATE = 22; 190 191 private static final int TIMEOUT_HANGUP_PENDINGMO = 500; 192 193 //***** Instance Variables 194 private ArrayList<ImsPhoneConnection> mConnections = new ArrayList<ImsPhoneConnection>(); 195 private RegistrantList mVoiceCallEndedRegistrants = new RegistrantList(); 196 private RegistrantList mVoiceCallStartedRegistrants = new RegistrantList(); 197 198 public ImsPhoneCall mRingingCall = new ImsPhoneCall(this, ImsPhoneCall.CONTEXT_RINGING); 199 public ImsPhoneCall mForegroundCall = new ImsPhoneCall(this, 200 ImsPhoneCall.CONTEXT_FOREGROUND); 201 public ImsPhoneCall mBackgroundCall = new ImsPhoneCall(this, 202 ImsPhoneCall.CONTEXT_BACKGROUND); 203 public ImsPhoneCall mHandoverCall = new ImsPhoneCall(this, ImsPhoneCall.CONTEXT_HANDOVER); 204 205 // Hold aggregated video call data usage for each video call since boot. 206 // The ImsCall's call id is the key of the map. 207 private final HashMap<Integer, Long> mVtDataUsageMap = new HashMap<>(); 208 private volatile long mTotalVtDataUsage = 0; 209 210 private ImsPhoneConnection mPendingMO; 211 private int mClirMode = CommandsInterface.CLIR_DEFAULT; 212 private Object mSyncHold = new Object(); 213 214 private ImsCall mUssdSession = null; 215 private Message mPendingUssd = null; 216 217 ImsPhone mPhone; 218 219 private boolean mDesiredMute = false; // false = mute off 220 private boolean mOnHoldToneStarted = false; 221 private int mOnHoldToneId = -1; 222 223 private PhoneConstants.State mState = PhoneConstants.State.IDLE; 224 225 private ImsManager mImsManager; 226 private int mServiceId = -1; 227 228 private Call.SrvccState mSrvccState = Call.SrvccState.NONE; 229 230 private boolean mIsInEmergencyCall = false; 231 232 private int pendingCallClirMode; 233 private int mPendingCallVideoState; 234 private Bundle mPendingIntentExtras; 235 private boolean pendingCallInEcm = false; 236 private boolean mSwitchingFgAndBgCalls = false; 237 private ImsCall mCallExpectedToResume = null; 238 private boolean mAllowEmergencyVideoCalls = false; 239 240 /** 241 * Carrier configuration option which determines if video calls which have been downgraded to an 242 * audio call should be treated as if they are still video calls. 243 */ 244 private boolean mTreatDowngradedVideoCallsAsVideoCalls = false; 245 246 /** 247 * Carrier configuration option which determines if an ongoing video call over wifi should be 248 * dropped when an audio call is answered. 249 */ 250 private boolean mDropVideoCallWhenAnsweringAudioCall = false; 251 252 /** 253 * Carrier configuration option which determines whether adding a call during a video call 254 * should be allowed. 255 */ 256 private boolean mAllowAddCallDuringVideoCall = true; 257 258 //***** Events 259 260 261 //***** Constructors 262 263 public ImsPhoneCallTracker(ImsPhone phone) { 264 this.mPhone = phone; 265 266 mEventLog = new TelephonyEventLog(mPhone.getPhoneId()); 267 268 IntentFilter intentfilter = new IntentFilter(); 269 intentfilter.addAction(ImsManager.ACTION_IMS_INCOMING_CALL); 270 intentfilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); 271 mPhone.getContext().registerReceiver(mReceiver, intentfilter); 272 cacheCarrierConfiguration(mPhone.getSubId()); 273 274 Thread t = new Thread() { 275 public void run() { 276 getImsService(); 277 } 278 }; 279 t.start(); 280 } 281 282 private PendingIntent createIncomingCallPendingIntent() { 283 Intent intent = new Intent(ImsManager.ACTION_IMS_INCOMING_CALL); 284 intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); 285 return PendingIntent.getBroadcast(mPhone.getContext(), 0, intent, 286 PendingIntent.FLAG_UPDATE_CURRENT); 287 } 288 289 private void getImsService() { 290 if (DBG) log("getImsService"); 291 mImsManager = ImsManager.getInstance(mPhone.getContext(), mPhone.getPhoneId()); 292 try { 293 mServiceId = mImsManager.open(ImsServiceClass.MMTEL, 294 createIncomingCallPendingIntent(), 295 mImsConnectionStateListener); 296 297 mImsManager.setImsConfigListener(mImsConfigListener); 298 299 // Get the ECBM interface and set IMSPhone's listener object for notifications 300 getEcbmInterface().setEcbmStateListener(mPhone.getImsEcbmStateListener()); 301 if (mPhone.isInEcm()) { 302 // Call exit ECBM which will invoke onECBMExited 303 mPhone.exitEmergencyCallbackMode(); 304 } 305 int mPreferredTtyMode = Settings.Secure.getInt( 306 mPhone.getContext().getContentResolver(), 307 Settings.Secure.PREFERRED_TTY_MODE, 308 Phone.TTY_MODE_OFF); 309 mImsManager.setUiTTYMode(mPhone.getContext(), mServiceId, mPreferredTtyMode, null); 310 311 ImsMultiEndpoint multiEndpoint = getMultiEndpointInterface(); 312 if (multiEndpoint != null) { 313 multiEndpoint.setExternalCallStateListener( 314 mPhone.getExternalCallTracker().getExternalCallStateListener()); 315 } 316 } catch (ImsException e) { 317 loge("getImsService: " + e); 318 //Leave mImsManager as null, then CallStateException will be thrown when dialing 319 mImsManager = null; 320 } 321 } 322 323 public void dispose() { 324 if (DBG) log("dispose"); 325 mRingingCall.dispose(); 326 mBackgroundCall.dispose(); 327 mForegroundCall.dispose(); 328 mHandoverCall.dispose(); 329 330 clearDisconnected(); 331 mPhone.getContext().unregisterReceiver(mReceiver); 332 } 333 334 @Override 335 protected void finalize() { 336 log("ImsPhoneCallTracker finalized"); 337 } 338 339 //***** Instance Methods 340 341 //***** Public Methods 342 @Override 343 public void registerForVoiceCallStarted(Handler h, int what, Object obj) { 344 Registrant r = new Registrant(h, what, obj); 345 mVoiceCallStartedRegistrants.add(r); 346 } 347 348 @Override 349 public void unregisterForVoiceCallStarted(Handler h) { 350 mVoiceCallStartedRegistrants.remove(h); 351 } 352 353 @Override 354 public void registerForVoiceCallEnded(Handler h, int what, Object obj) { 355 Registrant r = new Registrant(h, what, obj); 356 mVoiceCallEndedRegistrants.add(r); 357 } 358 359 @Override 360 public void unregisterForVoiceCallEnded(Handler h) { 361 mVoiceCallEndedRegistrants.remove(h); 362 } 363 364 public Connection dial(String dialString, int videoState, Bundle intentExtras) throws 365 CallStateException { 366 SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mPhone.getContext()); 367 int oirMode = sp.getInt(Phone.CLIR_KEY, CommandsInterface.CLIR_DEFAULT); 368 return dial(dialString, oirMode, videoState, intentExtras); 369 } 370 371 /** 372 * oirMode is one of the CLIR_ constants 373 */ 374 synchronized Connection 375 dial(String dialString, int clirMode, int videoState, Bundle intentExtras) 376 throws CallStateException { 377 boolean isPhoneInEcmMode = isPhoneInEcbMode(); 378 boolean isEmergencyNumber = PhoneNumberUtils.isEmergencyNumber(dialString); 379 380 if (DBG) log("dial clirMode=" + clirMode); 381 382 // note that this triggers call state changed notif 383 clearDisconnected(); 384 385 if (mImsManager == null) { 386 throw new CallStateException("service not available"); 387 } 388 389 if (!canDial()) { 390 throw new CallStateException("cannot dial in current state"); 391 } 392 393 if (isPhoneInEcmMode && isEmergencyNumber) { 394 handleEcmTimer(ImsPhone.CANCEL_ECM_TIMER); 395 } 396 397 // If the call is to an emergency number and the carrier does not support video emergency 398 // calls, dial as an audio-only call. 399 if (isEmergencyNumber && VideoProfile.isVideo(videoState) && 400 !mAllowEmergencyVideoCalls) { 401 loge("dial: carrier does not support video emergency calls; downgrade to audio-only"); 402 videoState = VideoProfile.STATE_AUDIO_ONLY; 403 } 404 405 boolean holdBeforeDial = false; 406 407 // The new call must be assigned to the foreground call. 408 // That call must be idle, so place anything that's 409 // there on hold 410 if (mForegroundCall.getState() == ImsPhoneCall.State.ACTIVE) { 411 if (mBackgroundCall.getState() != ImsPhoneCall.State.IDLE) { 412 //we should have failed in !canDial() above before we get here 413 throw new CallStateException("cannot dial in current state"); 414 } 415 // foreground call is empty for the newly dialed connection 416 holdBeforeDial = true; 417 // Cache the video state for pending MO call. 418 mPendingCallVideoState = videoState; 419 mPendingIntentExtras = intentExtras; 420 switchWaitingOrHoldingAndActive(); 421 } 422 423 ImsPhoneCall.State fgState = ImsPhoneCall.State.IDLE; 424 ImsPhoneCall.State bgState = ImsPhoneCall.State.IDLE; 425 426 mClirMode = clirMode; 427 428 synchronized (mSyncHold) { 429 if (holdBeforeDial) { 430 fgState = mForegroundCall.getState(); 431 bgState = mBackgroundCall.getState(); 432 433 //holding foreground call failed 434 if (fgState == ImsPhoneCall.State.ACTIVE) { 435 throw new CallStateException("cannot dial in current state"); 436 } 437 438 //holding foreground call succeeded 439 if (bgState == ImsPhoneCall.State.HOLDING) { 440 holdBeforeDial = false; 441 } 442 } 443 444 mPendingMO = new ImsPhoneConnection(mPhone, 445 checkForTestEmergencyNumber(dialString), this, mForegroundCall, 446 isEmergencyNumber); 447 mPendingMO.setVideoState(videoState); 448 } 449 addConnection(mPendingMO); 450 451 if (!holdBeforeDial) { 452 if ((!isPhoneInEcmMode) || (isPhoneInEcmMode && isEmergencyNumber)) { 453 dialInternal(mPendingMO, clirMode, videoState, intentExtras); 454 } else { 455 try { 456 getEcbmInterface().exitEmergencyCallbackMode(); 457 } catch (ImsException e) { 458 e.printStackTrace(); 459 throw new CallStateException("service not available"); 460 } 461 mPhone.setOnEcbModeExitResponse(this, EVENT_EXIT_ECM_RESPONSE_CDMA, null); 462 pendingCallClirMode = clirMode; 463 mPendingCallVideoState = videoState; 464 pendingCallInEcm = true; 465 } 466 } 467 468 updatePhoneState(); 469 mPhone.notifyPreciseCallStateChanged(); 470 471 return mPendingMO; 472 } 473 474 /** 475 * Caches frequently used carrier configuration items locally. 476 * 477 * @param subId The sub id. 478 */ 479 private void cacheCarrierConfiguration(int subId) { 480 CarrierConfigManager carrierConfigManager = (CarrierConfigManager) 481 mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); 482 if (carrierConfigManager == null) { 483 loge("cacheCarrierConfiguration: No carrier config service found."); 484 return; 485 } 486 487 PersistableBundle carrierConfig = carrierConfigManager.getConfigForSubId(subId); 488 if (carrierConfig == null) { 489 loge("cacheCarrierConfiguration: Empty carrier config."); 490 return; 491 } 492 493 mAllowEmergencyVideoCalls = 494 carrierConfig.getBoolean(CarrierConfigManager.KEY_ALLOW_EMERGENCY_VIDEO_CALLS_BOOL); 495 mTreatDowngradedVideoCallsAsVideoCalls = 496 carrierConfig.getBoolean( 497 CarrierConfigManager.KEY_TREAT_DOWNGRADED_VIDEO_CALLS_AS_VIDEO_CALLS_BOOL); 498 mDropVideoCallWhenAnsweringAudioCall = 499 carrierConfig.getBoolean( 500 CarrierConfigManager.KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL); 501 mAllowAddCallDuringVideoCall = 502 carrierConfig.getBoolean( 503 CarrierConfigManager.KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL); 504 } 505 506 private void handleEcmTimer(int action) { 507 mPhone.handleTimerInEmergencyCallbackMode(action); 508 switch (action) { 509 case ImsPhone.CANCEL_ECM_TIMER: 510 break; 511 case ImsPhone.RESTART_ECM_TIMER: 512 break; 513 default: 514 log("handleEcmTimer, unsupported action " + action); 515 } 516 } 517 518 private void dialInternal(ImsPhoneConnection conn, int clirMode, int videoState, 519 Bundle intentExtras) { 520 521 if (conn == null) { 522 return; 523 } 524 525 if (conn.getAddress()== null || conn.getAddress().length() == 0 526 || conn.getAddress().indexOf(PhoneNumberUtils.WILD) >= 0) { 527 // Phone number is invalid 528 conn.setDisconnectCause(DisconnectCause.INVALID_NUMBER); 529 sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO); 530 return; 531 } 532 533 // Always unmute when initiating a new call 534 setMute(false); 535 int serviceType = PhoneNumberUtils.isEmergencyNumber(conn.getAddress()) ? 536 ImsCallProfile.SERVICE_TYPE_EMERGENCY : ImsCallProfile.SERVICE_TYPE_NORMAL; 537 int callType = ImsCallProfile.getCallTypeFromVideoState(videoState); 538 //TODO(vt): Is this sufficient? At what point do we know the video state of the call? 539 conn.setVideoState(videoState); 540 541 try { 542 String[] callees = new String[] { conn.getAddress() }; 543 ImsCallProfile profile = mImsManager.createCallProfile(mServiceId, 544 serviceType, callType); 545 profile.setCallExtraInt(ImsCallProfile.EXTRA_OIR, clirMode); 546 547 // Translate call subject intent-extra from Telecom-specific extra key to the 548 // ImsCallProfile key. 549 if (intentExtras != null) { 550 if (intentExtras.containsKey(android.telecom.TelecomManager.EXTRA_CALL_SUBJECT)) { 551 intentExtras.putString(ImsCallProfile.EXTRA_DISPLAY_TEXT, 552 cleanseInstantLetteringMessage(intentExtras.getString( 553 android.telecom.TelecomManager.EXTRA_CALL_SUBJECT)) 554 ); 555 } 556 557 if (intentExtras.containsKey(ImsCallProfile.EXTRA_IS_CALL_PULL)) { 558 profile.mCallExtras.putBoolean(ImsCallProfile.EXTRA_IS_CALL_PULL, 559 intentExtras.getBoolean(ImsCallProfile.EXTRA_IS_CALL_PULL)); 560 } 561 562 // Pack the OEM-specific call extras. 563 profile.mCallExtras.putBundle(ImsCallProfile.EXTRA_OEM_EXTRAS, intentExtras); 564 565 // NOTE: Extras to be sent over the network are packed into the 566 // intentExtras individually, with uniquely defined keys. 567 // These key-value pairs are processed by IMS Service before 568 // being sent to the lower layers/to the network. 569 } 570 571 ImsCall imsCall = mImsManager.makeCall(mServiceId, profile, 572 callees, mImsCallListener); 573 conn.setImsCall(imsCall); 574 575 mEventLog.writeOnImsCallStart(imsCall.getSession()); 576 577 setVideoCallProvider(conn, imsCall); 578 conn.setAllowAddCallDuringVideoCall(mAllowAddCallDuringVideoCall); 579 } catch (ImsException e) { 580 loge("dialInternal : " + e); 581 conn.setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED); 582 sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO); 583 } catch (RemoteException e) { 584 } 585 } 586 587 /** 588 * Accepts a call with the specified video state. The video state is the video state that the 589 * user has agreed upon in the InCall UI. 590 * 591 * @param videoState The video State 592 * @throws CallStateException 593 */ 594 public void acceptCall (int videoState) throws CallStateException { 595 if (DBG) log("acceptCall"); 596 597 if (mForegroundCall.getState().isAlive() 598 && mBackgroundCall.getState().isAlive()) { 599 throw new CallStateException("cannot accept call"); 600 } 601 602 if ((mRingingCall.getState() == ImsPhoneCall.State.WAITING) 603 && mForegroundCall.getState().isAlive()) { 604 setMute(false); 605 606 boolean answeringWillDisconnect = false; 607 ImsCall activeCall = mForegroundCall.getImsCall(); 608 ImsCall ringingCall = mRingingCall.getImsCall(); 609 if (mForegroundCall.hasConnections() && mRingingCall.hasConnections()) { 610 answeringWillDisconnect = 611 shouldDisconnectActiveCallOnAnswer(activeCall, ringingCall); 612 } 613 614 // Cache video state for pending MT call. 615 mPendingCallVideoState = videoState; 616 617 if (answeringWillDisconnect) { 618 // We need to disconnect the foreground call before answering the background call. 619 mForegroundCall.hangup(); 620 try { 621 ringingCall.accept(ImsCallProfile.getCallTypeFromVideoState(videoState)); 622 } catch (ImsException e) { 623 throw new CallStateException("cannot accept call"); 624 } 625 } else { 626 switchWaitingOrHoldingAndActive(); 627 } 628 } else if (mRingingCall.getState().isRinging()) { 629 if (DBG) log("acceptCall: incoming..."); 630 // Always unmute when answering a new call 631 setMute(false); 632 try { 633 ImsCall imsCall = mRingingCall.getImsCall(); 634 if (imsCall != null) { 635 imsCall.accept(ImsCallProfile.getCallTypeFromVideoState(videoState)); 636 mEventLog.writeOnImsCallAccept(imsCall.getSession()); 637 } else { 638 throw new CallStateException("no valid ims call"); 639 } 640 } catch (ImsException e) { 641 throw new CallStateException("cannot accept call"); 642 } 643 } else { 644 throw new CallStateException("phone not ringing"); 645 } 646 } 647 648 public void rejectCall () throws CallStateException { 649 if (DBG) log("rejectCall"); 650 651 if (mRingingCall.getState().isRinging()) { 652 hangup(mRingingCall); 653 } else { 654 throw new CallStateException("phone not ringing"); 655 } 656 } 657 658 659 private void switchAfterConferenceSuccess() { 660 if (DBG) log("switchAfterConferenceSuccess fg =" + mForegroundCall.getState() + 661 ", bg = " + mBackgroundCall.getState()); 662 663 if (mBackgroundCall.getState() == ImsPhoneCall.State.HOLDING) { 664 log("switchAfterConferenceSuccess"); 665 mForegroundCall.switchWith(mBackgroundCall); 666 } 667 } 668 669 public void switchWaitingOrHoldingAndActive() throws CallStateException { 670 if (DBG) log("switchWaitingOrHoldingAndActive"); 671 672 if (mRingingCall.getState() == ImsPhoneCall.State.INCOMING) { 673 throw new CallStateException("cannot be in the incoming state"); 674 } 675 676 if (mForegroundCall.getState() == ImsPhoneCall.State.ACTIVE) { 677 ImsCall imsCall = mForegroundCall.getImsCall(); 678 if (imsCall == null) { 679 throw new CallStateException("no ims call"); 680 } 681 682 // Swap the ImsCalls pointed to by the foreground and background ImsPhoneCalls. 683 // If hold or resume later fails, we will swap them back. 684 mSwitchingFgAndBgCalls = true; 685 mCallExpectedToResume = mBackgroundCall.getImsCall(); 686 mForegroundCall.switchWith(mBackgroundCall); 687 688 // Hold the foreground call; once the foreground call is held, the background call will 689 // be resumed. 690 try { 691 imsCall.hold(); 692 mEventLog.writeOnImsCallHold(imsCall.getSession()); 693 694 // If there is no background call to resume, then don't expect there to be a switch. 695 if (mCallExpectedToResume == null) { 696 mSwitchingFgAndBgCalls = false; 697 } 698 } catch (ImsException e) { 699 mForegroundCall.switchWith(mBackgroundCall); 700 throw new CallStateException(e.getMessage()); 701 } 702 } else if (mBackgroundCall.getState() == ImsPhoneCall.State.HOLDING) { 703 resumeWaitingOrHolding(); 704 } 705 } 706 707 public void 708 conference() { 709 if (DBG) log("conference"); 710 711 ImsCall fgImsCall = mForegroundCall.getImsCall(); 712 if (fgImsCall == null) { 713 log("conference no foreground ims call"); 714 return; 715 } 716 717 ImsCall bgImsCall = mBackgroundCall.getImsCall(); 718 if (bgImsCall == null) { 719 log("conference no background ims call"); 720 return; 721 } 722 723 // Keep track of the connect time of the earliest call so that it can be set on the 724 // {@code ImsConference} when it is created. 725 long foregroundConnectTime = mForegroundCall.getEarliestConnectTime(); 726 long backgroundConnectTime = mBackgroundCall.getEarliestConnectTime(); 727 long conferenceConnectTime; 728 if (foregroundConnectTime > 0 && backgroundConnectTime > 0) { 729 conferenceConnectTime = Math.min(mForegroundCall.getEarliestConnectTime(), 730 mBackgroundCall.getEarliestConnectTime()); 731 log("conference - using connect time = " + conferenceConnectTime); 732 } else if (foregroundConnectTime > 0) { 733 log("conference - bg call connect time is 0; using fg = " + foregroundConnectTime); 734 conferenceConnectTime = foregroundConnectTime; 735 } else { 736 log("conference - fg call connect time is 0; using bg = " + backgroundConnectTime); 737 conferenceConnectTime = backgroundConnectTime; 738 } 739 740 ImsPhoneConnection foregroundConnection = mForegroundCall.getFirstConnection(); 741 if (foregroundConnection != null) { 742 foregroundConnection.setConferenceConnectTime(conferenceConnectTime); 743 } 744 745 try { 746 fgImsCall.merge(bgImsCall); 747 } catch (ImsException e) { 748 log("conference " + e.getMessage()); 749 } 750 } 751 752 public void 753 explicitCallTransfer() { 754 //TODO : implement 755 } 756 757 public void 758 clearDisconnected() { 759 if (DBG) log("clearDisconnected"); 760 761 internalClearDisconnected(); 762 763 updatePhoneState(); 764 mPhone.notifyPreciseCallStateChanged(); 765 } 766 767 public boolean 768 canConference() { 769 return mForegroundCall.getState() == ImsPhoneCall.State.ACTIVE 770 && mBackgroundCall.getState() == ImsPhoneCall.State.HOLDING 771 && !mBackgroundCall.isFull() 772 && !mForegroundCall.isFull(); 773 } 774 775 public boolean 776 canDial() { 777 boolean ret; 778 int serviceState = mPhone.getServiceState().getState(); 779 String disableCall = SystemProperties.get( 780 TelephonyProperties.PROPERTY_DISABLE_CALL, "false"); 781 782 ret = (serviceState != ServiceState.STATE_POWER_OFF) 783 && mPendingMO == null 784 && !mRingingCall.isRinging() 785 && !disableCall.equals("true") 786 && (!mForegroundCall.getState().isAlive() 787 || !mBackgroundCall.getState().isAlive()); 788 789 return ret; 790 } 791 792 public boolean 793 canTransfer() { 794 return mForegroundCall.getState() == ImsPhoneCall.State.ACTIVE 795 && mBackgroundCall.getState() == ImsPhoneCall.State.HOLDING; 796 } 797 798 //***** Private Instance Methods 799 800 private void 801 internalClearDisconnected() { 802 mRingingCall.clearDisconnected(); 803 mForegroundCall.clearDisconnected(); 804 mBackgroundCall.clearDisconnected(); 805 mHandoverCall.clearDisconnected(); 806 } 807 808 private void 809 updatePhoneState() { 810 PhoneConstants.State oldState = mState; 811 812 if (mRingingCall.isRinging()) { 813 mState = PhoneConstants.State.RINGING; 814 } else if (mPendingMO != null || 815 !(mForegroundCall.isIdle() && mBackgroundCall.isIdle())) { 816 mState = PhoneConstants.State.OFFHOOK; 817 } else { 818 mState = PhoneConstants.State.IDLE; 819 } 820 821 if (mState == PhoneConstants.State.IDLE && oldState != mState) { 822 mVoiceCallEndedRegistrants.notifyRegistrants( 823 new AsyncResult(null, null, null)); 824 } else if (oldState == PhoneConstants.State.IDLE && oldState != mState) { 825 mVoiceCallStartedRegistrants.notifyRegistrants ( 826 new AsyncResult(null, null, null)); 827 } 828 829 if (DBG) log("updatePhoneState oldState=" + oldState + ", newState=" + mState); 830 831 if (mState != oldState) { 832 mPhone.notifyPhoneStateChanged(); 833 mEventLog.writePhoneState(mState); 834 } 835 } 836 837 private void 838 handleRadioNotAvailable() { 839 // handlePollCalls will clear out its 840 // call list when it gets the CommandException 841 // error result from this 842 pollCallsWhenSafe(); 843 } 844 845 private void 846 dumpState() { 847 List l; 848 849 log("Phone State:" + mState); 850 851 log("Ringing call: " + mRingingCall.toString()); 852 853 l = mRingingCall.getConnections(); 854 for (int i = 0, s = l.size(); i < s; i++) { 855 log(l.get(i).toString()); 856 } 857 858 log("Foreground call: " + mForegroundCall.toString()); 859 860 l = mForegroundCall.getConnections(); 861 for (int i = 0, s = l.size(); i < s; i++) { 862 log(l.get(i).toString()); 863 } 864 865 log("Background call: " + mBackgroundCall.toString()); 866 867 l = mBackgroundCall.getConnections(); 868 for (int i = 0, s = l.size(); i < s; i++) { 869 log(l.get(i).toString()); 870 } 871 872 } 873 874 //***** Called from ImsPhone 875 876 public void setUiTTYMode(int uiTtyMode, Message onComplete) { 877 if (mImsManager == null) { 878 mPhone.sendErrorResponse(onComplete, getImsManagerIsNullException()); 879 return; 880 } 881 882 try { 883 mImsManager.setUiTTYMode(mPhone.getContext(), mServiceId, uiTtyMode, onComplete); 884 } catch (ImsException e) { 885 loge("setTTYMode : " + e); 886 mPhone.sendErrorResponse(onComplete, e); 887 } 888 } 889 890 public void setMute(boolean mute) { 891 mDesiredMute = mute; 892 mForegroundCall.setMute(mute); 893 } 894 895 public boolean getMute() { 896 return mDesiredMute; 897 } 898 899 public void sendDtmf(char c, Message result) { 900 if (DBG) log("sendDtmf"); 901 902 ImsCall imscall = mForegroundCall.getImsCall(); 903 if (imscall != null) { 904 imscall.sendDtmf(c, result); 905 } 906 } 907 908 public void 909 startDtmf(char c) { 910 if (DBG) log("startDtmf"); 911 912 ImsCall imscall = mForegroundCall.getImsCall(); 913 if (imscall != null) { 914 imscall.startDtmf(c); 915 } else { 916 loge("startDtmf : no foreground call"); 917 } 918 } 919 920 public void 921 stopDtmf() { 922 if (DBG) log("stopDtmf"); 923 924 ImsCall imscall = mForegroundCall.getImsCall(); 925 if (imscall != null) { 926 imscall.stopDtmf(); 927 } else { 928 loge("stopDtmf : no foreground call"); 929 } 930 } 931 932 //***** Called from ImsPhoneConnection 933 934 public void hangup (ImsPhoneConnection conn) throws CallStateException { 935 if (DBG) log("hangup connection"); 936 937 if (conn.getOwner() != this) { 938 throw new CallStateException ("ImsPhoneConnection " + conn 939 + "does not belong to ImsPhoneCallTracker " + this); 940 } 941 942 hangup(conn.getCall()); 943 } 944 945 //***** Called from ImsPhoneCall 946 947 public void hangup (ImsPhoneCall call) throws CallStateException { 948 if (DBG) log("hangup call"); 949 950 if (call.getConnections().size() == 0) { 951 throw new CallStateException("no connections"); 952 } 953 954 ImsCall imsCall = call.getImsCall(); 955 boolean rejectCall = false; 956 957 if (call == mRingingCall) { 958 if (Phone.DEBUG_PHONE) log("(ringing) hangup incoming"); 959 rejectCall = true; 960 } else if (call == mForegroundCall) { 961 if (call.isDialingOrAlerting()) { 962 if (Phone.DEBUG_PHONE) { 963 log("(foregnd) hangup dialing or alerting..."); 964 } 965 } else { 966 if (Phone.DEBUG_PHONE) { 967 log("(foregnd) hangup foreground"); 968 } 969 //held call will be resumed by onCallTerminated 970 } 971 } else if (call == mBackgroundCall) { 972 if (Phone.DEBUG_PHONE) { 973 log("(backgnd) hangup waiting or background"); 974 } 975 } else { 976 throw new CallStateException ("ImsPhoneCall " + call + 977 "does not belong to ImsPhoneCallTracker " + this); 978 } 979 980 call.onHangupLocal(); 981 982 try { 983 if (imsCall != null) { 984 if (rejectCall) { 985 imsCall.reject(ImsReasonInfo.CODE_USER_DECLINE); 986 mEventLog.writeOnImsCallReject(imsCall.getSession()); 987 } else { 988 imsCall.terminate(ImsReasonInfo.CODE_USER_TERMINATED); 989 mEventLog.writeOnImsCallTerminate(imsCall.getSession()); 990 } 991 } else if (mPendingMO != null && call == mForegroundCall) { 992 // is holding a foreground call 993 mPendingMO.update(null, ImsPhoneCall.State.DISCONNECTED); 994 mPendingMO.onDisconnect(); 995 removeConnection(mPendingMO); 996 mPendingMO = null; 997 updatePhoneState(); 998 removeMessages(EVENT_DIAL_PENDINGMO); 999 } 1000 } catch (ImsException e) { 1001 throw new CallStateException(e.getMessage()); 1002 } 1003 1004 mPhone.notifyPreciseCallStateChanged(); 1005 } 1006 1007 void callEndCleanupHandOverCallIfAny() { 1008 if (mHandoverCall.mConnections.size() > 0) { 1009 if (DBG) log("callEndCleanupHandOverCallIfAny, mHandoverCall.mConnections=" 1010 + mHandoverCall.mConnections); 1011 mHandoverCall.mConnections.clear(); 1012 mState = PhoneConstants.State.IDLE; 1013 } 1014 } 1015 1016 /* package */ 1017 void resumeWaitingOrHolding() throws CallStateException { 1018 if (DBG) log("resumeWaitingOrHolding"); 1019 1020 try { 1021 if (mForegroundCall.getState().isAlive()) { 1022 //resume foreground call after holding background call 1023 //they were switched before holding 1024 ImsCall imsCall = mForegroundCall.getImsCall(); 1025 if (imsCall != null) { 1026 imsCall.resume(); 1027 mEventLog.writeOnImsCallResume(imsCall.getSession()); 1028 } 1029 } else if (mRingingCall.getState() == ImsPhoneCall.State.WAITING) { 1030 //accept waiting call after holding background call 1031 ImsCall imsCall = mRingingCall.getImsCall(); 1032 if (imsCall != null) { 1033 imsCall.accept( 1034 ImsCallProfile.getCallTypeFromVideoState(mPendingCallVideoState)); 1035 mEventLog.writeOnImsCallAccept(imsCall.getSession()); 1036 } 1037 } else { 1038 //Just resume background call. 1039 //To distinguish resuming call with swapping calls 1040 //we do not switch calls.here 1041 //ImsPhoneConnection.update will chnage the parent when completed 1042 ImsCall imsCall = mBackgroundCall.getImsCall(); 1043 if (imsCall != null) { 1044 imsCall.resume(); 1045 mEventLog.writeOnImsCallResume(imsCall.getSession()); 1046 } 1047 } 1048 } catch (ImsException e) { 1049 throw new CallStateException(e.getMessage()); 1050 } 1051 } 1052 1053 public void sendUSSD (String ussdString, Message response) { 1054 if (DBG) log("sendUSSD"); 1055 1056 try { 1057 if (mUssdSession != null) { 1058 mUssdSession.sendUssd(ussdString); 1059 AsyncResult.forMessage(response, null, null); 1060 response.sendToTarget(); 1061 return; 1062 } 1063 1064 if (mImsManager == null) { 1065 mPhone.sendErrorResponse(response, getImsManagerIsNullException()); 1066 return; 1067 } 1068 1069 String[] callees = new String[] { ussdString }; 1070 ImsCallProfile profile = mImsManager.createCallProfile(mServiceId, 1071 ImsCallProfile.SERVICE_TYPE_NORMAL, ImsCallProfile.CALL_TYPE_VOICE); 1072 profile.setCallExtraInt(ImsCallProfile.EXTRA_DIALSTRING, 1073 ImsCallProfile.DIALSTRING_USSD); 1074 1075 mUssdSession = mImsManager.makeCall(mServiceId, profile, 1076 callees, mImsUssdListener); 1077 } catch (ImsException e) { 1078 loge("sendUSSD : " + e); 1079 mPhone.sendErrorResponse(response, e); 1080 } 1081 } 1082 1083 public void cancelUSSD() { 1084 if (mUssdSession == null) return; 1085 1086 try { 1087 mUssdSession.terminate(ImsReasonInfo.CODE_USER_TERMINATED); 1088 } catch (ImsException e) { 1089 } 1090 1091 } 1092 1093 private synchronized ImsPhoneConnection findConnection(final ImsCall imsCall) { 1094 for (ImsPhoneConnection conn : mConnections) { 1095 if (conn.getImsCall() == imsCall) { 1096 return conn; 1097 } 1098 } 1099 return null; 1100 } 1101 1102 private synchronized void removeConnection(ImsPhoneConnection conn) { 1103 mConnections.remove(conn); 1104 // If not emergency call is remaining, notify emergency call registrants 1105 if (mIsInEmergencyCall) { 1106 boolean isEmergencyCallInList = false; 1107 // if no emergency calls pending, set this to false 1108 for (ImsPhoneConnection imsPhoneConnection : mConnections) { 1109 if (imsPhoneConnection != null && imsPhoneConnection.isEmergency() == true) { 1110 isEmergencyCallInList = true; 1111 break; 1112 } 1113 } 1114 1115 if (!isEmergencyCallInList) { 1116 mIsInEmergencyCall = false; 1117 mPhone.sendEmergencyCallStateChange(false); 1118 } 1119 } 1120 } 1121 1122 private synchronized void addConnection(ImsPhoneConnection conn) { 1123 mConnections.add(conn); 1124 if (conn.isEmergency()) { 1125 mIsInEmergencyCall = true; 1126 mPhone.sendEmergencyCallStateChange(true); 1127 } 1128 } 1129 1130 private void processCallStateChange(ImsCall imsCall, ImsPhoneCall.State state, int cause) { 1131 if (DBG) log("processCallStateChange " + imsCall + " state=" + state + " cause=" + cause); 1132 // This method is called on onCallUpdate() where there is not necessarily a call state 1133 // change. In these situations, we'll ignore the state related updates and only process 1134 // the change in media capabilities (as expected). The default is to not ignore state 1135 // changes so we do not change existing behavior. 1136 processCallStateChange(imsCall, state, cause, false /* do not ignore state update */); 1137 } 1138 1139 private void processCallStateChange(ImsCall imsCall, ImsPhoneCall.State state, int cause, 1140 boolean ignoreState) { 1141 if (DBG) { 1142 log("processCallStateChange state=" + state + " cause=" + cause 1143 + " ignoreState=" + ignoreState); 1144 } 1145 1146 if (imsCall == null) return; 1147 1148 boolean changed = false; 1149 ImsPhoneConnection conn = findConnection(imsCall); 1150 1151 if (conn == null) { 1152 // TODO : what should be done? 1153 return; 1154 } 1155 1156 // processCallStateChange is triggered for onCallUpdated as well. 1157 // onCallUpdated should not modify the state of the call 1158 // It should modify only other capabilities of call through updateMediaCapabilities 1159 // State updates will be triggered through individual callbacks 1160 // i.e. onCallHeld, onCallResume, etc and conn.update will be responsible for the update 1161 conn.updateMediaCapabilities(imsCall); 1162 if (ignoreState) { 1163 conn.updateAddressDisplay(imsCall); 1164 conn.updateExtras(imsCall); 1165 1166 maybeSetVideoCallProvider(conn, imsCall); 1167 return; 1168 } 1169 1170 changed = conn.update(imsCall, state); 1171 if (state == ImsPhoneCall.State.DISCONNECTED) { 1172 changed = conn.onDisconnect(cause) || changed; 1173 //detach the disconnected connections 1174 conn.getCall().detach(conn); 1175 removeConnection(conn); 1176 } 1177 1178 if (changed) { 1179 if (conn.getCall() == mHandoverCall) return; 1180 updatePhoneState(); 1181 mPhone.notifyPreciseCallStateChanged(); 1182 } 1183 } 1184 1185 private void maybeSetVideoCallProvider(ImsPhoneConnection conn, ImsCall imsCall) { 1186 android.telecom.Connection.VideoProvider connVideoProvider = conn.getVideoProvider(); 1187 if (connVideoProvider != null || imsCall.getCallSession().getVideoCallProvider() == null) { 1188 return; 1189 } 1190 1191 try { 1192 setVideoCallProvider(conn, imsCall); 1193 } catch (RemoteException e) { 1194 loge("maybeSetVideoCallProvider: exception " + e); 1195 } 1196 } 1197 1198 private int getDisconnectCauseFromReasonInfo(ImsReasonInfo reasonInfo) { 1199 int cause = DisconnectCause.ERROR_UNSPECIFIED; 1200 1201 //int type = reasonInfo.getReasonType(); 1202 int code = reasonInfo.getCode(); 1203 switch (code) { 1204 case ImsReasonInfo.CODE_SIP_BAD_ADDRESS: 1205 case ImsReasonInfo.CODE_SIP_NOT_REACHABLE: 1206 return DisconnectCause.NUMBER_UNREACHABLE; 1207 1208 case ImsReasonInfo.CODE_SIP_BUSY: 1209 return DisconnectCause.BUSY; 1210 1211 case ImsReasonInfo.CODE_USER_TERMINATED: 1212 return DisconnectCause.LOCAL; 1213 1214 case ImsReasonInfo.CODE_LOCAL_CALL_DECLINE: 1215 return DisconnectCause.INCOMING_REJECTED; 1216 1217 case ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE: 1218 return DisconnectCause.NORMAL; 1219 1220 case ImsReasonInfo.CODE_SIP_REDIRECTED: 1221 case ImsReasonInfo.CODE_SIP_BAD_REQUEST: 1222 case ImsReasonInfo.CODE_SIP_FORBIDDEN: 1223 case ImsReasonInfo.CODE_SIP_NOT_ACCEPTABLE: 1224 case ImsReasonInfo.CODE_SIP_USER_REJECTED: 1225 case ImsReasonInfo.CODE_SIP_GLOBAL_ERROR: 1226 return DisconnectCause.SERVER_ERROR; 1227 1228 case ImsReasonInfo.CODE_SIP_SERVICE_UNAVAILABLE: 1229 case ImsReasonInfo.CODE_SIP_NOT_FOUND: 1230 case ImsReasonInfo.CODE_SIP_SERVER_ERROR: 1231 return DisconnectCause.SERVER_UNREACHABLE; 1232 1233 case ImsReasonInfo.CODE_LOCAL_NETWORK_ROAMING: 1234 case ImsReasonInfo.CODE_LOCAL_NETWORK_IP_CHANGED: 1235 case ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN: 1236 case ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE: 1237 case ImsReasonInfo.CODE_LOCAL_NOT_REGISTERED: 1238 case ImsReasonInfo.CODE_LOCAL_NETWORK_NO_LTE_COVERAGE: 1239 case ImsReasonInfo.CODE_LOCAL_NETWORK_NO_SERVICE: 1240 case ImsReasonInfo.CODE_LOCAL_CALL_VCC_ON_PROGRESSING: 1241 return DisconnectCause.OUT_OF_SERVICE; 1242 1243 case ImsReasonInfo.CODE_SIP_REQUEST_TIMEOUT: 1244 case ImsReasonInfo.CODE_TIMEOUT_1XX_WAITING: 1245 case ImsReasonInfo.CODE_TIMEOUT_NO_ANSWER: 1246 case ImsReasonInfo.CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE: 1247 return DisconnectCause.TIMED_OUT; 1248 1249 case ImsReasonInfo.CODE_LOCAL_LOW_BATTERY: 1250 case ImsReasonInfo.CODE_LOCAL_POWER_OFF: 1251 return DisconnectCause.POWER_OFF; 1252 1253 case ImsReasonInfo.CODE_FDN_BLOCKED: 1254 return DisconnectCause.FDN_BLOCKED; 1255 1256 case ImsReasonInfo.CODE_ANSWERED_ELSEWHERE: 1257 case ImsReasonInfo.CODE_CALL_END_CAUSE_CALL_PULL: 1258 return DisconnectCause.CALL_PULLED; 1259 default: 1260 } 1261 1262 return cause; 1263 } 1264 1265 /** 1266 * @return true if the phone is in Emergency Callback mode, otherwise false 1267 */ 1268 private boolean isPhoneInEcbMode() { 1269 return SystemProperties.getBoolean(TelephonyProperties.PROPERTY_INECM_MODE, false); 1270 } 1271 1272 /** 1273 * Before dialing pending MO request, check for the Emergency Callback mode. 1274 * If device is in Emergency callback mode, then exit the mode before dialing pending MO. 1275 */ 1276 private void dialPendingMO() { 1277 boolean isPhoneInEcmMode = isPhoneInEcbMode(); 1278 boolean isEmergencyNumber = mPendingMO.isEmergency(); 1279 if ((!isPhoneInEcmMode) || (isPhoneInEcmMode && isEmergencyNumber)) { 1280 sendEmptyMessage(EVENT_DIAL_PENDINGMO); 1281 } else { 1282 sendEmptyMessage(EVENT_EXIT_ECBM_BEFORE_PENDINGMO); 1283 } 1284 } 1285 1286 /** 1287 * Listen to the IMS call state change 1288 */ 1289 private ImsCall.Listener mImsCallListener = new ImsCall.Listener() { 1290 @Override 1291 public void onCallProgressing(ImsCall imsCall) { 1292 if (DBG) log("onCallProgressing"); 1293 1294 mPendingMO = null; 1295 processCallStateChange(imsCall, ImsPhoneCall.State.ALERTING, 1296 DisconnectCause.NOT_DISCONNECTED); 1297 mEventLog.writeOnImsCallProgressing(imsCall.getCallSession()); 1298 } 1299 1300 @Override 1301 public void onCallStarted(ImsCall imsCall) { 1302 if (DBG) log("onCallStarted"); 1303 1304 mPendingMO = null; 1305 processCallStateChange(imsCall, ImsPhoneCall.State.ACTIVE, 1306 DisconnectCause.NOT_DISCONNECTED); 1307 mEventLog.writeOnImsCallStarted(imsCall.getCallSession()); 1308 } 1309 1310 @Override 1311 public void onCallUpdated(ImsCall imsCall) { 1312 if (DBG) log("onCallUpdated"); 1313 if (imsCall == null) { 1314 return; 1315 } 1316 ImsPhoneConnection conn = findConnection(imsCall); 1317 if (conn != null) { 1318 processCallStateChange(imsCall, conn.getCall().mState, 1319 DisconnectCause.NOT_DISCONNECTED, true /*ignore state update*/); 1320 mEventLog.writeImsCallState(imsCall.getCallSession(), conn.getCall().mState); 1321 } 1322 } 1323 1324 /** 1325 * onCallStartFailed will be invoked when: 1326 * case 1) Dialing fails 1327 * case 2) Ringing call is disconnected by local or remote user 1328 */ 1329 @Override 1330 public void onCallStartFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) { 1331 if (DBG) log("onCallStartFailed reasonCode=" + reasonInfo.getCode()); 1332 1333 if (mPendingMO != null) { 1334 // To initiate dialing circuit-switched call 1335 if (reasonInfo.getCode() == ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED 1336 && mBackgroundCall.getState() == ImsPhoneCall.State.IDLE 1337 && mRingingCall.getState() == ImsPhoneCall.State.IDLE) { 1338 mForegroundCall.detach(mPendingMO); 1339 removeConnection(mPendingMO); 1340 mPendingMO.finalize(); 1341 mPendingMO = null; 1342 mPhone.initiateSilentRedial(); 1343 return; 1344 } else { 1345 mPendingMO = null; 1346 int cause = getDisconnectCauseFromReasonInfo(reasonInfo); 1347 processCallStateChange(imsCall, ImsPhoneCall.State.DISCONNECTED, cause); 1348 } 1349 mEventLog.writeOnImsCallStartFailed(imsCall.getCallSession(), reasonInfo); 1350 } 1351 } 1352 1353 @Override 1354 public void onCallTerminated(ImsCall imsCall, ImsReasonInfo reasonInfo) { 1355 if (DBG) log("onCallTerminated reasonCode=" + reasonInfo.getCode()); 1356 1357 ImsPhoneCall.State oldState = mForegroundCall.getState(); 1358 int cause = getDisconnectCauseFromReasonInfo(reasonInfo); 1359 ImsPhoneConnection conn = findConnection(imsCall); 1360 if (DBG) log("cause = " + cause + " conn = " + conn); 1361 1362 if (mOnHoldToneId == System.identityHashCode(conn)) { 1363 if (conn != null && mOnHoldToneStarted) { 1364 mPhone.stopOnHoldTone(conn); 1365 } 1366 mOnHoldToneStarted = false; 1367 mOnHoldToneId = -1; 1368 } 1369 if (conn != null && conn.isIncoming() && conn.getConnectTime() == 0) { 1370 // Missed 1371 if (cause == DisconnectCause.NORMAL) { 1372 cause = DisconnectCause.INCOMING_MISSED; 1373 } else { 1374 cause = DisconnectCause.INCOMING_REJECTED; 1375 } 1376 if (DBG) log("Incoming connection of 0 connect time detected - translated cause = " 1377 + cause); 1378 1379 } 1380 1381 if (cause == DisconnectCause.NORMAL && conn != null && conn.getImsCall().isMerged()) { 1382 // Call was terminated while it is merged instead of a remote disconnect. 1383 cause = DisconnectCause.IMS_MERGED_SUCCESSFULLY; 1384 } 1385 1386 mEventLog.writeOnImsCallTerminated(imsCall.getCallSession(), reasonInfo); 1387 1388 processCallStateChange(imsCall, ImsPhoneCall.State.DISCONNECTED, cause); 1389 if (mForegroundCall.getState() != ImsPhoneCall.State.ACTIVE) { 1390 if (mRingingCall.getState().isRinging()) { 1391 // Drop pending MO. We should address incoming call first 1392 mPendingMO = null; 1393 } else if (mPendingMO != null) { 1394 sendEmptyMessage(EVENT_DIAL_PENDINGMO); 1395 } 1396 } 1397 1398 if (mSwitchingFgAndBgCalls) { 1399 if (DBG) { 1400 log("onCallTerminated: Call terminated in the midst of Switching " + 1401 "Fg and Bg calls."); 1402 } 1403 // If we are the in midst of swapping FG and BG calls and the call that was 1404 // terminated was the one that we expected to resume, we need to swap the FG and 1405 // BG calls back. 1406 if (imsCall == mCallExpectedToResume) { 1407 if (DBG) { 1408 log("onCallTerminated: switching " + mForegroundCall + " with " 1409 + mBackgroundCall); 1410 } 1411 mForegroundCall.switchWith(mBackgroundCall); 1412 } 1413 // This call terminated in the midst of a switch after the other call was held, so 1414 // resume it back to ACTIVE state since the switch failed. 1415 if (mForegroundCall.getState() == ImsPhoneCall.State.HOLDING) { 1416 sendEmptyMessage(EVENT_RESUME_BACKGROUND); 1417 mSwitchingFgAndBgCalls = false; 1418 mCallExpectedToResume = null; 1419 } 1420 } 1421 } 1422 1423 @Override 1424 public void onCallHeld(ImsCall imsCall) { 1425 if (DBG) { 1426 if (mForegroundCall.getImsCall() == imsCall) { 1427 log("onCallHeld (fg) " + imsCall); 1428 } else if (mBackgroundCall.getImsCall() == imsCall) { 1429 log("onCallHeld (bg) " + imsCall); 1430 } 1431 } 1432 1433 synchronized (mSyncHold) { 1434 ImsPhoneCall.State oldState = mBackgroundCall.getState(); 1435 processCallStateChange(imsCall, ImsPhoneCall.State.HOLDING, 1436 DisconnectCause.NOT_DISCONNECTED); 1437 1438 // Note: If we're performing a switchWaitingOrHoldingAndActive, the call to 1439 // processCallStateChange above may have caused the mBackgroundCall and 1440 // mForegroundCall references below to change meaning. Watch out for this if you 1441 // are reading through this code. 1442 if (oldState == ImsPhoneCall.State.ACTIVE) { 1443 // Note: This case comes up when we have just held a call in response to a 1444 // switchWaitingOrHoldingAndActive. We now need to resume the background call. 1445 // The EVENT_RESUME_BACKGROUND causes resumeWaitingOrHolding to be called. 1446 if ((mForegroundCall.getState() == ImsPhoneCall.State.HOLDING) 1447 || (mRingingCall.getState() == ImsPhoneCall.State.WAITING)) { 1448 sendEmptyMessage(EVENT_RESUME_BACKGROUND); 1449 } else { 1450 //when multiple connections belong to background call, 1451 //only the first callback reaches here 1452 //otherwise the oldState is already HOLDING 1453 if (mPendingMO != null) { 1454 dialPendingMO(); 1455 } 1456 1457 // In this case there will be no call resumed, so we can assume that we 1458 // are done switching fg and bg calls now. 1459 // This may happen if there is no BG call and we are holding a call so that 1460 // we can dial another one. 1461 mSwitchingFgAndBgCalls = false; 1462 } 1463 } else if (oldState == ImsPhoneCall.State.IDLE && mSwitchingFgAndBgCalls) { 1464 // The other call terminated in the midst of a switch before this call was held, 1465 // so resume the foreground call back to ACTIVE state since the switch failed. 1466 if (mForegroundCall.getState() == ImsPhoneCall.State.HOLDING) { 1467 sendEmptyMessage(EVENT_RESUME_BACKGROUND); 1468 mSwitchingFgAndBgCalls = false; 1469 mCallExpectedToResume = null; 1470 } 1471 } 1472 } 1473 mEventLog.writeOnImsCallHeld(imsCall.getCallSession()); 1474 } 1475 1476 @Override 1477 public void onCallHoldFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) { 1478 if (DBG) log("onCallHoldFailed reasonCode=" + reasonInfo.getCode()); 1479 1480 synchronized (mSyncHold) { 1481 ImsPhoneCall.State bgState = mBackgroundCall.getState(); 1482 if (reasonInfo.getCode() == ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED) { 1483 // disconnected while processing hold 1484 if (mPendingMO != null) { 1485 dialPendingMO(); 1486 } 1487 } else if (bgState == ImsPhoneCall.State.ACTIVE) { 1488 mForegroundCall.switchWith(mBackgroundCall); 1489 1490 if (mPendingMO != null) { 1491 mPendingMO.setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED); 1492 sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO); 1493 } 1494 } 1495 mPhone.notifySuppServiceFailed(Phone.SuppService.HOLD); 1496 } 1497 mEventLog.writeOnImsCallHoldFailed(imsCall.getCallSession(), reasonInfo); 1498 } 1499 1500 @Override 1501 public void onCallResumed(ImsCall imsCall) { 1502 if (DBG) log("onCallResumed"); 1503 1504 // If we are the in midst of swapping FG and BG calls and the call we end up resuming 1505 // is not the one we expected, we likely had a resume failure and we need to swap the 1506 // FG and BG calls back. 1507 if (mSwitchingFgAndBgCalls) { 1508 if (imsCall != mCallExpectedToResume) { 1509 // If the call which resumed isn't as expected, we need to swap back to the 1510 // previous configuration; the swap has failed. 1511 if (DBG) { 1512 log("onCallResumed : switching " + mForegroundCall + " with " 1513 + mBackgroundCall); 1514 } 1515 mForegroundCall.switchWith(mBackgroundCall); 1516 } else { 1517 // The call which resumed is the one we expected to resume, so we can clear out 1518 // the mSwitchingFgAndBgCalls flag. 1519 if (DBG) { 1520 log("onCallResumed : expected call resumed."); 1521 } 1522 } 1523 mSwitchingFgAndBgCalls = false; 1524 mCallExpectedToResume = null; 1525 } 1526 processCallStateChange(imsCall, ImsPhoneCall.State.ACTIVE, 1527 DisconnectCause.NOT_DISCONNECTED); 1528 mEventLog.writeOnImsCallResumed(imsCall.getCallSession()); 1529 } 1530 1531 @Override 1532 public void onCallResumeFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) { 1533 if (mSwitchingFgAndBgCalls) { 1534 // If we are in the midst of swapping the FG and BG calls and 1535 // we got a resume fail, we need to swap back the FG and BG calls. 1536 // Since the FG call was held, will also try to resume the same. 1537 if (imsCall == mCallExpectedToResume) { 1538 if (DBG) { 1539 log("onCallResumeFailed : switching " + mForegroundCall + " with " 1540 + mBackgroundCall); 1541 } 1542 mForegroundCall.switchWith(mBackgroundCall); 1543 if (mForegroundCall.getState() == ImsPhoneCall.State.HOLDING) { 1544 sendEmptyMessage(EVENT_RESUME_BACKGROUND); 1545 } 1546 } 1547 1548 //Call swap is done, reset the relevant variables 1549 mCallExpectedToResume = null; 1550 mSwitchingFgAndBgCalls = false; 1551 } 1552 mPhone.notifySuppServiceFailed(Phone.SuppService.RESUME); 1553 mEventLog.writeOnImsCallResumeFailed(imsCall.getCallSession(), reasonInfo); 1554 } 1555 1556 @Override 1557 public void onCallResumeReceived(ImsCall imsCall) { 1558 if (DBG) log("onCallResumeReceived"); 1559 ImsPhoneConnection conn = findConnection(imsCall); 1560 if (conn != null && mOnHoldToneStarted) { 1561 mPhone.stopOnHoldTone(conn); 1562 mOnHoldToneStarted = false; 1563 } 1564 1565 SuppServiceNotification supp = new SuppServiceNotification(); 1566 // Type of notification: 0 = MO; 1 = MT 1567 // Refer SuppServiceNotification class documentation. 1568 supp.notificationType = 1; 1569 supp.code = SuppServiceNotification.MT_CODE_CALL_RETRIEVED; 1570 mPhone.notifySuppSvcNotification(supp); 1571 mEventLog.writeOnImsCallResumeReceived(imsCall.getCallSession()); 1572 } 1573 1574 @Override 1575 public void onCallHoldReceived(ImsCall imsCall) { 1576 if (DBG) log("onCallHoldReceived"); 1577 1578 ImsPhoneConnection conn = findConnection(imsCall); 1579 if (conn != null && conn.getState() == ImsPhoneCall.State.ACTIVE) { 1580 if (!mOnHoldToneStarted && ImsPhoneCall.isLocalTone(imsCall)) { 1581 mPhone.startOnHoldTone(conn); 1582 mOnHoldToneStarted = true; 1583 mOnHoldToneId = System.identityHashCode(conn); 1584 } 1585 } 1586 1587 SuppServiceNotification supp = new SuppServiceNotification(); 1588 // Type of notification: 0 = MO; 1 = MT 1589 // Refer SuppServiceNotification class documentation. 1590 supp.notificationType = 1; 1591 supp.code = SuppServiceNotification.MT_CODE_CALL_ON_HOLD; 1592 mPhone.notifySuppSvcNotification(supp); 1593 mEventLog.writeOnImsCallHoldReceived(imsCall.getCallSession()); 1594 } 1595 1596 @Override 1597 public void onCallSuppServiceReceived(ImsCall call, 1598 ImsSuppServiceNotification suppServiceInfo) { 1599 if (DBG) log("onCallSuppServiceReceived: suppServiceInfo=" + suppServiceInfo); 1600 1601 SuppServiceNotification supp = new SuppServiceNotification(); 1602 supp.notificationType = suppServiceInfo.notificationType; 1603 supp.code = suppServiceInfo.code; 1604 supp.index = suppServiceInfo.index; 1605 supp.number = suppServiceInfo.number; 1606 supp.history = suppServiceInfo.history; 1607 1608 mPhone.notifySuppSvcNotification(supp); 1609 } 1610 1611 @Override 1612 public void onCallMerged(final ImsCall call, final ImsCall peerCall, boolean swapCalls) { 1613 if (DBG) log("onCallMerged"); 1614 1615 ImsPhoneCall foregroundImsPhoneCall = findConnection(call).getCall(); 1616 ImsPhoneConnection peerConnection = findConnection(peerCall); 1617 ImsPhoneCall peerImsPhoneCall = peerConnection == null ? null 1618 : peerConnection.getCall(); 1619 1620 if (swapCalls) { 1621 switchAfterConferenceSuccess(); 1622 } 1623 foregroundImsPhoneCall.merge(peerImsPhoneCall, ImsPhoneCall.State.ACTIVE); 1624 1625 try { 1626 final ImsPhoneConnection conn = findConnection(call); 1627 log("onCallMerged: ImsPhoneConnection=" + conn); 1628 log("onCallMerged: CurrentVideoProvider=" + conn.getVideoProvider()); 1629 setVideoCallProvider(conn, call); 1630 log("onCallMerged: CurrentVideoProvider=" + conn.getVideoProvider()); 1631 } catch (Exception e) { 1632 loge("onCallMerged: exception " + e); 1633 } 1634 1635 // After merge complete, update foreground as Active 1636 // and background call as Held, if background call exists 1637 processCallStateChange(mForegroundCall.getImsCall(), ImsPhoneCall.State.ACTIVE, 1638 DisconnectCause.NOT_DISCONNECTED); 1639 if (peerConnection != null) { 1640 processCallStateChange(mBackgroundCall.getImsCall(), ImsPhoneCall.State.HOLDING, 1641 DisconnectCause.NOT_DISCONNECTED); 1642 } 1643 1644 // Check if the merge was requested by an existing conference call. In that 1645 // case, no further action is required. 1646 if (!call.isMergeRequestedByConf()) { 1647 log("onCallMerged :: calling onMultipartyStateChanged()"); 1648 onMultipartyStateChanged(call, true); 1649 } else { 1650 log("onCallMerged :: Merge requested by existing conference."); 1651 // Reset the flag. 1652 call.resetIsMergeRequestedByConf(false); 1653 } 1654 logState(); 1655 } 1656 1657 @Override 1658 public void onCallMergeFailed(ImsCall call, ImsReasonInfo reasonInfo) { 1659 if (DBG) log("onCallMergeFailed reasonInfo=" + reasonInfo); 1660 1661 // TODO: the call to notifySuppServiceFailed throws up the "merge failed" dialog 1662 // We should move this into the InCallService so that it is handled appropriately 1663 // based on the user facing UI. 1664 mPhone.notifySuppServiceFailed(Phone.SuppService.CONFERENCE); 1665 1666 // Start plumbing this even through Telecom so other components can take 1667 // appropriate action. 1668 ImsPhoneConnection conn = findConnection(call); 1669 if (conn != null) { 1670 conn.onConferenceMergeFailed(); 1671 } 1672 } 1673 1674 /** 1675 * Called when the state of IMS conference participant(s) has changed. 1676 * 1677 * @param call the call object that carries out the IMS call. 1678 * @param participants the participant(s) and their new state information. 1679 */ 1680 @Override 1681 public void onConferenceParticipantsStateChanged(ImsCall call, 1682 List<ConferenceParticipant> participants) { 1683 if (DBG) log("onConferenceParticipantsStateChanged"); 1684 1685 ImsPhoneConnection conn = findConnection(call); 1686 if (conn != null) { 1687 conn.updateConferenceParticipants(participants); 1688 } 1689 } 1690 1691 @Override 1692 public void onCallSessionTtyModeReceived(ImsCall call, int mode) { 1693 mPhone.onTtyModeReceived(mode); 1694 } 1695 1696 @Override 1697 public void onCallHandover(ImsCall imsCall, int srcAccessTech, int targetAccessTech, 1698 ImsReasonInfo reasonInfo) { 1699 if (DBG) { 1700 log("onCallHandover :: srcAccessTech=" + srcAccessTech + ", targetAccessTech=" + 1701 targetAccessTech + ", reasonInfo=" + reasonInfo); 1702 } 1703 mEventLog.writeOnImsCallHandover(imsCall.getCallSession(), 1704 srcAccessTech, targetAccessTech, reasonInfo); 1705 } 1706 1707 @Override 1708 public void onCallHandoverFailed(ImsCall imsCall, int srcAccessTech, int targetAccessTech, 1709 ImsReasonInfo reasonInfo) { 1710 if (DBG) { 1711 log("onCallHandoverFailed :: srcAccessTech=" + srcAccessTech + 1712 ", targetAccessTech=" + targetAccessTech + ", reasonInfo=" + reasonInfo); 1713 } 1714 mEventLog.writeOnImsCallHandoverFailed(imsCall.getCallSession(), 1715 srcAccessTech, targetAccessTech, reasonInfo); 1716 } 1717 1718 /** 1719 * Handles a change to the multiparty state for an {@code ImsCall}. Notifies the associated 1720 * {@link ImsPhoneConnection} of the change. 1721 * 1722 * @param imsCall The IMS call. 1723 * @param isMultiParty {@code true} if the call became multiparty, {@code false} 1724 * otherwise. 1725 */ 1726 @Override 1727 public void onMultipartyStateChanged(ImsCall imsCall, boolean isMultiParty) { 1728 if (DBG) log("onMultipartyStateChanged to " + (isMultiParty ? "Y" : "N")); 1729 1730 ImsPhoneConnection conn = findConnection(imsCall); 1731 if (conn != null) { 1732 conn.updateMultipartyState(isMultiParty); 1733 } 1734 } 1735 }; 1736 1737 /** 1738 * Listen to the IMS call state change 1739 */ 1740 private ImsCall.Listener mImsUssdListener = new ImsCall.Listener() { 1741 @Override 1742 public void onCallStarted(ImsCall imsCall) { 1743 if (DBG) log("mImsUssdListener onCallStarted"); 1744 1745 if (imsCall == mUssdSession) { 1746 if (mPendingUssd != null) { 1747 AsyncResult.forMessage(mPendingUssd); 1748 mPendingUssd.sendToTarget(); 1749 mPendingUssd = null; 1750 } 1751 } 1752 } 1753 1754 @Override 1755 public void onCallStartFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) { 1756 if (DBG) log("mImsUssdListener onCallStartFailed reasonCode=" + reasonInfo.getCode()); 1757 1758 onCallTerminated(imsCall, reasonInfo); 1759 } 1760 1761 @Override 1762 public void onCallTerminated(ImsCall imsCall, ImsReasonInfo reasonInfo) { 1763 if (DBG) log("mImsUssdListener onCallTerminated reasonCode=" + reasonInfo.getCode()); 1764 1765 if (imsCall == mUssdSession) { 1766 mUssdSession = null; 1767 if (mPendingUssd != null) { 1768 CommandException ex = 1769 new CommandException(CommandException.Error.GENERIC_FAILURE); 1770 AsyncResult.forMessage(mPendingUssd, null, ex); 1771 mPendingUssd.sendToTarget(); 1772 mPendingUssd = null; 1773 } 1774 } 1775 imsCall.close(); 1776 } 1777 1778 @Override 1779 public void onCallUssdMessageReceived(ImsCall call, 1780 int mode, String ussdMessage) { 1781 if (DBG) log("mImsUssdListener onCallUssdMessageReceived mode=" + mode); 1782 1783 int ussdMode = -1; 1784 1785 switch(mode) { 1786 case ImsCall.USSD_MODE_REQUEST: 1787 ussdMode = CommandsInterface.USSD_MODE_REQUEST; 1788 break; 1789 1790 case ImsCall.USSD_MODE_NOTIFY: 1791 ussdMode = CommandsInterface.USSD_MODE_NOTIFY; 1792 break; 1793 } 1794 1795 mPhone.onIncomingUSSD(ussdMode, ussdMessage); 1796 } 1797 }; 1798 1799 /** 1800 * Listen to the IMS service state change 1801 * 1802 */ 1803 private ImsConnectionStateListener mImsConnectionStateListener = 1804 new ImsConnectionStateListener() { 1805 @Override 1806 public void onImsConnected() { 1807 if (DBG) log("onImsConnected"); 1808 mPhone.setServiceState(ServiceState.STATE_IN_SERVICE); 1809 mPhone.setImsRegistered(true); 1810 mEventLog.writeOnImsConnectionState( 1811 TelephonyEventLog.IMS_CONNECTION_STATE_CONNECTED, null); 1812 } 1813 1814 @Override 1815 public void onImsDisconnected(ImsReasonInfo imsReasonInfo) { 1816 if (DBG) log("onImsDisconnected imsReasonInfo=" + imsReasonInfo); 1817 mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE); 1818 mPhone.setImsRegistered(false); 1819 mPhone.processDisconnectReason(imsReasonInfo); 1820 mEventLog.writeOnImsConnectionState( 1821 TelephonyEventLog.IMS_CONNECTION_STATE_DISCONNECTED, imsReasonInfo); 1822 } 1823 1824 @Override 1825 public void onImsProgressing() { 1826 if (DBG) log("onImsProgressing"); 1827 mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE); 1828 mPhone.setImsRegistered(false); 1829 mEventLog.writeOnImsConnectionState( 1830 TelephonyEventLog.IMS_CONNECTION_STATE_PROGRESSING, null); 1831 } 1832 1833 @Override 1834 public void onImsResumed() { 1835 if (DBG) log("onImsResumed"); 1836 mPhone.setServiceState(ServiceState.STATE_IN_SERVICE); 1837 mEventLog.writeOnImsConnectionState( 1838 TelephonyEventLog.IMS_CONNECTION_STATE_RESUMED, null); 1839 } 1840 1841 @Override 1842 public void onImsSuspended() { 1843 if (DBG) log("onImsSuspended"); 1844 mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE); 1845 mEventLog.writeOnImsConnectionState( 1846 TelephonyEventLog.IMS_CONNECTION_STATE_SUSPENDED, null); 1847 } 1848 1849 @Override 1850 public void onFeatureCapabilityChanged(int serviceClass, 1851 int[] enabledFeatures, int[] disabledFeatures) { 1852 if (serviceClass == ImsServiceClass.MMTEL) { 1853 boolean tmpIsVideoCallEnabled = isVideoCallEnabled(); 1854 // Check enabledFeatures to determine capabilities. We ignore disabledFeatures. 1855 StringBuilder sb; 1856 if (DBG) { 1857 sb = new StringBuilder(120); 1858 sb.append("onFeatureCapabilityChanged: "); 1859 } 1860 for (int i = ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE; 1861 i <= ImsConfig.FeatureConstants.FEATURE_TYPE_UT_OVER_WIFI && 1862 i < enabledFeatures.length; i++) { 1863 if (enabledFeatures[i] == i) { 1864 // If the feature is set to its own integer value it is enabled. 1865 if (DBG) { 1866 sb.append(mImsFeatureStrings[i]); 1867 sb.append(":true "); 1868 } 1869 1870 mImsFeatureEnabled[i] = true; 1871 } else if (enabledFeatures[i] 1872 == ImsConfig.FeatureConstants.FEATURE_TYPE_UNKNOWN) { 1873 // FEATURE_TYPE_UNKNOWN indicates that a feature is disabled. 1874 if (DBG) { 1875 sb.append(mImsFeatureStrings[i]); 1876 sb.append(":false "); 1877 } 1878 1879 mImsFeatureEnabled[i] = false; 1880 } else { 1881 // Feature has unknown state; it is not its own value or -1. 1882 if (DBG) { 1883 loge("onFeatureCapabilityChanged(" + i + ", " + mImsFeatureStrings[i] 1884 + "): unexpectedValue=" + enabledFeatures[i]); 1885 } 1886 } 1887 } 1888 if (DBG) { 1889 log(sb.toString()); 1890 } 1891 if (tmpIsVideoCallEnabled != isVideoCallEnabled()) { 1892 mPhone.notifyForVideoCapabilityChanged(isVideoCallEnabled()); 1893 } 1894 1895 // TODO: Use the ImsCallSession or ImsCallProfile to tell the initial Wifi state and 1896 // {@link ImsCallSession.Listener#callSessionHandover} to listen for changes to 1897 // wifi capability caused by a handover. 1898 if (DBG) log("onFeatureCapabilityChanged: isVolteEnabled=" + isVolteEnabled() 1899 + ", isVideoCallEnabled=" + isVideoCallEnabled() 1900 + ", isVowifiEnabled=" + isVowifiEnabled() 1901 + ", isUtEnabled=" + isUtEnabled()); 1902 for (ImsPhoneConnection connection : mConnections) { 1903 connection.updateWifiState(); 1904 } 1905 1906 mPhone.onFeatureCapabilityChanged(); 1907 1908 mEventLog.writeOnImsCapabilities(mImsFeatureEnabled); 1909 } 1910 } 1911 1912 @Override 1913 public void onVoiceMessageCountChanged(int count) { 1914 if (DBG) log("onVoiceMessageCountChanged :: count=" + count); 1915 mPhone.mDefaultPhone.setVoiceMessageCount(count); 1916 } 1917 }; 1918 1919 private ImsConfigListener.Stub mImsConfigListener = new ImsConfigListener.Stub() { 1920 @Override 1921 public void onGetFeatureResponse(int feature, int network, int value, int status) {} 1922 1923 @Override 1924 public void onSetFeatureResponse(int feature, int network, int value, int status) { 1925 mEventLog.writeImsSetFeatureValue(feature, network, value, status); 1926 } 1927 1928 @Override 1929 public void onGetVideoQuality(int status, int quality) {} 1930 1931 @Override 1932 public void onSetVideoQuality(int status) {} 1933 1934 }; 1935 1936 public ImsUtInterface getUtInterface() throws ImsException { 1937 if (mImsManager == null) { 1938 throw getImsManagerIsNullException(); 1939 } 1940 1941 ImsUtInterface ut = mImsManager.getSupplementaryServiceConfiguration(mServiceId); 1942 return ut; 1943 } 1944 1945 private void transferHandoverConnections(ImsPhoneCall call) { 1946 if (call.mConnections != null) { 1947 for (Connection c : call.mConnections) { 1948 c.mPreHandoverState = call.mState; 1949 log ("Connection state before handover is " + c.getStateBeforeHandover()); 1950 } 1951 } 1952 if (mHandoverCall.mConnections == null ) { 1953 mHandoverCall.mConnections = call.mConnections; 1954 } else { // Multi-call SRVCC 1955 mHandoverCall.mConnections.addAll(call.mConnections); 1956 } 1957 if (mHandoverCall.mConnections != null) { 1958 if (call.getImsCall() != null) { 1959 call.getImsCall().close(); 1960 } 1961 for (Connection c : mHandoverCall.mConnections) { 1962 ((ImsPhoneConnection)c).changeParent(mHandoverCall); 1963 ((ImsPhoneConnection)c).releaseWakeLock(); 1964 } 1965 } 1966 if (call.getState().isAlive()) { 1967 log ("Call is alive and state is " + call.mState); 1968 mHandoverCall.mState = call.mState; 1969 } 1970 call.mConnections.clear(); 1971 call.mState = ImsPhoneCall.State.IDLE; 1972 } 1973 1974 /* package */ 1975 void notifySrvccState(Call.SrvccState state) { 1976 if (DBG) log("notifySrvccState state=" + state); 1977 1978 mSrvccState = state; 1979 1980 if (mSrvccState == Call.SrvccState.COMPLETED) { 1981 transferHandoverConnections(mForegroundCall); 1982 transferHandoverConnections(mBackgroundCall); 1983 transferHandoverConnections(mRingingCall); 1984 } 1985 } 1986 1987 //****** Overridden from Handler 1988 1989 @Override 1990 public void 1991 handleMessage (Message msg) { 1992 AsyncResult ar; 1993 if (DBG) log("handleMessage what=" + msg.what); 1994 1995 switch (msg.what) { 1996 case EVENT_HANGUP_PENDINGMO: 1997 if (mPendingMO != null) { 1998 mPendingMO.onDisconnect(); 1999 removeConnection(mPendingMO); 2000 mPendingMO = null; 2001 } 2002 mPendingIntentExtras = null; 2003 updatePhoneState(); 2004 mPhone.notifyPreciseCallStateChanged(); 2005 break; 2006 case EVENT_RESUME_BACKGROUND: 2007 try { 2008 resumeWaitingOrHolding(); 2009 } catch (CallStateException e) { 2010 if (Phone.DEBUG_PHONE) { 2011 loge("handleMessage EVENT_RESUME_BACKGROUND exception=" + e); 2012 } 2013 } 2014 break; 2015 case EVENT_DIAL_PENDINGMO: 2016 dialInternal(mPendingMO, mClirMode, mPendingCallVideoState, mPendingIntentExtras); 2017 mPendingIntentExtras = null; 2018 break; 2019 2020 case EVENT_EXIT_ECBM_BEFORE_PENDINGMO: 2021 if (mPendingMO != null) { 2022 //Send ECBM exit request 2023 try { 2024 getEcbmInterface().exitEmergencyCallbackMode(); 2025 mPhone.setOnEcbModeExitResponse(this, EVENT_EXIT_ECM_RESPONSE_CDMA, null); 2026 pendingCallClirMode = mClirMode; 2027 pendingCallInEcm = true; 2028 } catch (ImsException e) { 2029 e.printStackTrace(); 2030 mPendingMO.setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED); 2031 sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO); 2032 } 2033 } 2034 break; 2035 2036 case EVENT_EXIT_ECM_RESPONSE_CDMA: 2037 // no matter the result, we still do the same here 2038 if (pendingCallInEcm) { 2039 dialInternal(mPendingMO, pendingCallClirMode, 2040 mPendingCallVideoState, mPendingIntentExtras); 2041 mPendingIntentExtras = null; 2042 pendingCallInEcm = false; 2043 } 2044 mPhone.unsetOnEcbModeExitResponse(this); 2045 break; 2046 case EVENT_VT_DATA_USAGE_UPDATE: 2047 ar = (AsyncResult) msg.obj; 2048 ImsCall call = (ImsCall) ar.userObj; 2049 Long usage = (long) ar.result; 2050 log("VT data usage update. usage = " + usage + ", imsCall = " + call); 2051 2052 Long oldUsage = 0L; 2053 if (mVtDataUsageMap.containsKey(call.uniqueId)) { 2054 oldUsage = mVtDataUsageMap.get(call.uniqueId); 2055 } 2056 mTotalVtDataUsage += (usage - oldUsage); 2057 mVtDataUsageMap.put(call.uniqueId, usage); 2058 break; 2059 } 2060 } 2061 2062 @Override 2063 protected void log(String msg) { 2064 Rlog.d(LOG_TAG, "[ImsPhoneCallTracker] " + msg); 2065 } 2066 2067 protected void loge(String msg) { 2068 Rlog.e(LOG_TAG, "[ImsPhoneCallTracker] " + msg); 2069 } 2070 2071 /** 2072 * Logs the current state of the ImsPhoneCallTracker. Useful for debugging issues with 2073 * call tracking. 2074 */ 2075 /* package */ 2076 void logState() { 2077 if (!VERBOSE_STATE_LOGGING) { 2078 return; 2079 } 2080 2081 StringBuilder sb = new StringBuilder(); 2082 sb.append("Current IMS PhoneCall State:\n"); 2083 sb.append(" Foreground: "); 2084 sb.append(mForegroundCall); 2085 sb.append("\n"); 2086 sb.append(" Background: "); 2087 sb.append(mBackgroundCall); 2088 sb.append("\n"); 2089 sb.append(" Ringing: "); 2090 sb.append(mRingingCall); 2091 sb.append("\n"); 2092 sb.append(" Handover: "); 2093 sb.append(mHandoverCall); 2094 sb.append("\n"); 2095 Rlog.v(LOG_TAG, sb.toString()); 2096 } 2097 2098 @Override 2099 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 2100 pw.println("ImsPhoneCallTracker extends:"); 2101 super.dump(fd, pw, args); 2102 pw.println(" mVoiceCallEndedRegistrants=" + mVoiceCallEndedRegistrants); 2103 pw.println(" mVoiceCallStartedRegistrants=" + mVoiceCallStartedRegistrants); 2104 pw.println(" mRingingCall=" + mRingingCall); 2105 pw.println(" mForegroundCall=" + mForegroundCall); 2106 pw.println(" mBackgroundCall=" + mBackgroundCall); 2107 pw.println(" mHandoverCall=" + mHandoverCall); 2108 pw.println(" mPendingMO=" + mPendingMO); 2109 //pw.println(" mHangupPendingMO=" + mHangupPendingMO); 2110 pw.println(" mPhone=" + mPhone); 2111 pw.println(" mDesiredMute=" + mDesiredMute); 2112 pw.println(" mState=" + mState); 2113 for (int i = 0; i < mImsFeatureEnabled.length; i++) { 2114 pw.println(" " + mImsFeatureStrings[i] + ": " 2115 + ((mImsFeatureEnabled[i]) ? "enabled" : "disabled")); 2116 } 2117 pw.println(" mTotalVtDataUsage=" + mTotalVtDataUsage); 2118 for (Map.Entry<Integer, Long> entry : mVtDataUsageMap.entrySet()) { 2119 pw.println(" id=" + entry.getKey() + " ,usage=" + entry.getValue()); 2120 } 2121 2122 pw.flush(); 2123 pw.println("++++++++++++++++++++++++++++++++"); 2124 2125 try { 2126 if (mImsManager != null) { 2127 mImsManager.dump(fd, pw, args); 2128 } 2129 } catch (Exception e) { 2130 e.printStackTrace(); 2131 } 2132 2133 if (mConnections != null && mConnections.size() > 0) { 2134 pw.println("mConnections:"); 2135 for (int i = 0; i < mConnections.size(); i++) { 2136 pw.println(" [" + i + "]: " + mConnections.get(i)); 2137 } 2138 } 2139 } 2140 2141 @Override 2142 protected void handlePollCalls(AsyncResult ar) { 2143 } 2144 2145 /* package */ 2146 ImsEcbm getEcbmInterface() throws ImsException { 2147 if (mImsManager == null) { 2148 throw getImsManagerIsNullException(); 2149 } 2150 2151 ImsEcbm ecbm = mImsManager.getEcbmInterface(mServiceId); 2152 return ecbm; 2153 } 2154 2155 /* package */ 2156 ImsMultiEndpoint getMultiEndpointInterface() throws ImsException { 2157 if (mImsManager == null) { 2158 throw getImsManagerIsNullException(); 2159 } 2160 2161 try { 2162 return mImsManager.getMultiEndpointInterface(mServiceId); 2163 } catch (ImsException e) { 2164 if (e.getCode() == ImsReasonInfo.CODE_MULTIENDPOINT_NOT_SUPPORTED) { 2165 return null; 2166 } else { 2167 throw e; 2168 } 2169 2170 } 2171 } 2172 2173 public boolean isInEmergencyCall() { 2174 return mIsInEmergencyCall; 2175 } 2176 2177 public boolean isVolteEnabled() { 2178 return mImsFeatureEnabled[ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_LTE]; 2179 } 2180 2181 public boolean isVowifiEnabled() { 2182 return mImsFeatureEnabled[ImsConfig.FeatureConstants.FEATURE_TYPE_VOICE_OVER_WIFI]; 2183 } 2184 2185 public boolean isVideoCallEnabled() { 2186 return (mImsFeatureEnabled[ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_LTE] 2187 || mImsFeatureEnabled[ImsConfig.FeatureConstants.FEATURE_TYPE_VIDEO_OVER_WIFI]); 2188 } 2189 2190 @Override 2191 public PhoneConstants.State getState() { 2192 return mState; 2193 } 2194 2195 private void setVideoCallProvider(ImsPhoneConnection conn, ImsCall imsCall) 2196 throws RemoteException { 2197 IImsVideoCallProvider imsVideoCallProvider = 2198 imsCall.getCallSession().getVideoCallProvider(); 2199 if (imsVideoCallProvider != null) { 2200 ImsVideoCallProviderWrapper imsVideoCallProviderWrapper = 2201 new ImsVideoCallProviderWrapper(imsVideoCallProvider); 2202 conn.setVideoProvider(imsVideoCallProviderWrapper); 2203 imsVideoCallProviderWrapper.registerForDataUsageUpdate 2204 (this, EVENT_VT_DATA_USAGE_UPDATE, imsCall); 2205 } 2206 } 2207 2208 public boolean isUtEnabled() { 2209 return (mImsFeatureEnabled[ImsConfig.FeatureConstants.FEATURE_TYPE_UT_OVER_LTE] 2210 || mImsFeatureEnabled[ImsConfig.FeatureConstants.FEATURE_TYPE_UT_OVER_WIFI]); 2211 } 2212 2213 /** 2214 * Given a call subject, removes any characters considered by the current carrier to be 2215 * invalid, as well as escaping (using \) any characters which the carrier requires to be 2216 * escaped. 2217 * 2218 * @param callSubject The call subject. 2219 * @return The call subject with invalid characters removed and escaping applied as required. 2220 */ 2221 private String cleanseInstantLetteringMessage(String callSubject) { 2222 if (TextUtils.isEmpty(callSubject)) { 2223 return callSubject; 2224 } 2225 2226 // Get the carrier config for the current sub. 2227 CarrierConfigManager configMgr = (CarrierConfigManager) 2228 mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); 2229 // Bail if we can't find the carrier config service. 2230 if (configMgr == null) { 2231 return callSubject; 2232 } 2233 2234 PersistableBundle carrierConfig = configMgr.getConfigForSubId(mPhone.getSubId()); 2235 // Bail if no carrier config found. 2236 if (carrierConfig == null) { 2237 return callSubject; 2238 } 2239 2240 // Try to replace invalid characters 2241 String invalidCharacters = carrierConfig.getString( 2242 CarrierConfigManager.KEY_CARRIER_INSTANT_LETTERING_INVALID_CHARS_STRING); 2243 if (!TextUtils.isEmpty(invalidCharacters)) { 2244 callSubject = callSubject.replaceAll(invalidCharacters, ""); 2245 } 2246 2247 // Try to escape characters which need to be escaped. 2248 String escapedCharacters = carrierConfig.getString( 2249 CarrierConfigManager.KEY_CARRIER_INSTANT_LETTERING_ESCAPED_CHARS_STRING); 2250 if (!TextUtils.isEmpty(escapedCharacters)) { 2251 callSubject = escapeChars(escapedCharacters, callSubject); 2252 } 2253 return callSubject; 2254 } 2255 2256 /** 2257 * Given a source string, return a string where a set of characters are escaped using the 2258 * backslash character. 2259 * 2260 * @param toEscape The characters to escape with a backslash. 2261 * @param source The source string. 2262 * @return The source string with characters escaped. 2263 */ 2264 private String escapeChars(String toEscape, String source) { 2265 StringBuilder escaped = new StringBuilder(); 2266 for (char c : source.toCharArray()) { 2267 if (toEscape.contains(Character.toString(c))) { 2268 escaped.append("\\"); 2269 } 2270 escaped.append(c); 2271 } 2272 2273 return escaped.toString(); 2274 } 2275 2276 /** 2277 * Initiates a pull of an external call. 2278 * 2279 * Initiates a pull by making a dial request with the {@link ImsCallProfile#EXTRA_IS_CALL_PULL} 2280 * extra specified. We call {@link ImsPhone#notifyUnknownConnection(Connection)} which notifies 2281 * Telecom of the new dialed connection. The 2282 * {@code PstnIncomingCallNotifier#maybeSwapWithUnknownConnection} logic ensures that the new 2283 * {@link ImsPhoneConnection} resulting from the dial gets swapped with the 2284 * {@link ImsExternalConnection}, which effectively makes the external call become a regular 2285 * call. Magic! 2286 * 2287 * @param number The phone number of the call to be pulled. 2288 * @param videoState The desired video state of the pulled call. 2289 */ 2290 @Override 2291 public void pullExternalCall(String number, int videoState) { 2292 Bundle extras = new Bundle(); 2293 extras.putBoolean(ImsCallProfile.EXTRA_IS_CALL_PULL, true); 2294 try { 2295 Connection connection = dial(number, videoState, extras); 2296 mPhone.notifyUnknownConnection(connection); 2297 } catch (CallStateException e) { 2298 loge("pullExternalCall failed - " + e); 2299 } 2300 } 2301 2302 private ImsException getImsManagerIsNullException() { 2303 return new ImsException("no ims manager", ImsReasonInfo.CODE_LOCAL_ILLEGAL_STATE); 2304 } 2305 2306 /** 2307 * Determines if answering an incoming call will cause the active call to be disconnected. 2308 * <p> 2309 * This will be the case if 2310 * {@link CarrierConfigManager#KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL} is 2311 * {@code true} for the carrier, the active call is a video call over WIFI, and the incoming 2312 * call is an audio call. 2313 * 2314 * @param activeCall The active call. 2315 * @param incomingCall The incoming call. 2316 * @return {@code true} if answering the incoming call will cause the active call to be 2317 * disconnected, {@code false} otherwise. 2318 */ 2319 private boolean shouldDisconnectActiveCallOnAnswer(ImsCall activeCall, 2320 ImsCall incomingCall) { 2321 2322 if (!mDropVideoCallWhenAnsweringAudioCall) { 2323 return false; 2324 } 2325 2326 boolean isActiveCallVideo = activeCall.isVideoCall() || 2327 (mTreatDowngradedVideoCallsAsVideoCalls && activeCall.wasVideoCall()); 2328 boolean isActiveCallOnWifi = activeCall.isWifiCall(); 2329 boolean isIncomingCallAudio = !incomingCall.isVideoCall(); 2330 2331 return isActiveCallVideo && isActiveCallOnWifi && isIncomingCallAudio; 2332 } 2333 2334 /** Get aggregated video call data usage since boot. 2335 * 2336 * @return data usage in bytes 2337 */ 2338 public long getVtDataUsage() { 2339 2340 // If there is an ongoing VT call, request the latest VT usage from the modem. The latest 2341 // usage will return asynchronously so it won't be counted in this round, but it will be 2342 // eventually counted when next getVtDataUsage is called. 2343 if (mState != PhoneConstants.State.IDLE) { 2344 for (ImsPhoneConnection conn : mConnections) { 2345 conn.getVideoProvider().onRequestConnectionDataUsage(); 2346 } 2347 } 2348 2349 return mTotalVtDataUsage; 2350 } 2351} 2352