Session Initiation Protocol – Übersicht

eSIMs und SIM-Karten erkennen

Karten erkennen

Android-Geräte mit SIM-Karten und eSIMs verwenden die folgenden IDs in den Telefonie-APIs, einschließlich [`TelephonyManager`](/reference/android/telephony/TelephonyManager) und [`SubscriptionManager`](/reference/android/telephony/SubscriptionManager): * Abo-ID: eindeutige ID für ein Mobilfunkabo. * Logischer Slotindex oder ID: eindeutiger Index, der sich auf einen logischen SIM-Steckplatz bezieht. Logische Slot-IDs beginnen bei 0 und erhöhen sich je nach Anzahl der unterstützten aktiven Slots auf einem Gerät. Ein Dual-SIM-Gerät hat beispielsweise normalerweise Slot 0 und Slot 1. Wenn ein Gerät mehrere physische Slots hat, aber nur einen aktiven Slot unterstützt, hat es nur die logische Slot-ID 0. * Physischer Slotindex oder ID: eindeutiger Index, der sich auf einen physischen SIM-Steckplatz bezieht. Die IDs physischer Slots beginnen bei 0 und erhöhen sich je nach Anzahl der physischen Slots auf dem Gerät. Diese unterscheidet sich von der Anzahl der logischen Slots, die ein Gerät hat, die der Anzahl der aktiven Slots entspricht, die ein Gerät verwenden kann. Beispielsweise kann ein Gerät, das zwischen Dual-SIM- und Single-SIM-Modus wechselt, immer zwei physische Slots haben, im Einzel-SIM-Modus hat es jedoch nur einen logischen Slot. * Karten-ID: Eindeutige ID zur Identifizierung einer UiccCard. ![Diagramm zur Verwendung von IDs in einem Fall mit zwei logischen und drei physischen Slots](/images/guide/topics/connectivity/tel-ids.png) Im obigen Diagramm: * Das Gerät hat zwei logische Slots. * Im physischen Slot 0 befindet sich eine physische UICC-Karte mit einem aktiven Profil. * Im physischen Slot 2 befindet sich ein eUICC mit einem aktiven Profil. * Physischer Slot 1 wird derzeit nicht verwendet. ![Diagramm zur Verwendung von IDs in einem Fall mit drei logischen Slots und zwei physischen Slots](/images/guide/topics/connectivity/tel-ids-2.png) Im obigen Diagramm: * Das Gerät hat drei logische Slots. * Im physischen Slot 0 befindet sich eine physische UICC-Karte mit einem aktiven Profil. * Im physischen Slot 1 befindet sich ein eUICC mit zwei heruntergeladenen Profilen, die beide mit MEP (Multiple Enabled Profiles) aktiv sind.

Session Initiation Protocol – Übersicht

Android stellt eine API zur Verfügung, die das Session Initiation Protocol (SIP) unterstützt. Dadurch können Sie Ihren Anwendungen SIP-basierte Funktionen für Internettelefonie hinzufügen. Android umfasst einen vollständigen SIP-Protokoll-Stack und integrierte Anrufverwaltungsdienste, mit denen Anwendungen auf einfache Weise ausgehende und eingehende Sprachanrufe einrichten können, ohne Sitzungen, Kommunikation auf Transportebene, Audioaufzeichnungen oder -wiedergabe direkt verwalten zu müssen.

Hier sind Beispiele für die Arten von Anwendungen, die die SIP API verwenden könnten:

  • Videokonferenzsysteme
  • Chat

Anforderungen und Einschränkungen

Für die Entwicklung einer SIP-Anwendung gelten folgende Anforderungen:

  • Sie benötigen ein Mobilgerät mit Android 2.3 oder höher.
  • SIP läuft über eine drahtlose Datenverbindung. Ihr Gerät muss also über eine Datenverbindung verfügen (mit einem mobilen Datendienst oder WLAN). Das bedeutet, dass Sie nicht auf AVD testen können, sondern nur auf einem physischen Gerät. Weitere Informationen finden Sie unter SIP-Anwendungen testen.
  • Jeder Teilnehmer der Kommunikationssitzung der Anwendung muss ein SIP-Konto haben. Es gibt viele verschiedene SIP-Anbieter, die SIP-Konten anbieten.

Hinweis:Videoanrufe werden von der android.net.sip-Bibliothek nicht unterstützt. Wenn Sie VoIP-Anrufe mit einem SIP-Stack wie android.net.sip implementieren möchten, sollten Sie eine der vielen modernen Open-Source-Alternativen als Grundlage für jede VOIP-Anrufimplementierung verwenden. Alternativ kannst du die ConnectionService API implementieren, um diese Aufrufe eng in die Telefon-App des Geräts einzubinden.

SIP API-Klassen und -Schnittstellen

Im Folgenden finden Sie eine Zusammenfassung der Klassen und einer Schnittstelle (SipRegistrationListener), die in der Android SIP API enthalten sind:

Klasse/Benutzeroberfläche Beschreibung
SipAudioCall Verarbeitet einen Internet-Audioanruf über SIP.
SipAudioCall.Listener Listener für Ereignisse im Zusammenhang mit einem SIP-Anruf, z. B. wenn ein Anruf empfangen wird („bei Klingeln“) oder ausgehender Anruf („bei Anrufen“).
SipErrorCode Definiert Fehlercodes, die bei SIP-Aktionen zurückgegeben werden.
SipManager Stellt APIs für SIP-Aufgaben wie das Initiieren von SIP-Verbindungen bereit und bietet Zugriff auf zugehörige SIP-Dienste.
SipProfile Definiert ein SIP-Profil, einschließlich SIP-Konto, Domain und Serverinformationen.
SipProfile.Builder Hilfsklasse zum Erstellen eines SipProfiles.
SipSession Stellt eine SIP-Sitzung dar, die einem SIP-Dialog oder einer eigenständigen Transaktion außerhalb eines Dialogfelds zugeordnet ist.
SipSession.Listener Listener für Ereignisse im Zusammenhang mit einer SIP-Sitzung, z. B. wenn eine Sitzung registriert wird („bei der Registrierung“) oder ein Anruf ausgehend („bei Anrufen“).
SipSession.State Definiert den SIP-Sitzungsstatus, z. B. „Registrierung“, „Ausgehender Anruf“ und „Im Anruf“.
SipRegistrationListener Eine Schnittstelle, die als Listener für SIP-Registrierungsereignisse fungiert.

Manifest wird erstellt

Wenn Sie eine Anwendung entwickeln, die die SIP API verwendet, denken Sie daran, dass die Funktion nur unter Android 2.3 (API-Level 9) und höheren Versionen der Plattform unterstützt wird. Außerdem bieten nicht alle Geräte mit Android 2.3 (API-Level 9) oder höher SIP-Unterstützung.

Fügen Sie dem Manifest Ihrer App die folgenden Berechtigungen hinzu, um SIP zu verwenden:

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

Damit Ihre Anwendung nur auf Geräten installiert werden kann, die SIP unterstützen, fügen Sie dem Manifest Ihrer Anwendung Folgendes hinzu:

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

Dies bedeutet, dass für Ihre Anwendung Android 2.3 oder höher erforderlich ist. Weitere Informationen finden Sie unter API-Ebenen und in der Dokumentation zum Element <uses-sdk>.

Wenn Sie festlegen möchten, wie Ihre App von Geräten gefiltert wird, die SIP nicht unterstützen (z. B. bei Google Play), fügen Sie dem Manifest Ihrer App Folgendes hinzu:

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

Dies gibt an, dass Ihre Anwendung die SIP API verwendet. Die Deklaration sollte ein android:required-Attribut enthalten, das angibt, ob die App von Geräten gefiltert werden soll, die keine SIP-Unterstützung bieten. Je nach Implementierung sind möglicherweise weitere <uses-feature>-Deklarationen erforderlich. Weitere Informationen finden Sie in der Dokumentation zum Element <uses-feature>.

Wenn Ihre Anwendung für den Empfang von Aufrufen konzipiert ist, müssen Sie im Manifest der Anwendung außerdem einen Empfänger (Unterklasse BroadcastReceiver) definieren:

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

Hier sind Auszüge aus dem SipDemo-Manifest:

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

SipManager wird erstellt

Damit Sie die SIP API verwenden können, muss Ihre Anwendung ein SipManager-Objekt erstellen. Der SipManager übernimmt in Ihrer Anwendung folgende Aufgaben:

  • SIP-Sitzungen werden gestartet.
  • Initiieren und Empfangen von Anrufen
  • Bei einem SIP-Anbieter registrieren und Registrierung aufheben
  • Sitzungsverbindung prüfen

So instanziieren Sie eine neue SipManager:

Kotlin

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

Java

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

Registrierung bei einem SIP-Server

An einer typischen Android-SIP-Anwendung sind ein oder mehrere Nutzer beteiligt, die jeweils ein SIP-Konto haben. In einer Android SIP-Anwendung wird jedes SIP-Konto durch ein SipProfile-Objekt dargestellt.

Ein SipProfile definiert ein SIP-Profil, einschließlich eines SIP-Kontos, sowie Domain- und Serverinformationen. Das mit dem SIP-Konto auf dem Gerät, auf dem die Anwendung ausgeführt wird, verknüpfte Profil wird als lokales Profil bezeichnet. Das Profil, mit dem die Sitzung verbunden ist, wird als Peer-Profil bezeichnet. Wenn sich Ihre SIP-Anwendung mit dem lokalen SipProfile beim SIP-Server anmeldet, wird das Gerät dadurch als Standort registriert, an den SIP-Anrufe für Ihre SIP-Adresse gesendet werden können.

In diesem Abschnitt wird gezeigt, wie Sie einen SipProfile erstellen, ihn bei einem SIP-Server registrieren und Registrierungsereignisse verfolgen.

So erstellen Sie ein SipProfile-Objekt:

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

Der folgende Codeauszug öffnet das lokale Profil für Anrufe und/oder den Empfang von allgemeinen SIP-Anrufen. Der Aufrufer kann über mSipManager.makeAudioCall nachfolgende Aufrufe tätigen. In diesem Auszug wird auch die Aktion android.SipDemo.INCOMING_CALL festgelegt, die von einem Intent-Filter verwendet wird, wenn das Gerät einen Aufruf empfängt (siehe Intent-Filter für den Empfang von Aufrufen einrichten). Dies ist der Registrierungsschritt:

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

Schließlich wird mit diesem Code ein SipRegistrationListener für das SipManager festgelegt. Damit wird erfasst, ob SipProfile bei Ihrem SIP-Dienstanbieter registriert wurde:

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

Wenn Ihre Anwendung ein Profil nicht mehr verwendet, sollte sie es schließen, um zugehörige Objekte im Arbeitsspeicher freizugeben und das Gerät vom Server zu trennen. Beispiel:

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

Sprachanruf starten

Für einen Sprachanruf benötigen Sie Folgendes:

  • Eine SipProfile, die den Anruf tätigt (das „lokale Profil“), und eine gültige SIP-Adresse für den Empfang des Anrufs (das „Peer-Profil“).
  • Ein SipManager-Objekt.

Für einen Sprachanruf solltest du eine SipAudioCall.Listener einrichten. Ein Großteil der Clientinteraktion mit dem SIP-Stack erfolgt über Listener. In diesem Snippet sehen Sie, wie die Einrichtung von SipAudioCall.Listener nach dem Aufruf erfolgt:

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

Sobald Sie SipAudioCall.Listener eingerichtet haben, können Sie den Anruf starten. Die SipManager-Methode makeAudioCall verwendet die folgenden Parameter:

  • Ein lokales SIP-Profil (der Anrufer).
  • Ein Peer-SIP-Profil (der angerufene Nutzer).
  • Ein SipAudioCall.Listener, um auf die Aufrufereignisse von SipAudioCall zu warten. Dies kann null sein. Wie oben gezeigt, wird der Listener jedoch zum Einrichten von Elementen verwendet, sobald der Aufruf erfolgt.
  • Der Wert für das Zeitlimit in Sekunden.

Beispiele:

Kotlin

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

Java

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

Anrufe erhalten

Zum Empfangen von Anrufen muss eine SIP-Anwendung eine abgeleitete Klasse von BroadcastReceiver enthalten, die in der Lage ist, auf einen Intent zu antworten, der einen eingehenden Anruf anzeigt. Daher müssen Sie in Ihrer Anwendung Folgendes tun:

  • Deklarieren Sie in AndroidManifest.xml einen <receiver>. In SipDemo ist dies <receiver android:name=".IncomingCallReceiver" android:label="Call Receiver" />.
  • Implementieren Sie den Empfänger, der eine abgeleitete Klasse von BroadcastReceiver ist. In SipDemo ist dies IncomingCallReceiver.
  • Initialisieren Sie das lokale Profil (SipProfile) mit einem ausstehenden Intent, der Ihren Empfänger auslöst, wenn jemand das lokale Profil aufruft.
  • Richten Sie einen Intent-Filter ein, der nach der Aktion filtert, die einen eingehenden Anruf darstellt. In SipDemo ist diese Aktion android.SipDemo.INCOMING_CALL.

Abgeleitete Klasse von BroadcastReceiver

Damit Sie Anrufe empfangen können, muss Ihre SIP-Anwendung die abgeleiteten Klassen von BroadcastReceiver haben. Das Android-System verarbeitet eingehende SIP-Anrufe und sendet den von der App definierten Intent „eingehender Anruf“, wenn ein Anruf eingeht. Hier ist der abgeleitete Code von BroadcastReceiver aus dem SipDemo-Beispiel.

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

Intent-Filter zum Empfang von Anrufen einrichten

Wenn der SIP-Dienst einen neuen Aufruf empfängt, sendet er einen Intent mit dem von der Anwendung bereitgestellten Aktionsstring. In SipDemo lautet dieser Aktionsstring android.SipDemo.INCOMING_CALL.

Dieser Codeauszug aus SipDemo zeigt, wie das Objekt SipProfile mit einem ausstehenden Intent basierend auf dem Aktionsstring android.SipDemo.INCOMING_CALL erstellt wird. Das PendingIntent-Objekt führt einen Broadcast aus, wenn SipProfile einen Aufruf empfängt:

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

Die Übertragung wird vom Intent-Filter abgefangen, der dann den Empfänger auslöst (IncomingCallReceiver). Sie können einen Intent-Filter in der Manifestdatei Ihrer Anwendung angeben oder dies im Code wie in der Methode SipDemo der Activity-Methode der Beispielanwendung onCreate()

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

SIP-Anwendungen testen

Zum Testen von SIP-Anwendungen benötigen Sie Folgendes:

  • Ein Mobilgerät mit Android 2.3 oder höher. SIP wird über WLAN ausgeführt, daher müssen Sie den Test auf einem echten Gerät durchführen. Tests mit AVD werden nicht funktionieren.
  • Ein SIP-Konto. Es gibt viele verschiedene SIP-Anbieter, die SIP-Konten anbieten.
  • Wenn Sie jemanden anrufen, muss es sich ebenfalls um ein gültiges SIP-Konto handeln.

So testen Sie eine SIP-Anwendung:

  1. Stellen Sie auf Ihrem Gerät eine WLAN-Verbindung her (Einstellungen > Drahtlos & Netzwerke > WLAN > WLAN-Einstellungen).
  2. Richten Sie Ihr Mobilgerät für Tests ein, wie unter Auf einem Gerät entwickeln beschrieben.
  3. Führen Sie die Anwendung wie unter Auf einem Gerät entwickeln beschrieben auf Ihrem Mobilgerät aus.
  4. Wenn Sie Android Studio verwenden, können Sie die Ausgabe des Anwendungslogs in der Ereignisprotokollkonsole ansehen (Ansicht > Toolfenster > Ereignisprotokoll).
  5. Achten Sie darauf, dass Ihre Anwendung so konfiguriert ist, dass Logcat bei der Ausführung automatisch gestartet wird:
    1. Wählen Sie Run > Edit Configurations (Ausführen > Konfigurationen bearbeiten) aus.
    2. Wählen Sie im Fenster Run/Debug Configurations den Tab Miscellaneous (Verschiedenes) aus.
    3. Wählen Sie unter Logcat (Logcat automatisch anzeigen) die Option Show logcat automatically (Logcat automatisch anzeigen) und dann OK aus.