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