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