ממשקים וחבילות

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

חבילות

לשמות של חבילות יכולות להיות רמות משנה כמו package.subpackage. ספריית השורש של חבילות HIDL שפורסמו היא hardware/interfaces או vendor/vendorName (לדוגמה, vendor/google ל-Pixel מכשירים). שם החבילה יוצר ספריית משנה אחת או יותר ברמה הבסיסית (root) directory; כל הקבצים שמגדירים חבילה נמצאים באותה ספרייה. לדוגמה, אפשר למצוא את package android.hardware.example.extension.light@2.0 מתחת ל-hardware/interfaces/example/extension/light/2.0.

בטבלה הבאה מפורטים הקידומות והמיקומים של חבילות:

קידומת חבילה מיקום סוגי ממשקים
android.hardware.* hardware/interfaces/* HAL
android.frameworks.* frameworks/hardware/interfaces/* frameworks/ related
android.system.* system/hardware/interfaces/* מערכת/ מידע קשור
android.hidl.* system/libhidl/transport/* התוכן הקבוע

ספריית החבילות מכילה קבצים עם הסיומת .hal. הכול הקובץ חייב להכיל הצהרת package שמציינת את החבילה, וגם הגרסה שהקובץ נכלל בה. הקובץ types.hal, אם קיים, לא מגדיר ממשק אלא מגדיר סוגי נתונים הנגישים לכל הממשק בחבילה.

הגדרת הממשק

מלבד types.hal, כל קובץ .hal אחר מגדיר ממשק. ממשק מוגדר בדרך כלל כך:

interface IBar extends IFoo { // IFoo is another interface
    // embedded types
    struct MyStruct {/*...*/};

    // interface methods
    create(int32_t id) generates (MyStruct s);
    close();
};

ממשק ללא הצהרה מרומזת של extends נמשך מ-android.hidl.base@1.0::IBase (בדומה ל- java.lang.Object ב-Java). ממשק IBase, במרומז מצהיר על כמה שיטות שמורות מוצהר מחדש בממשקים שהוגדרו על ידי המשתמש או שנעשה בו שימוש בדרך אחרת. השיטות האלה כוללים:

  • ping
  • interfaceChain
  • interfaceDescriptor
  • notifySyspropsChanged
  • linkToDeath
  • unlinkToDeath
  • setHALInstrumentation
  • getDebugInfo
  • debug
  • getHashChain

תהליך הייבוא

ההצהרה import היא מנגנון HIDL לגישה לחבילה והממשקים בחבילה אחרת. דף חשבון import עוסקת בשתי ישויות:

  • הישותשמיובאת, שיכולה להיות חבילה או ממשק
  • הישות המיובאת, שיכולה להיות חבילה או ממשק

הישות המיובאת נקבעת לפי המיקום של הישות המיובאת דף חשבון import. כשהדוח נמצא בתוך חבילה types.hal, הפרטים שמיובאים גלויים בחבילה כולה. זהו ייבוא ברמת החבילה. כשההצהרה נמצאת בתוך קובץ ממשק, הישות המיובאת היא הממשק עצמו; זו ייבוא ברמת interface-level.

הישות המיובאת נקבעת לפי הערך שמופיע אחרי import במילת מפתח. הערך לא חייב להיות שם שמוגדר במלואו; אם רכיב שהושמט, הוא ממולא אוטומטית במידע מהחבילה הנוכחית. כדי לקבל ערכים מוגדרים במלואם, תרחישי הייבוא הבאים נתמכים:

  • ייבוא של כל חבילה. אם הערך הוא שם חבילה (התחביר שמתואר בהמשך), החבילה כולה מיובאת המיובאת.
  • ייבוא חלקי. אם הערך הוא:
    • ממשק, ה-types.hal של החבילה והממשק הזה יובא לישות המיובאת.
    • UDT מוגדר ב-types.hal, ואז רק ה-UDT מיובא אליו. הישות המיובאת (סוגים אחרים ב-types.hal לא מיובאים).
  • ייבוא סוגים בלבד. אם הערך משתמש בתחביר של ייבוא חלקי שמתואר למעלה, אבל במקום זאת עם מילת המפתח types של שם ממשק, רק את ה-UDT ב-types.hal של החבילות מיובאות.

הישות המיובאת מקבלת גישה לשילוב של:

  • רכיבי ה-UDT הנפוצים של החבילה המיובאת מוגדרים ב-types.hal;
  • הממשקים של החבילה המיובאת (לייבוא של חבילה שלמה) או שצוינו (לייבוא חלקי) כדי להפעיל אותם, להעביר ו/או לקבל בירושה מהם.

הצהרת הייבוא משתמשת בתחביר שם-סוג המוגדר במלואו כדי לספק את השם והגרסה של החבילה או הממשק שמייבאים:

import android.hardware.nfc@1.0;            // import a whole package
import android.hardware.example@1.0::IQuux; // import an interface and types.hal
import android.hardware.example@1.0::types; // import just types.hal

ירושה בממשק

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

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

ממשק יכול להרחיב רק ממשק אחד אחר (ללא מספר ירושה). כל ממשק בחבילה עם מספר גרסה משנית שאינו אפס חייב להאריך בגרסה הקודמת של החבילה. לדוגמה, אם ממשק IBar בגרסה 4.0 של חבילה derivative מבוססת על (מרחיב) ממשק IFoo בגרסה 1.2 של החבילה original, וגרסה 1.3 של חבילה original היא נוצרה, גרסה 4.1 של IBar לא יכולה להרחיב את גרסה 1.3 של IFoo. במקום זאת, צריך להאריך את גרסה 4.1 של IBar IBar גרסה 4.0, שמקושרת לגרסה 1.2 של IFoo. ייתכן שגרסה 5.0 של IBar תרחיב את גרסה 1.3 של IFoo, אם הרצויה.

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

תוספי ספקים

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

ניהול גרסאות

לחבילות יש גרסאות, ובממשקים יש את גרסת החבילה. הגרסאות מבוטאות בשני מספרים שלמים, גדול.משני.

  • גרסאות ראשיות לא תואמות לאחור. מצטבר מספר הגרסה הראשית מאפס את מספר הגרסה המשנית ל-0.
  • גרסאות קטנות תואמות לאחור. הוספת מספר משני מציין שהגרסה החדשה יותר תואמת באופן מלא בגרסה הקודמת. אפשר להוסיף שיטות ומבנים חדשים של נתונים, אבל לא קיימות עדיין שיטות עשויים להיות שינויים במבני נתונים או בחתימות של שיטות.

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

סיכום פריסת הממשק

קטע זה מסכם את האופן שבו ניתן לנהל חבילת ממשק HIDL (כגון hardware/interfaces) ומאחד את המידע המוצג לכל אורך הקטע HIDL. לפני הקריאה, צריך לוודא שאתם יודעים ניהול גרסאות HIDL, גיבוב עם במושגים של hidl-gen, את הפרטים של העבודה עם HIDL באופן כללי, וההגדרות הבאות:

מונח הגדרה
application Binary Interface (ABI) ממשק תכנות יישומים וכן כל קישור בינארי כנדרש.
שם מוגדר במלואו (fqName) שם כדי להבדיל בין סוג ה-hidl. דוגמה: android.hardware.foo@1.0::IFoo
חבילה חבילה שמכילה ממשק וסוגים של HIDL. דוגמה: android.hardware.foo@1.0
הרמה הבסיסית של החבילה חבילת בסיס שמכילה את ממשקי HIDL. דוגמה: ממשק HIDL android.hardware נמצא ברמה הבסיסית (root) של החבילה android.hardware.foo@1.0.
נתיב הרמה הבסיסית של החבילה המיקום בעץ המקור של Android שאליו ממופה שורש החבילה.

להגדרות נוספות, אפשר לעיין ב-HIDL הסברים על המונחים.

ניתן למצוא כל קובץ מהמיפוי של שורש החבילה השם המוגדר במלואו

שורשי החבילה מוגדרים כארגומנט hidl-gen -r android.hardware:hardware/interfaces. לדוגמה, אם החבילה היא vendor.awesome.foo@1.0::IFoo ו-hidl-gen נשלח -r vendor.awesome:some/device/independent/path/interfaces, קובץ הממשק אמור להיות ממוקם $ANDROID_BUILD_TOP/some/device/independent/path/interfaces/foo/1.0/IFoo.hal

בפועל, מומלץ להשתמש במאפיין הזה עבור ספק או OEM (יצרן ציוד מקורי) בשם awesome לשים את הממשקים הסטנדרטיים שלהם ב-vendor.awesome. אחרי חבילה נבחר נתיב, אי אפשר לשנות אותו כי הוא נחשף ב-ABI של את הממשק.

המיפוי של נתיב החבילה צריך להיות ייחודי

לדוגמה, אם יש לך -rsome.package:$PATH_A ויש לך -rsome.package:$PATH_B, $PATH_A חייב להיות שווה ל- $PATH_B לספריית ממשק עקבית (הפעולה הזו גם לממשקים של גרסאות קל יותר).

ברמה הבסיסית של החבילה חייב להיות קובץ לניהול גרסאות

אם יוצרים נתיב חבילה כמו -r vendor.awesome:vendor/awesome/interfaces, כדאי לך גם ליצור את הקובץ $ANDROID_BUILD_TOP/vendor/awesome/interfaces/current.txt, צריך להכיל גיבובים של ממשקים שנוצרו באמצעות האפשרות -Lhash ב- hidl-gen (נדון בהרחבה בנושא גיבוב באמצעות hidl-gen).

הממשקים לא תלויים במכשיר מיקומים גיאוגרפיים

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