このページでは、ビルド間の不要なファイルの変更を減らすために AOSP に追加された変更について説明します。独自のビルドシステムを管理しているデバイスの実装者は、OTA アップデートのサイズを減らすためのガイドとしてこの情報を使用できます。
Android OTA アップデートには、コードの変更とは関係なく変更されたファイルが含まれることがあります。実は、これらはビルドシステムのアーティファクトで、同じコードが異なる時間に異なるディレクトリやマシン上でビルドされ、変更ファイルが多数生成された場合に発生します。このような余計なファイルがあると OTA パッチのサイズが増大し、変更されたコードを特定するのが困難になります。
OTA の内容の透明性を高めるため、AOSP には、OTA パッチのサイズを減らすように設計されたビルドシステムの変更が含まれています。ビルド間の不要なファイルの変更が排除され、パッチに関連したファイルのみが OTA アップデートに含まれます。AOSP には、共通のビルド関連ファイルの変更を抽出し、ビルドファイルの差分を作成するビルド差分ツールとブロックの割り当ての一貫性を維持するブロック マッピング ツールもあります。
ビルドシステムでは、方法によっては不必要に大きなパッチが作成されることがあります。これを軽減するため、Android 8.0 以降では、各ファイルの差分のパッチサイズを減らす新機能を実装しました。OTA アップデート パッケージのサイズを縮小した改善点は次のとおりです。
- Brotli(汎用の可逆圧縮アルゴリズム)の使用。非 A/B デバイスのアップデートでフルイメージに使用しています。圧縮を最適化するために Brotli をカスタマイズできます。
system.img
などのファイル システム内の複数のブロックで構成される大規模なアップデートでは、デバイス メーカーやパートナーは独自の圧縮アルゴリズムを追加し、同じアップデートの異なるブロックにそれぞれ異なる圧縮アルゴリズムを使用できます。 - Puffin による再圧縮の使用。これはデフレート ストリームのパッチツールで、決定性アルゴリズムを使用し、A/B OTA アップデートの生成において圧縮と差分関数を処理します。
- デルタ生成ツールの使用方法(パッチの圧縮に
bsdiff
ライブラリを使用する方法など)の変更。Android 9 以降では、bsdiff
ツールは、パッチに最適な圧縮結果をもたらす圧縮アルゴリズムを選択します。 update_engine
の改善により、A/B デバイスのアップデートにパッチを適用したときのメモリ使用量が減少しました。- ブロックベースの OTA アップデートで、大きな zip ファイルの分割を改善。
imgdiff
のモードの 1 つは、サイズが大きすぎる APK ファイルをエントリ名に基づいて分割します。この分割方法のほうが、ファイルを線形分割し、bsdiff
ツールを使用して圧縮する方法よりパッチが小さくなります。
以降のセクションでは、OTA アップデートのサイズに影響するさまざまな問題、ソリューション、AOSP での実装例について説明します。
ファイルの順序
問題: ディレクトリ内のファイルの一覧表示を求めたときに、ファイルシステムはファイルの表示順序を保証しません(ただし、同じチェックアウトを利用すれば通常は同じです)。ls
などのツールはデフォルトで結果を並べ替えますが、find
や make
などのコマンドで使用するワイルドカード機能は並べ替えをしません。これらのツールを使用する前に、出力を並べ替える必要があります。
解決策: find
や make
などのツールでワイルドカード機能を使用する場合は、ツールを使用する前に出力を並べ替えます。Android.mk
ファイル内で $(wildcard)
または $(shell
find)
を使用する場合も同じように並べ替えてください。入力を並べ替えてくれるツールもあるので(Java など)、並べ替えが行われていないかどうかを先に確認してから並べ替えを行いましょう。
例: コア ビルドシステムで、組み込みマクロ all-*-files-under
を使用して多数のインスタンスが修正されました。これには all-cpp-files-under
も含まれていました(他の makefile に定義が分散していたため)。詳細については、以下を参照してください。
- https://android.googlesource.com/platform/build/+/4d66adfd0e6d599d8502007e4ea9aaf82e95569f
- https://android.googlesource.com/platform/build/+/379f9f9cec4fe1c66b6d60a6c19fecb81b9eb410
- https://android.googlesource.com/platform/build/+/7c3e3f8314eec2c053012dd97d2ae649ebeb5653
- https://android.googlesource.com/platform/build/+/5c64b4e81c1331cab56d8a8c201f26bb263b630c
ビルド ディレクトリ
問題: ビルドが行われるディレクトリを変更すると、バイナリが異なってしまうことがあります。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__
これらのマクロは、ビルドごとに異なる出力を生成するため、使用しないでください。マクロを排除する方法は次のとおりです。
- マクロを削除します。例については、https://android.googlesource.com/platform/system/core/+/30622bbb209db187f6851e4cf0cdaa147c2fca9f をご覧ください。
- 実行中のバイナリを一意に識別するには、ELF ヘッダーからビルド ID を読み取ります。
- OS がいつビルドされたかを知るには、
ro.build.date
を読みます(この方法はすべてのビルドで使用できますが、増分ビルドではこの日付が更新されない可能性があるので使用はおすすめしません)。例については、https://android.googlesource.com/platform/external/libchrome/+/8b7977eccc94f6b3a3896cd13b4aeacbfa1e0f84 をご覧ください。
アーカイブ内の埋め込みタイムスタンプ(zip、jar)
Android 7.0 では、zip アーカイブ内の埋め込みタイムスタンプの問題を解決するため、zip
コマンドが使われるすべての場所に -X
を追加しました。この方法で zip ファイルからビルダーの UID / GID と拡張 Unix タイムスタンプが削除されました。
新しいツール ziptime
は /platform/build/+/main/tools/ziptime/
にあり、zip ヘッダー内の通常のタイムスタンプをリセットします。詳しくは、README ファイルをご覧ください。
signapk
ツールは APK ファイルのタイムスタンプを設定しますが、サーバーのタイムゾーンによってはタイムスタンプが異なる可能性があります。詳細については、CL の https://android.googlesource.com/platform/build/+/6c41036bcf35fe39162b50d27533f0f3bfab3028 をご覧ください。
バージョン文字列
問題: APK バージョンの文字列には、ハードコーディングされたバージョンに BUILD_NUMBER
が追加されていることがあります。そのため、APK 内で他に何も変更されていない場合でも違う APK になってしまいます。
解決策: APK バージョン文字列からビルド番号を削除します。
例:
- https://android.googlesource.com/platform/packages/apps/Camera2/+/5e0f4cf699a4c7c95e2c38ae3babe6f20c258d27
- https://android.googlesource.com/platform/build/+/d75d893da8f97a5c7781142aaa7a16cf1dbb669c
デバイスでの信頼性計算を有効にする
デバイスで dm-verity が有効になっている場合、OTA ツールは信頼性構成を自動的に選択し、デバイスでの信頼性計算を有効にします。それにより、信頼性ブロックを OTA パッケージに未加工のバイト列として保存せずに、Android デバイスで計算できます。信頼性ブロックは、2 GB のパーティションに対して約 16 MB 使用することがあります。
ただし、デバイスでの信頼性の計算には時間がかかる場合があります。特に、前方誤り訂正コードに時間がかかることがあります。Pixel デバイスでは 10 分程度かかることが多くなります。ローエンドのデバイスではさらに時間がかかることがあります。dm-verity を有効にしたままデバイスでの信頼性計算を無効にするには、OTA アップデートの生成時に --disable_fec_computation
を ota_from_target_files
ツールに渡します。このフラグにより、OTA アップデート中にデバイスでの信頼性計算が無効になります。それによって OTA のインストール時間が短縮されますが、OTA パッケージのサイズは増えます。デバイスで dm-verity が有効になっていない場合は、このフラグを渡しても効果はありません。
一貫したビルドツール
問題: インストールするファイルの生成ツールには一貫性がなくてはなりません。所定の入力に対しては常に同じ出力を生成する必要があります。
解決策 / 例: 次のビルドツールの変更が必要でした。
- NOTICE ファイル クリエイター。再現可能な NOTICE コレクションが作成されるように NOTICE ファイル クリエイターが変更されました。CL の https://android.googlesource.com/platform/build/+/8ae4984c2c8009e7a08e2a76b1762c2837ad4f64 をご覧ください。
- Java Android Compiler Kit(Jack)。Jack のツールチェーンでは、生成されたコンストラクタの順序の不定期な変更に対処する必要がありました。コンストラクタ用に確定的な方法で生成されるアクセサがツールチェーンに追加されました。CL の https://android.googlesource.com/toolchain/jack/+/056a5425b3ef57935206c19ecb198a89221ca64b をご覧ください。
- ART AOT コンパイラ(dex2oat)。イメージ生成に決定論を強制するオプションを ART コンパイラ バイナリに追加しました。CL の https://android.googlesource.com/platform/art/+/ace0dc1dd5480ad458e622085e51583653853fb9 をご覧ください。
- libpac.so ファイル(V8)。ビルドごとに V8 スナップショットが変わるため、ビルドごとに異なる
/system/lib/libpac.so
ファイルが作成されます。この解決策は、スナップショットを削除することでした。CL の https://android.googlesource.com/platform/external/v8/+/e537f38c36600fd0f3026adba6b3f4cbcee1fb29 をご覧ください。 - アプリの dexopt 処理済み(.odex)ファイル。dexopt 処理済み(.odex)ファイルには、64 ビットシステムに初期化されていないパディングが含まれていました。この問題は修正済みです。CL の https://android.googlesource.com/platform/art/+/34ed3afc41820c72a3c0ab9770be66b6668aa029 をご覧ください。
ビルド差分ツールを使用する
ビルドに関連したファイルの変更を排除できないケースに対応するために、AOSP には 2 つのファイル パッケージを比較するビルド差分ツール target_files_diff.py
が含まれています。このツールは、2 つのビルド間の再帰的な差分を実行しますが、次のようなビルド関連ファイルによくある変更は差分の対象から除外されています。
- ビルド出力に当然生じる変更(ビルド番号の変更など)
- 現行ビルドシステムでの既知の問題による変更。
ビルド差分ツールを使用するには、次のコマンドを実行します。
target_files_diff.py dir1 dir2
dir1
と dir2
はビルドごとに抽出されたターゲット ファイルを格納するベース ディレクトリです。
ブロック割り当ての一貫性を維持する
あるファイルの内容が 2 つのビルド間で同じであっても、そのデータを格納する実際のブロックが変更されている可能性があります。そのため、アップデータは不要な I/O を実行して OTA アップデートのためにブロックを移動しなければなりません。
仮想 A/B OTA アップデートでは、書き込み時のスナップショットのスナップショットを保存するのに必要なストレージ スペースを大幅に増やすことができます。非 A/B OTA アップデートでは、OTA アップデートのためにブロックを移動すると、ブロックの移動による I/O が増えているため、更新時間が長くなります。
この問題に対処するため、Android 7.0 では make_ext4fs
ツールを拡張し、ブロック割り当てをビルド間で一貫するようにしました。make_ext4fs
ツールにオプションの -d
base_fs
フラグを指定すると、ext4
イメージの生成時にこのツールは同じブロックへのファイル割り当てを試みます。以前のビルドのターゲット ファイルの zip ファイルからブロック マッピング ファイル(base_fs
マップファイルなど)を抽出できます。各 ext4
パーティションには、IMAGES
ディレクトリに .map
ファイルがあります(たとえば、IMAGES/system.map
は system
パーティションに対応します)。それらの 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 を適用するのに必要な保存容量が大幅に削減されます。
アプリの更新の回避
ビルド差分を最小限に抑えるほか、アプリストアで更新されるアプリのアップデートを OTA アップデートから除外することで、OTA アップデートのサイズを縮小できます。APK にデバイスの各種パーティションのかなりの部分が含まれることがよくあります。アプリストアで更新されるアプリの最新バージョンを OTA アップデートに含めると、OTA パッケージのサイズに大きな影響を与え、ユーザーの利点も制限される可能性があります。ユーザーが OTA パッケージを受け取るころには、アップデート済みのアプリか、より新しいバージョンをアプリストアから直接受け取れる可能性もあります。