Android: 检查手机是否是双 SIM 卡

经过在论坛上的大量研究,现在我知道没有办法找到 IMSI 或 SIM 序列号的两个 SIM 卡在双 SIM 电话(除了联系制造商)。现在我改变的问题是,我们能检测到手机有两个 SIMs 吗?我相信只要有点智慧,就能发现它。我能想到的几种方法是:

  1. 拨打 USSD 代码并跟踪 IMEI 号码的日志(我在印度用 * 139 # 尝试过这种方法。起作用了。)这将给我 IMEI 号码的 SIM 从我拨号的 USSD 代码。(据推测,这款手机遵循 Android 指南,并且有两个 IMEI 号码。)

  2. 存储 SIM 卡的序列号和/或 IMSI。在检测到任何其他 IMSI/Serial 号码之后,即使电话没有重启(即 SIM 被切换) ,也可以通过跟踪一些日志或通过一些广播事件处理。

  3. 通过拨打 * 06 # ,您将看到两个 IMEI 号码。用某种方式,得到那两个数字。(比如屏幕捕捉和文本图像解析。)

如果有人能想到其他办法,我们非常欢迎。如果你能帮忙,我会很感激的。此外,如果任何人有任何关于制造商 API 的信息或联系他们的链接,请与社区的人分享。

122961 次浏览

Commonsware 表示这是不可能的,请参阅以下内容:

使用 Android SDK 检测双 SIM 是不可能的。

Here is further dialog on the subject:

谷歌开发团队的家伙说,使用 Android SDK 检测双 SIM 是不可能的。

15年3月23日更新:

从 Android 5.1开始,官方的多重 SIM 应用程序界面已经可以使用了

其他可能的选择:

You can use Java 反射 to get both IMEI numbers.

使用这些 IMEI 号码,你可以检查手机是否是 DUAL SIM 卡。

尝试以下活动:

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;


public class MainActivity extends Activity {


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);


TelephonyInfo telephonyInfo = TelephonyInfo.getInstance(this);


String imeiSIM1 = telephonyInfo.getImsiSIM1();
String imeiSIM2 = telephonyInfo.getImsiSIM2();


boolean isSIM1Ready = telephonyInfo.isSIM1Ready();
boolean isSIM2Ready = telephonyInfo.isSIM2Ready();


boolean isDualSIM = telephonyInfo.isDualSIM();


TextView tv = (TextView) findViewById(R.id.tv);
tv.setText(" IME1 : " + imeiSIM1 + "\n" +
" IME2 : " + imeiSIM2 + "\n" +
" IS DUAL SIM : " + isDualSIM + "\n" +
" IS SIM1 READY : " + isSIM1Ready + "\n" +
" IS SIM2 READY : " + isSIM2Ready + "\n");
}
}

And here is TelephonyInfo.java :

import java.lang.reflect.Method;


import android.content.Context;
import android.telephony.TelephonyManager;


public final class TelephonyInfo {


private static TelephonyInfo telephonyInfo;
private String imeiSIM1;
private String imeiSIM2;
private boolean isSIM1Ready;
private boolean isSIM2Ready;


public String getImsiSIM1() {
return imeiSIM1;
}


/*public static void setImsiSIM1(String imeiSIM1) {
TelephonyInfo.imeiSIM1 = imeiSIM1;
}*/


public String getImsiSIM2() {
return imeiSIM2;
}


/*public static void setImsiSIM2(String imeiSIM2) {
TelephonyInfo.imeiSIM2 = imeiSIM2;
}*/


public boolean isSIM1Ready() {
return isSIM1Ready;
}


/*public static void setSIM1Ready(boolean isSIM1Ready) {
TelephonyInfo.isSIM1Ready = isSIM1Ready;
}*/


public boolean isSIM2Ready() {
return isSIM2Ready;
}


/*public static void setSIM2Ready(boolean isSIM2Ready) {
TelephonyInfo.isSIM2Ready = isSIM2Ready;
}*/


public boolean isDualSIM() {
return imeiSIM2 != null;
}


private TelephonyInfo() {
}


public static TelephonyInfo getInstance(Context context){


if(telephonyInfo == null) {


telephonyInfo = new TelephonyInfo();


TelephonyManager telephonyManager = ((TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE));


telephonyInfo.imeiSIM1 = telephonyManager.getDeviceId();;
telephonyInfo.imeiSIM2 = null;


try {
telephonyInfo.imeiSIM1 = getDeviceIdBySlot(context, "getDeviceIdGemini", 0);
telephonyInfo.imeiSIM2 = getDeviceIdBySlot(context, "getDeviceIdGemini", 1);
} catch (GeminiMethodNotFoundException e) {
e.printStackTrace();


try {
telephonyInfo.imeiSIM1 = getDeviceIdBySlot(context, "getDeviceId", 0);
telephonyInfo.imeiSIM2 = getDeviceIdBySlot(context, "getDeviceId", 1);
} catch (GeminiMethodNotFoundException e1) {
//Call here for next manufacturer's predicted method name if you wish
e1.printStackTrace();
}
}


telephonyInfo.isSIM1Ready = telephonyManager.getSimState() == TelephonyManager.SIM_STATE_READY;
telephonyInfo.isSIM2Ready = false;


try {
telephonyInfo.isSIM1Ready = getSIMStateBySlot(context, "getSimStateGemini", 0);
telephonyInfo.isSIM2Ready = getSIMStateBySlot(context, "getSimStateGemini", 1);
} catch (GeminiMethodNotFoundException e) {


e.printStackTrace();


try {
telephonyInfo.isSIM1Ready = getSIMStateBySlot(context, "getSimState", 0);
telephonyInfo.isSIM2Ready = getSIMStateBySlot(context, "getSimState", 1);
} catch (GeminiMethodNotFoundException e1) {
//Call here for next manufacturer's predicted method name if you wish
e1.printStackTrace();
}
}
}


return telephonyInfo;
}


private static String getDeviceIdBySlot(Context context, String predictedMethodName, int slotID) throws GeminiMethodNotFoundException {


String imei = null;


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 getSimID = telephonyClass.getMethod(predictedMethodName, parameter);


Object[] obParameter = new Object[1];
obParameter[0] = slotID;
Object ob_phone = getSimID.invoke(telephony, obParameter);


if(ob_phone != null){
imei = ob_phone.toString();


}
} catch (Exception e) {
e.printStackTrace();
throw new GeminiMethodNotFoundException(predictedMethodName);
}


return imei;
}


private static  boolean getSIMStateBySlot(Context context, String predictedMethodName, int slotID) throws GeminiMethodNotFoundException {


boolean isReady = false;


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 getSimStateGemini = telephonyClass.getMethod(predictedMethodName, parameter);


Object[] obParameter = new Object[1];
obParameter[0] = slotID;
Object ob_phone = getSimStateGemini.invoke(telephony, obParameter);


if(ob_phone != null){
int simState = Integer.parseInt(ob_phone.toString());
if(simState == TelephonyManager.SIM_STATE_READY){
isReady = true;
}
}
} catch (Exception e) {
e.printStackTrace();
throw new GeminiMethodNotFoundException(predictedMethodName);
}


return isReady;
}




private static class GeminiMethodNotFoundException extends Exception {


private static final long serialVersionUID = -996812356902545308L;


public GeminiMethodNotFoundException(String info) {
super(info);
}
}
}

编辑:

Getting access of methods like "getDeviceIdGemini" for other SIM slot's detail has prediction that method exist.

如果该方法的名称与设备制造商提供的名称不匹配,则该方法不会起作用。您必须为这些设备找到相应的方法名。

Finding method names for other manufacturers can be done using Java reflection as follows :

public static void printTelephonyManagerMethodNamesForThisDevice(Context context) {


TelephonyManager telephony = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
Class<?> telephonyClass;
try {
telephonyClass = Class.forName(telephony.getClass().getName());
Method[] methods = telephonyClass.getMethods();
for (int idx = 0; idx < methods.length; idx++) {


System.out.println("\n" + methods[idx] + " declared by " + methods[idx].getDeclaringClass());
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}

编辑:

正如 Seetha在其评论中指出的:

telephonyInfo.imeiSIM1 = getDeviceIdBySlot(context, "getDeviceIdDs", 0);
telephonyInfo.imeiSIM2 = getDeviceIdBySlot(context, "getDeviceIdDs", 1);

这对她很有效。她成功地获得了两个 IMEI 号码的两个 SIM 在三星 Duos 设备。

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

编辑2:

用于检索数据的方法是针对联想 A319和该制造商的其他手机(Credit Maher Abuthraa) :

telephonyInfo.imeiSIM1 = getDeviceIdBySlot(context, "getSimSerialNumberGemini", 0);
telephonyInfo.imeiSIM2 = getDeviceIdBySlot(context, "getSimSerialNumberGemini", 1);

小贴士:

你可以试试

ctx.getSystemService("phone_msim")

而不是

ctx.getSystemService(Context.TELEPHONY_SERVICE)

如果你已经尝试了 Vaibhav 的答案和 telephony.getClass().getMethod()失败,以上是我的 Qualcomm手机工程。

我看了一下通话记录,我注意到除了 ManagedCursor 内容中的通常字段之外,我们在双 SIM 手机中有一列“ simid”(我在 Xolo A500s Lite 上查过) ,以便用 SIM 卡标记通话记录中的每个通话。这个值是1或2,最可能表示 SIM1/SIM2。

managedCursor = context.getContentResolver().query(contacts, null, null, null, null);
managedCursor.moveToNext();
for(int i=0;i<managedCursor.getColumnCount();i++)
{//for dual sim phones
if(managedCursor.getColumnName(i).toLowerCase().equals("simid"))
indexSIMID=i;
}

我没有在一部 SIM 手机中找到这个专栏(我在 Xperia L 上查过)。

因此,虽然我不认为这是一个简单的方法来检查双 SIM 的性质,我张贴在这里,因为它可能是有用的人。

I have a Samsung Duos device with Android 4.4.4 and the method suggested by Seetha in the accepted answer (i.e. call getDeviceIdDs) does not work for me, as the method does not exist. I was able to recover all the information I needed by calling method "getDefault(int slotID)", as shown below:

public static void samsungTwoSims(Context context) {
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 getFirstMethod = telephonyClass.getMethod("getDefault", parameter);


Log.d(TAG, getFirstMethod.toString());


Object[] obParameter = new Object[1];
obParameter[0] = 0;
TelephonyManager first = (TelephonyManager) getFirstMethod.invoke(null, obParameter);


Log.d(TAG, "Device Id: " + first.getDeviceId() + ", device status: " + first.getSimState() + ", operator: " + first.getNetworkOperator() + "/" + first.getNetworkOperatorName());


obParameter[0] = 1;
TelephonyManager second = (TelephonyManager) getFirstMethod.invoke(null, obParameter);


Log.d(TAG, "Device Id: " + second.getDeviceId() + ", device status: " + second.getSimState()+ ", operator: " + second.getNetworkOperator() + "/" + second.getNetworkOperatorName());
} catch (Exception e) {
e.printStackTrace();
}
}

此外,我还重写了迭代测试方法以恢复此信息的代码,以便使用方法名数组而不是 try/catch 序列。例如,为了确定我们是否有两个活跃的 SIMs,我们可以这样做:

private static String[] simStatusMethodNames = {"getSimStateGemini", "getSimState"};




public static boolean hasTwoActiveSims(Context context) {
boolean first = false, second = false;


for (String methodName: simStatusMethodNames) {
// try with sim 0 first
try {
first = getSIMStateBySlot(context, methodName, 0);
// no exception thrown, means method exists
second = getSIMStateBySlot(context, methodName, 1);
return first && second;
} catch (GeminiMethodNotFoundException e) {
// method does not exist, nothing to do but test the next
}
}
return false;
}

这样,如果为某个设备建议了一个新的方法名,您可以简单地将其添加到数组中,它就应该可以工作了。

我能够阅读两个 IMEI 的从一加2手机

 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
TelephonyManager manager = (TelephonyManager) getActivity().getSystemService(Context.TELEPHONY_SERVICE);
Log.i(TAG, "Single or Dual Sim " + manager.getPhoneCount());
Log.i(TAG, "Default device ID " + manager.getDeviceId());
Log.i(TAG, "Single 1 " + manager.getDeviceId(0));
Log.i(TAG, "Single 2 " + manager.getDeviceId(1));
}

There are several native solutions I've found while searching the way to check network operator.

空气污染指数 > = 17:

TelephonyManager manager = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);


// Get information about all radio modules on device board
// and check what you need by calling #getCellIdentity.


final List<CellInfo> allCellInfo = manager.getAllCellInfo();
for (CellInfo cellInfo : allCellInfo) {
if (cellInfo instanceof CellInfoGsm) {
CellIdentityGsm cellIdentity = ((CellInfoGsm) cellInfo).getCellIdentity();
//TODO Use cellIdentity to check MCC/MNC code, for instance.
} else if (cellInfo instanceof CellInfoWcdma) {
CellIdentityWcdma cellIdentity = ((CellInfoWcdma) cellInfo).getCellIdentity();
} else if (cellInfo instanceof CellInfoLte) {
CellIdentityLte cellIdentity = ((CellInfoLte) cellInfo).getCellIdentity();
} else if (cellInfo instanceof CellInfoCdma) {
CellIdentityCdma cellIdentity = ((CellInfoCdma) cellInfo).getCellIdentity();
}
}

在 AndroidManifest 中添加权限:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
</manifest>

要获得网络运营商,你可以检查 mcc 和 mnc 代码:

空气污染指数 > = 22:

final SubscriptionManager subscriptionManager = SubscriptionManager.from(context);
final List<SubscriptionInfo> activeSubscriptionInfoList = subscriptionManager.getActiveSubscriptionInfoList();
for (SubscriptionInfo subscriptionInfo : activeSubscriptionInfoList) {
final CharSequence carrierName = subscriptionInfo.getCarrierName();
final CharSequence displayName = subscriptionInfo.getDisplayName();
final int mcc = subscriptionInfo.getMcc();
final int mnc = subscriptionInfo.getMnc();
final String subscriptionInfoNumber = subscriptionInfo.getNumber();
}

对于 API > = 23,只需检查手机是否是双卡/三卡/多卡:

TelephonyManager manager = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
if (manager.getPhoneCount() == 2) {
// Dual sim
}

我在三星 S8上发现了这些系统属性

SystemProperties.getInt("ro.multisim.simslotcount", 1) > 1

还有,根据资料来源: https://android.googlesource.com/platform/frameworks/base/+/master/telephony/java/com/android/internal/telephony/TelephonyProperties.java

在 Multisim 上返回“ dsds”或“ dsda”。

我已经在三星 S8上测试过了,它很管用

对于 sdk api 26 + (Build.VERION _ CODES.O) :

val telephony = context.getSystemService(Service.TELEPHONY_SERVICE) as? TelephonyManager
// check telephony on null
val simCount = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
telephony.activeModemCount
} else {
telephony.phoneCount
}