HAL למצלמת רכב

Android מכיל שכבת אבסטרציה של חומרת HIDL לרכב (HAL) אשר מספקת צילום והצגה של תמונות בשלב מוקדם מאוד בתהליך ההפעלה של Android וממשיכה לפעול כל עוד המערכת. מדד HAL כולל סטאק של מערכת התצוגה החיצונית (EVS), ובדרך כלל משמש לתמיכה מאחור מצלמה ומסך סראונד בכלי רכב עם מערכת Android מובנית מערכות מידע ובידור (IVI). מערכת EVS גם מאפשרת הטמעה של תכונות מתקדמות באפליקציות של משתמשים.

ב-Android יש גם כלי צילום ותצוגה ספציפיים לרכב חשמלי (EVS) ממשק (ב-/hardware/interfaces/automotive/evs/1.0). אומנם שאפשר לבנות אפליקציה של מצלמה אחורית על גבי מערכת Android הקיימת שירותי המצלמה והתצוגה, סביר להניח שאפליקציה כזו תפעל מאוחר מדי תהליך ההפעלה של Android. שימוש ב-HAL ייעודי מאפשר ממשק יעיל ומבהיר מה ה-OEM (יצרן הציוד המקורי) צריך להטמיע כדי לתמוך בסטאק של הרכבים החשמליים (EVS).

חלקי המערכת

הרכב החשמלי (EVS) כולל את רכיבי המערכת הבאים:

מערכת רכב חשמלי (EVS)
תרשים רכיבים
איור 1. סקירה כללית על רכיבי המערכת של הרכב החשמלי (EVS).

אפליקציית EVS

אפליקציה לדוגמה של C++ EVS (/packages/services/Car/evs/app) משמש כקובץ עזר יישום בפועל. האפליקציה הזו אחראית לשליחת פריימים של סרטונים מתוך מנהל מערכת הרכב החשמלי (EVS) ושולח את הפריימים שהסיום סיימת להציג אותם חזרה אל מנהל הרכב החשמלי (EVS). השירות מצפה להתחיל באתחול ברגע שכלי הרכב החשמליים (EVS) והשירות למכוניות זמינים, מטורגט תוך שתי (2) שניות מהפעלה. יצרני ציוד מקורי יכולים לשנות או להחליף את הרכבים החשמליים (EVS) באפליקציה הרצויה.

מנהל כלי רכב חשמליים (EVS)

הכלי EVS Manager (/packages/services/Car/evs/manager) מספק את אבני הבניין שדרושות לאפליקציה של רכבים חשמליים (EVS) כדי להטמיע תצוגה פשוטה של מצלמה אחורית בעיבוד של 6DOF מול מספר מצלמות. הממשק מוצג באמצעות HIDL ונועד לקבל מספר לקוחות בו-זמנית. אפליקציות ושירותים אחרים (במיוחד שירות הרכב) יכולים לשלוח שאילתה על הרכב החשמלי (EVS) מצב המנהל כדי לברר מתי מערכת הרכב החשמלי (EVS) פעילה.

ממשק EVS HIDL

מערכת הרכב החשמלי (EVS), גם המצלמה וגם רכיבי המסך, מוגדרים חבילה של android.hardware.automotive.evs. הטמעה לדוגמה שמפעיל את הממשק (יוצר תמונות לבדיקה סינתטית ומאמת את תמונות מובילות הלוך ושוב) /hardware/interfaces/automotive/evs/1.0/default

ה-OEM (יצרן הציוד המקורי) אחראי להטמיע את ה-API שצוין בקובצי ה- .hal ב-/hardware/interfaces/automotive/evs. הטמעות כאלה אחראי להגדרה ולאיסוף נתונים ממצלמות פיזיות והעברתו דרך מאגרי זיכרון משותפים ש-Gralloc יכולה לזהות. המסך של ההטמעה הוא הגורם האחראי ליצירת מאגר נתונים זמני משותף. שהאפליקציה יכולה למלא (בדרך כלל באמצעות רינדור EGL) ולהציג אותו הפריימים הסופיים מקבלים עדיפות על פני כל דבר אחר שעשוי להופיע בו את התצוגה הפיזית. ייתכן שההטמעות של ממשק הרכב החשמלי יאוחסן על ידי ספקים מתחת ל-/vendor/… /device/… או ל-hardware/… (למשל, /hardware/[vendor]/[platform]/evs).

מנהלי ליבה (kernel)

במכשיר שתומך בסטאק ה-EVS נדרשים מנהלי התקנים של ליבה. במקום כשיוצרים נהגים חדשים, יצרני ציוד מקורי יכולים לתמוך בתכונות שנדרשות לרכבים חשמליים (EVS) באמצעות מנהלי התקנים קיימים של מצלמה או חומרה של מסך. שימוש חוזר במנהלי התקנים יכול להיות היא שימושית, במיוחד במנהלי התקנים של תצוגה שבהם הצגת תמונות צריך לתאם עם שרשורים פעילים אחרים. Android 8.0 כולל גרסה מבוססת v4l2 נהג לדוגמה (ב-packages/services/Car/evs/sampleDriver) תלוי בליבה (kernel) לתמיכה ב-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);

משחרר את ממשק 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. אחרת, מדווח על המצב הנוכחי של הצג של הרכב החשמלי (ראו IEvsDisplay API).

struct CameraDesc {
    string      camera_id;
    int32       vendor_flags;       // Opaque value
}
  • camera_id מחרוזת שמזהה באופן ייחודי מצלמה נתונה. זה יכול להיות שם המכשיר הבסיסי (kernel) של המכשיר או שם המכשיר, כמו חזרה לאחור. הערך של המחרוזת הזו נבחר על ידי הטמעת HAL ונעשה בו שימוש אטום בסטאק שלמעלה.
  • vendor_flags שיטה להעברת מצלמה מיוחדת מידע אטום מהנהג לאפליקציה מותאמת אישית של רכב חשמלי. היא עברה ללא פרשנות, מהנהג ועד לאפליקציית הרכב החשמלי (EVS), ואפשר להתעלם ממנה בחינם את זה.

מצלמת IEvs

האובייקט הזה מייצג מצלמה אחת והוא הממשק הראשי של לצילום תמונות.

getCameraInfo() generates (CameraDesc info);

הפונקציה מחזירה CameraDesc של המצלמה הזו.

setMaxFramesInFlight(int32 bufferCount) generates (EvsResult result);

מציינת את העומק של שרשרת מאגר הנתונים הזמני שהמצלמה מבקשת לתמוך בה. Up to (עד) הלקוח של IEvscamera יכול להחזיק את הפריימים רבים בו-זמנית. אם הרבה פריימים נשלחו למקבל בלי החזר כספי doneWithFrame, השידור מדלג על פריימים עד שחוזר מאגר נתונים זמני לשימוש חוזר. זה חוקי לכך שהשיחה תתבצע בכל שלב, גם כשהשידורים חיים שכבר פועלות, במקרה כזה צריך להוסיף או להסיר אגירת נתונים מהרשת. לפי הצורך. אם לא תתבצע שיחה לנקודת הכניסה הזו, IEvs Camera תומך לפחות פריים אחד כברירת מחדל מקובלים יותר.

אם אי אפשר לאכלס את ה-bufferCount המבוקש, הפונקציה מחזירה BUFFER_NOT_AVAILABLE או קוד שגיאה רלוונטי אחר. במקרה הזה, המערכת ממשיכה לפעול עם הערך שהוגדר קודם לכן.

startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);

המערכת מבקשת לשלוח פריימים של מצלמות רכב חשמלי (EVS) מהמצלמה הזו. ה-IEvs CameraStream מתחיל לקבל קריאות תקופתיות עם פריימים חדשים של תמונות עד מתבצעת שיחה אל stopVideoStream(). צריך להתחיל להעביר את המסגרות בתוך 500 אלפיות השנייה מהשיחה אל startVideoStream ולאחר ההתחלה, בקצב של 10FPS לפחות. משך הזמן שנדרש כדי להתחיל את שידור הווידאו נספר ביעילות כנגד כל דרישה לזמן ההפעלה של המצלמה האחורית. אם השידור לא התחיל, צריך להחזיר קוד שגיאה. אחרת, מוחזר OK.

oneway doneWithFrame(BufferDesc buffer);

מחזירה פריים שנשלח על ידי IEvs CameraStream. בסיום צורכת פריים שהועברה לממשק של IEvs CameraStream, כאשר הפריים חייב להיות חזרו ל-IEvs Camera לשימוש חוזר. מספר קטן וסופי של מאגרים זמינות (יהיה קטן ככל האפשר), ואם המלאי מוצה, לא הפריימים לא מתעדכנים עד שיוחזרת מאגר נתונים זמני, מה שעלול להוביל על פריימים שדולגו (מאגר נתונים זמני עם נקודת אחיזה אפסית מציין את סוף השידור ופועל שאין צורך להחזיר אותו באמצעות הפונקציה הזו). מחזיר אישור לאחר הצלחה, או קוד שגיאה מתאים, שעשוי לכלול 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 ההטמעה צריכה לדרוש שהקריאה תפעל במצב ברירת מחדל. אם מנהל התקן מזהה ומקבל את הערכים, צריך להחזיר אישור, אחרת יש להחזיר 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 חצי מישור 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 תמיד מחזיר 'תקין', אלא אם המצב המבוקש הוא ערך 'טיפוסים בני מנייה (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 של הפלטפורמה.

במקרה של שגיאה, מוחזר מאגר נתונים זמני עם מזהה null, אבל מאגר כזה לא צריך להעביר אותם בחזרה אל 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 מחרוזת שמשמשת לזיהוי ייחודי של המסך. זה יכול להיות שם המכשיר הבסיסי (kernel) של המכשיר או השם של המכשיר, כמו צפייה אחורית. הערך של המחרוזת הזו נבחר על ידי HAL שנעשה בו שימוש באטימות על ידי המקבץ שלמעלה.
  • vendor_flags שיטה להעברת מצלמה מיוחדת מידע אטום מהנהג לאפליקציה מותאמת אישית של רכב חשמלי. היא עברה ללא פרשנות, מהנהג ועד לאפליקציית הרכב החשמלי (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 Manager מטמיעה את אותו API כמו של מנהלי ההתקנים הבסיסיים של HAL מספק שירות מורחב באמצעות תמיכה בלקוחות מרובים בו-זמנית (יותר מ- לקוח אחד יכול לפתוח את המצלמה דרך מנהל הרכב החשמלי (EVS) ולקבל סרטון ).

מנהל כלי רכב חשמליים (EVS) וגם
תרשים של EVS Hardware API.
איור 2. הכלי לניהול הרכב החשמלי (EVS) ישקף את הרכב החשמלי (EVS) Hardware API.

אין הבדלים באפליקציות במהלך ההפעלה באמצעות תקן HAL לחומרה של רכבים חשמליים (HAL). או על ה-EVS Manager API, אלא אם EVS Manager API מאפשר גישה לשידור בו-זמנית מהמצלמה. מנהל מערכת הרכב החשמלי (EVS) הוא עצמו מותר של שכבת HAL לחומרה של רכבים חשמליים (EVS), ומשמש כשרת proxy לחומרה של הרכבים החשמליים (EVS) HAL.

הקטעים הבאים מתארים רק את השיחות עם התנהגות (מורחבת) בהטמעה של EVS Manager; השיחות שנותרו זהה לתיאורים של EVS HAL.

IEvsEnumerator

openCamera(string camera_id) generates (IEvsCamera camera);

מקבל אובייקט בממשק שמשמש לאינטראקציה עם מצלמה ספציפית מזוהה באמצעות המחרוזת הייחודית של camera_id. מחזירה NULL במקרה של כשל. בשכבה EVS Manager, כל עוד יש מספיק משאבי מערכת, באמצעות תהליך אחר אפשר לפתוח שוב את המצלמה שכבר פתוחה, וכך שיוך של זרם הווידאו למספר אפליקציות לצרכנים. מחרוזות camera_id בשכבת EVS Manager זהות למחרוזות לשכבת החומרה של כלי רכב חשמליים (EVS).

מצלמת IEvs

הטמעת ה-IEvs Camera של מנהל המערכת לרכב חשמלי (EVS Manager) מבוססת באופן פנימי כך שפעולות של מצלמה על ידי לקוח אחד לא משפיעות על לקוחות אחרים, יכולים לגשת באופן עצמאי למצלמות שלהם.

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). חשבון Manager לא מוסיף פונקציונליות ופשוט מעביר את ממשק IEvsDisplay ישירות דרך הטמעת ה-HAL הבסיסית.

אפליקציית EVS

ב-Android יש יישום עזר מקורי של C++ של רכב חשמלי (EVS) אפליקציה שמתקשרת עם מנהל הרכבים החשמליים (EVS) וה-HAL של הרכב לספק פונקציות בסיסיות של המצלמה האחורית. האפליקציה צפויה להתחיל לפעול בשלב מוקדם מאוד בתהליך האתחול של המערכת, כשמוצג סרטון מתאים בהתאם המצלמות הזמינות ומצב הרכב (מצב האותות וגלגל השיניים). יצרני ציוד מקורי יכולים לשנות או להחליף את האפליקציה לרכב חשמלי (EVS) באפליקציה ספציפית לרכב בלוגיקה ומצגת.

איור 3. לוגיקה לדוגמה של אפליקציית EVS, אחזור המצלמה .


איור 4. לוגיקה של דגימת אפליקציה של EVS, קבלה קריאה חוזרת של מסגרת (frame callback).

כי נתוני התמונה מוצגים לאפליקציה בגרפיקה סטנדרטית מאגר נתונים זמני, האפליקציה אחראית להעברת התמונה מהמקור בתוך מאגר הנתונים הזמני. אומנם מוצגת העלות של עותק נתונים, היא גם מציעה לאפליקציה הזדמנות לעבד את התמונה להציג את מאגר הנתונים הזמני בכל צורה שהיא רוצה.

לדוגמה, האפליקציה עשויה לבחור להעביר את נתוני הפיקסלים עצמה, אולי באמצעות פעולת קנה מידה או סיבוב בתוך שורה. האפליקציה יכולה לבחור גם להשתמש בתמונת המקור כמרקם של OpenGL וליצור למאגר הנתונים הזמני של הפלט, כולל רכיבים וירטואליים כמו סמלים, הנחיות ואנימציות. אפליקציה מתוחכמת יותר עשויה גם לבחור כמה מצלמות קלט בו-זמנית וממזגות אותן למסגרת פלט אחת (למשל, לשימוש בתצוגה וירטואלית מלמעלה למטה של סביבת הרכב).

שימוש ב-EGL/SurfaceFlinger בצג HAL של הרכב החשמלי (EVS)

בקטע הזה מוסבר איך להשתמש ב-EGL כדי לעבד הטמעה של תצוגה עם HAL של רכב חשמלי. ב-Android 10.

רכב חשמלי בהטמעה של קובצי עזר של HAL נעשה שימוש ב-EGL כדי לעבד את התצוגה המקדימה של המצלמה במצב מופעל במסך והוא משתמש ב-libgui כדי ליצור את שטח היעד לעיבוד של EGL. ב-Android 8 ואילך), libgui מסווג כVNDK-private, מתייחסת לקבוצה של ספריות שזמינות לספריות VNDK שתהליכי ספקים לא יכולים להשתמש בהן. מאחר שהטמעות HAL חייבות להיכלל במחיצת הספק, אנחנו מונעים מהספקים להשתמש משטח בהטמעות HAL.

בניית libgui לתהליכי ספקים

השימוש ב-libgui משמש כאפשרות היחידה לשימוש ב-EGL/SurfaceFlinger. בהטמעות HAL של תצוגה לרכב חשמלי. הדרך הישירה ביותר להטמיע את libgui היא עד frameworks/Native/libs/gui ישירות באמצעות יעד build נוסף בסקריפט של ה-build. היעד הזה זהה בדיוק היעד 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);

build לדוגמה מופיעות בהמשך. צפי לקבלת $(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

שימוש ב-binder בהטמעת EVS HAL

ב-Android 8 (ואילך), הצומת של המכשיר /dev/binder הפך לבלעדי ל framework , ולכן לא נגיש לתהליכי ספקים. במקום זאת, בתהליכי הספק צריך להשתמש ב-/dev/hwbinder ולהמיר את כל ממשקי AIDL ל-HIDL. אם רוצים להמשיך להשתמש בממשקי AIDL בין תהליכי ספקים, יש להשתמש בדומיין ה-Binder, /dev/vndbinder.

דומיין IPC תיאור
/dev/binder IPC בין תהליכי framework ואפליקציה עם ממשקי AIDL
/dev/hwbinder IPC בין תהליכי framework/ספק עם ממשקי HIDL
IPC בין תהליכי ספק עם ממשקי HIDL
/dev/vndbinder IPC בין תהליכי ספק/ספק באמצעות ממשקי AIDL

למרות ש-SurfaceFlinger מגדיר ממשקי AIDL, תהליכי ספק יכולים להשתמש בממשקי HIDL רק כדי לתקשר עם תהליכי framework. נדרשת עבודה קטנה כדי להמיר את הנכס הקיים מתבצע ממשק של 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, או לפני ביצוע הפעלות של binder.

המדיניות של SELinux

אם הטמעת המכשיר מתבצעת בטרבל מלא, SELinux מונע את הספק תהליכים משימוש ב-/dev/binder. למשל, דגימת HAL של EVS מוקצה לדומיין 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

עם זאת, הוספת ההרשאות האלו גורמת לכשל ב-build כי היא מפירה את הכללים הבאים אף פעם לא מתירים כללים שמוגדרים ב-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;