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