סוגי נתונים

בקטע הזה מתוארים סוגי הנתונים HIDL. פרטים על ההטמעה זמינים במאמר HIDL C++ (ל-C++ הטמעות) או HIDL Java (להטמעות Java).

קווי דמיון ל-C++ כוללים:

  • structs צריך להשתמש בתחביר C++ ; הפלטפורמה unions תומכת בתחביר C++ כברירת מחדל. יש לתת שמות לשניהם; מבנים ואיחודים אנונימיים אינם נתמכים.
  • מותר להשתמש ב-Typedefs ב-HIDL (כפי שהן ב-C++ ).
  • תגובות בסגנון C++ מותרות ומועתקות לקובץ הכותרת שנוצר.

דוגמאות ל-Java:

  • לכל קובץ, HIDL מגדיר מרחב שמות בסגנון Java שחייב להתחיל ב- android.hardware.. מרחב השמות של C++ שנוצר הוא ::android::hardware::….
  • כל הגדרות הקובץ נכללות בסגנון Java wrapper של interface.
  • הצהרות מערך HIDL מותאמות לסגנון Java ולא בסגנון C++. דוגמה:
    struct Point {
        int32_t x;
        int32_t y;
    };
    Point[3] triangle;   // sized array
    
  • ההערות דומות לפורמט Javadoc.

ייצוג נתונים

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

סוגים ראשוניים של HIDL, וגם enum ו-bitfield (שתמיד נגזרים מסוגים פרימיטיביים), ממפים לסוגים סטנדרטיים של C++ כמו std::uint32_t cstdint.

מכיוון ש-Java לא תומכת בסוגים לא חתומים, סוגי HIDL לא חתומים ממופים סוג ה-Java החתום התואם. מבנים למפות למחלקות Java; מערכים ממופים למערכי Java; בשלב הזה אין תמיכה ב-unions ב-Java. מחרוזות נשמרות באופן פנימי כ-UTF8. מכיוון ש-Java תומכת רק מחרוזות UTF16, ערכי מחרוזות שנשלחו להטמעה של Java או ממנה וייתכן שלא יהיו זהים בתרגום מחדש, כי מערכות התווים למפות תמיד בצורה חלקה.

נתונים שהתקבלו דרך IPC ב-C++ מסומנים כ-const ונמצאים זיכרון לקריאה בלבד שנשאר רק במהלך הבקשה להפעלת הפונקציה. נתונים שהתקבלו דרך IPC ב-Java כבר הועתקו לאובייקטי Java, כך שניתן יישמרו ללא העתקה נוספת (וניתן לשנותה).

הערות

ניתן להוסיף הערות בסגנון Java להצהרות. ההערות הן בוצע ניתוח על ידי הקצה העורפי של ספק הבדיקה (VTS) של המהדר (compiler) ל-HIDL, אבל אף אחד אנוטציות מנותחות שכאלו למעשה מובנות על ידי המהדר של HIDL. במקום זאת, אנוטציות VTS שנותחו מטופלות על ידי VTS Compiler (VTSC).

ההערות משתמשות בתחביר Java: @annotation או @annotation(value) או @annotation(id=value, id=value…) כאשר הערך יכול להיות ביטוי קבוע, מחרוזת או רשימת ערכים בתוך {}, בדיוק כמו ב-Java. מספר הערות עם אותו שם יכולים להיות מצורפים לאותו פריט.

העברת הצהרות

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

ההגבלה הזאת מאפשרת להעתיק מבני נתונים לפי ערך באמצעות מודל פשוט בהעתקה עמוקה, במקום לעקוב אחרי ערכי המצביע שעשויים להתרחש במבנה נתונים עם התייחסות עצמית. אם אותם נתונים מועברים פעמיים, למשל באמצעות שני פרמטרים של method או vec<T> שמצביעים אל אותם נתונים, שני עותקים נפרדים נוצרים ונשלחים.

הצהרות מקוננות

HIDL תומך בהצהרות מקננות בכמה רמות שרוצים (באמצעות הצהרה אחת חריג שמצוין בהמשך). לדוגמה:

interface IFoo {
    uint32_t[3][4][5][6] multidimArray;

    vec<vec<vec<int8_t>>> multidimVector;

    vec<bool[4]> arrayVec;

    struct foo {
        struct bar {
            uint32_t val;
        };
        bar b;
    }
    struct baz {
        foo f;
        foo.bar fb; // HIDL uses dots to access nested type names
    }
    …

היוצא מן הכלל הוא שניתן להטמיע סוגי ממשק רק vec<T> ורק רמה אחת בקישורי עומק (לא vec<vec<IFoo>>).

תחביר של מצביע גולמי

שפת HIDL לא משתמשת ב-* ולא תומכת את הגמישות המלאה של מצביעי C/C++ גולמיים. לפרטים על האופן שבו היטל HIDL מצביעים ומערכים/וקטורים, ראו vec<T> תבנית.

ממשקים

למילת המפתח interface יש שני שימושים.

  • תיפתח ההגדרה של ממשק בקובץ .hal
  • אפשר להשתמש בו כסוג מיוחד בשדות struct/union, פרמטרים של method, והחזרות. הוא נתפס כממשק כללי ומילה נרדפת android.hidl.base@1.0::IBase.

לדוגמה, הפונקציה IServiceManager פועלת באופן הבא:

get(string fqName, string name) generates (interface service);

בשיטה הזו מבטיחים לחפש ממשק מסוים לפי שם. כמו כן זהה להחלפת הממשק ב-android.hidl.base@1.0::IBase.

ניתן להעביר ממשקים רק בשתי דרכים: כפרמטרים ברמה עליונה, או חברים של vec<IMyInterface>. הם לא יכולים להיות חברים ב- vecs, מבניים, מערכים או איחודים מקוננים.

MQDescriptorSync ו-MQDescriptorUnsync

הסוגים MQDescriptorSync וMQDescriptorUnsync העברה של מתארים מסונכרנים או לא מסונכרנים בתור 'תור הודעות מהירות' (FMQ) דרך ממשק HIDL. פרטים נוספים זמינים במאמר HIDL C++ (אין מתגי FMQ שנתמכות ב-Java).

סוג הזיכרון

הסוג memory משמש לייצוג זיכרון משותף לא ממופה HIDL. המדיניות הזו נתמכת רק ב-C++. ניתן להשתמש בערך מהסוג הזה ב המקבל לאתחול אובייקט IMemory, מיפוי הזיכרון והופך אותו לשימושי. פרטים נוספים זמינים במאמר HIDL C++.

אזהרה: נתונים מובְנים ששמורים בתיקיות 'משותפים' זיכרון חייב להיות סוג שהפורמט שלו אף פעם לא משתנה כל עוד לגרסת הממשק שעברה את memory. אחרת, מוצרי HAL עלולים לסבול בעיות תאימות חמורות.

סוג מצביע העכבר

הסוג pointer מיועד לשימוש פנימי בלבד מסוג HIDL.

Bitfield<T> סוג תבנית

bitfield<T> שבו T הוא user-defined enum מרמז על כך שהערך הוא ערך OR-או ב-bit ערכי enum שמוגדרים ב-T. בקוד שנוצר, bitfield<T> מופיע בתור הסוג הבסיסי של T. עבור דוגמה:

enum Flag : uint8_t {
    HAS_FOO = 1 << 0,
    HAS_BAR = 1 << 1,
    HAS_BAZ = 1 << 2
};
typedef bitfield<Flag> Flags;
setFlags(Flags flags) generates (bool success);

המהדר מטפל בסוג הדגלים בדיוק כמו ב-uint8_t.

למה לא להשתמש (u)int8_t/(u)int16_t/(u)int32_t/(u)int64_t? השימוש ב-bitfield מספק לקורא מידע נוסף בנוגע ל-HAL, שיודע עכשיו ש-setFlags מקבל ערך OR של דגל (כלומר יודע שקריאה ל-setFlags עם 16 אינה חוקית). ללא bitfield, המידע הזה מועבר רק באמצעות מסמכים. לחשבון הפונקציה VTS יכולה למעשה לבדוק אם ערך הדגלים הוא ערך OR של דגל.

כינויים מסוג ראשוני

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

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

custom_handle_t

ב-Android יש תמיכה ב-native_handle_t, רעיון כללי של כינוי הוגדרה ב-libcutils.

typedef struct native_handle
{
  int version;        /* sizeof(native_handle_t) */
  int numFds;         /* number of file-descriptors at &data[0] */
  int numInts;        /* number of ints at &data[numFds] */
  int data[0];        /* numFds + numInts ints */
} native_handle_t;

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

ל-native_handle_t יש גודל משתנה, לכן אי אפשר לכלול אותו ישירות בתוך מבנה. שדה כינוי יוצר מצביע אל הוקצה native_handle_t.

בגרסאות הקודמות של Android, הכינויים המקוריים נוצרו באמצעות אותם הפונקציות הקיימות libcutils. ב-Android מגרסה 8.0 ואילך, הפונקציות האלה מועתקות אל מרחב השמות android::hardware::hidl או הועבר ל-NDK. hiDL שנוצר אוטומטית, מחזיר את הפונקציות מהפונקציות האלה ומבצע אותן סריאליזציה שלהן באופן אוטומטי, ללא מעורבות של קוד שנכתב על ידי המשתמש.

הבעלות על הכינוי ועל מתאר הקובץ

כשמבצעים קריאה ל-method של ממשק HIDL שמעבירה (או מחזירה) אובייקט hidl_handle (ברמה העליונה או חלק מסוג מורכב), הבעלות על תיאורי הקבצים שכלולה בו היא:

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

HIDL לא תומך בכינויים ב-Java (כיוון ש-Java לא תומכת בכינויים ב- הכול).

מערכים בגודל

למערכים בגודל מבני HIDL, הרכיבים שלהם יכולים להיות מכל סוג שהוא מבנה יכול להכיל:

struct foo {
uint32_t[3] x; // array is contained in foo
};

מיתרים

מחרוזות נראות שונה ב-C++ וב-Java, אבל התעבורה הבסיסית סוג האחסון הוא מבנה של C++. פרטים נוספים זמינים במאמר HIDL C++ סוגי נתונים או סוגי נתונים של HIDL Java.

הערה: העברת מחרוזת אל Java או ממנה דרך ממשק HIDL (כולל Java ל-Java) גורם להמרות מסוג מערכת תווים שעשויים לא לשמור על הקידוד המקורי.

ו<T> סוג תבנית

התבנית vec<T> מייצגת מאגר נתונים זמני בגודל משתנה שמכיל מופעים של T.

T יכול להיות אחת מהאפשרויות הבאות:

  • סוגים ראשוניים (למשל uint32_t)
  • מיתרים
  • טיפוסים בני מנייה שהוגדרו על ידי המשתמש
  • מבנים שהוגדרו על ידי המשתמש
  • ממשקים, או מילת המפתח interface (יש תמיכה ב-vec<IFoo>, ב-vec<interface> רק כפרמטר ברמה עליונה)
  • כינויים
  • Bitfield<U>
  • vec<U>, כאשר U נמצא ברשימה הזו חוץ מהממשק (למשל אין תמיכה ב-vec<vec<IFoo>>)
  • U[] (מערך בגודל U), כאשר U נמצא ברשימה הזו חוץ מהממשק

סוגים בהגדרת המשתמש

בקטע הזה מתוארים סוגים שהוגדרו על ידי המשתמש.

טיפוסים בני מנייה (enum)

HIDL לא תומך בטיפוסים אנונימיים של טיפוסים בני מנייה (enum). אחרת, טיפוסים בני מנייה (enum) ב-HIDL דומים אל C++11:

enum name : type { enumerator , enumerator = constexpr , …  }

טיפוס enum בסיסי מוגדר במונחים של אחד מסוגי המספרים השלמים ב-HIDL. אם לא מצוין עבור המונה הראשון של טיפוסים בני מנייה (enum) המבוסס על מספר שלם 0. אם לא צוין ערך למונה מאוחר יותר, ערך ברירת המחדל של הערך הקודם הוא 1+. לדוגמה:

// RED == 0
// BLUE == 4 (GREEN + 1)
enum Color : uint32_t { RED, GREEN = 3, BLUE }

טיפוסים בני מנייה (enum) יכולים גם לרשת מטיפוס טיפוסים (enum) שהוגדר קודם לכן. אם לא קיים ערך מצוין עבור המונה הראשון של 'טיפוסים בני מנייה (enum)' (במקרה הזה FullSpectrumColor), ברירת המחדל היא הערך מונה של מספר ההורה +1. לדוגמה:

// ULTRAVIOLET == 5 (Color:BLUE + 1)
enum FullSpectrumColor : Color { ULTRAVIOLET }

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

לערכי enum יש תחביר נקודתיים (ולא תחביר נקודה כ- סוגים מקוננים). התחביר הוא Type:VALUE_NAME. אין צורך לציין אם יש הפניה לערך מאותו סוג enum או מאותו סוג צאצא. דוגמה:

enum Grayscale : uint32_t { BLACK = 0, WHITE = BLACK + 1 };
enum Color : Grayscale { RED = WHITE + 1 };
enum Unrelated : uint32_t { FOO = Color:RED + 1 };

החל מ-Android 10, ל-enum יש אפשר להשתמש במאפיין len בביטויים קבועים. MyEnum::len הוא המספר הכולל של הרשומות בספירה הזו. הוא שונה ממספר הערכים הכולל, שעשוי להיות קטן יותר כאשר יש כפילות.

מבנה

HIDL לא תומך במבנים אנונימיים. אחרת, מבני HIDL דומה ל-C.

HIDL לא תומך במבני נתונים באורך משתנה שנכללים באופן מלא בנייה. זה כולל את המערך באורך בלתי מוגבל שמשמש לפעמים השדה האחרון של מבנה ב-C/C++ (לפעמים נראה בגודל של [0]). HIDL vec<T> מייצג גודל דינמי מערכים שהנתונים שלהם מאוחסנים במאגר נתונים זמני נפרד. של מופעים כאלה מיוצגים עם מופע של vec<T> ב-struct.

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

Union

HIDL לא תומך באיחודים אנונימיים. אחרת, איחודים דומים ל-C.

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

union UnionType {
uint32_t a;
//  vec<uint32_t> r;  // Error: can't contain a vec<T>
uint8_t b;1
};
fun8(UnionType info); // Legal

אפשר להצהיר על איחודים גם בתוך מבני נתונים. לדוגמה:

struct MyStruct {
    union MyUnion {
      uint32_t a;
      uint8_t b;
    }; // declares type but not member

    union MyUnion2 {
      uint32_t a;
      uint8_t b;
    } data; // declares type but not member
  }