동적 파티션을 포함하는 비 A/B 기기의 OTA

Android 10에서는 무선(OTA) 업데이트 중에 파티션을 만들고 크기를 조절하고 제거할 수 있는 사용자 공간 파티션 나누기 시스템인 동적 파티션을 지원합니다.

이 페이지에서는 비 A/B 기기를 업데이트하는 동안 OTA 클라이언트에서 동적 파티션의 크기를 조절하는 방법을 설명합니다.

비 A/B 기기의 경우 동적 파티션의 OTA 업데이트는 업데이트 패키지 내의 updater를 사용하여 적용됩니다.

출시 기기 업데이트

이 섹션은 동적 파티션이 지원되는 상태로 출시되는 비 A/B 기기에 적용됩니다. 이러한 기기는 Android 10 이상 버전에서 업그레이드됩니다.

업데이트 패키지 생성

OTA 업데이트 패키지는 build/make/tools/releasetools 아래에 있는 ota_from_target_files 스크립트로 생성됩니다. 기본적으로 스크립트는 systemvendor 파티션을 업데이트하는 패키지를 생성합니다. 추가 동적 파티션(예: productproduct_services, odm)이 있으면 이러한 파티션의 업데이트를 기기별 코드로 생성해야 합니다.

업데이트를 생성하려면 확장된 Python 모듈에서 FullOTA_GetBlockDifferences()IncrementalOTA_GetBlockDifferences()를 구현합니다. 이 두 함수는 BlockDifference 객체 목록을 반환하며 각 객체는 파티션에 적용될 업데이트 패치를 설명합니다. 이러한 두 함수로 반환되는 파티션은 수동으로 수정하거나 *_InstallBegin() 또는 *_InstallEnd()와 같은 다른 위치에서 확인하면 안 됩니다.

업데이트 생성 예시:

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

업데이트 흐름

배경에서는 다음 함수가 edify 스크립트에 추가됩니다.

  • unmap_partition(name)
    • 매핑된 경우 파티션을 매핑 해제하고 매핑되지 않은 경우 아무 작업도 하지 않습니다.
    • 성공 시 문자열 t를, 실패 시 빈 문자열을 반환합니다.
  • map_partition(name)
    • 이미 매핑되지 않은 경우 파티션을 매핑합니다.
    • 성공 시 매핑된 블록 기기의 절대 경로를, 실패 시 빈 문자열을 반환합니다.
  • update_dynamic_partitions(op_list)
    • 동적 파티션 메타데이터에 주어진 작업 목록을 적용하며, 필요한 경우 파티션을 매핑 해제합니다.
    • 성공 시 t를, 실패 시 빈 문자열을 반환합니다.

update_dynamic_partitionsop_list 인수는 업데이트 패키지의 파일을 가리킵니다. 파일의 각 행은 작업을 지정합니다. 작업이 실패하면 update_dynamic_partitions는 즉시 빈 문자열을 반환합니다. 작업은 다음과 같습니다.

  • resize partition-name size
    • 파티션을 매핑 해제한 다음 size로 크기를 조절합니다.
  • remove partition_name
    • 파티션을 매핑 해제한 다음 제거합니다.
  • add partition-name group-name
    • 새 파티션을 지정된 그룹에 추가합니다.
    • 그룹이 존재하지 않거나 파티션이 이미 존재하는 경우 취소합니다.
  • move partition-name group-name
    • 파티션을 지정된 그룹으로 이동합니다.
    • 그룹이 존재하지 않거나 파티션이 존재하지 않는 경우 취소합니다.
  • add_group group-name maximum-size
    • 이름과 최대 크기가 주어진 그룹을 추가합니다.
    • 그룹이 이미 존재하는 경우 취소합니다.
    • maximum_size가 0이면 그룹의 파티션에 크기 제한이 없음을 의미합니다. 추가 테스트는 그룹의 파티션이 기기의 가용한 공간을 초과하지 않도록 해야 하는 경우에 필요합니다.
  • resize_group group-name maximum-size
    • 그룹 크기를 주어진 최대 크기로 조절합니다.
    • 그룹이 존재하지 않는 경우 취소합니다.
    • maximum_size가 0이면 그룹의 파티션에 크기 제한이 없음을 의미합니다. 추가 테스트는 그룹의 파티션이 기기의 가용한 공간을 초과하지 않도록 해야 하는 경우에 필요합니다.
  • remove_group group-name
    • 그룹을 삭제합니다.
    • 그룹에 파티션이 있는 경우 취소합니다.
  • remove_all_groups
    • 기기 매퍼에서 모든 파티션을 매핑 해제합니다.
    • 모든 파티션과 그룹을 제거합니다.

증분 OTA

증분 OTA 업데이트는 다음 논리를 사용합니다.

  1. 그룹 축소를 위한 공간이 충분하도록 파티션을 축소 및 삭제하고 그룹 외부로 이동합니다.
  2. 그룹 확장을 위한 공간이 충분하도록 그룹을 축소합니다.
  3. 파티션 확장/추가를 위한 공간이 충분하도록 그룹을 확장합니다.
  4. 파티션을 확장 및 추가하고 새 그룹으로 이동합니다.

구체적으로 update-script는 다음 논리로 생성됩니다.

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

update_dynamic_partitionsop_list 파일은 다음 논리로 생성됩니다.

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

전체 OTA 업데이트는 다음 논리를 사용합니다.

  1. 모든 기존 그룹 및 파티션 삭제
  2. 그룹 추가
  3. 파티션 추가

구체적으로 update-script는 다음 논리로 생성됩니다.

update_dynamic_partitions(op_list)

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

update_dynamic_partitionsop_list 파일은 다음 논리로 생성됩니다.

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