Help us understand the problem. What is going on with this article?

Android OS 起動とSElinux

More than 1 year has passed since last update.

はじめに

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層より上の世界ですね。

根っこは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スクリプトの記述仕様はこちら

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

  1. Actions
  2. Services

1. Actions

何かのイベントトリガーでの動作を書くことが出来ます。ここで使える動作はこちらのCommandsに記載されているものだけ。
chmodやchownのようなfilesystem構成を作るためのものや、2. Servicesを呼び出すコマンドがあったりします。

記述はこんな感じでon XXXの後にコマンドをつらつらと書く感じ。実際の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についてはこちらのExample policyを見るのが分かりやすいと思います。
typeでpolicyの名前+exec_typeを定義し、allow policy名 許可するルールをつらつらと書いていきます。
許可するルールの取得方法はこちらに記載されていますね。要約すると

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

ってところでしょうか。

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

2についてはこんな感じでファイルと権限について書かれたfileがあるので、そちらに定義を加えます。

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

参考

developer-kikikaikai
元CのLinux組み込み開発者→201904からGo言語とJavaScript/ReactのWebサービス屋さんになります。githubにツールやOSSを上げています。 組み込み時代はミドルウェアより上位層が主戦場でした。たまにRubyやpython、Java/Androidも若干触ります。 技術の幅を増やすのはもちろんだけど、それ以上にチーム構築・チーム開発への貢献力を磨きたい
https://github.com/developer-kikikaikai
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away