طبقة تجريد الأجهزة (HAL) لكاميرا المركبات

يحتوي Android على طبقة تجريد أجهزة HIDL الخاصة بالسيارات (HAL) والتي تتيح التقاط الصور وعرضها في وقت مبكر جدًا من عملية تمهيد Android ويستمر في العمل طوال عمر النظام. تتضمن قناة HAL كدسة نظام الرؤية الخارجية (EVS) التي تُستخدم عادةً لدعم الرؤية الخلفية أجهزة العرض داخل المركبات التي تعمل بنظام التشغيل Android وشاشة العرض المحيطي أنظمة الترفيه والمعلومات (IVI). تمكّن EVS أيضًا من تنفيذ الميزات المتقدّمة في تطبيقات المستخدم.

يتضمّن Android أيضًا برنامج تشغيل الالتقاط والعرض الخاص بـ EVS (في /hardware/interfaces/automotive/evs/1.0). إنّه إمكانية إنشاء تطبيق كاميرا للرؤية الخلفية في أعلى نظام Android الحالي خدمات الكاميرا والعرض، فمن المحتمل أن يعمل هذا التطبيق متأخرًا جدًا في عملية تشغيل Android. يعمل استخدام طبقة تجريد الأجهزة (HAL) على تمكين واجهة سلسة ويوضح ما يجب على المصنّع الأصلي للجهاز تنفيذه لدعم حزمة EVS.

مكونات النظام

تشمل خدمة EVS مكونات النظام التالية:

نظام EVS
الرسم التخطيطي للمكونات
الشكل 1 نظرة عامة على مكوّنات نظام EVS

تطبيق EVS

نموذج لتطبيق C++ EVS يتم استخدام (/packages/services/Car/evs/app) كمرجع. التنفيذ. هذا التطبيق مسؤول عن طلب إطارات الفيديو من مدير EVS وإرسال الإطارات النهائية لعرضها مرة أخرى إلى مدير EVS. ومن المتوقع أن يتم بدؤها فور توفر EVS وخدمة السيارة، مستهدَف خلال ثانيتين (2) من تشغيل الجهاز. يمكن للمصنّعين الأصليين للأجهزة تعديل EVS أو استبدالها. التطبيق كما تريد.

أداة إدارة EVS

يوفّر مدير EVS (/packages/services/Car/evs/manager) الكتل البرمجية الإنشائية التي يحتاجها تطبيق EVS لتنفيذ أي شيء من العرض بكاميرا خلفية بسيطة إلى عرض 6DOF من كاميرات متعددة. واجهته من خلال HIDL، وهي مصمّمة لقبول العديد من العملاء المتزامنين. يمكن للتطبيقات والخدمات الأخرى (على وجه التحديد خدمة السيارة) الاستعلام عن خدمة EVS حالة المدير لمعرفة الوقت الذي يكون فيه نظام EVS نشطًا.

واجهة EVS HIDL

يتم تحديد نظام EVS، الذي يضم الكاميرا وعناصر العرض، في حزمة android.hardware.automotive.evs نموذج التنفيذ التي تمارس الواجهة (تنشئ صور اختبار اصطناعية وتتحقق من صحة الصور تجعل الذهاب والعودة) متوفرة في /hardware/interfaces/automotive/evs/1.0/default

المصنّع الأصلي للجهاز مسؤول عن تنفيذ واجهة برمجة التطبيقات التي يتم التعبير عنها من خلال ملفات .hal في /hardware/interfaces/automotive/evs. وتتميز عمليات التنفيذ هذه عن تكوين البيانات وجمعها من الكاميرات من خلال مخازن الذاكرة المشتركة التي يمكن التعرف عليها من خلال Gralloc. شاشة العرض جانب التنفيذ هو توفير مخزن مؤقت مشترك للذاكرة يمكن ملؤها بواسطة التطبيق (عادةً مع عرض EGL) وتقديم الإطارات النهائية مفضّلة على أيّ شيء آخر قد يرغب في الظهور الشاشة المادية. قد يتم تخزين عمليات تنفيذ المورِّد لواجهة EVS أقل من /vendor/… /device/… أو hardware/… (مثل، /hardware/[vendor]/[platform]/evs).

برامج تشغيل النواة

يتطلب الجهاز الذي يتوافق مع حزمة EVS برامج تشغيل النواة. بدلاً من بعد إنشاء برامج تشغيل جديدة، يكون لدى المصنّعين الأصليين للأجهزة خيار دعم الميزات المطلوبة في EVS من خلال برامج تشغيل أجهزة العرض أو الكاميرا الحالية. قد تكون إعادة استخدام السائقين مفيدة، لا سيما في برامج تشغيل الشاشات حيث قد يتم عرض الصور تنسيقها مع سلاسل المحادثات النشطة الأخرى. يتضمن Android 8.0 إصدارًا إلى الإصدار v4l2 نموذج برنامج التشغيل (في packages/services/Car/evs/sampleDriver) والذي يعتمد دعم الإصدار 4l2 على النواة وعلى SurfaceFlinger لتقديم صورة إخراج.

وصف واجهة جهاز EVS

يصف هذا القسم HAL. يُتوقع من البائعين تقديم استخدام واجهة برمجة التطبيقات هذه لتتناسب مع أجهزتهم.

معادلة IEvsEnumerator

هذا الكائن مسؤول عن تعداد أجهزة EVS المتاحة في (كاميرا واحدة أو أكثر وجهاز عرض واحد).

getCameraList() generates (vec<CameraDesc> cameras);

تعرض متجهًا يحتوي على أوصاف لجميع الكاميرات في النظام. من المهم أن مجموعة الكاميرات ثابتة ويمكن معرفتها في وقت التشغيل. للحصول على تفاصيل حول أوصاف الكاميرا، يُرجى الاطّلاع على CameraDesc.

openCamera(string camera_id) generates (IEvsCamera camera);

الحصول على كائن واجهة يُستخدَم للتفاعل مع كاميرا معيّنة التي يتم تحديدها بواسطة سلسلة camera_id الفريدة. لعرض قيمة فارغة (NULL) عند الفشل. ولن تنجح محاولات إعادة فتح كاميرا مفتوحة. لتجنب العرق الحالات المرتبطة بتشغيل التطبيق وإيقافه، أي إعادة فتح الكاميرا إغلاق المثيل السابق بحيث يمكن تنفيذ الطلب الجديد. حاسمة يجب وضع مثيل الكاميرا المؤقت بهذه الطريقة في مكان غير نشط الأخيرة، وهي في انتظار التلف النهائي والاستجابة لأي طلب بالتأثير في حالة الكاميرا مع رمز الإرجاع OWNERSHIP_LOST.

closeCamera(IEvsCamera camera);

تصدر واجهة IEvs Camera (وعكس مكالمة واحدة (openCamera())). يجب أن يكون بث الفيديو بالكاميرا تم إيقاف الاتصال بـ stopVideoStream() قبل الاتصال بـ closeCamera.

openDisplay() generates (IEvsDisplay display);

الحصول على كائن واجهة يُستخدَم للتفاعل حصريًا مع واجهة النظام شاشة EVS لا يجوز أن يكون لدى عميل واحد سوى مثيل وظيفي من IEvsDisplay في الوقت. وعلى غرار السلوك المفتوح الصريح الموضَّح في openCamera، إمكانية إنشاء كائن IEvsDisplay جديد في أي وقت وإيقاف أي عنصر سابق الحالات. تظل المثيلات غير الصالحة موجودة وتستجيب لاستدعاءات الدوال من أصحابها، ولكن يجب عدم إجراء أي عمليات تبديل عند توقُّفها. في النهاية، من المتوقّع أن يلاحظ تطبيق العميل الخطأ OWNERSHIP_LOST. إرجاع الرموز وإغلاق الواجهة غير النشطة ثم تحريرها.

closeDisplay(IEvsDisplay display);

إصدار واجهة IEvsDisplay (وعكس مكالمة واحدة (openDisplay())). الموارد الاحتياطية المعلقة التي تم الحصول عليها مع يجب إعادة مكالمات "getTargetBuffer()" إلى الشاشة قبل إغلاق الشاشة.

getDisplayState() generates (DisplayState state);

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

struct CameraDesc {
    string      camera_id;
    int32       vendor_flags;       // Opaque value
}
  • camera_id يشير ذلك المصطلح إلى سلسلة تحدِّد كاميرا معيّنة بشكل فريد. يمكن أن يكون اسم الجهاز النواة أو اسمًا للجهاز، مثل الرؤية الخلفية. يتم اختيار قيمة هذه السلسلة من خلال تنفيذ HAL واستخدامها بشكل غير شفاف بواسطة المكدس أعلاه.
  • vendor_flags طريقة لتمرير كاميرا متخصصة معلومات مبهمة من برنامج التشغيل إلى تطبيق EVS المخصص. لقد تم تمريره لا يتم تفسيرها من برنامج التشغيل إلى تطبيق EVS، والتي يمكن تجاهلها مجانًا بها.

كاميرا IEvsكاميرا

يمثل هذا الكائن كاميرا واحدة وهو الواجهة الأساسية التقاط الصور.

getCameraInfo() generates (CameraDesc info);

عرض CameraDesc من هذه الكاميرا

setMaxFramesInFlight(int32 bufferCount) generates (EvsResult result);

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

إذا تعذر استيعاب عدد المخزن المؤقت المطلوب، تقوم الدالة بعرض BUFFER_NOT_AVAILABLE أو رمز خطأ آخر ذي صلة وفي هذه الحالة، يستمر النظام في العمل بالقيمة المحددة مسبقًا.

startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);

تطلب تسليم إطارات كاميرا EVS من هذه الكاميرا. IEvs CameraStream تبدأ في تلقي مكالمات دورية بإطارات صور جديدة حتى تم الاتصال بـ "stopVideoStream()". يجب أن يبدأ تسليم الإطارات خلال 500 ملي ثانية من اتصال startVideoStream وبعد البدء، يجب تم إنشاؤها بمعدل 10 لقطات في الثانية على الأقل. الوقت اللازم لبدء بث الفيديو بشكل فعّال من متطلبات وقت بدء تشغيل كاميرا الرؤية الخلفية. إذا كانت لم يبدأ البث المباشر، يجب عرض رمز خطأ. وبخلاف ذلك، يتم إرجاع "موافق".

oneway doneWithFrame(BufferDesc buffer);

لعرض إطار تم إرساله إلى IEvs CameraStream. عند الانتهاء استخدام إطار يتم إرساله إلى واجهة IEvscameraStream، فيجب أن يكون الإطار وإعادته إلى IEvscamera لإعادة استخدامه. يتم استخدام عدد صغير ومحدود من الموارد الاحتياطية المتاحة (قد تكون صغيرة مثل واحد)، وإذا تم استنفاد العرض، ويتم تسليم الإطارات حتى يتم إرجاع مخزن مؤقت، مما قد يؤدي إلى الإطارات التي تم تخطيها (يشير المخزن المؤقت ذو المقبض الفارغة إلى نهاية البث لا يلزم إرجاعها من خلال هذه الدالة). عرض "موافق" عند النجاح، أو رمز الخطأ المناسب الذي قد يتضمن INVALID_ARG أو BUFFER_NOT_AVAILABLE

stopVideoStream();

إيقاف عرض إطارات كاميرا EVS. ونظرًا لأن التسليم غير متزامن، قد تستمر الإطارات في الظهور لبعض الوقت بعد عودة هذه المكالمة. كل إطار إلى أن يتم إرسال إشارة إلى إغلاق البث IEvscameraStream. من القانون طلب stopVideoStream على بث مباشر. تم إيقافها أو لم تبدأ مطلقًا، وفي هذه الحالات يتم تجاهلها.

getExtendedInfo(int32 opaqueIdentifier) generates (int32 value);

تطلب معلومات خاصة ببرنامج التشغيل من تنفيذ HAL. القيم مسموح بها لـ opaqueIdentifier هي خاصة بالسائق، ولكن ما مِن قيمة بنجاح قد يؤدي إلى تعطُّل السائق. يجب أن يعرض برنامج التشغيل القيمة 0 إذا كانت أي أخطاء غير معروفة. opaqueIdentifier

setExtendedInfo(int32 opaqueIdentifier, int32 opaqueValue) generates (EvsResult result);

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

struct BufferDesc {
    uint32  width;      // Units of pixels
    uint32  height;     // Units of pixels
    uint32  stride;     // Units of pixels
    uint32  pixelSize;  // Size of single pixel in bytes
    uint32  format;     // May contain values from android_pixel_format_t
    uint32  usage;      // May contain values from Gralloc.h
    uint32  bufferId;   // Opaque value
    handle  memHandle;  // gralloc memory buffer handle
}

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

  • width عرض الصورة المعروضة بالبكسل.
  • height الارتفاع بالبكسل للصورة المعروضة.
  • stride عدد وحدات البكسل التي يشغلها كل صف في الذاكرة، مع مراعاة أي مساحة متروكة لمحاذاة الصفوف. يتم التعبير عنها بالبكسل للمطابقة والاصطلاح الذي اعتمدته gralloc لأوصاف المورد الاحتياطي.
  • pixelSize عدد وحدات البايت التي تشغلها كل وحدة بكسل على حدة، تمكين حساب الحجم بالبايت اللازمة للتنقل بين الصفوف في صورة (stride بالبايت = stride بالبكسل * pixelSize).
  • format تنسيق البكسل المستخدم في الصورة. التنسيق المقدّم متوافقًا مع تنفيذ OpenGL للنظام الأساسي. لاجتياز الاختبار اختبار التوافق، يجب أن يكون HAL_PIXEL_FORMAT_YCRCB_420_SP مفضّل لاستخدام الكاميرا، ومن المفترض أن يكون RGBA أو BGRA أن يتم تفضيلها للعرض.
  • usage علامات الاستخدام التي يتم ضبطها من خلال عملية تنفيذ HAL. عملاء HAL من المتوقع أن تجتاز هذه الميزات غير المعدلة (للحصول على التفاصيل، راجع Gralloc.h علامة ذات صلة
  • bufferId يشير هذا المصطلح إلى قيمة فريدة يتم تحديدها من خلال تنفيذ HAL على السماح بالتعرّف على المورد الاحتياطي بعد جولة ذهاب وعودة عبر واجهات برمجة تطبيقات HAL. تشير رسالة الأشكال البيانية قد يتم اختيار القيمة المخزنة في هذا الحقل عشوائيًا من خلال تنفيذ HAL.
  • memHandle يشير هذا المصطلح إلى مؤشر المخزن المؤقت الأساسي في الذاكرة على بيانات الصورة. قد يختار تنفيذ HAL تخزين Gralloc مؤشر المخزن المؤقت هنا.

IEvsCameraStream

ينفِّذ العميل هذه الواجهة لتلقي إطار فيديو غير متزامن وعمليات التسليم.

deliverFrame(BufferDesc buffer);

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

على الرغم من أنّ تنسيقات المخزن المؤقت الملكية ممكنة من الناحية الفنية، إلا أنّ التوافق يتطلب الاختبار أن يكون المورد الاحتياطي بأحد التنسيقات الأربعة المتوافقة: NV21 (YCrCb 4:2:0 نصف مستواها)، YV12 (YCrCb 4:2:0 Planar)، YUYV (YCrCb 4:2:2 متداخل)، RGBA (32 بت R:G:B:x) ، BGRA (32 بت B:G:R:x). يجب أن يكون التنسيق المحدد صالحًا مصدر زخرفة GL على تنفيذ GLES على النظام الأساسي

يجب ألا يعتمد التطبيق على أي مراسلات بين الحقل bufferId وmemHandle في بنية BufferDesc قيم bufferId هي خاصة بشكل أساسي بتنفيذ برنامج تشغيل HAL، وقد يستخدمها (ويعيد استخدامها) على النحو الذي تراه مناسبًا.

عرض IEvs

يمثِّل هذا الكائن شاشة Evs ويتحكم في حالة الشاشة وتعالج العرض الفعلي للصور.

getDisplayInfo() generates (DisplayDesc info);

لعرض المعلومات الأساسية حول شاشة EVS التي يوفرها النظام (يمكنك مراجعة DisplayDesc).

setDisplayState(DisplayState state) generates (EvsResult result);

لضبط حالة العرض. ويمكن للعملاء ضبط حالة العرض للتعبير عن الحالة المرغوبة، ويجب أن يقبل تنفيذ HAL طلب أي حالة أثناء أي حالة أخرى، وعلى الرغم من أن الاستجابة قد يتم تجاهل علامة طلبك.

عند التهيئة، يتم تحديد أن تبدأ الشاشة في حالة NOT_VISIBLE، حيث من المتوقّع أن يطلب العميل بعد ذلك حالة VISIBLE_ON_NEXT_FRAME والبدء في تقديم الفيديو. عندما لم تعد الشاشة مطلوبة، فمن المتوقع أن يطلب العميل حالة NOT_VISIBLE بعد تجاوز آخر إطار فيديو.

ويمكن طلب أي ولاية في أي وقت. إذا كانت الشاشة مرئية بالفعل، يجب أن تظل مرئية إذا تم ضبطها على VISIBLE_ON_NEXT_FRAME يعرض دائمًا "موافق" ما لم تكن الحالة المطلوبة قيمة تعداد غير معروفة، وفي هذه الحالة تكون INVALID_ARG عاد.

getDisplayState() generates (DisplayState state);

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

getTargetBuffer() generates (handle bufferHandle);

لعرض مؤشر للمخزن المؤقت للإطارات المرتبط بالشاشة. هذا المورد الاحتياطي مؤمّنًا ومكتوبًا له بواسطة برنامج و/أو GL. يجب إرجاع هذا المخزن المؤقت بالاتصال بـ returnTargetBufferForDisplay() حتى إذا كانت الشاشة لم تعد مرئية.

بالرغم من أنّ تنسيقات المخزن المؤقت المملوكة ممكنة من الناحية الفنية، إلا أن اختبار التوافق يتطلب أن يكون المخزن المؤقت بأحد التنسيقات الأربعة المتوافقة: NV21 (YCrCb 4:2:0 شبه مستوي)، YV12 (YCrCb 4:2:0 Planar)، YUYV (YCrCb 4:2:2 متداخل)، RGBA (32 بت R:G:B:x)، BGRA (32 بت B:G:R:x). يجب أن يكون التنسيق المحدد GL صالح العرض المستهدف في تنفيذ GLES للنظام الأساسي.

عند حدوث خطأ، يتم عرض مخزن مؤقت ذو مؤشر فارغ، ولكن لا يتم يجب إرسالها مرة أخرى إلى returnTargetBufferForDisplay.

returnTargetBufferForDisplay(handle bufferHandle) generates (EvsResult result);

تخبر الشاشة بأنّ المخزن المؤقت جاهز للعرض. تم استرداد الموارد الاحتياطية فقط. من خلال مكالمة إلى getTargetBuffer() صالحة للاستخدام مع هذا ولا يجوز تعديل محتوى BufferDesc من خلال التطبيق العميل. بعد هذا الاستدعاء، لن يعد المخزن المؤقت صالحًا للاستخدام في للعميل. عرض "حسنًا" عند نجاح الإجراء، أو قد يظهر رمز خطأ مناسب بما في ذلك INVALID_ARG أو BUFFER_NOT_AVAILABLE

struct DisplayDesc {
    string  display_id;
    int32   vendor_flags;  // Opaque value
}

وصف الخصائص الأساسية لشاشة EVS والمطلوبة من EVS التنفيذ. إن HAL مسئول عن ملء هذا الهيكل لوصف شاشة EVS. يمكن أن يكون شاشة مادية أو شاشة افتراضية متراكبة أو مختلَطة مع جهاز عرض آخر.

  • display_id سلسلة تعرِّف شاشة العرض بشكلٍ فريد قد يكون هذا هو اسم الجهاز النواة أو اسم الجهاز، مثل الرؤية الخلفية. يتم اختيار قيمة هذه السلسلة بواسطة HAL تطبيقه واستخدامه بشكل غير شفاف بواسطة المكدس أعلاه.
  • vendor_flags طريقة لتمرير كاميرا متخصصة معلومات مبهمة من برنامج التشغيل إلى تطبيق EVS المخصص. لقد تم تمريره لا يتم تفسيرها من برنامج التشغيل إلى تطبيق EVS، والتي يمكن تجاهلها مجانًا بها.
enum DisplayState : uint32 {
    NOT_OPEN,               // Display has not been “opened” yet
    NOT_VISIBLE,            // Display is inhibited
    VISIBLE_ON_NEXT_FRAME,  // Will become visible with next frame
    VISIBLE,                // Display is currently active
    DEAD,                   // Display is not available. Interface should be closed
}

تصف حالة عرض EVS التي يمكن إيقافها (وليس مرئية للسائق) أو مُفعّلة (تُظهر صورة للسائق). يتضمّن حالة عابرة لا تكون فيها الشاشة مرئية بعد ولكن يتم إعدادها لتظهر مع تسليم الإطار التالي للصور من خلال مكالمة واحدة (returnTargetBufferForDisplay())

أداة إدارة EVS

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

تنفذ مدير EVS واجهة برمجة التطبيقات نفسها مثل برامج تشغيل HAL الأساسية توفر خدمة موسّعة من خلال دعم العديد من العملاء المتزامنين (أكثر من يمكن لعميل واحد فتح الكاميرا من خلال مدير EVS واستقبال فيديو تدفق).

مدير EVS
مخطّط واجهة برمجة التطبيقات لأجهزة EVS
الشكل 2. النسخ المطابق لـ EVS Manager ضمن EVS واجهة برمجة تطبيقات الأجهزة:

لا تلاحظ التطبيقات أي اختلافات عند تشغيلها من خلال طبقة تجريد الأجهزة (HAL) لأجهزة EVS التنفيذ أو واجهة برمجة تطبيقات EVS Manager، باستثناء أنّ EVS Manager API تسمح الوصول إلى بث الكاميرا المتزامن. إنّ "مدير EVS" هو نفسه المدير المسموح به يعمل كعميل لطبقة HAL لأجهزة EVS، ويعمل كخادم وكيل لأجهزة EVS HAL.

تصف الأقسام التالية فقط تلك المكالمات التي لها السلوك (الموسّع) في عملية تنفيذ "مدير EVS" المكالمات المتبقية مماثلة لأوصاف EVS HAL.

معادلة IEvsEnumerator

openCamera(string camera_id) generates (IEvsCamera camera);

الحصول على كائن واجهة يُستخدَم للتفاعل مع كاميرا معيّنة التي يتم تحديدها بواسطة سلسلة camera_id الفريدة. لعرض قيمة فارغة (NULL) عند الفشل. في طبقة مدير EVS، طالما توفرت موارد نظام كافية، فقد يتم إعادة فتح كاميرا مفتوحة من خلال عملية أخرى، تشغيل بث الفيديو على العديد من تطبيقات المستهلك. تشير رسالة الأشكال البيانية camera_id سلسلة في طبقة "مدير EVS" هي نفسها تقديم تقرير إلى طبقة أجهزة EVS.

كاميرا IEvsكاميرا

قدم مدير EVS تطبيق IEvs Camera افتراضيًا داخليًا. وبالتالي لا تؤثر العمليات التي تتم على الكاميرا في برامج أحد العملاء في الأجهزة الأخرى، والتي الوصول المستقل إلى كاميراتهم.

startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);

يؤدي النقر على هذا الزر إلى بدء عمليات بث الفيديو. يمكن للعملاء بدء عمليات بث الفيديو وإيقافها بشكل مستقل على نفس الكاميرا الأساسية. تبدأ الكاميرا الأساسية عند تشغيل العميل.

doneWithFrame(uint32 frameId, handle bufferHandle) generates (EvsResult result);

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

stopVideoStream();

لإيقاف بث الفيديو. ويمكن لكل برنامج إيقاف بث الفيديو في أي وقت وبدون تؤثر في العملاء الآخرين. بث الكاميرا الأساسية في طبقة الجهاز هو يتم إيقافه عندما يتوقّف آخر عميل لكاميرا معيّنة عن البث.

setExtendedInfo(int32 opaqueIdentifier, int32 opaqueValue) generates (EvsResult result);

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

عرض IEvs

يُسمح بمالك واحد فقط للشاشة، حتى على مستوى مدير EVS. تشير رسالة الأشكال البيانية لا يضيف المدير أي وظائف ويقوم ببساطة بتمرير واجهة IEvsDisplay مباشرةً إلى تنفيذ HAL الأساسي.

تطبيق EVS

يشتمل Android على مرجع أصلي بلغة C++ لمعيار EVS يتواصل مع مدير EVS وHAL للمركبة توفير الوظائف الأساسية لكاميرا الرؤية الخلفية من المتوقع أن يبدأ التطبيق في وقت مبكر جدًا من عملية تمهيد النظام، مع عرض فيديو مناسب حسب الكاميرات المتاحة وحالة السيارة (الترس والانعطاف في الإشارة). يمكن للمصنّعين الأصليين للأجهزة تعديل تطبيق EVS أو استبداله بتطبيق خاص بالمركبة. المنطق والعرض التقديمي.

الشكل 3. نموذج منطقي لتطبيق EVS، الحصول على الكاميرا
.

الشكل 4. نموذج منطق عينة تطبيق EVS، لاستلام إطار معاودة الاتصال.

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

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

استخدام EGL/SurfaceFlinger في طبقة HAL لشاشة EVS

يوضح هذا القسم كيفية استخدام EGL لعرض تنفيذ HAL لشاشة EVS. في Android 10.

EVS إنّ تنفيذ مرجع HAL يستخدم EGL لعرض معاينة الكاميرا على. الشاشة ويستخدم libgui لإنشاء سطح عرض EGL المستهدف. في Android 8 (والإصدارات الأحدث)، libgui مصنّفة على أنها خاصة بـ VNDK، والذي يشير إلى مجموعة من المكتبات المتاحة لمكتبات VNDK والتي لا يمكن لعمليات البائعين استخدامها. ولأن عمليات تنفيذ HAL يجب أن تكون في قسم المورد، يتم منع الموردين من استخدام إبراز عمليات تنفيذ HAL

إنشاء libgui لعمليات البائعين

يُعدّ استخدام libgui الخيار الوحيد لاستخدام EGL/SurfaceFlinger. في عمليات تنفيذ بروتوكول HAL للعرض الخاص بخدمة EVS إنّ الطريقة الأسهل لتنفيذ libgui هي حتى frameworks/Native/libs/gui مباشرةً من خلال استخدام هدف إصدار إضافي في النص البرمجي للإصدار. هذا الهدف مماثل تمامًا هدف libgui باستثناء إضافة حقلَين:

  • name
  • vendor_available
cc_library_shared {
    name: "libgui_vendor",
    vendor_available: true,
    vndk: {
        enabled: false,
    },
    double_loadable: true,

defaults: ["libgui_bufferqueue-defaults"],
srcs: [ … // bufferhub is not used when building libgui for vendors target: { vendor: { cflags: [ "-DNO_BUFFERHUB", "-DNO_INPUT", ], …

ملاحظة: يتم إنشاء استهدافات المورّدين باستخدام وحدة ماكرو NO_INPUT، ما يؤدي إلى إزالة كلمة واحدة 32 بت من بيانات الطرود. وبما أنّ SurfaceFlinger تتوقع أنّ هذا الحقل قد تمت إزالته، يتعذّر على SurfaceFlinger تحليل الطرد. ويحدث ذلك كتعذُّر في fcntl:

W Parcel  : Attempt to read object from Parcel 0x78d9cffad8 at offset 428 that is not in the object list
E Parcel  : fcntl(F_DUPFD_CLOEXEC) failed in Parcel::read, i is 0, fds[i] is 0, fd_count is 20, error: Unknown error 2147483647
W Parcel  : Attempt to read object from Parcel 0x78d9cffad8 at offset 544 that is not in the object list

لحلّ هذا الشرط:

diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 6066421fa..25cf5f0ce 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -54,6 +54,9 @@ status_t layer_state_t::write(Parcel& output) const
    output.writeFloat(color.b);
#ifndef NO_INPUT
    inputInfo.write(output);
+#else
+    // Write a dummy 32-bit word.
+    output.writeInt32(0);
#endif
    output.write(transparentRegion);
    output.writeUint32(transform);

نموذج التصميم التعليمات أدناه. توقع تلقي $(ANDROID_PRODUCT_OUT)/system/lib64/libgui_vendor.so

$ cd <your_android_source_tree_top>
$ . ./build/envsetup.
$ lunch <product_name>-<build_variant>
============================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=10
TARGET_PRODUCT=<product_name>
TARGET_BUILD_VARIANT=<build_variant>
TARGET_BUILD_TYPE=release
TARGET_ARCH=arm64
TARGET_ARCH_VARIANT=armv8-a
TARGET_CPU_VARIANT=generic
TARGET_2ND_ARCH=arm
TARGET_2ND_ARCH_VARIANT=armv7-a-neon
TARGET_2ND_CPU_VARIANT=cortex-a9
HOST_ARCH=x86_64
HOST_2ND_ARCH=x86
HOST_OS=linux
HOST_OS_EXTRA=<host_linux_version>
HOST_CROSS_OS=windows
HOST_CROSS_ARCH=x86
HOST_CROSS_2ND_ARCH=x86_64
HOST_BUILD_TYPE=release
BUILD_ID=QT
OUT_DIR=out
============================================

$ m -j libgui_vendor … $ find $ANDROID_PRODUCT_OUT/system -name "libgui_vendor*" .../out/target/product/hawk/system/lib64/libgui_vendor.so .../out/target/product/hawk/system/lib/libgui_vendor.so

استخدام أداة الربط في تنفيذ برنامج EVS HAL

في Android 8 (والإصدارات الأحدث)، أصبحت عقدة الجهاز /dev/binder حصرية إطار العمل، وبالتالي لا يمكن الوصول إليها من خلال عمليات البائع. بدلاً من ذلك، يجب أن تستخدم عمليات المورّدين /dev/hwbinder ويجب أن تحولوا أي واجهات AIDL إلى HIDL. بالنسبة إلى هؤلاء الذين يرغبون في مواصلة استخدام واجهات AIDL بين عمليات البائعين، استخدم نطاق البيندر، /dev/vndbinder.

مجال IPC الوصف
/dev/binder IPC بين عمليات التطبيق/إطار العمل من خلال واجهات AIDL
/dev/hwbinder IPC بين عمليات إطار العمل/المورّد من خلال واجهات HIDL
IPC بين عمليات البائعين باستخدام واجهات HIDL
/dev/vndbinder IPC بين عمليات البائعين/المورِّدين باستخدام واجهات AIDL

في حين أنّ SurfaceFlinger تحدِّد واجهات AIDL، يمكن لعمليات المورّدين استخدام واجهات HIDL فقط التواصل مع عمليات إطار العمل. يلزم قدر غير تافه من العمل لتحويل الملفات الحالية واجهات AIDL في HIDL. لحسن الحظ، يوفر Android طريقة لاختيار الحافظة برنامج تشغيل libbinder، الذي تم ربط عمليات مكتبة مساحة المستخدم به.

diff --git a/evs/sampleDriver/service.cpp b/evs/sampleDriver/service.cpp
index d8fb3166..5fd02935 100644
--- a/evs/sampleDriver/service.cpp
+++ b/evs/sampleDriver/service.cpp
@@ -21,6 +21,7 @@
#include <utils/Errors.h>
#include <utils/StrongPointer.h>
#include <utils/Log.h>
+#include <binder/ProcessState.h>

#include "ServiceNames.h"
#include "EvsEnumerator.h"
@@ -43,6 +44,9 @@ using namespace android;
int main() {
    ALOGI("EVS Hardware Enumerator service is starting");


+    // Use /dev/binder for SurfaceFlinger
+    ProcessState::initWithDriver("/dev/binder");
+


    // Start a thread to listen to video device addition events.
    std::atomic<bool> running { true };
    std::thread ueventHandler(EvsEnumerator::EvsUeventThread, std::ref(running));

ملاحظة: يجب أن تطلب عمليات المورّدين هذا قبل الاتصال Process أو IPCThreadState، أو قبل إجراء أي طلبات تثبيت.

سياسات SELinux

في حال وصول تطبيق الجهاز إلى ثلاثة أضعاف، فسيمنع نظام SELinux هذا المورِّد من استخدام /dev/binder. على سبيل المثال، عينة EVS HAL تم تعيين عملية التنفيذ إلى النطاق hal_evs_driver ويتطلب أذونات r/w لنطاق binder_device.

W ProcessState: Opening '/dev/binder' failed: Permission denied
F ProcessState: Binder driver could not be opened. Terminating.
F libc    : Fatal signal 6 (SIGABRT), code -1 (SI_QUEUE) in tid 9145 (android.hardwar), pid 9145 (android.hardwar)
W android.hardwar: type=1400 audit(0.0:974): avc: denied { read write } for name="binder" dev="tmpfs" ino=2208 scontext=u:r:hal_evs_driver:s0 tcontext=u:object_r:binder_device:s0 tclass=chr_file permissive=0

إلا أن إضافة هذه الأذونات تتسبب في إخفاق الإصدار نظرًا لأنها تنتهك ما يلي: لا تسمح أبدًا بالقواعد المحددة في system/sepolicy/domain.te لجهاز الصوت العالي الطبقة.

libsepol.report_failure: neverallow on line 631 of system/sepolicy/public/domain.te (or line 12436 of policy.conf) violated by allow hal_evs_driver binder_device:chr_file { read write };
libsepol.check_assertions: 1 neverallow failures occurred
full_treble_only(`
neverallow {
    domain
    -coredomain
    -appdomain
    -binder_in_vendor_violators
} binder_device:chr_file rw_file_perms;
')

binder_in_vendor_violators هو سمة يتم تقديمها لرصد الأخطاء وتوجيه عملية التطوير. يمكن أيضًا استخدامها حلّ مخالفة Android 10 الموضّحة أعلاه.

diff --git a/evs/sepolicy/evs_driver.te b/evs/sepolicy/evs_driver.te
index f1f31e9fc..6ee67d88e 100644
--- a/evs/sepolicy/evs_driver.te
+++ b/evs/sepolicy/evs_driver.te
@@ -3,6 +3,9 @@ type hal_evs_driver, domain, coredomain;
hal_server_domain(hal_evs_driver, hal_evs)
hal_client_domain(hal_evs_driver, hal_evs)

+# Allow to use /dev/binder
+typeattribute hal_evs_driver binder_in_vendor_violators;
+
# allow init to launch processes in this context
type hal_evs_driver_exec, exec_type, file_type, system_file_type;
init_daemon_domain(hal_evs_driver)

بناء تطبيق مرجع HAL EVS كعملية بائع

كمرجع، يمكنك تطبيق التغييرات التالية على packages/services/Car/evs/Android.mk تأكد من أن جميع التغييرات الموضحة مناسبة لتنفيذ إعلاناتك.

diff --git a/evs/sampleDriver/Android.mk b/evs/sampleDriver/Android.mk
index 734feea7d..0d257214d 100644
--- a/evs/sampleDriver/Android.mk
+++ b/evs/sampleDriver/Android.mk
@@ -16,7 +16,7 @@ LOCAL_SRC_FILES := \
LOCAL_SHARED_LIBRARIES := \
    android.hardware.automotive.evs@1.0 \
    libui \
-    libgui \
+    libgui_vendor \
    libEGL \
    libGLESv2 \
    libbase \
@@ -33,6 +33,7 @@ LOCAL_SHARED_LIBRARIES := \
LOCAL_INIT_RC := android.hardware.automotive.evs@1.0-sample.rc

LOCAL_MODULE := android.hardware.automotive.evs@1.0-sample
+LOCAL_PROPRIETARY_MODULE := true

LOCAL_MODULE_TAGS := optional
LOCAL_STRIP_MODULE := keep_symbols
@@ -40,6 +41,7 @@ LOCAL_STRIP_MODULE := keep_symbols
LOCAL_CFLAGS += -DLOG_TAG=\"EvsSampleDriver\"
LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
+LOCAL_CFLAGS += -Iframeworks/native/include

#NOTE:  It can be helpful, while debugging, to disable optimizations
#LOCAL_CFLAGS += -O0 -g
diff --git a/evs/sampleDriver/service.cpp b/evs/sampleDriver/service.cpp
index d8fb31669..5fd029358 100644
--- a/evs/sampleDriver/service.cpp
+++ b/evs/sampleDriver/service.cpp
@@ -21,6 +21,7 @@
#include <utils/Errors.h>
#include <utils/StrongPointer.h>
#include <utils/Log.h>
+#include <binder/ProcessState.h>

#include "ServiceNames.h"
#include "EvsEnumerator.h"
@@ -43,6 +44,9 @@ using namespace android;
int main() {
    ALOGI("EVS Hardware Enumerator service is starting");
+    // Use /dev/binder for SurfaceFlinger
+    ProcessState::initWithDriver("/dev/binder");
+
     // Start a thread to listen video device addition events.
    std::atomic<bool> running { true };
    std::thread ueventHandler(EvsEnumerator::EvsUeventThread, std::ref(running));
diff --git a/evs/sepolicy/evs_driver.te b/evs/sepolicy/evs_driver.te
index f1f31e9fc..632fc7337 100644
--- a/evs/sepolicy/evs_driver.te
+++ b/evs/sepolicy/evs_driver.te
@@ -3,6 +3,9 @@ type hal_evs_driver, domain, coredomain;
hal_server_domain(hal_evs_driver, hal_evs)
hal_client_domain(hal_evs_driver, hal_evs)

+# allow to use /dev/binder
+typeattribute hal_evs_driver binder_in_vendor_violators;
+
# allow init to launch processes in this context
type hal_evs_driver_exec, exec_type, file_type, system_file_type;
init_daemon_domain(hal_evs_driver)
@@ -22,3 +25,7 @@ allow hal_evs_driver ion_device:chr_file r_file_perms;

# Allow the driver to access kobject uevents
allow hal_evs_driver self:netlink_kobject_uevent_socket create_socket_perms_no_ioctl;
+
+# Allow the driver to use the binder device
+allow hal_evs_driver binder_device:chr_file rw_file_perms;