Typy danych

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 jawnej zserializacji 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 parametrem shouldOwn ustawionym na true
  • Gdy obiekt hidl_handle jest tworzony przez konstrukcję tekstu z innego obiektu hidl_handle
  • Gdy obiekt hidl_handle jest kopiowany z innego hidl_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:

  1. Uzyskaj instancję IAllocator (obecnie tylko instancja „ashmem” jest dostępna) i używaj go do przydzielania pamięci współdzielonej.
  2. IAllocator::allocate() zwraca hidl_memory który może być przekazywany przez RPC HIDL i zmapowany na proces przy użyciu funkcji mapMemory w narzędziu libhidlmemory.
  3. mapMemory zwraca odwołanie do sp<IMemory> obiekt, który może być używany do uzyskiwania dostępu do pamięci. (IMemory i IAllocator są zdefiniowane w android.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<>.