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