Android调用getSimSerialNumber获取iccid不完整

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yin1031468524/article/details/79824844

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

猜你喜欢

转载自blog.csdn.net/yin1031468524/article/details/79824844