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