在 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 資訊,並以 SubscriptionInfo 例項的形式管理訂閱項目 (在 GSMA RSP 文件中稱為設定檔)。
如要呼叫公用 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 的相關資訊。此屬性包含作業系統版本。詳情請參閱 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);
透過通訊埠 (公開) 切換訂閱方案
(適用於 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
常數清單,請參閱「常數」。