قائمة انتظار الرسائل السريعة (FMQ)

إذا كنت تبحث عن دعم AIDL، يمكنك أيضًا FMQ مع AIDL

تستخدم البنية الأساسية لاستدعاء الإجراء عن بُعد (RPC) في HIDL آليات Binder، مما يعني أن الاتصالات تتضمن أعباء عامة وتتطلب عمليات نواة وقد تؤدي إجراء الجدولة. ومع ذلك، في الحالات التي يجب فيها نقل البيانات بين بأقل عبء أو مشاركة في النواة، فإن قائمة انتظار الرسائل السريعة (FMQ).

ينشئ FMQ قوائم انتظار الرسائل باستخدام الخصائص المطلوبة. إنّ يمكن استخدام الكائن MQDescriptorSync أو MQDescriptorUnsync يتم إرسالها عبر مكالمة HIDL RPC واستخدامها من قِبل عملية الاستلام للوصول إلى قائمة انتظار الرسائل.

لا تتوفّر قوائم انتظار الرسائل السريعة إلا في لغة C++ وعلى الأجهزة. يعمل بنظام التشغيل Android 8.0 أو إصدار أحدث.

أنواع قائمة انتظار الرسائل

يتيح Android استخدام نوعَين من قوائم الانتظار (المعروفة باسم النكهات):

  • يُسمح للقوائم غير المتزامنة بالتجاوز، ويمكن أن تحتوي على العديد القرّاء يجب على كل قارئ قراءة البيانات في الوقت المناسب أو فقدانها.
  • لا يُسمح بالتجاوز سعة قوائم الانتظار المتزامنة، ويمكن أن تتضمن فقط قارئ واحد

لا يُسمح لكلا النوعين من قوائم الانتظار بمواصلة العمل (القراءة من قائمة انتظار فارغة). فشل) ويمكن أن يكون لها كاتب واحد فقط.

غير متزامن

تتضمن قائمة الانتظار غير المتزامنة كاتبًا واحدًا فقط، ولكن يمكن أن تحتوي على أي عدد من للقرّاء هناك موضع كتابة واحد لقائمة الانتظار؛ مع ذلك، يحتفظ كل قارئ تتبع موضع القراءة المستقل الخاص بها.

نجاح الكتابة في قائمة الانتظار دائمًا (لا يتم التحقق منها بحثًا عن وجود تجاوز) ما دامت وهي ليست أكبر من سعة قائمة الانتظار المهيأة (تكتب بحجم أكبر من تفشل سعة قائمة الانتظار على الفور). ولأنّ كل قارئ قد يكون له قراءة مختلفة بدلاً من انتظار أن يقرأ كل قارئ كل جزء من البيانات يُسمح له بالسقوط خارج قائمة الانتظار كلما احتاجت عمليات الكتابة الجديدة إلى مساحة.

القراء هم مسئولون عن استرداد البيانات قبل أن تخرج من نهاية قائمة الانتظار. يشير هذا المصطلح إلى عملية قراءة تحاول قراءة بيانات أكثر من المتاحة. يتوقف على الفور (إذا كان لا يؤدي إلى الحظر) أو ينتظر توفير بيانات كافية (إذا حظر). يشير هذا المصطلح إلى قراءة تحاول قراءة بيانات أكبر من سعة قائمة الانتظار دائمًا. فشل على الفور.

إذا فشل القارئ في متابعة الكاتب، بحيث تكون كمية البيانات التي يكتبها ذلك القارئ ولم يقرأها بعد يتجاوز سعة قائمة الانتظار، القراءة التالية لا تُرجع البيانات؛ بل يعيد ضبط قراءة موضعًا يساوي آخر موضع كتابة ثم يعرض الفشل. إذا كانت يتم التحقق من البيانات المتاحة للقراءة بعد تجاوز السعة ولكن قبل القراءة التالية، تعرض بيانات متوفرة للقراءة أكثر من سعة قائمة الانتظار، مما يشير إلى حدث الفائض. (إذا كانت قائمة الانتظار تتجاوز التحقق من البيانات المتاحة ومحاولة قراءة تلك البيانات، فإن المؤشر الوحيد على تجاوز القيمة هو أن فشلت القراءة.)

من المحتمل ألا يريد قرّاء قائمة الانتظار غير المتزامنة إعادة ضبط مؤشرات القراءة والكتابة لقائمة الانتظار. لذلك، عند إنشاء قائمة الانتظار من يجب أن يستخدم قرّاء الواصف الوسيطة "false" في "resetPointers" .

متزامن

تتضمّن قائمة المحتوى التالي المتزامنة كاتبًا واحدًا وقارئًا واحدًا يتضمّن كتابة واحدة. موضعًا وموضع قراءة واحد. من المستحيل كتابة بيانات أكثر من تحتوي قائمة الانتظار على مساحة أو تقرأ بيانات أكثر مما تحتفظ به قائمة الانتظار حاليًا. اعتمادًا على ما إذا كانت دالة المنع أو القراءة أو التي لا تحظر يحاول تجاوز المساحة المتوفرة أو فشل إرجاع البيانات فورًا أو منعه حتى يتم إكمال العملية المطلوبة. محاولات يخفق دائمًا على الفور قراءة أو كتابة بيانات أكثر من سعة قائمة الانتظار.

إعداد جهاز FMQ

تتطلّب لائحة الرسائل عدة كائنات MessageQueue: واحد أن تتم كتابتها إليه وواحدة أو أكثر لتتم قراءتها منها. لا توجد موسيقى فاضحة تحديد الكائن المستخدَم للكتابة أو القراءة الأمر يعود إلى المستخدم للتأكد من عدم استخدام أي كائن للقراءة والكتابة، وأن كاتب واحد على الأكثر، وفي قوائم الانتظار المتزامنة، أنه يوجد واحد قارئ البطاقات.

إنشاء أول كائن Messageplaylist

يتم إنشاء قائمة انتظار الرسائل وإعدادها بمكالمة واحدة:

#include <fmq/MessageQueue.h>
using android::hardware::kSynchronizedReadWrite;
using android::hardware::kUnsynchronizedWrite;
using android::hardware::MQDescriptorSync;
using android::hardware::MQDescriptorUnsync;
using android::hardware::MessageQueue;
....
// For a synchronized non-blocking FMQ
mFmqSynchronized =
  new (std::nothrow) MessageQueue<uint16_t, kSynchronizedReadWrite>
      (kNumElementsInQueue);
// For an unsynchronized FMQ that supports blocking
mFmqUnsynchronizedBlocking =
  new (std::nothrow) MessageQueue<uint16_t, kUnsynchronizedWrite>
      (kNumElementsInQueue, true /* enable blocking operations */);
  • أداة إعداد "MessageQueue<T, flavor>(numElements)" ينشئ ويهيئ كائنًا يدعم وظيفة قائمة انتظار الرسائل.
  • ينشئ جهاز إعداد "MessageQueue<T, flavor>(numElements, configureEventFlagWord)" عنصرًا ويضبطه. يتوافق مع وظيفة قائمة انتظار الرسائل مع الحظر.
  • يمكن أن تكون قيمة الحقل "flavor" إما kSynchronizedReadWrite قائمة انتظار متزامنة أو kUnsynchronizedWrite لقائمة غير متزامنة قائمة الانتظار.
  • يمكن أن يكون uint16_t (في هذا المثال) أي نوع محدد HIDL الذي لا تتضمن مخازن مؤقتة متداخلة (لا تتضمن string أو vec وأنواعها أو مقابضها أو واجهاتها.
  • تشير السمة kNumElementsInQueue إلى حجم قائمة الانتظار بعدد الإدخالات يحدد حجم المخزن المؤقت المشترك المخصص للذاكرة لقائمة الانتظار.

إنشاء كائن Messageplaylist الثاني

يتم إنشاء الجانب الثاني من قائمة انتظار الرسائل باستخدام الكائن MQDescriptor الذي تم الحصول عليه من الجانب الأول. تشير رسالة الأشكال البيانية تم إرسال عنصر واحد (MQDescriptor) من خلال استدعاء HIDL أو AIDL RPC إلى العملية. تحتوي على الطرف الثاني من قائمة انتظار الرسائل. تشير رسالة الأشكال البيانية يتضمن MQDescriptor معلومات حول قائمة الانتظار، بما في ذلك:

  • معلومات لتعيين المخزن المؤقت وكتابة المؤشر
  • معلومات لربط مؤشر القراءة (إذا تمت مزامنة قائمة الانتظار)
  • معلومات لتعيين كلمة علامة الحدث (إذا كانت قائمة الانتظار محظورة)
  • نوع الكائن (<T, flavor>)، والذي يتضمّن نوع محدد HIDL من عناصر قائمة الانتظار ونكهة قائمة الانتظار (متزامنة أو غير متزامنة).

يمكن استخدام الكائن MQDescriptor لإنشاء كائن MessageQueue:

MessageQueue<T, flavor>::MessageQueue(const MQDescriptor<T, flavor>& Desc, bool resetPointers)

تحدّد مَعلمة resetPointers ما إذا كان يجب إعادة ضبط القراءة. واكتب المواضع على 0 أثناء إنشاء هذا الكائن MessageQueue. في قائمة انتظار غير متزامنة، يكون موضع القراءة (المحلي لكل منها) يتم دائمًا ضبط الكائن MessageQueue في قوائم الانتظار غير المتزامنة) على القيمة 0. أثناء عملية الإنشاء يتم عادةً إعداد MQDescriptor أثناء إنشاء أول كائن قائمة انتظار الرسائل. لمزيد من التحكم في البيانات المشتركة، يمكنك إعداد MQDescriptor يدويًا (يتم تحديد MQDescriptor في system/libhidl/base/include/hidl/MQDescriptor.h) ثم إنشاء كل كائن MessageQueue كما هو موضح في هذا القسم.

حظر قوائم الانتظار وعلامات الأحداث

حسب الإعدادات التلقائية، لا تتيح قوائم الانتظار حظر عمليات القراءة/الكتابة. هناك نوعان حظر استدعاءات القراءة/الكتابة:

  • الصيغة القصيرة، تتضمن ثلاث معلَمات (مؤشر البيانات، عدد العناصر، المهلة). يتيح الحظر في عمليات القراءة/الكتابة الفردية على قائمة الانتظار. عند استخدام هذا النموذج، تتعامل قائمة الانتظار مع علامة الحدث وأقنعة البت داخليًا، ويجب أن يكون كائن قائمة انتظار الرسالة الأولى باستخدام المعلمة الثانية true. مثل:
    // For an unsynchronized FMQ that supports blocking
    mFmqUnsynchronizedBlocking =
      new (std::nothrow) MessageQueue<uint16_t, kUnsynchronizedWrite>
          (kNumElementsInQueue, true /* enable blocking operations */);
    
  • الصيغة الطويلة، تتضمّن ست مَعلمات (بما في ذلك علامات الحدث وأقنعة البت). تتيح استخدام عنصر EventFlag مشترك بين قوائم انتظار متعدّدة. ويسمح بتحديد أقنعة بت الإشعار المراد استخدامها. في هذه الحالة، يجب تقديم علامة الحدث وأقنعة البت لكل استدعاء قراءة وكتابة.

وبالنسبة إلى النموذج الطويل، يمكن تقديم EventFlag صراحةً في كل مكالمة readBlocking() وwriteBlocking(). أحد قد تتم تهيئة قوائم الانتظار بعلامة حدث داخلية، والتي يجب تم استخلاصها من كائنات MessageQueue في قائمة الانتظار هذه باستخدام تم استخدام getEventFlagWord() لإنشاء EventFlag. في كل عملية لاستخدامها مع FMQs الأخرى. بدلاً من ذلك، يمكن إعداد EventFlag عنصرًا باستخدام أي عناصر مشتركة مناسبة الذاكرة.

بشكل عام، يجب أن تستخدم كل قائمة انتظار واحدة فقط من الأشكال القصيرة أو حظر المحتوى أو حظر المحتوى الطويل ليس من الخطأ مزجها، ولكن كن حذرًا البرمجة مطلوبة للحصول على النتيجة المرجوّة.

وضع علامة "للقراءة فقط" على الذكرى

تمتلك الذاكرة المشتركة أذونات القراءة والكتابة تلقائيًا. غير المتزامنة قوائم الانتظار (kUnsynchronizedWrite)، قد يحتاج الكاتب إلى إزالة أذونات الكتابة لجميع القرّاء قبل توزيع عناصر MQDescriptorUnsync. يضمن ذلك عدم وجود لا يمكن للعمليات الكتابة في قائمة الانتظار، وهو ما يوصى به للحماية من الأخطاء أو السلوك السيئ في عمليات القارئ. إذا أراد الكاتب أن يتمكن القراء من إعادة تعيين قائمة الانتظار عندما استخدموا MQDescriptorUnsync لإنشاء جانب القراءة من قائمة الانتظار، ثم لا يمكن وضع علامة على الذكرى للقراءة فقط. هذا هو السلوك الافتراضي للدالة الإنشائية `Messageplaylist`. لذلك، إذا كان هناك بالفعل المستخدمين الحاليين في قائمة الانتظار هذه، يجب تغيير الكود الخاص بهم لإنشاء قائمة الانتظار باستخدام resetPointer=false

  • كاتب: طلب ashmem_set_prot_region باستخدام واصف ملف MQDescriptor والمنطقة التي تم ضبطها للقراءة فقط (PROT_READ):
    int res = ashmem_set_prot_region(mqDesc->handle->data[0], PROT_READ)
  • القارئ: إنشاء قائمة انتظار رسائل باستخدام resetPointer=false ( القيمة التلقائية هي true):
    mFmq = new (std::nothrow) MessageQueue(mqDesc, false);

استخدام قائمة انتظار الرسائل

واجهة برمجة التطبيقات العامة للعنصر MessageQueue هي:

size_t availableToWrite()  // Space available (number of elements).
size_t availableToRead()  // Number of elements available.
size_t getQuantumSize()  // Size of type T in bytes.
size_t getQuantumCount() // Number of items of type T that fit in the FMQ.
bool isValid() // Whether the FMQ is configured correctly.
const MQDescriptor<T, flavor>* getDesc()  // Return info to send to other process.

bool write(const T* data)  // Write one T to FMQ; true if successful.
bool write(const T* data, size_t count) // Write count T's; no partial writes.

bool read(T* data);  // read one T from FMQ; true if successful.
bool read(T* data, size_t count);  // Read count T's; no partial reads.

bool writeBlocking(const T* data, size_t count, int64_t timeOutNanos = 0);
bool readBlocking(T* data, size_t count, int64_t timeOutNanos = 0);

// Allows multiple queues to share a single event flag word
std::atomic<uint32_t>* getEventFlagWord();

bool writeBlocking(const T* data, size_t count, uint32_t readNotification,
uint32_t writeNotification, int64_t timeOutNanos = 0,
android::hardware::EventFlag* evFlag = nullptr); // Blocking write operation for count Ts.

bool readBlocking(T* data, size_t count, uint32_t readNotification,
uint32_t writeNotification, int64_t timeOutNanos = 0,
android::hardware::EventFlag* evFlag = nullptr) // Blocking read operation for count Ts;

//APIs to allow zero copy read/write operations
bool beginWrite(size_t nMessages, MemTransaction* memTx) const;
bool commitWrite(size_t nMessages);
bool beginRead(size_t nMessages, MemTransaction* memTx) const;
bool commitRead(size_t nMessages);

يمكن استخدام availableToWrite() وavailableToRead(). لتحديد مقدار البيانات التي يمكن نقلها في عملية واحدة. في قائمة انتظار غير متزامنة:

  • تعرض الدالة availableToWrite() دائمًا سعة قائمة الانتظار.
  • لكل قارئ موضع قراءة خاص به ويقوم بعمليته الحسابية availableToRead()
  • من منظور القارئ البطيء، يُسمَح بعرض قائمة الانتظار بالتجاوز. قد يؤدي ذلك إلى عرض availableToRead() لقيمة أكبر من حجم قائمة الانتظار. القراءة الأولى بعد فشل التجاوز وينتج عن ذلك موضع القراءة لهذا القارئ الذي يتم ضبطه على قيمة مساوية لمؤشر الكتابة الحالي، ما إذا كان تم الإبلاغ عن التجاوز من خلال availableToRead()

تعرض الطريقتان read() وwrite() true إذا كان من الممكن نقل جميع البيانات المطلوبة (ونقلها) من/إلى قائمة الانتظار. وهذه الطرق لا تحظر إما أن تنجح (وترجع true) أو تعذُّر عملية الإرجاع (false) على الفور.

تنتظر الطريقتان readBlocking() وwriteBlocking() حتى اكتمال العملية المطلوبة أو إلى أن تنتهي مهلتها ( تعني قيمة timeOutNanos التي قيمتها 0 عدم انتهاء المهلة أبدًا).

يتم تنفيذ عمليات الحظر باستخدام كلمة علامة حدث. بشكل افتراضي، تنشئ كل قائمة انتظار وتستخدم كلمة العلامة الخاصة بها لدعم الشكل المختصَر "readBlocking()" وwriteBlocking()" من الممكن قوائم انتظار متعددة لمشاركة كلمة واحدة، بحيث يمكن للعملية الانتظار حتى عمليات الكتابة أو إلى أي من قوائم الانتظار. يمكن أن يكون المؤشر الذي تشير إلى كلمة علامة حدث في قائمة انتظار تم الحصول عليه من خلال استدعاء getEventFlagWord()، وهذا المؤشر (أو أي يشير إلى موقع ذاكرة مشترك مناسب) يمكن استخدامه لإنشاء كائن EventFlag لتمريره إلى الشكل الطويل من readBlocking() وwriteBlocking()لاختيار قائمة الانتظار. readNotification وwriteNotification تخبر المعلمات وحدات البت في علامة الحدث التي يجب استخدامها للإشارة إلى القراءات ويكتب في تلك القائمة. readNotification و writeNotification هي أقنعة بت ذات 32 بت.

ينتظر readBlocking() على writeNotification بت؛ إذا كانت هذه المعلمة هي 0، يفشل الاستدعاء دائمًا. إذا كانت قيمة readNotification هي 0، ويتعذَّر الاتصال، ولكن القراءة الناجحة لن يؤدي إلى تعيين أي وحدات بت للإشعارات. في قائمة انتظار متزامنة، فهذا يعني أنّ طلب writeBlocking() المقابل لا يستيقظ أبدًا ما لم يتم تعيين البت في مكان آخر. في قائمة انتظار غير متزامنة، لا ينتظر "writeBlocking()" (يجب أن يتم استخدامه لضبط وكتابة بت الإشعار)، ومن المناسب أن لا يتم تعيين أي وحدات بت الإشعارات. وبالمثل، يتعذّر تنفيذ writeblocking() في حال readNotification هي 0، وتضبط الكتابة الناجحة القيم المحددة writeNotification بت.

للانتظار في قوائم انتظار متعددة في الوقت نفسه، يُرجى استخدام عنصر EventFlag wait() طريقة للانتظار على قناع الإشعارات. تشير رسالة الأشكال البيانية تُرجع طريقة wait() كلمة حالة تتضمن وحدات البت التي تسببت في ميزة الاستيقاظ. يتم بعد ذلك استخدام هذه المعلومات للتحقق من احتواء قائمة الانتظار المقابلة على مساحة أو بيانات كافية لعملية الكتابة/القراءة المطلوبة لا يحظر write()/read(). للحصول على عملية نشر يُرجى استخدام مكالمة أخرى مع رقم EventFlag wake(). للاطّلاع على تعريف السمة EventFlag التجريد، راجع system/libfmq/include/fmq/EventFlag.h.

عمليات عدم نسخ البيانات

تشير رسالة الأشكال البيانية read/write/readBlocking/writeBlocking() تنقل واجهات برمجة التطبيقات مؤشرًا إلى مخزن مؤقت للإدخال/الإخراج كوسيطة وتستخدم memcpy() يتصل داخليًا لنسخ البيانات بين المخزن المؤقت لحلقة FMQ لتحسين الأداء، يتضمن الإصدار 8.0 من نظام التشغيل Android والإصدارات الأحدث مجموعة من توفر واجهات برمجة التطبيقات التي توفر الدخول المباشر للمؤشر إلى المخزن المؤقت الدائري، بحاجة إلى استخدام مكالمات memcpy.

يمكنك استخدام واجهات برمجة التطبيقات العامة التالية لإجراء عمليات FMQ بدون نسخة:

bool beginWrite(size_t nMessages, MemTransaction* memTx) const;
bool commitWrite(size_t nMessages);

bool beginRead(size_t nMessages, MemTransaction* memTx) const;
bool commitRead(size_t nMessages);
  • توفّر الطريقة beginWrite مؤشرات أساسية في حلقة FMQ. المورد الاحتياطي. بعد كتابة البيانات، عليك الالتزام بها باستخدام commitWrite(). تعمل الطريقتان beginRead/commitRead بالطريقة نفسها.
  • يتم استخدام الطريقتَين beginRead/Write كإدخال عدد الرسائل المراد قراءتها/كتابتها وعرض قيمة منطقية تشير إلى ما إذا كانت القراءة/الكتابة ممكنة. إذا كانت القراءة أو الكتابة ممكنة memTx تتمّ تعبئة البنية بالمؤشرات الأساسية التي يمكن استخدامها للمؤشر المباشر. الوصول إلى الذاكرة المشتركة للمخزن المؤقت الدائري.
  • تحتوي البنية MemRegion على تفاصيل حول كتلة من الذاكرة، بما في ذلك المؤشر الأساسي (العنوان الأساسي لكتلة الذاكرة) والطول في مصطلح T (طول كتلة الذاكرة من حيث تحديد HIDL قائمة انتظار الرسائل).
  • تحتوي بنية MemTransaction على MemRegion التراكيب، first وsecond كقراءة أو كتابة قد يتطلب المخزن المؤقت الدائري التفافًا حتى بداية قائمة الانتظار. هذا النمط أن هناك حاجة إلى مؤشرين أساسيين لقراءة/كتابة البيانات في FMQ المخزن المؤقت للرنين.

للحصول على العنوان الأساسي والطول من بنية MemRegion:

T* getAddress(); // gets the base address
size_t getLength(); // gets the length of the memory region in terms of T
size_t getLengthInBytes(); // gets the length of the memory region in bytes

للحصول على إشارات إلى أول وثانيتَين MemRegion داخل كائن MemTransaction:

const MemRegion& getFirstRegion(); // get a reference to the first MemRegion
const MemRegion& getSecondRegion(); // get a reference to the second MemRegion

مثال على الكتابة في FMQ باستخدام واجهات برمجة التطبيقات المتاحة بدون نسخ:

MessageQueueSync::MemTransaction tx;
if (mQueue->beginRead(dataLen, &tx)) {
    auto first = tx.getFirstRegion();
    auto second = tx.getSecondRegion();

    foo(first.getAddress(), first.getLength()); // method that performs the data write
    foo(second.getAddress(), second.getLength()); // method that performs the data write

    if(commitWrite(dataLen) == false) {
       // report error
    }
} else {
   // report error
}

تشكّل طرق المساعدة التالية جزءًا من MemTransaction أيضًا:

  • T* getSlot(size_t idx);
    عرض مؤشر في موضع idx داخل MemRegions التي تشكّل جزءًا من MemTransaction الخاص بك. إذا كان الكائن MemTransaction يمثّل الذكرى مناطق لقراءة/كتابة عدد N من العناصر من النوع T، ثم النطاق الصالح idx بين 0 وN-1.
  • bool copyTo(const T* data, size_t startIdx, size_t nMessages = 1);
    كتابة nMessages عنصر من النوع T في مناطق الذاكرة يصفها الكائن، بدءًا من الفهرس startIdx. هذه الطريقة يستخدم memcpy() ولا يهدف إلى استخدامه في نسخة الصفر العملية. إذا كان العنصر MemTransaction يمثّل الذاكرة قراءة/كتابة عدد N من العناصر من النوع T، فإن النطاق الصالح لـ idx هو بين 0 وN-1.
  • bool copyFrom(T* data, size_t startIdx, size_t nMessages = 1);
    طريقة مساعِدة لقراءة nMessages عنصر من النوع T من مناطق الذاكرة التي يصفها الكائن بدءًا من startIdx. هذا النمط تستخدم الطريقة memcpy() ولا يُقصد استخدامها العملية.

إرسال قائمة المحتوى التالي عبر HIDL

بالنسبة إلى صناعة المحتوى:

  1. أنشئ كائن قائمة انتظار الرسائل كما هو موضّح أعلاه.
  2. تحقَّق من صلاحية العنصر من خلال isValid().
  3. إذا كنت تنتظر في عدة قوائم انتظار من خلال تمرير EventFlag في الصيغة الطويلة من readBlocking()/writeBlocking()، يمكنك استخراج مؤشر علامة الحدث (باستخدام getEventFlagWord()) من MessageQueue الكائن الذي تم إعداده لإنشاء العلامة واستخدِم هذه العلامة لإنشاء كائن EventFlag اللازم.
  4. استخدِم طريقة MessageQueue getDesc() للحصول على واصف البيانات.
  5. في ملف .hal، أدخِل مَعلمة من النوع للطريقة. fmq_sync أو fmq_unsync حيث T نوع مناسب محدد HIDL. استخدم هذا الخيار لإرسال الكائن الذي تم إرجاعه بواسطة getDesc() إلى عملية الاستلام.

على الجانب المستقبِل:

  1. استخدِم كائن الوصف لإنشاء كائن MessageQueue. كن فتأكد من استخدام نفس صيغة قائمة الانتظار ونوع البيانات، وإلا فسيفشل القالب تجميع.
  2. إذا كنت قد استخرجت علامة حدث، استخرج العلامة من العلامة عنصر واحد (MessageQueue) قيد الاستلام.
  3. استخدِم عنصر MessageQueue لنقل البيانات.