在 Android 9 中,可透過 EuiccManager
類別使用設定檔管理 API (公開和 @SystemApi)。您可以透過 EuiccCardManager
類別使用 eUICC 通訊 API (僅限 @SystemApi)。
關於 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
您可以在 EuiccManager
參考資料文件和 EuiccManager.java
中找到下列 API。
取得執行個體 (公開)
透過 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 的相關資訊。此屬性包含 OS 版本。詳情請參閱 getEuiccInfo
。
EuiccInfo info = mgr.getEuiccInfo();
String osVer = info.getOsVersion();
下載訂閱 (公開)
下載指定的訂閱項目 (在 GSMA RSP 文件中稱為「profile」)。您可以使用啟用碼建立訂閱。舉例來說,啟用代碼可從 QR code 解析。下載訂閱項目是一項非同步作業。
呼叫端必須具備 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);
使用 port 切換訂閱項目 (公開)
(適用於 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 以上版本) 傳回傳遞的 port 索引是否可用。如果沒有啟用訂閱項目,或呼叫應用程式對所選端口上安裝的訂閱項目具有電信業者權限,則該端口可供使用。詳情請參閱 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
中的 public
常數清單,請參閱「常數」。