這陣子忙著整理項目了魔慷,所以就沒怎么出新的文章了只锭,不過下面寫的這篇文章對大家很有幫助。關于雙卡雙待的信息獲取盖彭,包含了imei纹烹、phonenumber、operatorName(sim卡生產(chǎn)商召边,國內就主要指三大運營商了)铺呵、NetworkType(這里就主要是4G、3G等了)隧熙。
前言:
睡著國內的雙卡手機出現(xiàn)片挂,導致獲取雙卡的信息也是成了一個頭痛的事了贞盯。google給開發(fā)者暴露的api還是停留在單卡上音念,所以在這里我就整理出相關的代碼,讓更多的猿友少走彎路躏敢。
首先從phonenumber的獲取著手吧闷愤,順便帶著大家一起去看下相關的源碼,以前獲取phonenumber我是這么獲取的:
((TelephonyManager) ctx.getSystemService(Context.TELEPHONY_SERVICE))
.getLine1Number();
這里就調用了TelephonyManager的getLine1Number方法件余,這里順道去源碼看看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());
}
還是有些區(qū)別的遭居,起碼方法的調用是不一樣的,所以建議你在看該篇文章的時候還是把compileSdk升到25:
compileSdkVersion 25
可以看到25的api是繼續(xù)調了:getLine1Number(getSubId())
該方法旬渠,那就繼續(xù)往下走吧:
/**
* 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類型的方法告丢,對于這種方法咋們就用到反射了枪蘑,后面會詳細介紹的,看看它的參數(shù)是如何解釋的:
@param subId whose phone number for line 1 is returned
反正我是英語不好的哈岖免,接著我就去查了查相關的說法岳颇,這里去看看這篇文章是如何解釋的(subid指的是什么),簡單來說subid
指的就是sim卡的索引了颅湘,當有一個sim卡的時候subid=1赦役,有兩個的時候subid=2。依次類推就可以知道有幾個卡subid就是多少了栅炒。不過這里的subid還是可以通過反射來獲取subid,后面也會講到如何獲取我們的subid:
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;
}
}
/**
*獲取相應卡的狀態(tài)
* @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
參數(shù)赢赊,這里還是去這篇文章看看slotldx
是咋回事(slotldx到底是啥玩意),通過了解后级历,slotldx指的是那個卡槽了释移,如果當前要獲取卡1,slotldx=0寥殖;如果是卡2玩讳,slotldx=1;到此知道為啥getSimPhonenumber
方法需要定義這么個參數(shù)了吧嚼贡。至于說getSimState
方法熏纯,還是一樣通過反射去獲取每個卡的狀態(tài)的,這里就不贅述源碼了粤策。上面可以看到獲取subId的代碼了吧樟澜,就是getSubidBySlotId
方法了,這里通過反射調用了SubscriptionManager
類中的getSubId
方法叮盘,需要的參數(shù)也是我們的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;
}
還有imei
、operatorName
柔吼、NetworkType
都可以通過相應的方法獲取了:
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) {
//這里的參數(shù)傳的是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
查看相關的源碼愈魏。還有一個就是插卡和拔卡的監(jiān)聽觅玻、網(wǎng)絡變化的監(jiān)聽:
//網(wǎng)絡變化的監(jiān)聽
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());
}
}
}
//插卡和拔卡的監(jiān)聽
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" />
歡迎客官到本店光臨:184793647
(qq群)
最后貼上該功能的代碼:
[github傳送門]
(https://github.com/1002326270xc/DoubleSimCard-master)
thanks:DualSIMCard
csdn傳送門
有什么問題可以email我:a1002326270@163.com