Merge "Revert "Remove option to create boot image based on preloaded classes""
diff --git a/Android.bp b/Android.bp
index fff17ef..fe3a390 100644
--- a/Android.bp
+++ b/Android.bp
@@ -55,6 +55,7 @@
         "android/mutator.go",
         "android/namespace.go",
         "android/neverallow.go",
+        "android/notices.go",
         "android/onceper.go",
         "android/override_module.go",
         "android/package_ctx.go",
diff --git a/android/config.go b/android/config.go
index b0d8b7f..e4012fa 100644
--- a/android/config.go
+++ b/android/config.go
@@ -216,6 +216,7 @@
 			AAPTPreferredConfig:         stringPtr("xhdpi"),
 			AAPTCharacteristics:         stringPtr("nosdcard"),
 			AAPTPrebuiltDPI:             []string{"xhdpi", "xxhdpi"},
+			UncompressPrivAppDex:        boolPtr(true),
 		},
 
 		buildDir:     buildDir,
diff --git a/android/module.go b/android/module.go
index 87e2ca7..66822f0 100644
--- a/android/module.go
+++ b/android/module.go
@@ -901,14 +901,6 @@
 	}
 
 	if m.Enabled() {
-		m.module.GenerateAndroidBuildActions(ctx)
-		if ctx.Failed() {
-			return
-		}
-
-		m.installFiles = append(m.installFiles, ctx.installFiles...)
-		m.checkbuildFiles = append(m.checkbuildFiles, ctx.checkbuildFiles...)
-
 		notice := proptools.StringDefault(m.commonProperties.Notice, "NOTICE")
 		if module := SrcIsModule(notice); module != "" {
 			m.noticeFile = ctx.ExpandOptionalSource(&notice, "notice")
@@ -916,6 +908,14 @@
 			noticePath := filepath.Join(ctx.ModuleDir(), notice)
 			m.noticeFile = ExistentPathForSource(ctx, noticePath)
 		}
+
+		m.module.GenerateAndroidBuildActions(ctx)
+		if ctx.Failed() {
+			return
+		}
+
+		m.installFiles = append(m.installFiles, ctx.installFiles...)
+		m.checkbuildFiles = append(m.checkbuildFiles, ctx.checkbuildFiles...)
 	} else if ctx.Config().AllowMissingDependencies() {
 		// If the module is not enabled it will not create any build rules, nothing will call
 		// ctx.GetMissingDependencies(), and blueprint will consider the missing dependencies to be unhandled
diff --git a/android/notices.go b/android/notices.go
new file mode 100644
index 0000000..dbb88fc
--- /dev/null
+++ b/android/notices.go
@@ -0,0 +1,87 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+	"path/filepath"
+
+	"github.com/google/blueprint"
+)
+
+func init() {
+	pctx.SourcePathVariable("merge_notices", "build/soong/scripts/mergenotice.py")
+	pctx.SourcePathVariable("generate_notice", "build/make/tools/generate-notice-files.py")
+
+	pctx.HostBinToolVariable("minigzip", "minigzip")
+}
+
+var (
+	mergeNoticesRule = pctx.AndroidStaticRule("mergeNoticesRule", blueprint.RuleParams{
+		Command:     `${merge_notices} --output $out $in`,
+		CommandDeps: []string{"${merge_notices}"},
+		Description: "merge notice files into $out",
+	})
+
+	generateNoticeRule = pctx.AndroidStaticRule("generateNoticeRule", blueprint.RuleParams{
+		Command: `rm -rf $tmpDir $$(dirname $out) && ` +
+			`mkdir -p $tmpDir $$(dirname $out) && ` +
+			`${generate_notice} --text-output $tmpDir/NOTICE.txt --html-output $tmpDir/NOTICE.html -t "$title" -s $inputDir && ` +
+			`${minigzip} -c $tmpDir/NOTICE.html > $out`,
+		CommandDeps: []string{"${generate_notice}", "${minigzip}"},
+		Description: "produce notice file $out",
+	}, "tmpDir", "title", "inputDir")
+)
+
+func MergeNotices(ctx ModuleContext, mergedNotice WritablePath, noticePaths []Path) {
+	ctx.Build(pctx, BuildParams{
+		Rule:        mergeNoticesRule,
+		Description: "merge notices",
+		Inputs:      noticePaths,
+		Output:      mergedNotice,
+	})
+}
+
+func BuildNoticeOutput(ctx ModuleContext, installPath OutputPath, installFilename string,
+	noticePaths []Path) ModuleOutPath {
+	// Merge all NOTICE files into one.
+	// TODO(jungjw): We should just produce a well-formatted NOTICE.html file in a single pass.
+	//
+	// generate-notice-files.py, which processes the merged NOTICE file, has somewhat strict rules
+	// about input NOTICE file paths.
+	// 1. Their relative paths to the src root become their NOTICE index titles. We want to use
+	// on-device paths as titles, and so output the merged NOTICE file the corresponding location.
+	// 2. They must end with .txt extension. Otherwise, they're ignored.
+	noticeRelPath := InstallPathToOnDevicePath(ctx, installPath.Join(ctx, installFilename+".txt"))
+	mergedNotice := PathForModuleOut(ctx, filepath.Join("NOTICE_FILES/src", noticeRelPath))
+	MergeNotices(ctx, mergedNotice, noticePaths)
+
+	// Transform the merged NOTICE file into a gzipped HTML file.
+	noticeOutput := PathForModuleOut(ctx, "NOTICE", "NOTICE.html.gz")
+	tmpDir := PathForModuleOut(ctx, "NOTICE_tmp")
+	title := "Notices for " + ctx.ModuleName()
+	ctx.Build(pctx, BuildParams{
+		Rule:        generateNoticeRule,
+		Description: "generate notice output",
+		Input:       mergedNotice,
+		Output:      noticeOutput,
+		Args: map[string]string{
+			"tmpDir":   tmpDir.String(),
+			"title":    title,
+			"inputDir": PathForModuleOut(ctx, "NOTICE_FILES/src").String(),
+		},
+	})
+
+	return noticeOutput
+}
diff --git a/apex/apex.go b/apex/apex.go
index ac0363b..a77e295 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -90,12 +90,6 @@
 		CommandDeps: []string{"${zip2zip}"},
 		Description: "app bundle",
 	}, "abi")
-
-	apexMergeNoticeRule = pctx.StaticRule("apexMergeNoticeRule", blueprint.RuleParams{
-		Command:     `${mergenotice} --output $out $inputs`,
-		CommandDeps: []string{"${mergenotice}"},
-		Description: "merge notice files into $out",
-	}, "inputs")
 )
 
 var imageApexSuffix = ".apex"
@@ -144,8 +138,6 @@
 	pctx.HostBinToolVariable("zip2zip", "zip2zip")
 	pctx.HostBinToolVariable("zipalign", "zipalign")
 
-	pctx.SourcePathVariable("mergenotice", "build/soong/scripts/mergenotice.py")
-
 	android.RegisterModuleType("apex", apexBundleFactory)
 	android.RegisterModuleType("apex_test", testApexBundleFactory)
 	android.RegisterModuleType("apex_defaults", defaultsFactory)
@@ -402,8 +394,6 @@
 	container_certificate_file android.Path
 	container_private_key_file android.Path
 
-	mergedNoticeFile android.WritablePath
-
 	// list of files to be included in this apex
 	filesInfo []apexFile
 
@@ -836,8 +826,6 @@
 	a.installDir = android.PathForModuleInstall(ctx, "apex")
 	a.filesInfo = filesInfo
 
-	a.buildNoticeFile(ctx)
-
 	if a.apexTypes.zip() {
 		a.buildUnflattenedApex(ctx, zipApex)
 	}
@@ -851,35 +839,27 @@
 	}
 }
 
-func (a *apexBundle) buildNoticeFile(ctx android.ModuleContext) {
+func (a *apexBundle) buildNoticeFile(ctx android.ModuleContext, apexFileName string) android.OptionalPath {
 	noticeFiles := []android.Path{}
-	noticeFilesString := []string{}
 	for _, f := range a.filesInfo {
 		if f.module != nil {
 			notice := f.module.NoticeFile()
 			if notice.Valid() {
 				noticeFiles = append(noticeFiles, notice.Path())
-				noticeFilesString = append(noticeFilesString, notice.Path().String())
 			}
 		}
 	}
 	// append the notice file specified in the apex module itself
 	if a.NoticeFile().Valid() {
 		noticeFiles = append(noticeFiles, a.NoticeFile().Path())
-		noticeFilesString = append(noticeFilesString, a.NoticeFile().Path().String())
 	}
 
-	if len(noticeFiles) > 0 {
-		a.mergedNoticeFile = android.PathForModuleOut(ctx, "NOTICE")
-		ctx.Build(pctx, android.BuildParams{
-			Rule:   apexMergeNoticeRule,
-			Inputs: noticeFiles,
-			Output: a.mergedNoticeFile,
-			Args: map[string]string{
-				"inputs": strings.Join(noticeFilesString, " "),
-			},
-		})
+	if len(noticeFiles) == 0 {
+		return android.OptionalPath{}
 	}
+
+	return android.OptionalPathForPath(
+		android.BuildNoticeOutput(ctx, a.installDir, apexFileName, android.FirstUniquePaths(noticeFiles)))
 }
 
 func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext, apexType apexPackaging) {
@@ -1004,6 +984,13 @@
 		}
 		optFlags = append(optFlags, "--target_sdk_version "+targetSdkVersion)
 
+		noticeFile := a.buildNoticeFile(ctx, ctx.ModuleName()+suffix)
+		if noticeFile.Valid() {
+			// If there's a NOTICE file, embed it as an asset file in the APEX.
+			implicitInputs = append(implicitInputs, noticeFile.Path())
+			optFlags = append(optFlags, "--assets_dir "+filepath.Dir(noticeFile.String()))
+		}
+
 		ctx.Build(pctx, android.BuildParams{
 			Rule:        apexRule,
 			Implicits:   implicitInputs,
@@ -1250,9 +1237,6 @@
 				fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", filepath.Join("$(OUT_DIR)", a.installDir.RelPathString()))
 				fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", name+apexType.suffix())
 				fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE :=", !a.installable())
-				if a.installable() && a.mergedNoticeFile != nil {
-					fmt.Fprintln(w, "LOCAL_NOTICE_FILE :=", a.mergedNoticeFile.String())
-				}
 				if len(moduleNames) > 0 {
 					fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES +=", strings.Join(moduleNames, " "))
 				}
diff --git a/apex/apex_test.go b/apex/apex_test.go
index f71abd7..2e44db7 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -27,9 +27,15 @@
 	"android/soong/java"
 )
 
+var buildDir string
+
 func testApex(t *testing.T, bp string) *android.TestContext {
-	config, buildDir := setup(t)
-	defer teardown(buildDir)
+	config := android.TestArchConfig(buildDir, nil)
+	config.TestProductVariables.DeviceVndkVersion = proptools.StringPtr("current")
+	config.TestProductVariables.DefaultAppCertificate = proptools.StringPtr("vendor/foo/devkeys/test")
+	config.TestProductVariables.CertificateOverrides = []string{"myapex_keytest:myapex.certificate.override"}
+	config.TestProductVariables.Platform_sdk_codename = proptools.StringPtr("Q")
+	config.TestProductVariables.Platform_sdk_final = proptools.BoolPtr(false)
 
 	ctx := android.NewTestArchContext()
 	ctx.RegisterModuleType("apex", android.ModuleFactoryAdaptor(apexBundleFactory))
@@ -188,22 +194,15 @@
 	return ctx
 }
 
-func setup(t *testing.T) (config android.Config, buildDir string) {
-	buildDir, err := ioutil.TempDir("", "soong_apex_test")
+func setUp() {
+	var err error
+	buildDir, err = ioutil.TempDir("", "soong_apex_test")
 	if err != nil {
-		t.Fatal(err)
+		panic(err)
 	}
-
-	config = android.TestArchConfig(buildDir, nil)
-	config.TestProductVariables.DeviceVndkVersion = proptools.StringPtr("current")
-	config.TestProductVariables.DefaultAppCertificate = proptools.StringPtr("vendor/foo/devkeys/test")
-	config.TestProductVariables.CertificateOverrides = []string{"myapex_keytest:myapex.certificate.override"}
-	config.TestProductVariables.Platform_sdk_codename = proptools.StringPtr("Q")
-	config.TestProductVariables.Platform_sdk_final = proptools.BoolPtr(false)
-	return
 }
 
-func teardown(buildDir string) {
+func tearDown() {
 	os.RemoveAll(buildDir)
 }
 
@@ -310,6 +309,8 @@
 
 	optFlags := apexRule.Args["opt_flags"]
 	ensureContains(t, optFlags, "--pubkey vendor/foo/devkeys/testkey.avbpubkey")
+	// Ensure that the NOTICE output is being packaged as an asset.
+	ensureContains(t, optFlags, "--assets_dir "+buildDir+"/.intermediates/myapex/android_common_myapex/NOTICE")
 
 	copyCmds := apexRule.Args["copy_commands"]
 
@@ -347,10 +348,10 @@
 		t.Errorf("Could not find all expected symlinks! foo: %t, foo_link_64: %t. Command was %s", found_foo, found_foo_link_64, copyCmds)
 	}
 
-	apexMergeNoticeRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("apexMergeNoticeRule")
-	noticeInputs := strings.Split(apexMergeNoticeRule.Args["inputs"], " ")
-	if len(noticeInputs) != 3 {
-		t.Errorf("number of input notice files: expected = 3, actual = %d", len(noticeInputs))
+	mergeNoticesRule := ctx.ModuleForTests("myapex", "android_common_myapex").Rule("mergeNoticesRule")
+	noticeInputs := mergeNoticesRule.Inputs.Strings()
+	if len(noticeInputs) != 2 {
+		t.Errorf("number of input notice files: expected = 2, actual = %q", len(noticeInputs))
 	}
 	ensureListContains(t, noticeInputs, "NOTICE")
 	ensureListContains(t, noticeInputs, "custom_notice")
@@ -1283,3 +1284,14 @@
 		t.Errorf("installFilename invalid. expected: %q, actual: %q", expected, p.installFilename)
 	}
 }
+
+func TestMain(m *testing.M) {
+	run := func() int {
+		setUp()
+		defer tearDown()
+
+		return m.Run()
+	}
+
+	os.Exit(run())
+}
diff --git a/cc/gen.go b/cc/gen.go
index c9f45ee..7516b28 100644
--- a/cc/gen.go
+++ b/cc/gen.go
@@ -98,6 +98,7 @@
 	}
 
 	cmd.Text("BISON_PKGDATADIR=prebuilts/build-tools/common/bison").
+		FlagWithInput("M4=", ctx.Config().PrebuiltBuildTool(ctx, "m4")).
 		Tool(ctx.Config().PrebuiltBuildTool(ctx, "bison")).
 		Flag("-d").
 		Flags(flags).
diff --git a/java/aar.go b/java/aar.go
index 460fbfa..c9306de 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -17,6 +17,7 @@
 import (
 	"android/soong/android"
 	"fmt"
+	"path/filepath"
 	"strings"
 
 	"github.com/google/blueprint"
@@ -80,6 +81,7 @@
 	rTxt                    android.Path
 	extraAaptPackagesFile   android.Path
 	mergedManifestFile      android.Path
+	noticeFile              android.OptionalPath
 	isLibrary               bool
 	useEmbeddedNativeLibs   bool
 	useEmbeddedDex          bool
@@ -151,10 +153,16 @@
 		assetFiles = append(assetFiles, androidResourceGlob(ctx, dir)...)
 	}
 
+	assetDirStrings := assetDirs.Strings()
+	if a.noticeFile.Valid() {
+		assetDirStrings = append(assetDirStrings, filepath.Dir(a.noticeFile.Path().String()))
+		assetFiles = append(assetFiles, a.noticeFile.Path())
+	}
+
 	linkFlags = append(linkFlags, "--manifest "+manifestPath.String())
 	linkDeps = append(linkDeps, manifestPath)
 
-	linkFlags = append(linkFlags, android.JoinWithPrefix(assetDirs.Strings(), "-A "))
+	linkFlags = append(linkFlags, android.JoinWithPrefix(assetDirStrings, "-A "))
 	linkDeps = append(linkDeps, assetFiles...)
 
 	// SDK version flags
diff --git a/java/app.go b/java/app.go
index cab97de..f58b0f8 100644
--- a/java/app.go
+++ b/java/app.go
@@ -19,6 +19,7 @@
 import (
 	"path/filepath"
 	"reflect"
+	"sort"
 	"strings"
 
 	"github.com/google/blueprint"
@@ -103,6 +104,10 @@
 	// Use_embedded_native_libs still selects whether they are stored uncompressed and aligned or compressed.
 	// True for android_test* modules.
 	AlwaysPackageNativeLibs bool `blueprint:"mutated"`
+
+	// If set, find and merge all NOTICE files that this module and its dependencies have and store
+	// it in the APK as an asset.
+	Embed_notices *bool
 }
 
 // android_app properties that can be overridden by override_android_app
@@ -224,15 +229,16 @@
 		return true
 	}
 
-	if ctx.Config().UnbundledBuild() {
-		return false
-	}
-
-	// Uncompress dex in APKs of privileged apps
+	// Uncompress dex in APKs of privileged apps (even for unbundled builds, they may
+	// be preinstalled as prebuilts).
 	if ctx.Config().UncompressPrivAppDex() && Bool(a.appProperties.Privileged) {
 		return true
 	}
 
+	if ctx.Config().UnbundledBuild() {
+		return false
+	}
+
 	return shouldUncompressDex(ctx, &a.dexpreopter)
 }
 
@@ -351,6 +357,54 @@
 	return jniJarFile
 }
 
+func (a *AndroidApp) noticeBuildActions(ctx android.ModuleContext, installDir android.OutputPath) android.OptionalPath {
+	if !Bool(a.appProperties.Embed_notices) && !ctx.Config().IsEnvTrue("ALWAYS_EMBED_NOTICES") {
+		return android.OptionalPath{}
+	}
+
+	// Collect NOTICE files from all dependencies.
+	seenModules := make(map[android.Module]bool)
+	noticePathSet := make(map[android.Path]bool)
+
+	ctx.WalkDeps(func(child android.Module, parent android.Module) bool {
+		// Have we already seen this?
+		if _, ok := seenModules[child]; ok {
+			return false
+		}
+		seenModules[child] = true
+
+		// Skip host modules.
+		if child.Target().Os.Class == android.Host || child.Target().Os.Class == android.HostCross {
+			return false
+		}
+
+		path := child.(android.Module).NoticeFile()
+		if path.Valid() {
+			noticePathSet[path.Path()] = true
+		}
+		return true
+	})
+
+	// If the app has one, add it too.
+	if a.NoticeFile().Valid() {
+		noticePathSet[a.NoticeFile().Path()] = true
+	}
+
+	if len(noticePathSet) == 0 {
+		return android.OptionalPath{}
+	}
+	var noticePaths []android.Path
+	for path := range noticePathSet {
+		noticePaths = append(noticePaths, path)
+	}
+	sort.Slice(noticePaths, func(i, j int) bool {
+		return noticePaths[i].String() < noticePaths[j].String()
+	})
+	noticeFile := android.BuildNoticeOutput(ctx, installDir, a.installApkName+".apk", noticePaths)
+
+	return android.OptionalPathForPath(noticeFile)
+}
+
 // Reads and prepends a main cert from the default cert dir if it hasn't been set already, i.e. it
 // isn't a cert module reference. Also checks and enforces system cert restriction if applicable.
 func processMainCert(m android.ModuleBase, certPropValue string, certificates []Certificate, ctx android.ModuleContext) []Certificate {
@@ -391,6 +445,18 @@
 	// Check if the install APK name needs to be overridden.
 	a.installApkName = ctx.DeviceConfig().OverridePackageNameFor(a.Name())
 
+	var installDir android.OutputPath
+	if ctx.ModuleName() == "framework-res" {
+		// framework-res.apk is installed as system/framework/framework-res.apk
+		installDir = android.PathForModuleInstall(ctx, "framework")
+	} else if Bool(a.appProperties.Privileged) {
+		installDir = android.PathForModuleInstall(ctx, "priv-app", a.installApkName)
+	} else {
+		installDir = android.PathForModuleInstall(ctx, "app", a.installApkName)
+	}
+
+	a.aapt.noticeFile = a.noticeBuildActions(ctx, installDir)
+
 	// Process all building blocks, from AAPT to certificates.
 	a.aaptBuildActions(ctx)
 
@@ -432,16 +498,6 @@
 	a.bundleFile = bundleFile
 
 	// Install the app package.
-	var installDir android.OutputPath
-	if ctx.ModuleName() == "framework-res" {
-		// framework-res.apk is installed as system/framework/framework-res.apk
-		installDir = android.PathForModuleInstall(ctx, "framework")
-	} else if Bool(a.appProperties.Privileged) {
-		installDir = android.PathForModuleInstall(ctx, "priv-app", a.installApkName)
-	} else {
-		installDir = android.PathForModuleInstall(ctx, "app", a.installApkName)
-	}
-
 	ctx.InstallFile(installDir, a.installApkName+".apk", a.outputFile)
 	for _, split := range a.aapt.splits {
 		ctx.InstallFile(installDir, a.installApkName+"_"+split.suffix+".apk", split.path)
diff --git a/java/app_test.go b/java/app_test.go
index 459f377..c7ea338 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -1396,3 +1396,181 @@
 		})
 	}
 }
+
+func TestEmbedNotice(t *testing.T) {
+	ctx := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
+		android_app {
+			name: "foo",
+			srcs: ["a.java"],
+			static_libs: ["javalib"],
+			jni_libs: ["libjni"],
+			notice: "APP_NOTICE",
+			embed_notices: true,
+		}
+
+		// No embed_notice flag
+		android_app {
+			name: "bar",
+			srcs: ["a.java"],
+			jni_libs: ["libjni"],
+			notice: "APP_NOTICE",
+		}
+
+		// No NOTICE files
+		android_app {
+			name: "baz",
+			srcs: ["a.java"],
+			embed_notices: true,
+		}
+
+		cc_library {
+			name: "libjni",
+			system_shared_libs: [],
+			stl: "none",
+			notice: "LIB_NOTICE",
+		}
+
+		java_library {
+			name: "javalib",
+			srcs: [
+				":gen",
+			],
+		}
+
+		genrule {
+			name: "gen",
+			tools: ["gentool"],
+			out: ["gen.java"],
+			notice: "GENRULE_NOTICE",
+		}
+
+		java_binary_host {
+			name: "gentool",
+			srcs: ["b.java"],
+			notice: "TOOL_NOTICE",
+		}
+	`)
+
+	// foo has NOTICE files to process, and embed_notices is true.
+	foo := ctx.ModuleForTests("foo", "android_common")
+	// verify merge notices rule.
+	mergeNotices := foo.Rule("mergeNoticesRule")
+	noticeInputs := mergeNotices.Inputs.Strings()
+	// TOOL_NOTICE should be excluded as it's a host module.
+	if len(mergeNotices.Inputs) != 3 {
+		t.Errorf("number of input notice files: expected = 3, actual = %q", noticeInputs)
+	}
+	if !inList("APP_NOTICE", noticeInputs) {
+		t.Errorf("APP_NOTICE is missing from notice files, %q", noticeInputs)
+	}
+	if !inList("LIB_NOTICE", noticeInputs) {
+		t.Errorf("LIB_NOTICE is missing from notice files, %q", noticeInputs)
+	}
+	if !inList("GENRULE_NOTICE", noticeInputs) {
+		t.Errorf("GENRULE_NOTICE is missing from notice files, %q", noticeInputs)
+	}
+	// aapt2 flags should include -A <NOTICE dir> so that its contents are put in the APK's /assets.
+	res := foo.Output("package-res.apk")
+	aapt2Flags := res.Args["flags"]
+	e := "-A " + buildDir + "/.intermediates/foo/android_common/NOTICE"
+	if !strings.Contains(aapt2Flags, e) {
+		t.Errorf("asset dir flag for NOTICE, %q is missing in aapt2 link flags, %q", e, aapt2Flags)
+	}
+
+	// bar has NOTICE files to process, but embed_notices is not set.
+	bar := ctx.ModuleForTests("bar", "android_common")
+	mergeNotices = bar.MaybeRule("mergeNoticesRule")
+	if mergeNotices.Rule != nil {
+		t.Errorf("mergeNotices shouldn't have run for bar")
+	}
+
+	// baz's embed_notice is true, but it doesn't have any NOTICE files.
+	baz := ctx.ModuleForTests("baz", "android_common")
+	mergeNotices = baz.MaybeRule("mergeNoticesRule")
+	if mergeNotices.Rule != nil {
+		t.Errorf("mergeNotices shouldn't have run for baz")
+	}
+}
+
+func TestUncompressDex(t *testing.T) {
+	testCases := []struct {
+		name string
+		bp   string
+
+		uncompressedPlatform  bool
+		uncompressedUnbundled bool
+	}{
+		{
+			name: "normal",
+			bp: `
+				android_app {
+					name: "foo",
+					srcs: ["a.java"],
+				}
+			`,
+			uncompressedPlatform:  true,
+			uncompressedUnbundled: false,
+		},
+		{
+			name: "use_embedded_dex",
+			bp: `
+				android_app {
+					name: "foo",
+					use_embedded_dex: true,
+					srcs: ["a.java"],
+				}
+			`,
+			uncompressedPlatform:  true,
+			uncompressedUnbundled: true,
+		},
+		{
+			name: "privileged",
+			bp: `
+				android_app {
+					name: "foo",
+					privileged: true,
+					srcs: ["a.java"],
+				}
+			`,
+			uncompressedPlatform:  true,
+			uncompressedUnbundled: true,
+		},
+	}
+
+	test := func(t *testing.T, bp string, want bool, unbundled bool) {
+		t.Helper()
+
+		config := testConfig(nil)
+		if unbundled {
+			config.TestProductVariables.Unbundled_build = proptools.BoolPtr(true)
+		}
+
+		ctx := testAppContext(config, bp, nil)
+
+		run(t, ctx, config)
+
+		foo := ctx.ModuleForTests("foo", "android_common")
+		dex := foo.Rule("r8")
+		uncompressedInDexJar := strings.Contains(dex.Args["zipFlags"], "-L 0")
+		aligned := foo.MaybeRule("zipalign").Rule != nil
+
+		if uncompressedInDexJar != want {
+			t.Errorf("want uncompressed in dex %v, got %v", want, uncompressedInDexJar)
+		}
+
+		if aligned != want {
+			t.Errorf("want aligned %v, got %v", want, aligned)
+		}
+	}
+
+	for _, tt := range testCases {
+		t.Run(tt.name, func(t *testing.T) {
+			t.Run("platform", func(t *testing.T) {
+				test(t, tt.bp, tt.uncompressedPlatform, false)
+			})
+			t.Run("unbundled", func(t *testing.T) {
+				test(t, tt.bp, tt.uncompressedUnbundled, true)
+			})
+		})
+	}
+}
diff --git a/java/java_test.go b/java/java_test.go
index 22dec07..a98ea84 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -121,6 +121,10 @@
 		"b.kt":                   nil,
 		"a.jar":                  nil,
 		"b.jar":                  nil,
+		"APP_NOTICE":             nil,
+		"GENRULE_NOTICE":         nil,
+		"LIB_NOTICE":             nil,
+		"TOOL_NOTICE":            nil,
 		"java-res/a/a":           nil,
 		"java-res/b/b":           nil,
 		"java-res2/a":            nil,