AOSP Framework & Internals
3 min read

Java AIDL Basics

Learn about Java AIDL Basics.

Introduction

Android Interface Definition Language (AIDL) defines the programming interface that both the client and service use to agree on IPC communication. Since Android processes cannot access each other's memory directly, AIDL marshals and unmarshals data into primitives that the operating system can understand.

AIDL File Syntax

An AIDL file uses a syntax similar to Java interfaces. It supports primitive types, String, CharSequence, List, Map, and custom Parcelable objects.

// IMyService.aidl
package com.example.android.ipc;

interface IMyService {
    int getStatus();
    void performAction(String data);
}

Directional tags (in, out, inout) are mandatory for non-primitive arguments (like List or custom Parcelables) to indicate which way the data flows, optimizing the marshalling overhead.

Generated Stub and Proxy Classes

When the Android build system compiles the .aidl file, it generates a corresponding Java interface (IMyService.java). Inside this interface, it generates an abstract inner class called Stub that extends android.os.Binder and implements IMyService.

The Stub class also contains a private inner class called Proxy. The Stub is used by the server to handle incoming calls, while the Proxy is used by the client to pack the arguments and send the transaction.

Implementing the Stub (Service Side)

On the service side, you instantiate an implementation of the Stub and return it in your Service.onBind() method.

public class MyService extends Service {
    private final IMyService.Stub mBinder = new IMyService.Stub() {
        @Override
        public int getStatus() throws RemoteException {
            return 1;
        }

        @Override
        public void performAction(String data) throws RemoteException {
            Log.i("MyService", "Received: " + data);
        }
    };

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

When a transaction arrives, the Stub.onTransact() method unpacks the Parcel and delegates the call to the actual methods implemented above.

Calling through the Proxy (Client Side)

The client process binds to the service using bindService(). In the ServiceConnection callback, the client receives an IBinder reference, which it casts into the IMyService interface using IMyService.Stub.asInterface(binder).

private IMyService mService;

private ServiceConnection mConnection = new ServiceConnection() {
    public void onServiceConnected(ComponentName className, IBinder service) {
        // This returns a Proxy object wrapping the remote IBinder
        mService = IMyService.Stub.asInterface(service);
        try {
            int status = mService.getStatus();
            Log.d("Client", "Service status: " + status);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
    public void onServiceDisconnected(ComponentName className) {
        mService = null;
    }
};

Under the hood, calling getStatus() on the proxy creates two Parcel objects (one for data, one for the reply) and invokes mRemote.transact().