Musisz użyć HIDL, aby opisać wszystkie flagi kompilacji używane do warunkowego kompilowania frameworka. Odpowiednie flagi kompilacji muszą być pogrupowane i zawarowane w pojedynczym pliku .hal
. Korzystanie z HIDL do określania elementów konfiguracji ma te zalety:
- Uwzględnione w wersji (aby można było dodać nowe elementy konfiguracji, dostawcy/producenci OEM muszą wyraźnie rozszerzać listę HAL)
- dobrze udokumentowany,
- Kontrola dostępu za pomocą SELinux
- Sprawdzanie poprawności elementów konfiguracji za pomocą pakietu Vendor Test Suite (sprawdzanie zakresu, sprawdzanie wzajemnych zależności między elementami itp.).
- automatycznie generowane interfejsy API w językach C++ i Java;
Określanie flag kompilacji używanych przez framework
Zacznij od wskazania konfiguracji kompilacji używanych do warunkowej kompilacji platformy, a następnie porzuć przestarzałe konfiguracje, aby zmniejszyć zbiór. Na przykład w przypadku surfaceflinger
zidentyfikowano następujący 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
Do konfiguracji kompilacji podsystemu można uzyskać dostęp 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 interfejsu 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
Dodaj do interfejsu nową funkcję dla każdego parametru kompilacji. Na przykład w polu 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:
- Zadbaj o zwięzłość nazw. Unikaj konwertowania nazw zmiennych w makefile na nazwy funkcji i pamiętaj, że prefiksy
TARGET_
iBOARD_
nie są już potrzebne. - Dodawanie komentarzy Pomagaj deweloperom zrozumieć cel elementu konfiguracji, sposób, w jaki zmienia on działanie frameworku, prawidłowe wartości i inne istotne informacje.
Typy zwracane przez funkcję mogą być takie: Optional[Bool|String|Int32|UInt32|Int64|UInt64]
. Typy są definiowane w types.hal
w tym samym katalogu i owijają wartości prymitywne za pomocą pola, 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 razie potrzeby zdefiniuj enum, który najlepiej odpowiada typowi elementu konfiguracji, i użyj go jako typu zwracanego. W przykładzie powyżej enumeracja NumBuffers
jest zdefiniowana w celu ograniczenia liczby prawidłowych wartości. Podczas definiowania takich niestandardowych typów danych dodaj pole lub wartość z enumeracji (np. USE_DEFAULT
), aby wskazać, czy wartość jest określona przez HAL.
Nie jest konieczne, aby jedna flaga kompilacji była jedną funkcją w HIDL. Właściciele modułów mogą też agregować blisko powiązane flagi kompilacji w strukturę i utworzyć funkcję, która zwraca tę strukturę (może to zmniejszyć liczbę wywołań funkcji).
Na przykład opcja zliczania 2 flag kompilacji w jedną strukturę w hardware/interfaces/configstore/1.0/ISurfaceFlingerConfigs.hal
:
interface ISurfaceFlingerConfigs { // other functions here struct SyncConfigs { OptionalInt64 vsyncEventPhaseoffsetNs; OptionalInt64 presentTimeoffsetFromSyncNs; }; getSyncConfigs() generates (SyncConfigs ret); // other functions here };
Odpowiedniki pojedynczej funkcji HAL
Jako alternatywę dla używania jednej funkcji HAL dla wszystkich flag kompilacji interfejs HAL udostępnia też proste funkcje, takie jak getBoolean(string
key)
i getInteger(string key)
. Rzeczywiste pary key=value
są przechowywane w osobnych plikach, a usługa HAL udostępnia wartości, odczytując i analizując te pliki.
Chociaż to podejście jest łatwe do zdefiniowania, nie zapewnia zalet HIDL (wymuszone wersjonowanie, łatwość dokumentacji, kontrola dostępu), dlatego nie jest zalecane.
Jeden i wiele interfejsów
Interfejs HAL dla elementów konfiguracji oferuje 2 opcje:
- Jeden interfejs obejmujący wszystkie elementy konfiguracji
- wiele interfejsów, z których każdy obejmuje zestaw powiązanych elementów konfiguracji;
Jeden interfejs jest prostszy, ale jego obsługa może się okazać niemożliwa, ponieważ do jednego pliku zostanie dodanych więcej elementów konfiguracji. Ponadto kontrola dostępu nie jest szczegółowa, więc proces, któremu przyznano dostęp do interfejsu, może odczytać 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
dla elementów konfiguracji związanych z surfaceflinger
oraz IBluetoothConfigs
dla elementów konfiguracji związanych z Bluetooth.