Android Telecom フレームワーク(単に「Telecom」とも呼ばれます)は、Android 搭載デバイスでの音声通話とビデオ通話を管理します。これには、テレフォニー フレームワークを使用する通話や、Core-Telecom
Jetpack ライブラリを実装する VoIP 通話など、SIM ベースの通話が含まれます。
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-Telecom
Jetpack ライブラリと統合すると、アプリはデバイス上のシステム電話通話だけでなく、Telecom と統合された他のスタンドアロン通話アプリとも相互運用できるようになります。Core-Telecom
ライブラリは、音声のルーティングとフォーカスも管理します。詳しくは、通話アプリを作成するをご覧ください。マネージド ConnectionService API を実装する: このオプションを使用すると、既存のデバイスの電話アプリに依存して通話のユーザー インターフェースを提供する通話ソリューションの開発が容易になります。たとえば、SIP 通話サービスや VoIP 通話サービスのサードパーティ実装などです。詳しくは
getDefaultDialerPackage()
をご覧ください。ConnectionService
だけでは、通話を接続する手段のみが提供されます。関連するユーザー インターフェースはありません。InCallService API と 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
を正しくトリガーできるように、マニフェスト ファイルに適切なインテント フィルタと権限を指定して CallScreeningService
の実装を登録する必要があります。
<service
android:name=".ScreeningService"
android:permission="android.permission.BIND_SCREENING_SERVICE">
<intent-filter>
<action android:name="android.telecom.CallScreeningService" />
</intent-filter>
</service>
通話を転送する
Android 10 以降を搭載したデバイスは、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
によって作成されたインテントを起動するため、ユーザーの選択を処理するように 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); } } } }