summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--telephony/java/com/android/internal/telephony/AdnRecord.java51
-rw-r--r--telephony/java/com/android/internal/telephony/AdnRecordCache.java34
-rw-r--r--telephony/java/com/android/internal/telephony/IccCard.java2
-rw-r--r--telephony/java/com/android/internal/telephony/IccConstants.java4
-rw-r--r--telephony/java/com/android/internal/telephony/IccPhoneBookInterfaceManager.java19
-rw-r--r--telephony/java/com/android/internal/telephony/IccProvider.java45
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/SIMFileHandler.java24
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/SimTlv.java8
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/UsimPhoneBookManager.java424
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);
+ }
+}