O sistema de build do Android 12 muda para a compilação AOT
dos arquivos DEX (dexpreopt) para módulos Java que têm <uses-library>
dependências. Em alguns casos, essas mudanças no sistema
de build podem ser corrompidas
builds. Use esta página para se preparar para interrupções e siga as receitas nela
para corrigir e reduzir as interrupções.
O dexpreopt é o processo de compilação antecipada de bibliotecas Java e apps. O dexpreopt acontece no host durante a compilação (diferente do dexopt, que acontece no dispositivo). A estrutura das dependências de bibliotecas compartilhadas usadas por um (uma biblioteca ou app) é conhecido como seu contexto de carregador de classe (CLC, na sigla em inglês). Para garantir a correção do dexpreopt, os CLCs de build-time e run-time precisam coincidir. O CLC no build é o que o compilador dex2oat usa no momento dexpreopt (ele é registrado nos arquivos ODEX), e o CLC no tempo de execução é o contexto em que o código pré-compilado é carregado no dispositivo.
Esses CLCs de build e de execução precisam coincidir por motivos de correção e desempenho. Para correção, é necessário lidar com classes duplicadas. Se as dependências da biblioteca compartilhada no momento da execução forem diferentes daquelas usadas para a compilação, algumas das classes poderão ser resolvidas de maneira diferente, causando bugs sutis no momento da execução. O desempenho também é afetado pelas verificações de execução para classes duplicadas.
Casos de uso afetados
A primeira inicialização é o principal caso de uso afetado por essas mudanças: se o ART detectar uma incompatibilidade entre CLCs de build-time e run-time, ele rejeitará artefatos dexpreopt e executará dexopt. Isso não é um problema para inicializações subsequentes, porque os apps podem ser desativados em segundo plano e armazenados no disco.
Áreas afetadas do Android
Isso afeta todos os apps e bibliotecas Java que têm dependências de ambiente de execução outras bibliotecas Java. O Android tem milhares de apps, e centenas deles usam bibliotecas compartilhadas. Os parceiros também são afetados, pois têm suas próprias funções bibliotecas e apps do Google.
Alterações de intervalo
O sistema de build precisa conhecer as dependências <uses-library>
antes de
gera regras de build de dexpreopt. No entanto, ele não pode acessar o manifesto diretamente
e leia o <uses-library>
porque o sistema de build não tem permissão para ler arquivos arbitrários
ele gera regras de build (por motivos de desempenho). Além disso, o manifesto pode
ser empacotados em um APK ou pré-criado. Portanto, as informações <uses-library>
precisam estar presentes nos arquivos de build (Android.bp
ou Android.mk
).
Anteriormente, o ART usava uma solução alternativa que ignorava as dependências de biblioteca compartilhada (conhecida
como &-classpath
). Isso não era seguro e causava bugs sutis. Por isso, a solução alternativa
foi removida no Android 12.
Como resultado, os módulos Java que não fornecem o <uses-library>
correto
informações nos arquivos de build podem causar falhas de build (causadas por uma
incompatibilidade de CLC durante a criação) ou regressões do tempo de primeira inicialização (causadas por um tempo de inicialização
incompatibilidade de CLC seguida por dexopt).
Caminho de migração
Siga estas etapas para corrigir um build corrompido:
Desativar globalmente a verificação no tempo de build para um produto específico por configuração
PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true
no makefile do produto. Isso corrige erros de build (exceto em casos especiais, listadas na seção Como corrigir falhas). No entanto, essa é uma solução temporária e pode causar uma incompatibilidade de CLC no momento da inicialização seguida por dexopt.
Corrija os módulos que falharam antes de desativar globalmente a verificação no build adicionando as informações
<uses-library>
necessárias aos arquivos de build. Consulte Como corrigir falhas para saber mais. Para a maioria dos módulos, isso requer a adição de algumas linhas emAndroid.bp
ouAndroid.mk
.Desativar a verificação de tempo de build e dexpreopt para os casos problemáticos, por módulo. Desative o dexpreopt para não perder tempo de build e armazenamento em artefatos rejeitados na inicialização.
Reativar globalmente a verificação no build desativando
PRODUCT_BROKEN_VERIFY_USES_LIBRARIES
que foi definido na etapa 1. O build não deve falhar após essa mudança (devido às etapas 2 e 3).Corrija os módulos desativados na etapa 3, um por vez, e reative o dexpreopt e a verificação
<uses-library>
. Registre bugs, se necessário.
As verificações <uses-library>
no tempo de build são aplicadas no Android 12.
Corrigir falhas
As seções a seguir explicam como corrigir tipos específicos de falha.
Erro de build: incompatibilidade de CLC
O sistema de build faz uma verificação de coerência no tempo de build entre as informações nos
arquivos Android.bp
ou Android.mk
e o manifesto. O sistema de build não consegue ler
o manifesto, mas pode gerar regras de build para ler o manifesto (extraindo
a partir de um APK, se necessário) e compare as tags <uses-library>
no manifesto.
com as informações de <uses-library>
nos arquivos de build. Se a verificação falhar,
o erro será semelhante a este:
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 a mensagem de erro sugere, há várias soluções, dependendo urgência:
- Para uma correção temporária em todo o produto, defina
PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true
no makefile do produto. A verificação de coerência no build ainda é realizada, mas uma falha na verificação não significa uma falha no build. Em vez disso, uma falha na verificação faz o sistema de compilação fazer downgrade O compilador dex2oat filtra paraverify
no dexpreopt, que desativa a compilação AOT. inteiramente neste módulo. - Para uma correção rápida e global de linha de comando, use a variável de ambiente
RELAX_USES_LIBRARY_CHECK=true
Tem o mesmo efeito quePRODUCT_BROKEN_VERIFY_USES_LIBRARIES
, mas destinado ao uso na linha de comando. A variável de ambiente substitui a variável do produto. - Para uma solução para corrigir a causa raiz do erro, informe o sistema de compilação
das tags
<uses-library>
no manifesto. Uma inspeção da mensagem de erro mostra quais bibliotecas causam o problema, assim como a inspeçãoAndroidManifest.xml
ou o manifesto dentro de um APK que pode ser verificado com "aapt dump badging $APK | grep uses-library
".
Para módulos Android.bp
:
Procure a biblioteca ausente na propriedade
libs
do módulo. Se for O Soong normalmente adiciona essas bibliotecas automaticamente, exceto nos casos casos especiais:- A biblioteca não é uma biblioteca do SDK. Ela é definida como
java_library
em vez dejava_sdk_library
. - A biblioteca tem um nome de biblioteca diferente (no manifesto) de seu módulo no sistema de build.
Para corrigir isso temporariamente, adicione
provides_uses_lib: "<library-name>"
na definição da bibliotecaAndroid.bp
. Para uma solução de longo prazo, corrija problema: converter a biblioteca para uma biblioteca do SDK ou renomear o módulo dela.- A biblioteca não é uma biblioteca do SDK. Ela é definida como
Se a etapa anterior não fornecer uma resolução, adicione
uses_libs: ["<library-module-name>"]
para bibliotecas obrigatórias ouoptional_uses_libs: ["<library-module-name>"]
para bibliotecas opcionais à definiçãoAndroid.bp
do módulo. Essas propriedades aceitam uma lista de nomes de módulos. A ordem relativa das bibliotecas na lista precisa ser a mesma que a ordem no manifesto.
Para módulos Android.mk
:
Verifique se a biblioteca tem um nome diferente (no manifesto) do nome do módulo (no sistema de build). Se isso acontecer, corrija temporariamente adicionando
LOCAL_PROVIDES_USES_LIBRARY := <library-name>
no arquivoAndroid.mk
da biblioteca ou adicioneprovides_uses_lib: "<library-name>"
no arquivoAndroid.bp
da biblioteca. Ambos os casos são possíveis, já que um móduloAndroid.mk
pode depender de uma bibliotecaAndroid.bp
. Para uma solução de longo prazo, corrija o problema subjacente: renomeie o módulo da biblioteca.Adicione
LOCAL_USES_LIBRARIES := <library-module-name>
aos campos obrigatórios bibliotecas adicionarLOCAL_OPTIONAL_USES_LIBRARIES := <library-module-name>
para bibliotecas opcionais à definiçãoAndroid.mk
do módulo. Esses aceitam uma lista de nomes de módulos. A ordem relativa das bibliotecas na lista precisa ser a mesma do manifesto.
Erro de build: caminho de biblioteca desconhecido
Se o sistema de compilação não conseguir encontrar um caminho para um jar DEX <uses-library>
(um arquivo
de tempo de build no host ou um caminho de instalação no dispositivo), ele geralmente falha
ser construído. Uma falha ao encontrar um caminho pode indicar que a biblioteca está configurada em
de forma inesperada. Corrija temporariamente o build desativando o dexpreopt para o módulo
com problemas.
Android.bp (propriedades do módulo):
enforce_uses_libs: false,
dex_preopt: {
enabled: false,
},
Android.mk (variáveis de módulo):
LOCAL_ENFORCE_USES_LIBRARIES := false
LOCAL_DEX_PREOPT := false
Informe um bug para investigar os cenários sem suporte.
Erro de build: dependência de biblioteca ausente
Uma tentativa de adicionar <uses-library>
X do manifesto do módulo Y ao build
para Y pode resultar em um erro de compilação devido à dependência ausente, X.
Confira um exemplo de mensagem de erro para módulos Android.bp:
"Y" depends on undefined module "X"
Este é um exemplo de mensagem de erro para 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
Uma fonte comum desses erros é quando uma biblioteca tem um nome diferente
módulo correspondente é nomeado no sistema de build. Por exemplo, se a entrada
<uses-library>
do manifesto for com.android.X
, mas o nome do módulo da biblioteca for
apenas X
, isso vai causar um erro. Para resolver esse caso, informe ao sistema de build que
o módulo com o nome X
fornece um <uses-library>
chamado com.android.X
.
Este é um exemplo para bibliotecas Android.bp
(propriedade do módulo):
provides_uses_lib: “com.android.X”,
Este é um exemplo de bibliotecas Android.mk (variável de módulo):
LOCAL_PROVIDES_USES_LIBRARY := com.android.X
Incompatibilidade de CLC no tempo de inicialização
Na primeira inicialização, procure no logcat mensagens relacionadas à incompatibilidade de CLC, conforme mostrado abaixo:
$ adb wait-for-device && adb logcat \
| grep -E 'ClassLoaderContext [a-z ]+ mismatch' -A1
A saída pode ter mensagens do formulário mostrado aqui:
[...] W system_server: ClassLoaderContext shared library size mismatch Expected=..., found=... (PCL[]... | PCL[]...)
[...] I PackageDexOptimizer: Running dexopt (dexoptNeeded=1) on: ...
Se você receber um aviso de incompatibilidade de CLC, procure um comando dexopt para mais tarde neste módulo. Para corrigir isso, verifique se a verificação no tempo de build do módulo é bem-sucedida. Se isso não funcionar, talvez o seu seja um caso especial que não tem suporte do sistema de build (como um app que carrega outro APK, não uma biblioteca). O sistema de build não lida com todos os casos porque, no momento da criação, é impossível para saber com certeza o que o app carrega durante a execução.
Contexto do carregador de classes
A CLC é uma estrutura semelhante a uma árvore que descreve a hierarquia do carregador de classes. O
sistema de build usa o CLC em um sentido restrito (ele abrange apenas bibliotecas, não APKs ou
carregadores de classe personalizados): é uma árvore de bibliotecas que representa o fechamento
transitivo de todas as dependências <uses-library>
de uma biblioteca ou app. Os elementos de nível superior
de um CLC são as dependências <uses-library>
diretas especificadas
no manifesto (o caminho de classe). Cada nó de uma árvore de CLC é um
nó <uses-library>
que pode ter seus próprios subnós <uses-library>
.
Como as dependências de <uses-library>
são um gráfico acíclico dirigido, e não
necessariamente uma árvore, a CLC pode conter vários subárvores para a mesma biblioteca. Em
em outras palavras, a CLC é o gráfico de dependência "desdobrado" para uma árvore. A duplicação
está apenas em um nível lógico: os carregadores de classes reais não são
duplicado (durante o tempo de execução há uma única instância de carregador de classe para cada biblioteca).
A CLC define a ordem de pesquisa das bibliotecas ao resolver classes Java usadas por biblioteca ou app. A ordem da pesquisa é importante porque as bibliotecas podem conter classes duplicadas, e a classe é resolvida para a primeira correspondência.
CLC no dispositivo (ambiente de execução)
PackageManager
(em frameworks/base
) cria um CLC para carregar um módulo Java
no dispositivo. Ele adiciona as bibliotecas listadas nas tags <uses-library>
no manifesto
do módulo como elementos de CLC de nível superior.
Para cada biblioteca usada, a PackageManager
recebe todos os <uses-library>
de dependências (especificadas como tags no manifesto dessa biblioteca) e adiciona uma
a CLC aninhada para cada dependência. Esse processo continua recursivamente até que
Os nós de folha da árvore de CLC construída são bibliotecas sem <uses-library>
dependências.
PackageManager
só reconhece bibliotecas compartilhadas. A definição de "compartilhado" nesse uso é diferente do significado usual (como "compartilhado" e "estático"). No Android,
Bibliotecas compartilhadas Java são aquelas listadas nas configurações XML que estão instaladas
no dispositivo (/system/etc/permissions/platform.xml
). Cada entrada contém o nome
de uma biblioteca compartilhada, um caminho para o respectivo arquivo jar DEX e uma lista de dependências
(outras bibliotecas compartilhadas que este usa no tempo de execução e especifica em
<uses-library>
no manifesto).
Em outras palavras, há duas fontes de informações que permitem que PackageManager
construa o CLC no momento da execução: tags <uses-library>
no manifesto e
dependências de biblioteca compartilhada em configurações XML.
CLC no host (no build)
A CLC não é necessária apenas ao carregar uma biblioteca ou um aplicativo, ela também é necessária ao
e compilar um. A compilação pode ocorrer no dispositivo (dexopt) ou durante o
build (dexpreopt). Como o dexopt ocorre no dispositivo, ele tem as mesmas
informações que PackageManager
(manifestos e dependências de biblioteca compartilhada).
O Dexpreopt, no entanto, ocorre no host e em um ambiente
e precisa das mesmas informações do sistema de build.
Assim, a CLC do tempo de compilação usada pelo dexpreopt e a CLC no ambiente de execução usadas pelo
PackageManager
são a mesma coisa, mas computadas de duas maneiras diferentes.
Os CLCs no momento do build e da execução precisam coincidir. Caso contrário, o código compilado pelo AOT
criado pelo dexpreopt será rejeitado. Para verificar a igualdade entre tempo de build e
CLCs em tempo de execução, o compilador dex2oat registra CLCs de tempo de compilação nos arquivos *.odex
.
(no campo classpath
do cabeçalho do arquivo OAT). Para encontrar o CLC armazenado, use
este comando:
oatdump --oat-file=<FILE> | grep '^classpath = '
A incompatibilidade do CLC no tempo de build e de execução é informada no logcat durante a inicialização. Pesquise por ele com este comando:
logcat | grep -E 'ClassLoaderContext [a-z ]+ mismatch'
A incompatibilidade é ruim para a performance, porque força a biblioteca ou o app a ser dexoptado ou a ser executado sem otimizações. Por exemplo, o código do app pode precisar ser extraído na memória do APK, uma operação muito cara.
Uma biblioteca compartilhada pode ser opcional ou obrigatória. De
para o ponto de vista de dexpreopt, uma biblioteca obrigatória deve estar presente no tempo de compilação (sua
a ausência é um erro de build). Uma biblioteca opcional pode estar presente ou ausente
no momento da criação: se presente, é adicionado ao CLC, passado para dex2oat e
gravado no arquivo *.odex
. Se não houver uma biblioteca opcional, ela será ignorada
e não é adicionado ao CLC. Se houver uma incompatibilidade entre o status de build e
de execução (a biblioteca opcional está presente em um caso, mas não no outro),
os CLCs de build e de execução não vão corresponder, e o código compilado será
rejeitado.
Detalhes do sistema de compilação avançado (corretor de manifesto)
Às vezes, as tags <uses-library>
estão ausentes do manifesto de origem de uma
biblioteca ou um app. Isso pode acontecer, por exemplo, se uma das dependências transitivas
da biblioteca ou do app começar a usar outra tag <uses-library>
e o
manifesto da biblioteca ou do app não for atualizado para incluí-la.
O Soong pode calcular algumas das tags <uses-library>
ausentes de uma determinada biblioteca
ou app automaticamente, como as bibliotecas do SDK no fechamento de dependência transitiva
da biblioteca ou do app. O fechamento é necessário porque a biblioteca (ou o app) pode
depender de uma biblioteca estática que depende de uma biblioteca do SDK e, possivelmente, pode
depender transitivamente de outra biblioteca.
Nem todas as tags <uses-library>
podem ser computadas dessa maneira, mas, quando possível, é
preferível permitir que o Soong adicione entradas de manifesto automaticamente. Isso é menos
propenso a erros e simplifica a manutenção. Por exemplo, quando muitos apps usam uma biblioteca
estática que adiciona uma nova dependência <uses-library>
, todos os apps precisam ser
atualizados, o que é difícil de manter.