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 
    710                 String dialingNumber = mSia;
    711                 int reason = scToCallForwardReason(mSc);
    712                 int serviceClass = siToServiceClass(mSib);
    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, serviceClass, 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                 int serviceClass = siToServiceClass(mSib);
    894 
    895                 if (isActivate() || isDeactivate()) {
    896                     mPhone.setCallWaiting(isActivate(), serviceClass,
    897                             obtainMessage(EVENT_SET_COMPLETE, this));
    898                 } else if (isInterrogate()) {
    899                     mPhone.getCallWaiting(obtainMessage(EVENT_QUERY_COMPLETE, this));
    900                 } else {
    901                     throw new RuntimeException ("Invalid or Unsupported MMI Code");
    902                 }
    903             } else if (mPoundString != null) {
    904                 Rlog.d(LOG_TAG, "Sending pound string '"
    905                        + mDialingNumber + "' over CS pipe.");
    906                 throw new CallStateException(ImsPhone.CS_FALLBACK);
    907             } else {
    908                 throw new RuntimeException ("Invalid or Unsupported MMI Code");
    909             }
    910         } catch (RuntimeException exc) {
    911             mState = State.FAILED;
    912             mMessage = mContext.getText(com.android.internal.R.string.mmiError);
    913             mPhone.onMMIDone(this);
    914         }
    915     }
    916 
    917     /**
    918      * Called from ImsPhone
    919      *
    920      * An unsolicited USSD NOTIFY or REQUEST has come in matching
    921      * up with this pending USSD request
    922      *
    923      * Note: If REQUEST, this exchange is complete, but the session remains
    924      *       active (ie, the network expects user input).
    925      */
    926     void
    927     onUssdFinished(String ussdMessage, boolean isUssdRequest) {
    928         if (mState == State.PENDING) {
    929             if (ussdMessage == null) {
    930                 mMessage = mContext.getText(com.android.internal.R.string.mmiComplete);
    931             } else {
    932                 mMessage = ussdMessage;
    933             }
    934             mIsUssdRequest = isUssdRequest;
    935             // If it's a request, leave it PENDING so that it's cancelable.
    936             if (!isUssdRequest) {
    937                 mState = State.COMPLETE;
    938             }
    939 
    940             mPhone.onMMIDone(this);
    941         }
    942     }
    943 
    944     /**
    945      * Called from ImsPhone
    946      *
    947      * The radio has reset, and this is still pending
    948      */
    949 
    950     void
    951     onUssdFinishedError() {
    952         if (mState == State.PENDING) {
    953             mState = State.FAILED;
    954             mMessage = mContext.getText(com.android.internal.R.string.mmiError);
    955 
    956             mPhone.onMMIDone(this);
    957         }
    958     }
    959 
    960     void sendUssd(String ussdMessage) {
    961         // Treat this as a USSD string
    962         mIsPendingUSSD = true;
    963 
    964         // Note that unlike most everything else, the USSD complete
    965         // response does not complete this MMI code...we wait for
    966         // an unsolicited USSD "Notify" or "Request".
    967         // The matching up of this is done in ImsPhone.
    968 
    969         mPhone.sendUSSD(ussdMessage,
    970             obtainMessage(EVENT_USSD_COMPLETE, this));
    971     }
    972 
    973     /** Called from ImsPhone.handleMessage; not a Handler subclass */
    974     @Override
    975     public void
    976     handleMessage (Message msg) {
    977         AsyncResult ar;
    978 
    979         switch (msg.what) {
    980             case EVENT_SET_COMPLETE:
    981                 ar = (AsyncResult) (msg.obj);
    982 
    983                 onSetComplete(msg, ar);
    984                 break;
    985 
    986             case EVENT_SET_CFF_COMPLETE:
    987                 ar = (AsyncResult) (msg.obj);
    988 
    989                 /*
    990                 * msg.arg1 = 1 means to set unconditional voice call forwarding
    991                 * msg.arg2 = 1 means to enable voice call forwarding
    992                 */
    993                 if ((ar.exception == null) && (msg.arg1 == 1)) {
    994                     boolean cffEnabled = (msg.arg2 == 1);
    995                     if (mIccRecords != null) {
    996                         mIccRecords.setVoiceCallForwardingFlag(1, cffEnabled, mDialingNumber);
    997                     }
    998                 }
    999 
   1000                 onSetComplete(msg, ar);
   1001                 break;
   1002 
   1003             case EVENT_QUERY_CF_COMPLETE:
   1004                 ar = (AsyncResult) (msg.obj);
   1005                 onQueryCfComplete(ar);
   1006                 break;
   1007 
   1008             case EVENT_QUERY_COMPLETE:
   1009                 ar = (AsyncResult) (msg.obj);
   1010                 onQueryComplete(ar);
   1011                 break;
   1012 
   1013             case EVENT_USSD_COMPLETE:
   1014                 ar = (AsyncResult) (msg.obj);
   1015 
   1016                 if (ar.exception != null) {
   1017                     mState = State.FAILED;
   1018                     mMessage = getErrorMessage(ar);
   1019 
   1020                     mPhone.onMMIDone(this);
   1021                 }
   1022 
   1023                 // Note that unlike most everything else, the USSD complete
   1024                 // response does not complete this MMI code...we wait for
   1025                 // an unsolicited USSD "Notify" or "Request".
   1026                 // The matching up of this is done in ImsPhone.
   1027 
   1028                 break;
   1029 
   1030             case EVENT_USSD_CANCEL_COMPLETE:
   1031                 mPhone.onMMIDone(this);
   1032                 break;
   1033 
   1034             case EVENT_SUPP_SVC_QUERY_COMPLETE:
   1035                 ar = (AsyncResult) (msg.obj);
   1036                 onSuppSvcQueryComplete(ar);
   1037                 break;
   1038 
   1039             case EVENT_GET_CLIR_COMPLETE:
   1040                 ar = (AsyncResult) (msg.obj);
   1041                 onQueryClirComplete(ar);
   1042                 break;
   1043 
   1044             default:
   1045                 break;
   1046         }
   1047     }
   1048 
   1049     //***** Private instance methods
   1050 
   1051     private void
   1052     processIcbMmiCodeForUpdate () {
   1053         String dialingNumber = mSia;
   1054         String[] icbNum = null;
   1055 
   1056         if (dialingNumber != null) {
   1057             icbNum = dialingNumber.split("\\$");
   1058         }
   1059 
   1060         try {
   1061             mPhone.mCT.getUtInterface()
   1062             .updateCallBarring(ImsUtInterface.CB_BS_MT,
   1063                                isActivate(),
   1064                                obtainMessage(EVENT_SUPP_SVC_QUERY_COMPLETE,this),
   1065                                icbNum);
   1066         } catch (ImsException e) {
   1067             Rlog.d(LOG_TAG, "Could not get UT handle for updating ICB.");
   1068         }
   1069     }
   1070 
   1071     private CharSequence getErrorMessage(AsyncResult ar) {
   1072         return mContext.getText(com.android.internal.R.string.mmiError);
   1073     }
   1074 
   1075     private CharSequence getScString() {
   1076         if (mSc != null) {
   1077             if (isServiceCodeCallBarring(mSc)) {
   1078                 return mContext.getText(com.android.internal.R.string.BaMmi);
   1079             } else if (isServiceCodeCallForwarding(mSc)) {
   1080                 return mContext.getText(com.android.internal.R.string.CfMmi);
   1081             } else if (mSc.equals(SC_PWD)) {
   1082                 return mContext.getText(com.android.internal.R.string.PwdMmi);
   1083             } else if (mSc.equals(SC_WAIT)) {
   1084                 return mContext.getText(com.android.internal.R.string.CwMmi);
   1085             } else if (mSc.equals(SC_CLIP)) {
   1086                 return mContext.getText(com.android.internal.R.string.ClipMmi);
   1087             } else if (mSc.equals(SC_CLIR)) {
   1088                 return mContext.getText(com.android.internal.R.string.ClirMmi);
   1089             } else if (mSc.equals(SC_COLP)) {
   1090                 return mContext.getText(com.android.internal.R.string.ColpMmi);
   1091             } else if (mSc.equals(SC_COLR)) {
   1092                 return mContext.getText(com.android.internal.R.string.ColrMmi);
   1093             }
   1094         }
   1095 
   1096         return "";
   1097     }
   1098 
   1099     private void
   1100     onSetComplete(Message msg, AsyncResult ar){
   1101         StringBuilder sb = new StringBuilder(getScString());
   1102         sb.append("\n");
   1103 
   1104         if (ar.exception != null) {
   1105             mState = State.FAILED;
   1106 
   1107             if (ar.exception instanceof CommandException) {
   1108                 CommandException.Error err = ((CommandException)(ar.exception)).getCommandError();
   1109                 if (err == CommandException.Error.PASSWORD_INCORRECT) {
   1110                     sb.append(mContext.getText(
   1111                             com.android.internal.R.string.passwordIncorrect));
   1112                 } else {
   1113                     sb.append(mContext.getText(
   1114                             com.android.internal.R.string.mmiError));
   1115                 }
   1116             } else {
   1117                 ImsException error = (ImsException) ar.exception;
   1118                 if (error.getMessage() != null) {
   1119                     sb.append(error.getMessage());
   1120                 } else {
   1121                     sb.append(getErrorMessage(ar));
   1122                 }
   1123             }
   1124         } else if (isActivate()) {
   1125             mState = State.COMPLETE;
   1126             if (mIsCallFwdReg) {
   1127                 sb.append(mContext.getText(
   1128                         com.android.internal.R.string.serviceRegistered));
   1129             } else {
   1130                 sb.append(mContext.getText(
   1131                         com.android.internal.R.string.serviceEnabled));
   1132             }
   1133         } else if (isDeactivate()) {
   1134             mState = State.COMPLETE;
   1135             sb.append(mContext.getText(
   1136                     com.android.internal.R.string.serviceDisabled));
   1137         } else if (isRegister()) {
   1138             mState = State.COMPLETE;
   1139             sb.append(mContext.getText(
   1140                     com.android.internal.R.string.serviceRegistered));
   1141         } else if (isErasure()) {
   1142             mState = State.COMPLETE;
   1143             sb.append(mContext.getText(
   1144                     com.android.internal.R.string.serviceErased));
   1145         } else {
   1146             mState = State.FAILED;
   1147             sb.append(mContext.getText(
   1148                     com.android.internal.R.string.mmiError));
   1149         }
   1150 
   1151         mMessage = sb;
   1152         mPhone.onMMIDone(this);
   1153     }
   1154 
   1155     /**
   1156      * @param serviceClass 1 bit of the service class bit vectory
   1157      * @return String to be used for call forward query MMI response text.
   1158      *        Returns null if unrecognized
   1159      */
   1160 
   1161     private CharSequence
   1162     serviceClassToCFString (int serviceClass) {
   1163         switch (serviceClass) {
   1164             case SERVICE_CLASS_VOICE:
   1165                 return mContext.getText(com.android.internal.R.string.serviceClassVoice);
   1166             case SERVICE_CLASS_DATA:
   1167                 return mContext.getText(com.android.internal.R.string.serviceClassData);
   1168             case SERVICE_CLASS_FAX:
   1169                 return mContext.getText(com.android.internal.R.string.serviceClassFAX);
   1170             case SERVICE_CLASS_SMS:
   1171                 return mContext.getText(com.android.internal.R.string.serviceClassSMS);
   1172             case SERVICE_CLASS_DATA_SYNC:
   1173                 return mContext.getText(com.android.internal.R.string.serviceClassDataSync);
   1174             case SERVICE_CLASS_DATA_ASYNC:
   1175                 return mContext.getText(com.android.internal.R.string.serviceClassDataAsync);
   1176             case SERVICE_CLASS_PACKET:
   1177                 return mContext.getText(com.android.internal.R.string.serviceClassPacket);
   1178             case SERVICE_CLASS_PAD:
   1179                 return mContext.getText(com.android.internal.R.string.serviceClassPAD);
   1180             default:
   1181                 return null;
   1182         }
   1183     }
   1184 
   1185     /** one CallForwardInfo + serviceClassMask -> one line of text */
   1186     private CharSequence
   1187     makeCFQueryResultMessage(CallForwardInfo info, int serviceClassMask) {
   1188         CharSequence template;
   1189         String sources[] = {"{0}", "{1}", "{2}"};
   1190         CharSequence destinations[] = new CharSequence[3];
   1191         boolean needTimeTemplate;
   1192 
   1193         // CF_REASON_NO_REPLY also has a time value associated with
   1194         // it. All others don't.
   1195 
   1196         needTimeTemplate =
   1197             (info.reason == CommandsInterface.CF_REASON_NO_REPLY);
   1198 
   1199         if (info.status == 1) {
   1200             if (needTimeTemplate) {
   1201                 template = mContext.getText(
   1202                         com.android.internal.R.string.cfTemplateForwardedTime);
   1203             } else {
   1204                 template = mContext.getText(
   1205                         com.android.internal.R.string.cfTemplateForwarded);
   1206             }
   1207         } else if (info.status == 0 && isEmptyOrNull(info.number)) {
   1208             template = mContext.getText(
   1209                         com.android.internal.R.string.cfTemplateNotForwarded);
   1210         } else { /* (info.status == 0) && !isEmptyOrNull(info.number) */
   1211             // A call forward record that is not active but contains
   1212             // a phone number is considered "registered"
   1213 
   1214             if (needTimeTemplate) {
   1215                 template = mContext.getText(
   1216                         com.android.internal.R.string.cfTemplateRegisteredTime);
   1217             } else {
   1218                 template = mContext.getText(
   1219                         com.android.internal.R.string.cfTemplateRegistered);
   1220             }
   1221         }
   1222 
   1223         // In the template (from strings.xmls)
   1224         //         {0} is one of "bearerServiceCode*"
   1225         //        {1} is dialing number
   1226         //      {2} is time in seconds
   1227 
   1228         destinations[0] = serviceClassToCFString(info.serviceClass & serviceClassMask);
   1229         destinations[1] = PhoneNumberUtils.stringFromStringAndTOA(info.number, info.toa);
   1230         destinations[2] = Integer.toString(info.timeSeconds);
   1231 
   1232         if (info.reason == CommandsInterface.CF_REASON_UNCONDITIONAL &&
   1233                 (info.serviceClass & serviceClassMask)
   1234                         == CommandsInterface.SERVICE_CLASS_VOICE) {
   1235             boolean cffEnabled = (info.status == 1);
   1236             if (mIccRecords != null) {
   1237                 mIccRecords.setVoiceCallForwardingFlag(1, cffEnabled, info.number);
   1238             }
   1239         }
   1240 
   1241         return TextUtils.replace(template, sources, destinations);
   1242     }
   1243 
   1244 
   1245     private void
   1246     onQueryCfComplete(AsyncResult ar) {
   1247         StringBuilder sb = new StringBuilder(getScString());
   1248         sb.append("\n");
   1249 
   1250         if (ar.exception != null) {
   1251             mState = State.FAILED;
   1252 
   1253             if (ar.exception instanceof ImsException) {
   1254                 ImsException error = (ImsException) ar.exception;
   1255                 if (error.getMessage() != null) {
   1256                     sb.append(error.getMessage());
   1257                 } else {
   1258                     sb.append(getErrorMessage(ar));
   1259                 }
   1260             }
   1261             else {
   1262                 sb.append(getErrorMessage(ar));
   1263             }
   1264         } else {
   1265             CallForwardInfo infos[];
   1266 
   1267             infos = (CallForwardInfo[]) ar.result;
   1268 
   1269             if (infos.length == 0) {
   1270                 // Assume the default is not active
   1271                 sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled));
   1272 
   1273                 // Set unconditional CFF in SIM to false
   1274                 if (mIccRecords != null) {
   1275                     mIccRecords.setVoiceCallForwardingFlag(1, false, null);
   1276                 }
   1277             } else {
   1278 
   1279                 SpannableStringBuilder tb = new SpannableStringBuilder();
   1280 
   1281                 // Each bit in the service class gets its own result line
   1282                 // The service classes may be split up over multiple
   1283                 // CallForwardInfos. So, for each service class, find out
   1284                 // which CallForwardInfo represents it and then build
   1285                 // the response text based on that
   1286 
   1287                 for (int serviceClassMask = 1
   1288                             ; serviceClassMask <= SERVICE_CLASS_MAX
   1289                             ; serviceClassMask <<= 1
   1290                 ) {
   1291                     for (int i = 0, s = infos.length; i < s ; i++) {
   1292                         if ((serviceClassMask & infos[i].serviceClass) != 0) {
   1293                             tb.append(makeCFQueryResultMessage(infos[i],
   1294                                             serviceClassMask));
   1295                             tb.append("\n");
   1296                         }
   1297                     }
   1298                 }
   1299                 sb.append(tb);
   1300             }
   1301 
   1302             mState = State.COMPLETE;
   1303         }
   1304 
   1305         mMessage = sb;
   1306         mPhone.onMMIDone(this);
   1307 
   1308     }
   1309 
   1310     private void onSuppSvcQueryComplete(AsyncResult ar) {
   1311         StringBuilder sb = new StringBuilder(getScString());
   1312         sb.append("\n");
   1313 
   1314         if (ar.exception != null) {
   1315             mState = State.FAILED;
   1316 
   1317             if (ar.exception instanceof ImsException) {
   1318                 ImsException error = (ImsException) ar.exception;
   1319                 if (error.getMessage() != null) {
   1320                     sb.append(error.getMessage());
   1321                 } else {
   1322                     sb.append(getErrorMessage(ar));
   1323                 }
   1324             } else {
   1325                 sb.append(getErrorMessage(ar));
   1326             }
   1327         } else {
   1328             mState = State.FAILED;
   1329             ImsSsInfo ssInfo = null;
   1330             if (ar.result instanceof Bundle) {
   1331                 Rlog.d(LOG_TAG, "Received CLIP/COLP/COLR Response.");
   1332                 // Response for CLIP, COLP and COLR queries.
   1333                 Bundle ssInfoResp = (Bundle) ar.result;
   1334                 ssInfo = (ImsSsInfo) ssInfoResp.getParcelable(UT_BUNDLE_KEY_SSINFO);
   1335                 if (ssInfo != null) {
   1336                     Rlog.d(LOG_TAG, "ImsSsInfo mStatus = " + ssInfo.mStatus);
   1337                     if (ssInfo.mStatus == ImsSsInfo.DISABLED) {
   1338                         sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled));
   1339                         mState = State.COMPLETE;
   1340                     } else if (ssInfo.mStatus == ImsSsInfo.ENABLED) {
   1341                         sb.append(mContext.getText(com.android.internal.R.string.serviceEnabled));
   1342                         mState = State.COMPLETE;
   1343                     } else {
   1344                         sb.append(mContext.getText(com.android.internal.R.string.mmiError));
   1345                     }
   1346                 } else {
   1347                     sb.append(mContext.getText(com.android.internal.R.string.mmiError));
   1348                 }
   1349 
   1350             } else {
   1351                 Rlog.d(LOG_TAG, "Received Call Barring Response.");
   1352                 // Response for Call Barring queries.
   1353                 int[] cbInfos = (int[]) ar.result;
   1354                 // Check if ImsPhone has received call barring
   1355                 // enabled for service class voice.
   1356                 if (cbInfos[0] == 1) {
   1357                     sb.append(mContext.getText(com.android.internal.R.string.serviceEnabled));
   1358                     mState = State.COMPLETE;
   1359                 } else {
   1360                     sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled));
   1361                     mState = State.COMPLETE;
   1362                 }
   1363             }
   1364 
   1365         }
   1366 
   1367         mMessage = sb;
   1368         mPhone.onMMIDone(this);
   1369     }
   1370 
   1371     private void onQueryClirComplete(AsyncResult ar) {
   1372         StringBuilder sb = new StringBuilder(getScString());
   1373         sb.append("\n");
   1374         mState = State.FAILED;
   1375 
   1376         if (ar.exception != null) {
   1377 
   1378             if (ar.exception instanceof ImsException) {
   1379                 ImsException error = (ImsException) ar.exception;
   1380                 if (error.getMessage() != null) {
   1381                     sb.append(error.getMessage());
   1382                 } else {
   1383                     sb.append(getErrorMessage(ar));
   1384                 }
   1385             }
   1386         } else {
   1387             Bundle ssInfo = (Bundle) ar.result;
   1388             int[] clirInfo = ssInfo.getIntArray(UT_BUNDLE_KEY_CLIR);
   1389             // clirInfo[0] = The 'n' parameter from TS 27.007 7.7
   1390             // clirInfo[1] = The 'm' parameter from TS 27.007 7.7
   1391             Rlog.d(LOG_TAG, "CLIR param n=" + clirInfo[0]
   1392                     + " m=" + clirInfo[1]);
   1393 
   1394             // 'm' parameter.
   1395             switch (clirInfo[1]) {
   1396                 case CLIR_NOT_PROVISIONED:
   1397                     sb.append(mContext.getText(
   1398                             com.android.internal.R.string.serviceNotProvisioned));
   1399                     mState = State.COMPLETE;
   1400                     break;
   1401                 case CLIR_PROVISIONED_PERMANENT:
   1402                     sb.append(mContext.getText(
   1403                             com.android.internal.R.string.CLIRPermanent));
   1404                     mState = State.COMPLETE;
   1405                     break;
   1406                 case CLIR_PRESENTATION_RESTRICTED_TEMPORARY:
   1407                     // 'n' parameter.
   1408                     switch (clirInfo[0]) {
   1409                         case CLIR_DEFAULT:
   1410                             sb.append(mContext.getText(
   1411                                     com.android.internal.R.string.CLIRDefaultOnNextCallOn));
   1412                             mState = State.COMPLETE;
   1413                             break;
   1414                         case CLIR_INVOCATION:
   1415                             sb.append(mContext.getText(
   1416                                     com.android.internal.R.string.CLIRDefaultOnNextCallOn));
   1417                             mState = State.COMPLETE;
   1418                             break;
   1419                         case CLIR_SUPPRESSION:
   1420                             sb.append(mContext.getText(
   1421                                     com.android.internal.R.string.CLIRDefaultOnNextCallOff));
   1422                             mState = State.COMPLETE;
   1423                             break;
   1424                         default:
   1425                             sb.append(mContext.getText(
   1426                                     com.android.internal.R.string.mmiError));
   1427                             mState = State.FAILED;
   1428                     }
   1429                     break;
   1430                 case CLIR_PRESENTATION_ALLOWED_TEMPORARY:
   1431                     // 'n' parameter.
   1432                     switch (clirInfo[0]) {
   1433                         case CLIR_DEFAULT:
   1434                             sb.append(mContext.getText(
   1435                                     com.android.internal.R.string.CLIRDefaultOffNextCallOff));
   1436                             mState = State.COMPLETE;
   1437                             break;
   1438                         case CLIR_INVOCATION:
   1439                             sb.append(mContext.getText(
   1440                                     com.android.internal.R.string.CLIRDefaultOffNextCallOn));
   1441                             mState = State.COMPLETE;
   1442                             break;
   1443                         case CLIR_SUPPRESSION:
   1444                             sb.append(mContext.getText(
   1445                                     com.android.internal.R.string.CLIRDefaultOffNextCallOff));
   1446                             mState = State.COMPLETE;
   1447                             break;
   1448                         default:
   1449                             sb.append(mContext.getText(
   1450                                     com.android.internal.R.string.mmiError));
   1451                             mState = State.FAILED;
   1452                     }
   1453                     break;
   1454                 default:
   1455                     sb.append(mContext.getText(
   1456                             com.android.internal.R.string.mmiError));
   1457                     mState = State.FAILED;
   1458             }
   1459         }
   1460 
   1461         mMessage = sb;
   1462         mPhone.onMMIDone(this);
   1463     }
   1464 
   1465     private void
   1466     onQueryComplete(AsyncResult ar) {
   1467         StringBuilder sb = new StringBuilder(getScString());
   1468         sb.append("\n");
   1469 
   1470         if (ar.exception != null) {
   1471             mState = State.FAILED;
   1472 
   1473             if (ar.exception instanceof ImsException) {
   1474                 ImsException error = (ImsException) ar.exception;
   1475                 if (error.getMessage() != null) {
   1476                     sb.append(error.getMessage());
   1477                 } else {
   1478                     sb.append(getErrorMessage(ar));
   1479                 }
   1480             } else {
   1481                 sb.append(getErrorMessage(ar));
   1482             }
   1483 
   1484         } else {
   1485             int[] ints = (int[])ar.result;
   1486 
   1487             if (ints.length != 0) {
   1488                 if (ints[0] == 0) {
   1489                     sb.append(mContext.getText(com.android.internal.R.string.serviceDisabled));
   1490                 } else if (mSc.equals(SC_WAIT)) {
   1491                     // Call Waiting includes additional data in the response.
   1492                     sb.append(createQueryCallWaitingResultMessage(ints[1]));
   1493                 } else if (ints[0] == 1) {
   1494                     // for all other services, treat it as a boolean
   1495                     sb.append(mContext.getText(com.android.internal.R.string.serviceEnabled));
   1496                 } else {
   1497                     sb.append(mContext.getText(com.android.internal.R.string.mmiError));
   1498                 }
   1499             } else {
   1500                 sb.append(mContext.getText(com.android.internal.R.string.mmiError));
   1501             }
   1502             mState = State.COMPLETE;
   1503         }
   1504 
   1505         mMessage = sb;
   1506         mPhone.onMMIDone(this);
   1507     }
   1508 
   1509     private CharSequence
   1510     createQueryCallWaitingResultMessage(int serviceClass) {
   1511         StringBuilder sb = new StringBuilder(
   1512                 mContext.getText(com.android.internal.R.string.serviceEnabledFor));
   1513 
   1514         for (int classMask = 1
   1515                     ; classMask <= SERVICE_CLASS_MAX
   1516                     ; classMask <<= 1
   1517         ) {
   1518             if ((classMask & serviceClass) != 0) {
   1519                 sb.append("\n");
   1520                 sb.append(serviceClassToCFString(classMask & serviceClass));
   1521             }
   1522         }
   1523         return sb;
   1524     }
   1525 
   1526     /***
   1527      * TODO: It would be nice to have a method here that can take in a dialstring and
   1528      * figure out if there is an MMI code embedded within it.  This code would replace
   1529      * some of the string parsing functionality in the Phone App's
   1530      * SpecialCharSequenceMgr class.
   1531      */
   1532 
   1533     @Override
   1534     public String toString() {
   1535         StringBuilder sb = new StringBuilder("ImsPhoneMmiCode {");
   1536 
   1537         sb.append("State=" + getState());
   1538         if (mAction != null) sb.append(" action=" + mAction);
   1539         if (mSc != null) sb.append(" sc=" + mSc);
   1540         if (mSia != null) sb.append(" sia=" + mSia);
   1541         if (mSib != null) sb.append(" sib=" + mSib);
   1542         if (mSic != null) sb.append(" sic=" + mSic);
   1543         if (mPoundString != null) sb.append(" poundString=" + mPoundString);
   1544         if (mDialingNumber != null) sb.append(" dialingNumber=" + mDialingNumber);
   1545         if (mPwd != null) sb.append(" pwd=" + mPwd);
   1546         sb.append("}");
   1547         return sb.toString();
   1548     }
   1549 }
   1550