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