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 17 package com.android.internal.telephony.imsphone; 18 19 import static com.android.internal.telephony.Phone.CS_FALLBACK; 20 21 import android.annotation.NonNull; 22 import android.annotation.UnsupportedAppUsage; 23 import android.content.BroadcastReceiver; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.IntentFilter; 27 import android.content.SharedPreferences; 28 import android.content.pm.PackageManager; 29 import android.net.ConnectivityManager; 30 import android.net.Network; 31 import android.net.NetworkCapabilities; 32 import android.net.NetworkInfo; 33 import android.net.NetworkRequest; 34 import android.net.NetworkStats; 35 import android.net.Uri; 36 import android.os.AsyncResult; 37 import android.os.Bundle; 38 import android.os.Handler; 39 import android.os.Message; 40 import android.os.PersistableBundle; 41 import android.os.Registrant; 42 import android.os.RegistrantList; 43 import android.os.RemoteException; 44 import android.os.SystemClock; 45 import android.os.SystemProperties; 46 import android.preference.PreferenceManager; 47 import android.provider.Settings; 48 import android.telecom.ConferenceParticipant; 49 import android.telecom.TelecomManager; 50 import android.telecom.VideoProfile; 51 import android.telephony.CallQuality; 52 import android.telephony.CarrierConfigManager; 53 import android.telephony.DisconnectCause; 54 import android.telephony.PhoneNumberUtils; 55 import android.telephony.Rlog; 56 import android.telephony.ServiceState; 57 import android.telephony.SubscriptionInfo; 58 import android.telephony.SubscriptionManager; 59 import android.telephony.TelephonyManager; 60 import android.telephony.emergency.EmergencyNumber; 61 import android.telephony.ims.ImsCallProfile; 62 import android.telephony.ims.ImsMmTelManager; 63 import android.telephony.ims.ImsReasonInfo; 64 import android.telephony.ims.ImsStreamMediaProfile; 65 import android.telephony.ims.ImsSuppServiceNotification; 66 import android.telephony.ims.ProvisioningManager; 67 import android.telephony.ims.feature.ImsFeature; 68 import android.telephony.ims.feature.MmTelFeature; 69 import android.telephony.ims.stub.ImsRegistrationImplBase; 70 import android.text.TextUtils; 71 import android.util.ArrayMap; 72 import android.util.Log; 73 import android.util.Pair; 74 75 import com.android.ims.ImsCall; 76 import com.android.ims.ImsConfig; 77 import com.android.ims.ImsConfigListener; 78 import com.android.ims.ImsEcbm; 79 import com.android.ims.ImsException; 80 import com.android.ims.ImsManager; 81 import com.android.ims.ImsMultiEndpoint; 82 import com.android.ims.ImsUtInterface; 83 import com.android.ims.internal.IImsCallSession; 84 import com.android.ims.internal.IImsVideoCallProvider; 85 import com.android.ims.internal.ImsVideoCallProviderWrapper; 86 import com.android.ims.internal.VideoPauseTracker; 87 import com.android.internal.annotations.VisibleForTesting; 88 import com.android.internal.os.SomeArgs; 89 import com.android.internal.telephony.Call; 90 import com.android.internal.telephony.CallStateException; 91 import com.android.internal.telephony.CallTracker; 92 import com.android.internal.telephony.CommandException; 93 import com.android.internal.telephony.CommandsInterface; 94 import com.android.internal.telephony.Connection; 95 import com.android.internal.telephony.LocaleTracker; 96 import com.android.internal.telephony.Phone; 97 import com.android.internal.telephony.PhoneConstants; 98 import com.android.internal.telephony.PhoneInternalInterface; 99 import com.android.internal.telephony.ServiceStateTracker; 100 import com.android.internal.telephony.SubscriptionController; 101 import com.android.internal.telephony.TelephonyProperties; 102 import com.android.internal.telephony.dataconnection.DataEnabledSettings; 103 import com.android.internal.telephony.dataconnection.DataEnabledSettings.DataEnabledChangedReason; 104 import com.android.internal.telephony.gsm.SuppServiceNotification; 105 import com.android.internal.telephony.metrics.CallQualityMetrics; 106 import com.android.internal.telephony.metrics.TelephonyMetrics; 107 import com.android.internal.telephony.nano.TelephonyProto.ImsConnectionState; 108 import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession; 109 import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.ImsCommand; 110 import com.android.server.net.NetworkStatsService; 111 112 import java.io.FileDescriptor; 113 import java.io.PrintWriter; 114 import java.util.ArrayList; 115 import java.util.HashMap; 116 import java.util.List; 117 import java.util.Map; 118 import java.util.Queue; 119 import java.util.concurrent.ConcurrentHashMap; 120 import java.util.concurrent.ConcurrentLinkedQueue; 121 import java.util.concurrent.Executor; 122 import java.util.concurrent.LinkedBlockingQueue; 123 import java.util.concurrent.atomic.AtomicInteger; 124 import java.util.regex.Pattern; 125 126 /** 127 * {@hide} 128 */ 129 public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall { 130 static final String LOG_TAG = "ImsPhoneCallTracker"; 131 static final String VERBOSE_STATE_TAG = "IPCTState"; 132 133 public interface PhoneStateListener { onPhoneStateChanged(PhoneConstants.State oldState, PhoneConstants.State newState)134 void onPhoneStateChanged(PhoneConstants.State oldState, PhoneConstants.State newState); 135 } 136 137 public interface SharedPreferenceProxy { getDefaultSharedPreferences(Context context)138 SharedPreferences getDefaultSharedPreferences(Context context); 139 } 140 141 public interface PhoneNumberUtilsProxy { isEmergencyNumber(String number)142 boolean isEmergencyNumber(String number); 143 } 144 145 private static final boolean DBG = true; 146 147 // When true, dumps the state of ImsPhoneCallTracker after changes to foreground and background 148 // calls. This is helpful for debugging. It is also possible to enable this at runtime by 149 // setting the IPCTState log tag to VERBOSE. 150 private static final boolean FORCE_VERBOSE_STATE_LOGGING = false; /* stopship if true */ 151 private static final boolean VERBOSE_STATE_LOGGING = FORCE_VERBOSE_STATE_LOGGING || 152 Rlog.isLoggable(VERBOSE_STATE_TAG, Log.VERBOSE); 153 154 private MmTelFeature.MmTelCapabilities mMmTelCapabilities = 155 new MmTelFeature.MmTelCapabilities(); 156 157 private TelephonyMetrics mMetrics; 158 private final Map<String, CallQualityMetrics> mCallQualityMetrics = new ConcurrentHashMap<>(); 159 private final ConcurrentLinkedQueue<CallQualityMetrics> mCallQualityMetricsHistory = 160 new ConcurrentLinkedQueue<>(); 161 private boolean mCarrierConfigLoaded = false; 162 163 private final MmTelFeatureListener mMmTelFeatureListener = new MmTelFeatureListener(); 164 private class MmTelFeatureListener extends MmTelFeature.Listener { 165 @Override onIncomingCall(IImsCallSession c, Bundle extras)166 public void onIncomingCall(IImsCallSession c, Bundle extras) { 167 if (DBG) log("onReceive : incoming call intent"); 168 169 if (mImsManager == null) return; 170 171 try { 172 // Network initiated USSD will be treated by mImsUssdListener 173 boolean isUssd = extras.getBoolean(ImsManager.EXTRA_USSD, false); 174 if (isUssd) { 175 if (DBG) log("onReceive : USSD"); 176 mUssdSession = mImsManager.takeCall(c, extras, mImsUssdListener); 177 if (mUssdSession != null) { 178 mUssdSession.accept(ImsCallProfile.CALL_TYPE_VOICE); 179 } 180 return; 181 } 182 183 boolean isUnknown = extras.getBoolean(ImsManager.EXTRA_IS_UNKNOWN_CALL, false); 184 if (DBG) { 185 log("onReceive : isUnknown = " + isUnknown 186 + " fg = " + mForegroundCall.getState() 187 + " bg = " + mBackgroundCall.getState()); 188 } 189 190 // Normal MT/Unknown call 191 ImsCall imsCall = mImsManager.takeCall(c, extras, mImsCallListener); 192 ImsPhoneConnection conn = new ImsPhoneConnection(mPhone, imsCall, 193 ImsPhoneCallTracker.this, 194 (isUnknown ? mForegroundCall : mRingingCall), isUnknown); 195 196 // If there is an active call. 197 if (mForegroundCall.hasConnections()) { 198 ImsCall activeCall = mForegroundCall.getFirstConnection().getImsCall(); 199 if (activeCall != null && imsCall != null) { 200 // activeCall could be null if the foreground call is in a disconnected 201 // state. If either of the calls is null there is no need to check if 202 // one will be disconnected on answer. 203 boolean answeringWillDisconnect = 204 shouldDisconnectActiveCallOnAnswer(activeCall, imsCall); 205 conn.setActiveCallDisconnectedOnAnswer(answeringWillDisconnect); 206 } 207 } 208 conn.setAllowAddCallDuringVideoCall(mAllowAddCallDuringVideoCall); 209 addConnection(conn); 210 211 setVideoCallProvider(conn, imsCall); 212 213 TelephonyMetrics.getInstance().writeOnImsCallReceive(mPhone.getPhoneId(), 214 imsCall.getSession()); 215 216 if (isUnknown) { 217 mPhone.notifyUnknownConnection(conn); 218 } else { 219 if ((mForegroundCall.getState() != ImsPhoneCall.State.IDLE) 220 || (mBackgroundCall.getState() != ImsPhoneCall.State.IDLE)) { 221 conn.update(imsCall, ImsPhoneCall.State.WAITING); 222 } 223 224 mPhone.notifyNewRingingConnection(conn); 225 mPhone.notifyIncomingRing(); 226 } 227 228 updatePhoneState(); 229 mPhone.notifyPreciseCallStateChanged(); 230 } catch (ImsException e) { 231 loge("onReceive : exception " + e); 232 } catch (RemoteException e) { 233 } 234 } 235 236 @Override onVoiceMessageCountUpdate(int count)237 public void onVoiceMessageCountUpdate(int count) { 238 if (mPhone != null && mPhone.mDefaultPhone != null) { 239 if (DBG) log("onVoiceMessageCountChanged :: count=" + count); 240 mPhone.mDefaultPhone.setVoiceMessageCount(count); 241 } else { 242 loge("onVoiceMessageCountUpdate: null phone"); 243 } 244 } 245 } 246 247 private BroadcastReceiver mReceiver = new BroadcastReceiver() { 248 @Override 249 public void onReceive(Context context, Intent intent) { 250 if (intent.getAction().equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) { 251 int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY, 252 SubscriptionManager.INVALID_SUBSCRIPTION_ID); 253 if (subId == mPhone.getSubId()) { 254 cacheCarrierConfiguration(subId); 255 log("onReceive : Updating mAllowEmergencyVideoCalls = " + 256 mAllowEmergencyVideoCalls); 257 } 258 } else if (TelecomManager.ACTION_CHANGE_DEFAULT_DIALER.equals(intent.getAction())) { 259 mDefaultDialerUid.set(getPackageUid(context, intent.getStringExtra( 260 TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME))); 261 } 262 } 263 }; 264 265 /** 266 * Tracks whether we are currently monitoring network connectivity for the purpose of warning 267 * the user of an inability to handover from LTE to WIFI for video calls. 268 */ 269 private boolean mIsMonitoringConnectivity = false; 270 271 /** 272 * Network callback used to schedule the handover check when a wireless network connects. 273 */ 274 private ConnectivityManager.NetworkCallback mNetworkCallback = 275 new ConnectivityManager.NetworkCallback() { 276 @Override 277 public void onAvailable(Network network) { 278 Rlog.i(LOG_TAG, "Network available: " + network); 279 scheduleHandoverCheck(); 280 } 281 }; 282 283 //***** Constants 284 285 static final int MAX_CONNECTIONS = 7; 286 static final int MAX_CONNECTIONS_PER_CALL = 5; 287 288 // Max number of calls we will keep call quality history for (the history is saved in-memory and 289 // included in bug reports). 290 private static final int MAX_CALL_QUALITY_HISTORY = 10; 291 292 private static final int EVENT_HANGUP_PENDINGMO = 18; 293 private static final int EVENT_DIAL_PENDINGMO = 20; 294 private static final int EVENT_EXIT_ECBM_BEFORE_PENDINGMO = 21; 295 private static final int EVENT_VT_DATA_USAGE_UPDATE = 22; 296 private static final int EVENT_DATA_ENABLED_CHANGED = 23; 297 private static final int EVENT_CHECK_FOR_WIFI_HANDOVER = 25; 298 private static final int EVENT_ON_FEATURE_CAPABILITY_CHANGED = 26; 299 private static final int EVENT_SUPP_SERVICE_INDICATION = 27; 300 private static final int EVENT_REDIAL_WIFI_E911_CALL = 28; 301 private static final int EVENT_REDIAL_WIFI_E911_TIMEOUT = 29; 302 private static final int EVENT_ANSWER_WAITING_CALL = 30; 303 private static final int EVENT_RESUME_NOW_FOREGROUND_CALL = 31; 304 305 private static final int TIMEOUT_HANGUP_PENDINGMO = 500; 306 307 private static final int HANDOVER_TO_WIFI_TIMEOUT_MS = 60000; // ms 308 309 private static final int TIMEOUT_REDIAL_WIFI_E911_MS = 10000; 310 311 private static final int TIMEOUT_PARTICIPANT_CONNECT_TIME_CACHE_MS = 60000; //ms 312 313 // Following values are for mHoldSwitchingState 314 private enum HoldSwapState { 315 // Not in the middle of a hold/swap operation 316 INACTIVE, 317 // Pending a single call getting held 318 PENDING_SINGLE_CALL_HOLD, 319 // Pending a single call getting unheld 320 PENDING_SINGLE_CALL_UNHOLD, 321 // Pending swapping a active and a held call 322 SWAPPING_ACTIVE_AND_HELD, 323 // Pending holding a call to answer a call-waiting call 324 HOLDING_TO_ANSWER_INCOMING, 325 // Pending resuming the foreground call after some kind of failure 326 PENDING_RESUME_FOREGROUND_AFTER_FAILURE, 327 // Pending holding a call to dial another outgoing call 328 HOLDING_TO_DIAL_OUTGOING, 329 } 330 331 //***** Instance Variables 332 @UnsupportedAppUsage 333 private ArrayList<ImsPhoneConnection> mConnections = new ArrayList<ImsPhoneConnection>(); 334 private RegistrantList mVoiceCallEndedRegistrants = new RegistrantList(); 335 private RegistrantList mVoiceCallStartedRegistrants = new RegistrantList(); 336 337 @UnsupportedAppUsage 338 public ImsPhoneCall mRingingCall = new ImsPhoneCall(this, ImsPhoneCall.CONTEXT_RINGING); 339 @UnsupportedAppUsage 340 public ImsPhoneCall mForegroundCall = new ImsPhoneCall(this, 341 ImsPhoneCall.CONTEXT_FOREGROUND); 342 @UnsupportedAppUsage 343 public ImsPhoneCall mBackgroundCall = new ImsPhoneCall(this, 344 ImsPhoneCall.CONTEXT_BACKGROUND); 345 @UnsupportedAppUsage 346 public ImsPhoneCall mHandoverCall = new ImsPhoneCall(this, ImsPhoneCall.CONTEXT_HANDOVER); 347 348 // Hold aggregated video call data usage for each video call since boot. 349 // The ImsCall's call id is the key of the map. 350 private final HashMap<Integer, Long> mVtDataUsageMap = new HashMap<>(); 351 private final Map<String, CacheEntry> mPhoneNumAndConnTime = new ConcurrentHashMap<>(); 352 private final Queue<CacheEntry> mUnknownPeerConnTime = new LinkedBlockingQueue<>(); 353 354 private static class CacheEntry { 355 private long mCachedTime; 356 private long mConnectTime; 357 private long mConnectElapsedTime; 358 /** 359 * The direction of the call; 360 * {@link android.telecom.Call.Details#DIRECTION_INCOMING} for incoming calls, or 361 * {@link android.telecom.Call.Details#DIRECTION_OUTGOING} for outgoing calls. 362 */ 363 private int mCallDirection; 364 CacheEntry(long cachedTime, long connectTime, long connectElapsedTime, int callDirection)365 CacheEntry(long cachedTime, long connectTime, long connectElapsedTime, int callDirection) { 366 mCachedTime = cachedTime; 367 mConnectTime = connectTime; 368 mConnectElapsedTime = connectElapsedTime; 369 mCallDirection = callDirection; 370 } 371 } 372 373 private volatile NetworkStats mVtDataUsageSnapshot = null; 374 private volatile NetworkStats mVtDataUsageUidSnapshot = null; 375 376 private final AtomicInteger mDefaultDialerUid = new AtomicInteger(NetworkStats.UID_ALL); 377 378 @UnsupportedAppUsage 379 private ImsPhoneConnection mPendingMO; 380 private int mClirMode = CommandsInterface.CLIR_DEFAULT; 381 @UnsupportedAppUsage 382 private Object mSyncHold = new Object(); 383 384 private ImsCall mUssdSession = null; 385 @UnsupportedAppUsage 386 private Message mPendingUssd = null; 387 388 @UnsupportedAppUsage 389 ImsPhone mPhone; 390 391 private boolean mDesiredMute = false; // false = mute off 392 @UnsupportedAppUsage 393 private boolean mOnHoldToneStarted = false; 394 @UnsupportedAppUsage 395 private int mOnHoldToneId = -1; 396 397 private PhoneConstants.State mState = PhoneConstants.State.IDLE; 398 399 private ImsManager mImsManager; 400 private ImsUtInterface mUtInterface; 401 402 private Call.SrvccState mSrvccState = Call.SrvccState.NONE; 403 404 private boolean mIsInEmergencyCall = false; 405 private boolean mIsDataEnabled = false; 406 407 private int pendingCallClirMode; 408 private int mPendingCallVideoState; 409 private Bundle mPendingIntentExtras; 410 private boolean pendingCallInEcm = false; 411 @UnsupportedAppUsage 412 private boolean mSwitchingFgAndBgCalls = false; 413 private ImsCall mCallExpectedToResume = null; 414 @UnsupportedAppUsage 415 private boolean mAllowEmergencyVideoCalls = false; 416 private boolean mIgnoreDataEnabledChangedForVideoCalls = false; 417 private boolean mIsViLteDataMetered = false; 418 private boolean mAlwaysPlayRemoteHoldTone = false; 419 private boolean mAutoRetryFailedWifiEmergencyCall = false; 420 // Tracks the state of our background/foreground calls while a call hold/swap operation is 421 // in progress. Values listed above. 422 private HoldSwapState mHoldSwitchingState = HoldSwapState.INACTIVE; 423 424 private String mLastDialString = null; 425 private PhoneInternalInterface.DialArgs mLastDialArgs = null; 426 /** 427 * Listeners to changes in the phone state. Intended for use by other interested IMS components 428 * without the need to register a full blown {@link android.telephony.PhoneStateListener}. 429 */ 430 private List<PhoneStateListener> mPhoneStateListeners = new ArrayList<>(); 431 432 /** 433 * Carrier configuration option which determines if video calls which have been downgraded to an 434 * audio call should be treated as if they are still video calls. 435 */ 436 private boolean mTreatDowngradedVideoCallsAsVideoCalls = false; 437 438 /** 439 * Carrier configuration option which determines if an ongoing video call over wifi should be 440 * dropped when an audio call is answered. 441 */ 442 private boolean mDropVideoCallWhenAnsweringAudioCall = false; 443 444 /** 445 * Carrier configuration option which determines whether adding a call during a video call 446 * should be allowed. 447 */ 448 private boolean mAllowAddCallDuringVideoCall = true; 449 450 /** 451 * Carrier configuration option which determines whether to notify the connection if a handover 452 * to wifi fails. 453 */ 454 private boolean mNotifyVtHandoverToWifiFail = false; 455 456 /** 457 * Carrier configuration option which determines whether the carrier supports downgrading a 458 * TX/RX/TX-RX video call directly to an audio-only call. 459 */ 460 private boolean mSupportDowngradeVtToAudio = false; 461 462 /** 463 * Carrier configuration option which determines whether the carrier wants to inform the user 464 * when a video call is handed over from WIFI to LTE. 465 * See {@link CarrierConfigManager#KEY_NOTIFY_HANDOVER_VIDEO_FROM_WIFI_TO_LTE_BOOL} for more 466 * information. 467 */ 468 private boolean mNotifyHandoverVideoFromWifiToLTE = false; 469 470 /** 471 * Carrier configuration option which determines whether the carrier wants to inform the user 472 * when a video call is handed over from LTE to WIFI. 473 * See {@link CarrierConfigManager#KEY_NOTIFY_HANDOVER_VIDEO_FROM_LTE_TO_WIFI_BOOL} for more 474 * information. 475 */ 476 private boolean mNotifyHandoverVideoFromLTEToWifi = false; 477 478 /** 479 * When {@code} false, indicates that no handover from LTE to WIFI has been attempted during the 480 * start of the call. 481 * When {@code true}, indicates that the start of call handover from LTE to WIFI has been 482 * attempted (it may have succeeded or failed). 483 */ 484 private boolean mHasAttemptedStartOfCallHandover = false; 485 486 /** 487 * Carrier configuration option which determines whether the carrier supports the 488 * {@link VideoProfile#STATE_PAUSED} signalling. 489 * See {@link CarrierConfigManager#KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL} for more information. 490 */ 491 private boolean mSupportPauseVideo = false; 492 493 /** 494 * Carrier configuration option which defines a mapping from pairs of 495 * {@link ImsReasonInfo#getCode()} and {@link ImsReasonInfo#getExtraMessage()} values to a new 496 * {@code ImsReasonInfo#CODE_*} value. 497 * 498 * See {@link CarrierConfigManager#KEY_IMS_REASONINFO_MAPPING_STRING_ARRAY}. 499 */ 500 private Map<Pair<Integer, String>, Integer> mImsReasonCodeMap = new ArrayMap<>(); 501 502 503 /** 504 * TODO: Remove this code; it is a workaround. 505 * When {@code true}, forces {@link ImsManager#updateImsServiceConfig(boolean)} to 506 * be called when an ongoing video call is disconnected. In some cases, where video pause is 507 * supported by the carrier, when {@link #onDataEnabledChanged(boolean, int)} reports that data 508 * has been disabled we will pause the video rather than disconnecting the call. When this 509 * happens we need to prevent the IMS service config from being updated, as this will cause VT 510 * to be disabled mid-call, resulting in an inability to un-pause the video. 511 */ 512 private boolean mShouldUpdateImsConfigOnDisconnect = false; 513 514 /** 515 * Default implementation for retrieving shared preferences; uses the actual PreferencesManager. 516 */ 517 private SharedPreferenceProxy mSharedPreferenceProxy = (Context context) -> { 518 return PreferenceManager.getDefaultSharedPreferences(context); 519 }; 520 521 /** 522 * Default implementation for determining if a number is an emergency number. Uses the real 523 * PhoneNumberUtils. 524 */ 525 private PhoneNumberUtilsProxy mPhoneNumberUtilsProxy = (String string) -> { 526 return PhoneNumberUtils.isEmergencyNumber(string); 527 }; 528 529 private final ImsManager.Connector mImsManagerConnector; 530 531 //***** Events 532 533 534 //***** Constructors ImsPhoneCallTracker(ImsPhone phone)535 public ImsPhoneCallTracker(ImsPhone phone) { 536 this(phone, phone.getContext().getMainExecutor()); 537 } 538 539 @VisibleForTesting ImsPhoneCallTracker(ImsPhone phone, Executor executor)540 public ImsPhoneCallTracker(ImsPhone phone, Executor executor) { 541 this.mPhone = phone; 542 543 mMetrics = TelephonyMetrics.getInstance(); 544 545 IntentFilter intentfilter = new IntentFilter(); 546 intentfilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); 547 intentfilter.addAction(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER); 548 mPhone.getContext().registerReceiver(mReceiver, intentfilter); 549 cacheCarrierConfiguration(mPhone.getSubId()); 550 551 mPhone.getDefaultPhone().getDataEnabledSettings().registerForDataEnabledChanged( 552 this, EVENT_DATA_ENABLED_CHANGED, null); 553 554 final TelecomManager telecomManager = 555 (TelecomManager) mPhone.getContext().getSystemService(Context.TELECOM_SERVICE); 556 mDefaultDialerUid.set( 557 getPackageUid(mPhone.getContext(), telecomManager.getDefaultDialerPackage())); 558 559 long currentTime = SystemClock.elapsedRealtime(); 560 mVtDataUsageSnapshot = new NetworkStats(currentTime, 1); 561 mVtDataUsageUidSnapshot = new NetworkStats(currentTime, 1); 562 563 // Allow the executor to be specified for testing. 564 mImsManagerConnector = new ImsManager.Connector(phone.getContext(), phone.getPhoneId(), 565 new ImsManager.Connector.Listener() { 566 @Override 567 public void connectionReady(ImsManager manager) throws ImsException { 568 mImsManager = manager; 569 startListeningForCalls(); 570 } 571 572 @Override 573 public void connectionUnavailable() { 574 stopListeningForCalls(); 575 } 576 }, executor); 577 mImsManagerConnector.connect(); 578 } 579 580 /** 581 * Test-only method used to mock out access to the shared preferences through the 582 * {@link PreferenceManager}. 583 * @param sharedPreferenceProxy 584 */ 585 @VisibleForTesting setSharedPreferenceProxy(SharedPreferenceProxy sharedPreferenceProxy)586 public void setSharedPreferenceProxy(SharedPreferenceProxy sharedPreferenceProxy) { 587 mSharedPreferenceProxy = sharedPreferenceProxy; 588 } 589 590 /** 591 * Test-only method used to mock out access to the phone number utils class. 592 * @param phoneNumberUtilsProxy 593 */ 594 @VisibleForTesting setPhoneNumberUtilsProxy(PhoneNumberUtilsProxy phoneNumberUtilsProxy)595 public void setPhoneNumberUtilsProxy(PhoneNumberUtilsProxy phoneNumberUtilsProxy) { 596 mPhoneNumberUtilsProxy = phoneNumberUtilsProxy; 597 } 598 599 /** 600 * Test-only method used to set the ImsService retry timeout. 601 */ 602 @VisibleForTesting setRetryTimeout(ImsManager.Connector.RetryTimeout retryTimeout)603 public void setRetryTimeout(ImsManager.Connector.RetryTimeout retryTimeout) { 604 mImsManagerConnector.mRetryTimeout = retryTimeout; 605 } 606 getPackageUid(Context context, String pkg)607 private int getPackageUid(Context context, String pkg) { 608 if (pkg == null) { 609 return NetworkStats.UID_ALL; 610 } 611 612 // Initialize to UID_ALL so at least it can be counted to overall data usage if 613 // the dialer's package uid is not available. 614 int uid = NetworkStats.UID_ALL; 615 try { 616 uid = context.getPackageManager().getPackageUid(pkg, 0); 617 } catch (PackageManager.NameNotFoundException e) { 618 loge("Cannot find package uid. pkg = " + pkg); 619 } 620 return uid; 621 } 622 startListeningForCalls()623 private void startListeningForCalls() throws ImsException { 624 log("startListeningForCalls"); 625 mImsManager.open(mMmTelFeatureListener); 626 mImsManager.addRegistrationCallback(mImsRegistrationCallback); 627 mImsManager.addCapabilitiesCallback(mImsCapabilityCallback); 628 629 mImsManager.setConfigListener(mImsConfigListener); 630 631 mImsManager.getConfigInterface().addConfigCallback(mConfigCallback); 632 633 // Get the ECBM interface and set IMSPhone's listener object for notifications 634 getEcbmInterface().setEcbmStateListener(mPhone.getImsEcbmStateListener()); 635 if (mPhone.isInEcm()) { 636 // Call exit ECBM which will invoke onECBMExited 637 mPhone.exitEmergencyCallbackMode(); 638 } 639 int mPreferredTtyMode = Settings.Secure.getInt( 640 mPhone.getContext().getContentResolver(), 641 Settings.Secure.PREFERRED_TTY_MODE, 642 Phone.TTY_MODE_OFF); 643 mImsManager.setUiTTYMode(mPhone.getContext(), mPreferredTtyMode, null); 644 645 ImsMultiEndpoint multiEndpoint = getMultiEndpointInterface(); 646 if (multiEndpoint != null) { 647 multiEndpoint.setExternalCallStateListener( 648 mPhone.getExternalCallTracker().getExternalCallStateListener()); 649 } 650 651 //Set UT interface listener to receive UT indications. 652 mUtInterface = getUtInterface(); 653 if (mUtInterface != null) { 654 mUtInterface.registerForSuppServiceIndication(this, 655 EVENT_SUPP_SERVICE_INDICATION, null); 656 } 657 658 if (mCarrierConfigLoaded) { 659 mImsManager.updateImsServiceConfig(true); 660 } 661 // For compatibility with apps that still use deprecated intent 662 sendImsServiceStateIntent(ImsManager.ACTION_IMS_SERVICE_UP); 663 } 664 stopListeningForCalls()665 private void stopListeningForCalls() { 666 log("stopListeningForCalls"); 667 resetImsCapabilities(); 668 // Only close on valid session. 669 if (mImsManager != null) { 670 try { 671 mImsManager.getConfigInterface().removeConfigCallback(mConfigCallback.getBinder()); 672 } catch (ImsException e) { 673 Log.w(LOG_TAG, "stopListeningForCalls: unable to remove config callback."); 674 } 675 mImsManager.close(); 676 } 677 // For compatibility with apps that still use deprecated intent 678 sendImsServiceStateIntent(ImsManager.ACTION_IMS_SERVICE_DOWN); 679 } 680 sendImsServiceStateIntent(String intentAction)681 private void sendImsServiceStateIntent(String intentAction) { 682 Intent intent = new Intent(intentAction); 683 intent.putExtra(ImsManager.EXTRA_PHONE_ID, mPhone.getPhoneId()); 684 if (mPhone != null && mPhone.getContext() != null) { 685 mPhone.getContext().sendBroadcast(intent); 686 } 687 } 688 dispose()689 public void dispose() { 690 if (DBG) log("dispose"); 691 mRingingCall.dispose(); 692 mBackgroundCall.dispose(); 693 mForegroundCall.dispose(); 694 mHandoverCall.dispose(); 695 696 clearDisconnected(); 697 if (mUtInterface != null) { 698 mUtInterface.unregisterForSuppServiceIndication(this); 699 } 700 mPhone.getContext().unregisterReceiver(mReceiver); 701 mPhone.getDefaultPhone().getDataEnabledSettings().unregisterForDataEnabledChanged(this); 702 mImsManagerConnector.disconnect(); 703 } 704 705 @Override finalize()706 protected void finalize() { 707 log("ImsPhoneCallTracker finalized"); 708 } 709 710 //***** Instance Methods 711 712 //***** Public Methods 713 @Override registerForVoiceCallStarted(Handler h, int what, Object obj)714 public void registerForVoiceCallStarted(Handler h, int what, Object obj) { 715 Registrant r = new Registrant(h, what, obj); 716 mVoiceCallStartedRegistrants.add(r); 717 } 718 719 @Override unregisterForVoiceCallStarted(Handler h)720 public void unregisterForVoiceCallStarted(Handler h) { 721 mVoiceCallStartedRegistrants.remove(h); 722 } 723 724 @Override registerForVoiceCallEnded(Handler h, int what, Object obj)725 public void registerForVoiceCallEnded(Handler h, int what, Object obj) { 726 Registrant r = new Registrant(h, what, obj); 727 mVoiceCallEndedRegistrants.add(r); 728 } 729 730 @Override unregisterForVoiceCallEnded(Handler h)731 public void unregisterForVoiceCallEnded(Handler h) { 732 mVoiceCallEndedRegistrants.remove(h); 733 } 734 getClirMode()735 public int getClirMode() { 736 if (mSharedPreferenceProxy != null && mPhone.getDefaultPhone() != null) { 737 SharedPreferences sp = mSharedPreferenceProxy.getDefaultSharedPreferences( 738 mPhone.getContext()); 739 return sp.getInt(Phone.CLIR_KEY + mPhone.getDefaultPhone().getPhoneId(), 740 CommandsInterface.CLIR_DEFAULT); 741 } else { 742 loge("dial; could not get default CLIR mode."); 743 return CommandsInterface.CLIR_DEFAULT; 744 } 745 } 746 747 @UnsupportedAppUsage dial(String dialString, int videoState, Bundle intentExtras)748 public Connection dial(String dialString, int videoState, Bundle intentExtras) throws 749 CallStateException { 750 ImsPhone.ImsDialArgs dialArgs = new ImsPhone.ImsDialArgs.Builder() 751 .setIntentExtras(intentExtras) 752 .setVideoState(videoState) 753 .setClirMode(getClirMode()) 754 .build(); 755 return dial(dialString, dialArgs); 756 } 757 dial(String dialString, ImsPhone.ImsDialArgs dialArgs)758 public synchronized Connection dial(String dialString, ImsPhone.ImsDialArgs dialArgs) 759 throws CallStateException { 760 boolean isPhoneInEcmMode = isPhoneInEcbMode(); 761 boolean isEmergencyNumber = mPhoneNumberUtilsProxy.isEmergencyNumber(dialString); 762 763 if (!shouldNumberBePlacedOnIms(isEmergencyNumber, dialString)) { 764 Rlog.i(LOG_TAG, "dial: shouldNumberBePlacedOnIms = false"); 765 throw new CallStateException(CS_FALLBACK); 766 } 767 768 int clirMode = dialArgs.clirMode; 769 int videoState = dialArgs.videoState; 770 771 if (DBG) log("dial clirMode=" + clirMode); 772 if (isEmergencyNumber) { 773 clirMode = CommandsInterface.CLIR_SUPPRESSION; 774 if (DBG) log("dial emergency call, set clirModIe=" + clirMode); 775 } 776 777 // note that this triggers call state changed notif 778 clearDisconnected(); 779 780 if (mImsManager == null) { 781 throw new CallStateException("service not available"); 782 } 783 784 // See if there are any issues which preclude placing a call; throw a CallStateException 785 // if there is. 786 checkForDialIssues(); 787 788 if (isPhoneInEcmMode && isEmergencyNumber) { 789 handleEcmTimer(ImsPhone.CANCEL_ECM_TIMER); 790 } 791 792 // If the call is to an emergency number and the carrier does not support video emergency 793 // calls, dial as an audio-only call. 794 if (isEmergencyNumber && VideoProfile.isVideo(videoState) && 795 !mAllowEmergencyVideoCalls) { 796 loge("dial: carrier does not support video emergency calls; downgrade to audio-only"); 797 videoState = VideoProfile.STATE_AUDIO_ONLY; 798 } 799 800 boolean holdBeforeDial = false; 801 802 // The new call must be assigned to the foreground call. 803 // That call must be idle, so place anything that's 804 // there on hold 805 if (mForegroundCall.getState() == ImsPhoneCall.State.ACTIVE) { 806 if (mBackgroundCall.getState() != ImsPhoneCall.State.IDLE) { 807 //we should have failed in checkForDialIssues above before we get here 808 throw new CallStateException(CallStateException.ERROR_TOO_MANY_CALLS, 809 "Already too many ongoing calls."); 810 } 811 // foreground call is empty for the newly dialed connection 812 holdBeforeDial = true; 813 // Cache the video state for pending MO call. 814 mPendingCallVideoState = videoState; 815 mPendingIntentExtras = dialArgs.intentExtras; 816 holdActiveCallForPendingMo(); 817 } 818 819 ImsPhoneCall.State fgState = ImsPhoneCall.State.IDLE; 820 ImsPhoneCall.State bgState = ImsPhoneCall.State.IDLE; 821 822 mClirMode = clirMode; 823 824 synchronized (mSyncHold) { 825 if (holdBeforeDial) { 826 fgState = mForegroundCall.getState(); 827 bgState = mBackgroundCall.getState(); 828 829 //holding foreground call failed 830 if (fgState == ImsPhoneCall.State.ACTIVE) { 831 throw new CallStateException("cannot dial in current state"); 832 } 833 834 //holding foreground call succeeded 835 if (bgState == ImsPhoneCall.State.HOLDING) { 836 holdBeforeDial = false; 837 } 838 } 839 840 mLastDialString = dialString; 841 mLastDialArgs = dialArgs; 842 mPendingMO = new ImsPhoneConnection(mPhone, 843 checkForTestEmergencyNumber(dialString), this, mForegroundCall, 844 isEmergencyNumber); 845 if (isEmergencyNumber && dialArgs != null && dialArgs.intentExtras != null) { 846 Rlog.i(LOG_TAG, "dial ims emergency dialer: " + dialArgs.intentExtras.getBoolean( 847 TelecomManager.EXTRA_IS_USER_INTENT_EMERGENCY_CALL)); 848 mPendingMO.setHasKnownUserIntentEmergency(dialArgs.intentExtras.getBoolean( 849 TelecomManager.EXTRA_IS_USER_INTENT_EMERGENCY_CALL)); 850 } 851 mPendingMO.setVideoState(videoState); 852 if (dialArgs.rttTextStream != null) { 853 log("dial: setting RTT stream on mPendingMO"); 854 mPendingMO.setCurrentRttTextStream(dialArgs.rttTextStream); 855 } 856 } 857 addConnection(mPendingMO); 858 859 if (!holdBeforeDial) { 860 if ((!isPhoneInEcmMode) || (isPhoneInEcmMode && isEmergencyNumber)) { 861 dialInternal(mPendingMO, clirMode, videoState, dialArgs.intentExtras); 862 } else { 863 try { 864 getEcbmInterface().exitEmergencyCallbackMode(); 865 } catch (ImsException e) { 866 e.printStackTrace(); 867 throw new CallStateException("service not available"); 868 } 869 mPhone.setOnEcbModeExitResponse(this, EVENT_EXIT_ECM_RESPONSE_CDMA, null); 870 pendingCallClirMode = clirMode; 871 mPendingCallVideoState = videoState; 872 pendingCallInEcm = true; 873 } 874 } 875 876 updatePhoneState(); 877 mPhone.notifyPreciseCallStateChanged(); 878 879 return mPendingMO; 880 } 881 isImsServiceReady()882 boolean isImsServiceReady() { 883 if (mImsManager == null) { 884 return false; 885 } 886 887 return mImsManager.isServiceReady(); 888 } 889 shouldNumberBePlacedOnIms(boolean isEmergency, String number)890 private boolean shouldNumberBePlacedOnIms(boolean isEmergency, String number) { 891 int processCallResult; 892 try { 893 if (mImsManager != null) { 894 processCallResult = mImsManager.shouldProcessCall(isEmergency, 895 new String[]{number}); 896 Rlog.i(LOG_TAG, "shouldProcessCall: number: " + Rlog.pii(LOG_TAG, number) 897 + ", result: " + processCallResult); 898 } else { 899 Rlog.w(LOG_TAG, "ImsManager unavailable, shouldProcessCall returning false."); 900 return false; 901 } 902 } catch (ImsException e) { 903 Rlog.w(LOG_TAG, "ImsService unavailable, shouldProcessCall returning false."); 904 return false; 905 } 906 switch(processCallResult) { 907 case MmTelFeature.PROCESS_CALL_IMS: { 908 // The ImsService wishes to place the call over IMS 909 return true; 910 } 911 case MmTelFeature.PROCESS_CALL_CSFB: { 912 Rlog.i(LOG_TAG, "shouldProcessCall: place over CSFB instead."); 913 return false; 914 } 915 default: { 916 Rlog.w(LOG_TAG, "shouldProcessCall returned unknown result."); 917 return false; 918 } 919 } 920 } 921 922 /** 923 * Caches frequently used carrier configuration items locally. 924 * 925 * @param subId The sub id. 926 */ cacheCarrierConfiguration(int subId)927 private void cacheCarrierConfiguration(int subId) { 928 CarrierConfigManager carrierConfigManager = (CarrierConfigManager) 929 mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); 930 if (carrierConfigManager == null 931 || !SubscriptionController.getInstance().isActiveSubId(subId)) { 932 loge("cacheCarrierConfiguration: No carrier config service found" + " " 933 + "or not active subId = " + subId); 934 mCarrierConfigLoaded = false; 935 return; 936 } 937 938 PersistableBundle carrierConfig = carrierConfigManager.getConfigForSubId(subId); 939 if (carrierConfig == null) { 940 loge("cacheCarrierConfiguration: Empty carrier config."); 941 mCarrierConfigLoaded = false; 942 return; 943 } 944 mCarrierConfigLoaded = true; 945 946 updateCarrierConfigCache(carrierConfig); 947 } 948 949 /** 950 * Updates the local carrier config cache from a bundle obtained from the carrier config 951 * manager. Also supports unit testing by injecting configuration at test time. 952 * @param carrierConfig The config bundle. 953 */ 954 @VisibleForTesting updateCarrierConfigCache(PersistableBundle carrierConfig)955 public void updateCarrierConfigCache(PersistableBundle carrierConfig) { 956 mAllowEmergencyVideoCalls = 957 carrierConfig.getBoolean(CarrierConfigManager.KEY_ALLOW_EMERGENCY_VIDEO_CALLS_BOOL); 958 mTreatDowngradedVideoCallsAsVideoCalls = 959 carrierConfig.getBoolean( 960 CarrierConfigManager.KEY_TREAT_DOWNGRADED_VIDEO_CALLS_AS_VIDEO_CALLS_BOOL); 961 mDropVideoCallWhenAnsweringAudioCall = 962 carrierConfig.getBoolean( 963 CarrierConfigManager.KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL); 964 mAllowAddCallDuringVideoCall = 965 carrierConfig.getBoolean( 966 CarrierConfigManager.KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL); 967 mNotifyVtHandoverToWifiFail = carrierConfig.getBoolean( 968 CarrierConfigManager.KEY_NOTIFY_VT_HANDOVER_TO_WIFI_FAILURE_BOOL); 969 mSupportDowngradeVtToAudio = carrierConfig.getBoolean( 970 CarrierConfigManager.KEY_SUPPORT_DOWNGRADE_VT_TO_AUDIO_BOOL); 971 mNotifyHandoverVideoFromWifiToLTE = carrierConfig.getBoolean( 972 CarrierConfigManager.KEY_NOTIFY_HANDOVER_VIDEO_FROM_WIFI_TO_LTE_BOOL); 973 mNotifyHandoverVideoFromLTEToWifi = carrierConfig.getBoolean( 974 CarrierConfigManager.KEY_NOTIFY_HANDOVER_VIDEO_FROM_LTE_TO_WIFI_BOOL); 975 mIgnoreDataEnabledChangedForVideoCalls = carrierConfig.getBoolean( 976 CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS); 977 mIsViLteDataMetered = carrierConfig.getBoolean( 978 CarrierConfigManager.KEY_VILTE_DATA_IS_METERED_BOOL); 979 mSupportPauseVideo = carrierConfig.getBoolean( 980 CarrierConfigManager.KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL); 981 mAlwaysPlayRemoteHoldTone = carrierConfig.getBoolean( 982 CarrierConfigManager.KEY_ALWAYS_PLAY_REMOTE_HOLD_TONE_BOOL); 983 mAutoRetryFailedWifiEmergencyCall = carrierConfig.getBoolean( 984 CarrierConfigManager.KEY_AUTO_RETRY_FAILED_WIFI_EMERGENCY_CALL); 985 986 String[] mappings = carrierConfig 987 .getStringArray(CarrierConfigManager.KEY_IMS_REASONINFO_MAPPING_STRING_ARRAY); 988 if (mappings != null && mappings.length > 0) { 989 for (String mapping : mappings) { 990 String[] values = mapping.split(Pattern.quote("|")); 991 if (values.length != 3) { 992 continue; 993 } 994 995 try { 996 Integer fromCode; 997 if (values[0].equals("*")) { 998 fromCode = null; 999 } else { 1000 fromCode = Integer.parseInt(values[0]); 1001 } 1002 String message = values[1]; 1003 if (message == null) { 1004 message = ""; 1005 } 1006 int toCode = Integer.parseInt(values[2]); 1007 1008 addReasonCodeRemapping(fromCode, message, toCode); 1009 log("Loaded ImsReasonInfo mapping : fromCode = " + 1010 fromCode == null ? "any" : fromCode + " ; message = " + 1011 message + " ; toCode = " + toCode); 1012 } catch (NumberFormatException nfe) { 1013 loge("Invalid ImsReasonInfo mapping found: " + mapping); 1014 } 1015 } 1016 } else { 1017 log("No carrier ImsReasonInfo mappings defined."); 1018 } 1019 } 1020 1021 @UnsupportedAppUsage handleEcmTimer(int action)1022 private void handleEcmTimer(int action) { 1023 mPhone.handleTimerInEmergencyCallbackMode(action); 1024 switch (action) { 1025 case ImsPhone.CANCEL_ECM_TIMER: 1026 break; 1027 case ImsPhone.RESTART_ECM_TIMER: 1028 break; 1029 default: 1030 log("handleEcmTimer, unsupported action " + action); 1031 } 1032 } 1033 dialInternal(ImsPhoneConnection conn, int clirMode, int videoState, Bundle intentExtras)1034 private void dialInternal(ImsPhoneConnection conn, int clirMode, int videoState, 1035 Bundle intentExtras) { 1036 1037 if (conn == null) { 1038 return; 1039 } 1040 1041 if (conn.getAddress()== null || conn.getAddress().length() == 0 1042 || conn.getAddress().indexOf(PhoneNumberUtils.WILD) >= 0) { 1043 // Phone number is invalid 1044 conn.setDisconnectCause(DisconnectCause.INVALID_NUMBER); 1045 sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO); 1046 return; 1047 } 1048 1049 // Always unmute when initiating a new call 1050 setMute(false); 1051 boolean isEmergencyCall = mPhoneNumberUtilsProxy.isEmergencyNumber(conn.getAddress()); 1052 int serviceType = isEmergencyCall 1053 ? ImsCallProfile.SERVICE_TYPE_EMERGENCY : ImsCallProfile.SERVICE_TYPE_NORMAL; 1054 int callType = ImsCallProfile.getCallTypeFromVideoState(videoState); 1055 //TODO(vt): Is this sufficient? At what point do we know the video state of the call? 1056 conn.setVideoState(videoState); 1057 1058 try { 1059 String[] callees = new String[] { conn.getAddress() }; 1060 ImsCallProfile profile = mImsManager.createCallProfile(serviceType, callType); 1061 profile.setCallExtraInt(ImsCallProfile.EXTRA_OIR, clirMode); 1062 1063 if (isEmergencyCall) { 1064 // Set emergency call information in ImsCallProfile 1065 setEmergencyCallInfo(profile, conn); 1066 } 1067 1068 // Translate call subject intent-extra from Telecom-specific extra key to the 1069 // ImsCallProfile key. 1070 if (intentExtras != null) { 1071 if (intentExtras.containsKey(android.telecom.TelecomManager.EXTRA_CALL_SUBJECT)) { 1072 intentExtras.putString(ImsCallProfile.EXTRA_DISPLAY_TEXT, 1073 cleanseInstantLetteringMessage(intentExtras.getString( 1074 android.telecom.TelecomManager.EXTRA_CALL_SUBJECT)) 1075 ); 1076 } 1077 1078 if (conn.hasRttTextStream()) { 1079 profile.mMediaProfile.mRttMode = ImsStreamMediaProfile.RTT_MODE_FULL; 1080 } 1081 1082 if (intentExtras.containsKey(ImsCallProfile.EXTRA_IS_CALL_PULL)) { 1083 profile.mCallExtras.putBoolean(ImsCallProfile.EXTRA_IS_CALL_PULL, 1084 intentExtras.getBoolean(ImsCallProfile.EXTRA_IS_CALL_PULL)); 1085 int dialogId = intentExtras.getInt( 1086 ImsExternalCallTracker.EXTRA_IMS_EXTERNAL_CALL_ID); 1087 conn.setIsPulledCall(true); 1088 conn.setPulledDialogId(dialogId); 1089 } 1090 1091 // Pack the OEM-specific call extras. 1092 profile.mCallExtras.putBundle(ImsCallProfile.EXTRA_OEM_EXTRAS, intentExtras); 1093 1094 // NOTE: Extras to be sent over the network are packed into the 1095 // intentExtras individually, with uniquely defined keys. 1096 // These key-value pairs are processed by IMS Service before 1097 // being sent to the lower layers/to the network. 1098 } 1099 1100 ImsCall imsCall = mImsManager.makeCall(profile, callees, mImsCallListener); 1101 conn.setImsCall(imsCall); 1102 1103 mMetrics.writeOnImsCallStart(mPhone.getPhoneId(), 1104 imsCall.getSession()); 1105 1106 setVideoCallProvider(conn, imsCall); 1107 conn.setAllowAddCallDuringVideoCall(mAllowAddCallDuringVideoCall); 1108 } catch (ImsException e) { 1109 loge("dialInternal : " + e); 1110 conn.setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED); 1111 sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO); 1112 retryGetImsService(); 1113 } catch (RemoteException e) { 1114 } 1115 } 1116 1117 /** 1118 * Accepts a call with the specified video state. The video state is the video state that the 1119 * user has agreed upon in the InCall UI. 1120 * 1121 * @param videoState The video State 1122 * @throws CallStateException 1123 */ acceptCall(int videoState)1124 public void acceptCall(int videoState) throws CallStateException { 1125 if (DBG) log("acceptCall"); 1126 1127 if (mForegroundCall.getState().isAlive() 1128 && mBackgroundCall.getState().isAlive()) { 1129 throw new CallStateException("cannot accept call"); 1130 } 1131 1132 if ((mRingingCall.getState() == ImsPhoneCall.State.WAITING) 1133 && mForegroundCall.getState().isAlive()) { 1134 setMute(false); 1135 1136 boolean answeringWillDisconnect = false; 1137 ImsCall activeCall = mForegroundCall.getImsCall(); 1138 ImsCall ringingCall = mRingingCall.getImsCall(); 1139 if (mForegroundCall.hasConnections() && mRingingCall.hasConnections()) { 1140 answeringWillDisconnect = 1141 shouldDisconnectActiveCallOnAnswer(activeCall, ringingCall); 1142 } 1143 1144 // Cache video state for pending MT call. 1145 mPendingCallVideoState = videoState; 1146 1147 if (answeringWillDisconnect) { 1148 // We need to disconnect the foreground call before answering the background call. 1149 mForegroundCall.hangup(); 1150 try { 1151 ringingCall.accept(ImsCallProfile.getCallTypeFromVideoState(videoState)); 1152 } catch (ImsException e) { 1153 throw new CallStateException("cannot accept call"); 1154 } 1155 } else { 1156 holdActiveCallForWaitingCall(); 1157 } 1158 } else if (mRingingCall.getState().isRinging()) { 1159 if (DBG) log("acceptCall: incoming..."); 1160 // Always unmute when answering a new call 1161 setMute(false); 1162 try { 1163 ImsCall imsCall = mRingingCall.getImsCall(); 1164 if (imsCall != null) { 1165 imsCall.accept(ImsCallProfile.getCallTypeFromVideoState(videoState)); 1166 mMetrics.writeOnImsCommand(mPhone.getPhoneId(), imsCall.getSession(), 1167 ImsCommand.IMS_CMD_ACCEPT); 1168 } else { 1169 throw new CallStateException("no valid ims call"); 1170 } 1171 } catch (ImsException e) { 1172 throw new CallStateException("cannot accept call"); 1173 } 1174 } else { 1175 throw new CallStateException("phone not ringing"); 1176 } 1177 } 1178 rejectCall()1179 public void rejectCall () throws CallStateException { 1180 if (DBG) log("rejectCall"); 1181 1182 if (mRingingCall.getState().isRinging()) { 1183 hangup(mRingingCall); 1184 } else { 1185 throw new CallStateException("phone not ringing"); 1186 } 1187 } 1188 1189 /** 1190 * Set the emergency call information if it is an emergency call. 1191 */ setEmergencyCallInfo(ImsCallProfile profile, Connection conn)1192 private void setEmergencyCallInfo(ImsCallProfile profile, Connection conn) { 1193 EmergencyNumber num = conn.getEmergencyNumberInfo(); 1194 if (num != null) { 1195 profile.setEmergencyCallInfo(num, conn.hasKnownUserIntentEmergency()); 1196 } 1197 } 1198 1199 @UnsupportedAppUsage switchAfterConferenceSuccess()1200 private void switchAfterConferenceSuccess() { 1201 if (DBG) log("switchAfterConferenceSuccess fg =" + mForegroundCall.getState() + 1202 ", bg = " + mBackgroundCall.getState()); 1203 1204 if (mBackgroundCall.getState() == ImsPhoneCall.State.HOLDING) { 1205 log("switchAfterConferenceSuccess"); 1206 mForegroundCall.switchWith(mBackgroundCall); 1207 } 1208 } 1209 holdActiveCallForPendingMo()1210 private void holdActiveCallForPendingMo() throws CallStateException { 1211 if (mHoldSwitchingState == HoldSwapState.PENDING_SINGLE_CALL_HOLD 1212 || mHoldSwitchingState == HoldSwapState.SWAPPING_ACTIVE_AND_HELD) { 1213 logi("Ignoring hold request while already holding or swapping"); 1214 return; 1215 } 1216 ImsCall callToHold = mForegroundCall.getImsCall(); 1217 1218 mHoldSwitchingState = HoldSwapState.HOLDING_TO_DIAL_OUTGOING; 1219 logHoldSwapState("holdActiveCallForPendingMo"); 1220 1221 mForegroundCall.switchWith(mBackgroundCall); 1222 try { 1223 callToHold.hold(); 1224 mMetrics.writeOnImsCommand(mPhone.getPhoneId(), callToHold.getSession(), 1225 ImsCommand.IMS_CMD_HOLD); 1226 } catch (ImsException e) { 1227 mForegroundCall.switchWith(mBackgroundCall); 1228 throw new CallStateException(e.getMessage()); 1229 } 1230 } 1231 1232 /** 1233 * Holds the active call, possibly resuming the already-held background call if it exists. 1234 */ holdActiveCall()1235 public void holdActiveCall() throws CallStateException { 1236 if (mForegroundCall.getState() == ImsPhoneCall.State.ACTIVE) { 1237 if (mHoldSwitchingState == HoldSwapState.PENDING_SINGLE_CALL_HOLD 1238 || mHoldSwitchingState == HoldSwapState.SWAPPING_ACTIVE_AND_HELD) { 1239 logi("Ignoring hold request while already holding or swapping"); 1240 return; 1241 } 1242 ImsCall callToHold = mForegroundCall.getImsCall(); 1243 if (mBackgroundCall.getState().isAlive()) { 1244 mCallExpectedToResume = mBackgroundCall.getImsCall(); 1245 mHoldSwitchingState = HoldSwapState.SWAPPING_ACTIVE_AND_HELD; 1246 } else { 1247 mHoldSwitchingState = HoldSwapState.PENDING_SINGLE_CALL_HOLD; 1248 } 1249 logHoldSwapState("holdActiveCall"); 1250 mForegroundCall.switchWith(mBackgroundCall); 1251 try { 1252 callToHold.hold(); 1253 mMetrics.writeOnImsCommand(mPhone.getPhoneId(), callToHold.getSession(), 1254 ImsCommand.IMS_CMD_HOLD); 1255 } catch (ImsException e) { 1256 mForegroundCall.switchWith(mBackgroundCall); 1257 throw new CallStateException(e.getMessage()); 1258 } 1259 } 1260 } 1261 1262 /** 1263 * Hold the currently active call in order to answer the waiting call. 1264 */ holdActiveCallForWaitingCall()1265 public void holdActiveCallForWaitingCall() throws CallStateException { 1266 boolean switchingWithWaitingCall = !mBackgroundCall.getState().isAlive() 1267 && mRingingCall.getState() == ImsPhoneCall.State.WAITING; 1268 if (switchingWithWaitingCall) { 1269 ImsCall callToHold = mForegroundCall.getImsCall(); 1270 mHoldSwitchingState = HoldSwapState.HOLDING_TO_ANSWER_INCOMING; 1271 mForegroundCall.switchWith(mBackgroundCall); 1272 logHoldSwapState("holdActiveCallForWaitingCall"); 1273 try { 1274 callToHold.hold(); 1275 mMetrics.writeOnImsCommand(mPhone.getPhoneId(), callToHold.getSession(), 1276 ImsCommand.IMS_CMD_HOLD); 1277 } catch (ImsException e) { 1278 mForegroundCall.switchWith(mBackgroundCall); 1279 throw new CallStateException(e.getMessage()); 1280 } 1281 } 1282 } 1283 1284 /** 1285 * Unhold the currently held call. 1286 */ unholdHeldCall()1287 void unholdHeldCall() throws CallStateException { 1288 try { 1289 ImsCall imsCall = mBackgroundCall.getImsCall(); 1290 if (mHoldSwitchingState == HoldSwapState.PENDING_SINGLE_CALL_UNHOLD 1291 || mHoldSwitchingState == HoldSwapState.SWAPPING_ACTIVE_AND_HELD) { 1292 logi("Ignoring unhold request while already unholding or swapping"); 1293 return; 1294 } 1295 if (imsCall != null) { 1296 mCallExpectedToResume = imsCall; 1297 mHoldSwitchingState = HoldSwapState.PENDING_SINGLE_CALL_UNHOLD; 1298 mForegroundCall.switchWith(mBackgroundCall); 1299 logHoldSwapState("unholdCurrentCall"); 1300 imsCall.resume(); 1301 mMetrics.writeOnImsCommand(mPhone.getPhoneId(), imsCall.getSession(), 1302 ImsCommand.IMS_CMD_RESUME); 1303 } 1304 } catch (ImsException e) { 1305 throw new CallStateException(e.getMessage()); 1306 } 1307 } 1308 resumeForegroundCall()1309 private void resumeForegroundCall() throws ImsException { 1310 //resume foreground call after holding background call 1311 //they were switched before holding 1312 ImsCall imsCall = mForegroundCall.getImsCall(); 1313 if (imsCall != null) { 1314 imsCall.resume(); 1315 mMetrics.writeOnImsCommand(mPhone.getPhoneId(), imsCall.getSession(), 1316 ImsCommand.IMS_CMD_RESUME); 1317 } 1318 } 1319 answerWaitingCall()1320 private void answerWaitingCall() throws ImsException { 1321 //accept waiting call after holding background call 1322 ImsCall imsCall = mRingingCall.getImsCall(); 1323 if (imsCall != null) { 1324 imsCall.accept( 1325 ImsCallProfile.getCallTypeFromVideoState(mPendingCallVideoState)); 1326 mMetrics.writeOnImsCommand(mPhone.getPhoneId(), imsCall.getSession(), 1327 ImsCommand.IMS_CMD_ACCEPT); 1328 } 1329 } 1330 1331 // Clean up expired cache entries. maintainConnectTimeCache()1332 private void maintainConnectTimeCache() { 1333 long threshold = SystemClock.elapsedRealtime() - TIMEOUT_PARTICIPANT_CONNECT_TIME_CACHE_MS; 1334 // The cached time is the system elapsed millisecond when the CacheEntry is created. 1335 mPhoneNumAndConnTime.entrySet().removeIf(e -> e.getValue().mCachedTime < threshold); 1336 // Remove all the cached records which are older than current caching threshold. Since the 1337 // queue is FIFO, keep polling records until the queue is empty or the head of the queue is 1338 // fresh enough. 1339 while (!mUnknownPeerConnTime.isEmpty() 1340 && mUnknownPeerConnTime.peek().mCachedTime < threshold) { 1341 mUnknownPeerConnTime.poll(); 1342 } 1343 } 1344 cacheConnectionTimeWithPhoneNumber(@onNull ImsPhoneConnection connection)1345 private void cacheConnectionTimeWithPhoneNumber(@NonNull ImsPhoneConnection connection) { 1346 int callDirection = 1347 connection.isIncoming() ? android.telecom.Call.Details.DIRECTION_INCOMING 1348 : android.telecom.Call.Details.DIRECTION_OUTGOING; 1349 CacheEntry cachedConnectTime = new CacheEntry(SystemClock.elapsedRealtime(), 1350 connection.getConnectTime(), connection.getConnectTimeReal(), callDirection); 1351 maintainConnectTimeCache(); 1352 if (PhoneConstants.PRESENTATION_ALLOWED == connection.getNumberPresentation()) { 1353 // In case of merging calls with the same number, use the latest connect time. Since 1354 // that call might be dropped and re-connected. So if the connectTime is earlier than 1355 // the cache, skip. 1356 String phoneNumber = getFormattedPhoneNumber(connection.getAddress()); 1357 if (mPhoneNumAndConnTime.containsKey(phoneNumber) 1358 && connection.getConnectTime() 1359 <= mPhoneNumAndConnTime.get(phoneNumber).mConnectTime) { 1360 // Use the latest connect time. 1361 return; 1362 } 1363 mPhoneNumAndConnTime.put(phoneNumber, cachedConnectTime); 1364 } else { 1365 mUnknownPeerConnTime.add(cachedConnectTime); 1366 } 1367 } 1368 findConnectionTimeUsePhoneNumber( @onNull ConferenceParticipant participant)1369 private CacheEntry findConnectionTimeUsePhoneNumber( 1370 @NonNull ConferenceParticipant participant) { 1371 maintainConnectTimeCache(); 1372 if (PhoneConstants.PRESENTATION_ALLOWED == participant.getParticipantPresentation()) { 1373 if (participant.getHandle() == null 1374 || participant.getHandle().getSchemeSpecificPart() == null) { 1375 return null; 1376 } 1377 1378 String number = ConferenceParticipant.getParticipantAddress(participant.getHandle(), 1379 getCountryIso()).getSchemeSpecificPart(); 1380 if (TextUtils.isEmpty(number)) { 1381 return null; 1382 } 1383 String formattedNumber = getFormattedPhoneNumber(number); 1384 return mPhoneNumAndConnTime.get(formattedNumber); 1385 } else { 1386 return mUnknownPeerConnTime.poll(); 1387 } 1388 } 1389 getFormattedPhoneNumber(String number)1390 private String getFormattedPhoneNumber(String number) { 1391 String countryIso = getCountryIso(); 1392 if (countryIso == null) { 1393 return number; 1394 } 1395 String phoneNumber = PhoneNumberUtils.formatNumberToE164(number, countryIso); 1396 return phoneNumber == null ? number : phoneNumber; 1397 } 1398 getCountryIso()1399 private String getCountryIso() { 1400 int subId = mPhone.getSubId(); 1401 SubscriptionInfo info = 1402 SubscriptionManager.from(mPhone.getContext()).getActiveSubscriptionInfo(subId); 1403 return info == null ? null : info.getCountryIso(); 1404 } 1405 1406 public void conference()1407 conference() { 1408 ImsCall fgImsCall = mForegroundCall.getImsCall(); 1409 if (fgImsCall == null) { 1410 log("conference no foreground ims call"); 1411 return; 1412 } 1413 1414 ImsCall bgImsCall = mBackgroundCall.getImsCall(); 1415 if (bgImsCall == null) { 1416 log("conference no background ims call"); 1417 return; 1418 } 1419 1420 if (fgImsCall.isCallSessionMergePending()) { 1421 log("conference: skip; foreground call already in process of merging."); 1422 return; 1423 } 1424 1425 if (bgImsCall.isCallSessionMergePending()) { 1426 log("conference: skip; background call already in process of merging."); 1427 return; 1428 } 1429 1430 // Keep track of the connect time of the earliest call so that it can be set on the 1431 // {@code ImsConference} when it is created. 1432 long foregroundConnectTime = mForegroundCall.getEarliestConnectTime(); 1433 long backgroundConnectTime = mBackgroundCall.getEarliestConnectTime(); 1434 long conferenceConnectTime; 1435 if (foregroundConnectTime > 0 && backgroundConnectTime > 0) { 1436 conferenceConnectTime = Math.min(mForegroundCall.getEarliestConnectTime(), 1437 mBackgroundCall.getEarliestConnectTime()); 1438 log("conference - using connect time = " + conferenceConnectTime); 1439 } else if (foregroundConnectTime > 0) { 1440 log("conference - bg call connect time is 0; using fg = " + foregroundConnectTime); 1441 conferenceConnectTime = foregroundConnectTime; 1442 } else { 1443 log("conference - fg call connect time is 0; using bg = " + backgroundConnectTime); 1444 conferenceConnectTime = backgroundConnectTime; 1445 } 1446 1447 String foregroundId = ""; 1448 ImsPhoneConnection foregroundConnection = mForegroundCall.getFirstConnection(); 1449 if (foregroundConnection != null) { 1450 foregroundConnection.setConferenceConnectTime(conferenceConnectTime); 1451 foregroundConnection.handleMergeStart(); 1452 foregroundId = foregroundConnection.getTelecomCallId(); 1453 cacheConnectionTimeWithPhoneNumber(foregroundConnection); 1454 } 1455 String backgroundId = ""; 1456 ImsPhoneConnection backgroundConnection = findConnection(bgImsCall); 1457 if (backgroundConnection != null) { 1458 backgroundConnection.handleMergeStart(); 1459 backgroundId = backgroundConnection.getTelecomCallId(); 1460 cacheConnectionTimeWithPhoneNumber(backgroundConnection); 1461 } 1462 log("conference: fgCallId=" + foregroundId + ", bgCallId=" + backgroundId); 1463 1464 try { 1465 fgImsCall.merge(bgImsCall); 1466 } catch (ImsException e) { 1467 log("conference " + e.getMessage()); 1468 } 1469 } 1470 1471 public void explicitCallTransfer()1472 explicitCallTransfer() { 1473 //TODO : implement 1474 } 1475 1476 @UnsupportedAppUsage 1477 public void clearDisconnected()1478 clearDisconnected() { 1479 if (DBG) log("clearDisconnected"); 1480 1481 internalClearDisconnected(); 1482 1483 updatePhoneState(); 1484 mPhone.notifyPreciseCallStateChanged(); 1485 } 1486 1487 public boolean canConference()1488 canConference() { 1489 return mForegroundCall.getState() == ImsPhoneCall.State.ACTIVE 1490 && mBackgroundCall.getState() == ImsPhoneCall.State.HOLDING 1491 && !mBackgroundCall.isFull() 1492 && !mForegroundCall.isFull(); 1493 } 1494 1495 /** 1496 * Determines if there are issues which would preclude dialing an outgoing call. Throws a 1497 * {@link CallStateException} if there is an issue. 1498 * @throws CallStateException 1499 */ checkForDialIssues()1500 public void checkForDialIssues() throws CallStateException { 1501 String disableCall = SystemProperties.get( 1502 TelephonyProperties.PROPERTY_DISABLE_CALL, "false"); 1503 if (disableCall.equals("true")) { 1504 throw new CallStateException(CallStateException.ERROR_CALLING_DISABLED, 1505 "ro.telephony.disable-call has been used to disable calling."); 1506 } 1507 if (mPendingMO != null) { 1508 throw new CallStateException(CallStateException.ERROR_ALREADY_DIALING, 1509 "Another outgoing call is already being dialed."); 1510 } 1511 if (mRingingCall.isRinging()) { 1512 throw new CallStateException(CallStateException.ERROR_CALL_RINGING, 1513 "Can't place a call while another is ringing."); 1514 } 1515 if (mForegroundCall.getState().isAlive() & mBackgroundCall.getState().isAlive()) { 1516 throw new CallStateException(CallStateException.ERROR_TOO_MANY_CALLS, 1517 "Already an active foreground and background call."); 1518 } 1519 } 1520 1521 public boolean canTransfer()1522 canTransfer() { 1523 return mForegroundCall.getState() == ImsPhoneCall.State.ACTIVE 1524 && mBackgroundCall.getState() == ImsPhoneCall.State.HOLDING; 1525 } 1526 1527 //***** Private Instance Methods 1528 1529 private void internalClearDisconnected()1530 internalClearDisconnected() { 1531 mRingingCall.clearDisconnected(); 1532 mForegroundCall.clearDisconnected(); 1533 mBackgroundCall.clearDisconnected(); 1534 mHandoverCall.clearDisconnected(); 1535 } 1536 1537 @UnsupportedAppUsage 1538 private void updatePhoneState()1539 updatePhoneState() { 1540 PhoneConstants.State oldState = mState; 1541 1542 boolean isPendingMOIdle = mPendingMO == null || !mPendingMO.getState().isAlive(); 1543 1544 if (mRingingCall.isRinging()) { 1545 mState = PhoneConstants.State.RINGING; 1546 } else if (!isPendingMOIdle || !mForegroundCall.isIdle() || !mBackgroundCall.isIdle()) { 1547 // There is a non-idle call, so we're off the hook. 1548 mState = PhoneConstants.State.OFFHOOK; 1549 } else { 1550 mState = PhoneConstants.State.IDLE; 1551 } 1552 1553 if (mState == PhoneConstants.State.IDLE && oldState != mState) { 1554 mVoiceCallEndedRegistrants.notifyRegistrants( 1555 new AsyncResult(null, null, null)); 1556 } else if (oldState == PhoneConstants.State.IDLE && oldState != mState) { 1557 mVoiceCallStartedRegistrants.notifyRegistrants ( 1558 new AsyncResult(null, null, null)); 1559 } 1560 1561 if (DBG) { 1562 log("updatePhoneState pendingMo = " + (mPendingMO == null ? "null" 1563 : mPendingMO.getState()) + ", fg= " + mForegroundCall.getState() + "(" 1564 + mForegroundCall.getConnections().size() + "), bg= " + mBackgroundCall 1565 .getState() + "(" + mBackgroundCall.getConnections().size() + ")"); 1566 log("updatePhoneState oldState=" + oldState + ", newState=" + mState); 1567 } 1568 1569 if (mState != oldState) { 1570 mPhone.notifyPhoneStateChanged(); 1571 mMetrics.writePhoneState(mPhone.getPhoneId(), mState); 1572 notifyPhoneStateChanged(oldState, mState); 1573 } 1574 } 1575 1576 private void handleRadioNotAvailable()1577 handleRadioNotAvailable() { 1578 // handlePollCalls will clear out its 1579 // call list when it gets the CommandException 1580 // error result from this 1581 pollCallsWhenSafe(); 1582 } 1583 1584 private void dumpState()1585 dumpState() { 1586 List l; 1587 1588 log("Phone State:" + mState); 1589 1590 log("Ringing call: " + mRingingCall.toString()); 1591 1592 l = mRingingCall.getConnections(); 1593 for (int i = 0, s = l.size(); i < s; i++) { 1594 log(l.get(i).toString()); 1595 } 1596 1597 log("Foreground call: " + mForegroundCall.toString()); 1598 1599 l = mForegroundCall.getConnections(); 1600 for (int i = 0, s = l.size(); i < s; i++) { 1601 log(l.get(i).toString()); 1602 } 1603 1604 log("Background call: " + mBackgroundCall.toString()); 1605 1606 l = mBackgroundCall.getConnections(); 1607 for (int i = 0, s = l.size(); i < s; i++) { 1608 log(l.get(i).toString()); 1609 } 1610 1611 } 1612 1613 //***** Called from ImsPhone 1614 /** 1615 * Set the TTY mode. This is the actual tty mode (varies depending on peripheral status) 1616 */ setTtyMode(int ttyMode)1617 public void setTtyMode(int ttyMode) { 1618 if (mImsManager == null) { 1619 Log.w(LOG_TAG, "ImsManager is null when setting TTY mode"); 1620 return; 1621 } 1622 1623 try { 1624 mImsManager.setTtyMode(ttyMode); 1625 } catch (ImsException e) { 1626 loge("setTtyMode : " + e); 1627 retryGetImsService(); 1628 } 1629 } 1630 1631 /** 1632 * Sets the UI TTY mode. This is the preferred TTY mode that the user sets in the call 1633 * settings screen. 1634 */ setUiTTYMode(int uiTtyMode, Message onComplete)1635 public void setUiTTYMode(int uiTtyMode, Message onComplete) { 1636 if (mImsManager == null) { 1637 mPhone.sendErrorResponse(onComplete, getImsManagerIsNullException()); 1638 return; 1639 } 1640 1641 try { 1642 mImsManager.setUiTTYMode(mPhone.getContext(), uiTtyMode, onComplete); 1643 } catch (ImsException e) { 1644 loge("setUITTYMode : " + e); 1645 mPhone.sendErrorResponse(onComplete, e); 1646 retryGetImsService(); 1647 } 1648 } 1649 setMute(boolean mute)1650 public void setMute(boolean mute) { 1651 mDesiredMute = mute; 1652 mForegroundCall.setMute(mute); 1653 } 1654 getMute()1655 public boolean getMute() { 1656 return mDesiredMute; 1657 } 1658 1659 /** 1660 * Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>, 1661 * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15, 1662 * and event flash to 16. Currently, event flash is not supported. 1663 * 1664 * @param c that represents the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs. 1665 * @param result the result message to send when done. If non-null, the {@link Message} must 1666 * contain a valid {@link android.os.Messenger} in the {@link Message#replyTo} field, 1667 * since this can be used across IPC boundaries. 1668 */ sendDtmf(char c, Message result)1669 public void sendDtmf(char c, Message result) { 1670 if (DBG) log("sendDtmf"); 1671 1672 ImsCall imscall = mForegroundCall.getImsCall(); 1673 if (imscall != null) { 1674 imscall.sendDtmf(c, result); 1675 } 1676 } 1677 1678 public void startDtmf(char c)1679 startDtmf(char c) { 1680 if (DBG) log("startDtmf"); 1681 1682 ImsCall imscall = mForegroundCall.getImsCall(); 1683 if (imscall != null) { 1684 imscall.startDtmf(c); 1685 } else { 1686 loge("startDtmf : no foreground call"); 1687 } 1688 } 1689 1690 public void stopDtmf()1691 stopDtmf() { 1692 if (DBG) log("stopDtmf"); 1693 1694 ImsCall imscall = mForegroundCall.getImsCall(); 1695 if (imscall != null) { 1696 imscall.stopDtmf(); 1697 } else { 1698 loge("stopDtmf : no foreground call"); 1699 } 1700 } 1701 1702 //***** Called from ImsPhoneConnection 1703 hangup(ImsPhoneConnection conn)1704 public void hangup (ImsPhoneConnection conn) throws CallStateException { 1705 if (DBG) log("hangup connection"); 1706 1707 if (conn.getOwner() != this) { 1708 throw new CallStateException ("ImsPhoneConnection " + conn 1709 + "does not belong to ImsPhoneCallTracker " + this); 1710 } 1711 1712 hangup(conn.getCall()); 1713 } 1714 1715 //***** Called from ImsPhoneCall 1716 hangup(ImsPhoneCall call)1717 public void hangup (ImsPhoneCall call) throws CallStateException { 1718 if (DBG) log("hangup call"); 1719 1720 if (call.getConnections().size() == 0) { 1721 throw new CallStateException("no connections"); 1722 } 1723 1724 ImsCall imsCall = call.getImsCall(); 1725 boolean rejectCall = false; 1726 1727 if (call == mRingingCall) { 1728 if (Phone.DEBUG_PHONE) log("(ringing) hangup incoming"); 1729 rejectCall = true; 1730 } else if (call == mForegroundCall) { 1731 if (call.isDialingOrAlerting()) { 1732 if (Phone.DEBUG_PHONE) { 1733 log("(foregnd) hangup dialing or alerting..."); 1734 } 1735 } else { 1736 if (Phone.DEBUG_PHONE) { 1737 log("(foregnd) hangup foreground"); 1738 } 1739 //held call will be resumed by onCallTerminated 1740 } 1741 } else if (call == mBackgroundCall) { 1742 if (Phone.DEBUG_PHONE) { 1743 log("(backgnd) hangup waiting or background"); 1744 } 1745 } else { 1746 throw new CallStateException ("ImsPhoneCall " + call + 1747 "does not belong to ImsPhoneCallTracker " + this); 1748 } 1749 1750 call.onHangupLocal(); 1751 1752 try { 1753 if (imsCall != null) { 1754 if (rejectCall) { 1755 imsCall.reject(ImsReasonInfo.CODE_USER_DECLINE); 1756 mMetrics.writeOnImsCommand(mPhone.getPhoneId(), imsCall.getSession(), 1757 ImsCommand.IMS_CMD_REJECT); 1758 } else { 1759 imsCall.terminate(ImsReasonInfo.CODE_USER_TERMINATED); 1760 mMetrics.writeOnImsCommand(mPhone.getPhoneId(), imsCall.getSession(), 1761 ImsCommand.IMS_CMD_TERMINATE); 1762 } 1763 } else if (mPendingMO != null && call == mForegroundCall) { 1764 // is holding a foreground call 1765 mPendingMO.update(null, ImsPhoneCall.State.DISCONNECTED); 1766 mPendingMO.onDisconnect(); 1767 removeConnection(mPendingMO); 1768 mPendingMO = null; 1769 updatePhoneState(); 1770 removeMessages(EVENT_DIAL_PENDINGMO); 1771 } 1772 } catch (ImsException e) { 1773 throw new CallStateException(e.getMessage()); 1774 } 1775 1776 mPhone.notifyPreciseCallStateChanged(); 1777 } 1778 callEndCleanupHandOverCallIfAny()1779 void callEndCleanupHandOverCallIfAny() { 1780 if (mHandoverCall.mConnections.size() > 0) { 1781 if (DBG) log("callEndCleanupHandOverCallIfAny, mHandoverCall.mConnections=" 1782 + mHandoverCall.mConnections); 1783 mHandoverCall.mConnections.clear(); 1784 mConnections.clear(); 1785 mState = PhoneConstants.State.IDLE; 1786 } 1787 } 1788 1789 sendUSSD(String ussdString, Message response)1790 public void sendUSSD (String ussdString, Message response) { 1791 if (DBG) log("sendUSSD"); 1792 1793 try { 1794 if (mUssdSession != null) { 1795 // Doesn't need mPendingUssd here. Listeners would use it if not null. 1796 mPendingUssd = null; 1797 mUssdSession.sendUssd(ussdString); 1798 AsyncResult.forMessage(response, null, null); 1799 response.sendToTarget(); 1800 return; 1801 } 1802 1803 if (mImsManager == null) { 1804 mPhone.sendErrorResponse(response, getImsManagerIsNullException()); 1805 return; 1806 } 1807 1808 String[] callees = new String[] { ussdString }; 1809 ImsCallProfile profile = mImsManager.createCallProfile( 1810 ImsCallProfile.SERVICE_TYPE_NORMAL, ImsCallProfile.CALL_TYPE_VOICE); 1811 profile.setCallExtraInt(ImsCallProfile.EXTRA_DIALSTRING, 1812 ImsCallProfile.DIALSTRING_USSD); 1813 1814 mUssdSession = mImsManager.makeCall(profile, callees, mImsUssdListener); 1815 mPendingUssd = response; 1816 if (DBG) log("pending ussd updated, " + mPendingUssd); 1817 } catch (ImsException e) { 1818 loge("sendUSSD : " + e); 1819 mPhone.sendErrorResponse(response, e); 1820 retryGetImsService(); 1821 } 1822 } 1823 1824 /** 1825 * Cancel USSD session. 1826 * 1827 * @param msg The message to dispatch when the USSD session terminated. 1828 */ cancelUSSD(Message msg)1829 public void cancelUSSD(Message msg) { 1830 if (mUssdSession == null) return; 1831 mPendingUssd = msg; 1832 mUssdSession.terminate(ImsReasonInfo.CODE_USER_TERMINATED); 1833 } 1834 findConnection(final ImsCall imsCall)1835 private synchronized ImsPhoneConnection findConnection(final ImsCall imsCall) { 1836 for (ImsPhoneConnection conn : mConnections) { 1837 if (conn.getImsCall() == imsCall) { 1838 return conn; 1839 } 1840 } 1841 return null; 1842 } 1843 1844 @UnsupportedAppUsage removeConnection(ImsPhoneConnection conn)1845 private synchronized void removeConnection(ImsPhoneConnection conn) { 1846 mConnections.remove(conn); 1847 // If not emergency call is remaining, notify emergency call registrants 1848 if (mIsInEmergencyCall) { 1849 boolean isEmergencyCallInList = false; 1850 // if no emergency calls pending, set this to false 1851 for (ImsPhoneConnection imsPhoneConnection : mConnections) { 1852 if (imsPhoneConnection != null && imsPhoneConnection.isEmergency() == true) { 1853 isEmergencyCallInList = true; 1854 break; 1855 } 1856 } 1857 1858 if (!isEmergencyCallInList) { 1859 mIsInEmergencyCall = false; 1860 mPhone.sendEmergencyCallStateChange(false); 1861 } 1862 } 1863 } 1864 1865 @UnsupportedAppUsage addConnection(ImsPhoneConnection conn)1866 private synchronized void addConnection(ImsPhoneConnection conn) { 1867 mConnections.add(conn); 1868 if (conn.isEmergency()) { 1869 mIsInEmergencyCall = true; 1870 mPhone.sendEmergencyCallStateChange(true); 1871 } 1872 } 1873 processCallStateChange(ImsCall imsCall, ImsPhoneCall.State state, int cause)1874 private void processCallStateChange(ImsCall imsCall, ImsPhoneCall.State state, int cause) { 1875 if (DBG) log("processCallStateChange " + imsCall + " state=" + state + " cause=" + cause); 1876 // This method is called on onCallUpdate() where there is not necessarily a call state 1877 // change. In these situations, we'll ignore the state related updates and only process 1878 // the change in media capabilities (as expected). The default is to not ignore state 1879 // changes so we do not change existing behavior. 1880 processCallStateChange(imsCall, state, cause, false /* do not ignore state update */); 1881 } 1882 processCallStateChange(ImsCall imsCall, ImsPhoneCall.State state, int cause, boolean ignoreState)1883 private void processCallStateChange(ImsCall imsCall, ImsPhoneCall.State state, int cause, 1884 boolean ignoreState) { 1885 if (DBG) { 1886 log("processCallStateChange state=" + state + " cause=" + cause 1887 + " ignoreState=" + ignoreState); 1888 } 1889 1890 if (imsCall == null) return; 1891 1892 boolean changed = false; 1893 ImsPhoneConnection conn = findConnection(imsCall); 1894 1895 if (conn == null) { 1896 // TODO : what should be done? 1897 return; 1898 } 1899 1900 // processCallStateChange is triggered for onCallUpdated as well. 1901 // onCallUpdated should not modify the state of the call 1902 // It should modify only other capabilities of call through updateMediaCapabilities 1903 // State updates will be triggered through individual callbacks 1904 // i.e. onCallHeld, onCallResume, etc and conn.update will be responsible for the update 1905 conn.updateMediaCapabilities(imsCall); 1906 if (ignoreState) { 1907 conn.updateAddressDisplay(imsCall); 1908 conn.updateExtras(imsCall); 1909 1910 maybeSetVideoCallProvider(conn, imsCall); 1911 return; 1912 } 1913 1914 changed = conn.update(imsCall, state); 1915 if (state == ImsPhoneCall.State.DISCONNECTED) { 1916 changed = conn.onDisconnect(cause) || changed; 1917 //detach the disconnected connections 1918 conn.getCall().detach(conn); 1919 removeConnection(conn); 1920 } 1921 1922 if (changed) { 1923 if (conn.getCall() == mHandoverCall) return; 1924 updatePhoneState(); 1925 mPhone.notifyPreciseCallStateChanged(); 1926 } 1927 } 1928 maybeSetVideoCallProvider(ImsPhoneConnection conn, ImsCall imsCall)1929 private void maybeSetVideoCallProvider(ImsPhoneConnection conn, ImsCall imsCall) { 1930 android.telecom.Connection.VideoProvider connVideoProvider = conn.getVideoProvider(); 1931 if (connVideoProvider != null || imsCall.getCallSession().getVideoCallProvider() == null) { 1932 return; 1933 } 1934 1935 try { 1936 setVideoCallProvider(conn, imsCall); 1937 } catch (RemoteException e) { 1938 loge("maybeSetVideoCallProvider: exception " + e); 1939 } 1940 } 1941 1942 /** 1943 * Adds a reason code remapping, for test purposes. 1944 * 1945 * @param fromCode The from code, or {@code null} if all. 1946 * @param message The message to map. 1947 * @param toCode The code to remap to. 1948 */ 1949 @VisibleForTesting addReasonCodeRemapping(Integer fromCode, String message, Integer toCode)1950 public void addReasonCodeRemapping(Integer fromCode, String message, Integer toCode) { 1951 mImsReasonCodeMap.put(new Pair<>(fromCode, message), toCode); 1952 } 1953 1954 /** 1955 * Returns the {@link ImsReasonInfo#getCode()}, potentially remapping to a new value based on 1956 * the {@link ImsReasonInfo#getCode()} and {@link ImsReasonInfo#getExtraMessage()}. 1957 * 1958 * See {@link #mImsReasonCodeMap}. 1959 * 1960 * @param reasonInfo The {@link ImsReasonInfo}. 1961 * @return The remapped code. 1962 */ 1963 @VisibleForTesting maybeRemapReasonCode(ImsReasonInfo reasonInfo)1964 public @ImsReasonInfo.ImsCode int maybeRemapReasonCode(ImsReasonInfo reasonInfo) { 1965 int code = reasonInfo.getCode(); 1966 String reason = reasonInfo.getExtraMessage(); 1967 if (reason == null) { 1968 reason = ""; 1969 } 1970 log("maybeRemapReasonCode : fromCode = " + reasonInfo.getCode() + " ; message = " 1971 + reason); 1972 Pair<Integer, String> toCheck = new Pair<>(code, reason); 1973 Pair<Integer, String> wildcardToCheck = new Pair<>(null, reason); 1974 if (mImsReasonCodeMap.containsKey(toCheck)) { 1975 int toCode = mImsReasonCodeMap.get(toCheck); 1976 1977 log("maybeRemapReasonCode : fromCode = " + reasonInfo.getCode() + " ; message = " 1978 + reason + " ; toCode = " + toCode); 1979 return toCode; 1980 } else if (!reason.isEmpty() && mImsReasonCodeMap.containsKey(wildcardToCheck)) { 1981 // Handle the case where a wildcard is specified for the fromCode; in this case we will 1982 // match without caring about the fromCode. 1983 // If the reason is empty, we won't do wildcard remapping; otherwise we'd basically be 1984 // able to remap all ImsReasonInfo codes to a single code, which is not desirable. 1985 int toCode = mImsReasonCodeMap.get(wildcardToCheck); 1986 1987 log("maybeRemapReasonCode : fromCode(wildcard) = " + reasonInfo.getCode() + 1988 " ; message = " + reason + " ; toCode = " + toCode); 1989 return toCode; 1990 } 1991 return code; 1992 } 1993 1994 /** 1995 * Maps an {@link ImsReasonInfo} reason code to a {@link DisconnectCause} cause code. 1996 * The {@link Call.State} provided is the state of the call prior to disconnection. 1997 * @param reasonInfo the {@link ImsReasonInfo} for the disconnection. 1998 * @param callState The {@link Call.State} prior to disconnection. 1999 * @return The {@link DisconnectCause} code. 2000 */ 2001 @VisibleForTesting getDisconnectCauseFromReasonInfo(ImsReasonInfo reasonInfo, Call.State callState)2002 public int getDisconnectCauseFromReasonInfo(ImsReasonInfo reasonInfo, Call.State callState) { 2003 int cause = DisconnectCause.ERROR_UNSPECIFIED; 2004 2005 int code = maybeRemapReasonCode(reasonInfo); 2006 switch (code) { 2007 case ImsReasonInfo.CODE_SIP_ALTERNATE_EMERGENCY_CALL: 2008 return DisconnectCause.IMS_SIP_ALTERNATE_EMERGENCY_CALL; 2009 case ImsReasonInfo.CODE_SIP_BAD_ADDRESS: 2010 case ImsReasonInfo.CODE_SIP_NOT_REACHABLE: 2011 return DisconnectCause.NUMBER_UNREACHABLE; 2012 2013 case ImsReasonInfo.CODE_SIP_BUSY: 2014 return DisconnectCause.BUSY; 2015 2016 case ImsReasonInfo.CODE_USER_TERMINATED: 2017 return DisconnectCause.LOCAL; 2018 2019 case ImsReasonInfo.CODE_LOCAL_ENDED_BY_CONFERENCE_MERGE: 2020 return DisconnectCause.IMS_MERGED_SUCCESSFULLY; 2021 2022 case ImsReasonInfo.CODE_LOCAL_CALL_DECLINE: 2023 case ImsReasonInfo.CODE_REMOTE_CALL_DECLINE: 2024 // If the call has been declined locally (on this device), or on remotely (on 2025 // another device using multiendpoint functionality), mark it as rejected. 2026 return DisconnectCause.INCOMING_REJECTED; 2027 2028 case ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE: 2029 case ImsReasonInfo.CODE_SIP_USER_REJECTED: 2030 return DisconnectCause.NORMAL; 2031 2032 case ImsReasonInfo.CODE_SIP_FORBIDDEN: 2033 return DisconnectCause.SERVER_ERROR; 2034 2035 case ImsReasonInfo.CODE_SIP_REDIRECTED: 2036 case ImsReasonInfo.CODE_SIP_BAD_REQUEST: 2037 case ImsReasonInfo.CODE_SIP_NOT_ACCEPTABLE: 2038 case ImsReasonInfo.CODE_SIP_GLOBAL_ERROR: 2039 return DisconnectCause.SERVER_ERROR; 2040 2041 case ImsReasonInfo.CODE_SIP_SERVICE_UNAVAILABLE: 2042 case ImsReasonInfo.CODE_SIP_SERVER_ERROR: 2043 return DisconnectCause.SERVER_UNREACHABLE; 2044 2045 case ImsReasonInfo.CODE_SIP_NOT_FOUND: 2046 return DisconnectCause.INVALID_NUMBER; 2047 2048 case ImsReasonInfo.CODE_LOCAL_NETWORK_ROAMING: 2049 case ImsReasonInfo.CODE_LOCAL_NETWORK_IP_CHANGED: 2050 case ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN: 2051 case ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE: 2052 case ImsReasonInfo.CODE_LOCAL_NOT_REGISTERED: 2053 case ImsReasonInfo.CODE_LOCAL_NETWORK_NO_LTE_COVERAGE: 2054 case ImsReasonInfo.CODE_LOCAL_NETWORK_NO_SERVICE: 2055 case ImsReasonInfo.CODE_LOCAL_CALL_VCC_ON_PROGRESSING: 2056 return DisconnectCause.OUT_OF_SERVICE; 2057 2058 case ImsReasonInfo.CODE_SIP_REQUEST_TIMEOUT: 2059 case ImsReasonInfo.CODE_TIMEOUT_1XX_WAITING: 2060 case ImsReasonInfo.CODE_TIMEOUT_NO_ANSWER: 2061 case ImsReasonInfo.CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE: 2062 return DisconnectCause.TIMED_OUT; 2063 2064 case ImsReasonInfo.CODE_LOCAL_POWER_OFF: 2065 return DisconnectCause.POWER_OFF; 2066 2067 case ImsReasonInfo.CODE_LOCAL_LOW_BATTERY: 2068 case ImsReasonInfo.CODE_LOW_BATTERY: { 2069 if (callState == Call.State.DIALING) { 2070 return DisconnectCause.DIAL_LOW_BATTERY; 2071 } else { 2072 return DisconnectCause.LOW_BATTERY; 2073 } 2074 } 2075 2076 case ImsReasonInfo.CODE_CALL_BARRED: 2077 return DisconnectCause.CALL_BARRED; 2078 2079 case ImsReasonInfo.CODE_FDN_BLOCKED: 2080 return DisconnectCause.FDN_BLOCKED; 2081 2082 case ImsReasonInfo.CODE_IMEI_NOT_ACCEPTED: 2083 return DisconnectCause.IMEI_NOT_ACCEPTED; 2084 2085 case ImsReasonInfo.CODE_ANSWERED_ELSEWHERE: 2086 return DisconnectCause.ANSWERED_ELSEWHERE; 2087 2088 case ImsReasonInfo.CODE_CALL_END_CAUSE_CALL_PULL: 2089 return DisconnectCause.CALL_PULLED; 2090 2091 case ImsReasonInfo.CODE_MAXIMUM_NUMBER_OF_CALLS_REACHED: 2092 return DisconnectCause.MAXIMUM_NUMBER_OF_CALLS_REACHED; 2093 2094 case ImsReasonInfo.CODE_DATA_DISABLED: 2095 return DisconnectCause.DATA_DISABLED; 2096 2097 case ImsReasonInfo.CODE_DATA_LIMIT_REACHED: 2098 return DisconnectCause.DATA_LIMIT_REACHED; 2099 2100 case ImsReasonInfo.CODE_WIFI_LOST: 2101 return DisconnectCause.WIFI_LOST; 2102 2103 case ImsReasonInfo.CODE_ACCESS_CLASS_BLOCKED: 2104 return DisconnectCause.IMS_ACCESS_BLOCKED; 2105 2106 case ImsReasonInfo.CODE_EMERGENCY_TEMP_FAILURE: 2107 return DisconnectCause.EMERGENCY_TEMP_FAILURE; 2108 2109 case ImsReasonInfo.CODE_EMERGENCY_PERM_FAILURE: 2110 return DisconnectCause.EMERGENCY_PERM_FAILURE; 2111 2112 case ImsReasonInfo.CODE_DIAL_MODIFIED_TO_USSD: 2113 return DisconnectCause.DIAL_MODIFIED_TO_USSD; 2114 2115 case ImsReasonInfo.CODE_DIAL_MODIFIED_TO_SS: 2116 return DisconnectCause.DIAL_MODIFIED_TO_SS; 2117 2118 case ImsReasonInfo.CODE_DIAL_MODIFIED_TO_DIAL: 2119 return DisconnectCause.DIAL_MODIFIED_TO_DIAL; 2120 2121 case ImsReasonInfo.CODE_DIAL_MODIFIED_TO_DIAL_VIDEO: 2122 return DisconnectCause.DIAL_MODIFIED_TO_DIAL_VIDEO; 2123 2124 case ImsReasonInfo.CODE_DIAL_VIDEO_MODIFIED_TO_DIAL: 2125 return DisconnectCause.DIAL_VIDEO_MODIFIED_TO_DIAL; 2126 2127 case ImsReasonInfo.CODE_DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO: 2128 return DisconnectCause.DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO; 2129 2130 case ImsReasonInfo.CODE_DIAL_VIDEO_MODIFIED_TO_SS: 2131 return DisconnectCause.DIAL_VIDEO_MODIFIED_TO_SS; 2132 2133 case ImsReasonInfo.CODE_DIAL_VIDEO_MODIFIED_TO_USSD: 2134 return DisconnectCause.DIAL_VIDEO_MODIFIED_TO_USSD; 2135 2136 case ImsReasonInfo.CODE_UNOBTAINABLE_NUMBER: 2137 return DisconnectCause.UNOBTAINABLE_NUMBER; 2138 2139 case ImsReasonInfo.CODE_UNSPECIFIED: 2140 if (mPhone.getDefaultPhone().getServiceStateTracker().mRestrictedState 2141 .isCsRestricted()) { 2142 return DisconnectCause.CS_RESTRICTED; 2143 } else if (mPhone.getDefaultPhone().getServiceStateTracker().mRestrictedState 2144 .isCsEmergencyRestricted()) { 2145 return DisconnectCause.CS_RESTRICTED_EMERGENCY; 2146 } else if (mPhone.getDefaultPhone().getServiceStateTracker().mRestrictedState 2147 .isCsNormalRestricted()) { 2148 return DisconnectCause.CS_RESTRICTED_NORMAL; 2149 } 2150 break; 2151 2152 default: 2153 } 2154 2155 return cause; 2156 } 2157 2158 /** 2159 * @return true if the phone is in Emergency Callback mode, otherwise false 2160 */ isPhoneInEcbMode()2161 private boolean isPhoneInEcbMode() { 2162 return mPhone != null && mPhone.isInEcm(); 2163 } 2164 2165 /** 2166 * Before dialing pending MO request, check for the Emergency Callback mode. 2167 * If device is in Emergency callback mode, then exit the mode before dialing pending MO. 2168 */ 2169 @UnsupportedAppUsage dialPendingMO()2170 private void dialPendingMO() { 2171 boolean isPhoneInEcmMode = isPhoneInEcbMode(); 2172 boolean isEmergencyNumber = mPendingMO.isEmergency(); 2173 if ((!isPhoneInEcmMode) || (isPhoneInEcmMode && isEmergencyNumber)) { 2174 sendEmptyMessage(EVENT_DIAL_PENDINGMO); 2175 } else { 2176 sendEmptyMessage(EVENT_EXIT_ECBM_BEFORE_PENDINGMO); 2177 } 2178 } 2179 2180 /** 2181 * Listen to the IMS call state change 2182 */ 2183 private ImsCall.Listener mImsCallListener = new ImsCall.Listener() { 2184 @Override 2185 public void onCallProgressing(ImsCall imsCall) { 2186 if (DBG) log("onCallProgressing"); 2187 2188 mPendingMO = null; 2189 processCallStateChange(imsCall, ImsPhoneCall.State.ALERTING, 2190 DisconnectCause.NOT_DISCONNECTED); 2191 mMetrics.writeOnImsCallProgressing(mPhone.getPhoneId(), imsCall.getCallSession()); 2192 } 2193 2194 @Override 2195 public void onCallStarted(ImsCall imsCall) { 2196 if (DBG) log("onCallStarted"); 2197 2198 if (mHoldSwitchingState == HoldSwapState.HOLDING_TO_ANSWER_INCOMING) { 2199 // If we put a call on hold to answer an incoming call, we should reset the 2200 // variables that keep track of the switch here. 2201 if (mCallExpectedToResume != null && mCallExpectedToResume == imsCall) { 2202 if (DBG) log("onCallStarted: starting a call as a result of a switch."); 2203 mHoldSwitchingState = HoldSwapState.INACTIVE; 2204 mCallExpectedToResume = null; 2205 logHoldSwapState("onCallStarted"); 2206 } 2207 } 2208 2209 mPendingMO = null; 2210 processCallStateChange(imsCall, ImsPhoneCall.State.ACTIVE, 2211 DisconnectCause.NOT_DISCONNECTED); 2212 2213 if (mNotifyVtHandoverToWifiFail && imsCall.isVideoCall() && !imsCall.isWifiCall()) { 2214 if (isWifiConnected()) { 2215 // Schedule check to see if handover succeeded. 2216 sendMessageDelayed(obtainMessage(EVENT_CHECK_FOR_WIFI_HANDOVER, imsCall), 2217 HANDOVER_TO_WIFI_TIMEOUT_MS); 2218 mHasAttemptedStartOfCallHandover = false; 2219 } else { 2220 // No wifi connectivity, so keep track of network availability for potential 2221 // handover. 2222 registerForConnectivityChanges(); 2223 // No WIFI, so assume we've already attempted a handover. 2224 mHasAttemptedStartOfCallHandover = true; 2225 } 2226 } 2227 mMetrics.writeOnImsCallStarted(mPhone.getPhoneId(), imsCall.getCallSession()); 2228 } 2229 2230 @Override 2231 public void onCallUpdated(ImsCall imsCall) { 2232 if (DBG) log("onCallUpdated"); 2233 if (imsCall == null) { 2234 return; 2235 } 2236 ImsPhoneConnection conn = findConnection(imsCall); 2237 if (conn != null) { 2238 if (DBG) log("onCallUpdated: profile is " + imsCall.getCallProfile()); 2239 processCallStateChange(imsCall, conn.getCall().mState, 2240 DisconnectCause.NOT_DISCONNECTED, true /*ignore state update*/); 2241 mMetrics.writeImsCallState(mPhone.getPhoneId(), 2242 imsCall.getCallSession(), conn.getCall().mState); 2243 } 2244 } 2245 2246 /** 2247 * onCallStartFailed will be invoked when: 2248 * case 1) Dialing fails 2249 * case 2) Ringing call is disconnected by local or remote user 2250 */ 2251 @Override 2252 public void onCallStartFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) { 2253 if (DBG) log("onCallStartFailed reasonCode=" + reasonInfo.getCode()); 2254 2255 if (mHoldSwitchingState == HoldSwapState.HOLDING_TO_ANSWER_INCOMING) { 2256 // If we put a call on hold to answer an incoming call, we should reset the 2257 // variables that keep track of the switch here. 2258 if (mCallExpectedToResume != null && mCallExpectedToResume == imsCall) { 2259 if (DBG) log("onCallStarted: starting a call as a result of a switch."); 2260 mHoldSwitchingState = HoldSwapState.INACTIVE; 2261 mCallExpectedToResume = null; 2262 logHoldSwapState("onCallStartFailed"); 2263 } 2264 } 2265 2266 if (mPendingMO != null) { 2267 // To initiate dialing circuit-switched call 2268 if (reasonInfo.getCode() == ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED 2269 && mBackgroundCall.getState() == ImsPhoneCall.State.IDLE 2270 && mRingingCall.getState() == ImsPhoneCall.State.IDLE) { 2271 mForegroundCall.detach(mPendingMO); 2272 removeConnection(mPendingMO); 2273 mPendingMO.finalize(); 2274 mPendingMO = null; 2275 mPhone.initiateSilentRedial(); 2276 return; 2277 } else { 2278 sendCallStartFailedDisconnect(imsCall, reasonInfo); 2279 } 2280 mMetrics.writeOnImsCallStartFailed(mPhone.getPhoneId(), imsCall.getCallSession(), 2281 reasonInfo); 2282 } 2283 } 2284 2285 @Override 2286 public void onCallTerminated(ImsCall imsCall, ImsReasonInfo reasonInfo) { 2287 if (DBG) log("onCallTerminated reasonCode=" + reasonInfo.getCode()); 2288 2289 ImsPhoneConnection conn = findConnection(imsCall); 2290 Call.State callState; 2291 if (conn != null) { 2292 callState = conn.getState(); 2293 } else { 2294 // Connection shouldn't be null, but if it is, we can assume the call was active. 2295 // This call state is only used for determining which disconnect message to show in 2296 // the case of the device's battery being low resulting in a call drop. 2297 callState = Call.State.ACTIVE; 2298 } 2299 int cause = getDisconnectCauseFromReasonInfo(reasonInfo, callState); 2300 2301 if (DBG) log("cause = " + cause + " conn = " + conn); 2302 2303 if (conn != null) { 2304 android.telecom.Connection.VideoProvider videoProvider = conn.getVideoProvider(); 2305 if (videoProvider instanceof ImsVideoCallProviderWrapper) { 2306 ImsVideoCallProviderWrapper wrapper = (ImsVideoCallProviderWrapper) 2307 videoProvider; 2308 wrapper.unregisterForDataUsageUpdate(ImsPhoneCallTracker.this); 2309 wrapper.removeImsVideoProviderCallback(conn); 2310 } 2311 } 2312 if (mOnHoldToneId == System.identityHashCode(conn)) { 2313 if (conn != null && mOnHoldToneStarted) { 2314 mPhone.stopOnHoldTone(conn); 2315 } 2316 mOnHoldToneStarted = false; 2317 mOnHoldToneId = -1; 2318 } 2319 if (conn != null) { 2320 if (conn.isPulledCall() && ( 2321 reasonInfo.getCode() == ImsReasonInfo.CODE_CALL_PULL_OUT_OF_SYNC || 2322 reasonInfo.getCode() == ImsReasonInfo.CODE_SIP_TEMPRARILY_UNAVAILABLE || 2323 reasonInfo.getCode() == ImsReasonInfo.CODE_SIP_FORBIDDEN) && 2324 mPhone != null && mPhone.getExternalCallTracker() != null) { 2325 2326 log("Call pull failed."); 2327 // Call was being pulled, but the call pull has failed -- inform the associated 2328 // TelephonyConnection that the pull failed, and provide it with the original 2329 // external connection which was pulled so that it can be swapped back. 2330 conn.onCallPullFailed(mPhone.getExternalCallTracker() 2331 .getConnectionById(conn.getPulledDialogId())); 2332 // Do not mark as disconnected; the call will just change from being a regular 2333 // call to being an external call again. 2334 cause = DisconnectCause.NOT_DISCONNECTED; 2335 2336 } else if (conn.isIncoming() && conn.getConnectTime() == 0 2337 && cause != DisconnectCause.ANSWERED_ELSEWHERE) { 2338 // Missed 2339 if (cause == DisconnectCause.NORMAL) { 2340 cause = DisconnectCause.INCOMING_MISSED; 2341 } else { 2342 cause = DisconnectCause.INCOMING_REJECTED; 2343 } 2344 if (DBG) log("Incoming connection of 0 connect time detected - translated " + 2345 "cause = " + cause); 2346 } 2347 } 2348 2349 if (cause == DisconnectCause.NORMAL && conn != null && conn.getImsCall().isMerged()) { 2350 // Call was terminated while it is merged instead of a remote disconnect. 2351 cause = DisconnectCause.IMS_MERGED_SUCCESSFULLY; 2352 } 2353 2354 String callId = imsCall.getSession().getCallId(); 2355 mMetrics.writeOnImsCallTerminated(mPhone.getPhoneId(), imsCall.getCallSession(), 2356 reasonInfo, mCallQualityMetrics.get(callId), conn.getEmergencyNumberInfo(), 2357 getNetworkCountryIso()); 2358 // Remove info for the callId from the current calls and add it to the history 2359 CallQualityMetrics lastCallMetrics = mCallQualityMetrics.remove(callId); 2360 if (lastCallMetrics != null) { 2361 mCallQualityMetricsHistory.add(lastCallMetrics); 2362 } 2363 pruneCallQualityMetricsHistory(); 2364 mPhone.notifyImsReason(reasonInfo); 2365 2366 if (reasonInfo.getCode() == ImsReasonInfo.CODE_SIP_ALTERNATE_EMERGENCY_CALL 2367 && mAutoRetryFailedWifiEmergencyCall) { 2368 Pair<ImsCall, ImsReasonInfo> callInfo = new Pair<>(imsCall, reasonInfo); 2369 mPhone.getDefaultPhone().getServiceStateTracker().registerForNetworkAttached( 2370 ImsPhoneCallTracker.this, EVENT_REDIAL_WIFI_E911_CALL, callInfo); 2371 sendMessageDelayed(obtainMessage(EVENT_REDIAL_WIFI_E911_TIMEOUT, callInfo), 2372 TIMEOUT_REDIAL_WIFI_E911_MS); 2373 final ConnectivityManager mgr = (ConnectivityManager) mPhone.getContext() 2374 .getSystemService(Context.CONNECTIVITY_SERVICE); 2375 mgr.setAirplaneMode(false); 2376 return; 2377 } else { 2378 processCallStateChange(imsCall, ImsPhoneCall.State.DISCONNECTED, cause); 2379 } 2380 2381 if (mForegroundCall.getState() != ImsPhoneCall.State.ACTIVE) { 2382 if (mRingingCall.getState().isRinging()) { 2383 // Drop pending MO. We should address incoming call first 2384 mPendingMO = null; 2385 } 2386 } 2387 2388 if (mHoldSwitchingState == HoldSwapState.SWAPPING_ACTIVE_AND_HELD) { 2389 if (DBG) { 2390 log("onCallTerminated: Call terminated in the midst of Switching " + 2391 "Fg and Bg calls."); 2392 } 2393 // If we are the in midst of swapping FG and BG calls and the call that was 2394 // terminated was the one that we expected to resume, we need to swap the FG and 2395 // BG calls back. 2396 if (imsCall == mCallExpectedToResume) { 2397 if (DBG) { 2398 log("onCallTerminated: switching " + mForegroundCall + " with " 2399 + mBackgroundCall); 2400 } 2401 mForegroundCall.switchWith(mBackgroundCall); 2402 } 2403 // This call terminated in the midst of a switch after the other call was held, so 2404 // resume it back to ACTIVE state since the switch failed. 2405 log("onCallTerminated: foreground call in state " + mForegroundCall.getState() + 2406 " and ringing call in state " + (mRingingCall == null ? "null" : 2407 mRingingCall.getState().toString())); 2408 2409 sendEmptyMessage(EVENT_RESUME_NOW_FOREGROUND_CALL); 2410 mHoldSwitchingState = HoldSwapState.INACTIVE; 2411 mCallExpectedToResume = null; 2412 logHoldSwapState("onCallTerminated swap active and hold case"); 2413 } else if (mHoldSwitchingState == HoldSwapState.PENDING_SINGLE_CALL_UNHOLD 2414 || mHoldSwitchingState == HoldSwapState.PENDING_SINGLE_CALL_HOLD) { 2415 mCallExpectedToResume = null; 2416 mHoldSwitchingState = HoldSwapState.INACTIVE; 2417 logHoldSwapState("onCallTerminated single call case"); 2418 } else if (mHoldSwitchingState == HoldSwapState.HOLDING_TO_ANSWER_INCOMING) { 2419 // Check to see which call got terminated. If it's the one that was gonna get held, 2420 // ignore it. If it's the one that was gonna get answered, restore the one that 2421 // possibly got held. 2422 if (imsCall == mCallExpectedToResume) { 2423 mForegroundCall.switchWith(mBackgroundCall); 2424 mCallExpectedToResume = null; 2425 mHoldSwitchingState = HoldSwapState.INACTIVE; 2426 logHoldSwapState("onCallTerminated hold to answer case"); 2427 sendEmptyMessage(EVENT_RESUME_NOW_FOREGROUND_CALL); 2428 } 2429 } else if (mHoldSwitchingState == HoldSwapState.HOLDING_TO_DIAL_OUTGOING) { 2430 // The call that we were gonna hold might've gotten terminated. If that's the case, 2431 // dial mPendingMo if present. 2432 if (mPendingMO == null 2433 || mPendingMO.getDisconnectCause() != DisconnectCause.NOT_DISCONNECTED) { 2434 mHoldSwitchingState = HoldSwapState.INACTIVE; 2435 logHoldSwapState("onCallTerminated hold to dial but no pendingMo"); 2436 } else if (imsCall != mPendingMO.getImsCall()) { 2437 sendEmptyMessage(EVENT_DIAL_PENDINGMO); 2438 mHoldSwitchingState = HoldSwapState.INACTIVE; 2439 logHoldSwapState("onCallTerminated hold to dial, dial pendingMo"); 2440 } 2441 } 2442 2443 if (mShouldUpdateImsConfigOnDisconnect) { 2444 // Ensure we update the IMS config when the call is disconnected; we delayed this 2445 // because a video call was paused. 2446 if (mImsManager != null) { 2447 mImsManager.updateImsServiceConfig(true); 2448 } 2449 mShouldUpdateImsConfigOnDisconnect = false; 2450 } 2451 } 2452 2453 @Override 2454 public void onCallHeld(ImsCall imsCall) { 2455 if (DBG) { 2456 if (mForegroundCall.getImsCall() == imsCall) { 2457 log("onCallHeld (fg) " + imsCall); 2458 } else if (mBackgroundCall.getImsCall() == imsCall) { 2459 log("onCallHeld (bg) " + imsCall); 2460 } 2461 } 2462 2463 synchronized (mSyncHold) { 2464 ImsPhoneCall.State oldState = mBackgroundCall.getState(); 2465 processCallStateChange(imsCall, ImsPhoneCall.State.HOLDING, 2466 DisconnectCause.NOT_DISCONNECTED); 2467 2468 // Note: If we're performing a switchWaitingOrHoldingAndActive, the call to 2469 // processCallStateChange above may have caused the mBackgroundCall and 2470 // mForegroundCall references below to change meaning. Watch out for this if you 2471 // are reading through this code. 2472 if (oldState == ImsPhoneCall.State.ACTIVE) { 2473 // Note: This case comes up when we have just held a call in response to a 2474 // switchWaitingOrHoldingAndActive. We now need to resume the background call. 2475 if (mForegroundCall.getState() == ImsPhoneCall.State.HOLDING 2476 && mHoldSwitchingState == HoldSwapState.SWAPPING_ACTIVE_AND_HELD) { 2477 sendEmptyMessage(EVENT_RESUME_NOW_FOREGROUND_CALL); 2478 } else if (mRingingCall.getState() == ImsPhoneCall.State.WAITING 2479 && mHoldSwitchingState == HoldSwapState.HOLDING_TO_ANSWER_INCOMING) { 2480 sendEmptyMessage(EVENT_ANSWER_WAITING_CALL); 2481 } else if (mPendingMO != null 2482 && mHoldSwitchingState == HoldSwapState.HOLDING_TO_DIAL_OUTGOING) { 2483 dialPendingMO(); 2484 mHoldSwitchingState = HoldSwapState.INACTIVE; 2485 logHoldSwapState("onCallHeld hold to dial"); 2486 } else { 2487 // In this case there will be no call resumed, so we can assume that we 2488 // are done switching fg and bg calls now. 2489 // This may happen if there is no BG call and we are holding a call so that 2490 // we can dial another one. 2491 mHoldSwitchingState = HoldSwapState.INACTIVE; 2492 logHoldSwapState("onCallHeld normal case"); 2493 } 2494 } else if (oldState == ImsPhoneCall.State.IDLE 2495 && (mHoldSwitchingState == HoldSwapState.SWAPPING_ACTIVE_AND_HELD 2496 || mHoldSwitchingState 2497 == HoldSwapState.HOLDING_TO_ANSWER_INCOMING)) { 2498 // The other call terminated in the midst of a switch before this call was held, 2499 // so resume the foreground call back to ACTIVE state since the switch failed. 2500 if (mForegroundCall.getState() == ImsPhoneCall.State.HOLDING) { 2501 sendEmptyMessage(EVENT_RESUME_NOW_FOREGROUND_CALL); 2502 mHoldSwitchingState = HoldSwapState.INACTIVE; 2503 mCallExpectedToResume = null; 2504 logHoldSwapState("onCallHeld premature termination of other call"); 2505 } 2506 } 2507 } 2508 mMetrics.writeOnImsCallHeld(mPhone.getPhoneId(), imsCall.getCallSession()); 2509 } 2510 2511 @Override 2512 public void onCallHoldFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) { 2513 if (DBG) log("onCallHoldFailed reasonCode=" + reasonInfo.getCode()); 2514 2515 synchronized (mSyncHold) { 2516 ImsPhoneCall.State bgState = mBackgroundCall.getState(); 2517 if (reasonInfo.getCode() == ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED) { 2518 // disconnected while processing hold 2519 if (mPendingMO != null) { 2520 dialPendingMO(); 2521 } else if (mRingingCall.getState() == ImsPhoneCall.State.WAITING 2522 && mHoldSwitchingState == HoldSwapState.HOLDING_TO_ANSWER_INCOMING) { 2523 sendEmptyMessage(EVENT_ANSWER_WAITING_CALL); 2524 } 2525 mHoldSwitchingState = HoldSwapState.INACTIVE; 2526 } else if (mPendingMO != null && mPendingMO.isEmergency()) { 2527 // If mPendingMO is an emergency call, disconnect the call that we tried to 2528 // hold. 2529 mBackgroundCall.getImsCall().terminate(ImsReasonInfo.CODE_UNSPECIFIED); 2530 if (imsCall != mCallExpectedToResume) { 2531 mCallExpectedToResume = null; 2532 } 2533 // Leave mHoldSwitchingState as is for now -- we'll reset it 2534 // in onCallTerminated, which will also dial the outgoing emergency call. 2535 } else if (mRingingCall.getState() == ImsPhoneCall.State.WAITING 2536 && mHoldSwitchingState == HoldSwapState.HOLDING_TO_ANSWER_INCOMING) { 2537 // If we issued a hold request in order to answer an incoming call, we need 2538 // to tell Telecom that we can't actually answer the incoming call. 2539 mHoldSwitchingState = HoldSwapState.INACTIVE; 2540 mForegroundCall.switchWith(mBackgroundCall); 2541 logHoldSwapState("onCallHoldFailed unable to answer waiting call"); 2542 } else if (bgState == ImsPhoneCall.State.ACTIVE) { 2543 mForegroundCall.switchWith(mBackgroundCall); 2544 2545 if (mPendingMO != null) { 2546 mPendingMO.setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED); 2547 sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO); 2548 } 2549 if (imsCall != mCallExpectedToResume) { 2550 mCallExpectedToResume = null; 2551 } 2552 mHoldSwitchingState = HoldSwapState.INACTIVE; 2553 } 2554 ImsPhoneConnection conn = findConnection(imsCall); 2555 if (conn != null) { 2556 conn.onConnectionEvent(android.telecom.Connection.EVENT_CALL_HOLD_FAILED, null); 2557 } 2558 mPhone.notifySuppServiceFailed(Phone.SuppService.HOLD); 2559 } 2560 mMetrics.writeOnImsCallHoldFailed(mPhone.getPhoneId(), imsCall.getCallSession(), 2561 reasonInfo); 2562 } 2563 2564 @Override 2565 public void onCallResumed(ImsCall imsCall) { 2566 if (DBG) log("onCallResumed"); 2567 2568 // If we are the in midst of swapping FG and BG calls and the call we end up resuming 2569 // is not the one we expected, we likely had a resume failure and we need to swap the 2570 // FG and BG calls back. 2571 if (mHoldSwitchingState == HoldSwapState.SWAPPING_ACTIVE_AND_HELD 2572 || mHoldSwitchingState == HoldSwapState.PENDING_RESUME_FOREGROUND_AFTER_FAILURE 2573 || mHoldSwitchingState == HoldSwapState.PENDING_SINGLE_CALL_UNHOLD) { 2574 if (imsCall != mCallExpectedToResume) { 2575 // If the call which resumed isn't as expected, we need to swap back to the 2576 // previous configuration; the swap has failed. 2577 if (DBG) { 2578 log("onCallResumed : switching " + mForegroundCall + " with " 2579 + mBackgroundCall); 2580 } 2581 mForegroundCall.switchWith(mBackgroundCall); 2582 } else { 2583 // The call which resumed is the one we expected to resume, so we can clear out 2584 // the mSwitchingFgAndBgCalls flag. 2585 if (DBG) { 2586 log("onCallResumed : expected call resumed."); 2587 } 2588 } 2589 mHoldSwitchingState = HoldSwapState.INACTIVE; 2590 mCallExpectedToResume = null; 2591 logHoldSwapState("onCallResumed"); 2592 } 2593 processCallStateChange(imsCall, ImsPhoneCall.State.ACTIVE, 2594 DisconnectCause.NOT_DISCONNECTED); 2595 mMetrics.writeOnImsCallResumed(mPhone.getPhoneId(), imsCall.getCallSession()); 2596 } 2597 2598 @Override 2599 public void onCallResumeFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) { 2600 if (mHoldSwitchingState == HoldSwapState.SWAPPING_ACTIVE_AND_HELD 2601 || mHoldSwitchingState 2602 == HoldSwapState.PENDING_RESUME_FOREGROUND_AFTER_FAILURE) { 2603 // If we are in the midst of swapping the FG and BG calls and 2604 // we got a resume fail, we need to swap back the FG and BG calls. 2605 // Since the FG call was held, will also try to resume the same. 2606 if (imsCall == mCallExpectedToResume) { 2607 if (DBG) { 2608 log("onCallResumeFailed : switching " + mForegroundCall + " with " 2609 + mBackgroundCall); 2610 } 2611 mForegroundCall.switchWith(mBackgroundCall); 2612 if (mForegroundCall.getState() == ImsPhoneCall.State.HOLDING) { 2613 sendEmptyMessage(EVENT_RESUME_NOW_FOREGROUND_CALL); 2614 } 2615 } 2616 2617 //Call swap is done, reset the relevant variables 2618 mCallExpectedToResume = null; 2619 mHoldSwitchingState = HoldSwapState.INACTIVE; 2620 logHoldSwapState("onCallResumeFailed: multi calls"); 2621 } else if (mHoldSwitchingState == HoldSwapState.PENDING_SINGLE_CALL_UNHOLD) { 2622 if (imsCall == mCallExpectedToResume) { 2623 if (DBG) { 2624 log("onCallResumeFailed: single call unhold case"); 2625 } 2626 mForegroundCall.switchWith(mBackgroundCall); 2627 2628 mCallExpectedToResume = null; 2629 mHoldSwitchingState = HoldSwapState.INACTIVE; 2630 logHoldSwapState("onCallResumeFailed: single call"); 2631 } else { 2632 Rlog.w(LOG_TAG, "onCallResumeFailed: got a resume failed for a different call" 2633 + " in the single call unhold case"); 2634 } 2635 } 2636 mPhone.notifySuppServiceFailed(Phone.SuppService.RESUME); 2637 mMetrics.writeOnImsCallResumeFailed(mPhone.getPhoneId(), imsCall.getCallSession(), 2638 reasonInfo); 2639 } 2640 2641 @Override 2642 public void onCallResumeReceived(ImsCall imsCall) { 2643 if (DBG) log("onCallResumeReceived"); 2644 ImsPhoneConnection conn = findConnection(imsCall); 2645 if (conn != null) { 2646 if (mOnHoldToneStarted) { 2647 mPhone.stopOnHoldTone(conn); 2648 mOnHoldToneStarted = false; 2649 } 2650 conn.onConnectionEvent(android.telecom.Connection.EVENT_CALL_REMOTELY_UNHELD, null); 2651 } 2652 2653 boolean useVideoPauseWorkaround = mPhone.getContext().getResources().getBoolean( 2654 com.android.internal.R.bool.config_useVideoPauseWorkaround); 2655 if (useVideoPauseWorkaround && mSupportPauseVideo && 2656 VideoProfile.isVideo(conn.getVideoState())) { 2657 // If we are using the video pause workaround, the vendor IMS code has issues 2658 // with video pause signalling. In this case, when a call is remotely 2659 // held, the modem does not reliably change the video state of the call to be 2660 // paused. 2661 // As a workaround, we will turn on that bit now. 2662 conn.changeToUnPausedState(); 2663 } 2664 2665 SuppServiceNotification supp = new SuppServiceNotification(); 2666 // Type of notification: 0 = MO; 1 = MT 2667 // Refer SuppServiceNotification class documentation. 2668 supp.notificationType = 1; 2669 supp.code = SuppServiceNotification.CODE_2_CALL_RETRIEVED; 2670 mPhone.notifySuppSvcNotification(supp); 2671 mMetrics.writeOnImsCallResumeReceived(mPhone.getPhoneId(), imsCall.getCallSession()); 2672 } 2673 2674 @Override 2675 public void onCallHoldReceived(ImsCall imsCall) { 2676 ImsPhoneCallTracker.this.onCallHoldReceived(imsCall); 2677 } 2678 2679 @Override 2680 public void onCallSuppServiceReceived(ImsCall call, 2681 ImsSuppServiceNotification suppServiceInfo) { 2682 if (DBG) log("onCallSuppServiceReceived: suppServiceInfo=" + suppServiceInfo); 2683 2684 SuppServiceNotification supp = new SuppServiceNotification(); 2685 supp.notificationType = suppServiceInfo.notificationType; 2686 supp.code = suppServiceInfo.code; 2687 supp.index = suppServiceInfo.index; 2688 supp.number = suppServiceInfo.number; 2689 supp.history = suppServiceInfo.history; 2690 2691 mPhone.notifySuppSvcNotification(supp); 2692 } 2693 2694 @Override 2695 public void onCallMerged(final ImsCall call, final ImsCall peerCall, boolean swapCalls) { 2696 if (DBG) log("onCallMerged"); 2697 2698 ImsPhoneCall foregroundImsPhoneCall = findConnection(call).getCall(); 2699 ImsPhoneConnection peerConnection = findConnection(peerCall); 2700 ImsPhoneCall peerImsPhoneCall = peerConnection == null ? null 2701 : peerConnection.getCall(); 2702 2703 if (swapCalls) { 2704 switchAfterConferenceSuccess(); 2705 } 2706 foregroundImsPhoneCall.merge(peerImsPhoneCall, ImsPhoneCall.State.ACTIVE); 2707 2708 try { 2709 final ImsPhoneConnection conn = findConnection(call); 2710 log("onCallMerged: ImsPhoneConnection=" + conn); 2711 log("onCallMerged: CurrentVideoProvider=" + conn.getVideoProvider()); 2712 setVideoCallProvider(conn, call); 2713 log("onCallMerged: CurrentVideoProvider=" + conn.getVideoProvider()); 2714 } catch (Exception e) { 2715 loge("onCallMerged: exception " + e); 2716 } 2717 2718 // After merge complete, update foreground as Active 2719 // and background call as Held, if background call exists 2720 processCallStateChange(mForegroundCall.getImsCall(), ImsPhoneCall.State.ACTIVE, 2721 DisconnectCause.NOT_DISCONNECTED); 2722 if (peerConnection != null) { 2723 processCallStateChange(mBackgroundCall.getImsCall(), ImsPhoneCall.State.HOLDING, 2724 DisconnectCause.NOT_DISCONNECTED); 2725 } 2726 2727 // Check if the merge was requested by an existing conference call. In that 2728 // case, no further action is required. 2729 if (!call.isMergeRequestedByConf()) { 2730 log("onCallMerged :: calling onMultipartyStateChanged()"); 2731 onMultipartyStateChanged(call, true); 2732 } else { 2733 log("onCallMerged :: Merge requested by existing conference."); 2734 // Reset the flag. 2735 call.resetIsMergeRequestedByConf(false); 2736 } 2737 logState(); 2738 } 2739 2740 @Override 2741 public void onCallMergeFailed(ImsCall call, ImsReasonInfo reasonInfo) { 2742 if (DBG) log("onCallMergeFailed reasonInfo=" + reasonInfo); 2743 2744 // TODO: the call to notifySuppServiceFailed throws up the "merge failed" dialog 2745 // We should move this into the InCallService so that it is handled appropriately 2746 // based on the user facing UI. 2747 mPhone.notifySuppServiceFailed(Phone.SuppService.CONFERENCE); 2748 2749 call.resetIsMergeRequestedByConf(false); 2750 2751 // Start plumbing this even through Telecom so other components can take 2752 // appropriate action. 2753 ImsPhoneConnection conn = findConnection(call); 2754 if (conn != null) { 2755 conn.onConferenceMergeFailed(); 2756 conn.handleMergeComplete(); 2757 } 2758 } 2759 2760 private void updateConferenceParticipantsTiming(List<ConferenceParticipant> participants) { 2761 for (ConferenceParticipant participant : participants) { 2762 // Every time participants are newly created from parcel, update their connect time. 2763 CacheEntry cachedConnectTime = findConnectionTimeUsePhoneNumber(participant); 2764 if (cachedConnectTime != null) { 2765 participant.setConnectTime(cachedConnectTime.mConnectTime); 2766 participant.setConnectElapsedTime(cachedConnectTime.mConnectElapsedTime); 2767 participant.setCallDirection(cachedConnectTime.mCallDirection); 2768 } 2769 } 2770 } 2771 2772 /** 2773 * Called when the state of IMS conference participant(s) has changed. 2774 * 2775 * @param call the call object that carries out the IMS call. 2776 * @param participants the participant(s) and their new state information. 2777 */ 2778 @Override 2779 public void onConferenceParticipantsStateChanged(ImsCall call, 2780 List<ConferenceParticipant> participants) { 2781 if (DBG) log("onConferenceParticipantsStateChanged"); 2782 2783 ImsPhoneConnection conn = findConnection(call); 2784 if (conn != null) { 2785 updateConferenceParticipantsTiming(participants); 2786 conn.updateConferenceParticipants(participants); 2787 } 2788 } 2789 2790 @Override 2791 public void onCallSessionTtyModeReceived(ImsCall call, int mode) { 2792 mPhone.onTtyModeReceived(mode); 2793 } 2794 2795 @Override 2796 public void onCallHandover(ImsCall imsCall, int srcAccessTech, int targetAccessTech, 2797 ImsReasonInfo reasonInfo) { 2798 // Check with the DCTracker to see if data is enabled; there may be a case when 2799 // ImsPhoneCallTracker isn't being informed of the right data enabled state via its 2800 // registration, so we'll refresh now. 2801 boolean isDataEnabled = mPhone.getDefaultPhone().getDataEnabledSettings() 2802 .isDataEnabled(); 2803 2804 if (DBG) { 2805 log("onCallHandover :: srcAccessTech=" + srcAccessTech + ", targetAccessTech=" 2806 + targetAccessTech + ", reasonInfo=" + reasonInfo + ", dataEnabled=" 2807 + mIsDataEnabled + "/" + isDataEnabled + ", dataMetered=" 2808 + mIsViLteDataMetered); 2809 } 2810 if (mIsDataEnabled != isDataEnabled) { 2811 loge("onCallHandover: data enabled state doesn't match! (was=" + mIsDataEnabled 2812 + ", actually=" + isDataEnabled); 2813 mIsDataEnabled = isDataEnabled; 2814 } 2815 2816 // Only consider it a valid handover to WIFI if the source radio tech is known. 2817 boolean isHandoverToWifi = srcAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN 2818 && srcAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN 2819 && targetAccessTech == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN; 2820 // Only consider it a handover from WIFI if the source and target radio tech is known. 2821 boolean isHandoverFromWifi = 2822 srcAccessTech == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN 2823 && targetAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN 2824 && targetAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN; 2825 2826 ImsPhoneConnection conn = findConnection(imsCall); 2827 if (conn != null) { 2828 ImsPhoneCall imsPhoneCall = conn.getCall(); 2829 if (imsPhoneCall != null) { 2830 // We might be playing ringback on the handover connection; we should stop 2831 // playing it at this point (otherwise it could play indefinitely). 2832 imsPhoneCall.maybeStopRingback(); 2833 } 2834 if (conn.getDisconnectCause() == DisconnectCause.NOT_DISCONNECTED) { 2835 if (isHandoverToWifi) { 2836 removeMessages(EVENT_CHECK_FOR_WIFI_HANDOVER); 2837 2838 if (mNotifyHandoverVideoFromLTEToWifi && mHasAttemptedStartOfCallHandover) { 2839 // This is a handover which happened mid-call (ie not the start of call 2840 // handover from LTE to WIFI), so we'll notify the InCall UI. 2841 conn.onConnectionEvent( 2842 TelephonyManager.EVENT_HANDOVER_VIDEO_FROM_LTE_TO_WIFI, null); 2843 } 2844 2845 // We are on WIFI now so no need to get notified of network availability. 2846 unregisterForConnectivityChanges(); 2847 } else if (isHandoverFromWifi && imsCall.isVideoCall()) { 2848 // A video call just dropped from WIFI to LTE; we want to be informed if a 2849 // new WIFI 2850 // network comes into range. 2851 registerForConnectivityChanges(); 2852 } 2853 } 2854 2855 if (isHandoverToWifi && mIsViLteDataMetered) { 2856 conn.setLocalVideoCapable(true); 2857 } 2858 2859 if (isHandoverFromWifi && imsCall.isVideoCall()) { 2860 if (mIsViLteDataMetered) { 2861 conn.setLocalVideoCapable(mIsDataEnabled); 2862 } 2863 2864 if (mNotifyHandoverVideoFromWifiToLTE && mIsDataEnabled) { 2865 if (conn.getDisconnectCause() == DisconnectCause.NOT_DISCONNECTED) { 2866 log("onCallHandover :: notifying of WIFI to LTE handover."); 2867 conn.onConnectionEvent( 2868 TelephonyManager.EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE, null); 2869 } else { 2870 // Call has already had a disconnect request issued by the user or is 2871 // in the process of disconnecting; do not inform the UI of this as it 2872 // is not relevant. 2873 log("onCallHandover :: skip notify of WIFI to LTE handover for " 2874 + "disconnected call."); 2875 } 2876 } 2877 2878 if (!mIsDataEnabled && mIsViLteDataMetered) { 2879 // Call was downgraded from WIFI to LTE and data is metered; downgrade the 2880 // call now. 2881 log("onCallHandover :: data is not enabled; attempt to downgrade."); 2882 downgradeVideoCall(ImsReasonInfo.CODE_WIFI_LOST, conn); 2883 } 2884 } 2885 } else { 2886 loge("onCallHandover :: connection null."); 2887 } 2888 // If there's a handover, then we're not in the "start of call" handover phase. 2889 if (!mHasAttemptedStartOfCallHandover) { 2890 mHasAttemptedStartOfCallHandover = true; 2891 } 2892 mMetrics.writeOnImsCallHandoverEvent(mPhone.getPhoneId(), 2893 TelephonyCallSession.Event.Type.IMS_CALL_HANDOVER, imsCall.getCallSession(), 2894 srcAccessTech, targetAccessTech, reasonInfo); 2895 } 2896 2897 @Override 2898 public void onCallHandoverFailed(ImsCall imsCall, int srcAccessTech, int targetAccessTech, 2899 ImsReasonInfo reasonInfo) { 2900 if (DBG) { 2901 log("onCallHandoverFailed :: srcAccessTech=" + srcAccessTech + 2902 ", targetAccessTech=" + targetAccessTech + ", reasonInfo=" + reasonInfo); 2903 } 2904 mMetrics.writeOnImsCallHandoverEvent(mPhone.getPhoneId(), 2905 TelephonyCallSession.Event.Type.IMS_CALL_HANDOVER_FAILED, 2906 imsCall.getCallSession(), srcAccessTech, targetAccessTech, reasonInfo); 2907 2908 boolean isHandoverToWifi = srcAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN && 2909 targetAccessTech == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN; 2910 ImsPhoneConnection conn = findConnection(imsCall); 2911 if (conn != null && isHandoverToWifi) { 2912 log("onCallHandoverFailed - handover to WIFI Failed"); 2913 2914 // If we know we failed to handover, don't check for failure in the future. 2915 removeMessages(EVENT_CHECK_FOR_WIFI_HANDOVER); 2916 2917 if (imsCall.isVideoCall() 2918 && conn.getDisconnectCause() == DisconnectCause.NOT_DISCONNECTED) { 2919 // Start listening for a WIFI network to come into range for potential handover. 2920 registerForConnectivityChanges(); 2921 } 2922 2923 if (mNotifyVtHandoverToWifiFail) { 2924 // Only notify others if carrier config indicates to do so. 2925 conn.onHandoverToWifiFailed(); 2926 } 2927 } 2928 if (!mHasAttemptedStartOfCallHandover) { 2929 mHasAttemptedStartOfCallHandover = true; 2930 } 2931 } 2932 2933 @Override 2934 public void onRttModifyRequestReceived(ImsCall imsCall) { 2935 ImsPhoneConnection conn = findConnection(imsCall); 2936 if (conn != null) { 2937 conn.onRttModifyRequestReceived(); 2938 } 2939 } 2940 2941 @Override 2942 public void onRttModifyResponseReceived(ImsCall imsCall, int status) { 2943 ImsPhoneConnection conn = findConnection(imsCall); 2944 if (conn != null) { 2945 conn.onRttModifyResponseReceived(status); 2946 } 2947 } 2948 2949 @Override 2950 public void onRttMessageReceived(ImsCall imsCall, String message) { 2951 ImsPhoneConnection conn = findConnection(imsCall); 2952 if (conn != null) { 2953 conn.onRttMessageReceived(message); 2954 } 2955 } 2956 2957 @Override 2958 public void onRttAudioIndicatorChanged(ImsCall imsCall, ImsStreamMediaProfile profile) { 2959 ImsPhoneConnection conn = findConnection(imsCall); 2960 if (conn != null) { 2961 conn.onRttAudioIndicatorChanged(profile); 2962 } 2963 } 2964 2965 /** 2966 * Handles a change to the multiparty state for an {@code ImsCall}. Notifies the associated 2967 * {@link ImsPhoneConnection} of the change. 2968 * 2969 * @param imsCall The IMS call. 2970 * @param isMultiParty {@code true} if the call became multiparty, {@code false} 2971 * otherwise. 2972 */ 2973 @Override 2974 public void onMultipartyStateChanged(ImsCall imsCall, boolean isMultiParty) { 2975 if (DBG) log("onMultipartyStateChanged to " + (isMultiParty ? "Y" : "N")); 2976 2977 ImsPhoneConnection conn = findConnection(imsCall); 2978 if (conn != null) { 2979 conn.updateMultipartyState(isMultiParty); 2980 } 2981 } 2982 2983 /** 2984 * Handles a change to the call quality for an {@code ImsCall}. 2985 * Notifies apps through the System API {@link PhoneStateListener#onCallAttributesChanged}. 2986 */ 2987 @Override 2988 public void onCallQualityChanged(ImsCall imsCall, CallQuality callQuality) { 2989 // convert ServiceState.radioTech to TelephonyManager.NetworkType constant 2990 mPhone.onCallQualityChanged(callQuality, 2991 ServiceState.rilRadioTechnologyToNetworkType(imsCall.getRadioTechnology())); 2992 String callId = imsCall.getSession().getCallId(); 2993 CallQualityMetrics cqm = mCallQualityMetrics.get(callId); 2994 if (cqm == null) { 2995 cqm = new CallQualityMetrics(mPhone); 2996 } 2997 cqm.saveCallQuality(callQuality); 2998 mCallQualityMetrics.put(callId, cqm); 2999 } 3000 }; 3001 3002 /** 3003 * Listen to the IMS call state change 3004 */ 3005 private ImsCall.Listener mImsUssdListener = new ImsCall.Listener() { 3006 @Override 3007 public void onCallStarted(ImsCall imsCall) { 3008 if (DBG) log("mImsUssdListener onCallStarted"); 3009 3010 if (imsCall == mUssdSession) { 3011 if (mPendingUssd != null) { 3012 AsyncResult.forMessage(mPendingUssd); 3013 mPendingUssd.sendToTarget(); 3014 mPendingUssd = null; 3015 } 3016 } 3017 } 3018 3019 @Override 3020 public void onCallStartFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) { 3021 if (DBG) log("mImsUssdListener onCallStartFailed reasonCode=" + reasonInfo.getCode()); 3022 3023 onCallTerminated(imsCall, reasonInfo); 3024 } 3025 3026 @Override 3027 public void onCallTerminated(ImsCall imsCall, ImsReasonInfo reasonInfo) { 3028 if (DBG) log("mImsUssdListener onCallTerminated reasonCode=" + reasonInfo.getCode()); 3029 removeMessages(EVENT_CHECK_FOR_WIFI_HANDOVER); 3030 mHasAttemptedStartOfCallHandover = false; 3031 unregisterForConnectivityChanges(); 3032 3033 if (imsCall == mUssdSession) { 3034 mUssdSession = null; 3035 if (mPendingUssd != null) { 3036 CommandException ex = 3037 new CommandException(CommandException.Error.GENERIC_FAILURE); 3038 AsyncResult.forMessage(mPendingUssd, null, ex); 3039 mPendingUssd.sendToTarget(); 3040 mPendingUssd = null; 3041 } 3042 } 3043 imsCall.close(); 3044 } 3045 3046 @Override 3047 public void onCallUssdMessageReceived(ImsCall call, 3048 int mode, String ussdMessage) { 3049 if (DBG) log("mImsUssdListener onCallUssdMessageReceived mode=" + mode); 3050 3051 int ussdMode = -1; 3052 3053 switch(mode) { 3054 case ImsCall.USSD_MODE_REQUEST: 3055 ussdMode = CommandsInterface.USSD_MODE_REQUEST; 3056 break; 3057 3058 case ImsCall.USSD_MODE_NOTIFY: 3059 ussdMode = CommandsInterface.USSD_MODE_NOTIFY; 3060 break; 3061 } 3062 3063 mPhone.onIncomingUSSD(ussdMode, ussdMessage); 3064 } 3065 }; 3066 3067 private final ImsMmTelManager.RegistrationCallback mImsRegistrationCallback = 3068 new ImsMmTelManager.RegistrationCallback() { 3069 3070 @Override 3071 public void onRegistered( 3072 @ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech) { 3073 if (DBG) log("onImsConnected imsRadioTech=" + imsRadioTech); 3074 mPhone.setServiceState(ServiceState.STATE_IN_SERVICE); 3075 mPhone.setImsRegistered(true); 3076 mMetrics.writeOnImsConnectionState(mPhone.getPhoneId(), 3077 ImsConnectionState.State.CONNECTED, null); 3078 } 3079 3080 @Override 3081 public void onRegistering( 3082 @ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech) { 3083 if (DBG) log("onImsProgressing imsRadioTech=" + imsRadioTech); 3084 mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE); 3085 mPhone.setImsRegistered(false); 3086 mMetrics.writeOnImsConnectionState(mPhone.getPhoneId(), 3087 ImsConnectionState.State.PROGRESSING, null); 3088 } 3089 3090 @Override 3091 public void onUnregistered(ImsReasonInfo imsReasonInfo) { 3092 if (DBG) log("onImsDisconnected imsReasonInfo=" + imsReasonInfo); 3093 mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE); 3094 mPhone.setImsRegistered(false); 3095 mPhone.processDisconnectReason(imsReasonInfo); 3096 mMetrics.writeOnImsConnectionState(mPhone.getPhoneId(), 3097 ImsConnectionState.State.DISCONNECTED, imsReasonInfo); 3098 } 3099 3100 @Override 3101 public void onSubscriberAssociatedUriChanged(Uri[] uris) { 3102 if (DBG) log("registrationAssociatedUriChanged"); 3103 mPhone.setCurrentSubscriberUris(uris); 3104 } 3105 }; 3106 3107 private final ImsMmTelManager.CapabilityCallback mImsCapabilityCallback = 3108 new ImsMmTelManager.CapabilityCallback() { 3109 @Override 3110 public void onCapabilitiesStatusChanged( 3111 MmTelFeature.MmTelCapabilities capabilities) { 3112 if (DBG) log("onCapabilitiesStatusChanged: " + capabilities); 3113 SomeArgs args = SomeArgs.obtain(); 3114 args.arg1 = capabilities; 3115 // Remove any pending updates; they're already stale, so no need to process 3116 // them. 3117 removeMessages(EVENT_ON_FEATURE_CAPABILITY_CHANGED); 3118 obtainMessage(EVENT_ON_FEATURE_CAPABILITY_CHANGED, args).sendToTarget(); 3119 } 3120 }; 3121 3122 private ImsConfigListener.Stub mImsConfigListener = new ImsConfigListener.Stub() { 3123 @Override 3124 public void onGetFeatureResponse(int feature, int network, int value, int status) {} 3125 3126 @Override 3127 public void onSetFeatureResponse(int feature, int network, int value, int status) { 3128 mMetrics.writeImsSetFeatureValue(mPhone.getPhoneId(), feature, network, value); 3129 } 3130 3131 @Override 3132 public void onGetVideoQuality(int status, int quality) {} 3133 3134 @Override 3135 public void onSetVideoQuality(int status) {} 3136 3137 }; 3138 3139 private final ProvisioningManager.Callback mConfigCallback = 3140 new ProvisioningManager.Callback() { 3141 @Override 3142 public void onProvisioningIntChanged(int item, int value) { 3143 sendConfigChangedIntent(item, Integer.toString(value)); 3144 } 3145 3146 @Override 3147 public void onProvisioningStringChanged(int item, String value) { 3148 sendConfigChangedIntent(item, value); 3149 } 3150 3151 // send IMS_CONFIG_CHANGED intent for older services that do not implement the new callback 3152 // interface. 3153 private void sendConfigChangedIntent(int item, String value) { 3154 log("sendConfigChangedIntent - [" + item + ", " + value + "]"); 3155 Intent configChangedIntent = new Intent(ImsConfig.ACTION_IMS_CONFIG_CHANGED); 3156 configChangedIntent.putExtra(ImsConfig.EXTRA_CHANGED_ITEM, item); 3157 configChangedIntent.putExtra(ImsConfig.EXTRA_NEW_VALUE, value); 3158 if (mPhone != null && mPhone.getContext() != null) { 3159 mPhone.getContext().sendBroadcast(configChangedIntent); 3160 } 3161 } 3162 }; 3163 sendCallStartFailedDisconnect(ImsCall imsCall, ImsReasonInfo reasonInfo)3164 public void sendCallStartFailedDisconnect(ImsCall imsCall, ImsReasonInfo reasonInfo) { 3165 mPendingMO = null; 3166 ImsPhoneConnection conn = findConnection(imsCall); 3167 Call.State callState; 3168 if (conn != null) { 3169 callState = conn.getState(); 3170 } else { 3171 // Need to fall back in case connection is null; it shouldn't be, but a sane 3172 // fallback is to assume we're dialing. This state is only used to 3173 // determine which disconnect string to show in the case of a low battery 3174 // disconnect. 3175 callState = Call.State.DIALING; 3176 } 3177 int cause = getDisconnectCauseFromReasonInfo(reasonInfo, callState); 3178 3179 processCallStateChange(imsCall, ImsPhoneCall.State.DISCONNECTED, cause); 3180 mPhone.notifyImsReason(reasonInfo); 3181 } 3182 3183 @UnsupportedAppUsage getUtInterface()3184 public ImsUtInterface getUtInterface() throws ImsException { 3185 if (mImsManager == null) { 3186 throw getImsManagerIsNullException(); 3187 } 3188 3189 ImsUtInterface ut = mImsManager.getSupplementaryServiceConfiguration(); 3190 return ut; 3191 } 3192 transferHandoverConnections(ImsPhoneCall call)3193 private void transferHandoverConnections(ImsPhoneCall call) { 3194 if (call.mConnections != null) { 3195 for (Connection c : call.mConnections) { 3196 c.mPreHandoverState = call.mState; 3197 log ("Connection state before handover is " + c.getStateBeforeHandover()); 3198 } 3199 } 3200 if (mHandoverCall.mConnections == null ) { 3201 mHandoverCall.mConnections = call.mConnections; 3202 } else { // Multi-call SRVCC 3203 mHandoverCall.mConnections.addAll(call.mConnections); 3204 } 3205 if (mHandoverCall.mConnections != null) { 3206 if (call.getImsCall() != null) { 3207 call.getImsCall().close(); 3208 } 3209 for (Connection c : mHandoverCall.mConnections) { 3210 ((ImsPhoneConnection)c).changeParent(mHandoverCall); 3211 ((ImsPhoneConnection)c).releaseWakeLock(); 3212 } 3213 } 3214 if (call.getState().isAlive()) { 3215 log ("Call is alive and state is " + call.mState); 3216 mHandoverCall.mState = call.mState; 3217 } 3218 call.mConnections.clear(); 3219 call.mState = ImsPhoneCall.State.IDLE; 3220 if (mPendingMO != null) { 3221 // If the call is handed over before moving to alerting (i.e. e911 CSFB redial), clear 3222 // pending MO here. 3223 logi("pending MO on handover, clearing..."); 3224 mPendingMO = null; 3225 } 3226 } 3227 3228 /* package */ notifySrvccState(Call.SrvccState state)3229 void notifySrvccState(Call.SrvccState state) { 3230 if (DBG) log("notifySrvccState state=" + state); 3231 3232 mSrvccState = state; 3233 3234 if (mSrvccState == Call.SrvccState.COMPLETED) { 3235 resetState(); 3236 transferHandoverConnections(mForegroundCall); 3237 transferHandoverConnections(mBackgroundCall); 3238 transferHandoverConnections(mRingingCall); 3239 } 3240 } 3241 resetState()3242 private void resetState() { 3243 mIsInEmergencyCall = false; 3244 } 3245 3246 //****** Overridden from Handler 3247 3248 @Override 3249 public void handleMessage(Message msg)3250 handleMessage (Message msg) { 3251 AsyncResult ar; 3252 if (DBG) log("handleMessage what=" + msg.what); 3253 3254 switch (msg.what) { 3255 case EVENT_HANGUP_PENDINGMO: 3256 if (mPendingMO != null) { 3257 mPendingMO.onDisconnect(); 3258 removeConnection(mPendingMO); 3259 mPendingMO = null; 3260 } 3261 mPendingIntentExtras = null; 3262 updatePhoneState(); 3263 mPhone.notifyPreciseCallStateChanged(); 3264 break; 3265 case EVENT_RESUME_NOW_FOREGROUND_CALL: 3266 try { 3267 resumeForegroundCall(); 3268 } catch (ImsException e) { 3269 if (Phone.DEBUG_PHONE) { 3270 loge("handleMessage EVENT_RESUME_NOW_FOREGROUND_CALL exception=" + e); 3271 } 3272 } 3273 break; 3274 case EVENT_ANSWER_WAITING_CALL: 3275 try { 3276 answerWaitingCall(); 3277 } catch (ImsException e) { 3278 if (Phone.DEBUG_PHONE) { 3279 loge("handleMessage EVENT_ANSWER_WAITING_CALL exception=" + e); 3280 } 3281 } 3282 break; 3283 case EVENT_DIAL_PENDINGMO: 3284 dialInternal(mPendingMO, mClirMode, mPendingCallVideoState, mPendingIntentExtras); 3285 mPendingIntentExtras = null; 3286 break; 3287 3288 case EVENT_EXIT_ECBM_BEFORE_PENDINGMO: 3289 if (mPendingMO != null) { 3290 //Send ECBM exit request 3291 try { 3292 getEcbmInterface().exitEmergencyCallbackMode(); 3293 mPhone.setOnEcbModeExitResponse(this, EVENT_EXIT_ECM_RESPONSE_CDMA, null); 3294 pendingCallClirMode = mClirMode; 3295 pendingCallInEcm = true; 3296 } catch (ImsException e) { 3297 e.printStackTrace(); 3298 mPendingMO.setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED); 3299 sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO); 3300 } 3301 } 3302 break; 3303 3304 case EVENT_EXIT_ECM_RESPONSE_CDMA: 3305 // no matter the result, we still do the same here 3306 if (pendingCallInEcm) { 3307 dialInternal(mPendingMO, pendingCallClirMode, 3308 mPendingCallVideoState, mPendingIntentExtras); 3309 mPendingIntentExtras = null; 3310 pendingCallInEcm = false; 3311 } 3312 mPhone.unsetOnEcbModeExitResponse(this); 3313 break; 3314 case EVENT_VT_DATA_USAGE_UPDATE: 3315 ar = (AsyncResult) msg.obj; 3316 ImsCall call = (ImsCall) ar.userObj; 3317 Long usage = (long) ar.result; 3318 log("VT data usage update. usage = " + usage + ", imsCall = " + call); 3319 if (usage > 0) { 3320 updateVtDataUsage(call, usage); 3321 } 3322 break; 3323 case EVENT_DATA_ENABLED_CHANGED: 3324 ar = (AsyncResult) msg.obj; 3325 if (ar.result instanceof Pair) { 3326 Pair<Boolean, Integer> p = (Pair<Boolean, Integer>) ar.result; 3327 onDataEnabledChanged(p.first, p.second); 3328 } 3329 break; 3330 case EVENT_CHECK_FOR_WIFI_HANDOVER: 3331 if (msg.obj instanceof ImsCall) { 3332 ImsCall imsCall = (ImsCall) msg.obj; 3333 if (imsCall != mForegroundCall.getImsCall()) { 3334 Rlog.i(LOG_TAG, "handoverCheck: no longer FG; check skipped."); 3335 unregisterForConnectivityChanges(); 3336 // Handover check and its not the foreground call any more. 3337 return; 3338 } 3339 if (!mHasAttemptedStartOfCallHandover) { 3340 mHasAttemptedStartOfCallHandover = true; 3341 } 3342 if (!imsCall.isWifiCall()) { 3343 // Call did not handover to wifi, notify of handover failure. 3344 ImsPhoneConnection conn = findConnection(imsCall); 3345 if (conn != null) { 3346 Rlog.i(LOG_TAG, "handoverCheck: handover failed."); 3347 conn.onHandoverToWifiFailed(); 3348 } 3349 3350 if (imsCall.isVideoCall() 3351 && conn.getDisconnectCause() == DisconnectCause.NOT_DISCONNECTED) { 3352 registerForConnectivityChanges(); 3353 } 3354 } 3355 } 3356 break; 3357 case EVENT_ON_FEATURE_CAPABILITY_CHANGED: { 3358 SomeArgs args = (SomeArgs) msg.obj; 3359 try { 3360 ImsFeature.Capabilities capabilities = (ImsFeature.Capabilities) args.arg1; 3361 handleFeatureCapabilityChanged(capabilities); 3362 } finally { 3363 args.recycle(); 3364 } 3365 break; 3366 } 3367 case EVENT_SUPP_SERVICE_INDICATION: { 3368 ar = (AsyncResult) msg.obj; 3369 ImsPhoneMmiCode mmiCode = new ImsPhoneMmiCode(mPhone); 3370 try { 3371 mmiCode.setIsSsInfo(true); 3372 mmiCode.processImsSsData(ar); 3373 } catch (ImsException e) { 3374 Rlog.e(LOG_TAG, "Exception in parsing SS Data: " + e); 3375 } 3376 break; 3377 } 3378 case EVENT_REDIAL_WIFI_E911_CALL: { 3379 Pair<ImsCall, ImsReasonInfo> callInfo = 3380 (Pair<ImsCall, ImsReasonInfo>) ((AsyncResult) msg.obj).userObj; 3381 removeMessages(EVENT_REDIAL_WIFI_E911_TIMEOUT); 3382 mPhone.getDefaultPhone().getServiceStateTracker() 3383 .unregisterForNetworkAttached(this); 3384 ImsPhoneConnection oldConnection = findConnection(callInfo.first); 3385 if (oldConnection == null) { 3386 sendCallStartFailedDisconnect(callInfo.first, callInfo.second); 3387 break; 3388 } 3389 mForegroundCall.detach(oldConnection); 3390 removeConnection(oldConnection); 3391 try { 3392 Connection newConnection = 3393 mPhone.getDefaultPhone().dial(mLastDialString, mLastDialArgs); 3394 oldConnection.onOriginalConnectionReplaced(newConnection); 3395 } catch (CallStateException e) { 3396 sendCallStartFailedDisconnect(callInfo.first, callInfo.second); 3397 } 3398 break; 3399 } 3400 case EVENT_REDIAL_WIFI_E911_TIMEOUT: { 3401 Pair<ImsCall, ImsReasonInfo> callInfo = (Pair<ImsCall, ImsReasonInfo>) msg.obj; 3402 mPhone.getDefaultPhone().getServiceStateTracker() 3403 .unregisterForNetworkAttached(this); 3404 removeMessages(EVENT_REDIAL_WIFI_E911_CALL); 3405 sendCallStartFailedDisconnect(callInfo.first, callInfo.second); 3406 break; 3407 } 3408 } 3409 } 3410 3411 /** 3412 * Update video call data usage 3413 * 3414 * @param call The IMS call 3415 * @param dataUsage The aggregated data usage for the call 3416 */ updateVtDataUsage(ImsCall call, long dataUsage)3417 private void updateVtDataUsage(ImsCall call, long dataUsage) { 3418 long oldUsage = 0L; 3419 if (mVtDataUsageMap.containsKey(call.uniqueId)) { 3420 oldUsage = mVtDataUsageMap.get(call.uniqueId); 3421 } 3422 3423 long delta = dataUsage - oldUsage; 3424 mVtDataUsageMap.put(call.uniqueId, dataUsage); 3425 3426 log("updateVtDataUsage: call=" + call + ", delta=" + delta); 3427 3428 long currentTime = SystemClock.elapsedRealtime(); 3429 int isRoaming = mPhone.getServiceState().getDataRoaming() ? 1 : 0; 3430 3431 // Create the snapshot of total video call data usage. 3432 NetworkStats vtDataUsageSnapshot = new NetworkStats(currentTime, 1); 3433 vtDataUsageSnapshot.combineAllValues(mVtDataUsageSnapshot); 3434 // Since the modem only reports the total vt data usage rather than rx/tx separately, 3435 // the only thing we can do here is splitting the usage into half rx and half tx. 3436 // Uid -1 indicates this is for the overall device data usage. 3437 vtDataUsageSnapshot.combineValues(new NetworkStats.Entry( 3438 NetworkStatsService.VT_INTERFACE, -1, NetworkStats.SET_FOREGROUND, 3439 NetworkStats.TAG_NONE, NetworkStats.METERED_YES, isRoaming, 3440 NetworkStats.DEFAULT_NETWORK_YES, delta / 2, 0, delta / 2, 0, 0)); 3441 mVtDataUsageSnapshot = vtDataUsageSnapshot; 3442 3443 // Create the snapshot of video call data usage per dialer. combineValues will create 3444 // a separate entry if uid is different from the previous snapshot. 3445 NetworkStats vtDataUsageUidSnapshot = new NetworkStats(currentTime, 1); 3446 vtDataUsageUidSnapshot.combineAllValues(mVtDataUsageUidSnapshot); 3447 3448 // The dialer uid might not be initialized correctly during boot up due to telecom service 3449 // not ready or its default dialer cache not ready. So we double check again here to see if 3450 // default dialer uid is really not available. 3451 if (mDefaultDialerUid.get() == NetworkStats.UID_ALL) { 3452 final TelecomManager telecomManager = 3453 (TelecomManager) mPhone.getContext().getSystemService(Context.TELECOM_SERVICE); 3454 mDefaultDialerUid.set( 3455 getPackageUid(mPhone.getContext(), telecomManager.getDefaultDialerPackage())); 3456 } 3457 3458 // Since the modem only reports the total vt data usage rather than rx/tx separately, 3459 // the only thing we can do here is splitting the usage into half rx and half tx. 3460 vtDataUsageUidSnapshot.combineValues(new NetworkStats.Entry( 3461 NetworkStatsService.VT_INTERFACE, mDefaultDialerUid.get(), 3462 NetworkStats.SET_FOREGROUND, NetworkStats.TAG_NONE, NetworkStats.METERED_YES, 3463 isRoaming, NetworkStats.DEFAULT_NETWORK_YES, delta / 2, 0, delta / 2, 0, 0)); 3464 mVtDataUsageUidSnapshot = vtDataUsageUidSnapshot; 3465 } 3466 3467 @UnsupportedAppUsage 3468 @Override log(String msg)3469 protected void log(String msg) { 3470 Rlog.d(LOG_TAG, "[" + mPhone.getPhoneId() + "] " + msg); 3471 } 3472 3473 @UnsupportedAppUsage loge(String msg)3474 protected void loge(String msg) { 3475 Rlog.e(LOG_TAG, "[" + mPhone.getPhoneId() + "] " + msg); 3476 } 3477 logi(String msg)3478 void logi(String msg) { 3479 Rlog.i(LOG_TAG, "[" + mPhone.getPhoneId() + "] " + msg); 3480 } 3481 logHoldSwapState(String loc)3482 void logHoldSwapState(String loc) { 3483 String holdSwapState = "???"; 3484 switch (mHoldSwitchingState) { 3485 case INACTIVE: 3486 holdSwapState = "INACTIVE"; 3487 break; 3488 case PENDING_SINGLE_CALL_HOLD: 3489 holdSwapState = "PENDING_SINGLE_CALL_HOLD"; 3490 break; 3491 case PENDING_SINGLE_CALL_UNHOLD: 3492 holdSwapState = "PENDING_SINGLE_CALL_UNHOLD"; 3493 break; 3494 case SWAPPING_ACTIVE_AND_HELD: 3495 holdSwapState = "SWAPPING_ACTIVE_AND_HELD"; 3496 break; 3497 case HOLDING_TO_ANSWER_INCOMING: 3498 holdSwapState = "HOLDING_TO_ANSWER_INCOMING"; 3499 break; 3500 case PENDING_RESUME_FOREGROUND_AFTER_FAILURE: 3501 holdSwapState = "PENDING_RESUME_FOREGROUND_AFTER_FAILURE"; 3502 break; 3503 case HOLDING_TO_DIAL_OUTGOING: 3504 holdSwapState = "HOLDING_TO_DIAL_OUTGOING"; 3505 break; 3506 } 3507 logi("holdSwapState set to " + holdSwapState + " at " + loc); 3508 } 3509 3510 /** 3511 * Logs the current state of the ImsPhoneCallTracker. Useful for debugging issues with 3512 * call tracking. 3513 */ 3514 /* package */ logState()3515 void logState() { 3516 if (!VERBOSE_STATE_LOGGING) { 3517 return; 3518 } 3519 3520 StringBuilder sb = new StringBuilder(); 3521 sb.append("Current IMS PhoneCall State:\n"); 3522 sb.append(" Foreground: "); 3523 sb.append(mForegroundCall); 3524 sb.append("\n"); 3525 sb.append(" Background: "); 3526 sb.append(mBackgroundCall); 3527 sb.append("\n"); 3528 sb.append(" Ringing: "); 3529 sb.append(mRingingCall); 3530 sb.append("\n"); 3531 sb.append(" Handover: "); 3532 sb.append(mHandoverCall); 3533 sb.append("\n"); 3534 Rlog.v(LOG_TAG, sb.toString()); 3535 } 3536 3537 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)3538 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 3539 pw.println("ImsPhoneCallTracker extends:"); 3540 super.dump(fd, pw, args); 3541 pw.println(" mVoiceCallEndedRegistrants=" + mVoiceCallEndedRegistrants); 3542 pw.println(" mVoiceCallStartedRegistrants=" + mVoiceCallStartedRegistrants); 3543 pw.println(" mRingingCall=" + mRingingCall); 3544 pw.println(" mForegroundCall=" + mForegroundCall); 3545 pw.println(" mBackgroundCall=" + mBackgroundCall); 3546 pw.println(" mHandoverCall=" + mHandoverCall); 3547 pw.println(" mPendingMO=" + mPendingMO); 3548 //pw.println(" mHangupPendingMO=" + mHangupPendingMO); 3549 pw.println(" mPhone=" + mPhone); 3550 pw.println(" mDesiredMute=" + mDesiredMute); 3551 pw.println(" mState=" + mState); 3552 pw.println(" mMmTelCapabilities=" + mMmTelCapabilities); 3553 pw.println(" mDefaultDialerUid=" + mDefaultDialerUid.get()); 3554 pw.println(" mVtDataUsageSnapshot=" + mVtDataUsageSnapshot); 3555 pw.println(" mVtDataUsageUidSnapshot=" + mVtDataUsageUidSnapshot); 3556 pw.println(" mCallQualityMetrics=" + mCallQualityMetrics); 3557 pw.println(" mCallQualityMetricsHistory=" + mCallQualityMetricsHistory); 3558 3559 pw.flush(); 3560 pw.println("++++++++++++++++++++++++++++++++"); 3561 3562 try { 3563 if (mImsManager != null) { 3564 mImsManager.dump(fd, pw, args); 3565 } 3566 } catch (Exception e) { 3567 e.printStackTrace(); 3568 } 3569 3570 if (mConnections != null && mConnections.size() > 0) { 3571 pw.println("mConnections:"); 3572 for (int i = 0; i < mConnections.size(); i++) { 3573 pw.println(" [" + i + "]: " + mConnections.get(i)); 3574 } 3575 } 3576 } 3577 3578 @Override handlePollCalls(AsyncResult ar)3579 protected void handlePollCalls(AsyncResult ar) { 3580 } 3581 3582 /* package */ getEcbmInterface()3583 ImsEcbm getEcbmInterface() throws ImsException { 3584 if (mImsManager == null) { 3585 throw getImsManagerIsNullException(); 3586 } 3587 3588 ImsEcbm ecbm = mImsManager.getEcbmInterface(); 3589 return ecbm; 3590 } 3591 3592 /* package */ getMultiEndpointInterface()3593 ImsMultiEndpoint getMultiEndpointInterface() throws ImsException { 3594 if (mImsManager == null) { 3595 throw getImsManagerIsNullException(); 3596 } 3597 3598 try { 3599 return mImsManager.getMultiEndpointInterface(); 3600 } catch (ImsException e) { 3601 if (e.getCode() == ImsReasonInfo.CODE_MULTIENDPOINT_NOT_SUPPORTED) { 3602 return null; 3603 } else { 3604 throw e; 3605 } 3606 3607 } 3608 } 3609 isInEmergencyCall()3610 public boolean isInEmergencyCall() { 3611 return mIsInEmergencyCall; 3612 } 3613 3614 /** 3615 * @return true if the IMS capability for the specified registration technology is currently 3616 * available. 3617 */ isImsCapabilityAvailable(int capability, int regTech)3618 public boolean isImsCapabilityAvailable(int capability, int regTech) { 3619 return (getImsRegistrationTech() == regTech) && mMmTelCapabilities.isCapable(capability); 3620 } 3621 isVolteEnabled()3622 public boolean isVolteEnabled() { 3623 boolean isRadioTechLte = getImsRegistrationTech() 3624 == ImsRegistrationImplBase.REGISTRATION_TECH_LTE; 3625 return isRadioTechLte && mMmTelCapabilities.isCapable( 3626 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE); 3627 } 3628 isVowifiEnabled()3629 public boolean isVowifiEnabled() { 3630 boolean isRadioTechIwlan = getImsRegistrationTech() 3631 == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN; 3632 return isRadioTechIwlan && mMmTelCapabilities.isCapable( 3633 MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE); 3634 } 3635 isVideoCallEnabled()3636 public boolean isVideoCallEnabled() { 3637 return mMmTelCapabilities.isCapable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO); 3638 } 3639 3640 @Override getState()3641 public PhoneConstants.State getState() { 3642 return mState; 3643 } 3644 getImsRegistrationTech()3645 public int getImsRegistrationTech() { 3646 if (mImsManager != null) { 3647 return mImsManager.getRegistrationTech(); 3648 } 3649 return ImsRegistrationImplBase.REGISTRATION_TECH_NONE; 3650 } 3651 retryGetImsService()3652 private void retryGetImsService() { 3653 // The binder connection is already up. Do not try to get it again. 3654 if (mImsManager.isServiceAvailable()) { 3655 return; 3656 } 3657 3658 mImsManagerConnector.connect(); 3659 } 3660 setVideoCallProvider(ImsPhoneConnection conn, ImsCall imsCall)3661 private void setVideoCallProvider(ImsPhoneConnection conn, ImsCall imsCall) 3662 throws RemoteException { 3663 IImsVideoCallProvider imsVideoCallProvider = 3664 imsCall.getCallSession().getVideoCallProvider(); 3665 if (imsVideoCallProvider != null) { 3666 // TODO: Remove this when we can better formalize the format of session modify requests. 3667 boolean useVideoPauseWorkaround = mPhone.getContext().getResources().getBoolean( 3668 com.android.internal.R.bool.config_useVideoPauseWorkaround); 3669 3670 ImsVideoCallProviderWrapper imsVideoCallProviderWrapper = 3671 new ImsVideoCallProviderWrapper(imsVideoCallProvider); 3672 if (useVideoPauseWorkaround) { 3673 imsVideoCallProviderWrapper.setUseVideoPauseWorkaround(useVideoPauseWorkaround); 3674 } 3675 conn.setVideoProvider(imsVideoCallProviderWrapper); 3676 imsVideoCallProviderWrapper.registerForDataUsageUpdate 3677 (this, EVENT_VT_DATA_USAGE_UPDATE, imsCall); 3678 imsVideoCallProviderWrapper.addImsVideoProviderCallback(conn); 3679 } 3680 } 3681 isUtEnabled()3682 public boolean isUtEnabled() { 3683 return mMmTelCapabilities.isCapable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT); 3684 } 3685 3686 /** 3687 * Given a call subject, removes any characters considered by the current carrier to be 3688 * invalid, as well as escaping (using \) any characters which the carrier requires to be 3689 * escaped. 3690 * 3691 * @param callSubject The call subject. 3692 * @return The call subject with invalid characters removed and escaping applied as required. 3693 */ cleanseInstantLetteringMessage(String callSubject)3694 private String cleanseInstantLetteringMessage(String callSubject) { 3695 if (TextUtils.isEmpty(callSubject)) { 3696 return callSubject; 3697 } 3698 3699 // Get the carrier config for the current sub. 3700 CarrierConfigManager configMgr = (CarrierConfigManager) 3701 mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE); 3702 // Bail if we can't find the carrier config service. 3703 if (configMgr == null) { 3704 return callSubject; 3705 } 3706 3707 PersistableBundle carrierConfig = configMgr.getConfigForSubId(mPhone.getSubId()); 3708 // Bail if no carrier config found. 3709 if (carrierConfig == null) { 3710 return callSubject; 3711 } 3712 3713 // Try to replace invalid characters 3714 String invalidCharacters = carrierConfig.getString( 3715 CarrierConfigManager.KEY_CARRIER_INSTANT_LETTERING_INVALID_CHARS_STRING); 3716 if (!TextUtils.isEmpty(invalidCharacters)) { 3717 callSubject = callSubject.replaceAll(invalidCharacters, ""); 3718 } 3719 3720 // Try to escape characters which need to be escaped. 3721 String escapedCharacters = carrierConfig.getString( 3722 CarrierConfigManager.KEY_CARRIER_INSTANT_LETTERING_ESCAPED_CHARS_STRING); 3723 if (!TextUtils.isEmpty(escapedCharacters)) { 3724 callSubject = escapeChars(escapedCharacters, callSubject); 3725 } 3726 return callSubject; 3727 } 3728 3729 /** 3730 * Given a source string, return a string where a set of characters are escaped using the 3731 * backslash character. 3732 * 3733 * @param toEscape The characters to escape with a backslash. 3734 * @param source The source string. 3735 * @return The source string with characters escaped. 3736 */ escapeChars(String toEscape, String source)3737 private String escapeChars(String toEscape, String source) { 3738 StringBuilder escaped = new StringBuilder(); 3739 for (char c : source.toCharArray()) { 3740 if (toEscape.contains(Character.toString(c))) { 3741 escaped.append("\\"); 3742 } 3743 escaped.append(c); 3744 } 3745 3746 return escaped.toString(); 3747 } 3748 3749 /** 3750 * Initiates a pull of an external call. 3751 * 3752 * Initiates a pull by making a dial request with the {@link ImsCallProfile#EXTRA_IS_CALL_PULL} 3753 * extra specified. We call {@link ImsPhone#notifyUnknownConnection(Connection)} which notifies 3754 * Telecom of the new dialed connection. The 3755 * {@code PstnIncomingCallNotifier#maybeSwapWithUnknownConnection} logic ensures that the new 3756 * {@link ImsPhoneConnection} resulting from the dial gets swapped with the 3757 * {@link ImsExternalConnection}, which effectively makes the external call become a regular 3758 * call. Magic! 3759 * 3760 * @param number The phone number of the call to be pulled. 3761 * @param videoState The desired video state of the pulled call. 3762 * @param dialogId The {@link ImsExternalConnection#getCallId()} dialog id associated with the 3763 * call which is being pulled. 3764 */ 3765 @Override pullExternalCall(String number, int videoState, int dialogId)3766 public void pullExternalCall(String number, int videoState, int dialogId) { 3767 Bundle extras = new Bundle(); 3768 extras.putBoolean(ImsCallProfile.EXTRA_IS_CALL_PULL, true); 3769 extras.putInt(ImsExternalCallTracker.EXTRA_IMS_EXTERNAL_CALL_ID, dialogId); 3770 try { 3771 Connection connection = dial(number, videoState, extras); 3772 mPhone.notifyUnknownConnection(connection); 3773 } catch (CallStateException e) { 3774 loge("pullExternalCall failed - " + e); 3775 } 3776 } 3777 getImsManagerIsNullException()3778 private ImsException getImsManagerIsNullException() { 3779 return new ImsException("no ims manager", ImsReasonInfo.CODE_LOCAL_ILLEGAL_STATE); 3780 } 3781 3782 /** 3783 * Determines if answering an incoming call will cause the active call to be disconnected. 3784 * <p> 3785 * This will be the case if 3786 * {@link CarrierConfigManager#KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL} is 3787 * {@code true} for the carrier, the active call is a video call over WIFI, and the incoming 3788 * call is an audio call. 3789 * 3790 * @param activeCall The active call. 3791 * @param incomingCall The incoming call. 3792 * @return {@code true} if answering the incoming call will cause the active call to be 3793 * disconnected, {@code false} otherwise. 3794 */ shouldDisconnectActiveCallOnAnswer(ImsCall activeCall, ImsCall incomingCall)3795 private boolean shouldDisconnectActiveCallOnAnswer(ImsCall activeCall, 3796 ImsCall incomingCall) { 3797 3798 if (activeCall == null || incomingCall == null) { 3799 return false; 3800 } 3801 3802 if (!mDropVideoCallWhenAnsweringAudioCall) { 3803 return false; 3804 } 3805 3806 boolean isActiveCallVideo = activeCall.isVideoCall() || 3807 (mTreatDowngradedVideoCallsAsVideoCalls && activeCall.wasVideoCall()); 3808 boolean isActiveCallOnWifi = activeCall.isWifiCall(); 3809 boolean isVoWifiEnabled = mImsManager.isWfcEnabledByPlatform() 3810 && mImsManager.isWfcEnabledByUser(); 3811 boolean isIncomingCallAudio = !incomingCall.isVideoCall(); 3812 log("shouldDisconnectActiveCallOnAnswer : isActiveCallVideo=" + isActiveCallVideo + 3813 " isActiveCallOnWifi=" + isActiveCallOnWifi + " isIncomingCallAudio=" + 3814 isIncomingCallAudio + " isVowifiEnabled=" + isVoWifiEnabled); 3815 3816 return isActiveCallVideo && isActiveCallOnWifi && isIncomingCallAudio && !isVoWifiEnabled; 3817 } 3818 3819 /** 3820 * Get aggregated video call data usage since boot. 3821 * 3822 * @param perUidStats True if requesting data usage per uid, otherwise overall usage. 3823 * @return Snapshot of video call data usage 3824 */ getVtDataUsage(boolean perUidStats)3825 public NetworkStats getVtDataUsage(boolean perUidStats) { 3826 3827 // If there is an ongoing VT call, request the latest VT usage from the modem. The latest 3828 // usage will return asynchronously so it won't be counted in this round, but it will be 3829 // eventually counted when next getVtDataUsage is called. 3830 if (mState != PhoneConstants.State.IDLE) { 3831 for (ImsPhoneConnection conn : mConnections) { 3832 android.telecom.Connection.VideoProvider videoProvider = conn.getVideoProvider(); 3833 if (videoProvider != null) { 3834 videoProvider.onRequestConnectionDataUsage(); 3835 } 3836 } 3837 } 3838 3839 return perUidStats ? mVtDataUsageUidSnapshot : mVtDataUsageSnapshot; 3840 } 3841 registerPhoneStateListener(PhoneStateListener listener)3842 public void registerPhoneStateListener(PhoneStateListener listener) { 3843 mPhoneStateListeners.add(listener); 3844 } 3845 unregisterPhoneStateListener(PhoneStateListener listener)3846 public void unregisterPhoneStateListener(PhoneStateListener listener) { 3847 mPhoneStateListeners.remove(listener); 3848 } 3849 3850 /** 3851 * Notifies local telephony listeners of changes to the IMS phone state. 3852 * 3853 * @param oldState The old state. 3854 * @param newState The new state. 3855 */ notifyPhoneStateChanged(PhoneConstants.State oldState, PhoneConstants.State newState)3856 private void notifyPhoneStateChanged(PhoneConstants.State oldState, 3857 PhoneConstants.State newState) { 3858 3859 for (PhoneStateListener listener : mPhoneStateListeners) { 3860 listener.onPhoneStateChanged(oldState, newState); 3861 } 3862 } 3863 3864 /** Modify video call to a new video state. 3865 * 3866 * @param imsCall IMS call to be modified 3867 * @param newVideoState New video state. (Refer to VideoProfile) 3868 */ modifyVideoCall(ImsCall imsCall, int newVideoState)3869 private void modifyVideoCall(ImsCall imsCall, int newVideoState) { 3870 ImsPhoneConnection conn = findConnection(imsCall); 3871 if (conn != null) { 3872 int oldVideoState = conn.getVideoState(); 3873 if (conn.getVideoProvider() != null) { 3874 conn.getVideoProvider().onSendSessionModifyRequest( 3875 new VideoProfile(oldVideoState), new VideoProfile(newVideoState)); 3876 } 3877 } 3878 } 3879 isViLteDataMetered()3880 public boolean isViLteDataMetered() { 3881 return mIsViLteDataMetered; 3882 } 3883 3884 /** 3885 * Handler of data enabled changed event 3886 * @param enabled True if data is enabled, otherwise disabled. 3887 * @param reason Reason for data enabled/disabled. See {@link DataEnabledChangedReason}. 3888 */ onDataEnabledChanged(boolean enabled, @DataEnabledChangedReason int reason)3889 private void onDataEnabledChanged(boolean enabled, @DataEnabledChangedReason int reason) { 3890 3891 log("onDataEnabledChanged: enabled=" + enabled + ", reason=" + reason); 3892 3893 mIsDataEnabled = enabled; 3894 3895 if (!mIsViLteDataMetered) { 3896 log("Ignore data " + ((enabled) ? "enabled" : "disabled") + " - carrier policy " 3897 + "indicates that data is not metered for ViLTE calls."); 3898 return; 3899 } 3900 3901 // Inform connections that data has been disabled to ensure we turn off video capability 3902 // if this is an LTE call. 3903 for (ImsPhoneConnection conn : mConnections) { 3904 ImsCall imsCall = conn.getImsCall(); 3905 boolean isLocalVideoCapable = enabled || (imsCall != null && imsCall.isWifiCall()); 3906 conn.setLocalVideoCapable(isLocalVideoCapable); 3907 } 3908 3909 int reasonCode; 3910 if (reason == DataEnabledSettings.REASON_POLICY_DATA_ENABLED) { 3911 reasonCode = ImsReasonInfo.CODE_DATA_LIMIT_REACHED; 3912 } else if (reason == DataEnabledSettings.REASON_USER_DATA_ENABLED) { 3913 reasonCode = ImsReasonInfo.CODE_DATA_DISABLED; 3914 } else { 3915 // Unexpected code, default to data disabled. 3916 reasonCode = ImsReasonInfo.CODE_DATA_DISABLED; 3917 } 3918 3919 // Potentially send connection events so the InCall UI knows that video calls are being 3920 // downgraded due to data being enabled/disabled. 3921 maybeNotifyDataDisabled(enabled, reasonCode); 3922 // Handle video state changes required as a result of data being enabled/disabled. 3923 handleDataEnabledChange(enabled, reasonCode); 3924 3925 // We do not want to update the ImsConfig for REASON_REGISTERED, since it can happen before 3926 // the carrier config has loaded and will deregister IMS. 3927 if (!mShouldUpdateImsConfigOnDisconnect 3928 && reason != DataEnabledSettings.REASON_REGISTERED && mCarrierConfigLoaded) { 3929 // This will call into updateVideoCallFeatureValue and eventually all clients will be 3930 // asynchronously notified that the availability of VT over LTE has changed. 3931 if (mImsManager != null) { 3932 mImsManager.updateImsServiceConfig(true); 3933 } 3934 } 3935 } 3936 maybeNotifyDataDisabled(boolean enabled, int reasonCode)3937 private void maybeNotifyDataDisabled(boolean enabled, int reasonCode) { 3938 if (!enabled) { 3939 // If data is disabled while there are ongoing VT calls which are not taking place over 3940 // wifi, then they should be disconnected to prevent the user from incurring further 3941 // data charges. 3942 for (ImsPhoneConnection conn : mConnections) { 3943 ImsCall imsCall = conn.getImsCall(); 3944 if (imsCall != null && imsCall.isVideoCall() && !imsCall.isWifiCall()) { 3945 if (conn.hasCapabilities( 3946 Connection.Capability.SUPPORTS_DOWNGRADE_TO_VOICE_LOCAL | 3947 Connection.Capability.SUPPORTS_DOWNGRADE_TO_VOICE_REMOTE)) { 3948 3949 // If the carrier supports downgrading to voice, then we can simply issue a 3950 // downgrade to voice instead of terminating the call. 3951 if (reasonCode == ImsReasonInfo.CODE_DATA_DISABLED) { 3952 conn.onConnectionEvent(TelephonyManager.EVENT_DOWNGRADE_DATA_DISABLED, 3953 null); 3954 } else if (reasonCode == ImsReasonInfo.CODE_DATA_LIMIT_REACHED) { 3955 conn.onConnectionEvent( 3956 TelephonyManager.EVENT_DOWNGRADE_DATA_LIMIT_REACHED, null); 3957 } 3958 } 3959 } 3960 } 3961 } 3962 } 3963 3964 /** 3965 * Handles changes to the enabled state of mobile data. 3966 * When data is disabled, handles auto-downgrade of video calls over LTE. 3967 * When data is enabled, handled resuming of video calls paused when data was disabled. 3968 * @param enabled {@code true} if mobile data is enabled, {@code false} if mobile data is 3969 * disabled. 3970 * @param reasonCode The {@link ImsReasonInfo} code for the data enabled state change. 3971 */ handleDataEnabledChange(boolean enabled, int reasonCode)3972 private void handleDataEnabledChange(boolean enabled, int reasonCode) { 3973 if (!enabled) { 3974 // If data is disabled while there are ongoing VT calls which are not taking place over 3975 // wifi, then they should be disconnected to prevent the user from incurring further 3976 // data charges. 3977 for (ImsPhoneConnection conn : mConnections) { 3978 ImsCall imsCall = conn.getImsCall(); 3979 if (imsCall != null && imsCall.isVideoCall() && !imsCall.isWifiCall()) { 3980 log("handleDataEnabledChange - downgrading " + conn); 3981 downgradeVideoCall(reasonCode, conn); 3982 } 3983 } 3984 } else if (mSupportPauseVideo) { 3985 // Data was re-enabled, so un-pause previously paused video calls. 3986 for (ImsPhoneConnection conn : mConnections) { 3987 // If video is paused, check to see if there are any pending pauses due to enabled 3988 // state of data changing. 3989 log("handleDataEnabledChange - resuming " + conn); 3990 if (VideoProfile.isPaused(conn.getVideoState()) && 3991 conn.wasVideoPausedFromSource(VideoPauseTracker.SOURCE_DATA_ENABLED)) { 3992 // The data enabled state was a cause of a pending pause, so potentially 3993 // resume the video now. 3994 conn.resumeVideo(VideoPauseTracker.SOURCE_DATA_ENABLED); 3995 } 3996 } 3997 mShouldUpdateImsConfigOnDisconnect = false; 3998 } 3999 } 4000 4001 /** 4002 * Handles downgrading a video call. The behavior depends on carrier capabilities; we will 4003 * attempt to take one of the following actions (in order of precedence): 4004 * 1. If supported by the carrier, the call will be downgraded to an audio-only call. 4005 * 2. If the carrier supports video pause signalling, the video will be paused. 4006 * 3. The call will be disconnected. 4007 * @param reasonCode The {@link ImsReasonInfo} reason code for the downgrade. 4008 * @param conn The {@link ImsPhoneConnection} to downgrade. 4009 */ downgradeVideoCall(int reasonCode, ImsPhoneConnection conn)4010 private void downgradeVideoCall(int reasonCode, ImsPhoneConnection conn) { 4011 ImsCall imsCall = conn.getImsCall(); 4012 if (imsCall != null) { 4013 if (conn.hasCapabilities( 4014 Connection.Capability.SUPPORTS_DOWNGRADE_TO_VOICE_LOCAL | 4015 Connection.Capability.SUPPORTS_DOWNGRADE_TO_VOICE_REMOTE) 4016 && !mSupportPauseVideo) { 4017 log("downgradeVideoCall :: callId=" + conn.getTelecomCallId() 4018 + " Downgrade to audio"); 4019 // If the carrier supports downgrading to voice, then we can simply issue a 4020 // downgrade to voice instead of terminating the call. 4021 modifyVideoCall(imsCall, VideoProfile.STATE_AUDIO_ONLY); 4022 } else if (mSupportPauseVideo && reasonCode != ImsReasonInfo.CODE_WIFI_LOST) { 4023 // The carrier supports video pause signalling, so pause the video if we didn't just 4024 // lose wifi; in that case just disconnect. 4025 log("downgradeVideoCall :: callId=" + conn.getTelecomCallId() 4026 + " Pause audio"); 4027 mShouldUpdateImsConfigOnDisconnect = true; 4028 conn.pauseVideo(VideoPauseTracker.SOURCE_DATA_ENABLED); 4029 } else { 4030 log("downgradeVideoCall :: callId=" + conn.getTelecomCallId() 4031 + " Disconnect call."); 4032 // At this point the only choice we have is to terminate the call. 4033 imsCall.terminate(ImsReasonInfo.CODE_USER_TERMINATED, reasonCode); 4034 } 4035 } 4036 } 4037 resetImsCapabilities()4038 private void resetImsCapabilities() { 4039 log("Resetting Capabilities..."); 4040 boolean tmpIsVideoCallEnabled = isVideoCallEnabled(); 4041 mMmTelCapabilities = new MmTelFeature.MmTelCapabilities(); 4042 4043 boolean isVideoEnabled = isVideoCallEnabled(); 4044 if (tmpIsVideoCallEnabled != isVideoEnabled) { 4045 mPhone.notifyForVideoCapabilityChanged(isVideoEnabled); 4046 } 4047 } 4048 4049 /** 4050 * @return {@code true} if the device is connected to a WIFI network, {@code false} otherwise. 4051 */ isWifiConnected()4052 private boolean isWifiConnected() { 4053 ConnectivityManager cm = (ConnectivityManager) mPhone.getContext() 4054 .getSystemService(Context.CONNECTIVITY_SERVICE); 4055 if (cm != null) { 4056 NetworkInfo ni = cm.getActiveNetworkInfo(); 4057 if (ni != null && ni.isConnected()) { 4058 return ni.getType() == ConnectivityManager.TYPE_WIFI; 4059 } 4060 } 4061 return false; 4062 } 4063 4064 /** 4065 * Registers for changes to network connectivity. Specifically requests the availability of new 4066 * WIFI networks which an IMS video call could potentially hand over to. 4067 */ registerForConnectivityChanges()4068 private void registerForConnectivityChanges() { 4069 if (mIsMonitoringConnectivity || !mNotifyVtHandoverToWifiFail) { 4070 return; 4071 } 4072 ConnectivityManager cm = (ConnectivityManager) mPhone.getContext() 4073 .getSystemService(Context.CONNECTIVITY_SERVICE); 4074 if (cm != null) { 4075 Rlog.i(LOG_TAG, "registerForConnectivityChanges"); 4076 NetworkCapabilities capabilities = new NetworkCapabilities(); 4077 capabilities.addTransportType(NetworkCapabilities.TRANSPORT_WIFI); 4078 NetworkRequest.Builder builder = new NetworkRequest.Builder(); 4079 builder.setCapabilities(capabilities); 4080 cm.registerNetworkCallback(builder.build(), mNetworkCallback); 4081 mIsMonitoringConnectivity = true; 4082 } 4083 } 4084 4085 /** 4086 * Unregister for connectivity changes. Will be called when a call disconnects or if the call 4087 * ends up handing over to WIFI. 4088 */ unregisterForConnectivityChanges()4089 private void unregisterForConnectivityChanges() { 4090 if (!mIsMonitoringConnectivity || !mNotifyVtHandoverToWifiFail) { 4091 return; 4092 } 4093 ConnectivityManager cm = (ConnectivityManager) mPhone.getContext() 4094 .getSystemService(Context.CONNECTIVITY_SERVICE); 4095 if (cm != null) { 4096 Rlog.i(LOG_TAG, "unregisterForConnectivityChanges"); 4097 cm.unregisterNetworkCallback(mNetworkCallback); 4098 mIsMonitoringConnectivity = false; 4099 } 4100 } 4101 4102 /** 4103 * If the foreground call is a video call, schedule a handover check if one is not already 4104 * scheduled. This method is intended ONLY for use when scheduling to watch for mid-call 4105 * handovers. 4106 */ scheduleHandoverCheck()4107 private void scheduleHandoverCheck() { 4108 ImsCall fgCall = mForegroundCall.getImsCall(); 4109 ImsPhoneConnection conn = mForegroundCall.getFirstConnection(); 4110 if (!mNotifyVtHandoverToWifiFail || fgCall == null || !fgCall.isVideoCall() || conn == null 4111 || conn.getDisconnectCause() != DisconnectCause.NOT_DISCONNECTED) { 4112 return; 4113 } 4114 4115 if (!hasMessages(EVENT_CHECK_FOR_WIFI_HANDOVER)) { 4116 Rlog.i(LOG_TAG, "scheduleHandoverCheck: schedule"); 4117 sendMessageDelayed(obtainMessage(EVENT_CHECK_FOR_WIFI_HANDOVER, fgCall), 4118 HANDOVER_TO_WIFI_TIMEOUT_MS); 4119 } 4120 } 4121 4122 /** 4123 * @return {@code true} if downgrading of a video call to audio is supported. 4124 */ isCarrierDowngradeOfVtCallSupported()4125 public boolean isCarrierDowngradeOfVtCallSupported() { 4126 return mSupportDowngradeVtToAudio; 4127 } 4128 4129 @VisibleForTesting setDataEnabled(boolean isDataEnabled)4130 public void setDataEnabled(boolean isDataEnabled) { 4131 mIsDataEnabled = isDataEnabled; 4132 } 4133 4134 // Removes old call quality metrics if mCallQualityMetricsHistory exceeds its max size pruneCallQualityMetricsHistory()4135 private void pruneCallQualityMetricsHistory() { 4136 if (mCallQualityMetricsHistory.size() > MAX_CALL_QUALITY_HISTORY) { 4137 mCallQualityMetricsHistory.poll(); 4138 } 4139 } 4140 handleFeatureCapabilityChanged(ImsFeature.Capabilities capabilities)4141 private void handleFeatureCapabilityChanged(ImsFeature.Capabilities capabilities) { 4142 boolean tmpIsVideoCallEnabled = isVideoCallEnabled(); 4143 // Check enabledFeatures to determine capabilities. We ignore disabledFeatures. 4144 StringBuilder sb; 4145 if (DBG) { 4146 sb = new StringBuilder(120); 4147 sb.append("handleFeatureCapabilityChanged: "); 4148 } 4149 sb.append(capabilities); 4150 mMmTelCapabilities = new MmTelFeature.MmTelCapabilities(capabilities); 4151 4152 boolean isVideoEnabled = isVideoCallEnabled(); 4153 boolean isVideoEnabledStatechanged = tmpIsVideoCallEnabled != isVideoEnabled; 4154 if (DBG) { 4155 sb.append(" isVideoEnabledStateChanged="); 4156 sb.append(isVideoEnabledStatechanged); 4157 } 4158 4159 if (isVideoEnabledStatechanged) { 4160 log("handleFeatureCapabilityChanged - notifyForVideoCapabilityChanged=" 4161 + isVideoEnabled); 4162 mPhone.notifyForVideoCapabilityChanged(isVideoEnabled); 4163 } 4164 4165 if (DBG) log(sb.toString()); 4166 4167 if (DBG) { 4168 log("handleFeatureCapabilityChanged: isVolteEnabled=" + isVolteEnabled() 4169 + ", isVideoCallEnabled=" + isVideoCallEnabled() 4170 + ", isVowifiEnabled=" + isVowifiEnabled() 4171 + ", isUtEnabled=" + isUtEnabled()); 4172 } 4173 4174 mPhone.onFeatureCapabilityChanged(); 4175 4176 mMetrics.writeOnImsCapabilities(mPhone.getPhoneId(), getImsRegistrationTech(), 4177 mMmTelCapabilities); 4178 } 4179 4180 @VisibleForTesting onCallHoldReceived(ImsCall imsCall)4181 public void onCallHoldReceived(ImsCall imsCall) { 4182 if (DBG) log("onCallHoldReceived"); 4183 4184 ImsPhoneConnection conn = findConnection(imsCall); 4185 if (conn != null) { 4186 if (!mOnHoldToneStarted && (ImsPhoneCall.isLocalTone(imsCall) 4187 || mAlwaysPlayRemoteHoldTone) && 4188 conn.getState() == ImsPhoneCall.State.ACTIVE) { 4189 mPhone.startOnHoldTone(conn); 4190 mOnHoldToneStarted = true; 4191 mOnHoldToneId = System.identityHashCode(conn); 4192 } 4193 conn.onConnectionEvent(android.telecom.Connection.EVENT_CALL_REMOTELY_HELD, null); 4194 4195 boolean useVideoPauseWorkaround = mPhone.getContext().getResources().getBoolean( 4196 com.android.internal.R.bool.config_useVideoPauseWorkaround); 4197 if (useVideoPauseWorkaround && mSupportPauseVideo && 4198 VideoProfile.isVideo(conn.getVideoState())) { 4199 // If we are using the video pause workaround, the vendor IMS code has issues 4200 // with video pause signalling. In this case, when a call is remotely 4201 // held, the modem does not reliably change the video state of the call to be 4202 // paused. 4203 // As a workaround, we will turn on that bit now. 4204 conn.changeToPausedState(); 4205 } 4206 } 4207 4208 SuppServiceNotification supp = new SuppServiceNotification(); 4209 supp.notificationType = SuppServiceNotification.NOTIFICATION_TYPE_CODE_2; 4210 supp.code = SuppServiceNotification.CODE_2_CALL_ON_HOLD; 4211 mPhone.notifySuppSvcNotification(supp); 4212 mMetrics.writeOnImsCallHoldReceived(mPhone.getPhoneId(), imsCall.getCallSession()); 4213 } 4214 4215 @VisibleForTesting setAlwaysPlayRemoteHoldTone(boolean shouldPlayRemoteHoldTone)4216 public void setAlwaysPlayRemoteHoldTone(boolean shouldPlayRemoteHoldTone) { 4217 mAlwaysPlayRemoteHoldTone = shouldPlayRemoteHoldTone; 4218 } 4219 getNetworkCountryIso()4220 private String getNetworkCountryIso() { 4221 String countryIso = ""; 4222 if (mPhone != null) { 4223 ServiceStateTracker sst = mPhone.getServiceStateTracker(); 4224 if (sst != null) { 4225 LocaleTracker lt = sst.getLocaleTracker(); 4226 if (lt != null) { 4227 countryIso = lt.getCurrentCountry(); 4228 } 4229 } 4230 } 4231 return countryIso; 4232 } 4233 4234 @Override getPhone()4235 public ImsPhone getPhone() { 4236 return mPhone; 4237 } 4238 } 4239