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

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

ระบบบิลด์จะติดตั้ง APEX ของผู้ให้บริการโดยอัตโนมัติในพาร์ติชัน /vendor และ apexd จะเปิดใช้งาน APEX ดังกล่าวเมื่อรันไทม์ เช่นเดียวกับ 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 ไฟล์ Manifest ได้รับการประมวลผลโดย linkerconfig เพื่อให้ทรัพยากร Dependency ของระบบภายนอกพร้อมใช้งานขณะรันไทม์

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

การใช้งาน HAL

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

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

ข้อมูล 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",
}

Query API

เมื่อเพิ่มข้อมูลโค้ด 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 Passthrough

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

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 เพื่อเริ่มการดําเนินการโดยเร็วที่สุด APEX ที่ใช้ Bootstrap สามารถใช้ 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 ของผู้ให้บริการ Bootstrap

บริการ HAL บางรายการ เช่น keymint ควรพร้อมใช้งานก่อนที่จะเปิดใช้งาน APEX โดยปกติแล้ว HAL เหล่านั้นจะตั้งค่า early_hal ในคําจํากัดความของบริการในสคริปต์ init อีกตัวอย่างหนึ่งคือคลาส animation ซึ่งปกติจะเริ่มเร็วกว่าเหตุการณ์ post-fs-data เมื่อแพ็กเกจบริการ HAL ระยะแรกดังกล่าวใน APEX ของผู้ให้บริการ ให้สร้าง apex "vendorBootstrap": true ใน APEX Manifest เพื่อให้เปิดใช้งานได้เร็วขึ้น โปรดทราบว่า APEX แบบบูตสแtrap จะเปิดใช้งานได้จากตำแหน่งที่สร้างไว้ล่วงหน้าเท่านั้น เช่น /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 ของผู้ให้บริการหลายเวอร์ชันที่ใช้ชื่อและคีย์ APEX เดียวกันได้ด้วย จากนั้นเลือกเวอร์ชันที่จะเปิดใช้งานในระหว่างการบูตแต่ละครั้งโดยใช้ sysprop แบบถาวร สำหรับบาง Use Case ของนักพัฒนาแอป การดำเนินการนี้อาจง่ายกว่าการติดตั้งสําเนา APEX ใหม่โดยใช้ adb install

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

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

ในระหว่างการบูต apexd จะค้นหา sysprops ตามรูปแบบที่เฉพาะเจาะจงเพื่อเปิดใช้งาน APEX เวอร์ชันที่ถูกต้อง

รูปแบบที่คาดไว้สำหรับคีย์พร็อพเพอร์ตี้มีดังนี้

  • Bootconfig
    • ใช้เพื่อตั้งค่าเริ่มต้นในสกุลเงิน BoardConfig.mk
    • androidboot.vendor.apex.<apex name>
  • sysprop แบบถาวร
    • ใช้เพื่อเปลี่ยนค่าเริ่มต้นที่ตั้งค่าไว้ในอุปกรณ์ที่บูตแล้ว
    • ลบล้างค่า bootconfig หากมี
    • persist.vendor.apex.<apex name>

ค่าของพร็อพเพอร์ตี้ควรเป็นชื่อไฟล์ของ 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";