安卓双卡手机如何通过代码判断来电归属SIM1还是SIM2?
安卓双卡手机识别来电归属SIM卡的通用解决方案
嘿,我来帮你搞定安卓双卡手机识别来电归属SIM卡的问题!你已经能获取到来电号码,但要确定对应SIM卡确实需要分版本处理——毕竟安卓官方的双卡API是逐步完善的。下面是分版本的通用方案,还有代码示例和注意事项:
核心思路
安卓从5.1(API 22)开始才提供官方双卡支持,不同版本的API差异很大,我们需要分版本适配:
- Android 10+(API 29及以上):用
TelecomManager获取来电详情,直接拿到对应SIM的订阅ID - Android 5.1~9(API 22~28):结合
SubscriptionManager匹配号码,或依赖厂商自定义的Intent参数 - Android 5.0及以下:无官方API,只能依赖厂商自定义字段,兼容性较差
代码实现(更新你的BroadcastReceiver)
下面是修改后的PhoneCallReceiver,集成了分版本的SIM卡识别逻辑:
import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.os.Build; import android.telecom.Call; import android.telecom.TelecomManager; import android.telephony.SubscriptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.widget.Toast; import java.util.List; public class PhoneCallReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { try { String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE); String incomingNumber = intent.getExtras().getString(TelephonyManager.EXTRA_INCOMING_NUMBER); // 空号码直接返回 if (TextUtils.isEmpty(incomingNumber)) { return; } if (state.equalsIgnoreCase(TelephonyManager.EXTRA_STATE_RINGING)) { String simInfo = getSimCardInfoForIncomingCall(context, incomingNumber, intent); Toast.makeText(context, "来电来自 " + simInfo + ":" + incomingNumber, Toast.LENGTH_LONG).show(); } if(state.equalsIgnoreCase(TelephonyManager.EXTRA_STATE_OFFHOOK)){ String simInfo = getSimCardInfoForIncomingCall(context, incomingNumber, intent); Toast.makeText(context, "已接听来自 " + simInfo + " 的电话", Toast.LENGTH_SHORT).show(); } if(state.equalsIgnoreCase(TelephonyManager.EXTRA_STATE_IDLE)){ Toast.makeText(context, "通话结束:" + incomingNumber, Toast.LENGTH_SHORT).show(); } } catch (Exception e) { e.printStackTrace(); } } private String getSimCardInfoForIncomingCall(Context context, String incomingNumber, Intent intent) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { // Android 10+ 官方方案:通过TelecomManager获取来电详情 TelecomManager telecomManager = (TelecomManager) context.getSystemService(Context.TELECOM_SERVICE); if (telecomManager != null) { try { List<Call> calls = telecomManager.getCalls(); for (Call call : calls) { // 匹配正在响铃的来电 if (call.getState() == Call.STATE_RINGING) { String callNumber = call.getDetails().getHandle().getSchemeSpecificPart(); if (incomingNumber.equals(callNumber)) { int subscriptionId = call.getDetails().getSubscriptionId(); return getSimLabelBySubscriptionId(context, subscriptionId); } } } } catch (SecurityException e) { e.printStackTrace(); return "权限不足,请检查权限配置"; } } } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { // Android 5.1~9:用SubscriptionManager匹配SIM号码 SubscriptionManager subscriptionManager = (SubscriptionManager) context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE); if (subscriptionManager != null) { List<SubscriptionInfo> activeSubs = subscriptionManager.getActiveSubscriptionInfoList(); if (activeSubs != null) { for (SubscriptionInfo info : activeSubs) { String simNumber = info.getNumber(); // 匹配号码(注意部分SIM可能未存储号码) if (!TextUtils.isEmpty(simNumber) && incomingNumber.contains(simNumber)) { return "SIM" + (info.getSimSlotIndex() + 1) + "(" + info.getDisplayName() + ")"; } } } // 适配厂商自定义Intent参数(小米、华为等) Integer slotId = intent.getIntExtra("android.intent.extra.slot", -1); if (slotId != -1) { return "SIM" + (slotId + 1); } slotId = intent.getIntExtra("slot_id", -1); if (slotId != -1) { return "SIM" + (slotId + 1); } } } else { // Android 5.0及以下:依赖厂商自定义字段 Integer slotId = intent.getIntExtra("android.intent.extra.slot", -1); if (slotId == -1) { slotId = intent.getIntExtra("slot_id", -1); } return slotId != -1 ? "SIM" + (slotId + 1) : "未知SIM卡"; } return "未知SIM卡"; } private String getSimLabelBySubscriptionId(Context context, int subscriptionId) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { SubscriptionManager subscriptionManager = (SubscriptionManager) context.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE); if (subscriptionManager != null) { SubscriptionInfo info = subscriptionManager.getActiveSubscriptionInfo(subscriptionId); if (info != null) { return "SIM" + (info.getSimSlotIndex() + 1) + "(" + info.getDisplayName() + ")"; } } } return "SIM" + subscriptionId; } }
权限配置(更新Manifest)
除了你已申请的权限,还需要添加高版本所需的权限:
<uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.CALL_PHONE" /> <uses-permission android:name="android.permission.READ_CALL_LOG" /> <!-- Android 10+ 读取电话号码权限 --> <uses-permission android:name="android.permission.READ_PHONE_NUMBERS" /> <!-- Android 12+ 读取来电详情权限 --> <uses-permission android:name="android.permission.READ_CALL_SCREENING" />
注意:动态权限申请
从Android 6.0(API 23)开始,READ_PHONE_STATE、READ_PHONE_NUMBERS等属于危险权限,需要在代码中动态申请,否则会触发权限异常。
关键注意事项
- SIM卡无号码的情况:部分SIM卡未存储本机号码,这时候通过号码匹配的方式会失效,优先使用Android 10+的
TelecomManager方案,它不依赖SIM号码。 - 厂商适配:不同厂商可能有自定义的Intent参数,比如小米用
android.intent.extra.slot,华为用slot_id,需要针对主流厂商做适配测试。 - 测试环境:建议用真实双卡手机测试,模拟器大多不支持双卡来电模拟。
- 权限限制:Android 10+的
TelecomManager.getCalls()需要READ_CALL_SCREENING或READ_PHONE_STATE权限,务必确保权限已授予。
内容的提问来源于stack exchange,提问作者Amrut Nandedkar




