Макет раздела

В Android 10 корневая файловая система больше не включается в ramdisk.img, а объединяется с system.img (то есть system.img всегда создается так, как если бы было задано значение BOARD_BUILD_SYSTEM_ROOT_IMAGE). Устройства, выпущенные с Android 10:

  • Используйте схему разделов system-as-root (применяется автоматически при сборке, изменить это нельзя).
  • Необходимо использовать ramdisk, который требуется для dm-linear.
  • Необходимо задать для параметра BOARD_BUILD_SYSTEM_ROOT_IMAGE значение false. Этот параметр используется только для того, чтобы различать устройства, которые используют ramdisk, и устройства, которые не используют ramdisk (и вместо этого монтируют system.img напрямую).

Значение конфигурации system-as-root различается в Android 9 и Android 10. В конфигурации Android 9 system-as-root для параметра BOARD_BUILD_SYSTEM_ROOT_IMAGE задано значение true, которое заставляет сборку объединить корневую файловую систему в system.img, а затем смонтировать system.img как корневую файловую систему (rootfs). Эта конфигурация обязательна для устройств с Android 9, но необязательна для устройств, на которых Android 9 был установлен в качестве обновления, и для устройств с более ранними версиями Android. В конфигурации Android 10 с системным разделом в корневом каталоге сборка всегда объединяет $TARGET_SYSTEM_OUT и $TARGET_ROOT_OUT в system.img. Эта конфигурация является режимом работы по умолчанию для всех устройств с Android 10.

В Android 10 также внесены изменения для поддержки динамических разделов – системы разделов пользовательского пространства, которая позволяет создавать, изменять размер и удалять разделы при обновлении по беспроводной сети. В рамках этого изменения ядро Linux больше не может монтировать логический системный раздел на устройствах с Android 10, поэтому эта операция обрабатывается на первом этапе инициализации.

В следующих разделах описаны требования к системному корневому разделу для OTA-обновлений только системы, а также приведены рекомендации по обновлению устройств для использования системного корневого раздела (включая изменения макета разделов и требования к ядру dm-verity). Подробнее об изменениях в ramdisk можно узнать в статье Разделы ramdisk.

О беспроводных обновлениях только системы

OTA-обновления только системы, которые позволяют обновлять выпуски Android system.img и product.img без изменения других разделов, требуют макета раздела "система как корень". Все устройства с Android 10 должны использовать системный раздел в качестве корневого, чтобы можно было выполнять OTA-обновления только системы.

  • Устройства с разделами A/B, в которых раздел system монтируется как rootfs, уже используют систему как корневую файловую систему и не требуют изменений для поддержки OTA-обновлений системы.
  • Устройства без A/B-обновлений, которые монтируют раздел system в /system, должны быть обновлены для использования макета раздела system-as-root, чтобы поддерживать беспроводные обновления системы.

Подробнее об устройствах с разделами A/B и без них можно узнать в статье Системные обновления A/B (бесшовные).

Использовать оверлей поставщика (<=AOSP 14)

Оверлей поставщика позволяет накладывать изменения на раздел vendor при загрузке устройства. Наложение поставщика – это набор модулей поставщика в разделе product, которые накладываются на раздел vendor при загрузке устройства, заменяя и дополняя существующие модули.

Когда устройство загружается, процесс init завершает монтирование на первом этапе и считывает свойства по умолчанию. Затем он ищет /product/vendor_overlay/<target_vendor_version> и монтирует каждый подкаталог в соответствующий каталог раздела vendor, если выполняются следующие условия:

  • /vendor/<overlay_dir>.
  • /product/vendor_overlay/<target_vendor_version>/<overlay_dir> имеет тот же контекст файла, что и /vendor/<overlay_dir>.
  • init может быть смонтирован в контексте файла /vendor/<overlay_dir>.

Как реализовать оверлей поставщика

Установите файлы наложения поставщика в /product/vendor_overlay/<target_vendor_version>. Эти файлы перекрывают раздел vendor при загрузке устройства, заменяя файлы с тем же именем и добавляя новые файлы. Оверлей поставщика не может удалять файлы из раздела vendor.

Файлы оверлея поставщика должны иметь тот же контекст, что и целевые файлы, которые они заменяют в разделе vendor. По умолчанию файлы в каталоге /product/vendor_overlay/<target_vendor_version> имеют контекст vendor_file. Если контекст файлов поставщика не совпадает с контекстом файлов, которые они заменяют, укажите это в файле sepolicy для устройства. Контекст файла задается на уровне каталога. Если контекст файла каталога наложения поставщика не совпадает с целевым каталогом и правильный контекст файла не указан в sepolicy для устройства, каталог наложения поставщика не будет наложен на целевой каталог.

Чтобы использовать наложение поставщика, ядро должно включить OverlayFS, задав значение CONFIG_OVERLAY_FS=y. Кроме того, ядро должно быть объединено с общим ядром версии 4.4 или более поздней или исправлено с помощью "overlayfs: override_creds=off option bypass creator_cred".

Пример реализации оверлея поставщика

В этой процедуре показано, как реализовать наложение поставщика, которое перекрывает каталоги /vendor/lib/*, /vendor/etc/* и /vendor/app/*.

  1. Добавьте готовые файлы поставщиков в папку device/<vendor>/<target>/vendor_overlay/<target_vendor_version>/:

    device/google/device/vendor_overlay/28/lib/libfoo.so
    device/google/device/vendor_overlay/28/lib/libbar.so
    device/google/device/vendor_overlay/28/etc/baz.xml
    device/google/device/vendor_overlay/28/app/qux.apk
  2. Установите готовые файлы поставщика в product/vendor_overlay в device/google/device/device.mk:

    PRODUCT_COPY_FILES += \
        $(call find-copy-subdir-files,*,device/google/device/vendor_overlay,$(TARGET_COPY_OUT_PRODUCT)/vendor_overlay)
  3. Определите контексты файлов, если целевые файлы раздела vendor имеют контексты, отличные от vendor_file. Поскольку в примере /vendor/lib/* используется контекст vendor_file, этот каталог не включен.

    Добавьте в чат-группу "device/google/device-sepolicy/private/file_contexts" следующее:

    /(product|system/product)/vendor_overlay/[0-9]+/etc(/.*)?   u:object_r:vendor_configs_file:s0
    /(product|system/product)/vendor_overlay/[0-9]+/app(/.*)?   u:object_r:vendor_app_file:s0
  4. Разрешить процессу init монтировать наложение поставщика в контекстах файлов, отличных от vendor_file. Поскольку у процесса init уже есть разрешение на монтирование в контексте vendor_file, в этом примере не определяются правила для vendor_file.

    Добавьте в device/google/device-sepolicy/public/init.te следующее:

    allow init vendor_configs_file:dir mounton;
    allow init vendor_app_file:dir mounton;

Проверка наложения поставщика

Чтобы проверить конфигурацию наложения поставщика, добавьте файлы в /product/vendor_overlay/<target_vendor_version>/<overlay_dir> и убедитесь, что они накладываются на файлы в /vendor/<overlay_dir>.

Для сборок userdebug существует тестовый модуль для Atest:

$ atest -v fs_mgr_vendor_overlay_test

Обновление до system-as-root

Чтобы обновить устройства, не поддерживающие A/B-обновления, и использовать system-as-root, необходимо обновить схему разбиения для boot.img и system.img, настроить dm-verity и удалить все зависимости загрузки от корневых папок, относящихся к устройству.

Как обновить разделы

В отличие от устройств с A/B-разделами, где /boot используется как раздел recovery, на устройствах без A/B-разделов раздел /recovery должен быть отдельным, поскольку на них нет резервного раздела (например, с boot_a на boot_b). Если на устройстве без A/B-разделов удалить раздел /recovery и сделать его похожим на схему с A/B-разделами, режим восстановления может перестать работать при сбое обновления раздела /boot. По этой причине раздел /recovery должен быть отдельным от раздела /boot на устройствах, не поддерживающих A/B-обновления. Это означает, что образ восстановления по-прежнему будет обновляться с задержкой (как на устройствах с Android 8.1.0 или более ранней версии).

В таблице ниже перечислены различия в разделах образов для устройств без поддержки A/B-обновлений до и после Android 9.

Изображение Ramdisk (до Android 9) System-as-root (после Android 9)
boot.img Содержит ядро и ramdisk.img:
ramdisk.img
  -/
    - init.rc
    - init
    - etc -> /system/etc
    - system/ (mount point)
    - vendor/ (mount point)
    - odm/ (mount point)
    ...
Содержит только обычное загрузочное ядро.
recovery.img Содержит ядро и ramdisk.img восстановления.
system.img Содержит следующие данные:
system.img
  -/
    - bin/
    - etc
    - vendor -> /vendor
    - ...
Содержит объединенный контент исходных файлов system.img и ramdisk.img:
system.img
  -/
    - init.rc
    - init
    - etc -> /system/etc
    - system/
      - bin/
      - etc/
      - vendor -> /vendor
      - ...
    - vendor/ (mount point)
    - odm/ (mount point)
    ...

Сами разделы не меняются. В обоих случаях используется следующая схема:

  • /boot
  • /system
  • /system
  • /recovery
  • /vendor и т. д.

Как настроить dm-verity

В системе с корневым разделом на устройстве ядро должно смонтировать system.img в / (точку монтирования) с помощью dm-verity. В AOSP поддерживаются следующие реализации dm-verity для system.img:

vboot 1.0

Для vboot 1.0 ядро должно анализировать метаданные, относящиеся к Android, на /system, а затем преобразовывать их в параметры dm-verity, чтобы настроить dm-verity (требуются эти исправления ядра). В следующем примере показаны настройки dm-verity для system-as-root в командной строке ядра:

ro root=/dev/dm-0 rootwait skip_initramfs init=/init
dm="system none ro,0 1 android-verity /dev/sda34"
veritykeyid=id:7e4333f9bba00adfe0ede979e28ed1920492b40f

vboot 2.0

Для vboot 2.0 (AVB) загрузчик должен интегрировать external/avb/libavb, который затем анализирует дескриптор дерева хешей для /system, преобразует его в параметры dm-verity и передает их ядру через командную строку ядра. (Дескрипторы хеш-дерева /system могут быть в /vbmeta или в самом /system.)

Для vboot 2.0 требуются следующие исправления ядра:

В следующем примере показаны настройки dm-verity для system-as-root в командной строке ядра:

ro root=/dev/dm-0 rootwait  skip_initramfs init=/init

dm="1 vroot none ro 1,0 5159992 verity 1
PARTUUID=00000016-0000-0000-0000-000000000000
PARTUUID=00000016-0000-0000-0000-000000000000 4096 4096 644999 644999
sha1 d80b4a8be3b58a8ab86fad1b498640892d4843a2
8d08feed2f55c418fb63447fec0d32b1b107e42c 10 restart_on_corruption
ignore_zero_blocks use_fec_from_device
PARTUUID=00000016-0000-0000-0000-000000000000 fec_roots 2 fec_blocks
650080 fec_start 650080"

Используйте корневые папки для разных устройств

При использовании системного раздела в качестве корневого после того, как на устройство будет записан стандартный системный образ (GSI) (и до запуска тестов набора для тестирования поставщика), все корневые папки, добавленные с помощью команды BOARD_ROOT_EXTRA_FOLDERS, будут удалены, поскольку все содержимое корневого каталога будет заменено на GSI с системным разделом в качестве корневого. Удаление этих папок может привести к тому, что устройство не сможет загрузиться, если оно зависит от корневых папок устройства (например, они используются в качестве точек подключения).

Чтобы избежать этой проблемы, не используйте BOARD_ROOT_EXTRA_FOLDERS для добавления корневых папок, относящихся к определенному устройству. Если вам нужно указать точки монтирования для определенных устройств, используйте /mnt/vendor/<mount point> (добавлено в этих списках изменений). Точки монтирования, относящиеся к определенному поставщику, можно указать непосредственно в дереве устройств fstab (для монтирования на первом этапе) и в файле /vendor/etc/fstab.{ro.hardware} без дополнительной настройки (поскольку fs_mgr создает их в каталоге /mnt/vendor/* автоматически).