As atualizações dinâmicas do sistema (DSU, na sigla em inglês) permitem criar uma imagem do sistema Android que os usuários podem transferir por download e testar sem o risco de corromper a imagem atual do sistema. Este documento descreve como oferecer suporte a DSUs.
Requisitos do kernel
Consulte Como implementar partições dinâmicas para ver os requisitos do kernel.
Além disso, a DSU depende do recurso de kernel device-mapper-verity (dm-verity) para verificar a imagem do sistema Android. Portanto, é necessário ativar as seguintes configurações do kernel:
CONFIG_DM_VERITY=y
CONFIG_DM_VERITY_FEC=y
Requisitos de partição
A partir do Android 11, a DSU exige que a partição /data
use o sistema de arquivos F2FS ou ext4. O F2FS oferece melhor desempenho e
é recomendado, mas a diferença é insignificante.
Confira alguns exemplos de quanto tempo uma atualização dinâmica do sistema leva com um dispositivo Pixel:
- Como usar F2FS:
- 109s, 8G de usuário, 867M de sistema, tipo de sistema de arquivos: F2FS: encryption=aes-256-xts:aes-256-cts
- 104s, usuário 8G, sistema de 867M, tipo de sistema de arquivos: F2FS: encryption=ice
- Usando ext4:
- 135s, 8G de usuário, 867M de sistema, tipo de sistema de arquivos: ext4: encryption=aes-256-xts:aes-256-cts
Se demorar muito na sua plataforma, verifique se a flag de montagem contém alguma flag que faz a gravação "sync" ou especifique uma flag "async" explicitamente para ter um desempenho melhor.
A partição metadata
(16 MB ou mais) é necessária para armazenar dados relacionados
às imagens instaladas. Ele precisa ser montado durante a montagem no primeiro estágio.
A partição userdata
precisa usar o sistema de arquivos F2FS ou ext4. Ao usar o F2FS,
inclua todos os patches relacionados ao F2FS disponíveis no
Kernel comum do Android.
As DSUs foram desenvolvidas e testadas com kernel/comum 4.9. É recomendável usar o kernel 4.9 e versões mais recentes para esse recurso.
Comportamento da HAL do fornecedor
HAL do Weaver
A HAL do tecelão fornece um número fixo de slots para armazenar chaves de usuário. O DSU usa dois slots de chaves extras. Se um OEM tiver uma HAL de tecelagem, ele precisará ter slots suficientes para uma imagem genérica do sistema (GSI) e uma imagem do host.
HAL da empresa guardiã
A HAL Gatekeeper precisa
oferecer suporte a grandes valores de USER_ID
, porque a GSI desloca UIDs para a HAL em
+1000000.
Verificação de inicialização
Se você quiser oferecer suporte à inicialização de imagens GSI do desenvolvedor
no estado BLOQUEADO sem desativar
a inicialização verificada, inclua as chaves GSI de desenvolvedor adicionando a seguinte linha
ao arquivo device/<device_name>/device.mk
:
$(call inherit-product, $(SRC_TARGET_DIR)/product/developer_gsi_keys.mk)
Proteção contra reversão
Ao usar a DSU, a imagem do sistema Android precisa ser mais recente do que a
imagem atual do sistema no dispositivo. Isso é feito comparando os níveis do patch de segurança no descritor de propriedade AVB da Inicialização verificada do Android (AVB, na sigla em inglês) de ambas as imagens do sistema: Prop: com.android.build.system.security_patch ->
'2019-04-05'
.
Para dispositivos que não usam o AVB, coloque o nível de patch de segurança da imagem
do sistema atual no cmdline do kernel ou no bootconfig com o carregador de inicialização:
androidboot.system.security_patch=2019-04-05
.
Requisitos de hardware
Quando você inicia uma instância de DSU, dois arquivos temporários são alocados:
- Uma partição lógica para armazenar
GSI.img
(1 a 1,5 GB) - Uma partição
/data
vazia de 8 GB como sandbox para executar a GSI.
Recomendamos reservar pelo menos 10 GB de espaço livre antes de iniciar uma instância de DSU. As DSUs também são compatíveis com alocação de um cartão SD. Quando há um cartão SD, ele tem a prioridade mais alta para a alocação. O suporte a cartões SD é essencial para dispositivos de baixa potência que podem não ter armazenamento interno suficiente. Quando um cartão SD estiver presente, certifique-se de que ele não seja adotado. A DSU não oferece suporte a cartões SD adotados.
Front-ends disponíveis
É possível iniciar as DSUs usando adb
, um app OEM ou o carregador de DSU de um clique (no
Android 11 ou em versões mais recentes).
Iniciar as DSUs usando o adb
Para iniciar as DSUs usando adb, digite estes comandos:
$ 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
Iniciar a DSU usando um app
O principal ponto de entrada para as DSUs é a 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);
}
É necessário agrupar/pré-instalar esse app no dispositivo. Como
DynamicSystemClient
é uma API do sistema, não é possível criar o app com a API SDK normal
nem publicar no Google Play. O objetivo do app é:
- Busque uma lista de imagens e o URL correspondente com um esquema definido pelo fornecedor.
- Corresponda as imagens na lista com o dispositivo e mostre imagens compatíveis para o usuário selecionar.
Invoque
DynamicSystemClient.start
desta forma:DynamicSystemClient aot = new DynamicSystemClient(...) aot.start( ...URL of the selected image..., ...uncompressed size of the selected image...);
O URL aponta para um arquivo de imagem do sistema compactado por gzip, não esparso, que pode ser criado com os seguintes comandos:
$ simg2img ${OUT}/system.img ${OUT}/system.raw
$ gzip ${OUT}/system.raw
$ ls ${OUT}/system.raw.gz
O nome do arquivo deve seguir este formato:
<android version>.<lunch name>.<user defined title>.raw.gz
Exemplos:
o.aosp_taimen-userdebug.2018dev.raw.gz
p.aosp_taimen-userdebug.2018dev.raw.gz
Carregador de DSU com um clique
O Android 11 apresenta o carregador de DSU de um clique, que é um front-end nas configurações do desenvolvedor.
Figura 1. Como iniciar o carregador de DSU
Quando o desenvolvedor clica no botão DSU Loader, ele busca um descritor DSU JSON pré-configurado na Web e mostra todas as imagens aplicáveis no menu flutuante. Selecione uma imagem para iniciar a instalação da DSU. O progresso será mostrado na barra de notificações.
Figura 2. Progresso da instalação da imagem de DSU
Por padrão, o carregador de DSU carrega um descritor JSON que contém as imagens GSI. As seções a seguir demonstram como criar pacotes de DSU assinados por OEM e carregá-los do carregador de DSU.
Sinalização de recurso
O recurso DSU está na flag de recurso settings_dynamic_android
. Antes
de usar as DSUs, verifique se a sinalização de recurso correspondente está ativada.
Figura 3. Como ativar a flag de recurso
A interface da flag de recurso pode ficar indisponível em um dispositivo com um build do usuário. Nesse
caso, use o comando adb
:
$ adb shell setprop persist.sys.fflag.override.settings_dynamic_system 1
O fornecedor hospeda imagens do sistema no GCE (opcional)
Um dos locais de armazenamento possíveis para as imagens do sistema é o bucket do Google Compute Engine (GCE). O administrador de lançamento usa o console de armazenamento do GCP para adicionar/excluir/mudar a imagem do sistema lançada.
As imagens precisam ter acesso público, como mostrado aqui:
Figura 4. Acesso público no GCE
O procedimento para tornar um item público está disponível na documentação do Google Cloud.
DSU com várias partições em arquivo ZIP
A partir do Android 11, a DSU pode ter mais de uma
partição. Por exemplo, ele pode conter uma product.img
além da
system.img
. Quando o dispositivo é inicializado, a init
do primeiro estágio detecta as
partições DSU instaladas e substitui a partição no dispositivo temporariamente, quando
a DSU instalada é ativada. O pacote DSU pode conter uma partição que não
tem uma partição correspondente no dispositivo.
Figura 5. Processo de DSU com várias partições
DSU assinada pelo OEM
Para garantir que todas as imagens em execução no dispositivo sejam autorizadas pelo fabricante do dispositivo, todas as imagens em um pacote de DSU precisam ser assinadas. Por exemplo, suponha que haja um pacote de DSU que contenha duas imagens de partição, como abaixo:
dsu.zip {
- system.img
- product.img
}
Tanto system.img
quanto product.img
precisam ser assinados pela chave OEM antes de serem
colocados no arquivo ZIP. A prática comum é usar um algoritmo
assimétrico, por exemplo, RSA, em que a chave secreta é usada para assinar o pacote e a
chave pública é usada para verificá-lo. O ramdisk do primeiro estágio precisa incluir a chave pública
de pareamento, por exemplo, /avb/*.avbpubkey
. Se o dispositivo já tiver adotado o AVB, o
procedimento de assinatura existente será suficiente. As seções a seguir ilustram o
processo de assinatura e destacam o posicionamento da chave pública do AVB que é usada para
verificar as imagens no pacote de DSU.
Descritor JSON de DSU
O descritor JSON das DSUs descreve os pacotes das DSUs. Ele oferece suporte a duas primitivas.
Primeiro, a primitiva include
inclui descritores JSON adicionais ou redireciona
o carregador de DSU para um novo local. Exemplo:
{
"include": ["https://.../gsi-release/gsi-src.json"]
}
Em segundo lugar, a primitiva image
é usada para descrever pacotes de DSU lançados. Dentro
do primitivo da imagem há vários atributos:
Os atributos
name
edetails
são strings que aparecem na caixa de diálogo para que o usuário selecione.Os atributos
cpu_api
,vndk
eos_version
são usados para verificações de compatibilidade, que são descritas na próxima seção.O atributo
pubkey
opcional descreve a chave pública que é pareada com a chave secreta usada para assinar o pacote DSU. Quando ela é especificada, o serviço de DSU pode conferir se o dispositivo tem a chave usada para verificar o pacote delas. Isso evita a instalação de um pacote DSU não reconhecido, por exemplo, a instalação de um DSU assinado por OEM-A em um dispositivo feito por OEM-B.O atributo
tos
opcional aponta para um arquivo de texto que descreve os termos de serviço do pacote DSU correspondente. Quando um desenvolvedor seleciona um pacote de DSU com o atributo "Termos de Serviço" especificado, a caixa de diálogo mostrada na Figura 6 é aberta, solicitando que o desenvolvedor aceite os termos de serviço antes de instalar o pacote de DSU.Figura 6. Caixa de diálogo dos Termos de Serviço
Para referência, confira um descritor JSON de DSU para a 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"
}
]
}
Gerenciamento de compatibilidade
Vários atributos são usados para especificar a compatibilidade entre um pacote de DSU e o dispositivo local:
cpu_api
é uma string que descreve a arquitetura do dispositivo. Esse atributo é obrigatório e é comparado com a propriedade do sistemaro.product.cpu.abi
. Os valores precisam ser exatamente iguais.os_version
é um número inteiro opcional que especifica uma versão do Android. Por exemplo, no Android 10,os_version
é10
e no Android 11,os_version
é11
. Quando esse atributo é especificado, ele precisa ser igual ou maior que a propriedade do sistemaro.system.build.version.release
. Essa verificação é usada para evitar a inicialização de uma imagem GSI do Android 10 em um dispositivo do fornecedor do Android 11, que não tem suporte no momento. A inicialização de uma imagem GSI do Android 11 em um dispositivo Android 10 é permitida.vndk
é uma matriz opcional que especifica todos os VNDKs incluídos no pacote DSU. Quando ela é especificada, o carregador de DSU verifica se o número extraído da propriedade do sistemaro.vndk.version
está incluído.
Revogar chaves DSU por motivos de segurança
No caso extremamente raro em que o par de chaves RSA usado para assinar as imagens da DSU é comprometido, o ramdisk precisa ser atualizado o mais rápido possível para remover a chave comprometida. Além de atualizar a partição de inicialização, é possível bloquear chaves comprometidas usando uma lista de revogação de chaves de DSU (lista de proibições de chaves) de um URL HTTPS.
A lista de revogação de chaves DSU contém uma lista de chaves públicas AVB revogadas. Durante a instalação do DSU, as chaves públicas dentro das imagens do DSU são validadas com a lista de revogação. Se as imagens contiverem uma chave pública revogada, o processo de instalação das DSUs será interrompido.
O URL da lista de revogação de chaves precisa ser um URL HTTPS para garantir a segurança e é especificado em uma string de recurso:
frameworks/base/packages/DynamicSystemInstallationService/res/values/strings.xml@key_revocation_list_url
O valor da string é
https://dl.google.com/developers/android/gsi/gsi-keyblacklist.json
, que é uma
lista de revogação para chaves GSI lançadas pelo Google. Essa string de recursos pode ser
sobreposta e personalizada para que os OEMs que adotarem o recurso de DSU possam fornecer e
manter a própria lista de proibições de chaves. Isso oferece uma maneira de o OEM bloquear
certas chaves públicas sem atualizar a imagem do ramdisk do dispositivo.
O formato da lista de revogação é:
{
"entries":[
{
"public_key":"bf14e439d1acf231095c4109f94f00fc473148e6",
"status":"REVOKED",
"reason":"Key revocation test key"
},
{
"public_key":"d199b2f29f3dc224cca778a7544ea89470cbef46",
"status":"REVOKED",
"reason":"Key revocation test key"
}
]
}
public_key
é o resumo SHA-1 da chave revogada, no formato descrito na seção Como gerar a chave pública do AVB.status
indica o status de revogação da chave. No momento, o único valor aceito éREVOKED
.reason
é uma string opcional que descreve o motivo da revogação.
Procedimentos das DSUs
Esta seção descreve como executar vários procedimentos de configuração de DSU.
Gerar um novo par de chaves
Use o comando openssl
para gerar um par de chaves RSA privada/pública no formato .pem
(por exemplo, com tamanho de 2.048 bits):
$ openssl genrsa -out oem_cert_pri.pem 2048
$ openssl rsa -in oem_cert_pri.pem -pubout -out oem_cert_pub.pem
A chave privada pode não ser acessível e é mantida apenas em um módulo de segurança de hardware (HSM). Nesse caso, pode haver um certificado de chave pública x509 disponível após a geração da chave. Consulte a seção Como adicionar a chave pública de pareamento ao ramdisk para consultar instruções sobre como gerar a chave pública AVB de um certificado x509.
Para converter um certificado x509 para o formato PEM:
$ openssl x509 -pubkey -noout -in oem_cert_pub.x509.pem > oem_cert_pub.pem
Pule esta etapa se o certificado já for um arquivo PEM.
Adicionar a chave pública de pareamento ao ramdisk
O oem_cert.avbpubkey
precisa ser colocado em /avb/*.avbpubkey
para verificar o
pacote DSU assinado. Primeiro, converta a chave pública no formato PEM para o formato de chave
pública AVB:
$ avbtool extract_public_key --key oem_cert_pub.pem --output oem_cert.avbpubkey
Em seguida, inclua a chave pública no ramdisk da primeira etapa seguindo as etapas abaixo.
Um módulo pré-criado foi adicionado para copiar o
avbpubkey
. Por exemplo, adicionedevice/<company>/<board>/oem_cert.avbpubkey
edevice/<company>/<board>/avb/Android.mk
com um conteúdo como este: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)
Faça com que o destino droidcore dependa do
oem_cert.avbpubkey
adicionado:droidcore: oem_cert.avbpubkey
Gerar o atributo pubkey do AVB no descritor JSON
O oem_cert.avbpubkey
está no formato binário de chave pública da AVB. Use SHA-1 para torná-lo legível antes de colocá-lo no descritor JSON:
$ sha1sum oem_cert.avbpubkey | cut -f1 -d ' '
3e62f2be9d9d813ef5........866ac72a51fd20
Esse será o conteúdo do atributo pubkey
do descritor JSON.
"images":[
{
...
"pubkey":"3e62f2be9d9d813ef5........866ac72a51fd20",
...
},
Assinar um pacote de DSU
Use um destes métodos para assinar um pacote DSU:
Método 1: reutilizar o artefato feito pelo processo original de assinatura AVB para criar um pacote de DSU. Uma abordagem alternativa é extrair as imagens já assinadas do pacote de lançamento e usar as imagens extraídas para criar o arquivo ZIP diretamente.
Método 2: use os comandos abaixo para assinar partições DSU se a chave privada estiver disponível. Cada
img
em um pacote de DSU (o arquivo ZIP) é assinado separadamente:$ 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
Para mais informações sobre como adicionar add_hashtree_footer
usando avbtool
, consulte
Como usar a avbtool.
Verificar o pacote de DSU localmente
É recomendável verificar todas as imagens locais com a chave pública de pareamento usando estes comandos:
for partition in system product; do
avbtool verify_image --image ${OUT}/${partition}.img --key oem_cert_pub.pem
done
A saída esperada é esta:
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
Criar um pacote de DSU
O exemplo a seguir cria um pacote de DSU que contém um system.img
e um
product.img
:
dsu.zip {
- system.img
- product.img
}
Depois que as duas imagens forem assinadas, use o seguinte comando para criar o arquivo ZIP:
$ mkdir -p dsu
$ cp ${OUT}/system.img dsu
$ cp ${OUT}/product.img dsu
$ cd dsu && zip ../dsu.zip *.img && cd -
Personalizar as DSUs com um clique
Por padrão, o carregador de DSU aponta para um metadado de imagens GSI, que é
https://...google.com/.../gsi-src.json
.
Os OEMs podem substituir a lista definindo a propriedade persist.sys.fflag.override.settings_dynamic_system.list
que aponta para o próprio descritor JSON. Por exemplo, um OEM pode
fornecer metadados JSON que incluem a GSI, além de imagens reservadas do OEM, como estas:
{
"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"
},
}
É possível que um OEM encadeie metadados DSU publicados, como mostrado na Figura 7.
Figura 7. Encadeamento de metadados de DSU publicados