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.

IBinderBpBinderBBinder
constructorBpBinder(int32_t handle)Nothing special
transact(code,data,reply,flag)call IPCThreadState::transact(mHandle)call onTransact() overrided in subclass
linkToDeathvalidN/A
localBinder(): BBinder *return NULLreturn this
remoteBinder():BpBinder *return thisreturn NULL
queryLocalInterfacereturn NULLreturn this
Important methods of IBinder interface, and their corresponding implementation in BpBinder and BBinder

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.

  1. Define IAdd by subclassing IInterface
  2. Subclass BpInterface with BpAdd, and implement interface method add
    1. Marshall the request data
    2. Call remote()->transact(code,data,reply) to send the transaction to bind driver
    3. Wait and unmarshall the reply
  1. Subclass BnInterace with BnAdd, and implement onTransact()
    1. Dispatching based on transaction code
    2. Call the real implementation in the subclass of this class – see step 4
    3. Marshall the result into reply, which will be sent to driver and be received in the BpAdd
  2. 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;
}