Add Compress_apk property to android_app_import

This change adds a new boolean property, Compress_apk, to
the android_app_import module type. When set to true, the output APK
will be compressed using gzip. This can reduce the size of the
installed APK, saving storage space.

This functionality is the same as the LOCAL_COMPRESSED_MODULE variable
in Android.mk builds.

Bug: 384848192
Bug: 383642702
Test: add compress_apk in android_app_import
Change-Id: If6793ea09951c54d42ba5cc4249b698b676cbcc7
diff --git a/java/app_import.go b/java/app_import.go
index b77e31a..a997e35 100644
--- a/java/app_import.go
+++ b/java/app_import.go
@@ -67,6 +67,13 @@
 		Command:     "unzip -p $in $extract_apk > $out",
 		Description: "Extract specific sub apk",
 	}, "extract_apk")
+
+	gzipRule = pctx.AndroidStaticRule("gzip",
+		blueprint.RuleParams{
+			Command:     "prebuilts/build-tools/path/linux-x86/gzip -9 -c $in > $out",
+			CommandDeps: []string{"prebuilts/build-tools/path/linux-x86/gzip"},
+			Description: "gzip $out",
+		})
 )
 
 func RegisterAppImportBuildComponents(ctx android.RegistrationContext) {
@@ -171,6 +178,9 @@
 
 	// Path of extracted apk which is extracted from prebuilt apk. Use this extracted to import.
 	Extract_apk *string
+
+	// Compress the output APK using gzip. Defaults to false.
+	Compress_apk proptools.Configurable[bool] `android:"arch_variant,replace_instead_of_append"`
 }
 
 func (a *AndroidAppImport) IsInstallable() bool {
@@ -427,7 +437,9 @@
 
 	a.dexpreopter.enforceUsesLibs = a.usesLibrary.enforceUsesLibraries(ctx)
 	a.dexpreopter.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
-	if a.usesLibrary.shouldDisableDexpreopt {
+
+	// Disable Dexpreopt if Compress_apk is true. It follows the build/make/core/app_prebuilt_internal.mk
+	if a.usesLibrary.shouldDisableDexpreopt || a.properties.Compress_apk.GetOrDefault(ctx, false) {
 		a.dexpreopter.disableDexpreopt()
 	}
 
@@ -446,7 +458,13 @@
 		jnisUncompressed = dexUncompressed
 	}
 
-	apkFilename := proptools.StringDefault(a.properties.Filename, a.BaseModuleName()+".apk")
+	defaultApkFilename := a.BaseModuleName()
+	if a.properties.Compress_apk.GetOrDefault(ctx, false) {
+		defaultApkFilename += ".apk.gz"
+	} else {
+		defaultApkFilename += ".apk"
+	}
+	apkFilename := proptools.StringDefault(a.properties.Filename, defaultApkFilename)
 
 	// TODO: Handle EXTERNAL
 
@@ -486,7 +504,16 @@
 		a.certificate = PresignedCertificate
 	}
 
-	// TODO: Optionally compress the output apk.
+	if a.properties.Compress_apk.GetOrDefault(ctx, false) {
+		outputFile := android.PathForModuleOut(ctx, "compressed_apk", apkFilename)
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        gzipRule,
+			Input:       a.outputFile,
+			Output:      outputFile,
+			Description: "Compressing " + a.outputFile.Base(),
+		})
+		a.outputFile = outputFile
+	}
 
 	if apexInfo.IsForPlatform() {
 		a.installPath = ctx.InstallFile(installDir, apkFilename, a.outputFile)