Google se compromete a impulsar la igualdad racial para las comunidades afrodescendientes. Obtén información al respecto.

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 construcció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 se 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 identi can única una interfaz AIDL.
  • srcs : La lista de los archivos de origen AIDL que componen la interfaz. El camino para un tipo de AIDL Foo definido en un paquete com.acme debe ser de al <base_path>/com/acme/Foo.aidl , donde <base_path> podría ser cualquier directorio relativo al directorio donde Android.bp es. En el ejemplo anterior, <base_path> es decir srcs/aidl .
  • local_include_dir : El camino de donde el nombre del paquete comienza. Corresponde a <base_path> explicó anteriormente.
  • imports : Una lista de aidl_interface módulos que este usos. Si uno de sus interfaces de AIDL utiliza una interfaz o un parcelable de otro aidl_interface , ponga su nombre aquí. Esto puede ser el nombre por sí mismo, para referirse a la última versión, o el nombre con el sufijo versión (como -V1 ) para referirse a una versión específica. La especificación de una versión es compatible desde Android 12
  • versions : Las versiones anteriores de la interfaz que se congela bajo api_dir , a partir de Android 11, las versions son congelado 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 estabilidad de esta interfase. Actualmente sólo 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 está ajustado a "vintf" , esto corresponde a una promesa de estabilidad: la interfaz debe mantenerse estable mientras se utiliza.
  • gen_trace : La bandera opcional para encender el trazado de encendido o apagado. Por defecto es false .
  • host_supported : La bandera opcional que cuando se establece en true marcas las bibliotecas generadas disponibles para el entorno de acogida.
  • unstable : La bandera opcional que se utiliza para marcar que esta interfaz no tiene que ser estable. Cuando se establece en true , el sistema de construcción no crea el volcado de API para la interfaz ni requiere que se actualiza.
  • backend.<type>.enabled : Estas banderas alternar cada uno de los backends que el compilador AIDL va a generar código para. Actualmente, tres motores son soportados: 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 de APEX que la biblioteca de código auxiliar generado está disponible para.
  • backend.[cpp|java].gen_log : La bandera opcional que controla si para generar código adicional para recopilar información sobre la transacción.
  • backend.[cpp|java].vndk.enabled : La bandera opcional para hacer esta interfaz una parte de VNDK. Por defecto es false .
  • backend.java.platform_apis : La bandera opcional que controla si la biblioteca de Java de código auxiliar está construido en la API privadas de la plataforma. Esto se debe establecer en "true" cuando stability se establece en "vintf" .
  • backend.java.sdk_version : La bandera opcional para especificar la versión del SDK de esa biblioteca de Java de código auxiliar se construye en contra. El valor por defecto es "system_current" . Esto no debería ser establecido cuando backend.java.platform_apis es cierto.
  • backend.java.platform_apis : La bandera opcional que debe ser ajustado en true cuando las bibliotecas generadas necesidad de construir en contra de 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ódigo auxiliar. Ver Módulo reglas de denominación de cómo hacer referencia a la versión específica de la biblioteca de código auxiliar de un motor 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, parcelables fueron declaradas hacia adelante; 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;
}

Una forma predeterminada se admite actualmente (aunque no es obligatorio) 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. Estos son ejemplos de bibliotecas de código auxiliar en el sistema de construcción ( Android.mk también se puede utilizar para las definiciones del módulo de legado):

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 objetivo en el sistema de construcción que se puede utilizar para administrar la API del módulo. Cuando se construyó, foo-freeze-api añade una nueva definición de la API, en api_dir o aidl_api/ name , dependiendo de la versión de Android, y añade un .hash archivo, ambos en representación de la nueva versión congelada de la interfaz. La construcción de este también actualiza la versions de propiedad para reflejar la versión adicional. Una vez que la versions se especifica la propiedad, el sistema de construcción ejecuta comprobaciones de compatibilidad entre versiones congeladas y también entre la parte superior del árbol (CpC) y la última versión congelada.

Además, debe administrar la definición de API de la versión ToT. Siempre que una API se actualiza, ejecutar fu-update-API para la actualización aidl_api/ name /current que contiene la definición de la API de CpC versión.

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 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).

Usando interfaces versionadas

Métodos de interfaz

En tiempo de ejecución, cuando se trata de llamar a nuevos métodos en un servidor viejo, nuevos clientes obtienen automáticamente UNKNOWN_TRANSACTION . Para que las estrategias para manejar esto ver la consulta de versiones y utilizando los valores predeterminados .

Parcelables

Cuando se agregan nuevos campos a parcelables, los clientes y servidores antiguos los eliminan. Cuando los nuevos clientes y servidores reciben parcelables antiguos, los valores predeterminados para los nuevos campos se completan automáticamente. Esto significa que los valores predeterminados deben especificarse para todos los campos nuevos en un parcelable.

Los clientes no deben esperar que los servidores para utilizar los nuevos campos a menos que sepan que el servidor está ejecutando la versión que tiene el campo definido (ver versiones que consultan ).

Enums y constantes

De manera similar, los clientes y 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. Por ejemplo, un servidor no debe abortar cuando recibe un enumerador que no conoce. Debería ignorarlo o devolver algo para que el cliente sepa que no es compatible con esta implementación.

Sindicatos

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 nuevo campo. La falla 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 Java). El error se recibe si el cliente envía un conjunto de unión al nuevo campo a un servidor antiguo, o cuando es un cliente antiguo que recibe la unión de un servidor nuevo.

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 código auxiliar específico para la vinculación, no utilice el nombre de la aidl_interface módulo, pero el nombre del módulo de biblioteca de código auxiliar, que es ifacename - version - backend , donde

  • ifacename : nombre del aidl_interface módulo
  • version es cualquiera de
    • V version-number para las versiones congeladas
    • V latest-frozen-version-number + 1 para la versión de punta-de-árbol (todavía-a-ser-congelado)
  • backend es cualquiera de
    • java para el backend de Java,
    • cpp para backend el C ++,
    • ndk o ndk_platform para el backend NDK. El primero es para aplicaciones y el segundo es para uso de plataforma.

Supongamos que hay un módulo con el nombre foo y su última versión es 2, y es compatible tanto 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 se refiere a la última versión estable se convierte en foo- V2 - backend
  • foo-unstable- backend , que se refiere a la versión de CpC se convierte 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 CpC: foo-V3-(cpp|ndk|ndk_platform).so

Tenga en cuenta que el compilador AIDL no crea una unstable módulo de versión o un módulo no versionado 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 cpp backend:

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 la ndk (y el ndk_platform ) backend:

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 java backend:

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 deberá aplicar getInterfaceVersion() y getInterfaceHash() de la 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 las clases generadas ( IFoo , IFoo.Stub , etc.) son compartidas entre el cliente y el servidor (por ejemplo, las clases pueden estar en la ruta de clase 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, mediante la aplicación del método anterior, el número de versión de la interfaz está incrustado en el código del servidor (ya IFoo.VERSION es un static final int que se colocarán en línea cuando se hace referencia) y por lo tanto el método se puede devolver la versión exacta del servidor fue construido 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 un viejo vuelve interfaz 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 está implementado 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. Métodos que están garantizados para ser implementado en el lado remoto (porque está seguro de que el mando a distancia se construye cuando los métodos estaban en la descripción de la interfaz AIDL) no necesitan ser anulado por defecto en el impl clase.

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 una aidl_interface paquete (abajo).

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