Reimplement verify_uses_libraries.sh in manifest_check.py.

Previously there were two different scripts that did similar things:
1) build/soong/scripts/manifest_check.py
2) build/make/core/verify_uses_libraries.sh

Both scripts extracted <uses-library> tags and `targetSdkVersion` from
the manifests of Java modules, but 1) worked for XML manifests, and 2)
worked for APKs. This CL reimplements the functionality from 2) in 1),
so that one script can handle both XML manifests and APKs.

Bug: 132357300

Test: lunch cf_x86_64_phone-userdebug && m && launch_cvd \
      adb wait-for-device && adb root && adb logcat \
      | grep -E 'ClassLoaderContext [a-z ]+ mismatch'
      # empty grep output, no errors

Change-Id: Id1b66e4f3f30f307dba70cb111c7571762cb546a
This commit is contained in:
Ulya Trafimovich 2021-03-03 16:38:37 +00:00
parent 2338d6f6e4
commit 0aba252e15
5 changed files with 248 additions and 173 deletions

View File

@ -261,21 +261,17 @@ func dexpreoptCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, g
} else if module.EnforceUsesLibraries {
// Generate command that saves target SDK version in a shell variable.
if module.ManifestPath != nil {
rule.Command().Text(`target_sdk_version="$(`).
Tool(globalSoong.ManifestCheck).
Flag("--extract-target-sdk-version").
Input(module.ManifestPath).
Text(`)"`)
} else {
// No manifest to extract targetSdkVersion from, hope that DexJar is an APK
rule.Command().Text(`target_sdk_version="$(`).
Tool(globalSoong.Aapt).
Flag("dump badging").
Input(module.DexPath).
Text(`| grep "targetSdkVersion" | sed -n "s/targetSdkVersion:'\(.*\)'/\1/p"`).
Text(`)"`)
manifestOrApk := module.ManifestPath
if manifestOrApk == nil {
// No manifest to extract targetSdkVersion from, hope that dexjar is an APK.
manifestOrApk = module.DexPath
}
rule.Command().Text(`target_sdk_version="$(`).
Tool(globalSoong.ManifestCheck).
Flag("--extract-target-sdk-version").
Input(manifestOrApk).
FlagWithInput("--aapt ", ctx.Config().HostToolPath(ctx, "aapt")).
Text(`)"`)
// Generate command that saves host and target class loader context in shell variables.
clc, paths := ComputeClassLoaderContext(module.ClassLoaderContexts)

View File

@ -1281,10 +1281,13 @@ func (u *usesLibrary) freezeEnforceUsesLibraries() {
u.usesLibraryProperties.Enforce_uses_libs = &enforce
}
// verifyUsesLibrariesManifest checks the <uses-library> tags in an AndroidManifest.xml against the ones specified
// in the uses_libs and optional_uses_libs properties. It returns the path to a copy of the manifest.
func (u *usesLibrary) verifyUsesLibrariesManifest(ctx android.ModuleContext, manifest android.Path) android.Path {
outputFile := android.PathForModuleOut(ctx, "manifest_check", "AndroidManifest.xml")
// verifyUsesLibraries checks the <uses-library> tags in the manifest against the ones specified
// in the `uses_libs`/`optional_uses_libs` properties. The input can be either an XML manifest, or
// an APK with the manifest embedded in it (manifest_check will know which one it is by the file
// extension: APKs are supposed to end with '.apk').
func (u *usesLibrary) verifyUsesLibraries(ctx android.ModuleContext, inputFile android.Path,
outputFile android.WritablePath) {
statusFile := dexpreopt.UsesLibrariesStatusFile(ctx)
// Disable verify_uses_libraries check if dexpreopt is globally disabled. Without dexpreopt the
@ -1292,15 +1295,19 @@ func (u *usesLibrary) verifyUsesLibrariesManifest(ctx android.ModuleContext, man
// non-linux build platforms where dexpreopt is generally disabled (the check may fail due to
// various unrelated reasons, such as a failure to get manifest from an APK).
if dexpreopt.GetGlobalConfig(ctx).DisablePreopt {
return manifest
return
}
rule := android.NewRuleBuilder(pctx, ctx)
cmd := rule.Command().BuiltTool("manifest_check").
Flag("--enforce-uses-libraries").
Input(manifest).
Input(inputFile).
FlagWithOutput("--enforce-uses-libraries-status ", statusFile).
FlagWithOutput("-o ", outputFile)
FlagWithInput("--aapt ", ctx.Config().HostToolPath(ctx, "aapt"))
if outputFile != nil {
cmd.FlagWithOutput("-o ", outputFile)
}
if dexpreopt.GetGlobalConfig(ctx).RelaxUsesLibraryCheck {
cmd.Flag("--enforce-uses-libraries-relax")
@ -1315,35 +1322,20 @@ func (u *usesLibrary) verifyUsesLibrariesManifest(ctx android.ModuleContext, man
}
rule.Build("verify_uses_libraries", "verify <uses-library>")
}
// verifyUsesLibrariesManifest checks the <uses-library> tags in an AndroidManifest.xml against
// the build system and returns the path to a copy of the manifest.
func (u *usesLibrary) verifyUsesLibrariesManifest(ctx android.ModuleContext, manifest android.Path) android.Path {
outputFile := android.PathForModuleOut(ctx, "manifest_check", "AndroidManifest.xml")
u.verifyUsesLibraries(ctx, manifest, outputFile)
return outputFile
}
// verifyUsesLibrariesAPK checks the <uses-library> tags in the manifest of an APK against the ones specified
// in the uses_libs and optional_uses_libs properties. It returns the path to a copy of the APK.
// verifyUsesLibrariesAPK checks the <uses-library> tags in the manifest of an APK against the build
// system and returns the path to a copy of the APK.
func (u *usesLibrary) verifyUsesLibrariesAPK(ctx android.ModuleContext, apk android.Path) android.Path {
u.verifyUsesLibraries(ctx, apk, nil) // for APKs manifest_check does not write output file
outputFile := android.PathForModuleOut(ctx, "verify_uses_libraries", apk.Base())
statusFile := dexpreopt.UsesLibrariesStatusFile(ctx)
// Disable verify_uses_libraries check if dexpreopt is globally disabled. Without dexpreopt the
// check is not necessary, and although it is good to have, it is difficult to maintain on
// non-linux build platforms where dexpreopt is generally disabled (the check may fail due to
// various unrelated reasons, such as a failure to get manifest from an APK).
if dexpreopt.GetGlobalConfig(ctx).DisablePreopt {
return apk
}
rule := android.NewRuleBuilder(pctx, ctx)
aapt := ctx.Config().HostToolPath(ctx, "aapt")
rule.Command().
Textf("aapt_binary=%s", aapt.String()).Implicit(aapt).
Textf(`uses_library_names="%s"`, strings.Join(u.usesLibraryProperties.Uses_libs, " ")).
Textf(`optional_uses_library_names="%s"`, strings.Join(u.usesLibraryProperties.Optional_uses_libs, " ")).
Textf(`relax_check="%t"`, dexpreopt.GetGlobalConfig(ctx).RelaxUsesLibraryCheck).
Tool(android.PathForSource(ctx, "build/make/core/verify_uses_libraries.sh")).Input(apk).Output(statusFile)
rule.Command().Text("cp -f").Input(apk).Output(outputFile)
rule.Build("verify_uses_libraries", "verify <uses-library>")
return outputFile
}

View File

@ -2405,13 +2405,13 @@ func TestUsesLibraries(t *testing.T) {
// Test that all libraries are verified for an APK (library order matters).
verifyApkCmd := prebuilt.Rule("verify_uses_libraries").RuleParams.Command
verifyApkReqLibs := `uses_library_names="foo com.non.sdk.lib android.test.runner"`
verifyApkOptLibs := `optional_uses_library_names="bar baz"`
if !strings.Contains(verifyApkCmd, verifyApkReqLibs) {
t.Errorf("wanted %q in %q", verifyApkReqLibs, verifyApkCmd)
}
if !strings.Contains(verifyApkCmd, verifyApkOptLibs) {
t.Errorf("wanted %q in %q", verifyApkOptLibs, verifyApkCmd)
verifyApkArgs := `--uses-library foo ` +
`--uses-library com.non.sdk.lib ` +
`--uses-library android.test.runner ` +
`--optional-uses-library bar ` +
`--optional-uses-library baz `
if !strings.Contains(verifyApkCmd, verifyApkArgs) {
t.Errorf("wanted %q in %q", verifyApkArgs, verifyApkCmd)
}
// Test that all present libraries are preopted, including implicit SDK dependencies, possibly stubs

View File

@ -19,6 +19,8 @@
from __future__ import print_function
import argparse
import re
import subprocess
import sys
from xml.dom import minidom
@ -59,64 +61,44 @@ def parse_args():
dest='extract_target_sdk_version',
action='store_true',
help='print the targetSdkVersion from the manifest')
parser.add_argument('--aapt',
dest='aapt',
help='path to aapt executable')
parser.add_argument('--output', '-o', dest='output', help='output AndroidManifest.xml file')
parser.add_argument('input', help='input AndroidManifest.xml file')
return parser.parse_args()
def enforce_uses_libraries(doc, uses_libraries, optional_uses_libraries, relax):
"""Verify that the <uses-library> tags in the manifest match those provided by the build system.
def enforce_uses_libraries(manifest, required, optional, relax, is_apk = False):
"""Verify that the <uses-library> tags in the manifest match those provided
by the build system.
Args:
doc: The XML document.
uses_libraries: The names of <uses-library> tags known to the build system
optional_uses_libraries: The names of <uses-library> tags with required:fals
known to the build system
Raises:
RuntimeError: Invalid manifest
ManifestMismatchError: Manifest does not match
manifest: manifest (either parsed XML or aapt dump of APK)
required: required libs known to the build system
optional: optional libs known to the build system
relax: if true, suppress error on mismatch and just write it to file
is_apk: if the manifest comes from an APK or an XML file
"""
if is_apk:
manifest_required, manifest_optional = extract_uses_libs_apk(manifest)
else:
manifest_required, manifest_optional = extract_uses_libs_xml(manifest)
manifest = parse_manifest(doc)
elems = get_children_with_tag(manifest, 'application')
application = elems[0] if len(elems) == 1 else None
if len(elems) > 1:
raise RuntimeError('found multiple <application> tags')
elif not elems:
if uses_libraries or optional_uses_libraries:
raise ManifestMismatchError('no <application> tag found')
return
if required is None:
required = []
return verify_uses_library(application, uses_libraries, optional_uses_libraries, relax)
def verify_uses_library(application, uses_libraries, optional_uses_libraries, relax):
"""Verify that the uses-library values known to the build system match the manifest.
Args:
application: the <application> tag in the manifest.
uses_libraries: the names of expected <uses-library> tags.
optional_uses_libraries: the names of expected <uses-library> tags with required="false".
Raises:
ManifestMismatchError: Manifest does not match
"""
if uses_libraries is None:
uses_libraries = []
if optional_uses_libraries is None:
optional_uses_libraries = []
manifest_uses_libraries, manifest_optional_uses_libraries = parse_uses_library(application)
if optional is None:
optional = []
err = []
if manifest_uses_libraries != uses_libraries:
if manifest_required != required:
err.append('Expected required <uses-library> tags "%s", got "%s"' %
(', '.join(uses_libraries), ', '.join(manifest_uses_libraries)))
(', '.join(required), ', '.join(manifest_required)))
if manifest_optional_uses_libraries != optional_uses_libraries:
if manifest_optional != optional:
err.append('Expected optional <uses-library> tags "%s", got "%s"' %
(', '.join(optional_uses_libraries), ', '.join(manifest_optional_uses_libraries)))
(', '.join(optional), ', '.join(manifest_optional)))
if err:
errmsg = '\n'.join(err)
@ -126,19 +108,43 @@ def verify_uses_library(application, uses_libraries, optional_uses_libraries, re
return None
def parse_uses_library(application):
"""Extract uses-library tags from the manifest.
Args:
application: the <application> tag in the manifest.
"""
def extract_uses_libs_apk(badging):
"""Extract <uses-library> tags from the manifest of an APK."""
pattern = re.compile("^uses-library(-not-required)?:'(.*)'$", re.MULTILINE)
required = []
optional = []
for match in re.finditer(pattern, badging):
libname = match.group(2)
if match.group(1) == None:
required.append(libname)
else:
optional.append(libname)
return first_unique_elements(required), first_unique_elements(optional)
def extract_uses_libs_xml(xml):
"""Extract <uses-library> tags from the manifest."""
manifest = parse_manifest(xml)
elems = get_children_with_tag(manifest, 'application')
application = elems[0] if len(elems) == 1 else None
if len(elems) > 1:
raise RuntimeError('found multiple <application> tags')
elif not elems:
if uses_libraries or optional_uses_libraries:
raise ManifestMismatchError('no <application> tag found')
return
libs = get_children_with_tag(application, 'uses-library')
uses_libraries = [uses_library_name(x) for x in libs if uses_library_required(x)]
optional_uses_libraries = [uses_library_name(x) for x in libs if not uses_library_required(x)]
required = [uses_library_name(x) for x in libs if uses_library_required(x)]
optional = [uses_library_name(x) for x in libs if not uses_library_required(x)]
return first_unique_elements(uses_libraries), first_unique_elements(optional_uses_libraries)
return first_unique_elements(required), first_unique_elements(optional)
def first_unique_elements(l):
@ -167,16 +173,34 @@ def uses_library_required(lib):
return (required.value == 'true') if required is not None else True
def extract_target_sdk_version(doc):
def extract_target_sdk_version(manifest, is_apk = False):
"""Returns the targetSdkVersion from the manifest.
Args:
doc: The XML document.
Raises:
RuntimeError: invalid manifest
manifest: manifest (either parsed XML or aapt dump of APK)
is_apk: if the manifest comes from an APK or an XML file
"""
if is_apk:
return extract_target_sdk_version_apk(manifest)
else:
return extract_target_sdk_version_xml(manifest)
manifest = parse_manifest(doc)
def extract_target_sdk_version_apk(badging):
"""Extract targetSdkVersion tags from the manifest of an APK."""
pattern = re.compile("^targetSdkVersion?:'(.*)'$", re.MULTILINE)
for match in re.finditer(pattern, badging):
return match.group(1)
raise RuntimeError('cannot find targetSdkVersion in the manifest')
def extract_target_sdk_version_xml(xml):
"""Extract targetSdkVersion tags from the manifest."""
manifest = parse_manifest(xml)
# Get or insert the uses-sdk element
uses_sdk = get_children_with_tag(manifest, 'uses-sdk')
@ -203,14 +227,22 @@ def main():
try:
args = parse_args()
doc = minidom.parse(args.input)
# The input can be either an XML manifest or an APK, they are parsed and
# processed in different ways.
is_apk = args.input.endswith('.apk')
if is_apk:
aapt = args.aapt if args.aapt != None else "aapt"
manifest = subprocess.check_output([aapt, "dump", "badging", args.input])
else:
manifest = minidom.parse(args.input)
if args.enforce_uses_libraries:
# Check if the <uses-library> lists in the build system agree with those
# in the manifest. Raise an exception on mismatch, unless the script was
# passed a special parameter to suppress exceptions.
errmsg = enforce_uses_libraries(doc, args.uses_libraries,
args.optional_uses_libraries, args.enforce_uses_libraries_relax)
errmsg = enforce_uses_libraries(manifest, args.uses_libraries,
args.optional_uses_libraries, args.enforce_uses_libraries_relax,
is_apk)
# Create a status file that is empty on success, or contains an error
# message on failure. When exceptions are suppressed, dexpreopt command
@ -221,11 +253,16 @@ def main():
f.write("%s\n" % errmsg)
if args.extract_target_sdk_version:
print(extract_target_sdk_version(doc))
print(extract_target_sdk_version(manifest, is_apk))
if args.output:
# XML output is supposed to be written only when this script is invoked
# with XML input manifest, not with an APK.
if is_apk:
raise RuntimeError('cannot save APK manifest as XML')
with open(args.output, 'wb') as f:
write_xml(f, doc)
write_xml(f, manifest)
# pylint: disable=broad-except
except Exception as err:

View File

@ -25,28 +25,38 @@ import manifest_check
sys.dont_write_bytecode = True
def uses_library(name, attr=''):
def uses_library_xml(name, attr=''):
return '<uses-library android:name="%s"%s />' % (name, attr)
def required(value):
def required_xml(value):
return ' android:required="%s"' % ('true' if value else 'false')
def uses_library_apk(name, sfx=''):
return "uses-library%s:'%s'" % (sfx, name)
def required_apk(value):
return '' if value else '-not-required'
class EnforceUsesLibrariesTest(unittest.TestCase):
"""Unit tests for add_extract_native_libs function."""
def run_test(self, input_manifest, uses_libraries=None, optional_uses_libraries=None):
doc = minidom.parseString(input_manifest)
def run_test(self, xml, apk, uses_libraries=[], optional_uses_libraries=[]):
doc = minidom.parseString(xml)
try:
relax = False
manifest_check.enforce_uses_libraries(doc, uses_libraries,
optional_uses_libraries, relax)
optional_uses_libraries, relax, is_apk=False)
manifest_check.enforce_uses_libraries(apk, uses_libraries,
optional_uses_libraries, relax, is_apk=True)
return True
except manifest_check.ManifestMismatchError:
return False
manifest_tmpl = (
xml_tmpl = (
'<?xml version="1.0" encoding="utf-8"?>\n'
'<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n'
' <application>\n'
@ -54,115 +64,155 @@ class EnforceUsesLibrariesTest(unittest.TestCase):
' </application>\n'
'</manifest>\n')
apk_tmpl = (
"package: name='com.google.android.something' versionCode='100'\n"
"sdkVersion:'29'\n"
"targetSdkVersion:'29'\n"
"uses-permission: name='android.permission.ACCESS_NETWORK_STATE'\n"
"%s\n"
"densities: '160' '240' '320' '480' '640' '65534")
def test_uses_library(self):
manifest_input = self.manifest_tmpl % (uses_library('foo'))
matches = self.run_test(manifest_input, uses_libraries=['foo'])
xml = self.xml_tmpl % (uses_library_xml('foo'))
apk = self.apk_tmpl % (uses_library_apk('foo'))
matches = self.run_test(xml, apk, uses_libraries=['foo'])
self.assertTrue(matches)
def test_uses_library_required(self):
manifest_input = self.manifest_tmpl % (uses_library('foo', required(True)))
matches = self.run_test(manifest_input, uses_libraries=['foo'])
xml = self.xml_tmpl % (uses_library_xml('foo', required_xml(True)))
apk = self.apk_tmpl % (uses_library_apk('foo', required_apk(True)))
matches = self.run_test(xml, apk, uses_libraries=['foo'])
self.assertTrue(matches)
def test_optional_uses_library(self):
manifest_input = self.manifest_tmpl % (uses_library('foo', required(False)))
matches = self.run_test(manifest_input, optional_uses_libraries=['foo'])
xml = self.xml_tmpl % (uses_library_xml('foo', required_xml(False)))
apk = self.apk_tmpl % (uses_library_apk('foo', required_apk(False)))
matches = self.run_test(xml, apk, optional_uses_libraries=['foo'])
self.assertTrue(matches)
def test_expected_uses_library(self):
manifest_input = self.manifest_tmpl % (uses_library('foo', required(False)))
matches = self.run_test(manifest_input, uses_libraries=['foo'])
xml = self.xml_tmpl % (uses_library_xml('foo', required_xml(False)))
apk = self.apk_tmpl % (uses_library_apk('foo', required_apk(False)))
matches = self.run_test(xml, apk, uses_libraries=['foo'])
self.assertFalse(matches)
def test_expected_optional_uses_library(self):
manifest_input = self.manifest_tmpl % (uses_library('foo'))
matches = self.run_test(manifest_input, optional_uses_libraries=['foo'])
xml = self.xml_tmpl % (uses_library_xml('foo'))
apk = self.apk_tmpl % (uses_library_apk('foo'))
matches = self.run_test(xml, apk, optional_uses_libraries=['foo'])
self.assertFalse(matches)
def test_missing_uses_library(self):
manifest_input = self.manifest_tmpl % ('')
matches = self.run_test(manifest_input, uses_libraries=['foo'])
xml = self.xml_tmpl % ('')
apk = self.apk_tmpl % ('')
matches = self.run_test(xml, apk, uses_libraries=['foo'])
self.assertFalse(matches)
def test_missing_optional_uses_library(self):
manifest_input = self.manifest_tmpl % ('')
matches = self.run_test(manifest_input, optional_uses_libraries=['foo'])
xml = self.xml_tmpl % ('')
apk = self.apk_tmpl % ('')
matches = self.run_test(xml, apk, optional_uses_libraries=['foo'])
self.assertFalse(matches)
def test_extra_uses_library(self):
manifest_input = self.manifest_tmpl % (uses_library('foo'))
matches = self.run_test(manifest_input)
xml = self.xml_tmpl % (uses_library_xml('foo'))
apk = self.apk_tmpl % (uses_library_xml('foo'))
matches = self.run_test(xml, apk)
self.assertFalse(matches)
def test_extra_optional_uses_library(self):
manifest_input = self.manifest_tmpl % (uses_library('foo', required(False)))
matches = self.run_test(manifest_input)
xml = self.xml_tmpl % (uses_library_xml('foo', required_xml(False)))
apk = self.apk_tmpl % (uses_library_apk('foo', required_apk(False)))
matches = self.run_test(xml, apk)
self.assertFalse(matches)
def test_multiple_uses_library(self):
manifest_input = self.manifest_tmpl % ('\n'.join([uses_library('foo'),
uses_library('bar')]))
matches = self.run_test(manifest_input, uses_libraries=['foo', 'bar'])
xml = self.xml_tmpl % ('\n'.join([uses_library_xml('foo'),
uses_library_xml('bar')]))
apk = self.apk_tmpl % ('\n'.join([uses_library_apk('foo'),
uses_library_apk('bar')]))
matches = self.run_test(xml, apk, uses_libraries=['foo', 'bar'])
self.assertTrue(matches)
def test_multiple_optional_uses_library(self):
manifest_input = self.manifest_tmpl % ('\n'.join([uses_library('foo', required(False)),
uses_library('bar', required(False))]))
matches = self.run_test(manifest_input, optional_uses_libraries=['foo', 'bar'])
xml = self.xml_tmpl % ('\n'.join([uses_library_xml('foo', required_xml(False)),
uses_library_xml('bar', required_xml(False))]))
apk = self.apk_tmpl % ('\n'.join([uses_library_apk('foo', required_apk(False)),
uses_library_apk('bar', required_apk(False))]))
matches = self.run_test(xml, apk, optional_uses_libraries=['foo', 'bar'])
self.assertTrue(matches)
def test_order_uses_library(self):
manifest_input = self.manifest_tmpl % ('\n'.join([uses_library('foo'),
uses_library('bar')]))
matches = self.run_test(manifest_input, uses_libraries=['bar', 'foo'])
xml = self.xml_tmpl % ('\n'.join([uses_library_xml('foo'),
uses_library_xml('bar')]))
apk = self.apk_tmpl % ('\n'.join([uses_library_apk('foo'),
uses_library_apk('bar')]))
matches = self.run_test(xml, apk, uses_libraries=['bar', 'foo'])
self.assertFalse(matches)
def test_order_optional_uses_library(self):
manifest_input = self.manifest_tmpl % ('\n'.join([uses_library('foo', required(False)),
uses_library('bar', required(False))]))
matches = self.run_test(manifest_input, optional_uses_libraries=['bar', 'foo'])
xml = self.xml_tmpl % ('\n'.join([uses_library_xml('foo', required_xml(False)),
uses_library_xml('bar', required_xml(False))]))
apk = self.apk_tmpl % ('\n'.join([uses_library_apk('foo', required_apk(False)),
uses_library_apk('bar', required_apk(False))]))
matches = self.run_test(xml, apk, optional_uses_libraries=['bar', 'foo'])
self.assertFalse(matches)
def test_duplicate_uses_library(self):
manifest_input = self.manifest_tmpl % ('\n'.join([uses_library('foo'),
uses_library('foo')]))
matches = self.run_test(manifest_input, uses_libraries=['foo'])
xml = self.xml_tmpl % ('\n'.join([uses_library_xml('foo'),
uses_library_xml('foo')]))
apk = self.apk_tmpl % ('\n'.join([uses_library_apk('foo'),
uses_library_apk('foo')]))
matches = self.run_test(xml, apk, uses_libraries=['foo'])
self.assertTrue(matches)
def test_duplicate_optional_uses_library(self):
manifest_input = self.manifest_tmpl % ('\n'.join([uses_library('foo', required(False)),
uses_library('foo', required(False))]))
matches = self.run_test(manifest_input, optional_uses_libraries=['foo'])
xml = self.xml_tmpl % ('\n'.join([uses_library_xml('foo', required_xml(False)),
uses_library_xml('foo', required_xml(False))]))
apk = self.apk_tmpl % ('\n'.join([uses_library_apk('foo', required_apk(False)),
uses_library_apk('foo', required_apk(False))]))
matches = self.run_test(xml, apk, optional_uses_libraries=['foo'])
self.assertTrue(matches)
def test_mixed(self):
manifest_input = self.manifest_tmpl % ('\n'.join([uses_library('foo'),
uses_library('bar', required(False))]))
matches = self.run_test(manifest_input, uses_libraries=['foo'],
xml = self.xml_tmpl % ('\n'.join([uses_library_xml('foo'),
uses_library_xml('bar', required_xml(False))]))
apk = self.apk_tmpl % ('\n'.join([uses_library_apk('foo'),
uses_library_apk('bar', required_apk(False))]))
matches = self.run_test(xml, apk, uses_libraries=['foo'],
optional_uses_libraries=['bar'])
self.assertTrue(matches)
class ExtractTargetSdkVersionTest(unittest.TestCase):
def test_target_sdk_version(self):
manifest = (
'<?xml version="1.0" encoding="utf-8"?>\n'
'<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n'
' <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="29" />\n'
'</manifest>\n')
doc = minidom.parseString(manifest)
target_sdk_version = manifest_check.extract_target_sdk_version(doc)
self.assertEqual(target_sdk_version, '29')
def run_test(self, xml, apk, version):
doc = minidom.parseString(xml)
v = manifest_check.extract_target_sdk_version(doc, is_apk=False)
self.assertEqual(v, version)
v = manifest_check.extract_target_sdk_version(apk, is_apk=True)
self.assertEqual(v, version)
def test_min_sdk_version(self):
manifest = (
xml_tmpl = (
'<?xml version="1.0" encoding="utf-8"?>\n'
'<manifest xmlns:android="http://schemas.android.com/apk/res/android">\n'
' <uses-sdk android:minSdkVersion="28" />\n'
' <uses-sdk android:minSdkVersion="28" android:targetSdkVersion="%s" />\n'
'</manifest>\n')
doc = minidom.parseString(manifest)
target_sdk_version = manifest_check.extract_target_sdk_version(doc)
self.assertEqual(target_sdk_version, '28')
apk_tmpl = (
"package: name='com.google.android.something' versionCode='100'\n"
"sdkVersion:'28'\n"
"targetSdkVersion:'%s'\n"
"uses-permission: name='android.permission.ACCESS_NETWORK_STATE'\n")
def test_targert_sdk_version_28(self):
xml = self.xml_tmpl % "28"
apk = self.apk_tmpl % "28"
self.run_test(xml, apk, "28")
def test_targert_sdk_version_29(self):
xml = self.xml_tmpl % "29"
apk = self.apk_tmpl % "29"
self.run_test(xml, apk, "29")
if __name__ == '__main__':
unittest.main(verbosity=2)