รูปแบบไฟล์ 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 ไฟล์นี้เป็นบัฟเฟอร์โปรโตคอลรูปแบบ JSON ของ ApexManifest

ไฟล์ 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 รองรับไฟล์ประเภทต่อไปนี้

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

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

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

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

การแก้ไขเคอร์เนลที่จำเป็น

การแก้ไขเคอร์เนลที่จำเป็นสำหรับการรองรับโมดูล APEX จะรวมอยู่ในต้นไม้ทั่วไปของ Android หากต้องการรับแพตช์ที่รองรับ 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)
  • UPSTREAM: loop: Add LOOP_SET_BLOCK_SIZE in compat ioctl (4.4)
  • ANDROID: mnt: แก้ไข next_descendent (4.4)
  • ANDROID: mnt: remount should propagate to slaves of slaves (4.4)
  • ANDROID: mnt: Propagate remount correctly (4.4)
  • เปลี่ยนกลับ "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 สำหรับ ARM ที่แปลแล้วใน x86)
ไฟล์สั่งการ /bin
ไลบรารี Java /javalib
รายการที่สร้างไว้ล่วงหน้า /etc

Dependency แบบทรานซิทีฟ

ไฟล์ APEX จะรวมการพึ่งพาแบบทรานซิทีฟของไลบรารีที่แชร์หรือไฟล์ปฏิบัติการแบบเนทีฟโดยอัตโนมัติ เช่น หาก libFoo ขึ้นอยู่กับ libBar ระบบจะรวมทั้ง 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 ด้วยวิธีใดวิธีหนึ่งต่อไปนี้

  • ไม่ได้ตั้งค่า: หากไม่ได้ตั้งค่า ระบบจะเซ็นชื่อ 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 ที่ยุบไม่ได้เกี่ยวข้องกับอุปกรณ์ 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

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

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

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

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

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

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

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

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

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

ไฟล์ APEX ประกอบด้วยไฟล์ 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 ความสามารถในการบีบอัดไฟล์ APEX จะควบคุมโดยพร็อพเพอร์ตี้ compressible ดังนี้

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 จะจัดเก็บไว้ในไฟล์ภาพระบบที่จับคู่กับ Hash Tree และตัวระบุ vbmeta เพื่อให้ไฟล์มีการรักษาความปลอดภัยในระดับเดียวกัน หากไม่มี DM-Verity นั้น 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 แบบคงที่ ซึ่งใช้ฟังก์ชันดังกล่าว ในกรณีเช่นนี้ แอปเหล่านั้นจะไม่ได้รับการเปลี่ยนเส้นทาง