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