電信架構總覽

Android Telecom 架構 (也稱為「Telecom」) 可管理音訊和 以及透過 Android 裝置進行視訊通話包括使用 SIM 卡的通話,例如 通話 ,以及採用 ConnectionService API。

Telecom 管理的主要元件為 ConnectionService InCallService

ConnectionService 實作項目會使用 VoIP 等技術進行連線 呼叫其他方。最常見的 ConnectionService 實作 電話是 ConnectionService。用於連結電信業者電話。

InCallService 實作提供使用者介面,以便呼叫由 電信,可讓使用者控制通話及進行互動。最常出現 InCallService 是預先安裝的手機應用程式 裝置。

電信可以做為開關機,並轉送 ConnectionService 的呼叫 實作項目為 InCallService 呼叫的使用者介面 實作方式

您可能會基於下列原因實作 Telecom API:

建立替代手機應用程式

如要在 Android 裝置上取代預設的手機應用程式,請按照下列步驟操作: 實作 InCallService API。實作項目必須符合下列條件: 規定:

  • 應用程式不得具有任何通話功能,且只能由使用者組成 呼叫介面。
  • 它必須處理電信架構知道的所有呼叫,且不得發出 所做出的假設。舉例來說,不得假設 通話是以 SIM 卡式的電話服務進行,且會導入通話限制, 依據任何 ConnectionService (例如強制執行電話服務) 為依據 視訊通話限制。

詳情請參閱 InCallService

整合通話解決方案

如要將呼叫解決方案整合到 Android 中,您必須遵守 以下選項:

  • 實作自行管理的 ConnectionService API: 如果開發人員不想使用獨立的通話應用程式,就很適合使用這個選項 顯示其通話在預設電話應用程式中,而不會顯示其他通話 使用者介面

    使用自行管理的 ConnectionService 時,您可以協助應用程式: 與裝置上原生電話通訊的互通性 與其他獨立呼叫應用程式 (實作此 API) 搭配使用。自行管理 ConnectionService API 也會管理音訊轉送和焦點。詳情請參閱 建構通話應用程式

  • 實作 Managed ConnectionService API: 這個選項有助於開發依賴於 可為通話功能提供使用者介面。 例如:第三方實作 SIP 通話和 VoIP 通話功能 免費 Google Cloud 服務詳情請參閱 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 實作項目 檔案含有適當意圖篩選器和權限,以利系統觸發 但事實並非如此

<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 也能使用 Google Cloud CLI 或 Compute Engine APICallRedirectionService 提供的介面可用於 修改 Android 平台撥出的電話。例如,第三方 應用程式可能會取消通話,並透過 IP 網路語音傳遞技術重新轉送。

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);
            }
        }
    }
}