From 2f1da168abf133f1bb13febfecb15fe69fbf64de Mon Sep 17 00:00:00 2001 From: Cole Faust Date: Mon, 17 Apr 2023 15:06:56 -0700 Subject: [PATCH] Expand preprocessed flag to work on android_app_imports Setting the preprocessed flag will now also verify that the apk is zip-aligned and does not have compressed JNI libs or dex files. Bug: 185811447 Test: m nothing Change-Id: I01b7c25f390345b14385f6f9e1640f48a5d9dc93 --- java/app.go | 4 +-- java/app_import.go | 71 +++++++++++++++++++++++++++++++---------- java/app_import_test.go | 24 ++++++++++++++ java/builder.go | 13 ++++++++ 4 files changed, 92 insertions(+), 20 deletions(-) diff --git a/java/app.go b/java/app.go index 03e233059..7bb8cdbd7 100755 --- a/java/app.go +++ b/java/app.go @@ -1461,10 +1461,8 @@ func (u *usesLibrary) verifyUsesLibrariesManifest(ctx android.ModuleContext, man // 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 { +func (u *usesLibrary) verifyUsesLibrariesAPK(ctx android.ModuleContext, apk 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()) - return outputFile } // For Bazel / bp2build diff --git a/java/app_import.go b/java/app_import.go index 85b35ebaa..bfd67679e 100644 --- a/java/app_import.go +++ b/java/app_import.go @@ -49,6 +49,17 @@ var ( CommandDeps: []string{"${config.Zip2ZipCmd}"}, Description: "Uncompress dex files", }) + + checkJniAndDexLibsAreUncompressedRule = pctx.AndroidStaticRule("check-jni-and-dex-libs-are-uncompressed", blueprint.RuleParams{ + // grep -v ' stor ' will search for lines that don't have ' stor '. stor means the file is stored uncompressed + Command: "if (zipinfo $in 'lib/*.so' '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then " + + "echo $in: Contains compressed JNI libraries and/or dex files >&2;" + + "exit 1; " + + "else " + + "touch $out; " + + "fi", + Description: "Check for compressed JNI libs or dex files", + }) ) func RegisterAppImportBuildComponents(ctx android.RegistrationContext) { @@ -73,8 +84,6 @@ type AndroidAppImport struct { usesLibrary usesLibrary - preprocessed bool - installPath android.InstallPath hideApexVariantFromMake bool @@ -128,6 +137,13 @@ type AndroidAppImportProperties struct { // Optional. Install to a subdirectory of the default install path for the module Relative_install_path *string + + // Whether the prebuilt apk can be installed without additional processing. Default is false. + Preprocessed *bool + + // Whether or not to skip checking the preprocessed apk for proper alignment and uncompressed + // JNI libs and dex files. Default is false + Skip_preprocessed_apk_checks *bool } func (a *AndroidAppImport) IsInstallable() bool { @@ -201,7 +217,7 @@ func (a *AndroidAppImport) uncompressEmbeddedJniLibs( ctx android.ModuleContext, inputPath android.Path, outputPath android.OutputPath) { // Test apps don't need their JNI libraries stored uncompressed. As a matter of fact, messing // with them may invalidate pre-existing signature data. - if ctx.InstallInTestcases() && (Bool(a.properties.Presigned) || a.preprocessed) { + if ctx.InstallInTestcases() && (Bool(a.properties.Presigned) || Bool(a.properties.Preprocessed)) { ctx.Build(pctx, android.BuildParams{ Rule: android.Cp, Output: outputPath, @@ -219,7 +235,7 @@ func (a *AndroidAppImport) uncompressEmbeddedJniLibs( // Returns whether this module should have the dex file stored uncompressed in the APK. func (a *AndroidAppImport) shouldUncompressDex(ctx android.ModuleContext) bool { - if ctx.Config().UnbundledBuild() || a.preprocessed { + if ctx.Config().UnbundledBuild() || proptools.Bool(a.properties.Preprocessed) { return false } @@ -297,7 +313,7 @@ func (a *AndroidAppImport) generateAndroidBuildActions(ctx android.ModuleContext a.dexpreopter.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx) if a.usesLibrary.enforceUsesLibraries() { - srcApk = a.usesLibrary.verifyUsesLibrariesAPK(ctx, srcApk) + a.usesLibrary.verifyUsesLibrariesAPK(ctx, srcApk) } a.dexpreopter.dexpreopt(ctx, jnisUncompressed) @@ -317,8 +333,15 @@ func (a *AndroidAppImport) generateAndroidBuildActions(ctx android.ModuleContext // Sign or align the package if package has not been preprocessed - if a.preprocessed { - a.outputFile = srcApk + if proptools.Bool(a.properties.Preprocessed) { + output := srcApk + // TODO(b/185811447) Uncomment this after all existing failing apks set skip_preprocessed_apk_checks: true + //if !proptools.Bool(a.properties.Skip_preprocessed_apk_checks) { + // writableOutput := android.PathForModuleOut(ctx, "validated-prebuilt", apkFilename) + // a.validatePreprocessedApk(ctx, srcApk, writableOutput) + // output = writableOutput + //} + a.outputFile = output a.certificate = PresignedCertificate } else if !Bool(a.properties.Presigned) { // If the certificate property is empty at this point, default_dev_cert must be set to true. @@ -352,6 +375,30 @@ func (a *AndroidAppImport) generateAndroidBuildActions(ctx android.ModuleContext // TODO: androidmk converter jni libs } +func (a *AndroidAppImport) validatePreprocessedApk(ctx android.ModuleContext, srcApk android.Path, dstApk android.WritablePath) { + alignmentStamp := android.PathForModuleOut(ctx, "validated-prebuilt", "alignment.stamp") + ctx.Build(pctx, android.BuildParams{ + Rule: checkZipAlignment, + Input: srcApk, + Output: alignmentStamp, + }) + compressionStamp := android.PathForModuleOut(ctx, "validated-prebuilt", "compression.stamp") + ctx.Build(pctx, android.BuildParams{ + Rule: checkJniAndDexLibsAreUncompressedRule, + Input: srcApk, + Output: compressionStamp, + }) + ctx.Build(pctx, android.BuildParams{ + Rule: android.Cp, + Input: srcApk, + Output: dstApk, + Validations: []android.Path{ + alignmentStamp, + compressionStamp, + }, + }) +} + func (a *AndroidAppImport) Prebuilt() *android.Prebuilt { return &a.prebuilt } @@ -487,11 +534,6 @@ func AndroidAppImportFactory() android.Module { return module } -type androidTestImportProperties struct { - // Whether the prebuilt apk can be installed without additional processing. Default is false. - Preprocessed *bool -} - type AndroidTestImport struct { AndroidAppImport @@ -508,14 +550,10 @@ type AndroidTestImport struct { Per_testcase_directory *bool } - testImportProperties androidTestImportProperties - data android.Paths } func (a *AndroidTestImport) GenerateAndroidBuildActions(ctx android.ModuleContext) { - a.preprocessed = Bool(a.testImportProperties.Preprocessed) - a.generateAndroidBuildActions(ctx) a.data = android.PathsForModuleSrc(ctx, a.testProperties.Data) @@ -532,7 +570,6 @@ func AndroidTestImportFactory() android.Module { module.AddProperties(&module.properties) module.AddProperties(&module.dexpreoptProperties) module.AddProperties(&module.testProperties) - module.AddProperties(&module.testImportProperties) module.populateAllVariantStructs() android.AddLoadHook(module, func(ctx android.LoadHookContext) { module.processVariants(ctx) diff --git a/java/app_import_test.go b/java/app_import_test.go index 80930248e..845a96299 100644 --- a/java/app_import_test.go +++ b/java/app_import_test.go @@ -657,6 +657,30 @@ func TestAndroidTestImport_Preprocessed(t *testing.T) { } } +// TODO(b/185811447) Uncomment this after all existing failing apks set skip_preprocessed_apk_checks: true +//func TestAndroidAppImport_Preprocessed(t *testing.T) { +// ctx, _ := testJava(t, ` +// android_app_import { +// name: "foo", +// apk: "prebuilts/apk/app.apk", +// presigned: true, +// preprocessed: true, +// } +// `) +// +// apkName := "foo.apk" +// variant := ctx.ModuleForTests("foo", "android_common") +// outputBuildParams := variant.Output("validated-prebuilt/" + apkName).BuildParams +// if outputBuildParams.Rule.String() != android.Cp.String() { +// t.Errorf("Unexpected prebuilt android_app_import rule: " + outputBuildParams.Rule.String()) +// } +// +// // Make sure compression and aligning were validated. +// if len(outputBuildParams.Validations) != 2 { +// t.Errorf("Expected compression/alignment validation rules, found %d validations", len(outputBuildParams.Validations)) +// } +//} + func TestAndroidTestImport_UncompressDex(t *testing.T) { testCases := []struct { name string diff --git a/java/builder.go b/java/builder.go index 462626712..0c5773823 100644 --- a/java/builder.go +++ b/java/builder.go @@ -246,6 +246,19 @@ var ( CommandDeps: []string{"${config.ZipAlign}"}, }, ) + + checkZipAlignment = pctx.AndroidStaticRule("checkzipalign", + blueprint.RuleParams{ + Command: "if ! ${config.ZipAlign} -c -p 4 $in > /dev/null; then " + + "echo $in: Improper package alignment >&2; " + + "exit 1; " + + "else " + + "touch $out; " + + "fi", + CommandDeps: []string{"${config.ZipAlign}"}, + Description: "Check zip alignment", + }, + ) ) func init() {