Triển khai bản cập nhật A/B

Các OEM và nhà cung cấp SoC muốn triển khai bản cập nhật hệ thống A/B phải đảm bảo trình tải khởi động của họ triển khai boot_control HAL và truyền các tham số chính xác đến nhân.

Triển khai HAL điều khiển quá trình khởi động

Trình tải khởi động có khả năng A/B phải triển khai HAL boot_control tại hardware/libhardware/include/hardware/boot_control.h. Bạn có thể kiểm thử các hoạt động triển khai bằng tiện ích system/extras/bootctlsystem/extras/tests/bootloader/.

Bạn cũng phải triển khai máy trạng thái như minh hoạ bên dưới:

Hình 1. Trạng thái trình tải khởi động

Thiết lập nhân

Cách triển khai bản cập nhật hệ thống A/B:

  1. Chọn lọc các bản vá hạt nhân sau đây (nếu cần):
  2. Đảm bảo các đối số dòng lệnh của kernel chứa các đối số bổ sung sau:
    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>"
    ... trong đó giá trị <public-key-id> là mã nhận dạng của khoá công khai dùng để xác minh chữ ký bảng verity (để biết thông tin chi tiết, hãy xem dm-verity).
  3. Thêm chứng chỉ .X509 chứa khoá công khai vào chuỗi khoá hệ thống:
    1. Sao chép chứng chỉ .X509 được định dạng theo định dạng .der vào thư mục gốc của kernel. Nếu chứng chỉ .X509 được định dạng dưới dạng tệp .pem, hãy dùng lệnh openssl sau đây để chuyển đổi từ định dạng .pem sang .der:
      openssl x509 -in <x509-pem-certificate> -outform der -out <x509-der-certificate>
    2. Tạo zImage để đưa chứng chỉ vào một phần của khoá hệ thống. Để xác minh,hãy kiểm tra mục procfs (yêu cầu phải bật 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
      Việc thêm thành công chứng chỉ .X509 cho biết sự hiện diện của khoá công khai trong móc khoá hệ thống (phần nổi bật biểu thị mã nhận dạng khoá công khai).
    3. Thay thế dấu cách bằng # và truyền dấu cách đó dưới dạng <public-key-id> trong dòng lệnh của nhân. Ví dụ: truyền Android:#7e4333f9bba00adfe0ede979e28ed1920492b40f thay cho <public-key-id>.

Đặt các biến bản dựng

Trình tải khởi động có khả năng A/B phải đáp ứng các tiêu chí sau về biến bản dựng:

Phải xác định cho mục tiêu thử nghiệm A/B
  • AB_OTA_UPDATER := true
  • AB_OTA_PARTITIONS := \
      boot \
      system \
      vendor
    và các phân vùng khác được cập nhật thông qua update_engine (đài, trình tải khởi động, v.v.)
  • PRODUCT_PACKAGES += \
      update_engine \
      update_verifier
Để xem ví dụ, hãy tham khảo /device/google/marlin/+/android-7.1.0_r1/device-common.mk. Bạn có thể tuỳ ý thực hiện bước dex2oat sau khi cài đặt (nhưng trước khi khởi động lại) như mô tả trong phần Biên dịch.
Rất nên dùng cho mục tiêu A/B
  • Xác định TARGET_NO_RECOVERY := true
  • Xác định BOARD_USES_RECOVERY_AS_BOOT := true
  • Không xác định BOARD_RECOVERYIMAGE_PARTITION_SIZE
Không thể xác định cho mục tiêu thử nghiệm A/B
  • BOARD_CACHEIMAGE_PARTITION_SIZE
  • BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE
Không bắt buộc đối với bản gỡ lỗi PRODUCT_PACKAGES_DEBUG += update_engine_client

Đặt phân vùng (vị trí)

Các thiết bị A/B không cần phân vùng khôi phục hoặc phân vùng bộ nhớ đệm vì Android không còn sử dụng các phân vùng này nữa. Phân vùng dữ liệu hiện được dùng cho gói OTA đã tải xuống và mã hình ảnh khôi phục nằm trên phân vùng khởi động. Tất cả các phân vùng được thử nghiệm A/B đều phải được đặt tên như sau (các ô luôn được đặt tên là a, b, v.v.): boot_a, boot_b, system_a, system_b, vendor_a, vendor_b.

Bộ nhớ đệm

Đối với các bản cập nhật không phải A/B, phân vùng bộ nhớ đệm được dùng để lưu trữ các gói OTA đã tải xuống và tạm thời lưu trữ các khối trong khi áp dụng bản cập nhật. Chưa bao giờ có cách nào hay để định cỡ phân vùng bộ nhớ đệm: kích thước cần thiết phụ thuộc vào những bản cập nhật mà bạn muốn áp dụng. Trường hợp xấu nhất là phân vùng bộ nhớ đệm có kích thước bằng với hình ảnh hệ thống. Với bản cập nhật A/B, bạn không cần phải lưu trữ các khối (vì bạn luôn ghi vào một phân vùng hiện không được dùng) và với A/B truyền phát trực tiếp, bạn không cần tải toàn bộ gói OTA xuống trước khi áp dụng.

Khôi phục

Đĩa RAM khôi phục hiện nằm trong tệp boot.img. Khi chuyển sang chế độ khôi phục, trình tải khởi động không thể đặt lựa chọn skip_initramfs trên dòng lệnh kernel.

Đối với các bản cập nhật không phải A/B, phân vùng khôi phục chứa mã dùng để áp dụng các bản cập nhật. update_engine chạy trong hình ảnh hệ thống khởi động thông thường sẽ áp dụng các bản cập nhật A/B. Vẫn có một chế độ khôi phục được dùng để triển khai tính năng đặt lại dữ liệu về trạng thái ban đầu và tải gói cập nhật lên thiết bị (đó là lý do có tên "khôi phục"). Mã và dữ liệu cho chế độ khôi phục được lưu trữ trong phân vùng khởi động thông thường trong một ramdisk; để khởi động vào hình ảnh hệ thống, trình tải khởi động sẽ yêu cầu hạt nhân bỏ qua ramdisk (nếu không, thiết bị sẽ khởi động vào chế độ khôi phục. Chế độ khôi phục có kích thước nhỏ (và phần lớn đã có trên phân vùng khởi động), vì vậy, phân vùng khởi động không tăng kích thước.

Fstab

Đối số slotselect phải nằm trên dòng cho các phân vùng A/B. Ví dụ:

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

Không được đặt tên phân vùng là vendor. Thay vào đó, phân vùng vendor_a hoặc vendor_b sẽ được chọn và gắn vào điểm gắn kết /vendor.

Đối số khe cắm kernel

Hậu tố khe cắm hiện tại phải được truyền qua một nút cây thiết bị (DT) cụ thể (/firmware/android/slot_suffix) hoặc thông qua dòng lệnh của hạt nhân androidboot.slot_suffix hoặc đối số bootconfig.

Theo mặc định, fastboot sẽ nhấp nháy khe cắm hiện tại trên thiết bị A/B. Nếu gói cập nhật cũng chứa hình ảnh cho khe cắm khác (không phải khe cắm hiện tại), thì fastboot cũng sẽ flash những hình ảnh đó. Các lựa chọn hiện có bao gồm:

  • --slot SLOT. Ghi đè hành vi mặc định và nhắc fastboot flash khe cắm được truyền vào dưới dạng một đối số.
  • --set-active [SLOT]. Đặt khe cắm ở trạng thái đang hoạt động. Nếu không chỉ định đối số không bắt buộc nào, thì khe hiện tại sẽ được đặt làm khe đang hoạt động.
  • fastboot --help. Xem thông tin chi tiết về các lệnh.

Nếu trình tải khởi động triển khai fastboot, thì trình tải khởi động đó phải hỗ trợ lệnh set_active <slot> đặt khe cắm đang hoạt động hiện tại thành khe cắm đã cho (thao tác này cũng phải xoá cờ không thể khởi động cho khe cắm đó và đặt lại số lần thử lại về giá trị mặc định). Trình tải khởi động cũng phải hỗ trợ các biến sau:

  • has-slot:<partition-base-name-without-suffix>. Trả về "yes" nếu phân vùng đã cho hỗ trợ các vị trí, nếu không thì trả về "no".
  • current-slot. Trả về hậu tố của khe sẽ khởi động từ lần tiếp theo.
  • slot-count. Trả về một số nguyên biểu thị số lượng vị trí còn trống. Hiện tại, hệ thống hỗ trợ 2 khe cắm nên giá trị này là 2.
  • slot-successful:<slot-suffix>. Trả về "yes" nếu khe cắm đã cho được đánh dấu là khởi động thành công, nếu không thì trả về "no".
  • slot-unbootable:<slot-suffix>. Trả về "yes" nếu khe cắm đã cho được đánh dấu là không thể khởi động, "no" nếu ngược lại.
  • slot-retry-count:<slot-suffix>. Số lần thử lại còn lại để cố gắng khởi động khe cắm đã cho.

Để xem tất cả các biến, hãy chạy fastboot getvar all.

Tạo gói OTA

Các công cụ gói OTA tuân theo các lệnh tương tự như các lệnh dành cho thiết bị không phải A/B. Bạn phải tạo tệp target_files.zip bằng cách xác định các biến bản dựng cho mục tiêu A/B. Các công cụ gói OTA sẽ tự động xác định và tạo các gói ở định dạng cho trình cập nhật A/B.

Ví dụ:

  • Cách tạo bản cập nhật OTA đầy đủ:
    ./build/make/tools/releasetools/ota_from_target_files \
        dist_output/tardis-target_files.zip \
        ota_update.zip
    
  • Cách tạo bản cập nhật OTA gia tăng:
    ./build/make/tools/releasetools/ota_from_target_files \
        -i PREVIOUS-tardis-target_files.zip \
        dist_output/tardis-target_files.zip \
        incremental_ota_update.zip
    

Định cấu hình phân vùng

update_engine có thể cập nhật mọi cặp phân vùng A/B được xác định trong cùng một đĩa. Một cặp phân vùng có một tiền tố chung (chẳng hạn như system hoặc boot) và hậu tố cho mỗi khe cắm (chẳng hạn như _a). Danh sách các phân vùng mà trình tạo tải trọng xác định một bản cập nhật được định cấu hình bằng biến AB_OTA_PARTITIONS make.

Ví dụ: nếu một cặp phân vùng bootloader_abooloader_b được đưa vào (_a_b là hậu tố của khe cắm), bạn có thể cập nhật các phân vùng này bằng cách chỉ định những nội dung sau trong cấu hình sản phẩm hoặc bảng:

AB_OTA_PARTITIONS := \
  boot \
  system \
  bootloader

Các phân vùng do update_engine cập nhật không được sửa đổi bởi phần còn lại của hệ thống. Trong quá trình cập nhật gia tăng hoặc delta, dữ liệu nhị phân từ khe cắm hiện tại được dùng để tạo dữ liệu trong khe cắm mới. Mọi nội dung sửa đổi đều có thể khiến dữ liệu về vị trí mới không xác minh được trong quá trình cập nhật, do đó không cập nhật được.

Định cấu hình sau khi cài đặt

Bạn có thể định cấu hình bước sau khi cài đặt theo cách khác nhau cho từng phân vùng được cập nhật bằng cách sử dụng một tập hợp các cặp khoá-giá trị. Để chạy một chương trình nằm ở /system/usr/bin/postinst trong một hình ảnh mới, hãy chỉ định đường dẫn tương ứng với gốc của hệ thống tệp trong phân vùng hệ thống.

Ví dụ: usr/bin/postinstsystem/usr/bin/postinst (nếu không dùng ổ đĩa RAM). Ngoài ra, hãy chỉ định loại hệ thống tệp để truyền đến lệnh gọi hệ thống mount(2). Thêm nội dung sau vào tệp .mk của sản phẩm hoặc thiết bị (nếu có):

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

Biên dịch ứng dụng

Các ứng dụng có thể được biên dịch ở chế độ nền trước khi khởi động lại bằng hình ảnh hệ thống mới. Để biên dịch các ứng dụng ở chế độ nền, hãy thêm nội dung sau vào cấu hình thiết bị của sản phẩm (trong device.mk của sản phẩm):

  1. Đưa các thành phần gốc vào bản dựng để đảm bảo tập lệnh biên dịch và các tệp nhị phân được biên dịch và đưa vào hình ảnh hệ thống.
      # A/B OTA dexopt package
      PRODUCT_PACKAGES += otapreopt_script
    
  2. Kết nối tập lệnh biên dịch với update_engine để chạy dưới dạng một bước sau khi cài đặt.
      # 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
    

Để biết cách cài đặt các tệp được tối ưu hoá trước trong phân vùng hệ thống thứ hai chưa dùng đến, hãy tham khảo phần Cài đặt khởi động đầu tiên của các tệp DEX_PREOPT.