Fila de mensagens rápidas com AIDL

No Android 12, a Fila de mensagens rápidas pode ser usada com interfaces AIDL usando o back-end do NDK. Isso permite que os processos se comuniquem sem a sobrecarga e as restrições das transações de binder após uma configuração curta. Usar a AIDL estável permite a comunicação entre processos do sistema e do fornecedor.

Tipos de payload compatíveis

  • Tipos FixedSize AIDL parcelable (não disponíveis no back-end cpp)
  • Tipos union da AIDL (não disponíveis no back-end cpp)
  • Tipos enum do AIDL
  • Tipos integrais do AIDL

As mensagens enviadas entre processos na fila de mensagens de memória compartilhada precisam ter o mesmo layout de memória em todos os limites de processo e não podem conter ponteiros. Tentar criar um AidlMessageQueue com um tipo não compatível causa um erro de compilação. Os tipos parcelable e union do AIDL no back-end cpp não podem ser usados com a fila de mensagens rápidas porque herdam de android::Parcelable, que tem funções virtuais.

Tipos de filas compatíveis

Os mesmos tipos de fila do HIDL, geralmente chamados de variantes, são compatíveis com o AIDL. Eles são usados como argumentos de modelo para as filas e os descritores.

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

Como usar

Defina a interface AIDL que transmite o MQDescriptor para o outro processo. MQDescriptor pode ser usado em qualquer lugar em que um parcelable possa ser usado.

Os argumentos de modelo obrigatórios para MQDescriptor são o tipo de payload e o sabor da fila.

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

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

O processo de configuração de cada lado da fila de mensagens é quase idêntico ao processo usando HIDL, apenas usando os tipos 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);

O processo de recebimento cria o outro lado da fila com o descritor recebido da interface 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()) {
   ...
}

Usar o AidlMessageQueue após a configuração é o mesmo que o HIDL MessageQueue. Todas as APIs descritas em Usar o MessageQueue são totalmente compatíveis com AidlMessageQueue, com uma exceção:

const MQDescriptor<T, flavor>* getDesc() foi substituído por MQDescriptor<T, U> dupeDesc() que retorna o AIDL MQDescriptor.