Inter-Process Communication in Android - Lessons & Learnings- 15 mins
Recently, I was tasked with building a solution for OTA (Over-the-Air) updates for one of our products at SodaLabs. Now for those of you who are not familiar with the concept, here’s a definition by TechTarget:
An over-the-air update is the wireless delivery of new software or data to mobile devices. Wireless carriers and original equipment manufacturers (OEMs) typically use over-the-air (OTA) updates to deploy firmware and configure phones for use on their networks. The initialization of a newly purchased phone, for example, requires an over-the-air update. With the rise of smartphones, tablets and internet of things (IoT) devices, carriers and manufacturers have also turned to over-the-air updates for deploying new operating systems (OSs) to these devices.
Basically, the updates you receive for your Android, iOS, Windows, Mac or Linux devices are distributed through the mechanism known as OTA programming.
Going the Sandbox Way
We had a couple of choices around how to build this OTA client, but in the end we decided to build it as a completely separate project. The reason was that both the main product and OTA client were equally complex solutions and we didn’t want to make it more complex by adding the OTA as part of the main project.
The OTA would essentially be just a service that runs as a background process and communicate with the main application (the product whose updates this OTA was supposed to manage) via AIDL i.e. the IPC mechanism used by Android.
This was the first time I was going to be working with IPC in general. So unsurprisingly, there was a lot of frustration, learning and fixing through trial and error involved.
My goal in this post is to write about the basics and share some insights from my experience so that you can avoid the confusions that I had to face.
Let’s start by understand what Inter-Process communication is. Inter-Process Communication or IPC in short, is a mechanism that allows multiple independent processes to communicate and exchange data.
This communication is often achieved through the use of some shared interfaces defined through Interface Description Language (IDL). What it does is basically establish a set of rules that all the interested processes follow. These shared interfaces serves as a common language that all the parties use to communicate with each other.
How IPC Works In Android
All the applications in Android runs in its own process in a sandbox environment. Which means that each application is independent of others. This isolation helps improve the performance of overall system and in achieving better security through different levels of permissions and access privileges.
Android has its own version of IDL called Android Interface Definition Language (AIDL), which has very much a Java-esque syntax. You basically start by defining an interface (or multiple based on your use case) and then implement the interface in your application and in the client application that wishes to communicate our app.
Understanding The Architecture
One important thing to understand is that IPC communication in Android is also client-server based. Which means that one application has to act as the server while others can interact with it using the interface it exposes.
Generally that Server application is the one you are writing and which you wish to be controlled from outside sources. Following are the components and steps involved:
- An AIDL interface: You define an interface in your server application containing the API that you want to expose to your clients.
Implementation of AIDL interface: Rebuilding your project will generate a java interface from your AIDL file, which you can then use to write a concrete implementation of it.
Expose the interface to the clients: Now that you have a concrete implementation of your AIDL interface, it is time to expose it to your clients by binding it with a Service. This Service can be invoked by external applications and then used a communication channel.
Exchanging data via IPC: On Android, one process cannot normally access the memory of another process. So to talk, they need to decompose their objects into primitives that the operating system can understand, and marshall the objects across that boundary for you. This means that you can only exchange primitive type data through IPC in the most basic from.
- Writing the parcelable implementations: Since in most real-world applications you cannot live off just the primitives. Android also provides a mechanism to exchange your Objects through IPC with the use of Parcelables.
These are the 5 main steps required to implement the IPC capabilities in an application. However, admittedly they are not simple and bound to give you a lot of trouble. Let’s look at the implementation now.
Off To The Code
Let’s build a very basic application that let’s you perform addition. To demonstrate the use of data exchange with both primitives and objects, instead of returning a simple integer value for result, we will return a made-up
Start by creating a simple project with two modules, one serving as a Client while the other as the Server. You can find the link to the complete code at the end of post.
The AIDL Interface
Now that you have a basic setup, let’s first define our very simple AIDL interface in which we want to expose a single method called
performAddition which takes 2 integer values and returns a made-up
Now as I mentioned above, since we are using a custom object, we need to define a simple AIDL implementation for it as well.
Build the project and if no errors occur, then you should be able to find a java implementation of of your AIDL interface.
Once successful, make sure to copy both of your AIDL files to the client project as well.
Implementation Of The AIDL Interface
Next step is to provide concrete implementation of the AIDL interfaces. For the
Result object, all you need is a
parcelable POJO for it. However! You need to write it the old school java way.
I know you love Kotlin and must be thinking of using
parcelize or at least take advantage of the simpler Kotlin code for data classes, but for some reason Kotlin based parcelables don’t work at the time of this writing.
So here’s your implementation for
Result class in pure java style:
Make sure to copy this class into client’s source code as well since it is needed by client to interpret the
Anyways, let’s now provide the implementation for our
IAddition AIDL interface in the server application:
While we are at it, let’s also register this service in our
manifest file. With that our server application will be ready:
Hooking The Service To The Client
Last step is to bind the
AdditionService in the server application to the client app and starting it remotely. I have a very basic activity with the following UI for client app:
Let’s implement the activity now:
If all goes well, you should be able to compute your additions through the server app using AIDL interfaces.
Things To Remember
In the end, as you may have realized that this mechanism is not very robust and prone to error. So here are few tips from what I learnt working with Android’s IPC about the things that could easily go wrong and make you waste a lot of time.
Be very careful about the intent that you use to trigger the service, a minor typo will lead to service never starting.
On the same note, if you face a situation at the start where you are not getting the results, verify your service bindings first before diving into debugging. It could just be a faulty service binding and will save you a lot of effort.
Remember that ONLY primitive types are allowed, so always make sure that you are not accidentally accepting or returning a non-primitive object.
One of the biggest pain was with threading, I never realized that thread would affect IPC as well. Basically, calls made from the local process are executed in the same thread that is making the call. If this is your main UI thread, that thread continues to execute in the AIDL interface. So be very careful if you are using RxJava or any other async mechanism.
Remember, Kotlin classes does not work for parcelable implementations with AIDL. At least not at the time of this writing, and what’s worse is that it does not give you any error.
- This awesome post by DevArea.
- Detailed guide about IPC in Android on Official Android Resources.
- Also, this Medium Article.
You can find complete source code for this post here
For suggestions and queries, just contact me.