Cómo crear la interfaz de HAL

Debes usar el HIDL para describir todas las marcas de compilación que se usan en la compilación condicional del framework. Las marcas de compilación relevantes deben agruparse y incluirse en un solo archivo .hal. El uso del HIDL para especificar elementos de configuración incluye los siguientes beneficios:

  • Con control de versiones (para agregar elementos de configuración nuevos, los proveedores y OEM deben extender la HAL de forma explícita)
  • Bien documentados
  • Control de acceso con SELinux
  • Realiza una verificación de coherencia de los elementos de configuración a través del paquete de pruebas del proveedor (verificación de rango, verificación de interdependencia entre elementos, etcétera).
  • APIs generadas automáticamente en C++ y Java

Cómo identificar las marcas de compilación que usa el framework

Comienza por identificar las configuraciones de compilación que se usan para compilar de forma condicional el framework y, luego, abandona las configuraciones obsoletas para reducir el conjunto. Por ejemplo, el siguiente conjunto de marcas de compilación se identifica para 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

Cómo crear una interfaz de HAL

Se puede acceder a los parámetros de configuración de compilación de un subsistema a través de una interfaz de HAL, mientras que las interfaces para proporcionar valores de configuración se agrupan en el paquete de HAL android.hardware.configstore (actualmente en la versión 1.0). Por ejemplo, para crear un archivo de interfaz de HAL para surfaceflinger, en hardware/interfaces/configstore/1.0/ISurfaceFlingerConfigs.hal, haz lo siguiente:

package android.hardware.configstore@1.0;

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

Después de crear el archivo .hal, ejecuta hardware/interfaces/update-makefiles.sh para agregar el archivo .hal nuevo a los archivos Android.bp y Android.mk.

Agrega funciones para marcas de compilación

Para cada marca de compilación, agrega una nueva función a la interfaz. Por ejemplo, en 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);
};

Cuando agregues una función, haz lo siguiente:

  • Sé breve con los nombres. Evita convertir los nombres de variables de archivos Make en nombres de funciones. Ten en cuenta que los prefijos TARGET_ y BOARD_ ya no son necesarios.
  • Agrega comentarios. Ayuda a los desarrolladores a comprender el propósito del elemento de configuración, cómo cambia el comportamiento del framework, los valores válidos y otra información relevante.

Los tipos de datos que se muestran de la función pueden ser Optional[Bool|String|Int32|UInt32|Int64|UInt64]. Los tipos se definen en types.hal en el mismo directorio y unen valores primitivos con un campo que indica si el HAL especifica el valor. De lo contrario, se usa el valor predeterminado.

struct OptionalString {
    bool specified;
    string value;
};

Cuando corresponda, define la enumeración que mejor represente el tipo de elemento de configuración y usa esa enumeración como el tipo de datos que se muestra. En el ejemplo anterior, se define la enum NumBuffers para limitar la cantidad de valores válidos. Cuando definas esos tipos de datos personalizados, agrega un campo o un valor de enumeración (por ejemplo, USE_DEFAULT) para indicar si la HAL especifica o no el valor.

No es obligatorio que una sola marca de compilación se convierta en una sola función en HIDL. De forma alternativa, los propietarios de módulos pueden agregar marcas de compilación estrechamente relacionadas a un struct y tener una función que muestre ese struct (si lo haces, se puede reducir la cantidad de llamadas a funciones).

Por ejemplo, una opción para agregar dos marcas de compilación en un solo struct en hardware/interfaces/configstore/1.0/ISurfaceFlingerConfigs.hal es la siguiente:

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

Alternativas a una sola función de HAL

Como alternativa al uso de una sola función de HAL para todas las marcas de compilación, la interfaz de HAL también proporciona funciones simples, como getBoolean(string key) y getInteger(string key). Los pares key=value reales se almacenan en archivos separados, y el servicio de HAL proporciona valores mediante la lectura o el análisis de esos archivos.

Si bien este enfoque es fácil de definir, no incluye los beneficios que proporciona HIDL (control de acceso, versión forzada, facilidad de documentación) y, por lo tanto, no se recomienda.

Interfaces únicas y múltiples

El diseño de la interfaz de la HAL para los elementos de configuración presenta dos opciones:

  • Una sola interfaz que abarca todos los elementos de configuración
  • Varias interfaces, cada una de las cuales abarca un conjunto de elementos de configuración relacionados

Una sola interfaz es más fácil, pero puede volverse inmanejable a medida que se agregan más elementos de configuración al archivo único. Además, el control de acceso no es detallado, por lo que un proceso al que se le otorgó acceso a la interfaz puede leer todos los elementos de configuración (no se puede otorgar acceso a un conjunto parcial de elementos de configuración). Como alternativa, si no se otorga acceso, no se pueden leer los elementos de configuración.

Debido a estos problemas, Android usa varias interfaces con una sola interfaz de HAL para un grupo de elementos de configuración relacionados. Por ejemplo, ISurfaceflingerConfigs para los elementos de configuración relacionados con surfaceflinger y IBluetoothConfigs para los elementos de configuración relacionados con Bluetooth.