From 53f67f7e9d5534cc7d4fe882e100c221ce0b193b Mon Sep 17 00:00:00 2001 From: Kazuhiro Ondo Date: Wed, 1 Jun 2011 00:38:42 -0500 Subject: [PATCH] Use CSIM to get provision information In case of LTE & CDMA hybrid system, provision information will be read from CSIM records. Also CSIM records can be updated over the air. No data connection attempt should be made until CSIM provisioning is notified via SIM_REFRESH event. Change-Id: I001db48d07d90af22104cb958df638e15e0d494a --- .../internal/telephony/IccConstants.java | 4 + .../internal/telephony/IccRecords.java | 10 + .../android/internal/telephony/IccUtils.java | 23 +++ .../cdma/CdmaLteServiceStateTracker.java | 15 +- .../cdma/CdmaLteUiccFileHandler.java | 20 ++ .../telephony/cdma/CdmaLteUiccRecords.java | 180 +++++++++++++++++- .../cdma/CdmaServiceStateTracker.java | 97 +++++----- .../gsm/GsmDataConnectionTracker.java | 2 + 8 files changed, 305 insertions(+), 46 deletions(-) diff --git a/telephony/java/com/android/internal/telephony/IccConstants.java b/telephony/java/com/android/internal/telephony/IccConstants.java index cafc79bf5fd9a..1ba6dfe19366c 100644 --- a/telephony/java/com/android/internal/telephony/IccConstants.java +++ b/telephony/java/com/android/internal/telephony/IccConstants.java @@ -63,6 +63,10 @@ public interface IccConstants { // 3GPP2 C.S0065 static final int EF_CSIM_LI = 0x6F3A; static final int EF_CSIM_SPN =0x6F41; + static final int EF_CSIM_MDN = 0x6F44; + static final int EF_CSIM_IMSIM = 0x6F22; + static final int EF_CSIM_CDMAHOME = 0x6F28; + static final int EF_CSIM_EPRL = 0x6F5A; //ISIM access static final int EF_IMPU = 0x6f04; diff --git a/telephony/java/com/android/internal/telephony/IccRecords.java b/telephony/java/com/android/internal/telephony/IccRecords.java index 10a3b69964409..3a2790123fb13 100644 --- a/telephony/java/com/android/internal/telephony/IccRecords.java +++ b/telephony/java/com/android/internal/telephony/IccRecords.java @@ -287,6 +287,16 @@ public abstract class IccRecords extends Handler implements IccConstants { public void setVoiceCallForwardingFlag(int line, boolean enable) { } + /** + * Indicates wether SIM is in provisioned state or not. + * Overridden only if SIM can be dynamically provisioned via OTA. + * + * @return true if provisioned + */ + public boolean isProvisioned () { + return true; + } + /** * Write string to log file * diff --git a/telephony/java/com/android/internal/telephony/IccUtils.java b/telephony/java/com/android/internal/telephony/IccUtils.java index df579b04851f3..8e60e6ed9c75d 100644 --- a/telephony/java/com/android/internal/telephony/IccUtils.java +++ b/telephony/java/com/android/internal/telephony/IccUtils.java @@ -63,6 +63,29 @@ public class IccUtils { return ret.toString(); } + /** + * Decode cdma byte into String. + */ + public static String + cdmaBcdToString(byte[] data, int offset, int length) { + StringBuilder ret = new StringBuilder(length); + + int count = 0; + for (int i = offset; count < length; i++) { + int v; + v = data[i] & 0xf; + if (v > 9) v = 0; + ret.append((char)('0' + v)); + + if (++count == length) break; + + v = (data[i] >> 4) & 0xf; + if (v > 9) v = 0; + ret.append((char)('0' + v)); + ++count; + } + return ret.toString(); + } /** * Decodes a GSM-style BCD byte, returning an int ranging from 0-99. diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java index faae6223399df..318cf3728f549 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java @@ -67,11 +67,24 @@ public class CdmaLteServiceStateTracker extends CdmaServiceStateTracker { case EVENT_SIM_READY: if (DBG) log("handleMessage EVENT_SIM_READY"); isSubscriptionFromRuim = false; - cm.getCDMASubscription( obtainMessage(EVENT_POLL_STATE_CDMA_SUBSCRIPTION)); + // Register SIM_RECORDS_LOADED dynamically. + // This is to avoid confilct with RUIM_READY scenario) + phone.mIccRecords.registerForRecordsLoaded(this, EVENT_SIM_RECORDS_LOADED, null); pollState(); // Signal strength polling stops when radio is off. queueNextSignalStrengthPoll(); break; + case EVENT_SIM_RECORDS_LOADED: + CdmaLteUiccRecords sim = (CdmaLteUiccRecords)phone.mIccRecords; + if ((sim != null) && sim.isProvisioned()) { + mMdn = sim.getMdn(); + mMin = sim.getMin(); + parseSidNid(sim.getSid(), sim.getNid()); + mPrlVersion = sim.getPrlVersion();; + mIsMinInfoReady = true; + updateOtaspState(); + } + break; default: super.handleMessage(msg); } diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccFileHandler.java b/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccFileHandler.java index 2aede29e01e04..b9d7c467fa972 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccFileHandler.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccFileHandler.java @@ -19,6 +19,7 @@ package com.android.internal.telephony.cdma; import android.util.Log; import com.android.internal.telephony.IccConstants; import com.android.internal.telephony.IccFileHandler; +import android.os.Message; /** * {@hide} @@ -34,6 +35,10 @@ public final class CdmaLteUiccFileHandler extends IccFileHandler { switch(efid) { case EF_CSIM_SPN: case EF_CSIM_LI: + case EF_CSIM_MDN: + case EF_CSIM_IMSIM: + case EF_CSIM_CDMAHOME: + case EF_CSIM_EPRL: return MF_SIM + DF_CDMA; case EF_AD: return MF_SIM + DF_GSM; @@ -41,6 +46,21 @@ public final class CdmaLteUiccFileHandler extends IccFileHandler { return getCommonIccEFPath(efid); } + @Override + public void loadEFTransparent(int fileid, Message onLoaded) { + if (fileid == EF_CSIM_EPRL) { + // Entire PRL could be huge. We are only interested in + // the first 4 bytes of the record. + phone.mCM.iccIO(COMMAND_READ_BINARY, fileid, getEFPath(fileid), + 0, 0, 4, null, null, + obtainMessage(EVENT_READ_BINARY_DONE, + fileid, 0, onLoaded)); + } else { + super.loadEFTransparent(fileid, onLoaded); + } + } + + protected void logd(String msg) { Log.d(LOG_TAG, "[CdmaLteUiccFileHandler] " + msg); } diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccRecords.java b/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccRecords.java index 78879d690cd1f..ac77f9ae8bd95 100755 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccRecords.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccRecords.java @@ -26,7 +26,8 @@ import android.os.AsyncResult; import android.os.Message; import android.os.SystemProperties; import android.util.Log; - +import java.util.Locale; +import java.util.ArrayList; /** * {@hide} @@ -36,10 +37,19 @@ public final class CdmaLteUiccRecords extends SIMRecords { private byte[] mEFpl = null; private byte[] mEFli = null; boolean csimSpnDisplayCondition = false; + private String mMdn; + private String mMin; + private String mPrlVersion; + private String mHomeSystemId; + private String mHomeNetworkId; private static final int EVENT_GET_PL_DONE = CSIM_EVENT_BASE; private static final int EVENT_GET_CSIM_LI_DONE = CSIM_EVENT_BASE + 1; private static final int EVENT_GET_CSIM_SPN_DONE = CSIM_EVENT_BASE + 2; + private static final int EVENT_GET_CSIM_MDN_DONE = CSIM_EVENT_BASE + 3; + private static final int EVENT_GET_CSIM_IMSIM_DONE = CSIM_EVENT_BASE + 4; + private static final int EVENT_GET_CSIM_CDMAHOME_DONE = CSIM_EVENT_BASE + 5; + private static final int EVENT_GET_CSIM_EPRL_DONE = CSIM_EVENT_BASE + 6; public CdmaLteUiccRecords(PhoneBase p) { super(p); @@ -109,6 +119,46 @@ public final class CdmaLteUiccRecords extends SIMRecords { } onGetCSimSpnDone(ar); break; + case EVENT_GET_CSIM_MDN_DONE: + if (DBG) log("EVENT_GET_CSIM_MDN_DONE"); + isCsimRecordLoadResponse = true; + ar = (AsyncResult) msg.obj; + if (ar.exception != null) { + Log.e(LOG_TAG, "ar.exception=" + ar.exception); + break; + } + onGetCSimMdnDone(ar); + break; + case EVENT_GET_CSIM_IMSIM_DONE: + if (DBG) log("EVENT_GET_CSIM_IMSIM_DONE"); + isCsimRecordLoadResponse = true; + ar = (AsyncResult) msg.obj; + if (ar.exception != null) { + Log.e(LOG_TAG, "ar.exception=" + ar.exception); + break; + } + onGetCSimImsimDone(ar); + break; + case EVENT_GET_CSIM_CDMAHOME_DONE: + if (DBG) log("EVENT_GET_CSIM_CDMAHOME_DONE"); + isCsimRecordLoadResponse = true; + ar = (AsyncResult) msg.obj; + if (ar.exception != null) { + Log.e(LOG_TAG, "ar.exception=" + ar.exception); + break; + } + onGetCSimCdmaHomeDone(ar); + break; + case EVENT_GET_CSIM_EPRL_DONE: + if (DBG) log("EVENT_GET_CSIM_EPRL_DONE"); + isCsimRecordLoadResponse = true; + ar = (AsyncResult) msg.obj; + if (ar.exception != null) { + Log.e(LOG_TAG, "ar.exception=" + ar.exception); + break; + } + onGetCSimEprlDone(ar); + break; default: super.handleMessage(msg); }}catch (RuntimeException exc) { @@ -156,6 +206,19 @@ public final class CdmaLteUiccRecords extends SIMRecords { iccFh.loadEFTransparent(EF_CSIM_SPN, obtainMessage(EVENT_GET_CSIM_SPN_DONE)); recordsToLoad++; + + iccFh.loadEFLinearFixed(EF_CSIM_MDN, 1, obtainMessage(EVENT_GET_CSIM_MDN_DONE)); + recordsToLoad++; + + iccFh.loadEFTransparent(EF_CSIM_IMSIM, obtainMessage(EVENT_GET_CSIM_IMSIM_DONE)); + recordsToLoad++; + + iccFh.loadEFLinearFixedAll(EF_CSIM_CDMAHOME, + obtainMessage(EVENT_GET_CSIM_CDMAHOME_DONE)); + recordsToLoad++; + + iccFh.loadEFTransparent(EF_CSIM_EPRL, obtainMessage(EVENT_GET_CSIM_EPRL_DONE)); + recordsToLoad++; } private void onGetCSimSpnDone(AsyncResult ar) { @@ -205,6 +268,93 @@ public final class CdmaLteUiccRecords extends SIMRecords { phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, spn); } + private void onGetCSimMdnDone(AsyncResult ar) { + byte[] data = (byte[]) ar.result; + if (DBG) log("CSIM_MDN=" + IccUtils.bytesToHexString(data)); + int mdnDigitsNum = 0x0F & data[0]; + mMdn = IccUtils.cdmaBcdToString(data, 1, mdnDigitsNum); + if (DBG) log("CSIM MDN=" + mMdn); + } + + private void onGetCSimImsimDone(AsyncResult ar) { + byte[] data = (byte[]) ar.result; + if (DBG) log("CSIM_IMSIM=" + IccUtils.bytesToHexString(data)); + // C.S0065 section 5.2.2 for IMSI_M encoding + // C.S0005 section 2.3.1 for MIN encoding in IMSI_M. + boolean provisioned = ((data[7] & 0x80) == 0x80); + + if (provisioned) { + int first3digits = ((0x03 & data[2]) << 8) + (0xFF & data[1]); + int second3digits = (((0xFF & data[5]) << 8) | (0xFF & data[4])) >> 6; + int digit7 = 0x0F & (data[4] >> 2); + if (digit7 > 0x09) digit7 = 0; + int last3digits = ((0x03 & data[4]) << 8) | (0xFF & data[3]); + first3digits = adjstMinDigits(first3digits); + second3digits = adjstMinDigits(second3digits); + last3digits = adjstMinDigits(last3digits); + + StringBuilder builder = new StringBuilder(); + builder.append(String.format(Locale.US, "%03d", first3digits)); + builder.append(String.format(Locale.US, "%03d", second3digits)); + builder.append(String.format(Locale.US, "%d", digit7)); + builder.append(String.format(Locale.US, "%03d", last3digits)); + if (DBG) log("min present=" + builder.toString()); + + mMin = builder.toString(); + } else { + if (DBG) log("min not present"); + } + } + + private int adjstMinDigits (int digits) { + // Per C.S0005 section 2.3.1. + digits += 111; + digits = (digits % 10 == 0)?(digits - 10):digits; + digits = ((digits / 10) % 10 == 0)?(digits - 100):digits; + digits = ((digits / 100) % 10 == 0)?(digits - 1000):digits; + return digits; + } + + private void onGetCSimCdmaHomeDone(AsyncResult ar) { + // Per C.S0065 section 5.2.8 + ArrayList dataList = (ArrayList) ar.result; + if (DBG) log("CSIM_CDMAHOME data size=" + dataList.size()); + if (dataList.isEmpty()) { + return; + } + StringBuilder sidBuf = new StringBuilder(); + StringBuilder nidBuf = new StringBuilder(); + + for (byte[] data : dataList) { + if (data.length == 5) { + int sid = ((data[1] & 0xFF) << 8) | (data[0] & 0xFF); + int nid = ((data[3] & 0xFF) << 8) | (data[2] & 0xFF); + sidBuf.append(sid).append(","); + nidBuf.append(nid).append(","); + } + } + // remove trailing "," + sidBuf.setLength(sidBuf.length()-1); + nidBuf.setLength(nidBuf.length()-1); + + mHomeSystemId = sidBuf.toString(); + mHomeNetworkId = nidBuf.toString(); + } + + private void onGetCSimEprlDone(AsyncResult ar) { + // C.S0065 section 5.2.57 for EFeprl encoding + // C.S0016 section 3.5.5 for PRL format. + byte[] data = (byte[]) ar.result; + if (DBG) log("CSIM_EPRL=" + IccUtils.bytesToHexString(data)); + + // Only need the first 4 bytes of record + if (data.length > 3) { + int prlId = ((data[2] & 0xFF) << 8) | (data[3] & 0xFF); + mPrlVersion = Integer.toString(prlId); + } + if (DBG) log("CSIM PRL version=" + mPrlVersion); + } + public byte[] getPreferredLanguage() { return mEFpl; } @@ -212,4 +362,32 @@ public final class CdmaLteUiccRecords extends SIMRecords { public byte[] getLanguageIndication() { return mEFli; } + + public String getMdn() { + return mMdn; + } + + public String getMin() { + return mMin; + } + + public String getSid() { + return mHomeSystemId; + } + + public String getNid() { + return mHomeNetworkId; + } + + public String getPrlVersion() { + return mPrlVersion; + } + + @Override + public boolean isProvisioned() { + // Look for MDN and MIN field to determine if the SIM is provisioned. + if ((mMdn != null) && (mMin != null)) return true; + + return false; + } } diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java index 805ee49aff028..5ebdd22e3c7e2 100755 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java @@ -129,12 +129,12 @@ public class CdmaServiceStateTracker extends ServiceStateTracker { /** Contains the name of the registered network in CDMA (either ONS or ERI text). */ private String curPlmn = null; - private String mMdn; + protected String mMdn; private int mHomeSystemId[] = null; private int mHomeNetworkId[] = null; - private String mMin; - private String mPrlVersion; - private boolean mIsMinInfoReady = false; + protected String mMin; + protected String mPrlVersion; + protected boolean mIsMinInfoReady = false; private boolean isEriTextLoaded = false; protected boolean isSubscriptionFromRuim = false; @@ -373,53 +373,15 @@ public class CdmaServiceStateTracker extends ServiceStateTracker { String cdmaSubscription[] = (String[])ar.result; if (cdmaSubscription != null && cdmaSubscription.length >= 5) { mMdn = cdmaSubscription[0]; - if (cdmaSubscription[1] != null) { - String[] sid = cdmaSubscription[1].split(","); - mHomeSystemId = new int[sid.length]; - for (int i = 0; i < sid.length; i++) { - try { - mHomeSystemId[i] = Integer.parseInt(sid[i]); - } catch (NumberFormatException ex) { - loge("error parsing system id: " + ex); - } - } - } - if (DBG) log("GET_CDMA_SUBSCRIPTION: SID=" + cdmaSubscription[1] ); + parseSidNid(cdmaSubscription[1], cdmaSubscription[2]); - if (cdmaSubscription[2] != null) { - String[] nid = cdmaSubscription[2].split(","); - mHomeNetworkId = new int[nid.length]; - for (int i = 0; i < nid.length; i++) { - try { - mHomeNetworkId[i] = Integer.parseInt(nid[i]); - } catch (NumberFormatException ex) { - loge("GET_CDMA_SUBSCRIPTION: error parsing network id: " + ex); - } - } - } - if (DBG) log("GET_CDMA_SUBSCRIPTION: NID=" + cdmaSubscription[2]); mMin = cdmaSubscription[3]; mPrlVersion = cdmaSubscription[4]; if (DBG) log("GET_CDMA_SUBSCRIPTION: MDN=" + mMdn); mIsMinInfoReady = true; - int otaspMode = getOtasp(); - int oldOtaspMode = mCurrentOtaspMode; - mCurrentOtaspMode = otaspMode; - - // Notify apps subscription info is ready - if (cdmaForSubscriptionInfoReadyRegistrants != null) { - if (DBG) log("GET_CDMA_SUBSCRIPTION: call notifyRegistrants()"); - cdmaForSubscriptionInfoReadyRegistrants.notifyRegistrants(); - } - if (oldOtaspMode != mCurrentOtaspMode) { - if (DBG) { - log("GET_CDMA_SUBSCRIPTION: call notifyOtaspChanged old otaspMode=" + - oldOtaspMode + " new otaspMode=" + mCurrentOtaspMode); - } - phone.notifyOtaspChanged(mCurrentOtaspMode); - } + updateOtaspState(); phone.getIccCard().broadcastIccStateChangedIntent(IccCard.INTENT_VALUE_ICC_IMSI, null); } else { @@ -1622,6 +1584,53 @@ public class CdmaServiceStateTracker extends ServiceStateTracker { cm.setRadioPower(false, null); } + protected void parseSidNid (String sidStr, String nidStr) { + if (sidStr != null) { + String[] sid = sidStr.split(","); + mHomeSystemId = new int[sid.length]; + for (int i = 0; i < sid.length; i++) { + try { + mHomeSystemId[i] = Integer.parseInt(sid[i]); + } catch (NumberFormatException ex) { + loge("error parsing system id: " + ex); + } + } + } + if (DBG) log("CDMA_SUBSCRIPTION: SID=" + sidStr); + + if (nidStr != null) { + String[] nid = nidStr.split(","); + mHomeNetworkId = new int[nid.length]; + for (int i = 0; i < nid.length; i++) { + try { + mHomeNetworkId[i] = Integer.parseInt(nid[i]); + } catch (NumberFormatException ex) { + loge("CDMA_SUBSCRIPTION: error parsing network id: " + ex); + } + } + } + if (DBG) log("CDMA_SUBSCRIPTION: NID=" + nidStr); + } + + protected void updateOtaspState() { + int otaspMode = getOtasp(); + int oldOtaspMode = mCurrentOtaspMode; + mCurrentOtaspMode = otaspMode; + + // Notify apps subscription info is ready + if (cdmaForSubscriptionInfoReadyRegistrants != null) { + if (DBG) log("CDMA_SUBSCRIPTION: call notifyRegistrants()"); + cdmaForSubscriptionInfoReadyRegistrants.notifyRegistrants(); + } + if (oldOtaspMode != mCurrentOtaspMode) { + if (DBG) { + log("CDMA_SUBSCRIPTION: call notifyOtaspChanged old otaspMode=" + + oldOtaspMode + " new otaspMode=" + mCurrentOtaspMode); + } + phone.notifyOtaspChanged(mCurrentOtaspMode); + } + } + @Override protected void log(String s) { Log.d(LOG_TAG, "[CdmaSST] " + s); diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java index db2b4903073bd..dcde71a50925a 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java @@ -561,6 +561,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { boolean allowed = (gprsState == ServiceState.STATE_IN_SERVICE || mAutoAttachOnCreation) && mPhone.mIccRecords.getRecordsLoaded() && + mPhone.mIccRecords.isProvisioned() && mPhone.getState() == Phone.State.IDLE && mInternalDataEnabled && (!mPhone.getServiceState().getRoaming() || getDataOnRoamingEnabled()) && @@ -572,6 +573,7 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { reason += " - gprs= " + gprsState; } if (!mPhone.mIccRecords.getRecordsLoaded()) reason += " - SIM not loaded"; + if (!mPhone.mIccRecords.isProvisioned()) reason += " - SIM not provisioned"; if (mPhone.getState() != Phone.State.IDLE) { reason += " - PhoneState= " + mPhone.getState(); }