在 Android 9 中,設定檔管理 API (公開和 @SystemApi) 可透過 EuiccManager
類別取得。eUICC 通訊 API (僅限 @SystemApi) 可透過 EuiccCardManager
類別使用。
關於 eUICC
如圖 1 所示,電信業者可使用 EuiccManager 建立電信業者應用程式來管理設定檔。電信業者應用程式不一定要是系統應用程式,但必須具備 eUICC 設定檔授予的電信業者權限。LPA 應用程式 (LUI 和 LPA 後端) 必須是系統應用程式 (即包含在系統映像檔中),才能呼叫 @SystemApi。
圖 1. 搭載電信業者應用程式和 OEM LPA 的 Android 手機
除了呼叫 EuiccCardManager
和與 eUICC 通訊的邏輯外,LPA 應用程式還必須實作下列項目:
- SM-DP+ 用戶端與 SM-DP+ 伺服器通訊,以驗證及下載設定檔
- [選用] SM-DS,取得更多可下載的設定檔
- 處理通知,將通知傳送至伺服器,以更新設定檔狀態
- [選用] 管理卡槽,包括切換 eSIM 和實體 SIM 卡邏輯。如果手機只有 eSIM 卡晶片,則為選填。
- eSIM 卡 OTA
雖然 Android 手機可以有多個 LPA 應用程式,但根據每個應用程式 AndroidManifest.xml
檔案中定義的優先順序,只能選取一個 LPA 做為實際運作的 LPA。
使用 EuiccManager
LPA API 會透過 EuiccManager
(位於套件 android.telephony.euicc
下方) 公開。電信業者應用程式可以取得 EuiccManager
的執行個體,並呼叫 EuiccManager
中的方法,以 SubscriptionInfo 執行個體的形式取得 eUICC 資訊及管理訂閱 (在 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 文件中稱為「設定檔」)。你可以使用啟用代碼建立訂閱方案。舉例來說,啟用代碼可以從 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) 傳回傳遞的連接埠索引是否可用。如果連接埠未啟用任何訂閱方案,或通話應用程式對所選連接埠上安裝的訂閱方案具有電信業者權限,則該連接埠可供使用。詳情請參閱 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
常數清單,請參閱「常數」。