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