‫Vendor APEX

می‌توانید از قالب فایل APEX برای بسته‌بندی و نصب کردن ماژول‌های سطح پایین‌تر سیستم‌عامل Android استفاده کنید. این ویژگی امکان ساخت و نصب مستقل عناصری مثل سرویس‌ها و کتابخانه‌های بومی، پیاده‌سازی‌های HAL، میان‌افزار، فایل‌های پیکربندی، و غیره را فراهم می‌کند.

‫APEXهای فروشنده به‌طور خودکار توسط سیستم ساخت در پارتیشن /vendor نصب می‌شوند و در زمان اجرا توسط apexd درست مثل APEXهای موجود در سایر پارتیشن‌ها فعال می‌شوند.

موارد استفاده

مدولارسازی تصاویر فروشنده

‫APEXها بسته‌بندی طبیعی و واحدبندی پیاده‌سازی‌های ویژگی در تصاویر فروشنده را تسهیل می‌کنند.

وقتی تصاویر فروشنده به‌عنوان ترکیبی از APEXهای فروشنده که به‌طور مستقل ساخته شده‌اند ساخته می‌شوند، سازندگان دستگاه می‌توانند به‌راحتی پیاده‌سازی‌های فروشنده موردنظر خود را در دستگاهشان انتخاب کنند. اگر هیچ‌یک از APEXهای ارائه‌شده با نیازهای سازندگان مطابقت نداشته باشد یا سخت‌افزار سفارشی کاملاً جدیدی داشته باشند، حتی می‌توانند APEX فروشنده جدیدی ایجاد کنند.

برای مثال، یک سازنده اصلی محصول ممکن است انتخاب کند که دستگاهش را با پیاده‌سازی Wi-Fi در AOSP APEX، پیاده‌سازی بلوتوث در SoC APEX، و پیاده‌سازی تلفن سفارشی سازنده اصلی محصول APEX بسازد.

بدون APEXهای فروشنده، پیاده‌سازی با این همه وابستگی بین عناصر فروشنده نیازمند هماهنگی و ردیابی دقیق است. با بسته‌بندی همه عناصر (ازجمله فایل‌های پیکربندی و کتابخانه‌های اضافی) در APEX با میاناهای به‌وضوح تعریف‌شده در هر نقطه از ارتباط بین ویژگی‌ها، عناصر مختلف قابل‌تبدیل می‌شوند.

تکرار توسعه‌دهنده

APEXهای فروشنده به توسعه‌دهندگان کمک می‌کنند تا با بسته‌بندی کردن کل پیاده‌سازی ویژگی، مانند HAL wifi، در 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 و اسکریپت init مربوطه را نیز مشخص کند.

قطعه‌های VINTF

وقتی تکه‌تکه‌ها در etc/vintf از APEX قرار داشته باشند، تکه‌تکه‌های 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 تنظیم کنید.

دستورگان Init در 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 برای اجرای کنش‌ها در اسرع وقت استفاده کنید. ‫Bootstrap APEXes می‌تواند از 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

واحدهای هسته

واحد‌های هسته را در 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

پودمان‌های هسته باید به‌طور صریح نصب شوند. اسکریپت init نمونه زیر در بخش فروشنده، نصب ازطریق 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های فروشنده وجود دارند پشتیبانی می‌کنند و موارد بیشتری درحال اضافه شدن است.

نمونه:

‫APEXهای فروشنده خودراه‌انداز

برخی‌از سرویس‌های HAL مثل keymint باید قبل‌از فعال شدن APEX دردسترس باشند. این HALها معمولاً early_hal را در تعریف سرویس خود در اسکریپت init تنظیم می‌کنند. مثال دیگر کلاس animation است که معمولاً زودتر از رویداد post-fs-data شروع می‌شود. وقتی چنین سرویس HAL زودهنگامی در APEX فروشنده بسته‌بندی می‌شود، apex "vendorBootstrap": true را در «مانیفست 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های اولیه استفاده می‌شود، Bootstrap APEX را در APEX ایجاد کنید و فضای نام پیونددهنده را پیکربندی کنید تا قابل‌مشاهده باشد.

خصوصیات سیستم را در برنامه‌های آغازین بااستفاده از setprop دستور تنظیم کنید.

ویژگی‌های اضافی

انتخاب APEX در زمان راه‌اندازی

مثال:

‫APEXهای فروشنده را می‌توان به‌صورت اختیاری درطول راه‌اندازی فعال کرد. اگر نام فایلی را بااستفاده از دارایی سیستم ro.vendor.apex.<apex name> مشخص کنید، فقط APEX منطبق با نام فایل برای <apex name> خاص فعال می‌شود. اگر این ویژگی سیستم روی none تنظیم شده باشد، APEX با <apex name> نادیده گرفته می‌شود (فعال نمی‌شود). می‌توانید از این ویژگی برای نصب چندین نسخه از یک APEX با نام یکسان استفاده کنید. اگر نسخه‌های متعددی از همان APEX وجود داشته باشد، باید از کلید یکسانی استفاده کنند.

موارد استفاده نمونه:

  • نصب ۳ نسخه از APEX فروشنده HAL Wi-Fi: تیم‌های QA می‌توانند بااستفاده از یک نسخه، آزمایش‌های دستی یا خودکار اجرا کنند، سپس به نسخه دیگری بازراه‌اندازی کنند و آزمایش‌ها را دوباره اجرا کنند، و درنهایت نتایج را مقایسه کنند.
  • ‫۲ نسخه از APEX فروشنده HAL دوربین را نصب کنید، فعلی و آزمایشی: کاربران داخلی می‌توانند بدون بارگیری و نصب فایل اضافی از نسخه آزمایشی استفاده کنند، بنابراین می‌توانند به‌راحتی به نسخه قبلی برگردند.

درطول راه‌اندازی، apexd به‌دنبال sysprops با قالب خاصی می‌گردد تا نسخه APEX صحیح را فعال کند.

قالب‌های موردانتظار برای کلید دارایی عبارت‌اند از:

  • Bootconfig:
    • برای تنظیم مقدار پیش‌فرض، در BoardConfig.mk استفاده می‌شود.
    • androidboot.vendor.apex.<apex name> (صادرشده به‌عنوان ro.boot.vendor.apex.<apex name>).
  • پیکربندی پویای راه‌اندازی اولیه (init_dev_config):
    • سازوکار تولید توصیه شده برای فعال‌سازی و غیرفعال‌سازی APEX براساس هر واحد انبار کالا (در Android 17 و بالاتر دردسترس است).
    • ‫ro.boot.vendor.apex.<apex name> را به‌صورت پویا در زمان راه‌اندازی قبل‌از اجرای apexd-bootstrap تنظیم می‌کند. تنظیم مقدار دارایی روی none فعال‌سازی APEX را صریحاً غیرفعال می‌کند.
    • برای جزئیات معماری، پیکربندی دستگاه راه‌اندازی اولیه (init_dev_config) ببینید.
  • سیستم‌پراپ پایدار (منسوخ):
    • persist.vendor.apex.<apex name>
    • ویژگی فقط برای توسعه. چون apexd پیش‌از نصب پارتیشن /data، «واحد اجرایی بسته‌های پیش‌نصب‌شده» را فعال می‌کند، نمی‌توان از syspropهای ماندگار ذخیره‌شده در /data برای انتخاب اولیه «واحد اجرایی بسته‌های پیش‌نصب‌شده» در ساخت‌های تولید استفاده کرد.

مقدار دارایی باید نام فایل 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";

برای انتخاب APEX از init_dev_config استفاده کنید

در Android 17 و بالاتر، برای انتخاب یا غیرفعال کردن پویای APEXهای فروشنده در زمان اجرا، ابزار فروشنده‌ای را که توسط سرویس init_dev_config اجرا می‌شود پیاده‌سازی کنید. این ابزار درطول early-init قبل‌از apexd-bootstrap اجرا می‌شود و ro.boot.vendor.apex.* دارایی را بااستفاده از rustutils::system_properties تنظیم می‌کند.

تنظیم کردن دارایی روی نام فایل APEX آن نسخه خاص را برای فعال‌سازی انتخاب می‌کند، درحالی‌که تنظیم مقدار روی none می‌تواند برای غیرفعال کردن صریح فعال‌سازی APEX استفاده شود، برای مثال، در واحدهای نگهداری کالا که لوازم جانبی سخت‌افزاری وجود ندارد.

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;