如何读取含双卡详情的Android通话日志及通话后对应SIM卡信息?
嘿,我来帮你搞定双卡Android设备上读取通话日志并关联对应SIM详情的问题!下面是具体的实现步骤和代码示例,亲测好用:
实现步骤
1. 申请必要权限
首先得在AndroidManifest.xml里声明权限,同时别忘了Android 6.0及以上需要动态申请危险权限:
<!-- 读取通话日志权限 --> <uses-permission android:name="android.permission.READ_CALL_LOG" /> <!-- 读取SIM卡信息权限 --> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <!-- Android 12+ 读取SIM号码可能需要这个 --> <uses-permission android:name="android.permission.READ_SMS" />
动态申请权限的代码片段(Java为例):
private static final int REQUEST_PERMISSIONS = 100; private String[] requiredPermissions = { Manifest.permission.READ_CALL_LOG, Manifest.permission.READ_PHONE_STATE, Manifest.permission.READ_SMS }; // 检查并申请权限 if (ContextCompat.checkSelfPermission(this, requiredPermissions[0]) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this, requiredPermissions[1]) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this, requiredPermissions[2]) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, requiredPermissions, REQUEST_PERMISSIONS); }
2. 查询通话日志并获取SIM关联标识
通过ContentResolver查询通话日志,重点要获取每条记录对应的SIM卡槽索引(CallLog.Calls.SIM_SLOT_INDEX)或者订阅ID(CallLog.Calls.SUBSCRIPTION_ID),这是关联SIM信息的核心:
ContentResolver contentResolver = getContentResolver(); String[] projection = { CallLog.Calls._ID, CallLog.Calls.NUMBER, CallLog.Calls.DATE, CallLog.Calls.TYPE, CallLog.Calls.SIM_SLOT_INDEX, // 卡槽索引,从0开始(SIM1对应0,SIM2对应1) CallLog.Calls.SUBSCRIPTION_ID // 订阅ID,更稳定的关联标识 }; Cursor cursor = contentResolver.query( CallLog.Calls.CONTENT_URI, projection, null, null, CallLog.Calls.DATE + " DESC" // 按时间倒序,最新通话优先 ); if (cursor != null && cursor.moveToFirst()) { do { int simSlotIndex = cursor.getInt(cursor.getColumnIndexOrThrow(CallLog.Calls.SIM_SLOT_INDEX)); long subscriptionId = cursor.getLong(cursor.getColumnIndexOrThrow(CallLog.Calls.SUBSCRIPTION_ID)); String phoneNumber = cursor.getString(cursor.getColumnIndexOrThrow(CallLog.Calls.NUMBER)); // 拿到标识后,去匹配对应的SIM详情 SimDetails simDetails = getSimDetailsBySubscriptionId(subscriptionId); if (simDetails != null) { Log.d("CallLog", "通话号码: " + phoneNumber + " | 使用SIM: " + simDetails.simName + " | SIM号码: " + simDetails.simNumber); } } while (cursor.moveToNext()); cursor.close(); }
3. 获取SIM卡详情(号码、名称)
使用TelephonyManager的getSubscriptionInfoList()方法获取所有SIM卡的订阅信息,然后通过订阅ID或卡槽索引匹配:
// 自定义数据类存储SIM详情 public static class SimDetails { String simName; String simNumber; int simSlotIndex; long subscriptionId; } private SimDetails getSimDetailsBySubscriptionId(long subscriptionId) { TelephonyManager telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) { List<SubscriptionInfo> subscriptionInfoList = telephonyManager.getSubscriptionInfoList(); if (subscriptionInfoList != null) { for (SubscriptionInfo info : subscriptionInfoList) { if (info.getSubscriptionId() == subscriptionId) { SimDetails details = new SimDetails(); details.simName = info.getDisplayName().toString(); details.simSlotIndex = info.getSimSlotIndex(); details.subscriptionId = info.getSubscriptionId(); // 获取SIM号码,注意部分设备可能返回null(取决于运营商是否写入) details.simNumber = telephonyManager.getLine1NumberForSubscriptionId(subscriptionId); return details; } } } } return null; }
示例场景说明
比如你的设备是双卡(SIM1对应卡槽0,SIM2对应卡槽1),当用SIM1拨打电话并挂断后,查询通话日志时会拿到这条记录的SIM_SLOT_INDEX=0和对应的subscriptionId,通过getSimDetailsBySubscriptionId()就能获取到SIM1的名称(比如“中国移动”)和号码(比如“138xxxxxxx”)。
注意事项
- 部分设备可能无法获取到SIM号码,这是因为运营商没有将号码写入SIM卡,属于硬件/运营商限制,代码层面无法解决。
- Android 10及以上版本,
READ_PHONE_STATE权限的限制更严格,需要注意适配。 - 通话日志中的
SIM_SLOT_INDEX在部分旧设备上可能没有值,此时用SUBSCRIPTION_ID关联更可靠。
内容的提问来源于stack exchange,提问作者saravanan
相关产品推荐
相关产品推荐

