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