Aktualizacja OTA na urządzeniach innych niż A/B z partycjami dynamicznymi

Android 10 obsługuje partycje dynamiczne, czyli system partycjonowania przestrzeni użytkownika, który może tworzyć, zmieniać rozmiar i usuwać partycje podczas aktualizacji bezprzewodowych (OTA).

Na tej stronie opisano, jak klienci OTA zmieniają rozmiar partycji dynamicznych podczas aktualizacji na urządzeniach innych niż A/B.

W przypadku urządzeń innych niż A/B aktualizacja OTA dla partycji dynamicznych jest stosowana za pomocą updater w pakiecie aktualizacji.

Aktualizowanie urządzeń uruchamiających

Ta sekcja dotyczy urządzeń innych niż A/B, które są uruchamiane z obsługą partycji dynamicznych. Te urządzenia są aktualizowane z Androida 10 do nowszych wersji.

Generowanie pakietów aktualizacji

Pakiety aktualizacji OTA są generowane przez skrypt ota_from_target_files, który znajduje się w sekcji build/make/tools/releasetools. Domyślnie skrypt generuje pakiet, który aktualizuje partycje system i vendor. Jeśli istnieją dodatkowe partycje dynamiczne, takie jak product, product_services lub odm, ich aktualizacje muszą być generowane w kodzie dla konkretnego urządzenia.

Aby wygenerować aktualizacje, w rozszerzonym module Pythona zastosuj funkcje FullOTA_GetBlockDifferences()IncrementalOTA_GetBlockDifferences(). Te dwie funkcje zwracają listę obiektów BlockDifference, z których każdy opisuje poprawkę do aktualizacji, która zostanie zastosowana na partycji. Partycji zwracanych przez te dwie funkcje nie należy modyfikować ręcznie ani weryfikować w innym miejscu, na przykład w funkcji *_InstallBegin() lub *_InstallEnd().

Przykład generowania aktualizacji:

# 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()]

Aktualizowanie przepływu

W tle do skryptu edify dodawane są te funkcje:

  • unmap_partition(name)
    • Odmapuj partycję, jeśli jest mapowana, w przeciwnym razie nic nie rób.
    • W przypadku powodzenia zwraca ciąg znaków t, a w przypadku błędu – pusty ciąg znaków.
  • map_partition(name)
    • Zmapuj partycję, jeśli nie została jeszcze zmapowana.
    • W przypadku powodzenia zwraca ścieżkę bezwzględną mapowanego urządzenia blokującego, a w przypadku błędu – pusty ciąg znaków.
  • update_dynamic_partitions(op_list)
    • Zastosuj podawaną listę operacji do metadanych dynamicznych partycji, w razie potrzeby odłączając partycje.
    • W przypadku powodzenia zwraca wartość t, a w przypadku błędu – pusty ciąg znaków.

Argument op_list w funkcji update_dynamic_partitions wskazuje plik w pakiecie aktualizacji. Każdy wiersz w pliku określa operację. Jeśli jakakolwiek operacja zakończy się niepowodzeniem, funkcja update_dynamic_partitions natychmiast zwróci pusty ciąg. Operacje:

  • resize partition-name size
    • Odmapuj partycję, a następnie zmień jej rozmiar na size.
  • remove partition_name
    • Odmapuj partycję, a następnie ją usuń.
  • add partition-name group-name
    • Dodaj nową partycję do określonej grupy.
    • Anuluj, jeśli grupa nie istnieje lub partycja już istnieje.
  • move partition-name group-name
    • Przenoszenie partycji do określonej grupy.
    • Anuluj, jeśli grupa lub partycja nie istnieje.
  • add_group group-name maximum-size
    • Dodaj grupę z podaną nazwą i maksymalną wielkością.
    • Anuluj, jeśli grupa już istnieje.
    • Wartość maximum_size 0 oznacza, że nie ma limitów rozmiaru dla partycji w grupie. Konieczne są dodatkowe testy, aby mieć pewność, że partycje w grupie nie przekraczają dostępnej przestrzeni na urządzeniu.
  • resize_group group-name maximum-size
    • Zmień rozmiar grupy do podanego maksymalnego rozmiaru.
    • Anuluj, jeśli grupa nie istnieje.
    • Wartość maximum_size 0 oznacza, że nie ma limitów rozmiaru dla partycji w grupie. Konieczne są dodatkowe testy, aby mieć pewność, że partycje w grupie nie przekraczają dostępnej przestrzeni na urządzeniu.
  • remove_group group-name
    • usunąć grupę.
    • Anuluj, jeśli w grupie są partycje.
  • remove_all_groups
    • Odmapuj wszystkie partycje w mapierze urządzenia.
    • Usuń wszystkie partycje i grupy.

Przyrostowa OTA

Incremental OTA updates use the following logic:

  1. Zmniejsz partycje, usuń partycje lub przenieś partycje poza grupę (aby było wystarczająco dużo miejsca na zmniejszenie grup).
  2. Zmniejsz grupy (aby było wystarczająco dużo miejsca na ich powiększenie).
  3. zwiększanie grup (aby mieć wystarczająco dużo miejsca na zwiększanie/dodawanie partycji);
  4. zwiększać partycje, dodawać partycje lub przenosić partycje do nowej grupy.

Szczegółowo update-script jest generowany według tej logiki:

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), …)

Plik op_list dla update_dynamic_partitions jest generowany za pomocą tej logiki:

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

Pełna aktualizacja OTA

Pełne aktualizacje OTA korzystają z tej logiki:

  1. usunąć wszystkie istniejące grupy i partycje.
  2. Dodaj grupy
  3. Dodawanie partycji

Szczegółowo update-script jest generowany według tej logiki:

update_dynamic_partitions(op_list)

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

Plik op_list dla update_dynamic_partitions jest generowany za pomocą tej logiki:

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