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

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

ใช้ HAL ของ boot_control

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

นอกจากนี้ คุณยังต้องใช้ State Machine ที่แสดงด้านล่างด้วย

รูปที่ 1 State Machine ของ 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> คือรหัสของคีย์สาธารณะที่ใช้ ยืนยันลายเซ็นตาราง verity (ดูรายละเอียดได้ที่ 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 สำเร็จแสดงว่ามีคีย์สาธารณะในคีย์ริงระบบ (ไฮไลต์จะระบุรหัสคีย์สาธารณะ)
    3. แทนที่ช่องว่างด้วย # และส่งเป็น <public-key-id> ในบรรทัดคำสั่งเคอร์เนล ตัวอย่างเช่น ส่ง Android:#7e4333f9bba00adfe0ede979e28ed1920492b40f แทน <public-key-id>

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

Bootloader ที่ใช้ 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 ควรมีชื่อดังนี้ (สล็อตจะมีชื่อเป็น a, b ฯลฯ เสมอ) boot_a, boot_b, system_a, system_b, vendor_a, vendor_b

แคช

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

การฟื้นตัว

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

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

Fstab

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

<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. Get details on commands.

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

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

หากต้องการดูตัวแปรทั้งหมด ให้เรียกใช้ 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 คู่ใดก็ได้ที่กำหนดไว้ในดิสก์เดียวกัน พาร์ติชัน 2 รายการจะมีคำนำหน้าร่วมกัน (เช่น system หรือ boot) และคำต่อท้ายต่อสล็อต (เช่น _a) รายการพาร์ติชันที่เครื่องมือสร้างเพย์โหลดกำหนดการอัปเดตจะได้รับการกำหนดค่าโดยตัวแปร AB_OTA_PARTITIONS

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

AB_OTA_PARTITIONS := \
  boot \
  system \
  bootloader

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

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

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

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

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

คอมไพล์แอป

คุณสามารถคอมไพล์แอปในเบื้องหลังก่อนรีบูตด้วยอิมเมจระบบใหม่ หากต้องการคอมไพล์ แอปในเบื้องหลัง ให้เพิ่มสิ่งต่อไปนี้ในการกำหนดค่าอุปกรณ์ของผลิตภัณฑ์ (ใน 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
    

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