diff --git a/android/config.go b/android/config.go index 3b7b47782..695a2980f 100644 --- a/android/config.go +++ b/android/config.go @@ -708,6 +708,22 @@ func (c *config) HostStaticBinaries() bool { 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 2eb990083..f2ba89bb2 100644 --- a/android/variable.go +++ b/android/variable.go @@ -190,6 +190,12 @@ type productVariables struct { 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 5d25dcf17..d1b04c3cc 100644 --- a/java/app.go +++ b/java/app.go @@ -67,7 +67,9 @@ type appProperties struct { // 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 @@ func (a *AndroidApp) GenerateAndroidBuildActions(ctx android.ModuleContext) { 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 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) { 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 @@ func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) { 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 @@ func AndroidAppFactory() android.Module { 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 @@ func AndroidTestFactory() android.Module { 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 @@ func AndroidTestHelperAppFactory() android.Module { 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 b9b5f43a7..75774443f 100644 --- a/java/app_builder.go +++ b/java/app_builder.go @@ -87,9 +87,6 @@ func CreateAppPackage(ctx android.ModuleContext, outputFile android.WritablePath 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 625fb83cc..5cec3252e 100644 --- a/java/dex.go +++ b/java/dex.go @@ -26,7 +26,7 @@ var d8 = pctx.AndroidStaticRule("d8", 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 @@ var d8 = pctx.AndroidStaticRule("d8", "${config.MergeZipsCmd}", }, }, - "outDir", "d8Flags") + "outDir", "d8Flags", "zipFlags") var r8 = pctx.AndroidStaticRule("r8", blueprint.RuleParams{ @@ -46,7 +46,7 @@ var r8 = pctx.AndroidStaticRule("r8", `-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 @@ var r8 = pctx.AndroidStaticRule("r8", "${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 @@ func (j *Module) compileDex(ctx android.ModuleContext, flags javaBuilderFlags, 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 @@ func (j *Module) compileDex(ctx android.ModuleContext, flags javaBuilderFlags, 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 @@ func (j *Module) compileDex(ctx android.ModuleContext, flags javaBuilderFlags, 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 f651884c3..1a73133e4 100644 --- a/java/java.go +++ b/java/java.go @@ -257,6 +257,8 @@ type CompilerDeviceProperties struct { // 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