Tworzenie interfejsu HAL

Do opisywania wszystkich flag kompilacji używanych do warunkowej kompilacji platformy należy używać HIDL. Odpowiednie flagi kompilacji muszą być zgrupowane i umieszczone w jednym pliku .hal. Używanie HIDL do określania elementów konfiguracji przynosi takie korzyści:

  • Obsługa wersji (aby można było dodać nowe elementy konfiguracji, dostawcy/firmy OEM muszą wyraźnie rozszerzać listę HAL)
  • Dobrze udokumentowany
  • Kontrola dostępu przy użyciu SELinux
  • Kontrola poprawności elementów konfiguracji za pomocą Vendor Test Suite (sprawdzanie zakresu, sprawdzanie zależności między elementami itp.)
  • Automatycznie generowane interfejsy API w C++ i Javie

Określ flagi kompilacji używane przez platformę

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 dla surfaceflinger identyfikuje się 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 przez interfejs HAL, a interfejsy do podawania wartości konfiguracyjnych są grupowane w pakiecie HAL android.hardware.configstore (obecnie w wersji 1.0). Aby np. 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

Do każdej flagi kompilacji dodaj nową funkcję do interfejsu. 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:

  • Zachowaj zwięzłość i nazwisko. Unikaj konwertowania nazw zmiennych Makefile na nazwy funkcji. Pamiętaj, że prefiksy TARGET_ i BOARD_ nie są już potrzebne.
  • dodawać komentarze, Pomoc deweloperom w zrozumieniu przeznaczenia elementu konfiguracji oraz tego, jak zmienia on działanie platformy, prawidłowe wartości i inne istotne informacje.

Zwracane typy funkcji mogą mieć wartość Optional[Bool|String|Int32|UInt32|Int64|UInt64]. Typy są zdefiniowane w elemencie types.hal w tym samym katalogu i opakuj wartości podstawowe w polu, które wskazuje, czy dana wartość jest określona przez HAL. W przeciwnym razie zostanie użyta wartość domyślna.

struct OptionalString {
    bool specified;
    string value;
};

W razie potrzeby określ wyliczenie, które najlepiej odpowiada typowi elementu konfiguracji, i użyj tej wartości jako zwracanego typu. W powyższym przykładzie zdefiniowano wyliczenie NumBuffers w celu ograniczenia liczby prawidłowych wartości. Podczas definiowania takich niestandardowych typów danych dodaj pole lub wartość wyliczeniową (np. USE_DEFAULT) do wskazywania, czy dana wartość nie 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 korzystać z funkcji zwracającej tę strukturę (może to zmniejszyć liczbę wywołań funkcji).

Na przykład opcja agregacji 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

Zamiast używać 1 funkcji HAL na potrzeby wszystkich flag kompilacji, interfejs HAL udostępnia również proste funkcje, takie jak getBoolean(string key) i getInteger(string key). Rzeczywiste pary key=value są przechowywane w osobnych plikach, a usługa HAL dostarcza wartości po odczytaniu/analizowaniu tych plików.

To podejście jest łatwe do zdefiniowania, ale nie uwzględnia korzyści zapewnianych przez HIDL (wymuszonej obsługi wersji, łatwości dokumentacji, kontroli dostępu) i dlatego nie jest zalecane.

Jeden i wiele interfejsów

Interfejs HAL elementów konfiguracji udostępnia 2 opcje:

  • Jeden interfejs obejmujący wszystkie elementy konfiguracji
  • wiele interfejsów, z których każdy obejmuje zbiór 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. Poza tym 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 zbioru elementów konfiguracji). Jeśli nie przyznasz dostępu, elementy konfiguracji nie będą odczytywane.

Z tego powodu Android używa wielu interfejsów z jednym interfejsem HAL do obsługi 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 Bluetoothem.