Do opisywania wszystkich flag kompilacji używanych do warunkowego kompilowania platformy musisz używać HIDL. Odpowiednie flagi kompilacji muszą być zgrupowane i umieszczone w jednym pliku .hal
. Używanie HIDL do określania elementów konfiguracji
ma następujące zalety:
- Wersjonowany (aby dodać nowe elementy konfiguracji, dostawcy/producenci OEM muszą wyraźnie rozszerzyć HAL)
- Dobrze udokumentowane
- Kontrola dostępu za pomocą SELinux
- Sprawdzenie poprawności elementów konfiguracji za pomocą zestawu testów dostawcy (sprawdzenie zakresu, wzajemnej zależności między elementami itp.).
- Automatycznie generowane interfejsy API w językach C++ i Java
Określanie flag kompilacji używanych przez platformę
Zacznij od zidentyfikowania konfiguracji kompilacji używanych do warunkowej kompilacji platformy, a następnie porzuć przestarzałe konfiguracje, aby zmniejszyć ich liczbę. Na przykład dla surfaceflinger
zidentyfikowano ten zestaw flag kompilacji:
TARGET_USES_HWC2
TARGET_BOARD_PLATFORM
TARGET_DISABLE_TRIPLE_BUFFERING
TARGET_FORCE_HWC_FOR_VIRTUAL_DISPLAYS
NUM_FRAMEBUFFER_SURFACE_BUFFERS
TARGET_RUNNING_WITHOUT_SYNC_FRAMEWORK
VSYNC_EVENT_PHASE_OFFSET_NS
SF_VSYNC_EVENT_PHASE_OFFSET_NS
PRESENT_TIME_OFFSET_FROM_VSYNC_NS
MAX_VIRTUAL_DISPLAY_DIMENSION
Tworzenie interfejsu HAL
Konfiguracje kompilacji dla podsystemu są dostępne za pomocą interfejsu HAL, a interfejsy do podawania wartości konfiguracji są zgrupowane w pakiecie HALandroid.hardware.configstore
(obecnie w wersji 1.0).
Aby na przykład utworzyć plik interfejsu HAL dla surfaceflinger
, w hardware/interfaces/configstore/1.0/ISurfaceFlingerConfigs.hal
:
package android.hardware.configstore@1.0; interface ISurfaceFlingerConfigs { // TO-BE-FILLED-BELOW };
Po utworzeniu pliku .hal
uruchom polecenie
hardware/interfaces/update-makefiles.sh
, aby dodać nowy plik
.hal
do plików Android.bp
i
Android.mk
.
Dodawanie funkcji do flag kompilacji
Do każdej flagi kompilacji dodaj nową funkcję do interfejsu. Na przykład w przypadku:hardware/interfaces/configstore/1.0/ISurfaceFlingerConfigs.hal
interface ISurfaceFlingerConfigs { disableTripleBuffering() generates(OptionalBool ret); forceHwcForVirtualDisplays() generates(OptionalBool ret); enum NumBuffers: uint8_t { USE_DEFAULT = 0, TWO = 2, THREE = 3, }; numFramebufferSurfaceBuffers() generates(NumBuffers ret); runWithoutSyncFramework() generates(OptionalBool ret); vsyncEventPhaseOffsetNs generates (OptionalUInt64 ret); presentTimeOffsetFromSyncNs generates (OptionalUInt64 ret); maxVirtualDisplayDimension() generates(OptionalInt32 ret); };
Podczas dodawania funkcji:
- Używaj zwięzłych nazw. Unikaj przekształcania nazw zmiennych pliku makefile w nazwy funkcji i pamiętaj, że prefiksy
TARGET_
iBOARD_
nie są już potrzebne. - Dodaj komentarze. Pomóż deweloperom zrozumieć przeznaczenie elementu konfiguracji, sposób, w jaki zmienia on działanie platformy, prawidłowe wartości i inne istotne informacje.
Typy zwracane przez funkcje mogą być Optional[Bool|String|Int32|UInt32|Int64|UInt64]
. Typy są zdefiniowane w types.hal
w tym samym katalogu i zawierają wartości pierwotne z polem, które wskazuje, czy wartość jest określona przez HAL. Jeśli nie, używana jest wartość domyślna.
struct OptionalString { bool specified; string value; };
W odpowiednich przypadkach zdefiniuj wyliczenie, które najlepiej reprezentuje typ elementu konfiguracji, i użyj go jako typu zwracanego. W powyższym przykładzie wyliczenie NumBuffers
zostało zdefiniowane w celu ograniczenia liczby prawidłowych wartości. Podczas definiowania takich niestandardowych typów danych dodaj pole lub wartość wyliczeniową (np. USE_DEFAULT
), aby oznaczyć, czy wartość jest określona przez HAL.
Nie jest konieczne, aby pojedyncza flaga kompilacji stała się pojedynczą funkcją w HIDL. Właściciele modułów mogą też agregować ściśle powiązane flagi kompilacji w strukturę i mieć funkcję, która zwraca tę strukturę (może to zmniejszyć liczbę wywołań funkcji).
Na przykład opcja agregowania 2 flag kompilacji w jedną strukturę w hardware/interfaces/configstore/1.0/ISurfaceFlingerConfigs.hal
to:
interface ISurfaceFlingerConfigs { // other functions here struct SyncConfigs { OptionalInt64 vsyncEventPhaseoffsetNs; OptionalInt64 presentTimeoffsetFromSyncNs; }; getSyncConfigs() generates (SyncConfigs ret); // other functions here };
Alternatywy dla pojedynczej funkcji HAL
Interfejs HAL udostępnia też proste funkcje, takie jak getBoolean(string
key)
i getInteger(string key)
, które mogą być używane zamiast jednej funkcji HAL dla wszystkich flag kompilacji. Rzeczywiste pary key=value
są przechowywane w oddzielnych plikach, a usługa HAL udostępnia wartości, odczytując i parsując te pliki.
Chociaż to podejście jest łatwe do zdefiniowania, nie obejmuje korzyści zapewnianych przez HIDL (wymuszone wersjonowanie, łatwość dokumentowania, kontrola dostępu) i dlatego nie jest zalecane.
Jeden i wiele interfejsów
Projekt interfejsu HAL dla elementów konfiguracji ma 2 opcje:
- Jeden interfejs obejmujący wszystkie elementy konfiguracji
- Wiele interfejsów, z których każdy obejmuje zestaw powiązanych elementów konfiguracji.
Pojedynczy interfejs jest łatwiejszy w obsłudze, ale może stać się trudny w utrzymaniu, gdy do jednego pliku zostanie dodanych więcej elementów konfiguracji. Dodatkowo kontrola dostępu nie jest szczegółowa, więc proces, który ma dostęp do interfejsu, może odczytywać wszystkie elementy konfiguracji (nie można przyznać dostępu do częściowego zestawu elementów konfiguracji). Jeśli dostęp nie zostanie przyznany, nie będzie można odczytać elementów konfiguracji.
Z tego powodu Android używa wielu interfejsów z jednym interfejsem HAL dla grupy powiązanych elementów konfiguracji. Na przykład ISurfaceflingerConfigs
w przypadku elementów konfiguracji związanych z surfaceflinger
i IBluetoothConfigs
w przypadku elementów konfiguracji związanych z Bluetooth.