יצירת ממשק HAL

צריך להשתמש ב-HIDL כדי לתאר את כל דגלי ה-build שמשמשים להדרה מותנית של המסגרת. צריך לקבץ את דגלים הרלוונטיים ל-build ולכלול אותם בקובץ .hal אחד. השימוש ב-HIDL לציון פריטי תצורה כולל את היתרונות הבאים:

  • עם גרסאות (כדי להוסיף פריטים חדשים להגדרה, ספקים או יצרני ציוד מקורי צריכים להרחיב את ה-HAL באופן מפורש)
  • מתועדים היטב
  • בקרת גישה באמצעות SELinux
  • בדיקת תקינות של פריטי ההגדרה באמצעות Vendor Test Suite (בדיקת טווח, בדיקת יחסי תלות בין פריטים וכו')
  • ממשקי API שנוצרו באופן אוטומטי ב-C++‎ וב-Java

זיהוי הדגלים של build שמשמשים את ה-framework

מתחילים בזיהוי הגדרות ה-build שמשמשות להדרת ה-framework באופן מותנה, ואז נוטשים את ההגדרות המיושנות כדי להקטין את הקבוצה. לדוגמה, הקבוצה הבאה של דגלי build מזוהה בשביל surfaceflinger:

  • TARGET_USES_HWC2
  • TARGET_BOARD_PLATFORM
  • TARGET_DISABLE_TRIPLE_BUFFERING
  • TARGET_FORCE_HWC_FOR_VIRTUAL_DISPLAYS
  • NUM_FRAMEBUFFER_SURFACE_BUFFERS
  • TARGET_RUNNING_WITHOUT_SYNC_FRAMEWORK
  • VSYNC_EVENT_PHASE_OFFSET_NS
  • SF_VSYNC_EVENT_PHASE_OFFSET_NS
  • PRESENT_TIME_OFFSET_FROM_VSYNC_NS
  • MAX_VIRTUAL_DISPLAY_DIMENSION

יצירת ממשק HAL

הגישה להגדרות build של מערכת משנה מתבצעת דרך ממשק HAL, וממשקים למתן ערכי תצורה מקובצים בחבילת HAL‏ android.hardware.configstore (גרסה 1.0 נכון לעכשיו). לדוגמה, כדי ליצור קובץ ממשק HAL עבור surfaceflinger, ב-hardware/interfaces/configstore/1.0/ISurfaceFlingerConfigs.hal:

package android.hardware.configstore@1.0;

interface ISurfaceFlingerConfigs {
    // TO-BE-FILLED-BELOW
};

אחרי שיוצרים את הקובץ .hal, מריצים את hardware/interfaces/update-makefiles.sh כדי להוסיף את הקובץ החדש .hal לקובצי Android.bp ו-Android.mk.

הוספת פונקציות לדגלים של build

לכל דגל build, מוסיפים פונקציה חדשה לממשק. לדוגמה, ב-hardware/interfaces/configstore/1.0/ISurfaceFlingerConfigs.hal:

interface ISurfaceFlingerConfigs {
    disableTripleBuffering() generates(OptionalBool ret);
    forceHwcForVirtualDisplays() generates(OptionalBool ret);
    enum NumBuffers: uint8_t {
        USE_DEFAULT = 0,
        TWO = 2,
        THREE = 3,
    };
    numFramebufferSurfaceBuffers() generates(NumBuffers ret);
    runWithoutSyncFramework() generates(OptionalBool ret);
    vsyncEventPhaseOffsetNs generates (OptionalUInt64 ret);
    presentTimeOffsetFromSyncNs generates (OptionalUInt64 ret);
    maxVirtualDisplayDimension() generates(OptionalInt32 ret);
};

כשאתם מוסיפים פונקציה:

  • השם צריך להיות תמציתי. מומלץ להימנע מהמרת שמות המשתנים של קובץ ה-makefile לשמות פונקציות, וצריך לזכור שהתחיליות TARGET_ ו-BOARD_ כבר לא נחוצות.
  • הוספת תגובות עוזרים למפתחים להבין את המטרה של פריט התצורה, איך הוא משנה את התנהגות המסגרת, ערכים חוקיים ומידע רלוונטי אחר.

סוגי ההחזרות של פונקציות יכולים להיות Optional[Bool|String|Int32|UInt32|Int64|UInt64]. הסוגים מוגדרים ב-types.hal באותה ספרייה, והערכים הפרימיטיביים עטופים בשדה שמציין אם הערך צוין על ידי ה-HAL. אם לא, המערכת משתמשת בערך ברירת המחדל.

struct OptionalString {
    bool specified;
    string value;
};

במקרים הרלוונטיים, מגדירים את המאפיין enum שמייצג בצורה הטובה ביותר את סוג פריט התצורה, ומשתמשים במאפיין enum הזה כסוג ההחזרה. בדוגמה שלמעלה, המאפיין NumBuffers מוגדר כדי להגביל את מספר הערכים התקפים. כשמגדירים סוגי נתונים מותאמים אישית כאלה, מוסיפים שדה או ערך enum (למשל, USE_DEFAULT) כדי לציין אם הערך צוין על ידי ה-HAL או לא.

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

לדוגמה, אפשרות לאסוף שני דגלים של build ל-struct יחיד ב-hardware/interfaces/configstore/1.0/ISurfaceFlingerConfigs.hal היא:

 interface ISurfaceFlingerConfigs {
    // other functions here
    struct SyncConfigs {
        OptionalInt64 vsyncEventPhaseoffsetNs;
        OptionalInt64 presentTimeoffsetFromSyncNs;
    };
    getSyncConfigs() generates (SyncConfigs ret);
    // other functions here
};

חלופות לפונקציית HAL יחידה

כחלופה לשימוש בפונקציית HAL אחת לכל דגל build, ממשק HAL מספק גם פונקציות פשוטות כמו getBoolean(string key) ו-getInteger(string key). זוגות ה-key=value בפועל מאוחסנים בקבצים נפרדים, ושירות HAL מספק ערכים על ידי קריאה או ניתוח של הקבצים האלה.

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

ממשק יחיד וממשקים מרובים

בתכנון של ממשק HAL לפריטי תצורה יש שתי אפשרויות:

  • ממשק אחד שמכסה את כל פריטי התצורה
  • ממשקים מרובים, שכל אחד מהם מכסה קבוצה של פריטי הגדרה קשורים

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

בגלל הבעיות האלה, מערכת Android משתמשת במספר ממשקים עם ממשק HAL יחיד לקבוצה של פריטי הגדרה קשורים. לדוגמה, ISurfaceflingerConfigs לפריטי תצורה שקשורים ל-surfaceflinger ו-IBluetoothConfigs לפריטי תצורה שקשורים ל-Bluetooth.