إنشاء واجهة HAL

يجب عليك استخدام HIDL لوصف كافة إشارات البناء المستخدمة للتجميع المشروط لإطار العمل. يجب تجميع إشارات البناء ذات الصلة وتضمينها في ملف .hal واحد. يتضمن استخدام HIDL لتحديد عناصر التكوين الفوائد التالية:

  • تم إصداره (لإضافة عناصر تكوين جديدة، يجب على البائعين/مصنعي المعدات الأصلية توسيع طبقة HAL بشكل صريح)
  • وثق بشكل جيد
  • التحكم في الوصول باستخدام SELinux
  • التحقق من سلامة عناصر التكوين من خلال مجموعة اختبار البائع (التحقق من النطاق، والتحقق من التبعية بين العناصر، وما إلى ذلك)
  • واجهات برمجة التطبيقات التي تم إنشاؤها تلقائيًا في كل من C++ وJava

تحديد إشارات البناء التي يستخدمها الإطار

ابدأ بتحديد تكوينات البناء المستخدمة لتجميع إطار العمل بشكل مشروط، ثم التخلي عن التكوينات القديمة لجعل المجموعة أصغر. على سبيل المثال، تم تحديد المجموعة التالية من علامات البناء لـ surfaceflinger :

  • TARGET_USES_HWC2
  • TARGET_BOARD_PLATFORM
  • TARGET_DISABLE_TRIPLE_BUFFERING
  • TARGET_FORCE_HWC_FOR_VIRTUAL_DISPLAYS
  • NUM_FRAMEBUFFER_SURFACE_BUFFERS
  • TARGET_RUNNING_WITHOUT_SYNC_FRAMEWORK
  • VSYNC_EVENT_PHASE_OFFSET_NS
  • SF_VSYNC_EVENT_PHASE_OFFSET_NS
  • PRESENT_TIME_OFFSET_FROM_VSYNC_NS
  • MAX_VIRTUAL_DISPLAY_DIMENSION

إنشاء واجهة HAL

يتم الوصول إلى تكوينات البناء لنظام فرعي من خلال واجهة HAL، بينما يتم تجميع واجهات إعطاء قيم التكوين في حزمة HAL android.hardware.configstore (حاليًا في الإصدار 1.0). على سبيل المثال، لإنشاء ملف واجهة HAL لـ surfaceflinger ، في hardware/interfaces/configstore/1.0/ISurfaceFlingerConfigs.hal :

package android.hardware.configstore@1.0;

interface ISurfaceFlingerConfigs {
    // TO-BE-FILLED-BELOW
};

بعد إنشاء ملف .hal ، قم بتشغيل hardware/interfaces/update-makefiles.sh لإضافة ملف .hal الجديد إلى ملفات Android.bp و Android.mk .

إضافة وظائف لبناء الأعلام

لكل علامة بناء، قم بإضافة وظيفة جديدة إلى الواجهة. على سبيل المثال، في hardware/interfaces/configstore/1.0/ISurfaceFlingerConfigs.hal :

interface ISurfaceFlingerConfigs {
    disableTripleBuffering() generates(OptionalBool ret);
    forceHwcForVirtualDisplays() generates(OptionalBool ret);
    enum NumBuffers: uint8_t {
        USE_DEFAULT = 0,
        TWO = 2,
        THREE = 3,
    };
    numFramebufferSurfaceBuffers() generates(NumBuffers ret);
    runWithoutSyncFramework() generates(OptionalBool ret);
    vsyncEventPhaseOffsetNs generates (OptionalUInt64 ret);
    presentTimeOffsetFromSyncNs generates (OptionalUInt64 ret);
    maxVirtualDisplayDimension() generates(OptionalInt32 ret);
};

عند إضافة وظيفة:

  • كن موجزا مع الأسماء. تجنب تحويل أسماء متغيرات makefile إلى أسماء وظائف وتذكر أن البادئات TARGET_ و BOARD_ لم تعد ضرورية.
  • أضف تعليقات. ساعد المطورين على فهم الغرض من عنصر التكوين، وكيفية تغيير سلوك إطار العمل، والقيم الصالحة، والمعلومات الأخرى ذات الصلة.

يمكن أن تكون أنواع إرجاع الوظائف Optional[Bool|String|Int32|UInt32|Int64|UInt64] . يتم تعريف الأنواع في types.hal في نفس الدليل ويتم تغليف القيم الأولية بحقل يشير إلى ما إذا كانت القيمة محددة بواسطة HAL أم لا؛ إذا لم يكن الأمر كذلك، يتم استخدام القيمة الافتراضية.

struct OptionalString {
    bool specified;
    string value;
};

عندما يكون ذلك مناسبًا، حدد التعداد الذي يمثل أفضل نوع لعنصر التكوين واستخدم هذا التعداد كنوع الإرجاع. في المثال أعلاه، تم تعريف تعداد NumBuffers لتحديد عدد القيم الصالحة. عند تحديد أنواع البيانات المخصصة هذه، قم بإضافة حقل أو قيمة تعداد (على سبيل المثال، USE_DEFAULT ) للإشارة إلى ما إذا كانت القيمة محددة/غير محددة بواسطة HAL.

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

على سبيل المثال، خيار تجميع علامتي بناء في بنية واحدة في hardware/interfaces/configstore/1.0/ISurfaceFlingerConfigs.hal هو:

 interface ISurfaceFlingerConfigs {
    // other functions here
    struct SyncConfigs {
        OptionalInt64 vsyncEventPhaseoffsetNs;
        OptionalInt64 presentTimeoffsetFromSyncNs;
    };
    getSyncConfigs() generates (SyncConfigs ret);
    // other functions here
};

بدائل لوظيفة HAL واحدة

كبديل لاستخدام دالة HAL واحدة لجميع إشارات البناء، توفر واجهة HAL أيضًا وظائف بسيطة مثل getBoolean(string key) و getInteger(string key) . يتم تخزين أزواج key=value الفعلية في ملفات منفصلة وتوفر خدمة HAL القيم من خلال قراءة/تحليل تلك الملفات.

على الرغم من سهولة تعريف هذا الأسلوب، إلا أنه لا يتضمن المزايا التي يوفرها HIDL (الإصدار القسري، وسهولة التوثيق، والتحكم في الوصول) وبالتالي لا يوصى به.

واجهات واحدة ومتعددة

يقدم تصميم واجهة HAL لعناصر التكوين خيارين:

  • واجهة واحدة تغطي جميع عناصر التكوين
  • واجهات متعددة، تغطي كل واحدة منها مجموعة من عناصر التكوين ذات الصلة

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

وبسبب هذه المشكلات، يستخدم Android واجهات متعددة مع واجهة HAL واحدة لمجموعة من عناصر التكوين ذات الصلة. على سبيل المثال، ISurfaceflingerConfigs لعناصر التكوين المتعلقة بـ surfaceflinger ، و IBluetoothConfigs لعناصر التكوين المرتبطة بـ Bluetooth.