Crea l'interfaccia HAL

Devi utilizzare HIDL per descrivere tutti i flag di build utilizzati per la compilazione condizionale del framework. I flag di build pertinenti devono essere raggruppati e inclusi in un unico file .hal. L'utilizzo di HIDL per specificare gli elementi di configurazione offre i seguenti vantaggi:

  • Con controllo delle versioni (per aggiungere nuovi elementi di configurazione, i fornitori/OEM devono estendere esplicitamente l'HAL)
  • Ben documentato
  • Controllo dell'accesso mediante SELinux
  • Controllo di integrità degli elementi di configurazione tramite la suite di test del fornitore (controllo dell'intervallo, controllo dell'interdipendenza tra gli elementi e così via).
  • API generate automaticamente in C++ e Java

Identificare i flag di build utilizzati dal framework

Inizia identificando le configurazioni di build utilizzate per compilare in modo condizionale il framework, poi abbandona le configurazioni obsolete per ridurre le dimensioni del set. Ad esempio, per surfaceflinger è stato identificato il seguente insieme di flag di build:

  • 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

Crea un'interfaccia HAL

Le configurazioni di build per un sottosistema sono accessibili tramite un'interfaccia HAL, mentre le interfacce per fornire valori di configurazione sono raggruppate nel pacchetto HAL android.hardware.configstore (attualmente alla versione 1.0). Ad esempio, per creare un file di interfaccia HAL per surfaceflinger, in hardware/interfaces/configstore/1.0/ISurfaceFlingerConfigs.hal:

package android.hardware.configstore@1.0;

interface ISurfaceFlingerConfigs {
    // TO-BE-FILLED-BELOW
};

Dopo aver creato il file .hal, esegui hardware/interfaces/update-makefiles.sh per aggiungere il nuovo file .hal ai file Android.bp e Android.mk.

Aggiungere funzioni per i flag di build

Per ogni flag di build, aggiungi una nuova funzione all'interfaccia. Ad esempio, in 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);
};

Quando aggiungi una funzione:

  • Sii conciso con i nomi. Evita di convertire i nomi delle variabili makefile in nomi di funzioni e tieni presente che i prefissi TARGET_ e BOARD_ non sono più necessari.
  • Aggiungi commenti. Aiuta gli sviluppatori a comprendere lo scopo dell'elemento di configurazione, il modo in cui modifica il comportamento del framework, i valori validi e altre informazioni pertinenti.

I tipi restituiti dalle funzioni possono essere Optional[Bool|String|Int32|UInt32|Int64|UInt64]. I tipi sono definiti in types.hal nella stessa directory e racchiudono i valori primitivi con un campo che indica se il valore è specificato dall'HAL; in caso contrario, viene utilizzato il valore predefinito.

struct OptionalString {
    bool specified;
    string value;
};

Se opportuno, definisci l'enumerazione che meglio rappresenta il tipo di elemento di configurazione e utilizzala come tipo restituito. Nell'esempio precedente, l'enumerazione NumBuffers è definita per limitare il numero di valori validi. Quando definisci questi tipi di dati personalizzati, aggiungi un campo o un valore enum (ad esempio USE_DEFAULT) per indicare se il valore è/non è specificato dall'HAL.

Non è obbligatorio che un singolo flag di build diventi una singola funzione in HIDL. In alternativa, i proprietari dei moduli possono aggregare flag di build strettamente correlati in una struct e avere una funzione che restituisce quella struct (in questo modo è possibile ridurre il numero di chiamate di funzioni).

Ad esempio, un'opzione per aggregare due flag di compilazione in una singola struttura in 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
};

Alternative a una singola funzione HAL

In alternativa all'utilizzo di una singola funzione HAL per tutti i flag di build, l'interfaccia HAL fornisce anche funzioni semplici come getBoolean(string key) e getInteger(string key). Le coppie key=value effettive sono archiviate in file separati e il servizio HAL fornisce i valori leggendo/analizzando questi file.

Sebbene questo approccio sia facile da definire, non include i vantaggi forniti da HIDL (controllo delle versioni forzato, facilità di documentazione, controllo dell'accesso) e pertanto non è consigliato.

Interfacce singole e multiple

La progettazione dell'interfaccia HAL per gli elementi di configurazione presenta due scelte:

  • Un'unica interfaccia che copre tutti gli elementi di configurazione
  • Più interfacce, ognuna delle quali copre un insieme di elementi di configurazione correlati

Una singola interfaccia è più semplice, ma può diventare non gestibile man mano che vengono aggiunti più elementi di configurazione al singolo file. Inoltre, il controllo dell'accesso non è granulare, quindi un processo a cui è stato concesso l'accesso all'interfaccia può leggere tutti gli elementi di configurazione (non è possibile concedere l'accesso a un insieme parziale di elementi di configurazione). In alternativa, se l'accesso non viene concesso, gli elementi di configurazione non possono essere letti.

A causa di questi problemi, Android utilizza più interfacce con una singola interfaccia HAL per un gruppo di elementi di configurazione correlati. Ad esempio, ISurfaceflingerConfigs per gli elementi di configurazione relativi a surfaceflinger e IBluetoothConfigs per gli elementi di configurazione relativi al Bluetooth.