manifest_check.py: add uses-libraries propagaged via dexpreopt.config.
Some of the uses-libraries in the manifest are propagated from
dependencies (which are not necessarily uses-libraries themselves).
In makefiles this has to be done via dexpreopt.config files to ensure
topological order. This patch makes manifest_check.py add such
propagated entries before doing the check (as it might otherwise fail).
Also, the patch adds deduplication of added library entries.
Bug: 214255490
Test: lunch aosp_cf_x86_64_phone-userdebug && m && launch_cvd \
&& adb wait-for-device && adb root \
&& adb logcat | grep -E 'ClassLoaderContext [a-z ]+ mismatch'
# a few unrelated errors caused by missing optional uses-libraries
# android.net.ipsec.ike
# androidx.window.extensions
# androidx.window.sidecar
# that were present before this patch
Change-Id: Ic8528dffd47fa5818b38b912cd6b606855480e33
diff --git a/scripts/manifest_check.py b/scripts/manifest_check.py
index c8d4f76..0216fc0 100755
--- a/scripts/manifest_check.py
+++ b/scripts/manifest_check.py
@@ -20,9 +20,11 @@
import argparse
import json
+import os
import re
import subprocess
import sys
+from collections import OrderedDict
from xml.dom import minidom
from manifest import android_ns
@@ -43,11 +45,13 @@
'--uses-library',
dest='uses_libraries',
action='append',
+ default=[],
help='specify uses-library entries known to the build system')
parser.add_argument(
'--optional-uses-library',
dest='optional_uses_libraries',
action='append',
+ default=[],
help='specify uses-library entries known to the build system with '
'required:false'
)
@@ -74,9 +78,14 @@
help='print the targetSdkVersion from the manifest')
parser.add_argument(
'--dexpreopt-config',
- dest='dexpreopt_configs',
+ dest='dexpreopt_config',
+ help='a path to dexpreopt.config file for this library/app')
+ parser.add_argument(
+ '--dexpreopt-dep-config',
+ dest='dexpreopt_dep_configs',
action='append',
- help='a paths to a dexpreopt.config of some library')
+ default=[],
+ help='a path to dexpreopt.config file for a dependency library')
parser.add_argument('--aapt', dest='aapt', help='path to aapt executable')
parser.add_argument(
'--output', '-o', dest='output', help='output AndroidManifest.xml file')
@@ -295,25 +304,53 @@
return target_attr.value
-def load_dexpreopt_configs(configs):
+def remove_duplicates(l):
+ return list(OrderedDict.fromkeys(l))
+
+
+def load_dexpreopt_configs(args):
"""Load dexpreopt.config files and map module names to library names."""
module_to_libname = {}
- if configs is None:
- configs = []
+ # Go over dexpreopt.config files for uses-library dependencies and create
+ # a mapping from module name to real library name (they may differ).
+ for config in args.dexpreopt_dep_configs:
+ # Empty dexpreopt.config files are expected for some dependencies.
+ if os.stat(config).st_size != 0:
+ with open(config, 'r') as f:
+ contents = json.load(f)
+ module_to_libname[contents['Name']] = contents['ProvidesUsesLibrary']
- for config in configs:
- with open(config, 'r') as f:
+ required = translate_libnames(args.uses_libraries, module_to_libname)
+ optional = translate_libnames(args.optional_uses_libraries, module_to_libname)
+
+ # Add extra uses-libraries from the library/app's own dexpreopt.config.
+ # Extra libraries may be propagated via dependencies' dexpreopt.config files
+ # (not only uses-library ones, but also transitively via static libraries).
+ if args.dexpreopt_config:
+ with open(args.dexpreopt_config, 'r') as f:
contents = json.load(f)
- module_to_libname[contents['Name']] = contents['ProvidesUsesLibrary']
+ for clc in contents['ClassLoaderContexts']['any']:
+ ulib = clc['Name']
+ if clc['Optional']:
+ optional.append(ulib)
+ else:
+ required.append(ulib)
- return module_to_libname
+ required = remove_duplicates(required)
+ optional = remove_duplicates(optional)
+
+ # If the same library is both in optional and required, prefer required.
+ # This may happen for compatibility libraries, e.g. org.apache.http.legacy.
+ for lib in required:
+ if lib in optional:
+ optional.remove(lib)
+
+ return required, optional
def translate_libnames(modules, module_to_libname):
"""Translate module names into library names using the mapping."""
- if modules is None:
- modules = []
libnames = []
for name in modules:
@@ -346,10 +383,7 @@
# `optional_uses_libs`, `LOCAL_USES_LIBRARIES`,
# `LOCAL_OPTIONAL_LIBRARY_NAMES` all contain module names), while
# the manifest addresses libraries by their name.
- mod_to_lib = load_dexpreopt_configs(args.dexpreopt_configs)
- required = translate_libnames(args.uses_libraries, mod_to_lib)
- optional = translate_libnames(args.optional_uses_libraries,
- mod_to_lib)
+ required, optional = load_dexpreopt_configs(args)
# Check if the <uses-library> lists in the build system agree with
# those in the manifest. Raise an exception on mismatch, unless the