eUICC API

在 Android 9 中,設定檔管理 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 資訊並以 SubscriptionInfo 實例的形式管理訂閱(在 GSMA RSP 文件中稱為設定檔)。

營運商應用程式需要具備對應的權限,才能呼叫公用API,包括下載、切換、刪除訂閱等操作。運營商權限由行動運營商添加到配置文件元資料中。 eUICC API 會相應地強制執行運營商特權規則。

Android 平台不處理設定檔策略規則。如果在設定檔元資料中聲明了策略規則,LPA 可以選擇如何處理設定檔下載和安裝過程。例如,第三方 OEM LPA 可以使用特殊錯誤代碼來處理策略規則(錯誤代碼從 OEM LPA 傳遞到平台,然後平台將程式碼傳遞到 OEM LUI)。

有關多個啟用的設定檔 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中的public常數列表,請參閱常數