Быстрая очередь сообщений с AIDL

В Android 12 быстрая очередь сообщений (FMQ) может использоваться с интерфейсами AIDL, в которых применяется серверная часть NDK. Это позволяет процессам обмениваться данными без дополнительных затрат и ограничений, связанных с транзакциями Binder, после небольшой настройки. Использование стабильного AIDL позволяет наладить связь между системными процессами и процессами поставщика.

Поддерживаемые типы полезной нагрузки

Сообщения, отправляемые между процессами в очереди сообщений с общей памятью, должны иметь одинаковую структуру памяти в разных процессах и не могут содержать указатели. При попытке создать AidlMessageQueue с неподдерживаемым типом возникает ошибка компиляции. Типы AIDL parcelable и union в бэкенде cpp нельзя использовать с Fast Message Queue, потому что они наследуются от android::Parcelable, у которого есть виртуальные функции.

Поддерживаемые типы очередей

В AIDL поддерживаются те же типы очередей, что и в HIDL. Они используются в качестве аргументов шаблона для очередей и дескрипторов.

Типы HIDLТипы AIDL
android::hardware::kSynchronizedReadWrite android.hardware.common.fmq.SynchronizedReadWrite
android::hardware::kUnsynchronizedWrite android.hardware.common.fmq.UnsynchronizedWrite

Как использовать презентацию

Определите интерфейс AIDL, который передает MQDescriptor другому процессу. MQDescriptor можно использовать везде, где можно использовать Parcelable.

Для шаблона MQDescriptor требуются аргументы типа полезной нагрузки и вкуса очереди.

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

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

Процесс настройки каждой стороны очереди сообщений почти идентичен процессу с использованием HIDL, только с использованием типов AIDL.

#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);

В процессе получения создается другая сторона очереди с дескриптором, полученным от интерфейса AIDL.

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()) {
   ...
}

После настройки AidlMessageQueue используется так же, как и HIDL MessageQueue. Все API, описанные в разделе Использование MessageQueue, полностью поддерживаются в AidlMessageQueue, за исключением одного:

const MQDescriptor<T, flavor>* getDesc() заменен на MQDescriptor<T, U> dupeDesc(), который возвращает AIDL MQDescriptor.