Implémenter une eSIM

La technologie 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 carte SIM physique. Il s'agit d'une spécification globale pilotée par la GSMA qui permet le provisionnement de SIM à distance (RSP) pour n'importe quel appareil mobile. À partir d'Android 9, le framework Android fournit des API standards pour accéder à l'eSIM et gérer les profils d'abonnement sur l'eSIM. Ces API eUICC permettent aux tiers de développer leurs propres applications d'opérateur et assistants de profil local (LPA) sur les appareils Android compatibles avec l'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 la LPA, car elle sert de pont entre le SM-DP+ (service à distance qui prépare, stocke et distribue des packages de profils aux appareils) et la puce eUICC. L'APK LPA peut éventuellement inclure un composant d'interface utilisateur, appelé UI LPA ou LUI, afin de fournir un emplacement central permettant à l'utilisateur final de gérer tous les profils d'abonnement intégrés. Le framework Android détecte et se connecte automatiquement au meilleur LPA disponible, et achemine toutes les opérations eUICC via une instance LPA.

Architecture simplifiée du provisionnement de la carte SIM à distance (RSP, Remote SIM Provisioning)

Figure 1 : Architecture RSP simplifiée

Les opérateurs de réseaux mobiles qui souhaitent créer une application opérateur doivent consulter les API de EuiccManager, qui fournissent des opérations de gestion de profil de haut niveau telles que downloadSubscription(), switchToSubscription() et deleteSubscription().

Si vous êtes un OEM d'appareil et que vous souhaitez créer votre propre application système LPA, vous devez étendre EuiccService pour que le framework Android puisse se connecter à vos services LPA. En outre, vous devez utiliser les API de EuiccCardManager, qui fournissent des fonctions ES10x basées sur GSMA RSP v2.0. Ces fonctions permettent d'envoyer des commandes à la puce eUICC, telles que prepareDownload(), loadBoundProfilePackage(), retrieveNotificationList() et resetMemory().

Pour fonctionner, les API de EuiccManager nécessitent une application LPA correctement implémentée, et l'appelant des API EuiccCardManager doit être une LPA. Cette règle est appliquée par le framework Android.

Les appareils équipés d'Android 10 ou version ultérieure peuvent prendre en charge les appareils avec plusieurs eSIM. Pour en savoir plus, consultez la section Compatibilité avec plusieurs eSIM.

Créer une application de l'opérateur

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

EuiccManager

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

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

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

Vous devez vérifier si l'eSIM est compatible avec l'appareil avant d'effectuer des opérations 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 de l'OS eSIM:

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

De nombreuses API, telles que downloadSubscription() et switchToSubscription(), utilisent des 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 de l'LPA en tant que EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, ce qui permet à l'application du transporteur de suivre à 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 code 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);

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 donné en fonction de son ID:

// 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 obtenir la liste complète des API EuiccManager et des exemples de code, consultez la page API eUICC.

Erreurs résolubles

Dans certains cas, le système ne parvient pas à effectuer l'opération eSIM, mais l'utilisateur peut résoudre l'erreur. Par exemple, downloadSubscription peut échouer si les métadonnées du profil indiquent qu'un code de confirmation de l'opérateur est requis. Sinon, 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 qu'elle ne dispose pas de privilèges d'opérateur sur le profil actuellement activé, et que 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. Le rappel Intent contient des extras internes de sorte que lorsque l'appelant le transmet à EuiccManager#startResolutionActivity, la résolution peut être demandée via l'interface utilisateur. Par exemple, si vous utilisez à nouveau le code de confirmation, EuiccManager#startResolutionActivity déclenche un écran de LUI permettant à l'utilisateur de saisir un code de confirmation. Une fois le code saisi, l'opération de téléchargement reprend. Cette approche donne à l'application de l'opérateur un contrôle total sur le moment où l'UI s'affiche, mais fournit à la LPA/LUI une méthode extensible pour ajouter de nouvelles solutions aux problèmes récupérables par l'utilisateur à l'avenir, sans que les applications clientes aient besoin d'être modifiées.

Android 9 définit ces erreurs pouvant être résolues dans EuiccService, que l'UI 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";

Droits de l'opérateur

Si vous êtes un opérateur et que vous développez votre propre application qui appelle EuiccManager pour télécharger des profils sur un appareil, votre profil doit inclure des règles d'accès de l'opérateur correspondant à votre application 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 de l'opérateur ne doit être autorisée qu'à accéder aux profils appartenant à cet opérateur. Par exemple, l'opérateur A ne doit pas pouvoir télécharger, activer ni désactiver un profil appartenant à l'opérateur B.

Pour s'assurer qu'un profil n'est accessible qu'à son propriétaire, Android utilise un mécanisme permettant d'accorder des droits 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 accorde aux applications signées par ces certificats l'autorisation d'effectuer des appels aux API EuiccManager. Le processus général est décrit ci-dessous:

  1. L'opérateur signe l'APK de l'application de l'opérateur ; l'outil apksigner associe 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 du transporteur (obligatoire)
    2. Nom du package de l'application du transporteur (recommandé)
  3. L'application de l'opérateur tente d'effectuer une opération eUICC avec 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 le formulaire de récupération de compte, 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) validés, le privilège du transporteur est accordé à l'application appelante sur le profil cible.

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

L'OS 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 correspondre aux mêmes règles de privilèges de l'opérateur que celles renvoyées par l'applet de règles d'accès (ARA) définies dans les privilèges de l'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 en savoir plus sur la signature d'application, consultez la section Signer votre application. Pour en savoir plus sur les droits de l'opérateur, consultez la section Droits de l'opérateur 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 associé 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/configuration requise pour le modem

Le LPA et l'OS eSIM de la puce eUICC doivent être compatibles avec au moins la version 2.0 ou 2.2 du RSP (Remote SIM Provisioning) GSMA. Vous devez également planifier d'utiliser des serveurs SM-DP+ et SM-DS avec une version RSP correspondante. Pour en savoir plus sur l'architecture RSP, consultez la spécification de l'architecture RSP GSMA SGP.21.

De plus, pour s'intégrer aux API eUICC d'Android 9, le modem de l'appareil doit envoyer des fonctionnalités de terminal avec la prise en charge des fonctionnalités eUICC encodées (gestion et téléchargement de profils locaux). Il doit également implémenter les méthodes suivantes:

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

  • IRadioConfig HAL v1.0: getSimSlotsStatus

  • IRadioConfig AIDL v1.0: getAllowedCarriers

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

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

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

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

Pour les appareils équipés d'Android 10, vous devez définir un tableau d'ID d'emplacements eUICC non amovibles. 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 obtenir la liste complète des exigences concernant les modems, consultez la section Exigences concernant les modems pour la prise en charge de l'eSIM.

EuiccService

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

Pour implémenter le backend LPA, vous devez étendre EuiccService et déclarer ce service dans votre fichier manifeste. Le service doit exiger l'autorisation système android.permission.BIND_EUICC_SERVICE pour garantir que seul le système peut s'y associer. Le service doit également inclure un filtre d'intent avec l'action android.service.euicc.EuiccService. La priorité du filtre d'intent doit être définie sur une valeur non nulle au cas où plusieurs implémentations seraient présentes sur l'appareil. 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 la LPA active et interagit avec elle si nécessaire pour prendre en charge les API eUICC Android. 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 compatibilité LPA est désactivée.

Pour implémenter la 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 comporter un filtre d'intent avec l'action appropriée, la catégorie android.service.euicc.category.EUICC_UI et une priorité non nulle. Pour choisir les implémentations de ces activités, la logique est la même que pour l'implémentation de EuiccService. 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'UI implémentant ces écrans peut provenir d'un APK différent de celui qui implémente EuiccService. Le choix d'utiliser un seul APK ou plusieurs APK (par exemple, un qui implémente EuiccService et un 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 (comme décrit 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 qui ne peut être appelée que par des applications privilégiées du système.

Applications d&#39;opérateur, LPA et API 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. Cette règle est appliquée 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.

Comme pour 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);

Pour obtenir tous les profils de 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 du téléphone) via une interface AIDL, et chaque méthode EuiccCardManager reçoit son rappel du processus du téléphone via une interface AIDL différente et dédiée. Lorsque vous utilisez les API EuiccCardManager, l'appelant (LPA) doit fournir un objet Executor via lequel le rappel est appelé. 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 schéma 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 la liste des notifications des événements de notification donnés:

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

Activer un profil eSIM via une application de l'opérateur

Sur les appareils équipés d'Android 9 ou d'une version ultérieure, vous pouvez utiliser une application de l'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 à l'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 encode les règles de droit de l'opérateur pour le profil. Si un profil ne comporte 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 refusé.

La section ci-dessous décrit l'activation d'une eSIM via l'application d'un 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 à partir de l'application de l'opérateur et télécharge le profil. Ce flux peut être lancé par la LPA, qui peut contrôler l'ensemble du parcours de l'UI, ce qui signifie qu'aucune UI d'application de l'opérateur n'est affichée. Cette approche contourne la vérification du tag BF76, et les opérateurs réseau n'ont pas besoin d'implémenter l'intégralité du parcours d'activation de l'eSIM, y compris le téléchargement d'un profil eSIM et la gestion des erreurs.

Définir le service de provisionnement eUICC de l'opérateur

L'application 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 de fichier manifeste. L'LPA doit se lier à ICarrierEuiccProvisioningService et implémenter IGetActivationCodeCallback. Pour en savoir plus sur l'implémentation et l'exposition d'une interface AIDL, consultez la section Définir une interface AIDL.

Pour définir les interfaces AIDL, créez les fichiers AIDL suivants à la fois pour l'application LPA et les applications 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 d'implémentation d'une 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, puis implémenter ServiceConnection.

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

Après s'être lié à l'implémentation ICarrierEuiccProvisioningService de l'application du transporteur, la LPA appelle getActivationCode ou getActivationCodeForEid pour obtenir le code d'activation de l'application du transporteur en transmettant l'implémentation de la classe de bouchon 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 que le processus de téléchargement ne commence.

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 d'opérateur

Pour que l'application LPA s'associe à l'application de l'opérateur, celle-ci doit copier ICarrierEuiccProvisioningService.aidl et IGetActivationCodeCallback.aidl dans votre projet et déclarer le service ICarrierEuiccProvisioningService dans le fichier AndroidManifest.xml. Le service doit exiger l'autorisation système android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS pour s'assurer que seule l'application LPA, une application privilégiée par le système, peut s'y associer. Le service doit également inclure un filtre d'intent 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. L'LPA peut ensuite 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é extrait du serveur de l'opérateur. Si l'opération échoue, l'application de l'opérateur doit répondre par 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émarrer l'UI de l'application de l'opérateur dans le flux d'activation de l'LPA

Sur les appareils équipés d'Android 11 ou version ultérieure, le LPA peut lancer l'interface utilisateur d'une application d'opérateur. Cela est utile, car une application de l'opérateur peut nécessiter des informations supplémentaires de la part de l'utilisateur avant de fournir un code d'activation à l'LPA. Par exemple, les opérateurs peuvent demander aux utilisateurs de se connecter pour activer leurs numéros de téléphone ou effectuer d'autres services de transfert.

Voici la procédure à suivre pour démarrer l'UI d'une application de l'opérateur dans la LPA:

  1. La LPA lance le flux d'activation de l'application de l'opérateur en envoyant l'intent 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 du transporteur doit être protégé dans la déclaration du fichier manifeste avec android:permission="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" pour éviter de recevoir des intents d'applications autres que des 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 à l'aide de sa propre interface utilisateur. Par exemple, la connexion de l'utilisateur ou l'envoi de requêtes HTTP au backend du transporteur.

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

    1. Si l'application de l'opérateur répond avec RESULT_OK, la 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, elle 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 supplémentaire booléen défini sur true. La LPA vérifie ensuite l'extra et lance le lecteur de code QR au lieu de lier l'implémentation ICarrierEuiccProvisioningService de l'application du transporteur.
    2. Si l'application de l'opérateur plante ou répond avec RESULT_CANCELED (code de réponse par défaut), la LPA annule le flux d'activation de l'eSIM.
    3. Si l'application du transporteur répond avec un autre code que RESULT_OK ou RESULT_CANCELED, la LPA le considère comme une erreur.

    Pour des raisons de sécurité, l'LPA ne doit pas accepter directement un code d'activation fourni dans l'intent de résultat afin de s'assurer que les appelants autres que l'LPA ne peuvent pas obtenir de code d'activation auprès de l'application de l'opérateur.

Lancer le flux d'activation de l'application de l'opérateur

À partir d'Android 11, les applications de l'opérateur peuvent utiliser les API eUICC pour démarrer une UIL pour l'activation de l'eSIM. Cette méthode affiche l'interface utilisateur du flux d'activation eSIM de la 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é comprenant un filtre d'intent avec l'action android.service.euicc.action.START_EUICC_ACTIVATION. La priorité du filtre d'intent doit être définie sur une valeur non nulle dans le cas où plusieurs implémentations sont présentes sur l'appareil. 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 à l'aide de sa propre interface utilisateur. Par exemple, la connexion de l'utilisateur ou l'envoi de requêtes HTTP au backend du transporteur.

  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 la LPA en appelant startActivityForResult(Intent, int) avec l'action android.telephony.euicc.action.START_EUICC_ACTIVATION. Le LPA vérifie également l'android.telephony.euicc.extra.USE_QR_SCANNER supplémentaire booléen. Si la valeur est true, la LPA lance le lecteur de code QR pour permettre à l'utilisateur de scanner le code QR du profil.

  4. Côté LPA, la 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. L'application LPA affiche tous les éléments d'interface utilisateur nécessaires pendant le téléchargement, tels qu'un écran de chargement.

  5. Une fois le flux d'activation de l'LPA terminé, l'LPA répond à l'application du transporteur avec un code de résultat, que l'application du transporteur 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 la LPA répond avec un autre code que RESULT_OK ou RESULT_CANCELED, l'application du transporteur considère cela comme une erreur.

    Pour des raisons de sécurité, l'LPA n'accepte pas de code d'activation directement dans l'intent fourni pour s'assurer que les appelants autres que l'LPA ne peuvent pas obtenir le code d'activation de l'application de l'opérateur.

Prise en charge de plusieurs eSIM

Pour les appareils équipés d'Android 10 ou version ultérieure, la classe EuiccManager est compatible avec les appareils dotés de plusieurs eSIM. Les appareils équipés d'une seule eSIM qui passent à Android 10 ne nécessitent aucune modification de l'implémentation de la LPA, car la plate-forme associe automatiquement l'instance EuiccManager à l'eUICC par défaut. L'eUICC par défaut est déterminée par la plate-forme pour les appareils équipés de la version 1.2 ou ultérieure du HAL radio et par la LPA pour les appareils équipés de versions antérieures à la version 1.2 du HAL radio.

Conditions requises

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

La version 1.2 ou ultérieure de Radio HAL est requise pour prendre en charge plusieurs eSIM. Les versions 1.4 de Radio HAL et 1.2 de RadioConfig HAL sont recommandées.

Implémentation

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

La ressource non_removable_euicc_slots spécifiée dans arrays.xml est un tableau d'entiers représentant les ID de fente des eUICC intégrées d'un appareil. Vous devez spécifier cette ressource pour permettre à la plate-forme de déterminer si une eUICC insérée est amovible ou non.

Application de l'opérateur pour un appareil avec plusieurs eSIM

Lorsque vous créez une application de l'opérateur pour un appareil doté de plusieurs eSIM, utilisez la méthode createForCardId dans EuiccManager pour créer un objet EuiccManager épinglé à un ID de carte donné. L'ID de 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 la 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 du HAL radio est inférieure à 1.2 et UNINITIALIZED_CARD_ID si l'appareil n'a pas lu l'eUICC.

Vous pouvez également obtenir des ID de carte à partir de getUiccCardsInfo et getUiccSlotsInfo (API système) dans TelephonyManager, et de 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 désactivé ou supprimé), EuiccManager ne fonctionne plus.

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

Exemple 1: Obtenir 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: Itérer sur les UICC et instancier EuiccManager pour une 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 fourni avec une implémentation LPA et vous ne devriez pas avoir de LPA disponible sur tous les builds Android (tous les téléphones ne sont pas compatibles avec eSIM). Pour cette raison, il n'existe pas de scénarios de test CTS de bout en bout. Toutefois, des scénarios de test de base sont disponibles dans AOSP pour garantir que les API eUICC exposées sont valides dans les builds Android.

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

Les opérateurs qui implémentent une application doivent suivre leurs cycles d'assurance qualité internes habituels pour s'assurer que toutes les fonctionnalités implémentées fonctionnent comme prévu. Au minimum, l'application de l'opérateur doit pouvoir lister tous les profils d'abonnement appartenant au même opérateur, télécharger et installer un profil, activer un service sur le profil, passer d'un profil à l'autre et supprimer des profils.

Si vous créez votre propre LPA, vous devez effectuer des tests beaucoup plus rigoureux. Vous devez collaborer avec votre fournisseur de modem, le fournisseur de la puce eUICC ou de l'OS eSIM, les fournisseurs SM-DP+ et les opérateurs pour résoudre les problèmes et assurer l'interopérabilité de votre LPA dans l'architecture RSP. Une bonne quantité de tests manuels est inévitable. Pour une couverture de test optimale, vous devez suivre le plan de test RSP SGP.23 de la GSMA.