summaryrefslogtreecommitdiffstats
path: root/telephony/java
diff options
context:
space:
mode:
authorJaikumar Ganesh <jaikumar@google.com>2009-08-26 12:15:14 -0700
committerJaikumar Ganesh <jaikumar@google.com>2009-09-03 13:55:36 -0700
commit34efc39f256d5833687c7bd7d83258d6394c9307 (patch)
tree91cfdb29cf1cccbc83441273920ddc9c5b4ea486 /telephony/java
parent19d6f7ac27448b59c936878756bad49f623a6a99 (diff)
downloadframeworks_base-34efc39f256d5833687c7bd7d83258d6394c9307.zip
frameworks_base-34efc39f256d5833687c7bd7d83258d6394c9307.tar.gz
frameworks_base-34efc39f256d5833687c7bd7d83258d6394c9307.tar.bz2
Implement USIM and add support for importing emails from USIM.
Refer to 3GPP Spec 31.102 for more details. We read and parse USIM records instead of the RIL doing it for us. We only support reading of USIM Phonebook records.
Diffstat (limited to 'telephony/java')
-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);
+ }
+}