HIDL דורש שכל ממשק שנכתב ב-HIDL יהיה בעל גרסה. לאחר פרסום ממשק HAL, הוא מוקפא ויש לבצע שינויים נוספים בגרסה חדשה של ממשק זה. אמנם לא ניתן לשנות ממשק נתון, אך ניתן להרחיב אותו על ידי ממשק אחר.
מבנה קוד HIDL
קוד HIDL מאורגן בסוגים, ממשקים וחבילות מוגדרות על ידי המשתמש:
- סוגים מוגדרי משתמש (UDTs) . HIDL מספקת גישה לקבוצה של סוגי נתונים פרימיטיביים שניתן להשתמש בהם כדי להרכיב טיפוסים מורכבים יותר באמצעות מבנים, איגודים וספירות. UDTs מועברים לשיטות של ממשקים, וניתן להגדיר אותם ברמת חבילה (משותף לכל הממשקים) או מקומית לממשק.
- ממשקים . כאבן בניין בסיסי של HIDL, ממשק מורכב מהצהרות UDT והצהרות שיטה. ממשקים יכולים לרשת גם מממשק אחר.
- חבילות . מארגן ממשקי HIDL קשורים וסוגי הנתונים עליהם הם פועלים. חבילה מזוהה על ידי שם וגרסה וכוללת את הדברים הבאים:
- קובץ הגדרה מסוג נתונים בשם
types.hal
. - אפס ממשקים או יותר, כל אחד בקובץ
.hal
משלו.
- קובץ הגדרה מסוג נתונים בשם
קובץ הגדרת סוג הנתונים types.hal
מכיל רק UDTs (כל UDTs ברמת החבילה נשמרים בקובץ בודד). ייצוגים בשפת היעד זמינים לכל הממשקים בחבילה.
פילוסופיית גירסאות
חבילת HIDL (כגון android.hardware.nfc
), לאחר שפורסמה עבור גרסה נתונה (כגון 1.0
), אינה ניתנת לשינוי; אי אפשר לשנות אותו. שינויים בממשקים בחבילה או כל שינוי ב-UDT שלה יכולים להתבצע רק בחבילה אחרת .
ב-HIDL, ניהול גרסאות חל ברמת החבילה, לא ברמת הממשק, וכל הממשקים וה-UDT בחבילה חולקים את אותה גרסה. גרסאות החבילות עוקבות אחר ניהול גרסאות סמנטי ללא רמת התיקון ורכיבי מטה-נתונים. בתוך חבילה נתונה, בליטת גרסה מינורית מרמזת שהגרסה החדשה של החבילה תואמת לאחור לחבילה הישנה, וחבטת גרסה גדולה מרמזת שהגרסה החדשה של החבילה אינה תואמת לאחור לחבילה הישנה.
מבחינה קונספטואלית, חבילה יכולה להתייחס לחבילה אחרת באחת מכמה דרכים:
- בכלל לא .
- יכולת הרחבה תואמת לאחור ברמת החבילה . זה קורה עבור uprevs של גירסה משנית חדשה (הגרסה המוגדלת הבאה) של חבילה; לחבילה החדשה יש את אותו שם וגרסה עיקרית כמו לחבילה הישנה, אך גרסה משנית גבוהה יותר. מבחינה פונקציונלית, החבילה החדשה היא ערכת-על של החבילה הישנה, כלומר:
- ממשקים ברמה העליונה של חבילת האב קיימים בחבילה החדשה, אם כי לממשקים עשויים להיות שיטות חדשות, UDT-ממשק מקומי חדש (הרחבה ברמת הממשק המתוארת להלן), ו-UDT חדשים ב-
types.hal
. - ניתן להוסיף לחבילה החדשה גם ממשקים חדשים.
- כל סוגי הנתונים של חבילת האב נמצאים בחבילה החדשה וניתן לטפל בהם בשיטות (אפשר להטמיע מחדש) מהחבילה הישנה.
- ניתן גם להוסיף סוגי נתונים חדשים לשימוש על ידי שיטות חדשות של ממשקים קיימים משודרגים, או על ידי ממשקים חדשים.
- ממשקים ברמה העליונה של חבילת האב קיימים בחבילה החדשה, אם כי לממשקים עשויים להיות שיטות חדשות, UDT-ממשק מקומי חדש (הרחבה ברמת הממשק המתוארת להלן), ו-UDT חדשים ב-
- יכולת הרחבה תואמת לאחור ברמת הממשק . החבילה החדשה יכולה גם להרחיב את החבילה המקורית על ידי מורכבת ממשקים נפרדים מבחינה לוגית שפשוט מספקים פונקציונליות נוספת, ולא את הליבה. לצורך כך, עשויים להיות רצויים:
- ממשקים בחבילה החדשה מצריכים פנייה לסוגי הנתונים של החבילה הישנה.
- ממשקים בחבילה חדשה עשויים להרחיב ממשקים של חבילה ישנה אחת או יותר.
- הרחב את חוסר התאימות המקורי לאחור . זהו גרסה עיקרית של החבילה ואין צורך להיות מתאם בין השניים. במידה שיש, זה יכול להתבטא בשילוב של סוגים מהגרסה הישנה יותר של החבילה, והורשה של תת-קבוצה של ממשקי החבילה הישנה.
בניית ממשקים
עבור ממשק מובנה היטב, הוספת סוגים חדשים של פונקציונליות שאינם חלק מהעיצוב המקורי צריכה לדרוש שינוי בממשק HIDL. לעומת זאת, אם אתה יכול או מצפה לבצע שינוי משני צידי הממשק שמציג פונקציונליות חדשה מבלי לשנות את הממשק עצמו, אז הממשק אינו מובנה.
טרבל תומך ברכיבי ספק ומערכת שהורכבו בנפרד, שבהם ניתן להרכיב את vendor.img
במכשיר ואת system.img
בנפרד. כל האינטראקציות בין vendor.img
ו- system.img
חייבות להיות מוגדרות באופן מפורש ויסודי כדי שיוכלו להמשיך לעבוד במשך שנים רבות. זה כולל משטחי API רבים, אך משטח עיקרי הוא מנגנון ה-IPC ש-HIDL משתמש בו לתקשורת בין תהליכים על גבול system.img
/ vendor.img
.
דרישות
כל הנתונים המועברים דרך HIDL חייבים להיות מוגדרים במפורש. כדי להבטיח שיישום ולקוח יוכלו להמשיך לעבוד יחד גם כשהם מורכבים בנפרד או מפותחים באופן עצמאי, הנתונים חייבים לעמוד בדרישות הבאות:
- ניתן לתאר ב-HIDL ישירות (באמצעות structs enums וכו') עם שמות ומשמעות סמנטיים.
- ניתן לתאר על ידי תקן ציבורי כגון ISO/IEC 7816.
- ניתן לתאר לפי תקן חומרה או פריסה פיזית של חומרה.
- יכולים להיות נתונים אטומים (כגון מפתחות ציבוריים, מזהים וכו') במידת הצורך.
אם נעשה שימוש בנתונים אטומים, יש לקרוא אותם רק על ידי צד אחד של ממשק HIDL. לדוגמה, אם קוד vendor.img
נותן לרכיב ב- system.img
הודעת מחרוזת או נתוני vec<uint8_t>
, לא ניתן לנתח את הנתונים האלה על-ידי system.img
עצמו; ניתן להעביר אותו בחזרה רק אל vendor.img
כדי לפרש. בעת העברת ערך מ- vendor.img
לקוד ספק ב- system.img
או למכשיר אחר, יש לתאר במדויק את הפורמט של הנתונים וכיצד יש לפרש אותם והוא עדיין חלק מהממשק .
הנחיות
אתה אמור להיות מסוגל לכתוב יישום או לקוח של HAL באמצעות קבצי .hal בלבד (כלומר לא צריך להסתכל על המקור של אנדרואיד או בסטנדרטים הציבוריים). אנו ממליצים לציין את ההתנהגות הנדרשת המדויקת. הצהרות כגון "יישום עשוי לעשות A או B" מעודדות יישומים להשתלב עם הלקוחות איתם הם פותחו.
פריסת קוד HIDL
HIDL כולל חבילות ליבה וספקים.
ממשקי ליבה HIDL הם אלה שצוינו על ידי Google. החבילות שהן שייכות להן מתחילות ב- android.hardware.
ונקראים לפי תת-מערכת, אולי עם רמות מקוננות של שמות. לדוגמה, חבילת ה-NFC נקראת android.hardware.nfc
וחבילת המצלמה היא android.hardware.camera
. באופן כללי, לחבילת ליבה יש את השם android.hardware.
[ name1
].[ name2
]…. לחבילות HIDL יש גרסה בנוסף לשם שלהן. לדוגמה, החבילה android.hardware.camera
עשויה להיות בגרסה 3.4
; זה חשוב, מכיוון שהגרסה של החבילה משפיעה על מיקומה בעץ המקור.
כל חבילות הליבה ממוקמות תחת hardware/interfaces/
במערכת הבנייה. החבילה android.hardware.
[ name1
].[ name2
]... בגרסה $m.$n
נמצא תחת hardware/interfaces/name1/name2/
… /$m.$n/
; החבילה android.hardware.camera
גרסה 3.4
נמצאת בספריית hardware/interfaces/camera/3.4/.
מיפוי מקודד קשה קיים בין קידומת החבילה android.hardware.
וחומרת hardware/interfaces/
.
חבילות שאינן ליבות (ספק) הן אלו המיוצרות על ידי ספק ה-SoC או ODM. הקידומת לחבילות שאינן ליבות היא vendor.$(VENDOR).hardware.
כאשר $(VENDOR)
מתייחס לספק SoC או OEM/ODM. זה ממפה לספק הנתיב vendor/$(VENDOR)/interfaces
בעץ (מיפוי זה גם מקודד קשה).
שמות מוסמכים לחלוטין של סוג משתמש מוגדר
ב-HIDL, לכל UDT יש שם מלא המורכב משם UDT, שם החבילה שבה מוגדר UDT וגרסת החבילה. השם המלא משמש רק כאשר מוצהרים מופעים מהסוג ולא במקום שבו מוגדר הטיפוס עצמו. לדוגמה, נניח שהחבילה android.hardware.nfc,
גרסה 1.0
מגדירה מבנה בשם NfcData
. באתר ההצהרה (בין אם ב- types.hal
ובין אם בהצהרה של ממשק), ההצהרה פשוט אומרת:
struct NfcData { vec<uint8_t> data; };
כאשר מצהירים על מופע מסוג זה (בין אם בתוך מבנה נתונים או כפרמטר של שיטה), השתמש בשם הסוג המלא:
android.hardware.nfc@1.0::NfcData
התחביר הכללי הוא PACKAGE @ VERSION :: UDT
, כאשר:
-
PACKAGE
הוא השם המופרד בנקודות של חבילת HIDL (למשל,android.hardware.nfc
). -
VERSION
הוא פורמט ה-major.minor-version המופרדים בנקודות של החבילה (למשל,1.0
). -
UDT
הוא השם המופרד מנקודות של HIDL UDT. מכיוון ש-HIDL תומך ב-UDT מקוננת וממשקי HIDL יכולים להכיל UDTs (סוג של הצהרת מקוננת), נעשה שימוש בנקודות כדי לגשת לשמות.
לדוגמה, אם ההצהרה המקוננת הבאה הוגדרה בקובץ הסוגים הנפוצים בחבילה android.hardware.example
גרסה 1.0
:
// types.hal package android.hardware.example@1.0; struct Foo { struct Bar { // … }; Bar cheers; };
השם המלא של Bar
הוא android.hardware.example@1.0::Foo.Bar
. אם, בנוסף להיותה בחבילה שלעיל, ההצהרה המקוננת הייתה בממשק שנקרא IQuux
:
// IQuux.hal package android.hardware.example@1.0; interface IQuux { struct Foo { struct Bar { // … }; Bar cheers; }; doSomething(Foo f) generates (Foo.Bar fb); };
השם המלא של Bar
הוא android.hardware.example@1.0::IQuux.Foo.Bar
.
בשני המקרים, ניתן לכנות את Bar
כ- Bar
רק במסגרת ההצהרה של Foo
. ברמת החבילה או הממשק, עליך להתייחס ל- Bar
via Foo
: Foo.Bar
, כמו בהצהרת השיטה doSomething
לעיל. לחלופין, תוכל להכריז על השיטה בצורה מילולית יותר כ:
// IQuux.hal doSomething(android.hardware.example@1.0::IQuux.Foo f) generates (android.hardware.example@1.0::IQuux.Foo.Bar fb);
ערכי ספירה מלאים
אם UDT הוא סוג enum, אז לכל ערך של סוג ה-enum יש שם מלא שמתחיל בשם המלא של סוג ה-enum, ואחריו נקודתיים ואחריו השם של ה-enum. לדוגמה, נניח שהחבילה android.hardware.nfc,
גרסה 1.0
מגדירה סוג enum NfcStatus
:
enum NfcStatus { STATUS_OK, STATUS_FAILED };
כאשר מתייחסים ל- STATUS_OK
, השם המלא הוא:
android.hardware.nfc@1.0::NfcStatus:STATUS_OK
התחביר הכללי הוא PACKAGE @ VERSION :: UDT : VALUE
, כאשר:
-
PACKAGE @ VERSION :: UDT
הוא בדיוק אותו שם מלא עבור סוג ה-enum. -
VALUE
הוא שם הערך.
כללי הסקה אוטומטית
אין צורך לציין שם UDT מלא. שם UDT יכול להשמיט בבטחה את הדברים הבאים:
- החבילה, למשל
@1.0::IFoo.Type
- גם חבילה וגם גרסה, למשל
IFoo.Type
HIDL מנסה להשלים את השם באמצעות כללי הפרעה אוטומטית (מספר כלל נמוך יותר פירושו עדיפות גבוהה יותר).
חוק מספר 1
אם לא מסופקות חבילה וגרסה, בוצע ניסיון חיפוש שם מקומי. דוגמא:
interface Nfc { typedef string NfcErrorMessage; send(NfcData d) generates (@1.0::NfcStatus s, NfcErrorMessage m); };
NfcErrorMessage
נבדק באופן מקומי, וה- typedef
מעליו נמצא. NfcData
נבדק גם באופן מקומי, אך מכיוון שהוא אינו מוגדר באופן מקומי, נעשה שימוש בכלל 2 ו-3. @1.0::NfcStatus
מספק גרסה, כך שכלל 1 אינו חל.
כלל 2
אם כלל 1 נכשל ורכיב של השם המלא חסר (חבילה, גרסה או חבילה וגרסה), הרכיב יתמלא אוטומטית עם מידע מהחבילה הנוכחית. לאחר מכן, מהדר HIDL מחפש בקובץ הנוכחי (ובכל הייבוא) כדי למצוא את השם הממולא באופן אוטומטי. באמצעות הדוגמה שלמעלה, נניח שההצהרה של ExtendedNfcData
נעשתה באותה חבילה ( android.hardware.nfc
) באותה גרסה ( 1.0
) כמו NfcData
, באופן הבא:
struct ExtendedNfcData { NfcData base; // … additional members };
מהדר HIDL ממלא את שם החבילה ושם הגרסה מהחבילה הנוכחית כדי לייצר את השם UDT המיומן android.hardware.nfc@1.0::NfcData
. מכיוון שהשם קיים בחבילה הנוכחית (בהנחה שהוא מיובא כהלכה), הוא משמש להצהרה.
שם בחבילה הנוכחית מיובא רק אם אחד מהבאים נכון:
- הוא מיובא במפורש עם הצהרת
import
. - הוא מוגדר ב-
types.hal
בחבילה הנוכחית
אותו תהליך מתבצע אם NfcData
הוסמכה רק על ידי מספר הגרסה:
struct ExtendedNfcData { // autofill the current package name (android.hardware.nfc) @1.0::NfcData base; // … additional members };
כלל 3
אם כלל 2 לא מצליח לייצר התאמה (ה-UDT אינו מוגדר בחבילה הנוכחית), מהדר HIDL סורק התאמה בתוך כל החבילות המיובאות. באמצעות הדוגמה שלעיל, נניח שה- ExtendedNfcData
מוצהר בגרסה 1.1
של החבילה android.hardware.nfc
, 1.1
מייבאת 1.0
כמו שצריך (ראה הרחבות ברמת החבילה ), וההגדרה מציינת רק את שם UDT:
struct ExtendedNfcData { NfcData base; // … additional members };
המהדר מחפש כל UDT בשם NfcData
ומוצא אחד ב- android.hardware.nfc
בגרסה 1.0
, וכתוצאה מכך UDT מוסמך מלא של android.hardware.nfc@1.0::NfcData
. אם נמצאה יותר מהתאמה אחת עבור UDT נתון מוסמך חלקית, מהדר HIDL זורק שגיאה.
דוגמא
באמצעות כלל 2, סוג מיובא המוגדר בחבילה הנוכחית מועדף על פני סוג מיובא מחבילה אחרת:
// hardware/interfaces/foo/1.0/types.hal package android.hardware.foo@1.0; struct S {}; // hardware/interfaces/foo/1.0/IFooCallback.hal package android.hardware.foo@1.0; interface IFooCallback {}; // hardware/interfaces/bar/1.0/types.hal package android.hardware.bar@1.0; typedef string S; // hardware/interfaces/bar/1.0/IFooCallback.hal package android.hardware.bar@1.0; interface IFooCallback {}; // hardware/interfaces/bar/1.0/IBar.hal package android.hardware.bar@1.0; import android.hardware.foo@1.0; interface IBar { baz1(S s); // android.hardware.bar@1.0::S baz2(IFooCallback s); // android.hardware.foo@1.0::IFooCallback };
-
S
משולבת כ-android.hardware.bar@1.0::S
, ונמצאת ב-bar/1.0/types.hal
(מכיוון ש-types.hal
מיובא אוטומטית). -
IFooCallback
מתערב כ-android.hardware.bar@1.0::IFooCallback
באמצעות כלל 2, אך לא ניתן למצוא אותו מכיוון ש-bar/1.0/IFooCallback.hal
אינו מיובא אוטומטית (כפי ש-types.hal
הוא). לפיכך, כלל 3 פותר זאת ל-android.hardware.foo@1.0::IFooCallback
במקום, אשר מיובא באמצעותimport android.hardware.foo@1.0;
).
types.hal
כל חבילת HIDL מכילה קובץ types.hal
המכיל UDTs המשותפים בין כל הממשקים המשתתפים בחבילה זו. סוגי HIDL הם תמיד ציבוריים; ללא קשר אם UDT מוצהר ב- types.hal
או בתוך הצהרת ממשק, סוגים אלה נגישים מחוץ לתחום שבו הם מוגדרים. types.hal
לא נועד לתאר את ה-API הציבורי של חבילה, אלא לארח UDTs המשמשים את כל הממשקים בחבילה. בשל אופיו של HIDL, כל UDTs הם חלק מהממשק.
types.hal
מורכב מ-UDTs והצהרות import
. מכיוון types.hal
זמין לכל ממשק של החבילה (זהו ייבוא מרומז), הצהרות import
הללו הן ברמת החבילה בהגדרה. UDTs ב- types.hal
עשויים לכלול גם UDTs וממשקים שיובאו כך.
לדוגמה, עבור IFoo.hal
:
package android.hardware.foo@1.0; // whole package import import android.hardware.bar@1.0; // types only import import android.hardware.baz@1.0::types; // partial imports import android.hardware.qux@1.0::IQux.Quux; // partial imports import android.hardware.quuz@1.0::Quuz;
המיובאים הבאים:
-
android.hidl.base@1.0::IBase
(במרומז) -
android.hardware.foo@1.0::types
(במרומז) - הכל ב-
android.hardware.bar@1.0
(כולל כל הממשקים והסוגיםtypes.hal
שלו) -
types.hal
מ-android.hardware.baz@1.0::types
(הממשקים ב-android.hardware.baz@1.0
אינם מיובאים) -
IQux.hal
ו-types.hal
מ-android.hardware.qux@1.0
-
Quuz
מ-android.hardware.quuz@1.0
(בהנחה ש-Quuz
מוגדר ב-types.hal
, כל קובץtypes.hal
מנותח, אך סוגים אחרים מלבדQuuz
אינם מיובאים).
ניהול גרסאות ברמת הממשק
כל ממשק בתוך חבילה נמצא בקובץ משלו. החבילה אליה שייך הממשק מוצהרת בחלק העליון של הממשק באמצעות הצהרת package
. לאחר הצהרת החבילה, עשוי להיות רשום יבוא אפס או יותר ברמת הממשק (חלקי או חבילה שלמה). לדוגמה:
package android.hardware.nfc@1.0;
ב-HIDL, ממשקים יכולים לרשת ממשקים אחרים באמצעות מילת המפתח extends
. כדי שממשק ירחיב ממשק אחר, חייבת להיות לו גישה אליו באמצעות הצהרת import
. שם הממשק המורחב (הממשק הבסיסי) עוקב אחר הכללים להסמכת שם סוג שהוסבר לעיל. ממשק יכול לרשת רק מממשק אחד; HIDL אינו תומך בירושה מרובה.
דוגמאות גירסת הגרסה הקודמת להלן משתמשות בחבילה הבאה:
// types.hal package android.hardware.example@1.0 struct Foo { struct Bar { vec<uint32_t> val; }; }; // IQuux.hal package android.hardware.example@1.0 interface IQuux { fromFooToBar(Foo f) generates (Foo.Bar b); }
כללים למעלה
כדי להגדיר חבילה package@major.minor
, A או כל B חייב להיות נכון:
כלל א | "האם גרסה משנית": אין להגדיר את כל הגרסאות המשניות הקודמות, package@major.0 , package@major.1 , …, package@major.(minor-1) . |
---|
כלל ב' | כל הדברים הבאים נכונים:
|
---|
בגלל כלל א':
- החבילה יכולה להתחיל עם כל מספר גרסה מינורי (לדוגמה,
android.hardware.biometrics.fingerprint
מתחיל@2.1
.) - הדרישה "
android.hardware.foo@1.0
אינה מוגדרת" פירושה שחומרתhardware/interfaces/foo/1.0
אפילו לא אמורה להתקיים.
עם זאת, כלל A אינו משפיע על חבילה עם אותו שם חבילה אלא גרסה עיקרית שונה (לדוגמה, android.hardware.camera.device
מוגדרים גם @1.0
@3.2
; @3.2
אינו צריך לקיים אינטראקציה עם @1.0
.) לפיכך, @3.2::IExtFoo
יכול להאריך את @1.0::IFoo
.
בתנאי ששם החבילה שונה, package@major.minor::IBar
עשוי להרחיב מממשק עם שם אחר (לדוגמה, android.hardware.bar@1.0::IBar
יכול להרחיב android.hardware.baz@2.2::IBaz
). אם ממשק לא מצהיר במפורש על סוג על עם מילת המפתח extend
, הוא ירחיב את android.hidl.base@1.0::IBase
(למעט IBase
עצמו).
יש לעקוב אחר B.2 ו-B.3 בו-זמנית. לדוגמה, גם אם android.hardware.foo@1.1::IFoo
מרחיב android.hardware.foo@1.0::IFoo
כדי לעבור את כלל B.2, אם android.hardware.foo@1.1::IExtBar
מרחיב את android.hardware.foo@1.0::IBar
, זה עדיין לא קוד קוד תקף.
העלאת ממשקים
כדי להעלות android.hardware.example@1.0
(מוגדר למעלה) @1.1
:
// types.hal package android.hardware.example@1.1; import android.hardware.example@1.0; // IQuux.hal package android.hardware.example@1.1 interface IQuux extends @1.0::IQuux { fromBarToFoo(Foo.Bar b) generates (Foo f); }
זהו import
ברמת החבילה של גרסה 1.0
של android.hardware.example
ב- types.hal
. אמנם לא מתווספים UDTs חדשים בגרסה 1.1
של החבילה, אך עדיין יש צורך בהפניות ל-UDTs בגרסה 1.0
, ומכאן היבוא ברמת החבילה ב- types.hal
. (ניתן היה להשיג את אותו האפקט עם ייבוא ברמת הממשק ב- IQuux.hal
.)
ב- extends @1.0::IQuux
בהצהרה של IQuux
, ציינו את הגרסה של IQuux
שעוברת בירושה (נדרשת ביעור כיוון ש- IQuux
משמש להכרזה על ממשק וכדי לרשת מממשק). מכיוון שהצהרות הן פשוט שמות היורשים את כל תכונות החבילה והגרסה באתר ההצהרה, הבלבול חייב להיות בשם ממשק הבסיס; יכולנו להשתמש גם ב-UDT המותאם במלואו, אבל זה היה מיותר.
הממשק החדש IQuux
אינו מכריז מחדש על שיטה fromFooToBar()
שהוא יורש @1.0::IQuux
; זה פשוט מפרט את השיטה החדשה שהוא מוסיף fromBarToFoo()
. ב-HIDL, לא ניתן להכריז שוב על שיטות שעברו בירושה בממשקי הילד, כך שממשק IQuux
לא יכול להכריז במפורש על שיטת fromFooToBar()
.
מוסכמות קודמות
לפעמים שמות הממשק חייבים לשנות את שם הממשק המתרחב. אנו ממליצים שהרחבות, מבנים ואיגודים של enum יהיו בעלי שם זהה לזה שהם מרחיבים, אלא אם הם שונים מספיק כדי להצדיק שם חדש. דוגמאות:
// in parent hal file enum Brightness : uint32_t { NONE, WHITE }; // in child hal file extending the existing set with additional similar values enum Brightness : @1.0::Brightness { AUTOMATIC }; // extending the existing set with values that require a new, more descriptive name: enum Color : @1.0::Brightness { HW_GREEN, RAINBOW };
אם לשיטה יכולה להיות שם סמנטי חדש (למשל fooWithLocation
) אז זה מועדף. אחרת, יש לקרוא לזה בדומה למה שהוא מרחיב. לדוגמה, השיטה foo_1_1
@1.1::IFoo
עשויה להחליף את הפונקציונליות של שיטת foo
@1.0::IFoo
אם אין שם חלופי טוב יותר.
ניהול גרסאות ברמת החבילה
גירסאות HIDL מתרחשות ברמת החבילה; לאחר פרסום חבילה, היא ניתנת לשינוי (לא ניתן לשנות את מערך הממשקים וה-UDT שלה). חבילות יכולות להתייחס זו לזו בכמה דרכים, שכולן ניתנות לביטוי באמצעות שילוב של ירושה ברמת הממשק ובניית UDTs לפי הרכב.
עם זאת, סוג אחד של קשר מוגדר בקפדנות ויש לאכוף אותו: ירושה תואמת לאחור ברמת החבילה . בתרחיש זה, חבילת האב היא החבילה שממנה עוברים בירושה וחבילת הילד היא זו שמרחיבה את האב. כללי הירושה התואמים לאחור ברמת החבילה הם כדלקמן:
- כל הממשקים ברמה העליונה של חבילת האב עוברים בירושה מממשקים בחבילת הילד.
- ניתן להוסיף ממשקים חדשים לחבילה החדשה (ללא הגבלות לגבי קשרים עם ממשקים אחרים בחבילות אחרות).
- ניתן גם להוסיף סוגי נתונים חדשים לשימוש על ידי שיטות חדשות של ממשקים קיימים משודרגים, או על ידי ממשקים חדשים.
ניתן ליישם כללים אלה באמצעות תורשה ברמת ממשק HIDL והרכב UDT, אך דורשים ידע ברמת מטה כדי לדעת שהיחסים הללו מהווים הרחבת חבילה תואמת לאחור. הידע הזה מתקבל כדלקמן:
אם חבילה עומדת בדרישה זו, hidl-gen
אוכף חוקים של תאימות לאחור.