在 Android 9 中,可以通过 EuiccManager
类使用配置文件管理 API(公共和 @SystemApi)。eUICC 通信 API(仅限 @SystemApi)可以通过 EuiccCardManager
类使用。
关于 eUICC
运营商可以使用 EuiccManager 创建运营商应用来管理配置文件,如图 1 所示。运营商应用无需是系统应用,但需要具有 eUICC 配置文件授予的运营商权限。LPA 应用(LUI 和 LPA 后端)需要是系统应用(即包含在系统映像中)才能调用 @SystemApi。
图 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 的信息,请参阅多个已启用配置文件。
API
以下 API 可以在 EuiccManager
参考文档和 EuiccManager.java
中找到。
获取实例(公共)
通过 Context#getSystemService
获取 EuiccManager
的实例。有关详细信息,请参阅 getSystemService
。
EuiccManager mgr = (EuiccManager) context.getSystemService(Context.EUICC_SERVICE);
检查是否启用(公共)
检查是否启用了嵌入式订阅。在访问 LPA API 之前,应检查此项。有关详细信息,请参阅 isEnabled
。
boolean isEnabled = mgr.isEnabled();
if (!isEnabled) {
return;
}
获取 EID(公共)
获取标识 eUICC 硬件的 EID。如果 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
。
删除订阅(公共)
删除具有订阅 ID 的订阅。如果订阅当前处于活动状态,则首先禁用它。调用方必须具有 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);
常量
要查看 EuiccManager
中的公共常量列表,请参阅常量。