ANDROID: Android Interface Definition Language (AIDL)

AIDL (Android Interface Definition Language) is Android's mechanism for inter-process communication. It allows one application to expose a type-safe interface that other applications — running in separate processes — can call as if calling local methods, using a client-server model built on top of Android's Binder IPC.

Android's security model enforces strict application sandboxing — each app runs in its own process with its own memory space. By design, one application cannot read or write another application's memory directly. This isolation is fundamental to Android security.

But sometimes, two applications genuinely need to talk to each other. A music player service might need to expose playback controls to a companion app. A sensor management service might need to provide real-time data to multiple client apps. A system service might need to offer a structured API to third-party apps.

This is the problem that AIDL solves.


What is AIDL?

AIDL stands for Android Interface Definition Language. It is a lightweight IPC (Inter-Process Communication) mechanism that allows an Android application to expose a structured, type-safe interface to clients running in different processes.

md
+-------------------------+     Binder IPC     +-------------------------+
|    Client App           |<------------------>|    Server App           |
|    (Process A)          |                    |    (Process B)          |
|                         |                    |                         |
|   myService.getData(1)  |----[marshalling]--->|   getData() handler     |
|                         |                    |                         |
|   result = "Data"       |<--[unmarshalling]--|   return "Data for 1"   |
+-------------------------+                    +-------------------------+

AIDL uses syntax similar to Java and Kotlin to define the interface. The Android build system auto-generates the boilerplate code — the Stub (server side) and the Proxy (client side) — that handles the marshalling and unmarshalling of method calls across process boundaries.


How AIDL Works: The Big Picture

Before writing any code, it helps to understand the overall architecture:

md
Server Side                          Client Side
-----------                          -----------
1. Define AIDL interface             1. Copy or share the AIDL file
2. Build project (generates Stub)    2. Bind to the service
3. Implement the Stub                3. Get Proxy via asInterface()
4. Return Stub from onBind()         4. Call methods on Proxy
5. Declare service in Manifest       5. Results return across IPC

The Stub lives on the server. It receives raw IPC data and dispatches it to your implementation.

The Proxy lives on the client. It takes your method calls and serializes them for IPC.

Both are generated automatically — you only implement the actual logic.


Creating an AIDL Interface: Server Side

Step 1: Define the AIDL Interface

In your Android project, under src/main, create an aidl directory at the same level as the java directory. Inside it, create a package structure matching your app's package name (e.g., com.example.myapp), then create an .aidl file:

java
// IMyService.aidl
package com.example.myapp;

interface IMyService {
    String getData(int id);
    void setData(int id, String data);
}

AIDL syntax is intentionally Java-like. You declare methods in the interface, and AIDL handles the serialization. Supported parameter types include:

  • Java primitives (int, long, boolean, float, double, byte, char)
  • String and CharSequence
  • List and Map (with elements of supported types)
  • Custom Parcelable objects (requires a separate .aidl declaration)

Step 2: Build the Project

When you build, Android Studio generates a Java interface from the .aidl file. This generated code (in the gen directory) contains the Stub abstract class (for your service to extend) and the Proxy class (for clients to use). You never edit this generated code directly.

Step 3: Implement the Bound Service

Create a service class that implements the generated Stub:

java
public class MyService extends Service {

    // Implement the AIDL interface using the generated Stub
    private final IMyService.Stub binder = new IMyService.Stub() {

        @Override
        public String getData(int id) throws RemoteException {
            return "Data for ID: " + id;
        }

        @Override
        public void setData(int id, String data) throws RemoteException {
            // Store or process the data
        }
    };

    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }
}

The binder object is your implementation. When a client calls getData(1) across processes, AIDL routes the call through Binder IPC to this method.

Step 4: Declare the Service in the Manifest

Your service must be declared in AndroidManifest.xml with an intent-filter so clients can discover and bind to it:

xml
<service
    android:name=".MyService"
    android:enabled="true"
    android:exported="true">
    <intent-filter>
        <action android:name="com.example.myapp.IMyService" />
    </intent-filter>
</service>

The android:exported="true" attribute is what allows clients from other apps to bind to this service. Without it, only components within the same app can bind.


Binding to the Service: Client Side

Step 1: Bind to the Service

In the client application (a different app or a different process), use bindService() with a ServiceConnection to establish the IPC connection:

java
private IMyService myService = null;

private ServiceConnection serviceConnection = new ServiceConnection() {

    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        // Convert the raw IBinder to the typed AIDL interface
        myService = IMyService.Stub.asInterface(service);
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        myService = null;
    }
};

private void bindToService() {
    Intent intent = new Intent();
    intent.setAction("com.example.myapp.IMyService");
    intent.setPackage("com.example.myapp");
    bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}

The critical line is IMyService.Stub.asInterface(service). This converts the raw IBinder returned by the service's onBind() into the typed IMyService proxy interface, handling whether the call is in-process (returns the Stub directly) or cross-process (wraps it in a Proxy).

Step 2: Make Remote Calls

Once bound (onServiceConnected has been called), you interact with the remote service exactly as if calling local methods — except that you must catch RemoteException:

java
if (myService != null) {
    try {
        String result = myService.getData(1);
        myService.setData(1, "Updated Data");
    } catch (RemoteException e) {
        e.printStackTrace();
    }
}

RemoteException is thrown when the remote process has died or the connection has been lost. Always handle it gracefully.

Step 3: Unbind When Done

Unbind from the service when your component is destroyed to prevent resource leaks:

java
@Override
protected void onDestroy() {
    super.onDestroy();
    unbindService(serviceConnection);
}


Full Implementation Flow

md
SERVER SIDE                              CLIENT SIDE
-----------                              -----------

Define .aidl file
       |
       v
Build project
(generates Stub + Proxy)
       |
       v
Implement Stub
in MyService.onBind()
       |
       v
Declare in Manifest         ---------> bindService()
                                              |
                                              v
                                       onServiceConnected()
                                       Stub.asInterface(binder)
                                              |
                                              v
                                       Call interface methods
                                       (getData / setData)
                                              |
                                              v
                                       unbindService() on destroy


Important Considerations

Threading

AIDL methods run on the service's Binder thread pool, not the main thread. If your implementation does heavy computation or blocking I/O, move that work to a background thread. Blocking the Binder thread can cause the calling client to hang.

java
@Override
public String getData(int id) throws RemoteException {
    // Heavy work should be dispatched to a background thread
    // Return a result or use a callback mechanism
    return processData(id);
}

Data Types

AIDL supports passing custom objects across processes, but those objects must implement the Parcelable interface (Android's serialization mechanism optimized for IPC). You also need to declare the Parcelable class in its own .aidl file.

java
// User.aidl (declares Parcelable to AIDL)
package com.example.myapp;
parcelable User;

Security

When android:exported="true" is set, any app on the device can attempt to bind to your service. Always validate callers:

  • Check the calling package name using getCallingUid() and PackageManager
  • Use Android permissions — declare a uses-permission requirement in the manifest and check it in the service
  • Never expose sensitive operations to unverified callers

AIDL vs. Other IPC Mechanisms

MechanismCross-ProcessType SafetyComplexityBest For
AIDLYesStrongMediumStructured cross-app APIs
MessengerYesLimitedLowSimple message passing
ContentProviderYesMediumMediumStructured data sharing
BroadcastYesNoneLowOne-way event broadcasting
Binder (direct)YesCustomHighAdvanced system-level IPC

AIDL is the right choice when you need a typed, method-call-style API between processes — especially when multiple clients need to call multiple methods with different parameters and return values.


Summary

  • AIDL enables type-safe inter-process communication between Android apps
  • The server defines an .aidl interface file; the build system generates the Stub and Proxy boilerplate
  • The server implements the Stub; the client uses Stub.asInterface() to get a Proxy
  • AIDL methods run on the Binder thread — never block the calling thread
  • Always handle RemoteException on the client side
  • Protect exported services with permission checks

AIDL is one of Android's more advanced concepts, but it is the foundation of how Android's own system services expose APIs to apps. Understanding it gives you deep insight into how the Android platform itself is architected.