diff options
9 files changed, 564 insertions, 47 deletions
diff --git a/telephony/java/com/android/internal/telephony/AdnRecord.java b/telephony/java/com/android/internal/telephony/AdnRecord.java index 5f40579..0896ba6 100644 --- a/telephony/java/com/android/internal/telephony/AdnRecord.java +++ b/telephony/java/com/android/internal/telephony/AdnRecord.java @@ -23,6 +23,8 @@ import android.util.Log; import com.android.internal.telephony.GsmAlphabet; +import java.util.Arrays; + /** * @@ -38,6 +40,7 @@ public class AdnRecord implements Parcelable { String alphaTag = ""; String number = ""; + String[] emails; int extRecord = 0xff; int efid; // or 0 if none int recordNumber; // or 0 if none @@ -74,13 +77,15 @@ public class AdnRecord implements Parcelable { int recordNumber; String alphaTag; String number; + String[] emails; efid = source.readInt(); recordNumber = source.readInt(); alphaTag = source.readString(); number = source.readString(); + emails = source.readStringArray(); - return new AdnRecord(efid, recordNumber, alphaTag, number); + return new AdnRecord(efid, recordNumber, alphaTag, number, emails); } public AdnRecord[] newArray(int size) { @@ -90,29 +95,38 @@ public class AdnRecord implements Parcelable { //***** Constructor - public - AdnRecord (byte[] record) { + public AdnRecord (byte[] record) { this(0, 0, record); } - public - AdnRecord (int efid, int recordNumber, byte[] record) { + public AdnRecord (int efid, int recordNumber, byte[] record) { this.efid = efid; this.recordNumber = recordNumber; parseRecord(record); } - public - AdnRecord (String alphaTag, String number) { + public AdnRecord (String alphaTag, String number) { this(0, 0, alphaTag, number); } - public - AdnRecord (int efid, int recordNumber, String alphaTag, String number) { + public AdnRecord (String alphaTag, String number, String[] emails) { + this(0, 0, alphaTag, number, emails); + } + + public AdnRecord (int efid, int recordNumber, String alphaTag, String number, String[] emails) { + this.efid = efid; + this.recordNumber = recordNumber; + this.alphaTag = alphaTag; + this.number = number; + this.emails = emails; + } + + public AdnRecord(int efid, int recordNumber, String alphaTag, String number) { this.efid = efid; this.recordNumber = recordNumber; this.alphaTag = alphaTag; this.number = number; + this.emails = null; } //***** Instance Methods @@ -125,12 +139,20 @@ public class AdnRecord implements Parcelable { return number; } + public String[] getEmails() { + return emails; + } + + public void setEmails(String[] emails) { + this.emails = emails; + } + public String toString() { - return "ADN Record '" + alphaTag + "' '" + number + "'"; + return "ADN Record '" + alphaTag + "' '" + number + " " + emails + "'"; } public boolean isEmpty() { - return alphaTag.equals("") && number.equals(""); + return alphaTag.equals("") && number.equals("") && emails == null; } public boolean hasExtendedRecord() { @@ -139,7 +161,8 @@ public class AdnRecord implements Parcelable { public boolean isEqual(AdnRecord adn) { return ( alphaTag.equals(adn.getAlphaTag()) && - number.equals(adn.getNumber()) ); + number.equals(adn.getNumber()) && + Arrays.equals(emails, adn.getEmails())); } //***** Parcelable Implementation @@ -152,6 +175,7 @@ public class AdnRecord implements Parcelable { dest.writeInt(recordNumber); dest.writeString(alphaTag); dest.writeString(number); + dest.writeStringArray(emails); } /** @@ -274,10 +298,13 @@ public class AdnRecord implements Parcelable { extRecord = 0xff & record[record.length - 1]; + emails = null; + } catch (RuntimeException ex) { Log.w(LOG_TAG, "Error parsing AdnRecord", ex); number = ""; alphaTag = ""; + emails = null; } } } diff --git a/telephony/java/com/android/internal/telephony/AdnRecordCache.java b/telephony/java/com/android/internal/telephony/AdnRecordCache.java index c270ae5..c8c0658 100644 --- a/telephony/java/com/android/internal/telephony/AdnRecordCache.java +++ b/telephony/java/com/android/internal/telephony/AdnRecordCache.java @@ -16,14 +16,16 @@ package com.android.internal.telephony; -import android.util.SparseArray; -import android.util.Log; -import android.os.Message; -import android.os.Handler; import android.os.AsyncResult; +import android.os.Handler; +import android.os.Message; +import android.util.Log; +import android.util.SparseArray; + +import com.android.internal.telephony.gsm.UsimPhoneBookManager; + import java.util.ArrayList; import java.util.Iterator; -import com.android.internal.telephony.IccConstants; /** * {@hide} @@ -32,6 +34,7 @@ public final class AdnRecordCache extends Handler implements IccConstants { //***** Instance Variables PhoneBase phone; + private UsimPhoneBookManager mUsimPhoneBookManager; // Indexed by EF ID SparseArray<ArrayList<AdnRecord>> adnLikeFiles @@ -55,6 +58,7 @@ public final class AdnRecordCache extends Handler implements IccConstants { public AdnRecordCache(PhoneBase phone) { this.phone = phone; + mUsimPhoneBookManager = new UsimPhoneBookManager(phone, this); } //***** Called from SIMRecords @@ -64,6 +68,7 @@ public final class AdnRecordCache extends Handler implements IccConstants { */ public void reset() { adnLikeFiles.clear(); + mUsimPhoneBookManager.reset(); clearWaiters(); clearUserWriters(); @@ -103,14 +108,14 @@ public final class AdnRecordCache extends Handler implements IccConstants { * * See 3GPP TS 51.011 for this mapping */ - private int - extensionEfForEf(int efid) { + int extensionEfForEf(int efid) { switch (efid) { case EF_MBDN: return EF_EXT6; case EF_ADN: return EF_EXT1; case EF_SDN: return EF_EXT3; case EF_FDN: return EF_EXT2; case EF_MSISDN: return EF_EXT1; + case EF_PBR: return 0; // The EF PBR doesn't have an extension record default: return -1; } } @@ -223,11 +228,15 @@ public final class AdnRecordCache extends Handler implements IccConstants { * record */ public void - requestLoadAllAdnLike (int efid, Message response) { + requestLoadAllAdnLike (int efid, int extensionEf, Message response) { ArrayList<Message> waiters; ArrayList<AdnRecord> result; - result = getRecordsIfLoaded(efid); + if (efid == EF_PBR) { + result = mUsimPhoneBookManager.loadEfFilesFromUsim(); + } else { + result = getRecordsIfLoaded(efid); + } // Have we already loaded this efid? if (result != null) { @@ -258,9 +267,8 @@ public final class AdnRecordCache extends Handler implements IccConstants { adnLikeWaiters.put(efid, waiters); - int extensionEF = extensionEfForEf(efid); - if (extensionEF < 0) { + if (extensionEf < 0) { // respond with error if not known ADN-like record if (response != null) { @@ -272,7 +280,7 @@ public final class AdnRecordCache extends Handler implements IccConstants { return; } - new AdnRecordLoader(phone).loadAllFromEF(efid, extensionEF, + new AdnRecordLoader(phone).loadAllFromEF(efid, extensionEf, obtainMessage(EVENT_LOAD_ALL_ADN_LIKE_DONE, efid, 0)); } @@ -311,7 +319,7 @@ public final class AdnRecordCache extends Handler implements IccConstants { adnLikeWaiters.delete(efid); if (ar.exception == null) { - adnLikeFiles.put(efid, (ArrayList<AdnRecord>) (ar.result)); + adnLikeFiles.put(efid, (ArrayList<AdnRecord>) ar.result); } notifyWaiters(waiters, ar); break; diff --git a/telephony/java/com/android/internal/telephony/IccCard.java b/telephony/java/com/android/internal/telephony/IccCard.java index 200340e..0f76633 100644 --- a/telephony/java/com/android/internal/telephony/IccCard.java +++ b/telephony/java/com/android/internal/telephony/IccCard.java @@ -655,7 +655,7 @@ public abstract class IccCard { } - public boolean hasApplicationType(IccCardApplication.AppType type) { + public boolean isApplicationOnIcc(IccCardApplication.AppType type) { if (mIccCardStatus == null) return false; for (int i = 0 ; i < mIccCardStatus.getNumApplications(); i++) { diff --git a/telephony/java/com/android/internal/telephony/IccConstants.java b/telephony/java/com/android/internal/telephony/IccConstants.java index 7eafafd..acc9197 100644 --- a/telephony/java/com/android/internal/telephony/IccConstants.java +++ b/telephony/java/com/android/internal/telephony/IccConstants.java @@ -42,6 +42,9 @@ public interface IccConstants { static final int EF_CFIS = 0x6FCB; static final int EF_IMG = 0x4f20; + // USIM SIM file ids from TS 31.102 + public static final int EF_PBR = 0x4F30; + // GSM SIM file ids from CPHS (phase 2, version 4.2) CPHS4_2.WW6 static final int EF_MAILBOX_CPHS = 0x6F17; static final int EF_VOICE_MAIL_INDICATOR_CPHS = 0x6F11; @@ -59,6 +62,7 @@ public interface IccConstants { static final String MF_SIM = "3F00"; static final String DF_TELECOM = "7F10"; + static final String DF_PHONEBOOK = "5F3A"; static final String DF_GRAPHICS = "5F50"; static final String DF_GSM = "7F20"; static final String DF_CDMA = "7F25"; diff --git a/telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java b/telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java index 0bcaaa6..31cf6a7 100644 --- a/telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java +++ b/telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java @@ -115,7 +115,8 @@ public abstract class IccPhoneBookInterfaceManager extends IIccPhoneBook.Stub { * Replace oldAdn with newAdn in ADN-like record in EF * * getAdnRecordsInEf must be called at least once before this function, - * otherwise an error will be returned + * otherwise an error will be returned. Currently the email field + * if set in the ADN record is ignored. * throws SecurityException if no WRITE_CONTACTS permission * * @param efid must be one among EF_ADN, EF_FDN, and EF_SDN @@ -167,7 +168,8 @@ public abstract class IccPhoneBookInterfaceManager extends IIccPhoneBook.Stub { * Update an ADN-like EF record by record index * * This is useful for iteration the whole ADN file, such as write the whole - * phone book or erase/format the whole phonebook + * phone book or erase/format the whole phonebook. Currently the email field + * if set in the ADN record is ignored. * throws SecurityException if no WRITE_CONTACTS permission * * @param efid must be one among EF_ADN, EF_FDN, and EF_SDN @@ -237,12 +239,13 @@ public abstract class IccPhoneBookInterfaceManager extends IIccPhoneBook.Stub { "Requires android.permission.READ_CONTACTS permission"); } + efid = updateEfForIccType(efid); if (DBG) logd("getAdnRecordsInEF: efid=" + efid); synchronized(mLock) { checkThread(); Message response = mBaseHandler.obtainMessage(EVENT_LOAD_DONE); - adnCache.requestLoadAllAdnLike(efid, response); + adnCache.requestLoadAllAdnLike(efid, adnCache.extensionEfForEf(efid), response); try { mLock.wait(); } catch (InterruptedException e) { @@ -262,5 +265,15 @@ public abstract class IccPhoneBookInterfaceManager extends IIccPhoneBook.Stub { } } } + + private int updateEfForIccType(int efid) { + // Check if we are trying to read ADN records + if (efid == IccConstants.EF_ADN) { + if (phone.getIccCard().isApplicationOnIcc(IccCardApplication.AppType.APPTYPE_USIM)) { + return IccConstants.EF_PBR; + } + } + return efid; + } } diff --git a/telephony/java/com/android/internal/telephony/IccProvider.java b/telephony/java/com/android/internal/telephony/IccProvider.java index 4cbd779..ffcfe6f 100644 --- a/telephony/java/com/android/internal/telephony/IccProvider.java +++ b/telephony/java/com/android/internal/telephony/IccProvider.java @@ -46,7 +46,8 @@ public class IccProvider extends ContentProvider { private static final String[] ADDRESS_BOOK_COLUMN_NAMES = new String[] { "name", - "number" + "number", + "emails" }; private static final int ADN = 1; @@ -55,6 +56,7 @@ public class IccProvider extends ContentProvider { private static final String STR_TAG = "tag"; private static final String STR_NUMBER = "number"; + private static final String STR_EMAILS = "emails"; private static final String STR_PIN2 = "pin2"; private static final UriMatcher URL_MATCHER = @@ -172,7 +174,8 @@ public class IccProvider extends ContentProvider { String tag = initialValues.getAsString("tag"); String number = initialValues.getAsString("number"); - boolean success = addIccRecordToEf(efType, tag, number, pin2); + // TODO(): Read email instead of sending null. + boolean success = addIccRecordToEf(efType, tag, number, null, pin2); if (!success) { return null; @@ -238,6 +241,7 @@ public class IccProvider extends ContentProvider { // parse where clause String tag = null; String number = null; + String[] emails = null; String pin2 = null; String[] tokens = where.split("AND"); @@ -261,6 +265,9 @@ public class IccProvider extends ContentProvider { tag = normalizeValue(val); } else if (STR_NUMBER.equals(key)) { number = normalizeValue(val); + } else if (STR_EMAILS.equals(key)) { + //TODO(): Email is null. + emails = null; } else if (STR_PIN2.equals(key)) { pin2 = normalizeValue(val); } @@ -274,7 +281,7 @@ public class IccProvider extends ContentProvider { return 0; } - boolean success = deleteIccRecordFromEf(efType, tag, number, pin2); + boolean success = deleteIccRecordFromEf(efType, tag, number, emails, pin2); if (!success) { return 0; } @@ -307,9 +314,11 @@ public class IccProvider extends ContentProvider { String tag = values.getAsString("tag"); String number = values.getAsString("number"); + String[] emails = null; String newTag = values.getAsString("newTag"); String newNumber = values.getAsString("newNumber"); - + String[] newEmails = null; + // TODO(): Update for email. boolean success = updateIccRecordInEf(efType, tag, number, newTag, newNumber, pin2); @@ -355,9 +364,9 @@ public class IccProvider extends ContentProvider { } private boolean - addIccRecordToEf(int efType, String name, String number, String pin2) { + addIccRecordToEf(int efType, String name, String number, String[] emails, String pin2) { if (DBG) log("addIccRecordToEf: efType=" + efType + ", name=" + name + - ", number=" + number); + ", number=" + number + ", emails=" + emails); boolean success = false; @@ -384,7 +393,7 @@ public class IccProvider extends ContentProvider { private boolean updateIccRecordInEf(int efType, String oldName, String oldNumber, - String newName, String newNumber,String pin2) { + String newName, String newNumber, String pin2) { if (DBG) log("updateIccRecordInEf: efType=" + efType + ", oldname=" + oldName + ", oldnumber=" + oldNumber + ", newname=" + newName + ", newnumber=" + newNumber); @@ -407,9 +416,10 @@ public class IccProvider extends ContentProvider { } - private boolean deleteIccRecordFromEf(int efType, String name, String number, String pin2) { + private boolean deleteIccRecordFromEf(int efType, String name, String number, String[] emails, + String pin2) { if (DBG) log("deleteIccRecordFromEf: efType=" + efType + - ", name=" + name + ", number=" + number + ", pin2=" + pin2); + ", name=" + name + ", number=" + number + ", emails=" + emails + ", pin2=" + pin2); boolean success = false; @@ -438,13 +448,26 @@ public class IccProvider extends ContentProvider { private void loadRecord(AdnRecord record, ArrayList<ArrayList> results) { if (!record.isEmpty()) { - ArrayList<String> contact = new ArrayList<String>(2); + ArrayList<String> contact = new ArrayList<String>(); String alphaTag = record.getAlphaTag(); String number = record.getNumber(); + String[] emails = record.getEmails(); - if (DBG) log("loadRecord: " + alphaTag + ", " + number); + if (DBG) log("loadRecord: " + alphaTag + ", " + number + ","); contact.add(alphaTag); contact.add(number); + StringBuilder emailString = new StringBuilder(); + + if (emails != null) { + for (String email: emails) { + if (DBG) log("Adding email:" + email); + emailString.append(email); + emailString.append(","); + } + contact.add(emailString.toString()); + } else { + contact.add(null); + } results.add(contact); } } diff --git a/telephony/java/com/android/internal/telephony/gsm/SIMFileHandler.java b/telephony/java/com/android/internal/telephony/gsm/SIMFileHandler.java index a08cdde..206e62f 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SIMFileHandler.java +++ b/telephony/java/com/android/internal/telephony/gsm/SIMFileHandler.java @@ -19,15 +19,18 @@ package com.android.internal.telephony.gsm; import android.os.Message; import android.util.Log; +import com.android.internal.telephony.IccCard; +import com.android.internal.telephony.IccCardApplication; import com.android.internal.telephony.IccConstants; import com.android.internal.telephony.IccFileHandler; +import com.android.internal.telephony.Phone; /** * {@hide} */ public final class SIMFileHandler extends IccFileHandler implements IccConstants { static final String LOG_TAG = "GSM"; - + private Phone mPhone; //***** Instance Variables @@ -35,6 +38,7 @@ public final class SIMFileHandler extends IccFileHandler implements IccConstants SIMFileHandler(GSMPhone phone) { super(phone); + mPhone = phone; } public void dispose() { @@ -53,7 +57,6 @@ public final class SIMFileHandler extends IccFileHandler implements IccConstants } protected String getEFPath(int efid) { - // TODO(): Make changes when USIM is supported // TODO(): DF_GSM can be 7F20 or 7F21 to handle backward compatibility. // Implement this after discussion with OEMs. switch(efid) { @@ -79,8 +82,23 @@ public final class SIMFileHandler extends IccFileHandler implements IccConstants case EF_SPN_SHORT_CPHS: case EF_INFO_CPHS: return MF_SIM + DF_GSM; + + case EF_PBR: + // we only support global phonebook. + return MF_SIM + DF_TELECOM + DF_PHONEBOOK; + } + String path = getCommonIccEFPath(efid); + if (path == null) { + // The EFids in USIM phone book entries are decided by the card manufacturer. + // So if we don't match any of the cases above and if its a USIM return + // the phone book path. + IccCard card = phone.getIccCard(); + if (card != null && card.isApplicationOnIcc(IccCardApplication.AppType.APPTYPE_USIM)) { + return MF_SIM + DF_TELECOM + DF_PHONEBOOK; + } + Log.e(LOG_TAG, "Error: EF Path being returned in null"); } - return getCommonIccEFPath(efid); + return path; } protected void logd(String msg) { diff --git a/telephony/java/com/android/internal/telephony/gsm/SimTlv.java b/telephony/java/com/android/internal/telephony/gsm/SimTlv.java index 30543c7..497cf5f 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SimTlv.java +++ b/telephony/java/com/android/internal/telephony/gsm/SimTlv.java @@ -47,7 +47,6 @@ public class SimTlv public boolean nextObject() { if (!hasValidTlvObject) return false; - curOffset = curDataOffset + curDataLength; hasValidTlvObject = parseCurrentTlvObject(); return hasValidTlvObject; @@ -88,11 +87,12 @@ public class SimTlv private boolean parseCurrentTlvObject() { // 0x00 and 0xff are invalid tag values - if (record[curOffset] == 0 || (record[curOffset] & 0xff) == 0xff) { - return false; - } try { + if (record[curOffset] == 0 || (record[curOffset] & 0xff) == 0xff) { + return false; + } + if ((record[curOffset + 1] & 0xff) < 0x80) { // one byte length 0 - 0x7f curDataLength = record[curOffset + 1] & 0xff; diff --git a/telephony/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java b/telephony/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java new file mode 100644 index 0000000..d27f240 --- /dev/null +++ b/telephony/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java @@ -0,0 +1,424 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.telephony.gsm; + +import android.os.AsyncResult; +import android.os.Handler; +import android.os.Message; +import android.util.Log; + +import com.android.internal.telephony.AdnRecord; +import com.android.internal.telephony.AdnRecordCache; +import com.android.internal.telephony.IccConstants; +import com.android.internal.telephony.IccUtils; +import com.android.internal.telephony.PhoneBase; + +import org.apache.harmony.luni.lang.reflect.ListOfTypes; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +/** + * This class implements reading and parsing USIM records. + * Refer to Spec 3GPP TS 31.102 for more details. + * + * {@hide} + */ +public class UsimPhoneBookManager extends Handler implements IccConstants { + private static final String LOG_TAG = "GSM"; + private static final boolean DBG = true; + private PbrFile mPbrFile; + private Boolean mIsPbrPresent; + private PhoneBase mPhone; + private AdnRecordCache mAdnCache; + private Object mLock = new Object(); + private ArrayList<AdnRecord> mPhoneBookRecords; + private boolean mEmailPresentInIap = false; + private int mEmailTagNumberInIap = 0; + private ArrayList<byte[]> mIapFileRecord; + private ArrayList<byte[]> mEmailFileRecord; + private Map<Integer, ArrayList<String>> mEmailsForAdnRec; + + private static final int EVENT_PBR_LOAD_DONE = 1; + private static final int EVENT_USIM_ADN_LOAD_DONE = 2; + private static final int EVENT_IAP_LOAD_DONE = 3; + private static final int EVENT_EMAIL_LOAD_DONE = 4; + + private static final int USIM_TYPE1_TAG = 0xA8; + private static final int USIM_TYPE2_TAG = 0xA9; + private static final int USIM_TYPE3_TAG = 0xAA; + private static final int USIM_EFADN_TAG = 0xC0; + private static final int USIM_EFIAP_TAG = 0xC1; + private static final int USIM_EFEXT1_TAG = 0xC2; + private static final int USIM_EFSNE_TAG = 0xC3; + private static final int USIM_EFANR_TAG = 0xC4; + private static final int USIM_EFPBC_TAG = 0xC5; + private static final int USIM_EFGRP_TAG = 0xC6; + private static final int USIM_EFAAS_TAG = 0xC7; + private static final int USIM_EFGSD_TAG = 0xC8; + private static final int USIM_EFUID_TAG = 0xC9; + private static final int USIM_EFEMAIL_TAG = 0xCA; + private static final int USIM_EFCCP1_TAG = 0xCB; + + public UsimPhoneBookManager(PhoneBase phone, AdnRecordCache cache) { + mPhone = phone; + mPhoneBookRecords = new ArrayList<AdnRecord>(); + mPbrFile = null; + // We assume its present, after the first read this is updated. + // So we don't have to read from UICC if its not present on subsequent reads. + mIsPbrPresent = true; + mAdnCache = cache; + } + + public void reset() { + mPhoneBookRecords.clear(); + mIapFileRecord = null; + mEmailFileRecord = null; + mPbrFile = null; + mIsPbrPresent = true; + } + + public ArrayList<AdnRecord> loadEfFilesFromUsim() { + synchronized (mLock) { + if (!mPhoneBookRecords.isEmpty()) return mPhoneBookRecords; + if (!mIsPbrPresent) return null; + + // Check if the PBR file is present in the cache, if not read it + // from the USIM. + if (mPbrFile == null) { + readPbrFileAndWait(); + } + + if (mPbrFile == null) return null; + + int numRecs = mPbrFile.mFileIds.size(); + for (int i = 0; i < numRecs; i++) { + readAdnFileAndWait(i); + readEmailFileAndWait(i); + } + // All EF files are loaded, post the response. + } + return mPhoneBookRecords; + } + + private void readPbrFileAndWait() { + mPhone.getIccFileHandler().loadEFLinearFixedAll(EF_PBR, obtainMessage(EVENT_PBR_LOAD_DONE)); + try { + mLock.wait(); + } catch (InterruptedException e) { + Log.e(LOG_TAG, "Interrupted Exception in readAdnFileAndWait"); + } + } + + private void readEmailFileAndWait(int recNum) { + Map <Integer,Integer> fileIds; + fileIds = mPbrFile.mFileIds.get(recNum); + if (fileIds == null) return; + + if (fileIds.containsKey(USIM_EFEMAIL_TAG)) { + int efid = fileIds.get(USIM_EFEMAIL_TAG); + // Check if the EFEmail is a Type 1 file or a type 2 file. + // If mEmailPresentInIap is true, its a type 2 file. + // So we read the IAP file and then read the email records. + // instead of reading directly. + if (mEmailPresentInIap) { + readIapFileAndWait(fileIds.get(USIM_EFIAP_TAG)); + if (mIapFileRecord == null) { + Log.e(LOG_TAG, "Error: IAP file is empty"); + return; + } + } + // Read the EFEmail file. + mPhone.getIccFileHandler().loadEFLinearFixedAll(fileIds.get(USIM_EFEMAIL_TAG), + obtainMessage(EVENT_EMAIL_LOAD_DONE)); + try { + mLock.wait(); + } catch (InterruptedException e) { + Log.e(LOG_TAG, "Interrupted Exception in readEmailFileAndWait"); + } + + if (mEmailFileRecord == null) { + Log.e(LOG_TAG, "Error: Email file is empty"); + return; + } + updatePhoneAdnRecord(); + } + + } + + private void readIapFileAndWait(int efid) { + mPhone.getIccFileHandler().loadEFLinearFixedAll(efid, obtainMessage(EVENT_IAP_LOAD_DONE)); + try { + mLock.wait(); + } catch (InterruptedException e) { + Log.e(LOG_TAG, "Interrupted Exception in readIapFileAndWait"); + } + } + + private void updatePhoneAdnRecord() { + if (mEmailFileRecord == null) return; + int numAdnRecs = mPhoneBookRecords.size(); + if (mIapFileRecord != null) { + // The number of records in the IAP file is same as the number of records in ADN file. + // The order of the pointers in an EFIAP shall be the same as the order of file IDs + // that appear in the TLV object indicated by Tag 'A9' in the reference file record. + // i.e value of mEmailTagNumberInIap + + for (int i = 0; i < numAdnRecs; i++) { + byte[] record = null; + try { + record = mIapFileRecord.get(i); + } catch (IndexOutOfBoundsException e) { + Log.e(LOG_TAG, "Error: Improper ICC card: No IAP record for ADN, continuing"); + break; + } + int recNum = record[mEmailTagNumberInIap]; + + if (recNum != -1) { + String[] emails = new String[1]; + // SIM record numbers are 1 based + emails[0] = readEmailRecord(recNum - 1); + AdnRecord rec = mPhoneBookRecords.get(i); + if (rec != null) { + rec.setEmails(emails); + } else { + // might be a record with only email + rec = new AdnRecord("", "", emails); + } + mPhoneBookRecords.set(i, rec); + } + } + } + + // ICC cards can be made such that they have an IAP file but all + // records are empty. So we read both type 1 and type 2 file + // email records, just to be sure. + + int len = mPhoneBookRecords.size(); + // Type 1 file, the number of records is the same as the number of + // records in the ADN file. + if (mEmailsForAdnRec == null) { + parseType1EmailFile(len); + } + for (int i = 0; i < numAdnRecs; i++) { + ArrayList<String> emailList = null; + try { + emailList = mEmailsForAdnRec.get(i); + } catch (IndexOutOfBoundsException e) { + break; + } + if (emailList == null) continue; + + AdnRecord rec = mPhoneBookRecords.get(i); + + String[] emails = new String[emailList.size()]; + System.arraycopy(emailList.toArray(), 0, emails, 0, emailList.size()); + rec.setEmails(emails); + mPhoneBookRecords.set(i, rec); + } + } + + void parseType1EmailFile(int numRecs) { + mEmailsForAdnRec = new HashMap<Integer, ArrayList<String>>(); + byte[] emailRec = null; + for (int i = 0; i < numRecs; i++) { + try { + emailRec = mEmailFileRecord.get(i); + } catch (IndexOutOfBoundsException e) { + Log.e(LOG_TAG, "Error: Improper ICC card: No email record for ADN, continuing"); + break; + } + int adnRecNum = emailRec[emailRec.length - 1]; + + if (adnRecNum == -1) { + continue; + } + + String email = readEmailRecord(i); + + if (email == null || email.equals("")) { + continue; + } + + // SIM record numbers are 1 based. + ArrayList<String> val = mEmailsForAdnRec.get(adnRecNum - 1); + if (val == null) { + val = new ArrayList<String>(); + } + val.add(email); + // SIM record numbers are 1 based. + mEmailsForAdnRec.put(adnRecNum - 1, val); + } + } + + private String readEmailRecord(int recNum) { + byte[] emailRec = null; + try { + emailRec = mEmailFileRecord.get(recNum); + } catch (IndexOutOfBoundsException e) { + return null; + } + + // The length of the record is X+2 byte, where X bytes is the email address + String email = IccUtils.adnStringFieldToString(emailRec, 0, emailRec.length - 2); + return email; + } + + private void readAdnFileAndWait(int recNum) { + Map <Integer,Integer> fileIds; + fileIds = mPbrFile.mFileIds.get(recNum); + if (fileIds == null) return; + + mAdnCache.requestLoadAllAdnLike(fileIds.get(USIM_EFADN_TAG), + fileIds.get(USIM_EFEXT1_TAG), obtainMessage(EVENT_USIM_ADN_LOAD_DONE)); + try { + mLock.wait(); + } catch (InterruptedException e) { + Log.e(LOG_TAG, "Interrupted Exception in readAdnFileAndWait"); + } + } + + private void createPbrFile(ArrayList<byte[]> records) { + if (records == null) { + mPbrFile = null; + mIsPbrPresent = false; + return; + } + mPbrFile = new PbrFile(records); + } + + @Override + public void handleMessage(Message msg) { + AsyncResult ar; + + switch(msg.what) { + case EVENT_PBR_LOAD_DONE: + ar = (AsyncResult) msg.obj; + if (ar.exception == null) { + createPbrFile((ArrayList<byte[]>)ar.result); + } + synchronized (mLock) { + mLock.notify(); + } + break; + case EVENT_USIM_ADN_LOAD_DONE: + log("Loading USIM ADN records done"); + ar = (AsyncResult) msg.obj; + if (ar.exception == null) { + mPhoneBookRecords.addAll((ArrayList<AdnRecord>)ar.result); + } + synchronized (mLock) { + mLock.notify(); + } + break; + case EVENT_IAP_LOAD_DONE: + log("Loading USIM IAP records done"); + ar = (AsyncResult) msg.obj; + if (ar.exception == null) { + mIapFileRecord = ((ArrayList<byte[]>)ar.result); + } + synchronized (mLock) { + mLock.notify(); + } + break; + case EVENT_EMAIL_LOAD_DONE: + log("Loading USIM Email records done"); + ar = (AsyncResult) msg.obj; + if (ar.exception == null) { + mEmailFileRecord = ((ArrayList<byte[]>)ar.result); + } + + synchronized (mLock) { + mLock.notify(); + } + break; + } + } + + private class PbrFile { + // RecNum <EF Tag, efid> + HashMap<Integer,Map<Integer,Integer>> mFileIds; + + PbrFile(ArrayList<byte[]> records) { + mFileIds = new HashMap<Integer, Map<Integer, Integer>>(); + SimTlv recTlv; + int recNum = 0; + for (byte[] record: records) { + recTlv = new SimTlv(record, 0, record.length); + parseTag(recTlv, recNum); + recNum ++; + } + } + + void parseTag(SimTlv tlv, int recNum) { + SimTlv tlvEf; + int tag; + byte[] data; + Map<Integer, Integer> val = new HashMap<Integer, Integer>(); + do { + tag = tlv.getTag(); + switch(tag) { + case USIM_TYPE1_TAG: // A8 + case USIM_TYPE3_TAG: // AA + case USIM_TYPE2_TAG: // A9 + data = tlv.getData(); + tlvEf = new SimTlv(data, 0, data.length); + parseEf(tlvEf, val, tag); + break; + } + } while (tlv.nextObject()); + mFileIds.put(recNum, val); + } + + void parseEf(SimTlv tlv, Map<Integer, Integer> val, int parentTag) { + int tag; + byte[] data; + int tagNumberWithinParentTag = 0; + do { + tag = tlv.getTag(); + if (parentTag == USIM_TYPE2_TAG && tag == USIM_EFEMAIL_TAG) { + mEmailPresentInIap = true; + mEmailTagNumberInIap = tagNumberWithinParentTag; + } + switch(tag) { + case USIM_EFEMAIL_TAG: + case USIM_EFADN_TAG: + case USIM_EFEXT1_TAG: + case USIM_EFANR_TAG: + case USIM_EFPBC_TAG: + case USIM_EFGRP_TAG: + case USIM_EFAAS_TAG: + case USIM_EFGSD_TAG: + case USIM_EFUID_TAG: + case USIM_EFCCP1_TAG: + case USIM_EFIAP_TAG: + case USIM_EFSNE_TAG: + data = tlv.getData(); + int efid = data[0] << 8 | data[1]; + val.put(tag, efid); + break; + } + tagNumberWithinParentTag ++; + } while(tlv.nextObject()); + } + } + + private void log(String msg) { + if(DBG) Log.d(LOG_TAG, msg); + } +} |
