Внедрить 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. APK-файл LPA может дополнительно включать компонент пользовательского интерфейса, называемый LPA UI или LUI, который предоставляет конечному пользователю централизованное место для управления всеми встроенными профилями подписки. Платформа Android автоматически обнаруживает и подключается к наиболее подходящему доступному LPA и направляет все операции с eUICC через экземпляр LPA.

Упрощенная архитектура удаленной инициализации SIM-карт (RSP).

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

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

Если вы являетесь производителем устройств и заинтересованы в создании собственного приложения для системы 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.

if (mgr == null || !mgr.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*/);

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

Определите и используйте разрешения в AndroidManifest.xml :

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

Чтобы перейти на подписку по идентификатору подписки:

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

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

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

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

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

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

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

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

Операционная система eUICC и модуль SM-DP+ должны поддерживать собственный тег BF76 в метаданных профиля. Содержимое тега должно соответствовать правилам привилегий оператора связи, возвращаемым апплетом правил доступа (ARA), определенным в UICC Carrier Privileges :

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), который должен быть подключен к API Android Euicc. В следующих разделах представлен краткий обзор создания приложения LPA и его интеграции с системой Android.

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

LPA и операционная система eSIM на чипе eUICC должны поддерживать как минимум GSMA RSP (Remote SIM Provisioning) версии 2.0 или 2.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

  • IRadioConfig AIDL v1.0: getAllowedCarriers

    Google LPA необходимо знать статус блокировки оператора связи, чтобы разрешить загрузку или перенос eSIM-карты только для разрешенного оператора. В противном случае пользователи могут загрузить и перенести SIM-карту, а позже обнаружить, что устройство заблокировано для другого оператора.

    • Производители или OEM-партнеры должны реализовать API HAL IRadioSim.getAllowedCarriers().

    • Производитель RIL/модем должен заполнить статус блокировки и идентификатор оператора связи, к которому привязано устройство, в рамках API HAL IRadioSimResponse.getAllowedCarriersResponse().

Модем должен распознать 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-файлом и несколькими (например, одним, реализующим 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 также проверяет логическое значение extra 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-карты, которая может быть либо встроенной, либо физическим слотом для SIM-карт, в который можно вставлять съемные eUICC-карты.

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

Выполнение

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

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

Приложение оператора связи для устройств с несколькими 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. Значительный объем ручного тестирования неизбежен. Для обеспечения наилучшего охвата тестирования следует следовать плану тестирования RSP GSMA SGP.23 .