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