Only allow setting presigned without preprocessed on targetSdk < 30

When targetSdk is >= 30, the system verifies that you use a valid
signature V2+ certificate. Uncompressing ndk/dex files or aligning
the zip file will break a signature V2, so these apks should really
just set preprocessed: true.

Fixes: 185811447
Test: Presubmits
Change-Id: Id89c42bcd5b5daa6eda1716bff4023423298036b
diff --git a/java/app_import.go b/java/app_import.go
index ad1765e..1718d93 100644
--- a/java/app_import.go
+++ b/java/app_import.go
@@ -277,6 +277,14 @@
 		a.hideApexVariantFromMake = true
 	}
 
+	if Bool(a.properties.Preprocessed) {
+		if a.properties.Presigned != nil && !*a.properties.Presigned {
+			ctx.ModuleErrorf("Setting preprocessed: true implies presigned: true, so you cannot set presigned to false")
+		}
+		t := true
+		a.properties.Presigned = &t
+	}
+
 	numCertPropsSet := 0
 	if String(a.properties.Certificate) != "" {
 		numCertPropsSet++
@@ -288,11 +296,9 @@
 		numCertPropsSet++
 	}
 	if numCertPropsSet != 1 {
-		ctx.ModuleErrorf("One and only one of certficate, presigned, and default_dev_cert properties must be set")
+		ctx.ModuleErrorf("One and only one of certficate, presigned (implied by preprocessed), and default_dev_cert properties must be set")
 	}
 
-	_, _, certificates := collectAppDeps(ctx, a, false, false)
-
 	// TODO: LOCAL_EXTRACT_APK/LOCAL_EXTRACT_DPI_APK
 	// TODO: LOCAL_PACKAGE_SPLITS
 
@@ -365,6 +371,7 @@
 	} else if !Bool(a.properties.Presigned) {
 		// If the certificate property is empty at this point, default_dev_cert must be set to true.
 		// Which makes processMainCert's behavior for the empty cert string WAI.
+		_, _, certificates := collectAppDeps(ctx, a, false, false)
 		a.certificate, certificates = processMainCert(a.ModuleBase, String(a.properties.Certificate), certificates, ctx)
 		signed := android.PathForModuleOut(ctx, "signed", apkFilename)
 		var lineageFile android.Path
@@ -377,8 +384,13 @@
 		SignAppPackage(ctx, signed, jnisUncompressed, certificates, nil, lineageFile, rotationMinSdkVersion)
 		a.outputFile = signed
 	} else {
+		// Presigned without Preprocessed shouldn't really be a thing, currently we disallow
+		// it for apps with targetSdk >= 30, because on those targetSdks you must be using signature
+		// v2 or later, and signature v2 would be wrecked by uncompressing libs / zipaligning.
+		// But ideally we would disallow it for all prebuilt apks, and remove the presigned property.
+		targetSdkCheck := a.validateTargetSdkLessThan30(ctx, srcApk)
 		alignedApk := android.PathForModuleOut(ctx, "zip-aligned", apkFilename)
-		TransformZipAlign(ctx, alignedApk, jnisUncompressed)
+		TransformZipAlign(ctx, alignedApk, jnisUncompressed, []android.Path{targetSdkCheck})
 		a.outputFile = alignedApk
 		a.certificate = PresignedCertificate
 	}
@@ -432,6 +444,16 @@
 	})
 }
 
+func (a *AndroidAppImport) validateTargetSdkLessThan30(ctx android.ModuleContext, srcApk android.Path) android.Path {
+	alignmentStamp := android.PathForModuleOut(ctx, "validated-prebuilt", "old_target_sdk.stamp")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   checkBelowTargetSdk30ForNonPreprocessedApks,
+		Input:  srcApk,
+		Output: alignmentStamp,
+	})
+	return alignmentStamp
+}
+
 func (a *AndroidAppImport) Prebuilt() *android.Prebuilt {
 	return &a.prebuilt
 }
diff --git a/java/app_import_test.go b/java/app_import_test.go
index bb8fab9..506c734 100644
--- a/java/app_import_test.go
+++ b/java/app_import_test.go
@@ -629,31 +629,21 @@
 			presigned: true,
 			preprocessed: true,
 		}
-
-		android_test_import {
-			name: "foo_cert",
-			apk: "prebuilts/apk/app.apk",
-			certificate: "cert/new_cert",
-			preprocessed: true,
-		}
 		`)
 
-	testModules := []string{"foo", "foo_cert"}
-	for _, m := range testModules {
-		apkName := m + ".apk"
-		variant := ctx.ModuleForTests(m, "android_common")
-		jniRule := variant.Output("jnis-uncompressed/" + apkName).BuildParams.Rule.String()
-		if jniRule != android.Cp.String() {
-			t.Errorf("Unexpected JNI uncompress rule: " + jniRule)
-		}
+	apkName := "foo.apk"
+	variant := ctx.ModuleForTests("foo", "android_common")
+	jniRule := variant.Output("jnis-uncompressed/" + apkName).BuildParams.Rule.String()
+	if jniRule != android.Cp.String() {
+		t.Errorf("Unexpected JNI uncompress rule: " + jniRule)
+	}
 
-		// Make sure signing and aligning were skipped.
-		if variant.MaybeOutput("signed/"+apkName).Rule != nil {
-			t.Errorf("signing rule shouldn't be included for preprocessed.")
-		}
-		if variant.MaybeOutput("zip-aligned/"+apkName).Rule != nil {
-			t.Errorf("aligning rule shouldn't be for preprocessed")
-		}
+	// Make sure signing and aligning were skipped.
+	if variant.MaybeOutput("signed/"+apkName).Rule != nil {
+		t.Errorf("signing rule shouldn't be included for preprocessed.")
+	}
+	if variant.MaybeOutput("zip-aligned/"+apkName).Rule != nil {
+		t.Errorf("aligning rule shouldn't be for preprocessed")
 	}
 }
 
diff --git a/java/base.go b/java/base.go
index 2c0b3ea..8f48398 100644
--- a/java/base.go
+++ b/java/base.go
@@ -1608,7 +1608,7 @@
 					false, nil, nil)
 				if *j.dexProperties.Uncompress_dex {
 					combinedAlignedJar := android.PathForModuleOut(ctx, "dex-withres-aligned", jarName).OutputPath
-					TransformZipAlign(ctx, combinedAlignedJar, combinedJar)
+					TransformZipAlign(ctx, combinedAlignedJar, combinedJar, nil)
 					dexOutputFile = combinedAlignedJar
 				} else {
 					dexOutputFile = combinedJar
diff --git a/java/builder.go b/java/builder.go
index debf49a..bf91917 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -277,6 +277,14 @@
 			Command:     `${config.Zip2ZipCmd} -i ${in} -o ${out} -x 'META-INF/services/**/*'`,
 			CommandDeps: []string{"${config.Zip2ZipCmd}"},
 		})
+
+	checkBelowTargetSdk30ForNonPreprocessedApks = pctx.AndroidStaticRule("checkBelowTargetSdk30ForNonPreprocessedApks",
+		blueprint.RuleParams{
+			Command:     "build/soong/scripts/check_target_sdk_less_than_30.py ${config.Aapt2Cmd} $in $out",
+			CommandDeps: []string{"build/soong/scripts/check_target_sdk_less_than_30.py", "${config.Aapt2Cmd}"},
+			Description: "Check prebuilt target sdk version",
+		},
+	)
 )
 
 func init() {
@@ -689,12 +697,13 @@
 	android.WriteFileRule(ctx, outputFile, "Main-Class: "+mainClass+"\n")
 }
 
-func TransformZipAlign(ctx android.ModuleContext, outputFile android.WritablePath, inputFile android.Path) {
+func TransformZipAlign(ctx android.ModuleContext, outputFile android.WritablePath, inputFile android.Path, validations android.Paths) {
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        zipalign,
 		Description: "align",
 		Input:       inputFile,
 		Output:      outputFile,
+		Validations: validations,
 	})
 }
 
diff --git a/java/dex.go b/java/dex.go
index df501bf..511ae5e 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -438,7 +438,7 @@
 	}
 	if proptools.Bool(d.dexProperties.Uncompress_dex) {
 		alignedJavalibJar := android.PathForModuleOut(ctx, "aligned", dexParams.jarName).OutputPath
-		TransformZipAlign(ctx, alignedJavalibJar, javalibJar)
+		TransformZipAlign(ctx, alignedJavalibJar, javalibJar, nil)
 		javalibJar = alignedJavalibJar
 	}
 
diff --git a/java/hiddenapi.go b/java/hiddenapi.go
index 4d08b83..fe3fe7b 100644
--- a/java/hiddenapi.go
+++ b/java/hiddenapi.go
@@ -305,7 +305,7 @@
 	})
 
 	if uncompressDex {
-		TransformZipAlign(ctx, output, encodeRuleOutput)
+		TransformZipAlign(ctx, output, encodeRuleOutput, nil)
 	}
 
 	return output