ניהול גרסאות

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 .
    • ניתן להוסיף לחבילה החדשה גם ממשקים חדשים.
    • כל סוגי הנתונים של חבילת האב נמצאים בחבילה החדשה וניתן לטפל בהם בשיטות (אפשר להטמיע מחדש) מהחבילה הישנה.
    • ניתן גם להוסיף סוגי נתונים חדשים לשימוש על ידי שיטות חדשות של ממשקים קיימים משודרגים, או על ידי ממשקים חדשים.
  • יכולת הרחבה תואמת לאחור ברמת הממשק . החבילה החדשה יכולה גם להרחיב את החבילה המקורית על ידי מורכבת ממשקים נפרדים מבחינה לוגית שפשוט מספקים פונקציונליות נוספת, ולא את הליבה. לצורך כך, עשויים להיות רצויים:
    • ממשקים בחבילה החדשה מצריכים פנייה לסוגי הנתונים של החבילה הישנה.
    • ממשקים בחבילה חדשה עשויים להרחיב ממשקים של חבילה ישנה אחת או יותר.
  • הרחב את חוסר התאימות המקורי לאחור . זהו גרסה עיקרית של החבילה ואין צורך להיות מתאם בין השניים. במידה שיש, זה יכול להתבטא בשילוב של סוגים מהגרסה הישנה יותר של החבילה, והורשה של תת-קבוצה של ממשקי החבילה הישנה.

בניית ממשקים

עבור ממשק מובנה היטב, הוספת סוגים חדשים של פונקציונליות שאינם חלק מהעיצוב המקורי צריכה לדרוש שינוי בממשק 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) .
אוֹ
כלל ב'

כל הדברים הבאים נכונים:

  1. "גרסה מינורית קודמת תקפה": יש להגדיר package@major.(minor-1) ולפעול לפי אותו כלל A (אף אחד מ- package@major.0 עד package@major.(minor-2) אינו מוגדר) או כלל B (אם זה uprev מ- @major.(minor-2) );

    ו

  2. "ירשו לפחות ממשק אחד באותו שם": קיים ממשק package@major.minor::IFoo שמרחיב package@major.(minor-1)::IFoo (אם לחבילה הקודמת יש ממשק);

    ו

  3. "אין ממשק בירושה עם שם אחר": אסור להתקיים package@major.minor::IBar שמרחיב את package@major.(minor-1)::IBaz , כאשר IBar ו- IBaz הם שני שמות שונים. אם יש ממשק עם אותו שם, package@major.minor::IBar חייב להרחיב package@major.(minor-k)::IBar כך שלא קיים IBar עם k קטן יותר.

בגלל כלל א':

  • החבילה יכולה להתחיל עם כל מספר גרסה מינורי (לדוגמה, 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 לפי הרכב.

עם זאת, סוג אחד של קשר מוגדר בקפדנות ויש לאכוף אותו: ירושה תואמת לאחור ברמת החבילה . בתרחיש זה, חבילת האב היא החבילה שממנה עוברים בירושה וחבילת הילד היא זו שמרחיבה את האב. כללי הירושה התואמים לאחור ברמת החבילה הם כדלקמן:

  1. כל הממשקים ברמה העליונה של חבילת האב עוברים בירושה מממשקים בחבילת הילד.
  2. ניתן להוסיף ממשקים חדשים לחבילה החדשה (ללא הגבלות לגבי קשרים עם ממשקים אחרים בחבילות אחרות).
  3. ניתן גם להוסיף סוגי נתונים חדשים לשימוש על ידי שיטות חדשות של ממשקים קיימים משודרגים, או על ידי ממשקים חדשים.

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

אם חבילה עומדת בדרישה זו, hidl-gen אוכף חוקים של תאימות לאחור.