Tipos de datos

En esta sección, se describen los tipos de datos HIDL. Para obtener más información sobre la implementación, consulta HIDL C++ (para C++) implementaciones) o HIDL Java (para implementaciones de Java).

Las similitudes con C++ incluyen:

  • structs usa sintaxis C++. unions admite sintaxis C++. de forma predeterminada. Ambos deben tener un nombre; Las uniones y structs anónimas no son compatibles.
  • Se permiten los typedefs en el HIDL (como en C++).
  • Se permiten los comentarios de estilo C++ y se copian en el archivo del encabezado generado.

Las similitudes con Java incluyen lo siguiente:

  • Para cada archivo, el HIDL define un espacio de nombres de estilo Java que debe comenzar con android.hardware.. El espacio de nombres de C++ generado ::android::hardware::….
  • Todas las definiciones del archivo se incluyen en un archivo Wrapper interface.
  • Las declaraciones de matriz de HIDL siguen el estilo Java, no el estilo C++. Ejemplo:
    struct Point {
        int32_t x;
        int32_t y;
    };
    Point[3] triangle;   // sized array
    
  • Los comentarios son similares al formato javadoc.

Representación de datos

Una struct o union compuesta por Diseño estándar (subconjunto del requisito de los tipos de datos simples) tiene una memoria coherente diseño en código C++ generado, aplicado con atributos de alineación explícitos en struct y union miembros.

Tipos primitivos de HIDL, así como enum y bitfield tipos (que siempre derivan de tipos primitivos), se asignan a tipos C++ estándar como std::uint32_t de cstdint:

Como Java no admite tipos sin firma, los tipos de HIDL sin firma se asignan a el tipo de Java con firma correspondiente. Se asignan structs a clases de Java; Los arrays se asignan a arrays de Java. Actualmente, no se admiten las uniones en Java. Las cadenas se almacenan internamente como UTF8. Dado que Java admite solo cadenas UTF16, valores de cadena enviados a una implementación de Java o desde ella y es posible que no sean idénticas en la retraducción, ya que los grupos de caracteres no mapear siempre sin problemas.

Los datos recibidos por IPC en C++ están marcados como const y están en memoria de solo lectura que persiste solo durante la llamada a función. Datos recibidos a través de IPC en Java ya se copiaron en objetos Java, por lo que puede se conserven sin copias adicionales (y se pueden modificar).

Anotaciones

Se pueden agregar anotaciones de estilo Java a las declaraciones de tipos. Las anotaciones son analizado por el backend del Conjunto de pruebas del proveedor (VTS) del compilador HIDL, pero ninguna de el compilador HIDL comprende estas anotaciones analizadas. Más bien, El compilador de VTS (VTSC) maneja las anotaciones de VTS analizadas.

Las anotaciones usan la sintaxis de Java: @annotation o @annotation(value) o @annotation(id=value, id=value…) donde el valor puede ser una expresión constante, una cadena o una lista de valores dentro de {}, al igual que en Java. Varias anotaciones con el mismo nombre pueden adjuntarse al mismo elemento.

Declaraciones directas

En el HIDL, es posible que las structs no se declaren hacia delante, lo que hace que las estructuras tipos de datos autorreferenciales imposibles (por ejemplo, no puedes describir una lista vinculada o un árbol en HIDL). La mayoría de las HAL existentes (previas a Android 8.x) tienen un uso limitado de que se pueden quitar reorganizando la estructura de los datos de datos.

Esta restricción permite copiar las estructuras de datos por valor con un método copia profunda, en lugar de realizar un seguimiento de los valores del puntero que pueden ocurrir varias en una estructura de datos autorreferencial. Si los mismos datos se pasan dos veces, como con dos parámetros de método o vec<T> que apuntan a los mismos datos, se hacen y entregan dos copias separadas.

Declaraciones anidadas

HIDL admite declaraciones anidadas en tantos niveles como se desee (con una se indica a continuación). Por ejemplo:

interface IFoo {
    uint32_t[3][4][5][6] multidimArray;

    vec<vec<vec<int8_t>>> multidimVector;

    vec<bool[4]> arrayVec;

    struct foo {
        struct bar {
            uint32_t val;
        };
        bar b;
    }
    struct baz {
        foo f;
        foo.bar fb; // HIDL uses dots to access nested type names
    }
    …

La excepción es que los tipos de interfaz solo se pueden incorporar en vec<T> y solo un nivel de profundidad (sin vec<vec<IFoo>>).

Sintaxis del puntero sin procesar

El lenguaje HIDL no usa * y no admite las y la flexibilidad total de los punteros sin procesar C/C++. Para obtener detalles sobre cómo el HIDL encapsula punteros y matrices/vectores, consulta vec<T> plantilla.

Interfaces

La palabra clave interface tiene dos usos.

  • Abre la definición de una interfaz en un archivo .hal.
  • Puede usarse como un tipo especial en campos de struct y unión, parámetros de métodos, y los resultados. Se ve como una interfaz general y sinónimo de android.hidl.base@1.0::IBase.

Por ejemplo, IServiceManager tiene el siguiente método:

get(string fqName, string name) generates (interface service);

El método promete buscar algunas interfaces por nombre. También se usa es idéntico a reemplazar la interfaz por android.hidl.base@1.0::IBase.

Las interfaces solo se pueden pasar de dos maneras: como parámetros de nivel superior o como miembros de un vec<IMyInterface>. No pueden ser miembros de Vecs, structs, arrays o fusiones anidados.

MQDescriptorSync y MQDescriptorUnsync

Los tipos MQDescriptorSync y MQDescriptorUnsync Pasar descriptores de cola de mensajes rápidos (FMQ) sincronizados o no sincronizados en una interfaz HIDL. Para obtener más información, consulta HIDL C++ (los FMQ no son es compatible con Java).

tipo de memoria

El tipo memory se usa para representar memoria compartida sin asignar en HIDL Solo es compatible con C++. Un valor de este tipo puede usarse en la extremo receptor para inicializar un objeto IMemory y asignar la memoria y hacerlo utilizable. Para obtener más información, consulta HIDL C++:

Advertencia: Datos estructurados colocados en archivos compartidos la memoria DEBE ser de un tipo cuyo formato nunca cambie durante la vida útil del versión de interfaz que pasa el memory. De lo contrario, las HAL pueden verse afectadas y fatales.

tipo de puntero

El tipo pointer es solo para uso interno de HIDL.

campo de bits<T> plantilla de tipos

bitfield<T>, en el que T es una enum definida por el usuario sugiere que el valor es un OR bit a bit de la enum definidos en T. En el código generado, bitfield<T> aparece como el tipo subyacente de T. Por ejemplo:

enum Flag : uint8_t {
    HAS_FOO = 1 << 0,
    HAS_BAR = 1 << 1,
    HAS_BAZ = 1 << 2
};
typedef bitfield<Flag> Flags;
setFlags(Flags flags) generates (bool success);

El compilador controla las marcas de tipo igual que uint8_t.

¿Por qué no usarlo? (u)int8_t/(u)int16_t/(u)int32_t/(u)int64_t? El uso de bitfield proporciona información adicional de la HAL al lector. que ahora sabe que setFlags toma un valor OR bit a bit de Flag (es decir, sabe que no es válido llamar a setFlags con 16). Sin bitfield, esta información se transmite solo a través de la documentación. En Además, el VTS puede comprobar si el valor de las marcas es un OR a nivel de bits de Flag.

Controladores de tipos primitivos

ADVERTENCIA: Direcciones de cualquier tipo (incluso físicas del dispositivo) nunca deben ser parte de un identificador nativo. Pasando por alto esto información entre procesos es peligrosa y los hace susceptibles de sufrir ataques. Todos los valores que se pasen entre procesos se deben validar antes de usarlos para buscar memoria asignada dentro de un proceso. De lo contrario, los identificadores incorrectos podrían causar el acceso a la memoria o los daños en la memoria.

La semántica de HIDL es copia por valor, lo que implica que los parámetros se copian. Datos grandes o datos que deben compartirse entre procesos (como una valla de sincronización), se controlan pasando descriptores de archivos que a objetos persistentes: ashmem para memoria compartida, archivos reales o todo lo demás que se pueda ocultar detrás de un descriptor de archivos. El controlador de Binder duplica el descriptor de archivo en el otro proceso.

identificador_nativo_t

Android admite native_handle_t, un concepto de controlador general. definido en libcutils.

typedef struct native_handle
{
  int version;        /* sizeof(native_handle_t) */
  int numFds;         /* number of file-descriptors at &data[0] */
  int numInts;        /* number of ints at &data[numFds] */
  int data[0];        /* numFds + numInts ints */
} native_handle_t;

Un controlador nativo es una colección de descriptores de archivos enteros y descriptores de archivo que se pasa por valor. Un solo descriptor de archivo puede almacenarse en un controlador nativo con sin ints y un único descriptor de archivo. Cómo pasar controladores con controladores nativos encapsulada con el tipo primitivo handle garantiza que se incluyen directamente en el HIDL.

Como native_handle_t tiene un tamaño variable, no se puede incluir directamente en un struct. Un campo de identificador genera un puntero a un controlador independiente asignó native_handle_t.

En versiones anteriores de Android, los identificadores nativos se creaban con los mismos funciones presentes en libcutils. En Android 8.0 y versiones posteriores, estas funciones se copian en el android::hardware::hidl o se movieron al NDK. HIDL el código generado automáticamente serializa y deserializa estas funciones automáticamente, sin la participación del código escrito por el usuario.

Propiedad del descriptor de archivo y identificador

Cuando llamas a un método de la interfaz HIDL que pasa (o devuelve) una Un objeto hidl_handle (ya sea de nivel superior o como parte de un tipo compuesto) la propiedad de los descriptores de archivo que contiene es la siguiente:

  • El emisor que pasa un objeto hidl_handle como un conserva la propiedad de los descriptores de archivo contenidos en el native_handle_t se une; el llamador debe cerrar este archivo los descriptores de producto cuando termina de usarlos.
  • El proceso que muestra un hidl_handle objeto (al pasarlo a una función _cb) conserva la propiedad del descriptores de archivos contenidos en el native_handle_t que une la objeto; el proceso debe cerrar estos descriptores de archivo cuando se termina con ellos.
  • Un transporte que recibe un hidl_handle tiene la propiedad de los descriptores de archivos dentro de native_handle_t unida por el objeto; el receptor puede usar estos descriptores de archivos tal como están la devolución de llamada de la transacción, pero debe clonar el controlador nativo para usar el archivo de consulta más allá de la devolución de llamada. El transporte llama automáticamente close() para los descriptores de archivos cuando se completa la transacción

HIDL no admite controladores en Java (ya que Java no admite controladores en todas).

Arrays con tamaño

En el caso de los arrays con tamaños en structs HIDL, sus elementos pueden ser de cualquier tipo. puede contener:

struct foo {
uint32_t[3] x; // array is contained in foo
};

Strings

Las cadenas aparecen de forma diferente en C++ y Java, pero el transporte subyacente el tipo de almacenamiento es una estructura C++. Para obtener más información, consulta Tipos de datos de C++ de HIDL o Tipos de datos de Java de HIDL.

Nota: Pasar una cadena hacia o desde Java a través de un La interfaz HIDL (incluida la de Java a Java) genera conversiones de grupos de caracteres. que no conserven la codificación original.

VEC<T> plantilla de tipos

La plantilla vec<T> representa un búfer de tamaño variable que contiene instancias de T.

T puede ser una de las siguientes opciones:

  • Tipos primitivos (p.ej., uint32_t)
  • Strings
  • Enumeraciones definidas por el usuario
  • structs definidos por el usuario
  • Interfaces o la palabra clave interface (vec<IFoo>, vec<interface> es compatible solo como parámetro de nivel superior)
  • Identificadores
  • campo de bits<U>
  • vec<U>, donde U está en esta lista excepto la interfaz (p.ej., vec<vec<IFoo>> no es compatible)
  • U[] (matriz de tamaño de U), donde U está en esta lista, excepto la interfaz

Tipos definidos por el usuario

En esta sección, se describen los tipos definidos por el usuario.

Enum

El HIDL no admite enumeraciones anónimas. Por lo demás, las enumeraciones en HIDL son similares a C++11:

enum name : type { enumerator , enumerator = constexpr , …  }

Una enumeración base se define en términos de uno de los tipos de número entero en HIDL. Si la respuesta es no se especifica para el primer enumerador de una enumeración basada en un número entero el tipo, el valor predeterminado es 0. Si no se especifica ningún valor para un enumerador posterior, el valor predeterminado es el valor anterior más uno. Por ejemplo:

// RED == 0
// BLUE == 4 (GREEN + 1)
enum Color : uint32_t { RED, GREEN = 3, BLUE }

Una enumeración también puede heredar de una enumeración definida con anterioridad. Si no se indica ningún valor especificado para el primer enumerador de una enumeración secundaria (en este caso, FullSpectrumColor), el valor predeterminado es el de la última enumerador de la enumeración superior más uno. Por ejemplo:

// ULTRAVIOLET == 5 (Color:BLUE + 1)
enum FullSpectrumColor : Color { ULTRAVIOLET }

Advertencia: La herencia de enum funciona hacia atrás de la mayoría de los otros tipos de herencia. No se puede usar un valor de enumeración secundario como el valor de enumeración principal. Esto se debe a que una enumeración secundaria incluye más valores que la superior. Sin embargo, se puede usar un valor de enumeración superior como valor de enumeración secundario. porque los valores de enumeración secundarios son, por definición, un superconjunto de valores de enumeración superiores. Ten esto en cuenta cuando diseñes interfaces, ya que los tipos que se refieren a Las enumeraciones superiores no pueden referirse a las enumeraciones secundarias en iteraciones posteriores de la interfaz de usuario.

Para hacer referencia a los valores de enumeraciones, se utiliza la sintaxis de dos puntos (no la sintaxis de punto como de tipos anidados). La sintaxis es Type:VALUE_NAME. No es necesario especificar si se hace referencia al valor en el mismo tipo enum o tipos secundarios. Ejemplo:

enum Grayscale : uint32_t { BLACK = 0, WHITE = BLACK + 1 };
enum Color : Grayscale { RED = WHITE + 1 };
enum Unrelated : uint32_t { FOO = Color:RED + 1 };

A partir de Android 10, las enumeraciones tienen un Atributo len que se puede usar en expresiones constantes. MyEnum::len es la cantidad total de entradas de esa enumeración. Esto es diferente del número total de valores, que podría ser menor cuando se duplican.

Estructura

El HIDL no admite structs anónimos. Por lo demás, los structs en HIDL son similares a C.

El HIDL no admite las estructuras de datos de longitud variable contenidos completamente en un struct. Esto incluye el array de longitud indefinida que a veces se usa como el último campo de un struct en C/C++ (a veces visto con un tamaño de [0]). El valor vec<T> de HIDL representa un tamaño dinámico arrays con datos almacenados en un búfer independiente estas instancias se representan con una instancia de vec<T> en struct.

De manera similar, string puede estar contenido en un struct (los búferes asociados son independientes). En el C++ generado, las instancias del HIDL tipo de identificador se representan con un puntero al controlador nativo real, como del tipo de datos subyacente son de longitud variable.

Union

El HIDL no admite uniones anónimas. De otro modo, las uniones son similares a C.

Las uniones no pueden contener tipos de corrección (como punteros, descriptores de archivos, Binder objetos). No necesitan campos especiales ni tipos asociados y se simplemente se copió con memcpy() o uno equivalente. Una unión puede no estar directamente contener (o contener con otras estructuras de datos) todo lo que requiera configuración desplazamientos de Binder (es decir, referencias de controlador o interfaz de Binder). Por ejemplo:

union UnionType {
uint32_t a;
//  vec<uint32_t> r;  // Error: can't contain a vec<T>
uint8_t b;1
};
fun8(UnionType info); // Legal

Las uniones también se pueden declarar dentro de las structs. Por ejemplo:

struct MyStruct {
    union MyUnion {
      uint32_t a;
      uint8_t b;
    }; // declares type but not member

    union MyUnion2 {
      uint32_t a;
      uint8_t b;
    } data; // declares type but not member
  }