Merge change 21774 into eclair
* changes: Implement USIM and add support for importing emails from USIM.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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++) {
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user