有了嵌入式 SIM 卡 (eSIM 卡或 eUICC) 技術,行動裝置使用者不必使用實體 SIM 卡,就能下載電信業者設定檔並啟用電信業者服務。這是由 GSMA 推動的全球規格,可對任何行動裝置進行遠端 SIM 卡供應 (RSP)。自 Android 9 起,Android 架構提供標準 API,可存取 eSIM 卡並管理 eSIM 卡上的訂閱設定檔。第三方可透過這些 eUICC API,在支援 eSIM 的 Android 裝置上開發自己的電信業者應用程式和本機設定檔助理 (LPA)。
LPA 是獨立的系統應用程式,應包含在 Android 建構映像檔中。eSIM 上的設定檔通常由 LPA 管理,因為 LPA 是 SM-DP+ (準備、儲存及將設定檔套件傳送至裝置的遠端服務) 和 eUICC 晶片之間的橋樑。LPA APK 可選擇性地納入 UI 元件 (稱為 LPA UI 或 LUI),為使用者提供集中管理所有內嵌訂閱設定檔的位置。Android 架構會自動探索並連線至最佳 LPA,並透過 LPA 執行個體傳送所有 eUICC 作業。
圖 1. 簡化 RSP 架構
有興趣建立電信業者應用程式的行動網路業者,應查看 EuiccManager
中的 API,這些 API 提供高階設定檔管理作業,例如 downloadSubscription()
、switchToSubscription()
和 deleteSubscription()
。
如果您是裝置 OEM,有意建立自己的 LPA 系統應用程式,就必須擴充 EuiccService
,才能讓 Android 架構連線至您的 LPA 服務。此外,您應使用 EuiccCardManager
中的 API,這些 API 提供以 GSMA RSP v2.0 為基礎的 ES10x 函式。這些函式用於向 eUICC 晶片發出指令,例如 prepareDownload()
、loadBoundProfilePackage()
、retrieveNotificationList()
和 resetMemory()
。
EuiccManager
API 必須有正確實作的 LPA 應用程式才能運作,且 EuiccCardManager
API 的呼叫端必須是 LPA。這項限制由 Android 架構強制執行。
搭載 Android 10 以上版本的裝置可支援多個 eSIM 卡。詳情請參閱「支援多個 eSIM 卡」。
製作電信業者應用程式
Android 9 中的 eUICC API 可讓行動網路業者建立自有品牌的應用程式,直接管理設定檔。包括下載及刪除電信業者擁有的訂閱設定檔,以及切換至電信業者擁有的設定檔。
EuiccManager
EuiccManager
是應用程式與 LPA 互動的主要進入點。包括下載、刪除電信業者擁有的訂閱項目,以及切換至這類訂閱項目的電信業者應用程式。這也包括 LUI 系統應用程式,該應用程式提供集中式位置/UI,用於管理所有內嵌訂閱項目,且可與提供 EuiccService
的應用程式分開。
如要使用公開 API,電信業者應用程式必須先透過 Context#getSystemService
取得 EuiccManager
的執行個體:
EuiccManager mgr = (EuiccManager) context.getSystemService(Context.EUICC_SERVICE);
執行任何 eSIM 作業前,請先確認裝置是否支援 eSIM。如果定義了 android.hardware.telephony.euicc
功能,且有 LPA 套件,EuiccManager#isEnabled()
通常會傳回 true
。
if (mgr == null || !mgr.isEnabled()) {
return;
}
如要取得 eUICC 硬體和 eSIM 作業系統版本的相關資訊,請按照下列步驟操作:
EuiccInfo info = mgr.getEuiccInfo();
String osVer = info.getOsVersion();
許多 API (例如 downloadSubscription()
和 switchToSubscription()
) 都會使用 PendingIntent
回呼,因為完成這些 API 可能需要幾秒甚至幾分鐘的時間。PendingIntent
會與 EuiccManager#EMBEDDED_SUBSCRIPTION_RESULT_
空格中的結果代碼一起傳送,提供架構定義的錯誤代碼,以及從 LPA 傳播的任意詳細結果代碼 (如 EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE
),方便電信業者應用程式追蹤,以利記錄/偵錯。PendingIntent
回呼必須為 BroadcastReceiver
。
如要下載可下載的訂閱內容 (透過啟用代碼或 QR code 建立):
// 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);
在 AndroidManifest.xml
中定義及使用權限:
<permission android:protectionLevel="signature" android:name="com.your.company.lpa.permission.BROADCAST" />
<uses-permission android:name="com.your.company.lpa.permission.BROADCAST"/>
如要使用訂閱 ID 切換訂閱方案,請按照下列步驟操作:
// 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);
如需 EuiccManager
API 和程式碼範例的完整清單,請參閱「eUICC API」。
可解決的錯誤
在某些情況下,系統無法完成 eSIM 卡作業,但使用者可以解決錯誤。舉例來說,如果設定檔中繼資料指出需要電信業者確認碼,downloadSubscription
可能會失敗。或者,如果電信業者應用程式對目的地設定檔具有電信業者權限 (即電信業者擁有該設定檔),但對目前啟用的設定檔沒有電信業者權限,因此需要使用者同意,則 switchToSubscription
可能會失敗。
在這些情況下,系統會使用 EuiccManager#EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR
呼叫呼叫者的回呼。回呼 Intent
包含內部額外資訊,因此當呼叫端將其傳遞至 EuiccManager#startResolutionActivity
時,即可透過 LUI 要求解析度。再次使用確認碼 (例如 EuiccManager#startResolutionActivity
) 會觸發 LUI 畫面,讓使用者輸入確認碼;輸入代碼後,下載作業就會繼續。這個方法可讓電信業者應用程式完全掌控 UI 的顯示時間,同時為 LPA/LUI 提供可擴充的方法,以便日後新增使用者可復原問題的處理方式,不必變更用戶端應用程式。
Android 9 在 EuiccService
中定義了這些可解決的錯誤,LUI 應處理這些錯誤:
/**
* 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";
電信業者特殊權限
如果您是電信業者,正在開發自己的電信業者應用程式,並呼叫 EuiccManager
將設定檔下載至裝置,設定檔應包含中繼資料中與電信業者應用程式對應的電信業者權限規則。這是因為屬於不同電信業者的訂閱設定檔可以共存在裝置的 eUICC 中,且每個電信業者應用程式只能存取該電信業者擁有的設定檔。舉例來說,電信業者 A 不應能夠下載、啟用或停用電信業者 B 擁有的設定檔。
為確保只有擁有者能存取設定檔,Android 會使用機制將特殊權限授予設定檔擁有者的應用程式 (即電信業者應用程式)。Android 平台會載入儲存在設定檔存取規則檔案 (ARF) 中的憑證,並授權由這些憑證簽署的應用程式呼叫 EuiccManager
API。高階程序如下:
- 電信業者會簽署電信業者應用程式 APK,而 apksigner 工具會將公開金鑰憑證附加到 APK。
電信業者/SM-DP+ 會準備設定檔及其包含 ARF 的中繼資料,其中包含:
- 電信業者應用程式公開金鑰憑證的簽章 (SHA-1 或 SHA-256) (必填)
- 電信業者應用程式的套件名稱 (強烈建議)
電信業者應用程式嘗試使用
EuiccManager
API 執行 eUICC 作業。Android 平台會驗證呼叫端應用程式憑證的 SHA-1 或 SHA-256 雜湊,是否與從目標設定檔 ARF 取得的憑證簽章相符。如果 ARF 中包含電信業者應用程式的套件名稱,則該名稱也必須與呼叫端應用程式的套件名稱相符。
驗證簽章和套件名稱 (如有) 後,系統會將目標設定檔的電信業者權限授予呼叫端應用程式。
由於設定檔中繼資料可在設定檔本身以外的位置取得 (因此 LPA 可在下載設定檔前從 SM-DP+ 擷取設定檔中繼資料,或在停用設定檔時從 ISD-R 擷取),因此應包含與設定檔中相同的電信業者權限規則。
eUICC OS 和 SM-DP+ 必須支援設定檔中繼資料中的專屬標記 BF76
。標記內容應與「UICC 貨運公司權限」中定義的存取規則小程式 (ARA) 傳回的貨運公司權限規則相同:
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
}
}
如要進一步瞭解應用程式簽署,請參閱「簽署應用程式」。如要進一步瞭解電信業者權限,請參閱「UICC 電信業者權限」。
製作當地商家檔案助理應用程式
裝置製造商可以實作自己的本機設定檔助理 (LPA),但必須與 Android Euicc API 連結。以下各節將簡要說明如何建立 LPA 應用程式,並與 Android 系統整合。
硬體/數據機需求
eUICC 晶片上的 LPA 和 eSIM OS 必須至少支援 GSMA RSP (遠端 SIM 卡供應) 2.0 版或 2.2 版。此外,您也應規劃使用 RSP 版本相符的 SM-DP+ 和 SM-DS 伺服器。如需詳細的 RSP 架構,請參閱 GSMA SGP.21 RSP 架構規格。
此外,如要整合 Android 9 中的 eUICC API,裝置數據機應傳送終端機功能,並支援編碼的 eUICC 功能 (本機設定檔管理和設定檔下載)。此外,也需要實作下列方法:
- IRadio HAL v1.1:
setSimPower
IRadio HAL v1.2:
getIccCardStatus
IRadioConfig HAL v1.0:
getSimSlotsStatus
IRadioConfig AIDL v1.0:
getAllowedCarriers
Google LPA 必須瞭解電信業者鎖定狀態,才能只允許下載或轉移允許的電信業者 eSIM 卡。否則使用者可能會下載及轉移 SIM 卡,之後才發現裝置已鎖定其他電信業者。
供應商或原始設備製造商必須實作 IRadioSim.getAllowedCarriers() HAL API。
供應商 RIL / 數據機應填入裝置鎖定的電信業者鎖定狀態和 carrierId,做為 IRadioSimResponse.getAllowedCarriersResponse() HAL API 的一部分。
數據機應會將已啟用預設啟動設定檔的 eSIM 卡視為有效 SIM 卡,並保持 SIM 卡電源開啟。
如果裝置搭載 Android 10,則必須定義不可移除的 eUICC 卡槽 ID 陣列。如需範例,請參閱 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>
如需數據機的完整需求清單,請參閱「支援 eSIM 的數據機需求」。
EuiccService
LPA 由兩個獨立元件組成 (可能都在同一個 APK 中實作):LPA 後端和 LPA UI 或 LUI。
如要實作 LPA 後端,您必須擴充 EuiccService
,並在資訊清單檔案中宣告這項服務。服務必須要求 android.permission.BIND_EUICC_SERVICE
系統權限,確保只有系統可以繫結至該服務。服務也必須包含 android.service.euicc.EuiccService
動作的意圖篩選器。如果裝置上有多個實作項目,意圖篩選器的優先順序應設為非零值。例如:
<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>
在內部,Android 架構會判斷有效的 LPA,並視需要與其互動,以支援 Android eUICC API。系統會查詢所有具有 android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS
權限的應用程式,PackageManager
指定 android.service.euicc.EuiccService
動作的服務。系統會選取優先順序最高的服務。如果找不到服務,系統會停用 LPA 支援。
如要實作 LUI,您必須提供下列動作的活動:
android.service.euicc.action.MANAGE_EMBEDDED_SUBSCRIPTIONS
android.service.euicc.action.PROVISION_EMBEDDED_SUBSCRIPTION
與服務相同,每項活動都必須要求 android.permission.BIND_EUICC_SERVICE
系統權限。每個意圖篩選器都應包含適當的動作、android.service.euicc.category.EUICC_UI
類別和非零優先順序。選取這些活動的實作方式時,所用的邏輯與選取 EuiccService
的實作方式相同。例如:
<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>
這表示實作這些畫面的 UI 可能來自與實作 EuiccService
的 APK 不同的 APK。要使用單一 APK 或多個 APK (例如實作 EuiccService
的 APK 和提供 LUI 活動的 APK),取決於設計選擇。
EuiccCardManager
EuiccCardManager
是與 eSIM 卡晶片通訊的介面。這個架構提供 ES10 函式 (如 GSMA RSP 規格所述),並處理低階 APDU 要求/回應指令和 ASN.1 剖析作業。EuiccCardManager
是系統 API,只能由具備系統權限的應用程式呼叫。
圖 2. 電信業者應用程式和 LPA 都使用 Euicc API
透過 EuiccCardManager
執行的設定檔作業 API,需要呼叫端為 LPA。這項限制由 Android 架構強制執行。也就是說,呼叫端必須擴充 EuiccService
,並在資訊清單檔案中宣告,如前幾節所述。
與 EuiccManager
類似,如要使用 EuiccCardManager
API,LPA 必須先透過 Context#getSystemService
取得 EuiccCardManager
的執行個體:
EuiccCardManager cardMgr = (EuiccCardManager) context.getSystemService(Context.EUICC_CARD_SERVICE);
接著,如要取得 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);
在內部,EuiccCardManager
會透過 AIDL 介面繫結至 EuiccCardController
(在手機程序中執行),且每個 EuiccCardManager
方法都會透過不同的專屬 AIDL 介面,從手機程序接收回呼。使用 EuiccCardManager
API 時,呼叫端 (LPA) 必須提供 Executor
物件,藉此叫用回呼。這個 Executor
物件可以在單一執行緒或您選擇的執行緒集區中執行。
大多數 EuiccCardManager
API 的使用模式都相同。舉例來說,如要在 eUICC 上載入繫結設定檔套件:
...
cardMgr.loadBoundProfilePackage(eid, boundProfilePackage,
AsyncTask.THREAD_POOL_EXECUTOR, callback);
如要使用特定 ICCID 切換至其他設定檔,請按照下列步驟操作:
...
cardMgr.switchToProfile(eid, iccid, true /* refresh */,
AsyncTask.THREAD_POOL_EXECUTOR, callback);
如何從 eUICC 晶片取得預設 SM-DP+ 位址:
...
cardMgr.requestDefaultSmdpAddress(eid, AsyncTask.THREAD_POOL_EXECUTOR,
callback);
如要擷取指定通知事件的通知清單,請執行下列操作:
...
cardMgr.listNotifications(eid,
EuiccNotification.Event.INSTALL
| EuiccNotification.Event.DELETE /* events */,
AsyncTask.THREAD_POOL_EXECUTOR, callback);
透過電信業者應用程式啟用 eSIM 設定檔
在搭載 Android 9 以上版本的裝置上,你可以使用電信業者應用程式啟用 eSIM 並下載設定檔。電信業者應用程式可以透過直接呼叫 downloadSubscription
,或向 LPA 提供啟用代碼,下載設定檔。
當電信業者應用程式呼叫 downloadSubscription
下載設定檔時,呼叫會透過編碼設定檔電信業者權限規則的 BF76
中繼資料標記,強制應用程式管理設定檔。如果設定檔沒有 BF76
標記,或 BF76
標記與撥號電信業者應用程式的簽章不符,系統就會拒絕下載。
以下說明如何使用啟用代碼,透過電信業者應用程式啟用 eSIM。
使用啟用代碼啟用 eSIM
使用啟用代碼啟用 eSIM 卡設定檔時,LPA 會從電信業者應用程式擷取啟用代碼,並下載設定檔。LPA 可以啟動這項流程,並控制整個 UI 流程,因此不會顯示任何電信業者應用程式 UI。這種做法會略過 BF76
標記檢查,網路業者也不需要實作整個 eSIM 啟用使用者介面流程,包括下載 eSIM 設定檔和處理錯誤。
定義電信業者 eUICC 佈建服務
LPA 和電信業者應用程式會透過兩個 AIDL 介面進行通訊:ICarrierEuiccProvisioningService
和 IGetActivationCodeCallback
。電信業者應用程式必須實作 ICarrierEuiccProvisioningService
介面,並在資訊清單宣告中公開該介面。LPA 必須繫結至 ICarrierEuiccProvisioningService
並實作 IGetActivationCodeCallback
。如要進一步瞭解如何實作及公開 AIDL 介面,請參閱「定義 AIDL 介面」。
如要定義 AIDL 介面,請為 LPA 和電信業者應用程式建立下列 AIDL 檔案。
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(); }
LPA 導入範例
如要繫結至電信業者應用程式的 ICarrierEuiccProvisioningService
實作項目,LPA 必須將 ICarrierEuiccProvisioningService.aidl
和 IGetActivationCodeCallback.aidl
複製到專案,並實作 ServiceConnection
。
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
mCarrierProvisioningService = ICarrierEuiccProvisioningService.Stub.asInterface(iBinder);
}
繫結至電信業者應用程式的 ICarrierEuiccProvisioningService
實作項目後,LPA 會呼叫 getActivationCode
或 getActivationCodeForEid
,方法是傳遞 IGetActivationCodeCallback
存根類別的實作項目,從電信業者應用程式取得啟用代碼。
getActivationCode
和 getActivationCodeForEid
的差異在於,getActivationCodeForEid
可讓電信業者在下載程序開始前,預先將設定檔繫結至裝置的 EID。
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
}
}
電信業者應用程式的導入範例
如要讓 LPA 繫結至電信業者應用程式,電信業者應用程式必須將 ICarrierEuiccProvisioningService.aidl
和 IGetActivationCodeCallback.aidl
複製到專案,並在 AndroidManifest.xml
檔案中宣告 ICarrierEuiccProvisioningService
服務。服務必須要求 android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS
系統權限,確保只有 LPA (具系統權限的應用程式) 能繫結至該服務。服務也必須包含 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>
如要實作 AIDL 電信業者應用程式服務,請建立服務、擴充 Stub
類別,並實作 getActivationCode
和 getActivationCodeForEid
方法。LPA 隨後可以呼叫任一方法,擷取設定檔啟用代碼。如果成功從電信業者的伺服器擷取啟用代碼,電信業者應用程式應呼叫 IGetActivationCodeCallback#onSuccess
並傳送啟用代碼做為回應。如果失敗,電信業者應用程式應以 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); } } }
在 LPA 啟用流程中啟動電信業者應用程式 UI
在搭載 Android 11 以上版本的裝置上,LPA 可以啟動電信業者應用程式的 UI。這項功能相當實用,因為電信業者應用程式可能需要使用者提供額外資訊,才能向 LPA 提供啟用碼。舉例來說,電信業者可能會要求使用者登入,才能啟用電話號碼或執行其他攜碼轉移服務。
以下是在 LPA 中啟動電信業者應用程式 UI 的程序:
LPA 會將
android.service.euicc.action.START_CARRIER_ACTIVATION
意圖傳送至包含動作的電信業者應用程式套件,藉此啟動電信業者應用程式的啟用流程。(載波應用程式接收器必須在資訊清單聲明中受到android:permission="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"
保護,以免收到來自非 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);
電信業者應用程式會使用自己的 UI 執行作業。例如登入使用者,或將 HTTP 要求傳送至電信業者的後端。
電信業者應用程式會呼叫
setResult(int, Intent)
和finish()
,以回應 LPA。- 如果電信業者應用程式以
RESULT_OK
回應,LPA 會繼續啟用流程。如果電信業者應用程式判斷使用者應掃描 QR code,而非讓 LPA 繫結電信業者應用程式的服務,電信業者應用程式會使用setResult(int, Intent)
回應 LPA,其中包含RESULT_OK
和Intent
例項,且布林值額外項目android.telephony.euicc.extra.USE_QR_SCANNER
設為true
。LPA 接著會檢查額外項目,並啟動 QR 掃描器,而不是繫結電信業者應用程式的ICarrierEuiccProvisioningService
實作項目。 - 如果電信業者應用程式當機或傳回
RESULT_CANCELED
(這是預設的回應代碼),LPA 會取消 eSIM 卡啟用流程。 - 如果電信業者應用程式傳回的內容不是
RESULT_OK
或RESULT_CANCELED
,LPA 會將其視為錯誤。
基於安全考量,LPA 不應直接接受結果 Intent 中提供的啟用代碼,確保非 LPA 呼叫端無法從電信業者應用程式取得啟用代碼。
- 如果電信業者應用程式以
在電信業者應用程式中啟動 LPA 啟用流程
自 Android 11 起,電信業者應用程式可以使用 eUICC API,啟動 eSIM 卡啟用程序的 LUI。這個方法會顯示 LPA 的 eSIM 卡啟用流程 UI,以便啟用 eSIM 卡設定檔。然後,LPA 會在 eSIM 卡設定檔啟用完成時傳送廣播。
LPA 必須宣告活動,包括含有
android.service.euicc.action.START_EUICC_ACTIVATION
動作的意圖篩選器。如果裝置上有數個實作項目,意圖篩選器的優先順序應設為非零值。例如:<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>
電信業者應用程式會使用自己的 UI 執行作業。例如登入使用者,或將 HTTP 要求傳送至電信業者的後端。
此時,電信業者應用程式必須準備好透過
ICarrierEuiccProvisioningService
實作提供啟用代碼。電信業者應用程式會呼叫startActivityForResult(Intent, int)
並使用android.telephony.euicc.action.START_EUICC_ACTIVATION
動作,啟動 LPA。LPA 也會檢查布林值額外內容android.telephony.euicc.extra.USE_QR_SCANNER
。如果值為true
,LPA 會啟動 QR 掃描器,讓使用者掃描設定檔 QR code。在 LPA 端,LPA 會繫結至電信業者應用程式的
ICarrierEuiccProvisioningService
實作項目,以擷取啟用代碼並下載對應的設定檔。LPA 會在下載期間顯示所有必要的 UI 元素,例如載入畫面。LPA 啟用流程完成後,LPA 會以結果代碼回應電信業者應用程式,電信業者應用程式則會在
onActivityResult(int, int, Intent)
中處理該代碼。- 如果 LPA 成功下載新的 eSIM 卡設定檔,就會以
RESULT_OK
回應。 - 如果使用者在 LPA 中取消啟用 eSIM 卡設定檔,LPA 會以
RESULT_CANCELED
回應。 - 如果 LPA 回應的內容不是
RESULT_OK
或RESULT_CANCELED
,電信業者應用程式會將此視為錯誤。
基於安全考量,LPA 不會直接在提供的意圖中接受啟用代碼,確保非 LPA 呼叫端無法從電信業者應用程式取得啟用代碼。
- 如果 LPA 成功下載新的 eSIM 卡設定檔,就會以
支援多張 eSIM 卡
搭載 Android 10 以上版本的裝置,EuiccManager
類別支援多個 eSIM 卡的裝置。如果裝置只有單一 eSIM 卡,升級至 Android 10 時,平台會自動將 EuiccManager
執行個體與預設 eUICC 建立關聯,因此 LPA 實作項目不需要進行任何修改。對於無線電 HAL 版本為 1.2 以上的裝置,預設 eUICC 由平台決定;對於無線電 HAL 版本低於 1.2 的裝置,預設 eUICC 由 LPA 決定。
需求條件
如要支援多張 eSIM 卡,裝置必須有多個 eUICC,可以是內建 eUICC,也可以是可插入可移除式 eUICC 的實體 SIM 卡插槽。
如要支援多張 eSIM 卡,必須使用 Radio HAL 1.2 以上版本。建議使用 Radio HAL 1.4 版和 RadioConfig HAL 1.2 版。
實作
如要支援多個 eSIM 卡 (包括可移除的 eUICC 或可程式化 SIM 卡),LPA 必須實作 EuiccService
,該函式會接收與呼叫端提供的卡片 ID 相對應的插槽 ID。
non_removable_euicc_slots
arrays.xml
中指定的資源是整數陣列,代表裝置內建 eUICC 的插槽 ID。您必須指定這項資源,平台才能判斷插入的 eUICC 是否可移除。
適用於有多張 eSIM 卡的裝置的電信業者應用程式
為有多張 eSIM 卡的裝置製作電信業者應用程式時,請使用 EuiccManager
中的 createForCardId
方法,建立固定至特定卡片 ID 的 EuiccManager
物件。卡片 ID 是指裝置上 UICC 或 eUICC 的專屬整數 ID。
如要取得裝置預設 eUICC 的卡片 ID,請使用 TelephonyManager
中的 getCardIdForDefaultEuicc
方法。如果無線電 HAL 版本低於 1.2,這個方法會傳回 UNSUPPORTED_CARD_ID
;如果裝置尚未讀取 eUICC,則會傳回 UNINITIALIZED_CARD_ID
。
您也可以從 TelephonyManager
中的 getUiccCardsInfo
和 getUiccSlotsInfo
(系統 API) 取得卡片 ID,以及從 SubscriptionInfo
中的 getCardId
取得卡片 ID。
以特定卡片 ID 例項化 EuiccManager
物件後,所有作業都會導向具有該卡片 ID 的 eUICC。如果 eUICC 無法連線 (例如關閉或移除),EuiccManager
將無法運作。
您可以使用下列程式碼範例建立貨運公司應用程式。
示例 1:取得有效訂閱項目並例項化 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);
範例 2:逐一迭代 UICC,並為可移除的 eUICC 例項化 EuiccManager
// 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);
}
驗證
AOSP 不會隨附 LPA 實作項目,且您不應期望所有 Android 版本都有 LPA (並非每支手機都支援 eSIM)。因此,沒有端對端 CTS 測試案例。不過,AOSP 提供基本測試案例,確保公開的 eUICC API 在 Android 建構版本中有效。
請務必確保建構版本通過下列 CTS 測試案例 (適用於公開 API):/platform/cts/tests/tests/telephony/current/src/android/telephony/euicc/cts。
導入電信業者應用程式時,電信業者應進行正常的內部品質保證週期,確保所有導入的功能都能正常運作。至少,電信業者應用程式應能列出同一電信業者擁有的所有訂閱設定檔、下載及安裝設定檔、啟用設定檔中的服務、切換設定檔,以及刪除設定檔。
如果您要自行製作 LPA,應進行更嚴格的測試。您應與數據機供應商、eUICC 晶片或 eSIM OS 供應商、SM-DP+ 供應商和電信業者合作解決問題,並確保 LPA 在 RSP 架構內可互通運作。手動測試在所難免。如要獲得最佳測試涵蓋範圍,請遵循 GSMA SGP.23 RSP 測試計畫。