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 }