OTA para dispositivos não A/B com 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 não A/B.

Para dispositivos não A/B, a atualização OTA para partições dinâmicas é aplicada usando o updater dentro do pacote de atualização.

Atualizar dispositivos de inicialização

Esta seção se aplica a dispositivos que não são A/B que são iniciados com suporte a partições dinâmicas. Esses dispositivos são atualizados do Android 10 para versões mais recentes.

Gerar pacotes de atualização

Os pacotes de atualização OTA são gerados pelo script ota_from_target_files, localizado em build/make/tools/releasetools. Por padrão, o script gera um pacote que atualiza as partições system e vendor. Se houver outras partições dinâmicas, como product, product_services ou odm, as atualizações delas precisam ser geradas no código específico do dispositivo.

Para gerar atualizações, no módulo estendido do Python, implemente FullOTA_GetBlockDifferences() e IncrementalOTA_GetBlockDifferences(). Essas duas funções retornam uma lista de objetos BlockDifference, cada uma descrevendo o patch de atualização que seria aplicado em uma partição. As partições retornadas por essas duas funções não podem ser modificadas manualmente ou verificadas em outro lugar, por exemplo, em *_InstallBegin() ou *_InstallEnd().

Exemplo de uma geração de atualização:

# device/yoyodyne/tardis/releasetools.py

import os
from common import BlockDifference, EmptyImage, GetUserImage

# The joined list of user image partitions of source and target builds.
# - Items should be added to the list if new dynamic partitions are added.
# - Items should not be removed from the list even if dynamic partitions are
#   deleted. When generating an incremental OTA package, this script needs to
#   know that an image is present in source build but not in target build.
USERIMAGE_PARTITIONS = [
    "product",
    "odm",
]

def GetUserImages(input_tmp, input_zip):
  return {partition: GetUserImage(partition, input_tmp, input_zip)
          for partition in USERIMAGE_PARTITIONS
          if os.path.exists(os.path.join(input_tmp,
                                         "IMAGES", partition + ".img"))}

def FullOTA_GetBlockDifferences(info):
  images = GetUserImages(info.input_tmp, info.input_zip)
  return [BlockDifference(partition, image)
          for partition, image in images.items()]

def IncrementalOTA_GetBlockDifferences(info):
  source_images = GetUserImages(info.source_tmp, info.source_zip)
  target_images = GetUserImages(info.target_tmp, info.target_zip)

  # Use EmptyImage() as a placeholder for partitions that will be deleted.
  for partition in source_images:
    target_images.setdefault(partition, EmptyImage())

  # Use source_images.get() because new partitions are not in source_images.
  return [BlockDifference(partition, target_image, source_images.get(partition))
          for partition, target_image in target_images.items()]

Atualizar fluxo

Nos bastidores, as seguintes funções são adicionadas ao script edify:

  • unmap_partition(name)
    • Desmapeia a partição se ela estiver mapeada. Caso contrário, nada será feito.
    • Retorna a string t em caso de sucesso ou uma string vazia em caso de falha.
  • map_partition(name)
    • Mapeie a partição, se ela ainda não estiver mapeada.
    • Retorna o caminho absoluto do dispositivo de bloco mapeado em caso de sucesso ou uma string vazia em caso de falha.
  • update_dynamic_partitions(op_list)
    • Aplique a lista de operações especificada nos metadados de partição dinâmica, desmapeando as partições, se necessário.
    • Retorne t em caso de sucesso ou uma string vazia em caso de falha.

O argumento op_list para update_dynamic_partitions aponta para um arquivo no pacote de atualização. Cada linha no arquivo especifica uma operação. Se qualquer operação falhar, update_dynamic_partitions retornará imediatamente uma string vazia. As operações são:

  • resize partition-name size
    • Desfaça o mapeamento da partição e redimensione-a para size.
  • remove partition_name
    • Desmapeie a partição e remova-a.
  • add partition-name group-name
    • Adicione uma nova partição ao grupo especificado.
    • Aborta se o grupo não existir ou se a partição já existir.
  • move partition-name group-name
    • Mova a partição para o grupo especificado.
    • Cancelar se o grupo ou a partição não existirem.
  • add_group group-name maximum-size
    • Adicione um grupo com o nome e o tamanho máximo fornecidos.
    • Cancelar se o grupo já existir.
    • Um maximum_size de 0 significa que não há limites de tamanho nas partições do grupo. Outros testes são necessários para garantir que as partições no grupo não excedam o espaço disponível no dispositivo.
  • resize_group group-name maximum-size
    • Redimensione o grupo para o tamanho máximo especificado.
    • Cancelar se o grupo não existir.
    • Um maximum_size de 0 significa que não há limites de tamanho nas partições do grupo. Outros testes são necessários para garantir que as partições no grupo não excedam o espaço disponível no dispositivo.
  • remove_group group-name
    • Remover um grupo.
    • Aborta se houver partições no grupo.
  • remove_all_groups
    • Cancelar o mapeamento de todas as partições do mapeador do dispositivo.
    • Remova todas as partições e grupos.

OTA incremental

As atualizações OTA incrementais usam a seguinte lógica:

  1. Reduzir partições/excluir partições/mover partições para fora do grupo (para que haja espaço suficiente para reduzir grupos)
  2. Reduzir grupos (para que haja espaço suficiente para aumentar os grupos)
  3. Aumente os grupos (para que tenhamos espaço suficiente para expandir/adicionar partições)
  4. Ampliar/adicionar/mover partições para um novo grupo

Em detalhes, update-script é gerado com esta lógica:

for each shrinking partition:
    block_image_update(map_partition(name), …)

update_dynamic_partitions(op_list)

for each growing / adding partition:
    block_image_update(map_partition(name), …)

O arquivo op_list para update_dynamic_partitions é gerado com esta lógica:

for each deleting partition:
    remove
for each partition that changes groups:
    move to "default"
for each shrinking partition:
    resize
for each shrinking / removing group:
    resize_group / remove_group
for each growing / adding group:
    resize_group / add_group
for each adding partition:
    add
for each growing / adding partition:
    resize
for each partition that changes groups:
    move to target group

OTA completo

As atualizações OTA completas usam a seguinte lógica:

  1. Excluir todos os grupos e partições
  2. Adicionar grupos
  3. Adicionar partições

Em detalhes, update-script é gerado com esta lógica:

update_dynamic_partitions(op_list)

for each adding partition:
    block_image_update(map_partition(name), …)

O arquivo op_list para update_dynamic_partitions é gerado com esta lógica:

remove_all_groups
for each adding group:
    add_group
for each adding partition:
    add
for each adding partition:
    resize