Android Interprocess Communication (IPC) with AIDL

Two processes cannot share memory and communicate with each other directly. So to communicate, objects have to be decomposed into primitives (marshalling) and transfered across process boundaries. To do this marshalling, one has to write a lot of complicated code, hence Android handles it for us with AIDL (Android Interface Definition Language). You should read the previous article on the binder framework to get some more idea.

So AIDL is sort of a communication contract. It defines a set of methods that both the client and service agree upon and hence the client can call those methods on the service as if it were all happening locally. The AIDL interface code has to be written in a file with an .aidl extension. Once it’s written, the Android SDK tools generates Java code out of the .aidl files and store it in the project’s directory that contains all the generates files like R for instance. For example in my case (Android Studio) it was created in build/generated/source/aidl/debug/com.example.app.aidl/GeneratedFile.java. In order to communicate, applications just need to be aware of the interface, i.e., methods available, not what’s generated into the file. The server application as well as all the client applications will need to have this .aidl file in order to communicate.

What's the one thing every developer wants? More screens! Enhance your coding experience with an external monitor to increase screen real estate.

In the generated interface file, there are two main classes called Proxy (for the client side) and Stub (for the server/service side). They handle sending and handling transactions, as well as marshalling and demarshalling of data. So actually the client proxy and server stub manages the transactions (RPC calls) on behalf of the client component and service.

At this point, you should definitely go through the documentation that covers all the details regarding how to:

  • Write an AIDL interface (and where is it generated).
  • Implement the interface.
  • Expose the interface to clients.
  • Bind/Connect to the Service and start communicating.

I don’t see a point in rehashing these sections as the documentation does a good job at explaining them with simplicity. So do go through them and you’ll learn about implementing Synchronous RPC.

Asynchronous RPC

By default, the RPC calls from the client are synchronous. This means if the call is invoked on the UI thread, then it’ll block the thread impacting the application’s responsiveness. To avoid this you can execute all remote calls on worker threads but then it still doesn’t solve the issue of memory leaks that could be caused because of the fact that the client worker thread and all of its object references are kept alive as the server thread is blocked.

To solve this problem, we can implement asynchronous RPC which is basically calling a method from the client and returning immediately and then when the server process is done with its work, it calls back a method of the client process. When the client initiates a transaction and returns immediately, the binder gives the transaction to the server process and the connection from the client to server is closed.

Asynchronous AIDL interface is defined with the oneway keyword either at the interface level or on individual methods:

oneway interface IAsyncInterface {
    void methodOne();
    void methodTwo();
}

/*
or

interface IAsyncInterface {
    oneway void methodOne();
    void methodTwo();
}
*/

Note: Asynchronous methods must not have out and inout arguments. They must also return void.

Now based on the AIDL interface that you see above, if you want the service to send back results, then you’ll need to define a callback interface in the method calls like this for example:

oneway interface IAsyncInterface {
    void methodOne(IAsyncCallback callback);
    void methodTwo(IAsyncCallback callback);
}

Then you create another AIDL file that declares the callback interface implemented in the client process:

interface IAsyncCallback {
    void handleResponse(String name);
}

So based on these two AIDL interfaces, this is how you’ll implement the first in in your Server:

IAsyncInterface.Stub mBinder = new IAsyncInterface.Stub() {
    public void methodOne(IAsyncCallback callback) throws RemoteException {
        callback.handleResponse("methodOne");
    }
    
    public void methodTwo(IAsyncCallback callback) throws RemoteException {
        callback.handleResponse("methodTwo");
    }
};

and the second AIDL (callback interface) is implemented like this in the client:

IAsyncCallback.Stub mCallback = new IAsyncCallback.Stub() {
  public void handleResponse(String name) throws RemoteException {
    Log.d(TAG, name);
  }
}

So calling methodOne() and/or methodTwo() from the client after binding will send a transaction and return instantly. Then later when the transaction is received by the service and processed there, the mCallback.handleResponse() is called with the result that is simply logged.

in, out, inout

Unlike Java there are three directional tags that can be specified in the method prototypes when defining an AIDL interface. These tags indicate which way the data will go. Let’s see an example first:

package com.pycitup.pyc.aidl;

import com.example.app.Bar;

interface IBoundService {
    void getAll(out 
    void save(inout Bar bar);
    void delete(in Bar bar);
}
  • in means that the object is transfered from client to service and only used for inputs. If any changes are made to the bar object in the service then they won’t reflect in the client.
  • out indicates that the object has no relevant data and will be populated by the service and returned as a response.
  • inout means that the data gets copied, i.e., if any changes are made to bar in the service then that’ll also reflect in the client’s bar object.

When to use AIDL

It is generally not recommended to use AIDL (or use only when you definitely know what you’re doing) to create a bound service as thread safety has to be ensured and multi threading capabilities have to be taken care of. This makes it very complicated to implement and you’ve to be really careful. Use AIDL when clients from different applications needs to talk to your service on multiple threads via IPC. If concurrent IPC across processes are not required then consider using simple bound services where you create a Binder (by implementing the Binder class) object and pass on to the client that can execute methods on the Service. If you want to perform IPC but don’t need multi threading then consider using a Messenger.

Just make sure you know what you’re doing when using AIDL and are sure that you definitely need to utilize it.

Conclusion

Binder is like one of the most important components of Android on which almost all IPC relies. In this article you learnt how to implement Synchronous RPC with it (documentation link) as well as Asynchronous RPC. Make sure you’ve read the previous article that gives you an overview of Binders and also links to an excellent video talk that divers deeper into the concept.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download

Author: Rishabh

Rishabh is a full stack web and mobile developer from India. Follow me on Twitter.

Leave a Reply

Your email address will not be published. Required fields are marked *