Внедрение обновлений A/B

Производители оригинального оборудования (OEM) и поставщики SoC, желающие внедрить A/B-обновления системы, должны убедиться, что их загрузчик реализует HAL boot_control и передает правильные параметры ядру.

Реализуйте HAL управления загрузкой.

Загрузчики с поддержкой A/B должны реализовывать HAL boot_control расположенный в файле hardware/libhardware/include/hardware/boot_control.h . Проверить реализацию можно с помощью утилиты system/extras/bootctl и system/extras/tests/bootloader/ .

Вам также необходимо реализовать конечный автомат, показанный ниже:

Рисунок 1. Конечный автомат загрузчика.

Настройте ядро

Для внедрения 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> — это идентификатор открытого ключа, используемого для проверки подписи таблицы достоверности (подробнее см. 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> в командной строке ядра. Например, вместо <public-key-id> передайте Android:#7e4333f9bba00adfe0ede979e28ed1920492b40f .

Задайте переменные сборки

Загрузчики с поддержкой 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-пакет перед его применением.

Восстановление

Теперь диск восстановления в оперативной памяти находится в файле boot.img . При переходе в режим восстановления загрузчик не может добавить параметр skip_initramfs в командную строку ядра.

Для обновлений, отличных от A/B, раздел восстановления содержит код, используемый для применения обновлений. Обновления A/B применяются движком update_engine работающим в обычном загруженном образе системы. Режим восстановления по-прежнему используется для сброса заводских настроек и загрузки пакетов обновлений (отсюда и название «восстановление»). Код и данные для режима восстановления хранятся в обычном загрузочном разделе в оперативной памяти (ramdisk); для загрузки образа системы загрузчик указывает ядру пропустить ramdisk (в противном случае устройство загружается в режим восстановления). Режим восстановления небольшой (и большая его часть уже находилась в загрузочном разделе), поэтому размер загрузочного раздела не увеличивается.

Фстаб

Аргумент 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 . Получить подробную информацию о командах.

Если загрузчик реализует fastboot, он должен поддерживать команду set_active <slot> , которая устанавливает текущий активный слот на заданный слот (это также должно сбросить флаг невозможности загрузки для этого слота и установить счетчик повторных попыток на значения по умолчанию). Загрузчик также должен поддерживать следующие переменные:

  • has-slot:<partition-base-name-without-suffix> . Возвращает «yes», если указанный раздел поддерживает слоты, «no» в противном случае.
  • current-slot . Возвращает суффикс слота, с которого будет выполнена следующая загрузка.
  • slot-count . Возвращает целое число, представляющее количество доступных слотов. В настоящее время поддерживаются два слота, поэтому это значение равно 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 ). Список разделов, для которых генератор полезной нагрузки определяет обновление, настраивается переменной make AB_OTA_PARTITIONS .

Например, если включены пара разделов bootloader_a и booloader_b ( _a и _b — суффиксы слотов), вы можете обновить эти разделы, указав следующее в конфигурации продукта или платы:

AB_OTA_PARTITIONS := \
  boot \
  system \
  bootloader

Все разделы, обновляемые функцией update_engine не должны изменяться остальной частью системы. Во время инкрементальных или дельта- обновлений двоичные данные из текущего слота используются для генерации данных в новом слоте. Любое изменение может привести к тому, что данные нового слота не пройдут проверку в процессе обновления, и, следовательно, обновление завершится неудачей.

Настройка после установки

Для каждого обновленного раздела можно настроить этап postinstall по-разному, используя набор пар ключ-значение. Чтобы запустить программу, расположенную по адресу /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
    

Для получения помощи по установке предварительно настроенных файлов во второй неиспользуемый системный раздел обратитесь к разделу «Установка файлов DEX_PREOPT при первой загрузке» .