Typy danych

W tej sekcji opisano typy danych HIDL. Szczegóły implementacji znajdziesz w artykule HIDL C++ (dla C++) wdrożenia) lub HIDL Java (w implementacjach Java).

Podobieństwa do języka C++ to między innymi:

  • structs używa składni C++. unions obsługuje składnię C++ domyślnie. Oba typy muszą być nazwane. anonimowe struktury i sumy nie są obsługiwane.
  • Obiekty Typedefs są dozwolone w HIDL (tak jak w C++).
  • Komentarze w stylu C++ są dozwolone – są kopiowane do wygenerowanego pliku nagłówka.

Podobieństwa do Javy to między innymi:

  • Dla każdego pliku HIDL definiuje przestrzeń nazw w stylu Java, która musi zaczynać się od android.hardware. Wygenerowana przestrzeń nazw C++ to ::android::hardware::…
  • Wszystkie definicje pliku są zawarte w kodzie Java Kod interface.
  • Deklaracje tablicy HIDL są zgodne ze stylem Java, a nie C++. Przykład:
    struct Point {
        int32_t x;
        int32_t y;
    };
    Point[3] triangle;   // sized array
    
  • Komentarze są podobne do formatu javadoc.

Przedstawienie danych

struct lub union, złożone z: Układ standardowy (podzbiór wymagań dotyczących zwykłych typów danych) ma spójną pamięć układ w wygenerowanym kodzie C++, wymuszany z włączonymi jawnymi atrybutami wyrównywania struct i union członków.

Proste typy HIDL oraz enum i bitfield typy (które zawsze wywodzą się z typów podstawowych) są mapowane na standardowe typy C++. na przykład std::uint32_t z cstdint.

Ponieważ Java nie obsługuje typów niepodpisanych, niepodpisane typy HIDL są mapowane na odpowiedniego podpisanego typu Java. Obiekty Structs są mapowane na klasy Java; tablice są mapowane na tablice Java; związki nie są obecnie obsługiwane w Javie. Ciągi tekstowe są przechowywane wewnętrznie jako UTF8. Ponieważ Java obsługuje tylko ciągi UTF16, wartości ciągów znaków wysyłane do lub z implementacji Java są przetłumaczone i po ponownym tłumaczeniu mogą różnić się od siebie, zawsze płynnie tworzyć mapę.

Dane odebrane przez IPC w C++ są oznaczone etykietą const i znajdują się w pamięć tylko do odczytu, która pozostaje dostępna tylko przez czas wywołania funkcji. Dane Odebrane przez IPC w Javie zostały już skopiowane do obiektów Java, więc można mogą być przechowywane bez dodatkowego kopiowania (i mogą być modyfikowane).

Adnotacje

Do deklaracji typów można dodawać adnotacje w stylu Java. Adnotacje analizowane przez backend Vendor Test Suite (VTS) kompilatora HIDL, ale żadne takie przeanalizowane adnotacje są odczytywane przez kompilator HIDL. Zamiast tego: przeanalizowane adnotacje VTS są obsługiwane przez narzędzie VTS Compiler (VTSC).

Adnotacje używają składni Java: @annotation lub @annotation(value) lub @annotation(id=value, id=value…) gdzie wartość może być wyrażeniem stałym, ciągiem znaków lub listą wartości wewnątrz {}, tak samo jak w Javie. Wiele adnotacji o tej samej nazwie które można dołączyć do tego samego elementu.

Prześlij deklaracje

W HIDL mogą nie być zadeklarowane struktury do przekazywania dalej, przez co są zdefiniowane Niemożliwe typy danych odnoszące się do siebie (np. nie możesz opisać połączonej listy lub drzewo w HIDL). Większość istniejących kont HAL (starszych niż 8.x) w ograniczonym zakresie deklaracje, które można usunąć, zmieniając strukturę danych deklaracje.

To ograniczenie umożliwia kopiowanie struktur danych według wartości za pomocą prostego głęboka kopia, a nie śledzenie wartości wskaźników, które mogą występować w autoreferencyjnej strukturze danych. Jeśli te same dane zostaną przekazane dwukrotnie, np. z 2 parametrami metody lub vec<T> wskazującymi na tych samych danych, tworzy się i dostarcza dwie oddzielne kopie.

Zagnieżdżone deklaracje

HIDL obsługuje zagnieżdżone deklaracje na dowolnej liczbie poziomów (w jednej wyjątkiem opisanym poniżej). Na przykład:

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
    }
    …

Wyjątkiem jest to, że typy interfejsów można umieszczać tylko w vec<T> i tylko jeden poziom głębokości (brak vec<vec<IFoo>>).

Składnia nieprzetworzonych wskaźników

Język HIDL nie zawiera symbolu * i nie obsługuje zapewnia pełną swobodę wskazówek w języku C/C++. Szczegółowe informacje o uwzględnianiu HIDL wskaźniki oraz tablice/wektory, patrz vec<T> .

Interfejsy

Słowo kluczowe interface ma 2 zastosowania.

  • Otwiera definicję interfejsu w pliku .hal.
  • Można jej używać jako specjalnego typu w polach struktury/łączności, parametrach metody i powrót. Jest on postrzegany jako ogólny interfejs i synonim android.hidl.base@1.0::IBase

Na przykład IServiceManager udostępnia tę metodę:

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

Metoda obiecuje wyszukiwanie jakiegoś interfejsu według nazwy. Jest także interfejs jest identyczny z interfejsem android.hidl.base@1.0::IBase.

Interfejsy mogą być przekazywane tylko na 2 sposoby: jako parametry najwyższego poziomu lub jako członków grupy vec<IMyInterface>. Nie mogą być członkami: zagnieżdżone obiekty vecs, strukturalne, tablice lub sumy.

MQDescriptorSync i MQDescriptorUnsync

Typy MQDescriptorSync i MQDescriptorUnsync przekazują zsynchronizowane lub niezsynchronizowane deskryptory Szybkich wiadomości (FMQ). za pomocą interfejsu HIDL. Więcej informacji: HIDL C++ (kody FMQ nie są obsługiwane w Javie).

typ pamięci

Typ memory jest używany do reprezentowania niezmapowanej pamięci współdzielonej w: HIDL. Jest on obsługiwany tylko w języku C++. Tego typu wartości można użyć w funkcji koniec odbierający, aby zainicjować obiekt IMemory, mapować pamięć i zapewnianiu użyteczności. Więcej informacji: HIDL C++.

Ostrzeżenie: uporządkowane dane umieszczone w udostępnionych danych musi być typem, którego format nigdy się nie zmienia przez cały czas istnienia wersję interfejsu z komunikatem memory. W przeciwnym razie właściciele HAL mogą mieć problemy krytyczne problemy ze zgodnością.

typ wskaźnika

Typ pointer jest przeznaczony tylko do użytku wewnętrznego HIDL.

bitfield<T> typ szablonu

bitfield<T>, w którym T to wyliczenie zdefiniowane przez użytkownika sugeruje, że wartość jest bitem LUB argumentu wartości wyliczeniowe zdefiniowane w funkcji T. W wygenerowanym kodzie bitfield<T> jest typowym typem T. Dla: przykład:

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

Kompilator obsługuje typ „Flagi” tak samo jak uint8_t.

Dlaczego nie stosować (u)int16_t.(u)int8_t.(u)int32_t/(u)int64_t? Użycie bitfield dostarcza czytelnikom dodatkowych informacji HAL, który teraz wie, że setFlags przyjmuje wartość bitową LUB flagę (tzn. wie, że nawiązanie połączenia setFlags z numerem 16 jest nieprawidłowe). Bez bitfield, te informacje są przekazywane tylko w ramach dokumentacji. W VTS może natomiast sprawdzić, czy wartość flag jest rozbita na bity LUB flagę.

Uchwyty typów podstawowych

OSTRZEŻENIE: wszelkiego rodzaju adresy (nawet fizyczne adresów urządzeń) nigdy nie mogą być częścią natywnego nicka. Zaliczono informacje między procesami są niebezpieczne i narażają je na atak. Wszelkie wartości przekazywane między procesami muszą zostać zweryfikowane, zanim zostaną użyte do ich użycia sprawdzać przydzieloną pamięć w ramach procesu. W przeciwnym razie nieprawidłowe nicki mogą powodować dostępu do pamięci lub jej uszkodzenia.

Semantyka HIDL to kopiowanie według wartości, co oznacza, że parametry są kopiowane. Wszelkie duże porcje danych lub dane, które muszą być współdzielone przez procesy. (takich jak ogrodzenie synchronizacji) są obsługiwane przez deskryptory plików wskazujące do obiektów trwałych: ashmem w przypadku pamięci współdzielonej, rzeczywistych plików lub wszystko, co można ukryć za deskryptorem pliku. Sterownik duplikuje deskryptor pliku do drugiego procesu.

uchwyt_natywny

Android obsługuje native_handle_t – ogólny nick zdefiniowane w: 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;

Uchwyt natywny to zbiór int i deskryptorów plików, który jest przekazywany wokół wartości. Deskryptor jednego pliku można przechowywać w natywnym nicku z bez int i deskryptora pojedynczego pliku. Uchwyty przekazywania za pomocą uchwytów natywnych umieszczony w elemencie podstawowym handle zapewnia uchwyty są bezpośrednio uwzględnione w HIDL.

Nie można użyć elementu native_handle_t, ponieważ ma zmienny rozmiar bezpośrednio w elemencie struct. Pole uchwytu generuje wskaźnik do oddzielnego przydzielonych native_handle_t.

We wcześniejszych wersjach Androida natywne nicki były tworzone przy użyciu tego samego funkcje obecne w libcutils. W Androidzie 8.0 i nowszych funkcje te są teraz kopiowane do Przestrzeń nazw android::hardware::hidl lub przeniesiona do NDK. HIDL automatycznie wygenerowany kod zserializuje i deserializuje te funkcje automatycznie, bez udziału kodu napisanego przez użytkownika.

Nick i własność deskryptora pliku

Gdy wywołujesz metodę interfejsu HIDL, która przekazuje (lub zwraca) hidl_handle obiekt (najwyższego poziomu lub część typu złożonego), własność zawartych w nim deskryptorów plików jest następująca:

  • Wywołujący przekazuje obiekt hidl_handle jako zachowuje prawo własności do deskryptorów plików zawartych w native_handle_t zawija, rozmówca musi zamknąć ten plik deskryptory.
  • Proces zwracający hidl_handle (przekazując go do funkcji _cb) zachowuje prawo własności do obiektu deskryptory plików zawarte w elemencie native_handle_t opakowanym przez komponent obiektu; proces musi zamknąć te deskryptory plików, gdy je skończy.
  • Transport, do którego przypisano hidl_handle, ma własność deskryptorów plików w obrębie native_handle_t opakowany przez obiekt; odbiorca może używać tych deskryptorów plików wywołanie zwrotne transakcji, ale trzeba skopiować uchwyt natywny, aby użyć pliku deskryptory poza wywołaniem zwrotnym. Transport automatycznie nawiąże połączenie close() dla deskryptorów plików po zakończeniu transakcji.

HIDL nie obsługuje nicków w Javie (ponieważ Java nie obsługuje nicków wszystkie).

Tablice o określonym rozmiarze

W przypadku tablic o określonych rozmiarach w strukturach HIDL ich elementy mogą mieć dowolny typ struktury mogą zawierać:

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

Strings

Ciągi znaków w C++ i Javie wyglądają inaczej, ale bazowy system transportowy typ pamięci masowej jest strukturą C++. Więcej informacji: Typy danych HIDL w C++ lub Typy danych HIDL w Javie.

Uwaga: przekazywanie ciągu znaków do lub z Javy Interfejs HIDL (w tym Java na Java) powoduje konwersję zestawu znaków które mogą nie zachować oryginalnego kodowania.

vec<T> typ szablonu

Szablon vec<T> reprezentuje bufor o zmiennym rozmiarze zawierający wystąpienia T.

T może być jednym z tych elementów:

  • Typy podstawowe (np. uint32_t)
  • Strings
  • Wyliczenia zdefiniowane przez użytkownika
  • Jednostki struct zdefiniowane przez użytkownika
  • Interfejsy lub słowo kluczowe interface (vec<IFoo>, vec<interface> są obsługiwane tylko jako parametr najwyższego poziomu)
  • Nicki
  • pole bitowe<U>
  • vec<U>, gdzie na liście znajduje się U, z wyjątkiem interfejsu (np. vec<vec<IFoo>> nie jest obsługiwany)
  • U[] (rozmiar tablicy U), gdzie U znajduje się na liście, oprócz interfejsu

Typy zdefiniowane przez użytkownika

W tej sekcji opisano typy zdefiniowane przez użytkownika.

Wyliczenie

HIDL nie obsługuje anonimowych wyliczeniowych. W przeciwnym razie wyliczenia w HIDL są podobne. do C++11:

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

Wyliczenie bazowe jest zdefiniowane w postaci jednego z typów liczb całkowitych w HIDL. Jeśli nie wartość jest określona dla pierwszego elementu wyliczającego wyliczenie na podstawie liczby całkowitej type, wartość domyślna to 0. Jeśli nie zostanie określona żadna wartość dla późniejszego modułu wyliczającego, domyślnie zostanie użyta poprzednia wartość plus jeden. Na przykład:

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

Wyliczenie może również dziedziczyć z wcześniej zdefiniowanego wyliczenia. Jeśli żadna wartość nie jest określony dla pierwszego wyliczającego wyliczenia podrzędnego (w tym przypadku FullSpectrumColor), domyślnie przyjmuje się wartość ostatniego parametru wyliczający wyliczenie nadrzędnej plus jeden. Na przykład:

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

Ostrzeżenie: dziedziczenie wyliczenia działa wstecz z większości innych typów dziedziczenia. Wartości wyliczeniowej nie można użyć jako wartość wyliczenia nadrzędnego. Dzieje się tak, ponieważ wyliczenie podrzędne zawiera więcej wartości niż elementu nadrzędnego. Wartości wyliczenia nadrzędnej można jednak bezpiecznie używać jako wartości wyliczeniowej podrzędnej ponieważ wartości wyliczenia podrzędne są z definicji nadzbiorem wartości wyliczeniowych nadrzędnych. Pamiętaj o tym przy projektowaniu interfejsów, ponieważ oznacza to typy odnoszące się do wyliczenia nadrzędne nie mogą odnosić się do wyliczeń podrzędnych w późniejszych iteracjach argumentu za pomocą prostego interfejsu online.

Wartości wyliczenia są odwoływane za pomocą składni dwukropka (a nie w składni kropek, jak zagnieżdżonych typów). Składnia to Type:VALUE_NAME. Nie trzeba podawać type, jeśli do wartości odwołuje się ten sam typ wyliczenia lub typy podrzędne. Przykład:

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

Począwszy od Androida 10 wyliczenia mają Atrybut len, którego można używać w wyrażeniach stałych. MyEnum::len to łączna liczba wpisów w tym wyliczeniu. Ta wartość różni się od łącznej liczby wartości, która może być mniejsza, wartości są zduplikowane.

Struktura

HIDL nie obsługuje anonimowych elementów struct. W przeciwnym razie struktura w HIDL jest bardzo podobnie jak C.

HIDL nie obsługuje struktur danych o zmiennej długości, które są w całości zawarte w struct. Obejmuje to macierz o nieokreślonej długości, która jest czasami używana jako ostatnie pole struktury w C/C++ (czasami wyświetlane w formacie o rozmiarze [0]). HIDL vec<T> reprezentuje rozmiar dynamiczny tablice z danymi przechowywanymi w osobnym buforze; takie instancje są reprezentowane z wystąpieniem vec<T> w tabeli struct.

Podobnie pole string może znajdować się w elemencie struct (powiązane bufory są oddzielne). Instancje HIDL w wygenerowanym języku C++ są reprezentowane przez wskaźnik do rzeczywistego uchwytu natywnego jako instancji typu danych bazowego mają zmienną długość.

Union

HIDL nie obsługuje sum anonimowych. W przeciwnym razie sumy są podobne do C.

Związki nie mogą zawierać typów poprawek (takich jak wskaźniki, deskryptory plików, spoiwo) obiekty). Nie wymagają pól specjalnych ani powiązanych typów. po prostu za pomocą funkcji memcpy() lub jej odpowiednika. Związek nie musi bezpośrednio zawierają (lub zawierają inne struktury danych) wszystko, co wymaga ustawienia przesunięcia (tzn. uchwytów lub odwołań do interfejsu segregatora). Na przykład:

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

Związki można też deklarować w elementach struct. Na przykład:

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
  }