Tổng quan về khung viễn thông

Khung Android Telecom (còn gọi là "Viễn thông") quản lý âm thanh và các cuộc gọi video trên thiết bị chạy Android. Bao gồm cả các cuộc gọi trên SIM, chẳng hạn như các cuộc gọi sử dụng khung điện thoại và các lệnh gọi VoIP triển khai ConnectionService.

Các thành phần chính mà Telecom quản lý là ConnectionServiceInCallService.

Quá trình triển khai ConnectionService sử dụng các công nghệ như VoIP để kết nối các cuộc gọi đến các bên khác. Cách triển khai ConnectionService phổ biến nhất trên số điện thoại là ConnectionService. Ứng dụng này kết nối các cuộc gọi của nhà mạng.

Phương thức triển khai InCallService cung cấp giao diện người dùng cho các cuộc gọi được quản lý bằng Viễn thông và cho phép người dùng kiểm soát cũng như tương tác với các cuộc gọi này. Nhiều nhất cách triển khai phổ biến của InCallService là ứng dụng điện thoại đi kèm với một thiết bị.

Viễn thông đóng vai trò là một tổng đài. Phương thức này định tuyến các cuộc gọi ConnectionService các phương thức triển khai cung cấp cho giao diện người dùng gọi mà InCallService mà các nhà triển khai cung cấp.

Bạn có thể muốn triển khai Telecom API (API Viễn thông) vì những lý do sau:

Tạo ứng dụng điện thoại thay thế

Cách tạo ứng dụng thay thế cho ứng dụng điện thoại mặc định trên thiết bị Android: triển khai API InCallService. Việc triển khai của bạn phải đáp ứng các yêu cầu sau: các yêu cầu:

  • Máy chủ không được có tính năng gọi và chỉ được bao gồm một người dùng để gọi.
  • Nó phải xử lý tất cả các cuộc gọi mà khung Viễn thông nhận biết được và không thực hiện giả định về bản chất của lệnh gọi. Ví dụ: mô-đun này không được giả định là các cuộc gọi điện thoại dựa trên SIM, cũng như không triển khai các hạn chế gọi điện đều dựa trên một ConnectionService bất kỳ, chẳng hạn như việc thực thi chế độ cài đặt điện thoại cho cuộc gọi video.

Để biết thêm thông tin, hãy xem InCallService.

Tích hợp giải pháp gọi điện

Để tích hợp giải pháp gọi điện vào Android, bạn có các tuỳ chọn sau:

  • Triển khai API ConnectionService tự quản lý: Lựa chọn này phù hợp với những nhà phát triển có các ứng dụng gọi độc lập không muốn để hiển thị cuộc gọi của họ trong ứng dụng điện thoại mặc định, cũng như không hiển thị các cuộc gọi khác trong giao diện người dùng.

    Khi dùng ConnectionService tự quản lý, bạn sẽ giúp ứng dụng của mình không chỉ tương tác với tính năng gọi điện thoại gốc trên thiết bị, mà còn với các ứng dụng gọi độc lập khác triển khai API này. Chế độ tự quản lý API ConnectionService cũng quản lý chế độ tập trung và định tuyến âm thanh. Để biết thông tin chi tiết, hãy xem Tạo ứng dụng gọi điện.

  • Triển khai API ConnectionService được quản lý: Lựa chọn này hỗ trợ phát triển một giải pháp gọi điện dựa vào ứng dụng điện thoại hiện có trên thiết bị để cung cấp giao diện người dùng cho các cuộc gọi. Ví dụ: việc triển khai tính năng gọi điện SIP và gọi VoIP của bên thứ ba luôn miễn phí. Để biết thêm thông tin, hãy xem getDefaultDialerPackage().

    Riêng ConnectionService chỉ cung cấp phương tiện để kết nối các cuộc gọi. Nó không có giao diện người dùng được liên kết.

  • Triển khai cả InCallService và API ConnectionService: Đây là lựa chọn lý tưởng nếu bạn muốn tạo Giải pháp gọi dựa trên ConnectionService, hoàn thiện với người dùng riêng giao diện người dùng và cũng hiển thị tất cả các lệnh gọi khác trên Android trong cùng một giao diện người dùng. Khi sử dụng phương pháp này, bạn không được triển khai InCallService đưa ra bất kỳ giả định nào về nguồn của lệnh gọi mà nó hiển thị. Ngoài ra, Việc triển khai ConnectionService phải tiếp tục hoạt động mà không có ứng dụng điện thoại mặc định của bạn thành InCallService tuỳ chỉnh.

Hiện màn hình cuộc gọi

Thiết bị chạy Android 10 (API cấp 29) trở lên cho phép ứng dụng của bạn xác định cuộc gọi từ các số không có trong sổ địa chỉ của người dùng có khả năng là làm phiền cuộc gọi. Người dùng có thể chọn từ chối tự động các cuộc gọi làm phiền. Để cung cấp nhiều hơn thông tin minh bạch cho người dùng khi họ bỏ lỡ cuộc gọi, thông tin về các cuộc gọi bị chặn các cuộc gọi được ghi lại trong nhật ký cuộc gọi. Việc sử dụng API Android 10 sẽ loại bỏ để có được READ_CALL_LOG quyền từ người dùng để cung cấp tính năng sàng lọc cuộc gọi và tên nhận dạng người gọi của Google.

Bạn sử dụng CallScreeningService để sàng lọc cuộc gọi. Gọi onScreenCall() cho mọi cuộc gọi đến hoặc cuộc gọi đi mới khi số này không nằm trong danh bạ của người dùng. Bạn có thể xem Đối tượng Call.Details cho thông tin về cuộc gọi này. Cụ thể, getCallerNumberVerificationStatus() hàm này bao gồm thông tin từ nhà cung cấp mạng về số khác. Nếu trạng thái xác minh không thành công, đây là một dấu hiệu tốt cho thấy cuộc gọi từ một số điện thoại không hợp lệ hoặc từ một cuộc gọi có khả năng làm phiền.

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

Đặt hàm onScreenCall() để gọi respondToCall() để cho hệ thống biết cách phản hồi cuộc gọi mới. Hàm này nhận một CallResponse mà bạn có thể sử dụng để yêu cầu hệ thống chặn cuộc gọi, từ chối cuộc gọi như thể người dùng đã thực hiện hoặc tắt tiếng. Bạn cũng có thể yêu cầu hệ thống bỏ qua việc thêm đoạn mã này cuộc gọi đến nhật ký cuộc gọi của thiết bị.

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

Bạn phải đăng ký phương thức triển khai CallScreeningService trong tệp kê khai có bộ lọc ý định và quyền thích hợp để hệ thống có thể kích hoạt một cách chính xác.

<service
    android:name=".ScreeningService"
    android:permission="android.permission.BIND_SCREENING_SERVICE">
    <intent-filter>
        <action android:name="android.telecom.CallScreeningService" />
    </intent-filter>
</service>

Chuyển hướng cuộc gọi

Các thiết bị chạy Android 10 trở lên quản lý ý định cuộc gọi theo cách khác với thiết bị chạy Android 9 trở xuống. Trên Android 10 trở lên, ACTION_NEW_OUTGOING_CALL Thông báo truyền tin không được dùng nữa và được thay thế bằng CallRedirectionService API. CallRedirectionService cung cấp các giao diện để bạn sử dụng sửa đổi các cuộc gọi đi do nền tảng Android thực hiện. Ví dụ: bên thứ ba có thể huỷ cuộc gọi và định tuyến lại qua 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();
        }
    }
}

Bạn phải đăng ký dịch vụ này trong tệp kê khai để hệ thống có thể khởi động dịch vụ đó chính xác.

<service
    android:name=".RedirectionService"
    android:permission="android.permission.BIND_CALL_REDIRECTION_SERVICE">
    <intent-filter>
        <action android:name="android.telecom.CallRedirectionService"/>
    </intent-filter>
</service>

Để sử dụng dịch vụ chuyển hướng, ứng dụng của bạn phải yêu cầu vai trò chuyển hướng cuộc gọi khỏi RoleManager. Quy trình này sẽ yêu cầu của người dùng nếu họ muốn cho phép ứng dụng của bạn xử lý lệnh chuyển hướng cuộc gọi. Nếu ứng dụng của bạn người dùng chưa được cấp vai trò này, thì dịch vụ chuyển hướng của bạn sẽ không được sử dụng.

Khi người dùng chạy ứng dụng, bạn nên kiểm tra xem ứng dụng của mình có vai trò này hay không. bạn có thể yêu cầu mã đó nếu cần. Bạn khởi chạy một ý định do RoleManager tạo, do đó, hãy đảm bảo bạn ghi đè onActivityResult() để xử lý lựa chọn của người dùng.

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