android iccid获取不完整,Android调用getSimSerialNumber获取iccid不完整

1、在Android中我们可以通过下面这段代码获取SIM的iccid,关于手机中常用术语简介可参考《Android中CS域和PS域以及手机中常用术语简介》

TelephonyManager telephonyManager=(TelephonyManager) getSystemService(TELEPHONY_SERVICE); String simSerialNumber = telephonyManager.getSimSerialNumber();

注:ICCID (Integrate circuit card identity ) 集成电路卡识别码(固化在手机SIM 卡中) ICCID 为IC 卡的唯一识别号码,共有20 位数字组成。

但调用这个方法有时候并不能获取到20位完整的SIM iccid,怀着好奇心看了下android源码,下面就来一层层揭开这面纱。

frameworks/base/telephony/java/android/telephony/TelephonyManager.java

public String getSimSerialNumber(int subId) {

android.util.SeempLog.record_str(388, ""+subId);

try {

IPhoneSubInfo info = getSubscriberInfo();

if (info == null)

return null;

return info.getIccSerialNumberForSubscriber(subId, mContext.getOpPackageName());

} catch (RemoteException ex) {

return null;

} catch (NullPointerException ex) {

// This could happen before phone restarts due to crashing

return null;

}

}

getIccSerialNumberForSubscriber是在PhoneSubInfoController.java中实现,具体方式如下:

frameworks/opt/telephony/src/java/com/android/internal/telephony/PhoneSubInfoController.java

public String getIccSerialNumberForSubscriber(int subId, String callingPackage) {

Phone phone = getPhone(subId);

if (phone != null) {

if (!checkReadPhoneState(callingPackage, "getIccSerialNumber")) {

return null;

}

return phone.getIccSerialNumber();

} else {

loge("getIccSerialNumber phone is null for Subscription:" + subId);

return null;

}

}

getIccSerialNumber是在GsmCdmaPhone.java实现:

frameworks/opt/telephony/src/java/com/android/internal/telephony/GsmCdmaPhone.java

@Override

public String getIccSerialNumber() {

IccRecords r = mIccRecords.get();

if (!isPhoneTypeGsm() && r == null) {

// to get ICCID form SIMRecords because it is on MF.

r = mUiccController.getIccRecords(mPhoneId, UiccController.APP_FAM_3GPP);

}

return (r != null) ? r.getIccId() : null;

}

getIccId是IccRecords.java中定义的,但其赋值是在IccRecords的两个子类SIMRecords.java和RuimRecords.java中赋值的

frameworks/opt/telephony/src/java/com/android/internal/telephony/uicc/SIMRecords.java

// ***** Overridden from Handler

@Override

public void handleMessage(Message msg) {

AsyncResult ar;

AdnRecord adn;

byte data[];

boolean isRecordLoadResponse = false;

if (mDestroyed.get()) {

loge("Received message " + msg + "[" + msg.what + "] " +

" while being destroyed. Ignoring.");

return;

}

try { switch (msg.what) {

case EVENT_GET_ICCID_DONE:

isRecordLoadResponse = true;

ar = (AsyncResult)msg.obj;

data = (byte[])ar.result;

if (ar.exception != null) {

break;

}

mIccId = IccUtils.bcdToString(data, 0, data.length);

mFullIccId = IccUtils.bchToString(data, 0, data.length);

log("iccid: " + SubscriptionInfo.givePrintableIccid(mFullIccId));

break;

...

default:

super.handleMessage(msg); // IccRecords handles generic record load responses

}}catch (RuntimeException exc) {

// I don't want these exceptions to be fatal

logw("Exception parsing SIM record", exc);

} finally {

// Count up record load responses even if they are fails

if (isRecordLoadResponse) {

onRecordLoaded();

}

}

}

EVENT_GET_ICCID_DONE是在IccFileHandler.java@loadEFTransparent里调用

public void loadEFTransparent(int fileid, Message onLoaded) {

Message response = obtainMessage(EVENT_GET_BINARY_SIZE_DONE,

fileid, 0, onLoaded);

mCi.iccIOForApp(COMMAND_GET_RESPONSE, fileid, getEFPath(fileid),

0, 0, GET_RESPONSE_EF_SIZE_BYTES, null, null, mAid, response);

}

这里会调用RIL.java里的iccIOForApp向modem发送查询请求,有结果返回后便会调用EVENT_GET_ICCID_DONE对mIccId进行赋值

frameworks/opt/telephony/src/java/com/android/internal/telephony/uicc/IccUtils.java

public static String

bcdToString(byte[] data, int offset, int length) {

StringBuilder ret = new StringBuilder(length*2);

for (int i = offset ; i < offset + length ; i++) {

int v;

v = data[i] & 0xf;

if (v > 9) break;

ret.append((char)('0' + v));

v = (data[i] >> 4) & 0xf;

// Some PLMNs have 'f' as high nibble, ignore it

if (v == 0xf) continue;

if (v > 9) break;

ret.append((char)('0' + v));

}

return ret.toString();

}

可以看到在bcdToString时,如果data里面包含非0-9的字符,就只取第一个非0-9的字段

eg:8986011785a113040534 ,按照这个方法获取的为8986011785

当然在GsmCdmaPhone@getFullIccSerialNumber,这个方法在调用的时候,只是调用了IccUtils.bchToString

/** * Some fields (like ICC ID) in GSM SIMs are stored as nibble-swizzled BCH */

public static String

bchToString(byte[] data, int offset, int length) {

StringBuilder ret = new StringBuilder(length*2);

for (int i = offset ; i < offset + length ; i++) {

int v;

v = data[i] & 0xf;

ret.append("0123456789abcdef".charAt(v));

v = (data[i] >> 4) & 0xf;

ret.append("0123456789abcdef".charAt(v));

}

return ret.toString();

}

此时获取到的iccid才是完整的,然而现在很多sim卡的iccid包含0-f之外的字符,还是不能取到完整的iccid,如果你有好的方案,欢迎留言指教,谢谢! eg:8986011785N113040534,不能获取到正确完整的20位iccid