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

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

EuiccМенеджер

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

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

  1. Оператор подписывает APK-файл приложения оператора связи; инструмент apksigner прикрепляет сертификат открытого ключа к APK.
  2. Оператор/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» :

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 (удаленное обеспечение 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

  • IRadioConfig AIDL v1.0: getAllowedCarriers

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

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

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

Модем должен распознать 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 и пользовательского интерфейса или LUI LPA.

Чтобы реализовать серверную часть 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.

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