Move to Android N-MR1 SDK.
[android_tools.git] / sdk / sources / android-25 / com / android / internal / telephony / gsm / UsimPhoneBookManager.java
blob00ab0f4059980e9988569a0ad95e0142a3faeb00
1 /*
2 * Copyright (C) 2009 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.internal.telephony.gsm;
19 import android.os.AsyncResult;
20 import android.os.Handler;
21 import android.os.Message;
22 import android.telephony.Rlog;
23 import android.util.SparseArray;
24 import android.util.SparseIntArray;
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 java.util.ArrayList;
33 /**
34 * This class implements reading and parsing USIM records.
35 * Refer to Spec 3GPP TS 31.102 for more details.
37 * {@hide}
39 public class UsimPhoneBookManager extends Handler implements IccConstants {
40 private static final String LOG_TAG = "UsimPhoneBookManager";
41 private static final boolean DBG = true;
42 private ArrayList<PbrRecord> mPbrRecords;
43 private Boolean mIsPbrPresent;
44 private IccFileHandler mFh;
45 private AdnRecordCache mAdnCache;
46 private Object mLock = new Object();
47 private ArrayList<AdnRecord> mPhoneBookRecords;
48 private ArrayList<byte[]> mIapFileRecord;
49 private ArrayList<byte[]> mEmailFileRecord;
51 // email list for each ADN record. The key would be
52 // ADN's efid << 8 + record #
53 private SparseArray<ArrayList<String>> mEmailsForAdnRec;
55 // SFI to ADN Efid mapping table
56 private SparseIntArray mSfiEfidTable;
58 private boolean mRefreshCache = false;
61 private static final int EVENT_PBR_LOAD_DONE = 1;
62 private static final int EVENT_USIM_ADN_LOAD_DONE = 2;
63 private static final int EVENT_IAP_LOAD_DONE = 3;
64 private static final int EVENT_EMAIL_LOAD_DONE = 4;
66 private static final int USIM_TYPE1_TAG = 0xA8;
67 private static final int USIM_TYPE2_TAG = 0xA9;
68 private static final int USIM_TYPE3_TAG = 0xAA;
69 private static final int USIM_EFADN_TAG = 0xC0;
70 private static final int USIM_EFIAP_TAG = 0xC1;
71 private static final int USIM_EFEXT1_TAG = 0xC2;
72 private static final int USIM_EFSNE_TAG = 0xC3;
73 private static final int USIM_EFANR_TAG = 0xC4;
74 private static final int USIM_EFPBC_TAG = 0xC5;
75 private static final int USIM_EFGRP_TAG = 0xC6;
76 private static final int USIM_EFAAS_TAG = 0xC7;
77 private static final int USIM_EFGSD_TAG = 0xC8;
78 private static final int USIM_EFUID_TAG = 0xC9;
79 private static final int USIM_EFEMAIL_TAG = 0xCA;
80 private static final int USIM_EFCCP1_TAG = 0xCB;
82 private static final int INVALID_SFI = -1;
83 private static final byte INVALID_BYTE = -1;
85 // class File represent a PBR record TLV object which points to the rest of the phonebook EFs
86 private class File {
87 // Phonebook reference file constructed tag defined in 3GPP TS 31.102
88 // section 4.4.2.1 table 4.1
89 private final int mParentTag;
90 // EFID of the file
91 private final int mEfid;
92 // SFI (Short File Identification) of the file. 0xFF indicates invalid SFI.
93 private final int mSfi;
94 // The order of this tag showing in the PBR record.
95 private final int mIndex;
97 File(int parentTag, int efid, int sfi, int index) {
98 mParentTag = parentTag;
99 mEfid = efid;
100 mSfi = sfi;
101 mIndex = index;
104 public int getParentTag() { return mParentTag; }
105 public int getEfid() { return mEfid; }
106 public int getSfi() { return mSfi; }
107 public int getIndex() { return mIndex; }
110 public UsimPhoneBookManager(IccFileHandler fh, AdnRecordCache cache) {
111 mFh = fh;
112 mPhoneBookRecords = new ArrayList<AdnRecord>();
113 mPbrRecords = null;
114 // We assume its present, after the first read this is updated.
115 // So we don't have to read from UICC if its not present on subsequent reads.
116 mIsPbrPresent = true;
117 mAdnCache = cache;
118 mEmailsForAdnRec = new SparseArray<ArrayList<String>>();
119 mSfiEfidTable = new SparseIntArray();
122 public void reset() {
123 mPhoneBookRecords.clear();
124 mIapFileRecord = null;
125 mEmailFileRecord = null;
126 mPbrRecords = null;
127 mIsPbrPresent = true;
128 mRefreshCache = false;
129 mEmailsForAdnRec.clear();
130 mSfiEfidTable.clear();
133 // Load all phonebook related EFs from the SIM.
134 public ArrayList<AdnRecord> loadEfFilesFromUsim() {
135 synchronized (mLock) {
136 if (!mPhoneBookRecords.isEmpty()) {
137 if (mRefreshCache) {
138 mRefreshCache = false;
139 refreshCache();
141 return mPhoneBookRecords;
144 if (!mIsPbrPresent) return null;
146 // Check if the PBR file is present in the cache, if not read it
147 // from the USIM.
148 if (mPbrRecords == null) {
149 readPbrFileAndWait();
152 if (mPbrRecords == null)
153 return null;
155 int numRecs = mPbrRecords.size();
157 log("loadEfFilesFromUsim: Loading adn and emails");
158 for (int i = 0; i < numRecs; i++) {
159 readAdnFileAndWait(i);
160 readEmailFileAndWait(i);
163 updatePhoneAdnRecord();
164 // All EF files are loaded, return all the records
166 return mPhoneBookRecords;
169 // Refresh the phonebook cache.
170 private void refreshCache() {
171 if (mPbrRecords == null) return;
172 mPhoneBookRecords.clear();
174 int numRecs = mPbrRecords.size();
175 for (int i = 0; i < numRecs; i++) {
176 readAdnFileAndWait(i);
180 // Invalidate the phonebook cache.
181 public void invalidateCache() {
182 mRefreshCache = true;
185 // Read the phonebook reference file EF_PBR.
186 private void readPbrFileAndWait() {
187 mFh.loadEFLinearFixedAll(EF_PBR, obtainMessage(EVENT_PBR_LOAD_DONE));
188 try {
189 mLock.wait();
190 } catch (InterruptedException e) {
191 Rlog.e(LOG_TAG, "Interrupted Exception in readAdnFileAndWait");
195 // Read EF_EMAIL which contains the email records.
196 private void readEmailFileAndWait(int recId) {
197 SparseArray<File> files;
198 files = mPbrRecords.get(recId).mFileIds;
199 if (files == null) return;
201 File email = files.get(USIM_EFEMAIL_TAG);
202 if (email != null) {
205 * Check if the EF_EMAIL is a Type 1 file or a type 2 file.
206 * If mEmailPresentInIap is true, its a type 2 file.
207 * So we read the IAP file and then read the email records.
208 * instead of reading directly.
210 if (email.getParentTag() == USIM_TYPE2_TAG) {
211 if (files.get(USIM_EFIAP_TAG) == null) {
212 Rlog.e(LOG_TAG, "Can't locate EF_IAP in EF_PBR.");
213 return;
216 log("EF_IAP exists. Loading EF_IAP to retrieve the index.");
217 readIapFileAndWait(files.get(USIM_EFIAP_TAG).getEfid());
218 if (mIapFileRecord == null) {
219 Rlog.e(LOG_TAG, "Error: IAP file is empty");
220 return;
223 log("EF_EMAIL order in PBR record: " + email.getIndex());
226 int emailEfid = email.getEfid();
227 log("EF_EMAIL exists in PBR. efid = 0x" +
228 Integer.toHexString(emailEfid).toUpperCase());
231 * Make sure this EF_EMAIL was never read earlier. Sometimes two PBR record points
233 // to the same EF_EMAIL
234 for (int i = 0; i < recId; i++) {
235 if (mPbrRecords.get(i) != null) {
236 SparseArray<File> previousFileIds = mPbrRecords.get(i).mFileIds;
237 if (previousFileIds != null) {
238 File id = previousFileIds.get(USIM_EFEMAIL_TAG);
239 if (id != null && id.getEfid() == emailEfid) {
240 log("Skipped this EF_EMAIL which was loaded earlier");
241 return;
247 // Read the EFEmail file.
248 mFh.loadEFLinearFixedAll(emailEfid,
249 obtainMessage(EVENT_EMAIL_LOAD_DONE));
250 try {
251 mLock.wait();
252 } catch (InterruptedException e) {
253 Rlog.e(LOG_TAG, "Interrupted Exception in readEmailFileAndWait");
256 if (mEmailFileRecord == null) {
257 Rlog.e(LOG_TAG, "Error: Email file is empty");
258 return;
261 // Build email list
262 if (email.getParentTag() == USIM_TYPE2_TAG && mIapFileRecord != null) {
263 // If the tag is type 2 and EF_IAP exists, we need to build tpe 2 email list
264 buildType2EmailList(recId);
266 else {
267 // If one the followings is true, we build type 1 email list
268 // 1. EF_IAP does not exist or it is failed to load
269 // 2. ICC cards can be made such that they have an IAP file but all
270 // records are empty. In that case buildType2EmailList will fail and
271 // we need to build type 1 email list.
273 // Build type 1 email list
274 buildType1EmailList(recId);
279 // Build type 1 email list
280 private void buildType1EmailList(int recId) {
282 * If this is type 1, the number of records in EF_EMAIL would be same as the record number
283 * in the master/reference file.
285 if (mPbrRecords.get(recId) == null)
286 return;
288 int numRecs = mPbrRecords.get(recId).mMasterFileRecordNum;
289 log("Building type 1 email list. recId = "
290 + recId + ", numRecs = " + numRecs);
292 byte[] emailRec;
293 for (int i = 0; i < numRecs; i++) {
294 try {
295 emailRec = mEmailFileRecord.get(i);
296 } catch (IndexOutOfBoundsException e) {
297 Rlog.e(LOG_TAG, "Error: Improper ICC card: No email record for ADN, continuing");
298 break;
302 * 3GPP TS 31.102 4.4.2.13 EF_EMAIL (e-mail address)
304 * The fields below are mandatory if and only if the file
305 * is not type 1 (as specified in EF_PBR)
307 * Byte [X + 1]: ADN file SFI (Short File Identification)
308 * Byte [X + 2]: ADN file Record Identifier
310 int sfi = emailRec[emailRec.length - 2];
311 int adnRecId = emailRec[emailRec.length - 1];
313 String email = readEmailRecord(i);
315 if (email == null || email.equals("")) {
316 continue;
319 // Get the associated ADN's efid first.
320 int adnEfid = 0;
321 if (sfi == INVALID_SFI || mSfiEfidTable.get(sfi) == 0) {
323 // If SFI is invalid or cannot be mapped to any ADN, use the ADN's efid
324 // in the same PBR files.
325 File file = mPbrRecords.get(recId).mFileIds.get(USIM_EFADN_TAG);
326 if (file == null)
327 continue;
328 adnEfid = file.getEfid();
330 else {
331 adnEfid = mSfiEfidTable.get(sfi);
334 * SIM record numbers are 1 based.
335 * The key is constructed by efid and record index.
337 int index = (((adnEfid & 0xFFFF) << 8) | ((adnRecId - 1) & 0xFF));
338 ArrayList<String> emailList = mEmailsForAdnRec.get(index);
339 if (emailList == null) {
340 emailList = new ArrayList<String>();
342 log("Adding email #" + i + " list to index 0x" +
343 Integer.toHexString(index).toUpperCase());
344 emailList.add(email);
345 mEmailsForAdnRec.put(index, emailList);
349 // Build type 2 email list
350 private boolean buildType2EmailList(int recId) {
352 if (mPbrRecords.get(recId) == null)
353 return false;
355 int numRecs = mPbrRecords.get(recId).mMasterFileRecordNum;
356 log("Building type 2 email list. recId = "
357 + recId + ", numRecs = " + numRecs);
360 * 3GPP TS 31.102 4.4.2.1 EF_PBR (Phone Book Reference file) table 4.1
362 * The number of records in the IAP file is same as the number of records in the master
363 * file (e.g EF_ADN). The order of the pointers in an EF_IAP shall be the same as the
364 * order of file IDs that appear in the TLV object indicated by Tag 'A9' in the
365 * reference file record (e.g value of mEmailTagNumberInIap)
368 File adnFile = mPbrRecords.get(recId).mFileIds.get(USIM_EFADN_TAG);
369 if (adnFile == null) {
370 Rlog.e(LOG_TAG, "Error: Improper ICC card: EF_ADN does not exist in PBR files");
371 return false;
373 int adnEfid = adnFile.getEfid();
375 for (int i = 0; i < numRecs; i++) {
376 byte[] record;
377 int emailRecId;
378 try {
379 record = mIapFileRecord.get(i);
380 emailRecId =
381 record[mPbrRecords.get(recId).mFileIds.get(USIM_EFEMAIL_TAG).getIndex()];
382 } catch (IndexOutOfBoundsException e) {
383 Rlog.e(LOG_TAG, "Error: Improper ICC card: Corrupted EF_IAP");
384 continue;
387 String email = readEmailRecord(emailRecId - 1);
388 if (email != null && !email.equals("")) {
389 // The key is constructed by efid and record index.
390 int index = (((adnEfid & 0xFFFF) << 8) | (i & 0xFF));
391 ArrayList<String> emailList = mEmailsForAdnRec.get(index);
392 if (emailList == null) {
393 emailList = new ArrayList<String>();
395 emailList.add(email);
396 log("Adding email list to index 0x" +
397 Integer.toHexString(index).toUpperCase());
398 mEmailsForAdnRec.put(index, emailList);
401 return true;
404 // Read Phonebook Index Admistration EF_IAP file
405 private void readIapFileAndWait(int efid) {
406 mFh.loadEFLinearFixedAll(efid, obtainMessage(EVENT_IAP_LOAD_DONE));
407 try {
408 mLock.wait();
409 } catch (InterruptedException e) {
410 Rlog.e(LOG_TAG, "Interrupted Exception in readIapFileAndWait");
414 private void updatePhoneAdnRecord() {
416 int numAdnRecs = mPhoneBookRecords.size();
418 for (int i = 0; i < numAdnRecs; i++) {
420 AdnRecord rec = mPhoneBookRecords.get(i);
422 int adnEfid = rec.getEfid();
423 int adnRecId = rec.getRecId();
425 int index = (((adnEfid & 0xFFFF) << 8) | ((adnRecId - 1) & 0xFF));
427 ArrayList<String> emailList;
428 try {
429 emailList = mEmailsForAdnRec.get(index);
430 } catch (IndexOutOfBoundsException e) {
431 continue;
434 if (emailList == null)
435 continue;
437 String[] emails = new String[emailList.size()];
438 System.arraycopy(emailList.toArray(), 0, emails, 0, emailList.size());
439 rec.setEmails(emails);
440 log("Adding email list to ADN (0x" +
441 Integer.toHexString(mPhoneBookRecords.get(i).getEfid()).toUpperCase() +
442 ") record #" + mPhoneBookRecords.get(i).getRecId());
443 mPhoneBookRecords.set(i, rec);
447 // Read email from the record of EF_EMAIL
448 private String readEmailRecord(int recId) {
449 byte[] emailRec;
450 try {
451 emailRec = mEmailFileRecord.get(recId);
452 } catch (IndexOutOfBoundsException e) {
453 return null;
456 // The length of the record is X+2 byte, where X bytes is the email address
457 return IccUtils.adnStringFieldToString(emailRec, 0, emailRec.length - 2);
460 // Read EF_ADN file
461 private void readAdnFileAndWait(int recId) {
462 SparseArray<File> files;
463 files = mPbrRecords.get(recId).mFileIds;
464 if (files == null || files.size() == 0) return;
466 int extEf = 0;
467 // Only call fileIds.get while EF_EXT1_TAG is available
468 if (files.get(USIM_EFEXT1_TAG) != null) {
469 extEf = files.get(USIM_EFEXT1_TAG).getEfid();
472 if (files.get(USIM_EFADN_TAG) == null)
473 return;
475 int previousSize = mPhoneBookRecords.size();
476 mAdnCache.requestLoadAllAdnLike(files.get(USIM_EFADN_TAG).getEfid(),
477 extEf, obtainMessage(EVENT_USIM_ADN_LOAD_DONE));
478 try {
479 mLock.wait();
480 } catch (InterruptedException e) {
481 Rlog.e(LOG_TAG, "Interrupted Exception in readAdnFileAndWait");
485 * The recent added ADN record # would be the reference record size
486 * for the rest of EFs associated within this PBR.
488 mPbrRecords.get(recId).mMasterFileRecordNum = mPhoneBookRecords.size() - previousSize;
491 // Create the phonebook reference file based on EF_PBR
492 private void createPbrFile(ArrayList<byte[]> records) {
493 if (records == null) {
494 mPbrRecords = null;
495 mIsPbrPresent = false;
496 return;
499 mPbrRecords = new ArrayList<PbrRecord>();
500 for (int i = 0; i < records.size(); i++) {
501 // Some cards have two records but the 2nd record is filled with all invalid char 0xff.
502 // So we need to check if the record is valid or not before adding into the PBR records.
503 if (records.get(i)[0] != INVALID_BYTE) {
504 mPbrRecords.add(new PbrRecord(records.get(i)));
508 for (PbrRecord record : mPbrRecords) {
509 File file = record.mFileIds.get(USIM_EFADN_TAG);
510 // If the file does not contain EF_ADN, we'll just skip it.
511 if (file != null) {
512 int sfi = file.getSfi();
513 if (sfi != INVALID_SFI) {
514 mSfiEfidTable.put(sfi, record.mFileIds.get(USIM_EFADN_TAG).getEfid());
520 @Override
521 public void handleMessage(Message msg) {
522 AsyncResult ar;
524 switch(msg.what) {
525 case EVENT_PBR_LOAD_DONE:
526 ar = (AsyncResult) msg.obj;
527 if (ar.exception == null) {
528 createPbrFile((ArrayList<byte[]>)ar.result);
530 synchronized (mLock) {
531 mLock.notify();
533 break;
534 case EVENT_USIM_ADN_LOAD_DONE:
535 log("Loading USIM ADN records done");
536 ar = (AsyncResult) msg.obj;
537 if (ar.exception == null) {
538 mPhoneBookRecords.addAll((ArrayList<AdnRecord>)ar.result);
540 synchronized (mLock) {
541 mLock.notify();
543 break;
544 case EVENT_IAP_LOAD_DONE:
545 log("Loading USIM IAP records done");
546 ar = (AsyncResult) msg.obj;
547 if (ar.exception == null) {
548 mIapFileRecord = ((ArrayList<byte[]>)ar.result);
550 synchronized (mLock) {
551 mLock.notify();
553 break;
554 case EVENT_EMAIL_LOAD_DONE:
555 log("Loading USIM Email records done");
556 ar = (AsyncResult) msg.obj;
557 if (ar.exception == null) {
558 mEmailFileRecord = ((ArrayList<byte[]>)ar.result);
561 synchronized (mLock) {
562 mLock.notify();
564 break;
568 // PbrRecord represents a record in EF_PBR
569 private class PbrRecord {
570 // TLV tags
571 private SparseArray<File> mFileIds;
574 * 3GPP TS 31.102 4.4.2.1 EF_PBR (Phone Book Reference file)
575 * If this is type 1 files, files that contain as many records as the
576 * reference/master file (EF_ADN, EF_ADN1) and are linked on record number
577 * bases (Rec1 -> Rec1). The master file record number is the reference.
579 private int mMasterFileRecordNum;
581 PbrRecord(byte[] record) {
582 mFileIds = new SparseArray<File>();
583 SimTlv recTlv;
584 log("PBR rec: " + IccUtils.bytesToHexString(record));
585 recTlv = new SimTlv(record, 0, record.length);
586 parseTag(recTlv);
589 void parseTag(SimTlv tlv) {
590 SimTlv tlvEfSfi;
591 int tag;
592 byte[] data;
594 do {
595 tag = tlv.getTag();
596 switch(tag) {
597 case USIM_TYPE1_TAG: // A8
598 case USIM_TYPE3_TAG: // AA
599 case USIM_TYPE2_TAG: // A9
600 data = tlv.getData();
601 tlvEfSfi = new SimTlv(data, 0, data.length);
602 parseEfAndSFI(tlvEfSfi, tag);
603 break;
605 } while (tlv.nextObject());
608 void parseEfAndSFI(SimTlv tlv, int parentTag) {
609 int tag;
610 byte[] data;
611 int tagNumberWithinParentTag = 0;
612 do {
613 tag = tlv.getTag();
614 switch(tag) {
615 case USIM_EFEMAIL_TAG:
616 case USIM_EFADN_TAG:
617 case USIM_EFEXT1_TAG:
618 case USIM_EFANR_TAG:
619 case USIM_EFPBC_TAG:
620 case USIM_EFGRP_TAG:
621 case USIM_EFAAS_TAG:
622 case USIM_EFGSD_TAG:
623 case USIM_EFUID_TAG:
624 case USIM_EFCCP1_TAG:
625 case USIM_EFIAP_TAG:
626 case USIM_EFSNE_TAG:
627 /** 3GPP TS 31.102, 4.4.2.1 EF_PBR (Phone Book Reference file)
629 * The SFI value assigned to an EF which is indicated in EF_PBR shall
630 * correspond to the SFI indicated in the TLV object in EF_PBR.
632 * The primitive tag identifies clearly the type of data, its value
633 * field indicates the file identifier and, if applicable, the SFI
634 * value of the specified EF. That is, the length value of a primitive
635 * tag indicates if an SFI value is available for the EF or not:
636 * - Length = '02' Value: 'EFID (2 bytes)'
637 * - Length = '03' Value: 'EFID (2 bytes)', 'SFI (1 byte)'
640 int sfi = INVALID_SFI;
641 data = tlv.getData();
643 if (data.length < 2 || data.length > 3) {
644 log("Invalid TLV length: " + data.length);
645 break;
648 if (data.length == 3) {
649 sfi = data[2] & 0xFF;
652 int efid = ((data[0] & 0xFF) << 8) | (data[1] & 0xFF);
654 mFileIds.put(tag, new File(parentTag, efid, sfi, tagNumberWithinParentTag));
655 break;
657 tagNumberWithinParentTag++;
658 } while(tlv.nextObject());
662 private void log(String msg) {
663 if(DBG) Rlog.d(LOG_TAG, msg);