Merge "Store dex files uncompressed and unstripped in privileged APKs"
diff --git a/android/config.go b/android/config.go
index 3b7b477..695a298 100644
--- a/android/config.go
+++ b/android/config.go
@@ -708,6 +708,22 @@
 	return Bool(c.productVariables.HostStaticBinaries)
 }
 
+func (c *config) UncompressPrivAppDex() bool {
+	return Bool(c.productVariables.UncompressPrivAppDex)
+}
+
+func (c *config) ModulesLoadedByPrivilegedModules() []string {
+	return c.productVariables.ModulesLoadedByPrivilegedModules
+}
+
+func (c *config) DefaultStripDex() bool {
+	return Bool(c.productVariables.DefaultStripDex)
+}
+
+func (c *config) DisableDexPreopt(name string) bool {
+	return Bool(c.productVariables.DisableDexPreopt) || InList(name, c.productVariables.DisableDexPreoptModules)
+}
+
 func (c *deviceConfig) Arches() []Arch {
 	var arches []Arch
 	for _, target := range c.config.Targets[Android] {
diff --git a/android/variable.go b/android/variable.go
index 2eb9900..f2ba89b 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -190,6 +190,12 @@
 	Arc                        *bool `json:",omitempty"`
 	MinimizeJavaDebugInfo      *bool `json:",omitempty"`
 
+	UncompressPrivAppDex             *bool    `json:",omitempty"`
+	ModulesLoadedByPrivilegedModules []string `json:",omitempty"`
+	DefaultStripDex                  *bool    `json:",omitempty"`
+	DisableDexPreopt                 *bool    `json:",omitempty"`
+	DisableDexPreoptModules          []string `json:",omitempty"`
+
 	IntegerOverflowExcludePaths *[]string `json:",omitempty"`
 
 	EnableCFI       *bool     `json:",omitempty"`
diff --git a/java/app.go b/java/app.go
index 5d25dcf..d1b04c3 100644
--- a/java/app.go
+++ b/java/app.go
@@ -67,7 +67,9 @@
 	// list of native libraries that will be provided in or alongside the resulting jar
 	Jni_libs []string `android:"arch_variant"`
 
-	EmbedJNI bool `blueprint:"mutated"`
+	AllowDexPreopt bool `blueprint:"mutated"`
+	EmbedJNI       bool `blueprint:"mutated"`
+	StripDex       bool `blueprint:"mutated"`
 }
 
 type AndroidApp struct {
@@ -139,6 +141,42 @@
 	a.generateAndroidBuildActions(ctx)
 }
 
+// Returns whether this module should have the dex file stored uncompressed in the APK, or stripped completely.  If
+// stripped, the code will still be present on the device in the dexpreopted files.
+// This is only necessary for APKs, and not jars, because APKs are signed and the dex file should not be uncompressed
+// or removed after the signature has been generated.  For jars, which are not signed, the dex file is uncompressed
+// or removed at installation time in Make.
+func (a *AndroidApp) uncompressOrStripDex(ctx android.ModuleContext) (uncompress, strip bool) {
+	if ctx.Config().UnbundledBuild() {
+		return false, false
+	}
+
+	strip = ctx.Config().DefaultStripDex()
+	// TODO(ccross): don't strip dex installed on partitions that may be updated separately (like vendor)
+	// TODO(ccross): don't strip dex on modules with LOCAL_APK_LIBRARIES equivalent
+	// TODO(ccross): don't strip dex on modules that are preopted to system_other
+
+	// Uncompress dex in APKs of privileged apps, and modules used by privileged apps.
+	if ctx.Config().UncompressPrivAppDex() &&
+		(Bool(a.appProperties.Privileged) ||
+			inList(ctx.ModuleName(), ctx.Config().ModulesLoadedByPrivilegedModules())) {
+
+		uncompress = true
+		// If the dex files is store uncompressed, don't strip it, we will reuse the uncompressed dex from the APK
+		// instead of copying it into the odex file.
+		strip = false
+	}
+
+	// If dexpreopt is disabled, don't strip the dex file
+	if !a.appProperties.AllowDexPreopt ||
+		!BoolDefault(a.deviceProperties.Dex_preopt.Enabled, true) ||
+		ctx.Config().DisableDexPreopt(ctx.ModuleName()) {
+		strip = false
+	}
+
+	return uncompress, strip
+}
+
 func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) {
 	linkFlags := append([]string(nil), a.extraLinkFlags...)
 
@@ -184,11 +222,16 @@
 	a.Module.extraProguardFlagFiles = append(a.Module.extraProguardFlagFiles, staticLibProguardFlagFiles...)
 	a.Module.extraProguardFlagFiles = append(a.Module.extraProguardFlagFiles, a.proguardOptionsFile)
 
+	a.deviceProperties.UncompressDex, a.appProperties.StripDex = a.uncompressOrStripDex(ctx)
+
 	if ctx.ModuleName() != "framework-res" {
 		a.Module.compile(ctx, a.aaptSrcJar)
 	}
+	dexJarFile := a.dexJarFile
 
-	packageFile := android.PathForModuleOut(ctx, "package.apk")
+	if a.appProperties.StripDex {
+		dexJarFile = nil
+	}
 
 	var certificates []certificate
 
@@ -226,8 +269,8 @@
 
 	certificates = append([]certificate{a.certificate}, certificateDeps...)
 
-	CreateAppPackage(ctx, packageFile, a.exportPackage, jniJarFile, a.outputFile, certificates)
-
+	packageFile := android.PathForModuleOut(ctx, "package.apk")
+	CreateAppPackage(ctx, packageFile, a.exportPackage, jniJarFile, dexJarFile, certificates)
 	a.outputFile = packageFile
 
 	if ctx.ModuleName() == "framework-res" {
@@ -284,6 +327,7 @@
 
 	module.Module.properties.Instrument = true
 	module.Module.properties.Installable = proptools.BoolPtr(true)
+	module.appProperties.AllowDexPreopt = true
 
 	module.AddProperties(
 		&module.Module.properties,
@@ -345,6 +389,7 @@
 	module.Module.properties.Instrument = true
 	module.Module.properties.Installable = proptools.BoolPtr(true)
 	module.appProperties.EmbedJNI = true
+	module.appProperties.AllowDexPreopt = false
 
 	module.AddProperties(
 		&module.Module.properties,
@@ -379,6 +424,7 @@
 
 	module.Module.properties.Installable = proptools.BoolPtr(true)
 	module.appProperties.EmbedJNI = true
+	module.appProperties.AllowDexPreopt = false
 
 	module.AddProperties(
 		&module.Module.properties,
diff --git a/java/app_builder.go b/java/app_builder.go
index b9b5f43..7577444 100644
--- a/java/app_builder.go
+++ b/java/app_builder.go
@@ -87,9 +87,6 @@
 		certificateArgs = append(certificateArgs, c.pem.String(), c.key.String())
 	}
 
-	// TODO(ccross): sometimes uncompress dex
-	// TODO(ccross): sometimes strip dex
-
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        signapk,
 		Description: "signapk",
diff --git a/java/dex.go b/java/dex.go
index 625fb83..5cec325 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -26,7 +26,7 @@
 	blueprint.RuleParams{
 		Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
 			`${config.D8Cmd} --output $outDir $d8Flags $in && ` +
-			`${config.SoongZipCmd} -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
+			`${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
 			`${config.MergeZipsCmd} -D -stripFile "**/*.class" $out $outDir/classes.dex.jar $in`,
 		CommandDeps: []string{
 			"${config.D8Cmd}",
@@ -34,7 +34,7 @@
 			"${config.MergeZipsCmd}",
 		},
 	},
-	"outDir", "d8Flags")
+	"outDir", "d8Flags", "zipFlags")
 
 var r8 = pctx.AndroidStaticRule("r8",
 	blueprint.RuleParams{
@@ -46,7 +46,7 @@
 			`-printmapping $outDict ` +
 			`$r8Flags && ` +
 			`touch "$outDict" && ` +
-			`${config.SoongZipCmd} -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
+			`${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
 			`${config.MergeZipsCmd} -D -stripFile "**/*.class" $out $outDir/classes.dex.jar $in`,
 		CommandDeps: []string{
 			"${config.R8Cmd}",
@@ -54,7 +54,7 @@
 			"${config.MergeZipsCmd}",
 		},
 	},
-	"outDir", "outDict", "r8Flags")
+	"outDir", "outDict", "r8Flags", "zipFlags")
 
 func (j *Module) dexCommonFlags(ctx android.ModuleContext) []string {
 	flags := j.deviceProperties.Dxflags
@@ -172,6 +172,11 @@
 	javalibJar := android.PathForModuleOut(ctx, "dex", jarName)
 	outDir := android.PathForModuleOut(ctx, "dex")
 
+	zipFlags := ""
+	if j.deviceProperties.UncompressDex {
+		zipFlags = "-L 0"
+	}
+
 	if useR8 {
 		proguardDictionary := android.PathForModuleOut(ctx, "proguard_dictionary")
 		j.proguardDictionary = proguardDictionary
@@ -184,9 +189,10 @@
 			Input:          classesJar,
 			Implicits:      r8Deps,
 			Args: map[string]string{
-				"r8Flags": strings.Join(r8Flags, " "),
-				"outDict": j.proguardDictionary.String(),
-				"outDir":  outDir.String(),
+				"r8Flags":  strings.Join(r8Flags, " "),
+				"zipFlags": zipFlags,
+				"outDict":  j.proguardDictionary.String(),
+				"outDir":   outDir.String(),
 			},
 		})
 	} else {
@@ -198,8 +204,9 @@
 			Input:       classesJar,
 			Implicits:   d8Deps,
 			Args: map[string]string{
-				"d8Flags": strings.Join(d8Flags, " "),
-				"outDir":  outDir.String(),
+				"d8Flags":  strings.Join(d8Flags, " "),
+				"zipFlags": zipFlags,
+				"outDir":   outDir.String(),
 			},
 		})
 	}
diff --git a/java/java.go b/java/java.go
index f651884..1a73133 100644
--- a/java/java.go
+++ b/java/java.go
@@ -257,6 +257,8 @@
 
 	// When targeting 1.9, override the modules to use with --system
 	System_modules *string
+
+	UncompressDex bool `blueprint:"mutated"`
 }
 
 // Module contains the properties and members used by all java module types