Android双卡通话记录获取问题:无法识别SIM归属,方法返回-1
解决Android API21+双卡通话记录的SIM卡识别问题
我完全理解你的痛点——双卡设备的通话记录SIM识别在API21(Lollipop)之后确实变得棘手,旧的方案大多失效,官方文档也没给出特别明确的指引。你遇到的旧方法返回-1,本质是因为早期的SimCallLog相关字段在API21之后已经不再被系统正确填充了,下面是针对API21+的可行解决方案:
核心思路:利用PhoneAccount机制关联SIM卡
API21引入了PhoneAccount体系,用来统一管理设备上的通话账户(包括双卡的两个SIM卡)。通话记录中会存储对应的PHONE_ACCOUNT_ID,我们可以通过这个ID关联到具体的SIM卡信息。
步骤1:确保权限齐全
首先要确保你的应用已经申请了必要的权限:
READ_CALL_LOG:必须权限,用于读取通话记录- API23+(Marshmallow)及以上:需要动态申请上述权限
- API26+(Oreo):如果需要获取SIM卡的详细信息(比如运营商名称),还需要
READ_PHONE_STATE权限
步骤2:修改通话记录查询逻辑,加入PHONE_ACCOUNT_ID字段
修改你的getCalldetailsNow()方法,在Cursor查询的projection中加入Calls.PHONE_ACCOUNT_ID字段,这样就能拿到每条通话对应的账户ID:
private void getCalldetailsNow() { @SuppressLint("MissingPermission") String[] projection = { Calls._ID, Calls.NUMBER, Calls.TYPE, Calls.DATE, Calls.DURATION, Calls.PHONE_ACCOUNT_ID // 新增:获取通话对应的PhoneAccount ID }; Cursor cursor = getContentResolver().query( Calls.CONTENT_URI, projection, null, null, Calls.DATE + " DESC" ); if (cursor != null && cursor.moveToFirst()) { do { // 读取原有通话详情字段 String number = cursor.getString(cursor.getColumnIndex(Calls.NUMBER)); int callType = cursor.getInt(cursor.getColumnIndex(Calls.TYPE)); long date = cursor.getLong(cursor.getColumnIndex(Calls.DATE)); long duration = cursor.getLong(cursor.getColumnIndex(Calls.DURATION)); // 读取PhoneAccount ID并关联SIM卡 String phoneAccountId = cursor.getString(cursor.getColumnIndex(Calls.PHONE_ACCOUNT_ID)); if (phoneAccountId != null) { bindSimInfoToCall(phoneAccountId); } } while (cursor.moveToNext()); cursor.close(); } }
步骤3:通过PhoneAccountManager获取SIM卡信息
新增bindSimInfoToCall()方法,利用PhoneAccountManager和TelephonyManager将PHONE_ACCOUNT_ID关联到具体的SIM卡:
private void bindSimInfoToCall(String phoneAccountId) { PhoneAccountManager accountManager = PhoneAccountManager.from(this); PhoneAccountHandle accountHandle = PhoneAccountHandle.fromString(phoneAccountId); PhoneAccount phoneAccount = accountManager.getPhoneAccount(accountHandle); if (phoneAccount != null) { // 获取SIM卡的显示名称(比如"SIM 1"、"中国移动") String simDisplayName = phoneAccount.getLabel().toString(); // API22+:获取SubscriptionId,用于匹配更详细的SIM信息 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { int subscriptionId = phoneAccount.getSubscriptionId(); TelephonyManager telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); List<SubscriptionInfo> subscriptionInfos = telephonyManager.getSubscriptionInfoList(); if (subscriptionInfos != null) { for (SubscriptionInfo info : subscriptionInfos) { if (info.getSubscriptionId() == subscriptionId) { // 获取SIM卡的运营商名称、号码等更详细信息 String carrierName = info.getCarrierName().toString(); String simNumber = info.getNumber(); // 这里就可以把SIM信息和通话记录关联起来了 Log.d("CallLog", "SIM卡:" + simDisplayName + ",运营商:" + carrierName); } } } } } }
应对厂商兼容问题
部分国产厂商(如小米、华为)可能没有完全遵循官方规范,PHONE_ACCOUNT_ID可能为空或者无法正确关联。这时候可以尝试查询厂商自定义的字段:
- 小米:尝试在projection中加入
"sim_id"字段 - 华为:尝试加入
"sub_id"字段
示例:
// 在projection中加入厂商自定义字段 String[] projection = { // 原有字段... "sim_id" // 小米自定义字段 }; // 然后读取 int simId = cursor.getInt(cursor.getColumnIndex("sim_id"));
为什么旧方法返回-1?
你之前用的旧方案依赖SimCallLog.Calls.SIM_ID这个字段,这个字段在API21之后已经被官方废弃,系统不再主动填充该字段,所以会返回-1,这是正常现象,建议完全切换到PhoneAccount的方案。
内容的提问来源于stack exchange,提问作者Kabilan




