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)