دوربین خودرو HAL

اندروید حاوی یک لایه انتزاعی سخت افزاری HIDL (HAL) است که در مراحل اولیه بوت اندروید امکان ضبط و نمایش تصاویر را فراهم می کند و تا پایان عمر سیستم به کار خود ادامه می دهد. HAL شامل پشته سیستم نمای خارجی (EVS) است و معمولاً برای پشتیبانی از دوربین دید عقب و نمایشگرهای دید فراگیر در خودروهایی با سیستم‌های In-Vehicle Infotainment (IVI) مبتنی بر Android استفاده می‌شود. EVS همچنین امکان پیاده سازی ویژگی های پیشرفته را در برنامه های کاربر فراهم می کند.

اندروید همچنین شامل یک رابط درایور عکسبرداری و نمایشگر مخصوص EVS است (در /hardware/interfaces/automotive/evs/1.0 ). در حالی که امکان ساخت یک برنامه دوربین دید عقب در بالای خدمات دوربین و نمایشگر اندروید موجود وجود دارد، چنین برنامه ای احتمالاً در فرآیند بوت اندروید بسیار دیر اجرا می شود. استفاده از یک HAL اختصاصی، یک رابط کارآمد را فعال می‌کند و روشن می‌کند که یک OEM برای پشتیبانی از پشته EVS چه چیزی را باید پیاده‌سازی کند.

اجزای سیستم

EVS شامل اجزای سیستم زیر است:

نمودار اجزای سیستم EVS
شکل 1. نمای کلی اجزای سیستم EVS.

برنامه EVS

یک نمونه برنامه C++ EVS ( /packages/services/Car/evs/app ) به عنوان پیاده‌سازی مرجع عمل می‌کند. این برنامه مسئول درخواست فریم های ویدیویی از مدیر EVS و ارسال فریم های تمام شده برای نمایش به مدیر EVS است. انتظار می رود به محض در دسترس بودن EVS و Car Service، توسط init راه اندازی شود و در عرض دو (2) ثانیه پس از روشن شدن روشن شود. OEM ها می توانند برنامه EVS را به دلخواه تغییر دهند یا جایگزین کنند.

مدیر EVS

مدیر EVS ( /packages/services/Car/evs/manager ) بلوک‌های ساختمان مورد نیاز یک برنامه EVS را برای پیاده‌سازی هر چیزی از یک نمایشگر ساده دوربین دید عقب گرفته تا یک رندر چند دوربینی 6DOF فراهم می‌کند. رابط آن از طریق HIDL ارائه شده است و برای پذیرش چندین مشتری همزمان ساخته شده است. سایر برنامه‌ها و سرویس‌ها (مخصوصاً Car Service) می‌توانند وضعیت EVS Manager را برای اطلاع از فعال بودن سیستم EVS پرس و جو کنند.

رابط EVS HIDL

سیستم EVS، هم دوربین و هم عناصر نمایشگر، در بسته android.hardware.automotive.evs تعریف شده است. یک نمونه پیاده‌سازی که رابط را تمرین می‌کند (تصاویر آزمایشی مصنوعی تولید می‌کند و تصاویر را در مسیر رفت و برگشت تأیید می‌کند) در /hardware/interfaces/automotive/evs/1.0/default ارائه شده است.

OEM مسئول پیاده سازی API است که توسط فایل های .hal در /hardware/interfaces/automotive/evs بیان شده است. چنین پیاده‌سازی‌هایی مسئول پیکربندی و جمع‌آوری داده‌ها از دوربین‌های فیزیکی و تحویل آن‌ها از طریق بافرهای حافظه مشترک قابل تشخیص توسط Gralloc هستند. سمت نمایش پیاده‌سازی مسئول ارائه یک بافر حافظه مشترک است که می‌تواند توسط برنامه پر شود (معمولاً با رندر EGL) و ارائه فریم‌های تمام‌شده به هر چیز دیگری که ممکن است بخواهد روی صفحه نمایش فیزیکی ظاهر شود. پیاده سازی فروشنده رابط EVS ممکن است در /vendor/… /device/… یا hardware/… ذخیره شود (به عنوان مثال، /hardware/[vendor]/[platform]/evs ).

درایورهای کرنل

دستگاهی که پشته EVS را پشتیبانی می کند به درایورهای هسته نیاز دارد. به جای ایجاد درایورهای جدید، OEM ها این گزینه را دارند که از ویژگی های مورد نیاز EVS از طریق درایورهای سخت افزاری دوربین یا نمایشگر موجود پشتیبانی کنند. استفاده مجدد از درایورها می تواند سودمند باشد، به خصوص برای درایورهای نمایشگر که در آن ارائه تصویر ممکن است نیاز به هماهنگی با موضوعات فعال دیگر داشته باشد. Android 8.0 شامل یک درایور نمونه مبتنی بر v4l2 (در packages/services/Car/evs/sampleDriver ) است که برای پشتیبانی از v4l2 به هسته و برای ارائه تصویر خروجی به SurfaceFlinger بستگی دارد.

توضیحات رابط سخت افزاری EVS

این بخش HAL را توصیف می کند. از فروشندگان انتظار می رود که پیاده سازی هایی از این API متناسب با سخت افزار خود ارائه دهند.

IEvsEnumerator

این شیء مسئول شمارش سخت افزار EVS موجود در سیستم (یک یا چند دوربین و دستگاه نمایشگر منفرد) است.

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

وکتوری حاوی توضیحات برای همه دوربین‌های موجود در سیستم را برمی‌گرداند. فرض بر این است که مجموعه دوربین‌ها در زمان راه‌اندازی ثابت و قابل شناسایی هستند. برای جزئیات بیشتر در مورد توضیحات دوربین، CameraDesc را ببینید.

openCamera(string camera_id) generates (IEvsCamera camera);

یک شئ رابط مورد استفاده برای تعامل با یک دوربین خاص که توسط رشته منحصر به فرد camera_id شناسایی شده است را به دست می آورد. در صورت شکست یک عدد NULL برمی گرداند. تلاش برای باز کردن مجدد دوربینی که از قبل باز است، نمی تواند شکست بخورد. برای جلوگیری از شرایط مسابقه مرتبط با راه اندازی و خاموش شدن برنامه، باز کردن مجدد دوربین باید نمونه قبلی را خاموش کند تا درخواست جدید برآورده شود. نمونه دوربینی که به این روش از قبل استفاده شده است باید در حالت غیرفعال قرار گیرد، در انتظار نابودی نهایی باشد و به هر درخواستی برای تأثیرگذاری بر وضعیت دوربین با کد بازگشتی OWNERSHIP_LOST پاسخ دهد.

closeCamera(IEvsCamera camera);

رابط IEvsCamera را آزاد می کند (و مخالف فراخوانی openCamera() است). قبل از فراخوانی closeCamera ، جریان ویدئوی دوربین باید با فراخوانی stopVideoStream() متوقف شود.

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 به گیرنده تحویل داده شده باشد، جریان از فریم ها می گذرد تا زمانی که یک بافر برای استفاده مجدد برگردانده شود. قانونی است که این تماس در هر زمانی انجام شود، حتی زمانی که جریان‌ها از قبل در حال اجرا هستند، در این صورت بافرها باید در صورت لزوم از زنجیره اضافه یا حذف شوند. اگر تماسی با این نقطه ورودی برقرار نشود، IEvsCamera حداقل یک فریم را به طور پیش فرض پشتیبانی می کند. با قابل قبول تر

اگر باfferCount درخواستی قابل تطبیق نباشد، تابع BUFFER_NOT_AVAILABLE یا کد خطای مرتبط دیگر را برمی‌گرداند. در این حالت، سیستم با مقدار تعیین شده قبلی به کار خود ادامه می دهد.

startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);

درخواست تحویل قاب دوربین EVS از این دوربین را دارد. IEvsCameraStream شروع به دریافت تماس های دوره ای با فریم های تصویر جدید می کند تا زمانی که stopVideoStream() فراخوانی شود. فریم‌ها باید در 500 میلی‌ثانیه پس از تماس startVideoStream تحویل داده شوند و پس از شروع، باید حداقل با سرعت 10 فریم در ثانیه تولید شوند. زمان لازم برای شروع پخش ویدئو به طور موثر در برابر هر زمان مورد نیاز راه اندازی دوربین دنده عقب حساب می شود. اگر جریان شروع نشد، یک کد خطا باید برگردانده شود. در غیر این صورت OK برگردانده می شود.

oneway doneWithFrame(BufferDesc buffer);

فریمی را برمی‌گرداند که توسط IEvsCameraStream تحویل داده شده است. پس از مصرف فریم تحویل داده شده به رابط IEvsCameraStream، فریم باید برای استفاده مجدد به IEvsCamera برگردانده شود. تعداد محدود و محدودی از بافرها موجود است (احتمالاً به اندازه یک بافر)، و اگر منبع تمام شود، هیچ فریم دیگری تحویل داده نمی شود تا زمانی که بافر بازگردانده شود، که به طور بالقوه منجر به فریم های نادیده گرفته می شود (یک بافر با یک دسته تهی نشان دهنده پایان است. یک جریان است و نیازی به بازگرداندن آن از طریق این تابع نیست). در صورت موفقیت، OK را برمی‌گرداند، یا کد خطای مناسب که احتمالاً شامل 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
}

تصویری را توصیف می کند که از طریق API ارسال شده است. درایو 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 برای شناسایی بافر پس از یک رفت و برگشت از طریق API های HAL. مقدار ذخیره شده در این فیلد ممکن است به طور دلخواه توسط اجرای HAL انتخاب شود.
  • memHandle . دسته برای بافر حافظه زیرین که حاوی داده های تصویر است. اجرای HAL ممکن است یک دسته بافر Gralloc را در اینجا ذخیره کند.

IEvsCameraStream

مشتری این رابط را برای دریافت تحویل فریم های ویدئویی ناهمزمان پیاده سازی می کند.

deliverFrame(BufferDesc buffer);

هر بار که فریم ویدیویی برای بازرسی آماده می شود، از HAL تماس دریافت می کند. دسته های بافر دریافت شده توسط این روش باید از طریق فراخوانی به IEvsCamera::doneWithFrame() برگردانده شوند. هنگامی که جریان ویدیو با تماس با IEvsCamera::stopVideoStream() متوقف می شود، ممکن است این تماس با تخلیه خط لوله ادامه یابد. هر فریم همچنان باید بازگردانده شود. هنگامی که آخرین فریم در جریان تحویل داده شد، یک bufferHandle NULL تحویل داده می شود که به معنای پایان جریان است و هیچ تحویل فریم دیگری اتفاق نمی افتد. خود NULL bufferHandle نیازی به ارسال مجدد با doneWithFrame() ندارد، اما همه دسته های دیگر باید برگردانده شوند.

در حالی که فرمت‌های بافر اختصاصی از نظر فنی امکان‌پذیر است، آزمایش سازگاری مستلزم آن است که بافر در یکی از چهار قالب پشتیبانی‌شده باشد: NV21 (YCrCb 4:2:0 نیمه مسطح)، YV12 (YCrCb 4:2:0 Planar)، YUYV (YCrCb 4: 2:2 Interleaved)، RGBA (32 بیت R:G:B:x)، BGRA (32 بیت B:G:R:x). قالب انتخابی باید یک منبع بافت GL معتبر در اجرای GLES پلت فرم باشد.

برنامه نباید به هیچ گونه مطابقت بین فیلد bufferId و memHandle در ساختار BufferDesc متکی باشد. مقادیر bufferId اساساً برای اجرای درایور HAL خصوصی هستند و ممکن است هر طور که صلاح می‌داند از آنها استفاده کند (و مجدداً استفاده کند).

IEvsDisplay

این شیء نمایشگر 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 تنظیم شده باشد، باید قابل مشاهده باقی بماند. همیشه OK را برمی‌گرداند مگر اینکه حالت درخواستی یک مقدار enum ناشناخته باشد که در این صورت 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 Interleaved)، RGBA (32 بیت R:G:B:x)، BGRA (32 بیت B:G:R:x). قالب انتخابی باید یک هدف رندر GL معتبر در اجرای GLES پلت فرم باشد.

در صورت خطا، بافری با دسته تهی برگردانده می‌شود، اما نیازی نیست که چنین بافری به returnTargetBufferForDisplay بازگردانده شود.

returnTargetBufferForDisplay(handle bufferHandle) generates (EvsResult result);

به نمایشگر می گوید که بافر برای نمایش آماده است. فقط بافرهای بازیابی شده از طریق تماس با getTargetBuffer() برای استفاده با این تماس معتبر هستند و ممکن است محتویات BufferDesc توسط برنامه مشتری تغییر نکند. پس از این تماس، بافر دیگر برای استفاده توسط مشتری معتبر نیست. در صورت موفقیت، OK را برمی‌گرداند، یا کد خطای مناسب که احتمالاً شامل 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 Manager است و تنها سرویس گیرنده ای است که مجاز به نوشتن داده های نمایشی است (به مشتریان اضافی می توان به تصاویر دوربین دسترسی فقط خواندنی داد).

مدیر EVS همان API درایورهای HAL زیربنایی را پیاده‌سازی می‌کند و خدمات گسترده‌ای را با پشتیبانی از چندین کلاینت همزمان ارائه می‌کند (بیش از یک کلاینت می‌توانند یک دوربین را از طریق مدیر EVS باز کنند و یک جریان ویدیویی دریافت کنند).

EVS Manager و نمودار API سخت افزار EVS.
شکل 2. EVS Manager آینه های زیربنایی EVS Hardware API.

برنامه‌ها هنگام کار از طریق اجرای HAL سخت‌افزار EVS یا API مدیریت EVS هیچ تفاوتی نمی‌بینند، به جز اینکه EVS Manager API امکان دسترسی همزمان به جریان دوربین را می‌دهد. مدیر EVS خود مشتری مجاز لایه HAL سخت افزار EVS است و به عنوان یک پروکسی برای HAL سخت افزار EVS عمل می کند.

بخش‌های زیر فقط تماس‌هایی را توصیف می‌کنند که رفتار (گسترده) متفاوتی در اجرای EVS Manager دارند. تماس های باقی مانده با توضیحات EVS HAL یکسان هستند.

IEvsEnumerator

openCamera(string camera_id) generates (IEvsCamera camera);

یک شئ رابط مورد استفاده برای تعامل با یک دوربین خاص که توسط رشته منحصر به فرد camera_id شناسایی شده است را به دست می آورد. در صورت شکست یک عدد NULL برمی گرداند. در لایه EVS Manager، تا زمانی که منابع سیستم کافی در دسترس باشد، دوربینی که از قبل باز است ممکن است با فرآیند دیگری دوباره باز شود و امکان پخش جریان ویدئو را به چندین برنامه مصرف کننده فراهم کند. رشته های camera_id در لایه EVS Manager همان رشته هایی است که به لایه سخت افزار EVS گزارش شده است.

دوربین IEVs

مدیر EVS ارائه کرده است که پیاده سازی IEvsCamera به صورت داخلی مجازی سازی شده است، بنابراین عملیات روی یک دوربین توسط یک کلاینت بر روی دیگر کلاینت ها تأثیر نمی گذارد، که دسترسی مستقل به دوربین های خود را حفظ می کنند.

startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);

پخش جریانی ویدیو را شروع می کند. مشتریان ممکن است به طور مستقل جریان های ویدئویی را در همان دوربین اصلی شروع و متوقف کنند. دوربین زیرین با شروع اولین مشتری شروع می شود.

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

یک قاب را برمی گرداند. هر مشتری باید قاب های خود را پس از اتمام کار بازگرداند، اما مجاز است تا زمانی که می خواهد قاب های خود را نگه دارد. هنگامی که تعداد فریم های نگهداری شده توسط یک کلاینت به حد مجاز خود می رسد، تا زمانی که یک فریم را برگرداند، فریم دیگری دریافت نخواهد کرد. این رد شدن از فریم روی دیگر کلاینت‌ها تأثیری نمی‌گذارد، که همچنان همه فریم‌ها را همانطور که انتظار می‌رود دریافت می‌کنند.

stopVideoStream();

یک جریان ویدیویی را متوقف می کند. هر کلاینت می‌تواند در هر زمان پخش ویدیوی خود را بدون تأثیرگذاری بر سایر مشتریان متوقف کند. جریان دوربین زیربنایی در لایه سخت‌افزار زمانی متوقف می‌شود که آخرین مشتری یک دوربین معین جریان خود را متوقف کند.

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

یک مقدار خاص درایور را ارسال می کند، که به طور بالقوه یک کلاینت را قادر می سازد روی کلاینت دیگر تأثیر بگذارد. از آنجایی که مدیر EVS نمی‌تواند مفاهیم کلمات کنترلی تعریف‌شده توسط فروشنده را درک کند، آنها مجازی‌سازی نمی‌شوند و هر گونه عوارض جانبی برای همه مشتریان یک دوربین خاص اعمال می‌شود. به عنوان مثال، اگر فروشنده ای از این تماس برای تغییر نرخ فریم استفاده کند، همه مشتریان دوربین لایه سخت افزاری آسیب دیده فریم ها را با نرخ جدید دریافت می کنند.

IEvsDisplay

فقط یک مالک نمایشگر مجاز است، حتی در سطح مدیر EVS. مدیر هیچ عملکردی اضافه نمی کند و به سادگی رابط IEvsDisplay را مستقیماً به پیاده سازی زیرین HAL منتقل می کند.

برنامه EVS

Android شامل یک پیاده سازی مرجع C++ از یک برنامه EVS است که با مدیر EVS و Vehicle HAL برای ارائه عملکردهای اولیه دوربین دید عقب ارتباط برقرار می کند. انتظار می‌رود این برنامه در مراحل اولیه راه‌اندازی سیستم، با ویدیوی مناسب بسته به دوربین‌های موجود و وضعیت خودرو (دنده و وضعیت چراغ راه‌انداز) شروع به کار کند. OEM ها می توانند برنامه EVS را با منطق و ارائه خاص وسیله نقلیه خود تغییر دهند یا جایگزین کنند.

شکل 3. منطق نمونه برنامه EVS، لیست دوربین را دریافت کنید.


شکل 4. منطق نمونه برنامه EVS، پاسخ تماس فریم را دریافت کنید.

از آنجایی که داده های تصویر در یک بافر گرافیکی استاندارد به برنامه ارائه می شود، برنامه وظیفه انتقال تصویر از بافر منبع به بافر خروجی را بر عهده دارد. در حالی که این هزینه کپی داده را معرفی می کند، همچنین این فرصت را برای برنامه فراهم می کند تا تصویر را به هر شکلی که می خواهد در بافر نمایشگر ارائه دهد.

برای مثال، برنامه ممکن است انتخاب کند که داده‌های پیکسل را خودش جابه‌جا کند، احتمالاً با مقیاس درون خطی یا عملیات چرخش. این برنامه همچنین می‌تواند از تصویر منبع به عنوان یک بافت OpenGL استفاده کند و یک صحنه پیچیده را در بافر خروجی، از جمله عناصر مجازی مانند آیکون‌ها، دستورالعمل‌ها و انیمیشن‌ها ارائه دهد. یک برنامه پیچیده تر همچنین ممکن است چندین دوربین ورودی همزمان را انتخاب کرده و آنها را در یک فریم خروجی واحد ادغام کند (مانند استفاده در یک نمای مجازی از بالا به پایین از محیط اطراف خودرو).

از EGL/SurfaceFlinger در EVS Display HAL استفاده کنید

این بخش نحوه استفاده از EGL را برای رندر کردن اجرای EVS Display HAL در اندروید 10 توضیح می‌دهد.

یک پیاده‌سازی مرجع EVS HAL از EGL برای رندر کردن پیش‌نمایش دوربین روی صفحه و از libgui برای ایجاد سطح رندر EGL هدف استفاده می‌کند. در اندروید 8 (و بالاتر)، libgui به عنوان VNDK-private طبقه‌بندی می‌شود که به گروهی از کتابخانه‌های موجود در کتابخانه‌های VNDK اشاره دارد که فرآیندهای فروشنده نمی‌توانند از آن‌ها استفاده کنند. از آنجا که پیاده سازی های HAL باید در پارتیشن فروشنده قرار داشته باشند، فروشندگان از استفاده از Surface در پیاده سازی های HAL جلوگیری می کنند.

ساخت libgui برای فرآیندهای فروشنده

استفاده از libgui به عنوان تنها گزینه برای استفاده از EGL/SurfaceFlinger در اجرای EVS Display HAL عمل می کند. ساده ترین راه برای پیاده سازی libgui از طریق فریمورک/نیتیو/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 استفاده کنید

در اندروید 8 (و بالاتر)، گره دستگاه /dev/binder منحصر به فرآیندهای فریمورک شد و بنابراین برای فرآیندهای فروشنده غیرقابل دسترسی بود. در عوض، فرآیندهای فروشنده باید از /dev/hwbinder استفاده کنند و باید هر رابط AIDL را به HIDL تبدیل کنند. برای کسانی که می خواهند به استفاده از رابط های AIDL بین فرآیندهای فروشنده ادامه دهند، از دامنه binder، /dev/vndbinder استفاده کنند.

دامنه IPC توضیحات
/dev/binder IPC بین فرآیندهای چارچوب/برنامه با رابط های AIDL
/dev/hwbinder IPC بین فرآیندهای چارچوب/فروشنده با رابط های HIDL
IPC بین فرآیندهای فروشنده با رابط های HIDL
/dev/vndbinder IPC بین فرآیندهای فروشنده/فروشنده با رابط های AIDL

در حالی که SurfaceFlinger رابط های AIDL را تعریف می کند، فرآیندهای فروشنده فقط می توانند از رابط های HIDL برای ارتباط با فرآیندهای چارچوب استفاده کنند. برای تبدیل رابط‌های AIDL موجود به HIDL، مقدار کمی کار لازم است. خوشبختانه، اندروید روشی را برای انتخاب درایور بایندر برای 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

با این حال، افزودن این مجوزها باعث خرابی ساخت می‌شود، زیرا قوانین neverallow زیر را که در 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)

پیاده سازی مرجع EVS HAL را به عنوان یک فرآیند فروشنده بسازید

به عنوان مرجع، می‌توانید تغییرات زیر را در 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;