การนำการอัปเดต A/B ไปใช้

จัดทุกอย่างให้เป็นระเบียบอยู่เสมอด้วยคอลเล็กชัน บันทึกและจัดหมวดหมู่เนื้อหาตามค่ากำหนดของคุณ

OEM และผู้จำหน่าย SoC ที่ต้องการใช้การอัปเดตระบบ A/B ต้องแน่ใจว่าโปรแกรมโหลดบูตของตนใช้ boot_control HAL และส่ง พารามิเตอร์ที่ถูกต้อง ไปยังเคอร์เนล

การใช้งานการควบคุมการบูตHAL

bootloaders ที่รองรับ A/B ต้องใช้ boot_control HAL ที่ hardware/libhardware/include/hardware/boot_control.h คุณสามารถทดสอบการใช้งานโดยใช้ยูทิลิตี้ system/extras/bootctl และ system/extras/tests/bootloader/

คุณต้องใช้เครื่องสถานะที่แสดงด้านล่าง:

รูปที่ 1. เครื่องสถานะ Bootloader

การตั้งค่าเคอร์เนล

การนำการอัปเดตระบบ A/B ไปใช้:

  1. Cherrypick ซีรีส์แพทช์เคอร์เนลต่อไปนี้ (ถ้าจำเป็น):
  2. ตรวจสอบให้แน่ใจว่าอาร์กิวเมนต์บรรทัดคำสั่งเคอร์เนลมีอาร์กิวเมนต์พิเศษต่อไปนี้:
    skip_initramfs rootwait ro init=/init root="/dev/dm-0 dm=system none ro,0 1 android-verity <public-key-id> <path-to-system-partition>"
    ... โดยที่ค่า <public-key-id> คือ ID ของคีย์สาธารณะที่ใช้ตรวจสอบลายเซ็นตารางความเป็นจริง (สำหรับรายละเอียด โปรดดู ที่ dm-verity ) .
  3. เพิ่มใบรับรอง .X509 ที่มีคีย์สาธารณะในคีย์ริงของระบบ:
    1. คัดลอกใบรับรอง .X509 ที่จัดรูปแบบในรูปแบบ . .der ไปยังรูทของไดเร็กทอรี kernel หากใบรับรอง .X509 มีรูปแบบเป็นไฟล์ . .pem ให้ใช้คำสั่ง openssl ต่อไปนี้เพื่อแปลงจากรูปแบบ . .pem เป็น . .der :
      openssl x509 -in <x509-pem-certificate> -outform der -out <x509-der-certificate>
    2. สร้าง zImage เพื่อรวมใบรับรองเป็นส่วนหนึ่งของคีย์ริงของระบบ ในการตรวจสอบ ให้ตรวจสอบรายการ procfs (ต้องเปิดใช้งาน KEYS_CONFIG_DEBUG_PROC_KEYS ):
      angler:/# cat /proc/keys
      
      1c8a217e I------     1 perm 1f010000     0     0 asymmetri
      Android: 7e4333f9bba00adfe0ede979e28ed1920492b40f: X509.RSA 0492b40f []
      2d454e3e I------     1 perm 1f030000     0     0 keyring
      .system_keyring: 1/4
      การรวมใบรับรอง .X509 ที่สำเร็จระบุว่ามีคีย์สาธารณะอยู่ในคีย์ริงของระบบ (ไฮไลต์หมายถึง ID คีย์สาธารณะ)
    3. แทนที่ช่องว่างด้วย # และส่งต่อเป็น <public-key-id> ในบรรทัดคำสั่งเคอร์เนล ตัวอย่างเช่น ส่ง Android:#7e4333f9bba00adfe0ede979e28ed1920492b40f แทน <public-key-id>

การตั้งค่าตัวแปรบิวด์

bootloaders ที่รองรับ A/B ต้องเป็นไปตามเกณฑ์ตัวแปรบิวด์ต่อไปนี้:

ต้องกำหนดสำหรับเป้าหมาย A/B
  • AB_OTA_UPDATER := true
  • AB_OTA_PARTITIONS := \
    boot \
    system \
    vendor
    และพาร์ติชั่นอื่นๆ ที่อัพเดตผ่าน update_engine (วิทยุ, bootloader ฯลฯ)
  • PRODUCT_PACKAGES += \
    update_engine \
    update_verifier
ตัวอย่างเช่น อ้างถึง /device/google/marlin/+/android-7.1.0_r1/device-common.mk คุณสามารถเลือกดำเนินการขั้นตอน dex2oat หลังการติดตั้ง (แต่ก่อนรีบูต) ที่อธิบายไว้ใน การ คอมไพล์
แนะนำเป็นอย่างยิ่งสำหรับเป้าหมาย A/B
  • กำหนด TARGET_NO_RECOVERY := true
  • กำหนด BOARD_USES_RECOVERY_AS_BOOT := true
  • อย่ากำหนด BOARD_RECOVERYIMAGE_PARTITION_SIZE
ไม่สามารถกำหนดเป้าหมาย A/B ได้
  • BOARD_CACHEIMAGE_PARTITION_SIZE
  • BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE
เป็นทางเลือกสำหรับการสร้างดีบัก PRODUCT_PACKAGES_DEBUG += update_engine_client

การตั้งค่าพาร์ติชั่น (สล็อต)

อุปกรณ์ A/B ไม่ต้องการพาร์ติชั่นการกู้คืนหรือพาร์ติชั่นแคช เนื่องจาก Android ไม่ได้ใช้พาร์ติชั่นเหล่านี้อีกต่อไป พาร์ติชั่นข้อมูลถูกใช้สำหรับแพ็คเกจ OTA ที่ดาวน์โหลดมา และรหัสอิมเมจการกู้คืนอยู่บนพาร์ติชั่นสำหรับเริ่มระบบ พาร์ติชั่นทั้งหมดที่เป็น A/B-ed ควรตั้งชื่อดังนี้ (สล็อตมักจะตั้งชื่อว่า a , b ฯลฯ ): boot_a , boot_b , system_a , system_b , vendor_a , vendor_b

แคช

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

การกู้คืน

ขณะนี้ดิสก์ RAM การกู้คืนมีอยู่ในไฟล์ boot.img เมื่อเข้าสู่การกู้คืน bootloader จะไม่สามารถ ใส่ตัวเลือก skip_initramfs บนบรรทัดรับคำสั่งเคอร์เนลได้

สำหรับการอัพเดตที่ไม่ใช่ A/B พาร์ติชั่นการกู้คืนจะมีรหัสที่ใช้ในการอัพเดต การอัปเดต A/B ถูกนำไปใช้โดย update_engine ที่ทำงานอยู่ในอิมเมจระบบที่บูตตามปกติ ยังคงมีโหมดการกู้คืนที่ใช้ในการดำเนินการรีเซ็ตข้อมูลเป็นค่าเริ่มต้นและไซด์โหลดของแพ็คเกจการอัปเดต (ซึ่งเป็นที่มาของชื่อ "การกู้คืน") รหัสและข้อมูลสำหรับโหมดการกู้คืนถูกเก็บไว้ในพาร์ติชันสำหรับเริ่มระบบปกติใน ramdisk ในการบูตเข้าสู่อิมเมจระบบ bootloader จะบอกเคอร์เนลให้ข้าม ramdisk (มิฉะนั้นอุปกรณ์จะบู๊ตเข้าสู่โหมดการกู้คืน โหมดการกู้คืนมีขนาดเล็ก (และส่วนใหญ่อยู่ในพาร์ติชันสำหรับเริ่มระบบแล้ว) ดังนั้นพาร์ติชันสำหรับเริ่มระบบจึงไม่เพิ่มขึ้น ในขนาด.

Fstab

อาร์กิวเมนต์ slotselect ต้อง อยู่ในบรรทัดสำหรับพาร์ติชัน A/B-ed ตัวอย่างเช่น:

<path-to-block-device>/vendor  /vendor  ext4  ro
wait,verify=<path-to-block-device>/metadata,slotselect

ไม่ควรตั้งชื่อพาร์ติชันว่า vendor พาร์ติชัน vendor_a หรือ vendor_b จะถูกเลือกและติดตั้งบนจุดเชื่อมต่อ /vendor แทน

อาร์กิวเมนต์สล็อตเคอร์เนล

ส่วนต่อท้ายสล็อตปัจจุบันควรส่งผ่านโหนดโครงสร้างอุปกรณ์ (DT) เฉพาะ ( /firmware/android/slot_suffix ) หรือผ่านบรรทัดคำสั่งเคอร์เนล androidboot.slot_suffix หรืออาร์กิวเมนต์ bootconfig

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

  • --slot SLOT . แทนที่การทำงานเริ่มต้นและพร้อมต์ให้ fastboot เพื่อแฟลชสล็อตที่ส่งผ่านเป็นอาร์กิวเมนต์
  • --set-active [ SLOT ] . ตั้งค่าช่องเป็นใช้งานอยู่ หากไม่มีการระบุอาร์กิวเมนต์ที่เป็นทางเลือก ช่องปัจจุบันจะถูกตั้งค่าเป็นแอ็คทีฟ
  • fastboot --help . รับรายละเอียดเกี่ยวกับคำสั่ง

หาก bootloader ใช้ fastboot ก็ควรสนับสนุนคำสั่ง set_active <slot> ที่ตั้งค่าช่องที่ใช้งานอยู่ในปัจจุบันเป็นสล็อตที่กำหนด (สิ่งนี้จะต้องล้างแฟล็กที่ไม่สามารถบูตได้สำหรับสล็อตนั้นและรีเซ็ตจำนวนการลองใหม่เป็นค่าเริ่มต้น) bootloader ควรสนับสนุนตัวแปรต่อไปนี้ด้วย:

  • has-slot:<partition-base-name-without-suffix> ส่งคืน "ใช่" หากพาร์ติชันที่ระบุรองรับสล็อต "ไม่" มิฉะนั้น
  • current-slot ส่งกลับส่วนต่อท้ายของสล็อตที่จะบูตจากครั้งถัดไป
  • slot-count ส่งกลับจำนวนเต็มแทนจำนวนช่องที่มีอยู่ ปัจจุบันรองรับสองช่อง ดังนั้นค่านี้คือ 2
  • slot-successful:<slot-suffix> . ส่งคืน "ใช่" หากช่องที่ระบุถูกทำเครื่องหมายว่าบูตสำเร็จ "ไม่" มิฉะนั้น
  • slot-unbootable:<slot-suffix> . ส่งคืน "ใช่" หากช่องที่ระบุถูกทำเครื่องหมายว่าไม่สามารถบูตได้ "ไม่" มิฉะนั้น
  • slot-retry-count . จำนวนครั้งของการลองใหม่ที่เหลือเพื่อพยายามบูตช่องที่กำหนด

หากต้องการดูตัวแปรทั้งหมด ให้รัน fastboot getvar all

กำลังสร้างแพ็คเกจ OTA

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

ตัวอย่าง:

  • ในการสร้าง OTA แบบเต็ม:
    ./build/make/tools/releasetools/ota_from_target_files \
        dist_output/tardis-target_files.zip \
        ota_update.zip
    
  • เพื่อสร้าง OTA ที่เพิ่มขึ้น:
    ./build/make/tools/releasetools/ota_from_target_files \
        -i PREVIOUS-tardis-target_files.zip \
        dist_output/tardis-target_files.zip \
        incremental_ota_update.zip
    

การกำหนดค่าพาร์ติชั่น

update_engine สามารถอัปเดตคู่ของพาร์ติชั่น A/B ที่กำหนดไว้ในดิสก์เดียวกัน พาร์ติชั่นคู่หนึ่งมีคำนำหน้าร่วมกัน (เช่น system หรือ boot ) และส่วนต่อท้ายต่อสล็อต (เช่น _a ) รายการพาร์ติชั่นที่ตัวสร้างเพย์โหลดกำหนดการอัปเดตได้รับการกำหนดค่าโดยตัวแปรสร้าง AB_OTA_PARTITIONS

ตัวอย่างเช่น หากรวมพาร์ติชั่นคู่ bootloader_a และ booloader_b ( _b _a ส่วนต่อท้ายของสล็อต) คุณสามารถอัพเดตพาร์ติชั่นเหล่านี้โดยระบุสิ่งต่อไปนี้บนคอนฟิกูเรชันผลิตภัณฑ์หรือบอร์ด:

AB_OTA_PARTITIONS := \
  boot \
  system \
  bootloader

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

การกำหนดค่าหลังการติดตั้ง

คุณสามารถกำหนดค่าขั้นตอนหลังการติดตั้งแตกต่างกันสำหรับแต่ละพาร์ติชันที่อัพเดตโดยใช้ชุดของคู่คีย์-ค่า ในการรันโปรแกรมที่อยู่ที่ /system/usr/bin/postinst ในอิมเมจใหม่ ให้ระบุพาธที่สัมพันธ์กับรูทของระบบไฟล์ในพาร์ติชันระบบ

ตัวอย่างเช่น usr/bin/postinst คือ system/usr/bin/postinst (หากไม่ได้ใช้ดิสก์ RAM) นอกจากนี้ ให้ระบุประเภทระบบไฟล์ที่จะส่งไปยังการเรียกระบบ mount(2) เพิ่มสิ่งต่อไปนี้ในไฟล์ .mk ของผลิตภัณฑ์หรืออุปกรณ์ (ถ้ามี):

AB_OTA_POSTINSTALL_CONFIG += \
  RUN_POSTINSTALL_system=true \
  POSTINSTALL_PATH_system=usr/bin/postinst \
  FILESYSTEM_TYPE_system=ext4

กำลังรวบรวม

ด้วยเหตุผลด้านความปลอดภัย system_server ไม่สามารถใช้การคอมไพล์ แบบ just-in-time (JIT) ได้ ซึ่งหมายความว่าคุณต้องคอมไพล์ไฟล์ odex ล่วงหน้าสำหรับ system_server และการพึ่งพาอย่างน้อยที่สุด อย่างอื่นเป็นตัวเลือก

ในการคอมไพล์แอปในเบื้องหลัง คุณต้องเพิ่มสิ่งต่อไปนี้ในการกำหนดค่าอุปกรณ์ของผลิตภัณฑ์ (ใน device.mk ของผลิตภัณฑ์):

  1. รวมส่วนประกอบดั้งเดิมในบิลด์เพื่อให้แน่ใจว่าคอมไพล์สคริปต์และไบนารีถูกคอมไพล์และรวมอยู่ในอิมเมจระบบ
      # A/B OTA dexopt package
      PRODUCT_PACKAGES += otapreopt_script
    
  2. เชื่อมต่อสคริปต์การคอมไพล์กับ update_engine ซึ่งทำงานเป็นขั้นตอนหลังการติดตั้ง
      # A/B OTA dexopt update_engine hookup
      AB_OTA_POSTINSTALL_CONFIG += \
        RUN_POSTINSTALL_system=true \
        POSTINSTALL_PATH_system=system/bin/otapreopt_script \
        FILESYSTEM_TYPE_system=ext4 \
        POSTINSTALL_OPTIONAL_system=true
    

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