【问题标题】:Android Binder InternalsAndroid Binder 内部结构
【发布时间】:2012-09-12 15:07:20
【问题描述】:

我正在开展一个项目,我们在其中向 android 系统添加了一些非标准的安全功能,但我在调整 Binder 时遇到了一些严重的问题。

有没有人对Binder系统有深入的了解并且知道Binder为什么会“阻塞”一个进程来传输Parcel,以及接收进程是如何被解除阻塞的?

【问题讨论】:

    标签: android driver android-binder


    【解决方案1】:

    这是 Android 文档中所说的预期行为: http://developer.android.com/reference/android/os/IBinder.html

    关键的 IBinder API 是由 Binder.onTransact() 匹配的 transact()。 这些方法允许您发送对 IBinder 对象的调用并 分别接收传入 Binder 对象的调用。这 事务 API 是同步的,因此对 transact() 的调用确实 在目标从 Binder.onTransact() 返回之前不返回; 这是调用存在于 本地进程和底层进程间通信 (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 类的文档:

    一个 Parcel 可以同时包含将在其上展开的展开的数据 IPC的另一端(使用这里的各种方法进行编写 特定类型,或通用 Parcelable 接口),以及参考 活的 IBinder 对象,这将导致对方收到一个 代理 IBinder 与 Parcel 中的原始 IBinder 连接。

    所以一旦对方收到序列化的数据,就会做出相应的响应。所以它因为系统设计而阻塞了调用过程,让开发更加连续,不增加应用程序开发的复杂性和一致性。 接收进程没有被阻塞,它是一个活动的 IBinder 对象,它的一个线程将响应请求。现在,如果对象在沉重的开销下工作,它可能会在响应之前阻塞很长时间。因此,如果您打算与忙碌的人交谈,请确保您有一个助手来继续等待回复(也许是另一个线程)。

    \o/

    【讨论】:

      【解决方案2】:

      阻塞和解除阻塞确实是设计使然 - Binder 是“基于事务的”,但它实际上发生在 IPCThreadState 的低得多的级别,它是每个线程实例保存 Binder 数据:

      消息通过 ioctl(2) 操作在低级驱动程序(通常是 /dev/binder,但从 8.0 vndbinder、hwbinder)上发送/接收。这使得调用者可以在一个逻辑操作中发送消息和接收回复,而不是像套接字那样执行两个系统调用(send(2)/receive(2))。

      您的应用程序有一个活页夹线程池(由 ProcessState 启动),Android 服务(特别是 system_server)也是如此。这是为来自驱动程序的传入事务和 BR_* 代码提供服务的内容,这就是 Binder 看似“异步”性质的发生方式 - 池处于休眠状态,但是当传入通知(例如 BR_DEAD_BINDER)到达时,其中一个线程(您可以't control which)在那个线程中被转换为 Java 回调(对于死亡通知,onBinderDied)。

      这种设计可以轻松享受两全其美 - 在需要时看似异步操作,并在您想要提交“事务”时阻止发送/接收,这也有助于 AIDL 生成的代码能够从池中取出一个包裹,完成后将其回收。

      【讨论】:

        猜你喜欢
        • 2011-06-02
        • 2012-12-12
        • 1970-01-01
        • 1970-01-01
        • 2014-10-20
        • 1970-01-01
        • 2015-09-16
        • 2019-01-08
        • 2021-07-12
        相关资源
        最近更新 更多