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


113

在论坛上进行了大量研究之后,现在我知道无法找到双SIM卡电话中两个SIM卡的IMSI或SIM序列号(与制造商联系除外)。现在我改变的问题是,我们是否可以检测到手机有两个SIM卡?我相信可以通过一些智能检测到它。我能想到的几种方法是:

  1. 拨打USSD代码并跟踪日志以获取IMEI号码(我在印度使用* 139#尝试了此操作。它有效。)这将为我提供从中拨打USSD代码的SIM卡的IMEI号码。(假定手机遵循android准则,并且具有两个IMEI号码。)

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

  3. 通过拨打* 06#,您将看到两个IMEI号码。通过某种方式,获得这两个数字。(类似于屏幕捕获和文本的图像解析。)

如果有人能想到其他方法,则非常欢迎。我真的很感激与此有关的任何帮助。另外,如果任何人有关于任何制造商API的任何信息或与他们联系的链接,请与社区人士共享。


您好Rajkiran,最后我得到了对我来说很好的解决方案。希望它对希望在移动应用程序中使用Duel SIM的所有人有所帮助。没有记录决斗SIM卡句柄API。请检查我的答案对我来说很好。stackoverflow.com/questions/17618651/...
Jebasuthan

1
谢谢..但是您的回答没有回答我的问题。我需要有关第二张SIM和IMEI的所有详细信息。@Pied Piper的答案可以帮助我获得一切。
拉吉兰2014年

@Rajkiran Pied Piper的回答真的对您有帮助吗?我已经在三星Galaxy y duos中检查了他的代码,但是没有用。
Nitish Patel 2014年

@nitishpatel:是的,它肯定有帮助。不幸的是,我没有Y Duos可以检查。但我相信三星会在Android 4.0及更高版本上对双SIM卡使用不同的机制。在4.0及更高版本的设备中,Pied Pipers的答案有所帮助。对于其余部分,您将需要使用反射进行更多挖掘。
拉吉兰2014年

嗨,我找到一个解决方案...请检查代码stackoverflow.com/a/32304799/3131373 它已在各种电话上进行了测试
user3131373

Answers:


184

2015年3月23日更新:

从Android 5.1开始,现已提供官方的多个SIM卡API

其他可能的选择:

您可以使用Java反射来获取两个IMEI号。

使用这些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");
    }
}

这里是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);
        }
    }
}

编辑:

对于其他SIM卡插槽的详细信息,如“ getDeviceIdGemini”之类的方法的访问可以预测该方法存在。

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

可以使用Java反射来查找其他制造商的方法名称,如下所示:

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); 

它为她工作。她成功获得了三星Duos设备中两个SIM卡的两个IMEI号码。

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

编辑2:

检索数据的方法适用于该制造商的Lenovo A319和其他手机(信贷Maher Abuthraa):

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

4
凉!在Karbonn上使用“ getDeviceId”对我有用。将搜索三星方法,并在我携带时在这里更新。谢啦。荣誉
Rajkiran

1
是。即使我做到了。发现三星com.android.internal.telephony.RILConstants$SimCardID内部使用。甚至尝试用相同的方法签名和相同的变量名称创建该类。但是没有运气。将尝试获取源代码并尝试进行检查。谢谢。
Rajkiran

4
我使用telephonyInfo.imeiSIM1 = getDeviceIdBySlot(context,“ getDeviceIdDs”,0); telephonyInfo.imeiSIM2 = getDeviceIdBySlot(context,“ getDeviceIdDs”,1); 它为我工作。我成功获得了两个SIM卡的两个IMEI号码。
Seetha

1
如何获得SIM2的电话号码?我正在使用telephony.getLine1Number()方法获取SIM1号码,在方法列表中我找不到类似getLine2Number()或getLine1Number(int)之类的方法
DCoder 2015年

4
deviceId是IMEI而不是IMSI,不是吗?
falko

5

我有一个装有Android 4.4.4的Samsung Duos设备,Seetha在接受的答案中建议的方法(即调用getDeviceIdDs)对我不起作用,因为该方法不存在。通过调用方法“ getDefault(int slotID)”,我能够恢复所需的所有信息,如下所示:

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序列。例如,要确定我们是否有两个活动的SIM卡,我们可以执行以下操作:

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;
}

这样,如果为某个设备建议了新的方法名称,则只需将其添加到数组中即可使用。


4

在寻找检查网络运营商的方式时,发现了几种本机解决方案。

对于API> = 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代码:

对于API> = 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
}

4

我可以从OnePlus 2手机上读取两个IMEI

 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));
            }

很好,它可以在One Plus上使用。但请您发布适用于所有手机和所有Android版本的答案(可能)
Rajkiran,2016年

1
这是一个官方的SDK。应该适用于所有电话。我已经测试了万普拉斯2
Swapnil Godambe

因此,@ Swapnil接受的答案也说明了同样的事情吗?
拉吉兰(Rajkiran)

2

我看了一下通话记录,我注意到,除了managedCursor内容中的常用字段之外,我们在Dual 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卡性质的万无一失的方法,但我将其发布在这里,因为它可能对某人有用。


您正在读取数据库,哪个数据库?请在这里说明您的操作。(“联系人”数据库​​存储在哪里?
not2qubit

1

提示:

您可以尝试使用

ctx.getSystemService("phone_msim")

代替

ctx.getSystemService(Context.TELEPHONY_SERVICE)

如果您已经尝试了Vaibhav的答案并telephony.getClass().getMethod()失败了,那么上面是适用于我的Qualcomm移动设备的方法。


只要答案是完整的,您就可以发布其他语言的网页链接以获取补充信息。meta.stackoverflow.com/questions/271060/...
马赫

这实际上是一个有用的链接,无需删除它。
not2qubit

0

我在三星S8上找到了这些系统属性

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

另外,根据消息来源:https : //android.googlesource.com/platform/frameworks/base/+/master/telephony/java/com/android/internal/telephony/TelephonyProperties.java

getprop persist.radio.multisim.config在multi sim上返回“ dsds”或“ dsda”。

我已经在三星S8上测试过了


没关系。Android现在已经具有用于双SIM卡的API。developer.android.com/about/versions/android-5.1.html#multisim 其他内容,您可以参考@vaibhav的上述答案
Rajkiran

-1

Commonsware说这是不可能的。请参阅以下内容:

无法使用Android SDK检测双SIM卡。

这是有关该主题的进一步对话框:

Google开发人员说无法使用Android SDK检测双SIM卡。


3
好的。我知道。但这就是为什么我试图找到一种解决方法。而且必须有一些。从Play商店尝试USSDDualWidget应用。它实际上可以在两个SIM卡之间切换。甚至试图对代码进行逆向工程,但没有运气。
Rajkiran 2013年

它能在所有设备上运行,还是仅在暴露某种专有接口的有限子集上运行?
gonzobrains

2
为什么这个答案不满意?这是正确的答案。^
N Sharma 2014年

24
很多时候,通用软件都说是不可能的,而其他开发者已经做到了。因此,不是像普通话所说的那样总是正确的:-)
Lalit Poptani 2014年

1
您可能是正确的,但我认为他是Android所有事物的可靠资源。对于这个特定的问题,可以肯定的是,我做了同样的事情,并使用反射来获取双重SIM卡数据,但这是针对我的特定设备的。直到今天,我仍然不相信有任何通用的方法可以做到这一点。还要注意,我也引用了Google开发人员,而不仅仅是Commonsware。
gonzobrains
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.