Descripción general del marco de trabajo de Telecom

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

Los componentes principales que administra Telecom son ConnectionService y InCallService.

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

Una implementación de InCallService proporciona una interfaz de usuario a las llamadas que administra Telecom y le permite al usuario controlarlas e interactuar con ellas. La implementación más común de un InCallService es la app de teléfono que se incluye con un dispositivo.

Las telecomunicaciones actúan como un conmutador. Enruta las llamadas que proporcionan las implementaciones de ConnectionService a las interfaces de usuario de llamada que proporcionan las implementaciones de InCallService.

Es posible que desees implementar las APIs de telecomunicaciones por los siguientes motivos:

Crear una app de teléfono de reemplazo

Para crear un reemplazo de la app de teléfono 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 llamada y debe consistir únicamente en la interfaz de usuario para realizar llamadas.
  • Debe controlar todas las llamadas que el framework de telecomunicaciones conozca y no debe hacer suposiciones sobre la naturaleza de las llamadas. Por ejemplo, no debe suponer que las llamadas son de telefonía basadas en SIM ni implementar restricciones de llamadas que se basen en un ConnectionService, como la aplicación forzosa de restricciones de telefonía para 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 biblioteca de Jetpack Core-Telecom autoadministrada: Esta opción es ideal para desarrolladores de apps de llamadas independientes que no quieren mostrar sus llamadas en la app de teléfono predeterminada ni mostrar otras llamadas en su interfaz de usuario.

    Cuando realizas la integración con la biblioteca de Jetpack Core-Telecom, ayudas a que tu app interopere no solo con las llamadas de telefonía del sistema en el dispositivo, sino también con otras apps de llamadas independientes que se integran con Telecom. La biblioteca Core-Telecom también administra el enrutamiento y el enfoque de audio. Para obtener más detalles, consulta Cómo compilar una app de llamadas.

  • Implementa 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 existente del dispositivo para proporcionar la interfaz de usuario para las llamadas. Algunos ejemplos son una implementación de terceros de servicios de llamadas de SIP y VoIP. Consulta getDefaultDialerPackage() para obtener más información.

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

  • Implementa las APIs de InCallService y ConnectionService: Esta opción es ideal si deseas crear tu propia solución de llamadas basada en ConnectionService, completa con su propia interfaz de usuario, y también mostrar todas las demás llamadas de Android en la misma interfaz de usuario. Cuando uses este enfoque, tu implementación de InCallService no debe hacer ninguna suposición sobre las fuentes de las llamadas que muestra. Además, tu implementación de ConnectionService debe seguir funcionando sin la app de teléfono predeterminada establecida en 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 rechazar en silencio las llamadas de spam. Para brindar mayor transparencia a los usuarios cuando pierden llamadas, la información sobre estas llamadas bloqueadas se registra en el registro de llamadas. El uso de la API de Android 10 elimina el requisito 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 cualquier llamada entrante o saliente nueva cuando el número no esté en la lista de contactos del usuario. Puedes consultar 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ó, es un buen indicador de que la llamada proviene de un número no válido o es 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
            }
        }
    }
}

Establece la función onScreenCall() para que llame a respondToCall() y le indique al sistema cómo responder a 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 lo hiciera el usuario o la silencie. También puedes indicarle al sistema que omita agregar esta llamada al registro de llamadas del dispositivo por completo.

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 adecuados 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 de 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 podrían cancelar llamadas y redirigirlas mediante 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 el rol de redireccionamiento de llamadas desde RoleManager. Se le preguntará al usuario si quiere permitir que tu app controle los redireccionamientos de llamadas. Si no se le otorga este rol a tu app, no se usará tu servicio de redireccionamiento.

Debes verificar si tu app tiene este rol cuando el usuario la inicia para poder solicitarlo 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);
            }
        }
    }
}