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