Android: 检查手机是否支持双卡

116
经过在论坛上的大量研究,我现在知道没有办法找到双卡手机中两张 SIM 卡的 IMSI 或 SIM 序列号(除非联系制造商)。现在我的问题变成了,我们能否检测到手机有两个 SIM 卡?我相信可以通过一些智能方法检测到。我能想到的几种方法如下:
  1. 拨打 USSD 码并跟踪日志以获取 IMEI 号码(我在印度使用 *139# 尝试过,它有效。)这将为我提供从其中一个 SIM 卡拨出 USSD 码的 IMEI 号码。(假定手机遵循 Android 指南并具有两个 IMEI 号码)

  2. 存储 SIM 序列号和/或 IMSI,并在检测到任何其他 IMSI/序列号时追踪一些日志或通过一些广播事件处理(即使手机没有重新启动,即 SIM 卡被切换)。

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

如果有人能想到其他方法,他们是非常欢迎的。我真的很感激对此提供任何帮助的人。此外,如果有人有任何关于制造商 API 或联系方式的信息,请与社区分享。

嗨Rajkiran,终于我找到了解决方案,对我来说它很好用。希望这对于想要在移动应用程序中处理双卡的每个人都有所帮助。双卡处理API没有文档记录。请检查我的答案,对我来说它很好用。https://dev59.com/bXTYa4cB1Zd3GeqPuGXh#21448605 - Jebasuthan
1
谢谢,但是你的回答并没有回答我的问题。我想知道关于第二个SIM卡和IMEI的所有细节。@Pied Piper的回答帮助我得到了所有信息。 - Rajkiran
@Rajkiran Pied Piper的答案确实帮了你吗?我已经在我的三星Galaxy Y Duos上检查过他的代码,但它并没有起作用。你能帮我找到双卡手机的IMEI号码吗? - Nitish Patel
@nitishpatel:是的,它确实有帮助。不幸的是,我没有Y Duos来检查。但我相信三星在Android 4.0及以上版本上使用了不同的双卡处理机制。Pied Pipers的答案适用于4.0及以上设备。对于其他设备,您需要使用反射进行更深入的挖掘。 - Rajkiran
有没有使用Cordova的解决方案? - Shri
显示剩余3条评论
9个回答

189

更新 2015年3月23日:

从Android 5.1版本开始,官方提供了多重SIM API

其他可能的选项:

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

通过这些IMEI号码,你可以检查手机是否是双卡。

尝试执行以下活动:

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 :

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

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

4
酷!在Karbonn上使用“getDeviceId”对我有效。将搜索三星的方法,并在拥有时更新此处。谢谢,太棒了。 - 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
1
抱歉Vaibhav,我提供的方法是针对串行号而不是IMSI的。我无法通过这种方式获取双重设备的IMSI。我用这种方式实现了它:http://stackoverflow.com/a/30647918/2267723 - Maher Abuthraa
4
deviceId指的是IMEI而不是IMSI,是吗? - falko
显示剩余29条评论

6

我有一台安卓4.4.4版本的三星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;
}

这样,如果有新的方法名被提议用于某个设备,您只需将其添加到数组中即可,它应该可以正常工作。

6

在搜索网络运营商检查方法时,我发现了几种本地解决方案。

对于 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,只需检查手机是否具有双重/三重/多重SIM卡:

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

5
我能够读取一加 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
1
这是官方的SDK。应该适用于所有手机。我已经在OnePlus 2上测试过了。 - Swapnil Godambe
那么@Swapnil的被接受的答案也是在说同样的事情,对吧? - Rajkiran

2

我查看了通话记录,发现在双卡手机中(我在 Xolo A500s Lite 上检查过),除了 managedCursor 的常规字段外,还有一个名为“simid”的列,用于在通话记录中标记每个通话的 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;
}

我在单卡手机上找不到这个栏目(我在Xperia L上检查过)。

因此,虽然我不认为这是检查双卡性质的绝对方法,但我在这里发布它,因为它可能对某些人有用。


你正在读取一个数据库,是哪一个?请澄清一下你在这里做什么。(“contacts”数据库存储在哪里?) - not2qubit

1

提示:

你可以尝试使用

ctx.getSystemService("phone_msim")

替代

ctx.getSystemService(Context.TELEPHONY_SERVICE)

如果您已经尝试过Vaibhav的答案并且telephony.getClass().getMethod()失败了,上面是适用于我使用的高通手机的解决方法。

只要答案是“完整的”,您可以在SO答案中发布其他语言网页的链接以获取补充信息。http://meta.stackoverflow.com/questions/271060/is-it-ok-if-i-add-links-to-non-english-written-posts-in-a-so-answer - mach
那其实是一个有用的链接,不需要删除它。 - not2qubit

1

没关系。Android现在已经有双卡API了。https://developer.android.com/about/versions/android-5.1.html#multisim对于其他问题,您可以参考@vaibhav的答案。 - Rajkiran

0

适用于 SDK API 26+ (Build.VERSION_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
}

-2

3
好的,我会尽力为您翻译以下内容:是的,我知道。但这就是为什么我正在尝试寻找解决方法。而且一定有一些方法。试试从Play商店下载USSDDualWidget应用程序。它可以在两个SIM卡之间切换。我甚至尝试过反向工程代码,但没有成功。 - Rajkiran
那似乎在三星双卡手机上可以工作,至少我只能在三星Galaxy Y Duos和Galaxy Grand上进行检查。 - Rajkiran
我的猜测是有一些制造商特定的API可以实现这一点。我甚至不知道三星公司制造双卡手机! - gonzobrains
24
许多时候,Commonsware曾说过"这是不可能的",但其他开发人员却实现了这一点。因此,并不是像Commonsware所说的总是正确的 :-) - Lalit Poptani
1
你说得没错,但我认为他是所有Android相关事物的相当可靠的资源。关于这个特定问题,当然,我也做了同样的事情并使用反射来获得双卡数据,但那是针对我的特定设备。直到今天,我仍然不相信有任何通用方法可以解决这个问题。同时请注意,我引用了谷歌开发者和Commonsware的观点。 - gonzobrains
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接