הטמעת eSIM

טכנולוגיית SIM משובצת (eSIM, או eUICC) מאפשרת למשתמשים ניידים להוריד פרופיל ספק ולהפעיל שירות של ספק ללא כרטיס SIM פיזי. זהו מפרט עולמי המונע על ידי ה-GSMA המאפשר אספקת SIM מרחוק (RSP) של כל מכשיר נייד. החל מ-Android 9, מסגרת Android מספקת ממשקי API סטנדרטיים לגישה ל-eSIM ולניהול פרופילי מנויים ב-eSIM. ממשקי API אלה של eUICC מאפשרים לצדדים שלישיים לפתח אפליקציות ספק משלהם ועוזרי פרופיל מקומיים (LPA) במכשירי אנדרואיד התומכים ב-eSIM.

ה-LPA הוא אפליקציית מערכת עצמאית שאמורה להיכלל בתמונת ה-build של אנדרואיד. ניהול הפרופילים ב-eSIM נעשה בדרך כלל על ידי ה-LPA, שכן הוא משמש כגשר בין ה-SM-DP+ (שירות מרוחק שמכין, מאחסן ומעביר חבילות פרופיל למכשירים) לבין שבב eUICC. ה-APK של LPA יכול לכלול אופציונלי רכיב UI, הנקרא LPA UI או LUI, כדי לספק מקום מרכזי למשתמש הקצה לניהול כל פרופילי המנויים המוטבעים. מסגרת האנדרואיד מגלה ומתחברת אוטומטית ל-LPA הזמין הטוב ביותר, ומנתבת את כל פעולות ה-eUICC דרך מופע LPA.

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

איור 1. ארכיטקטורת RSP פשוטה

מפעילי רשתות סלולריות המעוניינים ליצור אפליקציית ספק צריכים להסתכל על ממשקי ה-API ב- EuiccManager , המספקים פעולות ניהול פרופיל ברמה גבוהה כגון downloadSubscription() , switchToSubscription() ו- deleteSubscription() .

אם אתה יצרן OEM של מכשיר המעוניין ליצור אפליקציית מערכת LPA משלך, עליך להרחיב EuiccService למסגרת Android כדי להתחבר לשירותי LPA שלך. בנוסף, עליך להשתמש בממשקי ה-API ב- EuiccCardManager , המספקים פונקציות ES10x המבוססות על GSMA RSP v2.0. פונקציות אלו משמשות להנפקת פקודות לשבב eUICC, כגון prepareDownload() , loadBoundProfilePackage() , retrieveNotificationList() ו- resetMemory() .

ממשקי ה-API ב- EuiccManager דורשים אפליקציית LPA המיושמת כהלכה כדי לתפקד והקורא של ממשקי ה-API EuiccCardManager חייב להיות LPA. זה נאכף על ידי מסגרת אנדרואיד.

מכשירים עם אנדרואיד 10 ומעלה יכולים לתמוך במכשירים עם מספר eSIMs. למידע נוסף, ראה תמיכה במספר eSIMs .

יצירת אפליקציית ספק

ממשקי ה-API של eUICC באנדרואיד 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() , משתמשים בהתקשרות חוזרת PendingIntent מכיוון שהשלמה עשויה להימשך שניות או אפילו דקות. PendingIntent נשלח עם קוד תוצאה במרחב EuiccManager#EMBEDDED_SUBSCRIPTION_RESULT_ , המספק קודי שגיאה המוגדרים במסגרת, כמו גם קוד תוצאה מפורט שרירותי המופץ מה-LPA כ- EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE , המאפשר לעקוב אחר יישום רישום/המטרה של הספק. ההתקשרות חזרה 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 . Intent להתקשרות חוזרת מכילה תוספות פנימיות כך שכאשר המתקשר מעביר אותה ל- EuiccManager#startResolutionActivity , ניתן לבקש פתרון דרך ה-LUI. באמצעות קוד האישור למשל שוב, EuiccManager#startResolutionActivity מפעיל מסך LUI המאפשר למשתמש להזין קוד אישור; לאחר הזנת הקוד, פעולת ההורדה מחודשת. גישה זו מספקת לאפליקציית הספק שליטה מלאה על מועד הצגת ממשק המשתמש, אך מעניקה ל-LPA/LUI שיטה הניתנת להרחבה להוספת טיפול חדש בבעיות הניתנות לשחזור על ידי משתמשים בעתיד ללא צורך בשינוי אפליקציות לקוח.

אנדרואיד 9 מגדיר את השגיאות הניתנות לפתרון ב- EuiccService , שה-LUI צריך לטפל בהן:

/**
 * Alert the user that this action will result in an active SIM being
 * deactivated. To implement the LUI triggered by the system, you need to define
 * this in AndroidManifest.xml.
 */
public static final String ACTION_RESOLVE_DEACTIVATE_SIM =
        "android.service.euicc.action.RESOLVE_DEACTIVATE_SIM";
/**
 * Alert the user about a download/switch being done for an app that doesn't
 * currently have carrier privileges.
 */
public static final String ACTION_RESOLVE_NO_PRIVILEGES =
        "android.service.euicc.action.RESOLVE_NO_PRIVILEGES";

/** Ask the user to resolve all the resolvable errors. */
public static final String ACTION_RESOLVE_RESOLVABLE_ERRORS =
        "android.service.euicc.action.RESOLVE_RESOLVABLE_ERRORS";

הרשאות ספק

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

כדי להבטיח שפרופיל נגיש רק לבעליו, אנדרואיד משתמשת במנגנון להענקת הרשאות מיוחדות לאפליקציה של בעל הפרופיל (כלומר, אפליקציית הספק). פלטפורמת אנדרואיד טוענת אישורים המאוחסנים בקובץ כללי הגישה של הפרופיל (ARF) ומעניקה הרשאה לאפליקציות החתומות על ידי אישורים אלו לבצע קריאות לממשקי ה-API EuiccManager . התהליך ברמה גבוהה מתואר להלן:

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

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

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

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

מכיוון שמטא נתונים של פרופיל יכולים להיות זמינים מחוץ לפרופיל עצמו (כדי ש-LPA יוכל לאחזר את המטא נתונים של הפרופיל מ-SM-DP+ לפני הורדת הפרופיל, או מ-ISD-R כאשר הפרופיל מושבת), הם צריכים להכיל את אותם כללי הרשאות ספק כמו בפרופיל.

מערכת ההפעלה eUICC ו-SM-DP+ חייבים לתמוך בתג קנייני BF76 במטא נתונים של הפרופיל. תוכן התג צריך להיות אותם כללי הרשאות ספק כפי שהוחזרו על ידי יישומון כלל הגישה (ARA) המוגדר ב- UICC Carrier Privileges :

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 ושילובה עם מערכת אנדרואיד.

דרישות חומרה/מודם

ה-LPA ומערכת ההפעלה eSIM בשבב eUICC חייבים לתמוך לפחות ב-GSMA RSP (Remote SIM Provisioning) v2.0 או v2.2. כדאי גם לתכנן להשתמש בשרתי SM-DP+ ו-SM-DS בעלי גרסת RSP תואמת. לארכיטקטורת RSP מפורטת, ראה מפרט ארכיטקטורת RSP GSMA SGP.21 .

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

    • ספקים או יצרני OEM חייבים ליישם את IRadioSim.getAllowedCarriers()HAL API.

    • הספק RIL / Modem יאכלס את סטטוס הנעילה ואת ה-carrierId של הספק אליו נעול המכשיר כחלק מ-IRadioSimResponse.getAllowedCarriersResponse()HAL API.

המודם צריך לזהות את ה-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 הקצה האחורי וממשק המשתמש או ה-LUI של LPA.

כדי ליישם את ה-LPA backend, עליך להרחיב EuiccService ולהכריז על שירות זה בקובץ המניפסט שלך. השירות חייב לדרוש את הרשאת המערכת android.permission.BIND_EUICC_SERVICE כדי להבטיח שרק המערכת יכולה להתחבר אליו. השירות חייב לכלול גם מסנן כוונות עם הפעולה android.service.euicc.EuiccService . יש להגדיר את העדיפות של מסנן הכוונות לערך שאינו אפס במקרה שקיימים יישומים מרובים במכשיר. לדוגמה:

<service
    android:name=".EuiccServiceImpl"
    android:permission="android.permission.BIND_EUICC_SERVICE">
    <intent-filter android:priority="100">
        <action android:name="android.service.euicc.EuiccService" />
    </intent-filter>
</service>

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

EuiccCardManager

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

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

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

ממשקי API של תפעול הפרופיל באמצעות EuiccCardManager דורשים שהמתקשר יהיה LPA. זה נאכף על ידי מסגרת אנדרואיד. המשמעות היא שהמתקשר חייב להרחיב 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 מקבלת התקשרות חוזרת מתהליך הטלפון באמצעות ממשק 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 באמצעות אפליקציית ספק

במכשירים שבהם פועל אנדרואיד 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 כדי לקבל את קוד ההפעלה מאפליקציית הספק על-ידי העברת היישום של מחלקת הסטאב של 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, אפליקציה מוסמכת למערכת, יכולה להיקשר אליו. השירות חייב לכלול גם מסנן כוונות עם הפעולה 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 משיק את זרימת ההפעלה של אפליקציית הספק על ידי שליחת הכוונה android.service.euicc.action.START_CARRIER_ACTIVATION לחבילת אפליקציית הספק המכילה את הפעולה. (מקלט האפליקציה של הספק חייב להיות מוגן בהצהרת המניפסט עם android:permission="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" כדי למנוע קבלת כוונות מאפליקציות שאינן LPA.)

    String packageName = // The carrier app's package name
    
    Intent carrierAppIntent =
        new Intent(“android.service.euicc.action.START_CARRIER_ACTIVATION”)
            .setPackage(packageName);
    
    ResolveInfo activity =
        context.getPackageManager().resolveActivity(carrierAppIntent, 0);
    
    carrierAppIntent
        .setClassName(activity.activityInfo.packageName, activity.activityInfo.name);
    
    startActivityForResult(carrierAppIntent, requestCode);
    
  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 חייב להכריז על פעילות הכוללת מסנן כוונות עם הפעולה 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 לא יוכלו לקבל את קוד ההפעלה מאפליקציית הספק.

תמיכה במספר eSIMs

עבור מכשירים עם אנדרואיד 10 ומעלה, מחלקת EuiccManager תומכת במכשירים עם מספר eSIMs. מכשירים עם eSIM בודד שמשדרגים לאנדרואיד 10 אינם דורשים כל שינוי ביישום ה-LPA מכיוון שהפלטפורמה משייכת אוטומטית את מופע EuiccManager עם ברירת המחדל של eUICC. ברירת המחדל של eUICC נקבעת על ידי הפלטפורמה עבור מכשירים עם רדיו HAL גרסה 1.2 ומעלה ועל ידי LPA עבור מכשירים עם גרסאות רדיו HAL נמוכות מ-1.2.

דרישות

כדי לתמוך במספר eSIM, המכשיר חייב להיות בעל יותר מ-eUICC אחד, שיכול להיות eUICC מובנה או חריץ SIM פיזי שבו ניתן להכניס eUICC נשלפים.

נדרשת Radio HAL גרסה 1.2 ומעלה כדי לתמוך במספר eSIMs. מומלצים Radio HAL גרסה 1.4 ו- RadioConfig HAL גרסה 1.2.

יישום

כדי לתמוך במספר eSIMs (כולל eUICCs נשלפים או SIMs ניתנים לתכנות), ה-LPA חייב ליישם EuiccService , אשר מקבל את מזהה החריץ המתאים לזיהוי הכרטיס שסופק המתקשר.

המשאב non_removable_euicc_slots שצוין ב- arrays.xml הוא מערך של מספרים שלמים המייצגים את מזהי החריצים של eUICCs המובנים במכשיר. עליך לציין משאב זה כדי לאפשר לפלטפורמה לקבוע אם eUICC שהוכנס ניתן להסרה או לא.

אפליקציית ספק למכשיר עם מספר eSIMs

בעת יצירת אפליקציית ספק עבור מכשיר עם מספר eSIMs, השתמש בשיטת 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 זמין בכל ה-builds של אנדרואיד (לא כל טלפון תומך ב-eSIM). מסיבה זו, אין מקרי בדיקת CTS מקצה לקצה. עם זאת, מקרי בדיקה בסיסיים זמינים ב-AOSP כדי להבטיח שממשקי ה-API של eUICC החשופים תקפים ב-builds של Android.

עליך לוודא שה-builds עוברים את מקרי הבדיקה הבאים של CTS (עבור ממשקי API ציבוריים): /platform/cts/tests/tests/telephony/current/src/android/telephony/euicc/cts .

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

אם אתה מכין LPA משלך, אתה צריך לעבור בדיקות הרבה יותר קפדניות. עליך לעבוד עם ספק המודם שלך, ספק שבב eUICC או eSIM OS, ספקי SM-DP+ וספקים כדי לפתור בעיות ולהבטיח יכולת פעולה הדדית של ה-LPA שלך בתוך ארכיטקטורת RSP. כמות טובה של בדיקות ידניות היא בלתי נמנעת. לקבלת כיסוי הבדיקה הטוב ביותר, עליך לעקוב אחר תוכנית הבדיקה של GSMA SGP.23 RSP .