Implementar eSIM

A tecnologia de chip incorporado (eSIM ou eUICC) permite que usuários de dispositivos móveis façam o download de um perfil de operadora e ativem o serviço dela sem precisar de um chip físico. É uma especificação global impulsionada pela GSMA que permite o provisionamento remoto de chip (RSP, na sigla em inglês) de qualquer dispositivo móvel. A partir do Android 9, o framework do Android oferece APIs padrão para acessar o eSIM e gerenciar perfis de assinatura nesse chip. Essas APIs eUICC permitem que terceiros desenvolvam os próprios apps de operadora e assistentes de perfil local (LPAs, na sigla em inglês) em dispositivos Android com eSIM.

O LPA é um app autônomo do sistema que precisa ser incluído na imagem do build do Android. A gestão dos perfis no eSIM geralmente é feita pelo LPA, que funciona como uma ponte entre o SM-DP+ (serviço remoto que prepara, armazena e envia pacotes de perfil para dispositivos) e o chip eUICC. O APK LPA pode incluir um componente de interface, chamado de LPA UI ou LUI, para fornecer um local central para o usuário final gerenciar todos os perfis de assinatura incorporados. O framework do Android detecta e se conecta automaticamente ao melhor LPA disponível e encaminha todas as operações de eUICC por uma instância de LPA.

Arquitetura simplificada de provisionamento remoto de chip (RSP, na sigla em inglês)

Figura 1. Arquitetura RSP simplificada

Operadoras de rede de dispositivos móveis interessadas em criar um app de operadora devem consultar as APIs em EuiccManager, que oferece operações de gerenciamento de perfil de alto nível, como downloadSubscription(), switchToSubscription() e deleteSubscription().

Se você é um OEM de dispositivo interessado em criar seu próprio app de sistema LPA, é necessário estender EuiccService para que o framework do Android se conecte aos seus serviços de LPA. Além disso, use as APIs em EuiccCardManager, que oferecem funções ES10x com base no GSMA RSP v2.0. Essas funções são usadas para emitir comandos para o chip eUICC, como prepareDownload(), loadBoundProfilePackage(), retrieveNotificationList() e resetMemory().

As APIs em EuiccManager precisam de um app LPA implementado corretamente para funcionar, e o autor da chamada das APIs EuiccCardManager precisa ser um LPA. Isso é aplicado pelo framework do Android.

Dispositivos com o Android 10 ou mais recente podem oferecer suporte a dispositivos com vários eSIMs. Para mais informações, consulte Suporte a vários eSIMs.

Criar um app de operadora

As APIs eUICC no Android 9 permitem que operadoras de rede móvel criem apps com a marca da operadora para gerenciar os perfis diretamente. Isso inclui o download e a exclusão de perfis de assinatura pertencentes à operadora, bem como a troca para um perfil de propriedade da operadora.

EuiccManager

EuiccManager é o ponto de entrada principal para que os apps interajam com o LPA. Isso inclui apps da operadora que fazem o download, excluem e mudam para as assinaturas da operadora. Isso também inclui o app do sistema LUI, que oferece um local/interface central para gerenciar todas as assinaturas incorporadas e pode ser um app separado daquele que fornece o EuiccService.

Para usar as APIs públicas, um app de operadora precisa primeiro ter a instância de EuiccManager por meio de Context#getSystemService:

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

Verifique se o eSIM é compatível com o dispositivo antes de realizar qualquer operação. EuiccManager#isEnabled() geralmente retorna true se o recurso android.hardware.telephony.euicc estiver definido e um pacote LPA estiver presente.

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

Para saber mais sobre o hardware e a versão do SO do eUICC:

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

Muitas APIs, como downloadSubscription() e switchToSubscription(), usam callbacks PendingIntent, já que podem levar segundos ou até minutos para serem concluídos. PendingIntent é enviado com um código de resultado no espaço EuiccManager#EMBEDDED_SUBSCRIPTION_RESULT_, que fornece códigos de erro definidos pelo framework, bem como um código de resultado detalhado arbitrário propagado do LPA como EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, permitindo que o app da operadora rastreie para fins de registro/depuração. O callback PendingIntent precisa ser BroadcastReceiver.

Para fazer o download de uma assinatura por download (criada com um código de ativação ou um 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 e use a permissão em 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 mudar para uma assinatura com base no ID, faça o seguinte:

// 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 conferir uma lista completa de APIs EuiccManager e exemplos de código, consulte APIs eUICC.

Erros solucionáveis

Há alguns casos em que o sistema não consegue concluir a operação do eSIM, mas o erro pode ser resolvido pelo usuário. Por exemplo, downloadSubscription poderá falhar se os metadados do perfil indicarem que um código de confirmação da operadora é necessário. Ou switchToSubscription pode falhar se o app da operadora tiver privilégios de operadora sobre o perfil de destino (ou seja, a operadora é proprietária do perfil), mas não tiver privilégios de operadora sobre o perfil ativado no momento. Portanto, o consentimento do usuário é necessário.

Nesses casos, o callback do autor da chamada é chamado com EuiccManager#EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR. O callback Intent contém extras internos para que, quando o autor da chamada o transmita para EuiccManager#startResolutionActivity, a resolução possa ser solicitada pela LUI. Usando o código de confirmação como exemplo novamente, EuiccManager#startResolutionActivity aciona uma tela de LUI que permite ao usuário inserir um código de confirmação. Depois que o código é inserido, a operação de download é retomada. Essa abordagem oferece ao app da operadora controle total sobre quando a interface é mostrada, mas oferece ao LPA/LUI um método extensível para adicionar um novo processamento de problemas recuperáveis pelo usuário no futuro sem precisar mudar os apps cliente.

O Android 9 define esses erros solucionáveis em EuiccService, que a LUI precisa processar:

/**
 * Alert the user that this action will result in an active SIM being
 * deactivated. To implement the LUI triggered by the system, you need to define
 * this in AndroidManifest.xml.
 */
public static final String ACTION_RESOLVE_DEACTIVATE_SIM =
        "android.service.euicc.action.RESOLVE_DEACTIVATE_SIM";
/**
 * Alert the user about a download/switch being done for an app that doesn't
 * currently have carrier privileges.
 */
public static final String ACTION_RESOLVE_NO_PRIVILEGES =
        "android.service.euicc.action.RESOLVE_NO_PRIVILEGES";

/** Ask the user to resolve all the resolvable errors. */
public static final String ACTION_RESOLVE_RESOLVABLE_ERRORS =
        "android.service.euicc.action.RESOLVE_RESOLVABLE_ERRORS";

Privilégios da operadora

Se você for uma operadora que está desenvolvendo seu próprio app que chama EuiccManager para fazer o download de perfis em um dispositivo, seu perfil precisará incluir regras de privilégio da operadora correspondentes ao app da operadora nos metadados. Isso acontece porque perfis de assinatura pertencentes a diferentes operadoras podem coexistir no eUICC de um dispositivo, e cada app de operadora só pode acessar os perfis pertencentes a essa operadora. Por exemplo, a operadora A não pode fazer o download, ativar ou desativar um perfil que pertence à operadora B.

Para garantir que um perfil possa ser acessado apenas pelo proprietário, o Android usa um mecanismo para conceder privilégios especiais ao app do proprietário do perfil, ou seja, ao app da operadora. A plataforma Android carrega certificados armazenados no arquivo de regra de acesso do perfil (ARF) e concede permissão a apps assinados por esses certificados para fazer chamadas para APIs EuiccManager. O processo geral é descrito abaixo:

  1. A operadora assina o APK do app da operadora. A ferramenta apksigner anexa o certificado de chave pública ao APK.
  2. O operador/SM-DP+ prepara um perfil e os metadados dele, que incluem um ARF que contém:

    1. Assinatura (SHA-1 ou SHA-256) do certificado de chave pública do app da operadora (obrigatório)
    2. Nome do pacote do app da operadora (recomendado)
  3. O app da operadora tenta realizar uma operação eUICC com a API EuiccManager.

  4. A plataforma Android verifica se o hash SHA-1 ou SHA-256 do certificado do app de chamada corresponde à assinatura do certificado recebido do ARF do perfil de destino. Se o nome do pacote do app da operadora estiver incluído no ARF, ele também precisará corresponder ao nome do pacote do app autor da chamada.

  5. Depois que a assinatura e o nome do pacote (se incluído) forem verificados, o privilégio da operadora será concedido ao app autor da chamada no perfil de destino.

Como os metadados do perfil podem estar disponíveis fora do próprio perfil (para que o LPA possa recuperá-los do SM-DP+ antes do download do perfil ou do ISD-R quando o perfil é desativado), ele precisa conter as mesmas regras de privilégio da operadora que o perfil.

O SO eUICC e o SM-DP+ precisam oferecer suporte à tag proprietária BF76 nos metadados do perfil. O conteúdo da tag precisa ser as mesmas regras de privilégio da operadora retornadas pelo applet de regra de acesso (ARA, na sigla em inglês) definido em Privilégios da operadora 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 mais detalhes sobre a assinatura de apps, consulte Assinar o app. Para mais detalhes sobre os privilégios da operadora, consulte Privilégios da operadora UICC.

Criar um app assistente de perfil local

Os fabricantes de dispositivos podem implementar o próprio assistente de perfil local (LPA, na sigla em inglês), que precisa ser conectado às APIs Euicc do Android. As seções a seguir fornecem uma breve visão geral de como criar um app LPA e integrá-lo ao sistema Android.

Requisitos de hardware/modem

O LPA e o SO do eSIM no chip eUICC precisam oferecer suporte a pelo menos o GSMA RSP (provisionamento remoto de SIM) v2.0 ou v2.2. Você também precisa planejar o uso de servidores SM-DP+ e SM-DS com uma versão correspondente de RSP. Para mais detalhes sobre a arquitetura do RSP, consulte a Especificação de arquitetura RSP do GSMA SGP.21.

Além disso, para se integrar às APIs eUICC no Android 9, o modem do dispositivo precisa enviar os recursos do terminal com o suporte aos recursos eUICC codificados (gerenciamento de perfil local e download de perfil). Ela também precisa implementar os seguintes métodos:

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

  • HAL IRadioConfig v1.0: getSimSlotsStatus

  • AIDL do IRadioConfig v1.0: getAllowedCarriers

    O LPA do Google precisa saber o status de bloqueio da operadora para permitir o download ou a transferência do eSIM apenas para a operadora permitida. Caso contrário, os usuários podem acabar fazendo o download e transferindo um chip e, depois, percebem que a operadora do dispositivo está bloqueada para outra operadora.

    • Fornecedores ou OEMs precisam implementar a API IRadioSim.getAllowedCarriers()HAL.

    • A RIL / Modem do fornecedor precisa preencher o status de bloqueio e o merchantId da operadora com a qual o dispositivo está bloqueado como parte da API IRadioSimResponse.getAllowedCarriersResponse()HAL.

O modem precisa reconhecer o eSIM com o perfil de inicialização padrão ativado como um chip válido e manter a energia do chip ativada.

Para dispositivos com o Android 10, é necessário definir uma matriz de ID de slot eUICC não removível. Por exemplo, 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 uma lista completa dos requisitos de modem, consulte Requisitos de modem para suporte a eSIM.

EuiccService

Uma LPA consiste em dois componentes separados (ambos podem ser implementados no mesmo APK): o back-end da LPA e a interface ou LUI da LPA.

Para implementar o back-end do LPA, é necessário estender EuiccService e declarar esse serviço no arquivo de manifesto. O serviço precisa exigir a permissão do sistema android.permission.BIND_EUICC_SERVICE para garantir que somente o sistema possa se vincular a ele. O serviço também precisa incluir um filtro de intent com a ação android.service.euicc.EuiccService. A prioridade do filtro de intent precisa ser definida como um valor diferente de zero caso várias implementações estejam presentes no dispositivo. Exemplo:

<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, o framework do Android determina o LPA ativo e interage com ele conforme necessário para oferecer suporte às APIs Android eUICC. PackageManager é consultado para todos os apps com a permissão android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS, que especifica um serviço para a ação android.service.euicc.EuiccService. O serviço com a prioridade mais alta é selecionado. Se nenhum serviço for encontrado, o suporte à LPA será desativado.

Para implementar o LUI, você precisa fornecer uma atividade para as seguintes ações:

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

Assim como no serviço, cada atividade precisa exigir a permissão do sistema android.permission.BIND_EUICC_SERVICE. Cada um precisa ter um filtro de intent com a ação adequada, a categoria android.service.euicc.category.EUICC_UI e uma prioridade diferente de zero. Uma lógica semelhante é usada para escolher as implementações dessas atividades, como a escolha da implementação de EuiccService. Exemplo:

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

Isso implica que a IU que implementa essas telas pode vir de um APK diferente daquele que implementa EuiccService. Ter um único APK ou vários APKs (por exemplo, um que implementa EuiccService e outro que fornece atividades de LUI) é uma escolha de design.

EuiccCardManager

EuiccCardManager é a interface para se comunicar com o chip eSIM. Ele oferece funções ES10 (conforme descrito na especificação GSMA RSP) e processa os comandos de solicitação/resposta APDU de baixo nível, bem como a análise ASN.1. EuiccCardManager é uma API do sistema e só pode ser chamada por apps com privilégios pelo sistema.

APIs de apps da operadora, LPA e Euicc

Figura 2. O app da operadora e o LPA usam APIs Euicc

As APIs de operação de perfil por EuiccCardManager exigem que o autor da chamada seja um LPA. Isso é aplicado pelo framework do Android. Isso significa que o autor da chamada precisa estender EuiccService e ser declarado no arquivo de manifesto, conforme descrito nas seções anteriores.

Assim como EuiccManager, para usar as APIs EuiccCardManager, o LPA precisa primeiro acessar a instância de EuiccCardManager pelo Context#getSystemService:

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

Em seguida, para obter todos os perfis no 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 é executado no processo do smartphone) por uma interface AIDL, e cada método EuiccCardManager recebe o callback do processo do smartphone por uma interface AIDL diferente e dedicada. Ao usar APIs EuiccCardManager, o autor da chamada (LPA) precisa fornecer um objeto Executor pelo qual o callback é invocado. Esse objeto Executor pode ser executado em uma única linha de execução ou em um pool de linhas de execução de sua escolha.

A maioria das APIs EuiccCardManager tem o mesmo padrão de uso. Por exemplo, para carregar um pacote de perfil vinculado no eUICC:

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

Para alternar para outro perfil com um determinado ICCID:

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

Para receber o endereço SM-DP+ padrão do chip eUICC:

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

Para recuperar uma lista de notificações dos eventos de notificação fornecidos:

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

Ativar um perfil de eSIM pelo app da operadora

Em dispositivos com o Android 9 ou versões mais recentes, você pode usar um app de operadora para ativar o eSIM e fazer o download de perfis. O app da operadora pode fazer o download de perfis chamando downloadSubscription diretamente ou fornecendo um código de ativação ao LPA.

Quando um app de operadora faz o download de um perfil chamando downloadSubscription, a chamada garante que o app possa gerenciar o perfil usando uma BF76 tag de metadados que codifica as regras de privilégio da operadora para o perfil. Se um perfil não tiver uma tag BF76 ou se a tag BF76 não corresponder à assinatura do app da operadora de chamada, o download será rejeitado.

A seção abaixo descreve como ativar um eSIM usando um código de ativação em um app de operadora.

Ativar o eSIM usando um código de ativação

Ao usar um código de ativação para ativar um perfil de eSIM, o LPA busca um código de ativação do app da operadora e faz o download do perfil. Esse fluxo pode ser iniciado pela LPA, e ela pode controlar todo o fluxo da interface, o que significa que nenhuma interface do app da operadora é mostrada. Essa abordagem ignora a verificação de tags BF76, e os operadores de rede não precisam implementar todo o fluxo da interface de ativação do eSIM, incluindo o download de um perfil de eSIM e o tratamento de erros.

Definir o serviço de provisionamento de eUICC da operadora

O LPA e o app da operadora se comunicam por duas interfaces AIDL: ICarrierEuiccProvisioningService e IGetActivationCodeCallback. O app da operadora precisa implementar uma interface ICarrierEuiccProvisioningService e expô-la na declaração de manifesto. O LPA precisa se vincular a ICarrierEuiccProvisioningService e implementar IGetActivationCodeCallback. Para mais informações sobre como implementar e expor uma interface AIDL, consulte Definição e interface AIDL.

Para definir as interfaces AIDL, crie os seguintes arquivos AIDL para os apps de LPA e de operadora.

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

Exemplo de implementação de LPA

Para se vincular à implementação ICarrierEuiccProvisioningService do app da operadora, o LPA precisa copiar ICarrierEuiccProvisioningService.aidl e IGetActivationCodeCallback.aidl para o projeto e implementar ServiceConnection.

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

Depois de se vincular à implementação ICarrierEuiccProvisioningService do app da operadora, a LPA chama getActivationCode ou getActivationCodeForEid para receber o código de ativação do app da operadora, transmitindo a implementação da classe stub IGetActivationCodeCallback.

A diferença entre getActivationCode e getActivationCodeForEid é que getActivationCodeForEid permite que uma operadora vincule um perfil ao EID do dispositivo antes do início do processo de download.

void getActivationCodeFromCarrierApp() {
    IGetActivationCodeCallback.Stub callback =
            new IGetActivationCodeCallback.Stub() {
                @Override
                public void onSuccess(String activationCode) throws RemoteException {
                    // Handle the case LPA success to get activation code from a carrier app.
                }

                @Override
                public void onFailure() throws RemoteException {
                    // Handle the case LPA failed to get activation code from a carrier app.
                }
            };
    
    try {
        mCarrierProvisioningService.getActivationCode(callback);
    } catch (RemoteException e) {
        // Handle Remote Exception
    }
}

Exemplo de implementação para o app da operadora

Para que o LPA seja vinculado ao app da operadora, ele precisa copiar ICarrierEuiccProvisioningService.aidl e IGetActivationCodeCallback.aidl para seu projeto e declarar o serviço ICarrierEuiccProvisioningService no arquivo AndroidManifest.xml. O serviço precisa exigir a permissão do sistema android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS para garantir que apenas o LPA, um app com privilégios do sistema, possa se vincular a ele. O serviço também precisa incluir um filtro de intent com a ação 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 o serviço de app de operadora AIDL, crie um serviço, estenda a classe Stub e implemente os métodos getActivationCode e getActivationCodeForEid. O LPA pode chamar qualquer um dos métodos para buscar o código de ativação do perfil. O app da operadora precisa responder chamando IGetActivationCodeCallback#onSuccess com o código de ativação se ele foi buscado do servidor da operadora. Se não for bem-sucedido, o app da operadora vai responder com 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 a interface do app da operadora no fluxo de ativação de LPA

Em dispositivos com o Android 11 e versões mais recentes, o LPA pode iniciar a interface de um app de operadora. Isso é útil porque um app de operadora pode exigir informações adicionais do usuário antes de fornecer um código de ativação ao LPA. Por exemplo, as operadoras podem exigir que os usuários façam login para ativar os números de telefone ou realizar outros serviços de portabilidade.

Este é o processo para iniciar a interface do app da operadora no LPA:

  1. O LPA inicia o fluxo de ativação do app da operadora enviando a intent android.service.euicc.action.START_CARRIER_ACTIVATION para o pacote do app da operadora que contém a ação. O receptor do app da operadora precisa ser protegido na declaração de manifesto com android:permission="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" para evitar receber intents de apps que não são LPAs.

    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. O app da operadora funciona usando a própria interface. Por exemplo, fazer login no usuário ou enviar solicitações HTTP para o back-end da operadora.

  3. O app da operadora responde ao LPA chamando setResult(int, Intent) e finish().

    1. Se o app da operadora responder com RESULT_OK, o LPA continuará o fluxo de ativação. Se o app da operadora determinar que o usuário precisa ler um código QR em vez de permitir que o LPA associe o serviço do app da operadora, o app da operadora vai responder ao LPA usando setResult(int, Intent) com RESULT_OK e uma instância Intent que contém o extra booleano android.telephony.euicc.extra.USE_QR_SCANNER definido como true. O LPA verifica o extra e inicia o leitor de QR code em vez de vincular a implementação ICarrierEuiccProvisioningService do app da operadora.
    2. Se o app da operadora falhar ou responder com RESULT_CANCELED (o código de resposta padrão), o LPA vai cancelar o fluxo de ativação do eSIM.
    3. Se o app da operadora responder com algo diferente de RESULT_OK ou RESULT_CANCELED, o LPA vai tratar isso como um erro.

    Por motivos de segurança, o LPA não pode aceitar diretamente um código de ativação fornecido na intent de resultado para garantir que os autores de chamadas que não são LPA não possam receber um código de ativação do app da operadora.

Iniciar o fluxo de ativação de LPA em um app de operadora

A partir do Android 11, os apps da operadora podem usar APIs eUICC para iniciar uma LUI para ativação de eSIM. Esse método mostra a interface do fluxo de ativação do eSIM do LPA para ativar o perfil de eSIM. O LPA envia uma transmissão quando a ativação do perfil do eSIM é concluída.

  1. O LPA precisa declarar uma atividade que inclua um filtro de intent com a ação android.service.euicc.action.START_EUICC_ACTIVATION. A prioridade do filtro de intent precisa ser definida como um valor diferente de zero caso várias implementações estejam presentes no dispositivo. Exemplo:

    <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. O app da operadora funciona usando a própria interface. Por exemplo, fazer login no usuário ou enviar solicitações HTTP para o back-end da operadora.

  3. Nesse ponto, o app da operadora precisa estar pronto para fornecer um código de ativação pela implementação de ICarrierEuiccProvisioningService. O app da operadora inicia a LPA chamando startActivityForResult(Intent, int) com a ação android.telephony.euicc.action.START_EUICC_ACTIVATION. O LPA também verifica o extra booleano android.telephony.euicc.extra.USE_QR_SCANNER. Se o valor for true, a LPA vai iniciar o leitor para permitir que o usuário leia o QR code do perfil.

  4. No lado do LPA, ele se vincula à implementação ICarrierEuiccProvisioningService do app da operadora para buscar o código de ativação e fazer o download do perfil correspondente. O LPA mostra todos os elementos necessários da interface durante o download, como uma tela de carregamento.

  5. Quando o fluxo de ativação do LPA é concluído, ele responde ao app da operadora com um código de resultado, que é processado em onActivityResult(int, int, Intent).

    1. Se o LPA conseguir fazer o download do novo perfil de eSIM, ele vai responder com RESULT_OK.
    2. Se o usuário cancelar a ativação do perfil de eSIM no LPA, ele vai responder com RESULT_CANCELED.
    3. Se o LPA responder com algo diferente de RESULT_OK ou RESULT_CANCELED, o app da operadora tratará isso como um erro.

    Por motivos de segurança, o LPA não aceita um código de ativação diretamente na intent fornecida para garantir que os autores de chamadas que não são LPA não possam receber o código de ativação do app da operadora.

Oferecer suporte a vários eSIMs

Para dispositivos que executam o Android 10 ou versões mais recentes, a classe EuiccManager oferece suporte a dispositivos com vários eSIMs. Os dispositivos com um único eSIM que estão sendo atualizados para o Android 10 não exigem nenhuma modificação na implementação do LPA, já que a plataforma associa automaticamente a instância EuiccManager ao eUICC padrão. O eUICC padrão é determinado pela plataforma para dispositivos com a HAL de rádio versão 1.2 ou mais recente e pelo LPA para dispositivos com versões da HAL de rádio anteriores à 1.2.

Requisitos

Para oferecer suporte a vários eSIMs, o dispositivo precisa ter mais de um eUICC, que pode ser um eUICC integrado ou um slot de chip físico em que eUICCs removíveis podem ser inseridos.

A versão 1.2 ou mais recente do HAL de rádio é necessária para oferecer suporte a vários eSIMs. Recomendamos a versão 1.4 da HAL de rádio e a versão 1.2 da HAL de RadioConfig.

Implementação

Para oferecer suporte a vários eSIMs (incluindo eUICCs removíveis ou chips programáveis), o LPA precisa implementar EuiccService, que recebe o ID de slot correspondente ao ID do cartão fornecido pelo autor da chamada.

O recurso non_removable_euicc_slots especificado em arrays.xml é uma matriz de números inteiros que representa os IDs de slot dos eUICCs integrados de um dispositivo. É necessário especificar esse recurso para permitir que a plataforma determine se um eUICC inserido é removível ou não.

App da operadora para dispositivos com vários eSIMs

Ao criar um app de operadora para um dispositivo com vários eSIMs, use o método createForCardId em EuiccManager para criar um objeto EuiccManager fixado a um determinado ID de cartão. O ID do cartão é um valor inteiro que identifica exclusivamente um UICC ou um eUICC no dispositivo.

Para receber o ID do cartão para o eUICC padrão do dispositivo, use o método getCardIdForDefaultEuicc em TelephonyManager. Esse método retorna UNSUPPORTED_CARD_ID se a versão do HAL de rádio for inferior a 1.2 e retorna UNINITIALIZED_CARD_ID se o dispositivo não tiver lido o eUICC.

Também é possível receber IDs de cartão de getUiccCardsInfo e getUiccSlotsInfo (API do sistema) em TelephonyManager e getCardId em SubscriptionInfo.

Quando um objeto EuiccManager é instanciado com um ID de cartão específico, todas as operações são direcionadas ao eUICC com esse ID. Se o eUICC ficar inacessível (por exemplo, quando ele for desativado ou removido), o EuiccManager não vai mais funcionar.

Use os exemplos de código abaixo para criar um app de operadora.

Exemplo 1: receber a assinatura ativa e instanciar 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);

Exemplo 2: iterar usando UICCs e instanciar EuiccManager para um eUICC removível

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

Validação

O AOSP não vem com uma implementação de LPA, e não é esperado que você tenha um LPA disponível em todos os builds do Android (nem todos os smartphones oferecem suporte a eSIM). Por esse motivo, não há casos de teste de CTS de ponta a ponta. No entanto, casos de teste básicos estão disponíveis no AOSP para garantir que as APIs eUICC expostas sejam válidas em builds do Android.

Verifique se os builds atendem aos seguintes casos de teste do CTS (para APIs públicas): /platform/cts/tests/tests/telephony/current/src/android/telephony/euicc/cts.

As operadoras que implementam um app de operadora precisam passar pelos ciclos normais de garantia de qualidade internos para garantir que todos os recursos implementados funcionem conforme o esperado. No mínimo, o app da operadora precisa listar todos os perfis de assinatura pertencentes à mesma operadora, fazer o download e a instalação de um perfil, ativar um serviço no perfil, alternar entre perfis e excluir perfis.

Se você estiver criando sua própria LPA, precisará passar por testes muito mais rigorosos. Você precisa trabalhar com o fornecedor do modem, o chip eUICC ou o fornecedor do SO eSIM, fornecedores de SM-DP+ e operadoras para resolver problemas e garantir a interoperabilidade do LPA na arquitetura RSP. Uma boa quantidade de testes manuais é inevitável. Para ter a melhor cobertura de teste, siga o plano de teste do RSP do GSMA SGP.23.