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 lançamento

Esta seção se aplica a dispositivos não A/B que são lançados com suporte a partições dinâmicas e que 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 precisarão ser geradas em código específico do dispositivo.

Para gerar atualizações, no módulo Python estendido, implemente FullOTA_GetBlockDifferences() e IncrementalOTA_GetBlockDifferences(). Essas duas funções retornam uma lista de objetos BlockDifference, cada um 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 nem verificadas em outro lugar, por exemplo, em *_InstallBegin() ou *_InstallEnd().

Exemplo de 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)
    • Desvincule a partição se ela estiver vinculada. Caso contrário, não faça nada.
    • Retorne a string t em caso de sucesso ou uma string vazia em caso de falha.
  • map_partition(name)
    • Mapeie a partição, se ainda não tiver sido 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)
    • Aplica a lista de operações especificada aos metadados de partição dinâmica, removendo o mapeamento das 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 alguma operação falhar, update_dynamic_partitions vai retornar imediatamente uma string vazia. As operações são:

  • resize partition-name size
    • Desvincule a partição e redimensione-a para size.
  • remove partition_name
    • Desvincule e remova a partição.
  • add partition-name group-name
    • Adiciona uma nova partição ao grupo especificado.
    • Aborte 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.
    • Interrompa se o grupo ou a partição não existir.
  • add_group group-name maximum-size
    • Adiciona um grupo com o nome e o tamanho máximo especificados.
    • Interrompe se o grupo já existir.
    • Um maximum_size de 0 significa que não há limites de tamanho para partições no grupo. É necessário fazer mais testes 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.
    • Abortar se o grupo não existir.
    • Um maximum_size de 0 significa que não há limites de tamanho para partições no grupo. É necessário fazer mais testes 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.
    • Interrompa se houver partições no grupo.
  • remove_all_groups
    • Desvincule todas as partições do mapeador de dispositivos.
    • Remova todas as partições e grupos.

OTA incremental

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

  1. Reduzir/excluir/mover partições para fora do grupo (para que haja espaço suficiente para reduzir os grupos)
  2. Reduza os grupos para que haja espaço suficiente para o crescimento deles.
  3. Aumentar os grupos para que tenhamos espaço suficiente para aumentar/adicionar partições.
  4. Aumentar/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 completa

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

  1. Excluir todos os grupos e partições atuais
  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