Динамические обновления системы (DSU) позволяют создать образ системы Android, который пользователи могут загрузить из интернета и опробовать, не рискуя повредить текущий образ системы. В этом документе описывается, как поддерживать DSU.
Требования к ядру
Требования к ядру см. в разделе Реализация динамических разделов .
Кроме того, DSU использует функцию ядра device-mapper-verity (dm-verity) для проверки образа системы Android. Поэтому необходимо включить следующие конфигурации ядра:
-
CONFIG_DM_VERITY=y
-
CONFIG_DM_VERITY_FEC=y
Требования к разделам
Начиная с Android 11, DSU требует, чтобы раздел /data
использовал файловую систему F2FS или ext4. F2FS обеспечивает лучшую производительность и рекомендуется, но разница должна быть незначительной.
Вот несколько примеров того, сколько времени занимает динамическое обновление системы на устройстве Pixel:
- Использование F2FS:
- 109s, пользователь 8G, система 867M, тип файловой системы: F2FS: шифрование=aes-256-xts:aes-256-cts
- 104s, пользователь 8G, система 867M, тип файловой системы: F2FS: шифрование=ice
- Использование ext4:
- 135s, пользователь 8G, система 867M, тип файловой системы: ext4: шифрование=aes-256-xts:aes-256-cts
Если на вашей платформе это занимает гораздо больше времени, вы можете проверить, содержит ли флаг монтирования какой-либо флаг, который заставляет записывать «sync», или вы можете явно указать флаг «async», чтобы повысить производительность.
Раздел metadata
(объемом 16 МБ или более) необходим для хранения данных, связанных с установленными образами. Он должен быть смонтирован на первом этапе монтирования.
Раздел userdata
должен использовать файловую систему F2FS или ext4. При использовании F2FS включите все связанные с F2FS исправления, доступные в общем ядре Android .
DSU был разработан и протестирован с ядром kernel/common 4.9. Для этой функции рекомендуется использовать ядро 4.9 и выше.
Поведение HAL поставщика
Уивер ХЭЛ
HAL-системы Weaver предоставляют фиксированное количество слотов для хранения пользовательских ключей. DSU занимает два дополнительных слота для ключей. Если у OEM-производителя есть HAL-система Weaver, ему необходимо иметь достаточно слотов для общего образа системы (GSI) и образа хоста.
Привратник HAL
Gatekeeper HAL должен поддерживать большие значения USER_ID
, поскольку GSI смещает UID в HAL на +1000000.
Проверить загрузку
Если вы хотите поддерживать загрузку образов Developer GSI в состоянии LOCKED без отключения проверенной загрузки, включите ключи Developer GSI, добавив следующую строку в файл device/<device_name>/device.mk
:
$(call inherit-product, $(SRC_TARGET_DIR)/product/developer_gsi_keys.mk)
Защита от отката
При использовании DSU загруженный образ системы Android должен быть новее текущего образа системы на устройстве. Это достигается путём сравнения уровней исправлений безопасности в дескрипторе свойств AVB Android Verified Boot (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
Мы рекомендуем зарезервировать не менее 10 ГБ свободного места перед запуском экземпляра DSU. 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, вы не сможете собрать приложение с помощью обычного API SDK и опубликовать его в 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.gz
-
p.aosp_taimen-userdebug.2018dev.raw.gz
Загрузчик DSU в один клик
В Android 11 представлен загрузчик DSU в один клик, представляющий собой интерфейс в настройках разработчика.
Рисунок 1. Запуск загрузчика DSU
Когда разработчик нажимает кнопку «Загрузчик DSU» , он загружает предварительно настроенный JSON-дескриптор DSU из интернета и отображает все подходящие изображения во всплывающем меню. Выберите изображение, чтобы начать установку 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, где секретный ключ используется для подписи пакета, а открытый — для его проверки. Электронный диск первого этапа должен содержать открытый ключ сопряжения, например, /avb/*.avbpubkey
. Если устройство уже использует AVB, существующей процедуры подписи будет достаточно. В следующих разделах описывается процесс подписи и объясняется расположение открытого ключа AVB, используемого для проверки образов в пакете DSU.
Дескриптор DSU JSON
Дескриптор DSU JSON описывает пакеты DSU. Он поддерживает два примитива. Во-первых, примитив include
включает дополнительные JSON-дескрипторы или перенаправляет загрузчик DSU в новое место. Например:
{
"include": ["https://.../gsi-release/gsi-src.json"]
}
Во-вторых, примитив image
используется для описания выпущенных пакетов DSU. Внутри примитива изображения есть несколько атрибутов:
Атрибуты
name
иdetails
представляют собой строки, которые отображаются в диалоговом окне и могут быть выбраны пользователем.Атрибуты
cpu_api
,vndk
иos_version
используются для проверок совместимости, которые описаны в следующем разделе.Необязательный атрибут
pubkey
описывает открытый ключ, который используется в паре с секретным ключом для подписи пакета DSU. Если он указан, служба DSU может проверить, есть ли на устройстве ключ, используемый для проверки пакета DSU. Это позволяет избежать установки нераспознанного пакета DSU, например, установки DSU, подписанного OEM-A, на устройство OEM-B.Необязательный атрибут
tos
указывает на текстовый файл с описанием условий обслуживания для соответствующего пакета DSU. Когда разработчик выбирает пакет DSU с указанным атрибутом условий обслуживания, открывается диалоговое окно, показанное на рисунке 6, с предложением принять условия обслуживания перед установкой пакета DSU.Рисунок 6. Диалоговое окно «Условия обслуживания»
Для справки, вот дескриптор DSU 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
. Эта проверка используется для предотвращения загрузки образа Android 10 GSI на устройстве поставщика Android 11, что в настоящее время не поддерживается. Загрузка образа Android 11 GSI на устройстве Android 10 разрешена.vndk
— необязательный массив, содержащий все VNDK, входящие в пакет DSU. При его указании загрузчик DSU проверяет, включен ли номер, извлечённый из системного свойстваro.vndk.version
.
Отозвать ключи DSU в целях безопасности
В крайне редких случаях, когда пара ключей RSA, используемая для подписи образов DSU, скомпрометирована, следует как можно скорее обновить RAM-диск, чтобы удалить скомпрометированный ключ. Помимо обновления загрузочного раздела, вы можете заблокировать скомпрометированные ключи, используя список отозванных ключей DSU (чёрный список ключей) по 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. Эта строка ресурса может быть перекрыта и настроена, чтобы OEM-производители, использующие функцию DSU, могли предоставлять и поддерживать собственный чёрный список ключей. Это позволяет OEM-производителям блокировать определённые открытые ключи без обновления образа RAM-диска устройства.
Формат списка отзыва следующий:
{
"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.
Сгенерировать новую пару ключей
Используйте команду openssl
для генерации пары закрытых и открытых ключей RSA в формате .pem
(например, размером 2048 бит):
$ openssl genrsa -out oem_cert_pri.pem 2048
$ openssl rsa -in oem_cert_pri.pem -pubout -out oem_cert_pub.pem
Закрытый ключ может быть недоступен и храниться только в аппаратном модуле безопасности (HSM) . В этом случае после генерации ключа может быть доступен сертификат открытого ключа x509. Инструкции по генерации открытого ключа AVB из сертификата x509 см. в разделе «Добавление открытого ключа сопряжения в виртуальный диск» .
Чтобы преобразовать сертификат x509 в формат PEM:
$ openssl x509 -pubkey -noout -in oem_cert_pub.x509.pem > oem_cert_pub.pem
Пропустите этот шаг, если сертификат уже представляет собой PEM-файл.
Добавьте открытый ключ сопряжения на ramdisk
Для проверки подписанного пакета DSU необходимо поместить ключ oem_cert.avbpubkey
в папку /avb/*.avbpubkey
. Сначала преобразуйте открытый ключ из формата PEM в формат открытого ключа AVB:
$ avbtool extract_public_key --key oem_cert_pub.pem --output oem_cert.avbpubkey
Затем включите открытый ключ в виртуальный диск первого этапа, выполнив следующие шаги.
Добавьте готовый модуль для копирования
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
см. в разделе Использование 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 одним щелчком мыши
По умолчанию загрузчик DSU указывает на метаданные изображений GSI, которые являются https://...google.com/.../gsi-src.json
.
OEM-производители могут перезаписать список, определив свойство persist.sys.fflag.override.settings_dynamic_system.list
, указывающее на их собственный JSON-дескриптор. Например, OEM-производитель может предоставить метаданные 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"
},
}
OEM-производитель может объединить опубликованные метаданные DSU в цепочку, как показано на рисунке 7.
Рисунок 7. Цепочка опубликованных метаданных DSU