ImsPhoneMmiCode.java revision ce000ddfbb615f873e9fc0a4c68f992b12d3f82f
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 android.content.Context;
20import android.content.res.Resources;
21import android.os.AsyncResult;
22import android.os.Bundle;
23import android.os.Handler;
24import android.os.Message;
25import android.telephony.PhoneNumberUtils;
26import android.text.SpannableStringBuilder;
27import android.text.TextUtils;
28import android.telephony.Rlog;
29
30import com.android.ims.ImsException;
31import com.android.ims.ImsReasonInfo;
32import com.android.ims.ImsSsInfo;
33import com.android.ims.ImsUtInterface;
34import com.android.internal.telephony.CallForwardInfo;
35import com.android.internal.telephony.CommandException;
36import com.android.internal.telephony.CallStateException;
37import com.android.internal.telephony.CommandsInterface;
38import com.android.internal.telephony.uicc.IccRecords;
39
40import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_NONE;
41import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE;
42import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_DATA;
43import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_FAX;
44import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_SMS;
45import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_DATA_SYNC;
46import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_DATA_ASYNC;
47import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_PACKET;
48import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_PAD;
49import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_MAX;
50
51import com.android.internal.telephony.MmiCode;
52import com.android.internal.telephony.Phone;
53
54import java.util.regex.Pattern;
55import java.util.regex.Matcher;
56
57/**
58 * The motto for this file is:
59 *
60 * "NOTE:    By using the # as a separator, most cases are expected to be unambiguous."
61 *   -- TS 22.030 6.5.2
62 *
63 * {@hide}
64 *
65 */
66public final class ImsPhoneMmiCode extends Handler implements MmiCode {
67    static final String LOG_TAG = "ImsPhoneMmiCode";
68
69    //***** Constants
70
71    // Max Size of the Short Code (aka Short String from TS 22.030 6.5.2)
72    private static final int MAX_LENGTH_SHORT_CODE = 2;
73
74    // TS 22.030 6.5.2 Every Short String USSD command will end with #-key
75    // (known as #-String)
76    private static final char END_OF_USSD_COMMAND = '#';
77
78    // From TS 22.030 6.5.2
79    private static final String ACTION_ACTIVATE = "*";
80    private static final String ACTION_DEACTIVATE = "#";
81    private static final String ACTION_INTERROGATE = "*#";
82    private static final String ACTION_REGISTER = "**";
83    private static final String ACTION_ERASURE = "##";
84
85    // Supp Service codes from TS 22.030 Annex B
86
87    //Called line presentation
88    private static final String SC_CLIP    = "30";
89    private static final String SC_CLIR    = "31";
90    private static final String SC_COLP    = "76";
91    private static final String SC_COLR    = "77";
92
93    //Calling name presentation
94    private static final String SC_CNAP    = "300";
95
96    // Call Forwarding
97    private static final String SC_CFU     = "21";
98    private static final String SC_CFB     = "67";
99    private static final String SC_CFNRy   = "61";
100    private static final String SC_CFNR    = "62";
101    // Call Forwarding unconditional Timer
102    private static final String SC_CFUT     = "22";
103
104    private static final String SC_CF_All = "002";
105    private static final String SC_CF_All_Conditional = "004";
106
107    // Call Waiting
108    private static final String SC_WAIT     = "43";
109
110    // Call Barring
111    private static final String SC_BAOC         = "33";
112    private static final String SC_BAOIC        = "331";
113    private static final String SC_BAOICxH      = "332";
114    private static final String SC_BAIC         = "35";
115    private static final String SC_BAICr        = "351";
116
117    private static final String SC_BA_ALL       = "330";
118    private static final String SC_BA_MO        = "333";
119    private static final String SC_BA_MT        = "353";
120
121    // Incoming/Anonymous call barring
122    private static final String SC_BS_MT        = "156";
123    private static final String SC_BAICa        = "157";
124
125    // Supp Service Password registration
126    private static final String SC_PWD          = "03";
127
128    // PIN/PIN2/PUK/PUK2
129    private static final String SC_PIN          = "04";
130    private static final String SC_PIN2         = "042";
131    private static final String SC_PUK          = "05";
132    private static final String SC_PUK2         = "052";
133
134    //***** Event Constants
135
136    private static final int EVENT_SET_COMPLETE            = 0;
137    private static final int EVENT_QUERY_CF_COMPLETE       = 1;
138    private static final int EVENT_USSD_COMPLETE           = 2;
139    private static final int EVENT_QUERY_COMPLETE          = 3;
140    private static final int EVENT_SET_CFF_COMPLETE        = 4;
141    private static final int EVENT_USSD_CANCEL_COMPLETE    = 5;
142    private static final int EVENT_GET_CLIR_COMPLETE       = 6;
143    private static final int EVENT_SUPP_SVC_QUERY_COMPLETE = 7;
144    private static final int EVENT_QUERY_ICB_COMPLETE      = 10;
145
146    //***** Calling Line Presentation Constants
147    private static final int NUM_PRESENTATION_ALLOWED     = 0;
148    private static final int NUM_PRESENTATION_RESTRICTED  = 1;
149
150    //***** Supplementary Service Query Bundle Keys
151    // Used by IMS Service layer to put supp. serv. query
152    // responses into the ssInfo Bundle.
153    public static final String UT_BUNDLE_KEY_CLIR = "queryClir";
154    public static final String UT_BUNDLE_KEY_SSINFO = "imsSsInfo";
155
156    //***** Calling Line Identity Restriction Constants
157    // The 'm' parameter from TS 27.007 7.7
158    private static final int CLIR_NOT_PROVISIONED                    = 0;
159    private static final int CLIR_PROVISIONED_PERMANENT              = 1;
160    private static final int CLIR_PRESENTATION_RESTRICTED_TEMPORARY  = 3;
161    private static final int CLIR_PRESENTATION_ALLOWED_TEMPORARY     = 4;
162    // The 'n' parameter from TS 27.007 7.7
163    private static final int CLIR_DEFAULT     = 0;
164    private static final int CLIR_INVOCATION  = 1;
165    private static final int CLIR_SUPPRESSION = 2;
166
167    //***** Instance Variables
168
169    private ImsPhone mPhone;
170    private Context mContext;
171    private IccRecords mIccRecords;
172
173    private String mAction;              // One of ACTION_*
174    private String mSc;                  // Service Code
175    private String mSia, mSib, mSic;       // Service Info a,b,c
176    private String mPoundString;         // Entire MMI string up to and including #
177    private String mDialingNumber;
178    private String mPwd;                 // For password registration
179
180    private boolean mIsPendingUSSD;
181
182    private boolean mIsUssdRequest;
183
184    private boolean mIsCallFwdReg;
185    private State mState = State.PENDING;
186    private CharSequence mMessage;
187    //resgister/erasure of ICB (Specific DN)
188    static final String IcbDnMmi = "Specific Incoming Call Barring";
189    //ICB (Anonymous)
190    static final String IcbAnonymousMmi = "Anonymous Incoming Call Barring";
191    //***** Class Variables
192
193
194    // See TS 22.030 6.5.2 "Structure of the MMI"
195
196    private static Pattern sPatternSuppService = Pattern.compile(
197        "((\\*|#|\\*#|\\*\\*|##)(\\d{2,3})(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*)(\\*([^*#]*))?)?)?)?#)(.*)");
198/*       1  2                    3          4  5       6   7         8    9     10  11             12
199
200         1 = Full string up to and including #
201         2 = action (activation/interrogation/registration/erasure)
202         3 = service code
203         5 = SIA
204         7 = SIB
205         9 = SIC
206         10 = dialing number
207*/
208
209    private static final int MATCH_GROUP_POUND_STRING = 1;
210
211    private static final int MATCH_GROUP_ACTION = 2;
212                        //(activation/interrogation/registration/erasure)
213
214    private static final int MATCH_GROUP_SERVICE_CODE = 3;
215    private static final int MATCH_GROUP_SIA = 5;
216    private static final int MATCH_GROUP_SIB = 7;
217    private static final int MATCH_GROUP_SIC = 9;
218    private static final int MATCH_GROUP_PWD_CONFIRM = 11;
219    private static final int MATCH_GROUP_DIALING_NUMBER = 12;
220    static private String[] sTwoDigitNumberPattern;
221
222    //***** Public Class methods
223
224    /**
225     * Some dial strings in GSM are defined to do non-call setup
226     * things, such as modify or query supplementary service settings (eg, call
227     * forwarding). These are generally referred to as "MMI codes".
228     * We look to see if the dial string contains a valid MMI code (potentially
229     * with a dial string at the end as well) and return info here.
230     *
231     * If the dial string contains no MMI code, we return an instance with
232     * only "dialingNumber" set
233     *
234     * Please see flow chart in TS 22.030 6.5.3.2
235     */
236
237    static ImsPhoneMmiCode
238    newFromDialString(String dialString, ImsPhone phone) {
239        Matcher m;
240        ImsPhoneMmiCode ret = null;
241
242        m = sPatternSuppService.matcher(dialString);
243
244        // Is this formatted like a standard supplementary service code?
245        if (m.matches()) {
246            ret = new ImsPhoneMmiCode(phone);
247            ret.mPoundString = makeEmptyNull(m.group(MATCH_GROUP_POUND_STRING));
248            ret.mAction = makeEmptyNull(m.group(MATCH_GROUP_ACTION));
249            ret.mSc = makeEmptyNull(m.group(MATCH_GROUP_SERVICE_CODE));
250            ret.mSia = makeEmptyNull(m.group(MATCH_GROUP_SIA));
251            ret.mSib = makeEmptyNull(m.group(MATCH_GROUP_SIB));
252            ret.mSic = makeEmptyNull(m.group(MATCH_GROUP_SIC));
253            ret.mPwd = makeEmptyNull(m.group(MATCH_GROUP_PWD_CONFIRM));
254            ret.mDialingNumber = makeEmptyNull(m.group(MATCH_GROUP_DIALING_NUMBER));
255            // According to TS 22.030 6.5.2 "Structure of the MMI",
256            // the dialing number should not ending with #.
257            // The dialing number ending # is treated as unique USSD,
258            // eg, *400#16 digit number# to recharge the prepaid card
259            // in India operator(Mumbai MTNL)
260            if (ret.mDialingNumber != null &&
261                    ret.mDialingNumber.endsWith("#") &&
262                    dialString.endsWith("#")){
263                ret = new ImsPhoneMmiCode(phone);
264                ret.mPoundString = dialString;
265            }
266        } else if (dialString.endsWith("#")) {
267            // TS 22.030 sec 6.5.3.2
268            // "Entry of any characters defined in the 3GPP TS 23.038 [8] Default Alphabet
269            // (up to the maximum defined in 3GPP TS 24.080 [10]), followed by #SEND".
270
271            ret = new ImsPhoneMmiCode(phone);
272            ret.mPoundString = dialString;
273        } else if (isTwoDigitShortCode(phone.getContext(), dialString)) {
274            //Is a country-specific exception to short codes as defined in TS 22.030, 6.5.3.2
275            ret = null;
276        } else if (isShortCode(dialString, phone)) {
277            // this may be a short code, as defined in TS 22.030, 6.5.3.2
278            ret = new ImsPhoneMmiCode(phone);
279            ret.mDialingNumber = dialString;
280        }
281
282        return ret;
283    }
284
285    static ImsPhoneMmiCode
286    newNetworkInitiatedUssd(String ussdMessage, boolean isUssdRequest, ImsPhone phone) {
287        ImsPhoneMmiCode ret;
288
289        ret = new ImsPhoneMmiCode(phone);
290
291        ret.mMessage = ussdMessage;
292        ret.mIsUssdRequest = isUssdRequest;
293
294        // If it's a request, set to PENDING so that it's cancelable.
295        if (isUssdRequest) {
296            ret.mIsPendingUSSD = true;
297            ret.mState = State.PENDING;
298        } else {
299            ret.mState = State.COMPLETE;
300        }
301
302        return ret;
303    }
304
305    static ImsPhoneMmiCode newFromUssdUserInput(String ussdMessge, ImsPhone phone) {
306        ImsPhoneMmiCode ret = new ImsPhoneMmiCode(phone);
307
308        ret.mMessage = ussdMessge;
309        ret.mState = State.PENDING;
310        ret.mIsPendingUSSD = true;
311
312        return ret;
313    }
314
315    //***** Private Class methods
316
317    /** make empty strings be null.
318     *  Regexp returns empty strings for empty groups
319     */
320    private static String
321    makeEmptyNull (String s) {
322        if (s != null && s.length() == 0) return null;
323
324        return s;
325    }
326
327    static boolean isScMatchesSuppServType(String dialString) {
328        boolean isMatch = false;
329        Matcher m = sPatternSuppService.matcher(dialString);
330        if (m.matches()) {
331            String sc = makeEmptyNull(m.group(MATCH_GROUP_SERVICE_CODE));
332            if (sc.equals(SC_CFUT)) {
333                isMatch = true;
334            } else if(sc.equals(SC_BS_MT)) {
335                isMatch = true;
336            }
337        }
338        return isMatch;
339    }
340
341    /** returns true of the string is empty or null */
342    private static boolean
343    isEmptyOrNull(CharSequence s) {
344        return s == null || (s.length() == 0);
345    }
346
347    private static int
348    scToCallForwardReason(String sc) {
349        if (sc == null) {
350            throw new RuntimeException ("invalid call forward sc");
351        }
352
353        if (sc.equals(SC_CF_All)) {
354           return CommandsInterface.CF_REASON_ALL;
355        } else if (sc.equals(SC_CFU)) {
356            return CommandsInterface.CF_REASON_UNCONDITIONAL;
357        } else if (sc.equals(SC_CFB)) {
358            return CommandsInterface.CF_REASON_BUSY;
359        } else if (sc.equals(SC_CFNR)) {
360            return CommandsInterface.CF_REASON_NOT_REACHABLE;
361        } else if (sc.equals(SC_CFNRy)) {
362            return CommandsInterface.CF_REASON_NO_REPLY;
363        } else if (sc.equals(SC_CF_All_Conditional)) {
364           return CommandsInterface.CF_REASON_ALL_CONDITIONAL;
365        } else {
366            throw new RuntimeException ("invalid call forward sc");
367        }
368    }
369
370    private static int
371    siToServiceClass(String si) {
372        if (si == null || si.length() == 0) {
373                return  SERVICE_CLASS_NONE;
374        } else {
375            // NumberFormatException should cause MMI fail
376            int serviceCode = Integer.parseInt(si, 10);
377
378            switch (serviceCode) {
379                case 10: return SERVICE_CLASS_SMS + SERVICE_CLASS_FAX  + SERVICE_CLASS_VOICE;
380                case 11: return SERVICE_CLASS_VOICE;
381                case 12: return SERVICE_CLASS_SMS + SERVICE_CLASS_FAX;
382                case 13: return SERVICE_CLASS_FAX;
383
384                case 16: return SERVICE_CLASS_SMS;
385
386                case 19: return SERVICE_CLASS_FAX + SERVICE_CLASS_VOICE;
387
388                case 20: return SERVICE_CLASS_DATA_ASYNC + SERVICE_CLASS_DATA_SYNC;
389
390                case 21: return SERVICE_CLASS_PAD + SERVICE_CLASS_DATA_ASYNC;
391                case 22: return SERVICE_CLASS_PACKET + SERVICE_CLASS_DATA_SYNC;
392                case 24: return SERVICE_CLASS_DATA_SYNC;
393                case 25: return SERVICE_CLASS_DATA_ASYNC;
394                case 26: return SERVICE_CLASS_DATA_SYNC + SERVICE_CLASS_VOICE;
395                case 99: return SERVICE_CLASS_PACKET;
396
397                default:
398                    throw new RuntimeException("unsupported MMI service code " + si);
399            }
400        }
401    }
402
403    private static int
404    siToTime (String si) {
405        if (si == null || si.length() == 0) {
406            return 0;
407        } else {
408            // NumberFormatException should cause MMI fail
409            return Integer.parseInt(si, 10);
410        }
411    }
412
413    static boolean
414    isServiceCodeCallForwarding(String sc) {
415        return sc != null &&
416                (sc.equals(SC_CFU)
417                || sc.equals(SC_CFB) || sc.equals(SC_CFNRy)
418                || sc.equals(SC_CFNR) || sc.equals(SC_CF_All)
419                || sc.equals(SC_CF_All_Conditional));
420    }
421
422    static boolean
423    isServiceCodeCallBarring(String sc) {
424        Resources resource = Resources.getSystem();
425        if (sc != null) {
426            String[] barringMMI = resource.getStringArray(
427                com.android.internal.R.array.config_callBarringMMI);
428            if (barringMMI != null) {
429                for (String match : barringMMI) {
430                    if (sc.equals(match)) return true;
431                }
432            }
433        }
434        return false;
435    }
436
437    static String
438    scToBarringFacility(String sc) {
439        if (sc == null) {
440            throw new RuntimeException ("invalid call barring sc");
441        }
442
443        if (sc.equals(SC_BAOC)) {
444            return CommandsInterface.CB_FACILITY_BAOC;
445        } else if (sc.equals(SC_BAOIC)) {
446            return CommandsInterface.CB_FACILITY_BAOIC;
447        } else if (sc.equals(SC_BAOICxH)) {
448            return CommandsInterface.CB_FACILITY_BAOICxH;
449        } else if (sc.equals(SC_BAIC)) {
450            return CommandsInterface.CB_FACILITY_BAIC;
451        } else if (sc.equals(SC_BAICr)) {
452            return CommandsInterface.CB_FACILITY_BAICr;
453        } else if (sc.equals(SC_BA_ALL)) {
454            return CommandsInterface.CB_FACILITY_BA_ALL;
455        } else if (sc.equals(SC_BA_MO)) {
456            return CommandsInterface.CB_FACILITY_BA_MO;
457        } else if (sc.equals(SC_BA_MT)) {
458            return CommandsInterface.CB_FACILITY_BA_MT;
459        } else {
460            throw new RuntimeException ("invalid call barring sc");
461        }
462    }
463
464    //***** Constructor
465
466    ImsPhoneMmiCode(ImsPhone phone) {
467        // The telephony unit-test cases may create ImsPhoneMmiCode's
468        // in secondary threads
469        super(phone.getHandler().getLooper());
470        mPhone = phone;
471        mContext = phone.getContext();
472        mIccRecords = mPhone.mDefaultPhone.getIccRecords();
473    }
474
475    //***** MmiCode implementation
476
477    @Override
478    public State
479    getState() {
480        return mState;
481    }
482
483    @Override
484    public CharSequence
485    getMessage() {
486        return mMessage;
487    }
488
489    @Override
490    public Phone getPhone() { return mPhone; }
491
492    // inherited javadoc suffices
493    @Override
494    public void
495    cancel() {
496        // Complete or failed cannot be cancelled
497        if (mState == State.COMPLETE || mState == State.FAILED) {
498            return;
499        }
500
501        mState = State.CANCELLED;
502
503        if (mIsPendingUSSD) {
504            mPhone.cancelUSSD();
505        } else {
506            mPhone.onMMIDone (this);
507        }
508
509    }
510
511    @Override
512    public boolean isCancelable() {
513        /* Can only cancel pending USSD sessions. */
514        return mIsPendingUSSD;
515    }
516
517    //***** Instance Methods
518
519    String getDialingNumber() {
520        return mDialingNumber;
521    }
522
523    /** Does this dial string contain a structured or unstructured MMI code? */
524    boolean
525    isMMI() {
526        return mPoundString != null;
527    }
528
529    /* Is this a 1 or 2 digit "short code" as defined in TS 22.030 sec 6.5.3.2? */
530    boolean
531    isShortCode() {
532        return mPoundString == null
533                    && mDialingNumber != null && mDialingNumber.length() <= 2;
534
535    }
536
537    static private boolean
538    isTwoDigitShortCode(Context context, String dialString) {
539        Rlog.d(LOG_TAG, "isTwoDigitShortCode");
540
541        if (dialString == null || dialString.length() > 2) return false;
542
543        if (sTwoDigitNumberPattern == null) {
544            sTwoDigitNumberPattern = context.getResources().getStringArray(
545                    com.android.internal.R.array.config_twoDigitNumberPattern);
546        }
547
548        for (String dialnumber : sTwoDigitNumberPattern) {
549            Rlog.d(LOG_TAG, "Two Digit Number Pattern " + dialnumber);
550            if (dialString.equals(dialnumber)) {
551                Rlog.d(LOG_TAG, "Two Digit Number Pattern -true");
552                return true;
553            }
554        }
555        Rlog.d(LOG_TAG, "Two Digit Number Pattern -false");
556        return false;
557    }
558
559    /**
560     * Helper function for newFromDialString. Returns true if dialString appears
561     * to be a short code AND conditions are correct for it to be treated as
562     * such.
563     */
564    static private boolean isShortCode(String dialString, ImsPhone phone) {
565        // Refer to TS 22.030 Figure 3.5.3.2:
566        if (dialString == null) {
567            return false;
568        }
569
570        // Illegal dial string characters will give a ZERO length.
571        // At this point we do not want to crash as any application with
572        // call privileges may send a non dial string.
573        // It return false as when the dialString is equal to NULL.
574        if (dialString.length() == 0) {
575            return false;
576        }
577
578        if (PhoneNumberUtils.isLocalEmergencyNumber(phone.getContext(), dialString)) {
579            return false;
580        } else {
581            return isShortCodeUSSD(dialString, phone);
582        }
583    }
584
585    /**
586     * Helper function for isShortCode. Returns true if dialString appears to be
587     * a short code and it is a USSD structure
588     *
589     * According to the 3PGG TS 22.030 specification Figure 3.5.3.2: A 1 or 2
590     * digit "short code" is treated as USSD if it is entered while on a call or
591     * does not satisfy the condition (exactly 2 digits && starts with '1'), there
592     * are however exceptions to this rule (see below)
593     *
594     * Exception (1) to Call initiation is: If the user of the device is already in a call
595     * and enters a Short String without any #-key at the end and the length of the Short String is
596     * equal or less then the MAX_LENGTH_SHORT_CODE [constant that is equal to 2]
597     *
598     * The phone shall initiate a USSD/SS commands.
599     */
600    static private boolean isShortCodeUSSD(String dialString, ImsPhone phone) {
601        if (dialString != null && dialString.length() <= MAX_LENGTH_SHORT_CODE) {
602            if (phone.isInCall()) {
603                return true;
604            }
605
606            if (dialString.length() != MAX_LENGTH_SHORT_CODE ||
607                    dialString.charAt(0) != '1') {
608                return true;
609            }
610        }
611        return false;
612    }
613
614    /**
615     * @return true if the Service Code is PIN/PIN2/PUK/PUK2-related
616     */
617    public boolean isPinPukCommand() {
618        return mSc != null && (mSc.equals(SC_PIN) || mSc.equals(SC_PIN2)
619                              || mSc.equals(SC_PUK) || mSc.equals(SC_PUK2));
620    }
621
622    /**
623     * See TS 22.030 Annex B.
624     * In temporary mode, to suppress CLIR for a single call, enter:
625     *      " * 31 # [called number] SEND "
626     *  In temporary mode, to invoke CLIR for a single call enter:
627     *       " # 31 # [called number] SEND "
628     */
629    boolean
630    isTemporaryModeCLIR() {
631        return mSc != null && mSc.equals(SC_CLIR) && mDialingNumber != null
632                && (isActivate() || isDeactivate());
633    }
634
635    /**
636     * returns CommandsInterface.CLIR_*
637     * See also isTemporaryModeCLIR()
638     */
639    int
640    getCLIRMode() {
641        if (mSc != null && mSc.equals(SC_CLIR)) {
642            if (isActivate()) {
643                return CommandsInterface.CLIR_SUPPRESSION;
644            } else if (isDeactivate()) {
645                return CommandsInterface.CLIR_INVOCATION;
646            }
647        }
648
649        return CommandsInterface.CLIR_DEFAULT;
650    }
651
652    boolean isActivate() {
653        return mAction != null && mAction.equals(ACTION_ACTIVATE);
654    }
655
656    boolean isDeactivate() {
657        return mAction != null && mAction.equals(ACTION_DEACTIVATE);
658    }
659
660    boolean isInterrogate() {
661        return mAction != null && mAction.equals(ACTION_INTERROGATE);
662    }
663
664    boolean isRegister() {
665        return mAction != null && mAction.equals(ACTION_REGISTER);
666    }
667
668    boolean isErasure() {
669        return mAction != null && mAction.equals(ACTION_ERASURE);
670    }
671
672    /**
673     * Returns true if this is a USSD code that's been submitted to the
674     * network...eg, after processCode() is called
675     */
676    public boolean isPendingUSSD() {
677        return mIsPendingUSSD;
678    }
679
680    @Override
681    public boolean isUssdRequest() {
682        return mIsUssdRequest;
683    }
684
685    boolean
686    isSupportedOverImsPhone() {
687        if (isShortCode()) return true;
688        else if (mDialingNumber != null) return false;
689        else if (isServiceCodeCallForwarding(mSc)
690                || isServiceCodeCallBarring(mSc)
691                || (mSc != null && mSc.equals(SC_WAIT))
692                || (mSc != null && mSc.equals(SC_CLIR))
693                || (mSc != null && mSc.equals(SC_CLIP))
694                || (mSc != null && mSc.equals(SC_COLR))
695                || (mSc != null && mSc.equals(SC_COLP))
696                || (mSc != null && mSc.equals(SC_BS_MT))
697                || (mSc != null && mSc.equals(SC_BAICa))) {
698
699            try {
700                int serviceClass = siToServiceClass(mSib);
701                if (serviceClass != SERVICE_CLASS_NONE
702                        && serviceClass != SERVICE_CLASS_VOICE) {
703                    return false;
704                }
705                return true;
706            } catch (RuntimeException exc) {
707                Rlog.d(LOG_TAG, "Invalid service class " + exc);
708            }
709        } else if (isPinPukCommand()
710                || (mSc != null
711                    && (mSc.equals(SC_PWD) || mSc.equals(SC_CLIP) || mSc.equals(SC_CLIR)))) {
712            return false;
713        } else if (mPoundString != null) return true;
714
715        return false;
716    }
717
718    /*
719     * The below actions are IMS/Volte CallBarring actions.We have not defined
720     * these actions in ImscommandInterface.However we have reused existing
721     * actions of CallForwarding as, both CF and CB actions are used for same
722     * purpose.
723     */
724    public int callBarAction(String dialingNumber) {
725        if (isActivate()) {
726            return CommandsInterface.CF_ACTION_ENABLE;
727        } else if (isDeactivate()) {
728            return CommandsInterface.CF_ACTION_DISABLE;
729        } else if (isRegister()) {
730            if (!isEmptyOrNull(dialingNumber)) {
731                return CommandsInterface.CF_ACTION_REGISTRATION;
732            } else {
733                throw new RuntimeException ("invalid action");
734            }
735        } else if (isErasure()) {
736            return CommandsInterface.CF_ACTION_ERASURE;
737        } else {
738            throw new RuntimeException ("invalid action");
739        }
740    }
741
742    /** Process a MMI code or short code...anything that isn't a dialing number */
743    public void
744    processCode () throws CallStateException {
745        try {
746            if (isShortCode()) {
747                Rlog.d(LOG_TAG, "isShortCode");
748
749                // These just get treated as USSD.
750                Rlog.d(LOG_TAG, "Sending short code '"
751                       + mDialingNumber + "' over CS pipe.");
752                throw new CallStateException(Phone.CS_FALLBACK);
753            } else if (isServiceCodeCallForwarding(mSc)) {
754                Rlog.d(LOG_TAG, "is CF");
755
756                String dialingNumber = mSia;
757                int reason = scToCallForwardReason(mSc);
758                int serviceClass = siToServiceClass(mSib);
759                int time = siToTime(mSic);
760
761                if (isInterrogate()) {
762                    mPhone.getCallForwardingOption(reason,
763                            obtainMessage(EVENT_QUERY_CF_COMPLETE, this));
764                } else {
765                    int cfAction;
766
767                    if (isActivate()) {
768                        // 3GPP TS 22.030 6.5.2
769                        // a call forwarding request with a single * would be
770                        // interpreted as registration if containing a forwarded-to
771                        // number, or an activation if not
772                        if (isEmptyOrNull(dialingNumber)) {
773                            cfAction = CommandsInterface.CF_ACTION_ENABLE;
774                            mIsCallFwdReg = false;
775                        } else {
776                            cfAction = CommandsInterface.CF_ACTION_REGISTRATION;
777                            mIsCallFwdReg = true;
778                        }
779                    } else if (isDeactivate()) {
780                        cfAction = CommandsInterface.CF_ACTION_DISABLE;
781                    } else if (isRegister()) {
782                        cfAction = CommandsInterface.CF_ACTION_REGISTRATION;
783                    } else if (isErasure()) {
784                        cfAction = CommandsInterface.CF_ACTION_ERASURE;
785                    } else {
786                        throw new RuntimeException ("invalid action");
787                    }
788
789                    int isSettingUnconditional =
790                            ((reason == CommandsInterface.CF_REASON_UNCONDITIONAL) ||
791                             (reason == CommandsInterface.CF_REASON_ALL)) ? 1 : 0;
792
793                    int isEnableDesired =
794                        ((cfAction == CommandsInterface.CF_ACTION_ENABLE) ||
795                                (cfAction == CommandsInterface.CF_ACTION_REGISTRATION)) ? 1 : 0;
796
797                    Rlog.d(LOG_TAG, "is CF setCallForward");
798                    mPhone.setCallForwardingOption(cfAction, reason,
799                            dialingNumber, serviceClass, time, obtainMessage(
800                                    EVENT_SET_CFF_COMPLETE,
801                                    isSettingUnconditional,
802                                    isEnableDesired, this));
803                }
804            } else if (isServiceCodeCallBarring(mSc)) {
805                // sia = password
806                // sib = basic service group
807                // service group is not supported
808
809                String password = mSia;
810                String facility = scToBarringFacility(mSc);
811
812                if (isInterrogate()) {
813                    mPhone.getCallBarring(facility,
814                            obtainMessage(EVENT_SUPP_SVC_QUERY_COMPLETE, this));
815                } else if (isActivate() || isDeactivate()) {
816                    mPhone.setCallBarring(facility, isActivate(), password,
817                            obtainMessage(EVENT_SET_COMPLETE, this));
818                } else {
819                    throw new RuntimeException ("Invalid or Unsupported MMI Code");
820                }
821            } else if (mSc != null && mSc.equals(SC_CLIR)) {
822                // NOTE: Since these supplementary services are accessed only
823                //       via MMI codes, methods have not been added to ImsPhone.
824                //       Only the UT interface handle is used.
825                if (isActivate()) {
826                    try {
827                        mPhone.mCT.getUtInterface().updateCLIR(CommandsInterface.CLIR_INVOCATION,
828                            obtainMessage(EVENT_SET_COMPLETE, this));
829                    } catch (ImsException e) {
830                        Rlog.d(LOG_TAG, "Could not get UT handle for updateCLIR.");
831                    }
832                } else if (isDeactivate()) {
833                    try {
834                        mPhone.mCT.getUtInterface().updateCLIR(CommandsInterface.CLIR_SUPPRESSION,
835                            obtainMessage(EVENT_SET_COMPLETE, this));
836                    } catch (ImsException e) {
837                        Rlog.d(LOG_TAG, "Could not get UT handle for updateCLIR.");
838                    }
839                } else if (isInterrogate()) {
840                    try {
841                        mPhone.mCT.getUtInterface()
842                            .queryCLIR(obtainMessage(EVENT_GET_CLIR_COMPLETE, this));
843                    } catch (ImsException e) {
844                        Rlog.d(LOG_TAG, "Could not get UT handle for queryCLIR.");
845                    }
846                } else {
847                    throw new RuntimeException ("Invalid or Unsupported MMI Code");
848                }
849            } else if (mSc != null && mSc.equals(SC_CLIP)) {
850                // NOTE: Refer to the note above.
851                if (isInterrogate()) {
852                    try {
853                        mPhone.mCT.getUtInterface()
854                            .queryCLIP(obtainMessage(EVENT_SUPP_SVC_QUERY_COMPLETE, this));
855                    } catch (ImsException e) {
856                        Rlog.d(LOG_TAG, "Could not get UT handle for queryCLIP.");
857                    }
858                } else if (isActivate() || isDeactivate()) {
859                    try {
860                        mPhone.mCT.getUtInterface().updateCLIP(isActivate(),
861                                obtainMessage(EVENT_SET_COMPLETE, this));
862                    } catch (ImsException e) {
863                        Rlog.d(LOG_TAG, "Could not get UT handle for updateCLIP.");
864                    }
865                } else {
866                    throw new RuntimeException ("Invalid or Unsupported MMI Code");
867                }
868            } else if (mSc != null && mSc.equals(SC_COLP)) {
869                // NOTE: Refer to the note above.
870                if (isInterrogate()) {
871                    try {
872                        mPhone.mCT.getUtInterface()
873                            .queryCOLP(obtainMessage(EVENT_SUPP_SVC_QUERY_COMPLETE, this));
874                    } catch (ImsException e) {
875                        Rlog.d(LOG_TAG, "Could not get UT handle for queryCOLP.");
876                    }
877                } else if (isActivate() || isDeactivate()) {
878                    try {
879                        mPhone.mCT.getUtInterface().updateCOLP(isActivate(),
880                                 obtainMessage(EVENT_SET_COMPLETE, this));
881                     } catch (ImsException e) {
882                         Rlog.d(LOG_TAG, "Could not get UT handle for updateCOLP.");
883                     }
884                } else {
885                    throw new RuntimeException ("Invalid or Unsupported MMI Code");
886                }
887            } else if (mSc != null && mSc.equals(SC_COLR)) {
888                // NOTE: Refer to the note above.
889                if (isActivate()) {
890                    try {
891                        mPhone.mCT.getUtInterface().updateCOLR(NUM_PRESENTATION_RESTRICTED,
892                                obtainMessage(EVENT_SET_COMPLETE, this));
893                    } catch (ImsException e) {
894                        Rlog.d(LOG_TAG, "Could not get UT handle for updateCOLR.");
895                    }
896                } else if (isDeactivate()) {
897                    try {
898                        mPhone.mCT.getUtInterface().updateCOLR(NUM_PRESENTATION_ALLOWED,
899                                obtainMessage(EVENT_SET_COMPLETE, this));
900                    } catch (ImsException e) {
901                        Rlog.d(LOG_TAG, "Could not get UT handle for updateCOLR.");
902                    }
903                } else if (isInterrogate()) {
904                    try {
905                        mPhone.mCT.getUtInterface()
906                            .queryCOLR(obtainMessage(EVENT_SUPP_SVC_QUERY_COMPLETE, this));
907                    } catch (ImsException e) {
908                        Rlog.d(LOG_TAG, "Could not get UT handle for queryCOLR.");
909                    }
910                } else {
911                    throw new RuntimeException ("Invalid or Unsupported MMI Code");
912                }
913            } else if (mSc != null && (mSc.equals(SC_BS_MT))) {
914                try {
915                    if (isInterrogate()) {
916                        mPhone.mCT.getUtInterface()
917                        .queryCallBarring(ImsUtInterface.CB_BS_MT,
918                                          obtainMessage(EVENT_QUERY_ICB_COMPLETE,this));
919                    } else {
920                        processIcbMmiCodeForUpdate();
921                    }
922                 // TODO: isRegister() case needs to be handled.
923                } catch (ImsException e) {
924                    Rlog.d(LOG_TAG, "Could not get UT handle for ICB.");
925                }
926            } else if (mSc != null && mSc.equals(SC_BAICa)) {
927                int callAction =0;
928                // TODO: Should we route through queryCallBarring() here?
929                try {
930                    if (isInterrogate()) {
931                        mPhone.mCT.getUtInterface()
932                        .queryCallBarring(ImsUtInterface.CB_BIC_ACR,
933                                          obtainMessage(EVENT_QUERY_ICB_COMPLETE,this));
934                    } else {
935                        if (isActivate()) {
936                            callAction = CommandsInterface.CF_ACTION_ENABLE;
937                        } else if (isDeactivate()) {
938                            callAction = CommandsInterface.CF_ACTION_DISABLE;
939                        }
940                        mPhone.mCT.getUtInterface()
941                                .updateCallBarring(ImsUtInterface.CB_BIC_ACR,
942                                callAction,
943                                obtainMessage(EVENT_SET_COMPLETE,this),
944                                null);
945                    }
946                } catch (ImsException e) {
947                    Rlog.d(LOG_TAG, "Could not get UT handle for ICBa.");
948                }
949            } else if (mSc != null && mSc.equals(SC_WAIT)) {
950                // sia = basic service group
951                int serviceClass = siToServiceClass(mSib);
952
953                if (isActivate() || isDeactivate()) {
954                    mPhone.setCallWaiting(isActivate(), serviceClass,
955                            obtainMessage(EVENT_SET_COMPLETE, this));
956                } else if (isInterrogate()) {
957                    mPhone.getCallWaiting(obtainMessage(EVENT_QUERY_COMPLETE, this));
958                } else {
959                    throw new RuntimeException ("Invalid or Unsupported MMI Code");
960                }
961            } else if (mPoundString != null) {
962                Rlog.d(LOG_TAG, "Sending pound string '"
963                       + mDialingNumber + "' over CS pipe.");
964                throw new CallStateException(Phone.CS_FALLBACK);
965            } else {
966                throw new RuntimeException ("Invalid or Unsupported MMI Code");
967            }
968        } catch (RuntimeException exc) {
969            mState = State.FAILED;
970            mMessage = mContext.getText(com.android.internal.R.string.mmiError);
971            mPhone.onMMIDone(this);
972        }
973    }
974
975    /**
976     * Called from ImsPhone
977     *
978     * An unsolicited USSD NOTIFY or REQUEST has come in matching
979     * up with this pending USSD request
980     *
981     * Note: If REQUEST, this exchange is complete, but the session remains
982     *       active (ie, the network expects user input).
983     */
984    void
985    onUssdFinished(String ussdMessage, boolean isUssdRequest) {
986        if (mState == State.PENDING) {
987            if (ussdMessage == null) {
988                mMessage = mContext.getText(com.android.internal.R.string.mmiComplete);
989            } else {
990                mMessage = ussdMessage;
991            }
992            mIsUssdRequest = isUssdRequest;
993            // If it's a request, leave it PENDING so that it's cancelable.
994            if (!isUssdRequest) {
995                mState = State.COMPLETE;
996            }
997
998            mPhone.onMMIDone(this);
999        }
1000    }
1001
1002    /**
1003     * Called from ImsPhone
1004     *
1005     * The radio has reset, and this is still pending
1006     */
1007
1008    void
1009    onUssdFinishedError() {
1010        if (mState == State.PENDING) {
1011            mState = State.FAILED;
1012            mMessage = mContext.getText(com.android.internal.R.string.mmiError);
1013
1014            mPhone.onMMIDone(this);
1015        }
1016    }
1017
1018    void sendUssd(String ussdMessage) {
1019        // Treat this as a USSD string
1020        mIsPendingUSSD = true;
1021
1022        // Note that unlike most everything else, the USSD complete
1023        // response does not complete this MMI code...we wait for
1024        // an unsolicited USSD "Notify" or "Request".
1025        // The matching up of this is done in ImsPhone.
1026
1027        mPhone.sendUSSD(ussdMessage,
1028            obtainMessage(EVENT_USSD_COMPLETE, this));
1029    }
1030
1031    /** Called from ImsPhone.handleMessage; not a Handler subclass */
1032    @Override
1033    public void
1034    handleMessage (Message msg) {
1035        AsyncResult ar;
1036
1037        switch (msg.what) {
1038            case EVENT_SET_COMPLETE:
1039                ar = (AsyncResult) (msg.obj);
1040
1041                onSetComplete(msg, ar);
1042                break;
1043
1044            case EVENT_SET_CFF_COMPLETE:
1045                ar = (AsyncResult) (msg.obj);
1046
1047                /*
1048                * msg.arg1 = 1 means to set unconditional voice call forwarding
1049                * msg.arg2 = 1 means to enable voice call forwarding
1050                */
1051                if ((ar.exception == null) && (msg.arg1 == 1)) {
1052                    boolean cffEnabled = (msg.arg2 == 1);
1053                    if (mIccRecords != null) {
1054                        mPhone.setVoiceCallForwardingFlag(1, cffEnabled, mDialingNumber);
1055                    }
1056                }
1057
1058                onSetComplete(msg, ar);
1059                break;
1060
1061            case EVENT_QUERY_CF_COMPLETE:
1062                ar = (AsyncResult) (msg.obj);
1063                onQueryCfComplete(ar);
1064                break;
1065
1066            case EVENT_QUERY_COMPLETE:
1067                ar = (AsyncResult) (msg.obj);
1068                onQueryComplete(ar);
1069                break;
1070
1071            case EVENT_USSD_COMPLETE:
1072                ar = (AsyncResult) (msg.obj);
1073
1074                if (ar.exception != null) {
1075                    mState = State.FAILED;
1076                    mMessage = getErrorMessage(ar);
1077
1078                    mPhone.onMMIDone(this);
1079                }
1080
1081                // Note that unlike most everything else, the USSD complete
1082                // response does not complete this MMI code...we wait for
1083                // an unsolicited USSD "Notify" or "Request".
1084                // The matching up of this is done in ImsPhone.
1085
1086                break;
1087
1088            case EVENT_USSD_CANCEL_COMPLETE:
1089                mPhone.onMMIDone(this);
1090                break;
1091
1092            case EVENT_SUPP_SVC_QUERY_COMPLETE:
1093                ar = (AsyncResult) (msg.obj);
1094                onSuppSvcQueryComplete(ar);
1095                break;
1096
1097            case EVENT_QUERY_ICB_COMPLETE:
1098                ar = (AsyncResult) (msg.obj);
1099                onIcbQueryComplete(ar);
1100                break;
1101
1102            case EVENT_GET_CLIR_COMPLETE:
1103                ar = (AsyncResult) (msg.obj);
1104                onQueryClirComplete(ar);
1105                break;
1106
1107            default:
1108                break;
1109        }
1110    }
1111
1112    //***** Private instance methods
1113
1114    private void
1115    processIcbMmiCodeForUpdate () {
1116        String dialingNumber = mSia;
1117        String[] icbNum = null;
1118        int callAction;
1119        if (dialingNumber != null) {
1120            icbNum = dialingNumber.split("\\$");
1121        }
1122        callAction = callBarAction(dialingNumber);
1123
1124        try {
1125            mPhone.mCT.getUtInterface()
1126            .updateCallBarring(ImsUtInterface.CB_BS_MT,
1127                               callAction,
1128                               obtainMessage(EVENT_SET_COMPLETE,this),
1129                               icbNum);
1130        } catch (ImsException e) {
1131            Rlog.d(LOG_TAG, "Could not get UT handle for updating ICB.");
1132        }
1133    }
1134
1135    private CharSequence getErrorMessage(AsyncResult ar) {
1136        return mContext.getText(com.android.internal.R.string.mmiError);
1137    }
1138
1139    private CharSequence getScString() {
1140        if (mSc != null) {
1141            if (isServiceCodeCallBarring(mSc)) {
1142                return mContext.getText(com.android.internal.R.string.BaMmi);
1143            } else if (isServiceCodeCallForwarding(mSc)) {
1144                return mContext.getText(com.android.internal.R.string.CfMmi);
1145            } else if (mSc.equals(SC_PWD)) {
1146                return mContext.getText(com.android.internal.R.string.PwdMmi);
1147            } else if (mSc.equals(SC_WAIT)) {
1148                return mContext.getText(com.android.internal.R.string.CwMmi);
1149            } else if (mSc.equals(SC_CLIP)) {
1150                return mContext.getText(com.android.internal.R.string.ClipMmi);
1151            } else if (mSc.equals(SC_CLIR)) {
1152                return mContext.getText(com.android.internal.R.string.ClirMmi);
1153            } else if (mSc.equals(SC_COLP)) {
1154                return mContext.getText(com.android.internal.R.string.ColpMmi);
1155            } else if (mSc.equals(SC_COLR)) {
1156                return mContext.getText(com.android.internal.R.string.ColrMmi);
1157            } else if (mSc.equals(SC_BS_MT)) {
1158                return IcbDnMmi;
1159            } else if (mSc.equals(SC_BAICa)) {
1160                return IcbAnonymousMmi;
1161            }
1162        }
1163
1164        return "";
1165    }
1166
1167    private void
1168    onSetComplete(Message msg, AsyncResult ar){
1169        StringBuilder sb = new StringBuilder(getScString());
1170        sb.append("\n");
1171
1172        if (ar.exception != null) {
1173            mState = State.FAILED;
1174
1175            if (ar.exception instanceof CommandException) {
1176                CommandException err = (CommandException) ar.exception;
1177                if (err.getCommandError() == CommandException.Error.PASSWORD_INCORRECT) {
1178                    sb.append(mContext.getText(
1179                            com.android.internal.R.string.passwordIncorrect));
1180                } else if (err.getMessage() != null) {
1181                    sb.append(err.getMessage());
1182                } else {
1183                    sb.append(mContext.getText(com.android.internal.R.string.mmiError));
1184                }
1185            } else {
1186                ImsException error = (ImsException) ar.exception;
1187                if (error.getMessage() != null) {
1188                    sb.append(error.getMessage());
1189                } else {
1190                    sb.append(getErrorMessage(ar));
1191                }
1192            }
1193        } else if (isActivate()) {
1194            mState = State.COMPLETE;
1195            if (mIsCallFwdReg) {
1196                sb.append(mContext.getText(
1197                        com.android.internal.R.string.serviceRegistered));
1198            } else {
1199                sb.append(mContext.getText(
1200                        com.android.internal.R.string.serviceEnabled));
1201            }
1202        } else if (isDeactivate()) {
1203            mState = State.COMPLETE;
1204            sb.append(mContext.getText(
1205                    com.android.internal.R.string.serviceDisabled));
1206        } else if (isRegister()) {
1207            mState = State.COMPLETE;
1208            sb.append(mContext.getText(
1209                    com.android.internal.R.string.serviceRegistered));
1210        } else if (isErasure()) {
1211            mState = State.COMPLETE;
1212            sb.append(mContext.getText(
1213                    com.android.internal.R.string.serviceErased));
1214        } else {
1215            mState = State.FAILED;
1216            sb.append(mContext.getText(
1217                    com.android.internal.R.string.mmiError));
1218        }
1219
1220        mMessage = sb;
1221        mPhone.onMMIDone(this);
1222    }
1223
1224    /**
1225     * @param serviceClass 1 bit of the service class bit vectory
1226     * @return String to be used for call forward query MMI response text.
1227     *        Returns null if unrecognized
1228     */
1229
1230    private CharSequence
1231    serviceClassToCFString (int serviceClass) {
1232        switch (serviceClass) {
1233            case SERVICE_CLASS_VOICE:
1234                return mContext.getText(com.android.internal.R.string.serviceClassVoice);
1235            case SERVICE_CLASS_DATA:
1236                return mContext.getText(com.android.internal.R.string.serviceClassData);
1237            case SERVICE_CLASS_FAX:
1238                return mContext.getText(com.android.internal.R.string.serviceClassFAX);
1239            case SERVICE_CLASS_SMS:
1240                return mContext.getText(com.android.internal.R.string.serviceClassSMS);
1241            case SERVICE_CLASS_DATA_SYNC:
1242                return mContext.getText(com.android.internal.R.string.serviceClassDataSync);
1243            case SERVICE_CLASS_DATA_ASYNC:
1244                return mContext.getText(com.android.internal.R.string.serviceClassDataAsync);
1245            case SERVICE_CLASS_PACKET:
1246                return mContext.getText(com.android.internal.R.string.serviceClassPacket);
1247            case SERVICE_CLASS_PAD:
1248                return mContext.getText(com.android.internal.R.string.serviceClassPAD);
1249            default:
1250                return null;
1251        }
1252    }
1253
1254    /** one CallForwardInfo + serviceClassMask -> one line of text */
1255    private CharSequence
1256    makeCFQueryResultMessage(CallForwardInfo info, int serviceClassMask) {
1257        CharSequence template;
1258        String sources[] = {"{0}", "{1}", "{2}"};
1259        CharSequence destinations[] = new CharSequence[3];
1260        boolean needTimeTemplate;
1261
1262        // CF_REASON_NO_REPLY also has a time value associated with
1263        // it. All others don't.
1264
1265        needTimeTemplate =
1266            (info.reason == CommandsInterface.CF_REASON_NO_REPLY);
1267
1268        if (info.status == 1) {
1269            if (needTimeTemplate) {
1270                template = mContext.getText(
1271                        com.android.internal.R.string.cfTemplateForwardedTime);
1272            } else {
1273                template = mContext.getText(
1274                        com.android.internal.R.string.cfTemplateForwarded);
1275            }
1276        } else if (info.status == 0 && isEmptyOrNull(info.number)) {
1277            template = mContext.getText(
1278                        com.android.internal.R.string.cfTemplateNotForwarded);
1279        } else { /* (info.status == 0) && !isEmptyOrNull(info.number) */
1280            // A call forward record that is not active but contains
1281            // a phone number is considered "registered"
1282
1283            if (needTimeTemplate) {
1284                template = mContext.getText(
1285                        com.android.internal.R.string.cfTemplateRegisteredTime);
1286            } else {
1287                template = mContext.getText(
1288                        com.android.internal.R.string.cfTemplateRegistered);
1289            }
1290        }
1291
1292        // In the template (from strings.xmls)
1293        //         {0} is one of "bearerServiceCode*"
1294        //        {1} is dialing number
1295        //      {2} is time in seconds
1296
1297        destinations[0] = serviceClassToCFString(info.serviceClass & serviceClassMask);
1298        destinations[1] = PhoneNumberUtils.stringFromStringAndTOA(info.number, info.toa);
1299        destinations[2] = Integer.toString(info.timeSeconds);
1300
1301        if (info.reason == CommandsInterface.CF_REASON_UNCONDITIONAL &&
1302                (info.serviceClass & serviceClassMask)
1303                        == CommandsInterface.SERVICE_CLASS_VOICE) {
1304            boolean cffEnabled = (info.status == 1);
1305            if (mIccRecords != null) {
1306                mPhone.setVoiceCallForwardingFlag(1, cffEnabled, info.number);
1307            }
1308        }
1309
1310        return TextUtils.replace(template, sources, destinations);
1311    }
1312
1313
1314    private void
1315    onQueryCfComplete(AsyncResult ar) {
1316        StringBuilder sb = new StringBuilder(getScString());
1317        sb.append("\n");
1318
1319        if (ar.exception != null) {
1320            mState = State.FAILED;
1321
1322            if (ar.exception instanceof ImsException) {
1323                ImsException error = (ImsException) ar.exception;
1324                if (error.getMessage() != null) {
1325                    sb.append(error.getMessage());
1326                } else {
1327                    sb.append(getErrorMessage(ar));
1328                }
1329            }
1330            else {
1331                sb.append(getErrorMessage(ar));
1332            }
1333        } else {
1334            CallForwardInfo infos[];
1335
1336            infos = (CallForwardInfo[]) ar.result;
1337
1338            if (infos.length == 0) {
1339                // Assume the default is not active
1340                sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled));
1341
1342                // Set unconditional CFF in SIM to false
1343                if (mIccRecords != null) {
1344                    mPhone.setVoiceCallForwardingFlag(1, false, null);
1345                }
1346            } else {
1347
1348                SpannableStringBuilder tb = new SpannableStringBuilder();
1349
1350                // Each bit in the service class gets its own result line
1351                // The service classes may be split up over multiple
1352                // CallForwardInfos. So, for each service class, find out
1353                // which CallForwardInfo represents it and then build
1354                // the response text based on that
1355
1356                for (int serviceClassMask = 1
1357                            ; serviceClassMask <= SERVICE_CLASS_MAX
1358                            ; serviceClassMask <<= 1
1359                ) {
1360                    for (int i = 0, s = infos.length; i < s ; i++) {
1361                        if ((serviceClassMask & infos[i].serviceClass) != 0) {
1362                            tb.append(makeCFQueryResultMessage(infos[i],
1363                                            serviceClassMask));
1364                            tb.append("\n");
1365                        }
1366                    }
1367                }
1368                sb.append(tb);
1369            }
1370
1371            mState = State.COMPLETE;
1372        }
1373
1374        mMessage = sb;
1375        mPhone.onMMIDone(this);
1376
1377    }
1378
1379    private void onSuppSvcQueryComplete(AsyncResult ar) {
1380        StringBuilder sb = new StringBuilder(getScString());
1381        sb.append("\n");
1382
1383        if (ar.exception != null) {
1384            mState = State.FAILED;
1385
1386            if (ar.exception instanceof ImsException) {
1387                ImsException error = (ImsException) ar.exception;
1388                if (error.getMessage() != null) {
1389                    sb.append(error.getMessage());
1390                } else {
1391                    sb.append(getErrorMessage(ar));
1392                }
1393            } else {
1394                sb.append(getErrorMessage(ar));
1395            }
1396        } else {
1397            mState = State.FAILED;
1398            ImsSsInfo ssInfo = null;
1399            if (ar.result instanceof Bundle) {
1400                Rlog.d(LOG_TAG, "Received CLIP/COLP/COLR Response.");
1401                // Response for CLIP, COLP and COLR queries.
1402                Bundle ssInfoResp = (Bundle) ar.result;
1403                ssInfo = (ImsSsInfo) ssInfoResp.getParcelable(UT_BUNDLE_KEY_SSINFO);
1404                if (ssInfo != null) {
1405                    Rlog.d(LOG_TAG, "ImsSsInfo mStatus = " + ssInfo.mStatus);
1406                    if (ssInfo.mStatus == ImsSsInfo.DISABLED) {
1407                        sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled));
1408                        mState = State.COMPLETE;
1409                    } else if (ssInfo.mStatus == ImsSsInfo.ENABLED) {
1410                        sb.append(mContext.getText(com.android.internal.R.string.serviceEnabled));
1411                        mState = State.COMPLETE;
1412                    } else {
1413                        sb.append(mContext.getText(com.android.internal.R.string.mmiError));
1414                    }
1415                } else {
1416                    sb.append(mContext.getText(com.android.internal.R.string.mmiError));
1417                }
1418
1419            } else {
1420                Rlog.d(LOG_TAG, "Received Call Barring Response.");
1421                // Response for Call Barring queries.
1422                int[] cbInfos = (int[]) ar.result;
1423                if (cbInfos[0] == 1) {
1424                    sb.append(mContext.getText(com.android.internal.R.string.serviceEnabled));
1425                    mState = State.COMPLETE;
1426                } else {
1427                    sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled));
1428                    mState = State.COMPLETE;
1429                }
1430            }
1431        }
1432
1433        mMessage = sb;
1434        mPhone.onMMIDone(this);
1435    }
1436
1437    private void onIcbQueryComplete(AsyncResult ar) {
1438        Rlog.d(LOG_TAG, "onIcbQueryComplete ");
1439        StringBuilder sb = new StringBuilder(getScString());
1440        sb.append("\n");
1441
1442        if (ar.exception != null) {
1443            mState = State.FAILED;
1444
1445            if (ar.exception instanceof ImsException) {
1446                ImsException error = (ImsException) ar.exception;
1447                if (error.getMessage() != null) {
1448                    sb.append(error.getMessage());
1449                } else {
1450                    sb.append(getErrorMessage(ar));
1451                }
1452            } else {
1453                sb.append(getErrorMessage(ar));
1454            }
1455        } else {
1456            ImsSsInfo[] infos = (ImsSsInfo[])ar.result;
1457            if (infos.length == 0) {
1458                sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled));
1459            } else {
1460                for (int i = 0, s = infos.length; i < s ; i++) {
1461                    if (infos[i].mIcbNum !=null) {
1462                        sb.append("Num: " + infos[i].mIcbNum + " status: "
1463                                + infos[i].mStatus + "\n");
1464                    } else if (infos[i].mStatus == 1) {
1465                        sb.append(mContext.getText(com.android.internal
1466                                .R.string.serviceEnabled));
1467                    } else {
1468                        sb.append(mContext.getText(com.android.internal
1469                                .R.string.serviceDisabled));
1470                    }
1471                }
1472            }
1473            mState = State.COMPLETE;
1474        }
1475        mMessage = sb;
1476        mPhone.onMMIDone(this);
1477    }
1478
1479    private void onQueryClirComplete(AsyncResult ar) {
1480        StringBuilder sb = new StringBuilder(getScString());
1481        sb.append("\n");
1482        mState = State.FAILED;
1483
1484        if (ar.exception != null) {
1485
1486            if (ar.exception instanceof ImsException) {
1487                ImsException error = (ImsException) ar.exception;
1488                if (error.getMessage() != null) {
1489                    sb.append(error.getMessage());
1490                } else {
1491                    sb.append(getErrorMessage(ar));
1492                }
1493            }
1494        } else {
1495            Bundle ssInfo = (Bundle) ar.result;
1496            int[] clirInfo = ssInfo.getIntArray(UT_BUNDLE_KEY_CLIR);
1497            // clirInfo[0] = The 'n' parameter from TS 27.007 7.7
1498            // clirInfo[1] = The 'm' parameter from TS 27.007 7.7
1499            Rlog.d(LOG_TAG, "CLIR param n=" + clirInfo[0]
1500                    + " m=" + clirInfo[1]);
1501
1502            // 'm' parameter.
1503            switch (clirInfo[1]) {
1504                case CLIR_NOT_PROVISIONED:
1505                    sb.append(mContext.getText(
1506                            com.android.internal.R.string.serviceNotProvisioned));
1507                    mState = State.COMPLETE;
1508                    break;
1509                case CLIR_PROVISIONED_PERMANENT:
1510                    sb.append(mContext.getText(
1511                            com.android.internal.R.string.CLIRPermanent));
1512                    mState = State.COMPLETE;
1513                    break;
1514                case CLIR_PRESENTATION_RESTRICTED_TEMPORARY:
1515                    // 'n' parameter.
1516                    switch (clirInfo[0]) {
1517                        case CLIR_DEFAULT:
1518                            sb.append(mContext.getText(
1519                                    com.android.internal.R.string.CLIRDefaultOnNextCallOn));
1520                            mState = State.COMPLETE;
1521                            break;
1522                        case CLIR_INVOCATION:
1523                            sb.append(mContext.getText(
1524                                    com.android.internal.R.string.CLIRDefaultOnNextCallOn));
1525                            mState = State.COMPLETE;
1526                            break;
1527                        case CLIR_SUPPRESSION:
1528                            sb.append(mContext.getText(
1529                                    com.android.internal.R.string.CLIRDefaultOnNextCallOff));
1530                            mState = State.COMPLETE;
1531                            break;
1532                        default:
1533                            sb.append(mContext.getText(
1534                                    com.android.internal.R.string.mmiError));
1535                            mState = State.FAILED;
1536                    }
1537                    break;
1538                case CLIR_PRESENTATION_ALLOWED_TEMPORARY:
1539                    // 'n' parameter.
1540                    switch (clirInfo[0]) {
1541                        case CLIR_DEFAULT:
1542                            sb.append(mContext.getText(
1543                                    com.android.internal.R.string.CLIRDefaultOffNextCallOff));
1544                            mState = State.COMPLETE;
1545                            break;
1546                        case CLIR_INVOCATION:
1547                            sb.append(mContext.getText(
1548                                    com.android.internal.R.string.CLIRDefaultOffNextCallOn));
1549                            mState = State.COMPLETE;
1550                            break;
1551                        case CLIR_SUPPRESSION:
1552                            sb.append(mContext.getText(
1553                                    com.android.internal.R.string.CLIRDefaultOffNextCallOff));
1554                            mState = State.COMPLETE;
1555                            break;
1556                        default:
1557                            sb.append(mContext.getText(
1558                                    com.android.internal.R.string.mmiError));
1559                            mState = State.FAILED;
1560                    }
1561                    break;
1562                default:
1563                    sb.append(mContext.getText(
1564                            com.android.internal.R.string.mmiError));
1565                    mState = State.FAILED;
1566            }
1567        }
1568
1569        mMessage = sb;
1570        mPhone.onMMIDone(this);
1571    }
1572
1573    private void
1574    onQueryComplete(AsyncResult ar) {
1575        StringBuilder sb = new StringBuilder(getScString());
1576        sb.append("\n");
1577
1578        if (ar.exception != null) {
1579            mState = State.FAILED;
1580
1581            if (ar.exception instanceof ImsException) {
1582                ImsException error = (ImsException) ar.exception;
1583                if (error.getMessage() != null) {
1584                    sb.append(error.getMessage());
1585                } else {
1586                    sb.append(getErrorMessage(ar));
1587                }
1588            } else {
1589                sb.append(getErrorMessage(ar));
1590            }
1591
1592        } else {
1593            int[] ints = (int[])ar.result;
1594
1595            if (ints.length != 0) {
1596                if (ints[0] == 0) {
1597                    sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled));
1598                } else if (mSc.equals(SC_WAIT)) {
1599                    // Call Waiting includes additional data in the response.
1600                    sb.append(createQueryCallWaitingResultMessage(ints[1]));
1601                } else if (ints[0] == 1) {
1602                    // for all other services, treat it as a boolean
1603                    sb.append(mContext.getText(com.android.internal.R.string.serviceEnabled));
1604                } else {
1605                    sb.append(mContext.getText(com.android.internal.R.string.mmiError));
1606                }
1607            } else {
1608                sb.append(mContext.getText(com.android.internal.R.string.mmiError));
1609            }
1610            mState = State.COMPLETE;
1611        }
1612
1613        mMessage = sb;
1614        mPhone.onMMIDone(this);
1615    }
1616
1617    private CharSequence
1618    createQueryCallWaitingResultMessage(int serviceClass) {
1619        StringBuilder sb = new StringBuilder(
1620                mContext.getText(com.android.internal.R.string.serviceEnabledFor));
1621
1622        for (int classMask = 1
1623                    ; classMask <= SERVICE_CLASS_MAX
1624                    ; classMask <<= 1
1625        ) {
1626            if ((classMask & serviceClass) != 0) {
1627                sb.append("\n");
1628                sb.append(serviceClassToCFString(classMask & serviceClass));
1629            }
1630        }
1631        return sb;
1632    }
1633
1634    /***
1635     * TODO: It would be nice to have a method here that can take in a dialstring and
1636     * figure out if there is an MMI code embedded within it.  This code would replace
1637     * some of the string parsing functionality in the Phone App's
1638     * SpecialCharSequenceMgr class.
1639     */
1640
1641    @Override
1642    public String toString() {
1643        StringBuilder sb = new StringBuilder("ImsPhoneMmiCode {");
1644
1645        sb.append("State=" + getState());
1646        if (mAction != null) sb.append(" action=" + mAction);
1647        if (mSc != null) sb.append(" sc=" + mSc);
1648        if (mSia != null) sb.append(" sia=" + mSia);
1649        if (mSib != null) sb.append(" sib=" + mSib);
1650        if (mSic != null) sb.append(" sic=" + mSic);
1651        if (mPoundString != null) sb.append(" poundString=" + mPoundString);
1652        if (mDialingNumber != null) sb.append(" dialingNumber=" + mDialingNumber);
1653        if (mPwd != null) sb.append(" pwd=" + mPwd);
1654        sb.append("}");
1655        return sb.toString();
1656    }
1657}
1658