From 75d65f360c18806e683aed87679ce5bcc52823d4 Mon Sep 17 00:00:00 2001 From: Gurpreet Singh Date: Mon, 24 Jan 2022 17:44:05 +0000 Subject: [PATCH] Add testOnly attribute to AndroidManifest file of apex_test If the build file contains the apex_test module, add the testOnly attribute to the application element of the corresponding AndroidManifest file and set its value to true. If the testOnly attribute is already present and has value false, then do nothing. Tests added in manifest_fixer_test.py to check if the updated AndroidManifest file has the testOnly attribute set to true or not. Bug: 213310150 Test: atest --host manifest_fixer_test Test: m nothing Test: manually checked the AndroidManifest file generated Change-Id: I36247dbe0261c342d451a4422c314fd8fe0c2369 --- apex/builder.go | 21 ++++++ java/aar.go | 16 ++++- java/android_manifest.go | 125 +++++++++++++++++++-------------- java/app_test.go | 6 +- scripts/manifest_fixer.py | 26 +++++++ scripts/manifest_fixer_test.py | 45 +++++++++++- 6 files changed, 181 insertions(+), 58 deletions(-) diff --git a/apex/builder.go b/apex/builder.go index a66e1e0fc..1a1f22be2 100644 --- a/apex/builder.go +++ b/apex/builder.go @@ -397,6 +397,22 @@ func (a *apexBundle) buildBundleConfig(ctx android.ModuleContext) android.Output return output.OutputPath } +func markManifestTestOnly(ctx android.ModuleContext, androidManifestFile android.Path) android.Path { + return java.ManifestFixer(java.ManifestFixerParams{ + Ctx: ctx, + Manifest: androidManifestFile, + SdkContext: nil, + ClassLoaderContexts: nil, + IsLibrary: false, + UseEmbeddedNativeLibs: false, + UsesNonSdkApis: false, + UseEmbeddedDex: false, + HasNoCode: false, + TestOnly: true, + LoggingParent: "", + }) +} + // buildUnflattendApex creates build rules to build an APEX using apexer. func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) { apexType := a.properties.ApexType @@ -595,6 +611,11 @@ func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext) { if a.properties.AndroidManifest != nil { androidManifestFile := android.PathForModuleSrc(ctx, proptools.String(a.properties.AndroidManifest)) + + if a.testApex { + androidManifestFile = markManifestTestOnly(ctx, androidManifestFile) + } + implicitInputs = append(implicitInputs, androidManifestFile) optFlags = append(optFlags, "--android_manifest "+androidManifestFile.String()) } diff --git a/java/aar.go b/java/aar.go index aabbec6a3..4687424c2 100644 --- a/java/aar.go +++ b/java/aar.go @@ -276,9 +276,19 @@ func (a *aapt) buildActions(ctx android.ModuleContext, sdkContext android.SdkCon manifestFile := proptools.StringDefault(a.aaptProperties.Manifest, "AndroidManifest.xml") manifestSrcPath := android.PathForModuleSrc(ctx, manifestFile) - manifestPath := manifestFixer(ctx, manifestSrcPath, sdkContext, classLoaderContexts, - a.isLibrary, a.useEmbeddedNativeLibs, a.usesNonSdkApis, a.useEmbeddedDex, a.hasNoCode, - a.LoggingParent) + manifestPath := ManifestFixer(ManifestFixerParams{ + Ctx: ctx, + Manifest: manifestSrcPath, + SdkContext: sdkContext, + ClassLoaderContexts: classLoaderContexts, + IsLibrary: a.isLibrary, + UseEmbeddedNativeLibs: a.useEmbeddedNativeLibs, + UsesNonSdkApis: a.usesNonSdkApis, + UseEmbeddedDex: a.useEmbeddedDex, + HasNoCode: a.hasNoCode, + TestOnly: false, + LoggingParent: a.LoggingParent, + }) // Add additional manifest files to transitive manifests. additionalManifests := android.PathsForModuleSrc(ctx, a.aaptProperties.Additional_manifests) diff --git a/java/android_manifest.go b/java/android_manifest.go index f29d8ad1a..a5d5b97a0 100644 --- a/java/android_manifest.go +++ b/java/android_manifest.go @@ -28,13 +28,10 @@ import ( var manifestFixerRule = pctx.AndroidStaticRule("manifestFixer", blueprint.RuleParams{ Command: `${config.ManifestFixerCmd} ` + - `--minSdkVersion ${minSdkVersion} ` + - `--targetSdkVersion ${targetSdkVersion} ` + - `--raise-min-sdk-version ` + `$args $in $out`, CommandDeps: []string{"${config.ManifestFixerCmd}"}, }, - "minSdkVersion", "targetSdkVersion", "args") + "args") var manifestMergerRule = pctx.AndroidStaticRule("manifestMerger", blueprint.RuleParams{ @@ -58,84 +55,110 @@ func targetSdkVersionForManifestFixer(ctx android.ModuleContext, sdkContext andr return targetSdkVersion } -// Uses manifest_fixer.py to inject minSdkVersion, etc. into an AndroidManifest.xml -func manifestFixer(ctx android.ModuleContext, manifest android.Path, sdkContext android.SdkContext, - classLoaderContexts dexpreopt.ClassLoaderContextMap, isLibrary, useEmbeddedNativeLibs, usesNonSdkApis, - useEmbeddedDex, hasNoCode bool, loggingParent string) android.Path { +type ManifestFixerParams struct { + Ctx android.ModuleContext + Manifest android.Path + SdkContext android.SdkContext + ClassLoaderContexts dexpreopt.ClassLoaderContextMap + IsLibrary bool + UseEmbeddedNativeLibs bool + UsesNonSdkApis bool + UseEmbeddedDex bool + HasNoCode bool + TestOnly bool + LoggingParent string +} +// Uses manifest_fixer.py to inject minSdkVersion, etc. into an AndroidManifest.xml +func ManifestFixer(params ManifestFixerParams) android.Path { var args []string - if isLibrary { + + if params.IsLibrary { args = append(args, "--library") - } else { - minSdkVersion, err := sdkContext.MinSdkVersion(ctx).EffectiveVersion(ctx) + } else if params.SdkContext != nil { + minSdkVersion, err := params.SdkContext.MinSdkVersion(params.Ctx).EffectiveVersion(params.Ctx) if err != nil { - ctx.ModuleErrorf("invalid minSdkVersion: %s", err) + params.Ctx.ModuleErrorf("invalid minSdkVersion: %s", err) } if minSdkVersion.FinalOrFutureInt() >= 23 { - args = append(args, fmt.Sprintf("--extract-native-libs=%v", !useEmbeddedNativeLibs)) - } else if useEmbeddedNativeLibs { - ctx.ModuleErrorf("module attempted to store uncompressed native libraries, but minSdkVersion=%d doesn't support it", + args = append(args, fmt.Sprintf("--extract-native-libs=%v", !params.UseEmbeddedNativeLibs)) + } else if params.UseEmbeddedNativeLibs { + params.Ctx.ModuleErrorf("module attempted to store uncompressed native libraries, but minSdkVersion=%d doesn't support it", minSdkVersion) } } - if usesNonSdkApis { + if params.UsesNonSdkApis { args = append(args, "--uses-non-sdk-api") } - if useEmbeddedDex { + if params.UseEmbeddedDex { args = append(args, "--use-embedded-dex") } - // manifest_fixer should add only the implicit SDK libraries inferred by Soong, not those added - // explicitly via `uses_libs`/`optional_uses_libs`. - requiredUsesLibs, optionalUsesLibs := classLoaderContexts.ImplicitUsesLibs() - for _, usesLib := range requiredUsesLibs { - args = append(args, "--uses-library", usesLib) - } - for _, usesLib := range optionalUsesLibs { - args = append(args, "--optional-uses-library", usesLib) + if params.ClassLoaderContexts != nil { + // manifest_fixer should add only the implicit SDK libraries inferred by Soong, not those added + // explicitly via `uses_libs`/`optional_uses_libs`. + requiredUsesLibs, optionalUsesLibs := params.ClassLoaderContexts.ImplicitUsesLibs() + + for _, usesLib := range requiredUsesLibs { + args = append(args, "--uses-library", usesLib) + } + for _, usesLib := range optionalUsesLibs { + args = append(args, "--optional-uses-library", usesLib) + } } - if hasNoCode { + if params.HasNoCode { args = append(args, "--has-no-code") } - if loggingParent != "" { - args = append(args, "--logging-parent", loggingParent) + if params.TestOnly { + args = append(args, "--test-only") + } + + if params.LoggingParent != "" { + args = append(args, "--logging-parent", params.LoggingParent) } var deps android.Paths - targetSdkVersion := targetSdkVersionForManifestFixer(ctx, sdkContext) + var argsMapper = make(map[string]string) - if UseApiFingerprint(ctx) && ctx.ModuleName() != "framework-res" { - targetSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String()) - deps = append(deps, ApiFingerprintPath(ctx)) + if params.SdkContext != nil { + targetSdkVersion := targetSdkVersionForManifestFixer(params.Ctx, params.SdkContext) + args = append(args, "--targetSdkVersion ", targetSdkVersion) + + if UseApiFingerprint(params.Ctx) && params.Ctx.ModuleName() != "framework-res" { + targetSdkVersion = params.Ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(params.Ctx).String()) + deps = append(deps, ApiFingerprintPath(params.Ctx)) + } + + minSdkVersion, err := params.SdkContext.MinSdkVersion(params.Ctx).EffectiveVersionString(params.Ctx) + if err != nil { + params.Ctx.ModuleErrorf("invalid minSdkVersion: %s", err) + } + + if UseApiFingerprint(params.Ctx) && params.Ctx.ModuleName() != "framework-res" { + minSdkVersion = params.Ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(params.Ctx).String()) + deps = append(deps, ApiFingerprintPath(params.Ctx)) + } + + if err != nil { + params.Ctx.ModuleErrorf("invalid minSdkVersion: %s", err) + } + args = append(args, "--minSdkVersion ", minSdkVersion) + args = append(args, "--raise-min-sdk-version") } - minSdkVersion, err := sdkContext.MinSdkVersion(ctx).EffectiveVersionString(ctx) - if err != nil { - ctx.ModuleErrorf("invalid minSdkVersion: %s", err) - } - if UseApiFingerprint(ctx) && ctx.ModuleName() != "framework-res" { - minSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", ApiFingerprintPath(ctx).String()) - deps = append(deps, ApiFingerprintPath(ctx)) - } + fixedManifest := android.PathForModuleOut(params.Ctx, "manifest_fixer", "AndroidManifest.xml") + argsMapper["args"] = strings.Join(args, " ") - fixedManifest := android.PathForModuleOut(ctx, "manifest_fixer", "AndroidManifest.xml") - if err != nil { - ctx.ModuleErrorf("invalid minSdkVersion: %s", err) - } - ctx.Build(pctx, android.BuildParams{ + params.Ctx.Build(pctx, android.BuildParams{ Rule: manifestFixerRule, Description: "fix manifest", - Input: manifest, + Input: params.Manifest, Implicits: deps, Output: fixedManifest, - Args: map[string]string{ - "minSdkVersion": minSdkVersion, - "targetSdkVersion": targetSdkVersion, - "args": strings.Join(args, " "), - }, + Args: argsMapper, }) return fixedManifest.WithoutRel() diff --git a/java/app_test.go b/java/app_test.go index 2322ef44e..16bbec158 100644 --- a/java/app_test.go +++ b/java/app_test.go @@ -2512,7 +2512,7 @@ func TestUsesLibraries(t *testing.T) { `--uses-library qux ` + `--uses-library quuz ` + `--uses-library runtime-library` - android.AssertStringEquals(t, "manifest_fixer args", expectManifestFixerArgs, actualManifestFixerArgs) + android.AssertStringDoesContain(t, "manifest_fixer args", actualManifestFixerArgs, expectManifestFixerArgs) // Test that all libraries are verified (library order matters). verifyCmd := app.Rule("verify_uses_libraries").RuleParams.Command @@ -3055,7 +3055,7 @@ func TestTargetSdkVersionManifestFixer(t *testing.T) { result := fixture.RunTestWithBp(t, bp) foo := result.ModuleForTests("foo", "android_common") - manifestFixerArgs := foo.Output("manifest_fixer/AndroidManifest.xml").Args - android.AssertStringEquals(t, testCase.name, testCase.targetSdkVersionExpected, manifestFixerArgs["targetSdkVersion"]) + manifestFixerArgs := foo.Output("manifest_fixer/AndroidManifest.xml").Args["args"] + android.AssertStringDoesContain(t, testCase.name, manifestFixerArgs, "--targetSdkVersion "+testCase.targetSdkVersionExpected) } } diff --git a/scripts/manifest_fixer.py b/scripts/manifest_fixer.py index d80a617a1..2d3103b43 100755 --- a/scripts/manifest_fixer.py +++ b/scripts/manifest_fixer.py @@ -65,6 +65,9 @@ def parse_args(): parser.add_argument('--has-no-code', dest='has_no_code', action='store_true', help=('adds hasCode="false" attribute to application. Ignored if application elem ' 'already has a hasCode attribute.')) + parser.add_argument('--test-only', dest='test_only', action='store_true', + help=('adds testOnly="true" attribute to application. Assign true value if application elem ' + 'already has a testOnly attribute.')) parser.add_argument('input', help='input AndroidManifest.xml file') parser.add_argument('output', help='output AndroidManifest.xml file') return parser.parse_args() @@ -318,6 +321,26 @@ def set_has_code_to_false(doc): attr.value = 'false' application.setAttributeNode(attr) +def set_test_only_flag_to_true(doc): + 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: + application = doc.createElement('application') + indent = get_indent(manifest.firstChild, 1) + first = manifest.firstChild + manifest.insertBefore(doc.createTextNode(indent), first) + manifest.insertBefore(application, first) + + attr = application.getAttributeNodeNS(android_ns, 'testOnly') + if attr is not None: + # Do nothing If the application already has a testOnly attribute. + return + attr = doc.createAttributeNS(android_ns, 'android:testOnly') + attr.value = 'true' + application.setAttributeNode(attr) def main(): """Program entry point.""" @@ -349,6 +372,9 @@ def main(): if args.has_no_code: set_has_code_to_false(doc) + if args.test_only: + set_test_only_flag_to_true(doc) + if args.extract_native_libs is not None: add_extract_native_libs(doc, args.extract_native_libs) diff --git a/scripts/manifest_fixer_test.py b/scripts/manifest_fixer_test.py index f6fcaafe5..199b279cd 100755 --- a/scripts/manifest_fixer_test.py +++ b/scripts/manifest_fixer_test.py @@ -521,12 +521,55 @@ class AddNoCodeApplicationTest(unittest.TestCase): self.assert_xml_equal(output, manifest_input) def test_has_application_has_code_true(self): - """ Do nothing if there's already an application elemeent even if its + """ Do nothing if there's already an application element even if its hasCode attribute is true. """ manifest_input = self.manifest_tmpl % ' \n' output = self.run_test(manifest_input) self.assert_xml_equal(output, manifest_input) +class AddTestOnlyApplicationTest(unittest.TestCase): + """Unit tests for set_test_only_flag_to_true function.""" + + def assert_xml_equal(self, output, expected): + self.assertEqual(ET.canonicalize(output), ET.canonicalize(expected)) + + def run_test(self, input_manifest): + doc = minidom.parseString(input_manifest) + manifest_fixer.set_test_only_flag_to_true(doc) + output = io.StringIO() + manifest_fixer.write_xml(output, doc) + return output.getvalue() + + manifest_tmpl = ( + '\n' + '\n' + '%s' + '\n') + + def test_no_application(self): + manifest_input = self.manifest_tmpl % '' + expected = self.manifest_tmpl % ' \n' + output = self.run_test(manifest_input) + self.assert_xml_equal(output, expected) + + def test_has_application_no_test_only(self): + manifest_input = self.manifest_tmpl % ' \n' + expected = self.manifest_tmpl % ' \n' + output = self.run_test(manifest_input) + self.assert_xml_equal(output, expected) + + def test_has_application_test_only_true(self): + """ If there's already an application element.""" + manifest_input = self.manifest_tmpl % ' \n' + output = self.run_test(manifest_input) + self.assert_xml_equal(output, manifest_input) + + def test_has_application_test_only_false(self): + """ If there's already an application element with the testOnly attribute as false.""" + manifest_input = self.manifest_tmpl % ' \n' + output = self.run_test(manifest_input) + self.assert_xml_equal(output, manifest_input) + if __name__ == '__main__': unittest.main(verbosity=2)