Créer l'interface HAL

Vous devez utiliser HIDL pour décrire tous les indicateurs de compilation utilisés pour compiler de manière conditionnelle le framework. Les options de compilation pertinentes doivent être regroupées et incluses dans un seul fichier .hal. L'utilisation de HIDL pour spécifier des éléments de configuration présente les avantages suivants:

  • Avec gestion des versions (pour ajouter de nouveaux éléments de configuration, les fournisseurs/OEM doivent étendre explicitement le HAL)
  • Bien documenté
  • Contrôle des accès avec SELinux
  • Vérification de l'intégrité des éléments de configuration via la suite de test des fournisseurs (vérification de la plage, vérification de l'interdépendance entre les éléments, etc.)
  • API générées automatiquement en C++ et Java

Identifier les indicateurs de compilation utilisés par le framework

Commencez par identifier les configurations de compilation utilisées pour compiler de manière conditionnelle le framework, puis abandonnez les configurations obsolètes pour réduire l'ensemble. Par exemple, l'ensemble suivant d'indicateurs de compilation est identifié pour surfaceflinger:

  • 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

Créer une interface HAL

Les configurations de compilation d'un sous-système sont accessibles via une interface HAL, tandis que les interfaces permettant de fournir des valeurs de configuration sont regroupées dans le package HAL android.hardware.configstore (actuellement dans la version 1.0). Par exemple, pour créer un fichier d'interface HAL pour surfaceflinger, dans hardware/interfaces/configstore/1.0/ISurfaceFlingerConfigs.hal:

package android.hardware.configstore@1.0;

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

Après avoir créé le fichier .hal, exécutez hardware/interfaces/update-makefiles.sh pour ajouter le nouveau fichier .hal aux fichiers Android.bp et Android.mk.

Ajouter des fonctions pour les indicateurs de compilation

Pour chaque option de compilation, ajoutez une fonction à l'interface. Par exemple, dans 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);
};

Lorsque vous ajoutez une fonction:

  • Utilisez un nom concis. Évitez de convertir les noms de variable makefile en noms de fonction, et gardez à l'esprit que les préfixes TARGET_ et BOARD_ ne sont plus nécessaires.
  • Ajoutez des commentaires. Aider les développeurs à comprendre l'objectif de l'élément de configuration, la façon dont il modifie le comportement du framework, les valeurs valides et d'autres informations pertinentes.

Les types renvoyés par la fonction peuvent être Optional[Bool|String|Int32|UInt32|Int64|UInt64]. Les types sont définis dans types.hal dans le même répertoire et encapsulent les valeurs primitives avec un champ qui indique si la valeur est spécifiée par le HAL. Dans le cas contraire, la valeur par défaut est utilisée.

struct OptionalString {
    bool specified;
    string value;
};

Le cas échéant, définissez l'énumération qui représente le mieux le type de l'élément de configuration et utilisez cette énumération comme type renvoyé. Dans l'exemple ci-dessus, l'énumération NumBuffers est définie pour limiter le nombre de valeurs valides. Lorsque vous définissez ces types de données personnalisées, ajoutez un champ ou une valeur d'énumération (par exemple, USE_DEFAULT) pour indiquer si la valeur est ou non spécifiée par le HAL.

Il n'est pas obligatoire qu'un indicateur de compilation unique devienne une fonction unique dans HIDL. Les propriétaires de module peuvent également agréger des indicateurs de compilation étroitement liés dans un struct et disposer d'une fonction qui renvoie ce struct (cela peut réduire le nombre d'appels de fonction).

Par exemple, une option permettant d'agréger deux indicateurs de compilation dans une seule structure dans hardware/interfaces/configstore/1.0/ISurfaceFlingerConfigs.hal est la suivante:

 interface ISurfaceFlingerConfigs {
    // other functions here
    struct SyncConfigs {
        OptionalInt64 vsyncEventPhaseoffsetNs;
        OptionalInt64 presentTimeoffsetFromSyncNs;
    };
    getSyncConfigs() generates (SyncConfigs ret);
    // other functions here
};

Alternatives à une fonction HAL unique

Au lieu d'utiliser une seule fonction HAL pour tous les indicateurs de compilation, l'interface HAL fournit également des fonctions simples telles que getBoolean(string key) et getInteger(string key). Les paires key=value réelles sont stockées dans des fichiers distincts, et le service HAL fournit des valeurs en lisant et en analysant ces fichiers.

Bien que cette approche soit facile à définir, elle n'inclut pas les avantages offerts par le protocole HIDL (gestion forcée des versions, facilité de documentation, contrôle des accès) et n'est donc pas recommandée.

Interfaces uniques ou multiples

La conception de l'interface HAL pour les éléments de configuration présente deux options:

  • Une interface unique qui couvre tous les éléments de configuration
  • Plusieurs interfaces, chacune couvrant un ensemble d'éléments de configuration associés

Il est plus facile d'utiliser une seule interface, mais celle-ci peut devenir ingérable à mesure que davantage d'éléments de configuration sont ajoutés au fichier. En outre, le contrôle des accès n'est pas précis. Par conséquent, un processus autorisé à accéder à l'interface peut lire tous les éléments de configuration (l'accès à un ensemble partiel d'éléments de configuration ne peut pas être autorisé). Si l'accès n'est pas accordé, les éléments de configuration ne peuvent pas être lus.

En raison de ces problèmes, Android utilise plusieurs interfaces avec une seule interface HAL pour un groupe d'éléments de configuration associés. Par exemple, ISurfaceflingerConfigs pour les éléments de configuration liés à surfaceflinger et IBluetoothConfigs pour les éléments de configuration liés au Bluetooth.