טכנולוגיית eSIM (או eUICC) מאפשרת למשתמשים בניידים להוריד פרופיל ספק ולהפעיל את השירות של הספק בלי כרטיס SIM פיזי. זוהי מפרט גלובלי שמנוהל על ידי GSMA ומאפשר להקצות מרחוק כרטיס SIM (RSP) לכל מכשיר נייד. החל מ-Android 9, Android framework מספק ממשקי API סטנדרטיים לגישה ל-eSIM ולניהול פרופילי מינויים ב-eSIM. ממשקי ה-API של eUICC מאפשרים לצדדים שלישיים לפתח אפליקציות ספקי סלולר ועוזרי פרופילים מקומיים (LPA) משלהם במכשירי Android שתומכים ב-eSIM.
LPA היא אפליקציית מערכת עצמאית שצריכה להיכלל בקובץ האימג' של ה-build של Android. בדרך כלל, ה-LPA מנהל את הפרופילים ב-eSIM, כי הוא משמש כגשר בין SM-DP+ (שירות מרוחק שמכין, מאחסן ומעביר חבילות פרופילים למכשירים) לבין צ'יפ ה-eUICC. חבילת ה-APK של LPA יכולה לכלול רכיב של ממשק משתמש, שנקרא LPA UI או LUI, כדי לספק מקום מרכזי שבו משתמש הקצה יוכל לנהל את כל הפרופילים המוטמעים של המינויים. מסגרת Android מאתרת באופן אוטומטי את ה-LPA הזמין הטוב ביותר ומתחבר אליו, ומנתבת את כל הפעולות של eUICC דרך מכונה של LPA.
איור 1. ארכיטקטורת RSP פשוטה
למפעילי רשתות סלולר שרוצים ליצור אפליקציית ספק מומלץ לבדוק את ממשקי ה-API ב-EuiccManager
, שמספקים פעולות ברמה גבוהה לניהול פרופילים, כמו downloadSubscription()
, switchToSubscription()
ו-deleteSubscription()
.
יצרני ציוד מקורי (OEM) של מכשירים שרוצים ליצור אפליקציית מערכת LPA משלהם צריכים להרחיב את EuiccService
כדי ש-Android Framework יוכל להתחבר לשירותי ה-LPA שלהם. בנוסף, מומלץ להשתמש בממשקי ה-API שב-EuiccCardManager
, שמספקים פונקציות ES10x על סמך GSMA RSP v2.0.
הפונקציות האלה משמשות לשליחת פקודות לצ'יפ eUICC, למשל prepareDownload()
, loadBoundProfilePackage()
, retrieveNotificationList()
ו-resetMemory()
.
כדי שממשקי ה-API ב-EuiccManager
יפעלו, נדרשת אפליקציית LPA שמופעלת בצורה תקינה. בנוסף, מבצע הקריאה לממשקי ה-API של EuiccCardManager
חייב להיות LPA. המערכת של Android אוכפת את הכלל הזה.
מכשירי Android מגרסה 10 ואילך יכולים לתמוך במכשירים עם כמה כרטיסי eSIM. למידע נוסף, ראו תמיכה במספר כרטיסי eSIM.
יצירת אפליקציה של ספק הסלולר
ממשקי ה-API של eUICC ב-Android 9 מאפשרים למפעילי רשתות סלולריות ליצור אפליקציות ממותגות של ספקי הסלולר כדי לנהל את הפרופילים שלהם ישירות. הפעולות האלה כוללות הורדה ומחיקה של פרופילי מינויים שבבעלות הספק, וגם מעבר לפרופיל שבבעלות הספק.
EuiccManager
EuiccManager
היא נקודת הכניסה הראשית לאפליקציות שמאפשרת להן לקיים אינטראקציה עם ה-LPA. ההרשאה הזו כוללת אפליקציות של ספקים שמורידות, מוחקות ומעבירות מינויים שבבעלות הספק. זה כולל גם את אפליקציית מערכת LUI, שמספקת מיקום מרכזי או ממשק משתמש מרכזי לניהול כל המינויים המוטמעים, ויכולה להיות אפליקציה נפרדת מזו שמספקת את EuiccService
.
כדי להשתמש בממשקי ה-API הציבוריים, אפליקציה של ספק שירותי הסלולר צריכה קודם לקבל את המופע של EuiccManager
דרך Context#getSystemService
:
EuiccManager mgr = (EuiccManager) context.getSystemService(Context.EUICC_SERVICE);
לפני שמבצעים פעולות שקשורות ל-eSIM, צריך לבדוק אם המכשיר תומך ב-eSIM. בדרך כלל, הפונקציה EuiccManager#isEnabled()
מחזירה true
אם התכונה android.hardware.telephony.euicc
מוגדרת וקיימת חבילת LPA.
if (mgr == null || !mgr.isEnabled()) {
return;
}
כדי לקבל מידע על חומרת eUICC ועל גרסת מערכת ההפעלה של eSIM:
EuiccInfo info = mgr.getEuiccInfo();
String osVer = info.getOsVersion();
הרבה ממשקי API, כמו downloadSubscription()
ו-switchToSubscription()
, משתמשים בקריאות חוזרות (callbacks) של PendingIntent
כי השלמת הפעולה עשויה להימשך שניות או אפילו דקות.
הבקשה PendingIntent
נשלחת עם קוד תוצאה במרחב EuiccManager#EMBEDDED_SUBSCRIPTION_RESULT_
, שמספק קודי שגיאה שהוגדרו במסגרת, וגם קוד תוצאה מפורט שרירותי שמופץ מה-LPA בתור EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE
, ומאפשר לאפליקציית הספק לעקוב אחריו למטרות רישום ביומן או ניפוי באגים. הקריאה החוזרת (callback) של PendingIntent
חייבת להיות BroadcastReceiver
.
כדי להוריד מינוי נתון להורדה (שנוצר מקוד הפעלה או מקוד 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);
מגדירים את ההרשאה ומשתמשים בה ב-AndroidManifest.xml
:
<permission android:protectionLevel="signature" android:name="com.your.company.lpa.permission.BROADCAST" />
<uses-permission android:name="com.your.company.lpa.permission.BROADCAST"/>
כדי לעבור למינוי לפי מזהה המינוי:
// 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);
רשימה מלאה של ממשקי ה-API של EuiccManager
ודוגמאות קוד זמינות במאמר ממשקי API של eUICC.
שגיאות שניתן לפתור
יש מקרים שבהם המערכת לא יכולה להשלים את פעולת ה-eSIM, אבל המשתמשים יכולים לפתור את השגיאה. לדוגמה, downloadSubscription
יכול להיכשל אם המטא-נתונים של הפרופיל מציינים שנדרש קוד אישור של ספק. לחלופין, יכול להיות שהקריאה ל-switchToSubscription
תיכשל אם לאפליקציית הספק יש הרשאות ספק בפרופיל היעד (כלומר, הפרופיל בבעלות הספק), אבל אין לה הרשאות ספק בפרופיל המופעל כרגע, ולכן נדרשת הסכמה מהמשתמש.
במקרים כאלה, תיגרם קריאה חוזרת לשולח הקריאה עם הערך EuiccManager#EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR
. קריאת החזרה (callback) Intent
מכילה נתונים פנימיים נוספים, כך שכאשר מבצע הקריאה מעביר אותה אל EuiccManager#startResolutionActivity
, אפשר לבקש פתרון דרך LUI. לדוגמה, לחיצה שוב על קוד האישור EuiccManager#startResolutionActivity
מפעילה מסך LUI שמאפשר למשתמש להזין קוד אישור. אחרי הזנת הקוד, פעולת ההורדה ממשיכה. הגישה הזו מעניקה לאפליקציית הספק שליטה מלאה על מועד הצגת ממשק המשתמש, אבל מספקת ל-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
כדי להוריד פרופילים למכשיר, הפרופיל שלכם צריך לכלול ב-metadata כללי הרשאות של ספק הסלולר שתואמים לאפליקציה של ספק הסלולר. הסיבה לכך היא שפרופילי מינויים ששייכים לספקים שונים יכולים להתקיים יחד ב-eUICC של המכשיר, וכל אפליקציה של ספק צריכה לקבל גישה רק לפרופילים שבבעלות הספק הזה. לדוגמה, לספק א' לא צריכה להיות אפשרות להוריד, להפעיל או להשבית פרופיל שבבעלות ספק ב'.
כדי לוודא שרק הבעלים של פרופיל יכול לגשת אליו, מערכת Android משתמשת במנגנון שמעניק הרשאות מיוחדות לאפליקציה של הבעלים של הפרופיל (כלומר, אפליקציית הספק). פלטפורמת Android טוענת אישורים שמאוחסנים בקובץ כללי הגישה של הפרופיל (ARF) ומעניקה הרשאה לאפליקציות שחתומות על ידי האישורים האלה לבצע קריאות לממשקי API של EuiccManager
. התהליך הכללי מתואר בהמשך:
- המפעיל חותם על ה-APK של אפליקציית הספק. הכלי APKsigner מצרף את אישור המפתח הציבורי ל-APK.
המפעיל/SM-DP+ מכין פרופיל ואת המטא-נתונים שלו, כולל ARF שמכיל:
- חתימה (SHA-1 או SHA-256) של אישור המפתח הציבורי של אפליקציית הספק (חובה)
- שם החבילה של אפליקציית הספק (מומלץ מאוד)
אפליקציית הספק מנסה לבצע פעולת eUICC באמצעות ה-API
EuiccManager
.פלטפורמת Android מאמתת שהגיבוב מסוג SHA-1 או SHA-256 של האישור של אפליקציית מבצע הקריאה החוזרת תואם לחתימה של האישור שהתקבל מ-ARF של פרופיל היעד. אם שם החבילה של אפליקציית הספק כלול ב-ARF, הוא חייב להתאים גם לשם החבילה של אפליקציית מבצע הקריאה.
אחרי האימות של החתימה ושם החבילה (אם הוא כלול), הרשאת הספק ניתנת לאפליקציה מבצעת הקריאה בפרופיל היעד.
מאחר שמטא-נתונים של פרופיל יכולים להיות זמינים מחוץ לפרופיל עצמו (כדי ש-LPA יוכל לאחזר את המטא-נתונים של הפרופיל מ-SM-DP+ לפני ההורדה של הפרופיל, או מ-ISD-R כשהפרופיל מושבת), הם צריכים לכלול את אותם כללי הרשאות של הספק כמו בפרופיל.
מערכת ההפעלה של eUICC ו-SM-DP+ חייבות לתמוך בתג קנייני BF76
במטא-נתונים של הפרופיל. תוכן התג צריך להיות זהה לכללי ההרשאות של הספק שמוחזרים על ידי אפליקציית ה-ARA (Access Rule Applet) שמוגדרת בהרשאות של ספק 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
}
}
פרטים נוספים על חתימה על אפליקציות זמינים במאמר חתימה על האפליקציה. פרטים נוספים על הרשאות של ספקי סלולר זמינים במאמר הרשאות של ספקי סלולר ב-UICC.
יצירת אפליקציה של עוזר דיגיטלי לפרופיל מקומי
יצרני המכשירים יכולים להטמיע עוזר פרופיל מקומי (LPA) משלהם, שצריך לחבר לממשקי ה-API של Android Euicc. בקטעים הבאים מופיעה סקירה כללית קצרה על יצירת אפליקציית LPA ושילובה עם מערכת Android.
דרישות לחומרה או למכשיר המודם
ה-LPA ומערכת ההפעלה של eSIM שבשבב eUICC חייבים לתמוך לפחות ב-GSMA RSP (Remote SIM Provisioning) בגרסה 2.0 או 2.2. כדאי גם להשתמש בשרתי SM-DP+ ו-SM-DS עם גרסת RSP תואמת. למידע מפורט על ארכיטקטורת RSP, ראו מפרט ארכיטקטורת RSP של GSMA SGP.21.
בנוסף, כדי לשלב עם ממשקי ה-API של eUICC ב-Android 9, מודם המכשיר צריך לשלוח את יכולות המסוף עם התמיכה בקידוד של יכולות eUICC (ניהול פרופילים מקומיים והורדת פרופילים). בנוסף, צריך להטמיע את השיטות הבאות:
- IRadio HAL v1.1:
setSimPower
IRadio HAL v1.2:
getIccCardStatus
IRadioConfig HAL v1.0:
getSimSlotsStatus
IRadioConfig AIDL v1.0:
getAllowedCarriers
ה-LPA של Google צריך לדעת את סטטוס הנעילה של הספק כדי לאפשר הורדה או העברה של eSIM רק לספק המורשה. אחרת, המשתמשים עלולים להוריד ולהעביר כרטיס SIM, ולאחר מכן להבין שהמכשיר נעול לספק אחר.
ספקים או יצרני ציוד מקורי חייבים להטמיע את ממשק ה-API של IRadioSim.getAllowedCarriers()HAL.
RIL או המודם של הספק יאכלסו את סטטוס הנעילה ואת carrierId של הספק שאליו המכשיר נעול, כחלק מ-HAL API של IRadioSimResponse.getAllowedCarriersResponse().
המודם אמור לזהות את ה-eSIM כשפרופיל ההפעלה שמוגדר כברירת מחדל מופעל ככרטיס SIM תקין, ולהמשיך להפעיל את כרטיס ה-SIM.
במכשירים עם Android 10, צריך להגדיר מערך מזהה משבצת eUICC שלא ניתן להסרה. לדוגמה, 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 או ממשק המשתמש של ה-LUI.
כדי להטמיע את הקצה העורפי של ה-LPA, צריך להרחיב את EuiccService
ולהצהיר על השירות הזה בקובץ המניפסט. השירות חייב לדרוש הרשאת מערכת android.permission.BIND_EUICC_SERVICE
כדי להבטיח שרק המערכת תוכל להיות מקושרת אליו. השירות חייב לכלול גם מסנן Intent עם הפעולה android.service.euicc.EuiccService
. צריך להגדיר את העדיפות של מסנן Intent לערך שאינו אפס במקרה שיש במכשיר כמה הטמעות. לדוגמה:
<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 הפעיל ומבצעת איתו אינטראקציה לפי הצורך כדי לתמוך בממשקי ה-API של eUICC ב-Android. PackageManager
נשלחת שאילתה לכל האפליקציות עם ההרשאה android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS
, שמציינת שירות לפעולה 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
. לכל אחד מהם צריך להיות מסנן Intent עם הפעולה המתאימה, הקטגוריה 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>
המשמעות היא שממשק המשתמש שמטמיע את המסכים האלה יכול להגיע מ-APK שונה מה-APK שמטמיע את EuiccService
.
הבחירה אם להשתמש ב-APK אחד או בכמה חבילות APK (לדוגמה, אחת שמטמיעה את EuiccService
ואחת שמספקת פעילויות LUI) היא בחירה של תכנון.
EuiccCardManager
EuiccCardManager
הוא הממשק לתקשורת עם צ'יפ ה-eSIM. הוא מספק פונקציות ES10 (כפי שמתואר במפרט GSMA RSP) ומטפל בפקודות הבקשה/התשובה ברמת APDU הנמוכה, וגם בניתוח ASN.1.
EuiccCardManager
הוא ממשק API של מערכת, ורק אפליקציות עם הרשאות מערכת יכולות להפעיל אותו.
איור 2. גם אפליקציית הספק וגם ספק ה-LPA משתמשים בממשקי API של Euicc API
כדי להשתמש בממשקי ה-API של פעולות הפרופיל דרך EuiccCardManager
, מבצע הקריאה צריך להיות חשבון LPA. המערכת של Android אוכפת את הכלל הזה. המשמעות היא שהמבצע צריך להרחיב את EuiccService
ולהצהיר עליו בקובץ המניפסט, כפי שמתואר בקטעים הקודמים.
בדומה ל-EuiccManager
, כדי להשתמש בממשקי ה-API של EuiccCardManager
, ה-LPA צריך קודם לקבל את המכונה של EuiccCardManager
דרך Context#getSystemService
:
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
מקשר אל EuiccCardController
(שפועל בתהליך הטלפון) באמצעות ממשק AIDL, וכל method של EuiccCardManager
מקבלת את הקריאה החוזרת שלה מתהליך הטלפון דרך ממשק AIDL ייעודי אחר. כשמשתמשים בממשקי API של EuiccCardManager
, מבצע הקריאה (LPA) צריך לספק אובייקט Executor
דרכו מתבצעת הקריאה החוזרת. אובייקט Executor
יכול לפעול בשרשור יחיד או במאגר שרשור לבחירתכם.
לרוב ממשקי ה-API של EuiccCardManager
יש את אותו דפוס שימוש. לדוגמה, כדי לטעון לחבילת פרופיל מקושרת ל-eUICC:
...
cardMgr.loadBoundProfilePackage(eid, boundProfilePackage,
AsyncTask.THREAD_POOL_EXECUTOR, callback);
כדי לעבור לפרופיל אחר עם ICCID נתון:
...
cardMgr.switchToProfile(eid, iccid, true /* refresh */,
AsyncTask.THREAD_POOL_EXECUTOR, callback);
כדי לקבל את כתובת ברירת המחדל של SM-DP+ משבב ה-eUICC:
...
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, וה-LPA יכול לשלוט בכל תהליך ממשק המשתמש, כלומר לא יוצג ממשק משתמש של אפליקציית ספק. הגישה הזו עוקפת את בדיקת התג BF76
, ומפעילי הרשת לא צריכים להטמיע את כל התהליך של ממשק המשתמש של הפעלת ה-eSIM, כולל הורדה של פרופיל eSIM וטיפול בשגיאות.
הגדרת שירות הקצאת eUICC של הספק
האפליקציה של LPA ואפליקציית הספק מתקשרות באמצעות שני ממשקי AIDL: ICarrierEuiccProvisioningService
ו-IGetActivationCodeCallback
. אפליקציית הספק צריכה להטמיע ממשק ICarrierEuiccProvisioningService
ולהציג אותו בהצהרת המניפסט שלה.
ה-LPA חייב לקשר ל-ICarrierEuiccProvisioningService
ולהטמיע את IGetActivationCodeCallback
. למידע נוסף על הטמעה וחשיפת ממשק AIDL, ראו הגדרה של ממשק AIDL.
כדי להגדיר את ממשקי ה-AIDL, יוצרים את קובצי ה-AIDL הבאים גם לאפליקציית ה-LPA וגם לאפליקציות של הספק.
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
כדי לקבל את קוד ההפעלה מאפליקציית הספק, על ידי העברת ההטמעה של סוג ה-stub 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
לפרויקט, ולהצהיר על שירות ICarrierEuiccProvisioningService
בקובץ AndroidManifest.xml
. השירות חייב לדרוש את הרשאת המערכת android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS
כדי לוודא שרק ה-LPA, אפליקציה עם הרשאות מערכת, יכולה לקשר אליו. השירות צריך לכלול גם מסנן Intent עם הפעולה 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
במכשירים עם Android מגרסה 11 ואילך, ה-LPA יכול להפעיל ממשק משתמש של אפליקציית ספק. האפשרות הזו שימושית כי יכול להיות שאפליקציית הספק תצטרך לקבל מהמשתמש מידע נוסף לפני שהיא תספק קוד הפעלה ל-LPA. לדוגמה, יכול להיות שספקים דורשים מהמשתמשים להתחבר כדי להפעיל את מספרי הטלפון שלהם או לבצע שירותי ניוד אחרים.
זה התהליך להפעלת ממשק משתמש של אפליקציית ספק ב-LPA:
ה-LPA מפעיל את תהליך ההפעלה של אפליקציית הספק על ידי שליחת ה-Intent
android.service.euicc.action.START_CARRIER_ACTIVATION
לחבילת אפליקציית הספק שמכילה את הפעולה. (יש להגן על הנמען של אפליקציית הספק בהצהרת המניפסט באמצעותandroid:permission="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"
כדי למנוע קבלת כוונות (intents) מאפליקציות שאינן 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);
אפליקציית הספק מבצעת את הפעולות שלה באמצעות ממשק משתמש משלה. לדוגמה, רישום המשתמש ביומן או שליחת בקשות HTTP לקצה העורפי של הספק.
אפליקציית הספק מגיבה ל-LPA באמצעות קריאה ל-
setResult(int, Intent)
ול-finish()
.- אם אפליקציית הספק משיבה עם
RESULT_OK
, ה-LPA ממשיך בתהליך ההפעלה. אם אפליקציית הספק קובעת שהמשתמש צריך לסרוק קוד QR במקום לאפשר ל-LPA לקשר את השירות של אפליקציית הספק, אפליקציית הספק משיבה ל-LPA באמצעותsetResult(int, Intent)
עםRESULT_OK
ומופע שלIntent
שמכיל את הפרמטר הנוסף הבוליאניandroid.telephony.euicc.extra.USE_QR_SCANNER
שמוגדר כ-true
. לאחר מכן, ה-LPA בודק את התוסף ומפעיל את סורק ה-QR במקום לקשר את ההטמעה שלICarrierEuiccProvisioningService
באפליקציית הספק. - אם אפליקציית הספק קורסת או מגיבה עם
RESULT_CANCELED
(זהו קוד התגובה שמוגדר כברירת מחדל), ה-LPA מבטל את תהליך ההפעלה של ה-eSIM. - אם אפליקציית הספק מגיבה במשהו שאינו
RESULT_OK
אוRESULT_CANCELED
, ה-LPA מתייחס לכך כשגיאה.
מטעמי אבטחה, לא כדאי ש-LPA יקבל באופן ישיר קוד הפעלה שסופק בכוונה של התוצאה, כדי להבטיח שקריאות שלא מבוססות על LPA לא יוכלו לקבל קוד הפעלה מאפליקציית הספק.
- אם אפליקציית הספק משיבה עם
הפעלת תהליך ההפעלה של LPA באפליקציה של ספק
החל מ-Android 11, אפליקציות של ספקי סלולר יכולות להשתמש בממשקי API של eUICC כדי להפעיל ממשק LUI להפעלת eSIM. בשיטה הזו מוצג ממשק המשתמש של תהליך ההפעלה של ה-eSIM של LPA כדי להפעיל את פרופיל ה-eSIM. לאחר מכן, ה-LPA שולח שידור כשהפעלת פרופיל ה-eSIM מסתיימת.
ה-LPA חייב להצהיר על פעילות שכוללת מסנן Intent עם הפעולה
android.service.euicc.action.START_EUICC_ACTIVATION
. צריך להגדיר את העדיפות של מסנן Intent לערך שאינו אפס במקרה שיש כמה הטמעות של המכשיר. לדוגמה:<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>
אפליקציית הספק פועלת באמצעות ממשק משתמש משלה. לדוגמה, רישום המשתמש ביומן או שליחת בקשות HTTP לקצה העורפי של הספק.
בשלב הזה, אפליקציית הספק אמורה להיות מוכנה לספק קוד הפעלה באמצעות ההטמעה של
ICarrierEuiccProvisioningService
. אפליקציית הספק מפעילה את ה-LPA באמצעות קריאה ל-startActivityForResult(Intent, int)
עם הפעולהandroid.telephony.euicc.action.START_EUICC_ACTIVATION
. ה-LPA בודק גם את המאפיין הנוסף הבוליאניandroid.telephony.euicc.extra.USE_QR_SCANNER
. אם הערך הואtrue
, ה-LPA מפעיל את סורק ה-QR כדי לאפשר למשתמש לסרוק את קוד ה-QR של הפרופיל.בצד ה-LPA, ה-LPA מתחבר להטמעה של
ICarrierEuiccProvisioningService
באפליקציית הספק כדי לאחזר את קוד ההפעלה ולהוריד את הפרופיל התואם. ב-LPA מוצגים כל רכיבי ממשק המשתמש הנדרשים במהלך ההורדה, כמו מסך טעינה.כשתהליך ההפעלה של LPA מסתיים, ה-LPA משיב לאפליקציית הספק עם קוד תוצאה, שאפליקציית הספק מטפלת בו ב-
onActivityResult(int, int, Intent)
.- אם ה-LPA מצליח להוריד את פרופיל ה-eSIM החדש, הוא מגיב עם
RESULT_OK
. - אם המשתמש מבטל את הפעלת פרופיל ה-eSIM ב-LPA, הוא מקבל בתגובה את הערך
RESULT_CANCELED
. - אם ה-LPA משיב משהו שאינו
RESULT_OK
אוRESULT_CANCELED
, אפליקציית הספק מתייחסת לכך כאל שגיאה.
מטעמי אבטחה, ה-LPA לא מקבל קוד הפעלה ישירות בכוונה שסופקה, כדי לוודא שגורמים שאינם LPA לא יכולים לקבל את קוד ההפעלה מאפליקציית הספק.
- אם ה-LPA מצליח להוריד את פרופיל ה-eSIM החדש, הוא מגיב עם
תמיכה בכמה כרטיסי eSIM
במכשירים עם Android מגרסה 10 ואילך, הכיתה EuiccManager
תומכת במכשירים עם כמה כרטיסי eSIM. במכשירים עם eSIM יחיד שמשודרגים ל-Android 10, אין צורך לבצע שינויים בהטמעת LPA כי הפלטפורמה משייכת באופן אוטומטי את המכונה EuiccManager
ל-eUICC שמוגדר כברירת מחדל. ברירת המחדל של eUICC נקבעת על ידי הפלטפורמה במכשירים עם גרסה 1.2 ואילך של HAL רדיו, ועל ידי ה-LPA במכשירים עם גרסאות HAL רדיו נמוכות מ-1.2.
הדרישות
כדי לתמוך בכמה כרטיסי eSIM, צריך להיות במכשיר יותר מ-eUICC אחד. ה-eUICC יכול להיות מובנה או חריץ SIM פיזי שבו אפשר להכניס כרטיסי eUICC נשלפים.
כדי לתמוך בכמה כרטיסי eSIM נדרשת רדיו HAL מגרסה 1.2 ואילך. מומלץ להשתמש ב-Radio HAL בגרסה 1.4 וב-RadioConfig HAL בגרסה 1.2.
הטמעה
כדי לתמוך בכמה כרטיסי eSIM (כולל כרטיסי eUICC נשלפים או כרטיסי SIM שניתנים לתכנות), צריך להטמיע ב-LPA את הקוד EuiccService
, שמקבל את מזהה החריץ שתואם למזהה הכרטיס שסופק.
המשאב non_removable_euicc_slots
שצוין ב-arrays.xml
הוא מערך של מספרים שלמים שמייצגים את מזהי החריצים של כרטיסי eUICC המובנים במכשיר. צריך לציין את המשאב הזה כדי לאפשר לפלטפורמה לקבוע אם eUICC מוכנס ניתן להסרה או לא.
אפליקציית ספק לטלפון עם כמה כרטיסי eSIM
כשיוצרים אפליקציית ספק למכשיר עם כמה כרטיסי eSIM, צריך להשתמש ב-method createForCardId
ב-EuiccManager
כדי ליצור אובייקט EuiccManager
שמוצמד למזהה הכרטיס הנתון. מזהה הכרטיס הוא ערך מסוג מספר שלם שמזהה באופן ייחודי ממשק משתמש (UICC) או eUICC במכשיר.
כדי לקבל את מזהה הכרטיס של eUICC שמוגדר כברירת מחדל במכשיר, משתמשים בשיטה getCardIdForDefaultEuicc
ב-TelephonyManager
. השיטה הזו מחזירה את הערך UNSUPPORTED_CARD_ID
אם גרסת ה-HAL של הרדיו נמוכה מ-1.2, ואת הערך UNINITIALIZED_CARD_ID
אם המכשיר לא קרא את ה-eUICC.
אפשר לקבל מזהי כרטיסים גם מ-getUiccCardsInfo
ומ-getUiccSlotsInfo
(ממשק API של מערכת) ב-TelephonyManager
, ומ-getCardId
ב-SubscriptionInfo
.
כשיוצרים אובייקט EuiccManager
עם מזהה כרטיס ספציפי, כל הפעולות מופנות ל-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: איטרציה על UICCs ויצירת מופע של EuiccManager
עבור eUICC נשלף
// 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, ולא צפוי להיות LPA זמין בכל גרסאות ה-build של Android (לא כל טלפון תומך ב-eSIM). לכן, אין תרחישי בדיקה מקצה לקצה של CTS. עם זאת, יש תרחישי בדיקה בסיסיים ב-AOSP כדי לוודא שממשקי ה-API של eUICC שנחשפו תקינים בגרסאות build של Android.
צריך לוודא שהגרסאות הבנויות עוברות את תרחישי הבדיקה הבאים של CTS (לממשקי API ציבוריים): /platform/cts/tests/tests/telephony/current/src/android/telephony/euicc/cts.
ספקי סלולר שמטמיעים אפליקציה של ספק צריכים לעבור את מחזורי בקרת האיכות הרגילים שלהם כדי לוודא שכל התכונות שהוחלו פועלות כצפוי. לכל הפחות, אפליקציית הספק צריכה להיות מסוגלת להציג את רשימת כל הפרופילים של המינויים שבבעלות אותו מפעיל, להוריד ולהתקין פרופיל, להפעיל שירות בפרופיל, לעבור בין פרופילים ולמחוק פרופילים.
אם אתם יוצרים LPA משלכם, צריך לבצע בדיקות הרבה יותר קפדניות. עליכם לעבוד עם ספק המודם, עם שבב ה-eUICC או עם ספק ה-eSIM OS, עם ספקי ה-SM-DP+ ועם הספקים כדי לפתור בעיות ולהבטיח יכולת פעולה הדדית של ה-LPA בארכיטקטורת ה-RSP. כמות גדולה של בדיקות ידניות היא בלתי נמנעת. כדי לקבל את הכיסוי הטוב ביותר של הבדיקות, מומלץ לפעול לפי תוכנית הבדיקה של RSP ב-GSMA SGP.23.