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