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