Atualizações dinâmicas do sistema

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 às DSUs.

Requisitos do kernel

Consulte Implementar partições dinâmicas para ver os requisitos do kernel.

Além disso, as DSUs dependem do recurso de kernel do 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, as DSUs exigem que a partição /data use o sistema de arquivos F2FS ou ext4. O F2FS oferece melhor desempenho e é recomendado, mas a diferença deve ser insignificante.

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

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

Se levar muito mais tempo na sua plataforma, verifique se a flag de ativação contém alguma flag que faça a gravação "sync" ou especifique uma flag "async" explicitamente para ter um desempenho melhor.

A partição metadata (16 MB ou maior) é necessária para armazenar dados relacionados às imagens instaladas. Ela precisa ser ativada durante a ativação da primeira etapa.

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 o kernel/common 4.9. Recomendamos usar o kernel 4.9 e versões mais recentes para esse recurso.

Comportamento da HAL do fornecedor

HAL do Weaver

O HAL do Weaver fornece um número fixo de slots para armazenar chaves de usuário. A DSU consome dois slots de chave extras. Se um OEM tiver uma HAL do Weaver, ela precisará ter slots suficientes para uma imagem genérica do sistema (GSI) e uma imagem de host.

HAL do Gatekeeper

A HAL do Gatekeeper precisa oferecer suporte a valores USER_ID grandes, porque a GSI compensa os 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 do 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 transferida por download 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 do Android Inicialização verificada (AVB) das duas 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 do patch de segurança da imagem do sistema atual na linha de comando do kernel ou no bootconfig com o carregador de inicialização: androidboot.system.security_patch=2019-04-05.

Requisitos de hardware

Ao iniciar 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 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 instância de DSU. A DSU também oferece suporte à alocação de um cartão SD. Quando um cartão SD está presente, ele tem a maior prioridade para a alocação. O suporte a cartões SD é fundamental para dispositivos de baixa potência que podem não ter armazenamento interno suficiente. Quando um cartão SD estiver presente, verifique se ele não está adotado. A DSU não oferece suporte a cartões SD adotados.

Front-ends disponíveis

É possível iniciar a DSU usando adb, um app OEM ou o carregador de DSU de um clique (no Android 11 ou mais recente).

Iniciar a DSU usando adb

Para iniciar a DSU usando adb, insira 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 ponto de entrada principal para a DSU é 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 do SDK normal nem publicá-lo no Google Play. O objetivo desse app é:

  1. Buscar uma lista de imagens e o URL correspondente com um esquema definido pelo fornecedor.
  2. Comparar as imagens na lista com o dispositivo e mostrar imagens compatíveis para o usuário selecionar.
  3. Invocar 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 não esparso e compactado com gzip, 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 precisa 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 de um clique

O Android 11 apresenta 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 Carregador de DSU, ele busca um descritor JSON de DSU 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 é mostrado na barra de notificação.

Progresso da instalação da imagem de DSU

Figura 2. Progresso da instalação da imagem da 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 pelo OEM e carregá-los no carregador de DSU.

Flag de recurso

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

Ativar a flag de recurso.

Figura 3. Como ativar a flag de recurso

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

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

Imagens do sistema de host do fornecedor 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 ou 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 na documentação do Google Cloud.

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

A partir do Android 11, a DSU pode ter mais de uma partição. Por exemplo, ela pode conter um product.img além do system.img. Quando o dispositivo é inicializado, a primeira etapa init detecta as partições de DSU instaladas e substitui temporariamente a partição no dispositivo quando a DSU instalada está ativada. O pacote de DSU pode conter uma partição que não tem 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 pelo fabricante, 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 do 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 da primeira etapa precisa incluir a chave pública de pareamento, por exemplo, /avb/*.avbpubkey. Se o dispositivo já tiver adotado o AVB, o procedimento de assinatura atual será suficiente. As seções a seguir ilustram o processo de assinatura e destacam o posicionamento da chave pública do AVB usada para verificar as imagens no pacote de DSU.

Descritor JSON da DSU

O descritor JSON da DSU descreve pacotes de DSU. Ele oferece suporte a duas primitivas. Primeiro, a primitiva include inclui outros descritores JSON ou redireciona o carregador de DSU para um novo local. Por exemplo:

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

Em segundo lugar, a primitiva image é usada para descrever pacotes de DSU lançados. Dentro da primitiva de 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 a chave pública que é pareada com a chave secreta usada para assinar o pacote de DSU. Quando especificado, o serviço de DSU pode verificar se o dispositivo tem a chave usada para verificar o pacote de DSU. Isso evita a instalação de um pacote de DSU não reconhecido, por exemplo, a instalação de uma DSU assinada pelo OEM-A em um dispositivo fabricado pelo OEM-B.

  • O atributo tos opcional 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 Termos de Serviço especificado, a caixa de diálogo mostrada na Figura 6 é aberta, pedindo que o desenvolvedor aceite os Termos de Serviço antes de instalar o pacote de DSU.

    Caixa de diálogo dos Termos de Serviço

    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 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. Por exemplo, para o Android 10, os_version é 10 e, para o Android 11, os_version é 11. Quando esse atributo é especificado, ele precisa ser igual ou maior que a propriedade do sistema ro.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, o que não é compatível 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 de DSU. Quando especificado, o carregador de DSU verifica se o número extraído da propriedade do sistema ro.vndk.version está 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 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 negra de chaves) de um URL HTTPS.

A lista de revogação de chaves de DSU contém uma lista de chaves públicas do AVB revogadas. Durante a instalação da DSU, as chaves públicas dentro das imagens de DSU são validadas com a lista de revogação. Se as imagens contiverem uma chave pública revogada, o processo de instalação da DSU 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 recurso pode ser sobreposta e personalizada para que os OEMs que adotam o recurso de DSU possam fornecer e manter a própria lista negra de chaves. Isso oferece uma maneira para o OEM 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 a chave pública do AVB.
  • status indica o status de revogação da chave. No momento, o único valor compatível é REVOKED.
  • reason é uma string opcional que descreve o motivo da revogação.

Procedimentos de DSU

Esta seção descreve como realizar vários procedimentos de configuração de DSU.

Gerar um novo par de chaves

Use o comando openssl para gerar um par de chaves privada/pública RSA no formato .pem (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 estar acessível e só é mantida 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 instruções sobre como gerar a chave pública do 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 de DSU assinado. Primeiro, converta a chave pública no formato PEM para o formato de chave pública do 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 com as etapas a seguir.

  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 do droidcore dependa do oem_cert.avbpubkey adicionado:

    droidcore: oem_cert.avbpubkey
    

Gerar o atributo de chave pública do AVB no descritor JSON

O oem_cert.avbpubkey está no formato binário de chave pública do AVB. Use o 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: reutilize o artefato criado pelo processo de assinatura do AVB original 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 a seguir para assinar partições de 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 o avbtool.

Verificar o pacote de DSU localmente

Recomendamos verificar todas as imagens locais em relação à chave pública de pareamento com 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 comando a seguir 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 a DSU de um clique

Por padrão, o carregador de DSU aponta para um metadados 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 GSI e imagens proprietárias do OEM, como esta:

{
    "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 de DSU publicados, conforme mostrado na Figura 7.

Encadeamento de metadados de DSU publicados

Figura 7. Como encadear metadados de DSU publicados