تنسيق ملف 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.*
    • محجوزة لـ AOSP APEXes. لا يكون فريدًا لأي شركة أو جهاز.
  • com.<companyname>.*
    • محجوز لشركة. من المحتمل أن يتم استخدامها من خلال أجهزة متعددة من هذه الشركة.
  • com.<companyname>.<devicename>.*
    • محجوزة لحِزم APK الفريدة لجهاز معيّن (أو مجموعة فرعية من الأجهزة).

مدير 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) تحتوي على ملف AndroidManifest.xml. ويتيح ذلك لملفات APEX استخدام البنية الأساسية لملفات APK، مثل تطبيق أداة تثبيت الحزمة وأداة التوقيع ومدير الحزم.

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

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

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

  • libs المشتركة الأصلية
  • ملفات تنفيذية أصلية
  • ملفات 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 إلا بعد إعادة التشغيل.

متطلبات النواة (Kernel)

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

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

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

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

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

يتم تضمين تصحيحات النواة المطلوبة لتفعيل وحدات 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: إصلاح next_descendent (4.4)
  • ANDROID: mnt: يجب أن يتم نشر إعادة التحميل على عبيد العبيد (4.4)
  • ANDROID: mnt: نشر إعادة التحميل بشكل صحيح (4.4)
  • إلغاء التغييرات الأخيرة على "ANDROID: dm verity: إضافة الحد الأدنى لحجم الجلب المسبق" (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.

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

تعرض القائمة التالية متطلبات الإعداد الأساسية للسماح باستخدام وحدات APEX التي تم طرحها في Android 10. العناصر التي تحمل علامة النجمة (*) هي متطلبات حالية من 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

متطلبات مَعلمات سطر أوامر النواة (Kernel)

لإتاحة واجهة برمجة التطبيقات APEX، يجب التأكُّد من أنّ معلَمات سطر الأوامر في النواة تستوفي المتطلبات التالية:

  • يجب عدم ضبط 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 تلقائيًا ملحقات متبادلة لملفات libs المشتركة الأصلية أو الملفات التنفيذية. على سبيل المثال، إذا كانت 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 لتوقيع ملف 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 المثبَّت حاليًا (على سبيل المثال، تم إيقاف أي خدمات مضمّنة فيه)، يمكن تثبيت واجهة برمجة تطبيقات جديدة بدون إعادة تشغيل باستخدام علامة --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 مسطّح مباشرةً في دليل ضمن القسم المضمّن. على سبيل المثال، يتم تثبيت lib/libFoo.so في واجهة APEX مسطَّحة my.apex في /system/apex/my.apex/lib/libFoo.so.

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

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

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

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

ملفات APEX المضغوطة

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

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

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

  • مكونات واجهة برمجة التطبيقات (APEX) لتمهيد التشغيل المطلوب تثبيتها في وقت مبكر جدًا من تسلسل التشغيل.

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

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

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

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

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

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

لا يتوافق Android 12 إلا مع ميزة "الضغط المضغوط"

تفعيل ملف 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: تحسب الحجم المطلوب لفك ضغط ملفات APK في حزمة عبر الهواء. ويمكن استخدام ذلك للتحقق من أن الجهاز به مساحة كافية قبل تنزيل التحديث عبر الهواء.
  • reserveSpaceForCompressedApex: يحتفظ بمساحة على القرص لاستخدامها في المستقبل من خلال apexd لفك ضغط ملفات APEX المضغوطة داخل حزمة 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 بشكل ثابت، والتي تنفذ الدوال. وفي مثل هذه الحالات، لا تتم إعادة توجيه هذه التطبيقات.