Omówienie protokołu inicjowania sesji

Wykrywanie kart eSIM i kart SIM

Wykrywam karty

Urządzenia z Androidem z kartami SIM i kartami eSIM używają w telewizji następujących identyfikatorów: interfejsów API, w tym [„TelephonyManager”](/reference/android/telephony/TelephonyManager) i [`SubscriptionManager`](/reference/android/telephony/SubscriptionManager): * Identyfikator subskrypcji: unikalny identyfikator subskrypcji mobilnej. * Logiczny indeks gniazda lub identyfikator: unikalny indeks odnoszący się do logicznego gniazda karty SIM. Logiczne identyfikatory przedziałów zaczynają się od 0 i zwiększają się w zależności od liczby obsługiwane aktywne przedziały na urządzeniu. Na przykład zwykle urządzenia 2 SIM ma miejsce 0 i 1. Jeśli urządzenie ma wiele gniazd fizycznych, ale tylko obsługuje jeden aktywny przedział, będzie miał tylko identyfikator logiczny 0. * Fizyczny indeks gniazda lub identyfikator: unikalny indeks odnoszący się do fizycznego gniazda karty SIM. Identyfikatory przedziałów fizycznych zaczynają się od 0 i zwiększają się w zależności od liczby fizycznych gniazda na urządzeniu. Ta wartość różni się od liczby przedziałów logicznych urządzenia , która odpowiada liczbie aktywnych przedziałów, które może obsłużyć urządzenie. . Na przykład urządzenie przełączające się między kartami 2 SIM i 1 SIM tryb może mieć zawsze dwa gniazda fizyczne, a w trybie jednej karty SIM tylko jeden przedział logiczny. * Identyfikator karty: unikalny identyfikator służący do identyfikacji karty UiccCard. ![Schemat użycia identyfikatorów w przypadku z 2 przedziałami logicznymi i 3 przedziałami fizycznymi](/images/guide/topics/connectivity/tel-ids.png) Na powyższym diagramie: * Urządzenie ma dwa gniazda logiczne. * W gnieździe fizycznym 0 znajduje się fizyczna karta UICC z aktywnym profilem. * W fizycznym przedziale 2 jest to eUICC z aktywnym profilem. * Gniazdo fizyczne 1 nie jest obecnie używane. ![Schemat użycia identyfikatorów w przypadku z 3 przedziałami logicznymi i 2 przedziałami fizycznymi](/images/guide/topics/connectivity/tel-ids-2.png) Na powyższym diagramie: * Urządzenie ma 3 gniazda logiczne. * W gnieździe fizycznym 0 znajduje się fizyczna karta UICC z aktywnym profilem. * W fizycznym przedziale 1 jest to eUICC z 2 pobranymi profilami, z których oba są aktywne przy użyciu wielu włączonych profili MEP.

Omówienie protokołu inicjowania sesji

Android udostępnia interfejs API, który obsługuje protokół SIP (Session Initiation Protocol). Dzięki temu możesz dodać do swoich aplikacji funkcje telefonii internetowej oparte na SIP. Android obejmuje pełny stos protokołów SIP i zintegrowane zarządzanie połączeniami które pozwalają aplikacjom łatwo konfigurować wychodzące i przychodzące połączenia głosowe, bez konieczności zarządzania sesjami, komunikacją na poziomie transportu czy dźwiękiem. bezpośrednio nagrywać lub odtwarzać treści.

Oto przykłady typów aplikacji, które mogą używać interfejsu SIP API:

  • Rozmowy wideo
  • Komunikator internetowy

Wymagania i ograniczenia

Oto wymagania dotyczące programowania aplikacji SIP:

  • Musisz mieć urządzenie mobilne z systemem Android 2.3 lub nowszym.
  • SIP jest przesyłany przez bezprzewodowe połączenie transmisji danych, dlatego urządzenie musi mieć dostępną połączenie (przez komórkową usługę transmisji danych lub Wi-Fi). Oznacza to, że musisz nie możesz przeprowadzić testów na urządzeniu AVD – możesz to zrobić tylko na urządzeniu fizycznym. Więcej informacji: Testowanie aplikacji SIP
  • Każdy uczestnik sesji komunikacyjnej w aplikacji musi mieć Konto SIP. Konta SIP oferuje wielu różnych dostawców SIP.

Uwaga: biblioteka android.net.sip nie obsługuje filmów. połączeń. Jeśli chcesz wdrożyć połączenia VOIP za pomocą stosu SIP, takiego jak android.net.sip, zapoznaj się z jednym z wielu nowoczesnych rozwiązań typu open source jako podstawę implementacji VOIP. Ewentualnie możesz zastosować ConnectionService Interfejs API zapewniający ścisłą integrację tych połączeń z Telefonem na urządzeniu .

Klasy i interfejsy SIP API

Oto podsumowanie klas i 1 interfejsu (SipRegistrationListener) uwzględnione w Android SIP Interfejs API:

Klasa/interfejs Opis
SipAudioCall Obsługuje internetowe połączenie głosowe przez SIP.
SipAudioCall.Listener Detektor zdarzeń związanych z połączeniem SIP, na przykład podczas nawiązywania połączenia odebrane („sygnał dzwonienia”) lub połączenie wychodzące („trwa dzwonienie”).
SipErrorCode Definiuje kody błędów zwracanych podczas działań SIP.
SipManager Udostępnia interfejsy API do zadań SIP, takich jak inicjowanie połączeń SIP, oraz dostęp do powiązanych usług SIP.
SipProfile Definiuje profil SIP, w tym konto SIP, domenę i informacje o serwerze.
SipProfile.Builder Klasa pomocnicza do tworzenia profilu SipProfile.
SipSession Reprezentuje sesję SIP, która jest powiązana z oknem SIP lub samodzielną transakcją poza oknem.
SipSession.Listener Detektor zdarzeń związanych z sesją SIP, na przykład podczas rejestrowania sesji („w trakcie rejestracji”) lub połączenie wychodzące („trwa połączenie”).
SipSession.State Definiuje stany sesji SIP, takie jak „rejestrowanie”, „połączenie wychodzące” i „w trakcie rozmowy”.
SipRegistrationListener Interfejs, który jest detektorem zdarzeń rejestracji SIP.

Tworzę plik manifestu

Jeśli tworzysz aplikację korzystającą z interfejsu SIP API, pamiętaj, że jest obsługiwana tylko w Androidzie 2.3 (poziom API 9) i nowszych wersjach platformy. Poza tym na urządzeniach z Androidem 2.3 (poziom interfejsu API 9) lub nowszym nie wszystkie urządzenia obsługują protokół SIP.

Aby używać SIP, dodaj do pliku manifestu aplikacji te uprawnienia:

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

Aby aplikacje można było instalować tylko na urządzeniach, obsługujące SIP, dodaj poniższy kod do plik manifestu:

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

Oznacza to, że aplikacja wymaga systemu Android 2.3 lub nowszego. Dla: więcej informacji znajdziesz w Poziomy interfejsu API oraz dokumentacja usługi <uses-sdk> .

Aby kontrolować sposób filtrowania aplikacji z urządzeń, które nie obsługują SIP (np. w Google Play), dodaj ten kod do plik manifestu:

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

Oznacza to, że Twoja aplikacja używa interfejsu SIP API. Deklaracja powinna dodaj atrybut android:required, który wskazuje, czy chcesz, aby aplikacja była odfiltrowywana z urządzeń, które nie obsługują SIP. Mogą być też potrzebne inne deklaracje <uses-feature>, w zależności od implementacji. Więcej informacji znajdziesz w dokumentacji dla wartości <uses-feature> .

Jeśli Twoja aplikacja jest przeznaczona do odbierania połączeń, musisz też zdefiniować odbiornik (podklasę BroadcastReceiver) w pliku manifestu aplikacji:

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

Oto fragmenty pliku manifestu 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>

Tworzę SipManagera

Aby można było korzystać z interfejsu SIP API, aplikacja musi utworzyć obiekt SipManager. SipManager zajmuje zadbaj o następujące elementy swojej aplikacji:

  • Inicjuję sesje SIP.
  • inicjowanie i odbieranie połączeń,
  • Rejestrowanie i wyrejestrowanie u dostawcy SIP.
  • Weryfikuję połączenia sesji.

Tworzysz nową instancję SipManager w ten sposób:

Kotlin

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

Java

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

Rejestrowanie na serwerze SIP

Z typowej aplikacji SIP na Androida korzysta co najmniej 1 użytkownik, z których każdy ma konto SIP. W aplikacji SIP na Androidzie każde konto SIP reprezentowane przez obiekt SipProfile.

SipProfile definiuje profil SIP, w tym profil SIP informacje o koncie oraz domenie i serwerze. Profil powiązany z SIP na urządzeniu, na którym jest uruchomiona aplikacja, nazywamy lokalne profil. Profil, z którym jest połączona sesja, nosi nazwę profilu porównawczego. Gdy aplikacja SIP zaloguje się na serwerze SIP za pomocą SipProfile, spowoduje to efektywne zarejestrowanie urządzenia jako lokalizacji, do której chcesz wysyłać połączenia SIP z adresu SIP.

W tej sekcji dowiesz się, jak utworzyć SipProfile, zarejestrować go na serwerze SIP i śledzić zdarzenia rejestracji.

Obiekt SipProfile możesz utworzyć w ten sposób:

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

Ten fragment kodu otwiera lokalny profil umożliwiający nawiązywanie połączeń lub odbieranie ogólnych połączeń SIP. Rozmówca może wykonywać kolejne połączenia mSipManager.makeAudioCall Ten fragment określa także akcję, android.SipDemo.INCOMING_CALL, które będą używane przez intencję można filtrować, gdy urządzenie zostanie odebrane (patrz Konfigurowanie filtr intencji do odbierania połączeń). Etap rejestracji:

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

Na koniec ten kod ustawia SipRegistrationListener w: SipManager. Śledzi, czy urządzenie SipProfile zostało zarejestrowane w usłudze SIP dostawca:

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

Gdy aplikacja skończy korzystać z profilu, powinna zamknąć się bezpłatnie. powiązanych obiektów do pamięci i wyrejestrować urządzenie z serwera. Dla: przykład:

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

Rozpoczynanie rozmowy głosowej

Aby rozpocząć rozmowę głosową, musisz mieć:

  • SipProfile, który nawiązuje połączenie (parametr „lokalny profil”) i prawidłowy adres SIP umożliwiający odebranie połączenia ( „profil równorzędny”).
  • Obiekt SipManager.

Aby rozpocząć rozmowę głosową, musisz skonfigurować SipAudioCall.Listener. Duża część interakcji klienta z stosunki SIP są możliwe dzięki detektorom. Ten fragment kodu zawiera informacje o konfiguracji usługi SipAudioCall.Listener po zakończeniu połączenia ustanowiono:

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

Po skonfigurowaniu usługi SipAudioCall.Listener możesz: zadzwoń. Metoda SipManager makeAudioCall przyjmuje te parametry:

  • Lokalny profil SIP (rozmówca).
  • profil SIP połączenia równorzędnego (użytkownik jest wywoływany).
  • SipAudioCall.Listener, aby odsłuchać rozmowę wydarzenia od SipAudioCall. Może to być null, Jak widać powyżej, detektor służy do konfigurowania ustawień po zakończeniu połączenia .
  • Wartość limitu czasu w sekundach.

Na przykład:

Kotlin

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

Java

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

Odbieranie połączeń

Aby odbierać połączenia, aplikacja SIP musi zawierać podklasę BroadcastReceiver, która może reagować na intencję z informacją o nadejściu połączenia. Dlatego musisz wykonać następujące czynności w sekcji Twojej aplikacji:

  • W AndroidManifest.xml zadeklaruj parametr <receiver> W SipDemo jest to <receiver android:name=".IncomingCallReceiver" android:label="Call Receiver" />
  • Zaimplementuj odbiornik, który jest podklasą klasy BroadcastReceiver. W SipDemo jest to IncomingCallReceiver
  • Zainicjuj profil lokalny (SipProfile) za pomocą intencja oczekująca, która uruchamia odbiorcę, gdy ktoś zadzwoni do profilu lokalnego.
  • Skonfiguruj filtr intencji, który filtruje według działania reprezentującego połączenie przychodzące. W SipDemo to działanie jest android.SipDemo.INCOMING_CALL

Odbieranie podklasyfikacji BroadcastReceive

Aby można było odbierać połączenia, aplikacja SIP musi podklasa BroadcastReceiver. System Android obsługuje przychodzące połączenia SIP i przesyła „przychodzące wywołaj" intencję (zdefiniowaną przez aplikację), gdy otrzymana przez telefon. Oto podklasa: BroadcastReceiver z przykładu 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();
            }
        }
    }
}

Konfiguruję filtr intencji, aby otrzymywać połączenia

Po otrzymaniu nowego połączenia usługa SIP wysyła intencję z ciąg znaków działania dostarczony przez aplikację. W SipDemo ten ciąg znaków działania to android.SipDemo.INCOMING_CALL

Ten fragment kodu z SipDemo pokazuje, jak obiekt SipProfile jest tworzony z oczekującą intencją opartą na ciąg działania android.SipDemo.INCOMING_CALL. Obiekt PendingIntent przeprowadzi transmisję, gdy SipProfile otrzyma wywołanie:

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

Transmisja zostanie przechwycona przez filtr intencji, który następnie się uruchomi odbiornik (IncomingCallReceiver). Możesz określić intencję w pliku manifestu aplikacji lub zrób to w kodzie, tak jak w przypadku SipDemo metoda onCreate() przykładowej aplikacji Activity aplikacji:

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

Testowanie aplikacji SIP

Aby przetestować aplikacje SIP, potrzebujesz:

  • Urządzenie mobilne z Androidem 2.3 lub nowszym. Przebieg SIP bezprzewodowo, więc musisz przeprowadzić test na konkretnym urządzeniu. Testowanie za pomocą AVD się nie uda.
  • konto SIP. Konta SIP oferuje wielu różnych dostawców SIP.
  • Jeśli nawiązujesz połączenie, musi ono też być powiązane z prawidłowym kontem SIP.

Aby przetestować aplikację SIP:

  1. Na urządzeniu połącz się z siecią bezprzewodową (Ustawienia > Sieci zwykłe i bezprzewodowe &gt; Wi-Fi > Ustawienia Wi-Fi).
  2. Urządzenie mobilne musisz przygotować do testów w sposób opisany w artykule Tworzenie aplikacji na urządzeniu.
  3. Uruchom aplikację na urządzeniu mobilnym w sposób opisany w artykule Tworzenie aplikacji na urządzeniu.
  4. Jeśli korzystasz z Android Studio, dane wyjściowe dziennika aplikacji możesz wyświetlić, otwórz konsolę dziennika zdarzeń (Widok > Okna narzędzi > Dziennik zdarzeń).
  5. Sprawdź, czy aplikacja jest skonfigurowana tak, aby automatycznie uruchamiała Logcat po uruchomieniu:
    1. Wybierz Uruchom > Edytuj konfiguracje.
    2. Wybierz kartę Inne w oknie Konfiguracje uruchamiania/debugowania.
    3. W sekcji Logcat wybierz Wyświetl logcat automatycznie, a następnie kliknij OK.