• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.internal.telephony.imsphone;
18 
19 import static android.telephony.AccessNetworkConstants.TRANSPORT_TYPE_INVALID;
20 import static android.telephony.ims.ImsManager.EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE;
21 import static android.telephony.ims.ImsManager.EXTRA_WFC_REGISTRATION_FAILURE_TITLE;
22 import static android.telephony.ims.RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED;
23 import static android.telephony.ims.RegistrationManager.REGISTRATION_STATE_REGISTERED;
24 import static android.telephony.ims.RegistrationManager.REGISTRATION_STATE_REGISTERING;
25 import static android.telephony.ims.RegistrationManager.SUGGESTED_ACTION_NONE;
26 import static android.telephony.ims.RegistrationManager.SUGGESTED_ACTION_TRIGGER_CLEAR_RAT_BLOCKS;
27 import static android.telephony.ims.RegistrationManager.SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK;
28 import static android.telephony.ims.RegistrationManager.SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK_WITH_TIMEOUT;
29 import static android.telephony.ims.RegistrationManager.SUGGESTED_ACTION_TRIGGER_RAT_BLOCK;
30 import static android.telephony.ims.stub.ImsRegistrationImplBase.REGISTRATION_TECH_NONE;
31 
32 import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAIC;
33 import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAICr;
34 import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOC;
35 import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOIC;
36 import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BAOICxH;
37 import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_ALL;
38 import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_MO;
39 import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BA_MT;
40 import static com.android.internal.telephony.CommandsInterface.CB_FACILITY_BIC_ACR;
41 import static com.android.internal.telephony.CommandsInterface.CF_ACTION_DISABLE;
42 import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ENABLE;
43 import static com.android.internal.telephony.CommandsInterface.CF_ACTION_ERASURE;
44 import static com.android.internal.telephony.CommandsInterface.CF_ACTION_REGISTRATION;
45 import static com.android.internal.telephony.CommandsInterface.CF_REASON_ALL;
46 import static com.android.internal.telephony.CommandsInterface.CF_REASON_ALL_CONDITIONAL;
47 import static com.android.internal.telephony.CommandsInterface.CF_REASON_BUSY;
48 import static com.android.internal.telephony.CommandsInterface.CF_REASON_NOT_REACHABLE;
49 import static com.android.internal.telephony.CommandsInterface.CF_REASON_NO_REPLY;
50 import static com.android.internal.telephony.CommandsInterface.CF_REASON_UNCONDITIONAL;
51 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_NONE;
52 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE;
53 
54 import android.annotation.NonNull;
55 import android.app.Activity;
56 import android.app.Notification;
57 import android.app.NotificationManager;
58 import android.app.PendingIntent;
59 import android.compat.annotation.UnsupportedAppUsage;
60 import android.content.BroadcastReceiver;
61 import android.content.Context;
62 import android.content.Intent;
63 import android.content.SharedPreferences;
64 import android.net.Uri;
65 import android.os.AsyncResult;
66 import android.os.Build;
67 import android.os.Bundle;
68 import android.os.Handler;
69 import android.os.Message;
70 import android.os.PersistableBundle;
71 import android.os.PowerManager;
72 import android.os.PowerManager.WakeLock;
73 import android.os.Registrant;
74 import android.os.RegistrantList;
75 import android.os.ResultReceiver;
76 import android.os.UserHandle;
77 import android.preference.PreferenceManager;
78 import android.sysprop.TelephonyProperties;
79 import android.telecom.VideoProfile;
80 import android.telephony.AccessNetworkConstants;
81 import android.telephony.CarrierConfigManager;
82 import android.telephony.NetworkRegistrationInfo;
83 import android.telephony.PhoneNumberUtils;
84 import android.telephony.ServiceState;
85 import android.telephony.SubscriptionManager;
86 import android.telephony.TelephonyManager;
87 import android.telephony.UssdResponse;
88 import android.telephony.emergency.EmergencyNumber;
89 import android.telephony.ims.ImsCallForwardInfo;
90 import android.telephony.ims.ImsCallProfile;
91 import android.telephony.ims.ImsReasonInfo;
92 import android.telephony.ims.ImsRegistrationAttributes;
93 import android.telephony.ims.ImsSsData;
94 import android.telephony.ims.ImsSsInfo;
95 import android.telephony.ims.RegistrationManager;
96 import android.telephony.ims.feature.MmTelFeature;
97 import android.telephony.ims.stub.ImsRegistrationImplBase;
98 import android.telephony.ims.stub.ImsUtImplBase;
99 import android.text.TextUtils;
100 import android.util.LocalLog;
101 
102 import com.android.ims.ImsEcbm;
103 import com.android.ims.ImsEcbmStateListener;
104 import com.android.ims.ImsException;
105 import com.android.ims.ImsManager;
106 import com.android.ims.ImsUtInterface;
107 import com.android.internal.annotations.VisibleForTesting;
108 import com.android.internal.telephony.Call;
109 import com.android.internal.telephony.CallFailCause;
110 import com.android.internal.telephony.CallForwardInfo;
111 import com.android.internal.telephony.CallStateException;
112 import com.android.internal.telephony.CallTracker;
113 import com.android.internal.telephony.CarrierPrivilegesTracker;
114 import com.android.internal.telephony.CommandException;
115 import com.android.internal.telephony.CommandsInterface;
116 import com.android.internal.telephony.Connection;
117 import com.android.internal.telephony.GsmCdmaPhone;
118 import com.android.internal.telephony.MmiCode;
119 import com.android.internal.telephony.Phone;
120 import com.android.internal.telephony.PhoneConstants;
121 import com.android.internal.telephony.PhoneNotifier;
122 import com.android.internal.telephony.ServiceStateTracker;
123 import com.android.internal.telephony.TelephonyComponentFactory;
124 import com.android.internal.telephony.TelephonyIntents;
125 import com.android.internal.telephony.domainselection.DomainSelectionResolver;
126 import com.android.internal.telephony.emergency.EmergencyNumberTracker;
127 import com.android.internal.telephony.emergency.EmergencyStateTracker;
128 import com.android.internal.telephony.flags.FeatureFlags;
129 import com.android.internal.telephony.gsm.SuppServiceNotification;
130 import com.android.internal.telephony.metrics.ImsStats;
131 import com.android.internal.telephony.metrics.TelephonyMetrics;
132 import com.android.internal.telephony.metrics.VoiceCallSessionStats;
133 import com.android.internal.telephony.nano.TelephonyProto.ImsConnectionState;
134 import com.android.internal.telephony.subscription.SubscriptionInfoInternal;
135 import com.android.internal.telephony.uicc.IccRecords;
136 import com.android.internal.telephony.util.NotificationChannelController;
137 import com.android.internal.telephony.util.TelephonyUtils;
138 import com.android.internal.util.IndentingPrintWriter;
139 import com.android.telephony.Rlog;
140 
141 import java.io.FileDescriptor;
142 import java.io.PrintWriter;
143 import java.util.ArrayList;
144 import java.util.Arrays;
145 import java.util.List;
146 import java.util.concurrent.Executor;
147 import java.util.function.Consumer;
148 import java.util.stream.Stream;
149 
150 /**
151  * {@hide}
152  */
153 public class ImsPhone extends ImsPhoneBase {
154     private static final String LOG_TAG = "ImsPhone";
155     private static final boolean DBG = true;
156     private static final boolean VDBG = false; // STOPSHIP if true
157 
158     private static final int EVENT_SET_CALL_BARRING_DONE             = EVENT_LAST + 1;
159     private static final int EVENT_GET_CALL_BARRING_DONE             = EVENT_LAST + 2;
160     private static final int EVENT_SET_CALL_WAITING_DONE             = EVENT_LAST + 3;
161     private static final int EVENT_GET_CALL_WAITING_DONE             = EVENT_LAST + 4;
162     private static final int EVENT_SET_CLIR_DONE                     = EVENT_LAST + 5;
163     private static final int EVENT_GET_CLIR_DONE                     = EVENT_LAST + 6;
164     private static final int EVENT_DEFAULT_PHONE_DATA_STATE_CHANGED  = EVENT_LAST + 7;
165     @VisibleForTesting
166     public static final int EVENT_SERVICE_STATE_CHANGED             = EVENT_LAST + 8;
167     private static final int EVENT_VOICE_CALL_ENDED                  = EVENT_LAST + 9;
168     private static final int EVENT_INITIATE_VOLTE_SILENT_REDIAL      = EVENT_LAST + 10;
169     private static final int EVENT_GET_CLIP_DONE                     = EVENT_LAST + 11;
170 
171     static final int RESTART_ECM_TIMER = 0; // restart Ecm timer
172     static final int CANCEL_ECM_TIMER  = 1; // cancel Ecm timer
173 
174     // Default Emergency Callback Mode exit timer
175     private static final long DEFAULT_ECM_EXIT_TIMER_VALUE = 300000;
176 
177     // String to Call Composer Option Prefix set by user
178     private static final String PREF_USER_SET_CALL_COMPOSER_PREFIX = "userset_callcomposer_prefix";
179 
180     /**
181      * Used to create ImsManager instances, which may be injected during testing.
182      */
183     @VisibleForTesting
184     public interface ImsManagerFactory {
185         /**
186          * Create a new instance of ImsManager for the specified phoneId.
187          */
create(Context context, int phoneId)188         ImsManager create(Context context, int phoneId);
189     }
190 
191     public static class ImsDialArgs extends DialArgs {
192         public static class Builder extends DialArgs.Builder<ImsDialArgs.Builder> {
193             private android.telecom.Connection.RttTextStream mRttTextStream;
194             private int mRetryCallFailCause = ImsReasonInfo.CODE_UNSPECIFIED;
195             private int mRetryCallFailNetworkType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
196             private boolean mIsWpsCall = false;
197 
from(DialArgs dialArgs)198             public static ImsDialArgs.Builder from(DialArgs dialArgs) {
199                 if (dialArgs instanceof ImsDialArgs) {
200                     return new ImsDialArgs.Builder()
201                             .setUusInfo(dialArgs.uusInfo)
202                             .setIsEmergency(dialArgs.isEmergency)
203                             .setEccCategory(dialArgs.eccCategory)
204                             .setVideoState(dialArgs.videoState)
205                             .setIntentExtras(dialArgs.intentExtras)
206                             .setRttTextStream(((ImsDialArgs)dialArgs).rttTextStream)
207                             .setClirMode(dialArgs.clirMode)
208                             .setRetryCallFailCause(((ImsDialArgs)dialArgs).retryCallFailCause)
209                             .setRetryCallFailNetworkType(
210                                     ((ImsDialArgs)dialArgs).retryCallFailNetworkType)
211                             .setIsWpsCall(((ImsDialArgs)dialArgs).isWpsCall);
212                 }
213                 return new ImsDialArgs.Builder()
214                         .setUusInfo(dialArgs.uusInfo)
215                         .setIsEmergency(dialArgs.isEmergency)
216                         .setVideoState(dialArgs.videoState)
217                         .setClirMode(dialArgs.clirMode)
218                         .setIntentExtras(dialArgs.intentExtras);
219             }
220 
setRttTextStream( android.telecom.Connection.RttTextStream s)221             public ImsDialArgs.Builder setRttTextStream(
222                     android.telecom.Connection.RttTextStream s) {
223                 mRttTextStream = s;
224                 return this;
225             }
226 
setRetryCallFailCause(int retryCallFailCause)227             public ImsDialArgs.Builder setRetryCallFailCause(int retryCallFailCause) {
228                 this.mRetryCallFailCause = retryCallFailCause;
229                 return this;
230             }
231 
setRetryCallFailNetworkType(int retryCallFailNetworkType)232             public ImsDialArgs.Builder setRetryCallFailNetworkType(int retryCallFailNetworkType) {
233                 this.mRetryCallFailNetworkType = retryCallFailNetworkType;
234                 return this;
235             }
236 
setIsWpsCall(boolean isWpsCall)237             public ImsDialArgs.Builder setIsWpsCall(boolean isWpsCall) {
238                 this.mIsWpsCall = isWpsCall;
239                 return this;
240             }
241 
build()242             public ImsDialArgs build() {
243                 return new ImsDialArgs(this);
244             }
245         }
246 
247         /**
248          * The RTT text stream. If non-null, indicates that connection supports RTT
249          * communication with the in-call app.
250          */
251         public final android.telecom.Connection.RttTextStream rttTextStream;
252 
253         public final int retryCallFailCause;
254         public final int retryCallFailNetworkType;
255 
256         /** Indicates the call is Wireless Priority Service call */
257         public final boolean isWpsCall;
258 
ImsDialArgs(ImsDialArgs.Builder b)259         private ImsDialArgs(ImsDialArgs.Builder b) {
260             super(b);
261             this.rttTextStream = b.mRttTextStream;
262             this.retryCallFailCause = b.mRetryCallFailCause;
263             this.retryCallFailNetworkType = b.mRetryCallFailNetworkType;
264             this.isWpsCall = b.mIsWpsCall;
265         }
266     }
267 
268     /**
269      * Container to transfer IMS registration radio tech.
270      * This will be used as result value of AsyncResult to the handler that called
271      * {@link #registerForImsRegistrationChanges(Handler, int, Object)}
272      */
ImsRegistrationRadioTechInfo(int phoneId, int imsRegistrationTech, int imsRegistrationState)273     public record ImsRegistrationRadioTechInfo(int phoneId, int imsRegistrationTech,
274                                                 int imsRegistrationState) {}
275 
276     // Instance Variables
277     Phone mDefaultPhone;
278     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
279     ImsPhoneCallTracker mCT;
280     ImsExternalCallTracker mExternalCallTracker;
281     ImsNrSaModeHandler mImsNrSaModeHandler;
282     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
283     private ArrayList <ImsPhoneMmiCode> mPendingMMIs = new ArrayList<ImsPhoneMmiCode>();
284     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
285     private ServiceState mSS = new ServiceState();
286 
287     private final ImsManagerFactory mImsManagerFactory;
288 
289     private SharedPreferences mImsPhoneSharedPreferences;
290 
291     // To redial silently through GSM or CDMA when dialing through IMS fails
292     private String mLastDialString;
293 
294     private WakeLock mWakeLock;
295 
296     // mEcmExitRespRegistrant is informed after the phone has been exited the emergency
297     // callback mode keep track of if phone is in emergency callback mode
298     private Registrant mEcmExitRespRegistrant;
299 
300     private final RegistrantList mSilentRedialRegistrants = new RegistrantList();
301 
302     private final RegistrantList mImsRegistrationUpdateRegistrants = new RegistrantList();
303 
304     private final LocalLog mRegLocalLog = new LocalLog(64);
305     private TelephonyMetrics mMetrics;
306 
307     // The helper class to receive and store the MmTel registration status updated.
308     private ImsRegistrationCallbackHelper mImsMmTelRegistrationHelper;
309 
310     // The roaming state if currently in service, or the last roaming state when was in service.
311     private boolean mLastKnownRoamingState = false;
312 
313     private boolean mIsInImsEcm = false;
314 
315     // List of Registrants to send supplementary service notifications to.
316     private RegistrantList mSsnRegistrants = new RegistrantList();
317 
318     private ImsStats mImsStats;
319 
320     private int mImsRegistrationState;
321     // The access network type where IMS is registered
322     private @ImsRegistrationImplBase.ImsRegistrationTech int mImsRegistrationTech =
323             REGISTRATION_TECH_NONE;
324     private @RegistrationManager.SuggestedAction int mImsRegistrationSuggestedAction;
325     private @ImsRegistrationImplBase.ImsRegistrationTech int mImsDeregistrationTech =
326             REGISTRATION_TECH_NONE;
327     private @AccessNetworkConstants.TransportType int mTransportType = TRANSPORT_TYPE_INVALID;
328     private int mImsRegistrationCapabilities;
329     private boolean mNotifiedRegisteredState;
330 
331     // A runnable which is used to automatically exit from Ecm after a period of time.
332     private Runnable mExitEcmRunnable = new Runnable() {
333         @Override
334         public void run() {
335             exitEmergencyCallbackMode();
336         }
337     };
338 
339     private Uri[] mCurrentSubscriberUris;
340 
setCurrentSubscriberUris(Uri[] currentSubscriberUris)341     protected void setCurrentSubscriberUris(Uri[] currentSubscriberUris) {
342         this.mCurrentSubscriberUris = currentSubscriberUris;
343     }
344 
345     @Override
getCurrentSubscriberUris()346     public Uri[] getCurrentSubscriberUris() {
347         return mCurrentSubscriberUris;
348     }
349 
350     /** Set call composer status from users for the current subscription */
setCallComposerStatus(int status)351     public void setCallComposerStatus(int status) {
352         mImsPhoneSharedPreferences.edit().putInt(
353                 PREF_USER_SET_CALL_COMPOSER_PREFIX + getSubId(), status).commit();
354     }
355 
356     /** Get call composer status from users for the current subscription */
getCallComposerStatus()357     public int getCallComposerStatus() {
358         return mImsPhoneSharedPreferences.getInt(PREF_USER_SET_CALL_COMPOSER_PREFIX + getSubId(),
359                 TelephonyManager.CALL_COMPOSER_STATUS_OFF);
360     }
361 
362     @Override
getEmergencyNumberDbVersion()363     public int getEmergencyNumberDbVersion() {
364         return getEmergencyNumberTracker().getEmergencyNumberDbVersion();
365     }
366 
367     @Override
getEmergencyNumberTracker()368     public EmergencyNumberTracker getEmergencyNumberTracker() {
369         return mDefaultPhone.getEmergencyNumberTracker();
370     }
371 
372     @Override
getServiceStateTracker()373     public ServiceStateTracker getServiceStateTracker() {
374         return mDefaultPhone.getServiceStateTracker();
375     }
376 
377     // Create Cf (Call forward) so that dialling number &
378     // mIsCfu (true if reason is call forward unconditional)
379     // mOnComplete (Message object passed by client) can be packed &
380     // given as a single Cf object as user data to UtInterface.
381     private static class Cf {
382         final String mSetCfNumber;
383         final Message mOnComplete;
384         final boolean mIsCfu;
385 
386         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
Cf(String cfNumber, boolean isCfu, Message onComplete)387         Cf(String cfNumber, boolean isCfu, Message onComplete) {
388             mSetCfNumber = cfNumber;
389             mIsCfu = isCfu;
390             mOnComplete = onComplete;
391         }
392     }
393 
394     // Create SS (Supplementary Service) so that save SS params &
395     // mOnComplete (Message object passed by client) can be packed
396     // given as a single SS object as user data to UtInterface.
397     @VisibleForTesting
398     public static class SS {
399         int mCfAction;
400         int mCfReason;
401         String mDialingNumber;
402         int mTimerSeconds;
403         boolean mEnable;
404         int mClirMode;
405         String mFacility;
406         boolean mLockState;
407         String mPassword;
408         int mServiceClass;
409         @VisibleForTesting
410         public Message mOnComplete;
411 
412         // Default // Query CW, CLIR, CLIP
SS(Message onComplete)413         SS(Message onComplete) {
414             mOnComplete = onComplete;
415         }
416 
417         // Update CLIP
SS(boolean enable, Message onComplete)418         SS(boolean enable, Message onComplete) {
419             mEnable = enable;
420             mOnComplete = onComplete;
421         }
422 
423         // Update CLIR
SS(int clirMode, Message onComplete)424         SS(int clirMode, Message onComplete) {
425             mClirMode = clirMode;
426             mOnComplete = onComplete;
427         }
428 
429         // Update CW
SS(boolean enable, int serviceClass, Message onComplete)430         SS(boolean enable, int serviceClass, Message onComplete) {
431             mEnable = enable;
432             mServiceClass = serviceClass;
433             mOnComplete = onComplete;
434         }
435 
436         // Query CF
SS(int cfReason, int serviceClass, Message onComplete)437         SS(int cfReason, int serviceClass, Message onComplete) {
438             mCfReason = cfReason;
439             mServiceClass = serviceClass;
440             mOnComplete = onComplete;
441         }
442 
443         // Update CF
SS(int cfAction, int cfReason, String dialingNumber, int serviceClass, int timerSeconds, Message onComplete)444         SS(int cfAction, int cfReason, String dialingNumber,
445            int serviceClass, int timerSeconds, Message onComplete) {
446             mCfAction = cfAction;
447             mCfReason = cfReason;
448             mDialingNumber = dialingNumber;
449             mServiceClass = serviceClass;
450             mTimerSeconds = timerSeconds;
451             mOnComplete = onComplete;
452         }
453 
454         // Query CB
SS(String facility, String password, int serviceClass, Message onComplete)455         SS(String facility, String password, int serviceClass, Message onComplete) {
456             mFacility = facility;
457             mPassword = password;
458             mServiceClass = serviceClass;
459             mOnComplete = onComplete;
460         }
461 
462         // Update CB
SS(String facility, boolean lockState, String password, int serviceClass, Message onComplete)463         SS(String facility, boolean lockState, String password,
464                 int serviceClass, Message onComplete) {
465             mFacility = facility;
466             mLockState = lockState;
467             mPassword = password;
468             mServiceClass = serviceClass;
469             mOnComplete = onComplete;
470         }
471     }
472 
473     // Constructors
ImsPhone(Context context, PhoneNotifier notifier, Phone defaultPhone, FeatureFlags featureFlags)474     public ImsPhone(Context context, PhoneNotifier notifier,
475             Phone defaultPhone, FeatureFlags featureFlags) {
476         this(context, notifier, defaultPhone, ImsManager::getInstance, false, featureFlags);
477     }
478 
479     @VisibleForTesting
ImsPhone(Context context, PhoneNotifier notifier, Phone defaultPhone, ImsManagerFactory imsManagerFactory, boolean unitTestMode, FeatureFlags featureFlags)480     public ImsPhone(Context context, PhoneNotifier notifier, Phone defaultPhone,
481             ImsManagerFactory imsManagerFactory, boolean unitTestMode, FeatureFlags featureFlags) {
482         super("ImsPhone", context, notifier, unitTestMode, featureFlags);
483 
484         mDefaultPhone = defaultPhone;
485         mImsManagerFactory = imsManagerFactory;
486         mImsPhoneSharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
487         mImsStats = new ImsStats(this);
488         // The ImsExternalCallTracker needs to be defined before the ImsPhoneCallTracker, as the
489         // ImsPhoneCallTracker uses a thread to spool up the ImsManager.  Part of this involves
490         // setting the multiendpoint listener on the external call tracker.  So we need to ensure
491         // the external call tracker is available first to avoid potential timing issues.
492         mExternalCallTracker =
493                 TelephonyComponentFactory.getInstance()
494                         .inject(ImsExternalCallTracker.class.getName())
495                         .makeImsExternalCallTracker(this);
496         mImsNrSaModeHandler =
497                 TelephonyComponentFactory.getInstance()
498                         .inject(ImsNrSaModeHandler.class.getName())
499                         .makeImsNrSaModeHandler(this);
500         mCT = TelephonyComponentFactory.getInstance().inject(ImsPhoneCallTracker.class.getName())
501                 .makeImsPhoneCallTracker(this, featureFlags);
502         mCT.registerPhoneStateListener(mExternalCallTracker);
503         mExternalCallTracker.setCallPuller(mCT);
504 
505         mSS.setOutOfService(false);
506 
507         mPhoneId = mDefaultPhone.getPhoneId();
508 
509         mMetrics = TelephonyMetrics.getInstance();
510 
511         mImsMmTelRegistrationHelper = new ImsRegistrationCallbackHelper(mMmTelRegistrationUpdate,
512                 context.getMainExecutor());
513 
514         PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
515         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG);
516         mWakeLock.setReferenceCounted(false);
517 
518         if (mDefaultPhone.getServiceStateTracker() != null
519                 && mDefaultPhone.getAccessNetworksManager() != null) {
520             for (int transport : mDefaultPhone.getAccessNetworksManager()
521                     .getAvailableTransports()) {
522                 mDefaultPhone.getServiceStateTracker()
523                         .registerForDataRegStateOrRatChanged(transport, this,
524                                 EVENT_DEFAULT_PHONE_DATA_STATE_CHANGED, null);
525             }
526         }
527         // Sets the Voice reg state to STATE_OUT_OF_SERVICE and also queries the data service
528         // state. We don't ever need the voice reg state to be anything other than in or out of
529         // service.
530         setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
531 
532         mDefaultPhone.registerForServiceStateChanged(this, EVENT_SERVICE_STATE_CHANGED, null);
533         // Force initial roaming state update later, on EVENT_CARRIER_CONFIG_CHANGED.
534         // Settings provider or CarrierConfig may not be loaded now.
535 
536         mDefaultPhone.registerForVolteSilentRedial(this, EVENT_INITIATE_VOLTE_SILENT_REDIAL, null);
537     }
538 
539     //todo: get rid of this function. It is not needed since parentPhone obj never changes
540     @Override
dispose()541     public void dispose() {
542         logd("dispose");
543         // Nothing to dispose in Phone
544         //super.dispose();
545         mPendingMMIs.clear();
546         mExternalCallTracker.tearDown();
547         mImsNrSaModeHandler.tearDown();
548         mCT.unregisterPhoneStateListener(mExternalCallTracker);
549         mCT.unregisterForVoiceCallEnded(this);
550         mCT.dispose();
551 
552         //Force all referenced classes to unregister their former registered events
553         if (mDefaultPhone != null && mDefaultPhone.getServiceStateTracker() != null) {
554             for (int transport : mDefaultPhone.getAccessNetworksManager()
555                     .getAvailableTransports()) {
556                 mDefaultPhone.getServiceStateTracker()
557                         .unregisterForDataRegStateOrRatChanged(transport, this);
558             }
559             mDefaultPhone.unregisterForServiceStateChanged(this);
560         }
561 
562         if (mDefaultPhone != null) {
563             mDefaultPhone.unregisterForVolteSilentRedial(this);
564         }
565     }
566 
567     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
568     @Override
getServiceState()569     public ServiceState getServiceState() {
570         return new ServiceState(mSS);
571     }
572 
573     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
574     @VisibleForTesting
setServiceState(int state)575     public void setServiceState(int state) {
576         boolean isVoiceRegStateChanged = false;
577 
578         synchronized (this) {
579             isVoiceRegStateChanged = mSS.getState() != state;
580             mSS.setVoiceRegState(state);
581         }
582         updateDataServiceState();
583 
584         if (isVoiceRegStateChanged) {
585             if (mDefaultPhone.getServiceStateTracker() != null) {
586                 mDefaultPhone.getServiceStateTracker().onImsServiceStateChanged();
587             }
588         }
589     }
590 
591     @Override
getCallTracker()592     public CallTracker getCallTracker() {
593         return mCT;
594     }
595 
getExternalCallTracker()596     public ImsExternalCallTracker getExternalCallTracker() {
597         return mExternalCallTracker;
598     }
599 
600     @Override
601     public List<? extends ImsPhoneMmiCode>
getPendingMmiCodes()602     getPendingMmiCodes() {
603         return mPendingMMIs;
604     }
605 
606     @Override
607     public void
acceptCall(int videoState)608     acceptCall(int videoState) throws CallStateException {
609         mCT.acceptCall(videoState);
610     }
611 
612     @Override
613     public void
rejectCall()614     rejectCall() throws CallStateException {
615         mCT.rejectCall();
616     }
617 
618     @Override
619     public void
switchHoldingAndActive()620     switchHoldingAndActive() throws CallStateException {
621         throw new UnsupportedOperationException("Use hold() and unhold() instead.");
622     }
623 
624     @Override
canConference()625     public boolean canConference() {
626         return mCT.canConference();
627     }
628 
canDial()629     public boolean canDial() {
630         try {
631             mCT.checkForDialIssues();
632         } catch (CallStateException cse) {
633             return false;
634         }
635         return true;
636     }
637 
638     @Override
conference()639     public void conference() {
640         mCT.conference();
641     }
642 
643     @Override
clearDisconnected()644     public void clearDisconnected() {
645         mCT.clearDisconnected();
646     }
647 
648     @Override
canTransfer()649     public boolean canTransfer() {
650         return mCT.canTransfer();
651     }
652 
653     @Override
explicitCallTransfer()654     public void explicitCallTransfer() throws CallStateException {
655         mCT.explicitCallTransfer();
656     }
657 
658     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
659     @Override
660     public ImsPhoneCall
getForegroundCall()661     getForegroundCall() {
662         return mCT.mForegroundCall;
663     }
664 
665     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
666     @Override
667     public ImsPhoneCall
getBackgroundCall()668     getBackgroundCall() {
669         return mCT.mBackgroundCall;
670     }
671 
672     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
673     @Override
674     public ImsPhoneCall
getRingingCall()675     getRingingCall() {
676         return mCT.mRingingCall;
677     }
678 
679     @Override
isImsAvailable()680     public boolean isImsAvailable() {
681         return mCT.isImsServiceReady();
682     }
683 
684     @Override
getCarrierPrivilegesTracker()685     public CarrierPrivilegesTracker getCarrierPrivilegesTracker() {
686         return mDefaultPhone.getCarrierPrivilegesTracker();
687     }
688 
689     /**
690      * Hold the currently active call, possibly unholding a currently held call.
691      * @throws CallStateException
692      */
holdActiveCall()693     public void holdActiveCall() throws CallStateException {
694         mCT.holdActiveCall();
695     }
696 
697     /**
698      * Unhold the currently active call, possibly holding a currently active call.
699      * If the call tracker is already in the middle of a hold operation, this is a noop.
700      * @throws CallStateException
701      */
unholdHeldCall()702     public void unholdHeldCall() throws CallStateException {
703         mCT.unholdHeldCall();
704     }
705 
handleCallDeflectionIncallSupplementaryService( String dialString)706     private boolean handleCallDeflectionIncallSupplementaryService(
707             String dialString) {
708         if (dialString.length() > 1) {
709             return false;
710         }
711 
712         if (getRingingCall().getState() != ImsPhoneCall.State.IDLE) {
713             if (DBG) logd("MmiCode 0: rejectCall");
714             try {
715                 mCT.rejectCall();
716             } catch (CallStateException e) {
717                 if (DBG) Rlog.d(LOG_TAG, "reject failed", e);
718                 notifySuppServiceFailed(Phone.SuppService.REJECT);
719             }
720         } else if (getBackgroundCall().getState() != ImsPhoneCall.State.IDLE) {
721             if (DBG) logd("MmiCode 0: hangupWaitingOrBackground");
722             try {
723                 mCT.hangup(getBackgroundCall());
724             } catch (CallStateException e) {
725                 if (DBG) Rlog.d(LOG_TAG, "hangup failed", e);
726             }
727         }
728 
729         return true;
730     }
731 
sendUssdResponse(String ussdRequest, CharSequence message, int returnCode, ResultReceiver wrappedCallback)732     private void sendUssdResponse(String ussdRequest, CharSequence message, int returnCode,
733                                    ResultReceiver wrappedCallback) {
734         UssdResponse response = new UssdResponse(ussdRequest, message);
735         Bundle returnData = new Bundle();
736         returnData.putParcelable(TelephonyManager.USSD_RESPONSE, response);
737         wrappedCallback.send(returnCode, returnData);
738 
739     }
740 
741     @Override
handleUssdRequest(String ussdRequest, ResultReceiver wrappedCallback)742     public boolean handleUssdRequest(String ussdRequest, ResultReceiver wrappedCallback)
743             throws CallStateException {
744         if (mPendingMMIs.size() > 0) {
745             // There are MMI codes in progress; fail attempt now.
746             logi("handleUssdRequest: queue full: " + Rlog.pii(LOG_TAG, ussdRequest));
747             sendUssdResponse(ussdRequest, null, TelephonyManager.USSD_RETURN_FAILURE,
748                     wrappedCallback );
749             return true;
750         }
751         try {
752             dialInternal(ussdRequest, new ImsDialArgs.Builder().build(), wrappedCallback);
753         } catch (CallStateException cse) {
754             if (CS_FALLBACK.equals(cse.getMessage())) {
755                 throw cse;
756             } else {
757                 Rlog.w(LOG_TAG, "Could not execute USSD " + cse);
758                 sendUssdResponse(ussdRequest, null, TelephonyManager.USSD_RETURN_FAILURE,
759                         wrappedCallback);
760             }
761         } catch (Exception e) {
762             Rlog.w(LOG_TAG, "Could not execute USSD " + e);
763             sendUssdResponse(ussdRequest, null, TelephonyManager.USSD_RETURN_FAILURE,
764                     wrappedCallback);
765             return false;
766         }
767         return true;
768     }
769 
handleCallWaitingIncallSupplementaryService( String dialString)770     private boolean handleCallWaitingIncallSupplementaryService(
771             String dialString) {
772         int len = dialString.length();
773 
774         if (len > 2) {
775             return false;
776         }
777 
778         ImsPhoneCall call = getForegroundCall();
779 
780         try {
781             if (len > 1) {
782                 if (DBG) logd("not support 1X SEND");
783                 notifySuppServiceFailed(Phone.SuppService.HANGUP);
784             } else {
785                 if (call.getState() != ImsPhoneCall.State.IDLE) {
786                     if (DBG) logd("MmiCode 1: hangup foreground");
787                     mCT.hangup(call);
788                 } else {
789                     if (DBG) logd("MmiCode 1: holdActiveCallForWaitingCall");
790                     mCT.holdActiveCallForWaitingCall();
791                 }
792             }
793         } catch (CallStateException e) {
794             if (DBG) Rlog.d(LOG_TAG, "hangup failed", e);
795             notifySuppServiceFailed(Phone.SuppService.HANGUP);
796         }
797 
798         return true;
799     }
800 
handleCallHoldIncallSupplementaryService(String dialString)801     private boolean handleCallHoldIncallSupplementaryService(String dialString) {
802         int len = dialString.length();
803 
804         if (len > 2) {
805             return false;
806         }
807 
808         if (len > 1) {
809             if (DBG) logd("separate not supported");
810             notifySuppServiceFailed(Phone.SuppService.SEPARATE);
811         } else {
812             try {
813                 if (getRingingCall().getState() != ImsPhoneCall.State.IDLE) {
814                     if (DBG) logd("MmiCode 2: accept ringing call");
815                     if (mFeatureFlags.answerAudioOnlyWhenAnsweringViaMmiCode()) {
816                         mCT.acceptCall(VideoProfile.STATE_AUDIO_ONLY);
817                     } else {
818                         mCT.acceptCall(ImsCallProfile.CALL_TYPE_VOICE);
819                     }
820                 } else if (getBackgroundCall().getState() == ImsPhoneCall.State.HOLDING) {
821                     // If there's an active ongoing call as well, hold it and the background one
822                     // should automatically unhold. Otherwise just unhold the background call.
823                     if (getForegroundCall().getState() != ImsPhoneCall.State.IDLE) {
824                         if (DBG) logd("MmiCode 2: switch holding and active");
825                         mCT.holdActiveCall();
826                     } else {
827                         if (DBG) logd("MmiCode 2: unhold held call");
828                         mCT.unholdHeldCall();
829                     }
830                 } else if (getForegroundCall().getState() != ImsPhoneCall.State.IDLE) {
831                     if (DBG) logd("MmiCode 2: hold active call");
832                     mCT.holdActiveCall();
833                 }
834             } catch (CallStateException e) {
835                 if (DBG) Rlog.d(LOG_TAG, "switch failed", e);
836                 notifySuppServiceFailed(Phone.SuppService.SWITCH);
837             }
838         }
839 
840         return true;
841     }
842 
handleMultipartyIncallSupplementaryService( String dialString)843     private boolean handleMultipartyIncallSupplementaryService(
844             String dialString) {
845         if (dialString.length() > 1) {
846             return false;
847         }
848 
849         if (DBG) logd("MmiCode 3: merge calls");
850         conference();
851         return true;
852     }
853 
handleEctIncallSupplementaryService(String dialString)854     private boolean handleEctIncallSupplementaryService(String dialString) {
855         if (dialString.length() != 1) {
856             return false;
857         }
858 
859         if (DBG) logd("MmiCode 4: explicit call transfer");
860         try {
861             explicitCallTransfer();
862         } catch (CallStateException e) {
863             if (DBG) Rlog.d(LOG_TAG, "explicit call transfer failed", e);
864             notifySuppServiceFailed(Phone.SuppService.TRANSFER);
865         }
866         return true;
867     }
868 
handleCcbsIncallSupplementaryService(String dialString)869     private boolean handleCcbsIncallSupplementaryService(String dialString) {
870         if (dialString.length() > 1) {
871             return false;
872         }
873 
874         logi("MmiCode 5: CCBS not supported!");
875         // Treat it as an "unknown" service.
876         notifySuppServiceFailed(Phone.SuppService.UNKNOWN);
877         return true;
878     }
879 
notifySuppSvcNotification(SuppServiceNotification suppSvc)880     public void notifySuppSvcNotification(SuppServiceNotification suppSvc) {
881         logd("notifySuppSvcNotification: suppSvc = " + suppSvc);
882 
883         AsyncResult ar = new AsyncResult(null, suppSvc, null);
884         mSsnRegistrants.notifyRegistrants(ar);
885     }
886 
887     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
888     @Override
handleInCallMmiCommands(String dialString)889     public boolean handleInCallMmiCommands(String dialString) {
890         if (!isInCall()) {
891             return false;
892         }
893 
894         if (TextUtils.isEmpty(dialString)) {
895             return false;
896         }
897 
898         boolean result = false;
899         char ch = dialString.charAt(0);
900         switch (ch) {
901             case '0':
902                 result = handleCallDeflectionIncallSupplementaryService(
903                         dialString);
904                 break;
905             case '1':
906                 result = handleCallWaitingIncallSupplementaryService(
907                         dialString);
908                 break;
909             case '2':
910                 result = handleCallHoldIncallSupplementaryService(dialString);
911                 break;
912             case '3':
913                 result = handleMultipartyIncallSupplementaryService(dialString);
914                 break;
915             case '4':
916                 result = handleEctIncallSupplementaryService(dialString);
917                 break;
918             case '5':
919                 result = handleCcbsIncallSupplementaryService(dialString);
920                 break;
921             default:
922                 break;
923         }
924 
925         return result;
926     }
927 
isInCall()928     boolean isInCall() {
929         ImsPhoneCall.State foregroundCallState = getForegroundCall().getState();
930         ImsPhoneCall.State backgroundCallState = getBackgroundCall().getState();
931         ImsPhoneCall.State ringingCallState = getRingingCall().getState();
932 
933        return (foregroundCallState.isAlive() ||
934                backgroundCallState.isAlive() ||
935                ringingCallState.isAlive());
936     }
937 
938     @Override
isInImsEcm()939     public boolean isInImsEcm() {
940         if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) {
941             return EmergencyStateTracker.getInstance().isInImsEcm();
942         }
943         return mIsInImsEcm;
944     }
945 
946     @Override
isInEcm()947     public boolean isInEcm() {
948         return mDefaultPhone.isInEcm();
949     }
950 
951     @Override
setIsInEcm(boolean isInEcm)952     public void setIsInEcm(boolean isInEcm){
953         mIsInImsEcm = isInEcm;
954         mDefaultPhone.setIsInEcm(isInEcm);
955     }
956 
notifyNewRingingConnection(Connection c)957     public void notifyNewRingingConnection(Connection c) {
958         mDefaultPhone.notifyNewRingingConnectionP(c);
959     }
960 
961     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
notifyUnknownConnection(Connection c)962     void notifyUnknownConnection(Connection c) {
963         mDefaultPhone.notifyUnknownConnectionP(c);
964     }
965 
966     @Override
notifyForVideoCapabilityChanged(boolean isVideoCapable)967     public void notifyForVideoCapabilityChanged(boolean isVideoCapable) {
968         mIsVideoCapable = isVideoCapable;
969         mDefaultPhone.notifyForVideoCapabilityChanged(isVideoCapable);
970     }
971 
972     @Override
setRadioPower(boolean on, boolean forEmergencyCall, boolean isSelectedPhoneForEmergencyCall, boolean forceApply)973     public void setRadioPower(boolean on, boolean forEmergencyCall,
974             boolean isSelectedPhoneForEmergencyCall, boolean forceApply) {
975         mDefaultPhone.setRadioPower(on, forEmergencyCall, isSelectedPhoneForEmergencyCall,
976                 forceApply);
977     }
978 
979     @Override
startConference(String[] participantsToDial, DialArgs dialArgs)980     public Connection startConference(String[] participantsToDial, DialArgs dialArgs)
981             throws CallStateException {
982          ImsDialArgs.Builder imsDialArgsBuilder;
983          imsDialArgsBuilder = ImsDialArgs.Builder.from(dialArgs);
984          return mCT.startConference(participantsToDial, imsDialArgsBuilder.build());
985     }
986 
987     @Override
dial(String dialString, DialArgs dialArgs, Consumer<Phone> chosenPhoneConsumer)988     public Connection dial(String dialString, DialArgs dialArgs,
989             Consumer<Phone> chosenPhoneConsumer) throws CallStateException {
990         chosenPhoneConsumer.accept(this);
991         return dialInternal(dialString, dialArgs, null);
992     }
993 
dialInternal(String dialString, DialArgs dialArgs, ResultReceiver wrappedCallback)994     private Connection dialInternal(String dialString, DialArgs dialArgs,
995                                     ResultReceiver wrappedCallback)
996             throws CallStateException {
997 
998         mLastDialString = dialString;
999 
1000         // Need to make sure dialString gets parsed properly
1001         String newDialString = PhoneNumberUtils.stripSeparators(dialString);
1002 
1003         if (mFeatureFlags.skipMmiCodeCheckForEmergencyCall()) {
1004             // If not emergency number, handle in-call MMI first if applicable
1005             if (!dialArgs.isEmergency && handleInCallMmiCommands(newDialString)) {
1006                 return null;
1007             }
1008         } else {
1009             // handle in-call MMI first if applicable
1010             if (handleInCallMmiCommands(newDialString)) {
1011                 return null;
1012             }
1013         }
1014 
1015         ImsDialArgs.Builder imsDialArgsBuilder;
1016         imsDialArgsBuilder = ImsDialArgs.Builder.from(dialArgs);
1017         // Get the CLIR info if needed
1018         imsDialArgsBuilder.setClirMode(mCT.getClirMode());
1019 
1020         if (mDefaultPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
1021             return mCT.dial(dialString, imsDialArgsBuilder.build());
1022         }
1023 
1024         if (mFeatureFlags.skipMmiCodeCheckForEmergencyCall()) {
1025             // Skip to check mmi code if outgoing call is emergency
1026             if (dialArgs.isEmergency) {
1027                 logd("dialInternal: emergency number, skip to check mmi code");
1028                 return mCT.dial(dialString, imsDialArgsBuilder.build());
1029             }
1030         }
1031 
1032         // Only look at the Network portion for mmi
1033         String networkPortion = PhoneNumberUtils.extractNetworkPortionAlt(newDialString);
1034         ImsPhoneMmiCode mmi =  ImsPhoneMmiCode.newFromDialString(networkPortion, this,
1035                 wrappedCallback, mFeatureFlags);
1036         if (DBG) logd("dialInternal: dialing w/ mmi '" + mmi + "'...");
1037 
1038         if (mmi == null) {
1039             return mCT.dial(dialString, imsDialArgsBuilder.build());
1040         } else if (mmi.isTemporaryModeCLIR()) {
1041             imsDialArgsBuilder.setClirMode(mmi.getCLIRMode());
1042             return mCT.dial(mmi.getDialingNumber(), imsDialArgsBuilder.build());
1043         } else if (!mmi.isSupportedOverImsPhone()) {
1044             // If the mmi is not supported by IMS service,
1045             // try to initiate dialing with default phone
1046             // Note: This code is never reached; there is a bug in isSupportedOverImsPhone which
1047             // causes it to return true even though the "processCode" method ultimately throws the
1048             // exception.
1049             logi("dialInternal: USSD not supported by IMS; fallback to CS.");
1050             throw new CallStateException(CS_FALLBACK);
1051         } else {
1052             mPendingMMIs.add(mmi);
1053             mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));
1054 
1055             try {
1056                 mmi.processCode();
1057             } catch (CallStateException cse) {
1058                 if (CS_FALLBACK.equals(cse.getMessage())) {
1059                     logi("dialInternal: fallback to GSM required.");
1060                     // Make sure we remove from the list of pending MMIs since it will handover to
1061                     // GSM.
1062                     mPendingMMIs.remove(mmi);
1063                     throw cse;
1064                 }
1065             }
1066 
1067             return null;
1068         }
1069     }
1070 
1071     @Override
1072     public void
sendDtmf(char c)1073     sendDtmf(char c) {
1074         if (!PhoneNumberUtils.is12Key(c)) {
1075             loge("sendDtmf called with invalid character '" + c + "'");
1076         } else {
1077             if (mCT.getState() ==  PhoneConstants.State.OFFHOOK) {
1078                 mCT.sendDtmf(c, null);
1079             }
1080         }
1081     }
1082 
1083     @Override
1084     public void
startDtmf(char c)1085     startDtmf(char c) {
1086         if (!(PhoneNumberUtils.is12Key(c) || (c >= 'A' && c <= 'D'))) {
1087             loge("startDtmf called with invalid character '" + c + "'");
1088         } else {
1089             mCT.startDtmf(c);
1090         }
1091     }
1092 
1093     @Override
1094     public void
stopDtmf()1095     stopDtmf() {
1096         mCT.stopDtmf();
1097     }
1098 
notifyIncomingRing()1099     public void notifyIncomingRing() {
1100         if (DBG) logd("notifyIncomingRing");
1101         AsyncResult ar = new AsyncResult(null, null, null);
1102         sendMessage(obtainMessage(EVENT_CALL_RING, ar));
1103     }
1104 
1105     @Override
setMute(boolean muted)1106     public void setMute(boolean muted) {
1107         mCT.setMute(muted);
1108     }
1109 
1110     @Override
setTTYMode(int ttyMode, Message onComplete)1111     public void setTTYMode(int ttyMode, Message onComplete) {
1112         mCT.setTtyMode(ttyMode);
1113     }
1114 
1115     @Override
setUiTTYMode(int uiTtyMode, Message onComplete)1116     public void setUiTTYMode(int uiTtyMode, Message onComplete) {
1117         mCT.setUiTTYMode(uiTtyMode, onComplete);
1118     }
1119 
1120     @Override
getMute()1121     public boolean getMute() {
1122         return mCT.getMute();
1123     }
1124 
1125     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
1126     @Override
getState()1127     public PhoneConstants.State getState() {
1128         return mCT.getState();
1129     }
1130 
1131     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
isValidCommandInterfaceCFReason(int commandInterfaceCFReason)1132     private boolean isValidCommandInterfaceCFReason (int commandInterfaceCFReason) {
1133         switch (commandInterfaceCFReason) {
1134         case CF_REASON_UNCONDITIONAL:
1135         case CF_REASON_BUSY:
1136         case CF_REASON_NO_REPLY:
1137         case CF_REASON_NOT_REACHABLE:
1138         case CF_REASON_ALL:
1139         case CF_REASON_ALL_CONDITIONAL:
1140             return true;
1141         default:
1142             return false;
1143         }
1144     }
1145 
1146     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
isValidCommandInterfaceCFAction(int commandInterfaceCFAction)1147     private boolean isValidCommandInterfaceCFAction (int commandInterfaceCFAction) {
1148         switch (commandInterfaceCFAction) {
1149         case CF_ACTION_DISABLE:
1150         case CF_ACTION_ENABLE:
1151         case CF_ACTION_REGISTRATION:
1152         case CF_ACTION_ERASURE:
1153             return true;
1154         default:
1155             return false;
1156         }
1157     }
1158 
1159     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
isCfEnable(int action)1160     private  boolean isCfEnable(int action) {
1161         return (action == CF_ACTION_ENABLE) || (action == CF_ACTION_REGISTRATION);
1162     }
1163 
1164     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
getConditionFromCFReason(int reason)1165     private int getConditionFromCFReason(int reason) {
1166         switch(reason) {
1167             case CF_REASON_UNCONDITIONAL: return ImsUtInterface.CDIV_CF_UNCONDITIONAL;
1168             case CF_REASON_BUSY: return ImsUtInterface.CDIV_CF_BUSY;
1169             case CF_REASON_NO_REPLY: return ImsUtInterface.CDIV_CF_NO_REPLY;
1170             case CF_REASON_NOT_REACHABLE: return ImsUtInterface.CDIV_CF_NOT_REACHABLE;
1171             case CF_REASON_ALL: return ImsUtInterface.CDIV_CF_ALL;
1172             case CF_REASON_ALL_CONDITIONAL: return ImsUtInterface.CDIV_CF_ALL_CONDITIONAL;
1173             default:
1174                 break;
1175         }
1176 
1177         return ImsUtInterface.INVALID;
1178     }
1179 
getCFReasonFromCondition(int condition)1180     private int getCFReasonFromCondition(int condition) {
1181         switch(condition) {
1182             case ImsUtInterface.CDIV_CF_UNCONDITIONAL: return CF_REASON_UNCONDITIONAL;
1183             case ImsUtInterface.CDIV_CF_BUSY: return CF_REASON_BUSY;
1184             case ImsUtInterface.CDIV_CF_NO_REPLY: return CF_REASON_NO_REPLY;
1185             case ImsUtInterface.CDIV_CF_NOT_REACHABLE: return CF_REASON_NOT_REACHABLE;
1186             case ImsUtInterface.CDIV_CF_ALL: return CF_REASON_ALL;
1187             case ImsUtInterface.CDIV_CF_ALL_CONDITIONAL: return CF_REASON_ALL_CONDITIONAL;
1188             default:
1189                 break;
1190         }
1191 
1192         return CF_REASON_NOT_REACHABLE;
1193     }
1194 
1195     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
getActionFromCFAction(int action)1196     private int getActionFromCFAction(int action) {
1197         switch(action) {
1198             case CF_ACTION_DISABLE: return ImsUtInterface.ACTION_DEACTIVATION;
1199             case CF_ACTION_ENABLE: return ImsUtInterface.ACTION_ACTIVATION;
1200             case CF_ACTION_ERASURE: return ImsUtInterface.ACTION_ERASURE;
1201             case CF_ACTION_REGISTRATION: return ImsUtInterface.ACTION_REGISTRATION;
1202             default:
1203                 break;
1204         }
1205 
1206         return ImsUtInterface.INVALID;
1207     }
1208 
1209     @Override
getOutgoingCallerIdDisplay(Message onComplete)1210     public void getOutgoingCallerIdDisplay(Message onComplete) {
1211         if (DBG) logd("getCLIR");
1212         Message resp;
1213         SS ss = new SS(onComplete);
1214         resp = obtainMessage(EVENT_GET_CLIR_DONE, ss);
1215 
1216         try {
1217             ImsUtInterface ut = mCT.getUtInterface();
1218             ut.queryCLIR(resp);
1219         } catch (ImsException e) {
1220             sendErrorResponse(onComplete, e);
1221         }
1222     }
1223 
1224     @Override
setOutgoingCallerIdDisplay(int clirMode, Message onComplete)1225     public void setOutgoingCallerIdDisplay(int clirMode, Message onComplete) {
1226         if (DBG) logd("setCLIR action= " + clirMode);
1227         Message resp;
1228         // Packing CLIR value in the message. This will be required for
1229         // SharedPreference caching, if the message comes back as part of
1230         // a success response.
1231         SS ss = new SS(clirMode, onComplete);
1232         resp = obtainMessage(EVENT_SET_CLIR_DONE, ss);
1233         try {
1234             ImsUtInterface ut = mCT.getUtInterface();
1235             ut.updateCLIR(clirMode, resp);
1236         } catch (ImsException e) {
1237             sendErrorResponse(onComplete, e);
1238         }
1239     }
1240 
1241     @Override
queryCLIP(Message onComplete)1242     public void queryCLIP(Message onComplete) {
1243         Message resp;
1244         SS ss = new SS(onComplete);
1245         resp = obtainMessage(EVENT_GET_CLIP_DONE, ss);
1246 
1247         try {
1248             Rlog.d(LOG_TAG, "ut.queryCLIP");
1249             ImsUtInterface ut = mCT.getUtInterface();
1250             ut.queryCLIP(resp);
1251         } catch (ImsException e) {
1252             sendErrorResponse(onComplete, e);
1253         }
1254     }
1255 
1256     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
1257     @Override
getCallForwardingOption(int commandInterfaceCFReason, Message onComplete)1258     public void getCallForwardingOption(int commandInterfaceCFReason,
1259             Message onComplete) {
1260         getCallForwardingOption(commandInterfaceCFReason,
1261                 SERVICE_CLASS_VOICE, onComplete);
1262     }
1263 
1264     @Override
getCallForwardingOption(int commandInterfaceCFReason, int serviceClass, Message onComplete)1265     public void getCallForwardingOption(int commandInterfaceCFReason, int serviceClass,
1266             Message onComplete) {
1267         if (DBG) logd("getCallForwardingOption reason=" + commandInterfaceCFReason);
1268         if (isValidCommandInterfaceCFReason(commandInterfaceCFReason)) {
1269             if (DBG) logd("requesting call forwarding query.");
1270             Message resp;
1271             SS ss = new SS(commandInterfaceCFReason, serviceClass, onComplete);
1272             resp = obtainMessage(EVENT_GET_CALL_FORWARD_DONE, ss);
1273 
1274             try {
1275                 ImsUtInterface ut = mCT.getUtInterface();
1276                 ut.queryCallForward(getConditionFromCFReason(commandInterfaceCFReason), null, resp);
1277             } catch (ImsException e) {
1278                 sendErrorResponse(onComplete, e);
1279             }
1280         } else if (onComplete != null) {
1281             sendErrorResponse(onComplete);
1282         }
1283     }
1284 
1285     @Override
setCallForwardingOption(int commandInterfaceCFAction, int commandInterfaceCFReason, String dialingNumber, int timerSeconds, Message onComplete)1286     public void setCallForwardingOption(int commandInterfaceCFAction,
1287             int commandInterfaceCFReason,
1288             String dialingNumber,
1289             int timerSeconds,
1290             Message onComplete) {
1291         setCallForwardingOption(commandInterfaceCFAction, commandInterfaceCFReason, dialingNumber,
1292                 CommandsInterface.SERVICE_CLASS_VOICE, timerSeconds, onComplete);
1293     }
1294 
1295     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
1296     @Override
setCallForwardingOption(int commandInterfaceCFAction, int commandInterfaceCFReason, String dialingNumber, int serviceClass, int timerSeconds, Message onComplete)1297     public void setCallForwardingOption(int commandInterfaceCFAction,
1298             int commandInterfaceCFReason,
1299             String dialingNumber,
1300             int serviceClass,
1301             int timerSeconds,
1302             Message onComplete) {
1303         if (DBG) {
1304             logd("setCallForwardingOption action=" + commandInterfaceCFAction
1305                     + ", reason=" + commandInterfaceCFReason + " serviceClass=" + serviceClass);
1306         }
1307         if ((isValidCommandInterfaceCFAction(commandInterfaceCFAction)) &&
1308                 (isValidCommandInterfaceCFReason(commandInterfaceCFReason))) {
1309             Message resp;
1310             SS ss = new SS(commandInterfaceCFAction, commandInterfaceCFReason,
1311                     dialingNumber, serviceClass, timerSeconds, onComplete);
1312             resp = obtainMessage(EVENT_SET_CALL_FORWARD_DONE, ss);
1313 
1314             try {
1315                 ImsUtInterface ut = mCT.getUtInterface();
1316                 ut.updateCallForward(getActionFromCFAction(commandInterfaceCFAction),
1317                         getConditionFromCFReason(commandInterfaceCFReason),
1318                         dialingNumber,
1319                         serviceClass,
1320                         timerSeconds,
1321                         resp);
1322             } catch (ImsException e) {
1323                 sendErrorResponse(onComplete, e);
1324             }
1325         } else if (onComplete != null) {
1326             sendErrorResponse(onComplete);
1327         }
1328     }
1329 
1330     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
1331     @Override
getCallWaiting(Message onComplete)1332     public void getCallWaiting(Message onComplete) {
1333         if (DBG) logd("getCallWaiting");
1334         Message resp;
1335         SS ss = new SS(onComplete);
1336         resp = obtainMessage(EVENT_GET_CALL_WAITING_DONE, ss);
1337 
1338         try {
1339             ImsUtInterface ut = mCT.getUtInterface();
1340             ut.queryCallWaiting(resp);
1341         } catch (ImsException e) {
1342             sendErrorResponse(onComplete, e);
1343         }
1344     }
1345 
1346     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
1347     @Override
setCallWaiting(boolean enable, Message onComplete)1348     public void setCallWaiting(boolean enable, Message onComplete) {
1349         int serviceClass = CommandsInterface.SERVICE_CLASS_VOICE;
1350         CarrierConfigManager configManager = (CarrierConfigManager)
1351                 getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
1352         PersistableBundle b = configManager.getConfigForSubId(getSubId());
1353         if (b != null) {
1354             serviceClass = b.getInt(CarrierConfigManager.KEY_CALL_WAITING_SERVICE_CLASS_INT,
1355                     CommandsInterface.SERVICE_CLASS_VOICE);
1356         }
1357         setCallWaiting(enable, serviceClass, onComplete);
1358     }
1359 
setCallWaiting(boolean enable, int serviceClass, Message onComplete)1360     public void setCallWaiting(boolean enable, int serviceClass, Message onComplete) {
1361         if (DBG) logd("setCallWaiting enable=" + enable);
1362         Message resp;
1363         SS ss = new SS(enable, serviceClass, onComplete);
1364         resp = obtainMessage(EVENT_SET_CALL_WAITING_DONE, ss);
1365 
1366         try {
1367             ImsUtInterface ut = mCT.getUtInterface();
1368             ut.updateCallWaiting(enable, serviceClass, resp);
1369         } catch (ImsException e) {
1370             sendErrorResponse(onComplete, e);
1371         }
1372     }
1373 
getCBTypeFromFacility(String facility)1374     private int getCBTypeFromFacility(String facility) {
1375         if (CB_FACILITY_BAOC.equals(facility)) {
1376             return ImsUtImplBase.CALL_BARRING_ALL_OUTGOING;
1377         } else if (CB_FACILITY_BAOIC.equals(facility)) {
1378             return ImsUtImplBase.CALL_BARRING_OUTGOING_INTL;
1379         } else if (CB_FACILITY_BAOICxH.equals(facility)) {
1380             return ImsUtImplBase.CALL_BARRING_OUTGOING_INTL_EXCL_HOME;
1381         } else if (CB_FACILITY_BAIC.equals(facility)) {
1382             return ImsUtImplBase.CALL_BARRING_ALL_INCOMING;
1383         } else if (CB_FACILITY_BAICr.equals(facility)) {
1384             return ImsUtImplBase.CALL_BLOCKING_INCOMING_WHEN_ROAMING;
1385         } else if (CB_FACILITY_BA_ALL.equals(facility)) {
1386             return ImsUtImplBase.CALL_BARRING_ALL;
1387         } else if (CB_FACILITY_BA_MO.equals(facility)) {
1388             return ImsUtImplBase.CALL_BARRING_OUTGOING_ALL_SERVICES;
1389         } else if (CB_FACILITY_BA_MT.equals(facility)) {
1390             return ImsUtImplBase.CALL_BARRING_INCOMING_ALL_SERVICES;
1391         } else if (CB_FACILITY_BIC_ACR.equals(facility)) {
1392             return ImsUtImplBase.CALL_BARRING_ANONYMOUS_INCOMING;
1393         }
1394 
1395         return 0;
1396     }
1397 
getCallBarring(String facility, Message onComplete)1398     public void getCallBarring(String facility, Message onComplete) {
1399         getCallBarring(facility, onComplete, CommandsInterface.SERVICE_CLASS_VOICE);
1400     }
1401 
getCallBarring(String facility, Message onComplete, int serviceClass)1402     public void getCallBarring(String facility, Message onComplete, int serviceClass) {
1403         getCallBarring(facility, "", onComplete, serviceClass);
1404     }
1405 
1406     @Override
getCallBarring(String facility, String password, Message onComplete, int serviceClass)1407     public void getCallBarring(String facility, String password, Message onComplete,
1408             int serviceClass) {
1409         if (DBG) logd("getCallBarring facility=" + facility + ", serviceClass = " + serviceClass);
1410         Message resp;
1411         SS ss = new SS(facility, password, serviceClass, onComplete);
1412         resp = obtainMessage(EVENT_GET_CALL_BARRING_DONE, ss);
1413 
1414         try {
1415             ImsUtInterface ut = mCT.getUtInterface();
1416             // password is not required with Ut interface
1417             ut.queryCallBarring(getCBTypeFromFacility(facility), resp, serviceClass);
1418         } catch (ImsException e) {
1419             sendErrorResponse(onComplete, e);
1420         }
1421     }
1422 
setCallBarring(String facility, boolean lockState, String password, Message onComplete)1423     public void setCallBarring(String facility, boolean lockState, String password,
1424             Message onComplete) {
1425         setCallBarring(facility, lockState, password, onComplete,
1426                 CommandsInterface.SERVICE_CLASS_VOICE);
1427     }
1428 
1429     @Override
setCallBarring(String facility, boolean lockState, String password, Message onComplete, int serviceClass)1430     public void setCallBarring(String facility, boolean lockState, String password,
1431             Message onComplete, int serviceClass) {
1432         if (DBG) {
1433             logd("setCallBarring facility=" + facility
1434                     + ", lockState=" + lockState + ", serviceClass = " + serviceClass);
1435         }
1436         Message resp;
1437         SS ss = new SS(facility, lockState, password, serviceClass, onComplete);
1438         resp = obtainMessage(EVENT_SET_CALL_BARRING_DONE, ss);
1439 
1440         int action;
1441         if (lockState) {
1442             action = CommandsInterface.CF_ACTION_ENABLE;
1443         }
1444         else {
1445             action = CommandsInterface.CF_ACTION_DISABLE;
1446         }
1447 
1448         try {
1449             ImsUtInterface ut = mCT.getUtInterface();
1450             ut.updateCallBarring(getCBTypeFromFacility(facility), action,
1451                     resp, null, serviceClass, password);
1452         } catch (ImsException e) {
1453             sendErrorResponse(onComplete, e);
1454         }
1455     }
1456 
1457     @Override
sendUssdResponse(String ussdMessage)1458     public void sendUssdResponse(String ussdMessage) {
1459         logd("sendUssdResponse");
1460         ImsPhoneMmiCode mmi = ImsPhoneMmiCode.newFromUssdUserInput(ussdMessage, this,
1461                 mFeatureFlags);
1462         mPendingMMIs.add(mmi);
1463         mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));
1464         mmi.sendUssd(ussdMessage);
1465     }
1466 
sendUSSD(String ussdString, Message response)1467     public void sendUSSD(String ussdString, Message response) {
1468         Rlog.d(LOG_TAG, "sendUssd ussdString = " + ussdString);
1469         mLastDialString = ussdString;
1470         mCT.sendUSSD(ussdString, response);
1471     }
1472 
1473     @Override
cancelUSSD(Message msg)1474     public void cancelUSSD(Message msg) {
1475         mCT.cancelUSSD(msg);
1476     }
1477 
1478     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
sendErrorResponse(Message onComplete)1479     private void sendErrorResponse(Message onComplete) {
1480         logd("sendErrorResponse");
1481         if (onComplete != null) {
1482             AsyncResult.forMessage(onComplete, null,
1483                     new CommandException(CommandException.Error.GENERIC_FAILURE));
1484             onComplete.sendToTarget();
1485         }
1486     }
1487 
1488     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
1489     @VisibleForTesting
sendErrorResponse(Message onComplete, Throwable e)1490     public void sendErrorResponse(Message onComplete, Throwable e) {
1491         logd("sendErrorResponse");
1492         if (onComplete != null) {
1493             AsyncResult.forMessage(onComplete, null, getCommandException(e));
1494             onComplete.sendToTarget();
1495         }
1496     }
1497 
getCommandException(int code, String errorString)1498     private CommandException getCommandException(int code, String errorString) {
1499         logd("getCommandException code= " + code + ", errorString= " + errorString);
1500         CommandException.Error error = CommandException.Error.GENERIC_FAILURE;
1501 
1502         switch(code) {
1503             case ImsReasonInfo.CODE_UT_NOT_SUPPORTED:
1504                 // fall through
1505             case ImsReasonInfo.CODE_UT_OPERATION_NOT_ALLOWED:
1506                 // not allowed is reported by operators when the network doesn't support a specific
1507                 // type of barring.
1508                 error = CommandException.Error.REQUEST_NOT_SUPPORTED;
1509                 break;
1510             case ImsReasonInfo.CODE_UT_CB_PASSWORD_MISMATCH:
1511                 error = CommandException.Error.PASSWORD_INCORRECT;
1512                 break;
1513             case ImsReasonInfo.CODE_UT_SERVICE_UNAVAILABLE:
1514                 error = CommandException.Error.RADIO_NOT_AVAILABLE;
1515                 break;
1516             case ImsReasonInfo.CODE_FDN_BLOCKED:
1517                 error = CommandException.Error.FDN_CHECK_FAILURE;
1518                 break;
1519             case ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_DIAL:
1520                 error = CommandException.Error.SS_MODIFIED_TO_DIAL;
1521                 break;
1522             case ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_USSD:
1523                 error = CommandException.Error.SS_MODIFIED_TO_USSD;
1524                 break;
1525             case ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_SS:
1526                 error = CommandException.Error.SS_MODIFIED_TO_SS;
1527                 break;
1528             case ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO:
1529                 error = CommandException.Error.SS_MODIFIED_TO_DIAL_VIDEO;
1530                 break;
1531             default:
1532                 break;
1533         }
1534 
1535         return new CommandException(error, errorString);
1536     }
1537 
getCommandException(Throwable e)1538     private CommandException getCommandException(Throwable e) {
1539         CommandException ex = null;
1540 
1541         if (e instanceof ImsException) {
1542             ex = getCommandException(((ImsException)e).getCode(), e.getMessage());
1543         } else {
1544             logd("getCommandException generic failure");
1545             ex = new CommandException(CommandException.Error.GENERIC_FAILURE);
1546         }
1547         return ex;
1548     }
1549 
1550     private void
onNetworkInitiatedUssd(ImsPhoneMmiCode mmi)1551     onNetworkInitiatedUssd(ImsPhoneMmiCode mmi) {
1552         logd("onNetworkInitiatedUssd");
1553         mMmiCompleteRegistrants.notifyRegistrants(
1554             new AsyncResult(null, mmi, null));
1555     }
1556 
1557     /* package */
onIncomingUSSD(int ussdMode, String ussdMessage)1558     void onIncomingUSSD(int ussdMode, String ussdMessage) {
1559         if (DBG) logd("onIncomingUSSD ussdMode=" + ussdMode);
1560 
1561         boolean isUssdError;
1562         boolean isUssdRequest;
1563 
1564         isUssdRequest
1565             = (ussdMode == CommandsInterface.USSD_MODE_REQUEST);
1566 
1567         isUssdError
1568             = (ussdMode != CommandsInterface.USSD_MODE_NOTIFY
1569                 && ussdMode != CommandsInterface.USSD_MODE_REQUEST);
1570 
1571         ImsPhoneMmiCode found = null;
1572         for (int i = 0, s = mPendingMMIs.size() ; i < s; i++) {
1573             if(mPendingMMIs.get(i).isPendingUSSD()) {
1574                 found = mPendingMMIs.get(i);
1575                 break;
1576             }
1577         }
1578 
1579         if (found != null) {
1580             // Complete pending USSD
1581             if (isUssdError) {
1582                 found.onUssdFinishedError();
1583             } else {
1584                 found.onUssdFinished(ussdMessage, isUssdRequest);
1585             }
1586         } else if (!isUssdError && !TextUtils.isEmpty(ussdMessage)) {
1587                 // pending USSD not found
1588                 // The network may initiate its own USSD request
1589 
1590                 // ignore everything that isnt a Notify or a Request
1591                 // also, discard if there is no message to present
1592                 ImsPhoneMmiCode mmi;
1593                 mmi = ImsPhoneMmiCode.newNetworkInitiatedUssd(ussdMessage,
1594                         isUssdRequest, this, mFeatureFlags);
1595                 onNetworkInitiatedUssd(mmi);
1596         } else if (isUssdError) {
1597             ImsPhoneMmiCode mmi;
1598             mmi = ImsPhoneMmiCode.newNetworkInitiatedUssd(ussdMessage,
1599                     true, this, mFeatureFlags);
1600             mmi.onUssdFinishedError();
1601         }
1602     }
1603 
1604     /**
1605      * Removes the given MMI from the pending list and notifies
1606      * registrants that it is complete.
1607      * @param mmi MMI that is done
1608      */
1609     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
onMMIDone(ImsPhoneMmiCode mmi)1610     public void onMMIDone(ImsPhoneMmiCode mmi) {
1611         /* Only notify complete if it's on the pending list.
1612          * Otherwise, it's already been handled (eg, previously canceled).
1613          * The exception is cancellation of an incoming USSD-REQUEST, which is
1614          * not on the list.
1615          */
1616         logd("onMMIDone: mmi=" + mmi);
1617         if (mPendingMMIs.remove(mmi) || mmi.isUssdRequest() || mmi.isSsInfo()) {
1618             ResultReceiver receiverCallback = mmi.getUssdCallbackReceiver();
1619             if (receiverCallback != null) {
1620                 int returnCode = (mmi.getState() ==  MmiCode.State.COMPLETE) ?
1621                         TelephonyManager.USSD_RETURN_SUCCESS : TelephonyManager.USSD_RETURN_FAILURE;
1622                 sendUssdResponse(mmi.getDialString(), mmi.getMessage(), returnCode,
1623                         receiverCallback );
1624             } else {
1625                 logv("onMMIDone: notifyRegistrants");
1626                 mMmiCompleteRegistrants.notifyRegistrants(
1627                     new AsyncResult(null, mmi, null));
1628             }
1629         }
1630     }
1631 
1632     @Override
getHandoverConnection()1633     public ArrayList<Connection> getHandoverConnection() {
1634         ArrayList<Connection> connList = new ArrayList<Connection>();
1635         // Add all foreground call connections
1636         connList.addAll(getForegroundCall().getConnections());
1637         // Add all background call connections
1638         connList.addAll(getBackgroundCall().getConnections());
1639         // Add all background call connections
1640         connList.addAll(getRingingCall().getConnections());
1641         if (connList.size() > 0) {
1642             return connList;
1643         } else {
1644             return null;
1645         }
1646     }
1647 
1648     @Override
notifySrvccState(int state)1649     public void notifySrvccState(int state) {
1650         mCT.notifySrvccState(state);
1651     }
1652 
1653     /* package */ void
initiateSilentRedial()1654     initiateSilentRedial() {
1655         initiateSilentRedial(false, EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED);
1656     }
1657 
1658     /* package */ void
initiateSilentRedial(boolean isEmergency, int eccCategory)1659     initiateSilentRedial(boolean isEmergency, int eccCategory) {
1660         DialArgs dialArgs = new DialArgs.Builder()
1661                                         .setIsEmergency(isEmergency)
1662                                         .setEccCategory(eccCategory)
1663                                         .build();
1664         int cause = CallFailCause.LOCAL_CALL_CS_RETRY_REQUIRED;
1665         AsyncResult ar = new AsyncResult(null,
1666                                          new SilentRedialParam(mLastDialString, cause, dialArgs),
1667                                          null);
1668         if (ar != null) {
1669             // There is a race condition that can happen in some cases:
1670             // (Main thread) dial start
1671             // (Binder Thread) onCallSessionFailed
1672             // (Binder Thread) schedule a redial for CS on the main thread
1673             // (Main Thread) dial finish
1674             // (Main Thread) schedule to associate ImsPhoneConnection with
1675             //               GsmConnection on the main thread
1676             // If scheduling the CS redial occurs before the command to schedule the
1677             // ImsPhoneConnection to be  associated with the GsmConnection, the CS redial will occur
1678             // before GsmConnection has had callbacks to ImsPhone correctly updated. This will cause
1679             // Callbacks back to GsmCdmaPhone to never be set up correctly and we will lose track of
1680             // the instance.
1681             // Instead, schedule this redial to happen on the main thread, so that we know dial has
1682             // finished before scheduling a redial:
1683             // (Main thread) dial start
1684             // (Binder Thread) onCallSessionFailed -> move notify registrants to main thread
1685             // (Main Thread) dial finish
1686             // (Main Thread) schedule on main thread to associate ImsPhoneConnection with
1687             //               GsmConnection
1688             // (Main Thread) schedule a redial for CS
1689             mContext.getMainExecutor().execute(() -> {
1690                 logd("initiateSilentRedial: notifying registrants, isEmergency=" + isEmergency
1691                         + ", eccCategory=" + eccCategory);
1692                 mSilentRedialRegistrants.notifyRegistrants(ar);
1693             });
1694         }
1695     }
1696 
registerForImsRegistrationChanges(Handler h, int what, Object obj)1697     public void registerForImsRegistrationChanges(Handler h, int what, Object obj) {
1698         mImsRegistrationUpdateRegistrants.addUnique(h, what, obj);
1699     }
1700 
unregisterForImsRegistrationChanges(Handler h)1701     public void unregisterForImsRegistrationChanges(Handler h) {
1702         mImsRegistrationUpdateRegistrants.remove(h);
1703     }
1704 
1705     @Override
registerForSilentRedial(Handler h, int what, Object obj)1706     public void registerForSilentRedial(Handler h, int what, Object obj) {
1707         mSilentRedialRegistrants.addUnique(h, what, obj);
1708     }
1709 
1710     @Override
unregisterForSilentRedial(Handler h)1711     public void unregisterForSilentRedial(Handler h) {
1712         mSilentRedialRegistrants.remove(h);
1713     }
1714 
1715     @Override
registerForSuppServiceNotification(Handler h, int what, Object obj)1716     public void registerForSuppServiceNotification(Handler h, int what, Object obj) {
1717         mSsnRegistrants.addUnique(h, what, obj);
1718     }
1719 
1720     @Override
unregisterForSuppServiceNotification(Handler h)1721     public void unregisterForSuppServiceNotification(Handler h) {
1722         mSsnRegistrants.remove(h);
1723     }
1724 
1725     @Override
getSubId()1726     public int getSubId() {
1727         return mDefaultPhone.getSubId();
1728     }
1729 
1730     @Override
getPhoneId()1731     public int getPhoneId() {
1732         return mDefaultPhone.getPhoneId();
1733     }
1734 
getCallForwardInfo(ImsCallForwardInfo info)1735     private CallForwardInfo getCallForwardInfo(ImsCallForwardInfo info) {
1736         CallForwardInfo cfInfo = new CallForwardInfo();
1737         cfInfo.status = info.getStatus();
1738         cfInfo.reason = getCFReasonFromCondition(info.getCondition());
1739         cfInfo.serviceClass = SERVICE_CLASS_VOICE;
1740         cfInfo.toa = info.getToA();
1741         cfInfo.number = info.getNumber();
1742         cfInfo.timeSeconds = info.getTimeSeconds();
1743         return cfInfo;
1744     }
1745 
1746     @Override
getLine1Number()1747     public String getLine1Number() {
1748         return mDefaultPhone.getLine1Number();
1749     }
1750 
1751     /**
1752      * Used to Convert ImsCallForwardInfo[] to CallForwardInfo[].
1753      * Update received call forward status to default IccRecords.
1754      */
handleCfQueryResult(ImsCallForwardInfo[] infos)1755     public CallForwardInfo[] handleCfQueryResult(ImsCallForwardInfo[] infos) {
1756         CallForwardInfo[] cfInfos = null;
1757 
1758         if (infos != null && infos.length != 0) {
1759             cfInfos = new CallForwardInfo[infos.length];
1760         }
1761 
1762         if (infos == null || infos.length == 0) {
1763             // Assume the default is not active
1764             // Set unconditional CFF in SIM to false
1765             setVoiceCallForwardingFlag(getIccRecords(), 1, false, null);
1766         } else {
1767             for (int i = 0, s = infos.length; i < s; i++) {
1768                 if (infos[i].getCondition() == ImsUtInterface.CDIV_CF_UNCONDITIONAL) {
1769                     setVoiceCallForwardingFlag(getIccRecords(), 1, (infos[i].getStatus() == 1),
1770                         infos[i].getNumber());
1771                 }
1772                 cfInfos[i] = getCallForwardInfo(infos[i]);
1773             }
1774         }
1775 
1776         return cfInfos;
1777     }
1778 
handleCbQueryResult(ImsSsInfo[] infos)1779     private int[] handleCbQueryResult(ImsSsInfo[] infos) {
1780         int[] cbInfos = new int[1];
1781         cbInfos[0] = SERVICE_CLASS_NONE;
1782 
1783         if (infos[0].getStatus() == 1) {
1784             cbInfos[0] = SERVICE_CLASS_VOICE;
1785         }
1786 
1787         return cbInfos;
1788     }
1789 
handleCwQueryResult(ImsSsInfo[] infos)1790     private int[] handleCwQueryResult(ImsSsInfo[] infos) {
1791         int[] cwInfos = new int[2];
1792         cwInfos[0] = 0;
1793 
1794         if (infos[0].getStatus() == 1) {
1795             cwInfos[0] = 1;
1796             cwInfos[1] = SERVICE_CLASS_VOICE;
1797         }
1798 
1799         return cwInfos;
1800     }
1801 
1802     private void
sendResponse(Message onComplete, Object result, Throwable e)1803     sendResponse(Message onComplete, Object result, Throwable e) {
1804         if (onComplete != null) {
1805             CommandException ex = null;
1806             if (e != null) {
1807                 ex = getCommandException(e);
1808             }
1809             AsyncResult.forMessage(onComplete, result, ex);
1810             onComplete.sendToTarget();
1811         }
1812     }
1813 
updateDataServiceState()1814     private void updateDataServiceState() {
1815         if (mSS != null && mDefaultPhone.getServiceStateTracker() != null
1816                 && mDefaultPhone.getServiceStateTracker().mSS != null) {
1817             ServiceState ss = mDefaultPhone.getServiceStateTracker().mSS;
1818             mSS.setDataRegState(ss.getDataRegistrationState());
1819             List<NetworkRegistrationInfo> nriList =
1820                     ss.getNetworkRegistrationInfoListForDomain(NetworkRegistrationInfo.DOMAIN_PS);
1821             for (NetworkRegistrationInfo nri : nriList) {
1822                 mSS.addNetworkRegistrationInfo(nri);
1823             }
1824 
1825             mSS.setIwlanPreferred(ss.isIwlanPreferred());
1826             logd("updateDataServiceState: defSs = " + ss + " imsSs = " + mSS);
1827         }
1828     }
1829 
isCsRetryException(Throwable e)1830     boolean isCsRetryException(Throwable e) {
1831         if ((e != null) && (e instanceof ImsException)
1832                 && (((ImsException)e).getCode()
1833                     == ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED)) {
1834             return true;
1835         }
1836         return false;
1837     }
1838 
setCsfbBundle(boolean isCsRetry)1839     private Bundle setCsfbBundle(boolean isCsRetry) {
1840         Bundle b = new Bundle();
1841         b.putBoolean(CS_FALLBACK_SS, isCsRetry);
1842         return b;
1843     }
1844 
sendResponseOrRetryOnCsfbSs(SS ss, int what, Throwable e, Object obj)1845     private void sendResponseOrRetryOnCsfbSs(SS ss, int what, Throwable e, Object obj) {
1846         if (!isCsRetryException(e)) {
1847             sendResponse(ss.mOnComplete, obj, e);
1848             return;
1849         }
1850 
1851         Rlog.d(LOG_TAG, "Try CSFB: " + what);
1852         ss.mOnComplete.setData(setCsfbBundle(true));
1853 
1854         switch (what) {
1855             case EVENT_GET_CALL_FORWARD_DONE:
1856                 mDefaultPhone.getCallForwardingOption(ss.mCfReason,
1857                                                       ss.mServiceClass,
1858                                                       ss.mOnComplete);
1859                 break;
1860             case EVENT_SET_CALL_FORWARD_DONE:
1861                 mDefaultPhone.setCallForwardingOption(ss.mCfAction,
1862                                                       ss.mCfReason,
1863                                                       ss.mDialingNumber,
1864                                                       ss.mServiceClass,
1865                                                       ss.mTimerSeconds,
1866                                                       ss.mOnComplete);
1867                 break;
1868             case EVENT_GET_CALL_BARRING_DONE:
1869                 mDefaultPhone.getCallBarring(ss.mFacility,
1870                                              ss.mPassword,
1871                                              ss.mOnComplete,
1872                                              ss.mServiceClass);
1873                 break;
1874             case EVENT_SET_CALL_BARRING_DONE:
1875                 mDefaultPhone.setCallBarring(ss.mFacility,
1876                                              ss.mLockState,
1877                                              ss.mPassword,
1878                                              ss.mOnComplete,
1879                                              ss.mServiceClass);
1880                 break;
1881             case EVENT_GET_CALL_WAITING_DONE:
1882                 mDefaultPhone.getCallWaiting(ss.mOnComplete);
1883                 break;
1884             case EVENT_SET_CALL_WAITING_DONE:
1885                 mDefaultPhone.setCallWaiting(ss.mEnable,
1886                                              ss.mServiceClass,
1887                                              ss.mOnComplete);
1888                 break;
1889             case EVENT_GET_CLIR_DONE:
1890                 mDefaultPhone.getOutgoingCallerIdDisplay(ss.mOnComplete);
1891                 break;
1892             case EVENT_SET_CLIR_DONE:
1893                 mDefaultPhone.setOutgoingCallerIdDisplay(ss.mClirMode, ss.mOnComplete);
1894                 break;
1895             case EVENT_GET_CLIP_DONE:
1896                 mDefaultPhone.queryCLIP(ss.mOnComplete);
1897                 break;
1898             default:
1899                 break;
1900         }
1901     }
1902 
1903     @Override
handleMessage(Message msg)1904     public void handleMessage(Message msg) {
1905         AsyncResult ar = (AsyncResult) msg.obj;
1906         Message onComplete;
1907         SS ss = null;
1908         if (ar != null && ar.userObj instanceof SS) {
1909             ss = (SS) ar.userObj;
1910         }
1911 
1912         if (DBG) logd("handleMessage what=" + msg.what);
1913         switch (msg.what) {
1914             case EVENT_SET_CALL_FORWARD_DONE:
1915                 if (ar.exception == null && ss != null &&
1916                     (ss.mCfReason == CF_REASON_UNCONDITIONAL)) {
1917                     setVoiceCallForwardingFlag(getIccRecords(), 1, isCfEnable(ss.mCfAction),
1918                                                ss.mDialingNumber);
1919                 }
1920                 if (ss != null) {
1921                     sendResponseOrRetryOnCsfbSs(ss, msg.what, ar.exception, null);
1922                 }
1923                 break;
1924 
1925             case EVENT_GET_CALL_FORWARD_DONE:
1926                 CallForwardInfo[] cfInfos = null;
1927                 if (ar.exception == null) {
1928                     cfInfos = handleCfQueryResult((ImsCallForwardInfo[])ar.result);
1929                 }
1930                 if (ss != null) {
1931                     sendResponseOrRetryOnCsfbSs(ss, msg.what, ar.exception, cfInfos);
1932                 }
1933                 break;
1934 
1935             case EVENT_GET_CALL_BARRING_DONE:
1936             case EVENT_GET_CALL_WAITING_DONE:
1937                 int[] ssInfos = null;
1938                 if (ar.exception == null) {
1939                     if (msg.what == EVENT_GET_CALL_BARRING_DONE) {
1940                         ssInfos = handleCbQueryResult((ImsSsInfo[])ar.result);
1941                     } else if (msg.what == EVENT_GET_CALL_WAITING_DONE) {
1942                         ssInfos = handleCwQueryResult((ImsSsInfo[])ar.result);
1943                     }
1944                 }
1945                 if (ss != null) {
1946                     sendResponseOrRetryOnCsfbSs(ss, msg.what, ar.exception, ssInfos);
1947                 }
1948                 break;
1949 
1950             case EVENT_GET_CLIR_DONE:
1951                 ImsSsInfo ssInfo = (ImsSsInfo) ar.result;
1952                 int[] clirInfo = null;
1953                 if (ssInfo != null) {
1954                     // Unfortunately callers still use the old {n,m} format of ImsSsInfo, so return
1955                     // that for compatibility
1956                     clirInfo = ssInfo.getCompatArray(ImsSsData.SS_CLIR);
1957                 }
1958                 if (ss != null) {
1959                     sendResponseOrRetryOnCsfbSs(ss, msg.what, ar.exception, clirInfo);
1960                 }
1961                 break;
1962 
1963             case EVENT_GET_CLIP_DONE:
1964                 ImsSsInfo ssInfoResp = null;
1965                 if (ar.exception == null && ar.result instanceof ImsSsInfo) {
1966                     ssInfoResp = (ImsSsInfo) ar.result;
1967                 }
1968                 if (ss != null) {
1969                     sendResponseOrRetryOnCsfbSs(ss, msg.what, ar.exception, ssInfoResp);
1970                 }
1971                 break;
1972 
1973             case EVENT_SET_CLIR_DONE:
1974                 if (ar.exception == null) {
1975                     if (ss != null) {
1976                         saveClirSetting(ss.mClirMode);
1977                     }
1978                 }
1979                  // (Intentional fallthrough)
1980             case EVENT_SET_CALL_BARRING_DONE:
1981             case EVENT_SET_CALL_WAITING_DONE:
1982                 if (ss != null) {
1983                     sendResponseOrRetryOnCsfbSs(ss, msg.what, ar.exception, null);
1984                 }
1985                 break;
1986 
1987             case EVENT_DEFAULT_PHONE_DATA_STATE_CHANGED:
1988                 if (DBG) logd("EVENT_DEFAULT_PHONE_DATA_STATE_CHANGED");
1989                 updateDataServiceState();
1990                 break;
1991 
1992             case EVENT_SERVICE_STATE_CHANGED:
1993                 if (VDBG) logd("EVENT_SERVICE_STATE_CHANGED");
1994                 ar = (AsyncResult) msg.obj;
1995                 ServiceState newServiceState = (ServiceState) ar.result;
1996                 updateRoamingState(newServiceState);
1997                 break;
1998             case EVENT_VOICE_CALL_ENDED:
1999                 if (DBG) logd("Voice call ended. Handle pending updateRoamingState.");
2000                 mCT.unregisterForVoiceCallEnded(this);
2001                 // Get the current unmodified ServiceState from the tracker, as it has more info
2002                 // about the cell roaming state.
2003                 ServiceStateTracker sst = getDefaultPhone().getServiceStateTracker();
2004                 if (sst != null) {
2005                     updateRoamingState(sst.mSS);
2006                 }
2007                 break;
2008             case EVENT_INITIATE_VOLTE_SILENT_REDIAL: {
2009                 // This is a CS -> IMS redial
2010                 if (VDBG) logd("EVENT_INITIATE_VOLTE_SILENT_REDIAL");
2011                 ar = (AsyncResult) msg.obj;
2012                 if (ar.exception == null && ar.result != null) {
2013                     SilentRedialParam result = (SilentRedialParam) ar.result;
2014                     String dialString = result.dialString;
2015                     int causeCode = result.causeCode;
2016                     DialArgs dialArgs = result.dialArgs;
2017                     if (VDBG) logd("dialString=" + dialString + " causeCode=" + causeCode);
2018 
2019                     try {
2020                         Connection cn = dial(dialString,
2021                                 updateDialArgsForVolteSilentRedial(dialArgs, causeCode));
2022                         // The GSM/CDMA Connection that is owned by the GsmCdmaPhone is currently
2023                         // the one with a callback registered to TelephonyConnection. Notify the
2024                         // redial happened over that Phone so that it can be replaced with the
2025                         // new ImsPhoneConnection.
2026                         Rlog.d(LOG_TAG, "Notify volte redial connection changed cn: " + cn);
2027                         if (mDefaultPhone != null) {
2028                             // don't care it is null or not.
2029                             mDefaultPhone.notifyRedialConnectionChanged(cn);
2030                         }
2031                     } catch (CallStateException e) {
2032                         Rlog.e(LOG_TAG, "volte silent redial failed: " + e);
2033                         if (mDefaultPhone != null) {
2034                             mDefaultPhone.notifyRedialConnectionChanged(null);
2035                         }
2036                     }
2037                 } else {
2038                     if (VDBG) logd("EVENT_INITIATE_VOLTE_SILENT_REDIAL" +
2039                                    " has exception or empty result");
2040                 }
2041                 break;
2042             }
2043 
2044             default:
2045                 super.handleMessage(msg);
2046                 break;
2047         }
2048     }
2049 
2050     /**
2051      * Listen to the IMS ECBM state change
2052      */
2053     private ImsEcbmStateListener mImsEcbmStateListener =
2054             new ImsEcbmStateListener(mContext.getMainExecutor()) {
2055                 @Override
2056                 public void onECBMEntered(Executor executor) {
2057                     if (DBG) logd("onECBMEntered");
2058 
2059                     TelephonyUtils.runWithCleanCallingIdentity(()->
2060                             handleEnterEmergencyCallbackMode(), executor);
2061                 }
2062 
2063 
2064 
2065                 @Override
2066                 public void onECBMExited(Executor executor) {
2067                     if (DBG) logd("onECBMExited");
2068                     TelephonyUtils.runWithCleanCallingIdentity(()->
2069                             handleExitEmergencyCallbackMode(), executor);
2070                 }
2071             };
2072 
2073     @VisibleForTesting
getImsEcbmStateListener()2074     public ImsEcbmStateListener getImsEcbmStateListener() {
2075         return mImsEcbmStateListener;
2076     }
2077 
2078     @Override
isInEmergencyCall()2079     public boolean isInEmergencyCall() {
2080         return mCT.isInEmergencyCall();
2081     }
2082 
sendEmergencyCallbackModeChange()2083     private void sendEmergencyCallbackModeChange() {
2084         // Send an Intent
2085         Intent intent = new Intent(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
2086         intent.putExtra(TelephonyManager.EXTRA_PHONE_IN_ECM_STATE, isInEcm());
2087         SubscriptionManager.putPhoneIdAndSubIdExtra(intent, getPhoneId());
2088         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
2089         if (DBG) logd("sendEmergencyCallbackModeChange: isInEcm=" + isInEcm());
2090     }
2091 
2092     @Override
exitEmergencyCallbackMode()2093     public void exitEmergencyCallbackMode() {
2094         if (mWakeLock.isHeld()) {
2095             mWakeLock.release();
2096         }
2097         if (DBG) logd("exitEmergencyCallbackMode()");
2098 
2099         // Send a message which will invoke handleExitEmergencyCallbackMode
2100         ImsEcbm ecbm;
2101         try {
2102             ecbm = mCT.getEcbmInterface();
2103             ecbm.exitEmergencyCallbackMode();
2104         } catch (ImsException e) {
2105             e.printStackTrace();
2106         }
2107     }
2108 
2109     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
handleEnterEmergencyCallbackMode()2110     private void handleEnterEmergencyCallbackMode() {
2111         if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) {
2112             logd("DomainSelection enabled: ignore ECBM enter event.");
2113             return;
2114         }
2115         if (DBG) logd("handleEnterEmergencyCallbackMode,mIsPhoneInEcmState= " + isInEcm());
2116         // if phone is not in Ecm mode, and it's changed to Ecm mode
2117         if (!isInEcm()) {
2118             setIsInEcm(true);
2119             // notify change
2120             sendEmergencyCallbackModeChange();
2121             ((GsmCdmaPhone) mDefaultPhone).notifyEmergencyCallRegistrants(true);
2122 
2123             // Post this runnable so we will automatically exit
2124             // if no one invokes exitEmergencyCallbackMode() directly.
2125             long delayInMillis = TelephonyProperties.ecm_exit_timer()
2126                     .orElse(DEFAULT_ECM_EXIT_TIMER_VALUE);
2127             postDelayed(mExitEcmRunnable, delayInMillis);
2128             // We don't want to go to sleep while in Ecm
2129             mWakeLock.acquire();
2130         }
2131     }
2132 
2133     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
2134     @Override
handleExitEmergencyCallbackMode()2135     protected void handleExitEmergencyCallbackMode() {
2136         if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) {
2137             logd("DomainSelection enabled: ignore ECBM exit event.");
2138             return;
2139         }
2140         if (DBG) logd("handleExitEmergencyCallbackMode: mIsPhoneInEcmState = " + isInEcm());
2141 
2142         if (isInEcm()) {
2143             setIsInEcm(false);
2144         }
2145 
2146         // Remove pending exit Ecm runnable, if any
2147         removeCallbacks(mExitEcmRunnable);
2148 
2149         if (mEcmExitRespRegistrant != null) {
2150             mEcmExitRespRegistrant.notifyResult(Boolean.TRUE);
2151         }
2152 
2153         // release wakeLock
2154         if (mWakeLock.isHeld()) {
2155             mWakeLock.release();
2156         }
2157 
2158         // send an Intent
2159         sendEmergencyCallbackModeChange();
2160         ((GsmCdmaPhone) mDefaultPhone).notifyEmergencyCallRegistrants(false);
2161     }
2162 
2163     /**
2164      * Handle to cancel or restart Ecm timer in emergency call back mode if action is
2165      * CANCEL_ECM_TIMER, cancel Ecm timer and notify apps the timer is canceled; otherwise, restart
2166      * Ecm timer and notify apps the timer is restarted.
2167      */
handleTimerInEmergencyCallbackMode(int action)2168     void handleTimerInEmergencyCallbackMode(int action) {
2169         if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) return;
2170         switch (action) {
2171             case CANCEL_ECM_TIMER:
2172                 removeCallbacks(mExitEcmRunnable);
2173                 ((GsmCdmaPhone) mDefaultPhone).notifyEcbmTimerReset(Boolean.TRUE);
2174                 setEcmCanceledForEmergency(true /*isCanceled*/);
2175                 break;
2176             case RESTART_ECM_TIMER:
2177                 long delayInMillis = TelephonyProperties.ecm_exit_timer()
2178                         .orElse(DEFAULT_ECM_EXIT_TIMER_VALUE);
2179                 postDelayed(mExitEcmRunnable, delayInMillis);
2180                 ((GsmCdmaPhone) mDefaultPhone).notifyEcbmTimerReset(Boolean.FALSE);
2181                 setEcmCanceledForEmergency(false /*isCanceled*/);
2182                 break;
2183             default:
2184                 loge("handleTimerInEmergencyCallbackMode, unsupported action " + action);
2185         }
2186     }
2187 
2188     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
2189     @Override
setOnEcbModeExitResponse(Handler h, int what, Object obj)2190     public void setOnEcbModeExitResponse(Handler h, int what, Object obj) {
2191         mEcmExitRespRegistrant = new Registrant(h, what, obj);
2192     }
2193 
2194     @Override
unsetOnEcbModeExitResponse(Handler h)2195     public void unsetOnEcbModeExitResponse(Handler h) {
2196         mEcmExitRespRegistrant.clear();
2197     }
2198 
onFeatureCapabilityChanged()2199     public void onFeatureCapabilityChanged() {
2200         mDefaultPhone.getServiceStateTracker().onImsCapabilityChanged();
2201     }
2202 
2203     @Override
isImsCapabilityAvailable(int capability, int regTech)2204     public boolean isImsCapabilityAvailable(int capability, int regTech) throws ImsException {
2205         return mCT.isImsCapabilityAvailable(capability, regTech);
2206     }
2207 
2208     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
2209     @Override
isVolteEnabled()2210     public boolean isVolteEnabled() {
2211         return isVoiceOverCellularImsEnabled();
2212     }
2213 
2214     @Override
isVoiceOverCellularImsEnabled()2215     public boolean isVoiceOverCellularImsEnabled() {
2216         return mCT.isVoiceOverCellularImsEnabled();
2217     }
2218 
2219     @Override
isWifiCallingEnabled()2220     public boolean isWifiCallingEnabled() {
2221         return mCT.isVowifiEnabled();
2222     }
2223 
2224     @Override
isVideoEnabled()2225     public boolean isVideoEnabled() {
2226         return mCT.isVideoCallEnabled();
2227     }
2228 
2229     @Override
getImsRegistrationTech()2230     public int getImsRegistrationTech() {
2231         return mCT.getImsRegistrationTech();
2232     }
2233 
2234     @Override
getImsRegistrationTech(Consumer<Integer> callback)2235     public void getImsRegistrationTech(Consumer<Integer> callback) {
2236         mCT.getImsRegistrationTech(callback);
2237     }
2238 
2239     @Override
getImsRegistrationState(Consumer<Integer> callback)2240     public void getImsRegistrationState(Consumer<Integer> callback) {
2241         callback.accept(mImsMmTelRegistrationHelper.getImsRegistrationState());
2242     }
2243 
2244     @Override
getDefaultPhone()2245     public Phone getDefaultPhone() {
2246         return mDefaultPhone;
2247     }
2248 
2249     @Override
isImsRegistered()2250     public boolean isImsRegistered() {
2251         return mImsMmTelRegistrationHelper.isImsRegistered();
2252     }
2253 
2254     // Not used, but not removed due to UnsupportedAppUsage tag.
2255     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
setImsRegistered(boolean isRegistered)2256     public void setImsRegistered(boolean isRegistered) {
2257         mImsMmTelRegistrationHelper.updateRegistrationState(
2258                 isRegistered ? RegistrationManager.REGISTRATION_STATE_REGISTERED :
2259                         RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED);
2260     }
2261 
2262     @Override
callEndCleanupHandOverCallIfAny()2263     public void callEndCleanupHandOverCallIfAny() {
2264         mCT.callEndCleanupHandOverCallIfAny();
2265     }
2266 
2267     private BroadcastReceiver mResultReceiver = new BroadcastReceiver() {
2268         @Override
2269         public void onReceive(Context context, Intent intent) {
2270             // Add notification only if alert was not shown by WfcSettings
2271             if (getResultCode() == Activity.RESULT_OK) {
2272                 // Default result code (as passed to sendOrderedBroadcast)
2273                 // means that intent was not received by WfcSettings.
2274 
2275                 CharSequence title =
2276                         intent.getCharSequenceExtra(EXTRA_WFC_REGISTRATION_FAILURE_TITLE);
2277                 CharSequence messageAlert =
2278                         intent.getCharSequenceExtra(EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE);
2279                 CharSequence messageNotification =
2280                         intent.getCharSequenceExtra(EXTRA_KEY_NOTIFICATION_MESSAGE);
2281 
2282                 Intent resultIntent = new Intent(Intent.ACTION_MAIN);
2283                 // Note: If the classname below is ever removed, the call to
2284                 // PendingIntent.getActivity should also specify FLAG_IMMUTABLE to ensure the
2285                 // pending intent cannot be tampered with.
2286                 resultIntent.setClassName("com.android.settings",
2287                         "com.android.settings.Settings$WifiCallingSettingsActivity");
2288                 resultIntent.putExtra(EXTRA_KEY_ALERT_SHOW, true);
2289                 resultIntent.putExtra(EXTRA_WFC_REGISTRATION_FAILURE_TITLE, title);
2290                 resultIntent.putExtra(EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE, messageAlert);
2291                 PendingIntent resultPendingIntent =
2292                         PendingIntent.getActivity(
2293                                 mContext,
2294                                 0,
2295                                 resultIntent,
2296                                 PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
2297                         );
2298 
2299                 final Notification notification = new Notification.Builder(mContext)
2300                                 .setSmallIcon(android.R.drawable.stat_sys_warning)
2301                                 .setContentTitle(title)
2302                                 .setContentText(messageNotification)
2303                                 .setAutoCancel(true)
2304                                 .setContentIntent(resultPendingIntent)
2305                                 .setStyle(new Notification.BigTextStyle()
2306                                 .bigText(messageNotification))
2307                                 .setChannelId(NotificationChannelController.CHANNEL_ID_WFC)
2308                                 .build();
2309                 final String notificationTag = "wifi_calling";
2310                 final int notificationId = 1;
2311 
2312                 NotificationManager notificationManager =
2313                         (NotificationManager) mContext.getSystemService(
2314                                 Context.NOTIFICATION_SERVICE);
2315                 notificationManager.notify(notificationTag, notificationId,
2316                         notification);
2317             }
2318         }
2319     };
2320 
2321     /**
2322      * Show notification in case of some error codes.
2323      */
processDisconnectReason(ImsReasonInfo imsReasonInfo)2324     public void processDisconnectReason(ImsReasonInfo imsReasonInfo) {
2325         if (imsReasonInfo.mCode == imsReasonInfo.CODE_REGISTRATION_ERROR
2326                 && imsReasonInfo.mExtraMessage != null) {
2327             // Suppress WFC Registration notifications if WFC is not enabled by the user.
2328             if (mImsManagerFactory.create(mContext, mPhoneId).isWfcEnabledByUser()) {
2329                 processWfcDisconnectForNotification(imsReasonInfo);
2330             }
2331         }
2332     }
2333 
2334     // Processes an IMS disconnect cause for possible WFC registration errors and optionally
2335     // disable WFC.
processWfcDisconnectForNotification(ImsReasonInfo imsReasonInfo)2336     private void processWfcDisconnectForNotification(ImsReasonInfo imsReasonInfo) {
2337         CarrierConfigManager configManager =
2338                 (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
2339         if (configManager == null) {
2340             loge("processDisconnectReason: CarrierConfigManager is not ready");
2341             return;
2342         }
2343         PersistableBundle pb = configManager.getConfigForSubId(getSubId());
2344         if (pb == null) {
2345             loge("processDisconnectReason: no config for subId " + getSubId());
2346             return;
2347         }
2348         final String[] wfcOperatorErrorCodes =
2349                 pb.getStringArray(
2350                         CarrierConfigManager.KEY_WFC_OPERATOR_ERROR_CODES_STRING_ARRAY);
2351         if (wfcOperatorErrorCodes == null) {
2352             // no operator-specific error codes
2353             return;
2354         }
2355 
2356         final String[] wfcOperatorErrorAlertMessages =
2357                 mContext.getResources().getStringArray(
2358                         com.android.internal.R.array.wfcOperatorErrorAlertMessages);
2359         final String[] wfcOperatorErrorNotificationMessages =
2360                 mContext.getResources().getStringArray(
2361                         com.android.internal.R.array.wfcOperatorErrorNotificationMessages);
2362 
2363         for (int i = 0; i < wfcOperatorErrorCodes.length; i++) {
2364             String[] codes = wfcOperatorErrorCodes[i].split("\\|");
2365             if (codes.length != 2) {
2366                 loge("Invalid carrier config: " + wfcOperatorErrorCodes[i]);
2367                 continue;
2368             }
2369 
2370             // Match error code.
2371             if (!imsReasonInfo.mExtraMessage.startsWith(
2372                     codes[0])) {
2373                 continue;
2374             }
2375             // If there is no delimiter at the end of error code string
2376             // then we need to verify that we are not matching partial code.
2377             // EXAMPLE: "REG9" must not match "REG99".
2378             // NOTE: Error code must not be empty.
2379             int codeStringLength = codes[0].length();
2380             char lastChar = codes[0].charAt(codeStringLength - 1);
2381             if (Character.isLetterOrDigit(lastChar)) {
2382                 if (imsReasonInfo.mExtraMessage.length() > codeStringLength) {
2383                     char nextChar = imsReasonInfo.mExtraMessage.charAt(codeStringLength);
2384                     if (Character.isLetterOrDigit(nextChar)) {
2385                         continue;
2386                     }
2387                 }
2388             }
2389 
2390             final CharSequence title = mContext.getText(
2391                     com.android.internal.R.string.wfcRegErrorTitle);
2392 
2393             int idx = Integer.parseInt(codes[1]);
2394             if (idx < 0
2395                     || idx >= wfcOperatorErrorAlertMessages.length
2396                     || idx >= wfcOperatorErrorNotificationMessages.length) {
2397                 loge("Invalid index: " + wfcOperatorErrorCodes[i]);
2398                 continue;
2399             }
2400             String messageAlert = imsReasonInfo.mExtraMessage;
2401             String messageNotification = imsReasonInfo.mExtraMessage;
2402             if (!wfcOperatorErrorAlertMessages[idx].isEmpty()) {
2403                 messageAlert = String.format(
2404                         wfcOperatorErrorAlertMessages[idx],
2405                         imsReasonInfo.mExtraMessage); // Fill IMS error code into alert message
2406             }
2407             if (!wfcOperatorErrorNotificationMessages[idx].isEmpty()) {
2408                 messageNotification = String.format(
2409                         wfcOperatorErrorNotificationMessages[idx],
2410                         imsReasonInfo.mExtraMessage); // Fill IMS error code into notification
2411             }
2412 
2413             // If WfcSettings are active then alert will be shown
2414             // otherwise notification will be added.
2415             Intent intent = new Intent(
2416                     android.telephony.ims.ImsManager.ACTION_WFC_IMS_REGISTRATION_ERROR);
2417             intent.putExtra(EXTRA_WFC_REGISTRATION_FAILURE_TITLE, title);
2418             intent.putExtra(EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE, messageAlert);
2419             intent.putExtra(EXTRA_KEY_NOTIFICATION_MESSAGE, messageNotification);
2420             mContext.sendOrderedBroadcast(intent, null, mResultReceiver,
2421                     null, Activity.RESULT_OK, null, null);
2422 
2423             // We can only match a single error code
2424             // so should break the loop after a successful match.
2425             break;
2426         }
2427     }
2428 
2429     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
2430     @Override
isUtEnabled()2431     public boolean isUtEnabled() {
2432         return mCT.isUtEnabled();
2433     }
2434 
2435     @Override
sendEmergencyCallStateChange(boolean callActive)2436     public void sendEmergencyCallStateChange(boolean callActive) {
2437         mDefaultPhone.sendEmergencyCallStateChange(callActive);
2438     }
2439 
2440     @Override
setBroadcastEmergencyCallStateChanges(boolean broadcast)2441     public void setBroadcastEmergencyCallStateChanges(boolean broadcast) {
2442         mDefaultPhone.setBroadcastEmergencyCallStateChanges(broadcast);
2443     }
2444 
2445     @VisibleForTesting
getWakeLock()2446     public PowerManager.WakeLock getWakeLock() {
2447         return mWakeLock;
2448     }
2449 
2450     /**
2451      * Update roaming state and WFC mode in the following situations:
2452      *     1) voice is in service.
2453      *     2) data is in service.
2454      * @param ss non-null ServiceState
2455      */
updateRoamingState(ServiceState ss)2456     private void updateRoamingState(ServiceState ss) {
2457         if (ss == null) {
2458             loge("updateRoamingState: null ServiceState!");
2459             return;
2460         }
2461         boolean newRoamingState = ss.getRoaming();
2462         // Do not recalculate if there is no change to state.
2463         if (mLastKnownRoamingState == newRoamingState) {
2464             return;
2465         }
2466         boolean isInService = (ss.getState() == ServiceState.STATE_IN_SERVICE
2467                 || ss.getDataRegistrationState() == ServiceState.STATE_IN_SERVICE);
2468         // If we are not IN_SERVICE for voice or data, ignore change roaming state, as we always
2469         // move to home in this case.
2470         if (!isInService || !mDefaultPhone.isRadioOn()) {
2471             logi("updateRoamingState: we are not IN_SERVICE, ignoring roaming change.");
2472             return;
2473         }
2474 
2475         if (mCT.getState() == PhoneConstants.State.IDLE) {
2476             if (DBG) logd("updateRoamingState now: " + newRoamingState);
2477             if (!mFeatureFlags.updateRoamingStateToSetWfcMode()) {
2478                 mLastKnownRoamingState = newRoamingState;
2479             }
2480             CarrierConfigManager configManager = (CarrierConfigManager)
2481                     getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
2482             // Don't set wfc mode if carrierconfig has not loaded. It will be set by GsmCdmaPhone
2483             // when receives ACTION_CARRIER_CONFIG_CHANGED broadcast.
2484             if (configManager != null && CarrierConfigManager.isConfigForIdentifiedCarrier(
2485                     configManager.getConfigForSubId(getSubId()))) {
2486                 ImsManager imsManager = mImsManagerFactory.create(mContext, mPhoneId);
2487                 imsManager.setWfcMode(imsManager.getWfcMode(newRoamingState), newRoamingState);
2488                 if (mFeatureFlags.updateRoamingStateToSetWfcMode()) {
2489                     mLastKnownRoamingState = newRoamingState;
2490                 }
2491             }
2492         } else {
2493             if (DBG) logd("updateRoamingState postponed: " + newRoamingState);
2494             mCT.registerForVoiceCallEnded(this, EVENT_VOICE_CALL_ENDED, null);
2495         }
2496     }
2497 
getImsMmTelRegistrationCallback()2498     public RegistrationManager.RegistrationCallback getImsMmTelRegistrationCallback() {
2499         return mImsMmTelRegistrationHelper.getCallback();
2500     }
2501 
2502     /**
2503      * Reset the IMS registration state.
2504      */
resetImsRegistrationState()2505     public void resetImsRegistrationState() {
2506         if (DBG) logd("resetImsRegistrationState");
2507         mImsMmTelRegistrationHelper.reset();
2508         int subId = getSubId();
2509         if (SubscriptionManager.isValidSubscriptionId(subId)) {
2510             updateImsRegistrationInfo(REGISTRATION_STATE_NOT_REGISTERED,
2511                     REGISTRATION_TECH_NONE, SUGGESTED_ACTION_NONE, TRANSPORT_TYPE_INVALID);
2512         }
2513     }
2514 
2515     private ImsRegistrationCallbackHelper.ImsRegistrationUpdate mMmTelRegistrationUpdate = new
2516             ImsRegistrationCallbackHelper.ImsRegistrationUpdate() {
2517         @Override
2518         public void handleImsRegistered(@NonNull ImsRegistrationAttributes attributes) {
2519             int imsTransportType = attributes.getTransportType();
2520             if (DBG) {
2521                 logd("handleImsRegistered: onImsMmTelConnected imsTransportType="
2522                         + AccessNetworkConstants.transportTypeToString(imsTransportType));
2523             }
2524             mRegLocalLog.log("handleImsRegistered: onImsMmTelConnected imsTransportType="
2525                     + AccessNetworkConstants.transportTypeToString(imsTransportType));
2526             setServiceState(ServiceState.STATE_IN_SERVICE);
2527             getDefaultPhone().setImsRegistrationState(true);
2528             mMetrics.writeOnImsConnectionState(mPhoneId, ImsConnectionState.State.CONNECTED, null);
2529             mImsStats.onImsRegistered(attributes);
2530             mImsNrSaModeHandler.onImsRegistered(
2531                     attributes.getRegistrationTechnology(), attributes.getFeatureTags());
2532             updateImsRegistrationInfo(REGISTRATION_STATE_REGISTERED,
2533                     attributes.getRegistrationTechnology(), SUGGESTED_ACTION_NONE,
2534                     imsTransportType);
2535 
2536             AsyncResult ar;
2537             if (mFeatureFlags.changeMethodOfObtainingImsRegistrationRadioTech()) {
2538                 ar = new AsyncResult(null, new ImsRegistrationRadioTechInfo(mPhoneId,
2539                         attributes.getRegistrationTechnology(), REGISTRATION_STATE_REGISTERED),
2540                         null);
2541             } else {
2542                 ar = new AsyncResult(null, null, null);
2543             }
2544             mImsRegistrationUpdateRegistrants.notifyRegistrants(ar);
2545         }
2546 
2547         @Override
2548         public void handleImsRegistering(int imsRadioTech) {
2549             if (DBG) {
2550                 logd("handleImsRegistering: onImsMmTelProgressing imsRadioTech="
2551                         + AccessNetworkConstants.transportTypeToString(imsRadioTech));
2552             }
2553             mRegLocalLog.log("handleImsRegistering: onImsMmTelProgressing imsRadioTech="
2554                     + AccessNetworkConstants.transportTypeToString(imsRadioTech));
2555             setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
2556             getDefaultPhone().setImsRegistrationState(false);
2557             mMetrics.writeOnImsConnectionState(mPhoneId, ImsConnectionState.State.PROGRESSING,
2558                     null);
2559             mImsStats.onImsRegistering(imsRadioTech);
2560 
2561             AsyncResult ar;
2562             if (mFeatureFlags.changeMethodOfObtainingImsRegistrationRadioTech()) {
2563                 ar = new AsyncResult(null, new ImsRegistrationRadioTechInfo(mPhoneId,
2564                         imsRadioTech, REGISTRATION_STATE_REGISTERING),
2565                         null);
2566             } else {
2567                 ar = new AsyncResult(null, null, null);
2568             }
2569             mImsRegistrationUpdateRegistrants.notifyRegistrants(ar);
2570         }
2571 
2572         @Override
2573         public void handleImsUnregistered(ImsReasonInfo imsReasonInfo,
2574                 @RegistrationManager.SuggestedAction int suggestedAction,
2575                 @ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech) {
2576             if (DBG) {
2577                 logd("handleImsUnregistered: onImsMmTelDisconnected imsReasonInfo="
2578                         + imsReasonInfo + ", suggestedAction=" + suggestedAction
2579                         + ", disconnectedRadioTech=" + imsRadioTech);
2580             }
2581             mRegLocalLog.log("handleImsUnregistered: onImsMmTelDisconnected imsRadioTech="
2582                     + imsReasonInfo);
2583             setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
2584             processDisconnectReason(imsReasonInfo);
2585             getDefaultPhone().setImsRegistrationState(false);
2586             mMetrics.writeOnImsConnectionState(mPhoneId, ImsConnectionState.State.DISCONNECTED,
2587                     imsReasonInfo);
2588             mImsStats.onImsUnregistered(imsReasonInfo);
2589             mImsNrSaModeHandler.onImsUnregistered(imsRadioTech);
2590             mImsRegistrationTech = REGISTRATION_TECH_NONE;
2591             int suggestedModemAction = SUGGESTED_ACTION_NONE;
2592             if (imsReasonInfo.getCode() == ImsReasonInfo.CODE_REGISTRATION_ERROR) {
2593                 if ((suggestedAction == SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK)
2594                         || (suggestedAction == SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK_WITH_TIMEOUT)) {
2595                     suggestedModemAction = suggestedAction;
2596                 } else if (mFeatureFlags.addRatRelatedSuggestedActionToImsRegistration()) {
2597                     if ((suggestedAction == SUGGESTED_ACTION_TRIGGER_RAT_BLOCK)
2598                             || (suggestedAction == SUGGESTED_ACTION_TRIGGER_CLEAR_RAT_BLOCKS)) {
2599                         suggestedModemAction = suggestedAction;
2600                     }
2601                 }
2602             }
2603             updateImsRegistrationInfo(REGISTRATION_STATE_NOT_REGISTERED,
2604                     imsRadioTech, suggestedModemAction, TRANSPORT_TYPE_INVALID);
2605 
2606             if (mFeatureFlags.clearCachedImsPhoneNumberWhenDeviceLostImsRegistration()) {
2607                 // Clear the phone number from P-Associated-Uri
2608                 setCurrentSubscriberUris(null);
2609                 clearPhoneNumberForSourceIms();
2610             }
2611 
2612             AsyncResult ar;
2613             if (mFeatureFlags.changeMethodOfObtainingImsRegistrationRadioTech()) {
2614                 ar = new AsyncResult(null, new ImsRegistrationRadioTechInfo(mPhoneId,
2615                         REGISTRATION_TECH_NONE, REGISTRATION_STATE_NOT_REGISTERED),
2616                         null);
2617             } else {
2618                 ar = new AsyncResult(null, null, null);
2619             }
2620             mImsRegistrationUpdateRegistrants.notifyRegistrants(ar);
2621         }
2622 
2623         @Override
2624         public void handleImsSubscriberAssociatedUriChanged(Uri[] uris) {
2625             if (DBG) logd("handleImsSubscriberAssociatedUriChanged");
2626             setCurrentSubscriberUris(uris);
2627             setPhoneNumberForSourceIms(uris);
2628         }
2629     };
2630 
2631     /** Clear the IMS phone number from IMS associated Uris when IMS registration is lost. */
2632     @VisibleForTesting
clearPhoneNumberForSourceIms()2633     public void clearPhoneNumberForSourceIms() {
2634         int subId = getSubId();
2635         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
2636             return;
2637         }
2638 
2639         if (DBG) logd("clearPhoneNumberForSourceIms");
2640         mSubscriptionManagerService.setNumberFromIms(subId, new String(""));
2641     }
2642 
2643     /** Sets the IMS phone number from IMS associated URIs, if any found. */
2644     @VisibleForTesting
setPhoneNumberForSourceIms(Uri[] uris)2645     public void setPhoneNumberForSourceIms(Uri[] uris) {
2646         int subId = getSubId();
2647         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
2648             // Defending b/219080264:
2649             // SubscriptionManagerService.setSubscriptionProperty validates input subId
2650             // so do not proceed if subId invalid. This may be happening because cached
2651             // IMS callbacks are sent back to telephony after SIM state changed.
2652             return;
2653         }
2654         SubscriptionInfoInternal subInfo = mSubscriptionManagerService
2655                 .getSubscriptionInfoInternal(subId);
2656         if (subInfo == null) {
2657             loge("trigger setPhoneNumberForSourceIms, but subInfo is null");
2658             return;
2659         }
2660         String subCountryIso = subInfo.getCountryIso();
2661         String phoneNumber = extractPhoneNumberFromAssociatedUris(uris, /*isGlobalFormat*/true);
2662         if (phoneNumber != null) {
2663             phoneNumber = PhoneNumberUtils.formatNumberToE164(phoneNumber, subCountryIso);
2664             if (phoneNumber == null) {
2665                 loge("format to E164 failed");
2666                 return;
2667             }
2668             mSubscriptionManagerService.setNumberFromIms(subId, phoneNumber);
2669         } else if (isAllowNonGlobalNumberFormat()) {
2670             // If carrier config has true for KEY_IGNORE_GLOBAL_PHONE_NUMBER_FORMAT_BOOL and
2671             // P-Associated-Uri does not have global number,
2672             // try to find phone number excluding '+' one more time.
2673             phoneNumber = extractPhoneNumberFromAssociatedUris(uris, /*isGlobalFormat*/false);
2674             if (phoneNumber == null) {
2675                 loge("extract phone number without '+' failed");
2676                 return;
2677             }
2678             mSubscriptionManagerService.setNumberFromIms(subId, phoneNumber);
2679         } else {
2680             logd("extract phone number failed");
2681         }
2682     }
2683 
2684     /**
2685      * Finds the phone number from associated URIs.
2686      *
2687      * <p>Associated URIs are public user identities, and phone number could be used:
2688      * see 3GPP TS 24.229 5.4.1.2 and 3GPP TS 23.003 13.4. This algotihm look for the
2689      * possible "global number" in E.164 format.
2690      * <p>If true try finding phone number even if the P-Associated-Uri does not have global
2691      * number format.
2692      */
extractPhoneNumberFromAssociatedUris(Uri[] uris, boolean isGlobalFormat)2693     private static String extractPhoneNumberFromAssociatedUris(Uri[] uris, boolean isGlobalFormat) {
2694         if (uris == null) {
2695             return null;
2696         }
2697 
2698         Stream<String> intermediate = Arrays.stream(uris)
2699                 // Phone number is an opaque URI "tel:<phone-number>" or "sip:<phone-number>@<...>"
2700                 .filter(u -> u != null && u.isOpaque())
2701                 .filter(u -> "tel".equalsIgnoreCase(u.getScheme())
2702                         || "sip".equalsIgnoreCase(u.getScheme()))
2703                 .map(Uri::getSchemeSpecificPart);
2704 
2705         if (isGlobalFormat) {
2706             // "Global number" should be in E.164 format starting with "+" e.g. "+447539447777"
2707             return intermediate.filter(ssp -> ssp != null && ssp.startsWith("+"))
2708                     // Remove whatever after "@" for sip URI
2709                     .map(ssp -> ssp.split("@")[0])
2710                     // Returns the first winner
2711                     .findFirst()
2712                     .orElse(null);
2713         } else {
2714             // non global number format
2715             return intermediate.filter(ssp -> ssp != null)
2716                     // Remove whatever after "@" for sip URI
2717                     .map(ssp -> ssp.split("@")[0])
2718                     // regular expression, allow only number
2719                     .filter(ssp -> ssp.matches("^[0-9]+$"))
2720                     // Returns the first winner
2721                     .findFirst()
2722                     .orElse(null);
2723         }
2724     }
2725 
getIccRecords()2726     public IccRecords getIccRecords() {
2727         return mDefaultPhone.getIccRecords();
2728     }
2729 
updateDialArgsForVolteSilentRedial(DialArgs dialArgs, int causeCode)2730     public DialArgs updateDialArgsForVolteSilentRedial(DialArgs dialArgs, int causeCode) {
2731         if (dialArgs != null) {
2732             ImsPhone.ImsDialArgs.Builder imsDialArgsBuilder;
2733             imsDialArgsBuilder = ImsPhone.ImsDialArgs.Builder.from(dialArgs);
2734 
2735             Bundle extras = new Bundle(dialArgs.intentExtras);
2736             if (causeCode == CallFailCause.EMC_REDIAL_ON_VOWIFI) {
2737                 extras.putString(ImsCallProfile.EXTRA_CALL_RAT_TYPE,
2738                         String.valueOf(ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN));
2739                 logd("trigger VoWifi emergency call");
2740                 imsDialArgsBuilder.setIntentExtras(extras);
2741             } else if (causeCode == CallFailCause.EMC_REDIAL_ON_IMS) {
2742                 logd("trigger VoLte emergency call");
2743             }
2744             return imsDialArgsBuilder.build();
2745         }
2746         return new DialArgs.Builder<>().build();
2747     }
2748 
2749     @Override
getVoiceCallSessionStats()2750     public VoiceCallSessionStats getVoiceCallSessionStats() {
2751         return mDefaultPhone.getVoiceCallSessionStats();
2752     }
2753 
2754     /** Returns the {@link ImsStats} for this IMS phone. */
getImsStats()2755     public ImsStats getImsStats() {
2756         return mImsStats;
2757     }
2758 
2759     /** Returns the {@link AccessNetworkConstants.TransportType} used to register this IMS phone. */
getTransportType()2760     public @AccessNetworkConstants.TransportType int getTransportType() {
2761         return mTransportType;
2762     }
2763 
2764     /** Sets the {@link ImsStats} mock for this IMS phone during unit testing. */
2765     @VisibleForTesting
setImsStats(ImsStats imsStats)2766     public void setImsStats(ImsStats imsStats) {
2767         mImsStats = imsStats;
2768     }
2769 
hasAliveCall()2770     public boolean hasAliveCall() {
2771         return (getForegroundCall().getState() != Call.State.IDLE ||
2772                 getBackgroundCall().getState() != Call.State.IDLE);
2773     }
2774 
getLastKnownRoamingState()2775     public boolean getLastKnownRoamingState() {
2776         return mLastKnownRoamingState;
2777     }
2778 
2779     /**
2780      * Update IMS registration information to modem and other modules.
2781      *
2782      * @param capabilities indicate MMTEL capability such as VOICE, VIDEO and SMS.
2783      */
updateImsRegistrationInfo(int capabilities)2784     public void updateImsRegistrationInfo(int capabilities) {
2785         if (mImsRegistrationState == REGISTRATION_STATE_REGISTERED) {
2786             if (mNotifiedRegisteredState && (capabilities == mImsRegistrationCapabilities)) {
2787                 // Duplicated notification, no change in capabilities.
2788                 return;
2789             }
2790 
2791             mImsRegistrationCapabilities = capabilities;
2792             if (capabilities == 0) {
2793                 // Ignore this as this usually happens just before onUnregistered callback.
2794                 // We can notify modem when onUnregistered() flow occurs.
2795                 return;
2796             }
2797 
2798             mDefaultPhone.mCi.updateImsRegistrationInfo(mImsRegistrationState,
2799                     mImsRegistrationTech, 0, capabilities, null);
2800             mNotifiedRegisteredState = true;
2801 
2802             mImsNrSaModeHandler.updateImsCapability(capabilities);
2803         }
2804     }
2805 
2806     /**
2807      * Update IMS registration info
2808      *
2809      * @param regState indicates IMS registration state.
2810      * @param imsRadioTech indicates the type of the radio access network where IMS is registered.
2811      * @param suggestedAction indicates the suggested action for the radio to perform.
2812      */
updateImsRegistrationInfo( @egistrationManager.ImsRegistrationState int regState, @ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech, @RegistrationManager.SuggestedAction int suggestedAction, @AccessNetworkConstants.TransportType int transportType)2813     private void updateImsRegistrationInfo(
2814             @RegistrationManager.ImsRegistrationState int regState,
2815             @ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech,
2816             @RegistrationManager.SuggestedAction int suggestedAction,
2817             @AccessNetworkConstants.TransportType int transportType) {
2818 
2819         if (regState == mImsRegistrationState) {
2820             // In NOT_REGISTERED state, the current PLMN can be blocked with a suggested action.
2821             // But in this case, the same behavior is able to occur in different PLMNs with
2822             // same radio tech and suggested action.
2823             if ((regState == REGISTRATION_STATE_REGISTERED && imsRadioTech == mImsRegistrationTech)
2824                     || (regState == REGISTRATION_STATE_NOT_REGISTERED
2825                             && suggestedAction == SUGGESTED_ACTION_NONE
2826                             && mImsRegistrationSuggestedAction == SUGGESTED_ACTION_NONE
2827                             && imsRadioTech == mImsDeregistrationTech)) {
2828                 // Filter duplicate notification.
2829                 return;
2830             }
2831         }
2832 
2833         if (regState == REGISTRATION_STATE_NOT_REGISTERED) {
2834             mDefaultPhone.mCi.updateImsRegistrationInfo(regState,
2835                     imsRadioTech, suggestedAction, 0, null);
2836         } else if (mImsRegistrationState == REGISTRATION_STATE_REGISTERED) {
2837             // This happens when radio tech is changed while in REGISTERED state.
2838             if (mImsRegistrationCapabilities > 0) {
2839                 // Capability has been updated. Notify REGISTRATION_STATE_REGISTERED.
2840                 mDefaultPhone.mCi.updateImsRegistrationInfo(regState, imsRadioTech, 0,
2841                         mImsRegistrationCapabilities, null);
2842                 mImsRegistrationTech = imsRadioTech;
2843                 mTransportType = transportType;
2844                 mNotifiedRegisteredState = true;
2845                 return;
2846             }
2847         }
2848 
2849         mImsRegistrationState = regState;
2850         mImsRegistrationTech = imsRadioTech;
2851         mTransportType = transportType;
2852         mImsRegistrationSuggestedAction = suggestedAction;
2853         if (regState == REGISTRATION_STATE_NOT_REGISTERED) {
2854             mImsDeregistrationTech = imsRadioTech;
2855         } else {
2856             mImsDeregistrationTech = REGISTRATION_TECH_NONE;
2857         }
2858         mImsRegistrationCapabilities = 0;
2859         // REGISTRATION_STATE_REGISTERED will be notified when the capability is updated.
2860         mNotifiedRegisteredState = false;
2861     }
2862 
2863     @Override
setTerminalBasedCallWaitingStatus(int state)2864     public void setTerminalBasedCallWaitingStatus(int state) {
2865         mCT.setTerminalBasedCallWaitingStatus(state);
2866     }
2867 
2868     @Override
triggerEpsFallback(@mTelFeature.EpsFallbackReason int reason, Message response)2869     public void triggerEpsFallback(@MmTelFeature.EpsFallbackReason int reason, Message response) {
2870         mDefaultPhone.triggerEpsFallback(reason, response);
2871     }
2872 
2873     @Override
startImsTraffic(int token, @MmTelFeature.ImsTrafficType int trafficType, @AccessNetworkConstants.RadioAccessNetworkType int accessNetworkType, @MmTelFeature.ImsTrafficDirection int trafficDirection, Message response)2874     public void startImsTraffic(int token,
2875             @MmTelFeature.ImsTrafficType int trafficType,
2876             @AccessNetworkConstants.RadioAccessNetworkType int accessNetworkType,
2877             @MmTelFeature.ImsTrafficDirection int trafficDirection, Message response) {
2878         mDefaultPhone.startImsTraffic(token, trafficType,
2879                 accessNetworkType, trafficDirection, response);
2880     }
2881 
2882     @Override
stopImsTraffic(int token, Message response)2883     public void stopImsTraffic(int token, Message response) {
2884         mDefaultPhone.stopImsTraffic(token, response);
2885     }
2886 
2887     @Override
registerForConnectionSetupFailure(Handler h, int what, Object obj)2888     public void registerForConnectionSetupFailure(Handler h, int what, Object obj) {
2889         mDefaultPhone.registerForConnectionSetupFailure(h, what, obj);
2890     }
2891 
2892     @Override
unregisterForConnectionSetupFailure(Handler h)2893     public void unregisterForConnectionSetupFailure(Handler h) {
2894         mDefaultPhone.unregisterForConnectionSetupFailure(h);
2895     }
2896 
2897     @Override
triggerImsDeregistration( @msRegistrationImplBase.ImsDeregistrationReason int reason)2898     public void triggerImsDeregistration(
2899             @ImsRegistrationImplBase.ImsDeregistrationReason int reason) {
2900         mCT.triggerImsDeregistration(reason);
2901     }
2902 
2903     @Override
updateImsCallStatus(List<ImsCallInfo> imsCallInfo, Message response)2904     public void updateImsCallStatus(List<ImsCallInfo> imsCallInfo, Message response) {
2905         mDefaultPhone.updateImsCallStatus(imsCallInfo, response);
2906     }
2907 
2908     @Override
triggerNotifyAnbr(int mediaType, int direction, int bitsPerSecond)2909     public void triggerNotifyAnbr(int mediaType, int direction, int bitsPerSecond) {
2910         mCT.triggerNotifyAnbr(mediaType, direction, bitsPerSecond);
2911     }
2912 
2913     /**
2914      * Check whether making a call using Wi-Fi is possible or not.
2915      * @return {code true} if IMS is registered over IWLAN else return {code false}.
2916      */
canMakeWifiCall()2917     public boolean canMakeWifiCall() {
2918         return isImsRegistered() && (getImsRegistrationTech()
2919                 == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
2920     }
2921 
2922     @Override
dump(FileDescriptor fd, PrintWriter printWriter, String[] args)2923     public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
2924         IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
2925         pw.println("ImsPhone extends:");
2926         super.dump(fd, pw, args);
2927         pw.flush();
2928 
2929         pw.println("ImsPhone:");
2930         pw.println("  mDefaultPhone = " + mDefaultPhone);
2931         pw.println("  mPendingMMIs = " + mPendingMMIs);
2932         pw.println("  mPostDialHandler = " + mPostDialHandler);
2933         pw.println("  mSS = " + mSS);
2934         pw.println("  mWakeLock = " + mWakeLock);
2935         pw.println("  mIsPhoneInEcmState = " + isInEcm());
2936         pw.println("  mEcmExitRespRegistrant = " + mEcmExitRespRegistrant);
2937         pw.println("  mSilentRedialRegistrants = " + mSilentRedialRegistrants);
2938         pw.println("  mImsMmTelRegistrationState = "
2939                 + mImsMmTelRegistrationHelper.getImsRegistrationState());
2940         pw.println("  mLastKnownRoamingState = " + mLastKnownRoamingState);
2941         pw.println("  mSsnRegistrants = " + mSsnRegistrants);
2942         pw.println(" Registration Log:");
2943         pw.increaseIndent();
2944         mRegLocalLog.dump(pw);
2945         pw.decreaseIndent();
2946         pw.flush();
2947     }
2948 
isAllowNonGlobalNumberFormat()2949     private boolean isAllowNonGlobalNumberFormat() {
2950         PersistableBundle persistableBundle = null;
2951         CarrierConfigManager carrierConfigManager = (CarrierConfigManager) mContext
2952                 .getSystemService(Context.CARRIER_CONFIG_SERVICE);
2953         if (carrierConfigManager != null) {
2954             persistableBundle = carrierConfigManager.getConfigForSubId(getSubId(),
2955                     CarrierConfigManager.Ims.KEY_ALLOW_NON_GLOBAL_PHONE_NUMBER_FORMAT_BOOL);
2956         }
2957         if (persistableBundle != null) {
2958             return persistableBundle.getBoolean(
2959                     CarrierConfigManager.Ims.KEY_ALLOW_NON_GLOBAL_PHONE_NUMBER_FORMAT_BOOL, false);
2960         }
2961 
2962         return false;
2963     }
2964 
logi(String s)2965     private void logi(String s) {
2966         Rlog.i(LOG_TAG, "[" + mPhoneId + "] " + s);
2967     }
2968 
logv(String s)2969     private void logv(String s) {
2970         Rlog.v(LOG_TAG, "[" + mPhoneId + "] " + s);
2971     }
2972 
logd(String s)2973     private void logd(String s) {
2974         Rlog.d(LOG_TAG, "[" + mPhoneId + "] " + s);
2975     }
2976 
loge(String s)2977     private void loge(String s) {
2978         Rlog.e(LOG_TAG, "[" + mPhoneId + "] " + s);
2979     }
2980 }
2981