Deklaracje danych HIDL generują struktury danych o standardowym układzie w C++. Te można umieszczać w dowolnym miejscu (na stosie, w plikach lub globalnego zakresu lub na stercie) i mogą być tworzone w ten sam sposób. Klient wywołuje kod proxy HIDL przekazujący w odwołaniach stałych i typach podstawowych, a fragment i kod serwera proxy ukrywa szczegóły serializacji.
Uwaga: w żadnym momencie nie ma kodu napisanego przez programistę. wymagane do jawnego serializowania lub deserializacji struktur danych.
Tabela poniżej mapuje elementy podstawowe HIDL na typy danych w C++:
Typ HIDL | Typ C++ | Nagłówek/biblioteka |
---|---|---|
enum |
enum class |
|
uint8_t..uint64_t |
uint8_t..uint64_t |
<stdint.h> |
int8_t..int64_t |
int8_t..int64_t |
<stdint.h> |
float |
float |
|
double |
double |
|
vec<T> |
hidl_vec<T> |
libhidlbase |
T[S1][S2]...[SN] |
T[S1][S2]...[SN] |
|
string |
hidl_string |
libhidlbase |
handle |
hidl_handle |
libhidlbase |
safe_union |
(custom) struct |
|
struct |
struct |
|
union |
union |
|
fmq_sync |
MQDescriptorSync |
libhidlbase |
fmq_unsync |
MQDescriptorUnsync |
libhidlbase |
W poniższych sekcjach znajdziesz bardziej szczegółowe informacje o typach danych.
typ wyliczeniowy
Wyliczenie w HIDL staje się wyliczeniem w C++. Na przykład:
enum Mode : uint8_t { WRITE = 1 << 0, READ = 1 << 1 }; enum SpecialMode : Mode { NONE = 0, COMPARE = 1 << 2 };
... staje się:
enum class Mode : uint8_t { WRITE = 1, READ = 2 }; enum class SpecialMode : uint8_t { WRITE = 1, READ = 2, NONE = 0, COMPARE = 4 };
Począwszy od Androida 10, wyliczenie można iterować
więcej niż przy użyciu: ::android::hardware::hidl_enum_range
. Ten zakres
włącza każdy moduł wyliczający w kolejności, w jakiej występuje w kodzie źródłowym HIDL, zaczynając od
od enum nadrzędnej do ostatniego elementu podrzędnego. Na przykład:
ponad WRITE
, READ
, NONE
i
COMPARE
w tej kolejności. Dane SpecialMode
powyżej:
template <typename T> using hidl_enum_range = ::android::hardware::hidl_enum_range<T> for (SpecialMode mode : hidl_enum_range<SpecialMode>) {...}
Funkcja hidl_enum_range
ma również zaimplementowanie odwrotnych iteratorów i może być
używane w constexpr
kontekstach. Jeśli wartość występuje w wyliczeniu
, wartość występuje w zakresie kilka razy.
bitfield<T>
bitfield<T>
(gdzie T
jest wyliczeniem zdefiniowanym przez użytkownika)
staje się podstawowym typem tej wyliczenia w C++. W tym przykładzie
bitfield<Mode>
zmienia wartość na uint8_t
.
vec<T>
Szablon zajęć hidl_vec<T>
jest częścią
libhidlbase
i może służyć do przekazywania wektora dowolnego typu HIDL
o dowolnym rozmiarze. Porównywalny kontener o stałym rozmiarze to
hidl_array
hidl_vec<T>
może być też
zainicjowano tak, aby wskazywał zewnętrzny bufor danych typu T
, przy użyciu
funkcji hidl_vec::setToExternal()
.
Oprócz prawidłowego emisji/wstawienia obiektu struct w wygenerowanym
Nagłówek C++, użycie vec<T>
zapewnia pewną wygodę
funkcje do tłumaczenia na i z std::vector
oraz bare T
i wskaźnikami. Jeśli jako parametr używany jest vec<T>
, funkcja
jest przeciążony (wygenerowane są 2 prototypy), aby przyjmować
przekazują do tego celu zarówno strukturę HIDL, jak i std::vector<T>
.
tablica
Tablice stałe w hidl są reprezentowane przez klasę hidl_array
w aplikacji libhidlbase
. hidl_array<T, S1, S2, …,
SN>
reprezentuje N-wymiarową tablicę o stałym rozmiarze
T[S1][S2]…[SN]
tekst
Klasa hidl_string
(część libhidlbase
) może być
służy do przekazywania ciągów tekstowych przez interfejsy HIDL i jest zdefiniowany w
/system/libhidl/base/include/hidl/HidlSupport.h
Pierwsze miejsce na dane
lokalizacja klasy jest wskaźnikiem do jej bufora znaków.
hidl_string
wie, jak dokonać konwersji na lub z
std::string and char*
(ciąg w stylu C) z zastosowaniem
operator=
, rzutowanie niejawne i funkcja .c_str()
.
Struktury ciągów HIDL mają odpowiednie konstrukcje kopiowania i przypisanie
do:
- Wczytaj ciąg HIDL z ciągu
std::string
lub C. - Utwórz nowy
std::string
na podstawie ciągu HIDL.
Dodatkowo ciągi HIDL zawierają konstruktory konwersji, więc ciągi C
Ciągi (char *
) i ciągi znaków C++ (std::string
) mogą być używane w
przyjmujących ciąg HIDL.
struct
Pole struct
w HIDL może zawierać tylko typy danych o stałym rozmiarze i nie może
funkcji. Definicje struktury HIDL są mapowane bezpośrednio na układ standardowy
struct
w C++, dzięki czemu elementy struct
mają
spójny układ pamięci. Struktura może zawierać typy HIDL, w tym
handle
, string
i vec<T>
, czyli
i wskazywać bufory o zmiennej długości.
nick
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. Przed użyciem do analizy wszystkie wartości przekazywane między procesami muszą zostać zweryfikowane przydzieloną pamięć w ramach procesu. W przeciwnym razie nieprawidłowe nicki mogą powodować dostępu do pamięci lub jej uszkodzenia.
Typ handle
jest reprezentowany przez hidl_handle
struktury w języku C++, która stanowi proste otoczenie wokół wskaźnika
const native_handle_t
obiekt (w Androidzie przez
bardzo długi czas).
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;
Domyślnie hidl_handle
nie przejmuje własności
zawijanego wskaźnika native_handle_t
. Ma ona jedynie za zadanie
przechowuj wskaźnik do native_handle_t
, tak by można było go użyć
32- i 64-bitowych.
Sytuacje, w których hidl_handle
jest właścicielem załączonego pliku
deskryptory obejmują:
- Po wywołaniu metody
setTo(native_handle_t* handle, bool shouldOwn)
z parametremshouldOwn
ustawionym natrue
- Gdy obiekt
hidl_handle
jest tworzony przez konstrukcję tekstu z innego obiektuhidl_handle
- Gdy obiekt
hidl_handle
jest kopiowany z innegohidl_handle
obiekt
Funkcja hidl_handle
umożliwia zarówno bezpośrednie, jak i bezpośrednie konwersje
do/z native_handle_t*
obiektów. Głównym zastosowaniem
Typ handle
w HIDL to przekazywanie deskryptorów plików przez HIDL
i interfejsów. Deskryptor pojedynczego pliku jest więc reprezentowany przez
native_handle_t
bez elementów int
i 1 elementu
fd
Jeśli klient i serwer działają w innym procesie, RPC
implementacja automatycznie dba o deskryptor pliku,
oba procesy mogą działać na tym samym pliku.
Chociaż deskryptor pliku odebrany w hidl_handle
przez
jest prawidłowy w tym procesie i nie pozostaje on po przesłaniu
(zamyka się po zwróceniu funkcji). Proces, w którym chcemy
zachować stały dostęp do deskryptora pliku, musi dup()
załączonych deskryptorów plików lub skopiuj cały obiekt hidl_handle
.
Pamięć
Typ HIDL memory
jest mapowany na klasę hidl_memory
w kolumnie libhidlbase
, która reprezentuje niezmapowaną pamięć współdzieloną. To jest
obiekt, który musi być przekazywany między procesami, aby współdzielić pamięć w HIDL. Do
używaj współdzielonej pamięci:
- Uzyskaj instancję
IAllocator
(obecnie tylko instancja „ashmem” jest dostępna) i używaj go do przydzielania pamięci współdzielonej. IAllocator::allocate()
zwracahidl_memory
który może być przekazywany przez RPC HIDL i zmapowany na proces przy użyciu funkcjimapMemory
w narzędziulibhidlmemory
.mapMemory
zwraca odwołanie dosp<IMemory>
obiekt, który może być używany do uzyskiwania dostępu do pamięci. (IMemory
iIAllocator
są zdefiniowane wandroid.hidl.memory@1.0
).
Do przydzielania pamięci można używać instancji IAllocator
:
#include <android/hidl/allocator/1.0/IAllocator.h> #include <android/hidl/memory/1.0/IMemory.h> #include <hidlmemory/mapping.h> using ::android::hidl::allocator::V1_0::IAllocator; using ::android::hidl::memory::V1_0::IMemory; using ::android::hardware::hidl_memory; .... sp<IAllocator> ashmemAllocator = IAllocator::getService("ashmem"); ashmemAllocator->allocate(2048, [&](bool success, const hidl_memory& mem) { if (!success) { /* error */ } // now you can use the hidl_memory object 'mem' or pass it around }));
Rzeczywiste zmiany w pamięci muszą zostać wprowadzone za pomocą: IMemory
po stronie, która utworzyła obiekt mem
, lub po tej stronie,
otrzymuje go przez RPC HIDL.
// Same includes as above sp<IMemory> memory = mapMemory(mem); void* data = memory->getPointer(); memory->update(); // update memory however you wish after calling update and before calling commit data[0] = 42; memory->commit(); // … memory->update(); // the same memory can be updated multiple times // … memory->commit();
interfejs
Interfejsy mogą być przekazywane jako obiekty. Możesz użyć słowa interfejs
jako cukier składowy typu android.hidl.base@1.0::IBase
;
obecny interfejs oraz wszystkie importowane interfejsy są zdefiniowane
jako typ.
Zmienne obsługujące interfejsy powinny być dobrymi wskaźnikami:
sp<IName>
Funkcje HIDL, które przyjmują parametry interfejsu
przekształcać nieprzetworzone wskaźniki na mocne wskaźniki, co powoduje nieintuicyjne działanie;
(wskaźnik może zostać nieoczekiwanie wyczyszczony). Aby uniknąć problemów, zawsze zapisuj HIDL
jako sp<>
.