Android Telecom 框架(也简称为“Telecom”)用于管理 Android 设备上的音频和视频通话。这包括基于 SIM 卡的通话,例如使用电话框架的通话,以及实现 Core-Telecom Jetpack 库的 VoIP 通话。
Telecom 管理的主要组件是 ConnectionService 和 InCallService。
ConnectionService 实现基于 PSTN 等技术来将通话连接到其他方。手机上最常见的 ConnectionService 实现是电话 ConnectionService。它用于连接运营商通话。
InCallService 实现会为 Telecom 管理的通话提供界面,并允许用户控制这些通话并与之互动。InCallService 最常见的实现是与设备捆绑的电话应用。
电信运营商充当电话交换台。它会将 ConnectionService 实现提供的调用路由到 InCallService 实现提供的调用界面。
您可能出于以下原因而需要实现 Telecom API:
- 为系统电话应用创建替代应用。
- 将通话解决方案集成到 Android 通话体验中。
创建电话应用的替代应用
如需在 Android 设备上替换默认电话应用,请实现 InCallService API。您的实现必须满足以下要求:
- 它不得具有任何通话功能,并且必须仅包含用于通话的界面。
- 它必须处理 Telecom 框架知晓的所有通话,并且不得对通话性质做出假设。例如,它不得假定通话是基于 SIM 卡的电话通话,也不得实现基于任何一个
ConnectionService的通话限制,例如针对视频通话强制执行电话限制。
如需了解详情,请参阅 InCallService。
集成呼叫解决方案
如需将通话解决方案集成到 Android 中,您可以使用以下选项:
实现自管理的 Core-Telecom Jetpack 库:此选项非常适合不想在默认的电话应用中显示其通话,也不想在其界面中显示其他通话的独立通话应用开发者。
集成
Core-TelecomJetpack 库后,您的应用不仅可以与设备上的系统电话通话功能互操作,还可以与集成了 Telecom 的其他独立通话应用互操作。Core-Telecom库还会管理音频路由和焦点。如需了解详情,请参阅构建通话应用。实现托管的 ConnectionService API:此选项有助于开发依赖于现有设备电话应用来提供通话界面的通话解决方案。例如,第三方实现的 SIP 通话和 VoIP 通话服务。如需了解详情,请参阅
getDefaultDialerPackage()。仅
ConnectionService本身只能提供连接通话的方法。它没有关联的界面。同时实现 InCallService 和 ConnectionService API:如果您想创建自己的基于
ConnectionService的通话解决方案(包括自己的界面),并在同一界面中显示所有其他 Android 通话,此选项非常适合。使用此方法时,InCallService的实现不得对其显示的调用的来源做出任何假设。此外,即使默认电话应用未设置为自定义InCallService,您的ConnectionService实现也必须继续正常运行。
过滤来电
在搭载 Android 10(API 级别 29)或更高版本的设备上,您的应用可以将用户通讯录中不存在号码的来电标识为潜在骚扰电话。用户可以选择让系统静默拒接垃圾来电。为了让用户更清楚地了解何时有过未接来电,系统会在通话记录中记录这些已屏蔽的来电的相关信息。使用 Android 10 API 后,您无需从用户那里获取 READ_CALL_LOG 权限,即可提供来电过滤和来电显示功能。
您可以使用 CallScreeningService 实现来过滤来电。当来电或出行电话的号码不在用户的通讯录中时,调用 onScreenCall() 函数。您可以查看 Call.Details 对象,了解有关调用的详细信息。具体而言,getCallerNumberVerificationStatus() 函数包含来自网络提供商的其他号码信息。如果验证状态为“失败”,则很可能表明来电号码无效或来电可能属于骚扰电话。
Kotlin
class ScreeningService : CallScreeningService() { // This function is called when an ingoing or outgoing call // is from a number not in the user's contacts list override fun onScreenCall(callDetails: Call.Details) { // Can check the direction of the call val isIncoming = callDetails.callDirection == Call.Details.DIRECTION_INCOMING if (isIncoming) { // the handle (e.g. phone number) that the Call is currently connected to val handle: Uri = callDetails.handle // determine if you want to allow or reject the call when (callDetails.callerNumberVerificationStatus) { Connection.VERIFICATION_STATUS_FAILED -> { // Network verification failed, likely an invalid/spam call. } Connection.VERIFICATION_STATUS_PASSED -> { // Network verification passed, likely a valid call. } else -> { // Network could not perform verification. // This branch matches Connection.VERIFICATION_STATUS_NOT_VERIFIED. } } } } }
Java
class ScreeningService extends CallScreeningService {