يحتوي Android على طبقة تجريد أجهزة HIDL الخاصة بالسيارات (HAL) والتي تتيح التقاط الصور وعرضها في وقت مبكر جدًا من عملية تمهيد Android ويستمر في العمل طوال عمر النظام. تتضمن قناة HAL كدسة نظام الرؤية الخارجية (EVS) التي تُستخدم عادةً لدعم الرؤية الخلفية أجهزة العرض داخل المركبات التي تعمل بنظام التشغيل Android وشاشة العرض المحيطي أنظمة الترفيه والمعلومات (IVI). تمكّن EVS أيضًا من تنفيذ الميزات المتقدّمة في تطبيقات المستخدم.
يتضمّن Android أيضًا برنامج تشغيل الالتقاط والعرض الخاص بـ EVS
(في /hardware/interfaces/automotive/evs/1.0
). إنّه
إمكانية إنشاء تطبيق كاميرا للرؤية الخلفية في أعلى نظام Android الحالي
خدمات الكاميرا والعرض، فمن المحتمل أن يعمل هذا التطبيق متأخرًا جدًا في
عملية تشغيل Android. يعمل استخدام طبقة تجريد الأجهزة (HAL) على تمكين واجهة سلسة
ويوضح ما يجب على المصنّع الأصلي للجهاز تنفيذه لدعم حزمة EVS.
مكونات النظام
تشمل خدمة 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 واستقبال فيديو تدفق).
لا تلاحظ التطبيقات أي اختلافات عند تشغيلها من خلال طبقة تجريد الأجهزة (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 أو استبداله بتطبيق خاص بالمركبة. المنطق والعرض التقديمي.
لأنّه يتم تقديم بيانات الصور إلى التطبيق برسومات عادية مخزن مؤقت، فإن التطبيق مسؤول عن نقل الصورة من المصدر المخزن المؤقت إلى المخزن المؤقت للمخرجات. بينما يقدم هذا تكلفة نسخة البيانات، يتيح أيضًا للتطبيق إمكانية عرض الصورة في عرض المورد الاحتياطي بأي شكل يرغب.
على سبيل المثال، قد يختار التطبيق نقل بيانات البكسل بنفسه، من خلال عملية تدوير أو مقياس مضمَّنة. يمكن للتطبيق اختيار أيضًا استخدام الصورة المصدر كزخرفة 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;