2019-03-29 18:45:25 qq_35041057 阅读数 107

Android 获取联系人及其双卡联系人需要申请如下权限

<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_CONTACTS" />

接下来就是实现获取双卡联系人代码:

if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
    SubscriptionManager subscriptionManager = SubscriptionManager.from(mContext);
    List<SubscriptionInfo> mSubcriptionInfos = subscriptionManager.getActiveSubscriptionInfoList();
    List<String> subIds = new ArrayList<>();
    List<String> slotIds = new ArrayList<>();
    if(mSubcriptionInfos != null){
        for(int i = 0 ; i < mSubcriptionInfos.size() ; i ++){
            SubscriptionInfo info = mSubcriptionInfos.get(i);
            if(info != null){
                subIds.add(info.getSubscriptionId() + "");
                slotIds.add((info.getSimSlotIndex() + 1) + "");
            }
            LogUtils.e(TAG , "info : " + info.toString());
        }
    }
    for(int i = 0 ; i < subIds.size() ; i ++){
        ContactSupport.getSimQuery(mContext , "content://icc/adn/subId/" + subIds.get(i) , slotIds.get(i));//这里就是获取双卡的联系人详情void
    }
}
public static void getSimQuery(Context mContext , String mUri , String soltID) {
    //SIM的provider是IccProvider,IccProvider的Uri是content://icc/adn
    LogUtils.e(TAG , "mUri : " + mUri);
    Cursor cursor = null;
    try {
        Uri uri = Uri.parse(mUri);
        cursor = mContext.getContentResolver().query(uri, null,
                null, null, null);
        if(cursor != null){
            while (cursor.moveToNext()) {
                String contactName = cursor.getString(cursor.getColumnIndex("name"));//获取双卡中联系人的名字
                String telNumber = cursor.getString(cursor.getColumnIndex("number"));//获取双卡中联系人的电话号码
                String contactSimID = soltID;//双卡中该联系人为与卡1还是卡2
                String keys[] = cursor.getColumnNames();
                LogUtils.e(TAG , "开始 ===========================================");
                for(String key : keys){
                    LogUtils.e(TAG , "key : " + key + " , value : " + cursor.getString(cursor.getColumnIndex(key)));
                }
                LogUtils.e(TAG , "结束 ===========================================");
            }
        }
    }catch (Exception e){
        e.printStackTrace();
    }finally {
        if(cursor != null){
            cursor.close();
        }
    }

}

以上就是获取双卡联系人的详细信息。

 

2018-11-20 19:27:38 ReadyShowShow 阅读数 1350

关于Android获取运营商的文章已经很多,大部分都是没有深入测试,在双卡设备上存在读取错误信息的问题,这里给出总结。

首先是读取SIM卡权限的问题

注册

    <uses-permission android:name="android.permission.READ_PHONE_STATE" />

动态获取 读SIM卡的权限

获取权限的提示框

在动态获取SIM卡权限时,这里的权限弹窗提示特别让人恶心,总是听人吐槽:一个视频播放软件,要什么拨打电话权限!!!需要一个友好的引导提示。

判断是否拥有读取SIM卡权限:

if (ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) {}

存在误导的API

如果你百度获取SIM卡的运营商时,一般都会看到这个getNetworkOperator函数。从函数名上来看,确认了名称,注定要入坑。如果在双卡设备上反复测试,你就会怀疑这是什么鬼函数名,不按预期变化。事实上,这里的Network是蜂窝移动网络,不是万维网的数据流量。毕竟在手机出现之初期,只有打电话的能力,而且全部的移动设备本身就组织成了一个复杂的通话网络。所以,这里的getNetworkOperator是获取默认通话的运营商而已。

getNetworkOperator获取默认通话的运营商

较为靠谱API

getSimOperator能获取到上网卡的运营商。
从函数名上,很难想象出,这个函数是在读取上网卡信息,能准确获取到上网卡的运营商。

getSimOperator获取上网卡的运营商

android-20 源码

    /**
     * Returns the MCC+MNC (mobile country code + mobile network code) of the
     * provider of the SIM. 5 or 6 decimal digits.
     * <p>
     * Availability: SIM state must be {@link #SIM_STATE_READY}
     *
     * @see #getSimState
     */
    public String getSimOperator() {
        return SystemProperties.get(TelephonyProperties.PROPERTY_ICC_OPERATOR_NUMERIC);
    }

android-21 源码

    /**
     * Returns the MCC+MNC (mobile country code + mobile network code) of the
     * provider of the SIM. 5 or 6 decimal digits.
     * <p>
     * Availability: SIM state must be {@link #SIM_STATE_READY}
     *
     * @see #getSimState
     */
    public String getSimOperator() {
        long subId = getDefaultSubscription();
        Rlog.d(TAG, "getSimOperator(): default subId=" + subId);
        return getSimOperator(subId);
    }

利用getSimOperator获取上网卡运营商的调用如下:


    public static CarrierType getCarrierTypeAsFarAsPossible() {
        Context context = getApplication();
        if (context != null) {
            TelephonyManager telManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
            if (telManager == null) {
                return CarrierType.UNKNOWN;
            }
            String operator = telManager.getSimOperator();
            if (operator != null) {
                return parseOperatorCode(operator);
            }
        }
        return CarrierType.UNKNOWN;
    }

    public static CarrierType parseOperatorCode(String operatorCode) {
        if (operatorCode == null || "".equals(operatorCode)) return UNKNOWN;
        switch (operatorCode) {
            case "46000":
            case "46002":
            case "46007":
            case "46008":
                return MOBILE;
            case "46001":
            case "46006":
            case "46009":
                return UNICOM;
            case "46003":
            case "46005":
            case "46011":
                return TELECOM;
        }
        return UNKNOWN;
    }

另一个存在误导的API

在上述的getSimOperator中,成功的获取了运营的operatorCode,这里的code是5数字的字符串,并不是运营商直接给出的名称。
那什么是运营商给出的名称呢?看下图:

运营自己下发的名称

那如何获取这个运营商下发的名称呢?有一个API是getSimOperatorName,又是一个确认过名称,注定要入坑的API。这个函数看着像是获取上网卡的运营商名称,实际获取的是默认打电话的运营商名称,与getNetworkOperator变化是同样的规律。

比较推荐的方式是getSimOperator获取到operatorCode,再判断移动联通电信。
运营商直接给出的名称会非常的乱,以联通为例,可能会有CUCC、UNICOM、中国联通、中国联通 3G、中国联通 4G等等,非常个性,不可预测,取决于当地运营商大佬想干嘛就干嘛。

如何精确获取上网卡运营商

分系统版本来看待双卡

官方文档只支持5.1及其之后的系统提供双卡API。对于之前的系统版本,目前的市场占比已经很少了。采用的策略是放弃不支持的机器,因为这边对精度要求非常高,可以判断不出来运营商,但不能判断出错。

5.0及其之前 android.os.Build.VERSION.SDK_INT<=21

这部分放弃,将其归入没有获取到系统权限的一样,当做读不出运营商看待。

5.1及其之后 android.os.Build.VERSION.SDK_INT>=22

精准获取上网卡运营商的调用如下:

    public static CarrierType getCurrentCarrierType() {
        if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP_MR1) {
            return UNKNOWN;
        }
        Context context = getApplication();
        if (context == null) {
            return UNKNOWN;
        }
        TelephonyManager telMag = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
        if (telMag == null) {
            return UNKNOWN;
        }
        return parseOperatorCode(telMag.getSimOperator());
    }
    
    public static CarrierType parseOperatorCode(String operatorCode) {
        if (operatorCode == null || "".equals(operatorCode)) return UNKNOWN;
        switch (operatorCode) {
            case "46000":
            case "46002":
            case "46007":
            case "46008":
                return MOBILE;
            case "46001":
            case "46006":
            case "46009":
                return UNICOM;
            case "46003":
            case "46005":
            case "46011":
                return TELECOM;
        }
        return UNKNOWN;
    }

注意

getNetworkOperator不是获取上网卡运营商
getSimOperatorName不是获取上网卡运营商的名称

获取默认通话、默认信息卡

关于获取默认通话的SIM卡

获取默认通话的SIM卡,基本上,大量API都是获取默认通话SIM卡的。在Android5.1之前的版本中,只能获取到默认通话SIM,在Android5.1及其之后,可以用getDefaultVoiceSubscriptionInfo获取到默认通话SIM卡信息。

关于获取默认发信息的SIM卡

在Android5.1及其之后,用getDefaultSmsSubscriptionInfo可以获取到。同样,这是个被隐藏的方法,需要反射。

默认上网卡信息

在5.1及其之后,用getDefaultDataSubscriptionInfo可以获取到,也需要反射才行。

2014-11-06 15:37:42 jaysong2012 阅读数 2480

Android 双卡双待支持检验及SIM信息获取

本文将从应用开发者的角度分析手机是否支持双卡双待,获取如果说希望自己做出支持双卡双待的系统,本文估计就不适合你了,能力不足,还望见谅啊
好了,为了迎合大众的需要,我们还是从普通开发者角度(不需要系统源码支持)开始吧!
原装的android系统不存在双卡这个东西,所以绝大多数的手机几乎都不存在支持双卡双待。一般情况来讲,我们不需要考虑这些问题,如果,确实有那个需要考虑到双卡双待的支持
首先,我们要知道双卡双待是MTK对于android的改动
其次我们要知道TelePhonyManager是对于电话服务的实现
private TelephonyManager Mtm;
Mtm=(TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);

try {
//调用系统函数getSimStateGemini(原装android系统不支持双卡双待,所以不会有这个函数),MTK添加此函数(双卡双待中国特色)
method_ckeckDoublePhone=TelephonyManager.class.getMethod("getSimStateGemini",new Class[]{int.class});
//如果成功调用到此函数,则实现双卡双待
//获得SIM卡1
phone_result_1=method_ckeckDoublePhone.invoke(Mtm, new Object[]{Integer.valueOf(0)});
//获得SIM卡2
phone_result_2=method_ckeckDoublePhone.invoke(Mtm, new Object[]{Integer.valueOf(1)});
} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
isDoublePhone=false;
}catch(SecurityException e){
e.printStackTrace();
isDoublePhone=false;
}catch(IllegalArgumentException e){
e.printStackTrace();
isDoublePhone=false;
}catch(InvocationTargetException e){
e.printStackTrace();
isDoublePhone=false;
}catch(Exception e){
e.printStackTrace();
isDoublePhone=false;
}
顺便一提的是上面的函数getSimStateGemini返回是SIM状态,这里不用管,你只需要知道,如果不进入Exception就是双卡双待
当然,方法不止这一种。至于SIM信息获取,就请大家百度了

如果是做framework层开发发送相对就很简单了
try{
if(ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE)).isSimInsert(0)){
//SIMCard1   inserted
}
if(ITelephony.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_SERVICE)).isSimInsert(1)){
//SIMCard2 inserted
}}catch (RemoteException ex) {
} catch (NullPointerException ex) {
}

或者
if ("READY".equals(SystemProperties.get(TelephonyProperties.PROPERTY_SIM_STATE))) {
//SIMCard1 Ready
}
if ("READY".equals(SystemProperties.get(TelephonyProperties.PROPERTY_SIM_STATE_2))) {
//SIMCard2 Ready
}

当然这里也给大家透露以下SIM state
"ABSENT"     //缺失
"PIN_REQUIRED"   //
"PUK_REQUIRED"
"NETWORK_LOCKED"
"READY"     //ready







2013-08-13 14:13:17 benson37 阅读数 250

目前国内对于双卡智能手机的需求还是很大的,各种复杂的业务会涉及到双卡模块;而android标准的api又不提供对双卡的支持。导致国内双卡模块标准混乱,各个厂商各玩各的。目前我知道的双卡解决方案就有:mtk,展讯,高通,broadcom等。

 

由于公司业务需要,必须要对双卡手机获取各自的imei,imsi,所以也做了一些研究:

 

首先是最为应用广泛的mtk平台,国内山寨手机以及一些低端品牌双卡都是做的mtk的双卡解决方案

 

 

private static void initMtkDoubleSim() {
		try {
			TelephonyManager tm = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
			Class<?> c = Class.forName("com.android.internal.telephony.Phone");
			Field fields1 = c.getField("GEMINI_SIM_1");
			fields1.setAccessible(true);
			simId_1 = (Integer) fields1.get(null);
			Field fields2 = c.getField("GEMINI_SIM_2");
			fields2.setAccessible(true);
			simId_2 = (Integer) fields2.get(null);

			Method m = TelephonyManager.class.getDeclaredMethod(
					"getSubscriberIdGemini", int.class);
			imsi_1 = (String) m.invoke(tm, simId_1);
			imsi_2 = (String) m.invoke(tm, simId_2);

			Method m1 = TelephonyManager.class.getDeclaredMethod(
					"getDeviceIdGemini", int.class);
			imei_1 = (String) m1.invoke(tm, simId_1);
			imei_2 = (String) m1.invoke(tm, simId_2);

			Method mx = TelephonyManager.class.getDeclaredMethod(
					"getPhoneTypeGemini", int.class);
			phoneType_1 = (Integer) mx.invoke(tm, simId_1);
			phoneType_2 = (Integer) mx.invoke(tm, simId_2);

			if (TextUtils.isEmpty(imsi_1) && (!TextUtils.isEmpty(imsi_2))) {
				defaultImsi = imsi_2;
			}
			if (TextUtils.isEmpty(imsi_2) && (!TextUtils.isEmpty(imsi_1))) {
				defaultImsi = imsi_1;
			}
		} catch (Exception e) {
			isMtkDoubleSim = false;
			return;
		}
		isMtkDoubleSim = true;
	}

 

 

 可见,在TelephonyManager中提供了**Gemini的方法,可以用反射很方便地获取到相应的信息。

 

还有

private static void initMtkSecondDoubleSim() {
		try {
			TelephonyManager tm = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
			Class<?> c = Class.forName("com.android.internal.telephony.Phone");
			Field fields1 = c.getField("GEMINI_SIM_1");
			fields1.setAccessible(true);
			simId_1 = (Integer) fields1.get(null);
			Field fields2 = c.getField("GEMINI_SIM_2");
			fields2.setAccessible(true);
			simId_2 = (Integer) fields2.get(null);

			Method mx = TelephonyManager.class.getMethod("getDefault",
					int.class);
			TelephonyManager tm1 = (TelephonyManager) mx.invoke(tm, simId_1);
			TelephonyManager tm2 = (TelephonyManager) mx.invoke(tm, simId_2);

			imsi_1 = tm1.getSubscriberId();
			imsi_2 = tm2.getSubscriberId();

			imei_1 = tm1.getDeviceId();
			imei_2 = tm2.getDeviceId();

			phoneType_1 = tm1.getPhoneType();
			phoneType_2 = tm2.getPhoneType();

			if (TextUtils.isEmpty(imsi_1) && (!TextUtils.isEmpty(imsi_2))) {
				defaultImsi = imsi_2;
			}
			if (TextUtils.isEmpty(imsi_2) && (!TextUtils.isEmpty(imsi_1))) {
				defaultImsi = imsi_1;
			}

		} catch (Exception e) {
			isMtkSecondDoubleSim = false;
			return;
		}
		isMtkSecondDoubleSim = true;
	}

 

 看样子有似乎也是属于mtk平台的解决方案,因为都有GEMINI_SIM_1属性,这种双卡方案只在联想278t上发现过;有两个TelephonyManager实例,根据getDefault方法获取

下面是展讯平台的(貌似市面上手机不多啊):

private static void initSpreadDoubleSim() {
		try {
			Class<?> c = Class
					.forName("com.android.internal.telephony.PhoneFactory");
			Method m = c.getMethod("getServiceName", String.class, int.class);
			spreadTmService = (String) m
					.invoke(c, Context.TELEPHONY_SERVICE, 1);

			TelephonyManager tm = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
			imsi_1 = tm.getSubscriberId();
			imei_1 = tm.getDeviceId();
			phoneType_1 = tm.getPhoneType();
			TelephonyManager tm1 = (TelephonyManager) mContext.getSystemService(spreadTmService);
			imsi_2 = tm1.getSubscriberId();
			imei_2 = tm1.getDeviceId();
			phoneType_2 = tm1.getPhoneType();
			if (TextUtils.isEmpty(imsi_1) && (!TextUtils.isEmpty(imsi_2))) {
				defaultImsi = imsi_2;
			}
			if (TextUtils.isEmpty(imsi_2) && (!TextUtils.isEmpty(imsi_1))) {
				defaultImsi = imsi_1;
			}

		} catch (Exception e) {
			isSpreadDoubleSim = false;
			return;
		}
		isSpreadDoubleSim = true;
	}

 

 这个没有展讯sdk的话还是很难找的吧?

下面是高通的:(貌似高通做的不咋的有些接口没有双卡实现啊)

public static void initQualcommDoubleSim() {
try {
TelephonyManager tm = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
Class<?> cx = Class
.forName("android.telephony.MSimTelephonyManager");
Object obj =mContext.getSystemService(
"phone_msim");
simId_1 = 0;
simId_2 = 1;
 
Method mx = cx.getMethod("getDataState");
// int stateimei_1 = (Integer) mx.invoke(cx.newInstance());
int stateimei_2 = tm.getDataState();
Method mde = cx.getMethod("getDefault");
Method md = cx.getMethod("getDeviceId", int.class);
Method ms = cx.getMethod("getSubscriberId", int.class);
Method mp = cx.getMethod("getPhoneType");
 
// Object obj = mde.invoke(cx);
 
imei_1 = (String) md.invoke(obj, simId_1);
imei_2 = (String) md.invoke(obj, simId_2);
 
imsi_1 = (String) ms.invoke(obj, simId_1);
imsi_2 = (String) ms.invoke(obj, simId_2);
 
int statephoneType_1 = tm.getDataState();
int statephoneType_2 = (Integer) mx.invoke(obj);
Log.e("tag", statephoneType_1 + "---" + statephoneType_2);
 
// Class<?> msc = Class.forName("android.telephony.MSimSmsManager");
// for (Method m : msc.getMethods()) {
// if (m.getName().equals("sendTextMessage")) {
// m.getParameterTypes();
// }
// Log.e("tag", m.getName());
// }
 
} catch (Exception e) {
isQualcommDoubleSim = false;
return;
}
isQualcommDoubleSim = true;
 
}

 

getPhoneType&getDataState 方法看了底层发现没有双卡实现,目前也不知道该咋办...

 

 

未完待续....

没有更多推荐了,返回首页