OTA 크기 줄이기

이 페이지에서는 빌드 간의 불필요한 파일 변경을 줄이기 위해 AOSP에 추가된 변경사항에 대해 설명합니다. 자체 빌드 시스템을 유지관리하는 기기 구현자는 이 정보를 가이드로 사용하여 무선 업데이트(OTA)의 크기를 줄일 수 있습니다.

Android OTA 업데이트에 코드 변경사항과 일치하지 않는 변경된 파일이 포함되는 경우가 있습니다. 이는 빌드 시스템 아티팩트입니다. 이러한 상황은 서로 다른 시점에 서로 다른 디렉터리나 시스템에서 빌드된 동일한 코드가 다수의 변경된 파일을 생성하는 경우에 발생할 수 있습니다. 이러한 과도한 파일은 OTA 패치 크기를 늘리고, 따라서 어떤 코드가 변경되었는지 확인하기가 어려워집니다.

OTA의 콘텐츠를 더욱 투명하게 만들기 위해 AOSP에는 OTA 패치의 크기를 줄이도록 설계된 빌드 시스템 변경사항이 포함되어 있습니다. 빌드 간의 불필요한 파일 변경사항은 제거되었으며, 패치 관련 파일만 OTA 업데이트에 포함됩니다. AOSP에는 더욱 깔끔한 빌드 파일 diff를 제공하기 위해 일반적인 빌드 관련 파일 변경사항을 필터링하는 빌드 diff 도구와 블록 할당을 일정하게 유지해주는 블록 매핑 도구도 포함되어 있습니다.

빌드 시스템은 여러 가지 방법으로 불필요하게 큰 패치를 만들 수 있습니다. 이 문제를 해결하기 위해 Android 8.0 이상에서는 각 파일 diff의 패치 크기를 줄이기 위한 새로운 기능이 구현되었습니다. OTA 업데이트 패키지 크기를 줄이는 개선 사항은 다음과 같습니다.

  • A/B 외 기기 업데이트에서 전체 이미지를 위한 범용 무손실 압축 알고리즘인 Brotli를 사용합니다. Brotli를 맞춤설정하여 압축을 최적화할 수 있습니다. 파일 시스템에서 둘 이상의 블록(예: system.img)으로 구성된 대규모 업데이트의 경우 기기 제조업체 또는 파트너가 자체 압축 알고리즘을 추가하거나, 동일한 업데이트의 서로 다른 블록에 각각 다른 압축 알고리즘을 사용할 수 있습니다.
  • deflate 스트림의 확정적 패치 도구로서 A/B OTA 업데이트 세대의 압축 및 diff 함수를 처리하는 Puffin 재압축을 사용합니다.
  • 델타 생성 도구를 사용하는 방식(예: bsdiff 라이브러리가 패치 압축에 사용되는 방식)이 변경되었습니다. Android 9 이상에서 bsdiff 도구는 패치에 대한 최상의 압축 결과를 제공하는 압축 알고리즘을 선택합니다.
  • update_engine이 개선되어 A/B 기기 업데이트에 패치가 적용될 때 메모리를 덜 소비합니다.
  • 블록 기반 OTA 업데이트를 위해 대용량 zip 파일을 분할하는 기능이 개선되었습니다. imgdiff의 모드는 항목 이름에 따라 크기가 큰 APK 파일을 분할합니다. 그러면 파일을 선형으로 분할하고 bsdiff 도구를 사용하여 압축하는 것보다 더 작은 패치가 생성됩니다.

다음 섹션에서는 OTA 업데이트 크기에 영향을 미치는 다양한 문제, 이러한 문제의 해결 방법, AOSP의 구현 예시에 관해 설명합니다.

파일 순서

문제: 파일 시스템은 디렉터리의 파일 목록을 요청받았을 때 파일 순서를 보장하지 않지만, 동일한 결제의 경우 파일 순서가 똑같을 때가 많습니다. ls와 같은 도구는 기본적으로 결과를 정렬하지만 findmake와 같은 명령어가 사용하는 와일드 카드 함수는 결과를 정렬하지 않습니다. 이러한 도구를 사용하려면 먼저 출력부터 정렬해야 합니다.

해결 방법: 와일드 카드 함수에 findmake와 같은 도구를 사용하는 경우 사용하기 전에 이러한 명령어의 출력을 정렬합니다. Android.mk 파일에서 $(wildcard) 또는 $(shell find)를 사용하는 경우에도 정렬하세요. Java를 비롯한 일부 도구는 입력을 정렬하므로 파일을 정렬하기 전에 사용 중인 도구가 입력을 정렬하지 않았는지 확인하세요.

예시: 많은 인스턴스가 기본 제공 시스템을 사용하여 핵심 빌드 시스템에서 수정되었습니다.all-*-files-under 매크로에 포함된 all-cpp-files-under (여러 정의가 makefile에 배포되었기 때문) 자세한 내용은 다음을 참고하세요.

빌드 디렉터리

문제: 항목이 빌드된 디렉터리를 변경할 경우 바이너리가 달라질 수 있습니다. Android 빌드의 경로는 대부분 상대 경로이므로 C/C++의 __FILE__이 문제가 되지 않습니다. 하지만 디버그 기호는 전체 경로 이름을 기본으로 인코딩하며, 미리 삭제된 바이너리를 해싱하면 .note.gnu.build-id가 생성됩니다. 따라서 디버그 기호가 변경되면 이 또한 변경됩니다.

해결 방법: 이제 AOSP에서 디버그 경로를 상대 경로로 지정합니다. 자세한 내용은 CL(https://android.googlesource.com/platform/build/+/6a66a887baadc9eb3d0d60e26f748b8453e27a02)을 참고하세요.

타임스탬프

문제: 빌드 출력의 타임스탬프가 불필요한 파일 변경을 야기합니다. 이 문제는 다음과 같은 위치에서 발생할 가능성이 높습니다.

  • C 또는 C++ 코드의 __DATE__/__TIME__/__TIMESTAMP__ 매크로
  • zip 기반 아카이브에 삽입된 타임스탬프

해결 방법/예시: 빌드 출력에서 타임스탬프를 제거하려면 아래의 C/C++의 __DATE__/__TIME__/__TIMESTAMP__의 안내를 따르세요.삽입된 타임스탬프는 아카이브에 있습니다.

C/C++의 __DATE__/__TIME__/__TIMESTAMP__

이러한 매크로는 항상 다른 빌드의 다른 출력을 생성하기 때문에 사용하지 마세요. 다음은 이러한 매크로를 제거할 수 있는 몇 가지 방법입니다.

아카이브(zip, jar)에 삽입된 타임스탬프

Android 7.0에서는 모든 zip 명령어 사용에 -X를 추가하여 zip 파일에 삽입된 타임스탬프 관련 문제가 수정되었습니다. 따라서 빌더의 UID/GID 및 확장된 Unix 타임스탬프가 zip 파일에서 삭제되었습니다.

새로운 도구인 ziptime(/platform/build/+/main/tools/ziptime/에 위치)은 zip 헤더의 일반 타임스탬프를 재설정합니다. 자세한 내용은 README 파일을 참고하세요.

signapk 도구는 서버 시간대에 따라 다를 수 있는 APK 파일의 타임스탬프를 설정합니다. 자세한 내용은 CL(https://android.googlesource.com/platform/build/+/6c41036bcf35fe39162b50d27533f0f3bfab3028)을 참고하세요.

버전 문자열

문제: APK 버전 문자열은 하드코딩된 버전에 BUILD_NUMBER가 붙을 때가 많았습니다. APK의 다른 부분이 변경되지 않은 경우에도 APK가 달라집니다.

해결 방법: APK 버전 문자열에서 빌드 번호를 제거합니다.

예:

기기 내 verity 계산 사용

dm-verity가 기기에서 사용 설정되면 OTA 도구는 자동으로 verity 구성을 선택하고 기기 내 verity 계산을 사용 설정합니다. 따라서 verity 블록을 OTA 패키지에 원시 바이트로 저장하는 대신 Android 기기에서 계산할 수 있습니다. verity 블록은 2GB 파티션의 약 16MB를 사용합니다.

그러나 기기 내 verity 계산에 긴 시간이 소요될 수 있습니다. 특히 정방향 오류 수정 코드에서 시간이 오래 걸릴 가능성이 있습니다. Pixel 기기에서는 최대 10분 정도 걸립니다. 저사양 기기에서는 더 오래 걸릴 수 있습니다. 기기 내 verity 계산은 사용 중지하고 dm-verity는 계속 사용하려는 경우 OTA 업데이트를 생성할 때 ota_from_target_files 도구에 --disable_fec_computation을 전달하면 됩니다. 이 플래그는 OTA 업데이트를 하는 동안 기기 내 verity 계산을 사용 중지합니다. OTA 설치 시간은 줄지만 OTA 패키지 크기는 늘어납니다. 기기에 dm-verity가 사용 설정되지 않은 경우 이 플래그를 전달해도 아무 효과가 없습니다.

일관적인 빌드 도구

문제: 설치된 파일을 생성하는 도구가 일관적이어야 합니다(특정 입력이 항상 동일한 출력을 생성해야 함).

해결 방법/예시: 다음과 같은 빌드 도구에 변경사항이 필요했습니다.

빌드 diff 도구 사용

빌드 관련 파일 변경사항을 제거할 수 없는 경우를 위해 AOSP에는 빌드 diff 도구인 target_files_diff.py가 포함되어 있습니다. 이 도구는 두 개의 파일 패키지를 비교할 때 사용됩니다. 이 도구는 두 빌드 간의 반복적 diff를 수행하여 다음과 같은 일반적인 빌드 관련 파일 변경사항을 제외합니다.

  • 빌드 출력에서 예상되는 변경사항(예: 빌드 번호 변경)
  • 현재 빌드 시스템의 알려진 문제로 인한 변경사항

빌드 diff 도구를 사용하려면 다음 명령어를 실행합니다.

target_files_diff.py dir1 dir2

dir1dir2는 각 빌드에 대해 추출된 대상 파일이 포함되는 기본 디렉터리입니다.

블록 할당의 일관성 유지

파일의 경우 콘텐츠가 두 빌드 간에 동일하게 유지되지만 데이터가 보관되는 실질적인 블록은 변경되었습니다. 따라서 업데이터는 불필요한 I/O를 수행하여 OTA 업데이트의 블록을 이동해야 합니다.

가상 A/B OTA 업데이트에서 불필요한 I/O는 COW(기록 중 복사) 스냅샷을 저장하는 데 필요한 저장공간을 크게 늘릴 수 있습니다. 비 A/B OTA 업데이트에서는 블록 이동으로 인해 I/O가 더 있으므로 OTA 업데이트의 블록에서 블록을 이동하면 업데이트 시간에 기여합니다.

이 문제를 해결하기 위해 Android 7.0에서는 Google에서 빌드 간 블록 할당을 일관되게 유지하기 위한 make_ext4fs 도구를 확장했습니다. make_ext4fs 도구는 ext4 이미지를 생성할 때 동일한 블록에 파일을 할당하려고 시도하는 선택적 -d base_fs 플래그를 허용합니다. 이전 빌드의 타겟 파일 zip 파일에서 블록 매핑 파일(예: base_fs 맵 파일)을 추출할 수 있습니다. 각 ext4 파티션에는 IMAGES 디렉터리에 .map 파일이 있습니다. 예를 들어 IMAGES/system.mapsystem 파티션에 해당합니다. 이 예처럼 아래 base_fs 파일을 확인하고 PRODUCT_<partition>_BASE_FS_PATH을 통해 지정할 수 있습니다.

  PRODUCT_SYSTEM_BASE_FS_PATH := path/to/base_fs_files/base_system.map
  PRODUCT_SYSTEM_EXT_BASE_FS_PATH := path/to/base_fs_files/base_system_ext.map
  PRODUCT_VENDOR_BASE_FS_PATH := path/to/base_fs_files/base_vendor.map
  PRODUCT_PRODUCT_BASE_FS_PATH := path/to/base_fs_files/base_product.map
  PRODUCT_ODM_BASE_FS_PATH := path/to/base_fs_files/base_odm.map

이는 OTA 패키지의 전체 용량을 줄이는 데 도움이 되지 않지만 I/O 용량을 줄여 OTA 업데이트 성능을 개선합니다. 가상 A/B 업데이트의 경우 OTA를 적용하는 데 필요한 저장공간이 크게 줄어듭니다.

앱 업데이트 방지

빌드 diff를 최소화하는 것 외에도 앱 스토어를 통해 업데이트를 받는 앱의 업데이트를 제외하여 OTA 업데이트 크기를 줄일 수 있습니다. APK는 기기의 다양한 파티션의 상당 부분을 차지하는 경우가 많습니다. 앱 스토어에서 업데이트되는 앱의 최신 버전을 OTA 업데이트에 포함할 경우 OTA 패키지 크기에 대한 상당한 영향을 받을 수 있고 사용자 이점이 거의 없을 수 있습니다. OTA 패키지를 받을 시점이라면 사용자가 이미 업데이트된 앱이나 최신 버전을 앱 스토어에서 직접 받을 수도 있습니다.