تنسيق ملف APEX

تم تقديم تنسيق الحاوية Android Pony EXpress ‏ (APEX) في الإصدار 10 من Android، ويتم استخدامه في عملية تثبيت وحدات النظام ذات المستوى الأدنى. يسهّل هذا التنسيق تحديثات مكوّنات النظام التي لا تتوافق مع نموذج تطبيق Android العادي. تشمل بعض الأمثلة على المكوّنات الخدمات والمكتبات المدمجة، وطبقات تجريد الأجهزة (HALs)، ووقت التشغيل (ART)، ومكاتب الفئات.

يمكن أن يشير مصطلح APEX أيضًا إلى ملف APEX.

خلفية

على الرغم من أنّ نظام التشغيل Android يتيح تحديث الوحدات التي تتوافق مع نموذج التطبيق العادي (مثل الخدمات والأنشطة) من خلال تطبيقات مثبّتي الحِزم (مثل تطبيق "متجر Google Play")، فإنّ استخدام نموذج مماثل لمكوّنات النظام الأساسية يؤدي إلى العيوب التالية:

  • لا يمكن استخدام الوحدات المستندة إلى حِزم APK في وقت مبكر من تسلسل التشغيل. مدير الحِزم هو المستودع المركزي للمعلومات عن التطبيقات ولا يمكن بدؤه إلا من مدير الأنشطة الذي يصبح جاهزًا في مرحلة لاحقة من عملية التمهيد.
  • تم تصميم تنسيق APK (وخاصة ملف البيان) لتطبيقات Android، ولا تتوافق وحدات النظام معه دائمًا.

التصميم

يصف هذا القسم التصميم العام لتنسيق ملف APEX و مدير APEX، وهو خدمة تدير ملفات APEX.

لمزيد من المعلومات عن سبب اختيار هذا التصميم لبرنامج APEX، يُرجى الاطّلاع على البدائل التي تمّ النظر فيها عند تطوير برنامج APEX.

تنسيق APEX

هذا هو تنسيق ملف APEX.

تنسيق ملف APEX

الشكل 1: تنسيق ملف APEX

في المستوى الأعلى، يكون ملف APEX هو ملف zip يتم تخزين الملفات فيه بدون ضغط وتقع في حدود 4 كيلوبايت.

الملفات الأربعة في ملف APEX هي:

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

يحتوي ملف apex_manifest.json على اسم الحزمة وإصدارها، ما يحدّد ملف APEX. هذا هو ملف جدول بروتوكول ApexManifest بتنسيق JSON.

يسمح ملف AndroidManifest.xml لملف APEX باستخدام الأدوات والبنية الأساسية ذات الصلة بحِزم APK، مثل أداة ADB وPackageManager وتطبيقات تثبيت الحِزم (مثل متجر Play). على سبيل المثال، يمكن أن يستخدم ملف APEX أداة حالية مثل aapt لفحص البيانات الوصفية الأساسية من الملف. يحتوي الملف على اسم الحزمة ومعلومات الإصدار. تتوفّر هذه المعلومات بشكل عام أيضًا في apex_manifest.json.

يُنصح باستخدام apex_manifest.json بدلاً من AndroidManifest.xml للرموز البرمجية الجديدة والأنظمة التي تتعامل مع APEX. AndroidManifest.xml قد تحتوي على معلومات إضافية حول الاستهداف يمكن استخدامها من خلال أدوات نشر التطبيقات الحالية.

apex_payload.img هي صورة نظام ملفات ext4 مدعومة ببرنامج dm-verity. يتم تركيب الصورة أثناء التشغيل من خلال جهاز loopback. على وجه التحديد، يتم إنشاء شجرة التجزئة و كتلة البيانات الوصفية باستخدام مكتبة libavb. لا يتم تحليل الحمولة في نظام الملفات (لأنّه يجب أن تكون الصورة قابلة للتركيب في مكانها). يتم تضمين الملفات العادية داخل ملف apex_payload.img.

apex_pubkey هو المفتاح العام المستخدَم لتوقيع صورة نظام الملفات. أثناء التشغيل، يضمن هذا المفتاح توقيع حزمة APEX التي تم تنزيلها باستخدام الكيان نفسه الذي يوقع حزمة APEX نفسها في الأقسام المضمّنة.

إرشادات تسمية APEX

للمساعدة في منع حدوث تعارضات في الأسماء بين عناوين APEX الجديدة مع تطوّر المنصة، يُرجى اتّباع إرشادات التسمية التالية:

  • com.android.*
    • محجوزة لتطبيقات APEX في AOSP لا يكون فريدًا لأي شركة أو جهاز.
  • 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. يُنشئ جهاز loopback من ملف APEX.
    3. ينشئ جهازًا لوحدة تحكُّم في الأجهزة فوق جهاز إعادة التوجيه.
    4. تركيب جهاز حظر أداة ربط الأجهزة على مسار فريد (على سبيل المثال، /apex/name@ver)

عند ربط جميع ملفات APEX المدرَجة في قاعدة البيانات الداخلية، يقدّم مدير APEX خدمة ربط لمكوّنات النظام الأخرى من أجل طلب معلومات عن ملفات APEX المثبّتة. على سبيل المثال، يمكن لمكونات النظام الأخرى البحث في قائمة ملفات APEX المثبَّتة على الجهاز أو البحث في المسار الدقيق الذي تم تثبيت حزمة APEX عليه، حتى يمكن الوصول إلى الملفات.

ملفات APEX هي حِزم APK

ملفات APEX هي ملفات APK صالحة لأنّها أرشيفات ZIP موقَّعة (باستخدام نظام توقيع APK) تحتوي على ملف AndroidManifest.xml. يسمح ذلك لملفات APEX باستخدام البنية الأساسية لملفات APK، مثل تطبيق مُثبِّت الحِزم، وأداة التوقيع، ومدير الحِزم.

ملف AndroidManifest.xml داخل ملف APEX هو ملف صغير يتألّف من الحزمة name وversionCode وtargetSdkVersion وminSdkVersion وmaxSdkVersion الاختيارية وmaxSdkVersion لاستهداف دقيق. تسمح هذه المعلومات بتسليم ملفات APEX من خلال القنوات الحالية، مثل تطبيقات أدوات تثبيت الحِزم و IDE IDE.

أنواع الملفات المتوافقة

يتوافق تنسيق APEX مع أنواع الملفات التالية:

  • المكتبات المشتركة الأصلية
  • ملفات التشغيل الأصلية
  • ملفات JAR
  • ملفات البيانات
  • ملفات الإعداد

ولا يعني ذلك أنّه يمكن لخدمة APEX تحديث جميع أنواع الملفات هذه. يعتمد ما إذا كان يمكن تعديل نوع الملف على النظام الأساسي ومدى ثبات تعريفات واجهات أنواع الملفات.

خيارات التوقيع

يتم توقيع ملفات APEX بطريقتَين. أولاً، يتم توقيع ملف apex_payload.img (على وجه التحديد، وصف vbmeta المُرفَق بملف apex_payload.img) باستخدام مفتاح. بعد ذلك، يتم توقيع حزمة APEX بالكامل باستخدام الإصدار 3 من مخطّط توقيع حِزم APK. يتم استخدام مفتاحَين مختلفَين في هذه العملية.

على جانب الجهاز، يتم تثبيت مفتاح عام يتوافق مع المفتاح الخاص المستخدَم لتوقيع وصف vbmeta. يستخدم مدير APEX المفتاح العام للتحقّق من صحة وحدات APEX المطلوب تثبيتها. يجب توقيع كل ملف APEX باستخدام مفاتيح مختلفة، ويتم فرض ذلك في وقت الإنشاء ووقت التشغيل.

APEX في الأقسام المضمّنة

يمكن العثور على ملفات APEX في أقسام مضمّنة مثل /system. تم تثبيت القسم بالفعل على dm-verity، لذا يتم تثبيت ملفات APEX مباشرةً على جهاز loopback.

إذا كانت حزمة APEX متوفّرة في قسم مضمّن، يمكن تعديل حزمة APEX من خلال توفير حزمة APEX تحمل اسم الحزمة نفسه ورمز إصدار أكبر أو يساوي رمز الإصدار. يتم تخزين حزمة APEX الجديدة في /data، ومثل حِزم APK، يؤدي الإصدار المُثبَّت حديثًا إلى حجب الإصدار المُتاح حاليًا في القسم المُدمَج. ولكن على عكس حِزم APK، لا يتم تفعيل الإصدار المثبَّت حديثًا من حِزمة APEX إلا بعد إعادة التشغيل.

متطلبات النواة

لتفعيل وحدات APEX الرئيسية على جهاز Android، يجب توفُّر ميزات Linux kernel التالية: برنامج تشغيل loopback وdm-verity. يُثبِّت برنامج تشغيل loopback صورة نظام الملفات في وحدة APEX ويتحقّق dm-verity من وحدة APEX.

يُعدّ أداء برنامج تشغيل loopback وdm-verity مهمًا لتحقيق أداء جيد للنظام عند استخدام وحدات APEX.

إصدارات النواة المتوافقة

تتوفّر وحدات APEX الرئيسية على الأجهزة التي تستخدم إصدارات 4.4 من kernel أو إصدارات أحدث. بالنسبة إلى الأجهزة الجديدة التي تعمل بالإصدار 10 من نظام التشغيل Android أو إصدار أحدث، يجب استخدام الإصدار 4.9 من kernel أو إصدار أحدث لتتوافق مع وحدات APEX.

تصحيحات kernel المطلوبة

يتم تضمين تصحيحات النواة المطلوبة لتفعيل وحدات APEX في شجرة Android الشائعة. للحصول على الرموز البرمجية الإضافية التي تتيح استخدام APEX، استخدِم أحدث إصدار من شجرة Android الشائعة.

إصدار النواة 4.4

لا يتوفّر هذا الإصدار إلا للأجهزة التي تمت ترقيتها من Android 9 إلى Android 10 وتريد إتاحة وحدات APEX. للحصول على التصحيحات المطلوبة، ننصح بشدة بإجراء دمج للأسفل من فرع android-4.4. في ما يلي قائمة بالإصلاحات الفردية المطلوبة لإصدار kernel 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)
  • التراجع عن "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

للحصول على الرقع المطلوبة لإصدارات kernel 4.9/4.14/4.19، عليك دمج الإصدارات القديمة من فرع android-common.

خيارات ضبط النواة المطلوبة

تعرض القائمة التالية متطلبات الإعداد الأساسية للسماح باستخدام وحدات APEX التي تم طرحها في Android 10. إنّ العناصر التي تحمل علامة النجمة (*) هي متطلبات حالية من الإصدار 9 من Android والإصدارات الأقدم.

(*) 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

متطلبات مَعلمات سطر أوامر kernel

لتفعيل APEX، تأكَّد من استيفاء مَعلمات سطر الأوامر للنواة لل requirements التالية:

  • يجب عدم ضبط loop.max_loop.
  • يجب أن يكون loop.max_part أقل من أو يساوي 8

إنشاء نموذج APEX

يوضّح هذا القسم كيفية إنشاء حزمة APEX باستخدام نظام إنشاء تطبيقات Android. في ما يلي مثال على Android.bp لنقطة اتصال APEX باسم apex.test.

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 (/lib/arm لمعالج ARM المُترجَم في x86)
الملفات التنفيذية /bin
مكتبات Java /javalib
الأجهزة المُجمَّعة مسبقًا /etc

التبعيات الانتقالية

تتضمّن ملفات APEX تلقائيًا التبعيات الانتقالية للمكتبات المشترَكة الأصلية أو الملفات التنفيذية. على سبيل المثال، إذا كانت libFoo تعتمد على libBar، يتم تضمين المكتبتَين عند إدراج libFoo فقط في موقع native_shared_libs.

التعامل مع واجهات ABI متعددة

ثبِّت سمة native_shared_libs لكلٍّ من واجهات التطبيق الثنائية (ABI) الأساسية والثانوية للجهاز. إذا كان حِزم APEX تستهدف الأجهزة التي تستخدم IDE واحدة (أي 32 بت فقط أو 64 بت فقط)، لا يتم تثبيت سوى المكتبات التي تستخدم IDE المقابلة.

ثبِّت موقع binaries لواجهة ABI الأساسية للجهاز فقط على النحو الموضح أدناه:

  • إذا كان الجهاز يعمل بنظام 32 بت فقط، لن يتم تثبيت سوى الإصدار 32 بت من الملف الثنائي.
  • إذا كان الجهاز يعمل بنظام 64 بت فقط، سيتم تثبيت الإصدار 64 بت من الملف الثنائي فقط.

لإضافة تحكم دقيق في واجهات برمجة التطبيقات لنظام التشغيل (ABI) للمكتبات الأصلية والملفات الثنائية، استخدِم سمات multilib.[first|lib32|lib64|prefer32|both].[native_shared_libs|binaries].

  • first: يتطابق مع ABI الأساسي للجهاز. هذا هو الإعداد التلقائي لملفّات ملفّات برمجية ثنائية.
  • lib32: يتطابق مع معيار ABI لنظام التشغيل 32 بت على الجهاز، في حال توفّره.
  • lib64: يتطابق مع ABI‏ 64 بت للجهاز، وهو متوافق.
  • prefer32: يتطابق مع معيار ABI لنظام التشغيل 32 بت على الجهاز، في حال توفّره. إذا كانت واجهة برمجة التطبيقات (ABI) المتوافقة مع 32 بت غير متاحة، يجب أن تتطابق مع واجهة برمجة التطبيقات المتوافقة مع 64 بت.
  • both: تتطابق مع كلتا قيمتَي ABI. هذه هي القيمة التلقائية لحالة native_shared_libraries.

لا تعتمد سمات java وlibraries وprebuilts على معرّف ABI.

هذا المثال لجهاز متوافق مع 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. استخدِم السمة key ل signing the APEX باستخدام المفتاح. يتم تضمين المفتاح العام تلقائيًا في APEX بالاسم avb_pubkey.

# 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 باستخدام الشهادة التي تحدّدها ملفّة ‎"Soong" باسم <name>. يمكن تعريف وحدة الشهادة على النحو التالي:
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

لتثبيت حزمة APEX، استخدِم أداة ADB.

adb install apex_file_name
adb reboot

إذا تم ضبط supportsRebootlessUpdate على true في apex_manifest.json ولم يكن APEX المثبَّت حاليًا قيد الاستخدام (على سبيل المثال، تم stopped أي خدمات يحتوي عليها)، يمكن تثبيت APEX جديد بدون إعادة تشغيل باستخدام العلامة --force-non-staged.

adb install --force-non-staged apex_file_name

استخدام APEX

بعد إعادة التشغيل، يتم تثبيت APEX في الدليل /apex/<apex_name>@<version>. يمكن تركيب عدة إصدارات من APEX نفسه في الوقت نفسه. من بين مسارات التثبيت، يكون المسار الذي يتوافق مع أحدث إصدار هو التثبيت باستخدام الربط في /apex/<apex_name>.

يمكن للعملاء استخدام المسار المثبَّت بالربط لقراءة الملفات أو تنفيذها من APEX.

يتم استخدام نطاقات APEX عادةً على النحو التالي:

  1. يُحمِّل المصنّع الأصلي للجهاز أو المصنّع المحدّد للمنتج مسبقًا حزمة APEX التي تقلّ مساحتها عن /system/apex عند شحن الجهاز.
  2. يتم الوصول إلى الملفات في APEX من خلال مسار /apex/<apex_name>/.
  3. عند تثبيت إصدار مُحدَّث من حزمة APEX في /data/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
    

لا يمكن تحديد تعريفات الخدمات إلا في ملف .rc الخاص بملف APEX. لا تتوفّر عوامل التفعيل الإجراء في قواعد بيانات APEX.

إذا بدأت خدمة تم وضع علامة عليها على أنّها قابلة للتحديث قبل تفعيل وحدات APEX، يتم تأجيل البدء إلى أن يكتمل تفعيل وحدات APEX.

ضبط النظام لتتوافق مع تحديثات APEX

اضبط سمة النظام التالية على true لتفعيل تعديلات ملفات APEX.

<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)

APEX المُبسَّط

في الأجهزة القديمة، قد يكون من المستحيل أو غير الممكن أحيانًا تحديث ملف التمهيد القديم ليتيح استخدام APEX بالكامل. على سبيل المثال، قد تم إنشاء kernel بدون CONFIG_BLK_DEV_LOOP=Y، وهو أمر مهم لتركيب ملف ‎VMIMG داخل APEX.

حزمة APEX المسطّحة هي حزمة APEX مصمّمة خصيصًا ويمكن تفعيلها على الأجهزة التي تعمل بنظام تشغيل قديم. يتم تثبيت الملفات في ملف APEX مسطّح مباشرةً في دليل ضمن القسم المضمّن. على سبيل المثال، lib/libFoo.so في ملف APEX مسطّح my.apex تم تثبيته على /system/apex/my.apex/lib/libFoo.so.

لا يتطلّب تفعيل APEX المسطّح استخدام جهاز حلقة. يتم ربط directory /system/apex/my.apex بالكامل مباشرةً بـ /apex/name@ver.

لا يمكن تعديل ملفات APEX المسطّحة من خلال تنزيل إصدارات معدَّلة من ملفات APEX من الشبكة لأنّه لا يمكن تسطيح ملفات APEX التي تم تنزيلها. لا يمكن تعديل نطاقات APEX المسطّحة إلا من خلال عملية OTA عادية.

ويكون "النموذج المسطّح للنقطة العليا للصفحة" هو الإعداد التلقائي. وهذا يعني أنّه يتم تسطيح كل ملفات APEX تلقائيًا ما لم تضبط جهازك بوضوح لإنشاء ملفات APEX غير مسطّحة لتتوافق مع تحديثات APEX (كما هو موضّح أعلاه).

لا يُسمح بشدَّة بمزج تصاميم APEX المسطّحة وغير المسطّحة في جهاز معيّن. يجب أن تكون نقاط APEX في الجهاز إما غير مسطّحة أو مسطّحة بالكامل. ويُعدّ ذلك مهمًا بشكل خاص عند شحن الإصدارات المُعدّة مسبقًا من APEX الموقَّعة مسبقًا لمشاريع مثل Mainline. يجب أيضًا عدم تسطيح ملفات APEX التي لم يتم توقيعها مسبقًا (أي التي تم إنشاؤها من المصدر) وتوقيعها باستخدام مفاتيح صحيحة. يجب أن يرث الجهاز من updatable_apex.mk كما هو موضّح في مقالة تعديل خدمة باستخدام APEX.

مجموعات APEX المضغوطة

يتضمّن نظام التشغيل Android 12 والإصدارات الأحدث ميزة ضغط APEX بهدف تقليل تأثير حِزم APEX القابلة للتحديث في مساحة التخزين. بعد تثبيت تحديث لتطبيق APEX، يظلّ التطبيق يشغل المساحة نفسها، على الرغم من أنّه لم يعُد يتم استخدام الإصدار المثبَّت مسبقًا. تظل المساحة المُشغَّلة غير متاحة.

يقلل ضغط APEX من تأثير مساحة التخزين هذا باستخدام مجموعة مضغوطة للغاية من ملفات APEX على الأقسام المخصّصة للقراءة فقط (مثل القسم /system). يستخدم الإصدار 12 من Android والإصدارات الأحدث خوارزمية ضغط ملفات ZIP من نوع DEFLATE.

لا يقدّم الضغط تحسينًا للعناصر التالية:

  • وحدات APEX لبدء التشغيل التي يجب تثبيتها في وقت مبكر جدًا من تسلسل التمهيد

  • حِزم APEX غير القابلة للتحديث لا يكون الضغط مفيدًا إلا إذا تم تثبيت إصدار مُحدَّث من APEX على قسم /data. تتوفّر قائمة كاملة بنقاط APEX القابلة للتحديث في صفحة مكونات النظام المُركّب.

  • مكتبات APEX المشترَكة الديناميكية بما أنّ apexd ينشط دائمًا كلا الإصدارَين من هذه الوحدات (المُثبَّتة مسبقًا والمُعدَّلة)، لا يضيف ضغطها أي قيمة.

تنسيق ملف APEX المضغوط

هذا هو تنسيق ملف APEX المضغوط.

مخطّط بياني يعرض تنسيق ملف APEX مضغوط

الشكل 2: تنسيق ملف 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 مضغوطة.

بالنسبة إلى التجارب المحلية، يمكنك فرض تجميع لضغط ملفات APEX من خلال ضبط OVERRIDE_PRODUCT_COMPRESSED_APEX= على true.

تحتوي ملفات APEX المضغوطة التي أنشأها نظام الإنشاء على امتداد .capex. يسهّل هذا التمييز بين الإصدارين المضغوَط وغير المضغوَط من ملف APEX.

خوارزميات الضغط المتوافقة

لا يتيح نظام التشغيل Android 12 سوى ضغط deflate-zip.

تفعيل ملف APEX مضغوط أثناء عملية التشغيل

قبل تفعيل ملف APEX مضغوط، تتم أولاً إزالة ضغط ملف original_apex المضمّن فيه ونقله إلى الدليل /data/apex/decompressed. يتم ربط ملف APEX الذي تم فك ضغطه برابط ثابت بالدليل /data/apex/active.

إليك المثال التالي لتوضيح العملية الموضّحة أعلاه.

يمكنك اعتبار /system/apex/com.android.foo.capex كملف APEX مضغوط يتم تفعيله، مع رمز الإصدار 37.

  1. يتم إزالة ضغط ملف original_apex داخل /system/apex/com.android.foo.capex ونقله إلى /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 من المفتاح العام المُضمّن في /data/apex/decompressed/com.android.foo@37.apex للتأكّد من أنّه مماثل للمفتاح المُضمّن في/system/apex/com.android.foo.capex.
  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.

التفاعل مع OTA

تؤثر ملفات APEX المضغوطة في عملية إرسالها عبر الهواء وتطبيقها. بما أنّه قد يحتوي التحديث عبر شبكة غير سلكية على ملف APEX مضغوط بمستوى إصدار أعلى من الإصدار النشط على الجهاز، يجب حجز مساحة تخزين فارغة معيّنة قبل إعادة تشغيل الجهاز لتطبيق تحديث عبر شبكة غير سلكية.

ليتيح نظام OTA، يعرِض apexd واجهات برمجة التطبيقات Binder API التالية:

  • calculateSizeForCompressedApex - تُحتسب هذه السمة الحجم المطلوب لفك ضغطملفات APEX في حزمة OTA. يمكن استخدام هذا الإجراء للتأكّد من توفّر مساحة تخزين كافية على الجهاز قبل تنزيل تحديث OTA.
  • reserveSpaceForCompressedApex - تحجز مساحة على القرص لاستخدامها في المستقبل من قِبل apexd لفك ضغط ملفات APEX المضغوطة داخل حزمة OTA.

في حال توفّر تحديث عبر الهواء (OTA) من النوع A/B، يحاول apexd إزالة الضغط في الخلفية كجزء من الإجراء العادي للتحديث عبر الهواء بعد التثبيت. إذا تعذّر فك الضغط، ينفِّذ apexd عملية فك الضغط أثناء عملية التمهيد التي تُطبِّق التحديث عبر الهواء.

البدائل التي تمّ أخذها في الاعتبار عند تطوير APEX

في ما يلي بعض الخيارات التي أخذها فريق AOSP في الاعتبار عند تصميم تنسيق ملف APEX، وسبب تضمينها أو استبعادها.

أنظمة إدارة الحِزم العادية

تتضمّن توزيعات Linux أنظمة لإدارة الحِزم، مثل dpkg وrpm، وهي أنظمة فعّالة وموثوقة وقوية. ومع ذلك، لم يتم اعتمادها في APEX لأنّها لا يمكنها حماية الحِزم بعد التثبيت. ولا يتم إجراء عملية التحقّق إلا عند تثبيت الحِزم. يمكن للمهاجمين إزالة سلامة الحِزم المثبَّتة بدون أن يلاحظها أحد. يُعدّ ذلك تراجعًا في نظام التشغيل Android حيث كان يتم تخزين جميع مكوّنات النظام في أنظمة ملفات قراءة فقط تتم حماية سلامتها من خلال dm-verity لكل عملية إدخال/إخراج. يجب أن يكون أي محاولة للتلاعب بمكونات النظام محظورة أو يمكن رصدها كي يتمكّن الجهاز من رفض التشغيل في حال اختراقه.

dm-crypt للحفاظ على السلامة

تكون الملفات في حاوية APEX من أقسام مدمجة (مثل القسم /system) محمية من خلال dm-verity، حيث يُحظر إجراء أي تعديل على الملفات حتى بعد تركيب الأقسام. لتوفير المستوى نفسه من الأمان للملفات، يتم تخزين جميع الملفات في ملف APEX في ملف صورة نظام مقترن بشجرة تجزئة ووصف vbmeta. بدون ملف ‎Dm-verity، يكون حِزمة APEX في القسم ‎/data عرضة للتعديلات العميقة غير المقصودة التي يتم إجراؤها بعد إثبات صحتها وتثبيتها.

في الواقع، يتم أيضًا حماية قسم /data من خلال طبقات التشفير، مثل dm-crypt. على الرغم من أنّ هذا يقدّم مستوى معيّنًا من الحماية ضد التلاعب، إلا أنّ الغرض الأساسي منه هو الخصوصية وليس السلامة. عندما يصبح المهاجم قادرًا على الوصول إلى القسم /data، لا يمكن توفير أي حماية إضافية، وهذا بدوره يمثّل تدهورًا مقارنةً بوضع كل مكوّنات النظام في القسم /system. توفّر شجرة التجزئة داخل ملف APEX مع dm-verity مستوى الحماية نفسه للمحتوى.

إعادة توجيه المسارات من ‎ /system إلى ‎ /apex

يمكن الوصول إلى ملفات مكوّنات النظام المُجمَّعة في حزمة APEX من خلال مسارات جديدة مثل /apex/<name>/lib/libfoo.so. عندما كانت الملفات جزءًا من القسم /system ، كان بالإمكان الوصول إليها من خلال مسارات مثل /system/lib/libfoo.so. يجب أن يستخدم العميل الذي يستخدم ملف APEX (ملفات APEX الأخرى أو المنصة) المسارات الجديدة. قد تحتاج إلى تعديل الرمز الحالي نتيجةً لتغيير المسار.

على الرغم من أنّ إحدى الطرق لتجنُّب تغيير المسار هي تراكب محتوى الملف فيملف APEX على القسم /system، قرّر فريق Android عدم تراكب الملفات على القسم /system لأنّ ذلك قد يؤثر في الأداء مع زيادة عدد الملفات التي يتم تداخلها (ربما يتم تجميعها واحدة تلو الأخرى).

كان الخيار الآخر هو الاستيلاء على دوال الوصول إلى الملفات، مثل open وstat و readlink، بحيث تتم إعادة توجيه المسارات التي تبدأ بـ /system إلى مساراتها المقابلة ضمن /apex. رفض فريق Android هذا الخيار لأنّه من غير الممكن تغيير جميع الدوال التي تقبل المسارات. على سبيل المثال، تربط بعض التطبيقات Bionic بشكل ثابت، وهو ما يؤدي إلى تنفيذ الوظائف. وفي هذه الحالات، لا تتم إعادة توجيه هذه التطبيقات.