Implementa la eSIM

La tecnología de tarjeta SIM integrada (eSIM o eUICC) permite que los usuarios de dispositivos móviles descarguen el perfil de un operador y activen su servicio sin tener una tarjeta SIM física. Es una especificación global impulsada por la GSMA que permite el aprovisionamiento remoto de SIM (RSP) de cualquier dispositivo móvil. A partir de Android 9, el framework de Android proporciona APIs estándar para acceder a la eSIM y administrar los perfiles de suscripción en ella. Estas APIs de eUICC permiten que terceros desarrollen sus propias apps de operadores y asistentes de perfiles locales (LPA) en dispositivos Android compatibles con eSIM.

El LPA es una app del sistema independiente que se debe incluir en la imagen de compilación de Android. Por lo general, la LPA administra los perfiles en la eSIM, ya que actúa como puente entre el SM-DP+ (servicio remoto que prepara, almacena y entrega paquetes de perfiles a los dispositivos) y el chip de la eUICC. El APK de la LPA puede incluir, de manera opcional, un componente de IU, llamado IU de la LPA o LUI, para proporcionar un lugar central en el que el usuario final pueda administrar todos los perfiles de suscripción integrados. El framework de Android descubre y se conecta automáticamente al mejor LPA disponible, y enruta todas las operaciones de la eUICC a través de una instancia del LPA.

Arquitectura simplificada del aprovisionamiento remoto de SIM (RSP)

Figura 1: Arquitectura de RSP simplificada

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

Si eres un OEM de dispositivos y te interesa crear tu propia app del sistema de LPA, debes extender EuiccService para que el framework de Android se conecte a tus servicios de LPA. Además, debes usar las APIs en EuiccCardManager, que proporcionan funciones de ES10x basadas en GSMA RSP v2.0. Estas funciones se usan para enviar comandos al chip de la eUICC, como prepareDownload(), loadBoundProfilePackage(), retrieveNotificationList() y resetMemory().

Las APIs en EuiccManager requieren una app de LPA implementada correctamente para funcionar, y el llamador de las APIs de EuiccCardManager debe ser una LPA. El framework de Android aplica esta restricción.

Los dispositivos que ejecutan Android 10 o versiones posteriores pueden admitir dispositivos con varias eSIM. Para obtener más información, consulta Compatibilidad con varias eSIMs.

Crea una app del operador

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

EuiccManager

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

Para usar las APIs públicas, primero una app de operador debe obtener la instancia de EuiccManager a través de Context#getSystemService:

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

Antes de realizar cualquier operación con la eSIM, debes verificar si el dispositivo es compatible con ella. En general, EuiccManager#isEnabled() devuelve true si se define la función android.hardware.telephony.euicc y hay un paquete de LPA presente.

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

Para obtener información sobre el hardware de la eUICC y la versión del SO de la eSIM, haz lo siguiente:

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

Muchas APIs, como downloadSubscription() y switchToSubscription(), usan 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 framework, así como un código de resultado detallado arbitrario propagado desde la LPA como EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, lo que permite que la app del operador realice un seguimiento para fines de registro o 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), haz lo siguiente:

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

Define y usa 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 determinado el ID de suscripción, haz lo siguiente:

// 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 APIs de EuiccManager y ejemplos de código, consulta APIs de eUICC.

Errores que se pueden resolver

En algunos casos, 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 app de la operadora tiene privilegios de operadora sobre el perfil de destino (es decir, la operadora es propietaria del perfil), pero no tiene privilegios de operadora sobre el perfil habilitado actualmente y, por lo tanto, se requiere el consentimiento del usuario.

En estos casos, se llama a la devolución de llamada del llamador con EuiccManager#EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR. La devolución de llamada Intent contiene elementos adicionales internos de modo que, cuando el llamador la pasa a EuiccManager#startResolutionActivity, se puede solicitar la resolución a través de la LUI. Si volvemos a usar el código de confirmación como ejemplo, EuiccManager#startResolutionActivity activa una pantalla de LUI que permite al usuario ingresar un código de confirmación. Después de que se ingresa el código, se reanuda la operación de descarga. Este enfoque proporciona a la app de la empresa de telefonía celular un control total sobre cuándo se muestra la IU, pero le brinda al LPA/LUI un método extensible para agregar un nuevo control de problemas recuperables por el usuario en el futuro sin necesidad de que cambien las apps cliente.

Android 9 define estos errores solucionables en EuiccService, que la LUI debe controlar:

/**
 * 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 de operador

Si eres un operador que desarrolla su propia app de operador que llama a EuiccManager para descargar perfiles en un dispositivo, tu perfil debe incluir reglas de privilegios de operador correspondientes a tu app 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 solo se debe permitir que cada app del operador acceda a los perfiles que le pertenecen a ese operador. Por ejemplo, el operador A no debería poder descargar, habilitar ni inhabilitar un perfil que pertenezca al operador B.

Para garantizar que solo el propietario pueda acceder a un perfil, Android usa un mecanismo para otorgar privilegios especiales a la app del propietario del perfil (es decir, la app de la operadora). La plataforma de Android carga los certificados almacenados en el archivo de reglas de acceso (ARF) del perfil y otorga permiso a las apps firmadas por estos certificados para realizar llamadas a las APIs de EuiccManager. A continuación, se describe el proceso general:

  1. El operador firma el APK de la app de operador. La herramienta apksigner adjunta el certificado de clave pública al APK.
  2. El operador o el SM-DP+ prepara un perfil y sus metadatos, que incluyen un ARF que contiene lo siguiente:

    1. Firma (SHA-1 o SHA-256) del certificado de clave pública de la app de operador (obligatorio)
    2. Nombre del paquete de la app de la empresa de transporte (se recomienda encarecidamente)
  3. La app de operador intenta realizar una operación de eUICC con la API de EuiccManager.

  4. La plataforma de Android verifica que el hash SHA-1 o SHA-256 del certificado de la app que realiza la llamada coincida con la firma del certificado obtenido del ARF del perfil de destino. Si el nombre del paquete de la app de la aerolínea se incluye en el ARF, también debe coincidir con el nombre del paquete de la app que realiza la llamada.

  5. Después de verificar la firma y el nombre del paquete (si se incluye), se otorga el privilegio de operador a la app que realiza la llamada sobre el perfil de destino.

Dado que los metadatos del perfil pueden estar disponibles fuera del perfil en sí (para que el LPA pueda recuperar los metadatos del perfil del SM-DP+ antes de que se descargue el perfil, o del ISD-R cuando se inhabilita el perfil), deben contener las mismas reglas de privilegios del operador que en el perfil.

El SO de la eUICC y el SM-DP+ deben admitir una etiqueta patentada BF76 en los metadatos del perfil. El contenido de la etiqueta debe ser el mismo que el de las reglas de privilegios de operador que devuelve el subprograma de reglas de acceso (ARA) definido en Privilegios de operador de 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 apps, consulta Cómo firmar tu app. Para obtener detalles sobre los privilegios de operador, consulta Privilegios de operador de UICC.

Crea una app de asistente de perfil local

Los fabricantes de dispositivos pueden implementar su propio asistente de perfil local (LPA), que debe conectarse con las APIs de Euicc de Android. En las siguientes secciones, se ofrece una breve descripción general de cómo crear una app de LPA y cómo integrarla en el sistema Android.

Requisitos de hardware y módem

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

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

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

  • HAL de IRadioConfig v1.0: getSimSlotsStatus

  • IRadioConfig AIDL v1.0: getAllowedCarriers

    El LPA de Google necesita conocer el estado de bloqueo del operador para permitir la descarga o transferencia de la eSIM solo para el operador permitido. De lo contrario, es posible que los usuarios terminen descargando y transfiriendo una SIM, y luego se den cuenta de que el dispositivo está bloqueado para un operador diferente.

    • Los proveedores o los OEM deben implementar la API de HAL de IRadioSim.getAllowedCarriers().

    • El módem o la RIL del proveedor deben completar el estado de bloqueo y el carrierId del operador al que está bloqueado el dispositivo como parte de la API de IRadioSimResponse.getAllowedCarriersResponse() HAL.

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

En el caso de los dispositivos que ejecutan Android 10, debe definirse un array de IDs de ranura eUICC no extraíble. Por ejemplo, consulta arrays.xml.

<resources>
   <!-- Device-specific array of SIM slot indexes which are are embedded eUICCs.
        e.g. If a device has two physical slots with indexes 0, 1, and slot 1 is an
        eUICC, then the value of this array should be:
            <integer-array name="non_removable_euicc_slots">
                <item>1</item>
            </integer-array>
        If a device has three physical slots and slot 1 and 2 are eUICCs, then the value of
        this array should be:
            <integer-array name="non_removable_euicc_slots">
               <item>1</item>
               <item>2</item>
            </integer-array>
        This is used to differentiate between removable eUICCs and built in eUICCs, and should
        be set by OEMs for devices which use eUICCs. -->

   <integer-array name="non_removable_euicc_slots">
       <item>1</item>
   </integer-array>
</resources>

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

EuiccService

Un LPA consta de dos componentes separados (ambos se pueden implementar en el mismo APK): el backend del LPA y la IU del LPA o LUI.

Para implementar el backend de la LPA, debes extender EuiccService y declarar este servicio en tu 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 intents con la acción android.service.euicc.EuiccService. La prioridad del filtro de intents debe establecerse en un valor distinto de cero en caso de que haya varias implementaciones 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 framework de Android determina el LPA activo y, luego, interactúa con él según sea necesario para admitir las APIs de la eUICC de Android. Se consulta PackageManager para todas las apps 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 la prioridad más alta. Si no se encuentra ningún servicio, se inhabilita la compatibilidad con LPA.

Para implementar la LUI, debes 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 intents con la acción adecuada, la categoría android.service.euicc.category.EUICC_UI y una prioridad distinta de cero. Se usa una lógica similar para elegir las implementaciones de estas actividades, al igual que 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 IU que implementa estas pantallas puede provenir de un APK diferente del que implementa EuiccService. Tener un solo APK o varios (por ejemplo, uno que implemente EuiccService y otro que proporcione actividades de LUI) es una decisión de diseño.

EuiccCardManager

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

APIs de LPA, Euicc y apps de operadores

Figura 2: Tanto la app de la empresa de telefonía celular como la LPA usan las APIs de Euicc

Las APIs de operaciones de perfil a través de EuiccCardManager requieren que el llamador sea una LPA. El framework de Android aplica esta restricción. Esto significa que el llamador debe extender EuiccService y declararse en tu archivo de manifiesto, como se describió en las secciones anteriores.

Al igual que con EuiccManager, para usar las APIs de EuiccCardManager, tu 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 de la eUICC, haz lo siguiente:

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 de AIDL, y cada método de EuiccCardManager recibe su devolución de llamada del proceso del teléfono a través de una interfaz de AIDL diferente y dedicada. Cuando se usan las APIs de EuiccCardManager, la entidad 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 tu elección.

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

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

Sigue estos pasos para cambiar a otro perfil 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 de la eUICC, haz lo siguiente:

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

Para recuperar una lista de notificaciones de los eventos de notificación determinados, haz lo siguiente:

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

Cómo activar un perfil de eSIM a través de la app de un operador

En los dispositivos que ejecutan Android 9 o versiones posteriores, puedes usar una app del operador para activar la eSIM y descargar perfiles. La app del operador puede descargar perfiles llamando a downloadSubscription directamente o proporcionando un código de activación a la LPA.

Cuando una app de operador descarga un perfil llamando a downloadSubscription, la llamada exige que la app pueda administrar el perfil a través de una BF76 etiqueta de metadatos que codifica 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 app de operador de llamadas, se rechaza la descarga.

En la siguiente sección, se describe cómo activar una eSIM a través de la app del operador con un código de activación.

Cómo activar una eSIM con un código de activación

Cuando se usa un código de activación para activar un perfil de eSIM, la LPA recupera un código de activación de la app del operador y descarga el perfil. El LPA puede iniciar este flujo y controlar toda la IU, lo que significa que no se muestra la IU de la app de operador. Este enfoque omite la verificación de la etiqueta BF76, y los operadores de redes no necesitan implementar todo el flujo de la IU de activación de la eSIM, incluida la descarga de un perfil de eSIM y el control de errores.

Define el servicio de aprovisionamiento de la eUICC del operador

La LPA y la app de la operadora se comunican a través de dos interfaces de AIDL: ICarrierEuiccProvisioningService y IGetActivationCodeCallback. La app de la empresa de transporte debe implementar una interfaz ICarrierEuiccProvisioningService y exponerla en su declaración de manifiesto. La LPA debe vincularse a ICarrierEuiccProvisioningService y, luego, implementar IGetActivationCodeCallback. Para obtener más información sobre cómo implementar y exponer una interfaz de AIDL, consulta Cómo definir una interfaz de AIDL.

Para definir las interfaces de AIDL, crea los siguientes archivos de AIDL para las apps de LPA y de operador.

  • 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 de ICarrierEuiccProvisioningService de la app de la empresa de telefonía celular, el LPA debe copiar ICarrierEuiccProvisioningService.aidl y IGetActivationCodeCallback.aidl en tu proyecto, y, luego, implementar ServiceConnection.

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

Después de vincularse a la implementación de ICarrierEuiccProvisioningService de la app de operador, el LPA llama a getActivationCode o a getActivationCodeForEid para obtener el código de activación de la app de operador pasando la implementación de la clase stub 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
    }
}

Ejemplo de implementación para la app de la empresa de transporte

Para que la LPA se vincule a la app de la empresa de telefonía celular, esta debe copiar ICarrierEuiccProvisioningService.aidl y IGetActivationCodeCallback.aidl a tu 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 el LPA, una app con privilegios del sistema, pueda vincularse a él. El servicio también debe incluir un filtro de intents 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 la app de operador de AIDL, crea un servicio, extiende la clase Stub y, luego, implementa los métodos getActivationCode y getActivationCodeForEid. Luego, la LPA puede llamar a cualquiera de los métodos para recuperar el código de activación del perfil. La app de la empresa de telefonía celular debe responder llamando a IGetActivationCodeCallback#onSuccess con el código de activación si se recuperó correctamente del servidor de la empresa. Si no se realiza correctamente, la app de la empresa de telefonía celular debe 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);
              }
          }
    }
    

Cómo iniciar la IU de la app de operador en el flujo de activación de LPA

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

Este es el proceso para iniciar la IU de una app de operador en la LPA:

  1. La LPA inicia el flujo de activación de la app de operador enviando el intent android.service.euicc.action.START_CARRIER_ACTIVATION al paquete de la app de operador que contiene la acción. (El receptor de la app de operador debe protegerse en la declaración del manifiesto con android:permission="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" para evitar recibir intents de apps que no son de 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 app de la empresa de telefonía celular funciona con su propia IU. Por ejemplo, acceder a la cuenta del usuario o enviar solicitudes HTTP al backend del operador.

  3. La app de la aerolínea responde al LPA llamando a setResult(int, Intent) y finish().

    1. Si la app de operador responde con RESULT_OK, el LPA continúa con el flujo de activación. Si la app de la empresa de telefonía celular determina que el usuario debe escanear un código QR en lugar de permitir que el LPA vincule el servicio de la app de la empresa de telefonía celular, la app de la empresa de telefonía celular responde al LPA con setResult(int, Intent), RESULT_OK y una instancia de Intent que contiene el parámetro adicional booleano android.telephony.euicc.extra.USE_QR_SCANNER establecido en true. Luego, el LPA verifica el elemento adicional y, en su lugar, inicia el escáner de QR en lugar de vincular la implementación de ICarrierEuiccProvisioningService de la app del operador.
    2. Si la app de la operadora 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 app de la empresa de telefonía celular responde con algo que no sea RESULT_OK o RESULT_CANCELED, la LPA lo trata como un error.

    Por motivos de seguridad, la LPA no debe aceptar directamente un código de activación proporcionado en el intent de resultado para garantizar que los llamadores que no sean de la LPA no puedan obtener un código de activación de la app del operador.

Cómo iniciar el flujo de activación de LPA en una app de operador

A partir de Android 11, las apps de operador pueden usar las APIs de eUICC para iniciar una LUI para la activación de eSIM. Este método muestra la IU 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 de eSIM.

  1. La LPA debe declarar una actividad que incluya un filtro de intents con la acción android.service.euicc.action.START_EUICC_ACTIVATION. La prioridad del filtro de intents debe establecerse en un valor distinto de cero en caso de que haya varias implementaciones 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 app de la empresa de telefonía celular funciona con su propia IU. Por ejemplo, acceder a la cuenta del usuario o enviar solicitudes HTTP al backend del operador.

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

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

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

    1. Si el LPA logra descargar el nuevo perfil de eSIM, responde con RESULT_OK.
    2. Si el usuario cancela la activación del perfil de eSIM en la LPA, esta responde con RESULT_CANCELED.
    3. Si la LPA responde con algo que no sea RESULT_OK o RESULT_CANCELED, la app de la empresa de telefonía móvil lo considera un error.

    Por motivos de seguridad, la LPA no acepta un código de activación directamente en el intent proporcionado para garantizar que los llamadores que no son de la LPA no puedan obtener el código de activación de la app del operador.

Compatibilidad con varias eSIM

En el caso de los dispositivos que ejecutan Android 10 o versiones posteriores, la clase EuiccManager admite dispositivos con varias eSIM. Los dispositivos con una sola eSIM que se actualizan a Android 10 no requieren ninguna modificación en la implementación del LPA, ya que la plataforma asocia automáticamente la instancia de EuiccManager con la eUICC predeterminada. La plataforma determina la eUICC predeterminada para los dispositivos con la versión 1.2 o posterior de la HAL de radio, y el LPA lo hace para los dispositivos con versiones de la HAL de radio anteriores a la 1.2.

Requisitos

Para admitir varias eSIM, el dispositivo debe tener más de una eUICC, que puede ser una eUICC integrada o una ranura para SIM física en la que se pueden insertar eUICCs extraíbles.

Se requiere la versión 1.2 o una posterior de la HAL de Radio para admitir varias eSIM. Se recomiendan la versión 1.4 de la HAL de Radio y la versión 1.2 de la HAL de RadioConfig.

Implementación

Para admitir varias eSIM (incluidas las eUICCs extraíbles o las SIM programables), el LPA debe implementar EuiccService, que recibe el ID de ranura correspondiente al ID de tarjeta proporcionado por el llamador.

El recurso non_removable_euicc_slots especificado en arrays.xml es un array de números enteros que representan los IDs de ranura de las eUICCs integradas de un dispositivo. Debes especificar este recurso para permitir que la plataforma determine si una eUICC insertada es extraíble o no.

App del operador para dispositivos con varias eSIM

Cuando crees una app de operador para un dispositivo con varias eSIM, usa el método createForCardId en EuiccManager para crear un objeto EuiccManager que esté fijado a un ID de tarjeta determinado. 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 de la eUICC predeterminada del dispositivo, usa el método getCardIdForDefaultEuicc en TelephonyManager. Este método devuelve UNSUPPORTED_CARD_ID si la versión de HAL de radio es inferior a 1.2 y devuelve UNINITIALIZED_CARD_ID si el dispositivo no leyó la eUICC.

También puedes obtener IDs de tarjetas de getUiccCardsInfo y getUiccSlotsInfo (API del sistema) en TelephonyManager, y getCardId en SubscriptionInfo.

Cuando se crea una instancia de un objeto EuiccManager con un ID de tarjeta específico, todas las operaciones se dirigen a la eUICC con ese ID de tarjeta. Si no se puede acceder a la eUICC (por ejemplo, cuando se apaga o se quita), EuiccManager ya no funciona.

Puedes usar las siguientes muestras de código para crear una app de operador.

Ejemplo 1: Obtén la suscripción activa y crea una instancia de 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: Iteración a través de las UICC y creación de instancias de 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

El AOSP no incluye una implementación de LPA y no se espera que haya una LPA disponible en todas las compilaciones de Android (no todos los teléfonos admiten eSIM). Por este motivo, no hay casos de prueba de CTS de extremo a extremo. Sin embargo, los casos de prueba básicos están disponibles en AOSP para garantizar que las APIs de eUICC expuestas sean válidas en las compilaciones de Android.

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

Los operadores que implementen una app de operador deben completar sus ciclos internos normales de garantía de calidad para asegurarse de que todas las funciones implementadas funcionen según lo previsto. Como mínimo, la app de la empresa de telefonía celular debe poder enumerar todos los perfiles de suscripción que pertenecen al mismo operador, descargar e instalar un perfil, activar un servicio en el perfil, cambiar entre perfiles y borrar perfiles.

Si creas tu propia LPA, deberías someterla a pruebas mucho más rigurosas. Debes trabajar con el proveedor del módem, el chip de la eUICC o el SO de la eSIM, los proveedores de SM-DP+ y los operadores para resolver problemas y garantizar la interoperabilidad de tu LPA dentro de la arquitectura de RSP. Es inevitable una buena cantidad de pruebas manuales. Para obtener la mejor cobertura de pruebas, debes seguir el Plan de pruebas de RSP de GSMA SGP.23.