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