Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android Binder Internals

I am working on a project where we add some non-standard security features to an android system, and I have some serious problems to adapt the Binder.

Does anyone have a deep knowledge on the Binder System and knows why the Binder "blocks" a process to transfer a Parcel, and how the receiving process is unblocked?

like image 714
user1685376 Avatar asked Nov 04 '22 15:11

user1685376


1 Answers

This is expected behavior from what is said in Android documentation: http://developer.android.com/reference/android/os/IBinder.html

The key IBinder API is transact() matched by Binder.onTransact(). These methods allow you to send a call to an IBinder object and receive a call coming in to a Binder object, respectively. This transaction API is synchronous, such that a call to transact() does not return until the target has returned from Binder.onTransact(); this is the expected behavior when calling an object that exists in the local process, and the underlying inter-process communication (IPC) mechanism ensures that these same semantics apply when going across processes.

Observe the Notification API. We make some calls so we can finally obtain a reference to a NotificationManager object. With this object we call 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    }

This call is synchronous with your process and it will result on the following call:

 @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}

See the call to 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}

Now the method (from IBinder) mRemote.transact(Stub.TRANSACTION_enqueueNotification, _data, _reply, 0); will do the magic. According to the documentation on Parcel class:

A Parcel can contain both flattened data that will be unflattened on the other side of the IPC (using the various methods here for writing specific types, or the general Parcelable interface), and references to live IBinder objects that will result in the other side receiving a proxy IBinder connected with the original IBinder in the Parcel.

So once the other side receives the serialized data, it will respond accordingly. So it blocks the calling process because of system design, to let development be more continuous, to not add complexity in app development and for consistency. The receiving process is not blocked, it is a live IBinder object and one of its threads will answer the request. Now if the object is working under heavy overhead, it's possible that it can block for a long time before answering. So if you're planning on talking with busy guys, make sure you have a helper to keep waiting for a response (another thread, pehaps).

\o/

like image 151
HFSDev Avatar answered Nov 08 '22 10:11

HFSDev