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