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 
File(int parentTag, int efid, int sfi, int index)97         File(int parentTag, int efid, int sfi, int index) {
98             mParentTag = parentTag;
99             mEfid = efid;
100             mSfi = sfi;
101             mIndex = index;
102         }
103 
getParentTag()104         public int getParentTag() { return mParentTag; }
getEfid()105         public int getEfid() { return mEfid; }
getSfi()106         public int getSfi() { return mSfi; }
getIndex()107         public int getIndex() { return mIndex; }
108     }
109 
UsimPhoneBookManager(IccFileHandler fh, AdnRecordCache cache)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 
reset()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.
loadEfFilesFromUsim()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.
refreshCache()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.
invalidateCache()181     public void invalidateCache() {
182         mRefreshCache = true;
183     }
184 
185     // Read the phonebook reference file EF_PBR.
readPbrFileAndWait()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.
readEmailFileAndWait(int recId)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
buildType1EmailList(int recId)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
buildType2EmailList(int recId)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
readIapFileAndWait(int efid)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 
updatePhoneAdnRecord()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
readEmailRecord(int recId)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
readAdnFileAndWait(int recId)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
createPbrFile(ArrayList<byte[]> records)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
handleMessage(Message msg)521     public void handleMessage(Message msg) {
522         AsyncResult ar;
523 
524         switch(msg.what) {
525         case EVENT_PBR_LOAD_DONE:
526             ar = (AsyncResult) msg.obj;
527             if (ar.exception == null) {
528                 createPbrFile((ArrayList<byte[]>)ar.result);
529             }
530             synchronized (mLock) {
531                 mLock.notify();
532             }
533             break;
534         case EVENT_USIM_ADN_LOAD_DONE:
535             log("Loading USIM ADN records done");
536             ar = (AsyncResult) msg.obj;
537             if (ar.exception == null) {
538                 mPhoneBookRecords.addAll((ArrayList<AdnRecord>)ar.result);
539             }
540             synchronized (mLock) {
541                 mLock.notify();
542             }
543             break;
544         case EVENT_IAP_LOAD_DONE:
545             log("Loading USIM IAP records done");
546             ar = (AsyncResult) msg.obj;
547             if (ar.exception == null) {
548                 mIapFileRecord = ((ArrayList<byte[]>)ar.result);
549             }
550             synchronized (mLock) {
551                 mLock.notify();
552             }
553             break;
554         case EVENT_EMAIL_LOAD_DONE:
555             log("Loading USIM Email records done");
556             ar = (AsyncResult) msg.obj;
557             if (ar.exception == null) {
558                 mEmailFileRecord = ((ArrayList<byte[]>)ar.result);
559             }
560 
561             synchronized (mLock) {
562                 mLock.notify();
563             }
564             break;
565         }
566     }
567 
568     // PbrRecord represents a record in EF_PBR
569     private class PbrRecord {
570         // TLV tags
571         private SparseArray<File> mFileIds;
572 
573         /**
574          * 3GPP TS 31.102 4.4.2.1 EF_PBR (Phone Book Reference file)
575          * If this is type 1 files, files that contain as many records as the
576          * reference/master file (EF_ADN, EF_ADN1) and are linked on record number
577          * bases (Rec1 -> Rec1). The master file record number is the reference.
578          */
579         private int mMasterFileRecordNum;
580 
PbrRecord(byte[] record)581         PbrRecord(byte[] record) {
582             mFileIds = new SparseArray<File>();
583             SimTlv recTlv;
584             log("PBR rec: " + IccUtils.bytesToHexString(record));
585             recTlv = new SimTlv(record, 0, record.length);
586             parseTag(recTlv);
587         }
588 
parseTag(SimTlv tlv)589         void parseTag(SimTlv tlv) {
590             SimTlv tlvEfSfi;
591             int tag;
592             byte[] data;
593 
594             do {
595                 tag = tlv.getTag();
596                 switch(tag) {
597                 case USIM_TYPE1_TAG: // A8
598                 case USIM_TYPE3_TAG: // AA
599                 case USIM_TYPE2_TAG: // A9
600                     data = tlv.getData();
601                     tlvEfSfi = new SimTlv(data, 0, data.length);
602                     parseEfAndSFI(tlvEfSfi, tag);
603                     break;
604                 }
605             } while (tlv.nextObject());
606         }
607 
parseEfAndSFI(SimTlv tlv, int parentTag)608         void parseEfAndSFI(SimTlv tlv, int parentTag) {
609             int tag;
610             byte[] data;
611             int tagNumberWithinParentTag = 0;
612             do {
613                 tag = tlv.getTag();
614                 switch(tag) {
615                     case USIM_EFEMAIL_TAG:
616                     case USIM_EFADN_TAG:
617                     case USIM_EFEXT1_TAG:
618                     case USIM_EFANR_TAG:
619                     case USIM_EFPBC_TAG:
620                     case USIM_EFGRP_TAG:
621                     case USIM_EFAAS_TAG:
622                     case USIM_EFGSD_TAG:
623                     case USIM_EFUID_TAG:
624                     case USIM_EFCCP1_TAG:
625                     case USIM_EFIAP_TAG:
626                     case USIM_EFSNE_TAG:
627                         /** 3GPP TS 31.102, 4.4.2.1 EF_PBR (Phone Book Reference file)
628                          *
629                          * The SFI value assigned to an EF which is indicated in EF_PBR shall
630                          * correspond to the SFI indicated in the TLV object in EF_PBR.
631 
632                          * The primitive tag identifies clearly the type of data, its value
633                          * field indicates the file identifier and, if applicable, the SFI
634                          * value of the specified EF. That is, the length value of a primitive
635                          * tag indicates if an SFI value is available for the EF or not:
636                          * - Length = '02' Value: 'EFID (2 bytes)'
637                          * - Length = '03' Value: 'EFID (2 bytes)', 'SFI (1 byte)'
638                          */
639 
640                         int sfi = INVALID_SFI;
641                         data = tlv.getData();
642 
643                         if (data.length < 2 || data.length > 3) {
644                             log("Invalid TLV length: " + data.length);
645                             break;
646                         }
647 
648                         if (data.length == 3) {
649                             sfi = data[2] & 0xFF;
650                         }
651 
652                         int efid = ((data[0] & 0xFF) << 8) | (data[1] & 0xFF);
653 
654                         mFileIds.put(tag, new File(parentTag, efid, sfi, tagNumberWithinParentTag));
655                         break;
656                 }
657                 tagNumberWithinParentTag++;
658             } while(tlv.nextObject());
659         }
660     }
661 
log(String msg)662     private void log(String msg) {
663         if(DBG) Rlog.d(LOG_TAG, msg);
664     }
665 }