אנדרואיד מכיל שכבת HIDL Hardware Abstraction Layer (HAL) לרכב המספקת לכידת תמונות והצגה בשלב מוקדם מאוד בתהליך האתחול של אנדרואיד וממשיכה לתפקד למשך כל חיי המערכת. ה-HAL כולל את ערימת מערכת התצוגה החיצונית (EVS) ומשמשת בדרך כלל לתמיכה במצלמות אחוריות ובתצוגות תצוגה היקפית בכלי רכב עם מערכות אינפורמציה לרכב (IVI) מבוססות אנדרואיד. EVS גם מאפשר ליישם תכונות מתקדמות באפליקציות משתמש.
אנדרואיד כולל גם ממשק מנהל התקן לכידה ותצוגה ספציפי ל-EVS (ב /hardware/interfaces/automotive/evs/1.0
evs/1.0). אמנם ניתן לבנות אפליקציית מצלמה אחורית על גבי שירותי המצלמה והתצוגה הקיימים של אנדרואיד, אך סביר להניח שאפליקציה כזו תפעל מאוחר מדי בתהליך האתחול של אנדרואיד. שימוש ב-HAL ייעודי מאפשר ממשק יעיל ומבהיר מה יצרן OEM צריך ליישם כדי לתמוך בערימת EVS.
רכיבי מערכת
EVS כולל את רכיבי המערכת הבאים:
![דיאגרמת רכיבי מערכת EVS](https://source.android.com/static/docs/automotive/images/vhal_evs_components.png?authuser=1&hl=he)
אפליקציית EVS
אפליקציית C++ EVS לדוגמה ( /packages/services/Car/evs/app
) משמשת כיישום ייחוס. אפליקציה זו אחראית לבקשת מסגרות וידאו מ-EVS Manager ושליחת פריימים מוגמרים לתצוגה בחזרה ל-EVS Manager. זה מצפה להיות מופעל על ידי init ברגע ש-EVS ושירות רכב יהיו זמינים, ממוקד תוך שתי (2) שניות מההפעלה. יצרני OEM יכולים לשנות או להחליף את אפליקציית 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
.
ה-OEM אחראי להטמעת ה-API המובע על ידי קבצי ה-hal ב- /hardware/interfaces/automotive/evs
. יישומים כאלה אחראים על קביעת התצורה ואיסוף הנתונים ממצלמות פיזיות ומסירתם דרך מאגרי זיכרון משותפים הניתנים לזיהוי על ידי Gralloc. צד התצוגה של היישום אחראי על אספקת מאגר זיכרון משותף שניתן למלא על ידי האפליקציה (בדרך כלל עם עיבוד EGL) והצגת הפריימים המוגמרים בהעדפה לכל דבר אחר שאולי ירצה להופיע בתצוגה הפיזית. הטמעות של ספקים של ממשק EVS עשויות להיות מאוחסנות תחת /vendor/… /device/…
או hardware/…
(למשל, /hardware/[vendor]/[platform]/evs
).
נהגי ליבה
מכשיר התומך בערימת EVS דורש מנהלי התקנים של ליבה. במקום ליצור מנהלי התקנים חדשים, ליצרני ציוד מקורי יש אפשרות לתמוך בתכונות הנדרשות ל-EVS באמצעות מנהלי התקנים קיימים של מצלמה או תצוגה. שימוש חוזר במנהלי התקנים יכול להיות יתרון, במיוחד עבור מנהלי התקנים לתצוגה שבהם הצגת תמונה עשויה לדרוש תיאום עם שרשורים פעילים אחרים. אנדרואיד 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()
). יש לעצור את זרם הווידאו של המצלמה על ידי קריאה ל- 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 Display (ראה IEvsDisplay API ).
struct CameraDesc { string camera_id; int32 vendor_flags; // Opaque value }
-
camera_id
. מחרוזת המזהה באופן ייחודי מצלמה נתונה. יכול להיות שם מכשיר הליבה של ההתקן או שם למכשיר, כגון ראייה לאחור . הערך עבור מחרוזת זו נבחר על ידי יישום HAL ומשמש בצורה אטומה על ידי המחסנית שלמעלה. -
vendor_flags
. שיטה להעברת מידע מצלמה מיוחד בצורה אטומה מהנהג לאפליקציית EVS מותאמת אישית. זה מועבר ללא פירוש מהנהג עד לאפליקציית EVS, שהיא חופשית להתעלם ממנה.
IEvsCamera
אובייקט זה מייצג מצלמה בודדת והוא הממשק העיקרי ללכידת תמונות.
getCameraInfo() generates (CameraDesc info);
מחזירה CameraDesc
של מצלמה זו.
setMaxFramesInFlight(int32 bufferCount) generates (EvsResult result);
מציין את עומק שרשרת המאגר שהמצלמה מתבקשת לתמוך בו. עד זה מספר מסגרות עשוי להתקיים במקביל על ידי הלקוח של IEvsCamera. אם מסגרות רבות כל כך נמסרו למקלט מבלי שהוחזרו על ידי doneWithFrame
, הזרם מדלג על פריימים עד להחזרת מאגר לשימוש חוזר. זה חוקי שהקריאה הזו תגיע בכל עת, גם בזמן שהזרמים כבר פועלים, ובמקרה זה יש להוסיף או להסיר מאגרים מהשרשרת לפי הצורך. אם לא מתבצעת שיחה לנקודת כניסה זו, ה-IEvsCamera תומכת לפחות מסגרת אחת כברירת מחדל; עם יותר מקובל.
אם לא ניתן להכיל את bufferCount המבוקש, הפונקציה מחזירה את BUFFER_NOT_AVAILABLE
או קוד שגיאה רלוונטי אחר. במקרה זה, המערכת ממשיכה לפעול עם הערך שהוגדר קודם לכן.
startVideoStream(IEvsCameraStream receiver) generates (EvsResult result);
מבקש משלוח מסגרות למצלמות EVS ממצלמה זו. ה-IEvsCameraStream מתחיל לקבל שיחות תקופתיות עם מסגרות תמונה חדשות עד שנקרא stopVideoStream()
. מסגרות חייבות להתחיל להיות מסופקות בתוך 500 אלפיות השנייה מהשיחה startVideoStream
ולאחר ההתחלה, חייבות להיווצר במינימום של 10 FPS. הזמן הדרוש להפעלת זרם הווידאו נספר ביעילות כנגד כל דרישה לזמן ההפעלה של המצלמה האחורית. אם הזרם לא מופעל, יש להחזיר קוד שגיאה; אחרת אישור מוחזר.
oneway doneWithFrame(BufferDesc buffer);
מחזירה מסגרת שנמסרה על ידי ל-IEvsCameraStream. בסיום צריכת מסגרת שנמסרה לממשק 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 }
מתאר תמונה שעברה דרך ה-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()
, התקשרות חוזרת זו עשויה להימשך כאשר הצינור מתנקז. עדיין יש להחזיר כל מסגרת; כאשר המסגרת האחרונה בזרם נמסרה, נמסר NULL bufferHandle
, המסמל את סוף הזרם ולא מתרחשות מסירות פריים נוספות. אין צורך לשלוח את ה- NULL bufferHandle
עצמו בחזרה עם doneWithFrame()
, אך יש להחזיר את כל שאר ה-handles
בעוד פורמטים קנייניים של מאגר אפשריים מבחינה טכנית, בדיקת התאימות דורשת שהמאגר יהיה באחד מארבעה פורמטים נתמכים: NV21 (YCrCb 4:2:0 Semi-Planar), YV12 (YCrCb 4:2:0 מישור), 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
. תמיד מחזיר אישור אלא אם כן המצב המבוקש הוא ערך ספירה לא מזוהה, ובמקרה זה INVALID_ARG
מוחזר.
getDisplayState() generates (DisplayState state);
מקבל את מצב התצוגה. יישום HAL צריך לדווח על המצב הנוכחי בפועל, שעשוי להיות שונה מהמצב המבוקש האחרון. ההיגיון האחראי לשינוי מצבי תצוגה צריך להתקיים מעל שכבת המכשיר, מה שהופך אותו לבלתי רצוי שיישום HAL ישנה באופן ספונטני מצבי תצוגה.
getTargetBuffer() generates (handle bufferHandle);
מחזיר נקודת אחיזה למאגר מסגרת המשויך לתצוגה. מאגר זה עשוי להיות נעול ולכתוב אליו באמצעות תוכנה ו/או GL. יש להחזיר את המאגר הזה עם קריאה ל- returnTargetBufferForDisplay()
גם אם התצוגה כבר לא גלויה.
בעוד פורמטים קנייניים של מאגר אפשריים מבחינה טכנית, בדיקת התאימות דורשת שהמאגר יהיה באחד מארבעה פורמטים נתמכים: NV21 (YCrCb 4:2:0 Semi-Planar), 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
. מחרוזת המזהה באופן ייחודי את התצוגה. זה יכול להיות שם מכשיר הליבה של ההתקן, או שם למכשיר, כגון Rearview . הערך עבור מחרוזת זו נבחר על ידי יישום 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 ו-EVS Hardware API.](https://source.android.com/static/docs/automotive/images/vhal_evs_manager.png?authuser=1&hl=he)
אפליקציות אינן רואות הבדלים כאשר הן פועלות באמצעות יישום EVS Hardware HAL או EVS Manager API, מלבד העובדה שה-API של EVS Manager מאפשר גישה לזרימת מצלמה במקביל. מנהל ה-EVS הוא, בעצמו, הלקוח המותר של שכבת EVS Hardware HAL, ופועל כפרוקסי עבור EVS Hardware HAL.
הסעיפים הבאים מתארים רק את אותן שיחות שיש להן התנהגות שונה (מורחבת) ביישום EVS Manager; השיחות הנותרות זהות לתיאורי EVS HAL.
IEvsEnumerator
openCamera(string camera_id) generates (IEvsCamera camera);
משיג אובייקט ממשק המשמש לאינטראקציה עם מצלמה ספציפית המזוהה על ידי מחרוזת ה-camera_id הייחודית. מחזירה NULL בעת כשל. בשכבת EVS Manager, כל עוד יש מספיק משאבי מערכת זמינים, מצלמה שכבר פתוחה עשויה להיפתח שוב על ידי תהליך אחר, מה שמאפשר העברת זרם הווידאו למספר אפליקציות צרכניות. מחרוזות ה- camera_id
בשכבת EVS Manager זהות לאלו שדווחו לשכבת EVS Hardware.
IEvsCamera
מנהל ה-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
אנדרואיד כולל יישום ייחוס מקורי של C++ של אפליקציית EVS המתקשרת עם EVS Manager ו-Vehicle HAL כדי לספק פונקציות בסיסיות של מצלמה אחורית. האפליקציה צפויה להתחיל מוקדם מאוד בתהליך האתחול של המערכת, כאשר סרטון מתאים יוצג בהתאם למצלמות הזמינות ולמצב המכונית (מצב גיר ואותת פנייה). יצרני OEM יכולים לשנות או להחליף את אפליקציית EVS בהיגיון ובמצגת הספציפיים לרכב משלהם.
![](https://source.android.com/static/docs/automotive/images/vhal_evs_get_camera.png?authuser=1&hl=he)
![](https://source.android.com/static/docs/automotive/images/vhal_evs_receive_frame.png?authuser=1&hl=he)
מכיוון שנתוני תמונה מוצגים לאפליקציה במאגר גרפי סטנדרטי, האפליקציה אחראית להעברת התמונה ממאגר המקור למאגר הפלט. אמנם זה מציג את העלות של עותק נתונים, אבל זה גם מציע את ההזדמנות לאפליקציה להחזיר את התמונה למאגר התצוגה בכל אופן שהיא רוצה.
לדוגמה, האפליקציה עשויה לבחור להעביר את נתוני הפיקסלים בעצמה, פוטנציאלית באמצעות קנה מידה מוטבע או פעולת סיבוב. האפליקציה יכולה גם לבחור להשתמש בתמונת המקור כמרקם 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
היא באמצעות frameworks/native/libs/gui ישירות על ידי שימוש ביעד בנייה נוסף בסקריפט ה-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);
הוראות בנייה לדוגמה מסופקות להלן. צפו לקבל $(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. למרבה המזל, אנדרואיד מספקת שיטה שבה ניתן לבחור את מנהל ההתקן של ה-binder עבור 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)
בניית יישום הפניות של 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;