2012/09/24

Android:隔離プロセス上でServiceを実行する~isolatedProcess~


JellyBeanからServiceを隔離プロセス上で実行できるようになりました。

Serviceを隔離プロセス上で実行するにはAndroidManifestのserviceタグでisolatedProcess属性にtrueを指定します。
# デフォルト値はfalse
http://developer.android.com/guide/topics/manifest/service-element.html#isolated
<service ...
    android:isolatedProcess="true" />

●PIDとUID

実際にこのサービスを実行すると、呼出し元とは別のPIDで起動しているのがわかります。
# ps
USER      PID   PPID  VSIZE  RSS   WCHAN    PC         NAME
...
u0_a47    1068  37    171612 34304 ffffffff 40033a40 S yuki.test.isolateservice
u0_i5     1083  37    170200 28668 ffffffff 40033a40 S yuki.test.isolateservice
...
隔離プロセス上ではUIDも異なります。

isolatedProcess=false(同プロセス)でサービスを呼び出した場合
caller  UID=10047 / PID=1198
service UID=10047 / PID=1198

isolatedProcess=true(隔離プロセス)でサービスを呼び出した場合
caller  UID=10047 / PID=1068
service UID=99006 / PID=1083
隔離プロセスのUIDには99000~99999が割り当てられます。
参考:android.os.Process.FIRST_ISOLATED_UID / LAST_ISOLATED_UID


●隔離プロセスの制限

隔離プロセス上で実行できる動作には厳しい制限があります。

【動作制限の例】
  • ContentResolverを操作できない
  • BroadcastIntentを送信できない
  • BroadcastReceiverを登録できない
  • 自プロセスのメモリ情報を取得できない
  • その他色々...

この辺の動作制限は、主にActivityManagerServiceのenforceNoIsolatedCaller()でチェックしています。
http://tools.oesf.biz/android-4.1.1_r1.0/xref/frameworks/base/services/java/com/android/server/am/ActivityManagerService.java#2240
隔離プロセスから制限有りのAPIを呼ぶとエラーが返されます。

エラーメッセージ「Isolated process not allowed to call ***」

・BroadcastIntentの送信時
java.lang.SecurityException: Isolated process not allowed to call broadcastIntent
  ...
  at android.content.ContextWrapper.sendBroadcast(ContextWrapper.java:312)
  at yuki.test.isolateservice.IsolatedService.onHandleIntent(IsolatedService.java:14)

・ContentResolver経由でクエリ発行時
java.lang.SecurityException: Isolated process not allowed to call getContentProvider
  ...
  at android.content.ContentResolver.query(ContentResolver.java:313)
  at yuki.test.isolateservice.IsolatedService.onHandleIntent(IsolatedService.java:18)

●隔離プロセス上にあるサービスとの通信

隔離プロセス上にあるサービスとの通信はstartやbindといった基本APIに限られます。
バインド状態にあるサービスの独自APIをコールすることはできません。

サービス接続時に渡されるIBinderにはBinderProxyインスタンスが格納されています。
private ServiceConnection mConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        LocalBinder binder = (LocalBinder)service;  // ClassCastException発生!
    }
そのため、サービスインスタンスが取得できません。


●隔離プロセスの使いどころ

ドキュメントとして、使用すべきシーンやガイドラインが見当たりませんが、、、
最小権限の原理と、サービスが基本的にバックグラウンドで動作するという特性から、
セキュリティに関連するアップデートと思われます。
隔離プロセスを応用したベストプラクティスがあれば新たに記事を投稿しようと思います。

以上です。