إنشاء واجهة 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 لعناصر الضبط ذات الصلة بالبلوتوث.