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

يصف هذا القسم أنواع بيانات HIDL. للحصول على تفاصيل التنفيذ، يمكنك الاطّلاع على HIDL C++ (لـ C++ ) التنفيذ) أو HIDL Java (لعمليات تنفيذ Java).

تشمل أوجه التشابه مع C++ ما يلي:

  • تستخدم structs بنية C++، unions تدعم بنية C++ تلقائيًا. يجب تسمية كلاهما؛ لا يتم دعم التراكيب والاتحادات المجهولة.
  • يُسمح بـ Typedefs في HIDL (كما في C++).
  • يُسمح بالتعليقات ذات نمط C++ ويتم نسخها إلى ملف الرأس الذي تم إنشاؤه.

تشمل أوجه التشابه مع Java ما يلي:

  • لكل ملف، يحدد HIDL مساحة اسم بنمط Java يجب أن تبدأ بـ android.hardware. مساحة الاسم C++ التي تم إنشاؤها هي ::android::hardware::…
  • وتكون جميع تعريفات الملف مضمنة في نمط Java برنامج تضمين interface.
  • تتبع تعريفات صفيف HIDL نمط Java، وليس نمط C++. مثال:
    struct Point {
        int32_t x;
        int32_t y;
    };
    Point[3] triangle;   // sized array
    
  • تشبه التعليقات تنسيق javadoc.

تمثيل البيانات

struct أو union يتكون من التنسيق العادي (مجموعة فرعية من متطلبات أنواع البيانات القديمة) لها ذاكرة متسقة بتنسيق تم إنشاؤه بكود C++ تم إنشاؤه، وتم فرضه بسمات المحاذاة الصريحة على struct وunion عضو

أنواع HIDL الأساسية، بالإضافة إلى enum وbitfield (التي تستمد دائمًا من الأنواع الأولية)، تعين بأنواع C++ القياسية مثل std::uint32_t من cstdint.

نظرًا لأن Java لا تدعم الأنواع غير الموقعة، يتم تعيين أنواع HIDL غير الموقَّعة إلى نوع جافا الموقَّع المقابل. يتم ربط البِنى بفئات Java. تُعيِّن المصفوفات إلى مصفوفات Java، unions غير معتمَدة حاليًا في Java. تُخزَّن السلاسل داخليًا بتنسيق UTF8. نظرًا لأن Java يدعم سلاسل UTF16 فقط، وقيم السلسلة المرسلة إلى تنفيذ Java أو منه وقد لا تكون متطابقة عند إعادة الترجمة نظرًا لأن مجموعات الأحرف لا دائمًا بسلاسة.

يتم وضع علامة const على البيانات المُستلَمة عبر IPC في لغة C++ وهي متاحة حاليًا. هي ذاكرة للقراءة فقط تبقى طوال مدة استدعاء الدالة. البيانات المستلمة عبر IPC في Java تم نسخها بالفعل إلى كائنات Java، لذلك يمكن بدون نسخ إضافية (ويمكن تعديلها).

التعليقات التوضيحية

يمكن إضافة تعليقات توضيحية بنمط Java إلى تعريفات الكتابة. التعليقات التوضيحية هي بواسطة الواجهة الخلفية لـ Vendor Test Suite (VTS) من برنامج التحويل البرمجي HIDL ولكن لا شيء من هذه التعليقات التوضيحية التحليلية يفهمها مترجم HIDL. بدلاً من ذلك، تتم معالجة تعليقات VTS المحلَّلة بواسطة محوّل VTS Compiler (VTSC).

تستخدم التعليقات التوضيحية بنية Java: @annotation أو @annotation(value) أو @annotation(id=value, id=value…) حيث قد تكون القيمة إما تعبيرًا ثابتًا أو سلسلة أو قائمة من القيم داخل {}، تمامًا كما في Java. تعليقات توضيحية متعددة تحمل الاسم نفسه بالعنصر نفسه.

إعادة توجيه نماذج البيان

في HIDL، قد لا يتم الإعلان عن الهياكل إلى الأمام، مما يجعل تحديد المستخدم أنواع بيانات ذاتية المرجعية مستحيلة (على سبيل المثال، لا يمكنك وصف قائمة مرتبطة أو شجرة في HIDL). تتمتع معظم HALs الحالية (التي تسبق Android 8.x) باستخدام محدود إعادة توجيه نماذج البيانات، والتي يمكن إزالتها عن طريق إعادة ترتيب بنية البيانات والإقرارات.

يسمح هذا القيد بنسخ هياكل البيانات حسب القيمة باستخدام بدلاً من تتبع قيم المؤشر التي قد تظهر الأوقات في هيكل بيانات ذاتي المرجعية. إذا تم تمرير نفس البيانات مرتين، كما هو الحال مع مَعلمتَي طريقة أو vec<T> تشير إلى لنفس البيانات، فيتم عمل نسختين منفصلتين وتسليمها.

التعريفات المضمّنة

يدعم HIDL التعريفات المُدمَجة على أي عدد من المستويات (باستخدام مستوى واحد) الاستثناءات المذكورة أدناه). مثلاً:

interface IFoo {
    uint32_t[3][4][5][6] multidimArray;

    vec<vec<vec<int8_t>>> multidimVector;

    vec<bool[4]> arrayVec;

    struct foo {
        struct bar {
            uint32_t val;
        };
        bar b;
    }
    struct baz {
        foo f;
        foo.bar fb; // HIDL uses dots to access nested type names
    }
    …

والاستثناء هو أن أنواع الواجهات لا يمكن تضمينها إلا في vec<T> ومستوى عمق واحد فقط (لا vec<vec<IFoo>>).

بنية المؤشر الأولية

لا تستخدم لغة HIDL * ولا تتوافق مع المرونة الكاملة لمؤشرات C/C++ الأولية. للحصول على تفاصيل حول كيفية تلخيص HIDL المؤشرات والصفائف/المتجهات، راجع vec<T> نموذجك.

واجهات

هناك استخدامان للكلمة الرئيسية interface.

  • يتم فتح تعريف الواجهة في ملف .hal
  • ويمكن استخدامه كنوع خاص في حقول الهيكل/الاتحاد ومعلمات الطريقة والمرتجعات. ويُنظر إليها على أنها واجهة عامة ومرادفة لـ android.hidl.base@1.0::IBase

على سبيل المثال، يستخدم IServiceManager الطريقة التالية:

get(string fqName, string name) generates (interface service);

تعدك هذه الطريقة بالبحث عن بعض الواجهات حسب الاسم. من المهم أيضًا مماثلة لاستبدال الواجهة بـ android.hidl.base@1.0::IBase.

يمكن تمرير الواجهات بطريقتين فقط: كمعلمات المستوى الأعلى أو الأعضاء في vec<IMyInterface>. لا يمكن أن يكونوا أعضاء في vecs أو هياكل أو صفائف أو اتحادات متداخلة.

MQDescriptorSync وMQDescriptorUnsync

النوعان MQDescriptorSync وMQDescriptorUnsync تمرير أدوات وصف قائمة انتظار الرسائل السريعة (FMQ) المتزامنة أو غير المتزامنة عبر واجهة HIDL. للحصول على التفاصيل، يمكنك مراجعة HIDL C++ (قنوات FMQ غير مناسبة متوفرة في Java).

نوع الذاكرة

يُستخدَم النوع memory لتمثيل الذكريات المشتركة التي لم يتم تخصيصها في HIDL. وهو متاح فقط بلغة C++. ويمكن استخدام قيمة من هذا النوع في طرف مستلِم لإعداد كائن IMemory، وربط الذاكرة وتسهيل استخدامها. للحصول على التفاصيل، يمكنك مراجعة HIDL C++.

تحذير: تم وضع بيانات منظَّمة في ملف تمت مشاركته. يجب أن تكون الذاكرة من النوع الذي لا يتغير تنسيقه أبدًا طوال فترة بقاء إصدار واجهة يجتاز memory. خلاف ذلك، يمكن أن تتأثر HALs مشكلات التوافق الفادحة.

نوع المؤشر

النوع pointer مخصّص للاستخدام الداخلي فقط.

Bitfield<T> نموذج النوع

bitfield<T> حيث يكون T يشير التعداد المحدّد من قِبل المستخدم إلى أنّ القيمة هي على مستوى البت أو للدالة قيم التعداد المحددة في T. في التعليمة البرمجية التي تم إنشاؤها، يظهر bitfield<T> كنوع أساسي لـ T. بالنسبة مثال:

enum Flag : uint8_t {
    HAS_FOO = 1 << 0,
    HAS_BAR = 1 << 1,
    HAS_BAZ = 1 << 2
};
typedef bitfield<Flag> Flags;
setFlags(Flags flags) generates (bool success);

يتعامل برنامج التجميع مع نوع العلامات تمامًا مثل uint8_t.

سبب عدم الاستخدام (u)int8_t/(u)int16_t/(u)int32_t/(u)int64_t؟ يوفر استخدام bitfield معلومات HAL إضافية للقارئ، الذي يعرف الآن أن setFlags يأخذ قيمة بت -OR على مستوى البت Flag (أي أن الاتصال بـ setFlags الذي يحمل الرقم 16 غير صالح). بدون bitfield، لا يتم نقل هذه المعلومات إلا من خلال المستندات. ضِمن بالإضافة إلى ذلك، يمكن لـ VTS التحقق مما إذا كانت قيمة العلامات هي استخدام OR على مستوى البت.

مقابض الأنواع الأساسية

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

دلالات HIDL هي نسخ حسب القيمة، مما يعني أنه يتم نسخ المعلمات. أي أجزاء كبيرة من البيانات، أو البيانات التي يجب مشاركتها بين العمليات (مثل سياج المزامنة)، يتم التعامل معها من خلال تمرير أدوات وصف الملفات التي تشير إلى العناصر الثابتة: ashmem للذاكرة المشتركة أو الملفات الفعلية أي شيء آخر يمكن أن يختفي خلف واصف الملف. برنامج تشغيل الربط ويكرر واصف الملف في العملية الأخرى.

اسم_المعرِّف_الأصلي

يتوافق Android مع native_handle_t، وهو مفهوم عام للاسم المعرِّف. محددة في libcutils.

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;

الاسم المعرِّف الأصلي هو مجموعة من الكلمات الرئيسية التي يتم استخدامها وأدوات وصف الملفات التي يتم تمريرها. حسب القيمة. يمكن تخزين واصف ملف واحد في اسم معرِّف أصلي باستخدام بدون إدخال عدد صحيح وصف ملف واحد. الأسماء المعرِّفة الخاصة بالتمرير باستخدام الأسماء المعرِّفة الأصلية مغلفة بالنوع الأساسي handle إلى ضمان أن تكون الأسماء المعرِّفة متوفرة مباشرةً في شهادة HIDL.

بما أنّ حجم native_handle_t متغيّر، لا يمكن تضمينه. مباشرةً في هيكل. يقوم حقل المقبض بإنشاء مؤشر إلى وحدة التي تم تخصيصها: native_handle_t.

في الإصدارات السابقة من Android، كان يتم إنشاء الأسماء المعرِّفة باستخدام الأسماء المعرِّفة نفسها الدوال الموجودة في libcutils. في الإصدار Android 8.0 والإصدارات الأحدث، يتم نسخ هذه الدوال إلى مساحة الاسم android::hardware::hidl أو تم نقلها إلى NDK. مؤسسة HIDL يقوم الرمز المنشأ تلقائيًا بتسلسل هذه الدوال وإلغاء تسلسلها تلقائيًا، دون مشاركة من التعليمات البرمجية المكتوبة من المستخدم.

ملكية أداة وصف الملف والاسم المعرِّف

عند استدعاء طريقة واجهة HIDL التي تمرر (أو تعرض) كائن hidl_handle (إما من المستوى الأعلى أو جزء من نوع مركّب) تكون ملكية أدوات وصف الملفات المضمَّنة فيها على النحو التالي:

  • المتصل الذي يمرّر كائن hidl_handle باعتباره تحتفظ الوسيطة بملكية أدوات وصف الملفات المضمنة في native_handle_t يتم التفافه؛ يجب أن يغلق المتصل هذا الملف. واصفات عند الانتهاء منها.
  • العملية التي تؤدي إلى عرض hidl_handle (عن طريق تمريره إلى دالة _cb) يحتفظ بملكية واصفات الملفات المضمنة في native_handle_t ملفوفة بواسطة كائن؛ يجب أن تغلق العملية وصفات الملفات هذه عند الانتهاء منها.
  • يجب أن يستوفي النقل الذي يتلقى hidl_handle ملكية واصفات الملفات داخل native_handle_t ملفوفًا بالكائن؛ يمكن للمستلم استخدام واصفات الملفات هذه كما هو أثناء استدعاء المعاملة، ولكن يجب استنساخ الاسم المعرِّف الأصلي لاستخدام الملف واصفات تتجاوز معاودة الاتصال. تتصل وسيلة النقل تلقائيًا close() لوصفات الملفات عند إتمام المعاملة.

لا يدعم HIDL الأسماء المعرّفة في Java (لأن Java لا تدعم الأسماء المعرّفة في الكل).

الصفائف بحجم الصفائف

بالنسبة للصفائف ذات الحجم الكبير في هيكليات HIDL، يمكن أن تكون عناصرها من أي نوع هيكل يمكن أن يحتوي على:

struct foo {
uint32_t[3] x; // array is contained in foo
};

الأوتار

تظهر السلاسل بشكل مختلف في C++ وJava، ولكن النقل الأساسي نوع التخزين عبارة عن هيكل C++. للحصول على التفاصيل، يمكنك مراجعة أنواع بيانات HIDL C++ أو أنواع بيانات HIDL في Java:

ملاحظة: تمرير سلسلة من أو إلى Java من خلال تؤدي واجهة HIDL (بما في ذلك Java إلى Java) إلى إجراء إحالات ناجحة لمجموعة الأحرف قد لا يحتفظ بالترميز الأصلي.

vec<T> نموذج النوع

يمثّل النموذج vec<T> مخزنًا مؤقتًا بحجم متغيّر. التي تحتوي على المثيلات T.

يمكن أن تكون القيمة "T" واحدًا مما يلي:

  • الأنواع الأولية (مثل uint32_t)
  • الأوتار
  • تعدادات من تحديد المستخدم
  • بنى من تحديد المستخدم
  • الواجهات أو الكلمة الرئيسية interface (vec<IFoo>، vec<interface> متوافقة كمَعلمة من المستوى الأعلى فقط)
  • الأسماء المعرِّفة
  • Bitfield<U>
  • vec<U>، حيث تظهر U في هذه القائمة باستثناء الواجهة (على سبيل المثال، "vec<vec<IFoo>>" غير متاح.)
  • U[] (مصفوفة بحجم U)، حيث تظهر U في هذه القائمة باستثناء الواجهة

الأنواع من تحديد المستخدم

يصف هذا القسم الأنواع التي حددها المستخدم.

Enum

لا يتوافق HIDL مع التعداد المجهول. بخلاف ذلك، تشبه التعدادات في HIDL إلى C++11:

enum name : type { enumerator , enumerator = constexpr , …  }

يتم تعريف التعداد الأساسي بالنظر إلى أحد أنواع الأعداد الصحيحة في HIDL. إذا كانت الإجابة "لا" يتم تحديد قيمة للتعداد الأول للتعداد استنادًا إلى عدد صحيح. النوع، يتم تعيين القيمة الافتراضية على 0. إذا لم يتم تحديد أي قيمة لعدّ لاحق، يتم تعيين القيمة الافتراضية على القيمة السابقة زائد واحد. مثلاً:

// RED == 0
// BLUE == 4 (GREEN + 1)
enum Color : uint32_t { RED, GREEN = 3, BLUE }

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

// ULTRAVIOLET == 5 (Color:BLUE + 1)
enum FullSpectrumColor : Color { ULTRAVIOLET }

تحذير: يعمل اكتساب التعداد بشكل عكسي من معظم أنواع الوراثة الأخرى. لا يمكن استخدام قيمة تعداد فرعي كـ قيمة التعداد الأصل. وذلك لأن التعداد الفرعي يحتوي على قيم أكثر من أحد الوالدين. ومع ذلك، يمكن استخدام قيمة التعداد الأصلي بأمان كقيمة تعداد فرعي. وذلك لأن قيم التعداد الفرعي هي في التعريف مجموعة زائدة من قيم التعداد الأصلي. ضع ذلك في الاعتبار عند تصميم الواجهات لأن هذا يعني الأنواع التي تشير إلى لا يمكن أن تشير التعدادات الأصل إلى التعدادات الثانوية في التكرارات اللاحقة من واجهة pyplot.

يُشار إلى قيم التعدادات ببناء جملة النقطتين (وليس بناء جملة النقاط الأنواع المتداخلة). البنية هي Type:VALUE_NAME. لا حاجة إلى التحديد إذا تمت الإشارة إلى القيمة في نوع التعداد نفسه أو الأنواع الفرعية نفسها. مثال:

enum Grayscale : uint32_t { BLACK = 0, WHITE = BLACK + 1 };
enum Color : Grayscale { RED = WHITE + 1 };
enum Unrelated : uint32_t { FOO = Color:RED + 1 };

بدءًا من Android 10، سيكون للتعدادات سمة len التي يمكن استخدامها في التعبيرات الثابتة. MyEnum::len هو إجمالي عدد الإدخالات في ذلك التعداد. ويختلف هذا عن العدد الإجمالي للقيم، والذي قد يكون أصغر عندما فسيتم تكرارها.

بنية

لا يتوافق HIDL مع الإطارات المجهولة. خلاف ذلك، تكون الهياكل في HIDL تشبه C.

لا يدعم HIDL هياكل البيانات المتغيرة الطول الموجودة بالكامل داخل هيكل. يتضمن هذا الصفيفة ذات الطول غير المحدد والتي تُستخدم أحيانًا آخر حقل من الهيكل في لغة C/C++ (يُرى أحيانًا بحجم [0]). تمثّل HIDL vec<T> الحجم الديناميكي. الصفائف ذات البيانات المخزنة في مخزن مؤقت منفصل؛ يتم تمثيل مثل هذه الحالات مع مثال لـ vec<T> في struct.

وبالمثل، يمكن تضمين string في struct. (الموارد الاحتياطية المرتبطة منفصلة). في لغة C++ التي تم إنشاؤها، توجد حالات HIDL ويتم تمثيل نوع المقبض عن طريق مؤشر إلى الاسم المعرِّف الأصلي الفعلي فإن مثيلات نوع البيانات الأساسية هي value-length.

Union

لا يدعم HIDL النقابات المجهولة. خلاف ذلك، تكون الاتحادات مشابهة لـ C.

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

union UnionType {
uint32_t a;
//  vec<uint32_t> r;  // Error: can't contain a vec<T>
uint8_t b;1
};
fun8(UnionType info); // Legal

يمكن أيضًا الإعلان عن الاتحادات داخل الهياكل. مثلاً:

struct MyStruct {
    union MyUnion {
      uint32_t a;
      uint8_t b;
    }; // declares type but not member

    union MyUnion2 {
      uint32_t a;
      uint8_t b;
    } data; // declares type but not member
  }