/*
* Copyright (C) 2011 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.ims;
import android.content.Context;
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.CommandsInterface;
import com.android.internal.telephony.IccFileHandler;
import com.android.internal.telephony.IccRecords;
import com.android.internal.telephony.UiccCardApplication;
import com.android.internal.telephony.gsm.SimTlv;
import com.android.internal.telephony.gsm.SpnOverride;
//import com.android.internal.telephony.gsm.VoiceMailConstants;
import java.nio.charset.Charset;
import java.util.ArrayList;
import static com.android.internal.telephony.IccConstants.EF_DOMAIN;
import static com.android.internal.telephony.IccConstants.EF_IMPI;
import static com.android.internal.telephony.IccConstants.EF_IMPU;
/**
* {@hide}
*/
public final class IsimUiccRecords extends IccRecords implements IsimRecords {
protected static final String LOG_TAG = "GSM";
private static final boolean DBG = true;
private static final boolean DUMP_RECORDS = false; // Note: PII is logged when this is true
private static final int EVENT_APP_READY = 1;
// ISIM EF records (see 3GPP TS 31.103)
private String mIsimImpi; // IMS private user identity
private String mIsimDomain; // IMS home network domain name
private String[] mIsimImpu; // IMS public user identity(s)
private static final int TAG_ISIM_VALUE = 0x80; // From 3GPP TS 31.103
public IsimUiccRecords(UiccCardApplication app, Context c, CommandsInterface ci) {
super(app, c, ci);
recordsRequested = false; // No load request is made till SIM ready
// recordsToLoad is set to 0 because no requests are made yet
recordsToLoad = 0;
mParentApp.registerForReady(this, EVENT_APP_READY, null);
}
@Override
public void dispose() {
log("Disposing " + this);
//Unregister for all events
mParentApp.unregisterForReady(this);
resetRecords();
super.dispose();
}
// ***** Overridden from Handler
public void handleMessage(Message msg) {
if (mDestroyed.get()) {
Log.e(LOG_TAG, "Received message " + msg +
"[" + msg.what + "] while being destroyed. Ignoring.");
return;
}
try {
switch (msg.what) {
case EVENT_APP_READY:
onReady();
break;
default:
super.handleMessage(msg); // IccRecords handles generic record load responses
}
} catch (RuntimeException exc) {
// I don't want these exceptions to be fatal
Log.w(LOG_TAG, "Exception parsing SIM record", exc);
}
}
protected void fetchIsimRecords() {
recordsRequested = true;
mFh.loadEFTransparent(EF_IMPI, obtainMessage(
IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimImpiLoaded()));
recordsToLoad++;
mFh.loadEFLinearFixedAll(EF_IMPU, obtainMessage(
IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimImpuLoaded()));
recordsToLoad++;
mFh.loadEFTransparent(EF_DOMAIN, obtainMessage(
IccRecords.EVENT_GET_ICC_RECORD_DONE, new EfIsimDomainLoaded()));
recordsToLoad++;
log("fetchIsimRecords " + recordsToLoad);
}
protected void resetRecords() {
// recordsRequested is set to false indicating that the SIM
// read requests made so far are not valid. This is set to
// true only when fresh set of read requests are made.
recordsRequested = false;
}
private class EfIsimImpiLoaded implements IccRecords.IccRecordLoaded {
public String getEfName() {
return "EF_ISIM_IMPI";
}
public void onRecordLoaded(AsyncResult ar) {
byte[] data = (byte[]) ar.result;
mIsimImpi = isimTlvToString(data);
if (DUMP_RECORDS) log("EF_IMPI=" + mIsimImpi);
}
}
private class EfIsimImpuLoaded implements IccRecords.IccRecordLoaded {
public String getEfName() {
return "EF_ISIM_IMPU";
}
public void onRecordLoaded(AsyncResult ar) {
ArrayList<byte[]> impuList = (ArrayList<byte[]>) ar.result;
if (DBG) log("EF_IMPU record count: " + impuList.size());
mIsimImpu = new String[impuList.size()];
int i = 0;
for (byte[] identity : impuList) {
String impu = isimTlvToString(identity);
if (DUMP_RECORDS) log("EF_IMPU[" + i + "]=" + impu);
mIsimImpu[i++] = impu;
}
}
}
private class EfIsimDomainLoaded implements IccRecords.IccRecordLoaded {
public String getEfName() {
return "EF_ISIM_DOMAIN";
}
public void onRecordLoaded(AsyncResult ar) {
byte[] data = (byte[]) ar.result;
mIsimDomain = isimTlvToString(data);
if (DUMP_RECORDS) log("EF_DOMAIN=" + mIsimDomain);
}
}
/**
* ISIM records for IMS are stored inside a Tag-Length-Value record as a UTF-8 string
* with tag value 0x80.
* @param record the byte array containing the IMS data string
* @return the decoded String value, or null if the record can't be decoded
*/
private static String isimTlvToString(byte[] record) {
SimTlv tlv = new SimTlv(record, 0, record.length);
do {
if (tlv.getTag() == TAG_ISIM_VALUE) {
return new String(tlv.getData(), Charset.forName("UTF-8"));
}
} while (tlv.nextObject());
Log.e(LOG_TAG, "[ISIM] can't find TLV tag in ISIM record, returning null");
return null;
}
@Override
protected void onRecordLoaded() {
// One record loaded successfully or failed, In either case
// we need to update the recordsToLoad count
recordsToLoad -= 1;
if (recordsToLoad == 0 && recordsRequested == true) {
onAllRecordsLoaded();
} else if (recordsToLoad < 0) {
loge("recordsToLoad <0, programmer error suspected");
recordsToLoad = 0;
}
}
@Override
protected void onAllRecordsLoaded() {
recordsLoadedRegistrants.notifyRegistrants(
new AsyncResult(null, null, null));
}
/**
* Return the IMS private user identity (IMPI).
* Returns null if the IMPI hasn't been loaded or isn't present on the ISIM.
* @return the IMS private user identity string, or null if not available
*/
@Override
public String getIsimImpi() {
return mIsimImpi;
}
/**
* Return the IMS home network domain name.
* Returns null if the IMS domain hasn't been loaded or isn't present on the ISIM.
* @return the IMS home network domain name, or null if not available
*/
@Override
public String getIsimDomain() {
return mIsimDomain;
}
/**
* Return an array of IMS public user identities (IMPU).
* Returns null if the IMPU hasn't been loaded or isn't present on the ISIM.
* @return an array of IMS public user identity strings, or null if not available
*/
@Override
public String[] getIsimImpu() {
return (mIsimImpu != null) ? mIsimImpu.clone() : null;
}
@Override
public int getDisplayRule(String plmn) {
// Not applicable to Isim
return 0;
}
@Override
public void onReady() {
fetchIsimRecords();
}
@Override
public void onRefresh(boolean fileChanged, int[] fileList) {
// We do not handle it in Isim
}
@Override
public void setVoiceMailNumber(String alphaTag, String voiceNumber,
Message onComplete) {
// Not applicable to Isim
}
@Override
public void setVoiceMessageWaiting(int line, int countWaiting) {
// Not applicable to Isim
}
@Override
protected void log(String s) {
if (DBG) Log.d(LOG_TAG, "[ISIM] " + s);
}
@Override
protected void loge(String s) {
if (DBG) Log.e(LOG_TAG, "[ISIM] " + s);
}
}