Home | History | Annotate | Download | only in gsm
      1 /*
      2  * Copyright (C) 2006 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.gsm;
     18 
     19 import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA;
     20 import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY;
     21 import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC;
     22 import android.content.Context;
     23 import android.os.AsyncResult;
     24 import android.os.Message;
     25 import android.os.SystemProperties;
     26 import android.util.Log;
     27 
     28 import com.android.internal.telephony.AdnRecord;
     29 import com.android.internal.telephony.AdnRecordCache;
     30 import com.android.internal.telephony.AdnRecordLoader;
     31 import com.android.internal.telephony.BaseCommands;
     32 import com.android.internal.telephony.CommandsInterface;
     33 import com.android.internal.telephony.IccFileHandler;
     34 import com.android.internal.telephony.IccRecords;
     35 import com.android.internal.telephony.IccUtils;
     36 import com.android.internal.telephony.IccVmFixedException;
     37 import com.android.internal.telephony.IccVmNotSupportedException;
     38 import com.android.internal.telephony.MccTable;
     39 import com.android.internal.telephony.Phone;
     40 import com.android.internal.telephony.PhoneBase;
     41 import com.android.internal.telephony.SmsMessageBase;
     42 
     43 import java.util.ArrayList;
     44 
     45 
     46 /**
     47  * {@hide}
     48  */
     49 public class SIMRecords extends IccRecords {
     50     protected static final String LOG_TAG = "GSM";
     51 
     52     private static final boolean CRASH_RIL = false;
     53 
     54     protected static final boolean DBG = true;
     55 
     56     // ***** Instance Variables
     57 
     58     VoiceMailConstants mVmConfig;
     59 
     60 
     61     SpnOverride mSpnOverride;
     62 
     63     // ***** Cached SIM State; cleared on channel close
     64 
     65     private String imsi;
     66     private boolean callForwardingEnabled;
     67 
     68 
     69     /**
     70      * States only used by getSpnFsm FSM
     71      */
     72     private Get_Spn_Fsm_State spnState;
     73 
     74     /** CPHS service information (See CPHS 4.2 B.3.1.1)
     75      *  It will be set in onSimReady if reading GET_CPHS_INFO successfully
     76      *  mCphsInfo[0] is CPHS Phase
     77      *  mCphsInfo[1] and mCphsInfo[2] is CPHS Service Table
     78      */
     79     private byte[] mCphsInfo = null;
     80     boolean mCspPlmnEnabled = true;
     81 
     82     byte[] efMWIS = null;
     83     byte[] efCPHS_MWI =null;
     84     byte[] mEfCff = null;
     85     byte[] mEfCfis = null;
     86 
     87 
     88     int spnDisplayCondition;
     89     // Numeric network codes listed in TS 51.011 EF[SPDI]
     90     ArrayList<String> spdiNetworks = null;
     91 
     92     String pnnHomeName = null;
     93 
     94     // ***** Constants
     95 
     96     // Bitmasks for SPN display rules.
     97     static final int SPN_RULE_SHOW_SPN  = 0x01;
     98     static final int SPN_RULE_SHOW_PLMN = 0x02;
     99 
    100     // From TS 51.011 EF[SPDI] section
    101     static final int TAG_SPDI = 0xA3;
    102     static final int TAG_SPDI_PLMN_LIST = 0x80;
    103 
    104     // Full Name IEI from TS 24.008
    105     static final int TAG_FULL_NETWORK_NAME = 0x43;
    106 
    107     // Short Name IEI from TS 24.008
    108     static final int TAG_SHORT_NETWORK_NAME = 0x45;
    109 
    110     // active CFF from CPHS 4.2 B.4.5
    111     static final int CFF_UNCONDITIONAL_ACTIVE = 0x0a;
    112     static final int CFF_UNCONDITIONAL_DEACTIVE = 0x05;
    113     static final int CFF_LINE1_MASK = 0x0f;
    114     static final int CFF_LINE1_RESET = 0xf0;
    115 
    116     // CPHS Service Table (See CPHS 4.2 B.3.1)
    117     private static final int CPHS_SST_MBN_MASK = 0x30;
    118     private static final int CPHS_SST_MBN_ENABLED = 0x30;
    119 
    120     // ***** Event Constants
    121 
    122     private static final int EVENT_SIM_READY = 1;
    123     private static final int EVENT_RADIO_OFF_OR_NOT_AVAILABLE = 2;
    124     protected static final int EVENT_GET_IMSI_DONE = 3;
    125     protected static final int EVENT_GET_ICCID_DONE = 4;
    126     private static final int EVENT_GET_MBI_DONE = 5;
    127     private static final int EVENT_GET_MBDN_DONE = 6;
    128     private static final int EVENT_GET_MWIS_DONE = 7;
    129     private static final int EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE = 8;
    130     protected static final int EVENT_GET_AD_DONE = 9; // Admin data on SIM
    131     protected static final int EVENT_GET_MSISDN_DONE = 10;
    132     private static final int EVENT_GET_CPHS_MAILBOX_DONE = 11;
    133     private static final int EVENT_GET_SPN_DONE = 12;
    134     private static final int EVENT_GET_SPDI_DONE = 13;
    135     private static final int EVENT_UPDATE_DONE = 14;
    136     private static final int EVENT_GET_PNN_DONE = 15;
    137     private static final int EVENT_GET_SST_DONE = 17;
    138     private static final int EVENT_GET_ALL_SMS_DONE = 18;
    139     private static final int EVENT_MARK_SMS_READ_DONE = 19;
    140     private static final int EVENT_SET_MBDN_DONE = 20;
    141     private static final int EVENT_SMS_ON_SIM = 21;
    142     private static final int EVENT_GET_SMS_DONE = 22;
    143     private static final int EVENT_GET_CFF_DONE = 24;
    144     private static final int EVENT_SET_CPHS_MAILBOX_DONE = 25;
    145     private static final int EVENT_GET_INFO_CPHS_DONE = 26;
    146     private static final int EVENT_SET_MSISDN_DONE = 30;
    147     private static final int EVENT_SIM_REFRESH = 31;
    148     private static final int EVENT_GET_CFIS_DONE = 32;
    149     private static final int EVENT_GET_CSP_CPHS_DONE = 33;
    150 
    151     // Lookup table for carriers known to produce SIMs which incorrectly indicate MNC length.
    152 
    153     private static final String[] MCCMNC_CODES_HAVING_3DIGITS_MNC = {
    154         "405025", "405026", "405027", "405028", "405029", "405030", "405031", "405032",
    155         "405033", "405034", "405035", "405036", "405037", "405038", "405039", "405040",
    156         "405041", "405042", "405043", "405044", "405045", "405046", "405047", "405750",
    157         "405751", "405752", "405753", "405754", "405755", "405756", "405799", "405800",
    158         "405801", "405802", "405803", "405804", "405805", "405806", "405807", "405808",
    159         "405809", "405810", "405811", "405812", "405813", "405814", "405815", "405816",
    160         "405817", "405818", "405819", "405820", "405821", "405822", "405823", "405824",
    161         "405825", "405826", "405827", "405828", "405829", "405830", "405831", "405832",
    162         "405833", "405834", "405835", "405836", "405837", "405838", "405839", "405840",
    163         "405841", "405842", "405843", "405844", "405845", "405846", "405847", "405848",
    164         "405849", "405850", "405851", "405852", "405853", "405875", "405876", "405877",
    165         "405878", "405879", "405880", "405881", "405882", "405883", "405884", "405885",
    166         "405886", "405908", "405909", "405910", "405911", "405925", "405926", "405927",
    167         "405928", "405929", "405932"
    168     };
    169 
    170     // ***** Constructor
    171 
    172     public SIMRecords(PhoneBase p) {
    173         super(p);
    174 
    175         adnCache = new AdnRecordCache(phone);
    176 
    177         mVmConfig = new VoiceMailConstants();
    178         mSpnOverride = new SpnOverride();
    179 
    180         recordsRequested = false;  // No load request is made till SIM ready
    181 
    182         // recordsToLoad is set to 0 because no requests are made yet
    183         recordsToLoad = 0;
    184 
    185         p.mCM.registerForSIMReady(this, EVENT_SIM_READY, null);
    186         p.mCM.registerForOffOrNotAvailable(
    187                         this, EVENT_RADIO_OFF_OR_NOT_AVAILABLE, null);
    188         p.mCM.setOnSmsOnSim(this, EVENT_SMS_ON_SIM, null);
    189         p.mCM.registerForIccRefresh(this, EVENT_SIM_REFRESH, null);
    190 
    191         // Start off by setting empty state
    192         onRadioOffOrNotAvailable();
    193 
    194     }
    195 
    196     @Override
    197     public void dispose() {
    198         //Unregister for all events
    199         phone.mCM.unregisterForSIMReady(this);
    200         phone.mCM.unregisterForOffOrNotAvailable( this);
    201         phone.mCM.unregisterForIccRefresh(this);
    202     }
    203 
    204     protected void finalize() {
    205         if(DBG) Log.d(LOG_TAG, "SIMRecords finalized");
    206     }
    207 
    208     protected void onRadioOffOrNotAvailable() {
    209         imsi = null;
    210         msisdn = null;
    211         voiceMailNum = null;
    212         countVoiceMessages = 0;
    213         mncLength = UNINITIALIZED;
    214         iccid = null;
    215         // -1 means no EF_SPN found; treat accordingly.
    216         spnDisplayCondition = -1;
    217         efMWIS = null;
    218         efCPHS_MWI = null;
    219         spdiNetworks = null;
    220         pnnHomeName = null;
    221 
    222         adnCache.reset();
    223 
    224         phone.setSystemProperty(PROPERTY_ICC_OPERATOR_NUMERIC, null);
    225         phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, null);
    226         phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ISO_COUNTRY, null);
    227 
    228         // recordsRequested is set to false indicating that the SIM
    229         // read requests made so far are not valid. This is set to
    230         // true only when fresh set of read requests are made.
    231         recordsRequested = false;
    232     }
    233 
    234 
    235     //***** Public Methods
    236 
    237     /**
    238      * {@inheritDoc}
    239      */
    240     @Override
    241     public String getIMSI() {
    242         return imsi;
    243     }
    244 
    245     public String getMsisdnNumber() {
    246         return msisdn;
    247     }
    248 
    249     /**
    250      * Set subscriber number to SIM record
    251      *
    252      * The subscriber number is stored in EF_MSISDN (TS 51.011)
    253      *
    254      * When the operation is complete, onComplete will be sent to its handler
    255      *
    256      * @param alphaTag alpha-tagging of the dailing nubmer (up to 10 characters)
    257      * @param number dailing nubmer (up to 20 digits)
    258      *        if the number starts with '+', then set to international TOA
    259      * @param onComplete
    260      *        onComplete.obj will be an AsyncResult
    261      *        ((AsyncResult)onComplete.obj).exception == null on success
    262      *        ((AsyncResult)onComplete.obj).exception != null on fail
    263      */
    264     public void setMsisdnNumber(String alphaTag, String number,
    265             Message onComplete) {
    266 
    267         msisdn = number;
    268         msisdnTag = alphaTag;
    269 
    270         if(DBG) log("Set MSISDN: " + msisdnTag + " " + /*msisdn*/ "xxxxxxx");
    271 
    272 
    273         AdnRecord adn = new AdnRecord(msisdnTag, msisdn);
    274 
    275         new AdnRecordLoader(phone).updateEF(adn, EF_MSISDN, EF_EXT1, 1, null,
    276                 obtainMessage(EVENT_SET_MSISDN_DONE, onComplete));
    277     }
    278 
    279     public String getMsisdnAlphaTag() {
    280         return msisdnTag;
    281     }
    282 
    283     public String getVoiceMailNumber() {
    284         return voiceMailNum;
    285     }
    286 
    287     /**
    288      * Set voice mail number to SIM record
    289      *
    290      * The voice mail number can be stored either in EF_MBDN (TS 51.011) or
    291      * EF_MAILBOX_CPHS (CPHS 4.2)
    292      *
    293      * If EF_MBDN is available, store the voice mail number to EF_MBDN
    294      *
    295      * If EF_MAILBOX_CPHS is enabled, store the voice mail number to EF_CHPS
    296      *
    297      * So the voice mail number will be stored in both EFs if both are available
    298      *
    299      * Return error only if both EF_MBDN and EF_MAILBOX_CPHS fail.
    300      *
    301      * When the operation is complete, onComplete will be sent to its handler
    302      *
    303      * @param alphaTag alpha-tagging of the dailing nubmer (upto 10 characters)
    304      * @param voiceNumber dailing nubmer (upto 20 digits)
    305      *        if the number is start with '+', then set to international TOA
    306      * @param onComplete
    307      *        onComplete.obj will be an AsyncResult
    308      *        ((AsyncResult)onComplete.obj).exception == null on success
    309      *        ((AsyncResult)onComplete.obj).exception != null on fail
    310      */
    311     public void setVoiceMailNumber(String alphaTag, String voiceNumber,
    312             Message onComplete) {
    313         if (isVoiceMailFixed) {
    314             AsyncResult.forMessage((onComplete)).exception =
    315                     new IccVmFixedException("Voicemail number is fixed by operator");
    316             onComplete.sendToTarget();
    317             return;
    318         }
    319 
    320         newVoiceMailNum = voiceNumber;
    321         newVoiceMailTag = alphaTag;
    322 
    323         AdnRecord adn = new AdnRecord(newVoiceMailTag, newVoiceMailNum);
    324 
    325         if (mailboxIndex != 0 && mailboxIndex != 0xff) {
    326 
    327             new AdnRecordLoader(phone).updateEF(adn, EF_MBDN, EF_EXT6,
    328                     mailboxIndex, null,
    329                     obtainMessage(EVENT_SET_MBDN_DONE, onComplete));
    330 
    331         } else if (isCphsMailboxEnabled()) {
    332 
    333             new AdnRecordLoader(phone).updateEF(adn, EF_MAILBOX_CPHS,
    334                     EF_EXT1, 1, null,
    335                     obtainMessage(EVENT_SET_CPHS_MAILBOX_DONE, onComplete));
    336 
    337         } else {
    338             AsyncResult.forMessage((onComplete)).exception =
    339                     new IccVmNotSupportedException("Update SIM voice mailbox error");
    340             onComplete.sendToTarget();
    341         }
    342     }
    343 
    344     public String getVoiceMailAlphaTag()
    345     {
    346         return voiceMailTag;
    347     }
    348 
    349     /**
    350      * Sets the SIM voice message waiting indicator records
    351      * @param line GSM Subscriber Profile Number, one-based. Only '1' is supported
    352      * @param countWaiting The number of messages waiting, if known. Use
    353      *                     -1 to indicate that an unknown number of
    354      *                      messages are waiting
    355      */
    356     public void
    357     setVoiceMessageWaiting(int line, int countWaiting) {
    358         if (line != 1) {
    359             // only profile 1 is supported
    360             return;
    361         }
    362 
    363         // range check
    364         if (countWaiting < 0) {
    365             countWaiting = -1;
    366         } else if (countWaiting > 0xff) {
    367             // TS 23.040 9.2.3.24.2
    368             // "The value 255 shall be taken to mean 255 or greater"
    369             countWaiting = 0xff;
    370         }
    371 
    372         countVoiceMessages = countWaiting;
    373 
    374         phone.notifyMessageWaitingIndicator();
    375 
    376         try {
    377             if (efMWIS != null) {
    378                 // TS 51.011 10.3.45
    379 
    380                 // lsb of byte 0 is 'voicemail' status
    381                 efMWIS[0] = (byte)((efMWIS[0] & 0xfe)
    382                                     | (countVoiceMessages == 0 ? 0 : 1));
    383 
    384                 // byte 1 is the number of voice messages waiting
    385                 if (countWaiting < 0) {
    386                     // The spec does not define what this should be
    387                     // if we don't know the count
    388                     efMWIS[1] = 0;
    389                 } else {
    390                     efMWIS[1] = (byte) countWaiting;
    391                 }
    392 
    393                 phone.getIccFileHandler().updateEFLinearFixed(
    394                     EF_MWIS, 1, efMWIS, null,
    395                     obtainMessage (EVENT_UPDATE_DONE, EF_MWIS));
    396             }
    397 
    398             if (efCPHS_MWI != null) {
    399                     // Refer CPHS4_2.WW6 B4.2.3
    400                 efCPHS_MWI[0] = (byte)((efCPHS_MWI[0] & 0xf0)
    401                             | (countVoiceMessages == 0 ? 0x5 : 0xa));
    402 
    403                 phone.getIccFileHandler().updateEFTransparent(
    404                     EF_VOICE_MAIL_INDICATOR_CPHS, efCPHS_MWI,
    405                     obtainMessage (EVENT_UPDATE_DONE, EF_VOICE_MAIL_INDICATOR_CPHS));
    406             }
    407         } catch (ArrayIndexOutOfBoundsException ex) {
    408             Log.w(LOG_TAG,
    409                 "Error saving voice mail state to SIM. Probably malformed SIM record", ex);
    410         }
    411     }
    412 
    413     /**
    414      * {@inheritDoc}
    415      */
    416     @Override
    417     public boolean getVoiceCallForwardingFlag() {
    418         return callForwardingEnabled;
    419     }
    420 
    421     /**
    422      * {@inheritDoc}
    423      */
    424     @Override
    425     public void setVoiceCallForwardingFlag(int line, boolean enable) {
    426 
    427         if (line != 1) return; // only line 1 is supported
    428 
    429         callForwardingEnabled = enable;
    430 
    431         phone.notifyCallForwardingIndicator();
    432 
    433         try {
    434             if (mEfCfis != null) {
    435                 // lsb is of byte 1 is voice status
    436                 if (enable) {
    437                     mEfCfis[1] |= 1;
    438                 } else {
    439                     mEfCfis[1] &= 0xfe;
    440                 }
    441 
    442                 // TODO: Should really update other fields in EF_CFIS, eg,
    443                 // dialing number.  We don't read or use it right now.
    444 
    445                 phone.getIccFileHandler().updateEFLinearFixed(
    446                         EF_CFIS, 1, mEfCfis, null,
    447                         obtainMessage (EVENT_UPDATE_DONE, EF_CFIS));
    448             }
    449 
    450             if (mEfCff != null) {
    451                 if (enable) {
    452                     mEfCff[0] = (byte) ((mEfCff[0] & CFF_LINE1_RESET)
    453                             | CFF_UNCONDITIONAL_ACTIVE);
    454                 } else {
    455                     mEfCff[0] = (byte) ((mEfCff[0] & CFF_LINE1_RESET)
    456                             | CFF_UNCONDITIONAL_DEACTIVE);
    457                 }
    458 
    459                 phone.getIccFileHandler().updateEFTransparent(
    460                         EF_CFF_CPHS, mEfCff,
    461                         obtainMessage (EVENT_UPDATE_DONE, EF_CFF_CPHS));
    462             }
    463         } catch (ArrayIndexOutOfBoundsException ex) {
    464             Log.w(LOG_TAG,
    465                     "Error saving call fowarding flag to SIM. "
    466                             + "Probably malformed SIM record", ex);
    467 
    468         }
    469     }
    470 
    471     /**
    472      * Called by STK Service when REFRESH is received.
    473      * @param fileChanged indicates whether any files changed
    474      * @param fileList if non-null, a list of EF files that changed
    475      */
    476     public void onRefresh(boolean fileChanged, int[] fileList) {
    477         if (fileChanged) {
    478             // A future optimization would be to inspect fileList and
    479             // only reload those files that we care about.  For now,
    480             // just re-fetch all SIM records that we cache.
    481             fetchSimRecords();
    482         }
    483     }
    484 
    485     /**
    486      * {@inheritDoc}
    487      */
    488     @Override
    489     public String getOperatorNumeric() {
    490         if (imsi == null) {
    491             Log.d(LOG_TAG, "getOperatorNumeric: IMSI == null");
    492             return null;
    493         }
    494         if (mncLength == UNINITIALIZED || mncLength == UNKNOWN) {
    495             Log.d(LOG_TAG, "getSIMOperatorNumeric: bad mncLength");
    496             return null;
    497         }
    498 
    499         // Length = length of MCC + length of MNC
    500         // length of mcc = 3 (TS 23.003 Section 2.2)
    501         return imsi.substring(0, 3 + mncLength);
    502     }
    503 
    504     // ***** Overridden from Handler
    505     public void handleMessage(Message msg) {
    506         AsyncResult ar;
    507         AdnRecord adn;
    508 
    509         byte data[];
    510 
    511         boolean isRecordLoadResponse = false;
    512 
    513         try { switch (msg.what) {
    514             case EVENT_SIM_READY:
    515                 onSimReady();
    516             break;
    517 
    518             case EVENT_RADIO_OFF_OR_NOT_AVAILABLE:
    519                 onRadioOffOrNotAvailable();
    520             break;
    521 
    522             /* IO events */
    523             case EVENT_GET_IMSI_DONE:
    524                 isRecordLoadResponse = true;
    525 
    526                 ar = (AsyncResult)msg.obj;
    527 
    528                 if (ar.exception != null) {
    529                     Log.e(LOG_TAG, "Exception querying IMSI, Exception:" + ar.exception);
    530                     break;
    531                 }
    532 
    533                 imsi = (String) ar.result;
    534 
    535                 // IMSI (MCC+MNC+MSIN) is at least 6 digits, but not more
    536                 // than 15 (and usually 15).
    537                 if (imsi != null && (imsi.length() < 6 || imsi.length() > 15)) {
    538                     Log.e(LOG_TAG, "invalid IMSI " + imsi);
    539                     imsi = null;
    540                 }
    541 
    542                 Log.d(LOG_TAG, "IMSI: " + /* imsi.substring(0, 6) +*/ "xxxxxxx");
    543 
    544                 if (((mncLength == UNKNOWN) || (mncLength == 2)) &&
    545                         ((imsi != null) && (imsi.length() >= 6))) {
    546                     String mccmncCode = imsi.substring(0, 6);
    547                     for (String mccmnc : MCCMNC_CODES_HAVING_3DIGITS_MNC) {
    548                         if (mccmnc.equals(mccmncCode)) {
    549                             mncLength = 3;
    550                             break;
    551                         }
    552                     }
    553                 }
    554 
    555                 if (mncLength == UNKNOWN) {
    556                     // the SIM has told us all it knows, but it didn't know the mnc length.
    557                     // guess using the mcc
    558                     try {
    559                         int mcc = Integer.parseInt(imsi.substring(0,3));
    560                         mncLength = MccTable.smallestDigitsMccForMnc(mcc);
    561                     } catch (NumberFormatException e) {
    562                         mncLength = UNKNOWN;
    563                         Log.e(LOG_TAG, "SIMRecords: Corrupt IMSI!");
    564                     }
    565                 }
    566 
    567                 if (mncLength != UNKNOWN && mncLength != UNINITIALIZED) {
    568                     // finally have both the imsi and the mncLength and can parse the imsi properly
    569                     MccTable.updateMccMncConfiguration(phone, imsi.substring(0, 3 + mncLength));
    570                 }
    571                 phone.mIccCard.broadcastIccStateChangedIntent(
    572                         SimCard.INTENT_VALUE_ICC_IMSI, null);
    573             break;
    574 
    575             case EVENT_GET_MBI_DONE:
    576                 boolean isValidMbdn;
    577                 isRecordLoadResponse = true;
    578 
    579                 ar = (AsyncResult)msg.obj;
    580                 data = (byte[]) ar.result;
    581 
    582                 isValidMbdn = false;
    583                 if (ar.exception == null) {
    584                     // Refer TS 51.011 Section 10.3.44 for content details
    585                     Log.d(LOG_TAG, "EF_MBI: " +
    586                             IccUtils.bytesToHexString(data));
    587 
    588                     // Voice mail record number stored first
    589                     mailboxIndex = (int)data[0] & 0xff;
    590 
    591                     // check if dailing numbe id valid
    592                     if (mailboxIndex != 0 && mailboxIndex != 0xff) {
    593                         Log.d(LOG_TAG, "Got valid mailbox number for MBDN");
    594                         isValidMbdn = true;
    595                     }
    596                 }
    597 
    598                 // one more record to load
    599                 recordsToLoad += 1;
    600 
    601                 if (isValidMbdn) {
    602                     // Note: MBDN was not included in NUM_OF_SIM_RECORDS_LOADED
    603                     new AdnRecordLoader(phone).loadFromEF(EF_MBDN, EF_EXT6,
    604                             mailboxIndex, obtainMessage(EVENT_GET_MBDN_DONE));
    605                 } else {
    606                     // If this EF not present, try mailbox as in CPHS standard
    607                     // CPHS (CPHS4_2.WW6) is a european standard.
    608                     new AdnRecordLoader(phone).loadFromEF(EF_MAILBOX_CPHS,
    609                             EF_EXT1, 1,
    610                             obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE));
    611                 }
    612 
    613                 break;
    614             case EVENT_GET_CPHS_MAILBOX_DONE:
    615             case EVENT_GET_MBDN_DONE:
    616                 //Resetting the voice mail number and voice mail tag to null
    617                 //as these should be updated from the data read from EF_MBDN.
    618                 //If they are not reset, incase of invalid data/exception these
    619                 //variables are retaining their previous values and are
    620                 //causing invalid voice mailbox info display to user.
    621                 voiceMailNum = null;
    622                 voiceMailTag = null;
    623                 isRecordLoadResponse = true;
    624 
    625                 ar = (AsyncResult)msg.obj;
    626 
    627                 if (ar.exception != null) {
    628 
    629                     Log.d(LOG_TAG, "Invalid or missing EF"
    630                         + ((msg.what == EVENT_GET_CPHS_MAILBOX_DONE) ? "[MAILBOX]" : "[MBDN]"));
    631 
    632                     // Bug #645770 fall back to CPHS
    633                     // FIXME should use SST to decide
    634 
    635                     if (msg.what == EVENT_GET_MBDN_DONE) {
    636                         //load CPHS on fail...
    637                         // FIXME right now, only load line1's CPHS voice mail entry
    638 
    639                         recordsToLoad += 1;
    640                         new AdnRecordLoader(phone).loadFromEF(
    641                                 EF_MAILBOX_CPHS, EF_EXT1, 1,
    642                                 obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE));
    643                     }
    644                     break;
    645                 }
    646 
    647                 adn = (AdnRecord)ar.result;
    648 
    649                 Log.d(LOG_TAG, "VM: " + adn +
    650                         ((msg.what == EVENT_GET_CPHS_MAILBOX_DONE) ? " EF[MAILBOX]" : " EF[MBDN]"));
    651 
    652                 if (adn.isEmpty() && msg.what == EVENT_GET_MBDN_DONE) {
    653                     // Bug #645770 fall back to CPHS
    654                     // FIXME should use SST to decide
    655                     // FIXME right now, only load line1's CPHS voice mail entry
    656                     recordsToLoad += 1;
    657                     new AdnRecordLoader(phone).loadFromEF(
    658                             EF_MAILBOX_CPHS, EF_EXT1, 1,
    659                             obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE));
    660 
    661                     break;
    662                 }
    663 
    664                 voiceMailNum = adn.getNumber();
    665                 voiceMailTag = adn.getAlphaTag();
    666             break;
    667 
    668             case EVENT_GET_MSISDN_DONE:
    669                 isRecordLoadResponse = true;
    670 
    671                 ar = (AsyncResult)msg.obj;
    672 
    673                 if (ar.exception != null) {
    674                     Log.d(LOG_TAG, "Invalid or missing EF[MSISDN]");
    675                     break;
    676                 }
    677 
    678                 adn = (AdnRecord)ar.result;
    679 
    680                 msisdn = adn.getNumber();
    681                 msisdnTag = adn.getAlphaTag();
    682 
    683                 Log.d(LOG_TAG, "MSISDN: " + /*msisdn*/ "xxxxxxx");
    684             break;
    685 
    686             case EVENT_SET_MSISDN_DONE:
    687                 isRecordLoadResponse = false;
    688                 ar = (AsyncResult)msg.obj;
    689 
    690                 if (ar.userObj != null) {
    691                     AsyncResult.forMessage(((Message) ar.userObj)).exception
    692                             = ar.exception;
    693                     ((Message) ar.userObj).sendToTarget();
    694                 }
    695                 break;
    696 
    697             case EVENT_GET_MWIS_DONE:
    698                 isRecordLoadResponse = true;
    699 
    700                 ar = (AsyncResult)msg.obj;
    701                 data = (byte[])ar.result;
    702 
    703                 if (ar.exception != null) {
    704                     break;
    705                 }
    706 
    707                 Log.d(LOG_TAG, "EF_MWIS: " +
    708                    IccUtils.bytesToHexString(data));
    709 
    710                 efMWIS = data;
    711 
    712                 if ((data[0] & 0xff) == 0xff) {
    713                     Log.d(LOG_TAG, "SIMRecords: Uninitialized record MWIS");
    714                     break;
    715                 }
    716 
    717                 // Refer TS 51.011 Section 10.3.45 for the content description
    718                 boolean voiceMailWaiting = ((data[0] & 0x01) != 0);
    719                 countVoiceMessages = data[1] & 0xff;
    720 
    721                 if (voiceMailWaiting && countVoiceMessages == 0) {
    722                     // Unknown count = -1
    723                     countVoiceMessages = -1;
    724                 }
    725 
    726                 phone.notifyMessageWaitingIndicator();
    727             break;
    728 
    729             case EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE:
    730                 isRecordLoadResponse = true;
    731 
    732                 ar = (AsyncResult)msg.obj;
    733                 data = (byte[])ar.result;
    734 
    735                 if (ar.exception != null) {
    736                     break;
    737                 }
    738 
    739                 efCPHS_MWI = data;
    740 
    741                 // Use this data if the EF[MWIS] exists and
    742                 // has been loaded
    743 
    744                 if (efMWIS == null) {
    745                     int indicator = (int)(data[0] & 0xf);
    746 
    747                     // Refer CPHS4_2.WW6 B4.2.3
    748                     if (indicator == 0xA) {
    749                         // Unknown count = -1
    750                         countVoiceMessages = -1;
    751                     } else if (indicator == 0x5) {
    752                         countVoiceMessages = 0;
    753                     }
    754 
    755                     phone.notifyMessageWaitingIndicator();
    756                 }
    757             break;
    758 
    759             case EVENT_GET_ICCID_DONE:
    760                 isRecordLoadResponse = true;
    761 
    762                 ar = (AsyncResult)msg.obj;
    763                 data = (byte[])ar.result;
    764 
    765                 if (ar.exception != null) {
    766                     break;
    767                 }
    768 
    769                 iccid = IccUtils.bcdToString(data, 0, data.length);
    770 
    771                 Log.d(LOG_TAG, "iccid: " + iccid);
    772 
    773             break;
    774 
    775 
    776             case EVENT_GET_AD_DONE:
    777                 try {
    778                     isRecordLoadResponse = true;
    779 
    780                     ar = (AsyncResult)msg.obj;
    781                     data = (byte[])ar.result;
    782 
    783                     if (ar.exception != null) {
    784                         break;
    785                     }
    786 
    787                     Log.d(LOG_TAG, "EF_AD: " +
    788                             IccUtils.bytesToHexString(data));
    789 
    790                     if (data.length < 3) {
    791                         Log.d(LOG_TAG, "SIMRecords: Corrupt AD data on SIM");
    792                         break;
    793                     }
    794 
    795                     if (data.length == 3) {
    796                         Log.d(LOG_TAG, "SIMRecords: MNC length not present in EF_AD");
    797                         break;
    798                     }
    799 
    800                     mncLength = (int)data[3] & 0xf;
    801 
    802                     if (mncLength == 0xf) {
    803                         mncLength = UNKNOWN;
    804                     }
    805                 } finally {
    806                     if (((mncLength == UNINITIALIZED) || (mncLength == UNKNOWN) ||
    807                             (mncLength == 2)) && ((imsi != null) && (imsi.length() >= 6))) {
    808                         String mccmncCode = imsi.substring(0, 6);
    809                         for (String mccmnc : MCCMNC_CODES_HAVING_3DIGITS_MNC) {
    810                             if (mccmnc.equals(mccmncCode)) {
    811                                 mncLength = 3;
    812                                 break;
    813                             }
    814                         }
    815                     }
    816 
    817                     if (mncLength == UNKNOWN || mncLength == UNINITIALIZED) {
    818                         if (imsi != null) {
    819                             try {
    820                                 int mcc = Integer.parseInt(imsi.substring(0,3));
    821 
    822                                 mncLength = MccTable.smallestDigitsMccForMnc(mcc);
    823                             } catch (NumberFormatException e) {
    824                                 mncLength = UNKNOWN;
    825                                 Log.e(LOG_TAG, "SIMRecords: Corrupt IMSI!");
    826                             }
    827                         } else {
    828                             // Indicate we got this info, but it didn't contain the length.
    829                             mncLength = UNKNOWN;
    830 
    831                             Log.d(LOG_TAG, "SIMRecords: MNC length not present in EF_AD");
    832                         }
    833                     }
    834                     if (imsi != null && mncLength != UNKNOWN) {
    835                         // finally have both imsi and the length of the mnc and can parse
    836                         // the imsi properly
    837                         MccTable.updateMccMncConfiguration(phone, imsi.substring(0, 3 + mncLength));
    838                     }
    839                 }
    840             break;
    841 
    842             case EVENT_GET_SPN_DONE:
    843                 isRecordLoadResponse = true;
    844                 ar = (AsyncResult) msg.obj;
    845                 getSpnFsm(false, ar);
    846             break;
    847 
    848             case EVENT_GET_CFF_DONE:
    849                 isRecordLoadResponse = true;
    850 
    851                 ar = (AsyncResult) msg.obj;
    852                 data = (byte[]) ar.result;
    853 
    854                 if (ar.exception != null) {
    855                     break;
    856                 }
    857 
    858                 Log.d(LOG_TAG, "EF_CFF_CPHS: " +
    859                         IccUtils.bytesToHexString(data));
    860                 mEfCff = data;
    861 
    862                 if (mEfCfis == null) {
    863                     callForwardingEnabled =
    864                         ((data[0] & CFF_LINE1_MASK) == CFF_UNCONDITIONAL_ACTIVE);
    865 
    866                     phone.notifyCallForwardingIndicator();
    867                 }
    868                 break;
    869 
    870             case EVENT_GET_SPDI_DONE:
    871                 isRecordLoadResponse = true;
    872 
    873                 ar = (AsyncResult)msg.obj;
    874                 data = (byte[])ar.result;
    875 
    876                 if (ar.exception != null) {
    877                     break;
    878                 }
    879 
    880                 parseEfSpdi(data);
    881             break;
    882 
    883             case EVENT_UPDATE_DONE:
    884                 ar = (AsyncResult)msg.obj;
    885                 if (ar.exception != null) {
    886                     Log.i(LOG_TAG, "SIMRecords update failed", ar.exception);
    887                 }
    888             break;
    889 
    890             case EVENT_GET_PNN_DONE:
    891                 isRecordLoadResponse = true;
    892 
    893                 ar = (AsyncResult)msg.obj;
    894                 data = (byte[])ar.result;
    895 
    896                 if (ar.exception != null) {
    897                     break;
    898                 }
    899 
    900                 SimTlv tlv = new SimTlv(data, 0, data.length);
    901 
    902                 for ( ; tlv.isValidObject() ; tlv.nextObject()) {
    903                     if (tlv.getTag() == TAG_FULL_NETWORK_NAME) {
    904                         pnnHomeName
    905                             = IccUtils.networkNameToString(
    906                                 tlv.getData(), 0, tlv.getData().length);
    907                         break;
    908                     }
    909                 }
    910             break;
    911 
    912             case EVENT_GET_ALL_SMS_DONE:
    913                 isRecordLoadResponse = true;
    914 
    915                 ar = (AsyncResult)msg.obj;
    916                 if (ar.exception != null)
    917                     break;
    918 
    919                 handleSmses((ArrayList) ar.result);
    920                 break;
    921 
    922             case EVENT_MARK_SMS_READ_DONE:
    923                 Log.i("ENF", "marked read: sms " + msg.arg1);
    924                 break;
    925 
    926 
    927             case EVENT_SMS_ON_SIM:
    928                 isRecordLoadResponse = false;
    929 
    930                 ar = (AsyncResult)msg.obj;
    931 
    932                 int[] index = (int[])ar.result;
    933 
    934                 if (ar.exception != null || index.length != 1) {
    935                     Log.e(LOG_TAG, "[SIMRecords] Error on SMS_ON_SIM with exp "
    936                             + ar.exception + " length " + index.length);
    937                 } else {
    938                     Log.d(LOG_TAG, "READ EF_SMS RECORD index=" + index[0]);
    939                     phone.getIccFileHandler().loadEFLinearFixed(EF_SMS,index[0],
    940                             obtainMessage(EVENT_GET_SMS_DONE));
    941                 }
    942                 break;
    943 
    944             case EVENT_GET_SMS_DONE:
    945                 isRecordLoadResponse = false;
    946                 ar = (AsyncResult)msg.obj;
    947                 if (ar.exception == null) {
    948                     handleSms((byte[])ar.result);
    949                 } else {
    950                     Log.e(LOG_TAG, "[SIMRecords] Error on GET_SMS with exp "
    951                             + ar.exception);
    952                 }
    953                 break;
    954             case EVENT_GET_SST_DONE:
    955                 isRecordLoadResponse = true;
    956 
    957                 ar = (AsyncResult)msg.obj;
    958                 data = (byte[])ar.result;
    959 
    960                 if (ar.exception != null) {
    961                     break;
    962                 }
    963 
    964                 //Log.d(LOG_TAG, "SST: " + IccUtils.bytesToHexString(data));
    965             break;
    966 
    967             case EVENT_GET_INFO_CPHS_DONE:
    968                 isRecordLoadResponse = true;
    969 
    970                 ar = (AsyncResult)msg.obj;
    971 
    972                 if (ar.exception != null) {
    973                     break;
    974                 }
    975 
    976                 mCphsInfo = (byte[])ar.result;
    977 
    978                 if (DBG) log("iCPHS: " + IccUtils.bytesToHexString(mCphsInfo));
    979             break;
    980 
    981             case EVENT_SET_MBDN_DONE:
    982                 isRecordLoadResponse = false;
    983                 ar = (AsyncResult)msg.obj;
    984 
    985                 if (ar.exception == null) {
    986                     voiceMailNum = newVoiceMailNum;
    987                     voiceMailTag = newVoiceMailTag;
    988                 }
    989 
    990                 if (isCphsMailboxEnabled()) {
    991                     adn = new AdnRecord(voiceMailTag, voiceMailNum);
    992                     Message onCphsCompleted = (Message) ar.userObj;
    993 
    994                     /* write to cphs mailbox whenever it is available but
    995                     * we only need notify caller once if both updating are
    996                     * successful.
    997                     *
    998                     * so if set_mbdn successful, notify caller here and set
    999                     * onCphsCompleted to null
   1000                     */
   1001                     if (ar.exception == null && ar.userObj != null) {
   1002                         AsyncResult.forMessage(((Message) ar.userObj)).exception
   1003                                 = null;
   1004                         ((Message) ar.userObj).sendToTarget();
   1005 
   1006                         if (DBG) log("Callback with MBDN successful.");
   1007 
   1008                         onCphsCompleted = null;
   1009                     }
   1010 
   1011                     new AdnRecordLoader(phone).
   1012                             updateEF(adn, EF_MAILBOX_CPHS, EF_EXT1, 1, null,
   1013                             obtainMessage(EVENT_SET_CPHS_MAILBOX_DONE,
   1014                                     onCphsCompleted));
   1015                 } else {
   1016                     if (ar.userObj != null) {
   1017                         AsyncResult.forMessage(((Message) ar.userObj)).exception
   1018                                 = ar.exception;
   1019                         ((Message) ar.userObj).sendToTarget();
   1020                     }
   1021                 }
   1022                 break;
   1023             case EVENT_SET_CPHS_MAILBOX_DONE:
   1024                 isRecordLoadResponse = false;
   1025                 ar = (AsyncResult)msg.obj;
   1026                 if(ar.exception == null) {
   1027                     voiceMailNum = newVoiceMailNum;
   1028                     voiceMailTag = newVoiceMailTag;
   1029                 } else {
   1030                     if (DBG) log("Set CPHS MailBox with exception: "
   1031                             + ar.exception);
   1032                 }
   1033                 if (ar.userObj != null) {
   1034                     if (DBG) log("Callback with CPHS MB successful.");
   1035                     AsyncResult.forMessage(((Message) ar.userObj)).exception
   1036                             = ar.exception;
   1037                     ((Message) ar.userObj).sendToTarget();
   1038                 }
   1039                 break;
   1040             case EVENT_SIM_REFRESH:
   1041                 isRecordLoadResponse = false;
   1042                 ar = (AsyncResult)msg.obj;
   1043 		if (DBG) log("Sim REFRESH with exception: " + ar.exception);
   1044                 if (ar.exception == null) {
   1045                     handleSimRefresh((int[])(ar.result));
   1046                 }
   1047                 break;
   1048             case EVENT_GET_CFIS_DONE:
   1049                 isRecordLoadResponse = true;
   1050 
   1051                 ar = (AsyncResult)msg.obj;
   1052                 data = (byte[])ar.result;
   1053 
   1054                 if (ar.exception != null) {
   1055                     break;
   1056                 }
   1057 
   1058                 Log.d(LOG_TAG, "EF_CFIS: " +
   1059                    IccUtils.bytesToHexString(data));
   1060 
   1061                 mEfCfis = data;
   1062 
   1063                 // Refer TS 51.011 Section 10.3.46 for the content description
   1064                 callForwardingEnabled = ((data[1] & 0x01) != 0);
   1065 
   1066                 phone.notifyCallForwardingIndicator();
   1067                 break;
   1068 
   1069             case EVENT_GET_CSP_CPHS_DONE:
   1070                 isRecordLoadResponse = true;
   1071 
   1072                 ar = (AsyncResult)msg.obj;
   1073 
   1074                 if (ar.exception != null) {
   1075                     Log.e(LOG_TAG,"Exception in fetching EF_CSP data " + ar.exception);
   1076                     break;
   1077                 }
   1078 
   1079                 data = (byte[])ar.result;
   1080 
   1081                 Log.i(LOG_TAG,"EF_CSP: " + IccUtils.bytesToHexString(data));
   1082                 handleEfCspData(data);
   1083                 break;
   1084 
   1085             default:
   1086                 super.handleMessage(msg);   // IccRecords handles generic record load responses
   1087 
   1088         }}catch (RuntimeException exc) {
   1089             // I don't want these exceptions to be fatal
   1090             Log.w(LOG_TAG, "Exception parsing SIM record", exc);
   1091         } finally {
   1092             // Count up record load responses even if they are fails
   1093             if (isRecordLoadResponse) {
   1094                 onRecordLoaded();
   1095             }
   1096         }
   1097     }
   1098 
   1099     private void handleFileUpdate(int efid) {
   1100         switch(efid) {
   1101             case EF_MBDN:
   1102                 recordsToLoad++;
   1103                 new AdnRecordLoader(phone).loadFromEF(EF_MBDN, EF_EXT6,
   1104                         mailboxIndex, obtainMessage(EVENT_GET_MBDN_DONE));
   1105                 break;
   1106             case EF_MAILBOX_CPHS:
   1107                 recordsToLoad++;
   1108                 new AdnRecordLoader(phone).loadFromEF(EF_MAILBOX_CPHS, EF_EXT1,
   1109                         1, obtainMessage(EVENT_GET_CPHS_MAILBOX_DONE));
   1110                 break;
   1111             case EF_CSP_CPHS:
   1112                 recordsToLoad++;
   1113                 Log.i(LOG_TAG, "[CSP] SIM Refresh for EF_CSP_CPHS");
   1114                 phone.getIccFileHandler().loadEFTransparent(EF_CSP_CPHS,
   1115                         obtainMessage(EVENT_GET_CSP_CPHS_DONE));
   1116                 break;
   1117             default:
   1118                 // For now, fetch all records if this is not a
   1119                 // voicemail number.
   1120                 // TODO: Handle other cases, instead of fetching all.
   1121                 adnCache.reset();
   1122                 fetchSimRecords();
   1123                 break;
   1124         }
   1125     }
   1126 
   1127     private void handleSimRefresh(int[] result) {
   1128         if (result == null || result.length == 0) {
   1129 	    if (DBG) log("handleSimRefresh without input");
   1130             return;
   1131         }
   1132 
   1133         switch ((result[0])) {
   1134             case CommandsInterface.SIM_REFRESH_FILE_UPDATED:
   1135  		if (DBG) log("handleSimRefresh with SIM_REFRESH_FILE_UPDATED");
   1136                 // result[1] contains the EFID of the updated file.
   1137                 int efid = result[1];
   1138                 handleFileUpdate(efid);
   1139                 break;
   1140             case CommandsInterface.SIM_REFRESH_INIT:
   1141 		if (DBG) log("handleSimRefresh with SIM_REFRESH_INIT");
   1142                 // need to reload all files (that we care about)
   1143                 adnCache.reset();
   1144                 fetchSimRecords();
   1145                 break;
   1146             case CommandsInterface.SIM_REFRESH_RESET:
   1147 		if (DBG) log("handleSimRefresh with SIM_REFRESH_RESET");
   1148                 phone.mCM.setRadioPower(false, null);
   1149                 /* Note: no need to call setRadioPower(true).  Assuming the desired
   1150                 * radio power state is still ON (as tracked by ServiceStateTracker),
   1151                 * ServiceStateTracker will call setRadioPower when it receives the
   1152                 * RADIO_STATE_CHANGED notification for the power off.  And if the
   1153                 * desired power state has changed in the interim, we don't want to
   1154                 * override it with an unconditional power on.
   1155                 */
   1156                 break;
   1157             default:
   1158                 // unknown refresh operation
   1159 		if (DBG) log("handleSimRefresh with unknown operation");
   1160                 break;
   1161         }
   1162     }
   1163 
   1164     /**
   1165      * Dispatch 3GPP format message. Overridden for CDMA/LTE phones by
   1166      * {@link com.android.internal.telephony.cdma.CdmaLteUiccRecords}
   1167      * to send messages to the secondary 3GPP format SMS dispatcher.
   1168      */
   1169     protected int dispatchGsmMessage(SmsMessageBase message) {
   1170         return phone.mSMS.dispatchMessage(message);
   1171     }
   1172 
   1173     private void handleSms(byte[] ba) {
   1174         if (ba[0] != 0)
   1175             Log.d("ENF", "status : " + ba[0]);
   1176 
   1177         // 3GPP TS 51.011 v5.0.0 (20011-12)  10.5.3
   1178         // 3 == "received by MS from network; message to be read"
   1179         if (ba[0] == 3) {
   1180             int n = ba.length;
   1181 
   1182             // Note: Data may include trailing FF's.  That's OK; message
   1183             // should still parse correctly.
   1184             byte[] pdu = new byte[n - 1];
   1185             System.arraycopy(ba, 1, pdu, 0, n - 1);
   1186             SmsMessage message = SmsMessage.createFromPdu(pdu);
   1187 
   1188             dispatchGsmMessage(message);
   1189         }
   1190     }
   1191 
   1192 
   1193     private void handleSmses(ArrayList messages) {
   1194         int count = messages.size();
   1195 
   1196         for (int i = 0; i < count; i++) {
   1197             byte[] ba = (byte[]) messages.get(i);
   1198 
   1199             if (ba[0] != 0)
   1200                 Log.i("ENF", "status " + i + ": " + ba[0]);
   1201 
   1202             // 3GPP TS 51.011 v5.0.0 (20011-12)  10.5.3
   1203             // 3 == "received by MS from network; message to be read"
   1204 
   1205             if (ba[0] == 3) {
   1206                 int n = ba.length;
   1207 
   1208                 // Note: Data may include trailing FF's.  That's OK; message
   1209                 // should still parse correctly.
   1210                 byte[] pdu = new byte[n - 1];
   1211                 System.arraycopy(ba, 1, pdu, 0, n - 1);
   1212                 SmsMessage message = SmsMessage.createFromPdu(pdu);
   1213 
   1214                 dispatchGsmMessage(message);
   1215 
   1216                 // 3GPP TS 51.011 v5.0.0 (20011-12)  10.5.3
   1217                 // 1 == "received by MS from network; message read"
   1218 
   1219                 ba[0] = 1;
   1220 
   1221                 if (false) { // XXX writing seems to crash RdoServD
   1222                     phone.getIccFileHandler().updateEFLinearFixed(EF_SMS,
   1223                             i, ba, null, obtainMessage(EVENT_MARK_SMS_READ_DONE, i));
   1224                 }
   1225             }
   1226         }
   1227     }
   1228 
   1229     protected void onRecordLoaded() {
   1230         // One record loaded successfully or failed, In either case
   1231         // we need to update the recordsToLoad count
   1232         recordsToLoad -= 1;
   1233 
   1234         if (recordsToLoad == 0 && recordsRequested == true) {
   1235             onAllRecordsLoaded();
   1236         } else if (recordsToLoad < 0) {
   1237             Log.e(LOG_TAG, "SIMRecords: recordsToLoad <0, programmer error suspected");
   1238             recordsToLoad = 0;
   1239         }
   1240     }
   1241 
   1242     protected void onAllRecordsLoaded() {
   1243         Log.d(LOG_TAG, "SIMRecords: record load complete");
   1244 
   1245         String operator = getOperatorNumeric();
   1246 
   1247         // Some fields require more than one SIM record to set
   1248 
   1249         phone.setSystemProperty(PROPERTY_ICC_OPERATOR_NUMERIC, operator);
   1250 
   1251         if (imsi != null) {
   1252             phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ISO_COUNTRY,
   1253                     MccTable.countryCodeForMcc(Integer.parseInt(imsi.substring(0,3))));
   1254         }
   1255         else {
   1256             Log.e("SIM", "[SIMRecords] onAllRecordsLoaded: imsi is NULL!");
   1257         }
   1258 
   1259         setVoiceMailByCountry(operator);
   1260         setSpnFromConfig(operator);
   1261 
   1262         recordsLoadedRegistrants.notifyRegistrants(
   1263             new AsyncResult(null, null, null));
   1264         phone.mIccCard.broadcastIccStateChangedIntent(
   1265                 SimCard.INTENT_VALUE_ICC_LOADED, null);
   1266     }
   1267 
   1268     //***** Private methods
   1269 
   1270     private void setSpnFromConfig(String carrier) {
   1271         if (mSpnOverride.containsCarrier(carrier)) {
   1272             spn = mSpnOverride.getSpn(carrier);
   1273         }
   1274     }
   1275 
   1276 
   1277     private void setVoiceMailByCountry (String spn) {
   1278         if (mVmConfig.containsCarrier(spn)) {
   1279             isVoiceMailFixed = true;
   1280             voiceMailNum = mVmConfig.getVoiceMailNumber(spn);
   1281             voiceMailTag = mVmConfig.getVoiceMailTag(spn);
   1282         }
   1283     }
   1284 
   1285     public void onSimReady() {
   1286         /* broadcast intent SIM_READY here so that we can make sure
   1287           READY is sent before IMSI ready
   1288         */
   1289         phone.mIccCard.broadcastIccStateChangedIntent(
   1290                 SimCard.INTENT_VALUE_ICC_READY, null);
   1291 
   1292         fetchSimRecords();
   1293     }
   1294 
   1295     protected void fetchSimRecords() {
   1296         recordsRequested = true;
   1297         IccFileHandler iccFh = phone.getIccFileHandler();
   1298 
   1299         Log.v(LOG_TAG, "SIMRecords:fetchSimRecords " + recordsToLoad);
   1300 
   1301         phone.mCM.getIMSI(obtainMessage(EVENT_GET_IMSI_DONE));
   1302         recordsToLoad++;
   1303 
   1304         iccFh.loadEFTransparent(EF_ICCID, obtainMessage(EVENT_GET_ICCID_DONE));
   1305         recordsToLoad++;
   1306 
   1307         // FIXME should examine EF[MSISDN]'s capability configuration
   1308         // to determine which is the voice/data/fax line
   1309         new AdnRecordLoader(phone).loadFromEF(EF_MSISDN, EF_EXT1, 1,
   1310                     obtainMessage(EVENT_GET_MSISDN_DONE));
   1311         recordsToLoad++;
   1312 
   1313         // Record number is subscriber profile
   1314         iccFh.loadEFLinearFixed(EF_MBI, 1, obtainMessage(EVENT_GET_MBI_DONE));
   1315         recordsToLoad++;
   1316 
   1317         iccFh.loadEFTransparent(EF_AD, obtainMessage(EVENT_GET_AD_DONE));
   1318         recordsToLoad++;
   1319 
   1320         // Record number is subscriber profile
   1321         iccFh.loadEFLinearFixed(EF_MWIS, 1, obtainMessage(EVENT_GET_MWIS_DONE));
   1322         recordsToLoad++;
   1323 
   1324 
   1325         // Also load CPHS-style voice mail indicator, which stores
   1326         // the same info as EF[MWIS]. If both exist, both are updated
   1327         // but the EF[MWIS] data is preferred
   1328         // Please note this must be loaded after EF[MWIS]
   1329         iccFh.loadEFTransparent(
   1330                 EF_VOICE_MAIL_INDICATOR_CPHS,
   1331                 obtainMessage(EVENT_GET_VOICE_MAIL_INDICATOR_CPHS_DONE));
   1332         recordsToLoad++;
   1333 
   1334         // Same goes for Call Forward Status indicator: fetch both
   1335         // EF[CFIS] and CPHS-EF, with EF[CFIS] preferred.
   1336         iccFh.loadEFLinearFixed(EF_CFIS, 1, obtainMessage(EVENT_GET_CFIS_DONE));
   1337         recordsToLoad++;
   1338         iccFh.loadEFTransparent(EF_CFF_CPHS, obtainMessage(EVENT_GET_CFF_DONE));
   1339         recordsToLoad++;
   1340 
   1341 
   1342         getSpnFsm(true, null);
   1343 
   1344         iccFh.loadEFTransparent(EF_SPDI, obtainMessage(EVENT_GET_SPDI_DONE));
   1345         recordsToLoad++;
   1346 
   1347         iccFh.loadEFLinearFixed(EF_PNN, 1, obtainMessage(EVENT_GET_PNN_DONE));
   1348         recordsToLoad++;
   1349 
   1350         iccFh.loadEFTransparent(EF_SST, obtainMessage(EVENT_GET_SST_DONE));
   1351         recordsToLoad++;
   1352 
   1353         iccFh.loadEFTransparent(EF_INFO_CPHS, obtainMessage(EVENT_GET_INFO_CPHS_DONE));
   1354         recordsToLoad++;
   1355 
   1356         iccFh.loadEFTransparent(EF_CSP_CPHS,obtainMessage(EVENT_GET_CSP_CPHS_DONE));
   1357         recordsToLoad++;
   1358 
   1359         // XXX should seek instead of examining them all
   1360         if (false) { // XXX
   1361             iccFh.loadEFLinearFixedAll(EF_SMS, obtainMessage(EVENT_GET_ALL_SMS_DONE));
   1362             recordsToLoad++;
   1363         }
   1364 
   1365         if (CRASH_RIL) {
   1366             String sms = "0107912160130310f20404d0110041007030208054832b0120"
   1367                          + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
   1368                          + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
   1369                          + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
   1370                          + "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
   1371                          + "ffffffffffffffffffffffffffffff";
   1372             byte[] ba = IccUtils.hexStringToBytes(sms);
   1373 
   1374             iccFh.updateEFLinearFixed(EF_SMS, 1, ba, null,
   1375                             obtainMessage(EVENT_MARK_SMS_READ_DONE, 1));
   1376         }
   1377     }
   1378 
   1379     /**
   1380      * Returns the SpnDisplayRule based on settings on the SIM and the
   1381      * specified plmn (currently-registered PLMN).  See TS 22.101 Annex A
   1382      * and TS 51.011 10.3.11 for details.
   1383      *
   1384      * If the SPN is not found on the SIM, the rule is always PLMN_ONLY.
   1385      */
   1386     @Override
   1387     public int getDisplayRule(String plmn) {
   1388         int rule;
   1389         if (spn == null || spnDisplayCondition == -1) {
   1390             // EF_SPN was not found on the SIM, or not yet loaded.  Just show ONS.
   1391             rule = SPN_RULE_SHOW_PLMN;
   1392         } else if (isOnMatchingPlmn(plmn)) {
   1393             rule = SPN_RULE_SHOW_SPN;
   1394             if ((spnDisplayCondition & 0x01) == 0x01) {
   1395                 // ONS required when registered to HPLMN or PLMN in EF_SPDI
   1396                 rule |= SPN_RULE_SHOW_PLMN;
   1397             }
   1398         } else {
   1399             rule = SPN_RULE_SHOW_PLMN;
   1400             if ((spnDisplayCondition & 0x02) == 0x00) {
   1401                 // SPN required if not registered to HPLMN or PLMN in EF_SPDI
   1402                 rule |= SPN_RULE_SHOW_SPN;
   1403             }
   1404         }
   1405         return rule;
   1406     }
   1407 
   1408     /**
   1409      * Checks if plmn is HPLMN or on the spdiNetworks list.
   1410      */
   1411     private boolean isOnMatchingPlmn(String plmn) {
   1412         if (plmn == null) return false;
   1413 
   1414         if (plmn.equals(getOperatorNumeric())) {
   1415             return true;
   1416         }
   1417 
   1418         if (spdiNetworks != null) {
   1419             for (String spdiNet : spdiNetworks) {
   1420                 if (plmn.equals(spdiNet)) {
   1421                     return true;
   1422                 }
   1423             }
   1424         }
   1425         return false;
   1426     }
   1427 
   1428     /**
   1429      * States of Get SPN Finite State Machine which only used by getSpnFsm()
   1430      */
   1431     private enum Get_Spn_Fsm_State {
   1432         IDLE,               // No initialized
   1433         INIT,               // Start FSM
   1434         READ_SPN_3GPP,      // Load EF_SPN firstly
   1435         READ_SPN_CPHS,      // Load EF_SPN_CPHS secondly
   1436         READ_SPN_SHORT_CPHS // Load EF_SPN_SHORT_CPHS last
   1437     }
   1438 
   1439     /**
   1440      * Finite State Machine to load Service Provider Name , which can be stored
   1441      * in either EF_SPN (3GPP), EF_SPN_CPHS, or EF_SPN_SHORT_CPHS (CPHS4.2)
   1442      *
   1443      * After starting, FSM will search SPN EFs in order and stop after finding
   1444      * the first valid SPN
   1445      *
   1446      * @param start set true only for initialize loading
   1447      * @param ar the AsyncResult from loadEFTransparent
   1448      *        ar.exception holds exception in error
   1449      *        ar.result is byte[] for data in success
   1450      */
   1451     private void getSpnFsm(boolean start, AsyncResult ar) {
   1452         byte[] data;
   1453 
   1454         if (start) {
   1455             spnState = Get_Spn_Fsm_State.INIT;
   1456         }
   1457 
   1458         switch(spnState){
   1459             case INIT:
   1460                 spn = null;
   1461 
   1462                 phone.getIccFileHandler().loadEFTransparent( EF_SPN,
   1463                         obtainMessage(EVENT_GET_SPN_DONE));
   1464                 recordsToLoad++;
   1465 
   1466                 spnState = Get_Spn_Fsm_State.READ_SPN_3GPP;
   1467                 break;
   1468             case READ_SPN_3GPP:
   1469                 if (ar != null && ar.exception == null) {
   1470                     data = (byte[]) ar.result;
   1471                     spnDisplayCondition = 0xff & data[0];
   1472                     spn = IccUtils.adnStringFieldToString(data, 1, data.length - 1);
   1473 
   1474                     if (DBG) log("Load EF_SPN: " + spn
   1475                             + " spnDisplayCondition: " + spnDisplayCondition);
   1476                     phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, spn);
   1477 
   1478                     spnState = Get_Spn_Fsm_State.IDLE;
   1479                 } else {
   1480                     phone.getIccFileHandler().loadEFTransparent( EF_SPN_CPHS,
   1481                             obtainMessage(EVENT_GET_SPN_DONE));
   1482                     recordsToLoad++;
   1483 
   1484                     spnState = Get_Spn_Fsm_State.READ_SPN_CPHS;
   1485 
   1486                     // See TS 51.011 10.3.11.  Basically, default to
   1487                     // show PLMN always, and SPN also if roaming.
   1488                     spnDisplayCondition = -1;
   1489                 }
   1490                 break;
   1491             case READ_SPN_CPHS:
   1492                 if (ar != null && ar.exception == null) {
   1493                     data = (byte[]) ar.result;
   1494                     spn = IccUtils.adnStringFieldToString(
   1495                             data, 0, data.length - 1 );
   1496 
   1497                     if (DBG) log("Load EF_SPN_CPHS: " + spn);
   1498                     phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, spn);
   1499 
   1500                     spnState = Get_Spn_Fsm_State.IDLE;
   1501                 } else {
   1502                     phone.getIccFileHandler().loadEFTransparent(
   1503                             EF_SPN_SHORT_CPHS, obtainMessage(EVENT_GET_SPN_DONE));
   1504                     recordsToLoad++;
   1505 
   1506                     spnState = Get_Spn_Fsm_State.READ_SPN_SHORT_CPHS;
   1507                 }
   1508                 break;
   1509             case READ_SPN_SHORT_CPHS:
   1510                 if (ar != null && ar.exception == null) {
   1511                     data = (byte[]) ar.result;
   1512                     spn = IccUtils.adnStringFieldToString(
   1513                             data, 0, data.length - 1);
   1514 
   1515                     if (DBG) log("Load EF_SPN_SHORT_CPHS: " + spn);
   1516                     phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, spn);
   1517                 }else {
   1518                     if (DBG) log("No SPN loaded in either CHPS or 3GPP");
   1519                 }
   1520 
   1521                 spnState = Get_Spn_Fsm_State.IDLE;
   1522                 break;
   1523             default:
   1524                 spnState = Get_Spn_Fsm_State.IDLE;
   1525         }
   1526     }
   1527 
   1528     /**
   1529      * Parse TS 51.011 EF[SPDI] record
   1530      * This record contains the list of numeric network IDs that
   1531      * are treated specially when determining SPN display
   1532      */
   1533     private void
   1534     parseEfSpdi(byte[] data) {
   1535         SimTlv tlv = new SimTlv(data, 0, data.length);
   1536 
   1537         byte[] plmnEntries = null;
   1538 
   1539         for ( ; tlv.isValidObject() ; tlv.nextObject()) {
   1540             // Skip SPDI tag, if existant
   1541             if (tlv.getTag() == TAG_SPDI) {
   1542               tlv = new SimTlv(tlv.getData(), 0, tlv.getData().length);
   1543             }
   1544             // There should only be one TAG_SPDI_PLMN_LIST
   1545             if (tlv.getTag() == TAG_SPDI_PLMN_LIST) {
   1546                 plmnEntries = tlv.getData();
   1547                 break;
   1548             }
   1549         }
   1550 
   1551         if (plmnEntries == null) {
   1552             return;
   1553         }
   1554 
   1555         spdiNetworks = new ArrayList<String>(plmnEntries.length / 3);
   1556 
   1557         for (int i = 0 ; i + 2 < plmnEntries.length ; i += 3) {
   1558             String plmnCode;
   1559             plmnCode = IccUtils.bcdToString(plmnEntries, i, 3);
   1560 
   1561             // Valid operator codes are 5 or 6 digits
   1562             if (plmnCode.length() >= 5) {
   1563                 log("EF_SPDI network: " + plmnCode);
   1564                 spdiNetworks.add(plmnCode);
   1565             }
   1566         }
   1567     }
   1568 
   1569     /**
   1570      * check to see if Mailbox Number is allocated and activated in CPHS SST
   1571      */
   1572     private boolean isCphsMailboxEnabled() {
   1573         if (mCphsInfo == null)  return false;
   1574         return ((mCphsInfo[1] & CPHS_SST_MBN_MASK) == CPHS_SST_MBN_ENABLED );
   1575     }
   1576 
   1577     protected void log(String s) {
   1578         Log.d(LOG_TAG, "[SIMRecords] " + s);
   1579     }
   1580 
   1581     protected void loge(String s) {
   1582         Log.e(LOG_TAG, "[SIMRecords] " + s);
   1583     }
   1584 
   1585     /**
   1586      * Return true if "Restriction of menu options for manual PLMN selection"
   1587      * bit is set or EF_CSP data is unavailable, return false otherwise.
   1588      */
   1589     public boolean isCspPlmnEnabled() {
   1590         return mCspPlmnEnabled;
   1591     }
   1592 
   1593     /**
   1594      * Parse EF_CSP data and check if
   1595      * "Restriction of menu options for manual PLMN selection" is
   1596      * Enabled/Disabled
   1597      *
   1598      * @param data EF_CSP hex data.
   1599      */
   1600     private void handleEfCspData(byte[] data) {
   1601         // As per spec CPHS4_2.WW6, CPHS B.4.7.1, EF_CSP contains CPHS defined
   1602         // 18 bytes (i.e 9 service groups info) and additional data specific to
   1603         // operator. The valueAddedServicesGroup is not part of standard
   1604         // services. This is operator specific and can be programmed any where.
   1605         // Normally this is programmed as 10th service after the standard
   1606         // services.
   1607         int usedCspGroups = data.length / 2;
   1608         // This is the "Servive Group Number" of "Value Added Services Group".
   1609         byte valueAddedServicesGroup = (byte)0xC0;
   1610 
   1611         mCspPlmnEnabled = true;
   1612         for (int i = 0; i < usedCspGroups; i++) {
   1613              if (data[2 * i] == valueAddedServicesGroup) {
   1614                  Log.i(LOG_TAG, "[CSP] found ValueAddedServicesGroup, value "
   1615                        + data[(2 * i) + 1]);
   1616                  if ((data[(2 * i) + 1] & 0x80) == 0x80) {
   1617                      // Bit 8 is for
   1618                      // "Restriction of menu options for manual PLMN selection".
   1619                      // Operator Selection menu should be enabled.
   1620                      mCspPlmnEnabled = true;
   1621                  } else {
   1622                      mCspPlmnEnabled = false;
   1623                      // Operator Selection menu should be disabled.
   1624                      // Operator Selection Mode should be set to Automatic.
   1625                      Log.i(LOG_TAG,"[CSP] Set Automatic Network Selection");
   1626                      phone.setNetworkSelectionModeAutomatic(null);
   1627                  }
   1628                  return;
   1629              }
   1630         }
   1631 
   1632         Log.w(LOG_TAG, "[CSP] Value Added Service Group (0xC0), not found!");
   1633     }
   1634 }
   1635