Mise en œuvre de l'eSIM

La technologie SIM intégrée (eSIM ou eUICC) permet aux utilisateurs mobiles de télécharger un profil d'opérateur et d'activer le service d'un opérateur sans avoir de carte SIM physique. Il s'agit d'une spécification mondiale pilotée par la GSMA qui permet le provisionnement de cartes SIM à distance (RSP) de n'importe quel appareil mobile. À partir d'Android 9, le framework Android fournit des API standard pour accéder à l'eSIM et gérer les profils d'abonnement sur l'eSIM. Ces API eUICC permettent à des tiers de développer leurs propres applications d'opérateur et assistants de profil local (LPA) sur les appareils Android compatibles eSIM.

Le LPA est une application système autonome qui doit être incluse dans l’image de build Android. La gestion des profils sur l'eSIM est généralement effectuée par le LPA, car il sert de pont entre le SM-DP+ (service à distance qui prépare, stocke et fournit des packages de profils aux appareils) et la puce eUICC. L'APK LPA peut éventuellement inclure un composant d'interface utilisateur, appelé LPA UI ou LUI, pour fournir un endroit central permettant à l'utilisateur final de gérer tous les profils d'abonnement intégrés. Le framework Android découvre et se connecte automatiquement au meilleur LPA disponible, et achemine toutes les opérations eUICC via une instance LPA.

Architecture simplifiée de provisionnement SIM à distance (RSP)

Figure 1. Architecture RSP simplifiée

Les opérateurs de réseaux mobiles intéressés par la création d'une application d'opérateur devraient consulter les API d' EuiccManager , qui fournit des opérations de gestion de profil de haut niveau telles que downloadSubscription() , switchToSubscription() et deleteSubscription() .

Si vous êtes un équipementier OEM souhaitant créer votre propre application système LPA, vous devez étendre EuiccService pour le framework Android pour vous connecter à vos services LPA. De plus, vous devez utiliser les API d' EuiccCardManager , qui fournissent des fonctions ES10x basées sur GSMA RSP v2.0. Ces fonctions sont utilisées pour émettre des commandes vers la puce eUICC, telles que prepareDownload() , loadBoundProfilePackage() , retrieveNotificationList() et resetMemory() .

Les API d' EuiccManager nécessitent une application LPA correctement implémentée pour fonctionner et l'appelant des API EuiccCardManager doit être un LPA. Ceci est appliqué par le framework Android.

Les appareils fonctionnant sous Android 10 ou version ultérieure peuvent prendre en charge les appareils dotés de plusieurs eSIM. Pour plus d'informations, consultez Prise en charge de plusieurs eSIM .

Créer une application de transporteur

Les API eUICC d'Android 9 permettent aux opérateurs de réseaux mobiles de créer des applications de marque opérateur pour gérer directement leurs profils. Cela inclut le téléchargement et la suppression des profils d'abonnement appartenant à l'opérateur, ainsi que le passage à un profil appartenant à un opérateur.

EuiccManager

EuiccManager est le principal point d'entrée permettant aux applications d'interagir avec le LPA. Cela inclut les applications de l'opérateur qui téléchargent, suppriment et basculent vers des abonnements appartenant à l'opérateur. Cela inclut également l'application système LUI, qui fournit un emplacement/interface utilisateur central pour gérer tous les abonnements intégrés, et peut être une application distincte de celle qui fournit le EuiccService .

Pour utiliser les API publiques, une application d'opérateur doit d'abord obtenir l'instance d' EuiccManager via Context#getSystemService :

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

Vous devez vérifier si l'eSIM est pris en charge sur l'appareil avant d'effectuer toute opération eSIM. EuiccManager#isEnabled() renvoie généralement true si la fonctionnalité android.hardware.telephony.euicc est définie et qu'un package LPA est présent.

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

Pour obtenir des informations sur le matériel eUICC et la version du système d'exploitation eSIM :

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

De nombreuses API, telles que downloadSubscription() et switchToSubscription() , utilisent les rappels PendingIntent car leur exécution peut prendre quelques secondes, voire quelques minutes. PendingIntent est envoyé avec un code de résultat dans l'espace EuiccManager#EMBEDDED_SUBSCRIPTION_RESULT_ , qui fournit des codes d'erreur définis par le framework, ainsi qu'un code de résultat détaillé arbitraire propagé à partir du LPA sous la forme EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE , permettant à l'application de l'opérateur d'effectuer un suivi à des fins de journalisation/débogage. Le rappel PendingIntent doit être BroadcastReceiver .

Pour télécharger un abonnement téléchargeable donné (créé à partir d'un code d'activation ou d'un QR code) :

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

Définissez et utilisez l'autorisation dans AndroidManifest.xml :

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

Pour passer à un abonnement en fonction de l'ID d'abonnement :

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

Pour une liste complète des API EuiccManager et des exemples de code, consultez API eUICC .

Erreurs résolubles

Il existe des cas où le système ne parvient pas à terminer l'opération eSIM, mais l'erreur peut être résolue par l'utilisateur. Par exemple, downloadSubscription peut échouer si les métadonnées du profil indiquent qu'un code de confirmation de l'opérateur est requis. Ou encore, switchToSubscription peut échouer si l'application de l'opérateur dispose de privilèges d'opérateur sur le profil de destination (c'est-à-dire que l'opérateur est propriétaire du profil) mais ne dispose pas de privilèges d'opérateur sur le profil actuellement activé, et le consentement de l'utilisateur est donc requis.

Dans ces cas, le rappel de l'appelant est appelé avec EuiccManager#EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR . L' Intent de rappel contient des extras internes tels que lorsque l'appelant le transmet à EuiccManager#startResolutionActivity , la résolution peut être demandée via l'interface LUI. En utilisant à nouveau le code de confirmation par exemple, EuiccManager#startResolutionActivity déclenche un écran LUI qui permet à l'utilisateur de saisir un code de confirmation ; une fois le code saisi, l'opération de téléchargement reprend. Cette approche fournit à l'application de l'opérateur un contrôle total sur le moment où l'interface utilisateur est affichée, mais donne au LPA/LUI une méthode extensible pour ajouter une nouvelle gestion des problèmes récupérables par l'utilisateur à l'avenir sans avoir besoin de modifier les applications clientes.

Android 9 définit ces erreurs résolubles dans EuiccService , que l'interface LUI doit gérer :

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

Privilèges du transporteur

Si vous êtes un opérateur développant votre propre application d'opérateur qui appelle EuiccManager pour télécharger des profils sur un appareil, votre profil doit inclure les règles de privilèges d'opérateur correspondant à votre application d'opérateur dans les métadonnées. En effet, les profils d'abonnement appartenant à différents opérateurs peuvent coexister dans l'eUICC d'un appareil, et chaque application d'opérateur ne doit être autorisée à accéder qu'aux profils appartenant à cet opérateur. Par exemple, l'opérateur A ne devrait pas pouvoir télécharger, activer ou désactiver un profil appartenant à l'opérateur B.

Pour garantir qu'un profil n'est accessible qu'à son propriétaire, Android utilise un mécanisme pour accorder des privilèges spéciaux à l'application du propriétaire du profil (c'est-à-dire l'application de l'opérateur). La plate-forme Android charge les certificats stockés dans le fichier de règles d'accès (ARF) du profil et autorise les applications signées par ces certificats à effectuer des appels aux API EuiccManager . Le processus de haut niveau est décrit ci-dessous :

  1. L'opérateur signe l'APK de l'application du transporteur ; l'outil apksigner attache le certificat de clé publique à l'APK.
  2. L'opérateur/SM-DP+ prépare un profil et ses métadonnées, qui incluent un ARF contenant :

    1. Signature (SHA-1 ou SHA-256) du certificat de clé publique de l'application de l'opérateur (obligatoire)
    2. Nom du package de l'application du transporteur (fortement recommandé)
  3. L'application Carrier tente d'effectuer une opération eUICC via l'API EuiccManager .

  4. La plate-forme Android vérifie que le hachage SHA-1 ou SHA-256 du certificat de l'application appelante correspond à la signature du certificat obtenu à partir de l'ARF du profil cible. Si le nom du package de l’application de l’opérateur est inclus dans l’ARF, il doit également correspondre au nom du package de l’application appelante.

  5. Une fois la signature et le nom du package (le cas échéant) vérifiés, le privilège de l'opérateur est accordé à l'application appelante sur le profil cible.

Étant donné que les métadonnées du profil peuvent être disponibles en dehors du profil lui-même (afin que LPA puisse récupérer les métadonnées du profil depuis SM-DP+ avant le téléchargement du profil, ou depuis ISD-R lorsque le profil est désactivé), elles doivent contenir les mêmes règles de privilèges d'opérateur. comme dans le profil.

Le système d'exploitation eUICC et SM-DP+ doivent prendre en charge une balise propriétaire BF76 dans les métadonnées du profil. Le contenu de la balise doit être conforme aux mêmes règles de privilèges d'opérateur que celles renvoyées par l'applet de règle d'accès (ARA) définie dans les privilèges d'opérateur 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
    }
}

Pour plus de détails sur la signature d’applications, consultez Signer votre application . Pour plus de détails sur les privilèges des opérateurs, voir Privilèges des opérateurs UICC .

Créer une application d'assistant de profil local

Les fabricants d'appareils peuvent implémenter leur propre assistant de profil local (LPA), qui doit être connecté aux API Android Euicc. Les sections suivantes donnent un bref aperçu de la création d'une application LPA et de son intégration au système Android.

Configuration matérielle/modem requise

Le LPA et le système d'exploitation eSIM sur la puce eUICC doivent prendre en charge au moins GSMA RSP (Remote SIM Provisioning) v2.0 ou v2.2. Vous devez également prévoir d'utiliser des serveurs SM-DP+ et SM-DS dotés d'une version RSP correspondante. Pour une architecture RSP détaillée, voir la spécification d'architecture RSP GSMA SGP.21 .

De plus, pour s'intégrer aux API eUICC dans Android 9, le modem de l'appareil doit envoyer des fonctionnalités de terminal avec la prise en charge des fonctionnalités eUICC codées (gestion de profil local et téléchargement de profil). Il doit également mettre en œuvre les méthodes suivantes :

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

  • IRadioConfig HAL v1.0 : getSimSlotsStatus

  • IRadioConfig AIDL v1.0 : getAllowedCarriers

    Le Google LPA doit connaître l'état de verrouillage de l'opérateur afin de pouvoir autoriser le téléchargement ou le transfert d'eSIM uniquement pour l'opérateur autorisé. Sinon, les utilisateurs pourraient finir par télécharger et transférer une carte SIM et se rendre compte plus tard que l'appareil est verrouillé sur un autre opérateur.

    • Les fournisseurs ou les OEM doivent implémenter l’API HAL IRadioSim.getAllowedCarriers().

    • Le fournisseur RIL/Modem doit renseigner l'état de verrouillage et l'ID de transporteur de l'opérateur sur lequel l'appareil est verrouillé dans le cadre de l'API IRadioSimResponse.getAllowedCarriersResponse()HAL.

Le modem doit reconnaître l'eSIM avec le profil de démarrage par défaut activé comme une carte SIM valide et maintenir la carte SIM sous tension.

Pour les appareils exécutant Android 10, un tableau d'ID d'emplacement eUICC non amovible doit être défini. Par exemple, consultez 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>

Pour une liste complète des exigences du modem, consultez Exigences du modem pour la prise en charge eSIM .

EuiccService

Un LPA se compose de deux composants distincts (peuvent tous deux être implémentés dans le même APK) : le backend LPA et l'interface utilisateur ou LUI LPA.

Pour implémenter le backend LPA, vous devez étendre EuiccService et déclarer ce service dans votre fichier manifeste. Le service doit nécessiter l'autorisation système android.permission.BIND_EUICC_SERVICE pour garantir que seul le système peut s'y lier. Le service doit également inclure un filtre d'intention avec l'action android.service.euicc.EuiccService . La priorité du filtre d'intention doit être définie sur une valeur non nulle dans le cas où plusieurs implémentations sont présentes sur l'appareil. Par exemple:

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

En interne, le framework Android détermine le LPA actif et interagit avec lui si nécessaire pour prendre en charge les API Android eUICC. PackageManager est interrogé pour toutes les applications avec l'autorisation android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS , qui spécifie un service pour l'action android.service.euicc.EuiccService . Le service ayant la priorité la plus élevée est sélectionné. Si aucun service n'est trouvé, la prise en charge LPA est désactivée.

Pour implémenter l'interface LUI, vous devez fournir une activité pour les actions suivantes :

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

Comme pour le service, chaque activité doit nécessiter l'autorisation système android.permission.BIND_EUICC_SERVICE . Chacun doit avoir un filtre d'intention avec l'action appropriée, la catégorie android.service.euicc.category.EUICC_UI et une priorité non nulle. Une logique similaire est utilisée pour sélectionner les implémentations de ces activités comme pour la sélection de l'implémentation de EuiccService . Par exemple:

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

Cela implique que l'interface utilisateur implémentant ces écrans peut provenir d'un APK différent de celui qui implémente EuiccService . Qu'il s'agisse d'un seul APK ou de plusieurs APK (par exemple, celui qui implémente EuiccService et celui qui fournit des activités LUI) est un choix de conception.

EuiccCardManager

EuiccCardManager est l'interface de communication avec la puce eSIM. Il fournit des fonctions ES10 (telles que décrites dans la spécification GSMA RSP) et gère les commandes de requête/réponse APDU de bas niveau ainsi que l'analyse ASN.1. EuiccCardManager est une API système et ne peut être appelée que par des applications privilégiées par le système.

Applications d'opérateur, API LPA et Euicc

Figure 2. L'application de l'opérateur et la LPA utilisent les API Euicc

Les API d'opération de profil via EuiccCardManager nécessitent que l'appelant soit un LPA. Ceci est appliqué par le framework Android. Cela signifie que l'appelant doit étendre EuiccService et être déclaré dans votre fichier manifeste, comme décrit dans les sections précédentes.

Semblable à EuiccManager , pour utiliser les API EuiccCardManager , votre LPA doit d'abord obtenir l'instance de EuiccCardManager via Context#getSystemService :

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

Ensuite, pour récupérer tous les profils sur l'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);

En interne, EuiccCardManager se lie à EuiccCardController (qui s'exécute dans le processus téléphonique) via une interface AIDL, et chaque méthode EuiccCardManager reçoit son rappel du processus téléphonique via une interface AIDL dédiée différente. Lors de l'utilisation des API EuiccCardManager , l'appelant (LPA) doit fournir un objet Executor via lequel le rappel est invoqué. Cet objet Executor peut s'exécuter sur un seul thread ou sur un pool de threads de votre choix.

La plupart des API EuiccCardManager ont le même modèle d'utilisation. Par exemple, pour charger un package de profil lié sur l'eUICC :

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

Pour passer à un autre profil avec un ICCID donné :

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

Pour obtenir l'adresse SM-DP+ par défaut de la puce eUICC :

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

Pour récupérer une liste de notifications des événements de notification donnés :

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

Activation d'un profil eSIM via une application d'opérateur

Sur les appareils fonctionnant sous Android 9 ou version ultérieure, vous pouvez utiliser une application d'opérateur pour activer l'eSIM et télécharger des profils. L'application de l'opérateur peut télécharger des profils en appelant directement downloadSubscription ou en fournissant un code d'activation au LPA.

Lorsqu'une application d'opérateur télécharge un profil en appelant downloadSubscription , l'appel impose que l'application puisse gérer le profil via une balise de métadonnées BF76 qui code les règles de privilèges d'opérateur pour le profil. Si un profil ne possède pas de balise BF76 ou si sa balise BF76 ne correspond pas à la signature de l'application de l'opérateur appelant, le téléchargement est rejeté.

La section ci-dessous décrit l'activation d'une eSIM via une application d'opérateur à l'aide d'un code d'activation.

Activer l'eSIM à l'aide d'un code d'activation

Lorsque vous utilisez un code d'activation pour activer un profil eSIM, le LPA récupère un code d'activation depuis l'application de l'opérateur et télécharge le profil. Ce flux peut être initié par le LPA et le LPA peut contrôler l'intégralité du flux de l'interface utilisateur, ce qui signifie qu'aucune interface utilisateur de l'application de l'opérateur n'est affichée. Cette approche contourne la vérification des balises BF76 et les opérateurs de réseau n'ont pas besoin de mettre en œuvre l'intégralité du flux d'activation de l'interface utilisateur eSIM, y compris le téléchargement d'un profil eSIM et la gestion des erreurs.

Définition du service de provisionnement eUICC de l'opérateur

Le LPA et l'application de l'opérateur communiquent via deux interfaces AIDL : ICarrierEuiccProvisioningService et IGetActivationCodeCallback . L'application de l'opérateur doit implémenter une interface ICarrierEuiccProvisioningService et l'exposer dans sa déclaration manifeste . Le LPA doit se lier à ICarrierEuiccProvisioningService et implémenter IGetActivationCodeCallback . Pour plus d'informations sur la façon d'implémenter et d'exposer une interface AIDL, consultez Définition et interface AIDL .

Pour définir les interfaces AIDL, créez les fichiers AIDL suivants pour les applications LPA et de l'opérateur.

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

Exemple de mise en œuvre de LPA

Pour se lier à l’implémentation ICarrierEuiccProvisioningService de l’application de l’opérateur, la LPA doit copier à la fois ICarrierEuiccProvisioningService.aidl et IGetActivationCodeCallback.aidl dans votre projet et implémenter ServiceConnection .

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

Après avoir été lié à l'implémentation ICarrierEuiccProvisioningService de l'application de l'opérateur, le LPA appelle getActivationCode ou getActivationCodeForEid pour obtenir le code d'activation de l'application de l'opérateur en transmettant l'implémentation de la classe stub IGetActivationCodeCallback .

La différence entre getActivationCode et getActivationCodeForEid est que getActivationCodeForEid permet à un opérateur de pré-lier un profil à l'EID de l'appareil avant le début du processus de téléchargement.

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

Exemple d'implémentation pour une application de transporteur

Pour que le LPA se lie à l'application de l'opérateur, celle-ci doit copier à la fois ICarrierEuiccProvisioningService.aidl et IGetActivationCodeCallback.aidl dans votre projet et déclarer le service ICarrierEuiccProvisioningService dans le fichier AndroidManifest.xml . Le service doit nécessiter l'autorisation système android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS pour garantir que seul le LPA, une application privilégiée par le système, peut s'y lier. Le service doit également inclure un filtre d'intention avec l'action 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>
    

Pour implémenter le service d'application de l'opérateur AIDL, créez un service, étendez la classe Stub et implémentez les méthodes getActivationCode et getActivationCodeForEid . Le LPA peut alors appeler l’une ou l’autre méthode pour récupérer le code d’activation du profil. L'application de l'opérateur doit répondre en appelant IGetActivationCodeCallback#onSuccess avec le code d'activation si le code a été récupéré avec succès sur le serveur de l'opérateur. En cas d'échec, l'application de l'opérateur doit répondre avec 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);
              }
          }
    }
    

Démarrage de l'interface utilisateur de l'application de l'opérateur dans le flux d'activation LPA

Sur les appareils fonctionnant sous Android 11 et versions ultérieures, le LPA peut démarrer l'interface utilisateur d'une application de l'opérateur. Ceci est utile car une application d'opérateur peut exiger des informations supplémentaires de la part de l'utilisateur avant de fournir un code d'activation au LPA. Par exemple, les opérateurs peuvent exiger que les utilisateurs se connectent pour activer leurs numéros de téléphone ou effectuer d'autres services de portage.

Voici le processus de démarrage de l'interface utilisateur d'une application d'opérateur dans le LPA :

  1. Le LPA lance le flux d'activation de l'application de l'opérateur en envoyant l'intention android.service.euicc.action.START_CARRIER_ACTIVATION au package de l'application de l'opérateur contenant l'action. (Le récepteur de l'application de l'opérateur doit être protégé dans la déclaration du manifeste avec android:permission="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" pour éviter de recevoir des intentions d'applications 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'application de l'opérateur effectue son travail en utilisant sa propre interface utilisateur. Par exemple, connecter l'utilisateur ou envoyer des requêtes HTTP au backend de l'opérateur.

  3. L'application de l'opérateur répond au LPA en appelant setResult(int, Intent) et finish() .

    1. Si l'application de l'opérateur répond par RESULT_OK , le LPA poursuit le flux d'activation. Si l'application de l'opérateur détermine que l'utilisateur doit scanner un code QR au lieu de laisser le LPA lier le service de l'application de l'opérateur, l'application de l'opérateur répond au LPA en utilisant setResult(int, Intent) avec RESULT_OK et une instance Intent contenant l' android.telephony.euicc.extra.USE_QR_SCANNER défini sur true . Le LPA vérifie ensuite le supplément et lance le scanner QR au lieu de lier l'implémentation ICarrierEuiccProvisioningService de l'application de l'opérateur.
    2. Si l'application de l'opérateur plante ou répond avec RESULT_CANCELED (il s'agit du code de réponse par défaut), le LPA annule le flux d'activation eSIM.
    3. Si l'application de l'opérateur répond avec autre chose que RESULT_OK ou RESULT_CANCELED , le LPA le traite comme une erreur.

    Pour des raisons de sécurité, le LPA ne doit pas accepter directement un code d'activation fourni dans l'intention de résultat afin de garantir que les appelants non-LPA ne puissent pas obtenir de code d'activation de l'application de l'opérateur.

Lancer le flux d'activation LPA dans une application d'opérateur

À partir d'Android 11, les applications des opérateurs peuvent utiliser les API eUICC pour démarrer une LUI pour l'activation eSIM. Cette méthode fait apparaître l'interface utilisateur du flux d'activation eSIM du LPA pour activer le profil eSIM. Le LPA envoie ensuite une diffusion lorsque l'activation du profil eSIM est terminée.

  1. Le LPA doit déclarer une activité incluant un filtre d'intention avec l'action android.service.euicc.action.START_EUICC_ACTIVATION . La priorité du filtre d'intention doit être définie sur une valeur non nulle dans le cas où plusieurs implémentations sont présentes sur l'appareil. Par exemple:

    <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'application de l'opérateur effectue son travail en utilisant sa propre interface utilisateur. Par exemple, connecter l'utilisateur ou envoyer des requêtes HTTP au backend de l'opérateur.

  3. À ce stade, l'application de l'opérateur doit être prête à fournir un code d'activation via son implémentation ICarrierEuiccProvisioningService . L'application de l'opérateur lance le LPA en appelant startActivityForResult(Intent, int) avec l'action android.telephony.euicc.action.START_EUICC_ACTIVATION . Le LPA vérifie également le booléen supplémentaire android.telephony.euicc.extra.USE_QR_SCANNER . Si la valeur est true , le LPA lance le scanner QR pour permettre à l'utilisateur de scanner le code QR du profil.

  4. Du côté du LPA, le LPA se lie à l'implémentation ICarrierEuiccProvisioningService de l'application de l'opérateur pour récupérer le code d'activation et télécharger le profil correspondant. Le LPA affiche tous les éléments d'interface utilisateur nécessaires pendant le téléchargement, comme un écran de chargement.

  5. Lorsque le flux d'activation du LPA est terminé, le LPA répond à l'application de l'opérateur avec un code de résultat, que l'application de l'opérateur gère dans onActivityResult(int, int, Intent) .

    1. Si le LPA réussit à télécharger le nouveau profil eSIM, il répond avec RESULT_OK .
    2. Si l'utilisateur annule l'activation du profil eSIM dans le LPA, il répond avec RESULT_CANCELED .
    3. Si le LPA répond avec autre chose que RESULT_OK ou RESULT_CANCELED , l'application de l'opérateur traite cela comme une erreur.

    Pour des raisons de sécurité, le LPA n'accepte pas de code d'activation directement dans l'intention fournie afin de garantir que les appelants non-LPA ne puissent pas obtenir le code d'activation de l'application de l'opérateur.

Prise en charge de plusieurs eSIM

Pour les appareils exécutant Android 10 ou version ultérieure, la classe EuiccManager prend en charge les appareils dotés de plusieurs eSIM. Les appareils dotés d'un seul eSIM qui sont mis à niveau vers Android 10 ne nécessitent aucune modification de l'implémentation LPA, car la plate-forme associe automatiquement l'instance EuiccManager à l'eUICC par défaut. L'eUICC par défaut est déterminé par la plate-forme pour les appareils dotés d'une version radio HAL 1.2 ou supérieure et par le LPA pour les appareils dotés d'une version radio HAL inférieure à 1.2.

Exigences

Pour prendre en charge plusieurs eSIM, l'appareil doit disposer de plusieurs eUICC, qui peuvent être soit un eUICC intégré, soit un emplacement SIM physique dans lequel des eUICC amovibles peuvent être insérés.

Radio HAL version 1.2 ou supérieure est requise pour prendre en charge plusieurs eSIM. Radio HAL version 1.4 et RadioConfig HAL version 1.2 sont recommandées.

Mise en œuvre

Pour prendre en charge plusieurs eSIM (y compris les eUICC amovibles ou les SIM programmables), le LPA doit implémenter EuiccService , qui reçoit l'ID d'emplacement correspondant à l'ID de carte fourni par l'appelant.

La ressource non_removable_euicc_slots spécifiée dans arrays.xml est un tableau d'entiers qui représentent les ID d'emplacement des eUICC intégrés d'un périphérique. Vous devez spécifier cette ressource pour permettre à la plateforme de déterminer si un eUICC inséré est amovible ou non.

Application Carrier pour les appareils dotés de plusieurs eSIM

Lorsque vous créez une application d'opérateur pour un appareil doté de plusieurs eSIM, utilisez la méthode createForCardId dans EuiccManager pour créer un objet EuiccManager épinglé à un identifiant de carte donné. L'ID de la carte est une valeur entière qui identifie de manière unique un UICC ou un eUICC sur l'appareil.

Pour obtenir l'ID de carte pour l'eUICC par défaut de l'appareil, utilisez la méthode getCardIdForDefaultEuicc dans TelephonyManager . Cette méthode renvoie UNSUPPORTED_CARD_ID si la version radio HAL est inférieure à 1.2 et renvoie UNINITIALIZED_CARD_ID si l'appareil n'a pas lu l'eUICC.

Vous pouvez également obtenir des identifiants de carte auprès de getUiccCardsInfo et getUiccSlotsInfo (API système) dans TelephonyManager et getCardId dans SubscriptionInfo .

Lorsqu'un objet EuiccManager a été instancié avec un ID de carte spécifique, toutes les opérations sont dirigées vers l'eUICC avec cet ID de carte. Si l'eUICC devient inaccessible (par exemple, lorsqu'il est éteint ou supprimé), EuiccManager ne fonctionne plus.

Vous pouvez utiliser les exemples de code suivants pour créer une application d'opérateur.

Exemple 1 : Obtenez un abonnement actif et instancier 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);

Exemple 2 : parcourir les UICC et instancier EuiccManager pour un eUICC amovible

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

Validation

AOSP n'est pas livré avec une implémentation LPA et vous n'êtes pas censé disposer d'un LPA disponible sur toutes les versions d'Android (tous les téléphones ne prennent pas en charge eSIM). Pour cette raison, il n’existe pas de cas de test CTS de bout en bout. Cependant, des cas de test de base sont disponibles dans AOSP pour garantir que les API eUICC exposées sont valides dans les versions Android.

Vous devez vous assurer que les versions réussissent les cas de test CTS suivants (pour les API publiques) : /platform/cts/tests/tests/telephony/current/src/android/telephony/euicc/cts .

Les transporteurs qui mettent en œuvre une application pour transporteur doivent suivre leurs cycles normaux d'assurance qualité en interne pour garantir que toutes les fonctionnalités mises en œuvre fonctionnent comme prévu. Au minimum, l'application de l'opérateur doit être capable de répertorier tous les profils d'abonnement appartenant au même opérateur, de télécharger et d'installer un profil, d'activer un service sur le profil, de basculer entre les profils et de supprimer des profils.

Si vous créez votre propre LPA, vous devriez passer des tests beaucoup plus rigoureux. Vous devez travailler avec votre fournisseur de modem, votre fournisseur de puce eUICC ou de système d'exploitation eSIM, vos fournisseurs SM-DP+ et vos opérateurs pour résoudre les problèmes et garantir l'interopérabilité de votre LPA au sein de l'architecture RSP. Une bonne quantité de tests manuels est inévitable. Pour une meilleure couverture de test, vous devez suivre le plan de test GSMA SGP.23 RSP .