Проверки dexpreopt и <uses-library>

Android 12 имеет встроенные системные изменения в AOT компиляции DEX файлов (dexpreopt) для модулей Java , которые имеют <uses-library> зависимости. В некоторых случаях эти изменения системы сборки могут нарушить сборку. Используйте эту страницу, чтобы подготовиться к поломкам, и следуйте рецептам на этой странице, чтобы исправить и смягчить их.

Dexpreopt - это процесс предварительной компиляции библиотек и приложений Java. Dexpreopt происходит на хозяине во время сборки (в отличие от dexopt, что происходит на устройстве). Структура общих библиотечных зависимостей , используемых модулем Java (библиотека или приложение) , как известно , как его контекст загрузчика классов (CLC). Чтобы гарантировать правильность dexpreopt, CLC времени сборки и выполнения должны совпадать. CLC времени сборки - это то, что компилятор dex2oat использует во время dexpreopt (он записан в файлах ODEX), а CLC времени выполнения - это контекст, в котором предварительно скомпилированный код загружается на устройство.

Эти CLC времени сборки и выполнения должны совпадать по причинам как правильности, так и производительности. Для корректности необходимо обрабатывать повторяющиеся классы. Если зависимости разделяемой библиотеки во время выполнения отличаются от зависимостей, используемых для компиляции, некоторые из классов могут быть разрешены по-другому, вызывая тонкие ошибки во время выполнения. На производительность также влияют проверки среды выполнения на наличие повторяющихся классов.

Затронутые варианты использования

Первая загрузка - это основной вариант использования, на который влияют эти изменения: если ART обнаруживает несоответствие между CLC времени сборки и времени выполнения, он отклоняет артефакты dexpreopt и вместо этого запускает dexopt. Для последующих загрузок это нормально, потому что приложения можно декоптировать в фоновом режиме и сохранять на диске.

Затронутые области Android

Это влияет на все приложения и библиотеки Java, которые во время выполнения зависят от других библиотек Java. У Android есть тысячи приложений, и сотни из них используют общие библиотеки. Это также касается партнеров, поскольку у них есть собственные библиотеки и приложения.

Критические изменения

Система сборки должна знать <uses-library> зависимости , прежде чем создавать dexpreopt правила сборки. Тем не менее, он не может получить доступ к манифесту непосредственно и прочитать <uses-library> тегов в нем, так как систему сборки не может читать произвольные файлы , когда он создает правила сборки (по соображениям производительности). Более того, манифест может быть упакован в APK или предварительно собранный. Таким образом, <uses-library> информация должна присутствовать в файлах сборки ( Android.bp или Android.mk ).

Ранее АРТ используется обходной путь , который игнорировал совместно используемую библиотеку зависимостей (известный как &-classpath ). Это было небезопасно и вызывало небольшие ошибки, поэтому обходной путь был удален в Android 12.

В результате, модули Java , которые не обеспечивают правильную <uses-library> информации в их файлах сборки могут вызвать поломку сборки (вызываемую накопления время рассогласования CLC) или первую загрузку время регрессию (вызванную время загрузки CLC mismatch с последующим dexopt).

Путь миграции

Выполните следующие действия, чтобы исправить неработающую сборку:

  1. Глобально отключите проверку времени сборки для определенного продукта, установив

    PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true

    в make-файле продукта. Это исправление ошибок сборки (за исключением особых случаев, перечисленных в исключении обрывов Фиксации секции). Однако это временный обходной путь, и он может вызвать несоответствие CLC при загрузке, за которым следует dexopt.

  2. Закрепить модули , которые не удалось , прежде чем глобально отключить проверку сборки времени пути добавления необходимой <uses-library> информации их сборки файлов (см Крепления обрывов для деталей). Для большинства модулей этого необходимо добавить несколько строк в Android.bp , или в Android.mk .

  3. Отключите проверку времени сборки и dexpreopt для проблемных случаев для каждого модуля. Отключите dexpreopt, чтобы не тратить время сборки и хранилище на артефакты, которые отклоняются при загрузке.

  4. В мировом масштабе повторно включить проверку сборки времени с помощью незаходимого PRODUCT_BROKEN_VERIFY_USES_LIBRARIES , который был установлен в шаге 1; после этого изменения сборка не должна завершиться ошибкой (из-за шагов 2 и 3).

  5. Закрепить модули , которые отключены в шаге 3, по одному за раз, а затем снова включить dexpreopt и <uses-library> чек. При необходимости сообщайте об ошибках.

Сложение время <uses-library> проверяет, применяется в Android 12.

Устранение поломок

В следующих разделах рассказывается, как исправить определенные типы поломки.

Ошибка сборки: несоответствие CLC

Система сборки делает сборки времени проверки согласованности между информацией в Android.bp или Android.mk файлов и манифеста. Система сборки не может читать манифест, но он может генерировать правила сборки для чтения манифеста (извлечение его из APK , если это необходимо), и сравнить <uses-library> тегов в манифесте против <uses-library> информации файлы сборки. Если проверка не удалась, ошибка выглядит так:

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

Как следует из сообщения об ошибке, существует несколько решений, в зависимости от срочности:

  • Для временного продукта в масштабах исправления, установите PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true в Makefile продукта. Проверка согласованности во время сборки все еще выполняется, но сбой проверки не означает сбой сборки. Вместо этого, ошибка проверки делает систему сборки понижения фильтра dex2oat компилятора , чтобы verify в dexpreopt, который отключает АОТ-компиляцию полностью для этого модуля.
  • Для быстрого, глобальной командной строки исправить, используйте переменные окружения RELAX_USES_LIBRARY_CHECK=true . Она имеет тот же эффект, что делает PRODUCT_BROKEN_VERIFY_USES_LIBRARIES , но предназначена для использования в командной строке. Переменная среды переопределяет переменную продукта.
  • Для решения основной причины исправить ошибки, чтобы система сборки осознает <uses-library> тегов в манифесте. Осмотр показывают сообщения об ошибках , которые библиотеки вызывают проблемы (как это делает осмотр AndroidManifest.xml или манифест внутренней часть APK , которые могут быть проверены с помощью ` aapt dump badging $APK | grep uses-library `).

Для Android.bp модулей:

  1. Посмотрите на отсутствующую библиотеку в libs свойство модуля. Если он есть, Soong обычно добавляет такие библиотеки автоматически, за исключением следующих особых случаев:

    • Библиотека не библиотека SDK (это определяется как java_library , а не java_sdk_library ).
    • Имя библиотеки (в манифесте) отличается от имени модуля (в системе сборки).

    Чтобы исправить это временно, добавить provides_uses_lib: "<library-name>" в Android.bp определения библиотеки. Для долгосрочного решения устраните основную проблему: преобразуйте библиотеку в библиотеку SDK или переименуйте ее модуль.

  2. Если предыдущий шаг не обеспечивает разрешение, добавить uses_libs: ["<library-module-name>"] для необходимых библиотек или optional_uses_libs: ["<library-module-name>"] для дополнительных библиотек к Android.bp определение модуля. Эти свойства принимают список имен модулей. Относительный порядок библиотек в списке должен совпадать с порядком в манифесте.

Для Android.mk модулей:

  1. Убедитесь, что имя библиотеки (в манифесте) отличается от имени модуля (в системе сборки). Если это произойдет, это исправить временно добавив LOCAL_PROVIDES_USES_LIBRARY := <library-name> в Android.mk файл библиотеки, или добавить provides_uses_lib: "<library-name>" в Android.bp файл библиотеки (оба случая возможно так как Android.mk модуль может зависеть от Android.bp библиотеки). Для долгосрочного решения устраните основную проблему: переименуйте модуль библиотеки.

  2. Добавить LOCAL_USES_LIBRARIES := <library-module-name> для необходимых библиотек; добавить LOCAL_OPTIONAL_USES_LIBRARIES := <library-module-name> для дополнительных библиотек к Android.mk определению модуля. Эти свойства принимают список имен модулей. Относительный порядок библиотек в списке должен быть таким же, как в манифесте.

Ошибка сборки: неизвестный путь к библиотеке

Если система сборки не может найти путь к <uses-library> DEX банка (либо сборок время путь на хосте или пути установок на устройстве), как правило , не удается строить. Невозможность найти путь может указывать на то, что библиотека настроена каким-то неожиданным образом. Временно исправьте сборку, отключив dexpreopt для проблемного модуля.

Android.bp (свойства модуля):

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

Android.mk (переменные модуля):

LOCAL_ENFORCE_USES_LIBRARIES := false
LOCAL_DEX_PREOPT := false

Сообщите об ошибке, чтобы исследовать неподдерживаемые сценарии.

Ошибка сборки: отсутствует зависимость библиотеки

Попытка добавить <uses-library> X из манифеста модуля Y в файл сборки для Y может привести к ошибке сборки в связи с отсутствием зависимости, X.

Это пример сообщения об ошибке для модулей Android.bp:

"Y" depends on undefined module "X"

Это пример сообщения об ошибке для модулей 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

Распространенный источник таких ошибок - это когда библиотека называется иначе, чем ее соответствующий модуль назван в системе сборки. Например, если манифест <uses-library> запись com.android.X , но имя модуля библиотеки просто X , это вызывает ошибку. Для того, чтобы решить этот случай, скажите систему сборки , что модуль с именем X предоставляет <uses-library> с именем com.android.X .

Это пример для Android.bp библиотек (модуль собственности):

provides_uses_lib: “com.android.X”,

Это пример для библиотек Android.mk (переменная модуля):

LOCAL_PROVIDES_USES_LIBRARY := com.android.X

Несоответствие CLC при загрузке

При первой загрузке найдите в logcat сообщения, связанные с несоответствием CLC, как показано ниже:

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

На выходе могут быть сообщения в форме, показанной здесь:

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

Если вы получаете предупреждение о несоответствии CLC, ищите команду dexopt для неисправного модуля. Чтобы исправить это, убедитесь, что проверка модуля выполняется во время сборки. Если это не сработает, то ваш случай может быть особым, который не поддерживается системой сборки (например, приложение, которое загружает другой APK, а не библиотеку). Система сборки не обрабатывает все случаи, потому что во время сборки невозможно точно знать, что приложение загружает во время выполнения.

Контекст загрузчика классов

CLC представляет собой древовидную структуру, описывающую иерархию загрузчика классов. Система сборки использует CLC в узком смысле (она охватывает только библиотеки, а не APK - файлов или заказ загрузчиков классов): это дерево библиотек , которая представляет транзитивное замыкание всех <uses-library> Зависимости библиотеки или приложения. Верхний уровень элементы ХОК являются прямым <uses-library> зависимости , указанная в явных (пути к классам). Каждый узел дерева CLC является <uses-library> узел , который может иметь свои собственные <uses-library> суб-узлы.

Поскольку <uses-library> зависимости ориентированных ациклический граф, и не обязательно дерева, CLC может содержать несколько поддерев для одной и той же библиотеки. Другими словами, CLC - это граф зависимостей, «развернутый» в дерево. Дублирование происходит только на логическом уровне; фактические загрузчики базовых классов не дублируются (во время выполнения существует единственный экземпляр загрузчика классов для каждой библиотеки).

CLC определяет порядок поиска библиотек при разрешении классов Java, используемых библиотекой или приложением. Порядок поиска важен, потому что библиотеки могут содержать повторяющиеся классы, и класс разрешается до первого совпадения.

На устройстве (время выполнения) CLC

PackageManagerframeworks/base ) создает CLC , чтобы загрузить модуль Java на устройстве. Он добавляет библиотеки , перечисленные в <uses-library> тег в манифесте как элементы CLC верхнего уровня модуля.

Для каждой используемой библиотеки, PackageManager получает все его <uses-library> Зависимость (указанная в тегах в манифесте этой библиотеки) и добавляет вложенное CLC для каждой зависимости. Этот процесс продолжается рекурсивно до тех пор , все листовые узлы построенного CLC дерева не являются библиотеки без <uses-library> зависимостями.

PackageManager знает только разделяемых библиотек. Определение shared в этом использовании отличается от его обычного значения (как shared vs. static). В Android, Java разделяемые библиотеки являются те , которые перечислены в XML конфиги, которые установлены на устройстве ( /system/etc/permissions/platform.xml ). Каждая запись содержит имя общей библиотеки, путь к его файлу DEX банки, а также список зависимостей (другие разделяемым библиотек , которые на этот раз использую во время выполнения, и определяет в <uses-library> тегов в своем манифесте).

Другими словами, есть два источника информации , которые позволяют PackageManager построить CLC во время выполнения: <uses-library> теги в манифесте, и разделяемых библиотек зависимостей в XML - конфигов.

На хосте (время сборки) CLC

CLC нужен не только при загрузке библиотеки или приложения, но и при их компиляции. Компиляция может происходить либо на устройстве (dexopt), либо во время сборки (dexpreopt). Так как dexopt происходит на устройстве, оно имеет ту же информацию, PackageManager (манифесты и общие библиотеки зависимостей). Однако Dexpreopt выполняется на хосте и в совершенно другой среде, и он должен получать ту же информацию от системы сборки.

Таким образом, накопление время CLC используется dexpreopt и время выполнения CLC используется PackageManager одно и то же, но вычислены два различных способов.

Сложения время и время выполнения ХКИ должны совпадать, в противном случае АОТ-скомпилированный код , созданный dexpreopt отвергается. Чтобы проверить равенство сборки времени и время выполнения ХОК при записи dex2oat компилятора сборки время CLC в *.odex файлов (в classpath к classpath поля заголовка файла ОИТ). Чтобы найти сохраненный CLC, используйте эту команду:

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

Несоответствие CLC времени сборки и выполнения сообщается в logcat во время загрузки. Найдите его с помощью этой команды:

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

Несовпадение плохо сказывается на производительности, так как оно заставляет библиотеку или приложение либо декоптироваться, либо работать без оптимизации (например, код приложения может потребоваться извлечь в память из APK, что является очень дорогостоящей операцией).

Общая библиотека может быть необязательной или обязательной. С точки зрения dexpreopt, необходимая библиотека должна присутствовать во время сборки (ее отсутствие является ошибкой сборки). Дополнительная библиотека может быть либо присутствует , либо отсутствует во время сборки: если присутствует, он добавляется к CLC, передается dex2oat, и записывается в *.odex файл. Если дополнительная библиотека отсутствует, она пропускается и не добавляется в CLC. Если есть несоответствие между временем сборки и статусом выполнения (дополнительная библиотека присутствует в одном случае, но не в другом), то CLC времени сборки и времени выполнения не совпадают, и скомпилированный код отклоняется.

Подробная информация о расширенной системе сборки (средство исправления манифеста)

Иногда <uses-library> теги отсутствуют из источника манифесте библиотеки или приложения. Это может произойти, например, если один из переходных зависимостей библиотеки или приложения начинает использовать другой <uses-library> тег, и библиотека или приложения манифест не обновляются , чтобы включить его.

Сунг можно вычислить некоторые из недостающего <uses-library> тегов для данной библиотеки или приложений автоматически, как библиотеки SDK в закрытии транзитивного зависимости библиотеки или приложения. Закрытие необходимо, потому что библиотека (или приложение) может зависеть от статической библиотеки, которая зависит от библиотеки SDK, и, возможно, снова может зависеть транзитивно через другую библиотеку.

Не все <uses-library> теги могут быть вычислены таким образом, но , когда это возможно, это prefereable позволить Сунг добавить элементы манифеста автоматически; он менее подвержен ошибкам и упрощает обслуживание. Например, когда многие приложения используют библиотеку статическую , которая добавляет новый <uses-library> зависимость, все приложения должны быть обновлены, что трудно поддерживать.