eUICC API

В Android 9 API управления профилями (публичные и @SystemApi) доступны через класс EuiccManager . API связи eUICC (только @SystemApi) доступны через класс EuiccCardManager .

О eUICC

Операторы могут создавать приложения оператора с помощью EuiccManager для управления профилями, как показано на рисунке 1. Приложения оператора не обязательно должны быть системными приложениями, но должны иметь привилегии оператора, предоставленные профилями eUICC. Приложение LPA (LUI и бэкэнд LPA) должно быть системным приложением (т. е. быть включенным в образ системы) для вызова @SystemApi.

Телефон Android с приложением оператора и OEM LPA

Рисунок 1. Телефоны Android с приложением оператора и OEM LPA

Помимо логики вызова EuiccCardManager и взаимодействия с eUICC, приложения LPA должны реализовывать следующее:

  • Клиент SM-DP+ взаимодействует с сервером SM-DP+ для аутентификации и загрузки профилей
  • [Необязательно] SM-DS для получения большего количества потенциально загружаемых профилей
  • Обработка уведомлений для отправки уведомлений на сервер для обновления состояния профиля
  • [Необязательно] Управление слотами, включая переключение между логикой eSIM и pSIM. Это необязательно, если в телефоне есть только чип eSIM.
  • eSIM OTA

Хотя на телефоне Android может присутствовать более одного приложения LPA, только одно LPA может быть выбрано в качестве фактического рабочего LPA на основе приоритета, определенного в файле AndroidManifest.xml каждого приложения.

Использовать EuiccManager

API LPA являются общедоступными через EuiccManager (в пакете android.telephony.euicc ). Приложение оператора может получить экземпляр EuiccManager и вызвать методы в EuiccManager для получения информации eUICC и управления подписками (называемые профилями в документах GSMA RSP) как экземплярами SubscriptionInfo.

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

Платформа Android не обрабатывает правила политики профиля. Если правило политики объявлено в метаданных профиля, LPA может выбрать, как обрабатывать процедуру загрузки и установки профиля. Например, сторонний OEM LPA может обрабатывать правила политики с использованием специального кода ошибки (код ошибки передается из OEM LPA на платформу, затем платформа передает код в OEM LUI).

Информацию об API с несколькими включенными профилями см. в разделе Несколько включенных профилей .

API-интерфейсы

Следующие API можно найти в справочной документации EuiccManager и EuiccManager.java .

Получить экземпляр (публичный)

Получает экземпляр EuiccManager через Context#getSystemService . Подробности см. в getSystemService .

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

Проверка включена (публично)

Проверяет, включена ли встроенная подписка. Это следует проверить перед доступом к API LPA. Подробности см. в isEnabled .

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

Получить EID (публичный)

Получает EID, идентифицирующий оборудование eUICC. Может быть null, если eUICC не готов. Вызывающий должен иметь привилегию оператора или разрешение READ_PRIVILEGED_PHONE_STATE . Подробности см. в getEid .

String eid = mgr.getEid();
if (eid == null) {
  // Handle null case.
}

Получить EuiccInfo (публично)

Получает информацию о eUICC. Содержит версию ОС. Подробности см. в getEuiccInfo .

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

Скачать подписку (публично)

Загружает указанную подписку (называется «профилем» в документах GSMA RSP). Подписка может быть создана из кода активации. Например, код активации может быть проанализирован из QR-кода. Загрузка подписки — асинхронная операция.

Вызывающий должен иметь разрешение WRITE_EMBEDDED_SUBSCRIPTIONS или иметь привилегии оператора для целевой подписки. Подробности см. downloadSubscription .

// Register receiver.
String action = "download_subscription";
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),
        "example.broadcast.permission" /* broadcastPermission*/, null /* handler */);

// Download subscription asynchronously.
DownloadableSubscription sub =
        DownloadableSubscription.forActivationCode(code /* encodedActivationCode*/);
Intent intent = new Intent(action);
PendingIntent callbackIntent = PendingIntent.getBroadcast(
        getContext(), 0 /* requestCode */, intent, PendingIntent.FLAG_UPDATE_CURRENT);
mgr.downloadSubscription(sub, true /* switchAfterDownload */, callbackIntent);

Переключить подписку (публичную)

Переключает на (включает) указанную подписку. Вызывающий должен либо иметь WRITE_EMBEDDED_SUBSCRIPTIONS , либо иметь привилегии оператора для текущей включенной подписки и целевой подписки. Подробности см. в switchToSubscription .

// Register receiver.
String action = "switch_to_subscription";
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),
        "example.broadcast.permission" /* broadcastPermission*/, null /* handler */);

// Switch to a subscription asynchronously.
Intent intent = new Intent(action);
PendingIntent callbackIntent = PendingIntent.getBroadcast(
        getContext(), 0 /* requestCode */, intent, PendingIntent.FLAG_UPDATE_CURRENT);
mgr.switchToSubscription(1 /* subscriptionId */, callbackIntent);

Подписка на коммутатор с портом (публичная)

(Доступно с Android 13) Переключает на (включает) указанную подписку с указанным индексом порта. Вызывающий должен либо иметь WRITE_EMBEDDED_SUBSCRIPTIONS , либо иметь привилегии оператора для текущей включенной подписки и целевой подписки. Подробности см. switchToSubscription .

// Register receiver.
String action = "switch_to_subscription";
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),
        "example.broadcast.permission" /* broadcastPermission*/, null /* handler */);

// Switch to a subscription asynchronously.
Intent intent = new Intent(action);
PendingIntent callbackIntent = PendingIntent.getBroadcast(
        getContext(), 0 /* requestCode */, intent, PendingIntent.FLAG_UPDATE_CURRENT);
mgr.switchToSubscription(1 /* subscriptionId */, 0 /*portIndex*/, callbackIntent);

Доступен ли порт SIM (общедоступный)?

public boolean isSimPortAvailable(int portIndex)

(Доступно с Android 13) Возвращает, доступен ли индекс порта передачи. Порт доступен, если у него нет включенной подписки или вызывающее приложение имеет привилегию оператора над подпиской, установленной на выбранном порту. Подробности см. isSimPortAvailable .

Удалить подписку (публичную)

Удаляет подписку с идентификатором подписки. Если подписка в данный момент активна, она сначала отключается. Вызывающий должен иметь либо WRITE_EMBEDDED_SUBSCRIPTIONS , либо привилегии оператора для целевой подписки. Подробности см. в разделе deleteSubscription .

// Register receiver.
String action = "delete_subscription";
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),
        "example.broadcast.permission" /* broadcastPermission*/,
        null /* handler */);

// Delete a subscription asynchronously.
Intent intent = new Intent(action);
PendingIntent callbackIntent = PendingIntent.getBroadcast(
        getContext(), 0 /* requestCode */, intent, PendingIntent.FLAG_UPDATE_CURRENT);
mgr.deleteSubscription(1 /* subscriptionId */, callbackIntent);

Удалить все подписки (системный API)

Стирает все подписки на устройстве. Начиная с Android 11, необходимо указать значение перечисления EuiccCardManager#ResetOption , чтобы указать, следует ли стирать все тестовые, рабочие или оба типа подписок. Вызывающий должен иметь разрешение WRITE_EMBEDDED_SUBSCRIPTIONS .

// Register receiver.
String action = "delete_subscription";
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),
        "example.broadcast.permission" /* broadcastPermission*/,
        null /* handler */);

// Erase all operational subscriptions asynchronously.
Intent intent = new Intent(action);
PendingIntent callbackIntent = PendingIntent.getBroadcast(
        getContext(), 0 /* requestCode */, intent, PendingIntent.FLAG_UPDATE_CURRENT);
mgr.eraseSubscriptions(
        EuiccCardManager.RESET_OPTION_DELETE_OPERATIONAL_PROFILES, callbackIntent);

Начать разрешение деятельности (публично)

Запускает действие для устранения ошибки, которую может устранить пользователь. Если операция возвращает EuiccManager#EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR , этот метод можно вызвать, чтобы предложить пользователю устранить проблему. Этот метод можно вызвать только один раз для конкретной ошибки.

...
mgr.startResolutionActivity(getActivity(), 0 /* requestCode */, resultIntent, callbackIntent);

Константы

Чтобы просмотреть список public констант в EuiccManager , см. Константы .