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 iniciados 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. Por isso, 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 barra
    • 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 barra

Você pode usar a ferramenta lpdump em system/extras/partition_tools para despejar os metadados no seu dispositivo. Exemplo:

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

Retrofit para uma atualização

Em dispositivos com 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 extra de patches é criado para que o mapeamento possa ser aplicado diretamente às partições físicas atuais.

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 chamadas de 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 do retrofit, o cliente OTA é atualizado para funcionar com partições dinâmicas. As extensões das partições de origem nunca se estendem entre partições físicas de destino.

Fluxo de atualização 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 do dispositivo 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 da Retrofit for aplicado a um dispositivo que já ativa partições dinâmicas, o cliente OTA aplicará o arquivo super.img dividido diretamente aos dispositivos em bloco. O fluxo de atualização é semelhante a uma atualização de retrofit. Consulte Como fazer o ajuste de uma atualização novamente para ver mais detalhes.

Por exemplo, suponha que:

  • O slot A é o 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 no system_b físico, super_vendor.img no vendor_b físico e super_product.img no 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 de o build de base definir PRODUCT_USE_DYNAMIC_PARTITIONS e PRODUCT_RETROFIT_DYNAMIC_PARTITIONS.

  • Se o build de base não definir as variáveis, essa será uma atualização de retroajuste. O pacote de atualização contém o arquivo super.img dividido 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 o super.img dividido e desativa a etapa pós-instalação para a atualização de retrofitting.
    • 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 estiver 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, você pode exigir uma etapa pós-instalação para verificar a configuração do dispositivo. 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.