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 de espaço do usuário que pode criar, redimensionar e destruir partições durante atualizações over-the-air (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 iniciados com suporte a partições dinâmicas; esses dispositivos são atualizados do Android 10 para versões superiores.

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 do system e vendor . Se houver partições dinâmicas adicionais, como product , product_services ou odm , suas atualizações deverã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 devem ser modificadas manualmente ou 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()]

Fluxo de atualização

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

  • unmap_partition(name)
    • Remova o mapeamento da partição se estiver mapeada, caso contrário não faça nada.
    • Retorna 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 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 fornecida nos metadados da partição dinâmica, desmapeando as partições, se necessário.
    • Retorna 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 do arquivo especifica uma operação. Se alguma operação falhar, update_dynamic_partitions retornará imediatamente uma string vazia. As operações são:

  • resize partition-name size
    • Remova o mapeamento da partição e redimensione-a para size .
  • remove partition_name
    • Remova o mapeamento da partição e remova-a.
  • add partition-name group-name
    • Adicione 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.
    • Aborte se o grupo não existir ou a partição não existir.
  • add_group group-name maximum-size
    • Adicione um grupo com o nome fornecido e tamanho máximo.
    • Aborte se o grupo já existir.
    • Um maximum_size igual a 0 significa que não há limites de tamanho nas partições do grupo. São necessários testes adicionais para garantir que as partições do 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 fornecido.
    • Aborte se o grupo não existir.
    • Um maximum_size igual a 0 significa que não há limites de tamanho nas partições do grupo. São necessários testes adicionais para garantir que as partições do grupo não excedam o espaço disponível no dispositivo.
  • remove_group group-name
    • Remover um grupo.
    • Aborte se houver partições no grupo.
  • remove_all_groups
    • Remova o mapeamento de todas as partições do mapeador de dispositivos.
    • Remova todas as partições e grupos.

OTA incremental

As atualizações incrementais OTA 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. Diminuir grupos (para que haja espaço suficiente para aumentar os grupos)
  3. Aumentar grupos (para que tenhamos espaço suficiente para aumentar/adicionar partições)
  4. Aumentar partições/adicionar partições/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 completas do OTA usam a seguinte lógica:

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