Wdrażanie eSIM

Technologia wbudowanej karty SIM (eSIM lub eUICC) umożliwia użytkownikom mobilnym pobranie profilu operatora i aktywację usług operatora bez konieczności posiadania fizycznej karty SIM. Jest to globalna specyfikacja oparta na GSMA, która umożliwia zdalne udostępnianie kart SIM (RSP) dowolnego urządzenia mobilnego. Począwszy od Androida 9, platforma Android zapewnia standardowe interfejsy API umożliwiające dostęp do karty eSIM i zarządzanie profilami subskrypcji na karcie eSIM. Te interfejsy API eUICC umożliwiają stronom trzecim tworzenie własnych aplikacji operatorów i asystentów profili lokalnych (LPA) na urządzeniach z systemem Android obsługujących eSIM.

LPA to samodzielna aplikacja systemowa, którą należy uwzględnić w obrazie kompilacji systemu Android. Zarządzanie profilami na eSIM jest zazwyczaj realizowane przez LPA, ponieważ służy jako pomost pomiędzy SM-DP+ (zdalna usługa przygotowująca, przechowująca i dostarczająca pakiety profili do urządzeń) a chipem eUICC. Pakiet LPA APK może opcjonalnie zawierać komponent interfejsu użytkownika, zwany interfejsem użytkownika LPA lub LUI, zapewniający użytkownikowi końcowemu centralne miejsce do zarządzania wszystkimi osadzonymi profilami subskrypcji. Struktura Androida automatycznie wykrywa najlepszy dostępny LPA i łączy się z nim, a następnie kieruje wszystkie operacje eUICC przez instancję LPA.

Uproszczona architektura zdalnego udostępniania kart SIM (RSP).

Rysunek 1. Uproszczona architektura RSP

Operatorzy sieci komórkowych zainteresowani utworzeniem aplikacji dla operatora powinni przyjrzeć się interfejsom API w EuiccManager , który zapewnia operacje zarządzania profilami wysokiego poziomu, takie jak downloadSubscription() , switchToSubscription() i deleteSubscription() .

Jeśli jesteś producentem OEM urządzenia i chcesz stworzyć własną aplikację systemową LPA, musisz rozszerzyć EuiccService na platformę Android, aby móc łączyć się z usługami LPA. Dodatkowo warto skorzystać z API w EuiccCardManager , które udostępniają funkcje ES10x w oparciu o GSMA RSP v2.0. Funkcje te służą do wydawania poleceń chipowi eUICC, takich jak prepareDownload() , loadBoundProfilePackage() , retrieveNotificationList() i resetMemory() .

Interfejsy API w EuiccManager wymagają do działania prawidłowo zaimplementowanej aplikacji LPA, a obiekt wywołujący interfejsy API EuiccCardManager musi być LPA. Jest to wymuszane przez platformę Android.

Urządzenia z systemem Android 10 lub nowszym obsługują urządzenia z wieloma kartami eSIM. Aby uzyskać więcej informacji, zobacz Obsługa wielu kart eSIM .

Tworzenie aplikacji operatora

Interfejsy API eUICC w systemie Android 9 umożliwiają operatorom sieci komórkowych tworzenie aplikacji marki operatora do bezpośredniego zarządzania swoimi profilami. Obejmuje to pobieranie i usuwanie profili subskrypcji należących do operatora, a także przełączanie się na profil należący do operatora.

Menedżer Euicc

EuiccManager to główny punkt wejścia aplikacji do interakcji z LPA. Obejmuje to aplikacje operatora, które pobierają, usuwają i przełączają się na subskrypcje należące do operatora. Obejmuje to również aplikację systemową LUI, która zapewnia centralną lokalizację/interfejs użytkownika do zarządzania wszystkimi osadzonymi subskrypcjami i może być aplikacją odrębną od tej, która zapewnia EuiccService .

Aby korzystać z publicznych interfejsów API, aplikacja operatora musi najpierw uzyskać instancję EuiccManager za pośrednictwem Context#getSystemService :

EuiccManager mgr = (EuiccManager) context.getSystemService(Context.EUICC_SERVICE);

Przed wykonaniem jakichkolwiek operacji eSIM należy sprawdzić, czy urządzenie obsługuje kartę eSIM. EuiccManager#isEnabled() zazwyczaj zwraca true , jeśli zdefiniowano funkcję android.hardware.telephony.euicc i obecny jest pakiet LPA.

if (mgr == null || !mgr.isEnabled()) {
    return;
}

Aby uzyskać informacje o sprzęcie eUICC i wersji systemu operacyjnego eSIM:

EuiccInfo info = mgr.getEuiccInfo();
String osVer = info.getOsVersion();

Wiele interfejsów API, takich jak downloadSubscription() i switchToSubscription() , korzysta z wywołań zwrotnych PendingIntent , ponieważ ich wykonanie może zająć kilka sekund lub nawet minut. PendingIntent jest wysyłany z kodem wyniku w przestrzeni EuiccManager#EMBEDDED_SUBSCRIPTION_RESULT_ , która dostarcza zdefiniowane przez platformę kody błędów, a także dowolny szczegółowy kod wyniku propagowany z LPA jako EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE , umożliwiając aplikacji operatora śledzenie na potrzeby rejestrowania/debugowania. Wywołanie zwrotne PendingIntent musi mieć wartość BroadcastReceiver .

Aby pobrać daną subskrypcję do pobrania (utworzoną z kodu aktywacyjnego lub kodu QR):

// Register receiver.
static final String ACTION_DOWNLOAD_SUBSCRIPTION = "download_subscription";
static final String LPA_DECLARED_PERMISSION
    = "com.your.company.lpa.permission.BROADCAST";
BroadcastReceiver receiver =
        new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                if (!action.equals(intent.getAction())) {
                    return;
                }
                resultCode = getResultCode();
                detailedCode = intent.getIntExtra(
                    EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE,
                    0 /* defaultValue*/);

                // If the result code is a resolvable error, call startResolutionActivity
                if (resultCode == EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR) {
                    PendingIntent callbackIntent = PendingIntent.getBroadcast(
                        getContext(), 0 /* requestCode */, intent,
                        PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
                    mgr.startResolutionActivity(
                        activity,
                        0 /* requestCode */,
                        intent,
                        callbackIntent);
                }

                resultIntent = intent;
            }
        };
context.registerReceiver(receiver,
        new IntentFilter(ACTION_DOWNLOAD_SUBSCRIPTION),
        LPA_DECLARED_PERMISSION /* broadcastPermission*/,
        null /* handler */);

// Download subscription asynchronously.
DownloadableSubscription sub = DownloadableSubscription
        .forActivationCode(code /* encodedActivationCode*/);
Intent intent = new Intent(action).setPackage(context.getPackageName());
PendingIntent callbackIntent = PendingIntent.getBroadcast(
        getContext(), 0 /* requestCode */, intent,
        PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
mgr.downloadSubscription(sub, true /* switchAfterDownload */,
        callbackIntent);

Zdefiniuj i użyj uprawnień w AndroidManifest.xml :

    <permission android:protectionLevel="signature" android:name="com.your.company.lpa.permission.BROADCAST" />
    <uses-permission android:name="com.your.company.lpa.permission.BROADCAST"/>

Aby przejść na subskrypcję, biorąc pod uwagę identyfikator subskrypcji:

// Register receiver.
static final String ACTION_SWITCH_TO_SUBSCRIPTION = "switch_to_subscription";
static final String LPA_DECLARED_PERMISSION
    = "com.your.company.lpa.permission.BROADCAST";
BroadcastReceiver receiver =
        new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                if (!action.equals(intent.getAction())) {
                    return;
                }
                resultCode = getResultCode();
                detailedCode = intent.getIntExtra(
                    EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE,
                    0 /* defaultValue*/);
                resultIntent = intent;
            }
        };
context.registerReceiver(receiver,
        new IntentFilter(ACTION_SWITCH_TO_SUBSCRIPTION),
        LPA_DECLARED_PERMISSION /* broadcastPermission*/,
        null /* handler */);

// Switch to a subscription asynchronously.
Intent intent = new Intent(action).setPackage(context.getPackageName());
PendingIntent callbackIntent = PendingIntent.getBroadcast(
        getContext(), 0 /* requestCode */, intent,
        PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
mgr.switchToSubscription(1 /* subscriptionId */, callbackIntent);

Pełną listę interfejsów API EuiccManager i przykładów kodu można znaleźć w artykule Interfejsy API eUICC .

Rozwiązywalne błędy

W niektórych przypadkach system nie jest w stanie ukończyć operacji eSIM, ale użytkownik może rozwiązać błąd. Na przykład downloadSubscription może zakończyć się niepowodzeniem, jeśli metadane profilu wskazują, że wymagany jest kod potwierdzający operatora . Lub switchToSubscription może zakończyć się niepowodzeniem, jeśli aplikacja operatora ma uprawnienia operatora do profilu docelowego (to znaczy, że operator jest właścicielem profilu), ale nie ma uprawnień operatora do aktualnie włączonego profilu, w związku z czym wymagana jest zgoda użytkownika.

W takich przypadkach wywoływane jest wywołanie zwrotne osoby wywołującej z EuiccManager#EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR . Intent wywołania zwrotnego zawiera wewnętrzne dodatki, dzięki czemu gdy osoba wywołująca przekaże ją do EuiccManager#startResolutionActivity , można zażądać rozwiązania za pośrednictwem LUI. Na przykład ponownie używając kodu potwierdzającego, EuiccManager#startResolutionActivity uruchamia ekran LUI, który umożliwia użytkownikowi wprowadzenie kodu potwierdzającego; po wpisaniu kodu pobieranie zostaje wznowione. Takie podejście zapewnia aplikacji operatora pełną kontrolę nad wyświetlaniem interfejsu użytkownika, ale zapewnia LPA/LUI rozszerzalną metodę dodawania nowej obsługi problemów, które użytkownik może naprawić w przyszłości, bez konieczności zmiany aplikacji klienckich.

Android 9 definiuje w EuiccService następujące możliwe do rozwiązania błędy, które powinien obsługiwać LUI:

/**
 * Alert the user that this action will result in an active SIM being
 * deactivated. To implement the LUI triggered by the system, you need to define
 * this in AndroidManifest.xml.
 */
public static final String ACTION_RESOLVE_DEACTIVATE_SIM =
        "android.service.euicc.action.RESOLVE_DEACTIVATE_SIM";
/**
 * Alert the user about a download/switch being done for an app that doesn't
 * currently have carrier privileges.
 */
public static final String ACTION_RESOLVE_NO_PRIVILEGES =
        "android.service.euicc.action.RESOLVE_NO_PRIVILEGES";

/** Ask the user to resolve all the resolvable errors. */
public static final String ACTION_RESOLVE_RESOLVABLE_ERRORS =
        "android.service.euicc.action.RESOLVE_RESOLVABLE_ERRORS";

Przywileje przewoźnika

Jeśli jesteś operatorem tworzącym własną aplikację operatora, która wywołuje EuiccManager w celu pobrania profili na urządzenie, Twój profil powinien zawierać w metadanych zasady uprawnień operatora odpowiadające aplikacji Twojego operatora. Dzieje się tak, ponieważ profile subskrypcji należące do różnych operatorów mogą współistnieć w eUICC urządzenia, a każda aplikacja operatora powinna mieć dostęp wyłącznie do profili będących własnością tego operatora. Na przykład operator A nie powinien mieć możliwości pobrania, włączenia ani wyłączenia profilu należącego do operatora B.

Aby mieć pewność, że profil będzie dostępny tylko dla jego właściciela, system Android korzysta z mechanizmu przyznającego specjalne uprawnienia aplikacji właściciela profilu (czyli aplikacji operatora). Platforma Android ładuje certyfikaty przechowywane w pliku reguł dostępu profilu (ARF) i udziela aplikacjom podpisanym tymi certyfikatami uprawnień do wykonywania wywołań do interfejsów API EuiccManager . Proces wysokiego poziomu opisano poniżej:

  1. Operator podpisuje aplikację przewoźnika APK; narzędzie apksigner dołącza certyfikat klucza publicznego do pliku APK.
  2. Operator/SM-DP+ przygotowuje profil i jego metadane, w tym ARF zawierający:

    1. Podpis (SHA-1 lub SHA-256) certyfikatu klucza publicznego aplikacji operatora (wymagany)
    2. Nazwa pakietu aplikacji operatora (zdecydowanie zalecane)
  3. Aplikacja Carrier próbuje wykonać operację eUICC za pośrednictwem interfejsu API EuiccManager .

  4. Platforma Android sprawdza, czy skrót SHA-1 lub SHA-256 certyfikatu aplikacji wywołującej jest zgodny z podpisem certyfikatu uzyskanym z pliku ARF profilu docelowego. Jeśli nazwa pakietu aplikacji operatora jest zawarta w pliku ARF, musi ona również odpowiadać nazwie pakietu aplikacji wywołującej.

  5. Po zweryfikowaniu podpisu i nazwy pakietu (jeśli jest dołączona), aplikacji wywołującej przyznawane są uprawnienia operatora w profilu docelowym.

Ponieważ metadane profilu mogą być dostępne poza samym profilem (aby LPA mogła pobrać metadane profilu z SM-DP+ przed pobraniem profilu lub z ISD-R, gdy profil jest wyłączony), powinny one zawierać te same zasady uprawnień operatora jak w profilu.

System operacyjny eUICC i SM-DP+ muszą obsługiwać zastrzeżony znacznik BF76 w metadanych profilu. Treść znacznika powinna odpowiadać tym samym regułom uprawnień operatora, jakie zwraca aplet reguły dostępu (ARA) zdefiniowany w Uprzywilejach operatora UICC :

RefArDo ::= [PRIVATE 2] SEQUENCE {  -- Tag E2
    refDo [PRIVATE 1] SEQUENCE {  -- Tag E1
        deviceAppIdRefDo [PRIVATE 1] OCTET STRING (SIZE(20|32)),  -- Tag C1
        pkgRefDo [PRIVATE 10] OCTET STRING (SIZE(0..127)) OPTIONAL  -- Tag CA
    },
    arDo [PRIVATE 3] SEQUENCE {  -- Tag E3
        permArDo [PRIVATE 27] OCTET STRING (SIZE(8))  -- Tag DB
    }
}

Aby uzyskać więcej informacji na temat podpisywania aplikacji, zobacz Podpisywanie aplikacji . Aby uzyskać szczegółowe informacje na temat uprawnień przewoźnika, zobacz Uprawnienia przewoźnika UICC .

Tworzenie aplikacji asystenta profilu lokalnego

Producenci urządzeń mogą wdrożyć własnego asystenta profilu lokalnego (LPA), który musi być połączony z interfejsami API Android Euicc. Poniższe sekcje zawierają krótki przegląd tworzenia aplikacji LPA i integracji jej z systemem Android.

Wymagania sprzętowe/modemowe

LPA i system operacyjny eSIM na chipie eUICC muszą obsługiwać co najmniej GSMA RSP (Remote SIM Provisioning) w wersji 2.0 lub 2.2. Powinieneś także zaplanować użycie serwerów SM-DP+ i SM-DS, które mają pasującą wersję RSP. Aby zapoznać się ze szczegółową architekturą RSP, zobacz Specyfikacja architektury RSP GSMA SGP.21 .

Dodatkowo, aby zintegrować się z API eUICC w systemie Android 9, modem urządzenia powinien wysyłać możliwości terminala z zakodowaną obsługą funkcji eUICC (lokalne zarządzanie profilami i pobieranie profili). Musi także wdrożyć następujące metody:

  • IRadio HAL v1.1: setSimPower
  • IRadio HAL v1.2: getIccCardStatus

  • IRadioConfig HAL v1.0: getSimSlotsStatus

  • IRadioConfig AIDL v1.0: getAllowedCarriers

    Google LPA musi znać stan blokady operatora, aby móc zezwolić na pobranie lub transfer karty eSIM tylko dla dozwolonego operatora. W przeciwnym razie użytkownicy mogą pobrać i przenieść kartę SIM, a później zorientują się, że urządzenie jest zablokowane przez innego operatora.

    • Dostawcy lub producenci OEM muszą wdrożyć interfejs API IRadioSim.getAllowedCarriers()HAL.

    • Dostawca RIL/Modem powinien podać status blokady i identyfikator przewoźnika, na którym urządzenie jest zablokowane, w ramach interfejsu API IRadioSimResponse.getAllowedCarriersResponse()HAL.

Modem powinien rozpoznać kartę eSIM z domyślnym profilem rozruchowym jako prawidłową kartę SIM i pozostawić kartę SIM włączoną.

W przypadku urządzeń z systemem Android 10 należy zdefiniować niewymienną tablicę identyfikatorów gniazd eUICC. Zobacz na przykład arrays.xml .

<resources>
   <!-- Device-specific array of SIM slot indexes which are are embedded eUICCs.
        e.g. If a device has two physical slots with indexes 0, 1, and slot 1 is an
        eUICC, then the value of this array should be:
            <integer-array name="non_removable_euicc_slots">
                <item>1</item>
            </integer-array>
        If a device has three physical slots and slot 1 and 2 are eUICCs, then the value of
        this array should be:
            <integer-array name="non_removable_euicc_slots">
               <item>1</item>
               <item>2</item>
            </integer-array>
        This is used to differentiate between removable eUICCs and built in eUICCs, and should
        be set by OEMs for devices which use eUICCs. -->

   <integer-array name="non_removable_euicc_slots">
       <item>1</item>
   </integer-array>
</resources>

Aby zapoznać się z pełną listą wymagań modemu, zobacz Wymagania modemu dla obsługi eSIM .

Serwis Euicc

LPA składa się z dwóch oddzielnych komponentów (oba mogą być zaimplementowane w tym samym pliku APK): backendu LPA oraz interfejsu użytkownika LPA lub LUI.

Aby zaimplementować backend LPA, musisz rozszerzyć EuiccService i zadeklarować tę usługę w swoim pliku manifestu. Usługa musi wymagać uprawnienia systemowego android.permission.BIND_EUICC_SERVICE , aby mieć pewność, że tylko system może się z nią powiązać. Usługa musi zawierać także filtr intencji z akcją android.service.euicc.EuiccService . Priorytet filtra intencji powinien być ustawiony na wartość różną od zera w przypadku, gdy na urządzeniu znajduje się wiele implementacji. Na przykład:

<service
    android:name=".EuiccServiceImpl"
    android:permission="android.permission.BIND_EUICC_SERVICE">
    <intent-filter android:priority="100">
        <action android:name="android.service.euicc.EuiccService" />
    </intent-filter>
</service>

Wewnętrznie platforma Android określa aktywny LPA i wchodzi z nim w interakcję w razie potrzeby, aby obsługiwać interfejsy API eUICC systemu Android. PackageManager jest pytany o wszystkie aplikacje z uprawnieniem android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS , które określa usługę dla akcji android.service.euicc.EuiccService . Wybrana zostanie usługa o najwyższym priorytecie. Jeśli nie zostanie znaleziona żadna usługa, obsługa LPA jest wyłączona.

Aby zaimplementować LUI, musisz zapewnić działanie dla następujących działań:

  • android.service.euicc.action.MANAGE_EMBEDDED_SUBSCRIPTIONS
  • android.service.euicc.action.PROVISION_EMBEDDED_SUBSCRIPTION

Podobnie jak w przypadku usługi, każda czynność musi wymagać uprawnień systemowych android.permission.BIND_EUICC_SERVICE . Każdy powinien mieć filtr intencji z odpowiednią akcją, kategorią android.service.euicc.category.EUICC_UI i niezerowym priorytetem. Do wyboru implementacji tych działań używana jest podobna logika, jak przy wyborze implementacji EuiccService . Na przykład:

<activity android:name=".MyLuiActivity"
          android:exported="true"
          android:permission="android.permission.BIND_EUICC_SERVICE">
    <intent-filter android:priority="100">
        <action android:name="android.service.euicc.action.MANAGE_EMBEDDED_SUBSCRIPTIONS" />
        <action android:name="android.service.euicc.action.PROVISION_EMBEDDED_SUBSCRIPTION" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.service.euicc.category.EUICC_UI" />
    </intent-filter>
</activity>

Oznacza to, że interfejs użytkownika implementujący te ekrany może pochodzić z innego pliku APK niż ten, który implementuje EuiccService . To, czy mieć jeden plik APK, czy wiele plików APK (na przykład jeden implementujący EuiccService i taki, który zapewnia działania LUI) jest wyborem projektowym.

Menedżer kart Euicc

EuiccCardManager to interfejs do komunikacji z chipem eSIM. Zapewnia funkcje ES10 (opisane w specyfikacji GSMA RSP) i obsługuje polecenia żądania/odpowiedzi niskiego poziomu APDU, a także parsowanie ASN.1. EuiccCardManager to systemowy interfejs API, który może być wywoływany wyłącznie przez aplikacje z uprawnieniami systemowymi.

Aplikacje operatora, LPA i interfejsy API Euicc

Rysunek 2. Zarówno aplikacja operatora, jak i LPA korzystają z interfejsów API Euicc

Interfejsy API operacji profili za pośrednictwem EuiccCardManager wymagają, aby osoba wywołująca była LPA. Jest to wymuszane przez platformę Android. Oznacza to, że obiekt wywołujący musi rozszerzyć EuiccService i zostać zadeklarowany w pliku manifestu, jak opisano w poprzednich sekcjach.

Podobnie jak w przypadku EuiccManager , aby móc korzystać z interfejsów API EuiccCardManager , Twój LPA musi najpierw uzyskać instancję EuiccCardManager poprzez Context#getSystemService :

EuiccCardManager cardMgr = (EuiccCardManager) context.getSystemService(Context.EUICC_CARD_SERVICE);

Następnie, aby uzyskać wszystkie profile w eUICC:

ResultCallback<EuiccProfileInfo[]> callback =
       new ResultCallback<EuiccProfileInfo[]>() {
           @Override
           public void onComplete(int resultCode,
                   EuiccProfileInfo[] result) {
               if (resultCode == EuiccCardManagerReflector.RESULT_OK) {
                   // handle result
               } else {
                   // handle error
               }
           }
       };

cardMgr.requestAllProfiles(eid, AsyncTask.THREAD_POOL_EXECUTOR, callback);

Wewnętrznie EuiccCardManager łączy się z EuiccCardController (który działa w procesie telefonu) poprzez interfejs AIDL, a każda metoda EuiccCardManager otrzymuje wywołanie zwrotne z procesu telefonu poprzez inny, dedykowany interfejs AIDL. Podczas korzystania z interfejsów API EuiccCardManager obiekt wywołujący (LPA) musi udostępnić obiekt Executor , za pośrednictwem którego wywoływane jest wywołanie zwrotne. Ten obiekt Executor może działać w pojedynczym wątku lub w wybranej puli wątków.

Większość interfejsów API EuiccCardManager ma ten sam schemat użycia. Na przykład, aby załadować powiązany pakiet profili do eUICC:

...
cardMgr.loadBoundProfilePackage(eid, boundProfilePackage,
        AsyncTask.THREAD_POOL_EXECUTOR, callback);

Aby przełączyć się na inny profil z danym ICCID:

...
cardMgr.switchToProfile(eid, iccid, true /* refresh */,
        AsyncTask.THREAD_POOL_EXECUTOR, callback);

Aby uzyskać domyślny adres SM-DP+ z układu eUICC:

...
cardMgr.requestDefaultSmdpAddress(eid, AsyncTask.THREAD_POOL_EXECUTOR,
        callback);

Aby pobrać listę powiadomień o danych zdarzeniach powiadomień:

...
cardMgr.listNotifications(eid,
        EuiccNotification.Event.INSTALL
              | EuiccNotification.Event.DELETE /* events */,
        AsyncTask.THREAD_POOL_EXECUTOR, callback);

Aktywacja profilu eSIM za pośrednictwem aplikacji operatora

Na urządzeniach z systemem Android 9 lub nowszym możesz użyć aplikacji operatora, aby aktywować kartę eSIM i pobrać profile. Aplikacja operatora może pobierać profile, dzwoniąc bezpośrednio do downloadSubscription lub przekazując kod aktywacyjny do LPA.

Gdy aplikacja operatora pobiera profil, wywołując funkcję downloadSubscription , wywołanie to wymusza, aby aplikacja mogła zarządzać profilem za pomocą tagu metadanych BF76 , który koduje reguły uprawnień operatora dla profilu. Jeśli profil nie ma znacznika BF76 lub jego znacznik BF76 nie pasuje do podpisu aplikacji operatora wywołującego, pobieranie zostanie odrzucone.

W poniższej sekcji opisano aktywację karty eSIM za pośrednictwem aplikacji operatora przy użyciu kodu aktywacyjnego.

Aktywacja eSIM za pomocą kodu aktywacyjnego

Jeśli do aktywacji profilu eSIM używasz kodu aktywacyjnego, LPA pobiera kod aktywacyjny z aplikacji operatora i pobiera profil. Przepływ ten może zostać zainicjowany przez LPA, a LPA może kontrolować cały przepływ interfejsu użytkownika, co oznacza, że ​​nie jest wyświetlany żaden interfejs aplikacji operatora. To podejście omija sprawdzanie tagu BF76 , a operatorzy sieci nie muszą wdrażać całego procesu aktywacji interfejsu eSIM, w tym pobierania profilu eSIM i obsługi błędów.

Definiowanie usługi udostępniania eUICC operatora

Aplikacja LPA i przewoźnika komunikują się za pośrednictwem dwóch interfejsów AIDL : ICarrierEuiccProvisioningService i IGetActivationCodeCallback . Aplikacja operatora musi implementować interfejs ICarrierEuiccProvisioningService i udostępniać go w swojej deklaracji manifestu . LPA musi powiązać się z ICarrierEuiccProvisioningService i zaimplementować IGetActivationCodeCallback . Aby uzyskać więcej informacji na temat implementowania i udostępniania interfejsu AIDL, zobacz Definiowanie i interfejs AIDL .

Aby zdefiniować interfejsy AIDL, utwórz następujące pliki AIDL dla aplikacji LPA i operatora.

  • ICarrierEuiccProvisioningService.aidl

    package android.service.euicc;
    
    import android.service.euicc.IGetActivationCodeCallback;
    
    oneway interface ICarrierEuiccProvisioningService {
        // The method to get the activation code from the carrier app. The caller needs to pass in
        // the implementation of IGetActivationCodeCallback as the parameter.
        void getActivationCode(in IGetActivationCodeCallback callback);
    
        // The method to get the activation code from the carrier app. The caller needs to pass in
        // the activation code string as the first parameter and the implementation of
        // IGetActivationCodeCallback as the second parameter. This method provides the carrier
        // app the device EID which allows a carrier to pre-bind a profile to the device's EID before
        // the download process begins.
        void getActivationCodeForEid(in String eid, in IGetActivationCodeCallback callback);
    }
    
    
  • IGetActivationCodeCallback.aidl

    package android.service.euicc;
    
    oneway interface IGetActivationCodeCallback {
        // The call back method needs to be called when the carrier app gets the activation
        // code successfully. The caller needs to pass in the activation code string as the
        // parameter.
        void onSuccess(String activationCode);
    
        // The call back method needs to be called when the carrier app failed to get the
        // activation code.
        void onFailure();
    }
    

Przykładowa implementacja LPA

Aby powiązać się z implementacją ICarrierEuiccProvisioningService aplikacji operatora, LPA musi skopiować zarówno ICarrierEuiccProvisioningService.aidl , jak i IGetActivationCodeCallback.aidl do projektu i zaimplementować ServiceConnection .

@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
    mCarrierProvisioningService = ICarrierEuiccProvisioningService.Stub.asInterface(iBinder);
}

Po powiązaniu z implementacją ICarrierEuiccProvisioningService aplikacji operatora, LPA wywołuje funkcję getActivationCode lub getActivationCodeForEid w celu pobrania kodu aktywacyjnego z aplikacji operatora poprzez przekazanie implementacji klasy pośredniczącej IGetActivationCodeCallback .

Różnica między getActivationCode i getActivationCodeForEid polega na tym, że getActivationCodeForEid umożliwia operatorowi wstępne powiązanie profilu z identyfikatorem EID urządzenia przed rozpoczęciem procesu pobierania.

void getActivationCodeFromCarrierApp() {
    IGetActivationCodeCallback.Stub callback =
            new IGetActivationCodeCallback.Stub() {
                @Override
                public void onSuccess(String activationCode) throws RemoteException {
                    // Handle the case LPA success to get activation code from a carrier app.
                }

                @Override
                public void onFailure() throws RemoteException {
                    // Handle the case LPA failed to get activation code from a carrier app.
                }
            };
    
    try {
        mCarrierProvisioningService.getActivationCode(callback);
    } catch (RemoteException e) {
        // Handle Remote Exception
    }
}

Przykładowa implementacja aplikacji operatora

Aby LPA powiązała się z aplikacją operatora, aplikacja operatora musi skopiować pliki ICarrierEuiccProvisioningService.aidl i IGetActivationCodeCallback.aidl do projektu i zadeklarować usługę ICarrierEuiccProvisioningService w pliku AndroidManifest.xml . Usługa musi wymagać uprawnień systemowych android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS , aby mieć pewność, że tylko LPA, aplikacja z uprawnieniami systemowymi, może się z nią powiązać. Usługa musi zawierać także filtr intencji z akcją android.service.euicc.action.BIND_CARRIER_PROVISIONING_SERVICE .

  • AndroidManifest.xml

    <application>
      ...
      <service
          android:name=".CarrierEuiccProvisioningService"
          android:exported="true"
          android:permission="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS">
        <intent-filter>
          <action android:name="android.service.euicc.action.BIND_CARRIER_PROVISIONING_SERVICE"/>
        </intent-filter>
      </service>
      ...
    </application>
    

Aby zaimplementować usługę aplikacji przewoźnika AIDL, utwórz usługę, rozszerz klasę Stub i zaimplementuj metody getActivationCode i getActivationCodeForEid . LPA może następnie wywołać dowolną metodę w celu pobrania kodu aktywacyjnego profilu. Aplikacja operatora powinna odpowiedzieć, wywołując metodę IGetActivationCodeCallback#onSuccess z kodem aktywacyjnym, jeśli kod został pomyślnie pobrany z serwera operatora. W przypadku niepowodzenia aplikacja operatora powinna odpowiedzieć IGetActivationCodeCallback#onFailure .

  • CarrierEuiccProvisioningService.java

    import android.service.euicc.ICarrierEuiccProvisioningService;
    import android.service.euicc.ICarrierEuiccProvisioningService.Stub;
    import android.service.euicc.IGetActivationCodeCallback;
    
    public class CarrierEuiccProvisioningService extends Service {
        private final ICarrierEuiccProvisioningService.Stub binder =
            new Stub() {
              @Override
              public void getActivationCode(IGetActivationCodeCallback callback) throws RemoteException {
                String activationCode = // do whatever work necessary to get an activation code (HTTP requests to carrier server, fetch from storage, etc.)
                callback.onSuccess(activationCode);
              }
    
              @Override
              public void getActivationCodeForEid(String eid, IGetActivationCodeCallback callback) throws RemoteException {
                String activationCode = // do whatever work necessary (HTTP requests, fetch from storage, etc.)
                callback.onSuccess(activationCode);
              }
          }
    }
    

Uruchamianie interfejsu aplikacji operatora w procesie aktywacji LPA

Na urządzeniach z Androidem 11 lub nowszym LPA może uruchomić interfejs aplikacji operatora. Jest to przydatne, ponieważ aplikacja operatora może wymagać od użytkownika dodatkowych informacji przed podaniem kodu aktywacyjnego do LPA. Na przykład przewoźnicy mogą wymagać od użytkowników zalogowania się w celu aktywacji numerów telefonów lub wykonania innych usług przenoszenia.

Oto proces uruchamiania interfejsu użytkownika aplikacji operatora w LPA:

  1. LPA uruchamia proces aktywacji aplikacji operatora, wysyłając intencję android.service.euicc.action.START_CARRIER_ACTIVATION do pakietu aplikacji operatora zawierającego akcję. (Odbiornik aplikacji operatora musi być chroniony w deklaracji manifestu za pomocą android:permission="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" aby uniknąć odbierania intencji z aplikacji innych niż LPA).

    String packageName = // The carrier app's package name
    
    Intent carrierAppIntent =
        new Intent(“android.service.euicc.action.START_CARRIER_ACTIVATION”)
            .setPackage(packageName);
    
    ResolveInfo activity =
        context.getPackageManager().resolveActivity(carrierAppIntent, 0);
    
    carrierAppIntent
        .setClassName(activity.activityInfo.packageName, activity.activityInfo.name);
    
    startActivityForResult(carrierAppIntent, requestCode);
    
  2. Aplikacja operatora działa przy użyciu własnego interfejsu użytkownika. Na przykład logowanie użytkownika lub wysyłanie żądań HTTP do backendu operatora.

  3. Aplikacja operatora odpowiada na LPA, wywołując funkcję setResult(int, Intent) i finish() .

    1. Jeśli aplikacja operatora odpowie komunikatem RESULT_OK , LPA będzie kontynuować proces aktywacji. Jeśli aplikacja operatora ustali, że użytkownik powinien zeskanować kod QR zamiast pozwolić LPA powiązać usługę aplikacji operatora, aplikacja operatora odpowiada na LPA, używając setResult(int, Intent) z RESULT_OK i instancją Intent zawierającą dodatkową wartość logiczną android.telephony.euicc.extra.USE_QR_SCANNER ustawione na true . Następnie LPA sprawdza dodatek i uruchamia skaner QR zamiast wiązać implementację ICarrierEuiccProvisioningService aplikacji operatora.
    2. Jeśli aplikacja operatora ulegnie awarii lub odpowie komunikatem RESULT_CANCELED (jest to domyślny kod odpowiedzi), LPA anuluje proces aktywacji karty eSIM.
    3. Jeśli aplikacja operatora odpowie komunikatem innym niż RESULT_OK lub RESULT_CANCELED , LPA traktuje to jako błąd.

    Ze względów bezpieczeństwa LPA nie powinna bezpośrednio akceptować kodu aktywacyjnego podanego w wyniku, aby mieć pewność, że dzwoniący niebędący LPA nie będą mogli uzyskać kodu aktywacyjnego z aplikacji operatora.

Uruchomienie procesu aktywacji LPA w aplikacji operatora

Począwszy od Androida 11, aplikacje operatorów mogą używać interfejsów API eUICC do uruchamiania LUI w celu aktywacji eSIM. Ta metoda umożliwia wyświetlenie interfejsu użytkownika procesu aktywacji eSIM w LPA w celu aktywowania profilu eSIM. Następnie LPA wysyła rozgłoszenie po zakończeniu aktywacji profilu eSIM.

  1. LPA musi zadeklarować działanie obejmujące filtr intencji za pomocą akcji android.service.euicc.action.START_EUICC_ACTIVATION . Priorytet filtra intencji powinien być ustawiony na wartość różną od zera w przypadku, gdy na urządzeniu znajduje się wiele implementacji. Na przykład:

    <application>
      ...
    <activity
        android:name=".CarrierAppInitActivity"
        android:exported="true">
    
        <intent-filter android:priority="100">
            <action android:name="android.service.euicc.action.START_EUICC_ACTIVATION" />
        </intent-filter>
    </activity>
      ...
    </application>
    
  2. Aplikacja operatora działa przy użyciu własnego interfejsu użytkownika. Na przykład logowanie użytkownika lub wysyłanie żądań HTTP do backendu operatora.

  3. W tym momencie aplikacja operatora musi być gotowa do dostarczenia kodu aktywacyjnego poprzez implementację ICarrierEuiccProvisioningService . Aplikacja operatora uruchamia LPA, wywołując startActivityForResult(Intent, int) z akcją android.telephony.euicc.action.START_EUICC_ACTIVATION . LPA sprawdza również dodatkową wartość logiczną android.telephony.euicc.extra.USE_QR_SCANNER . Jeśli wartość ma wartość true , LPA uruchamia skaner QR, aby umożliwić użytkownikowi zeskanowanie kodu QR profilu.

  4. Po stronie LPA LPA łączy się z implementacją ICarrierEuiccProvisioningService aplikacji operatora w celu pobrania kodu aktywacyjnego i odpowiedniego profilu. Podczas pobierania LPA wyświetla wszystkie niezbędne elementy interfejsu użytkownika, takie jak ekran ładowania.

  5. Po zakończeniu procesu aktywacji LPA LPA odpowiada aplikacji operatora za pomocą kodu wyniku, który aplikacja operatora obsługuje w onActivityResult(int, int, Intent) .

    1. Jeśli LPA pomyślnie pobierze nowy profil eSIM, odpowie komunikatem RESULT_OK .
    2. Jeśli użytkownik anuluje aktywację profilu eSIM w LPA, odpowie komunikatem RESULT_CANCELED .
    3. Jeśli LPA odpowie czymś innym niż RESULT_OK lub RESULT_CANCELED , aplikacja operatora traktuje to jako błąd.

    Ze względów bezpieczeństwa LPA nie akceptuje kodu aktywacyjnego bezpośrednio w dostarczonej intencji, aby mieć pewność, że osoby dzwoniące inne niż LPA nie będą mogły uzyskać kodu aktywacyjnego z aplikacji operatora.

Obsługa wielu eSIM

W przypadku urządzeń z systemem Android 10 lub nowszym klasa EuiccManager obsługuje urządzenia z wieloma kartami eSIM. Urządzenia z pojedynczym eSIM, które aktualizują się do Androida 10, nie wymagają żadnych modyfikacji implementacji LPA, ponieważ platforma automatycznie kojarzy instancję EuiccManager z domyślnym eUICC. Domyślny eUICC jest określany przez platformę dla urządzeń z radiem HAL w wersji 1.2 lub wyższej oraz przez LPA dla urządzeń z radiem HAL w wersji niższej niż 1.2.

Wymagania

Aby obsługiwać wiele kart eSIM, urządzenie musi być wyposażone w więcej niż jedno urządzenie eUICC, którym może być wbudowany eUICC lub fizyczne gniazdo karty SIM, do którego można włożyć wymienne karty eUICC.

Do obsługi wielu kart eSIM wymagane jest radio HAL w wersji 1.2 lub nowszej. Zalecane jest Radio HAL w wersji 1.4 i RadioConfig HAL w wersji 1.2.

Realizacja

Aby obsługiwać wiele kart eSIM (w tym wymienne eUICC lub programowalne karty SIM), LPA musi wdrożyć usługę EuiccService , która odbiera identyfikator gniazda odpowiadający identyfikatorowi karty dostarczonemu przez dzwoniącego.

Zasób non_removable_euicc_slots określony w arrays.xml to tablica liczb całkowitych reprezentujących identyfikatory gniazd wbudowanych eUICC urządzenia. Należy określić ten zasób, aby umożliwić platformie określenie, czy wstawiony eUICC jest wymienny, czy nie.

Aplikacja operatora na urządzenie z wieloma kartami eSIM

Tworząc aplikację operatora dla urządzenia z wieloma kartami eSIM, użyj metody createForCardId w EuiccManager , aby utworzyć obiekt EuiccManager przypięty do danego identyfikatora karty. Identyfikator karty to wartość całkowita, która jednoznacznie identyfikuje UICC lub eUICC na urządzeniu.

Aby uzyskać identyfikator karty dla domyślnego eUICC urządzenia, użyj metody getCardIdForDefaultEuicc w TelephonyManager . Ta metoda zwraca UNSUPPORTED_CARD_ID , jeśli wersja radia HAL jest niższa niż 1.2 i zwraca UNINITIALIZED_CARD_ID , jeśli urządzenie nie odczytało eUICC.

Identyfikatory kart można także uzyskać z getUiccCardsInfo i getUiccSlotsInfo (systemowe API) w TelephonyManager oraz getCardId w SubscriptionInfo .

Kiedy utworzono instancję obiektu EuiccManager z określonym identyfikatorem karty, wszystkie operacje są kierowane do eUICC z tym identyfikatorem karty. Jeśli eUICC stanie się nieosiągalny (na przykład po wyłączeniu lub usunięciu), EuiccManager przestanie działać.

Aby utworzyć aplikację operatora, możesz użyć poniższych przykładów kodu.

Przykład 1: Uzyskaj aktywną subskrypcję i utwórz instancję EuiccManager

// Get the active subscription and instantiate an EuiccManager for the eUICC which holds
// that subscription
SubscriptionManager subMan = (SubscriptionManager)
        mContext.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
int cardId = subMan.getActiveSubscriptionInfo().getCardId();
EuiccManager euiccMan = (EuiccManager) mContext.getSystemService(Context.EUICC_SERVICE)
            .createForCardId(cardId);

Przykład 2: Iteracja po UICC i tworzenie instancji EuiccManager dla wymiennego eUICC

// On a device with a built-in eUICC and a removable eUICC, iterate through the UICC cards
// to instantiate an EuiccManager associated with a removable eUICC
TelephonyManager telMan = (TelephonyManager)
        mContext.getSystemService(Context.TELEPHONY_SERVICE);
List<UiccCardInfo> infos = telMan.getUiccCardsInfo();
int removableCardId = -1; // valid cardIds are 0 or greater
for (UiccCardInfo info : infos) {
    if (info.isRemovable()) {
        removableCardId = info.getCardId();
        break;
    }
}
if (removableCardId != -1) {
    EuiccManager euiccMan = (EuiccManager) mContext.getSystemService(Context.EUICC_SERVICE)
            .createForCardId(removableCardId);
}

Walidacja

AOSP nie jest wyposażony w implementację LPA i nie oczekuje się, że LPA będzie dostępny we wszystkich wersjach Androida (nie każdy telefon obsługuje eSIM). Z tego powodu nie ma kompleksowych przypadków testowych CTS. Jednak w AOSP dostępne są podstawowe przypadki testowe, które zapewniają, że ujawnione interfejsy API eUICC są prawidłowe w kompilacjach Androida.

Powinieneś upewnić się, że kompilacje przeszły następujące przypadki testowe CTS (dla publicznych interfejsów API): /platform/cts/tests/tests/telephony/current/src/android/telephony/euicc/cts .

Przewoźnicy wdrażający aplikację powinni przejść normalne wewnętrzne cykle zapewniania jakości, aby upewnić się, że wszystkie wdrożone funkcje działają zgodnie z oczekiwaniami. Aplikacja operatora powinna co najmniej umożliwiać wyświetlenie listy wszystkich profili subskrypcji należących do tego samego operatora, pobranie i zainstalowanie profilu, aktywację usługi w profilu, przełączanie między profilami i usuwanie profili.

Jeśli tworzysz własne LPA, powinieneś przejść znacznie bardziej rygorystyczne testy. Powinieneś współpracować z dostawcą modemu, dostawcą układu eUICC lub systemu operacyjnego eSIM, dostawcami SM-DP+ i operatorami, aby rozwiązać problemy i zapewnić interoperacyjność twojego LPA w architekturze RSP. Duża ilość testów ręcznych jest nieunikniona. Aby uzyskać najlepszy zasięg testów, należy postępować zgodnie z planem testów GSMA SGP.23 RSP .