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