eUICC API

在 Android 9 中,配置文件管理 API(公共 API 和 @SystemApi)通过 EuiccManager 类提供。eUICC 通信 API(仅限 @SystemApi)通过 EuiccCardManager 类提供。

eUICC 简介

运营商可以构建使用 EuiccManager 管理配置文件的运营商应用(如图 1 所示)。运营商应用不需要是系统应用,但需要拥有 eUICC 配置文件授予的运营商权限。LPA 应用(LUI 和 LPA 后端)需要是系统应用(即包含在系统映像中)才能调用 @SystemApi。

安装了运营商应用和 OEM LPA 的 Android 手机

图 1. 安装了运营商应用和 OEM LPA 的 Android 手机

除了实现用于调用 EuiccCardManager 以及与 eUICC 通信的逻辑之外,LPA 应用还必须实现以下各项:

  • SM-DP+ 客户端,用于与 SM-DP+ 服务器通信以进行身份验证和下载配置文件
  • [可选] SM-DS,用于获得更多潜在的可下载配置文件
  • 通知处理逻辑,用于向服务器发送通知以便更新配置文件状态
  • [可选] 插槽管理逻辑,包括在 eSIM 卡和 pSIM 卡逻辑之间切换。如果手机只有一个 eSIM 卡芯片,则此项为可选项。
  • eSIM 卡 OTA

虽然一部 Android 手机上可以有多个 LPA 应用,但只能根据每个应用的 AndroidManifest.xml 文件中定义的优先级来选择一个 LPA 作为实际运行的 LPA。

使用 EuiccManager

LPA API 通过 EuiccManager(在 android.telephony.euicc 软件包下)公开提供。运营商应用可以获得 EuiccManager 的实例,并调用 EuiccManager 中的方法,以获取 eUICC 信息并将订阅(在 GSMA RSP 文档中称为“配置文件”)作为 SubscriptionInfo 实例进行管理。

如需调用公共 API(包括下载、切换和删除订阅操作),运营商应用必须具有所需的权限。运营商权限由移动运营商添加到配置文件元数据中。eUICC API 会相应地强制执行运营商权限规则。

Android 平台不处理配置文件政策规则。如果政策规则是在配置文件元数据中声明的,则 LPA 可以选择如何处理配置文件下载和安装过程。例如,第三方 OEM LPA 可以使用特殊错误代码处理政策规则(错误代码会从 OEM LPA 传递到平台,然后平台会再将其传递到 OEM LUI)。

API

您可以在 EuiccManager 参考文档EuiccManager.java 中找到以下 API。

获取实例(公共)

通过 Context#getSystemService 获取 EuiccManager 的实例。

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

检查启用情况(公共)

检查是否已启用嵌入式订阅。应在访问 LPA API 之前完成这项检查。

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

获取 EID(公共)

获取用于标识 eUICC 硬件的 EID。如果 eUICC 尚未就绪,IED 可能为 null。调用方必须具备运营商权限或 READ_PRIVILEGED_PHONE_STATE 权限。

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

获取 EuiccInfo(公共)

获取有关 eUICC 的信息,其中包含操作系统版本。

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

下载订阅(公共)

下载指定订阅(在 GSMA RSP 文档中称为“配置文件”)。可以通过激活码创建订阅。例如,可以从二维码中解析激活码。下载订阅是一项异步操作。

调用方必须具有 WRITE_EMBEDDED_SUBSCRIPTIONS 权限或者对目标订阅具备运营商权限。

// 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 权限或者对当前启用的订阅和目标订阅具备运营商权限。

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

删除订阅(公共)

删除具有某个订阅 ID 的订阅。如果相应订阅当前处于活动状态,应先将其停用。调用方必须具有 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 */);

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

启动解决 Activity(公共)

启动一个 Activity 以解决可由用户解决的错误。如果操作返回 EuiccManager#EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR,则可以调用此方法来提示用户解决相应问题。对于同一项错误,此方法只能调用一次。

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

常量

如需查看 EuiccManager 中的 public 常量列表,请参阅常量