AOSP Framework & Internals
2 min read

Callbacks in AIDL

Learn about Callbacks in AIDL.

Introduction

While typical IPC transactions are synchronous (the client blocks until the service returns), many Android system architectures require asynchronous event delivery. The Callback pattern in AIDL enables the service to notify the client when a long-running operation completes or an event occurs.

Callback Interface Pattern

To implement a callback, you define a secondary AIDL interface. The client implements this callback interface and passes its reference to the service via the primary interface.

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

interface IResultCallback {
    void onResult(String resultData);
}
// IMyService.aidl
package com.example.android.ipc;
import com.example.android.ipc.IResultCallback;

interface IMyService {
    void registerCallback(IResultCallback cb);
    void unregisterCallback(IResultCallback cb);
}

Because the IResultCallback object is sent over Binder, the service receives a proxy proxy to the client's implementation.

One-Way Callbacks

Callbacks are almost always marked with the oneway keyword. If a service calls a client callback synchronously and the client process is slow or blocked, the service's Binder thread gets blocked as well. Using oneway prevents the service from stalling.

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

oneway interface IResultCallback {
    void onResult(String resultData);
}

Asynchronous Event Delivery

In the service implementation, callbacks are typically managed using a RemoteCallbackList. This specialized collection handles thread-safety and automatically removes callbacks if the underlying client process dies.

private final RemoteCallbackList<IResultCallback> mCallbacks = new RemoteCallbackList<>();

@Override
public void registerCallback(IResultCallback cb) {
    if (cb != null) mCallbacks.register(cb);
}

private void notifyClients(String data) {
    int count = mCallbacks.beginBroadcast();
    for (int i = 0; i < count; i++) {
        try {
            mCallbacks.getBroadcastItem(i).onResult(data);
        } catch (RemoteException e) {
            // Handled natively by RemoteCallbackList
        }
    }
    mCallbacks.finishBroadcast();
}

Death Recipient with Callbacks

When managing raw IBinder objects, a service might need to clean up resources if the client dies unexpectedly. This is done by registering a DeathRecipient via IBinder.linkToDeath().

IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
    @Override
    public void binderDied() {
        Log.w("Service", "Client died! Cleaning up resources.");
        clientBinder.unlinkToDeath(this, 0);
    }
};

clientBinder.linkToDeath(deathRecipient, 0);

The RemoteCallbackList encapsulates this linkToDeath logic under the hood, making it the standard approach for managing AIDL callbacks in Android.