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