1. 程式人生 > >android中雙卡雙待的那些程式碼

android中雙卡雙待的那些程式碼

這陣子忙著整理專案了,所以就沒怎麼出新的文章了,不過下面寫的這篇文章對大家很有幫助。關於雙卡雙待的資訊獲取,包含了imeiphonenumberoperatorName(sim卡生產商,國內就主要指三大運營商了)、NetworkType(這裡就主要是4G、3G等了)。

前言:

睡著國內的雙卡手機出現,導致獲取雙卡的資訊也是成了一個頭痛的事了。google給開發者暴露的api還是停留在單卡上,所以在這裡我就整理出相關的程式碼,讓更多的猿友少走彎路。

首先從phonenumber的獲取著手吧,順便帶著大家一起去看下相關的原始碼,以前獲取phonenumber我是這麼獲取的:

((TelephonyManager) ctx.getSystemService(Context.TELEPHONY_SERVICE))
                    .getLine1Number();

這裡就呼叫了TelephonyManagergetLine1Number方法,這裡順道去原始碼看看getLine1Number是怎麼獲取的:

/**
     * Returns the phone number string for line 1, for example, the MSISDN
     * for a GSM phone. Return null if it is unavailable.
     * <p>
     * Requires Permission:
     *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
     *   OR
     *   {@link android.Manifest.permission#READ_SMS}
     * <p>
     * The default SMS app can also use this.
     */
public String getLine1Number() { return getLine1Number(getSubId()); }

注:我這裡原始碼都是android-25下面的,剛看了下android-23下面的原始碼是這麼呼叫的:

/**
     * Returns the phone number string for line 1, for example, the MSISDN
     * for a GSM phone. Return null if it is unavailable.
     * <p>
     * Requires Permission:
     *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
     *   OR
     *   {@link android.Manifest.permission#READ_SMS}
     * <p>
     * The default SMS app can also use this.
     */
public String getLine1Number() { return getLine1NumberForSubscriber(getDefaultSubscription()); }

還是有些區別的,起碼方法的呼叫是不一樣的,所以建議你在看該篇文章的時候還是把compileSdk升到25
compileSdkVersion 25
可以看到25的api是繼續調了:getLine1Number(getSubId())該方法,那就繼續往下走吧:

/**
     * Returns the phone number string for line 1, for example, the MSISDN
     * for a GSM phone for a particular subscription. Return null if it is unavailable.
     * <p>
     * Requires Permission:
     *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
     *   OR
     *   {@link android.Manifest.permission#READ_SMS}
     * <p>
     * The default SMS app can also use this.
     *
     * @param subId whose phone number for line 1 is returned
     * @hide
     */
    public String getLine1Number(int subId) {
        String number = null;
        try {
            ITelephony telephony = getITelephony();
            if (telephony != null)
                number = telephony.getLine1NumberForDisplay(subId, mContext.getOpPackageName());
        } catch (RemoteException ex) {
        } catch (NullPointerException ex) {
        }
        if (number != null) {
            return number;
        }
        try {
            IPhoneSubInfo info = getSubscriberInfo();
            if (info == null)
                return null;
            return info.getLine1NumberForSubscriber(subId, mContext.getOpPackageName());
        } catch (RemoteException ex) {
            return null;
        } catch (NullPointerException ex) {
            // This could happen before phone restarts due to crashing
            return null;
        }
    }

看到這的時候真的是心灰意冷啊,為什麼這麼說,該方法竟然是hide型別的方法,對於這種方法咋們就用到反射了,後面會詳細介紹的,看看它的引數是如何解釋的:
@param subId whose phone number for line 1 is returned反正我是英語不好的哈,接著我就去查了查相關的說法,這裡去看看這篇文章是如何解釋的(subid指的是什麼),簡單來說subbed指的就是sim卡的索引了,當有一個sim卡的時候subid=1,有兩個的時候subid=2。依次類推就可以知道有幾個卡subid就是多少了。不過這裡的subid還是可以通過反射來獲取subid,後面也會講到如何獲取我們的subbed:

private static final String SIM_LINE_NUMBER = "getLine1Number";
private static final String SIM_STATE = "getSimState";

public static String getSimPhonenumber(Context context, int slotIdx) {
    if (PermissionUtil.hasSelfPermission(context, Manifest.permission.READ_PHONE_STATE) ||
            PermissionUtil.hasSelfPermission(context, "android.permission.READ_PRIVILEGED_PHONE_STATE")) {
        Log.d(TAG, "READ_PHONE_STATE permission has BEEN granted to getSimPhonenumber().");
        if (getSimStateBySlotIdx(context, slotIdx)) {
            return (String) getSimByMethod(context, SIM_LINE_NUMBER, getSubidBySlotId(context, slotIdx));
        }
        return null;
    } else {
        Log.d(TAG, "READ_PHONE_STATE permission has NOT been granted to getSimPhonenumber().");
        return null;
    }
}

/**
 *獲取相應卡的狀態
 * @param slotIdx:0(sim1),1(sim2)
 * @return true:使用中;false:未使用中
 */
public static boolean getSimStateBySlotIdx(Context context, int slotIdx) {
    boolean isReady = false;
    Object getSimState = getSimByMethod(context, SIM_STATE, slotIdx);
    if (getSimState != null) {
        int simState = Integer.parseInt(getSimState.toString());
        if ((simState != TelephonyManager.SIM_STATE_ABSENT) && (simState != TelephonyManager.SIM_STATE_UNKNOWN)) {
            isReady = true;
        }
    }
    return isReady;
}

/**
 * 通過slotid獲取相應卡的subid
 * @param context
 * @param slotId
 * @return
 */
public static int getSubidBySlotId(Context context, int slotId) {
    SubscriptionManager subscriptionManager = (SubscriptionManager) context.getSystemService(
            Context.TELEPHONY_SUBSCRIPTION_SERVICE);
    try {
        Class<?> telephonyClass = Class.forName(subscriptionManager.getClass().getName());
        Class<?>[] parameter = new Class[1];
        parameter[0] = int.class;
        Method getSimState = telephonyClass.getMethod("getSubId", parameter);
        Object[] obParameter = new Object[1];
        obParameter[0] = slotId;
        Object ob_phone = getSimState.invoke(subscriptionManager, obParameter);
        if (ob_phone != null) {
            Log.d(TAG, "slotId:" + slotId + ";" + ((int[]) ob_phone)[0]);
            return ((int[]) ob_phone)[0];
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return -1;
}

/**
*通過反射呼叫相應的方法
*
*/
public static Object getSimByMethod(Context context, String method, int param) {
    TelephonyManager telephony = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
    try {
        Class<?> telephonyClass = Class.forName(telephony.getClass().getName());
        Class<?>[] parameter = new Class[1];
        parameter[0] = int.class;
        Method getSimState = telephonyClass.getMethod(method, parameter);
        Object[] obParameter = new Object[1];
        obParameter[0] = param;
        Object ob_phone = getSimState.invoke(telephony, obParameter);
        if (ob_phone != null) {
            return ob_phone;
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

可以看到getSimPhonenumber方法需要slotIdx引數,這裡還是去這篇文章看看slotldx是咋回事(slotldx到底是啥玩意),通過了解後,slotldx指的是那個卡槽了,如果當前要獲取卡1,slotldx=0;如果是卡2,slotldx=1;到此知道為啥getSimPhonenumber方法需要定義這麼個引數了吧。至於說getSimState方法,還是一樣通過反射去獲取每個卡的狀態的,這裡就不贅述原始碼了。上面可以看到獲取subId的程式碼了吧,就是getSubidBySlotId方法了,這裡通過反射呼叫了SubscriptionManager類中的getSubId方法,需要的引數也是我們的slotId。原始碼如下:

/** @hide */
public static int[] getSubId(int slotId) {
    if (!isValidSlotId(slotId)) {
        logd("[getSubId]- fail");
        return null;
    }
    int[] subId = null;
    try {
        ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
        if (iSub != null) {
            subId = iSub.getSubId(slotId);
        }
    } catch (RemoteException ex) {
        // ignore it
    }
    return subId;
}

還有imeioperatorNameNetworkType都可以通過相應的方法獲取了:

private static final String SIM_OPERATOR_NAME = "getNetworkOperatorName";
private static final String SIM_NETWORK_TYPE = "getNetworkType";
private static final String SIM_IMEI = "getImei";

//獲取相應卡的imei
public static String getSimImei(Context context, int slotIdx) {
    if (PermissionUtil.hasSelfPermission(context, Manifest.permission.READ_PHONE_STATE) ||
            PermissionUtil.hasSelfPermission(context, "android.permission.READ_PRIVILEGED_PHONE_STATE")) {
        Log.d(TAG, "READ_PHONE_STATE permission has BEEN granted to getSimImei().");
        if (getSimStateBySlotIdx(context, slotIdx)) {
            //sim1
            if (slotIdx == 0) {
                //這裡的引數傳的是slotldx
                return (String) getSimByMethod(context, SIM_IMEI, 0);
            } else if (slotIdx == 1) {
                return (String) getSimByMethod(context, SIM_IMEI, 1);
            }
        }
        return null;
    } else {
        Log.d(TAG, "READ_PHONE_STATE permission has NOT been granted to getSimImei().");
        return null;
    }
}

public static String getSimNetworkName(Context context, int slotIdx) {
    if (getSimStateBySlotIdx(context, slotIdx)) {
        return getNetworkName((int)
                getSimByMethod(context, SIM_NETWORK_TYPE, getSubidBySlotId(context, slotIdx)));
    }
    return "UNKNOWN";
}

public static String getSimOperatorName(Context context, int slotIdx) {
    if (getSimStateBySlotIdx(context, slotIdx)) {
        return (String) getSimByMethod(context, SIM_OPERATOR_NAME, getSubidBySlotId(context, slotIdx));
    }
    return null;
}

到此相關的屬性獲取基本ok了,大家如果還需要獲取什麼屬性,直接去TelephonyManager檢視相關的原始碼。還有一個就是插卡和拔卡的監聽、網路變化的監聽:

//網路變化的監聽
public class SimConnectReceive extends BroadcastReceiver {
    private static final String TAG = SimConnectReceive.class.getSimpleName();
    public final static String ACTION_SIM_STATE_CHANGED = ConnectivityManager.CONNECTIVITY_ACTION;

    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals(ACTION_SIM_STATE_CHANGED)) {
            Log.d(TAG, "onReceive");
            EventBus.getDefault().post(new SimConnectChange());
        }
    }
}

//插卡和拔卡的監聽
public class SimStateReceive extends BroadcastReceiver {
    private static final String TAG = SimStateReceive.class.getSimpleName();
    public final static String ACTION_SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED";

    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals(ACTION_SIM_STATE_CHANGED)) {
            Log.d(TAG, "onReceive");
            EventBus.getDefault().post(new SimStateChange());
        }
    }
}

還有就是不要忘了manifest中許可權:
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

對於6.0的動態許可權處理也是新增該許可權的判斷。

demo.png
最後貼上該功能的程式碼:
github傳送門
thanks:DualSIMCard
有什麼問題可以email我:[email protected]

相關推薦

android那些程式碼

這陣子忙著整理專案了,所以就沒怎麼出新的文章了,不過下面寫的這篇文章對大家很有幫助。關於雙卡雙待的資訊獲取,包含了imei、phonenumber、operatorName(sim卡生產商,國內就主要指三大運營商了)、NetworkType(這裡就主要是4G、3

android實現異網識別運營商網路

檢測的程式碼 private void checkIMSi() {         boolean simStateBySlotIdx = SimUtils.getSimStateBySlotIdx(this, 0);     &n

android-手機登入介面嘗試獲取手機號碼,並可選擇subscriptionId

LogonActivity.java public class LogonActivity extends Activity { private EditText nickET; private EditText passwordET; private E

android sim

Android的RIL驅動模組,在hardware/ril目錄下,一共分rild,libril.so以及librefrence_ril.so三個部分,另有一 radiooptions可供自動或手動除錯使用。都依賴於include目錄中ril.h標頭檔案。目前cupcake分支上帶的是gsm的支援,另有

Android獲取本機號碼(無法獲取兩個號碼)

        搞了一個想獲取Android手機的本機號碼的功能,但是發現雙卡雙待的手機是無法獲取到兩個號碼 的。在Android的官方文件是沒有提供相應的Api的,因為標準的Andoird是沒有雙卡的,好像也只有國內才會搞雙卡雙待的神器吧。以下記錄一下做這個功能所學習到的

Android

轉自這裡 一、雙卡雙待背景分析 使用者為了兼顧運營商優勢,使用雙卡雙待手機: 雙卡雙待這項技術在發展中國家使用很普遍,因為在發展中國家電信運營商發展不夠成熟,相關管理制度不完善。從使用者的角度出發,主要考慮資費問題,比如:移動通話訊號好,聯通3G上網流暢、流量費相

Android 識別

簡介Android雙卡雙待已經越來越普及了,解決雙卡雙待管理是廣大手機開發人員必須得面對的問題,為實現Android平臺的雙卡雙待操作,筆者研究了Android 應用層操作雙卡雙待的機制。機制獲取基於ITelephony介面實現phone應用中的“phone服務”,通過Tel

Android 解決的問題 mtk,展訊,高通

目前國內對於雙卡智慧手機的需求還是很大的,各種複雜的業務會涉及到雙卡模組;而android標準的api又不提供對雙卡的支援。導致國內雙卡模組標準混亂,各個廠商各玩各的。目前我知道的雙卡解決方案就有:mtk,展訊,高通,broadcom等。 由於公司業務需要,必須要對雙

聚焦鏈改,

在北京 cdd 北京時間 投資 們的 生態 整合 term ces “為了你我用了半年的積蓄,熬著夜來看你……”這說的大概就是蘋果發布會吧 蘋果的新產品發布會在北京時間9月13日淩晨一點舉行相信果粉們應該都沒有錯過這場盛會 然鵝....窮人才看發布會,土豪都是睡醒直接買這次

華為手機聯通3G上網慢的解決辦法

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

AI技術的蘋果iPhone XS Max7納米6.5寸512GB頂配12799元(公號回覆“蘋果AI”下載PDF資料)

AI技術的蘋果iPhone XS Max雙卡雙待7納米6.5寸512GB頂配12799元(公號回覆“蘋果AI”下載PDF資料) 原創: 秦隴紀 科學Sciences 今天 科學Sciences導讀:北京時間9月13日凌晨1點、美國時間9月12日上午10點,蘋果2018秋季新品釋出會召

getDeviceId unique device ID IMEI 不唯一 會變問題

最近接到使用者反饋無法登入的情況越來越多,因為我們的app設計上是不能換手機用,也就是綁定了唯一的裝置ID。從反饋上來看,有一個線索是大部分是雙卡雙待的使用者出現這個問題,並且切換過SIM卡。看來getDeviceId這個方法在雙卡雙待手機上獲取IMEI還是有問題的。 ge

(一)

前言: 關於雙卡雙待這個問題,調研了幾個月終於有所進展,通過收集各方面的資料,整理出了這個文件。~~╭(╯^╰)╮ 1.獲取雙卡的subId 方法1: /** * @param slotId:卡槽的序號:0代表卡槽1,1代表卡槽2

Android view的緩衝技術

view實現雙緩衝技術 當要繪製的資料量比較大,繪圖時間比較長時,重複繪圖會出現閃爍現象,引起閃爍現象的主要原因是視覺反差比較大。 使用雙緩衝技術可以解決這個問題,Surfaceview預設是使用雙緩衝技術的。 在Android上實現雙緩衝技術的步驟是: 建立一個螢幕大小(實際繪圖區域)的緩衝區(

智慧手機之的實現方案

雙卡雙待的出現背景:         手機通訊發展到一定程度之後,很多使用者希望擁有或已經擁用多個手機電話號碼,特別是針對那些經常需要出差,需要經常切換SIM卡的商務人士而言,其迫切希望能將自己的手機承載多個電話號碼, 根據市場上的這一需求,能同時支援兩張SIM卡的

Android判斷網路是否可用的程式碼_大企鵝

這篇文章主要介紹了Android中判斷網路是否可用的程式碼分享, 本文直接給出實現程式碼,需要的朋友可以參考下 獲取網路資訊需要在AndroidManifest.xml檔案中加入相應的許可權: <uses-permission android:nam

如何有效的清除Android無用的資源(靜態程式碼分析)

最近公司要做這個,簡單調研了一下,現有的大多數部落格也比較舊了,不太合適,總結了這麼幾個方式吧,一起來學習下。 為什麼要清除Android中這些資源呢 是這樣的,今天收到的郵件裡,有這麼一條任務: 資源優化 軟體中無用的圖片和佈局檔案,找到並驗證是否無用. 這個需要設計一套工具進行分析(自

AndroidNFC功能流程圖解析及程式碼演示『轉』

在Android4.0推出的時候,一個非常引人注目的功能就是NFC(Near Field Communication). Near Field Communication (NFC) is a set of short-range wireless tech

cocos2dx對於androidsd的訪問方式

今天在弄專案的時候涉及到cocos2dx引擎需要訪問sdcard上的資源,目前發現兩種比較簡單的方法: 1、直接使用硬編碼的方式來進行訪問,例如“/storage/sdcard0/NetAnswer/questions%d.xml”的方式來確定檔案路徑,但是這種方式是正確的

Android實現擊點贊動畫效果

iv_easy_like.getBackground().setAlpha(0); rlLike.setOnTouchListener(new OnDoubleClickListener(new OnDoubleClickListener.DoubleClickCallb