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
FixedSizeAIDLparcelabletypes (not available from thecppbackend)- AIDL
uniontypes (not available from thecppbackend) - AIDL
enumtypes - AIDL integral types
The messages sent between processes in the shared memory message queue must
have the same memory layout across process boundaries and can't contain
pointers. Attempting to create an AidlMessageQueue with a type that isn't
supported causes a compilation error. The AIDL parcelable and union types
in the cpp backend can't be used with Fast Message Queue because they inherit
from android::Parcelable, which has virtual functions.
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 passes 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 creates 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 Use 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.