HIDL

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

HIDL מיועד לשמש לתקשורת בין תהליכים (IPC). HALs שנוצרו עם HDL נקראים HALs binerized בכך שהם יכולים לתקשר עם שכבות ארכיטקטורה אחרות באמצעות שיחות תקשורת בין-תהליכים של binder (IPC). HALs מכוננים פועלים בתהליך נפרד מהלקוח שמשתמש בהם. עבור ספריות שחייבות להיות מקושרות לתהליך, זמין גם מצב מעבר (לא נתמך ב-Java).

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

טרמינולוגיה

סעיף זה משתמש במונחים הבאים הקשורים ל-HIDL:

מקושר מציין ש-HIDL נמצא בשימוש עבור קריאות פרוצדורות מרחוק בין תהליכים, מיושם באמצעות מנגנון דמוי Binder. ראה גם מעבר .
התקשרות חוזרת, אסינכרונית ממשק המוגש על ידי משתמש HAL, הועבר ל-HAL (בשיטת HIDL), ונקרא על ידי ה-HAL להחזיר נתונים בכל עת.
התקשרות חוזרת, סינכרונית מחזיר נתונים מיישום שיטת HIDL של שרת ללקוח. לא בשימוש עבור שיטות המחזירות ריק או ערך פרימיטיבי יחיד.
לָקוּחַ תהליך הקורא לשיטות של ממשק מסוים. תהליך HAL או אנדרואיד עשוי להיות לקוח של ממשק אחד ושרת של אחר. ראה גם מעבר .
משתרע מציין ממשק שמוסיף שיטות ו/או סוגים לממשק אחר. ממשק יכול להרחיב רק ממשק אחד אחר. יכול לשמש עבור תוספת גרסה מינורית באותו שם חבילה או עבור חבילה חדשה (למשל הרחבת ספק) כדי לבנות על חבילה ישנה יותר.
מייצר מציין שיטת ממשק המחזירה ערכים ללקוח. כדי להחזיר ערך אחד לא פרימיטיבי, או יותר מערך אחד, נוצרת פונקציית התקשרות חוזרת סינכרונית.
מִמְשָׁק אוסף שיטות וסוגים. תורגם למחלקה ב-C++ או Java. כל השיטות בממשק נקראות באותו כיוון: תהליך לקוח מפעיל שיטות המיושמות על ידי תהליך שרת.
דרך אחת כאשר מוחל על שיטת HIDL, מציין שהשיטה אינה מחזירה ערכים ואינה חוסמת.
חֲבִילָה אוסף ממשקים וסוגי נתונים החולקים גרסה.
עובר דרך מצב HIDL שבו השרת הוא ספרייה משותפת, dlopen הלקוח. במצב מעבר, לקוח ושרת הם אותו תהליך אך בסיסי קוד נפרדים. משמש רק להכנסת בסיסי קוד מדור קודם למודל HIDL. ראה גם בינדריזד .
שרת תהליך המיישם שיטות של ממשק. ראה גם מעבר .
תַחְבּוּרָה תשתית HIDL המעבירה נתונים בין השרת ללקוח.
גִרְסָה גרסה של חבילה. מורכב משני מספרים שלמים, מז'ור וקטן. עליות גרסאות קטנות עשויות להוסיף (אך לא לשנות) סוגים ושיטות.

עיצוב HIDL

המטרה של HIDL היא שניתן להחליף את מסגרת האנדרואיד ללא צורך בבנייה מחדש של HALs. HALs ייבנו על ידי ספקים או יצרני SOC ויוכנסו למחיצת /vendor במכשיר, מה שמאפשר להחליף את ה-Android, במחיצה משלה, ב-OTA מבלי להדר מחדש את ה-HALs.

עיצוב HIDL מאזן את החששות הבאים:

  • יכולת פעולה הדדית . צור ממשקים המאפשרים פעולה הדדית מהימנה בין תהליכים העשויים להיות מורכבים עם ארכיטקטורות שונות, רשתות כלים ותצורות לבנות. ממשקי HIDL הם בעלי גרסאות ולא ניתן לשנות אותם לאחר פרסומם.
  • יעילות . HIDL מנסה למזער את מספר פעולות ההעתקה. נתונים המוגדרים ב-HIDL נמסרים לקוד C++ במבני נתוני פריסה סטנדרטיים של C++ שניתן להשתמש בהם מבלי לפרוק. HIDL מספקת גם ממשקי זיכרון משותפים, וכיוון ש-RPCs מטבעם איטיים במקצת, HIDL תומך בשתי דרכים להעברת נתונים ללא שימוש בשיחת RPC: זיכרון משותף ו-Fast Message Queue (FMQ).
  • אינטואיטיבי . HIDL נמנע מבעיות קוצניות של בעלות על זיכרון על ידי שימוש רק in עבור RPC (ראה שפת הגדרת ממשק אנדרואיד (AIDL) ); ערכים שלא ניתן להחזיר ביעילות משיטות מוחזרים באמצעות פונקציות התקשרות חוזרת. גם העברת נתונים ל-HIDL לצורך העברה וגם לא קבלת נתונים מ-HIDL משנים את הבעלות על הנתונים - הבעלות נשארת תמיד עם פונקציית ההתקשרות. הנתונים צריכים להימשך רק למשך הפונקציה שנקראה ועשויים להיהרס מיד לאחר החזרה של הפונקציה שנקראה.

שימוש במצב מעבר

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

מצב מעבר זמין רק עבור לקוחות C++ והטמעות. למכשירים המריצים גרסאות קודמות של אנדרואיד אין HALs כתובים ב-Java, כך ש-Java HALs מטבעם מאוגדים.

כאשר קובץ .hal מורכב, hidl-gen מייצר קובץ כותרת מעבר מעבר נוסף BsFoo.h בנוסף לכותרות המשמשות לתקשורת קלסר; כותרת זו מגדירה פונקציות שיש dlopen אותן. מכיוון ש-HALs של מעבר עוברים פועלים באותו תהליך שבו הם נקראים, ברוב המקרים שיטות מעבר מופעלות באמצעות קריאת פונקציה ישירה (אותו שרשור). שיטות oneway פועלות בשרשור משלהן מכיוון שהן אינן מיועדות לחכות שה-HAL יעבד אותן (משמעות הדבר היא שכל HAL שמשתמש בשיטות חד oneway כיווניות במצב מעבר חייב להיות בטוח ל-thread).

בהינתן IFoo.hal , BsFoo.h עוטף את השיטות שנוצרו על ידי HIDL כדי לספק תכונות נוספות (כגון ביצוע עסקאות oneway לרוץ בשרשור אחר). קובץ זה דומה ל- BpFoo.h , אולם במקום להעביר שיחות IPC באמצעות קלסר, הפונקציות הרצויות מופעלות ישירות. יישומים עתידיים של HALs עשויים לספק יישומים מרובים, כגון FooFast HAL ו- FooAccurate HAL. במקרים כאלה, ייווצר קובץ עבור כל מימוש נוסף (למשל, PTFooFast.cpp ו- PTFooAccurate.cpp ).

HALs למעבר מקשר מקשר

אתה יכול לייגד יישומי HAL התומכים במצב מעבר. בהינתן ממשק HAL abcd@MN::IFoo , נוצרות שתי חבילות:

  • abcd@MN::IFoo-impl . מכיל את היישום של ה-HAL וחושף את הפונקציה IFoo* HIDL_FETCH_IFoo(const char* name) . במכשירים מדור קודם, חבילה זו dlopen והיישום מתבצע באמצעות HIDL_FETCH_IFoo . אתה יכול ליצור את קוד הבסיס באמצעות hidl-gen ו- -Lc++-impl ו- -Landroidbp-impl .
  • abcd@MN::IFoo-service . פותח את ה-Passthrough HAL ורושם את עצמו כשירות מקשר, מה שמאפשר להשתמש באותו יישום HAL כ-Passthrough וגם כ-Binderized.

בהינתן הסוג IFoo , אתה יכול לקרוא ל- sp<IFoo> IFoo::getService(string name, bool getStub) כדי לקבל גישה למופע של IFoo . אם getStub נכון, getService מנסה לפתוח את ה-HAL רק במצב מעבר. אם getStub הוא false, getService מנסה למצוא שירות מקשר; אם זה נכשל, הוא מנסה למצוא את שירות המעבר. אין להשתמש בפרמטר getStub אלא ב- defaultPassthroughServiceImplementation . (מכשירים המופעלים עם אנדרואיד O הם מכשירים עם חיבור מלא, כך שפתיחת שירות במצב מעבר אסורה.)

דקדוק HIDL

בתכנון, שפת ה-HIDL דומה ל-C (אך אינה משתמשת במעבד הקדם-C). כל סימני הפיסוק שלא מתוארים להלן (מלבד השימוש הברור ב- = ו- | ) הוא חלק מהדקדוק.

הערה: לפרטים על סגנון קוד HIDL, עיין במדריך סגנון הקוד .

  • /** */ מציין הערת תיעוד. ניתן להחיל אותם רק על הצהרות ערך מסוג, שיטה, שדה ו-enum.
  • /* */ מציין הערה מרובת שורות.
  • // מציין הערה לסוף השורה. מלבד // , שורות חדשות זהות לכל רווח לבן אחר.
  • בדקדוק לדוגמה למטה, טקסט מ // עד סוף השורה אינו חלק מהדקדוק אלא מהווה הערה על הדקדוק.
  • [empty] פירושו שהמונח עשוי להיות ריק.
  • ? אחרי מילולי או מונח פירושו שהוא אופציונלי.
  • ... מציין רצף המכיל אפס פריטים או יותר עם סימני פיסוק מפרידים כמצוין. אין טיעונים וריאדיים ב-HIDL.
  • פסיקים מפרידים בין רכיבי רצף.
  • נקודה-פסיק מסיימת כל אלמנט, כולל האלמנט האחרון.
  • UPPERCASE הוא לא טרמינלי.
  • italics היא משפחת אסימונים כגון integer או identifier (כללי ניתוח C סטנדרטיים).
  • constexpr הוא ביטוי קבוע בסגנון C (כגון 1 + 1 ו 1L << 3 ).
  • import_name הוא שם חבילה או ממשק, המתאימים כמתואר ב- HIDL Versioning .
  • words קטנות הן אסימונים מילוליים.

דוגמא:

ROOT =
    PACKAGE IMPORTS PREAMBLE { ITEM ITEM ... }  // not for types.hal
  | PACKAGE IMPORTS ITEM ITEM...  // only for types.hal; no method definitions

ITEM =
    ANNOTATIONS? oneway? identifier(FIELD, FIELD ...) GENERATES?;
  |  safe_union identifier { UFIELD; UFIELD; ...};
  |  struct identifier { SFIELD; SFIELD; ...};  // Note - no forward declarations
  |  union identifier { UFIELD; UFIELD; ...};
  |  enum identifier: TYPE { ENUM_ENTRY, ENUM_ENTRY ... }; // TYPE = enum or scalar
  |  typedef TYPE identifier;

VERSION = integer.integer;

PACKAGE = package android.hardware.identifier[.identifier[...]]@VERSION;

PREAMBLE = interface identifier EXTENDS

EXTENDS = <empty> | extends import_name  // must be interface, not package

GENERATES = generates (FIELD, FIELD ...)

// allows the Binder interface to be used as a type
// (similar to typedef'ing the final identifier)
IMPORTS =
   [empty]
  |  IMPORTS import import_name;

TYPE =
  uint8_t | int8_t | uint16_t | int16_t | uint32_t | int32_t | uint64_t | int64_t |
 float | double | bool | string
|  identifier  // must be defined as a typedef, struct, union, enum or import
               // including those defined later in the file
|  memory
|  pointer
|  vec<TYPE>
|  bitfield<TYPE>  // TYPE is user-defined enum
|  fmq_sync<TYPE>
|  fmq_unsync<TYPE>
|  TYPE[SIZE]

FIELD =
   TYPE identifier

UFIELD =
   TYPE identifier
  |  safe_union identifier { FIELD; FIELD; ...} identifier;
  |  struct identifier { FIELD; FIELD; ...} identifier;
  |  union identifier { FIELD; FIELD; ...} identifier;

SFIELD =
   TYPE identifier
  |  safe_union identifier { FIELD; FIELD; ...};
  |  struct identifier { FIELD; FIELD; ...};
  |  union identifier { FIELD; FIELD; ...};
  |  safe_union identifier { FIELD; FIELD; ...} identifier;
  |  struct identifier { FIELD; FIELD; ...} identifier;
  |  union identifier { FIELD; FIELD; ...} identifier;

SIZE =  // Must be greater than zero
     constexpr

ANNOTATIONS =
     [empty]
  |  ANNOTATIONS ANNOTATION

ANNOTATION =
  |  @identifier
  |  @identifier(VALUE)
  |  @identifier(ANNO_ENTRY, ANNO_ENTRY  ...)

ANNO_ENTRY =
     identifier=VALUE

VALUE =
     "any text including \" and other escapes"
  |  constexpr
  |  {VALUE, VALUE ...}  // only in annotations

ENUM_ENTRY =
     identifier
  |  identifier = constexpr