APEX الخاص بالمورّد

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

يثبِّت نظام الإنشاء حِزم APEX الخاصة بالمورّدين تلقائيًا في القسم /vendor ويتم تفعيلها في وقت التشغيل من خلال apexd تمامًا مثل حِزم APEX في الأقسام الأخرى.

حالات الاستخدام

تقسيم صور البائعين إلى وحدات

تسهّل حِزم APEX عملية تجميع وتنظيم عمليات تنفيذ الميزات في وحدات على صور البائعين.

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

على سبيل المثال، يمكن للمصنّع الأصلي للجهاز اختيار إنشاء الجهاز باستخدام حزمة APEX لتنفيذ شبكة Wi-Fi في مشروع AOSP، وحزمة APEX لتنفيذ البلوتوث في نظام على شريحة، وحزمة APEX مخصّصة لتنفيذ خدمات الاتصال لدى المصنّع الأصلي للجهاز.

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

تكرار المطوّر

تساعد حِزم APEX الخاصة بالمورِّدين المطوّرين على تكرار عملية تطوير وحدات المورِّدين بشكل أسرع من خلال تجميع عملية تنفيذ ميزة كاملة، مثل طبقة HAL لشبكة Wi-Fi، داخل حزمة APEX الخاصة بالمورِّد. ويمكن للمطوّرين بعد ذلك إنشاء حزمة APEX الخاصة بالمورّد ودفعها بشكل فردي لاختبار التغييرات، بدلاً من إعادة إنشاء صورة المورّد بأكملها.

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

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

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

مثال على سير العمل:

# Build the entire device and flash. OR, obtain an already-flashed device.
source build/envsetup.sh && lunch oem_device-userdebug
m
fastboot flashall -w

# Test the device.
... testing ...

# Check previous behavior using a vendor APEX from one week ago, downloaded from
# your continuous integration build.
... download command ...
adb install <path to downloaded APEX>
adb reboot
... testing ...

# Edit and rebuild just the APEX to change and test behavior.
... edit APEX source contents ...
m <apex module name>
adb install out/<path to built APEX>
adb reboot
... testing ...

أمثلة

الأساسيات

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

في Android.bp، يؤدي ضبط السمة vendor: true إلى تحويل وحدة APEX إلى حزمة APEX خاصة بالمورّد.

apex {
  ..
  vendor: true,
  ..
}

الملفات الثنائية والمكتبات المشتركة

تتضمّن حزمة APEX التبعيات المتعدية داخل حمولة حزمة APEX ما لم تكن تتضمّن واجهات ثابتة.

تتضمّن الواجهات الأصلية الثابتة للعناصر التابعة لحِزم APEX الخاصة بالمورّد cc_library مع stubs ومكتبات LLNDK. ويتم استبعاد هذه التبعيات من الحِزم، ويتم تسجيل التبعيات في بيان APEX. تتم معالجة ملف البيان بواسطة linkerconfig حتى تصبح التبعيات الأصلية الخارجية متاحة في وقت التشغيل.

في المقتطف التالي، يحتوي حِزمة APEX على كل من الملف الثنائي (my_service) والتبعيات غير الثابتة (ملفات *.so).

apex {
  ..
  vendor: true,
  binaries: ["my_service"],
  ..
}

في مقتطف الرمز التالي، يحتوي ملف APEX على المكتبة المشترَكة my_standalone_libوأي من التبعيات غير الثابتة (كما هو موضّح أعلاه).

apex {
  ..
  vendor: true,
  native_shared_libs: ["my_standalone_lib"],
  ..
}

تصغير حجم APEX

قد يصبح تنسيق APEX أكبر حجمًا لأنّه يضم حِزمًا من التبعيات غير الثابتة. ننصحك باستخدام الربط الثابت. يمكن ربط المكتبات الشائعة، مثل libc++.so وlibbase.so، بشكل ثابت بملفات HAL الثنائية. يمكنك أيضًا إنشاء عنصر تابع لتوفير واجهة مستقرة. لن يتم تجميع التبعية في حزمة APEX.

عمليات تنفيذ HAL

لتحديد تنفيذ HAL، يجب توفير الثنائيات والمكتبات المقابلة داخل حزمة APEX خاصة بالمورّد على غرار الأمثلة التالية:

لتغليف تنفيذ طبقة HAL بالكامل، يجب أن يحدّد حِزمة APEX أيضًا أي أجزاء VINTF ذات صلة وبرامج نصية للتهيئة.

أجزاء VINTF

يمكن عرض أجزاء VINTF من حزمة APEX خاصة بمورّد عندما تكون الأجزاء مضمّنة في etc/vintf من حزمة APEX.

استخدِم السمة prebuilts لتضمين أجزاء VINTF في حزمة APEX.

apex {
  ..
  vendor: true,
  prebuilts: ["fragment.xml"],
  ..
}

prebuilt_etc {
  name: "fragment.xml",
  src: "fragment.xml",
  sub_dir: "vintf",
}

واجهات برمجة التطبيقات الخاصة بطلبات البحث

عند إضافة أجزاء VINTF إلى APEX، استخدِم واجهات برمجة التطبيقات libbinder_ndk للحصول على عمليات الربط بين واجهات HAL وأسماء APEX.

  • AServiceManager_isUpdatableViaApex("com.android.foo.IFoo/default") : true إذا تم تحديد مثيل HAL في APEX.
  • AServiceManager_getUpdatableApexName("com.android.foo.IFoo/default", ...) : يحصل على اسم APEX الذي يحدّد مثيل HAL.
  • AServiceManager_openDeclaredPassthroughHal("mapper", "instance", ...) : يُستخدَم هذا النوع لفتح طبقة تجريد الأجهزة (HAL) التي تتيح نقل البيانات بدون تعديل.

نصوص Init البرمجية

يمكن أن تتضمّن حِزم APEX نصوص init بطريقتين: (أ) ملف نصي مُعدّ مسبقًا ضمن حمولة APEX، أو (ب) نص init عادي في /vendor/etc. يمكنك ضبط كليهما على حزمة APEX نفسها.

نص برمجي للتهيئة في APEX:

prebuilt_etc {
  name: "myinit.rc",
  src: "myinit.rc"
}

apex {
  ..
  vendor: true,
  prebuilts: ["myinit.rc"],
  ..
}

يمكن أن تحتوي نصوص التهيئة في حِزم APEX الخاصة بالمورّدين على تعريفات service وتوجيهات on <property or event>.

تأكَّد من أنّ تعريف service يشير إلى ملف ثنائي في حزمة APEX نفسها. على سبيل المثال، قد تحدّد حزمة com.android.foo APEX خدمة باسم foo-service.

on foo-service /apex/com.android.foo/bin/foo
  ...

يجب توخّي الحذر عند استخدام توجيهات on. بما أنّ النصوص البرمجية الأولية في حِزم APEX يتم تحليلها وتنفيذها بعد تفعيل حِزم APEX، لا يمكن استخدام بعض الأحداث أو الخصائص. استخدِم apex.all.ready=true لتنفيذ الإجراءات في أقرب وقت ممكن. يمكن لحزم APEX التمهيدية استخدام on init، ولكن ليس on early-init.

البرامج الثابتة

مثال:

ضمِّن البرامج الثابتة في حزمة APEX خاصة بمورّد باستخدام نوع الوحدة prebuilt_firmware، كما يلي:

prebuilt_firmware {
  name: "my.bin",
  src: "path_to_prebuilt_firmware",
  vendor: true,
}

apex {
  ..
  vendor: true,
  prebuilts: ["my.bin"],  // installed inside APEX as /etc/firmware/my.bin
  ..
}

يتم تثبيت prebuilt_firmware وحدة في الدليل <apex name>/etc/firmware لحزمة APEX. تفحص ueventd الأدلة /apex/*/etc/firmware للعثور على وحدات البرامج الثابتة.

يجب أن يضع file_contexts في حزمة APEX تصنيفًا مناسبًا لأي إدخالات في حمولة البرامج الثابتة لضمان إمكانية وصول ueventd إلى هذه الملفات في وقت التشغيل، ويكون التصنيف vendor_file كافيًا في العادة. مثلاً:

(/.*)? u:object_r:vendor_file:s0

وحدات النواة

يمكنك تضمين وحدات kernel في حزمة APEX خاصة بالمورّد كوحدات مسبقة الإنشاء، وذلك على النحو التالي:

prebuilt_etc {
  name: "my.ko",
  src: "my.ko",
  vendor: true,
  sub_dir: "modules"
}

apex {
  ..
  vendor: true,
  prebuilts: ["my.ko"],  // installed inside APEX as /etc/modules/my.ko
  ..
}

يجب أن تصنّف file_contexts في حزمة APEX أي إدخالات لحِمل وحدة النواة بشكل صحيح. مثلاً:

/etc/modules(/.*)? u:object_r:vendor_kernel_modules:s0

يجب تثبيت وحدات النواة بشكلٍ صريح. يوضّح المثال التالي لبرنامج نصي للتهيئة في قسم المورّد عملية التثبيت من خلال insmod:

my_init.rc:

on early-boot
  insmod /apex/myapex/etc/modules/my.ko
  ..

تراكبات الموارد في وقت التشغيل

مثال:

يمكن تضمين تراكبات موارد وقت التشغيل في حزمة APEX خاصة بمورّد باستخدام السمة rros.

runtime_resource_overlay {
    name: "my_rro",
    soc_specific: true,
}


apex {
  ..
  vendor: true,
  rros: ["my_rro"],  // installed inside APEX as /overlay/my_rro.apk
  ..
}

ملفات الإعداد الأخرى

تتيح حِزم APEX الخاصة بالمورّدين استخدام العديد من ملفات الإعداد الأخرى التي يتم العثور عليها عادةً في قسم المورّدين كملفات معدّة مسبقًا داخل حِزم APEX الخاصة بالمورّدين، ويتم إضافة المزيد.

أمثلة:

Bootstrap Vendor APEXes

يجب أن تتوفّر بعض خدمات HAL، مثل keymint، قبل تفعيل حِزم APEX. تضبط طبقات HAL عادةً early_hal في تعريف الخدمة ضمن نص برمجي للبدء. مثال آخر هو الفئة animation التي يتم بدءها عادةً قبل الحدث post-fs-data. عند تضمين خدمة HAL مبكرة في حزمة APEX الخاصة بالبائع، يجب وضع علامة "vendorBootstrap": true على حزمة APEX في ملف بيان APEX الخاص بها حتى يمكن تفعيلها في وقت مبكر. يُرجى العِلم أنّه لا يمكن تفعيل حِزم APEX التمهيدية إلا من الموقع الجغرافي المُنشأ مسبقًا، مثل /vendor/apex، وليس من /data/apex.

خصائص النظام

في ما يلي خصائص النظام التي يقرأها إطار العمل لتوفير حِزم APEX خاصة بالمورِّدين:

  • input_device.config_file.apex=<apex name>: عند ضبط هذا الخيار، يتم البحث عن ملفات الإعداد الخاصة بالإدخال (*.idc و*.kl و*.kcm) في الدليل /etc/usr الخاص بحزمة APEX.
  • ro.vulkan.apex=<apex name> - عند ضبط هذا الخيار، يتم تحميل برنامج تشغيل Vulkan من حزمة APEX. بما أنّ برنامج تشغيل Vulkan يُستخدَم من خلال طبقات HAL القديمة، يجب إنشاء حزمة APEX التمهيدية في APEX وإتاحة مساحة الاسم الخاصة بالرابط.

اضبط خصائص النظام في برامج init النصية باستخدام الأمر setprop.

ميزات إضافية

اختيار APEX عند بدء التشغيل

مثال:

يمكن تفعيل حِزم APEX الخاصة بالمورّدين اختياريًا أثناء عملية التشغيل. إذا حدّدت اسم ملف باستخدام سمة النظام ro.vendor.apex.<apex name>، لن يتم تفعيل سوى حزمة APEX التي تتطابق مع اسم الملف لـ <apex name> المحدّد. يتم تجاهل (عدم تفعيل) حزمة APEX التي تتضمّن <apex name> إذا تم ضبط سمة النظام هذه على none. يمكنك استخدام هذه الميزة لتثبيت نُسخ متعددة من حزمة APEX بالاسم نفسه. إذا كانت هناك إصدارات متعددة من حزمة APEX نفسها، يجب أن تشترك في المفتاح نفسه.

أمثلة على حالات الاستخدام:

  • تثبيت 3 إصدارات من حزمة APEX الخاصة بمورّد طبقة تجريد الأجهزة (HAL) لشبكة Wi-Fi: يمكن لفِرق ضمان الجودة إجراء اختبارات يدوية أو آلية باستخدام أحد الإصدارات، ثم إعادة التشغيل إلى إصدار آخر وإعادة إجراء الاختبارات، ثم مقارنة النتائج النهائية.
  • تثبيت إصدارَين من حزمة APEX الخاصة بمورّد طبقة تجريد الأجهزة (HAL) للكاميرا، وهما الإصدار الحالي والإصدار التجريبي: يمكن للمستخدمين الذين يجرّبون الإصدارات الداخلية استخدام الإصدار التجريبي بدون الحاجة إلى تنزيل ملف إضافي وتثبيته، ما يتيح لهم التبديل بسهولة إلى الإصدار الحالي.

أثناء عملية بدء التشغيل، يبحث apexd عن خصائص النظام التي تتّبع تنسيقًا محدّدًا لتفعيل إصدار APEX المناسب.

التنسيقات المتوقّعة لمفتاح السمة هي:

  • Bootconfig
    • تُستخدَم لضبط القيمة التلقائية بالعملة BoardConfig.mk.
    • androidboot.vendor.apex.<apex name>
  • خاصية النظام الثابتة
    • يُستخدَم لتغيير القيمة التلقائية التي تم ضبطها على جهاز تم تشغيله من قبل.
    • تتجاوز قيمة bootconfig إذا كانت متوفّرة.
    • persist.vendor.apex.<apex name>

يجب أن تكون قيمة الخاصية هي اسم ملف APEX الذي سيتم تفعيله، أو none لإيقاف APEX.

// Default version.
apex {
  name: "com.oem.camera.hal.my_apex_default",
  vendor: true,
  ..
}

// Non-default version.
apex {
  name: "com.oem.camera.hal.my_apex_experimental",
  vendor: true,
  ..
}

يجب أيضًا ضبط الإصدار التلقائي باستخدام bootconfig في BoardConfig.mk:

# Example for APEX "com.oem.camera.hal" with the default above:
BOARD_BOOTCONFIG += \
    androidboot.vendor.apex.com.oem.camera.hal=com.oem.camera.hal.my_apex_default

بعد تشغيل الجهاز، غيِّر الإصدار المفعَّل من خلال ضبط sysprop الدائم:

$ adb root;
$ adb shell setprop \
    persist.vendor.apex.com.oem.camera.hal \
    com.oem.camera.hal.my_apex_experimental;
$ adb reboot;

إذا كان الجهاز يتيح تعديل bootconfig بعد التثبيت (مثل استخدام أوامر fastboot oem)، سيؤدي تغيير سمة bootconfig لحزمة APEX المثبَّتة عدة مرات إلى تغيير الإصدار الذي يتم تفعيله عند بدء التشغيل.

بالنسبة إلى الأجهزة المرجعية الافتراضية المستندة إلى Cuttlefish، يمكنك استخدام الأمر --extra_bootconfig_args لضبط سمة bootconfig مباشرةً أثناء التشغيل. مثلاً:

launch_cvd --noresume \
  --extra_bootconfig_args "androidboot.vendor.apex.com.oem.camera.hal:=com.oem.camera.hal.my_apex_experimental";