חובה להשתמש ב-HIDL כדי לתאר את כל דגלי הבנייה שמשמשים להידור מותנה של ה-framework. צריך לקבץ את דגלי ה-build הרלוונטיים ולכלול אותם בקובץ .hal
אחד. השימוש ב-HIDL לציון פריטי הגדרה כולל את היתרונות הבאים:
- עם ניהול גרסאות (כדי להוסיף פריטי הגדרה חדשים, ספקים או יצרני ציוד מקורי צריכים להרחיב באופן מפורש את HAL)
- מתועד היטב
- בקרת גישה באמצעות SELinux
- בדיקת תקינות של פריטי ההגדרה באמצעות הכלי לבדיקת ספקים (בדיקת טווח, בדיקת תלות הדדית בין פריטים וכו')
- ממשקי API שנוצרו באופן אוטומטי ב-C++ וב-Java
זיהוי של דגלי 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 קרובים במבנה, ולהגדיר פונקציה שמחזירה את המבנה הזה (פעולה כזו יכולה לצמצם את מספר הקריאות לפונקציה).
לדוגמה, אפשרות לצבירה של שני דגלי בנייה למבנה יחיד ב-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.