Android Telecom 框架(简称“Telecom”)用于管理音频和
进行视频通话。这包括基于 SIM 卡的通话,例如
通话
使用电话框架的 VoIP 通话,以及实现
ConnectionService
API。
Telecom 管理的主要组件是 ConnectionService
和
InCallService
。
ConnectionService
实现使用 VoIP 等技术来
向其他方发出广告请求。最常见的 ConnectionService
实现,
phone 是 Telephony ConnectionService
。它可以连接运营商通话。
InCallService
实现为由
Telecom,让用户能够控制这些通话并与之互动。最
InCallService
的常见实现是与 Android 应用捆绑的电话应用
。
电信服务可以充当总机。它会转接ConnectionService
的来电
实现提供给调用界面的 InCallService
提供的各种工具
您可能出于以下原因需要实现 Telecom API:
创建电话应用的替代应用
如需在 Android 设备上为默认手机应用创建替换项,请执行以下操作:
实现 InCallService
API。您的实现必须符合以下要求
要求:
- 设备不得提供任何通话功能,且只能由用户组成 界面。
- 它必须处理 Telecom 框架感知到的所有呼叫,并且不进行
一些假设。例如,您不能假设
通话是指基于 SIM 卡的电话通话,也不实施通话限制
基于任何一个
ConnectionService
,例如电话强制执行 视频通话限制
如需了解详情,请参阅 InCallService
。
集成呼叫解决方案
如需将呼叫解决方案集成到 Android 中,您需要 选项:
实现自行管理的 ConnectionService API: 对于独立通话应用的开发者,如果不想要 在默认电话应用中显示他们的通话, 界面
使用自行管理的
ConnectionService
,您可以帮助您的应用 不仅可与设备上的原生电话通话实现互操作, 与其他实现此 API 的独立通话应用搭配使用。自我管理ConnectionService
API 还可管理音频路由和焦点。有关详情,请参阅 构建通话应用。实现代管式 ConnectionService API: 此选项有助于开发依赖于 现有设备电话应用来提供通话界面。 例如,SIP 通话和 VoIP 通话的第三方实现 服务。如需了解详情,请参阅
getDefaultDialerPackage()
。仅
ConnectionService
仅提供连接通话的方式。它 没有关联的界面实现 InCallService 和 ConnectionService API: 如果您想自行创建 基于
ConnectionService
的通话解决方案,具有自己的用户 界面,还会显示同一界面中的所有其他 Android 调用。 使用此方法时,InCallService
的实现不得 并据此推测其显示的来电来源此外,您的ConnectionService
的实现必须能够继续正常工作,而不使用 将默认手机应用设置为您的自定义InCallService
。
过滤来电
搭载 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 { @Override public void onScreenCall(@NonNull Call.Details callDetails) { boolean isIncoming = callDetails.getCallDirection() == Call.Details.DIRECTION_INCOMING; if (isIncoming) { Uri handle = callDetails.getHandle(); switch (callDetails.getCallerNumberVerificationStatus()) { case Connection.VERIFICATION_STATUS_FAILED: // Network verification failed, likely an invalid/spam call. break; case Connection.VERIFICATION_STATUS_PASSED: // Network verification passed, likely a valid call. break; default: // Network could not perform verification. // This branch matches Connection.VERIFICATION_STATUS_NOT_VERIFIED } } } }
设置要调用的 onScreenCall()
函数
respondToCall()
以告知系统如何响应新的调用。此函数采用
CallResponse
参数,可用于告知系统阻止调用,
或让其静音您也可以指示系统跳过
所有来电。
Kotlin
// Tell the system how to respond to the incoming call // and if it should notify the user of the call. val response = CallResponse.Builder() // Sets whether the incoming call should be blocked. .setDisallowCall(false) // Sets whether the incoming call should be rejected as if the user did so manually. .setRejectCall(false) // Sets whether ringing should be silenced for the incoming call. .setSilenceCall(false) // Sets whether the incoming call should not be displayed in the call log. .setSkipCallLog(false) // Sets whether a missed call notification should not be shown for the incoming call. .setSkipNotification(false) .build() // Call this function to provide your screening response. respondToCall(callDetails, response)
Java
// Tell the system how to respond to the incoming call // and if it should notify the user of the call. CallResponse.Builder response = new CallResponse.Builder(); // Sets whether the incoming call should be blocked. response.setDisallowCall(false); // Sets whether the incoming call should be rejected as if the user did so manually. response.setRejectCall(false); // Sets whether ringing should be silenced for the incoming call. response.setSilenceCall(false); // Sets whether the incoming call should not be displayed in the call log. response.setSkipCallLog(false); // Sets whether a missed call notification should not be shown for the incoming call. response.setSkipNotification(false); // Call this function to provide your screening response. respondToCall(callDetails, response.build());
您必须在清单中注册 CallScreeningService
实现
文件与相应的 intent 过滤器和权限相关联,以便系统可以
正确。
<service
android:name=".ScreeningService"
android:permission="android.permission.BIND_SCREENING_SERVICE">
<intent-filter>
<action android:name="android.telecom.CallScreeningService" />
</intent-filter>
</service>
重定向呼叫
搭载 Android 10 或更高版本的设备管理通话 intent 的方式与管理通话 intent 不同
搭载 Android 9 或更低版本的设备。在 Android 10 及更高版本中,
ACTION_NEW_OUTGOING_CALL
广播功能已被弃用,取而代之的是
CallRedirectionService
API。CallRedirectionService
提供了一些接口,供您使用
修改 Android 平台拨出的电话。例如,第三方
应用可能会取消呼叫并通过 VoIP 重新路由呼叫。
Kotlin
class RedirectionService : CallRedirectionService() { override fun onPlaceCall( handle: Uri, initialPhoneAccount: PhoneAccountHandle, allowInteractiveResponse: Boolean ) { // Determine if the call should proceed, be redirected, or cancelled. val callShouldProceed = true val callShouldRedirect = false when { callShouldProceed -> { placeCallUnmodified() } callShouldRedirect -> { // Update the URI to point to a different phone number or modify the // PhoneAccountHandle and redirect. redirectCall(handle, initialPhoneAccount, true) } else -> { cancelCall() } } } }
Java
class RedirectionService extends CallRedirectionService { @Override public void onPlaceCall( @NonNull Uri handle, @NonNull PhoneAccountHandle initialPhoneAccount, boolean allowInteractiveResponse ) { // Determine if the call should proceed, be redirected, or cancelled. // Your app should implement this logic to determine the redirection. boolean callShouldProceed = true; boolean callShouldRedirect = false; if (callShouldProceed) { placeCallUnmodified(); } else if (callShouldRedirect) { // Update the URI to point to a different phone number or modify the // PhoneAccountHandle and redirect. redirectCall(handle, initialPhoneAccount, true); } else { cancelCall(); } } }
您必须在清单中注册此服务,以便系统可以启动它 正确。
<service
android:name=".RedirectionService"
android:permission="android.permission.BIND_CALL_REDIRECTION_SERVICE">
<intent-filter>
<action android:name="android.telecom.CallRedirectionService"/>
</intent-filter>
</service>
如要使用转接服务,您的应用必须请求来电转接角色
(来自 RoleManager
)。这会询问
用户。如果您的应用
未授予此角色,则系统不会使用您的重定向服务。
您应在用户启动您的应用时检查应用是否具有此角色,
您可以根据需要进行请求您启动一个由 RoleManager
创建的 intent,
因此请务必替换
onActivityResult()
函数来处理用户的选择。
Kotlin
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // Tell the system that you want your app to handle call redirects. This // is done by using the RoleManager to register your app to handle redirects. if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) { val roleManager = getSystemService(Context.ROLE_SERVICE) as RoleManager // Check if the app needs to register call redirection role. val shouldRequestRole = roleManager.isRoleAvailable(RoleManager.ROLE_CALL_REDIRECTION) && !roleManager.isRoleHeld(RoleManager.ROLE_CALL_REDIRECTION) if (shouldRequestRole) { val intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_CALL_REDIRECTION) startActivityForResult(intent, REDIRECT_ROLE_REQUEST_CODE) } } } companion object { private const val REDIRECT_ROLE_REQUEST_CODE = 1 } }
Java
class MainActivity extends AppCompatActivity { private static final int REDIRECT_ROLE_REQUEST_CODE = 0; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Tell the system that you want your app to handle call redirects. This // is done by using the RoleManager to register your app to handle redirects. if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) { RoleManager roleManager = (RoleManager) getSystemService(Context.ROLE_SERVICE); // Check if the app needs to register call redirection role. boolean shouldRequestRole = roleManager.isRoleAvailable(RoleManager.ROLE_CALL_REDIRECTION) && !roleManager.isRoleHeld(RoleManager.ROLE_CALL_REDIRECTION); if (shouldRequestRole) { Intent intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_CALL_REDIRECTION); startActivityForResult(intent, REDIRECT_ROLE_REQUEST_CODE); } } } }