2

私は、Android の BINDER IPC メカニズムの背後にあるソフトウェア アーキテクチャを理解しようとしているものをすべて読んでいます。私が理解していることから、BINDER はカーネル空間に位置し、プロセス間でメッセージを中継できる共有メモリの割り当てをユーザー空間アプリケーションに一時的に与えます。

私が理解を失い始めたのは、実際の実装がどのように機能するか、特にパーセルに関連する部分です。

ネットで調べていたところ、Network/Wifi/Notificaiton/Battery など ( Docs ) など、Android が提供する任意のサービスの実装を見つけました。私の読書から、ユーザー空間プログラムはサービスクラス自体をインスタンス化するのではなく、サービスクラスへの参照を取得する必要があることを学びました. そのため、これは、Androidが既にサービスを実行している、または少なくとも必要なときにそれを開始するためのリソースを持っていることを示す間接的な方法として受けとめました。実装は基本的に次のようにレイアウトされました。Context.getSystemService(Context.X)

Battery.class

BatteryManager.setBatteryState(){
    Parcel parcelLocal = Parcel.obtain();
    parcelLocal.writeInterfaceToken("android.power.BatteryManager");
    parcelLocal.writeInt(1/0); //Depending on requested state
    BinderObject.transact      //Send the data using BINDER
    CheckForExceptions();      //Upon return, check for exceptions 
    ReadResponse();            //If none, read the response from the target
    DoAppropriateAction();     //Whatever we need to do after setting the state
    parcelLocal.recycle();     //Return the parcel resource
}

最初は単純に思えます: ユーザーが次のようなことをすると:

BatteryMonitor bMonitor = Context.getSystemService(Context.POWER_SERVICE);
bMonitor.setBatteryStatus(1); 

次に、user'sインスタンスは BINDER メカニズムを使用してsystem's実際のサービス コントローラーと通信します (同じクラスのインスタンスはどれですか? )。ただし、上記のコードはシステムのバッテリー監視サービスの実装であり、BINDER データを実際に受信しているのは誰でしょうか?

TL;DR : 1000 行のコードを 10 行に凝縮しようとしたため、これがすべて非常に紛らわしい場合、要約は次のとおりです。ユーザーがネットワーク/Wifi などのハードウェアの状態を制御しようとするとき/Location/Notifications(タッチスクリーン) - Android 内で実際に何が起こっているのか、これらの抽象化されたサービスに関連付けられたハードウェアを実際に制御しているのなのか?

: 上記のコードは完全に捏造されたものであり、一般的な構造のみを示すことを目的としています。

4

1 に答える 1

1

ほとんどのシステム サービスは、プロセス内のスレッドとして実行されsystem_serverます。起動時に、呼び出しへの招待 ( の呼び出しを参照) を渡し、 を呼び出すaddService()アプリSystemServer.javaservicemanager招待を配布できますgetSystemService

物事が進んだら、セットアップ全体を、アプリがクライアント (リモートまたはプロキシ側) であり、サーバー (ローカルまたはスタブ側) が話しているシステム サービスである一種のクライアント サーバー アーキテクチャと考えることができます。に。クライアントとサーバーは、バインダーと呼ばれるプロセス間通信 (IPC) サブシステムを介して通信します。バインダーにはさまざまな部分があります。フレームワーク コンポーネントはパーセルのマーシャリングとアンマーシャリングを実行しますが、カーネル ドライバーは ioctl 呼び出しとの間で実際のメモリ コピーを行い、プロセスおよびスレッド レベルでの呼び出しに招待されたユーザーを追跡します。

アプリは、プロキシ経由でバインダーとやり取りします。たとえば、 を使用するLocationManagerServiceと、 のインスタンスが取得されますandroid.location.ILocationManager。Proxy クラスのメソッドの 1 つは次のgetLastLocation()とおりです。

...
@Override public android.location.Location getLastLocation(android.location.LocationRequest request, java.lang.String packageName) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
android.location.Location _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((request!=null)) {
_data.writeInt(1);
request.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
_data.writeString(packageName);
mRemote.transact(Stub.TRANSACTION_getLastLocation, _data, _reply, 0);
_reply.readException();
if ((0!=_reply.readInt())) {
_result = android.location.Location.CREATOR.createFromParcel(_reply);
}
else {
_result = null;
}
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
...

ここでは、トランザクション コードTRANSACTION_getLastLocationが必要なデータと共にインターフェイスに書き込まれ、結果が読み取られることがわかります。スタブ側ではonTransact()、トランザクション コードに従ってすべての受信トランザクションを処理するサービスのプロセス空間で実行されるメソッドがあります。

...
case TRANSACTION_getLastLocation:
{
data.enforceInterface(DESCRIPTOR);
android.location.LocationRequest _arg0;
if ((0!=data.readInt())) {
_arg0 = android.location.LocationRequest.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
java.lang.String _arg1;
_arg1 = data.readString();
android.location.Location _result = this.getLastLocation(_arg0, _arg1);
reply.writeNoException();
if ((_result!=null)) {
reply.writeInt(1);
_result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
}
else {
reply.writeInt(0);
}
return true;
}
...

簡単に言えば、system_serviceプロセスは呼び出し元に代わって動作します。これにより、通常はハードウェアまたはその他のシステム リソースに対する特権操作を行うことができます。service_managerセキュリティは、1) アプリが (経由で取得された) 呼び出しへの招待を持っていること、および 2) サービス自体が実装するすべてのチェック(マニフェストで宣言され、インストール時に承認された場合のgetSystemServiceチェックなど) を渡すACCESS_COARSE_LOCATIONことに基づいています。エンドユーザー)。ACCESS_FINE_LOCATIONLocationManagerService

更新: 位置情報サービスの場合、これらのハードウェア操作には GPS ハードウェアから実際の NMEA データを取得する必要があります。現在、これを実現する方法は、GpsLocationProviderJNI を介してネイティブ コードにインターフェイスするクラスを使用することです。このネイティブ コード ( com_android_server_location_GpsLocationProvider.cpp) は、(構造体に保持された抽象化レイヤーを介して) ハードウェア デバイスが開かれるhw_module_t場所であり、ロケーション コールバックが行われます (例: location_callback())。これらはすべて、system_server特権 UID を使用してプロセス空間内で実行されますsystemGpsLocationProviderこれを確認するには、位置情報対応アプリを実行し、logcat でタグを探し、ログに記録された PID が のものであることを確認しますsystem_server。例えば:

$ adb logcat | grep -i gps
...
D/GpsLocationProvider(  731): Reset GPS properties, previous size = 8
...

$ adb shell ps | grep system_server
system    731   441   1094884 89232 ffffffff b74d1d05 S system_server
$

最後に、ビデオ チュートリアルDeep Dive Into Android IPC/Binder Frameworkで詳細を学ぶことを強くお勧めします。講演のスライドはこちらからご覧いただけます

于 2015-06-22T20:27:00.677 に答える