Home | History | Annotate | Download | only in gsm
      1 /*
      2  * Copyright (C) 2009 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 android.os.AsyncResult;
     20 import android.os.Handler;
     21 import android.os.Message;
     22 import android.telephony.Rlog;
     23 import android.util.SparseArray;
     24 import android.util.SparseIntArray;
     25 
     26 import com.android.internal.telephony.uicc.AdnRecord;
     27 import com.android.internal.telephony.uicc.AdnRecordCache;
     28 import com.android.internal.telephony.uicc.IccConstants;
     29 import com.android.internal.telephony.uicc.IccFileHandler;
     30 import com.android.internal.telephony.uicc.IccUtils;
     31 import java.util.ArrayList;
     32 
     33 /**
     34  * This class implements reading and parsing USIM records.
     35  * Refer to Spec 3GPP TS 31.102 for more details.
     36  *
     37  * {@hide}
     38  */
     39 public class UsimPhoneBookManager extends Handler implements IccConstants {
     40     private static final String LOG_TAG = "UsimPhoneBookManager";
     41     private static final boolean DBG = true;
     42     private ArrayList<PbrRecord> mPbrRecords;
     43     private Boolean mIsPbrPresent;
     44     private IccFileHandler mFh;
     45     private AdnRecordCache mAdnCache;
     46     private Object mLock = new Object();
     47     private ArrayList<AdnRecord> mPhoneBookRecords;
     48     private ArrayList<byte[]> mIapFileRecord;
     49     private ArrayList<byte[]> mEmailFileRecord;
     50 
     51     // email list for each ADN record. The key would be
     52     // ADN's efid << 8 + record #
     53     private SparseArray<ArrayList<String>> mEmailsForAdnRec;
     54 
     55     // SFI to ADN Efid mapping table
     56     private SparseIntArray mSfiEfidTable;
     57 
     58     private boolean mRefreshCache = false;
     59 
     60 
     61     private static final int EVENT_PBR_LOAD_DONE = 1;
     62     private static final int EVENT_USIM_ADN_LOAD_DONE = 2;
     63     private static final int EVENT_IAP_LOAD_DONE = 3;
     64     private static final int EVENT_EMAIL_LOAD_DONE = 4;
     65 
     66     private static final int USIM_TYPE1_TAG   = 0xA8;
     67     private static final int USIM_TYPE2_TAG   = 0xA9;
     68     private static final int USIM_TYPE3_TAG   = 0xAA;
     69     private static final int USIM_EFADN_TAG   = 0xC0;
     70     private static final int USIM_EFIAP_TAG   = 0xC1;
     71     private static final int USIM_EFEXT1_TAG  = 0xC2;
     72     private static final int USIM_EFSNE_TAG   = 0xC3;
     73     private static final int USIM_EFANR_TAG   = 0xC4;
     74     private static final int USIM_EFPBC_TAG   = 0xC5;
     75     private static final int USIM_EFGRP_TAG   = 0xC6;
     76     private static final int USIM_EFAAS_TAG   = 0xC7;
     77     private static final int USIM_EFGSD_TAG   = 0xC8;
     78     private static final int USIM_EFUID_TAG   = 0xC9;
     79     private static final int USIM_EFEMAIL_TAG = 0xCA;
     80     private static final int USIM_EFCCP1_TAG  = 0xCB;
     81 
     82     private static final int INVALID_SFI = -1;
     83     private static final byte INVALID_BYTE = -1;
     84 
     85     // class File represent a PBR record TLV object which points to the rest of the phonebook EFs
     86     private class File {
     87         // Phonebook reference file constructed tag defined in 3GPP TS 31.102
     88         // section 4.4.2.1 table 4.1
     89         private final int mParentTag;
     90         // EFID of the file
     91         private final int mEfid;
     92         // SFI (Short File Identification) of the file. 0xFF indicates invalid SFI.
     93         private final int mSfi;
     94         // The order of this tag showing in the PBR record.
     95         private final int mIndex;
     96 
     97         File(int parentTag, int efid, int sfi, int index) {
     98             mParentTag = parentTag;
     99             mEfid = efid;
    100             mSfi = sfi;
    101             mIndex = index;
    102         }
    103 
    104         public int getParentTag() { return mParentTag; }
    105         public int getEfid() { return mEfid; }
    106         public int getSfi() { return mSfi; }
    107         public int getIndex() { return mIndex; }
    108     }
    109 
    110     public UsimPhoneBookManager(IccFileHandler fh, AdnRecordCache cache) {
    111         mFh = fh;
    112         mPhoneBookRecords = new ArrayList<AdnRecord>();
    113         mPbrRecords = null;
    114         // We assume its present, after the first read this is updated.
    115         // So we don't have to read from UICC if its not present on subsequent reads.
    116         mIsPbrPresent = true;
    117         mAdnCache = cache;
    118         mEmailsForAdnRec = new SparseArray<ArrayList<String>>();
    119         mSfiEfidTable = new SparseIntArray();
    120     }
    121 
    122     public void reset() {
    123         mPhoneBookRecords.clear();
    124         mIapFileRecord = null;
    125         mEmailFileRecord = null;
    126         mPbrRecords = null;
    127         mIsPbrPresent = true;
    128         mRefreshCache = false;
    129         mEmailsForAdnRec.clear();
    130         mSfiEfidTable.clear();
    131     }
    132 
    133     // Load all phonebook related EFs from the SIM.
    134     public ArrayList<AdnRecord> loadEfFilesFromUsim() {
    135         synchronized (mLock) {
    136             if (!mPhoneBookRecords.isEmpty()) {
    137                 if (mRefreshCache) {
    138                     mRefreshCache = false;
    139                     refreshCache();
    140                 }
    141                 return mPhoneBookRecords;
    142             }
    143 
    144             if (!mIsPbrPresent) return null;
    145 
    146             // Check if the PBR file is present in the cache, if not read it
    147             // from the USIM.
    148             if (mPbrRecords == null) {
    149                 readPbrFileAndWait();
    150             }
    151 
    152             if (mPbrRecords == null)
    153                 return null;
    154 
    155             int numRecs = mPbrRecords.size();
    156 
    157             log("loadEfFilesFromUsim: Loading adn and emails");
    158             for (int i = 0; i < numRecs; i++) {
    159                 readAdnFileAndWait(i);
    160                 readEmailFileAndWait(i);
    161             }
    162 
    163             updatePhoneAdnRecord();
    164             // All EF files are loaded, return all the records
    165         }
    166         return mPhoneBookRecords;
    167     }
    168 
    169     // Refresh the phonebook cache.
    170     private void refreshCache() {
    171         if (mPbrRecords == null) return;
    172         mPhoneBookRecords.clear();
    173 
    174         int numRecs = mPbrRecords.size();
    175         for (int i = 0; i < numRecs; i++) {
    176             readAdnFileAndWait(i);
    177         }
    178     }
    179 
    180     // Invalidate the phonebook cache.
    181     public void invalidateCache() {
    182         mRefreshCache = true;
    183     }
    184 
    185     // Read the phonebook reference file EF_PBR.
    186     private void readPbrFileAndWait() {
    187         mFh.loadEFLinearFixedAll(EF_PBR, obtainMessage(EVENT_PBR_LOAD_DONE));
    188         try {
    189             mLock.wait();
    190         } catch (InterruptedException e) {
    191             Rlog.e(LOG_TAG, "Interrupted Exception in readAdnFileAndWait");
    192         }
    193     }
    194 
    195     // Read EF_EMAIL which contains the email records.
    196     private void readEmailFileAndWait(int recId) {
    197         SparseArray<File> files;
    198         files = mPbrRecords.get(recId).mFileIds;
    199         if (files == null) return;
    200 
    201         File email = files.get(USIM_EFEMAIL_TAG);
    202         if (email != null) {
    203 
    204             /**
    205              * Check if the EF_EMAIL is a Type 1 file or a type 2 file.
    206              * If mEmailPresentInIap is true, its a type 2 file.
    207              * So we read the IAP file and then read the email records.
    208              * instead of reading directly.
    209              */
    210             if (email.getParentTag() == USIM_TYPE2_TAG) {
    211                 if (files.get(USIM_EFIAP_TAG) == null) {
    212                     Rlog.e(LOG_TAG, "Can't locate EF_IAP in EF_PBR.");
    213                     return;
    214                 }
    215 
    216                 log("EF_IAP exists. Loading EF_IAP to retrieve the index.");
    217                 readIapFileAndWait(files.get(USIM_EFIAP_TAG).getEfid());
    218                 if (mIapFileRecord == null) {
    219                     Rlog.e(LOG_TAG, "Error: IAP file is empty");
    220                     return;
    221                 }
    222 
    223                 log("EF_EMAIL order in PBR record: " + email.getIndex());
    224             }
    225 
    226             int emailEfid = email.getEfid();
    227             log("EF_EMAIL exists in PBR. efid = 0x" +
    228                     Integer.toHexString(emailEfid).toUpperCase());
    229 
    230             /**
    231              * Make sure this EF_EMAIL was never read earlier. Sometimes two PBR record points
    232              */
    233             // to the same EF_EMAIL
    234             for (int i = 0; i < recId; i++) {
    235                 if (mPbrRecords.get(i) != null) {
    236                     SparseArray<File> previousFileIds = mPbrRecords.get(i).mFileIds;
    237                     if (previousFileIds != null) {
    238                         File id = previousFileIds.get(USIM_EFEMAIL_TAG);
    239                         if (id != null && id.getEfid() == emailEfid) {
    240                             log("Skipped this EF_EMAIL which was loaded earlier");
    241                             return;
    242                         }
    243                     }
    244                 }
    245             }
    246 
    247             // Read the EFEmail file.
    248             mFh.loadEFLinearFixedAll(emailEfid,
    249                     obtainMessage(EVENT_EMAIL_LOAD_DONE));
    250             try {
    251                 mLock.wait();
    252             } catch (InterruptedException e) {
    253                 Rlog.e(LOG_TAG, "Interrupted Exception in readEmailFileAndWait");
    254             }
    255 
    256             if (mEmailFileRecord == null) {
    257                 Rlog.e(LOG_TAG, "Error: Email file is empty");
    258                 return;
    259             }
    260 
    261             // Build email list
    262             if (email.getParentTag() == USIM_TYPE2_TAG && mIapFileRecord != null) {
    263                 // If the tag is type 2 and EF_IAP exists, we need to build tpe 2 email list
    264                 buildType2EmailList(recId);
    265             }
    266             else {
    267                 // If one the followings is true, we build type 1 email list
    268                 // 1. EF_IAP does not exist or it is failed to load
    269                 // 2. ICC cards can be made such that they have an IAP file but all
    270                 //    records are empty. In that case buildType2EmailList will fail and
    271                 //    we need to build type 1 email list.
    272 
    273                 // Build type 1 email list
    274                 buildType1EmailList(recId);
    275             }
    276         }
    277     }
    278 
    279     // Build type 1 email list
    280     private void buildType1EmailList(int recId) {
    281         /**
    282          * If this is type 1, the number of records in EF_EMAIL would be same as the record number
    283          * in the master/reference file.
    284          */
    285         if (mPbrRecords.get(recId) == null)
    286             return;
    287 
    288         int numRecs = mPbrRecords.get(recId).mMasterFileRecordNum;
    289         log("Building type 1 email list. recId = "
    290                 + recId + ", numRecs = " + numRecs);
    291 
    292         byte[] emailRec;
    293         for (int i = 0; i < numRecs; i++) {
    294             try {
    295                 emailRec = mEmailFileRecord.get(i);
    296             } catch (IndexOutOfBoundsException e) {
    297                 Rlog.e(LOG_TAG, "Error: Improper ICC card: No email record for ADN, continuing");
    298                 break;
    299             }
    300 
    301             /**
    302              *  3GPP TS 31.102 4.4.2.13 EF_EMAIL (e-mail address)
    303              *
    304              *  The fields below are mandatory if and only if the file
    305              *  is not type 1 (as specified in EF_PBR)
    306              *
    307              *  Byte [X + 1]: ADN file SFI (Short File Identification)
    308              *  Byte [X + 2]: ADN file Record Identifier
    309              */
    310             int sfi = emailRec[emailRec.length - 2];
    311             int adnRecId = emailRec[emailRec.length - 1];
    312 
    313             String email = readEmailRecord(i);
    314 
    315             if (email == null || email.equals("")) {
    316                 continue;
    317             }
    318 
    319             // Get the associated ADN's efid first.
    320             int adnEfid = 0;
    321             if (sfi == INVALID_SFI || mSfiEfidTable.get(sfi) == 0) {
    322 
    323                 // If SFI is invalid or cannot be mapped to any ADN, use the ADN's efid
    324                 // in the same PBR files.
    325                 File file = mPbrRecords.get(recId).mFileIds.get(USIM_EFADN_TAG);
    326                 if (file == null)
    327                     continue;
    328                 adnEfid = file.getEfid();
    329             }
    330             else {
    331                 adnEfid = mSfiEfidTable.get(sfi);
    332             }
    333             /**
    334              * SIM record numbers are 1 based.
    335              * The key is constructed by efid and record index.
    336              */
    337             int index = (((adnEfid & 0xFFFF) << 8) | ((adnRecId - 1) & 0xFF));
    338             ArrayList<String> emailList = mEmailsForAdnRec.get(index);
    339             if (emailList == null) {
    340                 emailList = new ArrayList<String>();
    341             }
    342             log("Adding email #" + i + " list to index 0x" +
    343                     Integer.toHexString(index).toUpperCase());
    344             emailList.add(email);
    345             mEmailsForAdnRec.put(index, emailList);
    346         }
    347     }
    348 
    349     // Build type 2 email list
    350     private boolean buildType2EmailList(int recId) {
    351 
    352         if (mPbrRecords.get(recId) == null)
    353             return false;
    354 
    355         int numRecs = mPbrRecords.get(recId).mMasterFileRecordNum;
    356         log("Building type 2 email list. recId = "
    357                 + recId + ", numRecs = " + numRecs);
    358 
    359         /**
    360          * 3GPP TS 31.102 4.4.2.1 EF_PBR (Phone Book Reference file) table 4.1
    361 
    362          * The number of records in the IAP file is same as the number of records in the master
    363          * file (e.g EF_ADN). The order of the pointers in an EF_IAP shall be the same as the
    364          * order of file IDs that appear in the TLV object indicated by Tag 'A9' in the
    365          * reference file record (e.g value of mEmailTagNumberInIap)
    366          */
    367 
    368         File adnFile = mPbrRecords.get(recId).mFileIds.get(USIM_EFADN_TAG);
    369         if (adnFile == null) {
    370             Rlog.e(LOG_TAG, "Error: Improper ICC card: EF_ADN does not exist in PBR files");
    371             return false;
    372         }
    373         int adnEfid = adnFile.getEfid();
    374 
    375         for (int i = 0; i < numRecs; i++) {
    376             byte[] record;
    377             int emailRecId;
    378             try {
    379                 record = mIapFileRecord.get(i);
    380                 emailRecId =
    381                         record[mPbrRecords.get(recId).mFileIds.get(USIM_EFEMAIL_TAG).getIndex()];
    382             } catch (IndexOutOfBoundsException e) {
    383                 Rlog.e(LOG_TAG, "Error: Improper ICC card: Corrupted EF_IAP");
    384                 continue;
    385             }
    386 
    387             String email = readEmailRecord(emailRecId - 1);
    388             if (email != null && !email.equals("")) {
    389                 // The key is constructed by efid and record index.
    390                 int index = (((adnEfid & 0xFFFF) << 8) | (i & 0xFF));
    391                 ArrayList<String> emailList = mEmailsForAdnRec.get(index);
    392                 if (emailList == null) {
    393                     emailList = new ArrayList<String>();
    394                 }
    395                 emailList.add(email);
    396                 log("Adding email list to index 0x" +
    397                         Integer.toHexString(index).toUpperCase());
    398                 mEmailsForAdnRec.put(index, emailList);
    399             }
    400         }
    401         return true;
    402     }
    403 
    404     // Read Phonebook Index Admistration EF_IAP file
    405     private void readIapFileAndWait(int efid) {
    406         mFh.loadEFLinearFixedAll(efid, obtainMessage(EVENT_IAP_LOAD_DONE));
    407         try {
    408             mLock.wait();
    409         } catch (InterruptedException e) {
    410             Rlog.e(LOG_TAG, "Interrupted Exception in readIapFileAndWait");
    411         }
    412     }
    413 
    414     private void updatePhoneAdnRecord() {
    415 
    416         int numAdnRecs = mPhoneBookRecords.size();
    417 
    418         for (int i = 0; i < numAdnRecs; i++) {
    419 
    420             AdnRecord rec = mPhoneBookRecords.get(i);
    421 
    422             int adnEfid = rec.getEfid();
    423             int adnRecId = rec.getRecId();
    424 
    425             int index = (((adnEfid & 0xFFFF) << 8) | ((adnRecId - 1) & 0xFF));
    426 
    427             ArrayList<String> emailList;
    428             try {
    429                 emailList = mEmailsForAdnRec.get(index);
    430             } catch (IndexOutOfBoundsException e) {
    431                 continue;
    432             }
    433 
    434             if (emailList == null)
    435                 continue;
    436 
    437             String[] emails = new String[emailList.size()];
    438             System.arraycopy(emailList.toArray(), 0, emails, 0, emailList.size());
    439             rec.setEmails(emails);
    440             log("Adding email list to ADN (0x" +
    441                     Integer.toHexString(mPhoneBookRecords.get(i).getEfid()).toUpperCase() +
    442                     ") record #" + mPhoneBookRecords.get(i).getRecId());
    443             mPhoneBookRecords.set(i, rec);
    444         }
    445     }
    446 
    447     // Read email from the record of EF_EMAIL
    448     private String readEmailRecord(int recId) {
    449         byte[] emailRec;
    450         try {
    451             emailRec = mEmailFileRecord.get(recId);
    452         } catch (IndexOutOfBoundsException e) {
    453             return null;
    454         }
    455 
    456         // The length of the record is X+2 byte, where X bytes is the email address
    457         return IccUtils.adnStringFieldToString(emailRec, 0, emailRec.length - 2);
    458     }
    459 
    460     // Read EF_ADN file
    461     private void readAdnFileAndWait(int recId) {
    462         SparseArray<File> files;
    463         files = mPbrRecords.get(recId).mFileIds;
    464         if (files == null || files.size() == 0) return;
    465 
    466         int extEf = 0;
    467         // Only call fileIds.get while EF_EXT1_TAG is available
    468         if (files.get(USIM_EFEXT1_TAG) != null) {
    469             extEf = files.get(USIM_EFEXT1_TAG).getEfid();
    470         }
    471 
    472         if (files.get(USIM_EFADN_TAG) == null)
    473             return;
    474 
    475         int previousSize = mPhoneBookRecords.size();
    476         mAdnCache.requestLoadAllAdnLike(files.get(USIM_EFADN_TAG).getEfid(),
    477             extEf, obtainMessage(EVENT_USIM_ADN_LOAD_DONE));
    478         try {
    479             mLock.wait();
    480         } catch (InterruptedException e) {
    481             Rlog.e(LOG_TAG, "Interrupted Exception in readAdnFileAndWait");
    482         }
    483 
    484         /**
    485          * The recent added ADN record # would be the reference record size
    486          * for the rest of EFs associated within this PBR.
    487          */
    488         mPbrRecords.get(recId).mMasterFileRecordNum = mPhoneBookRecords.size() - previousSize;
    489     }
    490 
    491     // Create the phonebook reference file based on EF_PBR
    492     private void createPbrFile(ArrayList<byte[]> records) {
    493         if (records == null) {
    494             mPbrRecords = null;
    495             mIsPbrPresent = false;
    496             return;
    497         }
    498 
    499         mPbrRecords = new ArrayList<PbrRecord>();
    500         for (int i = 0; i < records.size(); i++) {
    501             // Some cards have two records but the 2nd record is filled with all invalid char 0xff.
    502             // So we need to check if the record is valid or not before adding into the PBR records.
    503             if (records.get(i)[0] != INVALID_BYTE) {
    504                 mPbrRecords.add(new PbrRecord(records.get(i)));
    505             }
    506         }
    507 
    508         for (PbrRecord record : mPbrRecords) {
    509             File file = record.mFileIds.get(USIM_EFADN_TAG);
    510             // If the file does not contain EF_ADN, we'll just skip it.
    511             if (file != null) {
    512                 int sfi = file.getSfi();
    513                 if (sfi != INVALID_SFI) {
    514                     mSfiEfidTable.put(sfi, record.mFileIds.get(USIM_EFADN_TAG).getEfid());
    515                 }
    516             }
    517         }
    518     }
    519 
    520     @Override
    521     public void handleMessage(Message msg) {
    522         AsyncResult ar;
    523 
    524         switch(msg.what) {
    525         case EVENT_PBR_LOAD_DONE:
    526             log("Loading PBR records done");
    527             ar = (AsyncResult) msg.obj;
    528             if (ar.exception == null) {
    529                 createPbrFile((ArrayList<byte[]>)ar.result);
    530             }
    531             synchronized (mLock) {
    532                 mLock.notify();
    533             }
    534             break;
    535         case EVENT_USIM_ADN_LOAD_DONE:
    536             log("Loading USIM ADN records done");
    537             ar = (AsyncResult) msg.obj;
    538             if (ar.exception == null) {
    539                 mPhoneBookRecords.addAll((ArrayList<AdnRecord>)ar.result);
    540             }
    541             synchronized (mLock) {
    542                 mLock.notify();
    543             }
    544             break;
    545         case EVENT_IAP_LOAD_DONE:
    546             log("Loading USIM IAP records done");
    547             ar = (AsyncResult) msg.obj;
    548             if (ar.exception == null) {
    549                 mIapFileRecord = ((ArrayList<byte[]>)ar.result);
    550             }
    551             synchronized (mLock) {
    552                 mLock.notify();
    553             }
    554             break;
    555         case EVENT_EMAIL_LOAD_DONE:
    556             log("Loading USIM Email records done");
    557             ar = (AsyncResult) msg.obj;
    558             if (ar.exception == null) {
    559                 mEmailFileRecord = ((ArrayList<byte[]>)ar.result);
    560             }
    561 
    562             synchronized (mLock) {
    563                 mLock.notify();
    564             }
    565             break;
    566         }
    567     }
    568 
    569     // PbrRecord represents a record in EF_PBR
    570     private class PbrRecord {
    571         // TLV tags
    572         private SparseArray<File> mFileIds;
    573 
    574         /**
    575          * 3GPP TS 31.102 4.4.2.1 EF_PBR (Phone Book Reference file)
    576          * If this is type 1 files, files that contain as many records as the
    577          * reference/master file (EF_ADN, EF_ADN1) and are linked on record number
    578          * bases (Rec1 -> Rec1). The master file record number is the reference.
    579          */
    580         private int mMasterFileRecordNum;
    581 
    582         PbrRecord(byte[] record) {
    583             mFileIds = new SparseArray<File>();
    584             SimTlv recTlv;
    585             log("PBR rec: " + IccUtils.bytesToHexString(record));
    586             recTlv = new SimTlv(record, 0, record.length);
    587             parseTag(recTlv);
    588         }
    589 
    590         void parseTag(SimTlv tlv) {
    591             SimTlv tlvEfSfi;
    592             int tag;
    593             byte[] data;
    594 
    595             do {
    596                 tag = tlv.getTag();
    597                 switch(tag) {
    598                 case USIM_TYPE1_TAG: // A8
    599                 case USIM_TYPE3_TAG: // AA
    600                 case USIM_TYPE2_TAG: // A9
    601                     data = tlv.getData();
    602                     tlvEfSfi = new SimTlv(data, 0, data.length);
    603                     parseEfAndSFI(tlvEfSfi, tag);
    604                     break;
    605                 }
    606             } while (tlv.nextObject());
    607         }
    608 
    609         void parseEfAndSFI(SimTlv tlv, int parentTag) {
    610             int tag;
    611             byte[] data;
    612             int tagNumberWithinParentTag = 0;
    613             do {
    614                 tag = tlv.getTag();
    615                 switch(tag) {
    616                     case USIM_EFEMAIL_TAG:
    617                     case USIM_EFADN_TAG:
    618                     case USIM_EFEXT1_TAG:
    619                     case USIM_EFANR_TAG:
    620                     case USIM_EFPBC_TAG:
    621                     case USIM_EFGRP_TAG:
    622                     case USIM_EFAAS_TAG:
    623                     case USIM_EFGSD_TAG:
    624                     case USIM_EFUID_TAG:
    625                     case USIM_EFCCP1_TAG:
    626                     case USIM_EFIAP_TAG:
    627                     case USIM_EFSNE_TAG:
    628                         /** 3GPP TS 31.102, 4.4.2.1 EF_PBR (Phone Book Reference file)
    629                          *
    630                          * The SFI value assigned to an EF which is indicated in EF_PBR shall
    631                          * correspond to the SFI indicated in the TLV object in EF_PBR.
    632 
    633                          * The primitive tag identifies clearly the type of data, its value
    634                          * field indicates the file identifier and, if applicable, the SFI
    635                          * value of the specified EF. That is, the length value of a primitive
    636                          * tag indicates if an SFI value is available for the EF or not:
    637                          * - Length = '02' Value: 'EFID (2 bytes)'
    638                          * - Length = '03' Value: 'EFID (2 bytes)', 'SFI (1 byte)'
    639                          */
    640 
    641                         int sfi = INVALID_SFI;
    642                         data = tlv.getData();
    643 
    644                         if (data.length < 2 || data.length > 3) {
    645                             log("Invalid TLV length: " + data.length);
    646                             break;
    647                         }
    648 
    649                         if (data.length == 3) {
    650                             sfi = data[2] & 0xFF;
    651                         }
    652 
    653                         int efid = ((data[0] & 0xFF) << 8) | (data[1] & 0xFF);
    654 
    655                         mFileIds.put(tag, new File(parentTag, efid, sfi, tagNumberWithinParentTag));
    656                         break;
    657                 }
    658                 tagNumberWithinParentTag++;
    659             } while(tlv.nextObject());
    660         }
    661     }
    662 
    663     private void log(String msg) {
    664         if(DBG) Rlog.d(LOG_TAG, msg);
    665     }
    666 }