Triển khai eSIM

Công nghệ SIM nhúng (eSIM hoặc eUICC) cho phép người dùng thiết bị di động tải hồ sơ nhà mạng xuống và kích hoạt dịch vụ của nhà mạng mà không cần thẻ SIM thực. Đây là một 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. Kể từ Android 9, khung Android sẽ cung cấp các API tiêu chuẩn để truy cập vào eSIM và quản lý hồ sơ gói thuê bao trên eSIM. Các API eUICC này cho phép 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) trên các thiết bị Android hỗ trợ eSIM.

LPA là một ứng dụng hệ thống độc lập, phải có trong hình ảnh bản dựng Android. LPA thường thực hiện việc quản lý hồ sơ trên eSIM 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 gói hồ sơ đến thiết bị) và khối eUICC. APK LPA có thể tuỳ ý bao gồm một thành phần giao diện người dùng, được gọi là giao diện người dùng LPA hoặc LUI, để cung cấp một nơi tập trung cho người dùng cuối quản lý tất cả hồ sơ thuê bao được nhúng. Khung Android sẽ tự động khám phá và kết nối với LPA tốt nhất hiện có, đồng thời định tuyến tất cả các thao tác eUICC thông qua một thực thể LPA.

Cấu trúc Cấp phép SIM từ xa đơn giản (RSP)

Hình 1. Cấu trúc RSP đơn giản hoá

Các nhà mạng di động muốn tạo ứng dụng của nhà mạng nên xem xét các API trong EuiccManager. API này cung cấp các thao tác quản lý hồ sơ cấp cao như downloadSubscription(), switchToSubscription()deleteSubscription().

Nếu là nhà sản xuất thiết bị gốc (OEM) muốn 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. Các API này cung cấp các hàm ES10x dựa trên GSMA RSP v2.0. Các hàm này dùng để đưa ra lệnh cho khối eUICC, chẳng hạn như prepareDownload(), loadBoundProfilePackage(), retrieveNotificationList()resetMemory().

Các API trong EuiccManager yêu cầu phải có một ứng dụng LPA được triển khai đúng cách để hoạt động và phương thức gọi của các API EuiccCardManager phải là một LPA. Yêu cầu này do khung Android thực thi.

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 phần Hỗ trợ nhiều eSIM.

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

API eUICC trong Android 9 cho phép các nhà mạng di động tạo ứng dụng mang thương hiệu của nhà mạng để trực tiếp quản lý hồ sơ của họ. Điều này bao gồm việc tải xuống và xoá hồ sơ gói thuê bao do nhà mạng sở hữu, cũng như chuyển sang hồ sơ do nhà mạng sở hữu.

EuiccManager

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ác ứng dụng của nhà mạng tải xuống, xoá và chuyển sang các gói thuê bao do nhà mạng sở hữu. Trong đó cũng bao gồm ứng dụng hệ thống LUI (Giao diện người dùng/vị trí trung tâm) để quản lý tất cả gói thuê bao được nhúng, đồng thời 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à mạng phải có được thực thể của EuiccManager thông qua Context#getSystemService:

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

Bạn nên kiểm tra xem thiết bị có hỗ trợ eSIM hay không trước khi thực hiện bất kỳ thao tác nào trên eSIM. 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;
}

Cách 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ì có thể mất vài giây hoặc thậm chí vài phút để hoàn tất. PendingIntent được gửi cùng với mã kết quả trong không gian EuiccManager#EMBEDDED_SUBSCRIPTION_RESULT_. Không gian này cung cấp các mã lỗi do khung xác định, cũng như một mã kết quả chi tiết tuỳ ý được truyền từ LPA dưới dạng EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, cho phép ứng dụng của nhà mạng theo dõi cho mục đích ghi nhật ký/gỡ lỗi. Lệnh gọi lại PendingIntent phải là BroadcastReceiver.

Cách tải một gói thuê bao có thể tải xuống (được tạo từ mộ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);

Định nghĩa 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"/>

Cách chuyển sang gói thuê bao được cung cấp mã nhận dạng gói thuê bao:

// 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 phần API eUICC.

Lỗi có thể khắc phục

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

Đối với các trường hợp này, lệnh gọi lại của phương thức gọi được gọi bằng EuiccManager#EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR. Lệnh gọi lại Intent chứa các dữ liệu bổ sung nội bộ mà khi phương thức gọi truyền nó đến EuiccManager#startResolutionActivity, quá trình phân giải có thể được yêu cầu thông qua LUI. Sử dụng lại mã xác nhận, ví dụ: EuiccManager#startResolutionActivity sẽ 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ẽ tiếp tục. Phương pháp này cung cấp cho ứng dụng của nhà mạng toàn quyền kiểm soát thời điểm hiển thị giao diện người dùng, nhưng cung cấp cho LPA/LUI một phương thức 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à mạng

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

Để đảm bảo chỉ chủ sở hữu mới có thể truy cập vào hồ sơ, Android sử dụng một cơ chế để cấp đặc quyền cho ứng dụng của chủ sở hữu hồ sơ (tức là ứng dụng của nhà mạng). 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 hồ sơ và cấp quyền cho các ứng dụng được ký bằng các chứng chỉ này để thực hiện lệnh gọi đến API EuiccManager. Dưới đây là quy trình cấp cao:

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

    1. Chữ ký (SHA-1 hoặc SHA-256) trong chứng chỉ khoá công khai của ứng dụng của nhà mạng (bắt buộc)
    2. Tên gói của ứng dụng của nhà mạng (nên dùng)
  3. Ứng dụng của nhà mạng cố gắng thực hiện thao tác eUICC bằng 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ỉ ứng dụng gọi khớp với chữ ký của chứng chỉ lấy được từ ARF của hồ sơ mục tiêu. Nếu tên gói của ứng dụng của nhà mạng có trong ARF, thì tên gói đó cũng phải khớp với tên gói của ứng dụng 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à mạng sẽ được cấp cho ứng dụng gọi qua hồ sơ mục tiêu.

Vì siêu dữ liệu hồ sơ có thể có sẵn bên ngoài chính hồ sơ (để LPA có thể truy xuất siêu dữ liệu hồ sơ từ SM-DP+ trước khi tải hồ sơ xuống hoặc từ ISD-R khi hồ sơ bị tắt), nên siêu dữ liệu hồ sơ phải chứa các quy tắc đặc quyền của nhà mạng giống 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 là các quy tắc đặc quyền của nhà mạng giống như nội dung do tệp đính kèm quy tắc truy cập (ARA) trả về được xác định trong Quyền đặc biệt của nhà mạng 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 thông tin chi tiết về việc ký ứng dụng, hãy xem bài viết Ký ứng dụng. Để biết thông tin chi tiết về đặc quyền của nhà mạng, hãy xem bài viết Đặc quyền của nhà mạng đối với UICC.

Tạo ứng dụng trợ lý hồ sơ cục bộ

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ọ, và 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 ứng dụng đó với hệ thống Android.

Yêu cầu về phần cứng/mô-đun

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

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

  • IRadio HAL v1.1: setSimPower
  • IRadio HAL phiên bản 1.2: getIccCardStatus

  • IRadioConfig HAL v1.0: getSimSlotsStatus

  • IRadioConfig AIDL v1.0: getAllowedCarriers

    LPA của Google cần biết trạng thái khoá của nhà mạng để chỉ có thể cho phép tải hoặc chuyển eSIM xuống đối với nhà mạng được cho phép. Nếu không, người dùng có thể tải xuống và chuyển SIM, sau đó mới nhận ra thiết bị bị nhà mạng khoá cho một nhà mạng 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 khoá và carrierId của nhà mạng mà thiết bị được khoá vào API IRadioSimResponse.getAllowedCarriersResponse()HAL.

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

Đối với các thiết bị chạy Android 10, bạn phải xác định một mảng mã nhận dạng khe eUICC không thể tháo rời. Ví dụ: hãy 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>

Để xem danh sách đầy đủ các yêu cầu đối với modem, hãy xem bài viết Yêu cầu đối với modem để hỗ trợ eSIM.

EuiccService

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 tệp APK): phần phụ trợ LPA và giao diện người dùng LPA 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. 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 dịch vụ đó. Dịch vụ cũng phải có một bộ lọc ý định với hành động android.service.euicc.EuiccService. Bạn nên đặt mức độ ưu tiên của bộ lọc ý định thành một giá trị khác 0 trong trường hợp có nhiều phương thức 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 LPA đó nếu cần để hỗ trợ các API eUICC của Android. PackageManager được truy vấn cho tất cả ứng dụng có quyền android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS, chỉ định một dịch vụ cho thao tác 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, tính năng hỗ trợ LPA sẽ bị tắt.

Để triển khai LUI, bạn phải cung cấp một hoạt động cho các thao tác 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 mụ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 dùng để chọn phương thức triển khai cho các hoạt động này giống như việc chọn phương thức 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 có nghĩa là 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 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ế.

EuiccCardManager

EuiccCardManager là giao diện để giao tiếp với khối eSIM. Thư viện này cung cấp các hàm ES10 (như mô tả trong thông số kỹ thuật 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 quyền của hệ thống.

Ứng dụng của nhà mạng, LPA và API Euicc

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

Các API thao tác trên hồ sơ thông qua EuiccCardManager yêu cầu phương thức gọi phải là LPA. Khung Android thực thi điều này. Đ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, như mô tả trong các phần trước.

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

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

Sau đó, để lấy 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 sẽ nhận lệnh gọi lại từ quy trình điện thoại thông qua một 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 mà lệnh gọi lại được gọi thông qua đó. Đối tượng Executor này có thể chạy trên một luồng 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ùng một kiểu sử dụng. Ví dụ: để tải một gói hồ sơ liên kết lên eUICC:

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

Cách chuyển sang một hồ sơ khác có ICCID nhất định:

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

Cách lấy địa chỉ SM-DP+ mặc định từ khối eUICC:

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

Cách truy xuất danh sách thông báo của 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 hồ sơ xuống. Ứng dụng của nhà mạng có thể tải hồ sơ xuống bằng cách gọi trực tiếp downloadSubscription hoặc cung cấp mã kích hoạt cho LPA.

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

Phần dưới đây mô tả việc kích hoạt eSIM thông qua một ứ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 hồ sơ eSIM, LPA sẽ tìm nạp mã kích hoạt từ ứng dụng của nhà mạng và tải hồ sơ xuống. Quy trình này có thể do LPA khởi tạo và LPA có thể kiểm soát toàn bộ quy trình 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à mạng nào xuất hiện. Phương pháp này bỏ qua bước kiểm tra thẻ BF76 và nhà mạng không cần triển khai toàn bộ quy trình giao diện người dùng kích hoạt eSIM, bao gồm cả việc tải hồ sơ eSIM xuống và xử lý lỗi.

Xác định dịch vụ cấp phép eUICC của nhà mạng

LPA và ứng dụng của nhà mạng giao tiếp thông qua hai giao diện AIDL: ICarrierEuiccProvisioningServiceIGetActivationCodeCallback. Ứng dụng của nhà mạng 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 phần 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 đây cho cả ứng dụng LPA và ứng dụng của nhà mạng.

  • 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ụ về cách triển khai LPA

Để liên kết với hoạt động triển khai ICarrierEuiccProvisioningService của ứng dụng của nhà mạng, 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 hoạt động triển khai ICarrierEuiccProvisioningService của ứng dụng của nhà mạng, LPA sẽ gọi getActivationCode hoặc getActivationCodeForEid để lấy mã kích hoạt từ ứng dụng của nhà mạng bằng cách truyền hoạt động triển khai lớp mô phỏng IGetActivationCodeCallback.

Sự khác biệt giữa getActivationCodegetActivationCodeForEidgetActivationCodeForEid cho phép nhà mạng liên kết trước một hồ sơ 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ụ về cách triển khai ứng dụng của nhà mạng

Để LPA liên kết với ứng dụng của nhà mạng, ứng dụng của nhà mạng 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ụ 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 quyền của hệ thống) mới có thể liên kết với dịch vụ đó. Dịch vụ cũng phải có một bộ lọc ý định với thao tác 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à mạng AIDL, hãy tạo một dịch vụ, mở rộng lớp Stub rồi triển khai các phương thức getActivationCodegetActivationCodeForEid. Sau đó, LPA có thể gọi một trong hai phương thức để tìm nạp mã kích hoạt hồ sơ. Ứng dụng của nhà mạng sẽ phản hồi bằng cách gọi IGetActivationCodeCallback#onSuccess bằng mã kích hoạt nếu mã này được tìm nạp thành công từ máy chủ của nhà mạng. Nếu không thành công, ứng dụng của nhà mạng 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);
              }
          }
    }
    

Khởi động giao diện người dùng của ứng dụng của nhà mạng 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 rất hữu ích vì ứng dụng của nhà mạng có thể yêu cầu người dùng cung cấp thêm thông tin trước khi cung cấp mã kích hoạt cho LPA. Ví dụ: nhà mạng có thể yêu cầu người dùng đăng nhập để kích hoạt số điện thoại hoặc thực hiện các dịch vụ chuyển số khác.

Đây là quy trình khởi động giao diện người dùng của ứng dụng của nhà mạng trong LPA:

  1. LPA khởi chạy quy trình kích hoạt của ứng dụng của nhà mạng bằng cách gửi ý định android.service.euicc.action.START_CARRIER_ACTIVATION đến gói ứng dụng của nhà mạng chứa hành động đó. (Bạn phải bảo vệ trình nhận ứng dụng của nhà mạng 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à mạng thực hiện công việc bằng giao diện người dùng của riêng ứng dụng. Ví dụ: đăng nhập người dùng hoặc gửi yêu cầu HTTP đến phần phụ trợ của nhà mạng.

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

    1. Nếu ứng dụng của nhà mạng 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à mạng 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à mạng, thì ứng dụng của nhà mạng sẽ phản hồi LPA bằng cách sử dụng setResult(int, Intent) với RESULT_OK và một thực thể Intent chứa android.telephony.euicc.extra.USE_QR_SCANNER boolean bổ sung được đặt thành true. Sau đó, LPA sẽ kiểm tra thông tin bổ sung và chạy trình quét QR thay vì liên kết quy trình triển khai ICarrierEuiccProvisioningService của ứng dụng nhà mạng.
    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), thì LPA sẽ huỷ quy trình kích hoạt eSIM.
    3. Nếu ứng dụng của nhà mạng phản hồi bằng một giá trị khác với RESULT_OK hoặc RESULT_CANCELED, thì LPA sẽ coi đó là lỗi.

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

Chạy quy trình kích hoạt LPA trong ứng dụng của nhà mạng

Kể từ Android 11, các ứng dụng của nhà mạng có thể sử dụng API eUICC để khởi động LUI nhằm kích hoạt eSIM. Phương thức này hiển thị giao diện người dùng quy trình kích hoạt eSIM của LPA để kích hoạt hồ sơ eSIM. Sau đó, LPA sẽ gửi thông báo truyền tin khi quá trình kích hoạt hồ sơ eSIM hoàn tất.

  1. LPA phải khai báo một hoạt động bao gồm bộ lọc ý định với thao tác android.service.euicc.action.START_EUICC_ACTIVATION. Bạn nên đặt mức độ ưu tiên của bộ lọc ý định thành một giá trị khác 0 trong trường hợp có nhiều phương thức 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à mạng thực hiện công việc bằng giao diện người dùng của riêng ứng dụng. Ví dụ: đăng nhập người dùng hoặc gửi yêu cầu HTTP đến phần phụ trợ của nhà mạng.

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

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

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

    1. Nếu tải hồ sơ eSIM mới xuống thành công, LPA sẽ phản hồi bằng RESULT_OK.
    2. Nếu người dùng huỷ kích hoạt hồ sơ eSIM trong LPA, thì LPA sẽ phản hồi bằng RESULT_CANCELED.
    3. Nếu LPA phản hồi bằng một giá trị khác với RESULT_OK hoặc RESULT_CANCELED, thì ứng dụng của nhà mạng sẽ coi đây là 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 trong ý định đã cung cấp để đảm bảo rằng các phương thức gọi không phải LPA không thể lấy mã kích hoạt từ ứng dụng của nhà mạng.

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 sửa đổi nào đối với việc triển khai LPA vì nền tảng sẽ tự động liên kết thực thể EuiccManager với eUICC mặc định. eUICC mặc định do nền tảng xác định cho các thiết bị có phiên bản HAL vô tuyến 1.2 trở lên và do LPA xác đị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. Đây có thể là eUICC tích hợp sẵn hoặc khe cắm SIM thực tế để có thể chèn eUICC có thể tháo rời.

Bạn cần có Radio HAL phiên bản 1.2 trở lên để hỗ trợ nhiều eSIM. Bạn nên dùng Radio HAL phiên bản 1.4 và RadioConfig HAL phiên bản 1.2.

Triển khai

Để hỗ trợ nhiều eSIM (bao gồm cả eUICC có thể tháo rời hoặc SIM có thể lập trình), LPA phải triển khai EuiccService. Lớp này sẽ nhận mã khe tương ứng với mã thẻ do phương thức 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 đại diện cho mã khe của eUICC tích hợp sẵn trên 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 đã chèn có thể tháo rời hay không.

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

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

Để lấy mã 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 trên đài phát thanh 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 mã thẻ từ getUiccCardsInfogetUiccSlotsInfo (API hệ thống) trong TelephonyManagergetCardId trong SubscriptionInfo.

Khi một đối tượng EuiccManager được tạo bản sao bằng một mã thẻ cụ thể, tất cả thao tác sẽ được chuyển đến eUICC bằng mã thẻ đó. Nếu không thể truy cập vào eUICC (chẳng hạn như khi bị tắt hoặc xoá), thì EuiccManager sẽ không hoạt động nữa.

Bạn có thể dùng các mã mẫu sau để tạo một ứng dụng của nhà mạng.

Ví dụ 1: Lấy gói thuê bao đang hoạt động và tạo bản sao 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 thông qua UICC và tạo thực thể EuiccManager cho một eUICC có thể xoá

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

Xác nhận kết quả

AOSP không đi kèm với hoạt động triển khai LPA và dự kiến sẽ không có LPA trên mọi bản dựng 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 kiểm thử CTS toàn diện. Tuy nhiên, các trường hợp kiểm thử cơ bản có sẵn trong AOSP để đảm bảo các API eUICC được hiển thị đều hợp lệ trong các bản dựng Android.

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

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

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