Des modifications ont été apportées au système de compilation d'Android 12 pour la compilation anticipée
de fichiers DEX (dexpreopt) pour les modules Java comportant <uses-library>
les dépendances. Dans certains cas, ces modifications du système de compilation
compilations. Utilisez cette page pour préparer les défaillances et suivez les recettes indiquées
pour les corriger et les atténuer.
Dexpreopt est le processus de compilation à l'avance des bibliothèques et applications. Dexpreopt se produit sur l'hôte au moment de la compilation (contrairement à dexopt, qui se produit sur l'appareil). Structure des dépendances de bibliothèque partagée utilisées par un environnement (bibliothèque ou application) est appelé contexte du chargeur de classe (CLC). Pour garantir la validité de dexpreopt, les CLC au moment de la compilation et de l'exécution doivent coïncider. Le CLC au moment de la compilation est ce que le compilateur dex2oat utilise au moment de la compilation dexpreopt (il est enregistré dans les fichiers ODEX), et le CLC au moment de l'exécution est le contexte dans lequel le code précompilé est chargé sur l'appareil.
Ces CLC de compilation et d'exécution doivent coïncider pour des raisons d'exactitude et de performances. Pour des raisons de correction, il est nécessaire de gérer les classes en double. Si les dépendances de la bibliothèque partagée lors de l'exécution sont différentes de celles utilisées pour de compilation, certaines classes peuvent être résolues différemment, entraînant de légères les bugs d'exécution. Les performances sont également affectées par la vérification de l'absence de doublons classes.
Cas d'utilisation concernés
Le premier démarrage est le principal cas d'utilisation affecté par ces modifications: si ART détecte une incohérence entre les CLC au moment de la compilation et de l'exécution, il rejette dexpreopt des artefacts et exécute dexopt à la place. Pour les démarrages ultérieurs, cela ne pose pas de problème, car les applications peuvent être désoptées en arrière-plan et stockées sur le disque.
Zones d'Android concernées
Cela affecte toutes les applications et bibliothèques Java qui ont des dépendances d'exécution sur dans d'autres bibliothèques Java. Android compte des milliers d'applications, dont des centaines utilisent des bibliothèques partagées. Les partenaires sont également concernés, bibliothèques et applications.
Interrompre les modifications
Le système de compilation doit connaître les dépendances <uses-library>
avant de
génère des règles de compilation dexpreopt. Toutefois, il ne peut pas accéder directement au fichier manifeste
et lisez les <uses-library>
tags, car le système de compilation n'est pas autorisé à lire des fichiers arbitraires lorsque
des règles de compilation sont alors générées (pour des raisons de performances). De plus, le fichier manifeste peut
être empaquetés dans un APK ou un fichier prédéfini. Par conséquent, le <uses-library>
les informations doivent être présentes dans les fichiers de compilation (Android.bp
ou Android.mk
).
Auparavant, ART utilisait un correctif qui ignorait les dépendances de bibliothèque partagée (appelées &-classpath
). Cette solution n'était pas sécurisée et causait des bugs subtils. Elle a donc été supprimée dans Android 12.
Par conséquent, les modules Java qui ne fournissent pas les bonnes <uses-library>
des informations contenues dans leurs fichiers de compilation peuvent causer des interruptions de compilation
non-concordance des cours au moment de la compilation) ou des régressions au premier démarrage (provoquées par une erreur
incohérence au niveau des commissions au niveau d'une catégorie et de dexopt).
Chemin de migration
Pour corriger un build défectueux, procédez comme suit:
Désactivez globalement la vérification de la durée de compilation pour un produit particulier en définissant
PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true
dans le fichier makefile du produit. Cela corrige les erreurs de compilation (sauf dans les cas particuliers listés dans la section Corriger les erreurs). Toutefois, il s'agit d'une solution temporaire qui peut entraîner un décalage CLC au démarrage, suivi d'une décompilation.
Corrigez les modules qui ont échoué avant d'avoir désactivé globalement la vérification au moment de la compilation en ajoutant les informations
<uses-library>
nécessaires à leurs fichiers de compilation (pour en savoir plus, consultez la section Corriger les erreurs). Pour la plupart des modules, cela nécessite d'ajouter quelques lignes dansAndroid.bp
, ou dansAndroid.mk
Désactivez la vérification au moment de la compilation et dexpreopt pour les cas problématiques, au niveau de chaque module. Désactivez dexpreopt pour ne pas gaspiller du temps de compilation et de l'espace de stockage sur des artefacts qui sont rejetés au démarrage.
Réactivez globalement la vérification de l'heure de compilation en désactivant
PRODUCT_BROKEN_VERIFY_USES_LIBRARIES
défini à l'étape 1 ; la compilation ne devrait pas échouer après cette modification (grâce aux étapes 2 et 3).Corrigez les modules que vous avez désactivés à l'étape 3, un par un, puis réactivez dexpreopt et la vérification
<uses-library>
. Signalez les bugs si nécessaire.
Les vérifications <uses-library>
au moment de la compilation sont appliquées dans Android 12.
Corriger les dysfonctionnements
Les sections suivantes vous expliquent comment corriger des types de pannes spécifiques.
Erreur de compilation: différence entre les commissions au niveau d'une catégorie
Le système de compilation effectue une vérification de cohérence au moment de la compilation entre les informations des fichiers Android.bp
ou Android.mk
et le fichier manifeste. Le système de compilation ne peut pas lire
le fichier manifeste, mais il peut générer des règles de compilation pour le lire (extraire
à partir d'un APK si nécessaire), et comparer les balises <uses-library>
dans le fichier manifeste
par rapport aux informations <uses-library>
des fichiers de compilation. En cas d'échec de la vérification, l'erreur se présente comme suit :
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
Comme le suggère le message d'erreur, il existe plusieurs solutions, en fonction de l'urgence :
- Pour une correction temporaire à l'échelle du produit, définissez
PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true
dans le fichier makefile du produit. La vérification de la cohérence au moment de la compilation est toujours effectuée, mais un échec de vérification ne signifie pas un échec de compilation. Au lieu de cela, un échec de vérification oblige le système de compilation à rétrograder le Filtre du compilateur dex2oat surverify
dans dexpreopt, ce qui désactive la compilation AOT dans le cadre de ce module. - Pour une correction globale rapide via la ligne de commande, utilisez la variable d'environnement
RELAX_USES_LIBRARY_CHECK=true
Il a le même effet quePRODUCT_BROKEN_VERIFY_USES_LIBRARIES
, mais est destiné à être utilisé sur la ligne de commande. La remplace la variable de produit. - Pour trouver une solution permettant de corriger l'erreur due à l'origine du problème, informez le système de compilation de
les balises
<uses-library>
dans le fichier manifeste. Une inspection du message d'erreur indique les bibliothèques à l'origine du problème (comme l'inspection deAndroidManifest.xml
ou du fichier manifeste dans un APK pouvant être vérifié avecaapt dump badging $APK | grep uses-library
).
Pour les modules Android.bp
:
Recherchez la bibliothèque manquante dans la propriété
libs
du module. Si c'est le cas là, Soong ajoute normalement ces bibliothèques automatiquement, sauf dans ces cas particuliers:- La bibliothèque n'est pas une bibliothèque de SDK (elle est définie comme
java_library
plutôt quejava_sdk_library
). - Le nom de bibliothèque (dans le fichier manifeste) de la bibliothèque est différent de celui de son module (dans le système de compilation).
Pour résoudre ce problème temporairement, ajoutez
provides_uses_lib: "<library-name>"
dans la définition de la bibliothèqueAndroid.bp
. Pour une solution à long terme, corrigez le problème problème: convertir la bibliothèque en bibliothèque SDK ou renommer son module.- La bibliothèque n'est pas une bibliothèque de SDK (elle est définie comme
Si l'étape précédente n'a pas permis de résoudre le problème, ajoutez
uses_libs: ["<library-module-name>"]
pour les bibliothèques requises ouoptional_uses_libs: ["<library-module-name>"]
pour les bibliothèques facultatives la définitionAndroid.bp
du module. Ces propriétés acceptent une liste de noms de modules. L'ordre relatif des bibliothèques sur la liste doit être le même comme celle indiquée dans le fichier manifeste.
Pour les modules Android.mk
:
Vérifiez si le nom de bibliothèque (dans le fichier manifeste) de la bibliothèque est différent de celui nom du module (dans le système de compilation). Si c'est le cas, corrigez-le temporairement en ajoutant
LOCAL_PROVIDES_USES_LIBRARY := <library-name>
dans le fichierAndroid.mk
de la bibliothèque ou en ajoutantprovides_uses_lib: "<library-name>"
dans le fichierAndroid.bp
de la bibliothèque (les deux cas sont possibles, car un moduleAndroid.mk
peut dépendre d'une bibliothèqueAndroid.bp
). Pour une solution à long terme, corrigez le problème sous-jacent : renommez le module de bibliothèque.Ajouter
LOCAL_USES_LIBRARIES := <library-module-name>
pour obligatoire les bibliothèques ; ajouterLOCAL_OPTIONAL_USES_LIBRARIES := <library-module-name>
pour des bibliothèques facultatives à la définitionAndroid.mk
du module. Ces acceptent une liste de noms de modules. L'ordre relatif des bibliothèques dans la liste doit être le même que dans le fichier manifeste.
Erreur de compilation : chemin d'accès à la bibliothèque inconnu
Si le système de compilation ne parvient pas à trouver un chemin d'accès à un fichier JAR DEX <uses-library>
(chemin d'accès au moment de la compilation sur l'hôte ou chemin d'installation sur l'appareil), la compilation échoue généralement. L'échec de la recherche d'un chemin peut indiquer que la bibliothèque est configurée dans
de manière inattendue. Corrigez temporairement le build en désactivant dexpreopt pour le problème qui pose problème.
de ce module.
Android.bp (propriétés du module) :
enforce_uses_libs: false,
dex_preopt: {
enabled: false,
},
Android.mk (variables de module) :
LOCAL_ENFORCE_USES_LIBRARIES := false
LOCAL_DEX_PREOPT := false
Envoyez un bug pour examiner les scénarios non pris en charge.
Erreur de compilation : dépendance de bibliothèque manquante
Une tentative d'ajout de <uses-library>
X à partir du fichier manifeste du module Y au fichier de compilation pour Y peut entraîner une erreur de compilation en raison de la dépendance manquante, X.
Voici un exemple de message d'erreur pour les modules Android.bp :
"Y" depends on undefined module "X"
Voici un exemple de message d'erreur pour les modules 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
Une source courante de ces erreurs se produit lorsqu'une bibliothèque est nommée différemment de son module correspondant dans le système de compilation. Par exemple, si le fichier manifeste
L'entrée de <uses-library>
est com.android.X
, mais le nom du module de bibliothèque est
uniquement X
, cela provoque une erreur. Pour résoudre ce problème, indiquez au système de compilation que le module nommé X
fournit un <uses-library>
nommé com.android.X
.
Voici un exemple pour les bibliothèques Android.bp
(propriété de module):
provides_uses_lib: “com.android.X”,
Voici un exemple pour les bibliothèques Android.mk (variable de module) :
LOCAL_PROVIDES_USES_LIBRARY := com.android.X
Incohérence du contrôleur en boucle fermée au démarrage
Au premier démarrage, recherchez dans logcat les messages liés à une non-concordance au niveau de la communauté, comme indiqué ci-dessous:
$ adb wait-for-device && adb logcat \
| grep -E 'ClassLoaderContext [a-z ]+ mismatch' -A1
La sortie peut contenir des messages de la forme suivante :
[...] W system_server: ClassLoaderContext shared library size mismatch Expected=..., found=... (PCL[]... | PCL[]...)
[...] I PackageDexOptimizer: Running dexopt (dexoptNeeded=1) on: ...
Si un avertissement de non-concordance CLC s'affiche, recherchez une commande dexopt pour le module défectueux. Pour résoudre ce problème, assurez-vous que la vérification de l'heure de la compilation du module réussit. Si cela ne fonctionne pas, il est possible que votre cas soit particulier et non pris en charge par le système de compilation (par exemple, une application qui charge un autre APK, et non une bibliothèque). La ne gère pas tous les cas, car au moment de la compilation, de savoir avec certitude ce que l'application charge au moment de l'exécution.
Contexte du chargeur de classe
Il s'agit d'une structure arborescente qui décrit la hiérarchie des chargeurs de classes. Le système de compilation utilise un CLC au sens strict (il ne couvre que les bibliothèques, pas les APK ni les chargeurs de classe personnalisés) : il s'agit d'un arbre de bibliothèques qui représente la fermeture transitive de toutes les dépendances <uses-library>
d'une bibliothèque ou d'une application. Les éléments de niveau supérieur d'un CLC sont les dépendances <uses-library>
directes spécifiées dans le fichier manifeste (le chemin d'accès aux classes). Chaque nœud d'un arbre CLC est un nœud <uses-library>
pouvant avoir ses propres sous-nœuds <uses-library>
.
Parce que les dépendances <uses-library>
sont un graphe orienté acyclique, et non
n'est pas nécessairement une arborescence, les commissions au niveau d'une catégorie peuvent contenir plusieurs sous-arborescences pour une même bibliothèque. Dans
Autrement dit, le graphique des dépendances est "déplié" à un arbre. La duplication
se situe uniquement à un niveau logique ; les chargeurs de classe sous-jacents
dupliqué (au moment de l'exécution, chaque bibliothèque ne compte qu'une seule instance de chargeur de classe).
Le CLC définit l'ordre de recherche des bibliothèques lors de la résolution des classes Java utilisées par la bibliothèque ou l'application. L'ordre de recherche est important, car les bibliothèques peuvent contenir des classes en double, et la classe est résolue en fonction de la première correspondance.
CLC sur l'appareil (au moment de l'exécution)
PackageManager
(dans frameworks/base
) crée une commissions au niveau d'une catégorie pour charger un module Java.
sur l'appareil. Il ajoute les bibliothèques répertoriées dans les balises <uses-library>
du fichier
se manifestent en tant qu'éléments
de niveau supérieur de commissions au niveau d'une catégorie.
Pour chaque bibliothèque utilisée, PackageManager
obtient tous ses <uses-library>
(spécifiées sous forme de balises dans le fichier manifeste de cette bibliothèque) et ajoute
des commissions au niveau d'une catégorie imbriquées
pour chaque dépendance. Ce processus se poursuit de manière récursive jusqu'à
Les nœuds feuilles de l'arborescence de commissions au niveau d'une catégorie construite sont des bibliothèques sans <uses-library>
les dépendances.
PackageManager
n'a connaissance que des bibliothèques partagées. La définition de "partagé" dans cet usage diffère de son sens habituel (par exemple, partagé par rapport à statique). Sous Android, les bibliothèques partagées Java sont celles listées dans les configurations XML installées sur l'appareil (/system/etc/permissions/platform.xml
). Chaque entrée contient le nom d'une bibliothèque partagée, un chemin d'accès à son fichier JAR DEX et une liste de dépendances (autres bibliothèques partagées que celle-ci utilise au moment de l'exécution et spécifiées dans les balises <uses-library>
de son fichier manifeste).
En d'autres termes, deux sources d'informations permettent à PackageManager
de créer des CLC au moment de l'exécution : les balises <uses-library>
dans le fichier manifeste et les dépendances de bibliothèque partagées dans les configurations XML.
CLC sur l'hôte (au moment de la compilation)
Les commissions au niveau d'une catégorie ne s'appliquent pas qu'au chargement d'une bibliothèque ou d'une application.
en compilant un. La compilation peut se produire sur l'appareil (dexopt) ou lors de la compilation (dexpreopt). Puisque dexopt s'exécute sur l'appareil, il a le même
les informations sous forme de PackageManager
(fichiers manifestes et dépendances de bibliothèque partagée).
Dexpreopt, cependant, se déroule sur l'hôte et dans un environnement totalement différent. Il doit obtenir les mêmes informations auprès du système de compilation.
Par conséquent, le CLC au moment de la compilation utilisé par dexpreopt et le CLC au moment de l'exécution utilisé par PackageManager
sont la même chose, mais calculés de deux manières différentes.
Les commissions au niveau d'une catégorie au moment de la compilation et de l'exécution doivent coïncider, sinon le code compilé par AOT
créées par dexpreopt sont refusées. Pour vérifier l'égalité des temps de compilation
CLC au moment de l'exécution, le compilateur dex2oat enregistre les CLC au moment de la compilation dans les fichiers *.odex
(dans le champ classpath
de l'en-tête du fichier OAT). Pour trouver les commissions au niveau d'une catégorie que vous avez stockées, utilisez
cette commande:
oatdump --oat-file=<FILE> | grep '^classpath = '
Un décalage entre le CLC au moment de la compilation et au moment de l'exécution est signalé dans logcat au démarrage. Réseau de Recherche à l'aide de la commande suivante:
logcat | grep -E 'ClassLoaderContext [a-z ]+ mismatch'
Les différences sont mauvaises pour les performances, car elles obligent la bibliothèque ou l'application à être déxoptée ou à s'exécuter sans optimisations (par exemple, le code de l'application peut devoir être extrait en mémoire de l'APK, une opération très coûteuse).
Une bibliothèque partagée peut être facultative ou obligatoire. De
Du point de vue de dexpreopt, une bibliothèque requise doit être présente au moment de la compilation (son
l'absence est une erreur de compilation). Une bibliothèque facultative peut être présente ou absente
au moment de la compilation: s'il est présent, il est ajouté à la CLC, transmis à dex2oat et
enregistré dans le fichier *.odex
. Si une bibliothèque facultative n'est pas présente, elle est ignorée.
et n'est pas ajoutée à la commission. En cas de non-concordance entre l'heure de compilation
l'état d'exécution (la bibliothèque facultative est présente dans un cas, mais pas dans l'autre) ;
alors les cours au moment de la compilation
et de l'exécution ne correspondent pas, et le code compilé
refusé.
Détails du système de compilation avancé (outil de correction de fichiers manifestes)
Parfois, les balises <uses-library>
ne figurent pas dans le fichier manifeste source d'une
bibliothèque ou application. Cela peut se produire, par exemple, si l'une des dépendances transitives
de la bibliothèque ou de l'application commence à utiliser une autre balise <uses-library>
, et la
le fichier manifeste de la bibliothèque ou
de l'application n'est pas mis à jour pour l'inclure.
Soong peut calculer automatiquement certaines des balises <uses-library>
manquantes pour une bibliothèque ou une application donnée, comme les bibliothèques de SDK dans la fermeture de dépendance transitive de la bibliothèque ou de l'application. La fermeture est nécessaire, car la bibliothèque (ou l'application) peut dépendre d'une bibliothèque statique qui dépend d'une bibliothèque de SDK, et peut-être à nouveau dépendre de manière transitive via une autre bibliothèque.
Toutes les balises <uses-library>
ne peuvent pas être calculées de cette manière, mais dans la mesure du possible, il est préférable de laisser Soong ajouter automatiquement des entrées de fichier manifeste. Cela réduit le risque d'erreurs et simplifie la maintenance. Par exemple, lorsque de nombreuses applications utilisent
bibliothèque qui ajoute une nouvelle dépendance <uses-library>
, toutes les applications doivent être
mis à jour, ce qui est difficile à gérer.