Atualizações dinâmicas do sistema

As Atualizações dinâmicas do sistema (DSU) permitem criar uma imagem do sistema Android que os usuários podem fazer downloads da Internet e testar sem o risco de serem corrompidos a imagem atual do sistema. Este documento descreve como oferecer suporte às DSUs.

Requisitos do kernel

Consulte Como implementar partições dinâmicas para requisitos do kernel.

Além disso, as DSUs também usam o recurso kernel device-mapper-verity (dm-verity) para verificar a imagem do sistema Android. Portanto, você precisa ativar o seguinte kernel configurações:

  • CONFIG_DM_VERITY=y
  • CONFIG_DM_VERITY_FEC=y

Requisitos de partição

A partir do Android 11, as DSUs exigem a /data para usar o sistema de arquivos F2FS ou ext4. O F2FS oferece melhor desempenho e é recomendado, mas a diferença deve ser pequena.

Confira alguns exemplos de quanto tempo leva uma atualização dinâmica do sistema com um Pixel dispositivo:

  • Como usar F2FS:
    • 109s, usuário 8G, sistema 867M, tipo de sistema de arquivos: F2FS: criptografia=aes-256-xts:aes-256-cts
    • 104s, usuário 8G, sistema de 867M, tipo de sistema de arquivos: F2FS: encryption=ice
  • Usando ext4:
    • 135s, usuário 8G, sistema 867M, tipo de sistema de arquivos: ext4: criptografia=aes-256-xts:aes-256-cts

Caso sua plataforma leve muito mais tempo, verifique se o suporte contém qualquer flag que faça a gravação de "sync", ou é possível especificar uma para melhorar o desempenho.

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 F2FS, inclua todos os patches relacionados a F2FS disponíveis no Kernel comum do Android:

As DSUs foram desenvolvidas e testadas com kernel/comum 4.9. Recomenda-se usar kernel 4.9 e superior 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. As DSUs consome dois slots de chave extras. Se um OEM tiver uma HAL de tecelão, ele precisará ter slots suficientes para uma imagem genérica do sistema (GSI) e uma imagem do host.

HAL de assistente

A HAL Gatekeeper precisa oferecem suporte a grandes valores USER_ID, porque a GSI desloca UIDs para a HAL +1000000.

Verificar inicialização

Se você quiser oferecer suporte à inicialização de Imagens GSI para desenvolvedores, no estado BLOQUEADO sem desativar 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 as DSUs, a imagem do sistema Android baixada precisa ser mais recente do que o imagem atual do sistema no dispositivo. Isso é feito comparando o patch de segurança níveis na Inicialização verificada do Android (AVB) Descritor de propriedade AVB das duas imagens do sistema: Prop: com.android.build.system.security_patch -> '2019-04-05'.

Para dispositivos que não usam o AVB, defina o nível do patch de segurança do sistema atual imagem na cmdline do kernel ou 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~1,5 G)
  • 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 DSU instância. As DSUs também oferecem suporte à alocação de um cartão SD. Quando um cartão SD é presente, ele tem a maior prioridade para a alocação. O suporte a cartão 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. Não compatível com DSUs 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 (na Android 11 ou mais recente).

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 as DSUs usando um app

O principal ponto de entrada para as DSUs é o android.os.image.DynamicSystemClient.java API:

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);
    }

Você precisa empacotar/pré-instalar este app no dispositivo. Devido ao A DynamicSystemClient é uma API do sistema. Não é possível criar o app com a API do SDK do Firebase. Não é possível publicá-lo no Google Play. O objetivo deste app é:

  1. Busque uma lista de imagens e o URL correspondente com um esquema definido pelo fornecedor.
  2. Corresponda as imagens na lista com as do dispositivo e mostre imagens compatíveis para o usuário selecionar.
  3. Invoque DynamicSystemClient.start assim:

    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 com gzip e não esparso, que você pode 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 introduz o carregador de DSU de um clique, que é um front-end nas configurações do desenvolvedor.

Como iniciar o carregador de DSU

Figura 1. Como iniciar o carregador de DSU

Quando o desenvolvedor clica no botão DSU Loader, ele busca um Descritor JSON de DSU da Web e exibe todas as imagens aplicáveis na no menu flutuante. Selecione uma imagem para iniciar a instalação das DSUs e o andamento é exibido na barra de notificações.

Progresso da instalação da imagem de DSU

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 e carregar pacotes de DSU assinados pelo OEM do carregador de DSU.

Sinalização de recurso

O recurso DSU está na flag de recurso settings_dynamic_android. Antes usando as DSUs, verifique se a flag de recurso correspondente está ativada.

Ativação da flag de recurso.

Figura 3. Como ativar a sinalização de recurso

A interface da flag de recurso pode não estar disponível em um dispositivo que executa uma versão do usuário. Em Neste caso, use o comando adb:

$ adb shell setprop persist.sys.fflag.override.settings_dynamic_system 1

Imagens do sistema host do fornecedor no GCE (opcional)

Um dos locais de armazenamento possíveis para as imagens do sistema é o do Compute Engine (GCE). O administrador da versão usa Console de armazenamento do GCP para adicionar/excluir/alterar a imagem do sistema lançada.

As imagens precisam ser de acesso público, conforme mostrado aqui:

Acesso público no GCE

Figura 4. Acesso público no GCE

O procedimento para tornar um item público está disponível no Documentação do Google Cloud.

DSU com várias partições em arquivo ZIP

A partir do Android 11, as DSUs podem ter mais de um partição. Por exemplo, ele pode conter um product.img, além do system.img. Quando o dispositivo é inicializado, a init do primeiro estágio detecta a as partições de DSU instaladas e substitui temporariamente a partição no dispositivo, quando se as DSUs instaladas estão ativadas. O pacote DSU pode conter uma partição que não contém uma partição correspondente no dispositivo.

Processo de DSU com várias partições

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 por ele fabricante, todas as imagens em um pacote de DSU precisam ser assinadas. Por exemplo: suponha que há um pacote DSU que contém duas imagens de partição, como abaixo:

dsu.zip {
    - system.img
    - product.img
}

Tanto system.img quanto product.img precisam ser assinados pela chave do OEM antes de são colocados no arquivo ZIP. A prática comum é usar um modelo algoritmo, por exemplo, RSA, em que a chave secreta é usada para assinar o pacote e o a chave pública é usada para verificá-la. O ramdisk do primeiro estágio precisa incluir o parâmetro chave pública, por exemplo, /avb/*.avbpubkey. Se o dispositivo já adotou o AVB, o o procedimento de assinatura existente será suficiente. As seções a seguir ilustram processo de assinatura e destacar o posicionamento do código público AVB que é usado para e verificar as imagens no pacote DSU.

Descritor JSON de DSU

O descritor JSON das DSUs descreve os pacotes das DSUs. Ele é compatível com dois primitivos. Primeiro, o primitivo include inclui descritores ou redirecionamentos JSON adicionais carregador de DSU para um novo local. Exemplo:

{
    "include": ["https://.../gsi-release/gsi-src.json"]
}

Segundo, o primitivo image é usado para descrever pacotes de DSU lançados. Interior do primitivo da imagem há vários atributos:

  • Os atributos name e details são strings mostradas na caixa de diálogo para o usuário selecionar.

  • Os atributos cpu_api, vndk e os_version são usados para verificações de compatibilidade, que são descritas na próxima seção.

  • O atributo pubkey opcional descreve o chave que está associada à chave secreta usada para assinar o pacote de DSUs. Quando especificado, o serviço DSU pode verificar se o dispositivo tem a chave usada para verificar o pacote de DSUs. Isso evita a instalação de uma DSU não reconhecida por exemplo, instalando uma DSU assinada pela OEM-A em um dispositivo fabricado pela OEM-B

  • O atributo opcional tos aponta para um arquivo de texto que descreve os termos de serviço do pacote de DSU correspondente. Quando um desenvolvedor seleciona um pacote de DSU com o atributo dos termos de serviço especificado, o a caixa de diálogo mostrada na figura 6 é aberta, pedindo ao desenvolvedor para aceitar os termos antes de instalar o pacote DSU.

    Caixa de diálogo dos Termos de Serviço

    Figura 6. Caixa de diálogo dos Termos de Serviço

Para referência, veja 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 DSUs. e o dispositivo local:

  • cpu_api é uma string que descreve a arquitetura do dispositivo. Este atributo é obrigatório e é comparado com a propriedade do sistema ro.product.cpu.abi. Os valores precisam ser exatamente iguais.

  • os_version é um número inteiro opcional que especifica uma versão do Android. Para exemplo, no Android 10, os_version é 10 e no Android 11, os_version é 11. Quando isso atributo for especificado, ele precisa ser igual ou maior que ro.system.build.version.release propriedade do sistema. Essa verificação é usada para impedir a inicialização de uma imagem GSI do Android 10 em um dispositivo com Android 11 dispositivo do fornecedor, 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 em do pacote DSU. Quando especificado, o carregador de DSU verifica se o número extraído da propriedade do sistema ro.vndk.version é incluído.

Revogar chaves de DSU para segurança

No caso extremamente raro em que o par de chaves RSA usado para assinar as imagens de DSU é comprometido, o ramdisk deve ser atualizado o mais rápido possível para 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 DSU (lista de proibições de chaves) de um servidor URL.

A lista de revogação de chaves de DSU contém uma lista de chaves públicas AVB revogadas. Durante a instalação das DSUs, as chaves públicas nas imagens das DSUs são validadas com a lista de revogação. Se as imagens contiverem um URL revogado 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 forç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 é um de revogação de chaves GSI lançadas pelo Google. Essa string de recurso pode ser sobrepostas e personalizadas, para que OEMs que adotam o recurso de DSU possam fornecer e manter sua própria lista negra de chaves. Com isso, o OEM pode bloquear determinadas 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 o código público do AVB nesta seção.
  • status indica o status de revogação da chave. Atualmente, o único o valor compatível é 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 em .pem formato (por exemplo, com tamanho de 2048 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 só é mantida em um módulo de segurança de hardware (HSM, na sigla em inglês). Nesse caso, pode haver um certificado de chave pública x509 disponível após a chave geração de imagens. Consulte Como adicionar a chave de pareamento ao ramdisk para 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 do pacote de DSU assinado. Primeiro, converta a chave pública no formato PEM em público AVB formato de chave:

$ 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.

  1. Adicione um módulo pré-criado para copiar o avbpubkey. Por exemplo, adicione device/<company>/<board>/oem_cert.avbpubkey e device/<company>/<board>/avb/Android.mk com 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)
    
  2. 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 de DSU:

  • Método 1: reutilizar o artefato criado pelo processo original de assinatura AVB para criar um pacote DSU. Uma abordagem alternativa é extrair os dados já assinados do pacote de lançamento e usar as imagens extraídas para criar o arquivo ZIP arquivo diretamente.

  • Método 2: usar os seguintes comandos para assinar partições de DSU se a função está 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 o avbtool.

Verificar o pacote de DSU localmente

É recomendável comparar todas as imagens locais com a chave pública de pareamento 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 é semelhante a 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 metadados de imagens GSI que são https://...google.com/.../gsi-src.json:

Os OEMs podem substituir a lista definindo a persist.sys.fflag.override.settings_dynamic_system.list. que aponta para o próprio descritor JSON. Por exemplo, um OEM pode fornecem metadados JSON que incluem a GSI, além de imagens reservadas do OEM, como este:

{
    "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.

Como encadear metadados de DSU publicados

Figura 7. Como encadear metadados de DSU publicados