أنواع البيانات

تنشئ إعلانات بيانات HIDL هياكل بيانات تخطيط C++ القياسي. هذه يمكن وضع الهياكل في أي مكان يبدو طبيعيًا (على المكدس، في الملف أو أو نطاق عمومي أو على كومة الذاكرة المؤقتة) ويمكن أن تتألّف بالطريقة نفسها. عميل لاستدعاء رمز وكيل HIDL الذي يتم تمريره في المراجع الثابتة والأنواع الأولية، بينما يخفي رمز التشتيت والخادم الوكيل تفاصيل التسلسل.

ملاحظة: ليست هناك أي رموز يكتبها المطوّرون المطلوبة لإنشاء تسلسل لهياكل البيانات أو إلغاء تسلسلها بشكل صريح.

يعيّن الجدول أدناه أساسيات HIDL لأنواع بيانات C++:

نوع HIDL نوع C++ العنوان/المكتبة
enum enum class
uint8_t..uint64_t uint8_t..uint64_t <stdint.h>
int8_t..int64_t int8_t..int64_t <stdint.h>
float float
double double
vec<T> hidl_vec<T> libhidlbase
T[S1][S2]...[SN] T[S1][S2]...[SN]
string hidl_string libhidlbase
handle hidl_handle libhidlbase
safe_union (custom) struct
struct struct
union union
fmq_sync MQDescriptorSync libhidlbase
fmq_unsync MQDescriptorUnsync libhidlbase

تصف الأقسام التالية أنواع البيانات بمزيد من التفصيل.

تعداد

يصبح التعداد في HIDL تعدادًا في لغة C++. مثل:

enum Mode : uint8_t { WRITE = 1 << 0, READ = 1 << 1 };
enum SpecialMode : Mode { NONE = 0, COMPARE = 1 << 2 };

... يصبح:

enum class Mode : uint8_t { WRITE = 1, READ = 2 };
enum class SpecialMode : uint8_t { WRITE = 1, READ = 2, NONE = 0, COMPARE = 4 };

بدءًا من Android 10، يمكن تكرار التعداد باستخدام ::android::hardware::hidl_enum_range. هذا النطاق يتضمّن كل عدّاد بالترتيب الذي يظهر به في رمز مصدر HIDL، بدءًا من من التعداد الأصلي إلى العنصر الثانوي الأخير. على سبيل المثال، تتكرر هذه التعليمة البرمجية أكثر من WRITE وREAD وNONE COMPARE بهذا الترتيب. وفقًا لما يلي: SpecialMode أعلاه:

template <typename T>
using hidl_enum_range = ::android::hardware::hidl_enum_range<T>

for (SpecialMode mode : hidl_enum_range<SpecialMode>) {...}

تستخدم hidl_enum_range أيضًا المكررات العكسية ويمكن في سياقات constexpr. في حال ظهور قيمة في تعداد عدة مرات، تظهر القيمة في النطاق عدة مرات.

Bitfield<T>

bitfield<T> (حيث يكون T تعدادًا يحدده المستخدم) النوع الأساسي لهذا التعداد في لغة C++. في المثال أعلاه، تصبح bitfield<Mode> uint8_t.

vec<T>

يُعد قالب الفئة hidl_vec<T> جزءًا من libhidlbase ويمكن استخدامها لتمرير متجه من أي نوع HIDL حجم عشوائي. الحاوية ذات الحجم الثابت المشابهة هي hidl_array يمكن أيضًا أن يكون hidl_vec<T> مهيأة للتوجيه إلى مخزن بيانات احتياطي خارجي من النوع T، باستخدام للدالة hidl_vec::setToExternal().

بالإضافة إلى إرسال/إدراج البنية بشكل مناسب في الوحدة التي تم إنشاؤها C++، فإن استخدام vec<T> يوفر بعض الراحة الدوال المطلوب الترجمة من/من std::vector والأحرف T المجردة والمؤشرات. إذا تم استخدام vec<T> كمعلمة، سيتم يستخدمه بشكل زائد (يتم إنشاء نموذجين أوّليين) لقبول اجتياز اختبار HIDL والنوع std::vector<T> .

مصفوفة

تُمثل الصفائف الثابتة في hidl صفة السمة hidl_array في libhidlbase. تمثّل hidl_array<T, S1, S2, …, SN> مصفوفة حجم ثابت بأبعاد N. T[S1][S2]…[SN]

سلسلة

يمكن أن تكون الفئة hidl_string (جزء من libhidlbase) يُستخدم لتمرير السلاسل عبر واجهات HIDL ويتم تحديده في /system/libhidl/base/include/hidl/HidlSupport.h مساحة التخزين الأولى location في الفئة مؤشر إلى المخزن المؤقت للأحرف الخاصة به.

يعرف hidl_string كيفية التحويل من وإلى std::string and char* (سلسلة من النمط C) باستخدام operator=، والبث الضمني، ودالة .c_str(). تشتمل بُنى سلسلة HIDL على المنشئات والإنشاءات المناسبة للنسخ العوامل إلى:

  • تحميل سلسلة HIDL من سلسلة std::string أو سلسلة C.
  • إنشاء std::string جديد من سلسلة HIDL

بالإضافة إلى ذلك، تحتوي سلاسل HIDL على أدوات إنشاء تحويل بحيث تكون سلاسل C يمكن استخدام سلاسل (char *) وسلاسل C++ (std::string) على الطرق التي تتخذ سلسلة HIDL.

إنشاء

يمكن أن يحتوي struct في HIDL فقط على أنواع بيانات ذات حجم ثابت الأخرى. ترتبط تعريفات هيكل HIDL مباشرةً بالتنسيق القياسي struct في لغة C++ ، ما يضمن حصول struct على تخطيط متناسق للذاكرة. يمكن أن يتضمن الهيكل أنواع HIDL، بما في ذلك handle وstring وvec<T>، لفصل المخازن المؤقتة ذات الطول المتغير.

اسم الحساب

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

يتم تمثيل النوع handle بالنوع hidl_handle في لغة C++، وهو برنامج التفاف بسيط حول مؤشر إلى كائن const native_handle_t (كان هذا العنصر متوفرًا في Android لمدة لفترة طويلة).

typedef struct native_handle
{
    int version;        /* sizeof(native_handle_t) */
    int numFds;         /* number of file descriptors at &data[0] */
    int numInts;        /* number of ints at &data[numFds] */
    int data[0];        /* numFds + numInts ints */
} native_handle_t;

لا يتم نقل الملكية إلى hidl_handle تلقائيًا. لمؤشر native_handle_t يلتف حوله. إنه موجود فقط بأمان تخزين مؤشر إلى native_handle_t بحيث يمكن استخدامه في كل من عمليات 32 و64 بت.

السيناريوهات التي يمتلك فيها hidl_handle الملف المضمّن تتضمن واصفات ما يلي:

  • بعد استدعاء طريقة setTo(native_handle_t* handle, bool shouldOwn) مع ضبط المعلمة shouldOwn على true
  • عندما يتم إنشاء الكائن hidl_handle من خلال إنشاء النسخ من كائن hidl_handle آخر
  • عندما يتم نسخ الكائن hidl_handle من جهاز آخر عنصر واحد (hidl_handle)

توفّر ميزة "hidl_handle" كلاً من الإحالات الناجحة الضمنية والصريحة. من/إلى native_handle_t* من الكائنات. إن الاستخدام الرئيسي النوع handle في HIDL هو تمرير أدوات وصف الملفات باستخدام HIDL. من الواجهات. وبالتالي، يتم تمثيل واصف ملف واحد بواسطة native_handle_t بدون int وعنوان واحد fd إذا كان العميل والخادم يعيشان في عملية مختلفة، فإن استدعاء إجراء عن بُعد (RPC) يتولى تنفيذها تلقائيًا واصف الملف لضمان يمكن أن تعمل كلتا العمليتين على نفس الملف.

رغم استلام واصف الملف في hidl_handle من خلال صالحة في هذه العملية، فإنها لا تستمر إلى ما بعد تلقي (يتم إغلاقها عند عرض الدالة). عملية تريد الاحتفاظ بإمكانية الوصول الدائم إلى واصف الملف يجب dup() أو تضمين واصفات الملفات، أو نسخ عنصر hidl_handle بالكامل.

ذكرى

يرتبط النوع HIDL memory بالفئة hidl_memory. في "libhidlbase"، وهو ما يمثّل ذكرى مشترَكة لم يتم تخصيصها. هذا هو هو الكائن الذي يجب تمريره بين العمليات لمشاركة الذاكرة في HIDL. إلى استخدام الذكريات المشترَكة:

  1. الحصول على نسخة افتراضية من IAllocator (المثيل الوحيد حاليًا "آشم" متاحة) وتستخدمها لتخصيص الذكريات المشتركة.
  2. تُرجع IAllocator::allocate() hidl_memory أي كائن يمكن تمريره من خلال HIDL RPC وتعيينه في عملية باستخدام دالة mapMemory في libhidlmemory.
  3. تعرض الدالة mapMemory مرجعًا إلى عنصر sp<IMemory> يمكن استخدامه للوصول إلى الذكرى. (تم تحديد IMemory وIAllocator في android.hidl.memory@1.0.)

يمكن استخدام مثال IAllocator لتخصيص ذاكرة:

#include <android/hidl/allocator/1.0/IAllocator.h>
#include <android/hidl/memory/1.0/IMemory.h>
#include <hidlmemory/mapping.h>
using ::android::hidl::allocator::V1_0::IAllocator;
using ::android::hidl::memory::V1_0::IMemory;
using ::android::hardware::hidl_memory;
....
  sp<IAllocator> ashmemAllocator = IAllocator::getService("ashmem");
  ashmemAllocator->allocate(2048, [&](bool success, const hidl_memory& mem) {
        if (!success) { /* error */ }
        // now you can use the hidl_memory object 'mem' or pass it around
  }));

ويجب أن يتم إجراء التغييرات الفعلية على الذاكرة من خلال IMemory. إما في الجانب الذي تم إنشاء mem منه أو على الجانب عبر HIDL RPC.

// Same includes as above

sp<IMemory> memory = mapMemory(mem);
void* data = memory->getPointer();
memory->update();
// update memory however you wish after calling update and before calling commit
data[0] = 42;
memory->commit();
// …
memory->update(); // the same memory can be updated multiple times
// …
memory->commit();

واحدة

يمكن تمرير الواجهات ككائنات. يمكن استخدام كلمة واجهة كسكر تركيبي للنوع android.hidl.base@1.0::IBase فضلاً عن ذلك، يتم تحديد الواجهة الحالية وأي واجهات مستوردة كنوع.

يجب أن تكون المتغيرات التي تحمل الواجهات مؤشرات قوية: sp<IName> دوال HIDL التي تأخذ معلمات الواجهة تحويل المؤشرات الأولية إلى مؤشرات قوية، ما يؤدي إلى سلوك غير بديهيّ (يمكن أن يتم محو المؤشر بشكل غير متوقع). لتجنّب المشاكل، احرص دائمًا على تخزين HIDL الواجهات الخاصة بـ sp<>.