รูปแบบไฟล์ APEX

รูปแบบคอนเทนเนอร์ Android Pony EXpress (APEX) เปิดตัวใน Android 10 และใช้ในขั้นตอนการติดตั้งสำหรับข้อบังคับของระบบในระดับล่าง รูปแบบนี้ช่วยให้อัปเดตคอมโพเนนต์ของระบบที่ไม่เข้ากับรูปแบบแอปพลิเคชัน Android มาตรฐานได้ ตัวอย่างคอมโพเนนต์ ได้แก่ บริการและไลบรารีแบบเนทีฟ, เลเยอร์การแยกแยะฮาร์ดแวร์ (HAL), รันไทม์ (ART) และไลบรารีคลาส

คำว่า "APEX" สามารถหมายถึงไฟล์ APEX ได้ด้วย

ฉากหลัง

แม้ว่า Android จะรองรับการอัปเดตโมดูลที่เหมาะกับรูปแบบแอปมาตรฐาน (เช่น บริการ กิจกรรม) ผ่านแอปโปรแกรมติดตั้งแพ็กเกจ (เช่น แอป Google Play Store) แต่การใช้รูปแบบที่คล้ายกันสำหรับคอมโพเนนต์ระบบปฏิบัติการในระดับล่างมีข้อเสียดังนี้

  • โมดูลที่ใช้ APK จะใช้ในลำดับการเปิดเครื่องไม่ได้ในตอนต้น เครื่องมือจัดการแพ็กเกจเป็นที่เก็บข้อมูลกลางเกี่ยวกับแอป และสามารถเริ่มต้นได้จากเครื่องมือจัดการกิจกรรมเท่านั้น ซึ่งจะพร้อมใช้งานในขั้นตอนถัดไปของกระบวนการบูต
  • รูปแบบ APK (โดยเฉพาะไฟล์ Manifest) ออกแบบมาสำหรับแอป Android และอาจไม่เหมาะกับโมดูลระบบเสมอไป

การออกแบบ

ส่วนนี้จะอธิบายการออกแบบระดับสูงของรูปแบบไฟล์ APEX และเครื่องมือจัดการ APEX ซึ่งเป็นบริการที่ใช้จัดการไฟล์ APEX

และดูข้อมูลเพิ่มเติมเกี่ยวกับเหตุผลที่การออกแบบ APEX นี้ได้รับเลือกได้ที่ตัวเลือกอื่นๆ ที่นำมาพิจารณาเมื่อพัฒนา APEX

รูปแบบ APEX

นี่คือรูปแบบของไฟล์ APEX

รูปแบบไฟล์ APEX

รูปที่ 1 รูปแบบไฟล์ APEX

ในระดับบนสุด ไฟล์ APEX คือไฟล์ ZIP ที่จัดเก็บไฟล์โดยไม่มีการบีบอัดและมีขนาด 4 KB

ไฟล์ 4 ไฟล์ในไฟล์ APEX มีดังนี้

  • apex_manifest.json
  • AndroidManifest.xml
  • apex_payload.img
  • apex_pubkey

ไฟล์ apex_manifest.json จะมีชื่อและเวอร์ชันของแพ็กเกจซึ่งระบุไฟล์ APEX นี่คือบัฟเฟอร์โปรโตคอล ApexManifest ในรูปแบบ JSON

ไฟล์ AndroidManifest.xml ช่วยให้ไฟล์ APEX ใช้เครื่องมือและโครงสร้างพื้นฐานที่เกี่ยวข้องกับ APK เช่น ADB, PackageManager และแอปโปรแกรมติดตั้งแพ็กเกจ (เช่น Play Store) ได้ เช่น ไฟล์ APEX สามารถใช้เครื่องมือที่มีอยู่ เช่น aapt เพื่อตรวจสอบข้อมูลเมตาพื้นฐานจากไฟล์ ไฟล์จะมีชื่อแพ็กเกจและข้อมูลเวอร์ชัน โดยทั่วไปแล้ว ข้อมูลนี้ยังพร้อมใช้งานใน apex_manifest.json ด้วย

แนะนำให้ใช้ apex_manifest.json มากกว่า AndroidManifest.xml สำหรับโค้ดและระบบใหม่ที่เกี่ยวข้องกับ APEX AndroidManifest.xml อาจมีข้อมูลการกำหนดเป้าหมายเพิ่มเติมที่เครื่องมือเผยแพร่แอปที่มีอยู่ใช้ได้

apex_payload.img คืออิมเมจระบบไฟล์ ext4 ที่ได้รับการสนับสนุนโดย dm-verity ระบบจะเมานต์อิมเมจเมื่อรันไทม์ผ่านอุปกรณ์ Loopback กล่าวโดยละเอียดคือ ระบบจะสร้าง Hash Tree และบล็อกข้อมูลเมตาโดยใช้ไลบรารี libavb เพย์โหลดระบบไฟล์ ไม่ได้รับการแยกวิเคราะห์ (เนื่องจากรูปภาพควรนำไปต่อเชื่อมได้) ไฟล์ปกติจะรวมอยู่ในไฟล์ apex_payload.img

apex_pubkey เป็นคีย์สาธารณะที่ใช้ในการลงนามอิมเมจระบบไฟล์ ขณะรันไทม์ คีย์นี้จะตรวจสอบว่า APEX ที่ดาวน์โหลดมาได้รับการลงนามด้วยเอนทิตีเดียวกันกับที่ลงนาม APEX เดียวกันในพาร์ติชันในตัว

หลักเกณฑ์การตั้งชื่อ APEX

โปรดใช้หลักเกณฑ์การตั้งชื่อต่อไปนี้เพื่อช่วยป้องกันความขัดแย้งในการตั้งชื่อระหว่าง APEX ใหม่เมื่อแพลตฟอร์มพัฒนาไป

  • com.android.*
    • สงวนไว้สำหรับ APEX ของ AOSP ไม่เจาะจงสำหรับบริษัทหรืออุปกรณ์ใดๆ
  • com.<companyname>.*
    • สงวนไว้สำหรับบริษัท อุปกรณ์หลายเครื่องจากบริษัทดังกล่าวอาจใช้
  • com.<companyname>.<devicename>.*
    • สงวนไว้สำหรับ APEX ที่ไม่ซ้ำกันสำหรับอุปกรณ์หนึ่งๆ (หรือกลุ่มย่อยของอุปกรณ์)

เครื่องมือจัดการ APEX

เครื่องมือจัดการ APEX (หรือ apexd) เป็นกระบวนการแบบสแตนด์อโลนที่รับผิดชอบในการยืนยัน ติดตั้ง และถอนการติดตั้งไฟล์ APEX กระบวนการนี้จะเริ่มต้นและพร้อมใช้งานในช่วงต้นของลำดับการบูต โดยปกติแล้วระบบจะติดตั้งไฟล์ APEX ไว้ล่วงหน้าในอุปกรณ์ในส่วน /system/apex โดยค่าเริ่มต้น เครื่องมือจัดการ APEX จะใช้แพ็กเกจเหล่านี้หากไม่มีการอัปเดต

ลำดับการอัปเดตของ APEX จะใช้คลาส PackageManager ดังนี้

  1. ไฟล์ APEX จะดาวน์โหลดผ่านแอปโปรแกรมติดตั้งแพ็กเกจ, ADB หรือแหล่งที่มาอื่นๆ
  2. ตัวจัดการแพ็กเกจจะเริ่มขั้นตอนการติดตั้ง เมื่อทราบว่าไฟล์ดังกล่าวเป็น APEX ตัวจัดการแพ็กเกจจะโอนการควบคุมไปยังตัวจัดการ APEX
  3. ผู้จัดการ APEX จะยืนยันไฟล์ APEX
  4. หากไฟล์ APEX ได้รับการยืนยันแล้ว ฐานข้อมูลภายในของตัวจัดการ APEX จะได้รับการอัปเดตเพื่อแสดงว่าไฟล์ APEX ได้รับการเปิดใช้งานเมื่อเปิดเครื่องครั้งถัดไป
  5. ผู้ส่งคำขอติดตั้งจะได้รับการออกอากาศเมื่อการยืนยันแพ็กเกจสำเร็จ
  6. คุณต้องรีบูตระบบเพื่อติดตั้งต่อ
  7. เมื่อเปิดเครื่องครั้งถัดไป ตัวจัดการ APEX จะเริ่มต้น อ่านฐานข้อมูลภายใน และดำเนินการต่อไปนี้สำหรับไฟล์ APEX ที่ระบุ

    1. ยืนยันไฟล์ APEX
    2. สร้างอุปกรณ์ Loopback จากไฟล์ APEX
    3. สร้างอุปกรณ์บล็อกโปรแกรมแมปอุปกรณ์บนอุปกรณ์การวนซ้ำ
    4. ติดตั้งอุปกรณ์บล็อกตัวแมปอุปกรณ์เข้ากับเส้นทางที่ไม่ซ้ำกัน (เช่น /apex/name@ver)

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

ไฟล์ APEX คือไฟล์ APK

ไฟล์ APEX เป็นไฟล์ APK ที่ถูกต้องเนื่องจากเป็นไฟล์ ZIP ที่มีการรับรอง (โดยใช้รูปแบบลายเซ็น APK) ที่มีไฟล์ AndroidManifest.xml ซึ่งจะช่วยให้ไฟล์ APEX ใช้โครงสร้างพื้นฐานสำหรับไฟล์ APK ได้ เช่น แอปตัวติดตั้งแพ็กเกจ ยูทิลิตีการลงนาม และเครื่องมือจัดการแพ็กเกจ

ไฟล์ AndroidManifest.xml ในไฟล์ APEX มีขนาดเล็กมาก ซึ่งประกอบไปด้วยแพ็กเกจ name, versionCode และ targetSdkVersion, minSdkVersion และ maxSdkVersion ที่ไม่บังคับสำหรับการกำหนดเป้าหมายแบบละเอียด ข้อมูลนี้ช่วยให้สามารถส่งไฟล์ APEX ผ่านช่องทางที่มีอยู่เดิมได้ เช่น แอปโปรแกรมติดตั้งแพ็กเกจและ ADB

ประเภทไฟล์ที่รองรับ

รูปแบบ APEX รองรับไฟล์ประเภทต่อไปนี้

  • Libs ที่แชร์แบบเนทีฟ
  • ไฟล์ปฏิบัติการที่มาพร้อมเครื่อง
  • ไฟล์ JAR
  • ไฟล์ข้อมูล
  • ไฟล์การกำหนดค่า

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

ตัวเลือกการรับรอง

ไฟล์ APEX มีการเซ็นชื่อได้ 2 วิธี ก่อนอื่น ไฟล์ apex_payload.img (โดยเฉพาะอย่างยิ่งตัวบ่งชี้ vbmeta ที่ต่อท้าย apex_payload.img) จะได้รับการลงนามด้วยคีย์ จากนั้น APEX ทั้งหมดจะลงนามโดยใช้ APK signature Scheme v3 ในกระบวนการนี้จะใช้คีย์ที่แตกต่างกัน 2 คีย์

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

APEX ในพาร์ติชันในตัว

ไฟล์ APEX อาจอยู่ในพาร์ติชันในตัว เช่น /system พาร์ติชันอยู่เหนือ dm-verity อยู่แล้ว ระบบจึงจะต่อเชื่อมไฟล์ APEX โดยตรงผ่านอุปกรณ์ loopback

หากมี APEX อยู่ในพาร์ติชันในตัว คุณจะอัปเดต APEX ได้โดยระบุแพ็กเกจ APEX ที่มีชื่อแพ็กเกจเดียวกันและมากกว่าหรือเท่ากับรหัสเวอร์ชัน APEX ใหม่จะจัดเก็บไว้ใน /data และเช่นเดียวกับ APK เวอร์ชันที่ติดตั้งใหม่จะแทนที่เวอร์ชันที่มีอยู่แล้วในพาร์ติชันในตัว แต่ APEX ต่างจาก APK ตรงที่จะเปิดใช้งานเวอร์ชัน APEX ที่ติดตั้งใหม่หลังจากรีบูตเท่านั้น

ข้อกำหนดของเคอร์เนล

หากต้องการรองรับโมดูลหลักของ APEX ในอุปกรณ์ Android คุณต้องมีฟีเจอร์เคอร์เนล Linux ต่อไปนี้ ไดรเวอร์ loopback และ dm-verity ไดรเวอร์ Loopback จะต่อเชื่อมอิมเมจระบบไฟล์ในโมดูล APEX และ dm-verity จะยืนยันโมดูล APEX

ประสิทธิภาพของไดรเวอร์ Loopback และ dm-verity สำคัญต่อการบรรลุประสิทธิภาพการทำงานของระบบที่ดีเมื่อใช้โมดูล APEX

เวอร์ชันเคอร์เนลที่รองรับ

อุปกรณ์ที่ใช้เคอร์เนลเวอร์ชัน 4.4 ขึ้นไปรองรับโมดูล APEX หลัก อุปกรณ์ใหม่ที่เปิดตัวด้วย Android 10 ขึ้นไป ต้องใช้เคอร์เนลเวอร์ชัน 4.9 ขึ้นไปเพื่อรองรับโมดูล APEX

แพตช์ของเคอร์เนลที่จำเป็น

แพตช์เคอร์เนลที่จำเป็นสำหรับการรองรับโมดูล APEX จะรวมอยู่ในแผนผังทั่วไปของ Android หากต้องการรับแพตช์ที่รองรับ APEX ให้ใช้เวอร์ชันล่าสุดของ Android Common Tree

Kernel เวอร์ชัน 4.4

เวอร์ชันนี้ใช้ได้กับอุปกรณ์ที่อัปเกรดจาก Android 9 เป็น Android 10 เท่านั้นและต้องการสนับสนุนโมดูล APEX หากต้องการรับแพตช์ที่จำเป็น ขอแนะนำให้ผสานลงจาก Branch android-4.4 ต่อไปนี้เป็นรายการแพตช์แต่ละรายการที่จำเป็นสำหรับเคอร์เนลเวอร์ชัน 4.4

  • UPSTREAM: ลูป: เพิ่ม ioctl สำหรับการเปลี่ยนขนาดบล็อกเชิงตรรกะ (4.4)
  • BACKPORT: บล็อก/ลูป: ตั้งค่า hw_sectors (4.4)
  • UPSTREAM: loop: Add LOOP_SET_BLOCK_SIZE in compat ioctl (4.4)
  • ANDROID: mnt: แก้ไข next_descendent (4.4)
  • ANDROID: mnt: การต่อเชื่อมใหม่ควรเผยแพร่ไปยังทาสของทาส (4.4)
  • ANDROID: mnt: Propagate remount correctly (4.4)
  • เปลี่ยนกลับ "ANDROID: เวอร์ชัน dm เวอร์ชัน: เพิ่มขนาดการดึงข้อมูลล่วงหน้าขั้นต่ำ" (4.4)
  • UPSTREAM: loop: drop caches if offset or block_size are changed (4.4)

Kernel เวอร์ชัน 4.9/4.14/4.19

หากต้องการรับแพตช์ที่จำเป็นสำหรับเคอร์เนลเวอร์ชัน 4.9/4.14/4.19 ให้ผสานลงจาก Branch ของ android-common

ตัวเลือกการกำหนดค่าเคอร์เนลที่จำเป็น

รายการต่อไปนี้แสดงข้อกำหนดการกำหนดค่าพื้นฐานสำหรับการรองรับโมดูล APEX ที่เปิดตัวไปใน Android 10 รายการที่มีเครื่องหมายดอกจัน (*) คือข้อกำหนดที่มีใน Android 9 และต่ำกว่า

(*) CONFIG_AIO=Y # AIO support (for direct I/O on loop devices)
CONFIG_BLK_DEV_LOOP=Y # for loop device support
CONFIG_BLK_DEV_LOOP_MIN_COUNT=16 # pre-create 16 loop devices
(*) CONFIG_CRYPTO_SHA1=Y # SHA1 hash for DM-verity
(*) CONFIG_CRYPTO_SHA256=Y # SHA256 hash for DM-verity
CONFIG_DM_VERITY=Y # DM-verity support

ข้อกำหนดพารามิเตอร์บรรทัดคำสั่ง Kernel

หากต้องการรองรับ APEX ให้ตรวจสอบว่าพารามิเตอร์บรรทัดคำสั่งของเคอร์เนลเป็นไปตามข้อกำหนดต่อไปนี้

  • ต้องไม่ตั้งค่า loop.max_loop
  • loop.max_part ต้อง <= 8

สร้าง APEX

ส่วนนี้จะอธิบายวิธีสร้าง APEX โดยใช้ระบบการสร้างของ Android ต่อไปนี้เป็นตัวอย่างของ Android.bp สำหรับ APEX ชื่อ apex.test

apex {
    name: "apex.test",
    manifest: "apex_manifest.json",
    file_contexts: "file_contexts",
    // libc.so and libcutils.so are included in the apex
    native_shared_libs: ["libc", "libcutils"],
    binaries: ["vold"],
    java_libs: ["core-all"],
    prebuilts: ["my_prebuilt"],
    compile_multilib: "both",
    key: "apex.test.key",
    certificate: "platform",
}

ตัวอย่าง apex_manifest.json:

{
  "name": "com.android.example.apex",
  "version": 1
}

file_contexts ตัวอย่าง

(/.*)?           u:object_r:system_file:s0
/sub(/.*)?       u:object_r:sub_file:s0
/sub/file3       u:object_r:file3_file:s0

ประเภทไฟล์และตำแหน่งใน APEX

ประเภทไฟล์ สถานที่ตั้งใน APEX
คลังภาพที่แชร์ /lib และ /lib64 (/lib/arm สำหรับ ARM ที่แปลแล้วใน x86)
ไฟล์สั่งการ /bin
ไลบรารี Java /javalib
รายการที่สร้างไว้ล่วงหน้า /etc

ทรัพยากร Dependency แบบทรานซิทีฟ

ไฟล์ APEX จะรวมทรัพยากร Dependency แบบทรานซิทีฟของไลบรารีที่ใช้ร่วมกันหรือไฟล์ปฏิบัติการในเครื่องโดยอัตโนมัติ เช่น หาก libFoo ขึ้นอยู่กับ libBar ระบบจะรวม Lib ทั้ง 2 รายการเมื่อมีเพียง libFoo เท่านั้นที่แสดงอยู่ในพร็อพเพอร์ตี้ native_shared_libs

จัดการ ABI หลายรายการ

ติดตั้งพร็อพเพอร์ตี้ native_shared_libs สําหรับทั้งอินเทอร์เฟซแบบไบนารีของแอปพลิเคชัน (ABI) หลักและรองของอุปกรณ์ หาก APEX กำหนดเป้าหมายเป็นอุปกรณ์ที่มี ABI เดียว (เช่น 32 บิตเท่านั้นหรือ 64 บิตเท่านั้น) ระบบจะติดตั้งเฉพาะไลบรารีที่มี ABI ที่เกี่ยวข้องเท่านั้น

ติดตั้งพร็อพเพอร์ตี้ binaries สำหรับ ABI หลักของอุปกรณ์เท่านั้นตามที่อธิบายไว้ด้านล่าง

  • หากอุปกรณ์เป็นแบบ 32 บิตเท่านั้น ระบบจะติดตั้งไบนารีเวอร์ชัน 32 บิตเท่านั้น
  • หากอุปกรณ์เป็นแบบ 64 บิตเท่านั้น ระบบจะติดตั้งไบนารีเวอร์ชัน 64 บิตเท่านั้น

หากต้องการเพิ่มการควบคุม ABI ของไลบรารีและไบนารีเนทีฟแบบละเอียด ให้ใช้พร็อพเพอร์ตี้multilib.[first|lib32|lib64|prefer32|both].[native_shared_libs|binaries]

  • first: ตรงกับ ABI หลักของอุปกรณ์ ตัวเลือกนี้เป็นค่าเริ่มต้นสำหรับไบนารี
  • lib32: ตรงกับ ABI 32 บิตของอุปกรณ์ หากรองรับ
  • lib64: ตรงกับ ABI 64 บิตของอุปกรณ์ที่รองรับ
  • prefer32: จับคู่ ABI 32 บิตของอุปกรณ์ หากรองรับ หากระบบไม่รองรับ ABI 32 บิต ให้จับคู่กับ ABI 64 บิต
  • both: ตรงกับ ABI ทั้ง 2 รายการ ซึ่งเป็นค่าเริ่มต้นสำหรับ native_shared_libraries

พร็อพเพอร์ตี้ java, libraries และ prebuilts ใช้ร่วมกับ ABI ได้

ตัวอย่างนี้มีไว้สำหรับอุปกรณ์ที่รองรับ 32/64 และไม่ต้องการเลือก 32

apex {
    // other properties are omitted
    native_shared_libs: ["libFoo"], // installed for 32 and 64
    binaries: ["exec1"], // installed for 64, but not for 32
    multilib: {
        first: {
            native_shared_libs: ["libBar"], // installed for 64, but not for 32
            binaries: ["exec2"], // same as binaries without multilib.first
        },
        both: {
            native_shared_libs: ["libBaz"], // same as native_shared_libs without multilib
            binaries: ["exec3"], // installed for 32 and 64
        },
        prefer32: {
            native_shared_libs: ["libX"], // installed for 32, but not for 64
        },
        lib64: {
            native_shared_libs: ["libY"], // installed for 64, but not for 32
        },
    },
}

การเซ็น vbmeta

ลงชื่อ APEX แต่ละรายการด้วยคีย์ที่แตกต่างกัน เมื่อต้องใช้คีย์ใหม่ ให้สร้างคู่คีย์สาธารณะ-ส่วนตัวและสร้างโมดูล apex_key ใช้พร็อพเพอร์ตี้ key เพื่อลงนาม APEX โดยใช้คีย์ดังกล่าว คีย์สาธารณะจะรวมอยู่ใน APEX โดยอัตโนมัติด้วยชื่อ avb_pubkey

# create an rsa key pair
openssl genrsa -out foo.pem 4096

# extract the public key from the key pair
avbtool extract_public_key --key foo.pem --output foo.avbpubkey

# in Android.bp
apex_key {
    name: "apex.test.key",
    public_key: "foo.avbpubkey",
    private_key: "foo.pem",
}

ในตัวอย่างข้างต้น ชื่อของคีย์สาธารณะ (foo) จะกลายเป็นรหัสของคีย์ รหัสของคีย์ที่ใช้ลงนามใน APEX จะเขียนอยู่ใน APEX ขณะรันไทม์ apexd จะยืนยัน APEX โดยใช้คีย์สาธารณะที่มีรหัสเดียวกันในอุปกรณ์

การลงนาม APEX

รับรอง APEX ในลักษณะเดียวกันกับที่รับรอง APK ลงนาม APEX 2 ครั้ง โดยลงนาม 1 ครั้งสำหรับระบบไฟล์ขนาดเล็ก (ไฟล์ apex_payload.img) และลงนาม 1 ครั้งสำหรับทั้งไฟล์

หากต้องการลงนาม APEX ที่ระดับไฟล์ ให้ตั้งค่าพร็อพเพอร์ตี้ certificate ด้วยวิธีใดวิธีหนึ่งใน 3 วิธีต่อไปนี้

  • ไม่ได้ตั้งค่า: หากไม่ได้ตั้งค่าไว้ APEX จะลงนามด้วยใบรับรองที่ PRODUCT_DEFAULT_DEV_CERTIFICATE หากไม่ได้ตั้งค่า Flag ไว้ เส้นทางจะมีค่าเริ่มต้นเป็น build/target/product/security/testkey
  • <name>: APEX ลงนามด้วยใบรับรอง <name> ในไดเรกทอรีเดียวกับ PRODUCT_DEFAULT_DEV_CERTIFICATE
  • :<name>: APEX ลงนามด้วยใบรับรองที่กำหนดโดยโมดูล Soong ที่ชื่อ <name> โมดูลใบรับรองสามารถกำหนดได้ดังนี้
android_app_certificate {
    name: "my_key_name",
    certificate: "dir/cert",
    // this will use dir/cert.x509.pem (the cert) and dir/cert.pk8 (the private key)
}

ติดตั้ง APEX

หากต้องการติดตั้ง APEX ให้ใช้ ADB

adb install apex_file_name
adb reboot

หากตั้งค่า supportsRebootlessUpdate เป็น true ใน apex_manifest.json และ APEX ที่ติดตั้งอยู่ในปัจจุบันไม่ได้ใช้งาน (เช่น บริการทั้งหมดใน APEX หยุดทำงานแล้ว) คุณจะติดตั้ง APEX ใหม่ได้โดยไม่ต้องรีบูตด้วย Flag --force-non-staged

adb install --force-non-staged apex_file_name

ใช้ APEX

หลังจากรีบูต ระบบจะต่อเชื่อม APEX ที่ไดเรกทอรี /apex/<apex_name>@<version> สามารถต่อเชื่อม APEX เดียวกันได้หลายเวอร์ชันพร้อมกัน ในบรรดาเส้นทางการต่อเชื่อม เส้นทางที่สอดคล้องกับเวอร์ชันล่าสุดจะได้รับการต่อเชื่อมแบบ "การเชื่อมโยง" ที่ /apex/<apex_name>

ไคลเอ็นต์สามารถใช้เส้นทางที่ผูกไว้เพื่ออ่านหรือเรียกใช้ไฟล์จาก APEX

โดยทั่วไป APEX จะใช้ในกรณีต่อไปนี้

  1. OEM หรือ ODM โหลด APEX ล่วงหน้าภายใต้ /system/apex เมื่อมีการจัดส่งอุปกรณ์
  2. ไฟล์ใน APEX จะเข้าถึงได้ผ่านเส้นทาง /apex/<apex_name>/
  3. เมื่อติดตั้ง APEX เวอร์ชันอัปเดตใน /data/apex แล้ว เส้นทางจะชี้ไปยัง APEX เวอร์ชันใหม่หลังจากรีบูต

อัปเดตบริการด้วย APEX

วิธีอัปเดตบริการโดยใช้ APEX

  1. ทำเครื่องหมายบริการในพาร์ติชันระบบเป็นอัปเดตได้ เพิ่มตัวเลือก updatable ลงในคำจำกัดความของบริการ

    /system/etc/init/myservice.rc:
    
    service myservice /system/bin/myservice
        class core
        user system
        ...
        updatable
    
  2. สร้างไฟล์ .rc ใหม่สำหรับบริการที่อัปเดต ใช้ตัวเลือก override เพื่อกำหนดบริการที่มีอยู่ใหม่

    /apex/my.apex/etc/init.rc:
    
    service myservice /apex/my.apex/bin/myservice
        class core
        user system
        ...
        override
    

คุณจะกำหนดคำจำกัดความของบริการได้ในไฟล์ .rc ของ APEX เท่านั้น APEX ไม่รองรับทริกเกอร์การดำเนินการ

หากบริการที่ทําเครื่องหมายว่าอัปเดตได้เริ่มทํางานก่อนที่ APEX จะเปิดใช้งาน การเริ่มจะล่าช้าจนกว่าการเปิดใช้งาน APEX จะเสร็จสมบูรณ์

กำหนดค่าระบบให้รองรับการอัปเดต APEX

ตั้งค่าพร็อพเพอร์ตี้ระบบต่อไปนี้เป็น true เพื่อรองรับการอัปเดตไฟล์ APEX

<device.mk>:

PRODUCT_PROPERTY_OVERRIDES += ro.apex.updatable=true

BoardConfig.mk:
TARGET_FLATTEN_APEX := false

หรือเพียงแค่

<device.mk>:

$(call inherit-product, $(SRC_TARGET_DIR)/product/updatable_apex.mk)

APEX ที่แยกเป็นหลายรายการ

สำหรับอุปกรณ์เดิม การอัปเดตเคอร์เนลเก่าเพื่อให้รองรับ APEX อย่างสมบูรณ์ไม่ได้ในบางครั้ง เช่น อาจมีการสร้างเคอร์เนลโดยไม่มี CONFIG_BLK_DEV_LOOP=Y ซึ่งจำเป็นสำหรับการต่อเชื่อมภาพระบบไฟล์ภายใน APEX

APEX แบบแบนราบเป็น APEX ที่สร้างขึ้นมาเป็นพิเศษและจะเปิดใช้งานได้ในอุปกรณ์ที่มีเคอร์เนลเดิม ไฟล์ใน APEX ที่รวมแล้วจะติดตั้งโดยตรงลงในไดเรกทอรีภายใต้พาร์ติชันในตัว เช่น lib/libFoo.so ใน APEX ที่แปลงเป็นตาราง my.apex ติดตั้งใน /system/apex/my.apex/lib/libFoo.so

การเปิดใช้งาน APEX ที่แยกเป็นหลายรายการจะไม่เกี่ยวข้องกับอุปกรณ์วนซ้ำ ไดเรกทอรี /system/apex/my.apex ทั้งหมดจะได้รับการเชื่อมโยงโดยตรงกับ /apex/name@ver

คุณจะอัปเดต APEX ที่แยกเป็นหลายรายการไม่ได้ด้วยการดาวน์โหลด APEX เวอร์ชันอัปเดตจากเครือข่าย เพราะจะแยก APEX ที่ดาวน์โหลดไม่ได้ APEX ที่ผสานรวมจะอัปเดตได้ผ่าน OTA ปกติเท่านั้น

APEX ที่แยกเป็นหลายรายการคือการกำหนดค่าเริ่มต้น ซึ่งหมายความว่า APEX ทั้งหมดจะได้รับการแปลงเป็นไฟล์เดียวโดยค่าเริ่มต้น เว้นแต่คุณจะกําหนดค่าอุปกรณ์อย่างชัดเจนเพื่อสร้าง APEX ที่ไม่ได้แปลงเป็นไฟล์เดียวเพื่อรองรับการอัปเดต APEX (ตามที่อธิบายไว้ข้างต้น)

ระบบไม่รองรับการผสม APEX แบบแบนและแบบไม่แบนในอุปกรณ์ APEX ในอุปกรณ์ต้องเป็นแบบไม่แบนหรือราบเรียบทั้งหมด ซึ่งสำคัญอย่างยิ่งเมื่อจัดส่ง APEX ที่คอมไพล์ไว้ล่วงหน้าซึ่งลงชื่อไว้ล่วงหน้าสำหรับโปรเจ็กต์ เช่น Mainline APEX ที่ไม่ได้เซ็นชื่อไว้ล่วงหน้า (สร้างจากต้นทาง) ไม่ควรเป็นแบบแบนราบและลงนามด้วยคีย์ที่เหมาะสม อุปกรณ์ควรรับค่าจาก updatable_apex.mk ตามที่อธิบายไว้ในการอัปเดตบริการด้วย APEX

APEX ที่บีบอัด

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

การบีบอัด APEX ช่วยลดผลกระทบของพื้นที่เก็บข้อมูลนี้ด้วยการใช้ชุดไฟล์ APEX ที่มีการบีบอัดสูงบนพาร์ติชันแบบอ่านอย่างเดียว (เช่น พาร์ติชัน /system) Android 12 ขึ้นไปใช้อัลกอริทึมการบีบอัด ZIP แบบ DEFLATE

การบีบอัดไม่มีการเพิ่มประสิทธิภาพสำหรับสิ่งต่อไปนี้

  • APEX ที่ใช้บูตซึ่งต้องได้รับการต่อเชื่อมในช่วงต้นของลำดับการบูต

  • APEXes ที่ไม่สามารถอัปเดตได้ การบีบอัดจะมีประโยชน์ก็ต่อเมื่อมีการติดตั้ง APEX เวอร์ชันที่อัปเดตแล้วในพาร์ติชัน /data ดูรายการ APEX ที่อัปเดตได้ทั้งหมดได้ในหน้าคอมโพเนนต์ของระบบแบบโมดูล

  • APEXes ไลบรารีที่ใช้ร่วมกันแบบไดนามิก เนื่องจาก apexd เปิดใช้งาน APEX ดังกล่าวทั้ง 2 เวอร์ชันเสมอ (ที่ติดตั้งไว้ล่วงหน้าและที่อัปเกรด) การบีบอัดจึงไม่มีประโยชน์

รูปแบบไฟล์ APEX ที่บีบอัด

นี่คือรูปแบบของไฟล์ APEX ที่บีบอัด

แผนภาพแสดงรูปแบบของไฟล์ APEX ที่บีบอัด

รูปที่ 2 รูปแบบไฟล์ APEX ที่บีบอัด

ที่ระดับบนสุด ไฟล์ APEX ที่บีบอัดจะเป็นไฟล์ ZIP ที่มีไฟล์ APEX ต้นฉบับในรูปแบบที่ไม่มีการบีบอัดโดยมีระดับการบีบอัด 9 และไฟล์อื่นๆ ที่เก็บไว้โดยไม่มีการบีบอัด

ไฟล์ APEX 1 ไฟล์ประกอบด้วยไฟล์ 4 ไฟล์ ดังนี้

  • original_apex: มีการขยายไฟล์ด้วยระดับการบีบอัด 9 นี่คือไฟล์ APEX ต้นฉบับที่ไม่มีการบีบอัด
  • apex_manifest.pb: จัดเก็บเท่านั้น
  • AndroidManifest.xml: จัดเก็บเท่านั้น
  • apex_pubkey: เก็บไว้เท่านั้น

ไฟล์ apex_manifest.pb, AndroidManifest.xml และ apex_pubkey เป็นสำเนาของไฟล์ที่เกี่ยวข้องใน original_apex

สร้าง APEX ที่บีบอัด

คุณสร้าง APEX ที่บีบอัดได้โดยใช้เครื่องมือ apex_compression_tool.py ที่ system/apex/tools

มีพารามิเตอร์หลายรายการที่เกี่ยวข้องกับการบีบอัด APEX อยู่ในระบบบิลด์

ใน Android.bp ว่าพร็อพเพอร์ตี้ compressible จะควบคุมไฟล์ APEX ที่บีบอัดได้หรือไม่

apex {
    name: "apex.test",
    manifest: "apex_manifest.json",
    file_contexts: "file_contexts",
    compressible: true,
}

Flag ผลิตภัณฑ์ PRODUCT_COMPRESSED_APEX จะควบคุมว่าอิมเมจระบบที่สร้างจากแหล่งที่มาต้องมีไฟล์ APEX ที่บีบอัดหรือไม่

สำหรับการทดลองในเครื่อง คุณบังคับให้บิลด์บีบอัด APEX ได้โดยการตั้งค่า OVERRIDE_PRODUCT_COMPRESSED_APEX= เป็น true

ไฟล์ APEX ที่บีบอัดซึ่งระบบบิลด์สร้างขึ้นจะมีนามสกุล .capex ส่วนขยายนี้ช่วยให้คุณแยกความแตกต่างระหว่างไฟล์ APEX เวอร์ชันที่บีบอัดและไม่บีบอัดได้ง่ายขึ้น

อัลกอริทึมการบีบอัดที่รองรับ

Android 12 รองรับเฉพาะการบีบอัดแบบ Deflate-zip

เปิดใช้งานไฟล์ APEX ที่บีบอัดระหว่างการบูต

ก่อนที่จะเปิดใช้งาน APEX ที่บีบอัด ระบบจะแตกไฟล์ original_apex ในไฟล์นั้นลงในไดเรกทอรี /data/apex/decompressed ไฟล์ APEX ที่แตกไฟล์แล้วจะฮาร์ดลิงก์อยู่กับไดเรกทอรี /data/apex/active

ตัวอย่างต่อไปนี้เป็นภาพประกอบของกระบวนการที่อธิบายไว้ข้างต้น

ให้ถือว่า /system/apex/com.android.foo.capex เป็น APEX ที่บีบอัดซึ่งเปิดใช้งานอยู่ โดยมีรหัสเวอร์ชัน 37

  1. ไฟล์ original_apex ใน /system/apex/com.android.foo.capex ได้รับการขยายไฟล์เป็น /data/apex/decompressed/com.android.foo@37.apex
  2. มีการดำเนินการ restorecon /data/apex/decompressed/com.android.foo@37.apex เพื่อยืนยันว่ามีป้ายกำกับ SELinux ที่ถูกต้อง
  3. การตรวจสอบการยืนยันจะดำเนินการกับ /data/apex/decompressed/com.android.foo@37.apex เพื่อความถูกต้อง apexd ตรวจสอบคีย์สาธารณะที่รวมกลุ่มใน /data/apex/decompressed/com.android.foo@37.apex เพื่อยืนยันว่าคีย์เท่ากับคีย์ที่รวมอยู่ใน /system/apex/com.android.foo.capex
  4. ไฟล์ /data/apex/decompressed/com.android.foo@37.apex ลิงก์ถาวรกับไดเรกทอรี /data/apex/active/com.android.foo@37.apex
  5. ตรรกะการเปิดใช้งานปกติสำหรับไฟล์ APEX ที่ไม่ได้บีบอัดจะดำเนินการใน /data/apex/active/com.android.foo@37.apex

การโต้ตอบกับ OTA

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

apexd จะแสดง Binder API ทั้ง 2 รายการต่อไปนี้เพื่อรองรับระบบ OTA

  • calculateSizeForCompressedApex คำนวณขนาดที่ต้องใช้ในการขยายไฟล์ APEX ในแพ็กเกจ OTA ซึ่งสามารถใช้เพื่อยืนยันว่าอุปกรณ์มีพื้นที่เพียงพอก่อนที่จะดาวน์โหลด OTA
  • reserveSpaceForCompressedApex - สำรองพื้นที่ในดิสก์ไว้ใช้ในอนาคตโดย apexd สำหรับการขยายไฟล์ APEX ที่บีบอัดภายในแพ็กเกจ OTA

ในกรณีของการอัปเดต OTA แบบ A/B apexd จะพยายามทำการขยายไฟล์ในเบื้องหลังโดยเป็นส่วนหนึ่งของกิจวัตร OTA หลังการติดตั้ง หากการขยายข้อมูลไม่สำเร็จ apexd จะดำเนินการคลายการบีบอัดระหว่างเปิดเครื่องที่ใช้การอัปเดต OTA

ทางเลือกที่นำมาพิจารณาเมื่อพัฒนา APEX

ต่อไปนี้เป็นตัวเลือกบางส่วนที่ AOSP นำมาใช้เมื่อออกแบบรูปแบบไฟล์ APEX และเหตุผลที่รูปแบบดังกล่าวถูกรวมหรือยกเว้น

ระบบการจัดการแพ็กเกจปกติ

ดิสทริบิวชัน Linux มีระบบการจัดการแพ็กเกจอย่าง dpkg และ rpm ซึ่งทรงพลัง สมบูรณ์แบบ และมีประสิทธิภาพ แต่ไม่ได้นำมาใช้ใน APEX เนื่องจากไม่สามารถปกป้องแพ็กเกจหลังการติดตั้ง การยืนยันจะดำเนินการเมื่อมีการติดตั้งแพ็กเกจเท่านั้น ผู้โจมตีสามารถทำลายความสมบูรณ์ของแพ็กเกจที่ติดตั้งได้โดยที่ผู้ใช้ไม่รู้ตัว นี่เป็นการถดถอยสำหรับ Android ที่เก็บคอมโพเนนต์ทั้งหมดของระบบไว้ในระบบไฟล์แบบอ่านอย่างเดียวซึ่งมีการปกป้องความสมบูรณ์ด้วย dm-verity ใน I/O ทุกครั้ง ต้องห้ามไม่ให้มีการเปลี่ยนแปลงใดๆ ในคอมโพเนนต์ของระบบ หรือต้องตรวจพบการเปลี่ยนแปลงดังกล่าวเพื่อให้อุปกรณ์ปฏิเสธการบูตหากถูกบุกรุก

dm-crypt สำหรับความสมบูรณ์

ไฟล์ในคอนเทนเนอร์ APEX มาจากพาร์ติชันในตัว (เช่น พาร์ติชัน /system) ที่ปกป้องโดย dm-verity ซึ่งไม่อนุญาตให้แก้ไขไฟล์แม้หลังจากติดตั้งพาร์ติชันแล้วก็ตาม เพื่อให้ไฟล์มีความปลอดภัยในระดับเดียวกัน ไฟล์ทั้งหมดใน APEX จะจัดเก็บอยู่ในอิมเมจระบบไฟล์ที่จับคู่กับแฮชทรีและข้อบ่งชี้ vbmeta หากไม่มี DMEX แล้ว APEX ในพาร์ติชัน /data จะมีช่องโหว่ให้ทำการแก้ไขโดยไม่ตั้งใจหลังจากได้รับการยืนยันและติดตั้งแล้ว

ที่จริงแล้วพาร์ติชัน /data ยังได้รับการปกป้องโดยเลเยอร์การเข้ารหัส เช่น dm-crypt ด้วย แม้ว่าวิธีนี้จะป้องกันการปลอมแปลงได้ในระดับหนึ่ง แต่วัตถุประสงค์หลักคือความเป็นส่วนตัว ไม่ใช่ความซื่อสัตย์ เมื่อผู้โจมตีเข้าถึงพาร์ติชัน /data ได้ ระบบจะไม่สามารถปกป้องเพิ่มเติมได้อีก และนี่ก็ถือเป็นการถดถอยอีกเมื่อเทียบกับการที่คอมโพเนนต์ของระบบทั้งหมดอยู่ในพาร์ติชัน /system ต้นไม้แฮชภายในไฟล์ APEX ร่วมกับ dm-verity จะให้การป้องกันเนื้อหาในระดับเดียวกัน

เปลี่ยนเส้นทางจาก /system ไปยัง /apex

ไฟล์คอมโพเนนต์ของระบบที่แพ็กเกจใน APEX จะเข้าถึงได้ผ่านเส้นทางใหม่ เช่น /apex/<name>/lib/libfoo.so เมื่อไฟล์เป็นส่วนหนึ่งของพาร์ติชัน /system คุณจะเข้าถึงไฟล์ได้ผ่านเส้นทาง เช่น /system/lib/libfoo.so โปรแกรมไคลเอ็นต์ของไฟล์ APEX (ไฟล์ APEX อื่นๆ หรือแพลตฟอร์ม) ต้องใช้เส้นทางใหม่ คุณอาจต้องอัปเดตโค้ดที่มีอยู่เนื่องจากมีการเปลี่ยนเส้นทาง

แม้ว่าวิธีหนึ่งที่จะหลีกเลี่ยงการเปลี่ยนเส้นทางได้คือการซ้อนทับเนื้อหาไฟล์ในไฟล์ APEX บนพาร์ติชัน /system แต่ทีม Android ตัดสินใจที่จะไม่วางซ้อนไฟล์บนพาร์ติชัน /system เพราะอาจส่งผลต่อประสิทธิภาพเนื่องจากจำนวนไฟล์ที่วางซ้อน (อาจจะกองซ้อนกันต่อกัน) เพิ่มขึ้น

อีกทางเลือกหนึ่งคือการลักลอบใช้ฟังก์ชันการเข้าถึงไฟล์ เช่น open, stat และ readlink เพื่อให้ระบบเปลี่ยนเส้นทางเส้นทางที่ขึ้นต้นด้วย /system ไปยังเส้นทางที่เกี่ยวข้องภายใต้ /apex ทีม Android ยกเลิกตัวเลือกนี้แล้ว เพราะเปลี่ยนแปลงฟังก์ชันทั้งหมดที่ยอมรับเส้นทางไม่ได้ ตัวอย่างเช่น แอปบางแอปลิงก์ Bionic ในเชิงสถิติ ซึ่งจะใช้ฟังก์ชันต่างๆ ในกรณีดังกล่าว แอปเหล่านั้นจะไม่ถูกเปลี่ยนเส้นทาง