Fast Message Queue with AIDL

As of Android 12, Fast Message Queue can be used with AIDL interfaces using the NDK backend. This allows processes to communicate without the overhead and restrictions of binder transactions after some short setup. Using Stable AIDL allows communication between system and vendor processes.

Supported payload types

The messages sent between processes in the shared memory message queue must have the same memory layout across process boundaries and cannot contain pointers. Attempting to create an AidlMessageQueue with a type that isn't supported will cause a compilation error.

Supported queue types

The same queue types from HIDL, often called flavors, are supported with AIDL. These are used as template arguments for the queues and descriptors.

HIDL Types AIDL Types
android::hardware::kSynchronizedReadWrite android.hardware.common.fmq.SynchronizedReadWrite
android::hardware::kUnsynchronizedWrite android.hardware.common.fmq.UnsynchronizedWrite

How to use

Define the AIDL interface that will pass the MQDescriptor to the other process. MQDescriptor can be used anywhere a parcelable can be.

The required template arguments for MQDescriptor are payload type and queue flavor.

import android.hardware.common.fmq.MQDescriptor
import android.hardware.common.fmq.SynchronizedReadWrite

void getQueue(out MQDescriptor<int, SynchronizedReadWrite> mqDesc);

The process of setting up each side of the message queue is nearly identical to the process using HIDL, just using the AIDL types.

#include <fmq/AidlMessageQueue.h>
...
using ::android::AidlMessageQueue;
using ::aidl::android::hardware::common::fmq::MQDescriptor;
using ::aidl::android::hardware::common::fmq::SynchronizedReadWrite;
...
ndk::ScopedAStatus MyInterface::getQueue(MQDescriptor<int32_t, SynchronizedReadWrite>* mqDesc) {
    *mqDesc = mFmqSynchronized->dupeDesc();
    return ndk::ScopedAStatus::ok();
}
...
// Create the first side of the queue before servicing getQueue() in this example
mFmqSynchronized =
  new AidlMessageQueue<int32_t, SynchronizedReadWrite>(kNumElementsInQueue);

The receiving process will create the other side of the queue with the descriptor received from the AIDL interface.

MQDescriptor<int32_t, SynchronizedReadWrite> desc;
auto ret = service->getQueue(true, &desc);
if (!ret.isOk()) {
   ...
}
// By default the constructor will reset the read and write pointers of the queue.
// Add a second `false` argument to avoid resetting the pointers.
mQueue = new (std::nothrow) AidlMessageQueue<int32_t, SynchronizedReadWrite>(desc);
if (!mQueue->isValid()) {
   ...
}

Using the AidlMessageQueue after setup is the same as the HIDL MessageQueue. All of the APIs described at Using the MessageQueue are fully supported with AidlMessageQueue with one exception:

const MQDescriptor<T, flavor>* getDesc() is replaced by MQDescriptor<T, U> dupeDesc() which returns the AIDL MQDescriptor.