Crea la interfaz de HAL

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

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

Identifica las marcas de compilación que usa el framework

Comienza por identificar los archivos de configuración de compilación que se usan para compilar el framework de forma condicional y, luego, abandona los archivos de configuración obsoletos para reducir el conjunto. Por ejemplo, para surfaceflinger, se identifica el siguiente conjunto de marcas de compilación:

  • 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 HAL

Se accede a las configuraciones de compilación de un subsistema a través de una interfaz HAL, mientras que las interfaces para proporcionar valores de configuración se agrupan en el paquete 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 nuevo archivo .hal a los archivos Android.bp y Android.mk.

Agrega funciones para las marcas de compilación

Para cada marca de compilación, agrega una función nueva 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 las variables de makefile en nombres de funciones y 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 pertinente.

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 encapsulan 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 del elemento de configuración y usa esa enumeración como el tipo de devolución. En el ejemplo anterior, el enum NumBuffers se define 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 el 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. Los propietarios de módulos también pueden agregar marcas de compilación estrechamente relacionadas en una estructura y tener una función que devuelva esa estructura (hacerlo puede reducir la cantidad de llamadas a funciones).

Por ejemplo, una opción para agregar dos marcas de compilación en una sola estructura 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 a usar 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 HAL proporciona valores leyendo y analizando esos archivos.

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

Interfaces únicas y múltiples

El diseño de la interfaz de 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 sencilla, pero puede volverse difícil de mantener 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 otorga 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.