Binder framework takes an object-oriented approach in its design principle and every entity that is capable of serving inter-process calls is a user-space object. In native user space the IBinder
is the base class of all kinds of Binder objects i.e. proxy objects and native objects. All native Binder objects inherit from BBinder
and all proxy Binder object inherit from BpBinder
. The IBinder
interface inherits from RefBase
which enables object reference counting.
Bn* and Bp*
What do Bn and Bp stand for in IInterface.h ?
We refer to a Binder object as local or native if the object is created in the current process. This kind of object is called Bn
, which is short for Binder native. We refer a Binder object as remote if the object is created in another process. The handle we use to call into a remote Binder object is called a Proxy. This kind of object is called Bp
object which is short for Binder proxy. The terms proxy comes from the fact that this kind of objects just delegate the calls to Bn side.
So “n” is native, that is the class you inherit from to implement the interface; “p” is proxy, that is the class that is created to perform interface calls through IPC.
IBinder Class
A class who inherits from IBinder
gains the capability of crossing process boundaries. The most important interface method is transact
which triggers a cross process transaction.
virtual bool isBinderAlive() const = 0; virtual status_t pingBinder() = 0; virtual status_t transact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0) = 0; // Register the recipient for a notification if this binder goes away. virtual status_t linkToDeath(const sp<DeathRecipient>& recipient, void* cookie = nullptr, uint32_t flags = 0) = 0; // Remove a previously registered death notification. virtual status_t unlinkToDeath( const wp<DeathRecipient>& recipient, void* cookie = nullptr, uint32_t flags = 0, wp<DeathRecipient>* outRecipient = nullptr) = 0; virtual BBinder* localBinder(); virtual BpBinder* remoteBinder();
There are four parameters to this method:
- The code argument is the transaction code of this transaction.
- The data argument is the input data for this transaction
- reply is the output data.
- The most important bit mask for flags argument is FLAG_ONEWAY by setting which you make this transaction asynchronous. If it is set then reply will be null and the caller process won’t be waiting for the target process to send a reply.
The isBinderAlive
and pingBinder
methods check whether the target native Binder object is still alive. Use linkToDeath
to register a death listener of the target native object and use unlinkToDeath
to remove it.
Use localBinder
and remoteBinder
to cast this object to a native object and proxy object respectively. For BpBinder
, localBinder
returns null and remoteBinder
returns itself. Similarly for BBinder
, localBinder
returns itself and remoteBinder
returns null.
BpBinder and BBinder
IBinder
defines the abstract interface for a binder object; BpBinder
(Proxy Binder) and BBinder
(Buffered Binder) are the concrete implementation in proxy and server side, respectively.
IBinder | BpBinder | BBinder |
---|---|---|
constructor | BpBinder(int32_t handle) | Nothing special |
transact(code,data,reply,flag) | call IPCThreadState::transact(mHandle) | call onTransact() overrided in subclass |
linkToDeath | valid | N/A |
localBinder(): BBinder * | return NULL | return this |
remoteBinder():BpBinder * | return this | return NULL |
queryLocalInterface | return NULL | return this |
That constructor of BpBinder takes an integer handle, which is created by binder driver. When calling BpBinder::transact(), that handle will be passed to the binder driver and used by the driver to locate the transaction’s target process and target BBinder object. Once the BBinder object is found, binder driver will wake up one of the target process’s binder threads and invoke the BBinder’s transact() method
That constructor of BpBinder
takes an integer handle, which is created by binder driver. When calling BpBinder::transact()
, that handle will be passed to the binder driver and used by the driver to locate the transaction’s target process and target BBinder object. Once the BBinder object is found, binder driver will wake up one of the target process’s binder threads and invoke the BBinder’s transact()
method
BBinder
BBinder is the base class of all native Binder objects. It is the class that implements the actual transaction business logic. Subclasses of BBinder should override onTransact to implement the actual transaction logic.
// Snippet from https://android.googlesource.com/platform/frameworks/native/+/master/libs/binder/include/binder/Binder.h class BBinder : public IBinder { public: BBinder(); virtual const String16& getInterfaceDescriptor() const; virtual bool isBinderAlive() const; virtual status_t pingBinder(); virtual status_t transact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0) final; virtual status_t linkToDeath(const sp<DeathRecipient>& recipient, void* cookie = nullptr, uint32_t flags = 0); // NOLINTNEXTLINE(google-default-arguments) virtual status_t unlinkToDeath( const wp<DeathRecipient>& recipient, void* cookie = nullptr, uint32_t flags = 0, wp<DeathRecipient>* outRecipient = nullptr); virtual void* attachObject(const void* objectID, void* object, void* cleanupCookie, object_cleanup_func func) final; virtual void* findObject(const void* objectID) const final; virtual void* detachObject(const void* objectID) final; void withLock(const std::function<void()>& doWithLock); virtual BBinder* localBinder(); }
BpBinder
BpBinder is the base class of all proxy Binder objects. It is a reference to a native Binder objects running in another process. Proxy objects delegate the call to the underlying BBinder in a remote process with the help of Binder driver.
// Code snippet from https://android.googlesource.com/platform/frameworks/native/+/master/libs/binder/include/binder/BpBinder. class BpBinder : public IBinder { public: /** * Return value: * true - this is associated with a socket RpcSession * false - (usual) binder over e.g. /dev/binder */ bool isRpcBinder() const; virtual const String16& getInterfaceDescriptor() const; virtual bool isBinderAlive() const; virtual status_t pingBinder(); virtual status_t transact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0) final; virtual status_t linkToDeath(const sp<DeathRecipient>& recipient, void* cookie = nullptr, uint32_t flags = 0); virtual status_t unlinkToDeath( const wp<DeathRecipient>& recipient, void* cookie = nullptr, uint32_t flags = 0, wp<DeathRecipient>* outRecipient = nullptr); virtual void* attachObject(const void* objectID, void* object, void* cleanupCookie, object_cleanup_func func) final; virtual void* findObject(const void* objectID) const final; virtual void* detachObject(const void* objectID) final; virtual BpBinder* remoteBinder(); void sendObituary(); static uint32_t getBinderProxyCount(uint32_t uid); static void setLimitCallback(binder_proxy_limit_callback cb); static void setBinderProxyCountWatermarks(int high, int low); private: Handle mHandle; struct Obituary { wp<DeathRecipient> recipient; void* cookie; uint32_t flags; }; void reportOneDeath(const Obituary& obit); bool isDescriptorCached() const; volatile int32_t mAlive; Vector<Obituary>* mObituaries; }
The Obituary
class just wraps around DeathRecipient and it represents a listener for remote BBinder
death and mObituaries
stores a list of such listeners. mAlive
and mObitsSent
are used to indicate whether the remote BBinder has died. The reportOneDeath
basically nvokes the death callbacks on a DeathRecipient.
Example
Here are the four steps needed to set up the binder based client/server architectures.
- Define IAdd by subclassing IInterface
- Subclass BpInterface with BpAdd, and implement interface method add
- Marshall the request data
- Call remote()->transact(code,data,reply) to send the transaction to bind driver
- Wait and unmarshall the reply
- Subclass BnInterace with BnAdd, and implement onTransact()
- Dispatching based on transaction code
- Call the real implementation in the subclass of this class – see step 4
- Marshall the result into reply, which will be sent to driver and be received in the BpAdd
- Subclass BnAdd with AddImpl, which provide the real implementation.
// BpAdd class BpAdd : public BpInterface {}; int BpAdd::add(int a, int b) { Parcel data, reply; // 2.a. data.writeInterfaceToken(IAdd::getInterfaceDescriptor()); data.writeInt32(a); data.writeInt32(b); // 2.b if (remote()->transact(ADD, data, &reply) == NO_ERROR) { // 2.c int result = reply.readInt32(); return result; } // on error, for simplicity, assume -1 means error. return -1; } // class BnAdd class BnAdd : public BnInterface {}; status_t BnAdd::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { switch(code) { // 3.a case ADD: { CHECK_INTERFACE(IAdd, data, reply); int a = data.readInt32(); int b = data.readInt32(); // 3.b int result = add(a, b); //implemented in AddImpl // 3.c reply->writeInt32(result); return NO_ERROR; } break; default: return BBinder::onTransact(code, data, reply, flags); } } // class AddImpl class AddImpl : public BnAdd {}; int AddImpl::add(int a, int b) { return a + b; }