From 0aba252e157457143b455511f1f811107015c3c1 Mon Sep 17 00:00:00 2001 From: Ulya Trafimovich Date: Wed, 3 Mar 2021 16:38:37 +0000 Subject: [PATCH] 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 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 --- dexpreopt/dexpreopt.go | 24 ++--- java/app.go | 54 +++++------ java/app_test.go | 14 +-- scripts/manifest_check.py | 159 ++++++++++++++++++------------ scripts/manifest_check_test.py | 170 +++++++++++++++++++++------------ 5 files changed, 248 insertions(+), 173 deletions(-) diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go index fdb00bd89..4999bc77e 100644 --- a/dexpreopt/dexpreopt.go +++ b/dexpreopt/dexpreopt.go @@ -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) diff --git a/java/app.go b/java/app.go index eef627c14..0c1c717a6 100755 --- a/java/app.go +++ b/java/app.go @@ -1281,10 +1281,13 @@ func (u *usesLibrary) freezeEnforceUsesLibraries() { u.usesLibraryProperties.Enforce_uses_libs = &enforce } -// verifyUsesLibrariesManifest checks the 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 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 ") +} +// verifyUsesLibrariesManifest checks the 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 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 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 ") - return outputFile } diff --git a/java/app_test.go b/java/app_test.go index 78e1a57a9..040ca1925 100644 --- a/java/app_test.go +++ b/java/app_test.go @@ -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 diff --git a/scripts/manifest_check.py b/scripts/manifest_check.py index 0eb1b7631..973a6755b 100755 --- a/scripts/manifest_check.py +++ b/scripts/manifest_check.py @@ -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 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 tags in the manifest match those provided + by the build system. Args: - doc: The XML document. - uses_libraries: The names of tags known to the build system - optional_uses_libraries: The names of 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 tags') - elif not elems: - if uses_libraries or optional_uses_libraries: - raise ManifestMismatchError('no 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 tag in the manifest. - uses_libraries: the names of expected tags. - optional_uses_libraries: the names of expected 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 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 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 tag in the manifest. - """ +def extract_uses_libs_apk(badging): + """Extract 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 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 tags') + elif not elems: + if uses_libraries or optional_uses_libraries: + raise ManifestMismatchError('no 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 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: diff --git a/scripts/manifest_check_test.py b/scripts/manifest_check_test.py index 56c2d9ed1..635ba9d1d 100755 --- a/scripts/manifest_check_test.py +++ b/scripts/manifest_check_test.py @@ -25,28 +25,38 @@ import manifest_check sys.dont_write_bytecode = True -def uses_library(name, attr=''): +def uses_library_xml(name, attr=''): return '' % (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 = ( '\n' '\n' ' \n' @@ -54,115 +64,155 @@ class EnforceUsesLibrariesTest(unittest.TestCase): ' \n' '\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 = ( - '\n' - '\n' - ' \n' - '\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 = ( '\n' '\n' - ' \n' + ' \n' '\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)