Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Send custom object using Android native Binder

I can only find examples for Java regarding Parcelable. My goal is to create a simple service and client in native C++ ( not NDK ), that will use Binder to receive and send serialized custom objects.

MyClass
+ std::string
+ enum
+ int
+ bool
like image 255
Cumatru Avatar asked Apr 14 '16 08:04

Cumatru


1 Answers

Since you're using Android 5.1 I'll stick to what's available on Lollipop. There are better tools like aidl-cpp to accomplish this which saves a lot of the hassle of hand-rolling boiler-plate codes.

First, craft the Interface for the service in IFooService.h.

class IFooService: public IInterface {
    public:
        DECLARE_META_INTERFACE(FooService);
        virtual int myFunc(const String8& str, int enumParam, int intParam, int boolParam) = 0;

};

Then write the marshalling code for the service, implementing a function as per the requirements in the question

class BnFooService: public BnInterface<IFooService>
{
public:
    virtual status_t    onTransact( uint32_t code,
                                    const Parcel& data,
                                    Parcel* reply,
                                    uint32_t flags = 0);
};

enum {
    MY_FUNC = IBinder::FIRST_CALL_TRANSACTION,
};

/* BinderProxy code, for clients of the service to call into the service and have
   the paremeters marshalled properly across the Binder interface. Executes in the
   client process context
 
*/
class BpFooService: public BpInterface<IFooService> {
public:
    virtual int myFunc(const String8& str, int enumParam, int intParam, int boolParam) {
        //2 Parcel objects, 1 to write the data to and another to receive it in
        Parcel data, reply;
        //This serves as the token for the Binder framework for a sanity check
        data.writeInterfaceToken(IFooService::getInterfaceDescriptor());
        data.writeString8(str);
        data.writeInt32(enumParam);
        data.writeInt32(intParam);
        data.writeInt32(boolParam);
        return remote()->transact(MY_FUNC, data, &reply);
    }
}

IMPLEMENT_META_INTERFACE(MediaPlayerService, "com.somedomain.IFooService");

/* Actual Binder implementation of the service which unpacks the data from the
   incoming Parcel and handles the call accordingly     
*/
status_t BnFooService::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) {
    switch (code) {
        case MY_FUNC: {
            //The same token is checked
            CHECK_INTERFACE(IFooService, data, reply);
            //Read off in exact same order as written to the data Parcel above
            String8 str = data.readString8();
            int enumParam = data.readInt32();
            int intParam = data.readInt32();
            int boolParam = data.readInt32();
            reply->writeInt32(myFunc(str, enumParam, intParam, boolParam));
            return NO_ERROR;
        }
        break;
    default:
        
}

Then create the main_foo.cpp with some code as follows which serves as the entry point for the service process:

int main() {
    if(!FooService::publish()) { //Call into your code to register with ServiceManager
        ALOGE("Can't instantiate FooService! Exiting");
        return 1;
    }
    ProcessState::self()->startThreadPool(); //Required for the binder threads
    IPCThreadState::self()->joinThreadPool(); //Infinite loop that waits for inbound connections
}

And, finally, implement the interface for the service in FooService.cpp

bool FooService::publish() {
    defaultServiceManager()->addService(String16("foo_service"), new FooService());
    sp<IBinder> binder = defaultServiceManager()->checkService(String16("foo_service"));
    while(binder == NULL && /*some upper time limit*/){
        ALOGI("Waiting for foo_service");
    }
    return binder != NULL;
}

int FooService::myFunc(const String8& str, int enumParam, int intParam, int boolParam) {
    //business logic
    return 0;
}

Please note that this is not code that's meant for regular application developers but instead for native services running in the Android Framework like InputFlinger, SurfaceFlinger etc. This code requires Framework Header files like Parcel.h, IInterface.h etc. which aren't available in an SDK.

like image 75
Zoso Avatar answered Nov 14 '22 20:11

Zoso