Tworzenie interfejsu HAL

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_BOARD_ 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)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.