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

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

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

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

นอกจากนี้ คุณยังต้องใช้สถานะการทำงานของเครื่องจักรที่แสดงด้านล่างด้วย

รูปที่ 1 สถานะการทำงานของ Bootloader

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

วิธีใช้การอัปเดตระบบ A/B

  1. เลือกชุดแพตช์เคอร์เนลต่อไปนี้ (หากจำเป็น)
  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>

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

บูตโหลดเดอร์ที่พร้อมใช้งาน A/B ต้องเป็นไปตามเกณฑ์ตัวแปรการสร้างต่อไปนี้

ต้องกําหนดสําหรับเป้าหมาย A/B
  • AB_OTA_UPDATER := true
  • AB_OTA_PARTITIONS := \
      boot \
      system \
      vendor
    และพาร์ติชันอื่นๆ ที่อัปเดตผ่าน update_engine (วิทยุ บูตโหลดเดอร์ เป็นต้น)
  • 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 สำหรับการกู้คืนจะอยู่ในไฟล์ boot.img เมื่อเข้าสู่การกู้คืน บูตโหลดเดอร์ไม่สามารถใส่ตัวเลือก skip_initramfs ในบรรทัดคำสั่งเคอร์เนล

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

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

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

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

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

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

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

  • has-slot:<partition-base-name-without-suffix> แสดงผลเป็น "ใช่" หากพาร์ติชันที่ระบุรองรับสล็อต และแสดงผลเป็น "ไม่" ในกรณีอื่น
  • 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 ใดก็ได้ที่กําหนดไว้ในดิสก์เดียวกัน พาร์ติชันคู่หนึ่งจะมีคำนำหน้า (เช่น system หรือ boot) และคำต่อท้ายต่อแต่ละช่อง (เช่น _a) รายการพาร์ติชันที่เครื่องมือสร้างเพย์โหลดกำหนดการอัปเดตจะได้รับการกําหนดค่าโดยตัวแปร AB_OTA_PARTITIONS make

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

AB_OTA_PARTITIONS := \
  boot \
  system \
  bootloader

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

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

คุณสามารถกําหนดค่าขั้นตอนหลังการติดตั้งให้แตกต่างกันสําหรับพาร์ติชันที่อัปเดตแต่ละรายการได้โดยใช้ชุดคู่คีย์-ค่า หากต้องการเรียกใช้โปรแกรมที่อยู่ที่ /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

คอมไพล์แอป

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

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