數據類型

本部分介紹了 HIDL 數據類型。有關實現詳情,請參閱 HIDL C++(如果是 C++ 實現)或 HIDL Java(如果是 Java 實現)。

與 C++ 的相似之處包括:

  • structs 使用 C++ 語法;unions 默認支持 C++ 語法。結構體和聯合都必須具有名稱;不支持匿名結構體和聯合。
  • HIDL 中允許使用 typedef(和在 C++ 中一樣)。
  • 允許使用 C++ 樣式的備註,並且此類備註會被複制到生成的標頭文件中。
    與 Java 的相似之處包括:

  • 對於每個文件,HIDL 都會定義一個 Java 樣式的命名空間,並且這些命名空間必須以 android.hardware. 開頭。生成的 C++ 命名空間爲 ::android::hardware::…。

  • 文件的所有定義都包含在一個 Java 樣式的 interface 封裝容器中。
  • HIDL 數組聲明遵循 Java 樣式,而非 C++ 樣式。例如:
struct Point {
    int32_t x;
    int32_t y;
};
Point[3] triangle;   // sized array
  • 備註類似於 javadoc 格式。

數據表示法

採用標準佈局(plain-old-data 類型相關要求的子集)的 struct 或 union 在生成的 C++ 代碼中具有一致的內存佈局,這是依靠 struct 和 union 成員上的顯式對齊屬性實現的。

基本的 HIDL 類型以及 enum 和 bitfield 類型(始終從基本類型派生而來)會映射到標準 C++ 類型,例如 cstdint 中的 std::uint32_t。

由於 Java 不支持無符號的類型,因此無符號的 HIDL 類型會映射到相應的有符號 Java 類型。結構體會映射到 Java 類;數組會映射到 Java 數組;Java 目前不支持聯合。字符串在內部以 UTF8 格式存儲。由於 Java 僅支持 UTF16 字符串,因此發送到或來自 Java 實現的字符串值會進行轉換;在重新轉換回來後,字符串值可能不會與原來的值完全相同,這是因爲字符集並非總能順暢映射。

在 C++ 中通過 IPC 接收的數據會被標記爲 const,並存儲在僅在函數調用期間存在的只讀內存中。在 Java 中通過 IPC 接收的數據已被複制到 Java 對象中,因此無需額外的複製操作即可保留下來(可以對其進行修改)。

註釋

可以將 Java 樣式的註釋添加到類型聲明中。註釋由 HIDL 編譯器的供應商測試套件 (VTS) 後端解析,但 HIDL 編譯器實際上並不理解任何此類經過解析的註釋。經過解析的 VTS 註釋將由 VTS 編譯器 (VTSC) 處理。

註釋使用 Java 語法:@annotation、@annotation(value) 或 @annotation(id=value, id=value…),其中值可以是常量表達式、字符串或在 {} 中列出的一系列值,正如在 Java 中一樣。可以將多個名稱相同的註釋附加到同一項內容。

前向聲明

在 HIDL 中,結構體不能採用前向聲明,因此無法實現用戶定義的自指數據類型(例如,您不能在 HIDL 中描述關聯的列表,也不能描述樹)。大多數現有(Android 8.x 之前的)HAL 都對使用前向聲明有限制,這種限制可以通過重新排列數據結構聲明來移除。

由於存在這種限制,因此可以通過簡單的深層複製按值複製數據結構,而無需跟蹤可以在一個自指數據結構中出現多次的指針值。如果將同一項數據傳遞兩次(例如,使用兩個方法參數或使用兩個指向該數據的 vec),則會生成並傳送兩個單獨的副本。

嵌套式聲明

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 中,並且只能嵌套一層(不能出現 vec

原始指針語法

HIDL 語言不使用 *,並且不支持 C/C++ 原始指針的全面靈活性。要詳細瞭解 HIDL 如何封裝指針和數組/向量,請參閱 vec 模板。

接口

interface 關鍵字有以下兩種用途。

  • 打開 .hal 文件中接口的定義。
  • 可用作結構體/聯合字段、方法參數和返回項中的特殊類型。該關鍵字被視爲一般接口,與 [email protected]::IBase 同義。
    例如,IServiceManager 具有以下方法:
get(string fqName, string name) generates (interface service);

該方法可按名稱查找某個接口。此外,該方法與使用 [email protected]::IBase 替換接口完全一樣。

接口只能以兩種方式傳遞:作爲頂級參數,或作爲 vec 的成員。它們不能是嵌套式向量、結構體、數組或聯合的成員。

MQDescriptorSync 和 MQDescriptorUnsync

MQDescriptorSync 和 MQDescriptorUnsync 類型用於在 HIDL 接口內傳遞已同步或未同步的快速消息隊列 (FMQ) 描述符。要了解詳情,請參閱 HIDL C++(Java 中不支持 FMQ)。

memory 類型

memory 類型用於表示 HIDL 中未映射的共享內存。只有 C++ 支持該類型。可以在接收端使用這種類型的值來初始化 IMemory 對象,從而映射內存並使其可用。要了解詳情,請參閱 HIDL C++。

警告:位於共享內存中的結構化數據所屬的類型必須符合以下條件:其格式在傳遞 memory 的接口版本的生命週期內絕不會改變。否則,HAL 可能會發生嚴重的兼容性問題。

pointer 類型

pointer 類型僅供 HIDL 內部使用。

bitfield 類型模板

bitfield(其中的 T 是用戶定義的枚舉)表明值是在 T 中定義的枚舉值的按位“或”值。在生成的代碼中,bitfield 會顯示爲 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 的相同方式處理 Flag 類型。

爲什麼不使用 (u)int8_t/(u)int16_t/(u)int32_t/(u)int64_t?使用 bitfield 可向讀取器提供額外的 HAL 信息,讀取器現在知道 setFlags 採用 Flag 的按位“或”值(即知道使用 int16_t 調用 setFlags 是無效的)。如果沒有 bitfield,則該信息僅通過文檔傳達。此外,VTS 實際上可以檢查標記的值是否爲 Flag 的按位“或”值。

句柄基本類型

警告:任何類型的地址(即使是物理設備地址)都不能是原生句柄的一部分。在進程之間傳遞該信息很危險,會導致進程容易受到攻擊。在進程之間傳遞的任何值都必須先經過驗證,然後才能用於在進程內查找分配的內存。否則,錯誤的句柄可能會導致內存訪問錯誤或內存損壞。

HIDL 語義是按值複製,這意味着參數會被複制。所有大型數據或需要在進程之間共享的數據(例如同步柵欄)都是通過傳遞指向以下持久對象的文件描述符進行處理:針對共享內存的 ashmem、實際文件或可隱藏在文件描述符後的任何其他內容。Binder 驅動程序會將文件描述符複製到其他進程。

native_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;

原生句柄是整數和文件描述符的集合(按值傳遞)。單個文件描述符可存儲在沒有整數、包含單個文件描述符的原生句柄中。使用封裝有 handle 基本類型的原生句柄傳遞句柄可確保相應的原生句柄直接包含在 HIDL 中。

native_handle_t 的大小可變,因此無法直接包含在結構體中。句柄字段會生成指向單獨分配的 native_handle_t 的指針。

在早期版本的 Android 中,原生句柄是使用 libcutils 中的相同函數創建的。在 Android 8.0 中,這些函數現在被複制到了 android::hardware::hidl 命名空間或移到了 NDK 中。HIDL 自動生成的代碼會自動對這些函數進行序列化和反序列化,而無需用戶編寫的代碼參與。

句柄和文件描述符所有權

當您調用傳遞(或返回)hidl_handle 對象(複合類型的頂級或一部分)的 HIDL 接口方法時,其中包含的文件描述符的所有權如下所述:

  • 將 hidl_handle 對象作爲參數傳遞的調用程序會保留對其封裝的 native_handle_t 中包含的文件描述符的所有權;該調用程序必須在對這些文件描述符的相關操作完成後將其關閉。
  • 返回 hidl_handle 對象(通過將其傳遞到 _cb 函數)的進程會保留對相應對象封裝的 native_handle_t 中包含的文件描述符的所有權;該進程必須在對這些文件描述符的相關操作完成後將其關閉。
  • 接收 hidl_handle 的 transport 是相應對象封裝的 native_handle_t 中的文件描述符的所有者;接收器可在事務回調期間按原樣使用這些文件描述符,但如果想要在回調完成後繼續使用這些文件描述符,則必須克隆相應的原生句柄。事務完成時,transport 將自動對文件描述符執行 close() 操作。
    HIDL 不支持在 Java 中使用句柄(因爲 Java 根本不支持句柄)。

有大小的數組

對於 HIDL 結構體中有大小的數組,其元素可以是結構體可包含的任何類型:

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

字符串

字符串在 C++ 和 Java 中的顯示方式不同,但基礎傳輸存儲類型是 C++ 結構。要了解詳情,請參閱 HIDL C++ 數據類型或 HIDL Java 數據類型。

注意:通過 HIDL 接口將字符串傳遞到 Java 或從 Java 傳遞字符串(包括從 Java 傳遞到 Java)將會導致字符集轉換,而此項轉換可能無法精確保留原始編碼。
vec 類型模板
vec 模板用於表示包含 T 實例且大小可變的緩衝區。T 可以是任何由 HIDL 提供的或由用戶定義的類型,句柄除外。(vec 的 vec<> 將指向 vec 結構體數組,而不是指向內部 T 緩衝區數組。)

T 可以是以下項之一:

  • 基本類型(例如 uint32_t)
  • 字符串
  • 用戶定義的枚舉
  • 用戶定義的結構體
  • 接口,或 interface 關鍵字(vec,- vec 僅在作爲頂級參數時受支持)
  • 句柄
  • bitfield
  • vec,其中 U 可以是此列表中的任何一項,接口除外(例如,vec

用戶定義的類型

本部分介紹了用戶定義的類型。

枚舉

HIDL 不支持匿名枚舉。另一方面,HIDL 中的枚舉與 C++11 類似:

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

枚舉是以 HIDL 中的一種基本類型定義的,或被定義爲其他枚舉的擴展。例如:

enum Color : uint32_t { RED = 0, GREEN, BLUE = 2 } // GREEN == 1

枚舉的值通過冒號語法(而不是像嵌套式類型一樣使用點語法)引用。語法是 Type:VALUE_NAME。如果在相同的枚舉類型或子類型中引用枚舉的值,則無需指定類型。例如:

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

結構體

HIDL 不支持匿名結構體。另一方面,HIDL 中的結構體與 C 非常類似。

HIDL 不支持完全包含在結構體內且長度可變的數據結構。這包括 C/C++ 中有時用作結構體最後一個字段且長度不定的數組(有時會看到其大小爲 [0])。HIDL vec 表示數據存儲在單獨的緩衝區中且大小動態變化的數組;此類實例由 struct 中的 vec 的實例表示。

同樣,string 可包含在 struct 中(關聯的緩衝區是相互獨立的)。在生成的 C++ 代碼中,HIDL 句柄類型的實例通過指向實際原生句柄的指針來表示,因爲基礎數據類型的實例的長度可變。

聯合

HIDL 不支持匿名聯合。另一方面,聯合與 C 類似。

聯合不能包含修正類型(指針、文件描述符、Binder 對象,等等)。它們不需要特殊字段或關聯的類型,只需通過 memcpy() 或等效函數即可複製。聯合不能直接包含(或通過其他數據結構包含)需要設置 Binder 偏移量(即句柄或 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
  }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章