پیاده سازی eSIM

فناوری سیم‌کارت جاسازی‌شده (eSIM یا eUICC) به کاربران تلفن همراه این امکان را می‌دهد تا بدون داشتن سیم‌کارت فیزیکی، نمایه شرکت مخابراتی را دانلود کرده و خدمات شرکت مخابراتی را فعال کنند. این یک مشخصات جهانی است که توسط GSMA هدایت می شود و تهیه سیم کارت از راه دور (RSP) را برای هر دستگاه تلفن همراه ممکن می کند. با شروع Android 9، چارچوب Android API های استانداردی را برای دسترسی به eSIM و مدیریت پروفایل های اشتراک در eSIM ارائه می دهد. این APIهای eUICC به اشخاص ثالث امکان می‌دهند تا برنامه‌های شرکت مخابراتی و دستیاران نمایه محلی (LPA) خود را در دستگاه‌های اندرویدی دارای سیم‌کارت فعال توسعه دهند.

LPA یک برنامه سیستمی مستقل است که باید در تصویر ساخت اندروید گنجانده شود. مدیریت پروفایل‌های روی eSIM عموماً توسط LPA انجام می‌شود، زیرا به‌عنوان پلی بین SM-DP+ (سرویس راه دور که بسته‌های پروفایل را آماده می‌کند، ذخیره می‌کند و به دستگاه‌ها تحویل می‌دهد) و تراشه eUICC عمل می‌کند. LPA APK می‌تواند به صورت اختیاری شامل یک مؤلفه رابط کاربری به نام LPA UI یا LUI باشد تا مکانی مرکزی برای کاربر نهایی برای مدیریت تمام پروفایل‌های اشتراک تعبیه‌شده فراهم کند. چارچوب Android به طور خودکار بهترین LPA موجود را کشف کرده و به آن متصل می شود و تمام عملیات eUICC را از طریق یک نمونه LPA هدایت می کند.

معماری ارائه سیم کارت از راه دور ساده شده (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 باشد. این توسط چارچوب Android اعمال می شود.

دستگاه‌های دارای Android 10 یا بالاتر می‌توانند از دستگاه‌هایی با چندین eSIM پشتیبانی کنند. برای اطلاعات بیشتر، به پشتیبانی از چندین eSIM مراجعه کنید.

ساخت اپلیکیشن حامل

APIهای eUICC در Android 9 این امکان را برای اپراتورهای شبکه تلفن همراه ایجاد می‌کنند تا اپلیکیشن‌هایی با نام تجاری شرکت مخابراتی برای مدیریت مستقیم نمایه‌های خود ایجاد کنند. این شامل دانلود و حذف نمایه‌های اشتراک متعلق به شرکت مخابراتی و همچنین تغییر به نمایه متعلق به شرکت مخابراتی است.

EuiccManager

EuiccManager نقطه ورود اصلی برنامه ها برای تعامل با LPA است. این شامل برنامه‌های شرکت مخابراتی می‌شود که دانلود، حذف و به اشتراک‌های متعلق به شرکت مخابراتی تغییر می‌دهند. این همچنین شامل برنامه سیستم LUI می‌شود که یک مکان مرکزی/UI برای مدیریت همه اشتراک‌های تعبیه‌شده ارائه می‌دهد و می‌تواند یک برنامه مجزا از برنامه‌ای باشد که EuiccService را ارائه می‌دهد.

برای استفاده از APIهای عمومی، یک برنامه حامل ابتدا باید نمونه EuiccManager را از طریق Context#getSystemService دریافت کند:

EuiccManager mgr = (EuiccManager) context.getSystemService(Context.EUICC_SERVICE);

قبل از انجام هرگونه عملیات eSIM، باید بررسی کنید که آیا eSIM در دستگاه پشتیبانی می شود یا خیر. اگر ویژگی android.hardware.telephony.euicc تعریف شده باشد و بسته LPA وجود داشته باشد EuiccManager#isEnabled() عموماً true برمی‌گرداند.

if (mgr == null || !mgr.isEnabled()) {
    return;
}

برای دریافت اطلاعات در مورد سخت افزار eUICC و نسخه eSIM OS:

EuiccInfo info = mgr.getEuiccInfo();
String osVer = info.getOsVersion();

بسیاری از APIها، مانند downloadSubscription() و switchToSubscription() از تماس‌های PendingIntent استفاده می‌کنند زیرا ممکن است چند ثانیه یا حتی چند دقیقه طول بکشد. PendingIntent با یک کد نتیجه در فضای EuiccManager#EMBEDDED_SUBSCRIPTION_RESULT_ ارسال می‌شود، که کدهای خطای تعریف‌شده از چارچوب را ارائه می‌کند، و همچنین یک کد نتیجه دلخواه با جزئیات منتشر شده از LPA به‌عنوان EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE ، اجازه می‌دهد تا برنامه‌ها را برای ردیابی اهداف/bugger.de. پاسخ تماس 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 را راه اندازی می کند که به کاربر اجازه می دهد یک کد تأیید را وارد کند. پس از وارد شدن کد، عملیات دانلود از سر گرفته می شود. این رویکرد به برنامه حامل کنترل کامل بر روی زمانی که UI نشان داده می‌شود، می‌دهد، اما به 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 یک دستگاه وجود داشته باشند و هر برنامه شرکت مخابراتی فقط باید اجازه دسترسی به نمایه‌های متعلق به آن شرکت مخابراتی را داشته باشد. برای مثال، شرکت مخابراتی A نباید بتواند نمایه ای را که متعلق به شرکت مخابراتی B است دانلود، فعال یا غیرفعال کند.

برای اطمینان از اینکه یک نمایه فقط برای مالک آن قابل دسترسی است، Android از مکانیزمی برای اعطای امتیازات ویژه به برنامه مالک نمایه (یعنی برنامه حامل) استفاده می کند. پلتفرم Android گواهی‌های ذخیره‌شده در فایل قانون دسترسی نمایه (ARF) را بارگیری می‌کند و به برنامه‌های امضاشده توسط این گواهی‌ها اجازه می‌دهد تا با APIهای EuiccManager تماس بگیرند. فرآیند سطح بالا در زیر توضیح داده شده است:

  1. اپراتور APK برنامه حامل را امضا می کند. ابزار apksigner گواهی کلید عمومی را به APK متصل می کند.
  2. Operator/SM-DP+ یک نمایه و ابرداده آن را آماده می کند که شامل یک ARF است که شامل:

    1. امضا (SHA-1 یا SHA-256) گواهی کلید عمومی برنامه حامل (الزامی)
    2. نام بسته برنامه حامل (اکیداً توصیه می شود)
  3. برنامه حامل سعی می کند یک عملیات eUICC را از طریق EuiccManager API انجام دهد.

  4. پلت فرم Android تأیید می کند که هش SHA-1 یا SHA-256 گواهی برنامه تماس گیرنده با امضای گواهی به دست آمده از ARF نمایه هدف مطابقت دارد. اگر نام بسته برنامه حامل در ARF گنجانده شده باشد، باید با نام بسته برنامه تماس گیرنده نیز مطابقت داشته باشد.

  5. پس از تأیید امضا و نام بسته (در صورت وجود)، امتیاز حامل از طریق نمایه هدف به برنامه تماس گیرنده اعطا می شود.

از آنجایی که فراداده نمایه می‌تواند خارج از خود نمایه در دسترس باشد (به طوری که LPA می‌تواند فراداده نمایه را قبل از دانلود نمایه از SM-DP+ یا زمانی که نمایه غیرفعال است از ISD-R بازیابی کند)، باید دارای همان قوانین امتیاز شرکت مخابراتی باشد. همانطور که در پروفایل

سیستم عامل eUICC و SM-DP+ باید از یک برچسب اختصاصی BF76 در فراداده نمایه پشتیبانی کند. محتوای برچسب باید همان قوانین امتیاز حاملی باشد که توسط اپلت قانون دسترسی (ARA) تعریف شده در امتیازات حامل 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 و ادغام آن با سیستم اندروید دارد.

نیازهای سخت افزار/مودم

LPA و eSIM OS در تراشه eUICC باید حداقل از GSMA RSP (Remote SIM Provisioning) نسخه 2.0 یا 2.2 پشتیبانی کنند. همچنین باید برنامه ریزی کنید که از سرورهای SM-DP+ و SM-DS که دارای نسخه RSP منطبق هستند، استفاده کنید. برای جزئیات معماری RSP، مشخصات معماری RSP GSMA SGP.21 را ببینید.

علاوه بر این، برای ادغام با API های eUICC در اندروید 9، مودم دستگاه باید قابلیت های ترمینال را با پشتیبانی از قابلیت های eUICC رمزگذاری شده ارسال کند (مدیریت نمایه محلی و دانلود نمایه). همچنین نیاز به پیاده سازی روش های زیر دارد:

  • IRadio HAL نسخه 1.1: setSimPower
  • IRadio HAL نسخه 1.2: getIccCardStatus

  • IRadioConfig HAL نسخه 1.0: getSimSlotsStatus

  • IRadioConfig AIDL v1.0: getAllowedCarriers

    Google LPA باید وضعیت قفل شرکت مخابراتی را بداند تا بتواند دانلود یا انتقال eSIM را فقط برای شرکت مخابراتی مجاز مجاز کند. در غیر این صورت ممکن است کاربران به دانلود و انتقال سیم‌کارت بپردازند و بعدا متوجه شوند که دستگاه به شرکت مخابراتی دیگری قفل شده است.

    • فروشندگان یا OEM ها باید API IRadioSim.getAllowedCarriers()HAL را پیاده سازی کنند.

    • فروشنده RIL / مودم باید وضعیت قفل و شناسه حامل حاملی را که دستگاه در آن قفل شده است را به عنوان بخشی از API IRadioSimResponse.getAllowedCarriersResponse()HAL پر کند.

مودم باید eSIM را با مشخصات راه‌اندازی پیش‌فرض فعال به‌عنوان یک سیم‌کارت معتبر تشخیص دهد و سیم‌کارت را روشن نگه دارد.

برای دستگاه‌های دارای 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 UI یا LUI.

برای پیاده سازی باطن LPA، باید EuiccService گسترش دهید و این سرویس را در فایل مانیفست خود اعلام کنید. این سرویس باید به مجوز سیستم android.permission.BIND_EUICC_SERVICE نیاز داشته باشد تا اطمینان حاصل شود که فقط سیستم می تواند به آن متصل شود. این سرویس همچنین باید دارای یک فیلتر هدف با عملکرد android.service.euicc.EuiccService باشد. اولویت فیلتر هدف باید روی یک مقدار غیر صفر تنظیم شود در صورتی که چندین پیاده سازی در دستگاه وجود داشته باشد. مثلا:

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

در داخل، چارچوب Android LPA فعال را تعیین می کند و در صورت لزوم با آن تعامل می کند تا از 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 نیاز داشته باشد. هر کدام باید یک فیلتر هدف با عملکرد مناسب، دسته 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 را ارائه می دهد) یک انتخاب طراحی است.

EuicCardManager

EuiccCardManager رابطی برای برقراری ارتباط با تراشه eSIM است. این توابع ES10 (همانطور که در مشخصات GSMA RSP توضیح داده شده است) ارائه می کند و دستورات درخواست/پاسخ APDU سطح پایین و همچنین تجزیه ASN.1 را مدیریت می کند. EuiccCardManager یک API سیستم است و فقط توسط برنامه های دارای امتیاز سیستم قابل فراخوانی است.

برنامه‌های حامل، LPA و APIهای Euicc

شکل 2. هم برنامه حامل و هم LPA از 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 از طریق یک رابط AIDL به EuiccCardController (که در فرآیند تلفن اجرا می‌شود) متصل می‌شود و هر روش EuiccCardManager پاسخ تماس خود را از فرآیند تلفن از طریق یک رابط AIDL اختصاصی متفاوت دریافت می‌کند. هنگام استفاده از API های EuiccCardManager ، تماس گیرنده (LPA) باید یک شی Executor ارائه دهد که از طریق آن تماس برگشتی فراخوانی می شود. این شی Executor ممکن است بر روی یک رشته یا روی یک Thread Pool به انتخاب شما اجرا شود.

اکثر 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 می تواند کل جریان UI را کنترل کند، به این معنی که هیچ رابط کاربری برنامه حامل نشان داده نمی شود. این رویکرد بررسی برچسب 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. برنامه حامل با فراخوانی setResult(int, Intent) و finish() به LPA پاسخ می دهد.

    1. اگر برنامه حامل با RESULT_OK پاسخ دهد، LPA به جریان فعال‌سازی ادامه می‌دهد. اگر برنامه حامل تشخیص دهد که کاربر به جای اینکه به LPA اجازه دهد به سرویس برنامه حامل متصل شود، باید یک کد QR را اسکن کند، برنامه حامل با استفاده از 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 تمام عناصر UI ضروری را در حین دانلود نمایش می دهد، مانند صفحه بارگیری.

  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 پیش‌فرض توسط پلتفرم برای دستگاه‌های دارای رادیو HAL نسخه 1.2 یا بالاتر و توسط LPA برای دستگاه‌هایی با نسخه‌های رادیویی HAL کمتر از 1.2 تعیین می‌شود.

الزامات

برای پشتیبانی از چندین eSIM، دستگاه باید بیش از یک eUICC داشته باشد، که می‌تواند یک eUICC داخلی یا یک اسلات فیزیکی سیم‌کارت باشد که می‌توان eUICC‌های قابل جابجایی را در آن قرار داد.

رادیو HAL نسخه 1.2 یا بالاتر برای پشتیبانی از چندین eSIM مورد نیاز است. Radio HAL نسخه 1.4 و RadioConfig HAL نسخه 1.2 توصیه می شود.

پیاده سازی

برای پشتیبانی از چندین eSIM (از جمله eUICCهای قابل جابجایی یا سیم‌کارت‌های قابل برنامه‌ریزی)، LPA باید EuiccService را پیاده‌سازی کند که شناسه اسلات مربوط به شناسه کارت ارائه‌شده توسط تماس‌گیرنده را دریافت می‌کند.

منبع non_removable_euicc_slots مشخص‌شده در arrays.xml آرایه‌ای از اعداد صحیح است که شناسه‌های اسلات از eUICC‌های داخلی دستگاه را نشان می‌دهد. شما باید این منبع را مشخص کنید تا به پلتفرم اجازه دهید تعیین کند که آیا eUICC درج شده قابل جابجایی است یا خیر.

برنامه شرکت مخابراتی برای دستگاهی با چندین eSIM

هنگام ساختن یک برنامه حامل برای دستگاهی با چندین eSIM، از روش createForCardId در EuiccManager برای ایجاد یک شی EuiccManager استفاده کنید که به شناسه کارت معین پین شده است. شناسه کارت یک مقدار صحیح است که به طور منحصربه‌فرد یک UICC یا یک eUICC را در دستگاه شناسایی می‌کند.

برای دریافت شناسه کارت برای eUICC پیش‌فرض دستگاه، از روش getCardIdForDefaultEuicc در TelephonyManager استفاده کنید. اگر نسخه HAL رادیویی کمتر از 1.2 باشد، این روش UNSUPPORTED_CARD_ID برمی‌گرداند و اگر دستگاه eUICC را نخوانده باشد، UNINITIALIZED_CARD_ID برمی‌گرداند.

همچنین می‌توانید شناسه‌های کارت را از 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: تکرار از طریق UICC و نمونه سازی 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 در همه بیلدهای اندروید در دسترس باشد (هر گوشی از eSIM پشتیبانی نمی کند). به همین دلیل، هیچ مورد تست CTS سرتاسری وجود ندارد. با این حال، موارد آزمایش اولیه در AOSP برای اطمینان از معتبر بودن APIهای eUICC در معرض در ساخت‌های اندروید موجود است.

باید مطمئن شوید که ساخت‌ها موارد تست CTS زیر را (برای APIهای عمومی) گذرانده‌اند: /platform/cts/tests/tests/telephony/current/src/android/telephony/euicc/cts .

اپراتورهایی که یک برنامه حامل را پیاده‌سازی می‌کنند باید چرخه‌های عادی تضمین کیفیت داخلی خود را طی کنند تا مطمئن شوند که همه ویژگی‌های پیاده‌سازی شده مطابق انتظار کار می‌کنند. حداقل، برنامه حامل باید بتواند تمام پروفایل های اشتراک متعلق به یک اپراتور را فهرست کند، یک نمایه را دانلود و نصب کند، یک سرویس را در نمایه فعال کند، بین پروفایل ها جابجا شود و نمایه ها را حذف کند.

اگر LPA خودتان را می سازید، باید آزمایش های بسیار دقیق تری را انجام دهید. شما باید با فروشنده مودم، تراشه eUICC یا فروشنده سیستم عامل eSIM، فروشندگان SM-DP+ و اپراتورها کار کنید تا مشکلات را حل کنید و از قابلیت همکاری LPA خود در معماری RSP اطمینان حاصل کنید. مقدار خوب تست دستی اجتناب ناپذیر است. برای بهترین پوشش آزمون، باید برنامه آزمایشی GSMA SGP.23 RSP را دنبال کنید.