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

Comprobaciones de Dexpreopt y <uses-library>

Android tiene 12 cambios del sistema de generación para AOT compilación de archivos DEX (dexpreopt) para los módulos de Java que tienen <uses-library> dependencias. En algunos casos, estos cambios en el sistema de compilación pueden romper las compilaciones. Utilice esta página para prepararse para las roturas y siga las recetas de esta página para solucionarlas y mitigarlas.

Dexpreopt es el proceso de compilación anticipada de bibliotecas y aplicaciones Java. Dexpreopt sucede en-anfitrión del tiempo de construcción (en contraposición a dexopt, lo que ocurre en el dispositivo). La estructura de las dependencias de bibliotecas compartidas utilizadas por un módulo de Java (una biblioteca o una aplicación) se conoce como su contexto cargador de clases (CLC). Para garantizar la corrección de dexpreopt, los CLC en tiempo de compilación y en tiempo de ejecución deben coincidir. CLC en tiempo de compilación es lo que usa el compilador dex2oat en tiempo dexpreopt (se registra en los archivos ODEX), y CLC en tiempo de ejecución es el contexto en el que se carga el código precompilado en el dispositivo.

Estos CLC en tiempo de compilación y en tiempo de ejecución deben coincidir por razones tanto de corrección como de rendimiento. Para que sea correcto, es necesario manejar clases duplicadas. Si las dependencias de la biblioteca compartida en tiempo de ejecución son diferentes a las que se usan para la compilación, algunas de las clases pueden resolverse de manera diferente, provocando errores sutiles en el tiempo de ejecución. El rendimiento también se ve afectado por las comprobaciones en tiempo de ejecución de clases duplicadas.

Casos de uso afectados

El primer arranque es el caso de uso principal que se ve afectado por estos cambios: si ART detecta una discrepancia entre los CLC en tiempo de compilación y en tiempo de ejecución, rechaza los artefactos dexpreopt y ejecuta dexopt en su lugar. Para arranques posteriores, esto está bien porque las aplicaciones pueden eliminarse en segundo plano y almacenarse en el disco.

Áreas afectadas de Android

Esto afecta a todas las aplicaciones y bibliotecas de Java que tienen dependencias de tiempo de ejecución en otras bibliotecas de Java. Android tiene miles de aplicaciones y cientos de ellas usan bibliotecas compartidas. Los socios también se ven afectados, ya que tienen sus propias bibliotecas y aplicaciones.

Rompiendo cambios

El sistema de construcción necesita saber <uses-library> dependencias antes de que genera reglas de generación dexpreopt. Sin embargo, no se puede acceder directamente el manifiesto y leer el <uses-library> etiquetas en él, debido a que el sistema de construcción no se le permite leer archivos arbitrarios cuando se genera reglas de generación (por razones de rendimiento). Además, el manifiesto puede estar empaquetado dentro de un APK o un archivo. Por lo tanto, el <uses-library> información debe estar presente en los ficheros de construcción ( Android.bp o Android.mk ).

Anteriormente ART utiliza una solución que ignoraba compartida dependencias de bibliotecas (conocido como el &-classpath ). Esto no era seguro y provocó errores sutiles, por lo que la solución se eliminó en Android 12.

Como resultado, los módulos de Java que no proporcionan correcta <uses-library> información en sus ficheros de construcción puede causar roturas de construcción (causada por un desajuste CLC-tiempo de construcción) o de primer arranque regresiones de tiempo (causada por un CLC-tiempo de arranque desajuste seguido de dexopt).

Ruta de migración

Siga estos pasos para reparar una compilación defectuosa:

  1. Deshabilite globalmente la verificación de tiempo de compilación para un producto en particular configurando

    PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true

    en el archivo MAKE del producto. Esto corrige errores de compilación (a excepción de casos especiales, que figuran en la rotura de la fijación de la sección). Sin embargo, esta es una solución temporal y puede causar una falta de coincidencia de CLC en el tiempo de arranque seguida de dexopt.

  2. Fijar los módulos que falló antes de que deshabilita globalmente el registro de entrada, tiempo de construcción mediante la adición de la necesaria <uses-library> información a sus ficheros de construcción (véase la fijación de roturas para más detalles). Para la mayoría de los módulos para ello es necesario la adición de unas pocas líneas en Android.bp , o en Android.mk .

  3. Deshabilite la verificación en tiempo de compilación y dexpreopt para los casos problemáticos, por módulo. Deshabilite dexpreopt para no perder tiempo de compilación y almacenamiento en artefactos que se rechazan en el arranque.

  4. A nivel mundial volver a habilitar el registro de entrada, tiempo de construcción por desarmado PRODUCT_BROKEN_VERIFY_USES_LIBRARIES que se estableció en el Paso 1; la compilación no debería fallar después de este cambio (debido a los pasos 2 y 3).

  5. Fijar los módulos que se desactivó en el paso 3, uno a la vez, a continuación, volver a habilitar dexpreopt y el <uses-library> cheque. Archiva errores si es necesario.

La acumulación de tiempo <uses-library> controles se aplican en Android 12.

Reparación de roturas

Las siguientes secciones le explican cómo reparar tipos específicos de roturas.

Error de compilación: discordancia de CLC

El sistema de construcción hace una comprobación de coherencia en tiempo de compilación entre la información de Android.bp o Android.mk archivos y el manifiesto. El sistema de construcción no puede leer el manifiesto, pero puede generar reglas de generación para leer el manifiesto (extrayéndolo de un APK si es necesario), y compara <uses-library> etiquetas en el manifiesto en contra de la <uses-library> información de los archivos de compilación. Si la verificación falla, el error se ve así:

error: mismatch in the <uses-library> tags between the build system and the manifest:
    - required libraries in build system: []
                     vs. in the manifest: [org.apache.http.legacy]
    - optional libraries in build system: []
                     vs. in the manifest: [com.x.y.z]
    - tags in the manifest (.../X_intermediates/manifest/AndroidManifest.xml):
        <uses-library android:name="com.x.y.z"/>
        <uses-library android:name="org.apache.http.legacy"/>

note: the following options are available:
    - to temporarily disable the check on command line, rebuild with RELAX_USES_LIBRARY_CHECK=true (this will set compiler filter "verify" and disable AOT-compilation in dexpreopt)
    - to temporarily disable the check for the whole product, set PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true in the product makefiles
    - to fix the check, make build system properties coherent with the manifest
    - see build/make/Changes.md for details

Como sugiere el mensaje de error, existen múltiples soluciones, según la urgencia:

  • Para una solución temporal a todo el producto, establecer PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true en el makefile producto. La verificación de coherencia en el tiempo de compilación aún se realiza, pero una falla de verificación no significa una falla de compilación. En cambio, un fallo de la prueba hace que el sistema de construcción rebaja el filtro dex2oat compilador para verify en dexpreopt, que desactiva AOT-compilación completo para este módulo.
  • Para una rápida, solución global de la línea de comandos, utilice la variable de entorno RELAX_USES_LIBRARY_CHECK=true . Tiene el mismo efecto que hace PRODUCT_BROKEN_VERIFY_USES_LIBRARIES , pero diseñado para su uso en la línea de comandos. La variable de entorno anula la variable de producto.
  • Para una solución a la causa raíz corregir el error, hacer que el sistema de construcción consciente de los <uses-library> etiquetas en el manifiesto. Una inspección de la muestra un mensaje de error que causa el problema bibliotecas (como lo hace la inspección AndroidManifest.xml o el interior manifiesta de un APK que se puede comprobar con ` aapt dump badging $APK | grep uses-library `).

Para Android.bp módulos:

  1. Buscar la biblioteca que falta en el libs propiedad del módulo. Si está allí, Soong normalmente agrega dichas bibliotecas automáticamente, excepto en estos casos especiales:

    • La biblioteca no es una biblioteca de SDK (se define como java_library en lugar de java_sdk_library ).
    • La biblioteca tiene un nombre de biblioteca diferente (en el manifiesto) de su nombre de módulo (en el sistema de compilación).

    Para solucionar este problema temporalmente, añadir provides_uses_lib: "<library-name>" en el Android.bp definición de biblioteca. Para una solución a largo plazo, solucione el problema subyacente: convierta la biblioteca en una biblioteca SDK o cambie el nombre de su módulo.

  2. Si el paso anterior no proporcionó una resolución agregue uses_libs: ["<library-module-name>"] para las bibliotecas necesarias, o optional_uses_libs: ["<library-module-name>"] para las bibliotecas facultativos de la Android.bp definición del módulo. Estas propiedades aceptan una lista de nombres de módulos. El orden relativo de las bibliotecas en la lista debe ser el mismo que el orden en el manifiesto.

Para Android.mk módulos:

  1. Compruebe si la biblioteca tiene un nombre de biblioteca diferente (en el manifiesto) de su nombre de módulo (en el sistema de compilación). Si lo hace, solucionar este problema temporalmente mediante la adición de LOCAL_PROVIDES_USES_LIBRARY := <library-name> en el Android.mk archivo de la biblioteca, o añadir provides_uses_lib: "<library-name>" en el Android.bp archivo de la biblioteca (ambos casos son posibles, ya que un Android.mk módulo podría depender de un Android.bp biblioteca). Para una solución a largo plazo, solucione el problema subyacente: cambie el nombre del módulo de la biblioteca.

  2. Añadir LOCAL_USES_LIBRARIES := <library-module-name> para las bibliotecas requeridas; añadir LOCAL_OPTIONAL_USES_LIBRARIES := <library-module-name> para las bibliotecas facultativos de la Android.mk definición del módulo. Estas propiedades aceptan una lista de nombres de módulos. El orden relativo de las bibliotecas en la lista debe ser el mismo que en el manifiesto.

Error de compilación: ruta de biblioteca desconocida

Si el sistema de construcción no puede encontrar una ruta a un <uses-library> DEX frasco (ya sea un camino-tiempo de construcción en el host o una ruta de instalación en el dispositivo), que por lo general no pasa la acumulación. Si no se encuentra una ruta, puede indicar que la biblioteca está configurada de alguna manera inesperada. Arregle temporalmente la compilación desactivando dexpreopt para el módulo problemático.

Android.bp (propiedades del módulo):

enforce_uses_libs: false,
dex_preopt: {
    enabled: false,
},

Android.mk (variables del módulo):

LOCAL_ENFORCE_USES_LIBRARIES := false
LOCAL_DEX_PREOPT := false

Presenta un error para investigar los escenarios no admitidos.

Error de compilación: falta la dependencia de la biblioteca

Un intento de añadir <uses-library> X desde el manifiesto del módulo de Y para el fichero de construcción para Y podría dar lugar a un error de generación debido a la falta de dependencia, X.

Este es un mensaje de error de muestra para los módulos de Android.bp:

"Y" depends on undefined module "X"

Este es un mensaje de error de muestra para los módulos Android.mk:

'.../JAVA_LIBRARIES/com.android.X_intermediates/dexpreopt.config', needed by '.../APPS/Y_intermediates/enforce_uses_libraries.status', missing and no known rule to make it

Una fuente común de tales errores es cuando una biblioteca recibe un nombre diferente al que se denomina su módulo correspondiente en el sistema de compilación. Por ejemplo, si el manifiesto <uses-library> entrada es com.android.X , pero el nombre del módulo de la biblioteca es sólo X , se produce un error. Para resolver este caso, indique al sistema de construcción que el módulo denominado X proporciona un <uses-library> llamado com.android.X .

Este es un ejemplo para Android.bp bibliotecas (propiedad) del módulo:

provides_uses_lib: “com.android.X”,

Este es un ejemplo de bibliotecas Android.mk (variable de módulo):

LOCAL_PROVIDES_USES_LIBRARY := com.android.X

Discrepancia de CLC en el tiempo de arranque

En el primer arranque, busque logcat para mensajes relacionados con la discrepancia de CLC, como se muestra a continuación:

$ adb wait-for-device && adb logcat \
  | grep -E 'ClassLoaderContext [a-z ]+ mismatch' -A1

La salida puede tener mensajes de la forma que se muestra aquí:

[...] W system_server: ClassLoaderContext shared library size mismatch Expected=..., found=... (PCL[]... | PCL[]...)
[...] I PackageDexOptimizer: Running dexopt (dexoptNeeded=1) on: ...

Si recibe una advertencia de desajuste de CLC, busque un comando dexopt para el módulo defectuoso. Para solucionarlo, asegúrese de que se apruebe la verificación de tiempo de compilación del módulo. Si eso no funciona, entonces el suyo podría ser un caso especial que no es compatible con el sistema de compilación (como una aplicación que carga otro APK, no una biblioteca). El sistema de compilación no maneja todos los casos, porque en el momento de la compilación es imposible saber con certeza qué carga la aplicación en tiempo de ejecución.

Contexto del cargador de clases

El CLC es una estructura en forma de árbol que describe la jerarquía del cargador de clases. El sistema de construcción utiliza CLC en un sentido restringido (sólo abarca las bibliotecas, no aPKs o cargadores de clase personalizada): se trata de un árbol de bibliotecas que representa el cierre transitivo de todo el <uses-library> dependencias de una biblioteca o aplicación. Los elementos de nivel superior de un CLC son la directa <uses-library> dependencias se especifica en el manifiesto (la ruta de clase). Cada nodo de un árbol CLC es un <uses-library> nodo que podría tener su propio <uses-library> sub-nodos.

Debido <uses-library> dependencias se un gráfico acíclico dirigido, y no necesariamente un árbol, CLC pueden contener varias subárboles para la misma biblioteca. En otras palabras, CLC es el gráfico de dependencia "desplegado" en un árbol. La duplicación es solo en un nivel lógico; los cargadores de clases subyacentes reales no están duplicados (en tiempo de ejecución hay una única instancia de cargador de clases para cada biblioteca).

CLC define el orden de búsqueda de las bibliotecas al resolver las clases de Java utilizadas por la biblioteca o la aplicación. El orden de búsqueda es importante porque las bibliotecas pueden contener clases duplicadas y la clase se resuelve en la primera coincidencia.

En el dispositivo (tiempo de ejecución) CLC

PackageManager (en frameworks/base ) crea una CLC para cargar un módulo de Java en el dispositivo. Añade las bibliotecas enumeradas en el <uses-library> etiquetas en el manifiesto como elementos CLC de alto nivel del módulo.

Para cada biblioteca usada, PackageManager recibe toda su <uses-library> dependencias (especificados como etiquetas en el manifiesto de esa biblioteca) y añade un CRC de anidado para cada dependencia. Este proceso continúa de forma recursiva hasta que todos los nodos de hoja del árbol CLC construido son bibliotecas sin <uses-library> dependencias.

PackageManager sólo es consciente de las bibliotecas compartidas. La definición de compartido en este uso difiere de su significado habitual (como compartido frente a estático). En Android, Java bibliotecas compartidas son los enumerados en configuraciones XML que se instala en el dispositivo ( /system/etc/permissions/platform.xml ). Cada entrada contiene el nombre de una biblioteca compartida, una ruta a su archivo jar DEX, y una lista de dependencias (otras bibliotecas compartidas que éste usos en tiempo de ejecución, y especifica en <uses-library> etiquetas en su manifiesto).

En otras palabras, hay dos fuentes de información que permiten PackageManager para la construcción de CLC en tiempo de ejecución: <uses-library> etiquetas en el manifiesto, y dependencias de bibliotecas compartidas en configuraciones XML.

CLC en el host (tiempo de compilación)

CLC no solo se necesita al cargar una biblioteca o una aplicación, también se necesita al compilar una. La compilación puede ocurrir en el dispositivo (dexopt) o durante la compilación (dexpreopt). Desde dexopt se lleva a cabo en el dispositivo, que tiene la misma información que PackageManager (manifiestos y dependencias de bibliotecas compartidas). Dexpreopt, sin embargo, se lleva a cabo en el host y en un entorno totalmente diferente, y debe obtener la misma información del sistema de compilación.

Por lo tanto, la acumulación de tiempo utilizado por CLC dexpreopt y el tiempo de ejecución utilizado por CLC PackageManager son la misma cosa, pero calcula de dos maneras diferentes.

En tiempo de compilación y en tiempo de ejecución CLC deben coincidir, de lo contrario el código AOT-compilado creado por dexpreopt es rechazado. Para comprobar la igualdad de los CAC en tiempo de compilación y en tiempo de ejecución, el compilador registros dex2oat-tiempo de construcción CLC en los *.odex archivos (en la classpath de campo de la cabecera del archivo OAT). Para encontrar el CLC almacenado, use este comando:

oatdump --oat-file=<FILE> | grep '^classpath = '

La discrepancia de CLC en tiempo de compilación y en tiempo de ejecución se informa en logcat durante el arranque. Búscalo con este comando:

logcat | grep -E 'ClassLoaderContext [az ]+ mismatch'

La falta de coincidencia es perjudicial para el rendimiento, ya que obliga a que la biblioteca o la aplicación se elimine o se ejecute sin optimizaciones (por ejemplo, es posible que el código de la aplicación deba extraerse en la memoria del APK, una operación muy costosa).

Una biblioteca compartida puede ser opcional o obligatoria. Desde el punto de vista de dexpreopt, una biblioteca requerida debe estar presente en el momento de la compilación (su ausencia es un error de compilación). Una biblioteca opcional puede estar presente o ausente en el tiempo de construcción: si está presente, se añade a la CLC, pasó a dex2oat, y se registra en el *.odex archivo. Si no hay una biblioteca opcional, se omite y no se agrega al CLC. Si hay una discrepancia entre el tiempo de compilación y el estado de tiempo de ejecución (la biblioteca opcional está presente en un caso, pero no en el otro), entonces los CLC de tiempo de compilación y tiempo de ejecución no coinciden y el código compilado se rechaza.

Detalles avanzados del sistema de compilación (reparador de manifiestos)

A veces <uses-library> etiquetas no están en el manifiesto fuente de una biblioteca o aplicación. Esto puede ocurrir, por ejemplo, si una de las dependencias transitivas de la biblioteca o aplicación se inicia utilizando otro <uses-library> etiqueta, y la biblioteca o del manifiesto de la aplicación no se actualiza para incluirlo.

Soong puede calcular algunos de los desaparecidos <uses-library> etiquetas para una biblioteca o una aplicación dada de forma automática, como las bibliotecas de SDK en el cierre dependencia transitiva de la biblioteca o aplicación. El cierre es necesario porque la biblioteca (o aplicación) puede depender de una biblioteca estática que depende de una biblioteca SDK, y posiblemente podría depender de nuevo de manera transitiva a través de otra biblioteca.

No todos <uses-library> etiquetas se pueden calcular de esta manera, pero siempre que sea posible, es prefereable dejar Soong añadir entradas se manifiestan de forma automática; es menos propenso a errores y simplifica el mantenimiento. Por ejemplo, cuando muchas de las aplicaciones utilizan una biblioteca estática que añade una nueva <uses-library> dependencia, todas las aplicaciones debe ser actualizado, que es difícil de mantener.