APEX ของผู้ให้บริการ

คุณสามารถใช้รูปแบบไฟล์ APEX เพื่อแพ็กเกจและติดตั้งโมดูลระบบปฏิบัติการ Android ระดับล่าง ซึ่งช่วยให้สามารถสร้างและ ติดตั้งคอมโพเนนต์ต่างๆ เช่น บริการและไลบรารีแบบเนทีฟ การใช้งาน HAL เฟิร์มแวร์ ไฟล์กำหนดค่า ฯลฯ ได้อย่างอิสระ

ระบบบิลด์จะติดตั้ง APEX ของผู้ให้บริการโดยอัตโนมัติในพาร์ติชัน /vendor และ apexd จะเปิดใช้งานที่รันไทม์เช่นเดียวกับ APEX ในพาร์ติชันอื่นๆ

กรณีการใช้งาน

การแยกส่วนรูปภาพของผู้ขาย

APEX ช่วยให้การจัดกลุ่มและการแยกโมดูลการติดตั้งใช้งานฟีเจอร์ในรูปภาพของผู้ให้บริการเป็นไปอย่างเป็นธรรมชาติ

เมื่อสร้างรูปภาพของผู้ให้บริการเป็นการผสมผสานระหว่าง APEX ของผู้ให้บริการที่สร้างขึ้นแยกกัน ผู้ผลิตอุปกรณ์จะเลือกการติดตั้งใช้งานของผู้ให้บริการที่ต้องการในอุปกรณ์ของตนเองได้อย่างง่ายดาย ผู้ผลิตยังสร้าง APEX ของผู้ให้บริการใหม่ได้หากไม่มี APEX ที่ให้มาตรงกับความต้องการ หรือมีฮาร์ดแวร์ที่กำหนดเองใหม่เอี่ยม

ตัวอย่างเช่น OEM อาจเลือกที่จะสร้างอุปกรณ์ด้วย APEX การติดตั้งใช้งาน Wi-Fi ของ AOSP , APEX การติดตั้งใช้งานบลูทูธของ SoC และ APEX การติดตั้งใช้งานโทรศัพท์ของ OEM ที่กำหนดเอง

หากไม่มี 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 เว้นแต่ว่าการขึ้นต่อกันเหล่านั้นจะมีอินเทอร์เฟซที่เสถียร

อินเทอร์เฟซแบบเนทีฟที่เสถียรสำหรับทรัพยากร Dependency ของ APEX ของผู้ให้บริการ ได้แก่ cc_library with stubs และไลบรารี LLNDK ระบบจะไม่รวมการอ้างอิงเหล่านี้ไว้ใน การแพ็กเกจ และจะบันทึกการอ้างอิงไว้ในไฟล์ Manifest ของ APEX linkerconfig จะประมวลผลไฟล์ Manifest เพื่อให้การอ้างอิงเนทีฟภายนอกพร้อมใช้งานในขณะรันไทม์

ในข้อมูลโค้ดต่อไปนี้ APEX มีทั้งไบนารี (my_service) และ การอ้างอิงที่ไม่เสถียร (ไฟล์ *.so)

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

ในข้อมูลโค้ดต่อไปนี้ APEX มีไลบรารีที่ใช้ร่วมกัน my_standalone_libและทรัพยากร Dependency ที่ไม่เสถียร (ตามที่อธิบายไว้ข้างต้น)

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

ลดขนาด APEX

APEX อาจมีขนาดใหญ่ขึ้นเนื่องจากรวมการอ้างอิงที่ไม่เสถียร เราขอแนะนำให้ ใช้การลิงก์แบบคงที่ ไลบรารีทั่วไป เช่น libc++.so และ libbase.so สามารถ ลิงก์แบบคงที่กับไบนารี HAL ได้ การสร้างทรัพยากร Dependency เพื่อให้มีอินเทอร์เฟซที่เสถียรอาจเป็นอีกตัวเลือกหนึ่ง ระบบจะไม่รวมทรัพยากร Dependency ไว้ใน APEX

การใช้งาน HAL

หากต้องการกำหนดการใช้งาน HAL ให้ระบุไบนารีและไลบรารีที่เกี่ยวข้อง ภายใน APEX ของผู้ให้บริการในลักษณะที่คล้ายกับตัวอย่างต่อไปนี้

APEX ควรระบุ ส่วน VINTF และสคริปต์ init ที่เกี่ยวข้องด้วยเพื่อให้ครอบคลุมการใช้งาน HAL อย่างเต็มที่

VINTF fragments

ระบบจะแสดง VINTF Fragment จาก APEX ของผู้ให้บริการได้เมื่อ Fragment อยู่ใน etc/vintf ของ APEX

ใช้พร็อพเพอร์ตี้ prebuilts เพื่อฝัง Fragment ของ VINTF ใน APEX

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

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

Query API

เมื่อเพิ่ม Fragment ของ VINTF ลงใน APEX ให้ใช้ libbinder_ndk API เพื่อรับ การแมปของอินเทอร์เฟซ 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 แบบส่งผ่าน

สคริปต์เริ่มต้น

APEX สามารถมีสคริปต์ init ได้ 2 วิธี ได้แก่ (ก) ไฟล์ข้อความที่สร้างไว้ล่วงหน้าภายใน เพย์โหลด APEX หรือ (ข) สคริปต์ init ปกติใน /vendor/etc คุณตั้งค่าทั้ง 2 อย่าง สำหรับ 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 เพื่อทริกเกอร์การดำเนินการโดยเร็วที่สุด Bootstrap 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

โมดูลเคอร์เนล

ฝังโมดูลเคอร์เนลใน 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
  ..
}

ไฟล์การกำหนดค่าอื่นๆ

Vendor APEX รองรับไฟล์การกำหนดค่าอื่นๆ อีกมากมายที่มักพบในพาร์ติชันของผู้ให้บริการ เป็นไฟล์ที่สร้างไว้ล่วงหน้าภายใน Vendor APEX และจะมีการเพิ่มไฟล์อื่นๆ อีก

ตัวอย่างเช่น

Bootstrap Vendor APEXes

บริการ HAL บางอย่าง เช่น keymint ควรพร้อมใช้งานก่อนที่จะเปิดใช้งาน APEX โดยปกติแล้ว HAL เหล่านั้นจะตั้งค่า early_hal ในคำจำกัดความของบริการใน สคริปต์ init อีกตัวอย่างคือanimation class ซึ่งโดยปกติจะเริ่ม เร็วกว่าpost-fs-data event เมื่อมีการแพ็กเกจบริการ HAL เวอร์ชันแรกดังกล่าวใน APEX ของผู้ให้บริการ ให้สร้าง APEX "vendorBootstrap": true ใน APEX Manifest เพื่อให้เปิดใช้งานได้เร็วขึ้น โปรดทราบว่าคุณจะเปิดใช้งาน 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 เนื่องจาก HAL รุ่นแรกๆ ใช้ไดรเวอร์ Vulkan ให้สร้าง APEX Bootstrap APEX และกำหนดค่าให้เนมสเปซของ Linker นั้น มองเห็นได้

ตั้งค่าพร็อพเพอร์ตี้ของระบบในสคริปต์ init โดยใช้คำสั่ง setprop

ฟีเจอร์เพิ่มเติม

การเลือก APEX เมื่อบูตเครื่อง

ตัวอย่าง

คุณจะเลือกเปิดใช้งาน APEX ของผู้ให้บริการในระหว่างการบูตหรือไม่ก็ได้ หากคุณระบุชื่อไฟล์โดยใช้พร็อพเพอร์ตี้ของระบบ ro.vendor.apex.<apex name> ระบบจะเปิดใช้งานเฉพาะ APEX ที่ตรงกับชื่อไฟล์สำหรับ <apex name> ที่เฉพาะเจาะจง ระบบจะไม่สนใจ (ไม่เปิดใช้งาน) APEX ที่มี <apex name> หากตั้งค่าพร็อพเพอร์ตี้ของระบบนี้เป็น none คุณใช้ฟีเจอร์นี้เพื่อติดตั้ง APEX หลายสำเนาที่มีชื่อเดียวกันได้ หากมี APEX เดียวกันหลายเวอร์ชัน APEX เหล่านั้นควรใช้คีย์เดียวกัน

ตัวอย่างกรณีการใช้งาน

  • ติดตั้ง HAL ของ Wi-Fi เวอร์ชัน 3 ของ APEX ของผู้ให้บริการ: ทีม QA สามารถเรียกใช้การทดสอบด้วยตนเอง หรือการทดสอบอัตโนมัติโดยใช้เวอร์ชันใดเวอร์ชันหนึ่ง จากนั้นรีบูตเป็นอีกเวอร์ชันหนึ่งและ เรียกใช้การทดสอบอีกครั้ง แล้วเปรียบเทียบผลลัพธ์สุดท้าย
  • ติดตั้ง HAL ของกล้อง 2 เวอร์ชันจาก APEX ของผู้ให้บริการ current และ experimental: ผู้ใช้ที่ทดสอบผลิตภัณฑ์ก่อนเปิดตัวจะใช้เวอร์ชันทดลองได้โดยไม่ต้อง ดาวน์โหลดและติดตั้งไฟล์เพิ่มเติม จึงสลับกลับได้ง่าย

ในระหว่างการบูต apexd จะค้นหา sysprop ที่มีรูปแบบเฉพาะเพื่อ เปิดใช้งาน 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)
  • Sysprop แบบถาวร (เลิกใช้งานแล้ว):
    • persist.vendor.apex.<apex name>
    • ฟีเจอร์นี้ใช้ได้เฉพาะในสภาพแวดล้อมการพัฒนาเท่านั้น เนื่องจาก apexd จะเปิดใช้งาน APEX ที่ติดตั้งไว้ล่วงหน้า ก่อนที่จะมีการติดตั้งพาร์ติชัน /data ดังนั้นจึงไม่สามารถใช้ Sysprop แบบถาวรที่จัดเก็บไว้ใน /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

ใน Android 17 ขึ้นไป หากต้องการเลือกหรือปิดใช้ 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");
}

ใช้ sysprop แบบถาวรสำหรับการพัฒนา (เลิกใช้งานแล้ว)

เพื่อวัตถุประสงค์ในการพัฒนาและแก้ไขข้อบกพร่อง หลังจากบูตอุปกรณ์แล้ว คุณจะ เปลี่ยนเวอร์ชันที่เปิดใช้งานได้โดยการตั้งค่า sysprop แบบถาวร ดังนี้

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