BETA

Android OS 起動とSElinux

投稿日:2019-03-01
最終更新:2019-03-01

はじめに

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等を参考にしてください。

#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

参考

技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
駆け出しエンジニアからエキスパートまで全ての方々のアウトプットを歓迎しております!
or 外部アカウントで 登録 / ログイン する
クランチについてもっと詳しく

この記事が掲載されているブログ

@dOpIa1PQNPi5jLrnの技術ブログ

よく一緒に読まれる記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう