Présentation du protocole SIP

Détecter les eSIM et cartes SIM

Détection des cartes

Les appareils Android équipés de cartes SIM et d'eSIM utilisent les ID suivants dans les API de téléphonie, y compris ["TelephonyManager"](/reference/android/telephony/TelephonyManager) et ["SubscriptionManager"](/reference/android/telephony/SubscriptionManager) : * ID d'abonnement: identifiant unique pour un abonnement mobile. * Index ou ID d'emplacement logique: index unique faisant référence à un emplacement SIM logique. Les ID d'emplacements logiques commencent à 0 et augmentent en fonction du nombre d'emplacements actifs compatibles sur un appareil. Par exemple, un appareil avec double carte SIM dispose généralement des emplacements 0 et 1. Si un appareil possède plusieurs emplacements physiques, mais n'accepte qu'un seul emplacement actif, il ne portera que l'ID d'emplacement logique 0. * Index d'emplacement physique ou ID: index unique faisant référence à un emplacement pour carte SIM physique. Les ID d'emplacements physiques commencent à 0 et augmentent en fonction du nombre d'emplacements physiques sur l'appareil. Il diffère du nombre d'emplacements logiques d'un appareil, qui correspond au nombre d'emplacements actifs qu'un appareil peut utiliser. Par exemple, un appareil qui passe du mode double carte SIM au mode à une seule carte SIM peut toujours disposer de deux emplacements physiques. En revanche, en mode avec une seule carte SIM, il n'aura qu'un seul emplacement logique. * Identifiant de carte: identifiant unique permettant d'identifier une carte UiccCard. ![Schéma d'utilisation des ID dans un cas comportant deux emplacements logiques et trois emplacements physiques](/images/guide/topics/connectivity/tel-ids.png) Dans le schéma ci-dessus : * L'appareil possède deux emplacements logiques. * L'emplacement physique 0 contient une carte UICC physique avec un profil actif. * L'emplacement physique 2 contient un eUICC avec un profil actif. * L'emplacement physique 1 n'est pas utilisé actuellement. ![Schéma d'utilisation des ID dans un cas comportant trois emplacements logiques et deux emplacements physiques](/images/guide/topics/connectivity/tel-ids-2.png) Dans le schéma ci-dessus : * L'appareil possède trois emplacements logiques. * L'emplacement physique 0 contient une carte UICC physique avec un profil actif. * L'emplacement physique 1 se trouve dans un eUICC comportant deux profils téléchargés, tous deux actifs à l'aide de profils MEP (Multiple Enabled Profiles).

Présentation du protocole SIP

Android fournit une API compatible avec le protocole SIP (Session Initiation Protocol). Vous pouvez ainsi ajouter des fonctionnalités de téléphonie Internet basées sur SIP à vos applications. Android inclut une pile de protocoles SIP complète et des services de gestion des appels intégrés qui permettent aux applications de configurer facilement les appels vocaux entrants et sortants, sans avoir à gérer directement les sessions, la communication au niveau du transport, ni l'enregistrement ou la lecture audio.

Voici des exemples de types d'applications qui peuvent utiliser l'API SIP:

  • Visioconférence
  • Messagerie instantanée

Conditions requises et limites

Voici les conditions requises pour développer une application SIP:

  • Vous devez disposer d'un appareil mobile équipé d'Android 2.3 ou version ultérieure.
  • SIP fonctionne via une connexion de données sans fil. Votre appareil doit donc disposer d'une connexion de données (avec un service de données mobiles ou Wi-Fi). Cela signifie que vous ne pouvez pas effectuer de tests sur AVD. Vous ne pouvez effectuer des tests que sur un appareil physique. Pour en savoir plus, consultez Tester des applications SIP.
  • Chaque participant à la session de communication de l'application doit disposer d'un compte SIP. De nombreux fournisseurs SIP différents proposent des comptes SIP.

Remarque:La bibliothèque android.net.sip n'est pas compatible avec les appels vidéo. Si vous souhaitez implémenter les appels VOIP à l'aide d'une pile SIP telle que android.net.sip, utilisez l'une des nombreuses alternatives Open Source modernes comme base de toute implémentation d'appel VOIP. Vous pouvez également implémenter l'API ConnectionService pour permettre une intégration étroite de ces appels dans l'application Téléphone de l'appareil.

Classes et interfaces de l'API SIP

Voici un résumé des classes et d'une interface (SipRegistrationListener) incluses dans l'API Android SIP:

Classe/Interface Description
SipAudioCall Gère un appel audio Internet via SIP.
SipAudioCall.Listener Écouteur d'événements liés à un appel SIP, par exemple lorsqu'un appel est en cours de réception ("En sonnant") ou qu'un appel est sortant ("Lors d'un appel").
SipErrorCode Définit les codes d'erreur renvoyés lors des actions SIP.
SipManager Fournit des API pour les tâches SIP, telles que l'initiation de connexions SIP, et permet d'accéder aux services SIP associés.
SipProfile Définit un profil SIP, y compris un compte SIP, des informations sur le domaine et le serveur.
SipProfile.Builder Classe d'assistance permettant de créer un SipProfile.
SipSession Représente une session SIP associée à une boîte de dialogue SIP ou à une transaction autonome, et non à une boîte de dialogue.
SipSession.Listener Paramètre "Listener" pour les événements liés à une session SIP, par exemple lorsqu'une session est en cours d'enregistrement ("lors de l'inscription") ou lorsqu'un appel est sortant ("lors d'un appel").
SipSession.State Définit les états de la session SIP, tels que "enregistrement", "appel sortant" et "appel en cours".
SipRegistrationListener Interface qui écoute les événements d'enregistrement SIP.

Création du fichier manifeste...

Si vous développez une application qui utilise l'API SIP, n'oubliez pas que cette fonctionnalité n'est compatible qu'avec Android 2.3 (niveau d'API 9) ou version ultérieure de la plate-forme. En outre, parmi les appareils équipés d'Android 2.3 (niveau d'API 9) ou version ultérieure, tous les appareils ne sont pas compatibles avec SIP.

Pour utiliser SIP, ajoutez les autorisations suivantes au fichier manifeste de votre application:

  • android.permission.USE_SIP
  • android.permission.INTERNET

Pour vous assurer que votre application ne peut être installée que sur des appareils compatibles avec SIP, ajoutez les éléments suivants au fichier manifeste de votre application:

<uses-sdk android:minSdkVersion="9" />

Cela signifie que votre application nécessite Android 2.3 ou version ultérieure. Pour en savoir plus, consultez la page Niveaux d'API et la documentation de l'élément <uses-sdk>.

Pour contrôler la manière dont votre application est exclue des appareils non compatibles avec SIP (par exemple, sur Google Play), ajoutez les éléments suivants au fichier manifeste de votre application:

<uses-feature android:name="android.software.sip.voip" />

Cela indique que votre application utilise l'API SIP. La déclaration doit inclure un attribut android:required qui indique si vous souhaitez que l'application soit exclue des appareils non compatibles avec SIP. D'autres déclarations <uses-feature> peuvent également être nécessaires, en fonction de votre implémentation. Pour en savoir plus, consultez la documentation de l'élément <uses-feature>.

Si votre application est conçue pour recevoir des appels, vous devez également définir un récepteur (sous-classe BroadcastReceiver) dans le fichier manifeste de l'application:

<receiver android:name=".IncomingCallReceiver" android:label="Call Receiver" />

Voici des extraits du fichier manifeste SipDemo:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.example.android.sip">
  ...
     <receiver android:name=".IncomingCallReceiver" android:label="Call Receiver" />
  ...
  <uses-sdk android:minSdkVersion="9" />
  <uses-permission android:name="android.permission.USE_SIP" />
  <uses-permission android:name="android.permission.INTERNET" />
  ...
  <uses-feature android:name="android.software.sip.voip" android:required="true" />
  <uses-feature android:name="android.hardware.wifi" android:required="true" />
  <uses-feature android:name="android.hardware.microphone" android:required="true" />
</manifest>

Créer SipManager

Pour utiliser l'API SIP, votre application doit créer un objet SipManager. SipManager prend en charge les éléments suivants dans votre application:

  • Lancement de sessions SIP...
  • Émission et réception d'appels
  • Enregistrement et annulation de l'enregistrement auprès d'un fournisseur SIP.
  • Vérification de la connectivité de la session...

Instanciez un nouveau SipManager comme suit:

Kotlin

val sipManager: SipManager? by lazy(LazyThreadSafetyMode.NONE) {
    SipManager.newInstance(this)
}

Java

public SipManager sipManager = null;
...
if (sipManager == null) {
    sipManager = SipManager.newInstance(this);
}

Enregistrer auprès d'un serveur SIP

Une application SIP Android classique implique un ou plusieurs utilisateurs, chacun disposant d'un compte SIP. Dans une application SIP Android, chaque compte SIP est représenté par un objet SipProfile.

Un SipProfile définit un profil SIP, y compris un compte SIP, ainsi que des informations sur le domaine et le serveur. Le profil associé au compte SIP sur l'appareil exécutant l'application s'appelle le profil local. Le profil auquel la session est connectée s'appelle le profil de pair. Lorsque votre application SIP se connecte au serveur SIP avec le SipProfile local, l'appareil est enregistré en tant qu'emplacement d'envoi des appels SIP pour votre adresse SIP.

Cette section explique comment créer un SipProfile, l'enregistrer auprès d'un serveur SIP et suivre les événements d'enregistrement.

Pour créer un objet SipProfile, procédez comme suit:

Kotlin

private var sipProfile: SipProfile? = null
...

val builder = SipProfile.Builder(username, domain)
        .setPassword(password)
sipProfile = builder.build()

Java

public SipProfile sipProfile = null;
...

SipProfile.Builder builder = new SipProfile.Builder(username, domain);
builder.setPassword(password);
sipProfile = builder.build();

L'extrait de code suivant ouvre le profil local permettant d'effectuer des appels et/ou de recevoir des appels SIP génériques. L'appelant peut effectuer des appels ultérieurs via mSipManager.makeAudioCall. Cet extrait définit également l'action android.SipDemo.INCOMING_CALL, qui sera utilisée par un filtre d'intent lorsque l'appareil reçoit un appel (consultez Configurer un filtre d'intent pour recevoir des appels). Voici l'étape d'inscription:

Kotlin

val intent = Intent("android.SipDemo.INCOMING_CALL")
val pendingIntent: PendingIntent = PendingIntent.getBroadcast(this, 0, intent, Intent.FILL_IN_DATA)
sipManager?.open(sipProfile, pendingIntent, null)

Java

Intent intent = new Intent();
intent.setAction("android.SipDemo.INCOMING_CALL");
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, Intent.FILL_IN_DATA);
sipManager.open(sipProfile, pendingIntent, null);

Enfin, ce code définit un SipRegistrationListener sur SipManager. Cela permet de savoir si le SipProfile a bien été enregistré auprès de votre fournisseur de services SIP:

Kotlin

sipManager?.setRegistrationListener(sipProfile?.uriString, object : SipRegistrationListener {

    override fun onRegistering(localProfileUri: String) {
        updateStatus("Registering with SIP Server...")
    }

    override fun onRegistrationDone(localProfileUri: String, expiryTime: Long) {
        updateStatus("Ready")
    }

    override fun onRegistrationFailed(
            localProfileUri: String,
            errorCode: Int,
            errorMessage: String
    ) {
        updateStatus("Registration failed. Please check settings.")
    }
})

Java

sipManager.setRegistrationListener(sipProfile.getUriString(), new SipRegistrationListener() {

    public void onRegistering(String localProfileUri) {
        updateStatus("Registering with SIP Server...");
    }

    public void onRegistrationDone(String localProfileUri, long expiryTime) {
        updateStatus("Ready");
    }

    public void onRegistrationFailed(String localProfileUri, int errorCode,
        String errorMessage) {
        updateStatus("Registration failed.  Please check settings.");
    }
}

Lorsque votre application a terminé d'utiliser un profil, elle doit le fermer pour libérer les objets associés en mémoire et annuler l'enregistrement de l'appareil sur le serveur. Par exemple :

Kotlin

fun closeLocalProfile() {
    try {
        sipManager?.close(sipProfile?.uriString)
    } catch (ee: Exception) {
        Log.d("WalkieTalkieActivity/onDestroy", "Failed to close local profile.", ee)
    }
}

Java

public void closeLocalProfile() {
    if (sipManager == null) {
       return;
    }
    try {
       if (sipProfile != null) {
          sipManager.close(sipProfile.getUriString());
       }
     } catch (Exception ee) {
       Log.d("WalkieTalkieActivity/onDestroy", "Failed to close local profile.", ee);
     }
}

Passer un appel audio

Pour passer un appel audio, vous devez disposer des éléments suivants:

  • Un SipProfile qui effectue l'appel (le "profil local") et une adresse SIP valide pour recevoir l'appel (le "profil d'appairage").
  • Un objet SipManager

Pour passer un appel audio, vous devez configurer un SipAudioCall.Listener. Une grande partie de l'interaction du client avec la pile SIP s'effectue via des écouteurs. Dans cet extrait de code, vous voyez comment SipAudioCall.Listener configure les éléments une fois l'appel établi:

Kotlin

var listener: SipAudioCall.Listener = object : SipAudioCall.Listener() {

    override fun onCallEstablished(call: SipAudioCall) {
        call.apply {
            startAudio()
            setSpeakerMode(true)
            toggleMute()
        }
    }

    override fun onCallEnded(call: SipAudioCall) {
        // Do something.
    }
}

Java

SipAudioCall.Listener listener = new SipAudioCall.Listener() {

   @Override
   public void onCallEstablished(SipAudioCall call) {
      call.startAudio();
      call.setSpeakerMode(true);
      call.toggleMute();
         ...
   }

   @Override

   public void onCallEnded(SipAudioCall call) {
      // Do something.
   }
};

Une fois que vous avez configuré SipAudioCall.Listener, vous pouvez effectuer l'appel. La méthode SipManager makeAudioCall utilise les paramètres suivants:

  • Un profil SIP local (l'appelant).
  • Un profil SIP de pairs (l'utilisateur appelé).
  • Un SipAudioCall.Listener pour écouter les événements d'appel de SipAudioCall. Il peut s'agir de null, mais comme indiqué ci-dessus, l'écouteur est utilisé pour effectuer la configuration une fois l'appel établi.
  • Valeur du délai avant expiration, en secondes.

Par exemple :

Kotlin

val call: SipAudioCall? = sipManager?.makeAudioCall(
        sipProfile?.uriString,
        sipAddress,
        listener,
        30
)

Java

call = sipManager.makeAudioCall(sipProfile.getUriString(), sipAddress, listener, 30);

Recevoir des appels

Pour recevoir des appels, une application SIP doit inclure une sous-classe de BroadcastReceiver capable de répondre à un intent indiquant qu'il y a un appel entrant. Vous devez donc effectuer les opérations suivantes dans votre application:

  • Dans AndroidManifest.xml, déclarez un <receiver>. Dans SipDemo, il s'agit de <receiver android:name=".IncomingCallReceiver" android:label="Call Receiver" />.
  • Implémentez le récepteur, qui est une sous-classe de BroadcastReceiver. Dans SipDemo, il s'agit de IncomingCallReceiver.
  • Initialisez le profil local (SipProfile) avec un intent en attente qui déclenche votre récepteur lorsque quelqu'un appelle le profil local.
  • Configurez un filtre d'intent qui filtre en fonction de l'action représentant un appel entrant. Dans SipDemo, cette action est android.SipDemo.INCOMING_CALL.

Sous-classement de BroadcastReceiver

Pour recevoir des appels, votre application SIP doit sous-classer BroadcastReceiver. Le système Android gère les appels SIP entrants et diffuse un intent "appel entrant" (tel que défini par l'application) lorsqu'il reçoit un appel. Voici le code BroadcastReceiver sous-classé de l'exemple SipDemo.

Kotlin

/**
 * Listens for incoming SIP calls, intercepts and hands them off to WalkieTalkieActivity.
 */
class IncomingCallReceiver : BroadcastReceiver() {

    /**
     * Processes the incoming call, answers it, and hands it over to the
     * WalkieTalkieActivity.
     * @param context The context under which the receiver is running.
     * @param intent The intent being received.
     */
    override fun onReceive(context: Context, intent: Intent) {
        val wtActivity = context as WalkieTalkieActivity

        var incomingCall: SipAudioCall? = null
        try {
            incomingCall = wtActivity.sipManager?.takeAudioCall(intent, listener)
            incomingCall?.apply {
                answerCall(30)
                startAudio()
                setSpeakerMode(true)
                if (isMuted) {
                    toggleMute()
                }
                wtActivity.call = this
                wtActivity.updateStatus(this)
            }
        } catch (e: Exception) {
            incomingCall?.close()
        }
    }

    private val listener = object : SipAudioCall.Listener() {

        override fun onRinging(call: SipAudioCall, caller: SipProfile) {
            try {
                call.answerCall(30)
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
    }
}

Java

/**
 * Listens for incoming SIP calls, intercepts and hands them off to WalkieTalkieActivity.
 */
public class IncomingCallReceiver extends BroadcastReceiver {
    /**
     * Processes the incoming call, answers it, and hands it over to the
     * WalkieTalkieActivity.
     * @param context The context under which the receiver is running.
     * @param intent The intent being received.
     */
    @Override
    public void onReceive(Context context, Intent intent) {
        SipAudioCall incomingCall = null;
        try {
            SipAudioCall.Listener listener = new SipAudioCall.Listener() {
                @Override
                public void onRinging(SipAudioCall call, SipProfile caller) {
                    try {
                        call.answerCall(30);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            };
            WalkieTalkieActivity wtActivity = (WalkieTalkieActivity) context;
            incomingCall = wtActivity.sipManager.takeAudioCall(intent, listener);
            incomingCall.answerCall(30);
            incomingCall.startAudio();
            incomingCall.setSpeakerMode(true);
            if(incomingCall.isMuted()) {
                incomingCall.toggleMute();
            }
            wtActivity.call = incomingCall;
            wtActivity.updateStatus(incomingCall);
        } catch (Exception e) {
            if (incomingCall != null) {
                incomingCall.close();
            }
        }
    }
}

Configurer un filtre d'intent pour recevoir des appels

Lorsque le service SIP reçoit un nouvel appel, il envoie un intent avec la chaîne d'action fournie par l'application. Dans SipDemo, cette chaîne d'action est android.SipDemo.INCOMING_CALL.

Cet extrait de code de SipDemo montre comment l'objet SipProfile est créé avec un intent en attente en fonction de la chaîne d'action android.SipDemo.INCOMING_CALL. L'objet PendingIntent effectue une diffusion lorsque SipProfile reçoit un appel:

Kotlin

val sipManager: SipManager? by lazy(LazyThreadSafetyMode.NONE) {
    SipManager.newInstance(this)
}

var sipProfile: SipProfile? = null
...

val intent = Intent("android.SipDemo.INCOMING_CALL")
val pendingIntent: PendingIntent = PendingIntent.getBroadcast(this, 0, intent, Intent.FILL_IN_DATA)
sipManager?.open (sipProfile, pendingIntent, null)

Java

public SipManager sipManager = null;
public SipProfile sipProfile = null;
...

Intent intent = new Intent();
intent.setAction("android.SipDemo.INCOMING_CALL");
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, Intent.FILL_IN_DATA);
sipManager.open(sipProfile, pendingIntent, null);

La diffusion sera interceptée par le filtre d'intent, qui déclenchera ensuite le récepteur (IncomingCallReceiver). Vous pouvez spécifier un filtre d'intent dans le fichier manifeste de votre application ou le faire dans le code, comme dans la méthode onCreate() de l'exemple SipDemo du Activity de l'application:

Kotlin

class WalkieTalkieActivity : Activity(), View.OnTouchListener {
    ...
    lateinit var callReceiver: IncomingCallReceiver
    ...

    override fun onCreate(savedInstanceState: Bundle) {
        val filter = IntentFilter().apply {
            addAction("android.SipDemo.INCOMING_CALL")
        }
        callReceiver = IncomingCallReceiver()
        this.registerReceiver(callReceiver, filter)
        ...
    }
    ...
}

Java

public class WalkieTalkieActivity extends Activity implements View.OnTouchListener {
...
    public IncomingCallReceiver callReceiver;
    ...

    @Override
    public void onCreate(Bundle savedInstanceState) {

       IntentFilter filter = new IntentFilter();
       filter.addAction("android.SipDemo.INCOMING_CALL");
       callReceiver = new IncomingCallReceiver();
       this.registerReceiver(callReceiver, filter);
       ...
    }
    ...
}

Tester les applications SIP

Pour tester les applications SIP, vous avez besoin des éléments suivants:

  • Un appareil mobile équipé d'Android 2.3 ou version ultérieure SIP fonctionne sans fil. Vous devez donc le tester sur un véritable appareil. Les tests sur AVD ne fonctionneront pas.
  • Un compte SIP. De nombreux fournisseurs SIP différents proposent des comptes SIP.
  • Si vous passez un appel, il doit également s'agir d'un compte SIP valide.

Pour tester une application SIP:

  1. Sur votre appareil, connectez-vous à un réseau sans fil (Paramètres > Sans fil et réseaux > Wi-Fi > Paramètres Wi-Fi).
  2. Configurez votre appareil mobile pour les tests, comme décrit dans Développer sur un appareil.
  3. Exécutez votre application sur votre appareil mobile, comme décrit dans Développer sur un appareil.
  4. Si vous utilisez Android Studio, vous pouvez afficher la sortie du journal d'application en ouvrant la console du journal des événements (View > Tool Windows > Event Log (Vue > Fenêtres d'outil > Journal des événements)).
  5. Assurez-vous que votre application est configurée pour lancer Logcat automatiquement lors de son exécution :
    1. Sélectionnez Run > Edit Configurations (Exécuter > Modifier les configurations).
    2. Dans la fenêtre Run/Debug Configurations, sélectionnez l'onglet Miscellaneous (Divers).
    3. Sous Logcat, sélectionnez Afficher Logcat automatiquement, puis sélectionnez OK.