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