Динамические обновления системы позволяют создать образ системы Android, который пользователи могут скачать из интернета и протестировать без риска повредить текущий образ. В этом документе описывается, как поддерживать DSU.
Требования к ядру
Подробнее о реализации динамических разделов…
Кроме того, для проверки образа системы Android в DSU используется функция ядра device-mapper-verity (dm-verity). Поэтому необходимо включить следующие конфигурации ядра:
CONFIG_DM_VERITY=yCONFIG_DM_VERITY_FEC=y
Требования к разделам
Начиная с Android 11, для DSU требуется, чтобы раздел /data использовал файловую систему F2FS или ext4. F2FS обеспечивает более высокую производительность, поэтому рекомендуется использовать именно ее, но разница должна быть незначительной.
Ниже приведены примеры того, сколько времени занимает динамическое обновление системы на устройстве Pixel:
- Использование F2FS:
- 109s, 8G user, 867M system, file system type: F2FS: encryption=aes-256-xts:aes-256-cts
- 104s, 8G user, 867M system, file system type: F2FS: encryption=ice
- С помощью ext4:
- 135 с, 8 ГБ пользовательских данных, 867 МБ системных данных, тип файловой системы: ext4: encryption=aes-256-xts:aes-256-cts
Если на вашей платформе это занимает гораздо больше времени, проверьте, не содержит ли флаг монтирования флаг, который заставляет "sync" выполнять запись. Вы также можете явно указать флаг "async", чтобы повысить производительность.
Раздел metadata (16 МБ или больше) необходим для хранения данных, связанных с установленными образами. Его необходимо подключить на первом этапе.
Раздел userdata должен использовать файловую систему F2FS или ext4. При использовании F2FS включите все исправления, связанные с F2FS, доступные в общем ядре Android.
DSU была разработана и протестирована с ядром/common 4.9. Для этой функции рекомендуется использовать ядро версии 4.9 и выше.
Поведение HAL поставщика
Weaver HAL
Weaver HAL предоставляет фиксированное количество слотов для хранения ключей пользователей. DSU занимает два дополнительных слота для ключей. Если у OEM есть HAL Weaver, ему нужно иметь достаточно слотов для общего образа системы (GSI) и образа хоста.
Gatekeeper HAL
Gatekeeper HAL должен поддерживать большие значения USER_ID, поскольку GSI смещает UID в HAL на +1000000.
Проверка при запуске
Если вы хотите поддерживать загрузку образов GSI для разработчиков в заблокированном состоянии без отключения проверки при запуске, добавьте ключи GSI для разработчиков, добавив следующую строку в файл device/<device_name>/device.mk:
$(call inherit-product, $(SRC_TARGET_DIR)/product/developer_gsi_keys.mk)
Защита от отката
При использовании DSU скачанный образ системы Android должен быть новее текущего образа системы на устройстве. Для этого сравниваются уровни исправления системы безопасности в дескрипторе свойств AVB проверки при запуске Android (AVB) обоих образов системы: Prop: com.android.build.system.security_patch ->
'2019-04-05'.
На устройствах, где не используется AVB, поместите уровень исправления системы безопасности текущего образа системы в командную строку ядра или bootconfig с загрузчиком операционной системы:androidboot.system.security_patch=2019-04-05.
Требования к оборудованию
При запуске экземпляра DSU выделяются два временных файла:
- Логический раздел для хранения
GSI.img(1–1,5 ГБ) - Пустой раздел
/dataразмером 8 ГБ, который будет использоваться в качестве песочницы для запуска GSI.
Перед запуском экземпляра DSU рекомендуется освободить не менее 10 ГБ. DSU также поддерживает выделение места на SD-карте. Если SD-карта установлена, она имеет наивысший приоритет при распределении памяти. Поддержка SD-карт критически важна для устройств с низкой производительностью, у которых может быть недостаточно внутренней памяти. Если SD-карта установлена, убедитесь, что она не используется как внутреннее хранилище. DSU не поддерживает внутренние SD-карты.
Доступные интерфейсы
Запустить DSU можно с помощью adb, приложения OEM или загрузчика DSU в один клик (в Android 11 или более поздней версии).
Как запустить DSU с помощью ADB
Чтобы запустить DSU с помощью adb, введите следующие команды:
$ simg2img out/target/product/.../system.img system.raw
$ gzip -c system.raw > system.raw.gz
$ adb push system.raw.gz /storage/emulated/0/Download
$ adb shell am start-activity \
-n com.android.dynsystem/com.android.dynsystem.VerificationActivity \
-a android.os.image.action.START_INSTALL \
-d file:///storage/emulated/0/Download/system.raw.gz \
--el KEY_SYSTEM_SIZE $(du -b system.raw|cut -f1) \
--el KEY_USERDATA_SIZE 8589934592
Как запустить DSU с помощью приложения
Основная точка входа в DSU – это API android.os.image.DynamicSystemClient.java:
public class DynamicSystemClient {
...
...
/**
* Start installing DynamicSystem from URL with default userdata size.
*
* @param systemUrl A network URL or a file URL to system image.
* @param systemSize size of system image.
*/
public void start(String systemUrl, long systemSize) {
start(systemUrl, systemSize, DEFAULT_USERDATA_SIZE);
}
Это приложение должно быть предустановлено на устройстве. Поскольку DynamicSystemClient – это системный API, вы не сможете создать приложение с помощью обычного SDK API и опубликовать его в Google Play. Цель этого приложения:
- Получать список изображений и соответствующие URL с помощью схемы, определенной поставщиком.
- Сопоставьте изображения в списке с устройством и покажите совместимые изображения, чтобы пользователь мог выбрать одно из них.
Вызовите
DynamicSystemClient.startследующим образом:DynamicSystemClient aot = new DynamicSystemClient(...) aot.start( ...URL of the selected image..., ...uncompressed size of the selected image...);
URL указывает на сжатый с помощью gzip файл образа системы, который можно создать с помощью следующих команд:
$ simg2img ${OUT}/system.img ${OUT}/system.raw
$ gzip ${OUT}/system.raw
$ ls ${OUT}/system.raw.gz
Название файла должно соответствовать следующему формату:
<android version>.<lunch name>.<user defined title>.raw.gz
Примеры:
o.aosp_taimen-userdebug.2018dev.raw.gzp.aosp_taimen-userdebug.2018dev.raw.gz
Загрузчик DSU в один клик
В Android 11 появился загрузчик DSU, который запускается одним нажатием и представляет собой интерфейс в настройках для разработчиков.
Рисунок 1. Запуск загрузчика DSU
Когда разработчик нажимает кнопку DSU Loader (Загрузчик DSU), она получает предварительно настроенный дескриптор DSU JSON из интернета и показывает все подходящие изображения в плавающем меню. Выберите образ, чтобы начать установку DSU. Прогресс будет отображаться на панели уведомлений.
Рисунок 2. Ход установки образа DSU
По умолчанию загрузчик DSU загружает дескриптор JSON, содержащий образы GSI. В следующих разделах показано, как создавать пакеты DSU с подписью OEM и загружать их из загрузчика DSU.
Флаг функции
Функция DSU находится под флагом функции settings_dynamic_android. Прежде чем использовать DSU, убедитесь, что соответствующий флаг функции включен.
Рисунок 3. Как включить флаг функции
Интерфейс флагов функций может быть недоступен на устройстве с пользовательской сборкой. В этом случае используйте команду adb:
$ adb shell setprop persist.sys.fflag.override.settings_dynamic_system 1
Образы хост-систем поставщиков в GCE (необязательно)
Один из возможных вариантов хранения системных образов – сегмент Google Compute Engine (GCE). Администратор выпуска использует консоль хранилища GCP, чтобы добавлять, удалять или изменять выпущенный образ системы.
Изображения должны быть общедоступными, как показано ниже.
Рисунок 4. Общедоступность в GCE
Инструкции по предоставлению общего доступа к объекту приведены в документации по Google Cloud.
DSU с несколькими разделами в ZIP-файле
Начиная с Android 11, DSU может иметь несколько разделов. Например, в нем может быть product.img, а также system.img. При загрузке устройства первый этап init обнаруживает установленные разделы DSU и временно заменяет раздел на устройстве, когда установленный DSU включен. Пакет DSU может содержать раздел, который не соответствует разделу на устройстве.
Рисунок 5. Процесс DSU с несколькими разделами
DSU, подписанные OEM-производителем
Чтобы убедиться, что все образы, запущенные на устройстве, авторизованы производителем устройства, все образы в пакете DSU должны быть подписаны. Предположим, что пакет DSU содержит два образа разделов, как показано ниже:
dsu.zip {
- system.img
- product.img
}
И system.img, и product.img должны быть подписаны ключом OEM, прежде чем их можно будет поместить в ZIP-файл. Обычно используется асимметричный алгоритм, например RSA, в котором секретный ключ используется для подписания пакета, а открытый – для его проверки. Первый образ RAM-диска должен содержать открытый ключ для сопряжения, например /avb/*.avbpubkey. Если на устройстве уже используется AVB, существующей процедуры подписания будет достаточно. В следующих разделах описан процесс подписи и показано, где размещается открытый ключ AVB, который используется для проверки образов в пакете DSU.
Дескриптор JSON DSU
Дескриптор DSU в формате JSON описывает пакеты DSU. Он поддерживает два примитива.
Во-первых, примитив include включает дополнительные дескрипторы JSON или перенаправляет загрузчик DSU в новое местоположение. Пример:
{
"include": ["https://.../gsi-release/gsi-src.json"]
}
Во-вторых, примитив image используется для описания выпущенных пакетов DSU. В примитиве image есть несколько атрибутов:
Атрибуты
nameиdetails– это строки, которые показываются в диалоговом окне для выбора пользователем.Атрибуты
cpu_api,vndkиos_versionиспользуются для проверки совместимости, которая описана в следующем разделе.Необязательный атрибут
pubkeyописывает открытый ключ, который используется в паре с секретным ключом для подписи пакета DSU. Если он указан, сервис DSU может проверить, есть ли на устройстве ключ, используемый для проверки пакета DSU. Это позволяет избежать установки неподписанного пакета DSU, например подписанного OEM-A на устройство, изготовленное OEM-B.Необязательный атрибут
tosуказывает на текстовый файл, в котором описаны условия использования соответствующего пакета DSU. Когда разработчик выбирает пакет DSU с указанным атрибутом условий использования, открывается диалоговое окно, показанное на рисунке 6, в котором разработчику предлагается принять условия использования перед установкой пакета DSU.
Рисунок 6. Диалоговое окно с Условиями использования
Ниже приведен дескриптор JSON для GSI:
{
"images":[
{
"name":"GSI+GMS x86",
"os_version":"10",
"cpu_abi": "x86",
"details":"exp-QP1A.190711.020.C4-5928301",
"vndk":[
27,
28,
29
],
"pubkey":"",
"tos": "https://dl.google.com/developers/android/gsi/gsi-tos.txt",
"uri":"https://.../gsi/gsi_gms_x86-exp-QP1A.190711.020.C4-5928301.zip"
},
{
"name":"GSI+GMS ARM64",
"os_version":"10",
"cpu_abi": "arm64-v8a",
"details":"exp-QP1A.190711.020.C4-5928301",
"vndk":[
27,
28,
29
],
"pubkey":"",
"tos": "https://dl.google.com/developers/android/gsi/gsi-tos.txt",
"uri":"https://.../gsi/gsi_gms_arm64-exp-QP1A.190711.020.C4-5928301.zip"
},
{
"name":"GSI ARM64",
"os_version":"10",
"cpu_abi": "arm64-v8a",
"details":"exp-QP1A.190711.020.C4-5928301",
"vndk":[
27,
28,
29
],
"pubkey":"",
"uri":"https://.../gsi/aosp_arm64-exp-QP1A.190711.020.C4-5928301.zip"
},
{
"name":"GSI x86_64",
"os_version":"10",
"cpu_abi": "x86_64",
"details":"exp-QP1A.190711.020.C4-5928301",
"vndk":[
27,
28,
29
],
"pubkey":"",
"uri":"https://.../gsi/aosp_x86_64-exp-QP1A.190711.020.C4-5928301.zip"
}
]
}
Управление совместимостью
Для указания совместимости между пакетом DSU и локальным устройством используются следующие атрибуты:
cpu_api– строка, описывающая архитектуру устройства. Этот атрибут обязателен и сравнивается с системным свойствомro.product.cpu.abi. Их значения должны полностью совпадать.os_version– необязательное целое число, указывающее на выпуск Android. Например, для Android 10os_version– это10, а для Android 11os_version– это11. Если этот атрибут указан, он должен быть равен или больше системного свойстваro.system.build.version.release. Эта проверка используется, чтобы предотвратить загрузку образа GSI Android 10 на устройстве поставщика с Android 11, что в настоящее время не поддерживается. Загрузка образа GSI Android 11 на устройстве с Android 10 разрешена.vndk– необязательный массив, в котором перечислены все VNDK, включенные в пакет DSU. Если он указан, загрузчик DSU проверяет, включено ли число, извлеченное из системного свойстваro.vndk.version.
Отзыв ключей DSU в целях безопасности
В крайне редких случаях, когда пара ключей RSA, используемая для подписи образов DSU, оказывается скомпрометирована, ramdisk следует как можно скорее обновить, чтобы удалить скомпрометированный ключ. Помимо обновления загрузочного раздела, вы можете заблокировать скомпрометированные ключи, используя список отзыва ключей DSU (черный список ключей) из URL-адреса HTTPS.
Список отзыва ключей DSU содержит список отозванных открытых ключей AVB. Во время установки DSU открытые ключи в образах DSU проверяются по списку отозванных сертификатов. Если в образах обнаруживается отозванный открытый ключ, процесс установки DSU останавливается.
URL списка отзыва ключей должен быть URL по HTTPS, чтобы обеспечить безопасность, и указывается в строке ресурса:
frameworks/base/packages/DynamicSystemInstallationService/res/values/strings.xml@key_revocation_list_url
Значение строки – https://dl.google.com/developers/android/gsi/gsi-keyblacklist.json. Это список отзыва для ключей GSI, выпущенных Google. Эту строку ресурса можно наложить и настроить, чтобы производители устройств, использующие функцию DSU, могли предоставлять и поддерживать собственный черный список ключей. Это позволяет OEM блокировать определенные открытые ключи без обновления образа виртуального диска устройства.
Список отзыва имеет следующий формат:
{
"entries":[
{
"public_key":"bf14e439d1acf231095c4109f94f00fc473148e6",
"status":"REVOKED",
"reason":"Key revocation test key"
},
{
"public_key":"d199b2f29f3dc224cca778a7544ea89470cbef46",
"status":"REVOKED",
"reason":"Key revocation test key"
}
]
}
public_key– это дайджест SHA-1 отозванного ключа в формате, описанном в разделе создание открытого ключа AVB.status– статус отзыва ключа. В настоящее время поддерживается только значениеREVOKED.reason– необязательная строка с описанием причины отзыва.
Процедуры DSU
В этом разделе рассказывается, как выполнить несколько процедур настройки DSU.
Как создать новую пару ключей
Чтобы создать пару закрытого и открытого ключей RSA в формате .pem (например, размером 2048 бит), используйте команду openssl:
$ openssl genrsa -out oem_cert_pri.pem 2048
$ openssl rsa -in oem_cert_pri.pem -pubout -out oem_cert_pub.pem
Закрытый ключ может быть недоступен и храниться только в аппаратном модуле безопасности (HSM). В этом случае после создания ключа может быть доступен сертификат открытого ключа x509. Инструкции по созданию открытого ключа AVB из сертификата X.509 приведены в разделе Добавление открытого ключа для связывания в RAM-диск.
Чтобы преобразовать сертификат x509 в формат PEM:
$ openssl x509 -pubkey -noout -in oem_cert_pub.x509.pem > oem_cert_pub.pem
Пропустите этот шаг, если сертификат уже является PEM-файлом.
Добавьте открытый ключ для связывания в RAM-диск.
Чтобы проверить подписанный пакет DSU, элемент oem_cert.avbpubkey необходимо разместить под элементом /avb/*.avbpubkey. Сначала преобразуйте открытый ключ в формате PEM в формат открытого ключа AVB:
$ avbtool extract_public_key --key oem_cert_pub.pem --output oem_cert.avbpubkey
Затем добавьте открытый ключ в ramdisk первого этапа, выполнив следующие действия:
Добавьте готовый модуль, чтобы скопировать
avbpubkey. Например, добавьтеdevice/<company>/<board>/oem_cert.avbpubkeyиdevice/<company>/<board>/avb/Android.mkсо следующим контентом:include $(CLEAR_VARS) LOCAL_MODULE := oem_cert.avbpubkey LOCAL_MODULE_CLASS := ETC LOCAL_SRC_FILES := $(LOCAL_MODULE) ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true) LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/first_stage_ramdisk/avb else LOCAL_MODULE_PATH := $(TARGET_RAMDISK_OUT)/avb endif include $(BUILD_PREBUILT)Сделайте так, чтобы целевой объект droidcore зависел от добавленного объекта
oem_cert.avbpubkey:droidcore: oem_cert.avbpubkey
Создайте атрибут открытого ключа AVB в дескрипторе JSON
Файл oem_cert.avbpubkey должен быть в двоичном формате открытого ключа AVB. Используйте SHA-1, чтобы сделать его читаемым, прежде чем добавлять в дескриптор JSON:
$ sha1sum oem_cert.avbpubkey | cut -f1 -d ' '
3e62f2be9d9d813ef5........866ac72a51fd20
Это будет контент атрибута pubkey дескриптора JSON.
"images":[
{
...
"pubkey":"3e62f2be9d9d813ef5........866ac72a51fd20",
...
},
Как подписать пакет DSU
Подписать пакет DSU можно одним из следующих способов:
Способ 1. Повторно использовать артефакт, созданный в процессе подписания AVB, чтобы создать пакет DSU. Другой способ – извлечь из пакета выпуска уже подписанные изображения и использовать их для создания ZIP-файла.
Способ 2. Используйте следующие команды, чтобы подписать разделы DSU, если доступен закрытый ключ. Каждый файл
imgв пакете DSU (ZIP-архиве) подписывается отдельно:$ key_len=$(openssl rsa -in oem_cert_pri.pem -text | grep Private-Key | sed -e 's/.*(\(.*\) bit.*/\1/') $ for partition in system product; do avbtool add_hashtree_footer \ --image ${OUT}/${partition}.img \ --partition_name ${partition} \ --algorithm SHA256_RSA${key_len} \ --key oem_cert_pri.pem done
Подробнее о том, как добавить add_hashtree_footer с помощью avbtool…
Как проверить пакет DSU локально
Рекомендуем проверить все локальные образы с помощью открытого ключа сопряжения, используя следующие команды:
for partition in system product; do
avbtool verify_image --image ${OUT}/${partition}.img --key oem_cert_pub.pem
done
Ожидаемый результат:
Verifying image dsu/system.img using key at oem_cert_pub.pem
vbmeta: Successfully verified footer and SHA256_RSA2048 vbmeta struct in dsu/system.img
: Successfully verified sha1 hashtree of dsu/system.img for image of 898494464 bytes
Verifying image dsu/product.img using key at oem_cert_pub.pem
vbmeta: Successfully verified footer and SHA256_RSA2048 vbmeta struct in dsu/product.img
: Successfully verified sha1 hashtree of dsu/product.img for image of 905830400 bytes
Как создать пакет DSU
В следующем примере создается пакет DSU, содержащий system.img и product.img:
dsu.zip {
- system.img
- product.img
}
После того как оба образа будут подписаны, используйте следующую команду, чтобы создать ZIP-файл:
$ mkdir -p dsu
$ cp ${OUT}/system.img dsu
$ cp ${OUT}/product.img dsu
$ cd dsu && zip ../dsu.zip *.img && cd -
Как настроить обновление системы одним нажатием
По умолчанию загрузчик DSU указывает на метаданные образов GSI, которые находятся по адресу https://...google.com/.../gsi-src.json.
Производители оригинального оборудования могут переопределить список, задав свойство persist.sys.fflag.override.settings_dynamic_system.list, которое указывает на их собственный дескриптор JSON. Например, производитель устройства может предоставить метаданные JSON, которые включают GSI, а также собственные изображения OEM, например так:
{
"include": ["https://dl.google.com/.../gsi-src.JSON"]
"images":[
{
"name":"OEM image",
"os_version":"10",
"cpu_abi": "arm64-v8a",
"details":"...",
"vndk":[
27,
28,
29
],
"spl":"...",
"pubkey":"",
"uri":"https://.../....zip"
},
}
Производитель оригинального оборудования может объединить опубликованные метаданные DSU в цепочку, как показано на рисунке 7.
Рисунок 7. Цепочки опубликованных метаданных DSU