Panoramica del framework per le telecomunicazioni

Il framework Android Telecom (noto anche semplicemente come "Telecom") gestisce le chiamate audio e video su un dispositivo Android. Sono incluse le chiamate basate su SIM, ad esempio quelle che utilizzano il framework di telefonia, e le chiamate VoIP che implementano la libreria Core-Telecom Jetpack.

I componenti principali gestiti da Telecom sono ConnectionService e InCallService.

Un'implementazione di ConnectionService si basa su tecnologie come la rete PSTN per collegare le chiamate ad altre parti. L'implementazione ConnectionService più comune su un telefono è la ConnectionService di telefonia. Consente di effettuare chiamate operatore.

Un'implementazione di InCallService fornisce un'interfaccia utente per le chiamate gestite da Telecom e consente all'utente di controllarle e interagire con esse. L'implementazione più comune di un InCallService è l'app Telefono fornita in dotazione con un dispositivo.

Telecom funge da centralino. Inoltra le chiamate fornite dalle implementazioni ConnectionService alle interfacce utente di chiamata fornite dalle implementazioni InCallService.

Potresti voler implementare le API di telecomunicazioni per i seguenti motivi:

Creare un'app Telefono sostitutiva

Per creare un'app Telefono predefinita alternativa su un dispositivo Android, implementa l'API InCallService. L'implementazione deve soddisfare i seguenti requisiti:

  • Non deve avere alcuna funzionalità di chiamata e deve essere costituito esclusivamente dall'interfaccia utente per le chiamate.
  • Deve gestire tutte le chiamate di cui è a conoscenza il framework di telecomunicazioni e non deve fare supposizioni sulla natura delle chiamate. Ad esempio, non deve assumere che le chiamate siano chiamate di telefonia basate su SIM, né implementare limitazioni per le chiamate che si basano su un ConnectionService, ad esempio l'applicazione di limitazioni per le chiamate di telefonia per le videochiamate.

Per ulteriori informazioni, consulta InCallService.

Integrare una soluzione di chiamata

Per integrare la tua soluzione di chiamata in Android, hai a disposizione le seguenti opzioni:

  • Implementa la libreria Jetpack Core-Telecom autogestita: Questa opzione è ideale per gli sviluppatori di app di chiamata autonome che non vogliono mostrare le proprie chiamate nell'app Telefono predefinita né mostrare altre chiamate nella loro interfaccia utente.

    Quando esegui l'integrazione con la libreria Jetpack Core-Telecom, consenti alla tua app di interoperare non solo con le chiamate di telefonia di sistema sul dispositivo, ma anche con altre app di chiamata autonome integrate con Telecom. La biblioteca Core-Telecom gestisce anche il routing e lo stato attivo dell'audio. Per maggiori dettagli, consulta Creare un'app di chiamata.

  • Implementa l'API ConnectionService gestita: Questa opzione semplifica lo sviluppo di una soluzione di chiamata che si basa sull'applicazione Telefono del dispositivo esistente per fornire l'interfaccia utente per le chiamate. Alcuni esempi sono l'implementazione di terze parti di servizi di chiamate SIP e VoIP. Per maggiori dettagli, consulta getDefaultDialerPackage().

    Un ConnectionService da solo fornisce solo il mezzo per collegare le chiamate. Non ha un'interfaccia utente associata.

  • Implementa sia l'API InCallService sia l'API ConnectionService: Questa opzione è ideale se vuoi creare la tua soluzione di chiamate basata su ConnectionService, completa di un'interfaccia utente, e mostrare anche tutte le altre chiamate Android nella stessa interfaccia utente. Quando utilizzi questo approccio, l'implementazione di InCallService non deve fare alcuna supposizione sulle origini delle chiamate visualizzate. Inoltre, l'implementazione di ConnectionService deve continuare a funzionare senza che l'app Telefono predefinita sia impostata sul tuo InCallService personalizzato.

Filtro delle chiamate

I dispositivi con Android 10 (livello API 29) o versioni successive consentono alla tua app di identificare come potenziali chiamate spam le chiamate provenienti da numeri non presenti nella rubrica dell'utente. Gli utenti possono scegliere di rifiutare in silenzio le chiamate indesiderate. Per offrire maggiore trasparenza agli utenti quando perdono una chiamata, le informazioni su queste chiamate bloccate vengono registrate nel registro chiamate. L'utilizzo dell'API Android 10 elimina il obbligo di ottenere l'autorizzazione READ_CALL_LOG dall'utente per fornire la funzionalità di identificazione delle chiamate e di ID chiamante.

Utilizzi un'implementazione CallScreeningService per filtrare le chiamate. Chiama la funzione onScreenCall() per tutte le nuove chiamate in entrata o in uscita quando il numero non è nell'elenco di contatti dell'utente. Puoi controllare l'oggetto Call.Details per informazioni sulla chiamata. Nello specifico, la funzione getCallerNumberVerificationStatus() include le informazioni del fornitore di rete sull'altro numero. Se lo stato della verifica non è andato a buon fine, è probabile che la chiamata provenga da un numero non valido o da una potenziale chiamata 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
            }
        }
    }
}

Imposta la funzione onScreenCall() per chiamare respondToCall() per indicare al sistema come rispondere alla nuova chiamata. Questa funzione accetta un CallResponse parametro che puoi utilizzare per indicare al sistema di bloccare la chiamata, rifiutarla come se fosse stato l'utente a farlo o silenziarla. Puoi anche chiedere al sistema di saltare del tutto l'aggiunta di questa chiamata al registro chiamate 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());

Devi registrare l'implementazione di CallScreeningService nel file manifest con l'autorizzazione e il filtro per intent appropriati affinché il sistema possa attivarla correttamente.

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

Reindirizzare una chiamata

I dispositivi con Android 10 o versioni successive gestiscono le intenzioni di chiamata in modo diverso rispetto ai dispositivi con Android 9 o versioni precedenti. Su Android 10 e versioni successive, la trasmissione ACTION_NEW_OUTGOING_CALL viene ritirata e sostituita dall'API CallRedirectionService. CallRedirectionService fornisce interfacce che puoi utilizzare per modificare le chiamate in uscita effettuate dalla piattaforma Android. Ad esempio, le app di terze parti potrebbero annullare le chiamate e reindirizzarle tramite 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();
        }
    }
}

Devi registrare questo servizio nel file manifest in modo che il sistema possa avviarlo correttamente.

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

Per utilizzare un servizio di reindirizzamento, la tua app deve richiedere il ruolo di reindirizzamento chiamate da RoleManager. Verrà chiesto all'utente se vuole consentire alla tua app di gestire i reindirizzamenti delle chiamate. Se all'app non viene concesso questo ruolo, il servizio di reindirizzamento non viene utilizzato.

Devi verificare se la tua app ha questo ruolo quando l'utente la avvia, in modo da poterlo richiedere in base alle esigenze. Avvia un'intenzione creata da RoleManager, quindi assicurati di eseguire l'override della funzione onActivityResult() per gestire la selezione dell'utente.

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