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