Merge changes I0d679648,I31542e7f

* changes:
  Remove the EXPERIMENTAL_USE_OPENJDK11_TOOLCHAIN option.
  Add a new option OVERRIDE_JLINK_VERSION_NUMBER.
diff --git a/android/config.go b/android/config.go
index 63994e6..1e5a24d 100644
--- a/android/config.go
+++ b/android/config.go
@@ -1087,6 +1087,10 @@
 	return c.productVariables.EnforceSystemCertificateWhitelist
 }
 
+func (c *config) EnforceProductPartitionInterface() bool {
+	return Bool(c.productVariables.EnforceProductPartitionInterface)
+}
+
 func (c *config) ProductHiddenAPIStubs() []string {
 	return c.productVariables.ProductHiddenAPIStubs
 }
diff --git a/android/prebuilt_etc.go b/android/prebuilt_etc.go
index 6c4813b..6c80370 100644
--- a/android/prebuilt_etc.go
+++ b/android/prebuilt_etc.go
@@ -54,6 +54,12 @@
 	Installable *bool
 }
 
+type PrebuiltEtcModule interface {
+	Module
+	SubDir() string
+	OutputFile() OutputPath
+}
+
 type PrebuiltEtc struct {
 	ModuleBase
 
diff --git a/android/variable.go b/android/variable.go
index abbdf21..25a5dc0 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -309,6 +309,8 @@
 	TargetFSConfigGen []string `json:",omitempty"`
 
 	MissingUsesLibraries []string `json:",omitempty"`
+
+	EnforceProductPartitionInterface *bool `json:",omitempty"`
 }
 
 func boolPtr(v bool) *bool {
diff --git a/apex/apex.go b/apex/apex.go
index 4e6827f..f03a8f9 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -41,7 +41,6 @@
 	// TODO(b/113082813) make this configurable using config.fs syntax
 	generateFsConfig = pctx.StaticRule("generateFsConfig", blueprint.RuleParams{
 		Command: `echo '/ 1000 1000 0755' > ${out} && ` +
-			`echo '/apex_manifest.json 1000 1000 0644' >> ${out} && ` +
 			`echo ${ro_paths} | tr ' ' '\n' | awk '{print "/"$$1 " 1000 1000 0644"}' >> ${out} && ` +
 			`echo ${exec_paths} | tr ' ' '\n' | awk '{print "/"$$1 " 0 2000 0755"}' >> ${out}`,
 		Description: "fs_config ${out}",
@@ -57,6 +56,18 @@
 		Description: "prepare ${out}",
 	}, "provideNativeLibs", "requireNativeLibs", "opt")
 
+	stripApexManifestRule = pctx.StaticRule("stripApexManifestRule", blueprint.RuleParams{
+		Command:     `rm -f $out && ${conv_apex_manifest} strip $in -o $out`,
+		CommandDeps: []string{"${conv_apex_manifest}"},
+		Description: "strip ${in}=>${out}",
+	})
+
+	pbApexManifestRule = pctx.StaticRule("pbApexManifestRule", blueprint.RuleParams{
+		Command:     `rm -f $out && ${conv_apex_manifest} proto $in -o $out`,
+		CommandDeps: []string{"${conv_apex_manifest}"},
+		Description: "convert ${in}=>${out}",
+	})
+
 	// TODO(b/113233103): make sure that file_contexts is sane, i.e., validate
 	// against the binary policy using sefcontext_compiler -p <policy>.
 
@@ -66,6 +77,7 @@
 			`(. ${out}.copy_commands) && ` +
 			`APEXER_TOOL_PATH=${tool_path} ` +
 			`${apexer} --force --manifest ${manifest} ` +
+			`--manifest_json ${manifest_json} --manifest_json_full ${manifest_json_full} ` +
 			`--file_contexts ${file_contexts} ` +
 			`--canned_fs_config ${canned_fs_config} ` +
 			`--payload_type image ` +
@@ -76,20 +88,22 @@
 		Rspfile:        "${out}.copy_commands",
 		RspfileContent: "${copy_commands}",
 		Description:    "APEX ${image_dir} => ${out}",
-	}, "tool_path", "image_dir", "copy_commands", "manifest", "file_contexts", "canned_fs_config", "key", "opt_flags")
+	}, "tool_path", "image_dir", "copy_commands", "file_contexts", "canned_fs_config", "key", "opt_flags",
+		"manifest", "manifest_json", "manifest_json_full",
+	)
 
 	zipApexRule = pctx.StaticRule("zipApexRule", blueprint.RuleParams{
 		Command: `rm -rf ${image_dir} && mkdir -p ${image_dir} && ` +
 			`(. ${out}.copy_commands) && ` +
 			`APEXER_TOOL_PATH=${tool_path} ` +
-			`${apexer} --force --manifest ${manifest} ` +
+			`${apexer} --force --manifest ${manifest} --manifest_json_full ${manifest_json_full} ` +
 			`--payload_type zip ` +
 			`${image_dir} ${out} `,
 		CommandDeps:    []string{"${apexer}", "${merge_zips}", "${soong_zip}", "${zipalign}", "${aapt2}"},
 		Rspfile:        "${out}.copy_commands",
 		RspfileContent: "${copy_commands}",
 		Description:    "ZipAPEX ${image_dir} => ${out}",
-	}, "tool_path", "image_dir", "copy_commands", "manifest")
+	}, "tool_path", "image_dir", "copy_commands", "manifest", "manifest_json_full")
 
 	apexProtoConvertRule = pctx.AndroidStaticRule("apexProtoConvertRule",
 		blueprint.RuleParams{
@@ -181,6 +195,7 @@
 	pctx.HostBinToolVariable("zip2zip", "zip2zip")
 	pctx.HostBinToolVariable("zipalign", "zipalign")
 	pctx.HostBinToolVariable("jsonmodify", "jsonmodify")
+	pctx.HostBinToolVariable("conv_apex_manifest", "conv_apex_manifest")
 
 	android.RegisterModuleType("apex", BundleFactory)
 	android.RegisterModuleType("apex_test", testApexBundleFactory)
@@ -249,6 +264,9 @@
 		if vndkApex, ok := vndkApexList[vndkVersion]; ok {
 			mctx.AddReverseDependency(mctx.Module(), sharedLibTag, vndkApex)
 		}
+	} else if a, ok := mctx.Module().(*apexBundle); ok && a.vndkApex {
+		vndkVersion := proptools.StringDefault(a.vndkProperties.Vndk_version, "current")
+		mctx.AddDependency(mctx.Module(), prebuiltTag, cc.VndkLibrariesTxtModules(vndkVersion)...)
 	}
 }
 
@@ -634,10 +652,13 @@
 
 	testApex        bool
 	vndkApex        bool
+	artApex         bool
 	primaryApexType bool
 
 	// intermediate path for apex_manifest.json
-	manifestOut android.WritablePath
+	manifestJsonOut     android.WritablePath
+	manifestJsonFullOut android.WritablePath
+	manifestPbOut       android.WritablePath
 
 	// list of commands to create symlinks for backward compatibility
 	// these commands will be attached as LOCAL_POST_INSTALL_CMD to
@@ -975,7 +996,7 @@
 	return
 }
 
-func getCopyManifestForPrebuiltEtc(prebuilt *android.PrebuiltEtc) (fileToCopy android.Path, dirInApex string) {
+func getCopyManifestForPrebuiltEtc(prebuilt android.PrebuiltEtcModule) (fileToCopy android.Path, dirInApex string) {
 	dirInApex = filepath.Join("etc", prebuilt.SubDir())
 	fileToCopy = prebuilt.OutputFile()
 	return
@@ -1131,7 +1152,7 @@
 					ctx.PropertyErrorf("java_libs", "%q of type %q is not supported", depName, ctx.OtherModuleType(child))
 				}
 			case prebuiltTag:
-				if prebuilt, ok := child.(*android.PrebuiltEtc); ok {
+				if prebuilt, ok := child.(android.PrebuiltEtcModule); ok {
 					fileToCopy, dirInApex := getCopyManifestForPrebuiltEtc(prebuilt)
 					filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, etc, prebuilt, nil})
 					return true
@@ -1196,7 +1217,7 @@
 			if am, ok := child.(android.ApexModule); ok {
 				// We cannot use a switch statement on `depTag` here as the checked
 				// tags used below are private (e.g. `cc.sharedDepTag`).
-				if cc.IsSharedDepTag(depTag) || cc.IsRuntimeDepTag(depTag) {
+				if cc.IsSharedDepTag(depTag) || cc.IsRuntimeDepTag(depTag) || java.IsJniDepTag(depTag) {
 					if cc, ok := child.(*cc.Module); ok {
 						if android.InList(cc.Name(), providedNativeSharedLibs) {
 							// If we're using a shared library which is provided from other APEX,
@@ -1241,6 +1262,19 @@
 		return false
 	})
 
+	// Specific to the ART apex: dexpreopt artifacts for libcore Java libraries.
+	// Build rules are generated by the dexpreopt singleton, and here we access build artifacts
+	// via the global boot image config.
+	if a.artApex {
+		for arch, files := range java.DexpreoptedArtApexJars(ctx) {
+			dirInApex := filepath.Join("javalib", arch.String())
+			for _, f := range files {
+				localModule := "javalib_" + arch.String() + "_" + filepath.Base(f.String())
+				filesInfo = append(filesInfo, apexFile{f, localModule, dirInApex, etc, nil, nil})
+			}
+		}
+	}
+
 	if a.private_key_file == nil {
 		ctx.PropertyErrorf("key", "private_key for %q could not be found", String(a.properties.Key))
 		return
@@ -1288,9 +1322,24 @@
 	a.filesInfo = filesInfo
 
 	// prepare apex_manifest.json
-	a.manifestOut = android.PathForModuleOut(ctx, "apex_manifest.json")
+	a.buildManifest(ctx, provideNativeLibs, requireNativeLibs)
+
+	a.setCertificateAndPrivateKey(ctx)
+	if a.properties.ApexType == flattenedApex {
+		a.buildFlattenedApex(ctx)
+	} else {
+		a.buildUnflattenedApex(ctx)
+	}
+
+	apexName := proptools.StringDefault(a.properties.Apex_name, ctx.ModuleName())
+	a.compatSymlinks = makeCompatSymlinks(apexName, ctx)
+}
+
+func (a *apexBundle) buildManifest(ctx android.ModuleContext, provideNativeLibs, requireNativeLibs []string) {
 	manifestSrc := android.PathForModuleSrc(ctx, proptools.StringDefault(a.properties.Manifest, "apex_manifest.json"))
 
+	a.manifestJsonFullOut = android.PathForModuleOut(ctx, "apex_manifest_full.json")
+
 	// put dependency({provide|require}NativeLibs) in apex_manifest.json
 	provideNativeLibs = android.SortedUniqueStrings(provideNativeLibs)
 	requireNativeLibs = android.SortedUniqueStrings(android.RemoveListFromList(requireNativeLibs, provideNativeLibs))
@@ -1304,7 +1353,7 @@
 	ctx.Build(pctx, android.BuildParams{
 		Rule:   apexManifestRule,
 		Input:  manifestSrc,
-		Output: a.manifestOut,
+		Output: a.manifestJsonFullOut,
 		Args: map[string]string{
 			"provideNativeLibs": strings.Join(provideNativeLibs, " "),
 			"requireNativeLibs": strings.Join(requireNativeLibs, " "),
@@ -1312,15 +1361,22 @@
 		},
 	})
 
-	a.setCertificateAndPrivateKey(ctx)
-	if a.properties.ApexType == flattenedApex {
-		a.buildFlattenedApex(ctx)
-	} else {
-		a.buildUnflattenedApex(ctx)
-	}
+	// b/143654022 Q apexd can't understand newly added keys in apex_manifest.json
+	// prepare stripp-downed version so that APEX modules built from R+ can be installed to Q
+	a.manifestJsonOut = android.PathForModuleOut(ctx, "apex_manifest.json")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   stripApexManifestRule,
+		Input:  a.manifestJsonFullOut,
+		Output: a.manifestJsonOut,
+	})
 
-	apexName := proptools.StringDefault(a.properties.Apex_name, ctx.ModuleName())
-	a.compatSymlinks = makeCompatSymlinks(apexName, ctx)
+	// from R+, protobuf binary format (.pb) is the standard format for apex_manifest
+	a.manifestPbOut = android.PathForModuleOut(ctx, "apex_manifest.pb")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   pbApexManifestRule,
+		Input:  a.manifestJsonFullOut,
+		Output: a.manifestPbOut,
+	})
 }
 
 func (a *apexBundle) buildNoticeFile(ctx android.ModuleContext, apexFileName string) android.OptionalPath {
@@ -1382,7 +1438,7 @@
 	emitCommands = append(emitCommands, "sort -o "+imageContentFile.String()+" "+imageContentFile.String())
 
 	implicitInputs := append(android.Paths(nil), filesToCopy...)
-	implicitInputs = append(implicitInputs, a.manifestOut)
+	implicitInputs = append(implicitInputs, a.manifestPbOut, a.manifestJsonFullOut, a.manifestJsonOut)
 
 	if a.properties.Whitelisted_files != nil {
 		ctx.Build(pctx, android.BuildParams{
@@ -1418,7 +1474,7 @@
 
 	if apexType == imageApex {
 		// files and dirs that will be created in APEX
-		var readOnlyPaths []string
+		var readOnlyPaths = []string{"apex_manifest.json", "apex_manifest.pb"}
 		var executablePaths []string // this also includes dirs
 		for _, f := range a.filesInfo {
 			pathInApex := filepath.Join(f.installDir, f.builtFile.Base())
@@ -1516,14 +1572,16 @@
 			Output:      unsignedOutputFile,
 			Description: "apex (" + apexType.name() + ")",
 			Args: map[string]string{
-				"tool_path":        outHostBinDir + ":" + prebuiltSdkToolsBinDir,
-				"image_dir":        android.PathForModuleOut(ctx, "image"+suffix).String(),
-				"copy_commands":    strings.Join(copyCommands, " && "),
-				"manifest":         a.manifestOut.String(),
-				"file_contexts":    fileContexts.String(),
-				"canned_fs_config": cannedFsConfig.String(),
-				"key":              a.private_key_file.String(),
-				"opt_flags":        strings.Join(optFlags, " "),
+				"tool_path":          outHostBinDir + ":" + prebuiltSdkToolsBinDir,
+				"image_dir":          android.PathForModuleOut(ctx, "image"+suffix).String(),
+				"copy_commands":      strings.Join(copyCommands, " && "),
+				"manifest_json_full": a.manifestJsonFullOut.String(),
+				"manifest_json":      a.manifestJsonOut.String(),
+				"manifest":           a.manifestPbOut.String(),
+				"file_contexts":      fileContexts.String(),
+				"canned_fs_config":   cannedFsConfig.String(),
+				"key":                a.private_key_file.String(),
+				"opt_flags":          strings.Join(optFlags, " "),
 			},
 		})
 
@@ -1554,10 +1612,11 @@
 			Output:      unsignedOutputFile,
 			Description: "apex (" + apexType.name() + ")",
 			Args: map[string]string{
-				"tool_path":     outHostBinDir + ":" + prebuiltSdkToolsBinDir,
-				"image_dir":     android.PathForModuleOut(ctx, "image"+suffix).String(),
-				"copy_commands": strings.Join(copyCommands, " && "),
-				"manifest":      a.manifestOut.String(),
+				"tool_path":          outHostBinDir + ":" + prebuiltSdkToolsBinDir,
+				"image_dir":          android.PathForModuleOut(ctx, "image"+suffix).String(),
+				"copy_commands":      strings.Join(copyCommands, " && "),
+				"manifest":           a.manifestPbOut.String(),
+				"manifest_json_full": a.manifestJsonFullOut.String(),
 			},
 		})
 	}
@@ -1615,7 +1674,8 @@
 	if a.installable() {
 		// For flattened APEX, do nothing but make sure that apex_manifest.json and apex_pubkey are also copied along
 		// with other ordinary files.
-		a.filesInfo = append(a.filesInfo, apexFile{a.manifestOut, "apex_manifest.json." + ctx.ModuleName() + a.suffix, ".", etc, nil, nil})
+		a.filesInfo = append(a.filesInfo, apexFile{a.manifestJsonOut, "apex_manifest.json." + ctx.ModuleName() + a.suffix, ".", etc, nil, nil})
+		a.filesInfo = append(a.filesInfo, apexFile{a.manifestPbOut, "apex_manifest.pb." + ctx.ModuleName() + a.suffix, ".", etc, nil, nil})
 
 		// rename to apex_pubkey
 		copiedPubkey := android.PathForModuleOut(ctx, "apex_pubkey")
@@ -1752,7 +1812,7 @@
 		} else {
 			fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", fi.builtFile.Base())
 			// For flattened apexes, compat symlinks are attached to apex_manifest.json which is guaranteed for every apex
-			if a.primaryApexType && fi.builtFile.Base() == "apex_manifest.json" && len(a.compatSymlinks) > 0 {
+			if a.primaryApexType && fi.builtFile == a.manifestPbOut && len(a.compatSymlinks) > 0 {
 				fmt.Fprintln(w, "LOCAL_POST_INSTALL_CMD :=", strings.Join(a.compatSymlinks, " && "))
 			}
 			fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
@@ -1829,9 +1889,10 @@
 	return module
 }
 
-func ApexBundleFactory(testApex bool) android.Module {
+func ApexBundleFactory(testApex bool, artApex bool) android.Module {
 	bundle := newApexBundle()
 	bundle.testApex = testApex
+	bundle.artApex = artApex
 	return bundle
 }
 
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 77c1fb0..614164d 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -17,6 +17,7 @@
 import (
 	"io/ioutil"
 	"os"
+	"path"
 	"reflect"
 	"sort"
 	"strings"
@@ -117,6 +118,7 @@
 	ctx.RegisterModuleType("cc_test", android.ModuleFactoryAdaptor(cc.TestFactory))
 	ctx.RegisterModuleType("llndk_library", android.ModuleFactoryAdaptor(cc.LlndkLibraryFactory))
 	ctx.RegisterModuleType("vndk_prebuilt_shared", android.ModuleFactoryAdaptor(cc.VndkPrebuiltSharedFactory))
+	ctx.RegisterModuleType("vndk_libraries_txt", android.ModuleFactoryAdaptor(cc.VndkLibrariesTxtFactory))
 	ctx.RegisterModuleType("toolchain_library", android.ModuleFactoryAdaptor(cc.ToolchainLibraryFactory))
 	ctx.RegisterModuleType("prebuilt_etc", android.ModuleFactoryAdaptor(android.PrebuiltEtcFactory))
 	ctx.RegisterModuleType("sh_binary", android.ModuleFactoryAdaptor(android.ShBinaryFactory))
@@ -298,6 +300,7 @@
 		"framework/aidl/a.aidl":                      nil,
 		"build/make/core/proguard.flags":             nil,
 		"build/make/core/proguard_basic_keeps.flags": nil,
+		"dummy.txt":                                  nil,
 	}
 
 	for _, handler := range handlers {
@@ -517,6 +520,26 @@
 	ensureListContains(t, noticeInputs, "custom_notice")
 }
 
+func TestApexManifest(t *testing.T) {
+	ctx, _ := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+	`)
+
+	module := ctx.ModuleForTests("myapex", "android_common_myapex_image")
+	module.Output("apex_manifest.pb")
+	module.Output("apex_manifest.json")
+	module.Output("apex_manifest_full.json")
+}
+
 func TestBasicZipApex(t *testing.T) {
 	ctx, _ := testApex(t, `
 		apex {
@@ -1319,7 +1342,18 @@
 	apexRule := ctx.ModuleForTests(moduleName, "android_common_"+moduleName+"_image").Rule("apexRule")
 	copyCmds := apexRule.Args["copy_commands"]
 	imageApexDir := "/image.apex/"
-	dstFiles := []string{}
+	var failed bool
+	var surplus []string
+	filesMatched := make(map[string]bool)
+	addContent := func(content string) {
+		for _, expected := range files {
+			if matched, _ := path.Match(expected, content); matched {
+				filesMatched[expected] = true
+				return
+			}
+		}
+		surplus = append(surplus, content)
+	}
 	for _, cmd := range strings.Split(copyCmds, "&&") {
 		cmd = strings.TrimSpace(cmd)
 		if cmd == "" {
@@ -1338,42 +1372,26 @@
 				t.Fatal("copyCmds should copy a file to image.apex/", cmd)
 			}
 			dstFile := dst[index+len(imageApexDir):]
-			dstFiles = append(dstFiles, dstFile)
+			addContent(dstFile)
 		default:
 			t.Fatalf("copyCmds should contain mkdir/cp commands only: %q", cmd)
 		}
 	}
-	sort.Strings(dstFiles)
-	sort.Strings(files)
-	missing := []string{}
-	surplus := []string{}
-	i := 0
-	j := 0
-	for i < len(dstFiles) && j < len(files) {
-		if dstFiles[i] == files[j] {
-			i++
-			j++
-		} else if dstFiles[i] < files[j] {
-			surplus = append(surplus, dstFiles[i])
-			i++
-		} else {
-			missing = append(missing, files[j])
-			j++
-		}
-	}
-	if i < len(dstFiles) {
-		surplus = append(surplus, dstFiles[i:]...)
-	}
-	if j < len(files) {
-		missing = append(missing, files[j:]...)
-	}
 
-	failed := false
 	if len(surplus) > 0 {
+		sort.Strings(surplus)
 		t.Log("surplus files", surplus)
 		failed = true
 	}
-	if len(missing) > 0 {
+
+	if len(files) > len(filesMatched) {
+		var missing []string
+		for _, expected := range files {
+			if !filesMatched[expected] {
+				missing = append(missing, expected)
+			}
+		}
+		sort.Strings(missing)
 		t.Log("missing files", missing)
 		failed = true
 	}
@@ -1418,13 +1436,18 @@
 			system_shared_libs: [],
 			stl: "none",
 		}
-	`)
+	`+vndkLibrariesTxtFiles("current"))
 
 	ensureExactContents(t, ctx, "myapex", []string{
 		"lib/libvndk.so",
 		"lib/libvndksp.so",
 		"lib64/libvndk.so",
 		"lib64/libvndksp.so",
+		"etc/llndk.libraries.VER.txt",
+		"etc/vndkcore.libraries.VER.txt",
+		"etc/vndksp.libraries.VER.txt",
+		"etc/vndkprivate.libraries.VER.txt",
+		"etc/vndkcorevariant.libraries.VER.txt",
 	})
 }
 
@@ -1469,18 +1492,44 @@
 			system_shared_libs: [],
 			stl: "none",
 		}
-	`, withFiles(map[string][]byte{
-		"libvndk.so":     nil,
-		"libvndk.arm.so": nil,
-	}))
+		`+vndkLibrariesTxtFiles("current"),
+		withFiles(map[string][]byte{
+			"libvndk.so":     nil,
+			"libvndk.arm.so": nil,
+		}))
 
 	ensureExactContents(t, ctx, "myapex", []string{
 		"lib/libvndk.so",
 		"lib/libvndk.arm.so",
 		"lib64/libvndk.so",
+		"etc/*",
 	})
 }
 
+func vndkLibrariesTxtFiles(vers ...string) (result string) {
+	for _, v := range vers {
+		if v == "current" {
+			for _, txt := range []string{"llndk", "vndkcore", "vndksp", "vndkprivate", "vndkcorevariant"} {
+				result += `
+					vndk_libraries_txt {
+						name: "` + txt + `.libraries.txt",
+					}
+				`
+			}
+		} else {
+			for _, txt := range []string{"llndk", "vndkcore", "vndksp", "vndkprivate"} {
+				result += `
+					prebuilt_etc {
+						name: "` + txt + `.libraries.` + v + `.txt",
+						src: "dummy.txt",
+					}
+				`
+			}
+		}
+	}
+	return
+}
+
 func TestVndkApexVersion(t *testing.T) {
 	ctx, _ := testApex(t, `
 		apex_vndk {
@@ -1530,17 +1579,19 @@
 					srcs: ["libvndk27_x86_64.so"],
 				},
 			},
-	}
-	`, withFiles(map[string][]byte{
-		"libvndk27_arm.so":    nil,
-		"libvndk27_arm64.so":  nil,
-		"libvndk27_x86.so":    nil,
-		"libvndk27_x86_64.so": nil,
-	}))
+		}
+		`+vndkLibrariesTxtFiles("27"),
+		withFiles(map[string][]byte{
+			"libvndk27_arm.so":    nil,
+			"libvndk27_arm64.so":  nil,
+			"libvndk27_x86.so":    nil,
+			"libvndk27_x86_64.so": nil,
+		}))
 
 	ensureExactContents(t, ctx, "myapex_v27", []string{
 		"lib/libvndk27_arm.so",
 		"lib64/libvndk27_arm64.so",
+		"etc/*",
 	})
 }
 
@@ -1607,7 +1658,7 @@
 			name: "myapex.key",
 			public_key: "testkey.avbpubkey",
 			private_key: "testkey.pem",
-		}`)
+		}`+vndkLibrariesTxtFiles("28", "current"))
 
 	assertApexName := func(expected, moduleName string) {
 		bundle := ctx.ModuleForTests(moduleName, "android_common_"+moduleName+"_image").Module().(*apexBundle)
@@ -1647,18 +1698,20 @@
 			system_shared_libs: [],
 			stl: "none",
 		}
-	`, withTargets(map[android.OsType][]android.Target{
-		android.Android: []android.Target{
-			{Os: android.Android, Arch: android.Arch{ArchType: android.Arm64, ArchVariant: "armv8-a", Abi: []string{"arm64-v8a"}}, NativeBridge: android.NativeBridgeDisabled, NativeBridgeHostArchName: "", NativeBridgeRelativePath: ""},
-			{Os: android.Android, Arch: android.Arch{ArchType: android.Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}}, NativeBridge: android.NativeBridgeDisabled, NativeBridgeHostArchName: "", NativeBridgeRelativePath: ""},
-			{Os: android.Android, Arch: android.Arch{ArchType: android.X86_64, ArchVariant: "silvermont", Abi: []string{"arm64-v8a"}}, NativeBridge: android.NativeBridgeEnabled, NativeBridgeHostArchName: "arm64", NativeBridgeRelativePath: "x86_64"},
-			{Os: android.Android, Arch: android.Arch{ArchType: android.X86, ArchVariant: "silvermont", Abi: []string{"armeabi-v7a"}}, NativeBridge: android.NativeBridgeEnabled, NativeBridgeHostArchName: "arm", NativeBridgeRelativePath: "x86"},
-		},
-	}))
+		`+vndkLibrariesTxtFiles("current"),
+		withTargets(map[android.OsType][]android.Target{
+			android.Android: []android.Target{
+				{Os: android.Android, Arch: android.Arch{ArchType: android.Arm64, ArchVariant: "armv8-a", Abi: []string{"arm64-v8a"}}, NativeBridge: android.NativeBridgeDisabled, NativeBridgeHostArchName: "", NativeBridgeRelativePath: ""},
+				{Os: android.Android, Arch: android.Arch{ArchType: android.Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}}, NativeBridge: android.NativeBridgeDisabled, NativeBridgeHostArchName: "", NativeBridgeRelativePath: ""},
+				{Os: android.Android, Arch: android.Arch{ArchType: android.X86_64, ArchVariant: "silvermont", Abi: []string{"arm64-v8a"}}, NativeBridge: android.NativeBridgeEnabled, NativeBridgeHostArchName: "arm64", NativeBridgeRelativePath: "x86_64"},
+				{Os: android.Android, Arch: android.Arch{ArchType: android.X86, ArchVariant: "silvermont", Abi: []string{"armeabi-v7a"}}, NativeBridge: android.NativeBridgeEnabled, NativeBridgeHostArchName: "arm", NativeBridgeRelativePath: "x86"},
+			},
+		}))
 
 	ensureExactContents(t, ctx, "myapex", []string{
 		"lib/libvndk.so",
 		"lib64/libvndk.so",
+		"etc/*",
 	})
 }
 
@@ -1693,8 +1746,7 @@
 }
 
 func TestVndkApexWithBinder32(t *testing.T) {
-	ctx, _ := testApex(t,
-		`
+	ctx, _ := testApex(t, `
 		apex_vndk {
 			name: "myapex_v27",
 			key: "myapex.key",
@@ -1738,7 +1790,7 @@
 				}
 			},
 		}
-		`,
+		`+vndkLibrariesTxtFiles("27"),
 		withFiles(map[string][]byte{
 			"libvndk27.so":         nil,
 			"libvndk27binder32.so": nil,
@@ -1753,6 +1805,7 @@
 
 	ensureExactContents(t, ctx, "myapex_v27", []string{
 		"lib/libvndk27binder32.so",
+		"etc/*",
 	})
 }
 
@@ -2460,6 +2513,7 @@
 			srcs: ["foo/bar/MyClass.java"],
 			sdk_version: "none",
 			system_modules: "none",
+			jni_libs: ["libjni"],
 		}
 
 		android_app {
@@ -2469,6 +2523,13 @@
 			system_modules: "none",
 			privileged: true,
 		}
+
+		cc_library_shared {
+			name: "libjni",
+			srcs: ["mylib.cpp"],
+			stl: "none",
+			system_shared_libs: [],
+		}
 	`)
 
 	module := ctx.ModuleForTests("myapex", "android_common_myapex_image")
@@ -2477,6 +2538,7 @@
 
 	ensureContains(t, copyCmds, "image.apex/app/AppFoo/AppFoo.apk")
 	ensureContains(t, copyCmds, "image.apex/priv-app/AppFooPriv/AppFooPriv.apk")
+	ensureContains(t, copyCmds, "image.apex/lib64/libjni.so")
 }
 
 func TestApexWithAppImports(t *testing.T) {
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 91a3c99..ff181d8 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -327,14 +327,15 @@
 			filepath.Dir(fuzz.config.String())+":config.json")
 	}
 
-	if len(fuzzFiles) > 0 {
-		ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
-			fmt.Fprintln(w, "LOCAL_TEST_DATA := "+strings.Join(fuzzFiles, " "))
-		})
-	}
-
 	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
 		fmt.Fprintln(w, "LOCAL_IS_FUZZ_TARGET := true")
+		if len(fuzzFiles) > 0 {
+			fmt.Fprintln(w, "LOCAL_TEST_DATA := "+strings.Join(fuzzFiles, " "))
+		}
+		if fuzz.installedSharedDeps != nil {
+			fmt.Fprintln(w, "LOCAL_FUZZ_INSTALLED_SHARED_DEPS :="+
+				strings.Join(fuzz.installedSharedDeps, " "))
+		}
 	})
 }
 
diff --git a/cc/binary.go b/cc/binary.go
index b27142c..617d4dd 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -227,7 +227,7 @@
 
 	if ctx.Host() && !ctx.Windows() && !binary.static() {
 		if !ctx.Config().IsEnvTrue("DISABLE_HOST_PIE") {
-			flags.LdFlags = append(flags.LdFlags, "-pie")
+			flags.Global.LdFlags = append(flags.Global.LdFlags, "-pie")
 		}
 	}
 
@@ -235,7 +235,7 @@
 	// all code is position independent, and then those warnings get promoted to
 	// errors.
 	if !ctx.Windows() {
-		flags.CFlags = append(flags.CFlags, "-fPIE")
+		flags.Global.CFlags = append(flags.Global.CFlags, "-fPIE")
 	}
 
 	if ctx.toolchain().Bionic() {
@@ -244,11 +244,11 @@
 			// However, bionic/linker uses -shared to overwrite.
 			// Linker for x86 targets does not allow coexistance of -static and -shared,
 			// so we add -static only if -shared is not used.
-			if !inList("-shared", flags.LdFlags) {
-				flags.LdFlags = append(flags.LdFlags, "-static")
+			if !inList("-shared", flags.Local.LdFlags) {
+				flags.Global.LdFlags = append(flags.Global.LdFlags, "-static")
 			}
 
-			flags.LdFlags = append(flags.LdFlags,
+			flags.Global.LdFlags = append(flags.Global.LdFlags,
 				"-nostdlib",
 				"-Bstatic",
 				"-Wl,--gc-sections",
@@ -278,14 +278,14 @@
 				if ctx.Os() == android.LinuxBionic {
 					// Use the dlwrap entry point, but keep _start around so
 					// that it can be used by host_bionic_inject
-					flags.LdFlags = append(flags.LdFlags,
+					flags.Global.LdFlags = append(flags.Global.LdFlags,
 						"-Wl,--entry=__dlwrap__start",
 						"-Wl,--undefined=_start",
 					)
 				}
 			}
 
-			flags.LdFlags = append(flags.LdFlags,
+			flags.Global.LdFlags = append(flags.Global.LdFlags,
 				"-pie",
 				"-nostdlib",
 				"-Bdynamic",
@@ -295,10 +295,10 @@
 		}
 	} else {
 		if binary.static() {
-			flags.LdFlags = append(flags.LdFlags, "-static")
+			flags.Global.LdFlags = append(flags.Global.LdFlags, "-static")
 		}
 		if ctx.Darwin() {
-			flags.LdFlags = append(flags.LdFlags, "-Wl,-headerpad_max_install_names")
+			flags.Global.LdFlags = append(flags.Global.LdFlags, "-Wl,-headerpad_max_install_names")
 		}
 	}
 
@@ -315,14 +315,14 @@
 	var linkerDeps android.Paths
 
 	if deps.LinkerFlagsFile.Valid() {
-		flags.LdFlags = append(flags.LdFlags, "$$(cat "+deps.LinkerFlagsFile.String()+")")
+		flags.Local.LdFlags = append(flags.Local.LdFlags, "$$(cat "+deps.LinkerFlagsFile.String()+")")
 		linkerDeps = append(linkerDeps, deps.LinkerFlagsFile.Path())
 	}
 
 	if flags.DynamicLinker != "" {
-		flags.LdFlags = append(flags.LdFlags, "-Wl,-dynamic-linker,"+flags.DynamicLinker)
+		flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-dynamic-linker,"+flags.DynamicLinker)
 	} else if ctx.toolchain().Bionic() && !binary.static() {
-		flags.LdFlags = append(flags.LdFlags, "-Wl,--no-dynamic-linker")
+		flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--no-dynamic-linker")
 	}
 
 	builderFlags := flagsToBuilderFlags(flags)
diff --git a/cc/builder.go b/cc/builder.go
index 068c930..1ec323f 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -261,27 +261,37 @@
 }
 
 type builderFlags struct {
-	globalFlags     string
-	arFlags         string
-	asFlags         string
-	cFlags          string
-	toolingCFlags   string // A separate set of cFlags for clang LibTooling tools
-	toolingCppFlags string // A separate set of cppFlags for clang LibTooling tools
-	conlyFlags      string
-	cppFlags        string
-	ldFlags         string
-	libFlags        string
-	extraLibFlags   string
-	tidyFlags       string
-	sAbiFlags       string
-	yasmFlags       string
-	aidlFlags       string
-	rsFlags         string
-	toolchain       config.Toolchain
-	tidy            bool
-	coverage        bool
-	sAbiDump        bool
-	emitXrefs       bool
+	globalCommonFlags     string
+	globalAsFlags         string
+	globalYasmFlags       string
+	globalCFlags          string
+	globalToolingCFlags   string // A separate set of cFlags for clang LibTooling tools
+	globalToolingCppFlags string // A separate set of cppFlags for clang LibTooling tools
+	globalConlyFlags      string
+	globalCppFlags        string
+	globalLdFlags         string
+
+	localCommonFlags     string
+	localAsFlags         string
+	localYasmFlags       string
+	localCFlags          string
+	localToolingCFlags   string // A separate set of cFlags for clang LibTooling tools
+	localToolingCppFlags string // A separate set of cppFlags for clang LibTooling tools
+	localConlyFlags      string
+	localCppFlags        string
+	localLdFlags         string
+
+	libFlags      string
+	extraLibFlags string
+	tidyFlags     string
+	sAbiFlags     string
+	aidlFlags     string
+	rsFlags       string
+	toolchain     config.Toolchain
+	tidy          bool
+	coverage      bool
+	sAbiDump      bool
+	emitXrefs     bool
 
 	assemblerWithCpp bool
 
@@ -349,39 +359,45 @@
 		kytheFiles = make(android.Paths, 0, len(srcFiles))
 	}
 
-	commonFlags := strings.Join([]string{
-		flags.globalFlags,
-		flags.systemIncludeFlags,
-	}, " ")
+	// Produce fully expanded flags for use by C tools, C compiles, C++ tools, C++ compiles, and asm compiles
+	// respectively.
+	toolingCflags := flags.globalCommonFlags + " " +
+		flags.globalToolingCFlags + " " +
+		flags.globalConlyFlags + " " +
+		flags.localCommonFlags + " " +
+		flags.localToolingCFlags + " " +
+		flags.localConlyFlags + " " +
+		flags.systemIncludeFlags
 
-	toolingCflags := strings.Join([]string{
-		commonFlags,
-		flags.toolingCFlags,
-		flags.conlyFlags,
-	}, " ")
+	cflags := flags.globalCommonFlags + " " +
+		flags.globalCFlags + " " +
+		flags.globalConlyFlags + " " +
+		flags.localCommonFlags + " " +
+		flags.localCFlags + " " +
+		flags.localConlyFlags + " " +
+		flags.systemIncludeFlags
 
-	cflags := strings.Join([]string{
-		commonFlags,
-		flags.cFlags,
-		flags.conlyFlags,
-	}, " ")
+	toolingCppflags := flags.globalCommonFlags + " " +
+		flags.globalToolingCFlags + " " +
+		flags.globalToolingCppFlags + " " +
+		flags.localCommonFlags + " " +
+		flags.localToolingCFlags + " " +
+		flags.localToolingCppFlags + " " +
+		flags.systemIncludeFlags
 
-	toolingCppflags := strings.Join([]string{
-		commonFlags,
-		flags.toolingCFlags,
-		flags.toolingCppFlags,
-	}, " ")
+	cppflags := flags.globalCommonFlags + " " +
+		flags.globalCFlags + " " +
+		flags.globalCppFlags + " " +
+		flags.localCommonFlags + " " +
+		flags.localCFlags + " " +
+		flags.localCppFlags + " " +
+		flags.systemIncludeFlags
 
-	cppflags := strings.Join([]string{
-		commonFlags,
-		flags.cFlags,
-		flags.cppFlags,
-	}, " ")
-
-	asflags := strings.Join([]string{
-		commonFlags,
-		flags.asFlags,
-	}, " ")
+	asflags := flags.globalCommonFlags + " " +
+		flags.globalAsFlags + " " +
+		flags.localCommonFlags + " " +
+		flags.localAsFlags + " " +
+		flags.systemIncludeFlags
 
 	var sAbiDumpFiles android.Paths
 	if flags.sAbiDump {
@@ -408,7 +424,7 @@
 				Implicits:   cFlagsDeps,
 				OrderOnly:   pathDeps,
 				Args: map[string]string{
-					"asFlags": flags.yasmFlags,
+					"asFlags": flags.globalYasmFlags + " " + flags.localYasmFlags,
 				},
 			})
 			continue
@@ -431,8 +447,9 @@
 			continue
 		}
 
-		var moduleCflags string
-		var moduleToolingCflags string
+		var moduleFlags string
+		var moduleToolingFlags string
+
 		var ccCmd string
 		tidy := flags.tidy
 		coverage := flags.coverage
@@ -448,19 +465,19 @@
 			fallthrough
 		case ".S":
 			ccCmd = "clang"
-			moduleCflags = asflags
+			moduleFlags = asflags
 			tidy = false
 			coverage = false
 			dump = false
 			emitXref = false
 		case ".c":
 			ccCmd = "clang"
-			moduleCflags = cflags
-			moduleToolingCflags = toolingCflags
+			moduleFlags = cflags
+			moduleToolingFlags = toolingCflags
 		case ".cpp", ".cc", ".cxx", ".mm":
 			ccCmd = "clang++"
-			moduleCflags = cppflags
-			moduleToolingCflags = toolingCppflags
+			moduleFlags = cppflags
+			moduleToolingFlags = toolingCppflags
 		default:
 			ctx.ModuleErrorf("File %s has unknown extension", srcFile)
 			continue
@@ -486,7 +503,7 @@
 			Implicits:       cFlagsDeps,
 			OrderOnly:       pathDeps,
 			Args: map[string]string{
-				"cFlags": moduleCflags,
+				"cFlags": moduleFlags,
 				"ccCmd":  ccCmd,
 			},
 		})
@@ -501,7 +518,7 @@
 				Implicits:   cFlagsDeps,
 				OrderOnly:   pathDeps,
 				Args: map[string]string{
-					"cFlags": moduleCflags,
+					"cFlags": moduleFlags,
 				},
 			})
 			kytheFiles = append(kytheFiles, kytheFile)
@@ -522,7 +539,7 @@
 				Implicits: cFlagsDeps,
 				OrderOnly: pathDeps,
 				Args: map[string]string{
-					"cFlags":    moduleToolingCflags,
+					"cFlags":    moduleToolingFlags,
 					"tidyFlags": flags.tidyFlags,
 				},
 			})
@@ -541,7 +558,7 @@
 				Implicits:   cFlagsDeps,
 				OrderOnly:   pathDeps,
 				Args: map[string]string{
-					"cFlags":     moduleToolingCflags,
+					"cFlags":     moduleToolingFlags,
 					"exportDirs": flags.sAbiFlags,
 				},
 			})
@@ -567,9 +584,6 @@
 	if !ctx.Darwin() {
 		arFlags += " -format=gnu"
 	}
-	if flags.arFlags != "" {
-		arFlags += " " + flags.arFlags
-	}
 
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        ar,
@@ -651,7 +665,7 @@
 			"crtBegin":      crtBegin.String(),
 			"libFlags":      strings.Join(libFlagsList, " "),
 			"extraLibFlags": flags.extraLibFlags,
-			"ldFlags":       flags.ldFlags,
+			"ldFlags":       flags.globalLdFlags + " " + flags.localLdFlags,
 			"crtEnd":        crtEnd.String(),
 		},
 	})
@@ -788,7 +802,7 @@
 		Implicits:   deps,
 		Args: map[string]string{
 			"ldCmd":   ldCmd,
-			"ldFlags": flags.ldFlags,
+			"ldFlags": flags.globalLdFlags + " " + flags.localLdFlags,
 		},
 	})
 }
diff --git a/cc/cc.go b/cc/cc.go
index db7f0f6..840fe24 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -140,26 +140,34 @@
 	DynamicLinker android.OptionalPath
 }
 
-type Flags struct {
-	GlobalFlags     []string // Flags that apply to C, C++, and assembly source files
-	ArFlags         []string // Flags that apply to ar
+// LocalOrGlobalFlags contains flags that need to have values set globally by the build system or locally by the module
+// tracked separately, in order to maintain the required ordering (most of the global flags need to go first on the
+// command line so they can be overridden by the local module flags).
+type LocalOrGlobalFlags struct {
+	CommonFlags     []string // Flags that apply to C, C++, and assembly source files
 	AsFlags         []string // Flags that apply to assembly source files
+	YasmFlags       []string // Flags that apply to yasm assembly source files
 	CFlags          []string // Flags that apply to C and C++ source files
 	ToolingCFlags   []string // Flags that apply to C and C++ source files parsed by clang LibTooling tools
 	ConlyFlags      []string // Flags that apply to C source files
 	CppFlags        []string // Flags that apply to C++ source files
 	ToolingCppFlags []string // Flags that apply to C++ source files parsed by clang LibTooling tools
-	aidlFlags       []string // Flags that apply to aidl source files
-	rsFlags         []string // Flags that apply to renderscript source files
 	LdFlags         []string // Flags that apply to linker command lines
-	libFlags        []string // Flags to add libraries early to the link order
-	extraLibFlags   []string // Flags to add libraries late in the link order after LdFlags
-	TidyFlags       []string // Flags that apply to clang-tidy
-	SAbiFlags       []string // Flags that apply to header-abi-dumper
-	YasmFlags       []string // Flags that apply to yasm assembly source files
+}
+
+type Flags struct {
+	Local  LocalOrGlobalFlags
+	Global LocalOrGlobalFlags
+
+	aidlFlags     []string // Flags that apply to aidl source files
+	rsFlags       []string // Flags that apply to renderscript source files
+	libFlags      []string // Flags to add libraries early to the link order
+	extraLibFlags []string // Flags to add libraries late in the link order after LdFlags
+	TidyFlags     []string // Flags that apply to clang-tidy
+	SAbiFlags     []string // Flags that apply to header-abi-dumper
 
 	// Global include flags that apply to C, C++, and assembly source files
-	// These must be after any module include flags, which will be in GlobalFlags.
+	// These must be after any module include flags, which will be in CommonFlags.
 	SystemIncludeFlags []string
 
 	Toolchain config.Toolchain
@@ -858,26 +866,33 @@
 	if flagsProducer, ok := c.linker.(exportedFlagsProducer); ok {
 		return flagsProducer.exportedDirs()
 	}
-	return []android.Path{}
+	return nil
 }
 
 func (c *Module) ExportedSystemIncludeDirs() android.Paths {
 	if flagsProducer, ok := c.linker.(exportedFlagsProducer); ok {
 		return flagsProducer.exportedSystemDirs()
 	}
-	return []android.Path{}
+	return nil
 }
 
 func (c *Module) ExportedFlags() []string {
 	if flagsProducer, ok := c.linker.(exportedFlagsProducer); ok {
 		return flagsProducer.exportedFlags()
 	}
-	return []string{}
+	return nil
+}
+
+func (c *Module) ExportedDeps() android.Paths {
+	if flagsProducer, ok := c.linker.(exportedFlagsProducer); ok {
+		return flagsProducer.exportedDeps()
+	}
+	return nil
 }
 
 func isBionic(name string) bool {
 	switch name {
-	case "libc", "libm", "libdl", "libdl_android", "linker":
+	case "libc", "libm", "libdl", "linker":
 		return true
 	}
 	return false
@@ -1277,17 +1292,17 @@
 		return
 	}
 
-	flags.CFlags, _ = filterList(flags.CFlags, config.IllegalFlags)
-	flags.CppFlags, _ = filterList(flags.CppFlags, config.IllegalFlags)
-	flags.ConlyFlags, _ = filterList(flags.ConlyFlags, config.IllegalFlags)
+	flags.Local.CFlags, _ = filterList(flags.Local.CFlags, config.IllegalFlags)
+	flags.Local.CppFlags, _ = filterList(flags.Local.CppFlags, config.IllegalFlags)
+	flags.Local.ConlyFlags, _ = filterList(flags.Local.ConlyFlags, config.IllegalFlags)
 
-	flags.GlobalFlags = append(flags.GlobalFlags, deps.Flags...)
+	flags.Local.CommonFlags = append(flags.Local.CommonFlags, deps.Flags...)
 
 	for _, dir := range deps.IncludeDirs {
-		flags.GlobalFlags = append(flags.GlobalFlags, "-I"+dir.String())
+		flags.Local.CommonFlags = append(flags.Local.CommonFlags, "-I"+dir.String())
 	}
 	for _, dir := range deps.SystemIncludeDirs {
-		flags.GlobalFlags = append(flags.GlobalFlags, "-isystem "+dir.String())
+		flags.Local.CommonFlags = append(flags.Local.CommonFlags, "-isystem "+dir.String())
 	}
 
 	c.flags = flags
@@ -1296,16 +1311,16 @@
 		flags = c.sabi.flags(ctx, flags)
 	}
 
-	flags.AssemblerWithCpp = inList("-xassembler-with-cpp", flags.AsFlags)
+	flags.AssemblerWithCpp = inList("-xassembler-with-cpp", flags.Local.AsFlags)
 
 	// Optimization to reduce size of build.ninja
 	// Replace the long list of flags for each file with a module-local variable
-	ctx.Variable(pctx, "cflags", strings.Join(flags.CFlags, " "))
-	ctx.Variable(pctx, "cppflags", strings.Join(flags.CppFlags, " "))
-	ctx.Variable(pctx, "asflags", strings.Join(flags.AsFlags, " "))
-	flags.CFlags = []string{"$cflags"}
-	flags.CppFlags = []string{"$cppflags"}
-	flags.AsFlags = []string{"$asflags"}
+	ctx.Variable(pctx, "cflags", strings.Join(flags.Local.CFlags, " "))
+	ctx.Variable(pctx, "cppflags", strings.Join(flags.Local.CppFlags, " "))
+	ctx.Variable(pctx, "asflags", strings.Join(flags.Local.AsFlags, " "))
+	flags.Local.CFlags = []string{"$cflags"}
+	flags.Local.CppFlags = []string{"$cppflags"}
+	flags.Local.AsFlags = []string{"$asflags"}
 
 	var objs Objects
 	if c.compiler != nil {
diff --git a/cc/cc_test.go b/cc/cc_test.go
index dcf117c..0cbdd52 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -248,27 +248,45 @@
 	}
 }
 
-func checkVndkSnapshot(t *testing.T, ctx *android.TestContext, name, subDir, variant string) {
+func checkVndkSnapshot(t *testing.T, ctx *android.TestContext, moduleName, snapshotFilename, subDir, variant string) {
 	vndkSnapshot := ctx.SingletonForTests("vndk-snapshot")
 
-	mod := ctx.ModuleForTests(name, variant).Module().(*Module)
-	if !mod.outputFile.Valid() {
-		t.Errorf("%q must have output\n", name)
+	mod, ok := ctx.ModuleForTests(moduleName, variant).Module().(android.OutputFileProducer)
+	if !ok {
+		t.Errorf("%q must have output\n", moduleName)
 		return
 	}
-	snapshotPath := filepath.Join(subDir, mod.outputFile.Path().Base())
+	outputFiles, err := mod.OutputFiles("")
+	if err != nil || len(outputFiles) != 1 {
+		t.Errorf("%q must have single output\n", moduleName)
+		return
+	}
+	snapshotPath := filepath.Join(subDir, snapshotFilename)
 
 	out := vndkSnapshot.Output(snapshotPath)
-	if out.Input != mod.outputFile.Path() {
-		t.Errorf("The input of VNDK snapshot must be %q, but %q", out.Input.String(), mod.outputFile.String())
+	if out.Input.String() != outputFiles[0].String() {
+		t.Errorf("The input of VNDK snapshot must be %q, but %q", out.Input.String(), outputFiles[0])
 	}
 }
 
+func checkWriteFileOutput(t *testing.T, params android.TestingBuildParams, expected []string) {
+	t.Helper()
+	assertString(t, params.Rule.String(), android.WriteFile.String())
+	actual := strings.FieldsFunc(strings.ReplaceAll(params.Args["content"], "\\n", "\n"), func(r rune) bool { return r == '\n' })
+	assertArrayString(t, actual, expected)
+}
+
 func checkVndkOutput(t *testing.T, ctx *android.TestContext, output string, expected []string) {
 	t.Helper()
 	vndkSnapshot := ctx.SingletonForTests("vndk-snapshot")
-	actual := strings.FieldsFunc(strings.ReplaceAll(vndkSnapshot.Output(output).Args["content"], "\\n", "\n"), func(r rune) bool { return r == '\n' })
-	assertArrayString(t, actual, expected)
+	checkWriteFileOutput(t, vndkSnapshot.Output(output), expected)
+}
+
+func checkVndkLibrariesOutput(t *testing.T, ctx *android.TestContext, module string, expected []string) {
+	t.Helper()
+	vndkLibraries := ctx.ModuleForTests(module, "")
+	output := insertVndkVersion(module, "VER")
+	checkWriteFileOutput(t, vndkLibraries.Output(output), expected)
 }
 
 func TestVndk(t *testing.T) {
@@ -321,6 +339,21 @@
 				},
 			},
 		}
+		vndk_libraries_txt {
+			name: "llndk.libraries.txt",
+		}
+		vndk_libraries_txt {
+			name: "vndkcore.libraries.txt",
+		}
+		vndk_libraries_txt {
+			name: "vndksp.libraries.txt",
+		}
+		vndk_libraries_txt {
+			name: "vndkprivate.libraries.txt",
+		}
+		vndk_libraries_txt {
+			name: "vndkcorevariant.libraries.txt",
+		}
 	`, config)
 
 	checkVndkModule(t, ctx, "libvndk", "vndk-VER", false, "")
@@ -346,17 +379,17 @@
 	variant := "android_arm64_armv8-a_vendor.VER_shared"
 	variant2nd := "android_arm_armv7-a-neon_vendor.VER_shared"
 
-	checkVndkSnapshot(t, ctx, "libvndk", vndkCoreLibPath, variant)
-	checkVndkSnapshot(t, ctx, "libvndk", vndkCoreLib2ndPath, variant2nd)
-	checkVndkSnapshot(t, ctx, "libvndk_sp", vndkSpLibPath, variant)
-	checkVndkSnapshot(t, ctx, "libvndk_sp", vndkSpLib2ndPath, variant2nd)
+	checkVndkSnapshot(t, ctx, "libvndk", "libvndk.so", vndkCoreLibPath, variant)
+	checkVndkSnapshot(t, ctx, "libvndk", "libvndk.so", vndkCoreLib2ndPath, variant2nd)
+	checkVndkSnapshot(t, ctx, "libvndk_sp", "libvndk_sp-x.so", vndkSpLibPath, variant)
+	checkVndkSnapshot(t, ctx, "libvndk_sp", "libvndk_sp-x.so", vndkSpLib2ndPath, variant2nd)
 
-	checkVndkOutput(t, ctx, "vndk/llndk.libraries.txt", []string{"libc.so", "libdl.so", "libft2.so", "libm.so"})
-	checkVndkOutput(t, ctx, "vndk/vndkcore.libraries.txt", []string{"libvndk-private.so", "libvndk.so"})
-	checkVndkOutput(t, ctx, "vndk/vndkprivate.libraries.txt", []string{"libft2.so", "libvndk-private.so", "libvndk_sp_private-x.so"})
-	checkVndkOutput(t, ctx, "vndk/vndksp.libraries.txt", []string{"libc++.so", "libvndk_sp-x.so", "libvndk_sp_private-x.so"})
-	checkVndkOutput(t, ctx, "vndk/vndkcorevariant.libraries.txt", nil)
-	// merged & tagged & filtered-out(libclang_rt)
+	snapshotConfigsPath := filepath.Join(snapshotVariantPath, "configs")
+	checkVndkSnapshot(t, ctx, "llndk.libraries.txt", "llndk.libraries.txt", snapshotConfigsPath, "")
+	checkVndkSnapshot(t, ctx, "vndkcore.libraries.txt", "vndkcore.libraries.txt", snapshotConfigsPath, "")
+	checkVndkSnapshot(t, ctx, "vndksp.libraries.txt", "vndksp.libraries.txt", snapshotConfigsPath, "")
+	checkVndkSnapshot(t, ctx, "vndkprivate.libraries.txt", "vndkprivate.libraries.txt", snapshotConfigsPath, "")
+
 	checkVndkOutput(t, ctx, "vndk/vndk.libraries.txt", []string{
 		"LLNDK: libc.so",
 		"LLNDK: libdl.so",
@@ -371,19 +404,25 @@
 		"VNDK-private: libvndk-private.so",
 		"VNDK-private: libvndk_sp_private-x.so",
 	})
-	checkVndkOutput(t, ctx, "vndk/llndk.libraries.txt", []string{
-		"libc.so", "libdl.so", "libft2.so", "libm.so",
-	})
-	checkVndkOutput(t, ctx, "vndk/vndkcore.libraries.txt", []string{
-		"libvndk-private.so", "libvndk.so",
-	})
-	checkVndkOutput(t, ctx, "vndk/vndksp.libraries.txt", []string{
-		"libc++.so", "libvndk_sp-x.so", "libvndk_sp_private-x.so",
-	})
-	checkVndkOutput(t, ctx, "vndk/vndkprivate.libraries.txt", []string{
-		"libft2.so", "libvndk-private.so", "libvndk_sp_private-x.so",
-	})
-	checkVndkOutput(t, ctx, "vndk/vndkcorevariant.libraries.txt", []string{})
+	checkVndkLibrariesOutput(t, ctx, "llndk.libraries.txt", []string{"libc.so", "libdl.so", "libft2.so", "libm.so"})
+	checkVndkLibrariesOutput(t, ctx, "vndkcore.libraries.txt", []string{"libvndk-private.so", "libvndk.so"})
+	checkVndkLibrariesOutput(t, ctx, "vndkprivate.libraries.txt", []string{"libft2.so", "libvndk-private.so", "libvndk_sp_private-x.so"})
+	checkVndkLibrariesOutput(t, ctx, "vndksp.libraries.txt", []string{"libc++.so", "libvndk_sp-x.so", "libvndk_sp_private-x.so"})
+	checkVndkLibrariesOutput(t, ctx, "vndkcorevariant.libraries.txt", nil)
+}
+
+func TestVndkLibrariesTxtAndroidMk(t *testing.T) {
+	config := android.TestArchConfig(buildDir, nil)
+	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
+	config.TestProductVariables.Platform_vndk_version = StringPtr("VER")
+	ctx := testCcWithConfig(t, `
+		vndk_libraries_txt {
+			name: "llndk.libraries.txt",
+		}`, config)
+
+	module := ctx.ModuleForTests("llndk.libraries.txt", "")
+	entries := android.AndroidMkEntriesForTest(t, config, "", module.Module())
+	assertArrayString(t, entries.EntryMap["LOCAL_MODULE_STEM"], []string{"llndk.libraries.VER.txt"})
 }
 
 func TestVndkUsingCoreVariant(t *testing.T) {
@@ -422,20 +461,17 @@
 			},
 			nocrt: true,
 		}
+
+		vndk_libraries_txt {
+			name: "vndkcorevariant.libraries.txt",
+		}
 	`, config)
 
-	checkVndkOutput(t, ctx, "vndk/vndkcore.libraries.txt", []string{"libvndk.so", "libvndk2.so"})
-	checkVndkOutput(t, ctx, "vndk/vndkcorevariant.libraries.txt", []string{
-		"libc++.so", "libvndk2.so", "libvndk_sp.so",
-	})
+	checkVndkLibrariesOutput(t, ctx, "vndkcorevariant.libraries.txt", []string{"libc++.so", "libvndk2.so", "libvndk_sp.so"})
 }
 
 func TestVndkWhenVndkVersionIsNotSet(t *testing.T) {
-	config := android.TestArchConfig(buildDir, nil)
-	config.TestProductVariables.DeviceVndkVersion = nil
-	config.TestProductVariables.Platform_vndk_version = nil
-
-	ctx := testCcWithConfig(t, `
+	ctx := testCcNoVndk(t, `
 		cc_library {
 			name: "libvndk",
 			vendor_available: true,
@@ -444,7 +480,7 @@
 			},
 			nocrt: true,
 		}
-	`, config)
+	`)
 
 	checkVndkOutput(t, ctx, "vndk/vndk.libraries.txt", []string{
 		"LLNDK: libc.so",
diff --git a/cc/cflag_artifacts.go b/cc/cflag_artifacts.go
index 9ed3876..b61f2a8 100644
--- a/cc/cflag_artifacts.go
+++ b/cc/cflag_artifacts.go
@@ -147,8 +147,8 @@
 	ctx.VisitAllModules(func(module android.Module) {
 		if ccModule, ok := module.(*Module); ok {
 			if allowedDir(ctx.ModuleDir(ccModule)) {
-				cflags := ccModule.flags.CFlags
-				cppflags := ccModule.flags.CppFlags
+				cflags := ccModule.flags.Local.CFlags
+				cppflags := ccModule.flags.Local.CppFlags
 				module := fmt.Sprintf("%s:%s (%s)",
 					ctx.BlueprintFile(ccModule),
 					ctx.ModuleName(ccModule),
diff --git a/cc/cmakelists.go b/cc/cmakelists.go
index be18ab9..97d21f4 100644
--- a/cc/cmakelists.go
+++ b/cc/cmakelists.go
@@ -162,25 +162,41 @@
 	f.WriteString(")\n")
 
 	// Add all header search path and compiler parameters (-D, -W, -f, -XXXX)
-	f.WriteString("\n# GLOBAL FLAGS:\n")
-	globalParameters := parseCompilerParameters(ccModule.flags.GlobalFlags, ctx, f)
-	translateToCMake(globalParameters, f, true, true)
+	f.WriteString("\n# GLOBAL ALL FLAGS:\n")
+	globalAllParameters := parseCompilerParameters(ccModule.flags.Global.CommonFlags, ctx, f)
+	translateToCMake(globalAllParameters, f, true, true)
 
-	f.WriteString("\n# CFLAGS:\n")
-	cParameters := parseCompilerParameters(ccModule.flags.CFlags, ctx, f)
-	translateToCMake(cParameters, f, true, true)
+	f.WriteString("\n# LOCAL ALL FLAGS:\n")
+	localAllParameters := parseCompilerParameters(ccModule.flags.Local.CommonFlags, ctx, f)
+	translateToCMake(localAllParameters, f, true, true)
 
-	f.WriteString("\n# C ONLY FLAGS:\n")
-	cOnlyParameters := parseCompilerParameters(ccModule.flags.ConlyFlags, ctx, f)
-	translateToCMake(cOnlyParameters, f, true, false)
+	f.WriteString("\n# GLOBAL CFLAGS:\n")
+	globalCParameters := parseCompilerParameters(ccModule.flags.Global.CFlags, ctx, f)
+	translateToCMake(globalCParameters, f, true, true)
 
-	f.WriteString("\n# CPP FLAGS:\n")
-	cppParameters := parseCompilerParameters(ccModule.flags.CppFlags, ctx, f)
-	translateToCMake(cppParameters, f, false, true)
+	f.WriteString("\n# LOCAL CFLAGS:\n")
+	localCParameters := parseCompilerParameters(ccModule.flags.Local.CFlags, ctx, f)
+	translateToCMake(localCParameters, f, true, true)
 
-	f.WriteString("\n# SYSTEM INCLUDE FLAGS:\n")
-	includeParameters := parseCompilerParameters(ccModule.flags.SystemIncludeFlags, ctx, f)
-	translateToCMake(includeParameters, f, true, true)
+	f.WriteString("\n# GLOBAL C ONLY FLAGS:\n")
+	globalConlyParameters := parseCompilerParameters(ccModule.flags.Global.ConlyFlags, ctx, f)
+	translateToCMake(globalConlyParameters, f, true, false)
+
+	f.WriteString("\n# LOCAL C ONLY FLAGS:\n")
+	localConlyParameters := parseCompilerParameters(ccModule.flags.Local.ConlyFlags, ctx, f)
+	translateToCMake(localConlyParameters, f, true, false)
+
+	f.WriteString("\n# GLOBAL CPP FLAGS:\n")
+	globalCppParameters := parseCompilerParameters(ccModule.flags.Global.CppFlags, ctx, f)
+	translateToCMake(globalCppParameters, f, false, true)
+
+	f.WriteString("\n# LOCAL CPP FLAGS:\n")
+	localCppParameters := parseCompilerParameters(ccModule.flags.Local.CppFlags, ctx, f)
+	translateToCMake(localCppParameters, f, false, true)
+
+	f.WriteString("\n# GLOBAL SYSTEM INCLUDE FLAGS:\n")
+	globalIncludeParameters := parseCompilerParameters(ccModule.flags.SystemIncludeFlags, ctx, f)
+	translateToCMake(globalIncludeParameters, f, true, true)
 
 	// Add project executable.
 	f.WriteString(fmt.Sprintf("\nadd_executable(%s ${SOURCE_FILES})\n",
diff --git a/cc/compdb.go b/cc/compdb.go
index ecc67b8..519380f 100644
--- a/cc/compdb.go
+++ b/cc/compdb.go
@@ -152,12 +152,16 @@
 		clangPath = ccPath
 	}
 	args = append(args, clangPath)
-	args = append(args, expandAllVars(ctx, ccModule.flags.GlobalFlags)...)
-	args = append(args, expandAllVars(ctx, ccModule.flags.CFlags)...)
+	args = append(args, expandAllVars(ctx, ccModule.flags.Global.CommonFlags)...)
+	args = append(args, expandAllVars(ctx, ccModule.flags.Local.CommonFlags)...)
+	args = append(args, expandAllVars(ctx, ccModule.flags.Global.CFlags)...)
+	args = append(args, expandAllVars(ctx, ccModule.flags.Local.CFlags)...)
 	if isCpp {
-		args = append(args, expandAllVars(ctx, ccModule.flags.CppFlags)...)
+		args = append(args, expandAllVars(ctx, ccModule.flags.Global.CppFlags)...)
+		args = append(args, expandAllVars(ctx, ccModule.flags.Local.CppFlags)...)
 	} else if !isAsm {
-		args = append(args, expandAllVars(ctx, ccModule.flags.ConlyFlags)...)
+		args = append(args, expandAllVars(ctx, ccModule.flags.Global.ConlyFlags)...)
+		args = append(args, expandAllVars(ctx, ccModule.flags.Local.ConlyFlags)...)
 	}
 	args = append(args, expandAllVars(ctx, ccModule.flags.SystemIncludeFlags)...)
 	args = append(args, src.String())
diff --git a/cc/compiler.go b/cc/compiler.go
index ff68101..bb40a5b 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -265,11 +265,11 @@
 
 	esc := proptools.NinjaAndShellEscapeList
 
-	flags.CFlags = append(flags.CFlags, esc(compiler.Properties.Cflags)...)
-	flags.CppFlags = append(flags.CppFlags, esc(compiler.Properties.Cppflags)...)
-	flags.ConlyFlags = append(flags.ConlyFlags, esc(compiler.Properties.Conlyflags)...)
-	flags.AsFlags = append(flags.AsFlags, esc(compiler.Properties.Asflags)...)
-	flags.YasmFlags = append(flags.YasmFlags, esc(compiler.Properties.Asflags)...)
+	flags.Local.CFlags = append(flags.Local.CFlags, esc(compiler.Properties.Cflags)...)
+	flags.Local.CppFlags = append(flags.Local.CppFlags, esc(compiler.Properties.Cppflags)...)
+	flags.Local.ConlyFlags = append(flags.Local.ConlyFlags, esc(compiler.Properties.Conlyflags)...)
+	flags.Local.AsFlags = append(flags.Local.AsFlags, esc(compiler.Properties.Asflags)...)
+	flags.Local.YasmFlags = append(flags.Local.YasmFlags, esc(compiler.Properties.Asflags)...)
 
 	flags.Yacc = compiler.Properties.Yacc
 
@@ -277,20 +277,20 @@
 	localIncludeDirs := android.PathsForModuleSrc(ctx, compiler.Properties.Local_include_dirs)
 	if len(localIncludeDirs) > 0 {
 		f := includeDirsToFlags(localIncludeDirs)
-		flags.GlobalFlags = append(flags.GlobalFlags, f)
-		flags.YasmFlags = append(flags.YasmFlags, f)
+		flags.Local.CommonFlags = append(flags.Local.CommonFlags, f)
+		flags.Local.YasmFlags = append(flags.Local.YasmFlags, f)
 	}
 	rootIncludeDirs := android.PathsForSource(ctx, compiler.Properties.Include_dirs)
 	if len(rootIncludeDirs) > 0 {
 		f := includeDirsToFlags(rootIncludeDirs)
-		flags.GlobalFlags = append(flags.GlobalFlags, f)
-		flags.YasmFlags = append(flags.YasmFlags, f)
+		flags.Local.CommonFlags = append(flags.Local.CommonFlags, f)
+		flags.Local.YasmFlags = append(flags.Local.YasmFlags, f)
 	}
 
 	if compiler.Properties.Include_build_directory == nil ||
 		*compiler.Properties.Include_build_directory {
-		flags.GlobalFlags = append(flags.GlobalFlags, "-I"+android.PathForModuleSrc(ctx).String())
-		flags.YasmFlags = append(flags.YasmFlags, "-I"+android.PathForModuleSrc(ctx).String())
+		flags.Local.CommonFlags = append(flags.Local.CommonFlags, "-I"+android.PathForModuleSrc(ctx).String())
+		flags.Local.YasmFlags = append(flags.Local.YasmFlags, "-I"+android.PathForModuleSrc(ctx).String())
 	}
 
 	if !(ctx.useSdk() || ctx.useVndk()) || ctx.Host() {
@@ -312,16 +312,17 @@
 	}
 
 	if ctx.useVndk() {
-		flags.GlobalFlags = append(flags.GlobalFlags, "-D__ANDROID_VNDK__")
+		flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_VNDK__")
 	}
 
 	if ctx.inRecovery() {
-		flags.GlobalFlags = append(flags.GlobalFlags, "-D__ANDROID_RECOVERY__")
+		flags.Global.CommonFlags = append(flags.Global.CommonFlags, "-D__ANDROID_RECOVERY__")
 	}
 
 	if ctx.apexName() != "" {
-		flags.GlobalFlags = append(flags.GlobalFlags, "-D__ANDROID_APEX__")
-		flags.GlobalFlags = append(flags.GlobalFlags, "-D__ANDROID_APEX_"+makeDefineString(ctx.apexName())+"__")
+		flags.Global.CommonFlags = append(flags.Global.CommonFlags,
+			"-D__ANDROID_APEX__",
+			"-D__ANDROID_APEX_"+makeDefineString(ctx.apexName())+"__")
 	}
 
 	instructionSet := String(compiler.Properties.Instruction_set)
@@ -336,17 +337,17 @@
 	CheckBadCompilerFlags(ctx, "release.cflags", compiler.Properties.Release.Cflags)
 
 	// TODO: debug
-	flags.CFlags = append(flags.CFlags, esc(compiler.Properties.Release.Cflags)...)
+	flags.Local.CFlags = append(flags.Local.CFlags, esc(compiler.Properties.Release.Cflags)...)
 
 	CheckBadCompilerFlags(ctx, "clang_cflags", compiler.Properties.Clang_cflags)
 	CheckBadCompilerFlags(ctx, "clang_asflags", compiler.Properties.Clang_asflags)
 
-	flags.CFlags = config.ClangFilterUnknownCflags(flags.CFlags)
-	flags.CFlags = append(flags.CFlags, esc(compiler.Properties.Clang_cflags)...)
-	flags.AsFlags = append(flags.AsFlags, esc(compiler.Properties.Clang_asflags)...)
-	flags.CppFlags = config.ClangFilterUnknownCflags(flags.CppFlags)
-	flags.ConlyFlags = config.ClangFilterUnknownCflags(flags.ConlyFlags)
-	flags.LdFlags = config.ClangFilterUnknownCflags(flags.LdFlags)
+	flags.Local.CFlags = config.ClangFilterUnknownCflags(flags.Local.CFlags)
+	flags.Local.CFlags = append(flags.Local.CFlags, esc(compiler.Properties.Clang_cflags)...)
+	flags.Local.AsFlags = append(flags.Local.AsFlags, esc(compiler.Properties.Clang_asflags)...)
+	flags.Local.CppFlags = config.ClangFilterUnknownCflags(flags.Local.CppFlags)
+	flags.Local.ConlyFlags = config.ClangFilterUnknownCflags(flags.Local.ConlyFlags)
+	flags.Local.LdFlags = config.ClangFilterUnknownCflags(flags.Local.LdFlags)
 
 	target := "-target " + tc.ClangTriple()
 	if ctx.Os().Class == android.Device {
@@ -360,45 +361,45 @@
 
 	gccPrefix := "-B" + config.ToolPath(tc)
 
-	flags.CFlags = append(flags.CFlags, target, gccPrefix)
-	flags.AsFlags = append(flags.AsFlags, target, gccPrefix)
-	flags.LdFlags = append(flags.LdFlags, target, gccPrefix)
+	flags.Global.CFlags = append(flags.Global.CFlags, target, gccPrefix)
+	flags.Global.AsFlags = append(flags.Global.AsFlags, target, gccPrefix)
+	flags.Global.LdFlags = append(flags.Global.LdFlags, target, gccPrefix)
 
 	hod := "Host"
 	if ctx.Os().Class == android.Device {
 		hod = "Device"
 	}
 
-	flags.GlobalFlags = append(flags.GlobalFlags, instructionSetFlags)
-	flags.ConlyFlags = append([]string{"${config.CommonGlobalConlyflags}"}, flags.ConlyFlags...)
-	flags.CppFlags = append([]string{fmt.Sprintf("${config.%sGlobalCppflags}", hod)}, flags.CppFlags...)
+	flags.Global.CommonFlags = append(flags.Global.CommonFlags, instructionSetFlags)
+	flags.Global.ConlyFlags = append([]string{"${config.CommonGlobalConlyflags}"}, flags.Global.ConlyFlags...)
+	flags.Global.CppFlags = append([]string{fmt.Sprintf("${config.%sGlobalCppflags}", hod)}, flags.Global.CppFlags...)
 
-	flags.AsFlags = append(flags.AsFlags, tc.ClangAsflags())
-	flags.CppFlags = append([]string{"${config.CommonClangGlobalCppflags}"}, flags.CppFlags...)
-	flags.GlobalFlags = append(flags.GlobalFlags,
+	flags.Global.AsFlags = append(flags.Global.AsFlags, tc.ClangAsflags())
+	flags.Global.CppFlags = append([]string{"${config.CommonClangGlobalCppflags}"}, flags.Global.CppFlags...)
+	flags.Global.CommonFlags = append(flags.Global.CommonFlags,
 		tc.ClangCflags(),
 		"${config.CommonClangGlobalCflags}",
 		fmt.Sprintf("${config.%sClangGlobalCflags}", hod))
 
 	if strings.HasPrefix(android.PathForModuleSrc(ctx).String(), "external/") {
-		flags.GlobalFlags = append([]string{"${config.ClangExternalCflags}"}, flags.GlobalFlags...)
+		flags.Global.CommonFlags = append([]string{"${config.ClangExternalCflags}"}, flags.Global.CommonFlags...)
 	}
 
 	if tc.Bionic() {
 		if Bool(compiler.Properties.Rtti) {
-			flags.CppFlags = append(flags.CppFlags, "-frtti")
+			flags.Local.CppFlags = append(flags.Local.CppFlags, "-frtti")
 		} else {
-			flags.CppFlags = append(flags.CppFlags, "-fno-rtti")
+			flags.Local.CppFlags = append(flags.Local.CppFlags, "-fno-rtti")
 		}
 	}
 
-	flags.AsFlags = append(flags.AsFlags, "-D__ASSEMBLY__")
+	flags.Global.AsFlags = append(flags.Global.AsFlags, "-D__ASSEMBLY__")
 
-	flags.CppFlags = append(flags.CppFlags, tc.ClangCppflags())
+	flags.Global.CppFlags = append(flags.Global.CppFlags, tc.ClangCppflags())
 
-	flags.YasmFlags = append(flags.YasmFlags, tc.YasmFlags())
+	flags.Global.YasmFlags = append(flags.Global.YasmFlags, tc.YasmFlags())
 
-	flags.GlobalFlags = append(flags.GlobalFlags, tc.ToolchainClangCflags())
+	flags.Global.CommonFlags = append(flags.Global.CommonFlags, tc.ToolchainClangCflags())
 
 	cStd := config.CStdVersion
 	if String(compiler.Properties.C_std) == "experimental" {
@@ -420,15 +421,15 @@
 		cppStd = gnuToCReplacer.Replace(cppStd)
 	}
 
-	flags.ConlyFlags = append([]string{"-std=" + cStd}, flags.ConlyFlags...)
-	flags.CppFlags = append([]string{"-std=" + cppStd}, flags.CppFlags...)
+	flags.Local.ConlyFlags = append([]string{"-std=" + cStd}, flags.Local.ConlyFlags...)
+	flags.Local.CppFlags = append([]string{"-std=" + cppStd}, flags.Local.CppFlags...)
 
 	if ctx.useVndk() {
-		flags.CFlags = append(flags.CFlags, esc(compiler.Properties.Target.Vendor.Cflags)...)
+		flags.Local.CFlags = append(flags.Local.CFlags, esc(compiler.Properties.Target.Vendor.Cflags)...)
 	}
 
 	if ctx.inRecovery() {
-		flags.CFlags = append(flags.CFlags, esc(compiler.Properties.Target.Recovery.Cflags)...)
+		flags.Local.CFlags = append(flags.Local.CFlags, esc(compiler.Properties.Target.Recovery.Cflags)...)
 	}
 
 	// We can enforce some rules more strictly in the code we own. strict
@@ -444,7 +445,7 @@
 	// Can be used to make some annotations stricter for code we can fix
 	// (such as when we mark functions as deprecated).
 	if strict {
-		flags.CFlags = append(flags.CFlags, "-DANDROID_STRICT")
+		flags.Global.CFlags = append(flags.Global.CFlags, "-DANDROID_STRICT")
 	}
 
 	if compiler.hasSrcExt(".proto") {
@@ -452,12 +453,12 @@
 	}
 
 	if compiler.hasSrcExt(".y") || compiler.hasSrcExt(".yy") {
-		flags.GlobalFlags = append(flags.GlobalFlags,
+		flags.Local.CommonFlags = append(flags.Local.CommonFlags,
 			"-I"+android.PathForModuleGen(ctx, "yacc", ctx.ModuleDir()).String())
 	}
 
 	if compiler.hasSrcExt(".mc") {
-		flags.GlobalFlags = append(flags.GlobalFlags,
+		flags.Local.CommonFlags = append(flags.Local.CommonFlags,
 			"-I"+android.PathForModuleGen(ctx, "windmc", ctx.ModuleDir()).String())
 	}
 
@@ -475,7 +476,7 @@
 			flags.aidlFlags = append(flags.aidlFlags, "-t")
 		}
 
-		flags.GlobalFlags = append(flags.GlobalFlags,
+		flags.Local.CommonFlags = append(flags.Local.CommonFlags,
 			"-I"+android.PathForModuleGen(ctx, "aidl").String())
 	}
 
@@ -484,26 +485,26 @@
 	}
 
 	if compiler.hasSrcExt(".sysprop") {
-		flags.GlobalFlags = append(flags.GlobalFlags,
+		flags.Local.CommonFlags = append(flags.Local.CommonFlags,
 			"-I"+android.PathForModuleGen(ctx, "sysprop", "include").String())
 	}
 
 	if len(compiler.Properties.Srcs) > 0 {
 		module := ctx.ModuleDir() + "/Android.bp:" + ctx.ModuleName()
-		if inList("-Wno-error", flags.CFlags) || inList("-Wno-error", flags.CppFlags) {
+		if inList("-Wno-error", flags.Local.CFlags) || inList("-Wno-error", flags.Local.CppFlags) {
 			addToModuleList(ctx, modulesUsingWnoErrorKey, module)
-		} else if !inList("-Werror", flags.CFlags) && !inList("-Werror", flags.CppFlags) {
+		} else if !inList("-Werror", flags.Local.CFlags) && !inList("-Werror", flags.Local.CppFlags) {
 			if warningsAreAllowed(ctx.ModuleDir()) {
 				addToModuleList(ctx, modulesAddedWallKey, module)
-				flags.CFlags = append([]string{"-Wall"}, flags.CFlags...)
+				flags.Local.CFlags = append([]string{"-Wall"}, flags.Local.CFlags...)
 			} else {
-				flags.CFlags = append([]string{"-Wall", "-Werror"}, flags.CFlags...)
+				flags.Local.CFlags = append([]string{"-Wall", "-Werror"}, flags.Local.CFlags...)
 			}
 		}
 	}
 
 	if Bool(compiler.Properties.Openmp) {
-		flags.CFlags = append(flags.CFlags, "-fopenmp")
+		flags.Local.CFlags = append(flags.Local.CFlags, "-fopenmp")
 	}
 
 	return flags
diff --git a/cc/config/clang.go b/cc/config/clang.go
index 71bea42..1b6744e 100644
--- a/cc/config/clang.go
+++ b/cc/config/clang.go
@@ -139,9 +139,7 @@
 
 	pctx.StaticVariable("ClangExtraCppflags", strings.Join([]string{
 		// Enable clang's thread-safety annotations in libcxx.
-		// Turn off -Wthread-safety-negative, to avoid breaking projects that use -Weverything.
 		"-D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS",
-		"-Wno-thread-safety-negative",
 
 		// libc++'s math.h has an #include_next outside of system_headers.
 		"-Wno-gnu-include-next",
diff --git a/cc/coverage.go b/cc/coverage.go
index 2e81a9e..c03a568 100644
--- a/cc/coverage.go
+++ b/cc/coverage.go
@@ -69,12 +69,12 @@
 
 	if cov.Properties.CoverageEnabled {
 		flags.Coverage = true
-		flags.GlobalFlags = append(flags.GlobalFlags, "--coverage", "-O0")
+		flags.Local.CommonFlags = append(flags.Local.CommonFlags, "--coverage", "-O0")
 		cov.linkCoverage = true
 
 		// Override -Wframe-larger-than and non-default optimization
 		// flags that the module may use.
-		flags.CFlags = append(flags.CFlags, "-Wno-frame-larger-than=", "-O0")
+		flags.Local.CFlags = append(flags.Local.CFlags, "-Wno-frame-larger-than=", "-O0")
 	}
 
 	// Even if we don't have coverage enabled, if any of our object files were compiled
@@ -112,12 +112,12 @@
 	}
 
 	if cov.linkCoverage {
-		flags.LdFlags = append(flags.LdFlags, "--coverage")
+		flags.Local.LdFlags = append(flags.Local.LdFlags, "--coverage")
 
 		coverage := ctx.GetDirectDepWithTag(getProfileLibraryName(ctx), coverageDepTag).(*Module)
 		deps.WholeStaticLibs = append(deps.WholeStaticLibs, coverage.OutputFile().Path())
 
-		flags.LdFlags = append(flags.LdFlags, "-Wl,--wrap,getenv")
+		flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--wrap,getenv")
 	}
 
 	return flags, deps
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 577fa70..1f06fb0 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -17,10 +17,9 @@
 import (
 	"encoding/json"
 	"path/filepath"
+	"sort"
 	"strings"
 
-	"github.com/google/blueprint/proptools"
-
 	"android/soong/android"
 	"android/soong/cc/config"
 )
@@ -82,6 +81,7 @@
 	corpus                android.Paths
 	corpusIntermediateDir android.Path
 	config                android.Path
+	installedSharedDeps   []string
 }
 
 func (fuzz *fuzzBinary) linkerProps() []interface{} {
@@ -91,21 +91,6 @@
 }
 
 func (fuzz *fuzzBinary) linkerInit(ctx BaseModuleContext) {
-	// Add ../lib[64] to rpath so that out/host/linux-x86/fuzz/<fuzzer> can
-	// find out/host/linux-x86/lib[64]/library.so
-	runpaths := []string{"../lib"}
-	for _, runpath := range runpaths {
-		if ctx.toolchain().Is64Bit() {
-			runpath += "64"
-		}
-		fuzz.binaryDecorator.baseLinker.dynamicProperties.RunPaths = append(
-			fuzz.binaryDecorator.baseLinker.dynamicProperties.RunPaths, runpath)
-	}
-
-	// add "" to rpath so that fuzzer binaries can find libraries in their own fuzz directory
-	fuzz.binaryDecorator.baseLinker.dynamicProperties.RunPaths = append(
-		fuzz.binaryDecorator.baseLinker.dynamicProperties.RunPaths, "")
-
 	fuzz.binaryDecorator.linkerInit(ctx)
 }
 
@@ -118,9 +103,80 @@
 
 func (fuzz *fuzzBinary) linkerFlags(ctx ModuleContext, flags Flags) Flags {
 	flags = fuzz.binaryDecorator.linkerFlags(ctx, flags)
+	// RunPaths on devices isn't instantiated by the base linker.
+	flags.Local.LdFlags = append(flags.Local.LdFlags, `-Wl,-rpath,\$$ORIGIN/../lib`)
 	return flags
 }
 
+// This function performs a breadth-first search over the provided module's
+// dependencies using `visitDirectDeps` to enumerate all shared library
+// dependencies. We require breadth-first expansion, as otherwise we may
+// incorrectly use the core libraries (sanitizer runtimes, libc, libdl, etc.)
+// from a dependency. This may cause issues when dependencies have explicit
+// sanitizer tags, as we may get a dependency on an unsanitized libc, etc.
+func collectAllSharedDependencies(
+	module android.Module,
+	sharedDeps map[string]android.Path,
+	ctx android.SingletonContext) {
+	var fringe []android.Module
+
+	// Enumerate the first level of dependencies, as we discard all non-library
+	// modules in the BFS loop below.
+	ctx.VisitDirectDeps(module, func(dep android.Module) {
+		fringe = append(fringe, dep)
+	})
+
+	for i := 0; i < len(fringe); i++ {
+		module := fringe[i]
+		if !isValidSharedDependency(module, sharedDeps) {
+			continue
+		}
+
+		ccModule := module.(*Module)
+		sharedDeps[ccModule.Name()] = ccModule.UnstrippedOutputFile()
+		ctx.VisitDirectDeps(module, func(dep android.Module) {
+			fringe = append(fringe, dep)
+		})
+	}
+}
+
+// This function takes a module and determines if it is a unique shared library
+// that should be installed in the fuzz target output directories. This function
+// returns true, unless:
+//  - The module already exists in `sharedDeps`, or
+//  - The module is not a shared library, or
+//  - The module is a header, stub, or vendor-linked library.
+func isValidSharedDependency(
+	dependency android.Module,
+	sharedDeps map[string]android.Path) bool {
+	// TODO(b/144090547): We should be parsing these modules using
+	// ModuleDependencyTag instead of the current brute-force checking.
+
+	if linkable, ok := dependency.(LinkableInterface); !ok || // Discard non-linkables.
+		!linkable.CcLibraryInterface() || !linkable.Shared() || // Discard static libs.
+		linkable.UseVndk() || // Discard vendor linked libraries.
+		!linkable.CcLibrary() || linkable.BuildStubs() { // Discard stubs libs (only CCLibrary variants).
+		return false
+	}
+
+	// If this library has already been traversed, we don't need to do any more work.
+	if _, exists := sharedDeps[dependency.Name()]; exists {
+		return false
+	}
+	return true
+}
+
+func sharedLibraryInstallLocation(
+	libraryPath android.Path, isHost bool, archString string) string {
+	installLocation := "$(PRODUCT_OUT)/data"
+	if isHost {
+		installLocation = "$(HOST_OUT)"
+	}
+	installLocation = filepath.Join(
+		installLocation, "fuzz", archString, "lib", libraryPath.Base())
+	return installLocation
+}
+
 func (fuzz *fuzzBinary) install(ctx ModuleContext, file android.Path) {
 	fuzz.binaryDecorator.baseInstaller.dir = filepath.Join(
 		"fuzz", ctx.Target().Arch.ArchType.String(), ctx.ModuleName())
@@ -160,6 +216,22 @@
 		})
 		fuzz.config = configPath
 	}
+
+	// Grab the list of required shared libraries.
+	sharedLibraries := make(map[string]android.Path)
+	ctx.WalkDeps(func(child, parent android.Module) bool {
+		if isValidSharedDependency(child, sharedLibraries) {
+			sharedLibraries[child.Name()] = child.(*Module).UnstrippedOutputFile()
+			return true
+		}
+		return false
+	})
+
+	for _, lib := range sharedLibraries {
+		fuzz.installedSharedDeps = append(fuzz.installedSharedDeps,
+			sharedLibraryInstallLocation(
+				lib, ctx.Host(), ctx.Arch().ArchType.String()))
+	}
 }
 
 func NewFuzz(hod android.HostOrDeviceSupported) *Module {
@@ -193,28 +265,15 @@
 		ctx.AppendProperties(&disableDarwinAndLinuxBionic)
 	})
 
-	// Statically link the STL. This allows fuzz target deployment to not have to
-	// include the STL.
-	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
-		staticStlLinkage := struct {
-			Target struct {
-				Linux_glibc struct {
-					Stl *string
-				}
-			}
-		}{}
-
-		staticStlLinkage.Target.Linux_glibc.Stl = proptools.StringPtr("libc++_static")
-		ctx.AppendProperties(&staticStlLinkage)
-	})
-
 	return module
 }
 
 // Responsible for generating GNU Make rules that package fuzz targets into
 // their architecture & target/host specific zip file.
 type fuzzPackager struct {
-	packages android.Paths
+	packages                android.Paths
+	sharedLibInstallStrings []string
+	fuzzTargets             map[string]bool
 }
 
 func fuzzPackagingFactory() android.Singleton {
@@ -226,18 +285,31 @@
 	DestinationPathPrefix string
 }
 
+type archAndLibraryKey struct {
+	ArchDir android.OutputPath
+	Library android.Path
+}
+
 func (s *fuzzPackager) GenerateBuildActions(ctx android.SingletonContext) {
 	// Map between each architecture + host/device combination, and the files that
 	// need to be packaged (in the tuple of {source file, destination folder in
 	// archive}).
 	archDirs := make(map[android.OutputPath][]fileToZip)
 
+	// List of shared library dependencies for each architecture + host/device combo.
+	archSharedLibraryDeps := make(map[archAndLibraryKey]bool)
+
+	// List of individual fuzz targets, so that 'make fuzz' also installs the targets
+	// to the correct output directories as well.
+	s.fuzzTargets = make(map[string]bool)
+
 	ctx.VisitAllModules(func(module android.Module) {
 		// Discard non-fuzz targets.
 		ccModule, ok := module.(*Module)
 		if !ok {
 			return
 		}
+
 		fuzzModule, ok := ccModule.compiler.(*fuzzBinary)
 		if !ok {
 			return
@@ -249,6 +321,8 @@
 			return
 		}
 
+		s.fuzzTargets[module.Name()] = true
+
 		hostOrTargetString := "target"
 		if ccModule.Host() {
 			hostOrTargetString = "host"
@@ -257,6 +331,29 @@
 		archString := ccModule.Arch().ArchType.String()
 		archDir := android.PathForIntermediates(ctx, "fuzz", hostOrTargetString, archString)
 
+		// Grab the list of required shared libraries.
+		sharedLibraries := make(map[string]android.Path)
+		collectAllSharedDependencies(module, sharedLibraries, ctx)
+
+		for _, library := range sharedLibraries {
+			if _, exists := archSharedLibraryDeps[archAndLibraryKey{archDir, library}]; exists {
+				continue
+			}
+
+			// For each architecture-specific shared library dependency, we need to
+			// install it to the output directory. Setup the install destination here,
+			// which will be used by $(copy-many-files) in the Make backend.
+			archSharedLibraryDeps[archAndLibraryKey{archDir, library}] = true
+			installDestination := sharedLibraryInstallLocation(
+				library, ccModule.Host(), archString)
+			// Escape all the variables, as the install destination here will be called
+			// via. $(eval) in Make.
+			installDestination = strings.ReplaceAll(
+				installDestination, "$", "$$")
+			s.sharedLibInstallStrings = append(s.sharedLibInstallStrings,
+				library.String()+":"+installDestination)
+		}
+
 		// The executable.
 		archDirs[archDir] = append(archDirs[archDir],
 			fileToZip{ccModule.UnstrippedOutputFile(), ccModule.Name()})
@@ -280,6 +377,12 @@
 		}
 	})
 
+	// Add the shared library deps for packaging.
+	for key, _ := range archSharedLibraryDeps {
+		archDirs[key.ArchDir] = append(archDirs[key.ArchDir],
+			fileToZip{key.Library, "lib"})
+	}
+
 	for archDir, filesToZip := range archDirs {
 		arch := archDir.Base()
 		hostOrTarget := filepath.Base(filepath.Dir(archDir.String()))
@@ -302,9 +405,22 @@
 }
 
 func (s *fuzzPackager) MakeVars(ctx android.MakeVarsContext) {
+	packages := s.packages.Strings()
+	sort.Strings(packages)
+	sort.Strings(s.sharedLibInstallStrings)
 	// TODO(mitchp): Migrate this to use MakeVarsContext::DistForGoal() when it's
 	// ready to handle phony targets created in Soong. In the meantime, this
 	// exports the phony 'fuzz' target and dependencies on packages to
 	// core/main.mk so that we can use dist-for-goals.
-	ctx.Strict("SOONG_FUZZ_PACKAGING_ARCH_MODULES", strings.Join(s.packages.Strings(), " "))
+	ctx.Strict("SOONG_FUZZ_PACKAGING_ARCH_MODULES", strings.Join(packages, " "))
+	ctx.Strict("FUZZ_TARGET_SHARED_DEPS_INSTALL_PAIRS",
+		strings.Join(s.sharedLibInstallStrings, " "))
+
+	// Preallocate the slice of fuzz targets to minimise memory allocations.
+	fuzzTargets := make([]string, 0, len(s.fuzzTargets))
+	for target, _ := range s.fuzzTargets {
+		fuzzTargets = append(fuzzTargets, target)
+	}
+	sort.Strings(fuzzTargets)
+	ctx.Strict("ALL_FUZZ_TARGETS", strings.Join(fuzzTargets, " "))
 }
diff --git a/cc/gen_test.go b/cc/gen_test.go
index da3b4e8..ceecf1c 100644
--- a/cc/gen_test.go
+++ b/cc/gen_test.go
@@ -34,7 +34,7 @@
 		aidl := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_core_shared").Rule("aidl")
 		libfoo := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_core_shared").Module().(*Module)
 
-		if !inList("-I"+filepath.Dir(aidl.Output.String()), libfoo.flags.GlobalFlags) {
+		if !inList("-I"+filepath.Dir(aidl.Output.String()), libfoo.flags.Local.CommonFlags) {
 			t.Errorf("missing aidl includes in global flags")
 		}
 	})
@@ -58,7 +58,7 @@
 		aidl := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_core_shared").Rule("aidl")
 		libfoo := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_core_shared").Module().(*Module)
 
-		if !inList("-I"+filepath.Dir(aidl.Output.String()), libfoo.flags.GlobalFlags) {
+		if !inList("-I"+filepath.Dir(aidl.Output.String()), libfoo.flags.Local.CommonFlags) {
 			t.Errorf("missing aidl includes in global flags")
 		}
 
diff --git a/cc/library.go b/cc/library.go
index 8d90cd8..dde067c 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -394,13 +394,13 @@
 	// all code is position independent, and then those warnings get promoted to
 	// errors.
 	if !ctx.Windows() {
-		flags.CFlags = append(flags.CFlags, "-fPIC")
+		flags.Global.CFlags = append(flags.Global.CFlags, "-fPIC")
 	}
 
 	if library.static() {
-		flags.CFlags = append(flags.CFlags, library.StaticProperties.Static.Cflags...)
+		flags.Local.CFlags = append(flags.Local.CFlags, library.StaticProperties.Static.Cflags...)
 	} else if library.shared() {
-		flags.CFlags = append(flags.CFlags, library.SharedProperties.Shared.Cflags...)
+		flags.Local.CFlags = append(flags.Local.CFlags, library.SharedProperties.Shared.Cflags...)
 	}
 
 	if library.shared() {
@@ -431,7 +431,7 @@
 			}
 		}
 
-		flags.LdFlags = append(f, flags.LdFlags...)
+		flags.Global.LdFlags = append(flags.Global.LdFlags, f...)
 	}
 
 	return flags
@@ -441,8 +441,8 @@
 	exportIncludeDirs := library.flagExporter.exportedIncludes(ctx)
 	if len(exportIncludeDirs) > 0 {
 		f := includeDirsToFlags(exportIncludeDirs)
-		flags.GlobalFlags = append(flags.GlobalFlags, f)
-		flags.YasmFlags = append(flags.YasmFlags, f)
+		flags.Local.CommonFlags = append(flags.Local.CommonFlags, f)
+		flags.Local.YasmFlags = append(flags.Local.YasmFlags, f)
 	}
 
 	flags = library.baseCompiler.compilerFlags(ctx, flags, deps)
@@ -462,8 +462,8 @@
 			}
 			return ret
 		}
-		flags.GlobalFlags = removeInclude(flags.GlobalFlags)
-		flags.CFlags = removeInclude(flags.CFlags)
+		flags.Local.CommonFlags = removeInclude(flags.Local.CommonFlags)
+		flags.Local.CFlags = removeInclude(flags.Local.CFlags)
 
 		flags = addStubLibraryCompilerFlags(flags)
 	}
@@ -776,21 +776,21 @@
 		}
 	} else {
 		if unexportedSymbols.Valid() {
-			flags.LdFlags = append(flags.LdFlags, "-Wl,-unexported_symbols_list,"+unexportedSymbols.String())
+			flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-unexported_symbols_list,"+unexportedSymbols.String())
 			linkerDeps = append(linkerDeps, unexportedSymbols.Path())
 		}
 		if forceNotWeakSymbols.Valid() {
-			flags.LdFlags = append(flags.LdFlags, "-Wl,-force_symbols_not_weak_list,"+forceNotWeakSymbols.String())
+			flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-force_symbols_not_weak_list,"+forceNotWeakSymbols.String())
 			linkerDeps = append(linkerDeps, forceNotWeakSymbols.Path())
 		}
 		if forceWeakSymbols.Valid() {
-			flags.LdFlags = append(flags.LdFlags, "-Wl,-force_symbols_weak_list,"+forceWeakSymbols.String())
+			flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-force_symbols_weak_list,"+forceWeakSymbols.String())
 			linkerDeps = append(linkerDeps, forceWeakSymbols.Path())
 		}
 	}
 	if library.buildStubs() {
 		linkerScriptFlags := "-Wl,--version-script," + library.versionScriptPath.String()
-		flags.LdFlags = append(flags.LdFlags, linkerScriptFlags)
+		flags.Local.LdFlags = append(flags.Local.LdFlags, linkerScriptFlags)
 		linkerDeps = append(linkerDeps, library.versionScriptPath)
 	}
 
@@ -802,7 +802,7 @@
 	if ctx.Windows() {
 		importLibraryPath := android.PathForModuleOut(ctx, pathtools.ReplaceExtension(fileName, "lib"))
 
-		flags.LdFlags = append(flags.LdFlags, "-Wl,--out-implib="+importLibraryPath.String())
+		flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--out-implib="+importLibraryPath.String())
 		implicitOutputs = append(implicitOutputs, importLibraryPath)
 	}
 
@@ -862,7 +862,7 @@
 
 		symbolOrderingFile := android.PathForModuleOut(ctx, "unsorted", fileName+".symbol_order")
 		symbolOrderingFlag := library.baseLinker.sortBssSymbolsBySize(ctx, unsortedOutputFile, symbolOrderingFile, builderFlags)
-		builderFlags.ldFlags += " " + symbolOrderingFlag
+		builderFlags.localLdFlags += " " + symbolOrderingFlag
 		linkerDeps = append(linkerDeps, symbolOrderingFile)
 	}
 
diff --git a/cc/library_test.go b/cc/library_test.go
index 2acae35..f8d8934 100644
--- a/cc/library_test.go
+++ b/cc/library_test.go
@@ -181,7 +181,7 @@
 		}
 
 		libfoo := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_core_shared").Module().(*Module)
-		if !inList("-DGOOGLE_PROTOBUF_NO_RTTI", libfoo.flags.CFlags) {
+		if !inList("-DGOOGLE_PROTOBUF_NO_RTTI", libfoo.flags.Local.CFlags) {
 			t.Errorf("missing protobuf cflags")
 		}
 	})
diff --git a/cc/linker.go b/cc/linker.go
index e5e1486..61ae757 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -331,65 +331,66 @@
 	}
 
 	if linker.useClangLld(ctx) {
-		flags.LdFlags = append(flags.LdFlags, fmt.Sprintf("${config.%sGlobalLldflags}", hod))
+		flags.Global.LdFlags = append(flags.Global.LdFlags, fmt.Sprintf("${config.%sGlobalLldflags}", hod))
 		if !BoolDefault(linker.Properties.Pack_relocations, true) {
-			flags.LdFlags = append(flags.LdFlags, "-Wl,--pack-dyn-relocs=none")
+			flags.Global.LdFlags = append(flags.Global.LdFlags, "-Wl,--pack-dyn-relocs=none")
 		} else if ctx.Device() {
 			// The SHT_RELR relocations is only supported by API level >= 28.
 			// Do not turn this on if older version NDK is used.
 			if !ctx.useSdk() || CheckSdkVersionAtLeast(ctx, 28) {
-				flags.LdFlags = append(flags.LdFlags, "-Wl,--pack-dyn-relocs=android+relr")
-				flags.LdFlags = append(flags.LdFlags, "-Wl,--use-android-relr-tags")
+				flags.Global.LdFlags = append(flags.Global.LdFlags,
+					"-Wl,--pack-dyn-relocs=android+relr",
+					"-Wl,--use-android-relr-tags")
 			}
 		}
 	} else {
-		flags.LdFlags = append(flags.LdFlags, fmt.Sprintf("${config.%sGlobalLdflags}", hod))
+		flags.Global.LdFlags = append(flags.Global.LdFlags, fmt.Sprintf("${config.%sGlobalLdflags}", hod))
 	}
 	if Bool(linker.Properties.Allow_undefined_symbols) {
 		if ctx.Darwin() {
 			// darwin defaults to treating undefined symbols as errors
-			flags.LdFlags = append(flags.LdFlags, "-Wl,-undefined,dynamic_lookup")
+			flags.Global.LdFlags = append(flags.Global.LdFlags, "-Wl,-undefined,dynamic_lookup")
 		}
 	} else if !ctx.Darwin() && !ctx.Windows() {
-		flags.LdFlags = append(flags.LdFlags, "-Wl,--no-undefined")
+		flags.Global.LdFlags = append(flags.Global.LdFlags, "-Wl,--no-undefined")
 	}
 
 	if linker.useClangLld(ctx) {
-		flags.LdFlags = append(flags.LdFlags, toolchain.ClangLldflags())
+		flags.Global.LdFlags = append(flags.Global.LdFlags, toolchain.ClangLldflags())
 	} else {
-		flags.LdFlags = append(flags.LdFlags, toolchain.ClangLdflags())
+		flags.Global.LdFlags = append(flags.Global.LdFlags, toolchain.ClangLdflags())
 	}
 
 	if !ctx.toolchain().Bionic() && !ctx.Fuchsia() {
 		CheckBadHostLdlibs(ctx, "host_ldlibs", linker.Properties.Host_ldlibs)
 
-		flags.LdFlags = append(flags.LdFlags, linker.Properties.Host_ldlibs...)
+		flags.Local.LdFlags = append(flags.Local.LdFlags, linker.Properties.Host_ldlibs...)
 
 		if !ctx.Windows() {
 			// Add -ldl, -lpthread, -lm and -lrt to host builds to match the default behavior of device
 			// builds
-			flags.LdFlags = append(flags.LdFlags,
+			flags.Global.LdFlags = append(flags.Global.LdFlags,
 				"-ldl",
 				"-lpthread",
 				"-lm",
 			)
 			if !ctx.Darwin() {
-				flags.LdFlags = append(flags.LdFlags, "-lrt")
+				flags.Global.LdFlags = append(flags.Global.LdFlags, "-lrt")
 			}
 		}
 	}
 
 	if ctx.Fuchsia() {
-		flags.LdFlags = append(flags.LdFlags, "-lfdio", "-lzircon")
+		flags.Global.LdFlags = append(flags.Global.LdFlags, "-lfdio", "-lzircon")
 	}
 
 	if ctx.toolchain().LibclangRuntimeLibraryArch() != "" {
-		flags.LdFlags = append(flags.LdFlags, "-Wl,--exclude-libs="+config.BuiltinsRuntimeLibrary(ctx.toolchain())+".a")
+		flags.Global.LdFlags = append(flags.Global.LdFlags, "-Wl,--exclude-libs="+config.BuiltinsRuntimeLibrary(ctx.toolchain())+".a")
 	}
 
 	CheckBadLinkerFlags(ctx, "ldflags", linker.Properties.Ldflags)
 
-	flags.LdFlags = append(flags.LdFlags, proptools.NinjaAndShellEscapeList(linker.Properties.Ldflags)...)
+	flags.Local.LdFlags = append(flags.Local.LdFlags, proptools.NinjaAndShellEscapeList(linker.Properties.Ldflags)...)
 
 	if ctx.Host() && !ctx.Windows() {
 		rpath_prefix := `\$$ORIGIN/`
@@ -399,7 +400,7 @@
 
 		if !ctx.static() {
 			for _, rpath := range linker.dynamicProperties.RunPaths {
-				flags.LdFlags = append(flags.LdFlags, "-Wl,-rpath,"+rpath_prefix+rpath)
+				flags.Global.LdFlags = append(flags.Global.LdFlags, "-Wl,-rpath,"+rpath_prefix+rpath)
 			}
 		}
 	}
@@ -409,10 +410,10 @@
 		// to older devices requires the old style hash. Fortunately, we can build with both and
 		// it'll work anywhere.
 		// This is not currently supported on MIPS architectures.
-		flags.LdFlags = append(flags.LdFlags, "-Wl,--hash-style=both")
+		flags.Global.LdFlags = append(flags.Global.LdFlags, "-Wl,--hash-style=both")
 	}
 
-	flags.LdFlags = append(flags.LdFlags, toolchain.ToolchainClangLdflags())
+	flags.Global.LdFlags = append(flags.Global.LdFlags, toolchain.ToolchainClangLdflags())
 
 	if Bool(linker.Properties.Group_static_libs) {
 		flags.GroupStaticLibs = true
@@ -434,13 +435,13 @@
 			if ctx.Darwin() {
 				ctx.PropertyErrorf("version_script", "Not supported on Darwin")
 			} else {
-				flags.LdFlags = append(flags.LdFlags,
+				flags.Local.LdFlags = append(flags.Local.LdFlags,
 					"-Wl,--version-script,"+versionScript.String())
 				flags.LdFlagsDeps = append(flags.LdFlagsDeps, versionScript.Path())
 
 				if linker.sanitize.isSanitizerEnabled(cfi) {
 					cfiExportsMap := android.PathForSource(ctx, cfiExportsMapPath)
-					flags.LdFlags = append(flags.LdFlags,
+					flags.Local.LdFlags = append(flags.Local.LdFlags,
 						"-Wl,--version-script,"+cfiExportsMap.String())
 					flags.LdFlagsDeps = append(flags.LdFlagsDeps, cfiExportsMap)
 				}
diff --git a/cc/llndk_library.go b/cc/llndk_library.go
index 16e089e..3a5b3a6 100644
--- a/cc/llndk_library.go
+++ b/cc/llndk_library.go
@@ -133,7 +133,7 @@
 
 	if !Bool(stub.Properties.Unversioned) {
 		linkerScriptFlag := "-Wl,--version-script," + stub.versionScriptPath.String()
-		flags.LdFlags = append(flags.LdFlags, linkerScriptFlag)
+		flags.Local.LdFlags = append(flags.Local.LdFlags, linkerScriptFlag)
 		flags.LdFlagsDeps = append(flags.LdFlagsDeps, stub.versionScriptPath)
 	}
 
diff --git a/cc/lto.go b/cc/lto.go
index 580bb09..4489fc7 100644
--- a/cc/lto.go
+++ b/cc/lto.go
@@ -82,7 +82,7 @@
 func (lto *lto) flags(ctx BaseModuleContext, flags Flags) Flags {
 	// TODO(b/131771163): Disable LTO when using explicit fuzzing configurations.
 	// LTO breaks fuzzer builds.
-	if inList("-fsanitize=fuzzer-no-link", flags.CFlags) {
+	if inList("-fsanitize=fuzzer-no-link", flags.Local.CFlags) {
 		return flags
 	}
 
@@ -94,27 +94,28 @@
 			ltoFlag = "-flto"
 		}
 
-		flags.CFlags = append(flags.CFlags, ltoFlag)
-		flags.LdFlags = append(flags.LdFlags, ltoFlag)
+		flags.Local.CFlags = append(flags.Local.CFlags, ltoFlag)
+		flags.Local.LdFlags = append(flags.Local.LdFlags, ltoFlag)
 
 		if ctx.Config().IsEnvTrue("USE_THINLTO_CACHE") && Bool(lto.Properties.Lto.Thin) && lto.useClangLld(ctx) {
 			// Set appropriate ThinLTO cache policy
 			cacheDirFormat := "-Wl,--thinlto-cache-dir="
 			cacheDir := android.PathForOutput(ctx, "thinlto-cache").String()
-			flags.LdFlags = append(flags.LdFlags, cacheDirFormat+cacheDir)
+			flags.Local.LdFlags = append(flags.Local.LdFlags, cacheDirFormat+cacheDir)
 
 			// Limit the size of the ThinLTO cache to the lesser of 10% of available
 			// disk space and 10GB.
 			cachePolicyFormat := "-Wl,--thinlto-cache-policy="
 			policy := "cache_size=10%:cache_size_bytes=10g"
-			flags.LdFlags = append(flags.LdFlags, cachePolicyFormat+policy)
+			flags.Local.LdFlags = append(flags.Local.LdFlags, cachePolicyFormat+policy)
 		}
 
 		// If the module does not have a profile, be conservative and do not inline
 		// or unroll loops during LTO, in order to prevent significant size bloat.
 		if !ctx.isPgoCompile() {
-			flags.LdFlags = append(flags.LdFlags, "-Wl,-plugin-opt,-inline-threshold=0")
-			flags.LdFlags = append(flags.LdFlags, "-Wl,-plugin-opt,-unroll-threshold=0")
+			flags.Local.LdFlags = append(flags.Local.LdFlags,
+				"-Wl,-plugin-opt,-inline-threshold=0",
+				"-Wl,-plugin-opt,-unroll-threshold=0")
 		}
 	}
 	return flags
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index 3747b41..b75c4c8 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -257,7 +257,7 @@
 }
 
 func addStubLibraryCompilerFlags(flags Flags) Flags {
-	flags.CFlags = append(flags.CFlags,
+	flags.Global.CFlags = append(flags.Global.CFlags,
 		// We're knowingly doing some otherwise unsightly things with builtin
 		// functions here. We're just generating stub libraries, so ignore it.
 		"-Wno-incompatible-library-redeclaration",
@@ -337,7 +337,7 @@
 
 	if useVersionScript {
 		linkerScriptFlag := "-Wl,--version-script," + stub.versionScriptPath.String()
-		flags.LdFlags = append(flags.LdFlags, linkerScriptFlag)
+		flags.Local.LdFlags = append(flags.Local.LdFlags, linkerScriptFlag)
 		flags.LdFlagsDeps = append(flags.LdFlagsDeps, stub.versionScriptPath)
 	}
 
diff --git a/cc/object.go b/cc/object.go
index 31729a5..ad31d09 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -87,10 +87,10 @@
 }
 
 func (object *objectLinker) linkerFlags(ctx ModuleContext, flags Flags) Flags {
-	flags.LdFlags = append(flags.LdFlags, ctx.toolchain().ToolchainClangLdflags())
+	flags.Global.LdFlags = append(flags.Global.LdFlags, ctx.toolchain().ToolchainClangLdflags())
 
 	if lds := android.OptionalPathForModuleSrc(ctx, object.Properties.Linker_script); lds.Valid() {
-		flags.LdFlags = append(flags.LdFlags, "-Wl,-T,"+lds.String())
+		flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-T,"+lds.String())
 		flags.LdFlagsDeps = append(flags.LdFlagsDeps, lds.Path())
 	}
 	return flags
diff --git a/cc/pgo.go b/cc/pgo.go
index 4e915ff..4618f4e 100644
--- a/cc/pgo.go
+++ b/cc/pgo.go
@@ -89,18 +89,18 @@
 }
 
 func (props *PgoProperties) addProfileGatherFlags(ctx ModuleContext, flags Flags) Flags {
-	flags.CFlags = append(flags.CFlags, props.Pgo.Cflags...)
+	flags.Local.CFlags = append(flags.Local.CFlags, props.Pgo.Cflags...)
 
 	if props.isInstrumentation() {
-		flags.CFlags = append(flags.CFlags, profileInstrumentFlag)
+		flags.Local.CFlags = append(flags.Local.CFlags, profileInstrumentFlag)
 		// The profile runtime is added below in deps().  Add the below
 		// flag, which is the only other link-time action performed by
 		// the Clang driver during link.
-		flags.LdFlags = append(flags.LdFlags, "-u__llvm_profile_runtime")
+		flags.Local.LdFlags = append(flags.Local.LdFlags, "-u__llvm_profile_runtime")
 	}
 	if props.isSampling() {
-		flags.CFlags = append(flags.CFlags, profileSamplingFlag)
-		flags.LdFlags = append(flags.LdFlags, profileSamplingFlag)
+		flags.Local.CFlags = append(flags.Local.CFlags, profileSamplingFlag)
+		flags.Local.LdFlags = append(flags.Local.LdFlags, profileSamplingFlag)
 	}
 	return flags
 }
@@ -170,8 +170,8 @@
 		profileFilePath := profileFile.Path()
 		profileUseFlags := props.profileUseFlags(ctx, profileFilePath.String())
 
-		flags.CFlags = append(flags.CFlags, profileUseFlags...)
-		flags.LdFlags = append(flags.LdFlags, profileUseFlags...)
+		flags.Local.CFlags = append(flags.Local.CFlags, profileUseFlags...)
+		flags.Local.LdFlags = append(flags.Local.LdFlags, profileUseFlags...)
 
 		// Update CFlagsDeps and LdFlagsDeps so the module is rebuilt
 		// if profileFile gets updated
diff --git a/cc/proto.go b/cc/proto.go
index f818edc..ae988ec 100644
--- a/cc/proto.go
+++ b/cc/proto.go
@@ -114,13 +114,13 @@
 }
 
 func protoFlags(ctx ModuleContext, flags Flags, p *android.ProtoProperties) Flags {
-	flags.CFlags = append(flags.CFlags, "-DGOOGLE_PROTOBUF_NO_RTTI")
+	flags.Local.CFlags = append(flags.Local.CFlags, "-DGOOGLE_PROTOBUF_NO_RTTI")
 
 	flags.proto = android.GetProtoFlags(ctx, p)
 	if flags.proto.CanonicalPathFromRoot {
-		flags.GlobalFlags = append(flags.GlobalFlags, "-I"+flags.proto.SubDir.String())
+		flags.Local.CommonFlags = append(flags.Local.CommonFlags, "-I"+flags.proto.SubDir.String())
 	}
-	flags.GlobalFlags = append(flags.GlobalFlags, "-I"+flags.proto.Dir.String())
+	flags.Local.CommonFlags = append(flags.Local.CommonFlags, "-I"+flags.proto.Dir.String())
 
 	if String(p.Proto.Plugin) == "" {
 		var plugin string
diff --git a/cc/rs.go b/cc/rs.go
index 5951edb..61fd1a8 100644
--- a/cc/rs.go
+++ b/cc/rs.go
@@ -123,7 +123,7 @@
 	rootRsIncludeDirs := android.PathsForSource(ctx, properties.Renderscript.Include_dirs)
 	flags.rsFlags = append(flags.rsFlags, includeDirsToFlags(rootRsIncludeDirs))
 
-	flags.GlobalFlags = append(flags.GlobalFlags,
+	flags.Local.CommonFlags = append(flags.Local.CommonFlags,
 		"-I"+android.PathForModuleGen(ctx, "rs").String(),
 		"-Iframeworks/rs",
 		"-Iframeworks/rs/cpp",
diff --git a/cc/sabi.go b/cc/sabi.go
index 4760313..8cef170 100644
--- a/cc/sabi.go
+++ b/cc/sabi.go
@@ -70,8 +70,10 @@
 func (sabimod *sabi) flags(ctx ModuleContext, flags Flags) Flags {
 	// Assuming that the cflags which clang LibTooling tools cannot
 	// understand have not been converted to ninja variables yet.
-	flags.ToolingCFlags = filterOutWithPrefix(flags.CFlags, config.ClangLibToolingUnknownCflags)
-	flags.ToolingCppFlags = filterOutWithPrefix(flags.CppFlags, config.ClangLibToolingUnknownCflags)
+	flags.Local.ToolingCFlags = filterOutWithPrefix(flags.Local.CFlags, config.ClangLibToolingUnknownCflags)
+	flags.Global.ToolingCFlags = filterOutWithPrefix(flags.Global.CFlags, config.ClangLibToolingUnknownCflags)
+	flags.Local.ToolingCppFlags = filterOutWithPrefix(flags.Local.CppFlags, config.ClangLibToolingUnknownCflags)
+	flags.Global.ToolingCppFlags = filterOutWithPrefix(flags.Global.CppFlags, config.ClangLibToolingUnknownCflags)
 
 	return flags
 }
diff --git a/cc/sanitize.go b/cc/sanitize.go
index a3b4e8e..2bf051e 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -426,8 +426,9 @@
 	minimalRuntimePath := "${config.ClangAsanLibDir}/" + minimalRuntimeLib
 
 	if ctx.Device() && sanitize.Properties.MinimalRuntimeDep {
-		flags.LdFlags = append(flags.LdFlags, minimalRuntimePath)
-		flags.LdFlags = append(flags.LdFlags, "-Wl,--exclude-libs,"+minimalRuntimeLib)
+		flags.Local.LdFlags = append(flags.Local.LdFlags,
+			minimalRuntimePath,
+			"-Wl,--exclude-libs,"+minimalRuntimeLib)
 	}
 	if !sanitize.Properties.SanitizerEnabled && !sanitize.Properties.UbsanRuntimeDep {
 		return flags
@@ -439,15 +440,15 @@
 			// TODO: put in flags?
 			flags.RequiredInstructionSet = "arm"
 		}
-		flags.CFlags = append(flags.CFlags, asanCflags...)
-		flags.LdFlags = append(flags.LdFlags, asanLdflags...)
+		flags.Local.CFlags = append(flags.Local.CFlags, asanCflags...)
+		flags.Local.LdFlags = append(flags.Local.LdFlags, asanLdflags...)
 
 		if ctx.Host() {
 			// -nodefaultlibs (provided with libc++) prevents the driver from linking
 			// libraries needed with -fsanitize=address. http://b/18650275 (WAI)
-			flags.LdFlags = append(flags.LdFlags, "-Wl,--no-as-needed")
+			flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--no-as-needed")
 		} else {
-			flags.CFlags = append(flags.CFlags, "-mllvm", "-asan-globals=0")
+			flags.Local.CFlags = append(flags.Local.CFlags, "-mllvm", "-asan-globals=0")
 			if ctx.bootstrap() {
 				flags.DynamicLinker = "/system/bin/bootstrap/linker_asan"
 			} else {
@@ -460,33 +461,30 @@
 	}
 
 	if Bool(sanitize.Properties.Sanitize.Hwaddress) {
-		flags.CFlags = append(flags.CFlags, hwasanCflags...)
+		flags.Local.CFlags = append(flags.Local.CFlags, hwasanCflags...)
 	}
 
 	if Bool(sanitize.Properties.Sanitize.Fuzzer) {
-		flags.CFlags = append(flags.CFlags, "-fsanitize=fuzzer-no-link")
+		flags.Local.CFlags = append(flags.Local.CFlags, "-fsanitize=fuzzer-no-link")
 
 		// TODO(b/131771163): LTO and Fuzzer support is mutually incompatible.
-		_, flags.LdFlags = removeFromList("-flto", flags.LdFlags)
-		_, flags.CFlags = removeFromList("-flto", flags.CFlags)
-		flags.LdFlags = append(flags.LdFlags, "-fno-lto")
-		flags.CFlags = append(flags.CFlags, "-fno-lto")
+		_, flags.Local.LdFlags = removeFromList("-flto", flags.Local.LdFlags)
+		_, flags.Local.CFlags = removeFromList("-flto", flags.Local.CFlags)
+		flags.Local.LdFlags = append(flags.Local.LdFlags, "-fno-lto")
+		flags.Local.CFlags = append(flags.Local.CFlags, "-fno-lto")
 
 		// TODO(b/142430592): Upstream linker scripts for sanitizer runtime libraries
 		// discard the sancov_lowest_stack symbol, because it's emulated TLS (and thus
 		// doesn't match the linker script due to the "__emutls_v." prefix).
-		flags.LdFlags = append(flags.LdFlags, "-fno-sanitize-coverage=stack-depth")
-		flags.CFlags = append(flags.CFlags, "-fno-sanitize-coverage=stack-depth")
+		flags.Local.LdFlags = append(flags.Local.LdFlags, "-fno-sanitize-coverage=stack-depth")
+		flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize-coverage=stack-depth")
 
 		// TODO(b/133876586): Experimental PM breaks sanitizer coverage.
-		_, flags.CFlags = removeFromList("-fexperimental-new-pass-manager", flags.CFlags)
-		flags.CFlags = append(flags.CFlags, "-fno-experimental-new-pass-manager")
+		flags.Local.CFlags = append(flags.Local.CFlags, "-fno-experimental-new-pass-manager")
 
 		// Disable fortify for fuzzing builds. Generally, we'll be building with
 		// UBSan or ASan here and the fortify checks pollute the stack traces.
-		_, flags.CFlags = removeFromList("-D_FORTIFY_SOURCE=1", flags.CFlags)
-		_, flags.CFlags = removeFromList("-D_FORTIFY_SOURCE=2", flags.CFlags)
-		flags.CFlags = append(flags.CFlags, "-U_FORTIFY_SOURCE")
+		flags.Local.CFlags = append(flags.Local.CFlags, "-U_FORTIFY_SOURCE")
 	}
 
 	if Bool(sanitize.Properties.Sanitize.Cfi) {
@@ -496,75 +494,75 @@
 			flags.RequiredInstructionSet = "thumb"
 		}
 
-		flags.CFlags = append(flags.CFlags, cfiCflags...)
-		flags.AsFlags = append(flags.AsFlags, cfiAsflags...)
+		flags.Local.CFlags = append(flags.Local.CFlags, cfiCflags...)
+		flags.Local.AsFlags = append(flags.Local.AsFlags, cfiAsflags...)
 		// Only append the default visibility flag if -fvisibility has not already been set
 		// to hidden.
-		if !inList("-fvisibility=hidden", flags.CFlags) {
-			flags.CFlags = append(flags.CFlags, "-fvisibility=default")
+		if !inList("-fvisibility=hidden", flags.Local.CFlags) {
+			flags.Local.CFlags = append(flags.Local.CFlags, "-fvisibility=default")
 		}
-		flags.LdFlags = append(flags.LdFlags, cfiLdflags...)
+		flags.Local.LdFlags = append(flags.Local.LdFlags, cfiLdflags...)
 
 		if ctx.staticBinary() {
-			_, flags.CFlags = removeFromList("-fsanitize-cfi-cross-dso", flags.CFlags)
-			_, flags.LdFlags = removeFromList("-fsanitize-cfi-cross-dso", flags.LdFlags)
+			_, flags.Local.CFlags = removeFromList("-fsanitize-cfi-cross-dso", flags.Local.CFlags)
+			_, flags.Local.LdFlags = removeFromList("-fsanitize-cfi-cross-dso", flags.Local.LdFlags)
 		}
 	}
 
 	if Bool(sanitize.Properties.Sanitize.Integer_overflow) {
-		flags.CFlags = append(flags.CFlags, intOverflowCflags...)
+		flags.Local.CFlags = append(flags.Local.CFlags, intOverflowCflags...)
 	}
 
 	if len(sanitize.Properties.Sanitizers) > 0 {
 		sanitizeArg := "-fsanitize=" + strings.Join(sanitize.Properties.Sanitizers, ",")
 
-		flags.CFlags = append(flags.CFlags, sanitizeArg)
-		flags.AsFlags = append(flags.AsFlags, sanitizeArg)
+		flags.Local.CFlags = append(flags.Local.CFlags, sanitizeArg)
+		flags.Local.AsFlags = append(flags.Local.AsFlags, sanitizeArg)
 		if ctx.Host() {
 			// Host sanitizers only link symbols in the final executable, so
 			// there will always be undefined symbols in intermediate libraries.
-			_, flags.LdFlags = removeFromList("-Wl,--no-undefined", flags.LdFlags)
-			flags.LdFlags = append(flags.LdFlags, sanitizeArg)
+			_, flags.Global.LdFlags = removeFromList("-Wl,--no-undefined", flags.Global.LdFlags)
+			flags.Local.LdFlags = append(flags.Local.LdFlags, sanitizeArg)
 		} else {
 			if enableMinimalRuntime(sanitize) {
-				flags.CFlags = append(flags.CFlags, strings.Join(minimalRuntimeFlags, " "))
+				flags.Local.CFlags = append(flags.Local.CFlags, strings.Join(minimalRuntimeFlags, " "))
 				flags.libFlags = append([]string{minimalRuntimePath}, flags.libFlags...)
-				flags.LdFlags = append(flags.LdFlags, "-Wl,--exclude-libs,"+minimalRuntimeLib)
+				flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--exclude-libs,"+minimalRuntimeLib)
 			}
 		}
 
 		if Bool(sanitize.Properties.Sanitize.Fuzzer) {
 			// When fuzzing, we wish to crash with diagnostics on any bug.
-			flags.CFlags = append(flags.CFlags, "-fno-sanitize-trap=all", "-fno-sanitize-recover=all")
+			flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize-trap=all", "-fno-sanitize-recover=all")
 		} else if ctx.Host() {
-			flags.CFlags = append(flags.CFlags, "-fno-sanitize-recover=all")
+			flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize-recover=all")
 		} else {
-			flags.CFlags = append(flags.CFlags, "-fsanitize-trap=all", "-ftrap-function=abort")
+			flags.Local.CFlags = append(flags.Local.CFlags, "-fsanitize-trap=all", "-ftrap-function=abort")
 		}
 		// http://b/119329758, Android core does not boot up with this sanitizer yet.
-		if toDisableImplicitIntegerChange(flags.CFlags) {
-			flags.CFlags = append(flags.CFlags, "-fno-sanitize=implicit-integer-sign-change")
+		if toDisableImplicitIntegerChange(flags.Local.CFlags) {
+			flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize=implicit-integer-sign-change")
 		}
 	}
 
 	if len(sanitize.Properties.DiagSanitizers) > 0 {
-		flags.CFlags = append(flags.CFlags, "-fno-sanitize-trap="+strings.Join(sanitize.Properties.DiagSanitizers, ","))
+		flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize-trap="+strings.Join(sanitize.Properties.DiagSanitizers, ","))
 	}
 	// FIXME: enable RTTI if diag + (cfi or vptr)
 
 	if sanitize.Properties.Sanitize.Recover != nil {
-		flags.CFlags = append(flags.CFlags, "-fsanitize-recover="+
+		flags.Local.CFlags = append(flags.Local.CFlags, "-fsanitize-recover="+
 			strings.Join(sanitize.Properties.Sanitize.Recover, ","))
 	}
 
 	if sanitize.Properties.Sanitize.Diag.No_recover != nil {
-		flags.CFlags = append(flags.CFlags, "-fno-sanitize-recover="+
+		flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize-recover="+
 			strings.Join(sanitize.Properties.Sanitize.Diag.No_recover, ","))
 	}
 
 	blacklist := android.OptionalPathForModuleSrc(ctx, sanitize.Properties.Sanitize.Blacklist)
 	if blacklist.Valid() {
-		flags.CFlags = append(flags.CFlags, "-fsanitize-blacklist="+blacklist.String())
+		flags.Local.CFlags = append(flags.Local.CFlags, "-fsanitize-blacklist="+blacklist.String())
 		flags.CFlagsDeps = append(flags.CFlagsDeps, blacklist.Path())
 	}
 
diff --git a/cc/stl.go b/cc/stl.go
index 101519b..5ccd44a 100644
--- a/cc/stl.go
+++ b/cc/stl.go
@@ -215,12 +215,12 @@
 			// these availability attributes are meaningless for us but cause
 			// build breaks when we try to use code that would not be available
 			// in the system's dylib.
-			flags.CppFlags = append(flags.CppFlags,
+			flags.Local.CppFlags = append(flags.Local.CppFlags,
 				"-D_LIBCPP_DISABLE_AVAILABILITY")
 		}
 
 		if !ctx.toolchain().Bionic() {
-			flags.CppFlags = append(flags.CppFlags, "-nostdinc++")
+			flags.Local.CppFlags = append(flags.Local.CppFlags, "-nostdinc++")
 			flags.extraLibFlags = append(flags.extraLibFlags, "-nostdlib++")
 			if ctx.Windows() {
 				if stl.Properties.SelectedStl == "libc++_static" {
@@ -231,9 +231,9 @@
 				// Use SjLj exceptions for 32-bit.  libgcc_eh implements SjLj
 				// exception model for 32-bit.
 				if ctx.Arch().ArchType == android.X86 {
-					flags.CppFlags = append(flags.CppFlags, "-fsjlj-exceptions")
+					flags.Local.CppFlags = append(flags.Local.CppFlags, "-fsjlj-exceptions")
 				}
-				flags.CppFlags = append(flags.CppFlags,
+				flags.Local.CppFlags = append(flags.Local.CppFlags,
 					// Disable visiblity annotations since we're using static
 					// libc++.
 					"-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS",
@@ -243,23 +243,23 @@
 			}
 		} else {
 			if ctx.Arch().ArchType == android.Arm {
-				flags.LdFlags = append(flags.LdFlags, "-Wl,--exclude-libs,libunwind_llvm.a")
+				flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--exclude-libs,libunwind_llvm.a")
 			}
 		}
 	case "libstdc++":
 		// Nothing
 	case "ndk_system":
 		ndkSrcRoot := android.PathForSource(ctx, "prebuilts/ndk/current/sources/cxx-stl/system/include")
-		flags.CFlags = append(flags.CFlags, "-isystem "+ndkSrcRoot.String())
+		flags.Local.CFlags = append(flags.Local.CFlags, "-isystem "+ndkSrcRoot.String())
 	case "ndk_libc++_shared", "ndk_libc++_static":
 		if ctx.Arch().ArchType == android.Arm {
 			// Make sure the _Unwind_XXX symbols are not re-exported.
-			flags.LdFlags = append(flags.LdFlags, "-Wl,--exclude-libs,libunwind.a")
+			flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--exclude-libs,libunwind.a")
 		}
 	case "":
 		// None or error.
 		if !ctx.toolchain().Bionic() {
-			flags.CppFlags = append(flags.CppFlags, "-nostdinc++")
+			flags.Local.CppFlags = append(flags.Local.CppFlags, "-nostdinc++")
 			flags.extraLibFlags = append(flags.extraLibFlags, "-nostdlib++")
 		}
 	default:
diff --git a/cc/test.go b/cc/test.go
index 5c49d6e..05e6fe5 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -220,20 +220,20 @@
 		return flags
 	}
 
-	flags.CFlags = append(flags.CFlags, "-DGTEST_HAS_STD_STRING")
+	flags.Local.CFlags = append(flags.Local.CFlags, "-DGTEST_HAS_STD_STRING")
 	if ctx.Host() {
-		flags.CFlags = append(flags.CFlags, "-O0", "-g")
+		flags.Local.CFlags = append(flags.Local.CFlags, "-O0", "-g")
 
 		switch ctx.Os() {
 		case android.Windows:
-			flags.CFlags = append(flags.CFlags, "-DGTEST_OS_WINDOWS")
+			flags.Local.CFlags = append(flags.Local.CFlags, "-DGTEST_OS_WINDOWS")
 		case android.Linux:
-			flags.CFlags = append(flags.CFlags, "-DGTEST_OS_LINUX")
+			flags.Local.CFlags = append(flags.Local.CFlags, "-DGTEST_OS_LINUX")
 		case android.Darwin:
-			flags.CFlags = append(flags.CFlags, "-DGTEST_OS_MAC")
+			flags.Local.CFlags = append(flags.Local.CFlags, "-DGTEST_OS_MAC")
 		}
 	} else {
-		flags.CFlags = append(flags.CFlags, "-DGTEST_OS_LINUX_ANDROID")
+		flags.Local.CFlags = append(flags.Local.CFlags, "-DGTEST_OS_LINUX_ANDROID")
 	}
 
 	return flags
diff --git a/cc/testing.go b/cc/testing.go
index fafaeb0..9ad72d9 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -269,6 +269,7 @@
 	ctx.RegisterModuleType("cc_object", android.ModuleFactoryAdaptor(ObjectFactory))
 	ctx.RegisterModuleType("filegroup", android.ModuleFactoryAdaptor(android.FileGroupFactory))
 	ctx.RegisterModuleType("vndk_prebuilt_shared", android.ModuleFactoryAdaptor(VndkPrebuiltSharedFactory))
+	ctx.RegisterModuleType("vndk_libraries_txt", android.ModuleFactoryAdaptor(VndkLibrariesTxtFactory))
 	ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
 		ctx.BottomUp("image", ImageMutator).Parallel()
 		ctx.BottomUp("link", LinkageMutator).Parallel()
diff --git a/cc/util.go b/cc/util.go
index 2f7bec2..60070bb 100644
--- a/cc/util.go
+++ b/cc/util.go
@@ -55,27 +55,37 @@
 
 func flagsToBuilderFlags(in Flags) builderFlags {
 	return builderFlags{
-		globalFlags:     strings.Join(in.GlobalFlags, " "),
-		arFlags:         strings.Join(in.ArFlags, " "),
-		asFlags:         strings.Join(in.AsFlags, " "),
-		cFlags:          strings.Join(in.CFlags, " "),
-		toolingCFlags:   strings.Join(in.ToolingCFlags, " "),
-		toolingCppFlags: strings.Join(in.ToolingCppFlags, " "),
-		conlyFlags:      strings.Join(in.ConlyFlags, " "),
-		cppFlags:        strings.Join(in.CppFlags, " "),
-		aidlFlags:       strings.Join(in.aidlFlags, " "),
-		rsFlags:         strings.Join(in.rsFlags, " "),
-		ldFlags:         strings.Join(in.LdFlags, " "),
-		libFlags:        strings.Join(in.libFlags, " "),
-		extraLibFlags:   strings.Join(in.extraLibFlags, " "),
-		tidyFlags:       strings.Join(in.TidyFlags, " "),
-		sAbiFlags:       strings.Join(in.SAbiFlags, " "),
-		yasmFlags:       strings.Join(in.YasmFlags, " "),
-		toolchain:       in.Toolchain,
-		coverage:        in.Coverage,
-		tidy:            in.Tidy,
-		sAbiDump:        in.SAbiDump,
-		emitXrefs:       in.EmitXrefs,
+		globalCommonFlags:     strings.Join(in.Global.CommonFlags, " "),
+		globalAsFlags:         strings.Join(in.Global.AsFlags, " "),
+		globalYasmFlags:       strings.Join(in.Global.YasmFlags, " "),
+		globalCFlags:          strings.Join(in.Global.CFlags, " "),
+		globalToolingCFlags:   strings.Join(in.Global.ToolingCFlags, " "),
+		globalToolingCppFlags: strings.Join(in.Global.ToolingCppFlags, " "),
+		globalConlyFlags:      strings.Join(in.Global.ConlyFlags, " "),
+		globalCppFlags:        strings.Join(in.Global.CppFlags, " "),
+		globalLdFlags:         strings.Join(in.Global.LdFlags, " "),
+
+		localCommonFlags:     strings.Join(in.Local.CommonFlags, " "),
+		localAsFlags:         strings.Join(in.Local.AsFlags, " "),
+		localYasmFlags:       strings.Join(in.Local.YasmFlags, " "),
+		localCFlags:          strings.Join(in.Local.CFlags, " "),
+		localToolingCFlags:   strings.Join(in.Local.ToolingCFlags, " "),
+		localToolingCppFlags: strings.Join(in.Local.ToolingCppFlags, " "),
+		localConlyFlags:      strings.Join(in.Local.ConlyFlags, " "),
+		localCppFlags:        strings.Join(in.Local.CppFlags, " "),
+		localLdFlags:         strings.Join(in.Local.LdFlags, " "),
+
+		aidlFlags:     strings.Join(in.aidlFlags, " "),
+		rsFlags:       strings.Join(in.rsFlags, " "),
+		libFlags:      strings.Join(in.libFlags, " "),
+		extraLibFlags: strings.Join(in.extraLibFlags, " "),
+		tidyFlags:     strings.Join(in.TidyFlags, " "),
+		sAbiFlags:     strings.Join(in.SAbiFlags, " "),
+		toolchain:     in.Toolchain,
+		coverage:      in.Coverage,
+		tidy:          in.Tidy,
+		sAbiDump:      in.SAbiDump,
+		emitXrefs:     in.EmitXrefs,
 
 		systemIncludeFlags: strings.Join(in.SystemIncludeFlags, " "),
 
diff --git a/cc/vendor_public_library.go b/cc/vendor_public_library.go
index f0de267..e9d1c73 100644
--- a/cc/vendor_public_library.go
+++ b/cc/vendor_public_library.go
@@ -124,7 +124,7 @@
 	objs Objects) android.Path {
 	if !Bool(stub.Properties.Unversioned) {
 		linkerScriptFlag := "-Wl,--version-script," + stub.versionScriptPath.String()
-		flags.LdFlags = append(flags.LdFlags, linkerScriptFlag)
+		flags.Local.LdFlags = append(flags.Local.LdFlags, linkerScriptFlag)
 		flags.LdFlagsDeps = append(flags.LdFlagsDeps, stub.versionScriptPath)
 	}
 	return stub.libraryDecorator.link(ctx, flags, deps, objs)
diff --git a/cc/vndk.go b/cc/vndk.go
index bb4aafc..f25861a 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -27,6 +27,34 @@
 	"android/soong/cc/config"
 )
 
+const (
+	llndkLibrariesTxt                = "llndk.libraries.txt"
+	vndkCoreLibrariesTxt             = "vndkcore.libraries.txt"
+	vndkSpLibrariesTxt               = "vndksp.libraries.txt"
+	vndkPrivateLibrariesTxt          = "vndkprivate.libraries.txt"
+	vndkUsingCoreVariantLibrariesTxt = "vndkcorevariant.libraries.txt"
+)
+
+func VndkLibrariesTxtModules(vndkVersion string) []string {
+	if vndkVersion == "current" {
+		return []string{
+			llndkLibrariesTxt,
+			vndkCoreLibrariesTxt,
+			vndkSpLibrariesTxt,
+			vndkPrivateLibrariesTxt,
+			vndkUsingCoreVariantLibrariesTxt,
+		}
+	}
+	// Snapshot vndks have their own *.libraries.VER.txt files.
+	// Note that snapshots don't have "vndkcorevariant.libraries.VER.txt"
+	return []string{
+		insertVndkVersion(llndkLibrariesTxt, vndkVersion),
+		insertVndkVersion(vndkCoreLibrariesTxt, vndkVersion),
+		insertVndkVersion(vndkSpLibrariesTxt, vndkVersion),
+		insertVndkVersion(vndkPrivateLibrariesTxt, vndkVersion),
+	}
+}
+
 type VndkProperties struct {
 	Vndk struct {
 		// declared as a VNDK or VNDK-SP module. The vendor variant
@@ -360,22 +388,109 @@
 }
 
 func init() {
+	android.RegisterModuleType("vndk_libraries_txt", VndkLibrariesTxtFactory)
 	android.RegisterSingletonType("vndk-snapshot", VndkSnapshotSingleton)
 }
 
+type vndkLibrariesTxt struct {
+	android.ModuleBase
+	outputFile android.OutputPath
+}
+
+var _ android.PrebuiltEtcModule = &vndkLibrariesTxt{}
+var _ android.OutputFileProducer = &vndkLibrariesTxt{}
+
+// vndk_libraries_txt is a special kind of module type in that it name is one of
+// - llndk.libraries.txt
+// - vndkcore.libraries.txt
+// - vndksp.libraries.txt
+// - vndkprivate.libraries.txt
+// - vndkcorevariant.libraries.txt
+// A module behaves like a prebuilt_etc but its content is generated by soong.
+// By being a soong module, these files can be referenced by other soong modules.
+// For example, apex_vndk can depend on these files as prebuilt.
+func VndkLibrariesTxtFactory() android.Module {
+	m := &vndkLibrariesTxt{}
+	android.InitAndroidModule(m)
+	return m
+}
+
+func insertVndkVersion(filename string, vndkVersion string) string {
+	if index := strings.LastIndex(filename, "."); index != -1 {
+		return filename[:index] + "." + vndkVersion + filename[index:]
+	}
+	return filename
+}
+
+func (txt *vndkLibrariesTxt) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	var list []string
+	switch txt.Name() {
+	case llndkLibrariesTxt:
+		for _, filename := range android.SortedStringMapValues(llndkLibraries(ctx.Config())) {
+			if strings.HasPrefix(filename, "libclang_rt.hwasan-") {
+				continue
+			}
+			list = append(list, filename)
+		}
+	case vndkCoreLibrariesTxt:
+		list = android.SortedStringMapValues(vndkCoreLibraries(ctx.Config()))
+	case vndkSpLibrariesTxt:
+		list = android.SortedStringMapValues(vndkSpLibraries(ctx.Config()))
+	case vndkPrivateLibrariesTxt:
+		list = android.SortedStringMapValues(vndkPrivateLibraries(ctx.Config()))
+	case vndkUsingCoreVariantLibrariesTxt:
+		list = android.SortedStringMapValues(vndkUsingCoreVariantLibraries(ctx.Config()))
+	default:
+		ctx.ModuleErrorf("name(%s) is unknown.", txt.Name())
+		return
+	}
+
+	filename := insertVndkVersion(txt.Name(), ctx.DeviceConfig().PlatformVndkVersion())
+	txt.outputFile = android.PathForModuleOut(ctx, filename).OutputPath
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        android.WriteFile,
+		Output:      txt.outputFile,
+		Description: "Writing " + txt.outputFile.String(),
+		Args: map[string]string{
+			"content": strings.Join(list, "\\n"),
+		},
+	})
+
+	installPath := android.PathForModuleInstall(ctx, "etc")
+	ctx.InstallFile(installPath, filename, txt.outputFile)
+}
+
+func (txt *vndkLibrariesTxt) AndroidMkEntries() android.AndroidMkEntries {
+	return android.AndroidMkEntries{
+		Class:      "ETC",
+		OutputFile: android.OptionalPathForPath(txt.outputFile),
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(entries *android.AndroidMkEntries) {
+				entries.SetString("LOCAL_MODULE_STEM", txt.outputFile.Base())
+			},
+		},
+	}
+}
+
+func (txt *vndkLibrariesTxt) OutputFile() android.OutputPath {
+	return txt.outputFile
+}
+
+func (txt *vndkLibrariesTxt) OutputFiles(tag string) (android.Paths, error) {
+	return android.Paths{txt.outputFile}, nil
+}
+
+func (txt *vndkLibrariesTxt) SubDir() string {
+	return ""
+}
+
 func VndkSnapshotSingleton() android.Singleton {
 	return &vndkSnapshotSingleton{}
 }
 
 type vndkSnapshotSingleton struct {
-	installedLlndkLibraries      []string
-	llndkLibrariesFile           android.Path
-	vndkSpLibrariesFile          android.Path
-	vndkCoreLibrariesFile        android.Path
-	vndkPrivateLibrariesFile     android.Path
-	vndkCoreVariantLibrariesFile android.Path
-	vndkLibrariesFile            android.Path
-	vndkSnapshotZipFile          android.OptionalPath
+	vndkLibrariesFile   android.OutputPath
+	vndkSnapshotZipFile android.OptionalPath
 }
 
 func (c *vndkSnapshotSingleton) GenerateBuildActions(ctx android.SingletonContext) {
@@ -650,12 +765,14 @@
 		}
 	}
 
-	snapshotOutputs = append(snapshotOutputs,
-		installSnapshotFileFromPath(c.vndkCoreLibrariesFile, filepath.Join(configsDir, "vndkcore.libraries.txt")),
-		installSnapshotFileFromPath(c.vndkPrivateLibrariesFile, filepath.Join(configsDir, "vndkprivate.libraries.txt")),
-		installSnapshotFileFromPath(c.vndkSpLibrariesFile, filepath.Join(configsDir, "vndksp.libraries.txt")),
-		installSnapshotFileFromPath(c.llndkLibrariesFile, filepath.Join(configsDir, "llndk.libraries.txt")),
-	)
+	// install *.libraries.txt except vndkcorevariant.libraries.txt
+	ctx.VisitAllModules(func(module android.Module) {
+		m, ok := module.(*vndkLibrariesTxt)
+		if !ok || !m.Enabled() || m.Name() == vndkUsingCoreVariantLibrariesTxt {
+			return
+		}
+		snapshotOutputs = append(snapshotOutputs, installSnapshotFileFromPath(m.OutputFile(), filepath.Join(configsDir, m.Name())))
+	})
 
 	/*
 		Dump a map to a list file as:
@@ -737,46 +854,12 @@
 }
 
 func (c *vndkSnapshotSingleton) buildVndkLibrariesTxtFiles(ctx android.SingletonContext) {
-	// Make uses LLNDK_LIBRARIES to determine which libraries to install.
-	// HWASAN is only part of the LL-NDK in builds in which libc depends on HWASAN.
-	// Therefore, by removing the library here, we cause it to only be installed if libc
-	// depends on it.
-	installedLlndkLibraries := make(map[string]string)
-	for lib, filename := range llndkLibraries(ctx.Config()) {
-		if strings.HasPrefix(lib, "libclang_rt.hwasan-") {
-			continue
-		}
-		installedLlndkLibraries[lib] = filename
-	}
-
-	installListFile := func(list []string, fileName string) android.Path {
-		out := android.PathForOutput(ctx, "vndk", fileName)
-		ctx.Build(pctx, android.BuildParams{
-			Rule:        android.WriteFile,
-			Output:      out,
-			Description: "Writing " + out.String(),
-			Args: map[string]string{
-				"content": strings.Join(list, "\\n"),
-			},
-		})
-		return out
-	}
-
-	c.installedLlndkLibraries = android.SortedStringKeys(installedLlndkLibraries)
-
-	llndk := android.SortedStringMapValues(installedLlndkLibraries)
+	llndk := android.SortedStringMapValues(llndkLibraries(ctx.Config()))
 	vndkcore := android.SortedStringMapValues(vndkCoreLibraries(ctx.Config()))
 	vndksp := android.SortedStringMapValues(vndkSpLibraries(ctx.Config()))
 	vndkprivate := android.SortedStringMapValues(vndkPrivateLibraries(ctx.Config()))
-	vndkcorevariant := android.SortedStringMapValues(vndkUsingCoreVariantLibraries(ctx.Config()))
 
-	c.llndkLibrariesFile = installListFile(llndk, "llndk.libraries.txt")
-	c.vndkCoreLibrariesFile = installListFile(vndkcore, "vndkcore.libraries.txt")
-	c.vndkSpLibrariesFile = installListFile(vndksp, "vndksp.libraries.txt")
-	c.vndkPrivateLibrariesFile = installListFile(vndkprivate, "vndkprivate.libraries.txt")
-	c.vndkCoreVariantLibrariesFile = installListFile(vndkcorevariant, "vndkcorevariant.libraries.txt")
-
-	// merged & tagged & filtered-out(libclang_rt)
+	// Build list of vndk libs as merged & tagged & filter-out(libclang_rt):
 	// Since each target have different set of libclang_rt.* files,
 	// keep the common set of files in vndk.libraries.txt
 	var merged []string
@@ -792,32 +875,48 @@
 	merged = append(merged, addPrefix(vndksp, "VNDK-SP: ")...)
 	merged = append(merged, addPrefix(filterOutLibClangRt(vndkcore), "VNDK-core: ")...)
 	merged = append(merged, addPrefix(vndkprivate, "VNDK-private: ")...)
-	c.vndkLibrariesFile = installListFile(merged, "vndk.libraries.txt")
+	c.vndkLibrariesFile = android.PathForOutput(ctx, "vndk", "vndk.libraries.txt")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        android.WriteFile,
+		Output:      c.vndkLibrariesFile,
+		Description: "Writing " + c.vndkLibrariesFile.String(),
+		Args: map[string]string{
+			"content": strings.Join(merged, "\\n"),
+		},
+	})
 }
 
 func (c *vndkSnapshotSingleton) MakeVars(ctx android.MakeVarsContext) {
 	// Make uses LLNDK_MOVED_TO_APEX_LIBRARIES to avoid installing libraries on /system if
 	// they been moved to an apex.
 	movedToApexLlndkLibraries := []string{}
-	for _, lib := range c.installedLlndkLibraries {
+	for lib := range llndkLibraries(ctx.Config()) {
 		// Skip bionic libs, they are handled in different manner
 		if android.DirectlyInAnyApex(&notOnHostContext{}, lib) && !isBionic(lib) {
 			movedToApexLlndkLibraries = append(movedToApexLlndkLibraries, lib)
 		}
 	}
 	ctx.Strict("LLNDK_MOVED_TO_APEX_LIBRARIES", strings.Join(movedToApexLlndkLibraries, " "))
-	ctx.Strict("LLNDK_LIBRARIES", strings.Join(c.installedLlndkLibraries, " "))
+
+	// Make uses LLNDK_LIBRARIES to determine which libraries to install.
+	// HWASAN is only part of the LL-NDK in builds in which libc depends on HWASAN.
+	// Therefore, by removing the library here, we cause it to only be installed if libc
+	// depends on it.
+	installedLlndkLibraries := []string{}
+	for lib := range llndkLibraries(ctx.Config()) {
+		if strings.HasPrefix(lib, "libclang_rt.hwasan-") {
+			continue
+		}
+		installedLlndkLibraries = append(installedLlndkLibraries, lib)
+	}
+	sort.Strings(installedLlndkLibraries)
+	ctx.Strict("LLNDK_LIBRARIES", strings.Join(installedLlndkLibraries, " "))
+
 	ctx.Strict("VNDK_CORE_LIBRARIES", strings.Join(android.SortedStringKeys(vndkCoreLibraries(ctx.Config())), " "))
 	ctx.Strict("VNDK_SAMEPROCESS_LIBRARIES", strings.Join(android.SortedStringKeys(vndkSpLibraries(ctx.Config())), " "))
 	ctx.Strict("VNDK_PRIVATE_LIBRARIES", strings.Join(android.SortedStringKeys(vndkPrivateLibraries(ctx.Config())), " "))
 	ctx.Strict("VNDK_USING_CORE_VARIANT_LIBRARIES", strings.Join(android.SortedStringKeys(vndkUsingCoreVariantLibraries(ctx.Config())), " "))
 
-	ctx.Strict("LLNDK_LIBRARIES_FILE", c.llndkLibrariesFile.String())
-	ctx.Strict("VNDKCORE_LIBRARIES_FILE", c.vndkCoreLibrariesFile.String())
-	ctx.Strict("VNDKSP_LIBRARIES_FILE", c.vndkSpLibrariesFile.String())
-	ctx.Strict("VNDKPRIVATE_LIBRARIES_FILE", c.vndkPrivateLibrariesFile.String())
-	ctx.Strict("VNDKCOREVARIANT_LIBRARIES_FILE", c.vndkCoreVariantLibrariesFile.String())
-
 	ctx.Strict("VNDK_LIBRARIES_FILE", c.vndkLibrariesFile.String())
 	ctx.Strict("SOONG_VNDK_SNAPSHOT_ZIP", c.vndkSnapshotZipFile.String())
 }
diff --git a/cc/xom.go b/cc/xom.go
index 9337990..e1cac53 100644
--- a/cc/xom.go
+++ b/cc/xom.go
@@ -68,7 +68,7 @@
 	if !disableXom || (xom.Properties.Xom != nil && *xom.Properties.Xom) {
 		// XOM is only supported on AArch64 when using lld.
 		if ctx.Arch().ArchType == android.Arm64 && ctx.useClangLld(ctx) {
-			flags.LdFlags = append(flags.LdFlags, "-Wl,-execute-only")
+			flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-execute-only")
 		}
 	}
 
diff --git a/java/aar.go b/java/aar.go
index afe860c..d8db192 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -520,7 +520,7 @@
 }
 
 func (a *AARImport) sdkVersion() string {
-	return proptools.StringDefault(a.properties.Sdk_version, defaultSdkVersion(a))
+	return String(a.properties.Sdk_version)
 }
 
 func (a *AARImport) systemModules() string {
diff --git a/java/androidmk.go b/java/androidmk.go
index 63c7d9a..0510680 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -332,10 +332,9 @@
 				if len(app.dexpreopter.builtInstalled) > 0 {
 					entries.SetString("LOCAL_SOONG_BUILT_INSTALLED", app.dexpreopter.builtInstalled)
 				}
-				for _, split := range app.aapt.splits {
-					install := app.onDeviceDir + "/" +
-						strings.TrimSuffix(app.installApkName, ".apk") + "_" + split.suffix + ".apk"
-					entries.AddStrings("LOCAL_SOONG_BUILT_INSTALLED", split.path.String()+":"+install)
+				for _, extra := range app.extraOutputFiles {
+					install := app.onDeviceDir + "/" + extra.Base()
+					entries.AddStrings("LOCAL_SOONG_BUILT_INSTALLED", extra.String()+":"+install)
 				}
 			},
 		},
diff --git a/java/app.go b/java/app.go
index 58b7721..d53d626 100644
--- a/java/app.go
+++ b/java/app.go
@@ -205,6 +205,7 @@
 
 func (a *AndroidApp) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	a.checkPlatformAPI(ctx)
+	a.checkSdkVersion(ctx)
 	a.generateAndroidBuildActions(ctx)
 }
 
@@ -478,14 +479,13 @@
 	a.certificate = certificates[0]
 
 	// Build a final signed app package.
-	// TODO(jungjw): Consider changing this to installApkName.
-	packageFile := android.PathForModuleOut(ctx, ctx.ModuleName()+".apk")
+	packageFile := android.PathForModuleOut(ctx, a.installApkName+".apk")
 	CreateAndSignAppPackage(ctx, packageFile, a.exportPackage, jniJarFile, dexJarFile, certificates, apkDeps)
 	a.outputFile = packageFile
 
 	for _, split := range a.aapt.splits {
 		// Sign the split APKs
-		packageFile := android.PathForModuleOut(ctx, ctx.ModuleName()+"_"+split.suffix+".apk")
+		packageFile := android.PathForModuleOut(ctx, a.installApkName+"_"+split.suffix+".apk")
 		CreateAndSignAppPackage(ctx, packageFile, split.path, nil, nil, certificates, apkDeps)
 		a.extraOutputFiles = append(a.extraOutputFiles, packageFile)
 	}
@@ -496,9 +496,9 @@
 	a.bundleFile = bundleFile
 
 	// Install the app package.
-	ctx.InstallFile(a.installDir, a.installApkName+".apk", a.outputFile)
-	for _, split := range a.aapt.splits {
-		ctx.InstallFile(a.installDir, a.installApkName+"_"+split.suffix+".apk", split.path)
+	ctx.InstallFile(a.installDir, a.outputFile.Base(), a.outputFile)
+	for _, extra := range a.extraOutputFiles {
+		ctx.InstallFile(a.installDir, extra.Base(), extra)
 	}
 }
 
diff --git a/java/app_test.go b/java/app_test.go
index 05ab856..7635f3d 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -666,6 +666,44 @@
 	}
 }
 
+func TestAppSdkVersionByPartition(t *testing.T) {
+	testJavaError(t, "sdk_version must have a value when the module is located at vendor or product", `
+		android_app {
+			name: "foo",
+			srcs: ["a.java"],
+			vendor: true,
+			platform_apis: true,
+		}
+	`)
+
+	testJava(t, `
+		android_app {
+			name: "bar",
+			srcs: ["b.java"],
+			platform_apis: true,
+		}
+	`)
+
+	for _, enforce := range []bool{true, false} {
+
+		config := testConfig(nil)
+		config.TestProductVariables.EnforceProductPartitionInterface = proptools.BoolPtr(enforce)
+		bp := `
+			android_app {
+				name: "foo",
+				srcs: ["a.java"],
+				product_specific: true,
+				platform_apis: true,
+			}
+		`
+		if enforce {
+			testJavaErrorWithConfig(t, "sdk_version must have a value when the module is located at vendor or product", bp, config)
+		} else {
+			testJavaWithConfig(t, bp, config)
+		}
+	}
+}
+
 func TestJNIPackaging(t *testing.T) {
 	ctx, _ := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
 		cc_library {
@@ -876,7 +914,7 @@
 			packageNameOverride: "foo:bar",
 			expected: []string{
 				// The package apk should be still be the original name for test dependencies.
-				buildDir + "/.intermediates/foo/android_common/foo.apk",
+				buildDir + "/.intermediates/foo/android_common/bar.apk",
 				buildDir + "/target/product/test_device/system/app/bar/bar.apk",
 			},
 		},
@@ -1016,7 +1054,7 @@
 		}
 
 		// Check the certificate paths
-		signapk := variant.Output("foo.apk")
+		signapk := variant.Output(expected.moduleName + ".apk")
 		signFlag := signapk.Args["certificates"]
 		if expected.signFlag != signFlag {
 			t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected.signFlag, signFlag)
diff --git a/java/builder.go b/java/builder.go
index b5dc88a..417a7fa 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -313,7 +313,7 @@
 			// ensure turbine does not fall back to the default bootclasspath.
 			bootClasspath = `--bootclasspath ""`
 		} else {
-			bootClasspath = strings.Join(flags.bootClasspath.FormTurbineClasspath("--bootclasspath "), " ")
+			bootClasspath = flags.bootClasspath.FormTurbineClassPath("--bootclasspath ")
 		}
 	}
 
@@ -330,7 +330,7 @@
 			"javacFlags":    flags.javacFlags,
 			"bootClasspath": bootClasspath,
 			"srcJars":       strings.Join(srcJars.Strings(), " "),
-			"classpath":     strings.Join(classpath.FormTurbineClasspath("--classpath "), " "),
+			"classpath":     classpath.FormTurbineClassPath("--classpath "),
 			"outDir":        android.PathForModuleOut(ctx, "turbine", "classes").String(),
 			"javaVersion":   flags.javaVersion.String(),
 		},
@@ -523,18 +523,26 @@
 
 type classpath android.Paths
 
-func (x *classpath) FormJavaClassPath(optName string) string {
+func (x *classpath) formJoinedClassPath(optName string, sep string) string {
 	if optName != "" && !strings.HasSuffix(optName, "=") && !strings.HasSuffix(optName, " ") {
 		optName += " "
 	}
 	if len(*x) > 0 {
-		return optName + strings.Join(x.Strings(), ":")
+		return optName + strings.Join(x.Strings(), sep)
 	} else {
 		return ""
 	}
 }
+func (x *classpath) FormJavaClassPath(optName string) string {
+	return x.formJoinedClassPath(optName, ":")
+}
 
-func (x *classpath) FormTurbineClasspath(optName string) []string {
+func (x *classpath) FormTurbineClassPath(optName string) string {
+	return x.formJoinedClassPath(optName, " ")
+}
+
+// FormRepeatedClassPath returns a list of arguments with the given optName prefixed to each element of the classpath.
+func (x *classpath) FormRepeatedClassPath(optName string) []string {
 	if x == nil || *x == nil {
 		return nil
 	}
diff --git a/java/dex.go b/java/dex.go
index c8a4fa8..5b25b21 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -85,8 +85,8 @@
 func (j *Module) d8Flags(ctx android.ModuleContext, flags javaBuilderFlags) ([]string, android.Paths) {
 	d8Flags := j.dexCommonFlags(ctx)
 
-	d8Flags = append(d8Flags, flags.bootClasspath.FormTurbineClasspath("--lib ")...)
-	d8Flags = append(d8Flags, flags.classpath.FormTurbineClasspath("--lib ")...)
+	d8Flags = append(d8Flags, flags.bootClasspath.FormRepeatedClassPath("--lib ")...)
+	d8Flags = append(d8Flags, flags.classpath.FormRepeatedClassPath("--lib ")...)
 
 	var d8Deps android.Paths
 	d8Deps = append(d8Deps, flags.bootClasspath...)
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 74ef667..a29665e 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -51,6 +51,7 @@
 
 type bootImageConfig struct {
 	name         string
+	stem         string
 	modules      []string
 	dexLocations []string
 	dexPaths     android.WritablePaths
@@ -71,7 +72,7 @@
 	// In addition, each .art file has an associated .oat and .vdex file, and an
 	// unstripped .oat file
 	for i, m := range image.modules {
-		name := image.name
+		name := image.stem
 		if i != 0 {
 			name += "-" + stemOf(m)
 		}
@@ -139,6 +140,12 @@
 	return false
 }
 
+func skipDexpreoptArtBootJars(ctx android.BuilderContext) bool {
+	// with EMMA_INSTRUMENT_FRAMEWORK=true ART boot class path libraries have dependencies on framework,
+	// therefore dexpreopt ART libraries cannot be dexpreopted in isolation => no ART boot image
+	return ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK")
+}
+
 type dexpreoptBootJars struct {
 	defaultBootImage *bootImage
 	otherImages      []*bootImage
@@ -146,6 +153,14 @@
 	dexpreoptConfigForMake android.WritablePath
 }
 
+// Accessor function for the apex package. Returns nil if dexpreopt is disabled.
+func DexpreoptedArtApexJars(ctx android.BuilderContext) map[android.ArchType]android.Paths {
+	if skipDexpreoptBootJars(ctx) || skipDexpreoptArtBootJars(ctx) {
+		return nil
+	}
+	return artBootImageConfig(ctx).imagesDeps
+}
+
 // dexpreoptBoot singleton rules
 func (d *dexpreoptBootJars) GenerateBuildActions(ctx android.SingletonContext) {
 	if skipDexpreoptBootJars(ctx) {
@@ -169,7 +184,12 @@
 
 	// Always create the default boot image first, to get a unique profile rule for all images.
 	d.defaultBootImage = buildBootImage(ctx, defaultBootImageConfig(ctx))
+	if !skipDexpreoptArtBootJars(ctx) {
+		// Create boot image for the ART apex (build artifacts are accessed via the global boot image config).
+		buildBootImage(ctx, artBootImageConfig(ctx))
+	}
 	if global.GenerateApexImage {
+		// Create boot images for the JIT-zygote experiment.
 		d.otherImages = append(d.otherImages, buildBootImage(ctx, apexBootImageConfig(ctx)))
 	}
 
@@ -178,8 +198,6 @@
 
 // buildBootImage takes a bootImageConfig, creates rules to build it, and returns a *bootImage.
 func buildBootImage(ctx android.SingletonContext, config bootImageConfig) *bootImage {
-	global := dexpreoptGlobalConfig(ctx)
-
 	image := newBootImage(ctx, config)
 
 	bootDexJars := make(android.Paths, len(image.modules))
@@ -223,12 +241,9 @@
 	bootFrameworkProfileRule(ctx, image, missingDeps)
 
 	var allFiles android.Paths
-
-	if !global.DisablePreopt {
-		for _, target := range image.targets {
-			files := buildBootImageRuleForArch(ctx, image, target.Arch.ArchType, profile, missingDeps)
-			allFiles = append(allFiles, files.Paths()...)
-		}
+	for _, target := range image.targets {
+		files := buildBootImageRuleForArch(ctx, image, target.Arch.ArchType, profile, missingDeps)
+		allFiles = append(allFiles, files.Paths()...)
 	}
 
 	if image.zip != nil {
@@ -251,7 +266,7 @@
 	global := dexpreoptGlobalConfig(ctx)
 
 	symbolsDir := image.symbolsDir.Join(ctx, "system/framework", arch.String())
-	symbolsFile := symbolsDir.Join(ctx, image.name+".oat")
+	symbolsFile := symbolsDir.Join(ctx, image.stem+".oat")
 	outputDir := image.dir.Join(ctx, "system/framework", arch.String())
 	outputPath := image.images[arch]
 	oatLocation := pathtools.ReplaceExtension(dexpreopt.PathToLocation(outputPath, arch), "oat")
@@ -381,8 +396,9 @@
 	if global.DisableGenerateProfile || ctx.Config().IsPdkBuild() || ctx.Config().UnbundledBuild() {
 		return nil
 	}
-	return ctx.Config().Once(bootImageProfileRuleKey, func() interface{} {
+	profile := ctx.Config().Once(bootImageProfileRuleKey, func() interface{} {
 		tools := global.Tools
+		defaultProfile := "frameworks/base/config/boot-image-profile.txt"
 
 		rule := android.NewRuleBuilder()
 		rule.MissingDeps(missingDeps)
@@ -394,18 +410,13 @@
 			bootImageProfile = combinedBootImageProfile
 		} else if len(global.BootImageProfiles) == 1 {
 			bootImageProfile = global.BootImageProfiles[0]
+		} else if path := android.ExistentPathForSource(ctx, defaultProfile); path.Valid() {
+			bootImageProfile = path.Path()
 		} else {
-			// If not set, use the default.  Some branches like master-art-host don't have frameworks/base, so manually
-			// handle the case that the default is missing.  Those branches won't attempt to build the profile rule,
-			// and if they do they'll get a missing deps error.
-			defaultProfile := "frameworks/base/config/boot-image-profile.txt"
-			path := android.ExistentPathForSource(ctx, defaultProfile)
-			if path.Valid() {
-				bootImageProfile = path.Path()
-			} else {
-				missingDeps = append(missingDeps, defaultProfile)
-				bootImageProfile = android.PathForOutput(ctx, "missing")
-			}
+			// No profile (not even a default one, which is the case on some branches
+			// like master-art-host that don't have frameworks/base).
+			// Return nil and continue without profile.
+			return nil
 		}
 
 		profile := image.dir.Join(ctx, "boot.prof")
@@ -425,7 +436,11 @@
 		image.profileInstalls = rule.Installs()
 
 		return profile
-	}).(android.WritablePath)
+	})
+	if profile == nil {
+		return nil // wrap nil into a typed pointer with value nil
+	}
+	return profile.(android.WritablePath)
 }
 
 var bootImageProfileRuleKey = android.NewOnceKey("bootImageProfileRule")
diff --git a/java/dexpreopt_bootjars_test.go b/java/dexpreopt_bootjars_test.go
index 244bd52..a684ab2 100644
--- a/java/dexpreopt_bootjars_test.go
+++ b/java/dexpreopt_bootjars_test.go
@@ -62,7 +62,6 @@
 	bootArt := dexpreoptBootJars.Output("boot.art")
 
 	expectedInputs := []string{
-		"dex_bootjars/boot.prof",
 		"dex_bootjars_input/foo.jar",
 		"dex_bootjars_input/bar.jar",
 		"dex_bootjars_input/baz.jar",
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index b3b1317..a6661b3 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -106,15 +106,20 @@
 	return moduleName
 }
 
-func getBootImageConfig(ctx android.PathContext, key android.OnceKey, name string,
-	needZip bool) bootImageConfig {
+// Construct a variant of the global config for dexpreopted bootclasspath jars. The variants differ
+// in the list of input jars (libcore, framework, or both), in the naming scheme for the dexpreopt
+// files (ART recognizes "apex" names as special), and whether to include a zip archive.
+//
+// 'name' is a string unique for each profile (used in directory names and ninja rule names)
+// 'stem' is the basename of the image: the resulting filenames are <stem>[-<jar>].{art,oat,vdex}.
+func getBootImageConfig(ctx android.PathContext, key android.OnceKey, name string, stem string,
+	needZip bool, artApexJarsOnly bool) bootImageConfig {
+
 	return ctx.Config().Once(key, func() interface{} {
 		global := dexpreoptGlobalConfig(ctx)
 
 		artModules := global.ArtApexJars
-		nonFrameworkModules := concat(artModules, global.ProductUpdatableBootModules)
-		frameworkModules := android.RemoveListFromList(global.BootJars, nonFrameworkModules)
-		imageModules := concat(artModules, frameworkModules)
+		imageModules := artModules
 
 		var bootLocations []string
 
@@ -123,9 +128,15 @@
 				filepath.Join("/apex/com.android.art/javalib", stemOf(m)+".jar"))
 		}
 
-		for _, m := range frameworkModules {
-			bootLocations = append(bootLocations,
-				filepath.Join("/system/framework", stemOf(m)+".jar"))
+		if !artApexJarsOnly {
+			nonFrameworkModules := concat(artModules, global.ProductUpdatableBootModules)
+			frameworkModules := android.RemoveListFromList(global.BootJars, nonFrameworkModules)
+			imageModules = concat(imageModules, frameworkModules)
+
+			for _, m := range frameworkModules {
+				bootLocations = append(bootLocations,
+					filepath.Join("/system/framework", stemOf(m)+".jar"))
+			}
 		}
 
 		// The path to bootclasspath dex files needs to be known at module GenerateAndroidBuildAction time, before
@@ -143,13 +154,14 @@
 
 		var zip android.WritablePath
 		if needZip {
-			zip = dir.Join(ctx, name+".zip")
+			zip = dir.Join(ctx, stem+".zip")
 		}
 
 		targets := dexpreoptTargets(ctx)
 
 		imageConfig := bootImageConfig{
 			name:         name,
+			stem:         stem,
 			modules:      imageModules,
 			dexLocations: bootLocations,
 			dexPaths:     bootDexPaths,
@@ -163,7 +175,7 @@
 
 		for _, target := range targets {
 			imageDir := dir.Join(ctx, "system/framework", target.Arch.ArchType.String())
-			imageConfig.images[target.Arch.ArchType] = imageDir.Join(ctx, name+".art")
+			imageConfig.images[target.Arch.ArchType] = imageDir.Join(ctx, stem+".art")
 
 			imagesDeps := make([]android.Path, 0, len(imageConfig.modules)*3)
 			for _, dep := range imageConfig.moduleFiles(ctx, imageDir, ".art", ".oat", ".vdex") {
@@ -176,15 +188,25 @@
 	}).(bootImageConfig)
 }
 
+// Default config is the one that goes in the system image. It includes both libcore and framework.
 var defaultBootImageConfigKey = android.NewOnceKey("defaultBootImageConfig")
-var apexBootImageConfigKey = android.NewOnceKey("apexBootImageConfig")
 
 func defaultBootImageConfig(ctx android.PathContext) bootImageConfig {
-	return getBootImageConfig(ctx, defaultBootImageConfigKey, "boot", true)
+	return getBootImageConfig(ctx, defaultBootImageConfigKey, "boot", "boot", true, false)
 }
 
+// Apex config is used for the JIT-zygote experiment. It includes both libcore and framework, but AOT-compiles only libcore.
+var apexBootImageConfigKey = android.NewOnceKey("apexBootImageConfig")
+
 func apexBootImageConfig(ctx android.PathContext) bootImageConfig {
-	return getBootImageConfig(ctx, apexBootImageConfigKey, "apex", false)
+	return getBootImageConfig(ctx, apexBootImageConfigKey, "apex", "apex", false, false)
+}
+
+// ART config is the one used for the ART apex. It includes only libcore.
+var artBootImageConfigKey = android.NewOnceKey("artBootImageConfig")
+
+func artBootImageConfig(ctx android.PathContext) bootImageConfig {
+	return getBootImageConfig(ctx, artBootImageConfigKey, "art", "boot", false, true)
 }
 
 func defaultBootclasspath(ctx android.PathContext) []string {
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 5d01b54..54f93fe 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -404,7 +404,7 @@
 var _ android.OutputFileProducer = (*Javadoc)(nil)
 
 func (j *Javadoc) sdkVersion() string {
-	return proptools.StringDefault(j.properties.Sdk_version, defaultSdkVersion(j))
+	return String(j.properties.Sdk_version)
 }
 
 func (j *Javadoc) systemModules() string {
@@ -1325,13 +1325,8 @@
 		validatingNullability :=
 			strings.Contains(d.Javadoc.args, "--validate-nullability-from-merged-stubs") ||
 				String(d.properties.Validate_nullability_from_list) != ""
+
 		migratingNullability := String(d.properties.Previous_api) != ""
-
-		if !(migratingNullability || validatingNullability) {
-			ctx.PropertyErrorf("previous_api",
-				"has to be non-empty if annotations was enabled (unless validating nullability)")
-		}
-
 		if migratingNullability {
 			previousApi := android.PathForModuleSrc(ctx, String(d.properties.Previous_api))
 			cmd.FlagWithInput("--migrate-nullness ", previousApi)
diff --git a/java/java.go b/java/java.go
index d7077d1..9bbdff7 100644
--- a/java/java.go
+++ b/java/java.go
@@ -54,6 +54,18 @@
 	android.RegisterSingletonType("kythe_java_extract", kytheExtractJavaFactory)
 }
 
+func (j *Module) checkSdkVersion(ctx android.ModuleContext) {
+	if j.SocSpecific() || j.DeviceSpecific() ||
+		(j.ProductSpecific() && ctx.Config().EnforceProductPartitionInterface()) {
+		if sc, ok := ctx.Module().(sdkContext); ok {
+			if sc.sdkVersion() == "" {
+				ctx.PropertyErrorf("sdk_version",
+					"sdk_version must have a value when the module is located at vendor or product(only if PRODUCT_ENFORCE_PRODUCT_PARTITION_INTERFACE is set).")
+			}
+		}
+	}
+}
+
 func (j *Module) checkPlatformAPI(ctx android.ModuleContext) {
 	if sc, ok := ctx.Module().(sdkContext); ok {
 		usePlatformAPI := proptools.Bool(j.deviceProperties.Platform_apis)
@@ -430,6 +442,11 @@
 	target android.Target
 }
 
+func IsJniDepTag(depTag blueprint.DependencyTag) bool {
+	_, ok := depTag.(*jniDependencyTag)
+	return ok
+}
+
 var (
 	staticLibTag          = dependencyTag{name: "staticlib"}
 	libTag                = dependencyTag{name: "javalib"}
@@ -447,18 +464,6 @@
 	usesLibTag            = dependencyTag{name: "uses-library"}
 )
 
-func defaultSdkVersion(ctx checkVendorModuleContext) string {
-	if ctx.SocSpecific() || ctx.DeviceSpecific() {
-		return "system_current"
-	}
-	return ""
-}
-
-type checkVendorModuleContext interface {
-	SocSpecific() bool
-	DeviceSpecific() bool
-}
-
 type sdkDep struct {
 	useModule, useFiles, useDefaultLibs, invalidVersion bool
 
@@ -505,7 +510,7 @@
 }
 
 func (j *Module) sdkVersion() string {
-	return proptools.StringDefault(j.deviceProperties.Sdk_version, defaultSdkVersion(j))
+	return String(j.deviceProperties.Sdk_version)
 }
 
 func (j *Module) systemModules() string {
@@ -967,6 +972,7 @@
 		// disk and memory usage.
 		javacFlags = append(javacFlags, "-g:source,lines")
 	}
+	javacFlags = append(javacFlags, "-Xlint:-dep-ann")
 
 	if ctx.Config().RunErrorProne() {
 		if config.ErrorProneClasspath == nil {
@@ -1635,6 +1641,7 @@
 }
 
 func (j *Library) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	j.checkSdkVersion(ctx)
 	j.dexpreopter.installPath = android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar")
 	j.dexpreopter.isSDKLibrary = j.deviceProperties.IsSDKLibrary
 	j.dexpreopter.isInstallable = Bool(j.properties.Installable)
@@ -1978,7 +1985,7 @@
 }
 
 func (j *Import) sdkVersion() string {
-	return proptools.StringDefault(j.properties.Sdk_version, defaultSdkVersion(j))
+	return String(j.properties.Sdk_version)
 }
 
 func (j *Import) minSdkVersion() string {
diff --git a/java/java_test.go b/java/java_test.go
index a6ae503..e7b68dd 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -23,6 +23,8 @@
 	"strings"
 	"testing"
 
+	"github.com/google/blueprint/proptools"
+
 	"android/soong/android"
 	"android/soong/cc"
 	"android/soong/dexpreopt"
@@ -228,9 +230,13 @@
 	android.FailIfErrored(t, errs)
 }
 
-func testJavaError(t *testing.T, pattern string, bp string) {
+func testJavaError(t *testing.T, pattern string, bp string) (*android.TestContext, android.Config) {
 	t.Helper()
-	config := testConfig(nil)
+	return testJavaErrorWithConfig(t, pattern, bp, testConfig(nil))
+}
+
+func testJavaErrorWithConfig(t *testing.T, pattern string, bp string, config android.Config) (*android.TestContext, android.Config) {
+	t.Helper()
 	ctx := testContext(bp, nil)
 
 	pathCtx := android.PathContextForTesting(config, nil)
@@ -240,20 +246,26 @@
 	_, errs := ctx.ParseBlueprintsFiles("Android.bp")
 	if len(errs) > 0 {
 		android.FailIfNoMatchingErrors(t, pattern, errs)
-		return
+		return ctx, config
 	}
 	_, errs = ctx.PrepareBuildActions(config)
 	if len(errs) > 0 {
 		android.FailIfNoMatchingErrors(t, pattern, errs)
-		return
+		return ctx, config
 	}
 
 	t.Fatalf("missing expected error %q (0 errors are returned)", pattern)
+
+	return ctx, config
 }
 
 func testJava(t *testing.T, bp string) (*android.TestContext, android.Config) {
 	t.Helper()
-	config := testConfig(nil)
+	return testJavaWithConfig(t, bp, testConfig(nil))
+}
+
+func testJavaWithConfig(t *testing.T, bp string, config android.Config) (*android.TestContext, android.Config) {
+	t.Helper()
 	ctx := testContext(bp, nil)
 	run(t, ctx, config)
 
@@ -315,29 +327,38 @@
 	}
 }
 
-func TestSdkVersion(t *testing.T) {
-	ctx, _ := testJava(t, `
+func TestSdkVersionByPartition(t *testing.T) {
+	testJavaError(t, "sdk_version must have a value when the module is located at vendor or product", `
 		java_library {
 			name: "foo",
 			srcs: ["a.java"],
 			vendor: true,
 		}
+	`)
 
+	testJava(t, `
 		java_library {
 			name: "bar",
 			srcs: ["b.java"],
 		}
 	`)
 
-	foo := ctx.ModuleForTests("foo", "android_common").Module().(*Library)
-	bar := ctx.ModuleForTests("bar", "android_common").Module().(*Library)
+	for _, enforce := range []bool{true, false} {
 
-	if foo.sdkVersion() != "system_current" {
-		t.Errorf("If sdk version of vendor module is empty, it must change to system_current.")
-	}
-
-	if bar.sdkVersion() != "" {
-		t.Errorf("If sdk version of non-vendor module is empty, it keeps empty.")
+		config := testConfig(nil)
+		config.TestProductVariables.EnforceProductPartitionInterface = proptools.BoolPtr(enforce)
+		bp := `
+			java_library {
+				name: "foo",
+				srcs: ["a.java"],
+				product_specific: true,
+			}
+		`
+		if enforce {
+			testJavaErrorWithConfig(t, "sdk_version must have a value when the module is located at vendor or product", bp, config)
+		} else {
+			testJavaWithConfig(t, bp, config)
+		}
 	}
 }
 
diff --git a/java/kotlin.go b/java/kotlin.go
index aa65314..5319a4f 100644
--- a/java/kotlin.go
+++ b/java/kotlin.go
@@ -133,7 +133,7 @@
 	deps = append(deps, srcJars...)
 	deps = append(deps, flags.processorPath...)
 
-	kaptProcessorPath := flags.processorPath.FormTurbineClasspath("-P plugin:org.jetbrains.kotlin.kapt3:apclasspath=")
+	kaptProcessorPath := flags.processorPath.FormRepeatedClassPath("-P plugin:org.jetbrains.kotlin.kapt3:apclasspath=")
 
 	kaptProcessor := ""
 	if flags.processor != "" {
diff --git a/sdk/sdk.go b/sdk/sdk.go
index cb81a14..4eb3665 100644
--- a/sdk/sdk.go
+++ b/sdk/sdk.go
@@ -29,6 +29,7 @@
 )
 
 func init() {
+	pctx.Import("android/soong/android")
 	android.RegisterModuleType("sdk", ModuleFactory)
 	android.RegisterModuleType("sdk_snapshot", SnapshotModuleFactory)
 	android.PreDepsMutators(RegisterPreDepsMutators)
@@ -41,8 +42,7 @@
 
 	properties sdkProperties
 
-	updateScript android.OutputPath
-	freezeScript android.OutputPath
+	snapshotFile android.OptionalPath
 }
 
 type sdkProperties struct {
@@ -104,11 +104,24 @@
 }
 
 func (s *sdk) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	s.buildSnapshotGenerationScripts(ctx)
+	if !s.snapshot() {
+		// We don't need to create a snapshot out of sdk_snapshot.
+		// That doesn't make sense. We need a snapshot to create sdk_snapshot.
+		s.snapshotFile = android.OptionalPathForPath(s.buildSnapshot(ctx))
+	}
 }
 
 func (s *sdk) AndroidMkEntries() android.AndroidMkEntries {
-	return s.androidMkEntriesForScript()
+	if !s.snapshotFile.Valid() {
+		return android.AndroidMkEntries{}
+	}
+
+	return android.AndroidMkEntries{
+		Class:      "FAKE",
+		OutputFile: s.snapshotFile,
+		DistFile:   s.snapshotFile,
+		Include:    "$(BUILD_PHONY_PACKAGE)",
+	}
 }
 
 // RegisterPreDepsMutators registers pre-deps mutators to support modules implementing SdkAware
diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go
index 96129b8..3471bc9 100644
--- a/sdk/sdk_test.go
+++ b/sdk/sdk_test.go
@@ -17,6 +17,7 @@
 import (
 	"io/ioutil"
 	"os"
+	"path/filepath"
 	"strings"
 	"testing"
 
@@ -100,6 +101,8 @@
 		"myapex.pk8":                                 nil,
 		"Test.java":                                  nil,
 		"Test.cpp":                                   nil,
+		"include/Test.h":                             nil,
+		"aidl/foo/bar/Test.aidl":                     nil,
 		"libfoo.so":                                  nil,
 	})
 
@@ -398,7 +401,9 @@
 	var inputs []string
 	buildParams := ctx.ModuleForTests("mysdk", "android_common").Module().BuildParamsForTests()
 	for _, bp := range buildParams {
-		inputs = append(inputs, bp.Implicits.Strings()...)
+		if bp.Input != nil {
+			inputs = append(inputs, bp.Input.String())
+		}
 	}
 
 	// ensure that both 32/64 outputs are inputs of the sdk snapshot
@@ -406,6 +411,69 @@
 	ensureListContains(t, inputs, arm64Output.String())
 }
 
+func TestSnapshot(t *testing.T) {
+	ctx, config := testSdk(t, `
+		sdk {
+			name: "mysdk",
+			java_libs: ["myjavalib"],
+			native_shared_libs: ["mynativelib"],
+		}
+
+		java_library {
+			name: "myjavalib",
+			srcs: ["Test.java"],
+			aidl: {
+				export_include_dirs: ["aidl"],
+			},
+			system_modules: "none",
+			sdk_version: "none",
+			compile_dex: true,
+			host_supported: true,
+		}
+
+		cc_library_shared {
+			name: "mynativelib",
+			srcs: [
+				"Test.cpp",
+				"aidl/foo/bar/Test.aidl",
+			],
+			export_include_dirs: ["include"],
+			aidl: {
+				export_aidl_headers: true,
+			},
+			system_shared_libs: [],
+			stl: "none",
+		}
+	`)
+
+	var copySrcs []string
+	var copyDests []string
+	buildParams := ctx.ModuleForTests("mysdk", "android_common").Module().BuildParamsForTests()
+	for _, bp := range buildParams {
+		if bp.Rule.String() == "android/soong/android.Cp" {
+			copySrcs = append(copySrcs, bp.Input.String())
+			copyDests = append(copyDests, bp.Output.Rel()) // rooted at the snapshot root
+		}
+	}
+
+	buildDir := config.BuildDir()
+	ensureListContains(t, copySrcs, "aidl/foo/bar/Test.aidl")
+	ensureListContains(t, copySrcs, "include/Test.h")
+	ensureListContains(t, copySrcs, filepath.Join(buildDir, ".intermediates/mynativelib/android_arm64_armv8-a_core_shared/gen/aidl/aidl/foo/bar/BnTest.h"))
+	ensureListContains(t, copySrcs, filepath.Join(buildDir, ".intermediates/mynativelib/android_arm64_armv8-a_core_shared/gen/aidl/aidl/foo/bar/BpTest.h"))
+	ensureListContains(t, copySrcs, filepath.Join(buildDir, ".intermediates/mynativelib/android_arm64_armv8-a_core_shared/gen/aidl/aidl/foo/bar/Test.h"))
+	ensureListContains(t, copySrcs, filepath.Join(buildDir, ".intermediates/myjavalib/android_common/turbine-combined/myjavalib.jar"))
+	ensureListContains(t, copySrcs, filepath.Join(buildDir, ".intermediates/mynativelib/android_arm64_armv8-a_core_shared/mynativelib.so"))
+
+	ensureListContains(t, copyDests, "aidl/aidl/foo/bar/Test.aidl")
+	ensureListContains(t, copyDests, "arm64/include/include/Test.h")
+	ensureListContains(t, copyDests, "arm64/include_gen/mynativelib/aidl/foo/bar/BnTest.h")
+	ensureListContains(t, copyDests, "arm64/include_gen/mynativelib/aidl/foo/bar/BpTest.h")
+	ensureListContains(t, copyDests, "arm64/include_gen/mynativelib/aidl/foo/bar/Test.h")
+	ensureListContains(t, copyDests, "java/myjavalib.jar")
+	ensureListContains(t, copyDests, "arm64/lib/mynativelib.so")
+}
+
 var buildDir string
 
 func setUp() {
diff --git a/sdk/update.go b/sdk/update.go
index ce60827..171bb3f 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -16,9 +16,7 @@
 
 import (
 	"fmt"
-	"io"
 	"path/filepath"
-	"strconv"
 	"strings"
 
 	"github.com/google/blueprint/proptools"
@@ -38,9 +36,9 @@
 	indentLevel int
 }
 
-func newGeneratedFile(ctx android.ModuleContext, name string) *generatedFile {
+func newGeneratedFile(ctx android.ModuleContext, path ...string) *generatedFile {
 	return &generatedFile{
-		path:        android.PathForModuleOut(ctx, name).OutputPath,
+		path:        android.PathForModuleOut(ctx, path...).OutputPath,
 		indentLevel: 0,
 	}
 }
@@ -89,6 +87,7 @@
 	exportedIncludeDirs       android.Paths
 	exportedSystemIncludeDirs android.Paths
 	exportedFlags             []string
+	exportedDeps              android.Paths
 	outputFile                android.Path
 }
 
@@ -132,6 +131,7 @@
 			exportedIncludeDirs:       ccModule.ExportedIncludeDirs(),
 			exportedSystemIncludeDirs: ccModule.ExportedSystemIncludeDirs(),
 			exportedFlags:             ccModule.ExportedFlags(),
+			exportedDeps:              ccModule.ExportedDeps(),
 			outputFile:                ccModule.OutputFile().Path(),
 		})
 	})
@@ -169,11 +169,11 @@
 //         aidl/
 //            frameworks/base/core/..../IFoo.aidl   : an exported AIDL file
 //         java/
-//            java/<module_name>/stub.jar    : a stub jar for a java library 'module_name'
+//            <module_name>.jar    : the stub jar for a java library 'module_name'
 //         include/
 //            bionic/libc/include/stdlib.h   : an exported header file
 //         include_gen/
-//            com/android/.../IFoo.h : a generated header file
+//            <module_name>/com/android/.../IFoo.h : a generated header file
 //         <arch>/include/   : arch-specific exported headers
 //         <arch>/include_gen/   : arch-specific generated headers
 //         <arch>/lib/
@@ -182,7 +182,7 @@
 const (
 	aidlIncludeDir            = "aidl"
 	javaStubDir               = "java"
-	javaStubFile              = "stub.jar"
+	javaStubFileSuffix        = ".jar"
 	nativeIncludeDir          = "include"
 	nativeGeneratedIncludeDir = "include_gen"
 	nativeStubDir             = "lib"
@@ -191,7 +191,7 @@
 
 // path to the stub file of a java library. Relative to <sdk_root>/<api_dir>
 func javaStubFilePathFor(javaLib *java.Library) string {
-	return filepath.Join(javaStubDir, javaLib.Name(), javaStubFile)
+	return filepath.Join(javaStubDir, javaLib.Name()+javaStubFileSuffix)
 }
 
 // path to the stub file of a native shared library. Relative to <sdk_root>/<api_dir>
@@ -204,7 +204,6 @@
 func nativeIncludeDirPathsFor(ctx android.ModuleContext, lib archSpecificNativeLibInfo,
 	systemInclude bool, archSpecific bool) []string {
 	var result []string
-	buildDir := ctx.Config().BuildDir()
 	var includeDirs []android.Path
 	if !systemInclude {
 		includeDirs = lib.exportedIncludeDirs
@@ -213,8 +212,8 @@
 	}
 	for _, dir := range includeDirs {
 		var path string
-		if gen := strings.HasPrefix(dir.String(), buildDir); gen {
-			path = filepath.Join(nativeGeneratedIncludeDir, dir.Rel())
+		if _, gen := dir.(android.WritablePath); gen {
+			path = filepath.Join(nativeGeneratedIncludeDir, lib.name)
 		} else {
 			path = filepath.Join(nativeIncludeDir, dir.String())
 		}
@@ -226,21 +225,19 @@
 	return result
 }
 
-// A name that uniquely identifies an prebuilt SDK member for a version of SDK snapshot
+// A name that uniquely identifies a prebuilt SDK member for a version of SDK snapshot
 // This isn't visible to users, so could be changed in future.
 func versionedSdkMemberName(ctx android.ModuleContext, memberName string, version string) string {
 	return ctx.ModuleName() + "_" + memberName + string(android.SdkVersionSeparator) + version
 }
 
-// arm64, arm, x86, x86_64, etc.
-func archTypeOf(module android.Module) string {
-	return module.Target().Arch.ArchType.String()
-}
-
 // buildAndroidBp creates the blueprint file that defines prebuilt modules for each of
 // the SDK members, and the entire sdk_snapshot module for the specified version
+// TODO(jiyong): create a meta info file (e.g. json, protobuf, etc.) instead, and convert it to
+// Android.bp in the (presumably old) branch where the snapshots will be used. This will give us
+// some flexibility to introduce backwards incompatible changes in soong.
 func (s *sdk) buildAndroidBp(ctx android.ModuleContext, version string) android.OutputPath {
-	bp := newGeneratedFile(ctx, "blueprint-"+version+".bp")
+	bp := newGeneratedFile(ctx, "snapshot", "Android.bp")
 	bp.printfln("// This is auto-generated. DO NOT EDIT.")
 	bp.printfln("")
 
@@ -352,52 +349,42 @@
 	return bp.path
 }
 
-func (s *sdk) buildScript(ctx android.ModuleContext, version string) android.OutputPath {
-	sh := newGeneratedFile(ctx, "update_prebuilt-"+version+".sh")
-	buildDir := ctx.Config().BuildDir()
-
-	snapshotPath := func(paths ...string) string {
-		return filepath.Join(ctx.ModuleDir(), version, filepath.Join(paths...))
+// buildSnapshot is the main function in this source file. It creates rules to copy
+// the contents (header files, stub libraries, etc) into the zip file.
+func (s *sdk) buildSnapshot(ctx android.ModuleContext) android.OutputPath {
+	snapshotPath := func(paths ...string) android.OutputPath {
+		return android.PathForModuleOut(ctx, "snapshot").Join(ctx, paths...)
 	}
 
-	// TODO(jiyong) instead of creating script, create a zip file having the Android.bp, the headers,
-	// and the stubs and put it to the dist directory. The dist'ed zip file then would be downloaded,
-	// unzipped and then uploaded to gerrit again.
-	sh.printfln("#!/bin/bash")
-	sh.printfln("echo Updating snapshot of %s in %s", ctx.ModuleName(), snapshotPath())
-	sh.printfln("pushd $ANDROID_BUILD_TOP > /dev/null")
-	sh.printfln("mkdir -p %s", snapshotPath(aidlIncludeDir))
-	sh.printfln("mkdir -p %s", snapshotPath(javaStubDir))
-	sh.printfln("mkdir -p %s", snapshotPath(nativeIncludeDir))
-	sh.printfln("mkdir -p %s", snapshotPath(nativeGeneratedIncludeDir))
-	for _, target := range ctx.MultiTargets() {
-		arch := target.Arch.ArchType.String()
-		sh.printfln("mkdir -p %s", snapshotPath(arch, nativeStubDir))
-		sh.printfln("mkdir -p %s", snapshotPath(arch, nativeIncludeDir))
-		sh.printfln("mkdir -p %s", snapshotPath(arch, nativeGeneratedIncludeDir))
+	var filesToZip android.Paths
+	// copy src to dest and add the dest to the zip
+	copy := func(src android.Path, dest android.OutputPath) {
+		ctx.Build(pctx, android.BuildParams{
+			Rule:   android.Cp,
+			Input:  src,
+			Output: dest,
+		})
+		filesToZip = append(filesToZip, dest)
 	}
 
-	var implicits android.Paths
+	// copy exported AIDL files and stub jar files
 	for _, m := range s.javaLibs(ctx) {
 		headerJars := m.HeaderJars()
 		if len(headerJars) != 1 {
 			panic(fmt.Errorf("there must be only one header jar from %q", m.Name()))
 		}
-		implicits = append(implicits, headerJars...)
+		copy(headerJars[0], snapshotPath(javaStubFilePathFor(m)))
 
-		exportedAidlIncludeDirs := m.AidlIncludeDirs()
-		for _, dir := range exportedAidlIncludeDirs {
-			// Using tar to copy with the directory structure
+		for _, dir := range m.AidlIncludeDirs() {
 			// TODO(jiyong): copy parcelable declarations only
-			sh.printfln("find %s -name \"*.aidl\" | tar cf - -T - | (cd %s; tar xf -)",
-				dir.String(), snapshotPath(aidlIncludeDir))
+			aidlFiles, _ := ctx.GlobWithDeps(dir.String()+"/**/*.aidl", nil)
+			for _, file := range aidlFiles {
+				copy(android.PathForSource(ctx, file), snapshotPath(aidlIncludeDir, file))
+			}
 		}
-
-		copyTarget := snapshotPath(javaStubFilePathFor(m))
-		sh.printfln("mkdir -p %s && cp %s %s",
-			filepath.Dir(copyTarget), headerJars[0].String(), copyTarget)
 	}
 
+	// copy exported header files and stub *.so files
 	nativeLibInfos := s.nativeMemberInfos(ctx)
 	for _, info := range nativeLibInfos {
 
@@ -409,26 +396,32 @@
 				return
 			}
 			for _, dir := range includeDirs {
-				gen := strings.HasPrefix(dir.String(), buildDir)
-				targetDir := nativeIncludeDir
-				if gen {
-					targetDir = nativeGeneratedIncludeDir
+				if _, gen := dir.(android.WritablePath); gen {
+					// generated headers are copied via exportedDeps. See below.
+					continue
 				}
+				targetDir := nativeIncludeDir
 				if info.hasArchSpecificFlags {
 					targetDir = filepath.Join(lib.archType, targetDir)
 				}
-				targetDir = snapshotPath(targetDir)
 
-				sourceDirRoot := "."
-				sourceDirRel := dir.String()
-				if gen {
-					// ex) out/soong/.intermediate/foo/bar/gen/aidl
-					sourceDirRoot = strings.TrimSuffix(dir.String(), dir.Rel())
-					sourceDirRel = dir.Rel()
-				}
 				// TODO(jiyong) copy headers having other suffixes
-				sh.printfln("(cd %s; find %s -name \"*.h\" | tar cf - -T - ) | (cd %s; tar xf -)",
-					sourceDirRoot, sourceDirRel, targetDir)
+				headers, _ := ctx.GlobWithDeps(dir.String()+"/**/*.h", nil)
+				for _, file := range headers {
+					src := android.PathForSource(ctx, file)
+					dest := snapshotPath(targetDir, file)
+					copy(src, dest)
+				}
+			}
+
+			genHeaders := lib.exportedDeps
+			for _, file := range genHeaders {
+				targetDir := nativeGeneratedIncludeDir
+				if info.hasArchSpecificFlags {
+					targetDir = filepath.Join(lib.archType, targetDir)
+				}
+				dest := snapshotPath(targetDir, lib.name, file.Rel())
+				copy(file, dest)
 			}
 		}
 
@@ -438,10 +431,7 @@
 
 		// for each architecture
 		for _, av := range info.archVariants {
-			stub := av.outputFile
-			implicits = append(implicits, stub)
-			copiedStub := snapshotPath(nativeStubFilePathFor(av))
-			sh.printfln("cp %s %s", stub.String(), copiedStub)
+			copy(av.outputFile, snapshotPath(nativeStubFilePathFor(av)))
 
 			if info.hasArchSpecificFlags {
 				printExportedDirCopyCommandsForNativeLibs(av)
@@ -449,69 +439,19 @@
 		}
 	}
 
-	bp := s.buildAndroidBp(ctx, version)
-	implicits = append(implicits, bp)
-	sh.printfln("cp %s %s", bp.String(), snapshotPath("Android.bp"))
+	// generate Android.bp
+	bp := s.buildAndroidBp(ctx, "current")
+	filesToZip = append(filesToZip, bp)
 
-	sh.printfln("popd > /dev/null")
-	sh.printfln("rm -- \"$0\"") // self deleting so that stale script is not used
-	sh.printfln("echo Done")
+	// zip them all
+	zipFile := android.PathForModuleOut(ctx, ctx.ModuleName()+"-current.zip").OutputPath
+	rb := android.NewRuleBuilder()
+	rb.Command().
+		BuiltTool(ctx, "soong_zip").
+		FlagWithArg("-C ", snapshotPath().String()).
+		FlagWithRspFileInputList("-l ", filesToZip).
+		FlagWithOutput("-o ", zipFile)
+	rb.Build(pctx, ctx, "snapshot", "Building snapshot for "+ctx.ModuleName())
 
-	sh.build(pctx, ctx, implicits)
-	return sh.path
-}
-
-func (s *sdk) buildSnapshotGenerationScripts(ctx android.ModuleContext) {
-	if s.snapshot() {
-		// we don't need a script for sdk_snapshot.. as they are frozen
-		return
-	}
-
-	// script to update the 'current' snapshot
-	s.updateScript = s.buildScript(ctx, "current")
-
-	versions := s.frozenVersions(ctx)
-	newVersion := "1"
-	if len(versions) >= 1 {
-		lastVersion := versions[len(versions)-1]
-		lastVersionNum, err := strconv.Atoi(lastVersion)
-		if err != nil {
-			panic(err)
-			return
-		}
-		newVersion = strconv.Itoa(lastVersionNum + 1)
-	}
-	// script to create a new frozen version of snapshot
-	s.freezeScript = s.buildScript(ctx, newVersion)
-}
-
-func (s *sdk) androidMkEntriesForScript() android.AndroidMkEntries {
-	if s.snapshot() {
-		// we don't need a script for sdk_snapshot.. as they are frozen
-		return android.AndroidMkEntries{}
-	}
-
-	entries := android.AndroidMkEntries{
-		Class: "FAKE",
-		// TODO(jiyong): remove this? but androidmk.go expects OutputFile to be specified anyway
-		OutputFile: android.OptionalPathForPath(s.updateScript),
-		Include:    "$(BUILD_SYSTEM)/base_rules.mk",
-		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-			func(entries *android.AndroidMkEntries) {
-				entries.AddStrings("LOCAL_ADDITIONAL_DEPENDENCIES",
-					s.updateScript.String(), s.freezeScript.String())
-			},
-		},
-		ExtraFooters: []android.AndroidMkExtraFootersFunc{
-			func(w io.Writer, name, prefix, moduleDir string, entries *android.AndroidMkEntries) {
-				fmt.Fprintln(w, "$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)")
-				fmt.Fprintln(w, "	touch $@")
-				fmt.Fprintln(w, "	echo ##################################################")
-				fmt.Fprintln(w, "	echo To update current SDK: execute", filepath.Join("\\$$ANDROID_BUILD_TOP", s.updateScript.String()))
-				fmt.Fprintln(w, "	echo To freeze current SDK: execute", filepath.Join("\\$$ANDROID_BUILD_TOP", s.freezeScript.String()))
-				fmt.Fprintln(w, "	echo ##################################################")
-			},
-		},
-	}
-	return entries
+	return zipFile
 }