APEX फ़ाइल का फ़ॉर्मैट

Android Pony EXpress (APEX) कंटेनर फ़ॉर्मैट को Android 10 में पेश किया गया था. इसका इस्तेमाल, सिस्टम के निचले लेवल के मॉड्यूल को इंस्टॉल करने के लिए किया जाता है. इस फ़ॉर्मैट की मदद से, सिस्टम कॉम्पोनेंट को अपडेट किया जा सकता है. ये कॉम्पोनेंट, Android ऐप्लिकेशन के स्टैंडर्ड मॉडल के मुताबिक नहीं होते. उदाहरण के लिए, कुछ कॉम्पोनेंट नेटिव सेवाएं और लाइब्रेरी, हार्डवेयर ऐब्स्ट्रैक्शन लेयर (HAL), रनटाइम (ART), और क्लास लाइब्रेरी हैं.

"APEX" शब्द का इस्तेमाल, APEX फ़ाइल के लिए भी किया जा सकता है.

बैकग्राउंड

Android, पैकेज इंस्टॉलर ऐप्लिकेशन (जैसे कि Google Play Store ऐप्लिकेशन) के ज़रिए, स्टैंडर्ड ऐप्लिकेशन मॉडल (जैसे कि सेवाएं, गतिविधियां) में फ़िट होने वाले मॉड्यूल के अपडेट का समर्थन करता है. हालांकि, ओएस के निचले स्तर के कॉम्पोनेंट के लिए इसी मॉडल का इस्तेमाल करने से ये समस्याएं हो सकती हैं:

  • APK पर आधारित मॉड्यूल का इस्तेमाल, बूट सीक्वेंस की शुरुआत में नहीं किया जा सकता. पैकेज मैनेजर, ऐप्लिकेशन के बारे में जानकारी का मुख्य सोर्स होता है. इसे सिर्फ़ गतिविधि मैनेजर से शुरू किया जा सकता है. गतिविधि मैनेजर, बूट करने की प्रोसेस के बाद के चरण में तैयार होता है.
  • APK फ़ॉर्मैट (खास तौर पर मेनिफ़ेस्ट) को Android ऐप्लिकेशन के लिए डिज़ाइन किया गया है. सिस्टम मॉड्यूल के लिए यह फ़ॉर्मैट हमेशा सही नहीं होता.

डिज़ाइन

इस सेक्शन में, APEX फ़ाइल फ़ॉर्मैट और APEX मैनेजर के हाई-लेवल डिज़ाइन के बारे में बताया गया है. APEX मैनेजर, APEX फ़ाइलों को मैनेज करने वाली एक सेवा है.

APEX के लिए इस डिज़ाइन को क्यों चुना गया, इस बारे में ज़्यादा जानने के लिए, APEX को डेवलप करते समय जिन विकल्पों पर विचार किया गया लेख पढ़ें.

APEX फ़ॉर्मैट

यह APEX फ़ाइल का फ़ॉर्मैट है.

APEX फ़ाइल फ़ॉर्मैट

पहली इमेज. APEX फ़ाइल फ़ॉर्मैट

टॉप लेवल पर, APEX फ़ाइल एक ज़िप फ़ाइल होती है. इसमें फ़ाइलों को बिना कंप्रेस किए सेव किया जाता है और ये 4 केबी की सीमाओं पर मौजूद होती हैं.

APEX फ़ाइल में ये चार फ़ाइलें होती हैं:

  • apex_manifest.json
  • AndroidManifest.xml
  • apex_payload.img
  • apex_pubkey

apex_manifest.json फ़ाइल में पैकेज का नाम और वर्शन होता है. इससे APEX फ़ाइल की पहचान होती है. यह JSON फ़ॉर्मैट में ApexManifest प्रोटोकॉल बफ़र है.

AndroidManifest.xml फ़ाइल की मदद से, APEX फ़ाइल, APK से जुड़े टूल और इन्फ़्रास्ट्रक्चर का इस्तेमाल कर सकती है. जैसे, ADB, PackageManager, और पैकेज इंस्टॉलर ऐप्लिकेशन (जैसे, Play Store). उदाहरण के लिए, APEX फ़ाइल में मौजूद बुनियादी मेटाडेटा की जांच करने के लिए, aapt जैसे किसी मौजूदा टूल का इस्तेमाल किया जा सकता है. इस फ़ाइल में पैकेज का नाम और वर्शन की जानकारी होती है. यह जानकारी आम तौर पर, apex_manifest.json में भी उपलब्ध होती है.

नए कोड और APEX से जुड़े सिस्टम के लिए, AndroidManifest.xml के बजाय apex_manifest.json का इस्तेमाल करने का सुझाव दिया जाता है. AndroidManifest.xml में टारगेट करने से जुड़ी अतिरिक्त जानकारी शामिल हो सकती है. इसका इस्तेमाल, ऐप्लिकेशन पब्लिश करने के मौजूदा टूल कर सकते हैं.

apex_payload.img, dm-verity की मदद से बैकअप ली गई ext4 फ़ाइल सिस्टम इमेज है. इमेज को लूपबैक डिवाइस के ज़रिए रनटाइम में माउंट किया जाता है. खास तौर पर, हैश ट्री और मेटाडेटा ब्लॉक को libavb लाइब्रेरी का इस्तेमाल करके बनाया जाता है. फ़ाइल सिस्टम के पेलोड को पार्स नहीं किया गया है, क्योंकि इमेज को उसी जगह पर माउंट किया जाना चाहिए. सामान्य फ़ाइलें, apex_payload.img फ़ाइल में शामिल होती हैं.

apex_pubkey, फ़ाइल सिस्टम इमेज को साइन करने के लिए इस्तेमाल किया गया सार्वजनिक पासकोड है. रनटाइम के दौरान, यह कुंजी यह पक्का करती है कि डाउनलोड किए गए APEX पर उसी इकाई ने हस्ताक्षर किए हों जिसने बिल्ट-इन पार्टीशन में मौजूद APEX पर हस्ताक्षर किए हैं.

APEX के नाम से जुड़े दिशा-निर्देश

प्लैटफ़ॉर्म के बेहतर होने के साथ-साथ, नए APEX के नामों में टकराव से बचने के लिए, नाम रखने से जुड़े इन दिशा-निर्देशों का पालन करें:

  • com.android.*
    • यह AOSP APEX के लिए रिज़र्व है. यह किसी कंपनी या डिवाइस के लिए यूनीक नहीं होता.
  • com.<companyname>.*
    • किसी कंपनी के लिए बुक किया गया है. इस कुकी का इस्तेमाल, उस कंपनी के कई डिवाइसों पर किया जा सकता है.
  • com.<companyname>.<devicename>.*
    • यह किसी डिवाइस (या डिवाइसों के सबसेट) के लिए यूनीक APEX के लिए रिज़र्व किया गया है.

APEX मैनेजर

APEX मैनेजर (या apexd) एक स्टैंडअलोन नेटिव प्रोसेस है. यह APEX फ़ाइलों की पुष्टि करने, उन्हें इंस्टॉल करने, और उन्हें अनइंस्टॉल करने के लिए ज़िम्मेदार है. यह प्रोसेस लॉन्च हो जाती है और बूट सीक्वेंस में पहले से ही तैयार रहती है. APEX फ़ाइलें आम तौर पर डिवाइस पर /system/apex में पहले से इंस्टॉल होती हैं. अगर कोई अपडेट उपलब्ध नहीं है, तो APEX मैनेजर डिफ़ॉल्ट रूप से इन पैकेज का इस्तेमाल करता है.

किसी APEX को अपडेट करने के लिए, PackageManager क्लास का इस्तेमाल किया जाता है. अपडेट करने का क्रम इस तरह होता है.

  1. APEX फ़ाइल को पैकेज इंस्टॉलर ऐप्लिकेशन, ADB या किसी अन्य सोर्स से डाउनलोड किया जाता है.
  2. पैकेज मैनेजर, इंस्टॉल करने की प्रोसेस शुरू करता है. फ़ाइल को APEX के तौर पर पहचानने के बाद, पैकेज मैनेजर, कंट्रोल को APEX मैनेजर को ट्रांसफ़र कर देता है.
  3. APEX मैनेजर, APEX फ़ाइल की पुष्टि करता है.
  4. अगर APEX फ़ाइल की पुष्टि हो जाती है, तो APEX मैनेजर का इंटरनल डेटाबेस अपडेट हो जाता है. इससे पता चलता है कि अगली बार बूट करने पर APEX फ़ाइल चालू हो जाएगी.
  5. पैकेज की पुष्टि हो जाने के बाद, इंस्टॉल करने का अनुरोध करने वाले व्यक्ति को ब्रॉडकास्ट मिलता है.
  6. इंस्टॉलेशन जारी रखने के लिए, सिस्टम को रीबूट करना होगा.
  7. अगले बूट पर, APEX मैनेजर शुरू होता है. यह इंटरनल डेटाबेस को पढ़ता है और सूची में शामिल हर APEX फ़ाइल के लिए, यह काम करता है:

    1. APEX फ़ाइल की पुष्टि करता है.
    2. यह APEX फ़ाइल से लूपबैक डिवाइस बनाता है.
    3. यह लूपबैक डिवाइस के ऊपर एक डिवाइस मैपर ब्लॉक डिवाइस बनाता है.
    4. यह डिवाइस मैपर ब्लॉक डिवाइस को यूनीक पाथ (उदाहरण के लिए, /apex/name@ver) पर माउंट करता है.

जब इंटरनल डेटाबेस में मौजूद सभी APEX फ़ाइलें माउंट हो जाती हैं, तब APEX मैनेजर, सिस्टम के अन्य कॉम्पोनेंट के लिए बाइंडर सेवा उपलब्ध कराता है. इससे इंस्टॉल की गई APEX फ़ाइलों के बारे में जानकारी क्वेरी की जा सकती है. उदाहरण के लिए, सिस्टम के अन्य कॉम्पोनेंट, डिवाइस में इंस्टॉल की गई APEX फ़ाइलों की सूची के बारे में क्वेरी कर सकते हैं. इसके अलावा, वे उस सटीक पाथ के बारे में भी क्वेरी कर सकते हैं जहां कोई खास APEX माउंट किया गया है, ताकि फ़ाइलों को ऐक्सेस किया जा सके.

APEX फ़ाइलें, APK फ़ाइलें होती हैं

APEX फ़ाइलें, मान्य APK फ़ाइलें होती हैं. ऐसा इसलिए, क्योंकि ये APK सिग्नेचर स्कीम का इस्तेमाल करके साइन किए गए ZIP संग्रह होते हैं. इनमें AndroidManifest.xml फ़ाइल होती है. इससे APEX फ़ाइलें, APK फ़ाइलों के लिए उपलब्ध इन्फ़्रास्ट्रक्चर का इस्तेमाल कर पाती हैं. जैसे, पैकेज इंस्टॉलर ऐप्लिकेशन, साइनिंग यूटिलिटी, और पैकेज मैनेजर.

APEX फ़ाइल में मौजूद AndroidManifest.xml फ़ाइल में कम से कम जानकारी होती है. इसमें पैकेज name, versionCode, और ज़रूरत के हिसाब से targetSdkVersion, minSdkVersion, और maxSdkVersion शामिल होते हैं, ताकि सटीक टारगेटिंग की जा सके. इस जानकारी की मदद से, APEX फ़ाइलों को मौजूदा चैनलों के ज़रिए डिलीवर किया जा सकता है. जैसे, पैकेज इंस्टॉलर ऐप्लिकेशन और ADB.

इस्तेमाल किए जा सकने वाले फ़ाइल टाइप

APEX फ़ॉर्मैट में इन फ़ाइल टाइप का इस्तेमाल किया जा सकता है:

  • नेटिव शेयर की गई लाइब्रेरी
  • नेटिव एक्ज़ीक्यूटेबल
  • JAR फ़ाइलें
  • डेटा फ़ाइलें
  • कॉन्फ़िगरेशन फ़ाइलें

इसका मतलब यह नहीं है कि APEX, इन सभी फ़ाइल टाइप को अपडेट कर सकता है. किसी फ़ाइल टाइप को अपडेट किया जा सकता है या नहीं, यह इस बात पर निर्भर करता है कि प्लैटफ़ॉर्म कौन-सा है. साथ ही, फ़ाइल टाइप के इंटरफ़ेस की परिभाषाएं कितनी स्थिर हैं.

साइन इन करने के विकल्प

APEX फ़ाइलों पर दो तरह से हस्ताक्षर किए जाते हैं. सबसे पहले, apex_payload.img फ़ाइल (खास तौर पर, apex_payload.img में जोड़ी गई vbmeta डिस्क्रिप्टर) पर एक कुंजी से हस्ताक्षर किया जाता है. इसके बाद, पूरे APEX को APK सिग्नेचर स्कीम v3 का इस्तेमाल करके साइन किया जाता है. इस प्रोसेस में दो अलग-अलग कुंजियों का इस्तेमाल किया जाता है.

डिवाइस पर, vbmeta डिस्क्रिप्टर पर हस्ताक्षर करने के लिए इस्तेमाल की गई निजी कुंजी से जुड़ी सार्वजनिक कुंजी इंस्टॉल की जाती है. APEX मैनेजर, सार्वजनिक पासकोड का इस्तेमाल करके उन APEX की पुष्टि करता है जिन्हें इंस्टॉल करने का अनुरोध किया गया है. हर APEX को अलग-अलग कुंजियों से साइन किया जाना चाहिए. इसे बिल्ड टाइम और रनटाइम, दोनों में लागू किया जाता है.

पहले से मौजूद पार्टीशन में APEX

APEX फ़ाइलें, बिल्ट-इन पार्टीशन में मौजूद हो सकती हैं. जैसे, /system. यह पार्टिशन पहले से ही dm-verity पर है. इसलिए, APEX फ़ाइलें सीधे तौर पर लूपबैक डिवाइस पर माउंट की जाती हैं.

अगर कोई APEX, बिल्ट-इन पार्टीशन में मौजूद है, तो उसे अपडेट किया जा सकता है. इसके लिए, आपको उसी पैकेज के नाम वाला APEX पैकेज देना होगा. साथ ही, उसका वर्शन कोड, मौजूदा वर्शन कोड से ज़्यादा या उसके बराबर होना चाहिए. नया APEX, /data में सेव किया जाता है. साथ ही, APK की तरह ही, नया इंस्टॉल किया गया वर्शन, बिल्ट-इन पार्टीशन में मौजूद वर्शन को बदल देता है. हालांकि, APK के उलट, APEX का नया इंस्टॉल किया गया वर्शन सिर्फ़ रीबूट करने के बाद चालू होता है.

कर्नेल से जुड़ी ज़रूरी शर्तें

किसी Android डिवाइस पर APEX मेनलाइन मॉड्यूल इस्तेमाल करने के लिए, Linux कर्नल की इन सुविधाओं का होना ज़रूरी है: लूपबैक ड्राइवर और dm-verity. लूपबैक ड्राइवर, फ़ाइल सिस्टम इमेज को APEX मॉड्यूल में माउंट करता है. साथ ही, dm-verity, APEX मॉड्यूल की पुष्टि करता है.

APEX मॉड्यूल का इस्तेमाल करते समय, सिस्टम की परफ़ॉर्मेंस को बेहतर बनाने के लिए, लूपबैक ड्राइवर और dm-verity की परफ़ॉर्मेंस का अच्छा होना ज़रूरी है.

इस्तेमाल किए जा सकने वाले कर्नेल वर्शन

APEX मेनलाइन मॉड्यूल, कर्नेल वर्शन 4.4 या इसके बाद के वर्शन का इस्तेमाल करने वाले डिवाइसों पर काम करते हैं. Android 10 या इसके बाद के वर्शन के साथ लॉन्च होने वाले नए डिवाइसों में, APEX मॉड्यूल के साथ काम करने के लिए कर्नेल वर्शन 4.9 या इसके बाद के वर्शन का इस्तेमाल करना ज़रूरी है.

ज़रूरी कर्नेल पैच

APEX मॉड्यूल के साथ काम करने के लिए ज़रूरी कर्नल पैच, Android के कॉमन ट्री में शामिल होते हैं. APEX के साथ काम करने वाले पैच पाने के लिए, Android के कॉमन ट्री का नया वर्शन इस्तेमाल करें.

कर्नेल वर्शन 4.4

यह वर्शन सिर्फ़ उन डिवाइसों के लिए काम करता है जिन्हें Android 9 से Android 10 में अपग्रेड किया गया है और जिनमें APEX मॉड्यूल इस्तेमाल किए जा सकते हैं. ज़रूरी पैच पाने के लिए, android-4.4 ब्रांच से डाउन-मर्ज करने का सुझाव दिया जाता है. यहां कर्नल के वर्शन 4.4 के लिए ज़रूरी अलग-अलग पैच की सूची दी गई है.

  • UPSTREAM: loop: add ioctl for changing logical block size (4.4)
  • BACKPORT: block/loop: set hw_sectors (4.4)
  • UPSTREAM: loop: Add LOOP_SET_BLOCK_SIZE in compat ioctl (4.4)
  • ANDROID: mnt: Fix next_descendent (4.4)
  • ANDROID: mnt: remount should propagate to slaves of slaves (4.4)
  • ANDROID: mnt: Propagate remount correctly (4.4)
  • Revert "ANDROID: dm verity: add minimum prefetch size" (4.4)
  • UPSTREAM: loop: drop caches if offset or block_size are changed (4.4)

कर्नेल वर्शन 4.9/4.14/4.19

कर्नेल के वर्शन 4.9/4.14/4.19 के लिए ज़रूरी पैच पाने के लिए, android-common ब्रांच से डाउन-मर्ज करें.

कर्नेल कॉन्फ़िगरेशन के ज़रूरी विकल्प

यहां दी गई सूची में, Android 10 में पेश किए गए APEX मॉड्यूल के साथ काम करने के लिए, बेस कॉन्फ़िगरेशन की ज़रूरी शर्तों के बारे में बताया गया है. जिन आइटम के आगे स्टार (*) का निशान लगा है वे Android 9 और इससे पहले के वर्शन के लिए ज़रूरी शर्तें हैं.

(*) CONFIG_AIO=Y # AIO support (for direct I/O on loop devices)
CONFIG_BLK_DEV_LOOP=Y # for loop device support
CONFIG_BLK_DEV_LOOP_MIN_COUNT=16 # pre-create 16 loop devices
(*) CONFIG_CRYPTO_SHA1=Y # SHA1 hash for DM-verity
(*) CONFIG_CRYPTO_SHA256=Y # SHA256 hash for DM-verity
CONFIG_DM_VERITY=Y # DM-verity support

कर्नेल कमांड-लाइन पैरामीटर की ज़रूरी शर्तें

APEX के साथ काम करने के लिए, पक्का करें कि कर्नल कमांड-लाइन पैरामीटर इन ज़रूरी शर्तों को पूरा करते हों:

  • loop.max_loop को सेट नहीं किया जाना चाहिए
  • loop.max_part की वैल्यू 8 से ज़्यादा नहीं होनी चाहिए

APEX बनाना

इस सेक्शन में, Android बिल्ड सिस्टम का इस्तेमाल करके APEX बनाने का तरीका बताया गया है. यहां apex.test नाम के APEX के लिए Android.bp का उदाहरण दिया गया है.

apex {
    name: "apex.test",
    manifest: "apex_manifest.json",
    file_contexts: "file_contexts",
    // libc.so and libcutils.so are included in the apex
    native_shared_libs: ["libc", "libcutils"],
    binaries: ["vold"],
    java_libs: ["core-all"],
    prebuilts: ["my_prebuilt"],
    compile_multilib: "both",
    key: "apex.test.key",
    certificate: "platform",
}

apex_manifest.json उदाहरण:

{
  "name": "com.android.example.apex",
  "version": 1
}

file_contexts उदाहरण:

(/.*)?           u:object_r:system_file:s0
/sub(/.*)?       u:object_r:sub_file:s0
/sub/file3       u:object_r:file3_file:s0

APEX में फ़ाइल टाइप और जगहें

फ़ाइल टाइप APEX में जगह की जानकारी
शेयर की गई लाइब्रेरी /lib और /lib64 (x86 में अनुवादित आर्म के लिए /lib/arm)
एक्ज़ीक्यूटेबल /bin
Java लाइब्रेरी /javalib
पहले से बनाए गए /etc

ट्रांज़िटिव डिपेंडेंसी

APEX फ़ाइलों में, नेटिव शेयर की गई लाइब्रेरी या एक्ज़ीक्यूटेबल की ट्रांज़िटिव डिपेंडेंसी अपने-आप शामिल हो जाती हैं. उदाहरण के लिए, अगर libFoo, libBar पर निर्भर है, तो native_shared_libs प्रॉपर्टी में सिर्फ़ libFoo को शामिल करने पर, दोनों लाइब्रेरी शामिल की जाती हैं.

एक से ज़्यादा एबीआई मैनेज करना

डिवाइस के प्राइमरी और सेकंडरी, दोनों ऐप्लिकेशन बाइनरी इंटरफ़ेस (एबीआई) के लिए native_shared_libs प्रॉपर्टी इंस्टॉल करें. अगर कोई एपीईएक्स, एक ही एबीआइ (यानी कि सिर्फ़ 32 बिट या सिर्फ़ 64 बिट) वाले डिवाइसों को टारगेट करता है, तो सिर्फ़ उस एबीआइ वाली लाइब्रेरी इंस्टॉल की जाती हैं.

डिवाइस के मुख्य एबीआई के लिए, सिर्फ़ binaries प्रॉपर्टी इंस्टॉल करें. इसके बारे में यहां बताया गया है:

  • अगर डिवाइस सिर्फ़ 32 बिट का है, तो बाइनरी का सिर्फ़ 32-बिट वाला वैरिएंट इंस्टॉल किया जाता है.
  • अगर डिवाइस सिर्फ़ 64 बिट का है, तो बाइनरी का सिर्फ़ 64-बिट वाला वर्शन इंस्टॉल किया जाता है.

नेटिव लाइब्रेरी और बाइनरी के एबीआइ पर ज़्यादा कंट्रोल पाने के लिए, multilib.[first|lib32|lib64|prefer32|both].[native_shared_libs|binaries] प्रॉपर्टी का इस्तेमाल करें.

  • first: यह डिवाइस के प्राइमरी एबीआई से मेल खाता है. यह बाइनरी के लिए डिफ़ॉल्ट वैल्यू है.
  • lib32: अगर डिवाइस पर काम करता है, तो यह डिवाइस के 32-बिट एबीआई से मेल खाता है.
  • lib64: यह उस डिवाइस के 64-बिट एबीआई से मेल खाता है जिस पर यह काम करता है.
  • prefer32: अगर डिवाइस पर काम करता है, तो यह डिवाइस के 32-बिट एबीआई से मेल खाता है. अगर 32-बिट ABI काम नहीं करता है, तो यह 64-बिट ABI से मेल खाता है.
  • both: दोनों ABI से मैच करता है. यह native_shared_libraries के लिए डिफ़ॉल्ट वैल्यू है.

java, libraries, और prebuilts प्रॉपर्टी, एबीआई से जुड़ी नहीं हैं.

यह उदाहरण, ऐसे डिवाइस के लिए है जो 32/64 बिट वाले वर्शन के साथ काम करता है और 32 बिट वाले वर्शन को प्राथमिकता नहीं देता:

apex {
    // other properties are omitted
    native_shared_libs: ["libFoo"], // installed for 32 and 64
    binaries: ["exec1"], // installed for 64, but not for 32
    multilib: {
        first: {
            native_shared_libs: ["libBar"], // installed for 64, but not for 32
            binaries: ["exec2"], // same as binaries without multilib.first
        },
        both: {
            native_shared_libs: ["libBaz"], // same as native_shared_libs without multilib
            binaries: ["exec3"], // installed for 32 and 64
        },
        prefer32: {
            native_shared_libs: ["libX"], // installed for 32, but not for 64
        },
        lib64: {
            native_shared_libs: ["libY"], // installed for 64, but not for 32
        },
    },
}

vbmeta पर हस्ताक्षर करना

हर APEX को अलग-अलग कुंजियों से साइन करें. जब नई कुंजी की ज़रूरत हो, तब सार्वजनिक-निजी कुंजी का एक जोड़ा बनाएं और एक apex_key मॉड्यूल बनाएं. कुंजी का इस्तेमाल करके APEX पर हस्ताक्षर करने के लिए, key प्रॉपर्टी का इस्तेमाल करें. सार्वजनिक पासकोड, avb_pubkey नाम से APEX में अपने-आप शामिल हो जाता है.

# create an rsa key pair
openssl genrsa -out foo.pem 4096

# extract the public key from the key pair
avbtool extract_public_key --key foo.pem --output foo.avbpubkey

# in Android.bp
apex_key {
    name: "apex.test.key",
    public_key: "foo.avbpubkey",
    private_key: "foo.pem",
}

ऊपर दिए गए उदाहरण में, सार्वजनिक कुंजी (foo) का नाम, कुंजी का आईडी बन जाता है. APEX पर हस्ताक्षर करने के लिए इस्तेमाल की गई कुंजी का आईडी, APEX में लिखा जाता है. रनटाइम के दौरान, apexd डिवाइस में मौजूद उसी आईडी वाली सार्वजनिक कुंजी का इस्तेमाल करके, APEX की पुष्टि करता है.

APEX पर हस्ताक्षर करना

APEX को उसी तरह साइन करें जिस तरह APK को साइन किया जाता है. APEX को दो बार साइन करें. एक बार मिनी फ़ाइल सिस्टम (apex_payload.img फ़ाइल) के लिए और दूसरी बार पूरी फ़ाइल के लिए.

फ़ाइल-लेवल पर किसी APEX पर हस्ताक्षर करने के लिए, इन तीन तरीकों में से किसी एक तरीके से certificate प्रॉपर्टी सेट करें:

  • सेट नहीं है: अगर कोई वैल्यू सेट नहीं की जाती है, तो APEX पर PRODUCT_DEFAULT_DEV_CERTIFICATE पर मौजूद सर्टिफ़िकेट से हस्ताक्षर किया जाता है. अगर कोई फ़्लैग सेट नहीं किया गया है, तो पाथ डिफ़ॉल्ट रूप से build/target/product/security/testkey पर सेट होता है.
  • <name>: APEX पर, <name> सर्टिफ़िकेट से हस्ताक्षर किया गया है. यह सर्टिफ़िकेट, PRODUCT_DEFAULT_DEV_CERTIFICATE वाली डायरेक्ट्री में ही मौजूद है.
  • :<name>: APEX को उस सर्टिफ़िकेट से साइन किया जाता है जिसे <name> नाम के Soong मॉड्यूल से तय किया जाता है. सर्टिफ़िकेट मॉड्यूल को इस तरह से तय किया जा सकता है.
android_app_certificate {
    name: "my_key_name",
    certificate: "dir/cert",
    // this will use dir/cert.x509.pem (the cert) and dir/cert.pk8 (the private key)
}

APEX इंस्टॉल करना

ADB का इस्तेमाल करके, APEX इंस्टॉल करें.

adb install apex_file_name
adb reboot

अगर apex_manifest.json में supportsRebootlessUpdate को true पर सेट किया गया है और फ़िलहाल इंस्टॉल किए गए APEX का इस्तेमाल नहीं किया जा रहा है (उदाहरण के लिए, इसमें शामिल सभी सेवाएं बंद कर दी गई हैं), तो --force-non-staged फ़्लैग की मदद से, बिना रीबूट किए नया APEX इंस्टॉल किया जा सकता है.

adb install --force-non-staged apex_file_name

APEX का इस्तेमाल करना

रीबूट करने के बाद, APEX को /apex/<apex_name>@<version> डायरेक्ट्री में माउंट किया जाता है. एक ही APEX के कई वर्शन एक साथ माउंट किए जा सकते हैं. माउंट पाथ में से, सबसे नए वर्शन से जुड़ा पाथ /apex/<apex_name> पर बाइंड-माउंट किया जाता है.

क्लाइंट, बाइंड-माउंट किए गए पाथ का इस्तेमाल करके, APEX से फ़ाइलें पढ़ सकते हैं या उन्हें एक्ज़ीक्यूट कर सकते हैं.

APEX का इस्तेमाल आम तौर पर इस तरह किया जाता है:

  1. डिवाइस शिप करते समय, ओईएम या ओडीएम /system/apex में APEX को पहले से लोड करता है.
  2. APEX में मौजूद फ़ाइलों को /apex/<apex_name>/ पाथ के ज़रिए ऐक्सेस किया जाता है.
  3. जब /data/apex में APEX का अपडेट किया गया वर्शन इंस्टॉल किया जाता है, तो रीबूट करने के बाद पाथ नए APEX की ओर ले जाता है.

APEX का इस्तेमाल करके किसी सेवा को अपडेट करना

APEX का इस्तेमाल करके किसी सेवा को अपडेट करने के लिए:

  1. सिस्टम पार्टीशन में मौजूद सेवा को अपडेट किए जा सकने वाले के तौर पर मार्क करें. सेवा की परिभाषा में updatable विकल्प जोड़ें.

    /system/etc/init/myservice.rc:
    
    service myservice /system/bin/myservice
        class core
        user system
        ...
        updatable
    
  2. अपडेट की गई सेवा के लिए, नई .rc फ़ाइल बनाएं. मौजूदा सेवा को फिर से तय करने के लिए, override विकल्प का इस्तेमाल करें.

    /apex/my.apex/etc/init.rc:
    
    service myservice /apex/my.apex/bin/myservice
        class core
        user system
        ...
        override
    

सेवा की परिभाषाएं, सिर्फ़ APEX की .rc फ़ाइल में तय की जा सकती हैं. ऐक्शन ट्रिगर, APEX में काम नहीं करते.

अगर अपडेट की जा सकने वाली कोई सेवा, APEX चालू होने से पहले शुरू हो जाती है, तो APEX चालू होने तक सेवा शुरू होने में देरी होती है.

सिस्टम को APEX अपडेट के साथ काम करने के लिए कॉन्फ़िगर करना

APEX फ़ाइल अपडेट करने की सुविधा के लिए, इस सिस्टम प्रॉपर्टी को true पर सेट करें.

<device.mk>:

PRODUCT_PROPERTY_OVERRIDES += ro.apex.updatable=true

BoardConfig.mk:
TARGET_FLATTEN_APEX := false

या सिर्फ़

<device.mk>:

$(call inherit-product, $(SRC_TARGET_DIR)/product/updatable_apex.mk)

Flattened APEX

लेगसी डिवाइसों के लिए, कभी-कभी पुराने कर्नल को अपडेट करना मुमकिन नहीं होता. ऐसा इसलिए, ताकि APEX को पूरी तरह से सपोर्ट किया जा सके. उदाहरण के लिए, हो सकता है कि कर्नल को CONFIG_BLK_DEV_LOOP=Y के बिना बनाया गया हो. यह एपीईएक्स के अंदर फ़ाइल सिस्टम इमेज को माउंट करने के लिए ज़रूरी है.

फ़्लैटंड एपीईएक्स, खास तौर पर बनाया गया एपीईएक्स होता है. इसे ऐसे डिवाइसों पर चालू किया जा सकता है जिनमें लेगसी कर्नल होता है. फ़्लैट किए गए APEX में मौजूद फ़ाइलें, सीधे तौर पर बिल्ट-इन पार्टीशन के तहत किसी डायरेक्ट्री में इंस्टॉल की जाती हैं. उदाहरण के लिए, फ़्लैट किए गए APEX में lib/libFoo.so को my.apex से /system/apex/my.apex/lib/libFoo.so में इंस्टॉल किया जाता है.

फ़्लैट किए गए APEX को चालू करने के लिए, लूप डिवाइस की ज़रूरत नहीं होती. पूरी डायरेक्ट्री /system/apex/my.apex को सीधे तौर पर /apex/name@ver से बाइंड-माउंट किया जाता है.

नेटवर्क से APEX के अपडेट किए गए वर्शन डाउनलोड करके, फ़्लैट किए गए APEX अपडेट नहीं किए जा सकते. इसकी वजह यह है कि डाउनलोड किए गए APEX को फ़्लैट नहीं किया जा सकता. फ़्लैट किए गए APEX को सिर्फ़ रेगुलर OTA के ज़रिए अपडेट किया जा सकता है.

फ़्लैट किए गए APEX का इस्तेमाल डिफ़ॉल्ट कॉन्फ़िगरेशन के तौर पर किया जाता है. इसका मतलब है कि सभी APEX डिफ़ॉल्ट रूप से फ़्लैट किए जाते हैं. हालांकि, अगर आपको APEX अपडेट करने के लिए, फ़्लैट नहीं किए गए APEX बनाने हैं, तो आपको अपने डिवाइस को साफ़ तौर पर कॉन्फ़िगर करना होगा. इसके बारे में ऊपर बताया गया है.

किसी डिवाइस में फ़्लैट किए गए और फ़्लैट नहीं किए गए APEX को एक साथ इस्तेमाल नहीं किया जा सकता. किसी डिवाइस में मौजूद सभी APEX, या तो नॉन-फ़्लैट किए गए होने चाहिए या फ़्लैट किए गए होने चाहिए. यह खास तौर पर तब ज़रूरी होता है, जब Mainline जैसे प्रोजेक्ट के लिए, पहले से साइन किए गए APEX प्रीबिल्ट शिप किए जा रहे हों. ऐसे APEX जो पहले से साइन नहीं किए गए हैं (यानी कि सोर्स से बनाए गए हैं) उन्हें भी फ़्लैट नहीं किया जाना चाहिए. साथ ही, उन्हें सही कुंजियों से साइन किया जाना चाहिए. डिवाइस को updatable_apex.mk से इनहेरिट करना चाहिए. इसके बारे में APEX की मदद से सेवा अपडेट करना लेख में बताया गया है.

कंप्रेस किए गए APEX

Android 12 और इसके बाद के वर्शन में, अपडेट किए जा सकने वाले APEX पैकेज के स्टोरेज पर पड़ने वाले असर को कम करने के लिए, APEX कंप्रेस करने की सुविधा दी गई है. APEX को अपडेट करने के बाद, पहले से इंस्टॉल किए गए वर्शन का इस्तेमाल नहीं किया जाता. हालांकि, यह अब भी उतनी ही जगह लेता है. इस्तेमाल किया गया स्पेस उपलब्ध नहीं रहता.

APEX कंप्रेस करने की सुविधा, स्टोरेज पर पड़ने वाले इस असर को कम करती है. इसके लिए, यह सुविधा रीड-ओनली पार्टिशन (जैसे कि /system पार्टिशन) पर, APEX फ़ाइलों के कंप्रेस किए गए सेट का इस्तेमाल करती है. Android 12 और इसके बाद के वर्शन में, DEFLATE zip कंप्रेस करने वाले एल्गोरिदम का इस्तेमाल किया जाता है.

कंप्रेशन की सुविधा, इन फ़ाइलों को ऑप्टिमाइज़ नहीं करती:

  • बूटस्ट्रैप APEX, जिन्हें बूट सीक्वेंस में बहुत पहले माउंट करने की ज़रूरत होती है.

  • ऐसे APEX जिन्हें अपडेट नहीं किया जा सकता. कंप्रेशन का फ़ायदा सिर्फ़ तब मिलता है, जब /data पार्टीशन पर APEX का अपडेट किया गया वर्शन इंस्टॉल हो. अपडेट किए जा सकने वाले APEX की पूरी सूची, मॉड्यूलर सिस्टम कॉम्पोनेंट पेज पर उपलब्ध है.

  • डाइनैमिक शेयर की गई लाइब्रेरी वाले APEX. apexd हमेशा ऐसे APEX के दोनों वर्शन (पहले से इंस्टॉल और अपग्रेड किया गया) चालू करता है. इसलिए, उन्हें कंप्रेस करने से कोई फ़ायदा नहीं होता.

कंप्रेस की गई APEX फ़ाइल का फ़ॉर्मैट

यह कंप्रेस की गई APEX फ़ाइल का फ़ॉर्मैट है.

डायग्राम में, कंप्रेस की गई APEX फ़ाइल का फ़ॉर्मैट दिखाया गया है

दूसरी इमेज. कंप्रेस की गई APEX फ़ाइल का फ़ॉर्मैट

सबसे ऊपर, कंप्रेस की गई APEX फ़ाइल एक ZIP फ़ाइल होती है. इसमें ओरिजनल APEX फ़ाइल, कंप्रेस किए गए फ़ॉर्म में होती है. इसका कंप्रेस करने का लेवल 9 होता है. साथ ही, इसमें अन्य फ़ाइलें बिना कंप्रेस किए सेव की जाती हैं.

APEX फ़ाइल में चार फ़ाइलें होती हैं:

  • original_apex: कंप्रेस करने के लेवल 9 के साथ डिफ़्लेट किया गया यह ओरिजनल, बिना कंप्रेस की गई APEX फ़ाइल है.
  • apex_manifest.pb: सिर्फ़ सेव किया गया
  • AndroidManifest.xml: सिर्फ़ सेव किया गया
  • apex_pubkey: सिर्फ़ सेव किया गया

apex_manifest.pb, AndroidManifest.xml, और apex_pubkey फ़ाइलें, original_apex में मौजूद फ़ाइलों की कॉपी हैं.

कंप्रेस किया गया APEX बनाएं

कंप्रेस किए गए APEX को apex_compression_tool.py टूल का इस्तेमाल करके बनाया जा सकता है. यह टूल system/apex/tools पर मौजूद है.

बिल्ड सिस्टम में, APEX कंप्रेस करने से जुड़े कई पैरामीटर उपलब्ध हैं.

Android.bp में, APEX फ़ाइल को कंप्रेस किया जा सकता है या नहीं, यह compressible प्रॉपर्टी से कंट्रोल होता है:

apex {
    name: "apex.test",
    manifest: "apex_manifest.json",
    file_contexts: "file_contexts",
    compressible: true,
}

PRODUCT_COMPRESSED_APEX प्रॉडक्ट फ़्लैग से यह कंट्रोल होता है कि सोर्स से बनाई गई सिस्टम इमेज में कंप्रेस की गई APEX फ़ाइलें शामिल होनी चाहिए या नहीं.

लोकल एक्सपेरिमेंट के लिए, OVERRIDE_PRODUCT_COMPRESSED_APEX= को true पर सेट करके, किसी बिल्ड को APEX को कंप्रेस करने के लिए मजबूर किया जा सकता है.

बिल्ड सिस्टम से जनरेट की गई कंप्रेस की गई APEX फ़ाइलों में .capex एक्सटेंशन होता है. इस एक्सटेंशन की मदद से, APEX फ़ाइल के कंप्रेस किए गए और कंप्रेस न किए गए वर्शन के बीच अंतर करना आसान हो जाता है.

कंप्रेशन के लिए इस्तेमाल किए जा सकने वाले एल्गोरिदम

Android 12 में सिर्फ़ डिफ़्लेट-ज़िप कंप्रेशन काम करता है.

बूट के दौरान कंप्रेस की गई APEX फ़ाइल को चालू करना

कंप्रेस किए गए किसी APEX को चालू करने से पहले, उसके अंदर मौजूद original_apex फ़ाइल को /data/apex/decompressed डायरेक्ट्री में डीकंप्रेस किया जाता है. डीकंप्रेस की गई APEX फ़ाइल, /data/apex/active डायरेक्ट्री से हार्ड-लिंक होती है.

ऊपर बताई गई प्रोसेस को समझने के लिए, यहां दिया गया उदाहरण देखें.

/system/apex/com.android.foo.capex को कंप्रेस किया गया APEX माना जाता है. इसे चालू किया गया है और इसका versionCode 37 है.

  1. /system/apex/com.android.foo.capex में मौजूद original_apex फ़ाइल को /data/apex/decompressed/com.android.foo@37.apex में अनकंप्रेस किया जाता है.
  2. restorecon /data/apex/decompressed/com.android.foo@37.apex किया जाता है, ताकि यह पुष्टि की जा सके कि इसमें सही SELinux लेबल है.
  3. /data/apex/decompressed/com.android.foo@37.apex की वैधता की पुष्टि करने के लिए, इस पर पुष्टि करने की जांच की जाती है: apexd में बंडल की गई सार्वजनिक कुंजी की जांच करता है, ताकि यह पुष्टि की जा सके कि यह /system/apex/com.android.foo.capex में बंडल की गई सार्वजनिक कुंजी के बराबर है./data/apex/decompressed/com.android.foo@37.apex
  4. /data/apex/decompressed/com.android.foo@37.apex फ़ाइल, /data/apex/active/com.android.foo@37.apex डायरेक्ट्री से हार्ड-लिंक की गई है.
  5. कंप्रेस न की गई APEX फ़ाइलों को नियमित तौर पर चालू करने की प्रोसेस, /data/apex/active/com.android.foo@37.apex पर की जाती है.

ओटीए के साथ इंटरैक्शन

कंप्रेस की गई APEX फ़ाइलों का असर, ओटीए डिलीवरी और ऐप्लिकेशन पर पड़ता है. ऐसा हो सकता है कि ओटीए अपडेट में, कंप्रेस की गई APEX फ़ाइल हो. इसका वर्शन लेवल, डिवाइस पर मौजूद फ़ाइल के वर्शन लेवल से ज़्यादा हो. इसलिए, ओटीए अपडेट लागू करने के लिए, डिवाइस को रीबूट करने से पहले, कुछ खाली जगह रिज़र्व करनी होगी.

OTA सिस्टम के साथ काम करने के लिए, apexd इन दो बाइंडर एपीआई को दिखाता है:

  • calculateSizeForCompressedApex - OTA पैकेज में APEX फ़ाइलों को डीकंप्रेस करने के लिए ज़रूरी साइज़ का हिसाब लगाता है. इसका इस्तेमाल यह पुष्टि करने के लिए किया जा सकता है कि किसी डिवाइस में ओटीए डाउनलोड करने के लिए, ज़रूरी जगह है या नहीं.
  • reserveSpaceForCompressedApex - यह डिस्क पर जगह रिज़र्व करता है, ताकि apexd इसका इस्तेमाल कर सके. ऐसा, ओटीए पैकेज में मौजूद कंप्रेस की गई APEX फ़ाइलों को डीकंप्रेस करने के लिए किया जाता है.

A/B OTA अपडेट के मामले में, apexd इंस्टॉल होने के बाद OTA रूटीन के हिस्से के तौर पर, बैकग्राउंड में डीकंप्रेशन की कोशिश करता है. अगर डीकंप्रेशन नहीं हो पाता है, तो apexd बूट के दौरान डीकंप्रेशन करता है. इससे ओटीए अपडेट लागू होता है.

APEX बनाते समय जिन विकल्पों पर विचार किया गया

APEX फ़ाइल फ़ॉर्मैट डिज़ाइन करते समय, AOSP ने इन विकल्पों पर विचार किया था. साथ ही, यह भी बताया गया है कि इन्हें शामिल क्यों किया गया या क्यों नहीं किया गया.

पैकेज मैनेजमेंट के सामान्य सिस्टम

Linux डिस्ट्रिब्यूशन में पैकेज मैनेजमेंट सिस्टम होते हैं. जैसे, dpkg और rpm. ये सिस्टम, बेहतर तरीके से काम करते हैं और भरोसेमंद होते हैं. हालांकि, इन्हें APEX के लिए इस्तेमाल नहीं किया गया, क्योंकि ये इंस्टॉलेशन के बाद पैकेज की सुरक्षा नहीं कर सकते. पुष्टि सिर्फ़ तब की जाती है, जब पैकेज इंस्टॉल किए जा रहे हों. हमलावर, इंस्टॉल किए गए पैकेज की सुरक्षा को बिना किसी को पता चले तोड़ सकते हैं. यह Android के लिए एक रिग्रेशन है, जहां सभी सिस्टम कॉम्पोनेंट को रीड-ओनली फ़ाइल सिस्टम में सेव किया गया था. इनकी अखंडता की सुरक्षा, हर I/O के लिए dm-verity से की जाती है. सिस्टम कॉम्पोनेंट में किसी भी तरह की छेड़छाड़ को रोका जाना चाहिए. अगर ऐसा नहीं किया जा सकता, तो छेड़छाड़ का पता लगाया जाना चाहिए, ताकि डिवाइस के साथ छेड़छाड़ होने पर उसे बूट होने से रोका जा सके.

dm-crypt for integrity

APEX कंटेनर में मौजूद फ़ाइलें, बिल्ट-इन पार्टीशन (उदाहरण के लिए, /system पार्टीशन) से होती हैं. इन्हें dm-verity से सुरक्षित किया जाता है. इसमें पार्टीशन माउंट होने के बाद भी, फ़ाइलों में किसी तरह का बदलाव करने की अनुमति नहीं होती. फ़ाइलों को एक जैसी सुरक्षा देने के लिए, APEX में मौजूद सभी फ़ाइलों को फ़ाइल सिस्टम इमेज में सेव किया जाता है. इसे हैश ट्री और vbmeta डिस्क्रिप्टर के साथ जोड़ा जाता है. dm-verity के बिना, /data पार्टीशन में मौजूद APEX में अनचाहे बदलाव किए जा सकते हैं. ये बदलाव, APEX की पुष्टि होने और उसे इंस्टॉल किए जाने के बाद किए जाते हैं.

दरअसल, /data पार्टिशन को भी dm-crypt जैसे एन्क्रिप्शन लेयर से सुरक्षित किया जाता है. इससे डेटा में छेड़छाड़ होने से कुछ हद तक सुरक्षा मिलती है. हालांकि, इसका मुख्य मकसद निजता की सुरक्षा करना है, न कि डेटा की अखंडता बनाए रखना. जब हमलावर को /data पार्टीशन का ऐक्सेस मिल जाता है, तो कोई सुरक्षा नहीं होती. साथ ही, यह /system पार्टीशन में मौजूद हर सिस्टम कॉम्पोनेंट की तुलना में एक रिग्रेशन है. APEX फ़ाइल में मौजूद हैश ट्री और dm-verity, कॉन्टेंट को एक ही लेवल की सुरक्षा देते हैं.

/system से /apex पर रीडायरेक्ट करने के पाथ

APEX में पैकेज की गई सिस्टम कॉम्पोनेंट फ़ाइलों को नए पाथ के ज़रिए ऐक्सेस किया जा सकता है. जैसे, /apex/<name>/lib/libfoo.so. जब फ़ाइलें /system पार्टीशन का हिस्सा थीं, तब उन्हें /system/lib/libfoo.so जैसे पाथ से ऐक्सेस किया जा सकता था. एपीईएक्स फ़ाइल के क्लाइंट (अन्य एपीईएक्स फ़ाइलें या प्लैटफ़ॉर्म) को नए पाथ का इस्तेमाल करना होगा. पाथ में बदलाव होने की वजह से, आपको मौजूदा कोड अपडेट करना पड़ सकता है.

पाथ में बदलाव से बचने का एक तरीका यह है कि APEX फ़ाइल में मौजूद फ़ाइल के कॉन्टेंट को /system पार्टीशन पर ओवरले किया जाए. हालांकि, Android टीम ने /system पार्टीशन पर फ़ाइलों को ओवरले न करने का फ़ैसला किया है. इसकी वजह यह है कि ओवरले की जा रही फ़ाइलों की संख्या बढ़ने से, परफ़ॉर्मेंस पर असर पड़ सकता है. ऐसा भी हो सकता है कि फ़ाइलों को एक के बाद एक करके स्टैक किया जा रहा हो.

दूसरा विकल्प, फ़ाइल ऐक्सेस करने वाले फ़ंक्शन जैसे कि open, stat, और readlink को हाइजैक करना था, ताकि /system से शुरू होने वाले पाथ को /apex के तहत उनके संबंधित पाथ पर रीडायरेक्ट किया जा सके. Android टीम ने इस विकल्प को खारिज कर दिया है, क्योंकि पाथ स्वीकार करने वाले सभी फ़ंक्शन को बदलना मुमकिन नहीं है. उदाहरण के लिए, कुछ ऐप्लिकेशन Bionic को स्टैटिक तौर पर लिंक करते हैं, जो फ़ंक्शन लागू करता है. ऐसे मामलों में, उन ऐप्लिकेशन को रीडायरेक्ट नहीं किया जाता.