Google se compromete a impulsar la igualdad racial para las comunidades afrodescendientes. Obtén información al respecto.
Se usó la API de Cloud Translation para traducir esta página.
Switch to English

AIDL estable

Android 10 agrega soporte para el lenguaje de definición de interfaz de Android (AIDL) estable, una nueva forma de realizar un seguimiento de la interfaz del programa de aplicación (API) / interfaz binaria de la aplicación (ABI) proporcionada por las interfaces AIDL. El AIDL estable tiene las siguientes diferencias clave con el AIDL:

  • Las interfaces se definen en el sistema de compilación con aidl_interfaces .
  • Las interfaces solo pueden contener datos estructurados. Los parcelables que representan los tipos deseados se crean automáticamente en función de su definición AIDL y se clasifican y eliminan automáticamente.
  • Las interfaces se pueden declarar como estables (compatibles con versiones anteriores). Cuando esto sucede, su API se rastrea y se versiona en un archivo junto a la interfaz AIDL.

Definición de una interfaz AIDL

Una definición de aidl_interface ve así:

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

}
  • name : El nombre del módulo de interfaz AIDL que identifica de forma exclusiva una interfaz AIDL.
  • srcs : la lista de archivos fuente AIDL que componen la interfaz. La ruta para un tipo AIDL Foo definido en un paquete com.acme debe estar en <base_path>/com/acme/Foo.aidl , donde <base_path> podría ser cualquier directorio relacionado con el directorio donde está Android.bp . En el ejemplo anterior, <base_path> es srcs/aidl .
  • local_include_dir : la ruta desde donde comienza el nombre del paquete. Corresponde a <base_path> explicado anteriormente.
  • imports : la lista opcional de otros módulos de interfaz AIDL que esta interfaz desea importar. Los tipos de AIDL definidos en las interfaces AIDL importadas son accesibles con la declaración de import .
  • versions : Las versiones anteriores de la interfaz que están congeladas bajo api_dir . A partir de Android 11, las versions están congeladas bajo aidl_api/ name . Si no hay versiones congeladas de una interfaz, esto no debe especificarse y no habrá comprobaciones de compatibilidad.
  • stability : la bandera opcional para la promesa de estabilidad de esta interfaz. Actualmente solo es compatible con "vintf" . Si no está configurado, esto corresponde a una interfaz con estabilidad dentro de este contexto de compilación (por lo que una interfaz cargada aquí solo se puede usar con cosas compiladas juntas, por ejemplo, en system.img). Si se establece en "vintf" , esto corresponde a una promesa de estabilidad: la interfaz debe mantenerse estable mientras se utilice.
  • gen_trace : el indicador opcional para activar o desactivar el rastreo. El valor predeterminado es false .
  • host_supported : el indicador opcional que cuando se establece en true hace que las bibliotecas generadas estén disponibles para el entorno del host.
  • unstable : el indicador opcional utilizado para marcar que esta interfaz no necesita ser estable. Cuando se establece en true , el sistema de compilación no crea el volcado de API para la interfaz ni requiere que se actualice.
  • backend.<type>.enabled : Estos indicadores alternan cada uno de los backends para los que el compilador AIDL generará código. Actualmente, se admiten tres backends: java , cpp y ndk . Todos los backends están habilitados de forma predeterminada. Cuando no se necesita un backend específico, debe deshabilitarse explícitamente.
  • backend.<type>.apex_available : La lista de nombres APEX para los que está disponible la biblioteca de stub generada.
  • backend.[cpp|java].gen_log : El indicador opcional que controla si se genera código adicional para recopilar información sobre la transacción.
  • backend.[cpp|java].vndk.enabled : El indicador opcional para hacer que esta interfaz sea parte de VNDK. El valor predeterminado es false .
  • backend.java.platform_apis : el indicador opcional que controla si la biblioteca de stub de Java se construye contra las API privadas de la plataforma. Esto debe establecerse en "true" cuando la stability se establece en "vintf" .
  • backend.java.sdk_version : el indicador opcional para especificar la versión del SDK con la que se compila la biblioteca de stub de Java. El valor predeterminado es "system_current" . Esto no debería establecerse cuando backend.java.platform_apis es verdadero.
  • backend.java.platform_apis : el indicador opcional que debe establecerse en true cuando las bibliotecas generadas deben compilarse con la API de la plataforma en lugar del SDK.

Para cada combinación de las versions y los backends habilitados, se crea una biblioteca de códigos auxiliares. Consulte Reglas de nomenclatura de módulos para saber cómo hacer referencia a la versión específica de la biblioteca de códigos auxiliares para un backend específico.

Escribir archivos AIDL

Las interfaces en AIDL estable son similares a las interfaces tradicionales, con la excepción de que no se les permite usar parcelables no estructurados (¡porque no son estables!). La principal diferencia en AIDL estable es cómo se definen los parcelables. Anteriormente, las parcelas se declaraban adelantadas ; en AIDL estable, los campos y variables parcelables se definen explícitamente.

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

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

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

Usar bibliotecas de códigos auxiliares

Después de agregar bibliotecas de código auxiliar como una dependencia a su módulo, puede incluirlas en sus archivos. A continuación, se muestran ejemplos de bibliotecas de Android.mk auxiliares en el sistema de compilación ( Android.mk también se puede usar para definiciones de módulos heredados):

cc_... {
    name: ...,
    shared_libs: ["my-module-name-cpp"],
    ...
}
# or
java_... {
    name: ...,
    // can also be shared_libs if desire 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"],
    ...
}

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

Interfaces de control de versiones

Declarar un módulo con el nombre foo también crea un destino en el sistema de compilación que puede usar para administrar la API del módulo. Cuando se crea , foo-freeze-api agrega una nueva definición de API bajo api_dir o aidl_api/ name , según la versión de Android, para la próxima versión de la interfaz. La construcción de esto también actualiza la propiedad de versions para reflejar la versión adicional. Una vez que se especifica la propiedad de las versions , el sistema de compilación ejecuta verificaciones de compatibilidad entre las versiones congeladas y también entre Top of Trunk (ToT) y la última versión congelada.

Además, debe administrar la definición de API de la versión ToT. Siempre que se actualice una API, ejecute foo-update-api para actualizar aidl_api/ name /current que contiene la definición de API de la versión ToT.

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

  • Métodos hasta el final de una interfaz (o métodos con nuevas series definidas explícitamente)
  • Elementos al final de un parcelable (requiere que se agregue un valor predeterminado para cada elemento)
  • Valores constantes
  • En Android 11, enumeradores
  • En Android 12, 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).

En tiempo de ejecución, al intentar llamar a nuevos métodos en un servidor antiguo, los nuevos clientes obtienen automáticamente UNKNOWN_TRANSACTION . Para conocer las estrategias para manejar esto, consulte las versiones de consulta y el uso de valores predeterminados . Cuando se agregan nuevos campos a parcelables, los clientes / servidores antiguos los eliminan. Cuando nuevos clientes / servidores reciben parcelables antiguos, los valores predeterminados para los nuevos campos se completan automáticamente. Esto significa que se deben especificar los valores predeterminados para todos los campos nuevos en un parcelable. De manera similar, los clientes / servidores deben rechazar o ignorar los valores constantes no reconocidos y los enumeradores según corresponda, ya que es posible que se agreguen más en el futuro. Intentar enviar una unión con un campo nuevo falla si el receptor es antiguo y no conoce el campo. El error se ignora si se trata de una transacción unidireccional; de lo contrario, el error es BAD_VALUE (para el backend de C ++ o NDK) o IllegalArgumentException (para el backend de Java).

Reglas de nomenclatura de módulos

En Android 11, para cada combinación de versiones y backends habilitados, se crea automáticamente un módulo de biblioteca de códigos auxiliares. Para hacer referencia a un módulo de biblioteca de stub específico para vincularlo, no use el nombre del módulo aidl_interface , sino el nombre del módulo de la biblioteca de stub, que es ifacename - version - backend , donde

  • ifacename : nombre del módulo aidl_interface
  • version es cualquiera de
    • V version-number de V version-number para las versiones congeladas
    • V latest-frozen-version-number + 1 para la V latest-frozen-version-number + 1 punta de árbol (aún por congelar)
  • 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 y el segundo es para uso de plataforma.

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

  • Basado en la versión 1
    • foo-V1-(java|cpp|ndk|ndk_platform)
  • Basado en la versión 2 (la última versión estable)
    • foo-V2-(java|cpp|ndk|ndk_platform)
  • Basado en la versión ToT
    • foo-V3-(java|cpp|ndk|ndk_platform)

En comparación con Android 11,

  • foo- backend , que hace referencia a la última versión estable se convierte en foo- V2 - backend
  • foo-unstable- backend , que se refiere a la versión foo- V3 - backend convierte en foo- V3 - backend

Los nombres de los archivos 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).so
  • Basado en la versión 2: foo-V2-(cpp|ndk|ndk_platform).so
  • Basado en la versión de ToT: foo-V3-(cpp|ndk|ndk_platform).so

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

Nuevos métodos de meta interfaz

Android 10 agrega varios métodos de meta interfaz para el AIDL estable.

Consultar la versión de la interfaz del objeto remoto

Los clientes pueden consultar la versión y el hash de la interfaz que está implementando el objeto remoto y comparar los valores devueltos con los valores de la interfaz que está utilizando el cliente.

Ejemplo con el backend de 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 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 de 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 el lenguaje Java, el lado remoto DEBE implementar getInterfaceVersion() y getInterfaceHash() siguiente manera:

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

    @Override
    public final String getInterfaceHash() { return IFoo.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 la ruta de clases de arranque). Cuando las clases se comparten, el servidor también se vincula con la versión más reciente de las clases, aunque podría haber sido construido con una versión anterior de la interfaz. Si esta meta interfaz se implementa en la clase compartida, siempre devuelve la versión más reciente. Sin embargo, al implementar el método como se IFoo.VERSION anteriormente, el número de versión de la interfaz está incrustado en el código del servidor (porque IFoo.VERSION es un static final int que está en línea cuando se hace referencia) y, por lo tanto, el método puede devolver la versión exacta en la que se construyó el servidor. con.

Tratar con interfaces más antiguas

Es posible que un cliente se actualice con la versión más reciente de una interfaz AIDL pero el servidor esté usando la interfaz AIDL anterior. En tales casos, llamar a un método en una interfaz antigua devuelve UNKNOWN_TRANSACTION .

Con AIDL estable, los clientes tienen más control. En el lado del cliente, puede establecer una implementación predeterminada para una interfaz AIDL. Un método en la implementación predeterminada se invoca solo cuando el método no se implementa en el lado remoto (porque se creó con una versión anterior de la interfaz). Dado que los valores predeterminados se establecen globalmente, no deben utilizarse en contextos potencialmente compartidos.

Ejemplo en C ++:

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

// once per an interface in a process
IFoo::setDefaultImpl(std::unique_ptr<IFoo>(MyDefault));

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 proporcione la implementación predeterminada de todos los métodos en una interfaz AIDL. Los métodos que se garantiza que se implementarán en el lado remoto (porque está seguro de que el control remoto se construyó cuando los métodos estaban en la descripción de la interfaz AIDL) no necesitan ser reemplazados en la clase impl predeterminada.

Conversión de AIDL existente en AIDL estructurado / estable

Si tiene una interfaz AIDL existente y un código que la usa, siga los siguientes pasos para convertir la interfaz en una interfaz AIDL estable.

  1. Identifique todas las dependencias de su interfaz. Para cada paquete del que depende la interfaz, determine si el paquete está definido en AIDL estable. Si no está definido, el paquete debe convertirse.

  2. Convierta todos los parcelables en su interfaz en parcelables estables (los archivos de la interfaz en sí pueden permanecer sin cambios). Haga esto expresando su estructura directamente en archivos AIDL. Las clases de gestión deben reescribirse para utilizar estos nuevos tipos. Esto se puede hacer antes de crear un paquete aidl_interface (abajo).

  3. Cree un paquete aidl_interface (como se describe arriba) que contenga el nombre de su módulo, sus dependencias y cualquier otra información que necesite. Para estabilizarlo (no solo estructurarlo), también necesita ser versionado. Para obtener más información, consulte Control de versiones de interfaces .