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