Présentation du framework de télécommunications

Le framework Android Telecom (également appelé simplement "Telecom") gère les appels audio et vidéo sur un appareil Android. Cela inclut les appels basés sur une carte SIM, tels que les appels qui utilisent le framework de téléphonie et les appels VoIP qui implémentent l'API ConnectionService.

Les principaux composants gérés par Telecom sont ConnectionService et InCallService.

Une implémentation ConnectionService utilise des technologies telles que la VoIP pour connecter les appels à d'autres parties. L'implémentation ConnectionService la plus courante sur un téléphone est l'ConnectionService de téléphonie. Elle connecte les appels via l'opérateur.

Une implémentation InCallService fournit une interface utilisateur aux appels gérés par Telecom et permet à l'utilisateur de les contrôler et d'interagir avec ces appels. L'implémentation la plus courante d'un InCallService est l'application pour téléphone fournie avec un appareil.

Les télécommunications font office de standard. Elle achemine les appels fournis par les implémentations ConnectionService vers les interfaces utilisateur appelantes fournies par les implémentations InCallService.

Vous pouvez implémenter les API Telecom pour les raisons suivantes:

Créer une application pour téléphone de remplacement

Pour créer un remplacement de l'application pour téléphone par défaut sur un appareil Android, implémentez l'API InCallService. Votre mise en œuvre doit répondre aux exigences suivantes:

  • Elle ne doit pas comporter de capacité d'appel et doit se composer uniquement de l'interface utilisateur permettant d'effectuer des appels.
  • Il doit gérer tous les appels dont le framework de télécommunications a connaissance et ne pas émettre d'hypothèses sur la nature de ces appels. Par exemple, il ne doit pas supposer que les appels sont des appels de téléphonie basés sur une carte SIM ni implémenter des restrictions d'appel basées sur un seul ConnectionService, telles que l'application des restrictions de téléphonie pour les appels vidéo.

Pour en savoir plus, consultez InCallService.

Intégrer une solution d'appel

Pour intégrer votre solution d'appel à Android, vous disposez des options suivantes:

  • Implémenter l'API ConnectionService autogérée:cette option est idéale pour les développeurs d'applications d'appel autonomes qui ne souhaitent pas afficher leurs appels dans l'application de téléphone par défaut ni afficher d'autres appels dans leur interface utilisateur.

    Lorsque vous utilisez un ConnectionService autogéré, vous aidez votre application à interagir non seulement avec les appels de téléphonie natifs sur l'appareil, mais également avec d'autres applications d'appel autonomes qui implémentent cette API. L'API ConnectionService autogérée gère également le routage et la sélection audio. Pour en savoir plus, consultez Créer une application appelante.

  • Implémenter l'API ConnectionService gérée:cette option facilite le développement d'une solution d'appel qui repose sur l'application de téléphonie d'appareil existante pour fournir l'interface utilisateur pour les appels. Il peut s'agir, par exemple, d'une implémentation tierce de services d'appel SIP et de VoIP. Pour en savoir plus, consultez getDefaultDialerPackage().

    À lui seul, un ConnectionService ne permet que de connecter des appels. Elle n'est associée à aucune interface utilisateur.

  • Implémentez à la fois l'API InCallService et l'API ConnectionService:cette option est idéale si vous souhaitez créer votre propre solution d'appel basée sur ConnectionService, dotée de sa propre interface utilisateur, ou afficher tous les autres appels Android dans la même interface utilisateur. Lorsque vous utilisez cette approche, l'implémentation de InCallService ne doit faire aucune hypothèse concernant les sources des appels qu'il affiche. En outre, votre implémentation de ConnectionService doit continuer à fonctionner si l'application pour téléphone par défaut n'est pas définie sur votre InCallService personnalisé.

Identifier l'appelant

Les appareils équipés d'Android 10 (niveau d'API 29) ou version ultérieure permettent à votre application d'identifier les appels provenant de numéros ne figurant pas dans le carnet d'adresses de l'utilisateur comme des appels potentiellement indésirables. Les utilisateurs peuvent faire en sorte que les appels indésirables soient rejetés en mode silencieux. Pour assurer une plus grande transparence aux utilisateurs lorsqu'ils manquent des appels, les informations sur ces appels bloqués sont consignées dans le journal d'appels. Avec l'API Android 10, il n'est pas nécessaire d'obtenir l'autorisation READ_CALL_LOG de la part de l'utilisateur pour permettre le filtrage des appels et l'affichage du numéro de l'appelant.

Vous utilisez une implémentation CallScreeningService pour filtrer les appels. Appelez la fonction onScreenCall() pour tout nouvel appel entrant ou sortant lorsque le numéro ne figure pas dans la liste de contacts de l'utilisateur. Vous pouvez consulter l'objet Call.Details pour obtenir des informations sur l'appel. Plus précisément, la fonction getCallerNumberVerificationStatus() inclut des informations de la part du fournisseur d'accès sur l'autre numéro. Si l'état de la validation a échoué, cela signifie que l'appel provient d'un numéro non valide ou est potentiellement indésirable.

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

Définissez la fonction onScreenCall() pour qu'elle appelle respondToCall() afin d'indiquer au système comment répondre au nouvel appel. Cette fonction utilise un paramètre CallResponse que vous pouvez utiliser pour indiquer au système de bloquer l'appel, de le rejeter comme si l'utilisateur l'avait fait ou de le mettre sous silence. Vous pouvez également demander au système d'ignorer l'ajout de cet appel au journal d'appels de l'appareil.

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

Vous devez enregistrer l'implémentation de CallScreeningService dans le fichier manifeste avec le filtre d'intent et l'autorisation appropriés pour que le système puisse la déclencher correctement.

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

Rediriger un appel

Les appareils équipés d'Android 10 ou version ultérieure gèrent les intents d'appel différemment de ceux équipés d'Android 9 ou version antérieure. Sur Android 10 ou version ultérieure, la diffusion ACTION_NEW_OUTGOING_CALL est obsolète et remplacée par l'API CallRedirectionService. CallRedirectionService fournit des interfaces que vous pouvez utiliser pour modifier les appels sortants effectués par la plate-forme Android. Par exemple, des applications tierces peuvent annuler des appels et les réacheminer via 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();
        }
    }
}

Vous devez enregistrer ce service dans le fichier manifeste pour que le système puisse le démarrer correctement.

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

Pour utiliser un service de redirection, votre application doit demander le rôle de redirection des appels à partir de RoleManager. Celle-ci demandera à l'utilisateur s'il souhaite autoriser votre application à gérer les redirections d'appel. Si ce rôle ne lui est pas attribué, votre service de redirection n'est pas utilisé.

Vous devez vérifier si votre application dispose de ce rôle lorsque l'utilisateur la lance afin de pouvoir le demander si nécessaire. Vous lancez un intent créé par RoleManager. Assurez-vous donc de remplacer la fonction onActivityResult() pour gérer la sélection de l'utilisateur.

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