AIDL estable

Android 10 agrega compatibilidad con la interfaz estable de Android Lenguaje de definición (AIDL), una nueva forma de hacer un seguimiento del programa de postulación (API) y la interfaz binaria de aplicación (ABI) proporcionadas por el AIDL interfaces. El AIDL estable funciona igual que el AIDL, pero el sistema de compilación realiza un seguimiento la compatibilidad de la interfaz y hay restricciones sobre lo que puedes hacer:

  • Las interfaces se definen en el sistema de compilación con aidl_interfaces.
  • Las interfaces solo pueden contener datos estructurados. Objetos parcelables que representan el tipos preferidos se crean automáticamente según su definición del AIDL y se ordenan y deserializan automáticamente.
  • Las interfaces se pueden declarar como estables (compatibles con versiones anteriores). Cuando esta sucede, se realiza un seguimiento de su API y se controla su versión en un archivo junto al AIDL interfaz de usuario.

AIDL estructurado versus estable

El AIDL estructurado se refiere a los tipos definidos completamente en AIDL. Por ejemplo, un la declaración parcelable (un parcelable personalizado) no está estructurado como AIDL. Objetos parcelables con sus campos definidos en el AIDL se denominan parcelables estructurados.

El AIDL estable requiere un AIDL estructurado para que el sistema de compilación y el compilador pueda comprender si los cambios realizados en objetos parcelables son retrocompatibles. Sin embargo, no todas las interfaces estructuradas son estables. Para ser estables, una interfaz debe usar solo tipos estructurados y, además, debe usar los siguientes las funciones de control de versiones. Por el contrario, una interfaz no es estable si la compilación principal se usa para compilarlo o si se configura unstable:true.

Cómo definir una interfaz de AIDL

Una definición de aidl_interface se ve de la siguiente manera:

aidl_interface {
    name: "my-aidl",
    srcs: ["srcs/aidl/**/*.aidl"],
    local_include_dir: "srcs/aidl",
    imports: ["other-aidl"],
    versions_with_info: [
        {
            version: "1",
            imports: ["other-aidl-V1"],
        },
        {
            version: "2",
            imports: ["other-aidl-V3"],
        }
    ],
    stability: "vintf",
    backend: {
        java: {
            enabled: true,
            platform_apis: true,
        },
        cpp: {
            enabled: true,
        },
        ndk: {
            enabled: true,
        },
        rust: {
            enabled: true,
        },
    },

}
  • name: Es el nombre del módulo de interfaz del AIDL que identifica de forma única un Interfaz de AIDL.
  • srcs: Es la lista de archivos de origen del AIDL que componen la interfaz. La ruta Para un tipo de AIDL, Foo definido en un paquete com.acme debe estar en <base_path>/com/acme/Foo.aidl, donde <base_path> puede ser cualquier directorio relacionado con el directorio en el que se encuentra Android.bp. En el ejemplo anterior, <base_path> es srcs/aidl.
  • local_include_dir: Es la ruta desde donde comienza el nombre del paquete. Integra corresponde a <base_path> que se explicó anteriormente.
  • imports: Es una lista de los módulos aidl_interface que usa. Si uno de tus Las interfaces de AIDL usan una interfaz o un objeto parcelable de otro aidl_interface, escribe su nombre aquí. Este puede ser solo el nombre, para consultar las últimas versión o el nombre con el sufijo de la versión (como -V1) al que se hará referencia una versión específica. Se admite la especificación de una versión desde Android 12
  • versions: Son las versiones anteriores de la interfaz que son inmovilizado en api_dir, a partir de Android 11, las versions están inmovilizadas en aidl_api/name. Si no hay versiones sin actualizar de una interfaz, Esto no debe especificarse y no habrá verificaciones de compatibilidad. Este campo se reemplazó por versions_with_info para Android 13 y versiones posteriores.
  • versions_with_info: Lista de tuplas, cada una de las cuales contiene el nombre de un versión sin actualizar y una lista con las importaciones de versiones de otra aidl_interface módulos que importó esta versión de aidl_interface. La definición de la versión V de IFACE de la interfaz del AIDL se encuentra en aidl_api/IFACE/V Este campo se introdujo en Android 13, y no se debe modificar directamente en Android.bp. El campo es que se agregan o actualizan mediante la invocación de *-update-api o *-freeze-api. Además, los campos versions se migran automáticamente a versions_with_info. Cuando un usuario invoca a *-update-api o *-freeze-api
  • stability: Es la marca opcional de la promesa de estabilidad de esta interfaz. Esto solo es compatible con "vintf". Si no estableces stability, la compilación verifica que la interfaz sea retrocompatible, a menos Se especifica unstable. Si no se configura, corresponde a una interfaz con estabilidad dentro de este contexto de compilación (de modo que todos los elementos del sistema, por ejemplo, Por ejemplo, elementos en system.img y particiones relacionadas, o todos los proveedores elementos, por ejemplo, elementos en vendor.img y particiones relacionadas). Si stability se configura como "vintf", lo que corresponde a una promesa de estabilidad: la interfaz debe mantenerse estable mientras se usa.
  • gen_trace: Es la marca opcional para activar o desactivar el registro. Comienza en En Android 14, el valor predeterminado es true para cpp y java backends.
  • host_supported: La marca opcional que, cuando se establece en true, hace que la y las bibliotecas generadas disponibles para el entorno del host.
  • unstable: Es la marca opcional que se usa para marcar que esta interfaz no funciona. necesitan ser estables. Cuando se establece en true, el sistema de compilación no Crea el volcado de API para la interfaz ni requiere que se actualice.
  • frozen: Es la marca opcional que, cuando se establece en true, significa que la interfaz no tiene cambios desde la versión anterior de la interfaz. Esto permite más verificaciones en el tiempo de compilación. Cuando se establece en false, significa que la interfaz está en desarrollo y tiene cambios nuevos, por lo que ejecutar foo-freeze-api genera un a la versión nueva y cambiará automáticamente el valor a true. Introducida en Android 14.
  • backend.<type>.enabled: Estas marcas activan o desactivan cada uno de los backends que para el que el compilador de AIDL genera código. Hay cuatro backends compatibles: Java, C++, NDK y Rust. Los backends de Java, C++ y NDK están habilitados de forma predeterminada. Si no se necesita alguno de estos tres backends, se debe inhabilitado explícitamente. Rust está inhabilitado de forma predeterminada hasta que Android
  • backend.<type>.apex_available: Es la lista de nombres de APEX que se generó. biblioteca de stubs disponible.
  • backend.[cpp|java].gen_log: Es la marca opcional que controla si se realiza lo siguiente: generar un código adicional para recopilar información sobre la transacción.
  • backend.[cpp|java].vndk.enabled: Es la marca opcional para hacer que esta interfaz. parte de VNDK. El valor predeterminado es false.
  • backend.[cpp|ndk].additional_shared_libraries: Se introdujo en En Android 14, esta marca agrega dependencias a la bibliotecas nativas. Esta marca es útil con ndk_header y cpp_header.
  • backend.java.sdk_version: Es la marca opcional para especificar la versión. del SDK con el que se compila la biblioteca de stubs de Java. El valor predeterminado es "system_current" No se debe configurar cuando backend.java.platform_apis es true.
  • backend.java.platform_apis: La marca opcional que se debe establecer en true cuando las bibliotecas generadas deben compilar en la API de la plataforma. en lugar del SDK.

Para cada combinación de versiones y backends habilitados, se usa biblioteca existente. Para obtener información sobre cómo hacer referencia a la versión específica de la biblioteca de stubs Para un backend específico, consulta las Reglas de nombres de módulos.

Cómo escribir archivos AIDL

Las interfaces en AIDL estables son similares a las tradicionales, con el que no tienen permitido usar objetos parcelables no estructurados (porque estos no son estables. consulta Estructurados y estables AIDL). La principal diferencia en el AIDL estable es cómo se definen los objetos parcelables. Anteriormente, se declaraban hacia adelante los objetos parcelables. en (y, por lo tanto, estructurado) de AIDL, los campos parcelables y las variables definidos de manera explícita.

// in a file like 'some/package/Thing.aidl'
package some.package;

parcelable SubThing {
    String a = "foo";
    int b;
}

Se admite un valor predeterminado (pero no es obligatorio) para boolean, char y float, double, byte, int, long y String. En Android 12. Los valores predeterminados para las enumeraciones definidas por el usuario también se no es compatible. Cuando no se especifica un valor predeterminado, se usa un valor similar a 0 o vacío. Las enumeraciones sin un valor predeterminado se inicializan en 0 incluso si hay no existe un enumerador cero.

Cómo usar bibliotecas de stubs

Después de agregar bibliotecas de stub como dependencia a tu módulo, puedes puedes incluirlos en tus archivos. Estos son ejemplos de bibliotecas de stubs en la sistema de compilación (Android.mk también se puede usar para las definiciones de módulos heredados):

cc_... {
    name: ...,
    shared_libs: ["my-module-name-cpp"],
    ...
}
# or
java_... {
    name: ...,
    // can also be shared_libs if your preference is to load a library and share
    // it among multiple users or if you only need access to constants
    static_libs: ["my-module-name-java"],
    ...
}
# or
rust_... {
    name: ...,
    rustlibs: ["my-module-name-rust"],
    ...
}

Ejemplo en C++:

#include "some/package/IFoo.h"
#include "some/package/Thing.h"
...
    // use just like traditional AIDL

Ejemplo en Java:

import some.package.IFoo;
import some.package.Thing;
...
    // use just like traditional AIDL

Ejemplo en Rust:

use aidl_interface_name::aidl::some::package::{IFoo, Thing};
...
    // use just like traditional AIDL

Interfaces del control de versiones

La declaración de un módulo con el nombre foo también crea un objetivo en el sistema de compilación que puedes usar para administrar la API del módulo. Cuando se compila, foo-freeze-api Agrega una nueva definición de API en api_dir. aidl_api/name, según la versión de Android agrega un archivo .hash, que representa la versión recién congelada del interfaz de usuario. foo-freeze-api también actualiza la propiedad versions_with_info. para reflejar la versión adicional y imports para la versión. Básicamente, imports en versions_with_info se copia del campo imports. Sin embargo, se especificó la última versión estable en imports, en versions_with_info para el importar, que no tiene una versión explícita. Después de especificar la propiedad versions_with_info, se ejecuta el sistema de compilación comprobaciones de compatibilidad entre versiones congeladas y también entre la parte superior del árbol (ToT) y la última versión sin actualizar.

Además, debes administrar la definición de API de la versión de ToT. Cada vez que se crea una API actualizado, ejecuta foo-update-api para actualizar aidl_api/name/current que contiene la definición de la API de la versión ToT.

Para mantener la estabilidad de una interfaz, los propietarios pueden agregar nuevos:

  • Métodos hasta el final de una interfaz (o métodos con reglas nuevas series)
  • Elementos hasta el final de un elemento parcelable (requiere que se agregue un valor predeterminado para cada uno elemento)
  • Valores constantes
  • En Android 11, los enumeradores
  • En Android 12, los campos al final de una unión

No se permiten otras acciones y nadie más puede modificar una interfaz (de lo contrario, corren el riesgo de colisionar con los cambios que realiza un propietario).

Para probar que todas las interfaces estén bloqueadas antes de lanzarse, puedes compilar con el las siguientes variables de entorno:

  • AIDL_FROZEN_REL=true m ...: La compilación requiere todas las interfaces de AIDL estables para se inmovilicen que no tengan ningún campo owner: especificado.
  • AIDL_FROZEN_OWNERS="aosp test": La compilación requiere todas las interfaces de AIDL estables. se inmovilice con el campo owner: especificado como "aosp" o "probar".

Estabilidad de las importaciones

Actualizar las versiones de importaciones para las versiones congeladas de una interfaz retrocompatible en la capa del AIDL estable. Sin embargo, para actualizarlas, actualizar todos los servidores y clientes que usan una versión anterior de la interfaz y algunas aplicaciones pueden confundirse cuando se mezclan diferentes versiones de tipos. Generalmente, para paquetes de solo tipos o comunes, esto es seguro porque el código necesita que ya están escritas para manejar tipos desconocidos de transacciones de IPC.

En la plataforma de Android, el código android.hardware.graphics.common es de este tipo de actualización de versión.

Usa interfaces con control de versiones

Métodos de interfaz

En el tiempo de ejecución, cuando se intentan llamar nuevos métodos en un servidor antiguo, los clientes nuevos un error o una excepción, según el backend.

  • El backend cpp obtiene ::android::UNKNOWN_TRANSACTION.
  • El backend ndk obtiene STATUS_UNKNOWN_TRANSACTION.
  • El backend java obtiene android.os.RemoteException con un mensaje que dice lo siguiente: No se implementó la API.

Si quieres ver estrategias para manejar esto, consulta consultar versiones con los valores predeterminados.

Objetos parcelables

Cuando se agregan campos nuevos a objetos parcelables, los clientes y servidores antiguos los descartan. Cuando los clientes y servidores nuevos reciben objetos parcelables antiguos, los valores predeterminados para los nuevos los campos se completan automáticamente. Esto significa que los valores predeterminados deben ser que se especifica para todos los campos nuevos de un objeto parcelable.

Los clientes no deberían esperar que los servidores usen los campos nuevos, a menos que conozcan servidor implementa la versión que tiene el campo definido (consulta consultar versiones).

Enumeraciones y constantes

Del mismo modo, los clientes y los servidores deben rechazar o ignorar los mensajes no reconocidos valores constantes y enumeradores según corresponda, ya que se pueden agregar más el futuro. Por ejemplo, un servidor no debe anularse cuando recibe un enumerador que no conoce. El servidor debe ignorar el enumerador o mostrará algo para que el cliente sepa que no es compatible en para llevar a cabo esta implementación.

Uniones

Intentar enviar una unión con un campo nuevo falla si el receptor es antiguo y no conoce el campo. La implementación nunca verá la unión con el campo nuevo. La falla se ignora si es una transacción unidireccional; de lo contrario, el error es BAD_VALUE(para C++ o NDK backend) o IllegalArgumentException(para el backend de Java). El error es si el cliente envía un conjunto de unión al nuevo campo a un o cuando se trata de un cliente anterior que recibe la unión de un servidor nuevo.

Administra varias versiones

Un espacio de nombres del vinculador en Android puede tener solo 1 versión de un aidl específico interfaz para evitar situaciones en las que los tipos aidl generados tengan varias definiciones C++ tiene la regla de una definición que requiere solo una definición. de cada símbolo.

La compilación de Android genera un error cuando un módulo depende de diferentes de la misma biblioteca aidl_interface. El módulo puede depender de estas bibliotecas directa o indirectamente a través de dependencias de sus dependencias. Estos errores muestran el gráfico de dependencias del módulo con errores al las versiones en conflicto de la biblioteca aidl_interface Todas las las dependencias deben actualizarse para incluir la misma versión (por lo general, la más reciente) de estas bibliotecas.

Si muchos módulos diferentes usan la biblioteca de la interfaz, podría ser útil. para crear cc_defaults, java_defaults y rust_defaults para cualquier grupo de bibliotecas y procesos que deben usar la misma versión. Al presentar un en la nueva versión de la interfaz, esos valores predeterminados se pueden actualizar y todos los módulos que las utilizan se actualizan juntos, lo que garantiza que no usen versiones diferentes de la interfaz.

cc_defaults {
  name: "my.aidl.my-process-group-ndk-shared",
  shared_libs: ["my.aidl-V3-ndk"],
  ...
}

cc_library {
  name: "foo",
  defaults: ["my.aidl.my-process-group-ndk-shared"],
  ...
}

cc_binary {
  name: "bar",
  defaults: ["my.aidl.my-process-group-ndk-shared"],
  ...
}

Cuando los módulos aidl_interface importan otros módulos aidl_interface, se crea y las dependencias adicionales que requieren versiones específicas que se usen en conjunto. Esta puede ser difícil de controlar cuando hay aidl_interface comunes los módulos que se importan en varios módulos aidl_interface que se usan en los mismos procesos.

Se puede usar aidl_interfaces_defaults para mantener una definición del versiones más recientes de dependencias para un aidl_interface que se pueden actualizar en un solo lugar, y lo usan todos los módulos de aidl_interface que quieren importar esa interfaz común.

aidl_interface_defaults {
  name: "android.popular.common-latest-defaults",
  imports: ["android.popular.common-V3"],
  ...
}

aidl_interface {
  name: "android.foo",
  defaults: ["my.aidl.latest-ndk-shared"],
  ...
}

aidl_interface {
  name: "android.bar",
  defaults: ["my.aidl.latest-ndk-shared"],
  ...
}

Desarrollo basado en marcas

Las interfaces en desarrollo (descongeladas) no se pueden usar en los dispositivos de lanzamiento, ya que no se garantiza que sean retrocompatibles.

El AIDL admite el resguardo en el tiempo de ejecución para estas bibliotecas de interfaces no inmovilizadas a fin de para que el código se escriba en la última versión no inmovilizada y se siga usando en dispositivos de lanzamiento. El comportamiento retrocompatible de los clientes es similar al en el comportamiento existente y, con el resguardo, las implementaciones esos comportamientos. Consulta Usa interfaces con control de versiones.

marca de compilación del AIDL

La marca que controla este comportamiento es RELEASE_AIDL_USE_UNFROZEN definido en build/release/build_flags.bzl. true significa que la versión descongelada de se usa la interfaz durante el tiempo de ejecución, y false significa que las bibliotecas de la Las versiones no inmovilizadas se comportan como la última versión sin actualizar. Puedes anular la marca true para desarrollo local, pero deberá revertirlo a false antes del lanzamiento. Precio habitual el desarrollo se realiza con una configuración que tiene la marca establecida en true.

Matriz de compatibilidad y manifiestos

Los objetos de interfaz de proveedor (objetos VINTF) definen qué versiones se esperan y qué versiones se proporcionan en ambos lados la interfaz del proveedor.

La mayoría de los dispositivos que no son de Cuttlefish se orientan a la matriz de compatibilidad más reciente. solo después de que se inmovilizan las interfaces, por lo que no hay diferencia en el AIDL bibliotecas basadas en RELEASE_AIDL_USE_UNFROZEN.

Matriz

Las interfaces que son propiedad del socio se agregan a interfaces específicas del dispositivo o del producto de compatibilidad a las que se orienta el dispositivo durante el desarrollo. Entonces, cuando un se agrega a la matriz de compatibilidad una versión nueva y descongelada de la interfaz las versiones congeladas anteriores deben permanecer RELEASE_AIDL_USE_UNFROZEN=false Puedes manejar esto usando diferentes archivos de matriz de compatibilidad para diferentes RELEASE_AIDL_USE_UNFROZEN configuraciones o permitir ambas versiones en un único archivo de matriz de compatibilidad que se usa en todas las configuraciones.

Por ejemplo, cuando agregues una versión 4 no inmovilizada, usa <version>3-4</version>.

Cuando la versión 4 esté bloqueada, puedes quitar la versión 3 de la matriz de compatibilidad. porque la versión sin actualizar 4 se usa cuando RELEASE_AIDL_USE_UNFROZEN está false

Manifiestos

En Android 15, se introdujo un cambio en libvintf en modificar los archivos de manifiesto en el tiempo de compilación según el valor de RELEASE_AIDL_USE_UNFROZEN

Los manifiestos y los fragmentos de manifiesto declaran qué versión de una interfaz. implementa un servicio. Cuando uses la versión más reciente de una interfaz, el manifiesto debe actualizarse para reflejar esta nueva versión. Cuándo RELEASE_AIDL_USE_UNFROZEN=false las entradas del manifiesto se ajustan según libvintf para reflejar el cambio en la biblioteca del AIDL generada. La versión se modifica de la versión descongelada, N, a la última versión sin actualizar N - 1. Por lo tanto, los usuarios no necesitan administrar manifiestos o fragmentos de manifiestos para cada uno de sus servicios.

Cambios en el cliente de HAL

El código del cliente HAL debe ser retrocompatible con todos los dispositivos congelados admitidos. versión. Cuando RELEASE_AIDL_USE_UNFROZEN es false, los servicios siempre buscan como la última versión sin actualizar o una anterior (por ejemplo, llamar al nuevo dispositivo desbloqueado) métodos que muestran UNKNOWN_TRANSACTION, o los nuevos campos parcelable tienen su valores predeterminados). Los clientes del framework de Android deben estar retrocedidos compatibles con otras versiones anteriores, pero este es un nuevo detalle para clientes proveedores y clientes de interfaces propiedad de socios.

Cambios en la implementación de HAL

La mayor diferencia en el desarrollo de HAL con el desarrollo basado en marcas es necesario para que las implementaciones de HAL sean retrocompatibles con el último versión sin actualizar para que funcione cuando RELEASE_AIDL_USE_UNFROZEN sea false. La consideración de la retrocompatibilidad en las implementaciones y el código del dispositivo es una ejercicio. Consulta Usa versiones interfaces de usuario.

Las consideraciones de retrocompatibilidad suelen ser las mismas para el clientes y servidores, y para el código del framework y del proveedor, pero existen diferencias sutiles que debes tener en cuenta, ya que ahora con la implementación de dos versiones que usan el mismo código fuente (la versión actual, versión).

Ejemplo: Una interfaz tiene tres versiones congeladas. La interfaz se actualiza con una método nuevo. El cliente y el servicio se actualizan para usar la nueva versión 4 biblioteca. Debido a que la biblioteca V4 se basa en una versión no inmovilizada de la se comporta como la última versión sin actualizar, cuando RELEASE_AIDL_USE_UNFROZEN es false y evita el uso del método nuevo.

Cuando la interfaz está bloqueada, todos los valores de RELEASE_AIDL_USE_UNFROZEN usan eso. versión sin actualizar y se puede quitar el código que controla la retrocompatibilidad.

Cuando llames a métodos en devoluciones de llamada, debes manejar correctamente el caso cuando Se muestra UNKNOWN_TRANSACTION. Es posible que los clientes implementen dos de una devolución de llamada según la configuración de lanzamiento, así que no puedes se da por sentado que el cliente envía la versión más reciente, y es posible que se devuelvan esto. Esto es similar a cómo los clientes estables de AIDL mantienen la compatibilidad con los servidores se describe en Cómo usar interfaces de usuario.

// Get the callback along with the version of the callback
ScopedAStatus RegisterMyCallback(const std::shared_ptr<IMyCallback>& cb) override {
    mMyCallback = cb;
    // Get the version of the callback for later when we call methods on it
    auto status = mMyCallback->getInterfaceVersion(&mMyCallbackVersion);
    return status;
}

// Example of using the callback later
void NotifyCallbackLater() {
  // From the latest frozen version (V2)
  mMyCallback->foo();
  // Call this method from the unfrozen V3 only if the callback is at least V3
  if (mMyCallbackVersion >= 3) {
    mMyCallback->bar();
  }
}

Es posible que los campos nuevos en los tipos existentes (parcelable, enum, union) no existen o contienen sus valores predeterminados cuando RELEASE_AIDL_USE_UNFROZEN es false y los valores de los campos nuevos que un servicio intenta enviar se descartan. para salir del proceso.

No se pueden enviar los nuevos tipos que se agregan en esta versión no inmovilizada o recibidos a través de la interfaz.

La implementación nunca recibe una llamada de nuevos métodos por parte de ningún cliente cuando RELEASE_AIDL_USE_UNFROZEN es false.

Ten cuidado de usar nuevos enumeradores solo con la versión en la que se presentan. y no a la versión anterior.

Por lo general, usas foo->getInterfaceVersion() para ver qué versión del control remoto usa la interfaz de usuario. Sin embargo, con la compatibilidad con el control de versiones basado en marcas implementando dos versiones diferentes, así que quizás quieras obtener la versión la interfaz actual. Puedes hacerlo obteniendo la versión de interfaz del objeto actual, por ejemplo, this->getInterfaceVersion() o el otro métodos para my_ver. Visita Consulta de la versión de interfaz del control remoto objeto para obtener más información.

Nuevas interfaces estables de VINTF

Cuando se agrega un nuevo paquete de interfaz de AIDL, no hay última versión sin actualizar, por lo que no hay comportamiento al que recurrir cuando RELEASE_AIDL_USE_UNFROZEN es false No uses estas interfaces. Cuando RELEASE_AIDL_USE_UNFROZEN sea false, el administrador de servicios no permitirá que el servicio registre la interfaz y los clientes no lo encontrarán.

Puedes agregar los servicios de forma condicional según el valor de la RELEASE_AIDL_USE_UNFROZEN en el archivo makefile del dispositivo:

ifeq ($(RELEASE_AIDL_USE_UNFROZEN),true)
PRODUCT_PACKAGES += \
    android.hardware.health.storage-service
endif

Si el servicio forma parte de un proceso más grande, por lo que no puedes agregarlo al dispositivo condicionalmente, puedes verificar si el servicio se declaró con IServiceManager::isDeclared() Si se declaró y no se pudo registrar, a anular el proceso. Si no está declarado, se espera que no se registre.

Cuttlefish como herramienta de desarrollo

Después del congelamiento de VINTF todos los años, ajustamos la compatibilidad con el marco de trabajo. matrix (FCM) target-level y el PRODUCT_SHIPPING_API_LEVEL de Cuttlefish así que reflejan los dispositivos que se lanzan con el lanzamiento del próximo año. Ajustamos target-level y PRODUCT_SHIPPING_API_LEVEL para asegurarte de que haya dispositivo de lanzamiento probado y que cumpla con los nuevos requisitos de los lanzamiento.

Cuando RELEASE_AIDL_USE_UNFROZEN es true, Cuttlefish es para desarrollar versiones futuras de Android. Se orienta a las aplicaciones de Android nivel de FCM de la versión y PRODUCT_SHIPPING_API_LEVEL, lo cual requiere que cumpla con los requisitos de software del proveedor (VSR) del próximo lanzamiento.

Cuando RELEASE_AIDL_USE_UNFROZEN es false, Cuttlefish tiene la configuración target-level y PRODUCT_SHIPPING_API_LEVEL para reflejar un dispositivo de lanzamiento. En Android 14 y versiones anteriores, esta diferenciación sería logrado con diferentes ramas de Git que no detectan el cambio a FCM target-level, nivel de API de envío o cualquier otro código segmentado para la siguiente lanzamiento.

Reglas de nomenclatura de módulos

En Android 11, para cada combinación de versiones y habilitar los backends, se crea automáticamente un módulo de biblioteca de stub. Para recomendar a un módulo de biblioteca de stub específico para la vinculación, no uses el nombre del aidl_interface, pero el nombre del módulo de biblioteca de stub, que es ifacename-version-backend, donde

  • ifacename: Es el nombre del módulo aidl_interface.
  • version es cualquiera de
    • Vversion-number para las versiones sin actualizar
    • Vlatest-frozen-version-number + 1 para el versión en punta del árbol (aún congelada)
  • backend es cualquiera de
    • java para el backend de Java,
    • cpp para el backend de C++,
    • ndk o ndk_platform para el backend del NDK. El primero es para aplicaciones, La última es para uso de la plataforma hasta Android 13. En Para Android 13 y versiones posteriores, solo usan ndk.
    • rust para el backend de Rust

Supongamos que hay un módulo con el nombre foo y su última versión es 2, y admite NDK y C++. En este caso, el AIDL genera los siguientes módulos:

  • Basado en la versión 1
    • foo-V1-(java|cpp|ndk|ndk_platform|rust)
  • Basado en la versión 2 (la versión estable más reciente)
    • foo-V2-(java|cpp|ndk|ndk_platform|rust)
  • Según la versión de ToT
    • foo-V3-(java|cpp|ndk|ndk_platform|rust)

En comparación con Android 11:

  • foo-backend, que hace referencia a la versión estable más reciente versión se convierte en foo-V2-backend
  • foo-unstable-backend, que hace referencia al ToT versión se convierte en foo-V3-backend

Los nombres del archivo de salida son siempre los mismos que los nombres de los módulos.

  • Basado en la versión 1: foo-V1-(cpp|ndk|ndk_platform|rust).so
  • Basado en la versión 2: foo-V2-(cpp|ndk|ndk_platform|rust).so
  • Según la versión de ToT: foo-V3-(cpp|ndk|ndk_platform|rust).so

Ten en cuenta que el compilador de AIDL no crea un módulo de versión unstable. o un módulo sin control de versiones para una interfaz de AIDL estable. A partir de Android 12, el nombre de módulo generado a partir de una estable de AIDL siempre incluye su versión.

Nuevos métodos de metainterfaz

En Android 10, se agregan varios métodos de metainterfaz para el AIDL estable.

Consulta la versión de la interfaz del objeto remoto

Los clientes pueden consultar la versión y el hash de la interfaz a la que el objeto remoto implementa y compara los valores que se muestran con los valores de la interfaz que usa el cliente.

Ejemplo con el backend cpp:

sp<IFoo> foo = ... // the remote object
int32_t my_ver = IFoo::VERSION;
int32_t remote_ver = foo->getInterfaceVersion();
if (remote_ver < my_ver) {
  // the remote side is using an older interface
}

std::string my_hash = IFoo::HASH;
std::string remote_hash = foo->getInterfaceHash();

Ejemplo con el backend ndk (y ndk_platform):

IFoo* foo = ... // the remote object
int32_t my_ver = IFoo::version;
int32_t remote_ver = 0;
if (foo->getInterfaceVersion(&remote_ver).isOk() && remote_ver < my_ver) {
  // the remote side is using an older interface
}

std::string my_hash = IFoo::hash;
std::string remote_hash;
foo->getInterfaceHash(&remote_hash);

Ejemplo con el backend java:

IFoo foo = ... // the remote object
int myVer = IFoo.VERSION;
int remoteVer = foo.getInterfaceVersion();
if (remoteVer < myVer) {
  // the remote side is using an older interface
}

String myHash = IFoo.HASH;
String remoteHash = foo.getInterfaceHash();

Para lenguaje Java, el lado remoto DEBE implementar getInterfaceVersion() y getInterfaceHash() de la siguiente manera (se usa super en lugar de IFoo para evitar errores de copiar y pegar. La anotación @SuppressWarnings("static") podría necesaria para inhabilitar las advertencias, según la configuración de javac):

class MyFoo extends IFoo.Stub {
    @Override
    public final int getInterfaceVersion() { return super.VERSION; }

    @Override
    public final String getInterfaceHash() { return super.HASH; }
}

Esto se debe a que las clases generadas (IFoo, IFoo.Stub, etc.) se comparten entre el cliente y el servidor (por ejemplo, las clases pueden estar en el la ruta de clase). Cuando se comparten clases, el servidor también se vincula al a la versión más nueva de las clases, aunque se haya creado con una versión más reciente de la interfaz. Si esta metainterfaz se implementa en el entorno esta siempre devuelve la versión más reciente. Sin embargo, si implementas el método como se muestra arriba, el número de versión de la interfaz está incorporado en el código del servidor. (porque IFoo.VERSION es un static final int que está intercalado cuando se hace referencia a él) y, por lo tanto, el método puede devolver la versión exacta con la que se compiló el servidor.

Cómo manejar interfaces más antiguas

Es posible que un cliente se haya actualizado con la versión más reciente de un AIDL. pero el servidor usa la interfaz del AIDL anterior. En esos casos, Si llamas a un método en una interfaz anterior, se muestra UNKNOWN_TRANSACTION.

Con un AIDL estable, los clientes tienen más control. En el lado del cliente, puedes establecer una implementación predeterminada en una interfaz de AIDL. Un método en la configuración la implementación solo se invoca cuando el método no se implementa en la aplicación remota (porque se compiló con una versión anterior de la interfaz). Desde los valores predeterminados se establecen globalmente, no se deben usar de grupos diferentes.

Ejemplo en C++ en Android 13 y versiones posteriores:

class MyDefault : public IFooDefault {
  Status anAddedMethod(...) {
   // do something default
  }
};

// once per an interface in a process
IFoo::setDefaultImpl(::android::sp<MyDefault>::make());

foo->anAddedMethod(...); // MyDefault::anAddedMethod() will be called if the
                         // remote side is not implementing it

Ejemplo en Java:

IFoo.Stub.setDefaultImpl(new IFoo.Default() {
    @Override
    public xxx anAddedMethod(...)  throws RemoteException {
        // do something default
    }
}); // once per an interface in a process

foo.anAddedMethod(...);

No es necesario que proporciones la implementación predeterminada de todos los métodos en un AIDL. interfaz de usuario. Métodos que se garantizan la implementación en el lado remoto (porque sabes que el control remoto está compilado cuando los métodos estaban en la descripción de la interfaz del AIDL) no es necesario anular en el impl predeterminado clase.

Convierte el AIDL existente en AIDL estructurado o estable

Si ya tienes una interfaz de AIDL y un código que la usa, usa el siguiente comando: pasos para convertir la interfaz en una interfaz de AIDL estable.

  1. Identifica todas las dependencias de tu interfaz. Para cada paquete, la de la que depende la interfaz, determina si el paquete se define en un AIDL estable. Si sin definir, el paquete se debe convertir.

  2. Convierte todos los objetos parcelables de tu interfaz en objetos parcelables estables (el los archivos de la interfaz en sí pueden permanecer sin cambios). Hazlo antes del y expresar su estructura directamente en archivos AIDL. Las clases de administración deben que se reescribirán para usar estos nuevos tipos. Esto puede hacerse antes de crear aidl_interface (abajo).

  3. Crea un paquete aidl_interface (como se describió más arriba) que contenga lo siguiente: de tu módulo, sus dependencias y cualquier otra información que necesites. Para que se pueda estabilizar (no solo estructurado), también se necesita un control de versiones. Para obtener más información, consulta Interfaces de control de versiones.