Merge change 21774 into eclair

* changes:
  Implement USIM and add support for importing emails from USIM.
This commit is contained in:
Android (Google) Code Review
2009-09-03 14:45:00 -07:00
9 changed files with 564 additions and 47 deletions

View File

@@ -23,6 +23,8 @@ import android.util.Log;
import com.android.internal.telephony.GsmAlphabet; import com.android.internal.telephony.GsmAlphabet;
import java.util.Arrays;
/** /**
* *
@@ -38,6 +40,7 @@ public class AdnRecord implements Parcelable {
String alphaTag = ""; String alphaTag = "";
String number = ""; String number = "";
String[] emails;
int extRecord = 0xff; int extRecord = 0xff;
int efid; // or 0 if none int efid; // or 0 if none
int recordNumber; // or 0 if none int recordNumber; // or 0 if none
@@ -74,13 +77,15 @@ public class AdnRecord implements Parcelable {
int recordNumber; int recordNumber;
String alphaTag; String alphaTag;
String number; String number;
String[] emails;
efid = source.readInt(); efid = source.readInt();
recordNumber = source.readInt(); recordNumber = source.readInt();
alphaTag = source.readString(); alphaTag = source.readString();
number = 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) { public AdnRecord[] newArray(int size) {
@@ -90,29 +95,38 @@ public class AdnRecord implements Parcelable {
//***** Constructor //***** Constructor
public public AdnRecord (byte[] record) {
AdnRecord (byte[] record) {
this(0, 0, record); this(0, 0, record);
} }
public public AdnRecord (int efid, int recordNumber, byte[] record) {
AdnRecord (int efid, int recordNumber, byte[] record) {
this.efid = efid; this.efid = efid;
this.recordNumber = recordNumber; this.recordNumber = recordNumber;
parseRecord(record); parseRecord(record);
} }
public public AdnRecord (String alphaTag, String number) {
AdnRecord (String alphaTag, String number) {
this(0, 0, alphaTag, number); this(0, 0, alphaTag, number);
} }
public public AdnRecord (String alphaTag, String number, String[] emails) {
AdnRecord (int efid, int recordNumber, String alphaTag, String number) { this(0, 0, alphaTag, number, emails);
}
public AdnRecord (int efid, int recordNumber, String alphaTag, String number, String[] emails) {
this.efid = efid; this.efid = efid;
this.recordNumber = recordNumber; this.recordNumber = recordNumber;
this.alphaTag = alphaTag; this.alphaTag = alphaTag;
this.number = number; 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 //***** Instance Methods
@@ -125,12 +139,20 @@ public class AdnRecord implements Parcelable {
return number; return number;
} }
public String[] getEmails() {
return emails;
}
public void setEmails(String[] emails) {
this.emails = emails;
}
public String toString() { public String toString() {
return "ADN Record '" + alphaTag + "' '" + number + "'"; return "ADN Record '" + alphaTag + "' '" + number + " " + emails + "'";
} }
public boolean isEmpty() { public boolean isEmpty() {
return alphaTag.equals("") && number.equals(""); return alphaTag.equals("") && number.equals("") && emails == null;
} }
public boolean hasExtendedRecord() { public boolean hasExtendedRecord() {
@@ -139,7 +161,8 @@ public class AdnRecord implements Parcelable {
public boolean isEqual(AdnRecord adn) { public boolean isEqual(AdnRecord adn) {
return ( alphaTag.equals(adn.getAlphaTag()) && return ( alphaTag.equals(adn.getAlphaTag()) &&
number.equals(adn.getNumber()) ); number.equals(adn.getNumber()) &&
Arrays.equals(emails, adn.getEmails()));
} }
//***** Parcelable Implementation //***** Parcelable Implementation
@@ -152,6 +175,7 @@ public class AdnRecord implements Parcelable {
dest.writeInt(recordNumber); dest.writeInt(recordNumber);
dest.writeString(alphaTag); dest.writeString(alphaTag);
dest.writeString(number); dest.writeString(number);
dest.writeStringArray(emails);
} }
/** /**
@@ -274,10 +298,13 @@ public class AdnRecord implements Parcelable {
extRecord = 0xff & record[record.length - 1]; extRecord = 0xff & record[record.length - 1];
emails = null;
} catch (RuntimeException ex) { } catch (RuntimeException ex) {
Log.w(LOG_TAG, "Error parsing AdnRecord", ex); Log.w(LOG_TAG, "Error parsing AdnRecord", ex);
number = ""; number = "";
alphaTag = ""; alphaTag = "";
emails = null;
} }
} }
} }

View File

@@ -16,14 +16,16 @@
package com.android.internal.telephony; 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.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.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import com.android.internal.telephony.IccConstants;
/** /**
* {@hide} * {@hide}
@@ -32,6 +34,7 @@ public final class AdnRecordCache extends Handler implements IccConstants {
//***** Instance Variables //***** Instance Variables
PhoneBase phone; PhoneBase phone;
private UsimPhoneBookManager mUsimPhoneBookManager;
// Indexed by EF ID // Indexed by EF ID
SparseArray<ArrayList<AdnRecord>> adnLikeFiles SparseArray<ArrayList<AdnRecord>> adnLikeFiles
@@ -55,6 +58,7 @@ public final class AdnRecordCache extends Handler implements IccConstants {
public AdnRecordCache(PhoneBase phone) { public AdnRecordCache(PhoneBase phone) {
this.phone = phone; this.phone = phone;
mUsimPhoneBookManager = new UsimPhoneBookManager(phone, this);
} }
//***** Called from SIMRecords //***** Called from SIMRecords
@@ -64,6 +68,7 @@ public final class AdnRecordCache extends Handler implements IccConstants {
*/ */
public void reset() { public void reset() {
adnLikeFiles.clear(); adnLikeFiles.clear();
mUsimPhoneBookManager.reset();
clearWaiters(); clearWaiters();
clearUserWriters(); clearUserWriters();
@@ -103,14 +108,14 @@ public final class AdnRecordCache extends Handler implements IccConstants {
* *
* See 3GPP TS 51.011 for this mapping * See 3GPP TS 51.011 for this mapping
*/ */
private int int extensionEfForEf(int efid) {
extensionEfForEf(int efid) {
switch (efid) { switch (efid) {
case EF_MBDN: return EF_EXT6; case EF_MBDN: return EF_EXT6;
case EF_ADN: return EF_EXT1; case EF_ADN: return EF_EXT1;
case EF_SDN: return EF_EXT3; case EF_SDN: return EF_EXT3;
case EF_FDN: return EF_EXT2; case EF_FDN: return EF_EXT2;
case EF_MSISDN: return EF_EXT1; case EF_MSISDN: return EF_EXT1;
case EF_PBR: return 0; // The EF PBR doesn't have an extension record
default: return -1; default: return -1;
} }
} }
@@ -223,11 +228,15 @@ public final class AdnRecordCache extends Handler implements IccConstants {
* record * record
*/ */
public void public void
requestLoadAllAdnLike (int efid, Message response) { requestLoadAllAdnLike (int efid, int extensionEf, Message response) {
ArrayList<Message> waiters; ArrayList<Message> waiters;
ArrayList<AdnRecord> result; ArrayList<AdnRecord> result;
result = getRecordsIfLoaded(efid); if (efid == EF_PBR) {
result = mUsimPhoneBookManager.loadEfFilesFromUsim();
} else {
result = getRecordsIfLoaded(efid);
}
// Have we already loaded this efid? // Have we already loaded this efid?
if (result != null) { if (result != null) {
@@ -258,9 +267,8 @@ public final class AdnRecordCache extends Handler implements IccConstants {
adnLikeWaiters.put(efid, waiters); adnLikeWaiters.put(efid, waiters);
int extensionEF = extensionEfForEf(efid);
if (extensionEF < 0) { if (extensionEf < 0) {
// respond with error if not known ADN-like record // respond with error if not known ADN-like record
if (response != null) { if (response != null) {
@@ -272,7 +280,7 @@ public final class AdnRecordCache extends Handler implements IccConstants {
return; return;
} }
new AdnRecordLoader(phone).loadAllFromEF(efid, extensionEF, new AdnRecordLoader(phone).loadAllFromEF(efid, extensionEf,
obtainMessage(EVENT_LOAD_ALL_ADN_LIKE_DONE, efid, 0)); obtainMessage(EVENT_LOAD_ALL_ADN_LIKE_DONE, efid, 0));
} }
@@ -311,7 +319,7 @@ public final class AdnRecordCache extends Handler implements IccConstants {
adnLikeWaiters.delete(efid); adnLikeWaiters.delete(efid);
if (ar.exception == null) { if (ar.exception == null) {
adnLikeFiles.put(efid, (ArrayList<AdnRecord>) (ar.result)); adnLikeFiles.put(efid, (ArrayList<AdnRecord>) ar.result);
} }
notifyWaiters(waiters, ar); notifyWaiters(waiters, ar);
break; break;

View File

@@ -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; if (mIccCardStatus == null) return false;
for (int i = 0 ; i < mIccCardStatus.getNumApplications(); i++) { for (int i = 0 ; i < mIccCardStatus.getNumApplications(); i++) {

View File

@@ -42,6 +42,9 @@ public interface IccConstants {
static final int EF_CFIS = 0x6FCB; static final int EF_CFIS = 0x6FCB;
static final int EF_IMG = 0x4f20; 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 // 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_MAILBOX_CPHS = 0x6F17;
static final int EF_VOICE_MAIL_INDICATOR_CPHS = 0x6F11; 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 MF_SIM = "3F00";
static final String DF_TELECOM = "7F10"; static final String DF_TELECOM = "7F10";
static final String DF_PHONEBOOK = "5F3A";
static final String DF_GRAPHICS = "5F50"; static final String DF_GRAPHICS = "5F50";
static final String DF_GSM = "7F20"; static final String DF_GSM = "7F20";
static final String DF_CDMA = "7F25"; static final String DF_CDMA = "7F25";

View File

@@ -115,7 +115,8 @@ public abstract class IccPhoneBookInterfaceManager extends IIccPhoneBook.Stub {
* Replace oldAdn with newAdn in ADN-like record in EF * Replace oldAdn with newAdn in ADN-like record in EF
* *
* getAdnRecordsInEf must be called at least once before this function, * 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 * throws SecurityException if no WRITE_CONTACTS permission
* *
* @param efid must be one among EF_ADN, EF_FDN, and EF_SDN * @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 * Update an ADN-like EF record by record index
* *
* This is useful for iteration the whole ADN file, such as write the whole * 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 * throws SecurityException if no WRITE_CONTACTS permission
* *
* @param efid must be one among EF_ADN, EF_FDN, and EF_SDN * @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"); "Requires android.permission.READ_CONTACTS permission");
} }
efid = updateEfForIccType(efid);
if (DBG) logd("getAdnRecordsInEF: efid=" + efid); if (DBG) logd("getAdnRecordsInEF: efid=" + efid);
synchronized(mLock) { synchronized(mLock) {
checkThread(); checkThread();
Message response = mBaseHandler.obtainMessage(EVENT_LOAD_DONE); Message response = mBaseHandler.obtainMessage(EVENT_LOAD_DONE);
adnCache.requestLoadAllAdnLike(efid, response); adnCache.requestLoadAllAdnLike(efid, adnCache.extensionEfForEf(efid), response);
try { try {
mLock.wait(); mLock.wait();
} catch (InterruptedException e) { } 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;
}
} }

View File

@@ -46,7 +46,8 @@ public class IccProvider extends ContentProvider {
private static final String[] ADDRESS_BOOK_COLUMN_NAMES = new String[] { private static final String[] ADDRESS_BOOK_COLUMN_NAMES = new String[] {
"name", "name",
"number" "number",
"emails"
}; };
private static final int ADN = 1; 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_TAG = "tag";
private static final String STR_NUMBER = "number"; private static final String STR_NUMBER = "number";
private static final String STR_EMAILS = "emails";
private static final String STR_PIN2 = "pin2"; private static final String STR_PIN2 = "pin2";
private static final UriMatcher URL_MATCHER = private static final UriMatcher URL_MATCHER =
@@ -172,7 +174,8 @@ public class IccProvider extends ContentProvider {
String tag = initialValues.getAsString("tag"); String tag = initialValues.getAsString("tag");
String number = initialValues.getAsString("number"); 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) { if (!success) {
return null; return null;
@@ -238,6 +241,7 @@ public class IccProvider extends ContentProvider {
// parse where clause // parse where clause
String tag = null; String tag = null;
String number = null; String number = null;
String[] emails = null;
String pin2 = null; String pin2 = null;
String[] tokens = where.split("AND"); String[] tokens = where.split("AND");
@@ -261,6 +265,9 @@ public class IccProvider extends ContentProvider {
tag = normalizeValue(val); tag = normalizeValue(val);
} else if (STR_NUMBER.equals(key)) { } else if (STR_NUMBER.equals(key)) {
number = normalizeValue(val); number = normalizeValue(val);
} else if (STR_EMAILS.equals(key)) {
//TODO(): Email is null.
emails = null;
} else if (STR_PIN2.equals(key)) { } else if (STR_PIN2.equals(key)) {
pin2 = normalizeValue(val); pin2 = normalizeValue(val);
} }
@@ -274,7 +281,7 @@ public class IccProvider extends ContentProvider {
return 0; return 0;
} }
boolean success = deleteIccRecordFromEf(efType, tag, number, pin2); boolean success = deleteIccRecordFromEf(efType, tag, number, emails, pin2);
if (!success) { if (!success) {
return 0; return 0;
} }
@@ -307,9 +314,11 @@ public class IccProvider extends ContentProvider {
String tag = values.getAsString("tag"); String tag = values.getAsString("tag");
String number = values.getAsString("number"); String number = values.getAsString("number");
String[] emails = null;
String newTag = values.getAsString("newTag"); String newTag = values.getAsString("newTag");
String newNumber = values.getAsString("newNumber"); String newNumber = values.getAsString("newNumber");
String[] newEmails = null;
// TODO(): Update for email.
boolean success = updateIccRecordInEf(efType, tag, number, boolean success = updateIccRecordInEf(efType, tag, number,
newTag, newNumber, pin2); newTag, newNumber, pin2);
@@ -355,9 +364,9 @@ public class IccProvider extends ContentProvider {
} }
private boolean 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 + if (DBG) log("addIccRecordToEf: efType=" + efType + ", name=" + name +
", number=" + number); ", number=" + number + ", emails=" + emails);
boolean success = false; boolean success = false;
@@ -384,7 +393,7 @@ public class IccProvider extends ContentProvider {
private boolean private boolean
updateIccRecordInEf(int efType, String oldName, String oldNumber, 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 + if (DBG) log("updateIccRecordInEf: efType=" + efType +
", oldname=" + oldName + ", oldnumber=" + oldNumber + ", oldname=" + oldName + ", oldnumber=" + oldNumber +
", newname=" + newName + ", newnumber=" + newNumber); ", 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 + if (DBG) log("deleteIccRecordFromEf: efType=" + efType +
", name=" + name + ", number=" + number + ", pin2=" + pin2); ", name=" + name + ", number=" + number + ", emails=" + emails + ", pin2=" + pin2);
boolean success = false; boolean success = false;
@@ -438,13 +448,26 @@ public class IccProvider extends ContentProvider {
private void loadRecord(AdnRecord record, private void loadRecord(AdnRecord record,
ArrayList<ArrayList> results) { ArrayList<ArrayList> results) {
if (!record.isEmpty()) { if (!record.isEmpty()) {
ArrayList<String> contact = new ArrayList<String>(2); ArrayList<String> contact = new ArrayList<String>();
String alphaTag = record.getAlphaTag(); String alphaTag = record.getAlphaTag();
String number = record.getNumber(); 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(alphaTag);
contact.add(number); 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); results.add(contact);
} }
} }

View File

@@ -19,15 +19,18 @@ package com.android.internal.telephony.gsm;
import android.os.Message; import android.os.Message;
import android.util.Log; 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.IccConstants;
import com.android.internal.telephony.IccFileHandler; import com.android.internal.telephony.IccFileHandler;
import com.android.internal.telephony.Phone;
/** /**
* {@hide} * {@hide}
*/ */
public final class SIMFileHandler extends IccFileHandler implements IccConstants { public final class SIMFileHandler extends IccFileHandler implements IccConstants {
static final String LOG_TAG = "GSM"; static final String LOG_TAG = "GSM";
private Phone mPhone;
//***** Instance Variables //***** Instance Variables
@@ -35,6 +38,7 @@ public final class SIMFileHandler extends IccFileHandler implements IccConstants
SIMFileHandler(GSMPhone phone) { SIMFileHandler(GSMPhone phone) {
super(phone); super(phone);
mPhone = phone;
} }
public void dispose() { public void dispose() {
@@ -53,7 +57,6 @@ public final class SIMFileHandler extends IccFileHandler implements IccConstants
} }
protected String getEFPath(int efid) { protected String getEFPath(int efid) {
// TODO(): Make changes when USIM is supported
// TODO(): DF_GSM can be 7F20 or 7F21 to handle backward compatibility. // TODO(): DF_GSM can be 7F20 or 7F21 to handle backward compatibility.
// Implement this after discussion with OEMs. // Implement this after discussion with OEMs.
switch(efid) { switch(efid) {
@@ -79,8 +82,23 @@ public final class SIMFileHandler extends IccFileHandler implements IccConstants
case EF_SPN_SHORT_CPHS: case EF_SPN_SHORT_CPHS:
case EF_INFO_CPHS: case EF_INFO_CPHS:
return MF_SIM + DF_GSM; return MF_SIM + DF_GSM;
case EF_PBR:
// we only support global phonebook.
return MF_SIM + DF_TELECOM + DF_PHONEBOOK;
} }
return getCommonIccEFPath(efid); 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 path;
} }
protected void logd(String msg) { protected void logd(String msg) {

View File

@@ -47,7 +47,6 @@ public class SimTlv
public boolean nextObject() { public boolean nextObject() {
if (!hasValidTlvObject) return false; if (!hasValidTlvObject) return false;
curOffset = curDataOffset + curDataLength; curOffset = curDataOffset + curDataLength;
hasValidTlvObject = parseCurrentTlvObject(); hasValidTlvObject = parseCurrentTlvObject();
return hasValidTlvObject; return hasValidTlvObject;
@@ -88,11 +87,12 @@ public class SimTlv
private boolean parseCurrentTlvObject() { private boolean parseCurrentTlvObject() {
// 0x00 and 0xff are invalid tag values // 0x00 and 0xff are invalid tag values
if (record[curOffset] == 0 || (record[curOffset] & 0xff) == 0xff) {
return false;
}
try { try {
if (record[curOffset] == 0 || (record[curOffset] & 0xff) == 0xff) {
return false;
}
if ((record[curOffset + 1] & 0xff) < 0x80) { if ((record[curOffset + 1] & 0xff) < 0x80) {
// one byte length 0 - 0x7f // one byte length 0 - 0x7f
curDataLength = record[curOffset + 1] & 0xff; curDataLength = record[curOffset + 1] & 0xff;

View File

@@ -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);
}
}