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 vật lý. Đây là một quy cách toàn cầu do GSMA đưa ra, cho phép cung cấp SIM từ xa (RSP) cho mọi thiết bị di động. Kể từ Android 9, khung Android cung cấp các API tiêu chuẩn để truy cập vào eSIM và quản lý hồ sơ 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 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 có 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. Việc quản lý các hồ sơ trên eSIM thường do LPA thực hiện, vì LPA đó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ơ cho thiết bị) và chip eUICC. APK LPA có thể bao gồm một thành phần giao diện người dùng (không bắt buộc), đượ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ả các hồ sơ thuê bao được nhúng. Khung Android sẽ tự động phát hiện và kết nối với LPA có sẵn tốt nhất, đồng thời định tuyến tất cả các thao tác eUICC thông qua một phiên bản LPA.

Kiến trúc đơn giản hoá của tính năng Cấp SIM từ xa (RSP)

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

Các nhà khai thác 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, chẳng hạn như downloadSubscription(), switchToSubscription()deleteSubscription().

Nếu là một OEM thiết bị 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 để khung Android kết nối với các dịch vụ LPA của bạn. 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 phiên bản 2.0. Các hàm này được dùng để gửi lệnh đến chip eUICC, chẳng hạn như prepareDownload(), loadBoundProfilePackage(), retrieveNotificationList()resetMemory().

Các API trong EuiccManager yêu cầu 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 API EuiccCardManager phải là một LPA. Khung Android sẽ thực thi điều này.

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 SIM điện tử.

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

Các API eUICC trong Android 9 giúp nhà khai thác mạng di động tạo các ứng dụng mang thương 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à xoá hồ sơ 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ả những ứng dụng của nhà mạng có thể tải xuống, xoá và chuyển sang các gói thuê bao do nhà mạng sở hữu. Điều này cũng bao gồm ứng dụng hệ thống LUI, cung cấp một vị trí/giao diện người dùng tập trung để quản lý tất cả các gói thuê bao đượ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 hãng vận chuyển phải lấy được 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 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 với 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 lấy 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 các 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 kèm theo 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 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ã 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"/>

Cách chuyển sang một gói thuê bao bằng 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);

Để xem 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

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ể giải quyết lỗi này. Ví dụ: downloadSubscription có thể không thành công nếu siêu dữ liệu hồ sơ cho biết cần có mã xác nhận của 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 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 bật, do đó, cần có sự đồng ý của người dùng.

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

Android 9 xác định những lỗi có thể khắc phục này trong EuiccService mà LUI cần 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 nhà mạng 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à mạng tương ứng với ứng dụng nhà mạng của bạn trong siêu dữ liệu. Điều này là do 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ụ: hãng vận chuyển A không được phép tải xuống, bật hoặc tắt một hồ sơ thuộc quyền sở hữu của hãng vận chuyển B.

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

  1. Nhà mạng ký tệp APK ứng dụng của nhà mạng; công cụ apksigner sẽ đí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 cả một ARF chứa:

    1. Chữ ký (SHA-1 hoặc SHA-256) của chứng chỉ khoá công khai của ứng dụng nhà mạng (bắt buộc)
    2. Tên gói của ứng dụng hãng vận chuyển (rất nên dùng)
  3. Ứng dụng nhà mạng cố gắng thực hiện một 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ỉ nhận được từ ARF của hồ sơ mục tiêu. Nếu tên gói của ứng dụng 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 trên hồ sơ mục tiêu.

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ị vô hiệu hoá), 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 tuân theo các quy tắc đặc quyền của hãng vận chuyển giống như nội dung do ứng dụng nhỏ quy tắc truy cập (ARA) trả về, được xác định trong Đặc quyền của hãng vận chuyển trên 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 về tính năng ký ứng dụng, hãy xem bài viết Ký ứng dụng. Để biết thông tin về đặc quyền của nhà mạng, hãy xem bài viết Đặc quyền của nhà mạng trên UICC.

Tạo một ứ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ọ. LPA này phải được liên kết với các API Euicc của Android. Các phần sau đây trình bày 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/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) 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 tương ứng. Để biết chi tiết về cấu trúc RSP, hãy xem Quy cách cấu trúc RSP SGP.21 của GSMA.

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 có 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). Bạn cũng cần triển khai các phương thức sau:

  • IRadio HAL phiên bản 1.1: setSimPower
  • IRadio HAL phiên bản 1.2: getIccCardStatus

  • IRadioConfig HAL phiên bản 1.0: getSimSlotsStatus

  • IRadioConfig AIDL phiên bản 1.0: getAllowedCarriers

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

Modem sẽ nhận dạng eSIM có hồ sơ khởi động mặc định được bật dưới dạng một SIM hợp lệ và duy trì trạng thái 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 cắm eUICC không tháo rời được. 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>

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

EuiccService

LPA bao gồm 2 thành phần riêng biệt (cả hai thành phần này đều có thể được triển khai trong cùng một APK): phần phụ trợ LPA và giao diện người dùng LPA (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ụ này cũng phải có một bộ lọc ý định với thao tác 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 cách 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>

Về nội bộ, khung Android xác định LPA đang hoạt động và tương tác với LPA đó khi cần để hỗ trợ các 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, trong đó 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, thì 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 lớp 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 các hoạt động triển khai cho những hoạt động này như khi chọn hoạt động 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ế.

EuiccCardManager

EuiccCardManager là giao diện để giao tiếp với chip eSIM. Nó cung cấp các hàm ES10 (như mô tả trong thông số RSP của GSMA) và xử lý các lệnh yêu cầu/phản hồi APDU cấp thấp cũng như việc 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 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 Euicc API

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 sẽ 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, LPA của bạn trước tiên 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);

Về nội bộ, EuiccCardManager liên kết với EuiccCardController (chạy trong quy trình điện thoại) thông qua một giao diện AIDL và mỗi phương thức EuiccCardManager sẽ nhận được 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 một đối tượng Executor thông qua đó lệnh gọi lại được gọi. Đối tượng Executor này có thể chạy trên một luồng duy nhất 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ơ được liên kết vào eUICC:

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

Cách chuyển sang một hồ sơ khác có mã 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ừ chip eUICC:

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

Cách 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ể dùng một ứ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 downloadSubscription trực tiếp hoặc bằng cách 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 một BF76 thẻ siêu dữ liệu 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ì quá trình tải xuống sẽ bị từ chối.

Phần dưới đây mô tả cách 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 bạn 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. LPA có thể bắt đầu quy trình này và kiểm soát toàn bộ quy trình giao diện người dùng, tức là không có giao diện người dùng ứng dụng 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 kích hoạt eSIM, bao gồm cả việc tải một hồ sơ eSIM xuống và xử lý lỗi.

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

LPA và ứng dụng của nhà mạng giao tiếp thông qua 2 giao diện AIDL: ICarrierEuiccProvisioningServiceIGetActivationCodeCallback. Ứng dụng của hãng vận chuyển phải triển khai một 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ị một giao diện AIDL, hãy xem bài viết Xác định và giao diện AIDL.

Để xác định các 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 việc triển khai ICarrierEuiccProvisioningService của ứng dụng nhà mạng, LPA phải sao chép cả ICarrierEuiccProvisioningService.aidlIGetActivationCodeCallback.aidl vào dự án của bạn, đồng thời 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 chế độ triển khai ICarrierEuiccProvisioningService của ứng dụng nhà mạng, LPA sẽ gọi getActivationCode hoặc getActivationCodeForEid để lấy mã kích hoạt từ ứng dụng nhà mạng bằng cách truyền chế độ triển khai của lớp gốc IGetActivationCodeCallback.

Điểm 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 cho ứng dụng của hãng vận chuyển

Để 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, đồng thời 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ó đặc quyền 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 nhà mạng 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. 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ã đượ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 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 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 mạng khác.

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

  1. LPA sẽ chạy quy trình kích hoạt ứ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 thao tác. (Trình nhận ứng dụng của hãng vận chuyển phải được bảo vệ trong nội dung khai báo tệp kê khai bằng android:permission="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" để tránh nhận được các ý định từ các ứng dụng không phải là ứng dụng 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 hoạt động 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 phần phụ trợ của nhà mạng.

  3. Ứng dụng 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 hãng vận chuyển xác định rằng người dùng nên quét mã QR thay vì cho phép LPA liên kết dịch vụ của ứng dụng hãng vận chuyển, thì ứng dụng hãng vận chuyển sẽ phản hồi LPA bằng setResult(int, Intent) với RESULT_OK và một thực thể Intent chứa dữ liệu bổ sung boolean 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 trình quét mã QR thay vì liên kết việc 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 hãng vận chuyển phản hồi bằng một mã khác ngoài RESULT_OK hoặc RESULT_CANCELED, thì LPA sẽ coi đó là 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 trong ý định kết quả để đảm bảo rằng những đối tượng 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ể dùng API eUICC để bắt đầu LUI cho quy trình 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 một 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 có 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 cách 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 hoạt động 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 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 việc triển khai ICarrierEuiccProvisioningService. Ứng dụng của nhà mạng sẽ chạy LPA bằng cách gọi startActivityForResult(Intent, int) với thao tác android.telephony.euicc.action.START_EUICC_ACTIVATION. LPA cũng kiểm tra giá trị boolean bổ sung android.telephony.euicc.extra.USE_QR_SCANNER. Nếu giá trị là true, thì LPA sẽ khởi chạy trình quét mã 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 để 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 phần tử 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 LPA tải thành công hồ sơ eSIM mới, thì 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 mã khác ngoà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 được cung cấp để đảm bảo rằng những bên 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 và đ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 tự động liên kết phiên bản 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ó HAL vô tuyến phiên bản 1.2 trở lên và do LPA xác định cho các thiết bị có HAL vô tuyến phiên bả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 hoặc khe cắm SIM thực để lắp eUICC có thể tháo rời.

Bạn phải dùng 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. LPA này sẽ nhận được mã nhận dạng khe cắm tương ứng với mã nhận dạng 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ị mã nhận dạng khe cắm 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 được chèn có tháo rời được hay không.

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

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

Để lấy mã nhận dạng 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 của đài 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 TelephonyManager, cũng như getCardId trong SubscriptionInfo.

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

Bạn có thể sử dụng các mẫu mã sau để tạo một ứng dụng hãng vận chuyển.

Ví dụ 1: Nhận gói thuê bao đ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 thông qua các UICC và khởi tạo EuiccManager cho một 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);
}

Xác nhận kết quả

AOSP không đi kèm với một quy trình triển khai LPA và bạn không cần phải có LPA trên tất cả các bản dựng Android (không phải điện thoại nào cũng hỗ trợ eSIM). Vì lý do này, không có trường hợp kiểm thử CTS hai đầu. Tuy nhiên, các trường hợp kiểm thử cơ bản có trong AOSP để đảm bảo các API eUICC được hiển thị là 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 hãng vận chuyển triển khai ứng dụng của hãng vận chuyển 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ư dự kiến. Tối thiểu, ứng dụng của nhà mạng phải có thể liệt kê tất cả hồ sơ thuê bao thuộc cùng một nhà khai thác, tải xuống và cài đặt mộ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 tự tạo LPA, bạn nên tiến hành kiểm thử kỹ lưỡng 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. Bạn chắc chắn phải thực hiện một lượng lớn kiểm thử thủ công. Để có phạm vi kiểm thử tốt nhất, bạn nên tuân theo Kế hoạch kiểm thử RSP SGP.23 của GSMA.