eUICC API

Android 9 では、クラス EuiccManager を通じてプロファイル管理 API(公開と @SystemApi)を使用できます。eUICC 通信 API(@SystemApi のみ)はクラス EuiccCardManager から使用できます。

eUICC について

携帯通信会社は、図 1 に示すように EuiccManager を使用してプロファイルを管理する携帯通信会社アプリを作成できます。携帯通信会社アプリはシステムアプリである必要はありませんが、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 アプリをインストールできますが、実際に動作する LPA は、各アプリの AndroidManifest.xml ファイルで定義されている優先度に基づいて選択される 1 つの 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 の準備ができていない場合は、null が返されることがあります。呼び出し元には携帯通信会社の権限または 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 ドキュメントでは「プロファイル」と呼ばれる)をダウンロードします。サブスクリプションはアクティベーション コードから作成できます。たとえば、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 が返された場合は、このメソッドを呼び出して、ユーザーに問題の解決を促すことができます。このメソッドは、特定のエラーに対して 1 回だけ呼び出すことができます。

...
mgr.startResolutionActivity(getActivity(), 0 /* requestCode */, resultIntent, callbackIntent);

定数

EuiccManagerpublic 定数のリストについては、定数をご覧ください。