ImsPhoneMmiCode.java revision 340a263442798f3d959bc443375ab9a5c6654441
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.mIccRecords.get();
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.Error err = ((CommandException)(ar.exception)).getCommandError();
1177                if (err == CommandException.Error.PASSWORD_INCORRECT) {
1178                    sb.append(mContext.getText(
1179                            com.android.internal.R.string.passwordIncorrect));
1180                } else {
1181                    sb.append(mContext.getText(
1182                            com.android.internal.R.string.mmiError));
1183                }
1184            } else {
1185                ImsException error = (ImsException) ar.exception;
1186                if (error.getMessage() != null) {
1187                    sb.append(error.getMessage());
1188                } else {
1189                    sb.append(getErrorMessage(ar));
1190                }
1191            }
1192        } else if (isActivate()) {
1193            mState = State.COMPLETE;
1194            if (mIsCallFwdReg) {
1195                sb.append(mContext.getText(
1196                        com.android.internal.R.string.serviceRegistered));
1197            } else {
1198                sb.append(mContext.getText(
1199                        com.android.internal.R.string.serviceEnabled));
1200            }
1201        } else if (isDeactivate()) {
1202            mState = State.COMPLETE;
1203            sb.append(mContext.getText(
1204                    com.android.internal.R.string.serviceDisabled));
1205        } else if (isRegister()) {
1206            mState = State.COMPLETE;
1207            sb.append(mContext.getText(
1208                    com.android.internal.R.string.serviceRegistered));
1209        } else if (isErasure()) {
1210            mState = State.COMPLETE;
1211            sb.append(mContext.getText(
1212                    com.android.internal.R.string.serviceErased));
1213        } else {
1214            mState = State.FAILED;
1215            sb.append(mContext.getText(
1216                    com.android.internal.R.string.mmiError));
1217        }
1218
1219        mMessage = sb;
1220        mPhone.onMMIDone(this);
1221    }
1222
1223    /**
1224     * @param serviceClass 1 bit of the service class bit vectory
1225     * @return String to be used for call forward query MMI response text.
1226     *        Returns null if unrecognized
1227     */
1228
1229    private CharSequence
1230    serviceClassToCFString (int serviceClass) {
1231        switch (serviceClass) {
1232            case SERVICE_CLASS_VOICE:
1233                return mContext.getText(com.android.internal.R.string.serviceClassVoice);
1234            case SERVICE_CLASS_DATA:
1235                return mContext.getText(com.android.internal.R.string.serviceClassData);
1236            case SERVICE_CLASS_FAX:
1237                return mContext.getText(com.android.internal.R.string.serviceClassFAX);
1238            case SERVICE_CLASS_SMS:
1239                return mContext.getText(com.android.internal.R.string.serviceClassSMS);
1240            case SERVICE_CLASS_DATA_SYNC:
1241                return mContext.getText(com.android.internal.R.string.serviceClassDataSync);
1242            case SERVICE_CLASS_DATA_ASYNC:
1243                return mContext.getText(com.android.internal.R.string.serviceClassDataAsync);
1244            case SERVICE_CLASS_PACKET:
1245                return mContext.getText(com.android.internal.R.string.serviceClassPacket);
1246            case SERVICE_CLASS_PAD:
1247                return mContext.getText(com.android.internal.R.string.serviceClassPAD);
1248            default:
1249                return null;
1250        }
1251    }
1252
1253    /** one CallForwardInfo + serviceClassMask -> one line of text */
1254    private CharSequence
1255    makeCFQueryResultMessage(CallForwardInfo info, int serviceClassMask) {
1256        CharSequence template;
1257        String sources[] = {"{0}", "{1}", "{2}"};
1258        CharSequence destinations[] = new CharSequence[3];
1259        boolean needTimeTemplate;
1260
1261        // CF_REASON_NO_REPLY also has a time value associated with
1262        // it. All others don't.
1263
1264        needTimeTemplate =
1265            (info.reason == CommandsInterface.CF_REASON_NO_REPLY);
1266
1267        if (info.status == 1) {
1268            if (needTimeTemplate) {
1269                template = mContext.getText(
1270                        com.android.internal.R.string.cfTemplateForwardedTime);
1271            } else {
1272                template = mContext.getText(
1273                        com.android.internal.R.string.cfTemplateForwarded);
1274            }
1275        } else if (info.status == 0 && isEmptyOrNull(info.number)) {
1276            template = mContext.getText(
1277                        com.android.internal.R.string.cfTemplateNotForwarded);
1278        } else { /* (info.status == 0) && !isEmptyOrNull(info.number) */
1279            // A call forward record that is not active but contains
1280            // a phone number is considered "registered"
1281
1282            if (needTimeTemplate) {
1283                template = mContext.getText(
1284                        com.android.internal.R.string.cfTemplateRegisteredTime);
1285            } else {
1286                template = mContext.getText(
1287                        com.android.internal.R.string.cfTemplateRegistered);
1288            }
1289        }
1290
1291        // In the template (from strings.xmls)
1292        //         {0} is one of "bearerServiceCode*"
1293        //        {1} is dialing number
1294        //      {2} is time in seconds
1295
1296        destinations[0] = serviceClassToCFString(info.serviceClass & serviceClassMask);
1297        destinations[1] = PhoneNumberUtils.stringFromStringAndTOA(info.number, info.toa);
1298        destinations[2] = Integer.toString(info.timeSeconds);
1299
1300        if (info.reason == CommandsInterface.CF_REASON_UNCONDITIONAL &&
1301                (info.serviceClass & serviceClassMask)
1302                        == CommandsInterface.SERVICE_CLASS_VOICE) {
1303            boolean cffEnabled = (info.status == 1);
1304            if (mIccRecords != null) {
1305                mPhone.setVoiceCallForwardingFlag(1, cffEnabled, info.number);
1306            }
1307        }
1308
1309        return TextUtils.replace(template, sources, destinations);
1310    }
1311
1312
1313    private void
1314    onQueryCfComplete(AsyncResult ar) {
1315        StringBuilder sb = new StringBuilder(getScString());
1316        sb.append("\n");
1317
1318        if (ar.exception != null) {
1319            mState = State.FAILED;
1320
1321            if (ar.exception instanceof ImsException) {
1322                ImsException error = (ImsException) ar.exception;
1323                if (error.getMessage() != null) {
1324                    sb.append(error.getMessage());
1325                } else {
1326                    sb.append(getErrorMessage(ar));
1327                }
1328            }
1329            else {
1330                sb.append(getErrorMessage(ar));
1331            }
1332        } else {
1333            CallForwardInfo infos[];
1334
1335            infos = (CallForwardInfo[]) ar.result;
1336
1337            if (infos.length == 0) {
1338                // Assume the default is not active
1339                sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled));
1340
1341                // Set unconditional CFF in SIM to false
1342                if (mIccRecords != null) {
1343                    mPhone.setVoiceCallForwardingFlag(1, false, null);
1344                }
1345            } else {
1346
1347                SpannableStringBuilder tb = new SpannableStringBuilder();
1348
1349                // Each bit in the service class gets its own result line
1350                // The service classes may be split up over multiple
1351                // CallForwardInfos. So, for each service class, find out
1352                // which CallForwardInfo represents it and then build
1353                // the response text based on that
1354
1355                for (int serviceClassMask = 1
1356                            ; serviceClassMask <= SERVICE_CLASS_MAX
1357                            ; serviceClassMask <<= 1
1358                ) {
1359                    for (int i = 0, s = infos.length; i < s ; i++) {
1360                        if ((serviceClassMask & infos[i].serviceClass) != 0) {
1361                            tb.append(makeCFQueryResultMessage(infos[i],
1362                                            serviceClassMask));
1363                            tb.append("\n");
1364                        }
1365                    }
1366                }
1367                sb.append(tb);
1368            }
1369
1370            mState = State.COMPLETE;
1371        }
1372
1373        mMessage = sb;
1374        mPhone.onMMIDone(this);
1375
1376    }
1377
1378    private void onSuppSvcQueryComplete(AsyncResult ar) {
1379        StringBuilder sb = new StringBuilder(getScString());
1380        sb.append("\n");
1381
1382        if (ar.exception != null) {
1383            mState = State.FAILED;
1384
1385            if (ar.exception instanceof ImsException) {
1386                ImsException error = (ImsException) ar.exception;
1387                if (error.getMessage() != null) {
1388                    sb.append(error.getMessage());
1389                } else {
1390                    sb.append(getErrorMessage(ar));
1391                }
1392            } else {
1393                sb.append(getErrorMessage(ar));
1394            }
1395        } else {
1396            mState = State.FAILED;
1397            ImsSsInfo ssInfo = null;
1398            if (ar.result instanceof Bundle) {
1399                Rlog.d(LOG_TAG, "Received CLIP/COLP/COLR Response.");
1400                // Response for CLIP, COLP and COLR queries.
1401                Bundle ssInfoResp = (Bundle) ar.result;
1402                ssInfo = (ImsSsInfo) ssInfoResp.getParcelable(UT_BUNDLE_KEY_SSINFO);
1403                if (ssInfo != null) {
1404                    Rlog.d(LOG_TAG, "ImsSsInfo mStatus = " + ssInfo.mStatus);
1405                    if (ssInfo.mStatus == ImsSsInfo.DISABLED) {
1406                        sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled));
1407                        mState = State.COMPLETE;
1408                    } else if (ssInfo.mStatus == ImsSsInfo.ENABLED) {
1409                        sb.append(mContext.getText(com.android.internal.R.string.serviceEnabled));
1410                        mState = State.COMPLETE;
1411                    } else {
1412                        sb.append(mContext.getText(com.android.internal.R.string.mmiError));
1413                    }
1414                } else {
1415                    sb.append(mContext.getText(com.android.internal.R.string.mmiError));
1416                }
1417
1418            } else {
1419                Rlog.d(LOG_TAG, "Received Call Barring Response.");
1420                // Response for Call Barring queries.
1421                int[] cbInfos = (int[]) ar.result;
1422                if (cbInfos[0] == 1) {
1423                    sb.append(mContext.getText(com.android.internal.R.string.serviceEnabled));
1424                    mState = State.COMPLETE;
1425                } else {
1426                    sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled));
1427                    mState = State.COMPLETE;
1428                }
1429            }
1430        }
1431
1432        mMessage = sb;
1433        mPhone.onMMIDone(this);
1434    }
1435
1436    private void onIcbQueryComplete(AsyncResult ar) {
1437        Rlog.d(LOG_TAG, "onIcbQueryComplete ");
1438        StringBuilder sb = new StringBuilder(getScString());
1439        sb.append("\n");
1440
1441        if (ar.exception != null) {
1442            mState = State.FAILED;
1443
1444            if (ar.exception instanceof ImsException) {
1445                ImsException error = (ImsException) ar.exception;
1446                if (error.getMessage() != null) {
1447                    sb.append(error.getMessage());
1448                } else {
1449                    sb.append(getErrorMessage(ar));
1450                }
1451            } else {
1452                sb.append(getErrorMessage(ar));
1453            }
1454        } else {
1455            ImsSsInfo[] infos = (ImsSsInfo[])ar.result;
1456            if (infos.length == 0) {
1457                sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled));
1458            } else {
1459                for (int i = 0, s = infos.length; i < s ; i++) {
1460                    if (infos[i].mIcbNum !=null) {
1461                        sb.append("Num: " + infos[i].mIcbNum + " status: "
1462                                + infos[i].mStatus + "\n");
1463                    } else if (infos[i].mStatus == 1) {
1464                        sb.append(mContext.getText(com.android.internal
1465                                .R.string.serviceEnabled));
1466                    } else {
1467                        sb.append(mContext.getText(com.android.internal
1468                                .R.string.serviceDisabled));
1469                    }
1470                }
1471            }
1472            mState = State.COMPLETE;
1473        }
1474        mMessage = sb;
1475        mPhone.onMMIDone(this);
1476    }
1477
1478    private void onQueryClirComplete(AsyncResult ar) {
1479        StringBuilder sb = new StringBuilder(getScString());
1480        sb.append("\n");
1481        mState = State.FAILED;
1482
1483        if (ar.exception != null) {
1484
1485            if (ar.exception instanceof ImsException) {
1486                ImsException error = (ImsException) ar.exception;
1487                if (error.getMessage() != null) {
1488                    sb.append(error.getMessage());
1489                } else {
1490                    sb.append(getErrorMessage(ar));
1491                }
1492            }
1493        } else {
1494            Bundle ssInfo = (Bundle) ar.result;
1495            int[] clirInfo = ssInfo.getIntArray(UT_BUNDLE_KEY_CLIR);
1496            // clirInfo[0] = The 'n' parameter from TS 27.007 7.7
1497            // clirInfo[1] = The 'm' parameter from TS 27.007 7.7
1498            Rlog.d(LOG_TAG, "CLIR param n=" + clirInfo[0]
1499                    + " m=" + clirInfo[1]);
1500
1501            // 'm' parameter.
1502            switch (clirInfo[1]) {
1503                case CLIR_NOT_PROVISIONED:
1504                    sb.append(mContext.getText(
1505                            com.android.internal.R.string.serviceNotProvisioned));
1506                    mState = State.COMPLETE;
1507                    break;
1508                case CLIR_PROVISIONED_PERMANENT:
1509                    sb.append(mContext.getText(
1510                            com.android.internal.R.string.CLIRPermanent));
1511                    mState = State.COMPLETE;
1512                    break;
1513                case CLIR_PRESENTATION_RESTRICTED_TEMPORARY:
1514                    // 'n' parameter.
1515                    switch (clirInfo[0]) {
1516                        case CLIR_DEFAULT:
1517                            sb.append(mContext.getText(
1518                                    com.android.internal.R.string.CLIRDefaultOnNextCallOn));
1519                            mState = State.COMPLETE;
1520                            break;
1521                        case CLIR_INVOCATION:
1522                            sb.append(mContext.getText(
1523                                    com.android.internal.R.string.CLIRDefaultOnNextCallOn));
1524                            mState = State.COMPLETE;
1525                            break;
1526                        case CLIR_SUPPRESSION:
1527                            sb.append(mContext.getText(
1528                                    com.android.internal.R.string.CLIRDefaultOnNextCallOff));
1529                            mState = State.COMPLETE;
1530                            break;
1531                        default:
1532                            sb.append(mContext.getText(
1533                                    com.android.internal.R.string.mmiError));
1534                            mState = State.FAILED;
1535                    }
1536                    break;
1537                case CLIR_PRESENTATION_ALLOWED_TEMPORARY:
1538                    // 'n' parameter.
1539                    switch (clirInfo[0]) {
1540                        case CLIR_DEFAULT:
1541                            sb.append(mContext.getText(
1542                                    com.android.internal.R.string.CLIRDefaultOffNextCallOff));
1543                            mState = State.COMPLETE;
1544                            break;
1545                        case CLIR_INVOCATION:
1546                            sb.append(mContext.getText(
1547                                    com.android.internal.R.string.CLIRDefaultOffNextCallOn));
1548                            mState = State.COMPLETE;
1549                            break;
1550                        case CLIR_SUPPRESSION:
1551                            sb.append(mContext.getText(
1552                                    com.android.internal.R.string.CLIRDefaultOffNextCallOff));
1553                            mState = State.COMPLETE;
1554                            break;
1555                        default:
1556                            sb.append(mContext.getText(
1557                                    com.android.internal.R.string.mmiError));
1558                            mState = State.FAILED;
1559                    }
1560                    break;
1561                default:
1562                    sb.append(mContext.getText(
1563                            com.android.internal.R.string.mmiError));
1564                    mState = State.FAILED;
1565            }
1566        }
1567
1568        mMessage = sb;
1569        mPhone.onMMIDone(this);
1570    }
1571
1572    private void
1573    onQueryComplete(AsyncResult ar) {
1574        StringBuilder sb = new StringBuilder(getScString());
1575        sb.append("\n");
1576
1577        if (ar.exception != null) {
1578            mState = State.FAILED;
1579
1580            if (ar.exception instanceof ImsException) {
1581                ImsException error = (ImsException) ar.exception;
1582                if (error.getMessage() != null) {
1583                    sb.append(error.getMessage());
1584                } else {
1585                    sb.append(getErrorMessage(ar));
1586                }
1587            } else {
1588                sb.append(getErrorMessage(ar));
1589            }
1590
1591        } else {
1592            int[] ints = (int[])ar.result;
1593
1594            if (ints.length != 0) {
1595                if (ints[0] == 0) {
1596                    sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled));
1597                } else if (mSc.equals(SC_WAIT)) {
1598                    // Call Waiting includes additional data in the response.
1599                    sb.append(createQueryCallWaitingResultMessage(ints[1]));
1600                } else if (ints[0] == 1) {
1601                    // for all other services, treat it as a boolean
1602                    sb.append(mContext.getText(com.android.internal.R.string.serviceEnabled));
1603                } else {
1604                    sb.append(mContext.getText(com.android.internal.R.string.mmiError));
1605                }
1606            } else {
1607                sb.append(mContext.getText(com.android.internal.R.string.mmiError));
1608            }
1609            mState = State.COMPLETE;
1610        }
1611
1612        mMessage = sb;
1613        mPhone.onMMIDone(this);
1614    }
1615
1616    private CharSequence
1617    createQueryCallWaitingResultMessage(int serviceClass) {
1618        StringBuilder sb = new StringBuilder(
1619                mContext.getText(com.android.internal.R.string.serviceEnabledFor));
1620
1621        for (int classMask = 1
1622                    ; classMask <= SERVICE_CLASS_MAX
1623                    ; classMask <<= 1
1624        ) {
1625            if ((classMask & serviceClass) != 0) {
1626                sb.append("\n");
1627                sb.append(serviceClassToCFString(classMask & serviceClass));
1628            }
1629        }
1630        return sb;
1631    }
1632
1633    /***
1634     * TODO: It would be nice to have a method here that can take in a dialstring and
1635     * figure out if there is an MMI code embedded within it.  This code would replace
1636     * some of the string parsing functionality in the Phone App's
1637     * SpecialCharSequenceMgr class.
1638     */
1639
1640    @Override
1641    public String toString() {
1642        StringBuilder sb = new StringBuilder("ImsPhoneMmiCode {");
1643
1644        sb.append("State=" + getState());
1645        if (mAction != null) sb.append(" action=" + mAction);
1646        if (mSc != null) sb.append(" sc=" + mSc);
1647        if (mSia != null) sb.append(" sia=" + mSia);
1648        if (mSib != null) sb.append(" sib=" + mSib);
1649        if (mSic != null) sb.append(" sic=" + mSic);
1650        if (mPoundString != null) sb.append(" poundString=" + mPoundString);
1651        if (mDialingNumber != null) sb.append(" dialingNumber=" + mDialingNumber);
1652        if (mPwd != null) sb.append(" pwd=" + mPwd);
1653        sb.append("}");
1654        return sb.toString();
1655    }
1656}
1657