Implementa eSIM

La tecnologia Embedded SIM (eSIM o eUICC) consente agli utenti di dispositivi mobili di scaricare un profilo dell'operatore e attivare il servizio di un operatore senza avere una scheda SIM fisica. Si tratta di una specifica globale creata dalla 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 accedere all'eSIM e gestire i profili degli abbonamenti sull'eSIM. Queste API eUICC consentono a terze parti di sviluppare le proprie app dell'operatore e gli assistenti per i profili locali (LPA) sui dispositivi Android con eSIM.

L'LPA è un'app di sistema autonoma che deve essere inclusa nell'immagine di compilazione Android. La gestione dei profili sull'eSIM viene generalmente eseguita dall'LPA, in quanto funge da ponte tra SM-DP+ (servizio remoto che prepara, memorizza e carica i pacchetti di profili sui dispositivi) e il chip eUICC. L'APK LPA può includere facoltativamente un componente dell'interfaccia utente, chiamato LPA UI o LUI, per fornire all'utente finale un punto di riferimento centrale per gestire tutti i profili di abbonamento incorporati. Il framework Android rileva e si connette automaticamente al miglior LPA disponibile e inoltra tutte le operazioni eUICC tramite un'istanza LPA.

Architettura semplificata del provisioning SIM da remoto (RSP)

Figura 1. Architettura RSP semplificata

Gli operatori di rete mobile interessati a creare un'app dell'operatore devono esaminare le API in EuiccManager, che forniscono operazioni di gestione dei profili 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 per consentire al framework Android di connettersi ai tuoi servizi LPA. Inoltre, è consigliabile utilizzare le API in EuiccCardManager, che forniscono funzioni ES10x basate su GSMA RSP v2.0. Queste funzioni vengono utilizzate per emettere comandi al chip eUICC, ad esempio prepareDownload(), loadBoundProfilePackage(), retrieveNotificationList() e resetMemory().

Le API in EuiccManager richiedono un'app LPA implementata correttamente per funzionare e l'autore della chiamata delle API EuiccCardManager deve essere un'app LPA. Questo viene applicato dal framework Android.

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

Creare un'app dell'operatore

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

EuiccManager

EuiccManager è il punto di accesso principale delle app all'LPA. Sono incluse le app dell'operatore che scaricano, eliminano e passano abbonamenti di proprietà dell'operatore. È inclusa anche l'app di sistema LUI, che fornisce una posizione/UI 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);

Prima di eseguire qualsiasi operazione eSIM, devi verificare se l'eSIM è supportata sul dispositivo. EuiccManager#isEnabled() in genere restituisce true se la funzionalità android.hardware.telephony.euicc è definita e è presente un pacchetto LPA.

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

Per 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 perché il loro completamento può richiedere secondi o addirittura minuti. PendingIntent viene inviato con un codice di risultato nello spazio EuiccManager#EMBEDDED_SUBSCRIPTION_RESULT_, che fornisce codici di errore definiti dal framework, nonché un codice di risultato dettagliato arbitrario propagato dall'LPA come EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, consentendo all'app dell'operatore di monitorare per scopi di logging/debug. Il callback PendingIntent debe essere BroadcastReceiver.

Per scaricare un 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 ed esempi di codice, consulta API eUICC.

Errori risolvibili

In alcuni casi 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 è necessario un codice di conferma dell'operatore. In alternativa, switchToSubscription potrebbe non funzionare se l'app dell'operatore dispone dei privilegi dell'operatore sul profilo di destinazione (ovvero, l'operatore è proprietario del profilo), ma non dispone dei privilegi dell'operatore rispetto al profilo attualmente attivato, pertanto è richiesto il consenso dell'utente.

In questi casi, il callback dell'autore della chiamata viene chiamato con EuiccManager#EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR. Il callback Intent contiene extra interni in modo che, quando il chiamante lo passa a EuiccManager#startResolutionActivity, la risoluzione possa essere richiesta tramite la LUI. L'utilizzo del 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 viene ripresa. Questo approccio offre all'app dell'operatore il controllo completo sul momento in cui viene visualizzata l'interfaccia utente, ma fornisce all'LPA/LUI un metodo estendibile per aggiungere in futuro una nuova gestione dei problemi recuperabili 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 corriere

Se sei un operatore che sviluppa la propria app che chiama EuiccManager per scaricare i profili su un dispositivo, il tuo profilo deve includere le regole dei privilegi dell'operatore corrispondenti all'app dell'operatore nei metadati. Questo accade perché i profili degli abbonamenti appartenenti a diversi operatori possono coesistere nell'eUICC di un dispositivo e ogni app dell'operatore deve poter accedere solo ai profili di proprietà dell'operatore in questione. Ad esempio, l'operatore A non deve essere in grado di scaricare, attivare o disattivare 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 l'app dell'operatore). La piattaforma Android carica i certificati memorizzati nel file delle regole di accesso (ARF) del profilo e concede alle app firmate da questi certificati l'autorizzazione per effettuare chiamate alle API EuiccManager. La procedura di alto livello è descritta di seguito:

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

    1. Firma (SHA-1 o SHA-256) del certificato della 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 con 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 nel file ARF, deve corrispondere anche al nome del pacchetto dell'app chiamante.

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

Poiché i metadati del profilo possono essere disponibili al di fuori del profilo stesso (in modo che l'LPA possa recuperare i metadati del profilo da SM-DP+ prima che il profilo venga scaricato o da ISD-R quando il profilo è disattivato), deve contenere le stesse regole dei privilegi dell'operatore presenti nel profilo.

Il sistema operativo eUICC e SM-DP+ devono supportare un tag proprietario BF76 nei metadati del profilo. I contenuti del tag devono essere gli stessi delle regole dei privilegi dell'operatore restituite dall'applet di regole di accesso (ARA) definito 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 maggiori dettagli sulla firma dell'app, vedi Firmare l'app. Per maggiori dettagli sui privilegi dell'operatore, vedi Privilegi operatore UICC.

Crea un'app di assistente del profilo locale

I produttori di dispositivi possono implementare il proprio assistente per i profili locali (LPA), che deve essere collegato alle API Euicc di Android. Le sezioni seguenti forniscono una breve panoramica della creazione di un'app LPA e della 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. Inoltre, ti consigliamo di utilizzare server SM-DP+ e SM-DS con una versione RSP corrispondente. Per un'architettura RSP dettagliata, consulta la specifica dell'architettura RSP GSMA SGP.21.

Inoltre, per l'integrazione con le API eUICC in Android 9, il modem del dispositivo deve inviare le funzionalità del terminale con il supporto per le funzionalità eUICC codificate (gestione del profilo locale e download del profilo). Inoltre, deve 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 del blocco dell'operatore per poter consentire il download o il trasferimento dell'eSIM solo per l'operatore consentito. In caso contrario, gli utenti potrebbero scaricare e trasferire una SIM per poi rendersi conto che il dispositivo è bloccato su un operatore diverso.

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

    • Il fornitore RIL / Modem deve compilare lo stato di blocco e l'ID operatore dell'operatore a cui è bloccato il dispositivo nell'ambito 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 con Android 10, è necessario definire un array di ID slot eUICC non rimovibili. Ad esempio, consulta 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, vedi Requisiti del modem per l'assistenza eSIM.

EuiccService

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

Per implementare il backend LPA, devi estendere EuiccService e dichiarare questo servizio nel file manifest. Il servizio deve richiedere l'autorizzazione di sistema android.permission.BIND_EUICC_SERVICE per garantire che solo il sistema possa eseguire l'associazione al servizio. Il servizio deve includere anche un filtro per intent con l'azione android.service.euicc.EuiccService. La priorità del filtro per intent deve essere impostata su un valore diverso da zero nel caso in cui sul dispositivo siano presenti più implementazioni. Ad 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 vi interagisce se necessario per supportare le API Android eUICC. Viene richiesta una query su 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 viene disattivato.

Per implementare la LUI, devi 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. Ciascuno deve avere un filtro di intent con l'azione appropriata, la categoria android.service.euicc.category.EUICC_UI e una priorità diversa da zero. Per scegliere le implementazioni di queste attività viene utilizzata una logica simile a quella utilizzata per scegliere l'implementazione di EuiccService. Ad 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. È una scelta di design disporre di un singolo APK o di più APK (ad esempio, uno che implementa EuiccService e uno che fornisce attività LUI).

EuiccCardManager

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

App dell&#39;operatore, LPA ed API Euicc

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

Le API di operazioni sul profilo tramite EuiccCardManager richiedono che chi chiama sia un LPA. Questa operazione viene applicata dal framework Android. Ciò significa che il chiamante deve estendere EuiccService ed essere dichiarato nel file manifest, come descritto nelle sezioni precedenti.

Come per EuiccManager, per utilizzare le API EuiccCardManager, il tuo LPA deve prima ottenere l'istanza di EuiccCardManager tramite Context#getSystemService:

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

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 lega a EuiccCardController (che viene eseguito nel procedura dello smartphone) tramite un'interfaccia AIDL e ogni metodo EuiccCardManager riceve il proprio callback dal processo dello smartphone tramite un'altra interfaccia AIDL dedicata. Quando utilizzi le API EuiccCardManager, l'utente che chiama (LPA) deve fornire un oggetto Executor tramite il quale viene invocato il callback. Questo oggetto Executor può essere eseguito su un singolo thread o su un pool di thread a tua scelta.

La maggior parte delle API EuiccCardManager ha lo stesso pattern di utilizzo. Ad esempio, per caricare un pacchetto di profili associati nella eUICC:

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

Per passare a un altro profilo 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 relative agli eventi di notifica specificati:

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

Attivare un profilo eSIM tramite un'app dell'operatore

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

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

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

Attiva l'eSIM usando 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 dall'LPA, che può controllare l'intero flusso dell'interfaccia utente, il che significa che non viene visualizzata alcuna interfaccia utente dell'app dell'operatore. Questo approccio aggira il controllo del tag BF76 e gli operatori di rete non devono implementare l'intero flusso dell'interfaccia utente di attivazione dell'eSIM, incluso il download di un profilo eSIM e la gestione degli errori.

Definisci il servizio di provisioning eUICC dell'operatore

L'app LPA e l'app dell'operatore comunicano tramite due interfacce AIDL:ICarrierEuiccProvisioningService e IGetActivationCodeCallback. L'app dell'operatore deve implementare un'interfaccia ICarrierEuiccProvisioningService e metterla in evidenza nella dichiarazione manifest. L'LPA deve essere associata a ICarrierEuiccProvisioningService e implementare IGetActivationCodeCallback. Per ulteriori informazioni su come implementare ed esporre un'interfaccia AIDL, consulta Definizione e interfaccia AIDL.

Per definire le interfacce AIDL, crea i seguenti file AIDL sia per le app LPA sia per quelle 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 di LPA

Per eseguire il binding all'implementazione di ICarrierEuiccProvisioningService dell'app dell'operatore, l'LPA deve copiare sia ICarrierEuiccProvisioningService.aidl sia 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, l'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 i criteri getActivationCode e getActivationCodeForEid è che getActivationCodeForEid consente a un operatore di associare un profilo all'EID del dispositivo prima dell'inizio del 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 possa essere associato all'app dell'operatore, l'app dell'operatore deve copiare ICarrierEuiccProvisioningService.aidl e 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 l'app con privilegi di sistema possa essere associata all'LPA. Il servizio deve includere anche un filtro per 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 due 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 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);
              }
          }
    }
    

Avvia l'interfaccia utente dell'app dell'operatore nel flusso di attivazione dell'LPA

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

Questa è la procedura per avviare l'interfaccia utente di un'app dell'operatore nell'LPA:

  1. L'LPA avvia il flusso di attivazione dell'app dell'operatore inviando l'intent android.service.euicc.action.START_CARRIER_ACTIVATION al pacchetto dell'app dell'operatore contenente l'azione. Il destinatario 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 svolge la sua attività utilizzando la propria UI. 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, l'LPA prosegue il flusso di attivazione. Se l'app dell'operatore stabilisce che l'utente deve scansionare un codice QR anziché 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'extra booleano android.telephony.euicc.extra.USE_QR_SCANNER impostato su true. L'LPA controlla quindi l'extra e avvia lo scanner QR anziché 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), l'LPA annulla il flusso di attivazione della eSIM.
    3. Se l'app dell'operatore risponde con un valore diverso da RESULT_OK o RESULT_CANCELED, l'LPA lo tratta come un errore.

    Per motivi di sicurezza, l'LPA non deve accettare direttamente un codice di attivazione fornito nell'intent del risultato per garantire che gli utenti che non sono LPA non possano ricevere un codice di attivazione dall'app dell'operatore.

Avviare il 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 di eSIM. Questo metodo mostra l'interfaccia utente del flusso di attivazione eSIM dell'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 per intent con l'azioneandroid.service.euicc.action.START_EUICC_ACTIVATION. La priorità del filtro per intent deve essere impostata su un valore diverso da zero nel caso in cui siano presenti più implementazioni sul dispositivo. Ad 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 svolge il proprio lavoro utilizzando la propria UI. Ad esempio, l'accesso dell'utente o l'invio di richieste HTTP al backend dell'operatore.

  3. A questo punto, l'app dell'operatore deve essere pronta a fornire un codice di attivazione tramite la sua implementazione di 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 il valore booleano extra android.telephony.euicc.extra.USE_QR_SCANNER. Se il valore è true, il LPA avvia lo scanner QR per consentire all'utente di scansionare il codice QR del profilo.

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

  5. Al termine del flusso di attivazione dell'LPA, l'app risponde all'app dell'operatore con un codice di risultato, gestito dall'app dell'operatore 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 nell'LPA, risponde con RESULT_CANCELED.
    3. Se l'LPA risponde con un valore diverso da RESULT_OK o RESULT_CANCELED, l'app dell'operatore lo considera un errore.

    Per motivi di sicurezza, l'LPA non accetta un codice di attivazione direttamente nell'intent fornito per garantire che gli utenti che chiamano da un altro 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 esegue l'upgrade ad Android 10 non richiedono alcuna modifica all'implementazione LPA, poiché la piattaforma associa automaticamente l'istanza EuiccManager alla eUICC predefinita. L'eUICC predefinita è determinata dalla piattaforma per i dispositivi con versione radio HAL 1.2 o successive e dall'LPA per i dispositivi con versioni radio HAL precedenti alla 1.2.

Requisiti

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

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

Implementazione

Per supportare più eSIM (incluse eUICC rimovibili o SIM programmabili), il fornitore di servizi di telefonia cellulare deve implementare EuiccService, che riceve l'ID slot corrispondente all'ID carta fornito dall'utente che chiama.

La risorsa non_removable_euicc_slots specificata in arrays.xml è un array di numeri interi che rappresentano gli ID slot delle eUICC integrate di un dispositivo. Devi specificare questa risorsa per consentire alla piattaforma di determinare se un'eUICC inserita è rimovibile o meno.

App dell'operatore per un dispositivo 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 bloccato su un determinato ID carta. L'ID carta è un valore intero che identifica in modo univoco una UICC o un'eUICC sul dispositivo.

Per ottenere l'ID della scheda per l'eUICC predefinita del dispositivo, utilizza il metodo getCardIdForDefaultEuicc in TelephonyManager. Questo metodo restituisce UNSUPPORTED_CARD_ID se la versione HAL radio è precedente alla 1.2 e restituisce UNINITIALIZED_CARD_ID se il dispositivo non ha letto la eUICC.

Puoi anche recuperare gli ID carta da getUiccCardsInfo e getUiccSlotsInfo (API di sistema) in TelephonyManager, nonché da getCardId in SubscriptionInfo.

Dopo aver creato un'istanza di un oggetto EuiccManager con uno specifico ID carta, tutte le operazioni vengono indirizzate all'eUICC con quell'ID carta. Se la eUICC diventa irraggiungibile (ad esempio, quando viene disattivata o rimossa), EuiccManager non funzionerà più.

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

Esempio 1: attivare un abbonamento attivo e creare un'istanza di 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: esegui l'iterazione delle UICC e crea un'istanza di 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);
}

Convalida

AOSP non include un'implementazione di LPA e non è previsto che un LPA sia disponibile su tutte le build di Android (non tutti gli smartphone supportano eSIM). Per questo motivo, non sono disponibili scenari 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 di Android.

Devi assicurarti che le build superino i seguenti casi di test CTS (per le 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 di controllo qualità interni per assicurarsi che tutte le funzionalità implementate funzionino come previsto. Come minimo, l'app dell'operatore deve essere in grado di elencare tutti i profili di abbonamento di proprietà dello stesso operatore, scaricare e installare un profilo, attivare un servizio nel profilo, passare da un profilo all'altro ed eliminare i profili.

Se crei il tuo LPA, devi sottoporlo a test molto più rigorosi. Collabora con il tuo fornitore di modem, con il chip eUICC o con il fornitore del sistema operativo eSIM, con i fornitori SM-DP+ e con gli operatori per risolvere i problemi e garantire l'interoperabilità del tuo LPA all'interno dell'architettura RSP. È inevitabile una buona quantità di test manuali. Per una copertura ottimale dei test, devi seguire il GSMA SGP.23 RSP Test Plan.