הטמעת eSIM

טכנולוגיית eSIM (או eUICC) מאפשרת למשתמשים בנייד להוריד פרופיל ספק ולהפעיל את השירות של הספק בלי כרטיס SIM פיזי. זוהי מפרט גלובלי שמנוהל על ידי GSMA ומאפשר להקצות כרטיס SIM מרחוק (RSP) לכל מכשיר נייד. החל מ-Android 9, מסגרת Android מספקת ממשקי 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 של המכשיר, וכל אפליקציה של ספק צריכה לקבל גישה רק לפרופילים שבבעלות הספק הזה. לדוגמה, ספק A לא אמור להיות מסוגל להוריד, להפעיל או להשבית פרופיל שבבעלות ספק B.

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

    • ספקים או יצרני ציוד מקורי חייבים להטמיע את ממשק ה-HAL API‏ IRadioSim.getAllowedCarriers().

    • 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. צריך להגדיר את העדיפות של מסנן הכוונה לערך שאינו אפס, למקרה שיש כמה הטמעות במכשיר. לדוגמה:

<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 של Android eUICC. מתבצעת שאילתה על 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 שונה מזה שמטמיע את EuiccService. הבחירה אם להשתמש ב-APK אחד או בכמה חבילות APK (לדוגמה, אחת שמטמיעה את EuiccService ואחת שמספקת פעילויות LUI) היא בחירה של תכנון.

EuiccCardManager

EuiccCardManager הוא הממשק לתקשורת עם שבב ה-eSIM. הוא מספק פונקציות ES10 (כפי שמתואר במפרט GSMA RSP) ומטפל בפקודות הבקשה/התשובה ברמת APDU הנמוכה, וגם בניתוח ASN.1. EuiccCardManager הוא ממשק API של מערכת, ורק אפליקציות עם הרשאות מערכת יכולות לבצע קריאה אליו.

אפליקציות ספק, LPA וממשקי API של Euicc

איור 2. גם אפליקציית הספק וגם LPA משתמשים בממשקי API של Euicc

כדי להשתמש בממשקי ה-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, וכל שיטה של EuiccCardManager מקבלת את קריאת החזרה (callback) שלה מתהליך הטלפון דרך ממשק 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 מתייחס לכך כשגיאה.

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

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

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

  1. ה-LPA חייב להצהיר על פעילות שכוללת מסנן Intent עם הפעולה 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>
    
  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, נדרשת גרסה 1.2 ואילך של Radio HAL. מומלץ להשתמש ב-Radio HAL בגרסה 1.4 וב-RadioConfig HAL בגרסה 1.2.

הטמעה

כדי לתמוך בכמה כרטיסי eSIM (כולל eUICCs נשלפים או כרטיסי 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 משלכם, כדאי לבצע בדיקה קפדנית הרבה יותר. כדי לפתור בעיות ולהבטיח את יכולת הפעולה ההדדית של ה-LPA בארכיטקטורה של ה-RSP, עליכם לעבוד עם ספק המודם, ספק הצ'יפ eUICC או ספק מערכת ההפעלה של eSIM, ספק SM-DP+ וספקי הסלולר. אי אפשר להימנע מחלק גדול של בדיקות ידניות. כדי לקבל את הכיסוי הטוב ביותר של הבדיקות, מומלץ לפעול לפי תוכנית הבדיקה של RSP ב-GSMA SGP.23.