Triển khai eSIM

Công nghệ SIM nhúng (eSIM hoặc eUICC) cho phép người dùng di động tải xuống hồ sơ nhà cung cấp dịch vụ và kích hoạt dịch vụ của nhà cung cấp dịch vụ mà không cần có thẻ SIM vật lý. Đó là thông số kỹ thuật toàn cầu do GSMA điều khiển cho phép cung cấp SIM từ xa (RSP) cho mọi thiết bị di động. Bắt đầu từ Android 9, khung Android cung cấp các API tiêu chuẩn để truy cập eSIM và quản lý hồ sơ đăng ký trên eSIM. Các API eUICC này cho phép các bên thứ ba phát triển ứng dụng của nhà mạng và trợ lý hồ sơ cục bộ (LPA) của riêng họ trên các thiết bị Android hỗ trợ eSIM.

LPA là một ứng dụng hệ thống độc lập nên được đưa vào hình ảnh bản dựng Android. Việc quản lý hồ sơ trên eSIM thường do LPA thực hiện vì nó đóng vai trò là cầu nối giữa SM-DP+ (dịch vụ từ xa chuẩn bị, lưu trữ và phân phối các gói hồ sơ đến thiết bị) và chip eUICC. APK LPA có thể tùy ý bao gồm một thành phần giao diện người dùng, được gọi là LPA UI hoặc LUI, để cung cấp vị trí trung tâm cho người dùng cuối quản lý tất cả hồ sơ đăng ký được nhúng. Khung Android tự động phát hiện và kết nối với LPA tốt nhất hiện có, đồng thời định tuyến tất cả hoạt động eUICC thông qua phiên bản LPA.

Kiến trúc cung cấp SIM từ xa (RSP) được đơn giản hóa

Hình 1. Kiến trúc RSP đơn giản hóa

Các nhà khai thác mạng di động quan tâm đến việc tạo ứng dụng của nhà cung cấp dịch vụ nên xem xét các API trong EuiccManager , nơi cung cấp các hoạt động quản lý hồ sơ cấp cao như downloadSubscription() , switchToSubscription()deleteSubscription() .

Nếu bạn là OEM thiết bị quan tâm đến việc tạo ứng dụng hệ thống LPA của riêng mình, bạn phải mở rộng EuiccService cho khung Android để kết nối với các dịch vụ LPA của mình. Ngoài ra, bạn nên sử dụng các API trong EuiccCardManager , cung cấp các chức năng ES10x dựa trên GSMA RSP v2.0. Các hàm này được sử dụng để ra lệnh cho chip eUICC, chẳng hạn như prepareDownload() , loadBoundProfilePackage() , retrieveNotificationList()resetMemory() .

Các API trong EuiccManager yêu cầu ứng dụng LPA được triển khai đúng cách để hoạt động và lệnh gọi API EuiccCardManager phải là LPA. Điều này được thực thi bởi khung Android.

Các thiết bị chạy Android 10 trở lên có thể hỗ trợ các thiết bị có nhiều eSIM. Để biết thêm thông tin, hãy xem Hỗ trợ nhiều eSIM .

Tạo ứng dụng của nhà mạng

API eUICC trong Android 9 giúp các nhà khai thác mạng di động có thể tạo các ứng dụng mang nhãn hiệu nhà mạng để quản lý trực tiếp hồ sơ của họ. Điều này bao gồm việc tải xuống và xóa hồ sơ đăng ký do nhà cung cấp dịch vụ sở hữu cũng như chuyển sang hồ sơ do nhà cung cấp dịch vụ sở hữu.

EuiccQuản lý

EuiccManager là điểm truy cập chính để các ứng dụng tương tác với LPA. Điều này bao gồm các ứng dụng của nhà cung cấp dịch vụ tải xuống, xóa và chuyển sang các đăng ký do nhà cung cấp dịch vụ sở hữu. Điều này cũng bao gồm ứng dụng hệ thống LUI, cung cấp vị trí/giao diện người dùng trung tâm để quản lý tất cả các đăng ký được nhúng và có thể là một ứng dụng riêng biệt với ứng dụng cung cấp EuiccService .

Để sử dụng các API công khai, trước tiên ứng dụng của nhà cung cấp dịch vụ phải lấy phiên bản của EuiccManager thông qua Context#getSystemService :

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

Bạn nên kiểm tra xem eSIM có được hỗ trợ trên thiết bị hay không trước khi thực hiện bất kỳ thao tác eSIM nào. EuiccManager#isEnabled() thường trả về true nếu tính năng android.hardware.telephony.euicc được xác định và có gói LPA.

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

Để nhận thông tin về phần cứng eUICC và phiên bản hệ điều hành eSIM:

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

Nhiều API, chẳng hạn như downloadSubscription()switchToSubscription() , sử dụng lệnh gọi lại PendingIntent vì chúng có thể mất vài giây hoặc thậm chí vài phút để hoàn thành. PendingIntent được gửi cùng với mã kết quả trong không gian EuiccManager#EMBEDDED_SUBSCRIPTION_RESULT_ , cung cấp mã lỗi do khung xác định cũng như mã kết quả chi tiết tùy ý được truyền từ LPA dưới dạng EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE , cho phép ứng dụng của nhà cung cấp dịch vụ theo dõi nhằm mục đích ghi nhật ký/gỡ lỗi. Lệnh gọi lại PendingIntent phải là BroadcastReceiver .

Để tải xuống đăng ký có thể tải xuống nhất định (được tạo từ mã kích hoạt hoặc mã QR):

// Register receiver.
static final String ACTION_DOWNLOAD_SUBSCRIPTION = "download_subscription";
static final String LPA_DECLARED_PERMISSION
    = "com.your.company.lpa.permission.BROADCAST";
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*/);

                // If the result code is a resolvable error, call startResolutionActivity
                if (resultCode == EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR) {
                    PendingIntent callbackIntent = PendingIntent.getBroadcast(
                        getContext(), 0 /* requestCode */, intent,
                        PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
                    mgr.startResolutionActivity(
                        activity,
                        0 /* requestCode */,
                        intent,
                        callbackIntent);
                }

                resultIntent = intent;
            }
        };
context.registerReceiver(receiver,
        new IntentFilter(ACTION_DOWNLOAD_SUBSCRIPTION),
        LPA_DECLARED_PERMISSION /* broadcastPermission*/,
        null /* handler */);

// Download subscription asynchronously.
DownloadableSubscription sub = DownloadableSubscription
        .forActivationCode(code /* encodedActivationCode*/);
Intent intent = new Intent(action).setPackage(context.getPackageName());
PendingIntent callbackIntent = PendingIntent.getBroadcast(
        getContext(), 0 /* requestCode */, intent,
        PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
mgr.downloadSubscription(sub, true /* switchAfterDownload */,
        callbackIntent);

Xác định và sử dụng quyền trong AndroidManifest.xml :

    <permission android:protectionLevel="signature" android:name="com.your.company.lpa.permission.BROADCAST" />
    <uses-permission android:name="com.your.company.lpa.permission.BROADCAST"/>

Để chuyển sang đăng ký với ID đăng ký:

// Register receiver.
static final String ACTION_SWITCH_TO_SUBSCRIPTION = "switch_to_subscription";
static final String LPA_DECLARED_PERMISSION
    = "com.your.company.lpa.permission.BROADCAST";
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_SWITCH_TO_SUBSCRIPTION),
        LPA_DECLARED_PERMISSION /* broadcastPermission*/,
        null /* handler */);

// Switch to a subscription asynchronously.
Intent intent = new Intent(action).setPackage(context.getPackageName());
PendingIntent callbackIntent = PendingIntent.getBroadcast(
        getContext(), 0 /* requestCode */, intent,
        PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
mgr.switchToSubscription(1 /* subscriptionId */, callbackIntent);

Để biết danh sách đầy đủ các API EuiccManager và ví dụ về mã, hãy xem API eUICC .

Lỗi có thể giải quyết được

Có một số trường hợp hệ thống không thể hoàn tất thao tác eSIM nhưng lỗi có thể được người dùng khắc phục. Ví dụ: downloadSubscription có thể không thành công nếu siêu dữ liệu hồ sơ chỉ ra rằng cần phải có mã xác nhận của nhà cung cấp dịch vụ . Hoặc switchToSubscription có thể không thành công nếu ứng dụng của nhà cung cấp dịch vụ có đặc quyền của nhà cung cấp dịch vụ đối với hồ sơ đích (nghĩa là nhà cung cấp dịch vụ sở hữu hồ sơ) nhưng không có đặc quyền của nhà cung cấp dịch vụ đối với hồ sơ hiện được bật và do đó cần có sự đồng ý của người dùng.

Đối với những trường hợp này, cuộc gọi lại của người gọi được gọi với EuiccManager#EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR . Intent gọi lại chứa các phần bổ sung nội bộ sao cho khi người gọi chuyển nó tới EuiccManager#startResolutionActivity , độ phân giải có thể được yêu cầu thông qua LUI. Ví dụ: sử dụng lại mã xác nhận, EuiccManager#startResolutionActivity kích hoạt màn hình LUI cho phép người dùng nhập mã xác nhận; sau khi nhập mã, thao tác tải xuống sẽ được tiếp tục. Cách tiếp cận này cung cấp cho ứng dụng của nhà cung cấp dịch vụ toàn quyền kiểm soát thời điểm giao diện người dùng được hiển thị, nhưng cung cấp cho LPA/LUI một phương pháp có thể mở rộng để thêm cách xử lý mới cho các vấn đề mà người dùng có thể khôi phục trong tương lai mà không cần thay đổi ứng dụng khách.

Android 9 xác định các lỗi có thể giải quyết này trong EuiccService mà LUI sẽ xử lý:

/**
 * Alert the user that this action will result in an active SIM being
 * deactivated. To implement the LUI triggered by the system, you need to define
 * this in AndroidManifest.xml.
 */
public static final String ACTION_RESOLVE_DEACTIVATE_SIM =
        "android.service.euicc.action.RESOLVE_DEACTIVATE_SIM";
/**
 * Alert the user about a download/switch being done for an app that doesn't
 * currently have carrier privileges.
 */
public static final String ACTION_RESOLVE_NO_PRIVILEGES =
        "android.service.euicc.action.RESOLVE_NO_PRIVILEGES";

/** Ask the user to resolve all the resolvable errors. */
public static final String ACTION_RESOLVE_RESOLVABLE_ERRORS =
        "android.service.euicc.action.RESOLVE_RESOLVABLE_ERRORS";

Đặc quyền của nhà cung cấp dịch vụ

Nếu bạn là nhà cung cấp dịch vụ đang phát triển ứng dụng nhà cung cấp dịch vụ của riêng mình gọi EuiccManager để tải hồ sơ xuống thiết bị thì hồ sơ của bạn phải bao gồm các quy tắc đặc quyền của nhà cung cấp dịch vụ tương ứng với ứng dụng nhà cung cấp dịch vụ của bạn trong siêu dữ liệu. Điều này là do hồ sơ đăng ký thuộc các nhà cung cấp dịch vụ khác nhau có thể cùng tồn tại trong eUICC của thiết bị và mỗi ứng dụng của nhà cung cấp dịch vụ chỉ được phép truy cập vào hồ sơ do nhà cung cấp dịch vụ đó sở hữu. Ví dụ: nhà cung cấp dịch vụ A sẽ không thể tải xuống, bật hoặc tắt cấu hình do nhà cung cấp dịch vụ B sở hữu.

Để đảm bảo chỉ chủ sở hữu hồ sơ mới có thể truy cập được hồ sơ, Android sử dụng cơ chế cấp các đặc quyền cho ứng dụng của chủ sở hữu hồ sơ (tức là ứng dụng của nhà cung cấp dịch vụ). Nền tảng Android tải các chứng chỉ được lưu trữ trong tệp quy tắc truy cập (ARF) của cấu hình và cấp quyền cho các ứng dụng được các chứng chỉ này ký để thực hiện lệnh gọi tới API EuiccManager . Quá trình cấp cao được mô tả dưới đây:

  1. Nhà điều hành ký APK ứng dụng của nhà cung cấp dịch vụ; công cụ apksigner đính kèm chứng chỉ khóa công khai vào APK.
  2. Nhà điều hành/SM-DP+ chuẩn bị hồ sơ và siêu dữ liệu của nó, bao gồm ARF có chứa:

    1. Chữ ký (SHA-1 hoặc SHA-256) trên chứng chỉ khóa công khai của ứng dụng của nhà cung cấp dịch vụ (bắt buộc)
    2. Tên gói của ứng dụng của nhà mạng (được khuyến nghị)
  3. Ứng dụng của nhà cung cấp dịch vụ cố gắng thực hiện thao tác eUICC thông qua API EuiccManager .

  4. Nền tảng Android xác minh hàm băm SHA-1 hoặc SHA-256 của chứng chỉ của ứng dụng người gọi khớp với chữ ký của chứng chỉ thu được từ ARF của hồ sơ đích. Nếu tên gói của ứng dụng của nhà cung cấp dịch vụ được bao gồm trong ARF thì tên đó cũng phải khớp với tên gói của ứng dụng người gọi.

  5. Sau khi chữ ký và tên gói (nếu có) được xác minh, đặc quyền của nhà cung cấp dịch vụ sẽ được cấp cho ứng dụng người gọi qua hồ sơ đích.

Vì siêu dữ liệu hồ sơ có thể có sẵn bên ngoài hồ sơ (để LPA có thể truy xuất siêu dữ liệu hồ sơ từ SM-DP+ trước khi hồ sơ được tải xuống hoặc từ ISD-R khi hồ sơ bị tắt), nên nó phải chứa các quy tắc đặc quyền của nhà cung cấp dịch vụ tương tự như trong hồ sơ.

Hệ điều hành eUICC và SM-DP+ phải hỗ trợ thẻ độc quyền BF76 trong siêu dữ liệu hồ sơ. Nội dung thẻ phải có cùng quy tắc đặc quyền của nhà cung cấp dịch vụ được trả về bởi applet quy tắc truy cập (ARA) được xác định trong Đặc quyền của nhà cung cấp UICC :

RefArDo ::= [PRIVATE 2] SEQUENCE {  -- Tag E2
    refDo [PRIVATE 1] SEQUENCE {  -- Tag E1
        deviceAppIdRefDo [PRIVATE 1] OCTET STRING (SIZE(20|32)),  -- Tag C1
        pkgRefDo [PRIVATE 10] OCTET STRING (SIZE(0..127)) OPTIONAL  -- Tag CA
    },
    arDo [PRIVATE 3] SEQUENCE {  -- Tag E3
        permArDo [PRIVATE 27] OCTET STRING (SIZE(8))  -- Tag DB
    }
}

Để biết thêm chi tiết về ký ứng dụng, hãy xem Ký ứng dụng của bạn . Để biết chi tiết về các đặc quyền của nhà cung cấp dịch vụ, hãy xem Đặc quyền của nhà cung cấp dịch vụ UICC .

Tạo ứng dụng trợ lý hồ sơ địa phương

Các nhà sản xuất thiết bị có thể triển khai trợ lý hồ sơ cục bộ (LPA) của riêng họ, trợ lý này phải được kết nối với API Android Euicc. Các phần sau đây cung cấp thông tin tổng quan ngắn gọn về cách tạo ứng dụng LPA và tích hợp nó với hệ thống Android.

Yêu cầu về phần cứng/modem

LPA và hệ điều hành eSIM trên chip eUICC phải hỗ trợ ít nhất GSMA RSP (Cung cấp SIM từ xa) v2.0 hoặc v2.2. Bạn cũng nên lập kế hoạch sử dụng máy chủ SM-DP+ và SM-DS có phiên bản RSP phù hợp. Để biết kiến ​​trúc RSP chi tiết, hãy xem Đặc tả kiến ​​trúc GSMA SGP.21 RSP .

Ngoài ra, để tích hợp với API eUICC trong Android 9, modem thiết bị phải gửi các khả năng đầu cuối có hỗ trợ các khả năng eUICC được mã hóa (quản lý hồ sơ cục bộ và tải xuống hồ sơ). Nó cũng cần phải thực hiện các phương pháp sau:

  • IRadio HAL v1.1: setSimPower
  • IRadio HAL v1.2: getIccCardStatus

  • IRadioConfig HAL v1.0: getSimSlotsStatus

  • IRadioConfig AIDL v1.0: getAllowedCarriers

    Google LPA cần biết trạng thái khóa nhà mạng để có thể chỉ cho phép tải xuống hoặc chuyển eSIM đối với nhà mạng được phép. Nếu không, người dùng có thể tải xuống và chuyển SIM và sau đó nhận ra thiết bị bị khóa bởi một nhà cung cấp dịch vụ khác.

    • Nhà cung cấp hoặc OEM phải triển khai API IRadioSim.getAllowedCarriers()HAL.

    • RIL / Modem của nhà cung cấp sẽ điền trạng thái khóa và CarrierId của nhà cung cấp dịch vụ nơi thiết bị bị khóa như một phần của API IRadioSimResponse.getAllowedCarriersResponse()HAL.

Modem sẽ nhận dạng eSIM có cấu hình khởi động mặc định được bật làm SIM hợp lệ và luôn bật nguồn SIM.

Đối với các thiết bị chạy Android 10, phải xác định mảng ID vị trí eUICC không thể tháo rời. Ví dụ: xem arrays.xml .

<resources>
   <!-- Device-specific array of SIM slot indexes which are are embedded eUICCs.
        e.g. If a device has two physical slots with indexes 0, 1, and slot 1 is an
        eUICC, then the value of this array should be:
            <integer-array name="non_removable_euicc_slots">
                <item>1</item>
            </integer-array>
        If a device has three physical slots and slot 1 and 2 are eUICCs, then the value of
        this array should be:
            <integer-array name="non_removable_euicc_slots">
               <item>1</item>
               <item>2</item>
            </integer-array>
        This is used to differentiate between removable eUICCs and built in eUICCs, and should
        be set by OEMs for devices which use eUICCs. -->

   <integer-array name="non_removable_euicc_slots">
       <item>1</item>
   </integer-array>
</resources>

Để biết danh sách đầy đủ các yêu cầu về modem, hãy xem Yêu cầu về modem để hỗ trợ eSIM .

Dịch vụ Euicc

LPA bao gồm hai thành phần riêng biệt (cả hai đều có thể được triển khai trong cùng một APK): phần phụ trợ LPA và LPA UI hoặc LUI.

Để triển khai phần phụ trợ LPA, bạn phải mở rộng EuiccService và khai báo dịch vụ này trong tệp kê khai của mình. Dịch vụ phải yêu cầu quyền hệ thống android.permission.BIND_EUICC_SERVICE để đảm bảo rằng chỉ hệ thống mới có thể liên kết với nó. Dịch vụ này cũng phải bao gồm bộ lọc ý định với hành động android.service.euicc.EuiccService . Mức độ ưu tiên của bộ lọc ý định phải được đặt thành giá trị khác 0 trong trường hợp có nhiều triển khai trên thiết bị. Ví dụ:

<service
    android:name=".EuiccServiceImpl"
    android:permission="android.permission.BIND_EUICC_SERVICE">
    <intent-filter android:priority="100">
        <action android:name="android.service.euicc.EuiccService" />
    </intent-filter>
</service>

Trong nội bộ, khung Android xác định LPA đang hoạt động và tương tác với nó khi cần để hỗ trợ API eUICC của Android. PackageManager được truy vấn cho tất cả các ứng dụng có quyền android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS , quyền này chỉ định một dịch vụ cho hành động android.service.euicc.EuiccService . Dịch vụ có mức độ ưu tiên cao nhất sẽ được chọn. Nếu không tìm thấy dịch vụ nào, hỗ trợ LPA sẽ bị tắt.

Để triển khai LUI, bạn phải cung cấp hoạt động cho các hành động sau:

  • android.service.euicc.action.MANAGE_EMBEDDED_SUBSCRIPTIONS
  • android.service.euicc.action.PROVISION_EMBEDDED_SUBSCRIPTION

Giống như dịch vụ, mỗi hoạt động phải yêu cầu quyền hệ thống android.permission.BIND_EUICC_SERVICE . Mỗi bộ lọc phải có một bộ lọc ý định với hành động thích hợp, danh mục android.service.euicc.category.EUICC_UI và mức độ ưu tiên khác 0. Logic tương tự được sử dụng để chọn cách triển khai cho các hoạt động này cũng như khi chọn cách triển khai EuiccService . Ví dụ:

<activity android:name=".MyLuiActivity"
          android:exported="true"
          android:permission="android.permission.BIND_EUICC_SERVICE">
    <intent-filter android:priority="100">
        <action android:name="android.service.euicc.action.MANAGE_EMBEDDED_SUBSCRIPTIONS" />
        <action android:name="android.service.euicc.action.PROVISION_EMBEDDED_SUBSCRIPTION" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.service.euicc.category.EUICC_UI" />
    </intent-filter>
</activity>

Điều này ngụ ý rằng giao diện người dùng triển khai các màn hình này có thể đến từ một APK khác với APK triển khai EuiccService . Việc có một APK duy nhất hay nhiều APK (ví dụ: một APK triển khai EuiccService và một APK cung cấp các hoạt động LUI) là một lựa chọn thiết kế.

EuiccTrình quản lý thẻ

EuiccCardManager là giao diện để giao tiếp với chip eSIM. Nó cung cấp các chức năng ES10 (như được mô tả trong thông số GSMA RSP) và xử lý các lệnh yêu cầu/phản hồi APDU cấp thấp cũng như phân tích cú pháp ASN.1. EuiccCardManager là một API hệ thống và chỉ có thể được gọi bởi các ứng dụng có đặc quyền của hệ thống.

Ứng dụng của nhà cung cấp dịch vụ, LPA và API Euicc

Hình 2. Cả ứng dụng của nhà mạng và LPA đều sử dụng API Euicc

API vận hành hồ sơ thông qua EuiccCardManager yêu cầu người gọi phải là LPA. Điều này được thực thi bởi khung Android. Điều này có nghĩa là phương thức gọi phải mở rộng EuiccService và được khai báo trong tệp kê khai của bạn, như được mô tả trong các phần trước.

Tương tự như EuiccManager , để sử dụng API EuiccCardManager , LPA của bạn trước tiên phải lấy phiên bản EuiccCardManager thông qua Context#getSystemService :

EuiccCardManager cardMgr = (EuiccCardManager) context.getSystemService(Context.EUICC_CARD_SERVICE);

Sau đó, để có được tất cả hồ sơ trên eUICC:

ResultCallback<EuiccProfileInfo[]> callback =
       new ResultCallback<EuiccProfileInfo[]>() {
           @Override
           public void onComplete(int resultCode,
                   EuiccProfileInfo[] result) {
               if (resultCode == EuiccCardManagerReflector.RESULT_OK) {
                   // handle result
               } else {
                   // handle error
               }
           }
       };

cardMgr.requestAllProfiles(eid, AsyncTask.THREAD_POOL_EXECUTOR, callback);

Trong nội bộ, EuiccCardManager liên kết với EuiccCardController (chạy trong quy trình điện thoại) thông qua giao diện AIDL và mỗi phương thức EuiccCardManager nhận được lệnh gọi lại từ quy trình điện thoại thông qua giao diện AIDL chuyên dụng khác. Khi sử dụng API EuiccCardManager , phương thức gọi (LPA) phải cung cấp đối tượng Executor để gọi lại lệnh gọi lại. Đối tượng Executor này có thể chạy trên một luồng đơn hoặc trên một nhóm luồng mà bạn chọn.

Hầu hết các API EuiccCardManager đều có cách sử dụng giống nhau. Ví dụ: để tải gói hồ sơ bị ràng buộc lên eUICC:

...
cardMgr.loadBoundProfilePackage(eid, boundProfilePackage,
        AsyncTask.THREAD_POOL_EXECUTOR, callback);

Để chuyển sang một hồ sơ khác với ICCID nhất định:

...
cardMgr.switchToProfile(eid, iccid, true /* refresh */,
        AsyncTask.THREAD_POOL_EXECUTOR, callback);

Để lấy địa chỉ SM-DP+ mặc định từ chip eUICC:

...
cardMgr.requestDefaultSmdpAddress(eid, AsyncTask.THREAD_POOL_EXECUTOR,
        callback);

Để truy xuất danh sách thông báo về các sự kiện thông báo đã cho:

...
cardMgr.listNotifications(eid,
        EuiccNotification.Event.INSTALL
              | EuiccNotification.Event.DELETE /* events */,
        AsyncTask.THREAD_POOL_EXECUTOR, callback);

Kích hoạt hồ sơ eSIM thông qua ứng dụng của nhà mạng

Trên các thiết bị chạy Android 9 trở lên, bạn có thể sử dụng ứng dụng của nhà mạng để kích hoạt eSIM và tải xuống hồ sơ. Ứng dụng của nhà cung cấp dịch vụ có thể tải xuống hồ sơ bằng cách gọi trực tiếp downloadSubscription hoặc bằng cách cung cấp mã kích hoạt cho LPA.

Khi ứng dụng của nhà cung cấp dịch vụ tải xuống hồ sơ bằng cách gọi downloadSubscription , lệnh gọi sẽ buộc ứng dụng đó có thể quản lý hồ sơ thông qua thẻ siêu dữ liệu BF76 mã hóa các quy tắc đặc quyền của nhà cung cấp dịch vụ cho hồ sơ. Nếu cấu hình không có thẻ BF76 hoặc nếu thẻ BF76 của nó không khớp với chữ ký của ứng dụng của nhà cung cấp dịch vụ gọi điện thì quá trình tải xuống sẽ bị từ chối.

Phần bên dưới mô tả việc kích hoạt eSIM thông qua ứng dụng của nhà mạng bằng mã kích hoạt.

Kích hoạt eSIM bằng mã kích hoạt

Khi sử dụng mã kích hoạt để kích hoạt cấu hình eSIM, LPA sẽ tìm nạp mã kích hoạt từ ứng dụng của nhà mạng và tải cấu hình xuống. Luồng này có thể được bắt đầu bởi LPA và LPA có thể kiểm soát toàn bộ luồng giao diện người dùng, nghĩa là không có giao diện người dùng ứng dụng của nhà cung cấp dịch vụ nào được hiển thị. Phương pháp này bỏ qua quá trình kiểm tra thẻ BF76 và nhà khai thác mạng không cần triển khai toàn bộ luồng giao diện người dùng kích hoạt eSIM, bao gồm cả việc tải xuống hồ sơ eSIM và xử lý lỗi.

Xác định dịch vụ cung cấp eUICC của nhà cung cấp dịch vụ

Ứng dụng LPA và nhà cung cấp dịch vụ giao tiếp thông qua hai giao diện AIDL : ICarrierEuiccProvisioningServiceIGetActivationCodeCallback . Ứng dụng của nhà cung cấp dịch vụ phải triển khai giao diện ICarrierEuiccProvisioningService và hiển thị giao diện đó trong phần khai báo tệp kê khai . LPA phải liên kết với ICarrierEuiccProvisioningService và triển khai IGetActivationCodeCallback . Để biết thêm thông tin về cách triển khai và hiển thị giao diện AIDL, hãy xem Xác định và giao diện AIDL .

Để xác định giao diện AIDL, hãy tạo các tệp AIDL sau cho cả ứng dụng LPA và nhà cung cấp dịch vụ.

  • ICarrierEuiccProvisioningService.aidl

    package android.service.euicc;
    
    import android.service.euicc.IGetActivationCodeCallback;
    
    oneway interface ICarrierEuiccProvisioningService {
        // The method to get the activation code from the carrier app. The caller needs to pass in
        // the implementation of IGetActivationCodeCallback as the parameter.
        void getActivationCode(in IGetActivationCodeCallback callback);
    
        // The method to get the activation code from the carrier app. The caller needs to pass in
        // the activation code string as the first parameter and the implementation of
        // IGetActivationCodeCallback as the second parameter. This method provides the carrier
        // app the device EID which allows a carrier to pre-bind a profile to the device's EID before
        // the download process begins.
        void getActivationCodeForEid(in String eid, in IGetActivationCodeCallback callback);
    }
    
    
  • IGetActivationCodeCallback.aidl

    package android.service.euicc;
    
    oneway interface IGetActivationCodeCallback {
        // The call back method needs to be called when the carrier app gets the activation
        // code successfully. The caller needs to pass in the activation code string as the
        // parameter.
        void onSuccess(String activationCode);
    
        // The call back method needs to be called when the carrier app failed to get the
        // activation code.
        void onFailure();
    }
    

Ví dụ triển khai LPA

Để liên kết với việc triển khai ICarrierEuiccProvisioningService của ứng dụng nhà cung cấp dịch vụ, LPA phải sao chép cả ICarrierEuiccProvisioningService.aidlIGetActivationCodeCallback.aidl vào dự án của bạn và triển khai ServiceConnection .

@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
    mCarrierProvisioningService = ICarrierEuiccProvisioningService.Stub.asInterface(iBinder);
}

Sau khi liên kết với việc triển khai ICarrierEuiccProvisioningService của ứng dụng của nhà cung cấp dịch vụ, LPA gọi getActivationCode hoặc getActivationCodeForEid để lấy mã kích hoạt từ ứng dụng của nhà cung cấp dịch vụ bằng cách chuyển việc triển khai lớp sơ khai IGetActivationCodeCallback .

Sự khác biệt giữa getActivationCodegetActivationCodeForEidgetActivationCodeForEid cho phép nhà cung cấp dịch vụ liên kết trước cấu hình với EID của thiết bị trước khi quá trình tải xuống bắt đầu.

void getActivationCodeFromCarrierApp() {
    IGetActivationCodeCallback.Stub callback =
            new IGetActivationCodeCallback.Stub() {
                @Override
                public void onSuccess(String activationCode) throws RemoteException {
                    // Handle the case LPA success to get activation code from a carrier app.
                }

                @Override
                public void onFailure() throws RemoteException {
                    // Handle the case LPA failed to get activation code from a carrier app.
                }
            };
    
    try {
        mCarrierProvisioningService.getActivationCode(callback);
    } catch (RemoteException e) {
        // Handle Remote Exception
    }
}

Ví dụ triển khai cho ứng dụng của nhà cung cấp dịch vụ

Để LPA liên kết với ứng dụng của nhà cung cấp dịch vụ, ứng dụng của nhà cung cấp dịch vụ phải sao chép cả ICarrierEuiccProvisioningService.aidlIGetActivationCodeCallback.aidl vào dự án của bạn và khai báo dịch vụ ICarrierEuiccProvisioningService trong tệp AndroidManifest.xml . Dịch vụ này phải yêu cầu quyền hệ thống android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS để đảm bảo rằng chỉ LPA, một ứng dụng có đặc quyền của hệ thống, mới có thể liên kết với nó. Dịch vụ này cũng phải bao gồm bộ lọc ý định với hành động android.service.euicc.action.BIND_CARRIER_PROVISIONING_SERVICE .

  • AndroidManifest.xml

    <application>
      ...
      <service
          android:name=".CarrierEuiccProvisioningService"
          android:exported="true"
          android:permission="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS">
        <intent-filter>
          <action android:name="android.service.euicc.action.BIND_CARRIER_PROVISIONING_SERVICE"/>
        </intent-filter>
      </service>
      ...
    </application>
    

Để triển khai dịch vụ ứng dụng của nhà cung cấp dịch vụ AIDL, hãy tạo một dịch vụ, mở rộng lớp Stub và triển khai các phương thức getActivationCodegetActivationCodeForEid . LPA sau đó có thể gọi một trong hai phương thức để lấy mã kích hoạt hồ sơ. Ứng dụng của nhà cung cấp dịch vụ sẽ phản hồi bằng cách gọi IGetActivationCodeCallback#onSuccess kèm theo mã kích hoạt nếu mã được tìm nạp thành công từ máy chủ của nhà cung cấp dịch vụ. Nếu không thành công, ứng dụng của nhà cung cấp dịch vụ sẽ phản hồi bằng IGetActivationCodeCallback#onFailure .

  • CarrierEuiccProvisioningService.java

    import android.service.euicc.ICarrierEuiccProvisioningService;
    import android.service.euicc.ICarrierEuiccProvisioningService.Stub;
    import android.service.euicc.IGetActivationCodeCallback;
    
    public class CarrierEuiccProvisioningService extends Service {
        private final ICarrierEuiccProvisioningService.Stub binder =
            new Stub() {
              @Override
              public void getActivationCode(IGetActivationCodeCallback callback) throws RemoteException {
                String activationCode = // do whatever work necessary to get an activation code (HTTP requests to carrier server, fetch from storage, etc.)
                callback.onSuccess(activationCode);
              }
    
              @Override
              public void getActivationCodeForEid(String eid, IGetActivationCodeCallback callback) throws RemoteException {
                String activationCode = // do whatever work necessary (HTTP requests, fetch from storage, etc.)
                callback.onSuccess(activationCode);
              }
          }
    }
    

Bắt đầu giao diện người dùng ứng dụng của nhà cung cấp dịch vụ trong quy trình kích hoạt LPA

Trên các thiết bị chạy Android 11 trở lên, LPA có thể khởi động giao diện người dùng của ứng dụng của nhà mạng. Điều này hữu ích vì ứng dụng của nhà cung cấp dịch vụ có thể yêu cầu thông tin bổ sung từ người dùng trước khi cung cấp mã kích hoạt cho LPA. Ví dụ: nhà cung cấp dịch vụ có thể yêu cầu người dùng đăng nhập để kích hoạt số điện thoại của họ hoặc thực hiện các dịch vụ chuyển mạng khác.

Đây là quá trình bắt đầu giao diện người dùng của ứng dụng của nhà cung cấp dịch vụ trong LPA:

  1. LPA khởi chạy quy trình kích hoạt ứng dụng của nhà cung cấp dịch vụ bằng cách gửi ý định android.service.euicc.action.START_CARRIER_ACTIVATION tới gói ứng dụng của nhà cung cấp dịch vụ có chứa hành động. (Bộ thu ứng dụng của nhà cung cấp dịch vụ phải được bảo vệ trong phần khai báo tệp kê khai bằng android:permission="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" để tránh nhận ý định từ các ứng dụng không phải LPA.)

    String packageName = // The carrier app's package name
    
    Intent carrierAppIntent =
        new Intent(“android.service.euicc.action.START_CARRIER_ACTIVATION”)
            .setPackage(packageName);
    
    ResolveInfo activity =
        context.getPackageManager().resolveActivity(carrierAppIntent, 0);
    
    carrierAppIntent
        .setClassName(activity.activityInfo.packageName, activity.activityInfo.name);
    
    startActivityForResult(carrierAppIntent, requestCode);
    
  2. Ứng dụng của nhà cung cấp dịch vụ thực hiện công việc của mình bằng giao diện người dùng riêng. Ví dụ: đăng nhập người dùng hoặc gửi yêu cầu HTTP đến chương trình phụ trợ của nhà cung cấp dịch vụ.

  3. Ứng dụng của nhà cung cấp dịch vụ phản hồi LPA bằng cách gọi setResult(int, Intent)finish() .

    1. Nếu ứng dụng của nhà cung cấp dịch vụ phản hồi bằng RESULT_OK thì LPA sẽ tiếp tục quy trình kích hoạt. Nếu ứng dụng của nhà cung cấp dịch vụ xác định rằng người dùng nên quét mã QR thay vì để LPA liên kết dịch vụ của ứng dụng của nhà cung cấp dịch vụ thì ứng dụng của nhà cung cấp dịch vụ sẽ phản hồi LPA bằng cách sử dụng setResult(int, Intent) với RESULT_OK và một phiên bản Intent chứa android.telephony.euicc.extra.USE_QR_SCANNER được đặt thành true . Sau đó, LPA sẽ kiểm tra phần bổ sung và khởi chạy máy quét QR thay vì ràng buộc việc triển khai ICarrierEuiccProvisioningService của ứng dụng của nhà cung cấp dịch vụ.
    2. Nếu ứng dụng của nhà mạng gặp sự cố hoặc phản hồi bằng RESULT_CANCELED (đây là mã phản hồi mặc định), LPA sẽ hủy luồng kích hoạt eSIM.
    3. Nếu ứng dụng của nhà cung cấp dịch vụ phản hồi bằng nội dung nào đó không phải là RESULT_OK hoặc RESULT_CANCELED thì LPA sẽ coi đó là một lỗi.

    Vì lý do bảo mật, LPA không được trực tiếp chấp nhận mã kích hoạt được cung cấp với mục đích kết quả để đảm bảo rằng những người gọi không thuộc LPA không thể nhận được mã kích hoạt từ ứng dụng của nhà cung cấp dịch vụ.

Khởi chạy quy trình kích hoạt LPA trong ứng dụng của nhà cung cấp dịch vụ

Bắt đầu từ Android 11, các ứng dụng của nhà mạng có thể sử dụng API eUICC để bắt đầu LUI nhằm kích hoạt eSIM. Phương pháp này hiển thị giao diện người dùng luồng kích hoạt eSIM của LPA để kích hoạt cấu hình eSIM. Sau đó, LPA sẽ gửi một thông báo khi quá trình kích hoạt hồ sơ eSIM kết thúc.

  1. LPA phải khai báo một hoạt động bao gồm bộ lọc ý định với hành động android.service.euicc.action.START_EUICC_ACTIVATION . Mức độ ưu tiên của bộ lọc ý định phải được đặt thành giá trị khác 0 trong trường hợp có nhiều triển khai trên thiết bị. Ví dụ:

    <application>
      ...
    <activity
        android:name=".CarrierAppInitActivity"
        android:exported="true">
    
        <intent-filter android:priority="100">
            <action android:name="android.service.euicc.action.START_EUICC_ACTIVATION" />
        </intent-filter>
    </activity>
      ...
    </application>
    
  2. Ứng dụng của nhà cung cấp dịch vụ thực hiện công việc của mình bằng giao diện người dùng riêng. Ví dụ: đăng nhập người dùng hoặc gửi yêu cầu HTTP đến chương trình phụ trợ của nhà cung cấp dịch vụ.

  3. Tại thời điểm này, ứng dụng của nhà cung cấp dịch vụ phải sẵn sàng cung cấp mã kích hoạt thông qua việc triển khai ICarrierEuiccProvisioningService . Ứng dụng của nhà cung cấp dịch vụ khởi chạy LPA bằng cách gọi startActivityForResult(Intent, int) bằng hành động android.telephony.euicc.action.START_EUICC_ACTIVATION . LPA cũng kiểm tra phần bổ sung boolean android.telephony.euicc.extra.USE_QR_SCANNER . Nếu giá trị là true , LPA sẽ khởi chạy trình quét QR để cho phép người dùng quét mã QR hồ sơ.

  4. Về phía LPA, LPA liên kết với việc triển khai ICarrierEuiccProvisioningService của ứng dụng nhà mạng để lấy mã kích hoạt và tải xuống hồ sơ tương ứng. LPA hiển thị tất cả các thành phần UI cần thiết trong quá trình tải xuống, chẳng hạn như màn hình tải.

  5. Khi luồng kích hoạt LPA hoàn tất, LPA sẽ phản hồi ứng dụng của nhà cung cấp dịch vụ bằng mã kết quả mà ứng dụng của nhà cung cấp xử lý trong onActivityResult(int, int, Intent) .

    1. Nếu LPA tải xuống hồ sơ eSIM mới thành công, nó sẽ phản hồi bằng RESULT_OK .
    2. Nếu người dùng hủy kích hoạt hồ sơ eSIM trong LPA, nó sẽ phản hồi bằng RESULT_CANCELED .
    3. Nếu LPA phản hồi bằng nội dung nào đó không phải là RESULT_OK hoặc RESULT_CANCELED thì ứng dụng của nhà cung cấp dịch vụ sẽ coi đây là một lỗi.

    Vì lý do bảo mật, LPA không chấp nhận mã kích hoạt trực tiếp với mục đích được cung cấp để đảm bảo rằng những người gọi không thuộc LPA không thể nhận được mã kích hoạt từ ứng dụng của nhà cung cấp dịch vụ.

Hỗ trợ nhiều eSIM

Đối với các thiết bị chạy Android 10 trở lên, lớp EuiccManager hỗ trợ các thiết bị có nhiều eSIM. Các thiết bị có một eSIM đang nâng cấp lên Android 10 không yêu cầu bất kỳ sửa đổi nào đối với việc triển khai LPA vì nền tảng này sẽ tự động liên kết phiên bản EuiccManager với eUICC mặc định. eUICC mặc định được xác định bởi nền tảng dành cho các thiết bị có phiên bản HAL vô tuyến 1.2 trở lên và bởi LPA dành cho các thiết bị có phiên bản HAL vô tuyến thấp hơn 1.2.

Yêu cầu

Để hỗ trợ nhiều eSIM, thiết bị phải có nhiều eUICC, có thể là eUICC tích hợp hoặc khe cắm SIM vật lý để có thể lắp eUICC có thể tháo rời.

Cần có Radio HAL phiên bản 1.2 trở lên để hỗ trợ nhiều eSIM. Nên sử dụng Radio HAL phiên bản 1.4 và RadioConfig HAL phiên bản 1.2.

Thực hiện

Để hỗ trợ nhiều eSIM (bao gồm eUICC có thể tháo rời hoặc SIM có thể lập trình), LPA phải triển khai EuiccService , nhận ID vị trí tương ứng với ID thẻ do người gọi cung cấp.

Tài nguyên non_removable_euicc_slots được chỉ định trong arrays.xml là một mảng số nguyên biểu thị ID vị trí của eUICC tích hợp của thiết bị. Bạn phải chỉ định tài nguyên này để cho phép nền tảng xác định xem eUICC được chèn có thể tháo rời được hay không.

Ứng dụng của nhà mạng dành cho thiết bị có nhiều eSIM

Khi tạo ứng dụng của nhà cung cấp dịch vụ cho một thiết bị có nhiều eSIM, hãy sử dụng phương thức createForCardId trong EuiccManager để tạo đối tượng EuiccManager được ghim vào một ID thẻ nhất định. ID thẻ là một giá trị số nguyên xác định duy nhất UICC hoặc eUICC trên thiết bị.

Để lấy ID thẻ cho eUICC mặc định của thiết bị, hãy sử dụng phương thức getCardIdForDefaultEuicc trong TelephonyManager . Phương thức này trả về UNSUPPORTED_CARD_ID nếu phiên bản HAL radio thấp hơn 1.2 và trả về UNINITIALIZED_CARD_ID nếu thiết bị chưa đọc eUICC.

Bạn cũng có thể lấy ID thẻ từ getUiccCardsInfogetUiccSlotsInfo (API hệ thống) trong TelephonyManagergetCardId trong SubscriptionInfo .

Khi một đối tượng EuiccManager được khởi tạo bằng một ID thẻ cụ thể, tất cả các hoạt động sẽ được chuyển hướng đến eUICC với ID thẻ đó. Nếu eUICC không thể truy cập được (ví dụ: khi nó bị tắt hoặc bị xóa) EuiccManager không còn hoạt động nữa.

Bạn có thể sử dụng các mẫu mã sau để tạo ứng dụng của nhà cung cấp dịch vụ.

Ví dụ 1: Nhận đăng ký đang hoạt động và khởi tạo EuiccManager

// Get the active subscription and instantiate an EuiccManager for the eUICC which holds
// that subscription
SubscriptionManager subMan = (SubscriptionManager)
        mContext.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
int cardId = subMan.getActiveSubscriptionInfo().getCardId();
EuiccManager euiccMan = (EuiccManager) mContext.getSystemService(Context.EUICC_SERVICE)
            .createForCardId(cardId);

Ví dụ 2: Lặp lại qua UICC và khởi tạo EuiccManager cho eUICC có thể tháo rời

// On a device with a built-in eUICC and a removable eUICC, iterate through the UICC cards
// to instantiate an EuiccManager associated with a removable eUICC
TelephonyManager telMan = (TelephonyManager)
        mContext.getSystemService(Context.TELEPHONY_SERVICE);
List<UiccCardInfo> infos = telMan.getUiccCardsInfo();
int removableCardId = -1; // valid cardIds are 0 or greater
for (UiccCardInfo info : infos) {
    if (info.isRemovable()) {
        removableCardId = info.getCardId();
        break;
    }
}
if (removableCardId != -1) {
    EuiccManager euiccMan = (EuiccManager) mContext.getSystemService(Context.EUICC_SERVICE)
            .createForCardId(removableCardId);
}

Thẩm định

AOSP không đi kèm với việc triển khai LPA và bạn không cần phải có LPA trên tất cả các phiên bản Android (không phải mọi điện thoại đều hỗ trợ eSIM). Vì lý do này, không có trường hợp thử nghiệm CTS đầu cuối nào. Tuy nhiên, các trường hợp thử nghiệm cơ bản có sẵn trong AOSP để đảm bảo API eUICC được hiển thị hợp lệ trong các bản dựng Android.

Bạn nên đảm bảo rằng các bản dựng vượt qua các trường hợp kiểm tra CTS sau (đối với API công khai): /platform/cts/tests/tests/telephony/current/src/android/telephony/euicc/cts .

Các nhà cung cấp dịch vụ triển khai ứng dụng của nhà cung cấp dịch vụ phải thực hiện các chu trình đảm bảo chất lượng nội bộ thông thường của mình để đảm bảo tất cả các tính năng được triển khai đều hoạt động như mong đợi. Ở mức tối thiểu, ứng dụng của nhà cung cấp dịch vụ phải có khả năng liệt kê tất cả hồ sơ đăng ký do cùng một nhà điều hành sở hữu, tải xuống và cài đặt hồ sơ, kích hoạt dịch vụ trên hồ sơ, chuyển đổi giữa các hồ sơ và xóa hồ sơ.

Nếu bạn đang tạo LPA của riêng mình, bạn nên trải qua quá trình kiểm tra nghiêm ngặt hơn nhiều. Bạn nên làm việc với nhà cung cấp modem, chip eUICC hoặc nhà cung cấp hệ điều hành eSIM, nhà cung cấp SM-DP+ và nhà cung cấp dịch vụ để giải quyết các vấn đề và đảm bảo khả năng tương tác của LPA trong kiến ​​trúc RSP. Một lượng lớn thử nghiệm thủ công là không thể tránh khỏi. Để có phạm vi kiểm tra tốt nhất, bạn nên tuân theo Kế hoạch kiểm tra GSMA SGP.23 RSP .