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 }