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 desorganizan 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 en api_dir . A partir de Android 11, las versions están congeladas en 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 que se usa 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 stub. 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 S (AOSP experimental), 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 vacío o similar a 0. 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 código auxiliar en el sistema de compilación ( Android.mk también se puede utilizar 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

La declaración de 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 construye, 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. Construir 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, los enumeradores
  • En Android S (AOSP experimental), 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 el 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 la consulta de versiones y el uso de valores predeterminados . Cuando se agregan nuevos campos a parcelables, los clientes / servidores antiguos los eliminan. Cuando los 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 se pueden agregar 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 C ++ o NDK) o IllegalArgumentException (para el backend de Java).

Reglas de nomenclatura de módulos

En 11, para cada combinación de las versiones y los backends habilitados, se crea automáticamente un módulo de biblioteca de stub. Para hacer referencia a un módulo de biblioteca de aidl_interface auxiliar específico para la vinculación, no use el nombre del módulo aidl_interface , sino el nombre del módulo de biblioteca de ifacename auxiliar, 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
    • unstable para la versión de 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.

Para la última versión congelada, omita el campo de versión excepto el destino Java. En otras palabras, module-V latest-frozen-version -(cpp|ndk|ndk_platform) no se genera.

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-(java|cpp|ndk|ndk_platform)
    • foo-V2-java (el contenido es idéntico a foo-java)
  • Basado en la versión de ToT
    • foo-unstable-(java|cpp|ndk|ndk_platform)

En la mayoría de los casos, los nombres de los archivos de salida son los mismos que los de los módulos. Sin embargo, para la versión ToT de un módulo C ++ o NDK, el nombre del archivo de salida es diferente del nombre del módulo.

Por ejemplo, el nombre del archivo de salida de foo-unstable-cpp es foo-V3-cpp.so , no foo-unstable-cpp.so como se muestra a continuación.

  • 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

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 creó el servidor. con.

Tratar con interfaces 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 de forma global, 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 están garantizados para implementarse en el lado remoto (porque está seguro de que el control remoto está construido 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 .