はじめに
AOSP のビルドが完了したあとに、取り急ぎ仮想環境で動作確認したいと思いました。
Emulator または Cuttlefish で起動できますが、Cuttlefish のほうが軽快らしいので Cuttlefish にチャレンジしました。
ただ、公式ドキュメントは微妙に情報が足りず、少々難儀しました。
- Get Started ページは ci.android.com のビルド成果物を前提にしています。今回はローカルでビルドしたものを使いたいケースです。
- カスタム デバイスを作成する ページは、独自にビルドをカスタマイズしたときの記述です。今回は既存の cuttlefish ターゲットをそのまま使いたかったです。
この記事では、ローカルでビルドした AOSP(aosp_cf_x86_64_tv-trunk_staging-userdebug)を Cuttlefish で起動するまでにハマったところと、その解消法を紹介します。
前提
- 環境
- Ubuntu 24.04
- CPU: Intel Core i5-12400(VT-x 対応)
- メモリ: 64GB
- AOSP のビルド(
aosp_cf_x86_64_tv-trunk_staging-userdebug)が完了していること- 他の Cuttlefish ターゲットでも同様の方法で起動できるはずなので、適宜読み替えてください
結論
最終的には、こんな感じの流れでCuttlefishを起動できました:
AOSP=/path/to/aosp-project
# 1. HOME として使う作業ディレクトリを作り、cvd-host_package を展開
mkdir -p /tmp/cvd_home
cp -r $AOSP/out/host/linux-x86/cvd-host_package/. /tmp/cvd_home/
# 2. assemble_cvd が参照する ~/etc/cvd_config を実ファイルで配置
# (シンボリックリンクだと動かないので注意)
mkdir -p /tmp/cvd_home/etc/cvd_config
cp $AOSP/out/host/linux-x86/cvd-host_package/etc/cvd_config/*.json \
/tmp/cvd_home/etc/cvd_config/
# 3. GPU アクセラレーション用のバイナリを配置
mkdir -p /tmp/cvd_home/bin/x86_64-linux-gnu
cp $AOSP/out/host/linux-x86/bin/x86_64-linux-gnu/gfxstream_graphics_detector \
/tmp/cvd_home/bin/x86_64-linux-gnu/
# 4. 起動
cd /tmp/cvd_home
HOME=/tmp/cvd_home ./bin/launch_cvd \
--config=tv \
--daemon \
--system_image_dir=$AOSP/out/target/product/vsoc_x86_64 \
--x_res=1920 --y_res=1080 --dpi=320
以降のセクションは、このコマンドに辿り着くまでに潰した問題の記録です。
Step 1: Cuttlefish ホスト環境のセットアップ
Cuttlefish を動かすには、ホスト側に cuttlefish-base / cuttlefish-user という deb パッケージが必要です。
公式の Get Startedにも書いてありますが、これらは android-cuttlefish リポジトリをクローンして自前でビルドします。
sudo apt install -y git devscripts equivs config-package-dev debhelper-compat golang curl
git clone https://github.com/google/android-cuttlefish
cd android-cuttlefish
tools/buildutils/build_packages.sh
sudo dpkg -i ./cuttlefish-base_*_*64.deb || sudo apt-get install -f
sudo dpkg -i ./cuttlefish-user_*_*64.deb || sudo apt-get install -f
sudo usermod -aG kvm,cvdnetwork,render $USER
sudo reboot
これらのパッケージが提供するもの
-
capability_query.py(KVM / vhost-net / vhost-vsock の利用可否判定スクリプト) -
/dev/vhost-vsockの udev ルール -
cvdnetworkグループ(TAP インターフェース作成用)
reboot は必須
ユーザーのグループ反映、カーネルモジュールの読み込み、udev ルールの反映のために reboot が必要です。
これをスキップすると後続の起動ステップでエラーになります。
Step 2: launch_cvd してみる → エラー
ホスト環境が整ったら、launch_cvd で起動を試みます。
以下、実際に踏んだ罠を時系列で並べていきます。
罠1: cuttlefish-base の deb パッケージビルドに失敗していた
症状
tools/buildutils/build_packages.sh 実行時、次のようなエラーで失敗します。
Removing cuttlefish-common-build-deps:amd64 because I can't find bazel:amd64
mk-build-deps: Unable to install cuttlefish-common-build-deps at /usr/bin/mk-build-deps line 460.
mk-build-deps: Unable to install all build-dep packages
原因
恥ずかしながら気づいていませんでしたが、前段で cuttlefish-base のインストールが失敗していたことが原因でした。
cuttlefish-common-build-deps が bazel に依存していますが、bazel は Ubuntu 標準の apt リポジトリには含まれていません。
しかも、公式の Get started ページに bazel のインストール手順は記載されていません。
対処
Google 公式 APT リポジトリから bazel をインストールします:
sudo apt install -y apt-transport-https curl gnupg
curl -fsSL https://bazel.build/bazel-release.pub.gpg | gpg --dearmor > bazel-archive-keyring.gpg
sudo mv bazel-archive-keyring.gpg /usr/share/keyrings/
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/bazel-archive-keyring.gpg] https://storage.googleapis.com/bazel-apt stable jdk1.8" \
| sudo tee /etc/apt/sources.list.d/bazel.list
sudo apt update && sudo apt install -y bazel
その後、tools/buildutils/build_packages.sh を再実行すると、エラーが解消されました。
関連して出るエラー(こちらで気づくケース)
cuttlefish-base のインストールが中途半端なまま launch_cvd を実行すると、次のエラーで起動失敗します:
sh: 1: /usr/lib/cuttlefish-common/bin/capability_query.py: not found
VM manager crosvm is not supported on this machine.
Invalid vm_manager: crosvm
capability_query.py は cuttlefish-base パッケージが提供するファイルです。
このエラーが出たら、上記のパッケージビルド&インストールが完了しているか確認してください。
罠2: bootloader ファイルが見つからない
症状
crosvm E: exiting with error 1: failed to open bios ./out/target/product/vsoc_x86_64/bootloader
crosvm E: Caused by: No such file or directory (os error 2)
VIRTUAL_DEVICE_BOOT_FAILED
原因
bootloader ファイルは存在しているのに、crosvm から見えていません。
相対パスで --system_image_dir を指定したため、crosvm の作業ディレクトリからの相対パス解決に失敗しています。
対処
--system_image_dir 引数に絶対パスを指定することで、解消しました。
./bin/launch_cvd \
--system_image_dir=$(pwd)/out/target/product/vsoc_x86_64 \
...
罠3: /dev/vhost-vsock Permission denied
症状
crosvm E: failed to set up virtual socket device
crosvm E: failed to open virtual socket device /dev/vhost-vsock
crosvm E: Permission denied (os error 13)
同時に TAP デバイスのエラーも出ます:
network.cpp: Unable to connect to cvd-wifiap-01 tap interface: Operation not permitted
crosvm_builder.cpp: Unable to connect to "cvd-wifiap-01": Bad file descriptor
原因
cuttlefish-base の udev ルールとグループ権限が反映されていません。つまり reboot していません。
対処
公式のサンプルにも記載されている通り、再起動が必要です。
再起動すると、その他のカーネル モジュールのインストールがトリガーされ、udev のルールが適用されます。(引用元)
sudo reboot
罠4: Could not read from dir "/home/$USER/etc/cvd_config"
※ 以下、$USER と記載した部分は、実際にはユーザー名が入っていました。
症状
Could not read from dir "/home/$USER/etc/cvd_config"
zsh: IOT instruction (core dumped) ./bin/launch_cvd ...
原因
assemble_cvd が $HOME/etc/cvd_config を参照しますが、ディレクトリが存在しません。
対処
$HOME/etc/cvd_config にディレクトリを作成します(中身は次の罠で配置します)。
後述の HOME=/tmp/cvd_home 方式を使う場合は:
mkdir -p /tmp/cvd_home/etc/cvd_config
罠5: invalid config preset: 'tv' → phone にフォールバック
症状
config_flag.cpp: /path/to/android-info.txt contains invalid config preset: 'tv'.
config_flag.cpp: Launching CVD using --config='phone'.
Could not read config file "/home/$USER/etc/cvd_config/cvd_config_phone.json"
原因
~/etc/cvd_config/ に cvd_config_tv.json / cvd_config_phone.json が配置されていません。
AOSP のビルド成果物には含まれているので、そこから配置すればよいです。
対処
cvd-host_package 内の config ファイルを $HOME/etc/cvd_config/ にコピーします:
mkdir -p /tmp/cvd_home/etc/cvd_config
cp $AOSP/out/host/linux-x86/cvd-host_package/etc/cvd_config/*.json \
/tmp/cvd_home/etc/cvd_config/
ls /tmp/cvd_home/etc/cvd_config/
# cvd_config_auto.json cvd_config_phone.json cvd_config_tv.json ...
ハマりポイント:シンボリックリンクでは動かない
最初、ディレクトリごとシンボリックリンクにしてみましたが、assemble_cvd が config ファイルを読み込めず同じエラーが再発しました。
実ファイルとしてコピーするのが確実です。
(補足:既存ディレクトリに対して ln -sf ... dir/ を実行すると、既存ディレクトリの中にリンクが入って dir/dir という二重構造になる罠もあります。これもコピーにすれば回避できます。)
罠6: extract-ikconfig が /home/$USER/bin/ を参照する
症状
Started (pid: xxxxx): /home/$USER/bin/extract-ikconfig
exec of /home/$USER/bin/extract-ikconfig failed (No such file or directory)
Failed to extract ikconfig from .../boot.img
~/bin/ は存在しないし、PATH にも入っていないのに、なぜか assemble_cvd はそこを見に行きます。
原因
assemble_cvd は $HOME/bin/ 以下のヘルパーツールを探す設計になっています。
つまり、cvd-host_package を展開したディレクトリを HOME として認識させる必要があります。
対処
公式ドキュメントの HOME=$PWD 方式を使います:
cd $AOSP/out/host/linux-x86/cvd-host_package
HOME=$PWD ./bin/launch_cvd ...
公式 Get started ページに HOME=$PWD ./bin/launch_cvd --daemon と書いてある意味がここで腑に落ちました。
単なるおまじないではなく、assemble_cvd のパス解決の中核になっているわけです。
罠7: Unix socket のパス長超過 (namelen=121 > 108)
症状
Check failed: namelen <= sizeof(dest->sun_path) (namelen=121, sizeof(dest->sun_path)=108)
MakeAddress failed. Name=/media/$USER/.../cuttlefish/instances/cvd-1/internal/rotary.sock is longer than allowed.
原因
Linux の Unix domain socket のパスは 108 文字制限があります。
AOSP のビルドパスが深い(外付けストレージにマウントしていると特に)と、HOME=$PWD にした際に各種ソケットパスが 108 文字を超えてしまいます。
対処
HOME を短いパスに変えます。cvd-host_package を /tmp 以下にコピーするのが簡単です:
mkdir -p /tmp/cvd_home
cp -r $AOSP/out/host/linux-x86/cvd-host_package/. /tmp/cvd_home/
cd /tmp/cvd_home
HOME=/tmp/cvd_home ./bin/launch_cvd ...
罠8: GPU アクセラレーションが効かない
症状
起動はしますが、描画が重いです。ログを見ると:
exec of .../bin/x86_64-linux-gnu/gfxstream_graphics_detector failed (No such file or directory)
Failed to get graphics availability ... Assuming none.
GPU auto mode: did not detect prerequisites for accelerated rendering support, enabling --gpu_mode=guest_swiftshader.
swiftshader(ソフトウェアレンダリング)にフォールバックしています。
原因
gfxstream_graphics_detector バイナリが $HOME/bin/x86_64-linux-gnu/ 配下にありません。
/tmp/cvd_home に cvd-host_package をコピーした時にサブディレクトリが漏れている可能性があります。
あるいは、AOSP の out/host/linux-x86/bin/x86_64-linux-gnu/ にあって、cvd-host_package 側には含まれていないケースもあります。
対処
AOSP のビルド成果物から /tmp/cvd_home/bin/x86_64-linux-gnu/ に配置します:
mkdir -p /tmp/cvd_home/bin/x86_64-linux-gnu
cp $AOSP/out/host/linux-x86/bin/x86_64-linux-gnu/gfxstream_graphics_detector \
/tmp/cvd_home/bin/x86_64-linux-gnu/
再起動すると、ログが次のように変わります:
GPU auto mode: detected prerequisites for accelerated rendering support.
Enabling --gpu_mode=gfxstream.
体感でレイテンシが明らかに改善します。
動作確認
WebRTC 経由で操作
ブラウザで https://localhost:8443 を開きます。
自己署名証明書の警告は「詳細設定」→「アクセスする」で進めます。
adb で接続
./bin/adb devices
# 0.0.0.0:6520 device
TV ターゲットの DPAD 操作
画面右側のコントロールパネルに DPAD ボタンがあります。キーボードショートカットも使えます:
| キー | 動作 |
|---|---|
| ↑↓←→ | DPAD 上下左右 |
| Enter | DPAD 中央(決定) |
WebUI の画面をクリックしてフォーカスを合わせてから操作してください。
停止
cd /tmp/cvd_home
HOME=/tmp/cvd_home ./bin/stop_cvd
まとめ
公式ドキュメントは、前提を満たせば素直に通る内容になっています。
ですが、前提のうち bazel のインストールと HOME=$PWD の意図については明示されていないため、自前ビルドで始めると詰まります。
自前ビルドで詰まる本質的な理由をまとめると:
-
assemble_cvdは$HOME/bin/と$HOME/etc/cvd_config/を参照する設計になっている - つまり、
cvd-host_packageを展開したディレクトリをHOMEとして認識させる必要がある - ビルドパスが深いと、
HOME=$PWDではソケットパス制限(108 文字)に当たる - GPU アクセラレーションには
gfxstream_graphics_detectorが別途必要
「HOME=$PWD ./bin/launch_cvd」という公式のワンライナーは、おまじないではなくパス解決の核心だった、というのが一番の学びでした。