Android OS　起動とSElinux

## はじめに
Androidってkernelは完全にJavaでラップされていて、開発者が触るレイヤーはJavaの領域だけなのかななんて思っていました。
しかし実はLinux部分もガツガツ触って開発したりするんですね。

中々触る機会はなさそうですが、起動周りに触れたので記録に残しておこうと思います。

## Androidの基本構造: Architecture

こんな感じに一番下にLinux kernelがあり、その上にHAL(Hardware abstraction layer)、System services、Binder IPC、Application frameworkと階層が続いています。Application frameworkがJava向けのAPI達で、javaから下位層を扱うの階層をちょっとずつ切ってる感じ。Andoridアプリが触るのはjava層より上の世界ですね。

![](https://source.android.com/devices/images/ape_fwk_all.png)

### 根っこはLinux。組込の人にはなじみがある…けどちょっと違う

Linux kernel上でAndroidの思想通りに開発してもらえるよう、googleさんがAPI公開や開発ルールを定義しながらOSを公開してるって印象を受けました。
Linux kernel自体も例えばUbuntuやCent OSと同じ動きをするわけではないので、例えば起動の仕組みが独特だったり、udevのような仕組みが使えなかったりします。
udev、フォルダはあるからガンガン使ったろうと思ったのにびくともしなくてビビった(笑)

## Androidと起動: init.rcとService

Android ApplicationだとActivity, Service, Applicationそれぞれでライフサイクルが違って、Activityなら最初にonCreateが呼ばれて～といった取り決めがあると思いますが、それ以前のboot後はどう動いているかという話です。

initプロセスから起動されるのですが、起動シーケンスの制御をどうするかというと、**rcスクリプト**というものを使います。
rcスクリプトの記述仕様は[こちら](https://android.googlesource.com/platform/system/core/+/master/init/README.md)。

以下の2種類の要素で構成されます。

1. Actions
2. Services

### 1. Actions

何かのイベントトリガーでの動作を書くことが出来ます。ここで使える動作は[こちら](https://android.googlesource.com/platform/system/core/+/master/init/README.md)のCommandsに記載されているものだけ。
chmodやchownのようなfilesystem構成を作るためのものや、2. Servicesを呼び出すコマンドがあったりします。

記述はこんな感じで`on XXX`の後にコマンドをつらつらと書く感じ。[実際のinit.rc](https://android.googlesource.com/platform/system/core/+/master/rootdir/init.rc)等を参考にしてください。

```init.rc
#on XXXで定義
on early-init
    #こんな感じでコマンドを羅列
    chown root system /dev/memcg/memory.pressure_level
    chmod 0040 /dev/memcg/memory.pressure_level
    #start サービス名で指定したサービスが起動されます。
    start ueventd
```

### 2. Services

initスクリプトからコマンドをSerivceとして起動することが出来ます。こちらもinit.rcより抜粋。
こんな感じで`service サービス名 コマンド`と記載することで、on XXXのstartトリガーでサービスの起動が出来ます。
サービスの権限や終了後再起動するかといった設定をserviceの下に記載していきます。

```
service flash_recovery /system/bin/install-recovery.sh
    class main
    oneshot
```

serviceを定義する際に重要になるのが**SELinux**。Android OSはSELinuxを使用していて、正しくSELinuxのルールを定義していなければserviceが起動できません。
~~正直めんどい~~

## AndroidとSELinux

AndroidのserviceにはSELinuxのルールを適切に定義して上げないと起動できないようになっています。
このSELinuxルールが中々曲者で、ただ自前サービスに合わせて好きに定義すればいいわけではなく、**Androidのポリシーに従って正しく定義してあげないといけない**。
そうでないとneverallow rulesってのに引っかかり、ビルドすら通らないようになっています。~~つらい~~
後は、system配下とvendor配下のルールで出来ることが違ったりするので、やりたいことや構成によってどの配下に置くかを意識しないといけない。~~これは大変すね~~

やることは以下。

1. SELinuxのpolicy ruleを定義する。
2. 実行するコマンドファイルににfile_contextで権限を設定

1については[こちら](https://source.android.com/security/selinux/customize)のExample policyを見るのが分かりやすいと思います。
typeでpolicyの名前+exec_typeを定義し、`allow policy名 許可するルール`をつらつらと書いていきます。
許可するルールの取得方法は[こちら](https://source.android.com/security/selinux/validate)に記載されていますね。要約すると

1. Reading denials
    2. `dmesg | grep avc:`で拒否されたログを取得する。
2. Switching to permissive
    3. ログ取る時は`adb shell setenforce 0`等でpermissibeモードにしてね
2. Using audit2allow
    3. ログの結果にselinux/policycoreutils/audit2allowコマンドをかますとルールが取れるよ！

ってところでしょうか。

詳細は参考のURLを参照ください。

2については[こんな感じ](https://android.googlesource.com/platform/external/sepolicy/+/jb-mr2-dev/file_contexts)でファイルと権限について書かれたfileがあるので、そちらに定義を加えます。

```file_contexts
#############################
# Vendor files
#
/vendor(/.*)?		u:object_r:system_file:s0
/vendor/bin/gpsd	u:object_r:gpsd_exec:s0
```

## 参考

- init.rcによる起動周り
[Android起動周りのノウハウ](https://www.slideshare.net/chancelab/android-27395892)：起動や構造の概要
[Android Init Language](https://android.googlesource.com/platform/system/core/+/master/init/README.md)：Initスクリプトの仕様

- SELinux
[AndroidとSELinux](https://www.slideshare.net/sola1980/androidse-linux)：概要をつかむのに非常に助けられました。
[Customizing SELinux](https://source.android.com/security/selinux/customize)：Android側のポリシーがガンガン変わるようで、結局公式が一番役に立ちました。
[Policy Compatibility](https://source.android.com/security/selinux/compatibility)：互換性と設計思想の話
[Validating SELinux](https://source.android.com/security/selinux/validate)：SELinuxルール作成方法
[Implementing SELinux](https://source.android.com/security/selinux/implement)：実装概要の話