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_NUMBERSTelephonyManager.getSubscriptionId 需要 READ_PHONE_STATESubscriptionManager.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卡槽?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用javascript从当前位置查找用户电话国家代码

如何在颤动中回调谷歌位置服务对话框按钮点击?

如何从设备获取联系电话?

一个通知推送窗口电话的多个回调

如何从 Android 设备中仅获取电话号码联系人

设备令牌电话间隙