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