onCallAdded的回调如何获取当前设备的电话号码和SIM卡槽?
Posted
技术标签:
【中文标题】onCallAdded的回调如何获取当前设备的电话号码和SIM卡槽?【英文标题】:How to get the phone number and SIM card slot for the current device, on the callback of onCallAdded? 【发布时间】:2021-12-16 22:37:00 【问题描述】:背景
我正在开发一个实现InCallService 的应用,因此它可以处理电话
问题
在具有多 SIM 卡的设备上,我需要显示使用哪个 SIM 卡和相关电话号码的信息(当前设备)。
问题是,我找不到从哪里获得这些信息。
我发现了什么
鉴于我达到了onCallAdded 之类的功能,我得到了一个Call 类的实例,所以我需要将我从那里得到的东西关联到一个 SIM 卡插槽和电话号码。
使用 call.getDetails().getHandle()
,我可以获得一个 Uri,其中仅包含另一个呼叫者的电话号码(呼叫当前用户或当前用户呼叫的人)。
我可以遍历所有 SIM 卡,但找不到可以用来在它们和当前通话之间映射的内容:
final TelephonyManager telephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
final SubscriptionManager subscriptionManager = SubscriptionManager.from(context);
for (final SubscriptionInfo subscriptionInfo : subscriptionManager.getActiveSubscriptionInfoList())
final TelephonyManager subscriptionId = telephonyManager.createForSubscriptionId(subscriptionInfo.getSubscriptionId());
有一个旧代码不再使用 call.getDetails().getAccountHandle().getId()
和 SubscriptionManager.from(context)getActiveSubscriptionInfoList().getActiveSubscriptionInfoList()
。
我想我可以使用 telephonyManager.getSubscriptionId(callDetails.getAccountHandle())
,但它需要 API 30,这是相当新的...
问题
给定我从这个回调(可能还有其他)收到的一个电话,我怎样才能得到它的关联 SIM 插槽和电话号码?
我更喜欢知道如何为尽可能广泛的 android 版本执行此操作,因为 InCallService 来自 API 23……在 API 30 之前应该可以,对吧?
【问题讨论】:
TelecomManager.getPhoneAccount()
有效吗?
@vlp 它实际上似乎运作良好!可悲的是,我无法在旧设备上进行测试(没有任何设备),并且模拟器不能拥有多 SIM 卡,但它看起来很有希望。我用它来获取电话号码:telecomManager.getPhoneAccount(call.details.accountHandle)?.address?.schemeSpecificPart 然后我从 number 字段中的 subscriptionManager.activeSubscriptionInfoList 列表中找到它。你为什么不把它写在答案里?你是怎么知道的?我试图搜索它几个小时......
说实话——我在官方文档中读到过它,但从未尝试过。我很高兴它对你有用。祝你的项目好运!
@vlp 请写下你从哪里得到的答案。
我从 Android 官方文档中获得了这些信息。
【参考方案1】:
使用call.getDetails().getAccountHandle()
获取PhoneAccountHandle。
然后使用TelecomManager.getPhoneAccount() 获取PhoneAccount 实例。
针对 API 级别 31+ 的应用程序需要权限 Manifest.permission.READ_PHONE_NUMBERS
。
免责声明:我不是 Android 专家,所以请验证我的想法。
编辑:因此 API 30 之前和 API 30 之前的解决方案可能是这样的:
@RequiresApi(Build.VERSION_CODES.M)
fun handleCall(context: Context, call: Call)
var foundAndSetSimDetails = false
val callDetailsAccountHandle = callDetails.accountHandle
val subscriptionManager = context
.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE) as SubscriptionManager
val telephonyManager =
context.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager
val telecomManager =
context.getSystemService(Context.TELECOM_SERVICE) as TelecomManager
val hasReadPhoneStatePermission =
ActivityCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) == android.content.pm.PackageManager.PERMISSION_GRANTED
val phoneAccount = telecomManager.getPhoneAccount(callDetailsAccountHandle)
//TODO when targeting API 31, we might need to check for a new permission here, of READ_PHONE_NUMBERS
//find SIM by phone account
if (!foundAndSetSimDetails && phoneAccount != null && hasReadPhoneStatePermission)
val callCapablePhoneAccounts = telecomManager.callCapablePhoneAccounts
run
callCapablePhoneAccounts?.forEachIndexed index, phoneAccountHandle ->
if (phoneAccountHandle != callDetailsAccountHandle)
return@forEachIndexed
if (!phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION))
return@run
//found the sim card index
simName = phoneAccount.label?.toString().orEmpty()
simIndex = index + 1
foundAndSetSimDetails = true
return@run
//find SIM by subscription ID
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && hasReadPhoneStatePermission)
try
val callSubscriptionId: Int =
telephonyManager.getSubscriptionId(callDetailsAccountHandle!!)
for (subscriptionInfo: SubscriptionInfo in subscriptionManager.activeSubscriptionInfoList)
val activeSubscriptionId: Int = subscriptionInfo.subscriptionId
if (activeSubscriptionId == callSubscriptionId)
setSimDetails(telephonyManager, subscriptionInfo)
foundAndSetSimDetails = true
break
catch (e: Throwable)
e.printStackTrace()
//find SIM by phone number
if (!foundAndSetSimDetails && hasReadPhoneStatePermission)
try
val simPhoneNumber: String? = phoneAccount?.address?.schemeSpecificPart
if (!simPhoneNumber.isNullOrBlank())
for (subscriptionInfo in subscriptionManager.activeSubscriptionInfoList)
if (simPhoneNumber == subscriptionInfo.number)
setSimDetails(telephonyManager, subscriptionInfo)
foundAndSetSimDetails = true
break
if (!foundAndSetSimDetails)
catch (e: Throwable)
e.printStackTrace()
private fun setSimDetails(telephonyManager: TelephonyManager, subscriptionInfo: SubscriptionInfo)
var foundSimName: String? = null
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
val telephonyManagerForSubscriptionId =
telephonyManager.createForSubscriptionId(subscriptionInfo.subscriptionId)
foundSimName = telephonyManagerForSubscriptionId.simOperatorName
if (foundSimName.isNullOrBlank())
foundSimName = subscriptionInfo.carrierName?.toString()
simName = if (foundSimName.isNullOrBlank())
""
else foundSimName
simIndex = subscriptionInfo.simSlotIndex + 1
【讨论】:
您看到哪个功能需要READ_PHONE_NUMBERS
权限?此外,即使我使用 telephonyManager.getSubscriptionId
的相对较新的 API(他们在 subscriptionManager.activeSubscriptionInfoList
项目列表中找到它,检查他们的 subscriptionId
),是否也需要?
文档指出TelecomManager.getPhoneAccount()
需要 API 级别 31+ 的 READ_PHONE_NUMBERS
,TelephonyManager.getSubscriptionId
需要 READ_PHONE_STATE
和 SubscriptionManager.getActiveSubscriptionInfoList()
需要 READ_PHONE_STATE
或运营商特权。另请注意,您计划使用的SubscriptionInfo.getNumber()
需要一些权限(API 级别 30 以下的READ_PHONE_STATE
或 API 级别 30 以上的 READ_PRIVILEGED_PHONE_STATE
/READ_PHONE_NUMBERS
/READ_SMS
之一)或运营商权限。
因此,对于telephonyManager.getSubscriptionId(callDetails.getAccountHandle())
,然后在SubscriptionManager.getActiveSubscriptionInfoList()
方法中查找,理论上您需要READ_PHONE_STATE
(TelephonyManager.getSubscriptionId
+SubscriptionManager.getActiveSubscriptionInfoList()
) 以及从SubsciptionInfo
读取信息的相应权限( READ_PRIVILEGED_PHONE_STATE
/READ_PHONE_NUMBERS
/READ_SMS
获取电话号码)。假设 API 级别 30+。请交叉验证我的想法。
我已编辑您的答案以包含两种情况的完整代码。至于权限,谢谢。这两种情况在针对 API 30 时都需要 READ_PHONE_STATE。我认为当针对 API 31 时,只有 getPhoneAccount
会出现问题,但是它仅用于 API 30 之前的情况,所以应该没问题。我现在尝试了,但它只警告我READ_PHONE_STATE
权限,所以不知道这段代码中是否还有更多可能的问题。在此报告:issuetracker.google.com/issues/205726880。如果您有任何其他您认为重要的事情,请告诉我。
谷歌回答了我如何做到这一点,但不确定他们写了什么:issuetracker.google.com/issues/204791554#comment5以上是关于onCallAdded的回调如何获取当前设备的电话号码和SIM卡槽?的主要内容,如果未能解决你的问题,请参考以下文章