hiDL

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

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

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

טרמינולוגיה

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

מקושר מציין שנעשה שימוש ב-HIDL עבור הפעלות לשירות מרחוק בין תהליכים הוטמעו באמצעות מנגנון דמוי Binder. כדאי לעיין גם במעבר חיצוני.
קריאה חוזרת, אסינכרונית ממשק שמופעל על ידי משתמש HAL, שמועבר ל-HAL (באמצעות שיטת HIDL), וגם שנקראה על ידי HAL כדי להחזיר נתונים בכל שלב.
קריאה חוזרת, סינכרונית מחזירה נתונים מהטמעה של שיטת HIDL של שרת ללקוח. לא משתמשים בו ל-methods שמחזירות ערך מבוטל או ערך ראשוני יחיד.
לקוח תהליך שקורא ל-methods של ממשק מסוים. מסגרת HAL או Android יכול להיות לקוח של ממשק אחד ושרת של ממשק אחר. עוד באותו הקשר העברת מידע.
נמשך מציין ממשק שמוסיף שיטות ו/או סוגים לממשק אחר. ממשק יכול להרחיב רק ממשק אחד נוסף. מתאים לקטינים מספר גרסאות באותו שם חבילה או עבור חבילה חדשה (למשל, ספק ) כדי להתבסס על חבילה ישנה יותר.
יוצרת מציינת שיטת ממשק שמחזירה ערכים ללקוח. כדי לחזור ערך לא פרימיטיבי אחד, או יותר מערך אחד, פונקציית קריאה חוזרת סינכרונית נוצרת.
ממשק אוסף של שיטות וסוגים. תורגם לכיתה ב-C++ או ב-Java. הכול ה-methods בממשק נקראים באותו כיוון: תהליך לקוח מפעיל שיטות שהוטמעו על ידי תהליך בשרת.
כיוון אחד כשמחילים את השיטה HIDL, היא מציינת שהשיטה לא מחזירה ערכים לא חוסם.
חבילה איסוף של ממשקים וסוגי נתונים שחולקים גרסה.
העברת סיגנל ללא שינוי מצב HIDL שבו השרת הוא ספרייה משותפת, dlopened על ידי הלקוח. במצב מעבר, הלקוח והשרת הם אותו תהליך, אבל באמצעות Codebases נפרדים. משמש רק להעברת מסדי קוד מדור קודם למודל HIDL. ראו גם Binderized.
שרת תהליך שמטמיע שיטות בממשק. עוד באותו הקשר העברת מידע.
תחבורה תשתית HIDL שמעבירה נתונים בין השרת ללקוח.
גרסה גרסת החבילה. מורכב משני מספרים שלמים: ראשי וקטין. עריכות קלות שדרוגי גרסאות עשויים להוסיף (אבל לא לשנות) סוגים ושיטות.

עיצוב HIDL

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

תכנון HIDL מאזן את החששות הבאים:

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

שימוש במצב העברת נתונים

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

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

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

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

HAL של העברה מסוג העברת נתונים

אפשר לקשר בין הטמעות HAL שתומכות במצב העברה. בהינתן בממשק HAL a.b.c.d@M.N::IFoo, נוצרות שתי חבילות:

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

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

דקדוק HIDL

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

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

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