Динамические обновления системы (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:
- 109 с, пользователь 8G, система 867M, тип файловой системы: F2FS: шифрование = aes-256-xts: aes-256-cts
- 104 с, пользователь 8G, система 867M, тип файловой системы: F2FS: шифрование = лед
- Использование ext4:
- 135 с, пользователь 8G, система 867M, тип файловой системы: ext4: шифрование = aes-256-xts: aes-256-cts
Если на вашей платформе это занимает гораздо больше времени, вы можете проверить, содержит ли флаг монтирования какой-либо флаг, который делает запись «синхронной», или вы можете явно указать флаг «асинхронный» для повышения производительности.
Раздел metadata
(16 МБ или больше) необходим для хранения данных, относящихся к установленным образам. Его необходимо установить на первом этапе монтажа.
Раздел userdata
должен использовать файловую систему F2FS или ext4. При использовании F2FS включите все патчи, связанные с F2FS, доступные в общем ядре Android .
DSU был разработан и протестирован с использованием ядра/common 4.9. Для этой функции рекомендуется использовать ядро 4.9 и выше.
Поведение HAL поставщика
Уивер ХАЛ
Weaver HAL предоставляет фиксированное количество слотов для хранения пользовательских ключей. DSU занимает два дополнительных слота для ключей. Если у OEM-производителя есть HAL Weaver, у него должно быть достаточно слотов для общего образа системы (GSI) и образа хоста.
Привратник HAL
Привратник 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 должен быть новее текущего образа системы на устройстве. Это делается путем сравнения уровней исправлений безопасности в дескрипторе свойства Android Verified Boot (AVB) AVB обоих образов системы: Prop: com.android.build.system.security_patch -> '2019-04-05'
.
Для устройств, не использующих AVB, поместите уровень исправления безопасности текущего образа системы в командную строку ядра или загрузочную конфигурацию с помощью загрузчика: 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 может иметь более одного раздела. Например, помимо system.img
он может содержать файл product.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. Внутри примитива изображения есть несколько атрибутов:
Атрибуты
name
иdetails
представляют собой строки, которые отображаются в диалоговом окне, которые пользователь может выбрать.Атрибуты
cpu_api
,vndk
иos_version
используются для проверок совместимости, которые описаны в следующем разделе.Необязательный атрибут
pubkey
описывает открытый ключ, который сочетается с секретным ключом, который используется для подписи пакета DSU. Если он указан, служба DSU может проверить, имеет ли устройство ключ, используемый для проверки пакета DSU. Это позволяет избежать установки неизвестного пакета DSU, например, установки DSU, подписанного OEM-A, на устройство, изготовленное OEM-B.Необязательный атрибут
tos
указывает на текстовый файл, описывающий условия обслуживания для соответствующего пакета DSU. Когда разработчик выбирает пакет DSU с указанным атрибутом условий обслуживания, открывается диалоговое окно, показанное на рис. 6, в котором разработчику предлагается принять условия обслуживания перед установкой пакета DSU.Рисунок 6. Диалоговое окно «Условия обслуживания»
Для справки, вот JSON-дескриптор DSU для 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 11 значениеos_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, скомпрометирована, виртуальный диск следует обновить как можно скорее, чтобы удалить скомпрометированный ключ. Помимо обновления загрузочного раздела, вы можете заблокировать скомпрометированные ключи, используя список отзыва ключей 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. Эту строку ресурса можно накладывать и настраивать, чтобы OEM-производители, использующие функцию 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.
Создайте новую пару ключей
Используйте команду 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.
Добавьте публичный ключ сопряжения на виртуальный диск
Ключ oem_cert.avbpubkey
необходимо поместить в /avb/*.avbpubkey
для проверки подписанного пакета DSU. Сначала преобразуйте открытый ключ в формате 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 в цепочку