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