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