ImsPhoneMmiCode.java revision 4419d18a631493663dd50cc6e7616d74c1b48e7d
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
17package com.android.internal.telephony.imsphone;
18
19import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_DATA;
20import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_DATA_ASYNC;
21import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_DATA_SYNC;
22import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_FAX;
23import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_MAX;
24import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_NONE;
25import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_PACKET;
26import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_PAD;
27import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_SMS;
28import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE;
29
30import android.content.Context;
31import android.content.res.Resources;
32import android.os.AsyncResult;
33import android.os.Bundle;
34import android.os.Handler;
35import android.os.Message;
36import android.os.ResultReceiver;
37import android.telephony.PhoneNumberUtils;
38import android.telephony.Rlog;
39import android.telephony.ims.ImsReasonInfo;
40import android.telephony.ims.ImsSsData;
41import android.telephony.ims.ImsSsInfo;
42import android.text.SpannableStringBuilder;
43import android.text.TextUtils;
44
45import com.android.ims.ImsException;
46import com.android.ims.ImsUtInterface;
47import com.android.internal.telephony.CallForwardInfo;
48import com.android.internal.telephony.CallStateException;
49import com.android.internal.telephony.CommandException;
50import com.android.internal.telephony.CommandsInterface;
51import com.android.internal.telephony.MmiCode;
52import com.android.internal.telephony.Phone;
53import com.android.internal.telephony.uicc.IccRecords;
54
55import java.util.regex.Matcher;
56import 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 */
67public 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
240    static ImsPhoneMmiCode newFromDialString(String dialString, ImsPhone phone) {
241       return newFromDialString(dialString, phone, null);
242    }
243
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
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
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
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
356    makeEmptyNull (String s) {
357        if (s != null && s.length() == 0) return null;
358
359        return s;
360    }
361
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
378    isEmptyOrNull(CharSequence s) {
379        return s == null || (s.length() == 0);
380    }
381
382    private static int
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
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
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
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
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
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
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
514    getState() {
515        return mState;
516    }
517
518    @Override
519    public CharSequence
520    getMessage() {
521        return mMessage;
522    }
523
524    @Override
525    public Phone getPhone() { return mPhone; }
526
527    // inherited javadoc suffices
528    @Override
529    public void
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
547    public boolean isCancelable() {
548        /* Can only cancel pending USSD sessions. */
549        return mIsPendingUSSD;
550    }
551
552    //***** Instance Methods
553
554    String getDialingNumber() {
555        return mDialingNumber;
556    }
557
558    /** Does this dial string contain a structured or unstructured MMI code? */
559    boolean
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
566    isShortCode() {
567        return mPoundString == null
568                    && mDialingNumber != null && mDialingNumber.length() <= 2;
569
570    }
571
572    @Override
573    public String getDialString() {
574        return mPoundString;
575    }
576
577    static private boolean
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     */
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     */
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     */
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
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
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
692    boolean isActivate() {
693        return mAction != null && mAction.equals(ACTION_ACTIVATE);
694    }
695
696    boolean isDeactivate() {
697        return mAction != null && mAction.equals(ACTION_DEACTIVATE);
698    }
699
700    boolean isInterrogate() {
701        return mAction != null && mAction.equals(ACTION_INTERROGATE);
702    }
703
704    boolean isRegister() {
705        return mAction != null && mAction.equals(ACTION_REGISTER);
706    }
707
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     */
716    public boolean isPendingUSSD() {
717        return mIsPendingUSSD;
718    }
719
720    @Override
721    public boolean isUssdRequest() {
722        return mIsUssdRequest;
723    }
724
725    boolean
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     */
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
1711    public ResultReceiver getUssdCallbackReceiver() {
1712        return this.mCallbackReceiver;
1713    }
1714
1715    /**
1716     * Process IMS SS Data received.
1717     */
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
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
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
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
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
1866    public boolean isSsInfo() {
1867        return mIsSsInfo;
1868    }
1869
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
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