Atualizações dinâmicas do sistema

As atualizações dinâmicas do sistema (DSU) permitem que você crie uma imagem do sistema Android que os usuários podem baixar da Internet e experimentar sem o risco de corromper a imagem atual do sistema. Este documento descreve como oferecer suporte a DSU.

Requisitos do kernel

Consulte Implementando partições dinâmicas para obter os requisitos do kernel.

Além disso, o DSU conta com o recurso de kernel device-mapper-verity (dm-verity) para verificar a imagem do sistema Android. Então você deve habilitar as seguintes configurações do kernel:

  • CONFIG_DM_VERITY=y
  • CONFIG_DM_VERITY_FEC=y

Requisitos de partição

A partir do Android 11, o DSU requer 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.

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

  • Usando 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 867M, tipo de sistema de arquivos: F2FS: criptografia=ice
  • Usando ext4:
    • 135s, usuário 8G, sistema 867M, tipo de sistema de arquivos: ext4: criptografia=aes-256-xts:aes-256-cts

Se levar muito mais tempo em sua plataforma, você pode querer verificar se o sinalizador de montagem contém algum sinalizador que faz a gravação “sincronizar” ou pode especificar um sinalizador “assíncrono” explicitamente para obter melhor desempenho.

A partição de metadata (16 MB ou maior) é necessária para armazenar dados relacionados às imagens instaladas. Deve ser montado durante a montagem do primeiro estágio.

A partição userdata deve 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 .

DSU foi desenvolvido e testado com kernel/common 4.9. Recomenda-se usar o kernel 4.9 e superior para este recurso.

Comportamento HAL do fornecedor

Tecelão HAL

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

Porteiro HAL

O Gatekeeper HAL precisa oferecer suporte a valores grandes de USER_ID , porque o GSI desloca UIDs para o HAL em +1.000.000.

Verificar inicialização

Se você quiser dar suporte à inicialização de Imagens GSI do desenvolvedor no estado LOCKED sem desabilitar 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 o DSU, a imagem do sistema Android baixada deve ser mais recente do que a imagem do sistema atual no dispositivo. Isso é feito comparando os níveis de patch de segurança no descritor de propriedade AVB do Android Verified Boot (AVB) de ambas as imagens do sistema: Prop: com.android.build.system.security_patch -> '2019-04-05' .

Para dispositivos que não usam AVB, coloque o nível de patch de segurança da imagem do sistema atual no cmdline do kernel ou bootconfig com o bootloader: androidboot.system.security_patch=2019-04-05 .

Requisitos de hardware

Ao executar uma instância 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 o GSI

Recomendamos reservar pelo menos 10 GB de espaço livre antes de iniciar uma instância DSU. DSU também suporta alocação de um cartão SD. Quando um cartão SD está presente, ele tem a prioridade mais alta para a alocação. O suporte ao cartão SD é fundamental 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. DSU não suporta cartões SD adotados .

Interfaces disponíveis

Você pode iniciar o DSU usando adb , um aplicativo OEM ou o carregador de DSU de um clique (no Android 11 ou superior).

Iniciando o DSU usando o adb

Para iniciar o DSU usando o 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

Iniciando o DSU usando um aplicativo

O principal ponto de entrada para o 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);
    }

Você deve agrupar/pré-instalar este aplicativo no dispositivo. Como DynamicSystemClient é uma API do sistema, você não pode criar o aplicativo com a API SDK normal e não pode publicá-lo no Google Play. O objetivo deste aplicativo é:

  1. Busque uma lista de imagens e a URL correspondente com um esquema definido pelo fornecedor.
  2. Combine as imagens na lista com o 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 não esparso e compactado em gzip, que você pode criar 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 de um clique

O Android 11 apresenta o carregador DSU de um clique, que é um front-end nas configurações do desenvolvedor.

Iniciando o carregador DSU

Figura 1. Iniciando o carregador de DSU

Quando o desenvolvedor clica no botão DSU Loader , ele busca um descritor DSU JSON pré-configurado da web e exibe todas as imagens aplicáveis ​​no menu flutuante. Selecione uma imagem para iniciar a instalação do DSU e o progresso é exibido na barra de notificação.

Progresso da instalação da imagem DSU

Figura 2. Progresso da instalação da imagem DSU

Por padrão, o carregador DSU carrega um descritor JSON que contém as imagens GSI. As seções a seguir demonstram como criar pacotes DSU assinados por OEM e carregá-los do carregador de DSU.

Sinalizador de recurso

O recurso DSU está sob o sinalizador de recurso settings_dynamic_android . Antes de usar o DSU, certifique-se de que o sinalizador de recurso correspondente esteja ativado.

Ativando o sinalizador de recurso.

Figura 3. Ativando o sinalizador de recurso

A IU do sinalizador de recurso pode estar indisponível em um dispositivo que executa uma compilação de usuário. Nesse 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 possíveis locais de armazenamento para as imagens do sistema é o bucket do Google Compute Engine (GCE). O administrador da versão usa o console de armazenamento do GCP para adicionar/excluir/alterar a imagem do sistema lançada.

As imagens devem 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, o DSU pode ter mais de uma partição. Por exemplo, pode conter um product.img além do system.img . Quando o dispositivo inicializa, o primeiro estágio init detecta as partições de DSU instaladas e substitui a partição no dispositivo temporariamente, quando a DSU instalada está habilitada. O pacote DSU pode conter uma partição que não possui uma partição correspondente no dispositivo.

Processo DSU com várias partições

Figura 5. Processo DSU com várias partições

DSU assinado 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 DSU devem 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
}

Ambos system.img e product.img devem ser assinados pela chave OEM antes de serem colocados no arquivo ZIP. A prática comum é usar um algoritmo assimétrico, por exemplo, RSA, onde a chave secreta é usada para assinar o pacote e a chave pública é usada para verificá-lo. O ramdisk do primeiro estágio deve incluir a chave pública de pareamento, por exemplo, /avb/*.avbpubkey . Se o dispositivo já adotou 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 de publicação do AVB que é usada para verificar as imagens no pacote DSU.

Descritor DSU JSON

O descritor DSU JSON descreve os pacotes DSU. Ele suporta duas primitivas. Primeiro, a primitiva include inclui descritores JSON adicionais ou redireciona o carregador DSU para um novo local. Por exemplo:

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

Em segundo lugar, a primitiva de image é usada para descrever os pacotes DSU lançados. Dentro da primitiva da imagem existem vários atributos:

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

  • Os 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 opcional pubkey descreve a chave pública que faz par com a chave secreta usada para assinar o pacote DSU. Quando especificado, o serviço DSU pode verificar se o dispositivo possui a chave usada para verificar o pacote DSU. Isso evita a instalação de um pacote DSU não reconhecido, por exemplo, a instalação de um DSU assinado pelo OEM-A em um dispositivo fabricado pelo OEM-B.

  • O atributo opcional tos 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 de 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.

    Caixa de diálogo de termos de serviço

    Figura 6. Caixa de diálogo Termos de serviço

Para referência, aqui está um descritor DSU JSON para o 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 DSU 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 . Seus valores devem corresponder exatamente.

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

Revogando chaves DSU para segurança

No caso extremamente raro em que o par de chaves RSA usado para assinar as imagens DSU for comprometido, o ramdisk deve ser atualizado o mais rápido possível para remover a chave comprometida. Além de atualizar a partição de inicialização, você pode bloquear chaves comprometidas usando uma lista de revogação de chaves DSU (lista negra de chaves) de um URL HTTPS.

A lista de revogação de chave 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 conterem uma chave pública revogada, o processo de instalação da DSU será interrompido.

O URL da lista de revogação de chaves deve ser um URL HTTPS para garantir o nível de 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 cadeia de recursos pode ser sobreposta e personalizada, para que os OEMs que adotam o recurso DSU possam fornecer e manter sua própria lista negra de chaves. Isso fornece uma maneira para o OEM bloquear determinadas chaves públicas sem atualizar a imagem do disco ram 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 de geração da chave pub AVB .
  • status indica o status de revogação da chave. Atualmente, o único valor suportado é 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.

Gerando 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 é 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 Adicionando o pubkey de emparelhamento ao ramdisk para obter 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

Ignore esta etapa se o certificado já for um arquivo PEM.

Adicionando a pubkey de emparelhamento ao ramdisk

O oem_cert.avbpubkey deve 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 do primeiro estágio com as etapas a seguir.

  1. Adicione um módulo pré-compilado 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
    

Gerando o atributo pubkey AVB no descritor JSON

O oem_cert.avbpubkey está no formato binário de chave pública 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

Este será o conteúdo do atributo pubkey do descritor JSON.

   "images":[
      {
         ...
         "pubkey":"3e62f2be9d9d813ef5........866ac72a51fd20",
         ...
      },

Assinando um pacote de DSU

Use um destes métodos para assinar um pacote DSU:

  • Método 1: Reutilize o artefato criado pelo processo de assinatura AVB original para criar um pacote 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 DSU se a chave privada estiver disponível. Cada img dentro de um pacote 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 obter mais informações sobre como adicionar add_hashtree_footer usando avbtool , consulte Usando avbtool .

Verificando o pacote DSU localmente

É recomendável 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 é assim:

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

Fazendo um pacote DSU

O exemplo a seguir cria um pacote DSU que contém um system.img e um product.img :

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

Depois que ambas as 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 -

Personalizando o DSU de 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 propriedade persist.sys.fflag.override.settings_dynamic_system.list que aponta para seu próprio descritor JSON. Por exemplo, um OEM pode fornecer metadados JSON que incluem GSI, bem como 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. Encadeamento de metadados de DSU publicados