1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.internal.telephony.imsphone;
18 
19 import static android.telephony.ServiceState.STATE_IN_SERVICE;
20 
21 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_DATA;
22 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_DATA_ASYNC;
23 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_DATA_SYNC;
24 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_FAX;
25 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_MAX;
26 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_NONE;
27 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_PACKET;
28 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_PAD;
29 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_SMS;
30 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE;
31 
32 import android.compat.annotation.UnsupportedAppUsage;
33 import android.content.Context;
34 import android.content.res.Resources;
35 import android.os.AsyncResult;
36 import android.os.Handler;
37 import android.os.Message;
38 import android.os.ResultReceiver;
39 import android.telephony.PhoneNumberUtils;
40 import android.telephony.ims.ImsCallForwardInfo;
41 import android.telephony.ims.ImsReasonInfo;
42 import android.telephony.ims.ImsSsData;
43 import android.telephony.ims.ImsSsInfo;
44 import android.telephony.ims.ImsUtListener;
45 import android.telephony.ims.stub.ImsUtImplBase;
46 import android.text.SpannableStringBuilder;
47 import android.text.TextUtils;
48 
49 import com.android.ims.ImsException;
50 import com.android.internal.telephony.CallForwardInfo;
51 import com.android.internal.telephony.CallStateException;
52 import com.android.internal.telephony.CommandException;
53 import com.android.internal.telephony.CommandsInterface;
54 import com.android.internal.telephony.MmiCode;
55 import com.android.internal.telephony.Phone;
56 import com.android.internal.telephony.uicc.IccRecords;
57 import com.android.telephony.Rlog;
58 
59 import java.util.Arrays;
60 import java.util.List;
61 import java.util.regex.Matcher;
62 import java.util.regex.Pattern;
63 
64 /**
65  * The motto for this file is:
66  *
67  * "NOTE:    By using the # as a separator, most cases are expected to be unambiguous."
68  *   -- TS 22.030 6.5.2
69  *
70  * {@hide}
71  *
72  */
73 public final class ImsPhoneMmiCode extends Handler implements MmiCode {
74     static final String LOG_TAG = "ImsPhoneMmiCode";
75 
76     //***** Constants
77 
78     // Max Size of the Short Code (aka Short String from TS 22.030 6.5.2)
79     private static final int MAX_LENGTH_SHORT_CODE = 2;
80 
81     // TS 22.030 6.5.2 Every Short String USSD command will end with #-key
82     // (known as #-String)
83     private static final char END_OF_USSD_COMMAND = '#';
84 
85     // From TS 22.030 6.5.2
86     private static final String ACTION_ACTIVATE = "*";
87     private static final String ACTION_DEACTIVATE = "#";
88     private static final String ACTION_INTERROGATE = "*#";
89     private static final String ACTION_REGISTER = "**";
90     private static final String ACTION_ERASURE = "##";
91 
92     // Supp Service codes from TS 22.030 Annex B
93 
94     //Called line presentation
95     private static final String SC_CLIP    = "30";
96     private static final String SC_CLIR    = "31";
97     private static final String SC_COLP    = "76";
98     private static final String SC_COLR    = "77";
99 
100     //Calling name presentation
101     private static final String SC_CNAP    = "300";
102 
103     // Call Forwarding
104     private static final String SC_CFU     = "21";
105     private static final String SC_CFB     = "67";
106     private static final String SC_CFNRy   = "61";
107     private static final String SC_CFNR    = "62";
108     // Call Forwarding unconditional Timer
109     private static final String SC_CFUT     = "22";
110 
111     private static final String SC_CF_All = "002";
112     private static final String SC_CF_All_Conditional = "004";
113 
114     // Call Waiting
115     private static final String SC_WAIT     = "43";
116 
117     // Call Barring
118     private static final String SC_BAOC         = "33";
119     private static final String SC_BAOIC        = "331";
120     private static final String SC_BAOICxH      = "332";
121     private static final String SC_BAIC         = "35";
122     private static final String SC_BAICr        = "351";
123 
124     private static final String SC_BA_ALL       = "330";
125     private static final String SC_BA_MO        = "333";
126     private static final String SC_BA_MT        = "353";
127 
128     // Incoming/Anonymous call barring
129     private static final String SC_BS_MT        = "156";
130     private static final String SC_BAICa        = "157";
131 
132     // Supp Service Password registration
133     private static final String SC_PWD          = "03";
134 
135     // PIN/PIN2/PUK/PUK2
136     private static final String SC_PIN          = "04";
137     private static final String SC_PIN2         = "042";
138     private static final String SC_PUK          = "05";
139     private static final String SC_PUK2         = "052";
140 
141     //***** Event Constants
142 
143     private static final int EVENT_SET_COMPLETE            = 0;
144     private static final int EVENT_QUERY_CF_COMPLETE       = 1;
145     private static final int EVENT_USSD_COMPLETE           = 2;
146     private static final int EVENT_QUERY_COMPLETE          = 3;
147     private static final int EVENT_SET_CFF_COMPLETE        = 4;
148     private static final int EVENT_USSD_CANCEL_COMPLETE    = 5;
149     private static final int EVENT_GET_CLIR_COMPLETE       = 6;
150     private static final int EVENT_SUPP_SVC_QUERY_COMPLETE = 7;
151     private static final int EVENT_QUERY_ICB_COMPLETE      = 10;
152 
153     //***** Calling Line Presentation Constants
154     private static final int NUM_PRESENTATION_ALLOWED     = 0;
155     private static final int NUM_PRESENTATION_RESTRICTED  = 1;
156 
157     //***** Supplementary Service Query Bundle Keys
158     // Used by IMS Service layer to put supp. serv. query
159     // responses into the ssInfo Bundle.
160     /**
161      * @deprecated Use {@link ImsUtListener#onLineIdentificationSupplementaryServiceResponse(int,
162      * ImsSsInfo)} API instead.
163      */
164     @Deprecated
165     // Not used, only kept around to not break vendors using this key.
166     public static final String UT_BUNDLE_KEY_CLIR = "queryClir";
167     /**
168      * @deprecated Use {@link ImsUtListener#onLineIdentificationSupplementaryServiceResponse(int,
169      * ImsSsInfo)} API instead.
170      */
171     @Deprecated
172     // Not used, only kept around to not break vendors using this key.
173     public static final String UT_BUNDLE_KEY_SSINFO = "imsSsInfo";
174 
175     //***** Instance Variables
176 
177     @UnsupportedAppUsage
178     private ImsPhone mPhone;
179     @UnsupportedAppUsage
180     private Context mContext;
181     private IccRecords mIccRecords;
182 
183     private String mAction;              // One of ACTION_*
184     private String mSc;                  // Service Code
185     private String mSia, mSib, mSic;       // Service Info a,b,c
186     private String mPoundString;         // Entire MMI string up to and including #
187     private String mDialingNumber;
188     private String mPwd;                 // For password registration
189     private ResultReceiver mCallbackReceiver;
190 
191     private boolean mIsPendingUSSD;
192 
193     private boolean mIsUssdRequest;
194 
195     private boolean mIsCallFwdReg;
196 
197     private boolean mIsNetworkInitiatedUSSD;
198 
199     private State mState = State.PENDING;
200     private CharSequence mMessage;
201     private boolean mIsSsInfo = false;
202     //resgister/erasure of ICB (Specific DN)
203     static final String IcbDnMmi = "Specific Incoming Call Barring";
204     //ICB (Anonymous)
205     static final String IcbAnonymousMmi = "Anonymous Incoming Call Barring";
206     //***** Class Variables
207 
208 
209     // See TS 22.030 6.5.2 "Structure of the MMI"
210 
211     private static Pattern sPatternSuppService = Pattern.compile(
212         "((\\*|#|\\*#|\\*\\*|##)(\\d{2,3})(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*))?)?)?)?#)(.*)");
213 /*       1  2                    3          4  5       6   7         8    9     10  11             12
214 
215          1 = Full string up to and including #
216          2 = action (activation/interrogation/registration/erasure)
217          3 = service code
218          5 = SIA
219          7 = SIB
220          9 = SIC
221          10 = dialing number
222 */
223 
224     private static final int MATCH_GROUP_POUND_STRING = 1;
225 
226     private static final int MATCH_GROUP_ACTION = 2;
227                         //(activation/interrogation/registration/erasure)
228 
229     private static final int MATCH_GROUP_SERVICE_CODE = 3;
230     private static final int MATCH_GROUP_SIA = 5;
231     private static final int MATCH_GROUP_SIB = 7;
232     private static final int MATCH_GROUP_SIC = 9;
233     private static final int MATCH_GROUP_PWD_CONFIRM = 11;
234     private static final int MATCH_GROUP_DIALING_NUMBER = 12;
235     static private String[] sTwoDigitNumberPattern;
236 
237     //***** Public Class methods
238 
239     /**
240      * Some dial strings in GSM are defined to do non-call setup
241      * things, such as modify or query supplementary service settings (eg, call
242      * forwarding). These are generally referred to as "MMI codes".
243      * We look to see if the dial string contains a valid MMI code (potentially
244      * with a dial string at the end as well) and return info here.
245      *
246      * If the dial string contains no MMI code, we return an instance with
247      * only "dialingNumber" set
248      *
249      * Please see flow chart in TS 22.030 6.5.3.2
250      */
251 
252     @UnsupportedAppUsage
newFromDialString(String dialString, ImsPhone phone)253     static ImsPhoneMmiCode newFromDialString(String dialString, ImsPhone phone) {
254        return newFromDialString(dialString, phone, null);
255     }
256 
newFromDialString(String dialString, ImsPhone phone, ResultReceiver wrappedCallback)257     static ImsPhoneMmiCode newFromDialString(String dialString,
258                                              ImsPhone phone, ResultReceiver wrappedCallback) {
259         Matcher m;
260         ImsPhoneMmiCode ret = null;
261 
262         if (phone.getDefaultPhone().getServiceState().getVoiceRoaming()
263                 && phone.getDefaultPhone().supportsConversionOfCdmaCallerIdMmiCodesWhileRoaming()) {
264             /* The CDMA MMI coded dialString will be converted to a 3GPP MMI Coded dialString
265                so that it can be processed by the matcher and code below
266              */
267             dialString = convertCdmaMmiCodesTo3gppMmiCodes(dialString);
268         }
269 
270         m = sPatternSuppService.matcher(dialString);
271 
272         // Is this formatted like a standard supplementary service code?
273         if (m.matches()) {
274             ret = new ImsPhoneMmiCode(phone);
275             ret.mPoundString = makeEmptyNull(m.group(MATCH_GROUP_POUND_STRING));
276             ret.mAction = makeEmptyNull(m.group(MATCH_GROUP_ACTION));
277             ret.mSc = makeEmptyNull(m.group(MATCH_GROUP_SERVICE_CODE));
278             ret.mSia = makeEmptyNull(m.group(MATCH_GROUP_SIA));
279             ret.mSib = makeEmptyNull(m.group(MATCH_GROUP_SIB));
280             ret.mSic = makeEmptyNull(m.group(MATCH_GROUP_SIC));
281             ret.mPwd = makeEmptyNull(m.group(MATCH_GROUP_PWD_CONFIRM));
282             ret.mDialingNumber = makeEmptyNull(m.group(MATCH_GROUP_DIALING_NUMBER));
283             ret.mCallbackReceiver = wrappedCallback;
284             // According to TS 22.030 6.5.2 "Structure of the MMI",
285             // the dialing number should not ending with #.
286             // The dialing number ending # is treated as unique USSD,
287             // eg, *400#16 digit number# to recharge the prepaid card
288             // in India operator(Mumbai MTNL)
289             if (ret.mDialingNumber != null &&
290                     ret.mDialingNumber.endsWith("#") &&
291                     dialString.endsWith("#")){
292                 ret = new ImsPhoneMmiCode(phone);
293                 ret.mPoundString = dialString;
294             }
295         } else if (dialString.endsWith("#")) {
296             // TS 22.030 sec 6.5.3.2
297             // "Entry of any characters defined in the 3GPP TS 23.038 [8] Default Alphabet
298             // (up to the maximum defined in 3GPP TS 24.080 [10]), followed by #SEND".
299 
300             ret = new ImsPhoneMmiCode(phone);
301             ret.mPoundString = dialString;
302         } else if (isTwoDigitShortCode(phone.getContext(), dialString)) {
303             //Is a country-specific exception to short codes as defined in TS 22.030, 6.5.3.2
304             ret = null;
305         } else if (isShortCode(dialString, phone)) {
306             // this may be a short code, as defined in TS 22.030, 6.5.3.2
307             ret = new ImsPhoneMmiCode(phone);
308             ret.mDialingNumber = dialString;
309         }
310 
311         return ret;
312     }
313 
convertCdmaMmiCodesTo3gppMmiCodes(String dialString)314     private static String convertCdmaMmiCodesTo3gppMmiCodes(String dialString) {
315         Matcher m;
316         m = sPatternCdmaMmiCodeWhileRoaming.matcher(dialString);
317         if (m.matches()) {
318             String serviceCode = makeEmptyNull(m.group(MATCH_GROUP_CDMA_MMI_CODE_SERVICE_CODE));
319             String prefix = m.group(MATCH_GROUP_CDMA_MMI_CODE_NUMBER_PREFIX);
320             String number = makeEmptyNull(m.group(MATCH_GROUP_CDMA_MMI_CODE_NUMBER));
321 
322             if (serviceCode.equals("67") && number != null) {
323                 // "#31#number" to invoke CLIR
324                 dialString = ACTION_DEACTIVATE + SC_CLIR + ACTION_DEACTIVATE + prefix + number;
325             } else if (serviceCode.equals("82") && number != null) {
326                 // "*31#number" to suppress CLIR
327                 dialString = ACTION_ACTIVATE + SC_CLIR + ACTION_DEACTIVATE + prefix + number;
328             }
329         }
330         return dialString;
331     }
332 
333     static ImsPhoneMmiCode
newNetworkInitiatedUssd(String ussdMessage, boolean isUssdRequest, ImsPhone phone)334     newNetworkInitiatedUssd(String ussdMessage, boolean isUssdRequest, ImsPhone phone) {
335         ImsPhoneMmiCode ret;
336 
337         ret = new ImsPhoneMmiCode(phone);
338 
339         ret.mMessage = ussdMessage;
340         ret.mIsUssdRequest = isUssdRequest;
341         ret.mIsNetworkInitiatedUSSD = true;
342 
343         // If it's a request, set to PENDING so that it's cancelable.
344         if (isUssdRequest) {
345             ret.mIsPendingUSSD = true;
346             ret.mState = State.PENDING;
347         } else {
348             ret.mState = State.COMPLETE;
349         }
350 
351         return ret;
352     }
353 
newFromUssdUserInput(String ussdMessge, ImsPhone phone)354     static ImsPhoneMmiCode newFromUssdUserInput(String ussdMessge, ImsPhone phone) {
355         ImsPhoneMmiCode ret = new ImsPhoneMmiCode(phone);
356 
357         ret.mMessage = ussdMessge;
358         ret.mState = State.PENDING;
359         ret.mIsPendingUSSD = true;
360 
361         return ret;
362     }
363 
364     //***** Private Class methods
365 
366     /** make empty strings be null.
367      *  Regexp returns empty strings for empty groups
368      */
369     private static String
makeEmptyNull(String s)370     makeEmptyNull (String s) {
371         if (s != null && s.length() == 0) return null;
372 
373         return s;
374     }
375 
isScMatchesSuppServType(String dialString)376     static boolean isScMatchesSuppServType(String dialString) {
377         boolean isMatch = false;
378         Matcher m = sPatternSuppService.matcher(dialString);
379         if (m.matches()) {
380             String sc = makeEmptyNull(m.group(MATCH_GROUP_SERVICE_CODE));
381             if (sc.equals(SC_CFUT)) {
382                 isMatch = true;
383             } else if(sc.equals(SC_BS_MT)) {
384                 isMatch = true;
385             }
386         }
387         return isMatch;
388     }
389 
390     /** returns true of the string is empty or null */
391     @UnsupportedAppUsage
392     private static boolean
isEmptyOrNull(CharSequence s)393     isEmptyOrNull(CharSequence s) {
394         return s == null || (s.length() == 0);
395     }
396 
397     private static int
scToCallForwardReason(String sc)398     scToCallForwardReason(String sc) {
399         if (sc == null) {
400             throw new RuntimeException ("invalid call forward sc");
401         }
402 
403         if (sc.equals(SC_CF_All)) {
404            return CommandsInterface.CF_REASON_ALL;
405         } else if (sc.equals(SC_CFU)) {
406             return CommandsInterface.CF_REASON_UNCONDITIONAL;
407         } else if (sc.equals(SC_CFB)) {
408             return CommandsInterface.CF_REASON_BUSY;
409         } else if (sc.equals(SC_CFNR)) {
410             return CommandsInterface.CF_REASON_NOT_REACHABLE;
411         } else if (sc.equals(SC_CFNRy)) {
412             return CommandsInterface.CF_REASON_NO_REPLY;
413         } else if (sc.equals(SC_CF_All_Conditional)) {
414            return CommandsInterface.CF_REASON_ALL_CONDITIONAL;
415         } else {
416             throw new RuntimeException ("invalid call forward sc");
417         }
418     }
419 
420     private static int
siToServiceClass(String si)421     siToServiceClass(String si) {
422         if (si == null || si.length() == 0) {
423                 return  SERVICE_CLASS_NONE;
424         } else {
425             // NumberFormatException should cause MMI fail
426             int serviceCode = Integer.parseInt(si, 10);
427 
428             switch (serviceCode) {
429                 case 10: return SERVICE_CLASS_SMS + SERVICE_CLASS_FAX  + SERVICE_CLASS_VOICE;
430                 case 11: return SERVICE_CLASS_VOICE;
431                 case 12: return SERVICE_CLASS_SMS + SERVICE_CLASS_FAX;
432                 case 13: return SERVICE_CLASS_FAX;
433 
434                 case 16: return SERVICE_CLASS_SMS;
435 
436                 case 19: return SERVICE_CLASS_FAX + SERVICE_CLASS_VOICE;
437 
438                 case 20: return SERVICE_CLASS_DATA_ASYNC + SERVICE_CLASS_DATA_SYNC;
439 
440                 case 21: return SERVICE_CLASS_PAD + SERVICE_CLASS_DATA_ASYNC;
441                 case 22: return SERVICE_CLASS_PACKET + SERVICE_CLASS_DATA_SYNC;
442                 case 24: return SERVICE_CLASS_DATA_SYNC;
443                 case 25: return SERVICE_CLASS_DATA_ASYNC;
444                 case 26: return SERVICE_CLASS_DATA_SYNC + SERVICE_CLASS_VOICE;
445                 case 99: return SERVICE_CLASS_PACKET;
446 
447                 default:
448                     throw new RuntimeException("unsupported MMI service code " + si);
449             }
450         }
451     }
452 
453     private static int
siToTime(String si)454     siToTime (String si) {
455         if (si == null || si.length() == 0) {
456             return 0;
457         } else {
458             // NumberFormatException should cause MMI fail
459             return Integer.parseInt(si, 10);
460         }
461     }
462 
463     static boolean
isServiceCodeCallForwarding(String sc)464     isServiceCodeCallForwarding(String sc) {
465         return sc != null &&
466                 (sc.equals(SC_CFU)
467                 || sc.equals(SC_CFB) || sc.equals(SC_CFNRy)
468                 || sc.equals(SC_CFNR) || sc.equals(SC_CF_All)
469                 || sc.equals(SC_CF_All_Conditional));
470     }
471 
472     static boolean
isServiceCodeCallBarring(String sc)473     isServiceCodeCallBarring(String sc) {
474         Resources resource = Resources.getSystem();
475         if (sc != null) {
476             String[] barringMMI = resource.getStringArray(
477                 com.android.internal.R.array.config_callBarringMMI_for_ims);
478             if (barringMMI != null) {
479                 for (String match : barringMMI) {
480                     if (sc.equals(match)) return true;
481                 }
482             }
483         }
484         return false;
485     }
486 
isPinPukCommand(String sc)487     static boolean isPinPukCommand(String sc) {
488         return sc != null && (sc.equals(SC_PIN) || sc.equals(SC_PIN2)
489                 || sc.equals(SC_PUK) || sc.equals(SC_PUK2));
490     }
491 
492     /**
493      * Whether the dial string is supplementary service code.
494      *
495      * @param dialString The dial string.
496      * @return true if the dial string is supplementary service code, and {@code false} otherwise.
497      */
isSuppServiceCodes(String dialString, Phone phone)498     public static boolean isSuppServiceCodes(String dialString, Phone phone) {
499         if (phone != null && phone.getServiceState().getVoiceRoaming()
500                 && phone.getDefaultPhone().supportsConversionOfCdmaCallerIdMmiCodesWhileRoaming()) {
501             /* The CDMA MMI coded dialString will be converted to a 3GPP MMI Coded dialString
502                so that it can be processed by the matcher and code below
503              */
504             dialString = convertCdmaMmiCodesTo3gppMmiCodes(dialString);
505         }
506 
507         Matcher m = sPatternSuppService.matcher(dialString);
508         if (m.matches()) {
509             String sc = makeEmptyNull(m.group(MATCH_GROUP_SERVICE_CODE));
510             if (isServiceCodeCallForwarding(sc)) {
511                 return true;
512             } else if (isServiceCodeCallBarring(sc)) {
513                 return true;
514             } else if (sc != null && sc.equals(SC_CFUT)) {
515                 return true;
516             } else if (sc != null && sc.equals(SC_CLIP)) {
517                 return true;
518             } else if (sc != null && sc.equals(SC_CLIR)) {
519                 return true;
520             } else if (sc != null && sc.equals(SC_COLP)) {
521                 return true;
522             } else if (sc != null && sc.equals(SC_COLR)) {
523                 return true;
524             } else if (sc != null && sc.equals(SC_CNAP)) {
525                 return true;
526             } else if (sc != null && sc.equals(SC_BS_MT)) {
527                 return true;
528             } else if (sc != null && sc.equals(SC_BAICa)) {
529                 return true;
530             } else if (sc != null && sc.equals(SC_PWD)) {
531                 return true;
532             } else if (sc != null && sc.equals(SC_WAIT)) {
533                 return true;
534             } else if (isPinPukCommand(sc)) {
535                 return true;
536             }
537         }
538         return false;
539     }
540 
541     static String
scToBarringFacility(String sc)542     scToBarringFacility(String sc) {
543         if (sc == null) {
544             throw new RuntimeException ("invalid call barring sc");
545         }
546 
547         if (sc.equals(SC_BAOC)) {
548             return CommandsInterface.CB_FACILITY_BAOC;
549         } else if (sc.equals(SC_BAOIC)) {
550             return CommandsInterface.CB_FACILITY_BAOIC;
551         } else if (sc.equals(SC_BAOICxH)) {
552             return CommandsInterface.CB_FACILITY_BAOICxH;
553         } else if (sc.equals(SC_BAIC)) {
554             return CommandsInterface.CB_FACILITY_BAIC;
555         } else if (sc.equals(SC_BAICr)) {
556             return CommandsInterface.CB_FACILITY_BAICr;
557         } else if (sc.equals(SC_BA_ALL)) {
558             return CommandsInterface.CB_FACILITY_BA_ALL;
559         } else if (sc.equals(SC_BA_MO)) {
560             return CommandsInterface.CB_FACILITY_BA_MO;
561         } else if (sc.equals(SC_BA_MT)) {
562             return CommandsInterface.CB_FACILITY_BA_MT;
563         } else {
564             throw new RuntimeException ("invalid call barring sc");
565         }
566     }
567 
568     //***** Constructor
569 
ImsPhoneMmiCode(ImsPhone phone)570     public ImsPhoneMmiCode(ImsPhone phone) {
571         // The telephony unit-test cases may create ImsPhoneMmiCode's
572         // in secondary threads
573         super(phone.getHandler().getLooper());
574         mPhone = phone;
575         mContext = phone.getContext();
576         mIccRecords = mPhone.mDefaultPhone.getIccRecords();
577     }
578 
579     //***** MmiCode implementation
580 
581     @Override
582     public State
getState()583     getState() {
584         return mState;
585     }
586 
587     @Override
588     public CharSequence
getMessage()589     getMessage() {
590         return mMessage;
591     }
592 
593     @Override
getPhone()594     public Phone getPhone() { return mPhone; }
595 
596     // inherited javadoc suffices
597     @Override
598     public void
cancel()599     cancel() {
600         // Complete or failed cannot be cancelled
601         if (mState == State.COMPLETE || mState == State.FAILED) {
602             return;
603         }
604 
605         mState = State.CANCELLED;
606 
607         if (mIsPendingUSSD) {
608             mPhone.cancelUSSD(obtainMessage(EVENT_USSD_CANCEL_COMPLETE, this));
609         } else {
610             mPhone.onMMIDone (this);
611         }
612 
613     }
614 
615     @Override
isCancelable()616     public boolean isCancelable() {
617         /* Can only cancel pending USSD sessions. */
618         return mIsPendingUSSD;
619     }
620 
621     //***** Instance Methods
622 
623     @UnsupportedAppUsage
getDialingNumber()624     String getDialingNumber() {
625         return mDialingNumber;
626     }
627 
628     /** Does this dial string contain a structured or unstructured MMI code? */
629     boolean
isMMI()630     isMMI() {
631         return mPoundString != null;
632     }
633 
634     /* Is this a 1 or 2 digit "short code" as defined in TS 22.030 sec 6.5.3.2? */
635     boolean
isShortCode()636     isShortCode() {
637         return mPoundString == null
638                     && mDialingNumber != null && mDialingNumber.length() <= 2;
639 
640     }
641 
642     @Override
getDialString()643     public String getDialString() {
644         return mPoundString;
645     }
646 
647     static private boolean
isTwoDigitShortCode(Context context, String dialString)648     isTwoDigitShortCode(Context context, String dialString) {
649         Rlog.d(LOG_TAG, "isTwoDigitShortCode");
650 
651         if (dialString == null || dialString.length() > 2) return false;
652 
653         if (sTwoDigitNumberPattern == null) {
654             sTwoDigitNumberPattern = context.getResources().getStringArray(
655                     com.android.internal.R.array.config_twoDigitNumberPattern);
656         }
657 
658         for (String dialnumber : sTwoDigitNumberPattern) {
659             Rlog.d(LOG_TAG, "Two Digit Number Pattern " + dialnumber);
660             if (dialString.equals(dialnumber)) {
661                 Rlog.d(LOG_TAG, "Two Digit Number Pattern -true");
662                 return true;
663             }
664         }
665         Rlog.d(LOG_TAG, "Two Digit Number Pattern -false");
666         return false;
667     }
668 
669     /**
670      * Helper function for newFromDialString. Returns true if dialString appears
671      * to be a short code AND conditions are correct for it to be treated as
672      * such.
673      */
isShortCode(String dialString, ImsPhone phone)674     static private boolean isShortCode(String dialString, ImsPhone phone) {
675         // Refer to TS 22.030 Figure 3.5.3.2:
676         if (dialString == null) {
677             return false;
678         }
679 
680         // Illegal dial string characters will give a ZERO length.
681         // At this point we do not want to crash as any application with
682         // call privileges may send a non dial string.
683         // It return false as when the dialString is equal to NULL.
684         if (dialString.length() == 0) {
685             return false;
686         }
687 
688         if (PhoneNumberUtils.isLocalEmergencyNumber(phone.getContext(), dialString)) {
689             return false;
690         } else {
691             return isShortCodeUSSD(dialString, phone);
692         }
693     }
694 
695     /**
696      * Helper function for isShortCode. Returns true if dialString appears to be
697      * a short code and it is a USSD structure
698      *
699      * According to the 3PGG TS 22.030 specification Figure 3.5.3.2: A 1 or 2
700      * digit "short code" is treated as USSD if it is entered while on a call or
701      * does not satisfy the condition (exactly 2 digits && starts with '1'), there
702      * are however exceptions to this rule (see below)
703      *
704      * Exception (1) to Call initiation is: If the user of the device is already in a call
705      * and enters a Short String without any #-key at the end and the length of the Short String is
706      * equal or less then the MAX_LENGTH_SHORT_CODE [constant that is equal to 2]
707      *
708      * The phone shall initiate a USSD/SS commands.
709      */
isShortCodeUSSD(String dialString, ImsPhone phone)710     static private boolean isShortCodeUSSD(String dialString, ImsPhone phone) {
711         if (dialString != null && dialString.length() <= MAX_LENGTH_SHORT_CODE) {
712             if (phone.isInCall()) {
713                 return true;
714             }
715 
716             if (dialString.length() != MAX_LENGTH_SHORT_CODE ||
717                     dialString.charAt(0) != '1') {
718                 return true;
719             }
720         }
721         return false;
722     }
723 
724     /**
725      * @return true if the Service Code is PIN/PIN2/PUK/PUK2-related
726      */
isPinPukCommand()727     public boolean isPinPukCommand() {
728         return isPinPukCommand(mSc);
729     }
730 
731     /**
732      * See TS 22.030 Annex B.
733      * In temporary mode, to suppress CLIR for a single call, enter:
734      *      " * 31 # [called number] SEND "
735      *  In temporary mode, to invoke CLIR for a single call enter:
736      *       " # 31 # [called number] SEND "
737      */
738     @UnsupportedAppUsage
739     boolean
isTemporaryModeCLIR()740     isTemporaryModeCLIR() {
741         return mSc != null && mSc.equals(SC_CLIR) && mDialingNumber != null
742                 && (isActivate() || isDeactivate());
743     }
744 
745     /**
746      * returns CommandsInterface.CLIR_*
747      * See also isTemporaryModeCLIR()
748      */
749     @UnsupportedAppUsage
750     int
getCLIRMode()751     getCLIRMode() {
752         if (mSc != null && mSc.equals(SC_CLIR)) {
753             if (isActivate()) {
754                 return CommandsInterface.CLIR_SUPPRESSION;
755             } else if (isDeactivate()) {
756                 return CommandsInterface.CLIR_INVOCATION;
757             }
758         }
759 
760         return CommandsInterface.CLIR_DEFAULT;
761     }
762 
763     @UnsupportedAppUsage
isActivate()764     boolean isActivate() {
765         return mAction != null && mAction.equals(ACTION_ACTIVATE);
766     }
767 
768     @UnsupportedAppUsage
isDeactivate()769     boolean isDeactivate() {
770         return mAction != null && mAction.equals(ACTION_DEACTIVATE);
771     }
772 
isInterrogate()773     boolean isInterrogate() {
774         return mAction != null && mAction.equals(ACTION_INTERROGATE);
775     }
776 
777     @UnsupportedAppUsage
isRegister()778     boolean isRegister() {
779         return mAction != null && mAction.equals(ACTION_REGISTER);
780     }
781 
782     @UnsupportedAppUsage
isErasure()783     boolean isErasure() {
784         return mAction != null && mAction.equals(ACTION_ERASURE);
785     }
786 
787     /**
788      * Returns true if this is a USSD code that's been submitted to the
789      * network...eg, after processCode() is called
790      */
isPendingUSSD()791     public boolean isPendingUSSD() {
792         return mIsPendingUSSD;
793     }
794 
795     @Override
isUssdRequest()796     public boolean isUssdRequest() {
797         return mIsUssdRequest;
798     }
799 
800     @UnsupportedAppUsage
801     boolean
isSupportedOverImsPhone()802     isSupportedOverImsPhone() {
803         if (isShortCode()) return true;
804         else if (isServiceCodeCallForwarding(mSc)
805                 || isServiceCodeCallBarring(mSc)
806                 || (mSc != null && mSc.equals(SC_WAIT))
807                 || (mSc != null && mSc.equals(SC_CLIR))
808                 || (mSc != null && mSc.equals(SC_CLIP))
809                 || (mSc != null && mSc.equals(SC_COLR))
810                 || (mSc != null && mSc.equals(SC_COLP))
811                 || (mSc != null && mSc.equals(SC_BS_MT))
812                 || (mSc != null && mSc.equals(SC_BAICa))) {
813 
814             try {
815                 int serviceClass = siToServiceClass(mSib);
816                 if (serviceClass != SERVICE_CLASS_NONE
817                         && serviceClass != SERVICE_CLASS_VOICE
818                         && serviceClass != (SERVICE_CLASS_PACKET
819                             + SERVICE_CLASS_DATA_SYNC)) {
820                     return false;
821                 }
822                 return true;
823             } catch (RuntimeException exc) {
824                 Rlog.d(LOG_TAG, "Invalid service class " + exc);
825             }
826         } else if (isPinPukCommand()
827                 || (mSc != null
828                     && (mSc.equals(SC_PWD) || mSc.equals(SC_CLIP) || mSc.equals(SC_CLIR)))) {
829             return false;
830         } else if (mPoundString != null) return true;
831 
832         return false;
833     }
834 
835     /*
836      * The below actions are IMS/Volte CallBarring actions.We have not defined
837      * these actions in ImscommandInterface.However we have reused existing
838      * actions of CallForwarding as, both CF and CB actions are used for same
839      * purpose.
840      */
callBarAction(String dialingNumber)841     public int callBarAction(String dialingNumber) {
842         if (isActivate()) {
843             return CommandsInterface.CF_ACTION_ENABLE;
844         } else if (isDeactivate()) {
845             return CommandsInterface.CF_ACTION_DISABLE;
846         } else if (isRegister()) {
847             if (!isEmptyOrNull(dialingNumber)) {
848                 return CommandsInterface.CF_ACTION_REGISTRATION;
849             } else {
850                 throw new RuntimeException ("invalid action");
851             }
852         } else if (isErasure()) {
853             return CommandsInterface.CF_ACTION_ERASURE;
854         } else {
855             throw new RuntimeException ("invalid action");
856         }
857     }
858 
859     /** Process a MMI code or short code...anything that isn't a dialing number */
860     @UnsupportedAppUsage
861     public void
processCode()862     processCode () throws CallStateException {
863         try {
864             if (isShortCode()) {
865                 Rlog.d(LOG_TAG, "processCode: isShortCode");
866 
867                 // These just get treated as USSD.
868                 Rlog.d(LOG_TAG, "processCode: Sending short code '"
869                        + mDialingNumber + "' over CS pipe.");
870                 throw new CallStateException(Phone.CS_FALLBACK);
871             } else if (isServiceCodeCallForwarding(mSc)) {
872                 Rlog.d(LOG_TAG, "processCode: is CF");
873 
874                 String dialingNumber = mSia;
875                 int reason = scToCallForwardReason(mSc);
876                 int serviceClass = siToServiceClass(mSib);
877                 int time = siToTime(mSic);
878 
879                 if (isInterrogate()) {
880                     mPhone.getCallForwardingOption(reason, serviceClass,
881                             obtainMessage(EVENT_QUERY_CF_COMPLETE, this));
882                 } else {
883                     int cfAction;
884 
885                     if (isActivate()) {
886                         // 3GPP TS 22.030 6.5.2
887                         // a call forwarding request with a single * would be
888                         // interpreted as registration if containing a forwarded-to
889                         // number, or an activation if not
890                         if (isEmptyOrNull(dialingNumber)) {
891                             cfAction = CommandsInterface.CF_ACTION_ENABLE;
892                             mIsCallFwdReg = false;
893                         } else {
894                             cfAction = CommandsInterface.CF_ACTION_REGISTRATION;
895                             mIsCallFwdReg = true;
896                         }
897                     } else if (isDeactivate()) {
898                         cfAction = CommandsInterface.CF_ACTION_DISABLE;
899                     } else if (isRegister()) {
900                         cfAction = CommandsInterface.CF_ACTION_REGISTRATION;
901                     } else if (isErasure()) {
902                         cfAction = CommandsInterface.CF_ACTION_ERASURE;
903                     } else {
904                         throw new RuntimeException ("invalid action");
905                     }
906 
907                     int isSettingUnconditional =
908                             ((reason == CommandsInterface.CF_REASON_UNCONDITIONAL) ||
909                              (reason == CommandsInterface.CF_REASON_ALL)) ? 1 : 0;
910 
911                     int isEnableDesired =
912                         ((cfAction == CommandsInterface.CF_ACTION_ENABLE) ||
913                                 (cfAction == CommandsInterface.CF_ACTION_REGISTRATION)) ? 1 : 0;
914 
915                     Rlog.d(LOG_TAG, "processCode: is CF setCallForward");
916                     mPhone.setCallForwardingOption(cfAction, reason,
917                             dialingNumber, serviceClass, time, obtainMessage(
918                                     EVENT_SET_CFF_COMPLETE,
919                                     isSettingUnconditional,
920                                     isEnableDesired, this));
921                 }
922             } else if (isServiceCodeCallBarring(mSc)) {
923                 // sia = password
924                 // sib = basic service group
925                 // service group is not supported
926 
927                 String password = mSia;
928                 String facility = scToBarringFacility(mSc);
929                 int serviceClass = siToServiceClass(mSib);
930 
931                 if (isInterrogate()) {
932                     mPhone.getCallBarring(facility,
933                             obtainMessage(EVENT_SUPP_SVC_QUERY_COMPLETE, this), serviceClass);
934                 } else if (isActivate() || isDeactivate()) {
935                     mPhone.setCallBarring(facility, isActivate(), password,
936                             obtainMessage(EVENT_SET_COMPLETE, this), serviceClass);
937                 } else {
938                     throw new RuntimeException ("Invalid or Unsupported MMI Code");
939                 }
940             } else if (mSc != null && mSc.equals(SC_CLIR)) {
941                 // NOTE: Since these supplementary services are accessed only
942                 //       via MMI codes, methods have not been added to ImsPhone.
943                 //       Only the UT interface handle is used.
944                 if (isActivate()
945                         && !mPhone.getDefaultPhone().isClirActivationAndDeactivationPrevented()) {
946                     try {
947                         mPhone.setOutgoingCallerIdDisplay(CommandsInterface.CLIR_INVOCATION,
948                             obtainMessage(EVENT_SET_COMPLETE, this));
949                     } catch (Exception e) {
950                         Rlog.d(LOG_TAG, "processCode: Could not get UT handle for updateCLIR.");
951                     }
952                 } else if (isDeactivate()
953                         && !mPhone.getDefaultPhone().isClirActivationAndDeactivationPrevented()) {
954                     try {
955                         mPhone.setOutgoingCallerIdDisplay(CommandsInterface.CLIR_SUPPRESSION,
956                             obtainMessage(EVENT_SET_COMPLETE, this));
957                     } catch (Exception e) {
958                         Rlog.d(LOG_TAG, "processCode: Could not get UT handle for updateCLIR.");
959                     }
960                 } else if (isInterrogate()) {
961                     try {
962                         mPhone.getOutgoingCallerIdDisplay(obtainMessage(EVENT_GET_CLIR_COMPLETE, this));
963                     } catch (Exception e) {
964                         Rlog.d(LOG_TAG, "processCode: Could not get UT handle for queryCLIR.");
965                     }
966                 } else {
967                     throw new RuntimeException ("Invalid or Unsupported MMI Code");
968                 }
969             } else if (mSc != null && mSc.equals(SC_CLIP)) {
970                 // NOTE: Refer to the note above.
971                 if (isInterrogate()) {
972                     try {
973                         mPhone.queryCLIP(obtainMessage(EVENT_SUPP_SVC_QUERY_COMPLETE, this));
974                     } catch (Exception e) {
975                         Rlog.d(LOG_TAG, "processCode: Could not get UT handle for queryCLIP.");
976                     }
977                 } else if (isActivate() || isDeactivate()) {
978                     try {
979                         mPhone.mCT.getUtInterface().updateCLIP(isActivate(),
980                                 obtainMessage(EVENT_SET_COMPLETE, this));
981                     } catch (ImsException e) {
982                         Rlog.d(LOG_TAG, "processCode: Could not get UT handle for updateCLIP.");
983                     }
984                 } else {
985                     throw new RuntimeException ("Invalid or Unsupported MMI Code");
986                 }
987             } else if (mSc != null && mSc.equals(SC_COLP)) {
988                 // NOTE: Refer to the note above.
989                 if (isInterrogate()) {
990                     try {
991                         mPhone.mCT.getUtInterface()
992                             .queryCOLP(obtainMessage(EVENT_SUPP_SVC_QUERY_COMPLETE, this));
993                     } catch (ImsException e) {
994                         Rlog.d(LOG_TAG, "processCode: Could not get UT handle for queryCOLP.");
995                     }
996                 } else if (isActivate() || isDeactivate()) {
997                     try {
998                         mPhone.mCT.getUtInterface().updateCOLP(isActivate(),
999                                  obtainMessage(EVENT_SET_COMPLETE, this));
1000                      } catch (ImsException e) {
1001                          Rlog.d(LOG_TAG, "processCode: Could not get UT handle for updateCOLP.");
1002                      }
1003                 } else {
1004                     throw new RuntimeException ("Invalid or Unsupported MMI Code");
1005                 }
1006             } else if (mSc != null && mSc.equals(SC_COLR)) {
1007                 // NOTE: Refer to the note above.
1008                 if (isActivate()) {
1009                     try {
1010                         mPhone.mCT.getUtInterface().updateCOLR(NUM_PRESENTATION_RESTRICTED,
1011                                 obtainMessage(EVENT_SET_COMPLETE, this));
1012                     } catch (ImsException e) {
1013                         Rlog.d(LOG_TAG, "processCode: Could not get UT handle for updateCOLR.");
1014                     }
1015                 } else if (isDeactivate()) {
1016                     try {
1017                         mPhone.mCT.getUtInterface().updateCOLR(NUM_PRESENTATION_ALLOWED,
1018                                 obtainMessage(EVENT_SET_COMPLETE, this));
1019                     } catch (ImsException e) {
1020                         Rlog.d(LOG_TAG, "processCode: Could not get UT handle for updateCOLR.");
1021                     }
1022                 } else if (isInterrogate()) {
1023                     try {
1024                         mPhone.mCT.getUtInterface()
1025                             .queryCOLR(obtainMessage(EVENT_SUPP_SVC_QUERY_COMPLETE, this));
1026                     } catch (ImsException e) {
1027                         Rlog.d(LOG_TAG, "processCode: Could not get UT handle for queryCOLR.");
1028                     }
1029                 } else {
1030                     throw new RuntimeException ("Invalid or Unsupported MMI Code");
1031                 }
1032             } else if (mSc != null && (mSc.equals(SC_BS_MT))) {
1033                 try {
1034                     if (isInterrogate()) {
1035                         mPhone.mCT.getUtInterface().queryCallBarring(
1036                                 ImsUtImplBase.CALL_BARRING_SPECIFIC_INCOMING_CALLS,
1037                                 obtainMessage(EVENT_QUERY_ICB_COMPLETE, this));
1038                     } else {
1039                         processIcbMmiCodeForUpdate();
1040                     }
1041                  // TODO: isRegister() case needs to be handled.
1042                 } catch (ImsException e) {
1043                     Rlog.d(LOG_TAG, "processCode: Could not get UT handle for ICB.");
1044                 }
1045             } else if (mSc != null && mSc.equals(SC_BAICa)) {
1046                 int callAction =0;
1047                 // TODO: Should we route through queryCallBarring() here?
1048                 try {
1049                     if (isInterrogate()) {
1050                         mPhone.mCT.getUtInterface().queryCallBarring(
1051                                 ImsUtImplBase.CALL_BARRING_ANONYMOUS_INCOMING,
1052                                 obtainMessage(EVENT_QUERY_ICB_COMPLETE, this));
1053                     } else {
1054                         if (isActivate()) {
1055                             callAction = CommandsInterface.CF_ACTION_ENABLE;
1056                         } else if (isDeactivate()) {
1057                             callAction = CommandsInterface.CF_ACTION_DISABLE;
1058                         }
1059                         mPhone.mCT.getUtInterface()
1060                                 .updateCallBarring(ImsUtImplBase.CALL_BARRING_ANONYMOUS_INCOMING,
1061                                 callAction,
1062                                 obtainMessage(EVENT_SET_COMPLETE,this),
1063                                 null);
1064                     }
1065                 } catch (ImsException e) {
1066                     Rlog.d(LOG_TAG, "processCode: Could not get UT handle for ICBa.");
1067                 }
1068             } else if (mSc != null && mSc.equals(SC_WAIT)) {
1069                 // sia = basic service group
1070                 int serviceClass = siToServiceClass(mSia);
1071 
1072                 if (isActivate() || isDeactivate()) {
1073                     mPhone.setCallWaiting(isActivate(), serviceClass,
1074                             obtainMessage(EVENT_SET_COMPLETE, this));
1075                 } else if (isInterrogate()) {
1076                     mPhone.getCallWaiting(obtainMessage(EVENT_QUERY_COMPLETE, this));
1077                 } else {
1078                     throw new RuntimeException ("Invalid or Unsupported MMI Code");
1079                 }
1080             } else if (mPoundString != null) {
1081                 if (mContext.getResources().getBoolean(
1082                         com.android.internal.R.bool.config_allow_ussd_over_ims)) {
1083                     // We'll normally send USSD over the CS pipe, but if it happens that
1084                     // the CS phone is out of service, we'll just try over IMS instead.
1085                     if (mPhone.getDefaultPhone().getServiceStateTracker().mSS.getState()
1086                             == STATE_IN_SERVICE) {
1087                         Rlog.i(LOG_TAG, "processCode: Sending ussd string '"
1088                                 + Rlog.pii(LOG_TAG, mPoundString) + "' over CS pipe "
1089                                 + "(allowed over ims).");
1090                         throw new CallStateException(Phone.CS_FALLBACK);
1091                     } else {
1092                         Rlog.i(LOG_TAG, "processCode: CS is out of service, sending ussd string '"
1093                                 + Rlog.pii(LOG_TAG, mPoundString) + "' over IMS pipe.");
1094                         sendUssd(mPoundString);
1095                     }
1096                 } else {
1097                     // USSD codes are not supported over IMS due to modem limitations; send over
1098                     // the CS pipe instead.  This should be fixed in the future.
1099                     Rlog.i(LOG_TAG, "processCode: Sending ussd string '"
1100                             + Rlog.pii(LOG_TAG, mPoundString) + "' over CS pipe.");
1101                     throw new CallStateException(Phone.CS_FALLBACK);
1102                 }
1103             } else {
1104                 Rlog.d(LOG_TAG, "processCode: invalid or unsupported MMI");
1105                 throw new RuntimeException ("Invalid or Unsupported MMI Code");
1106             }
1107         } catch (RuntimeException exc) {
1108             mState = State.FAILED;
1109             mMessage = mContext.getText(com.android.internal.R.string.mmiError);
1110             Rlog.d(LOG_TAG, "processCode: RuntimeException = " + exc);
1111             mPhone.onMMIDone(this);
1112         }
1113     }
1114 
1115     /**
1116      * Called from ImsPhone
1117      *
1118      * An unsolicited USSD NOTIFY or REQUEST has come in matching
1119      * up with this pending USSD request
1120      *
1121      * Note: If REQUEST, this exchange is complete, but the session remains
1122      *       active (ie, the network expects user input).
1123      */
1124     void
onUssdFinished(String ussdMessage, boolean isUssdRequest)1125     onUssdFinished(String ussdMessage, boolean isUssdRequest) {
1126         if (mState == State.PENDING) {
1127             if (TextUtils.isEmpty(ussdMessage)) {
1128                 mMessage = mContext.getText(com.android.internal.R.string.mmiComplete);
1129                 Rlog.v(LOG_TAG, "onUssdFinished: no message; using: " + mMessage);
1130             } else {
1131                 Rlog.v(LOG_TAG, "onUssdFinished: message: " + ussdMessage);
1132                 mMessage = ussdMessage;
1133             }
1134             mIsUssdRequest = isUssdRequest;
1135             // If it's a request, leave it PENDING so that it's cancelable.
1136             if (!isUssdRequest) {
1137                 mState = State.COMPLETE;
1138             }
1139             mPhone.onMMIDone(this);
1140         }
1141     }
1142 
1143     /**
1144      * Called from ImsPhone
1145      *
1146      * The radio has reset, and this is still pending
1147      */
1148 
1149     void
onUssdFinishedError()1150     onUssdFinishedError() {
1151         if (mState == State.PENDING) {
1152             mState = State.FAILED;
1153             mMessage = mContext.getText(com.android.internal.R.string.mmiError);
1154             Rlog.d(LOG_TAG, "onUssdFinishedError: mmi=" + this);
1155             mPhone.onMMIDone(this);
1156         }
1157     }
1158 
sendUssd(String ussdMessage)1159     void sendUssd(String ussdMessage) {
1160         // Treat this as a USSD string
1161         mIsPendingUSSD = true;
1162 
1163         // Note that unlike most everything else, the USSD complete
1164         // response does not complete this MMI code...we wait for
1165         // an unsolicited USSD "Notify" or "Request".
1166         // The matching up of this is done in ImsPhone.
1167 
1168         mPhone.sendUSSD(ussdMessage,
1169             obtainMessage(EVENT_USSD_COMPLETE, this));
1170     }
1171 
1172     /** Called from ImsPhone.handleMessage; not a Handler subclass */
1173     @Override
1174     public void
handleMessage(Message msg)1175     handleMessage (Message msg) {
1176         AsyncResult ar;
1177 
1178         switch (msg.what) {
1179             case EVENT_SET_COMPLETE:
1180                 ar = (AsyncResult) (msg.obj);
1181 
1182                 onSetComplete(msg, ar);
1183                 break;
1184 
1185             case EVENT_SET_CFF_COMPLETE:
1186                 ar = (AsyncResult) (msg.obj);
1187 
1188                 /*
1189                 * msg.arg1 = 1 means to set unconditional voice call forwarding
1190                 * msg.arg2 = 1 means to enable voice call forwarding
1191                 */
1192                 if ((ar.exception == null) && (msg.arg1 == 1)) {
1193                     boolean cffEnabled = (msg.arg2 == 1);
1194                     if (mIccRecords != null) {
1195                         mPhone.setVoiceCallForwardingFlag(mIccRecords,
1196                                 1, cffEnabled, mDialingNumber);
1197                     }
1198                 }
1199 
1200                 onSetComplete(msg, ar);
1201                 break;
1202 
1203             case EVENT_QUERY_CF_COMPLETE:
1204                 ar = (AsyncResult) (msg.obj);
1205                 onQueryCfComplete(ar);
1206                 break;
1207 
1208             case EVENT_QUERY_COMPLETE:
1209                 ar = (AsyncResult) (msg.obj);
1210                 onQueryComplete(ar);
1211                 break;
1212 
1213             case EVENT_USSD_COMPLETE:
1214                 ar = (AsyncResult) (msg.obj);
1215 
1216                 if (ar.exception != null) {
1217                     mState = State.FAILED;
1218                     mMessage = getErrorMessage(ar);
1219 
1220                     mPhone.onMMIDone(this);
1221                 }
1222 
1223                 // Note that unlike most everything else, the USSD complete
1224                 // response does not complete this MMI code...we wait for
1225                 // an unsolicited USSD "Notify" or "Request".
1226                 // The matching up of this is done in ImsPhone.
1227 
1228                 break;
1229 
1230             case EVENT_USSD_CANCEL_COMPLETE:
1231                 mPhone.onMMIDone(this);
1232                 break;
1233 
1234             case EVENT_SUPP_SVC_QUERY_COMPLETE:
1235                 ar = (AsyncResult) (msg.obj);
1236                 onSuppSvcQueryComplete(ar);
1237                 break;
1238 
1239             case EVENT_QUERY_ICB_COMPLETE:
1240                 ar = (AsyncResult) (msg.obj);
1241                 onIcbQueryComplete(ar);
1242                 break;
1243 
1244             case EVENT_GET_CLIR_COMPLETE:
1245                 ar = (AsyncResult) (msg.obj);
1246                 onQueryClirComplete(ar);
1247                 break;
1248 
1249             default:
1250                 break;
1251         }
1252     }
1253 
1254     //***** Private instance methods
1255 
1256     private void
processIcbMmiCodeForUpdate()1257     processIcbMmiCodeForUpdate () {
1258         String dialingNumber = mSia;
1259         String[] icbNum = null;
1260         int callAction;
1261         if (dialingNumber != null) {
1262             icbNum = dialingNumber.split("\\$");
1263         }
1264         callAction = callBarAction(dialingNumber);
1265 
1266         try {
1267             mPhone.mCT.getUtInterface().updateCallBarring(
1268                     ImsUtImplBase.CALL_BARRING_SPECIFIC_INCOMING_CALLS,
1269                     callAction,
1270                     obtainMessage(EVENT_SET_COMPLETE, this),
1271                     icbNum);
1272         } catch (ImsException e) {
1273             Rlog.d(LOG_TAG, "processIcbMmiCodeForUpdate:Could not get UT handle for updating ICB.");
1274         }
1275     }
1276 
1277     @UnsupportedAppUsage
getErrorMessage(AsyncResult ar)1278     private CharSequence getErrorMessage(AsyncResult ar) {
1279         CharSequence errorMessage;
1280         return ((errorMessage = getMmiErrorMessage(ar)) != null) ? errorMessage :
1281                 mContext.getText(com.android.internal.R.string.mmiError);
1282     }
1283 
getMmiErrorMessage(AsyncResult ar)1284     private CharSequence getMmiErrorMessage(AsyncResult ar) {
1285         if (ar.exception instanceof ImsException) {
1286             switch (((ImsException) ar.exception).getCode()) {
1287                 case ImsReasonInfo.CODE_FDN_BLOCKED:
1288                     return mContext.getText(com.android.internal.R.string.mmiFdnError);
1289                 case ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_DIAL:
1290                     return mContext.getText(com.android.internal.R.string.stk_cc_ss_to_dial);
1291                 case ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_USSD:
1292                     return mContext.getText(com.android.internal.R.string.stk_cc_ss_to_ussd);
1293                 case ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_SS:
1294                     return mContext.getText(com.android.internal.R.string.stk_cc_ss_to_ss);
1295                 case ImsReasonInfo.CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO:
1296                     return mContext.getText(com.android.internal.R.string.stk_cc_ss_to_dial_video);
1297                 default:
1298                     return null;
1299             }
1300         } else if (ar.exception instanceof CommandException) {
1301             CommandException err = (CommandException) ar.exception;
1302             if (err.getCommandError() == CommandException.Error.FDN_CHECK_FAILURE) {
1303                 return mContext.getText(com.android.internal.R.string.mmiFdnError);
1304             } else if (err.getCommandError() == CommandException.Error.SS_MODIFIED_TO_DIAL) {
1305                 return mContext.getText(com.android.internal.R.string.stk_cc_ss_to_dial);
1306             } else if (err.getCommandError() == CommandException.Error.SS_MODIFIED_TO_USSD) {
1307                 return mContext.getText(com.android.internal.R.string.stk_cc_ss_to_ussd);
1308             } else if (err.getCommandError() == CommandException.Error.SS_MODIFIED_TO_SS) {
1309                 return mContext.getText(com.android.internal.R.string.stk_cc_ss_to_ss);
1310             } else if (err.getCommandError() == CommandException.Error.SS_MODIFIED_TO_DIAL_VIDEO) {
1311                 return mContext.getText(com.android.internal.R.string.stk_cc_ss_to_dial_video);
1312             } else if (err.getCommandError() == CommandException.Error.INTERNAL_ERR) {
1313                 return mContext.getText(com.android.internal.R.string.mmiError);
1314             }
1315         }
1316         return null;
1317     }
1318 
1319     @UnsupportedAppUsage
getScString()1320     private CharSequence getScString() {
1321         if (mSc != null) {
1322             if (isServiceCodeCallBarring(mSc)) {
1323                 return mContext.getText(com.android.internal.R.string.BaMmi);
1324             } else if (isServiceCodeCallForwarding(mSc)) {
1325                 return mContext.getText(com.android.internal.R.string.CfMmi);
1326             } else if (mSc.equals(SC_PWD)) {
1327                 return mContext.getText(com.android.internal.R.string.PwdMmi);
1328             } else if (mSc.equals(SC_WAIT)) {
1329                 return mContext.getText(com.android.internal.R.string.CwMmi);
1330             } else if (mSc.equals(SC_CLIP)) {
1331                 return mContext.getText(com.android.internal.R.string.ClipMmi);
1332             } else if (mSc.equals(SC_CLIR)) {
1333                 return mContext.getText(com.android.internal.R.string.ClirMmi);
1334             } else if (mSc.equals(SC_COLP)) {
1335                 return mContext.getText(com.android.internal.R.string.ColpMmi);
1336             } else if (mSc.equals(SC_COLR)) {
1337                 return mContext.getText(com.android.internal.R.string.ColrMmi);
1338             } else if (mSc.equals(SC_BS_MT)) {
1339                 return IcbDnMmi;
1340             } else if (mSc.equals(SC_BAICa)) {
1341                 return IcbAnonymousMmi;
1342             }
1343         }
1344 
1345         return "";
1346     }
1347 
1348     private void
onSetComplete(Message msg, AsyncResult ar)1349     onSetComplete(Message msg, AsyncResult ar){
1350         StringBuilder sb = new StringBuilder(getScString());
1351         sb.append("\n");
1352 
1353         if (ar.exception != null) {
1354             mState = State.FAILED;
1355 
1356             if (ar.exception instanceof CommandException) {
1357                 CommandException err = (CommandException) ar.exception;
1358                 CharSequence errorMessage;
1359                 if (err.getCommandError() == CommandException.Error.PASSWORD_INCORRECT) {
1360                     sb.append(mContext.getText(
1361                             com.android.internal.R.string.passwordIncorrect));
1362                 } else if ((errorMessage = getMmiErrorMessage(ar)) != null) {
1363                     sb.append(errorMessage);
1364                 } else if (err.getMessage() != null) {
1365                     sb.append(err.getMessage());
1366                 } else {
1367                     sb.append(mContext.getText(com.android.internal.R.string.mmiError));
1368                 }
1369             } else if (ar.exception instanceof ImsException) {
1370                 sb.append(getImsErrorMessage(ar));
1371             }
1372         } else if (ar.result != null && (int) ar.result == CommandsInterface.SS_STATUS_UNKNOWN) {
1373             mState = State.FAILED;
1374             sb = null;
1375         } else if (isActivate()) {
1376             mState = State.COMPLETE;
1377             if (mIsCallFwdReg) {
1378                 sb.append(mContext.getText(
1379                         com.android.internal.R.string.serviceRegistered));
1380             } else {
1381                 sb.append(mContext.getText(
1382                         com.android.internal.R.string.serviceEnabled));
1383             }
1384             // Record CLIR setting
1385             if (mSc.equals(SC_CLIR)) {
1386                 mPhone.saveClirSetting(CommandsInterface.CLIR_INVOCATION);
1387             }
1388         } else if (isDeactivate()) {
1389             mState = State.COMPLETE;
1390             sb.append(mContext.getText(
1391                     com.android.internal.R.string.serviceDisabled));
1392             // Record CLIR setting
1393             if (mSc.equals(SC_CLIR)) {
1394                 mPhone.saveClirSetting(CommandsInterface.CLIR_SUPPRESSION);
1395             }
1396         } else if (isRegister()) {
1397             mState = State.COMPLETE;
1398             sb.append(mContext.getText(
1399                     com.android.internal.R.string.serviceRegistered));
1400         } else if (isErasure()) {
1401             mState = State.COMPLETE;
1402             sb.append(mContext.getText(
1403                     com.android.internal.R.string.serviceErased));
1404         } else {
1405             mState = State.FAILED;
1406             sb.append(mContext.getText(
1407                     com.android.internal.R.string.mmiError));
1408         }
1409 
1410         mMessage = sb;
1411         Rlog.d(LOG_TAG, "onSetComplete: mmi=" + this);
1412         mPhone.onMMIDone(this);
1413     }
1414 
1415     /**
1416      * @param serviceClass 1 bit of the service class bit vectory
1417      * @return String to be used for call forward query MMI response text.
1418      *        Returns null if unrecognized
1419      */
1420 
1421     @UnsupportedAppUsage
1422     private CharSequence
serviceClassToCFString(int serviceClass)1423     serviceClassToCFString (int serviceClass) {
1424         switch (serviceClass) {
1425             case SERVICE_CLASS_VOICE:
1426                 return mContext.getText(com.android.internal.R.string.serviceClassVoice);
1427             case SERVICE_CLASS_DATA:
1428                 return mContext.getText(com.android.internal.R.string.serviceClassData);
1429             case SERVICE_CLASS_FAX:
1430                 return mContext.getText(com.android.internal.R.string.serviceClassFAX);
1431             case SERVICE_CLASS_SMS:
1432                 return mContext.getText(com.android.internal.R.string.serviceClassSMS);
1433             case SERVICE_CLASS_DATA_SYNC:
1434                 return mContext.getText(com.android.internal.R.string.serviceClassDataSync);
1435             case SERVICE_CLASS_DATA_ASYNC:
1436                 return mContext.getText(com.android.internal.R.string.serviceClassDataAsync);
1437             case SERVICE_CLASS_PACKET:
1438                 return mContext.getText(com.android.internal.R.string.serviceClassPacket);
1439             case SERVICE_CLASS_PAD:
1440                 return mContext.getText(com.android.internal.R.string.serviceClassPAD);
1441             default:
1442                 return null;
1443         }
1444     }
1445 
1446     /** one CallForwardInfo + serviceClassMask -> one line of text */
1447     private CharSequence
makeCFQueryResultMessage(CallForwardInfo info, int serviceClassMask)1448     makeCFQueryResultMessage(CallForwardInfo info, int serviceClassMask) {
1449         CharSequence template;
1450         String sources[] = {"{0}", "{1}", "{2}"};
1451         CharSequence destinations[] = new CharSequence[3];
1452         boolean needTimeTemplate;
1453 
1454         // CF_REASON_NO_REPLY also has a time value associated with
1455         // it. All others don't.
1456 
1457         needTimeTemplate =
1458             (info.reason == CommandsInterface.CF_REASON_NO_REPLY);
1459 
1460         if (info.status == 1) {
1461             if (needTimeTemplate) {
1462                 template = mContext.getText(
1463                         com.android.internal.R.string.cfTemplateForwardedTime);
1464             } else {
1465                 template = mContext.getText(
1466                         com.android.internal.R.string.cfTemplateForwarded);
1467             }
1468         } else if (info.status == 0 && isEmptyOrNull(info.number)) {
1469             template = mContext.getText(
1470                         com.android.internal.R.string.cfTemplateNotForwarded);
1471         } else { /* (info.status == 0) && !isEmptyOrNull(info.number) */
1472             // A call forward record that is not active but contains
1473             // a phone number is considered "registered"
1474 
1475             if (needTimeTemplate) {
1476                 template = mContext.getText(
1477                         com.android.internal.R.string.cfTemplateRegisteredTime);
1478             } else {
1479                 template = mContext.getText(
1480                         com.android.internal.R.string.cfTemplateRegistered);
1481             }
1482         }
1483 
1484         // In the template (from strings.xmls)
1485         //         {0} is one of "bearerServiceCode*"
1486         //        {1} is dialing number
1487         //      {2} is time in seconds
1488 
1489         destinations[0] = serviceClassToCFString(info.serviceClass & serviceClassMask);
1490         destinations[1] = PhoneNumberUtils.stringFromStringAndTOA(info.number, info.toa);
1491         destinations[2] = Integer.toString(info.timeSeconds);
1492 
1493         if (info.reason == CommandsInterface.CF_REASON_UNCONDITIONAL &&
1494                 (info.serviceClass & serviceClassMask)
1495                         == CommandsInterface.SERVICE_CLASS_VOICE) {
1496             boolean cffEnabled = (info.status == 1);
1497             mPhone.setVoiceCallForwardingFlag(mIccRecords, 1, cffEnabled, info.number);
1498         }
1499 
1500         return TextUtils.replace(template, sources, destinations);
1501     }
1502 
1503 
1504     private void
onQueryCfComplete(AsyncResult ar)1505     onQueryCfComplete(AsyncResult ar) {
1506         StringBuilder sb = new StringBuilder(getScString());
1507         sb.append("\n");
1508 
1509         if (ar.exception != null) {
1510             mState = State.FAILED;
1511 
1512             if (ar.exception instanceof ImsException) {
1513                 sb.append(getImsErrorMessage(ar));
1514             }
1515             else {
1516                 sb.append(getErrorMessage(ar));
1517             }
1518         } else if (ar.result instanceof CallForwardInfo[] &&
1519                    ((CallForwardInfo[]) ar.result)[0].status
1520                     == CommandsInterface.SS_STATUS_UNKNOWN) {
1521             sb = null;
1522             mState = State.FAILED;
1523         } else {
1524             CallForwardInfo infos[];
1525 
1526             infos = (CallForwardInfo[]) ar.result;
1527 
1528             if (infos == null || infos.length == 0) {
1529                 // Assume the default is not active
1530                 sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled));
1531 
1532                 // Set unconditional CFF in SIM to false
1533                 mPhone.setVoiceCallForwardingFlag(mIccRecords, 1, false, null);
1534             } else {
1535 
1536                 SpannableStringBuilder tb = new SpannableStringBuilder();
1537 
1538                 // Each bit in the service class gets its own result line
1539                 // The service classes may be split up over multiple
1540                 // CallForwardInfos. So, for each service class, find out
1541                 // which CallForwardInfo represents it and then build
1542                 // the response text based on that
1543 
1544                 for (int serviceClassMask = 1
1545                             ; serviceClassMask <= SERVICE_CLASS_MAX
1546                             ; serviceClassMask <<= 1
1547                 ) {
1548                     for (int i = 0, s = infos.length; i < s ; i++) {
1549                         if ((serviceClassMask & infos[i].serviceClass) != 0) {
1550                             tb.append(makeCFQueryResultMessage(infos[i],
1551                                             serviceClassMask));
1552                             tb.append("\n");
1553                         }
1554                     }
1555                 }
1556                 sb.append(tb);
1557             }
1558 
1559             mState = State.COMPLETE;
1560         }
1561 
1562         mMessage = sb;
1563         Rlog.d(LOG_TAG, "onQueryCfComplete: mmi=" + this);
1564         mPhone.onMMIDone(this);
1565 
1566     }
1567 
onSuppSvcQueryComplete(AsyncResult ar)1568     private void onSuppSvcQueryComplete(AsyncResult ar) {
1569         StringBuilder sb = new StringBuilder(getScString());
1570         sb.append("\n");
1571 
1572         mState = State.FAILED;
1573         if (ar.exception != null) {
1574             if (ar.exception instanceof ImsException) {
1575                 sb.append(getImsErrorMessage(ar));
1576             } else {
1577                 sb.append(getErrorMessage(ar));
1578             }
1579         } else {
1580             if (ar.result instanceof ImsSsInfo) {
1581                 Rlog.d(LOG_TAG, "onSuppSvcQueryComplete: Received CLIP/COLP/COLR Response.");
1582                 // Response for CLIP, COLP and COLR queries.
1583                 ImsSsInfo ssInfo = (ImsSsInfo) ar.result;
1584                 if (ssInfo != null) {
1585                     Rlog.d(LOG_TAG,
1586                             "onSuppSvcQueryComplete: ImsSsInfo mStatus = " + ssInfo.getStatus());
1587                     if (ssInfo.getProvisionStatus() == ImsSsInfo.SERVICE_NOT_PROVISIONED) {
1588                         sb.append(mContext.getText(
1589                                 com.android.internal.R.string.serviceNotProvisioned));
1590                         mState = State.COMPLETE;
1591                     } else if (ssInfo.getStatus() == ImsSsInfo.DISABLED) {
1592                         sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled));
1593                         mState = State.COMPLETE;
1594                     } else if (ssInfo.getStatus() == ImsSsInfo.ENABLED) {
1595                         sb.append(mContext.getText(com.android.internal.R.string.serviceEnabled));
1596                         mState = State.COMPLETE;
1597                     } else {
1598                         sb.append(mContext.getText(com.android.internal.R.string.mmiError));
1599                     }
1600                 } else {
1601                     sb.append(mContext.getText(com.android.internal.R.string.mmiError));
1602                 }
1603 
1604             } else {
1605                 Rlog.d(LOG_TAG,
1606                         "onSuppSvcQueryComplete: Received Call Barring/CSFB CLIP Response.");
1607                 // Response for Call Barring and CSFB CLIP queries.
1608                 int[] infos = (int[]) ar.result;
1609                 if (infos == null || infos.length == 0) {
1610                     sb.append(mContext.getText(com.android.internal.R.string.mmiError));
1611                 } else {
1612                     if (infos[0] == 1) {
1613                         sb.append(mContext.getText(com.android.internal.R.string.serviceEnabled));
1614                         mState = State.COMPLETE;
1615                     } else {
1616                         sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled));
1617                         mState = State.COMPLETE;
1618                     }
1619                 }
1620             }
1621         }
1622 
1623         mMessage = sb;
1624         Rlog.d(LOG_TAG, "onSuppSvcQueryComplete mmi=" + this);
1625         mPhone.onMMIDone(this);
1626     }
1627 
onIcbQueryComplete(AsyncResult ar)1628     private void onIcbQueryComplete(AsyncResult ar) {
1629         Rlog.d(LOG_TAG, "onIcbQueryComplete mmi=" + this);
1630         StringBuilder sb = new StringBuilder(getScString());
1631         sb.append("\n");
1632 
1633         if (ar.exception != null) {
1634             mState = State.FAILED;
1635 
1636             if (ar.exception instanceof ImsException) {
1637                 sb.append(getImsErrorMessage(ar));
1638             } else {
1639                 sb.append(getErrorMessage(ar));
1640             }
1641         } else {
1642             List<ImsSsInfo> infos = null;
1643             try {
1644                 infos = (List<ImsSsInfo>) ar.result;
1645             } catch (ClassCastException cce) {
1646                 // TODO in R: #157# still has ImsSsInfo[] type, fix the type in IImsUtListener.aidl.
1647                 infos = Arrays.asList((ImsSsInfo[]) ar.result);
1648             }
1649             if (infos == null || infos.size() == 0) {
1650                 sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled));
1651             } else {
1652                 ImsSsInfo info;
1653                 for (int i = 0, s = infos.size(); i < s; i++) {
1654                     info = infos.get(i);
1655                     if (info.getIncomingCommunicationBarringNumber() != null) {
1656                         sb.append("Num: " + info.getIncomingCommunicationBarringNumber()
1657                                 + " status: " + info.getStatus() + "\n");
1658                     } else if (info.getStatus() == 1) {
1659                         sb.append(mContext.getText(com.android.internal
1660                                 .R.string.serviceEnabled));
1661                     } else {
1662                         sb.append(mContext.getText(com.android.internal
1663                                 .R.string.serviceDisabled));
1664                     }
1665                 }
1666             }
1667             mState = State.COMPLETE;
1668         }
1669         mMessage = sb;
1670         mPhone.onMMIDone(this);
1671     }
1672 
onQueryClirComplete(AsyncResult ar)1673     private void onQueryClirComplete(AsyncResult ar) {
1674         StringBuilder sb = new StringBuilder(getScString());
1675         sb.append("\n");
1676         mState = State.FAILED;
1677 
1678         if (ar.exception != null) {
1679             if (ar.exception instanceof ImsException) {
1680                 sb.append(getImsErrorMessage(ar));
1681             } else {
1682                 sb.append(getErrorMessage(ar));
1683             }
1684         } else {
1685             int[] clirInfo = (int[]) ar.result;
1686             // ssInfo.getClirOutgoingState() = The 'n' parameter from TS 27.007 7.7
1687             // ssInfo.getClirInterrogationStatus() = The 'm' parameter from TS 27.007 7.7
1688             Rlog.d(LOG_TAG, "onQueryClirComplete: CLIR param n=" + clirInfo[0]
1689                     + " m=" + clirInfo[1]);
1690 
1691             // 'm' parameter.
1692             switch (clirInfo[1]) {
1693                 case ImsSsInfo.CLIR_STATUS_NOT_PROVISIONED:
1694                     sb.append(mContext.getText(
1695                             com.android.internal.R.string.serviceNotProvisioned));
1696                     mState = State.COMPLETE;
1697                     break;
1698                 case ImsSsInfo.CLIR_STATUS_PROVISIONED_PERMANENT:
1699                     sb.append(mContext.getText(
1700                             com.android.internal.R.string.CLIRPermanent));
1701                     mState = State.COMPLETE;
1702                     break;
1703                 case ImsSsInfo.CLIR_STATUS_TEMPORARILY_RESTRICTED:
1704                     // 'n' parameter.
1705                     switch (clirInfo[0]) {
1706                         case ImsSsInfo.CLIR_OUTGOING_DEFAULT:
1707                             sb.append(mContext.getText(
1708                                     com.android.internal.R.string.CLIRDefaultOnNextCallOn));
1709                             mState = State.COMPLETE;
1710                             break;
1711                         case ImsSsInfo.CLIR_OUTGOING_INVOCATION:
1712                             sb.append(mContext.getText(
1713                                     com.android.internal.R.string.CLIRDefaultOnNextCallOn));
1714                             mState = State.COMPLETE;
1715                             break;
1716                         case ImsSsInfo.CLIR_OUTGOING_SUPPRESSION:
1717                             sb.append(mContext.getText(
1718                                     com.android.internal.R.string.CLIRDefaultOnNextCallOff));
1719                             mState = State.COMPLETE;
1720                             break;
1721                         default:
1722                             sb.append(mContext.getText(
1723                                     com.android.internal.R.string.mmiError));
1724                             mState = State.FAILED;
1725                     }
1726                     break;
1727                 case ImsSsInfo.CLIR_STATUS_TEMPORARILY_ALLOWED:
1728                     // 'n' parameter.
1729                     switch (clirInfo[0]) {
1730                         case ImsSsInfo.CLIR_OUTGOING_DEFAULT:
1731                             sb.append(mContext.getText(
1732                                     com.android.internal.R.string.CLIRDefaultOffNextCallOff));
1733                             mState = State.COMPLETE;
1734                             break;
1735                         case ImsSsInfo.CLIR_OUTGOING_INVOCATION:
1736                             sb.append(mContext.getText(
1737                                     com.android.internal.R.string.CLIRDefaultOffNextCallOn));
1738                             mState = State.COMPLETE;
1739                             break;
1740                         case ImsSsInfo.CLIR_OUTGOING_SUPPRESSION:
1741                             sb.append(mContext.getText(
1742                                     com.android.internal.R.string.CLIRDefaultOffNextCallOff));
1743                             mState = State.COMPLETE;
1744                             break;
1745                         default:
1746                             sb.append(mContext.getText(
1747                                     com.android.internal.R.string.mmiError));
1748                             mState = State.FAILED;
1749                     }
1750                     break;
1751                 default:
1752                     sb.append(mContext.getText(
1753                             com.android.internal.R.string.mmiError));
1754                     mState = State.FAILED;
1755             }
1756         }
1757 
1758         mMessage = sb;
1759         Rlog.d(LOG_TAG, "onQueryClirComplete mmi=" + this);
1760         mPhone.onMMIDone(this);
1761     }
1762 
1763     private void
onQueryComplete(AsyncResult ar)1764     onQueryComplete(AsyncResult ar) {
1765         StringBuilder sb = new StringBuilder(getScString());
1766         sb.append("\n");
1767 
1768         mState = State.FAILED;
1769         if (ar.exception != null) {
1770             if (ar.exception instanceof ImsException) {
1771                 sb.append(getImsErrorMessage(ar));
1772             } else {
1773                 sb.append(getErrorMessage(ar));
1774             }
1775         } else if ((ar.result instanceof int[]) &&
1776                    ((int[])ar.result)[0] == CommandsInterface.SS_STATUS_UNKNOWN) {
1777             sb = null;
1778         } else {
1779             int[] ints = (int[])ar.result;
1780 
1781             if (ints != null && ints.length != 0) {
1782                 if (ints[0] == 0) {
1783                     sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled));
1784                     mState = State.COMPLETE;
1785                 } else if (mSc.equals(SC_WAIT)) {
1786                     // Call Waiting includes additional data in the response.
1787                     sb.append(createQueryCallWaitingResultMessage(ints[1]));
1788                     mState = State.COMPLETE;
1789                 } else if (ints[0] == 1) {
1790                     // for all other services, treat it as a boolean
1791                     sb.append(mContext.getText(com.android.internal.R.string.serviceEnabled));
1792                     mState = State.COMPLETE;
1793                 } else {
1794                     sb.append(mContext.getText(com.android.internal.R.string.mmiError));
1795                 }
1796             } else {
1797                 sb.append(mContext.getText(com.android.internal.R.string.mmiError));
1798             }
1799         }
1800 
1801         mMessage = sb;
1802         Rlog.d(LOG_TAG, "onQueryComplete mmi=" + this);
1803         mPhone.onMMIDone(this);
1804     }
1805 
1806     private CharSequence
createQueryCallWaitingResultMessage(int serviceClass)1807     createQueryCallWaitingResultMessage(int serviceClass) {
1808         StringBuilder sb = new StringBuilder(
1809                 mContext.getText(com.android.internal.R.string.serviceEnabledFor));
1810 
1811         for (int classMask = 1
1812                     ; classMask <= SERVICE_CLASS_MAX
1813                     ; classMask <<= 1
1814         ) {
1815             if ((classMask & serviceClass) != 0) {
1816                 sb.append("\n");
1817                 sb.append(serviceClassToCFString(classMask & serviceClass));
1818             }
1819         }
1820         return sb;
1821     }
1822 
getImsErrorMessage(AsyncResult ar)1823     private CharSequence getImsErrorMessage(AsyncResult ar) {
1824         ImsException error = (ImsException) ar.exception;
1825         CharSequence errorMessage;
1826         if ((errorMessage = getMmiErrorMessage(ar)) != null) {
1827             return errorMessage;
1828         } else if (error.getMessage() != null) {
1829             return error.getMessage();
1830         } else {
1831             return getErrorMessage(ar);
1832         }
1833     }
1834 
1835     @Override
getUssdCallbackReceiver()1836     public ResultReceiver getUssdCallbackReceiver() {
1837         return this.mCallbackReceiver;
1838     }
1839 
1840     /**
1841      * Process IMS SS Data received.
1842      */
processImsSsData(AsyncResult data)1843     public void processImsSsData(AsyncResult data) throws ImsException {
1844         try {
1845             ImsSsData ssData = (ImsSsData) data.result;
1846             parseSsData(ssData);
1847         } catch (ClassCastException | NullPointerException ex) {
1848             throw new ImsException("Exception in parsing SS Data", 0);
1849         }
1850     }
1851 
parseSsData(ImsSsData ssData)1852     void parseSsData(ImsSsData ssData) {
1853         ImsException ex = (ssData.getResult() != ImsSsData.RESULT_SUCCESS)
1854                 ? new ImsException(null, ssData.getResult()) : null;
1855         mSc = getScStringFromScType(ssData.getServiceType());
1856         mAction = getActionStringFromReqType(ssData.getRequestType());
1857         Rlog.d(LOG_TAG, "parseSsData msc = " + mSc + ", action = " + mAction + ", ex = " + ex);
1858 
1859         switch (ssData.getRequestType()) {
1860             case ImsSsData.SS_ACTIVATION:
1861             case ImsSsData.SS_DEACTIVATION:
1862             case ImsSsData.SS_REGISTRATION:
1863             case ImsSsData.SS_ERASURE:
1864                 if ((ssData.getResult() == ImsSsData.RESULT_SUCCESS)
1865                         && ssData.isTypeUnConditional()) {
1866                     /*
1867                      * When ssData.serviceType is unconditional (SS_CFU or SS_CF_ALL) and
1868                      * ssData.requestType is activate/register and
1869                      * ServiceClass is Voice/Video/None, turn on voice call forwarding.
1870                      */
1871                     boolean cffEnabled = ((ssData.getRequestType() == ImsSsData.SS_ACTIVATION
1872                             || ssData.getRequestType() == ImsSsData.SS_REGISTRATION)
1873                             && isServiceClassVoiceVideoOrNone(ssData.getServiceClass()));
1874 
1875                     Rlog.d(LOG_TAG, "setCallForwardingFlag cffEnabled: " + cffEnabled);
1876                     if (mIccRecords != null) {
1877                         Rlog.d(LOG_TAG, "setVoiceCallForwardingFlag done from SS Info.");
1878                         //Only CF status is set here as part of activation/registration,
1879                         //number is not available until interrogation.
1880                         mPhone.setVoiceCallForwardingFlag(1, cffEnabled, null);
1881                     } else {
1882                         Rlog.e(LOG_TAG, "setCallForwardingFlag aborted. sim records is null.");
1883                     }
1884                 }
1885                 onSetComplete(null, new AsyncResult(null, ssData.getCallForwardInfo(), ex));
1886                 break;
1887             case ImsSsData.SS_INTERROGATION:
1888                 if (ssData.isTypeClir()) {
1889                     Rlog.d(LOG_TAG, "CLIR INTERROGATION");
1890                     onQueryClirComplete(new AsyncResult(null, ssData.getSuppServiceInfoCompat(), ex));
1891                 } else if (ssData.isTypeCF()) {
1892                     Rlog.d(LOG_TAG, "CALL FORWARD INTERROGATION");
1893                     // Have to translate to an array, since the modem still returns it in the
1894                     // ImsCallForwardInfo[] format.
1895                     List<ImsCallForwardInfo> mCfInfos = ssData.getCallForwardInfo();
1896                     ImsCallForwardInfo[] mCfInfosCompat = null;
1897                     if (mCfInfos != null) {
1898                         mCfInfosCompat = new ImsCallForwardInfo[mCfInfos.size()];
1899                         mCfInfosCompat = mCfInfos.toArray(mCfInfosCompat);
1900                     }
1901                     onQueryCfComplete(new AsyncResult(null, mPhone.handleCfQueryResult(
1902                             mCfInfosCompat), ex));
1903                 } else if (ssData.isTypeBarring()) {
1904                     onSuppSvcQueryComplete(new AsyncResult(null, ssData.getSuppServiceInfoCompat(),
1905                             ex));
1906                 } else if (ssData.isTypeColr() || ssData.isTypeClip() || ssData.isTypeColp()) {
1907                     onSuppSvcQueryComplete(new AsyncResult(null, ssData.getSuppServiceInfo().get(0),
1908                             ex));
1909                 } else if (ssData.isTypeIcb()) {
1910                     onIcbQueryComplete(new AsyncResult(null, ssData.getSuppServiceInfo(), ex));
1911                 } else {
1912                     onQueryComplete(new AsyncResult(null, ssData.getSuppServiceInfoCompat(), ex));
1913                 }
1914                 break;
1915             default:
1916                 Rlog.e(LOG_TAG, "Invaid requestType in SSData : " + ssData.getRequestType());
1917                 break;
1918         }
1919     }
1920 
getScStringFromScType(int serviceType)1921     private String getScStringFromScType(int serviceType) {
1922         switch (serviceType) {
1923             case ImsSsData.SS_CFU:
1924                 return SC_CFU;
1925             case ImsSsData.SS_CF_BUSY:
1926                 return SC_CFB;
1927             case ImsSsData.SS_CF_NO_REPLY:
1928                 return SC_CFNRy;
1929             case ImsSsData.SS_CF_NOT_REACHABLE:
1930                 return SC_CFNR;
1931             case ImsSsData.SS_CF_ALL:
1932                 return SC_CF_All;
1933             case ImsSsData.SS_CF_ALL_CONDITIONAL:
1934                 return SC_CF_All_Conditional;
1935             case ImsSsData.SS_CLIP:
1936                 return SC_CLIP;
1937             case ImsSsData.SS_CLIR:
1938                 return SC_CLIR;
1939             case ImsSsData.SS_COLP:
1940                 return SC_COLP;
1941             case ImsSsData.SS_COLR:
1942                 return SC_COLR;
1943             case ImsSsData.SS_CNAP:
1944                 return SC_CNAP;
1945             case ImsSsData.SS_WAIT:
1946                 return SC_WAIT;
1947             case ImsSsData.SS_BAOC:
1948                 return SC_BAOC;
1949             case ImsSsData.SS_BAOIC:
1950                 return SC_BAOIC;
1951             case ImsSsData.SS_BAOIC_EXC_HOME:
1952                 return SC_BAOICxH;
1953             case ImsSsData.SS_BAIC:
1954                 return SC_BAIC;
1955             case ImsSsData.SS_BAIC_ROAMING:
1956                 return SC_BAICr;
1957             case ImsSsData.SS_ALL_BARRING:
1958                 return SC_BA_ALL;
1959             case ImsSsData.SS_OUTGOING_BARRING:
1960                 return SC_BA_MO;
1961             case ImsSsData.SS_INCOMING_BARRING:
1962                 return SC_BA_MT;
1963             case ImsSsData.SS_INCOMING_BARRING_DN:
1964                 return SC_BS_MT;
1965             case ImsSsData.SS_INCOMING_BARRING_ANONYMOUS:
1966                 return SC_BAICa;
1967             default:
1968                 return null;
1969         }
1970     }
1971 
getActionStringFromReqType(int requestType)1972     private String getActionStringFromReqType(int requestType) {
1973         switch (requestType) {
1974             case ImsSsData.SS_ACTIVATION:
1975                 return ACTION_ACTIVATE;
1976             case ImsSsData.SS_DEACTIVATION:
1977                 return ACTION_DEACTIVATE;
1978             case ImsSsData.SS_INTERROGATION:
1979                 return ACTION_INTERROGATE;
1980             case ImsSsData.SS_REGISTRATION:
1981                 return ACTION_REGISTER;
1982             case ImsSsData.SS_ERASURE:
1983                 return ACTION_ERASURE;
1984             default:
1985                 return null;
1986         }
1987     }
1988 
isServiceClassVoiceVideoOrNone(int serviceClass)1989     private boolean isServiceClassVoiceVideoOrNone(int serviceClass) {
1990         return ((serviceClass == SERVICE_CLASS_NONE) || (serviceClass == SERVICE_CLASS_VOICE)
1991                 || (serviceClass == (SERVICE_CLASS_PACKET + SERVICE_CLASS_DATA_SYNC)));
1992     }
1993 
isSsInfo()1994     public boolean isSsInfo() {
1995         return mIsSsInfo;
1996     }
1997 
setIsSsInfo(boolean isSsInfo)1998     public void setIsSsInfo(boolean isSsInfo) {
1999         mIsSsInfo = isSsInfo;
2000     }
2001 
2002     /***
2003      * TODO: It would be nice to have a method here that can take in a dialstring and
2004      * figure out if there is an MMI code embedded within it.  This code would replace
2005      * some of the string parsing functionality in the Phone App's
2006      * SpecialCharSequenceMgr class.
2007      */
2008 
2009     @Override
toString()2010     public String toString() {
2011         StringBuilder sb = new StringBuilder("ImsPhoneMmiCode {");
2012 
2013         sb.append("State=" + getState());
2014         if (mAction != null) sb.append(" action=" + mAction);
2015         if (mSc != null) sb.append(" sc=" + mSc);
2016         if (mSia != null) sb.append(" sia=" + mSia);
2017         if (mSib != null) sb.append(" sib=" + mSib);
2018         if (mSic != null) sb.append(" sic=" + mSic);
2019         if (mPoundString != null) sb.append(" poundString=" + Rlog.pii(LOG_TAG, mPoundString));
2020         if (mDialingNumber != null) sb.append(" dialingNumber="
2021                 + Rlog.pii(LOG_TAG, mDialingNumber));
2022         if (mPwd != null) sb.append(" pwd=" + Rlog.pii(LOG_TAG, mPwd));
2023         if (mCallbackReceiver != null) sb.append(" hasReceiver");
2024         sb.append("}");
2025         return sb.toString();
2026     }
2027 
2028     @Override
isNetworkInitiatedUssd()2029     public boolean isNetworkInitiatedUssd() {
2030         return mIsNetworkInitiatedUSSD;
2031     }
2032 }
2033