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