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         // handle in-call MMI first if applicable
1004         if (handleInCallMmiCommands(newDialString)) {
1005             return null;
1006         }
1007 
1008         ImsDialArgs.Builder imsDialArgsBuilder;
1009         imsDialArgsBuilder = ImsDialArgs.Builder.from(dialArgs);
1010         // Get the CLIR info if needed
1011         imsDialArgsBuilder.setClirMode(mCT.getClirMode());
1012 
1013         if (mDefaultPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
1014             return mCT.dial(dialString, imsDialArgsBuilder.build());
1015         }
1016 
1017         // Only look at the Network portion for mmi
1018         String networkPortion = PhoneNumberUtils.extractNetworkPortionAlt(newDialString);
1019         ImsPhoneMmiCode mmi =
1020                 ImsPhoneMmiCode.newFromDialString(networkPortion, this, wrappedCallback);
1021         if (DBG) logd("dialInternal: dialing w/ mmi '" + mmi + "'...");
1022 
1023         if (mmi == null) {
1024             return mCT.dial(dialString, imsDialArgsBuilder.build());
1025         } else if (mmi.isTemporaryModeCLIR()) {
1026             imsDialArgsBuilder.setClirMode(mmi.getCLIRMode());
1027             return mCT.dial(mmi.getDialingNumber(), imsDialArgsBuilder.build());
1028         } else if (!mmi.isSupportedOverImsPhone()) {
1029             // If the mmi is not supported by IMS service,
1030             // try to initiate dialing with default phone
1031             // Note: This code is never reached; there is a bug in isSupportedOverImsPhone which
1032             // causes it to return true even though the "processCode" method ultimately throws the
1033             // exception.
1034             logi("dialInternal: USSD not supported by IMS; fallback to CS.");
1035             throw new CallStateException(CS_FALLBACK);
1036         } else {
1037             mPendingMMIs.add(mmi);
1038             mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));
1039 
1040             try {
1041                 mmi.processCode();
1042             } catch (CallStateException cse) {
1043                 if (CS_FALLBACK.equals(cse.getMessage())) {
1044                     logi("dialInternal: fallback to GSM required.");
1045                     // Make sure we remove from the list of pending MMIs since it will handover to
1046                     // GSM.
1047                     mPendingMMIs.remove(mmi);
1048                     throw cse;
1049                 }
1050             }
1051 
1052             return null;
1053         }
1054     }
1055 
1056     @Override
1057     public void
sendDtmf(char c)1058     sendDtmf(char c) {
1059         if (!PhoneNumberUtils.is12Key(c)) {
1060             loge("sendDtmf called with invalid character '" + c + "'");
1061         } else {
1062             if (mCT.getState() ==  PhoneConstants.State.OFFHOOK) {
1063                 mCT.sendDtmf(c, null);
1064             }
1065         }
1066     }
1067 
1068     @Override
1069     public void
startDtmf(char c)1070     startDtmf(char c) {
1071         if (!(PhoneNumberUtils.is12Key(c) || (c >= 'A' && c <= 'D'))) {
1072             loge("startDtmf called with invalid character '" + c + "'");
1073         } else {
1074             mCT.startDtmf(c);
1075         }
1076     }
1077 
1078     @Override
1079     public void
stopDtmf()1080     stopDtmf() {
1081         mCT.stopDtmf();
1082     }
1083 
notifyIncomingRing()1084     public void notifyIncomingRing() {
1085         if (DBG) logd("notifyIncomingRing");
1086         AsyncResult ar = new AsyncResult(null, null, null);
1087         sendMessage(obtainMessage(EVENT_CALL_RING, ar));
1088     }
1089 
1090     @Override
setMute(boolean muted)1091     public void setMute(boolean muted) {
1092         mCT.setMute(muted);
1093     }
1094 
1095     @Override
setTTYMode(int ttyMode, Message onComplete)1096     public void setTTYMode(int ttyMode, Message onComplete) {
1097         mCT.setTtyMode(ttyMode);
1098     }
1099 
1100     @Override
setUiTTYMode(int uiTtyMode, Message onComplete)1101     public void setUiTTYMode(int uiTtyMode, Message onComplete) {
1102         mCT.setUiTTYMode(uiTtyMode, onComplete);
1103     }
1104 
1105     @Override
getMute()1106     public boolean getMute() {
1107         return mCT.getMute();
1108     }
1109 
1110     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
1111     @Override
getState()1112     public PhoneConstants.State getState() {
1113         return mCT.getState();
1114     }
1115 
1116     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
isValidCommandInterfaceCFReason(int commandInterfaceCFReason)1117     private boolean isValidCommandInterfaceCFReason (int commandInterfaceCFReason) {
1118         switch (commandInterfaceCFReason) {
1119         case CF_REASON_UNCONDITIONAL:
1120         case CF_REASON_BUSY:
1121         case CF_REASON_NO_REPLY:
1122         case CF_REASON_NOT_REACHABLE:
1123         case CF_REASON_ALL:
1124         case CF_REASON_ALL_CONDITIONAL:
1125             return true;
1126         default:
1127             return false;
1128         }
1129     }
1130 
1131     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
isValidCommandInterfaceCFAction(int commandInterfaceCFAction)1132     private boolean isValidCommandInterfaceCFAction (int commandInterfaceCFAction) {
1133         switch (commandInterfaceCFAction) {
1134         case CF_ACTION_DISABLE:
1135         case CF_ACTION_ENABLE:
1136         case CF_ACTION_REGISTRATION:
1137         case CF_ACTION_ERASURE:
1138             return true;
1139         default:
1140             return false;
1141         }
1142     }
1143 
1144     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
isCfEnable(int action)1145     private  boolean isCfEnable(int action) {
1146         return (action == CF_ACTION_ENABLE) || (action == CF_ACTION_REGISTRATION);
1147     }
1148 
1149     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
getConditionFromCFReason(int reason)1150     private int getConditionFromCFReason(int reason) {
1151         switch(reason) {
1152             case CF_REASON_UNCONDITIONAL: return ImsUtInterface.CDIV_CF_UNCONDITIONAL;
1153             case CF_REASON_BUSY: return ImsUtInterface.CDIV_CF_BUSY;
1154             case CF_REASON_NO_REPLY: return ImsUtInterface.CDIV_CF_NO_REPLY;
1155             case CF_REASON_NOT_REACHABLE: return ImsUtInterface.CDIV_CF_NOT_REACHABLE;
1156             case CF_REASON_ALL: return ImsUtInterface.CDIV_CF_ALL;
1157             case CF_REASON_ALL_CONDITIONAL: return ImsUtInterface.CDIV_CF_ALL_CONDITIONAL;
1158             default:
1159                 break;
1160         }
1161 
1162         return ImsUtInterface.INVALID;
1163     }
1164 
getCFReasonFromCondition(int condition)1165     private int getCFReasonFromCondition(int condition) {
1166         switch(condition) {
1167             case ImsUtInterface.CDIV_CF_UNCONDITIONAL: return CF_REASON_UNCONDITIONAL;
1168             case ImsUtInterface.CDIV_CF_BUSY: return CF_REASON_BUSY;
1169             case ImsUtInterface.CDIV_CF_NO_REPLY: return CF_REASON_NO_REPLY;
1170             case ImsUtInterface.CDIV_CF_NOT_REACHABLE: return CF_REASON_NOT_REACHABLE;
1171             case ImsUtInterface.CDIV_CF_ALL: return CF_REASON_ALL;
1172             case ImsUtInterface.CDIV_CF_ALL_CONDITIONAL: return CF_REASON_ALL_CONDITIONAL;
1173             default:
1174                 break;
1175         }
1176 
1177         return CF_REASON_NOT_REACHABLE;
1178     }
1179 
1180     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
getActionFromCFAction(int action)1181     private int getActionFromCFAction(int action) {
1182         switch(action) {
1183             case CF_ACTION_DISABLE: return ImsUtInterface.ACTION_DEACTIVATION;
1184             case CF_ACTION_ENABLE: return ImsUtInterface.ACTION_ACTIVATION;
1185             case CF_ACTION_ERASURE: return ImsUtInterface.ACTION_ERASURE;
1186             case CF_ACTION_REGISTRATION: return ImsUtInterface.ACTION_REGISTRATION;
1187             default:
1188                 break;
1189         }
1190 
1191         return ImsUtInterface.INVALID;
1192     }
1193 
1194     @Override
getOutgoingCallerIdDisplay(Message onComplete)1195     public void getOutgoingCallerIdDisplay(Message onComplete) {
1196         if (DBG) logd("getCLIR");
1197         Message resp;
1198         SS ss = new SS(onComplete);
1199         resp = obtainMessage(EVENT_GET_CLIR_DONE, ss);
1200 
1201         try {
1202             ImsUtInterface ut = mCT.getUtInterface();
1203             ut.queryCLIR(resp);
1204         } catch (ImsException e) {
1205             sendErrorResponse(onComplete, e);
1206         }
1207     }
1208 
1209     @Override
setOutgoingCallerIdDisplay(int clirMode, Message onComplete)1210     public void setOutgoingCallerIdDisplay(int clirMode, Message onComplete) {
1211         if (DBG) logd("setCLIR action= " + clirMode);
1212         Message resp;
1213         // Packing CLIR value in the message. This will be required for
1214         // SharedPreference caching, if the message comes back as part of
1215         // a success response.
1216         SS ss = new SS(clirMode, onComplete);
1217         resp = obtainMessage(EVENT_SET_CLIR_DONE, ss);
1218         try {
1219             ImsUtInterface ut = mCT.getUtInterface();
1220             ut.updateCLIR(clirMode, resp);
1221         } catch (ImsException e) {
1222             sendErrorResponse(onComplete, e);
1223         }
1224     }
1225 
1226     @Override
queryCLIP(Message onComplete)1227     public void queryCLIP(Message onComplete) {
1228         Message resp;
1229         SS ss = new SS(onComplete);
1230         resp = obtainMessage(EVENT_GET_CLIP_DONE, ss);
1231 
1232         try {
1233             Rlog.d(LOG_TAG, "ut.queryCLIP");
1234             ImsUtInterface ut = mCT.getUtInterface();
1235             ut.queryCLIP(resp);
1236         } catch (ImsException e) {
1237             sendErrorResponse(onComplete, e);
1238         }
1239     }
1240 
1241     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
1242     @Override
getCallForwardingOption(int commandInterfaceCFReason, Message onComplete)1243     public void getCallForwardingOption(int commandInterfaceCFReason,
1244             Message onComplete) {
1245         getCallForwardingOption(commandInterfaceCFReason,
1246                 SERVICE_CLASS_VOICE, onComplete);
1247     }
1248 
1249     @Override
getCallForwardingOption(int commandInterfaceCFReason, int serviceClass, Message onComplete)1250     public void getCallForwardingOption(int commandInterfaceCFReason, int serviceClass,
1251             Message onComplete) {
1252         if (DBG) logd("getCallForwardingOption reason=" + commandInterfaceCFReason);
1253         if (isValidCommandInterfaceCFReason(commandInterfaceCFReason)) {
1254             if (DBG) logd("requesting call forwarding query.");
1255             Message resp;
1256             SS ss = new SS(commandInterfaceCFReason, serviceClass, onComplete);
1257             resp = obtainMessage(EVENT_GET_CALL_FORWARD_DONE, ss);
1258 
1259             try {
1260                 ImsUtInterface ut = mCT.getUtInterface();
1261                 ut.queryCallForward(getConditionFromCFReason(commandInterfaceCFReason), null, resp);
1262             } catch (ImsException e) {
1263                 sendErrorResponse(onComplete, e);
1264             }
1265         } else if (onComplete != null) {
1266             sendErrorResponse(onComplete);
1267         }
1268     }
1269 
1270     @Override
setCallForwardingOption(int commandInterfaceCFAction, int commandInterfaceCFReason, String dialingNumber, int timerSeconds, Message onComplete)1271     public void setCallForwardingOption(int commandInterfaceCFAction,
1272             int commandInterfaceCFReason,
1273             String dialingNumber,
1274             int timerSeconds,
1275             Message onComplete) {
1276         setCallForwardingOption(commandInterfaceCFAction, commandInterfaceCFReason, dialingNumber,
1277                 CommandsInterface.SERVICE_CLASS_VOICE, timerSeconds, onComplete);
1278     }
1279 
1280     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
1281     @Override
setCallForwardingOption(int commandInterfaceCFAction, int commandInterfaceCFReason, String dialingNumber, int serviceClass, int timerSeconds, Message onComplete)1282     public void setCallForwardingOption(int commandInterfaceCFAction,
1283             int commandInterfaceCFReason,
1284             String dialingNumber,
1285             int serviceClass,
1286             int timerSeconds,
1287             Message onComplete) {
1288         if (DBG) {
1289             logd("setCallForwardingOption action=" + commandInterfaceCFAction
1290                     + ", reason=" + commandInterfaceCFReason + " serviceClass=" + serviceClass);
1291         }
1292         if ((isValidCommandInterfaceCFAction(commandInterfaceCFAction)) &&
1293                 (isValidCommandInterfaceCFReason(commandInterfaceCFReason))) {
1294             Message resp;
1295             SS ss = new SS(commandInterfaceCFAction, commandInterfaceCFReason,
1296                     dialingNumber, serviceClass, timerSeconds, onComplete);
1297             resp = obtainMessage(EVENT_SET_CALL_FORWARD_DONE, ss);
1298 
1299             try {
1300                 ImsUtInterface ut = mCT.getUtInterface();
1301                 ut.updateCallForward(getActionFromCFAction(commandInterfaceCFAction),
1302                         getConditionFromCFReason(commandInterfaceCFReason),
1303                         dialingNumber,
1304                         serviceClass,
1305                         timerSeconds,
1306                         resp);
1307             } catch (ImsException e) {
1308                 sendErrorResponse(onComplete, e);
1309             }
1310         } else if (onComplete != null) {
1311             sendErrorResponse(onComplete);
1312         }
1313     }
1314 
1315     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
1316     @Override
getCallWaiting(Message onComplete)1317     public void getCallWaiting(Message onComplete) {
1318         if (DBG) logd("getCallWaiting");
1319         Message resp;
1320         SS ss = new SS(onComplete);
1321         resp = obtainMessage(EVENT_GET_CALL_WAITING_DONE, ss);
1322 
1323         try {
1324             ImsUtInterface ut = mCT.getUtInterface();
1325             ut.queryCallWaiting(resp);
1326         } catch (ImsException e) {
1327             sendErrorResponse(onComplete, e);
1328         }
1329     }
1330 
1331     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
1332     @Override
setCallWaiting(boolean enable, Message onComplete)1333     public void setCallWaiting(boolean enable, Message onComplete) {
1334         int serviceClass = CommandsInterface.SERVICE_CLASS_VOICE;
1335         CarrierConfigManager configManager = (CarrierConfigManager)
1336                 getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
1337         PersistableBundle b = configManager.getConfigForSubId(getSubId());
1338         if (b != null) {
1339             serviceClass = b.getInt(CarrierConfigManager.KEY_CALL_WAITING_SERVICE_CLASS_INT,
1340                     CommandsInterface.SERVICE_CLASS_VOICE);
1341         }
1342         setCallWaiting(enable, serviceClass, onComplete);
1343     }
1344 
setCallWaiting(boolean enable, int serviceClass, Message onComplete)1345     public void setCallWaiting(boolean enable, int serviceClass, Message onComplete) {
1346         if (DBG) logd("setCallWaiting enable=" + enable);
1347         Message resp;
1348         SS ss = new SS(enable, serviceClass, onComplete);
1349         resp = obtainMessage(EVENT_SET_CALL_WAITING_DONE, ss);
1350 
1351         try {
1352             ImsUtInterface ut = mCT.getUtInterface();
1353             ut.updateCallWaiting(enable, serviceClass, resp);
1354         } catch (ImsException e) {
1355             sendErrorResponse(onComplete, e);
1356         }
1357     }
1358 
getCBTypeFromFacility(String facility)1359     private int getCBTypeFromFacility(String facility) {
1360         if (CB_FACILITY_BAOC.equals(facility)) {
1361             return ImsUtImplBase.CALL_BARRING_ALL_OUTGOING;
1362         } else if (CB_FACILITY_BAOIC.equals(facility)) {
1363             return ImsUtImplBase.CALL_BARRING_OUTGOING_INTL;
1364         } else if (CB_FACILITY_BAOICxH.equals(facility)) {
1365             return ImsUtImplBase.CALL_BARRING_OUTGOING_INTL_EXCL_HOME;
1366         } else if (CB_FACILITY_BAIC.equals(facility)) {
1367             return ImsUtImplBase.CALL_BARRING_ALL_INCOMING;
1368         } else if (CB_FACILITY_BAICr.equals(facility)) {
1369             return ImsUtImplBase.CALL_BLOCKING_INCOMING_WHEN_ROAMING;
1370         } else if (CB_FACILITY_BA_ALL.equals(facility)) {
1371             return ImsUtImplBase.CALL_BARRING_ALL;
1372         } else if (CB_FACILITY_BA_MO.equals(facility)) {
1373             return ImsUtImplBase.CALL_BARRING_OUTGOING_ALL_SERVICES;
1374         } else if (CB_FACILITY_BA_MT.equals(facility)) {
1375             return ImsUtImplBase.CALL_BARRING_INCOMING_ALL_SERVICES;
1376         } else if (CB_FACILITY_BIC_ACR.equals(facility)) {
1377             return ImsUtImplBase.CALL_BARRING_ANONYMOUS_INCOMING;
1378         }
1379 
1380         return 0;
1381     }
1382 
getCallBarring(String facility, Message onComplete)1383     public void getCallBarring(String facility, Message onComplete) {
1384         getCallBarring(facility, onComplete, CommandsInterface.SERVICE_CLASS_VOICE);
1385     }
1386 
getCallBarring(String facility, Message onComplete, int serviceClass)1387     public void getCallBarring(String facility, Message onComplete, int serviceClass) {
1388         getCallBarring(facility, "", onComplete, serviceClass);
1389     }
1390 
1391     @Override
getCallBarring(String facility, String password, Message onComplete, int serviceClass)1392     public void getCallBarring(String facility, String password, Message onComplete,
1393             int serviceClass) {
1394         if (DBG) logd("getCallBarring facility=" + facility + ", serviceClass = " + serviceClass);
1395         Message resp;
1396         SS ss = new SS(facility, password, serviceClass, onComplete);
1397         resp = obtainMessage(EVENT_GET_CALL_BARRING_DONE, ss);
1398 
1399         try {
1400             ImsUtInterface ut = mCT.getUtInterface();
1401             // password is not required with Ut interface
1402             ut.queryCallBarring(getCBTypeFromFacility(facility), resp, serviceClass);
1403         } catch (ImsException e) {
1404             sendErrorResponse(onComplete, e);
1405         }
1406     }
1407 
setCallBarring(String facility, boolean lockState, String password, Message onComplete)1408     public void setCallBarring(String facility, boolean lockState, String password,
1409             Message onComplete) {
1410         setCallBarring(facility, lockState, password, onComplete,
1411                 CommandsInterface.SERVICE_CLASS_VOICE);
1412     }
1413 
1414     @Override
setCallBarring(String facility, boolean lockState, String password, Message onComplete, int serviceClass)1415     public void setCallBarring(String facility, boolean lockState, String password,
1416             Message onComplete, int serviceClass) {
1417         if (DBG) {
1418             logd("setCallBarring facility=" + facility
1419                     + ", lockState=" + lockState + ", serviceClass = " + serviceClass);
1420         }
1421         Message resp;
1422         SS ss = new SS(facility, lockState, password, serviceClass, onComplete);
1423         resp = obtainMessage(EVENT_SET_CALL_BARRING_DONE, ss);
1424 
1425         int action;
1426         if (lockState) {
1427             action = CommandsInterface.CF_ACTION_ENABLE;
1428         }
1429         else {
1430             action = CommandsInterface.CF_ACTION_DISABLE;
1431         }
1432 
1433         try {
1434             ImsUtInterface ut = mCT.getUtInterface();
1435             ut.updateCallBarring(getCBTypeFromFacility(facility), action,
1436                     resp, null, serviceClass, password);
1437         } catch (ImsException e) {
1438             sendErrorResponse(onComplete, e);
1439         }
1440     }
1441 
1442     @Override
sendUssdResponse(String ussdMessge)1443     public void sendUssdResponse(String ussdMessge) {
1444         logd("sendUssdResponse");
1445         ImsPhoneMmiCode mmi = ImsPhoneMmiCode.newFromUssdUserInput(ussdMessge, this);
1446         mPendingMMIs.add(mmi);
1447         mMmiRegistrants.notifyRegistrants(new AsyncResult(null, mmi, null));
1448         mmi.sendUssd(ussdMessge);
1449     }
1450 
sendUSSD(String ussdString, Message response)1451     public void sendUSSD(String ussdString, Message response) {
1452         Rlog.d(LOG_TAG, "sendUssd ussdString = " + ussdString);
1453         mLastDialString = ussdString;
1454         mCT.sendUSSD(ussdString, response);
1455     }
1456 
1457     @Override
cancelUSSD(Message msg)1458     public void cancelUSSD(Message msg) {
1459         mCT.cancelUSSD(msg);
1460     }
1461 
1462     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
sendErrorResponse(Message onComplete)1463     private void sendErrorResponse(Message onComplete) {
1464         logd("sendErrorResponse");
1465         if (onComplete != null) {
1466             AsyncResult.forMessage(onComplete, null,
1467                     new CommandException(CommandException.Error.GENERIC_FAILURE));
1468             onComplete.sendToTarget();
1469         }
1470     }
1471 
1472     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
1473     @VisibleForTesting
sendErrorResponse(Message onComplete, Throwable e)1474     public void sendErrorResponse(Message onComplete, Throwable e) {
1475         logd("sendErrorResponse");
1476         if (onComplete != null) {
1477             AsyncResult.forMessage(onComplete, null, getCommandException(e));
1478             onComplete.sendToTarget();
1479         }
1480     }
1481 
getCommandException(int code, String errorString)1482     private CommandException getCommandException(int code, String errorString) {
1483         logd("getCommandException code= " + code + ", errorString= " + errorString);
1484         CommandException.Error error = CommandException.Error.GENERIC_FAILURE;
1485 
1486         switch(code) {
1487             case ImsReasonInfo.CODE_UT_NOT_SUPPORTED:
1488                 // fall through
1489             case ImsReasonInfo.CODE_UT_OPERATION_NOT_ALLOWED:
1490                 // not allowed is reported by operators when the network doesn't support a specific
1491                 // type of barring.
1492                 error = CommandException.Error.REQUEST_NOT_SUPPORTED;
1493                 break;
1494             case ImsReasonInfo.CODE_UT_CB_PASSWORD_MISMATCH:
1495                 error = CommandException.Error.PASSWORD_INCORRECT;
1496                 break;
1497             case ImsReasonInfo.CODE_UT_SERVICE_UNAVAILABLE:
1498                 error = CommandException.Error.RADIO_NOT_AVAILABLE;
1499                 break;
1500             case ImsReasonInfo.CODE_FDN_BLOCKED:
1501                 error = CommandException.Error.FDN_CHECK_FAILURE;
1502                 break;
1503             case ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_DIAL:
1504                 error = CommandException.Error.SS_MODIFIED_TO_DIAL;
1505                 break;
1506             case ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_USSD:
1507                 error = CommandException.Error.SS_MODIFIED_TO_USSD;
1508                 break;
1509             case ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_SS:
1510                 error = CommandException.Error.SS_MODIFIED_TO_SS;
1511                 break;
1512             case ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO:
1513                 error = CommandException.Error.SS_MODIFIED_TO_DIAL_VIDEO;
1514                 break;
1515             default:
1516                 break;
1517         }
1518 
1519         return new CommandException(error, errorString);
1520     }
1521 
getCommandException(Throwable e)1522     private CommandException getCommandException(Throwable e) {
1523         CommandException ex = null;
1524 
1525         if (e instanceof ImsException) {
1526             ex = getCommandException(((ImsException)e).getCode(), e.getMessage());
1527         } else {
1528             logd("getCommandException generic failure");
1529             ex = new CommandException(CommandException.Error.GENERIC_FAILURE);
1530         }
1531         return ex;
1532     }
1533 
1534     private void
onNetworkInitiatedUssd(ImsPhoneMmiCode mmi)1535     onNetworkInitiatedUssd(ImsPhoneMmiCode mmi) {
1536         logd("onNetworkInitiatedUssd");
1537         mMmiCompleteRegistrants.notifyRegistrants(
1538             new AsyncResult(null, mmi, null));
1539     }
1540 
1541     /* package */
onIncomingUSSD(int ussdMode, String ussdMessage)1542     void onIncomingUSSD(int ussdMode, String ussdMessage) {
1543         if (DBG) logd("onIncomingUSSD ussdMode=" + ussdMode);
1544 
1545         boolean isUssdError;
1546         boolean isUssdRequest;
1547 
1548         isUssdRequest
1549             = (ussdMode == CommandsInterface.USSD_MODE_REQUEST);
1550 
1551         isUssdError
1552             = (ussdMode != CommandsInterface.USSD_MODE_NOTIFY
1553                 && ussdMode != CommandsInterface.USSD_MODE_REQUEST);
1554 
1555         ImsPhoneMmiCode found = null;
1556         for (int i = 0, s = mPendingMMIs.size() ; i < s; i++) {
1557             if(mPendingMMIs.get(i).isPendingUSSD()) {
1558                 found = mPendingMMIs.get(i);
1559                 break;
1560             }
1561         }
1562 
1563         if (found != null) {
1564             // Complete pending USSD
1565             if (isUssdError) {
1566                 found.onUssdFinishedError();
1567             } else {
1568                 found.onUssdFinished(ussdMessage, isUssdRequest);
1569             }
1570         } else if (!isUssdError && !TextUtils.isEmpty(ussdMessage)) {
1571                 // pending USSD not found
1572                 // The network may initiate its own USSD request
1573 
1574                 // ignore everything that isnt a Notify or a Request
1575                 // also, discard if there is no message to present
1576                 ImsPhoneMmiCode mmi;
1577                 mmi = ImsPhoneMmiCode.newNetworkInitiatedUssd(ussdMessage,
1578                         isUssdRequest,
1579                         this);
1580                 onNetworkInitiatedUssd(mmi);
1581         } else if (isUssdError) {
1582             ImsPhoneMmiCode mmi;
1583             mmi = ImsPhoneMmiCode.newNetworkInitiatedUssd(ussdMessage,
1584                     true,
1585                     this);
1586             mmi.onUssdFinishedError();
1587         }
1588     }
1589 
1590     /**
1591      * Removes the given MMI from the pending list and notifies
1592      * registrants that it is complete.
1593      * @param mmi MMI that is done
1594      */
1595     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
onMMIDone(ImsPhoneMmiCode mmi)1596     public void onMMIDone(ImsPhoneMmiCode mmi) {
1597         /* Only notify complete if it's on the pending list.
1598          * Otherwise, it's already been handled (eg, previously canceled).
1599          * The exception is cancellation of an incoming USSD-REQUEST, which is
1600          * not on the list.
1601          */
1602         logd("onMMIDone: mmi=" + mmi);
1603         if (mPendingMMIs.remove(mmi) || mmi.isUssdRequest() || mmi.isSsInfo()) {
1604             ResultReceiver receiverCallback = mmi.getUssdCallbackReceiver();
1605             if (receiverCallback != null) {
1606                 int returnCode = (mmi.getState() ==  MmiCode.State.COMPLETE) ?
1607                         TelephonyManager.USSD_RETURN_SUCCESS : TelephonyManager.USSD_RETURN_FAILURE;
1608                 sendUssdResponse(mmi.getDialString(), mmi.getMessage(), returnCode,
1609                         receiverCallback );
1610             } else {
1611                 logv("onMMIDone: notifyRegistrants");
1612                 mMmiCompleteRegistrants.notifyRegistrants(
1613                     new AsyncResult(null, mmi, null));
1614             }
1615         }
1616     }
1617 
1618     @Override
getHandoverConnection()1619     public ArrayList<Connection> getHandoverConnection() {
1620         ArrayList<Connection> connList = new ArrayList<Connection>();
1621         // Add all foreground call connections
1622         connList.addAll(getForegroundCall().getConnections());
1623         // Add all background call connections
1624         connList.addAll(getBackgroundCall().getConnections());
1625         // Add all background call connections
1626         connList.addAll(getRingingCall().getConnections());
1627         if (connList.size() > 0) {
1628             return connList;
1629         } else {
1630             return null;
1631         }
1632     }
1633 
1634     @Override
notifySrvccState(int state)1635     public void notifySrvccState(int state) {
1636         mCT.notifySrvccState(state);
1637     }
1638 
1639     /* package */ void
initiateSilentRedial()1640     initiateSilentRedial() {
1641         initiateSilentRedial(false, EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED);
1642     }
1643 
1644     /* package */ void
initiateSilentRedial(boolean isEmergency, int eccCategory)1645     initiateSilentRedial(boolean isEmergency, int eccCategory) {
1646         DialArgs dialArgs = new DialArgs.Builder()
1647                                         .setIsEmergency(isEmergency)
1648                                         .setEccCategory(eccCategory)
1649                                         .build();
1650         int cause = CallFailCause.LOCAL_CALL_CS_RETRY_REQUIRED;
1651         AsyncResult ar = new AsyncResult(null,
1652                                          new SilentRedialParam(mLastDialString, cause, dialArgs),
1653                                          null);
1654         if (ar != null) {
1655             // There is a race condition that can happen in some cases:
1656             // (Main thread) dial start
1657             // (Binder Thread) onCallSessionFailed
1658             // (Binder Thread) schedule a redial for CS on the main thread
1659             // (Main Thread) dial finish
1660             // (Main Thread) schedule to associate ImsPhoneConnection with
1661             //               GsmConnection on the main thread
1662             // If scheduling the CS redial occurs before the command to schedule the
1663             // ImsPhoneConnection to be  associated with the GsmConnection, the CS redial will occur
1664             // before GsmConnection has had callbacks to ImsPhone correctly updated. This will cause
1665             // Callbacks back to GsmCdmaPhone to never be set up correctly and we will lose track of
1666             // the instance.
1667             // Instead, schedule this redial to happen on the main thread, so that we know dial has
1668             // finished before scheduling a redial:
1669             // (Main thread) dial start
1670             // (Binder Thread) onCallSessionFailed -> move notify registrants to main thread
1671             // (Main Thread) dial finish
1672             // (Main Thread) schedule on main thread to associate ImsPhoneConnection with
1673             //               GsmConnection
1674             // (Main Thread) schedule a redial for CS
1675             mContext.getMainExecutor().execute(() -> {
1676                 logd("initiateSilentRedial: notifying registrants, isEmergency=" + isEmergency
1677                         + ", eccCategory=" + eccCategory);
1678                 mSilentRedialRegistrants.notifyRegistrants(ar);
1679             });
1680         }
1681     }
1682 
registerForImsRegistrationChanges(Handler h, int what, Object obj)1683     public void registerForImsRegistrationChanges(Handler h, int what, Object obj) {
1684         mImsRegistrationUpdateRegistrants.addUnique(h, what, obj);
1685     }
1686 
unregisterForImsRegistrationChanges(Handler h)1687     public void unregisterForImsRegistrationChanges(Handler h) {
1688         mImsRegistrationUpdateRegistrants.remove(h);
1689     }
1690 
1691     @Override
registerForSilentRedial(Handler h, int what, Object obj)1692     public void registerForSilentRedial(Handler h, int what, Object obj) {
1693         mSilentRedialRegistrants.addUnique(h, what, obj);
1694     }
1695 
1696     @Override
unregisterForSilentRedial(Handler h)1697     public void unregisterForSilentRedial(Handler h) {
1698         mSilentRedialRegistrants.remove(h);
1699     }
1700 
1701     @Override
registerForSuppServiceNotification(Handler h, int what, Object obj)1702     public void registerForSuppServiceNotification(Handler h, int what, Object obj) {
1703         mSsnRegistrants.addUnique(h, what, obj);
1704     }
1705 
1706     @Override
unregisterForSuppServiceNotification(Handler h)1707     public void unregisterForSuppServiceNotification(Handler h) {
1708         mSsnRegistrants.remove(h);
1709     }
1710 
1711     @Override
getSubId()1712     public int getSubId() {
1713         return mDefaultPhone.getSubId();
1714     }
1715 
1716     @Override
getPhoneId()1717     public int getPhoneId() {
1718         return mDefaultPhone.getPhoneId();
1719     }
1720 
getCallForwardInfo(ImsCallForwardInfo info)1721     private CallForwardInfo getCallForwardInfo(ImsCallForwardInfo info) {
1722         CallForwardInfo cfInfo = new CallForwardInfo();
1723         cfInfo.status = info.getStatus();
1724         cfInfo.reason = getCFReasonFromCondition(info.getCondition());
1725         cfInfo.serviceClass = SERVICE_CLASS_VOICE;
1726         cfInfo.toa = info.getToA();
1727         cfInfo.number = info.getNumber();
1728         cfInfo.timeSeconds = info.getTimeSeconds();
1729         return cfInfo;
1730     }
1731 
1732     @Override
getLine1Number()1733     public String getLine1Number() {
1734         return mDefaultPhone.getLine1Number();
1735     }
1736 
1737     /**
1738      * Used to Convert ImsCallForwardInfo[] to CallForwardInfo[].
1739      * Update received call forward status to default IccRecords.
1740      */
handleCfQueryResult(ImsCallForwardInfo[] infos)1741     public CallForwardInfo[] handleCfQueryResult(ImsCallForwardInfo[] infos) {
1742         CallForwardInfo[] cfInfos = null;
1743 
1744         if (infos != null && infos.length != 0) {
1745             cfInfos = new CallForwardInfo[infos.length];
1746         }
1747 
1748         if (infos == null || infos.length == 0) {
1749             // Assume the default is not active
1750             // Set unconditional CFF in SIM to false
1751             setVoiceCallForwardingFlag(getIccRecords(), 1, false, null);
1752         } else {
1753             for (int i = 0, s = infos.length; i < s; i++) {
1754                 if (infos[i].getCondition() == ImsUtInterface.CDIV_CF_UNCONDITIONAL) {
1755                     setVoiceCallForwardingFlag(getIccRecords(), 1, (infos[i].getStatus() == 1),
1756                         infos[i].getNumber());
1757                 }
1758                 cfInfos[i] = getCallForwardInfo(infos[i]);
1759             }
1760         }
1761 
1762         return cfInfos;
1763     }
1764 
handleCbQueryResult(ImsSsInfo[] infos)1765     private int[] handleCbQueryResult(ImsSsInfo[] infos) {
1766         int[] cbInfos = new int[1];
1767         cbInfos[0] = SERVICE_CLASS_NONE;
1768 
1769         if (infos[0].getStatus() == 1) {
1770             cbInfos[0] = SERVICE_CLASS_VOICE;
1771         }
1772 
1773         return cbInfos;
1774     }
1775 
handleCwQueryResult(ImsSsInfo[] infos)1776     private int[] handleCwQueryResult(ImsSsInfo[] infos) {
1777         int[] cwInfos = new int[2];
1778         cwInfos[0] = 0;
1779 
1780         if (infos[0].getStatus() == 1) {
1781             cwInfos[0] = 1;
1782             cwInfos[1] = SERVICE_CLASS_VOICE;
1783         }
1784 
1785         return cwInfos;
1786     }
1787 
1788     private void
sendResponse(Message onComplete, Object result, Throwable e)1789     sendResponse(Message onComplete, Object result, Throwable e) {
1790         if (onComplete != null) {
1791             CommandException ex = null;
1792             if (e != null) {
1793                 ex = getCommandException(e);
1794             }
1795             AsyncResult.forMessage(onComplete, result, ex);
1796             onComplete.sendToTarget();
1797         }
1798     }
1799 
updateDataServiceState()1800     private void updateDataServiceState() {
1801         if (mSS != null && mDefaultPhone.getServiceStateTracker() != null
1802                 && mDefaultPhone.getServiceStateTracker().mSS != null) {
1803             ServiceState ss = mDefaultPhone.getServiceStateTracker().mSS;
1804             mSS.setDataRegState(ss.getDataRegistrationState());
1805             List<NetworkRegistrationInfo> nriList =
1806                     ss.getNetworkRegistrationInfoListForDomain(NetworkRegistrationInfo.DOMAIN_PS);
1807             for (NetworkRegistrationInfo nri : nriList) {
1808                 mSS.addNetworkRegistrationInfo(nri);
1809             }
1810 
1811             mSS.setIwlanPreferred(ss.isIwlanPreferred());
1812             logd("updateDataServiceState: defSs = " + ss + " imsSs = " + mSS);
1813         }
1814     }
1815 
isCsRetryException(Throwable e)1816     boolean isCsRetryException(Throwable e) {
1817         if ((e != null) && (e instanceof ImsException)
1818                 && (((ImsException)e).getCode()
1819                     == ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED)) {
1820             return true;
1821         }
1822         return false;
1823     }
1824 
setCsfbBundle(boolean isCsRetry)1825     private Bundle setCsfbBundle(boolean isCsRetry) {
1826         Bundle b = new Bundle();
1827         b.putBoolean(CS_FALLBACK_SS, isCsRetry);
1828         return b;
1829     }
1830 
sendResponseOrRetryOnCsfbSs(SS ss, int what, Throwable e, Object obj)1831     private void sendResponseOrRetryOnCsfbSs(SS ss, int what, Throwable e, Object obj) {
1832         if (!isCsRetryException(e)) {
1833             sendResponse(ss.mOnComplete, obj, e);
1834             return;
1835         }
1836 
1837         Rlog.d(LOG_TAG, "Try CSFB: " + what);
1838         ss.mOnComplete.setData(setCsfbBundle(true));
1839 
1840         switch (what) {
1841             case EVENT_GET_CALL_FORWARD_DONE:
1842                 mDefaultPhone.getCallForwardingOption(ss.mCfReason,
1843                                                       ss.mServiceClass,
1844                                                       ss.mOnComplete);
1845                 break;
1846             case EVENT_SET_CALL_FORWARD_DONE:
1847                 mDefaultPhone.setCallForwardingOption(ss.mCfAction,
1848                                                       ss.mCfReason,
1849                                                       ss.mDialingNumber,
1850                                                       ss.mServiceClass,
1851                                                       ss.mTimerSeconds,
1852                                                       ss.mOnComplete);
1853                 break;
1854             case EVENT_GET_CALL_BARRING_DONE:
1855                 mDefaultPhone.getCallBarring(ss.mFacility,
1856                                              ss.mPassword,
1857                                              ss.mOnComplete,
1858                                              ss.mServiceClass);
1859                 break;
1860             case EVENT_SET_CALL_BARRING_DONE:
1861                 mDefaultPhone.setCallBarring(ss.mFacility,
1862                                              ss.mLockState,
1863                                              ss.mPassword,
1864                                              ss.mOnComplete,
1865                                              ss.mServiceClass);
1866                 break;
1867             case EVENT_GET_CALL_WAITING_DONE:
1868                 mDefaultPhone.getCallWaiting(ss.mOnComplete);
1869                 break;
1870             case EVENT_SET_CALL_WAITING_DONE:
1871                 mDefaultPhone.setCallWaiting(ss.mEnable,
1872                                              ss.mServiceClass,
1873                                              ss.mOnComplete);
1874                 break;
1875             case EVENT_GET_CLIR_DONE:
1876                 mDefaultPhone.getOutgoingCallerIdDisplay(ss.mOnComplete);
1877                 break;
1878             case EVENT_SET_CLIR_DONE:
1879                 mDefaultPhone.setOutgoingCallerIdDisplay(ss.mClirMode, ss.mOnComplete);
1880                 break;
1881             case EVENT_GET_CLIP_DONE:
1882                 mDefaultPhone.queryCLIP(ss.mOnComplete);
1883                 break;
1884             default:
1885                 break;
1886         }
1887     }
1888 
1889     @Override
handleMessage(Message msg)1890     public void handleMessage(Message msg) {
1891         AsyncResult ar = (AsyncResult) msg.obj;
1892         Message onComplete;
1893         SS ss = null;
1894         if (ar != null && ar.userObj instanceof SS) {
1895             ss = (SS) ar.userObj;
1896         }
1897 
1898         if (DBG) logd("handleMessage what=" + msg.what);
1899         switch (msg.what) {
1900             case EVENT_SET_CALL_FORWARD_DONE:
1901                 if (ar.exception == null && ss != null &&
1902                     (ss.mCfReason == CF_REASON_UNCONDITIONAL)) {
1903                     setVoiceCallForwardingFlag(getIccRecords(), 1, isCfEnable(ss.mCfAction),
1904                                                ss.mDialingNumber);
1905                 }
1906                 if (ss != null) {
1907                     sendResponseOrRetryOnCsfbSs(ss, msg.what, ar.exception, null);
1908                 }
1909                 break;
1910 
1911             case EVENT_GET_CALL_FORWARD_DONE:
1912                 CallForwardInfo[] cfInfos = null;
1913                 if (ar.exception == null) {
1914                     cfInfos = handleCfQueryResult((ImsCallForwardInfo[])ar.result);
1915                 }
1916                 if (ss != null) {
1917                     sendResponseOrRetryOnCsfbSs(ss, msg.what, ar.exception, cfInfos);
1918                 }
1919                 break;
1920 
1921             case EVENT_GET_CALL_BARRING_DONE:
1922             case EVENT_GET_CALL_WAITING_DONE:
1923                 int[] ssInfos = null;
1924                 if (ar.exception == null) {
1925                     if (msg.what == EVENT_GET_CALL_BARRING_DONE) {
1926                         ssInfos = handleCbQueryResult((ImsSsInfo[])ar.result);
1927                     } else if (msg.what == EVENT_GET_CALL_WAITING_DONE) {
1928                         ssInfos = handleCwQueryResult((ImsSsInfo[])ar.result);
1929                     }
1930                 }
1931                 if (ss != null) {
1932                     sendResponseOrRetryOnCsfbSs(ss, msg.what, ar.exception, ssInfos);
1933                 }
1934                 break;
1935 
1936             case EVENT_GET_CLIR_DONE:
1937                 ImsSsInfo ssInfo = (ImsSsInfo) ar.result;
1938                 int[] clirInfo = null;
1939                 if (ssInfo != null) {
1940                     // Unfortunately callers still use the old {n,m} format of ImsSsInfo, so return
1941                     // that for compatibility
1942                     clirInfo = ssInfo.getCompatArray(ImsSsData.SS_CLIR);
1943                 }
1944                 if (ss != null) {
1945                     sendResponseOrRetryOnCsfbSs(ss, msg.what, ar.exception, clirInfo);
1946                 }
1947                 break;
1948 
1949             case EVENT_GET_CLIP_DONE:
1950                 ImsSsInfo ssInfoResp = null;
1951                 if (ar.exception == null && ar.result instanceof ImsSsInfo) {
1952                     ssInfoResp = (ImsSsInfo) ar.result;
1953                 }
1954                 if (ss != null) {
1955                     sendResponseOrRetryOnCsfbSs(ss, msg.what, ar.exception, ssInfoResp);
1956                 }
1957                 break;
1958 
1959             case EVENT_SET_CLIR_DONE:
1960                 if (ar.exception == null) {
1961                     if (ss != null) {
1962                         saveClirSetting(ss.mClirMode);
1963                     }
1964                 }
1965                  // (Intentional fallthrough)
1966             case EVENT_SET_CALL_BARRING_DONE:
1967             case EVENT_SET_CALL_WAITING_DONE:
1968                 if (ss != null) {
1969                     sendResponseOrRetryOnCsfbSs(ss, msg.what, ar.exception, null);
1970                 }
1971                 break;
1972 
1973             case EVENT_DEFAULT_PHONE_DATA_STATE_CHANGED:
1974                 if (DBG) logd("EVENT_DEFAULT_PHONE_DATA_STATE_CHANGED");
1975                 updateDataServiceState();
1976                 break;
1977 
1978             case EVENT_SERVICE_STATE_CHANGED:
1979                 if (VDBG) logd("EVENT_SERVICE_STATE_CHANGED");
1980                 ar = (AsyncResult) msg.obj;
1981                 ServiceState newServiceState = (ServiceState) ar.result;
1982                 updateRoamingState(newServiceState);
1983                 break;
1984             case EVENT_VOICE_CALL_ENDED:
1985                 if (DBG) logd("Voice call ended. Handle pending updateRoamingState.");
1986                 mCT.unregisterForVoiceCallEnded(this);
1987                 // Get the current unmodified ServiceState from the tracker, as it has more info
1988                 // about the cell roaming state.
1989                 ServiceStateTracker sst = getDefaultPhone().getServiceStateTracker();
1990                 if (sst != null) {
1991                     updateRoamingState(sst.mSS);
1992                 }
1993                 break;
1994             case EVENT_INITIATE_VOLTE_SILENT_REDIAL: {
1995                 // This is a CS -> IMS redial
1996                 if (VDBG) logd("EVENT_INITIATE_VOLTE_SILENT_REDIAL");
1997                 ar = (AsyncResult) msg.obj;
1998                 if (ar.exception == null && ar.result != null) {
1999                     SilentRedialParam result = (SilentRedialParam) ar.result;
2000                     String dialString = result.dialString;
2001                     int causeCode = result.causeCode;
2002                     DialArgs dialArgs = result.dialArgs;
2003                     if (VDBG) logd("dialString=" + dialString + " causeCode=" + causeCode);
2004 
2005                     try {
2006                         Connection cn = dial(dialString,
2007                                 updateDialArgsForVolteSilentRedial(dialArgs, causeCode));
2008                         // The GSM/CDMA Connection that is owned by the GsmCdmaPhone is currently
2009                         // the one with a callback registered to TelephonyConnection. Notify the
2010                         // redial happened over that Phone so that it can be replaced with the
2011                         // new ImsPhoneConnection.
2012                         Rlog.d(LOG_TAG, "Notify volte redial connection changed cn: " + cn);
2013                         if (mDefaultPhone != null) {
2014                             // don't care it is null or not.
2015                             mDefaultPhone.notifyRedialConnectionChanged(cn);
2016                         }
2017                     } catch (CallStateException e) {
2018                         Rlog.e(LOG_TAG, "volte silent redial failed: " + e);
2019                         if (mDefaultPhone != null) {
2020                             mDefaultPhone.notifyRedialConnectionChanged(null);
2021                         }
2022                     }
2023                 } else {
2024                     if (VDBG) logd("EVENT_INITIATE_VOLTE_SILENT_REDIAL" +
2025                                    " has exception or empty result");
2026                 }
2027                 break;
2028             }
2029 
2030             default:
2031                 super.handleMessage(msg);
2032                 break;
2033         }
2034     }
2035 
2036     /**
2037      * Listen to the IMS ECBM state change
2038      */
2039     private ImsEcbmStateListener mImsEcbmStateListener =
2040             new ImsEcbmStateListener(mContext.getMainExecutor()) {
2041                 @Override
2042                 public void onECBMEntered(Executor executor) {
2043                     if (DBG) logd("onECBMEntered");
2044 
2045                     TelephonyUtils.runWithCleanCallingIdentity(()->
2046                             handleEnterEmergencyCallbackMode(), executor);
2047                 }
2048 
2049 
2050 
2051                 @Override
2052                 public void onECBMExited(Executor executor) {
2053                     if (DBG) logd("onECBMExited");
2054                     TelephonyUtils.runWithCleanCallingIdentity(()->
2055                             handleExitEmergencyCallbackMode(), executor);
2056                 }
2057             };
2058 
2059     @VisibleForTesting
getImsEcbmStateListener()2060     public ImsEcbmStateListener getImsEcbmStateListener() {
2061         return mImsEcbmStateListener;
2062     }
2063 
2064     @Override
isInEmergencyCall()2065     public boolean isInEmergencyCall() {
2066         return mCT.isInEmergencyCall();
2067     }
2068 
sendEmergencyCallbackModeChange()2069     private void sendEmergencyCallbackModeChange() {
2070         // Send an Intent
2071         Intent intent = new Intent(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED);
2072         intent.putExtra(TelephonyManager.EXTRA_PHONE_IN_ECM_STATE, isInEcm());
2073         SubscriptionManager.putPhoneIdAndSubIdExtra(intent, getPhoneId());
2074         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
2075         if (DBG) logd("sendEmergencyCallbackModeChange: isInEcm=" + isInEcm());
2076     }
2077 
2078     @Override
exitEmergencyCallbackMode()2079     public void exitEmergencyCallbackMode() {
2080         if (mWakeLock.isHeld()) {
2081             mWakeLock.release();
2082         }
2083         if (DBG) logd("exitEmergencyCallbackMode()");
2084 
2085         // Send a message which will invoke handleExitEmergencyCallbackMode
2086         ImsEcbm ecbm;
2087         try {
2088             ecbm = mCT.getEcbmInterface();
2089             ecbm.exitEmergencyCallbackMode();
2090         } catch (ImsException e) {
2091             e.printStackTrace();
2092         }
2093     }
2094 
2095     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
handleEnterEmergencyCallbackMode()2096     private void handleEnterEmergencyCallbackMode() {
2097         if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) {
2098             logd("DomainSelection enabled: ignore ECBM enter event.");
2099             return;
2100         }
2101         if (DBG) logd("handleEnterEmergencyCallbackMode,mIsPhoneInEcmState= " + isInEcm());
2102         // if phone is not in Ecm mode, and it's changed to Ecm mode
2103         if (!isInEcm()) {
2104             setIsInEcm(true);
2105             // notify change
2106             sendEmergencyCallbackModeChange();
2107             ((GsmCdmaPhone) mDefaultPhone).notifyEmergencyCallRegistrants(true);
2108 
2109             // Post this runnable so we will automatically exit
2110             // if no one invokes exitEmergencyCallbackMode() directly.
2111             long delayInMillis = TelephonyProperties.ecm_exit_timer()
2112                     .orElse(DEFAULT_ECM_EXIT_TIMER_VALUE);
2113             postDelayed(mExitEcmRunnable, delayInMillis);
2114             // We don't want to go to sleep while in Ecm
2115             mWakeLock.acquire();
2116         }
2117     }
2118 
2119     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
2120     @Override
handleExitEmergencyCallbackMode()2121     protected void handleExitEmergencyCallbackMode() {
2122         if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) {
2123             logd("DomainSelection enabled: ignore ECBM exit event.");
2124             return;
2125         }
2126         if (DBG) logd("handleExitEmergencyCallbackMode: mIsPhoneInEcmState = " + isInEcm());
2127 
2128         if (isInEcm()) {
2129             setIsInEcm(false);
2130         }
2131 
2132         // Remove pending exit Ecm runnable, if any
2133         removeCallbacks(mExitEcmRunnable);
2134 
2135         if (mEcmExitRespRegistrant != null) {
2136             mEcmExitRespRegistrant.notifyResult(Boolean.TRUE);
2137         }
2138 
2139         // release wakeLock
2140         if (mWakeLock.isHeld()) {
2141             mWakeLock.release();
2142         }
2143 
2144         // send an Intent
2145         sendEmergencyCallbackModeChange();
2146         ((GsmCdmaPhone) mDefaultPhone).notifyEmergencyCallRegistrants(false);
2147     }
2148 
2149     /**
2150      * Handle to cancel or restart Ecm timer in emergency call back mode if action is
2151      * CANCEL_ECM_TIMER, cancel Ecm timer and notify apps the timer is canceled; otherwise, restart
2152      * Ecm timer and notify apps the timer is restarted.
2153      */
handleTimerInEmergencyCallbackMode(int action)2154     void handleTimerInEmergencyCallbackMode(int action) {
2155         if (DomainSelectionResolver.getInstance().isDomainSelectionSupported()) return;
2156         switch (action) {
2157             case CANCEL_ECM_TIMER:
2158                 removeCallbacks(mExitEcmRunnable);
2159                 ((GsmCdmaPhone) mDefaultPhone).notifyEcbmTimerReset(Boolean.TRUE);
2160                 setEcmCanceledForEmergency(true /*isCanceled*/);
2161                 break;
2162             case RESTART_ECM_TIMER:
2163                 long delayInMillis = TelephonyProperties.ecm_exit_timer()
2164                         .orElse(DEFAULT_ECM_EXIT_TIMER_VALUE);
2165                 postDelayed(mExitEcmRunnable, delayInMillis);
2166                 ((GsmCdmaPhone) mDefaultPhone).notifyEcbmTimerReset(Boolean.FALSE);
2167                 setEcmCanceledForEmergency(false /*isCanceled*/);
2168                 break;
2169             default:
2170                 loge("handleTimerInEmergencyCallbackMode, unsupported action " + action);
2171         }
2172     }
2173 
2174     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
2175     @Override
setOnEcbModeExitResponse(Handler h, int what, Object obj)2176     public void setOnEcbModeExitResponse(Handler h, int what, Object obj) {
2177         mEcmExitRespRegistrant = new Registrant(h, what, obj);
2178     }
2179 
2180     @Override
unsetOnEcbModeExitResponse(Handler h)2181     public void unsetOnEcbModeExitResponse(Handler h) {
2182         mEcmExitRespRegistrant.clear();
2183     }
2184 
onFeatureCapabilityChanged()2185     public void onFeatureCapabilityChanged() {
2186         mDefaultPhone.getServiceStateTracker().onImsCapabilityChanged();
2187     }
2188 
2189     @Override
isImsCapabilityAvailable(int capability, int regTech)2190     public boolean isImsCapabilityAvailable(int capability, int regTech) throws ImsException {
2191         return mCT.isImsCapabilityAvailable(capability, regTech);
2192     }
2193 
2194     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
2195     @Override
isVolteEnabled()2196     public boolean isVolteEnabled() {
2197         return isVoiceOverCellularImsEnabled();
2198     }
2199 
2200     @Override
isVoiceOverCellularImsEnabled()2201     public boolean isVoiceOverCellularImsEnabled() {
2202         return mCT.isVoiceOverCellularImsEnabled();
2203     }
2204 
2205     @Override
isWifiCallingEnabled()2206     public boolean isWifiCallingEnabled() {
2207         return mCT.isVowifiEnabled();
2208     }
2209 
2210     @Override
isVideoEnabled()2211     public boolean isVideoEnabled() {
2212         return mCT.isVideoCallEnabled();
2213     }
2214 
2215     @Override
getImsRegistrationTech()2216     public int getImsRegistrationTech() {
2217         return mCT.getImsRegistrationTech();
2218     }
2219 
2220     @Override
getImsRegistrationTech(Consumer<Integer> callback)2221     public void getImsRegistrationTech(Consumer<Integer> callback) {
2222         mCT.getImsRegistrationTech(callback);
2223     }
2224 
2225     @Override
getImsRegistrationState(Consumer<Integer> callback)2226     public void getImsRegistrationState(Consumer<Integer> callback) {
2227         callback.accept(mImsMmTelRegistrationHelper.getImsRegistrationState());
2228     }
2229 
2230     @Override
getDefaultPhone()2231     public Phone getDefaultPhone() {
2232         return mDefaultPhone;
2233     }
2234 
2235     @Override
isImsRegistered()2236     public boolean isImsRegistered() {
2237         return mImsMmTelRegistrationHelper.isImsRegistered();
2238     }
2239 
2240     // Not used, but not removed due to UnsupportedAppUsage tag.
2241     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
setImsRegistered(boolean isRegistered)2242     public void setImsRegistered(boolean isRegistered) {
2243         mImsMmTelRegistrationHelper.updateRegistrationState(
2244                 isRegistered ? RegistrationManager.REGISTRATION_STATE_REGISTERED :
2245                         RegistrationManager.REGISTRATION_STATE_NOT_REGISTERED);
2246     }
2247 
2248     @Override
callEndCleanupHandOverCallIfAny()2249     public void callEndCleanupHandOverCallIfAny() {
2250         mCT.callEndCleanupHandOverCallIfAny();
2251     }
2252 
2253     private BroadcastReceiver mResultReceiver = new BroadcastReceiver() {
2254         @Override
2255         public void onReceive(Context context, Intent intent) {
2256             // Add notification only if alert was not shown by WfcSettings
2257             if (getResultCode() == Activity.RESULT_OK) {
2258                 // Default result code (as passed to sendOrderedBroadcast)
2259                 // means that intent was not received by WfcSettings.
2260 
2261                 CharSequence title =
2262                         intent.getCharSequenceExtra(EXTRA_WFC_REGISTRATION_FAILURE_TITLE);
2263                 CharSequence messageAlert =
2264                         intent.getCharSequenceExtra(EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE);
2265                 CharSequence messageNotification =
2266                         intent.getCharSequenceExtra(EXTRA_KEY_NOTIFICATION_MESSAGE);
2267 
2268                 Intent resultIntent = new Intent(Intent.ACTION_MAIN);
2269                 // Note: If the classname below is ever removed, the call to
2270                 // PendingIntent.getActivity should also specify FLAG_IMMUTABLE to ensure the
2271                 // pending intent cannot be tampered with.
2272                 resultIntent.setClassName("com.android.settings",
2273                         "com.android.settings.Settings$WifiCallingSettingsActivity");
2274                 resultIntent.putExtra(EXTRA_KEY_ALERT_SHOW, true);
2275                 resultIntent.putExtra(EXTRA_WFC_REGISTRATION_FAILURE_TITLE, title);
2276                 resultIntent.putExtra(EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE, messageAlert);
2277                 PendingIntent resultPendingIntent =
2278                         PendingIntent.getActivity(
2279                                 mContext,
2280                                 0,
2281                                 resultIntent,
2282                                 PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
2283                         );
2284 
2285                 final Notification notification = new Notification.Builder(mContext)
2286                                 .setSmallIcon(android.R.drawable.stat_sys_warning)
2287                                 .setContentTitle(title)
2288                                 .setContentText(messageNotification)
2289                                 .setAutoCancel(true)
2290                                 .setContentIntent(resultPendingIntent)
2291                                 .setStyle(new Notification.BigTextStyle()
2292                                 .bigText(messageNotification))
2293                                 .setChannelId(NotificationChannelController.CHANNEL_ID_WFC)
2294                                 .build();
2295                 final String notificationTag = "wifi_calling";
2296                 final int notificationId = 1;
2297 
2298                 NotificationManager notificationManager =
2299                         (NotificationManager) mContext.getSystemService(
2300                                 Context.NOTIFICATION_SERVICE);
2301                 notificationManager.notify(notificationTag, notificationId,
2302                         notification);
2303             }
2304         }
2305     };
2306 
2307     /**
2308      * Show notification in case of some error codes.
2309      */
processDisconnectReason(ImsReasonInfo imsReasonInfo)2310     public void processDisconnectReason(ImsReasonInfo imsReasonInfo) {
2311         if (imsReasonInfo.mCode == imsReasonInfo.CODE_REGISTRATION_ERROR
2312                 && imsReasonInfo.mExtraMessage != null) {
2313             // Suppress WFC Registration notifications if WFC is not enabled by the user.
2314             if (mImsManagerFactory.create(mContext, mPhoneId).isWfcEnabledByUser()) {
2315                 processWfcDisconnectForNotification(imsReasonInfo);
2316             }
2317         }
2318     }
2319 
2320     // Processes an IMS disconnect cause for possible WFC registration errors and optionally
2321     // disable WFC.
processWfcDisconnectForNotification(ImsReasonInfo imsReasonInfo)2322     private void processWfcDisconnectForNotification(ImsReasonInfo imsReasonInfo) {
2323         CarrierConfigManager configManager =
2324                 (CarrierConfigManager) mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);
2325         if (configManager == null) {
2326             loge("processDisconnectReason: CarrierConfigManager is not ready");
2327             return;
2328         }
2329         PersistableBundle pb = configManager.getConfigForSubId(getSubId());
2330         if (pb == null) {
2331             loge("processDisconnectReason: no config for subId " + getSubId());
2332             return;
2333         }
2334         final String[] wfcOperatorErrorCodes =
2335                 pb.getStringArray(
2336                         CarrierConfigManager.KEY_WFC_OPERATOR_ERROR_CODES_STRING_ARRAY);
2337         if (wfcOperatorErrorCodes == null) {
2338             // no operator-specific error codes
2339             return;
2340         }
2341 
2342         final String[] wfcOperatorErrorAlertMessages =
2343                 mContext.getResources().getStringArray(
2344                         com.android.internal.R.array.wfcOperatorErrorAlertMessages);
2345         final String[] wfcOperatorErrorNotificationMessages =
2346                 mContext.getResources().getStringArray(
2347                         com.android.internal.R.array.wfcOperatorErrorNotificationMessages);
2348 
2349         for (int i = 0; i < wfcOperatorErrorCodes.length; i++) {
2350             String[] codes = wfcOperatorErrorCodes[i].split("\\|");
2351             if (codes.length != 2) {
2352                 loge("Invalid carrier config: " + wfcOperatorErrorCodes[i]);
2353                 continue;
2354             }
2355 
2356             // Match error code.
2357             if (!imsReasonInfo.mExtraMessage.startsWith(
2358                     codes[0])) {
2359                 continue;
2360             }
2361             // If there is no delimiter at the end of error code string
2362             // then we need to verify that we are not matching partial code.
2363             // EXAMPLE: "REG9" must not match "REG99".
2364             // NOTE: Error code must not be empty.
2365             int codeStringLength = codes[0].length();
2366             char lastChar = codes[0].charAt(codeStringLength - 1);
2367             if (Character.isLetterOrDigit(lastChar)) {
2368                 if (imsReasonInfo.mExtraMessage.length() > codeStringLength) {
2369                     char nextChar = imsReasonInfo.mExtraMessage.charAt(codeStringLength);
2370                     if (Character.isLetterOrDigit(nextChar)) {
2371                         continue;
2372                     }
2373                 }
2374             }
2375 
2376             final CharSequence title = mContext.getText(
2377                     com.android.internal.R.string.wfcRegErrorTitle);
2378 
2379             int idx = Integer.parseInt(codes[1]);
2380             if (idx < 0
2381                     || idx >= wfcOperatorErrorAlertMessages.length
2382                     || idx >= wfcOperatorErrorNotificationMessages.length) {
2383                 loge("Invalid index: " + wfcOperatorErrorCodes[i]);
2384                 continue;
2385             }
2386             String messageAlert = imsReasonInfo.mExtraMessage;
2387             String messageNotification = imsReasonInfo.mExtraMessage;
2388             if (!wfcOperatorErrorAlertMessages[idx].isEmpty()) {
2389                 messageAlert = String.format(
2390                         wfcOperatorErrorAlertMessages[idx],
2391                         imsReasonInfo.mExtraMessage); // Fill IMS error code into alert message
2392             }
2393             if (!wfcOperatorErrorNotificationMessages[idx].isEmpty()) {
2394                 messageNotification = String.format(
2395                         wfcOperatorErrorNotificationMessages[idx],
2396                         imsReasonInfo.mExtraMessage); // Fill IMS error code into notification
2397             }
2398 
2399             // If WfcSettings are active then alert will be shown
2400             // otherwise notification will be added.
2401             Intent intent = new Intent(
2402                     android.telephony.ims.ImsManager.ACTION_WFC_IMS_REGISTRATION_ERROR);
2403             intent.putExtra(EXTRA_WFC_REGISTRATION_FAILURE_TITLE, title);
2404             intent.putExtra(EXTRA_WFC_REGISTRATION_FAILURE_MESSAGE, messageAlert);
2405             intent.putExtra(EXTRA_KEY_NOTIFICATION_MESSAGE, messageNotification);
2406             mContext.sendOrderedBroadcast(intent, null, mResultReceiver,
2407                     null, Activity.RESULT_OK, null, null);
2408 
2409             // We can only match a single error code
2410             // so should break the loop after a successful match.
2411             break;
2412         }
2413     }
2414 
2415     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
2416     @Override
isUtEnabled()2417     public boolean isUtEnabled() {
2418         return mCT.isUtEnabled();
2419     }
2420 
2421     @Override
sendEmergencyCallStateChange(boolean callActive)2422     public void sendEmergencyCallStateChange(boolean callActive) {
2423         mDefaultPhone.sendEmergencyCallStateChange(callActive);
2424     }
2425 
2426     @Override
setBroadcastEmergencyCallStateChanges(boolean broadcast)2427     public void setBroadcastEmergencyCallStateChanges(boolean broadcast) {
2428         mDefaultPhone.setBroadcastEmergencyCallStateChanges(broadcast);
2429     }
2430 
2431     @VisibleForTesting
getWakeLock()2432     public PowerManager.WakeLock getWakeLock() {
2433         return mWakeLock;
2434     }
2435 
2436     /**
2437      * Update roaming state and WFC mode in the following situations:
2438      *     1) voice is in service.
2439      *     2) data is in service.
2440      * @param ss non-null ServiceState
2441      */
updateRoamingState(ServiceState ss)2442     private void updateRoamingState(ServiceState ss) {
2443         if (ss == null) {
2444             loge("updateRoamingState: null ServiceState!");
2445             return;
2446         }
2447         boolean newRoamingState = ss.getRoaming();
2448         // Do not recalculate if there is no change to state.
2449         if (mLastKnownRoamingState == newRoamingState) {
2450             return;
2451         }
2452         boolean isInService = (ss.getState() == ServiceState.STATE_IN_SERVICE
2453                 || ss.getDataRegistrationState() == ServiceState.STATE_IN_SERVICE);
2454         // If we are not IN_SERVICE for voice or data, ignore change roaming state, as we always
2455         // move to home in this case.
2456         if (!isInService || !mDefaultPhone.isRadioOn()) {
2457             logi("updateRoamingState: we are not IN_SERVICE, ignoring roaming change.");
2458             return;
2459         }
2460 
2461         if (mCT.getState() == PhoneConstants.State.IDLE) {
2462             if (DBG) logd("updateRoamingState now: " + newRoamingState);
2463             if (!mFeatureFlags.updateRoamingStateToSetWfcMode()) {
2464                 mLastKnownRoamingState = newRoamingState;
2465             }
2466             CarrierConfigManager configManager = (CarrierConfigManager)
2467                     getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
2468             // Don't set wfc mode if carrierconfig has not loaded. It will be set by GsmCdmaPhone
2469             // when receives ACTION_CARRIER_CONFIG_CHANGED broadcast.
2470             if (configManager != null && CarrierConfigManager.isConfigForIdentifiedCarrier(
2471                     configManager.getConfigForSubId(getSubId()))) {
2472                 ImsManager imsManager = mImsManagerFactory.create(mContext, mPhoneId);
2473                 imsManager.setWfcMode(imsManager.getWfcMode(newRoamingState), newRoamingState);
2474                 if (mFeatureFlags.updateRoamingStateToSetWfcMode()) {
2475                     mLastKnownRoamingState = newRoamingState;
2476                 }
2477             }
2478         } else {
2479             if (DBG) logd("updateRoamingState postponed: " + newRoamingState);
2480             mCT.registerForVoiceCallEnded(this, EVENT_VOICE_CALL_ENDED, null);
2481         }
2482     }
2483 
getImsMmTelRegistrationCallback()2484     public RegistrationManager.RegistrationCallback getImsMmTelRegistrationCallback() {
2485         return mImsMmTelRegistrationHelper.getCallback();
2486     }
2487 
2488     /**
2489      * Reset the IMS registration state.
2490      */
resetImsRegistrationState()2491     public void resetImsRegistrationState() {
2492         if (DBG) logd("resetImsRegistrationState");
2493         mImsMmTelRegistrationHelper.reset();
2494         int subId = getSubId();
2495         if (SubscriptionManager.isValidSubscriptionId(subId)) {
2496             updateImsRegistrationInfo(REGISTRATION_STATE_NOT_REGISTERED,
2497                     REGISTRATION_TECH_NONE, SUGGESTED_ACTION_NONE, TRANSPORT_TYPE_INVALID);
2498         }
2499     }
2500 
2501     private ImsRegistrationCallbackHelper.ImsRegistrationUpdate mMmTelRegistrationUpdate = new
2502             ImsRegistrationCallbackHelper.ImsRegistrationUpdate() {
2503         @Override
2504         public void handleImsRegistered(@NonNull ImsRegistrationAttributes attributes) {
2505             int imsTransportType = attributes.getTransportType();
2506             if (DBG) {
2507                 logd("handleImsRegistered: onImsMmTelConnected imsTransportType="
2508                         + AccessNetworkConstants.transportTypeToString(imsTransportType));
2509             }
2510             mRegLocalLog.log("handleImsRegistered: onImsMmTelConnected imsTransportType="
2511                     + AccessNetworkConstants.transportTypeToString(imsTransportType));
2512             setServiceState(ServiceState.STATE_IN_SERVICE);
2513             getDefaultPhone().setImsRegistrationState(true);
2514             mMetrics.writeOnImsConnectionState(mPhoneId, ImsConnectionState.State.CONNECTED, null);
2515             mImsStats.onImsRegistered(attributes);
2516             mImsNrSaModeHandler.onImsRegistered(
2517                     attributes.getRegistrationTechnology(), attributes.getFeatureTags());
2518             updateImsRegistrationInfo(REGISTRATION_STATE_REGISTERED,
2519                     attributes.getRegistrationTechnology(), SUGGESTED_ACTION_NONE,
2520                     imsTransportType);
2521 
2522             AsyncResult ar;
2523             if (mFeatureFlags.changeMethodOfObtainingImsRegistrationRadioTech()) {
2524                 ar = new AsyncResult(null, new ImsRegistrationRadioTechInfo(mPhoneId,
2525                         attributes.getRegistrationTechnology(), REGISTRATION_STATE_REGISTERED),
2526                         null);
2527             } else {
2528                 ar = new AsyncResult(null, null, null);
2529             }
2530             mImsRegistrationUpdateRegistrants.notifyRegistrants(ar);
2531         }
2532 
2533         @Override
2534         public void handleImsRegistering(int imsRadioTech) {
2535             if (DBG) {
2536                 logd("handleImsRegistering: onImsMmTelProgressing imsRadioTech="
2537                         + AccessNetworkConstants.transportTypeToString(imsRadioTech));
2538             }
2539             mRegLocalLog.log("handleImsRegistering: onImsMmTelProgressing imsRadioTech="
2540                     + AccessNetworkConstants.transportTypeToString(imsRadioTech));
2541             setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
2542             getDefaultPhone().setImsRegistrationState(false);
2543             mMetrics.writeOnImsConnectionState(mPhoneId, ImsConnectionState.State.PROGRESSING,
2544                     null);
2545             mImsStats.onImsRegistering(imsRadioTech);
2546 
2547             AsyncResult ar;
2548             if (mFeatureFlags.changeMethodOfObtainingImsRegistrationRadioTech()) {
2549                 ar = new AsyncResult(null, new ImsRegistrationRadioTechInfo(mPhoneId,
2550                         imsRadioTech, REGISTRATION_STATE_REGISTERING),
2551                         null);
2552             } else {
2553                 ar = new AsyncResult(null, null, null);
2554             }
2555             mImsRegistrationUpdateRegistrants.notifyRegistrants(ar);
2556         }
2557 
2558         @Override
2559         public void handleImsUnregistered(ImsReasonInfo imsReasonInfo,
2560                 @RegistrationManager.SuggestedAction int suggestedAction,
2561                 @ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech) {
2562             if (DBG) {
2563                 logd("handleImsUnregistered: onImsMmTelDisconnected imsReasonInfo="
2564                         + imsReasonInfo + ", suggestedAction=" + suggestedAction
2565                         + ", disconnectedRadioTech=" + imsRadioTech);
2566             }
2567             mRegLocalLog.log("handleImsUnregistered: onImsMmTelDisconnected imsRadioTech="
2568                     + imsReasonInfo);
2569             setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
2570             processDisconnectReason(imsReasonInfo);
2571             getDefaultPhone().setImsRegistrationState(false);
2572             mMetrics.writeOnImsConnectionState(mPhoneId, ImsConnectionState.State.DISCONNECTED,
2573                     imsReasonInfo);
2574             mImsStats.onImsUnregistered(imsReasonInfo);
2575             mImsNrSaModeHandler.onImsUnregistered(imsRadioTech);
2576             mImsRegistrationTech = REGISTRATION_TECH_NONE;
2577             int suggestedModemAction = SUGGESTED_ACTION_NONE;
2578             if (imsReasonInfo.getCode() == ImsReasonInfo.CODE_REGISTRATION_ERROR) {
2579                 if ((suggestedAction == SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK)
2580                         || (suggestedAction == SUGGESTED_ACTION_TRIGGER_PLMN_BLOCK_WITH_TIMEOUT)) {
2581                     suggestedModemAction = suggestedAction;
2582                 } else if (mFeatureFlags.addRatRelatedSuggestedActionToImsRegistration()) {
2583                     if ((suggestedAction == SUGGESTED_ACTION_TRIGGER_RAT_BLOCK)
2584                             || (suggestedAction == SUGGESTED_ACTION_TRIGGER_CLEAR_RAT_BLOCKS)) {
2585                         suggestedModemAction = suggestedAction;
2586                     }
2587                 }
2588             }
2589             updateImsRegistrationInfo(REGISTRATION_STATE_NOT_REGISTERED,
2590                     imsRadioTech, suggestedModemAction, TRANSPORT_TYPE_INVALID);
2591 
2592             if (mFeatureFlags.clearCachedImsPhoneNumberWhenDeviceLostImsRegistration()) {
2593                 // Clear the phone number from P-Associated-Uri
2594                 setCurrentSubscriberUris(null);
2595                 clearPhoneNumberForSourceIms();
2596             }
2597 
2598             AsyncResult ar;
2599             if (mFeatureFlags.changeMethodOfObtainingImsRegistrationRadioTech()) {
2600                 ar = new AsyncResult(null, new ImsRegistrationRadioTechInfo(mPhoneId,
2601                         REGISTRATION_TECH_NONE, REGISTRATION_STATE_NOT_REGISTERED),
2602                         null);
2603             } else {
2604                 ar = new AsyncResult(null, null, null);
2605             }
2606             mImsRegistrationUpdateRegistrants.notifyRegistrants(ar);
2607         }
2608 
2609         @Override
2610         public void handleImsSubscriberAssociatedUriChanged(Uri[] uris) {
2611             if (DBG) logd("handleImsSubscriberAssociatedUriChanged");
2612             setCurrentSubscriberUris(uris);
2613             setPhoneNumberForSourceIms(uris);
2614         }
2615     };
2616 
2617     /** Clear the IMS phone number from IMS associated Uris when IMS registration is lost. */
2618     @VisibleForTesting
clearPhoneNumberForSourceIms()2619     public void clearPhoneNumberForSourceIms() {
2620         int subId = getSubId();
2621         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
2622             return;
2623         }
2624 
2625         if (DBG) logd("clearPhoneNumberForSourceIms");
2626         mSubscriptionManagerService.setNumberFromIms(subId, new String(""));
2627     }
2628 
2629     /** Sets the IMS phone number from IMS associated URIs, if any found. */
2630     @VisibleForTesting
setPhoneNumberForSourceIms(Uri[] uris)2631     public void setPhoneNumberForSourceIms(Uri[] uris) {
2632         int subId = getSubId();
2633         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
2634             // Defending b/219080264:
2635             // SubscriptionManagerService.setSubscriptionProperty validates input subId
2636             // so do not proceed if subId invalid. This may be happening because cached
2637             // IMS callbacks are sent back to telephony after SIM state changed.
2638             return;
2639         }
2640         SubscriptionInfoInternal subInfo = mSubscriptionManagerService
2641                 .getSubscriptionInfoInternal(subId);
2642         if (subInfo == null) {
2643             loge("trigger setPhoneNumberForSourceIms, but subInfo is null");
2644             return;
2645         }
2646         String subCountryIso = subInfo.getCountryIso();
2647         String phoneNumber = extractPhoneNumberFromAssociatedUris(uris, /*isGlobalFormat*/true);
2648         if (phoneNumber != null) {
2649             phoneNumber = PhoneNumberUtils.formatNumberToE164(phoneNumber, subCountryIso);
2650             if (phoneNumber == null) {
2651                 loge("format to E164 failed");
2652                 return;
2653             }
2654             mSubscriptionManagerService.setNumberFromIms(subId, phoneNumber);
2655         } else if (isAllowNonGlobalNumberFormat()) {
2656             // If carrier config has true for KEY_IGNORE_GLOBAL_PHONE_NUMBER_FORMAT_BOOL and
2657             // P-Associated-Uri does not have global number,
2658             // try to find phone number excluding '+' one more time.
2659             phoneNumber = extractPhoneNumberFromAssociatedUris(uris, /*isGlobalFormat*/false);
2660             if (phoneNumber == null) {
2661                 loge("extract phone number without '+' failed");
2662                 return;
2663             }
2664             mSubscriptionManagerService.setNumberFromIms(subId, phoneNumber);
2665         } else {
2666             logd("extract phone number failed");
2667         }
2668     }
2669 
2670     /**
2671      * Finds the phone number from associated URIs.
2672      *
2673      * <p>Associated URIs are public user identities, and phone number could be used:
2674      * see 3GPP TS 24.229 5.4.1.2 and 3GPP TS 23.003 13.4. This algotihm look for the
2675      * possible "global number" in E.164 format.
2676      * <p>If true try finding phone number even if the P-Associated-Uri does not have global
2677      * number format.
2678      */
extractPhoneNumberFromAssociatedUris(Uri[] uris, boolean isGlobalFormat)2679     private static String extractPhoneNumberFromAssociatedUris(Uri[] uris, boolean isGlobalFormat) {
2680         if (uris == null) {
2681             return null;
2682         }
2683 
2684         Stream<String> intermediate = Arrays.stream(uris)
2685                 // Phone number is an opaque URI "tel:<phone-number>" or "sip:<phone-number>@<...>"
2686                 .filter(u -> u != null && u.isOpaque())
2687                 .filter(u -> "tel".equalsIgnoreCase(u.getScheme())
2688                         || "sip".equalsIgnoreCase(u.getScheme()))
2689                 .map(Uri::getSchemeSpecificPart);
2690 
2691         if (isGlobalFormat) {
2692             // "Global number" should be in E.164 format starting with "+" e.g. "+447539447777"
2693             return intermediate.filter(ssp -> ssp != null && ssp.startsWith("+"))
2694                     // Remove whatever after "@" for sip URI
2695                     .map(ssp -> ssp.split("@")[0])
2696                     // Returns the first winner
2697                     .findFirst()
2698                     .orElse(null);
2699         } else {
2700             // non global number format
2701             return intermediate.filter(ssp -> ssp != null)
2702                     // Remove whatever after "@" for sip URI
2703                     .map(ssp -> ssp.split("@")[0])
2704                     // regular expression, allow only number
2705                     .filter(ssp -> ssp.matches("^[0-9]+$"))
2706                     // Returns the first winner
2707                     .findFirst()
2708                     .orElse(null);
2709         }
2710     }
2711 
getIccRecords()2712     public IccRecords getIccRecords() {
2713         return mDefaultPhone.getIccRecords();
2714     }
2715 
updateDialArgsForVolteSilentRedial(DialArgs dialArgs, int causeCode)2716     public DialArgs updateDialArgsForVolteSilentRedial(DialArgs dialArgs, int causeCode) {
2717         if (dialArgs != null) {
2718             ImsPhone.ImsDialArgs.Builder imsDialArgsBuilder;
2719             imsDialArgsBuilder = ImsPhone.ImsDialArgs.Builder.from(dialArgs);
2720 
2721             Bundle extras = new Bundle(dialArgs.intentExtras);
2722             if (causeCode == CallFailCause.EMC_REDIAL_ON_VOWIFI) {
2723                 extras.putString(ImsCallProfile.EXTRA_CALL_RAT_TYPE,
2724                         String.valueOf(ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN));
2725                 logd("trigger VoWifi emergency call");
2726                 imsDialArgsBuilder.setIntentExtras(extras);
2727             } else if (causeCode == CallFailCause.EMC_REDIAL_ON_IMS) {
2728                 logd("trigger VoLte emergency call");
2729             }
2730             return imsDialArgsBuilder.build();
2731         }
2732         return new DialArgs.Builder<>().build();
2733     }
2734 
2735     @Override
getVoiceCallSessionStats()2736     public VoiceCallSessionStats getVoiceCallSessionStats() {
2737         return mDefaultPhone.getVoiceCallSessionStats();
2738     }
2739 
2740     /** Returns the {@link ImsStats} for this IMS phone. */
getImsStats()2741     public ImsStats getImsStats() {
2742         return mImsStats;
2743     }
2744 
2745     /** Returns the {@link AccessNetworkConstants.TransportType} used to register this IMS phone. */
getTransportType()2746     public @AccessNetworkConstants.TransportType int getTransportType() {
2747         return mTransportType;
2748     }
2749 
2750     /** Sets the {@link ImsStats} mock for this IMS phone during unit testing. */
2751     @VisibleForTesting
setImsStats(ImsStats imsStats)2752     public void setImsStats(ImsStats imsStats) {
2753         mImsStats = imsStats;
2754     }
2755 
hasAliveCall()2756     public boolean hasAliveCall() {
2757         return (getForegroundCall().getState() != Call.State.IDLE ||
2758                 getBackgroundCall().getState() != Call.State.IDLE);
2759     }
2760 
getLastKnownRoamingState()2761     public boolean getLastKnownRoamingState() {
2762         return mLastKnownRoamingState;
2763     }
2764 
2765     /**
2766      * Update IMS registration information to modem.
2767      *
2768      * @param capabilities indicate MMTEL capability such as VOICE, VIDEO and SMS.
2769      */
updateImsRegistrationInfo(int capabilities)2770     public void updateImsRegistrationInfo(int capabilities) {
2771         if (mImsRegistrationState == REGISTRATION_STATE_REGISTERED) {
2772             if (mNotifiedRegisteredState && (capabilities == mImsRegistrationCapabilities)) {
2773                 // Duplicated notification, no change in capabilities.
2774                 return;
2775             }
2776 
2777             mImsRegistrationCapabilities = capabilities;
2778             if (capabilities == 0) {
2779                 // Ignore this as this usually happens just before onUnregistered callback.
2780                 // We can notify modem when onUnregistered() flow occurs.
2781                 return;
2782             }
2783 
2784             mDefaultPhone.mCi.updateImsRegistrationInfo(mImsRegistrationState,
2785                     mImsRegistrationTech, 0, capabilities, null);
2786             mNotifiedRegisteredState = true;
2787         }
2788     }
2789 
2790     /**
2791      * Update IMS registration info
2792      *
2793      * @param regState indicates IMS registration state.
2794      * @param imsRadioTech indicates the type of the radio access network where IMS is registered.
2795      * @param suggestedAction indicates the suggested action for the radio to perform.
2796      */
updateImsRegistrationInfo( @egistrationManager.ImsRegistrationState int regState, @ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech, @RegistrationManager.SuggestedAction int suggestedAction, @AccessNetworkConstants.TransportType int transportType)2797     private void updateImsRegistrationInfo(
2798             @RegistrationManager.ImsRegistrationState int regState,
2799             @ImsRegistrationImplBase.ImsRegistrationTech int imsRadioTech,
2800             @RegistrationManager.SuggestedAction int suggestedAction,
2801             @AccessNetworkConstants.TransportType int transportType) {
2802 
2803         if (regState == mImsRegistrationState) {
2804             // In NOT_REGISTERED state, the current PLMN can be blocked with a suggested action.
2805             // But in this case, the same behavior is able to occur in different PLMNs with
2806             // same radio tech and suggested action.
2807             if ((regState == REGISTRATION_STATE_REGISTERED && imsRadioTech == mImsRegistrationTech)
2808                     || (regState == REGISTRATION_STATE_NOT_REGISTERED
2809                             && suggestedAction == SUGGESTED_ACTION_NONE
2810                             && mImsRegistrationSuggestedAction == SUGGESTED_ACTION_NONE
2811                             && imsRadioTech == mImsDeregistrationTech)) {
2812                 // Filter duplicate notification.
2813                 return;
2814             }
2815         }
2816 
2817         if (regState == REGISTRATION_STATE_NOT_REGISTERED) {
2818             mDefaultPhone.mCi.updateImsRegistrationInfo(regState,
2819                     imsRadioTech, suggestedAction, 0, null);
2820         } else if (mImsRegistrationState == REGISTRATION_STATE_REGISTERED) {
2821             // This happens when radio tech is changed while in REGISTERED state.
2822             if (mImsRegistrationCapabilities > 0) {
2823                 // Capability has been updated. Notify REGISTRATION_STATE_REGISTERED.
2824                 mDefaultPhone.mCi.updateImsRegistrationInfo(regState, imsRadioTech, 0,
2825                         mImsRegistrationCapabilities, null);
2826                 mImsRegistrationTech = imsRadioTech;
2827                 mTransportType = transportType;
2828                 mNotifiedRegisteredState = true;
2829                 return;
2830             }
2831         }
2832 
2833         mImsRegistrationState = regState;
2834         mImsRegistrationTech = imsRadioTech;
2835         mTransportType = transportType;
2836         mImsRegistrationSuggestedAction = suggestedAction;
2837         if (regState == REGISTRATION_STATE_NOT_REGISTERED) {
2838             mImsDeregistrationTech = imsRadioTech;
2839         } else {
2840             mImsDeregistrationTech = REGISTRATION_TECH_NONE;
2841         }
2842         mImsRegistrationCapabilities = 0;
2843         // REGISTRATION_STATE_REGISTERED will be notified when the capability is updated.
2844         mNotifiedRegisteredState = false;
2845     }
2846 
2847     @Override
setTerminalBasedCallWaitingStatus(int state)2848     public void setTerminalBasedCallWaitingStatus(int state) {
2849         mCT.setTerminalBasedCallWaitingStatus(state);
2850     }
2851 
2852     @Override
triggerEpsFallback(@mTelFeature.EpsFallbackReason int reason, Message response)2853     public void triggerEpsFallback(@MmTelFeature.EpsFallbackReason int reason, Message response) {
2854         mDefaultPhone.triggerEpsFallback(reason, response);
2855     }
2856 
2857     @Override
startImsTraffic(int token, @MmTelFeature.ImsTrafficType int trafficType, @AccessNetworkConstants.RadioAccessNetworkType int accessNetworkType, @MmTelFeature.ImsTrafficDirection int trafficDirection, Message response)2858     public void startImsTraffic(int token,
2859             @MmTelFeature.ImsTrafficType int trafficType,
2860             @AccessNetworkConstants.RadioAccessNetworkType int accessNetworkType,
2861             @MmTelFeature.ImsTrafficDirection int trafficDirection, Message response) {
2862         mDefaultPhone.startImsTraffic(token, trafficType,
2863                 accessNetworkType, trafficDirection, response);
2864     }
2865 
2866     @Override
stopImsTraffic(int token, Message response)2867     public void stopImsTraffic(int token, Message response) {
2868         mDefaultPhone.stopImsTraffic(token, response);
2869     }
2870 
2871     @Override
registerForConnectionSetupFailure(Handler h, int what, Object obj)2872     public void registerForConnectionSetupFailure(Handler h, int what, Object obj) {
2873         mDefaultPhone.registerForConnectionSetupFailure(h, what, obj);
2874     }
2875 
2876     @Override
unregisterForConnectionSetupFailure(Handler h)2877     public void unregisterForConnectionSetupFailure(Handler h) {
2878         mDefaultPhone.unregisterForConnectionSetupFailure(h);
2879     }
2880 
2881     @Override
triggerImsDeregistration( @msRegistrationImplBase.ImsDeregistrationReason int reason)2882     public void triggerImsDeregistration(
2883             @ImsRegistrationImplBase.ImsDeregistrationReason int reason) {
2884         mCT.triggerImsDeregistration(reason);
2885     }
2886 
2887     @Override
updateImsCallStatus(List<ImsCallInfo> imsCallInfo, Message response)2888     public void updateImsCallStatus(List<ImsCallInfo> imsCallInfo, Message response) {
2889         mDefaultPhone.updateImsCallStatus(imsCallInfo, response);
2890     }
2891 
2892     @Override
triggerNotifyAnbr(int mediaType, int direction, int bitsPerSecond)2893     public void triggerNotifyAnbr(int mediaType, int direction, int bitsPerSecond) {
2894         mCT.triggerNotifyAnbr(mediaType, direction, bitsPerSecond);
2895     }
2896 
2897     /**
2898      * Check whether making a call using Wi-Fi is possible or not.
2899      * @return {code true} if IMS is registered over IWLAN else return {code false}.
2900      */
canMakeWifiCall()2901     public boolean canMakeWifiCall() {
2902         return isImsRegistered() && (getImsRegistrationTech()
2903                 == ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN);
2904     }
2905 
2906     @Override
dump(FileDescriptor fd, PrintWriter printWriter, String[] args)2907     public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
2908         IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
2909         pw.println("ImsPhone extends:");
2910         super.dump(fd, pw, args);
2911         pw.flush();
2912 
2913         pw.println("ImsPhone:");
2914         pw.println("  mDefaultPhone = " + mDefaultPhone);
2915         pw.println("  mPendingMMIs = " + mPendingMMIs);
2916         pw.println("  mPostDialHandler = " + mPostDialHandler);
2917         pw.println("  mSS = " + mSS);
2918         pw.println("  mWakeLock = " + mWakeLock);
2919         pw.println("  mIsPhoneInEcmState = " + isInEcm());
2920         pw.println("  mEcmExitRespRegistrant = " + mEcmExitRespRegistrant);
2921         pw.println("  mSilentRedialRegistrants = " + mSilentRedialRegistrants);
2922         pw.println("  mImsMmTelRegistrationState = "
2923                 + mImsMmTelRegistrationHelper.getImsRegistrationState());
2924         pw.println("  mLastKnownRoamingState = " + mLastKnownRoamingState);
2925         pw.println("  mSsnRegistrants = " + mSsnRegistrants);
2926         pw.println(" Registration Log:");
2927         pw.increaseIndent();
2928         mRegLocalLog.dump(pw);
2929         pw.decreaseIndent();
2930         pw.flush();
2931     }
2932 
isAllowNonGlobalNumberFormat()2933     private boolean isAllowNonGlobalNumberFormat() {
2934         PersistableBundle persistableBundle = null;
2935         CarrierConfigManager carrierConfigManager = (CarrierConfigManager) mContext
2936                 .getSystemService(Context.CARRIER_CONFIG_SERVICE);
2937         if (carrierConfigManager != null) {
2938             persistableBundle = carrierConfigManager.getConfigForSubId(getSubId(),
2939                     CarrierConfigManager.Ims.KEY_ALLOW_NON_GLOBAL_PHONE_NUMBER_FORMAT_BOOL);
2940         }
2941         if (persistableBundle != null) {
2942             return persistableBundle.getBoolean(
2943                     CarrierConfigManager.Ims.KEY_ALLOW_NON_GLOBAL_PHONE_NUMBER_FORMAT_BOOL, false);
2944         }
2945 
2946         return false;
2947     }
2948 
logi(String s)2949     private void logi(String s) {
2950         Rlog.i(LOG_TAG, "[" + mPhoneId + "] " + s);
2951     }
2952 
logv(String s)2953     private void logv(String s) {
2954         Rlog.v(LOG_TAG, "[" + mPhoneId + "] " + s);
2955     }
2956 
logd(String s)2957     private void logd(String s) {
2958         Rlog.d(LOG_TAG, "[" + mPhoneId + "] " + s);
2959     }
2960 
loge(String s)2961     private void loge(String s) {
2962         Rlog.e(LOG_TAG, "[" + mPhoneId + "] " + s);
2963     }
2964 }
2965