הטמעת eSIM

טכנולוגיית 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.

ארכיטקטורה פשוטה של הקצאת SIM מרחוק (RSP)

איור 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. התהליך הכללי מתואר בהמשך:

  1. המפעיל חותם על ה-APK של אפליקציית הספק. הכלי APKsigner מצרף את אישור המפתח הציבורי ל-APK.
  2. המפעיל/SM-DP+ מכין פרופיל ואת המטא-נתונים שלו, כולל ARF שמכיל:

    1. חתימה (SHA-1 או SHA-256) של אישור המפתח הציבורי של אפליקציית הספק (חובה)
    2. שם החבילה של אפליקציית הספק (מומלץ מאוד)
  3. אפליקציית הספק מנסה לבצע פעולת eUICC באמצעות ה-API EuiccManager.

  4. פלטפורמת Android מאמתת שהגיבוב מסוג SHA-1 או SHA-256 של האישור של אפליקציית מבצע הקריאה החוזרת תואם לחתימה של האישור שהתקבל מ-ARF של פרופיל היעד. אם שם החבילה של אפליקציית הספק כלול ב-ARF, הוא חייב להתאים גם לשם החבילה של אפליקציית מבצע הקריאה.

  5. אחרי האימות של החתימה ושם החבילה (אם הוא כלול), הרשאת הספק ניתנת לאפליקציה מבצעת הקריאה בפרופיל היעד.

מאחר שמטא-נתונים של פרופיל יכולים להיות זמינים מחוץ לפרופיל עצמו (כדי ש-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 של מערכת, ורק אפליקציות עם הרשאות מערכת יכולות להפעיל אותו.

אפליקציות של ספקים, ממשקי API של LPA ו-Euicc 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:

  1. ה-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);
    
  2. אפליקציית הספק מבצעת את הפעולות שלה באמצעות ממשק משתמש משלה. לדוגמה, רישום המשתמש ביומן או שליחת בקשות HTTP לקצה העורפי של הספק.

  3. אפליקציית הספק מגיבה ל-LPA באמצעות קריאה ל-setResult(int, Intent) ול-finish().

    1. אם אפליקציית הספק משיבה עם RESULT_OK, ה-LPA ממשיך בתהליך ההפעלה. אם אפליקציית הספק קובעת שהמשתמש צריך לסרוק קוד QR במקום לאפשר ל-LPA לקשר את השירות של אפליקציית הספק, אפליקציית הספק משיבה ל-LPA באמצעות setResult(int, Intent) עם RESULT_OK ומופע של Intent שמכיל את הפרמטר הנוסף הבוליאני android.telephony.euicc.extra.USE_QR_SCANNER שמוגדר כ-true. לאחר מכן, ה-LPA בודק את התוסף ומפעיל את סורק ה-QR במקום לקשר את ההטמעה של ICarrierEuiccProvisioningService באפליקציית הספק.
    2. אם אפליקציית הספק קורסת או מגיבה עם RESULT_CANCELED (זהו קוד התגובה שמוגדר כברירת מחדל), ה-LPA מבטל את תהליך ההפעלה של ה-eSIM.
    3. אם אפליקציית הספק מגיבה במשהו שאינו RESULT_OK או RESULT_CANCELED, ה-LPA מתייחס לכך כשגיאה.

    מטעמי אבטחה, לא כדאי ש-LPA יקבל באופן ישיר קוד הפעלה שסופק בכוונה של התוצאה, כדי להבטיח שקריאות שלא מבוססות על LPA לא יוכלו לקבל קוד הפעלה מאפליקציית הספק.

הפעלת תהליך ההפעלה של LPA באפליקציה של ספק

החל מ-Android 11, אפליקציות של ספקי סלולר יכולות להשתמש בממשקי API של eUICC כדי להפעיל ממשק LUI להפעלת eSIM. בשיטה הזו מוצג ממשק המשתמש של תהליך ההפעלה של ה-eSIM של LPA כדי להפעיל את פרופיל ה-eSIM. לאחר מכן, ה-LPA שולח שידור כשהפעלת פרופיל ה-eSIM מסתיימת.

  1. ה-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>
    
  2. אפליקציית הספק פועלת באמצעות ממשק משתמש משלה. לדוגמה, רישום המשתמש ביומן או שליחת בקשות HTTP לקצה העורפי של הספק.

  3. בשלב הזה, אפליקציית הספק אמורה להיות מוכנה לספק קוד הפעלה באמצעות ההטמעה של ICarrierEuiccProvisioningService. אפליקציית הספק מפעילה את ה-LPA באמצעות קריאה ל-startActivityForResult(Intent, int) עם הפעולה android.telephony.euicc.action.START_EUICC_ACTIVATION. ה-LPA בודק גם את המאפיין הנוסף הבוליאני android.telephony.euicc.extra.USE_QR_SCANNER. אם הערך הוא true, ה-LPA מפעיל את סורק ה-QR כדי לאפשר למשתמש לסרוק את קוד ה-QR של הפרופיל.

  4. בצד ה-LPA, ה-LPA מתחבר להטמעה של ICarrierEuiccProvisioningService באפליקציית הספק כדי לאחזר את קוד ההפעלה ולהוריד את הפרופיל התואם. ב-LPA מוצגים כל רכיבי ממשק המשתמש הנדרשים במהלך ההורדה, כמו מסך טעינה.

  5. כשתהליך ההפעלה של LPA מסתיים, ה-LPA משיב לאפליקציית הספק עם קוד תוצאה, שאפליקציית הספק מטפלת בו ב-onActivityResult(int, int, Intent).

    1. אם ה-LPA מצליח להוריד את פרופיל ה-eSIM החדש, הוא מגיב עם RESULT_OK.
    2. אם המשתמש מבטל את הפעלת פרופיל ה-eSIM ב-LPA, הוא מקבל בתגובה את הערך RESULT_CANCELED.
    3. אם ה-LPA משיב משהו שאינו RESULT_OK או RESULT_CANCELED, אפליקציית הספק מתייחסת לכך כאל שגיאה.

    מטעמי אבטחה, ה-LPA לא מקבל קוד הפעלה ישירות בכוונה שסופקה, כדי לוודא שגורמים שאינם LPA לא יכולים לקבל את קוד ההפעלה מאפליקציית הספק.

תמיכה בכמה כרטיסי 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.