Технология встроенной 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.
Рисунок 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
. Ниже описан общий процесс:
- Оператор подписывает APK-файл приложения-оператора; инструмент apksigner прикрепляет сертификат открытого ключа к APK-файлу.
Оператор/SM-DP+ подготавливает профиль и его метаданные, которые включают ARF, содержащий:
- Подпись (SHA-1 или SHA-256) сертификата открытого ключа приложения оператора (обязательно)
- Название пакета приложения-оператора (настоятельно рекомендуется)
Приложение оператора связи пытается выполнить операцию eUICC с помощью API
EuiccManager
.Платформа Android проверяет хэш SHA-1 или SHA-256 сертификата вызывающего приложения на соответствие подписи сертификата, полученного из ARF целевого профиля. Если имя пакета приложения оператора указано в ARF, оно также должно совпадать с именем пакета вызывающего приложения.
После проверки подписи и имени пакета (если оно включено) вызывающему приложению предоставляется привилегия оператора связи по целевому профилю.
Поскольку метаданные профиля могут быть доступны за пределами самого профиля (чтобы 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-карт) версии 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 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 и пользовательский интерфейс 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, который может вызываться только системными приложениями.
Рисунок 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:
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);
Приложение оператора связи выполняет свою работу, используя собственный пользовательский интерфейс. Например, оно авторизует пользователя или отправляет HTTP-запросы в бэкенд оператора связи.
Приложение-оператор реагирует на LPA, вызывая
setResult(int, Intent)
иfinish()
.- Если приложение оператора связи отвечает
RESULT_OK
, LPA продолжает процесс активации. Если приложение оператора связи определяет, что пользователю следует отсканировать QR-код вместо того, чтобы позволить LPA привязать службу приложения оператора, приложение оператора отвечает LPA, используяsetResult(int, Intent)
сRESULT_OK
и экземпляромIntent
, содержащим логическое дополнениеandroid.telephony.euicc.extra.USE_QR_SCANNER
, установленное вtrue
. Затем LPA проверяет это дополнение и запускает QR-сканер вместо привязки реализацииICarrierEuiccProvisioningService
приложения оператора связи. - Если приложение оператора дает сбой или отвечает кодом
RESULT_CANCELED
(это код ответа по умолчанию), LPA отменяет процесс активации eSIM. - Если приложение оператора связи отвечает чем-то, отличным от
RESULT_OK
илиRESULT_CANCELED
, LPA рассматривает это как ошибку.
По соображениям безопасности LPA не должен напрямую принимать код активации, предоставленный в результате намерения, чтобы гарантировать, что вызывающие абоненты, не являющиеся LPA, не смогут получить код активации из приложения оператора.
- Если приложение оператора связи отвечает
Запустите процесс активации LPA в приложении оператора
Начиная с Android 11, приложения операторов связи могут использовать API eUICC для запуска LUI-интерфейса активации eSIM. Этот метод использует пользовательский интерфейс LPA для активации профиля eSIM. После завершения активации профиля eSIM LPA отправляет широковещательное сообщение.
В 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>
Приложение оператора связи выполняет свою работу, используя собственный пользовательский интерфейс. Например, оно авторизует пользователя или отправляет HTTP-запросы в бэкенд оператора связи.
На этом этапе приложение оператора связи должно быть готово предоставить код активации через реализацию
ICarrierEuiccProvisioningService
. Приложение оператора связи запускает LPA, вызываяstartActivityForResult(Intent, int)
с действиемandroid.telephony.euicc.action.START_EUICC_ACTIVATION
. LPA также проверяет логическое значение параметраandroid.telephony.euicc.extra.USE_QR_SCANNER
. Если значение равноtrue
, LPA запускает QR-сканер, чтобы пользователь мог отсканировать QR-код профиля.Со стороны LPA, LPA привязывается к реализации
ICarrierEuiccProvisioningService
в приложении оператора связи для получения кода активации и загрузки соответствующего профиля. Во время загрузки LPA отображает все необходимые элементы пользовательского интерфейса, например, загрузочный экран.После завершения потока активации LPA LPA отвечает приложению-носителю кодом результата, который приложение-носитель обрабатывает в
onActivityResult(int, int, Intent)
.- Если LPA успешно загружает новый профиль eSIM, он отвечает
RESULT_OK
. - Если пользователь отменяет активацию профиля eSIM в LPA, система отвечает
RESULT_CANCELED
. - Если LPA отвечает чем-то, отличным от
RESULT_OK
илиRESULT_CANCELED
, приложение оператора связи воспринимает это как ошибку.
По соображениям безопасности LPA не принимает код активации непосредственно в предоставленном намерении, чтобы гарантировать, что абоненты, не являющиеся пользователями LPA, не смогут получить код активации из приложения оператора.
- Если LPA успешно загружает новый профиль eSIM, он отвечает
Поддержка нескольких 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. Значительный объём ручного тестирования неизбежен. Для наилучшего покрытия тестами следует следовать плану тестирования RSP GSMA SGP.23 .