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](https://source.android.google.cn/setup/build/gsi))を作成しました。
詳細な流れは省略しますが、Ubuntu上で**ビルド環境の構築→ソース取得→ビルド環境選択→ビルド**です。
詳しくは[こちら](https://source.android.google.cn/setup/build/initializing)や[こちら](https://source.android.google.cn/setup/build/downloading)や[こちら](https://source.android.google.cn/setup/build/building)ですね。

```:ビルドの流れ(ざっくり例)
$ 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公式](https://developer.android.com/studio/run/emulator-commandline)に解決できる起動オプションが載っていました。

*-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 の場合のみ更新されることがわかります。

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

以上です。