Androidエミュレータ(Windows)で使用するシステムイメージを差し替える・/systemの内容を変更する


はじめに

Android Studioでアプリ開発するにあたり、SDK Managerでエミュレータとシステムイメージを取得して使用していましたが
・自前でAOSPからビルドしたシステムイメージを使う
・エミュレータで/system以下に変更を加える(アプリをpushするなど)
という必要があり、それで行き詰まった部分があるので、解決した方法と合わせて記録しておきます。

わたしの環境

  • Android Studio/エミュレータ動作環境

    • Windows 10 Home 64bit
    • CPU: Core i7-8700 @ 3.20GHz
    • RAM: 16GB
    • Android Studio: 3.6.3
    • Android Emulator: 30.0.12
  • AOSPビルド環境(仮想PC)

    • Oracle VM VirtualBox 6.0
    • Ubuntu 16.04 LTS
    • AOSPソースコード: Android Pie android-9.0.0_r42

自前でビルドしたシステムイメージを使いたい

困ったこと

エミュレータを使用する際はAVD Managerで何らかのデバイスを作成→システムイメージを選択します。
このシステムイメージはAndroid各バージョン(私の環境ではAPIレベル30~古いのは7), x86-32/64, Google Play有無など選ぶことができますが、自前でカスタマイズしたシステムイメージを使用したいということもあると思います。

私の場合はAndroid9に対し、ビルドオプション変更やプリインストールのアプリを追加したものを使いたかったので、AOSPでx86-64bit向けのシステムイメージ(GSI)を作成しました。
詳細な流れは省略しますが、Ubuntu上でビルド環境の構築→ソース取得→ビルド環境選択→ビルドです。
詳しくはこちらこちらこちらですね。

ビルドの流れ(ざっくり例)
$ sudo apt-get install git-core gnupg flex bison gperf build-essential zip curl zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 lib32ncurses5-dev x11proto-core-dev libx11-dev lib32z-dev libgl1-mesa-dev libxml2-utils xsltproc unzip
$ mkdir <作業ディレクトリとか>
$ repo init -u https://android.googlesource.com/platform/manifest -b android-9.0.0_r42
$ repo sync
$ source build/envsetup.sh
$ lunch aosp_x86_64-userdebug
$ make -j8

出力先ディレクトリ(/home/<user>/<workspace>/out/target/product/generic_x86_64)にこんなイメージ群ができあがりました:

$ ls | grep img
cache.img
encryptionkey.img
ramdisk.img
system-qemu.img
system.img
userdata.img
vbmeta.img
vendor-qemu.img
vendor.img

これで作成したシステムイメージをエミュレータで使用したかったのですが…AVD Managerでは自前で用意したシステムイメージを選択するようなメニューが無かったので困りました。

解決方法

エミュレータ起動時のオプションで

C:\xxx\Android\android-sdk\tools>emulator -system <自前でビルドしたイメージ置き場>/system-qmeu.img -avd xxx

でシステムイメージを参照することで、そのイメージを使用してエミュレータが起動します。
ポイントはsystem.imgではなくsystem-qemu.imgを使用することでしょうか。(system.imgだと起動しませんでした)
vendorも同様に -vendor <自前でビルドしたイメージ置き場>/vendor-qmeu.img のオプションを使用して自前のイメージが使えます。

起動オプションで指定するのが面倒な場合は、SDK Managerで取得したシステムイメージ置き場のファイルを直接置き換えれば済みます。自分はそうしてしまいました。

C:\xxx\Android\android-sdk\system-images\android-28\default\x86_64\
  system.img ←system-qmeu.imgをリネームして配置
  vendor.img ←vendor-qmeu.imgをリネームした配置

※注意
自前のシステムイメージを置き換えて使用する際は、置き換え前と後でAndroidのバージョンやアーキテクチャを合わせる必要があります。
SDK Managerでx86-32bit用で取得したシステムイメージに対し、x86-64bit用でビルドしたイメージを置き換えても正常に動作しない(はず)です。

/systemパーティションの内容を変更したい

困ったこと

自前でビルドしたシステムイメージでエミュレータを起動することはできましたが、それに追加で/systemパーティションにアプリやライブラリをpushしようとしてもRead-Onlyでできません。
よくある解決方法として、/systemをリマウントするというものがありますが、どうにもうまくいきません。

mountコマンド
C:\>adb shell
$ mount -o,rw remount /system
$ mount: 'remount'->'/system': No such file or directory
adbコマンド
C:>adb root
C:>adb remount
remount of the / superblock failed: Permission denied

解決方法

Android公式に解決できる起動オプションが載っていました。

-writable-system
エミュレーション セッション中に書き込み可能なシステム イメージを作成する際にこのオプションを使用します。方法は次のとおりです。
 1. -writable-system オプションを使用して仮想デバイスを起動します。
 2. コマンド ターミナルから adb remount コマンドを入力し、system/ を読み取り / 書き込み用として再マウントするようにエミュレータに指示します(デフォルトでは読み取り専用としてマウントされます)。

writable-systemで起動
C:\xxx\Android\android-sdk\tools>emulator -writable-system -avd xxx
emulator: WARNING: System image is writable
...
emulator: INFO: boot completed
からのadbコマンド
C:>adb root
C:>adb remount
remount succeeded

すんなりいけました!

ここで/systemに加えた変更はエミュレータを落としても保持されます。
次回、また-writable-systemオプションで起動すれば前回の変更内容が反映された状態です。
ただ-writable-systemなしで起動した際は、/systemの変更なしの初期状態となります。(そしてRead-Onlyです)
また通常Read-Onlyなのは/systemだけでなく/vendorなども同様ですが、-writable-systemでエミュレータを起動すると、/vendorなども変更が可能となります。

なお各AVDの設定やユーザーデータの類は以下の場所に置かれていますが、デフォルトの状態に対して加えた変更は"xxx.img.qcow2"ファイルに書かれているようです。
AVD ManagerなどでWipe Dataするとすべての変更はなくなります。

C:\Users<user>.android\avd\xxx.avd\
  userdata-qemu.img.qcow2
  system.img.qcow2
  vendor.img.qcow2
  …

userdata-qemu.img.qcow2は-writable-systemなしで起動しても毎回エミュレータ終了時に更新されて保存されますが
system.img.qcow2, vendor.img.qcow2 などは-writable-system の場合のみ更新されることがわかります。

最後に

当然といえば当然ですが、実機とエミュレータとではいろいろと扱い方に違いがあるのだなと感じました。
また何か困りごと&解決方法が出てきたら追記しようと思います。

以上です。