يمكنك استخدام تنسيق ملف APEX لتجميع وحدات نظام التشغيل Android ذات المستوى الأدنى وتثبيتها. ويتيح إنشاء وتثبيت المكوّنات بشكل مستقل، مثل الخدمات والمكتبات المجمّعة من رموز برمجية أصلية وعمليات تنفيذ HAL والبرامج الثابتة وملفات الإعداد وما إلى ذلك.
يتم تثبيت حِزم APEX الخاصة بالمورّدين تلقائيًا من خلال نظام التصميم في القسم /vendor، ويتم تفعيلها في وقت التشغيل من خلال apexd تمامًا مثل حِزم APEX في الأقسام الأخرى.
حالات الاستخدام
تقسيم صور البائعين إلى وحدات
تسهّل حِزم APEX عملية تجميع وتنظيم عمليات تنفيذ الميزات في وحدات على صور المورِّدين.
عند إنشاء صور المورّدين كمجموعة من حِزم APEX الخاصة بالمورّدين والتي تم إنشاؤها بشكل مستقل، يمكن لمصنّعي الأجهزة اختيار عمليات التنفيذ المحدّدة الخاصة بالمورّدين التي يريدونها على أجهزتهم بسهولة. يمكن للمصنّعين أيضًا إنشاء حزمة APEX جديدة خاصة بالمورّد إذا لم تتوافق أي من حِزم APEX المتوفّرة مع احتياجاتهم، أو إذا كان لديهم أجهزة مخصّصة جديدة تمامًا.
على سبيل المثال، يمكن للمصنّع الأصلي للجهاز اختيار إنشاء الجهاز باستخدام حزمة APEX لتنفيذ شبكة Wi-Fi في مشروع Android المفتوح المصدر، وحزمة 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 خاصة بالمورّد على غرار الأمثلة التالية:
- طبقة تجريد الأجهزة لأدوات الاستشعار
- Vibrator HAL
- طبقة تجريد الأجهزة (HAL) لشبكة Wi-Fi
- طبقة تجريد الأجهزة الخاصة بالاتصال الهاتفي
لتغليف تنفيذ طبقة 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 نصوصًا برمجية للتهيئة بطريقتين: (أ) ملف نصي مُعدّ مسبقًا ضمن حمولة حزمة APEX، أو (ب) نص برمجي عادي للتهيئة في /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. بما أنّه يتم تحليل وتنفيذ نصوص init البرمجية في حِزم 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 الخاصة بالمورّدين، ويتم إضافة المزيد.
أمثلة:
- ملفات XML لبيانات الميزات
- تتضمّن ميزة أجهزة الاستشعار ملفات XML كـ حِزم APEX مُنشأة مسبقًا في HAL الخاص بمورّد أجهزة الاستشعار
- ملفات الإعداد الخاصة بالمدخلات
- إعدادات شاشة اللمس كـ إصدارات مسبقة في حزمة APEX خاصة بالمورّد تتضمّن الإعدادات فقط
Bootstrap Vendor APEXes
يجب أن تتوفّر بعض خدمات HAL، مثل keymint، قبل تفعيل حِزم APEX. تضبط هذه الطبقات عادةً 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 عن sysprops بالتنسيق التالي لتفعيل إصدار APEX المناسب.
التنسيقات المتوقّعة لمفتاح السمة هي:
- Bootconfig:
- تُستخدَم لضبط القيمة التلقائية بالعملة
BoardConfig.mk. androidboot.vendor.apex.<apex name>(تم تصديرها باسمro.boot.vendor.apex.<apex name>)
- تُستخدَم لضبط القيمة التلقائية بالعملة
- إعدادات التشغيل المبكر الديناميكية (
init_dev_config):- آلية الإنتاج المقترَحة لتفعيل APEX وتعطيله على مستوى وحدة حفظ المخزون (SKU) (متاحة في Android 17 والإصدارات الأحدث)
- يضبط هذا الخيار
ro.boot.vendor.apex.<apex name>بشكل ديناميكي عند بدء التشغيل قبل تنفيذapexd-bootstrap. يؤدي ضبط قيمة الخاصية علىnoneإلى إيقاف تفعيل حزمة APEX بشكل صريح. - راجِع إعدادات الجهاز عند بدء التشغيل المبكر (
init_dev_config) للاطّلاع على تفاصيل حول البنية.
- خاصية النظام المستمرة (متوقّفة نهائيًا):
persist.vendor.apex.<apex name>- ميزة مخصّصة للمطوّرين فقط. بما أنّ
apexdيفعّل حِزم APEX المثبَّتة مسبقًا قبل تحميل القسم/data، لا يمكن استخدام خصائص النظام الدائمة المخزَّنة في/dataلاختيار حِزم APEX مبكرًا في الإصدارات المخصّصة للإنتاج.
يجب أن تكون قيمة السمة هي اسم ملف 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 لاختيار APEX
للتعريف بحزمة APEX التي سيتم استخدامها في مدّة التصميم، اضبط الإصدار التلقائي باستخدام 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
إذا كان الجهاز يتيح تعديل 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";
استخدام init_dev_config لاختيار APEX
في الإصدار 17 من نظام التشغيل Android والإصدارات الأحدث، لتحديد حِزم APEX الخاصة بالمورّد أو إيقافها بشكل ديناميكي في وقت التشغيل، عليك تنفيذ أداة خاصة بالمورّد يتم تشغيلها من خلال خدمة init_dev_config.
يتم تشغيل الأداة خلال early-init قبل apexd-bootstrap ويتم ضبط خصائص ro.boot.vendor.apex.* باستخدام rustutils::system_properties.
يؤدي ضبط السمة على اسم ملف APEX إلى اختيار هذا الإصدار المحدّد للتفعيل، بينما يمكن استخدام ضبط القيمة على none لإيقاف تفعيل APEX بشكل صريح، على سبيل المثال، في وحدات SKU التي لا تتضمّن أجهزة طرفية.
use rustutils::system_properties;
fn main() {
// Select an APEX version based on detected SKU, or disable it altogether:
let hal_apex = match sku_type {
1 => "com.oem.camera.hal.my_apex_default",
2 => "com.oem.camera.hal.my_apex_experimental",
_ => "none",
};
system_properties::write(
"ro.boot.vendor.apex.com.oem.camera.hal",
hal_apex,
).expect("Failed to set vendor APEX selection property");
}
استخدام sysprops ثابتة للتطوير (متوقّفة نهائيًا)
لأغراض التطوير وتصحيح الأخطاء، يمكنك بعد تشغيل الجهاز تغيير الإصدار المفعَّل من خلال ضبط sysprop الدائم على النحو التالي:
$ adb root;
$ adb shell setprop \
persist.vendor.apex.com.oem.camera.hal \
com.oem.camera.hal.my_apex_experimental;
$ adb reboot;