3

I want to implement a simple IPC mechanism using Binders in android. For that, I searched on the Internet and found this. I compiled it and it runs fine on my Android device. I tried to take an overall understanding of the program, by searching for each class on AOSP, but everything got more difficult and messed up. Can anyone please explain (just high level), maybe by adding more comments, so that it also helps some future visitors. Here's the code is taken from there:

#define LOG_TAG "binder_demo"

#include <stdlib.h>
#include "utils/RefBase.h"
#include "utils/Log.h"
#include "utils/TextOutput.h"
#include <binder/IInterface.h>
#include <binder/IBinder.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>
using namespace android;
#define INFO(...) \
    do { \
        printf(__VA_ARGS__); \
        printf("\n"); \
        LOGD(__VA_ARGS__); \
    } while(0)
void assert_fail(const char *file, int line, const char *func, const char *expr) {
    INFO("assertion failed at file %s, line %d, function %s:",
            file, line, func);
    INFO("%s", expr);
    abort();
}
#define ASSERT(e) \
    do { \
        if (!(e)) \
        assert_fail(__FILE__, __LINE__, __func__, #e); \
    } while(0)
// Where to print the parcel contents: aout, alog, aerr. alog doesn't seem to work.
#define PLOG aout
// Interface (our AIDL) - Shared by server and client
class IDemo : public IInterface {
    public:
        enum {
            ALERT = IBinder::FIRST_CALL_TRANSACTION,
            PUSH,
            ADD
        };
        // Sends a user-provided value to the service
        virtual void push(int32_t data) = 0;
        // Sends a fixed alert string to the service
        virtual void alert() = 0;
        // Requests the service to perform an addition and return the result
        virtual int32_t add(int32_t v1, int32_t v2) = 0;
        DECLARE_META_INTERFACE(Demo); // Expands to 5 lines below:
        //static const android::String16 descriptor;
        //static android::sp<IDemo> asInterface(const android::sp<android::IBinder>& obj);
        //virtual const android::String16& getInterfaceDescriptor() const;
        //IDemo();
        //virtual ~IDemo();
};
// Client
class BpDemo : public BpInterface<IDemo> {
    public:
        BpDemo(const sp<IBinder>& impl) : BpInterface<IDemo>(impl) {
            LOGD("BpDemo::BpDemo()");
        }
        virtual void push(int32_t push_data) {
            Parcel data, reply;
            data.writeInterfaceToken(IDemo::getInterfaceDescriptor());
            data.writeInt32(push_data);
            aout << "BpDemo::push parcel to be sent:\n";
            data.print(PLOG); endl(PLOG);
            remote()->transact(PUSH, data, &reply);
            aout << "BpDemo::push parcel reply:\n";
            reply.print(PLOG); endl(PLOG);
            LOGD("BpDemo::push(%i)", push_data);
        }
        virtual void alert() {
            Parcel data, reply;
            data.writeInterfaceToken(IDemo::getInterfaceDescriptor());
            data.writeString16(String16("The alert string"));
            remote()->transact(ALERT, data, &reply, IBinder::FLAG_ONEWAY); // asynchronous call
            LOGD("BpDemo::alert()");
        }
        virtual int32_t add(int32_t v1, int32_t v2) {
            Parcel data, reply;
            data.writeInterfaceToken(IDemo::getInterfaceDescriptor());
            data.writeInt32(v1);
            data.writeInt32(v2);
            aout << "BpDemo::add parcel to be sent:\n";
            data.print(PLOG); endl(PLOG);
            remote()->transact(ADD, data, &reply);
            LOGD("BpDemo::add transact reply");
            reply.print(PLOG); endl(PLOG);
            int32_t res;
            status_t status = reply.readInt32(&res);
            LOGD("BpDemo::add(%i, %i) = %i (status: %i)", v1, v2, res, status);
            return res;
        }
};
//IMPLEMENT_META_INTERFACE(Demo, "Demo");
// Macro above expands to code below. Doing it by hand so we can log ctor and destructor calls.
const android::String16 IDemo::descriptor("Demo");
const android::String16& IDemo::getInterfaceDescriptor() const {
    return IDemo::descriptor;
}
android::sp<IDemo> IDemo::asInterface(const android::sp<android::IBinder>& obj) {
    android::sp<IDemo> intr;
    if (obj != NULL) {
        intr = static_cast<IDemo*>(obj->queryLocalInterface(IDemo::descriptor).get());
        if (intr == NULL) {
            intr = new BpDemo(obj);
        }
    }
    return intr;
}
IDemo::IDemo() { LOGD("IDemo::IDemo()"); }
IDemo::~IDemo() { LOGD("IDemo::~IDemo()"); }
// End of macro expansion
// Server
class BnDemo : public BnInterface<IDemo> {
    virtual status_t onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0);
};
status_t BnDemo::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
    LOGD("BnDemo::onTransact(%i) %i", code, flags);
    data.checkInterface(this);
    data.print(PLOG); endl(PLOG);
    switch(code) {
        case ALERT: {
                        alert(); // Ignoring the fixed alert string
                        return NO_ERROR;
                    } break;
        case PUSH: {
                       int32_t inData = data.readInt32();
                       LOGD("BnDemo::onTransact got %i", inData);
                       push(inData);
                       ASSERT(reply != 0);
                       reply->print(PLOG); endl(PLOG);
                       return NO_ERROR;
                   } break;
        case ADD: {
                      int32_t inV1 = data.readInt32();
                      int32_t inV2 = data.readInt32();
                      int32_t sum = add(inV1, inV2);
                      LOGD("BnDemo::onTransact add(%i, %i) = %i", inV1, inV2, sum);
                      ASSERT(reply != 0);
                      reply->print(PLOG); endl(PLOG);
                      reply->writeInt32(sum);
                      return NO_ERROR;
                  } break;
        default:
                  return BBinder::onTransact(code, data, reply, flags);
    }
}
class Demo : public BnDemo {
    virtual void push(int32_t data) {
        INFO("Demo::push(%i)", data);
    }
    virtual void alert() {
        INFO("Demo::alert()");
    }
    virtual int32_t add(int32_t v1, int32_t v2) {
        INFO("Demo::add(%i, %i)", v1, v2);
        return v1 + v2;
    }
};
// Helper function to get a hold of the "Demo" service.
sp<IDemo> getDemoServ() {
    sp<IServiceManager> sm = defaultServiceManager();
    ASSERT(sm != 0);
    sp<IBinder> binder = sm->getService(String16("Demo"));
    // TODO: If the "Demo" service is not running, getService times out and binder == 0.
    ASSERT(binder != 0);
    sp<IDemo> demo = interface_cast<IDemo>(binder);
    ASSERT(demo != 0);
    return demo;
}
int main(int argc, char **argv) {
    if (argc == 1) {
        LOGD("We're the service");
        defaultServiceManager()->addService(String16("Demo"), new Demo());
        android::ProcessState::self()->startThreadPool();
        LOGD("Demo service is now ready");
        IPCThreadState::self()->joinThreadPool();
        LOGD("Demo service thread joined");
    } else if (argc == 2) {
        INFO("We're the client: %s", argv[1]);
        int v = atoi(argv[1]);
        sp<IDemo> demo = getDemoServ();
        demo->alert();
        demo->push(v);
        const int32_t adder = 5;
        int32_t sum = demo->add(v, adder);
        LOGD("Addition result: %i + %i = %i", v, adder, sum);
    }
    return 0;
}
aminography
  • 18,195
  • 11
  • 51
  • 57
Insane Coder
  • 6,859
  • 11
  • 62
  • 140
  • in java it is `android.os.IBinder` interface that defines how to deal with remotable objects, the only implementation is `android.os.Binder` and the typical situation is that a `Service` returns a custom `android.os.Binder` which overrides `onTransact` method and the client simply calls `android.os.Binder#transact` method – pskink Nov 04 '15 at 13:24
  • if you need the "hello world" Binder example just let me know and i will try to find something – pskink Nov 04 '15 at 14:41
  • @pskink : Thanks for the help. I have understood the above example to a great extent. If you can provide one more such example, like "hello world" as you said, then that would definitely be helpful. – Insane Coder Nov 05 '15 at 07:30
  • it will be in java, not c++, but it think you want to know high level, not the implementation / language details? – pskink Nov 05 '15 at 07:33
  • @pskink I am trying to send a custom Binder from one Activity to the next but having problem. I send the binder in a parcelable in the intent extra. Then I get Parcelable extra. casting the binder to my custom binder type triggers casting exception. I then try sending a android.os.Binder the same way. Casting to android.os.Binder triggers same Exception. I then test instanceof to find out it is just instanceof IBinder. using transact method never calls my custom onTransact method. I however notice that Messenger is able to send interprocess messages. How can I make my binder work. –  Mar 18 '20 at 17:53
  • @JaveneCPPMcGowan so what class is it? transact/onTransact is the only way to use it and *EVERY* code that uses IPC do that, even `Messsenger` - you can check the sources – pskink Mar 18 '20 at 18:01
  • Check what sources? I need to know why my custom onTransact method is not being called. I call transact in one process, I dont get the ontrasact call in the other process. Do I need to do some special initialization? I just want to use my binder straight and forget about AIDL and Messenger. –  Mar 19 '20 at 00:09
  • @JaveneCPPMcGowan so post your code - the minimal (not) working example – pskink Mar 19 '20 at 05:09
  • @JaveneCPPMcGowan for example did you try the minimal version of "bound" service that returns `MyBinder` from its `onBind(Intent intent)` method (where `MyBinder` extends `'Binder` and overrides `onTransact` method)? – pskink Mar 19 '20 at 05:35
  • @pskink 1. Where am I going to post my code for you to read it?2. Where is the minimal version of bound service to be found? –  Mar 19 '20 at 06:05
  • https://paste.ubuntu.com/ set expiration to 1 day for example, the minimal is to return `MyBinder` from the service side and do call `bindService` and `transact` from the client side - you call `transact` from `ServiceConnection.onServiceConnected` callback – pskink Mar 19 '20 at 06:09
  • @pskink note I copied the generated aidl Stub class and used it as a template to build my binder interface. I thought I would finally get through but still no success. Note that calling ontransact in the same process works and onTransact gets called. But calling transact from a different process does nothing and transact RETURNS FALSE; –  Mar 19 '20 at 06:09
  • @JaveneCPPMcGowan you passed null `Parcel data`? if so, dont do that (afair `Parcel reply` can be null thoufgh...) – pskink Mar 19 '20 at 06:11
  • @pskink no. I used Parcel.obtain() for both data and reply –  Mar 19 '20 at 06:13
  • @pskink can I send the code to your email? –  Mar 19 '20 at 06:15
  • @JaveneCPPMcGowan see https://stackoverflow.com/a/33688843/2252830 - and remove everything related to `ParcelFileDescriptor` / `FileDescriptor` – pskink Mar 19 '20 at 06:17
  • @JaveneCPPMcGowan and of course to be sure that any data is passed use for example `data.writeString("hello world")` and `data.readString()` on the other side – pskink Mar 19 '20 at 06:37
  • @pskink thanks for the link. I hope I get through. –  Mar 19 '20 at 07:25

1 Answers1

0

I know this is a bit late but checkout this amazing explanation by Gabriel Burca on Android IPC mechanism here. You can find a working example with a very simple C++ code from the same author here. Further it has clear instructions how to compile and add it to your AOSP source. Cheers!