Implementare l'eSIM

La tecnologia SIM incorporata (eSIM o eUICC) consente agli utenti di dispositivi mobili di scaricare un profilo operatore e attivare il servizio di un operatore senza una scheda SIM fisica. Si tratta di una specifica globale guidata 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 alla eSIM e gestire i profili di abbonamento sulla eSIM. Queste API eUICC consentono a terze parti di sviluppare le proprie app operatore e assistenti del profilo locale (LPA) su dispositivi Android compatibili con eSIM.

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

Architettura RSP (Remote SIM Provisioning) semplificata

Figura 1. Architettura RSP semplificata

Gli operatori di rete mobile interessati a creare un'app operatore devono esaminare le API in EuiccManager, che fornisce 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, devi 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, ad esempio prepareDownload(), loadBoundProfilePackage(), retrieveNotificationList() e resetMemory().

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

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

Crea un'app operatore

Le API eUICC in Android 9 consentono agli operatori di rete mobile di creare app con il brand 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à di un operatore.

EuiccManager

EuiccManager è l'entry point principale per le app per interagire con LPA. Sono incluse le app dell'operatore che scaricano, eliminano e passano ad abbonamenti di proprietà dell'operatore. È inclusa 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 operatore deve prima ottenere l'istanza di EuiccManager tramite Context#getSystemService:

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

Prima di eseguire qualsiasi operazione con la eSIM, controlla se è supportata sul dispositivo. EuiccManager#isEnabled() in genere 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 i callback PendingIntent perché il completamento potrebbe richiedere secondi o addirittura minuti. 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 eseguire il monitoraggio a scopo di logging/debug. Il callback PendingIntent deve essere BroadcastReceiver.

Per scaricare un determinato abbonamento scaricabile (creato da un codice di attivazione o 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 dato l'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 di 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 è richiesto un codice di conferma dell'operatore. oppure switchToSubscription potrebbe non riuscire se l'app dell'operatore ha privilegi sull'operatore sul profilo di destinazione (ovvero l'operatore è proprietario del profilo), ma non ha privilegi dell'operatore sul profilo attualmente abilitato e quindi è necessario il consenso dell'utente.

Per questi casi, il callback del chiamante 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. Utilizzando di nuovo il codice di conferma, ad esempio, EuiccManager#startResolutionActivity viene visualizzata 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 su quando viene visualizzata la UI, ma fornisce all'LPA/LUI un metodo estensibile per aggiungere in futuro una nuova gestione dei problemi recuperabili dall'utente senza dover modificare le app client.

Android 9 definisce questi errori risolvibili in EuiccService, che l'interfaccia utente locale deve 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 operatore che chiama EuiccManager per scaricare i profili su un dispositivo, i metadati del tuo profilo devono includere le regole dei privilegi dell'operatore corrispondenti alla tua app operatore. Questo perché i profili di abbonamento appartenenti a operatori diversi possono coesistere nell'eUICC di un dispositivo e ogni app operatore deve avere l'autorizzazione di accedere solo ai profili di proprietà di quell'operatore. 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 archiviati nel file delle regole di accesso (ARF) del profilo e concede alle app firmate da questi certificati l'autorizzazione a effettuare chiamate alle API EuiccManager. La procedura generale è descritta di seguito:

  1. L'operatore firma l'APK dell'app dell'operatore; lo strumento apksigner collega il certificato della 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 della chiave pubblica dell'app dell'operatore (obbligatorio)
    2. Nome del pacchetto dell'app dell'operatore (consigliato vivamente)
  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 dal file 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. Una volta verificata 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 al di fuori 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 è disattivato), devono contenere le stesse regole di privilegio dell'operatore del profilo.

Il sistema operativo eUICC e SM-DP+ devono supportare un tag proprietario BF76 nei metadati del profilo. Il contenuto del tag deve essere uguale alle regole dei privilegi dell'operatore restituite dall'applet della regola di accesso (ARA) definita 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, vedi Firma la tua app. Per dettagli sui privilegi dell'operatore, vedi Privilegi dell'operatore UICC.

Creare un'app assistente per il profilo locale

I produttori di dispositivi possono implementare il proprio assistente del profilo locale (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. Devi anche prevedere di utilizzare server SM-DP+ e SM-DS con una versione RSP corrispondente. Per l'architettura RSP dettagliata, vedi GSMA SGP.21 RSP Architecture Specification.

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

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

    • Il modem / RIL del fornitore 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 attivato come SIM valida e mantenere l'alimentazione della SIM attiva.

Per i dispositivi con Android 10, deve essere definito un array di ID slot eUICC non rimovibili. Ad esempio, vedi 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 dell'eSIM.

EuiccService

Un LPA è costituito da due componenti separati (possono essere implementati entrambi nello stesso APK): il backend LPA e la UI 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 associarsi. Il servizio deve includere anche un filtro per intent con l'azione android.service.euicc.EuiccService. La priorità del filtro dell'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>

A livello interno, il framework Android determina l'LPA attivo e interagisce con questo in base alle necessità per supportare le API eUICC di Android. PackageManager viene interrogato 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 è disattivato.

Per implementare l'interfaccia utente vocale, 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 per intent con l'azione appropriata, la categoria android.service.euicc.category.EUICC_UI e una priorità diversa da zero. Per scegliere le implementazioni per 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 la UI 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 decisione di progettazione.

EuiccCardManager

EuiccCardManager è l'interfaccia per comunicare con il chip eSIM. Fornisce funzioni ES10 (come descritto nella specifica 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 chiamata solo da app con privilegi di sistema.

API per app dell&#39;operatore, LPA ed 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 il chiamante sia un LPA. Questo limite viene applicato dal framework Android. Ciò significa che il chiamante deve estendere EuiccService ed essere dichiarato nel file manifest, come descritto nelle sezioni precedenti.

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

Poi, 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 associa a EuiccCardController (che viene eseguito nel processo del telefono) tramite un'interfaccia AIDL e ogni metodo EuiccCardManager riceve il callback dal processo del telefono tramite un'interfaccia AIDL dedicata diversa. Quando utilizzi le API EuiccCardManager, il chiamante (LPA) deve fornire un oggetto Executor tramite il quale viene richiamato 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 sull'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 degli eventi di notifica specificati:

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

Attivare un profilo eSIM tramite l'app di un operatore

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

Quando un'app operatore scarica un profilo chiamando downloadSubscription, la chiamata impone che l'app possa gestire il profilo tramite un BF76 tag di metadati che codifica le regole dei privilegi dell'operatore per il profilo. Se un profilo non ha un tag BF76 o se il suo 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 di un operatore utilizzando un codice di attivazione.

Attivare l'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 dall'LPA e l'LPA può controllare l'intero flusso dell'interfaccia utente, il che significa che non viene visualizzata l'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'UI 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 operatore comunicano tramite due interfacce AIDL: ICarrierEuiccProvisioningService e IGetActivationCodeCallback. L'app dell'operatore deve implementare un'interfaccia ICarrierEuiccProvisioningService ed esporla nella dichiarazione del manifest. L'LPA deve essere associato a ICarrierEuiccProvisioningService e implementare IGetActivationCodeCallback. Per ulteriori informazioni su come implementare ed esporre un'interfaccia AIDL, consulta Definizione di un'interfaccia AIDL.

Per definire le interfacce AIDL, crea i seguenti file AIDL sia per l'app LPA sia per l'app 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 operatore, l'LPA deve copiare sia ICarrierEuiccProvisioningService.aidl sia IGetActivationCodeCallback.aidl nel tuo progetto e implementare ServiceConnection.

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

Dopo il binding 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 getActivationCode e getActivationCodeForEid è che getActivationCodeForEid consente a un operatore di pre-associare un profilo all'EID del dispositivo prima dell'inizio della procedura 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é LPA si associ all'app dell'operatore, quest'ultima deve copiare sia ICarrierEuiccProvisioningService.aidl sia 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'LPA, un'app con privilegi di sistema, possa associarsi. 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 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 deve 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 deve 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 LPA

Sui dispositivi con Android 11 e versioni successive, l'LPA può avviare la UI di un'app operatore. Ciò è utile in quanto un'app 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 accedere per attivare i propri numeri di telefono o eseguire altri servizi di trasferimento.

Questa è la procedura per avviare la UI di un'app operatore in 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 ricevitore dell'app dell'operatore deve essere protetto nella dichiarazione del 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 il suo lavoro utilizzando la propria UI. Ad esempio, l'accesso dell'utente o l'invio di 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 continua il flusso di attivazione. Se l'app dell'operatore determina 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é eseguire il binding dell'implementazione ICarrierEuiccProvisioningService dell'app operatore.
    2. Se l'app dell'operatore va in arresto 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 considera un errore.

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

Avviare il flusso di attivazione LPA in un'app operatore

A partire da Android 11, le app degli operatori possono utilizzare le API eUICC per avviare un'interfaccia utente di livello basso per l'attivazione dell'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'azione android.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 sul dispositivo siano presenti più implementazioni. 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 suo 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 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, l'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'implementazione ICarrierEuiccProvisioningService dell'app dell'operatore per recuperare il codice di attivazione e scaricare il profilo corrispondente. L'LPA mostra tutti gli elementi dell'interfaccia utente necessari durante il download, ad esempio una schermata di caricamento.

  5. Al termine del flusso di attivazione della LPA, quest'ultima risponde all'app dell'operatore con un codice di 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 nell'LPA, viene restituito 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 i chiamanti non LPA non possano ottenere il codice di attivazione dall'app dell'operatore.

Supportare più eSIM

Per i dispositivi con Android 10 o versioni successive, la classe EuiccManager supporta i dispositivi con più eSIM. I dispositivi con una sola eSIM che eseguono l'upgrade ad Android 10 non richiedono alcuna modifica all'implementazione LPA, in quanto la piattaforma associa automaticamente l'istanza EuiccManager all'eUICC predefinita. L'eUICC predefinita è determinata dalla piattaforma per i dispositivi con versione HAL radio 1.2 o successive e dall'LPA per i dispositivi con versioni HAL radio precedenti alla 1.2.

Requisiti

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

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

Implementazione

Per supportare più eSIM (incluse eUICCs rimovibili o SIM programmabili), l'LPA deve implementare EuiccService, che riceve l'ID slot corrispondente all'ID scheda fornito dal chiamante.

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 una eUICC inserita è rimovibile o meno.

App dell'operatore per il dispositivo con più eSIM

Quando crei un'app operatore per un dispositivo con più eSIM, utilizza il metodo createForCardId in EuiccManager per creare un oggetto EuiccManager bloccato a un determinato ID carta. L'ID scheda è un valore intero che identifica in modo univoco una UICC o una 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 l'eUICC.

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

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

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

Esempio 1: recupera l'abbonamento attivo e crea 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 tra le UICC e crea un'istanza di EuiccManager per una 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 LPA e non è previsto che sia disponibile un LPA su tutte le build Android (non tutti gli smartphone supportano l'eSIM). Per questo motivo, non esistono 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 scenari di test CTS (per le API pubbliche): /platform/cts/tests/tests/telephony/current/src/android/telephony/euicc/cts.

Gli operatori che implementano un'app operatore devono eseguire i normali cicli interni di controllo qualità 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 sul profilo, passare da un profilo all'altro ed eliminare i profili.

Se crei il tuo LPA, devi sottoporti a test molto più rigorosi. Devi collaborare con il fornitore del modem, del chip eUICC o del sistema operativo eSIM, con i fornitori SM-DP+ e con gli operatori per risolvere i problemi e garantire l'interoperabilità del tuo LPA nell'architettura RSP. Una buona quantità di test manuali è inevitabile. Per una copertura ottimale dei test, devi seguire il piano di test GSMA SGP.23 RSP.