יצירת ממשק HAL

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

  • גרסאות (כדי להוסיף פריטי הגדרה חדשים, ספקים/יצרני ציוד מקורי חייבים להרחיב באופן מפורש את HAL)
  • מתועד היטב
  • בקרת גישה באמצעות SELinux
  • בדיקת שאיפה לפריטי תצורה באמצעות חבילת הבדיקה של הספק (בדיקת טווח, בדיקת תלות הדדית בין פריטים וכו')
  • ממשקי 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);
};

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

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

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

struct OptionalString {
    bool specified;
    string value;
};

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

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

לדוגמה, אפשרות לצבירת שני דגלי build למבנה יחיד ב-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.