You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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()方法,利用PhoneAccountManagerTelephonyManagerPHONE_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

火山引擎 最新活动