Implementazione dell'eSIM

La tecnologia SIM incorporata (eSIM o eUICC) consente agli utenti mobili di scaricare il profilo di un operatore e attivare il servizio di un operatore senza disporre di una scheda SIM fisica. Si tratta di una specifica globale guidata dal GSMA che consente il provisioning remoto della SIM (RSP) di qualsiasi dispositivo mobile. A partire da Android 9, il framework Android fornisce API standard per l'accesso all'eSIM e la gestione dei profili di abbonamento sull'eSIM. Queste API eUICC consentono a terze parti di sviluppare le proprie app dell'operatore e assistenti di profilo locale (LPA) su dispositivi Android abilitati per eSIM.

LPA è un'app di sistema autonoma che deve essere inclusa nell'immagine della build Android. La gestione dei profili sull'eSIM viene generalmente eseguita dall'LPA, poiché funge da ponte tra SM-DP+ (servizio remoto che prepara, archivia e fornisce pacchetti di profili ai dispositivi) e il chip eUICC. L'APK LPA può facoltativamente includere un componente dell'interfaccia utente, denominato LPA UI o LUI, per fornire all'utente finale una posizione centrale in cui gestire tutti i profili di abbonamento incorporati. Il framework Android rileva e si connette automaticamente alla migliore LPA disponibile e instrada tutte le operazioni eUICC attraverso un'istanza LPA.

Architettura semplificata di provisioning SIM remoto (RSP).

Figura 1. Architettura RSP semplificata

Gli operatori di rete mobile interessati a creare un'app per l'operatore dovrebbero esaminare le API in EuiccManager , che fornisce operazioni di gestione del profilo di alto livello come downloadSubscription() , switchToSubscription() e deleteSubscription() .

Se sei un OEM di dispositivi interessato a creare la tua app di sistema LPA, devi estendere EuiccService affinché il framework Android si connetta ai tuoi servizi LPA. Inoltre, dovresti utilizzare le API in EuiccCardManager , che forniscono funzioni ES10x basate su GSMA RSP v2.0. Queste funzioni vengono utilizzate per inviare comandi al chip eUICC, come prepareDownload() , loadBoundProfilePackage() , retrieveNotificationList() e resetMemory() .

Le API in EuiccManager richiedono un'app LPA correttamente implementata per funzionare e il chiamante delle API EuiccCardManager deve essere un LPA. Ciò è applicato dal framework Android.

I dispositivi con Android 10 o versioni successive possono supportare dispositivi con più eSIM. Per ulteriori informazioni, consulta Supporto di più eSIM .

Creare un'app per l'operatore

Le API eUICC in Android 9 consentono agli operatori di rete mobile di creare app con il marchio dell'operatore per gestire direttamente i propri profili. Ciò include il download e l'eliminazione dei profili di abbonamento di proprietà dell'operatore, nonché il passaggio a un profilo di proprietà dell'operatore.

EuiccManager

EuiccManager è il punto di ingresso principale per consentire alle app di interagire con la LPA. Sono incluse le app dell'operatore che scaricano, eliminano e passano ad abbonamenti di proprietà dell'operatore. Ciò include anche l'app di sistema LUI, che fornisce una posizione/interfaccia utente centrale per la gestione di tutti gli abbonamenti incorporati e può essere un'app separata da quella che fornisce EuiccService .

Per utilizzare le API pubbliche, un'app dell'operatore deve prima ottenere l'istanza di EuiccManager tramite Context#getSystemService :

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

Dovresti verificare se l'eSIM è supportata sul dispositivo prima di eseguire qualsiasi operazione eSIM. EuiccManager#isEnabled() generalmente restituisce true se la funzionalità android.hardware.telephony.euicc è definita ed è presente un pacchetto LPA.

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

Per ottenere informazioni sull'hardware eUICC e sulla versione del sistema operativo eSIM:

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

Molte API, come downloadSubscription() e switchToSubscription() , utilizzano callback PendingIntent poiché potrebbero richiedere secondi o addirittura minuti per essere completate. PendingIntent viene inviato con un codice risultato nello spazio EuiccManager#EMBEDDED_SUBSCRIPTION_RESULT_ , che fornisce codici di errore definiti dal framework, nonché un codice risultato dettagliato arbitrario propagato dall'LPA come EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE , consentendo all'app dell'operatore di tenere traccia a scopo di registrazione/debug. Il callback PendingIntent deve essere BroadcastReceiver .

Per scaricare un determinato abbonamento scaricabile (creato da un codice di attivazione o da un codice 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);

Definisci e utilizza l'autorizzazione in AndroidManifest.xml :

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

Per passare a un abbonamento in base all'ID abbonamento:

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

Per un elenco completo delle API EuiccManager e degli esempi di codice, consulta API eUICC .

Errori risolvibili

Esistono alcuni casi in cui il sistema non è in grado di completare l'operazione eSIM ma l'errore può essere risolto dall'utente. Ad esempio, downloadSubscription potrebbe non riuscire se i metadati del profilo indicano che è richiesto un codice di conferma dell'operatore . Oppure switchToSubscription potrebbe non riuscire se l'app dell'operatore dispone dei privilegi dell'operatore sul profilo di destinazione (ovvero, l'operatore possiede il profilo) ma non dispone dei privilegi dell'operatore sul profilo attualmente abilitato e quindi è richiesto il consenso dell'utente.

In questi casi, la richiamata del chiamante viene chiamata con EuiccManager#EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR . L' Intent di callback contiene extra interni in modo tale che quando il chiamante lo passa a EuiccManager#startResolutionActivity , la risoluzione può essere richiesta tramite la LUI. Utilizzando nuovamente il codice di conferma, ad esempio, EuiccManager#startResolutionActivity attiva una schermata LUI che consente all'utente di inserire un codice di conferma; dopo aver inserito il codice, l'operazione di download riprende. Questo approccio fornisce all'app dell'operatore il controllo completo su quando viene visualizzata l'interfaccia utente, ma offre a LPA/LUI un metodo estendibile per aggiungere in futuro una nuova gestione dei problemi ripristinabili dall'utente senza che sia necessario modificare le app client.

Android 9 definisce questi errori risolvibili in EuiccService , che la LUI dovrebbe gestire:

/**
 * 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";

Privilegi del vettore

Se sei un operatore che sviluppa la propria app dell'operatore che chiama EuiccManager per scaricare i profili su un dispositivo, il tuo profilo deve includere nei metadati le regole sui privilegi dell'operatore corrispondenti all'app dell'operatore. Questo perché i profili di abbonamento appartenenti a diversi operatori possono coesistere nell'eUICC di un dispositivo e a ciascuna app dell'operatore dovrebbe essere consentito accedere solo ai profili di proprietà di tale operatore. Ad esempio, l'operatore A non dovrebbe essere in grado di scaricare, abilitare o disabilitare un profilo di proprietà dell'operatore B.

Per garantire che un profilo sia accessibile solo al suo proprietario, Android utilizza un meccanismo per concedere privilegi speciali all'app del proprietario del profilo (ovvero, all'app dell'operatore). La piattaforma Android carica i certificati archiviati nel file delle regole di accesso (ARF) del profilo e concede l'autorizzazione alle app firmate da questi certificati per effettuare chiamate alle API EuiccManager . Il processo di alto livello è descritto di seguito:

  1. L'operatore firma l'APK dell'app dell'operatore; lo strumento apksigner allega il certificato di chiave pubblica all'APK.
  2. L'Operatore/SM-DP+ prepara un profilo e i relativi metadati, che includono un ARF che contiene:

    1. Firma (SHA-1 o SHA-256) del certificato di chiave pubblica dell'app dell'operatore (obbligatorio)
    2. Nome del pacchetto dell'app dell'operatore (fortemente consigliato)
  3. L'app dell'operatore tenta di eseguire un'operazione eUICC tramite l'API EuiccManager .

  4. La piattaforma Android verifica che l'hash SHA-1 o SHA-256 del certificato dell'app chiamante corrisponda alla firma del certificato ottenuto dall'ARF del profilo di destinazione. Se il nome del pacchetto dell'app dell'operatore è incluso nell'ARF, deve corrispondere anche al nome del pacchetto dell'app del chiamante.

  5. Dopo aver verificato la firma e il nome del pacchetto (se incluso), il privilegio dell'operatore viene concesso all'app chiamante sul profilo di destinazione.

Poiché i metadati del profilo possono essere disponibili all'esterno del profilo stesso (in modo che LPA possa recuperare i metadati del profilo da SM-DP+ prima che il profilo venga scaricato o da ISD-R quando il profilo è disabilitato), dovrebbe contenere le stesse regole di privilegio dell'operatore come nel profilo.

Il sistema operativo eUICC e SM-DP+ devono supportare un tag proprietario BF76 nei metadati del profilo. Il contenuto del tag deve corrispondere alle stesse regole di privilegio dell'operatore restituite dall'applet delle regole di accesso (ARA) definite in Privilegi dell'operatore 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
    }
}

Per ulteriori dettagli sulla firma dell'app, consulta Firmare la tua app . Per i dettagli sui privilegi dell'operatore, consulta Privilegi dell'operatore UICC .

Creare un'app per l'assistente del profilo locale

I produttori di dispositivi possono implementare il proprio assistente profilo locale (LPA), che deve essere collegato alle API Android Euicc. Le sezioni seguenti forniscono una breve panoramica sulla creazione di un'app LPA e sulla sua integrazione con il sistema Android.

Requisiti hardware/modem

L'LPA e il sistema operativo eSIM sul chip eUICC devono supportare almeno GSMA RSP (Remote SIM Provisioning) v2.0 o v2.2. Dovresti anche pianificare l'utilizzo dei server SM-DP+ e SM-DS che dispongono di una versione RSP corrispondente. Per informazioni dettagliate sull'architettura RSP, consultare la specifica dell'architettura RSP GSMA SGP.21 .

Inoltre, per integrarsi con le API eUICC in Android 9, il modem del dispositivo deve inviare funzionalità del terminale con il supporto per le funzionalità eUICC codificate (gestione del profilo locale e download del profilo). È inoltre necessario implementare i seguenti metodi:

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

  • IRadioConfig HAL v1.0: getSimSlotsStatus

  • IRadioConfig AIDL v1.0: getAllowedCarriers

    L'LPA di Google deve conoscere lo stato di blocco dell'operatore in modo da poter consentire il download o il trasferimento di eSIM solo per l'operatore consentito. In caso contrario, gli utenti potrebbero finire per scaricare e trasferire una SIM e in seguito rendersi conto che il dispositivo è bloccato da un operatore diverso.

    • I fornitori o gli OEM devono implementare l'API HAL IRadioSim.getAllowedCarriers().

    • Il RIL/modem del fornitore dovrà compilare lo stato di blocco e l'ID dell'operatore a cui è bloccato il dispositivo come parte dell'API HAL IRadioSimResponse.getAllowedCarriersResponse().

Il modem dovrebbe riconoscere l'eSIM con il profilo di avvio predefinito abilitato come SIM valida e mantenere la SIM accesa.

Per i dispositivi che eseguono Android 10, è necessario definire un array di ID slot eUICC non rimovibile. Ad esempio, vedere 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>

Per un elenco completo dei requisiti del modem, consulta Requisiti del modem per il supporto eSIM .

EuiccService

Una LPA è costituita da due componenti separati (possono essere entrambi implementati nello stesso APK): il backend LPA e l'interfaccia utente o LUI LPA.

Per implementare il backend LPA, devi estendere EuiccService e dichiarare questo servizio nel tuo file manifest. Il servizio deve richiedere l'autorizzazione di sistema android.permission.BIND_EUICC_SERVICE per garantire che solo il sistema possa collegarsi ad esso. Il servizio deve includere anche un filtro intent con l'azione android.service.euicc.EuiccService . La priorità del filtro intent deve essere impostata su un valore diverso da zero nel caso in cui sul dispositivo siano presenti più implementazioni. Per esempio:

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

Internamente, il framework Android determina l'LPA attivo e interagisce con esso secondo necessità per supportare le API eUICC Android. Viene eseguita una query PackageManager per tutte le app con l'autorizzazione android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS , che specifica un servizio per l'azione android.service.euicc.EuiccService . Viene selezionato il servizio con la priorità più alta. Se non viene trovato alcun servizio, il supporto LPA è disabilitato.

Per implementare la LUI, è necessario fornire un'attività per le seguenti azioni:

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

Come per il servizio, ogni attività deve richiedere l'autorizzazione di sistema android.permission.BIND_EUICC_SERVICE . Ognuno dovrebbe avere un filtro di intenti con l'azione appropriata, la categoria android.service.euicc.category.EUICC_UI e una priorità diversa da zero. Per selezionare le implementazioni per queste attività viene utilizzata una logica simile a quella utilizzata per selezionare l'implementazione di EuiccService . Per esempio:

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

Ciò implica che l'interfaccia utente che implementa queste schermate può provenire da un APK diverso da quello che implementa EuiccService . La scelta di avere un singolo APK o più APK (ad esempio, uno che implementa EuiccService e uno che fornisce attività LUI) è una scelta di progettazione.

EuiccCardManager

EuiccCardManager è l'interfaccia per comunicare con il chip eSIM. Fornisce funzioni ES10 (come descritto nelle specifiche GSMA RSP) e gestisce i comandi di richiesta/risposta APDU di basso livello nonché l'analisi ASN.1. EuiccCardManager è un'API di sistema e può essere richiamata solo da app con privilegi di sistema.

App dell'operatore, LPA e API Euicc

Figura 2. Sia l'app dell'operatore che l'LPA utilizzano le API Euicc

Le API per l'operazione del profilo tramite EuiccCardManager richiedono che il chiamante sia un LPA. Ciò è applicato dal framework Android. Ciò significa che il chiamante deve estendere EuiccService ed essere dichiarato nel file manifest, come descritto nelle sezioni precedenti.

Similmente a EuiccManager , per utilizzare le API EuiccCardManager , la tua LPA deve prima ottenere l'istanza di EuiccCardManager tramite Context#getSystemService :

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

Quindi, per ottenere tutti i profili sull'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);

Internamente, EuiccCardManager si collega a EuiccCardController (che viene eseguito nel processo telefonico) tramite un'interfaccia AIDL e ciascun metodo EuiccCardManager riceve la richiamata dal processo telefonico tramite un'interfaccia AIDL diversa e dedicata. Quando si utilizzano le API EuiccCardManager , il chiamante (LPA) deve fornire un oggetto Executor attraverso il quale viene richiamato il callback. Questo oggetto Executor può essere eseguito su un singolo thread o su un pool di thread di tua scelta.

La maggior parte delle API EuiccCardManager hanno lo stesso modello di utilizzo. Ad esempio, per caricare un pacchetto di profili associati nell'eUICC:

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

Per passare a un profilo diverso con un determinato ICCID:

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

Per ottenere l'indirizzo SM-DP+ predefinito dal chip eUICC:

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

Per recuperare un elenco di notifiche degli eventi di notifica specificati:

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

Attivazione di un profilo eSIM tramite l'app dell'operatore

Sui dispositivi con Android 9 o versioni successive, puoi utilizzare l'app dell'operatore per attivare l'eSIM e scaricare i profili. L'app dell'operatore può scaricare i profili chiamando direttamente downloadSubscription o fornendo un codice di attivazione alla LPA.

Quando un'app dell'operatore scarica un profilo chiamando downloadSubscription , la chiamata impone che l'app possa gestire il profilo tramite un tag di metadati BF76 che codifica le regole dei privilegi dell'operatore per il profilo. Se un profilo non dispone di un tag BF76 o se il relativo tag BF76 non corrisponde alla firma dell'app dell'operatore chiamante, il download viene rifiutato.

La sezione seguente descrive l'attivazione di una eSIM tramite l'app dell'operatore utilizzando un codice di attivazione.

Attivazione dell'eSIM utilizzando un codice di attivazione

Quando si utilizza un codice di attivazione per attivare un profilo eSIM, l'LPA recupera un codice di attivazione dall'app dell'operatore e scarica il profilo. Questo flusso può essere avviato dalla LPA e la LPA può controllare l'intero flusso dell'interfaccia utente, il che significa che non viene mostrata alcuna interfaccia utente dell'app dell'operatore. Questo approccio ignora il controllo dei tag BF76 e gli operatori di rete non devono implementare l'intero flusso dell'interfaccia utente di attivazione eSIM, incluso il download di un profilo eSIM e la gestione degli errori.

Definizione del servizio di provisioning eUICC del carrier

L'LPA e l'app dell'operatore comunicano tramite due interfacce AIDL : ICarrierEuiccProvisioningService e IGetActivationCodeCallback . L'app dell'operatore deve implementare un'interfaccia ICarrierEuiccProvisioningService ed esporla nella dichiarazione manifest . L'LPA deve associarsi a ICarrierEuiccProvisioningService e implementare IGetActivationCodeCallback . Per ulteriori informazioni su come implementare ed esporre un'interfaccia AIDL, vedere Definizione e interfaccia AIDL .

Per definire le interfacce AIDL, crea i seguenti file AIDL sia per l'app LPA che per quella dell'operatore.

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

Esempio di implementazione LPA

Per associarsi all'implementazione ICarrierEuiccProvisioningService dell'app dell'operatore, l'LPA deve copiare sia ICarrierEuiccProvisioningService.aidl che IGetActivationCodeCallback.aidl nel progetto e implementare ServiceConnection .

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

Dopo l'associazione all'implementazione ICarrierEuiccProvisioningService dell'app dell'operatore, la LPA chiama getActivationCode o getActivationCodeForEid per ottenere il codice di attivazione dall'app dell'operatore passando l'implementazione della classe stub IGetActivationCodeCallback .

La differenza tra getActivationCode e getActivationCodeForEid è che getActivationCodeForEid consente a un operatore di preassociare un profilo all'EID del dispositivo prima che inizi il processo di download.

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

Esempio di implementazione per l'app dell'operatore

Affinché l'LPA si associ all'app dell'operatore, l'app dell'operatore deve copiare sia ICarrierEuiccProvisioningService.aidl che IGetActivationCodeCallback.aidl nel tuo progetto e dichiarare il servizio ICarrierEuiccProvisioningService nel file AndroidManifest.xml . Il servizio deve richiedere l'autorizzazione di sistema android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS per garantire che solo LPA, un'app con privilegi di sistema, possa collegarsi ad esso. Il servizio deve includere anche un filtro intent con l'azione 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>
    

Per implementare il servizio dell'app dell'operatore AIDL, crea un servizio, estendi la classe Stub e implementa i metodi getActivationCode e getActivationCodeForEid . L'LPA può quindi chiamare uno dei metodi per recuperare il codice di attivazione del profilo. L'app dell'operatore dovrebbe rispondere chiamando IGetActivationCodeCallback#onSuccess con il codice di attivazione se il codice è stato recuperato correttamente dal server dell'operatore. In caso di esito negativo, l'app dell'operatore dovrebbe rispondere con 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);
              }
          }
    }
    

Avvio dell'interfaccia utente dell'app dell'operatore nel flusso di attivazione LPA

Sui dispositivi con Android 11 e versioni successive, LPA può avviare l'interfaccia utente dell'app dell'operatore. Ciò è utile poiché l'app dell'operatore potrebbe richiedere informazioni aggiuntive all'utente prima di fornire un codice di attivazione alla LPA. Ad esempio, gli operatori potrebbero richiedere agli utenti di accedere per attivare i propri numeri di telefono o eseguire altri servizi di portabilità.

Questa è la procedura per avviare l'interfaccia utente dell'app dell'operatore nella LPA:

  1. L'LPA avvia il flusso di attivazione dell'app dell'operatore inviando l'intento android.service.euicc.action.START_CARRIER_ACTIVATION al pacchetto dell'app dell'operatore contenente l'azione. (Il ricevitore dell'app dell'operatore deve essere protetto nella dichiarazione manifest con android:permission="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" per evitare di ricevere intent da app non 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. L'app dell'operatore funziona utilizzando la propria interfaccia utente. Ad esempio, accedendo all'utente o inviando richieste HTTP al backend dell'operatore.

  3. L'app dell'operatore risponde all'LPA chiamando setResult(int, Intent) e finish() .

    1. Se l'app dell'operatore risponde con RESULT_OK , la LPA continua il flusso di attivazione. Se l'app dell'operatore determina che l'utente deve scansionare un codice QR invece di consentire all'LPA di associare il servizio dell'app dell'operatore, l'app dell'operatore risponde all'LPA utilizzando setResult(int, Intent) con RESULT_OK e un'istanza Intent contenente l' android.telephony.euicc.extra.USE_QR_SCANNER impostato su true . L'LPA controlla quindi l'extra e avvia lo scanner QR invece di vincolare l'implementazione ICarrierEuiccProvisioningService dell'app dell'operatore.
    2. Se l'app dell'operatore si arresta in modo anomalo o risponde con RESULT_CANCELED (questo è il codice di risposta predefinito), la LPA annulla il flusso di attivazione dell'eSIM.
    3. Se l'app dell'operatore risponde con qualcosa di diverso da RESULT_OK o RESULT_CANCELED , LPA lo considera un errore.

    Per motivi di sicurezza, la LPA non deve accettare direttamente un codice di attivazione fornito nell'intento del risultato per garantire che i chiamanti non LPA non possano ottenere un codice di attivazione dall'app dell'operatore.

Avvio del flusso di attivazione LPA in un'app dell'operatore

A partire da Android 11, le app dell'operatore possono utilizzare le API eUICC per avviare una LUI per l'attivazione eSIM. Questo metodo fa emergere l'interfaccia utente del flusso di attivazione eSIM della LPA per attivare il profilo eSIM. L'LPA invia quindi una trasmissione al termine dell'attivazione del profilo eSIM.

  1. L'LPA deve dichiarare un'attività che includa un filtro di intenti con l'azione android.service.euicc.action.START_EUICC_ACTIVATION . La priorità del filtro intent deve essere impostata su un valore diverso da zero nel caso in cui sul dispositivo siano presenti più implementazioni. Per esempio:

    <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. L'app dell'operatore funziona utilizzando la propria interfaccia utente. Ad esempio, accedendo all'utente o inviando richieste HTTP al backend dell'operatore.

  3. A questo punto, l'app dell'operatore deve essere pronta a fornire un codice di attivazione tramite l'implementazione ICarrierEuiccProvisioningService . L'app dell'operatore avvia l'LPA chiamando startActivityForResult(Intent, int) con l'azione android.telephony.euicc.action.START_EUICC_ACTIVATION . L'LPA controlla anche l'extra booleano android.telephony.euicc.extra.USE_QR_SCANNER . Se il valore è true , la LPA avvia lo scanner QR per consentire all'utente di scansionare il codice QR del profilo.

  4. Sul lato LPA, quest'ultimo si lega all'implementazione ICarrierEuiccProvisioningService dell'app dell'operatore per recuperare il codice di attivazione e scaricare il profilo corrispondente. L'LPA visualizza tutti gli elementi dell'interfaccia utente necessari durante il download, ad esempio una schermata di caricamento.

  5. Una volta completato il flusso di attivazione dell'LPA, l'LPA risponde all'app dell'operatore con un codice risultato, che l'app dell'operatore gestisce in onActivityResult(int, int, Intent) .

    1. Se l'LPA riesce a scaricare il nuovo profilo eSIM, risponde con RESULT_OK .
    2. Se l'utente annulla l'attivazione del profilo eSIM nella LPA, risponde con RESULT_CANCELED .
    3. Se la LPA risponde con qualcosa di diverso da RESULT_OK o RESULT_CANCELED , l'app dell'operatore lo considera un errore.

    Per motivi di sicurezza, la LPA non accetta un codice di attivazione direttamente nell'intento fornito per garantire che i chiamanti non LPA non possano ottenere il codice di attivazione dall'app dell'operatore.

Supporta più eSIM

Per i dispositivi con Android 10 o versioni successive, la classe EuiccManager supporta dispositivi con più eSIM. I dispositivi con una singola eSIM che stanno eseguendo l'aggiornamento ad Android 10 non richiedono alcuna modifica all'implementazione LPA poiché la piattaforma associa automaticamente l'istanza EuiccManager all'eUICC predefinito. L'eUICC predefinito è determinato dalla piattaforma per i dispositivi con versione radio HAL 1.2 o successiva e dall'LPA per i dispositivi con versione radio HAL inferiore a 1.2.

Requisiti

Per supportare più eSIM, il dispositivo deve avere più di un eUICC, che può essere un eUICC integrato o uno slot SIM fisico in cui è possibile inserire eUICC rimovibili.

Per supportare più eSIM è necessaria Radio HAL versione 1.2 o successiva. Si consigliano Radio HAL versione 1.4 e RadioConfig HAL versione 1.2.

Implementazione

Per supportare più eSIM (inclusi eUICC rimovibili o SIM programmabili), l'LPA deve implementare EuiccService , che riceve l'ID dello slot corrispondente all'ID della carta fornita dal chiamante.

La risorsa non_removable_euicc_slots specificata in arrays.xml è una matrice di numeri interi che rappresentano gli ID slot degli eUICC integrati di un dispositivo. È necessario specificare questa risorsa per consentire alla piattaforma di determinare se un eUICC inserito è rimovibile o meno.

App operatore per dispositivi con più eSIM

Quando crei un'app dell'operatore per un dispositivo con più eSIM, utilizza il metodo createForCardId in EuiccManager per creare un oggetto EuiccManager che viene aggiunto a un determinato ID della carta. L'ID della carta è un valore intero che identifica in modo univoco un UICC o un eUICC sul dispositivo.

Per ottenere l'ID della carta per l'eUICC predefinito del dispositivo, utilizzare il metodo getCardIdForDefaultEuicc in TelephonyManager . Questo metodo restituisce UNSUPPORTED_CARD_ID se la versione dell'HAL della radio è inferiore a 1.2 e restituisce UNINITIALIZED_CARD_ID se il dispositivo non ha letto l'eUICC.

Puoi anche ottenere gli ID delle carte da getUiccCardsInfo e getUiccSlotsInfo (API di sistema) in TelephonyManager e getCardId in SubscriptionInfo .

Quando un oggetto EuiccManager è stato istanziato con un ID carta specifico, tutte le operazioni vengono indirizzate all'eUICC con quell'ID carta. Se eUICC diventa irraggiungibile (ad esempio, quando viene spento o rimosso) EuiccManager non funziona più.

Puoi utilizzare i seguenti esempi di codice per creare un'app dell'operatore.

Esempio 1: ottieni un abbonamento attivo e crea un'istanza 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);

Esempio 2: scorrere gli UICC e creare un'istanza EuiccManager per un eUICC rimovibile

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

Validazione

AOSP non viene fornito con un'implementazione LPA e non è previsto che sia disponibile su tutte le build Android (non tutti i telefoni supportano eSIM). Per questo motivo non esistono casi di test CTS end-to-end. Tuttavia, in AOSP sono disponibili casi di test di base per garantire che le API eUICC esposte siano valide nelle build Android.

Dovresti assicurarti che le build superino i seguenti test case CTS (per API pubbliche): /platform/cts/tests/tests/telephony/current/src/android/telephony/euicc/cts .

Gli operatori che implementano un'app per operatori devono seguire i normali cicli interni di garanzia della qualità per garantire che tutte le funzionalità implementate funzionino come previsto. Come minimo, l'app dell'operatore dovrebbe essere in grado di elencare tutti i profili di abbonamento di proprietà dello stesso operatore, scaricare e installare un profilo, attivare un servizio sul profilo, passare da un profilo all'altro ed eliminare profili.

Se stai creando il tuo LPA, dovresti sottoporsi a test molto più rigorosi. Dovresti collaborare con il fornitore del modem, del chip eUICC o del sistema operativo eSIM, dei fornitori SM-DP+ e degli operatori per risolvere i problemi e garantire l'interoperabilità del tuo LPA all'interno dell'architettura RSP. Una buona quantità di test manuali è inevitabile. Per una migliore copertura del test, è necessario seguire il piano di test GSMA SGP.23 RSP .