Внедрение eSIM

Технология встроенной SIM-карты (eSIM или eUICC) позволяет мобильным пользователям загружать профиль оператора и активировать услугу оператора без физической SIM-карты. Это глобальная спецификация, управляемая GSMA, которая обеспечивает удаленную инициализацию SIM-карты (RSP) любого мобильного устройства. Начиная с Android 9, платформа Android предоставляет стандартные API для доступа к eSIM и управления профилями подписки на eSIM. Эти API-интерфейсы eUICC позволяют третьим сторонам разрабатывать собственные приложения для операторов связи и помощников по работе с локальными профилями (LPA) на устройствах Android с поддержкой eSIM.

LPA — это автономное системное приложение, которое должно быть включено в образ сборки Android. Управление профилями на eSIM обычно осуществляется LPA, поскольку он служит мостом между SM-DP+ (удаленная служба, которая подготавливает, хранит и доставляет пакеты профилей на устройства) и микросхемой eUICC. LPA APK может дополнительно включать компонент пользовательского интерфейса, называемый пользовательским интерфейсом LPA или LUI, чтобы предоставить конечному пользователю центральное место для управления всеми встроенными профилями подписки. Платформа Android автоматически обнаруживает и подключается к лучшему доступному LPA, а также направляет все операции eUICC через экземпляр LPA.

Упрощенная архитектура Remote SIM Provisioning (RSP)

Рисунок 1. Упрощенная архитектура RSP

Операторы мобильных сетей, заинтересованные в создании мобильного приложения, должны обратить внимание на API-интерфейсы в EuiccManager , которые обеспечивают высокоуровневые операции управления профилями, такие как downloadSubscription() , switchToSubscription() и deleteSubscription() .

Если вы OEM-производитель устройств, заинтересованный в создании собственного системного приложения LPA, вы должны расширить EuiccService для платформы Android, чтобы подключиться к вашим службам LPA. Кроме того, вы должны использовать API-интерфейсы в EuiccCardManager , которые предоставляют функции ES10x на основе GSMA RSP v2.0. Эти функции используются для выдачи команд чипу eUICC, таких как prepareDownload() , loadBoundProfilePackage() , retrieveNotificationList() и resetMemory() .

Для работы API в EuiccManager требуется правильно реализованное приложение LPA, а вызывающим API EuiccCardManager должен быть LPA. Это обеспечивается платформой Android.

Устройства под управлением Android 10 или более поздней версии могут поддерживать устройства с несколькими eSIM. Дополнительные сведения см. в разделе Поддержка нескольких карт eSIM .

Делаем приложение оператора

API-интерфейсы eUICC в Android 9 позволяют операторам мобильных сетей создавать фирменные приложения для непосредственного управления своими профилями. Это включает загрузку и удаление профилей подписки, принадлежащих оператору, а также переключение на профиль, принадлежащий оператору.

EuiccManager

EuiccManager — это основная точка входа для взаимодействия приложений с LPA. Сюда входят приложения оператора, которые загружают, удаляют и переключаются на подписки, принадлежащие оператору. Это также включает в себя системное приложение LUI, которое обеспечивает централизованное расположение/пользовательский интерфейс для управления всеми встроенными подписками и может быть отдельным приложением от того, которое предоставляет EuiccService .

Чтобы использовать общедоступные API, приложение-носитель должно сначала получить экземпляр EuiccManager через Context#getSystemService :

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

Вы должны проверить, поддерживается ли eSIM на устройстве, прежде чем выполнять какие-либо операции с eSIM. EuiccManager#isEnabled() обычно возвращает значение true , если функция android.hardware.telephony.euicc определена и присутствует пакет LPA.

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

Чтобы получить информацию об оборудовании eUICC и версии ОС eSIM:

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

Многие API, такие как downloadSubscription() и switchToSubscription() , используют обратные вызовы PendingIntent , поскольку их выполнение может занять секунды или даже минуты. PendingIntent отправляется с кодом результата в пространстве EuiccManager#EMBEDDED_SUBSCRIPTION_RESULT_ , который предоставляет коды ошибок, определенные платформой, а также произвольный подробный код результата, распространяемый из LPA как EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE , что позволяет приложению-перевозчику отслеживать для целей ведения журнала/отладки. Обратный вызов PendingIntent должен быть BroadcastReceiver .

Чтобы загрузить данную загружаемую подписку (созданную из кода активации или 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*/);
                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);
PendingIntent callbackIntent = PendingIntent.getBroadcast(
        getContext(), 0 /* requestCode */, intent,
        PendingIntent.FLAG_UPDATE_CURRENT);
mgr.downloadSubscription(sub, true /* switchAfterDownload */,
        callbackIntent);

Чтобы переключиться на подписку с идентификатором подписки:

// 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);
PendingIntent callbackIntent = PendingIntent.getBroadcast(
        getContext(), 0 /* requestCode */, intent,
        PendingIntent.FLAG_UPDATE_CURRENT);
mgr.switchToSubscription(1 /* subscriptionId */, callbackIntent);

Полный список API-интерфейсов EuiccManager и примеры кода см. в разделе API-интерфейсы eUICC .

Устранимые ошибки

В некоторых случаях система не может завершить операцию eSIM, но ошибка может быть устранена пользователем. Например, downloadSubscription может завершиться ошибкой, если в метаданных профиля указано, что требуется код подтверждения оператора . Или может произойти сбой switchToSubscription , если приложение оператора связи имеет привилегии оператора над целевым профилем (то есть оператор владеет профилем), но не имеет привилегий оператора над текущим включенным профилем, и, следовательно, требуется согласие пользователя.

В этих случаях обратный вызов вызывающей стороны вызывается с помощью EuiccManager#EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR . Intent обратного вызова содержит внутренние дополнения, поэтому, когда вызывающий объект передает его EuiccManager#startResolutionActivity , разрешение может быть запрошено через LUI. Например, снова используя код подтверждения, EuiccManager#startResolutionActivity запускает экран LUI, который позволяет пользователю ввести код подтверждения; после ввода кода операция загрузки возобновляется. Этот подход предоставляет приложению-носителю полный контроль над тем, когда отображается пользовательский интерфейс, но дает LPA/LUI расширяемый метод для добавления новой обработки восстанавливаемых пользователем проблем в будущем без необходимости изменения клиентских приложений.

Android 9 определяет эти устранимые ошибки в EuiccService , которые должен обрабатывать LUI:

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

Привилегии перевозчика

Если вы являетесь оператором связи, разрабатывающим собственное приложение оператора, которое вызывает EuiccManager для загрузки профилей на устройство, ваш профиль должен включать в метаданные правила привилегий оператора, соответствующие вашему приложению оператора. Это связано с тем, что профили подписки, принадлежащие разным операторам, могут сосуществовать в eUICC устройства, и каждому приложению оператора должен быть разрешен доступ только к профилям, принадлежащим этому оператору. Например, оператор связи А не должен иметь возможности загружать, включать или отключать профиль, принадлежащий оператору связи Б.

Чтобы профиль был доступен только его владельцу, Android использует механизм предоставления специальных привилегий приложению владельца профиля (то есть приложению-носителю). Платформа Android загружает сертификаты, хранящиеся в файле правил доступа профиля (ARF), и предоставляет приложениям, подписанным этими сертификатами, разрешение на вызовы API EuiccManager . Процесс высокого уровня описан ниже:

  1. Оператор подписывает APK приложения оператора; инструмент apksigner прикрепляет сертификат открытого ключа к APK.
  2. Оператор/SM-DP+ подготавливает профиль и его метаданные, которые включают ARF, который содержит:

    1. Подпись (SHA-1 или SHA-256) сертификата открытого ключа приложения-перевозчика (обязательно)
    2. Название пакета мобильного приложения (необязательно)
  3. Приложение Carrier пытается выполнить операцию EuiccManager .

  4. Платформа Android проверяет хэш SHA-1 или SHA-256 сертификата вызывающего приложения на соответствие подписи сертификата, полученного из ARF целевого профиля. Если имя пакета приложения-носителя включено в ARF, оно также должно совпадать с именем пакета вызывающего приложения.

  5. После проверки подписи и имени пакета (если оно включено) привилегия оператора связи предоставляется вызывающему приложению в целевом профиле.

Поскольку метаданные профиля могут быть доступны за пределами самого профиля (чтобы LPA мог получить метаданные профиля из SM-DP+ до загрузки профиля или из ISD-R, когда профиль отключен), они должны содержать те же правила привилегий оператора. как в профиле.

ОС eUICC и SM-DP+ должны поддерживать собственный тег BF76 в метаданных профиля. Содержимое тега должно соответствовать тем же правилам привилегий оператора связи, которые возвращаются апплетом правил доступа (ARA), определенным в привилегиях оператора связи 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
    }
}

Дополнительные сведения о подписании приложений см. в разделе Подпишите приложение . Дополнительные сведения о привилегиях оператора см. в разделе Привилегии оператора UICC .

Создание LPA-приложения

Вы можете реализовать свой собственный LPA, который должен быть связан с Android Euicc API. В следующих разделах дается краткий обзор создания приложения LPA и его интеграции с системой Android.

Требования к оборудованию/модему

LPA и ОС eSIM на чипе eUICC должны поддерживать как минимум GSMA RSP (удаленная подготовка SIM-карты) v2.0 или v2.2. Вы также должны планировать использование серверов SM-DP+ и SM-DS с соответствующей версией RSP. Подробную информацию об архитектуре RSP см. в Спецификации архитектуры RSP GSMA SGP.21 .

Кроме того, для интеграции с API-интерфейсами eUICC в Android 9 модем устройства должен отправлять возможности терминала с закодированной поддержкой возможностей eUICC (локальное управление профилями и загрузка профилей). Также необходимо реализовать следующие методы:

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

  • IRadioConfig HAL v1.0: getSimSlotsStatus

Модем должен распознать eSIM с включенным профилем загрузки по умолчанию как действующую SIM-карту и оставить питание SIM-карты включенным.

Для устройств под управлением Android 10 необходимо определить несъемный массив идентификаторов слотов eUICC. Например, см. 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>

Полный список требований к модему см. в разделе Требования к модему для поддержки eSIM .

EuiccService

LPA состоит из двух отдельных компонентов (оба могут быть реализованы в одном APK): серверной части LPA и пользовательского интерфейса LPA или LUI.

Чтобы реализовать серверную часть LPA, вы должны расширить EuiccService и объявить эту службу в файле манифеста. Службе должно требоваться системное разрешение android.permission.BIND_EUICC_SERVICE , чтобы гарантировать, что только система может привязаться к ней. Служба также должна включать фильтр намерений с действием android.service.euicc.EuiccService . Приоритет фильтра намерений должен быть установлен на ненулевое значение, если на устройстве присутствует несколько реализаций. Например:

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

Внутри платформа Android определяет активный LPA и взаимодействует с ним по мере необходимости для поддержки API-интерфейсов Android eUICC. PackageManager запрашивается для всех приложений с разрешением android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS , которое указывает службу для действия android.service.euicc.EuiccService . Выбирается услуга с наивысшим приоритетом. Если служба не найдена, поддержка LPA отключена.

Чтобы реализовать LUI, вы должны предоставить активность для следующих действий:

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

Как и в случае со службой, каждое действие должно требовать системного разрешения android.permission.BIND_EUICC_SERVICE . У каждого должен быть фильтр намерений с соответствующим действием, категорией android.service.euicc.category.EUICC_UI и ненулевым приоритетом. Аналогичная логика используется для выбора реализации этих действий, как и при выборе реализации EuiccService . Например:

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

Это означает, что пользовательский интерфейс, реализующий эти экраны, может исходить из APK, отличного от того, который реализует EuiccService . Иметь один APK или несколько APK (например, один, который реализует EuiccService , а другой, который предоставляет действия LUI), является выбором дизайна.

EuiccCardManager

EuiccCardManager — это интерфейс для связи с чипом eSIM. Он предоставляет функции ES10 (как описано в спецификации GSMA RSP) и обрабатывает низкоуровневые команды запроса/ответа APDU, а также синтаксический анализ ASN.1. EuiccCardManager — это системный API, который может вызываться только приложениями с системными привилегиями.

Приложения оператора связи, LPA и API Euicc

Рис. 2. И операторское приложение, и LPA используют API-интерфейсы Euicc.

API-интерфейсы операций с профилями через EuiccCardManager требуют, чтобы вызывающий объект был LPA. Это обеспечивается платформой Android. Это означает, что вызывающая сторона должна расширить EuiccService и быть объявлена ​​в вашем файле манифеста, как описано в предыдущих разделах.

Подобно EuiccManager , для использования API EuiccCardManager ваш LPA должен сначала получить экземпляр EuiccCardManager через Context#getSystemService :

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

Затем, чтобы получить все профили на 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);

Внутри EuiccCardManager привязывается к EuiccCardController (который выполняется в процессе телефона) через интерфейс AIDL, и каждый метод EuiccCardManager получает обратный вызов от процесса телефона через другой выделенный интерфейс AIDL. При использовании API EuiccCardManager вызывающая сторона (LPA) должна предоставить объект Executor , через который вызывается обратный вызов. Этот объект Executor может работать в одном потоке или в пуле потоков по вашему выбору.

Большинство API EuiccCardManager имеют одинаковую схему использования. Например, чтобы загрузить связанный пакет профиля на eUICC:

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

Чтобы переключиться на другой профиль с данным ICCID:

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

Чтобы получить адрес SM-DP+ по умолчанию от чипа eUICC:

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

Чтобы получить список уведомлений о заданных событиях уведомлений:

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

Активация профиля eSIM через приложение оператора

На устройствах под управлением Android 9 или выше вы можете использовать приложение оператора для активации eSIM и загрузки профилей. Приложение оператора связи может загружать профили, напрямую вызывая downloadSubscription или предоставляя код активации LPA.

Когда приложение оператора связи загружает профиль с помощью вызова downloadSubscription , этот вызов обеспечивает возможность управления профилем приложением с помощью тега метаданных BF76 , который кодирует правила привилегий оператора связи для профиля. Если профиль не имеет тега BF76 или его тег BF76 не соответствует подписи приложения вызывающего оператора, загрузка отклоняется.

В разделе ниже описывается активация eSIM через приложение оператора связи с использованием кода активации.

Активация eSIM с помощью кода активации

При использовании кода активации для активации профиля eSIM LPA извлекает код активации из приложения оператора связи и загружает профиль. Этот поток может быть инициирован LPA, и LPA может управлять всем потоком пользовательского интерфейса, что означает, что пользовательский интерфейс приложения оператора не отображается. Этот подход обходит проверку тега BF76 , и сетевым операторам не нужно реализовывать весь процесс активации пользовательского интерфейса eSIM, включая загрузку профиля eSIM и обработку ошибок.

Определение службы подготовки eUICC оператора

LPA и приложение оператора взаимодействуют через два интерфейса AIDL : ICarrierEuiccProvisioningService и IGetActivationCodeCallback . Приложение-перевозчик должно реализовать интерфейс ICarrierEuiccProvisioningService и предоставить его в своем объявлении манифеста . LPA должен привязаться к ICarrierEuiccProvisioningService и реализовать IGetActivationCodeCallback . Дополнительные сведения о реализации и предоставлении интерфейса AIDL см. в разделе Определение и интерфейс AIDL .

Чтобы определить интерфейсы AIDL, создайте следующие файлы AIDL как для LPA, так и для приложений-носителей.

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

Пример реализации LPA

Чтобы выполнить привязку к реализации ICarrierEuiccProvisioningService приложения-носителя, LPA должен скопировать оба ICarrierEuiccProvisioningService.aidl и IGetActivationCodeCallback.aidl в ваш проект и реализовать ServiceConnection .

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

После привязки к реализации ICarrierEuiccProvisioningService приложения-носителя LPA вызывает либо getActivationCode , либо getActivationCodeForEid , чтобы получить код активации из приложения-носителя, передав реализацию класса-заглушки IGetActivationCodeCallback .

Разница между getActivationCode и getActivationCodeForEid заключается в том, что getActivationCodeForEid позволяет оператору предварительно привязать профиль к EID устройства до начала процесса загрузки.

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

Пример реализации для мобильного приложения

Чтобы LPA привязывался к приложению-носителю, приложение-носитель должно скопировать оба ICarrierEuiccProvisioningService.aidl и IGetActivationCodeCallback.aidl в ваш проект и объявить службу ICarrierEuiccProvisioningService в файле AndroidManifest.xml . Службе должно требоваться системное разрешение android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS , чтобы гарантировать, что только LPA, приложение с системными привилегиями, может связываться с ней. Служба также должна включать фильтр намерений с действием 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>

Чтобы реализовать службу приложения-носителя AIDL, создайте службу, расширьте класс Stub и реализуйте методы getActivationCode и getActivationCodeForEid . Затем LPA может вызвать любой метод для получения кода активации профиля. Приложение оператора должно ответить вызовом IGetActivationCodeCallback#onSuccess с кодом активации, если код был успешно получен с сервера оператора. В случае неудачи приложение оператора связи должно ответить 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);
            }
      }
}

Запуск пользовательского интерфейса мобильного приложения в потоке активации LPA

На устройствах под управлением Android 11 и выше LPA может запускать пользовательский интерфейс приложения-оператора. Это полезно, поскольку приложению-оператору может потребоваться дополнительная информация от пользователя перед отправкой кода активации в LPA. Например, операторы связи могут потребовать, чтобы пользователи вошли в систему, чтобы активировать свои телефонные номера или выполнить другие услуги переноса.

Это процесс запуска пользовательского интерфейса приложения-носителя в LPA:

  1. LPA запускает поток активации приложения-оператора, отправляя намерение android.service.euicc.action.START_CARRIER_ACTIVATION пакету приложения-оператора, содержащему действие. (Получатель приложения-носителя должен быть защищен в объявлении манифеста с помощью android:permission="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" , чтобы избежать получения намерений от приложений, не относящихся к 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. Приложение оператора выполняет свою работу, используя собственный пользовательский интерфейс. Например, вход пользователя или отправка HTTP-запросов на серверную часть оператора.

  3. Приложение-носитель отвечает на LPA, вызывая setResult(int, Intent) и finish() .

    1. Если приложение-носитель отвечает RESULT_OK , LPA продолжает процесс активации. Если приложение-носитель определяет, что пользователь должен сканировать QR-код вместо того, чтобы позволять LPA привязывать службу приложения-носителя, приложение-носитель отвечает LPA, используя setResult(int, Intent) с RESULT_OK и экземпляром Intent , содержащим логический дополнительный android.telephony.euicc.extra.USE_QR_SCANNER имеет значение true . Затем LPA проверяет лишнее и запускает сканер QR вместо привязки реализации ICarrierEuiccProvisioningService приложения-носителя.
    2. Если приложение оператора дает сбой или отвечает RESULT_CANCELED (это код ответа по умолчанию), LPA отменяет процесс активации eSIM.
    3. Если приложение-носитель отвечает чем-то другим, кроме RESULT_OK или RESULT_CANCELED , LPA рассматривает это как ошибку.

    Из соображений безопасности LPA не должен напрямую принимать код активации, предоставленный в результате, чтобы гарантировать, что вызывающие абоненты, не являющиеся LPA, не смогут получить код активации из приложения оператора связи.

Запуск процесса активации LPA в приложении оператора

Начиная с Android 11, приложения оператора связи могут использовать API-интерфейсы eUICC для запуска LUI для активации eSIM. Этот метод отображает пользовательский интерфейс процесса активации eSIM LPA для активации профиля eSIM. Затем LPA отправляет широковещательную рассылку, когда активация профиля eSIM завершается.

  1. LPA должен объявить действие, включая фильтр намерений, с действием android.service.euicc.action.START_EUICC_ACTIVATION . Приоритет фильтра намерений должен быть установлен на ненулевое значение, если на устройстве присутствует несколько реализаций. Например:

    <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. Приложение оператора выполняет свою работу, используя собственный пользовательский интерфейс. Например, вход пользователя или отправка HTTP-запросов на серверную часть оператора.

  3. На этом этапе приложение оператора связи должно быть готово предоставить код активации через свою реализацию ICarrierEuiccProvisioningService . Приложение оператора запускает LPA, вызывая startActivityForResult(Intent, int) с действием android.telephony.euicc.action.START_EUICC_ACTIVATION . LPA также проверяет логический дополнительный android.telephony.euicc.extra.USE_QR_SCANNER . Если значение равно true , LPA запускает сканер QR, чтобы пользователь мог отсканировать QR-код профиля.

  4. На стороне LPA LPA привязывается к реализации ICarrierEuiccProvisioningService приложения-перевозчика, чтобы получить код активации и загрузить соответствующий профиль. LPA отображает все необходимые элементы пользовательского интерфейса во время загрузки, например экран загрузки.

  5. Когда поток активации LPA завершен, LPA отвечает приложению-перевозчику кодом результата, который приложение-носитель обрабатывает в onActivityResult(int, int, Intent) .

    1. Если LPA удается загрузить новый профиль eSIM, он отвечает RESULT_OK .
    2. Если пользователь отменяет активацию профиля eSIM в LPA, он отвечает RESULT_CANCELED .
    3. Если LPA отвечает чем-то другим, кроме RESULT_OK или RESULT_CANCELED , приложение-носитель рассматривает это как ошибку.

    Из соображений безопасности LPA не принимает код активации непосредственно в предоставленном намерении, чтобы гарантировать, что вызывающие абоненты, не являющиеся LPA, не смогут получить код активации из приложения оператора связи.

Поддержка нескольких eSIM

Для устройств под управлением Android 10 или более поздней версии класс EuiccManager поддерживает устройства с несколькими eSIM. Устройства с одним eSIM, которые обновляются до Android 10, не требуют каких-либо изменений в реализации LPA, поскольку платформа автоматически связывает экземпляр EuiccManager с eUICC по умолчанию. eUICC по умолчанию определяется платформой для устройств с радио HAL версии 1.2 или выше и LPA для устройств с радио HAL версии ниже 1,2.

Требования

Для поддержки нескольких карт eSIM устройство должно иметь более одной карты eUICC, которая может быть либо встроенной картой eUICC, либо физическим слотом для SIM-карты, в который можно вставлять съемные карты eUICC.

Radio HAL версии 1.2 или выше требуется для поддержки нескольких eSIM. Рекомендуется Radio HAL версии 1.4 и RadioConfig HAL версии 1.2.

Реализация

Для поддержки нескольких eSIM (включая съемные eUICC или программируемые SIM-карты) LPA должен реализовать EuiccService , который получает идентификатор слота, соответствующий идентификатору карты, предоставленному вызывающим абонентом.

non_removable_euicc_slots , указанный в arrays.xml , представляет собой массив целых чисел, представляющих идентификаторы слотов встроенных карт eUICC устройства. Вы должны указать этот ресурс, чтобы платформа могла определить, является ли вставленная карта eUICC съемной или нет.

Приложение Carrier для устройства с несколькими eSIM

При создании приложения-носителя для устройства с несколькими eSIM используйте метод createForCardId в EuiccManager , чтобы создать объект EuiccManager , привязанный к данному идентификатору карты. Идентификатор карты — это целочисленное значение, которое однозначно идентифицирует UICC или eUICC на устройстве.

Чтобы получить идентификатор карты для eUICC устройства по умолчанию, используйте метод getCardIdForDefaultEuicc в TelephonyManager . Этот метод возвращает UNSUPPORTED_CARD_ID , если версия радио HAL ниже 1.2, и возвращает UNINITIALIZED_CARD_ID , если устройство не прочитало карту eUICC.

Вы также можете получить идентификаторы карт из getUiccCardsInfo и getUiccSlotsInfo (системный API) в TelephonyManager и getCardId в SubscriptionInfo .

Когда создается экземпляр объекта EuiccManager с определенным идентификатором карты, все операции направляются на карту eUICC с этим идентификатором карты. Если карта eUICC становится недоступной (например, когда она выключена или удалена) EuiccManager больше не работает.

Вы можете использовать следующие примеры кода для создания мобильного приложения.

Пример 1: Получить активную подписку и создать экземпляр 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);

Пример 2. Перебор карт UICC и создание экземпляра EuiccManager для съемной карты eUICC.

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

Проверка

AOSP не поставляется с реализацией LPA, и не ожидается, что LPA будет доступен во всех сборках Android (не каждый телефон поддерживает eSIM). По этой причине не существует сквозных тестов CTS. Однако в AOSP доступны базовые тестовые примеры, чтобы убедиться, что открытые API-интерфейсы eUICC действительны в сборках Android.

Вы должны убедиться, что сборки проходят следующие тестовые случаи CTS (для общедоступных API): /platform/cts/tests/tests/telephony/current/src/android/telephony/euicc/cts .

Операторы связи, внедряющие приложение оператора связи, должны пройти обычные внутренние циклы проверки качества, чтобы убедиться, что все реализованные функции работают должным образом. Как минимум, приложение оператора связи должно иметь возможность перечислить все профили подписки, принадлежащие одному и тому же оператору, загрузить и установить профиль, активировать службу в профиле, переключаться между профилями и удалять профили.

Если вы делаете свой собственный LPA, вы должны пройти гораздо более тщательное тестирование. Вам следует сотрудничать с поставщиком модема, чипом eUICC или поставщиком ОС eSIM, поставщиками SM-DP+ и операторами связи для решения проблем и обеспечения совместимости вашего LPA в архитектуре RSP. Большое количество ручного тестирования неизбежно. Для наилучшего покрытия тестами вы должны следовать плану тестирования GSMA SGP.23 RSP .