Wykrywanie kart eSIM i SIM
Wykrywanie kart
Urządzenia z Androidem z kartami SIM i kartami eSIM oraz w interfejsach API telefonii używają tych identyfikatorów, w tym [`TelephonyManager`](/reference/android/telephony/TelephonyManager) i [`SubscriptionManager`](/reference/android/telephony/SubscriptionManager): * Identyfikator subskrypcji: unikalny identyfikator subskrypcji mobilnej. * Indeks lub identyfikator przedziału logicznego: unikalny indeks odnoszący się do logicznego gniazda SIM. Identyfikatory przedziałów logicznych zaczynają się od 0 i rosną w zależności od liczby aktywnych przedziałów na urządzeniu. Na przykład urządzenie dual SIM ma zwykle gniazdo 0 i gniazdo 1. Jeśli urządzenie ma wiele przedziałów fizycznych, ale obsługuje tylko 1 aktywny przedział, będzie miał tylko identyfikator przedziału logicznego 0. * Indeks lub identyfikator gniazda fizycznego: unikalny indeks odnoszący się do fizycznego gniazda SIM. Identyfikatory przedziałów fizycznych zaczynają się od 0 i rosną w zależności od liczby przedziałów fizycznych na urządzeniu. Różni się ona od liczby przedziałów logicznych na urządzeniu, która odpowiada liczbie aktywnych przedziałów, z których może korzystać. Na przykład urządzenie przełączające się między trybem 2 SIM i 1 SIM może zawsze mieć 2 gniazda fizyczne, ale w trybie pojedynczej karty SIM będzie mieć tylko 1 gniazdo logiczne. * Identyfikator karty: unikalny identyfikator używany do identyfikacji karty UiccCard. ![Schemat wykorzystania identyfikatorów w przypadku z 2 przedziałami logicznymi i 3 przedziałami fizycznymi](/images/guide/topics/connectivity/tel-ids.png) Na powyższym schemacie: * Urządzenie ma 2 przedziały logiczne. * W gnieździe fizycznym 0 znajduje się fizyczna karta UICC z aktywnym profilem. * W miejscu fizycznym 2 jest interfejs eUICC z aktywnym profilem. * Gniazdo fizyczne 1 nie jest obecnie używane. ![Schemat sposobu 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 schemacie: * Urządzenie ma 3 przedziały logiczne. * W gnieździe fizycznym 0 znajduje się fizyczna karta UICC z aktywnym profilem. * W miejscu fizycznym 1 jest eUICC z 2 pobranymi profilami, z których oba są aktywne za pomocą MEP (wielokrotnego włączenia profilu).
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 opartej na SIP. Android obejmuje pełny stos protokołów SIP i zintegrowane usługi zarządzania połączeniami, które pozwalają aplikacjom z łatwością konfigurować wychodzące i przychodzące połączenia głosowe bez konieczności bezpośredniego zarządzania sesjami, komunikacji na poziomie transportowym ani bezpośredniego nagrywania czy odtwarzania dźwięku.
Oto przykłady aplikacji, które mogą korzystać z interfejsu SIP API:
- Wideokonferencje
- Komunikator internetowy
Wymagania i ograniczenia
Oto wymagania dotyczące tworzenia aplikacji SIP:
- Musisz mieć urządzenie mobilne z Androidem 2.3 lub nowszym.
- SIP działa przez bezprzewodowe połączenie do transmisji danych, dlatego Twoje urządzenie musi mieć połączenie do transmisji danych (z mobilną usługą transmisji danych lub Wi-Fi). Oznacza to, że nie możesz przeprowadzić testu za pomocą AVD, lecz tylko na urządzeniu fizycznym. Więcej informacji znajdziesz w artykule o testowaniu aplikacji SIP.
- Każdy uczestnik sesji komunikacyjnej w aplikacji musi mieć konto SIP. Jest wielu różnych dostawców usług SIP, którzy oferują konta SIP.
Uwaga: biblioteka android.net.sip
nie obsługuje rozmów wideo. Jeśli chcesz wdrożyć połączenia VOIP za pomocą stosu SIP, takiego jak android.net.sip
, zapoznaj się z jedną z wielu nowoczesnych alternatyw open source jako podstawy do wdrożenia połączeń VOIP. Możesz też wdrożyć interfejs API ConnectionService
, aby zapewnić ścisłą integrację tych wywołań z aplikacją Telefon na urządzeniu.
Klasy i interfejsy SIP API
Oto podsumowanie klas i 1 interfejsu (SipRegistrationListener
), które wchodzi w skład Android SIP API:
Klasa/interfejs | Opis |
---|---|
SipAudioCall |
Obsługuje internetowe połączenie głosowe przez SIP. |
SipAudioCall.Listener |
Nasłuchiwanie zdarzeń związanych z połączeniem SIP, na przykład podczas odbierania połączenia („gdy dzwoni”) lub tego, że jest ono wychodzące („podczas nawiązywania połączenia”). |
SipErrorCode |
Definiuje kody błędów zwracane 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 informacje o koncie SIP, domenie i serwerze. |
SipProfile.Builder |
Klasa pomocnicza do tworzenia profilu SipProfile. |
SipSession |
Reprezentuje sesję SIP, która jest powiązana z oknem SIP lub samodzielną transakcją, która nie znajduje się w oknie. |
SipSession.Listener |
Detektor zdarzeń związanych z sesją SIP, np. podczas rejestracji sesji lub podczas nawiązywania połączenia. |
SipSession.State |
Definiuje stany sesji SIP, takie jak „rejestracja”, „połączenie wychodzące” i „podczas rozmowy”. |
SipRegistrationListener |
Interfejs, który jest odbiornikiem zdarzeń rejestracji SIP. |
Tworzę plik manifestu
Jeśli tworzysz aplikację, która korzysta z interfejsu SIP API, pamiętaj, że ta funkcja jest obsługiwana tylko na Androidzie 2.3 (poziom API 9) i nowszych wersjach platformy. Pamiętaj też, że nie wszystkie urządzenia z Androidem 2.3 (poziom interfejsu API 9) lub nowszym zapewniają obsługę SIP.
Aby używać SIP, dodaj do pliku manifestu aplikacji te uprawnienia:
android.permission.USE_SIP
android.permission.INTERNET
Aby mieć pewność, że Twoją aplikację można zainstalować tylko na urządzeniach obsługujących protokół SIP, dodaj do jej pliku manifestu ten fragment:
<uses-sdk android:minSdkVersion="9" />
Oznacza to, że Twoja aplikacja wymaga Androida 2.3 lub nowszego. Więcej informacji znajdziesz w sekcji Poziomy interfejsów API i w dokumentacji elementu <uses-sdk>
.
Aby kontrolować sposób, w jaki aplikacja jest odfiltrowywana z urządzeń, które nie obsługują SIP (np. w Google Play), do pliku manifestu aplikacji dodaj ten fragment kodu:
<uses-feature android:name="android.software.sip.voip" />
Oznacza to, że Twoja aplikacja korzysta z interfejsu SIP API. Deklaracja powinna zawierać atrybut android:required
wskazujący, czy aplikacja ma być odfiltrowywana z urządzeń, które nie oferują obsługi SIP.
W zależności od implementacji mogą być potrzebne też inne deklaracje <uses-feature>
. Więcej informacji znajdziesz w dokumentacji elementu <uses-feature>
.
Jeśli Twoja aplikacja jest przeznaczona do odbierania połączeń, musisz też zdefiniować w jej pliku manifestu podklasę odbiorcy (BroadcastReceiver
):
<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ę menedżera SipManagera
Aby korzystać z interfejsu SIP API, aplikacja musi utworzyć obiekt SipManager
. SipManager
dba o te kwestie w Twojej aplikacji:
- Inicjowanie sesji SIP.
- Inicjowanie i odbieranie połączeń.
- Rejestrowanie i wyrejestrowanie u dostawcy SIP.
- Weryfikuję połączenie 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); }
Rejestracja na serwerze SIP
Typowa aplikacja SIP na Androidzie obejmuje co najmniej jednego użytkownika, z którego każdy ma konto SIP. W aplikacji SIP na Androida każde konto SIP jest reprezentowane przez obiekt SipProfile
.
SipProfile
definiuje profil SIP, w tym konto SIP, oraz informacje o domenie i serwerze. Profil powiązany z kontem SIP na urządzeniu, na którym działa aplikacja, jest nazywany profilem lokalnym. Profil, z którym połączona jest sesja, jest nazywany profilem peera. Gdy aplikacja SIP loguje się na serwer SIP za pomocą lokalnego numeru SipProfile
, rejestruje to urządzenie jako lokalizację, na którą mają być wysyłane połączenia SIP trunk pod Twój adres.
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();
Poniższy fragment kodu otwiera profil lokalny, gdzie można nawiązywać połączenia lub odbierać ogólne połączenia SIP. Rozmówca może nawiązywać kolejne połączenia za pomocą funkcji mSipManager.makeAudioCall
. Ten fragment ustawia też działanie android.SipDemo.INCOMING_CALL
, które będzie używane przez filtr intencji, gdy urządzenie otrzyma połączenie (patrz Konfigurowanie filtra intencji na potrzeby 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
na platformie SipManager
. Pozwala to śledzić, czy urządzenie SipProfile
zostało zarejestrowane u dostawcy usług 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."); } }
Gdy aplikacja zakończy korzystanie z profilu, powinna ją zamknąć, uwolnić powiązane z nią obiekty i wyrejestrować urządzenie z serwera. 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); } }
Zaczynam rozmowę głosową
Aby prowadzić rozmowę głosową, musisz mieć:
SipProfile
, który nawiązuje połączenie („profil lokalny”), i prawidłowy adres SIP, który umożliwia odebranie połączenia („profil połączenia równorzędnego”).- Obiekt
SipManager
.
Aby prowadzić rozmowę głosową, musisz skonfigurować urządzenie SipAudioCall.Listener
. Znaczna część interakcji klienta ze stosem SIP
odbywa się przez detektory. W tym fragmencie widać, jak SipAudioCall.Listener
konfiguruje się po nawiązaniu połączenia:
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 urządzenia SipAudioCall.Listener
możesz zacząć dzwonić. Metoda SipManager
makeAudioCall
przyjmuje te parametry:
- Lokalny profil SIP (rozmówca).
- Profil SIP peera (wywoływany użytkownik).
SipAudioCall.Listener
do nasłuchiwania zdarzeń wywołania zSipAudioCall
. Może to byćnull
, ale jak pokazano powyżej, odbiornik służy do konfiguracji ustawień po nawiązaniu 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 można było odbierać połączenia, aplikacja SIP musi zawierać podklasę klasy BroadcastReceiver
z możliwością odpowiadania na intencję wskazującą na połączenie przychodzące. Dlatego w aplikacji musisz wykonać te czynności:
- W polu
AndroidManifest.xml
zadeklaruj<receiver>
. W SipDemo jest to<receiver android:name=".IncomingCallReceiver" android:label="Call Receiver" />
. - Zaimplementuj odbiornik, który jest podklasą klasy
BroadcastReceiver
. W SipDemo jest toIncomingCallReceiver
. - Zainicjuj profil lokalny (
SipProfile
) z intencją oczekiwania, która uruchamia odbiornik, gdy ktoś zadzwoni na profil lokalny. - Skonfiguruj filtr intencji, który będzie filtrować według działania reprezentującego połączenie przychodzące. W SipDemo to działanie jest
android.SipDemo.INCOMING_CALL
.
Podklasyfikacja BroadcastReceivedr
Aby można było odbierać połączenia, aplikacja SIP musi należeć do podklasy BroadcastReceiver
. System Android obsługuje przychodzące połączenia SIP i gdy odbiera połączenie, rozgłasza intencję „połączenie przychodzące” (zgodnie z definicją w aplikacji). Oto podklasyfikowany kod
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(); } } } }
Konfigurowanie filtra intencji pod kątem odbierania połączeń
Gdy usługa SIP otrzymuje nowe połączenie, wysyła intencję z ciągiem tekstowym podanym przez aplikację. W SipDemo ten ciąg działań to android.SipDemo.INCOMING_CALL
.
Ten fragment kodu z SipDemo pokazuje, jak jest tworzony obiekt SipProfile
z oczekującą intencją na podstawie ciągu znaków działania android.SipDemo.INCOMING_CALL
. Obiekt PendingIntent
wykona komunikat, gdy SipProfile
otrzyma połączenie:
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 uruchomi odbiornik (IncomingCallReceiver
). Filtr intencji możesz określić w pliku manifestu aplikacji lub zrobić to w kodzie, tak jak w przykładowej metodzie onCreate()
SipDemo w 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 systemem Android 2.3 lub nowszym. SIP działa przez sieć bezprzewodową, więc musisz przeprowadzić test na rzeczywistym urządzeniu. Testowanie AVD się nie powiedzie.
- Konto SIP. Jest wielu różnych dostawców usług SIP, którzy oferują konta SIP.
- Jeśli chcesz nawiązać połączenie, musisz połączyć je z aktywnym kontem SIP.
Aby przetestować aplikację SIP:
- Na urządzeniu połącz się z siecią bezprzewodową (Ustawienia > Sieci zwykłe i bezprzewodowe > Wi-Fi > Ustawienia Wi-Fi).
- Konfigurowanie urządzenia mobilnego do testowania zgodnie z opisem w sekcji Programowanie na urządzeniu.
- Uruchom aplikację na urządzeniu mobilnym zgodnie z opisem w sekcji Programowanie na urządzeniu.
- Jeśli używasz Android Studio, dane wyjściowe dziennika aplikacji możesz wyświetlić, otwierając konsolę dziennika zdarzeń (Widok > Narzędzia Okna > Dziennik zdarzeń).
- Sprawdź, czy aplikacja jest skonfigurowana tak, aby automatycznie uruchamiała Logcat po uruchomieniu:
- Wybierz Uruchom > Edytuj konfiguracje.
- Wybierz kartę Różne w oknie Konfiguracja uruchamiania/debugowania.
- W sekcji Logcat wybierz Show logcat automatycznie, a potem OK.