I'm currently trying to handle some cross-process interactions using
Binder. I have a few questions, but first some background.
The system I'm trying to implement is a callback from an Android native
system "service", whenever a certain event occurs[1]. The "client" which
receives this callback may be written in Java or native code.
My questions are:
1) Callbacks versus blocking calls.
My intention is:
* The service exposes two additional APIs on its existing IInterface:
"register listener" and "unregister listener".
* These take an instance of a new IInterface, which has a single
callback method indicating the event has occurred.
* The service maintains a vector of these registered listeners.
* The service calls a callback on each listener each time the event
occurs.
Is this the normal way to notify clients of events asynchronously?
Or alternatively, the client could make a single blocking call to the
service: "waitUntilEventOccurs". The service would only return from its
onTransact when the event occurred. Is that more normal in a Binder
world?
2) Abandoning blocking transactions.
If I use the "waitUntilEventOccurs" solution: how best should I cancel
an outstanding wait? One way is with another API that somehow signals
the existing blocked Binder thread and causes it to return immediately.
Or is there some means built into Binder?
(All subsequent questions assume I am using a genuine 'callback' rather
than waiting within onTransact calls).
3) Can weak binders be used?
The original OpenBinder[2] had a concept of a 'weak binder'. This would
be useful because I don't want my "service" to retain a reference to the
callback object if that callback object has been deleted by the client.
Weak binders do not appear to be used anywhere in the Android codebase,
and aren't present in the Java APIs. Using them in the C++ APIs appears
more complex than I'd expect if they were in common use[3].
I assume the lack of weak-binder stuff means there's always a better way
to do it in Android? (I believe I can avoid them by ensuring clients
always call "deregisterListener" unless they crash, in which case
linkToDeath should tell me).
4) Can strong binders be converted to weak binders? (Related).
If I write a strong binder, e.g. from the Java client, but then want to
store it as a weak reference on the service side, is there any way to do
this? Can I writeStrongBinder but readWeakBinder from the same parcel?
5) Weak references don't appear to work cross-process. (Related).
If I have an sp<> to the callback object at the client end, but only
wp<>s at the service end, then Binder appears to delete my callback
object. Is this expected behaviour, or should wp<> and sp<> observe
cross-process reference counts? If so, I've probably just made a silly
mistake.
5) Is the IBinder supposed to be the same between multiple transactions?
My service has two APIs, "register listener" and "deregister listener",
which are passed the very same parameter (the callback object) but at
different times. I'd hope that the IBinder address of the parameter
would be identical at the service end as well as the client end[4]. It
doesn't appear to be. Are IBinder pointers only guaranteed to be
identical within the same cluster of recursive calls, or am I doing
something wrong?
This makes it trickier to remove the listener from a list of listeners
at the service end. Is there a normal solution to this? I'd expect the
best way is to return some sort of token which allows the service later
to key into a KeyedVector.
6) Death.
If the client dies, I assume I can clean up if I use linkToDeath. But if
I ever get weak binders working, I'm assuming I wouldn't need to --
would the weak reference simply evaporate and become null if the client
died?
7) Robustness.
These callbacks can't be allowed to affect the primary function of the
service. How can I best ensure that, especially if the client is wedged
and takes an infinitely long time to process the callback? For example,
I am tempted to use IBinder.FLAG_ONEWAY. But what happens in that case
if there are many callbacks? Does Binder keep creating new Binder
threads in the client each time? Or do I eventually get an error
returned from the transact call?
8) Documentation...
Is there any documentation for sp, wp, and the native binder interfaces?
Yes, I know, native development is not supported :-) But if there are
any hints anywhere in the source tree it would be great to know where.
9) Are there good examples of such callbacks?
Is there such a callback anywhere within the Android source on which I
can model things? Especially if a single event might need calls to
multiple fragile listeners, which would help me work out the right way
to maintain the vector of listeners.
Thanks very much indeed for any hints. I hope your answers may be useful
to others who are trying to get their heads round this stuff too!
Regards
Adrian
[1] The event is screen draws, the service is SurfaceFlinger, and the
clients are VNC servers which wish to send the screen image to VNC
viewers. They want to know about the screen draws so they only send
network data when the image has changed, and don't use too much battery
power polling and comparing the screen image all the time. We are fully
aware we have zero chance of persuading you to add these APIs right now,
especially since even the existing screenshotting APIs are closed (cf.
Gerrit 8866), but we want to be prepared in case we ever think of a
cunning way to convince you :-)
[2] http://www.angryredplanet.com/~hackbod/openbinder/ Thanks for
posting that stuff, Dianne!
[3] myParcel.writeWeakBinder(myInterfaceWp.promote()->asBinder()), plus
null checks!
[4] http://developer.android.com/reference/android/os/IBinder.html
1) Callbacks versus blocking calls.
My intention is:
* The service exposes two additional APIs on its existing IInterface:
"register listener" and "unregister listener".
* These take an instance of a new IInterface, which has a single
callback method indicating the event has occurred.
* The service maintains a vector of these registered listeners.
* The service calls a callback on each listener each time the event
occurs.
Is this the normal way to notify clients of events asynchronously?
Or alternatively, the client could make a single blocking call to the
service: "waitUntilEventOccurs". The service would only return from its
onTransact when the event occurred. Is that more normal in a Binder
world?
2) Abandoning blocking transactions.
If I use the "waitUntilEventOccurs" solution: how best should I cancel
an outstanding wait? One way is with another API that somehow signals
the existing blocked Binder thread and causes it to return immediately.
Or is there some means built into Binder?
3) Can weak binders be used?
The original OpenBinder[2] had a concept of a 'weak binder'. This would
be useful because I don't want my "service" to retain a reference to the
callback object if that callback object has been deleted by the client.
I assume the lack of weak-binder stuff means there's always a better way
to do it in Android? (I believe I can avoid them by ensuring clients
always call "deregisterListener" unless they crash, in which case
linkToDeath should tell me).
4) Can strong binders be converted to weak binders? (Related).
If I write a strong binder, e.g. from the Java client, but then want to
store it as a weak reference on the service side, is there any way to do
this? Can I writeStrongBinder but readWeakBinder from the same parcel?
5) Weak references don't appear to work cross-process. (Related).
5) Is the IBinder supposed to be the same between multiple transactions?
My service has two APIs, "register listener" and "deregister listener",
which are passed the very same parameter (the callback object) but at
different times. I'd hope that the IBinder address of the parameter
would be identical at the service end as well as the client end[4]. It
doesn't appear to be. Are IBinder pointers only guaranteed to be
identical within the same cluster of recursive calls, or am I doing
something wrong?
This makes it trickier to remove the listener from a list of listeners
at the service end. Is there a normal solution to this? I'd expect the
best way is to return some sort of token which allows the service later
to key into a KeyedVector.
6) Death.
If the client dies, I assume I can clean up if I use linkToDeath. But if
I ever get weak binders working, I'm assuming I wouldn't need to --
would the weak reference simply evaporate and become null if the client
died?
7) Robustness.
These callbacks can't be allowed to affect the primary function of the
service. How can I best ensure that, especially if the client is wedged
and takes an infinitely long time to process the callback? For example,
I am tempted to use IBinder.FLAG_ONEWAY. But what happens in that case
if there are many callbacks? Does Binder keep creating new Binder
threads in the client each time? Or do I eventually get an error
returned from the transact call?
8) Documentation...
Is there any documentation for sp, wp, and the native binder interfaces?
Yes, I know, native development is not supported :-) But if there are
any hints anywhere in the source tree it would be great to know where.
9) Are there good examples of such callbacks?
Is there such a callback anywhere within the Android source on which I
can model things? Especially if a single event might need calls to
multiple fragile listeners, which would help me work out the right way
to maintain the vector of listeners.
Thanks very much indeed for any hints. I hope your answers may be useful
to others who are trying to get their heads round this stuff too!
Thanks very much for the full answers (and thanks to Dave too). You've
answered everything I needed to know!
You say:
> An IBinder is guaranteed to be a unique identity for an object within
> a process. If the IBinder reference goes around to other processes,
> gets received multiple times, or whatever else, the IBinder address
> you get will always be the same.
For some reason, this wasn't the case when I tried this. The IBinder
address was different each time I passed it across. But I just tried it
again and it worked fine, so I must have been doing something daft :-)
Thanks again,
Adrian
For some reason, this wasn't the case when I tried this. The IBinder
address was different each time I passed it across. But I just tried it
again and it worked fine, so I must have been doing something daft :-)
I remain hopeful some way to do this without compromising security can
be found :-) If your VNC server doesn't require access to the
notification bar part of the screen, maybe the app-specific shared key
thing I mentioned could be applied to both screenshotting and VNC.
> [2]http://www.angryredplanet.com/~hackbod/openbinder/Thanks for
> posting that stuff, Dianne!thanks for the link. You can easily see the evolution of ideas across
This is just an incredibly interesting site for androidologists,
Be, then PalmSource, then Android. The "Looper/Handler/Message" APIs
seem to originate in BeOS. The whole idea of a process-transparent
operating system seems to originate with a never-released OS at Palm.
And now Android takes the intra-process messaging APIs from Be, the
binder/process/services model from Palm and adds the whole Intents/
Activities idea on top.
http://groups.google.com/group/android-developers/browse_frm/thread/279c9484b9c0fdb2/e17a267ca44821a8
but it never got a response. I guess nobody knew the answer.
It is clear now from reading the OpenBinder docs that the Binder is a
lot more than a way to optimize out a few context switches. It is
actually meant to be a whole component architecture in which you never
have to think about processes again, and the system can dynamically
choose how to separate address spaces based on whatever policies it
wants. Android does exactly that. But the Binder isn't quite
transparent, because implementing a remoteable object is still a lot
more work than a local object.
In my own sync app I spent quite a long time separating the meat into
a service so the user could task-switch away and have the sync
continue running in the background rather than die. A bit chunk of the
work was doing the IPC goop .... handling connect/disconnect/callback
registration/handling of dead objects etc.
Is it safe to assume services will always run in the same process as
the activity, if you don't export it via the manifest? If so I'd be
happy to submit a patch documenting this trick, because I think a
common use of the Service API is going to be just to handle multi-
tasking rather than exporting app innards to the sea of components.