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