OTA para dispositivos A/B sem partições dinâmicas

O Android 10 oferece suporte a partições dinâmicas, um sistema de particionamento no espaço do usuário que pode criar, redimensionar e destruir partições durante atualizações OTA.

Esta página descreve como os clientes OTA redimensionam partições dinâmicas durante uma atualização para dispositivos A/B que foram lançados sem suporte a partições dinâmicas e como os clientes OTA fazem upgrade para o Android 10.

Contexto

Durante uma atualização de um dispositivo A/B para oferecer suporte a partições dinâmicas, a tabela de partições GUID (GPT) no dispositivo é preservada, portanto, não há partição super no dispositivo. Os metadados são armazenados em system_a e system_b, mas isso pode ser personalizado mudando BOARD_SUPER_PARTITION_METADATA_DEVICE.

Em cada um dos dispositivos de bloco, há dois slots de metadados. Apenas um slot de metadados em cada dispositivo de bloco é usado. Por exemplo, os metadados 0 em system_a e os metadados 1 em system_b correspondem às partições nos slots A e B, respectivamente. No momento da execução, não importa qual slot está sendo atualizado.

Nesta página, os slots de metadados são chamados de Metadata S (origem) e Metadata T (destino). Da mesma forma, as partições são chamadas de system_s, vendor_t e assim por diante.

Para mais informações sobre as configurações do sistema de build, consulte Atualizar dispositivos.

Para mais informações sobre como as partições pertencem a grupos de atualização, consulte Mudanças de configuração do dispositivo para novos dispositivos.

Um exemplo de metadados em um dispositivo é:

  • Dispositivo de bloco físico system_a
    • Metadados 0
      • Grupo foo_a
        • Partição lógica (dinâmica) system_a
        • Partição lógica (dinâmica) product_services_a
        • Outras partições atualizadas por Foo
      • Grupo bar_a
        • Partição lógica (dinâmica) vendor_a
        • Partição lógica (dinâmica) product_a
        • Outras partições atualizadas por Bar
    • Metadados 1 (não usados)
  • Dispositivo de bloco físico system_b
    • Metadados 0 (não usados)
    • Metadados 1
      • Grupo foo_b
        • Partição lógica (dinâmica) system_b
        • Partição lógica (dinâmica) product_services_b
        • Outras partições atualizadas por Foo
      • Grupo bar_b
        • Partição lógica (dinâmica) vendor_b
        • Partição lógica (dinâmica) product_b
        • Outras partições atualizadas por Bar

É possível usar a ferramenta lpdump em system/extras/partition_tools para despejar os metadados no dispositivo. Exemplo:

lpdump --slot 0 /dev/block/by-name/system_a
lpdump --slot 1 /dev/block/by-name/system_b

Fazer uma atualização

Em dispositivos com o Android 9 e versões anteriores, o cliente OTA no dispositivo não oferece suporte ao mapeamento de partições dinâmicas antes da atualização. Um conjunto adicional de patches é criado para que o mapeamento possa ser aplicado diretamente às partições físicas existentes.

O gerador OTA gera o arquivo super.img final que contém o conteúdo de todas as partições dinâmicas e, em seguida, divide a imagem em várias imagens que correspondem aos tamanhos dos dispositivos de bloco físico correspondentes ao sistema, fornecedor etc. Essas imagens são nomeadas super_system.img, super_vendor.img e assim por diante. O cliente OTA aplica essas imagens às partições físicas, em vez de aplicar as imagens às partições lógicas (dinâmicas).

Como o cliente OTA não sabe como mapear partições dinâmicas, todas as etapas pós-instalação são desativadas automaticamente para essas partições quando o pacote de atualização é gerado. Consulte Como configurar a pós-instalação para mais detalhes.

O fluxo de atualização é o mesmo do Android 9.

Antes da atualização:

ro.boot.dynamic_partitions=
ro.boot.dynamic_partitions_retrofit=

Após a atualização:

ro.boot.dynamic_partitions=true
ro.boot.dynamic_partitions_retrofit=true

Atualizações futuras após o retrofit

Após a atualização de retrofit, o cliente OTA é atualizado para funcionar com partições dinâmicas. As extensões das partições de origem nunca se estendem pelas partições físicas de destino.

Atualizar o fluxo usando um pacote de atualização regular

  1. Inicializar os metadados da partição super.
    1. Criar novos metadados M a partir de metadados S (metadados de origem). Por exemplo, se o Metadata S usa [system_s, vendor_s, product_s] como dispositivos de bloqueio, o novo Metadata M usa [system_t, vendor_t, product_t] como dispositivos de bloqueio. Todos os grupos e partições são descartados em M.
    2. Adicione grupos e partições de destino de acordo com o campo dynamic_partition_metadata no manifesto de atualização. O tamanho de cada partição pode ser encontrado em new_partition_info.
    3. Gravar M em Metadata T.
    4. Mapeie as partições adicionadas no mapeador de dispositivos como graváveis.
  2. Aplique a atualização nos dispositivos de bloco.
    1. Se necessário, mapeie as partições de origem no mapeador de dispositivos como somente leitura. Isso é necessário para o sideload porque as partições de origem não são mapeadas antes da atualização.
    2. Aplique uma atualização completa ou delta a todos os dispositivos de bloco no slot de destino.
    3. Monte as partições para executar o script pós-instalação e, em seguida, desmonte as partições.
  3. Desmapear as partições de destino.

Atualizar o fluxo usando um pacote de atualização de retrofit

Se o pacote de atualização de retrofit for aplicado em um dispositivo que já ativa partições dinâmicas, o cliente OTA vai aplicar o arquivo super.img dividido em dispositivos de bloco diretamente. O fluxo de atualização é semelhante a uma atualização de retrofit. Consulte Como fazer a retrocompatibilidade de uma atualização para saber mais.

Por exemplo, considere o seguinte:

  • A é o slot ativo.
  • system_a contém os metadados ativos no slot 0.
  • system_a, vendor_a e product_a são usados como dispositivos de bloco.

Quando o cliente OTA recebe um pacote de atualização de retrofit, ele aplica super_system.img em system_b físico, super_vendor.img em vendor_b físico e super_product.img em product_b físico. O dispositivo de bloco físico system_b contém os metadados corretos para mapear o system_b, vendor_b e product_b lógicos no momento da inicialização.

Gerar pacotes de atualização

OTA incremental

Ao gerar OTAs incrementais para dispositivos de retrofit, as atualizações dependem se o build base define ou não PRODUCT_USE_DYNAMIC_PARTITIONS e PRODUCT_RETROFIT_DYNAMIC_PARTITIONS.

  • Se o build base não definir as variáveis, essa será uma atualização de retrofit. O pacote de atualização contém o arquivo super.img fendido e desativa a etapa pós-instalação.
  • Se o build base definir as variáveis, isso será igual a uma atualização típica com partições dinâmicas. O pacote de atualização contém as imagens para partições lógicas (dinâmicas). A etapa pós-instalação pode ser ativada.

OTA completo

Dois pacotes OTA completos são gerados para dispositivos de retrofit.

  • $(PRODUCT)-ota-retrofit-$(TAG).zip sempre contém super.img dividido e desativa a etapa pós-instalação para atualizar a retrofit.
    • Ele é gerado com um argumento --retrofit_dynamic_partitions adicional para o script ota_from_target_files.
    • Ele pode ser aplicado a todos os builds.
  • $(PRODUCT)-ota-$(TAG).zip contém imagens lógicas para atualizações futuras.
    • Aplique isso apenas a builds com partições dinâmicas ativadas. Confira os detalhes abaixo sobre como fazer isso.

Rejeitar a atualização não retrofit em builds antigos

Aplique o pacote OTA completo normal apenas a builds com partições dinâmicas ativadas. Se o servidor OTA for configurado incorretamente e enviar esses pacotes para dispositivos com o Android 9 ou versões anteriores, os dispositivos não vão inicializar. O cliente OTA no Android 9 e versões anteriores não consegue identificar a diferença entre um pacote OTA de retrofit e um pacote OTA completo normal. Portanto, o cliente não rejeita o pacote completo.

Para impedir que o dispositivo aceite o pacote OTA completo, exija uma etapa pós-instalação para verificar a configuração do dispositivo atual. Exemplo:

device/device_name/dynamic_partitions/check_dynamic_partitions

#!/system/bin/sh
DP_PROPERTY_NAME="ro.boot.dynamic_partitions"
DP_RETROFIT_PROPERTY_NAME="ro.boot.dynamic_partitions_retrofit"

DP_PROPERTY=$(getprop ${DP_PROPERTY_NAME})
DP_RETROFIT_PROPERTY=$(getprop ${DP_RETROFIT_PROPERTY_NAME})

if [ "${DP_PROPERTY}" != "true" ] || [ "${DP_RETROFIT_PROPERTY}" != "true" ] ; then
    echo "Error: applied non-retrofit update on build without dynamic" \
         "partitions."
    echo "${DP_PROPERTY_NAME}=${DP_PROPERTY}"
    echo "${DP_RETROFIT_PROPERTY_NAME}=${DP_RETROFIT_PROPERTY}"
    exit 1
fi

device/device_name/dynamic_partitions/Android.mk

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE:= check_dynamic_partitions
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := EXECUTABLES
LOCAL_SRC_FILES := check_dynamic_partitions
LOCAL_PRODUCT_MODULE := true
include $(BUILD_PREBUILT)

device/device_name/device.mk

PRODUCT_PACKAGES += check_dynamic_partitions

# OPTIONAL=false so that the error in check_dynamic_partitions will be
# propagated to OTA client.
AB_OTA_POSTINSTALL_CONFIG += \
    RUN_POSTINSTALL_product=true \
    POSTINSTALL_PATH_product=bin/check_dynamic_partitions \
    FILESYSTEM_TYPE_product=ext4 \
    POSTINSTALL_OPTIONAL_product=false \

Quando o pacote OTA normal é aplicado em um dispositivo sem partições dinâmicas ativadas, o cliente OTA executa check_dynamic_partitions como uma etapa pós-instalação e rejeita a atualização.