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

คุณสามารถใช้รูปแบบไฟล์ APEX เพื่อแพ็กเกจและ ติดตั้งโมดูล Android OS ระดับล่างได้ ซึ่งช่วยให้สามารถสร้างและ ติดตั้งคอมโพเนนต์ต่างๆ เช่น บริการและไลบรารีแบบเนทีฟ การใช้งาน 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 ไว้ใน APEX

การใช้งาน HAL

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

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

VINTF fragments

ระบบจะแสดงผล VINTF Fragment จาก APEX ของผู้ให้บริการได้เมื่อ Fragment อยู่ใน 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 Fragment ลงใน 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
  ..
}

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

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

ตัวอย่าง

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

บริการ 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>
  • Sysprop แบบถาวร
    • ใช้เพื่อเปลี่ยนค่าเริ่มต้นที่ตั้งค่าไว้ในอุปกรณ์ที่บูตแล้ว
    • ลบล้างค่า 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";