Implementación de eSIM

La tecnología SIM integrada (eSIM o eUICC) permite a los usuarios de dispositivos móviles descargar un perfil de operador y activar el servicio de un operador sin tener una tarjeta SIM física. Es una especificación global impulsada por GSMA que permite el aprovisionamiento remoto de SIM (RSP) de cualquier dispositivo móvil. A partir de Android 9, el marco de Android proporciona API estándar para acceder a la eSIM y administrar perfiles de suscripción en la eSIM. Estas API de eUICC permiten a terceros desarrollar sus propias aplicaciones de operador y asistentes de perfil local (LPA) en dispositivos Android habilitados para eSIM.

LPA es una aplicación de sistema independiente que debe incluirse en la imagen de compilación de Android. La gestión de los perfiles en la eSIM generalmente la realiza la LPA, ya que sirve como puente entre el SM-DP+ (servicio remoto que prepara, almacena y entrega paquetes de perfiles a los dispositivos) y el chip eUICC. El APK de LPA puede incluir opcionalmente un componente de interfaz de usuario, llamado LPA UI o LUI, para proporcionar un lugar central para que el usuario final administre todos los perfiles de suscripción integrados. El marco de Android descubre y se conecta automáticamente al mejor LPA disponible y enruta todas las operaciones de eUICC a través de una instancia de LPA.

Arquitectura simplificada de aprovisionamiento remoto de SIM (RSP)

Figura 1. Arquitectura RSP simplificada

Los operadores de redes móviles interesados ​​en crear una aplicación de operador deben consultar las API en EuiccManager , que proporciona operaciones de administración de perfiles de alto nivel, como downloadSubscription() , switchToSubscription() y deleteSubscription() .

Si es un OEM de dispositivos interesado en crear su propia aplicación del sistema LPA, debe extender EuiccService para que el marco de Android se conecte a sus servicios LPA. Además, debe utilizar las API en EuiccCardManager , que proporcionan funciones ES10x basadas en GSMA RSP v2.0. Estas funciones se utilizan para emitir comandos al chip eUICC, como prepareDownload() , loadBoundProfilePackage() , retrieveNotificationList() y resetMemory() .

Las API de EuiccManager requieren una aplicación LPA implementada correctamente para funcionar y la persona que llama a las API de EuiccCardManager debe ser un LPA. Esto lo aplica el marco de Android.

Los dispositivos con Android 10 o superior pueden admitir dispositivos con varias eSIM. Para obtener más información, consulte Compatibilidad con varias eSIM .

Crear una aplicación de operador

Las API eUICC en Android 9 hacen posible que los operadores de redes móviles creen aplicaciones con la marca del operador para administrar sus perfiles directamente. Esto incluye descargar y eliminar perfiles de suscripción propiedad del operador, así como cambiar a un perfil propiedad de un operador.

Administrador de Euicc

EuiccManager es el principal punto de entrada para que las aplicaciones interactúen con LPA. Esto incluye aplicaciones del operador que descargan, eliminan y cambian a suscripciones propiedad del operador. Esto también incluye la aplicación del sistema LUI, que proporciona una ubicación/UI central para administrar todas las suscripciones integradas y puede ser una aplicación independiente de la que proporciona EuiccService .

Para utilizar las API públicas, una aplicación de operador primero debe obtener la instancia de EuiccManager a través de Context#getSystemService :

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

Debe verificar si eSIM es compatible con el dispositivo antes de realizar cualquier operación con eSIM. EuiccManager#isEnabled() generalmente devuelve true si la función android.hardware.telephony.euicc está definida y hay un paquete LPA presente.

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

Para obtener información sobre el hardware eUICC y la versión del sistema operativo eSIM:

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

Muchas API, como downloadSubscription() y switchToSubscription() , utilizan devoluciones de llamada PendingIntent , ya que pueden tardar segundos o incluso minutos en completarse. PendingIntent se envía con un código de resultado en el espacio EuiccManager#EMBEDDED_SUBSCRIPTION_RESULT_ , que proporciona códigos de error definidos por el marco, así como un código de resultado detallado arbitrario propagado desde LPA como EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE , lo que permite que la aplicación del operador realice un seguimiento con fines de registro/depuración. La devolución de llamada PendingIntent debe ser BroadcastReceiver .

Para descargar una suscripción descargable determinada (creada a partir de un código de activación o un código 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);

Defina y use el permiso en AndroidManifest.xml :

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

Para cambiar a una suscripción según el ID de suscripción:

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

Para obtener una lista completa de las API EuiccManager y ejemplos de código, consulte API de eUICC .

Errores resolubles

Hay algunos casos en los que el sistema no puede completar la operación de la eSIM pero el usuario puede resolver el error. Por ejemplo, downloadSubscription puede fallar si los metadatos del perfil indican que se requiere un código de confirmación del operador . O bien, switchToSubscription puede fallar si la aplicación del operador tiene privilegios de operador sobre el perfil de destino (es decir, el operador es propietario del perfil) pero no tiene privilegios de operador sobre el perfil actualmente habilitado y, por lo tanto, se requiere el consentimiento del usuario.

Para estos casos, la devolución de llamada de la persona que llama se llama con EuiccManager#EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR . La Intent de devolución de llamada contiene extras internos, de modo que cuando la persona que llama la pasa a EuiccManager#startResolutionActivity , se puede solicitar la resolución a través de la LUI. Usando nuevamente el código de confirmación, por ejemplo, EuiccManager#startResolutionActivity activa una pantalla LUI que permite al usuario ingresar un código de confirmación; Después de ingresar el código, se reanuda la operación de descarga. Este enfoque proporciona a la aplicación del operador control total sobre cuándo se muestra la interfaz de usuario, pero le brinda a LPA/LUI un método extensible para agregar un nuevo manejo de problemas recuperables por el usuario en el futuro sin necesidad de cambiar las aplicaciones del cliente.

Android 9 define estos errores resolubles en EuiccService , que la LUI debe manejar:

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

Privilegios del transportista

Si es un operador que desarrolla su propia aplicación de operador que llama a EuiccManager para descargar perfiles en un dispositivo, su perfil debe incluir reglas de privilegios de operador correspondientes a su aplicación de operador en los metadatos. Esto se debe a que los perfiles de suscripción que pertenecen a diferentes operadores pueden coexistir en la eUICC de un dispositivo, y cada aplicación de operador solo debe tener acceso a los perfiles que pertenecen a ese operador. Por ejemplo, el operador A no debería poder descargar, habilitar o deshabilitar un perfil propiedad del operador B.

Para garantizar que solo su propietario pueda acceder a un perfil, Android utiliza un mecanismo para otorgar privilegios especiales a la aplicación del propietario del perfil (es decir, la aplicación del operador). La plataforma Android carga los certificados almacenados en el archivo de reglas de acceso (ARF) del perfil y otorga permiso a las aplicaciones firmadas por estos certificados para realizar llamadas a las API EuiccManager . El proceso de alto nivel se describe a continuación:

  1. El operador firma el APK de la aplicación del operador; la herramienta apksigner adjunta el certificado de clave pública al APK.
  2. Operador/SM-DP+ prepara un perfil y sus metadatos, que incluyen un ARF que contiene:

    1. Firma (SHA-1 o SHA-256) del certificado de clave pública de la aplicación del operador (obligatorio)
    2. Nombre del paquete de la aplicación del operador (muy recomendable)
  3. La aplicación Carrier intenta realizar una operación eUICC a través de la API EuiccManager .

  4. La plataforma Android verifica que el hash SHA-1 o SHA-256 del certificado de la aplicación que llama coincide con la firma del certificado obtenido del ARF del perfil de destino. Si el nombre del paquete de la aplicación del operador está incluido en el ARF, también debe coincidir con el nombre del paquete de la aplicación de la persona que llama.

  5. Después de verificar la firma y el nombre del paquete (si está incluido), se otorga el privilegio de operador a la aplicación que llama sobre el perfil de destino.

Debido a que los metadatos del perfil pueden estar disponibles fuera del perfil mismo (para que LPA pueda recuperar los metadatos del perfil de SM-DP+ antes de descargar el perfil, o de ISD-R cuando el perfil está deshabilitado), debe contener las mismas reglas de privilegios del operador. como en el perfil.

El sistema operativo eUICC y SM-DP+ deben admitir una etiqueta patentada BF76 en los metadatos del perfil. El contenido de la etiqueta debe tener las mismas reglas de privilegios del operador que devuelve el subprograma de reglas de acceso (ARA) definido en Privilegios del operador 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
    }
}

Para obtener más detalles sobre la firma de aplicaciones, consulte Firme su aplicación . Para obtener detalles sobre los privilegios del operador, consulte Privilegios del operador UICC .

Crear una aplicación de asistente de perfil local

Los fabricantes de dispositivos pueden implementar su propio asistente de perfil local (LPA), que debe estar conectado con las API Euicc de Android. Las siguientes secciones brindan una breve descripción general sobre cómo crear una aplicación LPA e integrarla con el sistema Android.

Requisitos de hardware/módem

El LPA y el sistema operativo eSIM en el chip eUICC deben admitir al menos GSMA RSP (Remote SIM Provisioning) v2.0 o v2.2. También debe planificar el uso de servidores SM-DP+ y SM-DS que tengan una versión RSP coincidente. Para obtener una arquitectura RSP detallada, consulte la especificación de arquitectura RSP GSMA SGP.21 .

Además, para integrarse con las API de eUICC en Android 9, el módem del dispositivo debe enviar capacidades de terminal con soporte para capacidades de eUICC codificadas (administración de perfiles locales y descarga de perfiles). También necesita implementar los siguientes métodos:

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

  • IRadioConfig HAL v1.0: getSimSlotsStatus

  • IRadioConfig AIDL v1.0: getAllowedCarriers

    Google LPA necesita conocer el estado de bloqueo del operador para poder permitir la descarga o transferencia de eSIM solo para el operador permitido. De lo contrario, los usuarios pueden terminar descargando y transfiriendo una SIM y luego darse cuenta de que el dispositivo está bloqueado por un operador diferente.

    • Los proveedores u OEM deben implementar la API HAL IRadioSim.getAllowedCarriers().

    • El proveedor RIL/módem deberá completar el estado de bloqueo y el ID del operador donde está bloqueado el dispositivo como parte de la API HAL IRadioSimResponse.getAllowedCarriersResponse().

El módem debe reconocer la eSIM con el perfil de inicio predeterminado habilitado como una SIM válida y mantener la SIM encendida.

Para dispositivos que ejecutan Android 10, se debe definir una matriz de ID de ranura eUICC no extraíble. Por ejemplo, consulte 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>

Para obtener una lista completa de los requisitos del módem, consulte Requisitos del módem para compatibilidad con eSIM .

Servicio Euicc

Un LPA consta de dos componentes separados (ambos pueden implementarse en el mismo APK): el backend de LPA y la interfaz de usuario o LUI de LPA.

Para implementar el backend de LPA, debe extender EuiccService y declarar este servicio en su archivo de manifiesto. El servicio debe requerir el permiso del sistema android.permission.BIND_EUICC_SERVICE para garantizar que solo el sistema pueda vincularse a él. El servicio también debe incluir un filtro de intención con la acción android.service.euicc.EuiccService . La prioridad del filtro de intención debe establecerse en un valor distinto de cero en caso de que haya varias implementaciones presentes en el dispositivo. Por ejemplo:

<service
    android:name=".EuiccServiceImpl"
    android:permission="android.permission.BIND_EUICC_SERVICE">
    <intent-filter android:priority="100">
        <action android:name="android.service.euicc.EuiccService" />
    </intent-filter>
</service>

Internamente, el marco de Android determina el LPA activo e interactúa con él según sea necesario para admitir las API eUICC de Android. Se consulta PackageManager para todas las aplicaciones con el permiso android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS , que especifica un servicio para la acción android.service.euicc.EuiccService . Se selecciona el servicio con mayor prioridad. Si no se encuentra ningún servicio, el soporte LPA está deshabilitado.

Para implementar la LUI, debe proporcionar una actividad para las siguientes acciones:

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

Al igual que con el servicio, cada actividad debe requerir el permiso del sistema android.permission.BIND_EUICC_SERVICE . Cada uno debe tener un filtro de intención con la acción adecuada, la categoría android.service.euicc.category.EUICC_UI y una prioridad distinta de cero. Se utiliza una lógica similar para elegir las implementaciones de estas actividades como para elegir la implementación de EuiccService . Por ejemplo:

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

Esto implica que la UI que implementa estas pantallas puede provenir de un APK diferente al que implementa EuiccService . Tener un único APK o varios APK (por ejemplo, uno que implemente EuiccService y otro que proporcione actividades LUI) es una opción de diseño.

Administrador de tarjetas Euicc

EuiccCardManager es la interfaz para comunicarse con el chip eSIM. Proporciona funciones ES10 (como se describe en la especificación GSMA RSP) y maneja los comandos de solicitud/respuesta de APDU de bajo nivel, así como el análisis ASN.1. EuiccCardManager es una API del sistema y solo pueden llamarla aplicaciones con privilegios del sistema.

Aplicaciones de operador, LPA y API Euicc

Figura 2. Tanto la aplicación del operador como la LPA utilizan las API de Euicc

Las API de operación de perfil a través de EuiccCardManager requieren que la persona que llama sea un LPA. Esto lo aplica el marco de Android. Esto significa que la persona que llama debe extender EuiccService y declararse en su archivo de manifiesto, como se describe en las secciones anteriores.

De manera similar a EuiccManager , para usar las API EuiccCardManager , su LPA primero debe obtener la instancia de EuiccCardManager a través de Context#getSystemService :

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

Luego, para obtener todos los perfiles en la 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 se vincula a EuiccCardController (que se ejecuta en el proceso del teléfono) a través de una interfaz AIDL, y cada método EuiccCardManager recibe su devolución de llamada del proceso del teléfono a través de una interfaz AIDL dedicada diferente. Cuando se utilizan las API EuiccCardManager , la persona que llama (LPA) debe proporcionar un objeto Executor a través del cual se invoca la devolución de llamada. Este objeto Executor puede ejecutarse en un solo subproceso o en un grupo de subprocesos de su elección.

La mayoría de las API EuiccCardManager tienen el mismo patrón de uso. Por ejemplo, para cargar un paquete de perfil vinculado en la eUICC:

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

Para cambiar a un perfil diferente con un ICCID determinado:

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

Para obtener la dirección SM-DP+ predeterminada del chip eUICC:

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

Para recuperar una lista de notificaciones de los eventos de notificación dados:

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

Activar un perfil eSIM a través de una aplicación de operador

En dispositivos con Android 9 o superior, puede utilizar una aplicación de operador para activar la eSIM y descargar perfiles. La aplicación del operador puede descargar perfiles llamando directamente a downloadSubscription o proporcionando un código de activación a la LPA.

Cuando una aplicación de operador descarga un perfil llamando downloadSubscription , la llamada exige que la aplicación pueda administrar el perfil a través de una etiqueta de metadatos BF76 que codifica las reglas de privilegios del operador para el perfil. Si un perfil no tiene una etiqueta BF76 o si su etiqueta BF76 no coincide con la firma de la aplicación del operador que llama, se rechaza la descarga.

La siguiente sección describe la activación de una eSIM a través de una aplicación de operador usando un código de activación.

Activar eSIM usando un código de activación

Cuando se utiliza un código de activación para activar un perfil eSIM, la LPA obtiene un código de activación de la aplicación del operador y descarga el perfil. Este flujo puede ser iniciado por la LPA y la LPA puede controlar todo el flujo de la interfaz de usuario, lo que significa que no se muestra ninguna interfaz de usuario de la aplicación del operador. Este enfoque omite la verificación de la etiqueta BF76 y los operadores de red no necesitan implementar todo el flujo de la interfaz de usuario de activación de eSIM, incluida la descarga de un perfil de eSIM y el manejo de errores.

Definición del servicio de aprovisionamiento eUICC del operador

La LPA y la aplicación del operador se comunican a través de dos interfaces AIDL : ICarrierEuiccProvisioningService e IGetActivationCodeCallback . La aplicación del operador debe implementar una interfaz ICarrierEuiccProvisioningService y exponerla en su declaración de manifiesto . La LPA debe vincularse a ICarrierEuiccProvisioningService e implementar IGetActivationCodeCallback . Para obtener más información sobre cómo implementar y exponer una interfaz AIDL, consulte Definición de una interfaz AIDL .

Para definir las interfaces AIDL, cree los siguientes archivos AIDL para las aplicaciones LPA y Carrier.

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

Ejemplo de implementación de LPA

Para vincularse a la implementación ICarrierEuiccProvisioningService de la aplicación del operador, la LPA debe copiar tanto ICarrierEuiccProvisioningService.aidl como IGetActivationCodeCallback.aidl a su proyecto e implementar ServiceConnection .

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

Después de vincularse a la implementación ICarrierEuiccProvisioningService de la aplicación del operador, LPA llama a getActivationCode o getActivationCodeForEid para obtener el código de activación de la aplicación del operador pasando la implementación de la clase auxiliar IGetActivationCodeCallback .

La diferencia entre getActivationCode y getActivationCodeForEid es que getActivationCodeForEid permite que un operador vincule previamente un perfil al EID del dispositivo antes de que comience el proceso de descarga.

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

Implementación de ejemplo para aplicación de operador

Para que LPA se vincule a la aplicación del operador, la aplicación del operador debe copiar ICarrierEuiccProvisioningService.aidl e IGetActivationCodeCallback.aidl a su proyecto y declarar el servicio ICarrierEuiccProvisioningService en el archivo AndroidManifest.xml . El servicio debe requerir el permiso del sistema android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS para garantizar que solo LPA, una aplicación con privilegios del sistema, pueda vincularse a él. El servicio también debe incluir un filtro de intención con la acción 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>
    

Para implementar el servicio de aplicación del operador AIDL, cree un servicio, extienda la clase Stub e implemente los métodos getActivationCode y getActivationCodeForEid . Luego, la LPA puede llamar a cualquiera de los métodos para obtener el código de activación del perfil. La aplicación del operador debe responder llamando IGetActivationCodeCallback#onSuccess con el código de activación si el código se obtuvo correctamente del servidor del operador. Si no tiene éxito, la aplicación del operador debería responder 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);
              }
          }
    }
    

Iniciar la interfaz de usuario de la aplicación del operador en el flujo de activación de LPA

En dispositivos con Android 11 y versiones posteriores, LPA puede iniciar la interfaz de usuario de una aplicación de operador. Esto es útil ya que una aplicación de operador puede requerir información adicional del usuario antes de proporcionar un código de activación a la LPA. Por ejemplo, los operadores pueden exigir a los usuarios que inicien sesión para activar sus números de teléfono o realizar otros servicios de portabilidad.

Este es el proceso para iniciar la interfaz de usuario de una aplicación de operador en LPA:

  1. La LPA inicia el flujo de activación de la aplicación del operador enviando la intención android.service.euicc.action.START_CARRIER_ACTIVATION al paquete de la aplicación del operador que contiene la acción. (El receptor de la aplicación del operador debe estar protegido en la declaración del manifiesto con android:permission="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" para evitar recibir intents de aplicaciones que no sean 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. La aplicación del operador hace su trabajo utilizando su propia interfaz de usuario. Por ejemplo, iniciar sesión como usuario o enviar solicitudes HTTP al backend del operador.

  3. La aplicación del operador responde a la LPA llamando setResult(int, Intent) y finish() .

    1. Si la aplicación del operador responde con RESULT_OK , la LPA continúa el flujo de activación. Si la aplicación del operador determina que el usuario debe escanear un código QR en lugar de permitir que la LPA vincule el servicio de la aplicación del operador, la aplicación del operador responde al LPA usando setResult(int, Intent) con RESULT_OK y una instancia Intent que contiene el android.telephony.euicc.extra.USE_QR_SCANNER establecido en true . Luego, la LPA verifica el extra e inicia el escáner QR en lugar de vincular la implementación ICarrierEuiccProvisioningService de la aplicación del operador.
    2. Si la aplicación del operador falla o responde con RESULT_CANCELED (este es el código de respuesta predeterminado), la LPA cancela el flujo de activación de la eSIM.
    3. Si la aplicación del operador responde con algo distinto a RESULT_OK o RESULT_CANCELED , la LPA lo trata como un error.

    Por razones de seguridad, la LPA no debe aceptar directamente un código de activación proporcionado en el resultado con la intención de garantizar que las personas que no son LPA no puedan obtener un código de activación de la aplicación del operador.

Iniciar el flujo de activación de LPA en una aplicación de operador

A partir de Android 11, las aplicaciones de los operadores pueden usar las API de eUICC para iniciar una LUI para la activación de eSIM. Este método muestra la interfaz de usuario del flujo de activación de eSIM de la LPA para activar el perfil de eSIM. Luego, la LPA envía una transmisión cuando finaliza la activación del perfil eSIM.

  1. La LPA debe declarar una actividad que incluya un filtro de intención con la acción android.service.euicc.action.START_EUICC_ACTIVATION . La prioridad del filtro de intención debe establecerse en un valor distinto de cero en caso de que haya varias implementaciones presentes en el dispositivo. Por ejemplo:

    <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. La aplicación del operador hace su trabajo utilizando su propia interfaz de usuario. Por ejemplo, iniciar sesión como usuario o enviar solicitudes HTTP al backend del operador.

  3. En este punto, la aplicación del operador debe estar lista para proporcionar un código de activación a través de su implementación ICarrierEuiccProvisioningService . La aplicación del operador inicia la LPA llamando startActivityForResult(Intent, int) con la acción android.telephony.euicc.action.START_EUICC_ACTIVATION . La LPA también verifica el valor booleano adicional android.telephony.euicc.extra.USE_QR_SCANNER . Si el valor es true , LPA inicia el escáner QR para permitir al usuario escanear el código QR del perfil.

  4. En el lado de LPA, LPA se vincula a la implementación ICarrierEuiccProvisioningService de la aplicación del operador para recuperar el código de activación y descargar el perfil correspondiente. La LPA muestra todos los elementos de la interfaz de usuario necesarios durante la descarga, como una pantalla de carga.

  5. Cuando se completa el flujo de activación de LPA, LPA responde a la aplicación del operador con un código de resultado, que la aplicación del operador maneja en onActivityResult(int, int, Intent) .

    1. Si la LPA logra descargar el nuevo perfil eSIM, responde con RESULT_OK .
    2. Si el usuario cancela la activación del perfil eSIM en la LPA, responde con RESULT_CANCELED .
    3. Si la LPA responde con algo distinto a RESULT_OK o RESULT_CANCELED , la aplicación del operador lo trata como un error.

    Por razones de seguridad, LPA no acepta un código de activación directamente en la intención proporcionada para garantizar que las personas que no son LPA no puedan obtener el código de activación de la aplicación del operador.

Admite múltiples eSIM

Para dispositivos con Android 10 o superior, la clase EuiccManager admite dispositivos con múltiples eSIM. Los dispositivos con una única eSIM que se actualizan a Android 10 no requieren ninguna modificación en la implementación de LPA, ya que la plataforma asocia automáticamente la instancia EuiccManager con la eUICC predeterminada. La eUICC predeterminada está determinada por la plataforma para dispositivos con radio HAL versión 1.2 o superior y por la LPA para dispositivos con radio HAL versiones inferiores a 1.2.

Requisitos

Para admitir múltiples eSIM, el dispositivo debe tener más de una eUICC, que puede ser una eUICC incorporada o una ranura SIM física donde se pueden insertar eUICC extraíbles.

Se requiere Radio HAL versión 1.2 o superior para admitir múltiples eSIM. Se recomienda Radio HAL versión 1.4 y RadioConfig HAL versión 1.2.

Implementación

Para admitir múltiples eSIM (incluidas eUICC extraíbles o SIM programables), la LPA debe implementar EuiccService , que recibe la identificación de la ranura correspondiente a la identificación de la tarjeta proporcionada por la persona que llama.

El recurso non_removable_euicc_slots especificado en arrays.xml es una matriz de números enteros que representan los ID de ranura de los eUICC integrados de un dispositivo. Debe especificar este recurso para permitir que la plataforma determine si una eUICC insertada es extraíble o no.

Aplicación de operador para dispositivos con múltiples eSIM

Al crear una aplicación de operador para un dispositivo con varias eSIM, utilice el método createForCardId en EuiccManager para crear un objeto EuiccManager que esté anclado a una ID de tarjeta determinada. El ID de la tarjeta es un valor entero que identifica de forma única una UICC o una eUICC en el dispositivo.

Para obtener el ID de la tarjeta para la eUICC predeterminada del dispositivo, use el método getCardIdForDefaultEuicc en TelephonyManager . Este método devuelve UNSUPPORTED_CARD_ID si la versión HAL de la radio es inferior a 1.2 y devuelve UNINITIALIZED_CARD_ID si el dispositivo no ha leído la eUICC.

También puede obtener ID de tarjetas de getUiccCardsInfo y getUiccSlotsInfo (API del sistema) en TelephonyManager y getCardId en SubscriptionInfo .

Cuando se ha creado una instancia de un objeto EuiccManager con una ID de tarjeta específica, todas las operaciones se dirigen a la eUICC con esa ID de tarjeta. Si eUICC se vuelve inaccesible (por ejemplo, cuando se apaga o se elimina), EuiccManager ya no funciona.

Puede utilizar los siguientes ejemplos de código para crear una aplicación de operador.

Ejemplo 1: obtener una suscripción activa y crear una instancia 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);

Ejemplo 2: iterar a través de UICC y crear una instancia EuiccManager para una eUICC extraíble

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

Validación

AOSP no viene con una implementación de LPA y no se espera que tengas una LPA disponible en todas las versiones de Android (no todos los teléfonos admiten eSIM). Por este motivo, no existen casos de prueba CTS de un extremo a otro. Sin embargo, hay casos de prueba básicos disponibles en AOSP para garantizar que las API eUICC expuestas sean válidas en las compilaciones de Android.

Debe asegurarse de que las compilaciones pasen los siguientes casos de prueba CTS (para API públicas): /platform/cts/tests/tests/telephony/current/src/android/telephony/euicc/cts .

Los operadores que implementan una aplicación de operador deben pasar por sus ciclos internos normales de control de calidad para garantizar que todas las funciones implementadas funcionen como se espera. Como mínimo, la aplicación del operador debería poder enumerar todos los perfiles de suscripción propiedad del mismo operador, descargar e instalar un perfil, activar un servicio en el perfil, cambiar entre perfiles y eliminar perfiles.

Si está creando su propio LPA, debería pasar por pruebas mucho más rigurosas. Debe trabajar con su proveedor de módem, chip eUICC o proveedor de sistema operativo eSIM, proveedores de SM-DP+ y operadores para resolver problemas y garantizar la interoperabilidad de su LPA dentro de la arquitectura RSP. Es inevitable realizar una buena cantidad de pruebas manuales. Para obtener la mejor cobertura de prueba, debe seguir el Plan de prueba RSP GSMA SGP.23 .