Descripción general del marco de trabajo de Telecom

El framework de Android Telecom (también conocido como "Telecom") administra llamadas de audio y video en un dispositivo Android. Esto incluye las llamadas basadas en SIM, como las que usan el framework de telefonía, y las llamadas VoIP que implementan la API de ConnectionService.

Los componentes principales que administra Telecom son ConnectionService y InCallService.

Una implementación de ConnectionService usa tecnologías como VoIP para conectar llamadas a otras partes. La implementación de ConnectionService más común en un teléfono es la ConnectionService de telefonía. Conecta llamadas mediante operador.

Una implementación de InCallService proporciona una interfaz de usuario para las llamadas administradas por Telecom y le permite al usuario interactuar con ellas y controlarlas. La implementación más común de un InCallService es la aplicación para teléfonos que se incluye con un dispositivo.

Las telecomunicaciones funcionan como un conmutador. Enruta las llamadas que proporcionan las implementaciones de ConnectionService a las interfaces de usuario que realizan llamadas y proporcionan las implementaciones de InCallService.

Recomendamos implementar las APIs de Telecom por los siguientes motivos:

Crear una app de teléfono de reemplazo

Para crear un reemplazo de la aplicación para teléfonos predeterminada en un dispositivo Android, implementa la API de InCallService. Tu implementación debe cumplir con los siguientes requisitos:

  • No debe tener ninguna capacidad de realizar llamadas y debe consistir solo en la interfaz de usuario para realizar llamadas.
  • Debe manejar todas las llamadas que el framework de Telecom reconozca y no debe hacer suposiciones sobre la naturaleza de las llamadas. Por ejemplo, no debe suponer que las llamadas son telefónicas basadas en SIM ni implementar restricciones de llamada basadas en un ConnectionService, como la aplicación de restricciones de telefonía para las videollamadas.

Para obtener más información, consulta InCallService.

Integrar una solución de llamadas

Para integrar tu solución de llamadas en Android, tienes las siguientes opciones:

  • Implementa la API de ConnectionService autoadministrada: Esta opción es ideal para los desarrolladores de apps de llamadas independientes que no desean mostrar sus llamadas dentro de la app de teléfono predeterminada, ni que se muestren otras llamadas en su interfaz de usuario.

    Cuando usas un ConnectionService autoadministrado, ayudas a tu app a interoperar no solo con las llamadas telefónicas nativas del dispositivo, sino también con otras apps de llamadas independientes que implementan esta API. La API de ConnectionService autoadministrada también controla el enrutamiento y el foco de audio. Para obtener más información, consulta Cómo compilar una app de llamadas.

  • Implementar la API de ConnectionService administrada: Esta opción facilita el desarrollo de una solución de llamadas que se basa en la aplicación de teléfono del dispositivo existente para proporcionar la interfaz de usuario para las llamadas. Entre los ejemplos, se incluye una implementación de terceros de servicios de llamadas SIP y VoIP. Consulta getDefaultDialerPackage() para obtener más información.

    Un objeto ConnectionService por sí solo proporciona los medios para conectar llamadas. No tiene una interfaz de usuario asociada.

  • Implementa la API de InCallService y ConnectionService: esta opción es ideal si deseas crear tu propia solución de llamadas basada en ConnectionService, completar con su propia interfaz de usuario y también mostrar todas las demás llamadas a Android en la misma interfaz de usuario. Cuando usas este enfoque, la implementación de InCallService no debe hacer suposiciones sobre las fuentes de las llamadas que muestra. Además, tu implementación de ConnectionService debe seguir funcionando sin la aplicación para teléfonos predeterminada configurada como tu InCallService personalizado.

Filtrar llamadas

Los dispositivos que ejecutan Android 10 (nivel de API 29) o versiones posteriores permiten que tu app identifique llamadas de números que no están en la libreta de direcciones del usuario como posibles llamadas de spam. Los usuarios pueden elegir que se rechacen las llamadas de spam de manera silenciosa. Para brindar una mayor transparencia a los usuarios cuando pierden una llamada, la información sobre las llamadas bloqueadas se registra en el registro de llamadas. El uso de la API de Android 10 elimina la necesidad de obtener el permiso READ_CALL_LOG del usuario para proporcionar la funcionalidad de identificador de llamadas y número de llamada.

Usas una implementación de CallScreeningService para filtrar llamadas. Llama a la función onScreenCall() para las llamadas entrantes o salientes nuevas cuando el número no esté en la lista de contactos del usuario. Puedes verificar el objeto Call.Details para obtener información sobre la llamada. Específicamente, la función getCallerNumberVerificationStatus() incluye información del proveedor de red sobre el otro número. Si el estado de verificación falló, esta es una buena indicación de que la llamada proviene de un número no válido o de una posible llamada de spam.

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

Configura la función onScreenCall() para que llame a respondToCall() y le indique al sistema cómo responder la llamada nueva. Esta función toma un parámetro CallResponse que puedes usar para indicarle al sistema que bloquee la llamada, la rechace como si el usuario lo hiciera o la silencie. También puedes indicarle al sistema que omita por completo agregar esta llamada al registro de llamadas del dispositivo.

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

Debes registrar la implementación de CallScreeningService en el archivo de manifiesto con el filtro de intents y el permiso apropiados para que el sistema pueda activarla correctamente.

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

Redireccionar una llamada

Los dispositivos que ejecutan Android 10 o versiones posteriores administran los intents de llamada de manera diferente a los dispositivos que ejecutan Android 9 o versiones anteriores. En Android 10 y versiones posteriores, la transmisión ACTION_NEW_OUTGOING_CALL dejó de estar disponible y se reemplazó por la API de CallRedirectionService. CallRedirectionService proporciona interfaces que puedes usar para modificar las llamadas salientes que realiza la plataforma de Android. Por ejemplo, las apps de terceros pueden cancelar llamadas y redirigirlas por 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();
        }
    }
}

Debes registrar este servicio en el manifiesto para que el sistema pueda iniciarlo correctamente.

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

Para usar un servicio de redireccionamiento, tu app debe solicitar la función de redireccionamiento de llamadas desde RoleManager. Con esta acción, se le preguntará al usuario si desea permitir que tu app controle los redireccionamientos de llamadas. Si tu app no recibe esta función, no se usa tu servicio de redireccionamiento.

Debes verificar si tu app tiene esta función cuando el usuario la inicia para poder solicitarla según sea necesario. Inicias un intent creado por RoleManager, así que asegúrate de anular la función onActivityResult() para controlar la selección del usuario.

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