รูปแบบไฟล์ APEX

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

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

ฉากหลัง

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

  • ใช้โมดูลที่อิงตาม APK ในช่วงต้นของลำดับการบูตไม่ได้ Package Manager เป็นที่เก็บข้อมูลส่วนกลางเกี่ยวกับแอปและจะเริ่มได้จาก Activity Manager เท่านั้น ซึ่งจะพร้อมใช้งานในขั้นตอนต่อๆ ไปของ กระบวนการบูต
  • รูปแบบ 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.*
    • สงวนไว้สำหรับ AOSP APEX ไม่เฉพาะเจาะจงกับบริษัทหรืออุปกรณ์ใดๆ
  • com.<companyname>.*
    • สงวนไว้สำหรับบริษัท อุปกรณ์หลายเครื่องจากบริษัทนั้นอาจใช้ข้อมูลนี้
  • com.<companyname>.<devicename>.*
    • สงวนไว้สำหรับ APEX ที่ไม่ซ้ำกันสำหรับอุปกรณ์เฉพาะ (หรือกลุ่มย่อยของอุปกรณ์)

ผู้จัดการ APEX

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

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

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

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

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

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

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

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

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

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

  • ไลบรารีที่แชร์แบบเนทีฟ
  • ไฟล์ที่เรียกใช้งานได้แบบเนทีฟ
  • ไฟล์ 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 mainline ในอุปกรณ์ Android คุณจะต้องมีฟีเจอร์เคอร์เนล Linux ต่อไปนี้ ได้แก่ ไดรเวอร์ Loopback และ dm-verity ไดรเวอร์ Loopback จะติดตั้งอิมเมจระบบไฟล์ในโมดูล APEX และ dm-verity จะยืนยันโมดูล APEX

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

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

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

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

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

เคอร์เนลเวอร์ชัน 4.4

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

  • UPSTREAM: loop: add ioctl for changing logical block size (4.4)
  • BACKPORT: block/loop: set hw_sectors (4.4)
  • ต้นทาง: loop: Add LOOP_SET_BLOCK_SIZE in compat ioctl (4.4)
  • ANDROID: mnt: Fix next_descendent (4.4)
  • ANDROID: mnt: remount should propagate to slaves of slaves (4.4)
  • ANDROID: mnt: Propagate remount correctly (4.4)
  • Revert "ANDROID: dm verity: add minimum prefetch size" (4.4)
  • UPSTREAM: loop: drop caches if offset or block_size are changed (4.4)

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

หากต้องการรับแพตช์ที่จำเป็นสำหรับเคอร์เนลเวอร์ชัน 4.9/4.14/4.19 ให้ดาวน์โหลดและผสานจากสาขา 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

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

หากต้องการรองรับ 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 สำหรับ อาร์มที่แปลแล้วใน x86)
ไฟล์สั่งการ /bin
ไลบรารี Java /javalib
สำเร็จรูป /etc

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

ไฟล์ APEX จะรวมการอ้างอิงแบบทรานซิทีฟของไลบรารีที่แชร์แบบเนทีฟ หรือไฟล์ที่เรียกใช้งานได้โดยอัตโนมัติ ตัวอย่างเช่น หาก 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: ตรงกับทั้ง 2 ABI โดยตัวเลือกนี้จะเป็นค่าเริ่มต้นสำหรับ 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 ครั้ง ครั้งหนึ่งสำหรับ ระบบไฟล์ขนาดเล็ก (ไฟล์ apex_payload.img) และอีกครั้งสำหรับทั้งไฟล์

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

  • ไม่ได้ตั้งค่า: หากไม่ได้ตั้งค่า ระบบจะลงนาม APEX ด้วยใบรับรองที่อยู่ใน PRODUCT_DEFAULT_DEV_CERTIFICATE หากไม่ได้ตั้งค่าแฟล็กไว้ เส้นทางจะเป็นค่าเริ่มต้น เป็น 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 ใหม่ได้โดยไม่ต้องรีบูตด้วยแฟล็ก --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 ที่แบนราบไม่เกี่ยวข้องกับอุปกรณ์ Loop ทั้ง ไดเรกทอรี /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

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

  • Bootstrap APEX ที่ต้องติดตั้งในช่วงต้นๆ ของลำดับการบูต

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

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

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

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

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

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

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

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

  • original_apex: deflated with compression level of 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 พร็อพเพอร์ตี้ต่อไปนี้จะควบคุมว่าไฟล์ APEX บีบอัดได้หรือไม่ compressible

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

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 ที่บีบอัดซึ่งกำลัง เปิดใช้งาน โดยมี versionCode 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 เพื่อให้ไฟล์มี ระดับความปลอดภัยเดียวกัน หากไม่มี dm-verity APEX ในพาร์ติชัน /data จะเสี่ยงต่อการแก้ไขโดยไม่ตั้งใจ ซึ่งเกิดขึ้นหลังจากที่ได้รับการยืนยันและติดตั้งแล้ว

ในความเป็นจริงแล้ว /data พาร์ติชันยังได้รับการปกป้องโดยเลเยอร์การเข้ารหัส เช่น dm-crypt แม้ว่าการดำเนินการนี้จะช่วยป้องกันการดัดแปลงได้ในระดับหนึ่ง แต่ วัตถุประสงค์หลักคือความเป็นส่วนตัว ไม่ใช่ความสมบูรณ์ เมื่อผู้โจมตีได้รับสิทธิ์เข้าถึงพาร์ติชัน /data จะไม่มีการป้องกันเพิ่มเติม และนี่ก็เป็นอีกครั้งที่ การถดถอยเมื่อเทียบกับคอมโพเนนต์ของระบบทุกรายการที่อยู่ในพาร์ติชัน /system Hash Tree ภายในไฟล์ 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 แบบคงที่ ซึ่งจะใช้ฟังก์ชันต่างๆ ในกรณีดังกล่าว ระบบจะไม่เปลี่ยนเส้นทางแอปเหล่านั้น