تنسيق ملف APEX

تم طرح تنسيق الحاوية Android Pony EXpress (APEX) في نظام التشغيل Android 10، ويتم استخدامه في عملية تثبيت وحدات النظام ذات المستوى الأدنى. يسهّل هذا التنسيق تحديث مكوّنات النظام التي لا تتوافق مع نموذج تطبيقات Android العادي. تتضمّن بعض الأمثلة على المكوّنات الخدمات والمكتبات الأصلية، وطبقات تجريد الأجهزة (HAL)، ووقت التشغيل (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. يتم تركيب الصورة في وقت التشغيل من خلال جهاز حلقة وصل. على وجه التحديد، يتم إنشاء شجرة التجزئة وكتلة البيانات الوصفية باستخدام مكتبة libavb. لا يتم تحليل حمولة نظام الملفات (لأنّه من المفترض أن تكون الصورة قابلة للتحميل في مكانها). يتم تضمين الملفات العادية داخل الملف apex_payload.img.

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

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

للمساعدة في منع حدوث تعارضات في التسمية بين حِزم APEX الجديدة مع تطوّر النظام الأساسي، اتّبِع إرشادات التسمية التالية:

  • com.android.*
    • محجوز لحِزم APEX في مشروع Android المفتوح المصدر (AOSP). لا يكون فريدًا لأي شركة أو جهاز.
  • com.<companyname>.*
    • محجوزة لشركة يُحتمَل أن تكون قد استُخدمت من قِبل أجهزة متعددة تابعة لتلك الشركة.
  • com.<companyname>.<devicename>.*
    • محفوظة لحِزم APEX الفريدة لجهاز معيّن (أو مجموعة فرعية من الأجهزة).

مدير APEX

مدير APEX (أو apexd) هو عملية أصلية مستقلة مسؤولة عن التحقّق من ملفات APEX وتثبيتها وإلغاء تثبيتها. يتم تشغيل هذه العملية وتكون جاهزة في وقت مبكر من تسلسل بدء التشغيل. عادةً ما تكون ملفات APEX مثبّتة مسبقًا على الجهاز ضمن /system/apex. يستخدم مدير APEX تلقائيًا هذه الحِزم في حال عدم توفّر أي تحديثات.

يستخدم تسلسل التحديث لحزمة APEX فئة PackageManager ويكون على النحو التالي.

  1. يتم تنزيل ملف APEX من خلال تطبيق مثبِّت الحِزم أو أداة تصحيح الأخطاء في Android أو مصدر آخر.
  2. يبدأ مدير الحِزم إجراءات التثبيت. عندما يتعرّف مدير الحِزم على أنّ الملف هو APEX، ينقل التحكّم إلى مدير APEX.
  3. يتأكّد مدير APEX من صحة ملف APEX.
  4. في حال تم التحقّق من ملف APEX، يتم تعديل قاعدة البيانات الداخلية الخاصة بمدير APEX لتعكس أنّه سيتم تفعيل ملف APEX عند إعادة التشغيل التالية.
  5. يتلقّى مقدّم طلب التثبيت بثًا عند إتمام عملية التحقّق من الحزمة بنجاح.
  6. لمتابعة عملية التثبيت، يجب إعادة تشغيل النظام.
  7. عند إعادة التشغيل التالية، يبدأ مدير APEX، ويقرأ قاعدة البيانات الداخلية، وينفّذ ما يلي لكل ملف APEX مُدرَج:

    1. للتحقّق من ملف APEX.
    2. ينشئ جهازًا حلقيًا من ملف APEX.
    3. ينشئ هذا الأمر جهازًا لربط وحدات التخزين على جهاز loopback.
    4. يربط هذا الخيار جهاز حظر أداة ربط الأجهزة بمسار فريد (على سبيل المثال، /apex/name@ver).

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

ملفات APEX هي ملفات APK

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

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

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

يتوافق تنسيق 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. يعمل برنامج تشغيل حلقة الربط على تركيب صورة نظام الملفات في وحدة APEX، وتتحقّق أداة dm-verity من وحدة APEX.

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

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

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

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

يتم تضمين تصحيحات النواة المطلوبة لدعم وحدات 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 إلى التوابع من التوابع (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.

خيارات إعداد النواة المطلوبة

تعرض القائمة التالية متطلبات الإعداد الأساسي اللازمة لتوفير وحدات 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

متطلبات مَعلمات سطر الأوامر في النواة

لإتاحة استخدام 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
Prebuilts /etc

التبعيات المتعدية

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

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

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

ثبِّت السمة binaries لواجهة التطبيق الثنائية (ABI) الأساسية للجهاز فقط، كما هو موضّح أدناه:

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

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

  • first: يتطابق مع واجهة التطبيق الثنائية الأساسية للجهاز. هذا هو الإعداد التلقائي للملفات الثنائية.
  • lib32: تتطابق مع واجهة التطبيق الثنائية (ABI) ذات 32 بت للجهاز، إذا كانت متوافقة.
  • lib64: يتطابق مع واجهة التطبيق الثنائية (ABI) التي تعمل بنظام 64 بت على الجهاز.
  • prefer32: تتطابق مع واجهة التطبيق الثنائية (ABI) ذات 32 بت للجهاز، إذا كانت متوافقة. إذا كان ABI ذو 32 بت غير متوافق، يجب أن يتطابق مع ABI ذو 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 في الحزمة. في وقت التشغيل، تتحقّق 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، يجب استبدال جميع مفاتيح الاختبار بمفاتيح الإصدارات المقابلة قبل الإصدار العلني. يمكن لخوادم الإصدار الخاصة بمصنّعي المعدات الأصلية دمج أداة المضيف sign_target_files_apks، التي تعيد توقيع كل من صورة نظام الملفات وملف APEX بالكامل لجميع ملفات APEX التي تم العثور عليها في أرشيف ZIP الخاص بملفات الإصدار.

للحفاظ على الأمان، من الضروري الالتزام بأفضل الممارسات التالية في ما يتعلق بعمليات إدارة المفاتيح وتوقيع الإصدارات:

  • احتفِظ بمفاتيح الإصدار في بيئة آمنة للحدّ من إمكانية الوصول إليها.

  • يجب أن تتحكّم قائمة التحكّم بالوصول في بدء عمليات توقيع الإصدار.

  • لا توقِّع على العناصر إلا بمفتاح إصدار بعد اختبارها واعتمادها للإصدار.

  • يجب أن ينفّذ أحد المستخدمين إجراءات توقيع الإصدار، ولا يجب أن تتم هذه العملية تلقائيًا.

  • يجب تخزين العناصر الموقَّعة بمفتاح الإصدار في بيئة آمنة.

  • يجب حصر إمكانية الوصول إلى العناصر الموقَّعة بمفتاح الإصدار على الأسباب التجارية الصالحة.

  • يجب أن يحتفظ خادم الإصدار الخاص بمصنّع المعدات الأصلية بسجلّ لكل طلب توقيع في قاعدة بيانات التوقيع.

تثبيت حزمة APEX

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

adb install apex_file_name
adb reboot

إذا تم ضبط supportsRebootlessUpdate على true في apex_manifest.json وكان حِزمة APEX المثبَّتة حاليًا غير مستخدَمة (على سبيل المثال، تم إيقاف أي خدمات تحتوي عليها)، يمكن تثبيت حِزمة 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. لا تتوفّر مشغّلات الإجراءات في APEXes.

إذا بدأت خدمة مصنّفة على أنّها قابلة للتحديث قبل تفعيل حِزم 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)

Flattened APEX

في الأجهزة القديمة، يتعذّر أحيانًا أو يكون من غير العملي تحديث النواة القديمة لتتوافق تمامًا مع APEX. على سبيل المثال، قد يكون النواة قد تم إنشاؤها بدون CONFIG_BLK_DEV_LOOP=Y، وهو أمر بالغ الأهمية لتركيب صورة نظام الملفات داخل حزمة APEX.

حزمة 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 المسطّحة إلا من خلال تحديث عادي عبر الأثير (OTA).

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

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

حِزم APEX المضغوطة

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

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

لا يوفّر الضغط تحسينًا لما يلي:

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

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

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

تنسيق ملف 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.

التفاعل مع وكالة السفر على الإنترنت

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

لتوفير نظام تحديث البرامج عبر الأثير، يعرض apexd واجهتَي برمجة تطبيقات Binder التاليتَين:

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

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

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

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

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

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

dm-crypt for integrity

تكون الملفات في حاوية 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 بشكل ثابت، ما يؤدي إلى تنفيذ الوظائف. في مثل هذه الحالات، لا تتم إعادة توجيه هذه التطبيقات.