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 앱이 있을 수 있지만 각 앱의 AndroidManifest.xml 파일에 정의된 우선순위에 따라 하나의 LPA만 실제 작동하는 LPA로 선택할 수 있습니다.

EuiccManager 사용

LPA API는 android.telephony.euicc 패키지 아래에 있는 EuiccManager를 통해 공개됩니다. 이동통신사 앱은 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의 인스턴스를 가져옵니다.

EuiccManager mgr = (EuiccManager) context.getSystemService(Context.EUICC_SERVICE);

사용 설정 확인(공개)

삽입된 구독이 사용 설정되어 있는지 확인합니다. LPA API에 액세스하기 전에 이를 확인해야 합니다.

boolean isEnabled = mgr.isEnabled();
if (!isEnabled) {
    return;
}

EID 가져오기(공개)

eUICC 하드웨어를 식별하는 EID를 가져옵니다. 이는 eUICC가 준비되지 않았다면 null이 될 수 있습니다. 호출자에는 이동통신사 권한 또는 READ_PRIVILEGED_PHONE_STATE 권한이 있어야 합니다.

String eid = mgr.getEid();
if (eid == null) {
  // Handle null case.
}

EuiccInfo 가져오기(공개)

eUICC 정보를 가져옵니다. 여기에는 OS 버전이 포함됩니다.

EuiccInfo info = mgr.getEuiccInfo();
String osVer = info.getOsVersion();

구독 다운로드(공개)

지정된 구독을 다운로드합니다(GSMA RSP 문서에서는 '프로필'이라고 함). 구독은 활성화 코드에서 만들 수 있습니다. 예를 들어, 활성화 코드는 QR 코드에서 파싱할 수 있습니다. 구독 다운로드는 비동기 작업입니다.

호출자에는 WRITE_EMBEDDED_SUBSCRIPTIONS 권한 또는 타겟 구독을 위한 이동통신사 권한이 있어야 합니다.

// 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 또는 현재 사용 설정된 구독 및 타겟 구독을 위한 이동통신사 권한이 있어야 합니다.

// 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);

구독 삭제(공개)

구독 ID로 구독을 삭제합니다. 구독이 현재 활성화되어 있다면 먼저 구독이 중지됩니다. 호출자에는 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 */);

// 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 enum 값을 제공하여 모든 테스트, 운영 또는 두 가지 유형의 구독을 모두 삭제할지 지정합니다. 호출자에는 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);

상수

EuiccManagerpublic 상수 목록을 보려면 상수를 참조하세요.