私は Android システムにいくつかの非標準のセキュリティ機能を追加するプロジェクトに取り組んでおり、Binder を適応させるにはいくつかの深刻な問題があります。
Binder System について深い知識があり、Binder が小包を転送するプロセスを「ブロック」する理由と、受信プロセスのブロックを解除する方法を知っている人はいますか?
私は Android システムにいくつかの非標準のセキュリティ機能を追加するプロジェクトに取り組んでおり、Binder を適応させるにはいくつかの深刻な問題があります。
Binder System について深い知識があり、Binder が小包を転送するプロセスを「ブロック」する理由と、受信プロセスのブロックを解除する方法を知っている人はいますか?
これは、Android のドキュメントに記載されている予想される動作です: http://developer.android.com/reference/android/os/IBinder.html
重要な IBinder API は、Binder.onTransact() に対応する transact() です。これらのメソッドを使用すると、それぞれ IBinder オブジェクトに呼び出しを送信し、Binder オブジェクトに着信する呼び出しを受信できます。このトランザクション API は同期的であるため、ターゲットが Binder.onTransact() から戻るまで、transact() への呼び出しは返されません。これは、ローカル プロセスに存在するオブジェクトを呼び出すときに想定される動作であり、基になるプロセス間通信 (IPC) メカニズムにより、プロセス間を移動するときにこれらの同じセマンティクスが適用されることが保証されます。
通知 API を観察します。最終的に NotificationManager オブジェクトへの参照を取得できるように、いくつかの呼び出しを行います。このオブジェクトで、notify(...); を呼び出します。
117 public void notify(String tag, int id, Notification notification)
118 {
119 int[] idOut = new int[1];
120 INotificationManager service = getService();
121 String pkg = mContext.getPackageName();
122 if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
123 try {
124 service.enqueueNotificationWithTag(pkg, tag, id, notification, idOut);
125 if (id != idOut[0]) {
126 Log.w(TAG, "notify: id corrupted: sent " + id + ", got back " + idOut[0]);
127 }
128 } catch (RemoteException e) {
129 }
130 }
この呼び出しはプロセスと同期しており、次の呼び出しになります。
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
38 {
39 switch (code)
40 {
...
46 case TRANSACTION_enqueueNotification:
47 {
48 data.enforceInterface(DESCRIPTOR);
49 java.lang.String _arg0;
50 _arg0 = data.readString();
51 int _arg1;
52 _arg1 = data.readInt();
53 android.app.Notification _arg2;
54 if ((0!=data.readInt())) {
55 _arg2 = android.app.Notification.CREATOR.createFromParcel(data);
56 }
57 else {
58 _arg2 = null;
59 }
60 int[] _arg3;
61 _arg3 = data.createIntArray();
62 this.enqueueNotification(_arg0, _arg1, _arg2, _arg3);
63 reply.writeNoException();
64 reply.writeIntArray(_arg3);
65 return true;
66 }
67 case TRANSACTION_cancelNotification:
68 {
...
169return super.onTransact(code, data, reply, flags);
170}
this.enqueueNotification への呼び出しを参照してください。
public void enqueueNotification(java.lang.String pkg, int id, android.app.Notification notification, int[] idReceived) throws android.os.RemoteException
188{
189android.os.Parcel _data = android.os.Parcel.obtain();
190android.os.Parcel _reply = android.os.Parcel.obtain();
191try {
192_data.writeInterfaceToken(DESCRIPTOR);
193_data.writeString(pkg);
194_data.writeInt(id);
195if ((notification!=null)) {
196_data.writeInt(1);
197notification.writeToParcel(_data, 0);
198}
199else {
200_data.writeInt(0);
201}
202_data.writeIntArray(idReceived);
203mRemote.transact(Stub.TRANSACTION_enqueueNotification, _data, _reply, 0);
204_reply.readException();
205_reply.readIntArray(idReceived);
206}
207finally {
208_reply.recycle();
209_data.recycle();
210}
211}
メソッド (IBinder から) mRemote.transact(Stub.TRANSACTION_enqueueNotification, _data, _reply, 0); 魔法を行います。Parcel クラスのドキュメントによると:
パーセルには、IPC の反対側で非フラット化されるフラット化されたデータ (特定の型を書き込むためのさまざまなメソッド、または一般的な Parcelable インターフェイスを使用) と、ライブ IBinder オブジェクトへの参照の両方を含めることができます。パーセル内の元の IBinder に接続されたプロキシ IBinder。
したがって、反対側がシリアル化されたデータを受信すると、それに応じて応答します。そのため、システム設計のために呼び出しプロセスがブロックされ、開発がより継続的に行われ、アプリ開発が複雑にならないようになり、一貫性が保たれます。受信プロセスはブロックされず、ライブ IBinder オブジェクトであり、そのスレッドの 1 つが要求に応答します。オブジェクトが重いオーバーヘッドの下で動作している場合、応答する前に長時間ブロックされる可能性があります。したがって、忙しい人たちと話をする予定がある場合は、応答を待ち続けるためのヘルパーを確保してください (別のスレッド、おそらく)。
\o/
ブロックとブロック解除は実際には設計によるものです。Binder は「トランザクション ベース」ですが、実際には、Binder データを保持するスレッドごとのインスタンスである IPCThreadState のはるかに低いレベルで発生します。
メッセージは、低レベルのドライバー (通常は /dev/binder ですが、8.0 以降では vndbinder、hwbinder) で ioctl(2) 操作を使用して送受信されます。これにより、呼び出し元は、ソケットのように 2 つのシステム コール (send(2)/receive(2)) を実行するのではなく、1 つの論理操作でメッセージの送信と応答の受信の両方を実行できるようになります。
Android サービス (特に system_server) と同様に、アプリにはバインダー スレッド プール (ProcessState によって開始される) があります。これは、ドライバからの着信トランザクションと BR_* コードを提供するものであり、Binder の一見「非同期」な性質がどのように発生するかです。プールはスリープしますが、着信通知 (BR_DEAD_BINDER など) が到着すると、スレッドの 1 つ (そのスレッドで Java コールバック (死亡通知 onBinderDied の場合) に変換されるかどうかは制御できません。
この設計により、両方の長所を簡単に享受できます。必要な場合は非同期操作に見え、「トランザクション」をコミットしたい場合は送受信をブロックします。これは、パーセルを取得できる AIDL 生成コードにも役立ちます。プールから取り出し、完了したらリサイクルします。