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