O Google tem o compromisso de promover a igualdade racial para as comunidades negras. Saiba como.

Verificações Dexpreopt e <uses-library>

Android 12 tem mudanças no sistema de compilação para compilação AOT de arquivos DEX (dexpreopt) para os módulos Java que têm <uses-library> dependências. Em alguns casos, essas mudanças no sistema de builds podem interromper os builds. Use esta página para se preparar para quebras e siga as receitas desta página para corrigi-las e mitigá-las.

Dexpreopt é o processo de compilação antecipada de bibliotecas e aplicativos Java. Dexpreopt acontece no hospedeiro no tempo de construção (em oposição a dexopt, o que acontece no dispositivo). A estrutura de dependências de bibliotecas compartilhadas utilizados por um módulo de Java (uma biblioteca ou uma APP) é conhecida como o seu contexto do carregador de classe (CLC). Para garantir a exatidão do dexpreopt, os CLCs de tempo de construção e de execução devem coincidir. CLC em tempo de construção é o que o compilador dex2oat usa em tempo dexpreopt (é registrado nos arquivos ODEX), e CLC em tempo de execução é o contexto no qual o código pré-compilado é carregado no dispositivo.

Esses CLCs de tempo de construção e tempo de execução devem coincidir por motivos de correção e desempenho. Para correção, é necessário lidar com classes duplicadas. Se as dependências da biblioteca compartilhada em tempo de execução forem diferentes daquelas usadas para compilação, algumas das classes podem ser resolvidas de forma diferente, causando erros sutis de tempo de execução. O desempenho também é afetado pelas verificações de tempo 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 os CLCs de tempo de construção e de execução, ele rejeitará os artefatos dexpreopt e executará o dexopt. Para inicializações subsequentes, isso é bom porque os aplicativos podem ser desenvolvidos em segundo plano e armazenados no disco.

Áreas afetadas do Android

Isso afeta todos os aplicativos e bibliotecas Java que possuem dependências de tempo de execução em outras bibliotecas Java. O Android tem milhares de aplicativos, e centenas deles usam bibliotecas compartilhadas. Os parceiros também são afetados, pois têm suas próprias bibliotecas e aplicativos.

Quebrando mudanças

O sistema de compilação precisa saber <uses-library> dependências antes de gerar regras de compilação dexpreopt. No entanto, ele não pode acessar o manifesto diretamente e ler o <uses-library> marcas nele, porque o sistema de compilação não tem permissão para ler arquivos arbitrários quando gera regras de compilação (por motivos de desempenho). Além disso, o manifesto pode ser empacotado dentro de um APK ou um pré-construído. Portanto, o <uses-library> informação deve estar presente nos arquivos de compilação ( Android.bp ou Android.mk ).

Anteriormente ART usada uma solução que ignorou compartilhada dependências da biblioteca (conhecido como o &-classpath ). Isso não era seguro e causava bugs sutis, portanto, a solução alternativa foi removida no Android 12.

Como resultado, os módulos Java que não fornecem correta <uses-library> informações em seus arquivos de compilação pode causar rupturas de construção (causado por uma incompatibilidade CLC em tempo de compilação) ou de primeira inicialização regressões tempo (causada por uma CLC-tempo de inicialização incompatibilidade seguida por dexopt).

Caminho de migração

Siga estas etapas para consertar uma compilação quebrada:

  1. Desative globalmente a verificação de tempo de construção para um produto específico, definindo

    PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true

    no makefile do produto. Isso corrige erros de construção (exceto em casos especiais, indicados no quebras Fixação seção). No entanto, esta é uma solução temporária e pode causar incompatibilidade de CLC no tempo de inicialização seguida por dexopt.

  2. Fixar os módulos que falhou antes de desabilitado globalmente a verificação em tempo de compilação, adicionando o necessário <uses-library> informações aos seus arquivos de compilação (veja fixação rupturas para detalhes). Para a maioria dos módulos é necessário adicionar algumas linhas em Android.bp , ou em Android.mk .

  3. Desative a verificação de tempo de construção e dexpreopt para os casos problemáticos, em uma base por módulo. Desative o dexpreopt para não perder tempo de construção e armazenamento em artefatos que são rejeitados na inicialização.

  4. Globalmente re-permitir a verificação de tempo de compilação por unsetting PRODUCT_BROKEN_VERIFY_USES_LIBRARIES que foi definido na etapa 1; a compilação não deve falhar após essa alteração (por causa das etapas 2 e 3).

  5. Corrigir os módulos que foram desabilitados na Etapa 3, um de cada vez, em seguida, reabilitar dexpreopt eo <uses-library> cheque. Arquive bugs, se necessário.

Build-tempo <uses-library> verificações são executadas no Android 12.

Reparando quebras

As seções a seguir mostram como consertar tipos específicos de quebra.

Erro de compilação: incompatibilidade de CLC

O sistema de compilação faz uma verificação de coerência em tempo de compilação entre as informações em Android.bp ou Android.mk arquivos eo manifesto. O sistema de construção não pode ler o manifesto, mas pode gerar regras de compilação para ler o manifesto (extraí-lo a partir de um APK se necessário), e compará <uses-library> tags no manifesto contra o <uses-library> informações os arquivos de construção. Se a verificação falhar, o erro será assim:

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, existem várias soluções, dependendo da urgência:

  • Para uma correção temporária de todo o produto, definir PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true no makefile produto. A verificação de coerência do tempo de construção ainda é executada, mas uma falha de verificação não significa uma falha de construção. Em vez disso, uma falha de verificação faz com que o sistema de compilação rebaixamento do filtro compilador dex2oat para verify em dexpreopt, que desativa AOT-compilação inteiramente para este módulo.
  • Para um rápido, correção de linha de comando global, utilizar a variável de ambiente RELAX_USES_LIBRARY_CHECK=true . Tem o mesmo efeito que faz PRODUCT_BROKEN_VERIFY_USES_LIBRARIES , mas destina-se para uso na linha de comando. A variável de ambiente substitui a variável do produto.
  • Para uma solução a causa-raiz corrigir o erro, faça o sistema de compilação ciente dos <uses-library> tags no manifesto. Uma inspeção dos shows mensagem de erro que bibliotecas causa o problema (como se inspecionando AndroidManifest.xml ou no interior manifesto de um APK que pode ser verificado com ` aapt dump badging $APK | grep uses-library `).

Para Android.bp módulos:

  1. Olhe para a biblioteca em falta no libs propriedade do módulo. Se estiver lá, Soong normalmente adiciona essas bibliotecas automaticamente, exceto nestes casos especiais:

    • A biblioteca não é uma biblioteca de SDK (é definido como java_library em vez de java_sdk_library ).
    • A biblioteca possui um nome de biblioteca diferente (no manifesto) de seu nome de módulo (no sistema de compilação).

    Para corrigir isso temporariamente, adicione provides_uses_lib: "<library-name>" no Android.bp definição da biblioteca. Para uma solução de longo prazo, corrija o problema subjacente: converta a biblioteca em uma biblioteca SDK ou renomeie seu módulo.

  2. Se a etapa anterior não fornecer uma resolução, adicione uses_libs: ["<library-module-name>"] para bibliotecas necessárias, ou optional_uses_libs: ["<library-module-name>"] para bibliotecas opcionais para o Android.bp definição do módulo. Essas propriedades aceitam uma lista de nomes de módulos. A ordem relativa das bibliotecas na lista deve ser igual à ordem no manifesto.

Para Android.mk módulos:

  1. Verifique se a biblioteca tem um nome de biblioteca diferente (no manifesto) de seu nome de módulo (no sistema de compilação). Se isso acontecer, corrigir esse temporariamente adicionando LOCAL_PROVIDES_USES_LIBRARY := <library-name> na Android.mk arquivo da biblioteca, ou adicionar provides_uses_lib: "<library-name>" no Android.bp arquivo da biblioteca (ambos os casos são possíveis desde que um Android.mk módulo pode depender de um Android.bp biblioteca). Para uma solução de longo prazo, corrija o problema subjacente: renomeie o módulo de biblioteca.

  2. Adicionar LOCAL_USES_LIBRARIES := <library-module-name> para bibliotecas necessárias; adicionar LOCAL_OPTIONAL_USES_LIBRARIES := <library-module-name> para bibliotecas opcionais ao Android.mk definição do módulo. Essas propriedades aceitam uma lista de nomes de módulos. A ordem relativa das bibliotecas na lista deve ser a mesma do manifesto.

Erro de compilação: caminho de biblioteca desconhecido

Se o sistema de compilação não consegue encontrar um caminho para um <uses-library> DEX frasco (um caminho de tempo de compilação de acolhimento ou um caminho de instalação no dispositivo), que normalmente falhar a compilação. Uma falha em encontrar um caminho pode indicar que a biblioteca está configurada de alguma forma inesperada. Corrija temporariamente a compilação desativando o dexpreopt para o módulo problemático.

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

Registre um bug para investigar qualquer cenário sem suporte.

Erro de compilação: dependência de biblioteca ausente

Uma tentativa de adicionar <uses-library> X do manifesto de módulo Y para o arquivo de construção para Y pode resultar em um erro de compilação devido à falta dependência, X.

Este é 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 de tais erros é quando uma biblioteca é nomeada de forma diferente do que seu módulo correspondente é nomeado no sistema de construção. Por exemplo, se o manifesto <uses-library> entrada é com.android.X , mas o nome do módulo de biblioteca é apenas X , que provoca um erro. Para resolver esse caso, dizer ao sistema de compilação que o módulo chamado X fornece uma <uses-library> chamado com.android.X .

Este é um exemplo para Android.bp bibliotecas (propriedades de módulo):

provides_uses_lib: “com.android.X”,

Este é um exemplo para 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 por 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 no formato 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 o módulo com defeito. Para corrigi-lo, certifique-se de que a verificação de tempo de construção do módulo seja aprovada. Se isso não funcionar, então o seu pode ser um caso especial que não é compatível com o sistema de compilação (como um aplicativo que carrega outro APK, não uma biblioteca). O sistema de compilação não trata de todos os casos, porque em tempo de compilação é impossível saber ao certo o que o aplicativo carrega em tempo de execução.

Contexto do carregador de classes

O CLC é uma estrutura semelhante a uma árvore que descreve a hierarquia do carregador de classes. O sistema de compilação usa CLC em sentido estrito (que abrange apenas as bibliotecas, não APKs ou carregadores costume de classe): é uma árvore de bibliotecas que representa fechamento transitivo de todos os <uses-library> dependências de uma biblioteca ou aplicativo. Os elementos de topo da uma CLC são a direta <uses-library> dependências especificado no manifesto (classpath). Cada nó de uma árvore CLC é um <uses-library> nó que pode ter o seu próprio <uses-library> sub-nós.

Porque <uses-library> dependências são um gráfico acíclico dirigido, e não necessariamente uma árvore, CLC pode conter vários sub-árvores para a mesma biblioteca. Em outras palavras, CLC é o gráfico de dependência "desdobrado" em uma árvore. A duplicação é apenas em um nível lógico; os carregadores de classes subjacentes reais não são duplicados (em tempo de execução, há uma única instância do carregador de classes para cada biblioteca).

O CLC define a ordem de pesquisa das bibliotecas ao resolver as classes Java usadas pela biblioteca ou aplicativo. A ordem de pesquisa é importante porque as bibliotecas podem conter classes duplicadas e a classe é resolvida para a primeira correspondência.

CLC no dispositivo (tempo de execução)

PackageManager (em frameworks/base ) cria um CLC para carregar um módulo Java no dispositivo. Ele adiciona as bibliotecas listadas na <uses-library> tag no manifesto como elementos CLC de nível superior do módulo.

Para cada biblioteca usada, PackageManager recebe toda a sua <uses-library> dependências (especificados como tags no manifesto de que a biblioteca) e adiciona um CLC aninhada para cada dependência. Este processo continua recursivamente até que todos os nós de folhas da árvore CLC construídos são bibliotecas sem <uses-library> dependências.

PackageManager só é consciente de bibliotecas compartilhadas. A definição de compartilhado neste uso difere de seu significado usual (como em compartilhado vs. estático). Em Android, Java bibliotecas compartilhadas são os enumerados no configs XML que estão instalados no dispositivo ( /system/etc/permissions/platform.xml ). Cada entrada contém o nome de uma biblioteca compartilhada, um caminho para o seu arquivo de DEX frasco, e uma lista de dependências (outras bibliotecas compartilhadas que este usa no tempo de execução e especifica em <uses-library> marcas em seu manifesto).

Em outras palavras, existem duas fontes de informação que permitem PackageManager para construir CLC em tempo de execução: <uses-library> tags no manifesto e compartilhados dependências da biblioteca em configurações XML.

CLC no host (tempo de construção)

O CLC não é necessário apenas ao carregar uma biblioteca ou aplicativo, também é necessário ao compilar um. A compilação pode acontecer no dispositivo (dexopt) ou durante a compilação (dexpreopt). Desde dexopt ocorre no dispositivo, que tem a mesma informação que PackageManager (manifestos e dependências de bibliotecas compartilhadas). O Dexpreopt, no entanto, ocorre no host e em um ambiente totalmente diferente, e precisa obter as mesmas informações do sistema de construção.

Assim, a compilação em tempo CLC usado por dexpreopt eo tempo de execução CLC usado por PackageManager são a mesma coisa, mas calculado de duas maneiras diferentes.

Em tempo de compilação e tempo de execução CLCs devem coincidir, caso contrário, o código compilado AOT criado por dexpreopt for rejeitado. Para verificar a igualdade de tempo de compilação e tempo de execução CLCs, os registros do compilador dex2oat-tempo de compilação CLC nas *.odex arquivos (no classpath campo do cabeçalho do arquivo OAT). Para encontrar o CLC armazenado, use este comando:

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

A incompatibilidade de CLC em tempo de construção e tempo de execução é relatada no logcat durante a inicialização. Pesquise com este comando:

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

A incompatibilidade é ruim para o desempenho, pois força a biblioteca ou aplicativo a ser desenvolvido ou executado sem otimizações (por exemplo, o código do aplicativo pode precisar ser extraído na memória do APK, uma operação muito cara).

Uma biblioteca compartilhada pode ser opcional ou obrigatória. Do ponto de vista dexpreopt, uma biblioteca necessária deve estar presente no momento da construção (sua ausência é um erro de construção). Uma biblioteca opcional pode ser presente ou ausente em tempo de compilação: se presente, ele é adicionado à CLC, passou para dex2oat, e registradas no *.odex arquivo. Se uma biblioteca opcional estiver ausente, ela será ignorada e não será adicionada ao CLC. Se houver uma incompatibilidade entre o status do tempo de compilação e do tempo de execução (a biblioteca opcional está presente em um caso, mas não no outro), os CLCs de tempo de compilação e tempo de execução não correspondem e o código compilado é rejeitado.

Detalhes do sistema de compilação avançada (fixador de manifesto)

Às vezes <uses-library> marcas estão faltando o manifesto fonte de uma biblioteca ou aplicativo. Isso pode acontecer, por exemplo, se uma das dependências transitivas da biblioteca ou aplicativo começa a utilizar outro <uses-library> tag, e da biblioteca ou do aplicativo manifesto não é atualizada para incluí-lo.

Soong pode calcular alguns dos desaparecidos <uses-library> etiquetas para uma determinada biblioteca ou aplicativo automaticamente, como as bibliotecas do SDK no encerramento dependência transitiva da biblioteca ou aplicativo. O encerramento é necessário porque a biblioteca (ou aplicativo) pode depender de uma biblioteca estática que depende de uma biblioteca SDK e, possivelmente, pode novamente depender transitivamente por meio de outra biblioteca.

Nem todos <uses-library> tags podem ser calculado desta forma, mas quando possível, é prefereable deixar Soong adicionar entradas de manifesto automaticamente; é menos sujeito a erros e simplifica a manutenção. Por exemplo, quando muitos aplicativos usar uma biblioteca estática que adiciona um novo <uses-library> dependência, todos os aplicativos devem ser atualizados, o que é difícil de manter.