[automerger skipped] Revert "Load env variables before c.config()" am: 4e88859af0 am: 9ee45ad09a am: fb58d3aaaf am: b4d9316616 am: 27a8b4640a -s ours

am skip reason: Merged-In I416e8da75f84aa2b53995f525cf50501488dc972 with SHA-1 51c4091309 is already in history

Original change: https://googleplex-android-review.googlesource.com/c/platform/build/soong/+/15741350

Change-Id: I737b26805ce6ce65551973d0e49eb506f925705a
diff --git a/Android.bp b/Android.bp
index 8f7f3e2..d6260b4 100644
--- a/Android.bp
+++ b/Android.bp
@@ -92,7 +92,7 @@
 }
 
 cc_genrule {
-    name: "host_bionic_linker_flags",
+    name: "host_bionic_linker_script",
     host_supported: true,
     device_supported: false,
     target: {
@@ -107,12 +107,22 @@
         },
     },
     tools: ["extract_linker"],
-    cmd: "$(location) -f $(out) $(in)",
+    cmd: "$(location) -T $(out) $(in)",
     srcs: [":linker"],
-    out: ["linker.flags"],
+    out: ["linker.script"],
 }
 
 // Instantiate the dex_bootjars singleton module.
 dex_bootjars {
     name: "dex_bootjars",
 }
+
+// Pseudo-test that's run on checkbuilds to ensure that get_clang_version can
+// parse cc/config/global.go.
+genrule {
+    name: "get_clang_version_test",
+    cmd: "$(location get_clang_version) > $(out)",
+    tools: ["get_clang_version"],
+    srcs: ["cc/config/global.go"],
+    out: ["clang-prebuilts-version.txt"],
+}
diff --git a/OWNERS b/OWNERS
index e851bf7..f0ccd82 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,18 +1,27 @@
 # This file is included by several other projects as the list of people
 # approving build related projects.
 
+# AMER
 ahumesky@google.com
+alexmarquez@google.com
 asmundak@google.com
 ccross@android.com
 cparsons@google.com
 dwillemsen@google.com
 eakammer@google.com
-jingwen@google.com
+jobredeaux@google.com
 joeo@google.com
-jungjw@google.com
-lberki@google.com
+lamontjones@google.com
+spandandas@google.com
+weiwli@google.com
+yudiliu@google.com
+yuntaoxu@google.com
+
+# APAC
+jingwen@google.com
 ruperts@google.com
 
-# To expedite LON reviews
+# EMEA
 hansson@google.com
+lberki@google.com
 paulduffin@google.com
diff --git a/README.md b/README.md
index b7e93f4..e92349e 100644
--- a/README.md
+++ b/README.md
@@ -213,8 +213,8 @@
 
 A module name's **scope** is the smallest namespace containing it. Suppose a
 source tree has `device/my` and `device/my/display` namespaces. If `libfoo`
-module is defined in `device/co/display/lib/Android.bp`, its namespace is
-`device/co/display`.
+module is defined in `device/my/display/lib/Android.bp`, its namespace is
+`device/my/display`.
 
 The name uniqueness thus means that module's name is unique within its scope. In
 other words, "//_scope_:_name_" is globally unique module reference, e.g,
@@ -451,15 +451,10 @@
 
 The values of the variables can be set from a product's `BoardConfig.mk` file:
 ```
-SOONG_CONFIG_NAMESPACES += acme
-SOONG_CONFIG_acme += \
-    board \
-    feature \
-    width \
-
-SOONG_CONFIG_acme_board := soc_a
-SOONG_CONFIG_acme_feature := true
-SOONG_CONFIG_acme_width := 200
+$(call add_soong_config_namespace, acme)
+$(call add_soong_config_var_value, acme, board, soc_a)
+$(call add_soong_config_var_value, acme, feature, true)
+$(call add_soong_config_var_value, acme, width, 200)
 ```
 
 The `acme_cc_defaults` module type can be used anywhere after the definition in
diff --git a/android/Android.bp b/android/Android.bp
index 5d0f2b9..1bccd7b 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -17,6 +17,8 @@
         "soong-response",
         "soong-shared",
         "soong-ui-metrics_proto",
+        "golang-protobuf-proto",
+        "golang-protobuf-encoding-prototext",
     ],
     srcs: [
         "androidmk.go",
diff --git a/android/androidmk.go b/android/androidmk.go
index 557e7ba..9853d2c 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -486,9 +486,9 @@
 	if a.Include == "" {
 		a.Include = "$(BUILD_PREBUILT)"
 	}
-	a.Required = append(a.Required, amod.commonProperties.Required...)
-	a.Host_required = append(a.Host_required, amod.commonProperties.Host_required...)
-	a.Target_required = append(a.Target_required, amod.commonProperties.Target_required...)
+	a.Required = append(a.Required, mod.(Module).RequiredModuleNames()...)
+	a.Host_required = append(a.Host_required, mod.(Module).HostRequiredModuleNames()...)
+	a.Target_required = append(a.Target_required, mod.(Module).TargetRequiredModuleNames()...)
 
 	for _, distString := range a.GetDistForGoals(mod) {
 		fmt.Fprintf(&a.header, distString)
@@ -571,7 +571,7 @@
 
 	if host {
 		makeOs := amod.Os().String()
-		if amod.Os() == Linux || amod.Os() == LinuxBionic {
+		if amod.Os() == Linux || amod.Os() == LinuxBionic || amod.Os() == LinuxMusl {
 			makeOs = "linux"
 		}
 		a.SetString("LOCAL_MODULE_HOST_OS", makeOs)
diff --git a/android/apex.go b/android/apex.go
index 4618fe9..b9efe4e 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -54,6 +54,10 @@
 	// True if this module comes from an updatable apexBundle.
 	Updatable bool
 
+	// True if this module can use private platform APIs. Only non-updatable APEX can set this
+	// to true.
+	UsePlatformApis bool
+
 	// The list of SDK modules that the containing apexBundle depends on.
 	RequiredSdks SdkRefs
 
@@ -87,16 +91,31 @@
 
 var ApexInfoProvider = blueprint.NewMutatorProvider(ApexInfo{}, "apex")
 
+func (i ApexInfo) AddJSONData(d *map[string]interface{}) {
+	(*d)["Apex"] = map[string]interface{}{
+		"ApexVariationName": i.ApexVariationName,
+		"MinSdkVersion":     i.MinSdkVersion,
+		"InApexModules":     i.InApexModules,
+		"InApexVariants":    i.InApexVariants,
+		"ForPrebuiltApex":   i.ForPrebuiltApex,
+	}
+}
+
 // mergedName gives the name of the alias variation that will be used when multiple apex variations
 // of a module can be deduped into one variation. For example, if libfoo is included in both apex.a
 // and apex.b, and if the two APEXes have the same min_sdk_version (say 29), then libfoo doesn't
 // have to be built twice, but only once. In that case, the two apex variations apex.a and apex.b
-// are configured to have the same alias variation named apex29.
+// are configured to have the same alias variation named apex29. Whether platform APIs is allowed
+// or not also matters; if two APEXes don't have the same allowance, they get different names and
+// thus wouldn't be merged.
 func (i ApexInfo) mergedName(ctx PathContext) string {
 	name := "apex" + strconv.Itoa(i.MinSdkVersion.FinalOrFutureInt())
 	for _, sdk := range i.RequiredSdks {
 		name += "_" + sdk.Name + "_" + sdk.Version
 	}
+	if i.UsePlatformApis {
+		name += "_private"
+	}
 	return name
 }
 
@@ -527,6 +546,10 @@
 			merged[index].InApexModules = append(merged[index].InApexModules, apexInfo.InApexModules...)
 			merged[index].ApexContents = append(merged[index].ApexContents, apexInfo.ApexContents...)
 			merged[index].Updatable = merged[index].Updatable || apexInfo.Updatable
+			if merged[index].UsePlatformApis != apexInfo.UsePlatformApis {
+				panic(fmt.Errorf("variants having different UsePlatformApis can't be merged"))
+			}
+			merged[index].UsePlatformApis = apexInfo.UsePlatformApis
 		} else {
 			seen[mergedName] = len(merged)
 			apexInfo.ApexVariationName = mergedName
diff --git a/android/apex_test.go b/android/apex_test.go
index e112369..60a639b 100644
--- a/android/apex_test.go
+++ b/android/apex_test.go
@@ -33,10 +33,10 @@
 		{
 			name: "single",
 			in: []ApexInfo{
-				{"foo", FutureApiLevel, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
+				{"foo", FutureApiLevel, false, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
 			},
 			wantMerged: []ApexInfo{
-				{"apex10000", FutureApiLevel, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
+				{"apex10000", FutureApiLevel, false, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
 			},
 			wantAliases: [][2]string{
 				{"foo", "apex10000"},
@@ -45,11 +45,11 @@
 		{
 			name: "merge",
 			in: []ApexInfo{
-				{"foo", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
-				{"bar", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
+				{"foo", FutureApiLevel, false, false, SdkRefs{{"baz", "1"}}, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
+				{"bar", FutureApiLevel, false, false, SdkRefs{{"baz", "1"}}, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
 			},
 			wantMerged: []ApexInfo{
-				{"apex10000_baz_1", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, false}},
+				{"apex10000_baz_1", FutureApiLevel, false, false, SdkRefs{{"baz", "1"}}, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, false}},
 			wantAliases: [][2]string{
 				{"bar", "apex10000_baz_1"},
 				{"foo", "apex10000_baz_1"},
@@ -58,12 +58,12 @@
 		{
 			name: "don't merge version",
 			in: []ApexInfo{
-				{"foo", FutureApiLevel, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
-				{"bar", uncheckedFinalApiLevel(30), false, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
+				{"foo", FutureApiLevel, false, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
+				{"bar", uncheckedFinalApiLevel(30), false, false, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
 			},
 			wantMerged: []ApexInfo{
-				{"apex30", uncheckedFinalApiLevel(30), false, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
-				{"apex10000", FutureApiLevel, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
+				{"apex30", uncheckedFinalApiLevel(30), false, false, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
+				{"apex10000", FutureApiLevel, false, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
 			},
 			wantAliases: [][2]string{
 				{"bar", "apex30"},
@@ -73,11 +73,11 @@
 		{
 			name: "merge updatable",
 			in: []ApexInfo{
-				{"foo", FutureApiLevel, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
-				{"bar", FutureApiLevel, true, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
+				{"foo", FutureApiLevel, false, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
+				{"bar", FutureApiLevel, true, false, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
 			},
 			wantMerged: []ApexInfo{
-				{"apex10000", FutureApiLevel, true, nil, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex},
+				{"apex10000", FutureApiLevel, true, false, nil, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex},
 			},
 			wantAliases: [][2]string{
 				{"bar", "apex10000"},
@@ -87,12 +87,12 @@
 		{
 			name: "don't merge sdks",
 			in: []ApexInfo{
-				{"foo", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
-				{"bar", FutureApiLevel, false, SdkRefs{{"baz", "2"}}, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
+				{"foo", FutureApiLevel, false, false, SdkRefs{{"baz", "1"}}, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
+				{"bar", FutureApiLevel, false, false, SdkRefs{{"baz", "2"}}, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
 			},
 			wantMerged: []ApexInfo{
-				{"apex10000_baz_2", FutureApiLevel, false, SdkRefs{{"baz", "2"}}, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
-				{"apex10000_baz_1", FutureApiLevel, false, SdkRefs{{"baz", "1"}}, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
+				{"apex10000_baz_2", FutureApiLevel, false, false, SdkRefs{{"baz", "2"}}, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
+				{"apex10000_baz_1", FutureApiLevel, false, false, SdkRefs{{"baz", "1"}}, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
 			},
 			wantAliases: [][2]string{
 				{"bar", "apex10000_baz_2"},
@@ -102,21 +102,36 @@
 		{
 			name: "don't merge when for prebuilt_apex",
 			in: []ApexInfo{
-				{"foo", FutureApiLevel, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
-				{"bar", FutureApiLevel, true, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
+				{"foo", FutureApiLevel, false, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
+				{"bar", FutureApiLevel, true, false, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
 				// This one should not be merged in with the others because it is for
 				// a prebuilt_apex.
-				{"baz", FutureApiLevel, true, nil, []string{"baz"}, []string{"baz"}, nil, ForPrebuiltApex},
+				{"baz", FutureApiLevel, true, false, nil, []string{"baz"}, []string{"baz"}, nil, ForPrebuiltApex},
 			},
 			wantMerged: []ApexInfo{
-				{"apex10000", FutureApiLevel, true, nil, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex},
-				{"baz", FutureApiLevel, true, nil, []string{"baz"}, []string{"baz"}, nil, ForPrebuiltApex},
+				{"apex10000", FutureApiLevel, true, false, nil, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex},
+				{"baz", FutureApiLevel, true, false, nil, []string{"baz"}, []string{"baz"}, nil, ForPrebuiltApex},
 			},
 			wantAliases: [][2]string{
 				{"bar", "apex10000"},
 				{"foo", "apex10000"},
 			},
 		},
+		{
+			name: "don't merge different UsePlatformApis",
+			in: []ApexInfo{
+				{"foo", FutureApiLevel, false, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
+				{"bar", FutureApiLevel, false, true, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
+			},
+			wantMerged: []ApexInfo{
+				{"apex10000_private", FutureApiLevel, false, true, nil, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
+				{"apex10000", FutureApiLevel, false, false, nil, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
+			},
+			wantAliases: [][2]string{
+				{"bar", "apex10000_private"},
+				{"foo", "apex10000"},
+			},
+		},
 	}
 
 	for _, tt := range tests {
diff --git a/android/arch.go b/android/arch.go
index bb1b613..f7eb963 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -21,6 +21,8 @@
 	"runtime"
 	"strings"
 
+	"android/soong/bazel"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/bootstrap"
 	"github.com/google/blueprint/proptools"
@@ -253,7 +255,7 @@
 // Linux returns true if the OS uses the Linux kernel, i.e. if the OS is Android or is Linux
 // with or without the Bionic libc runtime.
 func (os OsType) Linux() bool {
-	return os == Android || os == Linux || os == LinuxBionic
+	return os == Android || os == Linux || os == LinuxBionic || os == LinuxMusl
 }
 
 // newOsType constructs an OsType and adds it to the global lists.
@@ -289,28 +291,6 @@
 	return NoOsType
 }
 
-// BuildOs returns the OsType for the OS that the build is running on.
-var BuildOs = func() OsType {
-	switch runtime.GOOS {
-	case "linux":
-		return Linux
-	case "darwin":
-		return Darwin
-	default:
-		panic(fmt.Sprintf("unsupported OS: %s", runtime.GOOS))
-	}
-}()
-
-// BuildArch returns the ArchType for the CPU that the build is running on.
-var BuildArch = func() ArchType {
-	switch runtime.GOARCH {
-	case "amd64":
-		return X86_64
-	default:
-		panic(fmt.Sprintf("unsupported Arch: %s", runtime.GOARCH))
-	}
-}()
-
 var (
 	// osTypeList contains a list of all the supported OsTypes, including ones not supported
 	// by the current build host or the target device.
@@ -325,6 +305,8 @@
 	NoOsType OsType
 	// Linux is the OS for the Linux kernel plus the glibc runtime.
 	Linux = newOsType("linux_glibc", Host, false, X86, X86_64)
+	// LinuxMusl is the OS for the Linux kernel plus the musl runtime.
+	LinuxMusl = newOsType("linux_musl", Host, false, X86, X86_64)
 	// Darwin is the OS for MacOS/Darwin host machines.
 	Darwin = newOsType("darwin", Host, false, X86_64)
 	// LinuxBionic is the OS for the Linux kernel plus the Bionic libc runtime, but without the
@@ -335,8 +317,6 @@
 	// Android is the OS for target devices that run all of Android, including the Linux kernel
 	// and the Bionic libc runtime.
 	Android = newOsType("android", Device, false, Arm, Arm64, X86, X86_64)
-	// Fuchsia is the OS for target devices that run Fuchsia.
-	Fuchsia = newOsType("fuchsia", Device, false, Arm64, X86_64)
 
 	// CommonOS is a pseudo OSType for a common OS variant, which is OsType agnostic and which
 	// has dependencies on all the OS variants.
@@ -885,6 +865,8 @@
 			"Android64",
 			"Android32",
 			"Bionic",
+			"Glibc",
+			"Musl",
 			"Linux",
 			"Not_windows",
 			"Arm_on_x86",
@@ -897,7 +879,7 @@
 
 			// Add the OS/Arch combinations, e.g. "android_arm64".
 			for _, archType := range osArchTypeMap[os] {
-				targets = append(targets, os.Field+"_"+archType.Name)
+				targets = append(targets, GetCompoundTargetField(os, archType))
 
 				// Also add the special "linux_<arch>" and "bionic_<arch>" property structs.
 				if os.Linux() {
@@ -1055,24 +1037,28 @@
 // Returns the immediate child of the input property struct that corresponds to
 // the sub-property "field".
 func getChildPropertyStruct(ctx ArchVariantContext,
-	src reflect.Value, field, userFriendlyField string) reflect.Value {
+	src reflect.Value, field, userFriendlyField string) (reflect.Value, bool) {
 
 	// Step into non-nil pointers to structs in the src value.
 	if src.Kind() == reflect.Ptr {
 		if src.IsNil() {
-			return src
+			return reflect.Value{}, false
 		}
 		src = src.Elem()
 	}
 
 	// Find the requested field in the src struct.
-	src = src.FieldByName(proptools.FieldNameForProperty(field))
-	if !src.IsValid() {
+	child := src.FieldByName(proptools.FieldNameForProperty(field))
+	if !child.IsValid() {
 		ctx.ModuleErrorf("field %q does not exist", userFriendlyField)
-		return src
+		return reflect.Value{}, false
 	}
 
-	return src
+	if child.IsZero() {
+		return reflect.Value{}, false
+	}
+
+	return child, true
 }
 
 // Squash the appropriate OS-specific property structs into the matching top level property structs
@@ -1099,8 +1085,9 @@
 			if os.Class == Host {
 				field := "Host"
 				prefix := "target.host"
-				hostProperties := getChildPropertyStruct(ctx, targetProp, field, prefix)
-				mergePropertyStruct(ctx, genProps, hostProperties)
+				if hostProperties, ok := getChildPropertyStruct(ctx, targetProp, field, prefix); ok {
+					mergePropertyStruct(ctx, genProps, hostProperties)
+				}
 			}
 
 			// Handle target OS generalities of the form:
@@ -1112,15 +1099,41 @@
 			if os.Linux() {
 				field := "Linux"
 				prefix := "target.linux"
-				linuxProperties := getChildPropertyStruct(ctx, targetProp, field, prefix)
-				mergePropertyStruct(ctx, genProps, linuxProperties)
+				if linuxProperties, ok := getChildPropertyStruct(ctx, targetProp, field, prefix); ok {
+					mergePropertyStruct(ctx, genProps, linuxProperties)
+				}
 			}
 
 			if os.Bionic() {
 				field := "Bionic"
 				prefix := "target.bionic"
-				bionicProperties := getChildPropertyStruct(ctx, targetProp, field, prefix)
-				mergePropertyStruct(ctx, genProps, bionicProperties)
+				if bionicProperties, ok := getChildPropertyStruct(ctx, targetProp, field, prefix); ok {
+					mergePropertyStruct(ctx, genProps, bionicProperties)
+				}
+			}
+
+			if os == Linux {
+				field := "Glibc"
+				prefix := "target.glibc"
+				if bionicProperties, ok := getChildPropertyStruct(ctx, targetProp, field, prefix); ok {
+					mergePropertyStruct(ctx, genProps, bionicProperties)
+				}
+			}
+
+			if os == LinuxMusl {
+				field := "Musl"
+				prefix := "target.musl"
+				if bionicProperties, ok := getChildPropertyStruct(ctx, targetProp, field, prefix); ok {
+					mergePropertyStruct(ctx, genProps, bionicProperties)
+				}
+
+				// Special case:  to ease the transition from glibc to musl, apply linux_glibc
+				// properties (which has historically mean host linux) to musl variants.
+				field = "Linux_glibc"
+				prefix = "target.linux_glibc"
+				if bionicProperties, ok := getChildPropertyStruct(ctx, targetProp, field, prefix); ok {
+					mergePropertyStruct(ctx, genProps, bionicProperties)
+				}
 			}
 
 			// Handle target OS properties in the form:
@@ -1137,14 +1150,16 @@
 			// },
 			field := os.Field
 			prefix := "target." + os.Name
-			osProperties := getChildPropertyStruct(ctx, targetProp, field, prefix)
-			mergePropertyStruct(ctx, genProps, osProperties)
+			if osProperties, ok := getChildPropertyStruct(ctx, targetProp, field, prefix); ok {
+				mergePropertyStruct(ctx, genProps, osProperties)
+			}
 
 			if os.Class == Host && os != Windows {
 				field := "Not_windows"
 				prefix := "target.not_windows"
-				notWindowsProperties := getChildPropertyStruct(ctx, targetProp, field, prefix)
-				mergePropertyStruct(ctx, genProps, notWindowsProperties)
+				if notWindowsProperties, ok := getChildPropertyStruct(ctx, targetProp, field, prefix); ok {
+					mergePropertyStruct(ctx, genProps, notWindowsProperties)
+				}
 			}
 
 			// Handle 64-bit device properties in the form:
@@ -1164,13 +1179,15 @@
 				if ctx.Config().Android64() {
 					field := "Android64"
 					prefix := "target.android64"
-					android64Properties := getChildPropertyStruct(ctx, targetProp, field, prefix)
-					mergePropertyStruct(ctx, genProps, android64Properties)
+					if android64Properties, ok := getChildPropertyStruct(ctx, targetProp, field, prefix); ok {
+						mergePropertyStruct(ctx, genProps, android64Properties)
+					}
 				} else {
 					field := "Android32"
 					prefix := "target.android32"
-					android32Properties := getChildPropertyStruct(ctx, targetProp, field, prefix)
-					mergePropertyStruct(ctx, genProps, android32Properties)
+					if android32Properties, ok := getChildPropertyStruct(ctx, targetProp, field, prefix); ok {
+						mergePropertyStruct(ctx, genProps, android32Properties)
+					}
 				}
 			}
 		}
@@ -1186,12 +1203,11 @@
 // },
 // This struct will also contain sub-structs containing to the architecture/CPU
 // variants and features that themselves contain properties specific to those.
-func getArchTypeStruct(ctx ArchVariantContext, archProperties interface{}, archType ArchType) reflect.Value {
+func getArchTypeStruct(ctx ArchVariantContext, archProperties interface{}, archType ArchType) (reflect.Value, bool) {
 	archPropValues := reflect.ValueOf(archProperties).Elem()
 	archProp := archPropValues.FieldByName("Arch").Elem()
 	prefix := "arch." + archType.Name
-	archStruct := getChildPropertyStruct(ctx, archProp, archType.Name, prefix)
-	return archStruct
+	return getChildPropertyStruct(ctx, archProp, archType.Name, prefix)
 }
 
 // Returns the struct containing the properties specific to a given multilib
@@ -1201,11 +1217,14 @@
 //         key: value,
 //     },
 // },
-func getMultilibStruct(ctx ArchVariantContext, archProperties interface{}, archType ArchType) reflect.Value {
+func getMultilibStruct(ctx ArchVariantContext, archProperties interface{}, archType ArchType) (reflect.Value, bool) {
 	archPropValues := reflect.ValueOf(archProperties).Elem()
 	multilibProp := archPropValues.FieldByName("Multilib").Elem()
-	multilibProperties := getChildPropertyStruct(ctx, multilibProp, archType.Multilib, "multilib."+archType.Multilib)
-	return multilibProperties
+	return getChildPropertyStruct(ctx, multilibProp, archType.Multilib, "multilib."+archType.Multilib)
+}
+
+func GetCompoundTargetField(os OsType, arch ArchType) string {
+	return os.Field + "_" + arch.Name
 }
 
 // Returns the structs corresponding to the properties specific to the given
@@ -1219,58 +1238,64 @@
 	archType := arch.ArchType
 
 	if arch.ArchType != Common {
-		archStruct := getArchTypeStruct(ctx, archProperties, arch.ArchType)
-		result = append(result, archStruct)
+		archStruct, ok := getArchTypeStruct(ctx, archProperties, arch.ArchType)
+		if ok {
+			result = append(result, archStruct)
 
-		// Handle arch-variant-specific properties in the form:
-		// arch: {
-		//     arm: {
-		//         variant: {
-		//             key: value,
-		//         },
-		//     },
-		// },
-		v := variantReplacer.Replace(arch.ArchVariant)
-		if v != "" {
-			prefix := "arch." + archType.Name + "." + v
-			variantProperties := getChildPropertyStruct(ctx, archStruct, v, prefix)
-			result = append(result, variantProperties)
-		}
+			// Handle arch-variant-specific properties in the form:
+			// arch: {
+			//     arm: {
+			//         variant: {
+			//             key: value,
+			//         },
+			//     },
+			// },
+			v := variantReplacer.Replace(arch.ArchVariant)
+			if v != "" {
+				prefix := "arch." + archType.Name + "." + v
+				if variantProperties, ok := getChildPropertyStruct(ctx, archStruct, v, prefix); ok {
+					result = append(result, variantProperties)
+				}
+			}
 
-		// Handle cpu-variant-specific properties in the form:
-		// arch: {
-		//     arm: {
-		//         variant: {
-		//             key: value,
-		//         },
-		//     },
-		// },
-		if arch.CpuVariant != arch.ArchVariant {
-			c := variantReplacer.Replace(arch.CpuVariant)
-			if c != "" {
-				prefix := "arch." + archType.Name + "." + c
-				cpuVariantProperties := getChildPropertyStruct(ctx, archStruct, c, prefix)
-				result = append(result, cpuVariantProperties)
+			// Handle cpu-variant-specific properties in the form:
+			// arch: {
+			//     arm: {
+			//         variant: {
+			//             key: value,
+			//         },
+			//     },
+			// },
+			if arch.CpuVariant != arch.ArchVariant {
+				c := variantReplacer.Replace(arch.CpuVariant)
+				if c != "" {
+					prefix := "arch." + archType.Name + "." + c
+					if cpuVariantProperties, ok := getChildPropertyStruct(ctx, archStruct, c, prefix); ok {
+						result = append(result, cpuVariantProperties)
+					}
+				}
+			}
+
+			// Handle arch-feature-specific properties in the form:
+			// arch: {
+			//     arm: {
+			//         feature: {
+			//             key: value,
+			//         },
+			//     },
+			// },
+			for _, feature := range arch.ArchFeatures {
+				prefix := "arch." + archType.Name + "." + feature
+				if featureProperties, ok := getChildPropertyStruct(ctx, archStruct, feature, prefix); ok {
+					result = append(result, featureProperties)
+				}
 			}
 		}
 
-		// Handle arch-feature-specific properties in the form:
-		// arch: {
-		//     arm: {
-		//         feature: {
-		//             key: value,
-		//         },
-		//     },
-		// },
-		for _, feature := range arch.ArchFeatures {
-			prefix := "arch." + archType.Name + "." + feature
-			featureProperties := getChildPropertyStruct(ctx, archStruct, feature, prefix)
-			result = append(result, featureProperties)
+		if multilibProperties, ok := getMultilibStruct(ctx, archProperties, archType); ok {
+			result = append(result, multilibProperties)
 		}
 
-		multilibProperties := getMultilibStruct(ctx, archProperties, archType)
-		result = append(result, multilibProperties)
-
 		// Handle combined OS-feature and arch specific properties in the form:
 		// target: {
 		//     bionic_x86: {
@@ -1280,15 +1305,17 @@
 		if os.Linux() {
 			field := "Linux_" + arch.ArchType.Name
 			userFriendlyField := "target.linux_" + arch.ArchType.Name
-			linuxProperties := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField)
-			result = append(result, linuxProperties)
+			if linuxProperties, ok := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField); ok {
+				result = append(result, linuxProperties)
+			}
 		}
 
 		if os.Bionic() {
 			field := "Bionic_" + archType.Name
 			userFriendlyField := "target.bionic_" + archType.Name
-			bionicProperties := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField)
-			result = append(result, bionicProperties)
+			if bionicProperties, ok := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField); ok {
+				result = append(result, bionicProperties)
+			}
 		}
 
 		// Handle combined OS and arch specific properties in the form:
@@ -1306,10 +1333,21 @@
 		//         key: value,
 		//     },
 		// },
-		field := os.Field + "_" + archType.Name
+		field := GetCompoundTargetField(os, archType)
 		userFriendlyField := "target." + os.Name + "_" + archType.Name
-		osArchProperties := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField)
-		result = append(result, osArchProperties)
+		if osArchProperties, ok := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField); ok {
+			result = append(result, osArchProperties)
+		}
+
+		if os == LinuxMusl {
+			// Special case:  to ease the transition from glibc to musl, apply linux_glibc
+			// properties (which has historically mean host linux) to musl variants.
+			field := "Linux_glibc_" + archType.Name
+			userFriendlyField := "target.linux_glibc_" + archType.Name
+			if osArchProperties, ok := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField); ok {
+				result = append(result, osArchProperties)
+			}
+		}
 	}
 
 	// Handle arm on x86 properties in the form:
@@ -1326,21 +1364,24 @@
 			hasArmAndroidArch(ctx.Config().Targets[Android])) {
 			field := "Arm_on_x86"
 			userFriendlyField := "target.arm_on_x86"
-			armOnX86Properties := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField)
-			result = append(result, armOnX86Properties)
+			if armOnX86Properties, ok := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField); ok {
+				result = append(result, armOnX86Properties)
+			}
 		}
 		if arch.ArchType == X86_64 && (hasArmAbi(arch) ||
 			hasArmAndroidArch(ctx.Config().Targets[Android])) {
 			field := "Arm_on_x86_64"
 			userFriendlyField := "target.arm_on_x86_64"
-			armOnX8664Properties := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField)
-			result = append(result, armOnX8664Properties)
+			if armOnX8664Properties, ok := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField); ok {
+				result = append(result, armOnX8664Properties)
+			}
 		}
 		if os == Android && nativeBridgeEnabled {
 			userFriendlyField := "Native_bridge"
 			prefix := "target.native_bridge"
-			nativeBridgeProperties := getChildPropertyStruct(ctx, targetProp, userFriendlyField, prefix)
-			result = append(result, nativeBridgeProperties)
+			if nativeBridgeProperties, ok := getChildPropertyStruct(ctx, targetProp, userFriendlyField, prefix); ok {
+				result = append(result, nativeBridgeProperties)
+			}
 		}
 	}
 
@@ -1371,6 +1412,34 @@
 	}
 }
 
+// determineBuildOS stores the OS and architecture used for host targets used during the build into
+// config based on the runtime OS and architecture determined by Go and the product configuration.
+func determineBuildOS(config *config) {
+	config.BuildOS = func() OsType {
+		switch runtime.GOOS {
+		case "linux":
+			if Bool(config.productVariables.HostMusl) {
+				return LinuxMusl
+			}
+			return Linux
+		case "darwin":
+			return Darwin
+		default:
+			panic(fmt.Sprintf("unsupported OS: %s", runtime.GOOS))
+		}
+	}()
+
+	config.BuildArch = func() ArchType {
+		switch runtime.GOARCH {
+		case "amd64":
+			return X86_64
+		default:
+			panic(fmt.Sprintf("unsupported Arch: %s", runtime.GOARCH))
+		}
+	}()
+
+}
+
 // Convert the arch product variables into a list of targets for each OsType.
 func decodeTargetProductVariables(config *config) (map[OsType][]Target, error) {
 	variables := config.productVariables
@@ -1404,9 +1473,9 @@
 		hostCross := false
 		if os.Class == Host {
 			var osSupported bool
-			if os == BuildOs {
+			if os == config.BuildOS {
 				osSupported = true
-			} else if BuildOs.Linux() && os.Linux() {
+			} else if config.BuildOS.Linux() && os.Linux() {
 				// LinuxBionic and Linux are compatible
 				osSupported = true
 			} else {
@@ -1444,11 +1513,11 @@
 	}
 
 	// The primary host target, which must always exist.
-	addTarget(BuildOs, *variables.HostArch, nil, nil, nil, NativeBridgeDisabled, nil, nil)
+	addTarget(config.BuildOS, *variables.HostArch, nil, nil, nil, NativeBridgeDisabled, nil, nil)
 
 	// An optional secondary host target.
 	if variables.HostSecondaryArch != nil && *variables.HostSecondaryArch != "" {
-		addTarget(BuildOs, *variables.HostSecondaryArch, nil, nil, nil, NativeBridgeDisabled, nil, nil)
+		addTarget(config.BuildOS, *variables.HostSecondaryArch, nil, nil, nil, NativeBridgeDisabled, nil, nil)
 	}
 
 	// Optional cross-compiled host targets, generally Windows.
@@ -1473,13 +1542,8 @@
 
 	// Optional device targets
 	if variables.DeviceArch != nil && *variables.DeviceArch != "" {
-		var target = Android
-		if Bool(variables.Fuchsia) {
-			target = Fuchsia
-		}
-
 		// The primary device target.
-		addTarget(target, *variables.DeviceArch, variables.DeviceArchVariant,
+		addTarget(Android, *variables.DeviceArch, variables.DeviceArchVariant,
 			variables.DeviceCpuVariant, variables.DeviceAbi, NativeBridgeDisabled, nil, nil)
 
 		// An optional secondary device target.
@@ -1540,23 +1604,26 @@
 	abi         []string
 }
 
-// getNdkAbisConfig returns a list of archConfigs for the ABIs supported by the NDK.
+// getNdkAbisConfig returns the list of archConfigs that are used for bulding
+// the API stubs and static libraries that are included in the NDK. These are
+// built *without Neon*, because non-Neon is still supported and building these
+// with Neon will break those users.
 func getNdkAbisConfig() []archConfig {
 	return []archConfig{
-		{"arm", "armv7-a", "", []string{"armeabi-v7a"}},
 		{"arm64", "armv8-a-branchprot", "", []string{"arm64-v8a"}},
-		{"x86", "", "", []string{"x86"}},
+		{"arm", "armv7-a", "", []string{"armeabi-v7a"}},
 		{"x86_64", "", "", []string{"x86_64"}},
+		{"x86", "", "", []string{"x86"}},
 	}
 }
 
 // getAmlAbisConfig returns a list of archConfigs for the ABIs supported by mainline modules.
 func getAmlAbisConfig() []archConfig {
 	return []archConfig{
-		{"arm", "armv7-a-neon", "", []string{"armeabi-v7a"}},
 		{"arm64", "armv8-a", "", []string{"arm64-v8a"}},
-		{"x86", "", "", []string{"x86"}},
+		{"arm", "armv7-a-neon", "", []string{"armeabi-v7a"}},
 		{"x86_64", "", "", []string{"x86_64"}},
+		{"x86", "", "", []string{"x86"}},
 	}
 }
 
@@ -1857,25 +1924,38 @@
 	PropertyErrorf(property, fmt string, args ...interface{})
 }
 
-// GetArchProperties returns a map of architectures to the values of the
-// properties of the 'propertySet' struct that are specific to that architecture.
+// ArchVariantProperties represents a map of arch-variant config strings to a property interface{}.
+type ArchVariantProperties map[string]interface{}
+
+// ConfigurationAxisToArchVariantProperties represents a map of bazel.ConfigurationAxis to
+// ArchVariantProperties, such that each independent arch-variant axis maps to the
+// configs/properties for that axis.
+type ConfigurationAxisToArchVariantProperties map[bazel.ConfigurationAxis]ArchVariantProperties
+
+// GetArchVariantProperties returns a ConfigurationAxisToArchVariantProperties where the
+// arch-variant properties correspond to the values of the properties of the 'propertySet' struct
+// that are specific to that axis/configuration. Each axis is independent, containing
+// non-overlapping configs that correspond to the various "arch-variant" support, at this time:
+//    arches (including multilib)
+//    oses
+//    arch+os combinations
 //
-// For example, passing a struct { Foo bool, Bar string } will return an
-// interface{} that can be type asserted back into the same struct, containing
-// the arch specific property value specified by the module if defined.
+// For example, passing a struct { Foo bool, Bar string } will return an interface{} that can be
+// type asserted back into the same struct, containing the config-specific property value specified
+// by the module if defined.
 //
 // Arch-specific properties may come from an arch stanza or a multilib stanza; properties
 // in these stanzas are combined.
 // For example: `arch: { x86: { Foo: ["bar"] } }, multilib: { lib32: {` Foo: ["baz"] } }`
 // will result in `Foo: ["bar", "baz"]` being returned for architecture x86, if the given
 // propertyset contains `Foo []string`.
-func (m *ModuleBase) GetArchProperties(ctx ArchVariantContext, propertySet interface{}) map[ArchType]interface{} {
+func (m *ModuleBase) GetArchVariantProperties(ctx ArchVariantContext, propertySet interface{}) ConfigurationAxisToArchVariantProperties {
 	// Return value of the arch types to the prop values for that arch.
-	archToProp := map[ArchType]interface{}{}
+	axisToProps := ConfigurationAxisToArchVariantProperties{}
 
 	// Nothing to do for non-arch-specific modules.
 	if !m.ArchSpecific() {
-		return archToProp
+		return axisToProps
 	}
 
 	dstType := reflect.ValueOf(propertySet).Type()
@@ -1887,15 +1967,17 @@
 		srcType := reflect.ValueOf(generalProp).Type()
 		if srcType == dstType {
 			archProperties = m.archProperties[i]
+			axisToProps[bazel.NoConfigAxis] = ArchVariantProperties{"": generalProp}
 			break
 		}
 	}
 
 	if archProperties == nil {
 		// This module does not have the property set requested
-		return archToProp
+		return axisToProps
 	}
 
+	archToProp := ArchVariantProperties{}
 	// For each arch type (x86, arm64, etc.)
 	for _, arch := range ArchTypeList() {
 		// Arch properties are sometimes sharded (see createArchPropTypeDesc() ).
@@ -1903,9 +1985,14 @@
 		// input one that contains the data specific to that arch.
 		propertyStructs := make([]reflect.Value, 0)
 		for _, archProperty := range archProperties {
-			archTypeStruct := getArchTypeStruct(ctx, archProperty, arch)
-			multilibStruct := getMultilibStruct(ctx, archProperty, arch)
-			propertyStructs = append(propertyStructs, archTypeStruct, multilibStruct)
+			archTypeStruct, ok := getArchTypeStruct(ctx, archProperty, arch)
+			if ok {
+				propertyStructs = append(propertyStructs, archTypeStruct)
+			}
+			multilibStruct, ok := getMultilibStruct(ctx, archProperty, arch)
+			if ok {
+				propertyStructs = append(propertyStructs, multilibStruct)
+			}
 		}
 
 		// Create a new instance of the requested property set
@@ -1916,95 +2003,68 @@
 			mergePropertyStruct(ctx, value, propertyStruct)
 		}
 
-		archToProp[arch] = value
+		archToProp[arch.Name] = value
 	}
+	axisToProps[bazel.ArchConfigurationAxis] = archToProp
 
-	return archToProp
-}
-
-// GetTargetProperties returns a map of OS target (e.g. android, windows) to the
-// values of the properties of the 'dst' struct that are specific to that OS
-// target.
-//
-// For example, passing a struct { Foo bool, Bar string } will return an
-// interface{} that can be type asserted back into the same struct, containing
-// the os-specific property value specified by the module if defined.
-//
-// While this looks similar to GetArchProperties, the internal representation of
-// the properties have a slightly different layout to warrant a standalone
-// lookup function.
-func (m *ModuleBase) GetTargetProperties(dst interface{}) map[OsType]interface{} {
-	// Return value of the arch types to the prop values for that arch.
-	osToProp := map[OsType]interface{}{}
-
-	// Nothing to do for non-OS/arch-specific modules.
-	if !m.ArchSpecific() {
-		return osToProp
-	}
-
-	// archProperties has the type of [][]interface{}. Looks complicated, so
-	// let's explain this step by step.
-	//
-	// Loop over the outer index, which determines the property struct that
-	// contains a matching set of properties in dst that we're interested in.
-	// For example, BaseCompilerProperties or BaseLinkerProperties.
-	for i := range m.archProperties {
-		if m.archProperties[i] == nil {
+	osToProp := ArchVariantProperties{}
+	archOsToProp := ArchVariantProperties{}
+	// For android, linux, ...
+	for _, os := range osTypeList {
+		if os == CommonOS {
+			// It looks like this OS value is not used in Blueprint files
 			continue
 		}
-
-		// Iterate over the supported OS types
-		for _, os := range osTypeList {
-			// e.g android, linux_bionic
-			field := os.Field
-
-			// If it's not nil, loop over the inner index, which determines the arch variant
-			// of the prop type. In an Android.bp file, this is like looping over:
-			//
-			// target: { android: { key: value, ... }, linux_bionic: { key: value, ... } }
-			for _, archProperties := range m.archProperties[i] {
-				archPropValues := reflect.ValueOf(archProperties).Elem()
-
-				// This is the archPropRoot struct. Traverse into the Targetnested struct.
-				src := archPropValues.FieldByName("Target").Elem()
-
-				// Step into non-nil pointers to structs in the src value.
-				if src.Kind() == reflect.Ptr {
-					if src.IsNil() {
-						continue
-					}
-					src = src.Elem()
-				}
-
-				// Find the requested field (e.g. android, linux_bionic) in the src struct.
-				src = src.FieldByName(field)
-
-				// Validation steps. We want valid non-nil pointers to structs.
-				if !src.IsValid() || src.IsNil() {
-					continue
-				}
-
-				if src.Kind() != reflect.Ptr || src.Elem().Kind() != reflect.Struct {
-					continue
-				}
-
-				// Clone the destination prop, since we want a unique prop struct per arch.
-				dstClone := reflect.New(reflect.ValueOf(dst).Elem().Type()).Interface()
-
-				// Copy the located property struct into the cloned destination property struct.
-				err := proptools.ExtendMatchingProperties([]interface{}{dstClone}, src.Interface(), nil, proptools.OrderReplace)
-				if err != nil {
-					// This is fine, it just means the src struct doesn't match.
-					continue
-				}
-
-				// Found the prop for the os, you have.
-				osToProp[os] = dstClone
-
-				// Go to the next prop.
-				break
-			}
+		osToProp[os.Name] = getTargetStruct(ctx, propertySet, archProperties, os.Field)
+		// For arm, x86, ...
+		for _, arch := range osArchTypeMap[os] {
+			targetField := GetCompoundTargetField(os, arch)
+			targetName := fmt.Sprintf("%s_%s", os.Name, arch.Name)
+			archOsToProp[targetName] = getTargetStruct(ctx, propertySet, archProperties, targetField)
 		}
 	}
-	return osToProp
+	axisToProps[bazel.OsConfigurationAxis] = osToProp
+	axisToProps[bazel.OsArchConfigurationAxis] = archOsToProp
+
+	axisToProps[bazel.BionicConfigurationAxis] = map[string]interface{}{
+		"bionic": getTargetStruct(ctx, propertySet, archProperties, "Bionic"),
+	}
+
+	return axisToProps
+}
+
+// Returns a struct matching the propertySet interface, containing properties specific to the targetName
+// For example, given these arguments:
+//    propertySet = BaseCompilerProperties
+//    targetName = "android_arm"
+// And given this Android.bp fragment:
+//    target:
+//       android_arm: {
+//          srcs: ["foo.c"],
+//       }
+//       android_arm64: {
+//          srcs: ["bar.c"],
+//      }
+//    }
+// This would return a BaseCompilerProperties with BaseCompilerProperties.Srcs = ["foo.c"]
+func getTargetStruct(ctx ArchVariantContext, propertySet interface{}, archProperties []interface{}, targetName string) interface{} {
+	propertyStructs := make([]reflect.Value, 0)
+	for _, archProperty := range archProperties {
+		archPropValues := reflect.ValueOf(archProperty).Elem()
+		targetProp := archPropValues.FieldByName("Target").Elem()
+		targetStruct, ok := getChildPropertyStruct(ctx, targetProp, targetName, targetName)
+		if ok {
+			propertyStructs = append(propertyStructs, targetStruct)
+		}
+	}
+
+	// Create a new instance of the requested property set
+	value := reflect.New(reflect.ValueOf(propertySet).Elem().Type()).Interface()
+
+	// Merge all the structs together
+	for _, propertyStruct := range propertyStructs {
+		mergePropertyStruct(ctx, value, propertyStruct)
+	}
+
+	return value
 }
diff --git a/android/arch_test.go b/android/arch_test.go
index 3aa4779..a828321 100644
--- a/android/arch_test.go
+++ b/android/arch_test.go
@@ -473,3 +473,186 @@
 		})
 	}
 }
+
+type testArchPropertiesModule struct {
+	ModuleBase
+	properties struct {
+		A []string `android:"arch_variant"`
+	}
+}
+
+func (testArchPropertiesModule) GenerateAndroidBuildActions(ctx ModuleContext) {}
+
+func TestArchProperties(t *testing.T) {
+	bp := `
+		module {
+			name: "foo",
+			a: ["root"],
+			arch: {
+				arm: {
+					a:  ["arm"],
+					armv7_a_neon: { a: ["armv7_a_neon"] },
+				},
+				arm64: {
+					a:  ["arm64"],
+					armv8_a: { a: ["armv8_a"] },
+				},
+				x86: { a:  ["x86"] },
+				x86_64: { a:  ["x86_64"] },
+			},
+			multilib: {
+				lib32: { a:  ["lib32"] },
+				lib64: { a:  ["lib64"] },
+			},
+			target: {
+				bionic: { a:  ["bionic"] },
+				host: { a: ["host"] },
+				android: { a:  ["android"] },
+				glibc: { a:  ["glibc"] },
+				musl: { a:  ["musl"] },
+				linux_bionic: { a:  ["linux_bionic"] },
+				linux: { a:  ["linux"] },
+				linux_glibc: { a:  ["linux_glibc"] },
+				linux_musl: { a:  ["linux_musl"] },
+				windows: { a:  ["windows"], enabled: true },
+				darwin: { a:  ["darwin"] },
+				not_windows: { a:  ["not_windows"] },
+				android32: { a:  ["android32"] },
+				android64: { a:  ["android64"] },
+				android_arm: { a:  ["android_arm"] },
+				android_arm64: { a:  ["android_arm64"] },
+				linux_x86: { a:  ["linux_x86"] },
+				linux_x86_64: { a:  ["linux_x86_64"] },
+				linux_glibc_x86: { a:  ["linux_glibc_x86"] },
+				linux_glibc_x86_64: { a:  ["linux_glibc_x86_64"] },
+				linux_musl_x86: { a:  ["linux_musl_x86"] },
+				linux_musl_x86_64: { a:  ["linux_musl_x86_64"] },
+				darwin_x86_64: { a:  ["darwin_x86_64"] },
+				windows_x86: { a:  ["windows_x86"] },
+				windows_x86_64: { a:  ["windows_x86_64"] },
+			},
+		}
+	`
+
+	type result struct {
+		module   string
+		variant  string
+		property []string
+	}
+
+	testCases := []struct {
+		name     string
+		goOS     string
+		preparer FixturePreparer
+		results  []result
+	}{
+		{
+			name: "default",
+			results: []result{
+				{
+					module:   "foo",
+					variant:  "android_arm64_armv8-a",
+					property: []string{"root", "linux", "bionic", "android", "android64", "arm64", "armv8_a", "lib64", "android_arm64"},
+				},
+				{
+					module:   "foo",
+					variant:  "android_arm_armv7-a-neon",
+					property: []string{"root", "linux", "bionic", "android", "android64", "arm", "armv7_a_neon", "lib32", "android_arm"},
+				},
+			},
+		},
+		{
+			name: "linux",
+			goOS: "linux",
+			results: []result{
+				{
+					module:   "foo",
+					variant:  "linux_glibc_x86_64",
+					property: []string{"root", "host", "linux", "glibc", "linux_glibc", "not_windows", "x86_64", "lib64", "linux_x86_64", "linux_glibc_x86_64"},
+				},
+				{
+					module:   "foo",
+					variant:  "linux_glibc_x86",
+					property: []string{"root", "host", "linux", "glibc", "linux_glibc", "not_windows", "x86", "lib32", "linux_x86", "linux_glibc_x86"},
+				},
+			},
+		},
+		{
+			name: "windows",
+			goOS: "linux",
+			preparer: FixtureModifyConfig(func(config Config) {
+				config.Targets[Windows] = []Target{
+					{Windows, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", "", true},
+					{Windows, Arch{ArchType: X86}, NativeBridgeDisabled, "", "", true},
+				}
+			}),
+			results: []result{
+				{
+					module:   "foo",
+					variant:  "windows_x86_64",
+					property: []string{"root", "host", "windows", "x86_64", "lib64", "windows_x86_64"},
+				},
+				{
+					module:   "foo",
+					variant:  "windows_x86",
+					property: []string{"root", "host", "windows", "x86", "lib32", "windows_x86"},
+				},
+			},
+		},
+		{
+			name:     "linux_musl",
+			goOS:     "linux",
+			preparer: FixtureModifyConfig(modifyTestConfigForMusl),
+			results: []result{
+				{
+					module:   "foo",
+					variant:  "linux_musl_x86_64",
+					property: []string{"root", "host", "linux", "musl", "linux_glibc", "linux_musl", "not_windows", "x86_64", "lib64", "linux_x86_64", "linux_musl_x86_64", "linux_glibc_x86_64"},
+				},
+				{
+					module:   "foo",
+					variant:  "linux_musl_x86",
+					property: []string{"root", "host", "linux", "musl", "linux_glibc", "linux_musl", "not_windows", "x86", "lib32", "linux_x86", "linux_musl_x86", "linux_glibc_x86"},
+				},
+			},
+		},
+		{
+			name: "darwin",
+			goOS: "darwin",
+			results: []result{
+				{
+					module:   "foo",
+					variant:  "darwin_x86_64",
+					property: []string{"root", "host", "darwin", "not_windows", "x86_64", "lib64", "darwin_x86_64"},
+				},
+			},
+		},
+	}
+
+	for _, tt := range testCases {
+		t.Run(tt.name, func(t *testing.T) {
+			if tt.goOS != "" && tt.goOS != runtime.GOOS {
+				t.Skipf("test requires runtime.GOOS==%s, got %s", tt.goOS, runtime.GOOS)
+			}
+			result := GroupFixturePreparers(
+				PrepareForTestWithArchMutator,
+				OptionalFixturePreparer(tt.preparer),
+				FixtureRegisterWithContext(func(ctx RegistrationContext) {
+					ctx.RegisterModuleType("module", func() Module {
+						module := &testArchPropertiesModule{}
+						module.AddProperties(&module.properties)
+						InitAndroidArchModule(module, HostAndDeviceDefault, MultilibBoth)
+						return module
+					})
+				}),
+			).RunTestWithBp(t, bp)
+
+			for _, want := range tt.results {
+				t.Run(want.module+"_"+want.variant, func(t *testing.T) {
+					got := result.ModuleForTests(want.module, want.variant).Module().(*testArchPropertiesModule).properties.A
+					AssertArrayString(t, "arch mutator property", want.property, got)
+				})
+			}
+		})
+	}
+}
diff --git a/android/bazel.go b/android/bazel.go
index 9621f3e..bebb61b 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -126,67 +126,61 @@
 )
 
 var (
-	// Do not write BUILD files for these directories
-	// NOTE: this is not recursive
-	bp2buildDoNotWriteBuildFileList = []string{
-		// Don't generate these BUILD files - because external BUILD files already exist
-		"external/boringssl",
-		"external/brotli",
-		"external/dagger2",
-		"external/flatbuffers",
-		"external/gflags",
-		"external/google-fruit",
-		"external/grpc-grpc",
-		"external/grpc-grpc/test/core/util",
-		"external/grpc-grpc/test/cpp/common",
-		"external/grpc-grpc/third_party/address_sorting",
-		"external/nanopb-c",
-		"external/nos/host/generic",
-		"external/nos/host/generic/libnos",
-		"external/nos/host/generic/libnos/generator",
-		"external/nos/host/generic/libnos_datagram",
-		"external/nos/host/generic/libnos_transport",
-		"external/nos/host/generic/nugget/proto",
-		"external/perfetto",
-		"external/protobuf",
-		"external/rust/cxx",
-		"external/rust/cxx/demo",
-		"external/ruy",
-		"external/tensorflow",
-		"external/tensorflow/tensorflow/lite",
-		"external/tensorflow/tensorflow/lite/java",
-		"external/tensorflow/tensorflow/lite/kernels",
-		"external/tflite-support",
-		"external/tinyalsa_new",
-		"external/wycheproof",
-		"external/libyuv",
+	// Keep any existing BUILD files (and do not generate new BUILD files) for these directories
+	// in the synthetic Bazel workspace.
+	bp2buildKeepExistingBuildFile = map[string]bool{
+		// This is actually build/bazel/build.BAZEL symlinked to ./BUILD
+		".":/*recursive = */ false,
+
+		// build/bazel/examples/apex/... BUILD files should be generated, so
+		// build/bazel is not recursive. Instead list each subdirectory under
+		// build/bazel explicitly.
+		"build/bazel":/* recursive = */ false,
+		"build/bazel/examples/android_app":/* recursive = */ true,
+		"build/bazel/examples/java":/* recursive = */ true,
+		"build/bazel/bazel_skylib":/* recursive = */ true,
+		"build/bazel/rules":/* recursive = */ true,
+		"build/bazel/rules_cc":/* recursive = */ true,
+		"build/bazel/tests":/* recursive = */ true,
+		"build/bazel/platforms":/* recursive = */ true,
+		"build/bazel/product_variables":/* recursive = */ true,
+		"build/bazel_common_rules":/* recursive = */ true,
+		"build/pesto":/* recursive = */ true,
+
+		// external/bazelbuild-rules_android/... is needed by mixed builds, otherwise mixed builds analysis fails
+		// e.g. ERROR: Analysis of target '@soong_injection//mixed_builds:buildroot' failed
+		"external/bazelbuild-rules_android":/* recursive = */ true,
+		"external/bazel-skylib":/* recursive = */ true,
+
+		"prebuilts/sdk":/* recursive = */ false,
+		"prebuilts/sdk/tools":/* recursive = */ false,
+		"prebuilts/r8":/* recursive = */ false,
+		"packages/apps/Music":/* recursive = */ true,
 	}
 
 	// Configure modules in these directories to enable bp2build_available: true or false by default.
 	bp2buildDefaultConfig = Bp2BuildConfig{
-		"bionic":                Bp2BuildDefaultTrueRecursively,
-		"external/gwp_asan":     Bp2BuildDefaultTrueRecursively,
-		"system/core/libcutils": Bp2BuildDefaultTrueRecursively,
+		"bionic":                            Bp2BuildDefaultTrueRecursively,
+		"build/bazel/examples/apex/minimal": Bp2BuildDefaultTrueRecursively,
+		"development/sdk":                   Bp2BuildDefaultTrueRecursively,
+		"external/gwp_asan":                 Bp2BuildDefaultTrueRecursively,
+		"system/core/libcutils":             Bp2BuildDefaultTrueRecursively,
 		"system/core/property_service/libpropertyinfoparser": Bp2BuildDefaultTrueRecursively,
 		"system/libbase":                  Bp2BuildDefaultTrueRecursively,
 		"system/logging/liblog":           Bp2BuildDefaultTrueRecursively,
-		"external/jemalloc_new":           Bp2BuildDefaultTrueRecursively,
-		"external/fmtlib":                 Bp2BuildDefaultTrueRecursively,
+		"system/timezone/apex":            Bp2BuildDefaultTrueRecursively,
+		"system/timezone/output_data":     Bp2BuildDefaultTrueRecursively,
 		"external/arm-optimized-routines": Bp2BuildDefaultTrueRecursively,
+		"external/fmtlib":                 Bp2BuildDefaultTrueRecursively,
+		"external/jemalloc_new":           Bp2BuildDefaultTrueRecursively,
+		"external/libcxxabi":              Bp2BuildDefaultTrueRecursively,
 		"external/scudo":                  Bp2BuildDefaultTrueRecursively,
+		"prebuilts/clang/host/linux-x86":  Bp2BuildDefaultTrueRecursively,
 	}
 
 	// Per-module denylist to always opt modules out of both bp2build and mixed builds.
 	bp2buildModuleDoNotConvertList = []string{
 		// Things that transitively depend on unconverted libc_* modules.
-		"libc_nopthread", // http://b/186821550, cc_library_static, depends on //bionic/libc:libc_bionic_ndk (http://b/186822256)
-		//                                                     also depends on //bionic/libc:libc_tzcode (http://b/186822591)
-		//                                                     also depends on //bionic/libc:libstdc++ (http://b/186822597)
-		"libc_common",        // http://b/186821517, cc_library_static, depends on //bionic/libc:libc_nopthread (http://b/186821550)
-		"libc_common_static", // http://b/186824119, cc_library_static, depends on //bionic/libc:libc_common (http://b/186821517)
-		"libc_common_shared", // http://b/186824118, cc_library_static, depends on //bionic/libc:libc_common (http://b/186821517)
-		"libc_nomalloc",      // http://b/186825031, cc_library_static, depends on //bionic/libc:libc_common (http://b/186821517)
-
 		"libbionic_spawn_benchmark", // http://b/186824595, cc_library_static, depends on //external/google-benchmark (http://b/186822740)
 		//                                                                also depends on //system/logging/liblog:liblog (http://b/186822772)
 
@@ -205,12 +199,8 @@
 		"liblinker_malloc", // http://b/186826466, cc_library_static, depends on //external/zlib:libz (http://b/186823782)
 		//                                                       also depends on //system/libziparchive:libziparchive (http://b/186823656)
 		//                                                       also depends on //system/logging/liblog:liblog (http://b/186822772)
-		"libc_jemalloc_wrapper", // http://b/187012490, cc_library_static, depends on //external/jemalloc_new:libjemalloc5 (http://b/186828626)
-		"libc_ndk",              // http://b/187013218, cc_library_static, depends on //bionic/libm:libm (http://b/183064661)
-		"libc",                  // http://b/183064430, cc_library, depends on //external/jemalloc_new:libjemalloc5 (http://b/186828626)
-		"libc_bionic_ndk",       // http://b/186822256, cc_library_static, fatal error: 'generated_android_ids.h' file not found
-		"libc_malloc_hooks",     // http://b/187016307, cc_library, ld.lld: error: undefined symbol: __malloc_hook
-		"libm",                  // http://b/183064661, cc_library, math.h:25:16: error: unexpected token in argument list
+		"libc_ndk",          // http://b/187013218, cc_library_static, depends on //bionic/libm:libm (http://b/183064661)
+		"libc_malloc_hooks", // http://b/187016307, cc_library, ld.lld: error: undefined symbol: __malloc_hook
 
 		// http://b/186823769: Needs C++ STL support, includes from unconverted standard libraries in //external/libcxx
 		// c++_static
@@ -218,53 +208,46 @@
 		// libcxx
 		"libBionicBenchmarksUtils", // cc_library_static, fatal error: 'map' file not found, from libcxx
 		"fmtlib",                   // cc_library_static, fatal error: 'cassert' file not found, from libcxx
-		"libbase",                  // http://b/186826479, cc_library, fatal error: 'memory' file not found, from libcxx
+		"fmtlib_ndk",               // cc_library_static, fatal error: 'cassert' file not found
+		"liblog",                   // http://b/186822772: cc_library, 'sys/cdefs.h' file not found
+		"libbase",                  // Requires liblog. http://b/186826479, cc_library, fatal error: 'memory' file not found, from libcxx.
+		// Also depends on fmtlib.
 
-		// http://b/186024507: Includes errors because of the system_shared_libs default value.
-		// Missing -isystem bionic/libc/include through the libc/libm/libdl
-		// default dependencies if system_shared_libs is unset.
-		"liblog",                 // http://b/186822772: cc_library, 'sys/cdefs.h' file not found
-		"libjemalloc5_jet",       // cc_library, 'sys/cdefs.h' file not found
-		"libseccomp_policy",      // http://b/186476753: cc_library, 'linux/filter.h' not found
-		"note_memtag_heap_async", // http://b/185127353: cc_library_static, error: feature.h not found
-		"note_memtag_heap_sync",  // http://b/185127353: cc_library_static, error: feature.h not found
+		"libseccomp_policy", // depends on libbase
+
+		"gwp_asan_crash_handler", // cc_library, ld.lld: error: undefined symbol: memset
 
 		// Tests. Handle later.
 		"libbionic_tests_headers_posix", // http://b/186024507, cc_library_static, sched.h, time.h not found
 		"libjemalloc5_integrationtest",
 		"libjemalloc5_stresstestlib",
 		"libjemalloc5_unittest",
+
+		// APEX support
+		"com.android.runtime", // http://b/194746715, apex, depends on 'libc_malloc_debug' and 'libc_malloc_hooks'
 	}
 
 	// Per-module denylist of cc_library modules to only generate the static
 	// variant if their shared variant isn't ready or buildable by Bazel.
 	bp2buildCcLibraryStaticOnlyList = []string{
-		"libstdc++", // http://b/186822597, cc_library, ld.lld: error: undefined symbol: __errno
+		"libstdc++",    // http://b/186822597, cc_library, ld.lld: error: undefined symbol: __errno
+		"libjemalloc5", // http://b/188503688, cc_library, `target: { android: { enabled: false } }` for android targets.
 	}
 
 	// Per-module denylist to opt modules out of mixed builds. Such modules will
 	// still be generated via bp2build.
 	mixedBuildsDisabledList = []string{
-		"libc_netbsd",                      // lberki@, cc_library_static, version script assignment of 'LIBC_PRIVATE' to symbol 'SHA1Final' failed: symbol not defined
-		"libc_openbsd",                     // ruperts@, cc_library_static, OK for bp2build but error: duplicate symbol: strcpy for mixed builds
-		"libsystemproperties",              // cparsons@, cc_library_static, wrong include paths
-		"libpropertyinfoparser",            // cparsons@, cc_library_static, wrong include paths
-		"libarm-optimized-routines-string", // jingwen@, cc_library_static, OK for bp2build but b/186615213 (asflags not handled in  bp2build), version script assignment of 'LIBC' to symbol 'memcmp' failed: symbol not defined (also for memrchr, strnlen)
-		"fmtlib_ndk",                       // http://b/187040371, cc_library_static, OK for bp2build but format-inl.h:11:10: fatal error: 'cassert' file not found for mixed builds
+		"libc++abi",      // http://b/195970501, cc_library_static, duplicate symbols because it propagates libc objects.
+		"libc++demangle", // http://b/195970501, cc_library_static, duplicate symbols because it propagates libc objects.
 	}
 
 	// Used for quicker lookups
-	bp2buildDoNotWriteBuildFile = map[string]bool{}
 	bp2buildModuleDoNotConvert  = map[string]bool{}
 	bp2buildCcLibraryStaticOnly = map[string]bool{}
 	mixedBuildsDisabled         = map[string]bool{}
 )
 
 func init() {
-	for _, moduleName := range bp2buildDoNotWriteBuildFileList {
-		bp2buildDoNotWriteBuildFile[moduleName] = true
-	}
-
 	for _, moduleName := range bp2buildModuleDoNotConvertList {
 		bp2buildModuleDoNotConvert[moduleName] = true
 	}
@@ -282,12 +265,21 @@
 	return bp2buildCcLibraryStaticOnly[ctx.Module().Name()]
 }
 
-func ShouldWriteBuildFileForDir(dir string) bool {
-	if _, ok := bp2buildDoNotWriteBuildFile[dir]; ok {
-		return false
-	} else {
+func ShouldKeepExistingBuildFileForDir(dir string) bool {
+	if _, ok := bp2buildKeepExistingBuildFile[dir]; ok {
+		// Exact dir match
 		return true
 	}
+	// Check if subtree match
+	for prefix, recursive := range bp2buildKeepExistingBuildFile {
+		if recursive {
+			if strings.HasPrefix(dir, prefix+"/") {
+				return true
+			}
+		}
+	}
+	// Default
+	return false
 }
 
 // MixedBuildsEnabled checks that a module is ready to be replaced by a
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index 8cddbb2..9c922dd 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -28,8 +28,6 @@
 
 	"android/soong/bazel/cquery"
 
-	"github.com/google/blueprint/bootstrap"
-
 	"android/soong/bazel"
 	"android/soong/shared"
 )
@@ -57,6 +55,17 @@
 	archType    ArchType
 }
 
+// bazelHandler is the interface for a helper object related to deferring to Bazel for
+// processing a module (during Bazel mixed builds). Individual module types should define
+// their own bazel handler if they support deferring to Bazel.
+type BazelHandler interface {
+	// Issue query to Bazel to retrieve information about Bazel's view of the current module.
+	// If Bazel returns this information, set module properties on the current module to reflect
+	// the returned information.
+	// Returns true if information was available from Bazel, false if bazel invocation still needs to occur.
+	GenerateBazelBuildActions(ctx ModuleContext, label string) bool
+}
+
 type BazelContext interface {
 	// The below methods involve queuing cquery requests to be later invoked
 	// by bazel. If any of these methods return (_, false), then the request
@@ -69,6 +78,9 @@
 	// Returns the results of GetOutputFiles and GetCcObjectFiles in a single query (in that order).
 	GetCcInfo(label string, archType ArchType) (cquery.CcInfo, bool, error)
 
+	// Returns the executable binary resultant from building together the python sources
+	GetPythonBinary(label string, archType ArchType) (string, bool)
+
 	// ** End cquery methods
 
 	// Issues commands to Bazel to receive results for all cquery requests
@@ -94,7 +106,7 @@
 	bazelPath    string
 	outputBase   string
 	workspaceDir string
-	buildDir     string
+	soongOutDir  string
 	metricsDir   string
 }
 
@@ -123,8 +135,9 @@
 type MockBazelContext struct {
 	OutputBaseDir string
 
-	LabelToOutputFiles map[string][]string
-	LabelToCcInfo      map[string]cquery.CcInfo
+	LabelToOutputFiles  map[string][]string
+	LabelToCcInfo       map[string]cquery.CcInfo
+	LabelToPythonBinary map[string]string
 }
 
 func (m MockBazelContext) GetOutputFiles(label string, archType ArchType) ([]string, bool) {
@@ -137,6 +150,11 @@
 	return result, ok, nil
 }
 
+func (m MockBazelContext) GetPythonBinary(label string, archType ArchType) (string, bool) {
+	result, ok := m.LabelToPythonBinary[label]
+	return result, ok
+}
+
 func (m MockBazelContext) InvokeBazel() error {
 	panic("unimplemented")
 }
@@ -174,6 +192,16 @@
 	return ret, ok, err
 }
 
+func (bazelCtx *bazelContext) GetPythonBinary(label string, archType ArchType) (string, bool) {
+	rawString, ok := bazelCtx.cquery(label, cquery.GetPythonBinary, archType)
+	var ret string
+	if ok {
+		bazelOutput := strings.TrimSpace(rawString)
+		ret = cquery.GetPythonBinary.ParseResult(bazelOutput)
+	}
+	return ret, ok
+}
+
 func (n noopBazelContext) GetOutputFiles(label string, archType ArchType) ([]string, bool) {
 	panic("unimplemented")
 }
@@ -182,6 +210,10 @@
 	panic("unimplemented")
 }
 
+func (n noopBazelContext) GetPythonBinary(label string, archType ArchType) (string, bool) {
+	panic("unimplemented")
+}
+
 func (n noopBazelContext) GetPrebuiltCcStaticLibraryFiles(label string, archType ArchType) ([]string, bool) {
 	panic("unimplemented")
 }
@@ -205,7 +237,7 @@
 func NewBazelContext(c *config) (BazelContext, error) {
 	// TODO(cparsons): Assess USE_BAZEL=1 instead once "mixed Soong/Bazel builds"
 	// are production ready.
-	if c.Getenv("USE_BAZEL_ANALYSIS") != "1" {
+	if !c.IsEnvTrue("USE_BAZEL_ANALYSIS") {
 		return noopBazelContext{}, nil
 	}
 
@@ -222,7 +254,7 @@
 
 func bazelPathsFromConfig(c *config) (*bazelPaths, error) {
 	p := bazelPaths{
-		buildDir: c.buildDir,
+		soongOutDir: c.soongOutDir,
 	}
 	missingEnvVars := []string{}
 	if len(c.Getenv("BAZEL_HOME")) > 1 {
@@ -321,7 +353,16 @@
 // the invocation returned an error code.
 func (r *builtinBazelRunner) issueBazelCommand(paths *bazelPaths, runName bazel.RunName, command bazelCommand,
 	extraFlags ...string) (string, string, error) {
-	cmdFlags := []string{"--output_base=" + absolutePath(paths.outputBase), command.command}
+	cmdFlags := []string{
+		// --noautodetect_server_javabase has the practical consequence of preventing Bazel from
+		// attempting to download rules_java, which is incompatible with
+		// --experimental_repository_disable_download set further below.
+		// rules_java is also not needed until mixed builds start building java targets.
+		// TODO(b/197958133): Once rules_java is pulled into AOSP, remove this flag.
+		"--noautodetect_server_javabase",
+		"--output_base=" + absolutePath(paths.outputBase),
+		command.command,
+	}
 	cmdFlags = append(cmdFlags, command.expression)
 	cmdFlags = append(cmdFlags, "--profile="+shared.BazelMetricsFilename(paths, runName))
 
@@ -333,7 +374,7 @@
 	// The actual platform values here may be overridden by configuration
 	// transitions from the buildroot.
 	cmdFlags = append(cmdFlags,
-		fmt.Sprintf("--platforms=%s", "//build/bazel/platforms:android_x86_64"))
+		fmt.Sprintf("--platforms=%s", "//build/bazel/platforms:android_target"))
 	cmdFlags = append(cmdFlags,
 		fmt.Sprintf("--extra_toolchains=%s", "//prebuilts/clang/host/linux-x86:all"))
 	// This should be parameterized on the host OS, but let's restrict to linux
@@ -350,7 +391,10 @@
 	bazelCmd.Env = append(os.Environ(),
 		"HOME="+paths.homeDir,
 		pwdPrefix(),
-		"BUILD_DIR="+absolutePath(paths.buildDir),
+		"BUILD_DIR="+absolutePath(paths.soongOutDir),
+		// Make OUT_DIR absolute here so tools/bazel.sh uses the correct
+		// OUT_DIR at <root>/out, instead of <root>/out/soong/workspace/out.
+		"OUT_DIR="+absolutePath(paths.outDir()),
 		// Disables local host detection of gcc; toolchain information is defined
 		// explicitly in BUILD files.
 		"BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1")
@@ -539,10 +583,13 @@
   platform_name = build_options(target)["//command_line_option:platforms"][0].name
   if platform_name == "host":
     return "HOST"
-  elif not platform_name.startswith("android_"):
-    fail("expected platform name of the form 'android_<arch>', but was " + str(platforms))
+  elif platform_name.startswith("android_"):
+    return platform_name[len("android_"):]
+  elif platform_name.startswith("linux_"):
+    return platform_name[len("linux_"):]
+  else:
+    fail("expected platform name of the form 'android_<arch>' or 'linux_<arch>', but was " + str(platforms))
     return "UNKNOWN"
-  return platform_name[len("android_"):]
 
 def format(target):
   id_string = str(target.label) + "|" + get_arch(target)
@@ -561,19 +608,24 @@
 // Returns a path containing build-related metadata required for interfacing
 // with Bazel. Example: out/soong/bazel.
 func (p *bazelPaths) intermediatesDir() string {
-	return filepath.Join(p.buildDir, "bazel")
+	return filepath.Join(p.soongOutDir, "bazel")
 }
 
 // Returns the path where the contents of the @soong_injection repository live.
 // It is used by Soong to tell Bazel things it cannot over the command line.
 func (p *bazelPaths) injectedFilesDir() string {
-	return filepath.Join(p.buildDir, "soong_injection")
+	return filepath.Join(p.soongOutDir, bazel.SoongInjectionDirName)
 }
 
 // Returns the path of the synthetic Bazel workspace that contains a symlink
 // forest composed the whole source tree and BUILD files generated by bp2build.
 func (p *bazelPaths) syntheticWorkspaceDir() string {
-	return filepath.Join(p.buildDir, "workspace")
+	return filepath.Join(p.soongOutDir, "workspace")
+}
+
+// Returns the path to the top level out dir ($OUT_DIR).
+func (p *bazelPaths) outDir() string {
+	return filepath.Dir(p.soongOutDir)
 }
 
 // Issues commands to Bazel to receive results for all cquery requests
@@ -715,7 +767,7 @@
 
 	// Add ninja file dependencies for files which all bazel invocations require.
 	bazelBuildList := absolutePath(filepath.Join(
-		filepath.Dir(bootstrap.CmdlineArgs.ModuleListFile), "bazel.list"))
+		filepath.Dir(ctx.Config().moduleListFile), "bazel.list"))
 	ctx.AddNinjaFileDeps(bazelBuildList)
 
 	data, err := ioutil.ReadFile(bazelBuildList)
@@ -734,8 +786,17 @@
 		}
 		rule := NewRuleBuilder(pctx, ctx)
 		cmd := rule.Command()
-		cmd.Text(fmt.Sprintf("cd %s/execroot/__main__ && %s",
-			ctx.Config().BazelContext.OutputBase(), buildStatement.Command))
+
+		// cd into Bazel's execution root, which is the action cwd.
+		cmd.Text(fmt.Sprintf("cd %s/execroot/__main__ && ", ctx.Config().BazelContext.OutputBase()))
+
+		for _, pair := range buildStatement.Env {
+			// Set per-action env variables, if any.
+			cmd.Flag(pair.Key + "=" + pair.Value)
+		}
+
+		// The actual Bazel action.
+		cmd.Text(" " + buildStatement.Command)
 
 		for _, outputPath := range buildStatement.OutputPaths {
 			cmd.ImplicitOutput(PathForBazelOut(ctx, outputPath))
@@ -748,6 +809,10 @@
 			cmd.ImplicitDepFile(PathForBazelOut(ctx, *depfile))
 		}
 
+		for _, symlinkPath := range buildStatement.SymlinkPaths {
+			cmd.ImplicitSymlinkOutput(PathForBazelOut(ctx, symlinkPath))
+		}
+
 		// This is required to silence warnings pertaining to unexpected timestamps. Particularly,
 		// some Bazel builtins (such as files in the bazel_tools directory) have far-future
 		// timestamps. Without restat, Ninja would emit warnings that the input files of a
diff --git a/android/bazel_handler_test.go b/android/bazel_handler_test.go
index f1fabec..557faea 100644
--- a/android/bazel_handler_test.go
+++ b/android/bazel_handler_test.go
@@ -101,7 +101,7 @@
 func testBazelContext(t *testing.T, bazelCommandResults map[bazelCommand]string) (*bazelContext, string) {
 	t.Helper()
 	p := bazelPaths{
-		buildDir:     t.TempDir(),
+		soongOutDir:  t.TempDir(),
 		outputBase:   "outputbase",
 		workspaceDir: "workspace_dir",
 	}
@@ -114,5 +114,5 @@
 		bazelRunner: runner,
 		paths:       &p,
 		requests:    map[cqueryKey]bool{},
-	}, p.buildDir
+	}, p.soongOutDir
 }
diff --git a/android/bazel_paths.go b/android/bazel_paths.go
index f4b2a7c..a4bd2ef 100644
--- a/android/bazel_paths.go
+++ b/android/bazel_paths.go
@@ -73,6 +73,7 @@
 	EarlyModulePathContext
 
 	GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag)
+	ModuleFromName(name string) (blueprint.Module, bool)
 	Module() Module
 	ModuleType() string
 	OtherModuleName(m blueprint.Module) string
@@ -83,14 +84,49 @@
 // or ":<module>") and returns a Bazel-compatible label which corresponds to dependencies on the
 // module within the given ctx.
 func BazelLabelForModuleDeps(ctx BazelConversionPathContext, modules []string) bazel.LabelList {
+	return bazelLabelForModuleDeps(ctx, modules, false)
+}
+
+// BazelLabelForModuleWholeDeps expects a list of references to other modules, ("<module>"
+// or ":<module>") and returns a Bazel-compatible label which corresponds to dependencies on the
+// module within the given ctx, where prebuilt dependencies will be appended with _alwayslink so
+// they can be handled as whole static libraries.
+func BazelLabelForModuleWholeDeps(ctx BazelConversionPathContext, modules []string) bazel.LabelList {
+	return bazelLabelForModuleDeps(ctx, modules, true)
+}
+
+// BazelLabelForModuleDepsExcludes expects two lists: modules (containing modules to include in the
+// list), and excludes (modules to exclude from the list). Both of these should contain references
+// to other modules, ("<module>" or ":<module>"). It returns a Bazel-compatible label list which
+// corresponds to dependencies on the module within the given ctx, and the excluded dependencies.
+func BazelLabelForModuleDepsExcludes(ctx BazelConversionPathContext, modules, excludes []string) bazel.LabelList {
+	return bazelLabelForModuleDepsExcludes(ctx, modules, excludes, false)
+}
+
+// BazelLabelForModuleWholeDepsExcludes expects two lists: modules (containing modules to include in
+// the list), and excludes (modules to exclude from the list). Both of these should contain
+// references to other modules, ("<module>" or ":<module>"). It returns a Bazel-compatible label
+// list which corresponds to dependencies on the module within the given ctx, and the excluded
+// dependencies.  Prebuilt dependencies will be appended with _alwayslink so they can be handled as
+// whole static libraries.
+func BazelLabelForModuleWholeDepsExcludes(ctx BazelConversionPathContext, modules, excludes []string) bazel.LabelList {
+	return bazelLabelForModuleDepsExcludes(ctx, modules, excludes, true)
+}
+
+func bazelLabelForModuleDeps(ctx BazelConversionPathContext, modules []string, isWholeLibs bool) bazel.LabelList {
 	var labels bazel.LabelList
+	// In some cases, a nil string list is different than an explicitly empty list.
+	if len(modules) == 0 && modules != nil {
+		labels.Includes = []bazel.Label{}
+		return labels
+	}
 	for _, module := range modules {
 		bpText := module
 		if m := SrcIsModule(module); m == "" {
 			module = ":" + module
 		}
 		if m, t := SrcIsModuleWithTag(module); m != "" {
-			l := getOtherModuleLabel(ctx, m, t)
+			l := getOtherModuleLabel(ctx, m, t, isWholeLibs)
 			l.OriginalModuleName = bpText
 			labels.Includes = append(labels.Includes, l)
 		} else {
@@ -100,10 +136,26 @@
 	return labels
 }
 
+func bazelLabelForModuleDepsExcludes(ctx BazelConversionPathContext, modules, excludes []string, isWholeLibs bool) bazel.LabelList {
+	moduleLabels := bazelLabelForModuleDeps(ctx, RemoveListFromList(modules, excludes), isWholeLibs)
+	if len(excludes) == 0 {
+		return moduleLabels
+	}
+	excludeLabels := bazelLabelForModuleDeps(ctx, excludes, isWholeLibs)
+	return bazel.LabelList{
+		Includes: moduleLabels.Includes,
+		Excludes: excludeLabels.Includes,
+	}
+}
+
 func BazelLabelForModuleSrcSingle(ctx BazelConversionPathContext, path string) bazel.Label {
 	return BazelLabelForModuleSrcExcludes(ctx, []string{path}, []string(nil)).Includes[0]
 }
 
+func BazelLabelForModuleDepSingle(ctx BazelConversionPathContext, path string) bazel.Label {
+	return BazelLabelForModuleDepsExcludes(ctx, []string{path}, []string(nil)).Includes[0]
+}
+
 // BazelLabelForModuleSrc expects a list of path (relative to local module directory) and module
 // references (":<module>") and returns a bazel.LabelList{} containing the resolved references in
 // paths, relative to the local module, or Bazel-labels (absolute if in a different package or
@@ -257,7 +309,7 @@
 
 	for _, p := range paths {
 		if m, tag := SrcIsModuleWithTag(p); m != "" {
-			l := getOtherModuleLabel(ctx, m, tag)
+			l := getOtherModuleLabel(ctx, m, tag, false)
 			if !InList(l.Label, expandedExcludes) {
 				l.OriginalModuleName = fmt.Sprintf(":%s", m)
 				labels.Includes = append(labels.Includes, l)
@@ -288,15 +340,18 @@
 // getOtherModuleLabel returns a bazel.Label for the given dependency/tag combination for the
 // module. The label will be relative to the current directory if appropriate. The dependency must
 // already be resolved by either deps mutator or path deps mutator.
-func getOtherModuleLabel(ctx BazelConversionPathContext, dep, tag string) bazel.Label {
-	m, _ := ctx.GetDirectDep(dep)
+func getOtherModuleLabel(ctx BazelConversionPathContext, dep, tag string, isWholeLibs bool) bazel.Label {
+	m, _ := ctx.ModuleFromName(dep)
 	if m == nil {
-		panic(fmt.Errorf(`Cannot get direct dep %q of %q.
-		This is likely because it was not added via AddDependency().
-		This may be due a mutator skipped during bp2build.`, dep, ctx.Module().Name()))
+		panic(fmt.Errorf("No module named %q found, but was a direct dep of %q", dep, ctx.Module().Name()))
 	}
 	otherLabel := bazelModuleLabel(ctx, m, tag)
 	label := bazelModuleLabel(ctx, ctx.Module(), "")
+	if isWholeLibs {
+		if m, ok := m.(Module); ok && IsModulePrebuilt(m) {
+			otherLabel += "_alwayslink"
+		}
+	}
 	if samePackage(label, otherLabel) {
 		otherLabel = bazelShortLabel(otherLabel)
 	}
@@ -359,7 +414,7 @@
 	}
 
 	outputPath := OutputPath{basePath{"", ""},
-		ctx.Config().buildDir,
+		ctx.Config().soongOutDir,
 		ctx.Config().BazelContext.OutputBase()}
 
 	return BazelOutPath{
diff --git a/android/config.go b/android/config.go
index 24fc522..1482e5d 100644
--- a/android/config.go
+++ b/android/config.go
@@ -35,6 +35,7 @@
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android/soongconfig"
+	"android/soong/bazel"
 	"android/soong/remoteexec"
 )
 
@@ -65,23 +66,19 @@
 	*config
 }
 
-// BuildDir returns the build output directory for the configuration.
-func (c Config) BuildDir() string {
-	return c.buildDir
+// SoongOutDir returns the build output directory for the configuration.
+func (c Config) SoongOutDir() string {
+	return c.soongOutDir
 }
 
-func (c Config) NinjaBuildDir() string {
-	return c.buildDir
+func (c Config) OutDir() string {
+	return c.soongOutDir
 }
 
 func (c Config) DebugCompilation() bool {
 	return false // Never compile Go code in the main build for debugging
 }
 
-func (c Config) SrcDir() string {
-	return c.srcDir
-}
-
 // A DeviceConfig object represents the configuration for a particular device
 // being built. For now there will only be one of these, but in the future there
 // may be multiple devices being built.
@@ -107,6 +104,12 @@
 
 	ProductVariablesFileName string
 
+	// BuildOS stores the OsType for the OS that the build is running on.
+	BuildOS OsType
+
+	// BuildArch stores the ArchType for the CPU that the build is running on.
+	BuildArch ArchType
+
 	Targets                  map[OsType][]Target
 	BuildOSTarget            Target // the Target for tools run on the build machine
 	BuildOSCommonTarget      Target // the Target for common (java) tools run on the build machine
@@ -119,8 +122,7 @@
 
 	deviceConfig *deviceConfig
 
-	srcDir         string // the path of the root source directory
-	buildDir       string // the path of the build output directory
+	soongOutDir    string // the path of the build output directory
 	moduleListFile string // the path to the file which lists blueprint files to parse.
 
 	env       map[string]string
@@ -169,7 +171,7 @@
 
 // loadFromConfigFile loads and decodes configuration options from a JSON file
 // in the current working directory.
-func loadFromConfigFile(configurable jsonConfigurable, filename string) error {
+func loadFromConfigFile(configurable *productVariables, filename string) error {
 	// Try to open the file
 	configFileReader, err := os.Open(filename)
 	defer configFileReader.Close()
@@ -194,13 +196,34 @@
 		}
 	}
 
-	// No error
-	return nil
+	if Bool(configurable.GcovCoverage) && Bool(configurable.ClangCoverage) {
+		return fmt.Errorf("GcovCoverage and ClangCoverage cannot both be set")
+	}
+
+	configurable.Native_coverage = proptools.BoolPtr(
+		Bool(configurable.GcovCoverage) ||
+			Bool(configurable.ClangCoverage))
+
+	// when Platform_sdk_final is true (or PLATFORM_VERSION_CODENAME is REL), use Platform_sdk_version;
+	// if false (pre-released version, for example), use Platform_sdk_codename.
+	if Bool(configurable.Platform_sdk_final) {
+		if configurable.Platform_sdk_version != nil {
+			configurable.Platform_sdk_version_or_codename =
+				proptools.StringPtr(strconv.Itoa(*(configurable.Platform_sdk_version)))
+		} else {
+			return fmt.Errorf("Platform_sdk_version cannot be pointed by a NULL pointer")
+		}
+	} else {
+		configurable.Platform_sdk_version_or_codename =
+			proptools.StringPtr(String(configurable.Platform_sdk_codename))
+	}
+
+	return saveToBazelConfigFile(configurable, filepath.Dir(filename))
 }
 
 // atomically writes the config file in case two copies of soong_build are running simultaneously
 // (for example, docs generation and ninja manifest generation)
-func saveToConfigFile(config jsonConfigurable, filename string) error {
+func saveToConfigFile(config *productVariables, filename string) error {
 	data, err := json.MarshalIndent(&config, "", "    ")
 	if err != nil {
 		return fmt.Errorf("cannot marshal config data: %s", err.Error())
@@ -229,13 +252,42 @@
 	return nil
 }
 
+func saveToBazelConfigFile(config *productVariables, outDir string) error {
+	dir := filepath.Join(outDir, bazel.SoongInjectionDirName, "product_config")
+	err := createDirIfNonexistent(dir, os.ModePerm)
+	if err != nil {
+		return fmt.Errorf("Could not create dir %s: %s", dir, err)
+	}
+
+	data, err := json.MarshalIndent(&config, "", "    ")
+	if err != nil {
+		return fmt.Errorf("cannot marshal config data: %s", err.Error())
+	}
+
+	bzl := []string{
+		bazel.GeneratedBazelFileWarning,
+		fmt.Sprintf(`_product_vars = json.decode("""%s""")`, data),
+		"product_vars = _product_vars\n",
+	}
+	err = ioutil.WriteFile(filepath.Join(dir, "product_variables.bzl"), []byte(strings.Join(bzl, "\n")), 0644)
+	if err != nil {
+		return fmt.Errorf("Could not write .bzl config file %s", err)
+	}
+	err = ioutil.WriteFile(filepath.Join(dir, "BUILD"), []byte(bazel.GeneratedBazelFileWarning), 0644)
+	if err != nil {
+		return fmt.Errorf("Could not write BUILD config file %s", err)
+	}
+
+	return nil
+}
+
 // NullConfig returns a mostly empty Config for use by standalone tools like dexpreopt_gen that
 // use the android package.
-func NullConfig(buildDir string) Config {
+func NullConfig(soongOutDir string) Config {
 	return Config{
 		config: &config{
-			buildDir: buildDir,
-			fs:       pathtools.OsFs,
+			soongOutDir: soongOutDir,
+			fs:          pathtools.OsFs,
 		},
 	}
 }
@@ -267,7 +319,7 @@
 			ShippingApiLevel:                  stringPtr("30"),
 		},
 
-		buildDir:     buildDir,
+		soongOutDir:  buildDir,
 		captureBuild: true,
 		env:          envCopy,
 
@@ -289,41 +341,28 @@
 	return Config{config}
 }
 
-func fuchsiaTargets() map[OsType][]Target {
-	return map[OsType][]Target{
-		Fuchsia: {
-			{Fuchsia, Arch{ArchType: Arm64, ArchVariant: "", Abi: []string{"arm64-v8a"}}, NativeBridgeDisabled, "", "", false},
-		},
-		BuildOs: {
-			{BuildOs, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", "", false},
-		},
-	}
-}
-
-var PrepareForTestSetDeviceToFuchsia = FixtureModifyConfig(func(config Config) {
-	config.Targets = fuchsiaTargets()
-})
-
 func modifyTestConfigToSupportArchMutator(testConfig Config) {
 	config := testConfig.config
 
+	determineBuildOS(config)
+
 	config.Targets = map[OsType][]Target{
 		Android: []Target{
 			{Android, Arch{ArchType: Arm64, ArchVariant: "armv8-a", Abi: []string{"arm64-v8a"}}, NativeBridgeDisabled, "", "", false},
 			{Android, Arch{ArchType: Arm, ArchVariant: "armv7-a-neon", Abi: []string{"armeabi-v7a"}}, NativeBridgeDisabled, "", "", false},
 		},
-		BuildOs: []Target{
-			{BuildOs, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", "", false},
-			{BuildOs, Arch{ArchType: X86}, NativeBridgeDisabled, "", "", false},
+		config.BuildOS: []Target{
+			{config.BuildOS, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", "", false},
+			{config.BuildOS, Arch{ArchType: X86}, NativeBridgeDisabled, "", "", false},
 		},
 	}
 
 	if runtime.GOOS == "darwin" {
-		config.Targets[BuildOs] = config.Targets[BuildOs][:1]
+		config.Targets[config.BuildOS] = config.Targets[config.BuildOS][:1]
 	}
 
-	config.BuildOSTarget = config.Targets[BuildOs][0]
-	config.BuildOSCommonTarget = getCommonTargets(config.Targets[BuildOs])[0]
+	config.BuildOSTarget = config.Targets[config.BuildOS][0]
+	config.BuildOSCommonTarget = getCommonTargets(config.Targets[config.BuildOS])[0]
 	config.AndroidCommonTarget = getCommonTargets(config.Targets[Android])[0]
 	config.AndroidFirstDeviceTarget = firstTarget(config.Targets[Android], "lib64", "lib32")[0]
 	config.TestProductVariables.DeviceArch = proptools.StringPtr("arm64")
@@ -332,6 +371,19 @@
 	config.TestProductVariables.DeviceSecondaryArchVariant = proptools.StringPtr("armv7-a-neon")
 }
 
+func modifyTestConfigForMusl(config Config) {
+	delete(config.Targets, config.BuildOS)
+	config.productVariables.HostMusl = boolPtr(true)
+	determineBuildOS(config.config)
+	config.Targets[config.BuildOS] = []Target{
+		{config.BuildOS, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", "", false},
+		{config.BuildOS, Arch{ArchType: X86}, NativeBridgeDisabled, "", "", false},
+	}
+
+	config.BuildOSTarget = config.Targets[config.BuildOS][0]
+	config.BuildOSCommonTarget = getCommonTargets(config.Targets[config.BuildOS])[0]
+}
+
 // TestArchConfig returns a Config object suitable for using for tests that
 // need to run the arch mutator.
 func TestArchConfig(buildDir string, env map[string]string, bp string, fs map[string][]byte) Config {
@@ -345,7 +397,7 @@
 // multiple runs in the same program execution is carried over (such as Bazel
 // context or environment deps).
 func ConfigForAdditionalRun(c Config) (Config, error) {
-	newConfig, err := NewConfig(c.srcDir, c.buildDir, c.moduleListFile, c.env)
+	newConfig, err := NewConfig(c.soongOutDir, c.moduleListFile, c.env)
 	if err != nil {
 		return Config{}, err
 	}
@@ -356,15 +408,14 @@
 
 // NewConfig creates a new Config object. The srcDir argument specifies the path
 // to the root source directory. It also loads the config file, if found.
-func NewConfig(srcDir, buildDir string, moduleListFile string, availableEnv map[string]string) (Config, error) {
+func NewConfig(soongOutDir string, moduleListFile string, availableEnv map[string]string) (Config, error) {
 	// Make a config with default options.
 	config := &config{
-		ProductVariablesFileName: filepath.Join(buildDir, productVariablesFileName),
+		ProductVariablesFileName: filepath.Join(soongOutDir, productVariablesFileName),
 
 		env: availableEnv,
 
-		srcDir:            srcDir,
-		buildDir:          buildDir,
+		soongOutDir:       soongOutDir,
 		multilibConflicts: make(map[ArchType]bool),
 
 		moduleListFile: moduleListFile,
@@ -377,12 +428,12 @@
 
 	// Soundness check of the build and source directories. This won't catch strange
 	// configurations with symlinks, but at least checks the obvious case.
-	absBuildDir, err := filepath.Abs(buildDir)
+	absBuildDir, err := filepath.Abs(soongOutDir)
 	if err != nil {
 		return Config{}, err
 	}
 
-	absSrcDir, err := filepath.Abs(srcDir)
+	absSrcDir, err := filepath.Abs(".")
 	if err != nil {
 		return Config{}, err
 	}
@@ -397,11 +448,13 @@
 		return Config{}, err
 	}
 
-	KatiEnabledMarkerFile := filepath.Join(buildDir, ".soong.kati_enabled")
+	KatiEnabledMarkerFile := filepath.Join(soongOutDir, ".soong.kati_enabled")
 	if _, err := os.Stat(absolutePath(KatiEnabledMarkerFile)); err == nil {
 		config.katiEnabled = true
 	}
 
+	determineBuildOS(config)
+
 	// Sets up the map of target OSes to the finer grained compilation targets
 	// that are configured from the product variables.
 	targets, err := decodeTargetProductVariables(config)
@@ -439,8 +492,8 @@
 	config.Targets = targets
 
 	// Compilation targets for host tools.
-	config.BuildOSTarget = config.Targets[BuildOs][0]
-	config.BuildOSCommonTarget = getCommonTargets(config.Targets[BuildOs])[0]
+	config.BuildOSTarget = config.Targets[config.BuildOS][0]
+	config.BuildOSCommonTarget = getCommonTargets(config.Targets[config.BuildOS])[0]
 
 	// Compilation targets for Android.
 	if len(config.Targets[Android]) > 0 {
@@ -448,14 +501,6 @@
 		config.AndroidFirstDeviceTarget = firstTarget(config.Targets[Android], "lib64", "lib32")[0]
 	}
 
-	if Bool(config.productVariables.GcovCoverage) && Bool(config.productVariables.ClangCoverage) {
-		return Config{}, fmt.Errorf("GcovCoverage and ClangCoverage cannot both be set")
-	}
-
-	config.productVariables.Native_coverage = proptools.BoolPtr(
-		Bool(config.productVariables.GcovCoverage) ||
-			Bool(config.productVariables.ClangCoverage))
-
 	config.BazelContext, err = NewBazelContext(config)
 	config.bp2buildPackageConfig = bp2buildDefaultConfig
 	config.bp2buildModuleTypeConfig = make(map[string]bool)
@@ -510,12 +555,10 @@
 
 // BlueprintToolLocation returns the directory containing build system tools
 // from Blueprint, like soong_zip and merge_zips.
-func (c *config) BlueprintToolLocation() string {
-	return filepath.Join(c.buildDir, "host", c.PrebuiltOS(), "bin")
+func (c *config) HostToolDir() string {
+	return filepath.Join(c.soongOutDir, "host", c.PrebuiltOS(), "bin")
 }
 
-var _ bootstrap.ConfigBlueprintToolLocation = (*config)(nil)
-
 func (c *config) HostToolPath(ctx PathContext, tool string) Path {
 	return PathForOutput(ctx, "host", c.PrebuiltOS(), "bin", tool)
 }
@@ -546,7 +589,7 @@
 
 // GoRoot returns the path to the root directory of the Go toolchain.
 func (c *config) GoRoot() string {
-	return fmt.Sprintf("%s/prebuilts/go/%s", c.srcDir, c.PrebuiltOS())
+	return fmt.Sprintf("prebuilts/go/%s", c.PrebuiltOS())
 }
 
 // PrebuiltBuildTool returns the path to a tool in the prebuilts directory containing
@@ -792,6 +835,12 @@
 	return Bool(c.productVariables.Unbundled_build_apps)
 }
 
+// Returns true if building image that aren't bundled with the platform.
+// UnbundledBuild() is always true when this is true.
+func (c *config) UnbundledBuildImage() bool {
+	return Bool(c.productVariables.Unbundled_build_image)
+}
+
 // Returns true if building modules against prebuilt SDKs.
 func (c *config) AlwaysUsePrebuiltSdks() bool {
 	return Bool(c.productVariables.Always_use_prebuilt_sdks)
@@ -802,10 +851,6 @@
 	return Bool(c.productVariables.Skip_boot_jars_check)
 }
 
-func (c *config) Fuchsia() bool {
-	return Bool(c.productVariables.Fuchsia)
-}
-
 func (c *config) MinimizeJavaDebugInfo() bool {
 	return Bool(c.productVariables.MinimizeJavaDebugInfo) && !Bool(c.productVariables.Eng)
 }
@@ -1618,8 +1663,9 @@
 	return ConfiguredJarList{apexes, jars}
 }
 
-// Filter keeps the entries if a jar appears in the given list of jars to keep; returns a new list.
-func (l *ConfiguredJarList) Filter(jarsToKeep []string) ConfiguredJarList {
+// Filter keeps the entries if a jar appears in the given list of jars to keep. Returns a new list
+// and any remaining jars that are not on this list.
+func (l *ConfiguredJarList) Filter(jarsToKeep []string) (ConfiguredJarList, []string) {
 	var apexes []string
 	var jars []string
 
@@ -1630,7 +1676,7 @@
 		}
 	}
 
-	return ConfiguredJarList{apexes, jars}
+	return ConfiguredJarList{apexes, jars}, RemoveListFromList(jarsToKeep, jars)
 }
 
 // CopyOfJars returns a copy of the list of strings containing jar module name
@@ -1812,16 +1858,16 @@
 func (c *config) BootJars() []string {
 	return c.Once(earlyBootJarsKey, func() interface{} {
 		list := c.productVariables.BootJars.CopyOfJars()
-		return append(list, c.productVariables.UpdatableBootJars.CopyOfJars()...)
+		return append(list, c.productVariables.ApexBootJars.CopyOfJars()...)
 	}).([]string)
 }
 
-func (c *config) NonUpdatableBootJars() ConfiguredJarList {
+func (c *config) NonApexBootJars() ConfiguredJarList {
 	return c.productVariables.BootJars
 }
 
-func (c *config) UpdatableBootJars() ConfiguredJarList {
-	return c.productVariables.UpdatableBootJars
+func (c *config) ApexBootJars() ConfiguredJarList {
+	return c.productVariables.ApexBootJars
 }
 
 func (c *config) RBEWrapper() string {
diff --git a/android/defaults.go b/android/defaults.go
index aacfbac..be80cf1 100644
--- a/android/defaults.go
+++ b/android/defaults.go
@@ -104,6 +104,7 @@
 	EarlyModuleContext
 
 	CreateModule(ModuleFactory, ...interface{}) Module
+	AddMissingDependencies(missingDeps []string)
 }
 
 type DefaultableHook func(ctx DefaultableHookContext)
diff --git a/android/filegroup.go b/android/filegroup.go
index fc6850e..54d01d3 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -15,8 +15,9 @@
 package android
 
 import (
-	"android/soong/bazel"
 	"strings"
+
+	"android/soong/bazel"
 )
 
 func init() {
@@ -33,24 +34,6 @@
 	Srcs bazel.LabelListAttribute
 }
 
-type bazelFilegroup struct {
-	BazelTargetModuleBase
-	bazelFilegroupAttributes
-}
-
-func BazelFileGroupFactory() Module {
-	module := &bazelFilegroup{}
-	module.AddProperties(&module.bazelFilegroupAttributes)
-	InitBazelTargetModule(module)
-	return module
-}
-
-func (bfg *bazelFilegroup) Name() string {
-	return bfg.BaseModuleName()
-}
-
-func (bfg *bazelFilegroup) GenerateAndroidBuildActions(ctx ModuleContext) {}
-
 func FilegroupBp2Build(ctx TopDownMutatorContext) {
 	fg, ok := ctx.Module().(*fileGroup)
 	if !ok || !fg.ConvertWithBp2build(ctx) {
@@ -63,9 +46,12 @@
 		Srcs: srcs,
 	}
 
-	props := bazel.BazelTargetModuleProperties{Rule_class: "filegroup"}
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class:        "filegroup",
+		Bzl_load_location: "//build/bazel/rules:filegroup.bzl",
+	}
 
-	ctx.CreateBazelTargetModule(BazelFileGroupFactory, fg.Name(), props, attrs)
+	ctx.CreateBazelTargetModule(fg.Name(), props, attrs)
 }
 
 type fileGroupProperties struct {
@@ -105,7 +91,7 @@
 	return module
 }
 
-func (fg *fileGroup) generateBazelBuildActions(ctx ModuleContext) bool {
+func (fg *fileGroup) GenerateBazelBuildActions(ctx ModuleContext) bool {
 	if !fg.MixedBuildsEnabled(ctx) {
 		return false
 	}
@@ -128,7 +114,7 @@
 }
 
 func (fg *fileGroup) GenerateAndroidBuildActions(ctx ModuleContext) {
-	if fg.generateBazelBuildActions(ctx) {
+	if fg.GenerateBazelBuildActions(ctx) {
 		return
 	}
 
diff --git a/android/fixture.go b/android/fixture.go
index fd051a7..728f031 100644
--- a/android/fixture.go
+++ b/android/fixture.go
@@ -834,7 +834,7 @@
 func (r *TestResult) NormalizePathForTesting(path Path) string {
 	pathContext := PathContextForTesting(r.Config)
 	pathAsString := path.String()
-	if rel, isRel := MaybeRel(pathContext, r.Config.BuildDir(), pathAsString); isRel {
+	if rel, isRel := MaybeRel(pathContext, r.Config.SoongOutDir(), pathAsString); isRel {
 		return rel
 	}
 	return pathAsString
diff --git a/android/image.go b/android/image.go
index 66101be..bc6b8cd 100644
--- a/android/image.go
+++ b/android/image.go
@@ -43,10 +43,9 @@
 	// its variation.
 	ExtraImageVariations(ctx BaseModuleContext) []string
 
-	// SetImageVariation will be passed a newly created recovery variant of the module.  ModuleBase implements
-	// SetImageVariation, most module types will not need to override it, and those that do must call the
-	// overridden method.  Implementors of SetImageVariation must be careful to modify the module argument
-	// and not the receiver.
+	// SetImageVariation is called for each newly created image variant. The receiver is the original
+	// module, "variation" is the name of the newly created variant and "module" is the newly created
+	// variant itself.
 	SetImageVariation(ctx BaseModuleContext, variation string, module Module)
 }
 
diff --git a/android/license_sdk_member.go b/android/license_sdk_member.go
index cd36ed6..2ce921b 100644
--- a/android/license_sdk_member.go
+++ b/android/license_sdk_member.go
@@ -31,9 +31,9 @@
 	SdkMemberTypeBase
 }
 
-func (l *licenseSdkMemberType) AddDependencies(mctx BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
+func (l *licenseSdkMemberType) AddDependencies(ctx SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) {
 	// Add dependencies onto the license module from the sdk module.
-	mctx.AddDependency(mctx.Module(), dependencyTag, names...)
+	ctx.AddDependency(ctx.Module(), dependencyTag, names...)
 }
 
 func (l *licenseSdkMemberType) IsInstance(module Module) bool {
diff --git a/android/metrics.go b/android/metrics.go
index b7aee54..2cd5efa 100644
--- a/android/metrics.go
+++ b/android/metrics.go
@@ -18,7 +18,7 @@
 	"io/ioutil"
 	"runtime"
 
-	"github.com/golang/protobuf/proto"
+	"google.golang.org/protobuf/proto"
 
 	soong_metrics_proto "android/soong/ui/metrics/metrics_proto"
 )
diff --git a/android/module.go b/android/module.go
index f745a4a..cc03418 100644
--- a/android/module.go
+++ b/android/module.go
@@ -223,6 +223,8 @@
 	// the first DependencyTag.
 	GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag)
 
+	ModuleFromName(name string) (blueprint.Module, bool)
+
 	// VisitDirectDepsBlueprint calls visit for each direct dependency.  If there are multiple
 	// direct dependencies on the same module visit will be called multiple times on that module
 	// and OtherModuleDependencyTag will return a different tag for each.
@@ -233,8 +235,8 @@
 
 	// VisitDirectDeps calls visit for each direct dependency.  If there are multiple
 	// direct dependencies on the same module visit will be called multiple times on that module
-	// and OtherModuleDependencyTag will return a different tag for each.  It skips any
-	// dependencies that are not an android.Module.
+	// and OtherModuleDependencyTag will return a different tag for each.  It raises an error if any of the
+	// dependencies are not an android.Module.
 	//
 	// The Module passed to the visit function should not be retained outside of the visit
 	// function, it may be invalidated by future mutators.
@@ -325,7 +327,6 @@
 	Host() bool
 	Device() bool
 	Darwin() bool
-	Fuchsia() bool
 	Windows() bool
 	Debug() bool
 	PrimaryArch() bool
@@ -344,8 +345,18 @@
 	// Deprecated: use ModuleContext.Build instead.
 	ModuleBuild(pctx PackageContext, params ModuleBuildParams)
 
+	// Returns a list of paths expanded from globs and modules referenced using ":module" syntax.  The property must
+	// be tagged with `android:"path" to support automatic source module dependency resolution.
+	//
+	// Deprecated: use PathsForModuleSrc or PathsForModuleSrcExcludes instead.
 	ExpandSources(srcFiles, excludes []string) Paths
+
+	// Returns a single path expanded from globs and modules referenced using ":module" syntax.  The property must
+	// be tagged with `android:"path" to support automatic source module dependency resolution.
+	//
+	// Deprecated: use PathForModuleSrc instead.
 	ExpandSource(srcFile, prop string) Path
+
 	ExpandOptionalSource(srcFile *string, prop string) OptionalPath
 
 	// InstallExecutable creates a rule to copy srcPath to name in the installPath directory,
@@ -403,6 +414,7 @@
 	InstallInDebugRamdisk() bool
 	InstallInRecovery() bool
 	InstallInRoot() bool
+	InstallInVendor() bool
 	InstallBypassMake() bool
 	InstallForceOS() (*OsType, *ArchType)
 
@@ -461,6 +473,7 @@
 	InstallInDebugRamdisk() bool
 	InstallInRecovery() bool
 	InstallInRoot() bool
+	InstallInVendor() bool
 	InstallBypassMake() bool
 	InstallForceOS() (*OsType, *ArchType)
 	HideFromMake()
@@ -473,10 +486,16 @@
 	InitRc() Paths
 	VintfFragments() Paths
 	NoticeFiles() Paths
+	EffectiveLicenseFiles() Paths
 
 	AddProperties(props ...interface{})
 	GetProperties() []interface{}
 
+	// IsConvertedByBp2build returns whether this module was converted via bp2build
+	IsConvertedByBp2build() bool
+	// Bp2buildTargets returns the target(s) generated for Bazel via bp2build for this module
+	Bp2buildTargets() []bp2buildInfo
+
 	BuildParamsForTests() []BuildParams
 	RuleParamsForTests() map[blueprint.Rule]blueprint.RuleParams
 	VariablesForTests() map[string]string
@@ -864,6 +883,11 @@
 	// for example "" for core or "recovery" for recovery.  It will often be set to one of the
 	// constants in image.go, but can also be set to a custom value by individual module types.
 	ImageVariation string `blueprint:"mutated"`
+
+	// Information about _all_ bp2build targets generated by this module. Multiple targets are
+	// supported as Soong handles some things within a single target that we may choose to split into
+	// multiple targets, e.g. renderscript, protos, yacc within a cc module.
+	Bp2buildInfo []bp2buildInfo `blueprint:"mutated"`
 }
 
 type distProperties struct {
@@ -962,10 +986,13 @@
 	// Device is built by default. Host and HostCross are not supported.
 	DeviceSupported = deviceSupported | deviceDefault
 
-	// Device is built by default. Host and HostCross are supported.
+	// By default, _only_ device variant is built. Device variant can be disabled with `device_supported: false`
+    // Host and HostCross are disabled by default and can be enabled with `host_supported: true`
 	HostAndDeviceSupported = hostSupported | hostCrossSupported | deviceSupported | deviceDefault
 
 	// Host, HostCross, and Device are built by default.
+    // Building Device can be disabled with `device_supported: false`
+    // Building Host and HostCross can be disabled with `host_supported: false`
 	HostAndDeviceDefault = hostSupported | hostCrossSupported | hostDefault |
 		deviceSupported | deviceDefault
 
@@ -1190,6 +1217,58 @@
 	vintfFragmentsPaths Paths
 }
 
+// A struct containing all relevant information about a Bazel target converted via bp2build.
+type bp2buildInfo struct {
+	Name       string
+	Dir        string
+	BazelProps bazel.BazelTargetModuleProperties
+	Attrs      interface{}
+}
+
+// TargetName returns the Bazel target name of a bp2build converted target.
+func (b bp2buildInfo) TargetName() string {
+	return b.Name
+}
+
+// TargetPackage returns the Bazel package of a bp2build converted target.
+func (b bp2buildInfo) TargetPackage() string {
+	return b.Dir
+}
+
+// BazelRuleClass returns the Bazel rule class of a bp2build converted target.
+func (b bp2buildInfo) BazelRuleClass() string {
+	return b.BazelProps.Rule_class
+}
+
+// BazelRuleLoadLocation returns the location of the  Bazel rule of a bp2build converted target.
+// This may be empty as native Bazel rules do not need to be loaded.
+func (b bp2buildInfo) BazelRuleLoadLocation() string {
+	return b.BazelProps.Bzl_load_location
+}
+
+// BazelAttributes returns the Bazel attributes of a bp2build converted target.
+func (b bp2buildInfo) BazelAttributes() interface{} {
+	return b.Attrs
+}
+
+func (m *ModuleBase) addBp2buildInfo(info bp2buildInfo) {
+	m.commonProperties.Bp2buildInfo = append(m.commonProperties.Bp2buildInfo, info)
+}
+
+// IsConvertedByBp2build returns whether this module was converted via bp2build.
+func (m *ModuleBase) IsConvertedByBp2build() bool {
+	return len(m.commonProperties.Bp2buildInfo) > 0
+}
+
+// Bp2buildTargets returns the Bazel targets bp2build generated for this module.
+func (m *ModuleBase) Bp2buildTargets() []bp2buildInfo {
+	return m.commonProperties.Bp2buildInfo
+}
+
+func (m *ModuleBase) AddJSONData(d *map[string]interface{}) {
+	(*d)["Android"] = map[string]interface{}{}
+}
+
 func (m *ModuleBase) ComponentDepsMutator(BottomUpMutatorContext) {}
 
 func (m *ModuleBase) DepsMutator(BottomUpMutatorContext) {}
@@ -1501,13 +1580,17 @@
 	return m.commonProperties.NamespaceExportedToMake
 }
 
+func (m *ModuleBase) EffectiveLicenseFiles() Paths {
+	return m.commonProperties.Effective_license_text
+}
+
 // computeInstallDeps finds the installed paths of all dependencies that have a dependency
 // tag that is annotated as needing installation via the IsInstallDepNeeded method.
 func (m *ModuleBase) computeInstallDeps(ctx ModuleContext) ([]*installPathsDepSet, []*packagingSpecsDepSet) {
 	var installDeps []*installPathsDepSet
 	var packagingSpecs []*packagingSpecsDepSet
 	ctx.VisitDirectDeps(func(dep Module) {
-		if IsInstallDepNeeded(ctx.OtherModuleDependencyTag(dep)) && !dep.IsHideFromMake() {
+		if IsInstallDepNeeded(ctx.OtherModuleDependencyTag(dep)) && !dep.IsHideFromMake() && !dep.IsSkipInstall() {
 			installDeps = append(installDeps, dep.base().installFilesDepSet)
 			packagingSpecs = append(packagingSpecs, dep.base().packagingSpecsDepSet)
 		}
@@ -1560,6 +1643,10 @@
 	return Bool(m.commonProperties.Recovery)
 }
 
+func (m *ModuleBase) InstallInVendor() bool {
+	return Bool(m.commonProperties.Vendor)
+}
+
 func (m *ModuleBase) InstallInRoot() bool {
 	return false
 }
@@ -2013,8 +2100,13 @@
 	tagPath  []blueprint.DependencyTag
 
 	strictVisitDeps bool // If true, enforce that all dependencies are enabled
+
+	bazelConversionMode bool
 }
 
+func (b *baseModuleContext) BazelConversionMode() bool {
+	return b.bazelConversionMode
+}
 func (b *baseModuleContext) OtherModuleName(m blueprint.Module) string {
 	return b.bp.OtherModuleName(m)
 }
@@ -2354,6 +2446,17 @@
 	return b.getDirectDepFirstTag(name)
 }
 
+func (b *baseModuleContext) ModuleFromName(name string) (blueprint.Module, bool) {
+	if !b.BazelConversionMode() {
+		panic("cannot call ModuleFromName if not in bazel conversion mode")
+	}
+	if moduleName, _ := SrcIsModuleWithTag(name); moduleName != "" {
+		return b.bp.ModuleFromName(moduleName)
+	} else {
+		return b.bp.ModuleFromName(name)
+	}
+}
+
 func (b *baseModuleContext) VisitDirectDepsBlueprint(visit func(blueprint.Module)) {
 	b.bp.VisitDirectDeps(visit)
 }
@@ -2544,10 +2647,6 @@
 	return b.os == Darwin
 }
 
-func (b *baseModuleContext) Fuchsia() bool {
-	return b.os == Fuchsia
-}
-
 func (b *baseModuleContext) Windows() bool {
 	return b.os == Windows
 }
@@ -2626,6 +2725,10 @@
 	return m.module.InstallForceOS()
 }
 
+func (m *moduleContext) InstallInVendor() bool {
+	return m.module.InstallInVendor()
+}
+
 func (m *moduleContext) skipInstall() bool {
 	if m.module.base().commonProperties.SkipInstall {
 		return true
@@ -2793,42 +2896,101 @@
 	return m.bp
 }
 
-// SrcIsModule decodes module references in the format ":name" into the module name, or empty string if the input
-// was not a module reference.
+// SrcIsModule decodes module references in the format ":unqualified-name" or "//namespace:name"
+// into the module name, or empty string if the input was not a module reference.
 func SrcIsModule(s string) (module string) {
-	if len(s) > 1 && s[0] == ':' {
-		return s[1:]
+	if len(s) > 1 {
+		if s[0] == ':' {
+			module = s[1:]
+			if !isUnqualifiedModuleName(module) {
+				// The module name should be unqualified but is not so do not treat it as a module.
+				module = ""
+			}
+		} else if s[0] == '/' && s[1] == '/' {
+			module = s
+		}
 	}
-	return ""
+	return module
 }
 
-// SrcIsModule decodes module references in the format ":name{.tag}" into the module name and tag, ":name" into the
-// module name and an empty string for the tag, or empty strings if the input was not a module reference.
+// SrcIsModuleWithTag decodes module references in the format ":unqualified-name{.tag}" or
+// "//namespace:name{.tag}" into the module name and tag, ":unqualified-name" or "//namespace:name"
+// into the module name and an empty string for the tag, or empty strings if the input was not a
+// module reference.
 func SrcIsModuleWithTag(s string) (module, tag string) {
-	if len(s) > 1 && s[0] == ':' {
-		module = s[1:]
-		if tagStart := strings.IndexByte(module, '{'); tagStart > 0 {
-			if module[len(module)-1] == '}' {
-				tag = module[tagStart+1 : len(module)-1]
-				module = module[:tagStart]
-				return module, tag
+	if len(s) > 1 {
+		if s[0] == ':' {
+			module = s[1:]
+		} else if s[0] == '/' && s[1] == '/' {
+			module = s
+		}
+
+		if module != "" {
+			if tagStart := strings.IndexByte(module, '{'); tagStart > 0 {
+				if module[len(module)-1] == '}' {
+					tag = module[tagStart+1 : len(module)-1]
+					module = module[:tagStart]
+				}
+			}
+
+			if s[0] == ':' && !isUnqualifiedModuleName(module) {
+				// The module name should be unqualified but is not so do not treat it as a module.
+				module = ""
+				tag = ""
 			}
 		}
-		return module, ""
 	}
-	return "", ""
+
+	return module, tag
 }
 
+// isUnqualifiedModuleName makes sure that the supplied module is an unqualified module name, i.e.
+// does not contain any /.
+func isUnqualifiedModuleName(module string) bool {
+	return strings.IndexByte(module, '/') == -1
+}
+
+// sourceOrOutputDependencyTag is the dependency tag added automatically by pathDepsMutator for any
+// module reference in a property annotated with `android:"path"` or passed to ExtractSourceDeps
+// or ExtractSourcesDeps.
+//
+// If uniquely identifies the dependency that was added as it contains both the module name used to
+// add the dependency as well as the tag. That makes it very simple to find the matching dependency
+// in GetModuleFromPathDep as all it needs to do is find the dependency whose tag matches the tag
+// used to add it. It does not need to check that the module name as returned by one of
+// Module.Name(), BaseModuleContext.OtherModuleName() or ModuleBase.BaseModuleName() matches the
+// name supplied in the tag. That means it does not need to handle differences in module names
+// caused by prebuilt_ prefix, or fully qualified module names.
 type sourceOrOutputDependencyTag struct {
 	blueprint.BaseDependencyTag
+
+	// The name of the module.
+	moduleName string
+
+	// The tag that will be passed to the module's OutputFileProducer.OutputFiles(tag) method.
 	tag string
 }
 
-func sourceOrOutputDepTag(tag string) blueprint.DependencyTag {
-	return sourceOrOutputDependencyTag{tag: tag}
+func sourceOrOutputDepTag(moduleName, tag string) blueprint.DependencyTag {
+	return sourceOrOutputDependencyTag{moduleName: moduleName, tag: tag}
 }
 
-var SourceDepTag = sourceOrOutputDepTag("")
+// IsSourceDepTag returns true if the supplied blueprint.DependencyTag is one that was used to add
+// dependencies by either ExtractSourceDeps, ExtractSourcesDeps or automatically for properties
+// tagged with `android:"path"`.
+func IsSourceDepTag(depTag blueprint.DependencyTag) bool {
+	_, ok := depTag.(sourceOrOutputDependencyTag)
+	return ok
+}
+
+// IsSourceDepTagWithOutputTag returns true if the supplied blueprint.DependencyTag is one that was
+// used to add dependencies by either ExtractSourceDeps, ExtractSourcesDeps or automatically for
+// properties tagged with `android:"path"` AND it was added using a module reference of
+// :moduleName{outputTag}.
+func IsSourceDepTagWithOutputTag(depTag blueprint.DependencyTag, outputTag string) bool {
+	t, ok := depTag.(sourceOrOutputDependencyTag)
+	return ok && t.tag == outputTag
+}
 
 // Adds necessary dependencies to satisfy filegroup or generated sources modules listed in srcFiles
 // using ":module" syntax, if any.
@@ -2843,7 +3005,7 @@
 				ctx.ModuleErrorf("found source dependency duplicate: %q!", s)
 			} else {
 				set[s] = true
-				ctx.AddDependency(ctx.Module(), sourceOrOutputDepTag(t), m)
+				ctx.AddDependency(ctx.Module(), sourceOrOutputDepTag(m, t), m)
 			}
 		}
 	}
@@ -2856,7 +3018,7 @@
 func ExtractSourceDeps(ctx BottomUpMutatorContext, s *string) {
 	if s != nil {
 		if m, t := SrcIsModuleWithTag(*s); m != "" {
-			ctx.AddDependency(ctx.Module(), sourceOrOutputDepTag(t), m)
+			ctx.AddDependency(ctx.Module(), sourceOrOutputDepTag(m, t), m)
 		}
 	}
 }
diff --git a/android/module_test.go b/android/module_test.go
index 9ac9291..9e2b0ca 100644
--- a/android/module_test.go
+++ b/android/module_test.go
@@ -55,6 +55,27 @@
 			},
 			wantModule: "foo:bar",
 		},
+		{
+			name: "fully qualified",
+			args: args{
+				s: "//foo:bar",
+			},
+			wantModule: "//foo:bar",
+		},
+		{
+			name: "fully qualified with tag",
+			args: args{
+				s: "//foo:bar{.tag}",
+			},
+			wantModule: "//foo:bar{.tag}",
+		},
+		{
+			name: "invalid unqualified name",
+			args: args{
+				s: ":foo/bar",
+			},
+			wantModule: "",
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
@@ -128,6 +149,35 @@
 			},
 			wantModule: "foo.bar}",
 		},
+		{
+			name: "fully qualified",
+			args: args{
+				s: "//foo:bar",
+			},
+			wantModule: "//foo:bar",
+		},
+		{
+			name: "fully qualified with tag",
+			args: args{
+				s: "//foo:bar{.tag}",
+			},
+			wantModule: "//foo:bar",
+			wantTag:    ".tag",
+		},
+		{
+			name: "invalid unqualified name",
+			args: args{
+				s: ":foo/bar",
+			},
+			wantModule: "",
+		},
+		{
+			name: "invalid unqualified name with tag",
+			args: args{
+				s: ":foo/bar{.tag}",
+			},
+			wantModule: "",
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
diff --git a/android/mutator.go b/android/mutator.go
index 365bf29..20ec621 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -19,6 +19,7 @@
 	"fmt"
 	"reflect"
 	"strings"
+	"sync"
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
@@ -34,7 +35,7 @@
 //   continue on to GenerateAndroidBuildActions
 
 // RegisterMutatorsForBazelConversion is a alternate registration pipeline for bp2build. Exported for testing.
-func RegisterMutatorsForBazelConversion(ctx *Context, preArchMutators, depsMutators, bp2buildMutators []RegisterMutatorFunc) {
+func RegisterMutatorsForBazelConversion(ctx *Context, preArchMutators, bp2buildMutators []RegisterMutatorFunc) {
 	mctx := &registerMutatorsContext{
 		bazelConversionMode: true,
 	}
@@ -52,16 +53,6 @@
 		f(mctx)
 	}
 
-	bp2buildDepsMutators = append([]RegisterMutatorFunc{
-		registerDepsMutatorBp2Build,
-		registerPathDepsMutator,
-		registerBp2buildArchPathDepsMutator,
-	}, depsMutators...)
-
-	for _, f := range bp2buildDepsMutators {
-		f(mctx)
-	}
-
 	// Register bp2build mutators
 	for _, f := range bp2buildMutators {
 		f(mctx)
@@ -226,9 +217,11 @@
 }
 
 var bp2buildPreArchMutators = []RegisterMutatorFunc{}
-var bp2buildDepsMutators = []RegisterMutatorFunc{}
 var bp2buildMutators = map[string]RegisterMutatorFunc{}
 
+// See http://b/192523357
+var bp2buildLock sync.Mutex
+
 // RegisterBp2BuildMutator registers specially crafted mutators for
 // converting Blueprint/Android modules into special modules that can
 // be code-generated into Bazel BUILD targets.
@@ -238,6 +231,9 @@
 	f := func(ctx RegisterMutatorsContext) {
 		ctx.TopDown(moduleType, m)
 	}
+	// Use a lock to avoid a concurrent map write if RegisterBp2BuildMutator is called in parallel
+	bp2buildLock.Lock()
+	defer bp2buildLock.Unlock()
 	bp2buildMutators[moduleType] = f
 }
 
@@ -247,12 +243,6 @@
 	bp2buildPreArchMutators = append(bp2buildPreArchMutators, f)
 }
 
-// DepsBp2BuildMutators adds mutators to be register for converting Android Blueprint modules into
-// Bazel BUILD targets that should run prior to conversion to resolve dependencies.
-func DepsBp2BuildMutators(f RegisterMutatorFunc) {
-	bp2buildDepsMutators = append(bp2buildDepsMutators, f)
-}
-
 type BaseMutatorContext interface {
 	BaseModuleContext
 
@@ -262,6 +252,9 @@
 	// Rename all variants of a module.  The new name is not visible to calls to ModuleName,
 	// AddDependency or OtherModuleName until after this mutator pass is complete.
 	Rename(name string)
+
+	// BazelConversionMode returns whether this mutator is being run as part of Bazel Conversion.
+	BazelConversionMode() bool
 }
 
 type TopDownMutator func(TopDownMutatorContext)
@@ -277,7 +270,7 @@
 	// factory method, just like in CreateModule, but also requires
 	// BazelTargetModuleProperties containing additional metadata for the
 	// bp2build codegenerator.
-	CreateBazelTargetModule(ModuleFactory, string, bazel.BazelTargetModuleProperties, interface{}) BazelTargetModule
+	CreateBazelTargetModule(string, bazel.BazelTargetModuleProperties, interface{})
 }
 
 type topDownMutatorContext struct {
@@ -403,26 +396,24 @@
 	// variant of the current module.  The value should not be modified after being passed to
 	// SetVariationProvider.
 	SetVariationProvider(module blueprint.Module, provider blueprint.ProviderKey, value interface{})
-
-	// BazelConversionMode returns whether this mutator is being run as part of Bazel Conversion.
-	BazelConversionMode() bool
 }
 
 type bottomUpMutatorContext struct {
 	bp blueprint.BottomUpMutatorContext
 	baseModuleContext
-	finalPhase          bool
-	bazelConversionMode bool
+	finalPhase bool
 }
 
 func bottomUpMutatorContextFactory(ctx blueprint.BottomUpMutatorContext, a Module,
 	finalPhase, bazelConversionMode bool) BottomUpMutatorContext {
 
+	moduleContext := a.base().baseModuleContextFactory(ctx)
+	moduleContext.bazelConversionMode = bazelConversionMode
+
 	return &bottomUpMutatorContext{
-		bp:                  ctx,
-		baseModuleContext:   a.base().baseModuleContextFactory(ctx),
-		finalPhase:          finalPhase,
-		bazelConversionMode: bazelConversionMode,
+		bp:                ctx,
+		baseModuleContext: a.base().baseModuleContextFactory(ctx),
+		finalPhase:        finalPhase,
 	}
 }
 
@@ -455,9 +446,11 @@
 func (x *registerMutatorsContext) TopDown(name string, m TopDownMutator) MutatorHandle {
 	f := func(ctx blueprint.TopDownMutatorContext) {
 		if a, ok := ctx.Module().(Module); ok {
+			moduleContext := a.base().baseModuleContextFactory(ctx)
+			moduleContext.bazelConversionMode = x.bazelConversionMode
 			actx := &topDownMutatorContext{
 				bp:                ctx,
-				baseModuleContext: a.base().baseModuleContextFactory(ctx),
+				baseModuleContext: moduleContext,
 			}
 			m(actx)
 		}
@@ -523,26 +516,24 @@
 }
 
 func (t *topDownMutatorContext) CreateBazelTargetModule(
-	factory ModuleFactory,
 	name string,
 	bazelProps bazel.BazelTargetModuleProperties,
-	attrs interface{}) BazelTargetModule {
+	attrs interface{}) {
 	if strings.HasPrefix(name, bazel.BazelTargetModuleNamePrefix) {
 		panic(fmt.Errorf(
 			"The %s name prefix is added automatically, do not set it manually: %s",
 			bazel.BazelTargetModuleNamePrefix,
 			name))
 	}
-	name = bazel.BazelTargetModuleNamePrefix + name
-	nameProp := struct {
-		Name *string
-	}{
-		Name: &name,
+
+	info := bp2buildInfo{
+		Name:       name,
+		Dir:        t.OtherModuleDir(t.Module()),
+		BazelProps: bazelProps,
+		Attrs:      attrs,
 	}
 
-	b := t.createModuleWithoutInheritance(factory, &nameProp, attrs).(BazelTargetModule)
-	b.SetBazelTargetModuleProperties(bazelProps)
-	return b
+	t.Module().base().addBp2buildInfo(info)
 }
 
 func (t *topDownMutatorContext) AppendProperties(props ...interface{}) {
@@ -726,7 +717,3 @@
 func (b *bottomUpMutatorContext) SetVariationProvider(module blueprint.Module, provider blueprint.ProviderKey, value interface{}) {
 	b.bp.SetVariationProvider(module, provider, value)
 }
-
-func (b *bottomUpMutatorContext) BazelConversionMode() bool {
-	return b.bazelConversionMode
-}
diff --git a/android/namespace.go b/android/namespace.go
index d137636..4f727e1 100644
--- a/android/namespace.go
+++ b/android/namespace.go
@@ -27,7 +27,11 @@
 )
 
 func init() {
-	RegisterModuleType("soong_namespace", NamespaceFactory)
+	registerNamespaceBuildComponents(InitRegistrationContext)
+}
+
+func registerNamespaceBuildComponents(ctx RegistrationContext) {
+	ctx.RegisterModuleType("soong_namespace", NamespaceFactory)
 }
 
 // threadsafe sorted list
diff --git a/android/namespace_test.go b/android/namespace_test.go
index 08e221a..ea399da 100644
--- a/android/namespace_test.go
+++ b/android/namespace_test.go
@@ -636,13 +636,12 @@
 	result := GroupFixturePreparers(
 		FixtureModifyContext(func(ctx *TestContext) {
 			ctx.RegisterModuleType("test_module", newTestModule)
-			ctx.RegisterModuleType("soong_namespace", NamespaceFactory)
 			ctx.Context.RegisterModuleType("blueprint_test_module", newBlueprintTestModule)
-			ctx.PreArchMutators(RegisterNamespaceMutator)
 			ctx.PreDepsMutators(func(ctx RegisterMutatorsContext) {
 				ctx.BottomUp("rename", renameMutator)
 			})
 		}),
+		PrepareForTestWithNamespace,
 		bps.AddToFixture(),
 	).
 		// Ignore errors for now so tests can check them later.
diff --git a/android/neverallow.go b/android/neverallow.go
index af072cd..fa19009 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -55,6 +55,7 @@
 	AddNeverAllowRules(createCcSdkVariantRules()...)
 	AddNeverAllowRules(createUncompressDexRules()...)
 	AddNeverAllowRules(createMakefileGoalRules()...)
+	AddNeverAllowRules(createInitFirstStageRules()...)
 }
 
 // Add a NeverAllow rule to the set of rules to apply.
@@ -217,6 +218,15 @@
 	}
 }
 
+func createInitFirstStageRules() []Rule {
+	return []Rule{
+		NeverAllow().
+			Without("name", "init_first_stage").
+			With("install_in_root", "true").
+			Because("install_in_root is only for init_first_stage."),
+	}
+}
+
 func neverallowMutator(ctx BottomUpMutatorContext) {
 	m, ok := ctx.Module().(Module)
 	if !ok {
diff --git a/android/override_module.go b/android/override_module.go
index 0a7e294..e72cb78 100644
--- a/android/override_module.go
+++ b/android/override_module.go
@@ -213,7 +213,6 @@
 // to keep them in this order and not put any order mutators between them.
 func RegisterOverridePostDepsMutators(ctx RegisterMutatorsContext) {
 	ctx.BottomUp("override_deps", overrideModuleDepsMutator).Parallel()
-	ctx.TopDown("register_override", registerOverrideMutator).Parallel()
 	ctx.BottomUp("perform_override", performOverrideMutator).Parallel()
 	// overridableModuleDepsMutator calls OverridablePropertiesDepsMutator so that overridable modules can
 	// add deps from overridable properties.
@@ -253,20 +252,11 @@
 				return
 			}
 		})
-		ctx.AddDependency(ctx.Module(), overrideBaseDepTag, *module.getOverrideModuleProperties().Base)
-	}
-}
-
-// Visits the base module added as a dependency above, checks the module type, and registers the
-// overriding module.
-func registerOverrideMutator(ctx TopDownMutatorContext) {
-	ctx.VisitDirectDepsWithTag(overrideBaseDepTag, func(base Module) {
-		if o, ok := base.(OverridableModule); ok {
+		baseModule := ctx.AddDependency(ctx.Module(), overrideBaseDepTag, *module.getOverrideModuleProperties().Base)[0]
+		if o, ok := baseModule.(OverridableModule); ok {
 			o.addOverride(ctx.Module().(OverrideModule))
-		} else {
-			ctx.PropertyErrorf("base", "unsupported base module type")
 		}
-	})
+	}
 }
 
 // Now, goes through all overridable modules, finds all modules overriding them, creates a local
diff --git a/android/packaging_test.go b/android/packaging_test.go
index f91dc5d..ff7446c 100644
--- a/android/packaging_test.go
+++ b/android/packaging_test.go
@@ -18,13 +18,15 @@
 	"testing"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
 )
 
 // Module to be packaged
 type componentTestModule struct {
 	ModuleBase
 	props struct {
-		Deps []string
+		Deps         []string
+		Skip_install *bool
 	}
 }
 
@@ -49,6 +51,9 @@
 	builtFile := PathForModuleOut(ctx, m.Name())
 	dir := ctx.Target().Arch.ArchType.Multilib
 	installDir := PathForModuleInstall(ctx, dir)
+	if proptools.Bool(m.props.Skip_install) {
+		m.SkipInstall()
+	}
 	ctx.InstallFile(installDir, m.Name(), builtFile)
 }
 
@@ -365,3 +370,31 @@
 		}
 		`, []string{"lib64/foo"})
 }
+
+func TestPackagingWithSkipInstallDeps(t *testing.T) {
+	// package -[dep]-> foo -[dep]-> bar      -[dep]-> baz
+	//                  OK           SKIPPED
+	multiTarget := false
+	runPackagingTest(t, multiTarget,
+		`
+		component {
+			name: "foo",
+			deps: ["bar"],
+		}
+
+		component {
+			name: "bar",
+			deps: ["baz"],
+			skip_install: true,
+		}
+
+		component {
+			name: "baz",
+		}
+
+		package_module {
+			name: "package",
+			deps: ["foo"],
+		}
+		`, []string{"lib64/foo"})
+}
diff --git a/android/path_properties.go b/android/path_properties.go
index 4446773..3976880 100644
--- a/android/path_properties.go
+++ b/android/path_properties.go
@@ -51,7 +51,7 @@
 	// Add dependencies to anything that is a module reference.
 	for _, s := range pathProperties {
 		if m, t := SrcIsModuleWithTag(s); m != "" {
-			ctx.AddDependency(ctx.Module(), sourceOrOutputDepTag(t), m)
+			ctx.AddDependency(ctx.Module(), sourceOrOutputDepTag(m, t), m)
 		}
 	}
 }
diff --git a/android/path_properties_test.go b/android/path_properties_test.go
index 568f868..07b4869 100644
--- a/android/path_properties_test.go
+++ b/android/path_properties_test.go
@@ -63,7 +63,8 @@
 
 	if p.props.Foo != "" {
 		// Make sure there is only one dependency on a module listed in a property present in multiple property structs
-		if ctx.GetDirectDepWithTag(SrcIsModule(p.props.Foo), sourceOrOutputDepTag("")) == nil {
+		m := SrcIsModule(p.props.Foo)
+		if GetModuleFromPathDep(ctx, m, "") == nil {
 			ctx.ModuleErrorf("GetDirectDepWithTag failed")
 		}
 	}
diff --git a/android/paths.go b/android/paths.go
index fb75117..763cd7c 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -20,6 +20,7 @@
 	"os"
 	"path/filepath"
 	"reflect"
+	"regexp"
 	"sort"
 	"strings"
 
@@ -88,7 +89,8 @@
 // the Path methods that rely on module dependencies having been resolved.
 type ModuleWithDepsPathContext interface {
 	EarlyModulePathContext
-	GetDirectDepWithTag(name string, tag blueprint.DependencyTag) blueprint.Module
+	VisitDirectDepsBlueprint(visit func(blueprint.Module))
+	OtherModuleDependencyTag(m blueprint.Module) blueprint.DependencyTag
 }
 
 // ModuleMissingDepsPathContext is a subset of *ModuleContext methods required by
@@ -184,13 +186,13 @@
 	// A standard build has the following structure:
 	//   ../top/
 	//          out/ - make install files go here.
-	//          out/soong - this is the buildDir passed to NewTestConfig()
+	//          out/soong - this is the soongOutDir passed to NewTestConfig()
 	//          ... - the source files
 	//
 	// This function converts a path so that it appears relative to the ../top/ directory, i.e.
-	// * Make install paths, which have the pattern "buildDir/../<path>" are converted into the top
+	// * Make install paths, which have the pattern "soongOutDir/../<path>" are converted into the top
 	//   relative path "out/<path>"
-	// * Soong install paths and other writable paths, which have the pattern "buildDir/<path>" are
+	// * Soong install paths and other writable paths, which have the pattern "soongOutDir/<path>" are
 	//   converted into the top relative path "out/soong/<path>".
 	// * Source paths are already relative to the top.
 	// * Phony paths are not relative to anything.
@@ -209,7 +211,7 @@
 	Path
 
 	// return the path to the build directory.
-	getBuildDir() string
+	getSoongOutDir() string
 
 	// the writablePath method doesn't directly do anything,
 	// but it allows a struct to distinguish between whether or not it implements the WritablePath interface
@@ -446,7 +448,7 @@
 // If the dependency is not found, a missingErrorDependency is returned.
 // If the module dependency is not a SourceFileProducer or OutputFileProducer, appropriate errors will be returned.
 func getPathsFromModuleDep(ctx ModuleWithDepsPathContext, path, moduleName, tag string) (Paths, error) {
-	module := ctx.GetDirectDepWithTag(moduleName, sourceOrOutputDepTag(tag))
+	module := GetModuleFromPathDep(ctx, moduleName, tag)
 	if module == nil {
 		return nil, missingDependencyError{[]string{moduleName}}
 	}
@@ -474,6 +476,39 @@
 	}
 }
 
+// GetModuleFromPathDep will return the module that was added as a dependency automatically for
+// properties tagged with `android:"path"` or manually using ExtractSourceDeps or
+// ExtractSourcesDeps.
+//
+// The moduleName and tag supplied to this should be the values returned from SrcIsModuleWithTag.
+// Or, if no tag is expected then the moduleName should be the value returned by  SrcIsModule and
+// the tag must be "".
+//
+// If tag is "" then the returned module will be the dependency that was added for ":moduleName".
+// Otherwise, it is the dependency that was added for ":moduleName{tag}".
+func GetModuleFromPathDep(ctx ModuleWithDepsPathContext, moduleName, tag string) blueprint.Module {
+	var found blueprint.Module
+	// The sourceOrOutputDepTag uniquely identifies the module dependency as it contains both the
+	// module name and the tag. Dependencies added automatically for properties tagged with
+	// `android:"path"` are deduped so are guaranteed to be unique. It is possible for duplicate
+	// dependencies to be added manually using ExtractSourcesDeps or ExtractSourceDeps but even then
+	// it will always be the case that the dependencies will be identical, i.e. the same tag and same
+	// moduleName referring to the same dependency module.
+	//
+	// It does not matter whether the moduleName is a fully qualified name or if the module
+	// dependency is a prebuilt module. All that matters is the same information is supplied to
+	// create the tag here as was supplied to create the tag when the dependency was added so that
+	// this finds the matching dependency module.
+	expectedTag := sourceOrOutputDepTag(moduleName, tag)
+	ctx.VisitDirectDepsBlueprint(func(module blueprint.Module) {
+		depTag := ctx.OtherModuleDependencyTag(module)
+		if depTag == expectedTag {
+			found = module
+		}
+	})
+	return found
+}
+
 // PathsAndMissingDepsForModuleSrcExcludes returns a Paths{} containing the resolved references in
 // paths, minus those listed in excludes. Elements of paths and excludes are resolved as:
 // * filepath, relative to local module directory, resolves as a filepath relative to the local
@@ -587,7 +622,7 @@
 // It intended for use in globs that only list files that exist, so it allows '$' in
 // filenames.
 func pathsForModuleSrcFromFullPath(ctx EarlyModulePathContext, paths []string, incDirs bool) Paths {
-	prefix := filepath.Join(ctx.Config().srcDir, ctx.ModuleDir()) + "/"
+	prefix := ctx.ModuleDir() + "/"
 	if prefix == "./" {
 		prefix = ""
 	}
@@ -623,7 +658,7 @@
 	}
 	// Use Glob so that if the default doesn't exist, a dependency is added so that when it
 	// is created, we're run again.
-	path := filepath.Join(ctx.Config().srcDir, ctx.ModuleDir(), def)
+	path := filepath.Join(ctx.ModuleDir(), def)
 	return Glob(ctx, path, nil)
 }
 
@@ -951,13 +986,13 @@
 // code that is embedding ninja variables in paths
 func safePathForSource(ctx PathContext, pathComponents ...string) (SourcePath, error) {
 	p, err := validateSafePath(pathComponents...)
-	ret := SourcePath{basePath{p, ""}, ctx.Config().srcDir}
+	ret := SourcePath{basePath{p, ""}, "."}
 	if err != nil {
 		return ret, err
 	}
 
 	// absolute path already checked by validateSafePath
-	if strings.HasPrefix(ret.String(), ctx.Config().buildDir) {
+	if strings.HasPrefix(ret.String(), ctx.Config().soongOutDir) {
 		return ret, fmt.Errorf("source path %q is in output", ret.String())
 	}
 
@@ -967,13 +1002,13 @@
 // pathForSource creates a SourcePath from pathComponents, but does not check that it exists.
 func pathForSource(ctx PathContext, pathComponents ...string) (SourcePath, error) {
 	p, err := validatePath(pathComponents...)
-	ret := SourcePath{basePath{p, ""}, ctx.Config().srcDir}
+	ret := SourcePath{basePath{p, ""}, "."}
 	if err != nil {
 		return ret, err
 	}
 
 	// absolute path already checked by validatePath
-	if strings.HasPrefix(ret.String(), ctx.Config().buildDir) {
+	if strings.HasPrefix(ret.String(), ctx.Config().soongOutDir) {
 		return ret, fmt.Errorf("source path %q is in output", ret.String())
 	}
 
@@ -1115,8 +1150,8 @@
 type OutputPath struct {
 	basePath
 
-	// The soong build directory, i.e. Config.BuildDir()
-	buildDir string
+	// The soong build directory, i.e. Config.SoongOutDir()
+	soongOutDir string
 
 	fullPath string
 }
@@ -1132,8 +1167,8 @@
 	return p
 }
 
-func (p OutputPath) getBuildDir() string {
-	return p.buildDir
+func (p OutputPath) getSoongOutDir() string {
+	return p.soongOutDir
 }
 
 func (p OutputPath) RelativeToTop() Path {
@@ -1141,8 +1176,8 @@
 }
 
 func (p OutputPath) outputPathRelativeToTop() OutputPath {
-	p.fullPath = StringPathRelativeToTop(p.buildDir, p.fullPath)
-	p.buildDir = OutSoongDir
+	p.fullPath = StringPathRelativeToTop(p.soongOutDir, p.fullPath)
+	p.soongOutDir = OutSoongDir
 	return p
 }
 
@@ -1183,12 +1218,12 @@
 	if err != nil {
 		reportPathError(ctx, err)
 	}
-	fullPath := filepath.Join(ctx.Config().buildDir, path)
+	fullPath := filepath.Join(ctx.Config().soongOutDir, path)
 	path = fullPath[len(fullPath)-len(path):]
-	return OutputPath{basePath{path, ""}, ctx.Config().buildDir, fullPath}
+	return OutputPath{basePath{path, ""}, ctx.Config().soongOutDir, fullPath}
 }
 
-// PathsForOutput returns Paths rooted from buildDir
+// PathsForOutput returns Paths rooted from soongOutDir
 func PathsForOutput(ctx PathContext, paths []string) WritablePaths {
 	ret := make(WritablePaths, len(paths))
 	for i, path := range paths {
@@ -1252,10 +1287,11 @@
 // PathForModuleSrc returns a Path representing the paths... under the
 // module's local source directory.
 func PathForModuleSrc(ctx ModuleMissingDepsPathContext, pathComponents ...string) Path {
-	p, err := validatePath(pathComponents...)
-	if err != nil {
-		reportPathError(ctx, err)
-	}
+	// Just join the components textually just to make sure that it does not corrupt a fully qualified
+	// module reference, e.g. if the pathComponents is "://other:foo" then using filepath.Join() or
+	// validatePath() will corrupt it, e.g. replace "//" with "/". If the path is not a module
+	// reference then it will be validated by expandOneSrcPath anyway when it calls expandOneSrcPath.
+	p := strings.Join(pathComponents, string(filepath.Separator))
 	paths, err := expandOneSrcPath(ctx, p, nil)
 	if err != nil {
 		if depErr, ok := err.(missingDependencyError); ok {
@@ -1508,8 +1544,8 @@
 type InstallPath struct {
 	basePath
 
-	// The soong build directory, i.e. Config.BuildDir()
-	buildDir string
+	// The soong build directory, i.e. Config.SoongOutDir()
+	soongOutDir string
 
 	// partitionDir is the part of the InstallPath that is automatically determined according to the context.
 	// For example, it is host/<os>-<arch> for host modules, and target/product/<device>/<partition> for device modules.
@@ -1529,12 +1565,12 @@
 
 func (p InstallPath) RelativeToTop() Path {
 	ensureTestOnly()
-	p.buildDir = OutSoongDir
+	p.soongOutDir = OutSoongDir
 	return p
 }
 
-func (p InstallPath) getBuildDir() string {
-	return p.buildDir
+func (p InstallPath) getSoongOutDir() string {
+	return p.soongOutDir
 }
 
 func (p InstallPath) ReplaceExtension(ctx PathContext, ext string) OutputPath {
@@ -1549,9 +1585,9 @@
 func (p InstallPath) String() string {
 	if p.makePath {
 		// Make path starts with out/ instead of out/soong.
-		return filepath.Join(p.buildDir, "../", p.path)
+		return filepath.Join(p.soongOutDir, "../", p.path)
 	} else {
-		return filepath.Join(p.buildDir, p.path)
+		return filepath.Join(p.soongOutDir, p.path)
 	}
 }
 
@@ -1560,9 +1596,9 @@
 // The ./soong is dropped if the install path is for Make.
 func (p InstallPath) PartitionDir() string {
 	if p.makePath {
-		return filepath.Join(p.buildDir, "../", p.partitionDir)
+		return filepath.Join(p.soongOutDir, "../", p.partitionDir)
 	} else {
-		return filepath.Join(p.buildDir, p.partitionDir)
+		return filepath.Join(p.soongOutDir, p.partitionDir)
 	}
 }
 
@@ -1591,6 +1627,18 @@
 // PathForModuleInstall returns a Path representing the install path for the
 // module appended with paths...
 func PathForModuleInstall(ctx ModuleInstallPathContext, pathComponents ...string) InstallPath {
+	os, arch := osAndArch(ctx)
+	partition := modulePartition(ctx, os)
+	return makePathForInstall(ctx, os, arch, partition, ctx.Debug(), pathComponents...)
+}
+
+// PathForModuleInPartitionInstall is similar to PathForModuleInstall but partition is provided by the caller
+func PathForModuleInPartitionInstall(ctx ModuleInstallPathContext, partition string, pathComponents ...string) InstallPath {
+	os, arch := osAndArch(ctx)
+	return makePathForInstall(ctx, os, arch, partition, ctx.Debug(), pathComponents...)
+}
+
+func osAndArch(ctx ModuleInstallPathContext) (OsType, ArchType) {
 	os := ctx.Os()
 	arch := ctx.Arch().ArchType
 	forceOS, forceArch := ctx.InstallForceOS()
@@ -1600,14 +1648,14 @@
 	if forceArch != nil {
 		arch = *forceArch
 	}
-	partition := modulePartition(ctx, os)
+	return os, arch
+}
 
-	ret := pathForInstall(ctx, os, arch, partition, ctx.Debug(), pathComponents...)
-
+func makePathForInstall(ctx ModuleInstallPathContext, os OsType, arch ArchType, partition string, debug bool, pathComponents ...string) InstallPath {
+	ret := pathForInstall(ctx, os, arch, partition, debug, pathComponents...)
 	if ctx.InstallBypassMake() && ctx.Config().KatiEnabled() {
 		ret = ret.ToMakePath()
 	}
-
 	return ret
 }
 
@@ -1620,7 +1668,7 @@
 		partionPaths = []string{"target", "product", ctx.Config().DeviceName(), partition}
 	} else {
 		osName := os.String()
-		if os == Linux {
+		if os == Linux || os == LinuxMusl {
 			// instead of linux_glibc
 			osName = "linux"
 		}
@@ -1646,7 +1694,7 @@
 
 	base := InstallPath{
 		basePath:     basePath{partionPath, ""},
-		buildDir:     ctx.Config().buildDir,
+		soongOutDir:  ctx.Config().soongOutDir,
 		partitionDir: partionPath,
 		makePath:     false,
 	}
@@ -1657,7 +1705,7 @@
 func pathForNdkOrSdkInstall(ctx PathContext, prefix string, paths []string) InstallPath {
 	base := InstallPath{
 		basePath:     basePath{prefix, ""},
-		buildDir:     ctx.Config().buildDir,
+		soongOutDir:  ctx.Config().soongOutDir,
 		partitionDir: prefix,
 		makePath:     false,
 	}
@@ -1803,7 +1851,7 @@
 
 func (p PhonyPath) writablePath() {}
 
-func (p PhonyPath) getBuildDir() string {
+func (p PhonyPath) getSoongOutDir() string {
 	// A phone path cannot contain any / so cannot be relative to the build directory.
 	return ""
 }
@@ -1990,6 +2038,10 @@
 
 func CreateOutputDirIfNonexistent(path WritablePath, perm os.FileMode) error {
 	dir := absolutePath(path.String())
+	return createDirIfNonexistent(dir, perm)
+}
+
+func createDirIfNonexistent(dir string, perm os.FileMode) error {
 	if _, err := os.Stat(dir); os.IsNotExist(err) {
 		return os.MkdirAll(dir, os.ModePerm)
 	} else {
@@ -1997,6 +2049,9 @@
 	}
 }
 
+// absolutePath is deliberately private so that Soong's Go plugins can't use it to find and
+// read arbitrary files without going through the methods in the current package that track
+// dependencies.
 func absolutePath(path string) string {
 	if filepath.IsAbs(path) {
 		return path
@@ -2040,3 +2095,25 @@
 	}
 	return ret
 }
+
+var thirdPartyDirPrefixExceptions = []*regexp.Regexp{
+	regexp.MustCompile("^vendor/[^/]*google[^/]*/"),
+	regexp.MustCompile("^hardware/google/"),
+	regexp.MustCompile("^hardware/interfaces/"),
+	regexp.MustCompile("^hardware/libhardware[^/]*/"),
+	regexp.MustCompile("^hardware/ril/"),
+}
+
+func IsThirdPartyPath(path string) bool {
+	thirdPartyDirPrefixes := []string{"external/", "vendor/", "hardware/"}
+
+	if HasAnyPrefix(path, thirdPartyDirPrefixes) {
+		for _, prefix := range thirdPartyDirPrefixExceptions {
+			if prefix.MatchString(path) {
+				return false
+			}
+		}
+		return true
+	}
+	return false
+}
diff --git a/android/paths_test.go b/android/paths_test.go
index 6f5d79e..f4e4ce1 100644
--- a/android/paths_test.go
+++ b/android/paths_test.go
@@ -1125,6 +1125,12 @@
 	rels []string
 	src  string
 	rel  string
+
+	// Make test specific preparations to the test fixture.
+	preparer FixturePreparer
+
+	// A test specific error handler.
+	errorHandler FixtureErrorHandler
 }
 
 func testPathForModuleSrc(t *testing.T, tests []pathForModuleSrcTestCase) {
@@ -1157,14 +1163,23 @@
 				"foo/src_special/$": nil,
 			}
 
+			errorHandler := test.errorHandler
+			if errorHandler == nil {
+				errorHandler = FixtureExpectsNoErrors
+			}
+
 			result := GroupFixturePreparers(
 				FixtureRegisterWithContext(func(ctx RegistrationContext) {
 					ctx.RegisterModuleType("test", pathForModuleSrcTestModuleFactory)
 					ctx.RegisterModuleType("output_file_provider", pathForModuleSrcOutputFileProviderModuleFactory)
-					ctx.RegisterModuleType("filegroup", FileGroupFactory)
 				}),
+				PrepareForTestWithFilegroup,
+				PrepareForTestWithNamespace,
 				mockFS.AddToFixture(),
-			).RunTest(t)
+				OptionalFixturePreparer(test.preparer),
+			).
+				ExtendWithErrorHandler(errorHandler).
+				RunTest(t)
 
 			m := result.ModuleForTests("foo", "").Module().(*pathForModuleSrcTestModule)
 
@@ -1333,6 +1348,73 @@
 			src: "foo/src_special/$",
 			rel: "src_special/$",
 		},
+		{
+			// This test makes sure that an unqualified module name cannot contain characters that make
+			// it appear as a qualified module name.
+			name: "output file provider, invalid fully qualified name",
+			bp: `
+			test {
+				name: "foo",
+				src: "://other:b",
+				srcs: ["://other:c"],
+			}`,
+			preparer: FixtureAddTextFile("other/Android.bp", `
+				soong_namespace {}
+
+				output_file_provider {
+					name: "b",
+					outs: ["gen/b"],
+				}
+
+				output_file_provider {
+					name: "c",
+					outs: ["gen/c"],
+				}
+			`),
+			src:  "foo/:/other:b",
+			rel:  ":/other:b",
+			srcs: []string{"foo/:/other:c"},
+			rels: []string{":/other:c"},
+		},
+		{
+			name: "output file provider, missing fully qualified name",
+			bp: `
+			test {
+				name: "foo",
+				src: "//other:b",
+				srcs: ["//other:c"],
+			}`,
+			errorHandler: FixtureExpectsAllErrorsToMatchAPattern([]string{
+				`"foo" depends on undefined module "//other:b"`,
+				`"foo" depends on undefined module "//other:c"`,
+			}),
+		},
+		{
+			name: "output file provider, fully qualified name",
+			bp: `
+			test {
+				name: "foo",
+				src: "//other:b",
+				srcs: ["//other:c"],
+			}`,
+			src:  "out/soong/.intermediates/other/b/gen/b",
+			rel:  "gen/b",
+			srcs: []string{"out/soong/.intermediates/other/c/gen/c"},
+			rels: []string{"gen/c"},
+			preparer: FixtureAddTextFile("other/Android.bp", `
+				soong_namespace {}
+
+				output_file_provider {
+					name: "b",
+					outs: ["gen/b"],
+				}
+
+				output_file_provider {
+					name: "c",
+					outs: ["gen/c"],
+				}
+			`),
+		},
 	}
 
 	testPathForModuleSrc(t, tests)
diff --git a/android/prebuilt.go b/android/prebuilt.go
index f3493bd..e611502 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -61,6 +61,16 @@
 	// a matching name.
 	Prefer *bool `android:"arch_variant"`
 
+	// When specified this names a Soong config variable that controls the prefer property.
+	//
+	// If the value of the named Soong config variable is true then prefer is set to false and vice
+	// versa. If the Soong config variable is not set then it defaults to false, so prefer defaults
+	// to true.
+	//
+	// If specified then the prefer property is ignored in favor of the value of the Soong config
+	// variable.
+	Use_source_config_var *ConfigVarProperties
+
 	SourceExists bool `blueprint:"mutated"`
 	UsePrebuilt  bool `blueprint:"mutated"`
 
@@ -68,6 +78,22 @@
 	PrebuiltRenamedToSource bool `blueprint:"mutated"`
 }
 
+// Properties that can be used to select a Soong config variable.
+type ConfigVarProperties struct {
+	// Allow instances of this struct to be used as a property value in a BpPropertySet.
+	BpPrintableBase
+
+	// The name of the configuration namespace.
+	//
+	// As passed to add_soong_config_namespace in Make.
+	Config_namespace *string
+
+	// The name of the configuration variable.
+	//
+	// As passed to add_soong_config_var_value in Make.
+	Var_name *string
+}
+
 type Prebuilt struct {
 	properties PrebuiltProperties
 
@@ -364,12 +390,18 @@
 		return false
 	}
 
-	// TODO: use p.Properties.Name and ctx.ModuleDir to override preference
-	if Bool(p.properties.Prefer) {
+	// If source is not available or is disabled then always use the prebuilt.
+	if source == nil || !source.Enabled() {
 		return true
 	}
 
-	return source == nil || !source.Enabled()
+	// If the use_source_config_var property is set then it overrides the prefer property setting.
+	if configVar := p.properties.Use_source_config_var; configVar != nil {
+		return !ctx.Config().VendorConfig(proptools.String(configVar.Config_namespace)).Bool(proptools.String(configVar.Var_name))
+	}
+
+	// TODO: use p.Properties.Name and ctx.ModuleDir to override preference
+	return Bool(p.properties.Prefer)
 }
 
 func (p *Prebuilt) SourceExists() bool {
diff --git a/android/prebuilt_build_tool.go b/android/prebuilt_build_tool.go
index 516d042..e5edf91 100644
--- a/android/prebuilt_build_tool.go
+++ b/android/prebuilt_build_tool.go
@@ -86,7 +86,7 @@
 
 func (t *prebuiltBuildTool) MakeVars(ctx MakeVarsModuleContext) {
 	if makeVar := String(t.properties.Export_to_make_var); makeVar != "" {
-		if t.Target().Os != BuildOs {
+		if t.Target().Os != ctx.Config().BuildOS {
 			return
 		}
 		ctx.StrictRaw(makeVar, t.toolPath.String())
diff --git a/android/prebuilt_test.go b/android/prebuilt_test.go
index 23524a5..a1f8e63 100644
--- a/android/prebuilt_test.go
+++ b/android/prebuilt_test.go
@@ -21,279 +21,362 @@
 	"github.com/google/blueprint"
 )
 
-var prebuiltsTests = []struct {
-	name      string
-	replaceBp bool // modules is added to default bp boilerplate if false.
-	modules   string
-	prebuilt  []OsType
-}{
-	{
-		name: "no prebuilt",
-		modules: `
-			source {
-				name: "bar",
-			}`,
-		prebuilt: nil,
-	},
-	{
-		name: "no source prebuilt not preferred",
-		modules: `
-			prebuilt {
-				name: "bar",
-				prefer: false,
-				srcs: ["prebuilt_file"],
-			}`,
-		prebuilt: []OsType{Android, BuildOs},
-	},
-	{
-		name: "no source prebuilt preferred",
-		modules: `
-			prebuilt {
-				name: "bar",
-				prefer: true,
-				srcs: ["prebuilt_file"],
-			}`,
-		prebuilt: []OsType{Android, BuildOs},
-	},
-	{
-		name: "prebuilt not preferred",
-		modules: `
-			source {
-				name: "bar",
-			}
+func TestPrebuilts(t *testing.T) {
+	buildOS := TestArchConfig(t.TempDir(), nil, "", nil).BuildOS
 
-			prebuilt {
-				name: "bar",
-				prefer: false,
-				srcs: ["prebuilt_file"],
-			}`,
-		prebuilt: nil,
-	},
-	{
-		name: "prebuilt preferred",
-		modules: `
-			source {
-				name: "bar",
-			}
+	var prebuiltsTests = []struct {
+		name      string
+		replaceBp bool // modules is added to default bp boilerplate if false.
+		modules   string
+		prebuilt  []OsType
+		preparer  FixturePreparer
+	}{
+		{
+			name: "no prebuilt",
+			modules: `
+				source {
+					name: "bar",
+				}`,
+			prebuilt: nil,
+		},
+		{
+			name: "no source prebuilt not preferred",
+			modules: `
+				prebuilt {
+					name: "bar",
+					prefer: false,
+					srcs: ["prebuilt_file"],
+				}`,
+			prebuilt: []OsType{Android, buildOS},
+		},
+		{
+			name: "no source prebuilt preferred",
+			modules: `
+				prebuilt {
+					name: "bar",
+					prefer: true,
+					srcs: ["prebuilt_file"],
+				}`,
+			prebuilt: []OsType{Android, buildOS},
+		},
+		{
+			name: "prebuilt not preferred",
+			modules: `
+				source {
+					name: "bar",
+				}
 
-			prebuilt {
-				name: "bar",
-				prefer: true,
-				srcs: ["prebuilt_file"],
-			}`,
-		prebuilt: []OsType{Android, BuildOs},
-	},
-	{
-		name: "prebuilt no file not preferred",
-		modules: `
-			source {
-				name: "bar",
-			}
+				prebuilt {
+					name: "bar",
+					prefer: false,
+					srcs: ["prebuilt_file"],
+				}`,
+			prebuilt: nil,
+		},
+		{
+			name: "prebuilt preferred",
+			modules: `
+				source {
+					name: "bar",
+				}
 
-			prebuilt {
-				name: "bar",
-				prefer: false,
-			}`,
-		prebuilt: nil,
-	},
-	{
-		name: "prebuilt no file preferred",
-		modules: `
-			source {
-				name: "bar",
-			}
+				prebuilt {
+					name: "bar",
+					prefer: true,
+					srcs: ["prebuilt_file"],
+				}`,
+			prebuilt: []OsType{Android, buildOS},
+		},
+		{
+			name: "prebuilt no file not preferred",
+			modules: `
+				source {
+					name: "bar",
+				}
 
-			prebuilt {
-				name: "bar",
-				prefer: true,
-			}`,
-		prebuilt: nil,
-	},
-	{
-		name: "prebuilt file from filegroup preferred",
-		modules: `
-			filegroup {
-				name: "fg",
-				srcs: ["prebuilt_file"],
-			}
-			prebuilt {
-				name: "bar",
-				prefer: true,
-				srcs: [":fg"],
-			}`,
-		prebuilt: []OsType{Android, BuildOs},
-	},
-	{
-		name: "prebuilt module for device only",
-		modules: `
-			source {
-				name: "bar",
-			}
+				prebuilt {
+					name: "bar",
+					prefer: false,
+				}`,
+			prebuilt: nil,
+		},
+		{
+			name: "prebuilt no file preferred",
+			modules: `
+				source {
+					name: "bar",
+				}
 
-			prebuilt {
-				name: "bar",
-				host_supported: false,
-				prefer: true,
-				srcs: ["prebuilt_file"],
-			}`,
-		prebuilt: []OsType{Android},
-	},
-	{
-		name: "prebuilt file for host only",
-		modules: `
-			source {
-				name: "bar",
-			}
+				prebuilt {
+					name: "bar",
+					prefer: true,
+				}`,
+			prebuilt: nil,
+		},
+		{
+			name: "prebuilt file from filegroup preferred",
+			modules: `
+				filegroup {
+					name: "fg",
+					srcs: ["prebuilt_file"],
+				}
+				prebuilt {
+					name: "bar",
+					prefer: true,
+					srcs: [":fg"],
+				}`,
+			prebuilt: []OsType{Android, buildOS},
+		},
+		{
+			name: "prebuilt module for device only",
+			modules: `
+				source {
+					name: "bar",
+				}
 
-			prebuilt {
-				name: "bar",
-				prefer: true,
-				target: {
-					host: {
-						srcs: ["prebuilt_file"],
-					},
-				},
-			}`,
-		prebuilt: []OsType{BuildOs},
-	},
-	{
-		name: "prebuilt override not preferred",
-		modules: `
-			source {
-				name: "baz",
-			}
+				prebuilt {
+					name: "bar",
+					host_supported: false,
+					prefer: true,
+					srcs: ["prebuilt_file"],
+				}`,
+			prebuilt: []OsType{Android},
+		},
+		{
+			name: "prebuilt file for host only",
+			modules: `
+				source {
+					name: "bar",
+				}
 
-			override_source {
-				name: "bar",
-				base: "baz",
-			}
-
-			prebuilt {
-				name: "bar",
-				prefer: false,
-				srcs: ["prebuilt_file"],
-			}`,
-		prebuilt: nil,
-	},
-	{
-		name: "prebuilt override preferred",
-		modules: `
-			source {
-				name: "baz",
-			}
-
-			override_source {
-				name: "bar",
-				base: "baz",
-			}
-
-			prebuilt {
-				name: "bar",
-				prefer: true,
-				srcs: ["prebuilt_file"],
-			}`,
-		prebuilt: []OsType{Android, BuildOs},
-	},
-	{
-		name:      "prebuilt including default-disabled OS",
-		replaceBp: true,
-		modules: `
-			source {
-				name: "foo",
-				deps: [":bar"],
-				target: {
-					windows: {
-						enabled: true,
-					},
-				},
-			}
-
-			source {
-				name: "bar",
-				target: {
-					windows: {
-						enabled: true,
-					},
-				},
-			}
-
-			prebuilt {
-				name: "bar",
-				prefer: true,
-				srcs: ["prebuilt_file"],
-				target: {
-					windows: {
-						enabled: true,
-					},
-				},
-			}`,
-		prebuilt: []OsType{Android, BuildOs, Windows},
-	},
-	{
-		name:      "fall back to source for default-disabled OS",
-		replaceBp: true,
-		modules: `
-			source {
-				name: "foo",
-				deps: [":bar"],
-				target: {
-					windows: {
-						enabled: true,
-					},
-				},
-			}
-
-			source {
-				name: "bar",
-				target: {
-					windows: {
-						enabled: true,
-					},
-				},
-			}
-
-			prebuilt {
-				name: "bar",
-				prefer: true,
-				srcs: ["prebuilt_file"],
-			}`,
-		prebuilt: []OsType{Android, BuildOs},
-	},
-	{
-		name:      "prebuilt properties customizable",
-		replaceBp: true,
-		modules: `
-			source {
-				name: "foo",
-				deps: [":bar"],
-			}
-
-			soong_config_module_type {
-				name: "prebuilt_with_config",
-				module_type: "prebuilt",
-				config_namespace: "any_namespace",
-				bool_variables: ["bool_var"],
-				properties: ["prefer"],
-			}
-
-			prebuilt_with_config {
-				name: "bar",
-				prefer: true,
-				srcs: ["prebuilt_file"],
-				soong_config_variables: {
-					bool_var: {
-						prefer: false,
-						conditions_default: {
-							prefer: true,
+				prebuilt {
+					name: "bar",
+					prefer: true,
+					target: {
+						host: {
+							srcs: ["prebuilt_file"],
 						},
 					},
-				},
-			}`,
-		prebuilt: []OsType{Android, BuildOs},
-	},
-}
+				}`,
+			prebuilt: []OsType{buildOS},
+		},
+		{
+			name: "prebuilt override not preferred",
+			modules: `
+				source {
+					name: "baz",
+				}
 
-func TestPrebuilts(t *testing.T) {
+				override_source {
+					name: "bar",
+					base: "baz",
+				}
+
+				prebuilt {
+					name: "bar",
+					prefer: false,
+					srcs: ["prebuilt_file"],
+				}`,
+			prebuilt: nil,
+		},
+		{
+			name: "prebuilt override preferred",
+			modules: `
+				source {
+					name: "baz",
+				}
+
+				override_source {
+					name: "bar",
+					base: "baz",
+				}
+
+				prebuilt {
+					name: "bar",
+					prefer: true,
+					srcs: ["prebuilt_file"],
+				}`,
+			prebuilt: []OsType{Android, buildOS},
+		},
+		{
+			name:      "prebuilt including default-disabled OS",
+			replaceBp: true,
+			modules: `
+				source {
+					name: "foo",
+					deps: [":bar"],
+					target: {
+						windows: {
+							enabled: true,
+						},
+					},
+				}
+
+				source {
+					name: "bar",
+					target: {
+						windows: {
+							enabled: true,
+						},
+					},
+				}
+
+				prebuilt {
+					name: "bar",
+					prefer: true,
+					srcs: ["prebuilt_file"],
+					target: {
+						windows: {
+							enabled: true,
+						},
+					},
+				}`,
+			prebuilt: []OsType{Android, buildOS, Windows},
+		},
+		{
+			name:      "fall back to source for default-disabled OS",
+			replaceBp: true,
+			modules: `
+				source {
+					name: "foo",
+					deps: [":bar"],
+					target: {
+						windows: {
+							enabled: true,
+						},
+					},
+				}
+
+				source {
+					name: "bar",
+					target: {
+						windows: {
+							enabled: true,
+						},
+					},
+				}
+
+				prebuilt {
+					name: "bar",
+					prefer: true,
+					srcs: ["prebuilt_file"],
+				}`,
+			prebuilt: []OsType{Android, buildOS},
+		},
+		{
+			name:      "prebuilt properties customizable",
+			replaceBp: true,
+			modules: `
+				source {
+					name: "foo",
+					deps: [":bar"],
+				}
+
+				soong_config_module_type {
+					name: "prebuilt_with_config",
+					module_type: "prebuilt",
+					config_namespace: "any_namespace",
+					bool_variables: ["bool_var"],
+					properties: ["prefer"],
+				}
+
+				prebuilt_with_config {
+					name: "bar",
+					prefer: true,
+					srcs: ["prebuilt_file"],
+					soong_config_variables: {
+						bool_var: {
+							prefer: false,
+							conditions_default: {
+								prefer: true,
+							},
+						},
+					},
+				}`,
+			prebuilt: []OsType{Android, buildOS},
+		},
+		{
+			name: "prebuilt use_source_config_var={acme, use_source} - no var specified",
+			modules: `
+				source {
+					name: "bar",
+				}
+
+				prebuilt {
+					name: "bar",
+					use_source_config_var: {config_namespace: "acme", var_name: "use_source"},
+					srcs: ["prebuilt_file"],
+				}`,
+			// When use_source_env is specified then it will use the prebuilt by default if the environment
+			// variable is not set.
+			prebuilt: []OsType{Android, buildOS},
+		},
+		{
+			name: "prebuilt use_source_config_var={acme, use_source} - acme_use_source=false",
+			modules: `
+				source {
+					name: "bar",
+				}
+
+				prebuilt {
+					name: "bar",
+					use_source_config_var: {config_namespace: "acme", var_name: "use_source"},
+					srcs: ["prebuilt_file"],
+				}`,
+			preparer: FixtureModifyProductVariables(func(variables FixtureProductVariables) {
+				variables.VendorVars = map[string]map[string]string{
+					"acme": {
+						"use_source": "false",
+					},
+				}
+			}),
+			// Setting the environment variable named in use_source_env to false will cause the prebuilt to
+			// be used.
+			prebuilt: []OsType{Android, buildOS},
+		},
+		{
+			name: "prebuilt use_source_config_var={acme, use_source} - acme_use_source=true",
+			modules: `
+				source {
+					name: "bar",
+				}
+
+				prebuilt {
+					name: "bar",
+					use_source_config_var: {config_namespace: "acme", var_name: "use_source"},
+					srcs: ["prebuilt_file"],
+				}`,
+			preparer: FixtureModifyProductVariables(func(variables FixtureProductVariables) {
+				variables.VendorVars = map[string]map[string]string{
+					"acme": {
+						"use_source": "true",
+					},
+				}
+			}),
+			// Setting the environment variable named in use_source_env to true will cause the source to be
+			// used.
+			prebuilt: nil,
+		},
+		{
+			name: "prebuilt use_source_config_var={acme, use_source} - acme_use_source=true, no source",
+			modules: `
+				prebuilt {
+					name: "bar",
+					use_source_config_var: {config_namespace: "acme", var_name: "use_source"},
+					srcs: ["prebuilt_file"],
+				}`,
+			preparer: FixtureModifyProductVariables(func(variables FixtureProductVariables) {
+				variables.VendorVars = map[string]map[string]string{
+					"acme": {
+						"use_source": "true",
+					},
+				}
+			}),
+			// Although the environment variable says to use source there is no source available.
+			prebuilt: []OsType{Android, buildOS},
+		},
+	}
+
 	fs := MockFS{
 		"prebuilt_file": nil,
 		"source_file":   nil,
@@ -329,6 +412,7 @@
 				}),
 				fs.AddToFixture(),
 				FixtureRegisterWithContext(registerTestPrebuiltModules),
+				OptionalFixturePreparer(test.preparer),
 			).RunTestWithBp(t, bp)
 
 			for _, variant := range result.ModuleVariantsForTests("foo") {
diff --git a/android/register.go b/android/register.go
index 4c8088d..5984862 100644
--- a/android/register.go
+++ b/android/register.go
@@ -180,7 +180,7 @@
 		bp2buildMutatorList = append(bp2buildMutatorList, f)
 	}
 
-	RegisterMutatorsForBazelConversion(ctx, bp2buildPreArchMutators, bp2buildDepsMutators, bp2buildMutatorList)
+	RegisterMutatorsForBazelConversion(ctx, bp2buildPreArchMutators, bp2buildMutatorList)
 }
 
 // Register the pipeline of singletons, module types, and mutators for
diff --git a/android/rule_builder.go b/android/rule_builder.go
index 2507c4c..6605869 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -22,9 +22,10 @@
 	"strings"
 	"testing"
 
-	"github.com/golang/protobuf/proto"
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
+	"google.golang.org/protobuf/encoding/prototext"
+	"google.golang.org/protobuf/proto"
 
 	"android/soong/cmd/sbox/sbox_proto"
 	"android/soong/remoteexec"
@@ -621,7 +622,11 @@
 		}
 
 		// Create a rule to write the manifest as a the textproto.
-		WriteFileRule(r.ctx, r.sboxManifestPath, proto.MarshalTextString(&manifest))
+		pbText, err := prototext.Marshal(&manifest)
+		if err != nil {
+			ReportPathErrorf(r.ctx, "sbox manifest failed to marshal: %q", err)
+		}
+		WriteFileRule(r.ctx, r.sboxManifestPath, string(pbText))
 
 		// Generate a new string to use as the command line of the sbox rule.  This uses
 		// a RuleBuilderCommand as a convenience method of building the command line, then
@@ -1266,7 +1271,7 @@
 	t.Helper()
 	content := ContentFromFileRuleForTests(t, params)
 	manifest := sbox_proto.Manifest{}
-	err := proto.UnmarshalText(content, &manifest)
+	err := prototext.Unmarshal([]byte(content), &manifest)
 	if err != nil {
 		t.Fatalf("failed to unmarshal manifest: %s", err.Error())
 	}
diff --git a/android/sdk.go b/android/sdk.go
index 42c8ffa..b8f76c1 100644
--- a/android/sdk.go
+++ b/android/sdk.go
@@ -239,6 +239,12 @@
 	// to the zip
 	CopyToSnapshot(src Path, dest string)
 
+	// Return the path to an empty file.
+	//
+	// This can be used by sdk member types that need to create an empty file in the snapshot, simply
+	// pass the value returned from this to the CopyToSnapshot() method.
+	EmptyFile() Path
+
 	// Unzip the supplied zip into the snapshot relative directory destDir.
 	UnzipToSnapshot(zipPath Path, destDir string)
 
@@ -342,6 +348,22 @@
 	Name() string
 }
 
+// BpPrintable is a marker interface that must be implemented by any struct that is added as a
+// property value.
+type BpPrintable interface {
+	bpPrintable()
+}
+
+// BpPrintableBase must be embedded within any struct that is added as a
+// property value.
+type BpPrintableBase struct {
+}
+
+func (b BpPrintableBase) bpPrintable() {
+}
+
+var _ BpPrintable = BpPrintableBase{}
+
 // An individual member of the SDK, includes all of the variants that the SDK
 // requires.
 type SdkMember interface {
@@ -379,26 +401,26 @@
 	ExportMember() bool
 }
 
-var _ SdkMemberTypeDependencyTag = (*sdkMemberDependencyTag)(nil)
-var _ ReplaceSourceWithPrebuilt = (*sdkMemberDependencyTag)(nil)
+var _ SdkMemberTypeDependencyTag = (*sdkMemberTypeDependencyTag)(nil)
+var _ ReplaceSourceWithPrebuilt = (*sdkMemberTypeDependencyTag)(nil)
 
-type sdkMemberDependencyTag struct {
+type sdkMemberTypeDependencyTag struct {
 	blueprint.BaseDependencyTag
 	memberType SdkMemberType
 	export     bool
 }
 
-func (t *sdkMemberDependencyTag) SdkMemberType(_ Module) SdkMemberType {
+func (t *sdkMemberTypeDependencyTag) SdkMemberType(_ Module) SdkMemberType {
 	return t.memberType
 }
 
-func (t *sdkMemberDependencyTag) ExportMember() bool {
+func (t *sdkMemberTypeDependencyTag) ExportMember() bool {
 	return t.export
 }
 
 // Prevent dependencies from the sdk/module_exports onto their members from being
 // replaced with a preferred prebuilt.
-func (t *sdkMemberDependencyTag) ReplaceSourceWithPrebuilt() bool {
+func (t *sdkMemberTypeDependencyTag) ReplaceSourceWithPrebuilt() bool {
 	return false
 }
 
@@ -406,7 +428,7 @@
 // dependencies added by the tag to be added to the sdk as the specified SdkMemberType and exported
 // (or not) as specified by the export parameter.
 func DependencyTagForSdkMemberType(memberType SdkMemberType, export bool) SdkMemberTypeDependencyTag {
-	return &sdkMemberDependencyTag{memberType: memberType, export: export}
+	return &sdkMemberTypeDependencyTag{memberType: memberType, export: export}
 }
 
 // Interface that must be implemented for every type that can be a member of an
@@ -453,7 +475,7 @@
 	// properties. The dependencies must be added with the supplied tag.
 	//
 	// The BottomUpMutatorContext provided is for the SDK module.
-	AddDependencies(mctx BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string)
+	AddDependencies(ctx SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string)
 
 	// Return true if the supplied module is an instance of this member type.
 	//
@@ -507,6 +529,12 @@
 	CreateVariantPropertiesStruct() SdkMemberProperties
 }
 
+// SdkDependencyContext provides access to information needed by the SdkMemberType.AddDependencies()
+// implementations.
+type SdkDependencyContext interface {
+	BottomUpMutatorContext
+}
+
 // Base type for SdkMemberType implementations.
 type SdkMemberTypeBase struct {
 	PropertyName string
@@ -548,9 +576,6 @@
 type SdkMemberTypesRegistry struct {
 	// The list of types sorted by property name.
 	list []SdkMemberType
-
-	// The key that uniquely identifies this registry instance.
-	key OnceKey
 }
 
 func (r *SdkMemberTypesRegistry) copyAndAppend(memberType SdkMemberType) *SdkMemberTypesRegistry {
@@ -570,18 +595,9 @@
 		return t1.SdkPropertyName() < t2.SdkPropertyName()
 	})
 
-	// Generate a key that identifies the slice of SdkMemberTypes by joining the property names
-	// from all the SdkMemberType .
-	var properties []string
-	for _, t := range list {
-		properties = append(properties, t.SdkPropertyName())
-	}
-	key := NewOnceKey(strings.Join(properties, "|"))
-
 	// Create a new registry so the pointer uniquely identifies the set of registered types.
 	return &SdkMemberTypesRegistry{
 		list: list,
-		key:  key,
 	}
 }
 
@@ -594,8 +610,10 @@
 	return NewCustomOnceKey(r)
 }
 
-// The set of registered SdkMemberTypes, one for sdk module and one for module_exports.
+// The set of registered SdkMemberTypes for module_exports modules.
 var ModuleExportsMemberTypes = &SdkMemberTypesRegistry{}
+
+// The set of registered SdkMemberTypes for sdk modules.
 var SdkMemberTypes = &SdkMemberTypesRegistry{}
 
 // Register an SdkMemberType object to allow them to be used in the sdk and sdk_snapshot module
diff --git a/android/singleton.go b/android/singleton.go
index bb6614d..7ff96c9 100644
--- a/android/singleton.go
+++ b/android/singleton.go
@@ -54,10 +54,10 @@
 
 	RequireNinjaVersion(major, minor, micro int)
 
-	// SetNinjaBuildDir sets the value of the top-level "builddir" Ninja variable
+	// SetOutDir sets the value of the top-level "builddir" Ninja variable
 	// that controls where Ninja stores its build log files.  This value can be
 	// set at most one time for a single build, later calls are ignored.
-	SetNinjaBuildDir(pctx PackageContext, value string)
+	SetOutDir(pctx PackageContext, value string)
 
 	// Eval takes a string with embedded ninja variables, and returns a string
 	// with all of the variables recursively expanded. Any variables references
@@ -180,8 +180,8 @@
 	addPhony(s.Config(), name, deps...)
 }
 
-func (s *singletonContextAdaptor) SetNinjaBuildDir(pctx PackageContext, value string) {
-	s.SingletonContext.SetNinjaBuildDir(pctx.PackageContext, value)
+func (s *singletonContextAdaptor) SetOutDir(pctx PackageContext, value string) {
+	s.SingletonContext.SetOutDir(pctx.PackageContext, value)
 }
 
 func (s *singletonContextAdaptor) Eval(pctx PackageContext, ninjaStr string) (string, error) {
diff --git a/android/soong_config_modules.go b/android/soong_config_modules.go
index 289e910..17f6d66 100644
--- a/android/soong_config_modules.go
+++ b/android/soong_config_modules.go
@@ -122,15 +122,10 @@
 //     }
 //
 // If an acme BoardConfig.mk file contained:
-//
-//     SOONG_CONFIG_NAMESPACES += acme
-//     SOONG_CONFIG_acme += \
-//         board \
-//         feature \
-//
-//     SOONG_CONFIG_acme_board := soc_a
-//     SOONG_CONFIG_acme_feature := true
-//     SOONG_CONFIG_acme_width := 200
+//     $(call add_sonng_config_namespace, acme)
+//     $(call add_soong_config_var_value, acme, board, soc_a)
+//     $(call add_soong_config_var_value, acme, feature, true)
+//     $(call add_soong_config_var_value, acme, width, 200)
 //
 // Then libacme_foo would build with cflags "-DGENERIC -DSOC_A -DFEATURE -DWIDTH=200".
 //
diff --git a/android/soong_config_modules_test.go b/android/soong_config_modules_test.go
index 8f252d9..b2f8eaa 100644
--- a/android/soong_config_modules_test.go
+++ b/android/soong_config_modules_test.go
@@ -313,6 +313,51 @@
 	})
 }
 
+func TestNonExistentPropertyInSoongConfigModule(t *testing.T) {
+	bp := `
+		soong_config_module_type {
+			name: "acme_test",
+			module_type: "test",
+			config_namespace: "acme",
+			bool_variables: ["feature1"],
+			properties: ["made_up_property"],
+		}
+
+		acme_test {
+			name: "foo",
+			cflags: ["-DGENERIC"],
+			soong_config_variables: {
+				feature1: {
+					made_up_property: true,
+				},
+			},
+		}
+    `
+
+	fixtureForVendorVars := func(vars map[string]map[string]string) FixturePreparer {
+		return FixtureModifyProductVariables(func(variables FixtureProductVariables) {
+			variables.VendorVars = vars
+		})
+	}
+
+	GroupFixturePreparers(
+		fixtureForVendorVars(map[string]map[string]string{"acme": {"feature1": "1"}}),
+		PrepareForTestWithDefaults,
+		FixtureRegisterWithContext(func(ctx RegistrationContext) {
+			ctx.RegisterModuleType("soong_config_module_type_import", soongConfigModuleTypeImportFactory)
+			ctx.RegisterModuleType("soong_config_module_type", soongConfigModuleTypeFactory)
+			ctx.RegisterModuleType("soong_config_string_variable", soongConfigStringVariableDummyFactory)
+			ctx.RegisterModuleType("soong_config_bool_variable", soongConfigBoolVariableDummyFactory)
+			ctx.RegisterModuleType("test_defaults", soongConfigTestDefaultsModuleFactory)
+			ctx.RegisterModuleType("test", soongConfigTestModuleFactory)
+		}),
+		FixtureWithRootAndroidBp(bp),
+	).ExtendWithErrorHandler(FixtureExpectsAllErrorsToMatchAPattern([]string{
+		// TODO(b/171232169): improve the error message for non-existent properties
+		`unrecognized property "soong_config_variables`,
+	})).RunTest(t)
+}
+
 func testConfigWithVendorVars(buildDir, bp string, fs map[string][]byte, vendorVars map[string]map[string]string) Config {
 	config := TestConfig(buildDir, nil, bp, fs)
 
diff --git a/android/test_asserts.go b/android/test_asserts.go
index edeb408..064f656 100644
--- a/android/test_asserts.go
+++ b/android/test_asserts.go
@@ -77,14 +77,14 @@
 // StringPathRelativeToTop on the actual string path.
 func AssertStringPathRelativeToTopEquals(t *testing.T, message string, config Config, expected string, actual string) {
 	t.Helper()
-	AssertStringEquals(t, message, expected, StringPathRelativeToTop(config.buildDir, actual))
+	AssertStringEquals(t, message, expected, StringPathRelativeToTop(config.soongOutDir, actual))
 }
 
 // AssertStringPathsRelativeToTopEquals checks if the expected value is equal to the result of
 // calling StringPathsRelativeToTop on the actual string paths.
 func AssertStringPathsRelativeToTopEquals(t *testing.T, message string, config Config, expected []string, actual []string) {
 	t.Helper()
-	AssertDeepEquals(t, message, expected, StringPathsRelativeToTop(config.buildDir, actual))
+	AssertDeepEquals(t, message, expected, StringPathsRelativeToTop(config.soongOutDir, actual))
 }
 
 // AssertErrorMessageEquals checks if the error is not nil and has the expected message. If it does
diff --git a/android/test_suites.go b/android/test_suites.go
index 6b7b909..22f6cf2 100644
--- a/android/test_suites.go
+++ b/android/test_suites.go
@@ -60,7 +60,7 @@
 	for _, module := range SortedStringKeys(files) {
 		installedPaths = append(installedPaths, files[module]...)
 	}
-	testCasesDir := pathForInstall(ctx, BuildOs, X86, "testcases", false).ToMakePath()
+	testCasesDir := pathForInstall(ctx, ctx.Config().BuildOS, X86, "testcases", false).ToMakePath()
 
 	outputFile := PathForOutput(ctx, "packaging", "robolectric-tests.zip")
 	rule := NewRuleBuilder(pctx, ctx)
diff --git a/android/testing.go b/android/testing.go
index b36f62c..e25e5c5 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -110,6 +110,11 @@
 	FixtureAddFile("build/soong/licenses/LICENSE", nil),
 )
 
+var PrepareForTestWithNamespace = FixtureRegisterWithContext(func(ctx RegistrationContext) {
+	registerNamespaceBuildComponents(ctx)
+	ctx.PreArchMutators(RegisterNamespaceMutator)
+})
+
 // Test fixture preparer that will register most java build components.
 //
 // Singletons and mutators should only be added here if they are needed for a majority of java
@@ -166,9 +171,9 @@
 
 type TestContext struct {
 	*Context
-	preArch, preDeps, postDeps, finalDeps           []RegisterMutatorFunc
-	bp2buildPreArch, bp2buildDeps, bp2buildMutators []RegisterMutatorFunc
-	NameResolver                                    *NameResolver
+	preArch, preDeps, postDeps, finalDeps []RegisterMutatorFunc
+	bp2buildPreArch, bp2buildMutators     []RegisterMutatorFunc
+	NameResolver                          *NameResolver
 
 	// The list of pre-singletons and singletons registered for the test.
 	preSingletons, singletons sortableComponents
@@ -219,12 +224,6 @@
 	ctx.bp2buildPreArch = append(ctx.bp2buildPreArch, f)
 }
 
-// DepsBp2BuildMutators adds mutators to be register for converting Android Blueprint modules into
-// Bazel BUILD targets that should run prior to conversion to resolve dependencies.
-func (ctx *TestContext) DepsBp2BuildMutators(f RegisterMutatorFunc) {
-	ctx.bp2buildDeps = append(ctx.bp2buildDeps, f)
-}
-
 // registeredComponentOrder defines the order in which a sortableComponent type is registered at
 // runtime and provides support for reordering the components registered for a test in the same
 // way.
@@ -459,7 +458,7 @@
 
 // RegisterForBazelConversion prepares a test context for bp2build conversion.
 func (ctx *TestContext) RegisterForBazelConversion() {
-	RegisterMutatorsForBazelConversion(ctx.Context, ctx.bp2buildPreArch, ctx.bp2buildDeps, ctx.bp2buildMutators)
+	RegisterMutatorsForBazelConversion(ctx.Context, ctx.bp2buildPreArch, ctx.bp2buildMutators)
 }
 
 func (ctx *TestContext) ParseFileList(rootDir string, filePaths []string) (deps []string, errs []error) {
@@ -665,15 +664,15 @@
 // containing at most one instance of the temporary build directory at the start of the path while
 // this assumes that there can be any number at any position.
 func normalizeStringRelativeToTop(config Config, s string) string {
-	// The buildDir usually looks something like: /tmp/testFoo2345/001
+	// The soongOutDir usually looks something like: /tmp/testFoo2345/001
 	//
-	// Replace any usage of the buildDir with out/soong, e.g. replace "/tmp/testFoo2345/001" with
+	// Replace any usage of the soongOutDir with out/soong, e.g. replace "/tmp/testFoo2345/001" with
 	// "out/soong".
-	outSoongDir := filepath.Clean(config.buildDir)
+	outSoongDir := filepath.Clean(config.soongOutDir)
 	re := regexp.MustCompile(`\Q` + outSoongDir + `\E\b`)
 	s = re.ReplaceAllString(s, "out/soong")
 
-	// Replace any usage of the buildDir/.. with out, e.g. replace "/tmp/testFoo2345" with
+	// Replace any usage of the soongOutDir/.. with out, e.g. replace "/tmp/testFoo2345" with
 	// "out". This must come after the previous replacement otherwise this would replace
 	// "/tmp/testFoo2345/001" with "out/001" instead of "out/soong".
 	outDir := filepath.Dir(outSoongDir)
@@ -992,7 +991,7 @@
 	}
 	p := path.String()
 	if w, ok := path.(WritablePath); ok {
-		rel, err := filepath.Rel(w.getBuildDir(), p)
+		rel, err := filepath.Rel(w.getSoongOutDir(), p)
 		if err != nil {
 			panic(err)
 		}
diff --git a/android/variable.go b/android/variable.go
index 0dc5262..03847a1 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -42,6 +42,14 @@
 			Cflags  []string
 		}
 
+		Platform_sdk_version_or_codename struct {
+			Java_resource_dirs []string
+		}
+
+		Platform_sdk_extension_version struct {
+			Cmd *string
+		}
+
 		// unbundled_build is a catch-all property to annotate modules that don't build in one or
 		// more unbundled branches, usually due to dependencies missing from the manifest.
 		Unbundled_build struct {
@@ -101,6 +109,11 @@
 				Keep_symbols                 *bool
 				Keep_symbols_and_debug_frame *bool
 			}
+			Static_libs       []string
+			Whole_static_libs []string
+			Shared_libs       []string
+
+			Cmdline []string
 		}
 
 		// eng is true for -eng builds, and can be used to turn on additionaly heavyweight debugging
@@ -162,7 +175,9 @@
 	Platform_version_name                     *string  `json:",omitempty"`
 	Platform_sdk_version                      *int     `json:",omitempty"`
 	Platform_sdk_codename                     *string  `json:",omitempty"`
+	Platform_sdk_version_or_codename          *string  `json:",omitempty"`
 	Platform_sdk_final                        *bool    `json:",omitempty"`
+	Platform_sdk_extension_version            *int     `json:",omitempty"`
 	Platform_version_active_codenames         []string `json:",omitempty"`
 	Platform_vndk_version                     *string  `json:",omitempty"`
 	Platform_systemsdk_versions               []string `json:",omitempty"`
@@ -201,6 +216,7 @@
 
 	HostArch          *string `json:",omitempty"`
 	HostSecondaryArch *string `json:",omitempty"`
+	HostMusl          *bool   `json:",omitempty"`
 
 	CrossHost              *string `json:",omitempty"`
 	CrossHostArch          *string `json:",omitempty"`
@@ -223,6 +239,7 @@
 	Allow_missing_dependencies   *bool `json:",omitempty"`
 	Unbundled_build              *bool `json:",omitempty"`
 	Unbundled_build_apps         *bool `json:",omitempty"`
+	Unbundled_build_image        *bool `json:",omitempty"`
 	Always_use_prebuilt_sdks     *bool `json:",omitempty"`
 	Skip_boot_jars_check         *bool `json:",omitempty"`
 	Malloc_not_svelte            *bool `json:",omitempty"`
@@ -249,8 +266,8 @@
 	UncompressPrivAppDex             *bool    `json:",omitempty"`
 	ModulesLoadedByPrivilegedModules []string `json:",omitempty"`
 
-	BootJars          ConfiguredJarList `json:",omitempty"`
-	UpdatableBootJars ConfiguredJarList `json:",omitempty"`
+	BootJars     ConfiguredJarList `json:",omitempty"`
+	ApexBootJars ConfiguredJarList `json:",omitempty"`
 
 	IntegerOverflowExcludePaths []string `json:",omitempty"`
 
@@ -283,7 +300,7 @@
 	NativeCoverageExcludePaths []string `json:",omitempty"`
 
 	// Set by NewConfig
-	Native_coverage *bool
+	Native_coverage *bool `json:",omitempty"`
 
 	SanitizeHost       []string `json:",omitempty"`
 	SanitizeDevice     []string `json:",omitempty"`
@@ -296,8 +313,6 @@
 
 	Override_rs_driver *string `json:",omitempty"`
 
-	Fuchsia *bool `json:",omitempty"`
-
 	DeviceKernelHeaders []string `json:",omitempty"`
 
 	ExtraVndkVersions []string `json:",omitempty"`
@@ -439,8 +454,8 @@
 		Malloc_pattern_fill_contents: boolPtr(false),
 		Safestack:                    boolPtr(false),
 
-		BootJars:          ConfiguredJarList{apexes: []string{}, jars: []string{}},
-		UpdatableBootJars: ConfiguredJarList{apexes: []string{}, jars: []string{}},
+		BootJars:     ConfiguredJarList{apexes: []string{}, jars: []string{}},
+		ApexBootJars: ConfiguredJarList{apexes: []string{}, jars: []string{}},
 	}
 
 	if runtime.GOOS == "linux" {
@@ -459,16 +474,17 @@
 // with the appropriate ProductConfigVariable.
 type ProductConfigProperty struct {
 	ProductConfigVariable string
+	FullConfig            string
 	Property              interface{}
 }
 
 // ProductConfigProperties is a map of property name to a slice of ProductConfigProperty such that
 // all it all product variable-specific versions of a property are easily accessed together
-type ProductConfigProperties map[string][]ProductConfigProperty
+type ProductConfigProperties map[string]map[string]ProductConfigProperty
 
 // ProductVariableProperties returns a ProductConfigProperties containing only the properties which
 // have been set for the module in the given context.
-func ProductVariableProperties(ctx ProductConfigContext) ProductConfigProperties {
+func ProductVariableProperties(ctx BaseMutatorContext) ProductConfigProperties {
 	module := ctx.Module()
 	moduleBase := module.base()
 
@@ -478,7 +494,24 @@
 		return productConfigProperties
 	}
 
-	variableValues := reflect.ValueOf(moduleBase.variableProperties).Elem().FieldByName("Product_variables")
+	productVariableValues(moduleBase.variableProperties, "", &productConfigProperties)
+
+	for _, configToProps := range moduleBase.GetArchVariantProperties(ctx, moduleBase.variableProperties) {
+		for config, props := range configToProps {
+			// GetArchVariantProperties is creating an instance of the requested type
+			// and productVariablesValues expects an interface, so no need to cast
+			productVariableValues(props, config, &productConfigProperties)
+		}
+	}
+
+	return productConfigProperties
+}
+
+func productVariableValues(variableProps interface{}, suffix string, productConfigProperties *ProductConfigProperties) {
+	if suffix != "" {
+		suffix = "-" + suffix
+	}
+	variableValues := reflect.ValueOf(variableProps).Elem().FieldByName("Product_variables")
 	for i := 0; i < variableValues.NumField(); i++ {
 		variableValue := variableValues.Field(i)
 		// Check if any properties were set for the module
@@ -496,15 +529,17 @@
 
 			// e.g. Asflags, Cflags, Enabled, etc.
 			propertyName := variableValue.Type().Field(j).Name
-			productConfigProperties[propertyName] = append(productConfigProperties[propertyName],
-				ProductConfigProperty{
-					ProductConfigVariable: productVariableName,
-					Property:              property.Interface(),
-				})
+			if (*productConfigProperties)[propertyName] == nil {
+				(*productConfigProperties)[propertyName] = make(map[string]ProductConfigProperty)
+			}
+			config := productVariableName + suffix
+			(*productConfigProperties)[propertyName][config] = ProductConfigProperty{
+				ProductConfigVariable: productVariableName,
+				FullConfig:            config,
+				Property:              property.Interface(),
+			}
 		}
 	}
-
-	return productConfigProperties
 }
 
 func VariableMutator(mctx BottomUpMutatorContext) {
diff --git a/android/writedocs.go b/android/writedocs.go
index 67b9aa3..c380a3d 100644
--- a/android/writedocs.go
+++ b/android/writedocs.go
@@ -34,12 +34,12 @@
 type docsSingleton struct{}
 
 func primaryBuilderPath(ctx SingletonContext) Path {
-	buildDir := absolutePath(ctx.Config().BuildDir())
+	soongOutDir := absolutePath(ctx.Config().SoongOutDir())
 	binary := absolutePath(os.Args[0])
-	primaryBuilder, err := filepath.Rel(buildDir, binary)
+	primaryBuilder, err := filepath.Rel(soongOutDir, binary)
 	if err != nil {
 		ctx.Errorf("path to primary builder %q is not in build dir %q (%q)",
-			os.Args[0], ctx.Config().BuildDir(), err)
+			os.Args[0], ctx.Config().SoongOutDir(), err)
 	}
 
 	return PathForOutput(ctx, primaryBuilder)
diff --git a/androidmk/androidmk/android.go b/androidmk/androidmk/android.go
index 5316d7b..08616a9 100644
--- a/androidmk/androidmk/android.go
+++ b/androidmk/androidmk/android.go
@@ -104,6 +104,7 @@
 			"LOCAL_NDK_STL_VARIANT":         "stl",
 			"LOCAL_JAR_MANIFEST":            "manifest",
 			"LOCAL_CERTIFICATE":             "certificate",
+			"LOCAL_CERTIFICATE_LINEAGE":     "lineage",
 			"LOCAL_PACKAGE_NAME":            "name",
 			"LOCAL_MODULE_RELATIVE_PATH":    "relative_install_path",
 			"LOCAL_PROTOC_OPTIMIZE_TYPE":    "proto.type",
diff --git a/androidmk/androidmk/androidmk_test.go b/androidmk/androidmk/androidmk_test.go
index 439f45d..02ab89d 100644
--- a/androidmk/androidmk/androidmk_test.go
+++ b/androidmk/androidmk/androidmk_test.go
@@ -794,7 +794,7 @@
 include $(BUILD_CTS_SUPPORT_PACKAGE)
 `,
 		expected: `
-android_test {
+android_test_helper_app {
     name: "FooTest",
     defaults: ["cts_support_defaults"],
     test_suites: ["cts"],
@@ -1463,6 +1463,22 @@
 }
 `,
 	},
+	{
+		desc: "LOCAL_CERTIFICATE_LINEAGE",
+		in: `
+include $(CLEAR_VARS)
+LOCAL_MODULE := foo
+LOCAL_MODULE_TAGS := tests
+LOCAL_CERTIFICATE_LINEAGE := lineage
+include $(BUILD_PACKAGE)
+`,
+		expected: `
+android_test {
+    name: "foo",
+    lineage: "lineage",
+}
+`,
+	},
 }
 
 func TestEndToEnd(t *testing.T) {
diff --git a/androidmk/parser/make_strings.go b/androidmk/parser/make_strings.go
index 3c4815e..aac4c4e 100644
--- a/androidmk/parser/make_strings.go
+++ b/androidmk/parser/make_strings.go
@@ -278,6 +278,15 @@
 	}
 }
 
+// If MakeString is $(var) after trimming, returns var
+func (ms *MakeString) SingleVariable() (*MakeString, bool) {
+	if len(ms.Strings) != 2 || strings.TrimSpace(ms.Strings[0]) != "" ||
+		strings.TrimSpace(ms.Strings[1]) != "" {
+		return nil, false
+	}
+	return ms.Variables[0].Name, true
+}
+
 func splitAnyN(s, sep string, n int) []string {
 	ret := []string{}
 	for n == -1 || n > 1 {
diff --git a/androidmk/parser/parser.go b/androidmk/parser/parser.go
index 5afef65..d24efc1 100644
--- a/androidmk/parser/parser.go
+++ b/androidmk/parser/parser.go
@@ -216,13 +216,14 @@
 		// Nothing
 	case "else":
 		p.ignoreSpaces()
-		if p.tok != '\n' {
+		if p.tok != '\n' && p.tok != '#' {
 			d = p.scanner.TokenText()
 			p.accept(scanner.Ident)
 			if d == "ifdef" || d == "ifndef" || d == "ifeq" || d == "ifneq" {
 				d = "el" + d
 				p.ignoreSpaces()
 				expression = p.parseExpression()
+				expression.TrimRightSpaces()
 			} else {
 				p.errorf("expected ifdef/ifndef/ifeq/ifneq, found %s", d)
 			}
diff --git a/apex/Android.bp b/apex/Android.bp
index 6269757..b9b5428 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -9,6 +9,7 @@
         "blueprint",
         "soong",
         "soong-android",
+        "soong-bazel",
         "soong-bpf",
         "soong-cc",
         "soong-filesystem",
diff --git a/apex/androidmk.go b/apex/androidmk.go
index ebf0833..2f10904 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -212,7 +212,7 @@
 			}
 			if host {
 				makeOs := fi.module.Target().Os.String()
-				if fi.module.Target().Os == android.Linux || fi.module.Target().Os == android.LinuxBionic {
+				if fi.module.Target().Os == android.Linux || fi.module.Target().Os == android.LinuxBionic || fi.module.Target().Os == android.LinuxMusl {
 					makeOs = "linux"
 				}
 				fmt.Fprintln(w, "LOCAL_MODULE_HOST_OS :=", makeOs)
diff --git a/apex/apex.go b/apex/apex.go
index 7785407..639c58f 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -27,6 +27,7 @@
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
+	"android/soong/bazel"
 	"android/soong/bpf"
 	"android/soong/cc"
 	prebuilt_etc "android/soong/etc"
@@ -53,6 +54,8 @@
 	ctx.PreArchMutators(registerPreArchMutators)
 	ctx.PreDepsMutators(RegisterPreDepsMutators)
 	ctx.PostDepsMutators(RegisterPostDepsMutators)
+
+	android.RegisterBp2BuildMutator("apex", ApexBundleBp2Build)
 }
 
 func registerPreArchMutators(ctx android.RegisterMutatorsContext) {
@@ -114,9 +117,6 @@
 	// List of platform_compat_config files that are embedded inside this APEX bundle.
 	Compat_configs []string
 
-	// List of BPF programs inside this APEX bundle.
-	Bpfs []string
-
 	// List of filesystem images that are embedded inside this APEX bundle.
 	Filesystems []string
 
@@ -130,6 +130,10 @@
 	// symlinking to the system libs. Default is true.
 	Updatable *bool
 
+	// Whether this APEX can use platform APIs or not. Can be set to true only when `updatable:
+	// false`. Default is false.
+	Platform_apis *bool
+
 	// Whether this APEX is installable to one of the partitions like system, vendor, etc.
 	// Default: true.
 	Installable *bool
@@ -285,6 +289,9 @@
 	// List of runtime resource overlays (RROs) that are embedded inside this APEX.
 	Rros []string
 
+	// List of BPF programs inside this APEX bundle.
+	Bpfs []string
+
 	// Names of modules to be overridden. Listed modules can only be other binaries (in Make or
 	// Soong). This does not completely prevent installation of the overridden binaries, but if
 	// both binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will
@@ -318,6 +325,7 @@
 	android.DefaultableModuleBase
 	android.OverridableModuleBase
 	android.SdkBase
+	android.BazelModuleBase
 
 	// Properties
 	properties            apexBundleProperties
@@ -415,6 +423,9 @@
 	// Path of API coverage generate file
 	apisUsedByModuleFile   android.ModuleOutPath
 	apisBackedByModuleFile android.ModuleOutPath
+
+	// Collect the module directory for IDE info in java/jdeps.go.
+	modulePaths []string
 }
 
 // apexFileClass represents a type of file that can be included in APEX.
@@ -736,27 +747,28 @@
 		}
 	}
 
-	// For prebuilt_etc, use the first variant (64 on 64/32bit device, 32 on 32bit device)
-	// regardless of the TARGET_PREFER_* setting. See b/144532908
-	archForPrebuiltEtc := config.Arches()[0]
-	for _, arch := range config.Arches() {
-		// Prefer 64-bit arch if there is any
-		if arch.ArchType.Multilib == "lib64" {
-			archForPrebuiltEtc = arch
-			break
+	if prebuilts := a.properties.Prebuilts; len(prebuilts) > 0 {
+		// For prebuilt_etc, use the first variant (64 on 64/32bit device, 32 on 32bit device)
+		// regardless of the TARGET_PREFER_* setting. See b/144532908
+		archForPrebuiltEtc := config.Arches()[0]
+		for _, arch := range config.Arches() {
+			// Prefer 64-bit arch if there is any
+			if arch.ArchType.Multilib == "lib64" {
+				archForPrebuiltEtc = arch
+				break
+			}
 		}
+		ctx.AddFarVariationDependencies([]blueprint.Variation{
+			{Mutator: "os", Variation: ctx.Os().String()},
+			{Mutator: "arch", Variation: archForPrebuiltEtc.String()},
+		}, prebuiltTag, prebuilts...)
 	}
-	ctx.AddFarVariationDependencies([]blueprint.Variation{
-		{Mutator: "os", Variation: ctx.Os().String()},
-		{Mutator: "arch", Variation: archForPrebuiltEtc.String()},
-	}, prebuiltTag, a.properties.Prebuilts...)
 
 	// Common-arch dependencies come next
 	commonVariation := ctx.Config().AndroidCommonTarget.Variations()
 	ctx.AddFarVariationDependencies(commonVariation, bcpfTag, a.properties.Bootclasspath_fragments...)
 	ctx.AddFarVariationDependencies(commonVariation, sscpfTag, a.properties.Systemserverclasspath_fragments...)
 	ctx.AddFarVariationDependencies(commonVariation, javaLibTag, a.properties.Java_libs...)
-	ctx.AddFarVariationDependencies(commonVariation, bpfTag, a.properties.Bpfs...)
 	ctx.AddFarVariationDependencies(commonVariation, fsTag, a.properties.Filesystems...)
 	ctx.AddFarVariationDependencies(commonVariation, compatConfigTag, a.properties.Compat_configs...)
 
@@ -789,6 +801,7 @@
 
 	commonVariation := ctx.Config().AndroidCommonTarget.Variations()
 	ctx.AddFarVariationDependencies(commonVariation, androidAppTag, a.overridableProperties.Apps...)
+	ctx.AddFarVariationDependencies(commonVariation, bpfTag, a.overridableProperties.Bpfs...)
 	ctx.AddFarVariationDependencies(commonVariation, rroTag, a.overridableProperties.Rros...)
 
 	// Dependencies for signing
@@ -913,6 +926,7 @@
 		MinSdkVersion:     minSdkVersion,
 		RequiredSdks:      a.RequiredSdks(),
 		Updatable:         a.Updatable(),
+		UsePlatformApis:   a.UsePlatformApis(),
 		InApexVariants:    []string{apexVariationName},
 		InApexModules:     []string{a.Name()}, // could be com.mycompany.android.foo
 		ApexContents:      []*android.ApexContents{apexContents},
@@ -1075,13 +1089,6 @@
 			mctx.ModuleErrorf("base property is not set")
 			return
 		}
-		// Workaround the issue reported in b/191269918 by using the unprefixed module name of this
-		// module as the default variation to use if dependencies of this module do not have the correct
-		// apex variant name. This name matches the name used to create the variations of modules for
-		// which apexModuleTypeRequiresVariant return true.
-		// TODO(b/191269918): Remove this workaround.
-		unprefixedModuleName := android.RemoveOptionalPrebuiltPrefix(mctx.ModuleName())
-		mctx.SetDefaultDependencyVariation(&unprefixedModuleName)
 		mctx.CreateVariations(apexBundleName)
 		if strings.HasPrefix(apexBundleName, "com.android.art") {
 			// TODO(b/183882457): See note for CreateAliasVariation above.
@@ -1277,6 +1284,10 @@
 	return proptools.BoolDefault(a.properties.Updatable, true)
 }
 
+func (a *apexBundle) UsePlatformApis() bool {
+	return proptools.BoolDefault(a.properties.Platform_apis, false)
+}
+
 // getCertString returns the name of the cert that should be used to sign this APEX. This is
 // basically from the "certificate" property, but could be overridden by the device config.
 func (a *apexBundle) getCertString(ctx android.BaseModuleContext) string {
@@ -1633,6 +1644,7 @@
 	a.checkUpdatable(ctx)
 	a.checkMinSdkVersion(ctx)
 	a.checkStaticLinkingToStubLibraries(ctx)
+	a.checkStaticExecutables(ctx)
 	if len(a.properties.Tests) > 0 && !a.testApex {
 		ctx.PropertyErrorf("tests", "property allowed only in apex_test module type")
 		return
@@ -1650,6 +1662,9 @@
 
 	handleSpecialLibs := !android.Bool(a.properties.Ignore_system_library_special_case)
 
+	// Collect the module directory for IDE info in java/jdeps.go.
+	a.modulePaths = append(a.modulePaths, ctx.ModuleDir())
+
 	// TODO(jiyong): do this using WalkPayloadDeps
 	// TODO(jiyong): make this clean!!!
 	ctx.WalkDepsBlueprint(func(child, parent blueprint.Module) bool {
@@ -1716,7 +1731,9 @@
 						ctx.PropertyErrorf("systemserverclasspath_fragments", "%q is not a systemserverclasspath_fragment module", depName)
 						return false
 					}
-					filesInfo = append(filesInfo, apexClasspathFragmentProtoFile(ctx, child))
+					if af := apexClasspathFragmentProtoFile(ctx, child); af != nil {
+						filesInfo = append(filesInfo, *af)
+					}
 					return true
 				}
 			case javaLibTag:
@@ -1998,7 +2015,7 @@
 	a.filesInfo = filesInfo
 
 	// Set suffix and primaryApexType depending on the ApexType
-	buildFlattenedAsDefault := ctx.Config().FlattenApex() && !ctx.Config().UnbundledBuildApps()
+	buildFlattenedAsDefault := ctx.Config().FlattenApex()
 	switch a.properties.ApexType {
 	case imageApex:
 		if buildFlattenedAsDefault {
@@ -2113,17 +2130,23 @@
 	}
 
 	// Add classpaths.proto config.
-	filesToAdd = append(filesToAdd, apexClasspathFragmentProtoFile(ctx, module))
+	if af := apexClasspathFragmentProtoFile(ctx, module); af != nil {
+		filesToAdd = append(filesToAdd, *af)
+	}
 
 	return filesToAdd
 }
 
-// apexClasspathFragmentProtoFile returns apexFile structure defining the classpath.proto config that
-// the module contributes to the apex.
-func apexClasspathFragmentProtoFile(ctx android.ModuleContext, module blueprint.Module) apexFile {
-	fragmentInfo := ctx.OtherModuleProvider(module, java.ClasspathFragmentProtoContentInfoProvider).(java.ClasspathFragmentProtoContentInfo)
-	classpathProtoOutput := fragmentInfo.ClasspathFragmentProtoOutput
-	return newApexFile(ctx, classpathProtoOutput, classpathProtoOutput.Base(), fragmentInfo.ClasspathFragmentProtoInstallDir.Rel(), etc, nil)
+// apexClasspathFragmentProtoFile returns *apexFile structure defining the classpath.proto config that
+// the module contributes to the apex; or nil if the proto config was not generated.
+func apexClasspathFragmentProtoFile(ctx android.ModuleContext, module blueprint.Module) *apexFile {
+	info := ctx.OtherModuleProvider(module, java.ClasspathFragmentProtoContentInfoProvider).(java.ClasspathFragmentProtoContentInfo)
+	if !info.ClasspathFragmentProtoGenerated {
+		return nil
+	}
+	classpathProtoOutput := info.ClasspathFragmentProtoOutput
+	af := newApexFile(ctx, classpathProtoOutput, classpathProtoOutput.Base(), info.ClasspathFragmentProtoInstallDir.Rel(), etc, nil)
+	return &af
 }
 
 // apexFileForBootclasspathFragmentContentModule creates an apexFile for a bootclasspath_fragment
@@ -2160,6 +2183,7 @@
 	android.InitDefaultableModule(module)
 	android.InitSdkAwareModule(module)
 	android.InitOverridableModule(module, &module.overridableProperties.Overrides)
+	android.InitBazelModule(module)
 	return module
 }
 
@@ -2305,16 +2329,33 @@
 	})
 }
 
-// Enforce that Java deps of the apex are using stable SDKs to compile
+// checkUpdatable enforces APEX and its transitive dep properties to have desired values for updatable APEXes.
 func (a *apexBundle) checkUpdatable(ctx android.ModuleContext) {
 	if a.Updatable() {
 		if String(a.properties.Min_sdk_version) == "" {
 			ctx.PropertyErrorf("updatable", "updatable APEXes should set min_sdk_version as well")
 		}
+		if a.UsePlatformApis() {
+			ctx.PropertyErrorf("updatable", "updatable APEXes can't use platform APIs")
+		}
 		a.checkJavaStableSdkVersion(ctx)
+		a.checkClasspathFragments(ctx)
 	}
 }
 
+// checkClasspathFragments enforces that all classpath fragments in deps generate classpaths.proto config.
+func (a *apexBundle) checkClasspathFragments(ctx android.ModuleContext) {
+	ctx.VisitDirectDeps(func(module android.Module) {
+		if tag := ctx.OtherModuleDependencyTag(module); tag == bcpfTag || tag == sscpfTag {
+			info := ctx.OtherModuleProvider(module, java.ClasspathFragmentProtoContentInfoProvider).(java.ClasspathFragmentProtoContentInfo)
+			if !info.ClasspathFragmentProtoGenerated {
+				ctx.OtherModuleErrorf(module, "is included in updatable apex %v, it must not set generate_classpaths_proto to false", ctx.ModuleName())
+			}
+		}
+	})
+}
+
+// checkJavaStableSdkVersion enforces that all Java deps are using stable SDKs to compile.
 func (a *apexBundle) checkJavaStableSdkVersion(ctx android.ModuleContext) {
 	// Visit direct deps only. As long as we guarantee top-level deps are using stable SDKs,
 	// java's checkLinkType guarantees correct usage for transitive deps
@@ -2333,7 +2374,7 @@
 	})
 }
 
-// Ensures that the all the dependencies are marked as available for this APEX
+// checkApexAvailability ensures that the all the dependencies are marked as available for this APEX.
 func (a *apexBundle) checkApexAvailability(ctx android.ModuleContext) {
 	// Let's be practical. Availability for test, host, and the VNDK apex isn't important
 	if ctx.Host() || a.testApex || a.vndkApex {
@@ -2385,6 +2426,49 @@
 	})
 }
 
+// checkStaticExecutable ensures that executables in an APEX are not static.
+func (a *apexBundle) checkStaticExecutables(ctx android.ModuleContext) {
+	// No need to run this for host APEXes
+	if ctx.Host() {
+		return
+	}
+
+	ctx.VisitDirectDepsBlueprint(func(module blueprint.Module) {
+		if ctx.OtherModuleDependencyTag(module) != executableTag {
+			return
+		}
+
+		if l, ok := module.(cc.LinkableInterface); ok && l.StaticExecutable() {
+			apex := a.ApexVariationName()
+			exec := ctx.OtherModuleName(module)
+			if isStaticExecutableAllowed(apex, exec) {
+				return
+			}
+			ctx.ModuleErrorf("executable %s is static", ctx.OtherModuleName(module))
+		}
+	})
+}
+
+// A small list of exceptions where static executables are allowed in APEXes.
+func isStaticExecutableAllowed(apex string, exec string) bool {
+	m := map[string][]string{
+		"com.android.runtime": []string{
+			"linker",
+			"linkerconfig",
+		},
+	}
+	execNames, ok := m[apex]
+	return ok && android.InList(exec, execNames)
+}
+
+// Collect information for opening IDE project files in java/jdeps.go.
+func (a *apexBundle) IDEInfo(dpInfo *android.IdeInfo) {
+	dpInfo.Deps = append(dpInfo.Deps, a.properties.Java_libs...)
+	dpInfo.Deps = append(dpInfo.Deps, a.properties.Bootclasspath_fragments...)
+	dpInfo.Deps = append(dpInfo.Deps, a.properties.Systemserverclasspath_fragments...)
+	dpInfo.Paths = append(dpInfo.Paths, a.modulePaths...)
+}
+
 var (
 	apexAvailBaseline        = makeApexAvailableBaseline()
 	inverseApexAvailBaseline = invertApexBaseline(apexAvailBaseline)
@@ -3072,3 +3156,109 @@
 		},
 	}
 }
+
+// For Bazel / bp2build
+
+type bazelApexBundleAttributes struct {
+	Manifest           bazel.LabelAttribute
+	Android_manifest   bazel.LabelAttribute
+	File_contexts      bazel.LabelAttribute
+	Key                bazel.LabelAttribute
+	Certificate        bazel.LabelAttribute
+	Min_sdk_version    string
+	Updatable          bazel.BoolAttribute
+	Installable        bazel.BoolAttribute
+	Native_shared_libs bazel.LabelListAttribute
+	Binaries           bazel.StringListAttribute
+	Prebuilts          bazel.LabelListAttribute
+}
+
+func ApexBundleBp2Build(ctx android.TopDownMutatorContext) {
+	module, ok := ctx.Module().(*apexBundle)
+	if !ok {
+		// Not an APEX bundle
+		return
+	}
+	if !module.ConvertWithBp2build(ctx) {
+		return
+	}
+	if ctx.ModuleType() != "apex" {
+		return
+	}
+
+	apexBundleBp2BuildInternal(ctx, module)
+}
+
+func apexBundleBp2BuildInternal(ctx android.TopDownMutatorContext, module *apexBundle) {
+	var manifestLabelAttribute bazel.LabelAttribute
+	if module.properties.Manifest != nil {
+		manifestLabelAttribute.SetValue(android.BazelLabelForModuleSrcSingle(ctx, *module.properties.Manifest))
+	}
+
+	var androidManifestLabelAttribute bazel.LabelAttribute
+	if module.properties.AndroidManifest != nil {
+		androidManifestLabelAttribute.SetValue(android.BazelLabelForModuleSrcSingle(ctx, *module.properties.AndroidManifest))
+	}
+
+	var fileContextsLabelAttribute bazel.LabelAttribute
+	if module.properties.File_contexts != nil {
+		fileContextsLabelAttribute.SetValue(android.BazelLabelForModuleDepSingle(ctx, *module.properties.File_contexts))
+	}
+
+	var minSdkVersion string
+	if module.properties.Min_sdk_version != nil {
+		minSdkVersion = *module.properties.Min_sdk_version
+	}
+
+	var keyLabelAttribute bazel.LabelAttribute
+	if module.overridableProperties.Key != nil {
+		keyLabelAttribute.SetValue(android.BazelLabelForModuleDepSingle(ctx, *module.overridableProperties.Key))
+	}
+
+	var certificateLabelAttribute bazel.LabelAttribute
+	if module.overridableProperties.Certificate != nil {
+		certificateLabelAttribute.SetValue(android.BazelLabelForModuleDepSingle(ctx, *module.overridableProperties.Certificate))
+	}
+
+	nativeSharedLibs := module.properties.ApexNativeDependencies.Native_shared_libs
+	nativeSharedLibsLabelList := android.BazelLabelForModuleDeps(ctx, nativeSharedLibs)
+	nativeSharedLibsLabelListAttribute := bazel.MakeLabelListAttribute(nativeSharedLibsLabelList)
+
+	prebuilts := module.properties.Prebuilts
+	prebuiltsLabelList := android.BazelLabelForModuleDeps(ctx, prebuilts)
+	prebuiltsLabelListAttribute := bazel.MakeLabelListAttribute(prebuiltsLabelList)
+
+	binaries := module.properties.ApexNativeDependencies.Binaries
+	binariesStringListAttribute := bazel.MakeStringListAttribute(binaries)
+
+	var updatableAttribute bazel.BoolAttribute
+	if module.properties.Updatable != nil {
+		updatableAttribute.Value = module.properties.Updatable
+	}
+
+	var installableAttribute bazel.BoolAttribute
+	if module.properties.Installable != nil {
+		installableAttribute.Value = module.properties.Installable
+	}
+
+	attrs := &bazelApexBundleAttributes{
+		Manifest:           manifestLabelAttribute,
+		Android_manifest:   androidManifestLabelAttribute,
+		File_contexts:      fileContextsLabelAttribute,
+		Min_sdk_version:    minSdkVersion,
+		Key:                keyLabelAttribute,
+		Certificate:        certificateLabelAttribute,
+		Updatable:          updatableAttribute,
+		Installable:        installableAttribute,
+		Native_shared_libs: nativeSharedLibsLabelListAttribute,
+		Binaries:           binariesStringListAttribute,
+		Prebuilts:          prebuiltsLabelListAttribute,
+	}
+
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class:        "apex",
+		Bzl_load_location: "//build/bazel/rules:apex.bzl",
+	}
+
+	ctx.CreateBazelTargetModule(module.Name(), props, attrs)
+}
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 7b01b94..3b3cafd 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -374,7 +374,6 @@
 			symlinks: ["foo_link_"],
 			symlink_preferred_arch: true,
 			system_shared_libs: [],
-			static_executable: true,
 			stl: "none",
 			apex_available: [ "myapex", "com.android.gki.*" ],
 		}
@@ -709,6 +708,79 @@
 	}
 }
 
+func TestApexManifestMinSdkVersion(t *testing.T) {
+	ctx := testApex(t, `
+		apex_defaults {
+			name: "my_defaults",
+			key: "myapex.key",
+			product_specific: true,
+			file_contexts: ":my-file-contexts",
+			updatable: false,
+		}
+		apex {
+			name: "myapex_30",
+			min_sdk_version: "30",
+			defaults: ["my_defaults"],
+		}
+
+		apex {
+			name: "myapex_current",
+			min_sdk_version: "current",
+			defaults: ["my_defaults"],
+		}
+
+		apex {
+			name: "myapex_none",
+			defaults: ["my_defaults"],
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		filegroup {
+			name: "my-file-contexts",
+			srcs: ["product_specific_file_contexts"],
+		}
+	`, withFiles(map[string][]byte{
+		"product_specific_file_contexts": nil,
+	}), android.FixtureModifyProductVariables(
+		func(variables android.FixtureProductVariables) {
+			variables.Unbundled_build = proptools.BoolPtr(true)
+			variables.Always_use_prebuilt_sdks = proptools.BoolPtr(false)
+		}), android.FixtureMergeEnv(map[string]string{
+		"UNBUNDLED_BUILD_TARGET_SDK_WITH_API_FINGERPRINT": "true",
+	}))
+
+	testCases := []struct {
+		module        string
+		minSdkVersion string
+	}{
+		{
+			module:        "myapex_30",
+			minSdkVersion: "30",
+		},
+		{
+			module:        "myapex_current",
+			minSdkVersion: "Q.$$(cat out/soong/api_fingerprint.txt)",
+		},
+		{
+			module:        "myapex_none",
+			minSdkVersion: "Q.$$(cat out/soong/api_fingerprint.txt)",
+		},
+	}
+	for _, tc := range testCases {
+		module := ctx.ModuleForTests(tc.module, "android_common_"+tc.module+"_image")
+		args := module.Rule("apexRule").Args
+		optFlags := args["opt_flags"]
+		if !strings.Contains(optFlags, "--min_sdk_version "+tc.minSdkVersion) {
+			t.Errorf("%s: Expected min_sdk_version=%s, got: %s", tc.module, tc.minSdkVersion, optFlags)
+		}
+	}
+}
+
 func TestBasicZipApex(t *testing.T) {
 	ctx := testApex(t, `
 		apex {
@@ -766,6 +838,7 @@
 			name: "myapex",
 			key: "myapex.key",
 			native_shared_libs: ["mylib", "mylib3"],
+			binaries: ["foo.rust"],
 			updatable: false,
 		}
 
@@ -814,6 +887,25 @@
 			stl: "none",
 			apex_available: [ "myapex" ],
 		}
+
+		rust_binary {
+			name: "foo.rust",
+			srcs: ["foo.rs"],
+			shared_libs: ["libfoo.shared_from_rust"],
+			prefer_rlib: true,
+			apex_available: ["myapex"],
+		}
+
+		cc_library_shared {
+			name: "libfoo.shared_from_rust",
+			srcs: ["mylib.cpp"],
+			system_shared_libs: [],
+			stl: "none",
+			stubs: {
+				versions: ["10", "11", "12"],
+			},
+		}
+
 	`)
 
 	apexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexRule")
@@ -851,7 +943,90 @@
 		"lib64/mylib.so",
 		"lib64/mylib3.so",
 		"lib64/mylib4.so",
+		"bin/foo.rust",
+		"lib64/libc++.so", // by the implicit dependency from foo.rust
+		"lib64/liblog.so", // by the implicit dependency from foo.rust
 	})
+
+	// Ensure that stub dependency from a rust module is not included
+	ensureNotContains(t, copyCmds, "image.apex/lib64/libfoo.shared_from_rust.so")
+	// The rust module is linked to the stub cc library
+	rustDeps := ctx.ModuleForTests("foo.rust", "android_arm64_armv8-a_apex10000").Rule("rustc").Args["linkFlags"]
+	ensureContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared_current/libfoo.shared_from_rust.so")
+	ensureNotContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared/libfoo.shared_from_rust.so")
+}
+
+func TestApexCanUsePrivateApis(t *testing.T) {
+	ctx := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			native_shared_libs: ["mylib"],
+			binaries: ["foo.rust"],
+			updatable: false,
+			platform_apis: true,
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "mylib",
+			srcs: ["mylib.cpp"],
+			shared_libs: ["mylib2"],
+			system_shared_libs: [],
+			stl: "none",
+			apex_available: [ "myapex" ],
+		}
+
+		cc_library {
+			name: "mylib2",
+			srcs: ["mylib.cpp"],
+			cflags: ["-include mylib.h"],
+			system_shared_libs: [],
+			stl: "none",
+			stubs: {
+				versions: ["1", "2", "3"],
+			},
+		}
+
+		rust_binary {
+			name: "foo.rust",
+			srcs: ["foo.rs"],
+			shared_libs: ["libfoo.shared_from_rust"],
+			prefer_rlib: true,
+			apex_available: ["myapex"],
+		}
+
+		cc_library_shared {
+			name: "libfoo.shared_from_rust",
+			srcs: ["mylib.cpp"],
+			system_shared_libs: [],
+			stl: "none",
+			stubs: {
+				versions: ["10", "11", "12"],
+			},
+		}
+	`)
+
+	apexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexRule")
+	copyCmds := apexRule.Args["copy_commands"]
+
+	// Ensure that indirect stubs dep is not included
+	ensureNotContains(t, copyCmds, "image.apex/lib64/mylib2.so")
+	ensureNotContains(t, copyCmds, "image.apex/lib64/libfoo.shared_from_rust.so")
+
+	// Ensure that we are using non-stub variants of mylib2 and libfoo.shared_from_rust (because
+	// of the platform_apis: true)
+	mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex10000_private").Rule("ld").Args["libFlags"]
+	ensureNotContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared_current/mylib2.so")
+	ensureContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared/mylib2.so")
+	rustDeps := ctx.ModuleForTests("foo.rust", "android_arm64_armv8-a_apex10000_private").Rule("rustc").Args["linkFlags"]
+	ensureNotContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared_current/libfoo.shared_from_rust.so")
+	ensureContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared/libfoo.shared_from_rust.so")
 }
 
 func TestApexWithStubsWithMinSdkVersion(t *testing.T) {
@@ -1620,6 +1795,36 @@
 	expectNoLink("libx", "shared_apex10000", "libz", "shared")
 }
 
+func TestApexMinSdkVersion_SupportsCodeNames_JavaLibs(t *testing.T) {
+	testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			java_libs: ["libx"],
+			min_sdk_version: "S",
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		java_library {
+			name: "libx",
+			srcs: ["a.java"],
+			apex_available: [ "myapex" ],
+			sdk_version: "current",
+			min_sdk_version: "S", // should be okay
+		}
+	`,
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.Platform_version_active_codenames = []string{"S"}
+			variables.Platform_sdk_codename = proptools.StringPtr("S")
+		}),
+	)
+}
+
 func TestApexMinSdkVersion_DefaultsToLatest(t *testing.T) {
 	ctx := testApex(t, `
 		apex {
@@ -1667,6 +1872,45 @@
 	expectNoLink("libx", "shared_apex10000", "libz", "shared")
 }
 
+func TestApexMinSdkVersion_crtobjectInVendorApex(t *testing.T) {
+	ctx := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			native_shared_libs: ["mylib"],
+			updatable: false,
+			vendor: true,
+			min_sdk_version: "29",
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "mylib",
+			vendor_available: true,
+			system_shared_libs: [],
+			stl: "none",
+			apex_available: [ "myapex" ],
+			min_sdk_version: "29",
+		}
+	`)
+
+	vendorVariant := "android_vendor.29_arm64_armv8-a"
+
+	// First check that the correct variant of crtbegin_so is used.
+	ldRule := ctx.ModuleForTests("mylib", vendorVariant+"_shared_apex29").Rule("ld")
+	crtBegin := names(ldRule.Args["crtBegin"])
+	ensureListContains(t, crtBegin, "out/soong/.intermediates/"+cc.DefaultCcCommonTestModulesDir+"crtbegin_so/"+vendorVariant+"_apex29/crtbegin_so.o")
+
+	// Ensure that the crtbegin_so used by the APEX is targeting 29
+	cflags := ctx.ModuleForTests("crtbegin_so", vendorVariant+"_apex29").Rule("cc").Args["cFlags"]
+	android.AssertStringDoesContain(t, "cflags", cflags, "-target aarch64-linux-android29")
+}
+
 func TestPlatformUsesLatestStubsFromApexes(t *testing.T) {
 	ctx := testApex(t, `
 		apex {
@@ -2286,7 +2530,6 @@
 			srcs: ["mylib.cpp"],
 			relative_install_path: "foo/bar",
 			system_shared_libs: [],
-			static_executable: true,
 			stl: "none",
 			apex_available: [ "myapex" ],
 		}
@@ -2346,7 +2589,6 @@
 			name: "mybin",
 			relative_install_path: "foo/bar",
 			system_shared_libs: [],
-			static_executable: true,
 			stl: "none",
 			apex_available: [ "myapex" ],
 			native_bridge_supported: true,
@@ -3115,60 +3357,109 @@
 }
 
 func TestVndkApexCurrent(t *testing.T) {
-	ctx := testApex(t, `
-		apex_vndk {
-			name: "com.android.vndk.current",
-			key: "com.android.vndk.current.key",
-			updatable: false,
-		}
-
-		apex_key {
-			name: "com.android.vndk.current.key",
-			public_key: "testkey.avbpubkey",
-			private_key: "testkey.pem",
-		}
-
-		cc_library {
-			name: "libvndk",
-			srcs: ["mylib.cpp"],
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-			},
-			system_shared_libs: [],
-			stl: "none",
-			apex_available: [ "com.android.vndk.current" ],
-		}
-
-		cc_library {
-			name: "libvndksp",
-			srcs: ["mylib.cpp"],
-			vendor_available: true,
-			product_available: true,
-			vndk: {
-				enabled: true,
-				support_system_process: true,
-			},
-			system_shared_libs: [],
-			stl: "none",
-			apex_available: [ "com.android.vndk.current" ],
-		}
-	`+vndkLibrariesTxtFiles("current"))
-
-	ensureExactContents(t, ctx, "com.android.vndk.current", "android_common_image", []string{
-		"lib/libvndk.so",
-		"lib/libvndksp.so",
+	commonFiles := []string{
 		"lib/libc++.so",
-		"lib64/libvndk.so",
-		"lib64/libvndksp.so",
 		"lib64/libc++.so",
 		"etc/llndk.libraries.29.txt",
 		"etc/vndkcore.libraries.29.txt",
 		"etc/vndksp.libraries.29.txt",
 		"etc/vndkprivate.libraries.29.txt",
 		"etc/vndkproduct.libraries.29.txt",
-	})
+	}
+	testCases := []struct {
+		vndkVersion   string
+		expectedFiles []string
+	}{
+		{
+			vndkVersion: "current",
+			expectedFiles: append(commonFiles,
+				"lib/libvndk.so",
+				"lib/libvndksp.so",
+				"lib64/libvndk.so",
+				"lib64/libvndksp.so"),
+		},
+		{
+			vndkVersion: "",
+			expectedFiles: append(commonFiles,
+				// Legacy VNDK APEX contains only VNDK-SP files (of core variant)
+				"lib/libvndksp.so",
+				"lib64/libvndksp.so"),
+		},
+	}
+	for _, tc := range testCases {
+		t.Run("VNDK.current with DeviceVndkVersion="+tc.vndkVersion, func(t *testing.T) {
+			ctx := testApex(t, `
+			apex_vndk {
+				name: "com.android.vndk.current",
+				key: "com.android.vndk.current.key",
+				updatable: false,
+			}
+
+			apex_key {
+				name: "com.android.vndk.current.key",
+				public_key: "testkey.avbpubkey",
+				private_key: "testkey.pem",
+			}
+
+			cc_library {
+				name: "libvndk",
+				srcs: ["mylib.cpp"],
+				vendor_available: true,
+				product_available: true,
+				vndk: {
+					enabled: true,
+				},
+				system_shared_libs: [],
+				stl: "none",
+				apex_available: [ "com.android.vndk.current" ],
+			}
+
+			cc_library {
+				name: "libvndksp",
+				srcs: ["mylib.cpp"],
+				vendor_available: true,
+				product_available: true,
+				vndk: {
+					enabled: true,
+					support_system_process: true,
+				},
+				system_shared_libs: [],
+				stl: "none",
+				apex_available: [ "com.android.vndk.current" ],
+			}
+
+			// VNDK-Ext should not cause any problems
+
+			cc_library {
+				name: "libvndk.ext",
+				srcs: ["mylib2.cpp"],
+				vendor: true,
+				vndk: {
+					enabled: true,
+					extends: "libvndk",
+				},
+				system_shared_libs: [],
+				stl: "none",
+			}
+
+			cc_library {
+				name: "libvndksp.ext",
+				srcs: ["mylib2.cpp"],
+				vendor: true,
+				vndk: {
+					enabled: true,
+					support_system_process: true,
+					extends: "libvndksp",
+				},
+				system_shared_libs: [],
+				stl: "none",
+			}
+		`+vndkLibrariesTxtFiles("current"), android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				variables.DeviceVndkVersion = proptools.StringPtr(tc.vndkVersion)
+			}))
+			ensureExactContents(t, ctx, "com.android.vndk.current", "android_common_image", tc.expectedFiles)
+		})
+	}
 }
 
 func TestVndkApexWithPrebuilt(t *testing.T) {
@@ -4261,7 +4552,15 @@
 
 			prebuilt_bootclasspath_fragment {
 				name: "art-bootclasspath-fragment",
+				image_name: "art",
 				contents: ["core-oj"],
+				hidden_api: {
+					annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv",
+					metadata: "my-bootclasspath-fragment/metadata.csv",
+					index: "my-bootclasspath-fragment/index.csv",
+					stub_flags: "my-bootclasspath-fragment/stub-flags.csv",
+					all_flags: "my-bootclasspath-fragment/all-flags.csv",
+				},
 			}
 
 			java_import {
@@ -4282,16 +4581,26 @@
 		p := ctx.ModuleForTests(name, "android_common_myapex").Module().(java.UsesLibraryDependency)
 		dexJarBuildPath := p.DexJarBuildPath()
 		stem := android.RemoveOptionalPrebuiltPrefix(name)
-		if expected, actual := ".intermediates/myapex.deapexer/android_common/deapexer/javalib/"+stem+".jar", android.NormalizePathForTesting(dexJarBuildPath); actual != expected {
-			t.Errorf("Incorrect DexJarBuildPath value '%s', expected '%s'", actual, expected)
-		}
+		android.AssertStringEquals(t, "DexJarBuildPath should be apex-related path.",
+			".intermediates/myapex.deapexer/android_common/deapexer/javalib/"+stem+".jar",
+			android.NormalizePathForTesting(dexJarBuildPath))
+	}
+
+	checkDexJarInstallPath := func(t *testing.T, ctx *android.TestContext, name string) {
+		// Make sure the import has been given the correct path to the dex jar.
+		p := ctx.ModuleForTests(name, "android_common_myapex").Module().(java.UsesLibraryDependency)
+		dexJarBuildPath := p.DexJarInstallPath()
+		stem := android.RemoveOptionalPrebuiltPrefix(name)
+		android.AssertStringEquals(t, "DexJarInstallPath should be apex-related path.",
+			"target/product/test_device/apex/myapex/javalib/"+stem+".jar",
+			android.NormalizePathForTesting(dexJarBuildPath))
 	}
 
 	ensureNoSourceVariant := func(t *testing.T, ctx *android.TestContext, name string) {
 		// Make sure that an apex variant is not created for the source module.
-		if expected, actual := []string{"android_common"}, ctx.ModuleVariantsForTests(name); !reflect.DeepEqual(expected, actual) {
-			t.Errorf("invalid set of variants for %q: expected %q, found %q", "libfoo", expected, actual)
-		}
+		android.AssertArrayString(t, "Check if there is no source variant",
+			[]string{"android_common"},
+			ctx.ModuleVariantsForTests(name))
 	}
 
 	t.Run("prebuilt only", func(t *testing.T) {
@@ -4340,8 +4649,10 @@
 		}
 
 		checkDexJarBuildPath(t, ctx, "libfoo")
+		checkDexJarInstallPath(t, ctx, "libfoo")
 
 		checkDexJarBuildPath(t, ctx, "libbar")
+		checkDexJarInstallPath(t, ctx, "libbar")
 	})
 
 	t.Run("prebuilt with source preferred", func(t *testing.T) {
@@ -4387,9 +4698,11 @@
 		ctx := testDexpreoptWithApexes(t, bp, "", transform)
 
 		checkDexJarBuildPath(t, ctx, "prebuilt_libfoo")
+		checkDexJarInstallPath(t, ctx, "prebuilt_libfoo")
 		ensureNoSourceVariant(t, ctx, "libfoo")
 
 		checkDexJarBuildPath(t, ctx, "prebuilt_libbar")
+		checkDexJarInstallPath(t, ctx, "prebuilt_libbar")
 		ensureNoSourceVariant(t, ctx, "libbar")
 	})
 
@@ -4437,16 +4750,18 @@
 		ctx := testDexpreoptWithApexes(t, bp, "", transform)
 
 		checkDexJarBuildPath(t, ctx, "prebuilt_libfoo")
+		checkDexJarInstallPath(t, ctx, "prebuilt_libfoo")
 		ensureNoSourceVariant(t, ctx, "libfoo")
 
 		checkDexJarBuildPath(t, ctx, "prebuilt_libbar")
+		checkDexJarInstallPath(t, ctx, "prebuilt_libbar")
 		ensureNoSourceVariant(t, ctx, "libbar")
 	})
 }
 
 func TestBootDexJarsFromSourcesAndPrebuilts(t *testing.T) {
 	preparer := android.GroupFixturePreparers(
-		java.FixtureConfigureBootJars("myapex:libfoo", "myapex:libbar"),
+		java.FixtureConfigureApexBootJars("myapex:libfoo", "myapex:libbar"),
 		// Make sure that the frameworks/base/Android.bp file exists as otherwise hidden API encoding
 		// is disabled.
 		android.FixtureAddTextFile("frameworks/base/Android.bp", ""),
@@ -4465,11 +4780,11 @@
 			}
 		}
 		if !foundLibfooJar {
-			t.Errorf("Rule for libfoo.jar missing in dex_bootjars singleton outputs %q", android.StringPathsRelativeToTop(ctx.Config().BuildDir(), s.AllOutputs()))
+			t.Errorf("Rule for libfoo.jar missing in dex_bootjars singleton outputs %q", android.StringPathsRelativeToTop(ctx.Config().SoongOutDir(), s.AllOutputs()))
 		}
 	}
 
-	checkHiddenAPIIndexInputs := func(t *testing.T, ctx *android.TestContext, expectedIntermediateInputs string) {
+	checkHiddenAPIIndexFromClassesInputs := func(t *testing.T, ctx *android.TestContext, expectedIntermediateInputs string) {
 		t.Helper()
 		platformBootclasspath := ctx.ModuleForTests("platform-bootclasspath", "android_common")
 		var rule android.TestingBuildParams
@@ -4478,6 +4793,15 @@
 		java.CheckHiddenAPIRuleInputs(t, "intermediate index", expectedIntermediateInputs, rule)
 	}
 
+	checkHiddenAPIIndexFromFlagsInputs := func(t *testing.T, ctx *android.TestContext, expectedIntermediateInputs string) {
+		t.Helper()
+		platformBootclasspath := ctx.ModuleForTests("platform-bootclasspath", "android_common")
+		var rule android.TestingBuildParams
+
+		rule = platformBootclasspath.Output("hiddenapi-index.csv")
+		java.CheckHiddenAPIRuleInputs(t, "monolithic index", expectedIntermediateInputs, rule)
+	}
+
 	fragment := java.ApexVariantReference{
 		Apex:   proptools.StringPtr("myapex"),
 		Module: proptools.StringPtr("my-bootclasspath-fragment"),
@@ -4502,12 +4826,20 @@
 			name: "my-bootclasspath-fragment",
 			contents: ["libfoo", "libbar"],
 			apex_available: ["myapex"],
+			hidden_api: {
+				annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv",
+				metadata: "my-bootclasspath-fragment/metadata.csv",
+				index: "my-bootclasspath-fragment/index.csv",
+				stub_flags: "my-bootclasspath-fragment/stub-flags.csv",
+				all_flags: "my-bootclasspath-fragment/all-flags.csv",
+			},
 		}
 
 		java_import {
 			name: "libfoo",
 			jars: ["libfoo.jar"],
 			apex_available: ["myapex"],
+			permitted_packages: ["foo"],
 		}
 
 		java_sdk_library_import {
@@ -4517,6 +4849,7 @@
 			},
 			apex_available: ["myapex"],
 			shared_library: false,
+			permitted_packages: ["bar"],
 		}
 	`
 
@@ -4525,9 +4858,10 @@
 		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
 		// Verify the correct module jars contribute to the hiddenapi index file.
-		checkHiddenAPIIndexInputs(t, ctx, `
-			out/soong/.intermediates/libbar.stubs/android_common/combined/libbar.stubs.jar
-			out/soong/.intermediates/libfoo/android_common_myapex/combined/libfoo.jar
+		checkHiddenAPIIndexFromClassesInputs(t, ctx, ``)
+		checkHiddenAPIIndexFromFlagsInputs(t, ctx, `
+			my-bootclasspath-fragment/index.csv
+			out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv
 		`)
 	})
 
@@ -4543,12 +4877,20 @@
 			name: "my-bootclasspath-fragment",
 			contents: ["libfoo", "libbar"],
 			apex_available: ["myapex"],
+			hidden_api: {
+				annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv",
+				metadata: "my-bootclasspath-fragment/metadata.csv",
+				index: "my-bootclasspath-fragment/index.csv",
+				stub_flags: "my-bootclasspath-fragment/stub-flags.csv",
+				all_flags: "my-bootclasspath-fragment/all-flags.csv",
+			},
 		}
 
 		java_import {
 			name: "libfoo",
 			jars: ["libfoo.jar"],
 			apex_available: ["myapex"],
+			permitted_packages: ["foo"],
 		}
 
 		java_sdk_library_import {
@@ -4558,6 +4900,7 @@
 			},
 			apex_available: ["myapex"],
 			shared_library: false,
+			permitted_packages: ["bar"],
 		}
 	`
 
@@ -4566,9 +4909,10 @@
 		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
 		// Verify the correct module jars contribute to the hiddenapi index file.
-		checkHiddenAPIIndexInputs(t, ctx, `
-			out/soong/.intermediates/libbar.stubs/android_common/combined/libbar.stubs.jar
-			out/soong/.intermediates/libfoo/android_common_myapex/combined/libfoo.jar
+		checkHiddenAPIIndexFromClassesInputs(t, ctx, ``)
+		checkHiddenAPIIndexFromFlagsInputs(t, ctx, `
+			my-bootclasspath-fragment/index.csv
+			out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv
 		`)
 	})
 
@@ -4591,6 +4935,13 @@
 			name: "my-bootclasspath-fragment",
 			contents: ["libfoo", "libbar"],
 			apex_available: ["myapex"],
+			hidden_api: {
+				annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv",
+				metadata: "my-bootclasspath-fragment/metadata.csv",
+				index: "my-bootclasspath-fragment/index.csv",
+				stub_flags: "my-bootclasspath-fragment/stub-flags.csv",
+				all_flags: "my-bootclasspath-fragment/all-flags.csv",
+			},
 		}
 
 		java_import {
@@ -4628,6 +4979,12 @@
 		// find the dex boot jar in it. We either need to disable the source libfoo
 		// or make the prebuilt libfoo preferred.
 		testDexpreoptWithApexes(t, bp, "module libfoo does not provide a dex boot jar", preparer, fragment)
+		// dexbootjar check is skipped if AllowMissingDependencies is true
+		preparerAllowMissingDeps := android.GroupFixturePreparers(
+			preparer,
+			android.PrepareForTestWithAllowMissingDependencies,
+		)
+		testDexpreoptWithApexes(t, bp, "", preparerAllowMissingDeps, fragment)
 	})
 
 	t.Run("prebuilt library preferred with source", func(t *testing.T) {
@@ -4649,6 +5006,13 @@
 			name: "my-bootclasspath-fragment",
 			contents: ["libfoo", "libbar"],
 			apex_available: ["myapex"],
+			hidden_api: {
+				annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv",
+				metadata: "my-bootclasspath-fragment/metadata.csv",
+				index: "my-bootclasspath-fragment/index.csv",
+				stub_flags: "my-bootclasspath-fragment/stub-flags.csv",
+				all_flags: "my-bootclasspath-fragment/all-flags.csv",
+			},
 		}
 
 		java_import {
@@ -4656,6 +5020,7 @@
 			prefer: true,
 			jars: ["libfoo.jar"],
 			apex_available: ["myapex"],
+			permitted_packages: ["foo"],
 		}
 
 		java_library {
@@ -4672,6 +5037,7 @@
 			},
 			apex_available: ["myapex"],
 			shared_library: false,
+			permitted_packages: ["bar"],
 		}
 
 		java_sdk_library {
@@ -4687,9 +5053,10 @@
 		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
 		// Verify the correct module jars contribute to the hiddenapi index file.
-		checkHiddenAPIIndexInputs(t, ctx, `
-			out/soong/.intermediates/prebuilt_libbar.stubs/android_common/combined/libbar.stubs.jar
-			out/soong/.intermediates/prebuilt_libfoo/android_common_myapex/combined/libfoo.jar
+		checkHiddenAPIIndexFromClassesInputs(t, ctx, ``)
+		checkHiddenAPIIndexFromFlagsInputs(t, ctx, `
+			my-bootclasspath-fragment/index.csv
+			out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv
 		`)
 	})
 
@@ -4725,6 +5092,13 @@
 			name: "my-bootclasspath-fragment",
 			contents: ["libfoo", "libbar"],
 			apex_available: ["myapex"],
+			hidden_api: {
+				annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv",
+				metadata: "my-bootclasspath-fragment/metadata.csv",
+				index: "my-bootclasspath-fragment/index.csv",
+				stub_flags: "my-bootclasspath-fragment/stub-flags.csv",
+				all_flags: "my-bootclasspath-fragment/all-flags.csv",
+			},
 		}
 
 		java_import {
@@ -4737,6 +5111,7 @@
 			name: "libfoo",
 			srcs: ["foo/bar/MyClass.java"],
 			apex_available: ["myapex"],
+			permitted_packages: ["foo"],
 		}
 
 		java_sdk_library_import {
@@ -4753,6 +5128,7 @@
 			srcs: ["foo/bar/MyClass.java"],
 			unsafe_ignore_missing_latest_api: true,
 			apex_available: ["myapex"],
+			permitted_packages: ["bar"],
 		}
 	`
 
@@ -4761,9 +5137,10 @@
 		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/libbar/android_common_myapex/hiddenapi/libbar.jar")
 
 		// Verify the correct module jars contribute to the hiddenapi index file.
-		checkHiddenAPIIndexInputs(t, ctx, `
-			out/soong/.intermediates/libbar/android_common_myapex/javac/libbar.jar
-			out/soong/.intermediates/libfoo/android_common_apex10000/javac/libfoo.jar
+		checkHiddenAPIIndexFromClassesInputs(t, ctx, ``)
+		checkHiddenAPIIndexFromFlagsInputs(t, ctx, `
+			my-bootclasspath-fragment/index.csv
+			out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv
 		`)
 	})
 
@@ -4799,6 +5176,13 @@
 			name: "my-bootclasspath-fragment",
 			contents: ["libfoo", "libbar"],
 			apex_available: ["myapex"],
+			hidden_api: {
+				annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv",
+				metadata: "my-bootclasspath-fragment/metadata.csv",
+				index: "my-bootclasspath-fragment/index.csv",
+				stub_flags: "my-bootclasspath-fragment/stub-flags.csv",
+				all_flags: "my-bootclasspath-fragment/all-flags.csv",
+			},
 		}
 
 		java_import {
@@ -4806,6 +5190,7 @@
 			prefer: true,
 			jars: ["libfoo.jar"],
 			apex_available: ["myapex"],
+			permitted_packages: ["foo"],
 		}
 
 		java_library {
@@ -4822,6 +5207,7 @@
 			},
 			apex_available: ["myapex"],
 			shared_library: false,
+			permitted_packages: ["bar"],
 		}
 
 		java_sdk_library {
@@ -4837,9 +5223,10 @@
 		checkBootDexJarPath(t, ctx, "libbar", "out/soong/.intermediates/myapex.deapexer/android_common/deapexer/javalib/libbar.jar")
 
 		// Verify the correct module jars contribute to the hiddenapi index file.
-		checkHiddenAPIIndexInputs(t, ctx, `
-			out/soong/.intermediates/prebuilt_libbar.stubs/android_common/combined/libbar.stubs.jar
-			out/soong/.intermediates/prebuilt_libfoo/android_common_myapex/combined/libfoo.jar
+		checkHiddenAPIIndexFromClassesInputs(t, ctx, ``)
+		checkHiddenAPIIndexFromFlagsInputs(t, ctx, `
+			my-bootclasspath-fragment/index.csv
+			out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv
 		`)
 	})
 }
@@ -5542,6 +5929,7 @@
 			name: "myapex",
 			key: "myapex.key",
 			apps: ["app"],
+			bpfs: ["bpf"],
 			overrides: ["oldapex"],
 			updatable: false,
 		}
@@ -5550,6 +5938,7 @@
 			name: "override_myapex",
 			base: "myapex",
 			apps: ["override_app"],
+			bpfs: ["override_bpf"],
 			overrides: ["unknownapex"],
 			logging_parent: "com.foo.bar",
 			package_name: "test.overridden.package",
@@ -5588,6 +5977,16 @@
 			base: "app",
 			package_name: "bar",
 		}
+
+		bpf {
+			name: "bpf",
+			srcs: ["bpf.c"],
+		}
+
+		bpf {
+			name: "override_bpf",
+			srcs: ["override_bpf.c"],
+		}
 	`, withManifestPackageNameOverrides([]string{"myapex:com.android.myapex"}))
 
 	originalVariant := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(android.OverridableModule)
@@ -5606,6 +6005,9 @@
 	ensureNotContains(t, copyCmds, "image.apex/app/app/app.apk")
 	ensureContains(t, copyCmds, "image.apex/app/override_app/override_app.apk")
 
+	ensureNotContains(t, copyCmds, "image.apex/etc/bpf/bpf.o")
+	ensureContains(t, copyCmds, "image.apex/etc/bpf/override_bpf.o")
+
 	apexBundle := module.Module().(*apexBundle)
 	name := apexBundle.Name()
 	if name != "override_myapex" {
@@ -5628,10 +6030,12 @@
 	data.Custom(&builder, name, "TARGET_", "", data)
 	androidMk := builder.String()
 	ensureContains(t, androidMk, "LOCAL_MODULE := override_app.override_myapex")
+	ensureContains(t, androidMk, "LOCAL_MODULE := override_bpf.o.override_myapex")
 	ensureContains(t, androidMk, "LOCAL_MODULE := apex_manifest.pb.override_myapex")
 	ensureContains(t, androidMk, "LOCAL_MODULE_STEM := override_myapex.apex")
 	ensureContains(t, androidMk, "LOCAL_OVERRIDES_MODULES := unknownapex myapex")
 	ensureNotContains(t, androidMk, "LOCAL_MODULE := app.myapex")
+	ensureNotContains(t, androidMk, "LOCAL_MODULE := bpf.myapex")
 	ensureNotContains(t, androidMk, "LOCAL_MODULE := override_app.myapex")
 	ensureNotContains(t, androidMk, "LOCAL_MODULE := apex_manifest.pb.myapex")
 	ensureNotContains(t, androidMk, "LOCAL_MODULE_STEM := myapex.apex")
@@ -6418,6 +6822,7 @@
 			apex_available: [
 				"some-updatable-apex",
 			],
+			permitted_packages: ["some.updatable.apex.lib"],
 		}
 
 		java_library {
@@ -6427,6 +6832,7 @@
 				"some-non-updatable-apex",
 			],
 			compile_dex: true,
+			permitted_packages: ["some.non.updatable.apex.lib"],
 		}
 
 		bootclasspath_fragment {
@@ -6568,73 +6974,6 @@
 	return result.TestContext
 }
 
-func TestDuplicateDeapexeresFromPrebuiltApexes(t *testing.T) {
-	preparers := android.GroupFixturePreparers(
-		java.PrepareForTestWithJavaDefaultModules,
-		PrepareForTestWithApexBuildComponents,
-	).
-		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
-			`Ambiguous duplicate deapexer module dependencies "com.android.myapex.deapexer" and "com.mycompany.android.myapex.deapexer"`))
-
-	bpBase := `
-		apex_set {
-			name: "com.android.myapex",
-			exported_bootclasspath_fragments: ["my-bootclasspath-fragment"],
-			set: "myapex.apks",
-		}
-
-		apex_set {
-			name: "com.mycompany.android.myapex",
-			apex_name: "com.android.myapex",
-			exported_bootclasspath_fragments: ["my-bootclasspath-fragment"],
-			set: "company-myapex.apks",
-		}
-
-		prebuilt_bootclasspath_fragment {
-			name: "my-bootclasspath-fragment",
-			apex_available: ["com.android.myapex"],
-			%s
-		}
-	`
-
-	t.Run("java_import", func(t *testing.T) {
-		_ = preparers.RunTestWithBp(t, fmt.Sprintf(bpBase, `contents: ["libfoo"]`)+`
-			java_import {
-				name: "libfoo",
-				jars: ["libfoo.jar"],
-				apex_available: ["com.android.myapex"],
-			}
-		`)
-	})
-
-	t.Run("java_sdk_library_import", func(t *testing.T) {
-		_ = preparers.RunTestWithBp(t, fmt.Sprintf(bpBase, `contents: ["libfoo"]`)+`
-			java_sdk_library_import {
-				name: "libfoo",
-				public: {
-					jars: ["libbar.jar"],
-				},
-				apex_available: ["com.android.myapex"],
-			}
-		`)
-	})
-
-	t.Run("prebuilt_bootclasspath_fragment", func(t *testing.T) {
-		_ = preparers.RunTestWithBp(t, fmt.Sprintf(bpBase, `
-			image_name: "art",
-			contents: ["libfoo"],
-		`)+`
-			java_sdk_library_import {
-				name: "libfoo",
-				public: {
-					jars: ["libbar.jar"],
-				},
-				apex_available: ["com.android.myapex"],
-			}
-		`)
-	})
-}
-
 func TestUpdatable_should_set_min_sdk_version(t *testing.T) {
 	testApexError(t, `"myapex" .*: updatable: updatable APEXes should set min_sdk_version`, `
 		apex {
@@ -6666,6 +7005,49 @@
 	`)
 }
 
+func TestUpdatable_should_not_set_generate_classpaths_proto(t *testing.T) {
+	testApexError(t, `"mysystemserverclasspathfragment" .* it must not set generate_classpaths_proto to false`, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			systemserverclasspath_fragments: [
+				"mysystemserverclasspathfragment",
+			],
+			min_sdk_version: "29",
+			updatable: true,
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		java_library {
+			name: "foo",
+			srcs: ["b.java"],
+			min_sdk_version: "29",
+			installable: true,
+			apex_available: [
+				"myapex",
+			],
+		}
+
+		systemserverclasspath_fragment {
+			name: "mysystemserverclasspathfragment",
+			generate_classpaths_proto: false,
+			contents: [
+				"foo",
+			],
+			apex_available: [
+				"myapex",
+			],
+		}
+	`,
+		dexpreopt.FixtureSetApexSystemServerJars("myapex:foo"),
+	)
+}
+
 func TestNoUpdatableJarsInBootImage(t *testing.T) {
 	// Set the BootJars in dexpreopt.GlobalConfig and productVariables to the same value. This can
 	// result in an invalid configuration as it does not set the ArtApexJars and allows art apex
@@ -6693,18 +7075,30 @@
 	}
 
 	t.Run("updatable jar from ART apex in the ART boot image => ok", func(t *testing.T) {
-		preparer := java.FixtureConfigureBootJars("com.android.art.debug:some-art-lib")
-		fragment := java.ApexVariantReference{
-			Apex:   proptools.StringPtr("com.android.art.debug"),
-			Module: proptools.StringPtr("art-bootclasspath-fragment"),
+		preparer := android.GroupFixturePreparers(
+			java.FixtureConfigureBootJars("com.android.art.debug:some-art-lib"),
+			java.FixtureConfigureApexBootJars("some-non-updatable-apex:some-non-updatable-apex-lib"),
+		)
+		fragments := []java.ApexVariantReference{
+			{
+				Apex:   proptools.StringPtr("com.android.art.debug"),
+				Module: proptools.StringPtr("art-bootclasspath-fragment"),
+			},
+			{
+				Apex:   proptools.StringPtr("some-non-updatable-apex"),
+				Module: proptools.StringPtr("some-non-updatable-fragment"),
+			},
 		}
-		testNoUpdatableJarsInBootImage(t, "", preparer, fragment)
+		testNoUpdatableJarsInBootImage(t, "", preparer, fragments...)
 	})
 
 	t.Run("updatable jar from ART apex in the framework boot image => error", func(t *testing.T) {
 		err := `module "some-art-lib" from updatable apexes \["com.android.art.debug"\] is not allowed in the framework boot image`
 		// Update the dexpreopt BootJars directly.
-		preparer := prepareSetBootJars("com.android.art.debug:some-art-lib")
+		preparer := android.GroupFixturePreparers(
+			prepareSetBootJars("com.android.art.debug:some-art-lib"),
+			java.FixtureConfigureApexBootJars("some-non-updatable-apex:some-non-updatable-apex-lib"),
+		)
 		testNoUpdatableJarsInBootImage(t, err, preparer)
 	})
 
@@ -6724,12 +7118,15 @@
 
 	t.Run("updatable jar from some other apex in the framework boot image => error", func(t *testing.T) {
 		err := `module "some-updatable-apex-lib" from updatable apexes \["some-updatable-apex"\] is not allowed in the framework boot image`
-		preparer := java.FixtureConfigureBootJars("some-updatable-apex:some-updatable-apex-lib")
+		preparer := android.GroupFixturePreparers(
+			java.FixtureConfigureBootJars("some-updatable-apex:some-updatable-apex-lib"),
+			java.FixtureConfigureApexBootJars("some-non-updatable-apex:some-non-updatable-apex-lib"),
+		)
 		testNoUpdatableJarsInBootImage(t, err, preparer)
 	})
 
 	t.Run("non-updatable jar from some other apex in the framework boot image => ok", func(t *testing.T) {
-		preparer := java.FixtureConfigureBootJars("some-non-updatable-apex:some-non-updatable-apex-lib")
+		preparer := java.FixtureConfigureApexBootJars("some-non-updatable-apex:some-non-updatable-apex-lib")
 		fragment := java.ApexVariantReference{
 			Apex:   proptools.StringPtr("some-non-updatable-apex"),
 			Module: proptools.StringPtr("some-non-updatable-fragment"),
@@ -6757,13 +7154,22 @@
 	})
 
 	t.Run("platform jar in the framework boot image => ok", func(t *testing.T) {
-		preparer := java.FixtureConfigureBootJars("platform:some-platform-lib")
-		testNoUpdatableJarsInBootImage(t, "", preparer)
+		preparer := android.GroupFixturePreparers(
+			java.FixtureConfigureBootJars("platform:some-platform-lib"),
+			java.FixtureConfigureApexBootJars("some-non-updatable-apex:some-non-updatable-apex-lib"),
+		)
+		fragments := []java.ApexVariantReference{
+			{
+				Apex:   proptools.StringPtr("some-non-updatable-apex"),
+				Module: proptools.StringPtr("some-non-updatable-fragment"),
+			},
+		}
+		testNoUpdatableJarsInBootImage(t, "", preparer, fragments...)
 	})
 }
 
 func TestDexpreoptAccessDexFilesFromPrebuiltApex(t *testing.T) {
-	preparer := java.FixtureConfigureBootJars("myapex:libfoo")
+	preparer := java.FixtureConfigureApexBootJars("myapex:libfoo")
 	t.Run("prebuilt no source", func(t *testing.T) {
 		fragment := java.ApexVariantReference{
 			Apex:   proptools.StringPtr("myapex"),
@@ -6788,18 +7194,26 @@
 				name: "my-bootclasspath-fragment",
 				contents: ["libfoo"],
 				apex_available: ["myapex"],
+				hidden_api: {
+					annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv",
+					metadata: "my-bootclasspath-fragment/metadata.csv",
+					index: "my-bootclasspath-fragment/index.csv",
+					stub_flags: "my-bootclasspath-fragment/stub-flags.csv",
+					all_flags: "my-bootclasspath-fragment/all-flags.csv",
+				},
 			}
 
 			java_import {
 				name: "libfoo",
 				jars: ["libfoo.jar"],
 				apex_available: ["myapex"],
+				permitted_packages: ["libfoo"],
 			}
 		`, "", preparer, fragment)
 	})
 }
 
-func testApexPermittedPackagesRules(t *testing.T, errmsg, bp string, apexBootJars []string, rules []android.Rule) {
+func testApexPermittedPackagesRules(t *testing.T, errmsg, bp string, bootJars []string, rules []android.Rule) {
 	t.Helper()
 	bp += `
 	apex_key {
@@ -6824,11 +7238,11 @@
 		PrepareForTestWithApexBuildComponents,
 		android.PrepareForTestWithNeverallowRules(rules),
 		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			updatableBootJars := make([]string, 0, len(apexBootJars))
-			for _, apexBootJar := range apexBootJars {
-				updatableBootJars = append(updatableBootJars, "myapex:"+apexBootJar)
+			apexBootJars := make([]string, 0, len(bootJars))
+			for _, apexBootJar := range bootJars {
+				apexBootJars = append(apexBootJars, "myapex:"+apexBootJar)
 			}
-			variables.UpdatableBootJars = android.CreateTestConfiguredJarList(updatableBootJars)
+			variables.ApexBootJars = android.CreateTestConfiguredJarList(apexBootJars)
 		}),
 		fs.AddToFixture(),
 	).
@@ -7627,6 +8041,28 @@
 	}
 }
 
+func TestHostApexInHostOnlyBuild(t *testing.T) {
+	testApex(t, `
+		apex {
+			name: "myapex",
+			host_supported: true,
+			key: "myapex.key",
+			updatable: false,
+			payload_type: "zip",
+		}
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+	`,
+		android.FixtureModifyConfig(func(config android.Config) {
+			// We may not have device targets in all builds, e.g. in
+			// prebuilts/build-tools/build-prebuilts.sh
+			config.Targets[android.Android] = []android.Target{}
+		}))
+}
+
 func TestApexJavaCoverage(t *testing.T) {
 	bp := `
 		apex {
@@ -7684,6 +8120,8 @@
 		java.PrepareForTestWithJavaDefaultModules,
 		android.PrepareForTestWithAndroidBuildComponents,
 		android.FixtureWithRootAndroidBp(bp),
+		dexpreopt.FixtureSetApexBootJars("myapex:mybootclasspathlib"),
+		dexpreopt.FixtureSetApexSystemServerJars("myapex:mysystemserverclasspathlib"),
 		android.FixtureMergeEnv(map[string]string{
 			"EMMA_INSTRUMENT": "true",
 		}),
@@ -7701,6 +8139,57 @@
 	}
 }
 
+func TestProhibitStaticExecutable(t *testing.T) {
+	testApexError(t, `executable mybin is static`, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			binaries: ["mybin"],
+			min_sdk_version: "29",
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_binary {
+			name: "mybin",
+			srcs: ["mylib.cpp"],
+			relative_install_path: "foo/bar",
+			static_executable: true,
+			system_shared_libs: [],
+			stl: "none",
+			apex_available: [ "myapex" ],
+			min_sdk_version: "29",
+		}
+	`)
+
+	testApexError(t, `executable mybin.rust is static`, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			binaries: ["mybin.rust"],
+			min_sdk_version: "29",
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		rust_binary {
+			name: "mybin.rust",
+			srcs: ["foo.rs"],
+			static_executable: true,
+			apex_available: ["myapex"],
+			min_sdk_version: "29",
+		}
+	`)
+}
+
 func TestMain(m *testing.M) {
 	os.Exit(m.Run())
 }
diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go
index 4b1600e..3e19014 100644
--- a/apex/bootclasspath_fragment_test.go
+++ b/apex/bootclasspath_fragment_test.go
@@ -130,7 +130,8 @@
 	result := android.GroupFixturePreparers(
 		prepareForTestWithBootclasspathFragment,
 		// Configure some libraries in the art bootclasspath_fragment and platform_bootclasspath.
-		java.FixtureConfigureBootJars("com.android.art:baz", "com.android.art:quuz", "platform:foo", "platform:bar"),
+		java.FixtureConfigureBootJars("com.android.art:baz", "com.android.art:quuz"),
+		java.FixtureConfigureApexBootJars("someapex:foo", "someapex:bar"),
 		prepareForTestWithArtApex,
 
 		java.PrepareForTestWithJavaSdkLibraryFiles,
@@ -383,6 +384,13 @@
 				apex_available: [
 					"com.android.art",
 				],
+				hidden_api: {
+					annotation_flags: "mybootclasspathfragment/annotation-flags.csv",
+					metadata: "mybootclasspathfragment/metadata.csv",
+					index: "mybootclasspathfragment/index.csv",
+					stub_flags: "mybootclasspathfragment/stub-flags.csv",
+					all_flags: "mybootclasspathfragment/all-flags.csv",
+				},
 			}
 		`, contentsInsert(contents), prefer)
 		return android.FixtureAddTextFile("prebuilts/module_sdk/art/Android.bp", text)
@@ -582,6 +590,13 @@
 			apex_available: [
 				"com.android.art",
 			],
+			hidden_api: {
+				annotation_flags: "mybootclasspathfragment/annotation-flags.csv",
+				metadata: "mybootclasspathfragment/metadata.csv",
+				index: "mybootclasspathfragment/index.csv",
+				stub_flags: "mybootclasspathfragment/stub-flags.csv",
+				all_flags: "mybootclasspathfragment/all-flags.csv",
+			},
 		}
 	`)
 
@@ -628,7 +643,7 @@
 		prepareForTestWithBootclasspathFragment,
 		prepareForTestWithMyapex,
 		// Configure bootclasspath jars to ensure that hidden API encoding is performed on them.
-		java.FixtureConfigureBootJars("myapex:foo", "myapex:bar"),
+		java.FixtureConfigureApexBootJars("myapex:foo", "myapex:bar"),
 		// Make sure that the frameworks/base/Android.bp file exists as otherwise hidden API encoding
 		// is disabled.
 		android.FixtureAddTextFile("frameworks/base/Android.bp", ""),
@@ -734,7 +749,7 @@
 		prepareForTestWithMyapex,
 		// Configure bootclasspath jars to ensure that hidden API encoding is performed on them.
 		java.FixtureConfigureBootJars("com.android.art:baz", "com.android.art:quuz"),
-		java.FixtureConfigureUpdatableBootJars("myapex:foo", "myapex:bar"),
+		java.FixtureConfigureApexBootJars("myapex:foo", "myapex:bar"),
 		// Make sure that the frameworks/base/Android.bp file exists as otherwise hidden API encoding
 		// is disabled.
 		android.FixtureAddTextFile("frameworks/base/Android.bp", ""),
@@ -879,7 +894,8 @@
 		prepareForTestWithArtApex,
 		prepareForTestWithMyapex,
 		// Configure bootclasspath jars to ensure that hidden API encoding is performed on them.
-		java.FixtureConfigureBootJars("com.android.art:baz", "com.android.art:quuz", "myapex:foo", "myapex:bar"),
+		java.FixtureConfigureBootJars("com.android.art:baz", "com.android.art:quuz"),
+		java.FixtureConfigureApexBootJars("myapex:foo", "myapex:bar"),
 		// Make sure that the frameworks/base/Android.bp file exists as otherwise hidden API encoding
 		// is disabled.
 		android.FixtureAddTextFile("frameworks/base/Android.bp", ""),
@@ -1048,7 +1064,8 @@
 		prepareForTestWithArtApex,
 		prepareForTestWithMyapex,
 		// Configure bootclasspath jars to ensure that hidden API encoding is performed on them.
-		java.FixtureConfigureBootJars("com.android.art:baz", "com.android.art:quuz", "myapex:foo", "myapex:bar"),
+		java.FixtureConfigureBootJars("com.android.art:baz", "com.android.art:quuz"),
+		java.FixtureConfigureApexBootJars("myapex:foo", "myapex:bar"),
 		// Make sure that the frameworks/base/Android.bp file exists as otherwise hidden API encoding
 		// is disabled.
 		android.FixtureAddTextFile("frameworks/base/Android.bp", ""),
diff --git a/apex/builder.go b/apex/builder.go
index d2e6ad8..5baa5c0 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -75,7 +75,7 @@
 	// by default set to (uid/gid/mode) = (1000/1000/0644)
 	// TODO(b/113082813) make this configurable using config.fs syntax
 	generateFsConfig = pctx.StaticRule("generateFsConfig", blueprint.RuleParams{
-		Command: `( echo '/ 1000 1000 0755' ` +
+		Command: `( set -e; echo '/ 1000 1000 0755' ` +
 			`&& for i in ${ro_paths}; do echo "/$$i 1000 1000 0644"; done ` +
 			`&& for i in  ${exec_paths}; do echo "/$$i 0 2000 0755"; done ` +
 			`&& ( tr ' ' '\n' <${out}.apklist | for i in ${apk_paths}; do read apk; echo "/$$i 0 2000 0755"; zipinfo -1 $$apk | sed "s:\(.*\):/$$i/\1 1000 1000 0644:"; done ) ) > ${out}`,
@@ -602,6 +602,11 @@
 		// codename
 		if moduleMinSdkVersion.IsCurrent() || moduleMinSdkVersion.IsNone() {
 			minSdkVersion = ctx.Config().DefaultAppTargetSdk(ctx).String()
+
+			if java.UseApiFingerprint(ctx) {
+				minSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", java.ApiFingerprintPath(ctx).String())
+				implicitInputs = append(implicitInputs, java.ApiFingerprintPath(ctx))
+			}
 		}
 		// apex module doesn't have a concept of target_sdk_version, hence for the time
 		// being targetSdkVersion == default targetSdkVersion of the branch.
@@ -611,10 +616,6 @@
 			targetSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", java.ApiFingerprintPath(ctx).String())
 			implicitInputs = append(implicitInputs, java.ApiFingerprintPath(ctx))
 		}
-		if java.UseApiFingerprint(ctx) {
-			minSdkVersion = ctx.Config().PlatformSdkCodename() + fmt.Sprintf(".$$(cat %s)", java.ApiFingerprintPath(ctx).String())
-			implicitInputs = append(implicitInputs, java.ApiFingerprintPath(ctx))
-		}
 		optFlags = append(optFlags, "--target_sdk_version "+targetSdkVersion)
 		optFlags = append(optFlags, "--min_sdk_version "+minSdkVersion)
 
@@ -760,7 +761,7 @@
 	rule := java.Signapk
 	args := map[string]string{
 		"certificates": pem.String() + " " + key.String(),
-		"flags":        "-a 4096", //alignment
+		"flags":        "-a 4096 --align-file-size", //alignment
 	}
 	implicits := android.Paths{pem, key}
 	if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_SIGNAPK") {
diff --git a/apex/classpath_element_test.go b/apex/classpath_element_test.go
index 0193127..60f18bd 100644
--- a/apex/classpath_element_test.go
+++ b/apex/classpath_element_test.go
@@ -58,6 +58,7 @@
 		}),
 		java.PrepareForTestWithJavaSdkLibraryFiles,
 		java.FixtureWithLastReleaseApis("foo", "othersdklibrary"),
+		java.FixtureConfigureApexBootJars("myapex:bar"),
 		android.FixtureWithRootAndroidBp(`
 		apex {
 			name: "com.android.art",
@@ -79,6 +80,7 @@
 
 		bootclasspath_fragment {
 			name: "art-bootclasspath-fragment",
+			image_name: "art",
 			apex_available: [
 				"com.android.art",
 			],
@@ -157,11 +159,6 @@
 			],
 		}
 
-		bootclasspath_fragment {
-			name: "non-apex-fragment",
-			contents: ["othersdklibrary"],
-		}
-
 		apex {
 			name: "otherapex",
 			key: "otherapex.key",
@@ -193,6 +190,10 @@
 					apex: "com.android.art",
 					module: "art-bootclasspath-fragment",
 				},
+				{
+					apex: "myapex",
+					module: "mybootclasspath-fragment",
+				},
 			],
 		}
 	`),
@@ -207,7 +208,6 @@
 	myFragment := result.Module("mybootclasspath-fragment", "android_common_apex10000")
 	myBar := result.Module("bar", "android_common_apex10000")
 
-	nonApexFragment := result.Module("non-apex-fragment", "android_common")
 	other := result.Module("othersdklibrary", "android_common_apex10000")
 
 	otherApexLibrary := result.Module("otherapexlibrary", "android_common_apex10000")
@@ -247,15 +247,6 @@
 		assertElementsEquals(t, "elements", expectedElements, elements)
 	})
 
-	// Verify that CreateClasspathElements detects when a fragment does not have an associated apex.
-	t.Run("non apex fragment", func(t *testing.T) {
-		ctx := newCtx()
-		elements := java.CreateClasspathElements(ctx, []android.Module{}, []android.Module{nonApexFragment})
-		android.FailIfNoMatchingErrors(t, "fragment non-apex-fragment{.*} is not part of an apex", ctx.errs)
-		expectedElements := java.ClasspathElements{}
-		assertElementsEquals(t, "elements", expectedElements, elements)
-	})
-
 	// Verify that CreateClasspathElements detects when an apex has multiple fragments.
 	t.Run("multiple fragments for same apex", func(t *testing.T) {
 		ctx := newCtx()
diff --git a/apex/key.go b/apex/key.go
index 8b33b59..468bb8a 100644
--- a/apex/key.go
+++ b/apex/key.go
@@ -20,6 +20,7 @@
 	"strings"
 
 	"android/soong/android"
+	"android/soong/bazel"
 
 	"github.com/google/blueprint/proptools"
 )
@@ -33,10 +34,13 @@
 func registerApexKeyBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("apex_key", ApexKeyFactory)
 	ctx.RegisterSingletonType("apex_keys_text", apexKeysTextFactory)
+
+	android.RegisterBp2BuildMutator("apex_key", ApexKeyBp2Build)
 }
 
 type apexKey struct {
 	android.ModuleBase
+	android.BazelModuleBase
 
 	properties apexKeyProperties
 
@@ -61,6 +65,7 @@
 	module := &apexKey{}
 	module.AddProperties(&module.properties)
 	android.InitAndroidArchModule(module, android.HostAndDeviceDefault, android.MultilibCommon)
+	android.InitBazelModule(module)
 	return module
 }
 
@@ -190,3 +195,50 @@
 func (s *apexKeysText) MakeVars(ctx android.MakeVarsContext) {
 	ctx.Strict("SOONG_APEX_KEYS_FILE", s.output.String())
 }
+
+// For Bazel / bp2build
+
+type bazelApexKeyAttributes struct {
+	Public_key  bazel.LabelAttribute
+	Private_key bazel.LabelAttribute
+}
+
+func ApexKeyBp2Build(ctx android.TopDownMutatorContext) {
+	module, ok := ctx.Module().(*apexKey)
+	if !ok {
+		// Not an APEX key
+		return
+	}
+	if !module.ConvertWithBp2build(ctx) {
+		return
+	}
+	if ctx.ModuleType() != "apex_key" {
+		return
+	}
+
+	apexKeyBp2BuildInternal(ctx, module)
+}
+
+func apexKeyBp2BuildInternal(ctx android.TopDownMutatorContext, module *apexKey) {
+	var privateKeyLabelAttribute bazel.LabelAttribute
+	if module.properties.Private_key != nil {
+		privateKeyLabelAttribute.SetValue(android.BazelLabelForModuleSrcSingle(ctx, *module.properties.Private_key))
+	}
+
+	var publicKeyLabelAttribute bazel.LabelAttribute
+	if module.properties.Public_key != nil {
+		publicKeyLabelAttribute.SetValue(android.BazelLabelForModuleSrcSingle(ctx, *module.properties.Public_key))
+	}
+
+	attrs := &bazelApexKeyAttributes{
+		Private_key: privateKeyLabelAttribute,
+		Public_key:  publicKeyLabelAttribute,
+	}
+
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class:        "apex_key",
+		Bzl_load_location: "//build/bazel/rules:apex_key.bzl",
+	}
+
+	ctx.CreateBazelTargetModule(module.Name(), props, attrs)
+}
diff --git a/apex/platform_bootclasspath_test.go b/apex/platform_bootclasspath_test.go
index bc35479..513ddc0 100644
--- a/apex/platform_bootclasspath_test.go
+++ b/apex/platform_bootclasspath_test.go
@@ -39,7 +39,7 @@
 		prepareForTestWithMyapex,
 		java.PrepareForTestWithJavaSdkLibraryFiles,
 		java.FixtureWithLastReleaseApis("foo"),
-		java.FixtureConfigureBootJars("myapex:bar"),
+		java.FixtureConfigureApexBootJars("myapex:bar"),
 		android.FixtureWithRootAndroidBp(`
 			platform_bootclasspath {
 				name: "platform-bootclasspath",
@@ -159,11 +159,12 @@
 		android.AssertPathsRelativeToTopEquals(t, message, expected, info.FlagsFilesByCategory[category])
 	}
 
-	android.AssertPathsRelativeToTopEquals(t, "stub flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/stub-flags.csv"}, info.StubFlagsPaths)
 	android.AssertPathsRelativeToTopEquals(t, "annotation flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/annotation-flags.csv"}, info.AnnotationFlagsPaths)
 	android.AssertPathsRelativeToTopEquals(t, "metadata flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/metadata.csv"}, info.MetadataPaths)
 	android.AssertPathsRelativeToTopEquals(t, "index flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/index.csv"}, info.IndexPaths)
-	android.AssertPathsRelativeToTopEquals(t, "all flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/all-flags.csv"}, info.AllFlagsPaths)
+
+	android.AssertArrayString(t, "stub flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/filtered-stub-flags.csv:out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/signature-patterns.csv"}, info.StubFlagSubsets.RelativeToTop())
+	android.AssertArrayString(t, "all flags", []string{"out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/filtered-flags.csv:out/soong/.intermediates/bar-fragment/android_common_apex10000/modular-hiddenapi/signature-patterns.csv"}, info.FlagSubsets.RelativeToTop())
 }
 
 func TestPlatformBootclasspathDependencies(t *testing.T) {
@@ -173,7 +174,7 @@
 		prepareForTestWithMyapex,
 		// Configure some libraries in the art and framework boot images.
 		java.FixtureConfigureBootJars("com.android.art:baz", "com.android.art:quuz", "platform:foo"),
-		java.FixtureConfigureUpdatableBootJars("myapex:bar"),
+		java.FixtureConfigureApexBootJars("myapex:bar"),
 		java.PrepareForTestWithJavaSdkLibraryFiles,
 		java.FixtureWithLastReleaseApis("foo"),
 	).RunTestWithBp(t, `
@@ -194,6 +195,7 @@
 
 		bootclasspath_fragment {
 			name: "art-bootclasspath-fragment",
+			image_name: "art",
 			apex_available: [
 				"com.android.art",
 			],
@@ -288,7 +290,7 @@
 		"com.android.art:quuz",
 		"platform:foo",
 
-		// The configured contents of UpdatableBootJars.
+		// The configured contents of ApexBootJars.
 		"myapex:bar",
 	})
 
@@ -313,7 +315,7 @@
 		`com.android.art:quuz`,
 		`platform:foo`,
 
-		// The configured contents of UpdatableBootJars.
+		// The configured contents of ApexBootJars.
 		`myapex:bar`,
 
 		// The fragments.
@@ -348,7 +350,7 @@
 		// if the dependency on myapex:foo is filtered out because of either of those conditions then
 		// the dependencies resolved by the platform_bootclasspath will not match the configured list
 		// and so will fail the test.
-		java.FixtureConfigureUpdatableBootJars("myapex:foo", "myapex:bar"),
+		java.FixtureConfigureApexBootJars("myapex:foo", "myapex:bar"),
 		java.PrepareForTestWithJavaSdkLibraryFiles,
 		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
 			variables.Always_use_prebuilt_sdks = proptools.BoolPtr(true)
@@ -482,3 +484,199 @@
 	pairs := java.ApexNamePairsFromModules(ctx, modules)
 	android.AssertDeepEquals(t, "module dependencies", expected, pairs)
 }
+
+// TestPlatformBootclasspath_IncludesRemainingApexJars verifies that any apex boot jar is present in
+// platform_bootclasspath's classpaths.proto config, if the apex does not generate its own config
+// by setting generate_classpaths_proto property to false.
+func TestPlatformBootclasspath_IncludesRemainingApexJars(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForTestWithPlatformBootclasspath,
+		prepareForTestWithMyapex,
+		java.FixtureConfigureApexBootJars("myapex:foo"),
+		android.FixtureWithRootAndroidBp(`
+			platform_bootclasspath {
+				name: "platform-bootclasspath",
+				fragments: [
+					{
+						apex: "myapex",
+						module:"foo-fragment",
+					},
+				],
+			}
+
+			apex {
+				name: "myapex",
+				key: "myapex.key",
+				bootclasspath_fragments: ["foo-fragment"],
+				updatable: false,
+			}
+
+			apex_key {
+				name: "myapex.key",
+				public_key: "testkey.avbpubkey",
+				private_key: "testkey.pem",
+			}
+
+			bootclasspath_fragment {
+				name: "foo-fragment",
+				generate_classpaths_proto: false,
+				contents: ["foo"],
+				apex_available: ["myapex"],
+			}
+
+			java_library {
+				name: "foo",
+				srcs: ["a.java"],
+				system_modules: "none",
+				sdk_version: "none",
+				compile_dex: true,
+				apex_available: ["myapex"],
+				permitted_packages: ["foo"],
+			}
+		`),
+	).RunTest(t)
+
+	java.CheckClasspathFragmentProtoContentInfoProvider(t, result,
+		true,         // proto should be generated
+		"myapex:foo", // apex doesn't generate its own config, so must be in platform_bootclasspath
+		"bootclasspath.pb",
+		"out/soong/target/product/test_device/system/etc/classpaths",
+	)
+}
+
+func TestBootJarNotInApex(t *testing.T) {
+	android.GroupFixturePreparers(
+		prepareForTestWithPlatformBootclasspath,
+		PrepareForTestWithApexBuildComponents,
+		prepareForTestWithMyapex,
+		java.FixtureConfigureApexBootJars("myapex:foo"),
+	).ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
+		`dependency "foo" of "myplatform-bootclasspath" missing variant`)).
+		RunTestWithBp(t, `
+			apex {
+				name: "myapex",
+				key: "myapex.key",
+				updatable: false,
+			}
+
+			apex_key {
+				name: "myapex.key",
+				public_key: "testkey.avbpubkey",
+				private_key: "testkey.pem",
+			}
+
+			java_library {
+				name: "foo",
+				srcs: ["b.java"],
+				installable: true,
+				apex_available: [
+					"myapex",
+				],
+			}
+
+			bootclasspath_fragment {
+				name: "not-in-apex-fragment",
+				contents: [
+					"foo",
+				],
+			}
+
+			platform_bootclasspath {
+				name: "myplatform-bootclasspath",
+			}
+		`)
+}
+
+func TestBootFragmentNotInApex(t *testing.T) {
+	android.GroupFixturePreparers(
+		prepareForTestWithPlatformBootclasspath,
+		PrepareForTestWithApexBuildComponents,
+		prepareForTestWithMyapex,
+		java.FixtureConfigureApexBootJars("myapex:foo"),
+	).ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
+		`library foo.*have no corresponding fragment.*`)).RunTestWithBp(t, `
+			apex {
+				name: "myapex",
+				key: "myapex.key",
+				java_libs: ["foo"],
+				updatable: false,
+			}
+
+			apex_key {
+				name: "myapex.key",
+				public_key: "testkey.avbpubkey",
+				private_key: "testkey.pem",
+			}
+
+			java_library {
+				name: "foo",
+				srcs: ["b.java"],
+				installable: true,
+				apex_available: ["myapex"],
+				permitted_packages: ["foo"],
+			}
+
+			bootclasspath_fragment {
+				name: "not-in-apex-fragment",
+				contents: ["foo"],
+			}
+
+			platform_bootclasspath {
+				name: "myplatform-bootclasspath",
+			}
+		`)
+}
+
+func TestNonBootJarInFragment(t *testing.T) {
+	android.GroupFixturePreparers(
+		prepareForTestWithPlatformBootclasspath,
+		PrepareForTestWithApexBuildComponents,
+		prepareForTestWithMyapex,
+		java.FixtureConfigureApexBootJars("myapex:foo"),
+	).ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
+		`in contents must also be declared in PRODUCT_APEX_BOOT_JARS`)).
+		RunTestWithBp(t, `
+			apex {
+				name: "myapex",
+				key: "myapex.key",
+				bootclasspath_fragments: ["apex-fragment"],
+				updatable: false,
+			}
+
+			apex_key {
+				name: "myapex.key",
+				public_key: "testkey.avbpubkey",
+				private_key: "testkey.pem",
+			}
+
+			java_library {
+				name: "foo",
+				srcs: ["b.java"],
+				installable: true,
+				apex_available: ["myapex"],
+				permitted_packages: ["foo"],
+			}
+
+			java_library {
+				name: "bar",
+				srcs: ["b.java"],
+				installable: true,
+				apex_available: ["myapex"],
+				permitted_packages: ["bar"],
+			}
+
+			bootclasspath_fragment {
+				name: "apex-fragment",
+				contents: ["foo", "bar"],
+				apex_available:[ "myapex" ],
+			}
+
+			platform_bootclasspath {
+				name: "myplatform-bootclasspath",
+				fragments: [{
+						apex: "myapex",
+						module:"apex-fragment",
+				}],
+			}
+		`)
+}
diff --git a/apex/systemserver_classpath_fragment_test.go b/apex/systemserver_classpath_fragment_test.go
index 95b6e23..a8d5931 100644
--- a/apex/systemserver_classpath_fragment_test.go
+++ b/apex/systemserver_classpath_fragment_test.go
@@ -15,6 +15,7 @@
 package apex
 
 import (
+	"android/soong/dexpreopt"
 	"testing"
 
 	"android/soong/android"
@@ -30,6 +31,7 @@
 	result := android.GroupFixturePreparers(
 		prepareForTestWithSystemserverclasspathFragment,
 		prepareForTestWithMyapex,
+		dexpreopt.FixtureSetApexSystemServerJars("myapex:foo"),
 	).RunTestWithBp(t, `
 		apex {
 			name: "myapex",
@@ -76,3 +78,106 @@
 		`mysystemserverclasspathfragment`,
 	})
 }
+
+func TestSystemserverclasspathFragmentNoGeneratedProto(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForTestWithSystemserverclasspathFragment,
+		prepareForTestWithMyapex,
+		dexpreopt.FixtureSetApexSystemServerJars("myapex:foo"),
+	).RunTestWithBp(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			systemserverclasspath_fragments: [
+				"mysystemserverclasspathfragment",
+			],
+			updatable: false,
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		java_library {
+			name: "foo",
+			srcs: ["b.java"],
+			installable: true,
+			apex_available: [
+				"myapex",
+			],
+		}
+
+		systemserverclasspath_fragment {
+			name: "mysystemserverclasspathfragment",
+			generate_classpaths_proto: false,
+			contents: [
+				"foo",
+			],
+			apex_available: [
+				"myapex",
+			],
+		}
+	`)
+
+	ensureExactContents(t, result.TestContext, "myapex", "android_common_myapex_image", []string{
+		"javalib/foo.jar",
+	})
+
+	java.CheckModuleDependencies(t, result.TestContext, "myapex", "android_common_myapex_image", []string{
+		`myapex.key`,
+		`mysystemserverclasspathfragment`,
+	})
+}
+
+func TestSystemServerClasspathFragmentWithContentNotInMake(t *testing.T) {
+	android.GroupFixturePreparers(
+		prepareForTestWithSystemserverclasspathFragment,
+		prepareForTestWithMyapex,
+		dexpreopt.FixtureSetApexSystemServerJars("myapex:foo"),
+	).
+		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
+			`in contents must also be declared in PRODUCT_UPDATABLE_SYSTEM_SERVER_JARS`)).
+		RunTestWithBp(t, `
+			apex {
+				name: "myapex",
+				key: "myapex.key",
+				systemserverclasspath_fragments: [
+					"mysystemserverclasspathfragment",
+				],
+				updatable: false,
+			}
+
+			apex_key {
+				name: "myapex.key",
+				public_key: "testkey.avbpubkey",
+				private_key: "testkey.pem",
+			}
+
+			java_library {
+				name: "foo",
+				srcs: ["b.java"],
+				installable: true,
+				apex_available: ["myapex"],
+			}
+
+			java_library {
+				name: "bar",
+				srcs: ["b.java"],
+				installable: true,
+				apex_available: ["myapex"],
+			}
+
+			systemserverclasspath_fragment {
+				name: "mysystemserverclasspathfragment",
+				contents: [
+					"foo",
+					"bar",
+				],
+				apex_available: [
+					"myapex",
+				],
+			}
+		`)
+}
diff --git a/bazel/Android.bp b/bazel/Android.bp
index b7c185a..b68d65b 100644
--- a/bazel/Android.bp
+++ b/bazel/Android.bp
@@ -7,6 +7,7 @@
     pkgPath: "android/soong/bazel",
     srcs: [
         "aquery.go",
+        "configurability.go",
         "constants.go",
         "properties.go",
     ],
diff --git a/bazel/aquery.go b/bazel/aquery.go
index 555f1dc..8741afb 100644
--- a/bazel/aquery.go
+++ b/bazel/aquery.go
@@ -73,31 +73,39 @@
 // BuildStatement contains information to register a build statement corresponding (one to one)
 // with a Bazel action from Bazel's action graph.
 type BuildStatement struct {
-	Command     string
-	Depfile     *string
-	OutputPaths []string
-	InputPaths  []string
-	Env         []KeyValuePair
-	Mnemonic    string
+	Command      string
+	Depfile      *string
+	OutputPaths  []string
+	InputPaths   []string
+	SymlinkPaths []string
+	Env          []KeyValuePair
+	Mnemonic     string
 }
 
-// AqueryBuildStatements returns an array of BuildStatements which should be registered (and output
-// to a ninja file) to correspond one-to-one with the given action graph json proto (from a bazel
-// aquery invocation).
-func AqueryBuildStatements(aqueryJsonProto []byte) ([]BuildStatement, error) {
-	buildStatements := []BuildStatement{}
+// A helper type for aquery processing which facilitates retrieval of path IDs from their
+// less readable Bazel structures (depset and path fragment).
+type aqueryArtifactHandler struct {
+	// Maps middleman artifact Id to input artifact depset ID.
+	// Middleman artifacts are treated as "substitute" artifacts for mixed builds. For example,
+	// if we find a middleman action which has outputs [foo, bar], and output [baz_middleman], then,
+	// for each other action which has input [baz_middleman], we add [foo, bar] to the inputs for
+	// that action instead.
+	middlemanIdToDepsetIds map[int][]int
+	// Maps depset Id to depset struct.
+	depsetIdToDepset map[int]depSetOfFiles
+	// depsetIdToArtifactIdsCache is a memoization of depset flattening, because flattening
+	// may be an expensive operation.
+	depsetIdToArtifactIdsCache map[int][]int
+	// Maps artifact Id to fully expanded path.
+	artifactIdToPath map[int]string
+}
 
-	var aqueryResult actionGraphContainer
-	err := json.Unmarshal(aqueryJsonProto, &aqueryResult)
-
-	if err != nil {
-		return nil, err
-	}
-
+func newAqueryHandler(aqueryResult actionGraphContainer) (*aqueryArtifactHandler, error) {
 	pathFragments := map[int]pathFragment{}
 	for _, pathFragment := range aqueryResult.PathFragments {
 		pathFragments[pathFragment.Id] = pathFragment
 	}
+
 	artifactIdToPath := map[int]string{}
 	for _, artifact := range aqueryResult.Artifacts {
 		artifactPath, err := expandPathFragment(artifact.PathFragmentId, pathFragments)
@@ -112,22 +120,87 @@
 		depsetIdToDepset[depset.Id] = depset
 	}
 
-	// depsetIdToArtifactIdsCache is a memoization of depset flattening, because flattening
-	// may be an expensive operation.
-	depsetIdToArtifactIdsCache := map[int][]int{}
-
 	// Do a pass through all actions to identify which artifacts are middleman artifacts.
-	// These will be omitted from the inputs of other actions.
-	// TODO(b/180945500): Handle middleman actions; without proper handling, depending on generated
-	// headers may cause build failures.
-	middlemanArtifactIds := map[int]bool{}
+	middlemanIdToDepsetIds := map[int][]int{}
 	for _, actionEntry := range aqueryResult.Actions {
 		if actionEntry.Mnemonic == "Middleman" {
 			for _, outputId := range actionEntry.OutputIds {
-				middlemanArtifactIds[outputId] = true
+				middlemanIdToDepsetIds[outputId] = actionEntry.InputDepSetIds
 			}
 		}
 	}
+	return &aqueryArtifactHandler{
+		middlemanIdToDepsetIds:     middlemanIdToDepsetIds,
+		depsetIdToDepset:           depsetIdToDepset,
+		depsetIdToArtifactIdsCache: map[int][]int{},
+		artifactIdToPath:           artifactIdToPath,
+	}, nil
+}
+
+func (a *aqueryArtifactHandler) getInputPaths(depsetIds []int) ([]string, error) {
+	inputPaths := []string{}
+
+	for _, inputDepSetId := range depsetIds {
+		inputArtifacts, err := a.artifactIdsFromDepsetId(inputDepSetId)
+		if err != nil {
+			return nil, err
+		}
+		for _, inputId := range inputArtifacts {
+			if middlemanInputDepsetIds, isMiddlemanArtifact := a.middlemanIdToDepsetIds[inputId]; isMiddlemanArtifact {
+				// Add all inputs from middleman actions which created middleman artifacts which are
+				// in the inputs for this action.
+				swappedInputPaths, err := a.getInputPaths(middlemanInputDepsetIds)
+				if err != nil {
+					return nil, err
+				}
+				inputPaths = append(inputPaths, swappedInputPaths...)
+			} else {
+				inputPath, exists := a.artifactIdToPath[inputId]
+				if !exists {
+					return nil, fmt.Errorf("undefined input artifactId %d", inputId)
+				}
+				inputPaths = append(inputPaths, inputPath)
+			}
+		}
+	}
+	return inputPaths, nil
+}
+
+func (a *aqueryArtifactHandler) artifactIdsFromDepsetId(depsetId int) ([]int, error) {
+	if result, exists := a.depsetIdToArtifactIdsCache[depsetId]; exists {
+		return result, nil
+	}
+	if depset, exists := a.depsetIdToDepset[depsetId]; exists {
+		result := depset.DirectArtifactIds
+		for _, childId := range depset.TransitiveDepSetIds {
+			childArtifactIds, err := a.artifactIdsFromDepsetId(childId)
+			if err != nil {
+				return nil, err
+			}
+			result = append(result, childArtifactIds...)
+		}
+		a.depsetIdToArtifactIdsCache[depsetId] = result
+		return result, nil
+	} else {
+		return nil, fmt.Errorf("undefined input depsetId %d", depsetId)
+	}
+}
+
+// AqueryBuildStatements returns an array of BuildStatements which should be registered (and output
+// to a ninja file) to correspond one-to-one with the given action graph json proto (from a bazel
+// aquery invocation).
+func AqueryBuildStatements(aqueryJsonProto []byte) ([]BuildStatement, error) {
+	buildStatements := []BuildStatement{}
+
+	var aqueryResult actionGraphContainer
+	err := json.Unmarshal(aqueryJsonProto, &aqueryResult)
+	if err != nil {
+		return nil, err
+	}
+	aqueryHandler, err := newAqueryHandler(aqueryResult)
+	if err != nil {
+		return nil, err
+	}
 
 	for _, actionEntry := range aqueryResult.Actions {
 		if shouldSkipAction(actionEntry) {
@@ -136,7 +209,7 @@
 		outputPaths := []string{}
 		var depfile *string
 		for _, outputId := range actionEntry.OutputIds {
-			outputPath, exists := artifactIdToPath[outputId]
+			outputPath, exists := aqueryHandler.artifactIdToPath[outputId]
 			if !exists {
 				return nil, fmt.Errorf("undefined outputId %d", outputId)
 			}
@@ -151,35 +224,32 @@
 				outputPaths = append(outputPaths, outputPath)
 			}
 		}
-		inputPaths := []string{}
-		for _, inputDepSetId := range actionEntry.InputDepSetIds {
-			inputArtifacts, err :=
-				artifactIdsFromDepsetId(depsetIdToDepset, depsetIdToArtifactIdsCache, inputDepSetId)
-			if err != nil {
-				return nil, err
-			}
-			for _, inputId := range inputArtifacts {
-				if _, isMiddlemanArtifact := middlemanArtifactIds[inputId]; isMiddlemanArtifact {
-					// Omit middleman artifacts.
-					continue
-				}
-				inputPath, exists := artifactIdToPath[inputId]
-				if !exists {
-					return nil, fmt.Errorf("undefined input artifactId %d", inputId)
-				}
-				inputPaths = append(inputPaths, inputPath)
-			}
+		inputPaths, err := aqueryHandler.getInputPaths(actionEntry.InputDepSetIds)
+		if err != nil {
+			return nil, err
 		}
+
 		buildStatement := BuildStatement{
 			Command:     strings.Join(proptools.ShellEscapeList(actionEntry.Arguments), " "),
 			Depfile:     depfile,
 			OutputPaths: outputPaths,
 			InputPaths:  inputPaths,
 			Env:         actionEntry.EnvironmentVariables,
-			Mnemonic:    actionEntry.Mnemonic}
-		if len(actionEntry.Arguments) < 1 {
+			Mnemonic:    actionEntry.Mnemonic,
+		}
+
+		if isSymlinkAction(actionEntry) {
+			if len(inputPaths) != 1 || len(outputPaths) != 1 {
+				return nil, fmt.Errorf("Expect 1 input and 1 output to symlink action, got: input %q, output %q", inputPaths, outputPaths)
+			}
+			out := outputPaths[0]
+			outDir := proptools.ShellEscapeIncludingSpaces(filepath.Dir(out))
+			out = proptools.ShellEscapeIncludingSpaces(out)
+			in := proptools.ShellEscapeIncludingSpaces(inputPaths[0])
+			buildStatement.Command = fmt.Sprintf("mkdir -p %[1]s && rm -f %[2]s && ln -rsf %[3]s %[2]s", outDir, out, in)
+			buildStatement.SymlinkPaths = outputPaths[:]
+		} else if len(actionEntry.Arguments) < 1 {
 			return nil, fmt.Errorf("received action with no command: [%v]", buildStatement)
-			continue
 		}
 		buildStatements = append(buildStatements, buildStatement)
 	}
@@ -187,13 +257,18 @@
 	return buildStatements, nil
 }
 
+func isSymlinkAction(a action) bool {
+	return a.Mnemonic == "Symlink" || a.Mnemonic == "SolibSymlink"
+}
+
 func shouldSkipAction(a action) bool {
-	// TODO(b/180945121): Handle symlink actions.
-	if a.Mnemonic == "Symlink" || a.Mnemonic == "SourceSymlinkManifest" || a.Mnemonic == "SymlinkTree" {
+	// TODO(b/180945121): Handle complex symlink actions.
+	if a.Mnemonic == "SymlinkTree" || a.Mnemonic == "SourceSymlinkManifest" {
 		return true
 	}
-	// TODO(b/180945500): Handle middleman actions; without proper handling, depending on generated
-	// headers may cause build failures.
+	// Middleman actions are not handled like other actions; they are handled separately as a
+	// preparatory step so that their inputs may be relayed to actions depending on middleman
+	// artifacts.
 	if a.Mnemonic == "Middleman" {
 		return true
 	}
@@ -209,28 +284,6 @@
 	return false
 }
 
-func artifactIdsFromDepsetId(depsetIdToDepset map[int]depSetOfFiles,
-	depsetIdToArtifactIdsCache map[int][]int, depsetId int) ([]int, error) {
-	if result, exists := depsetIdToArtifactIdsCache[depsetId]; exists {
-		return result, nil
-	}
-	if depset, exists := depsetIdToDepset[depsetId]; exists {
-		result := depset.DirectArtifactIds
-		for _, childId := range depset.TransitiveDepSetIds {
-			childArtifactIds, err :=
-				artifactIdsFromDepsetId(depsetIdToDepset, depsetIdToArtifactIdsCache, childId)
-			if err != nil {
-				return nil, err
-			}
-			result = append(result, childArtifactIds...)
-		}
-		depsetIdToArtifactIdsCache[depsetId] = result
-		return result, nil
-	} else {
-		return nil, fmt.Errorf("undefined input depsetId %d", depsetId)
-	}
-}
-
 func expandPathFragment(id int, pathFragmentsMap map[int]pathFragment) (string, error) {
 	labels := []string{}
 	currId := id
@@ -241,6 +294,9 @@
 			return "", fmt.Errorf("undefined path fragment id %d", currId)
 		}
 		labels = append([]string{currFragment.Label}, labels...)
+		if currId == currFragment.ParentId {
+			return "", fmt.Errorf("Fragment cannot refer to itself as parent %#v", currFragment)
+		}
 		currId = currFragment.ParentId
 	}
 	return filepath.Join(labels...), nil
diff --git a/bazel/aquery_test.go b/bazel/aquery_test.go
index fa8810f..43e4155 100644
--- a/bazel/aquery_test.go
+++ b/bazel/aquery_test.go
@@ -718,17 +718,316 @@
 	assertBuildStatements(t, expectedBuildStatements, actualbuildStatements)
 }
 
+func TestMiddlemenAction(t *testing.T) {
+	const inputString = `
+{
+  "artifacts": [{
+    "id": 1,
+    "pathFragmentId": 1
+  }, {
+    "id": 2,
+    "pathFragmentId": 2
+  }, {
+    "id": 3,
+    "pathFragmentId": 3
+  }, {
+    "id": 4,
+    "pathFragmentId": 4
+  }, {
+    "id": 5,
+    "pathFragmentId": 5
+  }, {
+    "id": 6,
+    "pathFragmentId": 6
+  }],
+  "pathFragments": [{
+    "id": 1,
+    "label": "middleinput_one"
+  }, {
+    "id": 2,
+    "label": "middleinput_two"
+  }, {
+    "id": 3,
+    "label": "middleman_artifact"
+  }, {
+    "id": 4,
+    "label": "maininput_one"
+  }, {
+    "id": 5,
+    "label": "maininput_two"
+  }, {
+    "id": 6,
+    "label": "output"
+  }],
+  "depSetOfFiles": [{
+    "id": 1,
+    "directArtifactIds": [1, 2]
+  }, {
+    "id": 2,
+    "directArtifactIds": [3, 4, 5]
+  }],
+  "actions": [{
+    "targetId": 1,
+    "actionKey": "x",
+    "mnemonic": "Middleman",
+    "arguments": ["touch", "foo"],
+    "inputDepSetIds": [1],
+    "outputIds": [3],
+    "primaryOutputId": 3
+  }, {
+    "targetId": 2,
+    "actionKey": "y",
+    "mnemonic": "Main action",
+    "arguments": ["touch", "foo"],
+    "inputDepSetIds": [2],
+    "outputIds": [6],
+    "primaryOutputId": 6
+  }]
+}`
+
+	actual, err := AqueryBuildStatements([]byte(inputString))
+	if err != nil {
+		t.Errorf("Unexpected error %q", err)
+	}
+	if expected := 1; len(actual) != expected {
+		t.Fatalf("Expected %d build statements, got %d", expected, len(actual))
+	}
+
+	bs := actual[0]
+	expectedInputs := []string{"middleinput_one", "middleinput_two", "maininput_one", "maininput_two"}
+	if !reflect.DeepEqual(bs.InputPaths, expectedInputs) {
+		t.Errorf("Expected main action inputs %q, but got %q", expectedInputs, bs.InputPaths)
+	}
+
+	expectedOutputs := []string{"output"}
+	if !reflect.DeepEqual(bs.OutputPaths, expectedOutputs) {
+		t.Errorf("Expected main action outputs %q, but got %q", expectedOutputs, bs.OutputPaths)
+	}
+}
+
+func TestSimpleSymlink(t *testing.T) {
+	const inputString = `
+{
+  "artifacts": [{
+    "id": 1,
+    "pathFragmentId": 3
+  }, {
+    "id": 2,
+    "pathFragmentId": 5
+  }],
+  "actions": [{
+    "targetId": 1,
+    "actionKey": "x",
+    "mnemonic": "Symlink",
+    "inputDepSetIds": [1],
+    "outputIds": [2],
+    "primaryOutputId": 2
+  }],
+  "depSetOfFiles": [{
+    "id": 1,
+    "directArtifactIds": [1]
+  }],
+  "pathFragments": [{
+    "id": 1,
+    "label": "one"
+  }, {
+    "id": 2,
+    "label": "file_subdir",
+    "parentId": 1
+  }, {
+    "id": 3,
+    "label": "file",
+    "parentId": 2
+  }, {
+    "id": 4,
+    "label": "symlink_subdir",
+    "parentId": 1
+  }, {
+    "id": 5,
+    "label": "symlink",
+    "parentId": 4
+  }]
+}`
+
+	actual, err := AqueryBuildStatements([]byte(inputString))
+
+	if err != nil {
+		t.Errorf("Unexpected error %q", err)
+	}
+
+	expectedBuildStatements := []BuildStatement{
+		BuildStatement{
+			Command: "mkdir -p one/symlink_subdir && " +
+				"rm -f one/symlink_subdir/symlink && " +
+				"ln -rsf one/file_subdir/file one/symlink_subdir/symlink",
+			InputPaths:   []string{"one/file_subdir/file"},
+			OutputPaths:  []string{"one/symlink_subdir/symlink"},
+			SymlinkPaths: []string{"one/symlink_subdir/symlink"},
+			Mnemonic:     "Symlink",
+		},
+	}
+	assertBuildStatements(t, actual, expectedBuildStatements)
+}
+
+func TestSymlinkQuotesPaths(t *testing.T) {
+	const inputString = `
+{
+  "artifacts": [{
+    "id": 1,
+    "pathFragmentId": 3
+  }, {
+    "id": 2,
+    "pathFragmentId": 5
+  }],
+  "actions": [{
+    "targetId": 1,
+    "actionKey": "x",
+    "mnemonic": "SolibSymlink",
+    "inputDepSetIds": [1],
+    "outputIds": [2],
+    "primaryOutputId": 2
+  }],
+  "depSetOfFiles": [{
+    "id": 1,
+    "directArtifactIds": [1]
+  }],
+  "pathFragments": [{
+    "id": 1,
+    "label": "one"
+  }, {
+    "id": 2,
+    "label": "file subdir",
+    "parentId": 1
+  }, {
+    "id": 3,
+    "label": "file",
+    "parentId": 2
+  }, {
+    "id": 4,
+    "label": "symlink subdir",
+    "parentId": 1
+  }, {
+    "id": 5,
+    "label": "symlink",
+    "parentId": 4
+  }]
+}`
+
+	actual, err := AqueryBuildStatements([]byte(inputString))
+
+	if err != nil {
+		t.Errorf("Unexpected error %q", err)
+	}
+
+	expectedBuildStatements := []BuildStatement{
+		BuildStatement{
+			Command: "mkdir -p 'one/symlink subdir' && " +
+				"rm -f 'one/symlink subdir/symlink' && " +
+				"ln -rsf 'one/file subdir/file' 'one/symlink subdir/symlink'",
+			InputPaths:   []string{"one/file subdir/file"},
+			OutputPaths:  []string{"one/symlink subdir/symlink"},
+			SymlinkPaths: []string{"one/symlink subdir/symlink"},
+			Mnemonic:     "SolibSymlink",
+		},
+	}
+	assertBuildStatements(t, actual, expectedBuildStatements)
+}
+
+func TestSymlinkMultipleInputs(t *testing.T) {
+	const inputString = `
+{
+  "artifacts": [{
+    "id": 1,
+    "pathFragmentId": 1
+  }, {
+    "id": 2,
+    "pathFragmentId": 2
+  }, {
+    "id": 3,
+    "pathFragmentId": 3
+  }],
+  "actions": [{
+    "targetId": 1,
+    "actionKey": "x",
+    "mnemonic": "Symlink",
+    "inputDepSetIds": [1],
+    "outputIds": [3],
+    "primaryOutputId": 3
+  }],
+  "depSetOfFiles": [{
+    "id": 1,
+    "directArtifactIds": [1,2]
+  }],
+  "pathFragments": [{
+    "id": 1,
+    "label": "file"
+  }, {
+    "id": 2,
+    "label": "other_file"
+  }, {
+    "id": 3,
+    "label": "symlink"
+  }]
+}`
+
+	_, err := AqueryBuildStatements([]byte(inputString))
+	assertError(t, err, `Expect 1 input and 1 output to symlink action, got: input ["file" "other_file"], output ["symlink"]`)
+}
+
+func TestSymlinkMultipleOutputs(t *testing.T) {
+	const inputString = `
+{
+  "artifacts": [{
+    "id": 1,
+    "pathFragmentId": 1
+  }, {
+    "id": 2,
+    "pathFragmentId": 2
+  }, {
+    "id": 3,
+    "pathFragmentId": 3
+  }],
+  "actions": [{
+    "targetId": 1,
+    "actionKey": "x",
+    "mnemonic": "Symlink",
+    "inputDepSetIds": [1],
+    "outputIds": [2,3],
+    "primaryOutputId": 2
+  }],
+  "depSetOfFiles": [{
+    "id": 1,
+    "directArtifactIds": [1]
+  }],
+  "pathFragments": [{
+    "id": 1,
+    "label": "file"
+  }, {
+    "id": 2,
+    "label": "symlink"
+  }, {
+    "id": 3,
+    "label": "other_symlink"
+  }]
+}`
+
+	_, err := AqueryBuildStatements([]byte(inputString))
+	assertError(t, err, `Expect 1 input and 1 output to symlink action, got: input ["file"], output ["symlink" "other_symlink"]`)
+}
+
 func assertError(t *testing.T, err error, expected string) {
+	t.Helper()
 	if err == nil {
 		t.Errorf("expected error '%s', but got no error", expected)
 	} else if err.Error() != expected {
-		t.Errorf("expected error '%s', but got: %s", expected, err.Error())
+		t.Errorf("expected error:\n\t'%s', but got:\n\t'%s'", expected, err.Error())
 	}
 }
 
 // Asserts that the given actual build statements match the given expected build statements.
 // Build statement equivalence is determined using buildStatementEquals.
 func assertBuildStatements(t *testing.T, expected []BuildStatement, actual []BuildStatement) {
+	t.Helper()
 	if len(expected) != len(actual) {
 		t.Errorf("expected %d build statements, but got %d,\n expected: %v,\n actual: %v",
 			len(expected), len(actual), expected, actual)
@@ -765,6 +1064,12 @@
 	if !reflect.DeepEqual(stringSet(first.OutputPaths), stringSet(second.OutputPaths)) {
 		return false
 	}
+	if !reflect.DeepEqual(stringSet(first.SymlinkPaths), stringSet(second.SymlinkPaths)) {
+		return false
+	}
+	if first.Depfile != second.Depfile {
+		return false
+	}
 	return true
 }
 
diff --git a/bazel/configurability.go b/bazel/configurability.go
new file mode 100644
index 0000000..7aaff9a
--- /dev/null
+++ b/bazel/configurability.go
@@ -0,0 +1,228 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package bazel
+
+import (
+	"fmt"
+	"strings"
+)
+
+const (
+	// ArchType names in arch.go
+	archArm    = "arm"
+	archArm64  = "arm64"
+	archX86    = "x86"
+	archX86_64 = "x86_64"
+
+	// OsType names in arch.go
+	osAndroid     = "android"
+	osDarwin      = "darwin"
+	osLinux       = "linux_glibc"
+	osLinuxMusl   = "linux_musl"
+	osLinuxBionic = "linux_bionic"
+	osWindows     = "windows"
+
+	// Targets in arch.go
+	osArchAndroidArm        = "android_arm"
+	osArchAndroidArm64      = "android_arm64"
+	osArchAndroidX86        = "android_x86"
+	osArchAndroidX86_64     = "android_x86_64"
+	osArchDarwinX86_64      = "darwin_x86_64"
+	osArchLinuxX86          = "linux_glibc_x86"
+	osArchLinuxX86_64       = "linux_glibc_x86_64"
+	osArchLinuxMuslX86      = "linux_musl_x86"
+	osArchLinuxMuslX86_64   = "linux_musl_x86_64"
+	osArchLinuxBionicArm64  = "linux_bionic_arm64"
+	osArchLinuxBionicX86_64 = "linux_bionic_x86_64"
+	osArchWindowsX86        = "windows_x86"
+	osArchWindowsX86_64     = "windows_x86_64"
+
+	// This is the string representation of the default condition wherever a
+	// configurable attribute is used in a select statement, i.e.
+	// //conditions:default for Bazel.
+	//
+	// This is consistently named "conditions_default" to mirror the Soong
+	// config variable default key in an Android.bp file, although there's no
+	// integration with Soong config variables (yet).
+	ConditionsDefaultConfigKey = "conditions_default"
+
+	ConditionsDefaultSelectKey = "//conditions:default"
+
+	productVariableBazelPackage = "//build/bazel/product_variables"
+)
+
+var (
+	// These are the list of OSes and architectures with a Bazel config_setting
+	// and constraint value equivalent. These exist in arch.go, but the android
+	// package depends on the bazel package, so a cyclic dependency prevents
+	// using those variables here.
+
+	// A map of architectures to the Bazel label of the constraint_value
+	// for the @platforms//cpu:cpu constraint_setting
+	platformArchMap = map[string]string{
+		archArm:                    "//build/bazel/platforms/arch:arm",
+		archArm64:                  "//build/bazel/platforms/arch:arm64",
+		archX86:                    "//build/bazel/platforms/arch:x86",
+		archX86_64:                 "//build/bazel/platforms/arch:x86_64",
+		ConditionsDefaultConfigKey: ConditionsDefaultSelectKey, // The default condition of as arch select map.
+	}
+
+	// A map of target operating systems to the Bazel label of the
+	// constraint_value for the @platforms//os:os constraint_setting
+	platformOsMap = map[string]string{
+		osAndroid:                  "//build/bazel/platforms/os:android",
+		osDarwin:                   "//build/bazel/platforms/os:darwin",
+		osLinux:                    "//build/bazel/platforms/os:linux",
+		osLinuxMusl:                "//build/bazel/platforms/os:linux_musl",
+		osLinuxBionic:              "//build/bazel/platforms/os:linux_bionic",
+		osWindows:                  "//build/bazel/platforms/os:windows",
+		ConditionsDefaultConfigKey: ConditionsDefaultSelectKey, // The default condition of an os select map.
+	}
+
+	platformBionicMap = map[string]string{
+		"bionic":                   "//build/bazel/platforms/os:bionic",
+		ConditionsDefaultConfigKey: ConditionsDefaultSelectKey, // The default condition of an os select map.
+	}
+
+	platformOsArchMap = map[string]string{
+		osArchAndroidArm:           "//build/bazel/platforms/os_arch:android_arm",
+		osArchAndroidArm64:         "//build/bazel/platforms/os_arch:android_arm64",
+		osArchAndroidX86:           "//build/bazel/platforms/os_arch:android_x86",
+		osArchAndroidX86_64:        "//build/bazel/platforms/os_arch:android_x86_64",
+		osArchDarwinX86_64:         "//build/bazel/platforms/os_arch:darwin_x86_64",
+		osArchLinuxX86:             "//build/bazel/platforms/os_arch:linux_glibc_x86",
+		osArchLinuxX86_64:          "//build/bazel/platforms/os_arch:linux_glibc_x86_64",
+		osArchLinuxMuslX86:         "//build/bazel/platforms/os_arch:linux_musl_x86",
+		osArchLinuxMuslX86_64:      "//build/bazel/platforms/os_arch:linux_musl_x86_64",
+		osArchLinuxBionicArm64:     "//build/bazel/platforms/os_arch:linux_bionic_arm64",
+		osArchLinuxBionicX86_64:    "//build/bazel/platforms/os_arch:linux_bionic_x86_64",
+		osArchWindowsX86:           "//build/bazel/platforms/os_arch:windows_x86",
+		osArchWindowsX86_64:        "//build/bazel/platforms/os_arch:windows_x86_64",
+		ConditionsDefaultConfigKey: ConditionsDefaultSelectKey, // The default condition of an os select map.
+	}
+)
+
+// basic configuration types
+type configurationType int
+
+const (
+	noConfig configurationType = iota
+	arch
+	os
+	osArch
+	bionic
+	productVariables
+)
+
+func (ct configurationType) String() string {
+	return map[configurationType]string{
+		noConfig:         "no_config",
+		arch:             "arch",
+		os:               "os",
+		osArch:           "arch_os",
+		bionic:           "bionic",
+		productVariables: "product_variables",
+	}[ct]
+}
+
+func (ct configurationType) validateConfig(config string) {
+	switch ct {
+	case noConfig:
+		if config != "" {
+			panic(fmt.Errorf("Cannot specify config with %s, but got %s", ct, config))
+		}
+	case arch:
+		if _, ok := platformArchMap[config]; !ok {
+			panic(fmt.Errorf("Unknown arch: %s", config))
+		}
+	case os:
+		if _, ok := platformOsMap[config]; !ok {
+			panic(fmt.Errorf("Unknown os: %s", config))
+		}
+	case osArch:
+		if _, ok := platformOsArchMap[config]; !ok {
+			panic(fmt.Errorf("Unknown os+arch: %s", config))
+		}
+	case bionic:
+		if _, ok := platformBionicMap[config]; !ok {
+			panic(fmt.Errorf("Unknown for %s: %s", ct.String(), config))
+		}
+	case productVariables:
+		// do nothing
+	default:
+		panic(fmt.Errorf("Unrecognized ConfigurationType %d", ct))
+	}
+}
+
+// SelectKey returns the Bazel select key for a given configurationType and config string.
+func (ct configurationType) SelectKey(config string) string {
+	ct.validateConfig(config)
+	switch ct {
+	case noConfig:
+		panic(fmt.Errorf("SelectKey is unnecessary for noConfig ConfigurationType "))
+	case arch:
+		return platformArchMap[config]
+	case os:
+		return platformOsMap[config]
+	case osArch:
+		return platformOsArchMap[config]
+	case bionic:
+		return platformBionicMap[config]
+	case productVariables:
+		if config == ConditionsDefaultConfigKey {
+			return ConditionsDefaultSelectKey
+		}
+		return fmt.Sprintf("%s:%s", productVariableBazelPackage, strings.ToLower(config))
+	default:
+		panic(fmt.Errorf("Unrecognized ConfigurationType %d", ct))
+	}
+}
+
+var (
+	// Indicating there is no configuration axis
+	NoConfigAxis = ConfigurationAxis{configurationType: noConfig}
+	// An axis for architecture-specific configurations
+	ArchConfigurationAxis = ConfigurationAxis{configurationType: arch}
+	// An axis for os-specific configurations
+	OsConfigurationAxis = ConfigurationAxis{configurationType: os}
+	// An axis for arch+os-specific configurations
+	OsArchConfigurationAxis = ConfigurationAxis{configurationType: osArch}
+	// An axis for bionic os-specific configurations
+	BionicConfigurationAxis = ConfigurationAxis{configurationType: bionic}
+)
+
+// ProductVariableConfigurationAxis returns an axis for the given product variable
+func ProductVariableConfigurationAxis(variable string) ConfigurationAxis {
+	return ConfigurationAxis{
+		configurationType: productVariables,
+		subType:           variable,
+	}
+}
+
+// ConfigurationAxis is an independent axis for configuration, there should be no overlap between
+// elements within an axis.
+type ConfigurationAxis struct {
+	configurationType
+	// some configuration types (e.g. productVariables) have multiple independent axes, subType helps
+	// distinguish between them without needing to list all 17 product variables.
+	subType string
+}
+
+func (ca *ConfigurationAxis) less(other ConfigurationAxis) bool {
+	if ca.configurationType < other.configurationType {
+		return true
+	}
+	return ca.subType < other.subType
+}
diff --git a/bazel/constants.go b/bazel/constants.go
index 15c75cf..6beb496 100644
--- a/bazel/constants.go
+++ b/bazel/constants.go
@@ -18,6 +18,10 @@
 
 	// Run bazel as a ninja executer
 	BazelNinjaExecRunName = RunName("bazel-ninja-exec")
+
+	SoongInjectionDirName = "soong_injection"
+
+	GeneratedBazelFileWarning = "# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT"
 )
 
 // String returns the name of the run.
diff --git a/bazel/cquery/request_type.go b/bazel/cquery/request_type.go
index c30abeb..131f0ec 100644
--- a/bazel/cquery/request_type.go
+++ b/bazel/cquery/request_type.go
@@ -6,8 +6,9 @@
 )
 
 var (
-	GetOutputFiles = &getOutputFilesRequestType{}
-	GetCcInfo      = &getCcInfoType{}
+	GetOutputFiles  = &getOutputFilesRequestType{}
+	GetPythonBinary = &getPythonBinaryRequestType{}
+	GetCcInfo       = &getCcInfoType{}
 )
 
 type CcInfo struct {
@@ -16,10 +17,20 @@
 	CcStaticLibraryFiles []string
 	Includes             []string
 	SystemIncludes       []string
+	// Archives owned by the current target (not by its dependencies). These will
+	// be a subset of OutputFiles. (or static libraries, this will be equal to OutputFiles,
+	// but general cc_library will also have dynamic libraries in output files).
+	RootStaticArchives []string
+	// Dynamic libraries (.so files) created by the current target. These will
+	// be a subset of OutputFiles. (or shared libraries, this will be equal to OutputFiles,
+	// but general cc_library will also have dynamic libraries in output files).
+	RootDynamicLibraries []string
 }
 
 type getOutputFilesRequestType struct{}
 
+type getPythonBinaryRequestType struct{}
+
 // Name returns a string name for this request type. Such request type names must be unique,
 // and must only consist of alphanumeric characters.
 func (g getOutputFilesRequestType) Name() string {
@@ -45,6 +56,31 @@
 	return splitOrEmpty(rawString, ", ")
 }
 
+// Name returns a string name for this request type. Such request type names must be unique,
+// and must only consist of alphanumeric characters.
+func (g getPythonBinaryRequestType) Name() string {
+	return "getPythonBinary"
+}
+
+// StarlarkFunctionBody returns a starlark function body to process this request type.
+// The returned string is the body of a Starlark function which obtains
+// all request-relevant information about a target and returns a string containing
+// this information.
+// The function should have the following properties:
+//   - `target` is the only parameter to this function (a configured target).
+//   - The return value must be a string.
+//   - The function body should not be indented outside of its own scope.
+func (g getPythonBinaryRequestType) StarlarkFunctionBody() string {
+	return "return providers(target)['FilesToRunProvider'].executable.path"
+}
+
+// ParseResult returns a value obtained by parsing the result of the request's Starlark function.
+// The given rawString must correspond to the string output which was created by evaluating the
+// Starlark given in StarlarkFunctionBody.
+func (g getPythonBinaryRequestType) ParseResult(rawString string) string {
+	return rawString
+}
+
 type getCcInfoType struct{}
 
 // Name returns a string name for this request type. Such request type names must be unique,
@@ -70,6 +106,7 @@
 
 ccObjectFiles = []
 staticLibraries = []
+rootStaticArchives = []
 linker_inputs = providers(target)["CcInfo"].linking_context.linker_inputs.to_list()
 
 for linker_input in linker_inputs:
@@ -78,6 +115,15 @@
       ccObjectFiles += [object.path]
     if library.static_library:
       staticLibraries.append(library.static_library.path)
+      if linker_input.owner == target.label:
+        rootStaticArchives.append(library.static_library.path)
+
+rootDynamicLibraries = []
+
+if "@rules_cc//examples:experimental_cc_shared_library.bzl%CcSharedLibraryInfo" in providers(target):
+  shared_info = providers(target)["@rules_cc//examples:experimental_cc_shared_library.bzl%CcSharedLibraryInfo"]
+  for lib in shared_info.linker_input.libraries:
+    rootDynamicLibraries += [lib.dynamic_library.path]
 
 returns = [
   outputFiles,
@@ -85,6 +131,8 @@
   ccObjectFiles,
   includes,
   system_includes,
+  rootStaticArchives,
+  rootDynamicLibraries
 ]
 
 return "|".join([", ".join(r) for r in returns])`
@@ -98,7 +146,7 @@
 	var ccObjects []string
 
 	splitString := strings.Split(rawString, "|")
-	if expectedLen := 5; len(splitString) != expectedLen {
+	if expectedLen := 7; len(splitString) != expectedLen {
 		return CcInfo{}, fmt.Errorf("Expected %d items, got %q", expectedLen, splitString)
 	}
 	outputFilesString := splitString[0]
@@ -109,12 +157,16 @@
 	ccObjects = splitOrEmpty(ccObjectsString, ", ")
 	includes := splitOrEmpty(splitString[3], ", ")
 	systemIncludes := splitOrEmpty(splitString[4], ", ")
+	rootStaticArchives := splitOrEmpty(splitString[5], ", ")
+	rootDynamicLibraries := splitOrEmpty(splitString[6], ", ")
 	return CcInfo{
 		OutputFiles:          outputFiles,
 		CcObjectFiles:        ccObjects,
 		CcStaticLibraryFiles: ccStaticLibraries,
 		Includes:             includes,
 		SystemIncludes:       systemIncludes,
+		RootStaticArchives:   rootStaticArchives,
+		RootDynamicLibraries: rootDynamicLibraries,
 	}, nil
 }
 
diff --git a/bazel/cquery/request_type_test.go b/bazel/cquery/request_type_test.go
index 6369999..49019ab 100644
--- a/bazel/cquery/request_type_test.go
+++ b/bazel/cquery/request_type_test.go
@@ -37,6 +37,31 @@
 	}
 }
 
+func TestGetPythonBinaryParseResults(t *testing.T) {
+	testCases := []struct {
+		description    string
+		input          string
+		expectedOutput string
+	}{
+		{
+			description:    "no result",
+			input:          "",
+			expectedOutput: "",
+		},
+		{
+			description:    "one result",
+			input:          "test",
+			expectedOutput: "test",
+		},
+	}
+	for _, tc := range testCases {
+		actualOutput := GetPythonBinary.ParseResult(tc.input)
+		if !reflect.DeepEqual(tc.expectedOutput, actualOutput) {
+			t.Errorf("%q: expected %#v != actual %#v", tc.description, tc.expectedOutput, actualOutput)
+		}
+	}
+}
+
 func TestGetCcInfoParseResults(t *testing.T) {
 	testCases := []struct {
 		description          string
@@ -46,48 +71,54 @@
 	}{
 		{
 			description: "no result",
-			input:       "||||",
+			input:       "||||||",
 			expectedOutput: CcInfo{
 				OutputFiles:          []string{},
 				CcObjectFiles:        []string{},
 				CcStaticLibraryFiles: []string{},
 				Includes:             []string{},
 				SystemIncludes:       []string{},
+				RootStaticArchives:   []string{},
+				RootDynamicLibraries: []string{},
 			},
 		},
 		{
 			description: "only output",
-			input:       "test||||",
+			input:       "test||||||",
 			expectedOutput: CcInfo{
 				OutputFiles:          []string{"test"},
 				CcObjectFiles:        []string{},
 				CcStaticLibraryFiles: []string{},
 				Includes:             []string{},
 				SystemIncludes:       []string{},
+				RootStaticArchives:   []string{},
+				RootDynamicLibraries: []string{},
 			},
 		},
 		{
 			description: "all items set",
-			input:       "out1, out2|static_lib1, static_lib2|object1, object2|., dir/subdir|system/dir, system/other/dir",
+			input:       "out1, out2|static_lib1, static_lib2|object1, object2|., dir/subdir|system/dir, system/other/dir|rootstaticarchive1|rootdynamiclibrary1",
 			expectedOutput: CcInfo{
 				OutputFiles:          []string{"out1", "out2"},
 				CcObjectFiles:        []string{"object1", "object2"},
 				CcStaticLibraryFiles: []string{"static_lib1", "static_lib2"},
 				Includes:             []string{".", "dir/subdir"},
 				SystemIncludes:       []string{"system/dir", "system/other/dir"},
+				RootStaticArchives:   []string{"rootstaticarchive1"},
+				RootDynamicLibraries: []string{"rootdynamiclibrary1"},
 			},
 		},
 		{
 			description:          "too few result splits",
 			input:                "|",
 			expectedOutput:       CcInfo{},
-			expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 5, []string{"", ""}),
+			expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 7, []string{"", ""}),
 		},
 		{
 			description:          "too many result splits",
 			input:                strings.Repeat("|", 8),
 			expectedOutput:       CcInfo{},
-			expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 5, make([]string, 9)),
+			expectedErrorMessage: fmt.Sprintf("Expected %d items, got %q", 7, make([]string, 9)),
 		},
 	}
 	for _, tc := range testCases {
diff --git a/bazel/properties.go b/bazel/properties.go
index a71b12b..1a846ba 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -19,6 +19,7 @@
 	"path/filepath"
 	"regexp"
 	"sort"
+	"strings"
 )
 
 // BazelTargetModuleProperties contain properties and metadata used for
@@ -33,6 +34,10 @@
 
 const BazelTargetModuleNamePrefix = "__bp2build__"
 
+func StripNamePrefix(moduleName string) string {
+	return strings.TrimPrefix(moduleName, BazelTargetModuleNamePrefix)
+}
+
 var productVariableSubstitutionPattern = regexp.MustCompile("%(d|s)")
 
 // Label is used to represent a Bazel compatible Label. Also stores the original
@@ -64,6 +69,34 @@
 	Excludes []Label
 }
 
+func (ll *LabelList) Equals(other LabelList) bool {
+	if len(ll.Includes) != len(other.Includes) || len(ll.Excludes) != len(other.Excludes) {
+		return false
+	}
+	for i, _ := range ll.Includes {
+		if ll.Includes[i] != other.Includes[i] {
+			return false
+		}
+	}
+	for i, _ := range ll.Excludes {
+		if ll.Excludes[i] != other.Excludes[i] {
+			return false
+		}
+	}
+	return true
+}
+
+func (ll *LabelList) IsNil() bool {
+	return ll.Includes == nil && ll.Excludes == nil
+}
+
+func (ll *LabelList) deepCopy() LabelList {
+	return LabelList{
+		Includes: ll.Includes[:],
+		Excludes: ll.Excludes[:],
+	}
+}
+
 // uniqueParentDirectories returns a list of the unique parent directories for
 // all files in ll.Includes.
 func (ll *LabelList) uniqueParentDirectories() []string {
@@ -105,7 +138,27 @@
 	return uniqueLabels
 }
 
-func UniqueBazelLabelList(originalLabelList LabelList) LabelList {
+func FirstUniqueBazelLabels(originalLabels []Label) []Label {
+	var labels []Label
+	found := make(map[Label]bool, len(originalLabels))
+	for _, l := range originalLabels {
+		if _, ok := found[l]; ok {
+			continue
+		}
+		labels = append(labels, l)
+		found[l] = true
+	}
+	return labels
+}
+
+func FirstUniqueBazelLabelList(originalLabelList LabelList) LabelList {
+	var uniqueLabelList LabelList
+	uniqueLabelList.Includes = FirstUniqueBazelLabels(originalLabelList.Includes)
+	uniqueLabelList.Excludes = FirstUniqueBazelLabels(originalLabelList.Excludes)
+	return uniqueLabelList
+}
+
+func UniqueSortedBazelLabelList(originalLabelList LabelList) LabelList {
 	var uniqueLabelList LabelList
 	uniqueLabelList.Includes = UniqueSortedBazelLabels(originalLabelList.Includes)
 	uniqueLabelList.Excludes = UniqueSortedBazelLabels(originalLabelList.Excludes)
@@ -136,6 +189,76 @@
 	return strings
 }
 
+// Map a function over all labels in a LabelList.
+func MapLabelList(mapOver LabelList, mapFn func(string) string) LabelList {
+	var includes []Label
+	for _, inc := range mapOver.Includes {
+		mappedLabel := Label{Label: mapFn(inc.Label), OriginalModuleName: inc.OriginalModuleName}
+		includes = append(includes, mappedLabel)
+	}
+	// mapFn is not applied over excludes, but they are propagated as-is.
+	return LabelList{Includes: includes, Excludes: mapOver.Excludes}
+}
+
+// Map a function over all Labels in a LabelListAttribute
+func MapLabelListAttribute(mapOver LabelListAttribute, mapFn func(string) string) LabelListAttribute {
+	var result LabelListAttribute
+
+	result.Value = MapLabelList(mapOver.Value, mapFn)
+
+	for axis, configToLabels := range mapOver.ConfigurableValues {
+		for config, value := range configToLabels {
+			result.SetSelectValue(axis, config, MapLabelList(value, mapFn))
+		}
+	}
+
+	return result
+}
+
+// Return all needles in a given haystack, where needleFn is true for needles.
+func FilterLabelList(haystack LabelList, needleFn func(string) bool) LabelList {
+	var includes []Label
+	for _, inc := range haystack.Includes {
+		if needleFn(inc.Label) {
+			includes = append(includes, inc)
+		}
+	}
+	// needleFn is not applied over excludes, but they are propagated as-is.
+	return LabelList{Includes: includes, Excludes: haystack.Excludes}
+}
+
+// Return all needles in a given haystack, where needleFn is true for needles.
+func FilterLabelListAttribute(haystack LabelListAttribute, needleFn func(string) bool) LabelListAttribute {
+	result := MakeLabelListAttribute(FilterLabelList(haystack.Value, needleFn))
+
+	for config, selects := range haystack.ConfigurableValues {
+		newSelects := make(labelListSelectValues, len(selects))
+		for k, v := range selects {
+			newSelects[k] = FilterLabelList(v, needleFn)
+		}
+		result.ConfigurableValues[config] = newSelects
+	}
+
+	return result
+}
+
+// Subtract needle from haystack
+func SubtractBazelLabelListAttribute(haystack LabelListAttribute, needle LabelListAttribute) LabelListAttribute {
+	result := MakeLabelListAttribute(SubtractBazelLabelList(haystack.Value, needle.Value))
+
+	for config, selects := range haystack.ConfigurableValues {
+		newSelects := make(labelListSelectValues, len(selects))
+		needleSelects := needle.ConfigurableValues[config]
+
+		for k, v := range selects {
+			newSelects[k] = SubtractBazelLabelList(v, needleSelects[k])
+		}
+		result.ConfigurableValues[config] = newSelects
+	}
+
+	return result
+}
+
 // Subtract needle from haystack
 func SubtractBazelLabels(haystack []Label, needle []Label) []Label {
 	// This is really a set
@@ -160,6 +283,14 @@
 	return labels
 }
 
+// Appends two LabelLists, returning the combined list.
+func AppendBazelLabelLists(a LabelList, b LabelList) LabelList {
+	var result LabelList
+	result.Includes = append(a.Includes, b.Includes...)
+	result.Excludes = append(a.Excludes, b.Excludes...)
+	return result
+}
+
 // Subtract needle from haystack
 func SubtractBazelLabelList(haystack LabelList, needle LabelList) LabelList {
 	var result LabelList
@@ -169,247 +300,335 @@
 	return result
 }
 
-const (
-	// ArchType names in arch.go
-	ARCH_ARM    = "arm"
-	ARCH_ARM64  = "arm64"
-	ARCH_X86    = "x86"
-	ARCH_X86_64 = "x86_64"
-
-	// OsType names in arch.go
-	OS_ANDROID      = "android"
-	OS_DARWIN       = "darwin"
-	OS_FUCHSIA      = "fuchsia"
-	OS_LINUX        = "linux_glibc"
-	OS_LINUX_BIONIC = "linux_bionic"
-	OS_WINDOWS      = "windows"
-
-	// This is the string representation of the default condition wherever a
-	// configurable attribute is used in a select statement, i.e.
-	// //conditions:default for Bazel.
-	//
-	// This is consistently named "conditions_default" to mirror the Soong
-	// config variable default key in an Android.bp file, although there's no
-	// integration with Soong config variables (yet).
-	CONDITIONS_DEFAULT = "conditions_default"
-)
-
-var (
-	// These are the list of OSes and architectures with a Bazel config_setting
-	// and constraint value equivalent. These exist in arch.go, but the android
-	// package depends on the bazel package, so a cyclic dependency prevents
-	// using those variables here.
-
-	// A map of architectures to the Bazel label of the constraint_value
-	// for the @platforms//cpu:cpu constraint_setting
-	PlatformArchMap = map[string]string{
-		ARCH_ARM:           "//build/bazel/platforms/arch:arm",
-		ARCH_ARM64:         "//build/bazel/platforms/arch:arm64",
-		ARCH_X86:           "//build/bazel/platforms/arch:x86",
-		ARCH_X86_64:        "//build/bazel/platforms/arch:x86_64",
-		CONDITIONS_DEFAULT: "//conditions:default", // The default condition of as arch select map.
-	}
-
-	// A map of target operating systems to the Bazel label of the
-	// constraint_value for the @platforms//os:os constraint_setting
-	PlatformOsMap = map[string]string{
-		OS_ANDROID:         "//build/bazel/platforms/os:android",
-		OS_DARWIN:          "//build/bazel/platforms/os:darwin",
-		OS_FUCHSIA:         "//build/bazel/platforms/os:fuchsia",
-		OS_LINUX:           "//build/bazel/platforms/os:linux",
-		OS_LINUX_BIONIC:    "//build/bazel/platforms/os:linux_bionic",
-		OS_WINDOWS:         "//build/bazel/platforms/os:windows",
-		CONDITIONS_DEFAULT: "//conditions:default", // The default condition of an os select map.
-	}
-)
-
 type Attribute interface {
 	HasConfigurableValues() bool
 }
 
+type labelSelectValues map[string]*Label
+
+type configurableLabels map[ConfigurationAxis]labelSelectValues
+
+func (cl configurableLabels) setValueForAxis(axis ConfigurationAxis, config string, value *Label) {
+	if cl[axis] == nil {
+		cl[axis] = make(labelSelectValues)
+	}
+	cl[axis][config] = value
+}
+
 // Represents an attribute whose value is a single label
 type LabelAttribute struct {
-	Value  Label
-	X86    Label
-	X86_64 Label
-	Arm    Label
-	Arm64  Label
+	Value *Label
+
+	ConfigurableValues configurableLabels
 }
 
-func (attr *LabelAttribute) GetValueForArch(arch string) Label {
-	switch arch {
-	case ARCH_ARM:
-		return attr.Arm
-	case ARCH_ARM64:
-		return attr.Arm64
-	case ARCH_X86:
-		return attr.X86
-	case ARCH_X86_64:
-		return attr.X86_64
-	case CONDITIONS_DEFAULT:
-		return attr.Value
-	default:
-		panic("Invalid arch type")
-	}
+// HasConfigurableValues returns whether there are configurable values set for this label.
+func (la LabelAttribute) HasConfigurableValues() bool {
+	return len(la.ConfigurableValues) > 0
 }
 
-func (attr *LabelAttribute) SetValueForArch(arch string, value Label) {
-	switch arch {
-	case ARCH_ARM:
-		attr.Arm = value
-	case ARCH_ARM64:
-		attr.Arm64 = value
-	case ARCH_X86:
-		attr.X86 = value
-	case ARCH_X86_64:
-		attr.X86_64 = value
-	default:
-		panic("Invalid arch type")
-	}
+// SetValue sets the base, non-configured value for the Label
+func (la *LabelAttribute) SetValue(value Label) {
+	la.SetSelectValue(NoConfigAxis, "", value)
 }
 
-func (attr LabelAttribute) HasConfigurableValues() bool {
-	return attr.Arm.Label != "" || attr.Arm64.Label != "" || attr.X86.Label != "" || attr.X86_64.Label != ""
-}
-
-// Arch-specific label_list typed Bazel attribute values. This should correspond
-// to the types of architectures supported for compilation in arch.go.
-type labelListArchValues struct {
-	X86    LabelList
-	X86_64 LabelList
-	Arm    LabelList
-	Arm64  LabelList
-	Common LabelList
-
-	ConditionsDefault LabelList
-}
-
-type labelListOsValues struct {
-	Android     LabelList
-	Darwin      LabelList
-	Fuchsia     LabelList
-	Linux       LabelList
-	LinuxBionic LabelList
-	Windows     LabelList
-
-	ConditionsDefault LabelList
-}
-
-// LabelListAttribute is used to represent a list of Bazel labels as an
-// attribute.
-type LabelListAttribute struct {
-	// The non-arch specific attribute label list Value. Required.
-	Value LabelList
-
-	// The arch-specific attribute label list values. Optional. If used, these
-	// are generated in a select statement and appended to the non-arch specific
-	// label list Value.
-	ArchValues labelListArchValues
-
-	// The os-specific attribute label list values. Optional. If used, these
-	// are generated in a select statement and appended to the non-os specific
-	// label list Value.
-	OsValues labelListOsValues
-}
-
-// MakeLabelListAttribute initializes a LabelListAttribute with the non-arch specific value.
-func MakeLabelListAttribute(value LabelList) LabelListAttribute {
-	return LabelListAttribute{Value: UniqueBazelLabelList(value)}
-}
-
-// Append all values, including os and arch specific ones, from another
-// LabelListAttribute to this LabelListAttribute.
-func (attrs *LabelListAttribute) Append(other LabelListAttribute) {
-	for arch := range PlatformArchMap {
-		this := attrs.GetValueForArch(arch)
-		that := other.GetValueForArch(arch)
-		this.Append(that)
-		attrs.SetValueForArch(arch, this)
-	}
-
-	for os := range PlatformOsMap {
-		this := attrs.GetValueForOS(os)
-		that := other.GetValueForOS(os)
-		this.Append(that)
-		attrs.SetValueForOS(os, this)
-	}
-
-	attrs.Value.Append(other.Value)
-}
-
-// HasArchSpecificValues returns true if the attribute contains
-// architecture-specific label_list values.
-func (attrs LabelListAttribute) HasConfigurableValues() bool {
-	for arch := range PlatformArchMap {
-		if len(attrs.GetValueForArch(arch).Includes) > 0 {
-			return true
+// SetSelectValue set a value for a bazel select for the given axis, config and value.
+func (la *LabelAttribute) SetSelectValue(axis ConfigurationAxis, config string, value Label) {
+	axis.validateConfig(config)
+	switch axis.configurationType {
+	case noConfig:
+		la.Value = &value
+	case arch, os, osArch, bionic, productVariables:
+		if la.ConfigurableValues == nil {
+			la.ConfigurableValues = make(configurableLabels)
 		}
+		la.ConfigurableValues.setValueForAxis(axis, config, &value)
+	default:
+		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
+	}
+}
+
+// SelectValue gets a value for a bazel select for the given axis and config.
+func (la *LabelAttribute) SelectValue(axis ConfigurationAxis, config string) Label {
+	axis.validateConfig(config)
+	switch axis.configurationType {
+	case noConfig:
+		return *la.Value
+	case arch, os, osArch, bionic, productVariables:
+		return *la.ConfigurableValues[axis][config]
+	default:
+		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
+	}
+}
+
+// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
+func (la *LabelAttribute) SortedConfigurationAxes() []ConfigurationAxis {
+	keys := make([]ConfigurationAxis, 0, len(la.ConfigurableValues))
+	for k := range la.ConfigurableValues {
+		keys = append(keys, k)
 	}
 
-	for os := range PlatformOsMap {
-		if len(attrs.GetValueForOS(os).Includes) > 0 {
+	sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
+	return keys
+}
+
+type configToBools map[string]bool
+
+func (ctb configToBools) setValue(config string, value *bool) {
+	if value == nil {
+		if _, ok := ctb[config]; ok {
+			delete(ctb, config)
+		}
+		return
+	}
+	ctb[config] = *value
+}
+
+type configurableBools map[ConfigurationAxis]configToBools
+
+func (cb configurableBools) setValueForAxis(axis ConfigurationAxis, config string, value *bool) {
+	if cb[axis] == nil {
+		cb[axis] = make(configToBools)
+	}
+	cb[axis].setValue(config, value)
+}
+
+// BoolAttribute represents an attribute whose value is a single bool but may be configurable..
+type BoolAttribute struct {
+	Value *bool
+
+	ConfigurableValues configurableBools
+}
+
+// HasConfigurableValues returns whether there are configurable values for this attribute.
+func (ba BoolAttribute) HasConfigurableValues() bool {
+	return len(ba.ConfigurableValues) > 0
+}
+
+// SetSelectValue sets value for the given axis/config.
+func (ba *BoolAttribute) SetSelectValue(axis ConfigurationAxis, config string, value *bool) {
+	axis.validateConfig(config)
+	switch axis.configurationType {
+	case noConfig:
+		ba.Value = value
+	case arch, os, osArch, bionic, productVariables:
+		if ba.ConfigurableValues == nil {
+			ba.ConfigurableValues = make(configurableBools)
+		}
+		ba.ConfigurableValues.setValueForAxis(axis, config, value)
+	default:
+		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
+	}
+}
+
+// SelectValue gets the value for the given axis/config.
+func (ba BoolAttribute) SelectValue(axis ConfigurationAxis, config string) *bool {
+	axis.validateConfig(config)
+	switch axis.configurationType {
+	case noConfig:
+		return ba.Value
+	case arch, os, osArch, bionic, productVariables:
+		if v, ok := ba.ConfigurableValues[axis][config]; ok {
+			return &v
+		} else {
+			return nil
+		}
+	default:
+		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
+	}
+}
+
+// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
+func (ba *BoolAttribute) SortedConfigurationAxes() []ConfigurationAxis {
+	keys := make([]ConfigurationAxis, 0, len(ba.ConfigurableValues))
+	for k := range ba.ConfigurableValues {
+		keys = append(keys, k)
+	}
+
+	sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
+	return keys
+}
+
+// labelListSelectValues supports config-specific label_list typed Bazel attribute values.
+type labelListSelectValues map[string]LabelList
+
+func (ll labelListSelectValues) appendSelects(other labelListSelectValues) {
+	for k, v := range other {
+		l := ll[k]
+		(&l).Append(v)
+		ll[k] = l
+	}
+}
+
+// HasConfigurableValues returns whether there are configurable values within this set of selects.
+func (ll labelListSelectValues) HasConfigurableValues() bool {
+	for _, v := range ll {
+		if v.Includes != nil {
 			return true
 		}
 	}
 	return false
 }
 
-func (attrs *LabelListAttribute) archValuePtrs() map[string]*LabelList {
-	return map[string]*LabelList{
-		ARCH_X86:           &attrs.ArchValues.X86,
-		ARCH_X86_64:        &attrs.ArchValues.X86_64,
-		ARCH_ARM:           &attrs.ArchValues.Arm,
-		ARCH_ARM64:         &attrs.ArchValues.Arm64,
-		CONDITIONS_DEFAULT: &attrs.ArchValues.ConditionsDefault,
+// LabelListAttribute is used to represent a list of Bazel labels as an
+// attribute.
+type LabelListAttribute struct {
+	// The non-configured attribute label list Value. Required.
+	Value LabelList
+
+	// The configured attribute label list Values. Optional
+	// a map of independent configurability axes
+	ConfigurableValues configurableLabelLists
+
+	// If true, differentiate between "nil" and "empty" list. nil means that
+	// this attribute should not be specified at all, and "empty" means that
+	// the attribute should be explicitly specified as an empty list.
+	// This mode facilitates use of attribute defaults: an empty list should
+	// override the default.
+	ForceSpecifyEmptyList bool
+}
+
+type configurableLabelLists map[ConfigurationAxis]labelListSelectValues
+
+func (cll configurableLabelLists) setValueForAxis(axis ConfigurationAxis, config string, list LabelList) {
+	if list.IsNil() {
+		if _, ok := cll[axis][config]; ok {
+			delete(cll[axis], config)
+		}
+		return
+	}
+	if cll[axis] == nil {
+		cll[axis] = make(labelListSelectValues)
+	}
+
+	cll[axis][config] = list
+}
+
+func (cll configurableLabelLists) Append(other configurableLabelLists) {
+	for axis, otherSelects := range other {
+		selects := cll[axis]
+		if selects == nil {
+			selects = make(labelListSelectValues, len(otherSelects))
+		}
+		selects.appendSelects(otherSelects)
+		cll[axis] = selects
 	}
 }
 
-// GetValueForArch returns the label_list attribute value for an architecture.
-func (attrs *LabelListAttribute) GetValueForArch(arch string) LabelList {
-	var v *LabelList
-	if v = attrs.archValuePtrs()[arch]; v == nil {
-		panic(fmt.Errorf("Unknown arch: %s", arch))
-	}
-	return *v
-}
-
-// SetValueForArch sets the label_list attribute value for an architecture.
-func (attrs *LabelListAttribute) SetValueForArch(arch string, value LabelList) {
-	var v *LabelList
-	if v = attrs.archValuePtrs()[arch]; v == nil {
-		panic(fmt.Errorf("Unknown arch: %s", arch))
-	}
-	*v = value
-}
-
-func (attrs *LabelListAttribute) osValuePtrs() map[string]*LabelList {
-	return map[string]*LabelList{
-		OS_ANDROID:         &attrs.OsValues.Android,
-		OS_DARWIN:          &attrs.OsValues.Darwin,
-		OS_FUCHSIA:         &attrs.OsValues.Fuchsia,
-		OS_LINUX:           &attrs.OsValues.Linux,
-		OS_LINUX_BIONIC:    &attrs.OsValues.LinuxBionic,
-		OS_WINDOWS:         &attrs.OsValues.Windows,
-		CONDITIONS_DEFAULT: &attrs.OsValues.ConditionsDefault,
+// MakeLabelListAttribute initializes a LabelListAttribute with the non-arch specific value.
+func MakeLabelListAttribute(value LabelList) LabelListAttribute {
+	return LabelListAttribute{
+		Value:              value,
+		ConfigurableValues: make(configurableLabelLists),
 	}
 }
 
-// GetValueForOS returns the label_list attribute value for an OS target.
-func (attrs *LabelListAttribute) GetValueForOS(os string) LabelList {
-	var v *LabelList
-	if v = attrs.osValuePtrs()[os]; v == nil {
-		panic(fmt.Errorf("Unknown os: %s", os))
-	}
-	return *v
+func (lla *LabelListAttribute) SetValue(list LabelList) {
+	lla.SetSelectValue(NoConfigAxis, "", list)
 }
 
-// SetValueForArch sets the label_list attribute value for an OS target.
-func (attrs *LabelListAttribute) SetValueForOS(os string, value LabelList) {
-	var v *LabelList
-	if v = attrs.osValuePtrs()[os]; v == nil {
-		panic(fmt.Errorf("Unknown os: %s", os))
+// SetSelectValue set a value for a bazel select for the given axis, config and value.
+func (lla *LabelListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list LabelList) {
+	axis.validateConfig(config)
+	switch axis.configurationType {
+	case noConfig:
+		lla.Value = list
+	case arch, os, osArch, bionic, productVariables:
+		if lla.ConfigurableValues == nil {
+			lla.ConfigurableValues = make(configurableLabelLists)
+		}
+		lla.ConfigurableValues.setValueForAxis(axis, config, list)
+	default:
+		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
 	}
-	*v = value
+}
+
+// SelectValue gets a value for a bazel select for the given axis and config.
+func (lla *LabelListAttribute) SelectValue(axis ConfigurationAxis, config string) LabelList {
+	axis.validateConfig(config)
+	switch axis.configurationType {
+	case noConfig:
+		return lla.Value
+	case arch, os, osArch, bionic, productVariables:
+		return lla.ConfigurableValues[axis][config]
+	default:
+		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
+	}
+}
+
+// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
+func (lla *LabelListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
+	keys := make([]ConfigurationAxis, 0, len(lla.ConfigurableValues))
+	for k := range lla.ConfigurableValues {
+		keys = append(keys, k)
+	}
+
+	sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
+	return keys
+}
+
+// Append all values, including os and arch specific ones, from another
+// LabelListAttribute to this LabelListAttribute.
+func (lla *LabelListAttribute) Append(other LabelListAttribute) {
+	if lla.ForceSpecifyEmptyList && !other.Value.IsNil() {
+		lla.Value.Includes = []Label{}
+	}
+	lla.Value.Append(other.Value)
+	if lla.ConfigurableValues == nil {
+		lla.ConfigurableValues = make(configurableLabelLists)
+	}
+	lla.ConfigurableValues.Append(other.ConfigurableValues)
+}
+
+// HasConfigurableValues returns true if the attribute contains axis-specific label list values.
+func (lla LabelListAttribute) HasConfigurableValues() bool {
+	return len(lla.ConfigurableValues) > 0
+}
+
+// IsEmpty returns true if the attribute has no values under any configuration.
+func (lla LabelListAttribute) IsEmpty() bool {
+	if len(lla.Value.Includes) > 0 {
+		return false
+	}
+	for axis, _ := range lla.ConfigurableValues {
+		if lla.ConfigurableValues[axis].HasConfigurableValues() {
+			return false
+		}
+	}
+	return true
+}
+
+// ResolveExcludes handles excludes across the various axes, ensuring that items are removed from
+// the base value and included in default values as appropriate.
+func (lla *LabelListAttribute) ResolveExcludes() {
+	for axis, configToLabels := range lla.ConfigurableValues {
+		baseLabels := lla.Value.deepCopy()
+		for config, val := range configToLabels {
+			// Exclude config-specific excludes from base value
+			lla.Value = SubtractBazelLabelList(lla.Value, LabelList{Includes: val.Excludes})
+
+			// add base values to config specific to add labels excluded by others in this axis
+			// then remove all config-specific excludes
+			allLabels := baseLabels.deepCopy()
+			allLabels.Append(val)
+			lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(allLabels, LabelList{Includes: val.Excludes})
+		}
+
+		// After going through all configs, delete the duplicates in the config
+		// values that are already in the base Value.
+		for config, val := range configToLabels {
+			lla.ConfigurableValues[axis][config] = SubtractBazelLabelList(val, lla.Value)
+		}
+
+		// Now that the Value list is finalized for this axis, compare it with the original
+		// list, and put the difference into the default condition for the axis.
+		lla.ConfigurableValues[axis][ConditionsDefaultConfigKey] = SubtractBazelLabelList(baseLabels, lla.Value)
+
+		// if everything ends up without includes, just delete the axis
+		if !lla.ConfigurableValues[axis].HasConfigurableValues() {
+			delete(lla.ConfigurableValues, axis)
+		}
+	}
 }
 
 // StringListAttribute corresponds to the string_list Bazel attribute type with
@@ -418,139 +637,110 @@
 	// The base value of the string list attribute.
 	Value []string
 
-	// The arch-specific attribute string list values. Optional. If used, these
-	// are generated in a select statement and appended to the non-arch specific
-	// label list Value.
-	ArchValues stringListArchValues
-
-	// The os-specific attribute string list values. Optional. If used, these
-	// are generated in a select statement and appended to the non-os specific
-	// label list Value.
-	OsValues stringListOsValues
+	// The configured attribute label list Values. Optional
+	// a map of independent configurability axes
+	ConfigurableValues configurableStringLists
 }
 
-// MakeStringListAttribute initializes a StringListAttribute with the non-arch specific value.
-func MakeStringListAttribute(value []string) StringListAttribute {
-	// NOTE: These strings are not necessarily unique or sorted.
-	return StringListAttribute{Value: value}
-}
+type configurableStringLists map[ConfigurationAxis]stringListSelectValues
 
-// Arch-specific string_list typed Bazel attribute values. This should correspond
-// to the types of architectures supported for compilation in arch.go.
-type stringListArchValues struct {
-	X86    []string
-	X86_64 []string
-	Arm    []string
-	Arm64  []string
-	Common []string
-
-	ConditionsDefault []string
-}
-
-type stringListOsValues struct {
-	Android     []string
-	Darwin      []string
-	Fuchsia     []string
-	Linux       []string
-	LinuxBionic []string
-	Windows     []string
-
-	ConditionsDefault []string
-}
-
-// HasConfigurableValues returns true if the attribute contains
-// architecture-specific string_list values.
-func (attrs StringListAttribute) HasConfigurableValues() bool {
-	for arch := range PlatformArchMap {
-		if len(attrs.GetValueForArch(arch)) > 0 {
-			return true
+func (csl configurableStringLists) Append(other configurableStringLists) {
+	for axis, otherSelects := range other {
+		selects := csl[axis]
+		if selects == nil {
+			selects = make(stringListSelectValues, len(otherSelects))
 		}
+		selects.appendSelects(otherSelects)
+		csl[axis] = selects
 	}
+}
 
-	for os := range PlatformOsMap {
-		if len(attrs.GetValueForOS(os)) > 0 {
+func (csl configurableStringLists) setValueForAxis(axis ConfigurationAxis, config string, list []string) {
+	if csl[axis] == nil {
+		csl[axis] = make(stringListSelectValues)
+	}
+	csl[axis][config] = list
+}
+
+type stringListSelectValues map[string][]string
+
+func (sl stringListSelectValues) appendSelects(other stringListSelectValues) {
+	for k, v := range other {
+		sl[k] = append(sl[k], v...)
+	}
+}
+
+func (sl stringListSelectValues) hasConfigurableValues(other stringListSelectValues) bool {
+	for _, val := range sl {
+		if len(val) > 0 {
 			return true
 		}
 	}
 	return false
 }
 
-func (attrs *StringListAttribute) archValuePtrs() map[string]*[]string {
-	return map[string]*[]string{
-		ARCH_X86:           &attrs.ArchValues.X86,
-		ARCH_X86_64:        &attrs.ArchValues.X86_64,
-		ARCH_ARM:           &attrs.ArchValues.Arm,
-		ARCH_ARM64:         &attrs.ArchValues.Arm64,
-		CONDITIONS_DEFAULT: &attrs.ArchValues.ConditionsDefault,
+// MakeStringListAttribute initializes a StringListAttribute with the non-arch specific value.
+func MakeStringListAttribute(value []string) StringListAttribute {
+	// NOTE: These strings are not necessarily unique or sorted.
+	return StringListAttribute{
+		Value:              value,
+		ConfigurableValues: make(configurableStringLists),
 	}
 }
 
-// GetValueForArch returns the string_list attribute value for an architecture.
-func (attrs *StringListAttribute) GetValueForArch(arch string) []string {
-	var v *[]string
-	if v = attrs.archValuePtrs()[arch]; v == nil {
-		panic(fmt.Errorf("Unknown arch: %s", arch))
-	}
-	return *v
-}
-
-// SetValueForArch sets the string_list attribute value for an architecture.
-func (attrs *StringListAttribute) SetValueForArch(arch string, value []string) {
-	var v *[]string
-	if v = attrs.archValuePtrs()[arch]; v == nil {
-		panic(fmt.Errorf("Unknown arch: %s", arch))
-	}
-	*v = value
-}
-
-func (attrs *StringListAttribute) osValuePtrs() map[string]*[]string {
-	return map[string]*[]string{
-		OS_ANDROID:         &attrs.OsValues.Android,
-		OS_DARWIN:          &attrs.OsValues.Darwin,
-		OS_FUCHSIA:         &attrs.OsValues.Fuchsia,
-		OS_LINUX:           &attrs.OsValues.Linux,
-		OS_LINUX_BIONIC:    &attrs.OsValues.LinuxBionic,
-		OS_WINDOWS:         &attrs.OsValues.Windows,
-		CONDITIONS_DEFAULT: &attrs.OsValues.ConditionsDefault,
-	}
-}
-
-// GetValueForOS returns the string_list attribute value for an OS target.
-func (attrs *StringListAttribute) GetValueForOS(os string) []string {
-	var v *[]string
-	if v = attrs.osValuePtrs()[os]; v == nil {
-		panic(fmt.Errorf("Unknown os: %s", os))
-	}
-	return *v
-}
-
-// SetValueForArch sets the string_list attribute value for an OS target.
-func (attrs *StringListAttribute) SetValueForOS(os string, value []string) {
-	var v *[]string
-	if v = attrs.osValuePtrs()[os]; v == nil {
-		panic(fmt.Errorf("Unknown os: %s", os))
-	}
-	*v = value
+// HasConfigurableValues returns true if the attribute contains axis-specific string_list values.
+func (sla StringListAttribute) HasConfigurableValues() bool {
+	return len(sla.ConfigurableValues) > 0
 }
 
 // Append appends all values, including os and arch specific ones, from another
 // StringListAttribute to this StringListAttribute
-func (attrs *StringListAttribute) Append(other StringListAttribute) {
-	for arch := range PlatformArchMap {
-		this := attrs.GetValueForArch(arch)
-		that := other.GetValueForArch(arch)
-		this = append(this, that...)
-		attrs.SetValueForArch(arch, this)
+func (sla *StringListAttribute) Append(other StringListAttribute) {
+	sla.Value = append(sla.Value, other.Value...)
+	if sla.ConfigurableValues == nil {
+		sla.ConfigurableValues = make(configurableStringLists)
+	}
+	sla.ConfigurableValues.Append(other.ConfigurableValues)
+}
+
+// SetSelectValue set a value for a bazel select for the given axis, config and value.
+func (sla *StringListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list []string) {
+	axis.validateConfig(config)
+	switch axis.configurationType {
+	case noConfig:
+		sla.Value = list
+	case arch, os, osArch, bionic, productVariables:
+		if sla.ConfigurableValues == nil {
+			sla.ConfigurableValues = make(configurableStringLists)
+		}
+		sla.ConfigurableValues.setValueForAxis(axis, config, list)
+	default:
+		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
+	}
+}
+
+// SelectValue gets a value for a bazel select for the given axis and config.
+func (sla *StringListAttribute) SelectValue(axis ConfigurationAxis, config string) []string {
+	axis.validateConfig(config)
+	switch axis.configurationType {
+	case noConfig:
+		return sla.Value
+	case arch, os, osArch, bionic, productVariables:
+		return sla.ConfigurableValues[axis][config]
+	default:
+		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
+	}
+}
+
+// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
+func (sla *StringListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
+	keys := make([]ConfigurationAxis, 0, len(sla.ConfigurableValues))
+	for k := range sla.ConfigurableValues {
+		keys = append(keys, k)
 	}
 
-	for os := range PlatformOsMap {
-		this := attrs.GetValueForOS(os)
-		that := other.GetValueForOS(os)
-		this = append(this, that...)
-		attrs.SetValueForOS(os, this)
-	}
-
-	attrs.Value = append(attrs.Value, other.Value...)
+	sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
+	return keys
 }
 
 // TryVariableSubstitution, replace string substitution formatting within each string in slice with
@@ -569,6 +759,6 @@
 // TryVariableSubstitution, replace string substitution formatting within s with Starlark
 // string.format compatible tag for productVariable.
 func TryVariableSubstitution(s string, productVariable string) (string, bool) {
-	sub := productVariableSubstitutionPattern.ReplaceAllString(s, "{"+productVariable+"}")
+	sub := productVariableSubstitutionPattern.ReplaceAllString(s, "$("+productVariable+")")
 	return sub, s != sub
 }
diff --git a/bazel/properties_test.go b/bazel/properties_test.go
index 229a4aa..9464245 100644
--- a/bazel/properties_test.go
+++ b/bazel/properties_test.go
@@ -122,7 +122,7 @@
 		}
 	}
 }
-func TestUniqueBazelLabelList(t *testing.T) {
+func TestFirstUniqueBazelLabelList(t *testing.T) {
 	testCases := []struct {
 		originalLabelList       LabelList
 		expectedUniqueLabelList LabelList
@@ -157,9 +157,139 @@
 		},
 	}
 	for _, tc := range testCases {
-		actualUniqueLabelList := UniqueBazelLabelList(tc.originalLabelList)
+		actualUniqueLabelList := FirstUniqueBazelLabelList(tc.originalLabelList)
 		if !reflect.DeepEqual(tc.expectedUniqueLabelList, actualUniqueLabelList) {
 			t.Fatalf("Expected %v, got %v", tc.expectedUniqueLabelList, actualUniqueLabelList)
 		}
 	}
 }
+
+func TestUniqueSortedBazelLabelList(t *testing.T) {
+	testCases := []struct {
+		originalLabelList       LabelList
+		expectedUniqueLabelList LabelList
+	}{
+		{
+			originalLabelList: LabelList{
+				Includes: []Label{
+					{Label: "c"},
+					{Label: "a"},
+					{Label: "a"},
+					{Label: "b"},
+				},
+				Excludes: []Label{
+					{Label: "y"},
+					{Label: "z"},
+					{Label: "x"},
+					{Label: "x"},
+				},
+			},
+			expectedUniqueLabelList: LabelList{
+				Includes: []Label{
+					{Label: "a"},
+					{Label: "b"},
+					{Label: "c"},
+				},
+				Excludes: []Label{
+					{Label: "x"},
+					{Label: "y"},
+					{Label: "z"},
+				},
+			},
+		},
+	}
+	for _, tc := range testCases {
+		actualUniqueLabelList := UniqueSortedBazelLabelList(tc.originalLabelList)
+		if !reflect.DeepEqual(tc.expectedUniqueLabelList, actualUniqueLabelList) {
+			t.Fatalf("Expected %v, got %v", tc.expectedUniqueLabelList, actualUniqueLabelList)
+		}
+	}
+}
+
+func makeLabels(labels ...string) []Label {
+	var ret []Label
+	for _, l := range labels {
+		ret = append(ret, Label{Label: l})
+	}
+	return ret
+}
+
+func makeLabelList(includes, excludes []string) LabelList {
+	return LabelList{
+		Includes: makeLabels(includes...),
+		Excludes: makeLabels(excludes...),
+	}
+}
+
+func TestResolveExcludes(t *testing.T) {
+	attr := LabelListAttribute{
+		Value: makeLabelList(
+			[]string{
+				"all_include",
+				"arm_exclude",
+				"android_exclude",
+			},
+			[]string{"all_exclude"},
+		),
+		ConfigurableValues: configurableLabelLists{
+			ArchConfigurationAxis: labelListSelectValues{
+				"arm": makeLabelList([]string{}, []string{"arm_exclude"}),
+				"x86": makeLabelList([]string{"x86_include"}, []string{}),
+			},
+			OsConfigurationAxis: labelListSelectValues{
+				"android": makeLabelList([]string{}, []string{"android_exclude"}),
+				"linux":   makeLabelList([]string{"linux_include"}, []string{}),
+			},
+			OsArchConfigurationAxis: labelListSelectValues{
+				"linux_x86": makeLabelList([]string{"linux_x86_include"}, []string{}),
+			},
+			ProductVariableConfigurationAxis("a"): labelListSelectValues{
+				"a": makeLabelList([]string{}, []string{"not_in_value"}),
+			},
+		},
+	}
+
+	attr.ResolveExcludes()
+
+	expectedBaseIncludes := []Label{Label{Label: "all_include"}}
+	if !reflect.DeepEqual(expectedBaseIncludes, attr.Value.Includes) {
+		t.Errorf("Expected Value includes %q, got %q", attr.Value.Includes, expectedBaseIncludes)
+	}
+	var nilLabels []Label
+	expectedConfiguredIncludes := map[ConfigurationAxis]map[string][]Label{
+		ArchConfigurationAxis: map[string][]Label{
+			"arm":                nilLabels,
+			"x86":                makeLabels("arm_exclude", "x86_include"),
+			"conditions_default": makeLabels("arm_exclude"),
+		},
+		OsConfigurationAxis: map[string][]Label{
+			"android":            nilLabels,
+			"linux":              makeLabels("android_exclude", "linux_include"),
+			"conditions_default": makeLabels("android_exclude"),
+		},
+		OsArchConfigurationAxis: map[string][]Label{
+			"linux_x86":          makeLabels("linux_x86_include"),
+			"conditions_default": nilLabels,
+		},
+	}
+	for _, axis := range attr.SortedConfigurationAxes() {
+		if _, ok := expectedConfiguredIncludes[axis]; !ok {
+			t.Errorf("Found unexpected axis %s", axis)
+			continue
+		}
+		expectedForAxis := expectedConfiguredIncludes[axis]
+		gotForAxis := attr.ConfigurableValues[axis]
+		if len(expectedForAxis) != len(gotForAxis) {
+			t.Errorf("Expected %d configs for %s, got %d: %s", len(expectedForAxis), axis, len(gotForAxis), gotForAxis)
+		}
+		for config, value := range gotForAxis {
+			if expected, ok := expectedForAxis[config]; ok {
+				if !reflect.DeepEqual(expected, value.Includes) {
+					t.Errorf("For %s, expected: %#v, got %#v", axis, expected, value.Includes)
+				}
+			} else {
+				t.Errorf("Got unexpected config %q for %s", config, axis)
+			}
+		}
+	}
+}
diff --git a/bloaty/bloaty_merger.py b/bloaty/bloaty_merger.py
index 1034462..46ce57f 100644
--- a/bloaty/bloaty_merger.py
+++ b/bloaty/bloaty_merger.py
@@ -24,58 +24,63 @@
 import csv
 import gzip
 
+# pylint: disable=import-error
 import ninja_rsp
 
 import file_sections_pb2
 
 BLOATY_EXTENSION = ".bloaty.csv"
 
+
 def parse_csv(path):
-  """Parses a Bloaty-generated CSV file into a protobuf.
+    """Parses a Bloaty-generated CSV file into a protobuf.
 
-  Args:
-    path: The filepath to the CSV file, relative to $ANDROID_TOP.
+    Args:
+      path: The filepath to the CSV file, relative to $ANDROID_TOP.
 
-  Returns:
-    A file_sections_pb2.File if the file was found; None otherwise.
-  """
-  file_proto = None
-  with open(path, newline='') as csv_file:
-    file_proto = file_sections_pb2.File()
-    if path.endswith(BLOATY_EXTENSION):
-      file_proto.path = path[:-len(BLOATY_EXTENSION)]
-    section_reader = csv.DictReader(csv_file)
-    for row in section_reader:
-      section = file_proto.sections.add()
-      section.name = row["sections"]
-      section.vm_size = int(row["vmsize"])
-      section.file_size = int(row["filesize"])
-  return file_proto
+    Returns:
+      A file_sections_pb2.File if the file was found; None otherwise.
+    """
+    file_proto = None
+    with open(path, newline='') as csv_file:
+        file_proto = file_sections_pb2.File()
+        if path.endswith(BLOATY_EXTENSION):
+            file_proto.path = path[: -len(BLOATY_EXTENSION)]
+        section_reader = csv.DictReader(csv_file)
+        for row in section_reader:
+            section = file_proto.sections.add()
+            section.name = row["sections"]
+            section.vm_size = int(row["vmsize"])
+            section.file_size = int(row["filesize"])
+    return file_proto
+
 
 def create_file_size_metrics(input_list, output_proto):
-  """Creates a FileSizeMetrics proto from a list of CSV files.
+    """Creates a FileSizeMetrics proto from a list of CSV files.
 
-  Args:
-    input_list: The path to the file which contains the list of CSV files. Each
-        filepath is separated by a space.
-    output_proto: The path for the output protobuf. It will be compressed using
-        gzip.
-  """
-  metrics = file_sections_pb2.FileSizeMetrics()
-  reader = ninja_rsp.NinjaRspFileReader(input_list)
-  for csv_path in reader:
-    file_proto = parse_csv(csv_path)
-    if file_proto:
-      metrics.files.append(file_proto)
-  with gzip.open(output_proto, "wb") as output:
-    output.write(metrics.SerializeToString())
+    Args:
+      input_list: The path to the file which contains the list of CSV files.
+          Each filepath is separated by a space.
+      output_proto: The path for the output protobuf. It will be compressed
+          using gzip.
+    """
+    metrics = file_sections_pb2.FileSizeMetrics()
+    reader = ninja_rsp.NinjaRspFileReader(input_list)
+    for csv_path in reader:
+        file_proto = parse_csv(csv_path)
+        if file_proto:
+            metrics.files.append(file_proto)
+    with gzip.open(output_proto, "wb") as output:
+        output.write(metrics.SerializeToString())
+
 
 def main():
-  parser = argparse.ArgumentParser()
-  parser.add_argument("input_list_file", help="List of bloaty csv files.")
-  parser.add_argument("output_proto", help="Output proto.")
-  args = parser.parse_args()
-  create_file_size_metrics(args.input_list_file, args.output_proto)
+    parser = argparse.ArgumentParser()
+    parser.add_argument("input_list_file", help="List of bloaty csv files.")
+    parser.add_argument("output_proto", help="Output proto.")
+    args = parser.parse_args()
+    create_file_size_metrics(args.input_list_file, args.output_proto)
+
 
 if __name__ == '__main__':
-  main()
+    main()
diff --git a/bloaty/bloaty_merger_test.py b/bloaty/bloaty_merger_test.py
index 9de049a..83680b9 100644
--- a/bloaty/bloaty_merger_test.py
+++ b/bloaty/bloaty_merger_test.py
@@ -14,6 +14,7 @@
 import gzip
 import unittest
 
+# pylint: disable=import-error
 from pyfakefs import fake_filesystem_unittest
 
 import bloaty_merger
@@ -21,46 +22,46 @@
 
 
 class BloatyMergerTestCase(fake_filesystem_unittest.TestCase):
-  def setUp(self):
-    self.setUpPyfakefs()
+    def setUp(self):
+        self.setUpPyfakefs()
 
-  def test_parse_csv(self):
-    csv_content = "sections,vmsize,filesize\nsection1,2,3\n"
-    self.fs.create_file("file1.bloaty.csv", contents=csv_content)
-    pb = bloaty_merger.parse_csv("file1.bloaty.csv")
-    self.assertEqual(pb.path, "file1")
-    self.assertEqual(len(pb.sections), 1)
-    s = pb.sections[0]
-    self.assertEqual(s.name, "section1")
-    self.assertEqual(s.vm_size, 2)
-    self.assertEqual(s.file_size, 3)
+    def test_parse_csv(self):
+        csv_content = "sections,vmsize,filesize\nsection1,2,3\n"
+        self.fs.create_file("file1.bloaty.csv", contents=csv_content)
+        pb = bloaty_merger.parse_csv("file1.bloaty.csv")
+        self.assertEqual(pb.path, "file1")
+        self.assertEqual(len(pb.sections), 1)
+        s = pb.sections[0]
+        self.assertEqual(s.name, "section1")
+        self.assertEqual(s.vm_size, 2)
+        self.assertEqual(s.file_size, 3)
 
-  def test_missing_file(self):
-    with self.assertRaises(FileNotFoundError):
-      bloaty_merger.parse_csv("missing.bloaty.csv")
+    def test_missing_file(self):
+        with self.assertRaises(FileNotFoundError):
+            bloaty_merger.parse_csv("missing.bloaty.csv")
 
-  def test_malformed_csv(self):
-    csv_content = "header1,heaVder2,header3\n4,5,6\n"
-    self.fs.create_file("file1.bloaty.csv", contents=csv_content)
-    with self.assertRaises(KeyError):
-      bloaty_merger.parse_csv("file1.bloaty.csv")
+    def test_malformed_csv(self):
+        csv_content = "header1,heaVder2,header3\n4,5,6\n"
+        self.fs.create_file("file1.bloaty.csv", contents=csv_content)
+        with self.assertRaises(KeyError):
+            bloaty_merger.parse_csv("file1.bloaty.csv")
 
-  def test_create_file_metrics(self):
-    file_list = "file1.bloaty.csv file2.bloaty.csv"
-    file1_content = "sections,vmsize,filesize\nsection1,2,3\nsection2,7,8"
-    file2_content = "sections,vmsize,filesize\nsection1,4,5\n"
+    def test_create_file_metrics(self):
+        file_list = "file1.bloaty.csv file2.bloaty.csv"
+        file1_content = "sections,vmsize,filesize\nsection1,2,3\nsection2,7,8"
+        file2_content = "sections,vmsize,filesize\nsection1,4,5\n"
 
-    self.fs.create_file("files.lst", contents=file_list)
-    self.fs.create_file("file1.bloaty.csv", contents=file1_content)
-    self.fs.create_file("file2.bloaty.csv", contents=file2_content)
+        self.fs.create_file("files.lst", contents=file_list)
+        self.fs.create_file("file1.bloaty.csv", contents=file1_content)
+        self.fs.create_file("file2.bloaty.csv", contents=file2_content)
 
-    bloaty_merger.create_file_size_metrics("files.lst", "output.pb.gz")
+        bloaty_merger.create_file_size_metrics("files.lst", "output.pb.gz")
 
-    metrics = file_sections_pb2.FileSizeMetrics()
-    with gzip.open("output.pb.gz", "rb") as output:
-      metrics.ParseFromString(output.read())
+        metrics = file_sections_pb2.FileSizeMetrics()
+        with gzip.open("output.pb.gz", "rb") as output:
+            metrics.ParseFromString(output.read())
 
 
 if __name__ == '__main__':
-  suite = unittest.TestLoader().loadTestsFromTestCase(BloatyMergerTestCase)
-  unittest.TextTestRunner(verbosity=2).run(suite)
+    suite = unittest.TestLoader().loadTestsFromTestCase(BloatyMergerTestCase)
+    unittest.TextTestRunner(verbosity=2).run(suite)
diff --git a/bp2build/Android.bp b/bp2build/Android.bp
index 3abbc4c..b1ccc96 100644
--- a/bp2build/Android.bp
+++ b/bp2build/Android.bp
@@ -11,6 +11,7 @@
         "build_conversion.go",
         "bzl_conversion.go",
         "configurability.go",
+        "compatibility.go",
         "constants.go",
         "conversion.go",
         "metrics.go",
@@ -18,14 +19,19 @@
     ],
     deps: [
         "soong-android",
+        "soong-apex",
         "soong-bazel",
         "soong-cc",
         "soong-cc-config",
+        "soong-etc",
         "soong-genrule",
         "soong-python",
         "soong-sh",
     ],
     testSrcs: [
+        "android_app_certificate_conversion_test.go",
+        "apex_conversion_test.go",
+        "apex_key_conversion_test.go",
         "build_conversion_test.go",
         "bzl_conversion_test.go",
         "cc_library_conversion_test.go",
@@ -33,7 +39,10 @@
         "cc_library_static_conversion_test.go",
         "cc_object_conversion_test.go",
         "conversion_test.go",
+        "performance_test.go",
+        "prebuilt_etc_conversion_test.go",
         "python_binary_conversion_test.go",
+        "python_library_conversion_test.go",
         "sh_conversion_test.go",
         "testing.go",
     ],
diff --git a/bp2build/android_app_certificate_conversion_test.go b/bp2build/android_app_certificate_conversion_test.go
new file mode 100644
index 0000000..022c687
--- /dev/null
+++ b/bp2build/android_app_certificate_conversion_test.go
@@ -0,0 +1,49 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package bp2build
+
+import (
+	"android/soong/android"
+	"android/soong/java"
+
+	"testing"
+)
+
+func runAndroidAppCertificateTestCase(t *testing.T, tc bp2buildTestCase) {
+	t.Helper()
+	runBp2BuildTestCase(t, registerAndroidAppCertificateModuleTypes, tc)
+}
+
+func registerAndroidAppCertificateModuleTypes(ctx android.RegistrationContext) {
+}
+
+func TestAndroidAppCertificateSimple(t *testing.T) {
+	runAndroidAppCertificateTestCase(t, bp2buildTestCase{
+		description:                        "Android app certificate - simple example",
+		moduleTypeUnderTest:                "android_app_certificate",
+		moduleTypeUnderTestFactory:         java.AndroidAppCertificateFactory,
+		moduleTypeUnderTestBp2BuildMutator: java.AndroidAppCertificateBp2Build,
+		filesystem:                         map[string]string{},
+		blueprint: `
+android_app_certificate {
+        name: "com.android.apogee.cert",
+        certificate: "chamber_of_secrets_dir",
+}
+`,
+		expectedBazelTargets: []string{`android_app_certificate(
+    name = "com.android.apogee.cert",
+    certificate = "chamber_of_secrets_dir",
+)`}})
+}
diff --git a/bp2build/apex_conversion_test.go b/bp2build/apex_conversion_test.go
new file mode 100644
index 0000000..456f18a
--- /dev/null
+++ b/bp2build/apex_conversion_test.go
@@ -0,0 +1,178 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package bp2build
+
+import (
+	"android/soong/android"
+	"android/soong/apex"
+	"android/soong/cc"
+	"android/soong/java"
+
+	"testing"
+)
+
+func runApexTestCase(t *testing.T, tc bp2buildTestCase) {
+	t.Helper()
+	runBp2BuildTestCase(t, registerApexModuleTypes, tc)
+}
+
+func registerApexModuleTypes(ctx android.RegistrationContext) {
+	// CC module types needed as they can be APEX dependencies
+	cc.RegisterCCBuildComponents(ctx)
+
+	ctx.RegisterModuleType("cc_library", cc.LibraryFactory)
+	ctx.RegisterModuleType("apex_key", apex.ApexKeyFactory)
+	ctx.RegisterModuleType("android_app_certificate", java.AndroidAppCertificateFactory)
+	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
+}
+
+func TestApexBundleSimple(t *testing.T) {
+	runApexTestCase(t, bp2buildTestCase{
+		description:                        "apex - simple example",
+		moduleTypeUnderTest:                "apex",
+		moduleTypeUnderTestFactory:         apex.BundleFactory,
+		moduleTypeUnderTestBp2BuildMutator: apex.ApexBundleBp2Build,
+		filesystem:                         map[string]string{},
+		blueprint: `
+apex_key {
+        name: "com.android.apogee.key",
+        public_key: "com.android.apogee.avbpubkey",
+        private_key: "com.android.apogee.pem",
+	bazel_module: { bp2build_available: false },
+}
+
+android_app_certificate {
+        name: "com.android.apogee.certificate",
+        certificate: "com.android.apogee",
+        bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+        name: "native_shared_lib_1",
+	bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+        name: "native_shared_lib_2",
+	bazel_module: { bp2build_available: false },
+}
+
+// TODO(b/194878861): Add bp2build support for prebuilt_etc
+cc_library {
+        name: "pretend_prebuilt_1",
+        bazel_module: { bp2build_available: false },
+}
+
+// TODO(b/194878861): Add bp2build support for prebuilt_etc
+cc_library {
+        name: "pretend_prebuilt_2",
+        bazel_module: { bp2build_available: false },
+}
+
+filegroup {
+	name: "com.android.apogee-file_contexts",
+        srcs: [
+                "com.android.apogee-file_contexts",
+        ],
+        bazel_module: { bp2build_available: false },
+}
+
+apex {
+	name: "com.android.apogee",
+	manifest: "apogee_manifest.json",
+	androidManifest: "ApogeeAndroidManifest.xml",
+        file_contexts: "com.android.apogee-file_contexts",
+	min_sdk_version: "29",
+	key: "com.android.apogee.key",
+	certificate: "com.android.apogee.certificate",
+	updatable: false,
+	installable: false,
+	native_shared_libs: [
+	    "native_shared_lib_1",
+	    "native_shared_lib_2",
+	],
+	binaries: [
+            "binary_1",
+	    "binary_2",
+	],
+	prebuilts: [
+	    "pretend_prebuilt_1",
+	    "pretend_prebuilt_2",
+	],
+}
+`,
+		expectedBazelTargets: []string{`apex(
+    name = "com.android.apogee",
+    android_manifest = "ApogeeAndroidManifest.xml",
+    binaries = [
+        "binary_1",
+        "binary_2",
+    ],
+    certificate = ":com.android.apogee.certificate",
+    file_contexts = ":com.android.apogee-file_contexts",
+    installable = False,
+    key = ":com.android.apogee.key",
+    manifest = "apogee_manifest.json",
+    min_sdk_version = "29",
+    native_shared_libs = [
+        ":native_shared_lib_1",
+        ":native_shared_lib_2",
+    ],
+    prebuilts = [
+        ":pretend_prebuilt_1",
+        ":pretend_prebuilt_2",
+    ],
+    updatable = False,
+)`}})
+}
+
+func TestApexBundleDefaultPropertyValues(t *testing.T) {
+	runApexTestCase(t, bp2buildTestCase{
+		description:                        "apex - default property values",
+		moduleTypeUnderTest:                "apex",
+		moduleTypeUnderTestFactory:         apex.BundleFactory,
+		moduleTypeUnderTestBp2BuildMutator: apex.ApexBundleBp2Build,
+		filesystem:                         map[string]string{},
+		blueprint: `
+apex {
+	name: "com.android.apogee",
+	manifest: "apogee_manifest.json",
+}
+`,
+		expectedBazelTargets: []string{`apex(
+    name = "com.android.apogee",
+    manifest = "apogee_manifest.json",
+)`}})
+}
+
+func TestApexBundleHasBazelModuleProps(t *testing.T) {
+	runApexTestCase(t, bp2buildTestCase{
+		description:                        "apex - has bazel module props",
+		moduleTypeUnderTest:                "apex",
+		moduleTypeUnderTestFactory:         apex.BundleFactory,
+		moduleTypeUnderTestBp2BuildMutator: apex.ApexBundleBp2Build,
+		filesystem:                         map[string]string{},
+		blueprint: `
+apex {
+	name: "apogee",
+	manifest: "manifest.json",
+	bazel_module: { bp2build_available: true },
+}
+`,
+		expectedBazelTargets: []string{`apex(
+    name = "apogee",
+    manifest = "manifest.json",
+)`}})
+}
diff --git a/bp2build/apex_key_conversion_test.go b/bp2build/apex_key_conversion_test.go
new file mode 100644
index 0000000..8e1aa09
--- /dev/null
+++ b/bp2build/apex_key_conversion_test.go
@@ -0,0 +1,51 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package bp2build
+
+import (
+	"android/soong/android"
+	"android/soong/apex"
+
+	"testing"
+)
+
+func runApexKeyTestCase(t *testing.T, tc bp2buildTestCase) {
+	t.Helper()
+	runBp2BuildTestCase(t, registerApexKeyModuleTypes, tc)
+}
+
+func registerApexKeyModuleTypes(ctx android.RegistrationContext) {
+}
+
+func TestApexKeySimple(t *testing.T) {
+	runApexKeyTestCase(t, bp2buildTestCase{
+		description:                        "apex key - simple example",
+		moduleTypeUnderTest:                "apex_key",
+		moduleTypeUnderTestFactory:         apex.ApexKeyFactory,
+		moduleTypeUnderTestBp2BuildMutator: apex.ApexKeyBp2Build,
+		filesystem:                         map[string]string{},
+		blueprint: `
+apex_key {
+        name: "com.android.apogee.key",
+        public_key: "com.android.apogee.avbpubkey",
+        private_key: "com.android.apogee.pem",
+}
+`,
+		expectedBazelTargets: []string{`apex_key(
+    name = "com.android.apogee.key",
+    private_key = "com.android.apogee.pem",
+    public_key = "com.android.apogee.avbpubkey",
+)`}})
+}
diff --git a/bp2build/bp2build.go b/bp2build/bp2build.go
index 59c5acd..06a7306 100644
--- a/bp2build/bp2build.go
+++ b/bp2build/bp2build.go
@@ -16,6 +16,7 @@
 
 import (
 	"android/soong/android"
+	"android/soong/bazel"
 	"fmt"
 	"os"
 )
@@ -28,12 +29,12 @@
 	bp2buildDir := android.PathForOutput(ctx, "bp2build")
 	android.RemoveAllOutputDir(bp2buildDir)
 
-	buildToTargets, metrics := GenerateBazelTargets(ctx, true)
+	buildToTargets, metrics, compatLayer := GenerateBazelTargets(ctx, true)
 	bp2buildFiles := CreateBazelFiles(nil, buildToTargets, ctx.mode)
 	writeFiles(ctx, bp2buildDir, bp2buildFiles)
 
-	soongInjectionDir := android.PathForOutput(ctx, "soong_injection")
-	writeFiles(ctx, soongInjectionDir, CreateSoongInjectionFiles())
+	soongInjectionDir := android.PathForOutput(ctx, bazel.SoongInjectionDirName)
+	writeFiles(ctx, soongInjectionDir, CreateSoongInjectionFiles(compatLayer))
 
 	return metrics
 }
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index bddc524..f652a35 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -14,13 +14,20 @@
 
 package bp2build
 
+/*
+For shareable/common functionality for conversion from soong-module to build files
+for queryview/bp2build
+*/
+
 import (
-	"android/soong/android"
-	"android/soong/bazel"
 	"fmt"
 	"reflect"
+	"sort"
 	"strings"
 
+	"android/soong/android"
+	"android/soong/bazel"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
@@ -31,9 +38,11 @@
 
 type BazelTarget struct {
 	name            string
+	packageName     string
 	content         string
 	ruleClass       string
 	bzlLoadLocation string
+	handcrafted     bool
 }
 
 // IsLoadedFromStarlark determines if the BazelTarget's rule class is loaded from a .bzl file,
@@ -42,15 +51,60 @@
 	return t.bzlLoadLocation != ""
 }
 
+// Label is the fully qualified Bazel label constructed from the BazelTarget's
+// package name and target name.
+func (t BazelTarget) Label() string {
+	if t.packageName == "." {
+		return "//:" + t.name
+	} else {
+		return "//" + t.packageName + ":" + t.name
+	}
+}
+
 // BazelTargets is a typedef for a slice of BazelTarget objects.
 type BazelTargets []BazelTarget
 
+// HasHandcraftedTargetsreturns true if a set of bazel targets contain
+// handcrafted ones.
+func (targets BazelTargets) hasHandcraftedTargets() bool {
+	for _, target := range targets {
+		if target.handcrafted {
+			return true
+		}
+	}
+	return false
+}
+
+// sort a list of BazelTargets in-place, by name, and by generated/handcrafted types.
+func (targets BazelTargets) sort() {
+	sort.Slice(targets, func(i, j int) bool {
+		if targets[i].handcrafted != targets[j].handcrafted {
+			// Handcrafted targets will be generated after the bp2build generated targets.
+			return targets[j].handcrafted
+		}
+		// This will cover all bp2build generated targets.
+		return targets[i].name < targets[j].name
+	})
+}
+
 // String returns the string representation of BazelTargets, without load
 // statements (use LoadStatements for that), since the targets are usually not
 // adjacent to the load statements at the top of the BUILD file.
 func (targets BazelTargets) String() string {
 	var res string
 	for i, target := range targets {
+		// There is only at most 1 handcrafted "target", because its contents
+		// represent the entire BUILD file content from the tree. See
+		// build_conversion.go#getHandcraftedBuildContent for more information.
+		//
+		// Add a header to make it easy to debug where the handcrafted targets
+		// are in a generated BUILD file.
+		if target.handcrafted {
+			res += "# -----------------------------\n"
+			res += "# Section: Handcrafted targets. \n"
+			res += "# -----------------------------\n\n"
+		}
+
 		res += target.content
 		if i != len(targets)-1 {
 			res += "\n\n"
@@ -176,7 +230,7 @@
 	return attributes
 }
 
-func GenerateBazelTargets(ctx *CodegenContext, generateFilegroups bool) (map[string]BazelTargets, CodegenMetrics) {
+func GenerateBazelTargets(ctx *CodegenContext, generateFilegroups bool) (map[string]BazelTargets, CodegenMetrics, CodegenCompatLayer) {
 	buildFileToTargets := make(map[string]BazelTargets)
 	buildFileToAppend := make(map[string]bool)
 
@@ -185,6 +239,10 @@
 		RuleClassCount: make(map[string]int),
 	}
 
+	compatLayer := CodegenCompatLayer{
+		NameToLabelMap: make(map[string]string),
+	}
+
 	dirs := make(map[string]bool)
 
 	bpCtx := ctx.Context()
@@ -192,30 +250,37 @@
 		dir := bpCtx.ModuleDir(m)
 		dirs[dir] = true
 
-		var t BazelTarget
+		var targets []BazelTarget
 
 		switch ctx.Mode() {
 		case Bp2Build:
 			if b, ok := m.(android.Bazelable); ok && b.HasHandcraftedLabel() {
 				metrics.handCraftedTargetCount += 1
 				metrics.TotalModuleCount += 1
+				compatLayer.AddNameToLabelEntry(m.Name(), b.HandcraftedLabel())
 				pathToBuildFile := getBazelPackagePath(b)
 				// We are using the entire contents of handcrafted build file, so if multiple targets within
 				// a package have handcrafted targets, we only want to include the contents one time.
 				if _, exists := buildFileToAppend[pathToBuildFile]; exists {
 					return
 				}
-				var err error
-				t, err = getHandcraftedBuildContent(ctx, b, pathToBuildFile)
+				t, err := getHandcraftedBuildContent(ctx, b, pathToBuildFile)
 				if err != nil {
 					panic(fmt.Errorf("Error converting %s: %s", bpCtx.ModuleName(m), err))
 				}
+				targets = append(targets, t)
 				// TODO(b/181575318): currently we append the whole BUILD file, let's change that to do
 				// something more targeted based on the rule type and target
 				buildFileToAppend[pathToBuildFile] = true
-			} else if btm, ok := m.(android.BazelTargetModule); ok {
-				t = generateBazelTarget(bpCtx, m, btm)
-				metrics.RuleClassCount[t.ruleClass] += 1
+			} else if aModule, ok := m.(android.Module); ok && aModule.IsConvertedByBp2build() {
+				targets = generateBazelTargets(bpCtx, aModule)
+				for _, t := range targets {
+					if t.name == m.Name() {
+						// only add targets that exist in Soong to compatibility layer
+						compatLayer.AddNameToLabelEntry(m.Name(), t.Label())
+					}
+					metrics.RuleClassCount[t.ruleClass] += 1
+				}
 			} else {
 				metrics.TotalModuleCount += 1
 				return
@@ -227,12 +292,13 @@
 				// be mapped cleanly to a bazel label.
 				return
 			}
-			t = generateSoongModuleTarget(bpCtx, m)
+			t := generateSoongModuleTarget(bpCtx, m)
+			targets = append(targets, t)
 		default:
 			panic(fmt.Errorf("Unknown code-generation mode: %s", ctx.Mode()))
 		}
 
-		buildFileToTargets[dir] = append(buildFileToTargets[dir], t)
+		buildFileToTargets[dir] = append(buildFileToTargets[dir], targets...)
 	})
 	if generateFilegroups {
 		// Add a filegroup target that exposes all sources in the subtree of this package
@@ -246,7 +312,7 @@
 		}
 	}
 
-	return buildFileToTargets, metrics
+	return buildFileToTargets, metrics, compatLayer
 }
 
 func getBazelPackagePath(b android.Bazelable) string {
@@ -267,25 +333,43 @@
 	}
 	// TODO(b/181575318): once this is more targeted, we need to include name, rule class, etc
 	return BazelTarget{
-		content: c,
+		content:     c,
+		handcrafted: true,
 	}, nil
 }
 
-func generateBazelTarget(ctx bpToBuildContext, m blueprint.Module, btm android.BazelTargetModule) BazelTarget {
-	ruleClass := btm.RuleClass()
-	bzlLoadLocation := btm.BzlLoadLocation()
+func generateBazelTargets(ctx bpToBuildContext, m android.Module) []BazelTarget {
+	var targets []BazelTarget
+	for _, m := range m.Bp2buildTargets() {
+		targets = append(targets, generateBazelTarget(ctx, m))
+	}
+	return targets
+}
+
+type bp2buildModule interface {
+	TargetName() string
+	TargetPackage() string
+	BazelRuleClass() string
+	BazelRuleLoadLocation() string
+	BazelAttributes() interface{}
+}
+
+func generateBazelTarget(ctx bpToBuildContext, m bp2buildModule) BazelTarget {
+	ruleClass := m.BazelRuleClass()
+	bzlLoadLocation := m.BazelRuleLoadLocation()
 
 	// extract the bazel attributes from the module.
-	props := getBuildProperties(ctx, m)
+	props := extractModuleProperties([]interface{}{m.BazelAttributes()})
 
 	delete(props.Attrs, "bp2build_available")
 
 	// Return the Bazel target with rule class and attributes, ready to be
 	// code-generated.
 	attributes := propsToAttributes(props.Attrs)
-	targetName := targetNameForBp2Build(ctx, m)
+	targetName := m.TargetName()
 	return BazelTarget{
 		name:            targetName,
+		packageName:     m.TargetPackage(),
 		ruleClass:       ruleClass,
 		bzlLoadLocation: bzlLoadLocation,
 		content: fmt.Sprintf(
@@ -294,6 +378,7 @@
 			targetName,
 			attributes,
 		),
+		handcrafted: false,
 	}
 }
 
@@ -334,24 +419,21 @@
 }
 
 func getBuildProperties(ctx bpToBuildContext, m blueprint.Module) BazelAttributes {
-	var allProps map[string]string
 	// TODO: this omits properties for blueprint modules (blueprint_go_binary,
 	// bootstrap_go_binary, bootstrap_go_package), which will have to be handled separately.
 	if aModule, ok := m.(android.Module); ok {
-		allProps = ExtractModuleProperties(aModule)
+		return extractModuleProperties(aModule.GetProperties())
 	}
 
-	return BazelAttributes{
-		Attrs: allProps,
-	}
+	return BazelAttributes{}
 }
 
 // Generically extract module properties and types into a map, keyed by the module property name.
-func ExtractModuleProperties(aModule android.Module) map[string]string {
+func extractModuleProperties(props []interface{}) BazelAttributes {
 	ret := map[string]string{}
 
 	// Iterate over this android.Module's property structs.
-	for _, properties := range aModule.GetProperties() {
+	for _, properties := range props {
 		propertiesValue := reflect.ValueOf(properties)
 		// Check that propertiesValue is a pointer to the Properties struct, like
 		// *cc.BaseLinkerProperties or *java.CompilerProperties.
@@ -370,7 +452,9 @@
 		}
 	}
 
-	return ret
+	return BazelAttributes{
+		Attrs: ret,
+	}
 }
 
 func isStructPtr(t reflect.Type) bool {
@@ -406,7 +490,7 @@
 		return prettyPrint(propertyValue.Elem(), indent)
 	case reflect.Slice:
 		if propertyValue.Len() == 0 {
-			return "", nil
+			return "[]", nil
 		}
 
 		if propertyValue.Len() == 1 {
@@ -449,6 +533,9 @@
 		ret = "{\n"
 		// Sort and print the struct props by the key.
 		structProps := extractStructProperties(propertyValue, indent)
+		if len(structProps) == 0 {
+			return "", nil
+		}
 		for _, k := range android.SortedStringKeys(structProps) {
 			ret += makeIndent(indent + 1)
 			ret += fmt.Sprintf("%q: %s,\n", k, structProps[k])
@@ -528,7 +615,14 @@
 		} else {
 			return true
 		}
+	// Always print bools, if you want a bool attribute to be able to take the default value, use a
+	// bool pointer instead
+	case reflect.Bool:
+		return false
 	default:
+		if !value.IsValid() {
+			return true
+		}
 		zeroValue := reflect.Zero(value.Type())
 		result := value.Interface() == zeroValue.Interface()
 		return result
diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go
index 63a6c2e..0d9106c 100644
--- a/bp2build/build_conversion_test.go
+++ b/bp2build/build_conversion_test.go
@@ -23,12 +23,14 @@
 
 func TestGenerateSoongModuleTargets(t *testing.T) {
 	testCases := []struct {
+		description         string
 		bp                  string
 		expectedBazelTarget string
 	}{
 		{
+			description: "only name",
 			bp: `custom { name: "foo" }
-		`,
+    `,
 			expectedBazelTarget: `soong_module(
     name = "foo",
     soong_module_name = "foo",
@@ -36,14 +38,16 @@
     soong_module_variant = "",
     soong_module_deps = [
     ],
+    bool_prop = False,
 )`,
 		},
 		{
+			description: "handles bool",
 			bp: `custom {
-	name: "foo",
-	ramdisk: true,
+  name: "foo",
+  bool_prop: true,
 }
-		`,
+    `,
 			expectedBazelTarget: `soong_module(
     name = "foo",
     soong_module_name = "foo",
@@ -51,15 +55,16 @@
     soong_module_variant = "",
     soong_module_deps = [
     ],
-    ramdisk = True,
+    bool_prop = True,
 )`,
 		},
 		{
+			description: "string escaping",
 			bp: `custom {
-	name: "foo",
-	owner: "a_string_with\"quotes\"_and_\\backslashes\\\\",
+  name: "foo",
+  owner: "a_string_with\"quotes\"_and_\\backslashes\\\\",
 }
-		`,
+    `,
 			expectedBazelTarget: `soong_module(
     name = "foo",
     soong_module_name = "foo",
@@ -67,15 +72,17 @@
     soong_module_variant = "",
     soong_module_deps = [
     ],
+    bool_prop = False,
     owner = "a_string_with\"quotes\"_and_\\backslashes\\\\",
 )`,
 		},
 		{
+			description: "single item string list",
 			bp: `custom {
-	name: "foo",
-	required: ["bar"],
+  name: "foo",
+  required: ["bar"],
 }
-		`,
+    `,
 			expectedBazelTarget: `soong_module(
     name = "foo",
     soong_module_name = "foo",
@@ -83,15 +90,17 @@
     soong_module_variant = "",
     soong_module_deps = [
     ],
+    bool_prop = False,
     required = ["bar"],
 )`,
 		},
 		{
+			description: "list of strings",
 			bp: `custom {
-	name: "foo",
-	target_required: ["qux", "bazqux"],
+  name: "foo",
+  target_required: ["qux", "bazqux"],
 }
-		`,
+    `,
 			expectedBazelTarget: `soong_module(
     name = "foo",
     soong_module_name = "foo",
@@ -99,6 +108,7 @@
     soong_module_variant = "",
     soong_module_deps = [
     ],
+    bool_prop = False,
     target_required = [
         "qux",
         "bazqux",
@@ -106,18 +116,19 @@
 )`,
 		},
 		{
+			description: "dist/dists",
 			bp: `custom {
-	name: "foo",
-	dist: {
-		targets: ["goal_foo"],
-		tag: ".foo",
-	},
-	dists: [{
-		targets: ["goal_bar"],
-		tag: ".bar",
-	}],
+  name: "foo",
+  dist: {
+    targets: ["goal_foo"],
+    tag: ".foo",
+  },
+  dists: [{
+    targets: ["goal_bar"],
+    tag: ".bar",
+  }],
 }
-		`,
+    `,
 			expectedBazelTarget: `soong_module(
     name = "foo",
     soong_module_name = "foo",
@@ -125,6 +136,7 @@
     soong_module_variant = "",
     soong_module_deps = [
     ],
+    bool_prop = False,
     dist = {
         "tag": ".foo",
         "targets": ["goal_foo"],
@@ -136,20 +148,21 @@
 )`,
 		},
 		{
+			description: "put it together",
 			bp: `custom {
-	name: "foo",
-	required: ["bar"],
-	target_required: ["qux", "bazqux"],
-	ramdisk: true,
-	owner: "custom_owner",
-	dists: [
-		{
-			tag: ".tag",
-			targets: ["my_goal"],
-		},
-	],
+  name: "foo",
+  required: ["bar"],
+  target_required: ["qux", "bazqux"],
+  bool_prop: true,
+  owner: "custom_owner",
+  dists: [
+    {
+      tag: ".tag",
+      targets: ["my_goal"],
+    },
+  ],
 }
-		`,
+    `,
 			expectedBazelTarget: `soong_module(
     name = "foo",
     soong_module_name = "foo",
@@ -157,12 +170,12 @@
     soong_module_variant = "",
     soong_module_deps = [
     ],
+    bool_prop = True,
     dists = [{
         "tag": ".tag",
         "targets": ["my_goal"],
     }],
     owner = "custom_owner",
-    ramdisk = True,
     required = ["bar"],
     target_required = [
         "qux",
@@ -174,31 +187,33 @@
 
 	dir := "."
 	for _, testCase := range testCases {
-		config := android.TestConfig(buildDir, nil, testCase.bp, nil)
-		ctx := android.NewTestContext(config)
+		t.Run(testCase.description, func(t *testing.T) {
+			config := android.TestConfig(buildDir, nil, testCase.bp, nil)
+			ctx := android.NewTestContext(config)
 
-		ctx.RegisterModuleType("custom", customModuleFactory)
-		ctx.Register()
+			ctx.RegisterModuleType("custom", customModuleFactory)
+			ctx.Register()
 
-		_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
-		android.FailIfErrored(t, errs)
-		_, errs = ctx.PrepareBuildActions(config)
-		android.FailIfErrored(t, errs)
+			_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
+			android.FailIfErrored(t, errs)
+			_, errs = ctx.PrepareBuildActions(config)
+			android.FailIfErrored(t, errs)
 
-		codegenCtx := NewCodegenContext(config, *ctx.Context, QueryView)
-		bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
-		if actualCount, expectedCount := len(bazelTargets), 1; actualCount != expectedCount {
-			t.Fatalf("Expected %d bazel target, got %d", expectedCount, actualCount)
-		}
+			codegenCtx := NewCodegenContext(config, *ctx.Context, QueryView)
+			bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
+			if actualCount, expectedCount := len(bazelTargets), 1; actualCount != expectedCount {
+				t.Fatalf("Expected %d bazel target, got %d", expectedCount, actualCount)
+			}
 
-		actualBazelTarget := bazelTargets[0]
-		if actualBazelTarget.content != testCase.expectedBazelTarget {
-			t.Errorf(
-				"Expected generated Bazel target to be '%s', got '%s'",
-				testCase.expectedBazelTarget,
-				actualBazelTarget.content,
-			)
-		}
+			actualBazelTarget := bazelTargets[0]
+			if actualBazelTarget.content != testCase.expectedBazelTarget {
+				t.Errorf(
+					"Expected generated Bazel target to be '%s', got '%s'",
+					testCase.expectedBazelTarget,
+					actualBazelTarget.content,
+				)
+			}
+		})
 	}
 }
 
@@ -319,16 +334,14 @@
 		config := android.TestConfig(buildDir, nil, testCase.bp, nil)
 		ctx := android.NewTestContext(config)
 
-		ctx.RegisterModuleType("custom", customModuleFactory)
-		ctx.RegisterBp2BuildMutator("custom", customBp2BuildMutator)
-		ctx.RegisterForBazelConversion()
+		registerCustomModuleForBp2buildConversion(ctx)
 
 		_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
-		if Errored(t, "", errs) {
+		if errored(t, "", errs) {
 			continue
 		}
 		_, errs = ctx.ResolveDependencies(config)
-		if Errored(t, "", errs) {
+		if errored(t, "", errs) {
 			continue
 		}
 
@@ -467,12 +480,12 @@
     name = "bar",
 )
 
-my_proto_library(
-    name = "bar_my_proto_library_deps",
-)
-
 proto_library(
     name = "bar_proto_library_deps",
+)
+
+my_proto_library(
+    name = "bar_my_proto_library_deps",
 )`,
 			expectedBazelTargetCount: 3,
 			expectedLoadStatements: `load("//build/bazel/rules:proto.bzl", "my_proto_library", "proto_library")
@@ -541,7 +554,6 @@
 		moduleTypeUnderTestFactory         android.ModuleFactory
 		moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext)
 		preArchMutators                    []android.RegisterMutatorFunc
-		depsMutators                       []android.RegisterMutatorFunc
 		bp                                 string
 		expectedBazelTargets               []string
 		fs                                 map[string]string
@@ -705,7 +717,6 @@
 			moduleTypeUnderTest:                "genrule",
 			moduleTypeUnderTestFactory:         genrule.GenRuleFactory,
 			moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
-			depsMutators:                       []android.RegisterMutatorFunc{genrule.RegisterGenruleBp2BuildDeps},
 			bp: `genrule {
     name: "foo.tool",
     out: ["foo_tool.out"],
@@ -743,7 +754,6 @@
 			moduleTypeUnderTest:                "genrule",
 			moduleTypeUnderTestFactory:         genrule.GenRuleFactory,
 			moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
-			depsMutators:                       []android.RegisterMutatorFunc{genrule.RegisterGenruleBp2BuildDeps},
 			bp: `genrule {
     name: "foo.tools",
     out: ["foo_tool.out", "foo_tool2.out"],
@@ -783,7 +793,6 @@
 			moduleTypeUnderTest:                "genrule",
 			moduleTypeUnderTestFactory:         genrule.GenRuleFactory,
 			moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
-			depsMutators:                       []android.RegisterMutatorFunc{genrule.RegisterGenruleBp2BuildDeps},
 			bp: `genrule {
     name: "foo",
     out: ["foo.out"],
@@ -807,7 +816,6 @@
 			moduleTypeUnderTest:                "genrule",
 			moduleTypeUnderTestFactory:         genrule.GenRuleFactory,
 			moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
-			depsMutators:                       []android.RegisterMutatorFunc{genrule.RegisterGenruleBp2BuildDeps},
 			bp: `genrule {
     name: "foo",
     out: ["foo.out"],
@@ -831,7 +839,6 @@
 			moduleTypeUnderTest:                "genrule",
 			moduleTypeUnderTestFactory:         genrule.GenRuleFactory,
 			moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
-			depsMutators:                       []android.RegisterMutatorFunc{genrule.RegisterGenruleBp2BuildDeps},
 			bp: `genrule {
     name: "foo",
     out: ["foo.out"],
@@ -858,7 +865,6 @@
 			moduleTypeUnderTest:                "genrule",
 			moduleTypeUnderTestFactory:         genrule.GenRuleFactory,
 			moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
-			depsMutators:                       []android.RegisterMutatorFunc{genrule.RegisterGenruleBp2BuildDeps},
 			bp: `genrule {
     name: "foo",
     out: ["foo.out"],
@@ -885,7 +891,6 @@
 			moduleTypeUnderTest:                "genrule",
 			moduleTypeUnderTestFactory:         genrule.GenRuleFactory,
 			moduleTypeUnderTestBp2BuildMutator: genrule.GenruleBp2Build,
-			depsMutators:                       []android.RegisterMutatorFunc{genrule.RegisterGenruleBp2BuildDeps},
 			bp: `genrule {
     name: "foo",
     out: ["foo.out"],
@@ -918,18 +923,15 @@
 		config := android.TestConfig(buildDir, nil, testCase.bp, fs)
 		ctx := android.NewTestContext(config)
 		ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
-		for _, m := range testCase.depsMutators {
-			ctx.DepsBp2BuildMutators(m)
-		}
 		ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
 		ctx.RegisterForBazelConversion()
 
 		_, errs := ctx.ParseFileList(dir, toParse)
-		if Errored(t, testCase.description, errs) {
+		if errored(t, testCase.description, errs) {
 			continue
 		}
 		_, errs = ctx.ResolveDependencies(config)
-		if Errored(t, testCase.description, errs) {
+		if errored(t, testCase.description, errs) {
 			continue
 		}
 
@@ -957,17 +959,6 @@
 	}
 }
 
-func Errored(t *testing.T, desc string, errs []error) bool {
-	t.Helper()
-	if len(errs) > 0 {
-		for _, err := range errs {
-			t.Errorf("%s: %s", desc, err)
-		}
-		return true
-	}
-	return false
-}
-
 type bp2buildMutator = func(android.TopDownMutatorContext)
 
 func TestBp2BuildInlinesDefaults(t *testing.T) {
@@ -1120,8 +1111,8 @@
         "out",
     ],
     srcs = [
-        "in1",
         "srcs-from-3",
+        "in1",
     ],
 )`,
 			description: "genrule applies properties from genrule_defaults transitively",
@@ -1222,22 +1213,24 @@
 
 	dir := "."
 	for _, testCase := range testCases {
-		config := android.TestConfig(buildDir, nil, testCase.bp, nil)
-		ctx := android.NewTestContext(config)
-		ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
-		ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
-		ctx.RegisterForBazelConversion()
+		t.Run(testCase.description, func(t *testing.T) {
+			config := android.TestConfig(buildDir, nil, testCase.bp, nil)
+			ctx := android.NewTestContext(config)
+			ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
+			ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
+			ctx.RegisterForBazelConversion()
 
-		_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
-		android.FailIfErrored(t, errs)
-		_, errs = ctx.ResolveDependencies(config)
-		android.FailIfErrored(t, errs)
+			_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
+			android.FailIfErrored(t, errs)
+			_, errs = ctx.ResolveDependencies(config)
+			android.FailIfErrored(t, errs)
 
-		codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
-		bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
-		if actualCount := len(bazelTargets); actualCount != testCase.expectedCount {
-			t.Fatalf("%s: Expected %d bazel target, got %d", testCase.description, testCase.expectedCount, actualCount)
-		}
+			codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+			bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
+			if actualCount := len(bazelTargets); actualCount != testCase.expectedCount {
+				t.Fatalf("%s: Expected %d bazel target, got %d", testCase.description, testCase.expectedCount, actualCount)
+			}
+		})
 	}
 }
 
@@ -1366,7 +1359,6 @@
 		moduleTypeUnderTestFactory         android.ModuleFactory
 		moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext)
 		preArchMutators                    []android.RegisterMutatorFunc
-		depsMutators                       []android.RegisterMutatorFunc
 		bp                                 string
 		expectedBazelTargets               []string
 		fs                                 map[string]string
@@ -1394,14 +1386,14 @@
 			moduleTypeUnderTestFactory:         android.FileGroupFactory,
 			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
 			bp: `filegroup {
-    name: "fg_foo",
-    bazel_module: { label: "//other:fg_foo" },
-}
+		    name: "fg_foo",
+		    bazel_module: { label: "//other:fg_foo" },
+		}
 
-filegroup {
-    name: "foo",
-    bazel_module: { label: "//other:foo" },
-}`,
+		filegroup {
+		    name: "foo",
+		    bazel_module: { label: "//other:foo" },
+		}`,
 			expectedBazelTargets: []string{
 				`// BUILD file`,
 			},
@@ -1410,25 +1402,31 @@
 			},
 		},
 		{
-			description:                        "filegroup bazel_module.label and bp2build",
+			description:                        "filegroup bazel_module.label and bp2build in subdir",
 			moduleTypeUnderTest:                "filegroup",
 			moduleTypeUnderTestFactory:         android.FileGroupFactory,
 			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
-			bp: `filegroup {
-    name: "fg_foo",
-    bazel_module: {
-      label: "//other:fg_foo",
-      bp2build_available: true,
-    },
-}`,
+			dir:                                "other",
+			bp:                                 ``,
+			fs: map[string]string{
+				"other/Android.bp": `filegroup {
+				name: "fg_foo",
+				bazel_module: {
+					bp2build_available: true,
+				},
+			}
+			filegroup {
+				name: "fg_bar",
+				bazel_module: {
+					label: "//other:fg_bar"
+				},
+			}`,
+				"other/BUILD.bazel": `// definition for fg_bar`,
+			},
 			expectedBazelTargets: []string{
 				`filegroup(
     name = "fg_foo",
-)`,
-				`// BUILD file`,
-			},
-			fs: map[string]string{
-				"other/BUILD.bazel": `// BUILD file`,
+)`, `// definition for fg_bar`,
 			},
 		},
 		{
@@ -1437,18 +1435,18 @@
 			moduleTypeUnderTestFactory:         android.FileGroupFactory,
 			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
 			bp: `filegroup {
-    name: "fg_foo",
-    bazel_module: {
-      label: "//other:fg_foo",
-    },
-}
+		    name: "fg_foo",
+		    bazel_module: {
+		      label: "//other:fg_foo",
+		    },
+		}
 
-filegroup {
-    name: "fg_bar",
-    bazel_module: {
-      bp2build_available: true,
-    },
-}`,
+		filegroup {
+		    name: "fg_bar",
+		    bazel_module: {
+		      bp2build_available: true,
+		    },
+		}`,
 			expectedBazelTargets: []string{
 				`filegroup(
     name = "fg_bar",
@@ -1463,53 +1461,58 @@
 
 	dir := "."
 	for _, testCase := range testCases {
-		fs := make(map[string][]byte)
-		toParse := []string{
-			"Android.bp",
-		}
-		for f, content := range testCase.fs {
-			if strings.HasSuffix(f, "Android.bp") {
-				toParse = append(toParse, f)
+		t.Run(testCase.description, func(t *testing.T) {
+			fs := make(map[string][]byte)
+			toParse := []string{
+				"Android.bp",
 			}
-			fs[f] = []byte(content)
-		}
-		config := android.TestConfig(buildDir, nil, testCase.bp, fs)
-		ctx := android.NewTestContext(config)
-		ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
-		for _, m := range testCase.depsMutators {
-			ctx.DepsBp2BuildMutators(m)
-		}
-		ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
-		ctx.RegisterForBazelConversion()
+			for f, content := range testCase.fs {
+				if strings.HasSuffix(f, "Android.bp") {
+					toParse = append(toParse, f)
+				}
+				fs[f] = []byte(content)
+			}
+			config := android.TestConfig(buildDir, nil, testCase.bp, fs)
+			ctx := android.NewTestContext(config)
+			ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
+			ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
+			ctx.RegisterForBazelConversion()
 
-		_, errs := ctx.ParseFileList(dir, toParse)
-		if Errored(t, testCase.description, errs) {
-			continue
-		}
-		_, errs = ctx.ResolveDependencies(config)
-		if Errored(t, testCase.description, errs) {
-			continue
-		}
+			_, errs := ctx.ParseFileList(dir, toParse)
+			if errored(t, testCase.description, errs) {
+				return
+			}
+			_, errs = ctx.ResolveDependencies(config)
+			if errored(t, testCase.description, errs) {
+				return
+			}
 
-		checkDir := dir
-		if testCase.dir != "" {
-			checkDir = testCase.dir
-		}
-		bazelTargets := generateBazelTargetsForDir(NewCodegenContext(config, *ctx.Context, Bp2Build), checkDir)
-		if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
-			t.Errorf("%s: Expected %d bazel target, got %d\n%s", testCase.description, expectedCount, actualCount, bazelTargets)
-		} else {
+			checkDir := dir
+			if testCase.dir != "" {
+				checkDir = testCase.dir
+			}
+			bazelTargets := generateBazelTargetsForDir(NewCodegenContext(config, *ctx.Context, Bp2Build), checkDir)
+			bazelTargets.sort()
+			actualCount := len(bazelTargets)
+			expectedCount := len(testCase.expectedBazelTargets)
+			if actualCount != expectedCount {
+				t.Errorf("Expected %d bazel target, got %d\n%s", expectedCount, actualCount, bazelTargets)
+			}
+			if !strings.Contains(bazelTargets.String(), "Section: Handcrafted targets. ") {
+				t.Errorf("Expected string representation of bazelTargets to contain handcrafted section header.")
+			}
 			for i, target := range bazelTargets {
-				if w, g := testCase.expectedBazelTargets[i], target.content; w != g {
+				actualContent := target.content
+				expectedContent := testCase.expectedBazelTargets[i]
+				if expectedContent != actualContent {
 					t.Errorf(
-						"%s: Expected generated Bazel target to be '%s', got '%s'",
-						testCase.description,
-						w,
-						g,
+						"Expected generated Bazel target to be '%s', got '%s'",
+						expectedContent,
+						actualContent,
 					)
 				}
 			}
-		}
+		})
 	}
 }
 
@@ -1538,10 +1541,10 @@
 			expectedBazelTargets: []string{`filegroup(
     name = "fg_foo",
     srcs = [
-        "//dir:e.txt",
-        "//dir:f.txt",
         "a.txt",
         "b.txt",
+        "//dir:e.txt",
+        "//dir:f.txt",
     ],
 )`,
 			},
@@ -1578,9 +1581,9 @@
 			expectedBazelTargets: []string{`filegroup(
     name = "fg_foo",
     srcs = [
+        "a.txt",
         "//dir/subdir:e.txt",
         "//dir/subdir:f.txt",
-        "a.txt",
     ],
 )`,
 			},
@@ -1606,11 +1609,11 @@
 		ctx.RegisterForBazelConversion()
 
 		_, errs := ctx.ParseFileList(dir, toParse)
-		if Errored(t, testCase.description, errs) {
+		if errored(t, testCase.description, errs) {
 			continue
 		}
 		_, errs = ctx.ResolveDependencies(config)
-		if Errored(t, testCase.description, errs) {
+		if errored(t, testCase.description, errs) {
 			continue
 		}
 
diff --git a/bp2build/bzl_conversion_test.go b/bp2build/bzl_conversion_test.go
index 32b12e4..9e0c0a1 100644
--- a/bp2build/bzl_conversion_test.go
+++ b/bp2build/bzl_conversion_test.go
@@ -22,8 +22,6 @@
 	"testing"
 )
 
-var buildDir string
-
 func setUp() {
 	var err error
 	buildDir, err = ioutil.TempDir("", "bazel_queryview_test")
@@ -87,6 +85,7 @@
         "soong_module_variant": attr.string(),
         "soong_module_deps": attr.label_list(providers = [SoongModuleInfo]),
         "arch_paths": attr.string_list(),
+        "arch_paths_exclude": attr.string_list(),
         # bazel_module start
 #         "label": attr.string(),
 #         "bp2build_available": attr.bool(),
@@ -116,6 +115,7 @@
         "soong_module_variant": attr.string(),
         "soong_module_deps": attr.label_list(providers = [SoongModuleInfo]),
         "arch_paths": attr.string_list(),
+        "arch_paths_exclude": attr.string_list(),
         "bool_prop": attr.bool(),
         "bool_ptr_prop": attr.bool(),
         "int64_ptr_prop": attr.int(),
@@ -141,6 +141,7 @@
         "soong_module_variant": attr.string(),
         "soong_module_deps": attr.label_list(providers = [SoongModuleInfo]),
         "arch_paths": attr.string_list(),
+        "arch_paths_exclude": attr.string_list(),
         "bool_prop": attr.bool(),
         "bool_ptr_prop": attr.bool(),
         "int64_ptr_prop": attr.int(),
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index bf40a6b..c840016 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -15,70 +15,72 @@
 package bp2build
 
 import (
+	"testing"
+
 	"android/soong/android"
 	"android/soong/cc"
-	"strings"
-	"testing"
 )
 
 const (
 	// See cc/testing.go for more context
 	soongCcLibraryPreamble = `
 cc_defaults {
-	name: "linux_bionic_supported",
+  name: "linux_bionic_supported",
 }
 
 toolchain_library {
-	name: "libclang_rt.builtins-x86_64-android",
-	defaults: ["linux_bionic_supported"],
-	vendor_available: true,
-	vendor_ramdisk_available: true,
-	product_available: true,
-	recovery_available: true,
-	native_bridge_supported: true,
-	src: "",
+  name: "libclang_rt.builtins-x86_64-android",
+  defaults: ["linux_bionic_supported"],
+  vendor_available: true,
+  vendor_ramdisk_available: true,
+  product_available: true,
+  recovery_available: true,
+  native_bridge_supported: true,
+  src: "",
 }`
 )
 
-func TestCcLibraryBp2Build(t *testing.T) {
-	// b/191166471 disabled in sc-dev
-	t.Skip()
-	testCases := []struct {
-		description                        string
-		moduleTypeUnderTest                string
-		moduleTypeUnderTestFactory         android.ModuleFactory
-		moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext)
-		bp                                 string
-		expectedBazelTargets               []string
-		filesystem                         map[string]string
-		dir                                string
-		depsMutators                       []android.RegisterMutatorFunc
-	}{
-		{
-			description:                        "cc_library - simple example",
-			moduleTypeUnderTest:                "cc_library",
-			moduleTypeUnderTestFactory:         cc.LibraryFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
-			filesystem: map[string]string{
-				"android.cpp": "",
-				"darwin.cpp":  "",
-				// Refer to cc.headerExts for the supported header extensions in Soong.
-				"header.h":         "",
-				"header.hh":        "",
-				"header.hpp":       "",
-				"header.hxx":       "",
-				"header.h++":       "",
-				"header.inl":       "",
-				"header.inc":       "",
-				"header.ipp":       "",
-				"header.h.generic": "",
-				"impl.cpp":         "",
-				"linux.cpp":        "",
-				"x86.cpp":          "",
-				"x86_64.cpp":       "",
-				"foo-dir/a.h":      "",
-			},
-			bp: soongCcLibraryPreamble + `
+func runCcLibraryTestCase(t *testing.T, tc bp2buildTestCase) {
+	t.Helper()
+	runBp2BuildTestCase(t, registerCcLibraryModuleTypes, tc)
+}
+
+func registerCcLibraryModuleTypes(ctx android.RegistrationContext) {
+	cc.RegisterCCBuildComponents(ctx)
+	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
+	ctx.RegisterModuleType("cc_library_static", cc.LibraryStaticFactory)
+	ctx.RegisterModuleType("cc_prebuilt_library_static", cc.PrebuiltStaticLibraryFactory)
+	ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory)
+	ctx.RegisterModuleType("cc_library_headers", cc.LibraryHeaderFactory)
+}
+
+func TestCcLibrarySimple(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		description:                        "cc_library - simple example",
+		moduleTypeUnderTest:                "cc_library",
+		moduleTypeUnderTestFactory:         cc.LibraryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		filesystem: map[string]string{
+			"android.cpp": "",
+			"bionic.cpp":  "",
+			"darwin.cpp":  "",
+			// Refer to cc.headerExts for the supported header extensions in Soong.
+			"header.h":         "",
+			"header.hh":        "",
+			"header.hpp":       "",
+			"header.hxx":       "",
+			"header.h++":       "",
+			"header.inl":       "",
+			"header.inc":       "",
+			"header.ipp":       "",
+			"header.h.generic": "",
+			"impl.cpp":         "",
+			"linux.cpp":        "",
+			"x86.cpp":          "",
+			"x86_64.cpp":       "",
+			"foo-dir/a.h":      "",
+		},
+		blueprint: soongCcLibraryPreamble + `
 cc_library_headers { name: "some-headers" }
 cc_library {
     name: "foo-lib",
@@ -107,16 +109,20 @@
         darwin: {
             srcs: ["darwin.cpp"],
         },
+        bionic: {
+          srcs: ["bionic.cpp"]
+        },
     },
 }
 `,
-			expectedBazelTargets: []string{`cc_library(
+		expectedBazelTargets: []string{`cc_library(
     name = "foo-lib",
     copts = [
         "-Wall",
         "-I.",
+        "-I$(BINDIR)/.",
     ],
-    deps = [":some-headers"],
+    implementation_deps = [":some-headers"],
     includes = ["foo-dir"],
     linkopts = ["-Wl,--exclude-libs=bar.a"] + select({
         "//build/bazel/platforms/arch:x86": ["-Wl,--exclude-libs=baz.a"],
@@ -132,22 +138,27 @@
         "//build/bazel/platforms/os:darwin": ["darwin.cpp"],
         "//build/bazel/platforms/os:linux": ["linux.cpp"],
         "//conditions:default": [],
+    }) + select({
+        "//build/bazel/platforms/os:bionic": ["bionic.cpp"],
+        "//conditions:default": [],
     }),
-)`},
+)`}})
+}
+
+func TestCcLibraryTrimmedLdAndroid(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		description:                        "cc_library - trimmed example of //bionic/linker:ld-android",
+		moduleTypeUnderTest:                "cc_library",
+		moduleTypeUnderTestFactory:         cc.LibraryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		filesystem: map[string]string{
+			"ld-android.cpp":           "",
+			"linked_list.h":            "",
+			"linker.h":                 "",
+			"linker_block_allocator.h": "",
+			"linker_cfi.h":             "",
 		},
-		{
-			description:                        "cc_library - trimmed example of //bionic/linker:ld-android",
-			moduleTypeUnderTest:                "cc_library",
-			moduleTypeUnderTestFactory:         cc.LibraryFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
-			filesystem: map[string]string{
-				"ld-android.cpp":           "",
-				"linked_list.h":            "",
-				"linker.h":                 "",
-				"linker_block_allocator.h": "",
-				"linker_cfi.h":             "",
-			},
-			bp: soongCcLibraryPreamble + `
+		blueprint: soongCcLibraryPreamble + `
 cc_library_headers { name: "libc_headers" }
 cc_library {
     name: "fake-ld-android",
@@ -177,7 +188,7 @@
     },
 }
 `,
-			expectedBazelTargets: []string{`cc_library(
+		expectedBazelTargets: []string{`cc_library(
     name = "fake-ld-android",
     copts = [
         "-Wall",
@@ -185,8 +196,9 @@
         "-Wunused",
         "-Werror",
         "-I.",
+        "-I$(BINDIR)/.",
     ],
-    deps = [":libc_headers"],
+    implementation_deps = [":libc_headers"],
     linkopts = [
         "-Wl,--exclude-libs=libgcc.a",
         "-Wl,--exclude-libs=libgcc_stripped.a",
@@ -201,20 +213,23 @@
     }),
     srcs = ["ld_android.cpp"],
 )`},
-		},
-		{
-			description:                        "cc_library exclude_srcs - trimmed example of //external/arm-optimized-routines:libarm-optimized-routines-math",
-			moduleTypeUnderTest:                "cc_library",
-			moduleTypeUnderTestFactory:         cc.LibraryFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
-			dir:                                "external",
-			filesystem: map[string]string{
-				"external/math/cosf.c":      "",
-				"external/math/erf.c":       "",
-				"external/math/erf_data.c":  "",
-				"external/math/erff.c":      "",
-				"external/math/erff_data.c": "",
-				"external/Android.bp": `
+	})
+}
+
+func TestCcLibraryExcludeSrcs(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		description:                        "cc_library exclude_srcs - trimmed example of //external/arm-optimized-routines:libarm-optimized-routines-math",
+		moduleTypeUnderTest:                "cc_library",
+		moduleTypeUnderTestFactory:         cc.LibraryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		dir:                                "external",
+		filesystem: map[string]string{
+			"external/math/cosf.c":      "",
+			"external/math/erf.c":       "",
+			"external/math/erf_data.c":  "",
+			"external/math/erff.c":      "",
+			"external/math/erff_data.c": "",
+			"external/Android.bp": `
 cc_library {
     name: "fake-libarm-optimized-routines-math",
     exclude_srcs: [
@@ -240,29 +255,34 @@
     bazel_module: { bp2build_available: true },
 }
 `,
-			},
-			bp: soongCcLibraryPreamble,
-			expectedBazelTargets: []string{`cc_library(
+		},
+		blueprint: soongCcLibraryPreamble,
+		expectedBazelTargets: []string{`cc_library(
     name = "fake-libarm-optimized-routines-math",
-    copts = ["-Iexternal"] + select({
+    copts = [
+        "-Iexternal",
+        "-I$(BINDIR)/external",
+    ] + select({
         "//build/bazel/platforms/arch:arm64": ["-DHAVE_FAST_FMA=1"],
         "//conditions:default": [],
     }),
-    srcs = ["math/cosf.c"],
+    srcs_c = ["math/cosf.c"],
 )`},
-		},
-		{
-			description:                        "cc_library shared/static props",
-			moduleTypeUnderTest:                "cc_library",
-			moduleTypeUnderTestFactory:         cc.LibraryFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
-			depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
-			dir:                                "foo/bar",
-			filesystem: map[string]string{
-				"foo/bar/both.cpp":       "",
-				"foo/bar/sharedonly.cpp": "",
-				"foo/bar/staticonly.cpp": "",
-				"foo/bar/Android.bp": `
+	})
+}
+
+func TestCcLibrarySharedStaticProps(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		description:                        "cc_library shared/static props",
+		moduleTypeUnderTest:                "cc_library",
+		moduleTypeUnderTestFactory:         cc.LibraryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		dir:                                "foo/bar",
+		filesystem: map[string]string{
+			"foo/bar/both.cpp":       "",
+			"foo/bar/sharedonly.cpp": "",
+			"foo/bar/staticonly.cpp": "",
+			"foo/bar/Android.bp": `
 cc_library {
     name: "a",
     srcs: ["both.cpp"],
@@ -305,39 +325,369 @@
 
 cc_library { name: "shared_dep_for_both" }
 `,
-			},
-			bp: soongCcLibraryPreamble,
-			expectedBazelTargets: []string{`cc_library(
+		},
+		blueprint: soongCcLibraryPreamble,
+		expectedBazelTargets: []string{`cc_library(
     name = "a",
     copts = [
         "bothflag",
         "-Ifoo/bar",
+        "-I$(BINDIR)/foo/bar",
     ],
-    deps = [":static_dep_for_both"],
     dynamic_deps = [":shared_dep_for_both"],
-    dynamic_deps_for_shared = [":shared_dep_for_shared"],
-    dynamic_deps_for_static = [":shared_dep_for_static"],
-    shared_copts = ["sharedflag"],
-    shared_srcs = ["sharedonly.cpp"],
+    implementation_deps = [":static_dep_for_both"],
+    shared = {
+        "copts": ["sharedflag"],
+        "dynamic_deps": [":shared_dep_for_shared"],
+        "srcs": ["sharedonly.cpp"],
+        "static_deps": [":static_dep_for_shared"],
+        "whole_archive_deps": [":whole_static_lib_for_shared"],
+    },
     srcs = ["both.cpp"],
-    static_copts = ["staticflag"],
-    static_deps_for_shared = [":static_dep_for_shared"],
-    static_deps_for_static = [":static_dep_for_static"],
-    static_srcs = ["staticonly.cpp"],
+    static = {
+        "copts": ["staticflag"],
+        "dynamic_deps": [":shared_dep_for_static"],
+        "srcs": ["staticonly.cpp"],
+        "static_deps": [":static_dep_for_static"],
+        "whole_archive_deps": [":whole_static_lib_for_static"],
+    },
     whole_archive_deps = [":whole_static_lib_for_both"],
-    whole_archive_deps_for_shared = [":whole_static_lib_for_shared"],
-    whole_archive_deps_for_static = [":whole_static_lib_for_static"],
 )`},
+	})
+}
+
+func TestCcLibraryWholeStaticLibsAlwaysLink(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		moduleTypeUnderTest:                "cc_library",
+		moduleTypeUnderTestFactory:         cc.LibraryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		dir:                                "foo/bar",
+		filesystem: map[string]string{
+			"foo/bar/Android.bp": `
+cc_library {
+    name: "a",
+    whole_static_libs: ["whole_static_lib_for_both"],
+    static: {
+        whole_static_libs: ["whole_static_lib_for_static"],
+    },
+    shared: {
+        whole_static_libs: ["whole_static_lib_for_shared"],
+    },
+    bazel_module: { bp2build_available: true },
+}
+
+cc_prebuilt_library_static { name: "whole_static_lib_for_shared" }
+
+cc_prebuilt_library_static { name: "whole_static_lib_for_static" }
+
+cc_prebuilt_library_static { name: "whole_static_lib_for_both" }
+`,
 		},
-		{
-			description:                        "cc_library non-configured version script",
-			moduleTypeUnderTest:                "cc_library",
-			moduleTypeUnderTestFactory:         cc.LibraryFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
-			depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
-			dir:                                "foo/bar",
-			filesystem: map[string]string{
-				"foo/bar/Android.bp": `
+		blueprint: soongCcLibraryPreamble,
+		expectedBazelTargets: []string{`cc_library(
+    name = "a",
+    copts = [
+        "-Ifoo/bar",
+        "-I$(BINDIR)/foo/bar",
+    ],
+    shared = {
+        "whole_archive_deps": [":whole_static_lib_for_shared_alwayslink"],
+    },
+    static = {
+        "whole_archive_deps": [":whole_static_lib_for_static_alwayslink"],
+    },
+    whole_archive_deps = [":whole_static_lib_for_both_alwayslink"],
+)`},
+	})
+}
+
+func TestCcLibrarySharedStaticPropsInArch(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		description:                        "cc_library shared/static props in arch",
+		moduleTypeUnderTest:                "cc_library",
+		moduleTypeUnderTestFactory:         cc.LibraryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		dir:                                "foo/bar",
+		filesystem: map[string]string{
+			"foo/bar/arm.cpp":        "",
+			"foo/bar/x86.cpp":        "",
+			"foo/bar/sharedonly.cpp": "",
+			"foo/bar/staticonly.cpp": "",
+			"foo/bar/Android.bp": `
+cc_library {
+    name: "a",
+    arch: {
+        arm: {
+            shared: {
+                srcs: ["arm_shared.cpp"],
+                cflags: ["-DARM_SHARED"],
+                static_libs: ["arm_static_dep_for_shared"],
+                whole_static_libs: ["arm_whole_static_dep_for_shared"],
+                shared_libs: ["arm_shared_dep_for_shared"],
+            },
+        },
+        x86: {
+            static: {
+                srcs: ["x86_static.cpp"],
+                cflags: ["-DX86_STATIC"],
+                static_libs: ["x86_dep_for_static"],
+            },
+        },
+    },
+    target: {
+        android: {
+            shared: {
+                srcs: ["android_shared.cpp"],
+                cflags: ["-DANDROID_SHARED"],
+                static_libs: ["android_dep_for_shared"],
+            },
+        },
+        android_arm: {
+            shared: {
+                cflags: ["-DANDROID_ARM_SHARED"],
+            },
+        },
+    },
+    srcs: ["both.cpp"],
+    cflags: ["bothflag"],
+    static_libs: ["static_dep_for_both"],
+    static: {
+        srcs: ["staticonly.cpp"],
+        cflags: ["staticflag"],
+        static_libs: ["static_dep_for_static"],
+    },
+    shared: {
+        srcs: ["sharedonly.cpp"],
+        cflags: ["sharedflag"],
+        static_libs: ["static_dep_for_shared"],
+    },
+    bazel_module: { bp2build_available: true },
+}
+
+cc_library_static { name: "static_dep_for_shared" }
+cc_library_static { name: "static_dep_for_static" }
+cc_library_static { name: "static_dep_for_both" }
+
+cc_library_static { name: "arm_static_dep_for_shared" }
+cc_library_static { name: "arm_whole_static_dep_for_shared" }
+cc_library_static { name: "arm_shared_dep_for_shared" }
+
+cc_library_static { name: "x86_dep_for_static" }
+
+cc_library_static { name: "android_dep_for_shared" }
+`,
+		},
+		blueprint: soongCcLibraryPreamble,
+		expectedBazelTargets: []string{`cc_library(
+    name = "a",
+    copts = [
+        "bothflag",
+        "-Ifoo/bar",
+        "-I$(BINDIR)/foo/bar",
+    ],
+    implementation_deps = [":static_dep_for_both"],
+    shared = {
+        "copts": ["sharedflag"] + select({
+            "//build/bazel/platforms/arch:arm": ["-DARM_SHARED"],
+            "//conditions:default": [],
+        }) + select({
+            "//build/bazel/platforms/os:android": ["-DANDROID_SHARED"],
+            "//conditions:default": [],
+        }) + select({
+            "//build/bazel/platforms/os_arch:android_arm": ["-DANDROID_ARM_SHARED"],
+            "//conditions:default": [],
+        }),
+        "dynamic_deps": select({
+            "//build/bazel/platforms/arch:arm": [":arm_shared_dep_for_shared"],
+            "//conditions:default": [],
+        }),
+        "srcs": ["sharedonly.cpp"] + select({
+            "//build/bazel/platforms/arch:arm": ["arm_shared.cpp"],
+            "//conditions:default": [],
+        }) + select({
+            "//build/bazel/platforms/os:android": ["android_shared.cpp"],
+            "//conditions:default": [],
+        }),
+        "static_deps": [":static_dep_for_shared"] + select({
+            "//build/bazel/platforms/arch:arm": [":arm_static_dep_for_shared"],
+            "//conditions:default": [],
+        }) + select({
+            "//build/bazel/platforms/os:android": [":android_dep_for_shared"],
+            "//conditions:default": [],
+        }),
+        "whole_archive_deps": select({
+            "//build/bazel/platforms/arch:arm": [":arm_whole_static_dep_for_shared"],
+            "//conditions:default": [],
+        }),
+    },
+    srcs = ["both.cpp"],
+    static = {
+        "copts": ["staticflag"] + select({
+            "//build/bazel/platforms/arch:x86": ["-DX86_STATIC"],
+            "//conditions:default": [],
+        }),
+        "srcs": ["staticonly.cpp"] + select({
+            "//build/bazel/platforms/arch:x86": ["x86_static.cpp"],
+            "//conditions:default": [],
+        }),
+        "static_deps": [":static_dep_for_static"] + select({
+            "//build/bazel/platforms/arch:x86": [":x86_dep_for_static"],
+            "//conditions:default": [],
+        }),
+    },
+)`},
+	})
+}
+
+func TestCcLibrarySharedStaticPropsWithMixedSources(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		description:                        "cc_library shared/static props with c/cpp/s mixed sources",
+		moduleTypeUnderTest:                "cc_library",
+		moduleTypeUnderTestFactory:         cc.LibraryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		dir:                                "foo/bar",
+		filesystem: map[string]string{
+			"foo/bar/both_source.cpp":   "",
+			"foo/bar/both_source.cc":    "",
+			"foo/bar/both_source.c":     "",
+			"foo/bar/both_source.s":     "",
+			"foo/bar/both_source.S":     "",
+			"foo/bar/shared_source.cpp": "",
+			"foo/bar/shared_source.cc":  "",
+			"foo/bar/shared_source.c":   "",
+			"foo/bar/shared_source.s":   "",
+			"foo/bar/shared_source.S":   "",
+			"foo/bar/static_source.cpp": "",
+			"foo/bar/static_source.cc":  "",
+			"foo/bar/static_source.c":   "",
+			"foo/bar/static_source.s":   "",
+			"foo/bar/static_source.S":   "",
+			"foo/bar/Android.bp": `
+cc_library {
+    name: "a",
+    srcs: [
+    "both_source.cpp",
+    "both_source.cc",
+    "both_source.c",
+    "both_source.s",
+    "both_source.S",
+        ":both_filegroup",
+  ],
+    static: {
+    srcs: [
+      "static_source.cpp",
+      "static_source.cc",
+      "static_source.c",
+      "static_source.s",
+      "static_source.S",
+      ":static_filegroup",
+    ],
+    },
+    shared: {
+    srcs: [
+      "shared_source.cpp",
+      "shared_source.cc",
+      "shared_source.c",
+      "shared_source.s",
+      "shared_source.S",
+      ":shared_filegroup",
+    ],
+    },
+    bazel_module: { bp2build_available: true },
+}
+
+filegroup {
+    name: "both_filegroup",
+    srcs: [
+        // Not relevant, handled by filegroup macro
+  ],
+}
+
+filegroup {
+    name: "shared_filegroup",
+    srcs: [
+        // Not relevant, handled by filegroup macro
+  ],
+}
+
+filegroup {
+    name: "static_filegroup",
+    srcs: [
+        // Not relevant, handled by filegroup macro
+  ],
+}
+`,
+		},
+		blueprint: soongCcLibraryPreamble,
+		expectedBazelTargets: []string{`cc_library(
+    name = "a",
+    asflags = [
+        "-Ifoo/bar",
+        "-I$(BINDIR)/foo/bar",
+    ],
+    copts = [
+        "-Ifoo/bar",
+        "-I$(BINDIR)/foo/bar",
+    ],
+    shared = {
+        "srcs": [
+            ":shared_filegroup_cpp_srcs",
+            "shared_source.cc",
+            "shared_source.cpp",
+        ],
+        "srcs_as": [
+            "shared_source.s",
+            "shared_source.S",
+            ":shared_filegroup_as_srcs",
+        ],
+        "srcs_c": [
+            "shared_source.c",
+            ":shared_filegroup_c_srcs",
+        ],
+    },
+    srcs = [
+        ":both_filegroup_cpp_srcs",
+        "both_source.cc",
+        "both_source.cpp",
+    ],
+    srcs_as = [
+        "both_source.s",
+        "both_source.S",
+        ":both_filegroup_as_srcs",
+    ],
+    srcs_c = [
+        "both_source.c",
+        ":both_filegroup_c_srcs",
+    ],
+    static = {
+        "srcs": [
+            ":static_filegroup_cpp_srcs",
+            "static_source.cc",
+            "static_source.cpp",
+        ],
+        "srcs_as": [
+            "static_source.s",
+            "static_source.S",
+            ":static_filegroup_as_srcs",
+        ],
+        "srcs_c": [
+            "static_source.c",
+            ":static_filegroup_c_srcs",
+        ],
+    },
+)`},
+	})
+}
+
+func TestCcLibraryNonConfiguredVersionScript(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		description:                        "cc_library non-configured version script",
+		moduleTypeUnderTest:                "cc_library",
+		moduleTypeUnderTestFactory:         cc.LibraryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		dir:                                "foo/bar",
+		filesystem: map[string]string{
+			"foo/bar/Android.bp": `
 cc_library {
     name: "a",
     srcs: ["a.cpp"],
@@ -345,44 +695,52 @@
     bazel_module: { bp2build_available: true },
 }
 `,
-			},
-			bp: soongCcLibraryPreamble,
-			expectedBazelTargets: []string{`cc_library(
+		},
+		blueprint: soongCcLibraryPreamble,
+		expectedBazelTargets: []string{`cc_library(
     name = "a",
-    copts = ["-Ifoo/bar"],
+    copts = [
+        "-Ifoo/bar",
+        "-I$(BINDIR)/foo/bar",
+    ],
     srcs = ["a.cpp"],
     version_script = "v.map",
 )`},
-		},
-		{
-			description:                        "cc_library configured version script",
-			moduleTypeUnderTest:                "cc_library",
-			moduleTypeUnderTestFactory:         cc.LibraryFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
-			depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
-			dir:                                "foo/bar",
-			filesystem: map[string]string{
-				"foo/bar/Android.bp": `
-		cc_library {
-		   name: "a",
-		   srcs: ["a.cpp"],
-		   arch: {
-		     arm: {
-		       version_script: "arm.map",
-		     },
-		     arm64: {
-		       version_script: "arm64.map",
-		     },
-		   },
+	})
+}
 
-		   bazel_module: { bp2build_available: true },
-		}
-		`,
-			},
-			bp: soongCcLibraryPreamble,
-			expectedBazelTargets: []string{`cc_library(
+func TestCcLibraryConfiguredVersionScript(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		description:                        "cc_library configured version script",
+		moduleTypeUnderTest:                "cc_library",
+		moduleTypeUnderTestFactory:         cc.LibraryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		dir:                                "foo/bar",
+		filesystem: map[string]string{
+			"foo/bar/Android.bp": `
+    cc_library {
+       name: "a",
+       srcs: ["a.cpp"],
+       arch: {
+         arm: {
+           version_script: "arm.map",
+         },
+         arm64: {
+           version_script: "arm64.map",
+         },
+       },
+
+       bazel_module: { bp2build_available: true },
+    }
+    `,
+		},
+		blueprint: soongCcLibraryPreamble,
+		expectedBazelTargets: []string{`cc_library(
     name = "a",
-    copts = ["-Ifoo/bar"],
+    copts = [
+        "-Ifoo/bar",
+        "-I$(BINDIR)/foo/bar",
+    ],
     srcs = ["a.cpp"],
     version_script = select({
         "//build/bazel/platforms/arch:arm": "arm.map",
@@ -390,16 +748,18 @@
         "//conditions:default": None,
     }),
 )`},
-		},
-		{
-			description:                        "cc_library shared_libs",
-			moduleTypeUnderTest:                "cc_library",
-			moduleTypeUnderTestFactory:         cc.LibraryFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
-			depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
-			dir:                                "foo/bar",
-			filesystem: map[string]string{
-				"foo/bar/Android.bp": `
+	})
+}
+
+func TestCcLibrarySharedLibs(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		description:                        "cc_library shared_libs",
+		moduleTypeUnderTest:                "cc_library",
+		moduleTypeUnderTestFactory:         cc.LibraryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		dir:                                "foo/bar",
+		filesystem: map[string]string{
+			"foo/bar/Android.bp": `
 cc_library {
     name: "mylib",
     bazel_module: { bp2build_available: true },
@@ -411,26 +771,34 @@
     bazel_module: { bp2build_available: true },
 }
 `,
-			},
-			bp: soongCcLibraryPreamble,
-			expectedBazelTargets: []string{`cc_library(
+		},
+		blueprint: soongCcLibraryPreamble,
+		expectedBazelTargets: []string{`cc_library(
     name = "a",
-    copts = ["-Ifoo/bar"],
+    copts = [
+        "-Ifoo/bar",
+        "-I$(BINDIR)/foo/bar",
+    ],
     dynamic_deps = [":mylib"],
 )`, `cc_library(
     name = "mylib",
-    copts = ["-Ifoo/bar"],
+    copts = [
+        "-Ifoo/bar",
+        "-I$(BINDIR)/foo/bar",
+    ],
 )`},
-		},
-		{
-			description:                        "cc_library pack_relocations test",
-			moduleTypeUnderTest:                "cc_library",
-			moduleTypeUnderTestFactory:         cc.LibraryFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
-			depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
-			dir:                                "foo/bar",
-			filesystem: map[string]string{
-				"foo/bar/Android.bp": `
+	})
+}
+
+func TestCcLibraryPackRelocations(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		description:                        "cc_library pack_relocations test",
+		moduleTypeUnderTest:                "cc_library",
+		moduleTypeUnderTestFactory:         cc.LibraryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		dir:                                "foo/bar",
+		filesystem: map[string]string{
+			"foo/bar/Android.bp": `
 cc_library {
     name: "a",
     srcs: ["a.cpp"],
@@ -443,8 +811,8 @@
     srcs: ["b.cpp"],
     arch: {
         x86_64: {
-		pack_relocations: false,
-	},
+    pack_relocations: false,
+  },
     },
     bazel_module: { bp2build_available: true },
 }
@@ -454,21 +822,27 @@
     srcs: ["c.cpp"],
     target: {
         darwin: {
-		pack_relocations: false,
-	},
+    pack_relocations: false,
+  },
     },
     bazel_module: { bp2build_available: true },
 }`,
-			},
-			bp: soongCcLibraryPreamble,
-			expectedBazelTargets: []string{`cc_library(
+		},
+		blueprint: soongCcLibraryPreamble,
+		expectedBazelTargets: []string{`cc_library(
     name = "a",
-    copts = ["-Ifoo/bar"],
+    copts = [
+        "-Ifoo/bar",
+        "-I$(BINDIR)/foo/bar",
+    ],
     linkopts = ["-Wl,--pack-dyn-relocs=none"],
     srcs = ["a.cpp"],
 )`, `cc_library(
     name = "b",
-    copts = ["-Ifoo/bar"],
+    copts = [
+        "-Ifoo/bar",
+        "-I$(BINDIR)/foo/bar",
+    ],
     linkopts = select({
         "//build/bazel/platforms/arch:x86_64": ["-Wl,--pack-dyn-relocs=none"],
         "//conditions:default": [],
@@ -476,80 +850,91 @@
     srcs = ["b.cpp"],
 )`, `cc_library(
     name = "c",
-    copts = ["-Ifoo/bar"],
+    copts = [
+        "-Ifoo/bar",
+        "-I$(BINDIR)/foo/bar",
+    ],
     linkopts = select({
         "//build/bazel/platforms/os:darwin": ["-Wl,--pack-dyn-relocs=none"],
         "//conditions:default": [],
     }),
     srcs = ["c.cpp"],
 )`},
-		},
-		{
-			description:                        "cc_library spaces in copts",
-			moduleTypeUnderTest:                "cc_library",
-			moduleTypeUnderTestFactory:         cc.LibraryFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
-			depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
-			dir:                                "foo/bar",
-			filesystem: map[string]string{
-				"foo/bar/Android.bp": `
+	})
+}
+
+func TestCcLibrarySpacesInCopts(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		description:                        "cc_library spaces in copts",
+		moduleTypeUnderTest:                "cc_library",
+		moduleTypeUnderTestFactory:         cc.LibraryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		dir:                                "foo/bar",
+		filesystem: map[string]string{
+			"foo/bar/Android.bp": `
 cc_library {
     name: "a",
     cflags: ["-include header.h",],
     bazel_module: { bp2build_available: true },
 }
 `,
-			},
-			bp: soongCcLibraryPreamble,
-			expectedBazelTargets: []string{`cc_library(
+		},
+		blueprint: soongCcLibraryPreamble,
+		expectedBazelTargets: []string{`cc_library(
     name = "a",
     copts = [
         "-include",
         "header.h",
         "-Ifoo/bar",
+        "-I$(BINDIR)/foo/bar",
     ],
 )`},
-		},
-		{
-			description:                        "cc_library cppflags goes into copts",
-			moduleTypeUnderTest:                "cc_library",
-			moduleTypeUnderTestFactory:         cc.LibraryFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
-			depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
-			dir:                                "foo/bar",
-			filesystem: map[string]string{
-				"foo/bar/Android.bp": `cc_library {
+	})
+}
+
+func TestCcLibraryCppFlagsGoesIntoCopts(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		description:                        "cc_library cppflags usage",
+		moduleTypeUnderTest:                "cc_library",
+		moduleTypeUnderTestFactory:         cc.LibraryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		dir:                                "foo/bar",
+		filesystem: map[string]string{
+			"foo/bar/Android.bp": `cc_library {
     name: "a",
     srcs: ["a.cpp"],
     cflags: [
-		"-Wall",
-	],
+    "-Wall",
+  ],
     cppflags: [
         "-fsigned-char",
         "-pedantic",
-	],
+  ],
     arch: {
         arm64: {
             cppflags: ["-DARM64=1"],
-		},
-	},
+    },
+  },
     target: {
         android: {
             cppflags: ["-DANDROID=1"],
-		},
-	},
+    },
+  },
     bazel_module: { bp2build_available: true  },
 }
 `,
-			},
-			bp: soongCcLibraryPreamble,
-			expectedBazelTargets: []string{`cc_library(
+		},
+		blueprint: soongCcLibraryPreamble,
+		expectedBazelTargets: []string{`cc_library(
     name = "a",
     copts = [
         "-Wall",
+        "-Ifoo/bar",
+        "-I$(BINDIR)/foo/bar",
+    ],
+    cppflags = [
         "-fsigned-char",
         "-pedantic",
-        "-Ifoo/bar",
     ] + select({
         "//build/bazel/platforms/arch:arm64": ["-DARM64=1"],
         "//conditions:default": [],
@@ -559,64 +944,670 @@
     }),
     srcs = ["a.cpp"],
 )`},
+	})
+}
+
+func TestCcLibraryLabelAttributeGetTargetProperties(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		description:                        "cc_library GetTargetProperties on a LabelAttribute",
+		moduleTypeUnderTest:                "cc_library",
+		moduleTypeUnderTestFactory:         cc.LibraryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		dir:                                "foo/bar",
+		filesystem: map[string]string{
+			"foo/bar/Android.bp": `
+    cc_library {
+       name: "a",
+       srcs: ["a.cpp"],
+       target: {
+         android_arm: {
+           version_script: "android_arm.map",
+         },
+         linux_bionic_arm64: {
+           version_script: "linux_bionic_arm64.map",
+         },
+       },
+
+       bazel_module: { bp2build_available: true },
+    }
+    `,
 		},
+		blueprint: soongCcLibraryPreamble,
+		expectedBazelTargets: []string{`cc_library(
+    name = "a",
+    copts = [
+        "-Ifoo/bar",
+        "-I$(BINDIR)/foo/bar",
+    ],
+    srcs = ["a.cpp"],
+    version_script = select({
+        "//build/bazel/platforms/os_arch:android_arm": "android_arm.map",
+        "//build/bazel/platforms/os_arch:linux_bionic_arm64": "linux_bionic_arm64.map",
+        "//conditions:default": None,
+    }),
+)`},
+	})
+}
+
+func TestCcLibraryExcludeLibs(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		moduleTypeUnderTest:                "cc_library",
+		moduleTypeUnderTestFactory:         cc.LibraryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		filesystem:                         map[string]string{},
+		blueprint: soongCcLibraryStaticPreamble + `
+cc_library {
+    name: "foo_static",
+    srcs: ["common.c"],
+    whole_static_libs: [
+        "arm_whole_static_lib_excludes",
+        "malloc_not_svelte_whole_static_lib_excludes"
+    ],
+    static_libs: [
+        "arm_static_lib_excludes",
+        "malloc_not_svelte_static_lib_excludes"
+    ],
+    shared_libs: [
+        "arm_shared_lib_excludes",
+    ],
+    arch: {
+        arm: {
+            exclude_shared_libs: [
+                 "arm_shared_lib_excludes",
+            ],
+            exclude_static_libs: [
+                "arm_static_lib_excludes",
+                "arm_whole_static_lib_excludes",
+            ],
+        },
+    },
+    product_variables: {
+        malloc_not_svelte: {
+            shared_libs: ["malloc_not_svelte_shared_lib"],
+            whole_static_libs: ["malloc_not_svelte_whole_static_lib"],
+            exclude_static_libs: [
+                "malloc_not_svelte_static_lib_excludes",
+                "malloc_not_svelte_whole_static_lib_excludes",
+            ],
+        },
+    },
+}
+
+cc_library {
+    name: "arm_whole_static_lib_excludes",
+    bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+    name: "malloc_not_svelte_whole_static_lib",
+    bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+    name: "malloc_not_svelte_whole_static_lib_excludes",
+    bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+    name: "arm_static_lib_excludes",
+    bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+    name: "malloc_not_svelte_static_lib_excludes",
+    bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+    name: "arm_shared_lib_excludes",
+    bazel_module: { bp2build_available: false },
+}
+
+cc_library {
+    name: "malloc_not_svelte_shared_lib",
+    bazel_module: { bp2build_available: false },
+}
+`,
+		expectedBazelTargets: []string{
+			`cc_library(
+    name = "foo_static",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    dynamic_deps = select({
+        "//build/bazel/platforms/arch:arm": [],
+        "//conditions:default": [":arm_shared_lib_excludes"],
+    }) + select({
+        "//build/bazel/product_variables:malloc_not_svelte": [":malloc_not_svelte_shared_lib"],
+        "//conditions:default": [],
+    }),
+    implementation_deps = select({
+        "//build/bazel/platforms/arch:arm": [],
+        "//conditions:default": [":arm_static_lib_excludes"],
+    }) + select({
+        "//build/bazel/product_variables:malloc_not_svelte": [],
+        "//conditions:default": [":malloc_not_svelte_static_lib_excludes"],
+    }),
+    srcs_c = ["common.c"],
+    whole_archive_deps = select({
+        "//build/bazel/platforms/arch:arm": [],
+        "//conditions:default": [":arm_whole_static_lib_excludes"],
+    }) + select({
+        "//build/bazel/product_variables:malloc_not_svelte": [":malloc_not_svelte_whole_static_lib"],
+        "//conditions:default": [":malloc_not_svelte_whole_static_lib_excludes"],
+    }),
+)`,
+		},
+	})
+}
+
+func TestCCLibraryNoCrtTrue(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		description:                        "cc_library - simple example",
+		moduleTypeUnderTest:                "cc_library",
+		moduleTypeUnderTestFactory:         cc.LibraryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		filesystem: map[string]string{
+			"impl.cpp": "",
+		},
+		blueprint: soongCcLibraryPreamble + `
+cc_library_headers { name: "some-headers" }
+cc_library {
+    name: "foo-lib",
+    srcs: ["impl.cpp"],
+    no_libcrt: true,
+}
+`,
+		expectedBazelTargets: []string{`cc_library(
+    name = "foo-lib",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    srcs = ["impl.cpp"],
+    use_libcrt = False,
+)`}})
+}
+
+func TestCCLibraryNoCrtFalse(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		moduleTypeUnderTest:                "cc_library",
+		moduleTypeUnderTestFactory:         cc.LibraryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		filesystem: map[string]string{
+			"impl.cpp": "",
+		},
+		blueprint: soongCcLibraryPreamble + `
+cc_library_headers { name: "some-headers" }
+cc_library {
+    name: "foo-lib",
+    srcs: ["impl.cpp"],
+    no_libcrt: false,
+}
+`,
+		expectedBazelTargets: []string{`cc_library(
+    name = "foo-lib",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    srcs = ["impl.cpp"],
+    use_libcrt = True,
+)`}})
+}
+
+func TestCCLibraryNoCrtArchVariant(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		moduleTypeUnderTest:                "cc_library",
+		moduleTypeUnderTestFactory:         cc.LibraryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		filesystem: map[string]string{
+			"impl.cpp": "",
+		},
+		blueprint: soongCcLibraryPreamble + `
+cc_library_headers { name: "some-headers" }
+cc_library {
+    name: "foo-lib",
+    srcs: ["impl.cpp"],
+    arch: {
+        arm: {
+            no_libcrt: true,
+        },
+        x86: {
+            no_libcrt: true,
+        },
+    },
+}
+`,
+		expectedBazelTargets: []string{`cc_library(
+    name = "foo-lib",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    srcs = ["impl.cpp"],
+    use_libcrt = select({
+        "//build/bazel/platforms/arch:arm": False,
+        "//build/bazel/platforms/arch:x86": False,
+        "//conditions:default": None,
+    }),
+)`}})
+}
+
+func TestCCLibraryNoCrtArchVariantWithDefault(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		moduleTypeUnderTest:                "cc_library",
+		moduleTypeUnderTestFactory:         cc.LibraryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		filesystem: map[string]string{
+			"impl.cpp": "",
+		},
+		blueprint: soongCcLibraryPreamble + `
+cc_library_headers { name: "some-headers" }
+cc_library {
+    name: "foo-lib",
+    srcs: ["impl.cpp"],
+    no_libcrt: false,
+    arch: {
+        arm: {
+            no_libcrt: true,
+        },
+        x86: {
+            no_libcrt: true,
+        },
+    },
+}
+`,
+		expectedBazelTargets: []string{`cc_library(
+    name = "foo-lib",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    srcs = ["impl.cpp"],
+    use_libcrt = select({
+        "//build/bazel/platforms/arch:arm": False,
+        "//build/bazel/platforms/arch:x86": False,
+        "//conditions:default": True,
+    }),
+)`}})
+}
+
+func TestCcLibraryStrip(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		description:                        "cc_library strip args",
+		moduleTypeUnderTest:                "cc_library",
+		moduleTypeUnderTestFactory:         cc.LibraryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		dir:                                "foo/bar",
+		filesystem: map[string]string{
+			"foo/bar/Android.bp": `
+cc_library {
+    name: "nothing",
+    bazel_module: { bp2build_available: true },
+}
+cc_library {
+    name: "keep_symbols",
+    bazel_module: { bp2build_available: true },
+    strip: {
+		keep_symbols: true,
 	}
-
-	dir := "."
-	for _, testCase := range testCases {
-		filesystem := make(map[string][]byte)
-		toParse := []string{
-			"Android.bp",
-		}
-		for f, content := range testCase.filesystem {
-			if strings.HasSuffix(f, "Android.bp") {
-				toParse = append(toParse, f)
-			}
-			filesystem[f] = []byte(content)
-		}
-		config := android.TestConfig(buildDir, nil, testCase.bp, filesystem)
-		ctx := android.NewTestContext(config)
-
-		cc.RegisterCCBuildComponents(ctx)
-		ctx.RegisterModuleType("cc_library_static", cc.LibraryStaticFactory)
-		ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory)
-		ctx.RegisterModuleType("cc_library_headers", cc.LibraryHeaderFactory)
-		ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
-		ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
-		ctx.RegisterBp2BuildConfig(bp2buildConfig) // TODO(jingwen): make this the default for all tests
-		for _, m := range testCase.depsMutators {
-			ctx.DepsBp2BuildMutators(m)
-		}
-		ctx.RegisterForBazelConversion()
-
-		_, errs := ctx.ParseFileList(dir, toParse)
-		if Errored(t, testCase.description, errs) {
-			continue
-		}
-		_, errs = ctx.ResolveDependencies(config)
-		if Errored(t, testCase.description, errs) {
-			continue
-		}
-
-		checkDir := dir
-		if testCase.dir != "" {
-			checkDir = testCase.dir
-		}
-		codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
-		bazelTargets := generateBazelTargetsForDir(codegenCtx, checkDir)
-		if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
-			t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount)
-		} else {
-			for i, target := range bazelTargets {
-				if w, g := testCase.expectedBazelTargets[i], target.content; w != g {
-					t.Errorf(
-						"%s: Expected generated Bazel target to be '%s', got '%s'",
-						testCase.description,
-						w,
-						g,
-					)
-				}
-			}
-		}
+}
+cc_library {
+    name: "keep_symbols_and_debug_frame",
+    bazel_module: { bp2build_available: true },
+    strip: {
+		keep_symbols_and_debug_frame: true,
 	}
 }
+cc_library {
+    name: "none",
+    bazel_module: { bp2build_available: true },
+    strip: {
+		none: true,
+	}
+}
+cc_library {
+    name: "keep_symbols_list",
+    bazel_module: { bp2build_available: true },
+    strip: {
+		keep_symbols_list: ["symbol"],
+	}
+}
+cc_library {
+    name: "all",
+    bazel_module: { bp2build_available: true },
+    strip: {
+		all: true,
+	}
+}
+`,
+		},
+		blueprint: soongCcLibraryPreamble,
+		expectedBazelTargets: []string{`cc_library(
+    name = "all",
+    copts = [
+        "-Ifoo/bar",
+        "-I$(BINDIR)/foo/bar",
+    ],
+    strip = {
+        "all": True,
+    },
+)`, `cc_library(
+    name = "keep_symbols",
+    copts = [
+        "-Ifoo/bar",
+        "-I$(BINDIR)/foo/bar",
+    ],
+    strip = {
+        "keep_symbols": True,
+    },
+)`, `cc_library(
+    name = "keep_symbols_and_debug_frame",
+    copts = [
+        "-Ifoo/bar",
+        "-I$(BINDIR)/foo/bar",
+    ],
+    strip = {
+        "keep_symbols_and_debug_frame": True,
+    },
+)`, `cc_library(
+    name = "keep_symbols_list",
+    copts = [
+        "-Ifoo/bar",
+        "-I$(BINDIR)/foo/bar",
+    ],
+    strip = {
+        "keep_symbols_list": ["symbol"],
+    },
+)`, `cc_library(
+    name = "none",
+    copts = [
+        "-Ifoo/bar",
+        "-I$(BINDIR)/foo/bar",
+    ],
+    strip = {
+        "none": True,
+    },
+)`, `cc_library(
+    name = "nothing",
+    copts = [
+        "-Ifoo/bar",
+        "-I$(BINDIR)/foo/bar",
+    ],
+)`},
+	})
+}
+
+func TestCcLibraryStripWithArch(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		description:                        "cc_library strip args",
+		moduleTypeUnderTest:                "cc_library",
+		moduleTypeUnderTestFactory:         cc.LibraryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		dir:                                "foo/bar",
+		filesystem: map[string]string{
+			"foo/bar/Android.bp": `
+cc_library {
+    name: "multi-arch",
+    bazel_module: { bp2build_available: true },
+    target: {
+        darwin: {
+            strip: {
+                keep_symbols_list: ["foo", "bar"]
+            }
+        },
+    },
+    arch: {
+        arm: {
+            strip: {
+                keep_symbols_and_debug_frame: true,
+            },
+        },
+        arm64: {
+            strip: {
+                keep_symbols: true,
+            },
+        },
+    }
+}
+`,
+		},
+		blueprint: soongCcLibraryPreamble,
+		expectedBazelTargets: []string{`cc_library(
+    name = "multi-arch",
+    copts = [
+        "-Ifoo/bar",
+        "-I$(BINDIR)/foo/bar",
+    ],
+    strip = {
+        "keep_symbols": select({
+            "//build/bazel/platforms/arch:arm64": True,
+            "//conditions:default": None,
+        }),
+        "keep_symbols_and_debug_frame": select({
+            "//build/bazel/platforms/arch:arm": True,
+            "//conditions:default": None,
+        }),
+        "keep_symbols_list": select({
+            "//build/bazel/platforms/os:darwin": [
+                "foo",
+                "bar",
+            ],
+            "//conditions:default": [],
+        }),
+    },
+)`},
+	})
+}
+
+func TestCcLibrary_SystemSharedLibsRootEmpty(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		description:                        "cc_library system_shared_libs empty at root",
+		moduleTypeUnderTest:                "cc_library",
+		moduleTypeUnderTestFactory:         cc.LibraryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		blueprint: soongCcLibraryPreamble + `
+cc_library {
+    name: "root_empty",
+	  system_shared_libs: [],
+}
+`,
+		expectedBazelTargets: []string{`cc_library(
+    name = "root_empty",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    system_dynamic_deps = [],
+)`},
+	})
+}
+
+func TestCcLibrary_SystemSharedLibsStaticEmpty(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		description:                        "cc_library system_shared_libs empty for static variant",
+		moduleTypeUnderTest:                "cc_library",
+		moduleTypeUnderTestFactory:         cc.LibraryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		blueprint: soongCcLibraryPreamble + `
+cc_library {
+    name: "static_empty",
+    static: {
+				system_shared_libs: [],
+		},
+}
+`,
+		expectedBazelTargets: []string{`cc_library(
+    name = "static_empty",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    static = {
+        "system_dynamic_deps": [],
+    },
+)`},
+	})
+}
+
+func TestCcLibrary_SystemSharedLibsSharedEmpty(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		description:                        "cc_library system_shared_libs empty for shared variant",
+		moduleTypeUnderTest:                "cc_library",
+		moduleTypeUnderTestFactory:         cc.LibraryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		blueprint: soongCcLibraryPreamble + `
+cc_library {
+    name: "shared_empty",
+    shared: {
+				system_shared_libs: [],
+		},
+}
+`,
+		expectedBazelTargets: []string{`cc_library(
+    name = "shared_empty",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    shared = {
+        "system_dynamic_deps": [],
+    },
+)`},
+	})
+}
+
+func TestCcLibrary_SystemSharedLibsSharedBionicEmpty(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		description:                        "cc_library system_shared_libs empty for shared, bionic variant",
+		moduleTypeUnderTest:                "cc_library",
+		moduleTypeUnderTestFactory:         cc.LibraryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		blueprint: soongCcLibraryPreamble + `
+cc_library {
+    name: "shared_empty",
+    target: {
+        bionic: {
+            shared: {
+                system_shared_libs: [],
+            }
+        }
+		},
+}
+`,
+		expectedBazelTargets: []string{`cc_library(
+    name = "shared_empty",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    shared = {
+        "system_dynamic_deps": [],
+    },
+)`},
+	})
+}
+
+func TestCcLibrary_SystemSharedLibsLinuxBionicEmpty(t *testing.T) {
+	// Note that this behavior is technically incorrect (it's a simplification).
+	// The correct behavior would be if bp2build wrote `system_dynamic_deps = []`
+	// only for linux_bionic, but `android` had `["libc", "libdl", "libm"].
+	// b/195791252 tracks the fix.
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		description:                        "cc_library system_shared_libs empty for linux_bionic variant",
+		moduleTypeUnderTest:                "cc_library",
+		moduleTypeUnderTestFactory:         cc.LibraryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		blueprint: soongCcLibraryPreamble + `
+cc_library {
+    name: "target_linux_bionic_empty",
+    target: {
+        linux_bionic: {
+            system_shared_libs: [],
+        },
+    },
+}
+`,
+		expectedBazelTargets: []string{`cc_library(
+    name = "target_linux_bionic_empty",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    system_dynamic_deps = [],
+)`},
+	})
+}
+
+func TestCcLibrary_SystemSharedLibsBionicEmpty(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		description:                        "cc_library system_shared_libs empty for bionic variant",
+		moduleTypeUnderTest:                "cc_library",
+		moduleTypeUnderTestFactory:         cc.LibraryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		blueprint: soongCcLibraryPreamble + `
+cc_library {
+    name: "target_bionic_empty",
+    target: {
+        bionic: {
+            system_shared_libs: [],
+        },
+    },
+}
+`,
+		expectedBazelTargets: []string{`cc_library(
+    name = "target_bionic_empty",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    system_dynamic_deps = [],
+)`},
+	})
+}
+
+func TestCcLibrary_SystemSharedLibsSharedAndRoot(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		description:                        "cc_library system_shared_libs set for shared and root",
+		moduleTypeUnderTest:                "cc_library",
+		moduleTypeUnderTestFactory:         cc.LibraryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		blueprint: soongCcLibraryPreamble + `
+cc_library {name: "libc"}
+cc_library {name: "libm"}
+
+cc_library {
+    name: "foo",
+    system_shared_libs: ["libc"],
+    shared: {
+				system_shared_libs: ["libm"],
+    },
+}
+`,
+		expectedBazelTargets: []string{`cc_library(
+    name = "foo",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    shared = {
+        "system_dynamic_deps": [":libm"],
+    },
+    system_dynamic_deps = [":libc"],
+)`, `cc_library(
+    name = "libc",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+)`, `cc_library(
+    name = "libm",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+)`},
+	})
+}
diff --git a/bp2build/cc_library_headers_conversion_test.go b/bp2build/cc_library_headers_conversion_test.go
index 0905aba..ea2c10a 100644
--- a/bp2build/cc_library_headers_conversion_test.go
+++ b/bp2build/cc_library_headers_conversion_test.go
@@ -15,10 +15,10 @@
 package bp2build
 
 import (
+	"testing"
+
 	"android/soong/android"
 	"android/soong/cc"
-	"strings"
-	"testing"
 )
 
 const (
@@ -64,41 +64,38 @@
 			t.Fatalf("Expected load statements to be %s, got %s", expected, actual)
 		}
 	}
-
 }
 
-func TestCcLibraryHeadersBp2Build(t *testing.T) {
-	testCases := []struct {
-		description                        string
-		moduleTypeUnderTest                string
-		moduleTypeUnderTestFactory         android.ModuleFactory
-		moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext)
-		preArchMutators                    []android.RegisterMutatorFunc
-		depsMutators                       []android.RegisterMutatorFunc
-		bp                                 string
-		expectedBazelTargets               []string
-		filesystem                         map[string]string
-		dir                                string
-	}{
-		{
-			description:                        "cc_library_headers test",
-			moduleTypeUnderTest:                "cc_library_headers",
-			moduleTypeUnderTestFactory:         cc.LibraryHeaderFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryHeadersBp2Build,
-			filesystem: map[string]string{
-				"lib-1/lib1a.h":                        "",
-				"lib-1/lib1b.h":                        "",
-				"lib-2/lib2a.h":                        "",
-				"lib-2/lib2b.h":                        "",
-				"dir-1/dir1a.h":                        "",
-				"dir-1/dir1b.h":                        "",
-				"dir-2/dir2a.h":                        "",
-				"dir-2/dir2b.h":                        "",
-				"arch_arm64_exported_include_dir/a.h":  "",
-				"arch_x86_exported_include_dir/b.h":    "",
-				"arch_x86_64_exported_include_dir/c.h": "",
-			},
-			bp: soongCcLibraryHeadersPreamble + `
+func registerCcLibraryHeadersModuleTypes(ctx android.RegistrationContext) {
+	cc.RegisterCCBuildComponents(ctx)
+	ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory)
+}
+
+func runCcLibraryHeadersTestCase(t *testing.T, tc bp2buildTestCase) {
+	t.Helper()
+	runBp2BuildTestCase(t, registerCcLibraryHeadersModuleTypes, tc)
+}
+
+func TestCcLibraryHeadersSimple(t *testing.T) {
+	runCcLibraryHeadersTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_headers test",
+		moduleTypeUnderTest:                "cc_library_headers",
+		moduleTypeUnderTestFactory:         cc.LibraryHeaderFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryHeadersBp2Build,
+		filesystem: map[string]string{
+			"lib-1/lib1a.h":                        "",
+			"lib-1/lib1b.h":                        "",
+			"lib-2/lib2a.h":                        "",
+			"lib-2/lib2b.h":                        "",
+			"dir-1/dir1a.h":                        "",
+			"dir-1/dir1b.h":                        "",
+			"dir-2/dir2a.h":                        "",
+			"dir-2/dir2b.h":                        "",
+			"arch_arm64_exported_include_dir/a.h":  "",
+			"arch_x86_exported_include_dir/b.h":    "",
+			"arch_x86_64_exported_include_dir/c.h": "",
+		},
+		blueprint: soongCcLibraryHeadersPreamble + `
 cc_library_headers {
     name: "lib-1",
     export_include_dirs: ["lib-1"],
@@ -129,10 +126,13 @@
 
     // TODO: Also support export_header_lib_headers
 }`,
-			expectedBazelTargets: []string{`cc_library_headers(
+		expectedBazelTargets: []string{`cc_library_headers(
     name = "foo_headers",
-    copts = ["-I."],
-    deps = [
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    implementation_deps = [
         ":lib-1",
         ":lib-2",
     ],
@@ -147,26 +147,33 @@
     }),
 )`, `cc_library_headers(
     name = "lib-1",
-    copts = ["-I."],
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
     includes = ["lib-1"],
 )`, `cc_library_headers(
     name = "lib-2",
-    copts = ["-I."],
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
     includes = ["lib-2"],
 )`},
-		},
-		{
-			description:                        "cc_library_headers test with os-specific header_libs props",
-			moduleTypeUnderTest:                "cc_library_headers",
-			moduleTypeUnderTestFactory:         cc.LibraryHeaderFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryHeadersBp2Build,
-			depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
-			filesystem:                         map[string]string{},
-			bp: soongCcLibraryPreamble + `
+	})
+}
+
+func TestCcLibraryHeadersOSSpecificHeader(t *testing.T) {
+	runCcLibraryHeadersTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_headers test with os-specific header_libs props",
+		moduleTypeUnderTest:                "cc_library_headers",
+		moduleTypeUnderTestFactory:         cc.LibraryHeaderFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryHeadersBp2Build,
+		filesystem:                         map[string]string{},
+		blueprint: soongCcLibraryPreamble + `
 cc_library_headers { name: "android-lib" }
 cc_library_headers { name: "base-lib" }
 cc_library_headers { name: "darwin-lib" }
-cc_library_headers { name: "fuchsia-lib" }
 cc_library_headers { name: "linux-lib" }
 cc_library_headers { name: "linux_bionic-lib" }
 cc_library_headers { name: "windows-lib" }
@@ -176,56 +183,74 @@
     target: {
         android: { header_libs: ["android-lib"] },
         darwin: { header_libs: ["darwin-lib"] },
-        fuchsia: { header_libs: ["fuchsia-lib"] },
         linux_bionic: { header_libs: ["linux_bionic-lib"] },
         linux_glibc: { header_libs: ["linux-lib"] },
         windows: { header_libs: ["windows-lib"] },
     },
     bazel_module: { bp2build_available: true },
 }`,
-			expectedBazelTargets: []string{`cc_library_headers(
+		expectedBazelTargets: []string{`cc_library_headers(
     name = "android-lib",
-    copts = ["-I."],
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
 )`, `cc_library_headers(
     name = "base-lib",
-    copts = ["-I."],
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
 )`, `cc_library_headers(
     name = "darwin-lib",
-    copts = ["-I."],
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
 )`, `cc_library_headers(
     name = "foo_headers",
-    copts = ["-I."],
-    deps = [":base-lib"] + select({
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    implementation_deps = [":base-lib"] + select({
         "//build/bazel/platforms/os:android": [":android-lib"],
         "//build/bazel/platforms/os:darwin": [":darwin-lib"],
-        "//build/bazel/platforms/os:fuchsia": [":fuchsia-lib"],
         "//build/bazel/platforms/os:linux": [":linux-lib"],
         "//build/bazel/platforms/os:linux_bionic": [":linux_bionic-lib"],
         "//build/bazel/platforms/os:windows": [":windows-lib"],
         "//conditions:default": [],
     }),
 )`, `cc_library_headers(
-    name = "fuchsia-lib",
-    copts = ["-I."],
-)`, `cc_library_headers(
     name = "linux-lib",
-    copts = ["-I."],
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
 )`, `cc_library_headers(
     name = "linux_bionic-lib",
-    copts = ["-I."],
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
 )`, `cc_library_headers(
     name = "windows-lib",
-    copts = ["-I."],
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
 )`},
-		},
-		{
-			description:                        "cc_library_headers test with os-specific header_libs and export_header_lib_headers props",
-			moduleTypeUnderTest:                "cc_library_headers",
-			moduleTypeUnderTestFactory:         cc.LibraryHeaderFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryHeadersBp2Build,
-			depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
-			filesystem:                         map[string]string{},
-			bp: soongCcLibraryPreamble + `
+	})
+}
+
+func TestCcLibraryHeadersOsSpecficHeaderLibsExportHeaderLibHeaders(t *testing.T) {
+	runCcLibraryHeadersTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_headers test with os-specific header_libs and export_header_lib_headers props",
+		moduleTypeUnderTest:                "cc_library_headers",
+		moduleTypeUnderTestFactory:         cc.LibraryHeaderFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryHeadersBp2Build,
+		filesystem:                         map[string]string{},
+		blueprint: soongCcLibraryPreamble + `
 cc_library_headers { name: "android-lib" }
 cc_library_headers { name: "exported-lib" }
 cc_library_headers {
@@ -234,32 +259,44 @@
         android: { header_libs: ["android-lib"], export_header_lib_headers: ["exported-lib"] },
     },
 }`,
-			expectedBazelTargets: []string{`cc_library_headers(
+		expectedBazelTargets: []string{`cc_library_headers(
     name = "android-lib",
-    copts = ["-I."],
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
 )`, `cc_library_headers(
     name = "exported-lib",
-    copts = ["-I."],
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
 )`, `cc_library_headers(
     name = "foo_headers",
-    copts = ["-I."],
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
     deps = select({
-        "//build/bazel/platforms/os:android": [
-            ":android-lib",
-            ":exported-lib",
-        ],
+        "//build/bazel/platforms/os:android": [":exported-lib"],
+        "//conditions:default": [],
+    }),
+    implementation_deps = select({
+        "//build/bazel/platforms/os:android": [":android-lib"],
         "//conditions:default": [],
     }),
 )`},
-		},
-		{
-			description:                        "cc_library_headers test with arch-specific and target-specific export_system_include_dirs props",
-			moduleTypeUnderTest:                "cc_library_headers",
-			moduleTypeUnderTestFactory:         cc.LibraryHeaderFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryHeadersBp2Build,
-			depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
-			filesystem:                         map[string]string{},
-			bp: soongCcLibraryPreamble + `cc_library_headers {
+	})
+}
+
+func TestCcLibraryHeadersArchAndTargetExportSystemIncludes(t *testing.T) {
+	runCcLibraryHeadersTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_headers test with arch-specific and target-specific export_system_include_dirs props",
+		moduleTypeUnderTest:                "cc_library_headers",
+		moduleTypeUnderTestFactory:         cc.LibraryHeaderFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryHeadersBp2Build,
+		filesystem:                         map[string]string{},
+		blueprint: soongCcLibraryPreamble + `cc_library_headers {
     name: "foo_headers",
     export_system_include_dirs: [
 	"shared_include_dir",
@@ -294,9 +331,12 @@
         },
     },
 }`,
-			expectedBazelTargets: []string{`cc_library_headers(
+		expectedBazelTargets: []string{`cc_library_headers(
     name = "foo_headers",
-    copts = ["-I."],
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
     includes = ["shared_include_dir"] + select({
         "//build/bazel/platforms/arch:arm": ["arm_include_dir"],
         "//build/bazel/platforms/arch:x86_64": ["x86_64_include_dir"],
@@ -308,65 +348,41 @@
         "//conditions:default": [],
     }),
 )`},
+	})
+}
+
+func TestCcLibraryHeadersNoCrtIgnored(t *testing.T) {
+	runCcLibraryHeadersTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_headers test",
+		moduleTypeUnderTest:                "cc_library_headers",
+		moduleTypeUnderTestFactory:         cc.LibraryHeaderFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryHeadersBp2Build,
+		filesystem: map[string]string{
+			"lib-1/lib1a.h":                        "",
+			"lib-1/lib1b.h":                        "",
+			"lib-2/lib2a.h":                        "",
+			"lib-2/lib2b.h":                        "",
+			"dir-1/dir1a.h":                        "",
+			"dir-1/dir1b.h":                        "",
+			"dir-2/dir2a.h":                        "",
+			"dir-2/dir2b.h":                        "",
+			"arch_arm64_exported_include_dir/a.h":  "",
+			"arch_x86_exported_include_dir/b.h":    "",
+			"arch_x86_64_exported_include_dir/c.h": "",
 		},
-	}
-
-	dir := "."
-	for _, testCase := range testCases {
-		filesystem := make(map[string][]byte)
-		toParse := []string{
-			"Android.bp",
-		}
-		for f, content := range testCase.filesystem {
-			if strings.HasSuffix(f, "Android.bp") {
-				toParse = append(toParse, f)
-			}
-			filesystem[f] = []byte(content)
-		}
-		config := android.TestConfig(buildDir, nil, testCase.bp, filesystem)
-		ctx := android.NewTestContext(config)
-
-		// TODO(jingwen): make this default for all bp2build tests
-		ctx.RegisterBp2BuildConfig(bp2buildConfig)
-
-		cc.RegisterCCBuildComponents(ctx)
-		ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory)
-
-		ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
-		for _, m := range testCase.depsMutators {
-			ctx.DepsBp2BuildMutators(m)
-		}
-		ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
-		ctx.RegisterForBazelConversion()
-
-		_, errs := ctx.ParseFileList(dir, toParse)
-		if Errored(t, testCase.description, errs) {
-			continue
-		}
-		_, errs = ctx.ResolveDependencies(config)
-		if Errored(t, testCase.description, errs) {
-			continue
-		}
-
-		checkDir := dir
-		if testCase.dir != "" {
-			checkDir = testCase.dir
-		}
-		codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
-		bazelTargets := generateBazelTargetsForDir(codegenCtx, checkDir)
-		if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
-			t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount)
-		} else {
-			for i, target := range bazelTargets {
-				if w, g := testCase.expectedBazelTargets[i], target.content; w != g {
-					t.Errorf(
-						"%s: Expected generated Bazel target to be '%s', got '%s'",
-						testCase.description,
-						w,
-						g,
-					)
-				}
-			}
-		}
-	}
+		blueprint: soongCcLibraryHeadersPreamble + `
+cc_library_headers {
+    name: "lib-1",
+    export_include_dirs: ["lib-1"],
+    no_libcrt: true,
+}`,
+		expectedBazelTargets: []string{`cc_library_headers(
+    name = "lib-1",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    includes = ["lib-1"],
+)`},
+	})
 }
diff --git a/bp2build/cc_library_static_conversion_test.go b/bp2build/cc_library_static_conversion_test.go
index d082db1..d9145f6 100644
--- a/bp2build/cc_library_static_conversion_test.go
+++ b/bp2build/cc_library_static_conversion_test.go
@@ -17,7 +17,8 @@
 import (
 	"android/soong/android"
 	"android/soong/cc"
-	"strings"
+	"android/soong/genrule"
+
 	"testing"
 )
 
@@ -67,45 +68,47 @@
 
 }
 
-func TestCcLibraryStaticBp2Build(t *testing.T) {
-	testCases := []struct {
-		description                        string
-		moduleTypeUnderTest                string
-		moduleTypeUnderTestFactory         android.ModuleFactory
-		moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext)
-		preArchMutators                    []android.RegisterMutatorFunc
-		depsMutators                       []android.RegisterMutatorFunc
-		bp                                 string
-		expectedBazelTargets               []string
-		filesystem                         map[string]string
-		dir                                string
-	}{
-		{
-			description:                        "cc_library_static test",
-			moduleTypeUnderTest:                "cc_library_static",
-			moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
-			filesystem: map[string]string{
-				// NOTE: include_dir headers *should not* appear in Bazel hdrs later (?)
-				"include_dir_1/include_dir_1_a.h": "",
-				"include_dir_1/include_dir_1_b.h": "",
-				"include_dir_2/include_dir_2_a.h": "",
-				"include_dir_2/include_dir_2_b.h": "",
-				// NOTE: local_include_dir headers *should not* appear in Bazel hdrs later (?)
-				"local_include_dir_1/local_include_dir_1_a.h": "",
-				"local_include_dir_1/local_include_dir_1_b.h": "",
-				"local_include_dir_2/local_include_dir_2_a.h": "",
-				"local_include_dir_2/local_include_dir_2_b.h": "",
-				// NOTE: export_include_dir headers *should* appear in Bazel hdrs later
-				"export_include_dir_1/export_include_dir_1_a.h": "",
-				"export_include_dir_1/export_include_dir_1_b.h": "",
-				"export_include_dir_2/export_include_dir_2_a.h": "",
-				"export_include_dir_2/export_include_dir_2_b.h": "",
-				// NOTE: Soong implicitly includes headers in the current directory
-				"implicit_include_1.h": "",
-				"implicit_include_2.h": "",
-			},
-			bp: soongCcLibraryStaticPreamble + `
+func registerCcLibraryStaticModuleTypes(ctx android.RegistrationContext) {
+	cc.RegisterCCBuildComponents(ctx)
+	ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory)
+	ctx.RegisterModuleType("cc_library_headers", cc.LibraryHeaderFactory)
+	ctx.RegisterModuleType("genrule", genrule.GenRuleFactory)
+	// Required for system_shared_libs dependencies.
+	ctx.RegisterModuleType("cc_library", cc.LibraryFactory)
+}
+
+func runCcLibraryStaticTestCase(t *testing.T, tc bp2buildTestCase) {
+	t.Helper()
+	runBp2BuildTestCase(t, registerCcLibraryStaticModuleTypes, tc)
+}
+
+func TestCcLibraryStaticSimple(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_static test",
+		moduleTypeUnderTest:                "cc_library_static",
+		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		filesystem: map[string]string{
+			// NOTE: include_dir headers *should not* appear in Bazel hdrs later (?)
+			"include_dir_1/include_dir_1_a.h": "",
+			"include_dir_1/include_dir_1_b.h": "",
+			"include_dir_2/include_dir_2_a.h": "",
+			"include_dir_2/include_dir_2_b.h": "",
+			// NOTE: local_include_dir headers *should not* appear in Bazel hdrs later (?)
+			"local_include_dir_1/local_include_dir_1_a.h": "",
+			"local_include_dir_1/local_include_dir_1_b.h": "",
+			"local_include_dir_2/local_include_dir_2_a.h": "",
+			"local_include_dir_2/local_include_dir_2_b.h": "",
+			// NOTE: export_include_dir headers *should* appear in Bazel hdrs later
+			"export_include_dir_1/export_include_dir_1_a.h": "",
+			"export_include_dir_1/export_include_dir_1_b.h": "",
+			"export_include_dir_2/export_include_dir_2_a.h": "",
+			"export_include_dir_2/export_include_dir_2_b.h": "",
+			// NOTE: Soong implicitly includes headers in the current directory
+			"implicit_include_1.h": "",
+			"implicit_include_2.h": "",
+		},
+		blueprint: soongCcLibraryStaticPreamble + `
 cc_library_headers {
     name: "header_lib_1",
     export_include_dirs: ["header_lib_1"],
@@ -163,8 +166,8 @@
         "local_include_dir_2",
     ],
     export_include_dirs: [
-    "export_include_dir_1",
-    "export_include_dir_2"
+        "export_include_dir_1",
+        "export_include_dir_2"
     ],
     header_libs: [
         "header_lib_1",
@@ -173,18 +176,23 @@
 
     // TODO: Also support export_header_lib_headers
 }`,
-			expectedBazelTargets: []string{`cc_library_static(
+		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
     copts = [
         "-Dflag1",
         "-Dflag2",
         "-Iinclude_dir_1",
+        "-I$(BINDIR)/include_dir_1",
         "-Iinclude_dir_2",
+        "-I$(BINDIR)/include_dir_2",
         "-Ilocal_include_dir_1",
+        "-I$(BINDIR)/local_include_dir_1",
         "-Ilocal_include_dir_2",
+        "-I$(BINDIR)/local_include_dir_2",
         "-I.",
+        "-I$(BINDIR)/.",
     ],
-    deps = [
+    implementation_deps = [
         ":header_lib_1",
         ":header_lib_2",
         ":static_lib_1",
@@ -205,46 +213,61 @@
     ],
 )`, `cc_library_static(
     name = "static_lib_1",
-    copts = ["-I."],
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
     linkstatic = True,
     srcs = ["static_lib_1.cc"],
 )`, `cc_library_static(
     name = "static_lib_2",
-    copts = ["-I."],
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
     linkstatic = True,
     srcs = ["static_lib_2.cc"],
 )`, `cc_library_static(
     name = "whole_static_lib_1",
-    copts = ["-I."],
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
     linkstatic = True,
     srcs = ["whole_static_lib_1.cc"],
 )`, `cc_library_static(
     name = "whole_static_lib_2",
-    copts = ["-I."],
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
     linkstatic = True,
     srcs = ["whole_static_lib_2.cc"],
 )`},
+	})
+}
+
+func TestCcLibraryStaticSubpackage(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_static subpackage test",
+		moduleTypeUnderTest:                "cc_library_static",
+		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		filesystem: map[string]string{
+			// subpackage with subdirectory
+			"subpackage/Android.bp":                         "",
+			"subpackage/subpackage_header.h":                "",
+			"subpackage/subdirectory/subdirectory_header.h": "",
+			// subsubpackage with subdirectory
+			"subpackage/subsubpackage/Android.bp":                         "",
+			"subpackage/subsubpackage/subsubpackage_header.h":             "",
+			"subpackage/subsubpackage/subdirectory/subdirectory_header.h": "",
+			// subsubsubpackage with subdirectory
+			"subpackage/subsubpackage/subsubsubpackage/Android.bp":                         "",
+			"subpackage/subsubpackage/subsubsubpackage/subsubsubpackage_header.h":          "",
+			"subpackage/subsubpackage/subsubsubpackage/subdirectory/subdirectory_header.h": "",
 		},
-		{
-			description:                        "cc_library_static subpackage test",
-			moduleTypeUnderTest:                "cc_library_static",
-			moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
-			filesystem: map[string]string{
-				// subpackage with subdirectory
-				"subpackage/Android.bp":                         "",
-				"subpackage/subpackage_header.h":                "",
-				"subpackage/subdirectory/subdirectory_header.h": "",
-				// subsubpackage with subdirectory
-				"subpackage/subsubpackage/Android.bp":                         "",
-				"subpackage/subsubpackage/subsubpackage_header.h":             "",
-				"subpackage/subsubpackage/subdirectory/subdirectory_header.h": "",
-				// subsubsubpackage with subdirectory
-				"subpackage/subsubpackage/subsubsubpackage/Android.bp":                         "",
-				"subpackage/subsubpackage/subsubsubpackage/subsubsubpackage_header.h":          "",
-				"subpackage/subsubpackage/subsubsubpackage/subdirectory/subdirectory_header.h": "",
-			},
-			bp: soongCcLibraryStaticPreamble + `
+		blueprint: soongCcLibraryStaticPreamble + `
 cc_library_static {
     name: "foo_static",
     srcs: [
@@ -253,70 +276,87 @@
 	"subpackage",
     ],
 }`,
-			expectedBazelTargets: []string{`cc_library_static(
+		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
     copts = [
         "-Isubpackage",
+        "-I$(BINDIR)/subpackage",
         "-I.",
+        "-I$(BINDIR)/.",
     ],
     linkstatic = True,
 )`},
+	})
+}
+
+func TestCcLibraryStaticExportIncludeDir(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_static export include dir",
+		moduleTypeUnderTest:                "cc_library_static",
+		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		filesystem: map[string]string{
+			// subpackage with subdirectory
+			"subpackage/Android.bp":                         "",
+			"subpackage/subpackage_header.h":                "",
+			"subpackage/subdirectory/subdirectory_header.h": "",
 		},
-		{
-			description:                        "cc_library_static export include dir",
-			moduleTypeUnderTest:                "cc_library_static",
-			moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
-			filesystem: map[string]string{
-				// subpackage with subdirectory
-				"subpackage/Android.bp":                         "",
-				"subpackage/subpackage_header.h":                "",
-				"subpackage/subdirectory/subdirectory_header.h": "",
-			},
-			bp: soongCcLibraryStaticPreamble + `
+		blueprint: soongCcLibraryStaticPreamble + `
 cc_library_static {
     name: "foo_static",
     export_include_dirs: ["subpackage"],
 }`,
-			expectedBazelTargets: []string{`cc_library_static(
+		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    copts = ["-I."],
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
     includes = ["subpackage"],
     linkstatic = True,
 )`},
+	})
+}
+
+func TestCcLibraryStaticExportSystemIncludeDir(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_static export system include dir",
+		moduleTypeUnderTest:                "cc_library_static",
+		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		filesystem: map[string]string{
+			// subpackage with subdirectory
+			"subpackage/Android.bp":                         "",
+			"subpackage/subpackage_header.h":                "",
+			"subpackage/subdirectory/subdirectory_header.h": "",
 		},
-		{
-			description:                        "cc_library_static export system include dir",
-			moduleTypeUnderTest:                "cc_library_static",
-			moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
-			filesystem: map[string]string{
-				// subpackage with subdirectory
-				"subpackage/Android.bp":                         "",
-				"subpackage/subpackage_header.h":                "",
-				"subpackage/subdirectory/subdirectory_header.h": "",
-			},
-			bp: soongCcLibraryStaticPreamble + `
+		blueprint: soongCcLibraryStaticPreamble + `
 cc_library_static {
     name: "foo_static",
     export_system_include_dirs: ["subpackage"],
 }`,
-			expectedBazelTargets: []string{`cc_library_static(
+		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    copts = ["-I."],
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
     includes = ["subpackage"],
     linkstatic = True,
 )`},
-		},
-		{
-			description:                        "cc_library_static include_dirs, local_include_dirs, export_include_dirs (b/183742505)",
-			moduleTypeUnderTest:                "cc_library_static",
-			moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
-			dir:                                "subpackage",
-			filesystem: map[string]string{
-				// subpackage with subdirectory
-				"subpackage/Android.bp": `
+	})
+}
+
+func TestCcLibraryStaticManyIncludeDirs(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_static include_dirs, local_include_dirs, export_include_dirs (b/183742505)",
+		moduleTypeUnderTest:                "cc_library_static",
+		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		dir:                                "subpackage",
+		filesystem: map[string]string{
+			// subpackage with subdirectory
+			"subpackage/Android.bp": `
 cc_library_static {
     name: "foo_static",
     // include_dirs are workspace/root relative
@@ -330,101 +370,122 @@
     include_build_directory: true,
     bazel_module: { bp2build_available: true },
 }`,
-				"subpackage/subsubpackage/header.h":          "",
-				"subpackage/subsubpackage2/header.h":         "",
-				"subpackage/exported_subsubpackage/header.h": "",
-				"subpackage2/header.h":                       "",
-				"subpackage3/subsubpackage/header.h":         "",
-			},
-			bp: soongCcLibraryStaticPreamble,
-			expectedBazelTargets: []string{`cc_library_static(
+			"subpackage/subsubpackage/header.h":          "",
+			"subpackage/subsubpackage2/header.h":         "",
+			"subpackage/exported_subsubpackage/header.h": "",
+			"subpackage2/header.h":                       "",
+			"subpackage3/subsubpackage/header.h":         "",
+		},
+		blueprint: soongCcLibraryStaticPreamble,
+		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
     copts = [
         "-Isubpackage/subsubpackage",
+        "-I$(BINDIR)/subpackage/subsubpackage",
         "-Isubpackage2",
+        "-I$(BINDIR)/subpackage2",
         "-Isubpackage3/subsubpackage",
+        "-I$(BINDIR)/subpackage3/subsubpackage",
         "-Isubpackage/subsubpackage2",
+        "-I$(BINDIR)/subpackage/subsubpackage2",
         "-Isubpackage",
+        "-I$(BINDIR)/subpackage",
     ],
     includes = ["./exported_subsubpackage"],
     linkstatic = True,
 )`},
+	})
+}
+
+func TestCcLibraryStaticIncludeBuildDirectoryDisabled(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_static include_build_directory disabled",
+		moduleTypeUnderTest:                "cc_library_static",
+		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		filesystem: map[string]string{
+			// subpackage with subdirectory
+			"subpackage/Android.bp":                         "",
+			"subpackage/subpackage_header.h":                "",
+			"subpackage/subdirectory/subdirectory_header.h": "",
 		},
-		{
-			description:                        "cc_library_static include_build_directory disabled",
-			moduleTypeUnderTest:                "cc_library_static",
-			moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
-			filesystem: map[string]string{
-				// subpackage with subdirectory
-				"subpackage/Android.bp":                         "",
-				"subpackage/subpackage_header.h":                "",
-				"subpackage/subdirectory/subdirectory_header.h": "",
-			},
-			bp: soongCcLibraryStaticPreamble + `
+		blueprint: soongCcLibraryStaticPreamble + `
 cc_library_static {
     name: "foo_static",
     include_dirs: ["subpackage"], // still used, but local_include_dirs is recommended
     local_include_dirs: ["subpackage2"],
     include_build_directory: false,
 }`,
-			expectedBazelTargets: []string{`cc_library_static(
+		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
     copts = [
         "-Isubpackage",
+        "-I$(BINDIR)/subpackage",
         "-Isubpackage2",
+        "-I$(BINDIR)/subpackage2",
     ],
     linkstatic = True,
 )`},
+	})
+}
+
+func TestCcLibraryStaticIncludeBuildDirectoryEnabled(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_static include_build_directory enabled",
+		moduleTypeUnderTest:                "cc_library_static",
+		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		filesystem: map[string]string{
+			// subpackage with subdirectory
+			"subpackage/Android.bp":                         "",
+			"subpackage/subpackage_header.h":                "",
+			"subpackage2/Android.bp":                        "",
+			"subpackage2/subpackage2_header.h":              "",
+			"subpackage/subdirectory/subdirectory_header.h": "",
 		},
-		{
-			description:                        "cc_library_static include_build_directory enabled",
-			moduleTypeUnderTest:                "cc_library_static",
-			moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
-			filesystem: map[string]string{
-				// subpackage with subdirectory
-				"subpackage/Android.bp":                         "",
-				"subpackage/subpackage_header.h":                "",
-				"subpackage2/Android.bp":                        "",
-				"subpackage2/subpackage2_header.h":              "",
-				"subpackage/subdirectory/subdirectory_header.h": "",
-			},
-			bp: soongCcLibraryStaticPreamble + `
+		blueprint: soongCcLibraryStaticPreamble + `
 cc_library_static {
     name: "foo_static",
     include_dirs: ["subpackage"], // still used, but local_include_dirs is recommended
     local_include_dirs: ["subpackage2"],
     include_build_directory: true,
 }`,
-			expectedBazelTargets: []string{`cc_library_static(
+		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
     copts = [
         "-Isubpackage",
+        "-I$(BINDIR)/subpackage",
         "-Isubpackage2",
+        "-I$(BINDIR)/subpackage2",
         "-I.",
+        "-I$(BINDIR)/.",
     ],
     linkstatic = True,
 )`},
-		},
-		{
-			description:                        "cc_library_static arch-specific static_libs",
-			moduleTypeUnderTest:                "cc_library_static",
-			moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
-			depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
-			filesystem:                         map[string]string{},
-			bp: soongCcLibraryStaticPreamble + `
+	})
+}
+
+func TestCcLibraryStaticArchSpecificStaticLib(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_static arch-specific static_libs",
+		moduleTypeUnderTest:                "cc_library_static",
+		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		filesystem:                         map[string]string{},
+		blueprint: soongCcLibraryStaticPreamble + `
 cc_library_static { name: "static_dep" }
 cc_library_static { name: "static_dep2" }
 cc_library_static {
     name: "foo_static",
     arch: { arm64: { static_libs: ["static_dep"], whole_static_libs: ["static_dep2"] } },
 }`,
-			expectedBazelTargets: []string{`cc_library_static(
+		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    copts = ["-I."],
-    deps = select({
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    implementation_deps = select({
         "//build/bazel/platforms/arch:arm64": [":static_dep"],
         "//conditions:default": [],
     }),
@@ -435,32 +496,43 @@
     }),
 )`, `cc_library_static(
     name = "static_dep",
-    copts = ["-I."],
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
     linkstatic = True,
 )`, `cc_library_static(
     name = "static_dep2",
-    copts = ["-I."],
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
     linkstatic = True,
 )`},
-		},
-		{
-			description:                        "cc_library_static os-specific static_libs",
-			moduleTypeUnderTest:                "cc_library_static",
-			moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
-			depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
-			filesystem:                         map[string]string{},
-			bp: soongCcLibraryStaticPreamble + `
+	})
+}
+
+func TestCcLibraryStaticOsSpecificStaticLib(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_static os-specific static_libs",
+		moduleTypeUnderTest:                "cc_library_static",
+		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		filesystem:                         map[string]string{},
+		blueprint: soongCcLibraryStaticPreamble + `
 cc_library_static { name: "static_dep" }
 cc_library_static { name: "static_dep2" }
 cc_library_static {
     name: "foo_static",
     target: { android: { static_libs: ["static_dep"], whole_static_libs: ["static_dep2"] } },
 }`,
-			expectedBazelTargets: []string{`cc_library_static(
+		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    copts = ["-I."],
-    deps = select({
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    implementation_deps = select({
         "//build/bazel/platforms/os:android": [":static_dep"],
         "//conditions:default": [],
     }),
@@ -471,22 +543,30 @@
     }),
 )`, `cc_library_static(
     name = "static_dep",
-    copts = ["-I."],
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
     linkstatic = True,
 )`, `cc_library_static(
     name = "static_dep2",
-    copts = ["-I."],
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
     linkstatic = True,
 )`},
-		},
-		{
-			description:                        "cc_library_static base, arch and os-specific static_libs",
-			moduleTypeUnderTest:                "cc_library_static",
-			moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
-			depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
-			filesystem:                         map[string]string{},
-			bp: soongCcLibraryStaticPreamble + `
+	})
+}
+
+func TestCcLibraryStaticBaseArchOsSpecificStaticLib(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_static base, arch and os-specific static_libs",
+		moduleTypeUnderTest:                "cc_library_static",
+		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		filesystem:                         map[string]string{},
+		blueprint: soongCcLibraryStaticPreamble + `
 cc_library_static { name: "static_dep" }
 cc_library_static { name: "static_dep2" }
 cc_library_static { name: "static_dep3" }
@@ -498,10 +578,13 @@
     target: { android: { static_libs: ["static_dep3"] } },
     arch: { arm64: { static_libs: ["static_dep4"] } },
 }`,
-			expectedBazelTargets: []string{`cc_library_static(
+		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    copts = ["-I."],
-    deps = [":static_dep"] + select({
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    implementation_deps = [":static_dep"] + select({
         "//build/bazel/platforms/arch:arm64": [":static_dep4"],
         "//conditions:default": [],
     }) + select({
@@ -512,88 +595,112 @@
     whole_archive_deps = [":static_dep2"],
 )`, `cc_library_static(
     name = "static_dep",
-    copts = ["-I."],
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
     linkstatic = True,
 )`, `cc_library_static(
     name = "static_dep2",
-    copts = ["-I."],
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
     linkstatic = True,
 )`, `cc_library_static(
     name = "static_dep3",
-    copts = ["-I."],
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
     linkstatic = True,
 )`, `cc_library_static(
     name = "static_dep4",
-    copts = ["-I."],
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
     linkstatic = True,
 )`},
+	})
+}
+
+func TestCcLibraryStaticSimpleExcludeSrcs(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_static simple exclude_srcs",
+		moduleTypeUnderTest:                "cc_library_static",
+		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		filesystem: map[string]string{
+			"common.c":       "",
+			"foo-a.c":        "",
+			"foo-excluded.c": "",
 		},
-		{
-			description:                        "cc_library_static simple exclude_srcs",
-			moduleTypeUnderTest:                "cc_library_static",
-			moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
-			depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
-			filesystem: map[string]string{
-				"common.c":       "",
-				"foo-a.c":        "",
-				"foo-excluded.c": "",
-			},
-			bp: soongCcLibraryStaticPreamble + `
+		blueprint: soongCcLibraryStaticPreamble + `
 cc_library_static {
     name: "foo_static",
     srcs: ["common.c", "foo-*.c"],
     exclude_srcs: ["foo-excluded.c"],
 }`,
-			expectedBazelTargets: []string{`cc_library_static(
+		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    copts = ["-I."],
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
     linkstatic = True,
-    srcs = [
+    srcs_c = [
         "common.c",
         "foo-a.c",
     ],
 )`},
+	})
+}
+
+func TestCcLibraryStaticOneArchSrcs(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_static one arch specific srcs",
+		moduleTypeUnderTest:                "cc_library_static",
+		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		filesystem: map[string]string{
+			"common.c":  "",
+			"foo-arm.c": "",
 		},
-		{
-			description:                        "cc_library_static one arch specific srcs",
-			moduleTypeUnderTest:                "cc_library_static",
-			moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
-			depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
-			filesystem: map[string]string{
-				"common.c":  "",
-				"foo-arm.c": "",
-			},
-			bp: soongCcLibraryStaticPreamble + `
+		blueprint: soongCcLibraryStaticPreamble + `
 cc_library_static {
     name: "foo_static",
     srcs: ["common.c"],
     arch: { arm: { srcs: ["foo-arm.c"] } }
 }`,
-			expectedBazelTargets: []string{`cc_library_static(
+		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    copts = ["-I."],
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
     linkstatic = True,
-    srcs = ["common.c"] + select({
+    srcs_c = ["common.c"] + select({
         "//build/bazel/platforms/arch:arm": ["foo-arm.c"],
         "//conditions:default": [],
     }),
 )`},
+	})
+}
+
+func TestCcLibraryStaticOneArchSrcsExcludeSrcs(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_static one arch specific srcs and exclude_srcs",
+		moduleTypeUnderTest:                "cc_library_static",
+		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		filesystem: map[string]string{
+			"common.c":           "",
+			"for-arm.c":          "",
+			"not-for-arm.c":      "",
+			"not-for-anything.c": "",
 		},
-		{
-			description:                        "cc_library_static one arch specific srcs and exclude_srcs",
-			moduleTypeUnderTest:                "cc_library_static",
-			moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
-			depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
-			filesystem: map[string]string{
-				"common.c":           "",
-				"for-arm.c":          "",
-				"not-for-arm.c":      "",
-				"not-for-anything.c": "",
-			},
-			bp: soongCcLibraryStaticPreamble + `
+		blueprint: soongCcLibraryStaticPreamble + `
 cc_library_static {
     name: "foo_static",
     srcs: ["common.c", "not-for-*.c"],
@@ -602,30 +709,35 @@
         arm: { srcs: ["for-arm.c"], exclude_srcs: ["not-for-arm.c"] },
     },
 }`,
-			expectedBazelTargets: []string{`cc_library_static(
+		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    copts = ["-I."],
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
     linkstatic = True,
-    srcs = ["common.c"] + select({
+    srcs_c = ["common.c"] + select({
         "//build/bazel/platforms/arch:arm": ["for-arm.c"],
         "//conditions:default": ["not-for-arm.c"],
     }),
 )`},
+	})
+}
+
+func TestCcLibraryStaticTwoArchExcludeSrcs(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_static arch specific exclude_srcs for 2 architectures",
+		moduleTypeUnderTest:                "cc_library_static",
+		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		filesystem: map[string]string{
+			"common.c":      "",
+			"for-arm.c":     "",
+			"for-x86.c":     "",
+			"not-for-arm.c": "",
+			"not-for-x86.c": "",
 		},
-		{
-			description:                        "cc_library_static arch specific exclude_srcs for 2 architectures",
-			moduleTypeUnderTest:                "cc_library_static",
-			moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
-			depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
-			filesystem: map[string]string{
-				"common.c":      "",
-				"for-arm.c":     "",
-				"for-x86.c":     "",
-				"not-for-arm.c": "",
-				"not-for-x86.c": "",
-			},
-			bp: soongCcLibraryStaticPreamble + `
+		blueprint: soongCcLibraryStaticPreamble + `
 cc_library_static {
     name: "foo_static",
     srcs: ["common.c", "not-for-*.c"],
@@ -635,11 +747,14 @@
         x86: { srcs: ["for-x86.c"], exclude_srcs: ["not-for-x86.c"] },
     },
 } `,
-			expectedBazelTargets: []string{`cc_library_static(
+		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    copts = ["-I."],
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
     linkstatic = True,
-    srcs = ["common.c"] + select({
+    srcs_c = ["common.c"] + select({
         "//build/bazel/platforms/arch:arm": [
             "for-arm.c",
             "not-for-x86.c",
@@ -654,26 +769,27 @@
         ],
     }),
 )`},
+	})
+}
+func TestCcLibraryStaticFourArchExcludeSrcs(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_static arch specific exclude_srcs for 4 architectures",
+		moduleTypeUnderTest:                "cc_library_static",
+		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		filesystem: map[string]string{
+			"common.c":             "",
+			"for-arm.c":            "",
+			"for-arm64.c":          "",
+			"for-x86.c":            "",
+			"for-x86_64.c":         "",
+			"not-for-arm.c":        "",
+			"not-for-arm64.c":      "",
+			"not-for-x86.c":        "",
+			"not-for-x86_64.c":     "",
+			"not-for-everything.c": "",
 		},
-		{
-			description:                        "cc_library_static arch specific exclude_srcs for 4 architectures",
-			moduleTypeUnderTest:                "cc_library_static",
-			moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
-			depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
-			filesystem: map[string]string{
-				"common.c":             "",
-				"for-arm.c":            "",
-				"for-arm64.c":          "",
-				"for-x86.c":            "",
-				"for-x86_64.c":         "",
-				"not-for-arm.c":        "",
-				"not-for-arm64.c":      "",
-				"not-for-x86.c":        "",
-				"not-for-x86_64.c":     "",
-				"not-for-everything.c": "",
-			},
-			bp: soongCcLibraryStaticPreamble + `
+		blueprint: soongCcLibraryStaticPreamble + `
 cc_library_static {
     name: "foo_static",
     srcs: ["common.c", "not-for-*.c"],
@@ -685,11 +801,14 @@
         x86_64: { srcs: ["for-x86_64.c"], exclude_srcs: ["not-for-x86_64.c"] },
 	},
 } `,
-			expectedBazelTargets: []string{`cc_library_static(
+		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    copts = ["-I."],
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
     linkstatic = True,
-    srcs = ["common.c"] + select({
+    srcs_c = ["common.c"] + select({
         "//build/bazel/platforms/arch:arm": [
             "for-arm.c",
             "not-for-arm64.c",
@@ -722,43 +841,129 @@
         ],
     }),
 )`},
+	})
+}
+
+func TestCcLibraryStaticOneArchEmpty(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_static one arch empty",
+		moduleTypeUnderTest:                "cc_library_static",
+		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		filesystem: map[string]string{
+			"common.cc":       "",
+			"foo-no-arm.cc":   "",
+			"foo-excluded.cc": "",
 		},
-		{
-			description:                        "cc_library_static multiple dep same name panic",
-			moduleTypeUnderTest:                "cc_library_static",
-			moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
-			depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
-			filesystem:                         map[string]string{},
-			bp: soongCcLibraryStaticPreamble + `
+		blueprint: soongCcLibraryStaticPreamble + `
+cc_library_static {
+    name: "foo_static",
+    srcs: ["common.cc", "foo-*.cc"],
+    exclude_srcs: ["foo-excluded.cc"],
+    arch: {
+        arm: { exclude_srcs: ["foo-no-arm.cc"] },
+    },
+}`,
+		expectedBazelTargets: []string{`cc_library_static(
+    name = "foo_static",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    linkstatic = True,
+    srcs = ["common.cc"] + select({
+        "//build/bazel/platforms/arch:arm": [],
+        "//conditions:default": ["foo-no-arm.cc"],
+    }),
+)`},
+	})
+}
+
+func TestCcLibraryStaticOneArchEmptyOtherSet(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_static one arch empty other set",
+		moduleTypeUnderTest:                "cc_library_static",
+		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		filesystem: map[string]string{
+			"common.cc":       "",
+			"foo-no-arm.cc":   "",
+			"x86-only.cc":     "",
+			"foo-excluded.cc": "",
+		},
+		blueprint: soongCcLibraryStaticPreamble + `
+cc_library_static {
+    name: "foo_static",
+    srcs: ["common.cc", "foo-*.cc"],
+    exclude_srcs: ["foo-excluded.cc"],
+    arch: {
+        arm: { exclude_srcs: ["foo-no-arm.cc"] },
+        x86: { srcs: ["x86-only.cc"] },
+    },
+}`,
+		expectedBazelTargets: []string{`cc_library_static(
+    name = "foo_static",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    linkstatic = True,
+    srcs = ["common.cc"] + select({
+        "//build/bazel/platforms/arch:arm": [],
+        "//build/bazel/platforms/arch:x86": [
+            "foo-no-arm.cc",
+            "x86-only.cc",
+        ],
+        "//conditions:default": ["foo-no-arm.cc"],
+    }),
+)`},
+	})
+}
+
+func TestCcLibraryStaticMultipleDepSameName(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_static multiple dep same name panic",
+		moduleTypeUnderTest:                "cc_library_static",
+		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		filesystem:                         map[string]string{},
+		blueprint: soongCcLibraryStaticPreamble + `
 cc_library_static { name: "static_dep" }
 cc_library_static {
     name: "foo_static",
     static_libs: ["static_dep", "static_dep"],
 }`,
-			expectedBazelTargets: []string{`cc_library_static(
+		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    copts = ["-I."],
-    deps = [":static_dep"],
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    implementation_deps = [":static_dep"],
     linkstatic = True,
 )`, `cc_library_static(
     name = "static_dep",
-    copts = ["-I."],
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
     linkstatic = True,
 )`},
+	})
+}
+
+func TestCcLibraryStaticOneMultilibSrcsExcludeSrcs(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_static 1 multilib srcs and exclude_srcs",
+		moduleTypeUnderTest:                "cc_library_static",
+		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		filesystem: map[string]string{
+			"common.c":        "",
+			"for-lib32.c":     "",
+			"not-for-lib32.c": "",
 		},
-		{
-			description:                        "cc_library_static 1 multilib srcs and exclude_srcs",
-			moduleTypeUnderTest:                "cc_library_static",
-			moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
-			depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
-			filesystem: map[string]string{
-				"common.c":        "",
-				"for-lib32.c":     "",
-				"not-for-lib32.c": "",
-			},
-			bp: soongCcLibraryStaticPreamble + `
+		blueprint: soongCcLibraryStaticPreamble + `
 cc_library_static {
     name: "foo_static",
     srcs: ["common.c", "not-for-*.c"],
@@ -766,31 +971,36 @@
         lib32: { srcs: ["for-lib32.c"], exclude_srcs: ["not-for-lib32.c"] },
     },
 } `,
-			expectedBazelTargets: []string{`cc_library_static(
+		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static",
-    copts = ["-I."],
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
     linkstatic = True,
-    srcs = ["common.c"] + select({
+    srcs_c = ["common.c"] + select({
         "//build/bazel/platforms/arch:arm": ["for-lib32.c"],
         "//build/bazel/platforms/arch:x86": ["for-lib32.c"],
         "//conditions:default": ["not-for-lib32.c"],
     }),
 )`},
+	})
+}
+
+func TestCcLibraryStaticTwoMultilibSrcsExcludeSrcs(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_static 2 multilib srcs and exclude_srcs",
+		moduleTypeUnderTest:                "cc_library_static",
+		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		filesystem: map[string]string{
+			"common.c":        "",
+			"for-lib32.c":     "",
+			"for-lib64.c":     "",
+			"not-for-lib32.c": "",
+			"not-for-lib64.c": "",
 		},
-		{
-			description:                        "cc_library_static 2 multilib srcs and exclude_srcs",
-			moduleTypeUnderTest:                "cc_library_static",
-			moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
-			depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
-			filesystem: map[string]string{
-				"common.c":        "",
-				"for-lib32.c":     "",
-				"for-lib64.c":     "",
-				"not-for-lib32.c": "",
-				"not-for-lib64.c": "",
-			},
-			bp: soongCcLibraryStaticPreamble + `
+		blueprint: soongCcLibraryStaticPreamble + `
 cc_library_static {
     name: "foo_static2",
     srcs: ["common.c", "not-for-*.c"],
@@ -799,11 +1009,14 @@
         lib64: { srcs: ["for-lib64.c"], exclude_srcs: ["not-for-lib64.c"] },
     },
 } `,
-			expectedBazelTargets: []string{`cc_library_static(
+		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static2",
-    copts = ["-I."],
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
     linkstatic = True,
-    srcs = ["common.c"] + select({
+    srcs_c = ["common.c"] + select({
         "//build/bazel/platforms/arch:arm": [
             "for-lib32.c",
             "not-for-lib64.c",
@@ -826,30 +1039,32 @@
         ],
     }),
 )`},
+	})
+}
+
+func TestCcLibrarySTaticArchMultilibSrcsExcludeSrcs(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_static arch and multilib srcs and exclude_srcs",
+		moduleTypeUnderTest:                "cc_library_static",
+		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		filesystem: map[string]string{
+			"common.c":             "",
+			"for-arm.c":            "",
+			"for-arm64.c":          "",
+			"for-x86.c":            "",
+			"for-x86_64.c":         "",
+			"for-lib32.c":          "",
+			"for-lib64.c":          "",
+			"not-for-arm.c":        "",
+			"not-for-arm64.c":      "",
+			"not-for-x86.c":        "",
+			"not-for-x86_64.c":     "",
+			"not-for-lib32.c":      "",
+			"not-for-lib64.c":      "",
+			"not-for-everything.c": "",
 		},
-		{
-			description:                        "cc_library_static arch and multilib srcs and exclude_srcs",
-			moduleTypeUnderTest:                "cc_library_static",
-			moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
-			depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
-			filesystem: map[string]string{
-				"common.c":             "",
-				"for-arm.c":            "",
-				"for-arm64.c":          "",
-				"for-x86.c":            "",
-				"for-x86_64.c":         "",
-				"for-lib32.c":          "",
-				"for-lib64.c":          "",
-				"not-for-arm.c":        "",
-				"not-for-arm64.c":      "",
-				"not-for-x86.c":        "",
-				"not-for-x86_64.c":     "",
-				"not-for-lib32.c":      "",
-				"not-for-lib64.c":      "",
-				"not-for-everything.c": "",
-			},
-			bp: soongCcLibraryStaticPreamble + `
+		blueprint: soongCcLibraryStaticPreamble + `
 cc_library_static {
    name: "foo_static3",
    srcs: ["common.c", "not-for-*.c"],
@@ -865,11 +1080,14 @@
        lib64: { srcs: ["for-lib64.c"], exclude_srcs: ["not-for-lib64.c"] },
    },
 }`,
-			expectedBazelTargets: []string{`cc_library_static(
+		expectedBazelTargets: []string{`cc_library_static(
     name = "foo_static3",
-    copts = ["-I."],
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
     linkstatic = True,
-    srcs = ["common.c"] + select({
+    srcs_c = ["common.c"] + select({
         "//build/bazel/platforms/arch:arm": [
             "for-arm.c",
             "for-lib32.c",
@@ -912,64 +1130,484 @@
         ],
     }),
 )`},
+	})
+}
+
+func TestCcLibraryStaticArchSrcsExcludeSrcsGeneratedFiles(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_static arch srcs/exclude_srcs with generated files",
+		moduleTypeUnderTest:                "cc_library_static",
+		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		filesystem: map[string]string{
+			"common.cpp":             "",
+			"for-x86.cpp":            "",
+			"not-for-x86.cpp":        "",
+			"not-for-everything.cpp": "",
+			"dep/Android.bp": `
+genrule {
+	name: "generated_src_other_pkg",
+	out: ["generated_src_other_pkg.cpp"],
+	cmd: "nothing to see here",
+}
+
+genrule {
+	name: "generated_hdr_other_pkg",
+	out: ["generated_hdr_other_pkg.cpp"],
+	cmd: "nothing to see here",
+}
+
+genrule {
+	name: "generated_hdr_other_pkg_x86",
+	out: ["generated_hdr_other_pkg_x86.cpp"],
+	cmd: "nothing to see here",
+}`,
 		},
-	}
+		blueprint: soongCcLibraryStaticPreamble + `
+genrule {
+    name: "generated_src",
+    out: ["generated_src.cpp"],
+    cmd: "nothing to see here",
+}
 
-	dir := "."
-	for _, testCase := range testCases {
-		filesystem := make(map[string][]byte)
-		toParse := []string{
-			"Android.bp",
-		}
-		for f, content := range testCase.filesystem {
-			if strings.HasSuffix(f, "Android.bp") {
-				toParse = append(toParse, f)
-			}
-			filesystem[f] = []byte(content)
-		}
-		config := android.TestConfig(buildDir, nil, testCase.bp, filesystem)
-		ctx := android.NewTestContext(config)
+genrule {
+    name: "generated_src_x86",
+    out: ["generated_src_x86.cpp"],
+    cmd: "nothing to see here",
+}
 
-		cc.RegisterCCBuildComponents(ctx)
-		ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory)
-		ctx.RegisterModuleType("cc_library_headers", cc.LibraryHeaderFactory)
+genrule {
+    name: "generated_hdr",
+    out: ["generated_hdr.h"],
+    cmd: "nothing to see here",
+}
 
-		ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
-		for _, m := range testCase.depsMutators {
-			ctx.DepsBp2BuildMutators(m)
-		}
-		ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
-		ctx.RegisterBp2BuildConfig(bp2buildConfig)
-		ctx.RegisterForBazelConversion()
+cc_library_static {
+   name: "foo_static3",
+   srcs: ["common.cpp", "not-for-*.cpp"],
+   exclude_srcs: ["not-for-everything.cpp"],
+   generated_sources: ["generated_src", "generated_src_other_pkg"],
+   generated_headers: ["generated_hdr", "generated_hdr_other_pkg"],
+   arch: {
+       x86: {
+           srcs: ["for-x86.cpp"],
+           exclude_srcs: ["not-for-x86.cpp"],
+           generated_sources: ["generated_src_x86"],
+           generated_headers: ["generated_hdr_other_pkg_x86"],
+       },
+   },
+}
+`,
+		expectedBazelTargets: []string{`cc_library_static(
+    name = "foo_static3",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    linkstatic = True,
+    srcs = [
+        "//dep:generated_hdr_other_pkg",
+        "//dep:generated_src_other_pkg",
+        ":generated_hdr",
+        ":generated_src",
+        "common.cpp",
+    ] + select({
+        "//build/bazel/platforms/arch:x86": [
+            "//dep:generated_hdr_other_pkg_x86",
+            ":generated_src_x86",
+            "for-x86.cpp",
+        ],
+        "//conditions:default": ["not-for-x86.cpp"],
+    }),
+)`},
+	})
+}
 
-		_, errs := ctx.ParseFileList(dir, toParse)
-		if Errored(t, testCase.description, errs) {
-			continue
-		}
-		_, errs = ctx.ResolveDependencies(config)
-		if Errored(t, testCase.description, errs) {
-			continue
-		}
+func TestCcLibraryStaticGetTargetProperties(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
 
-		checkDir := dir
-		if testCase.dir != "" {
-			checkDir = testCase.dir
-		}
-		codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
-		bazelTargets := generateBazelTargetsForDir(codegenCtx, checkDir)
-		if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
-			t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount)
-		} else {
-			for i, target := range bazelTargets {
-				if w, g := testCase.expectedBazelTargets[i], target.content; w != g {
-					t.Errorf(
-						"%s: Expected generated Bazel target to be '%s', got '%s'",
-						testCase.description,
-						w,
-						g,
-					)
+		description:                        "cc_library_static complex GetTargetProperties",
+		moduleTypeUnderTest:                "cc_library_static",
+		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		blueprint: soongCcLibraryStaticPreamble + `
+cc_library_static {
+    name: "foo_static",
+    target: {
+        android: {
+            srcs: ["android_src.c"],
+        },
+        android_arm: {
+            srcs: ["android_arm_src.c"],
+        },
+        android_arm64: {
+            srcs: ["android_arm64_src.c"],
+        },
+        android_x86: {
+            srcs: ["android_x86_src.c"],
+        },
+        android_x86_64: {
+            srcs: ["android_x86_64_src.c"],
+        },
+        linux_bionic_arm64: {
+            srcs: ["linux_bionic_arm64_src.c"],
+        },
+        linux_bionic_x86_64: {
+            srcs: ["linux_bionic_x86_64_src.c"],
+        },
+    },
+}`,
+		expectedBazelTargets: []string{`cc_library_static(
+    name = "foo_static",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    linkstatic = True,
+    srcs_c = select({
+        "//build/bazel/platforms/os:android": ["android_src.c"],
+        "//conditions:default": [],
+    }) + select({
+        "//build/bazel/platforms/os_arch:android_arm": ["android_arm_src.c"],
+        "//build/bazel/platforms/os_arch:android_arm64": ["android_arm64_src.c"],
+        "//build/bazel/platforms/os_arch:android_x86": ["android_x86_src.c"],
+        "//build/bazel/platforms/os_arch:android_x86_64": ["android_x86_64_src.c"],
+        "//build/bazel/platforms/os_arch:linux_bionic_arm64": ["linux_bionic_arm64_src.c"],
+        "//build/bazel/platforms/os_arch:linux_bionic_x86_64": ["linux_bionic_x86_64_src.c"],
+        "//conditions:default": [],
+    }),
+)`},
+	})
+}
+
+func TestCcLibraryStaticProductVariableSelects(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_static product variable selects",
+		moduleTypeUnderTest:                "cc_library_static",
+		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		blueprint: soongCcLibraryStaticPreamble + `
+cc_library_static {
+    name: "foo_static",
+    srcs: ["common.c"],
+    product_variables: {
+      malloc_not_svelte: {
+        cflags: ["-Wmalloc_not_svelte"],
+      },
+      malloc_zero_contents: {
+        cflags: ["-Wmalloc_zero_contents"],
+      },
+      binder32bit: {
+        cflags: ["-Wbinder32bit"],
+      },
+    },
+} `,
+		expectedBazelTargets: []string{`cc_library_static(
+    name = "foo_static",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ] + select({
+        "//build/bazel/product_variables:binder32bit": ["-Wbinder32bit"],
+        "//conditions:default": [],
+    }) + select({
+        "//build/bazel/product_variables:malloc_not_svelte": ["-Wmalloc_not_svelte"],
+        "//conditions:default": [],
+    }) + select({
+        "//build/bazel/product_variables:malloc_zero_contents": ["-Wmalloc_zero_contents"],
+        "//conditions:default": [],
+    }),
+    linkstatic = True,
+    srcs_c = ["common.c"],
+)`},
+	})
+}
+
+func TestCcLibraryStaticProductVariableArchSpecificSelects(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_static arch-specific product variable selects",
+		moduleTypeUnderTest:                "cc_library_static",
+		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		filesystem:                         map[string]string{},
+		blueprint: soongCcLibraryStaticPreamble + `
+cc_library_static {
+    name: "foo_static",
+    srcs: ["common.c"],
+    product_variables: {
+      malloc_not_svelte: {
+        cflags: ["-Wmalloc_not_svelte"],
+      },
+    },
+		arch: {
+				arm64: {
+						product_variables: {
+								malloc_not_svelte: {
+										cflags: ["-Warm64_malloc_not_svelte"],
+								},
+						},
+				},
+		},
+		multilib: {
+				lib32: {
+						product_variables: {
+								malloc_not_svelte: {
+										cflags: ["-Wlib32_malloc_not_svelte"],
+								},
+						},
+				},
+		},
+		target: {
+				android: {
+						product_variables: {
+								malloc_not_svelte: {
+										cflags: ["-Wandroid_malloc_not_svelte"],
+								},
+						},
 				}
-			}
-		}
-	}
+		},
+} `,
+		expectedBazelTargets: []string{`cc_library_static(
+    name = "foo_static",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ] + select({
+        "//build/bazel/product_variables:malloc_not_svelte": ["-Wmalloc_not_svelte"],
+        "//conditions:default": [],
+    }) + select({
+        "//build/bazel/product_variables:malloc_not_svelte-android": ["-Wandroid_malloc_not_svelte"],
+        "//conditions:default": [],
+    }) + select({
+        "//build/bazel/product_variables:malloc_not_svelte-arm": ["-Wlib32_malloc_not_svelte"],
+        "//conditions:default": [],
+    }) + select({
+        "//build/bazel/product_variables:malloc_not_svelte-arm64": ["-Warm64_malloc_not_svelte"],
+        "//conditions:default": [],
+    }) + select({
+        "//build/bazel/product_variables:malloc_not_svelte-x86": ["-Wlib32_malloc_not_svelte"],
+        "//conditions:default": [],
+    }),
+    linkstatic = True,
+    srcs_c = ["common.c"],
+)`},
+	})
+}
+
+func TestCcLibraryStaticProductVariableStringReplacement(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_static product variable string replacement",
+		moduleTypeUnderTest:                "cc_library_static",
+		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		filesystem:                         map[string]string{},
+		blueprint: soongCcLibraryStaticPreamble + `
+cc_library_static {
+    name: "foo_static",
+    srcs: ["common.S"],
+    product_variables: {
+      platform_sdk_version: {
+          asflags: ["-DPLATFORM_SDK_VERSION=%d"],
+      },
+    },
+} `,
+		expectedBazelTargets: []string{`cc_library_static(
+    name = "foo_static",
+    asflags = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ] + select({
+        "//build/bazel/product_variables:platform_sdk_version": ["-DPLATFORM_SDK_VERSION=$(Platform_sdk_version)"],
+        "//conditions:default": [],
+    }),
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    linkstatic = True,
+    srcs_as = ["common.S"],
+)`},
+	})
+}
+
+func TestStaticLibrary_SystemSharedLibsRootEmpty(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_static system_shared_lib empty root",
+		moduleTypeUnderTest:                "cc_library_static",
+		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		blueprint: soongCcLibraryStaticPreamble + `
+cc_library_static {
+    name: "root_empty",
+	  system_shared_libs: [],
+}
+`,
+		expectedBazelTargets: []string{`cc_library_static(
+    name = "root_empty",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    linkstatic = True,
+    system_dynamic_deps = [],
+)`},
+	})
+}
+
+func TestStaticLibrary_SystemSharedLibsStaticEmpty(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_static system_shared_lib empty static default",
+		moduleTypeUnderTest:                "cc_library_static",
+		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		blueprint: soongCcLibraryStaticPreamble + `
+cc_defaults {
+    name: "static_empty_defaults",
+    static: {
+				system_shared_libs: [],
+		},
+}
+cc_library_static {
+    name: "static_empty",
+	  defaults: ["static_empty_defaults"],
+}
+`,
+		expectedBazelTargets: []string{`cc_library_static(
+    name = "static_empty",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    linkstatic = True,
+    system_dynamic_deps = [],
+)`},
+	})
+}
+
+func TestStaticLibrary_SystemSharedLibsBionicEmpty(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_static system_shared_lib empty for bionic variant",
+		moduleTypeUnderTest:                "cc_library_static",
+		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		blueprint: soongCcLibraryStaticPreamble + `
+cc_library_static {
+    name: "target_bionic_empty",
+    target: {
+        bionic: {
+            system_shared_libs: [],
+        },
+    },
+}
+`,
+		expectedBazelTargets: []string{`cc_library_static(
+    name = "target_bionic_empty",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    linkstatic = True,
+    system_dynamic_deps = [],
+)`},
+	})
+}
+
+func TestStaticLibrary_SystemSharedLibsLinuxBionicEmpty(t *testing.T) {
+	// Note that this behavior is technically incorrect (it's a simplification).
+	// The correct behavior would be if bp2build wrote `system_dynamic_deps = []`
+	// only for linux_bionic, but `android` had `["libc", "libdl", "libm"].
+	// b/195791252 tracks the fix.
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_static system_shared_lib empty for linux_bionic variant",
+		moduleTypeUnderTest:                "cc_library_static",
+		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		blueprint: soongCcLibraryStaticPreamble + `
+cc_library_static {
+    name: "target_linux_bionic_empty",
+    target: {
+        linux_bionic: {
+            system_shared_libs: [],
+        },
+    },
+}
+`,
+		expectedBazelTargets: []string{`cc_library_static(
+    name = "target_linux_bionic_empty",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    linkstatic = True,
+    system_dynamic_deps = [],
+)`},
+	})
+}
+
+func TestStaticLibrary_SystemSharedLibsBionic(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_static system_shared_libs set for bionic variant",
+		moduleTypeUnderTest:                "cc_library_static",
+		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		blueprint: soongCcLibraryStaticPreamble + `
+cc_library{name: "libc"}
+
+cc_library_static {
+    name: "target_bionic",
+    target: {
+        bionic: {
+            system_shared_libs: ["libc"],
+        },
+    },
+}
+`,
+		expectedBazelTargets: []string{`cc_library_static(
+    name = "target_bionic",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    linkstatic = True,
+    system_dynamic_deps = select({
+        "//build/bazel/platforms/os:bionic": [":libc"],
+        "//conditions:default": [],
+    }),
+)`},
+	})
+}
+
+func TestStaticLibrary_SystemSharedLibsLinuxRootAndLinuxBionic(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_static system_shared_libs set for root and linux_bionic variant",
+		moduleTypeUnderTest:                "cc_library_static",
+		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		blueprint: soongCcLibraryStaticPreamble + `
+cc_library{name: "libc"}
+cc_library{name: "libm"}
+
+cc_library_static {
+    name: "target_linux_bionic",
+		system_shared_libs: ["libc"],
+    target: {
+        linux_bionic: {
+            system_shared_libs: ["libm"],
+        },
+    },
+}
+`,
+		expectedBazelTargets: []string{`cc_library_static(
+    name = "target_linux_bionic",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    linkstatic = True,
+    system_dynamic_deps = [":libc"] + select({
+        "//build/bazel/platforms/os:linux_bionic": [":libm"],
+        "//conditions:default": [],
+    }),
+)`},
+	})
 }
diff --git a/bp2build/cc_object_conversion_test.go b/bp2build/cc_object_conversion_test.go
index 9efdb53..9ac28a5 100644
--- a/bp2build/cc_object_conversion_test.go
+++ b/bp2build/cc_object_conversion_test.go
@@ -15,37 +15,38 @@
 package bp2build
 
 import (
+	"testing"
+
 	"android/soong/android"
 	"android/soong/cc"
-	"fmt"
-	"strings"
-	"testing"
 )
 
-func TestCcObjectBp2Build(t *testing.T) {
-	testCases := []struct {
-		description                        string
-		moduleTypeUnderTest                string
-		moduleTypeUnderTestFactory         android.ModuleFactory
-		moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext)
-		blueprint                          string
-		expectedBazelTargets               []string
-		filesystem                         map[string]string
-	}{
-		{
-			description:                        "simple cc_object generates cc_object with include header dep",
-			moduleTypeUnderTest:                "cc_object",
-			moduleTypeUnderTestFactory:         cc.ObjectFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
-			filesystem: map[string]string{
-				"a/b/foo.h":     "",
-				"a/b/bar.h":     "",
-				"a/b/exclude.c": "",
-				"a/b/c.c":       "",
-			},
-			blueprint: `cc_object {
+func registerCcObjectModuleTypes(ctx android.RegistrationContext) {
+	// Always register cc_defaults module factory
+	ctx.RegisterModuleType("cc_defaults", func() android.Module { return cc.DefaultsFactory() })
+}
+
+func runCcObjectTestCase(t *testing.T, tc bp2buildTestCase) {
+	t.Helper()
+	runBp2BuildTestCase(t, registerCcObjectModuleTypes, tc)
+}
+
+func TestCcObjectSimple(t *testing.T) {
+	runCcObjectTestCase(t, bp2buildTestCase{
+		description:                        "simple cc_object generates cc_object with include header dep",
+		moduleTypeUnderTest:                "cc_object",
+		moduleTypeUnderTestFactory:         cc.ObjectFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
+		filesystem: map[string]string{
+			"a/b/foo.h":     "",
+			"a/b/bar.h":     "",
+			"a/b/exclude.c": "",
+			"a/b/c.c":       "",
+		},
+		blueprint: `cc_object {
     name: "foo",
     local_include_dirs: ["include"],
+    system_shared_libs: [],
     cflags: [
         "-Wno-gcc-compat",
         "-Wall",
@@ -57,7 +58,7 @@
     exclude_srcs: ["a/b/exclude.c"],
 }
 `,
-			expectedBazelTargets: []string{`cc_object(
+		expectedBazelTargets: []string{`cc_object(
     name = "foo",
     copts = [
         "-fno-addrsig",
@@ -65,19 +66,25 @@
         "-Wall",
         "-Werror",
         "-Iinclude",
+        "-I$(BINDIR)/include",
         "-I.",
+        "-I$(BINDIR)/.",
     ],
     srcs = ["a/b/c.c"],
 )`,
-			},
 		},
-		{
-			description:                        "simple cc_object with defaults",
-			moduleTypeUnderTest:                "cc_object",
-			moduleTypeUnderTestFactory:         cc.ObjectFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
-			blueprint: `cc_object {
+	})
+}
+
+func TestCcObjectDefaults(t *testing.T) {
+	runCcObjectTestCase(t, bp2buildTestCase{
+		description:                        "simple cc_object with defaults",
+		moduleTypeUnderTest:                "cc_object",
+		moduleTypeUnderTestFactory:         cc.ObjectFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
+		blueprint: `cc_object {
     name: "foo",
+    system_shared_libs: [],
     local_include_dirs: ["include"],
     srcs: [
         "a/b/*.h",
@@ -101,7 +108,7 @@
     ],
 }
 `,
-			expectedBazelTargets: []string{`cc_object(
+		expectedBazelTargets: []string{`cc_object(
     name = "foo",
     copts = [
         "-Wno-gcc-compat",
@@ -109,37 +116,44 @@
         "-Werror",
         "-fno-addrsig",
         "-Iinclude",
+        "-I$(BINDIR)/include",
         "-I.",
+        "-I$(BINDIR)/.",
     ],
     srcs = ["a/b/c.c"],
 )`,
-			},
+		}})
+}
+
+func TestCcObjectCcObjetDepsInObjs(t *testing.T) {
+	runCcObjectTestCase(t, bp2buildTestCase{
+		description:                        "cc_object with cc_object deps in objs props",
+		moduleTypeUnderTest:                "cc_object",
+		moduleTypeUnderTestFactory:         cc.ObjectFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
+		filesystem: map[string]string{
+			"a/b/c.c": "",
+			"x/y/z.c": "",
 		},
-		{
-			description:                        "cc_object with cc_object deps in objs props",
-			moduleTypeUnderTest:                "cc_object",
-			moduleTypeUnderTestFactory:         cc.ObjectFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
-			filesystem: map[string]string{
-				"a/b/c.c": "",
-				"x/y/z.c": "",
-			},
-			blueprint: `cc_object {
+		blueprint: `cc_object {
     name: "foo",
+    system_shared_libs: [],
     srcs: ["a/b/c.c"],
     objs: ["bar"],
 }
 
 cc_object {
     name: "bar",
+    system_shared_libs: [],
     srcs: ["x/y/z.c"],
 }
 `,
-			expectedBazelTargets: []string{`cc_object(
+		expectedBazelTargets: []string{`cc_object(
     name = "bar",
     copts = [
         "-fno-addrsig",
         "-I.",
+        "-I$(BINDIR)/.",
     ],
     srcs = ["x/y/z.c"],
 )`, `cc_object(
@@ -147,161 +161,121 @@
     copts = [
         "-fno-addrsig",
         "-I.",
+        "-I$(BINDIR)/.",
     ],
     deps = [":bar"],
     srcs = ["a/b/c.c"],
 )`,
-			},
 		},
-		{
-			description:                        "cc_object with include_build_dir: false",
-			moduleTypeUnderTest:                "cc_object",
-			moduleTypeUnderTestFactory:         cc.ObjectFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
-			filesystem: map[string]string{
-				"a/b/c.c": "",
-				"x/y/z.c": "",
-			},
-			blueprint: `cc_object {
+	})
+}
+
+func TestCcObjectIncludeBuildDirFalse(t *testing.T) {
+	runCcObjectTestCase(t, bp2buildTestCase{
+		description:                        "cc_object with include_build_dir: false",
+		moduleTypeUnderTest:                "cc_object",
+		moduleTypeUnderTestFactory:         cc.ObjectFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
+		filesystem: map[string]string{
+			"a/b/c.c": "",
+			"x/y/z.c": "",
+		},
+		blueprint: `cc_object {
     name: "foo",
+    system_shared_libs: [],
     srcs: ["a/b/c.c"],
     include_build_directory: false,
 }
 `,
-			expectedBazelTargets: []string{`cc_object(
+		expectedBazelTargets: []string{`cc_object(
     name = "foo",
     copts = ["-fno-addrsig"],
     srcs = ["a/b/c.c"],
 )`,
-			},
 		},
-		{
-			description:                        "cc_object with product variable",
-			moduleTypeUnderTest:                "cc_object",
-			moduleTypeUnderTestFactory:         cc.ObjectFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
-			blueprint: `cc_object {
+	})
+}
+
+func TestCcObjectProductVariable(t *testing.T) {
+	runCcObjectTestCase(t, bp2buildTestCase{
+		description:                        "cc_object with product variable",
+		moduleTypeUnderTest:                "cc_object",
+		moduleTypeUnderTestFactory:         cc.ObjectFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
+		blueprint: `cc_object {
     name: "foo",
+    system_shared_libs: [],
     include_build_directory: false,
     product_variables: {
         platform_sdk_version: {
             asflags: ["-DPLATFORM_SDK_VERSION=%d"],
         },
     },
+    srcs: ["src.S"],
 }
 `,
-			expectedBazelTargets: []string{`cc_object(
+		expectedBazelTargets: []string{`cc_object(
     name = "foo",
-    asflags = ["-DPLATFORM_SDK_VERSION={Platform_sdk_version}"],
+    asflags = select({
+        "//build/bazel/product_variables:platform_sdk_version": ["-DPLATFORM_SDK_VERSION=$(Platform_sdk_version)"],
+        "//conditions:default": [],
+    }),
     copts = ["-fno-addrsig"],
+    srcs_as = ["src.S"],
 )`,
-			},
 		},
-	}
-
-	dir := "."
-	for _, testCase := range testCases {
-		filesystem := make(map[string][]byte)
-		toParse := []string{
-			"Android.bp",
-		}
-		for f, content := range testCase.filesystem {
-			if strings.HasSuffix(f, "Android.bp") {
-				toParse = append(toParse, f)
-			}
-			filesystem[f] = []byte(content)
-		}
-		config := android.TestConfig(buildDir, nil, testCase.blueprint, filesystem)
-		ctx := android.NewTestContext(config)
-		// Always register cc_defaults module factory
-		ctx.RegisterModuleType("cc_defaults", func() android.Module { return cc.DefaultsFactory() })
-
-		ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
-		ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
-		ctx.RegisterBp2BuildConfig(bp2buildConfig)
-		ctx.RegisterForBazelConversion()
-
-		_, errs := ctx.ParseFileList(dir, toParse)
-		if Errored(t, testCase.description, errs) {
-			continue
-		}
-		_, errs = ctx.ResolveDependencies(config)
-		if Errored(t, testCase.description, errs) {
-			continue
-		}
-
-		codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
-		bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
-		if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
-			fmt.Println(bazelTargets)
-			t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount)
-		} else {
-			for i, target := range bazelTargets {
-				if w, g := testCase.expectedBazelTargets[i], target.content; w != g {
-					t.Errorf(
-						"%s: Expected generated Bazel target to be '%s', got '%s'",
-						testCase.description,
-						w,
-						g,
-					)
-				}
-			}
-		}
-	}
+	})
 }
 
-func TestCcObjectConfigurableAttributesBp2Build(t *testing.T) {
-	testCases := []struct {
-		description                        string
-		moduleTypeUnderTest                string
-		moduleTypeUnderTestFactory         android.ModuleFactory
-		moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext)
-		blueprint                          string
-		expectedBazelTargets               []string
-		filesystem                         map[string]string
-	}{
-		{
-			description:                        "cc_object setting cflags for one arch",
-			moduleTypeUnderTest:                "cc_object",
-			moduleTypeUnderTestFactory:         cc.ObjectFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
-			blueprint: `cc_object {
+func TestCcObjectCflagsOneArch(t *testing.T) {
+	runCcObjectTestCase(t, bp2buildTestCase{
+		description:                        "cc_object setting cflags for one arch",
+		moduleTypeUnderTest:                "cc_object",
+		moduleTypeUnderTestFactory:         cc.ObjectFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
+		blueprint: `cc_object {
     name: "foo",
+    system_shared_libs: [],
     srcs: ["a.cpp"],
     arch: {
         x86: {
             cflags: ["-fPIC"], // string list
         },
         arm: {
-            srcs: ["arch/arm/file.S"], // label list
+            srcs: ["arch/arm/file.cpp"], // label list
         },
     },
 }
 `,
-			expectedBazelTargets: []string{
-				`cc_object(
+		expectedBazelTargets: []string{
+			`cc_object(
     name = "foo",
     copts = [
         "-fno-addrsig",
         "-I.",
+        "-I$(BINDIR)/.",
     ] + select({
         "//build/bazel/platforms/arch:x86": ["-fPIC"],
         "//conditions:default": [],
     }),
     srcs = ["a.cpp"] + select({
-        "//build/bazel/platforms/arch:arm": ["arch/arm/file.S"],
+        "//build/bazel/platforms/arch:arm": ["arch/arm/file.cpp"],
         "//conditions:default": [],
     }),
 )`,
-			},
 		},
-		{
-			description:                        "cc_object setting cflags for 4 architectures",
-			moduleTypeUnderTest:                "cc_object",
-			moduleTypeUnderTestFactory:         cc.ObjectFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
-			blueprint: `cc_object {
+	})
+}
+
+func TestCcObjectCflagsFourArch(t *testing.T) {
+	runCcObjectTestCase(t, bp2buildTestCase{
+		description:                        "cc_object setting cflags for 4 architectures",
+		moduleTypeUnderTest:                "cc_object",
+		moduleTypeUnderTestFactory:         cc.ObjectFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
+		blueprint: `cc_object {
     name: "foo",
+    system_shared_libs: [],
     srcs: ["base.cpp"],
     arch: {
         x86: {
@@ -323,12 +297,13 @@
     },
 }
 `,
-			expectedBazelTargets: []string{
-				`cc_object(
+		expectedBazelTargets: []string{
+			`cc_object(
     name = "foo",
     copts = [
         "-fno-addrsig",
         "-I.",
+        "-I$(BINDIR)/.",
     ] + select({
         "//build/bazel/platforms/arch:arm": ["-Wall"],
         "//build/bazel/platforms/arch:arm64": ["-Wall"],
@@ -344,15 +319,19 @@
         "//conditions:default": [],
     }),
 )`,
-			},
 		},
-		{
-			description:                        "cc_object setting cflags for multiple OSes",
-			moduleTypeUnderTest:                "cc_object",
-			moduleTypeUnderTestFactory:         cc.ObjectFactory,
-			moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
-			blueprint: `cc_object {
+	})
+}
+
+func TestCcObjectCflagsMultiOs(t *testing.T) {
+	runCcObjectTestCase(t, bp2buildTestCase{
+		description:                        "cc_object setting cflags for multiple OSes",
+		moduleTypeUnderTest:                "cc_object",
+		moduleTypeUnderTestFactory:         cc.ObjectFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
+		blueprint: `cc_object {
     name: "foo",
+    system_shared_libs: [],
     srcs: ["base.cpp"],
     target: {
         android: {
@@ -367,12 +346,13 @@
     },
 }
 `,
-			expectedBazelTargets: []string{
-				`cc_object(
+		expectedBazelTargets: []string{
+			`cc_object(
     name = "foo",
     copts = [
         "-fno-addrsig",
         "-I.",
+        "-I$(BINDIR)/.",
     ] + select({
         "//build/bazel/platforms/os:android": ["-fPIC"],
         "//build/bazel/platforms/os:darwin": ["-Wall"],
@@ -381,51 +361,6 @@
     }),
     srcs = ["base.cpp"],
 )`,
-			},
 		},
-	}
-
-	dir := "."
-	for _, testCase := range testCases {
-		filesystem := make(map[string][]byte)
-		toParse := []string{
-			"Android.bp",
-		}
-		config := android.TestConfig(buildDir, nil, testCase.blueprint, filesystem)
-		ctx := android.NewTestContext(config)
-		// Always register cc_defaults module factory
-		ctx.RegisterModuleType("cc_defaults", func() android.Module { return cc.DefaultsFactory() })
-
-		ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
-		ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
-		ctx.RegisterBp2BuildConfig(bp2buildConfig)
-		ctx.RegisterForBazelConversion()
-
-		_, errs := ctx.ParseFileList(dir, toParse)
-		if Errored(t, testCase.description, errs) {
-			continue
-		}
-		_, errs = ctx.ResolveDependencies(config)
-		if Errored(t, testCase.description, errs) {
-			continue
-		}
-
-		codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
-		bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
-		if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
-			fmt.Println(bazelTargets)
-			t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount)
-		} else {
-			for i, target := range bazelTargets {
-				if w, g := testCase.expectedBazelTargets[i], target.content; w != g {
-					t.Errorf(
-						"%s: Expected generated Bazel target to be '%s', got '%s'",
-						testCase.description,
-						w,
-						g,
-					)
-				}
-			}
-		}
-	}
+	})
 }
diff --git a/bp2build/compatibility.go b/bp2build/compatibility.go
new file mode 100644
index 0000000..5baa524
--- /dev/null
+++ b/bp2build/compatibility.go
@@ -0,0 +1,31 @@
+package bp2build
+
+import (
+	"android/soong/bazel"
+	"fmt"
+)
+
+// Data from the code generation process that is used to improve compatibility
+// between build systems.
+type CodegenCompatLayer struct {
+	// A map from the original module name to the generated/handcrafted Bazel
+	// label for legacy build systems to be able to build a fully-qualified
+	// Bazel target from an unique module name.
+	NameToLabelMap map[string]string
+}
+
+// Log an entry of module name -> Bazel target label.
+func (compatLayer CodegenCompatLayer) AddNameToLabelEntry(name, label string) {
+	// The module name may be prefixed with bazel.BazelTargetModuleNamePrefix if
+	// generated from bp2build.
+	name = bazel.StripNamePrefix(name)
+	if existingLabel, ok := compatLayer.NameToLabelMap[name]; ok {
+		panic(fmt.Errorf(
+			"Module '%s' maps to more than one Bazel target label: %s, %s. "+
+				"This shouldn't happen. It probably indicates a bug with the bp2build internals.",
+			name,
+			existingLabel,
+			label))
+	}
+	compatLayer.NameToLabelMap[name] = label
+}
diff --git a/bp2build/configurability.go b/bp2build/configurability.go
index 2b8f6cc..005f13d 100644
--- a/bp2build/configurability.go
+++ b/bp2build/configurability.go
@@ -11,80 +11,141 @@
 
 type selects map[string]reflect.Value
 
-func getStringListValues(list bazel.StringListAttribute) (reflect.Value, selects, selects) {
+func getStringListValues(list bazel.StringListAttribute) (reflect.Value, []selects) {
 	value := reflect.ValueOf(list.Value)
 	if !list.HasConfigurableValues() {
-		return value, nil, nil
+		return value, []selects{}
 	}
 
-	archSelects := map[string]reflect.Value{}
-	for arch, selectKey := range bazel.PlatformArchMap {
-		archSelects[selectKey] = reflect.ValueOf(list.GetValueForArch(arch))
-	}
-
-	osSelects := map[string]reflect.Value{}
-	for os, selectKey := range bazel.PlatformOsMap {
-		osSelects[selectKey] = reflect.ValueOf(list.GetValueForOS(os))
-	}
-
-	return value, archSelects, osSelects
-}
-
-func getLabelValue(label bazel.LabelAttribute) (reflect.Value, selects, selects) {
-	var value reflect.Value
-	var archSelects selects
-
-	if label.HasConfigurableValues() {
-		archSelects = map[string]reflect.Value{}
-		for arch, selectKey := range bazel.PlatformArchMap {
-			archSelects[selectKey] = reflect.ValueOf(label.GetValueForArch(arch))
+	var ret []selects
+	for _, axis := range list.SortedConfigurationAxes() {
+		configToLists := list.ConfigurableValues[axis]
+		archSelects := map[string]reflect.Value{}
+		for config, labels := range configToLists {
+			selectKey := axis.SelectKey(config)
+			archSelects[selectKey] = reflect.ValueOf(labels)
 		}
-	} else {
-		value = reflect.ValueOf(label.Value)
+		if len(archSelects) > 0 {
+			ret = append(ret, archSelects)
+		}
 	}
 
-	return value, archSelects, nil
+	return value, ret
 }
 
-func getLabelListValues(list bazel.LabelListAttribute) (reflect.Value, selects, selects) {
+func getLabelValue(label bazel.LabelAttribute) (reflect.Value, []selects) {
+	value := reflect.ValueOf(label.Value)
+	if !label.HasConfigurableValues() {
+		return value, []selects{}
+	}
+
+	ret := selects{}
+	for _, axis := range label.SortedConfigurationAxes() {
+		configToLabels := label.ConfigurableValues[axis]
+		for config, labels := range configToLabels {
+			selectKey := axis.SelectKey(config)
+			ret[selectKey] = reflect.ValueOf(labels)
+		}
+	}
+
+	return value, []selects{ret}
+}
+
+func getBoolValue(boolAttr bazel.BoolAttribute) (reflect.Value, []selects) {
+	value := reflect.ValueOf(boolAttr.Value)
+	if !boolAttr.HasConfigurableValues() {
+		return value, []selects{}
+	}
+
+	ret := selects{}
+	for _, axis := range boolAttr.SortedConfigurationAxes() {
+		configToBools := boolAttr.ConfigurableValues[axis]
+		for config, bools := range configToBools {
+			selectKey := axis.SelectKey(config)
+			ret[selectKey] = reflect.ValueOf(bools)
+		}
+	}
+	// if there is a select, use the base value as the conditions default value
+	if len(ret) > 0 {
+		ret[bazel.ConditionsDefaultSelectKey] = value
+		value = reflect.Zero(value.Type())
+	}
+
+	return value, []selects{ret}
+}
+func getLabelListValues(list bazel.LabelListAttribute) (reflect.Value, []selects) {
 	value := reflect.ValueOf(list.Value.Includes)
-	if !list.HasConfigurableValues() {
-		return value, nil, nil
+	var ret []selects
+	for _, axis := range list.SortedConfigurationAxes() {
+		configToLabels := list.ConfigurableValues[axis]
+		if !configToLabels.HasConfigurableValues() {
+			continue
+		}
+		archSelects := map[string]reflect.Value{}
+		defaultVal := configToLabels[bazel.ConditionsDefaultConfigKey]
+		for config, labels := range configToLabels {
+			// Omit any entries in the map which match the default value, for brevity.
+			if config != bazel.ConditionsDefaultConfigKey && labels.Equals(defaultVal) {
+				continue
+			}
+			selectKey := axis.SelectKey(config)
+			if use, value := labelListSelectValue(selectKey, labels); use {
+				archSelects[selectKey] = value
+			}
+		}
+		if len(archSelects) > 0 {
+			ret = append(ret, archSelects)
+		}
 	}
 
-	archSelects := map[string]reflect.Value{}
-	for arch, selectKey := range bazel.PlatformArchMap {
-		archSelects[selectKey] = reflect.ValueOf(list.GetValueForArch(arch).Includes)
-	}
-
-	osSelects := map[string]reflect.Value{}
-	for os, selectKey := range bazel.PlatformOsMap {
-		osSelects[selectKey] = reflect.ValueOf(list.GetValueForOS(os).Includes)
-	}
-
-	return value, archSelects, osSelects
+	return value, ret
 }
 
+func labelListSelectValue(selectKey string, list bazel.LabelList) (bool, reflect.Value) {
+	if selectKey == bazel.ConditionsDefaultSelectKey || len(list.Includes) > 0 {
+		return true, reflect.ValueOf(list.Includes)
+	} else if len(list.Excludes) > 0 {
+		// if there is still an excludes -- we need to have an empty list for this select & use the
+		// value in conditions default Includes
+		return true, reflect.ValueOf([]string{})
+	}
+	return false, reflect.Zero(reflect.TypeOf([]string{}))
+}
+
+var (
+	emptyBazelList = "[]"
+	bazelNone      = "None"
+)
+
 // prettyPrintAttribute converts an Attribute to its Bazel syntax. May contain
 // select statements.
 func prettyPrintAttribute(v bazel.Attribute, indent int) (string, error) {
 	var value reflect.Value
-	var archSelects, osSelects selects
-	var defaultSelectValue string
+	var configurableAttrs []selects
+	var defaultSelectValue *string
+	// If true, print the default attribute value, even if the attribute is zero.
+	shouldPrintDefault := false
 	switch list := v.(type) {
 	case bazel.StringListAttribute:
-		value, archSelects, osSelects = getStringListValues(list)
-		defaultSelectValue = "[]"
+		value, configurableAttrs = getStringListValues(list)
+		defaultSelectValue = &emptyBazelList
 	case bazel.LabelListAttribute:
-		value, archSelects, osSelects = getLabelListValues(list)
-		defaultSelectValue = "[]"
+		value, configurableAttrs = getLabelListValues(list)
+		defaultSelectValue = &emptyBazelList
+		if list.ForceSpecifyEmptyList && (!value.IsNil() || list.HasConfigurableValues()) {
+			shouldPrintDefault = true
+		}
 	case bazel.LabelAttribute:
-		value, archSelects, osSelects = getLabelValue(list)
-		defaultSelectValue = "None"
+		value, configurableAttrs = getLabelValue(list)
+		defaultSelectValue = &bazelNone
+	case bazel.BoolAttribute:
+		value, configurableAttrs = getBoolValue(list)
+		defaultSelectValue = &bazelNone
 	default:
 		return "", fmt.Errorf("Not a supported Bazel attribute type: %s", v)
 	}
 
+	var err error
 	ret := ""
 	if value.Kind() != reflect.Invalid {
 		s, err := prettyPrint(value, indent)
@@ -95,7 +156,7 @@
 		ret += s
 	}
 	// Convenience function to append selects components to an attribute value.
-	appendSelects := func(selectsData selects, defaultValue, s string) (string, error) {
+	appendSelects := func(selectsData selects, defaultValue *string, s string) (string, error) {
 		selectMap, err := prettyPrintSelectMap(selectsData, defaultValue, indent)
 		if err != nil {
 			return "", err
@@ -108,28 +169,29 @@
 		return s, nil
 	}
 
-	ret, err := appendSelects(archSelects, defaultSelectValue, ret)
-	if err != nil {
-		return "", err
+	for _, configurableAttr := range configurableAttrs {
+		ret, err = appendSelects(configurableAttr, defaultSelectValue, ret)
+		if err != nil {
+			return "", err
+		}
 	}
 
-	ret, err = appendSelects(osSelects, defaultSelectValue, ret)
-	return ret, err
+	if ret == "" && shouldPrintDefault {
+		return *defaultSelectValue, nil
+	}
+	return ret, nil
 }
 
 // prettyPrintSelectMap converts a map of select keys to reflected Values as a generic way
 // to construct a select map for any kind of attribute type.
-func prettyPrintSelectMap(selectMap map[string]reflect.Value, defaultValue string, indent int) (string, error) {
+func prettyPrintSelectMap(selectMap map[string]reflect.Value, defaultValue *string, indent int) (string, error) {
 	if selectMap == nil {
 		return "", nil
 	}
 
-	// addConditionsDefault := false
-	conditionsDefaultKey := bazel.PlatformArchMap[bazel.CONDITIONS_DEFAULT]
-
 	var selects string
 	for _, selectKey := range android.SortedStringKeys(selectMap) {
-		if selectKey == conditionsDefaultKey {
+		if selectKey == bazel.ConditionsDefaultSelectKey {
 			// Handle default condition later.
 			continue
 		}
@@ -159,18 +221,18 @@
 	ret += selects
 
 	// Handle the default condition
-	s, err := prettyPrintSelectEntry(selectMap[conditionsDefaultKey], conditionsDefaultKey, indent)
+	s, err := prettyPrintSelectEntry(selectMap[bazel.ConditionsDefaultSelectKey], bazel.ConditionsDefaultSelectKey, indent)
 	if err != nil {
 		return "", err
 	}
-	if s == "" {
-		// Print an explicit empty list (the default value) even if the value is
-		// empty, to avoid errors about not finding a configuration that matches.
-		ret += fmt.Sprintf("%s\"%s\": %s,\n", makeIndent(indent+1), "//conditions:default", defaultValue)
-	} else {
+	if s != "" {
 		// Print the custom default value.
 		ret += s
 		ret += ",\n"
+	} else if defaultValue != nil {
+		// Print an explicit empty list (the default value) even if the value is
+		// empty, to avoid errors about not finding a configuration that matches.
+		ret += fmt.Sprintf("%s\"%s\": %s,\n", makeIndent(indent+1), bazel.ConditionsDefaultSelectKey, *defaultValue)
 	}
 
 	ret += makeIndent(indent)
diff --git a/bp2build/constants.go b/bp2build/constants.go
index 70f320e..4870dff 100644
--- a/bp2build/constants.go
+++ b/bp2build/constants.go
@@ -19,7 +19,10 @@
 	// be preferred for use within a Bazel build.
 
 	// The file name used for automatically generated files.
-	GeneratedBuildFileName = "BUILD"
+	GeneratedBuildFileName = "BUILD.bazel"
+
 	// The file name used for hand-crafted build targets.
+	// NOTE: It is okay that this matches GeneratedBuildFileName, since we generate BUILD files in a different directory to source files
+	// FIXME: Because there are hundreds of existing BUILD.bazel files in the AOSP tree, we should pick another name here, like BUILD.android
 	HandcraftedBuildFileName = "BUILD.bazel"
 )
diff --git a/bp2build/conversion.go b/bp2build/conversion.go
index eb83b38..75bc2b4 100644
--- a/bp2build/conversion.go
+++ b/bp2build/conversion.go
@@ -5,7 +5,6 @@
 	"android/soong/cc/config"
 	"fmt"
 	"reflect"
-	"sort"
 	"strings"
 
 	"github.com/google/blueprint/proptools"
@@ -17,15 +16,31 @@
 	Contents string
 }
 
-func CreateSoongInjectionFiles() []BazelFile {
+func CreateSoongInjectionFiles(compatLayer CodegenCompatLayer) []BazelFile {
 	var files []BazelFile
 
-	files = append(files, newFile("cc_toolchain", "BUILD", "")) // Creates a //cc_toolchain package.
+	files = append(files, newFile("cc_toolchain", GeneratedBuildFileName, "")) // Creates a //cc_toolchain package.
 	files = append(files, newFile("cc_toolchain", "constants.bzl", config.BazelCcToolchainVars()))
 
+	files = append(files, newFile("module_name_to_label", GeneratedBuildFileName, nameToLabelAliases(compatLayer.NameToLabelMap)))
+
 	return files
 }
 
+func nameToLabelAliases(nameToLabelMap map[string]string) string {
+	ret := make([]string, len(nameToLabelMap))
+
+	for k, v := range nameToLabelMap {
+		// v is the fully qualified label rooted at '//'
+		ret = append(ret, fmt.Sprintf(
+			`alias(
+    name = "%s",
+    actual = "@%s",
+)`, k, v))
+	}
+	return strings.Join(ret, "\n\n")
+}
+
 func CreateBazelFiles(
 	ruleShims map[string]RuleShim,
 	buildToTargets map[string]BazelTargets,
@@ -59,27 +74,33 @@
 func createBuildFiles(buildToTargets map[string]BazelTargets, mode CodegenMode) []BazelFile {
 	files := make([]BazelFile, 0, len(buildToTargets))
 	for _, dir := range android.SortedStringKeys(buildToTargets) {
-		if mode == Bp2Build && !android.ShouldWriteBuildFileForDir(dir) {
+		if mode == Bp2Build && android.ShouldKeepExistingBuildFileForDir(dir) {
 			fmt.Printf("[bp2build] Not writing generated BUILD file for dir: '%s'\n", dir)
 			continue
 		}
 		targets := buildToTargets[dir]
-		sort.Slice(targets, func(i, j int) bool {
-			// this will cover all bp2build generated targets
-			if targets[i].name < targets[j].name {
-				return true
-			}
-			// give a strict ordering to content from hand-crafted targets
-			return targets[i].content < targets[j].content
-		})
-		content := soongModuleLoad
+		targets.sort()
+
+		var content string
 		if mode == Bp2Build {
-			content = `# This file was automatically generated by bp2build for the Bazel migration project.
-# Feel free to edit or test it, but do *not* check it into your version control system.`
-			content += "\n\n"
-			content += "package(default_visibility = [\"//visibility:public\"])"
-			content += "\n\n"
+			content = `# READ THIS FIRST:
+# This file was automatically generated by bp2build for the Bazel migration project.
+# Feel free to edit or test it, but do *not* check it into your version control system.
+`
+			if targets.hasHandcraftedTargets() {
+				// For BUILD files with both handcrafted and generated targets,
+				// don't hardcode actual content, like package() declarations.
+				// Leave that responsibility to the checked-in BUILD file
+				// instead.
+				content += `# This file contains generated targets and handcrafted targets that are manually managed in the source tree.`
+			} else {
+				// For fully-generated BUILD files, hardcode the default visibility.
+				content += "package(default_visibility = [\"//visibility:public\"])"
+			}
+			content += "\n"
 			content += targets.LoadStatements()
+		} else if mode == QueryView {
+			content = soongModuleLoad
 		}
 		if content != "" {
 			// If there are load statements, add a couple of newlines.
diff --git a/bp2build/conversion_test.go b/bp2build/conversion_test.go
index a08c03d..56ea589 100644
--- a/bp2build/conversion_test.go
+++ b/bp2build/conversion_test.go
@@ -29,7 +29,7 @@
 	expectedFilePaths := []bazelFilepath{
 		{
 			dir:      "",
-			basename: "BUILD",
+			basename: "BUILD.bazel",
 		},
 		{
 			dir:      "",
@@ -37,7 +37,7 @@
 		},
 		{
 			dir:      bazelRulesSubDir,
-			basename: "BUILD",
+			basename: "BUILD.bazel",
 		},
 		{
 			dir:      bazelRulesSubDir,
@@ -69,7 +69,7 @@
 
 		if actualFile.Dir != expectedFile.dir || actualFile.Basename != expectedFile.basename {
 			t.Errorf("Did not find expected file %s/%s", actualFile.Dir, actualFile.Basename)
-		} else if actualFile.Basename == "BUILD" || actualFile.Basename == "WORKSPACE" {
+		} else if actualFile.Basename == "BUILD.bazel" || actualFile.Basename == "WORKSPACE" {
 			if actualFile.Contents != "" {
 				t.Errorf("Expected %s to have no content.", actualFile)
 			}
@@ -80,17 +80,21 @@
 }
 
 func TestCreateBazelFiles_Bp2Build_CreatesDefaultFiles(t *testing.T) {
-	files := CreateSoongInjectionFiles()
+	files := CreateSoongInjectionFiles(CodegenCompatLayer{})
 
 	expectedFilePaths := []bazelFilepath{
 		{
 			dir:      "cc_toolchain",
-			basename: "BUILD",
+			basename: GeneratedBuildFileName,
 		},
 		{
 			dir:      "cc_toolchain",
 			basename: "constants.bzl",
 		},
+		{
+			dir:      "module_name_to_label",
+			basename: GeneratedBuildFileName,
+		},
 	}
 
 	if len(files) != len(expectedFilePaths) {
@@ -104,7 +108,7 @@
 			t.Errorf("Did not find expected file %s/%s", actualFile.Dir, actualFile.Basename)
 		}
 
-		if expectedFile.basename != "BUILD" && actualFile.Contents == "" {
+		if expectedFile.basename != GeneratedBuildFileName && actualFile.Contents == "" {
 			t.Errorf("Contents of %s unexpected empty.", actualFile)
 		}
 	}
diff --git a/bp2build/performance_test.go b/bp2build/performance_test.go
new file mode 100644
index 0000000..3283952
--- /dev/null
+++ b/bp2build/performance_test.go
@@ -0,0 +1,175 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package bp2build
+
+// to run the benchmarks in this file, you must run go test with the -bench.
+// The benchmarked portion will run for the specified time (can be set via -benchtime)
+// This can mean if you are benchmarking a faster portion of a larger operation, it will take
+// longer.
+// If you are seeing a small number of iterations for a specific run, the data is less reliable, to
+// run for longer, set -benchtime to a larger value.
+
+import (
+	"android/soong/android"
+	"fmt"
+	"math"
+	"strings"
+	"testing"
+)
+
+func genCustomModule(i int, convert bool) string {
+	var conversionString string
+	if convert {
+		conversionString = `bazel_module: { bp2build_available: true },`
+	}
+	return fmt.Sprintf(`
+custom {
+    name: "arch_paths_%[1]d",
+    string_list_prop: ["\t", "\n"],
+    string_prop: "a\t\n\r",
+    arch_paths: ["outer", ":outer_dep_%[1]d"],
+    arch: {
+      x86: {
+        arch_paths: ["abc", ":x86_dep_%[1]d"],
+      },
+      x86_64: {
+        arch_paths: ["64bit"],
+        arch_paths_exclude: ["outer"],
+      },
+    },
+		%[2]s
+}
+
+custom {
+    name: "outer_dep_%[1]d",
+		%[2]s
+}
+
+custom {
+    name: "x86_dep_%[1]d",
+		%[2]s
+}
+`, i, conversionString)
+}
+
+func genCustomModuleBp(pctConverted float64) string {
+	modules := 100
+
+	bp := make([]string, 0, modules)
+	toConvert := int(math.Round(float64(modules) * pctConverted))
+
+	for i := 0; i < modules; i++ {
+		bp = append(bp, genCustomModule(i, i < toConvert))
+	}
+	return strings.Join(bp, "\n\n")
+}
+
+var pctToConvert = []float64{0.0, 0.01, 0.05, 0.10, 0.25, 0.5, 0.75, 1.0}
+
+func BenchmarkManyModulesFull(b *testing.B) {
+	dir := "."
+	for _, tcSize := range pctToConvert {
+
+		b.Run(fmt.Sprintf("pctConverted %f", tcSize), func(b *testing.B) {
+			for n := 0; n < b.N; n++ {
+				b.StopTimer()
+				// setup we don't want to measure
+				config := android.TestConfig(buildDir, nil, genCustomModuleBp(tcSize), nil)
+				ctx := android.NewTestContext(config)
+
+				registerCustomModuleForBp2buildConversion(ctx)
+				codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+
+				b.StartTimer()
+				_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
+				if len(errs) > 0 {
+					b.Fatalf("Unexpected errors: %s", errs)
+				}
+
+				_, errs = ctx.ResolveDependencies(config)
+				if len(errs) > 0 {
+					b.Fatalf("Unexpected errors: %s", errs)
+				}
+
+				generateBazelTargetsForDir(codegenCtx, dir)
+				b.StopTimer()
+			}
+		})
+	}
+}
+
+func BenchmarkManyModulesResolveDependencies(b *testing.B) {
+	dir := "."
+	for _, tcSize := range pctToConvert {
+
+		b.Run(fmt.Sprintf("pctConverted %f", tcSize), func(b *testing.B) {
+			for n := 0; n < b.N; n++ {
+				b.StopTimer()
+				// setup we don't want to measure
+				config := android.TestConfig(buildDir, nil, genCustomModuleBp(tcSize), nil)
+				ctx := android.NewTestContext(config)
+
+				registerCustomModuleForBp2buildConversion(ctx)
+				codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+
+				_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
+				if len(errs) > 0 {
+					b.Fatalf("Unexpected errors: %s", errs)
+				}
+
+				b.StartTimer()
+				_, errs = ctx.ResolveDependencies(config)
+				b.StopTimer()
+				if len(errs) > 0 {
+					b.Fatalf("Unexpected errors: %s", errs)
+				}
+
+				generateBazelTargetsForDir(codegenCtx, dir)
+			}
+		})
+	}
+}
+
+func BenchmarkManyModulesGenerateBazelTargetsForDir(b *testing.B) {
+	dir := "."
+	for _, tcSize := range pctToConvert {
+
+		b.Run(fmt.Sprintf("pctConverted %f", tcSize), func(b *testing.B) {
+			for n := 0; n < b.N; n++ {
+				b.StopTimer()
+				// setup we don't want to measure
+				config := android.TestConfig(buildDir, nil, genCustomModuleBp(tcSize), nil)
+				ctx := android.NewTestContext(config)
+
+				registerCustomModuleForBp2buildConversion(ctx)
+				codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+
+				_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
+				if len(errs) > 0 {
+					b.Fatalf("Unexpected errors: %s", errs)
+				}
+
+				_, errs = ctx.ResolveDependencies(config)
+				if len(errs) > 0 {
+					b.Fatalf("Unexpected errors: %s", errs)
+				}
+
+				b.StartTimer()
+				generateBazelTargetsForDir(codegenCtx, dir)
+				b.StopTimer()
+			}
+		})
+	}
+}
diff --git a/bp2build/prebuilt_etc_conversion_test.go b/bp2build/prebuilt_etc_conversion_test.go
new file mode 100644
index 0000000..4e25d1b
--- /dev/null
+++ b/bp2build/prebuilt_etc_conversion_test.go
@@ -0,0 +1,55 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package bp2build
+
+import (
+	"android/soong/android"
+	"android/soong/etc"
+
+	"testing"
+)
+
+func runPrebuiltEtcTestCase(t *testing.T, tc bp2buildTestCase) {
+	t.Helper()
+	runBp2BuildTestCase(t, registerPrebuiltEtcModuleTypes, tc)
+}
+
+func registerPrebuiltEtcModuleTypes(ctx android.RegistrationContext) {
+}
+
+func TestPrebuiltEtcSimple(t *testing.T) {
+	runPrebuiltEtcTestCase(t, bp2buildTestCase{
+		description:                        "prebuilt_etc - simple example",
+		moduleTypeUnderTest:                "prebuilt_etc",
+		moduleTypeUnderTestFactory:         etc.PrebuiltEtcFactory,
+		moduleTypeUnderTestBp2BuildMutator: etc.PrebuiltEtcBp2Build,
+		filesystem:                         map[string]string{},
+		blueprint: `
+prebuilt_etc {
+    name: "apex_tz_version",
+    src: "version/tz_version",
+    filename: "tz_version",
+    sub_dir: "tz",
+    installable: false,
+}
+`,
+		expectedBazelTargets: []string{`prebuilt_etc(
+    name = "apex_tz_version",
+    filename = "tz_version",
+    installable = False,
+    src = "version/tz_version",
+    sub_dir = "tz",
+)`}})
+}
diff --git a/bp2build/python_binary_conversion_test.go b/bp2build/python_binary_conversion_test.go
index 2054e06..6f6fc11 100644
--- a/bp2build/python_binary_conversion_test.go
+++ b/bp2build/python_binary_conversion_test.go
@@ -1,47 +1,50 @@
 package bp2build
 
 import (
+	"testing"
+
 	"android/soong/android"
 	"android/soong/python"
-	"fmt"
-	"strings"
-	"testing"
 )
 
-func TestPythonBinaryHost(t *testing.T) {
-	testCases := []struct {
-		description                        string
-		moduleTypeUnderTest                string
-		moduleTypeUnderTestFactory         android.ModuleFactory
-		moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext)
-		blueprint                          string
-		expectedBazelTargets               []string
-		filesystem                         map[string]string
-	}{
-		{
-			description:                        "simple python_binary_host converts to a native py_binary",
-			moduleTypeUnderTest:                "python_binary_host",
-			moduleTypeUnderTestFactory:         python.PythonBinaryHostFactory,
-			moduleTypeUnderTestBp2BuildMutator: python.PythonBinaryBp2Build,
-			filesystem: map[string]string{
-				"a.py":           "",
-				"b/c.py":         "",
-				"b/d.py":         "",
-				"b/e.py":         "",
-				"files/data.txt": "",
-			},
-			blueprint: `python_binary_host {
+func runBp2BuildTestCaseWithLibs(t *testing.T, tc bp2buildTestCase) {
+	runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {
+		ctx.RegisterModuleType("python_library", python.PythonLibraryFactory)
+		ctx.RegisterModuleType("python_library_host", python.PythonLibraryHostFactory)
+	}, tc)
+}
+
+func TestPythonBinaryHostSimple(t *testing.T) {
+	runBp2BuildTestCaseWithLibs(t, bp2buildTestCase{
+		description:                        "simple python_binary_host converts to a native py_binary",
+		moduleTypeUnderTest:                "python_binary_host",
+		moduleTypeUnderTestFactory:         python.PythonBinaryHostFactory,
+		moduleTypeUnderTestBp2BuildMutator: python.PythonBinaryBp2Build,
+		filesystem: map[string]string{
+			"a.py":           "",
+			"b/c.py":         "",
+			"b/d.py":         "",
+			"b/e.py":         "",
+			"files/data.txt": "",
+		},
+		blueprint: `python_binary_host {
     name: "foo",
     main: "a.py",
     srcs: ["**/*.py"],
     exclude_srcs: ["b/e.py"],
     data: ["files/data.txt",],
+    libs: ["bar"],
     bazel_module: { bp2build_available: true },
 }
-`,
-			expectedBazelTargets: []string{`py_binary(
+    python_library_host {
+      name: "bar",
+      srcs: ["b/e.py"],
+      bazel_module: { bp2build_available: true },
+    }`,
+		expectedBazelTargets: []string{`py_binary(
     name = "foo",
     data = ["files/data.txt"],
+    deps = [":bar"],
     main = "a.py",
     srcs = [
         "a.py",
@@ -49,14 +52,17 @@
         "b/d.py",
     ],
 )`,
-			},
 		},
-		{
-			description:                        "py2 python_binary_host",
-			moduleTypeUnderTest:                "python_binary_host",
-			moduleTypeUnderTestFactory:         python.PythonBinaryHostFactory,
-			moduleTypeUnderTestBp2BuildMutator: python.PythonBinaryBp2Build,
-			blueprint: `python_binary_host {
+	})
+}
+
+func TestPythonBinaryHostPy2(t *testing.T) {
+	runBp2BuildTestCaseSimple(t, bp2buildTestCase{
+		description:                        "py2 python_binary_host",
+		moduleTypeUnderTest:                "python_binary_host",
+		moduleTypeUnderTestFactory:         python.PythonBinaryHostFactory,
+		moduleTypeUnderTestBp2BuildMutator: python.PythonBinaryBp2Build,
+		blueprint: `python_binary_host {
     name: "foo",
     srcs: ["a.py"],
     version: {
@@ -71,19 +77,22 @@
     bazel_module: { bp2build_available: true },
 }
 `,
-			expectedBazelTargets: []string{`py_binary(
+		expectedBazelTargets: []string{`py_binary(
     name = "foo",
     python_version = "PY2",
     srcs = ["a.py"],
 )`,
-			},
 		},
-		{
-			description:                        "py3 python_binary_host",
-			moduleTypeUnderTest:                "python_binary_host",
-			moduleTypeUnderTestFactory:         python.PythonBinaryHostFactory,
-			moduleTypeUnderTestBp2BuildMutator: python.PythonBinaryBp2Build,
-			blueprint: `python_binary_host {
+	})
+}
+
+func TestPythonBinaryHostPy3(t *testing.T) {
+	runBp2BuildTestCaseSimple(t, bp2buildTestCase{
+		description:                        "py3 python_binary_host",
+		moduleTypeUnderTest:                "python_binary_host",
+		moduleTypeUnderTestFactory:         python.PythonBinaryHostFactory,
+		moduleTypeUnderTestBp2BuildMutator: python.PythonBinaryBp2Build,
+		blueprint: `python_binary_host {
     name: "foo",
     srcs: ["a.py"],
     version: {
@@ -98,60 +107,12 @@
     bazel_module: { bp2build_available: true },
 }
 `,
-			expectedBazelTargets: []string{
-				// python_version is PY3 by default.
-				`py_binary(
+		expectedBazelTargets: []string{
+			// python_version is PY3 by default.
+			`py_binary(
     name = "foo",
     srcs = ["a.py"],
 )`,
-			},
 		},
-	}
-
-	dir := "."
-	for _, testCase := range testCases {
-		filesystem := make(map[string][]byte)
-		toParse := []string{
-			"Android.bp",
-		}
-		for f, content := range testCase.filesystem {
-			if strings.HasSuffix(f, "Android.bp") {
-				toParse = append(toParse, f)
-			}
-			filesystem[f] = []byte(content)
-		}
-		config := android.TestConfig(buildDir, nil, testCase.blueprint, filesystem)
-		ctx := android.NewTestContext(config)
-
-		ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
-		ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
-		ctx.RegisterForBazelConversion()
-
-		_, errs := ctx.ParseFileList(dir, toParse)
-		if Errored(t, testCase.description, errs) {
-			continue
-		}
-		_, errs = ctx.ResolveDependencies(config)
-		if Errored(t, testCase.description, errs) {
-			continue
-		}
-
-		codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
-		bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
-		if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
-			fmt.Println(bazelTargets)
-			t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount)
-		} else {
-			for i, target := range bazelTargets {
-				if w, g := testCase.expectedBazelTargets[i], target.content; w != g {
-					t.Errorf(
-						"%s: Expected generated Bazel target to be '%s', got '%s'",
-						testCase.description,
-						w,
-						g,
-					)
-				}
-			}
-		}
-	}
+	})
 }
diff --git a/bp2build/python_library_conversion_test.go b/bp2build/python_library_conversion_test.go
new file mode 100644
index 0000000..b6f45e5
--- /dev/null
+++ b/bp2build/python_library_conversion_test.go
@@ -0,0 +1,156 @@
+package bp2build
+
+import (
+	"fmt"
+	"testing"
+
+	"android/soong/android"
+	"android/soong/python"
+)
+
+// TODO(alexmarquez): Should be lifted into a generic Bp2Build file
+type PythonLibBp2Build func(ctx android.TopDownMutatorContext)
+
+func TestPythonLibrary(t *testing.T) {
+	testPythonLib(t, "python_library",
+		python.PythonLibraryFactory, python.PythonLibraryBp2Build,
+		func(ctx android.RegistrationContext) {})
+}
+
+func TestPythonLibraryHost(t *testing.T) {
+	testPythonLib(t, "python_library_host",
+		python.PythonLibraryHostFactory, python.PythonLibraryHostBp2Build,
+		func(ctx android.RegistrationContext) {
+			ctx.RegisterModuleType("python_library", python.PythonLibraryFactory)
+		})
+}
+
+func testPythonLib(t *testing.T, modType string,
+	factory android.ModuleFactory, mutator PythonLibBp2Build,
+	registration func(ctx android.RegistrationContext)) {
+	t.Helper()
+	// Simple
+	runBp2BuildTestCase(t, registration, bp2buildTestCase{
+		description:                        fmt.Sprintf("simple %s converts to a native py_library", modType),
+		moduleTypeUnderTest:                modType,
+		moduleTypeUnderTestFactory:         factory,
+		moduleTypeUnderTestBp2BuildMutator: mutator,
+		filesystem: map[string]string{
+			"a.py":           "",
+			"b/c.py":         "",
+			"b/d.py":         "",
+			"b/e.py":         "",
+			"files/data.txt": "",
+		},
+		blueprint: fmt.Sprintf(`%s {
+    name: "foo",
+    srcs: ["**/*.py"],
+    exclude_srcs: ["b/e.py"],
+    data: ["files/data.txt",],
+    libs: ["bar"],
+    bazel_module: { bp2build_available: true },
+}
+    python_library {
+      name: "bar",
+      srcs: ["b/e.py"],
+      bazel_module: { bp2build_available: false },
+    }`, modType),
+		expectedBazelTargets: []string{`py_library(
+    name = "foo",
+    data = ["files/data.txt"],
+    deps = [":bar"],
+    srcs = [
+        "a.py",
+        "b/c.py",
+        "b/d.py",
+    ],
+    srcs_version = "PY3",
+)`,
+		},
+	})
+
+	// PY2
+	runBp2BuildTestCaseSimple(t, bp2buildTestCase{
+		description:                        fmt.Sprintf("py2 %s converts to a native py_library", modType),
+		moduleTypeUnderTest:                modType,
+		moduleTypeUnderTestFactory:         factory,
+		moduleTypeUnderTestBp2BuildMutator: mutator,
+		blueprint: fmt.Sprintf(`%s {
+    name: "foo",
+    srcs: ["a.py"],
+    version: {
+        py2: {
+            enabled: true,
+        },
+        py3: {
+            enabled: false,
+        },
+    },
+
+    bazel_module: { bp2build_available: true },
+}`, modType),
+		expectedBazelTargets: []string{`py_library(
+    name = "foo",
+    srcs = ["a.py"],
+    srcs_version = "PY2",
+)`,
+		},
+	})
+
+	// PY3
+	runBp2BuildTestCaseSimple(t, bp2buildTestCase{
+		description:                        fmt.Sprintf("py3 %s converts to a native py_library", modType),
+		moduleTypeUnderTest:                modType,
+		moduleTypeUnderTestFactory:         factory,
+		moduleTypeUnderTestBp2BuildMutator: mutator,
+		blueprint: fmt.Sprintf(`%s {
+    name: "foo",
+    srcs: ["a.py"],
+    version: {
+        py2: {
+            enabled: false,
+        },
+        py3: {
+            enabled: true,
+        },
+    },
+
+    bazel_module: { bp2build_available: true },
+}`, modType),
+		expectedBazelTargets: []string{`py_library(
+    name = "foo",
+    srcs = ["a.py"],
+    srcs_version = "PY3",
+)`,
+		},
+	})
+
+	// Both
+	runBp2BuildTestCaseSimple(t, bp2buildTestCase{
+		description:                        fmt.Sprintf("py2&3 %s converts to a native py_library", modType),
+		moduleTypeUnderTest:                modType,
+		moduleTypeUnderTestFactory:         factory,
+		moduleTypeUnderTestBp2BuildMutator: mutator,
+		blueprint: fmt.Sprintf(`%s {
+    name: "foo",
+    srcs: ["a.py"],
+    version: {
+        py2: {
+            enabled: true,
+        },
+        py3: {
+            enabled: true,
+        },
+    },
+
+    bazel_module: { bp2build_available: true },
+}`, modType),
+		expectedBazelTargets: []string{
+			// srcs_version is PY2ANDPY3 by default.
+			`py_library(
+    name = "foo",
+    srcs = ["a.py"],
+)`,
+		},
+	})
+}
diff --git a/bp2build/sh_conversion_test.go b/bp2build/sh_conversion_test.go
index 37f542e..82e0a14 100644
--- a/bp2build/sh_conversion_test.go
+++ b/bp2build/sh_conversion_test.go
@@ -15,10 +15,10 @@
 package bp2build
 
 import (
+	"testing"
+
 	"android/soong/android"
 	"android/soong/sh"
-	"strings"
-	"testing"
 )
 
 func TestShBinaryLoadStatement(t *testing.T) {
@@ -46,88 +46,27 @@
 			t.Fatalf("Expected load statements to be %s, got %s", expected, actual)
 		}
 	}
-
 }
 
-func TestShBinaryBp2Build(t *testing.T) {
-	testCases := []struct {
-		description                        string
-		moduleTypeUnderTest                string
-		moduleTypeUnderTestFactory         android.ModuleFactory
-		moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext)
-		preArchMutators                    []android.RegisterMutatorFunc
-		depsMutators                       []android.RegisterMutatorFunc
-		bp                                 string
-		expectedBazelTargets               []string
-		filesystem                         map[string]string
-		dir                                string
-	}{
-		{
-			description:                        "sh_binary test",
-			moduleTypeUnderTest:                "sh_binary",
-			moduleTypeUnderTestFactory:         sh.ShBinaryFactory,
-			moduleTypeUnderTestBp2BuildMutator: sh.ShBinaryBp2Build,
-			bp: `sh_binary {
+func runShBinaryTestCase(t *testing.T, tc bp2buildTestCase) {
+	t.Helper()
+	runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, tc)
+}
+
+func TestShBinarySimple(t *testing.T) {
+	runShBinaryTestCase(t, bp2buildTestCase{
+		description:                        "sh_binary test",
+		moduleTypeUnderTest:                "sh_binary",
+		moduleTypeUnderTestFactory:         sh.ShBinaryFactory,
+		moduleTypeUnderTestBp2BuildMutator: sh.ShBinaryBp2Build,
+		blueprint: `sh_binary {
     name: "foo",
     src: "foo.sh",
     bazel_module: { bp2build_available: true },
 }`,
-			expectedBazelTargets: []string{`sh_binary(
+		expectedBazelTargets: []string{`sh_binary(
     name = "foo",
     srcs = ["foo.sh"],
 )`},
-		},
-	}
-
-	dir := "."
-	for _, testCase := range testCases {
-		filesystem := make(map[string][]byte)
-		toParse := []string{
-			"Android.bp",
-		}
-		for f, content := range testCase.filesystem {
-			if strings.HasSuffix(f, "Android.bp") {
-				toParse = append(toParse, f)
-			}
-			filesystem[f] = []byte(content)
-		}
-		config := android.TestConfig(buildDir, nil, testCase.bp, filesystem)
-		ctx := android.NewTestContext(config)
-		ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
-		for _, m := range testCase.depsMutators {
-			ctx.DepsBp2BuildMutators(m)
-		}
-		ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
-		ctx.RegisterForBazelConversion()
-
-		_, errs := ctx.ParseFileList(dir, toParse)
-		if Errored(t, testCase.description, errs) {
-			continue
-		}
-		_, errs = ctx.ResolveDependencies(config)
-		if Errored(t, testCase.description, errs) {
-			continue
-		}
-
-		checkDir := dir
-		if testCase.dir != "" {
-			checkDir = testCase.dir
-		}
-		codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
-		bazelTargets := generateBazelTargetsForDir(codegenCtx, checkDir)
-		if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
-			t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount)
-		} else {
-			for i, target := range bazelTargets {
-				if w, g := testCase.expectedBazelTargets[i], target.content; w != g {
-					t.Errorf(
-						"%s: Expected generated Bazel target to be '%s', got '%s'",
-						testCase.description,
-						w,
-						g,
-					)
-				}
-			}
-		}
-	}
+	})
 }
diff --git a/bp2build/testing.go b/bp2build/testing.go
index b925682..a549a93 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -1,6 +1,28 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 package bp2build
 
+/*
+For shareable/common bp2build testing functionality and dumping ground for
+specific-but-shared functionality among tests in package
+*/
+
 import (
+	"strings"
+	"testing"
+
 	"android/soong/android"
 	"android/soong/bazel"
 )
@@ -10,8 +32,91 @@
 	bp2buildConfig = android.Bp2BuildConfig{
 		android.BP2BUILD_TOPLEVEL: android.Bp2BuildDefaultTrueRecursively,
 	}
+
+	buildDir string
 )
 
+func errored(t *testing.T, desc string, errs []error) bool {
+	t.Helper()
+	if len(errs) > 0 {
+		for _, err := range errs {
+			t.Errorf("%s: %s", desc, err)
+		}
+		return true
+	}
+	return false
+}
+
+func runBp2BuildTestCaseSimple(t *testing.T, tc bp2buildTestCase) {
+	t.Helper()
+	runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, tc)
+}
+
+type bp2buildTestCase struct {
+	description                        string
+	moduleTypeUnderTest                string
+	moduleTypeUnderTestFactory         android.ModuleFactory
+	moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext)
+	blueprint                          string
+	expectedBazelTargets               []string
+	filesystem                         map[string]string
+	dir                                string
+}
+
+func runBp2BuildTestCase(t *testing.T, registerModuleTypes func(ctx android.RegistrationContext), tc bp2buildTestCase) {
+	t.Helper()
+	dir := "."
+	filesystem := make(map[string][]byte)
+	toParse := []string{
+		"Android.bp",
+	}
+	for f, content := range tc.filesystem {
+		if strings.HasSuffix(f, "Android.bp") {
+			toParse = append(toParse, f)
+		}
+		filesystem[f] = []byte(content)
+	}
+	config := android.TestConfig(buildDir, nil, tc.blueprint, filesystem)
+	ctx := android.NewTestContext(config)
+
+	registerModuleTypes(ctx)
+	ctx.RegisterModuleType(tc.moduleTypeUnderTest, tc.moduleTypeUnderTestFactory)
+	ctx.RegisterBp2BuildConfig(bp2buildConfig)
+	ctx.RegisterBp2BuildMutator(tc.moduleTypeUnderTest, tc.moduleTypeUnderTestBp2BuildMutator)
+	ctx.RegisterForBazelConversion()
+
+	_, errs := ctx.ParseFileList(dir, toParse)
+	if errored(t, tc.description, errs) {
+		return
+	}
+	_, errs = ctx.ResolveDependencies(config)
+	if errored(t, tc.description, errs) {
+		return
+	}
+
+	checkDir := dir
+	if tc.dir != "" {
+		checkDir = tc.dir
+	}
+	codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+	bazelTargets := generateBazelTargetsForDir(codegenCtx, checkDir)
+	if actualCount, expectedCount := len(bazelTargets), len(tc.expectedBazelTargets); actualCount != expectedCount {
+		t.Errorf("%s: Expected %d bazel target, got %d; %v",
+			tc.description, expectedCount, actualCount, bazelTargets)
+	} else {
+		for i, target := range bazelTargets {
+			if w, g := tc.expectedBazelTargets[i], target.content; w != g {
+				t.Errorf(
+					"%s: Expected generated Bazel target to be '%s', got '%s'",
+					tc.description,
+					w,
+					g,
+				)
+			}
+		}
+	}
+}
+
 type nestedProps struct {
 	Nested_prop string
 }
@@ -29,7 +134,8 @@
 	Nested_props     nestedProps
 	Nested_props_ptr *nestedProps
 
-	Arch_paths []string `android:"path,arch_variant"`
+	Arch_paths         []string `android:"path,arch_variant"`
+	Arch_paths_exclude []string `android:"path,arch_variant"`
 }
 
 type customModule struct {
@@ -124,30 +230,24 @@
 	customBazelModuleAttributes
 }
 
-func customBazelModuleFactory() android.Module {
-	module := &customBazelModule{}
-	module.AddProperties(&module.customBazelModuleAttributes)
-	android.InitBazelTargetModule(module)
-	return module
-}
-
-func (m *customBazelModule) Name() string                                          { return m.BaseModuleName() }
-func (m *customBazelModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {}
-
 func customBp2BuildMutator(ctx android.TopDownMutatorContext) {
 	if m, ok := ctx.Module().(*customModule); ok {
 		if !m.ConvertWithBp2build(ctx) {
 			return
 		}
 
-		paths := bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, m.props.Arch_paths))
+		paths := bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrcExcludes(ctx, m.props.Arch_paths, m.props.Arch_paths_exclude))
 
-		for arch, props := range m.GetArchProperties(ctx, &customProps{}) {
-			if archProps, ok := props.(*customProps); ok && archProps.Arch_paths != nil {
-				paths.SetValueForArch(arch.Name, android.BazelLabelForModuleSrc(ctx, archProps.Arch_paths))
+		for axis, configToProps := range m.GetArchVariantProperties(ctx, &customProps{}) {
+			for config, props := range configToProps {
+				if archProps, ok := props.(*customProps); ok && archProps.Arch_paths != nil {
+					paths.SetSelectValue(axis, config, android.BazelLabelForModuleSrcExcludes(ctx, archProps.Arch_paths, archProps.Arch_paths_exclude))
+				}
 			}
 		}
 
+		paths.ResolveExcludes()
+
 		attrs := &customBazelModuleAttributes{
 			String_prop:      m.props.String_prop,
 			String_list_prop: m.props.String_list_prop,
@@ -158,7 +258,7 @@
 			Rule_class: "custom",
 		}
 
-		ctx.CreateBazelTargetModule(customBazelModuleFactory, m.Name(), props, attrs)
+		ctx.CreateBazelTargetModule(m.Name(), props, attrs)
 	}
 }
 
@@ -177,25 +277,31 @@
 			Rule_class:        "my_library",
 			Bzl_load_location: "//build/bazel/rules:rules.bzl",
 		}
-		ctx.CreateBazelTargetModule(customBazelModuleFactory, baseName, myLibraryProps, attrs)
+		ctx.CreateBazelTargetModule(baseName, myLibraryProps, attrs)
 
 		protoLibraryProps := bazel.BazelTargetModuleProperties{
 			Rule_class:        "proto_library",
 			Bzl_load_location: "//build/bazel/rules:proto.bzl",
 		}
-		ctx.CreateBazelTargetModule(customBazelModuleFactory, baseName+"_proto_library_deps", protoLibraryProps, attrs)
+		ctx.CreateBazelTargetModule(baseName+"_proto_library_deps", protoLibraryProps, attrs)
 
 		myProtoLibraryProps := bazel.BazelTargetModuleProperties{
 			Rule_class:        "my_proto_library",
 			Bzl_load_location: "//build/bazel/rules:proto.bzl",
 		}
-		ctx.CreateBazelTargetModule(customBazelModuleFactory, baseName+"_my_proto_library_deps", myProtoLibraryProps, attrs)
+		ctx.CreateBazelTargetModule(baseName+"_my_proto_library_deps", myProtoLibraryProps, attrs)
 	}
 }
 
 // Helper method for tests to easily access the targets in a dir.
 func generateBazelTargetsForDir(codegenCtx *CodegenContext, dir string) BazelTargets {
 	// TODO: Set generateFilegroups to true and/or remove the generateFilegroups argument completely
-	buildFileToTargets, _ := GenerateBazelTargets(codegenCtx, false)
+	buildFileToTargets, _, _ := GenerateBazelTargets(codegenCtx, false)
 	return buildFileToTargets[dir]
 }
+
+func registerCustomModuleForBp2buildConversion(ctx *android.TestContext) {
+	ctx.RegisterModuleType("custom", customModuleFactory)
+	ctx.RegisterBp2BuildMutator("custom", customBp2BuildMutator)
+	ctx.RegisterForBazelConversion()
+}
diff --git a/bpfix/bpfix/bpfix.go b/bpfix/bpfix/bpfix.go
index fae6101..a608630 100644
--- a/bpfix/bpfix/bpfix.go
+++ b/bpfix/bpfix/bpfix.go
@@ -130,7 +130,15 @@
 	},
 	{
 		Name: "removePdkProperty",
-		Fix:  runPatchListMod(removePdkProperty),
+		Fix:  runPatchListMod(removeObsoleteProperty("product_variables.pdk")),
+	},
+	{
+		Name: "removeScudoProperty",
+		Fix:  runPatchListMod(removeObsoleteProperty("sanitize.scudo")),
+	},
+	{
+		Name: "formatFlagProperties",
+		Fix:  runPatchListMod(formatFlagProperties),
 	},
 }
 
@@ -319,7 +327,7 @@
 		var defStr string
 		switch mod.Type {
 		case "cts_support_package":
-			mod.Type = "android_test"
+			mod.Type = "android_test_helper_app"
 			defStr = "cts_support_defaults"
 		case "cts_package":
 			mod.Type = "android_test"
@@ -392,7 +400,7 @@
 			continue
 		}
 
-		if !strings.HasPrefix(mod.Type, "java_") && !strings.HasPrefix(mod.Type, "android_") {
+		if !strings.HasPrefix(mod.Type, "java_") && !strings.HasPrefix(mod.Type, "android_") && mod.Type != "cc_binary" {
 			continue
 		}
 
@@ -420,6 +428,14 @@
 				mod.Type = "java_test_host"
 			}
 		}
+
+		// when a cc_binary module has a nonempty test_suites field, modify the type to cc_test
+		if mod.Type == "cc_binary" {
+			hasTestSuites := hasNonEmptyLiteralListProperty(mod, "test_suites")
+			if hasTestSuites {
+				mod.Type = "cc_test"
+			}
+		}
 	}
 
 	return nil
@@ -622,12 +638,20 @@
 func rewriteAndroidTest(f *Fixer) error {
 	for _, def := range f.tree.Defs {
 		mod, ok := def.(*parser.Module)
-		if !(ok && mod.Type == "android_test") {
+		if !ok {
+			// The definition is not a module.
+			continue
+		}
+		if mod.Type != "android_test" && mod.Type != "android_test_helper_app" {
+			// The module is not an android_test or android_test_helper_app.
 			continue
 		}
 		// The rewriter converts LOCAL_MODULE_PATH attribute into a struct attribute
 		// 'local_module_path'. For the android_test module, it should be  $(TARGET_OUT_DATA_APPS),
 		// that is, `local_module_path: { var: "TARGET_OUT_DATA_APPS"}`
+		// 1. if the `key: val` pair matches, (key is `local_module_path`,
+		//    and val is `{ var: "TARGET_OUT_DATA_APPS"}`), this property is removed;
+		// 2. o/w, an error msg is thrown.
 		const local_module_path = "local_module_path"
 		if prop_local_module_path, ok := mod.GetProperty(local_module_path); ok {
 			removeProperty(mod, local_module_path)
@@ -637,7 +661,7 @@
 				continue
 			}
 			return indicateAttributeError(mod, "filename",
-				"Only LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS) is allowed for the android_test")
+				"Only LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS) is allowed for the %s", mod.Type)
 		}
 	}
 	return nil
@@ -847,7 +871,9 @@
 	}
 }
 
-func runPatchListMod(modFunc func(mod *parser.Module, buf []byte, patchlist *parser.PatchList) error) func(*Fixer) error {
+type patchListModFunction func(*parser.Module, []byte, *parser.PatchList) error
+
+func runPatchListMod(modFunc patchListModFunction) func(*Fixer) error {
 	return func(f *Fixer) error {
 		// Make sure all the offsets are accurate
 		buf, err := f.reparse()
@@ -1017,23 +1043,63 @@
 	return patchlist.Add(prop.Pos().Offset, prop.End().Offset+2, replaceStr)
 }
 
-func removePdkProperty(mod *parser.Module, buf []byte, patchlist *parser.PatchList) error {
-	prop, ok := mod.GetProperty("product_variables")
-	if !ok {
-		return nil
+type propertyProvider interface {
+	GetProperty(string) (*parser.Property, bool)
+	RemoveProperty(string) bool
+}
+
+func removeNestedProperty(mod *parser.Module, patchList *parser.PatchList, propName string) error {
+	propNames := strings.Split(propName, ".")
+
+	var propProvider, toRemoveFrom propertyProvider
+	propProvider = mod
+
+	var propToRemove *parser.Property
+	for i, name := range propNames {
+		p, ok := propProvider.GetProperty(name)
+		if !ok {
+			return nil
+		}
+		// if this is the inner most element, it's time to delete
+		if i == len(propNames)-1 {
+			if propToRemove == nil {
+				// if we cannot remove the properties that the current property is nested in,
+				// remove only the current property
+				propToRemove = p
+				toRemoveFrom = propProvider
+			}
+
+			// remove the property from the list, in case we remove other properties in this list
+			toRemoveFrom.RemoveProperty(propToRemove.Name)
+			// only removing the property would leave blank line(s), remove with a patch
+			if err := patchList.Add(propToRemove.Pos().Offset, propToRemove.End().Offset+2, ""); err != nil {
+				return err
+			}
+		} else {
+			propMap, ok := p.Value.(*parser.Map)
+			if !ok {
+				return nil
+			}
+			if len(propMap.Properties) > 1 {
+				// if there are other properties in this struct, we need to keep this struct
+				toRemoveFrom = nil
+				propToRemove = nil
+			} else if propToRemove == nil {
+				// otherwise, we can remove the empty struct entirely
+				toRemoveFrom = propProvider
+				propToRemove = p
+			}
+			propProvider = propMap
+		}
 	}
-	propMap, ok := prop.Value.(*parser.Map)
-	if !ok {
-		return nil
+
+	return nil
+}
+
+func removeObsoleteProperty(propName string) patchListModFunction {
+	return func(mod *parser.Module, buf []byte, patchList *parser.PatchList) error {
+		return removeNestedProperty(mod, patchList, propName)
 	}
-	pdkProp, ok := propMap.GetProperty("pdk")
-	if !ok {
-		return nil
-	}
-	if len(propMap.Properties) > 1 {
-		return patchlist.Add(pdkProp.Pos().Offset, pdkProp.End().Offset+2, "")
-	}
-	return patchlist.Add(prop.Pos().Offset, prop.End().Offset+2, "")
 }
 
 func mergeMatchingModuleProperties(mod *parser.Module, buf []byte, patchlist *parser.PatchList) error {
@@ -1281,3 +1347,69 @@
 	}
 	return false
 }
+
+func formatFlagProperty(mod *parser.Module, field string, buf []byte, patchlist *parser.PatchList) error {
+	// the comment or empty lines in the value of the field are skipped
+	listValue, ok := getLiteralListProperty(mod, field)
+	if !ok {
+		// if do not find
+		return nil
+	}
+	for i := 0; i < len(listValue.Values); i++ {
+		curValue, ok := listValue.Values[i].(*parser.String)
+		if !ok {
+			return fmt.Errorf("Expecting string for %s.%s fields", mod.Type, field)
+		}
+		if !strings.HasPrefix(curValue.Value, "-") {
+			return fmt.Errorf("Expecting the string `%s` starting with '-'", curValue.Value)
+		}
+		if i+1 < len(listValue.Values) {
+			nextValue, ok := listValue.Values[i+1].(*parser.String)
+			if !ok {
+				return fmt.Errorf("Expecting string for %s.%s fields", mod.Type, field)
+			}
+			if !strings.HasPrefix(nextValue.Value, "-") {
+				// delete the line
+				err := patchlist.Add(curValue.Pos().Offset, curValue.End().Offset+2, "")
+				if err != nil {
+					return err
+				}
+				// replace the line
+				value := "\"" + curValue.Value + " " + nextValue.Value + "\","
+				err = patchlist.Add(nextValue.Pos().Offset, nextValue.End().Offset+1, value)
+				if err != nil {
+					return err
+				}
+				// combined two lines to one
+				i++
+			}
+		}
+	}
+	return nil
+}
+
+func formatFlagProperties(mod *parser.Module, buf []byte, patchlist *parser.PatchList) error {
+	relevantFields := []string{
+		// cc flags
+		"asflags",
+		"cflags",
+		"clang_asflags",
+		"clang_cflags",
+		"conlyflags",
+		"cppflags",
+		"ldflags",
+		"tidy_flags",
+		// java flags
+		"aaptflags",
+		"dxflags",
+		"javacflags",
+		"kotlincflags",
+	}
+	for _, field := range relevantFields {
+		err := formatFlagProperty(mod, field, buf, patchlist)
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
diff --git a/bpfix/bpfix/bpfix_test.go b/bpfix/bpfix/bpfix_test.go
index 61dfe1a..d8772c1 100644
--- a/bpfix/bpfix/bpfix_test.go
+++ b/bpfix/bpfix/bpfix_test.go
@@ -636,7 +636,7 @@
 				}
 			`,
 			out: `
-				android_test {
+				android_test_helper_app {
 					name: "foo",
 					defaults: ["cts_support_defaults"],
 				}
@@ -999,7 +999,171 @@
 	}
 }
 
-func TestRemovePdkProperty(t *testing.T) {
+func TestRemoveNestedProperty(t *testing.T) {
+	tests := []struct {
+		name         string
+		in           string
+		out          string
+		propertyName string
+	}{
+		{
+			name: "remove no nesting",
+			in: `
+cc_library {
+	name: "foo",
+	foo: true,
+}`,
+			out: `
+cc_library {
+	name: "foo",
+}
+`,
+			propertyName: "foo",
+		},
+		{
+			name: "remove one nest",
+			in: `
+cc_library {
+	name: "foo",
+	foo: {
+		bar: true,
+	},
+}`,
+			out: `
+cc_library {
+	name: "foo",
+}
+`,
+			propertyName: "foo.bar",
+		},
+		{
+			name: "remove one nest, multiple props",
+			in: `
+cc_library {
+	name: "foo",
+	foo: {
+		bar: true,
+		baz: false,
+	},
+}`,
+			out: `
+cc_library {
+	name: "foo",
+	foo: {
+		baz: false,
+	},
+}
+`,
+			propertyName: "foo.bar",
+		},
+		{
+			name: "remove multiple nest",
+			in: `
+cc_library {
+	name: "foo",
+	foo: {
+		bar: {
+			baz: {
+				a: true,
+			}
+		},
+	},
+}`,
+			out: `
+cc_library {
+	name: "foo",
+}
+`,
+			propertyName: "foo.bar.baz.a",
+		},
+		{
+			name: "remove multiple nest, outer non-empty",
+			in: `
+cc_library {
+	name: "foo",
+	foo: {
+		bar: {
+			baz: {
+				a: true,
+			}
+		},
+		other: true,
+	},
+}`,
+			out: `
+cc_library {
+	name: "foo",
+	foo: {
+		other: true,
+	},
+}
+`,
+			propertyName: "foo.bar.baz.a",
+		},
+		{
+			name: "remove multiple nest, inner non-empty",
+			in: `
+cc_library {
+	name: "foo",
+	foo: {
+		bar: {
+			baz: {
+				a: true,
+			},
+			other: true,
+		},
+	},
+}`,
+			out: `
+cc_library {
+	name: "foo",
+	foo: {
+		bar: {
+			other: true,
+		},
+	},
+}
+`,
+			propertyName: "foo.bar.baz.a",
+		},
+		{
+			name: "remove multiple nest, inner-most non-empty",
+			in: `
+cc_library {
+	name: "foo",
+	foo: {
+		bar: {
+			baz: {
+				a: true,
+				other: true,
+			},
+		},
+	},
+}`,
+			out: `
+cc_library {
+	name: "foo",
+	foo: {
+		bar: {
+			baz: {
+				other: true,
+			},
+		},
+	},
+}
+`,
+			propertyName: "foo.bar.baz.a",
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			runPass(t, test.in, test.out, runPatchListMod(removeObsoleteProperty(test.propertyName)))
+		})
+	}
+}
+
+func TestRemoveObsoleteProperties(t *testing.T) {
 	tests := []struct {
 		name string
 		in   string
@@ -1052,7 +1216,7 @@
 	}
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
-			runPass(t, test.in, test.out, runPatchListMod(removePdkProperty))
+			runPass(t, test.in, test.out, runPatchListMod(removeObsoleteProperty("product_variables.pdk")))
 		})
 	}
 }
@@ -1124,3 +1288,323 @@
 		})
 	}
 }
+
+func TestRewriteTestModuleTypes(t *testing.T) {
+	tests := []struct {
+		name string
+		in   string
+		out  string
+	}{
+		{
+			name: "cc_binary with test_suites",
+			in: `
+				cc_binary {
+					name: "foo",
+					srcs: ["srcs"],
+					test_suites: ["test_suite1"],
+				}
+			`,
+			out: `
+				cc_test {
+					name: "foo",
+					srcs: ["srcs"],
+					test_suites: ["test_suite1"],
+				}
+			`,
+		},
+		{
+			name: "cc_binary without test_suites",
+			in: `
+				cc_binary {
+					name: "foo",
+					srcs: ["srcs"],
+				}
+			`,
+			out: `
+				cc_binary {
+					name: "foo",
+					srcs: ["srcs"],
+				}
+			`,
+		},
+	}
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			runPass(t, test.in, test.out, func(fixer *Fixer) error {
+				return rewriteTestModuleTypes(fixer)
+			})
+		})
+	}
+}
+
+func TestFormatFlagProperty(t *testing.T) {
+	tests := []struct {
+		name string
+		in   string
+		out  string
+	}{
+		{
+			name: "group options and values for apptflags, dxflags, javacflags, and kotlincflags",
+			in: `
+				android_test {
+					name: "foo",
+					aaptflags: [
+						// comment1_1
+						"--flag1",
+						// comment1_2
+						"1",
+						// comment2_1
+						// comment2_2
+						"--flag2",
+						// comment3_1
+						// comment3_2
+						// comment3_3
+						"--flag3",
+						// comment3_4
+						// comment3_5
+						// comment3_6
+						"3",
+						// other comment1_1
+						// other comment1_2
+					],
+					dxflags: [
+						"--flag1",
+						// comment1_1
+						"1",
+						// comment2_1
+						"--flag2",
+						// comment3_1
+						"--flag3",
+						// comment3_2
+						"3",
+					],
+					javacflags: [
+						"--flag1",
+
+						"1",
+						"--flag2",
+						"--flag3",
+						"3",
+					],
+					kotlincflags: [
+
+						"--flag1",
+						"1",
+
+						"--flag2",
+						"--flag3",
+						"3",
+
+					],
+				}
+			`,
+			out: `
+				android_test {
+					name: "foo",
+					aaptflags: [
+						// comment1_1
+						// comment1_2
+						"--flag1 1",
+						// comment2_1
+						// comment2_2
+						"--flag2",
+						// comment3_1
+						// comment3_2
+						// comment3_3
+						// comment3_4
+						// comment3_5
+						// comment3_6
+						"--flag3 3",
+						// other comment1_1
+						// other comment1_2
+					],
+					dxflags: [
+						// comment1_1
+						"--flag1 1",
+						// comment2_1
+						"--flag2",
+						// comment3_1
+						// comment3_2
+						"--flag3 3",
+					],
+					javacflags: [
+
+						"--flag1 1",
+						"--flag2",
+						"--flag3 3",
+					],
+					kotlincflags: [
+
+						"--flag1 1",
+
+						"--flag2",
+						"--flag3 3",
+
+					],
+				}
+			`,
+		},
+		{
+			name: "group options and values for asflags, cflags, clang_asflags, clang_cflags, conlyflags, cppflags, ldflags, and tidy_flags",
+			in: `
+				cc_test {
+					name: "foo",
+					asflags: [
+						// comment1_1
+						"--flag1",
+						"1",
+						// comment2_1
+						// comment2_2
+						"--flag2",
+						// comment2_3
+						"2",
+						// comment3_1
+						// comment3_2
+						"--flag3",
+						// comment3_3
+						// comment3_4
+						// comment3_4
+						"3",
+						// comment4_1
+						// comment4_2
+						// comment4_3
+						"--flag4",
+					],
+					cflags: [
+						"--flag1",
+						"1",
+						"--flag2",
+						"2",
+						"--flag3",
+						"3",
+						"--flag4",
+					],
+					clang_asflags: [
+						"--flag1",
+						"1",
+						"--flag2",
+						"2",
+						"--flag3",
+						"3",
+						"--flag4",
+					],
+					clang_cflags: [
+						"--flag1",
+						"1",
+						"--flag2",
+						"2",
+						"--flag3",
+						"3",
+						"--flag4",
+					],
+					conlyflags: [
+						"--flag1",
+						"1",
+						"--flag2",
+						"2",
+						"--flag3",
+						"3",
+						"--flag4",
+					],
+					cppflags: [
+						"--flag1",
+						"1",
+						"--flag2",
+						"2",
+						"--flag3",
+						"3",
+						"--flag4",
+					],
+					ldflags: [
+						"--flag1",
+						"1",
+						"--flag2",
+						"2",
+						"--flag3",
+						"3",
+						"--flag4",
+					],
+					tidy_flags: [
+						"--flag1",
+						"1",
+						"--flag2",
+						"2",
+						"--flag3",
+						"3",
+						"--flag4",
+					],
+				}
+			`,
+			out: `
+				cc_test {
+					name: "foo",
+					asflags: [
+						// comment1_1
+						"--flag1 1",
+						// comment2_1
+						// comment2_2
+						// comment2_3
+						"--flag2 2",
+						// comment3_1
+						// comment3_2
+						// comment3_3
+						// comment3_4
+						// comment3_4
+						"--flag3 3",
+						// comment4_1
+						// comment4_2
+						// comment4_3
+						"--flag4",
+					],
+					cflags: [
+						"--flag1 1",
+						"--flag2 2",
+						"--flag3 3",
+						"--flag4",
+					],
+					clang_asflags: [
+						"--flag1 1",
+						"--flag2 2",
+						"--flag3 3",
+						"--flag4",
+					],
+					clang_cflags: [
+						"--flag1 1",
+						"--flag2 2",
+						"--flag3 3",
+						"--flag4",
+					],
+					conlyflags: [
+						"--flag1 1",
+						"--flag2 2",
+						"--flag3 3",
+						"--flag4",
+					],
+					cppflags: [
+						"--flag1 1",
+						"--flag2 2",
+						"--flag3 3",
+						"--flag4",
+					],
+					ldflags: [
+						"--flag1 1",
+						"--flag2 2",
+						"--flag3 3",
+						"--flag4",
+					],
+					tidy_flags: [
+						"--flag1 1",
+						"--flag2 2",
+						"--flag3 3",
+						"--flag4",
+					],
+				}
+			`,
+		},
+	}
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			runPass(t, test.in, test.out, runPatchListMod(formatFlagProperties))
+		})
+	}
+}
diff --git a/build_kzip.bash b/build_kzip.bash
index a09335e..5655067 100755
--- a/build_kzip.bash
+++ b/build_kzip.bash
@@ -6,6 +6,8 @@
 # The following environment variables affect the result:
 #   BUILD_NUMBER          build number, used to generate unique ID (will use UUID if not set)
 #   SUPERPROJECT_SHA      superproject sha, used to generate unique id (will use BUILD_NUMBER if not set)
+#   SUPERPROJECT_REVISION superproject revision, used for unique id if defined as a sha
+#   KZIP_NAME             name of the output file (will use SUPERPROJECT_REVISION|SUPERPROJECT_SHA|BUILD_NUMBER|UUID if not set)
 #   DIST_DIR              where the resulting all.kzip will be placed
 #   KYTHE_KZIP_ENCODING   proto or json (proto is default)
 #   KYTHE_JAVA_SOURCE_BATCH_SIZE maximum number of the Java source files in a compilation unit
@@ -14,8 +16,16 @@
 #   TARGET_PRODUCT        target device name, e.g., 'aosp_blueline'
 #   XREF_CORPUS           source code repository URI, e.g., 'android.googlesource.com/platform/superproject'
 
-: ${BUILD_NUMBER:=$(uuidgen)}
-: ${SUPERPROJECT_SHA:=$BUILD_NUMBER}
+# If the SUPERPROJECT_REVISION is defined as a sha, use this as the default value if no
+# SUPERPROJECT_SHA is specified.
+if [[ ${SUPERPROJECT_REVISION:-} =~ [0-9a-f]{40} ]]; then
+  : ${KZIP_NAME:=${SUPERPROJECT_REVISION:-}}
+fi
+
+: ${KZIP_NAME:=${SUPERPROJECT_SHA:-}}
+: ${KZIP_NAME:=${BUILD_NUMBER:-}}
+: ${KZIP_NAME:=$(uuidgen)}
+
 : ${KYTHE_JAVA_SOURCE_BATCH_SIZE:=500}
 : ${KYTHE_KZIP_ENCODING:=proto}
 : ${XREF_CORPUS:?should be set}
@@ -50,6 +60,6 @@
 
 # Pack
 # TODO(asmundak): this should be done by soong.
-declare -r allkzip="$SUPERPROJECT_SHA.kzip"
+declare -r allkzip="$KZIP_NAME.kzip"
 "$out/soong/host/linux-x86/bin/merge_zips" "$DIST_DIR/$allkzip" @<(find "$out" -name '*.kzip')
 
diff --git a/build_test.bash b/build_test.bash
index 3230f2d..b039285 100755
--- a/build_test.bash
+++ b/build_test.bash
@@ -49,8 +49,12 @@
 esac
 
 echo
+echo "Free disk space:"
+df -h
+
+echo
 echo "Running Bazel smoke test..."
-"${TOP}/tools/bazel" --batch --max_idle_secs=1 info
+STANDALONE_BAZEL=true "${TOP}/tools/bazel" --batch --max_idle_secs=1 info
 
 echo
 echo "Running Soong test..."
diff --git a/cc/Android.bp b/cc/Android.bp
index 1fc8d9f..bff2761 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -13,7 +13,9 @@
         "soong-bazel",
         "soong-cc-config",
         "soong-etc",
+        "soong-fuzz",
         "soong-genrule",
+        "soong-snapshot",
         "soong-tradefed",
     ],
     srcs: [
@@ -65,9 +67,10 @@
         "test.go",
         "toolchain_library.go",
 
-        "ndk_prebuilt.go",
+        "ndk_abi.go",
         "ndk_headers.go",
         "ndk_library.go",
+        "ndk_prebuilt.go",
         "ndk_sysroot.go",
 
         "llndk_library.go",
diff --git a/cc/androidmk.go b/cc/androidmk.go
index e58d166..cd52363 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -24,12 +24,12 @@
 )
 
 var (
-	nativeBridgeSuffix  = ".native_bridge"
-	productSuffix       = ".product"
+	NativeBridgeSuffix  = ".native_bridge"
+	ProductSuffix       = ".product"
 	VendorSuffix        = ".vendor"
-	ramdiskSuffix       = ".ramdisk"
+	RamdiskSuffix       = ".ramdisk"
 	VendorRamdiskSuffix = ".vendor_ramdisk"
-	recoverySuffix      = ".recovery"
+	RecoverySuffix      = ".recovery"
 	sdkSuffix           = ".sdk"
 )
 
@@ -182,7 +182,7 @@
 	if ctx.Target().NativeBridge == android.NativeBridgeEnabled {
 		var result []string
 		for _, override := range overrides {
-			result = append(result, override+nativeBridgeSuffix)
+			result = append(result, override+NativeBridgeSuffix)
 		}
 		return result
 	}
@@ -294,6 +294,9 @@
 			if library.buildStubs() {
 				entries.SetBool("LOCAL_NO_NOTICE_FILE", true)
 			}
+			if library.apiListCoverageXmlPath.String() != "" {
+				entries.SetString("SOONG_CC_API_XML", "$(SOONG_CC_API_XML) "+library.apiListCoverageXmlPath.String())
+			}
 		})
 	}
 	// If a library providing a stub is included in an APEX, the private APIs of the library
@@ -401,24 +404,24 @@
 	ctx.subAndroidMk(entries, fuzz.binaryDecorator)
 
 	var fuzzFiles []string
-	for _, d := range fuzz.corpus {
+	for _, d := range fuzz.fuzzPackagedModule.Corpus {
 		fuzzFiles = append(fuzzFiles,
-			filepath.Dir(fuzz.corpusIntermediateDir.String())+":corpus/"+d.Base())
+			filepath.Dir(fuzz.fuzzPackagedModule.CorpusIntermediateDir.String())+":corpus/"+d.Base())
 	}
 
-	for _, d := range fuzz.data {
+	for _, d := range fuzz.fuzzPackagedModule.Data {
 		fuzzFiles = append(fuzzFiles,
-			filepath.Dir(fuzz.dataIntermediateDir.String())+":data/"+d.Rel())
+			filepath.Dir(fuzz.fuzzPackagedModule.DataIntermediateDir.String())+":data/"+d.Rel())
 	}
 
-	if fuzz.dictionary != nil {
+	if fuzz.fuzzPackagedModule.Dictionary != nil {
 		fuzzFiles = append(fuzzFiles,
-			filepath.Dir(fuzz.dictionary.String())+":"+fuzz.dictionary.Base())
+			filepath.Dir(fuzz.fuzzPackagedModule.Dictionary.String())+":"+fuzz.fuzzPackagedModule.Dictionary.Base())
 	}
 
-	if fuzz.config != nil {
+	if fuzz.fuzzPackagedModule.Config != nil {
 		fuzzFiles = append(fuzzFiles,
-			filepath.Dir(fuzz.config.String())+":config.json")
+			filepath.Dir(fuzz.fuzzPackagedModule.Config.String())+":config.json")
 	}
 
 	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
diff --git a/cc/binary.go b/cc/binary.go
index 999b82c..b423c50 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -146,16 +146,21 @@
 // modules common to most binaries, such as bionic libraries.
 func (binary *binaryDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps {
 	deps = binary.baseLinker.linkerDeps(ctx, deps)
-	if ctx.toolchain().Bionic() {
-		if !Bool(binary.baseLinker.Properties.Nocrt) {
-			if binary.static() {
-				deps.CrtBegin = "crtbegin_static"
-			} else {
-				deps.CrtBegin = "crtbegin_dynamic"
-			}
-			deps.CrtEnd = "crtend_android"
+	if !Bool(binary.baseLinker.Properties.Nocrt) {
+		if binary.static() {
+			deps.CrtBegin = ctx.toolchain().CrtBeginStaticBinary()
+			deps.CrtEnd = ctx.toolchain().CrtEndStaticBinary()
+		} else {
+			deps.CrtBegin = ctx.toolchain().CrtBeginSharedBinary()
+			deps.CrtEnd = ctx.toolchain().CrtEndSharedBinary()
 		}
+	}
 
+	if binary.static() {
+		deps.StaticLibs = append(deps.StaticLibs, deps.SystemSharedLibs...)
+	}
+
+	if ctx.toolchain().Bionic() {
 		if binary.static() {
 			if ctx.selectedStl() == "libc++_static" {
 				deps.StaticLibs = append(deps.StaticLibs, "libm", "libc")
@@ -169,16 +174,8 @@
 			deps.LateStaticLibs = append(groupLibs, deps.LateStaticLibs...)
 		}
 
-		// Embed the linker into host bionic binaries. This is needed to support host bionic,
-		// as the linux kernel requires that the ELF interpreter referenced by PT_INTERP be
-		// either an absolute path, or relative from CWD. To work around this, we extract
-		// the load sections from the runtime linker ELF binary and embed them into each host
-		// bionic binary, omitting the PT_INTERP declaration. The kernel will treat it as a static
-		// binary, and then we use a special entry point to fix up the arguments passed by
-		// the kernel before jumping to the embedded linker.
 		if ctx.Os() == android.LinuxBionic && !binary.static() {
 			deps.DynamicLinker = "linker"
-			deps.LinkerFlagsFile = "host_bionic_linker_flags"
 		}
 	}
 
@@ -190,11 +187,6 @@
 	return deps
 }
 
-func (binary *binaryDecorator) isDependencyRoot() bool {
-	// Binaries are always the dependency root.
-	return true
-}
-
 // NewBinary builds and returns a new Module corresponding to a C++ binary.
 // Individual module implementations which comprise a C++ binary should call this function,
 // set some fields on the result, and then call the Init function.
@@ -220,14 +212,14 @@
 func (binary *binaryDecorator) linkerInit(ctx BaseModuleContext) {
 	binary.baseLinker.linkerInit(ctx)
 
-	if !ctx.toolchain().Bionic() {
+	if !ctx.toolchain().Bionic() && !ctx.toolchain().Musl() {
 		if ctx.Os() == android.Linux {
 			// Unless explicitly specified otherwise, host static binaries are built with -static
 			// if HostStaticBinaries is true for the product configuration.
 			if binary.Properties.Static_executable == nil && ctx.Config().HostStaticBinaries() {
 				binary.Properties.Static_executable = BoolPtr(true)
 			}
-		} else if !ctx.Fuchsia() {
+		} else {
 			// Static executables are not supported on Darwin or Windows
 			binary.Properties.Static_executable = nil
 		}
@@ -345,15 +337,9 @@
 
 	var linkerDeps android.Paths
 
-	// Add flags from linker flags file.
-	if deps.LinkerFlagsFile.Valid() {
-		flags.Local.LdFlags = append(flags.Local.LdFlags, "$$(cat "+deps.LinkerFlagsFile.String()+")")
-		linkerDeps = append(linkerDeps, deps.LinkerFlagsFile.Path())
-	}
-
 	if flags.DynamicLinker != "" {
 		flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-dynamic-linker,"+flags.DynamicLinker)
-	} else if ctx.toolchain().Bionic() && !binary.static() {
+	} else if (ctx.toolchain().Bionic() || ctx.toolchain().Musl()) && !binary.static() {
 		flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--no-dynamic-linker")
 	}
 
@@ -401,16 +387,18 @@
 		}
 	}
 
+	var validations android.WritablePaths
+
 	// Handle host bionic linker symbols.
 	if ctx.Os() == android.LinuxBionic && !binary.static() {
-		injectedOutputFile := outputFile
-		outputFile = android.PathForModuleOut(ctx, "prelinker", fileName)
+		verifyFile := android.PathForModuleOut(ctx, "host_bionic_verify.stamp")
 
 		if !deps.DynamicLinker.Valid() {
 			panic("Non-static host bionic modules must have a dynamic linker")
 		}
 
-		binary.injectHostBionicLinkerSymbols(ctx, outputFile, deps.DynamicLinker.Path(), injectedOutputFile)
+		binary.verifyHostBionicLinker(ctx, outputFile, deps.DynamicLinker.Path(), verifyFile)
+		validations = append(validations, verifyFile)
 	}
 
 	var sharedLibs android.Paths
@@ -430,7 +418,7 @@
 	// Register link action.
 	transformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs, deps.StaticLibs,
 		deps.LateStaticLibs, deps.WholeStaticLibs, linkerDeps, deps.CrtBegin, deps.CrtEnd, true,
-		builderFlags, outputFile, nil)
+		builderFlags, outputFile, nil, validations)
 
 	objs.coverageFiles = append(objs.coverageFiles, deps.StaticLibObjs.coverageFiles...)
 	objs.coverageFiles = append(objs.coverageFiles, deps.WholeStaticLibObjs.coverageFiles...)
@@ -532,19 +520,19 @@
 }
 
 func init() {
-	pctx.HostBinToolVariable("hostBionicSymbolsInjectCmd", "host_bionic_inject")
+	pctx.HostBinToolVariable("verifyHostBionicCmd", "host_bionic_verify")
 }
 
-var injectHostBionicSymbols = pctx.AndroidStaticRule("injectHostBionicSymbols",
+var verifyHostBionic = pctx.AndroidStaticRule("verifyHostBionic",
 	blueprint.RuleParams{
-		Command:     "$hostBionicSymbolsInjectCmd -i $in -l $linker -o $out",
-		CommandDeps: []string{"$hostBionicSymbolsInjectCmd"},
+		Command:     "$verifyHostBionicCmd -i $in -l $linker && touch $out",
+		CommandDeps: []string{"$verifyHostBionicCmd"},
 	}, "linker")
 
-func (binary *binaryDecorator) injectHostBionicLinkerSymbols(ctx ModuleContext, in, linker android.Path, out android.WritablePath) {
+func (binary *binaryDecorator) verifyHostBionicLinker(ctx ModuleContext, in, linker android.Path, out android.WritablePath) {
 	ctx.Build(pctx, android.BuildParams{
-		Rule:        injectHostBionicSymbols,
-		Description: "inject host bionic symbols",
+		Rule:        verifyHostBionic,
+		Description: "verify host bionic",
 		Input:       in,
 		Implicit:    linker,
 		Output:      out,
diff --git a/cc/binary_sdk_member.go b/cc/binary_sdk_member.go
index ebf89ea..71e0cd8 100644
--- a/cc/binary_sdk_member.go
+++ b/cc/binary_sdk_member.go
@@ -38,16 +38,16 @@
 	android.SdkMemberTypeBase
 }
 
-func (mt *binarySdkMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
-	targets := mctx.MultiTargets()
+func (mt *binarySdkMemberType) AddDependencies(ctx android.SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) {
+	targets := ctx.MultiTargets()
 	for _, bin := range names {
 		for _, target := range targets {
 			variations := target.Variations()
-			if mctx.Device() {
+			if ctx.Device() {
 				variations = append(variations,
 					blueprint.Variation{Mutator: "image", Variation: android.CoreVariation})
 			}
-			mctx.AddFarVariationDependencies(variations, dependencyTag, bin)
+			ctx.AddFarVariationDependencies(variations, dependencyTag, bin)
 		}
 	}
 }
diff --git a/cc/bp2build.go b/cc/bp2build.go
index 9f9143b..537f01c 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -14,170 +14,232 @@
 package cc
 
 import (
-	"android/soong/android"
-	"android/soong/bazel"
+	"fmt"
 	"path/filepath"
 	"strings"
+
+	"android/soong/android"
+	"android/soong/bazel"
+
+	"github.com/google/blueprint/proptools"
 )
 
-// bp2build functions and helpers for converting cc_* modules to Bazel.
+// staticOrSharedAttributes are the Bazel-ified versions of StaticOrSharedProperties --
+// properties which apply to either the shared or static version of a cc_library module.
+type staticOrSharedAttributes struct {
+	Srcs    bazel.LabelListAttribute
+	Srcs_c  bazel.LabelListAttribute
+	Srcs_as bazel.LabelListAttribute
+	Copts   bazel.StringListAttribute
 
-func init() {
-	android.DepsBp2BuildMutators(RegisterDepsBp2Build)
+	Static_deps        bazel.LabelListAttribute
+	Dynamic_deps       bazel.LabelListAttribute
+	Whole_archive_deps bazel.LabelListAttribute
+
+	System_dynamic_deps bazel.LabelListAttribute
 }
 
-func RegisterDepsBp2Build(ctx android.RegisterMutatorsContext) {
-	ctx.BottomUp("cc_bp2build_deps", depsBp2BuildMutator)
-}
-
-// A naive deps mutator to add deps on all modules across all combinations of
-// target props for cc modules. This is needed to make module -> bazel label
-// resolution work in the bp2build mutator later. This is probably
-// the wrong way to do it, but it works.
-//
-// TODO(jingwen): can we create a custom os mutator in depsBp2BuildMutator to do this?
-func depsBp2BuildMutator(ctx android.BottomUpMutatorContext) {
-	module, ok := ctx.Module().(*Module)
-	if !ok {
-		// Not a cc module
-		return
+func groupSrcsByExtension(ctx android.TopDownMutatorContext, srcs bazel.LabelListAttribute) (cppSrcs, cSrcs, asSrcs bazel.LabelListAttribute) {
+	// Branch srcs into three language-specific groups.
+	// C++ is the "catch-all" group, and comprises generated sources because we don't
+	// know the language of these sources until the genrule is executed.
+	// TODO(b/190006308): Handle language detection of sources in a Bazel rule.
+	isCSrcOrFilegroup := func(s string) bool {
+		return strings.HasSuffix(s, ".c") || strings.HasSuffix(s, "_c_srcs")
 	}
 
-	if !module.ConvertWithBp2build(ctx) {
-		return
+	isAsmSrcOrFilegroup := func(s string) bool {
+		return strings.HasSuffix(s, ".S") || strings.HasSuffix(s, ".s") || strings.HasSuffix(s, "_as_srcs")
 	}
 
-	var allDeps []string
-
-	for _, p := range module.GetTargetProperties(&BaseLinkerProperties{}) {
-		// arch specific linker props
-		if baseLinkerProps, ok := p.(*BaseLinkerProperties); ok {
-			allDeps = append(allDeps, baseLinkerProps.Header_libs...)
-			allDeps = append(allDeps, baseLinkerProps.Export_header_lib_headers...)
-			allDeps = append(allDeps, baseLinkerProps.Static_libs...)
-			allDeps = append(allDeps, baseLinkerProps.Whole_static_libs...)
+	// Check that a module is a filegroup type named <label>.
+	isFilegroupNamed := func(m android.Module, fullLabel string) bool {
+		if ctx.OtherModuleType(m) != "filegroup" {
+			return false
+		}
+		labelParts := strings.Split(fullLabel, ":")
+		if len(labelParts) > 2 {
+			// There should not be more than one colon in a label.
+			panic(fmt.Errorf("%s is not a valid Bazel label for a filegroup", fullLabel))
+		} else {
+			return m.Name() == labelParts[len(labelParts)-1]
 		}
 	}
 
-	for _, p := range module.GetArchProperties(ctx, &BaseLinkerProperties{}) {
-		// arch specific linker props
-		if baseLinkerProps, ok := p.(*BaseLinkerProperties); ok {
-			allDeps = append(allDeps, baseLinkerProps.Header_libs...)
-			allDeps = append(allDeps, baseLinkerProps.Export_header_lib_headers...)
-			allDeps = append(allDeps, baseLinkerProps.Static_libs...)
-			allDeps = append(allDeps, baseLinkerProps.Whole_static_libs...)
+	// Convert the filegroup dependencies into the extension-specific filegroups
+	// filtered in the filegroup.bzl macro.
+	cppFilegroup := func(label string) string {
+		m, exists := ctx.ModuleFromName(label)
+		if exists {
+			aModule, _ := m.(android.Module)
+			if isFilegroupNamed(aModule, label) {
+				label = label + "_cpp_srcs"
+			}
 		}
+		return label
+	}
+	cFilegroup := func(label string) string {
+		m, exists := ctx.ModuleFromName(label)
+		if exists {
+			aModule, _ := m.(android.Module)
+			if isFilegroupNamed(aModule, label) {
+				label = label + "_c_srcs"
+			}
+		}
+		return label
+	}
+	asFilegroup := func(label string) string {
+		m, exists := ctx.ModuleFromName(label)
+		if exists {
+			aModule, _ := m.(android.Module)
+			if isFilegroupNamed(aModule, label) {
+				label = label + "_as_srcs"
+			}
+		}
+		return label
 	}
 
-	// Deps in the static: { .. } and shared: { .. } props of a cc_library.
-	if lib, ok := module.compiler.(*libraryDecorator); ok {
-		allDeps = append(allDeps, lib.SharedProperties.Shared.Static_libs...)
-		allDeps = append(allDeps, lib.SharedProperties.Shared.Whole_static_libs...)
-		allDeps = append(allDeps, lib.SharedProperties.Shared.Shared_libs...)
-		allDeps = append(allDeps, lib.SharedProperties.Shared.System_shared_libs...)
+	cSrcs = bazel.MapLabelListAttribute(srcs, cFilegroup)
+	cSrcs = bazel.FilterLabelListAttribute(cSrcs, isCSrcOrFilegroup)
 
-		allDeps = append(allDeps, lib.StaticProperties.Static.Static_libs...)
-		allDeps = append(allDeps, lib.StaticProperties.Static.Whole_static_libs...)
-		allDeps = append(allDeps, lib.StaticProperties.Static.Shared_libs...)
-		allDeps = append(allDeps, lib.StaticProperties.Static.System_shared_libs...)
-	}
+	asSrcs = bazel.MapLabelListAttribute(srcs, asFilegroup)
+	asSrcs = bazel.FilterLabelListAttribute(asSrcs, isAsmSrcOrFilegroup)
 
-	ctx.AddDependency(module, nil, android.SortedUniqueStrings(allDeps)...)
-}
-
-type sharedAttributes struct {
-	copts            bazel.StringListAttribute
-	srcs             bazel.LabelListAttribute
-	staticDeps       bazel.LabelListAttribute
-	dynamicDeps      bazel.LabelListAttribute
-	wholeArchiveDeps bazel.LabelListAttribute
+	cppSrcs = bazel.MapLabelListAttribute(srcs, cppFilegroup)
+	cppSrcs = bazel.SubtractBazelLabelListAttribute(cppSrcs, cSrcs)
+	cppSrcs = bazel.SubtractBazelLabelListAttribute(cppSrcs, asSrcs)
+	return
 }
 
 // bp2buildParseSharedProps returns the attributes for the shared variant of a cc_library.
-func bp2BuildParseSharedProps(ctx android.TopDownMutatorContext, module *Module) sharedAttributes {
+func bp2BuildParseSharedProps(ctx android.TopDownMutatorContext, module *Module) staticOrSharedAttributes {
 	lib, ok := module.compiler.(*libraryDecorator)
 	if !ok {
-		return sharedAttributes{}
+		return staticOrSharedAttributes{}
 	}
 
-	copts := bazel.StringListAttribute{Value: lib.SharedProperties.Shared.Cflags}
-
-	srcs := bazel.LabelListAttribute{
-		Value: android.BazelLabelForModuleSrc(ctx, lib.SharedProperties.Shared.Srcs)}
-
-	staticDeps := bazel.LabelListAttribute{
-		Value: android.BazelLabelForModuleDeps(ctx, lib.SharedProperties.Shared.Static_libs)}
-
-	dynamicDeps := bazel.LabelListAttribute{
-		Value: android.BazelLabelForModuleDeps(ctx, lib.SharedProperties.Shared.Shared_libs)}
-
-	wholeArchiveDeps := bazel.LabelListAttribute{
-		Value: android.BazelLabelForModuleDeps(ctx, lib.SharedProperties.Shared.Whole_static_libs)}
-
-	return sharedAttributes{
-		copts:            copts,
-		srcs:             srcs,
-		staticDeps:       staticDeps,
-		dynamicDeps:      dynamicDeps,
-		wholeArchiveDeps: wholeArchiveDeps,
-	}
-}
-
-type staticAttributes struct {
-	copts            bazel.StringListAttribute
-	srcs             bazel.LabelListAttribute
-	staticDeps       bazel.LabelListAttribute
-	dynamicDeps      bazel.LabelListAttribute
-	wholeArchiveDeps bazel.LabelListAttribute
+	return bp2buildParseStaticOrSharedProps(ctx, module, lib, false)
 }
 
 // bp2buildParseStaticProps returns the attributes for the static variant of a cc_library.
-func bp2BuildParseStaticProps(ctx android.TopDownMutatorContext, module *Module) staticAttributes {
+func bp2BuildParseStaticProps(ctx android.TopDownMutatorContext, module *Module) staticOrSharedAttributes {
 	lib, ok := module.compiler.(*libraryDecorator)
 	if !ok {
-		return staticAttributes{}
+		return staticOrSharedAttributes{}
 	}
 
-	copts := bazel.StringListAttribute{Value: lib.StaticProperties.Static.Cflags}
+	return bp2buildParseStaticOrSharedProps(ctx, module, lib, true)
+}
 
-	srcs := bazel.LabelListAttribute{
-		Value: android.BazelLabelForModuleSrc(ctx, lib.StaticProperties.Static.Srcs)}
+func bp2buildParseStaticOrSharedProps(ctx android.TopDownMutatorContext, module *Module, lib *libraryDecorator, isStatic bool) staticOrSharedAttributes {
+	attrs := staticOrSharedAttributes{}
 
-	staticDeps := bazel.LabelListAttribute{
-		Value: android.BazelLabelForModuleDeps(ctx, lib.StaticProperties.Static.Static_libs)}
+	setAttrs := func(axis bazel.ConfigurationAxis, config string, props StaticOrSharedProperties) {
+		attrs.Copts.SetSelectValue(axis, config, props.Cflags)
+		attrs.Srcs.SetSelectValue(axis, config, android.BazelLabelForModuleSrc(ctx, props.Srcs))
+		attrs.Static_deps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, props.Static_libs))
+		attrs.Dynamic_deps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, props.Shared_libs))
+		attrs.Whole_archive_deps.SetSelectValue(axis, config, android.BazelLabelForModuleWholeDeps(ctx, props.Whole_static_libs))
+		attrs.System_dynamic_deps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, props.System_shared_libs))
+	}
+	// system_dynamic_deps distinguishes between nil/empty list behavior:
+	//    nil -> use default values
+	//    empty list -> no values specified
+	attrs.System_dynamic_deps.ForceSpecifyEmptyList = true
 
-	dynamicDeps := bazel.LabelListAttribute{
-		Value: android.BazelLabelForModuleDeps(ctx, lib.StaticProperties.Static.Shared_libs)}
+	if isStatic {
+		for axis, configToProps := range module.GetArchVariantProperties(ctx, &StaticProperties{}) {
+			for config, props := range configToProps {
+				if staticOrSharedProps, ok := props.(*StaticProperties); ok {
+					setAttrs(axis, config, staticOrSharedProps.Static)
+				}
+			}
+		}
+	} else {
+		for axis, configToProps := range module.GetArchVariantProperties(ctx, &SharedProperties{}) {
+			for config, props := range configToProps {
+				if staticOrSharedProps, ok := props.(*SharedProperties); ok {
+					setAttrs(axis, config, staticOrSharedProps.Shared)
+				}
+			}
+		}
+	}
 
-	wholeArchiveDeps := bazel.LabelListAttribute{
-		Value: android.BazelLabelForModuleDeps(ctx, lib.StaticProperties.Static.Whole_static_libs)}
+	cppSrcs, cSrcs, asSrcs := groupSrcsByExtension(ctx, attrs.Srcs)
+	attrs.Srcs = cppSrcs
+	attrs.Srcs_c = cSrcs
+	attrs.Srcs_as = asSrcs
 
-	return staticAttributes{
-		copts:            copts,
-		srcs:             srcs,
-		staticDeps:       staticDeps,
-		dynamicDeps:      dynamicDeps,
-		wholeArchiveDeps: wholeArchiveDeps,
+	return attrs
+}
+
+// Convenience struct to hold all attributes parsed from prebuilt properties.
+type prebuiltAttributes struct {
+	Src bazel.LabelAttribute
+}
+
+func Bp2BuildParsePrebuiltLibraryProps(ctx android.TopDownMutatorContext, module *Module) prebuiltAttributes {
+	var srcLabelAttribute bazel.LabelAttribute
+
+	for axis, configToProps := range module.GetArchVariantProperties(ctx, &prebuiltLinkerProperties{}) {
+		for config, props := range configToProps {
+			if prebuiltLinkerProperties, ok := props.(*prebuiltLinkerProperties); ok {
+				if len(prebuiltLinkerProperties.Srcs) > 1 {
+					ctx.ModuleErrorf("Bp2BuildParsePrebuiltLibraryProps: Expected at most once source file for %s %s\n", axis, config)
+					continue
+				} else if len(prebuiltLinkerProperties.Srcs) == 0 {
+					continue
+				}
+				src := android.BazelLabelForModuleSrcSingle(ctx, prebuiltLinkerProperties.Srcs[0])
+				srcLabelAttribute.SetSelectValue(axis, config, src)
+			}
+		}
+	}
+
+	return prebuiltAttributes{
+		Src: srcLabelAttribute,
 	}
 }
 
 // Convenience struct to hold all attributes parsed from compiler properties.
 type compilerAttributes struct {
-	copts    bazel.StringListAttribute
+	// Options for all languages
+	copts bazel.StringListAttribute
+	// Assembly options and sources
+	asFlags bazel.StringListAttribute
+	asSrcs  bazel.LabelListAttribute
+	// C options and sources
+	conlyFlags bazel.StringListAttribute
+	cSrcs      bazel.LabelListAttribute
+	// C++ options and sources
+	cppFlags bazel.StringListAttribute
 	srcs     bazel.LabelListAttribute
-	includes bazel.StringListAttribute
+
+	rtti bazel.BoolAttribute
 }
 
 // bp2BuildParseCompilerProps returns copts, srcs and hdrs and other attributes.
 func bp2BuildParseCompilerProps(ctx android.TopDownMutatorContext, module *Module) compilerAttributes {
 	var srcs bazel.LabelListAttribute
 	var copts bazel.StringListAttribute
+	var asFlags bazel.StringListAttribute
+	var conlyFlags bazel.StringListAttribute
+	var cppFlags bazel.StringListAttribute
+	var rtti bazel.BoolAttribute
 
-	// Creates the -I flag for a directory, while making the directory relative
+	// Creates the -I flags for a directory, while making the directory relative
 	// to the exec root for Bazel to work.
-	includeFlag := func(dir string) string {
+	includeFlags := func(dir string) []string {
 		// filepath.Join canonicalizes the path, i.e. it takes care of . or .. elements.
-		return "-I" + filepath.Join(ctx.ModuleDir(), dir)
+		moduleDirRootedPath := filepath.Join(ctx.ModuleDir(), dir)
+		return []string{
+			"-I" + moduleDirRootedPath,
+			// Include the bindir-rooted path (using make variable substitution). This most
+			// closely matches Bazel's native include path handling, which allows for dependency
+			// on generated headers in these directories.
+			// TODO(b/188084383): Handle local include directories in Bazel.
+			"-I$(BINDIR)/" + moduleDirRootedPath,
+		}
 	}
 
 	// Parse the list of module-relative include directories (-I).
@@ -187,114 +249,114 @@
 		return append(includeDirs, baseCompilerProps.Local_include_dirs...)
 	}
 
-	// Parse the list of copts.
-	parseCopts := func(baseCompilerProps *BaseCompilerProperties) []string {
-		var copts []string
-		for _, flag := range append(baseCompilerProps.Cflags, baseCompilerProps.Cppflags...) {
+	parseCommandLineFlags := func(soongFlags []string) []string {
+		var result []string
+		for _, flag := range soongFlags {
 			// Soong's cflags can contain spaces, like `-include header.h`. For
 			// Bazel's copts, split them up to be compatible with the
 			// no_copts_tokenization feature.
-			copts = append(copts, strings.Split(flag, " ")...)
+			result = append(result, strings.Split(flag, " ")...)
 		}
-		for _, dir := range parseLocalIncludeDirs(baseCompilerProps) {
-			copts = append(copts, includeFlag(dir))
-		}
-		return copts
+		return result
 	}
 
-	// baseSrcs contain the list of src files that are used for every configuration.
-	var baseSrcs []string
-	// baseExcludeSrcs contain the list of src files that are excluded for every configuration.
-	var baseExcludeSrcs []string
-	// baseSrcsLabelList is a clone of the base srcs LabelList, used for computing the
-	// arch or os specific srcs later.
-	var baseSrcsLabelList bazel.LabelList
-
-	// Parse srcs from an arch or OS's props value, taking the base srcs and
-	// exclude srcs into account.
+	// Parse srcs from an arch or OS's props value.
 	parseSrcs := func(baseCompilerProps *BaseCompilerProperties) bazel.LabelList {
-		// Combine the base srcs and arch-specific srcs
-		allSrcs := append(baseSrcs, baseCompilerProps.Srcs...)
-		// Combine the base exclude_srcs and configuration-specific exclude_srcs
-		allExcludeSrcs := append(baseExcludeSrcs, baseCompilerProps.Exclude_srcs...)
-		return android.BazelLabelForModuleSrcExcludes(ctx, allSrcs, allExcludeSrcs)
+		// Add srcs-like dependencies such as generated files.
+		// First create a LabelList containing these dependencies, then merge the values with srcs.
+		generatedHdrsAndSrcs := baseCompilerProps.Generated_headers
+		generatedHdrsAndSrcs = append(generatedHdrsAndSrcs, baseCompilerProps.Generated_sources...)
+		generatedHdrsAndSrcsLabelList := android.BazelLabelForModuleDeps(ctx, generatedHdrsAndSrcs)
+
+		allSrcsLabelList := android.BazelLabelForModuleSrcExcludes(ctx, baseCompilerProps.Srcs, baseCompilerProps.Exclude_srcs)
+		return bazel.AppendBazelLabelLists(allSrcsLabelList, generatedHdrsAndSrcsLabelList)
 	}
 
-	for _, props := range module.compiler.compilerProps() {
-		if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
-			srcs.Value = parseSrcs(baseCompilerProps)
-			copts.Value = parseCopts(baseCompilerProps)
+	archVariantCompilerProps := module.GetArchVariantProperties(ctx, &BaseCompilerProperties{})
+	for axis, configToProps := range archVariantCompilerProps {
+		for config, props := range configToProps {
+			if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
+				// If there's arch specific srcs or exclude_srcs, generate a select entry for it.
+				// TODO(b/186153868): do this for OS specific srcs and exclude_srcs too.
+				if len(baseCompilerProps.Srcs) > 0 || len(baseCompilerProps.Exclude_srcs) > 0 {
+					srcsList := parseSrcs(baseCompilerProps)
+					srcs.SetSelectValue(axis, config, srcsList)
+				}
 
-			// Used for arch-specific srcs later.
-			baseSrcs = baseCompilerProps.Srcs
-			baseExcludeSrcs = baseCompilerProps.Exclude_srcs
-			baseSrcsLabelList = parseSrcs(baseCompilerProps)
-			break
-		}
-	}
+				archVariantCopts := parseCommandLineFlags(baseCompilerProps.Cflags)
+				archVariantAsflags := parseCommandLineFlags(baseCompilerProps.Asflags)
+				for _, dir := range parseLocalIncludeDirs(baseCompilerProps) {
+					archVariantCopts = append(archVariantCopts, includeFlags(dir)...)
+					archVariantAsflags = append(archVariantAsflags, includeFlags(dir)...)
+				}
 
-	// Handle include_build_directory prop. If the property is true, then the
-	// target has access to all headers recursively in the package, and has
-	// "-I<module-dir>" in its copts.
-	if c, ok := module.compiler.(*baseCompiler); ok && c.includeBuildDirectory() {
-		copts.Value = append(copts.Value, includeFlag("."))
-	} else if c, ok := module.compiler.(*libraryDecorator); ok && c.includeBuildDirectory() {
-		copts.Value = append(copts.Value, includeFlag("."))
-	}
+				if axis == bazel.NoConfigAxis {
+					if includeBuildDirectory(baseCompilerProps.Include_build_directory) {
+						flags := includeFlags(".")
+						archVariantCopts = append(archVariantCopts, flags...)
+						archVariantAsflags = append(archVariantAsflags, flags...)
+					}
+				}
 
-	for arch, props := range module.GetArchProperties(ctx, &BaseCompilerProperties{}) {
-		if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
-			// If there's arch specific srcs or exclude_srcs, generate a select entry for it.
-			// TODO(b/186153868): do this for OS specific srcs and exclude_srcs too.
-			if len(baseCompilerProps.Srcs) > 0 || len(baseCompilerProps.Exclude_srcs) > 0 {
-				srcsList := parseSrcs(baseCompilerProps)
-				srcs.SetValueForArch(arch.Name, srcsList)
-				// The base srcs value should not contain any arch-specific excludes.
-				srcs.Value = bazel.SubtractBazelLabelList(srcs.Value, bazel.LabelList{Includes: srcsList.Excludes})
+				copts.SetSelectValue(axis, config, archVariantCopts)
+				asFlags.SetSelectValue(axis, config, archVariantAsflags)
+				conlyFlags.SetSelectValue(axis, config, parseCommandLineFlags(baseCompilerProps.Conlyflags))
+				cppFlags.SetSelectValue(axis, config, parseCommandLineFlags(baseCompilerProps.Cppflags))
+				rtti.SetSelectValue(axis, config, baseCompilerProps.Rtti)
 			}
-
-			copts.SetValueForArch(arch.Name, parseCopts(baseCompilerProps))
 		}
 	}
 
-	// After going through all archs, delete the duplicate files in the arch
-	// values that are already in the base srcs.Value.
-	for arch, props := range module.GetArchProperties(ctx, &BaseCompilerProperties{}) {
-		if _, ok := props.(*BaseCompilerProperties); ok {
-			srcs.SetValueForArch(arch.Name, bazel.SubtractBazelLabelList(srcs.GetValueForArch(arch.Name), srcs.Value))
+	srcs.ResolveExcludes()
+
+	productVarPropNameToAttribute := map[string]*bazel.StringListAttribute{
+		"Cflags":   &copts,
+		"Asflags":  &asFlags,
+		"CppFlags": &cppFlags,
+	}
+	productVariableProps := android.ProductVariableProperties(ctx)
+	for propName, attr := range productVarPropNameToAttribute {
+		if props, exists := productVariableProps[propName]; exists {
+			for _, prop := range props {
+				flags, ok := prop.Property.([]string)
+				if !ok {
+					ctx.ModuleErrorf("Could not convert product variable %s property", proptools.PropertyNameForField(propName))
+				}
+				newFlags, _ := bazel.TryVariableSubstitutions(flags, prop.ProductConfigVariable)
+				attr.SetSelectValue(bazel.ProductVariableConfigurationAxis(prop.FullConfig), prop.FullConfig, newFlags)
+			}
 		}
 	}
 
-	// Now that the srcs.Value list is finalized, compare it with the original
-	// list, and put the difference into the default condition for the arch
-	// select.
-	defaultsSrcs := bazel.SubtractBazelLabelList(baseSrcsLabelList, srcs.Value)
-	// TODO(b/186153868): handle the case with multiple variant types, e.g. when arch and os are both used.
-	srcs.SetValueForArch(bazel.CONDITIONS_DEFAULT, defaultsSrcs)
-
-	// Handle OS specific props.
-	for os, props := range module.GetTargetProperties(&BaseCompilerProperties{}) {
-		if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
-			srcsList := parseSrcs(baseCompilerProps)
-			// TODO(b/186153868): add support for os-specific srcs and exclude_srcs
-			srcs.SetValueForOS(os.Name, bazel.SubtractBazelLabelList(srcsList, baseSrcsLabelList))
-			copts.SetValueForOS(os.Name, parseCopts(baseCompilerProps))
-		}
-	}
+	srcs, cSrcs, asSrcs := groupSrcsByExtension(ctx, srcs)
 
 	return compilerAttributes{
-		srcs:  srcs,
-		copts: copts,
+		copts:      copts,
+		srcs:       srcs,
+		asFlags:    asFlags,
+		asSrcs:     asSrcs,
+		cSrcs:      cSrcs,
+		conlyFlags: conlyFlags,
+		cppFlags:   cppFlags,
+		rtti:       rtti,
 	}
 }
 
 // Convenience struct to hold all attributes parsed from linker properties.
 type linkerAttributes struct {
-	deps             bazel.LabelListAttribute
-	dynamicDeps      bazel.LabelListAttribute
-	wholeArchiveDeps bazel.LabelListAttribute
-	linkopts         bazel.StringListAttribute
-	versionScript    bazel.LabelAttribute
+	deps                          bazel.LabelListAttribute
+	dynamicDeps                   bazel.LabelListAttribute
+	systemDynamicDeps             bazel.LabelListAttribute
+	wholeArchiveDeps              bazel.LabelListAttribute
+	exportedDeps                  bazel.LabelListAttribute
+	useLibcrt                     bazel.BoolAttribute
+	linkopts                      bazel.StringListAttribute
+	versionScript                 bazel.LabelAttribute
+	stripKeepSymbols              bazel.BoolAttribute
+	stripKeepSymbolsAndDebugFrame bazel.BoolAttribute
+	stripKeepSymbolsList          bazel.StringListAttribute
+	stripAll                      bazel.BoolAttribute
+	stripNone                     bazel.BoolAttribute
 }
 
 // FIXME(b/187655838): Use the existing linkerFlags() function instead of duplicating logic here
@@ -309,78 +371,144 @@
 // bp2BuildParseLinkerProps parses the linker properties of a module, including
 // configurable attribute values.
 func bp2BuildParseLinkerProps(ctx android.TopDownMutatorContext, module *Module) linkerAttributes {
-	var deps bazel.LabelListAttribute
+	var headerDeps bazel.LabelListAttribute
+	var staticDeps bazel.LabelListAttribute
+	var exportedDeps bazel.LabelListAttribute
 	var dynamicDeps bazel.LabelListAttribute
 	var wholeArchiveDeps bazel.LabelListAttribute
+	systemSharedDeps := bazel.LabelListAttribute{ForceSpecifyEmptyList: true}
 	var linkopts bazel.StringListAttribute
 	var versionScript bazel.LabelAttribute
+	var useLibcrt bazel.BoolAttribute
 
-	for _, linkerProps := range module.linker.linkerProps() {
-		if baseLinkerProps, ok := linkerProps.(*BaseLinkerProperties); ok {
-			libs := baseLinkerProps.Header_libs
-			libs = append(libs, baseLinkerProps.Export_header_lib_headers...)
-			libs = append(libs, baseLinkerProps.Static_libs...)
-			wholeArchiveLibs := baseLinkerProps.Whole_static_libs
-			libs = android.SortedUniqueStrings(libs)
-			deps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, libs))
-			linkopts.Value = getBp2BuildLinkerFlags(baseLinkerProps)
-			wholeArchiveDeps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, wholeArchiveLibs))
+	var stripKeepSymbols bazel.BoolAttribute
+	var stripKeepSymbolsAndDebugFrame bazel.BoolAttribute
+	var stripKeepSymbolsList bazel.StringListAttribute
+	var stripAll bazel.BoolAttribute
+	var stripNone bazel.BoolAttribute
 
-			if baseLinkerProps.Version_script != nil {
-				versionScript.Value = android.BazelLabelForModuleSrcSingle(ctx, *baseLinkerProps.Version_script)
+	for axis, configToProps := range module.GetArchVariantProperties(ctx, &StripProperties{}) {
+		for config, props := range configToProps {
+			if stripProperties, ok := props.(*StripProperties); ok {
+				stripKeepSymbols.SetSelectValue(axis, config, stripProperties.Strip.Keep_symbols)
+				stripKeepSymbolsList.SetSelectValue(axis, config, stripProperties.Strip.Keep_symbols_list)
+				stripKeepSymbolsAndDebugFrame.SetSelectValue(axis, config, stripProperties.Strip.Keep_symbols_and_debug_frame)
+				stripAll.SetSelectValue(axis, config, stripProperties.Strip.All)
+				stripNone.SetSelectValue(axis, config, stripProperties.Strip.None)
+			}
+		}
+	}
+
+	for axis, configToProps := range module.GetArchVariantProperties(ctx, &BaseLinkerProperties{}) {
+		for config, props := range configToProps {
+			if baseLinkerProps, ok := props.(*BaseLinkerProperties); ok {
+				// Excludes to parallel Soong:
+				// https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/linker.go;l=247-249;drc=088b53577dde6e40085ffd737a1ae96ad82fc4b0
+				staticLibs := android.FirstUniqueStrings(baseLinkerProps.Static_libs)
+				staticDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDepsExcludes(ctx, staticLibs, baseLinkerProps.Exclude_static_libs))
+				wholeArchiveLibs := android.FirstUniqueStrings(baseLinkerProps.Whole_static_libs)
+				wholeArchiveDeps.SetSelectValue(axis, config, android.BazelLabelForModuleWholeDepsExcludes(ctx, wholeArchiveLibs, baseLinkerProps.Exclude_static_libs))
+
+				systemSharedLibs := baseLinkerProps.System_shared_libs
+				// systemSharedLibs distinguishes between nil/empty list behavior:
+				//    nil -> use default values
+				//    empty list -> no values specified
+				if len(systemSharedLibs) > 0 {
+					systemSharedLibs = android.FirstUniqueStrings(systemSharedLibs)
+				}
+				systemSharedDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, systemSharedLibs))
+
+				sharedLibs := android.FirstUniqueStrings(baseLinkerProps.Shared_libs)
+				dynamicDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDepsExcludes(ctx, sharedLibs, baseLinkerProps.Exclude_shared_libs))
+
+				headerLibs := android.FirstUniqueStrings(baseLinkerProps.Header_libs)
+				headerDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, headerLibs))
+				exportedLibs := android.FirstUniqueStrings(baseLinkerProps.Export_header_lib_headers)
+				exportedDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, exportedLibs))
+
+				linkopts.SetSelectValue(axis, config, getBp2BuildLinkerFlags(baseLinkerProps))
+				if baseLinkerProps.Version_script != nil {
+					versionScript.SetSelectValue(axis, config, android.BazelLabelForModuleSrcSingle(ctx, *baseLinkerProps.Version_script))
+				}
+				useLibcrt.SetSelectValue(axis, config, baseLinkerProps.libCrt())
+			}
+		}
+	}
+
+	type productVarDep struct {
+		// the name of the corresponding excludes field, if one exists
+		excludesField string
+		// reference to the bazel attribute that should be set for the given product variable config
+		attribute *bazel.LabelListAttribute
+
+		depResolutionFunc func(ctx android.BazelConversionPathContext, modules, excludes []string) bazel.LabelList
+	}
+
+	productVarToDepFields := map[string]productVarDep{
+		// product variables do not support exclude_shared_libs
+		"Shared_libs":       productVarDep{attribute: &dynamicDeps, depResolutionFunc: android.BazelLabelForModuleDepsExcludes},
+		"Static_libs":       productVarDep{"Exclude_static_libs", &staticDeps, android.BazelLabelForModuleDepsExcludes},
+		"Whole_static_libs": productVarDep{"Exclude_static_libs", &wholeArchiveDeps, android.BazelLabelForModuleWholeDepsExcludes},
+	}
+
+	productVariableProps := android.ProductVariableProperties(ctx)
+	for name, dep := range productVarToDepFields {
+		props, exists := productVariableProps[name]
+		excludeProps, excludesExists := productVariableProps[dep.excludesField]
+		// if neither an include or excludes property exists, then skip it
+		if !exists && !excludesExists {
+			continue
+		}
+		// collect all the configurations that an include or exclude property exists for.
+		// we want to iterate all configurations rather than either the include or exclude because for a
+		// particular configuration we may have only and include or only an exclude to handle
+		configs := make(map[string]bool, len(props)+len(excludeProps))
+		for config := range props {
+			configs[config] = true
+		}
+		for config := range excludeProps {
+			configs[config] = true
+		}
+
+		for config := range configs {
+			prop, includesExists := props[config]
+			excludesProp, excludesExists := excludeProps[config]
+			var includes, excludes []string
+			var ok bool
+			// if there was no includes/excludes property, casting fails and that's expected
+			if includes, ok = prop.Property.([]string); includesExists && !ok {
+				ctx.ModuleErrorf("Could not convert product variable %s property", name)
+			}
+			if excludes, ok = excludesProp.Property.([]string); excludesExists && !ok {
+				ctx.ModuleErrorf("Could not convert product variable %s property", dep.excludesField)
 			}
 
-			sharedLibs := baseLinkerProps.Shared_libs
-			dynamicDeps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, sharedLibs))
-
-			break
+			dep.attribute.SetSelectValue(bazel.ProductVariableConfigurationAxis(config), config, dep.depResolutionFunc(ctx, android.FirstUniqueStrings(includes), excludes))
 		}
 	}
 
-	for arch, p := range module.GetArchProperties(ctx, &BaseLinkerProperties{}) {
-		if baseLinkerProps, ok := p.(*BaseLinkerProperties); ok {
-			libs := baseLinkerProps.Header_libs
-			libs = append(libs, baseLinkerProps.Export_header_lib_headers...)
-			libs = append(libs, baseLinkerProps.Static_libs...)
-			wholeArchiveLibs := baseLinkerProps.Whole_static_libs
-			libs = android.SortedUniqueStrings(libs)
-			deps.SetValueForArch(arch.Name, android.BazelLabelForModuleDeps(ctx, libs))
-			linkopts.SetValueForArch(arch.Name, getBp2BuildLinkerFlags(baseLinkerProps))
-			wholeArchiveDeps.SetValueForArch(arch.Name, android.BazelLabelForModuleDeps(ctx, wholeArchiveLibs))
+	staticDeps.ResolveExcludes()
+	dynamicDeps.ResolveExcludes()
+	wholeArchiveDeps.ResolveExcludes()
 
-			if baseLinkerProps.Version_script != nil {
-				versionScript.SetValueForArch(arch.Name,
-					android.BazelLabelForModuleSrcSingle(ctx, *baseLinkerProps.Version_script))
-			}
-
-			sharedLibs := baseLinkerProps.Shared_libs
-			dynamicDeps.SetValueForArch(arch.Name, android.BazelLabelForModuleDeps(ctx, sharedLibs))
-		}
-	}
-
-	for os, p := range module.GetTargetProperties(&BaseLinkerProperties{}) {
-		if baseLinkerProps, ok := p.(*BaseLinkerProperties); ok {
-			libs := baseLinkerProps.Header_libs
-			libs = append(libs, baseLinkerProps.Export_header_lib_headers...)
-			libs = append(libs, baseLinkerProps.Static_libs...)
-			wholeArchiveLibs := baseLinkerProps.Whole_static_libs
-			libs = android.SortedUniqueStrings(libs)
-			wholeArchiveDeps.SetValueForOS(os.Name, android.BazelLabelForModuleDeps(ctx, wholeArchiveLibs))
-			deps.SetValueForOS(os.Name, android.BazelLabelForModuleDeps(ctx, libs))
-
-			linkopts.SetValueForOS(os.Name, getBp2BuildLinkerFlags(baseLinkerProps))
-
-			sharedLibs := baseLinkerProps.Shared_libs
-			dynamicDeps.SetValueForOS(os.Name, android.BazelLabelForModuleDeps(ctx, sharedLibs))
-		}
-	}
+	headerDeps.Append(staticDeps)
 
 	return linkerAttributes{
-		deps:             deps,
-		dynamicDeps:      dynamicDeps,
-		wholeArchiveDeps: wholeArchiveDeps,
-		linkopts:         linkopts,
-		versionScript:    versionScript,
+		deps:              headerDeps,
+		exportedDeps:      exportedDeps,
+		dynamicDeps:       dynamicDeps,
+		systemDynamicDeps: systemSharedDeps,
+		wholeArchiveDeps:  wholeArchiveDeps,
+		linkopts:          linkopts,
+		useLibcrt:         useLibcrt,
+		versionScript:     versionScript,
+
+		// Strip properties
+		stripKeepSymbols:              stripKeepSymbols,
+		stripKeepSymbolsAndDebugFrame: stripKeepSymbolsAndDebugFrame,
+		stripKeepSymbolsList:          stripKeepSymbolsList,
+		stripAll:                      stripAll,
+		stripNone:                     stripNone,
 	}
 }
 
@@ -407,44 +535,46 @@
 	return relativePaths
 }
 
-// bp2BuildParseExportedIncludes creates a string list attribute contains the
-// exported included directories of a module.
 func bp2BuildParseExportedIncludes(ctx android.TopDownMutatorContext, module *Module) bazel.StringListAttribute {
 	libraryDecorator := module.linker.(*libraryDecorator)
+	return bp2BuildParseExportedIncludesHelper(ctx, module, libraryDecorator)
+}
 
+func Bp2BuildParseExportedIncludesForPrebuiltLibrary(ctx android.TopDownMutatorContext, module *Module) bazel.StringListAttribute {
+	prebuiltLibraryLinker := module.linker.(*prebuiltLibraryLinker)
+	libraryDecorator := prebuiltLibraryLinker.libraryDecorator
+	return bp2BuildParseExportedIncludesHelper(ctx, module, libraryDecorator)
+}
+
+// bp2BuildParseExportedIncludes creates a string list attribute contains the
+// exported included directories of a module.
+func bp2BuildParseExportedIncludesHelper(ctx android.TopDownMutatorContext, module *Module, libraryDecorator *libraryDecorator) bazel.StringListAttribute {
 	// Export_system_include_dirs and export_include_dirs are already module dir
 	// relative, so they don't need to be relativized like include_dirs, which
 	// are root-relative.
 	includeDirs := libraryDecorator.flagExporter.Properties.Export_system_include_dirs
 	includeDirs = append(includeDirs, libraryDecorator.flagExporter.Properties.Export_include_dirs...)
-	includeDirsAttribute := bazel.MakeStringListAttribute(includeDirs)
+	var includeDirsAttribute bazel.StringListAttribute
 
-	for arch, props := range module.GetArchProperties(ctx, &FlagExporterProperties{}) {
-		if flagExporterProperties, ok := props.(*FlagExporterProperties); ok {
-			archIncludeDirs := flagExporterProperties.Export_system_include_dirs
-			archIncludeDirs = append(archIncludeDirs, flagExporterProperties.Export_include_dirs...)
+	getVariantIncludeDirs := func(includeDirs []string, flagExporterProperties *FlagExporterProperties, subtract bool) []string {
+		variantIncludeDirs := flagExporterProperties.Export_system_include_dirs
+		variantIncludeDirs = append(variantIncludeDirs, flagExporterProperties.Export_include_dirs...)
 
+		if subtract {
 			// To avoid duplicate includes when base includes + arch includes are combined
-			// FIXME: This doesn't take conflicts between arch and os includes into account
-			archIncludeDirs = bazel.SubtractStrings(archIncludeDirs, includeDirs)
-
-			if len(archIncludeDirs) > 0 {
-				includeDirsAttribute.SetValueForArch(arch.Name, archIncludeDirs)
-			}
+			// TODO: Add something similar to ResolveExcludes() in bazel/properties.go
+			variantIncludeDirs = bazel.SubtractStrings(variantIncludeDirs, includeDirs)
 		}
+		return variantIncludeDirs
 	}
 
-	for os, props := range module.GetTargetProperties(&FlagExporterProperties{}) {
-		if flagExporterProperties, ok := props.(*FlagExporterProperties); ok {
-			osIncludeDirs := flagExporterProperties.Export_system_include_dirs
-			osIncludeDirs = append(osIncludeDirs, flagExporterProperties.Export_include_dirs...)
-
-			// To avoid duplicate includes when base includes + os includes are combined
-			// FIXME: This doesn't take conflicts between arch and os includes into account
-			osIncludeDirs = bazel.SubtractStrings(osIncludeDirs, includeDirs)
-
-			if len(osIncludeDirs) > 0 {
-				includeDirsAttribute.SetValueForOS(os.Name, osIncludeDirs)
+	for axis, configToProps := range module.GetArchVariantProperties(ctx, &FlagExporterProperties{}) {
+		for config, props := range configToProps {
+			if flagExporterProperties, ok := props.(*FlagExporterProperties); ok {
+				archVariantIncludeDirs := getVariantIncludeDirs(includeDirs, flagExporterProperties, axis != bazel.NoConfigAxis)
+				if len(archVariantIncludeDirs) > 0 {
+					includeDirsAttribute.SetSelectValue(axis, config, archVariantIncludeDirs)
+				}
 			}
 		}
 	}
diff --git a/cc/builder.go b/cc/builder.go
index 51c8a0b..748377b 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -205,11 +205,13 @@
 			Labels:       map[string]string{"type": "lint", "tool": "clang-tidy", "lang": "cpp"},
 			ExecStrategy: "${config.REClangTidyExecStrategy}",
 			Inputs:       []string{"$in"},
-			// OutputFile here is $in for remote-execution since its possible that
-			// clang-tidy modifies the given input file itself and $out refers to the
-			// ".tidy" file generated for ninja-dependency reasons.
-			OutputFiles: []string{"$in"},
-			Platform:    map[string]string{remoteexec.PoolKey: "${config.REClangTidyPool}"},
+			// Although clang-tidy has an option to "fix" source files, that feature is hardly useable
+			// under parallel compilation and RBE. So we assume no OutputFiles here.
+			// The clang-tidy fix option is best run locally in single thread.
+			// Copying source file back to local caused two problems:
+			// (1) New timestamps trigger clang and clang-tidy compilations again.
+			// (2) Changing source files caused concurrent clang or clang-tidy jobs to crash.
+			Platform: map[string]string{remoteexec.PoolKey: "${config.REClangTidyPool}"},
 		}, []string{"cFlags", "tidyFlags"}, []string{})
 
 	_ = pctx.SourcePathVariable("yasmCmd", "prebuilts/misc/${config.HostPrebuiltTag}/yasm/yasm")
@@ -237,7 +239,7 @@
 	// -w has been added since header-abi-dumper does not need to produce any sort of diagnostic information.
 	sAbiDump, sAbiDumpRE = pctx.RemoteStaticRules("sAbiDump",
 		blueprint.RuleParams{
-			Command:     "rm -f $out && $reTemplate$sAbiDumper -o ${out} $in $exportDirs -- $cFlags -w -isystem prebuilts/clang-tools/${config.HostPrebuiltTag}/clang-headers",
+			Command:     "rm -f $out && $reTemplate$sAbiDumper --root-dir . --root-dir $$OUT_DIR:out -o ${out} $in $exportDirs -- $cFlags -w -isystem prebuilts/clang-tools/${config.HostPrebuiltTag}/clang-headers",
 			CommandDeps: []string{"$sAbiDumper"},
 		}, &remoteexec.REParams{
 			Labels:       map[string]string{"type": "abi-dump", "tool": "header-abi-dumper"},
@@ -255,7 +257,7 @@
 	// sAbi dump file.
 	sAbiLink, sAbiLinkRE = pctx.RemoteStaticRules("sAbiLink",
 		blueprint.RuleParams{
-			Command:        "$reTemplate$sAbiLinker -o ${out} $symbolFilter -arch $arch  $exportedHeaderFlags @${out}.rsp ",
+			Command:        "$reTemplate$sAbiLinker --root-dir . --root-dir $$OUT_DIR:out -o ${out} $symbolFilter -arch $arch $exportedHeaderFlags @${out}.rsp",
 			CommandDeps:    []string{"$sAbiLinker"},
 			Rspfile:        "${out}.rsp",
 			RspfileContent: "${in}",
@@ -384,9 +386,6 @@
 
 	systemIncludeFlags string
 
-	// True if static libraries should be grouped (using `-Wl,--start-group` and `-Wl,--end-group`).
-	groupStaticLibs bool
-
 	proto            android.ProtoFlags
 	protoC           bool // If true, compile protos as `.c` files. Otherwise, output as `.cc`.
 	protoOptionsFile bool // If true, output a proto options file.
@@ -500,10 +499,10 @@
 		sAbiDumpFiles = make(android.Paths, 0, len(srcFiles))
 	}
 
-	cflags += " ${config.NoOverrideClangGlobalCflags}"
-	toolingCflags += " ${config.NoOverrideClangGlobalCflags}"
-	cppflags += " ${config.NoOverrideClangGlobalCflags}"
-	toolingCppflags += " ${config.NoOverrideClangGlobalCflags}"
+	cflags += " ${config.NoOverrideGlobalCflags}"
+	toolingCflags += " ${config.NoOverrideGlobalCflags}"
+	cppflags += " ${config.NoOverrideGlobalCflags}"
+	toolingCppflags += " ${config.NoOverrideGlobalCflags}"
 
 	for i, srcFile := range srcFiles {
 		objFile := android.ObjPathWithExt(ctx, subdir, srcFile, "o")
@@ -534,7 +533,7 @@
 				Implicits:   cFlagsDeps,
 				OrderOnly:   pathDeps,
 				Args: map[string]string{
-					"windresCmd": gccCmd(flags.toolchain, "windres"),
+					"windresCmd": mingwCmd(flags.toolchain, "windres"),
 					"flags":      flags.toolchain.WindresFlags(),
 				},
 			})
@@ -646,7 +645,7 @@
 				OrderOnly: pathDeps,
 				Args: map[string]string{
 					"cFlags":    moduleToolingFlags,
-					"tidyFlags": flags.tidyFlags,
+					"tidyFlags": config.TidyFlagsForSrcFile(srcFile, flags.tidyFlags),
 				},
 			})
 		}
@@ -730,8 +729,9 @@
 // Generate a rule for compiling multiple .o files, plus static libraries, whole static libraries,
 // and shared libraries, to a shared library (.so) or dynamic executable
 func transformObjToDynamicBinary(ctx android.ModuleContext,
-	objFiles, sharedLibs, staticLibs, lateStaticLibs, wholeStaticLibs, deps android.Paths,
-	crtBegin, crtEnd android.OptionalPath, groupLate bool, flags builderFlags, outputFile android.WritablePath, implicitOutputs android.WritablePaths) {
+	objFiles, sharedLibs, staticLibs, lateStaticLibs, wholeStaticLibs, deps, crtBegin, crtEnd android.Paths,
+	groupLate bool, flags builderFlags, outputFile android.WritablePath,
+	implicitOutputs android.WritablePaths, validations android.WritablePaths) {
 
 	ldCmd := "${config.ClangBin}/clang++"
 
@@ -751,13 +751,7 @@
 		}
 	}
 
-	if flags.groupStaticLibs && !ctx.Darwin() && len(staticLibs) > 0 {
-		libFlagsList = append(libFlagsList, "-Wl,--start-group")
-	}
 	libFlagsList = append(libFlagsList, staticLibs.Strings()...)
-	if flags.groupStaticLibs && !ctx.Darwin() && len(staticLibs) > 0 {
-		libFlagsList = append(libFlagsList, "-Wl,--end-group")
-	}
 
 	if groupLate && !ctx.Darwin() && len(lateStaticLibs) > 0 {
 		libFlagsList = append(libFlagsList, "-Wl,--start-group")
@@ -778,18 +772,17 @@
 	deps = append(deps, staticLibs...)
 	deps = append(deps, lateStaticLibs...)
 	deps = append(deps, wholeStaticLibs...)
-	if crtBegin.Valid() {
-		deps = append(deps, crtBegin.Path(), crtEnd.Path())
-	}
+	deps = append(deps, crtBegin...)
+	deps = append(deps, crtEnd...)
 
 	rule := ld
 	args := map[string]string{
 		"ldCmd":         ldCmd,
-		"crtBegin":      crtBegin.String(),
+		"crtBegin":      strings.Join(crtBegin.Strings(), " "),
 		"libFlags":      strings.Join(libFlagsList, " "),
 		"extraLibFlags": flags.extraLibFlags,
 		"ldFlags":       flags.globalLdFlags + " " + flags.localLdFlags,
-		"crtEnd":        crtEnd.String(),
+		"crtEnd":        strings.Join(crtEnd.Strings(), " "),
 	}
 	if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_CXX_LINKS") {
 		rule = ldRE
@@ -805,6 +798,7 @@
 		Inputs:          objFiles,
 		Implicits:       deps,
 		OrderOnly:       sharedLibs,
+		Validations:     validations.Paths(),
 		Args:            args,
 	})
 }
@@ -1069,6 +1063,6 @@
 	})
 }
 
-func gccCmd(toolchain config.Toolchain, cmd string) string {
+func mingwCmd(toolchain config.Toolchain, cmd string) string {
 	return filepath.Join(toolchain.GccRoot(), "bin", toolchain.GccTriple()+"-"+cmd)
 }
diff --git a/cc/cc.go b/cc/cc.go
index c62fd6c..d8e6feb 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -29,6 +29,7 @@
 
 	"android/soong/android"
 	"android/soong/cc/config"
+	"android/soong/fuzz"
 	"android/soong/genrule"
 )
 
@@ -53,25 +54,9 @@
 	})
 
 	ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.TopDown("asan_deps", sanitizerDepsMutator(Asan))
-		ctx.BottomUp("asan", sanitizerMutator(Asan)).Parallel()
-
-		ctx.TopDown("hwasan_deps", sanitizerDepsMutator(Hwasan))
-		ctx.BottomUp("hwasan", sanitizerMutator(Hwasan)).Parallel()
-
-		ctx.TopDown("fuzzer_deps", sanitizerDepsMutator(Fuzzer))
-		ctx.BottomUp("fuzzer", sanitizerMutator(Fuzzer)).Parallel()
-
-		// cfi mutator shouldn't run before sanitizers that return true for
-		// incompatibleWithCfi()
-		ctx.TopDown("cfi_deps", sanitizerDepsMutator(cfi))
-		ctx.BottomUp("cfi", sanitizerMutator(cfi)).Parallel()
-
-		ctx.TopDown("scs_deps", sanitizerDepsMutator(scs))
-		ctx.BottomUp("scs", sanitizerMutator(scs)).Parallel()
-
-		ctx.TopDown("tsan_deps", sanitizerDepsMutator(tsan))
-		ctx.BottomUp("tsan", sanitizerMutator(tsan)).Parallel()
+		for _, san := range Sanitizers {
+			san.registerMutators(ctx)
+		}
 
 		ctx.TopDown("sanitize_runtime_deps", sanitizerRuntimeDepsMutator).Parallel()
 		ctx.BottomUp("sanitize_runtime", sanitizerRuntimeMutator).Parallel()
@@ -126,11 +111,10 @@
 
 	ReexportGeneratedHeaders []string
 
-	CrtBegin, CrtEnd string
+	CrtBegin, CrtEnd []string
 
 	// Used for host bionic
-	LinkerFlagsFile string
-	DynamicLinker   string
+	DynamicLinker string
 
 	// List of libs that need to be excluded for APEX variant
 	ExcludeLibsForApex []string
@@ -177,10 +161,7 @@
 	ReexportedDeps             android.Paths
 
 	// Paths to crt*.o files
-	CrtBegin, CrtEnd android.OptionalPath
-
-	// Path to the file container flags to use with the linker
-	LinkerFlagsFile android.OptionalPath
+	CrtBegin, CrtEnd android.Paths
 
 	// Path to the dynamic linker binary
 	DynamicLinker android.OptionalPath
@@ -236,8 +217,6 @@
 
 	// True if .s files should be processed with the c preprocessor.
 	AssemblerWithCpp bool
-	// True if static libraries should be grouped (using `-Wl,--start-group` and `-Wl,--end-group`).
-	GroupStaticLibs bool
 
 	proto            android.ProtoFlags
 	protoC           bool // Whether to use C instead of C++
@@ -290,9 +269,15 @@
 	// Set by DepsMutator.
 	AndroidMkSystemSharedLibs []string `blueprint:"mutated"`
 
+	// The name of the image this module is built for, suffixed with a '.'
 	ImageVariationPrefix string `blueprint:"mutated"`
-	VndkVersion          string `blueprint:"mutated"`
-	SubName              string `blueprint:"mutated"`
+
+	// The VNDK version this module is built against. If empty, the module is not
+	// build against the VNDK.
+	VndkVersion string `blueprint:"mutated"`
+
+	// Suffix for the name of Android.mk entries generated by this module
+	SubName string `blueprint:"mutated"`
 
 	// *.logtags files, to combine together in order to generate the /system/etc/event-log-tags
 	// file
@@ -315,12 +300,15 @@
 	// Make this module available when building for recovery
 	Recovery_available *bool
 
-	// Set by imageMutator
-	CoreVariantNeeded          bool     `blueprint:"mutated"`
-	RamdiskVariantNeeded       bool     `blueprint:"mutated"`
-	VendorRamdiskVariantNeeded bool     `blueprint:"mutated"`
-	RecoveryVariantNeeded      bool     `blueprint:"mutated"`
-	ExtraVariants              []string `blueprint:"mutated"`
+	// Used by imageMutator, set by ImageMutatorBegin()
+	CoreVariantNeeded          bool `blueprint:"mutated"`
+	RamdiskVariantNeeded       bool `blueprint:"mutated"`
+	VendorRamdiskVariantNeeded bool `blueprint:"mutated"`
+	RecoveryVariantNeeded      bool `blueprint:"mutated"`
+
+	// A list of variations for the "image" mutator of the form
+	//<image name> '.' <version char>, for example, 'vendor.S'
+	ExtraVersionedImageVariations []string `blueprint:"mutated"`
 
 	// Allows this module to use non-APEX version of libraries. Useful
 	// for building binaries that are started before APEXes are activated.
@@ -332,6 +320,7 @@
 
 	// Used by vendor snapshot to record dependencies from snapshot modules.
 	SnapshotSharedLibs  []string `blueprint:"mutated"`
+	SnapshotStaticLibs  []string `blueprint:"mutated"`
 	SnapshotRuntimeLibs []string `blueprint:"mutated"`
 
 	Installable *bool
@@ -364,6 +353,24 @@
 	// can depend on libraries that are not exported by the APEXes and use private symbols
 	// from the exported libraries.
 	Test_for []string `android:"arch_variant"`
+
+	Target struct {
+		Platform struct {
+			// List of modules required by the core variant.
+			Required []string `android:"arch_variant"`
+
+			// List of modules not required by the core variant.
+			Exclude_required []string `android:"arch_variant"`
+		} `android:"arch_variant"`
+
+		Recovery struct {
+			// List of modules required by the recovery variant.
+			Required []string `android:"arch_variant"`
+
+			// List of modules not required by the recovery variant.
+			Exclude_required []string `android:"arch_variant"`
+		} `android:"arch_variant"`
+	} `android:"arch_variant"`
 }
 
 type VendorProperties struct {
@@ -520,8 +527,6 @@
 // feature represents additional (optional) steps to building cc-related modules, such as invocation
 // of clang-tidy.
 type feature interface {
-	begin(ctx BaseModuleContext)
-	deps(ctx DepsContext, deps Deps) Deps
 	flags(ctx ModuleContext, flags Flags) Flags
 	props() []interface{}
 }
@@ -580,17 +585,7 @@
 	hostToolPath() android.OptionalPath
 	relativeInstallPath() string
 	makeUninstallable(mod *Module)
-}
-
-// bazelHandler is the interface for a helper object related to deferring to Bazel for
-// processing a module (during Bazel mixed builds). Individual module types should define
-// their own bazel handler if they support deferring to Bazel.
-type bazelHandler interface {
-	// Issue query to Bazel to retrieve information about Bazel's view of the current module.
-	// If Bazel returns this information, set module properties on the current module to reflect
-	// the returned information.
-	// Returns true if information was available from Bazel, false if bazel invocation still needs to occur.
-	generateBazelBuildActions(ctx android.ModuleContext, label string) bool
+	installInRoot() bool
 }
 
 type xref interface {
@@ -717,7 +712,6 @@
 	genHeaderDepTag       = dependencyTag{name: "gen header"}
 	genHeaderExportDepTag = dependencyTag{name: "gen header export"}
 	objDepTag             = dependencyTag{name: "obj"}
-	linkerFlagsDepTag     = dependencyTag{name: "linker flags file"}
 	dynamicLinkerDepTag   = installDependencyTag{name: "dynamic linker"}
 	reuseObjTag           = dependencyTag{name: "reuse objects"}
 	staticVariantTag      = dependencyTag{name: "static variant"}
@@ -726,7 +720,6 @@
 	runtimeDepTag         = installDependencyTag{name: "runtime lib"}
 	testPerSrcDepTag      = dependencyTag{name: "test_per_src"}
 	stubImplDepTag        = dependencyTag{name: "stub_impl"}
-	llndkStubDepTag       = dependencyTag{name: "llndk stub"}
 )
 
 func IsSharedDepTag(depTag blueprint.DependencyTag) bool {
@@ -768,14 +761,13 @@
 // members of the cc.Module to this decorator. Thus, a cc_binary module has custom linker and
 // installer logic.
 type Module struct {
-	android.ModuleBase
-	android.DefaultableModuleBase
-	android.ApexModuleBase
+	fuzz.FuzzModule
+
 	android.SdkBase
 	android.BazelModuleBase
 
-	Properties       BaseProperties
 	VendorProperties VendorProperties
+	Properties       BaseProperties
 
 	// initialize before calling Init
 	hod      android.HostOrDeviceSupported
@@ -792,7 +784,7 @@
 	compiler     compiler
 	linker       linker
 	installer    installer
-	bazelHandler bazelHandler
+	bazelHandler android.BazelHandler
 
 	features []feature
 	stl      *stl
@@ -827,6 +819,64 @@
 	hideApexVariantFromMake bool
 }
 
+func (c *Module) AddJSONData(d *map[string]interface{}) {
+	var hasAidl, hasLex, hasProto, hasRenderscript, hasSysprop, hasWinMsg, hasYacc bool
+	if b, ok := c.compiler.(*baseCompiler); ok {
+		hasAidl = b.hasSrcExt(".aidl")
+		hasLex = b.hasSrcExt(".l") || b.hasSrcExt(".ll")
+		hasProto = b.hasSrcExt(".proto")
+		hasRenderscript = b.hasSrcExt(".rscript") || b.hasSrcExt(".fs")
+		hasSysprop = b.hasSrcExt(".sysprop")
+		hasWinMsg = b.hasSrcExt(".mc")
+		hasYacc = b.hasSrcExt(".y") || b.hasSrcExt(".yy")
+	}
+	c.AndroidModuleBase().AddJSONData(d)
+	(*d)["Cc"] = map[string]interface{}{
+		"SdkVersion":             c.SdkVersion(),
+		"MinSdkVersion":          c.MinSdkVersion(),
+		"VndkVersion":            c.VndkVersion(),
+		"ProductSpecific":        c.ProductSpecific(),
+		"SocSpecific":            c.SocSpecific(),
+		"DeviceSpecific":         c.DeviceSpecific(),
+		"InProduct":              c.InProduct(),
+		"InVendor":               c.InVendor(),
+		"InRamdisk":              c.InRamdisk(),
+		"InVendorRamdisk":        c.InVendorRamdisk(),
+		"InRecovery":             c.InRecovery(),
+		"VendorAvailable":        c.VendorAvailable(),
+		"ProductAvailable":       c.ProductAvailable(),
+		"RamdiskAvailable":       c.RamdiskAvailable(),
+		"VendorRamdiskAvailable": c.VendorRamdiskAvailable(),
+		"RecoveryAvailable":      c.RecoveryAvailable(),
+		"OdmAvailable":           c.OdmAvailable(),
+		"InstallInData":          c.InstallInData(),
+		"InstallInRamdisk":       c.InstallInRamdisk(),
+		"InstallInSanitizerDir":  c.InstallInSanitizerDir(),
+		"InstallInVendorRamdisk": c.InstallInVendorRamdisk(),
+		"InstallInRecovery":      c.InstallInRecovery(),
+		"InstallInRoot":          c.InstallInRoot(),
+		"IsVndk":                 c.IsVndk(),
+		"IsVndkExt":              c.IsVndkExt(),
+		"IsVndkPrivate":          c.IsVndkPrivate(),
+		"IsVndkSp":               c.IsVndkSp(),
+		"IsLlndk":                c.IsLlndk(),
+		"IsLlndkPublic":          c.IsLlndkPublic(),
+		"IsSnapshotLibrary":      c.IsSnapshotLibrary(),
+		"IsSnapshotPrebuilt":     c.IsSnapshotPrebuilt(),
+		"IsVendorPublicLibrary":  c.IsVendorPublicLibrary(),
+		"ApexSdkVersion":         c.apexSdkVersion,
+		"TestFor":                c.TestFor(),
+		"AidlSrcs":               hasAidl,
+		"LexSrcs":                hasLex,
+		"ProtoSrcs":              hasProto,
+		"RenderscriptSrcs":       hasRenderscript,
+		"SyspropSrcs":            hasSysprop,
+		"WinMsgSrcs":             hasWinMsg,
+		"YaccSrsc":               hasYacc,
+		"OnlyCSrcs":              !(hasAidl || hasLex || hasProto || hasRenderscript || hasSysprop || hasWinMsg || hasYacc),
+	}
+}
+
 func (c *Module) SetPreventInstall() {
 	c.Properties.PreventInstall = true
 }
@@ -839,6 +889,18 @@
 	return c.Properties.HideFromMake
 }
 
+func (c *Module) RequiredModuleNames() []string {
+	required := android.CopyOf(c.ModuleBase.RequiredModuleNames())
+	if c.ImageVariation().Variation == android.CoreVariation {
+		required = append(required, c.Properties.Target.Platform.Required...)
+		required = removeListFromList(required, c.Properties.Target.Platform.Exclude_required)
+	} else if c.InRecovery() {
+		required = append(required, c.Properties.Target.Recovery.Required...)
+		required = removeListFromList(required, c.Properties.Target.Recovery.Exclude_required)
+	}
+	return android.FirstUniqueStrings(required)
+}
+
 func (c *Module) Toc() android.OptionalPath {
 	if c.linker != nil {
 		if library, ok := c.linker.(libraryInterface); ok {
@@ -911,16 +973,17 @@
 	return String(c.Properties.Min_sdk_version)
 }
 
-func (c *Module) SplitPerApiLevel() bool {
-	if !c.canUseSdk() {
-		return false
-	}
+func (c *Module) isCrt() bool {
 	if linker, ok := c.linker.(*objectLinker); ok {
 		return linker.isCrt()
 	}
 	return false
 }
 
+func (c *Module) SplitPerApiLevel() bool {
+	return c.canUseSdk() && c.isCrt()
+}
+
 func (c *Module) AlwaysSdk() bool {
 	return c.Properties.AlwaysSdk || Bool(c.Properties.Sdk_variant_only)
 }
@@ -1075,17 +1138,6 @@
 	return c
 }
 
-// Returns true for dependency roots (binaries)
-// TODO(ccross): also handle dlopenable libraries
-func (c *Module) IsDependencyRoot() bool {
-	if root, ok := c.linker.(interface {
-		isDependencyRoot() bool
-	}); ok {
-		return root.isDependencyRoot()
-	}
-	return false
-}
-
 func (c *Module) UseVndk() bool {
 	return c.Properties.VndkVersion != ""
 }
@@ -1255,7 +1307,7 @@
 	return name
 }
 
-func (c *Module) bootstrap() bool {
+func (c *Module) Bootstrap() bool {
 	return Bool(c.Properties.Bootstrap)
 }
 
@@ -1268,8 +1320,8 @@
 }
 
 func (c *Module) IsSnapshotPrebuilt() bool {
-	if p, ok := c.linker.(snapshotInterface); ok {
-		return p.isSnapshotPrebuilt()
+	if p, ok := c.linker.(SnapshotInterface); ok {
+		return p.IsSnapshotPrebuilt()
 	}
 	return false
 }
@@ -1306,6 +1358,10 @@
 		Bool(c.sanitize.Properties.Sanitize.Config.Cfi_assembly_support)
 }
 
+func (c *Module) InstallInRoot() bool {
+	return c.installer != nil && c.installer.installInRoot()
+}
+
 type baseModuleContext struct {
 	android.BaseModuleContext
 	moduleContextImpl
@@ -1389,12 +1445,20 @@
 	// create versioned variants for. For example, if min_sdk_version is 16, then sdk variant of
 	// the crt object has local variants of 16, 17, ..., up to the latest version. sdk_version
 	// and min_sdk_version properties of the variants are set to the corresponding version
-	// numbers. However, the platform (non-sdk) variant of the crt object is left untouched.
-	// min_sdk_version: 16 doesn't actually mean that the platform variant has to support such
-	// an old version. Since the variant is for the platform, it's preferred to target the
-	// latest version.
-	if ctx.mod.SplitPerApiLevel() && !ctx.isSdkVariant() {
-		ver = strconv.Itoa(android.FutureApiLevelInt)
+	// numbers. However, the non-sdk variant (for apex or platform) of the crt object is left
+	// untouched.  min_sdk_version: 16 doesn't actually mean that the non-sdk variant has to
+	// support such an old version. The version is set to the later version in case when the
+	// non-sdk variant is for the platform, or the min_sdk_version of the containing APEX if
+	// it's for an APEX.
+	if ctx.mod.isCrt() && !ctx.isSdkVariant() {
+		if ctx.isForPlatform() {
+			ver = strconv.Itoa(android.FutureApiLevelInt)
+		} else { // for apex
+			ver = ctx.apexSdkVersion().String()
+			if ver == "" { // in case when min_sdk_version was not set by the APEX
+				ver = ctx.sdkVersion()
+			}
+		}
 	}
 
 	// Also make sure that minSdkVersion is not greater than sdkVersion, if they are both numbers
@@ -1496,7 +1560,7 @@
 }
 
 func (ctx *moduleContextImpl) bootstrap() bool {
-	return ctx.mod.bootstrap()
+	return ctx.mod.Bootstrap()
 }
 
 func (ctx *moduleContextImpl) nativeCoverage() bool {
@@ -1593,7 +1657,7 @@
 			return ""
 		}
 		vndkVersion = ctx.DeviceConfig().ProductVndkVersion()
-		nameSuffix = productSuffix
+		nameSuffix = ProductSuffix
 	} else {
 		vndkVersion = ctx.DeviceConfig().VndkVersion()
 		nameSuffix = VendorSuffix
@@ -1613,7 +1677,7 @@
 	c.Properties.SubName = ""
 
 	if c.Target().NativeBridge == android.NativeBridgeEnabled {
-		c.Properties.SubName += nativeBridgeSuffix
+		c.Properties.SubName += NativeBridgeSuffix
 	}
 
 	llndk := c.IsLlndk()
@@ -1629,11 +1693,11 @@
 		// such suffixes are already hard-coded in prebuilts/vndk/.../Android.bp.
 		c.Properties.SubName += VendorSuffix
 	} else if c.InRamdisk() && !c.OnlyInRamdisk() {
-		c.Properties.SubName += ramdiskSuffix
+		c.Properties.SubName += RamdiskSuffix
 	} else if c.InVendorRamdisk() && !c.OnlyInVendorRamdisk() {
 		c.Properties.SubName += VendorRamdiskSuffix
 	} else if c.InRecovery() && !c.OnlyInRecovery() {
-		c.Properties.SubName += recoverySuffix
+		c.Properties.SubName += RecoverySuffix
 	} else if c.IsSdkVariant() && (c.Properties.SdkAndPlatformVariantVisibleToMake || c.SplitPerApiLevel()) {
 		c.Properties.SubName += sdkSuffix
 		if c.SplitPerApiLevel() {
@@ -1647,7 +1711,7 @@
 	bazelModuleLabel := c.GetBazelLabel(actx, c)
 	bazelActionsUsed := false
 	if c.MixedBuildsEnabled(actx) && c.bazelHandler != nil {
-		bazelActionsUsed = c.bazelHandler.generateBazelBuildActions(actx, bazelModuleLabel)
+		bazelActionsUsed = c.bazelHandler.GenerateBazelBuildActions(actx, bazelModuleLabel)
 	}
 	return bazelActionsUsed
 }
@@ -1674,10 +1738,6 @@
 
 	c.makeLinkType = GetMakeLinkType(actx, c)
 
-	if c.maybeGenerateBazelActions(actx) {
-		return
-	}
-
 	ctx := &moduleContext{
 		ModuleContext: actx,
 		moduleContextImpl: moduleContextImpl{
@@ -1686,6 +1746,11 @@
 	}
 	ctx.ctx = ctx
 
+	if c.maybeGenerateBazelActions(actx) {
+		c.maybeInstall(ctx, apexInfo)
+		return
+	}
+
 	deps := c.depsToPaths(ctx)
 	if ctx.Failed() {
 		return
@@ -1773,19 +1838,7 @@
 		}
 		c.outputFile = android.OptionalPathForPath(outputFile)
 
-		// If a lib is directly included in any of the APEXes or is not available to the
-		// platform (which is often the case when the stub is provided as a prebuilt),
-		// unhide the stubs variant having the latest version gets visible to make. In
-		// addition, the non-stubs variant is renamed to <libname>.bootstrap. This is to
-		// force anything in the make world to link against the stubs library.  (unless it
-		// is explicitly referenced via .bootstrap suffix or the module is marked with
-		// 'bootstrap: true').
-		if c.HasStubsVariants() && c.NotInPlatform() && !c.InRamdisk() &&
-			!c.InRecovery() && !c.UseVndk() && !c.static() && !c.isCoverageVariant() &&
-			c.IsStubs() && !c.InVendorRamdisk() {
-			c.Properties.HideFromMake = false // unhide
-			// Note: this is still non-installable
-		}
+		c.maybeUnhideFromMake()
 
 		// glob exported headers for snapshot, if BOARD_VNDK_VERSION is current or
 		// RECOVERY_SNAPSHOT_VERSION is current.
@@ -1796,6 +1849,26 @@
 		}
 	}
 
+	c.maybeInstall(ctx, apexInfo)
+}
+
+func (c *Module) maybeUnhideFromMake() {
+	// If a lib is directly included in any of the APEXes or is not available to the
+	// platform (which is often the case when the stub is provided as a prebuilt),
+	// unhide the stubs variant having the latest version gets visible to make. In
+	// addition, the non-stubs variant is renamed to <libname>.bootstrap. This is to
+	// force anything in the make world to link against the stubs library.  (unless it
+	// is explicitly referenced via .bootstrap suffix or the module is marked with
+	// 'bootstrap: true').
+	if c.HasStubsVariants() && c.NotInPlatform() && !c.InRamdisk() &&
+		!c.InRecovery() && !c.UseVndk() && !c.static() && !c.isCoverageVariant() &&
+		c.IsStubs() && !c.InVendorRamdisk() {
+		c.Properties.HideFromMake = false // unhide
+		// Note: this is still non-installable
+	}
+}
+
+func (c *Module) maybeInstall(ctx ModuleContext, apexInfo android.ApexInfo) {
 	if !proptools.BoolDefault(c.Properties.Installable, true) {
 		// If the module has been specifically configure to not be installed then
 		// hide from make as otherwise it will break when running inside make
@@ -1840,21 +1913,12 @@
 	if c.coverage != nil {
 		c.coverage.begin(ctx)
 	}
-	if c.sabi != nil {
-		c.sabi.begin(ctx)
-	}
-	if c.vndkdep != nil {
-		c.vndkdep.begin(ctx)
-	}
 	if c.lto != nil {
 		c.lto.begin(ctx)
 	}
 	if c.pgo != nil {
 		c.pgo.begin(ctx)
 	}
-	for _, feature := range c.features {
-		feature.begin(ctx)
-	}
 	if ctx.useSdk() && c.IsSdkVariant() {
 		version, err := nativeApiLevelFromUser(ctx, ctx.sdkVersion())
 		if err != nil {
@@ -1878,24 +1942,9 @@
 	if c.stl != nil {
 		deps = c.stl.deps(ctx, deps)
 	}
-	if c.sanitize != nil {
-		deps = c.sanitize.deps(ctx, deps)
-	}
 	if c.coverage != nil {
 		deps = c.coverage.deps(ctx, deps)
 	}
-	if c.sabi != nil {
-		deps = c.sabi.deps(ctx, deps)
-	}
-	if c.vndkdep != nil {
-		deps = c.vndkdep.deps(ctx, deps)
-	}
-	if c.lto != nil {
-		deps = c.lto.deps(ctx, deps)
-	}
-	for _, feature := range c.features {
-		deps = feature.deps(ctx, deps)
-	}
 
 	deps.WholeStaticLibs = android.LastUniqueStrings(deps.WholeStaticLibs)
 	deps.StaticLibs = android.LastUniqueStrings(deps.StaticLibs)
@@ -1985,15 +2034,19 @@
 	}
 }
 
-func (c *Module) addSharedLibDependenciesWithVersions(ctx android.BottomUpMutatorContext,
-	variations []blueprint.Variation, depTag libraryDependencyTag, name, version string, far bool) {
+func AddSharedLibDependenciesWithVersions(ctx android.BottomUpMutatorContext, mod LinkableInterface,
+	variations []blueprint.Variation, depTag blueprint.DependencyTag, name, version string, far bool) {
 
 	variations = append([]blueprint.Variation(nil), variations...)
 
-	if version != "" && CanBeOrLinkAgainstVersionVariants(c) {
+	if version != "" && CanBeOrLinkAgainstVersionVariants(mod) {
 		// Version is explicitly specified. i.e. libFoo#30
 		variations = append(variations, blueprint.Variation{Mutator: "version", Variation: version})
-		depTag.explicitlyVersioned = true
+		if tag, ok := depTag.(libraryDependencyTag); ok {
+			tag.explicitlyVersioned = true
+		} else {
+			panic(fmt.Errorf("Unexpected dependency tag: %T", depTag))
+		}
 	}
 
 	if far {
@@ -2003,6 +2056,74 @@
 	}
 }
 
+func GetSnapshot(c LinkableInterface, snapshotInfo **SnapshotInfo, actx android.BottomUpMutatorContext) SnapshotInfo {
+	// Only device modules with BOARD_VNDK_VERSION uses snapshot.  Others use the zero value of
+	// SnapshotInfo, which provides no mappings.
+	if *snapshotInfo == nil && c.Device() {
+		// Only retrieve the snapshot on demand in order to avoid circular dependencies
+		// between the modules in the snapshot and the snapshot itself.
+		var snapshotModule []blueprint.Module
+		if c.InVendor() && c.VndkVersion() == actx.DeviceConfig().VndkVersion() {
+			snapshotModule = actx.AddVariationDependencies(nil, nil, "vendor_snapshot")
+		} else if recoverySnapshotVersion := actx.DeviceConfig().RecoverySnapshotVersion(); recoverySnapshotVersion != "current" && recoverySnapshotVersion != "" && c.InRecovery() {
+			snapshotModule = actx.AddVariationDependencies(nil, nil, "recovery_snapshot")
+		}
+		if len(snapshotModule) > 0 && snapshotModule[0] != nil {
+			snapshot := actx.OtherModuleProvider(snapshotModule[0], SnapshotInfoProvider).(SnapshotInfo)
+			*snapshotInfo = &snapshot
+			// republish the snapshot for use in later mutators on this module
+			actx.SetProvider(SnapshotInfoProvider, snapshot)
+		}
+	}
+	if *snapshotInfo == nil {
+		*snapshotInfo = &SnapshotInfo{}
+	}
+	return **snapshotInfo
+}
+
+func RewriteSnapshotLib(lib string, snapshotMap map[string]string) string {
+	if snapshot, ok := snapshotMap[lib]; ok {
+		return snapshot
+	}
+
+	return lib
+}
+
+// RewriteLibs takes a list of names of shared libraries and scans it for three types
+// of names:
+//
+// 1. Name of an NDK library that refers to a prebuilt module.
+//    For each of these, it adds the name of the prebuilt module (which will be in
+//    prebuilts/ndk) to the list of nonvariant libs.
+// 2. Name of an NDK library that refers to an ndk_library module.
+//    For each of these, it adds the name of the ndk_library module to the list of
+//    variant libs.
+// 3. Anything else (so anything that isn't an NDK library).
+//    It adds these to the nonvariantLibs list.
+//
+// The caller can then know to add the variantLibs dependencies differently from the
+// nonvariantLibs
+func RewriteLibs(c LinkableInterface, snapshotInfo **SnapshotInfo, actx android.BottomUpMutatorContext, config android.Config, list []string) (nonvariantLibs []string, variantLibs []string) {
+	variantLibs = []string{}
+
+	nonvariantLibs = []string{}
+	for _, entry := range list {
+		// strip #version suffix out
+		name, _ := StubsLibNameAndVersion(entry)
+		if c.InRecovery() {
+			nonvariantLibs = append(nonvariantLibs, RewriteSnapshotLib(entry, GetSnapshot(c, snapshotInfo, actx).SharedLibs))
+		} else if c.UseSdk() && inList(name, *getNDKKnownLibs(config)) {
+			variantLibs = append(variantLibs, name+ndkLibrarySuffix)
+		} else if c.UseVndk() {
+			nonvariantLibs = append(nonvariantLibs, RewriteSnapshotLib(entry, GetSnapshot(c, snapshotInfo, actx).SharedLibs))
+		} else {
+			// put name#version back
+			nonvariantLibs = append(nonvariantLibs, entry)
+		}
+	}
+	return nonvariantLibs, variantLibs
+}
+
 func (c *Module) DepsMutator(actx android.BottomUpMutatorContext) {
 	if !c.Enabled() {
 		return
@@ -2021,83 +2142,16 @@
 	c.Properties.AndroidMkSystemSharedLibs = deps.SystemSharedLibs
 
 	var snapshotInfo *SnapshotInfo
-	getSnapshot := func() SnapshotInfo {
-		// Only modules with BOARD_VNDK_VERSION uses snapshot.  Others use the zero value of
-		// SnapshotInfo, which provides no mappings.
-		if snapshotInfo == nil {
-			// Only retrieve the snapshot on demand in order to avoid circular dependencies
-			// between the modules in the snapshot and the snapshot itself.
-			var snapshotModule []blueprint.Module
-			if c.InVendor() && c.VndkVersion() == actx.DeviceConfig().VndkVersion() {
-				snapshotModule = ctx.AddVariationDependencies(nil, nil, "vendor_snapshot")
-			} else if recoverySnapshotVersion := actx.DeviceConfig().RecoverySnapshotVersion(); recoverySnapshotVersion != "current" && recoverySnapshotVersion != "" && c.InRecovery() {
-				snapshotModule = ctx.AddVariationDependencies(nil, nil, "recovery_snapshot")
-			}
-			if len(snapshotModule) > 0 {
-				snapshot := ctx.OtherModuleProvider(snapshotModule[0], SnapshotInfoProvider).(SnapshotInfo)
-				snapshotInfo = &snapshot
-				// republish the snapshot for use in later mutators on this module
-				ctx.SetProvider(SnapshotInfoProvider, snapshot)
-			} else {
-				snapshotInfo = &SnapshotInfo{}
-			}
-		}
-
-		return *snapshotInfo
-	}
-
-	rewriteSnapshotLib := func(lib string, snapshotMap map[string]string) string {
-		if snapshot, ok := snapshotMap[lib]; ok {
-			return snapshot
-		}
-
-		return lib
-	}
 
 	variantNdkLibs := []string{}
 	variantLateNdkLibs := []string{}
 	if ctx.Os() == android.Android {
-		// rewriteLibs takes a list of names of shared libraries and scans it for three types
-		// of names:
-		//
-		// 1. Name of an NDK library that refers to a prebuilt module.
-		//    For each of these, it adds the name of the prebuilt module (which will be in
-		//    prebuilts/ndk) to the list of nonvariant libs.
-		// 2. Name of an NDK library that refers to an ndk_library module.
-		//    For each of these, it adds the name of the ndk_library module to the list of
-		//    variant libs.
-		// 3. Anything else (so anything that isn't an NDK library).
-		//    It adds these to the nonvariantLibs list.
-		//
-		// The caller can then know to add the variantLibs dependencies differently from the
-		// nonvariantLibs
-
-		rewriteLibs := func(list []string) (nonvariantLibs []string, variantLibs []string) {
-			variantLibs = []string{}
-			nonvariantLibs = []string{}
-			for _, entry := range list {
-				// strip #version suffix out
-				name, _ := StubsLibNameAndVersion(entry)
-				if c.InRecovery() {
-					nonvariantLibs = append(nonvariantLibs, rewriteSnapshotLib(entry, getSnapshot().SharedLibs))
-				} else if ctx.useSdk() && inList(name, *getNDKKnownLibs(ctx.Config())) {
-					variantLibs = append(variantLibs, name+ndkLibrarySuffix)
-				} else if ctx.useVndk() {
-					nonvariantLibs = append(nonvariantLibs, rewriteSnapshotLib(entry, getSnapshot().SharedLibs))
-				} else {
-					// put name#version back
-					nonvariantLibs = append(nonvariantLibs, entry)
-				}
-			}
-			return nonvariantLibs, variantLibs
-		}
-
-		deps.SharedLibs, variantNdkLibs = rewriteLibs(deps.SharedLibs)
-		deps.LateSharedLibs, variantLateNdkLibs = rewriteLibs(deps.LateSharedLibs)
-		deps.ReexportSharedLibHeaders, _ = rewriteLibs(deps.ReexportSharedLibHeaders)
+		deps.SharedLibs, variantNdkLibs = RewriteLibs(c, &snapshotInfo, actx, ctx.Config(), deps.SharedLibs)
+		deps.LateSharedLibs, variantLateNdkLibs = RewriteLibs(c, &snapshotInfo, actx, ctx.Config(), deps.LateSharedLibs)
+		deps.ReexportSharedLibHeaders, _ = RewriteLibs(c, &snapshotInfo, actx, ctx.Config(), deps.ReexportSharedLibHeaders)
 
 		for idx, lib := range deps.RuntimeLibs {
-			deps.RuntimeLibs[idx] = rewriteSnapshotLib(lib, getSnapshot().SharedLibs)
+			deps.RuntimeLibs[idx] = RewriteSnapshotLib(lib, GetSnapshot(c, &snapshotInfo, actx).SharedLibs)
 		}
 	}
 
@@ -2107,7 +2161,7 @@
 			depTag.reexportFlags = true
 		}
 
-		lib = rewriteSnapshotLib(lib, getSnapshot().HeaderLibs)
+		lib = RewriteSnapshotLib(lib, GetSnapshot(c, &snapshotInfo, actx).HeaderLibs)
 
 		if c.IsStubs() {
 			actx.AddFarVariationDependencies(append(ctx.Target().Variations(), c.ImageVariation()),
@@ -2117,6 +2171,15 @@
 		}
 	}
 
+	if c.isNDKStubLibrary() {
+		// NDK stubs depend on their implementation because the ABI dumps are
+		// generated from the implementation library.
+		actx.AddFarVariationDependencies(append(ctx.Target().Variations(),
+			c.ImageVariation(),
+			blueprint.Variation{Mutator: "link", Variation: "shared"},
+		), stubImplementation, c.BaseModuleName())
+	}
+
 	// sysprop_library has to support both C++ and Java. So sysprop_library internally creates one
 	// C++ implementation library and one Java implementation library. When a module links against
 	// sysprop_library, the C++ implementation library has to be linked. syspropImplLibraries is a
@@ -2130,7 +2193,7 @@
 			lib = impl
 		}
 
-		lib = rewriteSnapshotLib(lib, getSnapshot().StaticLibs)
+		lib = RewriteSnapshotLib(lib, GetSnapshot(c, &snapshotInfo, actx).StaticLibs)
 
 		actx.AddVariationDependencies([]blueprint.Variation{
 			{Mutator: "link", Variation: "static"},
@@ -2150,7 +2213,7 @@
 			lib = impl
 		}
 
-		lib = rewriteSnapshotLib(lib, getSnapshot().StaticLibs)
+		lib = RewriteSnapshotLib(lib, GetSnapshot(c, &snapshotInfo, actx).StaticLibs)
 
 		actx.AddVariationDependencies([]blueprint.Variation{
 			{Mutator: "link", Variation: "static"},
@@ -2164,14 +2227,7 @@
 		depTag := libraryDependencyTag{Kind: staticLibraryDependency, staticUnwinder: true}
 		actx.AddVariationDependencies([]blueprint.Variation{
 			{Mutator: "link", Variation: "static"},
-		}, depTag, rewriteSnapshotLib(staticUnwinder(actx), getSnapshot().StaticLibs))
-	}
-
-	for _, lib := range deps.LateStaticLibs {
-		depTag := libraryDependencyTag{Kind: staticLibraryDependency, Order: lateLibraryDependency}
-		actx.AddVariationDependencies([]blueprint.Variation{
-			{Mutator: "link", Variation: "static"},
-		}, depTag, rewriteSnapshotLib(lib, getSnapshot().StaticLibs))
+		}, depTag, RewriteSnapshotLib(staticUnwinder(actx), GetSnapshot(c, &snapshotInfo, actx).StaticLibs))
 	}
 
 	// shared lib names without the #version suffix
@@ -2196,7 +2252,14 @@
 		variations := []blueprint.Variation{
 			{Mutator: "link", Variation: "shared"},
 		}
-		c.addSharedLibDependenciesWithVersions(ctx, variations, depTag, name, version, false)
+		AddSharedLibDependenciesWithVersions(ctx, c, variations, depTag, name, version, false)
+	}
+
+	for _, lib := range deps.LateStaticLibs {
+		depTag := libraryDependencyTag{Kind: staticLibraryDependency, Order: lateLibraryDependency}
+		actx.AddVariationDependencies([]blueprint.Variation{
+			{Mutator: "link", Variation: "static"},
+		}, depTag, RewriteSnapshotLib(lib, GetSnapshot(c, &snapshotInfo, actx).StaticLibs))
 	}
 
 	for _, lib := range deps.LateSharedLibs {
@@ -2210,7 +2273,7 @@
 		variations := []blueprint.Variation{
 			{Mutator: "link", Variation: "shared"},
 		}
-		c.addSharedLibDependenciesWithVersions(ctx, variations, depTag, lib, "", false)
+		AddSharedLibDependenciesWithVersions(ctx, c, variations, depTag, lib, "", false)
 	}
 
 	actx.AddVariationDependencies([]blueprint.Variation{
@@ -2233,16 +2296,13 @@
 
 	crtVariations := GetCrtVariations(ctx, c)
 	actx.AddVariationDependencies(crtVariations, objDepTag, deps.ObjFiles...)
-	if deps.CrtBegin != "" {
+	for _, crt := range deps.CrtBegin {
 		actx.AddVariationDependencies(crtVariations, CrtBeginDepTag,
-			rewriteSnapshotLib(deps.CrtBegin, getSnapshot().Objects))
+			RewriteSnapshotLib(crt, GetSnapshot(c, &snapshotInfo, actx).Objects))
 	}
-	if deps.CrtEnd != "" {
+	for _, crt := range deps.CrtEnd {
 		actx.AddVariationDependencies(crtVariations, CrtEndDepTag,
-			rewriteSnapshotLib(deps.CrtEnd, getSnapshot().Objects))
-	}
-	if deps.LinkerFlagsFile != "" {
-		actx.AddDependency(c, linkerFlagsDepTag, deps.LinkerFlagsFile)
+			RewriteSnapshotLib(crt, GetSnapshot(c, &snapshotInfo, actx).Objects))
 	}
 	if deps.DynamicLinker != "" {
 		actx.AddDependency(c, dynamicLinkerDepTag, deps.DynamicLinker)
@@ -2267,7 +2327,7 @@
 			actx.AddVariationDependencies([]blueprint.Variation{
 				c.ImageVariation(),
 				{Mutator: "link", Variation: "shared"},
-			}, vndkExtDepTag, vndkdep.getVndkExtendsModuleName())
+			}, vndkExtDepTag, RewriteSnapshotLib(vndkdep.getVndkExtendsModuleName(), GetSnapshot(c, &snapshotInfo, actx).SharedLibs))
 		}
 	}
 }
@@ -2542,17 +2602,10 @@
 				} else {
 					ctx.ModuleErrorf("module %q is not a genrule", depName)
 				}
-			case linkerFlagsDepTag:
-				if genRule, ok := dep.(genrule.SourceFileGenerator); ok {
-					files := genRule.GeneratedSourceFiles()
-					if len(files) == 1 {
-						depPaths.LinkerFlagsFile = android.OptionalPathForPath(files[0])
-					} else if len(files) > 1 {
-						ctx.ModuleErrorf("module %q can only generate a single file if used for a linker flag file", depName)
-					}
-				} else {
-					ctx.ModuleErrorf("module %q is not a genrule", depName)
-				}
+			case CrtBeginDepTag:
+				depPaths.CrtBegin = append(depPaths.CrtBegin, android.OutputFileForModule(ctx, dep, ""))
+			case CrtEndDepTag:
+				depPaths.CrtEnd = append(depPaths.CrtEnd, android.OutputFileForModule(ctx, dep, ""))
 			}
 			return
 		}
@@ -2625,64 +2678,8 @@
 					return
 				}
 
-				sharedLibraryInfo := ctx.OtherModuleProvider(dep, SharedLibraryInfoProvider).(SharedLibraryInfo)
-				sharedLibraryStubsInfo := ctx.OtherModuleProvider(dep, SharedLibraryStubsProvider).(SharedLibraryStubsInfo)
-
-				if !libDepTag.explicitlyVersioned && len(sharedLibraryStubsInfo.SharedStubLibraries) > 0 {
-					useStubs := false
-
-					if lib := moduleLibraryInterface(dep); lib.buildStubs() && c.UseVndk() { // LLNDK
-						if !apexInfo.IsForPlatform() {
-							// For platform libraries, use current version of LLNDK
-							useStubs = true
-						}
-					} else if apexInfo.IsForPlatform() {
-						// If not building for APEX, use stubs only when it is from
-						// an APEX (and not from platform)
-						// However, for host, ramdisk, vendor_ramdisk, recovery or bootstrap modules,
-						// always link to non-stub variant
-						useStubs = dep.(android.ApexModule).NotInPlatform() && !c.bootstrap()
-						if useStubs {
-							// Another exception: if this module is a test for an APEX, then
-							// it is linked with the non-stub variant of a module in the APEX
-							// as if this is part of the APEX.
-							testFor := ctx.Provider(android.ApexTestForInfoProvider).(android.ApexTestForInfo)
-							for _, apexContents := range testFor.ApexContents {
-								if apexContents.DirectlyInApex(depName) {
-									useStubs = false
-									break
-								}
-							}
-						}
-						if useStubs {
-							// Yet another exception: If this module and the dependency are
-							// available to the same APEXes then skip stubs between their
-							// platform variants. This complements the test_for case above,
-							// which avoids the stubs on a direct APEX library dependency, by
-							// avoiding stubs for indirect test dependencies as well.
-							//
-							// TODO(b/183882457): This doesn't work if the two libraries have
-							// only partially overlapping apex_available. For that test_for
-							// modules would need to be split into APEX variants and resolved
-							// separately for each APEX they have access to.
-							if android.AvailableToSameApexes(c, dep.(android.ApexModule)) {
-								useStubs = false
-							}
-						}
-					} else {
-						// If building for APEX, use stubs when the parent is in any APEX that
-						// the child is not in.
-						useStubs = !android.DirectlyInAllApexes(apexInfo, depName)
-					}
-
-					// when to use (unspecified) stubs, use the latest one.
-					if useStubs {
-						stubs := sharedLibraryStubsInfo.SharedStubLibraries
-						toUse := stubs[len(stubs)-1]
-						sharedLibraryInfo = toUse.SharedLibraryInfo
-						depExporterInfo = toUse.FlagExporterInfo
-					}
-				}
+				sharedLibraryInfo, returnedDepExporterInfo := ChooseStubOrImpl(ctx, dep)
+				depExporterInfo = returnedDepExporterInfo
 
 				// Stubs lib doesn't link to the shared lib dependencies. Don't set
 				// linkFile, depFile, and ptr.
@@ -2840,8 +2837,8 @@
 				// they merely serve as Make dependencies and do not affect this lib itself.
 				c.Properties.AndroidMkSharedLibs = append(
 					c.Properties.AndroidMkSharedLibs, makeLibName)
-				// Record baseLibName for snapshots.
-				c.Properties.SnapshotSharedLibs = append(c.Properties.SnapshotSharedLibs, baseLibName(depName))
+				// Record BaseLibName for snapshots.
+				c.Properties.SnapshotSharedLibs = append(c.Properties.SnapshotSharedLibs, BaseLibName(depName))
 			case libDepTag.static():
 				if libDepTag.wholeStatic {
 					c.Properties.AndroidMkWholeStaticLibs = append(
@@ -2850,6 +2847,8 @@
 					c.Properties.AndroidMkStaticLibs = append(
 						c.Properties.AndroidMkStaticLibs, makeLibName)
 				}
+				// Record BaseLibName for snapshots.
+				c.Properties.SnapshotStaticLibs = append(c.Properties.SnapshotStaticLibs, BaseLibName(depName))
 			}
 		} else if !c.IsStubs() {
 			// Stubs lib doesn't link to the runtime lib, object, crt, etc. dependencies.
@@ -2858,14 +2857,14 @@
 			case runtimeDepTag:
 				c.Properties.AndroidMkRuntimeLibs = append(
 					c.Properties.AndroidMkRuntimeLibs, MakeLibName(ctx, c, ccDep, depName)+libDepTag.makeSuffix)
-				// Record baseLibName for snapshots.
-				c.Properties.SnapshotRuntimeLibs = append(c.Properties.SnapshotRuntimeLibs, baseLibName(depName))
+				// Record BaseLibName for snapshots.
+				c.Properties.SnapshotRuntimeLibs = append(c.Properties.SnapshotRuntimeLibs, BaseLibName(depName))
 			case objDepTag:
 				depPaths.Objs.objFiles = append(depPaths.Objs.objFiles, linkFile.Path())
 			case CrtBeginDepTag:
-				depPaths.CrtBegin = linkFile
+				depPaths.CrtBegin = append(depPaths.CrtBegin, linkFile.Path())
 			case CrtEndDepTag:
-				depPaths.CrtEnd = linkFile
+				depPaths.CrtEnd = append(depPaths.CrtEnd, linkFile.Path())
 			case dynamicLinkerDepTag:
 				depPaths.DynamicLinker = linkFile
 			}
@@ -2895,6 +2894,100 @@
 	return depPaths
 }
 
+// ChooseStubOrImpl determines whether a given dependency should be redirected to the stub variant
+// of the dependency or not, and returns the SharedLibraryInfo and FlagExporterInfo for the right
+// dependency. The stub variant is selected when the dependency crosses a boundary where each side
+// has different level of updatability. For example, if a library foo in an APEX depends on a
+// library bar which provides stable interface and exists in the platform, foo uses the stub variant
+// of bar. If bar doesn't provide a stable interface (i.e. buildStubs() == false) or is in the
+// same APEX as foo, the non-stub variant of bar is used.
+func ChooseStubOrImpl(ctx android.ModuleContext, dep android.Module) (SharedLibraryInfo, FlagExporterInfo) {
+	depName := ctx.OtherModuleName(dep)
+	depTag := ctx.OtherModuleDependencyTag(dep)
+	libDepTag, ok := depTag.(libraryDependencyTag)
+	if !ok || !libDepTag.shared() {
+		panic(fmt.Errorf("Unexpected dependency tag: %T", depTag))
+	}
+
+	thisModule, ok := ctx.Module().(android.ApexModule)
+	if !ok {
+		panic(fmt.Errorf("Not an APEX module: %q", ctx.ModuleName()))
+	}
+
+	useVndk := false
+	bootstrap := false
+	if linkable, ok := ctx.Module().(LinkableInterface); !ok {
+		panic(fmt.Errorf("Not a Linkable module: %q", ctx.ModuleName()))
+	} else {
+		useVndk = linkable.UseVndk()
+		bootstrap = linkable.Bootstrap()
+	}
+
+	sharedLibraryInfo := ctx.OtherModuleProvider(dep, SharedLibraryInfoProvider).(SharedLibraryInfo)
+	depExporterInfo := ctx.OtherModuleProvider(dep, FlagExporterInfoProvider).(FlagExporterInfo)
+	sharedLibraryStubsInfo := ctx.OtherModuleProvider(dep, SharedLibraryStubsProvider).(SharedLibraryStubsInfo)
+	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+
+	if !libDepTag.explicitlyVersioned && len(sharedLibraryStubsInfo.SharedStubLibraries) > 0 {
+		useStubs := false
+
+		if lib := moduleLibraryInterface(dep); lib.buildStubs() && useVndk { // LLNDK
+			if !apexInfo.IsForPlatform() {
+				// For platform libraries, use current version of LLNDK
+				// If this is for use_vendor apex we will apply the same rules
+				// of apex sdk enforcement below to choose right version.
+				useStubs = true
+			}
+		} else if apexInfo.IsForPlatform() || apexInfo.UsePlatformApis {
+			// If not building for APEX or the containing APEX allows the use of
+			// platform APIs, use stubs only when it is from an APEX (and not from
+			// platform) However, for host, ramdisk, vendor_ramdisk, recovery or
+			// bootstrap modules, always link to non-stub variant
+			useStubs = dep.(android.ApexModule).NotInPlatform() && !bootstrap
+			if useStubs {
+				// Another exception: if this module is a test for an APEX, then
+				// it is linked with the non-stub variant of a module in the APEX
+				// as if this is part of the APEX.
+				testFor := ctx.Provider(android.ApexTestForInfoProvider).(android.ApexTestForInfo)
+				for _, apexContents := range testFor.ApexContents {
+					if apexContents.DirectlyInApex(depName) {
+						useStubs = false
+						break
+					}
+				}
+			}
+			if useStubs {
+				// Yet another exception: If this module and the dependency are
+				// available to the same APEXes then skip stubs between their
+				// platform variants. This complements the test_for case above,
+				// which avoids the stubs on a direct APEX library dependency, by
+				// avoiding stubs for indirect test dependencies as well.
+				//
+				// TODO(b/183882457): This doesn't work if the two libraries have
+				// only partially overlapping apex_available. For that test_for
+				// modules would need to be split into APEX variants and resolved
+				// separately for each APEX they have access to.
+				if android.AvailableToSameApexes(thisModule, dep.(android.ApexModule)) {
+					useStubs = false
+				}
+			}
+		} else {
+			// If building for APEX, use stubs when the parent is in any APEX that
+			// the child is not in.
+			useStubs = !android.DirectlyInAllApexes(apexInfo, depName)
+		}
+
+		// when to use (unspecified) stubs, use the latest one.
+		if useStubs {
+			stubs := sharedLibraryStubsInfo.SharedStubLibraries
+			toUse := stubs[len(stubs)-1]
+			sharedLibraryInfo = toUse.SharedLibraryInfo
+			depExporterInfo = toUse.FlagExporterInfo
+		}
+	}
+	return sharedLibraryInfo, depExporterInfo
+}
+
 // orderStaticModuleDeps rearranges the order of the static library dependencies of the module
 // to match the topological order of the dependency tree, including any static analogues of
 // direct shared libraries.  It returns the ordered static dependencies, and an android.DepSet
@@ -2907,8 +3000,8 @@
 		transitiveStaticLibsBuilder.Transitive(staticDep.TransitiveStaticLibrariesForOrdering)
 	}
 	for _, sharedDep := range sharedDeps {
-		if sharedDep.StaticAnalogue != nil {
-			transitiveStaticLibsBuilder.Transitive(sharedDep.StaticAnalogue.TransitiveStaticLibrariesForOrdering)
+		if sharedDep.TransitiveStaticLibrariesForOrdering != nil {
+			transitiveStaticLibsBuilder.Transitive(sharedDep.TransitiveStaticLibrariesForOrdering)
 		}
 	}
 	transitiveStaticLibs := transitiveStaticLibsBuilder.Build()
@@ -2927,8 +3020,8 @@
 	return orderedStaticPaths, transitiveStaticLibs
 }
 
-// baseLibName trims known prefixes and suffixes
-func baseLibName(depName string) string {
+// BaseLibName trims known prefixes and suffixes
+func BaseLibName(depName string) string {
 	libName := strings.TrimSuffix(depName, llndkLibrarySuffix)
 	libName = strings.TrimSuffix(libName, vendorPublicLibrarySuffix)
 	libName = android.RemoveOptionalPrebuiltPrefix(libName)
@@ -2936,7 +3029,7 @@
 }
 
 func MakeLibName(ctx android.ModuleContext, c LinkableInterface, ccDep LinkableInterface, depName string) string {
-	libName := baseLibName(depName)
+	libName := BaseLibName(depName)
 	ccDepModule, _ := ccDep.(*Module)
 	isLLndk := ccDepModule != nil && ccDepModule.IsLlndk()
 	nonSystemVariantsExist := ccDep.HasNonSystemVariants() || isLLndk
@@ -2944,10 +3037,10 @@
 	if ccDepModule != nil {
 		// TODO(ivanlozano) Support snapshots for Rust-produced C library variants.
 		// Use base module name for snapshots when exporting to Makefile.
-		if snapshotPrebuilt, ok := ccDepModule.linker.(snapshotInterface); ok {
+		if snapshotPrebuilt, ok := ccDepModule.linker.(SnapshotInterface); ok {
 			baseName := ccDepModule.BaseModuleName()
 
-			return baseName + snapshotPrebuilt.snapshotAndroidMkSuffix()
+			return baseName + snapshotPrebuilt.SnapshotAndroidMkSuffix()
 		}
 	}
 
@@ -2961,13 +3054,13 @@
 		// core module, so update the dependency name here accordingly.
 		return libName + ccDep.SubName()
 	} else if ccDep.InRamdisk() && !ccDep.OnlyInRamdisk() {
-		return libName + ramdiskSuffix
+		return libName + RamdiskSuffix
 	} else if ccDep.InVendorRamdisk() && !ccDep.OnlyInVendorRamdisk() {
 		return libName + VendorRamdiskSuffix
 	} else if ccDep.InRecovery() && !ccDep.OnlyInRecovery() {
-		return libName + recoverySuffix
+		return libName + RecoverySuffix
 	} else if ccDep.Target().NativeBridge == android.NativeBridgeEnabled {
-		return libName + nativeBridgeSuffix
+		return libName + NativeBridgeSuffix
 	} else {
 		return libName
 	}
@@ -3079,6 +3172,13 @@
 	return false
 }
 
+func (c *Module) StaticExecutable() bool {
+	if b, ok := c.linker.(*binaryDecorator); ok {
+		return b.static()
+	}
+	return false
+}
+
 func (c *Module) Object() bool {
 	if o, ok := c.linker.(interface {
 		object() bool
@@ -3240,8 +3340,8 @@
 			return false
 		}
 	}
-	if depTag == stubImplDepTag || depTag == llndkStubDepTag {
-		// We don't track beyond LLNDK or from an implementation library to its stubs.
+	if depTag == stubImplDepTag {
+		// We don't track from an implementation library to its stubs.
 		return false
 	}
 	if depTag == staticVariantTag {
@@ -3341,7 +3441,7 @@
 		&TestProperties{},
 		&TestBinaryProperties{},
 		&BenchmarkProperties{},
-		&FuzzProperties{},
+		&fuzz.FuzzProperties{},
 		&StlProperties{},
 		&SanitizeProperties{},
 		&StripProperties{},
@@ -3366,7 +3466,7 @@
 }
 
 func (c *Module) IsSdkVariant() bool {
-	return c.Properties.IsSdkVariant || c.AlwaysSdk()
+	return c.Properties.IsSdkVariant
 }
 
 func kytheExtractAllFactory() android.Singleton {
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 5acafbe..84c3a86 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -152,71 +152,6 @@
 	).RunTest(t)
 }
 
-func TestFuchsiaDeps(t *testing.T) {
-	t.Helper()
-
-	bp := `
-		cc_library {
-			name: "libTest",
-			srcs: ["foo.c"],
-			target: {
-				fuchsia: {
-					srcs: ["bar.c"],
-				},
-			},
-		}`
-
-	result := android.GroupFixturePreparers(
-		prepareForCcTest,
-		PrepareForTestOnFuchsia,
-	).RunTestWithBp(t, bp)
-
-	rt := false
-	fb := false
-
-	ld := result.ModuleForTests("libTest", "fuchsia_arm64_shared").Rule("ld")
-	implicits := ld.Implicits
-	for _, lib := range implicits {
-		if strings.Contains(lib.Rel(), "libcompiler_rt") {
-			rt = true
-		}
-
-		if strings.Contains(lib.Rel(), "libbioniccompat") {
-			fb = true
-		}
-	}
-
-	if !rt || !fb {
-		t.Errorf("fuchsia libs must link libcompiler_rt and libbioniccompat")
-	}
-}
-
-func TestFuchsiaTargetDecl(t *testing.T) {
-	t.Helper()
-
-	bp := `
-		cc_library {
-			name: "libTest",
-			srcs: ["foo.c"],
-			target: {
-				fuchsia: {
-					srcs: ["bar.c"],
-				},
-			},
-		}`
-
-	result := android.GroupFixturePreparers(
-		prepareForCcTest,
-		PrepareForTestOnFuchsia,
-	).RunTestWithBp(t, bp)
-	ld := result.ModuleForTests("libTest", "fuchsia_arm64_shared").Rule("ld")
-	var objs []string
-	for _, o := range ld.Inputs {
-		objs = append(objs, o.Base())
-	}
-	android.AssertArrayString(t, "libTest inputs", []string{"foo.o", "bar.o"}, objs)
-}
-
 func TestVendorSrc(t *testing.T) {
 	ctx := testCc(t, `
 		cc_library {
@@ -396,50 +331,6 @@
 	}
 }
 
-func checkSnapshotIncludeExclude(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string, include bool, fake bool) {
-	t.Helper()
-	mod := ctx.ModuleForTests(moduleName, variant)
-	outputFiles := mod.OutputFiles(t, "")
-	if len(outputFiles) != 1 {
-		t.Errorf("%q must have single output\n", moduleName)
-		return
-	}
-	snapshotPath := filepath.Join(subDir, snapshotFilename)
-
-	if include {
-		out := singleton.Output(snapshotPath)
-		if fake {
-			if out.Rule == nil {
-				t.Errorf("Missing rule for module %q output file %q", moduleName, outputFiles[0])
-			}
-		} else {
-			if out.Input.String() != outputFiles[0].String() {
-				t.Errorf("The input of snapshot %q must be %q, but %q", moduleName, out.Input.String(), outputFiles[0])
-			}
-		}
-	} else {
-		out := singleton.MaybeOutput(snapshotPath)
-		if out.Rule != nil {
-			t.Errorf("There must be no rule for module %q output file %q", moduleName, outputFiles[0])
-		}
-	}
-}
-
-func checkSnapshot(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string) {
-	t.Helper()
-	checkSnapshotIncludeExclude(t, ctx, singleton, moduleName, snapshotFilename, subDir, variant, true, false)
-}
-
-func checkSnapshotExclude(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string) {
-	t.Helper()
-	checkSnapshotIncludeExclude(t, ctx, singleton, moduleName, snapshotFilename, subDir, variant, false, false)
-}
-
-func checkSnapshotRule(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string) {
-	t.Helper()
-	checkSnapshotIncludeExclude(t, ctx, singleton, moduleName, snapshotFilename, subDir, variant, true, true)
-}
-
 func checkWriteFileOutput(t *testing.T, params android.TestingBuildParams, expected []string) {
 	t.Helper()
 	content := android.ContentFromFileRuleForTests(t, params)
@@ -631,21 +522,21 @@
 
 	snapshotSingleton := ctx.SingletonForTests("vndk-snapshot")
 
-	checkSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.so", vndkCoreLibPath, variant)
-	checkSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.so", vndkCoreLib2ndPath, variant2nd)
-	checkSnapshot(t, ctx, snapshotSingleton, "libvndk_product", "libvndk_product.so", vndkCoreLibPath, variant)
-	checkSnapshot(t, ctx, snapshotSingleton, "libvndk_product", "libvndk_product.so", vndkCoreLib2ndPath, variant2nd)
-	checkSnapshot(t, ctx, snapshotSingleton, "libvndk_sp", "libvndk_sp-x.so", vndkSpLibPath, variant)
-	checkSnapshot(t, ctx, snapshotSingleton, "libvndk_sp", "libvndk_sp-x.so", vndkSpLib2ndPath, variant2nd)
-	checkSnapshot(t, ctx, snapshotSingleton, "libllndk", "libllndk.so", llndkLibPath, variant)
-	checkSnapshot(t, ctx, snapshotSingleton, "libllndk", "libllndk.so", llndkLib2ndPath, variant2nd)
+	CheckSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.so", vndkCoreLibPath, variant)
+	CheckSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.so", vndkCoreLib2ndPath, variant2nd)
+	CheckSnapshot(t, ctx, snapshotSingleton, "libvndk_product", "libvndk_product.so", vndkCoreLibPath, variant)
+	CheckSnapshot(t, ctx, snapshotSingleton, "libvndk_product", "libvndk_product.so", vndkCoreLib2ndPath, variant2nd)
+	CheckSnapshot(t, ctx, snapshotSingleton, "libvndk_sp", "libvndk_sp-x.so", vndkSpLibPath, variant)
+	CheckSnapshot(t, ctx, snapshotSingleton, "libvndk_sp", "libvndk_sp-x.so", vndkSpLib2ndPath, variant2nd)
+	CheckSnapshot(t, ctx, snapshotSingleton, "libllndk", "libllndk.so", llndkLibPath, variant)
+	CheckSnapshot(t, ctx, snapshotSingleton, "libllndk", "libllndk.so", llndkLib2ndPath, variant2nd)
 
 	snapshotConfigsPath := filepath.Join(snapshotVariantPath, "configs")
-	checkSnapshot(t, ctx, snapshotSingleton, "llndk.libraries.txt", "llndk.libraries.txt", snapshotConfigsPath, "")
-	checkSnapshot(t, ctx, snapshotSingleton, "vndkcore.libraries.txt", "vndkcore.libraries.txt", snapshotConfigsPath, "")
-	checkSnapshot(t, ctx, snapshotSingleton, "vndksp.libraries.txt", "vndksp.libraries.txt", snapshotConfigsPath, "")
-	checkSnapshot(t, ctx, snapshotSingleton, "vndkprivate.libraries.txt", "vndkprivate.libraries.txt", snapshotConfigsPath, "")
-	checkSnapshot(t, ctx, snapshotSingleton, "vndkproduct.libraries.txt", "vndkproduct.libraries.txt", snapshotConfigsPath, "")
+	CheckSnapshot(t, ctx, snapshotSingleton, "llndk.libraries.txt", "llndk.libraries.txt", snapshotConfigsPath, "")
+	CheckSnapshot(t, ctx, snapshotSingleton, "vndkcore.libraries.txt", "vndkcore.libraries.txt", snapshotConfigsPath, "")
+	CheckSnapshot(t, ctx, snapshotSingleton, "vndksp.libraries.txt", "vndksp.libraries.txt", snapshotConfigsPath, "")
+	CheckSnapshot(t, ctx, snapshotSingleton, "vndkprivate.libraries.txt", "vndkprivate.libraries.txt", snapshotConfigsPath, "")
+	CheckSnapshot(t, ctx, snapshotSingleton, "vndkproduct.libraries.txt", "vndkproduct.libraries.txt", snapshotConfigsPath, "")
 
 	checkVndkOutput(t, ctx, "vndk/vndk.libraries.txt", []string{
 		"LLNDK: libc.so",
@@ -2643,15 +2534,6 @@
 	return modulesInOrder, allDeps
 }
 
-func getOutputPaths(ctx *android.TestContext, variant string, moduleNames []string) (paths android.Paths) {
-	for _, moduleName := range moduleNames {
-		module := ctx.ModuleForTests(moduleName, variant).Module().(*Module)
-		output := module.outputFile.Path().RelativeToTop()
-		paths = append(paths, output)
-	}
-	return paths
-}
-
 func TestStaticLibDepReordering(t *testing.T) {
 	ctx := testCc(t, `
 	cc_library {
@@ -2679,7 +2561,7 @@
 	moduleA := ctx.ModuleForTests("a", variant).Module().(*Module)
 	actual := ctx.ModuleProvider(moduleA, StaticLibraryInfoProvider).(StaticLibraryInfo).
 		TransitiveStaticLibrariesForOrdering.ToList().RelativeToTop()
-	expected := getOutputPaths(ctx, variant, []string{"a", "c", "b", "d"})
+	expected := GetOutputPaths(ctx, variant, []string{"a", "c", "b", "d"})
 
 	if !reflect.DeepEqual(actual, expected) {
 		t.Errorf("staticDeps orderings were not propagated correctly"+
@@ -2714,7 +2596,7 @@
 	moduleA := ctx.ModuleForTests("a", variant).Module().(*Module)
 	actual := ctx.ModuleProvider(moduleA, StaticLibraryInfoProvider).(StaticLibraryInfo).
 		TransitiveStaticLibrariesForOrdering.ToList().RelativeToTop()
-	expected := getOutputPaths(ctx, variant, []string{"a", "c", "b"})
+	expected := GetOutputPaths(ctx, variant, []string{"a", "c", "b"})
 
 	if !reflect.DeepEqual(actual, expected) {
 		t.Errorf("staticDeps orderings did not account for shared libs"+
@@ -2799,12 +2681,8 @@
 		}
 	}
 	expected := []string{
-		"android_vendor.29_arm64_armv8-a_shared_1",
-		"android_vendor.29_arm64_armv8-a_shared_2",
 		"android_vendor.29_arm64_armv8-a_shared_current",
 		"android_vendor.29_arm64_armv8-a_shared",
-		"android_vendor.29_arm_armv7-a-neon_shared_1",
-		"android_vendor.29_arm_armv7-a-neon_shared_2",
 		"android_vendor.29_arm_armv7-a-neon_shared_current",
 		"android_vendor.29_arm_armv7-a-neon_shared",
 	}
@@ -2813,9 +2691,6 @@
 	params := result.ModuleForTests("libllndk", "android_vendor.29_arm_armv7-a-neon_shared").Description("generate stub")
 	android.AssertSame(t, "use VNDK version for default stubs", "current", params.Args["apiLevel"])
 
-	params = result.ModuleForTests("libllndk", "android_vendor.29_arm_armv7-a-neon_shared_1").Description("generate stub")
-	android.AssertSame(t, "override apiLevel for versioned stubs", "1", params.Args["apiLevel"])
-
 	checkExportedIncludeDirs := func(module, variant string, expectedDirs ...string) {
 		t.Helper()
 		m := result.ModuleForTests(module, variant).Module()
@@ -3364,7 +3239,7 @@
 
 	mybin := ctx.ModuleForTests("mybin", "android_arm64_armv8-a").Rule("ld")
 	actual := mybin.Implicits[:2]
-	expected := getOutputPaths(ctx, "android_arm64_armv8-a_static", []string{"libfooB", "libfooC"})
+	expected := GetOutputPaths(ctx, "android_arm64_armv8-a_static", []string{"libfooB", "libfooC"})
 
 	if !reflect.DeepEqual(actual, expected) {
 		t.Errorf("staticDeps orderings were not propagated correctly"+
@@ -3722,305 +3597,6 @@
 	android.AssertStringDoesContain(t, "min sdk version", cFlags, "-target aarch64-linux-android29")
 }
 
-type MemtagNoteType int
-
-const (
-	None MemtagNoteType = iota + 1
-	Sync
-	Async
-)
-
-func (t MemtagNoteType) str() string {
-	switch t {
-	case None:
-		return "none"
-	case Sync:
-		return "sync"
-	case Async:
-		return "async"
-	default:
-		panic("invalid note type")
-	}
-}
-
-func checkHasMemtagNote(t *testing.T, m android.TestingModule, expected MemtagNoteType) {
-	note_async := "note_memtag_heap_async"
-	note_sync := "note_memtag_heap_sync"
-
-	found := None
-	implicits := m.Rule("ld").Implicits
-	for _, lib := range implicits {
-		if strings.Contains(lib.Rel(), note_async) {
-			found = Async
-			break
-		} else if strings.Contains(lib.Rel(), note_sync) {
-			found = Sync
-			break
-		}
-	}
-
-	if found != expected {
-		t.Errorf("Wrong Memtag note in target %q: found %q, expected %q", m.Module().(*Module).Name(), found.str(), expected.str())
-	}
-}
-
-var prepareForTestWithMemtagHeap = android.GroupFixturePreparers(
-	android.FixtureModifyMockFS(func(fs android.MockFS) {
-		templateBp := `
-		cc_test {
-			name: "%[1]s_test",
-			gtest: false,
-		}
-
-		cc_test {
-			name: "%[1]s_test_false",
-			gtest: false,
-			sanitize: { memtag_heap: false },
-		}
-
-		cc_test {
-			name: "%[1]s_test_true",
-			gtest: false,
-			sanitize: { memtag_heap: true },
-		}
-
-		cc_test {
-			name: "%[1]s_test_true_nodiag",
-			gtest: false,
-			sanitize: { memtag_heap: true, diag: { memtag_heap: false }  },
-		}
-
-		cc_test {
-			name: "%[1]s_test_true_diag",
-			gtest: false,
-			sanitize: { memtag_heap: true, diag: { memtag_heap: true }  },
-		}
-
-		cc_binary {
-			name: "%[1]s_binary",
-		}
-
-		cc_binary {
-			name: "%[1]s_binary_false",
-			sanitize: { memtag_heap: false },
-		}
-
-		cc_binary {
-			name: "%[1]s_binary_true",
-			sanitize: { memtag_heap: true },
-		}
-
-		cc_binary {
-			name: "%[1]s_binary_true_nodiag",
-			sanitize: { memtag_heap: true, diag: { memtag_heap: false }  },
-		}
-
-		cc_binary {
-			name: "%[1]s_binary_true_diag",
-			sanitize: { memtag_heap: true, diag: { memtag_heap: true }  },
-		}
-		`
-		subdirDefaultBp := fmt.Sprintf(templateBp, "default")
-		subdirExcludeBp := fmt.Sprintf(templateBp, "exclude")
-		subdirSyncBp := fmt.Sprintf(templateBp, "sync")
-		subdirAsyncBp := fmt.Sprintf(templateBp, "async")
-
-		fs.Merge(android.MockFS{
-			"subdir_default/Android.bp": []byte(subdirDefaultBp),
-			"subdir_exclude/Android.bp": []byte(subdirExcludeBp),
-			"subdir_sync/Android.bp":    []byte(subdirSyncBp),
-			"subdir_async/Android.bp":   []byte(subdirAsyncBp),
-		})
-	}),
-	android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-		variables.MemtagHeapExcludePaths = []string{"subdir_exclude"}
-		// "subdir_exclude" is covered by both include and exclude paths. Exclude wins.
-		variables.MemtagHeapSyncIncludePaths = []string{"subdir_sync", "subdir_exclude"}
-		variables.MemtagHeapAsyncIncludePaths = []string{"subdir_async", "subdir_exclude"}
-	}),
-)
-
-func TestSanitizeMemtagHeap(t *testing.T) {
-	variant := "android_arm64_armv8-a"
-
-	result := android.GroupFixturePreparers(
-		prepareForCcTest,
-		prepareForTestWithMemtagHeap,
-	).RunTest(t)
-	ctx := result.TestContext
-
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_test", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_test_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_diag", variant), Sync)
-
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_diag", variant), Sync)
-
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_diag", variant), Sync)
-
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_diag", variant), Sync)
-
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_test", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_test_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_diag", variant), Sync)
-
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_diag", variant), Sync)
-
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_diag", variant), Sync)
-
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_diag", variant), Sync)
-}
-
-func TestSanitizeMemtagHeapWithSanitizeDevice(t *testing.T) {
-	variant := "android_arm64_armv8-a"
-
-	result := android.GroupFixturePreparers(
-		prepareForCcTest,
-		prepareForTestWithMemtagHeap,
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.SanitizeDevice = []string{"memtag_heap"}
-		}),
-	).RunTest(t)
-	ctx := result.TestContext
-
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_test", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_test_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_diag", variant), Sync)
-
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_diag", variant), Sync)
-
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_diag", variant), Sync)
-
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_diag", variant), Sync)
-
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_test", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_test_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_diag", variant), Sync)
-
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_diag", variant), Sync)
-
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_diag", variant), Sync)
-
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_diag", variant), Sync)
-}
-
-func TestSanitizeMemtagHeapWithSanitizeDeviceDiag(t *testing.T) {
-	variant := "android_arm64_armv8-a"
-
-	result := android.GroupFixturePreparers(
-		prepareForCcTest,
-		prepareForTestWithMemtagHeap,
-		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.SanitizeDevice = []string{"memtag_heap"}
-			variables.SanitizeDeviceDiag = []string{"memtag_heap"}
-		}),
-	).RunTest(t)
-	ctx := result.TestContext
-
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_test", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_test_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_test_true_diag", variant), Sync)
-
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("default_binary_true_diag", variant), Sync)
-
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_test_true_diag", variant), Sync)
-
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("exclude_binary_true_diag", variant), Sync)
-
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_test", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_test_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_test_true_diag", variant), Sync)
-
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("async_binary_true_diag", variant), Sync)
-
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_test_true_diag", variant), Sync)
-
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_false", variant), None)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true", variant), Sync)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_nodiag", variant), Async)
-	checkHasMemtagNote(t, ctx.ModuleForTests("sync_binary_true_diag", variant), Sync)
-}
-
 func TestIncludeDirsExporting(t *testing.T) {
 
 	// Trim spaces from the beginning, end and immediately after any newline characters. Leaves
@@ -4291,3 +3867,155 @@
 		)
 	})
 }
+
+func TestIncludeDirectoryOrdering(t *testing.T) {
+	bp := `
+		cc_library {
+			name: "libfoo",
+			srcs: ["foo.c"],
+			local_include_dirs: ["local_include_dirs"],
+			export_include_dirs: ["export_include_dirs"],
+			export_system_include_dirs: ["export_system_include_dirs"],
+			static_libs: ["libstatic1", "libstatic2"],
+			whole_static_libs: ["libwhole1", "libwhole2"],
+			shared_libs: ["libshared1", "libshared2"],
+			header_libs: ["libheader1", "libheader2"],
+			target: {
+				android: {
+					shared_libs: ["libandroid"],
+					local_include_dirs: ["android_local_include_dirs"],
+					export_include_dirs: ["android_export_include_dirs"],
+				},
+				android_arm: {
+					shared_libs: ["libandroid_arm"],
+					local_include_dirs: ["android_arm_local_include_dirs"],
+					export_include_dirs: ["android_arm_export_include_dirs"],
+				},
+				linux: {
+					shared_libs: ["liblinux"],
+					local_include_dirs: ["linux_local_include_dirs"],
+					export_include_dirs: ["linux_export_include_dirs"],
+				},
+			},
+			multilib: {
+				lib32: {
+					shared_libs: ["lib32"],
+					local_include_dirs: ["lib32_local_include_dirs"],
+					export_include_dirs: ["lib32_export_include_dirs"],
+				},
+			},
+			arch: {
+				arm: {
+					shared_libs: ["libarm"],
+					local_include_dirs: ["arm_local_include_dirs"],
+					export_include_dirs: ["arm_export_include_dirs"],
+				},
+			},
+			stl: "libc++",
+			sdk_version: "20",
+		}
+
+		cc_library_headers {
+			name: "libheader1",
+			export_include_dirs: ["libheader1"],
+			sdk_version: "20",
+			stl: "none",
+		}
+
+		cc_library_headers {
+			name: "libheader2",
+			export_include_dirs: ["libheader2"],
+			sdk_version: "20",
+			stl: "none",
+		}
+	`
+
+	libs := []string{
+		"libstatic1",
+		"libstatic2",
+		"libwhole1",
+		"libwhole2",
+		"libshared1",
+		"libshared2",
+		"libandroid",
+		"libandroid_arm",
+		"liblinux",
+		"lib32",
+		"libarm",
+	}
+
+	for _, lib := range libs {
+		bp += fmt.Sprintf(`
+			cc_library {
+				name: "%s",
+				export_include_dirs: ["%s"],
+				sdk_version: "20",
+				stl: "none",
+			}
+		`, lib, lib)
+	}
+
+	ctx := android.GroupFixturePreparers(
+		PrepareForIntegrationTestWithCc,
+		android.FixtureAddTextFile("external/foo/Android.bp", bp),
+	).RunTest(t)
+	// Use the arm variant instead of the arm64 variant so that it gets headers from
+	// ndk_libandroid_support to test LateStaticLibs.
+	cflags := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_sdk_static").Output("obj/external/foo/foo.o").Args["cFlags"]
+
+	var includes []string
+	flags := strings.Split(cflags, " ")
+	for i, flag := range flags {
+		if strings.Contains(flag, "Cflags") {
+			includes = append(includes, flag)
+		} else if strings.HasPrefix(flag, "-I") {
+			includes = append(includes, strings.TrimPrefix(flag, "-I"))
+		} else if flag == "-isystem" {
+			includes = append(includes, flags[i+1])
+		}
+	}
+
+	want := []string{
+		"${config.ArmThumbCflags}",
+		"${config.ArmCflags}",
+		"${config.CommonGlobalCflags}",
+		"${config.DeviceGlobalCflags}",
+		"${config.ExternalCflags}",
+		"${config.ArmToolchainCflags}",
+		"${config.ArmArmv7ANeonCflags}",
+		"${config.ArmGenericCflags}",
+		"external/foo/android_arm_export_include_dirs",
+		"external/foo/lib32_export_include_dirs",
+		"external/foo/arm_export_include_dirs",
+		"external/foo/android_export_include_dirs",
+		"external/foo/linux_export_include_dirs",
+		"external/foo/export_include_dirs",
+		"external/foo/android_arm_local_include_dirs",
+		"external/foo/lib32_local_include_dirs",
+		"external/foo/arm_local_include_dirs",
+		"external/foo/android_local_include_dirs",
+		"external/foo/linux_local_include_dirs",
+		"external/foo/local_include_dirs",
+		"external/foo",
+		"external/foo/libheader1",
+		"external/foo/libheader2",
+		"external/foo/libwhole1",
+		"external/foo/libwhole2",
+		"external/foo/libstatic1",
+		"external/foo/libstatic2",
+		"external/foo/libshared1",
+		"external/foo/libshared2",
+		"external/foo/liblinux",
+		"external/foo/libandroid",
+		"external/foo/libarm",
+		"external/foo/lib32",
+		"external/foo/libandroid_arm",
+		"defaults/cc/common/ndk_libc++_shared",
+		"defaults/cc/common/ndk_libandroid_support",
+		"out/soong/ndk/sysroot/usr/include",
+		"out/soong/ndk/sysroot/usr/include/arm-linux-androideabi",
+		"${config.NoOverrideGlobalCflags}",
+	}
+
+	android.AssertArrayString(t, "includes", want, includes)
+}
diff --git a/cc/cmakelists.go b/cc/cmakelists.go
index 04536fc..ad130ba 100644
--- a/cc/cmakelists.go
+++ b/cc/cmakelists.go
@@ -316,7 +316,7 @@
 	if strings.HasPrefix(parameter, "--sysroot") {
 		return systemRoot
 	}
-	if strings.HasPrefix(parameter, "-fsanitize-blacklist") {
+	if strings.HasPrefix(parameter, "-fsanitize-ignorelist") {
 		return relativeFilePathFlag
 	}
 	if strings.HasPrefix(parameter, "-fprofile-sample-use") {
diff --git a/cc/compiler.go b/cc/compiler.go
index 78a5a5d..34d68a1 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -92,7 +92,7 @@
 
 	// list of generated headers to add to the include path. These are the names
 	// of genrule modules.
-	Generated_headers []string `android:"arch_variant"`
+	Generated_headers []string `android:"arch_variant,variant_prepend"`
 
 	// pass -frtti instead of -fno-rtti
 	Rtti *bool
@@ -189,6 +189,11 @@
 			// variant of the C/C++ module.
 			Cflags []string
 		}
+		Platform struct {
+			// List of additional cflags that should be used to build the platform
+			// variant of the C/C++ module.
+			Cflags []string
+		}
 	}
 
 	Proto struct {
@@ -256,8 +261,12 @@
 	return []interface{}{&compiler.Properties, &compiler.Proto}
 }
 
+func includeBuildDirectory(prop *bool) bool {
+	return proptools.BoolDefault(prop, true)
+}
+
 func (compiler *baseCompiler) includeBuildDirectory() bool {
-	return proptools.BoolDefault(compiler.Properties.Include_build_directory, true)
+	return includeBuildDirectory(compiler.Properties.Include_build_directory)
 }
 
 func (compiler *baseCompiler) compilerInit(ctx BaseModuleContext) {}
@@ -310,6 +319,7 @@
 	CheckBadCompilerFlags(ctx, "product.cflags", compiler.Properties.Target.Product.Cflags)
 	CheckBadCompilerFlags(ctx, "recovery.cflags", compiler.Properties.Target.Recovery.Cflags)
 	CheckBadCompilerFlags(ctx, "vendor_ramdisk.cflags", compiler.Properties.Target.Vendor_ramdisk.Cflags)
+	CheckBadCompilerFlags(ctx, "platform.cflags", compiler.Properties.Target.Platform.Cflags)
 
 	esc := proptools.NinjaAndShellEscapeList
 
@@ -392,7 +402,7 @@
 	if flags.RequiredInstructionSet != "" {
 		instructionSet = flags.RequiredInstructionSet
 	}
-	instructionSetFlags, err := tc.ClangInstructionSetFlags(instructionSet)
+	instructionSetFlags, err := tc.InstructionSetFlags(instructionSet)
 	if err != nil {
 		ctx.ModuleErrorf("%s", err)
 	}
@@ -437,15 +447,15 @@
 	flags.Global.ConlyFlags = append([]string{"${config.CommonGlobalConlyflags}"}, flags.Global.ConlyFlags...)
 	flags.Global.CppFlags = append([]string{fmt.Sprintf("${config.%sGlobalCppflags}", hod)}, flags.Global.CppFlags...)
 
-	flags.Global.AsFlags = append(flags.Global.AsFlags, tc.ClangAsflags())
-	flags.Global.CppFlags = append([]string{"${config.CommonClangGlobalCppflags}"}, flags.Global.CppFlags...)
+	flags.Global.AsFlags = append(flags.Global.AsFlags, tc.Asflags())
+	flags.Global.CppFlags = append([]string{"${config.CommonGlobalCppflags}"}, flags.Global.CppFlags...)
 	flags.Global.CommonFlags = append(flags.Global.CommonFlags,
-		tc.ClangCflags(),
-		"${config.CommonClangGlobalCflags}",
-		fmt.Sprintf("${config.%sClangGlobalCflags}", hod))
+		tc.Cflags(),
+		"${config.CommonGlobalCflags}",
+		fmt.Sprintf("${config.%sGlobalCflags}", hod))
 
-	if isThirdParty(modulePath) {
-		flags.Global.CommonFlags = append(flags.Global.CommonFlags, "${config.ClangExternalCflags}")
+	if android.IsThirdPartyPath(modulePath) {
+		flags.Global.CommonFlags = append(flags.Global.CommonFlags, "${config.ExternalCflags}")
 	}
 
 	if tc.Bionic() {
@@ -458,11 +468,11 @@
 
 	flags.Global.AsFlags = append(flags.Global.AsFlags, "-D__ASSEMBLY__")
 
-	flags.Global.CppFlags = append(flags.Global.CppFlags, tc.ClangCppflags())
+	flags.Global.CppFlags = append(flags.Global.CppFlags, tc.Cppflags())
 
 	flags.Global.YasmFlags = append(flags.Global.YasmFlags, tc.YasmFlags())
 
-	flags.Global.CommonFlags = append(flags.Global.CommonFlags, tc.ToolchainClangCflags())
+	flags.Global.CommonFlags = append(flags.Global.CommonFlags, tc.ToolchainCflags())
 
 	cStd := config.CStdVersion
 	if String(compiler.Properties.C_std) == "experimental" {
@@ -502,6 +512,9 @@
 	if ctx.inVendorRamdisk() {
 		flags.Local.CFlags = append(flags.Local.CFlags, esc(compiler.Properties.Target.Vendor_ramdisk.Cflags)...)
 	}
+	if !ctx.useSdk() {
+		flags.Local.CFlags = append(flags.Local.CFlags, esc(compiler.Properties.Target.Platform.Cflags)...)
+	}
 
 	// We can enforce some rules more strictly in the code we own. strict
 	// indicates if this is code that we can be stricter with. If we have
@@ -666,27 +679,6 @@
 	return transformSourceToObj(ctx, subdir, srcFiles, flags, pathDeps, cFlagsDeps)
 }
 
-var thirdPartyDirPrefixExceptions = []*regexp.Regexp{
-	regexp.MustCompile("^vendor/[^/]*google[^/]*/"),
-	regexp.MustCompile("^hardware/google/"),
-	regexp.MustCompile("^hardware/interfaces/"),
-	regexp.MustCompile("^hardware/libhardware[^/]*/"),
-	regexp.MustCompile("^hardware/ril/"),
-}
-
-func isThirdParty(path string) bool {
-	thirdPartyDirPrefixes := []string{"external/", "vendor/", "hardware/"}
-
-	if android.HasAnyPrefix(path, thirdPartyDirPrefixes) {
-		for _, prefix := range thirdPartyDirPrefixExceptions {
-			if prefix.MatchString(path) {
-				return false
-			}
-		}
-	}
-	return true
-}
-
 // Properties for rust_bindgen related to generating rust bindings.
 // This exists here so these properties can be included in a cc_default
 // which can be used in both cc and rust modules.
diff --git a/cc/compiler_test.go b/cc/compiler_test.go
index c301388..9ae4d18 100644
--- a/cc/compiler_test.go
+++ b/cc/compiler_test.go
@@ -16,27 +16,30 @@
 
 import (
 	"testing"
+
+	"android/soong/android"
 )
 
 func TestIsThirdParty(t *testing.T) {
-	shouldFail := []string{
+	thirdPartyPaths := []string{
 		"external/foo/",
 		"vendor/bar/",
 		"hardware/underwater_jaguar/",
 	}
-	shouldPass := []string{
+	nonThirdPartyPaths := []string{
 		"vendor/google/cts/",
 		"hardware/google/pixel",
 		"hardware/interfaces/camera",
 		"hardware/ril/supa_ril",
+		"bionic/libc",
 	}
-	for _, path := range shouldFail {
-		if !isThirdParty(path) {
+	for _, path := range thirdPartyPaths {
+		if !android.IsThirdPartyPath(path) {
 			t.Errorf("Expected %s to be considered third party", path)
 		}
 	}
-	for _, path := range shouldPass {
-		if isThirdParty(path) {
+	for _, path := range nonThirdPartyPaths {
+		if android.IsThirdPartyPath(path) {
 			t.Errorf("Expected %s to *not* be considered third party", path)
 		}
 	}
diff --git a/cc/config/Android.bp b/cc/config/Android.bp
index e4a8b62..3e8ee48 100644
--- a/cc/config/Android.bp
+++ b/cc/config/Android.bp
@@ -17,12 +17,12 @@
         "toolchain.go",
         "vndk.go",
 
+        "bionic.go",
+
         "arm_device.go",
         "arm64_device.go",
-        "arm64_fuchsia_device.go",
         "x86_device.go",
         "x86_64_device.go",
-        "x86_64_fuchsia_device.go",
 
         "x86_darwin_host.go",
         "x86_linux_host.go",
diff --git a/cc/config/arm64_device.go b/cc/config/arm64_device.go
index 864fba1..2d6bcb8 100644
--- a/cc/config/arm64_device.go
+++ b/cc/config/arm64_device.go
@@ -48,12 +48,12 @@
 		"-Wl,-z,separate-code",
 	}
 
-	arm64Lldflags = append(ClangFilterUnknownLldflags(arm64Ldflags),
+	arm64Lldflags = append(arm64Ldflags,
 		"-Wl,-z,max-page-size=4096")
 
 	arm64Cppflags = []string{}
 
-	arm64ClangCpuVariantCflags = map[string][]string{
+	arm64CpuVariantCflags = map[string][]string{
 		"cortex-a53": []string{
 			"-mcpu=cortex-a53",
 		},
@@ -96,64 +96,57 @@
 	pctx.SourcePathVariable("Arm64GccRoot",
 		"prebuilts/gcc/${HostPrebuiltTag}/aarch64/aarch64-linux-android-${arm64GccVersion}")
 
-	pctx.StaticVariable("Arm64Ldflags", strings.Join(arm64Ldflags, " "))
-	pctx.StaticVariable("Arm64Lldflags", strings.Join(arm64Lldflags, " "))
+	exportStringListStaticVariable("Arm64Ldflags", arm64Ldflags)
+	exportStringListStaticVariable("Arm64Lldflags", arm64Lldflags)
 
-	pctx.StaticVariable("Arm64ClangCflags", strings.Join(ClangFilterUnknownCflags(arm64Cflags), " "))
-	pctx.StaticVariable("Arm64ClangLdflags", strings.Join(ClangFilterUnknownCflags(arm64Ldflags), " "))
-	pctx.StaticVariable("Arm64ClangLldflags", strings.Join(ClangFilterUnknownCflags(arm64Lldflags), " "))
-	pctx.StaticVariable("Arm64ClangCppflags", strings.Join(ClangFilterUnknownCflags(arm64Cppflags), " "))
+	exportStringListStaticVariable("Arm64Cflags", arm64Cflags)
+	exportStringListStaticVariable("Arm64Cppflags", arm64Cppflags)
 
-	pctx.StaticVariable("Arm64ClangArmv8ACflags", strings.Join(arm64ArchVariantCflags["armv8-a"], " "))
-	pctx.StaticVariable("Arm64ClangArmv8ABranchProtCflags", strings.Join(arm64ArchVariantCflags["armv8-a-branchprot"], " "))
-	pctx.StaticVariable("Arm64ClangArmv82ACflags", strings.Join(arm64ArchVariantCflags["armv8-2a"], " "))
-	pctx.StaticVariable("Arm64ClangArmv82ADotprodCflags", strings.Join(arm64ArchVariantCflags["armv8-2a-dotprod"], " "))
+	exportedStringListDictVars.Set("Arm64ArchVariantCflags", arm64ArchVariantCflags)
+	exportedStringListDictVars.Set("Arm64CpuVariantCflags", arm64CpuVariantCflags)
 
-	pctx.StaticVariable("Arm64ClangCortexA53Cflags",
-		strings.Join(arm64ClangCpuVariantCflags["cortex-a53"], " "))
+	pctx.StaticVariable("Arm64Armv8ACflags", strings.Join(arm64ArchVariantCflags["armv8-a"], " "))
+	pctx.StaticVariable("Arm64Armv8ABranchProtCflags", strings.Join(arm64ArchVariantCflags["armv8-a-branchprot"], " "))
+	pctx.StaticVariable("Arm64Armv82ACflags", strings.Join(arm64ArchVariantCflags["armv8-2a"], " "))
+	pctx.StaticVariable("Arm64Armv82ADotprodCflags", strings.Join(arm64ArchVariantCflags["armv8-2a-dotprod"], " "))
 
-	pctx.StaticVariable("Arm64ClangCortexA55Cflags",
-		strings.Join(arm64ClangCpuVariantCflags["cortex-a55"], " "))
-
-	pctx.StaticVariable("Arm64ClangKryoCflags",
-		strings.Join(arm64ClangCpuVariantCflags["kryo"], " "))
-
-	pctx.StaticVariable("Arm64ClangExynosM1Cflags",
-		strings.Join(arm64ClangCpuVariantCflags["exynos-m1"], " "))
-
-	pctx.StaticVariable("Arm64ClangExynosM2Cflags",
-		strings.Join(arm64ClangCpuVariantCflags["exynos-m2"], " "))
+	pctx.StaticVariable("Arm64CortexA53Cflags", strings.Join(arm64CpuVariantCflags["cortex-a53"], " "))
+	pctx.StaticVariable("Arm64CortexA55Cflags", strings.Join(arm64CpuVariantCflags["cortex-a55"], " "))
+	pctx.StaticVariable("Arm64KryoCflags", strings.Join(arm64CpuVariantCflags["kryo"], " "))
+	pctx.StaticVariable("Arm64ExynosM1Cflags", strings.Join(arm64CpuVariantCflags["exynos-m1"], " "))
+	pctx.StaticVariable("Arm64ExynosM2Cflags", strings.Join(arm64CpuVariantCflags["exynos-m2"], " "))
 }
 
 var (
-	arm64ClangArchVariantCflagsVar = map[string]string{
-		"armv8-a":            "${config.Arm64ClangArmv8ACflags}",
-		"armv8-a-branchprot": "${config.Arm64ClangArmv8ABranchProtCflags}",
-		"armv8-2a":           "${config.Arm64ClangArmv82ACflags}",
-		"armv8-2a-dotprod":   "${config.Arm64ClangArmv82ADotprodCflags}",
+	arm64ArchVariantCflagsVar = map[string]string{
+		"armv8-a":            "${config.Arm64Armv8ACflags}",
+		"armv8-a-branchprot": "${config.Arm64Armv8ABranchProtCflags}",
+		"armv8-2a":           "${config.Arm64Armv82ACflags}",
+		"armv8-2a-dotprod":   "${config.Arm64Armv82ADotprodCflags}",
 	}
 
-	arm64ClangCpuVariantCflagsVar = map[string]string{
+	arm64CpuVariantCflagsVar = map[string]string{
 		"":           "",
-		"cortex-a53": "${config.Arm64ClangCortexA53Cflags}",
-		"cortex-a55": "${config.Arm64ClangCortexA55Cflags}",
-		"cortex-a72": "${config.Arm64ClangCortexA53Cflags}",
-		"cortex-a73": "${config.Arm64ClangCortexA53Cflags}",
-		"cortex-a75": "${config.Arm64ClangCortexA55Cflags}",
-		"cortex-a76": "${config.Arm64ClangCortexA55Cflags}",
-		"kryo":       "${config.Arm64ClangKryoCflags}",
-		"kryo385":    "${config.Arm64ClangCortexA53Cflags}",
-		"exynos-m1":  "${config.Arm64ClangExynosM1Cflags}",
-		"exynos-m2":  "${config.Arm64ClangExynosM2Cflags}",
+		"cortex-a53": "${config.Arm64CortexA53Cflags}",
+		"cortex-a55": "${config.Arm64CortexA55Cflags}",
+		"cortex-a72": "${config.Arm64CortexA53Cflags}",
+		"cortex-a73": "${config.Arm64CortexA53Cflags}",
+		"cortex-a75": "${config.Arm64CortexA55Cflags}",
+		"cortex-a76": "${config.Arm64CortexA55Cflags}",
+		"kryo":       "${config.Arm64KryoCflags}",
+		"kryo385":    "${config.Arm64CortexA53Cflags}",
+		"exynos-m1":  "${config.Arm64ExynosM1Cflags}",
+		"exynos-m2":  "${config.Arm64ExynosM2Cflags}",
 	}
 )
 
 type toolchainArm64 struct {
+	toolchainBionic
 	toolchain64Bit
 
-	ldflags              string
-	lldflags             string
-	toolchainClangCflags string
+	ldflags         string
+	lldflags        string
+	toolchainCflags string
 }
 
 func (t *toolchainArm64) Name() string {
@@ -180,24 +173,24 @@
 	return t.GccTriple()
 }
 
-func (t *toolchainArm64) ClangCflags() string {
-	return "${config.Arm64ClangCflags}"
+func (t *toolchainArm64) Cflags() string {
+	return "${config.Arm64Cflags}"
 }
 
-func (t *toolchainArm64) ClangCppflags() string {
-	return "${config.Arm64ClangCppflags}"
+func (t *toolchainArm64) Cppflags() string {
+	return "${config.Arm64Cppflags}"
 }
 
-func (t *toolchainArm64) ClangLdflags() string {
+func (t *toolchainArm64) Ldflags() string {
 	return t.ldflags
 }
 
-func (t *toolchainArm64) ClangLldflags() string {
+func (t *toolchainArm64) Lldflags() string {
 	return t.lldflags
 }
 
-func (t *toolchainArm64) ToolchainClangCflags() string {
-	return t.toolchainClangCflags
+func (t *toolchainArm64) ToolchainCflags() string {
+	return t.toolchainCflags
 }
 
 func (toolchainArm64) LibclangRuntimeLibraryArch() string {
@@ -215,9 +208,9 @@
 		panic(fmt.Sprintf("Unknown ARM architecture version: %q", arch.ArchVariant))
 	}
 
-	toolchainClangCflags := []string{arm64ClangArchVariantCflagsVar[arch.ArchVariant]}
-	toolchainClangCflags = append(toolchainClangCflags,
-		variantOrDefault(arm64ClangCpuVariantCflagsVar, arch.CpuVariant))
+	toolchainCflags := []string{arm64ArchVariantCflagsVar[arch.ArchVariant]}
+	toolchainCflags = append(toolchainCflags,
+		variantOrDefault(arm64CpuVariantCflagsVar, arch.CpuVariant))
 
 	var extraLdflags string
 	switch arch.CpuVariant {
@@ -234,7 +227,7 @@
 			"${config.Arm64Lldflags}",
 			extraLdflags,
 		}, " "),
-		toolchainClangCflags: strings.Join(toolchainClangCflags, " "),
+		toolchainCflags: strings.Join(toolchainCflags, " "),
 	}
 }
 
diff --git a/cc/config/arm64_fuchsia_device.go b/cc/config/arm64_fuchsia_device.go
deleted file mode 100644
index 02c0c14..0000000
--- a/cc/config/arm64_fuchsia_device.go
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright 2018 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package config
-
-import (
-	"android/soong/android"
-)
-
-var fuchsiaArm64SysRoot string = "prebuilts/fuchsia_sdk/arch/arm64/sysroot"
-var fuchsiaArm64PrebuiltLibsRoot string = "fuchsia/prebuilt_libs/"
-
-type toolchainFuchsiaArm64 struct {
-	toolchain64Bit
-	toolchainFuchsia
-}
-
-func (t *toolchainFuchsiaArm64) Name() string {
-	return "arm64"
-}
-
-func (t *toolchainFuchsiaArm64) GccRoot() string {
-	return "${config.Arm64GccRoot}"
-}
-
-func (t *toolchainFuchsiaArm64) GccTriple() string {
-	return "aarch64-linux-android"
-}
-
-func (t *toolchainFuchsiaArm64) GccVersion() string {
-	return arm64GccVersion
-}
-
-func (t *toolchainFuchsiaArm64) Cflags() string {
-	return ""
-}
-
-func (t *toolchainFuchsiaArm64) Cppflags() string {
-	return ""
-}
-
-func (t *toolchainFuchsiaArm64) Ldflags() string {
-	return "-Wl,--fix-cortex-a53-843419"
-}
-
-func (t *toolchainFuchsiaArm64) IncludeFlags() string {
-	return ""
-}
-
-func (t *toolchainFuchsiaArm64) ToolchainCflags() string {
-	return "-mcpu=cortex-a53"
-}
-
-func (t *toolchainFuchsiaArm64) ClangTriple() string {
-	return "arm64-fuchsia-android"
-}
-
-func (t *toolchainFuchsiaArm64) ClangCppflags() string {
-	return "-Wno-error=deprecated-declarations"
-}
-
-func (t *toolchainFuchsiaArm64) ClangLdflags() string {
-	return "--target=arm64-fuchsia --sysroot=" + fuchsiaArm64SysRoot + " -L" + fuchsiaArm64PrebuiltLibsRoot + "/aarch64-fuchsia/lib " + "-Lprebuilts/fuchsia_sdk/arch/arm64/dist/"
-}
-
-func (t *toolchainFuchsiaArm64) ClangLldflags() string {
-	return "--target=arm64-fuchsia --sysroot=" + fuchsiaArm64SysRoot + " -L" + fuchsiaArm64PrebuiltLibsRoot + "/aarch64-fuchsia/lib " + "-Lprebuilts/fuchsia_sdk/arch/arm64/dist/"
-}
-
-func (t *toolchainFuchsiaArm64) ClangCflags() string {
-	return "--target=arm64-fuchsia --sysroot=" + fuchsiaArm64SysRoot + " -I" + fuchsiaArm64SysRoot + "/include"
-}
-
-func (t *toolchainFuchsiaArm64) Bionic() bool {
-	return false
-}
-
-func (t *toolchainFuchsiaArm64) ToolchainClangCflags() string {
-	return "-march=armv8-a"
-}
-
-var toolchainArm64FuchsiaSingleton Toolchain = &toolchainFuchsiaArm64{}
-
-func arm64FuchsiaToolchainFactory(arch android.Arch) Toolchain {
-	return toolchainArm64FuchsiaSingleton
-}
-
-func init() {
-	registerToolchainFactory(android.Fuchsia, android.Arm64, arm64FuchsiaToolchainFactory)
-}
diff --git a/cc/config/arm64_linux_host.go b/cc/config/arm64_linux_host.go
index 59c52d1..853d818 100644
--- a/cc/config/arm64_linux_host.go
+++ b/cc/config/arm64_linux_host.go
@@ -15,14 +15,15 @@
 package config
 
 import (
-	"android/soong/android"
 	"strings"
+
+	"android/soong/android"
 )
 
 var (
 	// This is a host toolchain but flags for device toolchain are required
 	// as the flags are actually for Bionic-based builds.
-	linuxCrossCflags = ClangFilterUnknownCflags(append(deviceGlobalCflags,
+	linuxCrossCflags = append(deviceGlobalCflags,
 		// clang by default enables PIC when the clang triple is set to *-android.
 		// See toolchain/llvm-project/clang/lib/Driver/ToolChains/CommonArgs.cpp#920.
 		// However, for this host target, we don't set "-android" to avoid __ANDROID__ macro
@@ -33,9 +34,9 @@
 		// This is normally in ClangExtraTargetCflags, but that's for device and we need
 		// the same for host
 		"-nostdlibinc",
-	))
+	)
 
-	linuxCrossLdflags = ClangFilterUnknownCflags([]string{
+	linuxCrossLdflags = []string{
 		"-Wl,-z,noexecstack",
 		"-Wl,-z,relro",
 		"-Wl,-z,now",
@@ -44,7 +45,17 @@
 		"-Wl,--fatal-warnings",
 		"-Wl,--hash-style=gnu",
 		"-Wl,--no-undefined-version",
-	})
+	}
+
+	// Embed the linker into host bionic binaries. This is needed to support host bionic,
+	// as the linux kernel requires that the ELF interpreter referenced by PT_INTERP be
+	// either an absolute path, or relative from CWD. To work around this, we extract
+	// the load sections from the runtime linker ELF binary and embed them into each host
+	// bionic binary, omitting the PT_INTERP declaration. The kernel will treat it as a static
+	// binary, and then we use a special entry point to fix up the arguments passed by
+	// the kernel before jumping to the embedded linker.
+	linuxArm64CrtBeginSharedBinary = append(android.CopyOf(bionicCrtBeginSharedBinary),
+		"host_bionic_linker_script")
 )
 
 func init() {
@@ -63,14 +74,18 @@
 	return "aarch64-linux"
 }
 
-func (toolchainLinuxArm64) ClangCflags() string {
+func (toolchainLinuxArm64) Cflags() string {
 	// The inherited flags + extra flags
-	return "${config.Arm64ClangCflags} ${config.LinuxBionicArm64Cflags}"
+	return "${config.Arm64Cflags} ${config.LinuxBionicArm64Cflags}"
+}
+
+func (toolchainLinuxArm64) CrtBeginSharedBinary() []string {
+	return linuxArm64CrtBeginSharedBinary
 }
 
 func linuxArm64ToolchainFactory(arch android.Arch) Toolchain {
 	archVariant := "armv8-a" // for host, default to armv8-a
-	toolchainClangCflags := []string{arm64ClangArchVariantCflagsVar[archVariant]}
+	toolchainCflags := []string{arm64ArchVariantCflagsVar[archVariant]}
 
 	// We don't specify CPU architecture for host. Conservatively assume
 	// the host CPU needs the fix
@@ -89,7 +104,7 @@
 		"${config.LinuxBionicArm64Ldflags}",
 		extraLdflags,
 	}, " ")
-	ret.toolchainArm64.toolchainClangCflags = strings.Join(toolchainClangCflags, " ")
+	ret.toolchainArm64.toolchainCflags = strings.Join(toolchainCflags, " ")
 	return &ret
 }
 
diff --git a/cc/config/arm_device.go b/cc/config/arm_device.go
index a402f8f..0fe5e68 100644
--- a/cc/config/arm_device.go
+++ b/cc/config/arm_device.go
@@ -23,7 +23,6 @@
 
 var (
 	armToolchainCflags = []string{
-		"-mthumb-interwork",
 		"-msoft-float",
 	}
 
@@ -38,7 +37,7 @@
 		"-Wl,-m,armelf",
 	}
 
-	armLldflags = ClangFilterUnknownLldflags(armLdflags)
+	armLldflags = armLdflags
 
 	armArmCflags = []string{
 		"-fstrict-aliasing",
@@ -49,7 +48,7 @@
 		"-Os",
 	}
 
-	armClangArchVariantCflags = map[string][]string{
+	armArchVariantCflags = map[string][]string{
 		"armv7-a": []string{
 			"-march=armv7-a",
 			"-mfloat-abi=softfp",
@@ -72,7 +71,7 @@
 		},
 	}
 
-	armClangCpuVariantCflags = map[string][]string{
+	armCpuVariantCflags = map[string][]string{
 		"cortex-a7": []string{
 			"-mcpu=cortex-a7",
 			"-mfpu=neon-vfpv4",
@@ -163,95 +162,90 @@
 )
 
 const (
+	name          = "arm"
 	armGccVersion = "4.9"
+	gccTriple     = "arm-linux-androideabi"
+	clangTriple   = "armv7a-linux-androideabi"
 )
 
 func init() {
 	pctx.StaticVariable("armGccVersion", armGccVersion)
 
-	pctx.SourcePathVariable("ArmGccRoot",
-		"prebuilts/gcc/${HostPrebuiltTag}/arm/arm-linux-androideabi-${armGccVersion}")
+	pctx.SourcePathVariable("ArmGccRoot", "prebuilts/gcc/${HostPrebuiltTag}/arm/arm-linux-androideabi-${armGccVersion}")
 
-	pctx.StaticVariable("ArmLdflags", strings.Join(armLdflags, " "))
-	pctx.StaticVariable("ArmLldflags", strings.Join(armLldflags, " "))
+	// Just exported. Not created as a Ninja static variable.
+	exportedStringVars.Set("ArmClangTriple", clangTriple)
+
+	exportStringListStaticVariable("ArmLdflags", armLdflags)
+	exportStringListStaticVariable("ArmLldflags", armLldflags)
 
 	// Clang cflags
-	pctx.StaticVariable("ArmToolchainClangCflags", strings.Join(ClangFilterUnknownCflags(armToolchainCflags), " "))
-	pctx.StaticVariable("ArmClangCflags", strings.Join(ClangFilterUnknownCflags(armCflags), " "))
-	pctx.StaticVariable("ArmClangLdflags", strings.Join(ClangFilterUnknownCflags(armLdflags), " "))
-	pctx.StaticVariable("ArmClangLldflags", strings.Join(ClangFilterUnknownCflags(armLldflags), " "))
-	pctx.StaticVariable("ArmClangCppflags", strings.Join(ClangFilterUnknownCflags(armCppflags), " "))
+	exportStringListStaticVariable("ArmToolchainCflags", armToolchainCflags)
+	exportStringListStaticVariable("ArmCflags", armCflags)
+	exportStringListStaticVariable("ArmCppflags", armCppflags)
 
 	// Clang ARM vs. Thumb instruction set cflags
-	pctx.StaticVariable("ArmClangArmCflags", strings.Join(ClangFilterUnknownCflags(armArmCflags), " "))
-	pctx.StaticVariable("ArmClangThumbCflags", strings.Join(ClangFilterUnknownCflags(armThumbCflags), " "))
+	exportStringListStaticVariable("ArmArmCflags", armArmCflags)
+	exportStringListStaticVariable("ArmThumbCflags", armThumbCflags)
+
+	exportedStringListDictVars.Set("ArmArchVariantCflags", armArchVariantCflags)
+	exportedStringListDictVars.Set("ArmCpuVariantCflags", armCpuVariantCflags)
 
 	// Clang arch variant cflags
-	pctx.StaticVariable("ArmClangArmv7ACflags",
-		strings.Join(armClangArchVariantCflags["armv7-a"], " "))
-	pctx.StaticVariable("ArmClangArmv7ANeonCflags",
-		strings.Join(armClangArchVariantCflags["armv7-a-neon"], " "))
-	pctx.StaticVariable("ArmClangArmv8ACflags",
-		strings.Join(armClangArchVariantCflags["armv8-a"], " "))
-	pctx.StaticVariable("ArmClangArmv82ACflags",
-		strings.Join(armClangArchVariantCflags["armv8-2a"], " "))
+	exportStringListStaticVariable("ArmArmv7ACflags", armArchVariantCflags["armv7-a"])
+	exportStringListStaticVariable("ArmArmv7ANeonCflags", armArchVariantCflags["armv7-a-neon"])
+	exportStringListStaticVariable("ArmArmv8ACflags", armArchVariantCflags["armv8-a"])
+	exportStringListStaticVariable("ArmArmv82ACflags", armArchVariantCflags["armv8-2a"])
 
 	// Clang cpu variant cflags
-	pctx.StaticVariable("ArmClangGenericCflags",
-		strings.Join(armClangCpuVariantCflags[""], " "))
-	pctx.StaticVariable("ArmClangCortexA7Cflags",
-		strings.Join(armClangCpuVariantCflags["cortex-a7"], " "))
-	pctx.StaticVariable("ArmClangCortexA8Cflags",
-		strings.Join(armClangCpuVariantCflags["cortex-a8"], " "))
-	pctx.StaticVariable("ArmClangCortexA15Cflags",
-		strings.Join(armClangCpuVariantCflags["cortex-a15"], " "))
-	pctx.StaticVariable("ArmClangCortexA53Cflags",
-		strings.Join(armClangCpuVariantCflags["cortex-a53"], " "))
-	pctx.StaticVariable("ArmClangCortexA55Cflags",
-		strings.Join(armClangCpuVariantCflags["cortex-a55"], " "))
-	pctx.StaticVariable("ArmClangKraitCflags",
-		strings.Join(armClangCpuVariantCflags["krait"], " "))
-	pctx.StaticVariable("ArmClangKryoCflags",
-		strings.Join(armClangCpuVariantCflags["kryo"], " "))
+	exportStringListStaticVariable("ArmGenericCflags", armCpuVariantCflags[""])
+	exportStringListStaticVariable("ArmCortexA7Cflags", armCpuVariantCflags["cortex-a7"])
+	exportStringListStaticVariable("ArmCortexA8Cflags", armCpuVariantCflags["cortex-a8"])
+	exportStringListStaticVariable("ArmCortexA15Cflags", armCpuVariantCflags["cortex-a15"])
+	exportStringListStaticVariable("ArmCortexA53Cflags", armCpuVariantCflags["cortex-a53"])
+	exportStringListStaticVariable("ArmCortexA55Cflags", armCpuVariantCflags["cortex-a55"])
+	exportStringListStaticVariable("ArmKraitCflags", armCpuVariantCflags["krait"])
+	exportStringListStaticVariable("ArmKryoCflags", armCpuVariantCflags["kryo"])
 }
 
 var (
-	armClangArchVariantCflagsVar = map[string]string{
-		"armv7-a":      "${config.ArmClangArmv7ACflags}",
-		"armv7-a-neon": "${config.ArmClangArmv7ANeonCflags}",
-		"armv8-a":      "${config.ArmClangArmv8ACflags}",
-		"armv8-2a":     "${config.ArmClangArmv82ACflags}",
+	armArchVariantCflagsVar = map[string]string{
+		"armv7-a":      "${config.ArmArmv7ACflags}",
+		"armv7-a-neon": "${config.ArmArmv7ANeonCflags}",
+		"armv8-a":      "${config.ArmArmv8ACflags}",
+		"armv8-2a":     "${config.ArmArmv82ACflags}",
 	}
 
-	armClangCpuVariantCflagsVar = map[string]string{
-		"":               "${config.ArmClangGenericCflags}",
-		"cortex-a7":      "${config.ArmClangCortexA7Cflags}",
-		"cortex-a8":      "${config.ArmClangCortexA8Cflags}",
-		"cortex-a15":     "${config.ArmClangCortexA15Cflags}",
-		"cortex-a53":     "${config.ArmClangCortexA53Cflags}",
-		"cortex-a53.a57": "${config.ArmClangCortexA53Cflags}",
-		"cortex-a55":     "${config.ArmClangCortexA55Cflags}",
-		"cortex-a72":     "${config.ArmClangCortexA53Cflags}",
-		"cortex-a73":     "${config.ArmClangCortexA53Cflags}",
-		"cortex-a75":     "${config.ArmClangCortexA55Cflags}",
-		"cortex-a76":     "${config.ArmClangCortexA55Cflags}",
-		"krait":          "${config.ArmClangKraitCflags}",
-		"kryo":           "${config.ArmClangKryoCflags}",
-		"kryo385":        "${config.ArmClangCortexA53Cflags}",
-		"exynos-m1":      "${config.ArmClangCortexA53Cflags}",
-		"exynos-m2":      "${config.ArmClangCortexA53Cflags}",
+	armCpuVariantCflagsVar = map[string]string{
+		"":               "${config.ArmGenericCflags}",
+		"cortex-a7":      "${config.ArmCortexA7Cflags}",
+		"cortex-a8":      "${config.ArmCortexA8Cflags}",
+		"cortex-a15":     "${config.ArmCortexA15Cflags}",
+		"cortex-a53":     "${config.ArmCortexA53Cflags}",
+		"cortex-a53.a57": "${config.ArmCortexA53Cflags}",
+		"cortex-a55":     "${config.ArmCortexA55Cflags}",
+		"cortex-a72":     "${config.ArmCortexA53Cflags}",
+		"cortex-a73":     "${config.ArmCortexA53Cflags}",
+		"cortex-a75":     "${config.ArmCortexA55Cflags}",
+		"cortex-a76":     "${config.ArmCortexA55Cflags}",
+		"krait":          "${config.ArmKraitCflags}",
+		"kryo":           "${config.ArmKryoCflags}",
+		"kryo385":        "${config.ArmCortexA53Cflags}",
+		"exynos-m1":      "${config.ArmCortexA53Cflags}",
+		"exynos-m2":      "${config.ArmCortexA53Cflags}",
 	}
 )
 
 type toolchainArm struct {
+	toolchainBionic
 	toolchain32Bit
-	ldflags              string
-	lldflags             string
-	toolchainClangCflags string
+	ldflags         string
+	lldflags        string
+	toolchainCflags string
 }
 
 func (t *toolchainArm) Name() string {
-	return "arm"
+	return name
 }
 
 func (t *toolchainArm) GccRoot() string {
@@ -259,7 +253,7 @@
 }
 
 func (t *toolchainArm) GccTriple() string {
-	return "arm-linux-androideabi"
+	return gccTriple
 }
 
 func (t *toolchainArm) GccVersion() string {
@@ -272,7 +266,7 @@
 
 func (t *toolchainArm) ClangTriple() string {
 	// http://b/72619014 work around llvm LTO bug.
-	return "armv7a-linux-androideabi"
+	return clangTriple
 }
 
 func (t *toolchainArm) ndkTriple() string {
@@ -280,50 +274,50 @@
 	return t.GccTriple()
 }
 
-func (t *toolchainArm) ToolchainClangCflags() string {
-	return t.toolchainClangCflags
+func (t *toolchainArm) ToolchainCflags() string {
+	return t.toolchainCflags
 }
 
-func (t *toolchainArm) ClangCflags() string {
-	return "${config.ArmClangCflags}"
+func (t *toolchainArm) Cflags() string {
+	return "${config.ArmCflags}"
 }
 
-func (t *toolchainArm) ClangCppflags() string {
-	return "${config.ArmClangCppflags}"
+func (t *toolchainArm) Cppflags() string {
+	return "${config.ArmCppflags}"
 }
 
-func (t *toolchainArm) ClangLdflags() string {
+func (t *toolchainArm) Ldflags() string {
 	return t.ldflags
 }
 
-func (t *toolchainArm) ClangLldflags() string {
+func (t *toolchainArm) Lldflags() string {
 	return t.lldflags // TODO: handle V8 cases
 }
 
-func (t *toolchainArm) ClangInstructionSetFlags(isa string) (string, error) {
+func (t *toolchainArm) InstructionSetFlags(isa string) (string, error) {
 	switch isa {
 	case "arm":
-		return "${config.ArmClangArmCflags}", nil
+		return "${config.ArmArmCflags}", nil
 	case "thumb", "":
-		return "${config.ArmClangThumbCflags}", nil
+		return "${config.ArmThumbCflags}", nil
 	default:
-		return t.toolchainBase.ClangInstructionSetFlags(isa)
+		return t.toolchainBase.InstructionSetFlags(isa)
 	}
 }
 
 func (toolchainArm) LibclangRuntimeLibraryArch() string {
-	return "arm"
+	return name
 }
 
 func armToolchainFactory(arch android.Arch) Toolchain {
 	var fixCortexA8 string
-	toolchainClangCflags := make([]string, 2, 3)
+	toolchainCflags := make([]string, 2, 3)
 
-	toolchainClangCflags[0] = "${config.ArmToolchainClangCflags}"
-	toolchainClangCflags[1] = armClangArchVariantCflagsVar[arch.ArchVariant]
+	toolchainCflags[0] = "${config.ArmToolchainCflags}"
+	toolchainCflags[1] = armArchVariantCflagsVar[arch.ArchVariant]
 
-	toolchainClangCflags = append(toolchainClangCflags,
-		variantOrDefault(armClangCpuVariantCflagsVar, arch.CpuVariant))
+	toolchainCflags = append(toolchainCflags,
+		variantOrDefault(armCpuVariantCflagsVar, arch.CpuVariant))
 
 	switch arch.ArchVariant {
 	case "armv7-a-neon":
@@ -347,8 +341,8 @@
 			"${config.ArmLdflags}",
 			fixCortexA8,
 		}, " "),
-		lldflags:             "${config.ArmLldflags}",
-		toolchainClangCflags: strings.Join(toolchainClangCflags, " "),
+		lldflags:        "${config.ArmLldflags}",
+		toolchainCflags: strings.Join(toolchainCflags, " "),
 	}
 }
 
diff --git a/cc/config/bionic.go b/cc/config/bionic.go
new file mode 100644
index 0000000..e87f571
--- /dev/null
+++ b/cc/config/bionic.go
@@ -0,0 +1,37 @@
+// Copyright 2015 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package config
+
+type toolchainBionic struct {
+}
+
+var (
+	bionicDefaultSharedLibraries = []string{"libc", "libm", "libdl"}
+
+	bionicCrtBeginStaticBinary, bionicCrtEndStaticBinary   = []string{"crtbegin_static"}, []string{"crtend_android"}
+	bionicCrtBeginSharedBinary, bionicCrtEndSharedBinary   = []string{"crtbegin_dynamic"}, []string{"crtend_android"}
+	bionicCrtBeginSharedLibrary, bionicCrtEndSharedLibrary = []string{"crtbegin_so"}, []string{"crtend_so"}
+)
+
+func (toolchainBionic) Bionic() bool { return true }
+
+func (toolchainBionic) DefaultSharedLibraries() []string { return bionicDefaultSharedLibraries }
+
+func (toolchainBionic) CrtBeginStaticBinary() []string  { return bionicCrtBeginStaticBinary }
+func (toolchainBionic) CrtBeginSharedBinary() []string  { return bionicCrtBeginSharedBinary }
+func (toolchainBionic) CrtBeginSharedLibrary() []string { return bionicCrtBeginSharedLibrary }
+func (toolchainBionic) CrtEndStaticBinary() []string    { return bionicCrtEndStaticBinary }
+func (toolchainBionic) CrtEndSharedBinary() []string    { return bionicCrtEndSharedBinary }
+func (toolchainBionic) CrtEndSharedLibrary() []string   { return bionicCrtEndSharedLibrary }
diff --git a/cc/config/bp2build.go b/cc/config/bp2build.go
index 16892e6..d19f5ac 100644
--- a/cc/config/bp2build.go
+++ b/cc/config/bp2build.go
@@ -15,80 +15,182 @@
 package config
 
 import (
-	"android/soong/android"
 	"fmt"
 	"regexp"
+	"sort"
 	"strings"
 )
 
-// Helpers for exporting cc configuration information to Bazel.
+const (
+	bazelIndent = 4
+)
 
+type bazelVarExporter interface {
+	asBazel(exportedStringVariables, exportedStringListVariables) []bazelConstant
+}
+
+// Helpers for exporting cc configuration information to Bazel.
 var (
 	// Map containing toolchain variables that are independent of the
 	// environment variables of the build.
-	exportedVars = exportedVariablesMap{}
+	exportedStringListVars     = exportedStringListVariables{}
+	exportedStringVars         = exportedStringVariables{}
+	exportedStringListDictVars = exportedStringListDictVariables{}
 )
 
-// variableValue is a string slice because the exported variables are all lists
-// of string, and it's simpler to manipulate string lists before joining them
-// into their final string representation.
-type variableValue []string
+// Ensure that string s has no invalid characters to be generated into the bzl file.
+func validateCharacters(s string) string {
+	for _, c := range []string{`\n`, `"`, `\`} {
+		if strings.Contains(s, c) {
+			panic(fmt.Errorf("%s contains illegal character %s", s, c))
+		}
+	}
+	return s
+}
 
-// envDependentVariable is a toolchain variable computed based on an environment variable.
-type exportedVariablesMap map[string]variableValue
+type bazelConstant struct {
+	variableName       string
+	internalDefinition string
+}
 
-func (m exportedVariablesMap) Set(k string, v variableValue) {
+type exportedStringVariables map[string]string
+
+func (m exportedStringVariables) Set(k string, v string) {
 	m[k] = v
 }
 
+func bazelIndention(level int) string {
+	return strings.Repeat(" ", level*bazelIndent)
+}
+
+func printBazelList(items []string, indentLevel int) string {
+	list := make([]string, 0, len(items)+2)
+	list = append(list, "[")
+	innerIndent := bazelIndention(indentLevel + 1)
+	for _, item := range items {
+		list = append(list, fmt.Sprintf(`%s"%s",`, innerIndent, item))
+	}
+	list = append(list, bazelIndention(indentLevel)+"]")
+	return strings.Join(list, "\n")
+}
+
+func (m exportedStringVariables) asBazel(stringScope exportedStringVariables, stringListScope exportedStringListVariables) []bazelConstant {
+	ret := make([]bazelConstant, 0, len(m))
+	for k, variableValue := range m {
+		expandedVar := expandVar(variableValue, exportedStringVars, exportedStringListVars)
+		if len(expandedVar) > 1 {
+			panic(fmt.Errorf("%s expands to more than one string value: %s", variableValue, expandedVar))
+		}
+		ret = append(ret, bazelConstant{
+			variableName:       k,
+			internalDefinition: fmt.Sprintf(`"%s"`, validateCharacters(expandedVar[0])),
+		})
+	}
+	return ret
+}
+
 // Convenience function to declare a static variable and export it to Bazel's cc_toolchain.
-func staticVariableExportedToBazel(name string, value []string) {
+func exportStringStaticVariable(name string, value string) {
+	pctx.StaticVariable(name, value)
+	exportedStringVars.Set(name, value)
+}
+
+type exportedStringListVariables map[string][]string
+
+func (m exportedStringListVariables) Set(k string, v []string) {
+	m[k] = v
+}
+
+func (m exportedStringListVariables) asBazel(stringScope exportedStringVariables, stringListScope exportedStringListVariables) []bazelConstant {
+	ret := make([]bazelConstant, 0, len(m))
+	// For each exported variable, recursively expand elements in the variableValue
+	// list to ensure that interpolated variables are expanded according to their values
+	// in the variable scope.
+	for k, variableValue := range m {
+		var expandedVars []string
+		for _, v := range variableValue {
+			expandedVars = append(expandedVars, expandVar(v, stringScope, stringListScope)...)
+		}
+		// Assign the list as a bzl-private variable; this variable will be exported
+		// out through a constants struct later.
+		ret = append(ret, bazelConstant{
+			variableName:       k,
+			internalDefinition: printBazelList(expandedVars, 0),
+		})
+	}
+	return ret
+}
+
+// Convenience function to declare a static variable and export it to Bazel's cc_toolchain.
+func exportStringListStaticVariable(name string, value []string) {
 	pctx.StaticVariable(name, strings.Join(value, " "))
-	exportedVars.Set(name, variableValue(value))
+	exportedStringListVars.Set(name, value)
+}
+
+type exportedStringListDictVariables map[string]map[string][]string
+
+func (m exportedStringListDictVariables) Set(k string, v map[string][]string) {
+	m[k] = v
+}
+
+func printBazelStringListDict(dict map[string][]string) string {
+	bazelDict := make([]string, 0, len(dict)+2)
+	bazelDict = append(bazelDict, "{")
+	for k, v := range dict {
+		bazelDict = append(bazelDict,
+			fmt.Sprintf(`%s"%s": %s,`, bazelIndention(1), k, printBazelList(v, 1)))
+	}
+	bazelDict = append(bazelDict, "}")
+	return strings.Join(bazelDict, "\n")
+}
+
+// Since dictionaries are not supported in Ninja, we do not expand variables for dictionaries
+func (m exportedStringListDictVariables) asBazel(_ exportedStringVariables, _ exportedStringListVariables) []bazelConstant {
+	ret := make([]bazelConstant, 0, len(m))
+	for k, dict := range m {
+		ret = append(ret, bazelConstant{
+			variableName:       k,
+			internalDefinition: printBazelStringListDict(dict),
+		})
+	}
+	return ret
 }
 
 // BazelCcToolchainVars generates bzl file content containing variables for
 // Bazel's cc_toolchain configuration.
 func BazelCcToolchainVars() string {
+	return bazelToolchainVars(
+		exportedStringListDictVars,
+		exportedStringListVars,
+		exportedStringVars)
+}
+
+func bazelToolchainVars(vars ...bazelVarExporter) string {
 	ret := "# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.\n\n"
 
-	// Ensure that string s has no invalid characters to be generated into the bzl file.
-	validateCharacters := func(s string) string {
-		for _, c := range []string{`\n`, `"`, `\`} {
-			if strings.Contains(s, c) {
-				panic(fmt.Errorf("%s contains illegal character %s", s, c))
-			}
-		}
-		return s
+	results := []bazelConstant{}
+	for _, v := range vars {
+		results = append(results, v.asBazel(exportedStringVars, exportedStringListVars)...)
 	}
 
-	// For each exported variable, recursively expand elements in the variableValue
-	// list to ensure that interpolated variables are expanded according to their values
-	// in the exportedVars scope.
-	for _, k := range android.SortedStringKeys(exportedVars) {
-		variableValue := exportedVars[k]
-		var expandedVars []string
-		for _, v := range variableValue {
-			expandedVars = append(expandedVars, expandVar(v, exportedVars)...)
-		}
-		// Build the list for this variable.
-		list := "["
-		for _, flag := range expandedVars {
-			list += fmt.Sprintf("\n    \"%s\",", validateCharacters(flag))
-		}
-		list += "\n]"
-		// Assign the list as a bzl-private variable; this variable will be exported
-		// out through a constants struct later.
-		ret += fmt.Sprintf("_%s = %s\n", k, list)
-		ret += "\n"
+	sort.Slice(results, func(i, j int) bool { return results[i].variableName < results[j].variableName })
+
+	definitions := make([]string, 0, len(results))
+	constants := make([]string, 0, len(results))
+	for _, b := range results {
+		definitions = append(definitions,
+			fmt.Sprintf("_%s = %s", b.variableName, b.internalDefinition))
+		constants = append(constants,
+			fmt.Sprintf("%[1]s%[2]s = _%[2]s,", bazelIndention(1), b.variableName))
 	}
 
 	// Build the exported constants struct.
+	ret += strings.Join(definitions, "\n\n")
+	ret += "\n\n"
 	ret += "constants = struct(\n"
-	for _, k := range android.SortedStringKeys(exportedVars) {
-		ret += fmt.Sprintf("    %s = _%s,\n", k, k)
-	}
-	ret += ")"
+	ret += strings.Join(constants, "\n")
+	ret += "\n)"
+
 	return ret
 }
 
@@ -99,8 +201,8 @@
 // string slice than to handle a pass-by-referenced map, which would make it
 // quite complex to track depth-first interpolations. It's also unlikely the
 // interpolation stacks are deep (n > 1).
-func expandVar(toExpand string, exportedVars map[string]variableValue) []string {
-	// e.g. "${ClangExternalCflags}"
+func expandVar(toExpand string, stringScope exportedStringVariables, stringListScope exportedStringListVariables) []string {
+	// e.g. "${ExternalCflags}"
 	r := regexp.MustCompile(`\${([a-zA-Z0-9_]+)}`)
 
 	// Internal recursive function.
@@ -136,8 +238,11 @@
 				newSeenVars[k] = true
 			}
 			newSeenVars[variable] = true
-			unexpandedVars := exportedVars[variable]
-			for _, unexpandedVar := range unexpandedVars {
+			if unexpandedVars, ok := stringListScope[variable]; ok {
+				for _, unexpandedVar := range unexpandedVars {
+					ret = append(ret, expandVarInternal(unexpandedVar, newSeenVars)...)
+				}
+			} else if unexpandedVar, ok := stringScope[variable]; ok {
 				ret = append(ret, expandVarInternal(unexpandedVar, newSeenVars)...)
 			}
 		}
diff --git a/cc/config/bp2build_test.go b/cc/config/bp2build_test.go
index 7744b4b..883597a 100644
--- a/cc/config/bp2build_test.go
+++ b/cc/config/bp2build_test.go
@@ -20,54 +20,80 @@
 
 func TestExpandVars(t *testing.T) {
 	testCases := []struct {
-		description    string
-		exportedVars   map[string]variableValue
-		toExpand       string
-		expectedValues []string
+		description     string
+		stringScope     exportedStringVariables
+		stringListScope exportedStringListVariables
+		toExpand        string
+		expectedValues  []string
 	}{
 		{
-			description: "single level expansion",
-			exportedVars: map[string]variableValue{
-				"foo": variableValue([]string{"bar"}),
+			description:    "no expansion for non-interpolated value",
+			toExpand:       "foo",
+			expectedValues: []string{"foo"},
+		},
+		{
+			description: "single level expansion for string var",
+			stringScope: exportedStringVariables{
+				"foo": "bar",
 			},
 			toExpand:       "${foo}",
 			expectedValues: []string{"bar"},
 		},
 		{
+			description: "single level expansion string list var",
+			stringListScope: exportedStringListVariables{
+				"foo": []string{"bar"},
+			},
+			toExpand:       "${foo}",
+			expectedValues: []string{"bar"},
+		},
+		{
+			description: "mixed level expansion for string list var",
+			stringScope: exportedStringVariables{
+				"foo": "${bar}",
+				"qux": "hello",
+			},
+			stringListScope: exportedStringListVariables{
+				"bar": []string{"baz", "${qux}"},
+			},
+			toExpand:       "${foo}",
+			expectedValues: []string{"baz", "hello"},
+		},
+		{
 			description: "double level expansion",
-			exportedVars: map[string]variableValue{
-				"foo": variableValue([]string{"${bar}"}),
-				"bar": variableValue([]string{"baz"}),
+			stringListScope: exportedStringListVariables{
+				"foo": []string{"${bar}"},
+				"bar": []string{"baz"},
 			},
 			toExpand:       "${foo}",
 			expectedValues: []string{"baz"},
 		},
 		{
 			description: "double level expansion with a literal",
-			exportedVars: map[string]variableValue{
-				"a": variableValue([]string{"${b}", "c"}),
-				"b": variableValue([]string{"d"}),
+			stringListScope: exportedStringListVariables{
+				"a": []string{"${b}", "c"},
+				"b": []string{"d"},
 			},
 			toExpand:       "${a}",
 			expectedValues: []string{"d", "c"},
 		},
 		{
 			description: "double level expansion, with two variables in a string",
-			exportedVars: map[string]variableValue{
-				"a": variableValue([]string{"${b} ${c}"}),
-				"b": variableValue([]string{"d"}),
-				"c": variableValue([]string{"e"}),
+			stringListScope: exportedStringListVariables{
+				"a": []string{"${b} ${c}"},
+				"b": []string{"d"},
+				"c": []string{"e"},
 			},
 			toExpand:       "${a}",
 			expectedValues: []string{"d", "e"},
 		},
 		{
 			description: "triple level expansion with two variables in a string",
-			exportedVars: map[string]variableValue{
-				"a": variableValue([]string{"${b} ${c}"}),
-				"b": variableValue([]string{"${c}", "${d}"}),
-				"c": variableValue([]string{"${d}"}),
-				"d": variableValue([]string{"foo"}),
+			stringListScope: exportedStringListVariables{
+				"a": []string{"${b} ${c}"},
+				"b": []string{"${c}", "${d}"},
+				"c": []string{"${d}"},
+				"d": []string{"foo"},
 			},
 			toExpand:       "${a}",
 			expectedValues: []string{"foo", "foo", "foo"},
@@ -76,7 +102,7 @@
 
 	for _, testCase := range testCases {
 		t.Run(testCase.description, func(t *testing.T) {
-			output := expandVar(testCase.toExpand, testCase.exportedVars)
+			output := expandVar(testCase.toExpand, testCase.stringScope, testCase.stringListScope)
 			if len(output) != len(testCase.expectedValues) {
 				t.Errorf("Expected %d values, got %d", len(testCase.expectedValues), len(output))
 			}
@@ -89,3 +115,143 @@
 		})
 	}
 }
+
+func TestBazelToolchainVars(t *testing.T) {
+	testCases := []struct {
+		name        string
+		vars        []bazelVarExporter
+		expectedOut string
+	}{
+		{
+			name: "exports strings",
+			vars: []bazelVarExporter{
+				exportedStringVariables{
+					"a": "b",
+					"c": "d",
+				},
+			},
+			expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
+
+_a = "b"
+
+_c = "d"
+
+constants = struct(
+    a = _a,
+    c = _c,
+)`,
+		},
+		{
+			name: "exports string lists",
+			vars: []bazelVarExporter{
+				exportedStringListVariables{
+					"a": []string{"b1", "b2"},
+					"c": []string{"d1", "d2"},
+				},
+			},
+			expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
+
+_a = [
+    "b1",
+    "b2",
+]
+
+_c = [
+    "d1",
+    "d2",
+]
+
+constants = struct(
+    a = _a,
+    c = _c,
+)`,
+		},
+		{
+			name: "exports string lists dicts",
+			vars: []bazelVarExporter{
+				exportedStringListDictVariables{
+					"a": map[string][]string{"b1": []string{"b2"}},
+					"c": map[string][]string{"d1": []string{"d2"}},
+				},
+			},
+			expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
+
+_a = {
+    "b1": [
+        "b2",
+    ],
+}
+
+_c = {
+    "d1": [
+        "d2",
+    ],
+}
+
+constants = struct(
+    a = _a,
+    c = _c,
+)`,
+		},
+		{
+			name: "sorts across types",
+			vars: []bazelVarExporter{
+				exportedStringVariables{
+					"b": "b-val",
+					"d": "d-val",
+				},
+				exportedStringListVariables{
+					"c": []string{"c-val"},
+					"e": []string{"e-val"},
+				},
+				exportedStringListDictVariables{
+					"a": map[string][]string{"a1": []string{"a2"}},
+					"f": map[string][]string{"f1": []string{"f2"}},
+				},
+			},
+			expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
+
+_a = {
+    "a1": [
+        "a2",
+    ],
+}
+
+_b = "b-val"
+
+_c = [
+    "c-val",
+]
+
+_d = "d-val"
+
+_e = [
+    "e-val",
+]
+
+_f = {
+    "f1": [
+        "f2",
+    ],
+}
+
+constants = struct(
+    a = _a,
+    b = _b,
+    c = _c,
+    d = _d,
+    e = _e,
+    f = _f,
+)`,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			out := bazelToolchainVars(tc.vars...)
+			if out != tc.expectedOut {
+				t.Errorf("Expected \n%s, got \n%s", tc.expectedOut, out)
+			}
+		})
+	}
+}
diff --git a/cc/config/clang.go b/cc/config/clang.go
index 4fbb9c3..53a7306 100644
--- a/cc/config/clang.go
+++ b/cc/config/clang.go
@@ -15,9 +15,10 @@
 package config
 
 import (
-	"android/soong/android"
 	"sort"
 	"strings"
+
+	"android/soong/android"
 )
 
 // Cflags that should be filtered out when compiling with clang
@@ -80,12 +81,6 @@
 	"--enable-stdcall-fixup",
 })
 
-// Ldflags that should be filtered out when linking with clang lld
-var ClangUnknownLldflags = sorted([]string{
-	"-Wl,--fix-cortex-a8",
-	"-Wl,--no-fix-cortex-a8",
-})
-
 var ClangLibToolingUnknownCflags = sorted([]string{})
 
 // List of tidy checks that should be disabled globally. When the compiler is
@@ -97,138 +92,6 @@
 	"readability-function-cognitive-complexity", // http://b/175055536
 }
 
-func init() {
-	staticVariableExportedToBazel("ClangExtraCflags", []string{
-		"-D__compiler_offsetof=__builtin_offsetof",
-
-		// Emit address-significance table which allows linker to perform safe ICF. Clang does
-		// not emit the table by default on Android since NDK still uses GNU binutils.
-		"-faddrsig",
-
-		// Turn on -fcommon explicitly, since Clang now defaults to -fno-common. The cleanup bug
-		// tracking this is http://b/151457797.
-		"-fcommon",
-
-		// Help catch common 32/64-bit errors.
-		"-Werror=int-conversion",
-
-		// Enable the new pass manager.
-		"-fexperimental-new-pass-manager",
-
-		// Disable overly aggressive warning for macros defined with a leading underscore
-		// This happens in AndroidConfig.h, which is included nearly everywhere.
-		// TODO: can we remove this now?
-		"-Wno-reserved-id-macro",
-
-		// Workaround for ccache with clang.
-		// See http://petereisentraut.blogspot.com/2011/05/ccache-and-clang.html.
-		"-Wno-unused-command-line-argument",
-
-		// Force clang to always output color diagnostics. Ninja will strip the ANSI
-		// color codes if it is not running in a terminal.
-		"-fcolor-diagnostics",
-
-		// Warnings from clang-7.0
-		"-Wno-sign-compare",
-
-		// Warnings from clang-8.0
-		"-Wno-defaulted-function-deleted",
-
-		// Disable -Winconsistent-missing-override until we can clean up the existing
-		// codebase for it.
-		"-Wno-inconsistent-missing-override",
-
-		// Warnings from clang-10
-		// Nested and array designated initialization is nice to have.
-		"-Wno-c99-designator",
-
-		// Warnings from clang-12
-		"-Wno-gnu-folding-constant",
-
-		// Calls to the APIs that are newer than the min sdk version of the caller should be
-		// guarded with __builtin_available.
-		"-Wunguarded-availability",
-		// This macro allows the bionic versioning.h to indirectly determine whether the
-		// option -Wunguarded-availability is on or not.
-		"-D__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__",
-	})
-
-	staticVariableExportedToBazel("ClangExtraCppflags", []string{
-		// -Wimplicit-fallthrough is not enabled by -Wall.
-		"-Wimplicit-fallthrough",
-
-		// Enable clang's thread-safety annotations in libcxx.
-		"-D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS",
-
-		// libc++'s math.h has an #include_next outside of system_headers.
-		"-Wno-gnu-include-next",
-	})
-
-	staticVariableExportedToBazel("ClangExtraTargetCflags", []string{"-nostdlibinc"})
-
-	staticVariableExportedToBazel("ClangExtraNoOverrideCflags", []string{
-		"-Werror=address-of-temporary",
-		// Bug: http://b/29823425 Disable -Wnull-dereference until the
-		// new cases detected by this warning in Clang r271374 are
-		// fixed.
-		//"-Werror=null-dereference",
-		"-Werror=return-type",
-
-		// http://b/72331526 Disable -Wtautological-* until the instances detected by these
-		// new warnings are fixed.
-		"-Wno-tautological-constant-compare",
-		"-Wno-tautological-type-limit-compare",
-		// http://b/145210666
-		"-Wno-reorder-init-list",
-		// http://b/145211066
-		"-Wno-implicit-int-float-conversion",
-		// New warnings to be fixed after clang-r377782.
-		"-Wno-int-in-bool-context",          // http://b/148287349
-		"-Wno-sizeof-array-div",             // http://b/148815709
-		"-Wno-tautological-overlap-compare", // http://b/148815696
-		// New warnings to be fixed after clang-r383902.
-		"-Wno-deprecated-copy",                      // http://b/153746672
-		"-Wno-range-loop-construct",                 // http://b/153747076
-		"-Wno-misleading-indentation",               // http://b/153746954
-		"-Wno-zero-as-null-pointer-constant",        // http://b/68236239
-		"-Wno-deprecated-anon-enum-enum-conversion", // http://b/153746485
-		"-Wno-deprecated-enum-enum-conversion",      // http://b/153746563
-		"-Wno-string-compare",                       // http://b/153764102
-		"-Wno-enum-enum-conversion",                 // http://b/154138986
-		"-Wno-enum-float-conversion",                // http://b/154255917
-		"-Wno-pessimizing-move",                     // http://b/154270751
-		// New warnings to be fixed after clang-r399163
-		"-Wno-non-c-typedef-for-linkage", // http://b/161304145
-		// New warnings to be fixed after clang-r407598
-		"-Wno-string-concatenation", // http://b/175068488
-	})
-
-	// Extra cflags for external third-party projects to disable warnings that
-	// are infeasible to fix in all the external projects and their upstream repos.
-	staticVariableExportedToBazel("ClangExtraExternalCflags", []string{
-		"-Wno-enum-compare",
-		"-Wno-enum-compare-switch",
-
-		// http://b/72331524 Allow null pointer arithmetic until the instances detected by
-		// this new warning are fixed.
-		"-Wno-null-pointer-arithmetic",
-
-		// Bug: http://b/29823425 Disable -Wnull-dereference until the
-		// new instances detected by this warning are fixed.
-		"-Wno-null-dereference",
-
-		// http://b/145211477
-		"-Wno-pointer-compare",
-		// http://b/145211022
-		"-Wno-xor-used-as-pow",
-		// http://b/145211022
-		"-Wno-final-dtor-non-final-class",
-
-		// http://b/165945989
-		"-Wno-psabi",
-	})
-}
-
 func ClangFilterUnknownCflags(cflags []string) []string {
 	result, _ := android.FilterList(cflags, ClangUnknownCflags)
 	return result
@@ -255,26 +118,10 @@
 	return result
 }
 
-func ClangFilterUnknownLldflags(lldflags []string) []string {
-	result, _ := android.FilterList(lldflags, ClangUnknownLldflags)
-	return result
-}
-
 func ClangLibToolingFilterUnknownCflags(libToolingFlags []string) []string {
 	return android.RemoveListFromList(libToolingFlags, ClangLibToolingUnknownCflags)
 }
 
-func inListSorted(s string, list []string) bool {
-	for _, l := range list {
-		if s == l {
-			return true
-		} else if s < l {
-			return false
-		}
-	}
-	return false
-}
-
 func sorted(list []string) []string {
 	sort.Strings(list)
 	return list
diff --git a/cc/config/global.go b/cc/config/global.go
index ae731b2..9773345 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -15,6 +15,7 @@
 package config
 
 import (
+	"runtime"
 	"strings"
 
 	"android/soong/android"
@@ -36,7 +37,6 @@
 
 		// Make paths in deps files relative
 		"-no-canonical-prefixes",
-		"-fno-canonical-system-headers",
 
 		"-DNDEBUG",
 		"-UDEBUG",
@@ -55,13 +55,64 @@
 		"-Werror=pragma-pack-suspicious-include",
 		"-Werror=string-plus-int",
 		"-Werror=unreachable-code-loop-increment",
+
+		"-D__compiler_offsetof=__builtin_offsetof",
+
+		// Emit address-significance table which allows linker to perform safe ICF. Clang does
+		// not emit the table by default on Android since NDK still uses GNU binutils.
+		"-faddrsig",
+
+		// Turn on -fcommon explicitly, since Clang now defaults to -fno-common. The cleanup bug
+		// tracking this is http://b/151457797.
+		"-fcommon",
+
+		// Help catch common 32/64-bit errors.
+		"-Werror=int-conversion",
+
+		// Enable the new pass manager.
+		"-fexperimental-new-pass-manager",
+
+		// Disable overly aggressive warning for macros defined with a leading underscore
+		// This happens in AndroidConfig.h, which is included nearly everywhere.
+		// TODO: can we remove this now?
+		"-Wno-reserved-id-macro",
+
+		// Workaround for ccache with clang.
+		// See http://petereisentraut.blogspot.com/2011/05/ccache-and-clang.html.
+		"-Wno-unused-command-line-argument",
+
+		// Force clang to always output color diagnostics. Ninja will strip the ANSI
+		// color codes if it is not running in a terminal.
+		"-fcolor-diagnostics",
+
+		// Warnings from clang-7.0
+		"-Wno-sign-compare",
+
+		// Warnings from clang-8.0
+		"-Wno-defaulted-function-deleted",
+
+		// Disable -Winconsistent-missing-override until we can clean up the existing
+		// codebase for it.
+		"-Wno-inconsistent-missing-override",
+
+		// Warnings from clang-10
+		// Nested and array designated initialization is nice to have.
+		"-Wno-c99-designator",
+
+		// Warnings from clang-12
+		"-Wno-gnu-folding-constant",
+
+		// Calls to the APIs that are newer than the min sdk version of the caller should be
+		// guarded with __builtin_available.
+		"-Wunguarded-availability",
+		// This macro allows the bionic versioning.h to indirectly determine whether the
+		// option -Wunguarded-availability is on or not.
+		"-D__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__",
 	}
 
 	commonGlobalConlyflags = []string{}
 
 	deviceGlobalCflags = []string{
-		"-fdiagnostics-color",
-
 		"-ffunction-sections",
 		"-fdata-sections",
 		"-fno-short-enums",
@@ -77,6 +128,7 @@
 		"-Werror=address",
 		"-Werror=sequence-point",
 		"-Werror=format-security",
+		"-nostdlibinc",
 	}
 
 	deviceGlobalCppflags = []string{
@@ -100,7 +152,7 @@
 		"-Wl,--icf=safe",
 	}
 
-	deviceGlobalLldflags = append(ClangFilterUnknownLldflags(deviceGlobalLdflags),
+	deviceGlobalLldflags = append(deviceGlobalLdflags,
 		[]string{
 			"-fuse-ld=lld",
 		}...)
@@ -115,6 +167,15 @@
 
 	commonGlobalCppflags = []string{
 		"-Wsign-promo",
+
+		// -Wimplicit-fallthrough is not enabled by -Wall.
+		"-Wimplicit-fallthrough",
+
+		// Enable clang's thread-safety annotations in libcxx.
+		"-D_LIBCPP_ENABLE_THREAD_SAFETY_ANNOTATIONS",
+
+		// libc++'s math.h has an #include_next outside of system_headers.
+		"-Wno-gnu-include-next",
 	}
 
 	noOverrideGlobalCflags = []string{
@@ -132,6 +193,68 @@
 		// http://b/161386391 for -Wno-pointer-to-int-cast
 		"-Wno-pointer-to-int-cast",
 		"-Werror=fortify-source",
+
+		"-Werror=address-of-temporary",
+		// Bug: http://b/29823425 Disable -Wnull-dereference until the
+		// new cases detected by this warning in Clang r271374 are
+		// fixed.
+		//"-Werror=null-dereference",
+		"-Werror=return-type",
+
+		// http://b/72331526 Disable -Wtautological-* until the instances detected by these
+		// new warnings are fixed.
+		"-Wno-tautological-constant-compare",
+		"-Wno-tautological-type-limit-compare",
+		// http://b/145210666
+		"-Wno-reorder-init-list",
+		// http://b/145211066
+		"-Wno-implicit-int-float-conversion",
+		// New warnings to be fixed after clang-r377782.
+		"-Wno-int-in-bool-context",          // http://b/148287349
+		"-Wno-sizeof-array-div",             // http://b/148815709
+		"-Wno-tautological-overlap-compare", // http://b/148815696
+		// New warnings to be fixed after clang-r383902.
+		"-Wno-deprecated-copy",                      // http://b/153746672
+		"-Wno-range-loop-construct",                 // http://b/153747076
+		"-Wno-misleading-indentation",               // http://b/153746954
+		"-Wno-zero-as-null-pointer-constant",        // http://b/68236239
+		"-Wno-deprecated-anon-enum-enum-conversion", // http://b/153746485
+		"-Wno-deprecated-enum-enum-conversion",      // http://b/153746563
+		"-Wno-string-compare",                       // http://b/153764102
+		"-Wno-enum-enum-conversion",                 // http://b/154138986
+		"-Wno-enum-float-conversion",                // http://b/154255917
+		"-Wno-pessimizing-move",                     // http://b/154270751
+		// New warnings to be fixed after clang-r399163
+		"-Wno-non-c-typedef-for-linkage", // http://b/161304145
+		// New warnings to be fixed after clang-r407598
+		"-Wno-string-concatenation", // http://b/175068488
+		// New warnings to be fixed after clang-r428724
+		"-Wno-align-mismatch", // http://b/193679946
+	}
+
+	// Extra cflags for external third-party projects to disable warnings that
+	// are infeasible to fix in all the external projects and their upstream repos.
+	extraExternalCflags = []string{
+		"-Wno-enum-compare",
+		"-Wno-enum-compare-switch",
+
+		// http://b/72331524 Allow null pointer arithmetic until the instances detected by
+		// this new warning are fixed.
+		"-Wno-null-pointer-arithmetic",
+
+		// Bug: http://b/29823425 Disable -Wnull-dereference until the
+		// new instances detected by this warning are fixed.
+		"-Wno-null-dereference",
+
+		// http://b/145211477
+		"-Wno-pointer-compare",
+		// http://b/145211022
+		"-Wno-xor-used-as-pow",
+		// http://b/145211022
+		"-Wno-final-dtor-non-final-class",
+
+		// http://b/165945989
+		"-Wno-psabi",
 	}
 
 	IllegalFlags = []string{
@@ -145,8 +268,8 @@
 
 	// prebuilts/clang default settings.
 	ClangDefaultBase         = "prebuilts/clang/host"
-	ClangDefaultVersion      = "clang-r416183b1"
-	ClangDefaultShortVersion = "12.0.7"
+	ClangDefaultVersion      = "clang-r428724"
+	ClangDefaultShortVersion = "13.0.1"
 
 	// Directories with warnings from Android.bp files.
 	WarningAllowedProjects = []string{
@@ -161,33 +284,31 @@
 var pctx = android.NewPackageContext("android/soong/cc/config")
 
 func init() {
-	if android.BuildOs == android.Linux {
+	if runtime.GOOS == "linux" {
 		commonGlobalCflags = append(commonGlobalCflags, "-fdebug-prefix-map=/proc/self/cwd=")
 	}
 
-	staticVariableExportedToBazel("CommonGlobalConlyflags", commonGlobalConlyflags)
-	staticVariableExportedToBazel("DeviceGlobalCppflags", deviceGlobalCppflags)
-	staticVariableExportedToBazel("DeviceGlobalLdflags", deviceGlobalLdflags)
-	staticVariableExportedToBazel("DeviceGlobalLldflags", deviceGlobalLldflags)
-	staticVariableExportedToBazel("HostGlobalCppflags", hostGlobalCppflags)
-	staticVariableExportedToBazel("HostGlobalLdflags", hostGlobalLdflags)
-	staticVariableExportedToBazel("HostGlobalLldflags", hostGlobalLldflags)
+	exportStringListStaticVariable("CommonGlobalConlyflags", commonGlobalConlyflags)
+	exportStringListStaticVariable("DeviceGlobalCppflags", deviceGlobalCppflags)
+	exportStringListStaticVariable("DeviceGlobalLdflags", deviceGlobalLdflags)
+	exportStringListStaticVariable("DeviceGlobalLldflags", deviceGlobalLldflags)
+	exportStringListStaticVariable("HostGlobalCppflags", hostGlobalCppflags)
+	exportStringListStaticVariable("HostGlobalLdflags", hostGlobalLdflags)
+	exportStringListStaticVariable("HostGlobalLldflags", hostGlobalLldflags)
 
-	// Export the static default CommonClangGlobalCflags to Bazel.
+	// Export the static default CommonGlobalCflags to Bazel.
 	// TODO(187086342): handle cflags that are set in VariableFuncs.
-	commonClangGlobalCFlags := append(
-		ClangFilterUnknownCflags(commonGlobalCflags),
+	bazelCommonGlobalCflags := append(
+		commonGlobalCflags,
 		[]string{
-			"${ClangExtraCflags}",
 			// Default to zero initialization.
 			"-ftrivial-auto-var-init=zero",
 			"-enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang",
 		}...)
-	exportedVars.Set("CommonClangGlobalCflags", variableValue(commonClangGlobalCFlags))
+	exportedStringListVars.Set("CommonGlobalCflags", bazelCommonGlobalCflags)
 
-	pctx.VariableFunc("CommonClangGlobalCflags", func(ctx android.PackageVarContext) string {
-		flags := ClangFilterUnknownCflags(commonGlobalCflags)
-		flags = append(flags, "${ClangExtraCflags}")
+	pctx.VariableFunc("CommonGlobalCflags", func(ctx android.PackageVarContext) string {
+		flags := commonGlobalCflags
 
 		// http://b/131390872
 		// Automatically initialize any uninitialized stack variables.
@@ -205,38 +326,37 @@
 		return strings.Join(flags, " ")
 	})
 
-	// Export the static default DeviceClangGlobalCflags to Bazel.
+	// Export the static default DeviceGlobalCflags to Bazel.
 	// TODO(187086342): handle cflags that are set in VariableFuncs.
-	deviceClangGlobalCflags := append(ClangFilterUnknownCflags(deviceGlobalCflags), "${ClangExtraTargetCflags}")
-	exportedVars.Set("DeviceClangGlobalCflags", variableValue(deviceClangGlobalCflags))
+	exportedStringListVars.Set("DeviceGlobalCflags", deviceGlobalCflags)
 
-	pctx.VariableFunc("DeviceClangGlobalCflags", func(ctx android.PackageVarContext) string {
-		if ctx.Config().Fuchsia() {
-			return strings.Join(ClangFilterUnknownCflags(deviceGlobalCflags), " ")
-		} else {
-			return strings.Join(deviceClangGlobalCflags, " ")
-		}
+	pctx.VariableFunc("DeviceGlobalCflags", func(ctx android.PackageVarContext) string {
+		return strings.Join(deviceGlobalCflags, " ")
 	})
 
-	staticVariableExportedToBazel("HostClangGlobalCflags", ClangFilterUnknownCflags(hostGlobalCflags))
-	staticVariableExportedToBazel("NoOverrideClangGlobalCflags", append(ClangFilterUnknownCflags(noOverrideGlobalCflags), "${ClangExtraNoOverrideCflags}"))
-	staticVariableExportedToBazel("CommonClangGlobalCppflags", append(ClangFilterUnknownCflags(commonGlobalCppflags), "${ClangExtraCppflags}"))
-	staticVariableExportedToBazel("ClangExternalCflags", []string{"${ClangExtraExternalCflags}"})
+	exportStringListStaticVariable("HostGlobalCflags", hostGlobalCflags)
+	exportStringListStaticVariable("NoOverrideGlobalCflags", noOverrideGlobalCflags)
+	exportStringListStaticVariable("CommonGlobalCppflags", commonGlobalCppflags)
+	exportStringListStaticVariable("ExternalCflags", extraExternalCflags)
 
 	// Everything in these lists is a crime against abstraction and dependency tracking.
 	// Do not add anything to this list.
-	pctx.PrefixedExistentPathsForSourcesVariable("CommonGlobalIncludes", "-I",
-		[]string{
-			"system/core/include",
-			"system/logging/liblog/include",
-			"system/media/audio/include",
-			"hardware/libhardware/include",
-			"hardware/libhardware_legacy/include",
-			"hardware/ril/include",
-			"frameworks/native/include",
-			"frameworks/native/opengl/include",
-			"frameworks/av/include",
-		})
+	commonGlobalIncludes := []string{
+		"system/core/include",
+		"system/logging/liblog/include",
+		"system/media/audio/include",
+		"hardware/libhardware/include",
+		"hardware/libhardware_legacy/include",
+		"hardware/ril/include",
+		"frameworks/native/include",
+		"frameworks/native/opengl/include",
+		"frameworks/av/include",
+	}
+	exportedStringListVars.Set("CommonGlobalIncludes", commonGlobalIncludes)
+	pctx.PrefixedExistentPathsForSourcesVariable("CommonGlobalIncludes", "-I", commonGlobalIncludes)
+
+	exportStringStaticVariable("CLANG_DEFAULT_VERSION", ClangDefaultVersion)
+	exportStringStaticVariable("CLANG_DEFAULT_SHORT_VERSION", ClangDefaultShortVersion)
 
 	pctx.SourcePathVariable("ClangDefaultBase", ClangDefaultBase)
 	pctx.VariableFunc("ClangBase", func(ctx android.PackageVarContext) string {
@@ -293,12 +413,3 @@
 }
 
 var HostPrebuiltTag = pctx.VariableConfigMethod("HostPrebuiltTag", android.Config.PrebuiltOS)
-
-func envOverrideFunc(envVar, defaultVal string) func(ctx android.PackageVarContext) string {
-	return func(ctx android.PackageVarContext) string {
-		if override := ctx.Config().Getenv(envVar); override != "" {
-			return override
-		}
-		return defaultVal
-	}
-}
diff --git a/cc/config/tidy.go b/cc/config/tidy.go
index c4563e2..cf13503 100644
--- a/cc/config/tidy.go
+++ b/cc/config/tidy.go
@@ -106,6 +106,7 @@
 
 const tidyDefault = "${config.TidyDefaultGlobalChecks}"
 const tidyExternalVendor = "${config.TidyExternalVendorChecks}"
+const tidyDefaultNoAnalyzer = "${config.TidyDefaultGlobalChecks},-clang-analyzer-*"
 
 // This is a map of local path prefixes to the set of default clang-tidy checks
 // to be used.
@@ -139,3 +140,17 @@
 	}
 	return tidyDefault
 }
+
+func TidyFlagsForSrcFile(srcFile android.Path, flags string) string {
+	// Disable clang-analyzer-* checks globally for generated source files
+	// because some of them are too huge. Local .bp files can add wanted
+	// clang-analyzer checks through the tidy_checks property.
+	// Need to do this patch per source file, because some modules
+	// have both generated and organic source files.
+	if _, ok := srcFile.(android.WritablePath); ok {
+		if strings.Contains(flags, tidyDefault) {
+			return strings.ReplaceAll(flags, tidyDefault, tidyDefaultNoAnalyzer)
+		}
+	}
+	return flags
+}
diff --git a/cc/config/toolchain.go b/cc/config/toolchain.go
index fce28c1..20384a8 100644
--- a/cc/config/toolchain.go
+++ b/cc/config/toolchain.go
@@ -82,14 +82,14 @@
 	IncludeFlags() string
 
 	ClangTriple() string
-	ToolchainClangCflags() string
-	ToolchainClangLdflags() string
-	ClangAsflags() string
-	ClangCflags() string
-	ClangCppflags() string
-	ClangLdflags() string
-	ClangLldflags() string
-	ClangInstructionSetFlags(string) (string, error)
+	ToolchainCflags() string
+	ToolchainLdflags() string
+	Asflags() string
+	Cflags() string
+	Cppflags() string
+	Ldflags() string
+	Lldflags() string
+	InstructionSetFlags(string) (string, error)
 
 	ndkTriple() string
 
@@ -106,7 +106,20 @@
 
 	AvailableLibraries() []string
 
+	CrtBeginStaticBinary() []string
+	CrtBeginSharedBinary() []string
+	CrtBeginSharedLibrary() []string
+	CrtEndStaticBinary() []string
+	CrtEndSharedBinary() []string
+	CrtEndSharedLibrary() []string
+
+	// DefaultSharedLibraries returns the list of shared libraries that will be added to all
+	// targets unless they explicitly specify system_shared_libs.
+	DefaultSharedLibraries() []string
+
 	Bionic() bool
+	Glibc() bool
+	Musl() bool
 }
 
 type toolchainBase struct {
@@ -125,18 +138,18 @@
 	return triple
 }
 
-func (toolchainBase) ClangInstructionSetFlags(s string) (string, error) {
+func (toolchainBase) InstructionSetFlags(s string) (string, error) {
 	if s != "" {
 		return "", fmt.Errorf("instruction_set: %s is not a supported instruction set", s)
 	}
 	return "", nil
 }
 
-func (toolchainBase) ToolchainClangCflags() string {
+func (toolchainBase) ToolchainCflags() string {
 	return ""
 }
 
-func (toolchainBase) ToolchainClangLdflags() string {
+func (toolchainBase) ToolchainLdflags() string {
 	return ""
 }
 
@@ -148,7 +161,7 @@
 	return ""
 }
 
-func (toolchainBase) ClangAsflags() string {
+func (toolchainBase) Asflags() string {
 	return ""
 }
 
@@ -165,11 +178,30 @@
 }
 
 func (toolchainBase) AvailableLibraries() []string {
-	return []string{}
+	return nil
+}
+
+func (toolchainBase) CrtBeginStaticBinary() []string  { return nil }
+func (toolchainBase) CrtBeginSharedBinary() []string  { return nil }
+func (toolchainBase) CrtBeginSharedLibrary() []string { return nil }
+func (toolchainBase) CrtEndStaticBinary() []string    { return nil }
+func (toolchainBase) CrtEndSharedBinary() []string    { return nil }
+func (toolchainBase) CrtEndSharedLibrary() []string   { return nil }
+
+func (toolchainBase) DefaultSharedLibraries() []string {
+	return nil
 }
 
 func (toolchainBase) Bionic() bool {
-	return true
+	return false
+}
+
+func (toolchainBase) Glibc() bool {
+	return false
+}
+
+func (toolchainBase) Musl() bool {
+	return false
 }
 
 func (t toolchainBase) ToolPath() string {
diff --git a/cc/config/vndk.go b/cc/config/vndk.go
index 2894f89..1dfdc2d 100644
--- a/cc/config/vndk.go
+++ b/cc/config/vndk.go
@@ -19,55 +19,74 @@
 // has VndkUseCoreVariant set.
 // TODO(b/150578172): clean up unstable and non-versioned aidl module
 var VndkMustUseVendorVariantList = []string{
-	"android.hardware.authsecret-unstable-ndk_platform",
-	"android.hardware.authsecret-ndk_platform",
+	"android.hardware.authsecret-V1-ndk",
 	"android.hardware.authsecret-V1-ndk_platform",
-	"android.hardware.automotive.occupant_awareness-ndk_platform",
+	"android.hardware.authsecret-ndk_platform",
+	"android.hardware.authsecret-unstable-ndk_platform",
+	"android.hardware.automotive.occupant_awareness-V1-ndk",
 	"android.hardware.automotive.occupant_awareness-V1-ndk_platform",
-	"android.hardware.gnss-unstable-ndk_platform",
-	"android.hardware.gnss-ndk_platform",
+	"android.hardware.automotive.occupant_awareness-ndk_platform",
+	"android.hardware.gnss-V1-ndk",
 	"android.hardware.gnss-V1-ndk_platform",
+	"android.hardware.gnss-ndk_platform",
+	"android.hardware.gnss-unstable-ndk_platform",
+	"android.hardware.health.storage-V1-ndk",
 	"android.hardware.health.storage-V1-ndk_platform",
 	"android.hardware.health.storage-ndk_platform",
 	"android.hardware.health.storage-unstable-ndk_platform",
-	"android.hardware.light-V1-ndk_platform",
-	"android.hardware.light-ndk_platform",
 	"android.hardware.identity-V2-ndk_platform",
+	"android.hardware.identity-V3-ndk",
 	"android.hardware.identity-V3-ndk_platform",
 	"android.hardware.identity-ndk_platform",
-	"android.hardware.nfc@1.2",
+	"android.hardware.light-V1-ndk",
+	"android.hardware.light-V1-ndk_platform",
+	"android.hardware.light-ndk_platform",
+	"android.hardware.memtrack-V1-ndk",
 	"android.hardware.memtrack-V1-ndk_platform",
 	"android.hardware.memtrack-ndk_platform",
 	"android.hardware.memtrack-unstable-ndk_platform",
+	"android.hardware.nfc@1.2",
+	"android.hardware.oemlock-V1-ndk",
 	"android.hardware.oemlock-V1-ndk_platform",
 	"android.hardware.oemlock-ndk_platform",
 	"android.hardware.oemlock-unstable-ndk_platform",
 	"android.hardware.power-V1-ndk_platform",
+	"android.hardware.power-V2-ndk",
 	"android.hardware.power-V2-ndk_platform",
 	"android.hardware.power-ndk_platform",
+	"android.hardware.power.stats-V1-ndk",
 	"android.hardware.power.stats-V1-ndk_platform",
 	"android.hardware.power.stats-ndk_platform",
 	"android.hardware.power.stats-unstable-ndk_platform",
+	"android.hardware.rebootescrow-V1-ndk",
 	"android.hardware.rebootescrow-V1-ndk_platform",
 	"android.hardware.rebootescrow-ndk_platform",
+	"android.hardware.security.keymint-V1-ndk",
 	"android.hardware.security.keymint-V1-ndk_platform",
 	"android.hardware.security.keymint-ndk_platform",
 	"android.hardware.security.keymint-unstable-ndk_platform",
+	"android.hardware.security.secureclock-V1-ndk",
 	"android.hardware.security.secureclock-V1-ndk_platform",
-	"android.hardware.security.secureclock-unstable-ndk_platform",
 	"android.hardware.security.secureclock-ndk_platform",
+	"android.hardware.security.secureclock-unstable-ndk_platform",
+	"android.hardware.security.sharedsecret-V1-ndk",
 	"android.hardware.security.sharedsecret-V1-ndk_platform",
 	"android.hardware.security.sharedsecret-ndk_platform",
 	"android.hardware.security.sharedsecret-unstable-ndk_platform",
 	"android.hardware.vibrator-V1-ndk_platform",
+	"android.hardware.vibrator-V2-ndk",
 	"android.hardware.vibrator-V2-ndk_platform",
 	"android.hardware.vibrator-ndk_platform",
+	"android.hardware.weaver-V1-ndk",
 	"android.hardware.weaver-V1-ndk_platform",
 	"android.hardware.weaver-ndk_platform",
 	"android.hardware.weaver-unstable-ndk_platform",
+	"android.system.suspend-V1-ndk",
+	"android.system.keystore2-V1-ndk",
 	"android.system.keystore2-V1-ndk_platform",
 	"android.system.keystore2-ndk_platform",
 	"android.system.keystore2-unstable-ndk_platform",
+	"android.system.suspend-V1-ndk_platform",
 	"libbinder",
 	"libcrypto",
 	"libexpat",
diff --git a/cc/config/x86_64_device.go b/cc/config/x86_64_device.go
index 1e25a3b..00f07ff 100644
--- a/cc/config/x86_64_device.go
+++ b/cc/config/x86_64_device.go
@@ -32,8 +32,6 @@
 		"-Wl,--hash-style=gnu",
 	}
 
-	x86_64Lldflags = ClangFilterUnknownLldflags(x86_64Ldflags)
-
 	x86_64ArchVariantCflags = map[string][]string{
 		"": []string{
 			"-march=x86-64",
@@ -79,6 +77,14 @@
 		"popcnt": []string{"-mpopcnt"},
 		"aes_ni": []string{"-maes"},
 	}
+
+	x86_64DefaultArchVariantFeatures = []string{
+		"ssse3",
+		"sse4",
+		"sse4_1",
+		"sse4_2",
+		"popcnt",
+	}
 )
 
 const (
@@ -86,45 +92,45 @@
 )
 
 func init() {
-	android.RegisterDefaultArchVariantFeatures(android.Android, android.X86_64,
-		"ssse3",
-		"sse4",
-		"sse4_1",
-		"sse4_2",
-		"popcnt")
+	android.RegisterDefaultArchVariantFeatures(android.Android, android.X86_64, x86_64DefaultArchVariantFeatures...)
+	exportedStringListVars.Set("X86_64DefaultArchVariantFeatures", x86_64DefaultArchVariantFeatures)
 
 	pctx.StaticVariable("x86_64GccVersion", x86_64GccVersion)
 
 	pctx.SourcePathVariable("X86_64GccRoot",
 		"prebuilts/gcc/${HostPrebuiltTag}/x86/x86_64-linux-android-${x86_64GccVersion}")
 
-	pctx.StaticVariable("X86_64ToolchainCflags", "-m64")
-	pctx.StaticVariable("X86_64ToolchainLdflags", "-m64")
+	exportStringListStaticVariable("X86_64ToolchainCflags", []string{"-m64"})
+	exportStringListStaticVariable("X86_64ToolchainLdflags", []string{"-m64"})
 
-	pctx.StaticVariable("X86_64Ldflags", strings.Join(x86_64Ldflags, " "))
-	pctx.StaticVariable("X86_64Lldflags", strings.Join(x86_64Lldflags, " "))
+	exportStringListStaticVariable("X86_64Ldflags", x86_64Ldflags)
+	exportStringListStaticVariable("X86_64Lldflags", x86_64Ldflags)
 
 	// Clang cflags
-	pctx.StaticVariable("X86_64ClangCflags", strings.Join(ClangFilterUnknownCflags(x86_64Cflags), " "))
-	pctx.StaticVariable("X86_64ClangLdflags", strings.Join(ClangFilterUnknownCflags(x86_64Ldflags), " "))
-	pctx.StaticVariable("X86_64ClangLldflags", strings.Join(ClangFilterUnknownCflags(x86_64Lldflags), " "))
-	pctx.StaticVariable("X86_64ClangCppflags", strings.Join(ClangFilterUnknownCflags(x86_64Cppflags), " "))
+	exportStringListStaticVariable("X86_64Cflags", x86_64Cflags)
+	exportStringListStaticVariable("X86_64Cppflags", x86_64Cppflags)
 
 	// Yasm flags
-	pctx.StaticVariable("X86_64YasmFlags", "-f elf64 -m amd64")
+	exportStringListStaticVariable("X86_64YasmFlags", []string{
+		"-f elf64",
+		"-m amd64",
+	})
 
 	// Extended cflags
 
+	exportedStringListDictVars.Set("X86_64ArchVariantCflags", x86_64ArchVariantCflags)
+	exportedStringListDictVars.Set("X86_64ArchFeatureCflags", x86_64ArchFeatureCflags)
+
 	// Architecture variant cflags
 	for variant, cflags := range x86_64ArchVariantCflags {
-		pctx.StaticVariable("X86_64"+variant+"VariantClangCflags",
-			strings.Join(ClangFilterUnknownCflags(cflags), " "))
+		pctx.StaticVariable("X86_64"+variant+"VariantCflags", strings.Join(cflags, " "))
 	}
 }
 
 type toolchainX86_64 struct {
+	toolchainBionic
 	toolchain64Bit
-	toolchainClangCflags string
+	toolchainCflags string
 }
 
 func (t *toolchainX86_64) Name() string {
@@ -151,27 +157,27 @@
 	return t.GccTriple()
 }
 
-func (t *toolchainX86_64) ToolchainClangLdflags() string {
+func (t *toolchainX86_64) ToolchainLdflags() string {
 	return "${config.X86_64ToolchainLdflags}"
 }
 
-func (t *toolchainX86_64) ToolchainClangCflags() string {
-	return t.toolchainClangCflags
+func (t *toolchainX86_64) ToolchainCflags() string {
+	return t.toolchainCflags
 }
 
-func (t *toolchainX86_64) ClangCflags() string {
-	return "${config.X86_64ClangCflags}"
+func (t *toolchainX86_64) Cflags() string {
+	return "${config.X86_64Cflags}"
 }
 
-func (t *toolchainX86_64) ClangCppflags() string {
-	return "${config.X86_64ClangCppflags}"
+func (t *toolchainX86_64) Cppflags() string {
+	return "${config.X86_64Cppflags}"
 }
 
-func (t *toolchainX86_64) ClangLdflags() string {
+func (t *toolchainX86_64) Ldflags() string {
 	return "${config.X86_64Ldflags}"
 }
 
-func (t *toolchainX86_64) ClangLldflags() string {
+func (t *toolchainX86_64) Lldflags() string {
 	return "${config.X86_64Lldflags}"
 }
 
@@ -184,17 +190,17 @@
 }
 
 func x86_64ToolchainFactory(arch android.Arch) Toolchain {
-	toolchainClangCflags := []string{
+	toolchainCflags := []string{
 		"${config.X86_64ToolchainCflags}",
-		"${config.X86_64" + arch.ArchVariant + "VariantClangCflags}",
+		"${config.X86_64" + arch.ArchVariant + "VariantCflags}",
 	}
 
 	for _, feature := range arch.ArchFeatures {
-		toolchainClangCflags = append(toolchainClangCflags, x86_64ArchFeatureCflags[feature]...)
+		toolchainCflags = append(toolchainCflags, x86_64ArchFeatureCflags[feature]...)
 	}
 
 	return &toolchainX86_64{
-		toolchainClangCflags: strings.Join(toolchainClangCflags, " "),
+		toolchainCflags: strings.Join(toolchainCflags, " "),
 	}
 }
 
diff --git a/cc/config/x86_64_fuchsia_device.go b/cc/config/x86_64_fuchsia_device.go
deleted file mode 100644
index 0f2013b..0000000
--- a/cc/config/x86_64_fuchsia_device.go
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright 2018 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package config
-
-import (
-	"android/soong/android"
-)
-
-var fuchsiaSysRoot string = "prebuilts/fuchsia_sdk/arch/x64/sysroot"
-var fuchsiaPrebuiltLibsRoot string = "fuchsia/prebuilt_libs"
-
-type toolchainFuchsia struct {
-	cFlags, ldFlags string
-}
-
-type toolchainFuchsiaX8664 struct {
-	toolchain64Bit
-	toolchainFuchsia
-}
-
-func (t *toolchainFuchsiaX8664) Name() string {
-	return "x86_64"
-}
-
-func (t *toolchainFuchsiaX8664) GccRoot() string {
-	return "${config.X86_64GccRoot}"
-}
-
-func (t *toolchainFuchsiaX8664) GccTriple() string {
-	return "x86_64-linux-android"
-}
-
-func (t *toolchainFuchsiaX8664) GccVersion() string {
-	return x86_64GccVersion
-}
-
-func (t *toolchainFuchsiaX8664) Cflags() string {
-	return ""
-}
-
-func (t *toolchainFuchsiaX8664) Cppflags() string {
-	return ""
-}
-
-func (t *toolchainFuchsiaX8664) Ldflags() string {
-	return ""
-}
-
-func (t *toolchainFuchsiaX8664) IncludeFlags() string {
-	return ""
-}
-
-func (t *toolchainFuchsiaX8664) ClangTriple() string {
-	return "x86_64-fuchsia-android"
-}
-
-func (t *toolchainFuchsiaX8664) ClangCppflags() string {
-	return "-Wno-error=deprecated-declarations"
-}
-
-func (t *toolchainFuchsiaX8664) ClangLdflags() string {
-	return "--target=x86_64-fuchsia --sysroot=" + fuchsiaSysRoot + " -L" + fuchsiaPrebuiltLibsRoot + "/x86_64-fuchsia/lib " + "-Lprebuilts/fuchsia_sdk/arch/x64/dist/"
-
-}
-
-func (t *toolchainFuchsiaX8664) ClangLldflags() string {
-	return "--target=x86_64-fuchsia --sysroot=" + fuchsiaSysRoot + " -L" + fuchsiaPrebuiltLibsRoot + "/x86_64-fuchsia/lib " + "-Lprebuilts/fuchsia_sdk/arch/x64/dist/"
-}
-
-func (t *toolchainFuchsiaX8664) ClangCflags() string {
-	return "--target=x86_64-fuchsia --sysroot=" + fuchsiaSysRoot + " -I" + fuchsiaSysRoot + "/include"
-}
-
-func (t *toolchainFuchsiaX8664) Bionic() bool {
-	return false
-}
-
-func (t *toolchainFuchsiaX8664) YasmFlags() string {
-	return "-f elf64 -m amd64"
-}
-
-func (t *toolchainFuchsiaX8664) ToolchainClangCflags() string {
-	return "-mssse3"
-}
-
-var toolchainFuchsiaSingleton Toolchain = &toolchainFuchsiaX8664{}
-
-func fuchsiaToolchainFactory(arch android.Arch) Toolchain {
-	return toolchainFuchsiaSingleton
-}
-
-func init() {
-	registerToolchainFactory(android.Fuchsia, android.X86_64, fuchsiaToolchainFactory)
-}
diff --git a/cc/config/x86_darwin_host.go b/cc/config/x86_darwin_host.go
index b0344af..0bb1a81 100644
--- a/cc/config/x86_darwin_host.go
+++ b/cc/config/x86_darwin_host.go
@@ -26,8 +26,6 @@
 
 var (
 	darwinCflags = []string{
-		"-fdiagnostics-color",
-
 		"-fPIC",
 		"-funwind-tables",
 
@@ -41,6 +39,9 @@
 		"-DMACOSX_DEPLOYMENT_TARGET=${macMinVersion}",
 
 		"-m64",
+
+		"-integrated-as",
+		"-fstack-protector-strong",
 	}
 
 	darwinLdflags = []string{
@@ -48,26 +49,16 @@
 		"-Wl,-syslibroot,${macSdkRoot}",
 		"-mmacosx-version-min=${macMinVersion}",
 		"-m64",
+		"-mlinker-version=305",
 	}
 
-	darwinClangCflags = append(ClangFilterUnknownCflags(darwinCflags), []string{
-		"-integrated-as",
-		"-fstack-protector-strong",
-	}...)
-
-	darwinClangLdflags = ClangFilterUnknownCflags(darwinLdflags)
-
-	darwinClangLldflags = ClangFilterUnknownLldflags(darwinClangLdflags)
-
 	darwinSupportedSdkVersions = []string{
-		"10.10",
-		"10.11",
-		"10.12",
 		"10.13",
 		"10.14",
 		"10.15",
 		"11.0",
 		"11.1",
+		"11.3",
 	}
 
 	darwinAvailableLibraries = append(
@@ -96,7 +87,7 @@
 	pctx.VariableFunc("macSdkRoot", func(ctx android.PackageVarContext) string {
 		return getMacTools(ctx).sdkRoot
 	})
-	pctx.StaticVariable("macMinVersion", "10.10")
+	pctx.StaticVariable("macMinVersion", "10.13")
 	pctx.VariableFunc("MacArPath", func(ctx android.PackageVarContext) string {
 		return getMacTools(ctx).arPath
 	})
@@ -115,9 +106,9 @@
 
 	pctx.StaticVariable("DarwinGccTriple", "i686-apple-darwin11")
 
-	pctx.StaticVariable("DarwinClangCflags", strings.Join(darwinClangCflags, " "))
-	pctx.StaticVariable("DarwinClangLdflags", strings.Join(darwinClangLdflags, " "))
-	pctx.StaticVariable("DarwinClangLldflags", strings.Join(darwinClangLldflags, " "))
+	pctx.StaticVariable("DarwinCflags", strings.Join(darwinCflags, " "))
+	pctx.StaticVariable("DarwinLdflags", strings.Join(darwinLdflags, " "))
+	pctx.StaticVariable("DarwinLldflags", strings.Join(darwinLdflags, " "))
 
 	pctx.StaticVariable("DarwinYasmFlags", "-f macho -m amd64")
 }
@@ -213,20 +204,20 @@
 	return "x86_64-apple-darwin"
 }
 
-func (t *toolchainDarwin) ClangCflags() string {
-	return "${config.DarwinClangCflags}"
+func (t *toolchainDarwin) Cflags() string {
+	return "${config.DarwinCflags}"
 }
 
-func (t *toolchainDarwin) ClangCppflags() string {
+func (t *toolchainDarwin) Cppflags() string {
 	return ""
 }
 
-func (t *toolchainDarwin) ClangLdflags() string {
-	return "${config.DarwinClangLdflags}"
+func (t *toolchainDarwin) Ldflags() string {
+	return "${config.DarwinLdflags}"
 }
 
-func (t *toolchainDarwin) ClangLldflags() string {
-	return "${config.DarwinClangLldflags}"
+func (t *toolchainDarwin) Lldflags() string {
+	return "${config.DarwinLldflags}"
 }
 
 func (t *toolchainDarwin) YasmFlags() string {
@@ -241,10 +232,6 @@
 	return darwinAvailableLibraries
 }
 
-func (t *toolchainDarwin) Bionic() bool {
-	return false
-}
-
 func (t *toolchainDarwin) ToolPath() string {
 	return "${config.MacToolPath}"
 }
diff --git a/cc/config/x86_device.go b/cc/config/x86_device.go
index fe83098..29f0593 100644
--- a/cc/config/x86_device.go
+++ b/cc/config/x86_device.go
@@ -21,16 +21,14 @@
 )
 
 var (
-	x86Cflags = []string{}
-
-	x86ClangCflags = append(x86Cflags, []string{
+	x86Cflags = []string{
 		"-msse3",
 
 		// -mstackrealign is needed to realign stack in native code
 		// that could be called from JNI, so that movaps instruction
 		// will work on assumed stack aligned local variables.
 		"-mstackrealign",
-	}...)
+	}
 
 	x86Cppflags = []string{}
 
@@ -38,8 +36,6 @@
 		"-Wl,--hash-style=gnu",
 	}
 
-	x86Lldflags = ClangFilterUnknownLldflags(x86Ldflags)
-
 	x86ArchVariantCflags = map[string][]string{
 		"": []string{
 			"-march=prescott",
@@ -49,35 +45,27 @@
 		},
 		"atom": []string{
 			"-march=atom",
-			"-mfpmath=sse",
 		},
 		"broadwell": []string{
 			"-march=broadwell",
-			"-mfpmath=sse",
 		},
 		"haswell": []string{
 			"-march=core-avx2",
-			"-mfpmath=sse",
 		},
 		"ivybridge": []string{
 			"-march=core-avx-i",
-			"-mfpmath=sse",
 		},
 		"sandybridge": []string{
 			"-march=corei7",
-			"-mfpmath=sse",
 		},
 		"silvermont": []string{
 			"-march=slm",
-			"-mfpmath=sse",
 		},
 		"skylake": []string{
 			"-march=skylake",
-			"-mfpmath=sse",
 		},
 		"stoneyridge": []string{
 			"-march=bdver4",
-			"-mfpmath=sse",
 		},
 	}
 
@@ -109,33 +97,36 @@
 	pctx.SourcePathVariable("X86GccRoot",
 		"prebuilts/gcc/${HostPrebuiltTag}/x86/x86_64-linux-android-${x86GccVersion}")
 
-	pctx.StaticVariable("X86ToolchainCflags", "-m32")
-	pctx.StaticVariable("X86ToolchainLdflags", "-m32")
+	exportStringListStaticVariable("X86ToolchainCflags", []string{"-m32"})
+	exportStringListStaticVariable("X86ToolchainLdflags", []string{"-m32"})
 
-	pctx.StaticVariable("X86Ldflags", strings.Join(x86Ldflags, " "))
-	pctx.StaticVariable("X86Lldflags", strings.Join(x86Lldflags, " "))
+	exportStringListStaticVariable("X86Ldflags", x86Ldflags)
+	exportStringListStaticVariable("X86Lldflags", x86Ldflags)
 
 	// Clang cflags
-	pctx.StaticVariable("X86ClangCflags", strings.Join(ClangFilterUnknownCflags(x86ClangCflags), " "))
-	pctx.StaticVariable("X86ClangLdflags", strings.Join(ClangFilterUnknownCflags(x86Ldflags), " "))
-	pctx.StaticVariable("X86ClangLldflags", strings.Join(ClangFilterUnknownCflags(x86Lldflags), " "))
-	pctx.StaticVariable("X86ClangCppflags", strings.Join(ClangFilterUnknownCflags(x86Cppflags), " "))
+	exportStringListStaticVariable("X86Cflags", x86Cflags)
+	exportStringListStaticVariable("X86Cppflags", x86Cppflags)
 
 	// Yasm flags
-	pctx.StaticVariable("X86YasmFlags", "-f elf32 -m x86")
+	exportStringListStaticVariable("X86YasmFlags", []string{
+		"-f elf32",
+		"-m x86",
+	})
 
 	// Extended cflags
+	exportedStringListDictVars.Set("X86ArchVariantCflags", x86ArchVariantCflags)
+	exportedStringListDictVars.Set("X86ArchFeatureCflags", x86ArchFeatureCflags)
 
 	// Architecture variant cflags
 	for variant, cflags := range x86ArchVariantCflags {
-		pctx.StaticVariable("X86"+variant+"VariantClangCflags",
-			strings.Join(ClangFilterUnknownCflags(cflags), " "))
+		pctx.StaticVariable("X86"+variant+"VariantCflags", strings.Join(cflags, " "))
 	}
 }
 
 type toolchainX86 struct {
+	toolchainBionic
 	toolchain32Bit
-	toolchainClangCflags string
+	toolchainCflags string
 }
 
 func (t *toolchainX86) Name() string {
@@ -162,27 +153,27 @@
 	return "i686-linux-android"
 }
 
-func (t *toolchainX86) ToolchainClangLdflags() string {
+func (t *toolchainX86) ToolchainLdflags() string {
 	return "${config.X86ToolchainLdflags}"
 }
 
-func (t *toolchainX86) ToolchainClangCflags() string {
-	return t.toolchainClangCflags
+func (t *toolchainX86) ToolchainCflags() string {
+	return t.toolchainCflags
 }
 
-func (t *toolchainX86) ClangCflags() string {
-	return "${config.X86ClangCflags}"
+func (t *toolchainX86) Cflags() string {
+	return "${config.X86Cflags}"
 }
 
-func (t *toolchainX86) ClangCppflags() string {
-	return "${config.X86ClangCppflags}"
+func (t *toolchainX86) Cppflags() string {
+	return "${config.X86Cppflags}"
 }
 
-func (t *toolchainX86) ClangLdflags() string {
+func (t *toolchainX86) Ldflags() string {
 	return "${config.X86Ldflags}"
 }
 
-func (t *toolchainX86) ClangLldflags() string {
+func (t *toolchainX86) Lldflags() string {
 	return "${config.X86Lldflags}"
 }
 
@@ -195,17 +186,17 @@
 }
 
 func x86ToolchainFactory(arch android.Arch) Toolchain {
-	toolchainClangCflags := []string{
+	toolchainCflags := []string{
 		"${config.X86ToolchainCflags}",
-		"${config.X86" + arch.ArchVariant + "VariantClangCflags}",
+		"${config.X86" + arch.ArchVariant + "VariantCflags}",
 	}
 
 	for _, feature := range arch.ArchFeatures {
-		toolchainClangCflags = append(toolchainClangCflags, x86ArchFeatureCflags[feature]...)
+		toolchainCflags = append(toolchainCflags, x86ArchFeatureCflags[feature]...)
 	}
 
 	return &toolchainX86{
-		toolchainClangCflags: strings.Join(toolchainClangCflags, " "),
+		toolchainCflags: strings.Join(toolchainCflags, " "),
 	}
 }
 
diff --git a/cc/config/x86_linux_bionic_host.go b/cc/config/x86_linux_bionic_host.go
index fa625e3..4b7ba6a 100644
--- a/cc/config/x86_linux_bionic_host.go
+++ b/cc/config/x86_linux_bionic_host.go
@@ -21,9 +21,7 @@
 )
 
 var (
-	linuxBionicCflags = ClangFilterUnknownCflags([]string{
-		"-fdiagnostics-color",
-
+	linuxBionicCflags = []string{
 		"-Wa,--noexecstack",
 
 		"-fPIC",
@@ -34,21 +32,17 @@
 
 		// From x86_64_device
 		"-ffunction-sections",
-		"-finline-functions",
-		"-finline-limit=300",
 		"-fno-short-enums",
-		"-funswitch-loops",
 		"-funwind-tables",
-		"-fno-canonical-system-headers",
 
 		// Tell clang where the gcc toolchain is
 		"--gcc-toolchain=${LinuxBionicGccRoot}",
 
 		// This is normally in ClangExtraTargetCflags, but this is considered host
 		"-nostdlibinc",
-	})
+	}
 
-	linuxBionicLdflags = ClangFilterUnknownCflags([]string{
+	linuxBionicLdflags = []string{
 		"-Wl,-z,noexecstack",
 		"-Wl,-z,relro",
 		"-Wl,-z,now",
@@ -60,15 +54,23 @@
 
 		// Use the device gcc toolchain
 		"--gcc-toolchain=${LinuxBionicGccRoot}",
-	})
+	}
 
-	linuxBionicLldflags = ClangFilterUnknownLldflags(linuxBionicLdflags)
+	// Embed the linker into host bionic binaries. This is needed to support host bionic,
+	// as the linux kernel requires that the ELF interpreter referenced by PT_INTERP be
+	// either an absolute path, or relative from CWD. To work around this, we extract
+	// the load sections from the runtime linker ELF binary and embed them into each host
+	// bionic binary, omitting the PT_INTERP declaration. The kernel will treat it as a static
+	// binary, and then we use a special entry point to fix up the arguments passed by
+	// the kernel before jumping to the embedded linker.
+	linuxBionicCrtBeginSharedBinary = append(android.CopyOf(bionicCrtBeginSharedBinary),
+		"host_bionic_linker_script")
 )
 
 func init() {
 	pctx.StaticVariable("LinuxBionicCflags", strings.Join(linuxBionicCflags, " "))
 	pctx.StaticVariable("LinuxBionicLdflags", strings.Join(linuxBionicLdflags, " "))
-	pctx.StaticVariable("LinuxBionicLldflags", strings.Join(linuxBionicLldflags, " "))
+	pctx.StaticVariable("LinuxBionicLldflags", strings.Join(linuxBionicLdflags, " "))
 
 	// Use the device gcc toolchain for now
 	pctx.StaticVariable("LinuxBionicGccRoot", "${X86_64GccRoot}")
@@ -76,6 +78,7 @@
 
 type toolchainLinuxBionic struct {
 	toolchain64Bit
+	toolchainBionic
 }
 
 func (t *toolchainLinuxBionic) Name() string {
@@ -103,29 +106,29 @@
 	return "x86_64-linux-android"
 }
 
-func (t *toolchainLinuxBionic) ClangCflags() string {
+func (t *toolchainLinuxBionic) Cflags() string {
 	return "${config.LinuxBionicCflags}"
 }
 
-func (t *toolchainLinuxBionic) ClangCppflags() string {
+func (t *toolchainLinuxBionic) Cppflags() string {
 	return ""
 }
 
-func (t *toolchainLinuxBionic) ClangLdflags() string {
+func (t *toolchainLinuxBionic) Ldflags() string {
 	return "${config.LinuxBionicLdflags}"
 }
 
-func (t *toolchainLinuxBionic) ClangLldflags() string {
+func (t *toolchainLinuxBionic) Lldflags() string {
 	return "${config.LinuxBionicLldflags}"
 }
 
-func (t *toolchainLinuxBionic) ToolchainClangCflags() string {
+func (t *toolchainLinuxBionic) ToolchainCflags() string {
 	return "-m64 -march=x86-64" +
 		// TODO: We're not really android, but we don't have a triple yet b/31393676
 		" -U__ANDROID__"
 }
 
-func (t *toolchainLinuxBionic) ToolchainClangLdflags() string {
+func (t *toolchainLinuxBionic) ToolchainLdflags() string {
 	return "-m64"
 }
 
@@ -133,14 +136,14 @@
 	return nil
 }
 
-func (t *toolchainLinuxBionic) Bionic() bool {
-	return true
-}
-
 func (toolchainLinuxBionic) LibclangRuntimeLibraryArch() string {
 	return "x86_64"
 }
 
+func (toolchainLinuxBionic) CrtBeginSharedBinary() []string {
+	return linuxBionicCrtBeginSharedBinary
+}
+
 var toolchainLinuxBionicSingleton Toolchain = &toolchainLinuxBionic{}
 
 func linuxBionicToolchainFactory(arch android.Arch) Toolchain {
diff --git a/cc/config/x86_linux_host.go b/cc/config/x86_linux_host.go
index 13b5511..ac5d5f7 100644
--- a/cc/config/x86_linux_host.go
+++ b/cc/config/x86_linux_host.go
@@ -22,8 +22,6 @@
 
 var (
 	linuxCflags = []string{
-		"-fdiagnostics-color",
-
 		"-Wa,--noexecstack",
 
 		"-fPIC",
@@ -36,6 +34,19 @@
 		//See bug 12708004.
 		"-D__STDC_FORMAT_MACROS",
 		"-D__STDC_CONSTANT_MACROS",
+
+		"--gcc-toolchain=${LinuxGccRoot}",
+		"-fstack-protector-strong",
+	}
+
+	linuxGlibcCflags = []string{
+		"--sysroot ${LinuxGccRoot}/sysroot",
+	}
+
+	linuxMuslCflags = []string{
+		"-D_LIBCPP_HAS_MUSL_LIBC",
+		"-DANDROID_HOST_MUSL",
+		"-nostdlibinc",
 	}
 
 	linuxLdflags = []string{
@@ -43,12 +54,22 @@
 		"-Wl,-z,relro",
 		"-Wl,-z,now",
 		"-Wl,--no-undefined-version",
+
+		"--gcc-toolchain=${LinuxGccRoot}",
+	}
+
+	linuxGlibcLdflags = []string{
+		"--sysroot ${LinuxGccRoot}/sysroot",
+	}
+
+	linuxMuslLdflags = []string{
+		"-nostdlib",
+		"-lgcc", "-lgcc_eh",
 	}
 
 	// Extended cflags
 	linuxX86Cflags = []string{
 		"-msse3",
-		"-mfpmath=sse",
 		"-m32",
 		"-march=prescott",
 		"-D_FILE_OFFSET_BITS=64",
@@ -61,40 +82,17 @@
 
 	linuxX86Ldflags = []string{
 		"-m32",
+		"-B${LinuxGccRoot}/lib/gcc/${LinuxGccTriple}/${LinuxGccVersion}/32",
+		"-L${LinuxGccRoot}/lib/gcc/${LinuxGccTriple}/${LinuxGccVersion}/32",
+		"-L${LinuxGccRoot}/${LinuxGccTriple}/lib32",
 	}
 
 	linuxX8664Ldflags = []string{
 		"-m64",
-	}
-
-	linuxClangCflags = append(ClangFilterUnknownCflags(linuxCflags), []string{
-		"--gcc-toolchain=${LinuxGccRoot}",
-		"--sysroot ${LinuxGccRoot}/sysroot",
-		"-fstack-protector-strong",
-	}...)
-
-	linuxClangLdflags = append(ClangFilterUnknownCflags(linuxLdflags), []string{
-		"--gcc-toolchain=${LinuxGccRoot}",
-		"--sysroot ${LinuxGccRoot}/sysroot",
-	}...)
-
-	linuxClangLldflags = ClangFilterUnknownLldflags(linuxClangLdflags)
-
-	linuxX86ClangLdflags = append(ClangFilterUnknownCflags(linuxX86Ldflags), []string{
-		"-B${LinuxGccRoot}/lib/gcc/${LinuxGccTriple}/${LinuxGccVersion}/32",
-		"-L${LinuxGccRoot}/lib/gcc/${LinuxGccTriple}/${LinuxGccVersion}/32",
-		"-L${LinuxGccRoot}/${LinuxGccTriple}/lib32",
-	}...)
-
-	linuxX86ClangLldflags = ClangFilterUnknownLldflags(linuxX86ClangLdflags)
-
-	linuxX8664ClangLdflags = append(ClangFilterUnknownCflags(linuxX8664Ldflags), []string{
 		"-B${LinuxGccRoot}/lib/gcc/${LinuxGccTriple}/${LinuxGccVersion}",
 		"-L${LinuxGccRoot}/lib/gcc/${LinuxGccTriple}/${LinuxGccVersion}",
 		"-L${LinuxGccRoot}/${LinuxGccTriple}/lib64",
-	}...)
-
-	linuxX8664ClangLldflags = ClangFilterUnknownLldflags(linuxX8664ClangLdflags)
+	}
 
 	linuxAvailableLibraries = addPrefix([]string{
 		"c",
@@ -108,6 +106,12 @@
 		"rt",
 		"util",
 	}, "-l")
+
+	muslCrtBeginStaticBinary, muslCrtEndStaticBinary   = []string{"libc_musl_crtbegin_static"}, []string{"libc_musl_crtend"}
+	muslCrtBeginSharedBinary, muslCrtEndSharedBinary   = []string{"libc_musl_crtbegin_dynamic", "musl_linker_script"}, []string{"libc_musl_crtend"}
+	muslCrtBeginSharedLibrary, muslCrtEndSharedLibrary = []string{"libc_musl_crtbegin_so"}, []string{"libc_musl_crtend_so"}
+
+	muslDefaultSharedLibraries = []string{"libc_musl"}
 )
 
 const (
@@ -130,21 +134,26 @@
 
 	pctx.StaticVariable("LinuxGccTriple", "x86_64-linux")
 
-	pctx.StaticVariable("LinuxClangCflags", strings.Join(linuxClangCflags, " "))
-	pctx.StaticVariable("LinuxClangLdflags", strings.Join(linuxClangLdflags, " "))
-	pctx.StaticVariable("LinuxClangLldflags", strings.Join(linuxClangLldflags, " "))
+	pctx.StaticVariable("LinuxCflags", strings.Join(linuxCflags, " "))
+	pctx.StaticVariable("LinuxLdflags", strings.Join(linuxLdflags, " "))
+	pctx.StaticVariable("LinuxLldflags", strings.Join(linuxLdflags, " "))
 
-	pctx.StaticVariable("LinuxX86ClangCflags",
-		strings.Join(ClangFilterUnknownCflags(linuxX86Cflags), " "))
-	pctx.StaticVariable("LinuxX8664ClangCflags",
-		strings.Join(ClangFilterUnknownCflags(linuxX8664Cflags), " "))
-	pctx.StaticVariable("LinuxX86ClangLdflags", strings.Join(linuxX86ClangLdflags, " "))
-	pctx.StaticVariable("LinuxX86ClangLldflags", strings.Join(linuxX86ClangLldflags, " "))
-	pctx.StaticVariable("LinuxX8664ClangLdflags", strings.Join(linuxX8664ClangLdflags, " "))
-	pctx.StaticVariable("LinuxX8664ClangLldflags", strings.Join(linuxX8664ClangLldflags, " "))
+	pctx.StaticVariable("LinuxX86Cflags", strings.Join(linuxX86Cflags, " "))
+	pctx.StaticVariable("LinuxX8664Cflags", strings.Join(linuxX8664Cflags, " "))
+	pctx.StaticVariable("LinuxX86Ldflags", strings.Join(linuxX86Ldflags, " "))
+	pctx.StaticVariable("LinuxX86Lldflags", strings.Join(linuxX86Ldflags, " "))
+	pctx.StaticVariable("LinuxX8664Ldflags", strings.Join(linuxX8664Ldflags, " "))
+	pctx.StaticVariable("LinuxX8664Lldflags", strings.Join(linuxX8664Ldflags, " "))
 	// Yasm flags
 	pctx.StaticVariable("LinuxX86YasmFlags", "-f elf32 -m x86")
 	pctx.StaticVariable("LinuxX8664YasmFlags", "-f elf64 -m amd64")
+
+	pctx.StaticVariable("LinuxGlibcCflags", strings.Join(linuxGlibcCflags, " "))
+	pctx.StaticVariable("LinuxGlibcLdflags", strings.Join(linuxGlibcLdflags, " "))
+	pctx.StaticVariable("LinuxGlibcLldflags", strings.Join(linuxGlibcLdflags, " "))
+	pctx.StaticVariable("LinuxMuslCflags", strings.Join(linuxMuslCflags, " "))
+	pctx.StaticVariable("LinuxMuslLdflags", strings.Join(linuxMuslLdflags, " "))
+	pctx.StaticVariable("LinuxMuslLldflags", strings.Join(linuxMuslLdflags, " "))
 }
 
 type toolchainLinux struct {
@@ -189,11 +198,11 @@
 	return "i686-linux-gnu"
 }
 
-func (t *toolchainLinuxX86) ClangCflags() string {
-	return "${config.LinuxClangCflags} ${config.LinuxX86ClangCflags}"
+func (t *toolchainLinuxX86) Cflags() string {
+	return "${config.LinuxCflags} ${config.LinuxX86Cflags}"
 }
 
-func (t *toolchainLinuxX86) ClangCppflags() string {
+func (t *toolchainLinuxX86) Cppflags() string {
 	return ""
 }
 
@@ -201,28 +210,28 @@
 	return "x86_64-linux-gnu"
 }
 
-func (t *toolchainLinuxX8664) ClangCflags() string {
-	return "${config.LinuxClangCflags} ${config.LinuxX8664ClangCflags}"
+func (t *toolchainLinuxX8664) Cflags() string {
+	return "${config.LinuxCflags} ${config.LinuxX8664Cflags}"
 }
 
-func (t *toolchainLinuxX8664) ClangCppflags() string {
+func (t *toolchainLinuxX8664) Cppflags() string {
 	return ""
 }
 
-func (t *toolchainLinuxX86) ClangLdflags() string {
-	return "${config.LinuxClangLdflags} ${config.LinuxX86ClangLdflags}"
+func (t *toolchainLinuxX86) Ldflags() string {
+	return "${config.LinuxLdflags} ${config.LinuxX86Ldflags}"
 }
 
-func (t *toolchainLinuxX86) ClangLldflags() string {
-	return "${config.LinuxClangLldflags} ${config.LinuxX86ClangLldflags}"
+func (t *toolchainLinuxX86) Lldflags() string {
+	return "${config.LinuxLldflags} ${config.LinuxX86Lldflags}"
 }
 
-func (t *toolchainLinuxX8664) ClangLdflags() string {
-	return "${config.LinuxClangLdflags} ${config.LinuxX8664ClangLdflags}"
+func (t *toolchainLinuxX8664) Ldflags() string {
+	return "${config.LinuxLdflags} ${config.LinuxX8664Ldflags}"
 }
 
-func (t *toolchainLinuxX8664) ClangLldflags() string {
-	return "${config.LinuxClangLldflags} ${config.LinuxX8664ClangLldflags}"
+func (t *toolchainLinuxX8664) Lldflags() string {
+	return "${config.LinuxLldflags} ${config.LinuxX8664Lldflags}"
 }
 
 func (t *toolchainLinuxX86) YasmFlags() string {
@@ -245,22 +254,146 @@
 	return linuxAvailableLibraries
 }
 
-func (t *toolchainLinux) Bionic() bool {
-	return false
+// glibc specialization of the linux toolchain
+
+type toolchainGlibc struct {
 }
 
-var toolchainLinuxX86Singleton Toolchain = &toolchainLinuxX86{}
-var toolchainLinuxX8664Singleton Toolchain = &toolchainLinuxX8664{}
+func (toolchainGlibc) Glibc() bool { return true }
 
-func linuxX86ToolchainFactory(arch android.Arch) Toolchain {
-	return toolchainLinuxX86Singleton
+func (toolchainGlibc) Cflags() string {
+	return "${config.LinuxGlibcCflags}"
 }
 
-func linuxX8664ToolchainFactory(arch android.Arch) Toolchain {
-	return toolchainLinuxX8664Singleton
+func (toolchainGlibc) Ldflags() string {
+	return "${config.LinuxGlibcLdflags}"
+}
+
+func (toolchainGlibc) Lldflags() string {
+	return "${config.LinuxGlibcLldflags}"
+}
+
+type toolchainLinuxGlibcX86 struct {
+	toolchainLinuxX86
+	toolchainGlibc
+}
+
+type toolchainLinuxGlibcX8664 struct {
+	toolchainLinuxX8664
+	toolchainGlibc
+}
+
+func (t *toolchainLinuxGlibcX86) Cflags() string {
+	return t.toolchainLinuxX86.Cflags() + " " + t.toolchainGlibc.Cflags()
+}
+
+func (t *toolchainLinuxGlibcX86) Ldflags() string {
+	return t.toolchainLinuxX86.Ldflags() + " " + t.toolchainGlibc.Ldflags()
+}
+
+func (t *toolchainLinuxGlibcX86) Lldflags() string {
+	return t.toolchainLinuxX86.Lldflags() + " " + t.toolchainGlibc.Lldflags()
+}
+
+func (t *toolchainLinuxGlibcX8664) Cflags() string {
+	return t.toolchainLinuxX8664.Cflags() + " " + t.toolchainGlibc.Cflags()
+}
+
+func (t *toolchainLinuxGlibcX8664) Ldflags() string {
+	return t.toolchainLinuxX8664.Ldflags() + " " + t.toolchainGlibc.Ldflags()
+}
+
+func (t *toolchainLinuxGlibcX8664) Lldflags() string {
+	return t.toolchainLinuxX8664.Lldflags() + " " + t.toolchainGlibc.Lldflags()
+}
+
+var toolchainLinuxGlibcX86Singleton Toolchain = &toolchainLinuxGlibcX86{}
+var toolchainLinuxGlibcX8664Singleton Toolchain = &toolchainLinuxGlibcX8664{}
+
+func linuxGlibcX86ToolchainFactory(arch android.Arch) Toolchain {
+	return toolchainLinuxGlibcX86Singleton
+}
+
+func linuxGlibcX8664ToolchainFactory(arch android.Arch) Toolchain {
+	return toolchainLinuxGlibcX8664Singleton
+}
+
+// musl specialization of the linux toolchain
+
+type toolchainMusl struct {
+}
+
+func (toolchainMusl) Musl() bool { return true }
+
+func (toolchainMusl) CrtBeginStaticBinary() []string  { return muslCrtBeginStaticBinary }
+func (toolchainMusl) CrtBeginSharedBinary() []string  { return muslCrtBeginSharedBinary }
+func (toolchainMusl) CrtBeginSharedLibrary() []string { return muslCrtBeginSharedLibrary }
+func (toolchainMusl) CrtEndStaticBinary() []string    { return muslCrtEndStaticBinary }
+func (toolchainMusl) CrtEndSharedBinary() []string    { return muslCrtEndSharedBinary }
+func (toolchainMusl) CrtEndSharedLibrary() []string   { return muslCrtEndSharedLibrary }
+
+func (toolchainMusl) DefaultSharedLibraries() []string { return muslDefaultSharedLibraries }
+
+func (toolchainMusl) Cflags() string {
+	return "${config.LinuxMuslCflags}"
+}
+
+func (toolchainMusl) Ldflags() string {
+	return "${config.LinuxMuslLdflags}"
+}
+
+func (toolchainMusl) Lldflags() string {
+	return "${config.LinuxMuslLldflags}"
+}
+
+type toolchainLinuxMuslX86 struct {
+	toolchainLinuxX86
+	toolchainMusl
+}
+
+type toolchainLinuxMuslX8664 struct {
+	toolchainLinuxX8664
+	toolchainMusl
+}
+
+func (t *toolchainLinuxMuslX86) Cflags() string {
+	return t.toolchainLinuxX86.Cflags() + " " + t.toolchainMusl.Cflags()
+}
+
+func (t *toolchainLinuxMuslX86) Ldflags() string {
+	return t.toolchainLinuxX86.Ldflags() + " " + t.toolchainMusl.Ldflags()
+}
+
+func (t *toolchainLinuxMuslX86) Lldflags() string {
+	return t.toolchainLinuxX86.Lldflags() + " " + t.toolchainMusl.Lldflags()
+}
+
+func (t *toolchainLinuxMuslX8664) Cflags() string {
+	return t.toolchainLinuxX8664.Cflags() + " " + t.toolchainMusl.Cflags()
+}
+
+func (t *toolchainLinuxMuslX8664) Ldflags() string {
+	return t.toolchainLinuxX8664.Ldflags() + " " + t.toolchainMusl.Ldflags()
+}
+
+func (t *toolchainLinuxMuslX8664) Lldflags() string {
+	return t.toolchainLinuxX8664.Lldflags() + " " + t.toolchainMusl.Lldflags()
+}
+
+var toolchainLinuxMuslX86Singleton Toolchain = &toolchainLinuxMuslX86{}
+var toolchainLinuxMuslX8664Singleton Toolchain = &toolchainLinuxMuslX8664{}
+
+func linuxMuslX86ToolchainFactory(arch android.Arch) Toolchain {
+	return toolchainLinuxMuslX86Singleton
+}
+
+func linuxMuslX8664ToolchainFactory(arch android.Arch) Toolchain {
+	return toolchainLinuxMuslX8664Singleton
 }
 
 func init() {
-	registerToolchainFactory(android.Linux, android.X86, linuxX86ToolchainFactory)
-	registerToolchainFactory(android.Linux, android.X86_64, linuxX8664ToolchainFactory)
+	registerToolchainFactory(android.Linux, android.X86, linuxGlibcX86ToolchainFactory)
+	registerToolchainFactory(android.Linux, android.X86_64, linuxGlibcX8664ToolchainFactory)
+	registerToolchainFactory(android.LinuxMusl, android.X86, linuxMuslX86ToolchainFactory)
+	registerToolchainFactory(android.LinuxMusl, android.X86_64, linuxMuslX8664ToolchainFactory)
 }
diff --git a/cc/config/x86_windows_host.go b/cc/config/x86_windows_host.go
index b77df79..d9a7537 100644
--- a/cc/config/x86_windows_host.go
+++ b/cc/config/x86_windows_host.go
@@ -44,32 +44,28 @@
 
 		"--sysroot ${WindowsGccRoot}/${WindowsGccTriple}",
 	}
-	windowsClangCflags = append(ClangFilterUnknownCflags(windowsCflags), []string{}...)
 
 	windowsIncludeFlags = []string{
 		"-isystem ${WindowsGccRoot}/${WindowsGccTriple}/include",
 	}
 
-	windowsClangCppflags = []string{}
+	windowsCppflags = []string{}
 
-	windowsX86ClangCppflags = []string{
+	windowsX86Cppflags = []string{
 		// Use SjLj exceptions for 32-bit.  libgcc_eh implements SjLj
 		// exception model for 32-bit.
 		"-fsjlj-exceptions",
 	}
 
-	windowsX8664ClangCppflags = []string{}
+	windowsX8664Cppflags = []string{}
 
 	windowsLdflags = []string{
-		"--enable-stdcall-fixup",
 		"-Wl,--dynamicbase",
 		"-Wl,--nxcompat",
 	}
-	windowsLldflags = []string{
+	windowsLldflags = append(windowsLdflags, []string{
 		"-Wl,--Xlink=-Brepro", // Enable deterministic build
-	}
-	windowsClangLdflags  = append(ClangFilterUnknownCflags(windowsLdflags), []string{}...)
-	windowsClangLldflags = append(ClangFilterUnknownLldflags(windowsClangLdflags), windowsLldflags...)
+	}...)
 
 	windowsX86Cflags = []string{
 		"-m32",
@@ -84,28 +80,24 @@
 		"-Wl,--large-address-aware",
 		"-L${WindowsGccRoot}/${WindowsGccTriple}/lib32",
 		"-static-libgcc",
-	}
-	windowsX86ClangLdflags = append(ClangFilterUnknownCflags(windowsX86Ldflags), []string{
+
 		"-B${WindowsGccRoot}/${WindowsGccTriple}/bin",
 		"-B${WindowsGccRoot}/lib/gcc/${WindowsGccTriple}/4.8.3/32",
 		"-L${WindowsGccRoot}/lib/gcc/${WindowsGccTriple}/4.8.3/32",
 		"-B${WindowsGccRoot}/${WindowsGccTriple}/lib32",
-	}...)
-	windowsX86ClangLldflags = ClangFilterUnknownLldflags(windowsX86ClangLdflags)
+	}
 
 	windowsX8664Ldflags = []string{
 		"-m64",
 		"-L${WindowsGccRoot}/${WindowsGccTriple}/lib64",
 		"-Wl,--high-entropy-va",
 		"-static-libgcc",
-	}
-	windowsX8664ClangLdflags = append(ClangFilterUnknownCflags(windowsX8664Ldflags), []string{
+
 		"-B${WindowsGccRoot}/${WindowsGccTriple}/bin",
 		"-B${WindowsGccRoot}/lib/gcc/${WindowsGccTriple}/4.8.3",
 		"-L${WindowsGccRoot}/lib/gcc/${WindowsGccTriple}/4.8.3",
 		"-B${WindowsGccRoot}/${WindowsGccTriple}/lib64",
-	}...)
-	windowsX8664ClangLldflags = ClangFilterUnknownLldflags(windowsX8664ClangLdflags)
+	}
 
 	windowsAvailableLibraries = addPrefix([]string{
 		"gdi32",
@@ -138,21 +130,19 @@
 
 	pctx.StaticVariable("WindowsGccTriple", "x86_64-w64-mingw32")
 
-	pctx.StaticVariable("WindowsClangCflags", strings.Join(windowsClangCflags, " "))
-	pctx.StaticVariable("WindowsClangLdflags", strings.Join(windowsClangLdflags, " "))
-	pctx.StaticVariable("WindowsClangLldflags", strings.Join(windowsClangLldflags, " "))
-	pctx.StaticVariable("WindowsClangCppflags", strings.Join(windowsClangCppflags, " "))
+	pctx.StaticVariable("WindowsCflags", strings.Join(windowsCflags, " "))
+	pctx.StaticVariable("WindowsLdflags", strings.Join(windowsLdflags, " "))
+	pctx.StaticVariable("WindowsLldflags", strings.Join(windowsLldflags, " "))
+	pctx.StaticVariable("WindowsCppflags", strings.Join(windowsCppflags, " "))
 
-	pctx.StaticVariable("WindowsX86ClangCflags",
-		strings.Join(ClangFilterUnknownCflags(windowsX86Cflags), " "))
-	pctx.StaticVariable("WindowsX8664ClangCflags",
-		strings.Join(ClangFilterUnknownCflags(windowsX8664Cflags), " "))
-	pctx.StaticVariable("WindowsX86ClangLdflags", strings.Join(windowsX86ClangLdflags, " "))
-	pctx.StaticVariable("WindowsX86ClangLldflags", strings.Join(windowsX86ClangLldflags, " "))
-	pctx.StaticVariable("WindowsX8664ClangLdflags", strings.Join(windowsX8664ClangLdflags, " "))
-	pctx.StaticVariable("WindowsX8664ClangLldflags", strings.Join(windowsX8664ClangLldflags, " "))
-	pctx.StaticVariable("WindowsX86ClangCppflags", strings.Join(windowsX86ClangCppflags, " "))
-	pctx.StaticVariable("WindowsX8664ClangCppflags", strings.Join(windowsX8664ClangCppflags, " "))
+	pctx.StaticVariable("WindowsX86Cflags", strings.Join(windowsX86Cflags, " "))
+	pctx.StaticVariable("WindowsX8664Cflags", strings.Join(windowsX8664Cflags, " "))
+	pctx.StaticVariable("WindowsX86Ldflags", strings.Join(windowsX86Ldflags, " "))
+	pctx.StaticVariable("WindowsX86Lldflags", strings.Join(windowsX86Ldflags, " "))
+	pctx.StaticVariable("WindowsX8664Ldflags", strings.Join(windowsX8664Ldflags, " "))
+	pctx.StaticVariable("WindowsX8664Lldflags", strings.Join(windowsX8664Ldflags, " "))
+	pctx.StaticVariable("WindowsX86Cppflags", strings.Join(windowsX86Cppflags, " "))
+	pctx.StaticVariable("WindowsX8664Cppflags", strings.Join(windowsX8664Cppflags, " "))
 
 	pctx.StaticVariable("WindowsIncludeFlags", strings.Join(windowsIncludeFlags, " "))
 	// Yasm flags
@@ -214,36 +204,36 @@
 	return "x86_64-pc-windows-gnu"
 }
 
-func (t *toolchainWindowsX86) ClangCflags() string {
-	return "${config.WindowsClangCflags} ${config.WindowsX86ClangCflags}"
+func (t *toolchainWindowsX86) Cflags() string {
+	return "${config.WindowsCflags} ${config.WindowsX86Cflags}"
 }
 
-func (t *toolchainWindowsX8664) ClangCflags() string {
-	return "${config.WindowsClangCflags} ${config.WindowsX8664ClangCflags}"
+func (t *toolchainWindowsX8664) Cflags() string {
+	return "${config.WindowsCflags} ${config.WindowsX8664Cflags}"
 }
 
-func (t *toolchainWindowsX86) ClangCppflags() string {
-	return "${config.WindowsClangCppflags} ${config.WindowsX86ClangCppflags}"
+func (t *toolchainWindowsX86) Cppflags() string {
+	return "${config.WindowsCppflags} ${config.WindowsX86Cppflags}"
 }
 
-func (t *toolchainWindowsX8664) ClangCppflags() string {
-	return "${config.WindowsClangCppflags} ${config.WindowsX8664ClangCppflags}"
+func (t *toolchainWindowsX8664) Cppflags() string {
+	return "${config.WindowsCppflags} ${config.WindowsX8664Cppflags}"
 }
 
-func (t *toolchainWindowsX86) ClangLdflags() string {
-	return "${config.WindowsClangLdflags} ${config.WindowsX86ClangLdflags}"
+func (t *toolchainWindowsX86) Ldflags() string {
+	return "${config.WindowsLdflags} ${config.WindowsX86Ldflags}"
 }
 
-func (t *toolchainWindowsX86) ClangLldflags() string {
-	return "${config.WindowsClangLldflags} ${config.WindowsX86ClangLldflags}"
+func (t *toolchainWindowsX86) Lldflags() string {
+	return "${config.WindowsLldflags} ${config.WindowsX86Lldflags}"
 }
 
-func (t *toolchainWindowsX8664) ClangLdflags() string {
-	return "${config.WindowsClangLdflags} ${config.WindowsX8664ClangLdflags}"
+func (t *toolchainWindowsX8664) Ldflags() string {
+	return "${config.WindowsLdflags} ${config.WindowsX8664Ldflags}"
 }
 
-func (t *toolchainWindowsX8664) ClangLldflags() string {
-	return "${config.WindowsClangLldflags} ${config.WindowsX8664ClangLldflags}"
+func (t *toolchainWindowsX8664) Lldflags() string {
+	return "${config.WindowsLldflags} ${config.WindowsX8664Lldflags}"
 }
 
 func (t *toolchainWindowsX86) YasmFlags() string {
diff --git a/cc/coverage.go b/cc/coverage.go
index baf4226..8dd2db1 100644
--- a/cc/coverage.go
+++ b/cc/coverage.go
@@ -244,3 +244,19 @@
 		m[1].(Coverage).EnableCoverageIfNeeded()
 	}
 }
+
+func parseSymbolFileForAPICoverage(ctx ModuleContext, symbolFile string) android.ModuleOutPath {
+	apiLevelsJson := android.GetApiLevelsJson(ctx)
+	symbolFilePath := android.PathForModuleSrc(ctx, symbolFile)
+	outputFile := ctx.baseModuleName() + ".xml"
+	parsedApiCoveragePath := android.PathForModuleOut(ctx, outputFile)
+	rule := android.NewRuleBuilder(pctx, ctx)
+	rule.Command().
+		BuiltTool("ndk_api_coverage_parser").
+		Input(symbolFilePath).
+		Output(parsedApiCoveragePath).
+		Implicit(apiLevelsJson).
+		FlagWithArg("--api-map ", apiLevelsJson.String())
+	rule.Build("native_library_api_list", "Generate native API list based on symbol files for coverage measurement")
+	return parsedApiCoveragePath
+}
diff --git a/cc/fuzz.go b/cc/fuzz.go
index c780b6f..83f0037 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -15,7 +15,6 @@
 package cc
 
 import (
-	"encoding/json"
 	"path/filepath"
 	"sort"
 	"strings"
@@ -24,55 +23,9 @@
 
 	"android/soong/android"
 	"android/soong/cc/config"
+	"android/soong/fuzz"
 )
 
-type FuzzConfig struct {
-	// Email address of people to CC on bugs or contact about this fuzz target.
-	Cc []string `json:"cc,omitempty"`
-	// Specify whether to enable continuous fuzzing on devices. Defaults to true.
-	Fuzz_on_haiku_device *bool `json:"fuzz_on_haiku_device,omitempty"`
-	// Specify whether to enable continuous fuzzing on host. Defaults to true.
-	Fuzz_on_haiku_host *bool `json:"fuzz_on_haiku_host,omitempty"`
-	// Component in Google's bug tracking system that bugs should be filed to.
-	Componentid *int64 `json:"componentid,omitempty"`
-	// Hotlists in Google's bug tracking system that bugs should be marked with.
-	Hotlists []string `json:"hotlists,omitempty"`
-	// Specify whether this fuzz target was submitted by a researcher. Defaults
-	// to false.
-	Researcher_submitted *bool `json:"researcher_submitted,omitempty"`
-	// Specify who should be acknowledged for CVEs in the Android Security
-	// Bulletin.
-	Acknowledgement []string `json:"acknowledgement,omitempty"`
-	// Additional options to be passed to libfuzzer when run in Haiku.
-	Libfuzzer_options []string `json:"libfuzzer_options,omitempty"`
-	// Additional options to be passed to HWASAN when running on-device in Haiku.
-	Hwasan_options []string `json:"hwasan_options,omitempty"`
-	// Additional options to be passed to HWASAN when running on host in Haiku.
-	Asan_options []string `json:"asan_options,omitempty"`
-}
-
-func (f *FuzzConfig) String() string {
-	b, err := json.Marshal(f)
-	if err != nil {
-		panic(err)
-	}
-
-	return string(b)
-}
-
-type FuzzProperties struct {
-	// Optional list of seed files to be installed to the fuzz target's output
-	// directory.
-	Corpus []string `android:"path"`
-	// Optional list of data files to be installed to the fuzz target's output
-	// directory. Directory structure relative to the module is preserved.
-	Data []string `android:"path"`
-	// Optional dictionary to be installed to the fuzz target's output directory.
-	Dictionary *string `android:"path"`
-	// Config for running the target on fuzzing infrastructure.
-	Fuzz_config *FuzzConfig
-}
-
 func init() {
 	android.RegisterModuleType("cc_fuzz", FuzzFactory)
 	android.RegisterSingletonType("cc_fuzz_packaging", fuzzPackagingFactory)
@@ -94,19 +47,14 @@
 	*binaryDecorator
 	*baseCompiler
 
-	Properties            FuzzProperties
-	dictionary            android.Path
-	corpus                android.Paths
-	corpusIntermediateDir android.Path
-	config                android.Path
-	data                  android.Paths
-	dataIntermediateDir   android.Path
-	installedSharedDeps   []string
+	fuzzPackagedModule fuzz.FuzzPackagedModule
+
+	installedSharedDeps []string
 }
 
 func (fuzz *fuzzBinary) linkerProps() []interface{} {
 	props := fuzz.binaryDecorator.linkerProps()
-	props = append(props, &fuzz.Properties)
+	props = append(props, &fuzz.fuzzPackagedModule.FuzzProperties)
 	return props
 }
 
@@ -175,7 +123,7 @@
 // that should be installed in the fuzz target output directories. This function
 // returns true, unless:
 //  - The module is not an installable shared library, or
-//  - The module is a header, stub, or vendor-linked library, or
+//  - The module is a header or stub, or
 //  - The module is a prebuilt and its source is available, or
 //  - The module is a versioned member of an SDK snapshot.
 func isValidSharedDependency(dependency android.Module) bool {
@@ -193,11 +141,6 @@
 		return false
 	}
 
-	if linkable.UseVndk() {
-		// Discard vendor linked libraries.
-		return false
-	}
-
 	if lib := moduleLibraryInterface(dependency); lib != nil && lib.buildStubs() && linkable.CcLibrary() {
 		// Discard stubs libs (only CCLibrary variants). Prebuilt libraries should not
 		// be excluded on the basis of they're not CCLibrary()'s.
@@ -257,41 +200,41 @@
 		"fuzz", ctx.Target().Arch.ArchType.String(), ctx.ModuleName())
 	fuzz.binaryDecorator.baseInstaller.install(ctx, file)
 
-	fuzz.corpus = android.PathsForModuleSrc(ctx, fuzz.Properties.Corpus)
+	fuzz.fuzzPackagedModule.Corpus = android.PathsForModuleSrc(ctx, fuzz.fuzzPackagedModule.FuzzProperties.Corpus)
 	builder := android.NewRuleBuilder(pctx, ctx)
 	intermediateDir := android.PathForModuleOut(ctx, "corpus")
-	for _, entry := range fuzz.corpus {
+	for _, entry := range fuzz.fuzzPackagedModule.Corpus {
 		builder.Command().Text("cp").
 			Input(entry).
 			Output(intermediateDir.Join(ctx, entry.Base()))
 	}
 	builder.Build("copy_corpus", "copy corpus")
-	fuzz.corpusIntermediateDir = intermediateDir
+	fuzz.fuzzPackagedModule.CorpusIntermediateDir = intermediateDir
 
-	fuzz.data = android.PathsForModuleSrc(ctx, fuzz.Properties.Data)
+	fuzz.fuzzPackagedModule.Data = android.PathsForModuleSrc(ctx, fuzz.fuzzPackagedModule.FuzzProperties.Data)
 	builder = android.NewRuleBuilder(pctx, ctx)
 	intermediateDir = android.PathForModuleOut(ctx, "data")
-	for _, entry := range fuzz.data {
+	for _, entry := range fuzz.fuzzPackagedModule.Data {
 		builder.Command().Text("cp").
 			Input(entry).
 			Output(intermediateDir.Join(ctx, entry.Rel()))
 	}
 	builder.Build("copy_data", "copy data")
-	fuzz.dataIntermediateDir = intermediateDir
+	fuzz.fuzzPackagedModule.DataIntermediateDir = intermediateDir
 
-	if fuzz.Properties.Dictionary != nil {
-		fuzz.dictionary = android.PathForModuleSrc(ctx, *fuzz.Properties.Dictionary)
-		if fuzz.dictionary.Ext() != ".dict" {
+	if fuzz.fuzzPackagedModule.FuzzProperties.Dictionary != nil {
+		fuzz.fuzzPackagedModule.Dictionary = android.PathForModuleSrc(ctx, *fuzz.fuzzPackagedModule.FuzzProperties.Dictionary)
+		if fuzz.fuzzPackagedModule.Dictionary.Ext() != ".dict" {
 			ctx.PropertyErrorf("dictionary",
 				"Fuzzer dictionary %q does not have '.dict' extension",
-				fuzz.dictionary.String())
+				fuzz.fuzzPackagedModule.Dictionary.String())
 		}
 	}
 
-	if fuzz.Properties.Fuzz_config != nil {
+	if fuzz.fuzzPackagedModule.FuzzProperties.Fuzz_config != nil {
 		configPath := android.PathForModuleOut(ctx, "config").Join(ctx, "config.json")
-		android.WriteFileRule(ctx, configPath, fuzz.Properties.Fuzz_config.String())
-		fuzz.config = configPath
+		android.WriteFileRule(ctx, configPath, fuzz.fuzzPackagedModule.FuzzProperties.Fuzz_config.String())
+		fuzz.fuzzPackagedModule.Config = configPath
 	}
 
 	// Grab the list of required shared libraries.
@@ -359,32 +302,20 @@
 
 // Responsible for generating GNU Make rules that package fuzz targets into
 // their architecture & target/host specific zip file.
-type fuzzPackager struct {
-	packages                android.Paths
+type ccFuzzPackager struct {
+	fuzz.FuzzPackager
 	sharedLibInstallStrings []string
-	fuzzTargets             map[string]bool
 }
 
 func fuzzPackagingFactory() android.Singleton {
-	return &fuzzPackager{}
+	return &ccFuzzPackager{}
 }
 
-type fileToZip struct {
-	SourceFilePath        android.Path
-	DestinationPathPrefix string
-}
-
-type archOs struct {
-	hostOrTarget string
-	arch         string
-	dir          string
-}
-
-func (s *fuzzPackager) GenerateBuildActions(ctx android.SingletonContext) {
+func (s *ccFuzzPackager) 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[archOs][]fileToZip)
+	archDirs := make(map[fuzz.ArchOs][]fuzz.FileToZip)
 
 	// Map tracking whether each shared library has an install rule to avoid duplicate install rules from
 	// multiple fuzzers that depend on the same shared library.
@@ -392,12 +323,16 @@
 
 	// 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)
+	s.FuzzTargets = make(map[string]bool)
 
 	ctx.VisitAllModules(func(module android.Module) {
-		// Discard non-fuzz targets.
 		ccModule, ok := module.(*Module)
-		if !ok {
+		if !ok || ccModule.Properties.PreventInstall {
+			return
+		}
+
+		// Discard non-fuzz targets.
+		if ok := fuzz.IsValid(ccModule.FuzzModule); !ok {
 			return
 		}
 
@@ -406,18 +341,6 @@
 			return
 		}
 
-		// Discard ramdisk + vendor_ramdisk + recovery modules, they're duplicates of
-		// fuzz targets we're going to package anyway.
-		if !ccModule.Enabled() || ccModule.Properties.PreventInstall ||
-			ccModule.InRamdisk() || ccModule.InVendorRamdisk() || ccModule.InRecovery() {
-			return
-		}
-
-		// Discard modules that are in an unavailable namespace.
-		if !ccModule.ExportedToMake() {
-			return
-		}
-
 		hostOrTargetString := "target"
 		if ccModule.Host() {
 			hostOrTargetString = "host"
@@ -425,42 +348,21 @@
 
 		archString := ccModule.Arch().ArchType.String()
 		archDir := android.PathForIntermediates(ctx, "fuzz", hostOrTargetString, archString)
-		archOs := archOs{hostOrTarget: hostOrTargetString, arch: archString, dir: archDir.String()}
+		archOs := fuzz.ArchOs{HostOrTarget: hostOrTargetString, Arch: archString, Dir: archDir.String()}
 
 		// Grab the list of required shared libraries.
 		sharedLibraries := collectAllSharedDependencies(ctx, module)
 
-		var files []fileToZip
+		var files []fuzz.FileToZip
 		builder := android.NewRuleBuilder(pctx, ctx)
 
-		// Package the corpora into a zipfile.
-		if fuzzModule.corpus != nil {
-			corpusZip := archDir.Join(ctx, module.Name()+"_seed_corpus.zip")
-			command := builder.Command().BuiltTool("soong_zip").
-				Flag("-j").
-				FlagWithOutput("-o ", corpusZip)
-			rspFile := corpusZip.ReplaceExtension(ctx, "rsp")
-			command.FlagWithRspFileInputList("-r ", rspFile, fuzzModule.corpus)
-			files = append(files, fileToZip{corpusZip, ""})
-		}
-
-		// Package the data into a zipfile.
-		if fuzzModule.data != nil {
-			dataZip := archDir.Join(ctx, module.Name()+"_data.zip")
-			command := builder.Command().BuiltTool("soong_zip").
-				FlagWithOutput("-o ", dataZip)
-			for _, f := range fuzzModule.data {
-				intermediateDir := strings.TrimSuffix(f.String(), f.Rel())
-				command.FlagWithArg("-C ", intermediateDir)
-				command.FlagWithInput("-f ", f)
-			}
-			files = append(files, fileToZip{dataZip, ""})
-		}
+		// Package the corpus, data, dict and config into a zipfile.
+		files = s.PackageArtifacts(ctx, module, fuzzModule.fuzzPackagedModule, archDir, builder)
 
 		// Find and mark all the transiently-dependent shared libraries for
 		// packaging.
 		for _, library := range sharedLibraries {
-			files = append(files, fileToZip{library, "lib"})
+			files = append(files, fuzz.FileToZip{library, "lib"})
 
 			// For each architecture-specific shared library dependency, we need to
 			// install it to the output directory. Setup the install destination here,
@@ -492,83 +394,20 @@
 		}
 
 		// The executable.
-		files = append(files, fileToZip{ccModule.UnstrippedOutputFile(), ""})
+		files = append(files, fuzz.FileToZip{ccModule.UnstrippedOutputFile(), ""})
 
-		// The dictionary.
-		if fuzzModule.dictionary != nil {
-			files = append(files, fileToZip{fuzzModule.dictionary, ""})
+		archDirs[archOs], ok = s.BuildZipFile(ctx, module, fuzzModule.fuzzPackagedModule, files, builder, archDir, archString, hostOrTargetString, archOs, archDirs)
+		if !ok {
+			return
 		}
-
-		// Additional fuzz config.
-		if fuzzModule.config != nil {
-			files = append(files, fileToZip{fuzzModule.config, ""})
-		}
-
-		fuzzZip := archDir.Join(ctx, module.Name()+".zip")
-		command := builder.Command().BuiltTool("soong_zip").
-			Flag("-j").
-			FlagWithOutput("-o ", fuzzZip)
-		for _, file := range files {
-			if file.DestinationPathPrefix != "" {
-				command.FlagWithArg("-P ", file.DestinationPathPrefix)
-			} else {
-				command.Flag("-P ''")
-			}
-			command.FlagWithInput("-f ", file.SourceFilePath)
-		}
-
-		builder.Build("create-"+fuzzZip.String(),
-			"Package "+module.Name()+" for "+archString+"-"+hostOrTargetString)
-
-		// Don't add modules to 'make haiku' that are set to not be exported to the
-		// fuzzing infrastructure.
-		if config := fuzzModule.Properties.Fuzz_config; config != nil {
-			if ccModule.Host() && !BoolDefault(config.Fuzz_on_haiku_host, true) {
-				return
-			} else if !BoolDefault(config.Fuzz_on_haiku_device, true) {
-				return
-			}
-		}
-
-		s.fuzzTargets[module.Name()] = true
-		archDirs[archOs] = append(archDirs[archOs], fileToZip{fuzzZip, ""})
 	})
 
-	var archOsList []archOs
-	for archOs := range archDirs {
-		archOsList = append(archOsList, archOs)
-	}
-	sort.Slice(archOsList, func(i, j int) bool { return archOsList[i].dir < archOsList[j].dir })
+	s.CreateFuzzPackage(ctx, archDirs, fuzz.Cc, pctx)
 
-	for _, archOs := range archOsList {
-		filesToZip := archDirs[archOs]
-		arch := archOs.arch
-		hostOrTarget := archOs.hostOrTarget
-		builder := android.NewRuleBuilder(pctx, ctx)
-		outputFile := android.PathForOutput(ctx, "fuzz-"+hostOrTarget+"-"+arch+".zip")
-		s.packages = append(s.packages, outputFile)
-
-		command := builder.Command().BuiltTool("soong_zip").
-			Flag("-j").
-			FlagWithOutput("-o ", outputFile).
-			Flag("-L 0") // No need to try and re-compress the zipfiles.
-
-		for _, fileToZip := range filesToZip {
-			if fileToZip.DestinationPathPrefix != "" {
-				command.FlagWithArg("-P ", fileToZip.DestinationPathPrefix)
-			} else {
-				command.Flag("-P ''")
-			}
-			command.FlagWithInput("-f ", fileToZip.SourceFilePath)
-		}
-
-		builder.Build("create-fuzz-package-"+arch+"-"+hostOrTarget,
-			"Create fuzz target packages for "+arch+"-"+hostOrTarget)
-	}
 }
 
-func (s *fuzzPackager) MakeVars(ctx android.MakeVarsContext) {
-	packages := s.packages.Strings()
+func (s *ccFuzzPackager) 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
@@ -580,10 +419,5 @@
 		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, " "))
+	s.PreallocateSlice(ctx, "ALL_FUZZ_TARGETS")
 }
diff --git a/cc/gen.go b/cc/gen.go
index b152e02..3a1a0e2 100644
--- a/cc/gen.go
+++ b/cc/gen.go
@@ -204,7 +204,7 @@
 	headerFile := android.GenPathWithExt(ctx, "windmc", srcFile, "h")
 	rcFile := android.GenPathWithExt(ctx, "windmc", srcFile, "rc")
 
-	windmcCmd := gccCmd(flags.toolchain, "windmc")
+	windmcCmd := mingwCmd(flags.toolchain, "windmc")
 
 	ctx.Build(pctx, android.BuildParams{
 		Rule:           windmc,
diff --git a/cc/genrule.go b/cc/genrule.go
index 82d7205..0ca901e 100644
--- a/cc/genrule.go
+++ b/cc/genrule.go
@@ -17,6 +17,7 @@
 import (
 	"android/soong/android"
 	"android/soong/genrule"
+	"android/soong/snapshot"
 )
 
 func init() {
@@ -84,7 +85,7 @@
 	// is not needed.
 	recoverySnapshotVersion := ctx.DeviceConfig().RecoverySnapshotVersion()
 	if recoverySnapshotVersion != "current" && recoverySnapshotVersion != "" &&
-		!isRecoveryProprietaryModule(ctx) {
+		!snapshot.IsRecoveryProprietaryModule(ctx) {
 		return false
 	} else {
 		return Bool(g.Recovery_available)
@@ -103,7 +104,7 @@
 		// If not, we assume modules under proprietary paths are compatible for
 		// BOARD_VNDK_VERSION. The other modules are regarded as AOSP, that is
 		// PLATFORM_VNDK_VERSION.
-		if vndkVersion == "current" || !isVendorProprietaryModule(ctx) {
+		if vndkVersion == "current" || !snapshot.IsVendorProprietaryModule(ctx) {
 			variants = append(variants, VendorVariationPrefix+ctx.DeviceConfig().PlatformVndkVersion())
 		} else {
 			variants = append(variants, VendorVariationPrefix+vndkVersion)
diff --git a/cc/image.go b/cc/image.go
index 47a424b..3a0857b 100644
--- a/cc/image.go
+++ b/cc/image.go
@@ -22,6 +22,7 @@
 	"strings"
 
 	"android/soong/android"
+	"android/soong/snapshot"
 )
 
 var _ android.ImageInterface = (*Module)(nil)
@@ -341,11 +342,11 @@
 }
 
 func (m *Module) ExtraVariants() []string {
-	return m.Properties.ExtraVariants
+	return m.Properties.ExtraVersionedImageVariations
 }
 
 func (m *Module) AppendExtraVariant(extraVariant string) {
-	m.Properties.ExtraVariants = append(m.Properties.ExtraVariants, extraVariant)
+	m.Properties.ExtraVersionedImageVariations = append(m.Properties.ExtraVersionedImageVariations, extraVariant)
 }
 
 func (m *Module) SetRamdiskVariantNeeded(b bool) {
@@ -365,8 +366,8 @@
 }
 
 func (m *Module) SnapshotVersion(mctx android.BaseModuleContext) string {
-	if snapshot, ok := m.linker.(snapshotInterface); ok {
-		return snapshot.version()
+	if snapshot, ok := m.linker.(SnapshotInterface); ok {
+		return snapshot.Version()
 	} else {
 		mctx.ModuleErrorf("version is unknown for snapshot prebuilt")
 		// Should we be panicking here instead?
@@ -496,7 +497,7 @@
 		// BOARD_VNDK_VERSION. The other modules are regarded as AOSP, or
 		// PLATFORM_VNDK_VERSION.
 		if m.HasVendorVariant() {
-			if isVendorProprietaryModule(mctx) {
+			if snapshot.IsVendorProprietaryModule(mctx) {
 				vendorVariants = append(vendorVariants, boardVndkVersion)
 			} else {
 				vendorVariants = append(vendorVariants, platformVndkVersion)
@@ -525,7 +526,7 @@
 				platformVndkVersion,
 				boardVndkVersion,
 			)
-		} else if isVendorProprietaryModule(mctx) {
+		} else if snapshot.IsVendorProprietaryModule(mctx) {
 			vendorVariants = append(vendorVariants, boardVndkVersion)
 		} else {
 			vendorVariants = append(vendorVariants, platformVndkVersion)
@@ -579,10 +580,10 @@
 
 	// If using a snapshot, the recovery variant under AOSP directories is not needed,
 	// except for kernel headers, which needs all variants.
-	if m.KernelHeadersDecorator() &&
+	if !m.KernelHeadersDecorator() &&
 		!m.IsSnapshotPrebuilt() &&
 		usingRecoverySnapshot &&
-		!isRecoveryProprietaryModule(mctx) {
+		!snapshot.IsRecoveryProprietaryModule(mctx) {
 		recoveryVariantNeeded = false
 	}
 
@@ -629,7 +630,7 @@
 }
 
 func (c *Module) ExtraImageVariations(ctx android.BaseModuleContext) []string {
-	return c.Properties.ExtraVariants
+	return c.Properties.ExtraVersionedImageVariations
 }
 
 func squashVendorSrcs(m *Module) {
diff --git a/cc/installer.go b/cc/installer.go
index e551c63..f95b493 100644
--- a/cc/installer.go
+++ b/cc/installer.go
@@ -25,6 +25,10 @@
 type InstallerProperties struct {
 	// install to a subdirectory of the default install path for the module
 	Relative_install_path *string `android:"arch_variant"`
+
+	// Install output directly in {partition}/, not in any subdir.  This is only intended for use by
+	// init_first_stage.
+	Install_in_root *bool `android:"arch_variant"`
 }
 
 type installLocation int
@@ -66,6 +70,11 @@
 	if ctx.toolchain().Is64Bit() && installer.dir64 != "" {
 		dir = installer.dir64
 	}
+
+	if installer.installInRoot() {
+		dir = ""
+	}
+
 	if ctx.Target().NativeBridge == android.NativeBridgeEnabled {
 		dir = filepath.Join(dir, ctx.Target().NativeBridgeRelativePath)
 	} else if !ctx.Host() && ctx.Config().HasMultilibConflict(ctx.Arch().ArchType) {
@@ -110,3 +119,7 @@
 func (installer *baseInstaller) makeUninstallable(mod *Module) {
 	mod.ModuleBase.MakeUninstallable()
 }
+
+func (installer *baseInstaller) installInRoot() bool {
+	return Bool(installer.Properties.Install_in_root)
+}
diff --git a/cc/library.go b/cc/library.go
index 1ba3597..196116b 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -23,12 +23,13 @@
 	"strings"
 	"sync"
 
-	"github.com/google/blueprint"
-	"github.com/google/blueprint/pathtools"
-
 	"android/soong/android"
 	"android/soong/bazel"
+	"android/soong/bazel/cquery"
 	"android/soong/cc/config"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/pathtools"
 )
 
 // LibraryProperties is a collection of properties shared by cc library rules.
@@ -111,9 +112,6 @@
 		Check_all_apis *bool
 	}
 
-	// Order symbols in .bss section by their sizes.  Only useful for shared libraries.
-	Sort_bss_symbols_by_size *bool
-
 	// Inject boringssl hash into the shared library.  This is only intended for use by external/boringssl.
 	Inject_bssl_hash *bool `android:"arch_variant"`
 
@@ -186,11 +184,11 @@
 	// be added to the include path (using -I) for this module and any module that links
 	// against this module.  Directories listed in export_include_dirs do not need to be
 	// listed in local_include_dirs.
-	Export_include_dirs []string `android:"arch_variant"`
+	Export_include_dirs []string `android:"arch_variant,variant_prepend"`
 
 	// list of directories that will be added to the system include path
 	// using -isystem for this module and any module that links against this module.
-	Export_system_include_dirs []string `android:"arch_variant"`
+	Export_system_include_dirs []string `android:"arch_variant,variant_prepend"`
 
 	Target struct {
 		Vendor, Product struct {
@@ -221,46 +219,42 @@
 // For bp2build conversion.
 type bazelCcLibraryAttributes struct {
 	// Attributes pertaining to both static and shared variants.
-	Srcs               bazel.LabelListAttribute
-	Hdrs               bazel.LabelListAttribute
-	Deps               bazel.LabelListAttribute
-	Dynamic_deps       bazel.LabelListAttribute
-	Whole_archive_deps bazel.LabelListAttribute
-	Copts              bazel.StringListAttribute
-	Includes           bazel.StringListAttribute
-	Linkopts           bazel.StringListAttribute
-	// Attributes pertaining to shared variant.
-	Shared_copts                  bazel.StringListAttribute
-	Shared_srcs                   bazel.LabelListAttribute
-	Static_deps_for_shared        bazel.LabelListAttribute
-	Dynamic_deps_for_shared       bazel.LabelListAttribute
-	Whole_archive_deps_for_shared bazel.LabelListAttribute
-	User_link_flags               bazel.StringListAttribute
-	Version_script                bazel.LabelAttribute
-	// Attributes pertaining to static variant.
-	Static_copts                  bazel.StringListAttribute
-	Static_srcs                   bazel.LabelListAttribute
-	Static_deps_for_static        bazel.LabelListAttribute
-	Dynamic_deps_for_static       bazel.LabelListAttribute
-	Whole_archive_deps_for_static bazel.LabelListAttribute
+	Srcs    bazel.LabelListAttribute
+	Srcs_c  bazel.LabelListAttribute
+	Srcs_as bazel.LabelListAttribute
+
+	Copts      bazel.StringListAttribute
+	Cppflags   bazel.StringListAttribute
+	Conlyflags bazel.StringListAttribute
+	Asflags    bazel.StringListAttribute
+
+	Hdrs                bazel.LabelListAttribute
+	Deps                bazel.LabelListAttribute
+	Implementation_deps bazel.LabelListAttribute
+	Dynamic_deps        bazel.LabelListAttribute
+	Whole_archive_deps  bazel.LabelListAttribute
+	System_dynamic_deps bazel.LabelListAttribute
+	Includes            bazel.StringListAttribute
+	Linkopts            bazel.StringListAttribute
+	Use_libcrt          bazel.BoolAttribute
+	Rtti                bazel.BoolAttribute
+
+	// This is shared only.
+	Version_script bazel.LabelAttribute
+
+	// Common properties shared between both shared and static variants.
+	Shared staticOrSharedAttributes
+	Static staticOrSharedAttributes
+
+	Strip stripAttributes
 }
 
-type bazelCcLibrary struct {
-	android.BazelTargetModuleBase
-	bazelCcLibraryAttributes
-}
-
-func (m *bazelCcLibrary) Name() string {
-	return m.BaseModuleName()
-}
-
-func (m *bazelCcLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {}
-
-func BazelCcLibraryFactory() android.Module {
-	module := &bazelCcLibrary{}
-	module.AddProperties(&module.bazelCcLibraryAttributes)
-	android.InitBazelTargetModule(module)
-	return module
+type stripAttributes struct {
+	Keep_symbols                 bazel.BoolAttribute
+	Keep_symbols_and_debug_frame bazel.BoolAttribute
+	Keep_symbols_list            bazel.StringListAttribute
+	All                          bazel.BoolAttribute
+	None                         bazel.BoolAttribute
 }
 
 func CcLibraryBp2Build(ctx android.TopDownMutatorContext) {
@@ -287,28 +281,47 @@
 	linkerAttrs := bp2BuildParseLinkerProps(ctx, m)
 	exportedIncludes := bp2BuildParseExportedIncludes(ctx, m)
 
-	var srcs bazel.LabelListAttribute
-	srcs.Append(compilerAttrs.srcs)
+	srcs := compilerAttrs.srcs
+
+	asFlags := compilerAttrs.asFlags
+	if compilerAttrs.asSrcs.IsEmpty() && sharedAttrs.Srcs_as.IsEmpty() && staticAttrs.Srcs_as.IsEmpty() {
+		// Skip asflags for BUILD file simplicity if there are no assembly sources.
+		asFlags = bazel.MakeStringListAttribute(nil)
+	}
 
 	attrs := &bazelCcLibraryAttributes{
-		Srcs:                          srcs,
-		Deps:                          linkerAttrs.deps,
-		Dynamic_deps:                  linkerAttrs.dynamicDeps,
-		Whole_archive_deps:            linkerAttrs.wholeArchiveDeps,
-		Copts:                         compilerAttrs.copts,
-		Includes:                      exportedIncludes,
-		Linkopts:                      linkerAttrs.linkopts,
-		Shared_copts:                  sharedAttrs.copts,
-		Shared_srcs:                   sharedAttrs.srcs,
-		Static_deps_for_shared:        sharedAttrs.staticDeps,
-		Whole_archive_deps_for_shared: sharedAttrs.wholeArchiveDeps,
-		Dynamic_deps_for_shared:       sharedAttrs.dynamicDeps,
-		Version_script:                linkerAttrs.versionScript,
-		Static_copts:                  staticAttrs.copts,
-		Static_srcs:                   staticAttrs.srcs,
-		Static_deps_for_static:        staticAttrs.staticDeps,
-		Whole_archive_deps_for_static: staticAttrs.wholeArchiveDeps,
-		Dynamic_deps_for_static:       staticAttrs.dynamicDeps,
+		Srcs:    srcs,
+		Srcs_c:  compilerAttrs.cSrcs,
+		Srcs_as: compilerAttrs.asSrcs,
+
+		Copts:      compilerAttrs.copts,
+		Cppflags:   compilerAttrs.cppFlags,
+		Conlyflags: compilerAttrs.conlyFlags,
+		Asflags:    asFlags,
+
+		Implementation_deps: linkerAttrs.deps,
+		Deps:                linkerAttrs.exportedDeps,
+		Dynamic_deps:        linkerAttrs.dynamicDeps,
+		Whole_archive_deps:  linkerAttrs.wholeArchiveDeps,
+		System_dynamic_deps: linkerAttrs.systemDynamicDeps,
+		Includes:            exportedIncludes,
+		Linkopts:            linkerAttrs.linkopts,
+		Use_libcrt:          linkerAttrs.useLibcrt,
+		Rtti:                compilerAttrs.rtti,
+
+		Version_script: linkerAttrs.versionScript,
+
+		Strip: stripAttributes{
+			Keep_symbols:                 linkerAttrs.stripKeepSymbols,
+			Keep_symbols_and_debug_frame: linkerAttrs.stripKeepSymbolsAndDebugFrame,
+			Keep_symbols_list:            linkerAttrs.stripKeepSymbolsList,
+			All:                          linkerAttrs.stripAll,
+			None:                         linkerAttrs.stripNone,
+		},
+
+		Shared: sharedAttrs,
+
+		Static: staticAttrs,
 	}
 
 	props := bazel.BazelTargetModuleProperties{
@@ -316,7 +329,7 @@
 		Bzl_load_location: "//build/bazel/rules:full_cc_library.bzl",
 	}
 
-	ctx.CreateBazelTargetModule(BazelCcLibraryFactory, m.Name(), props, attrs)
+	ctx.CreateBazelTargetModule(m.Name(), props, attrs)
 }
 
 // cc_library creates both static and/or shared libraries for a device and/or
@@ -331,6 +344,7 @@
 		staticLibrarySdkMemberType,
 		staticAndSharedLibrarySdkMemberType,
 	}
+	module.bazelHandler = &ccLibraryBazelHandler{module: module}
 	return module.Init()
 }
 
@@ -339,7 +353,7 @@
 	module, library := NewLibrary(android.HostAndDeviceSupported)
 	library.BuildOnlyStatic()
 	module.sdkMemberTypes = []android.SdkMemberType{staticLibrarySdkMemberType}
-	module.bazelHandler = &staticLibraryBazelHandler{module: module}
+	module.bazelHandler = &ccLibraryBazelHandler{module: module}
 	return module.Init()
 }
 
@@ -522,38 +536,28 @@
 	*baseInstaller
 
 	collectedSnapshotHeaders android.Paths
+
+	apiListCoverageXmlPath android.ModuleOutPath
 }
 
-type staticLibraryBazelHandler struct {
-	bazelHandler
+type ccLibraryBazelHandler struct {
+	android.BazelHandler
 
 	module *Module
 }
 
-func (handler *staticLibraryBazelHandler) generateBazelBuildActions(ctx android.ModuleContext, label string) bool {
-	bazelCtx := ctx.Config().BazelContext
-	ccInfo, ok, err := bazelCtx.GetCcInfo(label, ctx.Arch().ArchType)
-	if err != nil {
-		ctx.ModuleErrorf("Error getting Bazel CcInfo: %s", err)
+// generateStaticBazelBuildActions constructs the StaticLibraryInfo Soong
+// provider from a Bazel shared library's CcInfo provider.
+func (handler *ccLibraryBazelHandler) generateStaticBazelBuildActions(ctx android.ModuleContext, label string, ccInfo cquery.CcInfo) bool {
+	rootStaticArchives := ccInfo.RootStaticArchives
+	if len(rootStaticArchives) != 1 {
+		ctx.ModuleErrorf("expected exactly one root archive file for '%s', but got %s", label, rootStaticArchives)
 		return false
 	}
-	if !ok {
-		return ok
-	}
-	outputPaths := ccInfo.OutputFiles
-	objPaths := ccInfo.CcObjectFiles
-	if len(outputPaths) > 1 {
-		// TODO(cparsons): This is actually expected behavior for static libraries with no srcs.
-		// We should support this.
-		ctx.ModuleErrorf("expected at most one output file for '%s', but got %s", label, objPaths)
-		return false
-	} else if len(outputPaths) == 0 {
-		handler.module.outputFile = android.OptionalPath{}
-		return true
-	}
-	outputFilePath := android.PathForBazelOut(ctx, outputPaths[0])
+	outputFilePath := android.PathForBazelOut(ctx, rootStaticArchives[0])
 	handler.module.outputFile = android.OptionalPathForPath(outputFilePath)
 
+	objPaths := ccInfo.CcObjectFiles
 	objFiles := make(android.Paths, len(objPaths))
 	for i, objPath := range objPaths {
 		objFiles[i] = android.PathForBazelOut(ctx, objPath)
@@ -567,40 +571,117 @@
 		ReuseObjects:  objects,
 		Objects:       objects,
 
-		// TODO(cparsons): Include transitive static libraries in this provider to support
+		// TODO(b/190524881): Include transitive static libraries in this provider to support
 		// static libraries with deps.
 		TransitiveStaticLibrariesForOrdering: android.NewDepSetBuilder(android.TOPOLOGICAL).
 			Direct(outputFilePath).
 			Build(),
 	})
 
-	ctx.SetProvider(FlagExporterInfoProvider, flagExporterInfoFromCcInfo(ctx, ccInfo))
+	return true
+}
+
+// generateSharedBazelBuildActions constructs the SharedLibraryInfo Soong
+// provider from a Bazel shared library's CcInfo provider.
+func (handler *ccLibraryBazelHandler) generateSharedBazelBuildActions(ctx android.ModuleContext, label string, ccInfo cquery.CcInfo) bool {
+	rootDynamicLibraries := ccInfo.RootDynamicLibraries
+
+	if len(rootDynamicLibraries) != 1 {
+		ctx.ModuleErrorf("expected exactly one root dynamic library file for '%s', but got %s", label, rootDynamicLibraries)
+		return false
+	}
+	outputFilePath := android.PathForBazelOut(ctx, rootDynamicLibraries[0])
+	handler.module.outputFile = android.OptionalPathForPath(outputFilePath)
+
+	handler.module.linker.(*libraryDecorator).unstrippedOutputFile = outputFilePath
+
+	tocFile := getTocFile(ctx, label, ccInfo.OutputFiles)
+	handler.module.linker.(*libraryDecorator).tocFile = tocFile
+
+	ctx.SetProvider(SharedLibraryInfoProvider, SharedLibraryInfo{
+		TableOfContents: tocFile,
+		SharedLibrary:   outputFilePath,
+		Target:          ctx.Target(),
+		// TODO(b/190524881): Include transitive static libraries in this provider to support
+		// static libraries with deps. The provider key for this is TransitiveStaticLibrariesForOrdering.
+	})
+	return true
+}
+
+// getTocFile looks for the .so.toc file in the target's output files, if any. The .so.toc file
+// contains the table of contents of all symbols of a shared object.
+func getTocFile(ctx android.ModuleContext, label string, outputFiles []string) android.OptionalPath {
+	var tocFile string
+	for _, file := range outputFiles {
+		if strings.HasSuffix(file, ".so.toc") {
+			if tocFile != "" {
+				ctx.ModuleErrorf("The %s target cannot produce more than 1 .toc file.", label)
+			}
+			tocFile = file
+			// Don't break to validate that there are no multiple .toc files per .so.
+		}
+	}
+	if tocFile == "" {
+		return android.OptionalPath{}
+	}
+	return android.OptionalPathForPath(android.PathForBazelOut(ctx, tocFile))
+}
+
+func (handler *ccLibraryBazelHandler) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool {
+	bazelCtx := ctx.Config().BazelContext
+	ccInfo, ok, err := bazelCtx.GetCcInfo(label, ctx.Arch().ArchType)
+	if err != nil {
+		ctx.ModuleErrorf("Error getting Bazel CcInfo: %s", err)
+		return false
+	}
+	if !ok {
+		return ok
+	}
+
+	if handler.module.static() {
+		if ok := handler.generateStaticBazelBuildActions(ctx, label, ccInfo); !ok {
+			return false
+		}
+	} else if handler.module.Shared() {
+		if ok := handler.generateSharedBazelBuildActions(ctx, label, ccInfo); !ok {
+			return false
+		}
+	} else {
+		return false
+	}
+
+	handler.module.linker.(*libraryDecorator).setFlagExporterInfoFromCcInfo(ctx, ccInfo)
+	handler.module.maybeUnhideFromMake()
+
 	if i, ok := handler.module.linker.(snapshotLibraryInterface); ok {
 		// Dependencies on this library will expect collectedSnapshotHeaders to
 		// be set, otherwise validation will fail. For now, set this to an empty
 		// list.
-		// TODO(cparsons): More closely mirror the collectHeadersForSnapshot
+		// TODO(b/190533363): More closely mirror the collectHeadersForSnapshot
 		// implementation.
 		i.(*libraryDecorator).collectedSnapshotHeaders = android.Paths{}
 	}
-
 	return ok
 }
 
-// collectHeadersForSnapshot collects all exported headers from library.
-// It globs header files in the source tree for exported include directories,
-// and tracks generated header files separately.
-//
-// This is to be called from GenerateAndroidBuildActions, and then collected
-// header files can be retrieved by snapshotHeaders().
-func (l *libraryDecorator) collectHeadersForSnapshot(ctx android.ModuleContext) {
+func (library *libraryDecorator) setFlagExporterInfoFromCcInfo(ctx android.ModuleContext, ccInfo cquery.CcInfo) {
+	flagExporterInfo := flagExporterInfoFromCcInfo(ctx, ccInfo)
+	// flag exporters consolidates properties like includes, flags, dependencies that should be
+	// exported from this module to other modules
+	ctx.SetProvider(FlagExporterInfoProvider, flagExporterInfo)
+	// Store flag info to be passed along to androidmk
+	// TODO(b/184387147): Androidmk should be done in Bazel, not Soong.
+	library.flagExporterInfo = &flagExporterInfo
+}
+
+func GlobHeadersForSnapshot(ctx android.ModuleContext, paths android.Paths) android.Paths {
 	ret := android.Paths{}
 
 	// Headers in the source tree should be globbed. On the contrast, generated headers
 	// can't be globbed, and they should be manually collected.
 	// So, we first filter out intermediate directories (which contains generated headers)
 	// from exported directories, and then glob headers under remaining directories.
-	for _, path := range append(android.CopyOfPaths(l.flagExporter.dirs), l.flagExporter.systemDirs...) {
+	for _, path := range paths {
 		dir := path.String()
 		// Skip if dir is for generated headers
 		if strings.HasPrefix(dir, android.PathForOutput(ctx).String()) {
@@ -616,7 +697,7 @@
 				glob, err := ctx.GlobWithDeps("external/eigen/"+subdir+"/**/*", nil)
 				if err != nil {
 					ctx.ModuleErrorf("glob failed: %#v", err)
-					return
+					return nil
 				}
 				for _, header := range glob {
 					if strings.HasSuffix(header, "/") {
@@ -634,7 +715,7 @@
 		glob, err := ctx.GlobWithDeps(dir+"/**/*", nil)
 		if err != nil {
 			ctx.ModuleErrorf("glob failed: %#v", err)
-			return
+			return nil
 		}
 		isLibcxx := strings.HasPrefix(dir, "external/libcxx/include")
 		for _, header := range glob {
@@ -647,7 +728,7 @@
 			} else {
 				// Filter out only the files with extensions that are headers.
 				found := false
-				for _, ext := range headerExts {
+				for _, ext := range HeaderExts {
 					if strings.HasSuffix(header, ext) {
 						found = true
 						break
@@ -660,15 +741,38 @@
 			ret = append(ret, android.PathForSource(ctx, header))
 		}
 	}
+	return ret
+}
 
-	// Collect generated headers
-	for _, header := range append(android.CopyOfPaths(l.flagExporter.headers), l.flagExporter.deps...) {
+func GlobGeneratedHeadersForSnapshot(ctx android.ModuleContext, paths android.Paths) android.Paths {
+	ret := android.Paths{}
+	for _, header := range paths {
 		// TODO(b/148123511): remove exportedDeps after cleaning up genrule
 		if strings.HasSuffix(header.Base(), "-phony") {
 			continue
 		}
 		ret = append(ret, header)
 	}
+	return ret
+}
+
+// collectHeadersForSnapshot collects all exported headers from library.
+// It globs header files in the source tree for exported include directories,
+// and tracks generated header files separately.
+//
+// This is to be called from GenerateAndroidBuildActions, and then collected
+// header files can be retrieved by snapshotHeaders().
+func (l *libraryDecorator) collectHeadersForSnapshot(ctx android.ModuleContext) {
+	ret := android.Paths{}
+
+	// Headers in the source tree should be globbed. On the contrast, generated headers
+	// can't be globbed, and they should be manually collected.
+	// So, we first filter out intermediate directories (which contains generated headers)
+	// from exported directories, and then glob headers under remaining directories.
+	ret = append(ret, GlobHeadersForSnapshot(ctx, append(android.CopyOfPaths(l.flagExporter.dirs), l.flagExporter.systemDirs...))...)
+
+	// Collect generated headers
+	ret = append(ret, GlobGeneratedHeadersForSnapshot(ctx, append(android.CopyOfPaths(l.flagExporter.headers), l.flagExporter.deps...))...)
 
 	l.collectedSnapshotHeaders = ret
 }
@@ -817,16 +921,23 @@
 		if library.stubsVersion() != "" {
 			vndkVer = library.stubsVersion()
 		}
-		objs, versionScript := compileStubLibrary(ctx, flags, String(library.Properties.Llndk.Symbol_file), vndkVer, "--llndk")
+		nativeAbiResult := parseNativeAbiDefinition(ctx,
+			String(library.Properties.Llndk.Symbol_file),
+			android.ApiLevelOrPanic(ctx, vndkVer), "--llndk")
+		objs := compileStubLibrary(ctx, flags, nativeAbiResult.stubSrc)
 		if !Bool(library.Properties.Llndk.Unversioned) {
-			library.versionScriptPath = android.OptionalPathForPath(versionScript)
+			library.versionScriptPath = android.OptionalPathForPath(
+				nativeAbiResult.versionScript)
 		}
 		return objs
 	}
 	if ctx.IsVendorPublicLibrary() {
-		objs, versionScript := compileStubLibrary(ctx, flags, String(library.Properties.Vendor_public_library.Symbol_file), "current", "")
+		nativeAbiResult := parseNativeAbiDefinition(ctx,
+			String(library.Properties.Vendor_public_library.Symbol_file),
+			android.FutureApiLevel, "")
+		objs := compileStubLibrary(ctx, flags, nativeAbiResult.stubSrc)
 		if !Bool(library.Properties.Vendor_public_library.Unversioned) {
-			library.versionScriptPath = android.OptionalPathForPath(versionScript)
+			library.versionScriptPath = android.OptionalPathForPath(nativeAbiResult.versionScript)
 		}
 		return objs
 	}
@@ -836,8 +947,18 @@
 			ctx.PropertyErrorf("symbol_file", "%q doesn't have .map.txt suffix", symbolFile)
 			return Objects{}
 		}
-		objs, versionScript := compileStubLibrary(ctx, flags, String(library.Properties.Stubs.Symbol_file), library.MutatedProperties.StubsVersion, "--apex")
-		library.versionScriptPath = android.OptionalPathForPath(versionScript)
+		nativeAbiResult := parseNativeAbiDefinition(ctx, symbolFile,
+			android.ApiLevelOrPanic(ctx, library.MutatedProperties.StubsVersion),
+			"--apex")
+		objs := compileStubLibrary(ctx, flags, nativeAbiResult.stubSrc)
+		library.versionScriptPath = android.OptionalPathForPath(
+			nativeAbiResult.versionScript)
+
+		// Parse symbol file to get API list for coverage
+		if library.stubsVersion() == "current" && ctx.PrimaryArch() {
+			library.apiListCoverageXmlPath = parseSymbolFileForAPICoverage(ctx, symbolFile)
+		}
+
 		return objs
 	}
 
@@ -1053,9 +1174,9 @@
 		deps.ReexportSharedLibHeaders = append(deps.ReexportSharedLibHeaders, library.StaticProperties.Static.Export_shared_lib_headers...)
 		deps.ReexportStaticLibHeaders = append(deps.ReexportStaticLibHeaders, library.StaticProperties.Static.Export_static_lib_headers...)
 	} else if library.shared() {
-		if ctx.toolchain().Bionic() && !Bool(library.baseLinker.Properties.Nocrt) {
-			deps.CrtBegin = "crtbegin_so"
-			deps.CrtEnd = "crtend_so"
+		if !Bool(library.baseLinker.Properties.Nocrt) {
+			deps.CrtBegin = append(deps.CrtBegin, ctx.toolchain().CrtBeginSharedLibrary()...)
+			deps.CrtEnd = append(deps.CrtEnd, ctx.toolchain().CrtEndSharedLibrary()...)
 		}
 		deps.WholeStaticLibs = append(deps.WholeStaticLibs, library.SharedProperties.Shared.Whole_static_libs...)
 		deps.StaticLibs = append(deps.StaticLibs, library.SharedProperties.Shared.Static_libs...)
@@ -1283,22 +1404,9 @@
 	linkerDeps = append(linkerDeps, deps.SharedLibsDeps...)
 	linkerDeps = append(linkerDeps, deps.LateSharedLibsDeps...)
 	linkerDeps = append(linkerDeps, objs.tidyFiles...)
-
-	if Bool(library.Properties.Sort_bss_symbols_by_size) && !library.buildStubs() {
-		unsortedOutputFile := android.PathForModuleOut(ctx, "unsorted", fileName)
-		transformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs,
-			deps.StaticLibs, deps.LateStaticLibs, deps.WholeStaticLibs,
-			linkerDeps, deps.CrtBegin, deps.CrtEnd, false, builderFlags, unsortedOutputFile, implicitOutputs)
-
-		symbolOrderingFile := android.PathForModuleOut(ctx, "unsorted", fileName+".symbol_order")
-		symbolOrderingFlag := library.baseLinker.sortBssSymbolsBySize(ctx, unsortedOutputFile, symbolOrderingFile, builderFlags)
-		builderFlags.localLdFlags += " " + symbolOrderingFlag
-		linkerDeps = append(linkerDeps, symbolOrderingFile)
-	}
-
 	transformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs,
 		deps.StaticLibs, deps.LateStaticLibs, deps.WholeStaticLibs,
-		linkerDeps, deps.CrtBegin, deps.CrtEnd, false, builderFlags, outputFile, implicitOutputs)
+		linkerDeps, deps.CrtBegin, deps.CrtEnd, false, builderFlags, outputFile, implicitOutputs, nil)
 
 	objs.coverageFiles = append(objs.coverageFiles, deps.StaticLibObjs.coverageFiles...)
 	objs.coverageFiles = append(objs.coverageFiles, deps.WholeStaticLibObjs.coverageFiles...)
@@ -1309,19 +1417,17 @@
 	library.coverageOutputFile = transformCoverageFilesToZip(ctx, objs, library.getLibName(ctx))
 	library.linkSAbiDumpFiles(ctx, objs, fileName, unstrippedOutputFile)
 
-	var staticAnalogue *StaticLibraryInfo
+	var transitiveStaticLibrariesForOrdering *android.DepSet
 	if static := ctx.GetDirectDepsWithTag(staticVariantTag); len(static) > 0 {
 		s := ctx.OtherModuleProvider(static[0], StaticLibraryInfoProvider).(StaticLibraryInfo)
-		staticAnalogue = &s
+		transitiveStaticLibrariesForOrdering = s.TransitiveStaticLibrariesForOrdering
 	}
 
 	ctx.SetProvider(SharedLibraryInfoProvider, SharedLibraryInfo{
-		TableOfContents:         android.OptionalPathForPath(tocFile),
-		SharedLibrary:           unstrippedOutputFile,
-		UnstrippedSharedLibrary: library.unstrippedOutputFile,
-		CoverageSharedLibrary:   library.coverageOutputFile,
-		StaticAnalogue:          staticAnalogue,
-		Target:                  ctx.Target(),
+		TableOfContents:                      android.OptionalPathForPath(tocFile),
+		SharedLibrary:                        unstrippedOutputFile,
+		TransitiveStaticLibrariesForOrdering: transitiveStaticLibrariesForOrdering,
+		Target:                               ctx.Target(),
 	})
 
 	stubs := ctx.GetDirectDepsWithTag(stubImplDepTag)
@@ -1639,7 +1745,7 @@
 					mayUseCoreVariant = false
 				}
 
-				if ctx.Config().CFIEnabledForPath(ctx.ModuleDir()) && ctx.Arch().ArchType == android.Arm64 {
+				if ctx.Config().CFIEnabledForPath(ctx.ModuleDir()) {
 					mayUseCoreVariant = false
 				}
 
@@ -1794,6 +1900,11 @@
 		return nil
 	}
 
+	if library.hasLLNDKStubs() && ctx.Module().(*Module).UseVndk() {
+		// LLNDK libraries only need a single stubs variant.
+		return []string{android.FutureApiLevel.String()}
+	}
+
 	// Future API level is implicitly added if there isn't
 	vers := library.Properties.Stubs.Versions
 	if inList(android.FutureApiLevel.String(), vers) {
@@ -2135,8 +2246,7 @@
 	return nil
 }
 
-// versionSelector normalizes the versions in the Stubs.Versions property into MutatedProperties.AllStubsVersions,
-// and propagates the value from implementation libraries to llndk libraries with the same name.
+// versionSelector normalizes the versions in the Stubs.Versions property into MutatedProperties.AllStubsVersions.
 func versionSelectorMutator(mctx android.BottomUpMutatorContext) {
 	if library := moduleLibraryInterface(mctx.Module()); library != nil && CanBeVersionVariant(mctx.Module().(*Module)) {
 		if library.buildShared() {
@@ -2150,15 +2260,6 @@
 				// depend on the implementation library and haven't been mutated yet.
 				library.setAllStubsVersions(versions)
 			}
-
-			if mctx.Module().(*Module).UseVndk() && library.hasLLNDKStubs() {
-				// Propagate the version to the llndk stubs module.
-				mctx.VisitDirectDepsWithTag(llndkStubDepTag, func(stubs android.Module) {
-					if stubsLib := moduleLibraryInterface(stubs); stubsLib != nil {
-						stubsLib.setAllStubsVersions(library.allStubsVersions())
-					}
-				})
-			}
 		}
 	}
 }
@@ -2217,14 +2318,27 @@
 }
 
 type bazelCcLibraryStaticAttributes struct {
-	Copts              bazel.StringListAttribute
-	Srcs               bazel.LabelListAttribute
-	Deps               bazel.LabelListAttribute
-	Whole_archive_deps bazel.LabelListAttribute
-	Linkopts           bazel.StringListAttribute
-	Linkstatic         bool
-	Includes           bazel.StringListAttribute
-	Hdrs               bazel.LabelListAttribute
+	Copts               bazel.StringListAttribute
+	Srcs                bazel.LabelListAttribute
+	Implementation_deps bazel.LabelListAttribute
+	Deps                bazel.LabelListAttribute
+	Whole_archive_deps  bazel.LabelListAttribute
+	Dynamic_deps        bazel.LabelListAttribute
+	System_dynamic_deps bazel.LabelListAttribute
+	Linkopts            bazel.StringListAttribute
+	Linkstatic          bool
+	Use_libcrt          bazel.BoolAttribute
+	Rtti                bazel.BoolAttribute
+	Includes            bazel.StringListAttribute
+	Hdrs                bazel.LabelListAttribute
+
+	Cppflags   bazel.StringListAttribute
+	Srcs_c     bazel.LabelListAttribute
+	Conlyflags bazel.StringListAttribute
+	Srcs_as    bazel.LabelListAttribute
+	Asflags    bazel.StringListAttribute
+
+	Static staticOrSharedAttributes
 }
 
 type bazelCcLibraryStatic struct {
@@ -2244,15 +2358,46 @@
 	linkerAttrs := bp2BuildParseLinkerProps(ctx, module)
 	exportedIncludes := bp2BuildParseExportedIncludes(ctx, module)
 
+	asFlags := compilerAttrs.asFlags
+	if compilerAttrs.asSrcs.IsEmpty() {
+		// Skip asflags for BUILD file simplicity if there are no assembly sources.
+		asFlags = bazel.MakeStringListAttribute(nil)
+	}
+
+	// Append static{} stanza properties. These won't be specified on
+	// cc_library_static itself, but may be specified in cc_defaults that this module
+	// depends on.
+	staticAttrs := bp2BuildParseStaticProps(ctx, module)
+
+	compilerAttrs.srcs.Append(staticAttrs.Srcs)
+	compilerAttrs.cSrcs.Append(staticAttrs.Srcs_c)
+	compilerAttrs.asSrcs.Append(staticAttrs.Srcs_as)
+	compilerAttrs.copts.Append(staticAttrs.Copts)
+	linkerAttrs.exportedDeps.Append(staticAttrs.Static_deps)
+	linkerAttrs.dynamicDeps.Append(staticAttrs.Dynamic_deps)
+	linkerAttrs.wholeArchiveDeps.Append(staticAttrs.Whole_archive_deps)
+	linkerAttrs.systemDynamicDeps.Append(staticAttrs.System_dynamic_deps)
+
 	attrs := &bazelCcLibraryStaticAttributes{
-		Copts:              compilerAttrs.copts,
-		Srcs:               compilerAttrs.srcs,
-		Deps:               linkerAttrs.deps,
-		Whole_archive_deps: linkerAttrs.wholeArchiveDeps,
+		Copts:               compilerAttrs.copts,
+		Srcs:                compilerAttrs.srcs,
+		Implementation_deps: linkerAttrs.deps,
+		Deps:                linkerAttrs.exportedDeps,
+		Whole_archive_deps:  linkerAttrs.wholeArchiveDeps,
+		Dynamic_deps:        linkerAttrs.dynamicDeps,
+		System_dynamic_deps: linkerAttrs.systemDynamicDeps,
 
 		Linkopts:   linkerAttrs.linkopts,
 		Linkstatic: true,
+		Use_libcrt: linkerAttrs.useLibcrt,
+		Rtti:       compilerAttrs.rtti,
 		Includes:   exportedIncludes,
+
+		Cppflags:   compilerAttrs.cppFlags,
+		Srcs_c:     compilerAttrs.cSrcs,
+		Conlyflags: compilerAttrs.conlyFlags,
+		Srcs_as:    compilerAttrs.asSrcs,
+		Asflags:    asFlags,
 	}
 
 	props := bazel.BazelTargetModuleProperties{
@@ -2260,7 +2405,7 @@
 		Bzl_load_location: "//build/bazel/rules:cc_library_static.bzl",
 	}
 
-	ctx.CreateBazelTargetModule(BazelCcLibraryStaticFactory, module.Name(), props, attrs)
+	ctx.CreateBazelTargetModule(module.Name(), props, attrs)
 }
 
 func CcLibraryStaticBp2Build(ctx android.TopDownMutatorContext) {
diff --git a/cc/library_headers.go b/cc/library_headers.go
index 0aba8de..44a7a71 100644
--- a/cc/library_headers.go
+++ b/cc/library_headers.go
@@ -44,13 +44,13 @@
 }
 
 type libraryHeaderBazelHander struct {
-	bazelHandler
+	android.BazelHandler
 
 	module  *Module
 	library *libraryDecorator
 }
 
-func (h *libraryHeaderBazelHander) generateBazelBuildActions(ctx android.ModuleContext, label string) bool {
+func (h *libraryHeaderBazelHander) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool {
 	bazelCtx := ctx.Config().BazelContext
 	ccInfo, ok, err := bazelCtx.GetCcInfo(label, ctx.Arch().ArchType)
 	if err != nil {
@@ -73,13 +73,7 @@
 	// HeaderLibraryInfo is an empty struct to indicate to dependencies that this is a header library
 	ctx.SetProvider(HeaderLibraryInfoProvider, HeaderLibraryInfo{})
 
-	flagExporterInfo := flagExporterInfoFromCcInfo(ctx, ccInfo)
-	// Store flag info to be passed along to androimk
-	// TODO(b/184387147): Androidmk should be done in Bazel, not Soong.
-	h.library.flagExporterInfo = &flagExporterInfo
-	// flag exporters consolidates properties like includes, flags, dependencies that should be
-	// exported from this module to other modules
-	ctx.SetProvider(FlagExporterInfoProvider, flagExporterInfo)
+	h.library.setFlagExporterInfoFromCcInfo(ctx, ccInfo)
 
 	// Dependencies on this library will expect collectedSnapshotHeaders to be set, otherwise
 	// validation will fail. For now, set this to an empty list.
@@ -109,22 +103,12 @@
 }
 
 type bazelCcLibraryHeadersAttributes struct {
-	Copts    bazel.StringListAttribute
-	Hdrs     bazel.LabelListAttribute
-	Includes bazel.StringListAttribute
-	Deps     bazel.LabelListAttribute
-}
-
-type bazelCcLibraryHeaders struct {
-	android.BazelTargetModuleBase
-	bazelCcLibraryHeadersAttributes
-}
-
-func BazelCcLibraryHeadersFactory() android.Module {
-	module := &bazelCcLibraryHeaders{}
-	module.AddProperties(&module.bazelCcLibraryHeadersAttributes)
-	android.InitBazelTargetModule(module)
-	return module
+	Copts               bazel.StringListAttribute
+	Hdrs                bazel.LabelListAttribute
+	Includes            bazel.StringListAttribute
+	Deps                bazel.LabelListAttribute
+	Implementation_deps bazel.LabelListAttribute
+	System_dynamic_deps bazel.LabelListAttribute
 }
 
 func CcLibraryHeadersBp2Build(ctx android.TopDownMutatorContext) {
@@ -147,9 +131,11 @@
 	linkerAttrs := bp2BuildParseLinkerProps(ctx, module)
 
 	attrs := &bazelCcLibraryHeadersAttributes{
-		Copts:    compilerAttrs.copts,
-		Includes: exportedIncludes,
-		Deps:     linkerAttrs.deps,
+		Copts:               compilerAttrs.copts,
+		Includes:            exportedIncludes,
+		Implementation_deps: linkerAttrs.deps,
+		Deps:                linkerAttrs.exportedDeps,
+		System_dynamic_deps: linkerAttrs.systemDynamicDeps,
 	}
 
 	props := bazel.BazelTargetModuleProperties{
@@ -157,11 +143,5 @@
 		Bzl_load_location: "//build/bazel/rules:cc_library_headers.bzl",
 	}
 
-	ctx.CreateBazelTargetModule(BazelCcLibraryHeadersFactory, module.Name(), props, attrs)
+	ctx.CreateBazelTargetModule(module.Name(), props, attrs)
 }
-
-func (m *bazelCcLibraryHeaders) Name() string {
-	return m.BaseModuleName()
-}
-
-func (m *bazelCcLibraryHeaders) GenerateAndroidBuildActions(ctx android.ModuleContext) {}
diff --git a/cc/library_sdk_member.go b/cc/library_sdk_member.go
index 9010a1a..1866ff3 100644
--- a/cc/library_sdk_member.go
+++ b/cc/library_sdk_member.go
@@ -74,8 +74,8 @@
 	linkTypes []string
 }
 
-func (mt *librarySdkMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
-	targets := mctx.MultiTargets()
+func (mt *librarySdkMemberType) AddDependencies(ctx android.SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) {
+	targets := ctx.MultiTargets()
 	for _, lib := range names {
 		for _, target := range targets {
 			name, version := StubsLibNameAndVersion(lib)
@@ -83,21 +83,21 @@
 				version = "latest"
 			}
 			variations := target.Variations()
-			if mctx.Device() {
+			if ctx.Device() {
 				variations = append(variations,
 					blueprint.Variation{Mutator: "image", Variation: android.CoreVariation})
 			}
 			if mt.linkTypes == nil {
-				mctx.AddFarVariationDependencies(variations, dependencyTag, name)
+				ctx.AddFarVariationDependencies(variations, dependencyTag, name)
 			} else {
 				for _, linkType := range mt.linkTypes {
 					libVariations := append(variations,
 						blueprint.Variation{Mutator: "link", Variation: linkType})
-					if mctx.Device() && linkType == "shared" {
+					if ctx.Device() && linkType == "shared" {
 						libVariations = append(libVariations,
 							blueprint.Variation{Mutator: "version", Variation: version})
 					}
-					mctx.AddFarVariationDependencies(libVariations, dependencyTag, name)
+					ctx.AddFarVariationDependencies(libVariations, dependencyTag, name)
 				}
 			}
 		}
diff --git a/cc/library_test.go b/cc/library_test.go
index 7975275..6b349b6 100644
--- a/cc/library_test.go
+++ b/cc/library_test.go
@@ -19,6 +19,7 @@
 	"testing"
 
 	"android/soong/android"
+	"android/soong/bazel/cquery"
 )
 
 func TestLibraryReuse(t *testing.T) {
@@ -240,3 +241,82 @@
 
 	testCcError(t, `"libfoo" .*: versions: "X" could not be parsed as an integer and is not a recognized codename`, bp)
 }
+
+func TestCcLibraryWithBazel(t *testing.T) {
+	bp := `
+cc_library {
+	name: "foo",
+	srcs: ["foo.cc"],
+	bazel_module: { label: "//foo/bar:bar" },
+}`
+	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
+	config.BazelContext = android.MockBazelContext{
+		OutputBaseDir: "outputbase",
+		LabelToCcInfo: map[string]cquery.CcInfo{
+			"//foo/bar:bar": cquery.CcInfo{
+				CcObjectFiles:        []string{"foo.o"},
+				Includes:             []string{"include"},
+				SystemIncludes:       []string{"system_include"},
+				RootStaticArchives:   []string{"foo.a"},
+				RootDynamicLibraries: []string{"foo.so"},
+			},
+		},
+	}
+	ctx := testCcWithConfig(t, config)
+
+	staticFoo := ctx.ModuleForTests("foo", "android_arm_armv7-a-neon_static").Module()
+	outputFiles, err := staticFoo.(android.OutputFileProducer).OutputFiles("")
+	if err != nil {
+		t.Errorf("Unexpected error getting cc_object outputfiles %s", err)
+	}
+
+	expectedOutputFiles := []string{"outputbase/execroot/__main__/foo.a"}
+	android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings())
+
+	sharedFoo := ctx.ModuleForTests("foo", "android_arm_armv7-a-neon_shared").Module()
+	outputFiles, err = sharedFoo.(android.OutputFileProducer).OutputFiles("")
+	if err != nil {
+		t.Errorf("Unexpected error getting cc_object outputfiles %s", err)
+	}
+	expectedOutputFiles = []string{"outputbase/execroot/__main__/foo.so"}
+	android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings())
+
+	entries := android.AndroidMkEntriesForTest(t, ctx, sharedFoo)[0]
+	expectedFlags := []string{"-Ioutputbase/execroot/__main__/include", "-isystem outputbase/execroot/__main__/system_include"}
+	gotFlags := entries.EntryMap["LOCAL_EXPORT_CFLAGS"]
+	android.AssertDeepEquals(t, "androidmk exported cflags", expectedFlags, gotFlags)
+}
+
+func TestLibraryVersionScript(t *testing.T) {
+	result := PrepareForIntegrationTestWithCc.RunTestWithBp(t, `
+		cc_library {
+			name: "libfoo",
+			srcs: ["foo.c"],
+			version_script: "foo.map.txt",
+		}`)
+
+	libfoo := result.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Rule("ld")
+
+	android.AssertStringListContains(t, "missing dependency on version_script",
+		libfoo.Implicits.Strings(), "foo.map.txt")
+	android.AssertStringDoesContain(t, "missing flag for version_script",
+		libfoo.Args["ldFlags"], "-Wl,--version-script,foo.map.txt")
+
+}
+
+func TestLibraryDynamicList(t *testing.T) {
+	result := PrepareForIntegrationTestWithCc.RunTestWithBp(t, `
+		cc_library {
+			name: "libfoo",
+			srcs: ["foo.c"],
+			dynamic_list: "foo.dynamic.txt",
+		}`)
+
+	libfoo := result.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Rule("ld")
+
+	android.AssertStringListContains(t, "missing dependency on dynamic_list",
+		libfoo.Implicits.Strings(), "foo.dynamic.txt")
+	android.AssertStringDoesContain(t, "missing flag for dynamic_list",
+		libfoo.Args["ldFlags"], "-Wl,--dynamic-list,foo.dynamic.txt")
+
+}
diff --git a/cc/linkable.go b/cc/linkable.go
index b583b69..b510508 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -3,6 +3,7 @@
 import (
 	"android/soong/android"
 	"android/soong/bazel/cquery"
+	"android/soong/snapshot"
 
 	"github.com/google/blueprint"
 )
@@ -14,11 +15,6 @@
 	// SanitizePropDefined returns whether the Sanitizer properties struct for this module is defined.
 	SanitizePropDefined() bool
 
-	// IsDependencyRoot returns whether a module is of a type which cannot be a linkage dependency
-	// of another module. For example, cc_binary and rust_binary represent dependency roots as other
-	// modules cannot have linkage dependencies against these types.
-	IsDependencyRoot() bool
-
 	// IsSanitizerEnabled returns whether a sanitizer is enabled.
 	IsSanitizerEnabled(t SanitizerType) bool
 
@@ -76,24 +72,27 @@
 
 // Snapshottable defines those functions necessary for handling module snapshots.
 type Snapshottable interface {
+	snapshot.VendorSnapshotModuleInterface
+	snapshot.RecoverySnapshotModuleInterface
+
 	// SnapshotHeaders returns a list of header paths provided by this module.
 	SnapshotHeaders() android.Paths
 
-	// ExcludeFromVendorSnapshot returns true if this module should be otherwise excluded from the vendor snapshot.
-	ExcludeFromVendorSnapshot() bool
-
-	// ExcludeFromRecoverySnapshot returns true if this module should be otherwise excluded from the recovery snapshot.
-	ExcludeFromRecoverySnapshot() bool
-
 	// SnapshotLibrary returns true if this module is a snapshot library.
 	IsSnapshotLibrary() bool
 
+	// EffectiveLicenseFiles returns the list of License files for this module.
+	EffectiveLicenseFiles() android.Paths
+
 	// SnapshotRuntimeLibs returns a list of libraries needed by this module at runtime but which aren't build dependencies.
 	SnapshotRuntimeLibs() []string
 
 	// SnapshotSharedLibs returns the list of shared library dependencies for this module.
 	SnapshotSharedLibs() []string
 
+	// SnapshotStaticLibs returns the list of static library dependencies for this module.
+	SnapshotStaticLibs() []string
+
 	// IsSnapshotPrebuilt returns true if this module is a snapshot prebuilt.
 	IsSnapshotPrebuilt() bool
 }
@@ -124,6 +123,7 @@
 	IsPrebuilt() bool
 	Toc() android.OptionalPath
 
+	Device() bool
 	Host() bool
 
 	InRamdisk() bool
@@ -165,6 +165,9 @@
 	// "product_specific: true" modules are included here.
 	UseVndk() bool
 
+	// Bootstrap tests if this module is allowed to use non-APEX version of libraries.
+	Bootstrap() bool
+
 	// IsVndkSp returns true if this is a VNDK-SP module.
 	IsVndkSp() bool
 
@@ -223,6 +226,9 @@
 	// Header returns true if this is a library headers module.
 	Header() bool
 
+	// StaticExecutable returns true if this is a binary module with "static_executable: true".
+	StaticExecutable() bool
+
 	// EverInstallable returns true if the module is ever installable
 	EverInstallable() bool
 
@@ -305,14 +311,13 @@
 
 // SharedLibraryInfo is a provider to propagate information about a shared C++ library.
 type SharedLibraryInfo struct {
-	SharedLibrary           android.Path
-	UnstrippedSharedLibrary android.Path
-	Target                  android.Target
+	SharedLibrary android.Path
+	Target        android.Target
 
-	TableOfContents       android.OptionalPath
-	CoverageSharedLibrary android.OptionalPath
+	TableOfContents android.OptionalPath
 
-	StaticAnalogue *StaticLibraryInfo
+	// should be obtained from static analogue
+	TransitiveStaticLibrariesForOrdering *android.DepSet
 }
 
 var SharedLibraryInfoProvider = blueprint.NewProvider(SharedLibraryInfo{})
diff --git a/cc/linker.go b/cc/linker.go
index 449b9ad..0d612b5 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -15,9 +15,10 @@
 package cc
 
 import (
+	"fmt"
+
 	"android/soong/android"
 	"android/soong/cc/config"
-	"fmt"
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
@@ -85,8 +86,7 @@
 	// compiling crt or libc.
 	Nocrt *bool `android:"arch_variant"`
 
-	// group static libraries.  This can resolve missing symbols issues with interdependencies
-	// between static libraries, but it is generally better to order them correctly instead.
+	// deprecated and ignored because lld makes it unnecessary. See b/189475744.
 	Group_static_libs *bool `android:"arch_variant"`
 
 	// list of modules that should be installed with this module.  This is similar to 'required'
@@ -94,6 +94,9 @@
 	// vendor variants and this module uses VNDK.
 	Runtime_libs []string `android:"arch_variant"`
 
+	// list of runtime libs that should not be installed along with this module.
+	Exclude_runtime_libs []string `android:"arch_variant"`
+
 	Target struct {
 		Vendor, Product struct {
 			// list of shared libs that only should be used to build vendor or
@@ -104,6 +107,10 @@
 			// product variant of the C/C++ module.
 			Static_libs []string
 
+			// list of ehader libs that only should be used to build vendor or product
+			// variant of the C/C++ module.
+			Header_libs []string
+
 			// list of shared libs that should not be used to build vendor or
 			// product variant of the C/C++ module.
 			Exclude_shared_libs []string
@@ -116,8 +123,8 @@
 			// product variant of the C/C++ module.
 			Exclude_header_libs []string
 
-			// list of runtime libs that should not be installed along with
-			// vendor or variant of the C/C++ module.
+			// list of runtime libs that should not be installed along with the
+			// vendor or product variant of the C/C++ module.
 			Exclude_runtime_libs []string
 
 			// version script for vendor or product variant
@@ -143,6 +150,10 @@
 			// list of header libs that should not be used to build the recovery variant
 			// of the C/C++ module.
 			Exclude_header_libs []string
+
+			// list of runtime libs that should not be installed along with the
+			// recovery variant of the C/C++ module.
+			Exclude_runtime_libs []string
 		}
 		Ramdisk struct {
 			// list of static libs that only should be used to build the recovery
@@ -156,6 +167,10 @@
 			// list of static libs that should not be used to build
 			// the ramdisk variant of the C/C++ module.
 			Exclude_static_libs []string
+
+			// list of runtime libs that should not be installed along with the
+			// ramdisk variant of the C/C++ module.
+			Exclude_runtime_libs []string
 		}
 		Vendor_ramdisk struct {
 			// list of shared libs that should not be used to build
@@ -165,6 +180,10 @@
 			// list of static libs that should not be used to build
 			// the vendor ramdisk variant of the C/C++ module.
 			Exclude_static_libs []string
+
+			// list of runtime libs that should not be installed along with the
+			// vendor ramdisk variant of the C/C++ module.
+			Exclude_runtime_libs []string
 		}
 		Platform struct {
 			// list of shared libs that should be use to build the platform variant
@@ -172,13 +191,21 @@
 			// in most cases the same libraries are available for the SDK and platform
 			// variants.
 			Shared_libs []string
+
+			// list of ehader libs that only should be used to build platform variant of
+			// the C/C++ module.
+			Header_libs []string
+
+			// list of shared libs that should not be used to build the platform variant
+			// of the C/C++ module.
+			Exclude_shared_libs []string
 		}
 		Apex struct {
 			// list of shared libs that should not be used to build the apex variant of
 			// the C/C++ module.
 			Exclude_shared_libs []string
 
-			// list of static libs that should not be used to build the apex ramdisk
+			// list of static libs that should not be used to build the apex
 			// variant of the C/C++ module.
 			Exclude_static_libs []string
 		}
@@ -193,6 +220,9 @@
 	// local file name to pass to the linker as --version_script
 	Version_script *string `android:"path,arch_variant"`
 
+	// local file name to pass to the linker as --dynamic-list
+	Dynamic_list *string `android:"path,arch_variant"`
+
 	// list of static libs that should not be used to build this module
 	Exclude_static_libs []string `android:"arch_variant"`
 
@@ -200,6 +230,18 @@
 	Exclude_shared_libs []string `android:"arch_variant"`
 }
 
+func invertBoolPtr(value *bool) *bool {
+	if value == nil {
+		return nil
+	}
+	ret := !(*value)
+	return &ret
+}
+
+func (blp *BaseLinkerProperties) libCrt() *bool {
+	return invertBoolPtr(blp.No_libcrt)
+}
+
 func NewBaseLinker(sanitize *sanitize) *baseLinker {
 	return &baseLinker{sanitize: sanitize}
 }
@@ -247,6 +289,7 @@
 	deps.SharedLibs = removeListFromList(deps.SharedLibs, linker.Properties.Exclude_shared_libs)
 	deps.StaticLibs = removeListFromList(deps.StaticLibs, linker.Properties.Exclude_static_libs)
 	deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Exclude_static_libs)
+	deps.RuntimeLibs = removeListFromList(deps.RuntimeLibs, linker.Properties.Exclude_runtime_libs)
 
 	// Record the libraries that need to be excluded when building for APEX. Unlike other
 	// target.*.exclude_* properties, SharedLibs and StaticLibs are not modified here because
@@ -268,6 +311,7 @@
 		deps.ReexportSharedLibHeaders = removeListFromList(deps.ReexportSharedLibHeaders, linker.Properties.Target.Vendor.Exclude_shared_libs)
 		deps.StaticLibs = append(deps.StaticLibs, linker.Properties.Target.Vendor.Static_libs...)
 		deps.StaticLibs = removeListFromList(deps.StaticLibs, linker.Properties.Target.Vendor.Exclude_static_libs)
+		deps.HeaderLibs = append(deps.HeaderLibs, linker.Properties.Target.Vendor.Header_libs...)
 		deps.HeaderLibs = removeListFromList(deps.HeaderLibs, linker.Properties.Target.Vendor.Exclude_header_libs)
 		deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, linker.Properties.Target.Vendor.Exclude_static_libs)
 		deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Vendor.Exclude_static_libs)
@@ -296,6 +340,7 @@
 		deps.ReexportHeaderLibHeaders = removeListFromList(deps.ReexportHeaderLibHeaders, linker.Properties.Target.Recovery.Exclude_header_libs)
 		deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, linker.Properties.Target.Recovery.Exclude_static_libs)
 		deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Recovery.Exclude_static_libs)
+		deps.RuntimeLibs = removeListFromList(deps.RuntimeLibs, linker.Properties.Target.Recovery.Exclude_runtime_libs)
 	}
 
 	if ctx.inRamdisk() {
@@ -305,6 +350,7 @@
 		deps.StaticLibs = removeListFromList(deps.StaticLibs, linker.Properties.Target.Ramdisk.Exclude_static_libs)
 		deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, linker.Properties.Target.Ramdisk.Exclude_static_libs)
 		deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Ramdisk.Exclude_static_libs)
+		deps.RuntimeLibs = removeListFromList(deps.RuntimeLibs, linker.Properties.Target.Ramdisk.Exclude_runtime_libs)
 	}
 
 	if ctx.inVendorRamdisk() {
@@ -313,10 +359,23 @@
 		deps.StaticLibs = removeListFromList(deps.StaticLibs, linker.Properties.Target.Vendor_ramdisk.Exclude_static_libs)
 		deps.ReexportStaticLibHeaders = removeListFromList(deps.ReexportStaticLibHeaders, linker.Properties.Target.Vendor_ramdisk.Exclude_static_libs)
 		deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Vendor_ramdisk.Exclude_static_libs)
+		deps.RuntimeLibs = removeListFromList(deps.RuntimeLibs, linker.Properties.Target.Vendor_ramdisk.Exclude_runtime_libs)
 	}
 
 	if !ctx.useSdk() {
 		deps.SharedLibs = append(deps.SharedLibs, linker.Properties.Target.Platform.Shared_libs...)
+		deps.SharedLibs = removeListFromList(deps.SharedLibs, linker.Properties.Target.Platform.Exclude_shared_libs)
+		deps.HeaderLibs = append(deps.HeaderLibs, linker.Properties.Target.Platform.Header_libs...)
+	}
+
+	deps.SystemSharedLibs = linker.Properties.System_shared_libs
+	// In Bazel conversion mode, variations have not been specified, so SystemSharedLibs may
+	// inaccuarately appear unset, which can cause issues with circular dependencies.
+	if deps.SystemSharedLibs == nil && !ctx.BazelConversionMode() {
+		// Provide a default system_shared_libs if it is unspecified. Note: If an
+		// empty list [] is specified, it implies that the module declines the
+		// default system_shared_libs.
+		deps.SystemSharedLibs = append(deps.SystemSharedLibs, ctx.toolchain().DefaultSharedLibraries()...)
 	}
 
 	if ctx.toolchain().Bionic() {
@@ -325,16 +384,6 @@
 			deps.LateStaticLibs = append(deps.LateStaticLibs, config.BuiltinsRuntimeLibrary(ctx.toolchain()))
 		}
 
-		deps.SystemSharedLibs = linker.Properties.System_shared_libs
-		// In Bazel conversion mode, variations have not been specified, so SystemSharedLibs may
-		// inaccuarately appear unset, which can cause issues with circular dependencies.
-		if deps.SystemSharedLibs == nil && !ctx.BazelConversionMode() {
-			// Provide a default system_shared_libs if it is unspecified. Note: If an
-			// empty list [] is specified, it implies that the module declines the
-			// default system_shared_libs.
-			deps.SystemSharedLibs = []string{"libc", "libm", "libdl"}
-		}
-
 		if inList("libdl", deps.SharedLibs) {
 			// If system_shared_libs has libc but not libdl, make sure shared_libs does not
 			// have libdl to avoid loading libdl before libc.
@@ -353,21 +402,13 @@
 			indexList("libdl", deps.SystemSharedLibs) < indexList("libc", deps.SystemSharedLibs) {
 			ctx.PropertyErrorf("system_shared_libs", "libdl must be after libc")
 		}
-
-		deps.LateSharedLibs = append(deps.LateSharedLibs, deps.SystemSharedLibs...)
+	} else if ctx.toolchain().Musl() {
+		if !Bool(linker.Properties.No_libcrt) && !ctx.header() {
+			deps.LateStaticLibs = append(deps.LateStaticLibs, config.BuiltinsRuntimeLibrary(ctx.toolchain()))
+		}
 	}
 
-	if ctx.Fuchsia() {
-		if ctx.ModuleName() != "libbioniccompat" &&
-			ctx.ModuleName() != "libcompiler_rt-extras" &&
-			ctx.ModuleName() != "libcompiler_rt" {
-			deps.StaticLibs = append(deps.StaticLibs, "libbioniccompat")
-		}
-		if ctx.ModuleName() != "libcompiler_rt" && ctx.ModuleName() != "libcompiler_rt-extras" {
-			deps.LateStaticLibs = append(deps.LateStaticLibs, "libcompiler_rt")
-		}
-
-	}
+	deps.LateSharedLibs = append(deps.LateSharedLibs, deps.SystemSharedLibs...)
 
 	if ctx.Windows() {
 		deps.LateStaticLibs = append(deps.LateStaticLibs, "libwinpthread")
@@ -447,12 +488,12 @@
 	}
 
 	if linker.useClangLld(ctx) {
-		flags.Global.LdFlags = append(flags.Global.LdFlags, toolchain.ClangLldflags())
+		flags.Global.LdFlags = append(flags.Global.LdFlags, toolchain.Lldflags())
 	} else {
-		flags.Global.LdFlags = append(flags.Global.LdFlags, toolchain.ClangLdflags())
+		flags.Global.LdFlags = append(flags.Global.LdFlags, toolchain.Ldflags())
 	}
 
-	if !ctx.toolchain().Bionic() && !ctx.Fuchsia() {
+	if !ctx.toolchain().Bionic() && ctx.Os() != android.LinuxMusl {
 		CheckBadHostLdlibs(ctx, "host_ldlibs", linker.Properties.Host_ldlibs)
 
 		flags.Local.LdFlags = append(flags.Local.LdFlags, linker.Properties.Host_ldlibs...)
@@ -471,10 +512,6 @@
 		}
 	}
 
-	if ctx.Fuchsia() {
-		flags.Global.LdFlags = append(flags.Global.LdFlags, "-lfdio", "-lzircon")
-	}
-
 	if ctx.toolchain().LibclangRuntimeLibraryArch() != "" {
 		flags.Global.LdFlags = append(flags.Global.LdFlags, "-Wl,--exclude-libs="+config.BuiltinsRuntimeLibrary(ctx.toolchain())+".a")
 	}
@@ -503,11 +540,7 @@
 		flags.Global.LdFlags = append(flags.Global.LdFlags, "-Wl,--hash-style=both")
 	}
 
-	flags.Global.LdFlags = append(flags.Global.LdFlags, toolchain.ToolchainClangLdflags())
-
-	if Bool(linker.Properties.Group_static_libs) {
-		flags.GroupStaticLibs = true
-	}
+	flags.Global.LdFlags = append(flags.Global.LdFlags, toolchain.ToolchainLdflags())
 
 	// Version_script is not needed when linking stubs lib where the version
 	// script is created from the symbol map file.
@@ -541,6 +574,17 @@
 				}
 			}
 		}
+
+		dynamicList := android.OptionalPathForModuleSrc(ctx, linker.Properties.Dynamic_list)
+		if dynamicList.Valid() {
+			if ctx.Darwin() {
+				ctx.PropertyErrorf("dynamic_list", "Not supported on Darwin")
+			} else {
+				flags.Local.LdFlags = append(flags.Local.LdFlags,
+					"-Wl,--dynamic-list,"+dynamicList.String())
+				flags.LdFlagsDeps = append(flags.LdFlagsDeps, dynamicList.Path())
+			}
+		}
 	}
 
 	return flags
@@ -594,28 +638,3 @@
 		},
 	})
 }
-
-// Rule to generate .bss symbol ordering file.
-
-var (
-	_                   = pctx.SourcePathVariable("genSortedBssSymbolsPath", "build/soong/scripts/gen_sorted_bss_symbols.sh")
-	genSortedBssSymbols = pctx.AndroidStaticRule("gen_sorted_bss_symbols",
-		blueprint.RuleParams{
-			Command:     "CLANG_BIN=${clangBin} $genSortedBssSymbolsPath ${in} ${out}",
-			CommandDeps: []string{"$genSortedBssSymbolsPath", "${clangBin}/llvm-nm"},
-		},
-		"clangBin")
-)
-
-func (linker *baseLinker) sortBssSymbolsBySize(ctx ModuleContext, in android.Path, symbolOrderingFile android.ModuleOutPath, flags builderFlags) string {
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        genSortedBssSymbols,
-		Description: "generate bss symbol order " + symbolOrderingFile.Base(),
-		Output:      symbolOrderingFile,
-		Input:       in,
-		Args: map[string]string{
-			"clangBin": "${config.ClangBin}",
-		},
-	})
-	return "-Wl,--symbol-ordering-file," + symbolOrderingFile.String()
-}
diff --git a/cc/lto.go b/cc/lto.go
index a3b28d9..d9a0118 100644
--- a/cc/lto.go
+++ b/cc/lto.go
@@ -15,6 +15,8 @@
 package cc
 
 import (
+	"github.com/google/blueprint/proptools"
+
 	"android/soong/android"
 )
 
@@ -67,23 +69,19 @@
 
 func (lto *lto) begin(ctx BaseModuleContext) {
 	if ctx.Config().IsEnvTrue("DISABLE_LTO") {
-		lto.Properties.Lto.Never = boolPtr(true)
+		lto.Properties.Lto.Never = proptools.BoolPtr(true)
 	} else if ctx.Config().IsEnvTrue("GLOBAL_THINLTO") {
 		staticLib := ctx.static() && !ctx.staticBinary()
 		hostBin := ctx.Host()
 		vndk := ctx.isVndk() // b/169217596
 		if !staticLib && !hostBin && !vndk {
 			if !lto.Never() && !lto.FullLTO() {
-				lto.Properties.Lto.Thin = boolPtr(true)
+				lto.Properties.Lto.Thin = proptools.BoolPtr(true)
 			}
 		}
 	}
 }
 
-func (lto *lto) deps(ctx BaseModuleContext, deps Deps) Deps {
-	return deps
-}
-
 func (lto *lto) useClangLld(ctx BaseModuleContext) bool {
 	if lto.Properties.Use_clang_lld != nil {
 		return Bool(lto.Properties.Use_clang_lld)
@@ -233,12 +231,12 @@
 
 				// LTO properties for dependencies
 				if name == "lto-full" {
-					variation.lto.Properties.Lto.Full = boolPtr(true)
-					variation.lto.Properties.Lto.Thin = boolPtr(false)
+					variation.lto.Properties.Lto.Full = proptools.BoolPtr(true)
+					variation.lto.Properties.Lto.Thin = proptools.BoolPtr(false)
 				}
 				if name == "lto-thin" {
-					variation.lto.Properties.Lto.Full = boolPtr(false)
-					variation.lto.Properties.Lto.Thin = boolPtr(true)
+					variation.lto.Properties.Lto.Full = proptools.BoolPtr(false)
+					variation.lto.Properties.Lto.Thin = proptools.BoolPtr(true)
 				}
 				variation.Properties.PreventInstall = true
 				variation.Properties.HideFromMake = true
diff --git a/cc/makevars.go b/cc/makevars.go
index da5f1fd..8d7a163 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -92,8 +92,8 @@
 	ctx.Strict("RS_LLVM_AS", "${config.RSLLVMPrebuiltsPath}/llvm-as")
 	ctx.Strict("RS_LLVM_LINK", "${config.RSLLVMPrebuiltsPath}/llvm-link")
 
-	ctx.Strict("CLANG_EXTERNAL_CFLAGS", "${config.ClangExternalCflags}")
-	ctx.Strict("GLOBAL_CLANG_CFLAGS_NO_OVERRIDE", "${config.NoOverrideClangGlobalCflags}")
+	ctx.Strict("CLANG_EXTERNAL_CFLAGS", "${config.ExternalCflags}")
+	ctx.Strict("GLOBAL_CLANG_CFLAGS_NO_OVERRIDE", "${config.NoOverrideGlobalCflags}")
 	ctx.Strict("GLOBAL_CLANG_CPPFLAGS_NO_OVERRIDE", "")
 
 	ctx.Strict("BOARD_VNDK_VERSION", ctx.DeviceConfig().VndkVersion())
@@ -165,7 +165,7 @@
 	sort.Strings(ndkKnownLibs)
 	ctx.Strict("NDK_KNOWN_LIBS", strings.Join(ndkKnownLibs, " "))
 
-	hostTargets := ctx.Config().Targets[android.BuildOs]
+	hostTargets := ctx.Config().Targets[ctx.Config().BuildOS]
 	makeVarsToolchain(ctx, "", hostTargets[0])
 	if len(hostTargets) > 1 {
 		makeVarsToolchain(ctx, "2ND_", hostTargets[1])
@@ -212,13 +212,13 @@
 	ctx.StrictRaw(makePrefix+"C_SYSTEM_INCLUDES", strings.Join(systemIncludes, " "))
 
 	if target.Arch.ArchType == android.Arm {
-		flags, err := toolchain.ClangInstructionSetFlags("arm")
+		flags, err := toolchain.InstructionSetFlags("arm")
 		if err != nil {
 			panic(err)
 		}
 		ctx.Strict(makePrefix+"arm_CFLAGS", flags)
 
-		flags, err = toolchain.ClangInstructionSetFlags("thumb")
+		flags, err = toolchain.InstructionSetFlags("thumb")
 		if err != nil {
 			panic(err)
 		}
@@ -230,29 +230,29 @@
 
 	ctx.Strict(clangPrefix+"TRIPLE", toolchain.ClangTriple())
 	ctx.Strict(clangPrefix+"GLOBAL_CFLAGS", strings.Join([]string{
-		toolchain.ClangCflags(),
-		"${config.CommonClangGlobalCflags}",
-		fmt.Sprintf("${config.%sClangGlobalCflags}", hod),
-		toolchain.ToolchainClangCflags(),
+		toolchain.Cflags(),
+		"${config.CommonGlobalCflags}",
+		fmt.Sprintf("${config.%sGlobalCflags}", hod),
+		toolchain.ToolchainCflags(),
 		clangExtras,
 		productExtraCflags,
 	}, " "))
 	ctx.Strict(clangPrefix+"GLOBAL_CPPFLAGS", strings.Join([]string{
-		"${config.CommonClangGlobalCppflags}",
+		"${config.CommonGlobalCppflags}",
 		fmt.Sprintf("${config.%sGlobalCppflags}", hod),
-		toolchain.ClangCppflags(),
+		toolchain.Cppflags(),
 	}, " "))
 	ctx.Strict(clangPrefix+"GLOBAL_LDFLAGS", strings.Join([]string{
 		fmt.Sprintf("${config.%sGlobalLdflags}", hod),
-		toolchain.ClangLdflags(),
-		toolchain.ToolchainClangLdflags(),
+		toolchain.Ldflags(),
+		toolchain.ToolchainLdflags(),
 		productExtraLdflags,
 		clangExtras,
 	}, " "))
 	ctx.Strict(clangPrefix+"GLOBAL_LLDFLAGS", strings.Join([]string{
 		fmt.Sprintf("${config.%sGlobalLldflags}", hod),
-		toolchain.ClangLldflags(),
-		toolchain.ToolchainClangLdflags(),
+		toolchain.Lldflags(),
+		toolchain.ToolchainLdflags(),
 		productExtraLdflags,
 		clangExtras,
 	}, " "))
@@ -288,9 +288,7 @@
 		ctx.Strict(makePrefix+"OBJCOPY", "${config.ClangBin}/llvm-objcopy")
 		ctx.Strict(makePrefix+"LD", "${config.ClangBin}/lld")
 		ctx.Strict(makePrefix+"NDK_TRIPLE", config.NDKTriple(toolchain))
-		// TODO: work out whether to make this "${config.ClangBin}/llvm-", which
-		// should mostly work, or remove it.
-		ctx.Strict(makePrefix+"TOOLS_PREFIX", gccCmd(toolchain, ""))
+		ctx.Strict(makePrefix+"TOOLS_PREFIX", "${config.ClangBin}/llvm-")
 		// TODO: GCC version is obsolete now that GCC has been removed.
 		ctx.Strict(makePrefix+"GCC_VERSION", toolchain.GccVersion())
 	}
diff --git a/cc/ndk_abi.go b/cc/ndk_abi.go
new file mode 100644
index 0000000..927fa2e
--- /dev/null
+++ b/cc/ndk_abi.go
@@ -0,0 +1,102 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package cc
+
+import (
+	"android/soong/android"
+)
+
+func init() {
+	android.RegisterSingletonType("ndk_abi_dump", NdkAbiDumpSingleton)
+	android.RegisterSingletonType("ndk_abi_diff", NdkAbiDiffSingleton)
+}
+
+func getNdkAbiDumpInstallBase(ctx android.PathContext) android.OutputPath {
+	return android.PathForOutput(ctx).Join(ctx, "abi-dumps/ndk")
+}
+
+func getNdkAbiDumpTimestampFile(ctx android.PathContext) android.OutputPath {
+	return android.PathForOutput(ctx, "ndk_abi_dump.timestamp")
+}
+
+func NdkAbiDumpSingleton() android.Singleton {
+	return &ndkAbiDumpSingleton{}
+}
+
+type ndkAbiDumpSingleton struct{}
+
+func (n *ndkAbiDumpSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+	var depPaths android.Paths
+	ctx.VisitAllModules(func(module android.Module) {
+		if !module.Enabled() {
+			return
+		}
+
+		if m, ok := module.(*Module); ok {
+			if installer, ok := m.installer.(*stubDecorator); ok {
+				if canDumpAbi() {
+					depPaths = append(depPaths, installer.abiDumpPath)
+				}
+			}
+		}
+	})
+
+	// `m dump-ndk-abi` will dump the NDK ABI.
+	// `development/tools/ndk/update_ndk_abi.sh` will dump the NDK ABI and
+	// update the golden copies in prebuilts/abi-dumps/ndk.
+	ctx.Build(pctx, android.BuildParams{
+		Rule:      android.Touch,
+		Output:    getNdkAbiDumpTimestampFile(ctx),
+		Implicits: depPaths,
+	})
+
+	ctx.Phony("dump-ndk-abi", getNdkAbiDumpTimestampFile(ctx))
+}
+
+func getNdkAbiDiffTimestampFile(ctx android.PathContext) android.WritablePath {
+	return android.PathForOutput(ctx, "ndk_abi_diff.timestamp")
+}
+
+func NdkAbiDiffSingleton() android.Singleton {
+	return &ndkAbiDiffSingleton{}
+}
+
+type ndkAbiDiffSingleton struct{}
+
+func (n *ndkAbiDiffSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+	var depPaths android.Paths
+	ctx.VisitAllModules(func(module android.Module) {
+		if m, ok := module.(android.Module); ok && !m.Enabled() {
+			return
+		}
+
+		if m, ok := module.(*Module); ok {
+			if installer, ok := m.installer.(*stubDecorator); ok {
+				depPaths = append(depPaths, installer.abiDiffPaths...)
+			}
+		}
+	})
+
+	depPaths = append(depPaths, getNdkAbiDumpTimestampFile(ctx))
+
+	// `m diff-ndk-abi` will diff the NDK ABI.
+	ctx.Build(pctx, android.BuildParams{
+		Rule:      android.Touch,
+		Output:    getNdkAbiDiffTimestampFile(ctx),
+		Implicits: depPaths,
+	})
+
+	ctx.Phony("diff-ndk-abi", getNdkAbiDiffTimestampFile(ctx))
+}
diff --git a/cc/ndk_api_coverage_parser/__init__.py b/cc/ndk_api_coverage_parser/__init__.py
index 7817c78..8b9cd66 100755
--- a/cc/ndk_api_coverage_parser/__init__.py
+++ b/cc/ndk_api_coverage_parser/__init__.py
@@ -21,7 +21,12 @@
 import sys
 
 from xml.etree.ElementTree import Element, SubElement, tostring
-from symbolfile import ALL_ARCHITECTURES, FUTURE_API_LEVEL, MultiplyDefinedSymbolError, SymbolFileParser
+from symbolfile import (
+    ALL_ARCHITECTURES,
+    FUTURE_API_LEVEL,
+    MultiplyDefinedSymbolError,
+    SymbolFileParser,
+)
 
 
 ROOT_ELEMENT_TAG = 'ndk-library'
@@ -63,6 +68,7 @@
 
 class XmlGenerator(object):
     """Output generator that writes parsed symbol file to a xml file."""
+
     def __init__(self, output_file):
         self.output_file = output_file
 
@@ -74,10 +80,14 @@
                 continue
             version_attributes = parse_tags(version.tags)
             _, _, postfix = version.name.partition('_')
-            is_platform = postfix == 'PRIVATE' or postfix == 'PLATFORM'
+            is_platform = postfix in ('PRIVATE' , 'PLATFORM')
             is_deprecated = postfix == 'DEPRECATED'
-            version_attributes.update({PLATFORM_ATTRIBUTE_KEY: str(is_platform)})
-            version_attributes.update({DEPRECATED_ATTRIBUTE_KEY: str(is_deprecated)})
+            version_attributes.update(
+                {PLATFORM_ATTRIBUTE_KEY: str(is_platform)}
+            )
+            version_attributes.update(
+                {DEPRECATED_ATTRIBUTE_KEY: str(is_deprecated)}
+            )
             for symbol in version.symbols:
                 if VARIABLE_TAG in symbol.tags:
                     continue
@@ -103,13 +113,20 @@
     """Parses and returns command line arguments."""
     parser = argparse.ArgumentParser()
 
-    parser.add_argument('symbol_file', type=os.path.realpath, help='Path to symbol file.')
     parser.add_argument(
-        'output_file', type=os.path.realpath,
-        help='The output parsed api coverage file.')
+        'symbol_file', type=os.path.realpath, help='Path to symbol file.'
+    )
     parser.add_argument(
-        '--api-map', type=os.path.realpath, required=True,
-        help='Path to the API level map JSON file.')
+        'output_file',
+        type=os.path.realpath,
+        help='The output parsed api coverage file.',
+    )
+    parser.add_argument(
+        '--api-map',
+        type=os.path.realpath,
+        required=True,
+        help='Path to the API level map JSON file.',
+    )
     return parser.parse_args()
 
 
@@ -122,13 +139,15 @@
 
     with open(args.symbol_file) as symbol_file:
         try:
-            versions = SymbolFileParser(symbol_file, api_map, "", FUTURE_API_LEVEL,
-                                        True, True).parse()
+            versions = SymbolFileParser(
+                symbol_file, api_map, "", FUTURE_API_LEVEL, True, True
+            ).parse()
         except MultiplyDefinedSymbolError as ex:
             sys.exit('{}: error: {}'.format(args.symbol_file, ex))
 
     generator = XmlGenerator(args.output_file)
     generator.write(versions)
 
+
 if __name__ == '__main__':
     main()
diff --git a/cc/ndk_api_coverage_parser/test_ndk_api_coverage_parser.py b/cc/ndk_api_coverage_parser/test_ndk_api_coverage_parser.py
index 3ec14c1..141059c 100644
--- a/cc/ndk_api_coverage_parser/test_ndk_api_coverage_parser.py
+++ b/cc/ndk_api_coverage_parser/test_ndk_api_coverage_parser.py
@@ -50,10 +50,12 @@
         return False
     return all(etree_equal(c1, c2) for c1, c2 in zip(elem1, elem2))
 
-
+# pylint: disable=line-too-long
 class ApiCoverageSymbolFileParserTest(unittest.TestCase):
     def test_parse(self):
-        input_file = io.StringIO(textwrap.dedent(u"""\
+        input_file = io.StringIO(
+            textwrap.dedent(
+                u"""\
             LIBLOG { # introduced-arm64=24 introduced-x86=24 introduced-x86_64=24
               global:
                 android_name_to_log_id; # apex llndk introduced=23
@@ -64,22 +66,28 @@
               local:
                 *;
             };
-            
+
             LIBLOG_PLATFORM {
                 android_fdtrack; # llndk
                 android_net; # introduced=23
             };
-            
+
             LIBLOG_FOO { # var
                 android_var;
             };
-        """))
-        parser = SymbolFileParser(input_file, {}, "", FUTURE_API_LEVEL, True, True)
+        """
+            )
+        )
+        parser = SymbolFileParser(
+            input_file, {}, "", FUTURE_API_LEVEL, True, True
+        )
         generator = nparser.XmlGenerator(io.StringIO())
         result = generator.convertToXml(parser.parse())
-        expected = fromstring('<ndk-library><symbol apex="True" arch="" introduced="23" introduced-arm64="24" introduced-x86="24" introduced-x86_64="24" is_deprecated="False" is_platform="False" llndk="True" name="android_name_to_log_id" /><symbol arch="arm" introduced-arm64="24" introduced-x86="24" introduced-x86_64="24" is_deprecated="False" is_platform="False" llndk="True" name="android_log_id_to_name" /><symbol arch="" introduced-arm64="24" introduced-x86="23" introduced-x86_64="24" is_deprecated="False" is_platform="False" name="__android_log_assert" /><symbol arch="" introduced-arm64="24" introduced-x86="24" introduced-x86_64="24" is_deprecated="False" is_platform="False" name="__android_log_buf_write" /><symbol arch="" is_deprecated="False" is_platform="True" llndk="True" name="android_fdtrack" /><symbol arch="" introduced="23" is_deprecated="False" is_platform="True" name="android_net" /></ndk-library>')
+        expected = fromstring(
+            '<ndk-library><symbol apex="True" arch="" introduced="23" introduced-arm64="24" introduced-x86="24" introduced-x86_64="24" is_deprecated="False" is_platform="False" llndk="True" name="android_name_to_log_id" /><symbol arch="arm" introduced-arm64="24" introduced-x86="24" introduced-x86_64="24" is_deprecated="False" is_platform="False" llndk="True" name="android_log_id_to_name" /><symbol arch="" introduced-arm64="24" introduced-x86="23" introduced-x86_64="24" is_deprecated="False" is_platform="False" name="__android_log_assert" /><symbol arch="" introduced-arm64="24" introduced-x86="24" introduced-x86_64="24" is_deprecated="False" is_platform="False" name="__android_log_buf_write" /><symbol arch="" is_deprecated="False" is_platform="True" llndk="True" name="android_fdtrack" /><symbol arch="" introduced="23" is_deprecated="False" is_platform="True" name="android_net" /></ndk-library>'
+        )
         self.assertTrue(etree_equal(expected, result))
-
+# pylint: enable=line-too-long
 
 def main():
     suite = unittest.TestLoader().loadTestsFromName(__name__)
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index 95d8477..51cdddf 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -16,6 +16,8 @@
 
 import (
 	"fmt"
+	"path/filepath"
+	"runtime"
 	"strings"
 	"sync"
 
@@ -27,7 +29,9 @@
 
 func init() {
 	pctx.HostBinToolVariable("ndkStubGenerator", "ndkstubgen")
-	pctx.HostBinToolVariable("ndk_api_coverage_parser", "ndk_api_coverage_parser")
+	pctx.HostBinToolVariable("abidiff", "abidiff")
+	pctx.HostBinToolVariable("abitidy", "abitidy")
+	pctx.HostBinToolVariable("abidw", "abidw")
 }
 
 var (
@@ -38,17 +42,31 @@
 			CommandDeps: []string{"$ndkStubGenerator"},
 		}, "arch", "apiLevel", "apiMap", "flags")
 
-	parseNdkApiRule = pctx.AndroidStaticRule("parseNdkApiRule",
+	abidw = pctx.AndroidStaticRule("abidw",
 		blueprint.RuleParams{
-			Command:     "$ndk_api_coverage_parser $in $out --api-map $apiMap",
-			CommandDeps: []string{"$ndk_api_coverage_parser"},
-		}, "apiMap")
+			Command: "$abidw --type-id-style hash --no-corpus-path " +
+				"--no-show-locs --no-comp-dir-path -w $symbolList $in | " +
+				"$abitidy --all -o $out",
+			CommandDeps: []string{"$abitidy", "$abidw"},
+		}, "symbolList")
+
+	abidiff = pctx.AndroidStaticRule("abidiff",
+		blueprint.RuleParams{
+			// Need to create *some* output for ninja. We don't want to use tee
+			// because we don't want to spam the build output with "nothing
+			// changed" messages, so redirect output message to $out, and if
+			// changes were detected print the output and fail.
+			Command:     "$abidiff $args $in > $out || (cat $out && false)",
+			CommandDeps: []string{"$abidiff"},
+		}, "args")
 
 	ndkLibrarySuffix = ".ndk"
 
 	ndkKnownLibsKey = android.NewOnceKey("ndkKnownLibsKey")
 	// protects ndkKnownLibs writes during parallel BeginMutator.
 	ndkKnownLibsLock sync.Mutex
+
+	stubImplementation = dependencyTag{name: "stubImplementation"}
 )
 
 // The First_version and Unversioned_until properties of this struct should not
@@ -89,6 +107,8 @@
 	versionScriptPath     android.ModuleGenPath
 	parsedCoverageXmlPath android.ModuleOutPath
 	installPath           android.Path
+	abiDumpPath           android.OutputPath
+	abiDiffPaths          android.Paths
 
 	apiLevel         android.ApiLevel
 	firstVersion     android.ApiLevel
@@ -123,6 +143,10 @@
 	if !ctx.Module().Enabled() {
 		return nil
 	}
+	if ctx.Target().NativeBridge == android.NativeBridgeEnabled {
+		ctx.Module().Disable()
+		return nil
+	}
 	firstVersion, err := nativeApiLevelFromUser(ctx,
 		String(this.properties.First_version))
 	if err != nil {
@@ -204,48 +228,179 @@
 	return addStubLibraryCompilerFlags(flags)
 }
 
-func compileStubLibrary(ctx ModuleContext, flags Flags, symbolFile, apiLevel, genstubFlags string) (Objects, android.ModuleGenPath) {
-	arch := ctx.Arch().ArchType.String()
+type ndkApiOutputs struct {
+	stubSrc       android.ModuleGenPath
+	versionScript android.ModuleGenPath
+	symbolList    android.ModuleGenPath
+}
+
+func parseNativeAbiDefinition(ctx ModuleContext, symbolFile string,
+	apiLevel android.ApiLevel, genstubFlags string) ndkApiOutputs {
 
 	stubSrcPath := android.PathForModuleGen(ctx, "stub.c")
 	versionScriptPath := android.PathForModuleGen(ctx, "stub.map")
 	symbolFilePath := android.PathForModuleSrc(ctx, symbolFile)
+	symbolListPath := android.PathForModuleGen(ctx, "abi_symbol_list.txt")
 	apiLevelsJson := android.GetApiLevelsJson(ctx)
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        genStubSrc,
 		Description: "generate stubs " + symbolFilePath.Rel(),
-		Outputs:     []android.WritablePath{stubSrcPath, versionScriptPath},
-		Input:       symbolFilePath,
-		Implicits:   []android.Path{apiLevelsJson},
+		Outputs: []android.WritablePath{stubSrcPath, versionScriptPath,
+			symbolListPath},
+		Input:     symbolFilePath,
+		Implicits: []android.Path{apiLevelsJson},
 		Args: map[string]string{
-			"arch":     arch,
-			"apiLevel": apiLevel,
+			"arch":     ctx.Arch().ArchType.String(),
+			"apiLevel": apiLevel.String(),
 			"apiMap":   apiLevelsJson.String(),
 			"flags":    genstubFlags,
 		},
 	})
 
-	subdir := ""
-	srcs := []android.Path{stubSrcPath}
-	return compileObjs(ctx, flagsToBuilderFlags(flags), subdir, srcs, nil, nil), versionScriptPath
+	return ndkApiOutputs{
+		stubSrc:       stubSrcPath,
+		versionScript: versionScriptPath,
+		symbolList:    symbolListPath,
+	}
 }
 
-func parseSymbolFileForCoverage(ctx ModuleContext, symbolFile string) android.ModuleOutPath {
-	apiLevelsJson := android.GetApiLevelsJson(ctx)
-	symbolFilePath := android.PathForModuleSrc(ctx, symbolFile)
-	outputFileName := strings.Split(symbolFilePath.Base(), ".")[0]
-	parsedApiCoveragePath := android.PathForModuleOut(ctx, outputFileName+".xml")
+func compileStubLibrary(ctx ModuleContext, flags Flags, src android.Path) Objects {
+	return compileObjs(ctx, flagsToBuilderFlags(flags), "",
+		android.Paths{src}, nil, nil)
+}
+
+func (this *stubDecorator) findImplementationLibrary(ctx ModuleContext) android.Path {
+	dep := ctx.GetDirectDepWithTag(strings.TrimSuffix(ctx.ModuleName(), ndkLibrarySuffix),
+		stubImplementation)
+	if dep == nil {
+		ctx.ModuleErrorf("Could not find implementation for stub")
+		return nil
+	}
+	impl, ok := dep.(*Module)
+	if !ok {
+		ctx.ModuleErrorf("Implementation for stub is not correct module type")
+	}
+	output := impl.UnstrippedOutputFile()
+	if output == nil {
+		ctx.ModuleErrorf("implementation module (%s) has no output", impl)
+		return nil
+	}
+
+	return output
+}
+
+func (this *stubDecorator) libraryName(ctx ModuleContext) string {
+	return strings.TrimSuffix(ctx.ModuleName(), ndkLibrarySuffix)
+}
+
+func (this *stubDecorator) findPrebuiltAbiDump(ctx ModuleContext,
+	apiLevel android.ApiLevel) android.OptionalPath {
+
+	subpath := filepath.Join("prebuilts/abi-dumps/ndk", apiLevel.String(),
+		ctx.Arch().ArchType.String(), this.libraryName(ctx), "abi.xml")
+	return android.ExistentPathForSource(ctx, subpath)
+}
+
+// Feature flag.
+func canDumpAbi() bool {
+	return runtime.GOOS != "darwin"
+}
+
+// Feature flag to disable diffing against prebuilts.
+func canDiffAbi() bool {
+	return false
+}
+
+func (this *stubDecorator) dumpAbi(ctx ModuleContext, symbolList android.Path) {
+	implementationLibrary := this.findImplementationLibrary(ctx)
+	this.abiDumpPath = getNdkAbiDumpInstallBase(ctx).Join(ctx,
+		this.apiLevel.String(), ctx.Arch().ArchType.String(),
+		this.libraryName(ctx), "abi.xml")
 	ctx.Build(pctx, android.BuildParams{
-		Rule:        parseNdkApiRule,
-		Description: "parse ndk api symbol file for api coverage: " + symbolFilePath.Rel(),
-		Outputs:     []android.WritablePath{parsedApiCoveragePath},
-		Input:       symbolFilePath,
-		Implicits:   []android.Path{apiLevelsJson},
+		Rule:        abidw,
+		Description: fmt.Sprintf("abidw %s", implementationLibrary),
+		Output:      this.abiDumpPath,
+		Input:       implementationLibrary,
+		Implicit:    symbolList,
 		Args: map[string]string{
-			"apiMap": apiLevelsJson.String(),
+			"symbolList": symbolList.String(),
 		},
 	})
-	return parsedApiCoveragePath
+}
+
+func findNextApiLevel(ctx ModuleContext, apiLevel android.ApiLevel) *android.ApiLevel {
+	apiLevels := append(ctx.Config().AllSupportedApiLevels(),
+		android.FutureApiLevel)
+	for _, api := range apiLevels {
+		if api.GreaterThan(apiLevel) {
+			return &api
+		}
+	}
+	return nil
+}
+
+func (this *stubDecorator) diffAbi(ctx ModuleContext) {
+	missingPrebuiltError := fmt.Sprintf(
+		"Did not find prebuilt ABI dump for %q. Generate with "+
+			"//development/tools/ndk/update_ndk_abi.sh.", this.libraryName(ctx))
+
+	// Catch any ABI changes compared to the checked-in definition of this API
+	// level.
+	abiDiffPath := android.PathForModuleOut(ctx, "abidiff.timestamp")
+	prebuiltAbiDump := this.findPrebuiltAbiDump(ctx, this.apiLevel)
+	if !prebuiltAbiDump.Valid() {
+		ctx.Build(pctx, android.BuildParams{
+			Rule:   android.ErrorRule,
+			Output: abiDiffPath,
+			Args: map[string]string{
+				"error": missingPrebuiltError,
+			},
+		})
+	} else {
+		ctx.Build(pctx, android.BuildParams{
+			Rule: abidiff,
+			Description: fmt.Sprintf("abidiff %s %s", prebuiltAbiDump,
+				this.abiDumpPath),
+			Output: abiDiffPath,
+			Inputs: android.Paths{prebuiltAbiDump.Path(), this.abiDumpPath},
+		})
+	}
+	this.abiDiffPaths = append(this.abiDiffPaths, abiDiffPath)
+
+	// Also ensure that the ABI of the next API level (if there is one) matches
+	// this API level. *New* ABI is allowed, but any changes to APIs that exist
+	// in this API level are disallowed.
+	if !this.apiLevel.IsCurrent() {
+		nextApiLevel := findNextApiLevel(ctx, this.apiLevel)
+		if nextApiLevel == nil {
+			panic(fmt.Errorf("could not determine which API level follows "+
+				"non-current API level %s", this.apiLevel))
+		}
+		nextAbiDiffPath := android.PathForModuleOut(ctx,
+			"abidiff_next.timestamp")
+		nextAbiDump := this.findPrebuiltAbiDump(ctx, *nextApiLevel)
+		if !nextAbiDump.Valid() {
+			ctx.Build(pctx, android.BuildParams{
+				Rule:   android.ErrorRule,
+				Output: nextAbiDiffPath,
+				Args: map[string]string{
+					"error": missingPrebuiltError,
+				},
+			})
+		} else {
+			ctx.Build(pctx, android.BuildParams{
+				Rule: abidiff,
+				Description: fmt.Sprintf("abidiff %s %s", this.abiDumpPath,
+					nextAbiDump),
+				Output: nextAbiDiffPath,
+				Inputs: android.Paths{this.abiDumpPath, nextAbiDump.Path()},
+				Args: map[string]string{
+					"args": "--no-added-syms",
+				},
+			})
+		}
+		this.abiDiffPaths = append(this.abiDiffPaths, nextAbiDiffPath)
+	}
 }
 
 func (c *stubDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
@@ -264,11 +419,17 @@
 	}
 
 	symbolFile := String(c.properties.Symbol_file)
-	objs, versionScript := compileStubLibrary(ctx, flags, symbolFile,
-		c.apiLevel.String(), "")
-	c.versionScriptPath = versionScript
+	nativeAbiResult := parseNativeAbiDefinition(ctx, symbolFile, c.apiLevel, "")
+	objs := compileStubLibrary(ctx, flags, nativeAbiResult.stubSrc)
+	c.versionScriptPath = nativeAbiResult.versionScript
+	if canDumpAbi() {
+		c.dumpAbi(ctx, nativeAbiResult.symbolList)
+		if canDiffAbi() {
+			c.diffAbi(ctx)
+		}
+	}
 	if c.apiLevel.IsCurrent() && ctx.PrimaryArch() {
-		c.parsedCoverageXmlPath = parseSymbolFileForCoverage(ctx, symbolFile)
+		c.parsedCoverageXmlPath = parseSymbolFileForAPICoverage(ctx, symbolFile)
 	}
 	return objs
 }
diff --git a/cc/ndk_prebuilt.go b/cc/ndk_prebuilt.go
index b91c737..51ec6b8 100644
--- a/cc/ndk_prebuilt.go
+++ b/cc/ndk_prebuilt.go
@@ -186,9 +186,8 @@
 		})
 	} else {
 		ctx.SetProvider(SharedLibraryInfoProvider, SharedLibraryInfo{
-			SharedLibrary:           lib,
-			UnstrippedSharedLibrary: lib,
-			Target:                  ctx.Target(),
+			SharedLibrary: lib,
+			Target:        ctx.Target(),
 		})
 	}
 
diff --git a/cc/ndk_sysroot.go b/cc/ndk_sysroot.go
index d8c500e..2726b1a 100644
--- a/cc/ndk_sysroot.go
+++ b/cc/ndk_sysroot.go
@@ -146,16 +146,20 @@
 
 	baseDepPaths := append(installPaths, combinedLicense)
 
-	// There's a dummy "ndk" rule defined in ndk/Android.mk that depends on
-	// this. `m ndk` will build the sysroots.
 	ctx.Build(pctx, android.BuildParams{
-		Rule:      android.Touch,
-		Output:    getNdkBaseTimestampFile(ctx),
-		Implicits: baseDepPaths,
+		Rule:       android.Touch,
+		Output:     getNdkBaseTimestampFile(ctx),
+		Implicits:  baseDepPaths,
+		Validation: getNdkAbiDiffTimestampFile(ctx),
 	})
 
 	fullDepPaths := append(staticLibInstallPaths, getNdkBaseTimestampFile(ctx))
 
+	// There's a phony "ndk" rule defined in core/main.mk that depends on this.
+	// `m ndk` will build the sysroots for the architectures in the current
+	// lunch target. `build/soong/scripts/build-ndk-prebuilts.sh` will build the
+	// sysroots for all the NDK architectures and package them so they can be
+	// imported into the NDK's build.
 	ctx.Build(pctx, android.BuildParams{
 		Rule:      android.Touch,
 		Output:    getNdkFullTimestampFile(ctx),
diff --git a/cc/ndkstubgen/__init__.py b/cc/ndkstubgen/__init__.py
index 86bf6ff..5e6b8f5 100755
--- a/cc/ndkstubgen/__init__.py
+++ b/cc/ndkstubgen/__init__.py
@@ -18,7 +18,7 @@
 import argparse
 import json
 import logging
-import os
+from pathlib import Path
 import sys
 from typing import Iterable, TextIO
 
@@ -28,10 +28,12 @@
 
 class Generator:
     """Output generator that writes stub source files and version scripts."""
-    def __init__(self, src_file: TextIO, version_script: TextIO, arch: Arch,
-                 api: int, llndk: bool, apex: bool) -> None:
+    def __init__(self, src_file: TextIO, version_script: TextIO,
+                 symbol_list: TextIO, arch: Arch, api: int, llndk: bool,
+                 apex: bool) -> None:
         self.src_file = src_file
         self.version_script = version_script
+        self.symbol_list = symbol_list
         self.arch = arch
         self.api = api
         self.llndk = llndk
@@ -39,6 +41,7 @@
 
     def write(self, versions: Iterable[Version]) -> None:
         """Writes all symbol data to the output files."""
+        self.symbol_list.write('[abi_symbol_list]\n')
         for version in versions:
             self.write_version(version)
 
@@ -76,11 +79,11 @@
                     weak = '__attribute__((weak)) '
 
                 if 'var' in symbol.tags:
-                    self.src_file.write('{}int {} = 0;\n'.format(
-                        weak, symbol.name))
+                    self.src_file.write(f'{weak}int {symbol.name} = 0;\n')
                 else:
-                    self.src_file.write('{}void {}() {{}}\n'.format(
-                        weak, symbol.name))
+                    self.src_file.write(f'{weak}void {symbol.name}() {{}}\n')
+
+                self.symbol_list.write(f'{symbol.name}\n')
 
             if not version_empty and section_versioned:
                 base = '' if version.base is None else ' ' + version.base
@@ -91,6 +94,10 @@
     """Parses and returns command line arguments."""
     parser = argparse.ArgumentParser()
 
+    def resolved_path(raw: str) -> Path:
+        """Returns a resolved Path for the given string."""
+        return Path(raw).resolve()
+
     parser.add_argument('-v', '--verbose', action='count', default=0)
 
     parser.add_argument(
@@ -101,28 +108,32 @@
     parser.add_argument(
         '--llndk', action='store_true', help='Use the LLNDK variant.')
     parser.add_argument(
-        '--apex', action='store_true', help='Use the APEX variant.')
+        '--apex',
+        action='store_true',
+        help='Use the APEX variant. Note: equivalent to --system-api.')
+    parser.add_argument(
+        '--system-api',
+        action='store_true',
+        dest='apex',
+        help='Use the SystemAPI variant. Note: equivalent to --apex.')
 
-    # https://github.com/python/mypy/issues/1317
-    # mypy has issues with using os.path.realpath as an argument here.
-    parser.add_argument(
-        '--api-map',
-        type=os.path.realpath,  # type: ignore
-        required=True,
-        help='Path to the API level map JSON file.')
+    parser.add_argument('--api-map',
+                        type=resolved_path,
+                        required=True,
+                        help='Path to the API level map JSON file.')
 
-    parser.add_argument(
-        'symbol_file',
-        type=os.path.realpath,  # type: ignore
-        help='Path to symbol file.')
-    parser.add_argument(
-        'stub_src',
-        type=os.path.realpath,  # type: ignore
-        help='Path to output stub source file.')
-    parser.add_argument(
-        'version_script',
-        type=os.path.realpath,  # type: ignore
-        help='Path to output version script.')
+    parser.add_argument('symbol_file',
+                        type=resolved_path,
+                        help='Path to symbol file.')
+    parser.add_argument('stub_src',
+                        type=resolved_path,
+                        help='Path to output stub source file.')
+    parser.add_argument('version_script',
+                        type=resolved_path,
+                        help='Path to output version script.')
+    parser.add_argument('symbol_list',
+                        type=resolved_path,
+                        help='Path to output abigail symbol list.')
 
     return parser.parse_args()
 
@@ -131,7 +142,7 @@
     """Program entry point."""
     args = parse_args()
 
-    with open(args.api_map) as map_file:
+    with args.api_map.open() as map_file:
         api_map = json.load(map_file)
     api = symbolfile.decode_api_level(args.api, api_map)
 
@@ -141,19 +152,20 @@
         verbosity = 2
     logging.basicConfig(level=verbose_map[verbosity])
 
-    with open(args.symbol_file) as symbol_file:
+    with args.symbol_file.open() as symbol_file:
         try:
             versions = symbolfile.SymbolFileParser(symbol_file, api_map,
                                                    args.arch, api, args.llndk,
                                                    args.apex).parse()
         except symbolfile.MultiplyDefinedSymbolError as ex:
-            sys.exit('{}: error: {}'.format(args.symbol_file, ex))
+            sys.exit(f'{args.symbol_file}: error: {ex}')
 
-    with open(args.stub_src, 'w') as src_file:
-        with open(args.version_script, 'w') as version_file:
-            generator = Generator(src_file, version_file, args.arch, api,
-                                  args.llndk, args.apex)
-            generator.write(versions)
+    with args.stub_src.open('w') as src_file:
+        with args.version_script.open('w') as version_script:
+            with args.symbol_list.open('w') as symbol_list:
+                generator = Generator(src_file, version_script, symbol_list,
+                                      args.arch, api, args.llndk, args.apex)
+                generator.write(versions)
 
 
 if __name__ == '__main__':
diff --git a/cc/ndkstubgen/test_ndkstubgen.py b/cc/ndkstubgen/test_ndkstubgen.py
index 6d2c9d6..c8cd056 100755
--- a/cc/ndkstubgen/test_ndkstubgen.py
+++ b/cc/ndkstubgen/test_ndkstubgen.py
@@ -19,9 +19,10 @@
 import textwrap
 import unittest
 
-import ndkstubgen
 import symbolfile
-from symbolfile import Arch, Tag
+from symbolfile import Arch, Tags
+
+import ndkstubgen
 
 
 # pylint: disable=missing-docstring
@@ -33,26 +34,30 @@
         # OmitVersionTest, PrivateVersionTest, and SymbolPresenceTest.
         src_file = io.StringIO()
         version_file = io.StringIO()
-        generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
-                                         9, False, False)
+        symbol_list_file = io.StringIO()
+        generator = ndkstubgen.Generator(src_file,
+                                         version_file, symbol_list_file,
+                                         Arch('arm'), 9, False, False)
 
-        version = symbolfile.Version('VERSION_PRIVATE', None, [], [
-            symbolfile.Symbol('foo', []),
+        version = symbolfile.Version('VERSION_PRIVATE', None, Tags(), [
+            symbolfile.Symbol('foo', Tags()),
         ])
         generator.write_version(version)
         self.assertEqual('', src_file.getvalue())
         self.assertEqual('', version_file.getvalue())
 
-        version = symbolfile.Version('VERSION', None, [Tag('x86')], [
-            symbolfile.Symbol('foo', []),
-        ])
+        version = symbolfile.Version('VERSION', None, Tags.from_strs(['x86']),
+                                     [
+                                         symbolfile.Symbol('foo', Tags()),
+                                     ])
         generator.write_version(version)
         self.assertEqual('', src_file.getvalue())
         self.assertEqual('', version_file.getvalue())
 
-        version = symbolfile.Version('VERSION', None, [Tag('introduced=14')], [
-            symbolfile.Symbol('foo', []),
-        ])
+        version = symbolfile.Version('VERSION', None,
+                                     Tags.from_strs(['introduced=14']), [
+                                         symbolfile.Symbol('foo', Tags()),
+                                     ])
         generator.write_version(version)
         self.assertEqual('', src_file.getvalue())
         self.assertEqual('', version_file.getvalue())
@@ -62,32 +67,34 @@
         # SymbolPresenceTest.
         src_file = io.StringIO()
         version_file = io.StringIO()
-        generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
-                                         9, False, False)
+        symbol_list_file = io.StringIO()
+        generator = ndkstubgen.Generator(src_file,
+                                         version_file, symbol_list_file,
+                                         Arch('arm'), 9, False, False)
 
-        version = symbolfile.Version('VERSION_1', None, [], [
-            symbolfile.Symbol('foo', [Tag('x86')]),
+        version = symbolfile.Version('VERSION_1', None, Tags(), [
+            symbolfile.Symbol('foo', Tags.from_strs(['x86'])),
         ])
         generator.write_version(version)
         self.assertEqual('', src_file.getvalue())
         self.assertEqual('', version_file.getvalue())
 
-        version = symbolfile.Version('VERSION_1', None, [], [
-            symbolfile.Symbol('foo', [Tag('introduced=14')]),
+        version = symbolfile.Version('VERSION_1', None, Tags(), [
+            symbolfile.Symbol('foo', Tags.from_strs(['introduced=14'])),
         ])
         generator.write_version(version)
         self.assertEqual('', src_file.getvalue())
         self.assertEqual('', version_file.getvalue())
 
-        version = symbolfile.Version('VERSION_1', None, [], [
-            symbolfile.Symbol('foo', [Tag('llndk')]),
+        version = symbolfile.Version('VERSION_1', None, Tags(), [
+            symbolfile.Symbol('foo', Tags.from_strs(['llndk'])),
         ])
         generator.write_version(version)
         self.assertEqual('', src_file.getvalue())
         self.assertEqual('', version_file.getvalue())
 
-        version = symbolfile.Version('VERSION_1', None, [], [
-            symbolfile.Symbol('foo', [Tag('apex')]),
+        version = symbolfile.Version('VERSION_1', None, Tags(), [
+            symbolfile.Symbol('foo', Tags.from_strs(['apex'])),
         ])
         generator.write_version(version)
         self.assertEqual('', src_file.getvalue())
@@ -96,22 +103,23 @@
     def test_write(self) -> None:
         src_file = io.StringIO()
         version_file = io.StringIO()
-        generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
-                                         9, False, False)
+        symbol_list_file = io.StringIO()
+        generator = ndkstubgen.Generator(src_file,
+                                         version_file, symbol_list_file,
+                                         Arch('arm'), 9, False, False)
 
         versions = [
-            symbolfile.Version('VERSION_1', None, [], [
-                symbolfile.Symbol('foo', []),
-                symbolfile.Symbol('bar', [Tag('var')]),
-                symbolfile.Symbol('woodly', [Tag('weak')]),
-                symbolfile.Symbol('doodly',
-                                  [Tag('weak'), Tag('var')]),
+            symbolfile.Version('VERSION_1', None, Tags(), [
+                symbolfile.Symbol('foo', Tags()),
+                symbolfile.Symbol('bar', Tags.from_strs(['var'])),
+                symbolfile.Symbol('woodly', Tags.from_strs(['weak'])),
+                symbolfile.Symbol('doodly', Tags.from_strs(['weak', 'var'])),
             ]),
-            symbolfile.Version('VERSION_2', 'VERSION_1', [], [
-                symbolfile.Symbol('baz', []),
+            symbolfile.Version('VERSION_2', 'VERSION_1', Tags(), [
+                symbolfile.Symbol('baz', Tags()),
             ]),
-            symbolfile.Version('VERSION_3', 'VERSION_1', [], [
-                symbolfile.Symbol('qux', [Tag('versioned=14')]),
+            symbolfile.Version('VERSION_3', 'VERSION_1', Tags(), [
+                symbolfile.Symbol('qux', Tags.from_strs(['versioned=14'])),
             ]),
         ]
 
@@ -141,6 +149,17 @@
         """)
         self.assertEqual(expected_version, version_file.getvalue())
 
+        expected_allowlist = textwrap.dedent("""\
+            [abi_symbol_list]
+            foo
+            bar
+            woodly
+            doodly
+            baz
+            qux
+        """)
+        self.assertEqual(expected_allowlist, symbol_list_file.getvalue())
+
 
 class IntegrationTest(unittest.TestCase):
     def test_integration(self) -> None:
@@ -186,8 +205,10 @@
 
         src_file = io.StringIO()
         version_file = io.StringIO()
-        generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
-                                         9, False, False)
+        symbol_list_file = io.StringIO()
+        generator = ndkstubgen.Generator(src_file,
+                                         version_file, symbol_list_file,
+                                         Arch('arm'), 9, False, False)
         generator.write(versions)
 
         expected_src = textwrap.dedent("""\
@@ -215,6 +236,16 @@
         """)
         self.assertEqual(expected_version, version_file.getvalue())
 
+        expected_allowlist = textwrap.dedent("""\
+            [abi_symbol_list]
+            foo
+            baz
+            qux
+            wibble
+            wobble
+        """)
+        self.assertEqual(expected_allowlist, symbol_list_file.getvalue())
+
     def test_integration_future_api(self) -> None:
         api_map = {
             'O': 9000,
@@ -238,8 +269,10 @@
 
         src_file = io.StringIO()
         version_file = io.StringIO()
-        generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
-                                         9001, False, False)
+        symbol_list_file = io.StringIO()
+        generator = ndkstubgen.Generator(src_file,
+                                         version_file, symbol_list_file,
+                                         Arch('arm'), 9001, False, False)
         generator.write(versions)
 
         expected_src = textwrap.dedent("""\
@@ -257,6 +290,13 @@
         """)
         self.assertEqual(expected_version, version_file.getvalue())
 
+        expected_allowlist = textwrap.dedent("""\
+            [abi_symbol_list]
+            foo
+            bar
+        """)
+        self.assertEqual(expected_allowlist, symbol_list_file.getvalue())
+
     def test_multiple_definition(self) -> None:
         input_file = io.StringIO(textwrap.dedent("""\
             VERSION_1 {
@@ -336,8 +376,10 @@
 
         src_file = io.StringIO()
         version_file = io.StringIO()
-        generator = ndkstubgen.Generator(src_file, version_file, Arch('arm'),
-                                         9, False, True)
+        symbol_list_file = io.StringIO()
+        generator = ndkstubgen.Generator(src_file,
+                                         version_file, symbol_list_file,
+                                         Arch('arm'), 9, False, True)
         generator.write(versions)
 
         expected_src = textwrap.dedent("""\
@@ -371,6 +413,40 @@
         """)
         self.assertEqual(expected_version, version_file.getvalue())
 
+    def test_empty_stub(self) -> None:
+        """Tests that empty stubs can be generated.
+
+        This is not a common case, but libraries whose only behavior is to
+        interpose symbols to alter existing behavior do not need to expose
+        their interposing symbols as API, so it's possible for the stub to be
+        empty while still needing a stub to link against. libsigchain is an
+        example of this.
+        """
+        input_file = io.StringIO(textwrap.dedent("""\
+            VERSION_1 {
+                local:
+                    *;
+            };
+        """))
+        parser = symbolfile.SymbolFileParser(input_file, {}, Arch('arm'),
+                                             9, llndk=False, apex=True)
+        versions = parser.parse()
+
+        src_file = io.StringIO()
+        version_file = io.StringIO()
+        symbol_list_file = io.StringIO()
+        generator = ndkstubgen.Generator(src_file,
+                                         version_file,
+                                         symbol_list_file,
+                                         Arch('arm'),
+                                         9,
+                                         llndk=False,
+                                         apex=True)
+        generator.write(versions)
+
+        self.assertEqual('', src_file.getvalue())
+        self.assertEqual('', version_file.getvalue())
+
 
 def main() -> None:
     suite = unittest.TestLoader().loadTestsFromName(__name__)
diff --git a/cc/object.go b/cc/object.go
index d8f1aba..606e368 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -47,12 +47,12 @@
 }
 
 type objectBazelHandler struct {
-	bazelHandler
+	android.BazelHandler
 
 	module *Module
 }
 
-func (handler *objectBazelHandler) generateBazelBuildActions(ctx android.ModuleContext, label string) bool {
+func (handler *objectBazelHandler) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool {
 	bazelCtx := ctx.Config().BazelContext
 	objPaths, ok := bazelCtx.GetOutputFiles(label, ctx.Arch().ArchType)
 	if ok {
@@ -67,9 +67,19 @@
 }
 
 type ObjectLinkerProperties struct {
+	// list of static library modules that should only provide headers for this module.
+	Static_libs []string `android:"arch_variant,variant_prepend"`
+
+	// list of shared library modules should only provide headers for this module.
+	Shared_libs []string `android:"arch_variant"`
+
 	// list of modules that should only provide headers for this module.
 	Header_libs []string `android:"arch_variant,variant_prepend"`
 
+	// list of default libraries that will provide headers for this module.  If unset, generally
+	// defaults to libc, libm, and libdl.  Set to [] to prevent using headers from the defaults.
+	System_shared_libs []string `android:"arch_variant"`
+
 	// names of other cc_object modules to link into this module using partial linking
 	Objs []string `android:"arch_variant"`
 
@@ -84,8 +94,8 @@
 	Crt *bool
 }
 
-func newObject() *Module {
-	module := newBaseModule(android.HostAndDeviceSupported, android.MultilibBoth)
+func newObject(hod android.HostOrDeviceSupported) *Module {
+	module := newBaseModule(hod, android.MultilibBoth)
 	module.sanitize = &sanitize{}
 	module.stl = &stl{}
 	return module
@@ -95,7 +105,7 @@
 // necessary, but sometimes used to generate .s files from .c files to use as
 // input to a cc_genrule module.
 func ObjectFactory() android.Module {
-	module := newObject()
+	module := newObject(android.HostAndDeviceSupported)
 	module.linker = &objectLinker{
 		baseLinker: NewBaseLinker(module.sanitize),
 	}
@@ -113,28 +123,11 @@
 // For bp2build conversion.
 type bazelObjectAttributes struct {
 	Srcs    bazel.LabelListAttribute
+	Srcs_as bazel.LabelListAttribute
 	Hdrs    bazel.LabelListAttribute
 	Deps    bazel.LabelListAttribute
 	Copts   bazel.StringListAttribute
-	Asflags []string
-}
-
-type bazelObject struct {
-	android.BazelTargetModuleBase
-	bazelObjectAttributes
-}
-
-func (m *bazelObject) Name() string {
-	return m.BaseModuleName()
-}
-
-func (m *bazelObject) GenerateAndroidBuildActions(ctx android.ModuleContext) {}
-
-func BazelObjectFactory() android.Module {
-	module := &bazelObject{}
-	module.AddProperties(&module.bazelObjectAttributes)
-	android.InitBazelTargetModule(module)
-	return module
+	Asflags bazel.StringListAttribute
 }
 
 // ObjectBp2Build is the bp2build converter from cc_object modules to the
@@ -157,8 +150,6 @@
 
 	// Set arch-specific configurable attributes
 	compilerAttrs := bp2BuildParseCompilerProps(ctx, m)
-	var asFlags []string
-
 	var deps bazel.LabelListAttribute
 	for _, props := range m.linker.linkerProps() {
 		if objectLinkerProps, ok := props.(*ObjectLinkerProperties); ok {
@@ -167,25 +158,20 @@
 		}
 	}
 
-	productVariableProps := android.ProductVariableProperties(ctx)
-	if props, exists := productVariableProps["Asflags"]; exists {
-		// TODO(b/183595873): consider deduplicating handling of product variable properties
-		for _, prop := range props {
-			flags, ok := prop.Property.([]string)
-			if !ok {
-				ctx.ModuleErrorf("Could not convert product variable asflag property")
-				return
-			}
-			// TODO(b/183595873) handle other product variable usages -- as selects?
-			if newFlags, subbed := bazel.TryVariableSubstitutions(flags, prop.ProductConfigVariable); subbed {
-				asFlags = append(asFlags, newFlags...)
-			}
-		}
+	// Don't split cc_object srcs across languages. Doing so would add complexity,
+	// and this isn't typically done for cc_object.
+	srcs := compilerAttrs.srcs
+	srcs.Append(compilerAttrs.cSrcs)
+
+	asFlags := compilerAttrs.asFlags
+	if compilerAttrs.asSrcs.IsEmpty() {
+		// Skip asflags for BUILD file simplicity if there are no assembly sources.
+		asFlags = bazel.MakeStringListAttribute(nil)
 	}
-	// TODO(b/183595872) warn/error if we're not handling product variables
 
 	attrs := &bazelObjectAttributes{
-		Srcs:    compilerAttrs.srcs,
+		Srcs:    srcs,
+		Srcs_as: compilerAttrs.asSrcs,
 		Deps:    deps,
 		Copts:   compilerAttrs.copts,
 		Asflags: asFlags,
@@ -196,7 +182,7 @@
 		Bzl_load_location: "//build/bazel/rules:cc_object.bzl",
 	}
 
-	ctx.CreateBazelTargetModule(BazelObjectFactory, m.Name(), props, attrs)
+	ctx.CreateBazelTargetModule(m.Name(), props, attrs)
 }
 
 func (object *objectLinker) appendLdflags(flags []string) {
@@ -211,12 +197,23 @@
 
 func (object *objectLinker) linkerDeps(ctx DepsContext, deps Deps) Deps {
 	deps.HeaderLibs = append(deps.HeaderLibs, object.Properties.Header_libs...)
+	deps.SharedLibs = append(deps.SharedLibs, object.Properties.Shared_libs...)
+	deps.StaticLibs = append(deps.StaticLibs, object.Properties.Static_libs...)
 	deps.ObjFiles = append(deps.ObjFiles, object.Properties.Objs...)
+
+	deps.SystemSharedLibs = object.Properties.System_shared_libs
+	if deps.SystemSharedLibs == nil {
+		// Provide a default set of shared libraries if system_shared_libs is unspecified.
+		// Note: If an empty list [] is specified, it implies that the module declines the
+		// default shared libraries.
+		deps.SystemSharedLibs = append(deps.SystemSharedLibs, ctx.toolchain().DefaultSharedLibraries()...)
+	}
+	deps.LateSharedLibs = append(deps.LateSharedLibs, deps.SystemSharedLibs...)
 	return deps
 }
 
 func (object *objectLinker) linkerFlags(ctx ModuleContext, flags Flags) Flags {
-	flags.Global.LdFlags = append(flags.Global.LdFlags, ctx.toolchain().ToolchainClangLdflags())
+	flags.Global.LdFlags = append(flags.Global.LdFlags, ctx.toolchain().ToolchainLdflags())
 
 	if lds := android.OptionalPathForModuleSrc(ctx, object.Properties.Linker_script); lds.Valid() {
 		flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-T,"+lds.String())
@@ -260,6 +257,20 @@
 	return outputFile
 }
 
+func (object *objectLinker) linkerSpecifiedDeps(specifiedDeps specifiedDeps) specifiedDeps {
+	specifiedDeps.sharedLibs = append(specifiedDeps.sharedLibs, object.Properties.Shared_libs...)
+
+	// Must distinguish nil and [] in system_shared_libs - ensure that [] in
+	// either input list doesn't come out as nil.
+	if specifiedDeps.systemSharedLibs == nil {
+		specifiedDeps.systemSharedLibs = object.Properties.System_shared_libs
+	} else {
+		specifiedDeps.systemSharedLibs = append(specifiedDeps.systemSharedLibs, object.Properties.System_shared_libs...)
+	}
+
+	return specifiedDeps
+}
+
 func (object *objectLinker) unstrippedOutputFilePath() android.Path {
 	return nil
 }
diff --git a/cc/object_test.go b/cc/object_test.go
index 0e5508a..259a892 100644
--- a/cc/object_test.go
+++ b/cc/object_test.go
@@ -15,8 +15,9 @@
 package cc
 
 import (
-	"android/soong/android"
 	"testing"
+
+	"android/soong/android"
 )
 
 func TestMinSdkVersionsOfCrtObjects(t *testing.T) {
@@ -27,24 +28,23 @@
 			crt: true,
 			stl: "none",
 			min_sdk_version: "28",
-
+			vendor_available: true,
 		}`)
 
-	arch := "android_arm64_armv8-a"
-	for _, v := range []string{"", "28", "29", "30", "current"} {
-		var variant string
-		// platform variant
-		if v == "" {
-			variant = arch
-		} else {
-			variant = arch + "_sdk_" + v
-		}
-		cflags := ctx.ModuleForTests("crt_foo", variant).Rule("cc").Args["cFlags"]
-		vNum := v
-		if v == "current" || v == "" {
-			vNum = "10000"
-		}
-		expected := "-target aarch64-linux-android" + vNum + " "
+	variants := []struct {
+		variant string
+		num     string
+	}{
+		{"android_arm64_armv8-a", "10000"},
+		{"android_arm64_armv8-a_sdk_28", "28"},
+		{"android_arm64_armv8-a_sdk_29", "29"},
+		{"android_arm64_armv8-a_sdk_30", "30"},
+		{"android_arm64_armv8-a_sdk_current", "10000"},
+		{"android_vendor.29_arm64_armv8-a", "29"},
+	}
+	for _, v := range variants {
+		cflags := ctx.ModuleForTests("crt_foo", v.variant).Rule("cc").Args["cFlags"]
+		expected := "-target aarch64-linux-android" + v.num + " "
 		android.AssertStringDoesContain(t, "cflag", cflags, expected)
 	}
 }
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index bea1782..d324241 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -15,9 +15,10 @@
 package cc
 
 import (
-	"android/soong/android"
 	"path/filepath"
 	"strings"
+
+	"android/soong/android"
 )
 
 func init() {
@@ -183,9 +184,8 @@
 			})
 
 			ctx.SetProvider(SharedLibraryInfoProvider, SharedLibraryInfo{
-				SharedLibrary:           outputFile,
-				UnstrippedSharedLibrary: p.unstrippedOutputFile,
-				Target:                  ctx.Target(),
+				SharedLibrary: outputFile,
+				Target:        ctx.Target(),
 
 				TableOfContents: p.tocFile,
 			})
@@ -322,13 +322,13 @@
 }
 
 type prebuiltStaticLibraryBazelHandler struct {
-	bazelHandler
+	android.BazelHandler
 
 	module  *Module
 	library *libraryDecorator
 }
 
-func (h *prebuiltStaticLibraryBazelHandler) generateBazelBuildActions(ctx android.ModuleContext, label string) bool {
+func (h *prebuiltStaticLibraryBazelHandler) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool {
 	bazelCtx := ctx.Config().BazelContext
 	ccInfo, ok, err := bazelCtx.GetCcInfo(label, ctx.Arch().ArchType)
 	if err != nil {
@@ -389,8 +389,8 @@
 	return true
 }
 
-func newPrebuiltObject() *Module {
-	module := newObject()
+func NewPrebuiltObject(hod android.HostOrDeviceSupported) *Module {
+	module := newObject(hod)
 	prebuilt := &prebuiltObjectLinker{
 		objectLinker: objectLinker{
 			baseLinker: NewBaseLinker(nil),
@@ -404,7 +404,7 @@
 }
 
 func prebuiltObjectFactory() android.Module {
-	module := newPrebuiltObject()
+	module := NewPrebuiltObject(android.HostAndDeviceSupported)
 	return module.Init()
 }
 
diff --git a/cc/prebuilt_test.go b/cc/prebuilt_test.go
index fa6dd87..c8f8103 100644
--- a/cc/prebuilt_test.go
+++ b/cc/prebuilt_test.go
@@ -15,6 +15,7 @@
 package cc
 
 import (
+	"runtime"
 	"testing"
 
 	"android/soong/android"
@@ -271,8 +272,8 @@
 }
 
 func TestPrebuiltSymlinkedHostBinary(t *testing.T) {
-	if android.BuildOs != android.Linux {
-		t.Skipf("Skipping host prebuilt testing that is only supported on %s not %s", android.Linux, android.BuildOs)
+	if runtime.GOOS != "linux" {
+		t.Skipf("Skipping host prebuilt testing that is only supported on linux not %s", runtime.GOOS)
 	}
 
 	ctx := testPrebuilt(t, `
diff --git a/cc/proto_test.go b/cc/proto_test.go
index b9c89c7..abcb273 100644
--- a/cc/proto_test.go
+++ b/cc/proto_test.go
@@ -51,7 +51,7 @@
 			},
 		}`)
 
-		buildOS := android.BuildOs.String()
+		buildOS := ctx.Config().BuildOS.String()
 
 		proto := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Output("proto/a.pb.cc")
 		foobar := ctx.ModuleForTests("protoc-gen-foobar", buildOS+"_x86_64")
diff --git a/cc/rs.go b/cc/rs.go
index ba69f23..fbc86e2 100644
--- a/cc/rs.go
+++ b/cc/rs.go
@@ -36,7 +36,8 @@
 
 var rsCppCmdLine = strings.Replace(`
 ${rsCmd} -o ${outDir} -d ${outDir} -a ${out} -MD -reflect-c++ ${rsFlags} $in &&
-(echo '${out}: \' && cat ${depFiles} | awk 'start { sub(/( \\)?$$/, " \\"); print } /:/ { start=1 }') > ${out}.d &&
+echo '${out}: \' > ${out}.d &&
+for f in ${depFiles}; do cat $${f} | awk 'start { sub(/( \\)?$$/, " \\"); print } /:/ { start=1 }' >> ${out}.d; done &&
 touch $out
 `, "\n", "", -1)
 
diff --git a/cc/sabi.go b/cc/sabi.go
index 1f331cb..e62ca66 100644
--- a/cc/sabi.go
+++ b/cc/sabi.go
@@ -45,12 +45,6 @@
 	return []interface{}{&sabi.Properties}
 }
 
-func (sabi *sabi) begin(ctx BaseModuleContext) {}
-
-func (sabi *sabi) deps(ctx BaseModuleContext, deps Deps) Deps {
-	return deps
-}
-
 func (sabi *sabi) flags(ctx ModuleContext, flags Flags) Flags {
 	// Filter out flags which libTooling don't understand.
 	// This is here for legacy reasons and future-proof, in case the version of libTooling and clang
@@ -107,10 +101,6 @@
 // Called from sabiDepsMutator to check whether ABI dumps should be created for this module.
 // ctx should be wrapping a native library type module.
 func shouldCreateSourceAbiDumpForLibrary(ctx android.BaseModuleContext) bool {
-	if ctx.Fuchsia() {
-		return false
-	}
-
 	// Only generate ABI dump for device modules.
 	if !ctx.Device() {
 		return false
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 941a955..f6a9d5b 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -21,9 +21,11 @@
 	"sync"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
 	"android/soong/cc/config"
+	"android/soong/snapshot"
 )
 
 var (
@@ -39,7 +41,6 @@
 
 	hwasanCflags = []string{"-fno-omit-frame-pointer", "-Wno-frame-larger-than=",
 		"-fsanitize-hwaddress-abi=platform",
-		"-fno-experimental-new-pass-manager",
 		// The following improves debug location information
 		// availability at the cost of its accuracy. It increases
 		// the likelihood of a stack variable's frame offset
@@ -55,7 +56,7 @@
 	}
 
 	cfiCflags = []string{"-flto", "-fsanitize-cfi-cross-dso",
-		"-fsanitize-blacklist=external/compiler-rt/lib/cfi/cfi_blocklist.txt"}
+		"-fsanitize-ignorelist=external/compiler-rt/lib/cfi/cfi_blocklist.txt"}
 	// -flto and -fvisibility are required by clang when -fsanitize=cfi is
 	// used, but have no effect on assembly files
 	cfiAsflags = []string{"-flto", "-fvisibility=default"}
@@ -63,7 +64,7 @@
 		"-Wl,-plugin-opt,O1"}
 	cfiExportsMapPath = "build/soong/cc/config/cfi_exports.map"
 
-	intOverflowCflags = []string{"-fsanitize-blacklist=build/soong/cc/config/integer_overflow_blocklist.txt"}
+	intOverflowCflags = []string{"-fsanitize-ignorelist=build/soong/cc/config/integer_overflow_blocklist.txt"}
 
 	minimalRuntimeFlags = []string{"-fsanitize-minimal-runtime", "-fno-sanitize-trap=integer,undefined",
 		"-fno-sanitize-recover=integer,undefined"}
@@ -73,25 +74,28 @@
 
 type SanitizerType int
 
-func boolPtr(v bool) *bool {
-	if v {
-		return &v
-	} else {
-		return nil
-	}
-}
-
 const (
 	Asan SanitizerType = iota + 1
 	Hwasan
 	tsan
 	intOverflow
-	cfi
 	scs
 	Fuzzer
 	memtag_heap
+	cfi // cfi is last to prevent it running before incompatible mutators
 )
 
+var Sanitizers = []SanitizerType{
+	Asan,
+	Hwasan,
+	tsan,
+	intOverflow,
+	scs,
+	Fuzzer,
+	memtag_heap,
+	cfi, // cfi is last to prevent it running before incompatible mutators
+}
+
 // Name of the sanitizer variation for this sanitizer type
 func (t SanitizerType) variationName() string {
 	switch t {
@@ -140,6 +144,18 @@
 	}
 }
 
+func (t SanitizerType) registerMutators(ctx android.RegisterMutatorsContext) {
+	switch t {
+	case Asan, Hwasan, Fuzzer, scs, tsan, cfi:
+		ctx.TopDown(t.variationName()+"_deps", sanitizerDepsMutator(t))
+		ctx.BottomUp(t.variationName(), sanitizerMutator(t))
+	case memtag_heap, intOverflow:
+		// do nothing
+	default:
+		panic(fmt.Errorf("unknown SanitizerType %d", t))
+	}
+}
+
 func (*Module) SanitizerSupported(t SanitizerType) bool {
 	switch t {
 	case Asan:
@@ -167,24 +183,46 @@
 }
 
 type SanitizeUserProps struct {
+	// Prevent use of any sanitizers on this module
 	Never *bool `android:"arch_variant"`
 
-	// main sanitizers
-	Address   *bool `android:"arch_variant"`
-	Thread    *bool `android:"arch_variant"`
+	// ASan (Address sanitizer), incompatible with static binaries.
+	// Always runs in a diagnostic mode.
+	// Use of address sanitizer disables cfi sanitizer.
+	// Hwaddress sanitizer takes precedence over this sanitizer.
+	Address *bool `android:"arch_variant"`
+	// TSan (Thread sanitizer), incompatible with static binaries and 32 bit architectures.
+	// Always runs in a diagnostic mode.
+	// Use of thread sanitizer disables cfi and scudo sanitizers.
+	// Hwaddress sanitizer takes precedence over this sanitizer.
+	Thread *bool `android:"arch_variant"`
+	// HWASan (Hardware Address sanitizer).
+	// Use of hwasan sanitizer disables cfi, address, thread, and scudo sanitizers.
 	Hwaddress *bool `android:"arch_variant"`
 
-	// local sanitizers
-	Undefined        *bool    `android:"arch_variant"`
-	All_undefined    *bool    `android:"arch_variant"`
-	Misc_undefined   []string `android:"arch_variant"`
-	Fuzzer           *bool    `android:"arch_variant"`
-	Safestack        *bool    `android:"arch_variant"`
-	Cfi              *bool    `android:"arch_variant"`
-	Integer_overflow *bool    `android:"arch_variant"`
-	Scudo            *bool    `android:"arch_variant"`
-	Scs              *bool    `android:"arch_variant"`
-	Memtag_heap      *bool    `android:"arch_variant"`
+	// Undefined behavior sanitizer
+	All_undefined *bool `android:"arch_variant"`
+	// Subset of undefined behavior sanitizer
+	Undefined *bool `android:"arch_variant"`
+	// List of specific undefined behavior sanitizers to enable
+	Misc_undefined []string `android:"arch_variant"`
+	// Fuzzer, incompatible with static binaries.
+	Fuzzer *bool `android:"arch_variant"`
+	// safe-stack sanitizer, incompatible with 32-bit architectures.
+	Safestack *bool `android:"arch_variant"`
+	// cfi sanitizer, incompatible with asan, hwasan, fuzzer, or Darwin
+	Cfi *bool `android:"arch_variant"`
+	// signed/unsigned integer overflow sanitizer, incompatible with Darwin.
+	Integer_overflow *bool `android:"arch_variant"`
+	// scudo sanitizer, incompatible with asan, hwasan, tsan
+	// This should not be used in Android 11+ : https://source.android.com/devices/tech/debug/scudo
+	// deprecated
+	Scudo *bool `android:"arch_variant"`
+	// shadow-call-stack sanitizer, only available on arm64
+	Scs *bool `android:"arch_variant"`
+	// Memory-tagging, only available on arm64
+	// if diag.memtag unset or false, enables async memory tagging
+	Memtag_heap *bool `android:"arch_variant"`
 
 	// A modifier for ASAN and HWASAN for write only instrumentation
 	Writeonly *bool `android:"arch_variant"`
@@ -193,12 +231,22 @@
 	// Replaces abort() on error with a human-readable error message.
 	// Address and Thread sanitizers always run in diagnostic mode.
 	Diag struct {
-		Undefined        *bool    `android:"arch_variant"`
-		Cfi              *bool    `android:"arch_variant"`
-		Integer_overflow *bool    `android:"arch_variant"`
-		Memtag_heap      *bool    `android:"arch_variant"`
-		Misc_undefined   []string `android:"arch_variant"`
-		No_recover       []string `android:"arch_variant"`
+		// Undefined behavior sanitizer, diagnostic mode
+		Undefined *bool `android:"arch_variant"`
+		// cfi sanitizer, diagnostic mode, incompatible with asan, hwasan, fuzzer, or Darwin
+		Cfi *bool `android:"arch_variant"`
+		// signed/unsigned integer overflow sanitizer, diagnostic mode, incompatible with Darwin.
+		Integer_overflow *bool `android:"arch_variant"`
+		// Memory-tagging, only available on arm64
+		// requires sanitizer.memtag: true
+		// if set, enables sync memory tagging
+		Memtag_heap *bool `android:"arch_variant"`
+		// List of specific undefined behavior sanitizers to enable in diagnostic mode
+		Misc_undefined []string `android:"arch_variant"`
+		// List of sanitizers to pass to -fno-sanitize-recover
+		// results in only the first detected error for these sanitizers being reported and program then
+		// exits with a non-zero exit code.
+		No_recover []string `android:"arch_variant"`
 	} `android:"arch_variant"`
 
 	// Sanitizers to run with flag configuration specified
@@ -207,17 +255,16 @@
 		Cfi_assembly_support *bool `android:"arch_variant"`
 	} `android:"arch_variant"`
 
-	// value to pass to -fsanitize-recover=
+	// List of sanitizers to pass to -fsanitize-recover
+	// allows execution to continue for these sanitizers to detect multiple errors rather than only
+	// the first one
 	Recover []string
 
-	// value to pass to -fsanitize-blacklist
+	// value to pass to -fsanitize-ignorelist
 	Blocklist *string
 }
 
 type SanitizeProperties struct {
-	// Enable AddressSanitizer, ThreadSanitizer, UndefinedBehaviorSanitizer, and
-	// others. Please see SanitizerUserProps in build/soong/cc/sanitize.go for
-	// details.
 	Sanitize          SanitizeUserProps `android:"arch_variant"`
 	SanitizerEnabled  bool              `blueprint:"mutated"`
 	SanitizeDep       bool              `blueprint:"mutated"`
@@ -257,20 +304,19 @@
 		s.Never = BoolPtr(true)
 	}
 
-	// Sanitizers do not work on Fuchsia yet.
-	if ctx.Fuchsia() {
-		s.Never = BoolPtr(true)
-	}
-
 	// Never always wins.
 	if Bool(s.Never) {
 		return
 	}
 
 	// cc_test targets default to SYNC MemTag unless explicitly set to ASYNC (via diag: {memtag_heap}).
-	if ctx.testBinary() && s.Memtag_heap == nil {
-		s.Memtag_heap = boolPtr(true)
-		s.Diag.Memtag_heap = boolPtr(true)
+	if ctx.testBinary() {
+		if s.Memtag_heap == nil {
+			s.Memtag_heap = proptools.BoolPtr(true)
+		}
+		if s.Diag.Memtag_heap == nil {
+			s.Diag.Memtag_heap = proptools.BoolPtr(true)
+		}
 	}
 
 	var globalSanitizers []string
@@ -291,48 +337,48 @@
 	if len(globalSanitizers) > 0 {
 		var found bool
 		if found, globalSanitizers = removeFromList("undefined", globalSanitizers); found && s.All_undefined == nil {
-			s.All_undefined = boolPtr(true)
+			s.All_undefined = proptools.BoolPtr(true)
 		}
 
 		if found, globalSanitizers = removeFromList("default-ub", globalSanitizers); found && s.Undefined == nil {
-			s.Undefined = boolPtr(true)
+			s.Undefined = proptools.BoolPtr(true)
 		}
 
 		if found, globalSanitizers = removeFromList("address", globalSanitizers); found && s.Address == nil {
-			s.Address = boolPtr(true)
+			s.Address = proptools.BoolPtr(true)
 		}
 
 		if found, globalSanitizers = removeFromList("thread", globalSanitizers); found && s.Thread == nil {
-			s.Thread = boolPtr(true)
+			s.Thread = proptools.BoolPtr(true)
 		}
 
 		if found, globalSanitizers = removeFromList("fuzzer", globalSanitizers); found && s.Fuzzer == nil {
-			s.Fuzzer = boolPtr(true)
+			s.Fuzzer = proptools.BoolPtr(true)
 		}
 
 		if found, globalSanitizers = removeFromList("safe-stack", globalSanitizers); found && s.Safestack == nil {
-			s.Safestack = boolPtr(true)
+			s.Safestack = proptools.BoolPtr(true)
 		}
 
 		if found, globalSanitizers = removeFromList("cfi", globalSanitizers); found && s.Cfi == nil {
 			if !ctx.Config().CFIDisabledForPath(ctx.ModuleDir()) {
-				s.Cfi = boolPtr(true)
+				s.Cfi = proptools.BoolPtr(true)
 			}
 		}
 
 		// Global integer_overflow builds do not support static libraries.
 		if found, globalSanitizers = removeFromList("integer_overflow", globalSanitizers); found && s.Integer_overflow == nil {
 			if !ctx.Config().IntegerOverflowDisabledForPath(ctx.ModuleDir()) && !ctx.static() {
-				s.Integer_overflow = boolPtr(true)
+				s.Integer_overflow = proptools.BoolPtr(true)
 			}
 		}
 
 		if found, globalSanitizers = removeFromList("scudo", globalSanitizers); found && s.Scudo == nil {
-			s.Scudo = boolPtr(true)
+			s.Scudo = proptools.BoolPtr(true)
 		}
 
 		if found, globalSanitizers = removeFromList("hwaddress", globalSanitizers); found && s.Hwaddress == nil {
-			s.Hwaddress = boolPtr(true)
+			s.Hwaddress = proptools.BoolPtr(true)
 		}
 
 		if found, globalSanitizers = removeFromList("writeonly", globalSanitizers); found && s.Writeonly == nil {
@@ -341,11 +387,11 @@
 			if s.Address == nil && s.Hwaddress == nil {
 				ctx.ModuleErrorf("writeonly modifier cannot be used without 'address' or 'hwaddress'")
 			}
-			s.Writeonly = boolPtr(true)
+			s.Writeonly = proptools.BoolPtr(true)
 		}
 		if found, globalSanitizers = removeFromList("memtag_heap", globalSanitizers); found && s.Memtag_heap == nil {
 			if !ctx.Config().MemtagHeapDisabledForPath(ctx.ModuleDir()) {
-				s.Memtag_heap = boolPtr(true)
+				s.Memtag_heap = proptools.BoolPtr(true)
 			}
 		}
 
@@ -356,17 +402,17 @@
 		// Global integer_overflow builds do not support static library diagnostics.
 		if found, globalSanitizersDiag = removeFromList("integer_overflow", globalSanitizersDiag); found &&
 			s.Diag.Integer_overflow == nil && Bool(s.Integer_overflow) && !ctx.static() {
-			s.Diag.Integer_overflow = boolPtr(true)
+			s.Diag.Integer_overflow = proptools.BoolPtr(true)
 		}
 
 		if found, globalSanitizersDiag = removeFromList("cfi", globalSanitizersDiag); found &&
 			s.Diag.Cfi == nil && Bool(s.Cfi) {
-			s.Diag.Cfi = boolPtr(true)
+			s.Diag.Cfi = proptools.BoolPtr(true)
 		}
 
 		if found, globalSanitizersDiag = removeFromList("memtag_heap", globalSanitizersDiag); found &&
 			s.Diag.Memtag_heap == nil && Bool(s.Memtag_heap) {
-			s.Diag.Memtag_heap = boolPtr(true)
+			s.Diag.Memtag_heap = proptools.BoolPtr(true)
 		}
 
 		if len(globalSanitizersDiag) > 0 {
@@ -378,30 +424,30 @@
 	if ctx.Arch().ArchType == android.Arm64 {
 		if ctx.Config().MemtagHeapSyncEnabledForPath(ctx.ModuleDir()) {
 			if s.Memtag_heap == nil {
-				s.Memtag_heap = boolPtr(true)
+				s.Memtag_heap = proptools.BoolPtr(true)
 			}
 			if s.Diag.Memtag_heap == nil {
-				s.Diag.Memtag_heap = boolPtr(true)
+				s.Diag.Memtag_heap = proptools.BoolPtr(true)
 			}
 		} else if ctx.Config().MemtagHeapAsyncEnabledForPath(ctx.ModuleDir()) {
 			if s.Memtag_heap == nil {
-				s.Memtag_heap = boolPtr(true)
+				s.Memtag_heap = proptools.BoolPtr(true)
 			}
 		}
 	}
 
-	// Enable CFI for all components in the include paths (for Aarch64 only)
-	if s.Cfi == nil && ctx.Config().CFIEnabledForPath(ctx.ModuleDir()) && ctx.Arch().ArchType == android.Arm64 {
-		s.Cfi = boolPtr(true)
+	// Enable CFI for non-host components in the include paths
+	if s.Cfi == nil && ctx.Config().CFIEnabledForPath(ctx.ModuleDir()) && !ctx.Host() {
+		s.Cfi = proptools.BoolPtr(true)
 		if inList("cfi", ctx.Config().SanitizeDeviceDiag()) {
-			s.Diag.Cfi = boolPtr(true)
+			s.Diag.Cfi = proptools.BoolPtr(true)
 		}
 	}
 
 	// Is CFI actually enabled?
 	if !ctx.Config().EnableCFI() {
-		s.Cfi = boolPtr(false)
-		s.Diag.Cfi = boolPtr(false)
+		s.Cfi = nil
+		s.Diag.Cfi = nil
 	}
 
 	// HWASan requires AArch64 hardware feature (top-byte-ignore).
@@ -421,14 +467,14 @@
 
 	// Also disable CFI if ASAN is enabled.
 	if Bool(s.Address) || Bool(s.Hwaddress) {
-		s.Cfi = boolPtr(false)
-		s.Diag.Cfi = boolPtr(false)
+		s.Cfi = nil
+		s.Diag.Cfi = nil
 	}
 
-	// Disable sanitizers that depend on the UBSan runtime for windows/darwin builds.
-	if !ctx.Os().Linux() {
-		s.Cfi = boolPtr(false)
-		s.Diag.Cfi = boolPtr(false)
+	// Disable sanitizers that depend on the UBSan runtime for windows/darwin/musl builds.
+	if !ctx.Os().Linux() || ctx.Os() == android.LinuxMusl {
+		s.Cfi = nil
+		s.Diag.Cfi = nil
 		s.Misc_undefined = nil
 		s.Undefined = nil
 		s.All_undefined = nil
@@ -443,8 +489,8 @@
 			s.Cfi = nil
 			s.Diag.Cfi = nil
 		} else {
-			s.Cfi = boolPtr(false)
-			s.Diag.Cfi = boolPtr(false)
+			s.Cfi = nil
+			s.Diag.Cfi = nil
 		}
 	}
 
@@ -495,18 +541,10 @@
 	// TODO(b/131771163): CFI transiently depends on LTO, and thus Fuzzer is
 	// mutually incompatible.
 	if Bool(s.Fuzzer) {
-		s.Cfi = boolPtr(false)
+		s.Cfi = nil
 	}
 }
 
-func (sanitize *sanitize) deps(ctx BaseModuleContext, deps Deps) Deps {
-	if !sanitize.Properties.SanitizerEnabled { // || c.static() {
-		return deps
-	}
-
-	return deps
-}
-
 func toDisableImplicitIntegerChange(flags []string) bool {
 	// Returns true if any flag is fsanitize*integer, and there is
 	// no explicit flag about sanitize=implicit-integer-sign-change.
@@ -718,7 +756,7 @@
 
 	blocklist := android.OptionalPathForModuleSrc(ctx, sanitize.Properties.Sanitize.Blocklist)
 	if blocklist.Valid() {
-		flags.Local.CFlags = append(flags.Local.CFlags, "-fsanitize-blacklist="+blocklist.String())
+		flags.Local.CFlags = append(flags.Local.CFlags, "-fsanitize-ignorelist="+blocklist.String())
 		flags.CFlagsDeps = append(flags.CFlagsDeps, blocklist.Path())
 	}
 
@@ -789,23 +827,27 @@
 }
 
 func (sanitize *sanitize) SetSanitizer(t SanitizerType, b bool) {
+	bPtr := proptools.BoolPtr(b)
+	if !b {
+		bPtr = nil
+	}
 	switch t {
 	case Asan:
-		sanitize.Properties.Sanitize.Address = boolPtr(b)
+		sanitize.Properties.Sanitize.Address = bPtr
 	case Hwasan:
-		sanitize.Properties.Sanitize.Hwaddress = boolPtr(b)
+		sanitize.Properties.Sanitize.Hwaddress = bPtr
 	case tsan:
-		sanitize.Properties.Sanitize.Thread = boolPtr(b)
+		sanitize.Properties.Sanitize.Thread = bPtr
 	case intOverflow:
-		sanitize.Properties.Sanitize.Integer_overflow = boolPtr(b)
+		sanitize.Properties.Sanitize.Integer_overflow = bPtr
 	case cfi:
-		sanitize.Properties.Sanitize.Cfi = boolPtr(b)
+		sanitize.Properties.Sanitize.Cfi = bPtr
 	case scs:
-		sanitize.Properties.Sanitize.Scs = boolPtr(b)
+		sanitize.Properties.Sanitize.Scs = bPtr
 	case memtag_heap:
-		sanitize.Properties.Sanitize.Memtag_heap = boolPtr(b)
+		sanitize.Properties.Sanitize.Memtag_heap = bPtr
 	case Fuzzer:
-		sanitize.Properties.Sanitize.Fuzzer = boolPtr(b)
+		sanitize.Properties.Sanitize.Fuzzer = bPtr
 	default:
 		panic(fmt.Errorf("unknown SanitizerType %d", t))
 	}
@@ -859,7 +901,7 @@
 // as vendor snapshot. Such modules must create both cfi and non-cfi variants,
 // except for ones which explicitly disable cfi.
 func needsCfiForVendorSnapshot(mctx android.TopDownMutatorContext) bool {
-	if isVendorProprietaryModule(mctx) {
+	if snapshot.IsVendorProprietaryModule(mctx) {
 		return false
 	}
 
@@ -1197,7 +1239,7 @@
 				if c.Device() {
 					variations = append(variations, c.ImageVariation())
 				}
-				c.addSharedLibDependenciesWithVersions(mctx, variations, depTag, runtimeLibrary, "", true)
+				AddSharedLibDependenciesWithVersions(mctx, c, variations, depTag, runtimeLibrary, "", true)
 			}
 			// static lib does not have dependency to the runtime library. The
 			// dependency will be added to the executables or shared libs using
@@ -1261,7 +1303,7 @@
 func sanitizerMutator(t SanitizerType) func(android.BottomUpMutatorContext) {
 	return func(mctx android.BottomUpMutatorContext) {
 		if c, ok := mctx.Module().(PlatformSanitizeable); ok && c.SanitizePropDefined() {
-			if c.IsDependencyRoot() && c.IsSanitizerEnabled(t) {
+			if c.Binary() && c.IsSanitizerEnabled(t) {
 				modules := mctx.CreateVariations(t.variationName())
 				modules[0].(PlatformSanitizeable).SetSanitizer(t, true)
 			} else if c.IsSanitizerEnabled(t) || c.SanitizeDep() {
diff --git a/cc/sanitize_test.go b/cc/sanitize_test.go
index f126346..0070e40 100644
--- a/cc/sanitize_test.go
+++ b/cc/sanitize_test.go
@@ -15,6 +15,8 @@
 package cc
 
 import (
+	"fmt"
+	"strings"
 	"testing"
 
 	"android/soong/android"
@@ -202,3 +204,354 @@
 	t.Run("host", func(t *testing.T) { check(t, result, result.Config.BuildOSTarget.String()) })
 	t.Run("device", func(t *testing.T) { check(t, result, "android_arm64_armv8-a") })
 }
+
+type MemtagNoteType int
+
+const (
+	None MemtagNoteType = iota + 1
+	Sync
+	Async
+)
+
+func (t MemtagNoteType) str() string {
+	switch t {
+	case None:
+		return "none"
+	case Sync:
+		return "sync"
+	case Async:
+		return "async"
+	default:
+		panic("type_note_invalid")
+	}
+}
+
+func checkHasMemtagNote(t *testing.T, m android.TestingModule, expected MemtagNoteType) {
+	t.Helper()
+	note_async := "note_memtag_heap_async"
+	note_sync := "note_memtag_heap_sync"
+
+	found := None
+	implicits := m.Rule("ld").Implicits
+	for _, lib := range implicits {
+		if strings.Contains(lib.Rel(), note_async) {
+			found = Async
+			break
+		} else if strings.Contains(lib.Rel(), note_sync) {
+			found = Sync
+			break
+		}
+	}
+
+	if found != expected {
+		t.Errorf("Wrong Memtag note in target %q: found %q, expected %q", m.Module().(*Module).Name(), found.str(), expected.str())
+	}
+}
+
+var prepareForTestWithMemtagHeap = android.GroupFixturePreparers(
+	android.FixtureModifyMockFS(func(fs android.MockFS) {
+		templateBp := `
+		cc_test {
+			name: "unset_test_%[1]s",
+			gtest: false,
+		}
+
+		cc_test {
+			name: "no_memtag_test_%[1]s",
+			gtest: false,
+			sanitize: { memtag_heap: false },
+		}
+
+		cc_test {
+			name: "set_memtag_test_%[1]s",
+			gtest: false,
+			sanitize: { memtag_heap: true },
+		}
+
+		cc_test {
+			name: "set_memtag_set_async_test_%[1]s",
+			gtest: false,
+			sanitize: { memtag_heap: true, diag: { memtag_heap: false }  },
+		}
+
+		cc_test {
+			name: "set_memtag_set_sync_test_%[1]s",
+			gtest: false,
+			sanitize: { memtag_heap: true, diag: { memtag_heap: true }  },
+		}
+
+		cc_test {
+			name: "unset_memtag_set_sync_test_%[1]s",
+			gtest: false,
+			sanitize: { diag: { memtag_heap: true }  },
+		}
+
+		cc_binary {
+			name: "unset_binary_%[1]s",
+		}
+
+		cc_binary {
+			name: "no_memtag_binary_%[1]s",
+			sanitize: { memtag_heap: false },
+		}
+
+		cc_binary {
+			name: "set_memtag_binary_%[1]s",
+			sanitize: { memtag_heap: true },
+		}
+
+		cc_binary {
+			name: "set_memtag_set_async_binary_%[1]s",
+			sanitize: { memtag_heap: true, diag: { memtag_heap: false }  },
+		}
+
+		cc_binary {
+			name: "set_memtag_set_sync_binary_%[1]s",
+			sanitize: { memtag_heap: true, diag: { memtag_heap: true }  },
+		}
+
+		cc_binary {
+			name: "unset_memtag_set_sync_binary_%[1]s",
+			sanitize: { diag: { memtag_heap: true }  },
+		}
+		`
+		subdirNoOverrideBp := fmt.Sprintf(templateBp, "no_override")
+		subdirOverrideDefaultDisableBp := fmt.Sprintf(templateBp, "override_default_disable")
+		subdirSyncBp := fmt.Sprintf(templateBp, "override_default_sync")
+		subdirAsyncBp := fmt.Sprintf(templateBp, "override_default_async")
+
+		fs.Merge(android.MockFS{
+			"subdir_no_override/Android.bp":              []byte(subdirNoOverrideBp),
+			"subdir_override_default_disable/Android.bp": []byte(subdirOverrideDefaultDisableBp),
+			"subdir_sync/Android.bp":                     []byte(subdirSyncBp),
+			"subdir_async/Android.bp":                    []byte(subdirAsyncBp),
+		})
+	}),
+	android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+		variables.MemtagHeapExcludePaths = []string{"subdir_override_default_disable"}
+		// "subdir_override_default_disable" is covered by both include and override_default_disable paths. override_default_disable wins.
+		variables.MemtagHeapSyncIncludePaths = []string{"subdir_sync", "subdir_override_default_disable"}
+		variables.MemtagHeapAsyncIncludePaths = []string{"subdir_async", "subdir_override_default_disable"}
+	}),
+)
+
+func TestSanitizeMemtagHeap(t *testing.T) {
+	variant := "android_arm64_armv8-a"
+
+	result := android.GroupFixturePreparers(
+		prepareForCcTest,
+		prepareForTestWithMemtagHeap,
+	).RunTest(t)
+	ctx := result.TestContext
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_no_override", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_async", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_sync", variant), None)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_no_override", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_async", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_sync", variant), None)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_no_override", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_async", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_disable", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_sync", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_sync", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_no_override", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_async", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_disable", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_sync", variant), Async)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_no_override", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_async", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_disable", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_sync", variant), Async)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_sync", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_sync", variant), Sync)
+
+	// should sanitize: { diag: { memtag: true } } result in Sync instead of None here?
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_no_override", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_async", variant), Sync)
+	// should sanitize: { diag: { memtag: true } } result in Sync instead of None here?
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_sync", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_sync", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_no_override", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_async", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_sync", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_sync", variant), Sync)
+}
+
+func TestSanitizeMemtagHeapWithSanitizeDevice(t *testing.T) {
+	variant := "android_arm64_armv8-a"
+
+	result := android.GroupFixturePreparers(
+		prepareForCcTest,
+		prepareForTestWithMemtagHeap,
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.SanitizeDevice = []string{"memtag_heap"}
+		}),
+	).RunTest(t)
+	ctx := result.TestContext
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_no_override", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_async", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_sync", variant), None)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_no_override", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_async", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_sync", variant), None)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_no_override", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_async", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_disable", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_sync", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_sync", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_no_override", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_async", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_disable", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_sync", variant), Async)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_no_override", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_async", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_disable", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_sync", variant), Async)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_sync", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_sync", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_async", variant), Sync)
+	// should sanitize: { diag: { memtag: true } } result in Sync instead of None here?
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_sync", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_sync", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_no_override", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_async", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_sync", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_sync", variant), Sync)
+}
+
+func TestSanitizeMemtagHeapWithSanitizeDeviceDiag(t *testing.T) {
+	variant := "android_arm64_armv8-a"
+
+	result := android.GroupFixturePreparers(
+		prepareForCcTest,
+		prepareForTestWithMemtagHeap,
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.SanitizeDevice = []string{"memtag_heap"}
+			variables.SanitizeDeviceDiag = []string{"memtag_heap"}
+		}),
+	).RunTest(t)
+	ctx := result.TestContext
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_no_override", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_async", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_binary_override_default_sync", variant), None)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_no_override", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_async", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("no_memtag_test_override_default_sync", variant), None)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_binary_override_default_sync", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_test_override_default_sync", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_no_override", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_async", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_disable", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_binary_override_default_sync", variant), Async)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_no_override", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_async", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_disable", variant), Async)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_async_test_override_default_sync", variant), Async)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_binary_override_default_sync", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("set_memtag_set_sync_test_override_default_sync", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_async", variant), Sync)
+	// should sanitize: { diag: { memtag: true } } result in Sync instead of None here?
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_binary_override_default_sync", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_memtag_set_sync_test_override_default_sync", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_disable", variant), None)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_binary_override_default_sync", variant), Sync)
+
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_no_override", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_async", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_disable", variant), Sync)
+	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_sync", variant), Sync)
+}
diff --git a/cc/sdk.go b/cc/sdk.go
index aec950b..a83e5ad 100644
--- a/cc/sdk.go
+++ b/cc/sdk.go
@@ -35,7 +35,8 @@
 			if !m.UseSdk() && !m.SplitPerApiLevel() {
 				ctx.ModuleErrorf("UseSdk() must return true when AlwaysSdk is set, did the factory forget to set Sdk_version?")
 			}
-			ctx.CreateVariations("sdk")
+			modules := ctx.CreateVariations("sdk")
+			modules[0].(*Module).Properties.IsSdkVariant = true
 		} else if m.UseSdk() || m.SplitPerApiLevel() {
 			modules := ctx.CreateVariations("", "sdk")
 
@@ -75,7 +76,7 @@
 			}
 			ctx.AliasVariation("")
 		}
-	case *snapshot:
+	case *snapshotModule:
 		ctx.CreateVariations("")
 	}
 }
diff --git a/cc/snapshot_prebuilt.go b/cc/snapshot_prebuilt.go
index a7351a9..9570664 100644
--- a/cc/snapshot_prebuilt.go
+++ b/cc/snapshot_prebuilt.go
@@ -18,57 +18,17 @@
 // snapshot mutators and snapshot information maps which are also defined in this file.
 
 import (
-	"path/filepath"
 	"strings"
 
 	"android/soong/android"
+	"android/soong/snapshot"
 
 	"github.com/google/blueprint"
 )
 
-// Defines the specifics of different images to which the snapshot process is applicable, e.g.,
-// vendor, recovery, ramdisk.
-type snapshotImage interface {
-	// Returns true if a snapshot should be generated for this image.
-	shouldGenerateSnapshot(ctx android.SingletonContext) bool
-
-	// Function that returns true if the module is included in this image.
-	// Using a function return instead of a value to prevent early
-	// evalution of a function that may be not be defined.
-	inImage(m LinkableInterface) func() bool
-
-	// Returns true if the module is private and must not be included in the
-	// snapshot. For example VNDK-private modules must return true for the
-	// vendor snapshots. But false for the recovery snapshots.
-	private(m LinkableInterface) bool
-
-	// Returns true if a dir under source tree is an SoC-owned proprietary
-	// directory, such as device/, vendor/, etc.
-	//
-	// For a given snapshot (e.g., vendor, recovery, etc.) if
-	// isProprietaryPath(dir, deviceConfig) returns true, then the module in dir
-	// will be built from sources.
-	isProprietaryPath(dir string, deviceConfig android.DeviceConfig) bool
-
-	// Whether to include VNDK in the snapshot for this image.
-	includeVndk() bool
-
-	// Whether a given module has been explicitly excluded from the
-	// snapshot, e.g., using the exclude_from_vendor_snapshot or
-	// exclude_from_recovery_snapshot properties.
-	excludeFromSnapshot(m LinkableInterface) bool
-
-	// Returns true if the build is using a snapshot for this image.
-	isUsingSnapshot(cfg android.DeviceConfig) bool
-
-	// Returns a version of which the snapshot should be used in this target.
-	// This will only be meaningful when isUsingSnapshot is true.
-	targetSnapshotVersion(cfg android.DeviceConfig) string
-
-	// Whether to exclude a given module from the directed snapshot or not.
-	// If the makefile variable DIRECTED_{IMAGE}_SNAPSHOT is true, directed snapshot is turned on,
-	// and only modules listed in {IMAGE}_SNAPSHOT_MODULES will be captured.
-	excludeFromDirectedSnapshot(cfg android.DeviceConfig, name string) bool
+// This interface overrides snapshot.SnapshotImage to implement cc module specific functions
+type SnapshotImage interface {
+	snapshot.SnapshotImage
 
 	// The image variant name for this snapshot image.
 	// For example, recovery snapshot image will return "recovery", and vendor snapshot image will
@@ -80,106 +40,12 @@
 	moduleNameSuffix() string
 }
 
-type vendorSnapshotImage struct{}
-type recoverySnapshotImage struct{}
-
-type directoryMap map[string]bool
-
-var (
-	// Modules under following directories are ignored. They are OEM's and vendor's
-	// proprietary modules(device/, kernel/, vendor/, and hardware/).
-	defaultDirectoryExcludedMap = directoryMap{
-		"device":   true,
-		"hardware": true,
-		"kernel":   true,
-		"vendor":   true,
-	}
-
-	// Modules under following directories are included as they are in AOSP,
-	// although hardware/ and kernel/ are normally for vendor's own.
-	defaultDirectoryIncludedMap = directoryMap{
-		"kernel/configs":              true,
-		"kernel/prebuilts":            true,
-		"kernel/tests":                true,
-		"hardware/interfaces":         true,
-		"hardware/libhardware":        true,
-		"hardware/libhardware_legacy": true,
-		"hardware/ril":                true,
-	}
-)
-
-func (vendorSnapshotImage) init(ctx android.RegistrationContext) {
-	ctx.RegisterSingletonType("vendor-snapshot", VendorSnapshotSingleton)
-	ctx.RegisterModuleType("vendor_snapshot", vendorSnapshotFactory)
-	ctx.RegisterModuleType("vendor_snapshot_shared", VendorSnapshotSharedFactory)
-	ctx.RegisterModuleType("vendor_snapshot_static", VendorSnapshotStaticFactory)
-	ctx.RegisterModuleType("vendor_snapshot_header", VendorSnapshotHeaderFactory)
-	ctx.RegisterModuleType("vendor_snapshot_binary", VendorSnapshotBinaryFactory)
-	ctx.RegisterModuleType("vendor_snapshot_object", VendorSnapshotObjectFactory)
-
-	ctx.RegisterSingletonType("vendor-fake-snapshot", VendorFakeSnapshotSingleton)
+type vendorSnapshotImage struct {
+	*snapshot.VendorSnapshotImage
 }
 
-func (vendorSnapshotImage) shouldGenerateSnapshot(ctx android.SingletonContext) bool {
-	// BOARD_VNDK_VERSION must be set to 'current' in order to generate a snapshot.
-	return ctx.DeviceConfig().VndkVersion() == "current"
-}
-
-func (vendorSnapshotImage) inImage(m LinkableInterface) func() bool {
-	return m.InVendor
-}
-
-func (vendorSnapshotImage) private(m LinkableInterface) bool {
-	return m.IsVndkPrivate()
-}
-
-func isDirectoryExcluded(dir string, excludedMap directoryMap, includedMap directoryMap) bool {
-	if dir == "." || dir == "/" {
-		return false
-	}
-	if includedMap[dir] {
-		return false
-	} else if excludedMap[dir] {
-		return true
-	} else if defaultDirectoryIncludedMap[dir] {
-		return false
-	} else if defaultDirectoryExcludedMap[dir] {
-		return true
-	} else {
-		return isDirectoryExcluded(filepath.Dir(dir), excludedMap, includedMap)
-	}
-}
-
-func (vendorSnapshotImage) isProprietaryPath(dir string, deviceConfig android.DeviceConfig) bool {
-	return isDirectoryExcluded(dir, deviceConfig.VendorSnapshotDirsExcludedMap(), deviceConfig.VendorSnapshotDirsIncludedMap())
-}
-
-// vendor snapshot includes static/header libraries with vndk: {enabled: true}.
-func (vendorSnapshotImage) includeVndk() bool {
-	return true
-}
-
-func (vendorSnapshotImage) excludeFromSnapshot(m LinkableInterface) bool {
-	return m.ExcludeFromVendorSnapshot()
-}
-
-func (vendorSnapshotImage) isUsingSnapshot(cfg android.DeviceConfig) bool {
-	vndkVersion := cfg.VndkVersion()
-	return vndkVersion != "current" && vndkVersion != ""
-}
-
-func (vendorSnapshotImage) targetSnapshotVersion(cfg android.DeviceConfig) string {
-	return cfg.VndkVersion()
-}
-
-// returns true iff a given module SHOULD BE EXCLUDED, false if included
-func (vendorSnapshotImage) excludeFromDirectedSnapshot(cfg android.DeviceConfig, name string) bool {
-	// If we're using full snapshot, not directed snapshot, capture every module
-	if !cfg.DirectedVendorSnapshot() {
-		return false
-	}
-	// Else, checks if name is in VENDOR_SNAPSHOT_MODULES.
-	return !cfg.VendorSnapshotModules()[name]
+type recoverySnapshotImage struct {
+	*snapshot.RecoverySnapshotImage
 }
 
 func (vendorSnapshotImage) imageVariantName(cfg android.DeviceConfig) string {
@@ -190,8 +56,28 @@
 	return VendorSuffix
 }
 
-func (recoverySnapshotImage) init(ctx android.RegistrationContext) {
-	ctx.RegisterSingletonType("recovery-snapshot", RecoverySnapshotSingleton)
+func (recoverySnapshotImage) imageVariantName(cfg android.DeviceConfig) string {
+	return android.RecoveryVariation
+}
+
+func (recoverySnapshotImage) moduleNameSuffix() string {
+	return RecoverySuffix
+}
+
+// Override existing vendor and recovery snapshot for cc module specific extra functions
+var VendorSnapshotImageSingleton vendorSnapshotImage = vendorSnapshotImage{&snapshot.VendorSnapshotImageSingleton}
+var recoverySnapshotImageSingleton recoverySnapshotImage = recoverySnapshotImage{&snapshot.RecoverySnapshotImageSingleton}
+
+func RegisterVendorSnapshotModules(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("vendor_snapshot", vendorSnapshotFactory)
+	ctx.RegisterModuleType("vendor_snapshot_shared", VendorSnapshotSharedFactory)
+	ctx.RegisterModuleType("vendor_snapshot_static", VendorSnapshotStaticFactory)
+	ctx.RegisterModuleType("vendor_snapshot_header", VendorSnapshotHeaderFactory)
+	ctx.RegisterModuleType("vendor_snapshot_binary", VendorSnapshotBinaryFactory)
+	ctx.RegisterModuleType("vendor_snapshot_object", VendorSnapshotObjectFactory)
+}
+
+func RegisterRecoverySnapshotModules(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("recovery_snapshot", recoverySnapshotFactory)
 	ctx.RegisterModuleType("recovery_snapshot_shared", RecoverySnapshotSharedFactory)
 	ctx.RegisterModuleType("recovery_snapshot_static", RecoverySnapshotStaticFactory)
@@ -200,130 +86,75 @@
 	ctx.RegisterModuleType("recovery_snapshot_object", RecoverySnapshotObjectFactory)
 }
 
-func (recoverySnapshotImage) shouldGenerateSnapshot(ctx android.SingletonContext) bool {
-	// RECOVERY_SNAPSHOT_VERSION must be set to 'current' in order to generate a
-	// snapshot.
-	return ctx.DeviceConfig().RecoverySnapshotVersion() == "current"
-}
-
-func (recoverySnapshotImage) inImage(m LinkableInterface) func() bool {
-	return m.InRecovery
-}
-
-// recovery snapshot does not have private libraries.
-func (recoverySnapshotImage) private(m LinkableInterface) bool {
-	return false
-}
-
-func (recoverySnapshotImage) isProprietaryPath(dir string, deviceConfig android.DeviceConfig) bool {
-	return isDirectoryExcluded(dir, deviceConfig.RecoverySnapshotDirsExcludedMap(), deviceConfig.RecoverySnapshotDirsIncludedMap())
-}
-
-// recovery snapshot does NOT treat vndk specially.
-func (recoverySnapshotImage) includeVndk() bool {
-	return false
-}
-
-func (recoverySnapshotImage) excludeFromSnapshot(m LinkableInterface) bool {
-	return m.ExcludeFromRecoverySnapshot()
-}
-
-func (recoverySnapshotImage) isUsingSnapshot(cfg android.DeviceConfig) bool {
-	recoverySnapshotVersion := cfg.RecoverySnapshotVersion()
-	return recoverySnapshotVersion != "current" && recoverySnapshotVersion != ""
-}
-
-func (recoverySnapshotImage) targetSnapshotVersion(cfg android.DeviceConfig) string {
-	return cfg.RecoverySnapshotVersion()
-}
-
-func (recoverySnapshotImage) excludeFromDirectedSnapshot(cfg android.DeviceConfig, name string) bool {
-	// If we're using full snapshot, not directed snapshot, capture every module
-	if !cfg.DirectedRecoverySnapshot() {
-		return false
-	}
-	// Else, checks if name is in RECOVERY_SNAPSHOT_MODULES.
-	return !cfg.RecoverySnapshotModules()[name]
-}
-
-func (recoverySnapshotImage) imageVariantName(cfg android.DeviceConfig) string {
-	return android.RecoveryVariation
-}
-
-func (recoverySnapshotImage) moduleNameSuffix() string {
-	return recoverySuffix
-}
-
-var vendorSnapshotImageSingleton vendorSnapshotImage
-var recoverySnapshotImageSingleton recoverySnapshotImage
-
 func init() {
-	vendorSnapshotImageSingleton.init(android.InitRegistrationContext)
-	recoverySnapshotImageSingleton.init(android.InitRegistrationContext)
+	RegisterVendorSnapshotModules(android.InitRegistrationContext)
+	RegisterRecoverySnapshotModules(android.InitRegistrationContext)
+	android.RegisterMakeVarsProvider(pctx, snapshotMakeVarsProvider)
 }
 
 const (
 	snapshotHeaderSuffix = "_header."
-	snapshotSharedSuffix = "_shared."
-	snapshotStaticSuffix = "_static."
+	SnapshotSharedSuffix = "_shared."
+	SnapshotStaticSuffix = "_static."
 	snapshotBinarySuffix = "_binary."
 	snapshotObjectSuffix = "_object."
+	SnapshotRlibSuffix   = "_rlib."
 )
 
 type SnapshotProperties struct {
 	Header_libs []string `android:"arch_variant"`
 	Static_libs []string `android:"arch_variant"`
 	Shared_libs []string `android:"arch_variant"`
+	Rlibs       []string `android:"arch_variant"`
 	Vndk_libs   []string `android:"arch_variant"`
 	Binaries    []string `android:"arch_variant"`
 	Objects     []string `android:"arch_variant"`
 }
-
-type snapshot struct {
+type snapshotModule struct {
 	android.ModuleBase
 
 	properties SnapshotProperties
 
-	baseSnapshot baseSnapshotDecorator
+	baseSnapshot BaseSnapshotDecorator
 
-	image snapshotImage
+	image SnapshotImage
 }
 
-func (s *snapshot) ImageMutatorBegin(ctx android.BaseModuleContext) {
+func (s *snapshotModule) ImageMutatorBegin(ctx android.BaseModuleContext) {
 	cfg := ctx.DeviceConfig()
-	if !s.image.isUsingSnapshot(cfg) || s.image.targetSnapshotVersion(cfg) != s.baseSnapshot.version() {
+	if !s.image.IsUsingSnapshot(cfg) || s.image.TargetSnapshotVersion(cfg) != s.baseSnapshot.Version() {
 		s.Disable()
 	}
 }
 
-func (s *snapshot) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
+func (s *snapshotModule) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
 	return false
 }
 
-func (s *snapshot) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+func (s *snapshotModule) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
 	return false
 }
 
-func (s *snapshot) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+func (s *snapshotModule) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
 	return false
 }
 
-func (s *snapshot) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+func (s *snapshotModule) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
 	return false
 }
 
-func (s *snapshot) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
+func (s *snapshotModule) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
 	return false
 }
 
-func (s *snapshot) ExtraImageVariations(ctx android.BaseModuleContext) []string {
+func (s *snapshotModule) ExtraImageVariations(ctx android.BaseModuleContext) []string {
 	return []string{s.image.imageVariantName(ctx.DeviceConfig())}
 }
 
-func (s *snapshot) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) {
+func (s *snapshotModule) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) {
 }
 
-func (s *snapshot) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+func (s *snapshotModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	// Nothing, the snapshot module is only used to forward dependency information in DepsMutator.
 }
 
@@ -335,13 +166,13 @@
 	return moduleSuffix + versionSuffix
 }
 
-func (s *snapshot) DepsMutator(ctx android.BottomUpMutatorContext) {
+func (s *snapshotModule) DepsMutator(ctx android.BottomUpMutatorContext) {
 	collectSnapshotMap := func(names []string, snapshotSuffix, moduleSuffix string) map[string]string {
 		snapshotMap := make(map[string]string)
 		for _, name := range names {
 			snapshotMap[name] = name +
 				getSnapshotNameSuffix(snapshotSuffix+moduleSuffix,
-					s.baseSnapshot.version(),
+					s.baseSnapshot.Version(),
 					ctx.DeviceConfig().Arches()[0].ArchType.String())
 		}
 		return snapshotMap
@@ -351,8 +182,9 @@
 	headers := collectSnapshotMap(s.properties.Header_libs, snapshotSuffix, snapshotHeaderSuffix)
 	binaries := collectSnapshotMap(s.properties.Binaries, snapshotSuffix, snapshotBinarySuffix)
 	objects := collectSnapshotMap(s.properties.Objects, snapshotSuffix, snapshotObjectSuffix)
-	staticLibs := collectSnapshotMap(s.properties.Static_libs, snapshotSuffix, snapshotStaticSuffix)
-	sharedLibs := collectSnapshotMap(s.properties.Shared_libs, snapshotSuffix, snapshotSharedSuffix)
+	staticLibs := collectSnapshotMap(s.properties.Static_libs, snapshotSuffix, SnapshotStaticSuffix)
+	sharedLibs := collectSnapshotMap(s.properties.Shared_libs, snapshotSuffix, SnapshotSharedSuffix)
+	rlibs := collectSnapshotMap(s.properties.Rlibs, snapshotSuffix, SnapshotRlibSuffix)
 	vndkLibs := collectSnapshotMap(s.properties.Vndk_libs, "", vndkSuffix)
 	for k, v := range vndkLibs {
 		sharedLibs[k] = v
@@ -364,36 +196,55 @@
 		Objects:    objects,
 		StaticLibs: staticLibs,
 		SharedLibs: sharedLibs,
+		Rlibs:      rlibs,
 	})
 }
 
 type SnapshotInfo struct {
-	HeaderLibs, Binaries, Objects, StaticLibs, SharedLibs map[string]string
+	HeaderLibs, Binaries, Objects, StaticLibs, SharedLibs, Rlibs map[string]string
 }
 
 var SnapshotInfoProvider = blueprint.NewMutatorProvider(SnapshotInfo{}, "deps")
 
-var _ android.ImageInterface = (*snapshot)(nil)
+var _ android.ImageInterface = (*snapshotModule)(nil)
+
+func snapshotMakeVarsProvider(ctx android.MakeVarsContext) {
+	snapshotSet := map[string]struct{}{}
+	ctx.VisitAllModules(func(m android.Module) {
+		if s, ok := m.(*snapshotModule); ok {
+			if _, ok := snapshotSet[s.Name()]; ok {
+				// arch variant generates duplicated modules
+				// skip this as we only need to know the path of the module.
+				return
+			}
+			snapshotSet[s.Name()] = struct{}{}
+			imageNameVersion := strings.Split(s.image.imageVariantName(ctx.DeviceConfig()), ".")
+			ctx.Strict(
+				strings.Join([]string{strings.ToUpper(imageNameVersion[0]), s.baseSnapshot.Version(), "SNAPSHOT_DIR"}, "_"),
+				ctx.ModuleDir(s))
+		}
+	})
+}
 
 func vendorSnapshotFactory() android.Module {
-	return snapshotFactory(vendorSnapshotImageSingleton)
+	return snapshotFactory(VendorSnapshotImageSingleton)
 }
 
 func recoverySnapshotFactory() android.Module {
 	return snapshotFactory(recoverySnapshotImageSingleton)
 }
 
-func snapshotFactory(image snapshotImage) android.Module {
-	snapshot := &snapshot{}
-	snapshot.image = image
-	snapshot.AddProperties(
-		&snapshot.properties,
-		&snapshot.baseSnapshot.baseProperties)
-	android.InitAndroidArchModule(snapshot, android.DeviceSupported, android.MultilibBoth)
-	return snapshot
+func snapshotFactory(image SnapshotImage) android.Module {
+	snapshotModule := &snapshotModule{}
+	snapshotModule.image = image
+	snapshotModule.AddProperties(
+		&snapshotModule.properties,
+		&snapshotModule.baseSnapshot.baseProperties)
+	android.InitAndroidArchModule(snapshotModule, android.DeviceSupported, android.MultilibBoth)
+	return snapshotModule
 }
 
-type baseSnapshotDecoratorProperties struct {
+type BaseSnapshotDecoratorProperties struct {
 	// snapshot version.
 	Version string
 
@@ -408,7 +259,7 @@
 	ModuleSuffix string `blueprint:"mutated"`
 }
 
-// baseSnapshotDecorator provides common basic functions for all snapshot modules, such as snapshot
+// BaseSnapshotDecorator provides common basic functions for all snapshot modules, such as snapshot
 // version, snapshot arch, etc. It also adds a special suffix to Soong module name, so it doesn't
 // collide with source modules. e.g. the following example module,
 //
@@ -420,70 +271,89 @@
 // }
 //
 // will be seen as "libbase.vendor_static.30.arm64" by Soong.
-type baseSnapshotDecorator struct {
-	baseProperties baseSnapshotDecoratorProperties
-	image          snapshotImage
+type BaseSnapshotDecorator struct {
+	baseProperties BaseSnapshotDecoratorProperties
+	Image          SnapshotImage
 }
 
-func (p *baseSnapshotDecorator) Name(name string) string {
+func (p *BaseSnapshotDecorator) Name(name string) string {
 	return name + p.NameSuffix()
 }
 
-func (p *baseSnapshotDecorator) NameSuffix() string {
-	return getSnapshotNameSuffix(p.moduleSuffix(), p.version(), p.arch())
+func (p *BaseSnapshotDecorator) NameSuffix() string {
+	return getSnapshotNameSuffix(p.moduleSuffix(), p.Version(), p.Arch())
 }
 
-func (p *baseSnapshotDecorator) version() string {
+func (p *BaseSnapshotDecorator) Version() string {
 	return p.baseProperties.Version
 }
 
-func (p *baseSnapshotDecorator) arch() string {
+func (p *BaseSnapshotDecorator) Arch() string {
 	return p.baseProperties.Target_arch
 }
 
-func (p *baseSnapshotDecorator) moduleSuffix() string {
+func (p *BaseSnapshotDecorator) moduleSuffix() string {
 	return p.baseProperties.ModuleSuffix
 }
 
-func (p *baseSnapshotDecorator) isSnapshotPrebuilt() bool {
+func (p *BaseSnapshotDecorator) IsSnapshotPrebuilt() bool {
 	return true
 }
 
-func (p *baseSnapshotDecorator) snapshotAndroidMkSuffix() string {
+func (p *BaseSnapshotDecorator) SnapshotAndroidMkSuffix() string {
 	return p.baseProperties.Androidmk_suffix
 }
 
-func (p *baseSnapshotDecorator) setSnapshotAndroidMkSuffix(ctx android.ModuleContext) {
-	coreVariations := append(ctx.Target().Variations(), blueprint.Variation{
+func (p *BaseSnapshotDecorator) SetSnapshotAndroidMkSuffix(ctx android.ModuleContext, variant string) {
+	// If there are any 2 or more variations among {core, product, vendor, recovery}
+	// we have to add the androidmk suffix to avoid duplicate modules with the same
+	// name.
+	variations := append(ctx.Target().Variations(), blueprint.Variation{
 		Mutator:   "image",
 		Variation: android.CoreVariation})
 
-	if ctx.OtherModuleFarDependencyVariantExists(coreVariations, ctx.Module().(*Module).BaseModuleName()) {
-		p.baseProperties.Androidmk_suffix = p.image.moduleNameSuffix()
+	if ctx.OtherModuleFarDependencyVariantExists(variations, ctx.Module().(LinkableInterface).BaseModuleName()) {
+		p.baseProperties.Androidmk_suffix = p.Image.moduleNameSuffix()
 		return
 	}
 
-	// If there is no matching core variation, there could still be a
-	// product variation, for example if a module is product specific and
-	// vendor available. In that case, we also want to add the androidmk
-	// suffix.
-
-	productVariations := append(ctx.Target().Variations(), blueprint.Variation{
+	variations = append(ctx.Target().Variations(), blueprint.Variation{
 		Mutator:   "image",
 		Variation: ProductVariationPrefix + ctx.DeviceConfig().PlatformVndkVersion()})
 
-	if ctx.OtherModuleFarDependencyVariantExists(productVariations, ctx.Module().(*Module).BaseModuleName()) {
-		p.baseProperties.Androidmk_suffix = p.image.moduleNameSuffix()
+	if ctx.OtherModuleFarDependencyVariantExists(variations, ctx.Module().(LinkableInterface).BaseModuleName()) {
+		p.baseProperties.Androidmk_suffix = p.Image.moduleNameSuffix()
 		return
 	}
 
+	images := []SnapshotImage{VendorSnapshotImageSingleton, recoverySnapshotImageSingleton}
+
+	for _, image := range images {
+		if p.Image == image {
+			continue
+		}
+		variations = append(ctx.Target().Variations(), blueprint.Variation{
+			Mutator:   "image",
+			Variation: image.imageVariantName(ctx.DeviceConfig())})
+
+		if ctx.OtherModuleFarDependencyVariantExists(variations,
+			ctx.Module().(LinkableInterface).BaseModuleName()+
+				getSnapshotNameSuffix(
+					image.moduleNameSuffix()+variant,
+					p.Version(),
+					ctx.DeviceConfig().Arches()[0].ArchType.String())) {
+			p.baseProperties.Androidmk_suffix = p.Image.moduleNameSuffix()
+			return
+		}
+	}
+
 	p.baseProperties.Androidmk_suffix = ""
 }
 
 // Call this with a module suffix after creating a snapshot module, such as
 // vendorSnapshotSharedSuffix, recoverySnapshotBinarySuffix, etc.
-func (p *baseSnapshotDecorator) init(m *Module, image snapshotImage, moduleSuffix string) {
-	p.image = image
+func (p *BaseSnapshotDecorator) Init(m LinkableInterface, image SnapshotImage, moduleSuffix string) {
+	p.Image = image
 	p.baseProperties.ModuleSuffix = image.moduleNameSuffix() + moduleSuffix
 	m.AddProperties(&p.baseProperties)
 	android.AddLoadHook(m, func(ctx android.LoadHookContext) {
@@ -493,8 +363,8 @@
 
 // vendorSnapshotLoadHook disables snapshots if it's not BOARD_VNDK_VERSION.
 // As vendor snapshot is only for vendor, such modules won't be used at all.
-func vendorSnapshotLoadHook(ctx android.LoadHookContext, p *baseSnapshotDecorator) {
-	if p.version() != ctx.DeviceConfig().VndkVersion() {
+func vendorSnapshotLoadHook(ctx android.LoadHookContext, p *BaseSnapshotDecorator) {
+	if p.Version() != ctx.DeviceConfig().VndkVersion() {
 		ctx.Module().Disable()
 		return
 	}
@@ -509,7 +379,7 @@
 // include directories, c flags, sanitize dependency information, etc.
 //
 // These modules are auto-generated by development/vendor_snapshot/update.py.
-type snapshotLibraryProperties struct {
+type SnapshotLibraryProperties struct {
 	// Prebuilt file for each arch.
 	Src *string `android:"arch_variant"`
 
@@ -535,14 +405,14 @@
 }
 
 type snapshotLibraryDecorator struct {
-	baseSnapshotDecorator
+	BaseSnapshotDecorator
 	*libraryDecorator
-	properties          snapshotLibraryProperties
+	properties          SnapshotLibraryProperties
 	sanitizerProperties struct {
 		CfiEnabled bool `blueprint:"mutated"`
 
 		// Library flags for cfi variant.
-		Cfi snapshotLibraryProperties `android:"arch_variant"`
+		Cfi SnapshotLibraryProperties `android:"arch_variant"`
 	}
 }
 
@@ -551,9 +421,9 @@
 	return p.libraryDecorator.linkerFlags(ctx, flags)
 }
 
-func (p *snapshotLibraryDecorator) matchesWithDevice(config android.DeviceConfig) bool {
+func (p *snapshotLibraryDecorator) MatchesWithDevice(config android.DeviceConfig) bool {
 	arches := config.Arches()
-	if len(arches) == 0 || arches[0].ArchType.String() != p.arch() {
+	if len(arches) == 0 || arches[0].ArchType.String() != p.Arch() {
 		return false
 	}
 	if !p.header() && p.properties.Src == nil {
@@ -566,7 +436,16 @@
 // As snapshots are prebuilts, this just returns the prebuilt binary after doing things which are
 // done by normal library decorator, e.g. exporting flags.
 func (p *snapshotLibraryDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps, objs Objects) android.Path {
-	p.setSnapshotAndroidMkSuffix(ctx)
+	var variant string
+	if p.shared() {
+		variant = SnapshotSharedSuffix
+	} else if p.static() {
+		variant = SnapshotStaticSuffix
+	} else {
+		variant = snapshotHeaderSuffix
+	}
+
+	p.SetSnapshotAndroidMkSuffix(ctx, variant)
 
 	if p.header() {
 		return p.libraryDecorator.link(ctx, flags, deps, objs)
@@ -576,7 +455,7 @@
 		p.properties = p.sanitizerProperties.Cfi
 	}
 
-	if !p.matchesWithDevice(ctx.DeviceConfig()) {
+	if !p.MatchesWithDevice(ctx.DeviceConfig()) {
 		return nil
 	}
 
@@ -606,9 +485,8 @@
 		transformSharedObjectToToc(ctx, in, tocFile, builderFlags)
 
 		ctx.SetProvider(SharedLibraryInfoProvider, SharedLibraryInfo{
-			SharedLibrary:           in,
-			UnstrippedSharedLibrary: p.unstrippedOutputFile,
-			Target:                  ctx.Target(),
+			SharedLibrary: in,
+			Target:        ctx.Target(),
 
 			TableOfContents: p.tocFile,
 		})
@@ -629,7 +507,7 @@
 }
 
 func (p *snapshotLibraryDecorator) install(ctx ModuleContext, file android.Path) {
-	if p.matchesWithDevice(ctx.DeviceConfig()) && (p.shared() || p.static()) {
+	if p.MatchesWithDevice(ctx.DeviceConfig()) && (p.shared() || p.static()) {
 		p.baseInstaller.install(ctx, file)
 	}
 }
@@ -659,7 +537,7 @@
 	}
 }
 
-func snapshotLibraryFactory(image snapshotImage, moduleSuffix string) (*Module, *snapshotLibraryDecorator) {
+func snapshotLibraryFactory(image SnapshotImage, moduleSuffix string) (*Module, *snapshotLibraryDecorator) {
 	module, library := NewLibrary(android.DeviceSupported)
 
 	module.stl = nil
@@ -682,7 +560,7 @@
 	module.linker = prebuilt
 	module.installer = prebuilt
 
-	prebuilt.init(module, image, moduleSuffix)
+	prebuilt.Init(module, image, moduleSuffix)
 	module.AddProperties(
 		&prebuilt.properties,
 		&prebuilt.sanitizerProperties,
@@ -696,7 +574,7 @@
 // overrides the vendor variant of the cc shared library with the same name, if BOARD_VNDK_VERSION
 // is set.
 func VendorSnapshotSharedFactory() android.Module {
-	module, prebuilt := snapshotLibraryFactory(vendorSnapshotImageSingleton, snapshotSharedSuffix)
+	module, prebuilt := snapshotLibraryFactory(VendorSnapshotImageSingleton, SnapshotSharedSuffix)
 	prebuilt.libraryDecorator.BuildOnlyShared()
 	return module.Init()
 }
@@ -706,7 +584,7 @@
 // overrides the recovery variant of the cc shared library with the same name, if BOARD_VNDK_VERSION
 // is set.
 func RecoverySnapshotSharedFactory() android.Module {
-	module, prebuilt := snapshotLibraryFactory(recoverySnapshotImageSingleton, snapshotSharedSuffix)
+	module, prebuilt := snapshotLibraryFactory(recoverySnapshotImageSingleton, SnapshotSharedSuffix)
 	prebuilt.libraryDecorator.BuildOnlyShared()
 	return module.Init()
 }
@@ -716,7 +594,7 @@
 // overrides the vendor variant of the cc static library with the same name, if BOARD_VNDK_VERSION
 // is set.
 func VendorSnapshotStaticFactory() android.Module {
-	module, prebuilt := snapshotLibraryFactory(vendorSnapshotImageSingleton, snapshotStaticSuffix)
+	module, prebuilt := snapshotLibraryFactory(VendorSnapshotImageSingleton, SnapshotStaticSuffix)
 	prebuilt.libraryDecorator.BuildOnlyStatic()
 	return module.Init()
 }
@@ -726,7 +604,7 @@
 // overrides the recovery variant of the cc static library with the same name, if BOARD_VNDK_VERSION
 // is set.
 func RecoverySnapshotStaticFactory() android.Module {
-	module, prebuilt := snapshotLibraryFactory(recoverySnapshotImageSingleton, snapshotStaticSuffix)
+	module, prebuilt := snapshotLibraryFactory(recoverySnapshotImageSingleton, SnapshotStaticSuffix)
 	prebuilt.libraryDecorator.BuildOnlyStatic()
 	return module.Init()
 }
@@ -736,7 +614,7 @@
 // overrides the vendor variant of the cc header library with the same name, if BOARD_VNDK_VERSION
 // is set.
 func VendorSnapshotHeaderFactory() android.Module {
-	module, prebuilt := snapshotLibraryFactory(vendorSnapshotImageSingleton, snapshotHeaderSuffix)
+	module, prebuilt := snapshotLibraryFactory(VendorSnapshotImageSingleton, snapshotHeaderSuffix)
 	prebuilt.libraryDecorator.HeaderOnly()
 	return module.Init()
 }
@@ -766,13 +644,13 @@
 }
 
 type snapshotBinaryDecorator struct {
-	baseSnapshotDecorator
+	BaseSnapshotDecorator
 	*binaryDecorator
 	properties snapshotBinaryProperties
 }
 
-func (p *snapshotBinaryDecorator) matchesWithDevice(config android.DeviceConfig) bool {
-	if config.DeviceArch() != p.arch() {
+func (p *snapshotBinaryDecorator) MatchesWithDevice(config android.DeviceConfig) bool {
+	if config.DeviceArch() != p.Arch() {
 		return false
 	}
 	if p.properties.Src == nil {
@@ -784,9 +662,9 @@
 // cc modules' link functions are to link compiled objects into final binaries.
 // As snapshots are prebuilts, this just returns the prebuilt binary
 func (p *snapshotBinaryDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps, objs Objects) android.Path {
-	p.setSnapshotAndroidMkSuffix(ctx)
+	p.SetSnapshotAndroidMkSuffix(ctx, snapshotBinarySuffix)
 
-	if !p.matchesWithDevice(ctx.DeviceConfig()) {
+	if !p.MatchesWithDevice(ctx.DeviceConfig()) {
 		return nil
 	}
 
@@ -814,7 +692,7 @@
 // development/vendor_snapshot/update.py. As a part of vendor snapshot, vendor_snapshot_binary
 // overrides the vendor variant of the cc binary with the same name, if BOARD_VNDK_VERSION is set.
 func VendorSnapshotBinaryFactory() android.Module {
-	return snapshotBinaryFactory(vendorSnapshotImageSingleton, snapshotBinarySuffix)
+	return snapshotBinaryFactory(VendorSnapshotImageSingleton, snapshotBinarySuffix)
 }
 
 // recovery_snapshot_binary is a special prebuilt executable binary which is auto-generated by
@@ -824,7 +702,7 @@
 	return snapshotBinaryFactory(recoverySnapshotImageSingleton, snapshotBinarySuffix)
 }
 
-func snapshotBinaryFactory(image snapshotImage, moduleSuffix string) android.Module {
+func snapshotBinaryFactory(image SnapshotImage, moduleSuffix string) android.Module {
 	module, binary := NewBinary(android.DeviceSupported)
 	binary.baseLinker.Properties.No_libcrt = BoolPtr(true)
 	binary.baseLinker.Properties.Nocrt = BoolPtr(true)
@@ -843,7 +721,7 @@
 	module.stl = nil
 	module.linker = prebuilt
 
-	prebuilt.init(module, image, moduleSuffix)
+	prebuilt.Init(module, image, moduleSuffix)
 	module.AddProperties(&prebuilt.properties)
 	return module.Init()
 }
@@ -861,13 +739,13 @@
 }
 
 type snapshotObjectLinker struct {
-	baseSnapshotDecorator
+	BaseSnapshotDecorator
 	objectLinker
 	properties vendorSnapshotObjectProperties
 }
 
-func (p *snapshotObjectLinker) matchesWithDevice(config android.DeviceConfig) bool {
-	if config.DeviceArch() != p.arch() {
+func (p *snapshotObjectLinker) MatchesWithDevice(config android.DeviceConfig) bool {
+	if config.DeviceArch() != p.Arch() {
 		return false
 	}
 	if p.properties.Src == nil {
@@ -879,9 +757,9 @@
 // cc modules' link functions are to link compiled objects into final binaries.
 // As snapshots are prebuilts, this just returns the prebuilt binary
 func (p *snapshotObjectLinker) link(ctx ModuleContext, flags Flags, deps PathDeps, objs Objects) android.Path {
-	p.setSnapshotAndroidMkSuffix(ctx)
+	p.SetSnapshotAndroidMkSuffix(ctx, snapshotObjectSuffix)
 
-	if !p.matchesWithDevice(ctx.DeviceConfig()) {
+	if !p.MatchesWithDevice(ctx.DeviceConfig()) {
 		return nil
 	}
 
@@ -896,7 +774,7 @@
 // development/vendor_snapshot/update.py. As a part of vendor snapshot, vendor_snapshot_object
 // overrides the vendor variant of the cc object with the same name, if BOARD_VNDK_VERSION is set.
 func VendorSnapshotObjectFactory() android.Module {
-	module := newObject()
+	module := newObject(android.DeviceSupported)
 
 	prebuilt := &snapshotObjectLinker{
 		objectLinker: objectLinker{
@@ -905,7 +783,7 @@
 	}
 	module.linker = prebuilt
 
-	prebuilt.init(module, vendorSnapshotImageSingleton, snapshotObjectSuffix)
+	prebuilt.Init(module, VendorSnapshotImageSingleton, snapshotObjectSuffix)
 	module.AddProperties(&prebuilt.properties)
 	return module.Init()
 }
@@ -914,7 +792,7 @@
 // development/vendor_snapshot/update.py. As a part of recovery snapshot, recovery_snapshot_object
 // overrides the recovery variant of the cc object with the same name, if BOARD_VNDK_VERSION is set.
 func RecoverySnapshotObjectFactory() android.Module {
-	module := newObject()
+	module := newObject(android.DeviceSupported)
 
 	prebuilt := &snapshotObjectLinker{
 		objectLinker: objectLinker{
@@ -923,19 +801,19 @@
 	}
 	module.linker = prebuilt
 
-	prebuilt.init(module, recoverySnapshotImageSingleton, snapshotObjectSuffix)
+	prebuilt.Init(module, recoverySnapshotImageSingleton, snapshotObjectSuffix)
 	module.AddProperties(&prebuilt.properties)
 	return module.Init()
 }
 
-type snapshotInterface interface {
-	matchesWithDevice(config android.DeviceConfig) bool
-	isSnapshotPrebuilt() bool
-	version() string
-	snapshotAndroidMkSuffix() string
+type SnapshotInterface interface {
+	MatchesWithDevice(config android.DeviceConfig) bool
+	IsSnapshotPrebuilt() bool
+	Version() string
+	SnapshotAndroidMkSuffix() string
 }
 
-var _ snapshotInterface = (*vndkPrebuiltLibraryDecorator)(nil)
-var _ snapshotInterface = (*snapshotLibraryDecorator)(nil)
-var _ snapshotInterface = (*snapshotBinaryDecorator)(nil)
-var _ snapshotInterface = (*snapshotObjectLinker)(nil)
+var _ SnapshotInterface = (*vndkPrebuiltLibraryDecorator)(nil)
+var _ SnapshotInterface = (*snapshotLibraryDecorator)(nil)
+var _ SnapshotInterface = (*snapshotBinaryDecorator)(nil)
+var _ SnapshotInterface = (*snapshotObjectLinker)(nil)
diff --git a/cc/snapshot_utils.go b/cc/snapshot_utils.go
index 8eb6164..24abcce 100644
--- a/cc/snapshot_utils.go
+++ b/cc/snapshot_utils.go
@@ -20,7 +20,7 @@
 )
 
 var (
-	headerExts = []string{".h", ".hh", ".hpp", ".hxx", ".h++", ".inl", ".inc", ".ipp", ".h.generic"}
+	HeaderExts = []string{".h", ".hh", ".hpp", ".hxx", ".h++", ".inl", ".inc", ".ipp", ".h.generic"}
 )
 
 func (m *Module) IsSnapshotLibrary() bool {
@@ -53,6 +53,10 @@
 	return m.Properties.SnapshotSharedLibs
 }
 
+func (m *Module) SnapshotStaticLibs() []string {
+	return m.Properties.SnapshotStaticLibs
+}
+
 // snapshotLibraryInterface is an interface for libraries captured to VNDK / vendor snapshots.
 type snapshotLibraryInterface interface {
 	libraryInterface
@@ -109,8 +113,8 @@
 		return ctx.Config().VndkSnapshotBuildArtifacts()
 	}
 
-	for _, image := range []snapshotImage{vendorSnapshotImageSingleton, recoverySnapshotImageSingleton} {
-		if isSnapshotAware(ctx.DeviceConfig(), m, image.isProprietaryPath(ctx.ModuleDir(), ctx.DeviceConfig()), apexInfo, image) {
+	for _, image := range []SnapshotImage{VendorSnapshotImageSingleton, recoverySnapshotImageSingleton} {
+		if isSnapshotAware(ctx.DeviceConfig(), m, image.IsProprietaryPath(ctx.ModuleDir(), ctx.DeviceConfig()), apexInfo, image) {
 			return true
 		}
 	}
diff --git a/cc/stl.go b/cc/stl.go
index 75921c6..0f2a878 100644
--- a/cc/stl.go
+++ b/cc/stl.go
@@ -15,8 +15,9 @@
 package cc
 
 import (
-	"android/soong/android"
 	"fmt"
+
+	"android/soong/android"
 )
 
 func getNdkStlFamily(m LinkableInterface) string {
@@ -92,26 +93,6 @@
 				ctx.ModuleErrorf("stl: %q is not a supported STL for windows", s)
 				return ""
 			}
-		} else if ctx.Fuchsia() {
-			switch s {
-			case "c++_static":
-				return "libc++_static"
-			case "c++_shared":
-				return "libc++"
-			case "libc++", "libc++_static":
-				return s
-			case "none":
-				return ""
-			case "":
-				if ctx.static() {
-					return "libc++_static"
-				} else {
-					return "libc++"
-				}
-			default:
-				ctx.ModuleErrorf("stl: %q is not a supported STL on Fuchsia", s)
-				return ""
-			}
 		} else {
 			switch s {
 			case "libc++", "libc++_static":
@@ -199,7 +180,9 @@
 			deps.StaticLibs = append(deps.StaticLibs, stl.Properties.SelectedStl, "ndk_libc++abi")
 		}
 		if needsLibAndroidSupport(ctx) {
-			deps.StaticLibs = append(deps.StaticLibs, "ndk_libandroid_support")
+			// Use LateStaticLibs for ndk_libandroid_support so that its include directories
+			// come after ndk_libc++_static or ndk_libc++_shared.
+			deps.LateStaticLibs = append(deps.LateStaticLibs, "ndk_libandroid_support")
 		}
 		deps.StaticLibs = append(deps.StaticLibs, "ndk_libunwind")
 	default:
diff --git a/cc/strip.go b/cc/strip.go
index b1f34bb..c60e135 100644
--- a/cc/strip.go
+++ b/cc/strip.go
@@ -59,6 +59,7 @@
 	return !forceDisable && (forceEnable || defaultEnable)
 }
 
+// Keep this consistent with //build/bazel/rules/stripped_shared_library.bzl.
 func (stripper *Stripper) strip(actx android.ModuleContext, in android.Path, out android.ModuleOutPath,
 	flags StripFlags, isStaticLib bool) {
 	if actx.Darwin() {
diff --git a/cc/symbolfile/__init__.py b/cc/symbolfile/__init__.py
index 5678e7d..f8d1841 100644
--- a/cc/symbolfile/__init__.py
+++ b/cc/symbolfile/__init__.py
@@ -14,18 +14,22 @@
 # limitations under the License.
 #
 """Parser for Android's version script information."""
-from dataclasses import dataclass
+from __future__ import annotations
+
+from dataclasses import dataclass, field
 import logging
 import re
 from typing import (
     Dict,
     Iterable,
+    Iterator,
     List,
     Mapping,
     NewType,
     Optional,
     TextIO,
     Tuple,
+    Union,
 )
 
 
@@ -52,11 +56,52 @@
 
 
 @dataclass
+class Tags:
+    """Container class for the tags attached to a symbol or version."""
+
+    tags: tuple[Tag, ...] = field(default_factory=tuple)
+
+    @classmethod
+    def from_strs(cls, strs: Iterable[str]) -> Tags:
+        """Constructs tags from a collection of strings.
+
+        Does not decode API levels.
+        """
+        return Tags(tuple(Tag(s) for s in strs))
+
+    def __contains__(self, tag: Union[Tag, str]) -> bool:
+        return tag in self.tags
+
+    def __iter__(self) -> Iterator[Tag]:
+        yield from self.tags
+
+    @property
+    def has_mode_tags(self) -> bool:
+        """Returns True if any mode tags (apex, llndk, etc) are set."""
+        return self.has_apex_tags or self.has_llndk_tags
+
+    @property
+    def has_apex_tags(self) -> bool:
+        """Returns True if any APEX tags are set."""
+        return 'apex' in self.tags or 'systemapi' in self.tags
+
+    @property
+    def has_llndk_tags(self) -> bool:
+        """Returns True if any LL-NDK tags are set."""
+        return 'llndk' in self.tags
+
+    @property
+    def has_platform_only_tags(self) -> bool:
+        """Returns True if any platform-only tags are set."""
+        return 'platform-only' in self.tags
+
+
+@dataclass
 class Symbol:
     """A symbol definition from a symbol file."""
 
     name: str
-    tags: List[Tag]
+    tags: Tags
 
 
 @dataclass
@@ -65,14 +110,22 @@
 
     name: str
     base: Optional[str]
-    tags: List[Tag]
+    tags: Tags
     symbols: List[Symbol]
 
+    @property
+    def is_private(self) -> bool:
+        """Returns True if this version block is private (platform only)."""
+        return self.name.endswith('_PRIVATE') or self.name.endswith('_PLATFORM')
 
-def get_tags(line: str) -> List[Tag]:
+
+def get_tags(line: str, api_map: ApiMap) -> Tags:
     """Returns a list of all tags on this line."""
     _, _, all_tags = line.strip().partition('#')
-    return [Tag(e) for e in re.split(r'\s+', all_tags) if e.strip()]
+    return Tags(tuple(
+        decode_api_level_tag(Tag(e), api_map)
+        for e in re.split(r'\s+', all_tags) if e.strip()
+    ))
 
 
 def is_api_level_tag(tag: Tag) -> bool:
@@ -104,24 +157,21 @@
     return api_map[api]
 
 
-def decode_api_level_tags(tags: Iterable[Tag], api_map: ApiMap) -> List[Tag]:
-    """Decodes API level code names in a list of tags.
+def decode_api_level_tag(tag: Tag, api_map: ApiMap) -> Tag:
+    """Decodes API level code name in a tag.
 
     Raises:
         ParseError: An unknown version name was found in a tag.
     """
-    decoded_tags = list(tags)
-    for idx, tag in enumerate(tags):
-        if not is_api_level_tag(tag):
-            continue
-        name, value = split_tag(tag)
+    if not is_api_level_tag(tag):
+        return tag
 
-        try:
-            decoded = str(decode_api_level(value, api_map))
-            decoded_tags[idx] = Tag('='.join([name, decoded]))
-        except KeyError:
-            raise ParseError(f'Unknown version name in tag: {tag}')
-    return decoded_tags
+    name, value = split_tag(tag)
+    try:
+        decoded = str(decode_api_level(value, api_map))
+        return Tag(f'{name}={decoded}')
+    except KeyError as ex:
+        raise ParseError(f'Unknown version name in tag: {tag}') from ex
 
 
 def split_tag(tag: Tag) -> Tuple[str, str]:
@@ -149,55 +199,52 @@
     return split_tag(tag)[1]
 
 
-def version_is_private(version: str) -> bool:
-    """Returns True if the version name should be treated as private."""
-    return version.endswith('_PRIVATE') or version.endswith('_PLATFORM')
+def _should_omit_tags(tags: Tags, arch: Arch, api: int, llndk: bool,
+                      apex: bool) -> bool:
+    """Returns True if the tagged object should be omitted.
+
+    This defines the rules shared between version tagging and symbol tagging.
+    """
+    # The apex and llndk tags will only exclude APIs from other modes. If in
+    # APEX or LLNDK mode and neither tag is provided, we fall back to the
+    # default behavior because all NDK symbols are implicitly available to APEX
+    # and LLNDK.
+    if tags.has_mode_tags:
+        if not apex and not llndk:
+            return True
+        if apex and not tags.has_apex_tags:
+            return True
+        if llndk and not tags.has_llndk_tags:
+            return True
+    if not symbol_in_arch(tags, arch):
+        return True
+    if not symbol_in_api(tags, arch, api):
+        return True
+    return False
 
 
 def should_omit_version(version: Version, arch: Arch, api: int, llndk: bool,
                         apex: bool) -> bool:
-    """Returns True if the version section should be ommitted.
+    """Returns True if the version section should be omitted.
 
     We want to omit any sections that do not have any symbols we'll have in the
     stub library. Sections that contain entirely future symbols or only symbols
     for certain architectures.
     """
-    if version_is_private(version.name):
+    if version.is_private:
         return True
-    if 'platform-only' in version.tags:
+    if version.tags.has_platform_only_tags:
         return True
-
-    no_llndk_no_apex = ('llndk' not in version.tags
-                        and 'apex' not in version.tags)
-    keep = no_llndk_no_apex or \
-           ('llndk' in version.tags and llndk) or \
-           ('apex' in version.tags and apex)
-    if not keep:
-        return True
-    if not symbol_in_arch(version.tags, arch):
-        return True
-    if not symbol_in_api(version.tags, arch, api):
-        return True
-    return False
+    return _should_omit_tags(version.tags, arch, api, llndk, apex)
 
 
 def should_omit_symbol(symbol: Symbol, arch: Arch, api: int, llndk: bool,
                        apex: bool) -> bool:
     """Returns True if the symbol should be omitted."""
-    no_llndk_no_apex = 'llndk' not in symbol.tags and 'apex' not in symbol.tags
-    keep = no_llndk_no_apex or \
-           ('llndk' in symbol.tags and llndk) or \
-           ('apex' in symbol.tags and apex)
-    if not keep:
-        return True
-    if not symbol_in_arch(symbol.tags, arch):
-        return True
-    if not symbol_in_api(symbol.tags, arch, api):
-        return True
-    return False
+    return _should_omit_tags(symbol.tags, arch, api, llndk, apex)
 
 
-def symbol_in_arch(tags: Iterable[Tag], arch: Arch) -> bool:
+def symbol_in_arch(tags: Tags, arch: Arch) -> bool:
     """Returns true if the symbol is present for the given architecture."""
     has_arch_tags = False
     for tag in tags:
@@ -282,7 +329,7 @@
     def parse(self) -> List[Version]:
         """Parses the symbol file and returns a list of Version objects."""
         versions = []
-        while self.next_line() != '':
+        while self.next_line():
             assert self.current_line is not None
             if '{' in self.current_line:
                 versions.append(self.parse_version())
@@ -325,12 +372,11 @@
         """Parses a single version section and returns a Version object."""
         assert self.current_line is not None
         name = self.current_line.split('{')[0].strip()
-        tags = get_tags(self.current_line)
-        tags = decode_api_level_tags(tags, self.api_map)
+        tags = get_tags(self.current_line, self.api_map)
         symbols: List[Symbol] = []
         global_scope = True
         cpp_symbols = False
-        while self.next_line() != '':
+        while self.next_line():
             if '}' in self.current_line:
                 # Line is something like '} BASE; # tags'. Both base and tags
                 # are optional here.
@@ -373,8 +419,7 @@
                 'Wildcard global symbols are not permitted.')
         # Line is now in the format "<symbol-name>; # tags"
         name, _, _ = self.current_line.strip().partition(';')
-        tags = get_tags(self.current_line)
-        tags = decode_api_level_tags(tags, self.api_map)
+        tags = get_tags(self.current_line, self.api_map)
         return Symbol(name, tags)
 
     def next_line(self) -> str:
@@ -383,11 +428,11 @@
         A return value of '' indicates EOF.
         """
         line = self.input_file.readline()
-        while line.strip() == '' or line.strip().startswith('#'):
+        while not line.strip() or line.strip().startswith('#'):
             line = self.input_file.readline()
 
             # We want to skip empty lines, but '' indicates EOF.
-            if line == '':
+            if not line:
                 break
         self.current_line = line
         return self.current_line
diff --git a/cc/symbolfile/test_symbolfile.py b/cc/symbolfile/test_symbolfile.py
index 92b1399..c1e8219 100644
--- a/cc/symbolfile/test_symbolfile.py
+++ b/cc/symbolfile/test_symbolfile.py
@@ -19,7 +19,7 @@
 import unittest
 
 import symbolfile
-from symbolfile import Arch, Tag
+from symbolfile import Arch, Tag, Tags, Version
 
 # pylint: disable=missing-docstring
 
@@ -35,12 +35,14 @@
 
 class TagsTest(unittest.TestCase):
     def test_get_tags_no_tags(self) -> None:
-        self.assertEqual([], symbolfile.get_tags(''))
-        self.assertEqual([], symbolfile.get_tags('foo bar baz'))
+        self.assertEqual(Tags(), symbolfile.get_tags('', {}))
+        self.assertEqual(Tags(), symbolfile.get_tags('foo bar baz', {}))
 
     def test_get_tags(self) -> None:
-        self.assertEqual(['foo', 'bar'], symbolfile.get_tags('# foo bar'))
-        self.assertEqual(['bar', 'baz'], symbolfile.get_tags('foo # bar baz'))
+        self.assertEqual(Tags.from_strs(['foo', 'bar']),
+                         symbolfile.get_tags('# foo bar', {}))
+        self.assertEqual(Tags.from_strs(['bar', 'baz']),
+                         symbolfile.get_tags('foo # bar baz', {}))
 
     def test_split_tag(self) -> None:
         self.assertTupleEqual(('foo', 'bar'),
@@ -77,12 +79,14 @@
         }
 
         tags = [
-            Tag('introduced=9'),
-            Tag('introduced-arm=14'),
-            Tag('versioned=16'),
-            Tag('arm'),
-            Tag('introduced=O'),
-            Tag('introduced=P'),
+            symbolfile.decode_api_level_tag(t, api_map) for t in (
+                Tag('introduced=9'),
+                Tag('introduced-arm=14'),
+                Tag('versioned=16'),
+                Tag('arm'),
+                Tag('introduced=O'),
+                Tag('introduced=P'),
+            )
         ]
         expected_tags = [
             Tag('introduced=9'),
@@ -92,33 +96,37 @@
             Tag('introduced=9000'),
             Tag('introduced=9001'),
         ]
-        self.assertListEqual(
-            expected_tags, symbolfile.decode_api_level_tags(tags, api_map))
+        self.assertListEqual(expected_tags, tags)
 
         with self.assertRaises(symbolfile.ParseError):
-            symbolfile.decode_api_level_tags([Tag('introduced=O')], {})
+            symbolfile.decode_api_level_tag(Tag('introduced=O'), {})
 
 
 class PrivateVersionTest(unittest.TestCase):
     def test_version_is_private(self) -> None:
-        self.assertFalse(symbolfile.version_is_private('foo'))
-        self.assertFalse(symbolfile.version_is_private('PRIVATE'))
-        self.assertFalse(symbolfile.version_is_private('PLATFORM'))
-        self.assertFalse(symbolfile.version_is_private('foo_private'))
-        self.assertFalse(symbolfile.version_is_private('foo_platform'))
-        self.assertFalse(symbolfile.version_is_private('foo_PRIVATE_'))
-        self.assertFalse(symbolfile.version_is_private('foo_PLATFORM_'))
+        def mock_version(name: str) -> Version:
+            return Version(name, base=None, tags=Tags(), symbols=[])
 
-        self.assertTrue(symbolfile.version_is_private('foo_PRIVATE'))
-        self.assertTrue(symbolfile.version_is_private('foo_PLATFORM'))
+        self.assertFalse(mock_version('foo').is_private)
+        self.assertFalse(mock_version('PRIVATE').is_private)
+        self.assertFalse(mock_version('PLATFORM').is_private)
+        self.assertFalse(mock_version('foo_private').is_private)
+        self.assertFalse(mock_version('foo_platform').is_private)
+        self.assertFalse(mock_version('foo_PRIVATE_').is_private)
+        self.assertFalse(mock_version('foo_PLATFORM_').is_private)
+
+        self.assertTrue(mock_version('foo_PRIVATE').is_private)
+        self.assertTrue(mock_version('foo_PLATFORM').is_private)
 
 
 class SymbolPresenceTest(unittest.TestCase):
     def test_symbol_in_arch(self) -> None:
-        self.assertTrue(symbolfile.symbol_in_arch([], Arch('arm')))
-        self.assertTrue(symbolfile.symbol_in_arch([Tag('arm')], Arch('arm')))
+        self.assertTrue(symbolfile.symbol_in_arch(Tags(), Arch('arm')))
+        self.assertTrue(
+            symbolfile.symbol_in_arch(Tags.from_strs(['arm']), Arch('arm')))
 
-        self.assertFalse(symbolfile.symbol_in_arch([Tag('x86')], Arch('arm')))
+        self.assertFalse(
+            symbolfile.symbol_in_arch(Tags.from_strs(['x86']), Arch('arm')))
 
     def test_symbol_in_api(self) -> None:
         self.assertTrue(symbolfile.symbol_in_api([], Arch('arm'), 9))
@@ -197,81 +205,99 @@
     def test_omit_private(self) -> None:
         self.assertFalse(
             symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, [], []), Arch('arm'), 9, False,
-                False))
+                symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9,
+                False, False))
 
         self.assertTrue(
             symbolfile.should_omit_version(
-                symbolfile.Version('foo_PRIVATE', None, [], []), Arch('arm'),
-                9, False, False))
+                symbolfile.Version('foo_PRIVATE', None, Tags(), []),
+                Arch('arm'), 9, False, False))
         self.assertTrue(
             symbolfile.should_omit_version(
-                symbolfile.Version('foo_PLATFORM', None, [], []), Arch('arm'),
-                9, False, False))
+                symbolfile.Version('foo_PLATFORM', None, Tags(), []),
+                Arch('arm'), 9, False, False))
 
         self.assertTrue(
             symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, [Tag('platform-only')], []),
+                symbolfile.Version('foo', None,
+                                   Tags.from_strs(['platform-only']), []),
                 Arch('arm'), 9, False, False))
 
     def test_omit_llndk(self) -> None:
         self.assertTrue(
             symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, [Tag('llndk')], []),
+                symbolfile.Version('foo', None, Tags.from_strs(['llndk']), []),
                 Arch('arm'), 9, False, False))
 
         self.assertFalse(
             symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, [], []), Arch('arm'), 9, True,
-                False))
+                symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9,
+                True, False))
         self.assertFalse(
             symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, [Tag('llndk')], []),
+                symbolfile.Version('foo', None, Tags.from_strs(['llndk']), []),
                 Arch('arm'), 9, True, False))
 
     def test_omit_apex(self) -> None:
         self.assertTrue(
             symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, [Tag('apex')], []),
+                symbolfile.Version('foo', None, Tags.from_strs(['apex']), []),
                 Arch('arm'), 9, False, False))
 
         self.assertFalse(
             symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, [], []), Arch('arm'), 9, False,
-                True))
+                symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9,
+                False, True))
         self.assertFalse(
             symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, [Tag('apex')], []),
+                symbolfile.Version('foo', None, Tags.from_strs(['apex']), []),
                 Arch('arm'), 9, False, True))
 
+    def test_omit_systemapi(self) -> None:
+        self.assertTrue(
+            symbolfile.should_omit_version(
+                symbolfile.Version('foo', None, Tags.from_strs(['systemapi']),
+                                   []), Arch('arm'), 9, False, False))
+
+        self.assertFalse(
+            symbolfile.should_omit_version(
+                symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9,
+                False, True))
+        self.assertFalse(
+            symbolfile.should_omit_version(
+                symbolfile.Version('foo', None, Tags.from_strs(['systemapi']),
+                                   []), Arch('arm'), 9, False, True))
+
     def test_omit_arch(self) -> None:
         self.assertFalse(
             symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, [], []), Arch('arm'), 9, False,
-                False))
+                symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9,
+                False, False))
         self.assertFalse(
             symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, [Tag('arm')], []), Arch('arm'),
-                9, False, False))
-
-        self.assertTrue(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, [Tag('x86')], []), Arch('arm'),
-                9, False, False))
-
-    def test_omit_api(self) -> None:
-        self.assertFalse(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, [], []), Arch('arm'), 9, False,
-                False))
-        self.assertFalse(
-            symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, [Tag('introduced=9')], []),
+                symbolfile.Version('foo', None, Tags.from_strs(['arm']), []),
                 Arch('arm'), 9, False, False))
 
         self.assertTrue(
             symbolfile.should_omit_version(
-                symbolfile.Version('foo', None, [Tag('introduced=14')], []),
+                symbolfile.Version('foo', None, Tags.from_strs(['x86']), []),
+                Arch('arm'), 9, False, False))
+
+    def test_omit_api(self) -> None:
+        self.assertFalse(
+            symbolfile.should_omit_version(
+                symbolfile.Version('foo', None, Tags(), []), Arch('arm'), 9,
+                False, False))
+        self.assertFalse(
+            symbolfile.should_omit_version(
+                symbolfile.Version('foo', None,
+                                   Tags.from_strs(['introduced=9']), []),
+                Arch('arm'), 9, False, False))
+
+        self.assertTrue(
+            symbolfile.should_omit_version(
+                symbolfile.Version('foo', None,
+                                   Tags.from_strs(['introduced=14']), []),
                 Arch('arm'), 9, False, False))
 
 
@@ -279,58 +305,72 @@
     def test_omit_llndk(self) -> None:
         self.assertTrue(
             symbolfile.should_omit_symbol(
-                symbolfile.Symbol('foo', [Tag('llndk')]), Arch('arm'), 9,
-                False, False))
+                symbolfile.Symbol('foo', Tags.from_strs(['llndk'])),
+                Arch('arm'), 9, False, False))
 
         self.assertFalse(
-            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []),
+            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', Tags()),
                                           Arch('arm'), 9, True, False))
         self.assertFalse(
             symbolfile.should_omit_symbol(
-                symbolfile.Symbol('foo', [Tag('llndk')]), Arch('arm'), 9, True,
-                False))
+                symbolfile.Symbol('foo', Tags.from_strs(['llndk'])),
+                Arch('arm'), 9, True, False))
 
     def test_omit_apex(self) -> None:
         self.assertTrue(
             symbolfile.should_omit_symbol(
-                symbolfile.Symbol('foo', [Tag('apex')]), Arch('arm'), 9, False,
-                False))
+                symbolfile.Symbol('foo', Tags.from_strs(['apex'])),
+                Arch('arm'), 9, False, False))
 
         self.assertFalse(
-            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []),
+            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', Tags()),
                                           Arch('arm'), 9, False, True))
         self.assertFalse(
             symbolfile.should_omit_symbol(
-                symbolfile.Symbol('foo', [Tag('apex')]), Arch('arm'), 9, False,
-                True))
+                symbolfile.Symbol('foo', Tags.from_strs(['apex'])),
+                Arch('arm'), 9, False, True))
+
+    def test_omit_systemapi(self) -> None:
+        self.assertTrue(
+            symbolfile.should_omit_symbol(
+                symbolfile.Symbol('foo', Tags.from_strs(['systemapi'])),
+                Arch('arm'), 9, False, False))
+
+        self.assertFalse(
+            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', Tags()),
+                                          Arch('arm'), 9, False, True))
+        self.assertFalse(
+            symbolfile.should_omit_symbol(
+                symbolfile.Symbol('foo', Tags.from_strs(['systemapi'])),
+                Arch('arm'), 9, False, True))
 
     def test_omit_arch(self) -> None:
         self.assertFalse(
-            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []),
+            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', Tags()),
                                           Arch('arm'), 9, False, False))
         self.assertFalse(
             symbolfile.should_omit_symbol(
-                symbolfile.Symbol('foo', [Tag('arm')]), Arch('arm'), 9, False,
-                False))
+                symbolfile.Symbol('foo', Tags.from_strs(['arm'])), Arch('arm'),
+                9, False, False))
 
         self.assertTrue(
             symbolfile.should_omit_symbol(
-                symbolfile.Symbol('foo', [Tag('x86')]), Arch('arm'), 9, False,
-                False))
+                symbolfile.Symbol('foo', Tags.from_strs(['x86'])), Arch('arm'),
+                9, False, False))
 
     def test_omit_api(self) -> None:
         self.assertFalse(
-            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', []),
+            symbolfile.should_omit_symbol(symbolfile.Symbol('foo', Tags()),
                                           Arch('arm'), 9, False, False))
         self.assertFalse(
             symbolfile.should_omit_symbol(
-                symbolfile.Symbol('foo', [Tag('introduced=9')]), Arch('arm'),
-                9, False, False))
+                symbolfile.Symbol('foo', Tags.from_strs(['introduced=9'])),
+                Arch('arm'), 9, False, False))
 
         self.assertTrue(
             symbolfile.should_omit_symbol(
-                symbolfile.Symbol('foo', [Tag('introduced=14')]), Arch('arm'),
-                9, False, False))
+                symbolfile.Symbol('foo', Tags.from_strs(['introduced=14'])),
+                Arch('arm'), 9, False, False))
 
 
 class SymbolFileParseTest(unittest.TestCase):
@@ -376,11 +416,11 @@
         version = parser.parse_version()
         self.assertEqual('VERSION_1', version.name)
         self.assertIsNone(version.base)
-        self.assertEqual(['foo', 'bar'], version.tags)
+        self.assertEqual(Tags.from_strs(['foo', 'bar']), version.tags)
 
         expected_symbols = [
-            symbolfile.Symbol('baz', []),
-            symbolfile.Symbol('qux', [Tag('woodly'), Tag('doodly')]),
+            symbolfile.Symbol('baz', Tags()),
+            symbolfile.Symbol('qux', Tags.from_strs(['woodly', 'doodly'])),
         ]
         self.assertEqual(expected_symbols, version.symbols)
 
@@ -388,7 +428,7 @@
         version = parser.parse_version()
         self.assertEqual('VERSION_2', version.name)
         self.assertEqual('VERSION_1', version.base)
-        self.assertEqual([], version.tags)
+        self.assertEqual(Tags(), version.tags)
 
     def test_parse_version_eof(self) -> None:
         input_file = io.StringIO(textwrap.dedent("""\
@@ -423,12 +463,12 @@
         parser.next_line()
         symbol = parser.parse_symbol()
         self.assertEqual('foo', symbol.name)
-        self.assertEqual([], symbol.tags)
+        self.assertEqual(Tags(), symbol.tags)
 
         parser.next_line()
         symbol = parser.parse_symbol()
         self.assertEqual('bar', symbol.name)
-        self.assertEqual(['baz', 'qux'], symbol.tags)
+        self.assertEqual(Tags.from_strs(['baz', 'qux']), symbol.tags)
 
     def test_wildcard_symbol_global(self) -> None:
         input_file = io.StringIO(textwrap.dedent("""\
@@ -497,14 +537,15 @@
         versions = parser.parse()
 
         expected = [
-            symbolfile.Version('VERSION_1', None, [], [
-                symbolfile.Symbol('foo', []),
-                symbolfile.Symbol('bar', [Tag('baz')]),
+            symbolfile.Version('VERSION_1', None, Tags(), [
+                symbolfile.Symbol('foo', Tags()),
+                symbolfile.Symbol('bar', Tags.from_strs(['baz'])),
             ]),
-            symbolfile.Version('VERSION_2', 'VERSION_1', [Tag('wasd')], [
-                symbolfile.Symbol('woodly', []),
-                symbolfile.Symbol('doodly', [Tag('asdf')]),
-            ]),
+            symbolfile.Version(
+                'VERSION_2', 'VERSION_1', Tags.from_strs(['wasd']), [
+                    symbolfile.Symbol('woodly', Tags()),
+                    symbolfile.Symbol('doodly', Tags.from_strs(['asdf'])),
+                ]),
         ]
 
         self.assertEqual(expected, versions)
@@ -527,10 +568,10 @@
         self.assertIsNone(version.base)
 
         expected_symbols = [
-            symbolfile.Symbol('foo', []),
-            symbolfile.Symbol('bar', [Tag('llndk')]),
-            symbolfile.Symbol('baz', [Tag('llndk'), Tag('apex')]),
-            symbolfile.Symbol('qux', [Tag('apex')]),
+            symbolfile.Symbol('foo', Tags()),
+            symbolfile.Symbol('bar', Tags.from_strs(['llndk'])),
+            symbolfile.Symbol('baz', Tags.from_strs(['llndk', 'apex'])),
+            symbolfile.Symbol('qux', Tags.from_strs(['apex'])),
         ]
         self.assertEqual(expected_symbols, version.symbols)
 
diff --git a/cc/testing.go b/cc/testing.go
index 15f7ebb..d0dca6b 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -15,8 +15,12 @@
 package cc
 
 import (
+	"path/filepath"
+	"testing"
+
 	"android/soong/android"
 	"android/soong/genrule"
+	"android/soong/snapshot"
 )
 
 func RegisterRequiredBuildComponentsForTest(ctx android.RegistrationContext) {
@@ -31,6 +35,7 @@
 	ctx.RegisterModuleType("cc_object", ObjectFactory)
 	ctx.RegisterModuleType("cc_genrule", genRuleFactory)
 	ctx.RegisterModuleType("ndk_prebuilt_shared_stl", NdkPrebuiltSharedStlFactory)
+	ctx.RegisterModuleType("ndk_prebuilt_static_stl", NdkPrebuiltStaticStlFactory)
 	ctx.RegisterModuleType("ndk_prebuilt_object", NdkPrebuiltObjectFactory)
 	ctx.RegisterModuleType("ndk_library", NdkLibraryFactory)
 }
@@ -40,9 +45,6 @@
 
 	supportLinuxBionic := false
 	for _, os := range oses {
-		if os == android.Fuchsia {
-			ret += withFuchsiaModules()
-		}
 		if os == android.Windows {
 			ret += withWindowsModules()
 		}
@@ -363,6 +365,7 @@
 			stl: "none",
 			min_sdk_version: "16",
 			crt: true,
+			system_shared_libs: [],
 			apex_available: [
 				"//apex_available:platform",
 				"//apex_available:anyapex",
@@ -372,26 +375,42 @@
 		cc_object {
 			name: "crtbegin_so",
 			defaults: ["crt_defaults"],
+			srcs: ["crtbegin_so.c"],
+			objs: ["crtbrand"],
 		}
 
 		cc_object {
 			name: "crtbegin_dynamic",
 			defaults: ["crt_defaults"],
+			srcs: ["crtbegin.c"],
+			objs: ["crtbrand"],
 		}
 
 		cc_object {
 			name: "crtbegin_static",
 			defaults: ["crt_defaults"],
+			srcs: ["crtbegin.c"],
+			objs: ["crtbrand"],
 		}
 
 		cc_object {
 			name: "crtend_so",
 			defaults: ["crt_defaults"],
+			srcs: ["crtend_so.c"],
+			objs: ["crtbrand"],
 		}
 
 		cc_object {
 			name: "crtend_android",
 			defaults: ["crt_defaults"],
+			srcs: ["crtend.c"],
+			objs: ["crtbrand"],
+		}
+
+		cc_object {
+			name: "crtbrand",
+			defaults: ["crt_defaults"],
+			srcs: ["crtbrand.c"],
 		}
 
 		cc_library {
@@ -400,7 +419,7 @@
 
 		cc_library {
 			name: "ndk_libunwind",
-			sdk_version: "current",
+			sdk_version: "minimum",
 			stl: "none",
 			system_shared_libs: [],
 		}
@@ -425,6 +444,12 @@
 
 		ndk_prebuilt_shared_stl {
 			name: "ndk_libc++_shared",
+			export_include_dirs: ["ndk_libc++_shared"],
+		}
+
+		ndk_prebuilt_static_stl {
+			name: "ndk_libandroid_support",
+			export_include_dirs: ["ndk_libandroid_support"],
 		}
 
 		cc_library_static {
@@ -441,6 +466,15 @@
 		cc_library_static {
 			name: "note_memtag_heap_sync",
 		}
+
+		cc_library {
+			name: "libc_musl",
+			host_supported: true,
+			no_libcrt: true,
+			nocrt: true,
+			system_shared_libs: [],
+			stl: "none",
+		}
 	`
 }
 
@@ -460,19 +494,6 @@
 		`
 }
 
-func withFuchsiaModules() string {
-	return `
-		cc_library {
-			name: "libbioniccompat",
-			stl: "none",
-		}
-		cc_library {
-			name: "libcompiler_rt",
-			stl: "none",
-		}
-		`
-}
-
 func withLinuxBionic() string {
 	return `
 				cc_binary {
@@ -487,7 +508,7 @@
 				}
 
 				cc_genrule {
-					name: "host_bionic_linker_flags",
+					name: "host_bionic_linker_script",
 					host_supported: true,
 					device_supported: false,
 					target: {
@@ -498,7 +519,7 @@
 							enabled: true,
 						},
 					},
-					out: ["linker.flags"],
+					out: ["linker.script"],
 				}
 
 				cc_defaults {
@@ -575,9 +596,16 @@
 
 	// Additional files needed in tests that disallow non-existent source.
 	android.MockFS{
-		"defaults/cc/common/libc.map.txt":  nil,
-		"defaults/cc/common/libdl.map.txt": nil,
-		"defaults/cc/common/libm.map.txt":  nil,
+		"defaults/cc/common/libc.map.txt":           nil,
+		"defaults/cc/common/libdl.map.txt":          nil,
+		"defaults/cc/common/libm.map.txt":           nil,
+		"defaults/cc/common/ndk_libandroid_support": nil,
+		"defaults/cc/common/ndk_libc++_shared":      nil,
+		"defaults/cc/common/crtbegin_so.c":          nil,
+		"defaults/cc/common/crtbegin.c":             nil,
+		"defaults/cc/common/crtend_so.c":            nil,
+		"defaults/cc/common/crtend.c":               nil,
+		"defaults/cc/common/crtbrand.c":             nil,
 	}.AddToFixture(),
 
 	// Place the default cc test modules that are common to all platforms in a location that will not
@@ -612,21 +640,15 @@
 	android.FixtureOverrideTextFile(linuxBionicDefaultsPath, withLinuxBionic()),
 )
 
-// The preparer to include if running a cc related test for fuchsia.
-var PrepareForTestOnFuchsia = android.GroupFixturePreparers(
-	// Place the default cc test modules for fuschia in a location that will not conflict with default
-	// test modules defined by other packages.
-	android.FixtureAddTextFile("defaults/cc/fuschia/Android.bp", withFuchsiaModules()),
-	android.PrepareForTestSetDeviceToFuchsia,
-)
-
 // This adds some additional modules and singletons which might negatively impact the performance
 // of tests so they are not included in the PrepareForIntegrationTestWithCc.
 var PrepareForTestWithCcIncludeVndk = android.GroupFixturePreparers(
 	PrepareForIntegrationTestWithCc,
 	android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
-		vendorSnapshotImageSingleton.init(ctx)
-		recoverySnapshotImageSingleton.init(ctx)
+		snapshot.VendorSnapshotImageSingleton.Init(ctx)
+		snapshot.RecoverySnapshotImageSingleton.Init(ctx)
+		RegisterVendorSnapshotModules(ctx)
+		RegisterRecoverySnapshotModules(ctx)
 		ctx.RegisterSingletonType("vndk-snapshot", VndkSnapshotSingleton)
 	}),
 )
@@ -650,14 +672,7 @@
 		mockFS[k] = v
 	}
 
-	var config android.Config
-	if os == android.Fuchsia {
-		panic("Fuchsia not supported use test fixture instead")
-	} else {
-		config = android.TestArchConfig(buildDir, env, bp, mockFS)
-	}
-
-	return config
+	return android.TestArchConfig(buildDir, env, bp, mockFS)
 }
 
 // CreateTestContext is the legacy way of creating a TestContext for testing cc modules.
@@ -674,8 +689,10 @@
 	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
 	ctx.RegisterModuleType("vndk_prebuilt_shared", VndkPrebuiltSharedFactory)
 
-	vendorSnapshotImageSingleton.init(ctx)
-	recoverySnapshotImageSingleton.init(ctx)
+	snapshot.VendorSnapshotImageSingleton.Init(ctx)
+	snapshot.RecoverySnapshotImageSingleton.Init(ctx)
+	RegisterVendorSnapshotModules(ctx)
+	RegisterRecoverySnapshotModules(ctx)
 	ctx.RegisterSingletonType("vndk-snapshot", VndkSnapshotSingleton)
 	RegisterVndkLibraryTxtTypes(ctx)
 
@@ -685,3 +702,64 @@
 
 	return ctx
 }
+
+func checkSnapshotIncludeExclude(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string, include bool, fake bool) {
+	t.Helper()
+	mod := ctx.ModuleForTests(moduleName, variant)
+	outputFiles := mod.OutputFiles(t, "")
+	if len(outputFiles) != 1 {
+		t.Errorf("%q must have single output\n", moduleName)
+		return
+	}
+	snapshotPath := filepath.Join(subDir, snapshotFilename)
+
+	if include {
+		out := singleton.Output(snapshotPath)
+		if fake {
+			if out.Rule == nil {
+				t.Errorf("Missing rule for module %q output file %q", moduleName, outputFiles[0])
+			}
+		} else {
+			if out.Input.String() != outputFiles[0].String() {
+				t.Errorf("The input of snapshot %q must be %q, but %q", moduleName, out.Input.String(), outputFiles[0])
+			}
+		}
+	} else {
+		out := singleton.MaybeOutput(snapshotPath)
+		if out.Rule != nil {
+			t.Errorf("There must be no rule for module %q output file %q", moduleName, outputFiles[0])
+		}
+	}
+}
+
+func CheckSnapshot(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string) {
+	t.Helper()
+	checkSnapshotIncludeExclude(t, ctx, singleton, moduleName, snapshotFilename, subDir, variant, true, false)
+}
+
+func CheckSnapshotExclude(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string) {
+	t.Helper()
+	checkSnapshotIncludeExclude(t, ctx, singleton, moduleName, snapshotFilename, subDir, variant, false, false)
+}
+
+func CheckSnapshotRule(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string) {
+	t.Helper()
+	checkSnapshotIncludeExclude(t, ctx, singleton, moduleName, snapshotFilename, subDir, variant, true, true)
+}
+
+func AssertExcludeFromVendorSnapshotIs(t *testing.T, ctx *android.TestContext, name string, expected bool, variant string) {
+	t.Helper()
+	m := ctx.ModuleForTests(name, variant).Module().(LinkableInterface)
+	if m.ExcludeFromVendorSnapshot() != expected {
+		t.Errorf("expected %q ExcludeFromVendorSnapshot to be %t", m.String(), expected)
+	}
+}
+
+func GetOutputPaths(ctx *android.TestContext, variant string, moduleNames []string) (paths android.Paths) {
+	for _, moduleName := range moduleNames {
+		module := ctx.ModuleForTests(moduleName, variant).Module().(*Module)
+		output := module.outputFile.Path().RelativeToTop()
+		paths = append(paths, output)
+	}
+	return paths
+}
diff --git a/cc/tidy.go b/cc/tidy.go
index 616cf8a..fefa7f0 100644
--- a/cc/tidy.go
+++ b/cc/tidy.go
@@ -61,13 +61,6 @@
 	return []interface{}{&tidy.Properties}
 }
 
-func (tidy *tidyFeature) begin(ctx BaseModuleContext) {
-}
-
-func (tidy *tidyFeature) deps(ctx DepsContext, deps Deps) Deps {
-	return deps
-}
-
 func (tidy *tidyFeature) flags(ctx ModuleContext, flags Flags) Flags {
 	CheckBadTidyFlags(ctx, "tidy_flags", tidy.Properties.Tidy_flags)
 	CheckBadTidyChecks(ctx, "tidy_checks", tidy.Properties.Tidy_checks)
@@ -153,6 +146,8 @@
 	tidyChecks = tidyChecks + ",-bugprone-signed-char-misuse"
 	// http://b/155034972
 	tidyChecks = tidyChecks + ",-bugprone-branch-clone"
+	// http://b/193716442
+	tidyChecks = tidyChecks + ",-bugprone-implicit-widening-of-multiplication-result"
 	flags.TidyFlags = append(flags.TidyFlags, tidyChecks)
 
 	if ctx.Config().IsEnvTrue("WITH_TIDY") {
diff --git a/cc/util.go b/cc/util.go
index 1220d84..88b0aba 100644
--- a/cc/util.go
+++ b/cc/util.go
@@ -21,6 +21,7 @@
 	"strings"
 
 	"android/soong/android"
+	"android/soong/snapshot"
 )
 
 // Efficiently converts a list of include directories to a single string
@@ -90,7 +91,6 @@
 		systemIncludeFlags: strings.Join(in.SystemIncludeFlags, " "),
 
 		assemblerWithCpp: in.AssemblerWithCpp,
-		groupStaticLibs:  in.GroupStaticLibs,
 
 		proto:            in.proto,
 		protoC:           in.protoC,
@@ -126,20 +126,6 @@
 		"ln -sf " + target + " " + filepath.Join(dir, linkName)
 }
 
-func copyFileRule(ctx android.SingletonContext, path android.Path, out string) android.OutputPath {
-	outPath := android.PathForOutput(ctx, out)
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        android.Cp,
-		Input:       path,
-		Output:      outPath,
-		Description: "copy " + path.String() + " -> " + out,
-		Args: map[string]string{
-			"cpFlags": "-f -L",
-		},
-	})
-	return outPath
-}
-
 func combineNoticesRule(ctx android.SingletonContext, paths android.Paths, out string) android.OutputPath {
 	outPath := android.PathForOutput(ctx, out)
 	ctx.Build(pctx, android.BuildParams{
@@ -151,12 +137,6 @@
 	return outPath
 }
 
-func writeStringToFileRule(ctx android.SingletonContext, content, out string) android.OutputPath {
-	outPath := android.PathForOutput(ctx, out)
-	android.WriteFileRule(ctx, outPath, content)
-	return outPath
-}
-
 // Dump a map to a list file as:
 //
 // {key1} {value1}
@@ -172,5 +152,5 @@
 		txtBuilder.WriteString(" ")
 		txtBuilder.WriteString(m[k])
 	}
-	return writeStringToFileRule(ctx, txtBuilder.String(), path)
+	return snapshot.WriteStringToFileRule(ctx, txtBuilder.String(), path)
 }
diff --git a/cc/vendor_public_library_test.go b/cc/vendor_public_library_test.go
index 01959b4..769be09 100644
--- a/cc/vendor_public_library_test.go
+++ b/cc/vendor_public_library_test.go
@@ -79,7 +79,7 @@
 	// test if libsystem is linked to the stub
 	ld := ctx.ModuleForTests("libsystem", coreVariant).Rule("ld")
 	libflags := ld.Args["libFlags"]
-	stubPaths := getOutputPaths(ctx, coreVariant, []string{"libvendorpublic"})
+	stubPaths := GetOutputPaths(ctx, coreVariant, []string{"libvendorpublic"})
 	if !strings.Contains(libflags, stubPaths[0].String()) {
 		t.Errorf("libflags for libsystem must contain %#v, but was %#v", stubPaths[0], libflags)
 	}
@@ -87,7 +87,7 @@
 	// test if libsystem is linked to the stub
 	ld = ctx.ModuleForTests("libproduct", productVariant).Rule("ld")
 	libflags = ld.Args["libFlags"]
-	stubPaths = getOutputPaths(ctx, productVariant, []string{"libvendorpublic"})
+	stubPaths = GetOutputPaths(ctx, productVariant, []string{"libvendorpublic"})
 	if !strings.Contains(libflags, stubPaths[0].String()) {
 		t.Errorf("libflags for libproduct must contain %#v, but was %#v", stubPaths[0], libflags)
 	}
@@ -95,7 +95,8 @@
 	// test if libvendor is linked to the real shared lib
 	ld = ctx.ModuleForTests("libvendor", vendorVariant).Rule("ld")
 	libflags = ld.Args["libFlags"]
-	stubPaths = getOutputPaths(ctx, vendorVariant, []string{"libvendorpublic"})
+	stubPaths = GetOutputPaths(ctx, vendorVariant, []string{"libvendorpublic"})
+
 	if !strings.Contains(libflags, stubPaths[0].String()) {
 		t.Errorf("libflags for libvendor must contain %#v, but was %#v", stubPaths[0], libflags)
 	}
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index 4e59a95..ba4d79f 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -13,141 +13,46 @@
 // limitations under the License.
 package cc
 
-// This file contains singletons to capture vendor and recovery snapshot. They consist of prebuilt
-// modules under AOSP so older vendor and recovery can be built with a newer system in a single
-// source tree.
-
 import (
 	"encoding/json"
 	"path/filepath"
-	"sort"
 	"strings"
 
 	"android/soong/android"
+	"android/soong/snapshot"
 )
 
-var vendorSnapshotSingleton = snapshotSingleton{
-	"vendor",
-	"SOONG_VENDOR_SNAPSHOT_ZIP",
-	android.OptionalPath{},
-	true,
-	vendorSnapshotImageSingleton,
-	false, /* fake */
-}
+// This file defines how to capture cc modules into snapshot package.
 
-var vendorFakeSnapshotSingleton = snapshotSingleton{
-	"vendor",
-	"SOONG_VENDOR_FAKE_SNAPSHOT_ZIP",
-	android.OptionalPath{},
-	true,
-	vendorSnapshotImageSingleton,
-	true, /* fake */
-}
-
-var recoverySnapshotSingleton = snapshotSingleton{
-	"recovery",
-	"SOONG_RECOVERY_SNAPSHOT_ZIP",
-	android.OptionalPath{},
-	false,
-	recoverySnapshotImageSingleton,
-	false, /* fake */
-}
-
-func VendorSnapshotSingleton() android.Singleton {
-	return &vendorSnapshotSingleton
-}
-
-func VendorFakeSnapshotSingleton() android.Singleton {
-	return &vendorFakeSnapshotSingleton
-}
-
-func RecoverySnapshotSingleton() android.Singleton {
-	return &recoverySnapshotSingleton
-}
-
-type snapshotSingleton struct {
-	// Name, e.g., "vendor", "recovery", "ramdisk".
-	name string
-
-	// Make variable that points to the snapshot file, e.g.,
-	// "SOONG_RECOVERY_SNAPSHOT_ZIP".
-	makeVar string
-
-	// Path to the snapshot zip file.
-	snapshotZipFile android.OptionalPath
-
-	// Whether the image supports VNDK extension modules.
-	supportsVndkExt bool
-
-	// Implementation of the image interface specific to the image
-	// associated with this snapshot (e.g., specific to the vendor image,
-	// recovery image, etc.).
-	image snapshotImage
-
-	// Whether this singleton is for fake snapshot or not.
-	// Fake snapshot is a snapshot whose prebuilt binaries and headers are empty.
-	// It is much faster to generate, and can be used to inspect dependencies.
-	fake bool
-}
-
-// Determine if a dir under source tree is an SoC-owned proprietary directory based
-// on vendor snapshot configuration
-// Examples: device/, vendor/
-func isVendorProprietaryPath(dir string, deviceConfig android.DeviceConfig) bool {
-	return VendorSnapshotSingleton().(*snapshotSingleton).image.isProprietaryPath(dir, deviceConfig)
-}
-
-// Determine if a dir under source tree is an SoC-owned proprietary directory based
-// on recovery snapshot configuration
-// Examples: device/, vendor/
-func isRecoveryProprietaryPath(dir string, deviceConfig android.DeviceConfig) bool {
-	return RecoverySnapshotSingleton().(*snapshotSingleton).image.isProprietaryPath(dir, deviceConfig)
-}
-
-func isVendorProprietaryModule(ctx android.BaseModuleContext) bool {
-	// Any module in a vendor proprietary path is a vendor proprietary
-	// module.
-	if isVendorProprietaryPath(ctx.ModuleDir(), ctx.DeviceConfig()) {
+// Checks if the target image would contain VNDK
+func includeVndk(image snapshot.SnapshotImage) bool {
+	if image.ImageName() == snapshot.VendorSnapshotImageName {
 		return true
 	}
 
-	// However if the module is not in a vendor proprietary path, it may
-	// still be a vendor proprietary module. This happens for cc modules
-	// that are excluded from the vendor snapshot, and it means that the
-	// vendor has assumed control of the framework-provided module.
-	if c, ok := ctx.Module().(LinkableInterface); ok {
-		if c.ExcludeFromVendorSnapshot() {
-			return true
-		}
-	}
-
 	return false
 }
 
-func isRecoveryProprietaryModule(ctx android.BaseModuleContext) bool {
-
-	// Any module in a recovery proprietary path is a recovery proprietary
-	// module.
-	if isRecoveryProprietaryPath(ctx.ModuleDir(), ctx.DeviceConfig()) {
+// Check if the module is VNDK private
+func isPrivate(image snapshot.SnapshotImage, m LinkableInterface) bool {
+	if image.ImageName() == snapshot.VendorSnapshotImageName && m.IsVndkPrivate() {
 		return true
 	}
 
-	// However if the module is not in a recovery proprietary path, it may
-	// still be a recovery proprietary module. This happens for cc modules
-	// that are excluded from the recovery snapshot, and it means that the
-	// vendor has assumed control of the framework-provided module.
+	return false
+}
 
-	if c, ok := ctx.Module().(LinkableInterface); ok {
-		if c.ExcludeFromRecoverySnapshot() {
-			return true
-		}
+// Checks if target image supports VNDK Ext
+func supportsVndkExt(image snapshot.SnapshotImage) bool {
+	if image.ImageName() == snapshot.VendorSnapshotImageName {
+		return true
 	}
 
 	return false
 }
 
 // Determines if the module is a candidate for snapshot.
-func isSnapshotAware(cfg android.DeviceConfig, m LinkableInterface, inProprietaryPath bool, apexInfo android.ApexInfo, image snapshotImage) bool {
+func isSnapshotAware(cfg android.DeviceConfig, m LinkableInterface, inProprietaryPath bool, apexInfo android.ApexInfo, image snapshot.SnapshotImage) bool {
 	if !m.Enabled() || m.HiddenFromMake() {
 		return false
 	}
@@ -158,12 +63,12 @@
 	}
 	// skip proprietary modules, but (for the vendor snapshot only)
 	// include all VNDK (static)
-	if inProprietaryPath && (!image.includeVndk() || !m.IsVndk()) {
+	if inProprietaryPath && (!includeVndk(image) || !m.IsVndk()) {
 		return false
 	}
 	// If the module would be included based on its path, check to see if
 	// the module is marked to be excluded. If so, skip it.
-	if image.excludeFromSnapshot(m) {
+	if image.ExcludeFromSnapshot(m) {
 		return false
 	}
 	if m.Target().Os.Class != android.Device {
@@ -173,7 +78,7 @@
 		return false
 	}
 	// the module must be installed in target image
-	if !apexInfo.IsForPlatform() || m.IsSnapshotPrebuilt() || !image.inImage(m)() {
+	if !apexInfo.IsForPlatform() || m.IsSnapshotPrebuilt() || !image.InImage(m)() {
 		return false
 	}
 	// skip kernel_headers which always depend on vendor
@@ -203,13 +108,13 @@
 			}
 		}
 		if sanitizable.Static() {
-			return sanitizable.OutputFile().Valid() && !image.private(m)
+			return sanitizable.OutputFile().Valid() && !isPrivate(image, m)
 		}
-		if sanitizable.Shared() {
+		if sanitizable.Shared() || sanitizable.Rlib() {
 			if !sanitizable.OutputFile().Valid() {
 				return false
 			}
-			if image.includeVndk() {
+			if includeVndk(image) {
 				if !sanitizable.IsVndk() {
 					return true
 				}
@@ -242,10 +147,12 @@
 	SanitizeUbsanDep   bool     `json:",omitempty"`
 
 	// binary flags
-	Symlinks []string `json:",omitempty"`
+	Symlinks         []string `json:",omitempty"`
+	StaticExecutable bool     `json:",omitempty"`
 
 	// dependencies
 	SharedLibs  []string `json:",omitempty"`
+	StaticLibs  []string `json:",omitempty"`
 	RuntimeLibs []string `json:",omitempty"`
 	Required    []string `json:",omitempty"`
 
@@ -254,15 +161,9 @@
 	VintfFragments []string `json:",omitempty"`
 }
 
-func (c *snapshotSingleton) GenerateBuildActions(ctx android.SingletonContext) {
-	if !c.image.shouldGenerateSnapshot(ctx) {
-		return
-	}
-
-	var snapshotOutputs android.Paths
-
+var ccSnapshotAction snapshot.GenerateSnapshotAction = func(s snapshot.SnapshotSingleton, ctx android.SingletonContext, snapshotArchDir string) android.Paths {
 	/*
-		Vendor snapshot zipped artifacts directory structure:
+		Vendor snapshot zipped artifacts directory structure for cc modules:
 		{SNAPSHOT_ARCH}/
 			arch-{TARGET_ARCH}-{TARGET_ARCH_VARIANT}/
 				shared/
@@ -294,13 +195,7 @@
 				(header files of same directory structure with source tree)
 	*/
 
-	snapshotDir := c.name + "-snapshot"
-	if c.fake {
-		// If this is a fake snapshot singleton, place all files under fake/ subdirectory to avoid
-		// collision with real snapshot files
-		snapshotDir = filepath.Join("fake", snapshotDir)
-	}
-	snapshotArchDir := filepath.Join(snapshotDir, ctx.DeviceConfig().DeviceArch())
+	var snapshotOutputs android.Paths
 
 	includeDir := filepath.Join(snapshotArchDir, "include")
 	configsDir := filepath.Join(snapshotArchDir, "configs")
@@ -315,9 +210,9 @@
 		if fake {
 			// All prebuilt binaries and headers are installed by copyFile function. This makes a fake
 			// snapshot just touch prebuilts and headers, rather than installing real files.
-			return writeStringToFileRule(ctx, "", out)
+			return snapshot.WriteStringToFileRule(ctx, "", out)
 		} else {
-			return copyFileRule(ctx, path, out)
+			return snapshot.CopyFileRule(pctx, ctx, path, out)
 		}
 	}
 
@@ -335,7 +230,7 @@
 
 		// Common properties among snapshots.
 		prop.ModuleName = ctx.ModuleName(m)
-		if c.supportsVndkExt && m.IsVndkExt() {
+		if supportsVndkExt(s.Image) && m.IsVndkExt() {
 			// vndk exts are installed to /vendor/lib(64)?/vndk(-sp)?
 			if m.IsVndkSp() {
 				prop.RelativeInstallPath = "vndk-sp"
@@ -381,6 +276,8 @@
 			if m.Shared() {
 				prop.SharedLibs = m.SnapshotSharedLibs()
 			}
+			// static libs dependencies are required to collect the NOTICE files.
+			prop.StaticLibs = m.SnapshotStaticLibs()
 			if sanitizable, ok := m.(PlatformSanitizeable); ok {
 				if sanitizable.Static() && sanitizable.SanitizePropDefined() {
 					prop.SanitizeMinimalDep = sanitizable.MinimalRuntimeDep() || sanitizable.MinimalRuntimeNeeded()
@@ -393,6 +290,8 @@
 				libType = "static"
 			} else if m.Shared() {
 				libType = "shared"
+			} else if m.Rlib() {
+				libType = "rlib"
 			} else {
 				libType = "header"
 			}
@@ -404,7 +303,7 @@
 				libPath := m.OutputFile().Path()
 				stem = libPath.Base()
 				if sanitizable, ok := m.(PlatformSanitizeable); ok {
-					if sanitizable.Static() && sanitizable.SanitizePropDefined() && sanitizable.IsSanitizerEnabled(cfi) {
+					if (sanitizable.Static() || sanitizable.Rlib()) && sanitizable.SanitizePropDefined() && sanitizable.IsSanitizerEnabled(cfi) {
 						// both cfi and non-cfi variant for static libraries can exist.
 						// attach .cfi to distinguish between cfi and non-cfi.
 						// e.g. libbase.a -> libbase.cfi.a
@@ -424,8 +323,10 @@
 		} else if m.Binary() {
 			// binary flags
 			prop.Symlinks = m.Symlinks()
+			prop.StaticExecutable = m.StaticExecutable()
 			prop.SharedLibs = m.SnapshotSharedLibs()
-
+			// static libs dependencies are required to collect the NOTICE files.
+			prop.StaticLibs = m.SnapshotStaticLibs()
 			// install bin
 			binPath := m.OutputFile().Path()
 			snapshotBinOut := filepath.Join(snapshotArchDir, targetArch, "binary", binPath.Base())
@@ -449,7 +350,7 @@
 			ctx.Errorf("json marshal to %q failed: %#v", propOut, err)
 			return nil
 		}
-		ret = append(ret, writeStringToFileRule(ctx, string(j), propOut))
+		ret = append(ret, snapshot.WriteStringToFileRule(ctx, string(j), propOut))
 
 		return ret
 	}
@@ -461,10 +362,10 @@
 		}
 
 		moduleDir := ctx.ModuleDir(module)
-		inProprietaryPath := c.image.isProprietaryPath(moduleDir, ctx.DeviceConfig())
+		inProprietaryPath := s.Image.IsProprietaryPath(moduleDir, ctx.DeviceConfig())
 		apexInfo := ctx.ModuleProvider(module, android.ApexInfoProvider).(android.ApexInfo)
 
-		if c.image.excludeFromSnapshot(m) {
+		if s.Image.ExcludeFromSnapshot(m) {
 			if inProprietaryPath {
 				// Error: exclude_from_vendor_snapshot applies
 				// to framework-path modules only.
@@ -473,7 +374,7 @@
 			}
 		}
 
-		if !isSnapshotAware(ctx.DeviceConfig(), m, inProprietaryPath, apexInfo, c.image) {
+		if !isSnapshotAware(ctx.DeviceConfig(), m, inProprietaryPath, apexInfo, s.Image) {
 			return
 		}
 
@@ -481,8 +382,8 @@
 		// list, we will still include the module as if it was a fake module.
 		// The reason is that soong needs all the dependencies to be present, even
 		// if they are not using during the build.
-		installAsFake := c.fake
-		if c.image.excludeFromDirectedSnapshot(ctx.DeviceConfig(), m.BaseModuleName()) {
+		installAsFake := s.Fake
+		if s.Image.ExcludeFromDirectedSnapshot(ctx.DeviceConfig(), m.BaseModuleName()) {
 			installAsFake = true
 		}
 
@@ -493,60 +394,25 @@
 			headers = append(headers, m.SnapshotHeaders()...)
 		}
 
-		if len(m.NoticeFiles()) > 0 {
+		if len(m.EffectiveLicenseFiles()) > 0 {
 			noticeName := ctx.ModuleName(m) + ".txt"
 			noticeOut := filepath.Join(noticeDir, noticeName)
 			// skip already copied notice file
 			if !installedNotices[noticeOut] {
 				installedNotices[noticeOut] = true
-				snapshotOutputs = append(snapshotOutputs, combineNoticesRule(ctx, m.NoticeFiles(), noticeOut))
+				snapshotOutputs = append(snapshotOutputs, combineNoticesRule(ctx, m.EffectiveLicenseFiles(), noticeOut))
 			}
 		}
 	})
 
 	// install all headers after removing duplicates
 	for _, header := range android.FirstUniquePaths(headers) {
-		snapshotOutputs = append(snapshotOutputs, copyFile(ctx, header, filepath.Join(includeDir, header.String()), c.fake))
+		snapshotOutputs = append(snapshotOutputs, copyFile(ctx, header, filepath.Join(includeDir, header.String()), s.Fake))
 	}
 
-	// All artifacts are ready. Sort them to normalize ninja and then zip.
-	sort.Slice(snapshotOutputs, func(i, j int) bool {
-		return snapshotOutputs[i].String() < snapshotOutputs[j].String()
-	})
-
-	zipPath := android.PathForOutput(
-		ctx,
-		snapshotDir,
-		c.name+"-"+ctx.Config().DeviceName()+".zip")
-	zipRule := android.NewRuleBuilder(pctx, ctx)
-
-	// filenames in rspfile from FlagWithRspFileInputList might be single-quoted. Remove it with tr
-	snapshotOutputList := android.PathForOutput(
-		ctx,
-		snapshotDir,
-		c.name+"-"+ctx.Config().DeviceName()+"_list")
-	rspFile := snapshotOutputList.ReplaceExtension(ctx, "rsp")
-	zipRule.Command().
-		Text("tr").
-		FlagWithArg("-d ", "\\'").
-		FlagWithRspFileInputList("< ", rspFile, snapshotOutputs).
-		FlagWithOutput("> ", snapshotOutputList)
-
-	zipRule.Temporary(snapshotOutputList)
-
-	zipRule.Command().
-		BuiltTool("soong_zip").
-		FlagWithOutput("-o ", zipPath).
-		FlagWithArg("-C ", android.PathForOutput(ctx, snapshotDir).String()).
-		FlagWithInput("-l ", snapshotOutputList)
-
-	zipRule.Build(zipPath.String(), c.name+" snapshot "+zipPath.String())
-	zipRule.DeleteTemporaryFiles()
-	c.snapshotZipFile = android.OptionalPathForPath(zipPath)
+	return snapshotOutputs
 }
 
-func (c *snapshotSingleton) MakeVars(ctx android.MakeVarsContext) {
-	ctx.Strict(
-		c.makeVar,
-		c.snapshotZipFile.String())
+func init() {
+	snapshot.RegisterSnapshotAction(ccSnapshotAction)
 }
diff --git a/cc/vendor_snapshot_test.go b/cc/vendor_snapshot_test.go
index c3b5e8c..ca2f569 100644
--- a/cc/vendor_snapshot_test.go
+++ b/cc/vendor_snapshot_test.go
@@ -108,27 +108,27 @@
 		// For shared libraries, only non-VNDK vendor_available modules are captured
 		sharedVariant := fmt.Sprintf("android_vendor.29_%s_%s_shared", archType, archVariant)
 		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
-		checkSnapshot(t, ctx, snapshotSingleton, "libvendor", "libvendor.so", sharedDir, sharedVariant)
-		checkSnapshot(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.so", sharedDir, sharedVariant)
+		CheckSnapshot(t, ctx, snapshotSingleton, "libvendor", "libvendor.so", sharedDir, sharedVariant)
+		CheckSnapshot(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.so", sharedDir, sharedVariant)
 		jsonFiles = append(jsonFiles,
 			filepath.Join(sharedDir, "libvendor.so.json"),
 			filepath.Join(sharedDir, "libvendor_available.so.json"))
 
 		// LLNDK modules are not captured
-		checkSnapshotExclude(t, ctx, snapshotSingleton, "libllndk", "libllndk.so", sharedDir, sharedVariant)
+		CheckSnapshotExclude(t, ctx, snapshotSingleton, "libllndk", "libllndk.so", sharedDir, sharedVariant)
 
 		// For static libraries, all vendor:true and vendor_available modules (including VNDK) are captured.
 		// Also cfi variants are captured, except for prebuilts like toolchain_library
 		staticVariant := fmt.Sprintf("android_vendor.29_%s_%s_static", archType, archVariant)
 		staticCfiVariant := fmt.Sprintf("android_vendor.29_%s_%s_static_cfi", archType, archVariant)
 		staticDir := filepath.Join(snapshotVariantPath, archDir, "static")
-		checkSnapshot(t, ctx, snapshotSingleton, "libb", "libb.a", staticDir, staticVariant)
-		checkSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.a", staticDir, staticVariant)
-		checkSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.cfi.a", staticDir, staticCfiVariant)
-		checkSnapshot(t, ctx, snapshotSingleton, "libvendor", "libvendor.a", staticDir, staticVariant)
-		checkSnapshot(t, ctx, snapshotSingleton, "libvendor", "libvendor.cfi.a", staticDir, staticCfiVariant)
-		checkSnapshot(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.a", staticDir, staticVariant)
-		checkSnapshot(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.cfi.a", staticDir, staticCfiVariant)
+		CheckSnapshot(t, ctx, snapshotSingleton, "libb", "libb.a", staticDir, staticVariant)
+		CheckSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.a", staticDir, staticVariant)
+		CheckSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.cfi.a", staticDir, staticCfiVariant)
+		CheckSnapshot(t, ctx, snapshotSingleton, "libvendor", "libvendor.a", staticDir, staticVariant)
+		CheckSnapshot(t, ctx, snapshotSingleton, "libvendor", "libvendor.cfi.a", staticDir, staticCfiVariant)
+		CheckSnapshot(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.a", staticDir, staticVariant)
+		CheckSnapshot(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.cfi.a", staticDir, staticCfiVariant)
 		jsonFiles = append(jsonFiles,
 			filepath.Join(staticDir, "libb.a.json"),
 			filepath.Join(staticDir, "libvndk.a.json"),
@@ -142,8 +142,8 @@
 		if archType == "arm64" {
 			binaryVariant := fmt.Sprintf("android_vendor.29_%s_%s", archType, archVariant)
 			binaryDir := filepath.Join(snapshotVariantPath, archDir, "binary")
-			checkSnapshot(t, ctx, snapshotSingleton, "vendor_bin", "vendor_bin", binaryDir, binaryVariant)
-			checkSnapshot(t, ctx, snapshotSingleton, "vendor_available_bin", "vendor_available_bin", binaryDir, binaryVariant)
+			CheckSnapshot(t, ctx, snapshotSingleton, "vendor_bin", "vendor_bin", binaryDir, binaryVariant)
+			CheckSnapshot(t, ctx, snapshotSingleton, "vendor_available_bin", "vendor_available_bin", binaryDir, binaryVariant)
 			jsonFiles = append(jsonFiles,
 				filepath.Join(binaryDir, "vendor_bin.json"),
 				filepath.Join(binaryDir, "vendor_available_bin.json"))
@@ -156,7 +156,7 @@
 		// For object modules, all vendor:true and vendor_available modules are captured.
 		objectVariant := fmt.Sprintf("android_vendor.29_%s_%s", archType, archVariant)
 		objectDir := filepath.Join(snapshotVariantPath, archDir, "object")
-		checkSnapshot(t, ctx, snapshotSingleton, "obj", "obj.o", objectDir, objectVariant)
+		CheckSnapshot(t, ctx, snapshotSingleton, "obj", "obj.o", objectDir, objectVariant)
 		jsonFiles = append(jsonFiles, filepath.Join(objectDir, "obj.o.json"))
 	}
 
@@ -239,15 +239,15 @@
 		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
 
 		// Included modules
-		checkSnapshot(t, ctx, snapshotSingleton, "libvendor", "libvendor.so", sharedDir, sharedVariant)
+		CheckSnapshot(t, ctx, snapshotSingleton, "libvendor", "libvendor.so", sharedDir, sharedVariant)
 		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libvendor.so.json"))
 		// Check that snapshot captures "prefer: true" prebuilt
-		checkSnapshot(t, ctx, snapshotSingleton, "prebuilt_libfoo", "libfoo.so", sharedDir, sharedVariant)
+		CheckSnapshot(t, ctx, snapshotSingleton, "prebuilt_libfoo", "libfoo.so", sharedDir, sharedVariant)
 		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libfoo.so.json"))
 
 		// Excluded modules. Modules not included in the directed vendor snapshot
 		// are still include as fake modules.
-		checkSnapshotRule(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.so", sharedDir, sharedVariant)
+		CheckSnapshotRule(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.so", sharedDir, sharedVariant)
 		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libvendor_available.so.json"))
 	}
 
@@ -459,6 +459,19 @@
 		srcs: ["client.cpp"],
 	}
 
+	cc_library_shared {
+		name: "libvndkext",
+		vendor: true,
+		nocrt: true,
+		no_libcrt: true,
+		stl: "none",
+		system_shared_libs: [],
+		vndk: {
+			extends: "libvndk",
+			enabled: true,
+		}
+	}
+
 	cc_binary {
 		name: "bin_without_snapshot",
 		vendor: true,
@@ -839,10 +852,11 @@
 		[]string{staticVariant, "libvendor.vendor_static.31.arm64"},
 		[]string{staticVariant, "libvendor_without_snapshot"},
 	} {
-		outputPaths := getOutputPaths(ctx, input[0] /* variant */, []string{input[1]} /* module name */)
+		outputPaths := GetOutputPaths(ctx, input[0] /* variant */, []string{input[1]} /* module name */)
 		if !strings.Contains(libclientLdFlags, outputPaths[0].String()) {
 			t.Errorf("libflags for libclient must contain %#v, but was %#v", outputPaths[0], libclientLdFlags)
 		}
+
 	}
 
 	libclientAndroidMkSharedLibs := ctx.ModuleForTests("libclient", sharedVariant).Module().(*Module).Properties.AndroidMkSharedLibs
@@ -868,7 +882,7 @@
 	}
 
 	libclientCfiLdFlags := ctx.ModuleForTests("libclient_cfi", sharedCfiVariant).Rule("ld").Args["libFlags"]
-	libvendorCfiOutputPaths := getOutputPaths(ctx, staticCfiVariant, []string{"libvendor.vendor_static.31.arm64"})
+	libvendorCfiOutputPaths := GetOutputPaths(ctx, staticCfiVariant, []string{"libvendor.vendor_static.31.arm64"})
 	if !strings.Contains(libclientCfiLdFlags, libvendorCfiOutputPaths[0].String()) {
 		t.Errorf("libflags for libclientCfi must contain %#v, but was %#v", libvendorCfiOutputPaths[0], libclientCfiLdFlags)
 	}
@@ -881,7 +895,7 @@
 	}
 
 	binWithoutSnapshotLdFlags := ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Rule("ld").Args["libFlags"]
-	libVndkStaticOutputPaths := getOutputPaths(ctx, staticVariant, []string{"libvndk.vendor_static.31.arm64"})
+	libVndkStaticOutputPaths := GetOutputPaths(ctx, staticVariant, []string{"libvndk.vendor_static.31.arm64"})
 	if !strings.Contains(binWithoutSnapshotLdFlags, libVndkStaticOutputPaths[0].String()) {
 		t.Errorf("libflags for bin_without_snapshot must contain %#v, but was %#v",
 			libVndkStaticOutputPaths[0], binWithoutSnapshotLdFlags)
@@ -1006,14 +1020,6 @@
 	assertString(t, staticCfiModule.outputFile.Path().Base(), "libsnapshot.cfi.a")
 }
 
-func assertExcludeFromVendorSnapshotIs(t *testing.T, ctx *android.TestContext, name string, expected bool) {
-	t.Helper()
-	m := ctx.ModuleForTests(name, vendorVariant).Module().(*Module)
-	if m.ExcludeFromVendorSnapshot() != expected {
-		t.Errorf("expected %q ExcludeFromVendorSnapshot to be %t", m.String(), expected)
-	}
-}
-
 func assertExcludeFromRecoverySnapshotIs(t *testing.T, ctx *android.TestContext, name string, expected bool) {
 	t.Helper()
 	m := ctx.ModuleForTests(name, recoveryVariant).Module().(*Module)
@@ -1081,13 +1087,13 @@
 	android.FailIfErrored(t, errs)
 
 	// Test an include and exclude framework module.
-	assertExcludeFromVendorSnapshotIs(t, ctx, "libinclude", false)
-	assertExcludeFromVendorSnapshotIs(t, ctx, "libexclude", true)
-	assertExcludeFromVendorSnapshotIs(t, ctx, "libavailable_exclude", true)
+	AssertExcludeFromVendorSnapshotIs(t, ctx, "libinclude", false, vendorVariant)
+	AssertExcludeFromVendorSnapshotIs(t, ctx, "libexclude", true, vendorVariant)
+	AssertExcludeFromVendorSnapshotIs(t, ctx, "libavailable_exclude", true, vendorVariant)
 
 	// A vendor module is excluded, but by its path, not the
 	// exclude_from_vendor_snapshot property.
-	assertExcludeFromVendorSnapshotIs(t, ctx, "libvendor", false)
+	AssertExcludeFromVendorSnapshotIs(t, ctx, "libvendor", false, vendorVariant)
 
 	// Verify the content of the vendor snapshot.
 
@@ -1110,15 +1116,15 @@
 		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
 
 		// Included modules
-		checkSnapshot(t, ctx, snapshotSingleton, "libinclude", "libinclude.so", sharedDir, sharedVariant)
+		CheckSnapshot(t, ctx, snapshotSingleton, "libinclude", "libinclude.so", sharedDir, sharedVariant)
 		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libinclude.so.json"))
 
 		// Excluded modules
-		checkSnapshotExclude(t, ctx, snapshotSingleton, "libexclude", "libexclude.so", sharedDir, sharedVariant)
+		CheckSnapshotExclude(t, ctx, snapshotSingleton, "libexclude", "libexclude.so", sharedDir, sharedVariant)
 		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libexclude.so.json"))
-		checkSnapshotExclude(t, ctx, snapshotSingleton, "libvendor", "libvendor.so", sharedDir, sharedVariant)
+		CheckSnapshotExclude(t, ctx, snapshotSingleton, "libvendor", "libvendor.so", sharedDir, sharedVariant)
 		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libvendor.so.json"))
-		checkSnapshotExclude(t, ctx, snapshotSingleton, "libavailable_exclude", "libavailable_exclude.so", sharedDir, sharedVariant)
+		CheckSnapshotExclude(t, ctx, snapshotSingleton, "libavailable_exclude", "libavailable_exclude.so", sharedDir, sharedVariant)
 		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libavailable_exclude.so.json"))
 	}
 
@@ -1258,9 +1264,9 @@
 		// For shared libraries, only recovery_available modules are captured.
 		sharedVariant := fmt.Sprintf("android_recovery_%s_%s_shared", archType, archVariant)
 		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
-		checkSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.so", sharedDir, sharedVariant)
-		checkSnapshot(t, ctx, snapshotSingleton, "librecovery", "librecovery.so", sharedDir, sharedVariant)
-		checkSnapshot(t, ctx, snapshotSingleton, "librecovery_available", "librecovery_available.so", sharedDir, sharedVariant)
+		CheckSnapshot(t, ctx, snapshotSingleton, "libvndk", "libvndk.so", sharedDir, sharedVariant)
+		CheckSnapshot(t, ctx, snapshotSingleton, "librecovery", "librecovery.so", sharedDir, sharedVariant)
+		CheckSnapshot(t, ctx, snapshotSingleton, "librecovery_available", "librecovery_available.so", sharedDir, sharedVariant)
 		jsonFiles = append(jsonFiles,
 			filepath.Join(sharedDir, "libvndk.so.json"),
 			filepath.Join(sharedDir, "librecovery.so.json"),
@@ -1269,9 +1275,9 @@
 		// For static libraries, all recovery:true and recovery_available modules are captured.
 		staticVariant := fmt.Sprintf("android_recovery_%s_%s_static", archType, archVariant)
 		staticDir := filepath.Join(snapshotVariantPath, archDir, "static")
-		checkSnapshot(t, ctx, snapshotSingleton, "libb", "libb.a", staticDir, staticVariant)
-		checkSnapshot(t, ctx, snapshotSingleton, "librecovery", "librecovery.a", staticDir, staticVariant)
-		checkSnapshot(t, ctx, snapshotSingleton, "librecovery_available", "librecovery_available.a", staticDir, staticVariant)
+		CheckSnapshot(t, ctx, snapshotSingleton, "libb", "libb.a", staticDir, staticVariant)
+		CheckSnapshot(t, ctx, snapshotSingleton, "librecovery", "librecovery.a", staticDir, staticVariant)
+		CheckSnapshot(t, ctx, snapshotSingleton, "librecovery_available", "librecovery_available.a", staticDir, staticVariant)
 		jsonFiles = append(jsonFiles,
 			filepath.Join(staticDir, "libb.a.json"),
 			filepath.Join(staticDir, "librecovery.a.json"),
@@ -1281,8 +1287,8 @@
 		if archType == "arm64" {
 			binaryVariant := fmt.Sprintf("android_recovery_%s_%s", archType, archVariant)
 			binaryDir := filepath.Join(snapshotVariantPath, archDir, "binary")
-			checkSnapshot(t, ctx, snapshotSingleton, "recovery_bin", "recovery_bin", binaryDir, binaryVariant)
-			checkSnapshot(t, ctx, snapshotSingleton, "recovery_available_bin", "recovery_available_bin", binaryDir, binaryVariant)
+			CheckSnapshot(t, ctx, snapshotSingleton, "recovery_bin", "recovery_bin", binaryDir, binaryVariant)
+			CheckSnapshot(t, ctx, snapshotSingleton, "recovery_available_bin", "recovery_available_bin", binaryDir, binaryVariant)
 			jsonFiles = append(jsonFiles,
 				filepath.Join(binaryDir, "recovery_bin.json"),
 				filepath.Join(binaryDir, "recovery_available_bin.json"))
@@ -1295,7 +1301,7 @@
 		// For object modules, all vendor:true and vendor_available modules are captured.
 		objectVariant := fmt.Sprintf("android_recovery_%s_%s", archType, archVariant)
 		objectDir := filepath.Join(snapshotVariantPath, archDir, "object")
-		checkSnapshot(t, ctx, snapshotSingleton, "obj", "obj.o", objectDir, objectVariant)
+		CheckSnapshot(t, ctx, snapshotSingleton, "obj", "obj.o", objectDir, objectVariant)
 		jsonFiles = append(jsonFiles, filepath.Join(objectDir, "obj.o.json"))
 	}
 
@@ -1393,15 +1399,15 @@
 		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
 
 		// Included modules
-		checkSnapshot(t, ctx, snapshotSingleton, "libinclude", "libinclude.so", sharedDir, sharedVariant)
+		CheckSnapshot(t, ctx, snapshotSingleton, "libinclude", "libinclude.so", sharedDir, sharedVariant)
 		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libinclude.so.json"))
 
 		// Excluded modules
-		checkSnapshotExclude(t, ctx, snapshotSingleton, "libexclude", "libexclude.so", sharedDir, sharedVariant)
+		CheckSnapshotExclude(t, ctx, snapshotSingleton, "libexclude", "libexclude.so", sharedDir, sharedVariant)
 		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libexclude.so.json"))
-		checkSnapshotExclude(t, ctx, snapshotSingleton, "librecovery", "librecovery.so", sharedDir, sharedVariant)
+		CheckSnapshotExclude(t, ctx, snapshotSingleton, "librecovery", "librecovery.so", sharedDir, sharedVariant)
 		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "librecovery.so.json"))
-		checkSnapshotExclude(t, ctx, snapshotSingleton, "libavailable_exclude", "libavailable_exclude.so", sharedDir, sharedVariant)
+		CheckSnapshotExclude(t, ctx, snapshotSingleton, "libavailable_exclude", "libavailable_exclude.so", sharedDir, sharedVariant)
 		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libavailable_exclude.so.json"))
 	}
 
@@ -1482,15 +1488,15 @@
 		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
 
 		// Included modules
-		checkSnapshot(t, ctx, snapshotSingleton, "librecovery", "librecovery.so", sharedDir, sharedVariant)
+		CheckSnapshot(t, ctx, snapshotSingleton, "librecovery", "librecovery.so", sharedDir, sharedVariant)
 		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "librecovery.so.json"))
 		// Check that snapshot captures "prefer: true" prebuilt
-		checkSnapshot(t, ctx, snapshotSingleton, "prebuilt_libfoo", "libfoo.so", sharedDir, sharedVariant)
+		CheckSnapshot(t, ctx, snapshotSingleton, "prebuilt_libfoo", "libfoo.so", sharedDir, sharedVariant)
 		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libfoo.so.json"))
 
 		// Excluded modules. Modules not included in the directed recovery snapshot
 		// are still include as fake modules.
-		checkSnapshotRule(t, ctx, snapshotSingleton, "librecovery_available", "librecovery_available.so", sharedDir, sharedVariant)
+		CheckSnapshotRule(t, ctx, snapshotSingleton, "librecovery_available", "librecovery_available.so", sharedDir, sharedVariant)
 		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "librecovery_available.so.json"))
 	}
 
diff --git a/cc/vndk.go b/cc/vndk.go
index 6a56c34..1ae79de 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -25,6 +25,7 @@
 	"android/soong/android"
 	"android/soong/cc/config"
 	"android/soong/etc"
+	"android/soong/snapshot"
 
 	"github.com/google/blueprint"
 )
@@ -100,12 +101,6 @@
 	return []interface{}{&vndk.Properties}
 }
 
-func (vndk *vndkdep) begin(ctx BaseModuleContext) {}
-
-func (vndk *vndkdep) deps(ctx BaseModuleContext, deps Deps) Deps {
-	return deps
-}
-
 func (vndk *vndkdep) isVndk() bool {
 	return Bool(vndk.Properties.Vndk.Enabled)
 }
@@ -360,7 +355,7 @@
 	// prebuilt vndk modules should match with device
 	// TODO(b/142675459): Use enabled: to select target device in vndk_prebuilt_shared
 	// When b/142675459 is landed, remove following check
-	if p, ok := m.linker.(*vndkPrebuiltLibraryDecorator); ok && !p.matchesWithDevice(mctx.DeviceConfig()) {
+	if p, ok := m.linker.(*vndkPrebuiltLibraryDecorator); ok && !p.MatchesWithDevice(mctx.DeviceConfig()) {
 		return false
 	}
 
@@ -371,7 +366,7 @@
 			if mctx.ModuleName() == "libz" {
 				return false
 			}
-			return m.ImageVariation().Variation == android.CoreVariation && lib.shared() && m.IsVndkSp()
+			return m.ImageVariation().Variation == android.CoreVariation && lib.shared() && m.IsVndkSp() && !m.IsVndkExt()
 		}
 
 		useCoreVariant := m.VndkVersion() == mctx.DeviceConfig().PlatformVndkVersion() &&
@@ -704,7 +699,7 @@
 
 		libPath := m.outputFile.Path()
 		snapshotLibOut := filepath.Join(snapshotArchDir, targetArch, "shared", vndkType, libPath.Base())
-		ret = append(ret, copyFileRule(ctx, libPath, snapshotLibOut))
+		ret = append(ret, snapshot.CopyFileRule(pctx, ctx, libPath, snapshotLibOut))
 
 		if ctx.Config().VndkSnapshotBuildArtifacts() {
 			prop := struct {
@@ -726,7 +721,7 @@
 				ctx.Errorf("json marshal to %q failed: %#v", propOut, err)
 				return nil, false
 			}
-			ret = append(ret, writeStringToFileRule(ctx, string(j), propOut))
+			ret = append(ret, snapshot.WriteStringToFileRule(ctx, string(j), propOut))
 		}
 		return ret, true
 	}
@@ -784,8 +779,8 @@
 
 	// install all headers after removing duplicates
 	for _, header := range android.FirstUniquePaths(headers) {
-		snapshotOutputs = append(snapshotOutputs, copyFileRule(
-			ctx, header, filepath.Join(includeDir, header.String())))
+		snapshotOutputs = append(snapshotOutputs, snapshot.CopyFileRule(
+			pctx, ctx, header, filepath.Join(includeDir, header.String())))
 	}
 
 	// install *.libraries.txt except vndkcorevariant.libraries.txt
@@ -794,8 +789,8 @@
 		if !ok || !m.Enabled() || m.Name() == vndkUsingCoreVariantLibrariesTxt {
 			return
 		}
-		snapshotOutputs = append(snapshotOutputs, copyFileRule(
-			ctx, m.OutputFile(), filepath.Join(configsDir, m.Name())))
+		snapshotOutputs = append(snapshotOutputs, snapshot.CopyFileRule(
+			pctx, ctx, m.OutputFile(), filepath.Join(configsDir, m.Name())))
 	})
 
 	/*
diff --git a/cc/vndk_prebuilt.go b/cc/vndk_prebuilt.go
index fc4412a..da34f36 100644
--- a/cc/vndk_prebuilt.go
+++ b/cc/vndk_prebuilt.go
@@ -82,7 +82,7 @@
 }
 
 func (p *vndkPrebuiltLibraryDecorator) NameSuffix() string {
-	suffix := p.version()
+	suffix := p.Version()
 	if p.arch() != "" {
 		suffix += "." + p.arch()
 	}
@@ -92,7 +92,7 @@
 	return vndkSuffix + suffix
 }
 
-func (p *vndkPrebuiltLibraryDecorator) version() string {
+func (p *vndkPrebuiltLibraryDecorator) Version() string {
 	return String(p.properties.Version)
 }
 
@@ -107,7 +107,7 @@
 	return "64"
 }
 
-func (p *vndkPrebuiltLibraryDecorator) snapshotAndroidMkSuffix() string {
+func (p *vndkPrebuiltLibraryDecorator) SnapshotAndroidMkSuffix() string {
 	return ".vendor"
 }
 
@@ -133,7 +133,7 @@
 func (p *vndkPrebuiltLibraryDecorator) link(ctx ModuleContext,
 	flags Flags, deps PathDeps, objs Objects) android.Path {
 
-	if !p.matchesWithDevice(ctx.DeviceConfig()) {
+	if !p.MatchesWithDevice(ctx.DeviceConfig()) {
 		ctx.Module().HideFromMake()
 		return nil
 	}
@@ -163,14 +163,13 @@
 		p.androidMkSuffix = p.NameSuffix()
 
 		vndkVersion := ctx.DeviceConfig().VndkVersion()
-		if vndkVersion == p.version() {
+		if vndkVersion == p.Version() {
 			p.androidMkSuffix = ""
 		}
 
 		ctx.SetProvider(SharedLibraryInfoProvider, SharedLibraryInfo{
-			SharedLibrary:           in,
-			UnstrippedSharedLibrary: p.unstrippedOutputFile,
-			Target:                  ctx.Target(),
+			SharedLibrary: in,
+			Target:        ctx.Target(),
 
 			TableOfContents: p.tocFile,
 		})
@@ -184,7 +183,7 @@
 	return nil
 }
 
-func (p *vndkPrebuiltLibraryDecorator) matchesWithDevice(config android.DeviceConfig) bool {
+func (p *vndkPrebuiltLibraryDecorator) MatchesWithDevice(config android.DeviceConfig) bool {
 	arches := config.Arches()
 	if len(arches) == 0 || arches[0].ArchType.String() != p.arch() {
 		return false
@@ -202,7 +201,7 @@
 	return false
 }
 
-func (p *vndkPrebuiltLibraryDecorator) isSnapshotPrebuilt() bool {
+func (p *vndkPrebuiltLibraryDecorator) IsSnapshotPrebuilt() bool {
 	return true
 }
 
diff --git a/cmd/extract_apks/Android.bp b/cmd/extract_apks/Android.bp
index 8a4ed63..21064c0 100644
--- a/cmd/extract_apks/Android.bp
+++ b/cmd/extract_apks/Android.bp
@@ -8,6 +8,7 @@
     deps: [
         "android-archive-zip",
         "golang-protobuf-proto",
+        "golang-protobuf-encoding-prototext",
         "soong-cmd-extract_apks-proto",
     ],
     testSrcs: ["main_test.go"],
@@ -16,7 +17,10 @@
 bootstrap_go_package {
     name: "soong-cmd-extract_apks-proto",
     pkgPath: "android/soong/cmd/extract_apks/bundle_proto",
-    deps: ["golang-protobuf-proto"],
+    deps: [
+        "golang-protobuf-reflect-protoreflect",
+        "golang-protobuf-runtime-protoimpl",
+    ],
     srcs: [
         "bundle_proto/commands.pb.go",
         "bundle_proto/config.pb.go",
diff --git a/cmd/extract_apks/bundle_proto/commands.pb.go b/cmd/extract_apks/bundle_proto/commands.pb.go
index bbf3314..2cf8e6d 100644
--- a/cmd/extract_apks/bundle_proto/commands.pb.go
+++ b/cmd/extract_apks/bundle_proto/commands.pb.go
@@ -1,24 +1,29 @@
+// Messages describing APK Set's table of contents (toc.pb entry).
+// Please be advised that the ultimate source is at
+// https://github.com/google/bundletool/tree/master/src/main/proto
+// so you have been warned.
+
 // Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.26.0
+// 	protoc        v3.9.1
 // source: commands.proto
 
-package android_bundle_proto
+package bundle_proto
 
 import (
-	fmt "fmt"
-	proto "github.com/golang/protobuf/proto"
-	math "math"
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
 )
 
-// Reference imports to suppress errors if they are not otherwise used.
-var _ = proto.Marshal
-var _ = fmt.Errorf
-var _ = math.Inf
-
-// This is a compile-time assertion to ensure that this generated file
-// is compatible with the proto package it is being compiled against.
-// A compilation error at this line likely means your copy of the
-// proto package needs to be updated.
-const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
 
 type DeliveryType int32
 
@@ -29,26 +34,47 @@
 	DeliveryType_FAST_FOLLOW           DeliveryType = 3
 )
 
-var DeliveryType_name = map[int32]string{
-	0: "UNKNOWN_DELIVERY_TYPE",
-	1: "INSTALL_TIME",
-	2: "ON_DEMAND",
-	3: "FAST_FOLLOW",
-}
+// Enum value maps for DeliveryType.
+var (
+	DeliveryType_name = map[int32]string{
+		0: "UNKNOWN_DELIVERY_TYPE",
+		1: "INSTALL_TIME",
+		2: "ON_DEMAND",
+		3: "FAST_FOLLOW",
+	}
+	DeliveryType_value = map[string]int32{
+		"UNKNOWN_DELIVERY_TYPE": 0,
+		"INSTALL_TIME":          1,
+		"ON_DEMAND":             2,
+		"FAST_FOLLOW":           3,
+	}
+)
 
-var DeliveryType_value = map[string]int32{
-	"UNKNOWN_DELIVERY_TYPE": 0,
-	"INSTALL_TIME":          1,
-	"ON_DEMAND":             2,
-	"FAST_FOLLOW":           3,
+func (x DeliveryType) Enum() *DeliveryType {
+	p := new(DeliveryType)
+	*p = x
+	return p
 }
 
 func (x DeliveryType) String() string {
-	return proto.EnumName(DeliveryType_name, int32(x))
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
 }
 
+func (DeliveryType) Descriptor() protoreflect.EnumDescriptor {
+	return file_commands_proto_enumTypes[0].Descriptor()
+}
+
+func (DeliveryType) Type() protoreflect.EnumType {
+	return &file_commands_proto_enumTypes[0]
+}
+
+func (x DeliveryType) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use DeliveryType.Descriptor instead.
 func (DeliveryType) EnumDescriptor() ([]byte, []int) {
-	return fileDescriptor_0dff099eb2e3dfdb, []int{0}
+	return file_commands_proto_rawDescGZIP(), []int{0}
 }
 
 type SystemApkMetadata_SystemApkType int32
@@ -64,30 +90,55 @@
 	SystemApkMetadata_SYSTEM_COMPRESSED SystemApkMetadata_SystemApkType = 3
 )
 
-var SystemApkMetadata_SystemApkType_name = map[int32]string{
-	0: "UNSPECIFIED_VALUE",
-	1: "SYSTEM",
-	2: "SYSTEM_STUB",
-	3: "SYSTEM_COMPRESSED",
-}
+// Enum value maps for SystemApkMetadata_SystemApkType.
+var (
+	SystemApkMetadata_SystemApkType_name = map[int32]string{
+		0: "UNSPECIFIED_VALUE",
+		1: "SYSTEM",
+		2: "SYSTEM_STUB",
+		3: "SYSTEM_COMPRESSED",
+	}
+	SystemApkMetadata_SystemApkType_value = map[string]int32{
+		"UNSPECIFIED_VALUE": 0,
+		"SYSTEM":            1,
+		"SYSTEM_STUB":       2,
+		"SYSTEM_COMPRESSED": 3,
+	}
+)
 
-var SystemApkMetadata_SystemApkType_value = map[string]int32{
-	"UNSPECIFIED_VALUE": 0,
-	"SYSTEM":            1,
-	"SYSTEM_STUB":       2,
-	"SYSTEM_COMPRESSED": 3,
+func (x SystemApkMetadata_SystemApkType) Enum() *SystemApkMetadata_SystemApkType {
+	p := new(SystemApkMetadata_SystemApkType)
+	*p = x
+	return p
 }
 
 func (x SystemApkMetadata_SystemApkType) String() string {
-	return proto.EnumName(SystemApkMetadata_SystemApkType_name, int32(x))
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
 }
 
+func (SystemApkMetadata_SystemApkType) Descriptor() protoreflect.EnumDescriptor {
+	return file_commands_proto_enumTypes[1].Descriptor()
+}
+
+func (SystemApkMetadata_SystemApkType) Type() protoreflect.EnumType {
+	return &file_commands_proto_enumTypes[1]
+}
+
+func (x SystemApkMetadata_SystemApkType) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use SystemApkMetadata_SystemApkType.Descriptor instead.
 func (SystemApkMetadata_SystemApkType) EnumDescriptor() ([]byte, []int) {
-	return fileDescriptor_0dff099eb2e3dfdb, []int{10, 0}
+	return file_commands_proto_rawDescGZIP(), []int{10, 0}
 }
 
 // Describes the output of the "build-apks" command.
 type BuildApksResult struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	// The package name of this app.
 	PackageName string `protobuf:"bytes,4,opt,name=package_name,json=packageName,proto3" json:"package_name,omitempty"`
 	// List of the created variants.
@@ -97,68 +148,72 @@
 	// List of the created asset slices.
 	AssetSliceSet []*AssetSliceSet `protobuf:"bytes,3,rep,name=asset_slice_set,json=assetSliceSet,proto3" json:"asset_slice_set,omitempty"`
 	// Information about local testing mode.
-	LocalTestingInfo     *LocalTestingInfo `protobuf:"bytes,5,opt,name=local_testing_info,json=localTestingInfo,proto3" json:"local_testing_info,omitempty"`
-	XXX_NoUnkeyedLiteral struct{}          `json:"-"`
-	XXX_unrecognized     []byte            `json:"-"`
-	XXX_sizecache        int32             `json:"-"`
+	LocalTestingInfo *LocalTestingInfo `protobuf:"bytes,5,opt,name=local_testing_info,json=localTestingInfo,proto3" json:"local_testing_info,omitempty"`
 }
 
-func (m *BuildApksResult) Reset()         { *m = BuildApksResult{} }
-func (m *BuildApksResult) String() string { return proto.CompactTextString(m) }
-func (*BuildApksResult) ProtoMessage()    {}
+func (x *BuildApksResult) Reset() {
+	*x = BuildApksResult{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_commands_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *BuildApksResult) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*BuildApksResult) ProtoMessage() {}
+
+func (x *BuildApksResult) ProtoReflect() protoreflect.Message {
+	mi := &file_commands_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use BuildApksResult.ProtoReflect.Descriptor instead.
 func (*BuildApksResult) Descriptor() ([]byte, []int) {
-	return fileDescriptor_0dff099eb2e3dfdb, []int{0}
+	return file_commands_proto_rawDescGZIP(), []int{0}
 }
 
-func (m *BuildApksResult) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_BuildApksResult.Unmarshal(m, b)
-}
-func (m *BuildApksResult) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_BuildApksResult.Marshal(b, m, deterministic)
-}
-func (m *BuildApksResult) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_BuildApksResult.Merge(m, src)
-}
-func (m *BuildApksResult) XXX_Size() int {
-	return xxx_messageInfo_BuildApksResult.Size(m)
-}
-func (m *BuildApksResult) XXX_DiscardUnknown() {
-	xxx_messageInfo_BuildApksResult.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_BuildApksResult proto.InternalMessageInfo
-
-func (m *BuildApksResult) GetPackageName() string {
-	if m != nil {
-		return m.PackageName
+func (x *BuildApksResult) GetPackageName() string {
+	if x != nil {
+		return x.PackageName
 	}
 	return ""
 }
 
-func (m *BuildApksResult) GetVariant() []*Variant {
-	if m != nil {
-		return m.Variant
+func (x *BuildApksResult) GetVariant() []*Variant {
+	if x != nil {
+		return x.Variant
 	}
 	return nil
 }
 
-func (m *BuildApksResult) GetBundletool() *Bundletool {
-	if m != nil {
-		return m.Bundletool
+func (x *BuildApksResult) GetBundletool() *Bundletool {
+	if x != nil {
+		return x.Bundletool
 	}
 	return nil
 }
 
-func (m *BuildApksResult) GetAssetSliceSet() []*AssetSliceSet {
-	if m != nil {
-		return m.AssetSliceSet
+func (x *BuildApksResult) GetAssetSliceSet() []*AssetSliceSet {
+	if x != nil {
+		return x.AssetSliceSet
 	}
 	return nil
 }
 
-func (m *BuildApksResult) GetLocalTestingInfo() *LocalTestingInfo {
-	if m != nil {
-		return m.LocalTestingInfo
+func (x *BuildApksResult) GetLocalTestingInfo() *LocalTestingInfo {
+	if x != nil {
+		return x.LocalTestingInfo
 	}
 	return nil
 }
@@ -166,6 +221,10 @@
 // Variant is a group of APKs that covers a part of the device configuration
 // space. APKs from multiple variants are never combined on one device.
 type Variant struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	// Variant-level targeting.
 	// This targeting is fairly high-level and each APK has its own targeting as
 	// well.
@@ -176,54 +235,58 @@
 	// A device will receive APKs from the first variant that matches the device
 	// configuration, with higher variant numbers having priority over lower
 	// variant numbers.
-	VariantNumber        uint32   `protobuf:"varint,3,opt,name=variant_number,json=variantNumber,proto3" json:"variant_number,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
+	VariantNumber uint32 `protobuf:"varint,3,opt,name=variant_number,json=variantNumber,proto3" json:"variant_number,omitempty"`
 }
 
-func (m *Variant) Reset()         { *m = Variant{} }
-func (m *Variant) String() string { return proto.CompactTextString(m) }
-func (*Variant) ProtoMessage()    {}
+func (x *Variant) Reset() {
+	*x = Variant{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_commands_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Variant) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Variant) ProtoMessage() {}
+
+func (x *Variant) ProtoReflect() protoreflect.Message {
+	mi := &file_commands_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Variant.ProtoReflect.Descriptor instead.
 func (*Variant) Descriptor() ([]byte, []int) {
-	return fileDescriptor_0dff099eb2e3dfdb, []int{1}
+	return file_commands_proto_rawDescGZIP(), []int{1}
 }
 
-func (m *Variant) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_Variant.Unmarshal(m, b)
-}
-func (m *Variant) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_Variant.Marshal(b, m, deterministic)
-}
-func (m *Variant) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_Variant.Merge(m, src)
-}
-func (m *Variant) XXX_Size() int {
-	return xxx_messageInfo_Variant.Size(m)
-}
-func (m *Variant) XXX_DiscardUnknown() {
-	xxx_messageInfo_Variant.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_Variant proto.InternalMessageInfo
-
-func (m *Variant) GetTargeting() *VariantTargeting {
-	if m != nil {
-		return m.Targeting
+func (x *Variant) GetTargeting() *VariantTargeting {
+	if x != nil {
+		return x.Targeting
 	}
 	return nil
 }
 
-func (m *Variant) GetApkSet() []*ApkSet {
-	if m != nil {
-		return m.ApkSet
+func (x *Variant) GetApkSet() []*ApkSet {
+	if x != nil {
+		return x.ApkSet
 	}
 	return nil
 }
 
-func (m *Variant) GetVariantNumber() uint32 {
-	if m != nil {
-		return m.VariantNumber
+func (x *Variant) GetVariantNumber() uint32 {
+	if x != nil {
+		return x.VariantNumber
 	}
 	return 0
 }
@@ -231,54 +294,66 @@
 // Represents a module.
 // For pre-L devices multiple modules (possibly all) may be merged into one.
 type ApkSet struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	ModuleMetadata *ModuleMetadata `protobuf:"bytes,1,opt,name=module_metadata,json=moduleMetadata,proto3" json:"module_metadata,omitempty"`
 	// APKs.
-	ApkDescription       []*ApkDescription `protobuf:"bytes,2,rep,name=apk_description,json=apkDescription,proto3" json:"apk_description,omitempty"`
-	XXX_NoUnkeyedLiteral struct{}          `json:"-"`
-	XXX_unrecognized     []byte            `json:"-"`
-	XXX_sizecache        int32             `json:"-"`
+	ApkDescription []*ApkDescription `protobuf:"bytes,2,rep,name=apk_description,json=apkDescription,proto3" json:"apk_description,omitempty"`
 }
 
-func (m *ApkSet) Reset()         { *m = ApkSet{} }
-func (m *ApkSet) String() string { return proto.CompactTextString(m) }
-func (*ApkSet) ProtoMessage()    {}
+func (x *ApkSet) Reset() {
+	*x = ApkSet{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_commands_proto_msgTypes[2]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ApkSet) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ApkSet) ProtoMessage() {}
+
+func (x *ApkSet) ProtoReflect() protoreflect.Message {
+	mi := &file_commands_proto_msgTypes[2]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ApkSet.ProtoReflect.Descriptor instead.
 func (*ApkSet) Descriptor() ([]byte, []int) {
-	return fileDescriptor_0dff099eb2e3dfdb, []int{2}
+	return file_commands_proto_rawDescGZIP(), []int{2}
 }
 
-func (m *ApkSet) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_ApkSet.Unmarshal(m, b)
-}
-func (m *ApkSet) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_ApkSet.Marshal(b, m, deterministic)
-}
-func (m *ApkSet) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_ApkSet.Merge(m, src)
-}
-func (m *ApkSet) XXX_Size() int {
-	return xxx_messageInfo_ApkSet.Size(m)
-}
-func (m *ApkSet) XXX_DiscardUnknown() {
-	xxx_messageInfo_ApkSet.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_ApkSet proto.InternalMessageInfo
-
-func (m *ApkSet) GetModuleMetadata() *ModuleMetadata {
-	if m != nil {
-		return m.ModuleMetadata
+func (x *ApkSet) GetModuleMetadata() *ModuleMetadata {
+	if x != nil {
+		return x.ModuleMetadata
 	}
 	return nil
 }
 
-func (m *ApkSet) GetApkDescription() []*ApkDescription {
-	if m != nil {
-		return m.ApkDescription
+func (x *ApkSet) GetApkDescription() []*ApkDescription {
+	if x != nil {
+		return x.ApkDescription
 	}
 	return nil
 }
 
 type ModuleMetadata struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	// Module name.
 	Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
 	// Indicates the delivery type (e.g. on-demand) of the module.
@@ -292,131 +367,149 @@
 	// Relevant only for Split APKs.
 	Targeting *ModuleTargeting `protobuf:"bytes,5,opt,name=targeting,proto3" json:"targeting,omitempty"`
 	// Deprecated. Please use delivery_type.
-	OnDemandDeprecated   bool     `protobuf:"varint,2,opt,name=on_demand_deprecated,json=onDemandDeprecated,proto3" json:"on_demand_deprecated,omitempty"` // Deprecated: Do not use.
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
+	//
+	// Deprecated: Do not use.
+	OnDemandDeprecated bool `protobuf:"varint,2,opt,name=on_demand_deprecated,json=onDemandDeprecated,proto3" json:"on_demand_deprecated,omitempty"`
 }
 
-func (m *ModuleMetadata) Reset()         { *m = ModuleMetadata{} }
-func (m *ModuleMetadata) String() string { return proto.CompactTextString(m) }
-func (*ModuleMetadata) ProtoMessage()    {}
+func (x *ModuleMetadata) Reset() {
+	*x = ModuleMetadata{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_commands_proto_msgTypes[3]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ModuleMetadata) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ModuleMetadata) ProtoMessage() {}
+
+func (x *ModuleMetadata) ProtoReflect() protoreflect.Message {
+	mi := &file_commands_proto_msgTypes[3]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ModuleMetadata.ProtoReflect.Descriptor instead.
 func (*ModuleMetadata) Descriptor() ([]byte, []int) {
-	return fileDescriptor_0dff099eb2e3dfdb, []int{3}
+	return file_commands_proto_rawDescGZIP(), []int{3}
 }
 
-func (m *ModuleMetadata) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_ModuleMetadata.Unmarshal(m, b)
-}
-func (m *ModuleMetadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_ModuleMetadata.Marshal(b, m, deterministic)
-}
-func (m *ModuleMetadata) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_ModuleMetadata.Merge(m, src)
-}
-func (m *ModuleMetadata) XXX_Size() int {
-	return xxx_messageInfo_ModuleMetadata.Size(m)
-}
-func (m *ModuleMetadata) XXX_DiscardUnknown() {
-	xxx_messageInfo_ModuleMetadata.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_ModuleMetadata proto.InternalMessageInfo
-
-func (m *ModuleMetadata) GetName() string {
-	if m != nil {
-		return m.Name
+func (x *ModuleMetadata) GetName() string {
+	if x != nil {
+		return x.Name
 	}
 	return ""
 }
 
-func (m *ModuleMetadata) GetDeliveryType() DeliveryType {
-	if m != nil {
-		return m.DeliveryType
+func (x *ModuleMetadata) GetDeliveryType() DeliveryType {
+	if x != nil {
+		return x.DeliveryType
 	}
 	return DeliveryType_UNKNOWN_DELIVERY_TYPE
 }
 
-func (m *ModuleMetadata) GetIsInstant() bool {
-	if m != nil {
-		return m.IsInstant
+func (x *ModuleMetadata) GetIsInstant() bool {
+	if x != nil {
+		return x.IsInstant
 	}
 	return false
 }
 
-func (m *ModuleMetadata) GetDependencies() []string {
-	if m != nil {
-		return m.Dependencies
+func (x *ModuleMetadata) GetDependencies() []string {
+	if x != nil {
+		return x.Dependencies
 	}
 	return nil
 }
 
-func (m *ModuleMetadata) GetTargeting() *ModuleTargeting {
-	if m != nil {
-		return m.Targeting
+func (x *ModuleMetadata) GetTargeting() *ModuleTargeting {
+	if x != nil {
+		return x.Targeting
 	}
 	return nil
 }
 
 // Deprecated: Do not use.
-func (m *ModuleMetadata) GetOnDemandDeprecated() bool {
-	if m != nil {
-		return m.OnDemandDeprecated
+func (x *ModuleMetadata) GetOnDemandDeprecated() bool {
+	if x != nil {
+		return x.OnDemandDeprecated
 	}
 	return false
 }
 
 // Set of asset slices belonging to a single asset module.
 type AssetSliceSet struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	// Module level metadata.
 	AssetModuleMetadata *AssetModuleMetadata `protobuf:"bytes,1,opt,name=asset_module_metadata,json=assetModuleMetadata,proto3" json:"asset_module_metadata,omitempty"`
 	// Asset slices.
-	ApkDescription       []*ApkDescription `protobuf:"bytes,2,rep,name=apk_description,json=apkDescription,proto3" json:"apk_description,omitempty"`
-	XXX_NoUnkeyedLiteral struct{}          `json:"-"`
-	XXX_unrecognized     []byte            `json:"-"`
-	XXX_sizecache        int32             `json:"-"`
+	ApkDescription []*ApkDescription `protobuf:"bytes,2,rep,name=apk_description,json=apkDescription,proto3" json:"apk_description,omitempty"`
 }
 
-func (m *AssetSliceSet) Reset()         { *m = AssetSliceSet{} }
-func (m *AssetSliceSet) String() string { return proto.CompactTextString(m) }
-func (*AssetSliceSet) ProtoMessage()    {}
+func (x *AssetSliceSet) Reset() {
+	*x = AssetSliceSet{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_commands_proto_msgTypes[4]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *AssetSliceSet) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*AssetSliceSet) ProtoMessage() {}
+
+func (x *AssetSliceSet) ProtoReflect() protoreflect.Message {
+	mi := &file_commands_proto_msgTypes[4]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use AssetSliceSet.ProtoReflect.Descriptor instead.
 func (*AssetSliceSet) Descriptor() ([]byte, []int) {
-	return fileDescriptor_0dff099eb2e3dfdb, []int{4}
+	return file_commands_proto_rawDescGZIP(), []int{4}
 }
 
-func (m *AssetSliceSet) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_AssetSliceSet.Unmarshal(m, b)
-}
-func (m *AssetSliceSet) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_AssetSliceSet.Marshal(b, m, deterministic)
-}
-func (m *AssetSliceSet) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_AssetSliceSet.Merge(m, src)
-}
-func (m *AssetSliceSet) XXX_Size() int {
-	return xxx_messageInfo_AssetSliceSet.Size(m)
-}
-func (m *AssetSliceSet) XXX_DiscardUnknown() {
-	xxx_messageInfo_AssetSliceSet.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_AssetSliceSet proto.InternalMessageInfo
-
-func (m *AssetSliceSet) GetAssetModuleMetadata() *AssetModuleMetadata {
-	if m != nil {
-		return m.AssetModuleMetadata
+func (x *AssetSliceSet) GetAssetModuleMetadata() *AssetModuleMetadata {
+	if x != nil {
+		return x.AssetModuleMetadata
 	}
 	return nil
 }
 
-func (m *AssetSliceSet) GetApkDescription() []*ApkDescription {
-	if m != nil {
-		return m.ApkDescription
+func (x *AssetSliceSet) GetApkDescription() []*ApkDescription {
+	if x != nil {
+		return x.ApkDescription
 	}
 	return nil
 }
 
 type AssetModuleMetadata struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	// Module name.
 	Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
 	// Indicates the delivery type for persistent install.
@@ -424,133 +517,153 @@
 	// Metadata for instant installs.
 	InstantMetadata *InstantMetadata `protobuf:"bytes,3,opt,name=instant_metadata,json=instantMetadata,proto3" json:"instant_metadata,omitempty"`
 	// Deprecated. Use delivery_type.
-	OnDemandDeprecated   bool     `protobuf:"varint,2,opt,name=on_demand_deprecated,json=onDemandDeprecated,proto3" json:"on_demand_deprecated,omitempty"` // Deprecated: Do not use.
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
+	//
+	// Deprecated: Do not use.
+	OnDemandDeprecated bool `protobuf:"varint,2,opt,name=on_demand_deprecated,json=onDemandDeprecated,proto3" json:"on_demand_deprecated,omitempty"`
 }
 
-func (m *AssetModuleMetadata) Reset()         { *m = AssetModuleMetadata{} }
-func (m *AssetModuleMetadata) String() string { return proto.CompactTextString(m) }
-func (*AssetModuleMetadata) ProtoMessage()    {}
+func (x *AssetModuleMetadata) Reset() {
+	*x = AssetModuleMetadata{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_commands_proto_msgTypes[5]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *AssetModuleMetadata) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*AssetModuleMetadata) ProtoMessage() {}
+
+func (x *AssetModuleMetadata) ProtoReflect() protoreflect.Message {
+	mi := &file_commands_proto_msgTypes[5]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use AssetModuleMetadata.ProtoReflect.Descriptor instead.
 func (*AssetModuleMetadata) Descriptor() ([]byte, []int) {
-	return fileDescriptor_0dff099eb2e3dfdb, []int{5}
+	return file_commands_proto_rawDescGZIP(), []int{5}
 }
 
-func (m *AssetModuleMetadata) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_AssetModuleMetadata.Unmarshal(m, b)
-}
-func (m *AssetModuleMetadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_AssetModuleMetadata.Marshal(b, m, deterministic)
-}
-func (m *AssetModuleMetadata) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_AssetModuleMetadata.Merge(m, src)
-}
-func (m *AssetModuleMetadata) XXX_Size() int {
-	return xxx_messageInfo_AssetModuleMetadata.Size(m)
-}
-func (m *AssetModuleMetadata) XXX_DiscardUnknown() {
-	xxx_messageInfo_AssetModuleMetadata.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_AssetModuleMetadata proto.InternalMessageInfo
-
-func (m *AssetModuleMetadata) GetName() string {
-	if m != nil {
-		return m.Name
+func (x *AssetModuleMetadata) GetName() string {
+	if x != nil {
+		return x.Name
 	}
 	return ""
 }
 
-func (m *AssetModuleMetadata) GetDeliveryType() DeliveryType {
-	if m != nil {
-		return m.DeliveryType
+func (x *AssetModuleMetadata) GetDeliveryType() DeliveryType {
+	if x != nil {
+		return x.DeliveryType
 	}
 	return DeliveryType_UNKNOWN_DELIVERY_TYPE
 }
 
-func (m *AssetModuleMetadata) GetInstantMetadata() *InstantMetadata {
-	if m != nil {
-		return m.InstantMetadata
+func (x *AssetModuleMetadata) GetInstantMetadata() *InstantMetadata {
+	if x != nil {
+		return x.InstantMetadata
 	}
 	return nil
 }
 
 // Deprecated: Do not use.
-func (m *AssetModuleMetadata) GetOnDemandDeprecated() bool {
-	if m != nil {
-		return m.OnDemandDeprecated
+func (x *AssetModuleMetadata) GetOnDemandDeprecated() bool {
+	if x != nil {
+		return x.OnDemandDeprecated
 	}
 	return false
 }
 
 type InstantMetadata struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	// Indicates whether this module is marked "instant".
 	IsInstant bool `protobuf:"varint,1,opt,name=is_instant,json=isInstant,proto3" json:"is_instant,omitempty"`
 	// Indicates the delivery type for instant install.
 	DeliveryType DeliveryType `protobuf:"varint,3,opt,name=delivery_type,json=deliveryType,proto3,enum=android.bundle.DeliveryType" json:"delivery_type,omitempty"`
 	// Deprecated. Use delivery_type.
-	OnDemandDeprecated   bool     `protobuf:"varint,2,opt,name=on_demand_deprecated,json=onDemandDeprecated,proto3" json:"on_demand_deprecated,omitempty"` // Deprecated: Do not use.
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
+	//
+	// Deprecated: Do not use.
+	OnDemandDeprecated bool `protobuf:"varint,2,opt,name=on_demand_deprecated,json=onDemandDeprecated,proto3" json:"on_demand_deprecated,omitempty"`
 }
 
-func (m *InstantMetadata) Reset()         { *m = InstantMetadata{} }
-func (m *InstantMetadata) String() string { return proto.CompactTextString(m) }
-func (*InstantMetadata) ProtoMessage()    {}
+func (x *InstantMetadata) Reset() {
+	*x = InstantMetadata{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_commands_proto_msgTypes[6]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *InstantMetadata) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*InstantMetadata) ProtoMessage() {}
+
+func (x *InstantMetadata) ProtoReflect() protoreflect.Message {
+	mi := &file_commands_proto_msgTypes[6]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use InstantMetadata.ProtoReflect.Descriptor instead.
 func (*InstantMetadata) Descriptor() ([]byte, []int) {
-	return fileDescriptor_0dff099eb2e3dfdb, []int{6}
+	return file_commands_proto_rawDescGZIP(), []int{6}
 }
 
-func (m *InstantMetadata) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_InstantMetadata.Unmarshal(m, b)
-}
-func (m *InstantMetadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_InstantMetadata.Marshal(b, m, deterministic)
-}
-func (m *InstantMetadata) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_InstantMetadata.Merge(m, src)
-}
-func (m *InstantMetadata) XXX_Size() int {
-	return xxx_messageInfo_InstantMetadata.Size(m)
-}
-func (m *InstantMetadata) XXX_DiscardUnknown() {
-	xxx_messageInfo_InstantMetadata.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_InstantMetadata proto.InternalMessageInfo
-
-func (m *InstantMetadata) GetIsInstant() bool {
-	if m != nil {
-		return m.IsInstant
+func (x *InstantMetadata) GetIsInstant() bool {
+	if x != nil {
+		return x.IsInstant
 	}
 	return false
 }
 
-func (m *InstantMetadata) GetDeliveryType() DeliveryType {
-	if m != nil {
-		return m.DeliveryType
+func (x *InstantMetadata) GetDeliveryType() DeliveryType {
+	if x != nil {
+		return x.DeliveryType
 	}
 	return DeliveryType_UNKNOWN_DELIVERY_TYPE
 }
 
 // Deprecated: Do not use.
-func (m *InstantMetadata) GetOnDemandDeprecated() bool {
-	if m != nil {
-		return m.OnDemandDeprecated
+func (x *InstantMetadata) GetOnDemandDeprecated() bool {
+	if x != nil {
+		return x.OnDemandDeprecated
 	}
 	return false
 }
 
 type ApkDescription struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	Targeting *ApkTargeting `protobuf:"bytes,1,opt,name=targeting,proto3" json:"targeting,omitempty"`
 	// Path to the APK file.
 	// BEGIN-INTERNAL
 	// The path may be a blobkey if the proto is not constructed by bundletool.
 	// END-INTERNAL
 	Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"`
-	// Types that are valid to be assigned to ApkMetadataOneofValue:
+	// Types that are assignable to ApkMetadataOneofValue:
 	//	*ApkDescription_SplitApkMetadata
 	//	*ApkDescription_StandaloneApkMetadata
 	//	*ApkDescription_InstantApkMetadata
@@ -558,75 +671,134 @@
 	//	*ApkDescription_AssetSliceMetadata
 	//	*ApkDescription_ApexApkMetadata
 	ApkMetadataOneofValue isApkDescription_ApkMetadataOneofValue `protobuf_oneof:"apk_metadata_oneof_value"`
-	XXX_NoUnkeyedLiteral  struct{}                               `json:"-"`
-	XXX_unrecognized      []byte                                 `json:"-"`
-	XXX_sizecache         int32                                  `json:"-"`
 }
 
-func (m *ApkDescription) Reset()         { *m = ApkDescription{} }
-func (m *ApkDescription) String() string { return proto.CompactTextString(m) }
-func (*ApkDescription) ProtoMessage()    {}
+func (x *ApkDescription) Reset() {
+	*x = ApkDescription{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_commands_proto_msgTypes[7]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ApkDescription) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ApkDescription) ProtoMessage() {}
+
+func (x *ApkDescription) ProtoReflect() protoreflect.Message {
+	mi := &file_commands_proto_msgTypes[7]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ApkDescription.ProtoReflect.Descriptor instead.
 func (*ApkDescription) Descriptor() ([]byte, []int) {
-	return fileDescriptor_0dff099eb2e3dfdb, []int{7}
+	return file_commands_proto_rawDescGZIP(), []int{7}
 }
 
-func (m *ApkDescription) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_ApkDescription.Unmarshal(m, b)
-}
-func (m *ApkDescription) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_ApkDescription.Marshal(b, m, deterministic)
-}
-func (m *ApkDescription) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_ApkDescription.Merge(m, src)
-}
-func (m *ApkDescription) XXX_Size() int {
-	return xxx_messageInfo_ApkDescription.Size(m)
-}
-func (m *ApkDescription) XXX_DiscardUnknown() {
-	xxx_messageInfo_ApkDescription.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_ApkDescription proto.InternalMessageInfo
-
-func (m *ApkDescription) GetTargeting() *ApkTargeting {
-	if m != nil {
-		return m.Targeting
+func (x *ApkDescription) GetTargeting() *ApkTargeting {
+	if x != nil {
+		return x.Targeting
 	}
 	return nil
 }
 
-func (m *ApkDescription) GetPath() string {
-	if m != nil {
-		return m.Path
+func (x *ApkDescription) GetPath() string {
+	if x != nil {
+		return x.Path
 	}
 	return ""
 }
 
+func (m *ApkDescription) GetApkMetadataOneofValue() isApkDescription_ApkMetadataOneofValue {
+	if m != nil {
+		return m.ApkMetadataOneofValue
+	}
+	return nil
+}
+
+func (x *ApkDescription) GetSplitApkMetadata() *SplitApkMetadata {
+	if x, ok := x.GetApkMetadataOneofValue().(*ApkDescription_SplitApkMetadata); ok {
+		return x.SplitApkMetadata
+	}
+	return nil
+}
+
+func (x *ApkDescription) GetStandaloneApkMetadata() *StandaloneApkMetadata {
+	if x, ok := x.GetApkMetadataOneofValue().(*ApkDescription_StandaloneApkMetadata); ok {
+		return x.StandaloneApkMetadata
+	}
+	return nil
+}
+
+func (x *ApkDescription) GetInstantApkMetadata() *SplitApkMetadata {
+	if x, ok := x.GetApkMetadataOneofValue().(*ApkDescription_InstantApkMetadata); ok {
+		return x.InstantApkMetadata
+	}
+	return nil
+}
+
+func (x *ApkDescription) GetSystemApkMetadata() *SystemApkMetadata {
+	if x, ok := x.GetApkMetadataOneofValue().(*ApkDescription_SystemApkMetadata); ok {
+		return x.SystemApkMetadata
+	}
+	return nil
+}
+
+func (x *ApkDescription) GetAssetSliceMetadata() *SplitApkMetadata {
+	if x, ok := x.GetApkMetadataOneofValue().(*ApkDescription_AssetSliceMetadata); ok {
+		return x.AssetSliceMetadata
+	}
+	return nil
+}
+
+func (x *ApkDescription) GetApexApkMetadata() *ApexApkMetadata {
+	if x, ok := x.GetApkMetadataOneofValue().(*ApkDescription_ApexApkMetadata); ok {
+		return x.ApexApkMetadata
+	}
+	return nil
+}
+
 type isApkDescription_ApkMetadataOneofValue interface {
 	isApkDescription_ApkMetadataOneofValue()
 }
 
 type ApkDescription_SplitApkMetadata struct {
+	// Set only for Split APKs.
 	SplitApkMetadata *SplitApkMetadata `protobuf:"bytes,3,opt,name=split_apk_metadata,json=splitApkMetadata,proto3,oneof"`
 }
 
 type ApkDescription_StandaloneApkMetadata struct {
+	// Set only for standalone APKs.
 	StandaloneApkMetadata *StandaloneApkMetadata `protobuf:"bytes,4,opt,name=standalone_apk_metadata,json=standaloneApkMetadata,proto3,oneof"`
 }
 
 type ApkDescription_InstantApkMetadata struct {
+	// Set only for Instant split APKs.
 	InstantApkMetadata *SplitApkMetadata `protobuf:"bytes,5,opt,name=instant_apk_metadata,json=instantApkMetadata,proto3,oneof"`
 }
 
 type ApkDescription_SystemApkMetadata struct {
+	// Set only for system APKs.
 	SystemApkMetadata *SystemApkMetadata `protobuf:"bytes,6,opt,name=system_apk_metadata,json=systemApkMetadata,proto3,oneof"`
 }
 
 type ApkDescription_AssetSliceMetadata struct {
+	// Set only for asset slices.
 	AssetSliceMetadata *SplitApkMetadata `protobuf:"bytes,7,opt,name=asset_slice_metadata,json=assetSliceMetadata,proto3,oneof"`
 }
 
 type ApkDescription_ApexApkMetadata struct {
+	// Set only for APEX APKs.
 	ApexApkMetadata *ApexApkMetadata `protobuf:"bytes,8,opt,name=apex_apk_metadata,json=apexApkMetadata,proto3,oneof"`
 }
 
@@ -642,58 +814,708 @@
 
 func (*ApkDescription_ApexApkMetadata) isApkDescription_ApkMetadataOneofValue() {}
 
-func (m *ApkDescription) GetApkMetadataOneofValue() isApkDescription_ApkMetadataOneofValue {
-	if m != nil {
-		return m.ApkMetadataOneofValue
+// Holds data specific to Split APKs.
+type SplitApkMetadata struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	SplitId string `protobuf:"bytes,1,opt,name=split_id,json=splitId,proto3" json:"split_id,omitempty"`
+	// Indicates whether this APK is the master split of the module.
+	IsMasterSplit bool `protobuf:"varint,2,opt,name=is_master_split,json=isMasterSplit,proto3" json:"is_master_split,omitempty"`
+}
+
+func (x *SplitApkMetadata) Reset() {
+	*x = SplitApkMetadata{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_commands_proto_msgTypes[8]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *SplitApkMetadata) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SplitApkMetadata) ProtoMessage() {}
+
+func (x *SplitApkMetadata) ProtoReflect() protoreflect.Message {
+	mi := &file_commands_proto_msgTypes[8]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use SplitApkMetadata.ProtoReflect.Descriptor instead.
+func (*SplitApkMetadata) Descriptor() ([]byte, []int) {
+	return file_commands_proto_rawDescGZIP(), []int{8}
+}
+
+func (x *SplitApkMetadata) GetSplitId() string {
+	if x != nil {
+		return x.SplitId
+	}
+	return ""
+}
+
+func (x *SplitApkMetadata) GetIsMasterSplit() bool {
+	if x != nil {
+		return x.IsMasterSplit
+	}
+	return false
+}
+
+// Holds data specific to Standalone APKs.
+type StandaloneApkMetadata struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Names of the modules fused in this standalone APK.
+	FusedModuleName []string `protobuf:"bytes,1,rep,name=fused_module_name,json=fusedModuleName,proto3" json:"fused_module_name,omitempty"`
+}
+
+func (x *StandaloneApkMetadata) Reset() {
+	*x = StandaloneApkMetadata{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_commands_proto_msgTypes[9]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *StandaloneApkMetadata) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*StandaloneApkMetadata) ProtoMessage() {}
+
+func (x *StandaloneApkMetadata) ProtoReflect() protoreflect.Message {
+	mi := &file_commands_proto_msgTypes[9]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use StandaloneApkMetadata.ProtoReflect.Descriptor instead.
+func (*StandaloneApkMetadata) Descriptor() ([]byte, []int) {
+	return file_commands_proto_rawDescGZIP(), []int{9}
+}
+
+func (x *StandaloneApkMetadata) GetFusedModuleName() []string {
+	if x != nil {
+		return x.FusedModuleName
 	}
 	return nil
 }
 
-func (m *ApkDescription) GetSplitApkMetadata() *SplitApkMetadata {
-	if x, ok := m.GetApkMetadataOneofValue().(*ApkDescription_SplitApkMetadata); ok {
-		return x.SplitApkMetadata
+// Holds data specific to system APKs.
+type SystemApkMetadata struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Names of the modules fused in this system APK.
+	FusedModuleName []string `protobuf:"bytes,1,rep,name=fused_module_name,json=fusedModuleName,proto3" json:"fused_module_name,omitempty"`
+	// Indicates whether the APK is uncompressed system APK, stub APK or
+	// compressed system APK.
+	SystemApkType SystemApkMetadata_SystemApkType `protobuf:"varint,2,opt,name=system_apk_type,json=systemApkType,proto3,enum=android.bundle.SystemApkMetadata_SystemApkType" json:"system_apk_type,omitempty"`
+}
+
+func (x *SystemApkMetadata) Reset() {
+	*x = SystemApkMetadata{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_commands_proto_msgTypes[10]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *SystemApkMetadata) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SystemApkMetadata) ProtoMessage() {}
+
+func (x *SystemApkMetadata) ProtoReflect() protoreflect.Message {
+	mi := &file_commands_proto_msgTypes[10]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use SystemApkMetadata.ProtoReflect.Descriptor instead.
+func (*SystemApkMetadata) Descriptor() ([]byte, []int) {
+	return file_commands_proto_rawDescGZIP(), []int{10}
+}
+
+func (x *SystemApkMetadata) GetFusedModuleName() []string {
+	if x != nil {
+		return x.FusedModuleName
 	}
 	return nil
 }
 
-func (m *ApkDescription) GetStandaloneApkMetadata() *StandaloneApkMetadata {
-	if x, ok := m.GetApkMetadataOneofValue().(*ApkDescription_StandaloneApkMetadata); ok {
-		return x.StandaloneApkMetadata
+func (x *SystemApkMetadata) GetSystemApkType() SystemApkMetadata_SystemApkType {
+	if x != nil {
+		return x.SystemApkType
+	}
+	return SystemApkMetadata_UNSPECIFIED_VALUE
+}
+
+// Holds data specific to APEX APKs.
+type ApexApkMetadata struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Configuration for processing of APKs embedded in an APEX image.
+	ApexEmbeddedApkConfig []*ApexEmbeddedApkConfig `protobuf:"bytes,1,rep,name=apex_embedded_apk_config,json=apexEmbeddedApkConfig,proto3" json:"apex_embedded_apk_config,omitempty"`
+}
+
+func (x *ApexApkMetadata) Reset() {
+	*x = ApexApkMetadata{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_commands_proto_msgTypes[11]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ApexApkMetadata) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ApexApkMetadata) ProtoMessage() {}
+
+func (x *ApexApkMetadata) ProtoReflect() protoreflect.Message {
+	mi := &file_commands_proto_msgTypes[11]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ApexApkMetadata.ProtoReflect.Descriptor instead.
+func (*ApexApkMetadata) Descriptor() ([]byte, []int) {
+	return file_commands_proto_rawDescGZIP(), []int{11}
+}
+
+func (x *ApexApkMetadata) GetApexEmbeddedApkConfig() []*ApexEmbeddedApkConfig {
+	if x != nil {
+		return x.ApexEmbeddedApkConfig
 	}
 	return nil
 }
 
-func (m *ApkDescription) GetInstantApkMetadata() *SplitApkMetadata {
-	if x, ok := m.GetApkMetadataOneofValue().(*ApkDescription_InstantApkMetadata); ok {
-		return x.InstantApkMetadata
-	}
-	return nil
+type LocalTestingInfo struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Indicates if the bundle is built in local testing mode.
+	Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
+	// The local testing path, as specified in the base manifest.
+	// This refers to the relative path on the external directory of the app where
+	// APKs will be pushed for local testing.
+	// Set only if local testing is enabled.
+	LocalTestingPath string `protobuf:"bytes,2,opt,name=local_testing_path,json=localTestingPath,proto3" json:"local_testing_path,omitempty"`
 }
 
-func (m *ApkDescription) GetSystemApkMetadata() *SystemApkMetadata {
-	if x, ok := m.GetApkMetadataOneofValue().(*ApkDescription_SystemApkMetadata); ok {
-		return x.SystemApkMetadata
+func (x *LocalTestingInfo) Reset() {
+	*x = LocalTestingInfo{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_commands_proto_msgTypes[12]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
 	}
-	return nil
 }
 
-func (m *ApkDescription) GetAssetSliceMetadata() *SplitApkMetadata {
-	if x, ok := m.GetApkMetadataOneofValue().(*ApkDescription_AssetSliceMetadata); ok {
-		return x.AssetSliceMetadata
-	}
-	return nil
+func (x *LocalTestingInfo) String() string {
+	return protoimpl.X.MessageStringOf(x)
 }
 
-func (m *ApkDescription) GetApexApkMetadata() *ApexApkMetadata {
-	if x, ok := m.GetApkMetadataOneofValue().(*ApkDescription_ApexApkMetadata); ok {
-		return x.ApexApkMetadata
+func (*LocalTestingInfo) ProtoMessage() {}
+
+func (x *LocalTestingInfo) ProtoReflect() protoreflect.Message {
+	mi := &file_commands_proto_msgTypes[12]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
 	}
-	return nil
+	return mi.MessageOf(x)
 }
 
-// XXX_OneofWrappers is for the internal use of the proto package.
-func (*ApkDescription) XXX_OneofWrappers() []interface{} {
-	return []interface{}{
+// Deprecated: Use LocalTestingInfo.ProtoReflect.Descriptor instead.
+func (*LocalTestingInfo) Descriptor() ([]byte, []int) {
+	return file_commands_proto_rawDescGZIP(), []int{12}
+}
+
+func (x *LocalTestingInfo) GetEnabled() bool {
+	if x != nil {
+		return x.Enabled
+	}
+	return false
+}
+
+func (x *LocalTestingInfo) GetLocalTestingPath() string {
+	if x != nil {
+		return x.LocalTestingPath
+	}
+	return ""
+}
+
+var File_commands_proto protoreflect.FileDescriptor
+
+var file_commands_proto_rawDesc = []byte{
+	0x0a, 0x0e, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x12, 0x0e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65,
+	0x1a, 0x0c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x0f,
+	0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22,
+	0xba, 0x02, 0x0a, 0x0f, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x41, 0x70, 0x6b, 0x73, 0x52, 0x65, 0x73,
+	0x75, 0x6c, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67, 0x65, 0x5f, 0x6e,
+	0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x61, 0x63, 0x6b, 0x61,
+	0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x31, 0x0a, 0x07, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e,
+	0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69,
+	0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74,
+	0x52, 0x07, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x12, 0x3a, 0x0a, 0x0a, 0x62, 0x75, 0x6e,
+	0x64, 0x6c, 0x65, 0x74, 0x6f, 0x6f, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e,
+	0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x42,
+	0x75, 0x6e, 0x64, 0x6c, 0x65, 0x74, 0x6f, 0x6f, 0x6c, 0x52, 0x0a, 0x62, 0x75, 0x6e, 0x64, 0x6c,
+	0x65, 0x74, 0x6f, 0x6f, 0x6c, 0x12, 0x45, 0x0a, 0x0f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x73,
+	0x6c, 0x69, 0x63, 0x65, 0x5f, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d,
+	0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e,
+	0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x53, 0x65, 0x74, 0x52, 0x0d, 0x61,
+	0x73, 0x73, 0x65, 0x74, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x53, 0x65, 0x74, 0x12, 0x4e, 0x0a, 0x12,
+	0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x69, 0x6e,
+	0x66, 0x6f, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f,
+	0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x54,
+	0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x10, 0x6c, 0x6f, 0x63, 0x61,
+	0x6c, 0x54, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0xa1, 0x01, 0x0a,
+	0x07, 0x56, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x12, 0x3e, 0x0a, 0x09, 0x74, 0x61, 0x72, 0x67,
+	0x65, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x61, 0x6e,
+	0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x56, 0x61, 0x72,
+	0x69, 0x61, 0x6e, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x09, 0x74,
+	0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x2f, 0x0a, 0x07, 0x61, 0x70, 0x6b, 0x5f,
+	0x73, 0x65, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x6e, 0x64, 0x72,
+	0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x41, 0x70, 0x6b, 0x53, 0x65,
+	0x74, 0x52, 0x06, 0x61, 0x70, 0x6b, 0x53, 0x65, 0x74, 0x12, 0x25, 0x0a, 0x0e, 0x76, 0x61, 0x72,
+	0x69, 0x61, 0x6e, 0x74, 0x5f, 0x6e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28,
+	0x0d, 0x52, 0x0d, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72,
+	0x22, 0x9a, 0x01, 0x0a, 0x06, 0x41, 0x70, 0x6b, 0x53, 0x65, 0x74, 0x12, 0x47, 0x0a, 0x0f, 0x6d,
+	0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62,
+	0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x4d, 0x65, 0x74, 0x61,
+	0x64, 0x61, 0x74, 0x61, 0x52, 0x0e, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x4d, 0x65, 0x74, 0x61,
+	0x64, 0x61, 0x74, 0x61, 0x12, 0x47, 0x0a, 0x0f, 0x61, 0x70, 0x6b, 0x5f, 0x64, 0x65, 0x73, 0x63,
+	0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e,
+	0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x41,
+	0x70, 0x6b, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x61,
+	0x70, 0x6b, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x9f, 0x02,
+	0x0a, 0x0e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
+	0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
+	0x6e, 0x61, 0x6d, 0x65, 0x12, 0x41, 0x0a, 0x0d, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79,
+	0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x61, 0x6e,
+	0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x44, 0x65, 0x6c,
+	0x69, 0x76, 0x65, 0x72, 0x79, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0c, 0x64, 0x65, 0x6c, 0x69, 0x76,
+	0x65, 0x72, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x73, 0x5f, 0x69, 0x6e,
+	0x73, 0x74, 0x61, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, 0x73, 0x49,
+	0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x12, 0x22, 0x0a, 0x0c, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64,
+	0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x64, 0x65,
+	0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x12, 0x3d, 0x0a, 0x09, 0x74, 0x61,
+	0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e,
+	0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x4d,
+	0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x09,
+	0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x34, 0x0a, 0x14, 0x6f, 0x6e, 0x5f,
+	0x64, 0x65, 0x6d, 0x61, 0x6e, 0x64, 0x5f, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65,
+	0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x42, 0x02, 0x18, 0x01, 0x52, 0x12, 0x6f, 0x6e, 0x44,
+	0x65, 0x6d, 0x61, 0x6e, 0x64, 0x44, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x22,
+	0xb1, 0x01, 0x0a, 0x0d, 0x41, 0x73, 0x73, 0x65, 0x74, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x53, 0x65,
+	0x74, 0x12, 0x57, 0x0a, 0x15, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
+	0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
+	0x32, 0x23, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c,
+	0x65, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x4d, 0x65, 0x74,
+	0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x13, 0x61, 0x73, 0x73, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x75,
+	0x6c, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x47, 0x0a, 0x0f, 0x61, 0x70,
+	0x6b, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20,
+	0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75,
+	0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x41, 0x70, 0x6b, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
+	0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x61, 0x70, 0x6b, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
+	0x69, 0x6f, 0x6e, 0x22, 0xee, 0x01, 0x0a, 0x13, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4d, 0x6f, 0x64,
+	0x75, 0x6c, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x12, 0x0a, 0x04, 0x6e,
+	0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12,
+	0x41, 0x0a, 0x0d, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65,
+	0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
+	0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x44, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79,
+	0x54, 0x79, 0x70, 0x65, 0x52, 0x0c, 0x64, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x54, 0x79,
+	0x70, 0x65, 0x12, 0x4a, 0x0a, 0x10, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x5f, 0x6d, 0x65,
+	0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x61,
+	0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x49, 0x6e,
+	0x73, 0x74, 0x61, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x0f, 0x69,
+	0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x34,
+	0x0a, 0x14, 0x6f, 0x6e, 0x5f, 0x64, 0x65, 0x6d, 0x61, 0x6e, 0x64, 0x5f, 0x64, 0x65, 0x70, 0x72,
+	0x65, 0x63, 0x61, 0x74, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x42, 0x02, 0x18, 0x01,
+	0x52, 0x12, 0x6f, 0x6e, 0x44, 0x65, 0x6d, 0x61, 0x6e, 0x64, 0x44, 0x65, 0x70, 0x72, 0x65, 0x63,
+	0x61, 0x74, 0x65, 0x64, 0x22, 0xa9, 0x01, 0x0a, 0x0f, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74,
+	0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x73, 0x5f, 0x69,
+	0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x09, 0x69, 0x73,
+	0x49, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74, 0x12, 0x41, 0x0a, 0x0d, 0x64, 0x65, 0x6c, 0x69, 0x76,
+	0x65, 0x72, 0x79, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c,
+	0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e,
+	0x44, 0x65, 0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0c, 0x64, 0x65,
+	0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x34, 0x0a, 0x14, 0x6f, 0x6e,
+	0x5f, 0x64, 0x65, 0x6d, 0x61, 0x6e, 0x64, 0x5f, 0x64, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74,
+	0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x42, 0x02, 0x18, 0x01, 0x52, 0x12, 0x6f, 0x6e,
+	0x44, 0x65, 0x6d, 0x61, 0x6e, 0x64, 0x44, 0x65, 0x70, 0x72, 0x65, 0x63, 0x61, 0x74, 0x65, 0x64,
+	0x22, 0xff, 0x04, 0x0a, 0x0e, 0x41, 0x70, 0x6b, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
+	0x69, 0x6f, 0x6e, 0x12, 0x3a, 0x0a, 0x09, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67,
+	0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
+	0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x41, 0x70, 0x6b, 0x54, 0x61, 0x72, 0x67, 0x65,
+	0x74, 0x69, 0x6e, 0x67, 0x52, 0x09, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x12,
+	0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70,
+	0x61, 0x74, 0x68, 0x12, 0x50, 0x0a, 0x12, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x5f, 0x61, 0x70, 0x6b,
+	0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32,
+	0x20, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65,
+	0x2e, 0x53, 0x70, 0x6c, 0x69, 0x74, 0x41, 0x70, 0x6b, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
+	0x61, 0x48, 0x00, 0x52, 0x10, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x41, 0x70, 0x6b, 0x4d, 0x65, 0x74,
+	0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x5f, 0x0a, 0x17, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x6c,
+	0x6f, 0x6e, 0x65, 0x5f, 0x61, 0x70, 0x6b, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
+	0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
+	0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x6c, 0x6f,
+	0x6e, 0x65, 0x41, 0x70, 0x6b, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x48, 0x00, 0x52,
+	0x15, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x6c, 0x6f, 0x6e, 0x65, 0x41, 0x70, 0x6b, 0x4d, 0x65,
+	0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x54, 0x0a, 0x14, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e,
+	0x74, 0x5f, 0x61, 0x70, 0x6b, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x05,
+	0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62,
+	0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x53, 0x70, 0x6c, 0x69, 0x74, 0x41, 0x70, 0x6b, 0x4d, 0x65,
+	0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x48, 0x00, 0x52, 0x12, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e,
+	0x74, 0x41, 0x70, 0x6b, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x53, 0x0a, 0x13,
+	0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x61, 0x70, 0x6b, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64,
+	0x61, 0x74, 0x61, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x61, 0x6e, 0x64, 0x72,
+	0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65,
+	0x6d, 0x41, 0x70, 0x6b, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x48, 0x00, 0x52, 0x11,
+	0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x41, 0x70, 0x6b, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
+	0x61, 0x12, 0x54, 0x0a, 0x14, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x73, 0x6c, 0x69, 0x63, 0x65,
+	0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32,
+	0x20, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65,
+	0x2e, 0x53, 0x70, 0x6c, 0x69, 0x74, 0x41, 0x70, 0x6b, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
+	0x61, 0x48, 0x00, 0x52, 0x12, 0x61, 0x73, 0x73, 0x65, 0x74, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x4d,
+	0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x4d, 0x0a, 0x11, 0x61, 0x70, 0x65, 0x78, 0x5f,
+	0x61, 0x70, 0x6b, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x08, 0x20, 0x01,
+	0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e,
+	0x64, 0x6c, 0x65, 0x2e, 0x41, 0x70, 0x65, 0x78, 0x41, 0x70, 0x6b, 0x4d, 0x65, 0x74, 0x61, 0x64,
+	0x61, 0x74, 0x61, 0x48, 0x00, 0x52, 0x0f, 0x61, 0x70, 0x65, 0x78, 0x41, 0x70, 0x6b, 0x4d, 0x65,
+	0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x42, 0x1a, 0x0a, 0x18, 0x61, 0x70, 0x6b, 0x5f, 0x6d, 0x65,
+	0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x6f, 0x6e, 0x65, 0x6f, 0x66, 0x5f, 0x76, 0x61, 0x6c,
+	0x75, 0x65, 0x22, 0x55, 0x0a, 0x10, 0x53, 0x70, 0x6c, 0x69, 0x74, 0x41, 0x70, 0x6b, 0x4d, 0x65,
+	0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x19, 0x0a, 0x08, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x5f,
+	0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x49,
+	0x64, 0x12, 0x26, 0x0a, 0x0f, 0x69, 0x73, 0x5f, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x73,
+	0x70, 0x6c, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x69, 0x73, 0x4d, 0x61,
+	0x73, 0x74, 0x65, 0x72, 0x53, 0x70, 0x6c, 0x69, 0x74, 0x22, 0x49, 0x0a, 0x15, 0x53, 0x74, 0x61,
+	0x6e, 0x64, 0x61, 0x6c, 0x6f, 0x6e, 0x65, 0x41, 0x70, 0x6b, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61,
+	0x74, 0x61, 0x12, 0x2a, 0x0a, 0x11, 0x66, 0x75, 0x73, 0x65, 0x64, 0x5f, 0x6d, 0x6f, 0x64, 0x75,
+	0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x66,
+	0x75, 0x73, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x4a, 0x04,
+	0x08, 0x02, 0x10, 0x03, 0x22, 0xf4, 0x01, 0x0a, 0x11, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x41,
+	0x70, 0x6b, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x2a, 0x0a, 0x11, 0x66, 0x75,
+	0x73, 0x65, 0x64, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18,
+	0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0f, 0x66, 0x75, 0x73, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75,
+	0x6c, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x57, 0x0a, 0x0f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d,
+	0x5f, 0x61, 0x70, 0x6b, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32,
+	0x2f, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65,
+	0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x41, 0x70, 0x6b, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61,
+	0x74, 0x61, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x41, 0x70, 0x6b, 0x54, 0x79, 0x70, 0x65,
+	0x52, 0x0d, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x41, 0x70, 0x6b, 0x54, 0x79, 0x70, 0x65, 0x22,
+	0x5a, 0x0a, 0x0d, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x41, 0x70, 0x6b, 0x54, 0x79, 0x70, 0x65,
+	0x12, 0x15, 0x0a, 0x11, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x5f,
+	0x56, 0x41, 0x4c, 0x55, 0x45, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x59, 0x53, 0x54, 0x45,
+	0x4d, 0x10, 0x01, 0x12, 0x0f, 0x0a, 0x0b, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x5f, 0x53, 0x54,
+	0x55, 0x42, 0x10, 0x02, 0x12, 0x15, 0x0a, 0x11, 0x53, 0x59, 0x53, 0x54, 0x45, 0x4d, 0x5f, 0x43,
+	0x4f, 0x4d, 0x50, 0x52, 0x45, 0x53, 0x53, 0x45, 0x44, 0x10, 0x03, 0x22, 0x71, 0x0a, 0x0f, 0x41,
+	0x70, 0x65, 0x78, 0x41, 0x70, 0x6b, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x5e,
+	0x0a, 0x18, 0x61, 0x70, 0x65, 0x78, 0x5f, 0x65, 0x6d, 0x62, 0x65, 0x64, 0x64, 0x65, 0x64, 0x5f,
+	0x61, 0x70, 0x6b, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b,
+	0x32, 0x25, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c,
+	0x65, 0x2e, 0x41, 0x70, 0x65, 0x78, 0x45, 0x6d, 0x62, 0x65, 0x64, 0x64, 0x65, 0x64, 0x41, 0x70,
+	0x6b, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x15, 0x61, 0x70, 0x65, 0x78, 0x45, 0x6d, 0x62,
+	0x65, 0x64, 0x64, 0x65, 0x64, 0x41, 0x70, 0x6b, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x5a,
+	0x0a, 0x10, 0x4c, 0x6f, 0x63, 0x61, 0x6c, 0x54, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x49, 0x6e,
+	0x66, 0x6f, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20,
+	0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x2c, 0x0a, 0x12,
+	0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x5f, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x61,
+	0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x54,
+	0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x61, 0x74, 0x68, 0x2a, 0x5b, 0x0a, 0x0c, 0x44, 0x65,
+	0x6c, 0x69, 0x76, 0x65, 0x72, 0x79, 0x54, 0x79, 0x70, 0x65, 0x12, 0x19, 0x0a, 0x15, 0x55, 0x4e,
+	0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x5f, 0x44, 0x45, 0x4c, 0x49, 0x56, 0x45, 0x52, 0x59, 0x5f, 0x54,
+	0x59, 0x50, 0x45, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x49, 0x4e, 0x53, 0x54, 0x41, 0x4c, 0x4c,
+	0x5f, 0x54, 0x49, 0x4d, 0x45, 0x10, 0x01, 0x12, 0x0d, 0x0a, 0x09, 0x4f, 0x4e, 0x5f, 0x44, 0x45,
+	0x4d, 0x41, 0x4e, 0x44, 0x10, 0x02, 0x12, 0x0f, 0x0a, 0x0b, 0x46, 0x41, 0x53, 0x54, 0x5f, 0x46,
+	0x4f, 0x4c, 0x4c, 0x4f, 0x57, 0x10, 0x03, 0x42, 0x41, 0x0a, 0x12, 0x63, 0x6f, 0x6d, 0x2e, 0x61,
+	0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x5a, 0x2b, 0x61,
+	0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x63, 0x6d, 0x64,
+	0x2f, 0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x5f, 0x61, 0x70, 0x6b, 0x73, 0x2f, 0x62, 0x75,
+	0x6e, 0x64, 0x6c, 0x65, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x33,
+}
+
+var (
+	file_commands_proto_rawDescOnce sync.Once
+	file_commands_proto_rawDescData = file_commands_proto_rawDesc
+)
+
+func file_commands_proto_rawDescGZIP() []byte {
+	file_commands_proto_rawDescOnce.Do(func() {
+		file_commands_proto_rawDescData = protoimpl.X.CompressGZIP(file_commands_proto_rawDescData)
+	})
+	return file_commands_proto_rawDescData
+}
+
+var file_commands_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
+var file_commands_proto_msgTypes = make([]protoimpl.MessageInfo, 13)
+var file_commands_proto_goTypes = []interface{}{
+	(DeliveryType)(0),                    // 0: android.bundle.DeliveryType
+	(SystemApkMetadata_SystemApkType)(0), // 1: android.bundle.SystemApkMetadata.SystemApkType
+	(*BuildApksResult)(nil),              // 2: android.bundle.BuildApksResult
+	(*Variant)(nil),                      // 3: android.bundle.Variant
+	(*ApkSet)(nil),                       // 4: android.bundle.ApkSet
+	(*ModuleMetadata)(nil),               // 5: android.bundle.ModuleMetadata
+	(*AssetSliceSet)(nil),                // 6: android.bundle.AssetSliceSet
+	(*AssetModuleMetadata)(nil),          // 7: android.bundle.AssetModuleMetadata
+	(*InstantMetadata)(nil),              // 8: android.bundle.InstantMetadata
+	(*ApkDescription)(nil),               // 9: android.bundle.ApkDescription
+	(*SplitApkMetadata)(nil),             // 10: android.bundle.SplitApkMetadata
+	(*StandaloneApkMetadata)(nil),        // 11: android.bundle.StandaloneApkMetadata
+	(*SystemApkMetadata)(nil),            // 12: android.bundle.SystemApkMetadata
+	(*ApexApkMetadata)(nil),              // 13: android.bundle.ApexApkMetadata
+	(*LocalTestingInfo)(nil),             // 14: android.bundle.LocalTestingInfo
+	(*Bundletool)(nil),                   // 15: android.bundle.Bundletool
+	(*VariantTargeting)(nil),             // 16: android.bundle.VariantTargeting
+	(*ModuleTargeting)(nil),              // 17: android.bundle.ModuleTargeting
+	(*ApkTargeting)(nil),                 // 18: android.bundle.ApkTargeting
+	(*ApexEmbeddedApkConfig)(nil),        // 19: android.bundle.ApexEmbeddedApkConfig
+}
+var file_commands_proto_depIdxs = []int32{
+	3,  // 0: android.bundle.BuildApksResult.variant:type_name -> android.bundle.Variant
+	15, // 1: android.bundle.BuildApksResult.bundletool:type_name -> android.bundle.Bundletool
+	6,  // 2: android.bundle.BuildApksResult.asset_slice_set:type_name -> android.bundle.AssetSliceSet
+	14, // 3: android.bundle.BuildApksResult.local_testing_info:type_name -> android.bundle.LocalTestingInfo
+	16, // 4: android.bundle.Variant.targeting:type_name -> android.bundle.VariantTargeting
+	4,  // 5: android.bundle.Variant.apk_set:type_name -> android.bundle.ApkSet
+	5,  // 6: android.bundle.ApkSet.module_metadata:type_name -> android.bundle.ModuleMetadata
+	9,  // 7: android.bundle.ApkSet.apk_description:type_name -> android.bundle.ApkDescription
+	0,  // 8: android.bundle.ModuleMetadata.delivery_type:type_name -> android.bundle.DeliveryType
+	17, // 9: android.bundle.ModuleMetadata.targeting:type_name -> android.bundle.ModuleTargeting
+	7,  // 10: android.bundle.AssetSliceSet.asset_module_metadata:type_name -> android.bundle.AssetModuleMetadata
+	9,  // 11: android.bundle.AssetSliceSet.apk_description:type_name -> android.bundle.ApkDescription
+	0,  // 12: android.bundle.AssetModuleMetadata.delivery_type:type_name -> android.bundle.DeliveryType
+	8,  // 13: android.bundle.AssetModuleMetadata.instant_metadata:type_name -> android.bundle.InstantMetadata
+	0,  // 14: android.bundle.InstantMetadata.delivery_type:type_name -> android.bundle.DeliveryType
+	18, // 15: android.bundle.ApkDescription.targeting:type_name -> android.bundle.ApkTargeting
+	10, // 16: android.bundle.ApkDescription.split_apk_metadata:type_name -> android.bundle.SplitApkMetadata
+	11, // 17: android.bundle.ApkDescription.standalone_apk_metadata:type_name -> android.bundle.StandaloneApkMetadata
+	10, // 18: android.bundle.ApkDescription.instant_apk_metadata:type_name -> android.bundle.SplitApkMetadata
+	12, // 19: android.bundle.ApkDescription.system_apk_metadata:type_name -> android.bundle.SystemApkMetadata
+	10, // 20: android.bundle.ApkDescription.asset_slice_metadata:type_name -> android.bundle.SplitApkMetadata
+	13, // 21: android.bundle.ApkDescription.apex_apk_metadata:type_name -> android.bundle.ApexApkMetadata
+	1,  // 22: android.bundle.SystemApkMetadata.system_apk_type:type_name -> android.bundle.SystemApkMetadata.SystemApkType
+	19, // 23: android.bundle.ApexApkMetadata.apex_embedded_apk_config:type_name -> android.bundle.ApexEmbeddedApkConfig
+	24, // [24:24] is the sub-list for method output_type
+	24, // [24:24] is the sub-list for method input_type
+	24, // [24:24] is the sub-list for extension type_name
+	24, // [24:24] is the sub-list for extension extendee
+	0,  // [0:24] is the sub-list for field type_name
+}
+
+func init() { file_commands_proto_init() }
+func file_commands_proto_init() {
+	if File_commands_proto != nil {
+		return
+	}
+	file_config_proto_init()
+	file_targeting_proto_init()
+	if !protoimpl.UnsafeEnabled {
+		file_commands_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*BuildApksResult); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_commands_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Variant); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_commands_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ApkSet); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_commands_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ModuleMetadata); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_commands_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*AssetSliceSet); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_commands_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*AssetModuleMetadata); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_commands_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*InstantMetadata); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_commands_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ApkDescription); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_commands_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*SplitApkMetadata); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_commands_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*StandaloneApkMetadata); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_commands_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*SystemApkMetadata); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_commands_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ApexApkMetadata); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_commands_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*LocalTestingInfo); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	file_commands_proto_msgTypes[7].OneofWrappers = []interface{}{
 		(*ApkDescription_SplitApkMetadata)(nil),
 		(*ApkDescription_StandaloneApkMetadata)(nil),
 		(*ApkDescription_InstantApkMetadata)(nil),
@@ -701,333 +1523,23 @@
 		(*ApkDescription_AssetSliceMetadata)(nil),
 		(*ApkDescription_ApexApkMetadata)(nil),
 	}
-}
-
-// Holds data specific to Split APKs.
-type SplitApkMetadata struct {
-	SplitId string `protobuf:"bytes,1,opt,name=split_id,json=splitId,proto3" json:"split_id,omitempty"`
-	// Indicates whether this APK is the master split of the module.
-	IsMasterSplit        bool     `protobuf:"varint,2,opt,name=is_master_split,json=isMasterSplit,proto3" json:"is_master_split,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
-}
-
-func (m *SplitApkMetadata) Reset()         { *m = SplitApkMetadata{} }
-func (m *SplitApkMetadata) String() string { return proto.CompactTextString(m) }
-func (*SplitApkMetadata) ProtoMessage()    {}
-func (*SplitApkMetadata) Descriptor() ([]byte, []int) {
-	return fileDescriptor_0dff099eb2e3dfdb, []int{8}
-}
-
-func (m *SplitApkMetadata) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_SplitApkMetadata.Unmarshal(m, b)
-}
-func (m *SplitApkMetadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_SplitApkMetadata.Marshal(b, m, deterministic)
-}
-func (m *SplitApkMetadata) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_SplitApkMetadata.Merge(m, src)
-}
-func (m *SplitApkMetadata) XXX_Size() int {
-	return xxx_messageInfo_SplitApkMetadata.Size(m)
-}
-func (m *SplitApkMetadata) XXX_DiscardUnknown() {
-	xxx_messageInfo_SplitApkMetadata.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_SplitApkMetadata proto.InternalMessageInfo
-
-func (m *SplitApkMetadata) GetSplitId() string {
-	if m != nil {
-		return m.SplitId
-	}
-	return ""
-}
-
-func (m *SplitApkMetadata) GetIsMasterSplit() bool {
-	if m != nil {
-		return m.IsMasterSplit
-	}
-	return false
-}
-
-// Holds data specific to Standalone APKs.
-type StandaloneApkMetadata struct {
-	// Names of the modules fused in this standalone APK.
-	FusedModuleName      []string `protobuf:"bytes,1,rep,name=fused_module_name,json=fusedModuleName,proto3" json:"fused_module_name,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
-}
-
-func (m *StandaloneApkMetadata) Reset()         { *m = StandaloneApkMetadata{} }
-func (m *StandaloneApkMetadata) String() string { return proto.CompactTextString(m) }
-func (*StandaloneApkMetadata) ProtoMessage()    {}
-func (*StandaloneApkMetadata) Descriptor() ([]byte, []int) {
-	return fileDescriptor_0dff099eb2e3dfdb, []int{9}
-}
-
-func (m *StandaloneApkMetadata) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_StandaloneApkMetadata.Unmarshal(m, b)
-}
-func (m *StandaloneApkMetadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_StandaloneApkMetadata.Marshal(b, m, deterministic)
-}
-func (m *StandaloneApkMetadata) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_StandaloneApkMetadata.Merge(m, src)
-}
-func (m *StandaloneApkMetadata) XXX_Size() int {
-	return xxx_messageInfo_StandaloneApkMetadata.Size(m)
-}
-func (m *StandaloneApkMetadata) XXX_DiscardUnknown() {
-	xxx_messageInfo_StandaloneApkMetadata.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_StandaloneApkMetadata proto.InternalMessageInfo
-
-func (m *StandaloneApkMetadata) GetFusedModuleName() []string {
-	if m != nil {
-		return m.FusedModuleName
-	}
-	return nil
-}
-
-// Holds data specific to system APKs.
-type SystemApkMetadata struct {
-	// Names of the modules fused in this system APK.
-	FusedModuleName []string `protobuf:"bytes,1,rep,name=fused_module_name,json=fusedModuleName,proto3" json:"fused_module_name,omitempty"`
-	// Indicates whether the APK is uncompressed system APK, stub APK or
-	// compressed system APK.
-	SystemApkType        SystemApkMetadata_SystemApkType `protobuf:"varint,2,opt,name=system_apk_type,json=systemApkType,proto3,enum=android.bundle.SystemApkMetadata_SystemApkType" json:"system_apk_type,omitempty"`
-	XXX_NoUnkeyedLiteral struct{}                        `json:"-"`
-	XXX_unrecognized     []byte                          `json:"-"`
-	XXX_sizecache        int32                           `json:"-"`
-}
-
-func (m *SystemApkMetadata) Reset()         { *m = SystemApkMetadata{} }
-func (m *SystemApkMetadata) String() string { return proto.CompactTextString(m) }
-func (*SystemApkMetadata) ProtoMessage()    {}
-func (*SystemApkMetadata) Descriptor() ([]byte, []int) {
-	return fileDescriptor_0dff099eb2e3dfdb, []int{10}
-}
-
-func (m *SystemApkMetadata) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_SystemApkMetadata.Unmarshal(m, b)
-}
-func (m *SystemApkMetadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_SystemApkMetadata.Marshal(b, m, deterministic)
-}
-func (m *SystemApkMetadata) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_SystemApkMetadata.Merge(m, src)
-}
-func (m *SystemApkMetadata) XXX_Size() int {
-	return xxx_messageInfo_SystemApkMetadata.Size(m)
-}
-func (m *SystemApkMetadata) XXX_DiscardUnknown() {
-	xxx_messageInfo_SystemApkMetadata.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_SystemApkMetadata proto.InternalMessageInfo
-
-func (m *SystemApkMetadata) GetFusedModuleName() []string {
-	if m != nil {
-		return m.FusedModuleName
-	}
-	return nil
-}
-
-func (m *SystemApkMetadata) GetSystemApkType() SystemApkMetadata_SystemApkType {
-	if m != nil {
-		return m.SystemApkType
-	}
-	return SystemApkMetadata_UNSPECIFIED_VALUE
-}
-
-// Holds data specific to APEX APKs.
-type ApexApkMetadata struct {
-	// Configuration for processing of APKs embedded in an APEX image.
-	ApexEmbeddedApkConfig []*ApexEmbeddedApkConfig `protobuf:"bytes,1,rep,name=apex_embedded_apk_config,json=apexEmbeddedApkConfig,proto3" json:"apex_embedded_apk_config,omitempty"`
-	XXX_NoUnkeyedLiteral  struct{}                 `json:"-"`
-	XXX_unrecognized      []byte                   `json:"-"`
-	XXX_sizecache         int32                    `json:"-"`
-}
-
-func (m *ApexApkMetadata) Reset()         { *m = ApexApkMetadata{} }
-func (m *ApexApkMetadata) String() string { return proto.CompactTextString(m) }
-func (*ApexApkMetadata) ProtoMessage()    {}
-func (*ApexApkMetadata) Descriptor() ([]byte, []int) {
-	return fileDescriptor_0dff099eb2e3dfdb, []int{11}
-}
-
-func (m *ApexApkMetadata) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_ApexApkMetadata.Unmarshal(m, b)
-}
-func (m *ApexApkMetadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_ApexApkMetadata.Marshal(b, m, deterministic)
-}
-func (m *ApexApkMetadata) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_ApexApkMetadata.Merge(m, src)
-}
-func (m *ApexApkMetadata) XXX_Size() int {
-	return xxx_messageInfo_ApexApkMetadata.Size(m)
-}
-func (m *ApexApkMetadata) XXX_DiscardUnknown() {
-	xxx_messageInfo_ApexApkMetadata.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_ApexApkMetadata proto.InternalMessageInfo
-
-func (m *ApexApkMetadata) GetApexEmbeddedApkConfig() []*ApexEmbeddedApkConfig {
-	if m != nil {
-		return m.ApexEmbeddedApkConfig
-	}
-	return nil
-}
-
-type LocalTestingInfo struct {
-	// Indicates if the bundle is built in local testing mode.
-	Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
-	// The local testing path, as specified in the base manifest.
-	// This refers to the relative path on the external directory of the app where
-	// APKs will be pushed for local testing.
-	// Set only if local testing is enabled.
-	LocalTestingPath     string   `protobuf:"bytes,2,opt,name=local_testing_path,json=localTestingPath,proto3" json:"local_testing_path,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
-}
-
-func (m *LocalTestingInfo) Reset()         { *m = LocalTestingInfo{} }
-func (m *LocalTestingInfo) String() string { return proto.CompactTextString(m) }
-func (*LocalTestingInfo) ProtoMessage()    {}
-func (*LocalTestingInfo) Descriptor() ([]byte, []int) {
-	return fileDescriptor_0dff099eb2e3dfdb, []int{12}
-}
-
-func (m *LocalTestingInfo) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_LocalTestingInfo.Unmarshal(m, b)
-}
-func (m *LocalTestingInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_LocalTestingInfo.Marshal(b, m, deterministic)
-}
-func (m *LocalTestingInfo) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_LocalTestingInfo.Merge(m, src)
-}
-func (m *LocalTestingInfo) XXX_Size() int {
-	return xxx_messageInfo_LocalTestingInfo.Size(m)
-}
-func (m *LocalTestingInfo) XXX_DiscardUnknown() {
-	xxx_messageInfo_LocalTestingInfo.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_LocalTestingInfo proto.InternalMessageInfo
-
-func (m *LocalTestingInfo) GetEnabled() bool {
-	if m != nil {
-		return m.Enabled
-	}
-	return false
-}
-
-func (m *LocalTestingInfo) GetLocalTestingPath() string {
-	if m != nil {
-		return m.LocalTestingPath
-	}
-	return ""
-}
-
-func init() {
-	proto.RegisterEnum("android.bundle.DeliveryType", DeliveryType_name, DeliveryType_value)
-	proto.RegisterEnum("android.bundle.SystemApkMetadata_SystemApkType", SystemApkMetadata_SystemApkType_name, SystemApkMetadata_SystemApkType_value)
-	proto.RegisterType((*BuildApksResult)(nil), "android.bundle.BuildApksResult")
-	proto.RegisterType((*Variant)(nil), "android.bundle.Variant")
-	proto.RegisterType((*ApkSet)(nil), "android.bundle.ApkSet")
-	proto.RegisterType((*ModuleMetadata)(nil), "android.bundle.ModuleMetadata")
-	proto.RegisterType((*AssetSliceSet)(nil), "android.bundle.AssetSliceSet")
-	proto.RegisterType((*AssetModuleMetadata)(nil), "android.bundle.AssetModuleMetadata")
-	proto.RegisterType((*InstantMetadata)(nil), "android.bundle.InstantMetadata")
-	proto.RegisterType((*ApkDescription)(nil), "android.bundle.ApkDescription")
-	proto.RegisterType((*SplitApkMetadata)(nil), "android.bundle.SplitApkMetadata")
-	proto.RegisterType((*StandaloneApkMetadata)(nil), "android.bundle.StandaloneApkMetadata")
-	proto.RegisterType((*SystemApkMetadata)(nil), "android.bundle.SystemApkMetadata")
-	proto.RegisterType((*ApexApkMetadata)(nil), "android.bundle.ApexApkMetadata")
-	proto.RegisterType((*LocalTestingInfo)(nil), "android.bundle.LocalTestingInfo")
-}
-
-func init() {
-	proto.RegisterFile("commands.proto", fileDescriptor_0dff099eb2e3dfdb)
-}
-
-var fileDescriptor_0dff099eb2e3dfdb = []byte{
-	// 1104 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0xcf, 0x6f, 0xe2, 0x46,
-	0x14, 0x5e, 0x03, 0x0b, 0xe1, 0x05, 0xb0, 0x33, 0x1b, 0xba, 0xde, 0x68, 0x77, 0xcb, 0xba, 0x4a,
-	0x85, 0xa2, 0x2a, 0xab, 0xa6, 0x3d, 0xad, 0xd4, 0x4a, 0x10, 0x9c, 0x96, 0x2d, 0x90, 0xc8, 0x26,
-	0x89, 0x92, 0x4a, 0x1d, 0x4d, 0x98, 0x49, 0xd6, 0xc2, 0xbf, 0xca, 0x98, 0x28, 0xf9, 0x57, 0x7a,
-	0xa9, 0x7a, 0xec, 0xb1, 0xd7, 0xfe, 0x51, 0x3d, 0xf5, 0xde, 0xca, 0x63, 0x03, 0xb6, 0xb1, 0xd4,
-	0x64, 0xd5, 0x13, 0x7e, 0x6f, 0xbe, 0xf9, 0xe6, 0xbd, 0xf7, 0xbd, 0x79, 0x0c, 0x34, 0x26, 0x9e,
-	0xe3, 0x10, 0x97, 0xf2, 0x7d, 0x7f, 0xe6, 0x05, 0x1e, 0x6a, 0x10, 0x97, 0xce, 0x3c, 0x8b, 0xee,
-	0x5f, 0xcd, 0x5d, 0x6a, 0xb3, 0x9d, 0xda, 0xc4, 0x73, 0xaf, 0xad, 0x9b, 0x68, 0x75, 0x47, 0x0e,
-	0xc8, 0xec, 0x86, 0x05, 0x96, 0x1b, 0x3b, 0xb4, 0x3f, 0x0b, 0x20, 0x77, 0xe7, 0x96, 0x4d, 0x3b,
-	0xfe, 0x94, 0x1b, 0x8c, 0xcf, 0xed, 0x00, 0xbd, 0x81, 0x9a, 0x4f, 0x26, 0x53, 0x72, 0xc3, 0xb0,
-	0x4b, 0x1c, 0xa6, 0x96, 0x5a, 0x52, 0xbb, 0x6a, 0x6c, 0xc6, 0xbe, 0x11, 0x71, 0x18, 0xfa, 0x12,
-	0x2a, 0xb7, 0x64, 0x66, 0x11, 0x37, 0x50, 0xa5, 0x56, 0xb1, 0xbd, 0x79, 0xf0, 0x7c, 0x3f, 0x7d,
-	0xee, 0xfe, 0x59, 0xb4, 0x6c, 0x2c, 0x70, 0xe8, 0x1d, 0x40, 0xb4, 0x14, 0x78, 0x9e, 0xad, 0x16,
-	0x5a, 0x52, 0x7b, 0xf3, 0x60, 0x27, 0xbb, 0xab, 0xbb, 0x44, 0x18, 0x09, 0x34, 0xd2, 0x41, 0x26,
-	0x9c, 0xb3, 0x00, 0x73, 0xdb, 0x9a, 0x30, 0xcc, 0x59, 0xa0, 0x16, 0xc5, 0xb1, 0xaf, 0xb2, 0x04,
-	0x9d, 0x10, 0x66, 0x86, 0x28, 0x93, 0x05, 0x46, 0x9d, 0x24, 0x4d, 0x34, 0x02, 0x64, 0x7b, 0x13,
-	0x62, 0xe3, 0x80, 0xf1, 0xb0, 0x06, 0xd8, 0x72, 0xaf, 0x3d, 0xf5, 0xa9, 0x08, 0xa5, 0x95, 0x65,
-	0x1a, 0x84, 0xc8, 0x71, 0x04, 0xec, 0xbb, 0xd7, 0x9e, 0xa1, 0xd8, 0x19, 0x8f, 0xf6, 0x9b, 0x04,
-	0x95, 0x38, 0x4f, 0xf4, 0x2d, 0x54, 0x97, 0xb5, 0x55, 0xa5, 0x7c, 0xca, 0x18, 0x3b, 0x5e, 0xe0,
-	0x8c, 0xd5, 0x16, 0xf4, 0x16, 0x2a, 0xc4, 0x9f, 0x8a, 0xd4, 0x0a, 0x22, 0xb5, 0x4f, 0xd6, 0x52,
-	0xf3, 0xa7, 0x61, 0x4e, 0x65, 0x22, 0x7e, 0xd1, 0x2e, 0x34, 0xe2, 0xd2, 0x62, 0x77, 0xee, 0x5c,
-	0xb1, 0x99, 0x5a, 0x6c, 0x49, 0xed, 0xba, 0x51, 0x8f, 0xbd, 0x23, 0xe1, 0xd4, 0x7e, 0x91, 0xa0,
-	0x1c, 0xed, 0x44, 0xdf, 0x81, 0xec, 0x78, 0x74, 0x6e, 0x33, 0xec, 0xb0, 0x80, 0x50, 0x12, 0x90,
-	0x38, 0xd0, 0xd7, 0xd9, 0xa3, 0x86, 0x02, 0x36, 0x8c, 0x51, 0x46, 0xc3, 0x49, 0xd9, 0x21, 0x51,
-	0x18, 0x2b, 0x65, 0x7c, 0x32, 0xb3, 0xfc, 0xc0, 0xf2, 0xdc, 0x38, 0xe6, 0xd7, 0x39, 0x31, 0xf7,
-	0x56, 0x28, 0xa3, 0x41, 0x52, 0xb6, 0xf6, 0x6b, 0x01, 0x1a, 0xe9, 0xb3, 0x10, 0x82, 0x92, 0x68,
-	0x3a, 0x49, 0x34, 0x9d, 0xf8, 0x46, 0x1d, 0xa8, 0x53, 0x66, 0x5b, 0xb7, 0x6c, 0x76, 0x8f, 0x83,
-	0x7b, 0x9f, 0xa9, 0xe5, 0x96, 0xd4, 0x6e, 0x1c, 0xbc, 0xcc, 0x9e, 0xd6, 0x8b, 0x41, 0xe3, 0x7b,
-	0x9f, 0x19, 0x35, 0x9a, 0xb0, 0xd0, 0x2b, 0x00, 0x8b, 0x63, 0xcb, 0xe5, 0x41, 0xd8, 0xb3, 0x61,
-	0xa5, 0x36, 0x8c, 0xaa, 0xc5, 0xfb, 0x91, 0x03, 0x69, 0x50, 0xa3, 0xcc, 0x67, 0x2e, 0x65, 0xee,
-	0xc4, 0x62, 0x5c, 0x2d, 0xb5, 0x8a, 0xed, 0xaa, 0x91, 0xf2, 0xa1, 0x6f, 0x92, 0x0a, 0x47, 0x4d,
-	0xf3, 0x69, 0x7e, 0xe1, 0x72, 0x05, 0xfe, 0x1a, 0xb6, 0x3d, 0x17, 0x53, 0x16, 0x5e, 0x56, 0x4c,
-	0x99, 0x3f, 0x63, 0x13, 0x12, 0x30, 0x2a, 0x6e, 0xc2, 0x46, 0xb7, 0xa0, 0x4a, 0x06, 0xf2, 0xdc,
-	0x9e, 0x58, 0xee, 0x2d, 0x57, 0xb5, 0x3f, 0x24, 0xa8, 0xa7, 0x7a, 0x1a, 0x9d, 0x43, 0x33, 0xba,
-	0x0b, 0xf9, 0x5a, 0x7e, 0x96, 0x7b, 0x23, 0x32, 0x82, 0x3e, 0x23, 0xeb, 0xce, 0xff, 0x4f, 0xd5,
-	0xbf, 0x24, 0x78, 0x96, 0x73, 0xea, 0xc3, 0xa4, 0x2d, 0x3d, 0x5a, 0xda, 0xf7, 0xa0, 0xc4, 0xba,
-	0xae, 0x6a, 0x51, 0xcc, 0x97, 0x27, 0x96, 0x7b, 0x59, 0x07, 0xd9, 0x4a, 0x3b, 0x3e, 0x52, 0xa4,
-	0xdf, 0x25, 0x90, 0x33, 0xd4, 0x99, 0x86, 0x93, 0xb2, 0x0d, 0xb7, 0x96, 0x77, 0xf1, 0xd1, 0x79,
-	0x7f, 0x5c, 0xac, 0xff, 0x94, 0xa0, 0x91, 0xd6, 0x0f, 0xbd, 0x5b, 0x1f, 0x5d, 0x2f, 0x73, 0x24,
-	0xcf, 0xed, 0x6a, 0x04, 0x25, 0x9f, 0x04, 0x1f, 0xc4, 0xa1, 0x55, 0x43, 0x7c, 0xa3, 0x13, 0x40,
-	0xdc, 0xb7, 0xad, 0x00, 0x87, 0xed, 0x94, 0x91, 0x64, 0x6d, 0x26, 0x9a, 0x21, 0xb2, 0xe3, 0x4f,
-	0x17, 0x85, 0xfb, 0xfe, 0x89, 0xa1, 0xf0, 0x8c, 0x0f, 0x61, 0x78, 0x1e, 0x96, 0x8d, 0x12, 0xdb,
-	0x73, 0x59, 0x9a, 0xb6, 0x24, 0x68, 0x77, 0xd7, 0x68, 0x97, 0xf0, 0x34, 0x77, 0x93, 0xe7, 0x2d,
-	0xa0, 0x31, 0x6c, 0x2f, 0x7a, 0x28, 0xc5, 0xfe, 0xf4, 0xc1, 0x41, 0xa3, 0x78, 0x7f, 0x92, 0xd5,
-	0x84, 0x67, 0xfc, 0x9e, 0x07, 0xcc, 0x49, 0x93, 0x96, 0x05, 0xe9, 0x9b, 0x35, 0x52, 0x01, 0x4d,
-	0xb3, 0x6e, 0xf1, 0xac, 0x33, 0x0c, 0x35, 0xf9, 0x5f, 0xb8, 0x64, 0xad, 0x3c, 0x3c, 0xd4, 0xd5,
-	0xbf, 0xe2, 0x92, 0x75, 0x08, 0x5b, 0xc4, 0x67, 0x77, 0xe9, 0x40, 0x37, 0xf2, 0x6f, 0x51, 0xc7,
-	0x67, 0x77, 0x69, 0x46, 0x99, 0xa4, 0x5d, 0xdd, 0x1d, 0x50, 0x93, 0x4c, 0xd8, 0x73, 0x99, 0x77,
-	0x8d, 0x6f, 0x89, 0x3d, 0x67, 0xda, 0x29, 0x28, 0xd9, 0xa0, 0xd0, 0x0b, 0xd8, 0x88, 0x5a, 0xc6,
-	0xa2, 0xf1, 0x78, 0xa8, 0x08, 0xbb, 0x4f, 0xd1, 0xe7, 0x20, 0x5b, 0x1c, 0x3b, 0x84, 0x07, 0x6c,
-	0x86, 0x85, 0x33, 0xea, 0x70, 0xa3, 0x6e, 0xf1, 0xa1, 0xf0, 0x0a, 0x36, 0xad, 0x0f, 0xcd, 0x5c,
-	0xd1, 0xd1, 0x1e, 0x6c, 0x5d, 0xcf, 0x39, 0xa3, 0x8b, 0x81, 0x19, 0xcf, 0xa0, 0x70, 0xc0, 0xcb,
-	0x62, 0x21, 0x1a, 0x53, 0xe1, 0xbb, 0xe6, 0x7d, 0x69, 0xa3, 0xa0, 0x14, 0xb5, 0xbf, 0x25, 0xd8,
-	0x5a, 0x53, 0xe3, 0x31, 0x3c, 0xe8, 0x1c, 0xe4, 0x84, 0xf2, 0xe2, 0x82, 0x17, 0xc4, 0x05, 0x7f,
-	0xfb, 0x9f, 0xaa, 0xaf, 0x3c, 0xe2, 0xce, 0xd7, 0x79, 0xd2, 0xd4, 0x2e, 0xa1, 0x9e, 0x5a, 0x47,
-	0x4d, 0xd8, 0x3a, 0x1d, 0x99, 0x27, 0xfa, 0x61, 0xff, 0xa8, 0xaf, 0xf7, 0xf0, 0x59, 0x67, 0x70,
-	0xaa, 0x2b, 0x4f, 0x10, 0x40, 0xd9, 0xbc, 0x30, 0xc7, 0xfa, 0x50, 0x91, 0x90, 0x0c, 0x9b, 0xd1,
-	0x37, 0x36, 0xc7, 0xa7, 0x5d, 0xa5, 0x10, 0xee, 0x89, 0x1d, 0x87, 0xc7, 0xc3, 0x13, 0x43, 0x37,
-	0x4d, 0xbd, 0xa7, 0x14, 0xb5, 0x9f, 0x41, 0xce, 0x48, 0x8b, 0x7e, 0x0a, 0x75, 0x64, 0x77, 0x98,
-	0x39, 0x57, 0x8c, 0x52, 0x46, 0x45, 0x3a, 0xd1, 0x8b, 0x32, 0x7e, 0xf8, 0xed, 0xe6, 0x75, 0x87,
-	0x1e, 0xc3, 0x3b, 0xfe, 0xf4, 0x50, 0x80, 0x8d, 0x26, 0xc9, 0x73, 0x6b, 0x97, 0xa0, 0x64, 0xdf,
-	0x59, 0x48, 0x85, 0x0a, 0x73, 0xc9, 0x95, 0xcd, 0x68, 0x3c, 0x36, 0x17, 0x26, 0xfa, 0x22, 0xfb,
-	0x7e, 0x4b, 0x8c, 0x9e, 0xd4, 0xeb, 0xec, 0x84, 0x04, 0x1f, 0xf6, 0x7e, 0x84, 0x5a, 0x72, 0x7a,
-	0xa2, 0x17, 0xd0, 0x3c, 0x1d, 0xfd, 0x30, 0x3a, 0x3e, 0x1f, 0xe1, 0x9e, 0x3e, 0xe8, 0x9f, 0xe9,
-	0xc6, 0x05, 0x1e, 0x5f, 0x9c, 0x84, 0xd5, 0x52, 0xa0, 0xd6, 0x1f, 0x99, 0xe3, 0xce, 0x60, 0x80,
-	0xc7, 0xfd, 0xa1, 0xae, 0x48, 0xa8, 0x0e, 0xd5, 0xe3, 0x10, 0x37, 0xec, 0x8c, 0x7a, 0x4a, 0x21,
-	0x2c, 0xe1, 0x51, 0xc7, 0x1c, 0xe3, 0xa3, 0xe3, 0xc1, 0xe0, 0xf8, 0x5c, 0x29, 0x76, 0xf7, 0x00,
-	0x4d, 0x3c, 0x27, 0x93, 0xfb, 0xe5, 0x76, 0x6c, 0xe3, 0xc8, 0xc6, 0xe2, 0x8d, 0x7d, 0x55, 0x16,
-	0x3f, 0x5f, 0xfd, 0x1b, 0x00, 0x00, 0xff, 0xff, 0xb1, 0xe5, 0xcb, 0x87, 0xab, 0x0b, 0x00, 0x00,
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_commands_proto_rawDesc,
+			NumEnums:      2,
+			NumMessages:   13,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_commands_proto_goTypes,
+		DependencyIndexes: file_commands_proto_depIdxs,
+		EnumInfos:         file_commands_proto_enumTypes,
+		MessageInfos:      file_commands_proto_msgTypes,
+	}.Build()
+	File_commands_proto = out.File
+	file_commands_proto_rawDesc = nil
+	file_commands_proto_goTypes = nil
+	file_commands_proto_depIdxs = nil
 }
diff --git a/cmd/extract_apks/bundle_proto/commands.proto b/cmd/extract_apks/bundle_proto/commands.proto
index b36340b..a24e26d 100644
--- a/cmd/extract_apks/bundle_proto/commands.proto
+++ b/cmd/extract_apks/bundle_proto/commands.proto
@@ -9,7 +9,7 @@
 import "config.proto";
 import "targeting.proto";
 
-option go_package = "android_bundle_proto";
+option go_package = "android/soong/cmd/extract_apks/bundle_proto";
 option java_package = "com.android.bundle";
 
 // Describes the output of the "build-apks" command.
diff --git a/cmd/extract_apks/bundle_proto/config.pb.go b/cmd/extract_apks/bundle_proto/config.pb.go
index a28147a..e358c4b 100644
--- a/cmd/extract_apks/bundle_proto/config.pb.go
+++ b/cmd/extract_apks/bundle_proto/config.pb.go
@@ -1,24 +1,29 @@
+// Messages describing APK Set's table of contents (toc.pb entry).
+// Please be advised that the ultimate source is at
+// https://github.com/google/bundletool/tree/master/src/main/proto
+// so you have been warned.
+
 // Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.26.0
+// 	protoc        v3.9.1
 // source: config.proto
 
-package android_bundle_proto
+package bundle_proto
 
 import (
-	fmt "fmt"
-	proto "github.com/golang/protobuf/proto"
-	math "math"
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
 )
 
-// Reference imports to suppress errors if they are not otherwise used.
-var _ = proto.Marshal
-var _ = fmt.Errorf
-var _ = math.Inf
-
-// This is a compile-time assertion to ensure that this generated file
-// is compatible with the proto package it is being compiled against.
-// A compilation error at this line likely means your copy of the
-// proto package needs to be updated.
-const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
 
 type BundleConfig_BundleType int32
 
@@ -28,24 +33,45 @@
 	BundleConfig_ASSET_ONLY BundleConfig_BundleType = 2
 )
 
-var BundleConfig_BundleType_name = map[int32]string{
-	0: "REGULAR",
-	1: "APEX",
-	2: "ASSET_ONLY",
-}
+// Enum value maps for BundleConfig_BundleType.
+var (
+	BundleConfig_BundleType_name = map[int32]string{
+		0: "REGULAR",
+		1: "APEX",
+		2: "ASSET_ONLY",
+	}
+	BundleConfig_BundleType_value = map[string]int32{
+		"REGULAR":    0,
+		"APEX":       1,
+		"ASSET_ONLY": 2,
+	}
+)
 
-var BundleConfig_BundleType_value = map[string]int32{
-	"REGULAR":    0,
-	"APEX":       1,
-	"ASSET_ONLY": 2,
+func (x BundleConfig_BundleType) Enum() *BundleConfig_BundleType {
+	p := new(BundleConfig_BundleType)
+	*p = x
+	return p
 }
 
 func (x BundleConfig_BundleType) String() string {
-	return proto.EnumName(BundleConfig_BundleType_name, int32(x))
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
 }
 
+func (BundleConfig_BundleType) Descriptor() protoreflect.EnumDescriptor {
+	return file_config_proto_enumTypes[0].Descriptor()
+}
+
+func (BundleConfig_BundleType) Type() protoreflect.EnumType {
+	return &file_config_proto_enumTypes[0]
+}
+
+func (x BundleConfig_BundleType) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use BundleConfig_BundleType.Descriptor instead.
 func (BundleConfig_BundleType) EnumDescriptor() ([]byte, []int) {
-	return fileDescriptor_3eaf2c85e69e9ea4, []int{0, 0}
+	return file_config_proto_rawDescGZIP(), []int{0, 0}
 }
 
 type SplitDimension_Value int32
@@ -57,36 +83,61 @@
 	SplitDimension_LANGUAGE                   SplitDimension_Value = 3
 	SplitDimension_TEXTURE_COMPRESSION_FORMAT SplitDimension_Value = 4
 	// BEGIN-INTERNAL
-	SplitDimension_GRAPHICS_API SplitDimension_Value = 5
+	SplitDimension_GRAPHICS_API SplitDimension_Value = 5 // END-INTERNAL
 )
 
-var SplitDimension_Value_name = map[int32]string{
-	0: "UNSPECIFIED_VALUE",
-	1: "ABI",
-	2: "SCREEN_DENSITY",
-	3: "LANGUAGE",
-	4: "TEXTURE_COMPRESSION_FORMAT",
-	5: "GRAPHICS_API",
-}
+// Enum value maps for SplitDimension_Value.
+var (
+	SplitDimension_Value_name = map[int32]string{
+		0: "UNSPECIFIED_VALUE",
+		1: "ABI",
+		2: "SCREEN_DENSITY",
+		3: "LANGUAGE",
+		4: "TEXTURE_COMPRESSION_FORMAT",
+		5: "GRAPHICS_API",
+	}
+	SplitDimension_Value_value = map[string]int32{
+		"UNSPECIFIED_VALUE":          0,
+		"ABI":                        1,
+		"SCREEN_DENSITY":             2,
+		"LANGUAGE":                   3,
+		"TEXTURE_COMPRESSION_FORMAT": 4,
+		"GRAPHICS_API":               5,
+	}
+)
 
-var SplitDimension_Value_value = map[string]int32{
-	"UNSPECIFIED_VALUE":          0,
-	"ABI":                        1,
-	"SCREEN_DENSITY":             2,
-	"LANGUAGE":                   3,
-	"TEXTURE_COMPRESSION_FORMAT": 4,
-	"GRAPHICS_API":               5,
+func (x SplitDimension_Value) Enum() *SplitDimension_Value {
+	p := new(SplitDimension_Value)
+	*p = x
+	return p
 }
 
 func (x SplitDimension_Value) String() string {
-	return proto.EnumName(SplitDimension_Value_name, int32(x))
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
 }
 
+func (SplitDimension_Value) Descriptor() protoreflect.EnumDescriptor {
+	return file_config_proto_enumTypes[1].Descriptor()
+}
+
+func (SplitDimension_Value) Type() protoreflect.EnumType {
+	return &file_config_proto_enumTypes[1]
+}
+
+func (x SplitDimension_Value) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use SplitDimension_Value.Descriptor instead.
 func (SplitDimension_Value) EnumDescriptor() ([]byte, []int) {
-	return fileDescriptor_3eaf2c85e69e9ea4, []int{9, 0}
+	return file_config_proto_rawDescGZIP(), []int{9, 0}
 }
 
 type BundleConfig struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	Bundletool    *Bundletool    `protobuf:"bytes,1,opt,name=bundletool,proto3" json:"bundletool,omitempty"`
 	Optimizations *Optimizations `protobuf:"bytes,2,opt,name=optimizations,proto3" json:"optimizations,omitempty"`
 	Compression   *Compression   `protobuf:"bytes,3,opt,name=compression,proto3" json:"compression,omitempty"`
@@ -97,226 +148,258 @@
 	UnsignedEmbeddedApkConfig []*UnsignedEmbeddedApkConfig `protobuf:"bytes,6,rep,name=unsigned_embedded_apk_config,json=unsignedEmbeddedApkConfig,proto3" json:"unsigned_embedded_apk_config,omitempty"`
 	AssetModulesConfig        *AssetModulesConfig          `protobuf:"bytes,7,opt,name=asset_modules_config,json=assetModulesConfig,proto3" json:"asset_modules_config,omitempty"`
 	Type                      BundleConfig_BundleType      `protobuf:"varint,8,opt,name=type,proto3,enum=android.bundle.BundleConfig_BundleType" json:"type,omitempty"`
-	XXX_NoUnkeyedLiteral      struct{}                     `json:"-"`
-	XXX_unrecognized          []byte                       `json:"-"`
-	XXX_sizecache             int32                        `json:"-"`
 }
 
-func (m *BundleConfig) Reset()         { *m = BundleConfig{} }
-func (m *BundleConfig) String() string { return proto.CompactTextString(m) }
-func (*BundleConfig) ProtoMessage()    {}
+func (x *BundleConfig) Reset() {
+	*x = BundleConfig{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_config_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *BundleConfig) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*BundleConfig) ProtoMessage() {}
+
+func (x *BundleConfig) ProtoReflect() protoreflect.Message {
+	mi := &file_config_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use BundleConfig.ProtoReflect.Descriptor instead.
 func (*BundleConfig) Descriptor() ([]byte, []int) {
-	return fileDescriptor_3eaf2c85e69e9ea4, []int{0}
+	return file_config_proto_rawDescGZIP(), []int{0}
 }
 
-func (m *BundleConfig) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_BundleConfig.Unmarshal(m, b)
-}
-func (m *BundleConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_BundleConfig.Marshal(b, m, deterministic)
-}
-func (m *BundleConfig) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_BundleConfig.Merge(m, src)
-}
-func (m *BundleConfig) XXX_Size() int {
-	return xxx_messageInfo_BundleConfig.Size(m)
-}
-func (m *BundleConfig) XXX_DiscardUnknown() {
-	xxx_messageInfo_BundleConfig.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_BundleConfig proto.InternalMessageInfo
-
-func (m *BundleConfig) GetBundletool() *Bundletool {
-	if m != nil {
-		return m.Bundletool
+func (x *BundleConfig) GetBundletool() *Bundletool {
+	if x != nil {
+		return x.Bundletool
 	}
 	return nil
 }
 
-func (m *BundleConfig) GetOptimizations() *Optimizations {
-	if m != nil {
-		return m.Optimizations
+func (x *BundleConfig) GetOptimizations() *Optimizations {
+	if x != nil {
+		return x.Optimizations
 	}
 	return nil
 }
 
-func (m *BundleConfig) GetCompression() *Compression {
-	if m != nil {
-		return m.Compression
+func (x *BundleConfig) GetCompression() *Compression {
+	if x != nil {
+		return x.Compression
 	}
 	return nil
 }
 
-func (m *BundleConfig) GetMasterResources() *MasterResources {
-	if m != nil {
-		return m.MasterResources
+func (x *BundleConfig) GetMasterResources() *MasterResources {
+	if x != nil {
+		return x.MasterResources
 	}
 	return nil
 }
 
-func (m *BundleConfig) GetApexConfig() *ApexConfig {
-	if m != nil {
-		return m.ApexConfig
+func (x *BundleConfig) GetApexConfig() *ApexConfig {
+	if x != nil {
+		return x.ApexConfig
 	}
 	return nil
 }
 
-func (m *BundleConfig) GetUnsignedEmbeddedApkConfig() []*UnsignedEmbeddedApkConfig {
-	if m != nil {
-		return m.UnsignedEmbeddedApkConfig
+func (x *BundleConfig) GetUnsignedEmbeddedApkConfig() []*UnsignedEmbeddedApkConfig {
+	if x != nil {
+		return x.UnsignedEmbeddedApkConfig
 	}
 	return nil
 }
 
-func (m *BundleConfig) GetAssetModulesConfig() *AssetModulesConfig {
-	if m != nil {
-		return m.AssetModulesConfig
+func (x *BundleConfig) GetAssetModulesConfig() *AssetModulesConfig {
+	if x != nil {
+		return x.AssetModulesConfig
 	}
 	return nil
 }
 
-func (m *BundleConfig) GetType() BundleConfig_BundleType {
-	if m != nil {
-		return m.Type
+func (x *BundleConfig) GetType() BundleConfig_BundleType {
+	if x != nil {
+		return x.Type
 	}
 	return BundleConfig_REGULAR
 }
 
 type Bundletool struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	// Version of BundleTool used to build the Bundle.
-	Version              string   `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
+	Version string `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"`
 }
 
-func (m *Bundletool) Reset()         { *m = Bundletool{} }
-func (m *Bundletool) String() string { return proto.CompactTextString(m) }
-func (*Bundletool) ProtoMessage()    {}
+func (x *Bundletool) Reset() {
+	*x = Bundletool{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_config_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Bundletool) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Bundletool) ProtoMessage() {}
+
+func (x *Bundletool) ProtoReflect() protoreflect.Message {
+	mi := &file_config_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Bundletool.ProtoReflect.Descriptor instead.
 func (*Bundletool) Descriptor() ([]byte, []int) {
-	return fileDescriptor_3eaf2c85e69e9ea4, []int{1}
+	return file_config_proto_rawDescGZIP(), []int{1}
 }
 
-func (m *Bundletool) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_Bundletool.Unmarshal(m, b)
-}
-func (m *Bundletool) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_Bundletool.Marshal(b, m, deterministic)
-}
-func (m *Bundletool) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_Bundletool.Merge(m, src)
-}
-func (m *Bundletool) XXX_Size() int {
-	return xxx_messageInfo_Bundletool.Size(m)
-}
-func (m *Bundletool) XXX_DiscardUnknown() {
-	xxx_messageInfo_Bundletool.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_Bundletool proto.InternalMessageInfo
-
-func (m *Bundletool) GetVersion() string {
-	if m != nil {
-		return m.Version
+func (x *Bundletool) GetVersion() string {
+	if x != nil {
+		return x.Version
 	}
 	return ""
 }
 
 type Compression struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	// Glob matching the list of files to leave uncompressed in the APKs.
 	// The matching is done against the path of files in the APK, thus excluding
 	// the name of the modules, and using forward slash ("/") as a name separator.
 	// Examples: "res/raw/**", "assets/**/*.uncompressed", etc.
-	UncompressedGlob     []string `protobuf:"bytes,1,rep,name=uncompressed_glob,json=uncompressedGlob,proto3" json:"uncompressed_glob,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
+	UncompressedGlob []string `protobuf:"bytes,1,rep,name=uncompressed_glob,json=uncompressedGlob,proto3" json:"uncompressed_glob,omitempty"`
 }
 
-func (m *Compression) Reset()         { *m = Compression{} }
-func (m *Compression) String() string { return proto.CompactTextString(m) }
-func (*Compression) ProtoMessage()    {}
+func (x *Compression) Reset() {
+	*x = Compression{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_config_proto_msgTypes[2]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Compression) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Compression) ProtoMessage() {}
+
+func (x *Compression) ProtoReflect() protoreflect.Message {
+	mi := &file_config_proto_msgTypes[2]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Compression.ProtoReflect.Descriptor instead.
 func (*Compression) Descriptor() ([]byte, []int) {
-	return fileDescriptor_3eaf2c85e69e9ea4, []int{2}
+	return file_config_proto_rawDescGZIP(), []int{2}
 }
 
-func (m *Compression) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_Compression.Unmarshal(m, b)
-}
-func (m *Compression) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_Compression.Marshal(b, m, deterministic)
-}
-func (m *Compression) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_Compression.Merge(m, src)
-}
-func (m *Compression) XXX_Size() int {
-	return xxx_messageInfo_Compression.Size(m)
-}
-func (m *Compression) XXX_DiscardUnknown() {
-	xxx_messageInfo_Compression.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_Compression proto.InternalMessageInfo
-
-func (m *Compression) GetUncompressedGlob() []string {
-	if m != nil {
-		return m.UncompressedGlob
+func (x *Compression) GetUncompressedGlob() []string {
+	if x != nil {
+		return x.UncompressedGlob
 	}
 	return nil
 }
 
 // Resources to keep in the master split.
 type MasterResources struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	// Resource IDs to be kept in master split.
 	ResourceIds []int32 `protobuf:"varint,1,rep,packed,name=resource_ids,json=resourceIds,proto3" json:"resource_ids,omitempty"`
 	// Resource names to be kept in master split.
-	ResourceNames        []string `protobuf:"bytes,2,rep,name=resource_names,json=resourceNames,proto3" json:"resource_names,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
+	ResourceNames []string `protobuf:"bytes,2,rep,name=resource_names,json=resourceNames,proto3" json:"resource_names,omitempty"`
 }
 
-func (m *MasterResources) Reset()         { *m = MasterResources{} }
-func (m *MasterResources) String() string { return proto.CompactTextString(m) }
-func (*MasterResources) ProtoMessage()    {}
+func (x *MasterResources) Reset() {
+	*x = MasterResources{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_config_proto_msgTypes[3]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *MasterResources) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*MasterResources) ProtoMessage() {}
+
+func (x *MasterResources) ProtoReflect() protoreflect.Message {
+	mi := &file_config_proto_msgTypes[3]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use MasterResources.ProtoReflect.Descriptor instead.
 func (*MasterResources) Descriptor() ([]byte, []int) {
-	return fileDescriptor_3eaf2c85e69e9ea4, []int{3}
+	return file_config_proto_rawDescGZIP(), []int{3}
 }
 
-func (m *MasterResources) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_MasterResources.Unmarshal(m, b)
-}
-func (m *MasterResources) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_MasterResources.Marshal(b, m, deterministic)
-}
-func (m *MasterResources) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_MasterResources.Merge(m, src)
-}
-func (m *MasterResources) XXX_Size() int {
-	return xxx_messageInfo_MasterResources.Size(m)
-}
-func (m *MasterResources) XXX_DiscardUnknown() {
-	xxx_messageInfo_MasterResources.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_MasterResources proto.InternalMessageInfo
-
-func (m *MasterResources) GetResourceIds() []int32 {
-	if m != nil {
-		return m.ResourceIds
+func (x *MasterResources) GetResourceIds() []int32 {
+	if x != nil {
+		return x.ResourceIds
 	}
 	return nil
 }
 
-func (m *MasterResources) GetResourceNames() []string {
-	if m != nil {
-		return m.ResourceNames
+func (x *MasterResources) GetResourceNames() []string {
+	if x != nil {
+		return x.ResourceNames
 	}
 	return nil
 }
 
 type Optimizations struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	SplitsConfig *SplitsConfig `protobuf:"bytes,1,opt,name=splits_config,json=splitsConfig,proto3" json:"splits_config,omitempty"`
 	// This is for uncompressing native libraries on M+ devices (L+ devices on
 	// instant apps).
@@ -326,293 +409,341 @@
 	// Configuration for the generation of standalone APKs.
 	// If no StandaloneConfig is set, the configuration is inherited from
 	// splits_config.
-	StandaloneConfig     *StandaloneConfig `protobuf:"bytes,4,opt,name=standalone_config,json=standaloneConfig,proto3" json:"standalone_config,omitempty"`
-	XXX_NoUnkeyedLiteral struct{}          `json:"-"`
-	XXX_unrecognized     []byte            `json:"-"`
-	XXX_sizecache        int32             `json:"-"`
+	StandaloneConfig *StandaloneConfig `protobuf:"bytes,4,opt,name=standalone_config,json=standaloneConfig,proto3" json:"standalone_config,omitempty"`
 }
 
-func (m *Optimizations) Reset()         { *m = Optimizations{} }
-func (m *Optimizations) String() string { return proto.CompactTextString(m) }
-func (*Optimizations) ProtoMessage()    {}
+func (x *Optimizations) Reset() {
+	*x = Optimizations{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_config_proto_msgTypes[4]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Optimizations) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Optimizations) ProtoMessage() {}
+
+func (x *Optimizations) ProtoReflect() protoreflect.Message {
+	mi := &file_config_proto_msgTypes[4]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Optimizations.ProtoReflect.Descriptor instead.
 func (*Optimizations) Descriptor() ([]byte, []int) {
-	return fileDescriptor_3eaf2c85e69e9ea4, []int{4}
+	return file_config_proto_rawDescGZIP(), []int{4}
 }
 
-func (m *Optimizations) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_Optimizations.Unmarshal(m, b)
-}
-func (m *Optimizations) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_Optimizations.Marshal(b, m, deterministic)
-}
-func (m *Optimizations) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_Optimizations.Merge(m, src)
-}
-func (m *Optimizations) XXX_Size() int {
-	return xxx_messageInfo_Optimizations.Size(m)
-}
-func (m *Optimizations) XXX_DiscardUnknown() {
-	xxx_messageInfo_Optimizations.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_Optimizations proto.InternalMessageInfo
-
-func (m *Optimizations) GetSplitsConfig() *SplitsConfig {
-	if m != nil {
-		return m.SplitsConfig
+func (x *Optimizations) GetSplitsConfig() *SplitsConfig {
+	if x != nil {
+		return x.SplitsConfig
 	}
 	return nil
 }
 
-func (m *Optimizations) GetUncompressNativeLibraries() *UncompressNativeLibraries {
-	if m != nil {
-		return m.UncompressNativeLibraries
+func (x *Optimizations) GetUncompressNativeLibraries() *UncompressNativeLibraries {
+	if x != nil {
+		return x.UncompressNativeLibraries
 	}
 	return nil
 }
 
-func (m *Optimizations) GetUncompressDexFiles() *UncompressDexFiles {
-	if m != nil {
-		return m.UncompressDexFiles
+func (x *Optimizations) GetUncompressDexFiles() *UncompressDexFiles {
+	if x != nil {
+		return x.UncompressDexFiles
 	}
 	return nil
 }
 
-func (m *Optimizations) GetStandaloneConfig() *StandaloneConfig {
-	if m != nil {
-		return m.StandaloneConfig
+func (x *Optimizations) GetStandaloneConfig() *StandaloneConfig {
+	if x != nil {
+		return x.StandaloneConfig
 	}
 	return nil
 }
 
 type UncompressNativeLibraries struct {
-	Enabled              bool     `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
 }
 
-func (m *UncompressNativeLibraries) Reset()         { *m = UncompressNativeLibraries{} }
-func (m *UncompressNativeLibraries) String() string { return proto.CompactTextString(m) }
-func (*UncompressNativeLibraries) ProtoMessage()    {}
+func (x *UncompressNativeLibraries) Reset() {
+	*x = UncompressNativeLibraries{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_config_proto_msgTypes[5]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *UncompressNativeLibraries) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*UncompressNativeLibraries) ProtoMessage() {}
+
+func (x *UncompressNativeLibraries) ProtoReflect() protoreflect.Message {
+	mi := &file_config_proto_msgTypes[5]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use UncompressNativeLibraries.ProtoReflect.Descriptor instead.
 func (*UncompressNativeLibraries) Descriptor() ([]byte, []int) {
-	return fileDescriptor_3eaf2c85e69e9ea4, []int{5}
+	return file_config_proto_rawDescGZIP(), []int{5}
 }
 
-func (m *UncompressNativeLibraries) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_UncompressNativeLibraries.Unmarshal(m, b)
-}
-func (m *UncompressNativeLibraries) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_UncompressNativeLibraries.Marshal(b, m, deterministic)
-}
-func (m *UncompressNativeLibraries) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_UncompressNativeLibraries.Merge(m, src)
-}
-func (m *UncompressNativeLibraries) XXX_Size() int {
-	return xxx_messageInfo_UncompressNativeLibraries.Size(m)
-}
-func (m *UncompressNativeLibraries) XXX_DiscardUnknown() {
-	xxx_messageInfo_UncompressNativeLibraries.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_UncompressNativeLibraries proto.InternalMessageInfo
-
-func (m *UncompressNativeLibraries) GetEnabled() bool {
-	if m != nil {
-		return m.Enabled
+func (x *UncompressNativeLibraries) GetEnabled() bool {
+	if x != nil {
+		return x.Enabled
 	}
 	return false
 }
 
 type UncompressDexFiles struct {
-	Enabled              bool     `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
 }
 
-func (m *UncompressDexFiles) Reset()         { *m = UncompressDexFiles{} }
-func (m *UncompressDexFiles) String() string { return proto.CompactTextString(m) }
-func (*UncompressDexFiles) ProtoMessage()    {}
+func (x *UncompressDexFiles) Reset() {
+	*x = UncompressDexFiles{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_config_proto_msgTypes[6]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *UncompressDexFiles) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*UncompressDexFiles) ProtoMessage() {}
+
+func (x *UncompressDexFiles) ProtoReflect() protoreflect.Message {
+	mi := &file_config_proto_msgTypes[6]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use UncompressDexFiles.ProtoReflect.Descriptor instead.
 func (*UncompressDexFiles) Descriptor() ([]byte, []int) {
-	return fileDescriptor_3eaf2c85e69e9ea4, []int{6}
+	return file_config_proto_rawDescGZIP(), []int{6}
 }
 
-func (m *UncompressDexFiles) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_UncompressDexFiles.Unmarshal(m, b)
-}
-func (m *UncompressDexFiles) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_UncompressDexFiles.Marshal(b, m, deterministic)
-}
-func (m *UncompressDexFiles) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_UncompressDexFiles.Merge(m, src)
-}
-func (m *UncompressDexFiles) XXX_Size() int {
-	return xxx_messageInfo_UncompressDexFiles.Size(m)
-}
-func (m *UncompressDexFiles) XXX_DiscardUnknown() {
-	xxx_messageInfo_UncompressDexFiles.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_UncompressDexFiles proto.InternalMessageInfo
-
-func (m *UncompressDexFiles) GetEnabled() bool {
-	if m != nil {
-		return m.Enabled
+func (x *UncompressDexFiles) GetEnabled() bool {
+	if x != nil {
+		return x.Enabled
 	}
 	return false
 }
 
 // Optimization configuration used to generate Split APKs.
 type SplitsConfig struct {
-	SplitDimension       []*SplitDimension `protobuf:"bytes,1,rep,name=split_dimension,json=splitDimension,proto3" json:"split_dimension,omitempty"`
-	XXX_NoUnkeyedLiteral struct{}          `json:"-"`
-	XXX_unrecognized     []byte            `json:"-"`
-	XXX_sizecache        int32             `json:"-"`
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	SplitDimension []*SplitDimension `protobuf:"bytes,1,rep,name=split_dimension,json=splitDimension,proto3" json:"split_dimension,omitempty"`
 }
 
-func (m *SplitsConfig) Reset()         { *m = SplitsConfig{} }
-func (m *SplitsConfig) String() string { return proto.CompactTextString(m) }
-func (*SplitsConfig) ProtoMessage()    {}
+func (x *SplitsConfig) Reset() {
+	*x = SplitsConfig{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_config_proto_msgTypes[7]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *SplitsConfig) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SplitsConfig) ProtoMessage() {}
+
+func (x *SplitsConfig) ProtoReflect() protoreflect.Message {
+	mi := &file_config_proto_msgTypes[7]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use SplitsConfig.ProtoReflect.Descriptor instead.
 func (*SplitsConfig) Descriptor() ([]byte, []int) {
-	return fileDescriptor_3eaf2c85e69e9ea4, []int{7}
+	return file_config_proto_rawDescGZIP(), []int{7}
 }
 
-func (m *SplitsConfig) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_SplitsConfig.Unmarshal(m, b)
-}
-func (m *SplitsConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_SplitsConfig.Marshal(b, m, deterministic)
-}
-func (m *SplitsConfig) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_SplitsConfig.Merge(m, src)
-}
-func (m *SplitsConfig) XXX_Size() int {
-	return xxx_messageInfo_SplitsConfig.Size(m)
-}
-func (m *SplitsConfig) XXX_DiscardUnknown() {
-	xxx_messageInfo_SplitsConfig.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_SplitsConfig proto.InternalMessageInfo
-
-func (m *SplitsConfig) GetSplitDimension() []*SplitDimension {
-	if m != nil {
-		return m.SplitDimension
+func (x *SplitsConfig) GetSplitDimension() []*SplitDimension {
+	if x != nil {
+		return x.SplitDimension
 	}
 	return nil
 }
 
 // Optimization configuration used to generate Standalone APKs.
 type StandaloneConfig struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	// Device targeting dimensions to shard.
 	SplitDimension []*SplitDimension `protobuf:"bytes,1,rep,name=split_dimension,json=splitDimension,proto3" json:"split_dimension,omitempty"`
 	// Whether 64 bit libraries should be stripped from Standalone APKs.
-	Strip_64BitLibraries bool     `protobuf:"varint,2,opt,name=strip_64_bit_libraries,json=strip64BitLibraries,proto3" json:"strip_64_bit_libraries,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
+	Strip_64BitLibraries bool `protobuf:"varint,2,opt,name=strip_64_bit_libraries,json=strip64BitLibraries,proto3" json:"strip_64_bit_libraries,omitempty"`
 }
 
-func (m *StandaloneConfig) Reset()         { *m = StandaloneConfig{} }
-func (m *StandaloneConfig) String() string { return proto.CompactTextString(m) }
-func (*StandaloneConfig) ProtoMessage()    {}
+func (x *StandaloneConfig) Reset() {
+	*x = StandaloneConfig{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_config_proto_msgTypes[8]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *StandaloneConfig) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*StandaloneConfig) ProtoMessage() {}
+
+func (x *StandaloneConfig) ProtoReflect() protoreflect.Message {
+	mi := &file_config_proto_msgTypes[8]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use StandaloneConfig.ProtoReflect.Descriptor instead.
 func (*StandaloneConfig) Descriptor() ([]byte, []int) {
-	return fileDescriptor_3eaf2c85e69e9ea4, []int{8}
+	return file_config_proto_rawDescGZIP(), []int{8}
 }
 
-func (m *StandaloneConfig) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_StandaloneConfig.Unmarshal(m, b)
-}
-func (m *StandaloneConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_StandaloneConfig.Marshal(b, m, deterministic)
-}
-func (m *StandaloneConfig) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_StandaloneConfig.Merge(m, src)
-}
-func (m *StandaloneConfig) XXX_Size() int {
-	return xxx_messageInfo_StandaloneConfig.Size(m)
-}
-func (m *StandaloneConfig) XXX_DiscardUnknown() {
-	xxx_messageInfo_StandaloneConfig.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_StandaloneConfig proto.InternalMessageInfo
-
-func (m *StandaloneConfig) GetSplitDimension() []*SplitDimension {
-	if m != nil {
-		return m.SplitDimension
+func (x *StandaloneConfig) GetSplitDimension() []*SplitDimension {
+	if x != nil {
+		return x.SplitDimension
 	}
 	return nil
 }
 
-func (m *StandaloneConfig) GetStrip_64BitLibraries() bool {
-	if m != nil {
-		return m.Strip_64BitLibraries
+func (x *StandaloneConfig) GetStrip_64BitLibraries() bool {
+	if x != nil {
+		return x.Strip_64BitLibraries
 	}
 	return false
 }
 
 type SplitDimension struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	Value SplitDimension_Value `protobuf:"varint,1,opt,name=value,proto3,enum=android.bundle.SplitDimension_Value" json:"value,omitempty"`
 	// If set to 'true', indicates that APKs should *not* be split by this
 	// dimension.
 	Negate bool `protobuf:"varint,2,opt,name=negate,proto3" json:"negate,omitempty"`
 	// Optional transformation to be applied to asset directories where
 	// the targeting is encoded in the directory name (e.g: assets/foo#tcf_etc1)
-	SuffixStripping      *SuffixStripping `protobuf:"bytes,3,opt,name=suffix_stripping,json=suffixStripping,proto3" json:"suffix_stripping,omitempty"`
-	XXX_NoUnkeyedLiteral struct{}         `json:"-"`
-	XXX_unrecognized     []byte           `json:"-"`
-	XXX_sizecache        int32            `json:"-"`
+	SuffixStripping *SuffixStripping `protobuf:"bytes,3,opt,name=suffix_stripping,json=suffixStripping,proto3" json:"suffix_stripping,omitempty"`
 }
 
-func (m *SplitDimension) Reset()         { *m = SplitDimension{} }
-func (m *SplitDimension) String() string { return proto.CompactTextString(m) }
-func (*SplitDimension) ProtoMessage()    {}
+func (x *SplitDimension) Reset() {
+	*x = SplitDimension{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_config_proto_msgTypes[9]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *SplitDimension) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SplitDimension) ProtoMessage() {}
+
+func (x *SplitDimension) ProtoReflect() protoreflect.Message {
+	mi := &file_config_proto_msgTypes[9]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use SplitDimension.ProtoReflect.Descriptor instead.
 func (*SplitDimension) Descriptor() ([]byte, []int) {
-	return fileDescriptor_3eaf2c85e69e9ea4, []int{9}
+	return file_config_proto_rawDescGZIP(), []int{9}
 }
 
-func (m *SplitDimension) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_SplitDimension.Unmarshal(m, b)
-}
-func (m *SplitDimension) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_SplitDimension.Marshal(b, m, deterministic)
-}
-func (m *SplitDimension) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_SplitDimension.Merge(m, src)
-}
-func (m *SplitDimension) XXX_Size() int {
-	return xxx_messageInfo_SplitDimension.Size(m)
-}
-func (m *SplitDimension) XXX_DiscardUnknown() {
-	xxx_messageInfo_SplitDimension.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_SplitDimension proto.InternalMessageInfo
-
-func (m *SplitDimension) GetValue() SplitDimension_Value {
-	if m != nil {
-		return m.Value
+func (x *SplitDimension) GetValue() SplitDimension_Value {
+	if x != nil {
+		return x.Value
 	}
 	return SplitDimension_UNSPECIFIED_VALUE
 }
 
-func (m *SplitDimension) GetNegate() bool {
-	if m != nil {
-		return m.Negate
+func (x *SplitDimension) GetNegate() bool {
+	if x != nil {
+		return x.Negate
 	}
 	return false
 }
 
-func (m *SplitDimension) GetSuffixStripping() *SuffixStripping {
-	if m != nil {
-		return m.SuffixStripping
+func (x *SplitDimension) GetSuffixStripping() *SuffixStripping {
+	if x != nil {
+		return x.SuffixStripping
 	}
 	return nil
 }
 
 type SuffixStripping struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	// If set to 'true', indicates that the targeting suffix should be removed
 	// from assets paths for this dimension when splits (or asset slices) are
 	// generated.
@@ -632,47 +763,51 @@
 	// used (for example, if both "assets/level1_textures#tcf_etc1" and
 	// "assets/level1_textures" are present and the default suffix is empty,
 	// then only "assets/level1_textures" will be used).
-	DefaultSuffix        string   `protobuf:"bytes,2,opt,name=default_suffix,json=defaultSuffix,proto3" json:"default_suffix,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
+	DefaultSuffix string `protobuf:"bytes,2,opt,name=default_suffix,json=defaultSuffix,proto3" json:"default_suffix,omitempty"`
 }
 
-func (m *SuffixStripping) Reset()         { *m = SuffixStripping{} }
-func (m *SuffixStripping) String() string { return proto.CompactTextString(m) }
-func (*SuffixStripping) ProtoMessage()    {}
+func (x *SuffixStripping) Reset() {
+	*x = SuffixStripping{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_config_proto_msgTypes[10]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *SuffixStripping) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SuffixStripping) ProtoMessage() {}
+
+func (x *SuffixStripping) ProtoReflect() protoreflect.Message {
+	mi := &file_config_proto_msgTypes[10]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use SuffixStripping.ProtoReflect.Descriptor instead.
 func (*SuffixStripping) Descriptor() ([]byte, []int) {
-	return fileDescriptor_3eaf2c85e69e9ea4, []int{10}
+	return file_config_proto_rawDescGZIP(), []int{10}
 }
 
-func (m *SuffixStripping) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_SuffixStripping.Unmarshal(m, b)
-}
-func (m *SuffixStripping) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_SuffixStripping.Marshal(b, m, deterministic)
-}
-func (m *SuffixStripping) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_SuffixStripping.Merge(m, src)
-}
-func (m *SuffixStripping) XXX_Size() int {
-	return xxx_messageInfo_SuffixStripping.Size(m)
-}
-func (m *SuffixStripping) XXX_DiscardUnknown() {
-	xxx_messageInfo_SuffixStripping.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_SuffixStripping proto.InternalMessageInfo
-
-func (m *SuffixStripping) GetEnabled() bool {
-	if m != nil {
-		return m.Enabled
+func (x *SuffixStripping) GetEnabled() bool {
+	if x != nil {
+		return x.Enabled
 	}
 	return false
 }
 
-func (m *SuffixStripping) GetDefaultSuffix() string {
-	if m != nil {
-		return m.DefaultSuffix
+func (x *SuffixStripping) GetDefaultSuffix() string {
+	if x != nil {
+		return x.DefaultSuffix
 	}
 	return ""
 }
@@ -680,273 +815,634 @@
 // Configuration for processing APEX bundles.
 // https://source.android.com/devices/tech/ota/apex
 type ApexConfig struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	// Configuration for processing of APKs embedded in an APEX image.
 	ApexEmbeddedApkConfig []*ApexEmbeddedApkConfig `protobuf:"bytes,1,rep,name=apex_embedded_apk_config,json=apexEmbeddedApkConfig,proto3" json:"apex_embedded_apk_config,omitempty"`
-	XXX_NoUnkeyedLiteral  struct{}                 `json:"-"`
-	XXX_unrecognized      []byte                   `json:"-"`
-	XXX_sizecache         int32                    `json:"-"`
 }
 
-func (m *ApexConfig) Reset()         { *m = ApexConfig{} }
-func (m *ApexConfig) String() string { return proto.CompactTextString(m) }
-func (*ApexConfig) ProtoMessage()    {}
+func (x *ApexConfig) Reset() {
+	*x = ApexConfig{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_config_proto_msgTypes[11]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ApexConfig) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ApexConfig) ProtoMessage() {}
+
+func (x *ApexConfig) ProtoReflect() protoreflect.Message {
+	mi := &file_config_proto_msgTypes[11]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ApexConfig.ProtoReflect.Descriptor instead.
 func (*ApexConfig) Descriptor() ([]byte, []int) {
-	return fileDescriptor_3eaf2c85e69e9ea4, []int{11}
+	return file_config_proto_rawDescGZIP(), []int{11}
 }
 
-func (m *ApexConfig) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_ApexConfig.Unmarshal(m, b)
-}
-func (m *ApexConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_ApexConfig.Marshal(b, m, deterministic)
-}
-func (m *ApexConfig) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_ApexConfig.Merge(m, src)
-}
-func (m *ApexConfig) XXX_Size() int {
-	return xxx_messageInfo_ApexConfig.Size(m)
-}
-func (m *ApexConfig) XXX_DiscardUnknown() {
-	xxx_messageInfo_ApexConfig.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_ApexConfig proto.InternalMessageInfo
-
-func (m *ApexConfig) GetApexEmbeddedApkConfig() []*ApexEmbeddedApkConfig {
-	if m != nil {
-		return m.ApexEmbeddedApkConfig
+func (x *ApexConfig) GetApexEmbeddedApkConfig() []*ApexEmbeddedApkConfig {
+	if x != nil {
+		return x.ApexEmbeddedApkConfig
 	}
 	return nil
 }
 
 type ApexEmbeddedApkConfig struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	// Android package name of the APK.
 	PackageName string `protobuf:"bytes,1,opt,name=package_name,json=packageName,proto3" json:"package_name,omitempty"`
 	// Path to the APK within the APEX system image.
-	Path                 string   `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
+	Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"`
 }
 
-func (m *ApexEmbeddedApkConfig) Reset()         { *m = ApexEmbeddedApkConfig{} }
-func (m *ApexEmbeddedApkConfig) String() string { return proto.CompactTextString(m) }
-func (*ApexEmbeddedApkConfig) ProtoMessage()    {}
+func (x *ApexEmbeddedApkConfig) Reset() {
+	*x = ApexEmbeddedApkConfig{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_config_proto_msgTypes[12]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ApexEmbeddedApkConfig) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ApexEmbeddedApkConfig) ProtoMessage() {}
+
+func (x *ApexEmbeddedApkConfig) ProtoReflect() protoreflect.Message {
+	mi := &file_config_proto_msgTypes[12]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ApexEmbeddedApkConfig.ProtoReflect.Descriptor instead.
 func (*ApexEmbeddedApkConfig) Descriptor() ([]byte, []int) {
-	return fileDescriptor_3eaf2c85e69e9ea4, []int{12}
+	return file_config_proto_rawDescGZIP(), []int{12}
 }
 
-func (m *ApexEmbeddedApkConfig) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_ApexEmbeddedApkConfig.Unmarshal(m, b)
-}
-func (m *ApexEmbeddedApkConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_ApexEmbeddedApkConfig.Marshal(b, m, deterministic)
-}
-func (m *ApexEmbeddedApkConfig) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_ApexEmbeddedApkConfig.Merge(m, src)
-}
-func (m *ApexEmbeddedApkConfig) XXX_Size() int {
-	return xxx_messageInfo_ApexEmbeddedApkConfig.Size(m)
-}
-func (m *ApexEmbeddedApkConfig) XXX_DiscardUnknown() {
-	xxx_messageInfo_ApexEmbeddedApkConfig.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_ApexEmbeddedApkConfig proto.InternalMessageInfo
-
-func (m *ApexEmbeddedApkConfig) GetPackageName() string {
-	if m != nil {
-		return m.PackageName
+func (x *ApexEmbeddedApkConfig) GetPackageName() string {
+	if x != nil {
+		return x.PackageName
 	}
 	return ""
 }
 
-func (m *ApexEmbeddedApkConfig) GetPath() string {
-	if m != nil {
-		return m.Path
+func (x *ApexEmbeddedApkConfig) GetPath() string {
+	if x != nil {
+		return x.Path
 	}
 	return ""
 }
 
 type UnsignedEmbeddedApkConfig struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	// Path to the APK inside the module (e.g. if the path inside the bundle
 	// is split/assets/example.apk, this will be assets/example.apk).
-	Path                 string   `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
+	Path string `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"`
 }
 
-func (m *UnsignedEmbeddedApkConfig) Reset()         { *m = UnsignedEmbeddedApkConfig{} }
-func (m *UnsignedEmbeddedApkConfig) String() string { return proto.CompactTextString(m) }
-func (*UnsignedEmbeddedApkConfig) ProtoMessage()    {}
+func (x *UnsignedEmbeddedApkConfig) Reset() {
+	*x = UnsignedEmbeddedApkConfig{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_config_proto_msgTypes[13]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *UnsignedEmbeddedApkConfig) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*UnsignedEmbeddedApkConfig) ProtoMessage() {}
+
+func (x *UnsignedEmbeddedApkConfig) ProtoReflect() protoreflect.Message {
+	mi := &file_config_proto_msgTypes[13]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use UnsignedEmbeddedApkConfig.ProtoReflect.Descriptor instead.
 func (*UnsignedEmbeddedApkConfig) Descriptor() ([]byte, []int) {
-	return fileDescriptor_3eaf2c85e69e9ea4, []int{13}
+	return file_config_proto_rawDescGZIP(), []int{13}
 }
 
-func (m *UnsignedEmbeddedApkConfig) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_UnsignedEmbeddedApkConfig.Unmarshal(m, b)
-}
-func (m *UnsignedEmbeddedApkConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_UnsignedEmbeddedApkConfig.Marshal(b, m, deterministic)
-}
-func (m *UnsignedEmbeddedApkConfig) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_UnsignedEmbeddedApkConfig.Merge(m, src)
-}
-func (m *UnsignedEmbeddedApkConfig) XXX_Size() int {
-	return xxx_messageInfo_UnsignedEmbeddedApkConfig.Size(m)
-}
-func (m *UnsignedEmbeddedApkConfig) XXX_DiscardUnknown() {
-	xxx_messageInfo_UnsignedEmbeddedApkConfig.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_UnsignedEmbeddedApkConfig proto.InternalMessageInfo
-
-func (m *UnsignedEmbeddedApkConfig) GetPath() string {
-	if m != nil {
-		return m.Path
+func (x *UnsignedEmbeddedApkConfig) GetPath() string {
+	if x != nil {
+		return x.Path
 	}
 	return ""
 }
 
 type AssetModulesConfig struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	// App versionCodes that will be updated with these asset modules.
 	// Only relevant for asset-only bundles.
 	AppVersion []int64 `protobuf:"varint,1,rep,packed,name=app_version,json=appVersion,proto3" json:"app_version,omitempty"`
 	// Version tag for the asset upload.
 	// Only relevant for asset-only bundles.
-	AssetVersionTag      string   `protobuf:"bytes,2,opt,name=asset_version_tag,json=assetVersionTag,proto3" json:"asset_version_tag,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
+	AssetVersionTag string `protobuf:"bytes,2,opt,name=asset_version_tag,json=assetVersionTag,proto3" json:"asset_version_tag,omitempty"`
 }
 
-func (m *AssetModulesConfig) Reset()         { *m = AssetModulesConfig{} }
-func (m *AssetModulesConfig) String() string { return proto.CompactTextString(m) }
-func (*AssetModulesConfig) ProtoMessage()    {}
+func (x *AssetModulesConfig) Reset() {
+	*x = AssetModulesConfig{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_config_proto_msgTypes[14]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *AssetModulesConfig) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*AssetModulesConfig) ProtoMessage() {}
+
+func (x *AssetModulesConfig) ProtoReflect() protoreflect.Message {
+	mi := &file_config_proto_msgTypes[14]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use AssetModulesConfig.ProtoReflect.Descriptor instead.
 func (*AssetModulesConfig) Descriptor() ([]byte, []int) {
-	return fileDescriptor_3eaf2c85e69e9ea4, []int{14}
+	return file_config_proto_rawDescGZIP(), []int{14}
 }
 
-func (m *AssetModulesConfig) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_AssetModulesConfig.Unmarshal(m, b)
-}
-func (m *AssetModulesConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_AssetModulesConfig.Marshal(b, m, deterministic)
-}
-func (m *AssetModulesConfig) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_AssetModulesConfig.Merge(m, src)
-}
-func (m *AssetModulesConfig) XXX_Size() int {
-	return xxx_messageInfo_AssetModulesConfig.Size(m)
-}
-func (m *AssetModulesConfig) XXX_DiscardUnknown() {
-	xxx_messageInfo_AssetModulesConfig.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_AssetModulesConfig proto.InternalMessageInfo
-
-func (m *AssetModulesConfig) GetAppVersion() []int64 {
-	if m != nil {
-		return m.AppVersion
+func (x *AssetModulesConfig) GetAppVersion() []int64 {
+	if x != nil {
+		return x.AppVersion
 	}
 	return nil
 }
 
-func (m *AssetModulesConfig) GetAssetVersionTag() string {
-	if m != nil {
-		return m.AssetVersionTag
+func (x *AssetModulesConfig) GetAssetVersionTag() string {
+	if x != nil {
+		return x.AssetVersionTag
 	}
 	return ""
 }
 
-func init() {
-	proto.RegisterEnum("android.bundle.BundleConfig_BundleType", BundleConfig_BundleType_name, BundleConfig_BundleType_value)
-	proto.RegisterEnum("android.bundle.SplitDimension_Value", SplitDimension_Value_name, SplitDimension_Value_value)
-	proto.RegisterType((*BundleConfig)(nil), "android.bundle.BundleConfig")
-	proto.RegisterType((*Bundletool)(nil), "android.bundle.Bundletool")
-	proto.RegisterType((*Compression)(nil), "android.bundle.Compression")
-	proto.RegisterType((*MasterResources)(nil), "android.bundle.MasterResources")
-	proto.RegisterType((*Optimizations)(nil), "android.bundle.Optimizations")
-	proto.RegisterType((*UncompressNativeLibraries)(nil), "android.bundle.UncompressNativeLibraries")
-	proto.RegisterType((*UncompressDexFiles)(nil), "android.bundle.UncompressDexFiles")
-	proto.RegisterType((*SplitsConfig)(nil), "android.bundle.SplitsConfig")
-	proto.RegisterType((*StandaloneConfig)(nil), "android.bundle.StandaloneConfig")
-	proto.RegisterType((*SplitDimension)(nil), "android.bundle.SplitDimension")
-	proto.RegisterType((*SuffixStripping)(nil), "android.bundle.SuffixStripping")
-	proto.RegisterType((*ApexConfig)(nil), "android.bundle.ApexConfig")
-	proto.RegisterType((*ApexEmbeddedApkConfig)(nil), "android.bundle.ApexEmbeddedApkConfig")
-	proto.RegisterType((*UnsignedEmbeddedApkConfig)(nil), "android.bundle.UnsignedEmbeddedApkConfig")
-	proto.RegisterType((*AssetModulesConfig)(nil), "android.bundle.AssetModulesConfig")
+var File_config_proto protoreflect.FileDescriptor
+
+var file_config_proto_rawDesc = []byte{
+	0x0a, 0x0c, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0e,
+	0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x22, 0x8b,
+	0x05, 0x0a, 0x0c, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12,
+	0x3a, 0x0a, 0x0a, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x74, 0x6f, 0x6f, 0x6c, 0x18, 0x01, 0x20,
+	0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75,
+	0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x74, 0x6f, 0x6f, 0x6c, 0x52,
+	0x0a, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x74, 0x6f, 0x6f, 0x6c, 0x12, 0x43, 0x0a, 0x0d, 0x6f,
+	0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01,
+	0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e,
+	0x64, 0x6c, 0x65, 0x2e, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e,
+	0x73, 0x52, 0x0d, 0x6f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+	0x12, 0x3d, 0x0a, 0x0b, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18,
+	0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e,
+	0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69,
+	0x6f, 0x6e, 0x52, 0x0b, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12,
+	0x4a, 0x0a, 0x10, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72,
+	0x63, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x61, 0x6e, 0x64, 0x72,
+	0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x4d, 0x61, 0x73, 0x74, 0x65,
+	0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x52, 0x0f, 0x6d, 0x61, 0x73, 0x74,
+	0x65, 0x72, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3b, 0x0a, 0x0b, 0x61,
+	0x70, 0x65, 0x78, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b,
+	0x32, 0x1a, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c,
+	0x65, 0x2e, 0x41, 0x70, 0x65, 0x78, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0a, 0x61, 0x70,
+	0x65, 0x78, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x6a, 0x0a, 0x1c, 0x75, 0x6e, 0x73, 0x69,
+	0x67, 0x6e, 0x65, 0x64, 0x5f, 0x65, 0x6d, 0x62, 0x65, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x61, 0x70,
+	0x6b, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x29,
+	0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e,
+	0x55, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x45, 0x6d, 0x62, 0x65, 0x64, 0x64, 0x65, 0x64,
+	0x41, 0x70, 0x6b, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x19, 0x75, 0x6e, 0x73, 0x69, 0x67,
+	0x6e, 0x65, 0x64, 0x45, 0x6d, 0x62, 0x65, 0x64, 0x64, 0x65, 0x64, 0x41, 0x70, 0x6b, 0x43, 0x6f,
+	0x6e, 0x66, 0x69, 0x67, 0x12, 0x54, 0x0a, 0x14, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x6d, 0x6f,
+	0x64, 0x75, 0x6c, 0x65, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x07, 0x20, 0x01,
+	0x28, 0x0b, 0x32, 0x22, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e,
+	0x64, 0x6c, 0x65, 0x2e, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73,
+	0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x12, 0x61, 0x73, 0x73, 0x65, 0x74, 0x4d, 0x6f, 0x64,
+	0x75, 0x6c, 0x65, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3b, 0x0a, 0x04, 0x74, 0x79,
+	0x70, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x27, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f,
+	0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65,
+	0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x54, 0x79, 0x70,
+	0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0x33, 0x0a, 0x0a, 0x42, 0x75, 0x6e, 0x64, 0x6c,
+	0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a, 0x07, 0x52, 0x45, 0x47, 0x55, 0x4c, 0x41, 0x52,
+	0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x41, 0x50, 0x45, 0x58, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a,
+	0x41, 0x53, 0x53, 0x45, 0x54, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x10, 0x02, 0x22, 0x2c, 0x0a, 0x0a,
+	0x42, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x74, 0x6f, 0x6f, 0x6c, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65,
+	0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72,
+	0x73, 0x69, 0x6f, 0x6e, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x22, 0x3a, 0x0a, 0x0b, 0x43, 0x6f,
+	0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2b, 0x0a, 0x11, 0x75, 0x6e, 0x63,
+	0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x65, 0x64, 0x5f, 0x67, 0x6c, 0x6f, 0x62, 0x18, 0x01,
+	0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x75, 0x6e, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73,
+	0x65, 0x64, 0x47, 0x6c, 0x6f, 0x62, 0x22, 0x5b, 0x0a, 0x0f, 0x4d, 0x61, 0x73, 0x74, 0x65, 0x72,
+	0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x65, 0x73,
+	0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x05, 0x52,
+	0x0b, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x73, 0x12, 0x25, 0x0a, 0x0e,
+	0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x18, 0x02,
+	0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4e, 0x61,
+	0x6d, 0x65, 0x73, 0x22, 0xe2, 0x02, 0x0a, 0x0d, 0x4f, 0x70, 0x74, 0x69, 0x6d, 0x69, 0x7a, 0x61,
+	0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x41, 0x0a, 0x0d, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x73, 0x5f,
+	0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x61,
+	0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x53, 0x70,
+	0x6c, 0x69, 0x74, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0c, 0x73, 0x70, 0x6c, 0x69,
+	0x74, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x69, 0x0a, 0x1b, 0x75, 0x6e, 0x63, 0x6f,
+	0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x6c, 0x69,
+	0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e,
+	0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x55,
+	0x6e, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x4e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4c,
+	0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x52, 0x19, 0x75, 0x6e, 0x63, 0x6f, 0x6d, 0x70,
+	0x72, 0x65, 0x73, 0x73, 0x4e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72,
+	0x69, 0x65, 0x73, 0x12, 0x54, 0x0a, 0x14, 0x75, 0x6e, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73,
+	0x73, 0x5f, 0x64, 0x65, 0x78, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28,
+	0x0b, 0x32, 0x22, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64,
+	0x6c, 0x65, 0x2e, 0x55, 0x6e, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x44, 0x65, 0x78,
+	0x46, 0x69, 0x6c, 0x65, 0x73, 0x52, 0x12, 0x75, 0x6e, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73,
+	0x73, 0x44, 0x65, 0x78, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x4d, 0x0a, 0x11, 0x73, 0x74, 0x61,
+	0x6e, 0x64, 0x61, 0x6c, 0x6f, 0x6e, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x04,
+	0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62,
+	0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x53, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x6c, 0x6f, 0x6e, 0x65,
+	0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x10, 0x73, 0x74, 0x61, 0x6e, 0x64, 0x61, 0x6c, 0x6f,
+	0x6e, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x35, 0x0a, 0x19, 0x55, 0x6e, 0x63, 0x6f,
+	0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x4e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x4c, 0x69, 0x62, 0x72,
+	0x61, 0x72, 0x69, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64,
+	0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22,
+	0x2e, 0x0a, 0x12, 0x55, 0x6e, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x44, 0x65, 0x78,
+	0x46, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64,
+	0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x22,
+	0x57, 0x0a, 0x0c, 0x53, 0x70, 0x6c, 0x69, 0x74, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12,
+	0x47, 0x0a, 0x0f, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x5f, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69,
+	0x6f, 0x6e, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f,
+	0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x53, 0x70, 0x6c, 0x69, 0x74, 0x44,
+	0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x44,
+	0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x90, 0x01, 0x0a, 0x10, 0x53, 0x74, 0x61,
+	0x6e, 0x64, 0x61, 0x6c, 0x6f, 0x6e, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x47, 0x0a,
+	0x0f, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x5f, 0x64, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e,
+	0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
+	0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x53, 0x70, 0x6c, 0x69, 0x74, 0x44, 0x69, 0x6d,
+	0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x73, 0x70, 0x6c, 0x69, 0x74, 0x44, 0x69, 0x6d,
+	0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x33, 0x0a, 0x16, 0x73, 0x74, 0x72, 0x69, 0x70, 0x5f,
+	0x36, 0x34, 0x5f, 0x62, 0x69, 0x74, 0x5f, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73,
+	0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x73, 0x74, 0x72, 0x69, 0x70, 0x36, 0x34, 0x42,
+	0x69, 0x74, 0x4c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, 0x65, 0x73, 0x22, 0xad, 0x02, 0x0a, 0x0e,
+	0x53, 0x70, 0x6c, 0x69, 0x74, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x3a,
+	0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x24, 0x2e,
+	0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x53,
+	0x70, 0x6c, 0x69, 0x74, 0x44, 0x69, 0x6d, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x56, 0x61,
+	0x6c, 0x75, 0x65, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x6e, 0x65,
+	0x67, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x6e, 0x65, 0x67, 0x61,
+	0x74, 0x65, 0x12, 0x4a, 0x0a, 0x10, 0x73, 0x75, 0x66, 0x66, 0x69, 0x78, 0x5f, 0x73, 0x74, 0x72,
+	0x69, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x61,
+	0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x53, 0x75,
+	0x66, 0x66, 0x69, 0x78, 0x53, 0x74, 0x72, 0x69, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x52, 0x0f, 0x73,
+	0x75, 0x66, 0x66, 0x69, 0x78, 0x53, 0x74, 0x72, 0x69, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x22, 0x7b,
+	0x0a, 0x05, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x15, 0x0a, 0x11, 0x55, 0x4e, 0x53, 0x50, 0x45,
+	0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x5f, 0x56, 0x41, 0x4c, 0x55, 0x45, 0x10, 0x00, 0x12, 0x07,
+	0x0a, 0x03, 0x41, 0x42, 0x49, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e, 0x53, 0x43, 0x52, 0x45, 0x45,
+	0x4e, 0x5f, 0x44, 0x45, 0x4e, 0x53, 0x49, 0x54, 0x59, 0x10, 0x02, 0x12, 0x0c, 0x0a, 0x08, 0x4c,
+	0x41, 0x4e, 0x47, 0x55, 0x41, 0x47, 0x45, 0x10, 0x03, 0x12, 0x1e, 0x0a, 0x1a, 0x54, 0x45, 0x58,
+	0x54, 0x55, 0x52, 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x52, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e,
+	0x5f, 0x46, 0x4f, 0x52, 0x4d, 0x41, 0x54, 0x10, 0x04, 0x12, 0x10, 0x0a, 0x0c, 0x47, 0x52, 0x41,
+	0x50, 0x48, 0x49, 0x43, 0x53, 0x5f, 0x41, 0x50, 0x49, 0x10, 0x05, 0x22, 0x52, 0x0a, 0x0f, 0x53,
+	0x75, 0x66, 0x66, 0x69, 0x78, 0x53, 0x74, 0x72, 0x69, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x12, 0x18,
+	0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52,
+	0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x25, 0x0a, 0x0e, 0x64, 0x65, 0x66, 0x61,
+	0x75, 0x6c, 0x74, 0x5f, 0x73, 0x75, 0x66, 0x66, 0x69, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x0d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x53, 0x75, 0x66, 0x66, 0x69, 0x78, 0x22,
+	0x6c, 0x0a, 0x0a, 0x41, 0x70, 0x65, 0x78, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x5e, 0x0a,
+	0x18, 0x61, 0x70, 0x65, 0x78, 0x5f, 0x65, 0x6d, 0x62, 0x65, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x61,
+	0x70, 0x6b, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
+	0x25, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65,
+	0x2e, 0x41, 0x70, 0x65, 0x78, 0x45, 0x6d, 0x62, 0x65, 0x64, 0x64, 0x65, 0x64, 0x41, 0x70, 0x6b,
+	0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x15, 0x61, 0x70, 0x65, 0x78, 0x45, 0x6d, 0x62, 0x65,
+	0x64, 0x64, 0x65, 0x64, 0x41, 0x70, 0x6b, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x22, 0x4e, 0x0a,
+	0x15, 0x41, 0x70, 0x65, 0x78, 0x45, 0x6d, 0x62, 0x65, 0x64, 0x64, 0x65, 0x64, 0x41, 0x70, 0x6b,
+	0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x21, 0x0a, 0x0c, 0x70, 0x61, 0x63, 0x6b, 0x61, 0x67,
+	0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x70, 0x61,
+	0x63, 0x6b, 0x61, 0x67, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74,
+	0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x2f, 0x0a,
+	0x19, 0x55, 0x6e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x45, 0x6d, 0x62, 0x65, 0x64, 0x64, 0x65,
+	0x64, 0x41, 0x70, 0x6b, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61,
+	0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, 0x61,
+	0x0a, 0x12, 0x41, 0x73, 0x73, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x43, 0x6f,
+	0x6e, 0x66, 0x69, 0x67, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x70, 0x70, 0x5f, 0x76, 0x65, 0x72, 0x73,
+	0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x03, 0x28, 0x03, 0x52, 0x0a, 0x61, 0x70, 0x70, 0x56, 0x65,
+	0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2a, 0x0a, 0x11, 0x61, 0x73, 0x73, 0x65, 0x74, 0x5f, 0x76,
+	0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x61, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x0f, 0x61, 0x73, 0x73, 0x65, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x61,
+	0x67, 0x42, 0x41, 0x0a, 0x12, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
+	0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x5a, 0x2b, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
+	0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x63, 0x6d, 0x64, 0x2f, 0x65, 0x78, 0x74, 0x72, 0x61,
+	0x63, 0x74, 0x5f, 0x61, 0x70, 0x6b, 0x73, 0x2f, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x5f, 0x70,
+	0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
-func init() {
-	proto.RegisterFile("config.proto", fileDescriptor_3eaf2c85e69e9ea4)
+var (
+	file_config_proto_rawDescOnce sync.Once
+	file_config_proto_rawDescData = file_config_proto_rawDesc
+)
+
+func file_config_proto_rawDescGZIP() []byte {
+	file_config_proto_rawDescOnce.Do(func() {
+		file_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_config_proto_rawDescData)
+	})
+	return file_config_proto_rawDescData
 }
 
-var fileDescriptor_3eaf2c85e69e9ea4 = []byte{
-	// 1001 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0xdb, 0x6e, 0xdb, 0x46,
-	0x10, 0x0d, 0x75, 0xb1, 0xe5, 0x91, 0x2c, 0xd1, 0xdb, 0x38, 0x50, 0x2e, 0x4d, 0x5c, 0xa2, 0x41,
-	0xdd, 0xb4, 0x50, 0x01, 0x3b, 0xcd, 0x83, 0x83, 0x3e, 0xd0, 0x32, 0xad, 0x2a, 0xd0, 0x0d, 0x4b,
-	0xc9, 0x4d, 0x5a, 0xa0, 0x8b, 0x95, 0xb8, 0x52, 0xb7, 0xa6, 0x48, 0x82, 0x4b, 0x1a, 0x4a, 0xfb,
-	0x09, 0x7d, 0xe9, 0x8f, 0xf4, 0xa7, 0xfa, 0x25, 0x05, 0x97, 0xa4, 0x2c, 0x51, 0x52, 0x9e, 0xfa,
-	0x24, 0xce, 0xec, 0x39, 0xb3, 0x3b, 0xb3, 0x67, 0x67, 0x04, 0x95, 0x89, 0xeb, 0x4c, 0xf9, 0xac,
-	0xe1, 0xf9, 0x6e, 0xe0, 0xa2, 0x2a, 0x75, 0x2c, 0xdf, 0xe5, 0x56, 0x63, 0x1c, 0x3a, 0x96, 0xcd,
-	0xb4, 0xbf, 0x8a, 0x50, 0xb9, 0x94, 0x9f, 0x4d, 0x09, 0x43, 0x17, 0x00, 0xf1, 0x52, 0xe0, 0xba,
-	0x76, 0x5d, 0x39, 0x51, 0x4e, 0xcb, 0x67, 0x4f, 0x1a, 0xeb, 0xac, 0xc6, 0xe5, 0x12, 0x81, 0x57,
-	0xd0, 0xa8, 0x09, 0x87, 0xae, 0x17, 0xf0, 0x39, 0xff, 0x83, 0x06, 0xdc, 0x75, 0x44, 0x3d, 0x27,
-	0xe9, 0x9f, 0x67, 0xe9, 0xfd, 0x55, 0x10, 0x5e, 0xe7, 0xa0, 0x1f, 0xa0, 0x3c, 0x71, 0xe7, 0x9e,
-	0xcf, 0x84, 0xe0, 0xae, 0x53, 0xcf, 0xcb, 0x10, 0x4f, 0xb3, 0x21, 0x9a, 0xf7, 0x10, 0xbc, 0x8a,
-	0x47, 0xef, 0x40, 0x9d, 0x53, 0x11, 0x30, 0x9f, 0xf8, 0x4c, 0xb8, 0xa1, 0x3f, 0x61, 0xa2, 0x5e,
-	0x90, 0x31, 0x5e, 0x64, 0x63, 0x74, 0x25, 0x0e, 0xa7, 0x30, 0x5c, 0x9b, 0xaf, 0x3b, 0xd0, 0x5b,
-	0x28, 0x53, 0x8f, 0x2d, 0x48, 0x5c, 0xc1, 0x7a, 0x71, 0x7b, 0x31, 0x74, 0x8f, 0x2d, 0xe2, 0xe2,
-	0x61, 0xa0, 0xcb, 0x6f, 0xf4, 0x3b, 0x3c, 0x0b, 0x1d, 0xc1, 0x67, 0x0e, 0xb3, 0x08, 0x9b, 0x8f,
-	0x99, 0x65, 0x31, 0x8b, 0x50, 0xef, 0x36, 0x8d, 0xb6, 0x77, 0x92, 0x3f, 0x2d, 0x9f, 0x7d, 0x9d,
-	0x8d, 0x36, 0x4a, 0x38, 0x46, 0x42, 0xd1, 0xbd, 0xdb, 0x24, 0xf8, 0xe3, 0x70, 0xd7, 0x12, 0x1a,
-	0xc2, 0x43, 0x2a, 0x04, 0x0b, 0xc8, 0xdc, 0xb5, 0x42, 0x9b, 0x89, 0x74, 0x8f, 0x7d, 0x79, 0x62,
-	0x6d, 0xe3, 0xc4, 0x11, 0xb6, 0x1b, 0x43, 0x93, 0xe0, 0x88, 0x6e, 0xf8, 0xd0, 0x5b, 0x28, 0x04,
-	0x1f, 0x3d, 0x56, 0x2f, 0x9d, 0x28, 0xa7, 0xd5, 0xb3, 0xaf, 0xb6, 0x8b, 0x20, 0xc6, 0x26, 0xc6,
-	0xf0, 0xa3, 0xc7, 0xb0, 0x24, 0x69, 0xe7, 0x00, 0xf7, 0x3e, 0x54, 0x86, 0x7d, 0x6c, 0xb4, 0x46,
-	0x1d, 0x1d, 0xab, 0x0f, 0x50, 0x09, 0x0a, 0xfa, 0xc0, 0x78, 0xaf, 0x2a, 0xa8, 0x0a, 0xa0, 0x9b,
-	0xa6, 0x31, 0x24, 0xfd, 0x5e, 0xe7, 0x83, 0x9a, 0xd3, 0xbe, 0x4d, 0x49, 0x52, 0x4e, 0x75, 0xd8,
-	0xbf, 0x63, 0xbe, 0x54, 0x41, 0x24, 0xa4, 0x03, 0x9c, 0x9a, 0xef, 0x0a, 0x25, 0x45, 0xcd, 0x69,
-	0x17, 0x50, 0x5e, 0x91, 0x01, 0xfa, 0x06, 0x8e, 0x42, 0x27, 0x95, 0x02, 0xb3, 0xc8, 0xcc, 0x76,
-	0xc7, 0x75, 0xe5, 0x24, 0x7f, 0x7a, 0x80, 0xd5, 0xd5, 0x85, 0x96, 0xed, 0x8e, 0xb5, 0x5f, 0xa0,
-	0x96, 0xb9, 0x7e, 0xf4, 0x05, 0x54, 0x52, 0xc9, 0x10, 0x6e, 0x09, 0x49, 0x2d, 0xe2, 0x72, 0xea,
-	0x6b, 0x5b, 0x02, 0xbd, 0x84, 0xea, 0x12, 0xe2, 0xd0, 0x39, 0x8b, 0x14, 0x1e, 0xc5, 0x3f, 0x4c,
-	0xbd, 0xbd, 0xc8, 0xa9, 0xfd, 0x9b, 0x83, 0xc3, 0x35, 0x8d, 0x23, 0x1d, 0x0e, 0x85, 0x67, 0xf3,
-	0x60, 0x79, 0x33, 0xf1, 0xc3, 0x7a, 0x96, 0xad, 0xa9, 0x29, 0x41, 0xc9, 0x9d, 0x54, 0xc4, 0x8a,
-	0x85, 0x38, 0x3c, 0xbd, 0xcf, 0x82, 0x38, 0x34, 0xe0, 0x77, 0x8c, 0xd8, 0x7c, 0xec, 0x53, 0x9f,
-	0xb3, 0xf4, 0xa9, 0x6d, 0x91, 0x53, 0x4a, 0xe9, 0x49, 0x46, 0x27, 0x25, 0x44, 0x72, 0xda, 0xb1,
-	0x14, 0xc9, 0x69, 0x65, 0x2b, 0x8b, 0x2d, 0xc8, 0x94, 0xdb, 0x4c, 0x24, 0x6f, 0x51, 0xdb, 0xbd,
-	0xc7, 0x15, 0x5b, 0x5c, 0x47, 0x48, 0x8c, 0xc2, 0x0d, 0x1f, 0xea, 0xc2, 0x91, 0x08, 0xa8, 0x63,
-	0x51, 0xdb, 0x75, 0x58, 0x5a, 0x87, 0xf8, 0x69, 0x9e, 0x6c, 0xd4, 0x61, 0x09, 0x4c, 0x6a, 0xa1,
-	0x8a, 0x8c, 0x47, 0xfb, 0x1e, 0x1e, 0xef, 0x4c, 0x2e, 0x92, 0x0e, 0x73, 0xe8, 0xd8, 0x66, 0x96,
-	0xac, 0x74, 0x09, 0xa7, 0xa6, 0xd6, 0x00, 0xb4, 0x79, 0xde, 0x4f, 0xe0, 0x7f, 0x82, 0xca, 0xea,
-	0xa5, 0xa0, 0x16, 0xd4, 0xe4, 0xb5, 0x10, 0x8b, 0xcf, 0x99, 0x23, 0xc5, 0xa9, 0xc8, 0x97, 0xfc,
-	0x7c, 0xeb, 0x5d, 0x5e, 0xa5, 0x28, 0x5c, 0x15, 0x6b, 0xb6, 0xf6, 0xb7, 0x02, 0x6a, 0x36, 0xcd,
-	0xff, 0x2d, 0x3a, 0x3a, 0x87, 0x47, 0x22, 0xf0, 0xb9, 0x47, 0xde, 0xbc, 0x26, 0x63, 0x1e, 0x64,
-	0x84, 0x52, 0xc2, 0x9f, 0xc9, 0xd5, 0x37, 0xaf, 0x2f, 0x79, 0xb0, 0xac, 0x9a, 0xf6, 0x4f, 0x0e,
-	0xaa, 0xeb, 0x71, 0xd1, 0x05, 0x14, 0xef, 0xa8, 0x1d, 0x32, 0x59, 0x96, 0xea, 0xd9, 0x97, 0x9f,
-	0x3e, 0x46, 0xe3, 0x26, 0xc2, 0xe2, 0x98, 0x82, 0x1e, 0xc1, 0x9e, 0xc3, 0x66, 0x34, 0x60, 0xc9,
-	0x9e, 0x89, 0x15, 0xb5, 0x68, 0x11, 0x4e, 0xa7, 0x7c, 0x41, 0xe4, 0x21, 0x3c, 0xee, 0xcc, 0x12,
-	0x69, 0x6d, 0xb4, 0x68, 0x53, 0xe2, 0xcc, 0x14, 0x86, 0x6b, 0x62, 0xdd, 0xa1, 0xfd, 0x09, 0x45,
-	0xb9, 0x27, 0x3a, 0x86, 0xa3, 0x51, 0xcf, 0x1c, 0x18, 0xcd, 0xf6, 0x75, 0xdb, 0xb8, 0x22, 0x37,
-	0x7a, 0x67, 0x64, 0xa8, 0x0f, 0xd0, 0x3e, 0xe4, 0xf5, 0xcb, 0xb6, 0xaa, 0x20, 0x04, 0x55, 0xb3,
-	0x89, 0x0d, 0xa3, 0x47, 0xae, 0x8c, 0x9e, 0xd9, 0x1e, 0x7e, 0x50, 0x73, 0xa8, 0x02, 0xa5, 0x8e,
-	0xde, 0x6b, 0x8d, 0xf4, 0x96, 0xa1, 0xe6, 0xd1, 0x73, 0x78, 0x32, 0x34, 0xde, 0x0f, 0x47, 0xd8,
-	0x20, 0xcd, 0x7e, 0x77, 0x80, 0x0d, 0xd3, 0x6c, 0xf7, 0x7b, 0xe4, 0xba, 0x8f, 0xbb, 0xfa, 0x50,
-	0x2d, 0x20, 0x15, 0x2a, 0x2d, 0xac, 0x0f, 0x7e, 0x6c, 0x37, 0x4d, 0xa2, 0x0f, 0xda, 0x6a, 0x51,
-	0xc3, 0x50, 0xcb, 0x1c, 0x70, 0xb7, 0x90, 0xa2, 0xde, 0x61, 0xb1, 0x29, 0x0d, 0xed, 0x80, 0xc4,
-	0x49, 0x24, 0x4d, 0xed, 0x30, 0xf1, 0xc6, 0x91, 0x34, 0x1b, 0xe0, 0x7e, 0xa0, 0xa0, 0x5f, 0xa1,
-	0x2e, 0x27, 0xd0, 0xb6, 0x01, 0x12, 0x0b, 0xe3, 0xe5, 0xb6, 0x71, 0xb4, 0x39, 0x3c, 0x8e, 0xe9,
-	0x36, 0xb7, 0xd6, 0x83, 0xe3, 0xad, 0xf8, 0xa8, 0x19, 0x7a, 0x74, 0x72, 0x4b, 0x67, 0x71, 0xa3,
-	0x93, 0xc9, 0x1c, 0xe0, 0x72, 0xe2, 0x8b, 0xda, 0x1c, 0x42, 0x50, 0xf0, 0x68, 0xf0, 0x5b, 0x92,
-	0x86, 0xfc, 0xd6, 0xbe, 0x8b, 0x1e, 0xe5, 0xae, 0x29, 0x95, 0x12, 0x94, 0x15, 0x02, 0x05, 0xb4,
-	0x39, 0x8d, 0xd0, 0x8b, 0x68, 0xf0, 0x7a, 0x24, 0xed, 0xfe, 0x51, 0xa6, 0xf9, 0x68, 0xb8, 0x7a,
-	0x37, 0xb1, 0x07, 0xbd, 0x82, 0xa3, 0x78, 0xe0, 0x25, 0x10, 0x12, 0xd0, 0x59, 0x72, 0x90, 0x9a,
-	0x5c, 0x48, 0x80, 0x43, 0x3a, 0xbb, 0x7c, 0x05, 0x68, 0xe2, 0xce, 0x33, 0x65, 0xfa, 0xf9, 0x61,
-	0x62, 0x93, 0xd8, 0x26, 0xf2, 0xef, 0xd1, 0x78, 0x4f, 0xfe, 0x9c, 0xff, 0x17, 0x00, 0x00, 0xff,
-	0xff, 0x6b, 0x05, 0xbf, 0x99, 0x35, 0x09, 0x00, 0x00,
+var file_config_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
+var file_config_proto_msgTypes = make([]protoimpl.MessageInfo, 15)
+var file_config_proto_goTypes = []interface{}{
+	(BundleConfig_BundleType)(0),      // 0: android.bundle.BundleConfig.BundleType
+	(SplitDimension_Value)(0),         // 1: android.bundle.SplitDimension.Value
+	(*BundleConfig)(nil),              // 2: android.bundle.BundleConfig
+	(*Bundletool)(nil),                // 3: android.bundle.Bundletool
+	(*Compression)(nil),               // 4: android.bundle.Compression
+	(*MasterResources)(nil),           // 5: android.bundle.MasterResources
+	(*Optimizations)(nil),             // 6: android.bundle.Optimizations
+	(*UncompressNativeLibraries)(nil), // 7: android.bundle.UncompressNativeLibraries
+	(*UncompressDexFiles)(nil),        // 8: android.bundle.UncompressDexFiles
+	(*SplitsConfig)(nil),              // 9: android.bundle.SplitsConfig
+	(*StandaloneConfig)(nil),          // 10: android.bundle.StandaloneConfig
+	(*SplitDimension)(nil),            // 11: android.bundle.SplitDimension
+	(*SuffixStripping)(nil),           // 12: android.bundle.SuffixStripping
+	(*ApexConfig)(nil),                // 13: android.bundle.ApexConfig
+	(*ApexEmbeddedApkConfig)(nil),     // 14: android.bundle.ApexEmbeddedApkConfig
+	(*UnsignedEmbeddedApkConfig)(nil), // 15: android.bundle.UnsignedEmbeddedApkConfig
+	(*AssetModulesConfig)(nil),        // 16: android.bundle.AssetModulesConfig
+}
+var file_config_proto_depIdxs = []int32{
+	3,  // 0: android.bundle.BundleConfig.bundletool:type_name -> android.bundle.Bundletool
+	6,  // 1: android.bundle.BundleConfig.optimizations:type_name -> android.bundle.Optimizations
+	4,  // 2: android.bundle.BundleConfig.compression:type_name -> android.bundle.Compression
+	5,  // 3: android.bundle.BundleConfig.master_resources:type_name -> android.bundle.MasterResources
+	13, // 4: android.bundle.BundleConfig.apex_config:type_name -> android.bundle.ApexConfig
+	15, // 5: android.bundle.BundleConfig.unsigned_embedded_apk_config:type_name -> android.bundle.UnsignedEmbeddedApkConfig
+	16, // 6: android.bundle.BundleConfig.asset_modules_config:type_name -> android.bundle.AssetModulesConfig
+	0,  // 7: android.bundle.BundleConfig.type:type_name -> android.bundle.BundleConfig.BundleType
+	9,  // 8: android.bundle.Optimizations.splits_config:type_name -> android.bundle.SplitsConfig
+	7,  // 9: android.bundle.Optimizations.uncompress_native_libraries:type_name -> android.bundle.UncompressNativeLibraries
+	8,  // 10: android.bundle.Optimizations.uncompress_dex_files:type_name -> android.bundle.UncompressDexFiles
+	10, // 11: android.bundle.Optimizations.standalone_config:type_name -> android.bundle.StandaloneConfig
+	11, // 12: android.bundle.SplitsConfig.split_dimension:type_name -> android.bundle.SplitDimension
+	11, // 13: android.bundle.StandaloneConfig.split_dimension:type_name -> android.bundle.SplitDimension
+	1,  // 14: android.bundle.SplitDimension.value:type_name -> android.bundle.SplitDimension.Value
+	12, // 15: android.bundle.SplitDimension.suffix_stripping:type_name -> android.bundle.SuffixStripping
+	14, // 16: android.bundle.ApexConfig.apex_embedded_apk_config:type_name -> android.bundle.ApexEmbeddedApkConfig
+	17, // [17:17] is the sub-list for method output_type
+	17, // [17:17] is the sub-list for method input_type
+	17, // [17:17] is the sub-list for extension type_name
+	17, // [17:17] is the sub-list for extension extendee
+	0,  // [0:17] is the sub-list for field type_name
+}
+
+func init() { file_config_proto_init() }
+func file_config_proto_init() {
+	if File_config_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*BundleConfig); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Bundletool); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Compression); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_config_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*MasterResources); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_config_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Optimizations); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_config_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*UncompressNativeLibraries); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_config_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*UncompressDexFiles); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_config_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*SplitsConfig); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_config_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*StandaloneConfig); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_config_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*SplitDimension); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_config_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*SuffixStripping); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_config_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ApexConfig); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_config_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ApexEmbeddedApkConfig); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_config_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*UnsignedEmbeddedApkConfig); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_config_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*AssetModulesConfig); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_config_proto_rawDesc,
+			NumEnums:      2,
+			NumMessages:   15,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_config_proto_goTypes,
+		DependencyIndexes: file_config_proto_depIdxs,
+		EnumInfos:         file_config_proto_enumTypes,
+		MessageInfos:      file_config_proto_msgTypes,
+	}.Build()
+	File_config_proto = out.File
+	file_config_proto_rawDesc = nil
+	file_config_proto_goTypes = nil
+	file_config_proto_depIdxs = nil
 }
diff --git a/cmd/extract_apks/bundle_proto/config.proto b/cmd/extract_apks/bundle_proto/config.proto
index 1c161aa..d6fac03 100644
--- a/cmd/extract_apks/bundle_proto/config.proto
+++ b/cmd/extract_apks/bundle_proto/config.proto
@@ -6,7 +6,7 @@
 
 package android.bundle;
 
-option go_package = "android_bundle_proto";
+option go_package = "android/soong/cmd/extract_apks/bundle_proto";
 option java_package = "com.android.bundle";
 
 message BundleConfig {
diff --git a/cmd/extract_apks/bundle_proto/targeting.pb.go b/cmd/extract_apks/bundle_proto/targeting.pb.go
index 187bc44..66e6f0d 100644
--- a/cmd/extract_apks/bundle_proto/targeting.pb.go
+++ b/cmd/extract_apks/bundle_proto/targeting.pb.go
@@ -1,24 +1,29 @@
+// Messages describing APK Set's table of contents (toc.pb entry).
+// Please be advised that the ultimate source is at
+// https://github.com/google/bundletool/tree/master/src/main/proto
+// so you have been warned.
+
 // Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.26.0
+// 	protoc        v3.9.1
 // source: targeting.proto
 
-package android_bundle_proto
+package bundle_proto
 
 import (
-	fmt "fmt"
-	proto "github.com/golang/protobuf/proto"
-	math "math"
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
 )
 
-// Reference imports to suppress errors if they are not otherwise used.
-var _ = proto.Marshal
-var _ = fmt.Errorf
-var _ = math.Inf
-
-// This is a compile-time assertion to ensure that this generated file
-// is compatible with the proto package it is being compiled against.
-// A compilation error at this line likely means your copy of the
-// proto package needs to be updated.
-const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
 
 type ScreenDensity_DensityAlias int32
 
@@ -34,36 +39,57 @@
 	ScreenDensity_XXXHDPI             ScreenDensity_DensityAlias = 8
 )
 
-var ScreenDensity_DensityAlias_name = map[int32]string{
-	0: "DENSITY_UNSPECIFIED",
-	1: "NODPI",
-	2: "LDPI",
-	3: "MDPI",
-	4: "TVDPI",
-	5: "HDPI",
-	6: "XHDPI",
-	7: "XXHDPI",
-	8: "XXXHDPI",
-}
+// Enum value maps for ScreenDensity_DensityAlias.
+var (
+	ScreenDensity_DensityAlias_name = map[int32]string{
+		0: "DENSITY_UNSPECIFIED",
+		1: "NODPI",
+		2: "LDPI",
+		3: "MDPI",
+		4: "TVDPI",
+		5: "HDPI",
+		6: "XHDPI",
+		7: "XXHDPI",
+		8: "XXXHDPI",
+	}
+	ScreenDensity_DensityAlias_value = map[string]int32{
+		"DENSITY_UNSPECIFIED": 0,
+		"NODPI":               1,
+		"LDPI":                2,
+		"MDPI":                3,
+		"TVDPI":               4,
+		"HDPI":                5,
+		"XHDPI":               6,
+		"XXHDPI":              7,
+		"XXXHDPI":             8,
+	}
+)
 
-var ScreenDensity_DensityAlias_value = map[string]int32{
-	"DENSITY_UNSPECIFIED": 0,
-	"NODPI":               1,
-	"LDPI":                2,
-	"MDPI":                3,
-	"TVDPI":               4,
-	"HDPI":                5,
-	"XHDPI":               6,
-	"XXHDPI":              7,
-	"XXXHDPI":             8,
+func (x ScreenDensity_DensityAlias) Enum() *ScreenDensity_DensityAlias {
+	p := new(ScreenDensity_DensityAlias)
+	*p = x
+	return p
 }
 
 func (x ScreenDensity_DensityAlias) String() string {
-	return proto.EnumName(ScreenDensity_DensityAlias_name, int32(x))
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
 }
 
+func (ScreenDensity_DensityAlias) Descriptor() protoreflect.EnumDescriptor {
+	return file_targeting_proto_enumTypes[0].Descriptor()
+}
+
+func (ScreenDensity_DensityAlias) Type() protoreflect.EnumType {
+	return &file_targeting_proto_enumTypes[0]
+}
+
+func (x ScreenDensity_DensityAlias) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use ScreenDensity_DensityAlias.Descriptor instead.
 func (ScreenDensity_DensityAlias) EnumDescriptor() ([]byte, []int) {
-	return fileDescriptor_df45b505afdf471e, []int{4, 0}
+	return file_targeting_proto_rawDescGZIP(), []int{4, 0}
 }
 
 type TextureCompressionFormat_TextureCompressionFormatAlias int32
@@ -82,40 +108,61 @@
 	TextureCompressionFormat_ETC2                                   TextureCompressionFormat_TextureCompressionFormatAlias = 10
 )
 
-var TextureCompressionFormat_TextureCompressionFormatAlias_name = map[int32]string{
-	0:  "UNSPECIFIED_TEXTURE_COMPRESSION_FORMAT",
-	1:  "ETC1_RGB8",
-	2:  "PALETTED",
-	3:  "THREE_DC",
-	4:  "ATC",
-	5:  "LATC",
-	6:  "DXT1",
-	7:  "S3TC",
-	8:  "PVRTC",
-	9:  "ASTC",
-	10: "ETC2",
-}
+// Enum value maps for TextureCompressionFormat_TextureCompressionFormatAlias.
+var (
+	TextureCompressionFormat_TextureCompressionFormatAlias_name = map[int32]string{
+		0:  "UNSPECIFIED_TEXTURE_COMPRESSION_FORMAT",
+		1:  "ETC1_RGB8",
+		2:  "PALETTED",
+		3:  "THREE_DC",
+		4:  "ATC",
+		5:  "LATC",
+		6:  "DXT1",
+		7:  "S3TC",
+		8:  "PVRTC",
+		9:  "ASTC",
+		10: "ETC2",
+	}
+	TextureCompressionFormat_TextureCompressionFormatAlias_value = map[string]int32{
+		"UNSPECIFIED_TEXTURE_COMPRESSION_FORMAT": 0,
+		"ETC1_RGB8":                              1,
+		"PALETTED":                               2,
+		"THREE_DC":                               3,
+		"ATC":                                    4,
+		"LATC":                                   5,
+		"DXT1":                                   6,
+		"S3TC":                                   7,
+		"PVRTC":                                  8,
+		"ASTC":                                   9,
+		"ETC2":                                   10,
+	}
+)
 
-var TextureCompressionFormat_TextureCompressionFormatAlias_value = map[string]int32{
-	"UNSPECIFIED_TEXTURE_COMPRESSION_FORMAT": 0,
-	"ETC1_RGB8":                              1,
-	"PALETTED":                               2,
-	"THREE_DC":                               3,
-	"ATC":                                    4,
-	"LATC":                                   5,
-	"DXT1":                                   6,
-	"S3TC":                                   7,
-	"PVRTC":                                  8,
-	"ASTC":                                   9,
-	"ETC2":                                   10,
+func (x TextureCompressionFormat_TextureCompressionFormatAlias) Enum() *TextureCompressionFormat_TextureCompressionFormatAlias {
+	p := new(TextureCompressionFormat_TextureCompressionFormatAlias)
+	*p = x
+	return p
 }
 
 func (x TextureCompressionFormat_TextureCompressionFormatAlias) String() string {
-	return proto.EnumName(TextureCompressionFormat_TextureCompressionFormatAlias_name, int32(x))
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
 }
 
+func (TextureCompressionFormat_TextureCompressionFormatAlias) Descriptor() protoreflect.EnumDescriptor {
+	return file_targeting_proto_enumTypes[1].Descriptor()
+}
+
+func (TextureCompressionFormat_TextureCompressionFormatAlias) Type() protoreflect.EnumType {
+	return &file_targeting_proto_enumTypes[1]
+}
+
+func (x TextureCompressionFormat_TextureCompressionFormatAlias) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use TextureCompressionFormat_TextureCompressionFormatAlias.Descriptor instead.
 func (TextureCompressionFormat_TextureCompressionFormatAlias) EnumDescriptor() ([]byte, []int) {
-	return fileDescriptor_df45b505afdf471e, []int{10, 0}
+	return file_targeting_proto_rawDescGZIP(), []int{10, 0}
 }
 
 type Abi_AbiAlias int32
@@ -131,34 +178,55 @@
 	Abi_MIPS64                       Abi_AbiAlias = 7
 )
 
-var Abi_AbiAlias_name = map[int32]string{
-	0: "UNSPECIFIED_CPU_ARCHITECTURE",
-	1: "ARMEABI",
-	2: "ARMEABI_V7A",
-	3: "ARM64_V8A",
-	4: "X86",
-	5: "X86_64",
-	6: "MIPS",
-	7: "MIPS64",
-}
+// Enum value maps for Abi_AbiAlias.
+var (
+	Abi_AbiAlias_name = map[int32]string{
+		0: "UNSPECIFIED_CPU_ARCHITECTURE",
+		1: "ARMEABI",
+		2: "ARMEABI_V7A",
+		3: "ARM64_V8A",
+		4: "X86",
+		5: "X86_64",
+		6: "MIPS",
+		7: "MIPS64",
+	}
+	Abi_AbiAlias_value = map[string]int32{
+		"UNSPECIFIED_CPU_ARCHITECTURE": 0,
+		"ARMEABI":                      1,
+		"ARMEABI_V7A":                  2,
+		"ARM64_V8A":                    3,
+		"X86":                          4,
+		"X86_64":                       5,
+		"MIPS":                         6,
+		"MIPS64":                       7,
+	}
+)
 
-var Abi_AbiAlias_value = map[string]int32{
-	"UNSPECIFIED_CPU_ARCHITECTURE": 0,
-	"ARMEABI":                      1,
-	"ARMEABI_V7A":                  2,
-	"ARM64_V8A":                    3,
-	"X86":                          4,
-	"X86_64":                       5,
-	"MIPS":                         6,
-	"MIPS64":                       7,
+func (x Abi_AbiAlias) Enum() *Abi_AbiAlias {
+	p := new(Abi_AbiAlias)
+	*p = x
+	return p
 }
 
 func (x Abi_AbiAlias) String() string {
-	return proto.EnumName(Abi_AbiAlias_name, int32(x))
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
 }
 
+func (Abi_AbiAlias) Descriptor() protoreflect.EnumDescriptor {
+	return file_targeting_proto_enumTypes[2].Descriptor()
+}
+
+func (Abi_AbiAlias) Type() protoreflect.EnumType {
+	return &file_targeting_proto_enumTypes[2]
+}
+
+func (x Abi_AbiAlias) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use Abi_AbiAlias.Descriptor instead.
 func (Abi_AbiAlias) EnumDescriptor() ([]byte, []int) {
-	return fileDescriptor_df45b505afdf471e, []int{11, 0}
+	return file_targeting_proto_rawDescGZIP(), []int{11, 0}
 }
 
 type Sanitizer_SanitizerAlias int32
@@ -168,98 +236,131 @@
 	Sanitizer_HWADDRESS Sanitizer_SanitizerAlias = 1
 )
 
-var Sanitizer_SanitizerAlias_name = map[int32]string{
-	0: "NONE",
-	1: "HWADDRESS",
-}
+// Enum value maps for Sanitizer_SanitizerAlias.
+var (
+	Sanitizer_SanitizerAlias_name = map[int32]string{
+		0: "NONE",
+		1: "HWADDRESS",
+	}
+	Sanitizer_SanitizerAlias_value = map[string]int32{
+		"NONE":      0,
+		"HWADDRESS": 1,
+	}
+)
 
-var Sanitizer_SanitizerAlias_value = map[string]int32{
-	"NONE":      0,
-	"HWADDRESS": 1,
+func (x Sanitizer_SanitizerAlias) Enum() *Sanitizer_SanitizerAlias {
+	p := new(Sanitizer_SanitizerAlias)
+	*p = x
+	return p
 }
 
 func (x Sanitizer_SanitizerAlias) String() string {
-	return proto.EnumName(Sanitizer_SanitizerAlias_name, int32(x))
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
 }
 
+func (Sanitizer_SanitizerAlias) Descriptor() protoreflect.EnumDescriptor {
+	return file_targeting_proto_enumTypes[3].Descriptor()
+}
+
+func (Sanitizer_SanitizerAlias) Type() protoreflect.EnumType {
+	return &file_targeting_proto_enumTypes[3]
+}
+
+func (x Sanitizer_SanitizerAlias) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Use Sanitizer_SanitizerAlias.Descriptor instead.
 func (Sanitizer_SanitizerAlias) EnumDescriptor() ([]byte, []int) {
-	return fileDescriptor_df45b505afdf471e, []int{13, 0}
+	return file_targeting_proto_rawDescGZIP(), []int{13, 0}
 }
 
 // Targeting on the level of variants.
 type VariantTargeting struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	SdkVersionTargeting               *SdkVersionTargeting               `protobuf:"bytes,1,opt,name=sdk_version_targeting,json=sdkVersionTargeting,proto3" json:"sdk_version_targeting,omitempty"`
 	AbiTargeting                      *AbiTargeting                      `protobuf:"bytes,2,opt,name=abi_targeting,json=abiTargeting,proto3" json:"abi_targeting,omitempty"`
 	ScreenDensityTargeting            *ScreenDensityTargeting            `protobuf:"bytes,3,opt,name=screen_density_targeting,json=screenDensityTargeting,proto3" json:"screen_density_targeting,omitempty"`
 	MultiAbiTargeting                 *MultiAbiTargeting                 `protobuf:"bytes,4,opt,name=multi_abi_targeting,json=multiAbiTargeting,proto3" json:"multi_abi_targeting,omitempty"`
 	TextureCompressionFormatTargeting *TextureCompressionFormatTargeting `protobuf:"bytes,5,opt,name=texture_compression_format_targeting,json=textureCompressionFormatTargeting,proto3" json:"texture_compression_format_targeting,omitempty"`
-	XXX_NoUnkeyedLiteral              struct{}                           `json:"-"`
-	XXX_unrecognized                  []byte                             `json:"-"`
-	XXX_sizecache                     int32                              `json:"-"`
 }
 
-func (m *VariantTargeting) Reset()         { *m = VariantTargeting{} }
-func (m *VariantTargeting) String() string { return proto.CompactTextString(m) }
-func (*VariantTargeting) ProtoMessage()    {}
+func (x *VariantTargeting) Reset() {
+	*x = VariantTargeting{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_targeting_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *VariantTargeting) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*VariantTargeting) ProtoMessage() {}
+
+func (x *VariantTargeting) ProtoReflect() protoreflect.Message {
+	mi := &file_targeting_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use VariantTargeting.ProtoReflect.Descriptor instead.
 func (*VariantTargeting) Descriptor() ([]byte, []int) {
-	return fileDescriptor_df45b505afdf471e, []int{0}
+	return file_targeting_proto_rawDescGZIP(), []int{0}
 }
 
-func (m *VariantTargeting) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_VariantTargeting.Unmarshal(m, b)
-}
-func (m *VariantTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_VariantTargeting.Marshal(b, m, deterministic)
-}
-func (m *VariantTargeting) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_VariantTargeting.Merge(m, src)
-}
-func (m *VariantTargeting) XXX_Size() int {
-	return xxx_messageInfo_VariantTargeting.Size(m)
-}
-func (m *VariantTargeting) XXX_DiscardUnknown() {
-	xxx_messageInfo_VariantTargeting.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_VariantTargeting proto.InternalMessageInfo
-
-func (m *VariantTargeting) GetSdkVersionTargeting() *SdkVersionTargeting {
-	if m != nil {
-		return m.SdkVersionTargeting
+func (x *VariantTargeting) GetSdkVersionTargeting() *SdkVersionTargeting {
+	if x != nil {
+		return x.SdkVersionTargeting
 	}
 	return nil
 }
 
-func (m *VariantTargeting) GetAbiTargeting() *AbiTargeting {
-	if m != nil {
-		return m.AbiTargeting
+func (x *VariantTargeting) GetAbiTargeting() *AbiTargeting {
+	if x != nil {
+		return x.AbiTargeting
 	}
 	return nil
 }
 
-func (m *VariantTargeting) GetScreenDensityTargeting() *ScreenDensityTargeting {
-	if m != nil {
-		return m.ScreenDensityTargeting
+func (x *VariantTargeting) GetScreenDensityTargeting() *ScreenDensityTargeting {
+	if x != nil {
+		return x.ScreenDensityTargeting
 	}
 	return nil
 }
 
-func (m *VariantTargeting) GetMultiAbiTargeting() *MultiAbiTargeting {
-	if m != nil {
-		return m.MultiAbiTargeting
+func (x *VariantTargeting) GetMultiAbiTargeting() *MultiAbiTargeting {
+	if x != nil {
+		return x.MultiAbiTargeting
 	}
 	return nil
 }
 
-func (m *VariantTargeting) GetTextureCompressionFormatTargeting() *TextureCompressionFormatTargeting {
-	if m != nil {
-		return m.TextureCompressionFormatTargeting
+func (x *VariantTargeting) GetTextureCompressionFormatTargeting() *TextureCompressionFormatTargeting {
+	if x != nil {
+		return x.TextureCompressionFormatTargeting
 	}
 	return nil
 }
 
 // Targeting on the level of individual APKs.
 type ApkTargeting struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	AbiTargeting                      *AbiTargeting                      `protobuf:"bytes,1,opt,name=abi_targeting,json=abiTargeting,proto3" json:"abi_targeting,omitempty"`
 	GraphicsApiTargeting              *GraphicsApiTargeting              `protobuf:"bytes,2,opt,name=graphics_api_targeting,json=graphicsApiTargeting,proto3" json:"graphics_api_targeting,omitempty"`
 	LanguageTargeting                 *LanguageTargeting                 `protobuf:"bytes,3,opt,name=language_targeting,json=languageTargeting,proto3" json:"language_targeting,omitempty"`
@@ -268,88 +369,92 @@
 	TextureCompressionFormatTargeting *TextureCompressionFormatTargeting `protobuf:"bytes,6,opt,name=texture_compression_format_targeting,json=textureCompressionFormatTargeting,proto3" json:"texture_compression_format_targeting,omitempty"`
 	MultiAbiTargeting                 *MultiAbiTargeting                 `protobuf:"bytes,7,opt,name=multi_abi_targeting,json=multiAbiTargeting,proto3" json:"multi_abi_targeting,omitempty"`
 	SanitizerTargeting                *SanitizerTargeting                `protobuf:"bytes,8,opt,name=sanitizer_targeting,json=sanitizerTargeting,proto3" json:"sanitizer_targeting,omitempty"`
-	XXX_NoUnkeyedLiteral              struct{}                           `json:"-"`
-	XXX_unrecognized                  []byte                             `json:"-"`
-	XXX_sizecache                     int32                              `json:"-"`
 }
 
-func (m *ApkTargeting) Reset()         { *m = ApkTargeting{} }
-func (m *ApkTargeting) String() string { return proto.CompactTextString(m) }
-func (*ApkTargeting) ProtoMessage()    {}
+func (x *ApkTargeting) Reset() {
+	*x = ApkTargeting{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_targeting_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ApkTargeting) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ApkTargeting) ProtoMessage() {}
+
+func (x *ApkTargeting) ProtoReflect() protoreflect.Message {
+	mi := &file_targeting_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ApkTargeting.ProtoReflect.Descriptor instead.
 func (*ApkTargeting) Descriptor() ([]byte, []int) {
-	return fileDescriptor_df45b505afdf471e, []int{1}
+	return file_targeting_proto_rawDescGZIP(), []int{1}
 }
 
-func (m *ApkTargeting) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_ApkTargeting.Unmarshal(m, b)
-}
-func (m *ApkTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_ApkTargeting.Marshal(b, m, deterministic)
-}
-func (m *ApkTargeting) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_ApkTargeting.Merge(m, src)
-}
-func (m *ApkTargeting) XXX_Size() int {
-	return xxx_messageInfo_ApkTargeting.Size(m)
-}
-func (m *ApkTargeting) XXX_DiscardUnknown() {
-	xxx_messageInfo_ApkTargeting.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_ApkTargeting proto.InternalMessageInfo
-
-func (m *ApkTargeting) GetAbiTargeting() *AbiTargeting {
-	if m != nil {
-		return m.AbiTargeting
+func (x *ApkTargeting) GetAbiTargeting() *AbiTargeting {
+	if x != nil {
+		return x.AbiTargeting
 	}
 	return nil
 }
 
-func (m *ApkTargeting) GetGraphicsApiTargeting() *GraphicsApiTargeting {
-	if m != nil {
-		return m.GraphicsApiTargeting
+func (x *ApkTargeting) GetGraphicsApiTargeting() *GraphicsApiTargeting {
+	if x != nil {
+		return x.GraphicsApiTargeting
 	}
 	return nil
 }
 
-func (m *ApkTargeting) GetLanguageTargeting() *LanguageTargeting {
-	if m != nil {
-		return m.LanguageTargeting
+func (x *ApkTargeting) GetLanguageTargeting() *LanguageTargeting {
+	if x != nil {
+		return x.LanguageTargeting
 	}
 	return nil
 }
 
-func (m *ApkTargeting) GetScreenDensityTargeting() *ScreenDensityTargeting {
-	if m != nil {
-		return m.ScreenDensityTargeting
+func (x *ApkTargeting) GetScreenDensityTargeting() *ScreenDensityTargeting {
+	if x != nil {
+		return x.ScreenDensityTargeting
 	}
 	return nil
 }
 
-func (m *ApkTargeting) GetSdkVersionTargeting() *SdkVersionTargeting {
-	if m != nil {
-		return m.SdkVersionTargeting
+func (x *ApkTargeting) GetSdkVersionTargeting() *SdkVersionTargeting {
+	if x != nil {
+		return x.SdkVersionTargeting
 	}
 	return nil
 }
 
-func (m *ApkTargeting) GetTextureCompressionFormatTargeting() *TextureCompressionFormatTargeting {
-	if m != nil {
-		return m.TextureCompressionFormatTargeting
+func (x *ApkTargeting) GetTextureCompressionFormatTargeting() *TextureCompressionFormatTargeting {
+	if x != nil {
+		return x.TextureCompressionFormatTargeting
 	}
 	return nil
 }
 
-func (m *ApkTargeting) GetMultiAbiTargeting() *MultiAbiTargeting {
-	if m != nil {
-		return m.MultiAbiTargeting
+func (x *ApkTargeting) GetMultiAbiTargeting() *MultiAbiTargeting {
+	if x != nil {
+		return x.MultiAbiTargeting
 	}
 	return nil
 }
 
-func (m *ApkTargeting) GetSanitizerTargeting() *SanitizerTargeting {
-	if m != nil {
-		return m.SanitizerTargeting
+func (x *ApkTargeting) GetSanitizerTargeting() *SanitizerTargeting {
+	if x != nil {
+		return x.SanitizerTargeting
 	}
 	return nil
 }
@@ -357,56 +462,64 @@
 // Targeting on the module level.
 // The semantic of the targeting is the "AND" rule on all immediate values.
 type ModuleTargeting struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	SdkVersionTargeting    *SdkVersionTargeting      `protobuf:"bytes,1,opt,name=sdk_version_targeting,json=sdkVersionTargeting,proto3" json:"sdk_version_targeting,omitempty"`
 	DeviceFeatureTargeting []*DeviceFeatureTargeting `protobuf:"bytes,2,rep,name=device_feature_targeting,json=deviceFeatureTargeting,proto3" json:"device_feature_targeting,omitempty"`
 	UserCountriesTargeting *UserCountriesTargeting   `protobuf:"bytes,3,opt,name=user_countries_targeting,json=userCountriesTargeting,proto3" json:"user_countries_targeting,omitempty"`
-	XXX_NoUnkeyedLiteral   struct{}                  `json:"-"`
-	XXX_unrecognized       []byte                    `json:"-"`
-	XXX_sizecache          int32                     `json:"-"`
 }
 
-func (m *ModuleTargeting) Reset()         { *m = ModuleTargeting{} }
-func (m *ModuleTargeting) String() string { return proto.CompactTextString(m) }
-func (*ModuleTargeting) ProtoMessage()    {}
+func (x *ModuleTargeting) Reset() {
+	*x = ModuleTargeting{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_targeting_proto_msgTypes[2]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ModuleTargeting) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ModuleTargeting) ProtoMessage() {}
+
+func (x *ModuleTargeting) ProtoReflect() protoreflect.Message {
+	mi := &file_targeting_proto_msgTypes[2]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ModuleTargeting.ProtoReflect.Descriptor instead.
 func (*ModuleTargeting) Descriptor() ([]byte, []int) {
-	return fileDescriptor_df45b505afdf471e, []int{2}
+	return file_targeting_proto_rawDescGZIP(), []int{2}
 }
 
-func (m *ModuleTargeting) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_ModuleTargeting.Unmarshal(m, b)
-}
-func (m *ModuleTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_ModuleTargeting.Marshal(b, m, deterministic)
-}
-func (m *ModuleTargeting) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_ModuleTargeting.Merge(m, src)
-}
-func (m *ModuleTargeting) XXX_Size() int {
-	return xxx_messageInfo_ModuleTargeting.Size(m)
-}
-func (m *ModuleTargeting) XXX_DiscardUnknown() {
-	xxx_messageInfo_ModuleTargeting.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_ModuleTargeting proto.InternalMessageInfo
-
-func (m *ModuleTargeting) GetSdkVersionTargeting() *SdkVersionTargeting {
-	if m != nil {
-		return m.SdkVersionTargeting
+func (x *ModuleTargeting) GetSdkVersionTargeting() *SdkVersionTargeting {
+	if x != nil {
+		return x.SdkVersionTargeting
 	}
 	return nil
 }
 
-func (m *ModuleTargeting) GetDeviceFeatureTargeting() []*DeviceFeatureTargeting {
-	if m != nil {
-		return m.DeviceFeatureTargeting
+func (x *ModuleTargeting) GetDeviceFeatureTargeting() []*DeviceFeatureTargeting {
+	if x != nil {
+		return x.DeviceFeatureTargeting
 	}
 	return nil
 }
 
-func (m *ModuleTargeting) GetUserCountriesTargeting() *UserCountriesTargeting {
-	if m != nil {
-		return m.UserCountriesTargeting
+func (x *ModuleTargeting) GetUserCountriesTargeting() *UserCountriesTargeting {
+	if x != nil {
+		return x.UserCountriesTargeting
 	}
 	return nil
 }
@@ -414,88 +527,125 @@
 // User Countries targeting describing an inclusive/exclusive list of country
 // codes that module targets.
 type UserCountriesTargeting struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	// List of country codes in the two-letter CLDR territory format.
 	CountryCodes []string `protobuf:"bytes,1,rep,name=country_codes,json=countryCodes,proto3" json:"country_codes,omitempty"`
 	// Indicates if the list above is exclusive.
-	Exclude              bool     `protobuf:"varint,2,opt,name=exclude,proto3" json:"exclude,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
+	Exclude bool `protobuf:"varint,2,opt,name=exclude,proto3" json:"exclude,omitempty"`
 }
 
-func (m *UserCountriesTargeting) Reset()         { *m = UserCountriesTargeting{} }
-func (m *UserCountriesTargeting) String() string { return proto.CompactTextString(m) }
-func (*UserCountriesTargeting) ProtoMessage()    {}
+func (x *UserCountriesTargeting) Reset() {
+	*x = UserCountriesTargeting{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_targeting_proto_msgTypes[3]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *UserCountriesTargeting) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*UserCountriesTargeting) ProtoMessage() {}
+
+func (x *UserCountriesTargeting) ProtoReflect() protoreflect.Message {
+	mi := &file_targeting_proto_msgTypes[3]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use UserCountriesTargeting.ProtoReflect.Descriptor instead.
 func (*UserCountriesTargeting) Descriptor() ([]byte, []int) {
-	return fileDescriptor_df45b505afdf471e, []int{3}
+	return file_targeting_proto_rawDescGZIP(), []int{3}
 }
 
-func (m *UserCountriesTargeting) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_UserCountriesTargeting.Unmarshal(m, b)
-}
-func (m *UserCountriesTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_UserCountriesTargeting.Marshal(b, m, deterministic)
-}
-func (m *UserCountriesTargeting) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_UserCountriesTargeting.Merge(m, src)
-}
-func (m *UserCountriesTargeting) XXX_Size() int {
-	return xxx_messageInfo_UserCountriesTargeting.Size(m)
-}
-func (m *UserCountriesTargeting) XXX_DiscardUnknown() {
-	xxx_messageInfo_UserCountriesTargeting.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_UserCountriesTargeting proto.InternalMessageInfo
-
-func (m *UserCountriesTargeting) GetCountryCodes() []string {
-	if m != nil {
-		return m.CountryCodes
+func (x *UserCountriesTargeting) GetCountryCodes() []string {
+	if x != nil {
+		return x.CountryCodes
 	}
 	return nil
 }
 
-func (m *UserCountriesTargeting) GetExclude() bool {
-	if m != nil {
-		return m.Exclude
+func (x *UserCountriesTargeting) GetExclude() bool {
+	if x != nil {
+		return x.Exclude
 	}
 	return false
 }
 
 type ScreenDensity struct {
-	// Types that are valid to be assigned to DensityOneof:
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Types that are assignable to DensityOneof:
 	//	*ScreenDensity_DensityAlias_
 	//	*ScreenDensity_DensityDpi
-	DensityOneof         isScreenDensity_DensityOneof `protobuf_oneof:"density_oneof"`
-	XXX_NoUnkeyedLiteral struct{}                     `json:"-"`
-	XXX_unrecognized     []byte                       `json:"-"`
-	XXX_sizecache        int32                        `json:"-"`
+	DensityOneof isScreenDensity_DensityOneof `protobuf_oneof:"density_oneof"`
 }
 
-func (m *ScreenDensity) Reset()         { *m = ScreenDensity{} }
-func (m *ScreenDensity) String() string { return proto.CompactTextString(m) }
-func (*ScreenDensity) ProtoMessage()    {}
+func (x *ScreenDensity) Reset() {
+	*x = ScreenDensity{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_targeting_proto_msgTypes[4]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ScreenDensity) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ScreenDensity) ProtoMessage() {}
+
+func (x *ScreenDensity) ProtoReflect() protoreflect.Message {
+	mi := &file_targeting_proto_msgTypes[4]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ScreenDensity.ProtoReflect.Descriptor instead.
 func (*ScreenDensity) Descriptor() ([]byte, []int) {
-	return fileDescriptor_df45b505afdf471e, []int{4}
+	return file_targeting_proto_rawDescGZIP(), []int{4}
 }
 
-func (m *ScreenDensity) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_ScreenDensity.Unmarshal(m, b)
-}
-func (m *ScreenDensity) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_ScreenDensity.Marshal(b, m, deterministic)
-}
-func (m *ScreenDensity) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_ScreenDensity.Merge(m, src)
-}
-func (m *ScreenDensity) XXX_Size() int {
-	return xxx_messageInfo_ScreenDensity.Size(m)
-}
-func (m *ScreenDensity) XXX_DiscardUnknown() {
-	xxx_messageInfo_ScreenDensity.DiscardUnknown(m)
+func (m *ScreenDensity) GetDensityOneof() isScreenDensity_DensityOneof {
+	if m != nil {
+		return m.DensityOneof
+	}
+	return nil
 }
 
-var xxx_messageInfo_ScreenDensity proto.InternalMessageInfo
+func (x *ScreenDensity) GetDensityAlias() ScreenDensity_DensityAlias {
+	if x, ok := x.GetDensityOneof().(*ScreenDensity_DensityAlias_); ok {
+		return x.DensityAlias
+	}
+	return ScreenDensity_DENSITY_UNSPECIFIED
+}
+
+func (x *ScreenDensity) GetDensityDpi() int32 {
+	if x, ok := x.GetDensityOneof().(*ScreenDensity_DensityDpi); ok {
+		return x.DensityDpi
+	}
+	return 0
+}
 
 type isScreenDensity_DensityOneof interface {
 	isScreenDensity_DensityOneof()
@@ -513,169 +663,148 @@
 
 func (*ScreenDensity_DensityDpi) isScreenDensity_DensityOneof() {}
 
-func (m *ScreenDensity) GetDensityOneof() isScreenDensity_DensityOneof {
-	if m != nil {
-		return m.DensityOneof
-	}
-	return nil
-}
-
-func (m *ScreenDensity) GetDensityAlias() ScreenDensity_DensityAlias {
-	if x, ok := m.GetDensityOneof().(*ScreenDensity_DensityAlias_); ok {
-		return x.DensityAlias
-	}
-	return ScreenDensity_DENSITY_UNSPECIFIED
-}
-
-func (m *ScreenDensity) GetDensityDpi() int32 {
-	if x, ok := m.GetDensityOneof().(*ScreenDensity_DensityDpi); ok {
-		return x.DensityDpi
-	}
-	return 0
-}
-
-// XXX_OneofWrappers is for the internal use of the proto package.
-func (*ScreenDensity) XXX_OneofWrappers() []interface{} {
-	return []interface{}{
-		(*ScreenDensity_DensityAlias_)(nil),
-		(*ScreenDensity_DensityDpi)(nil),
-	}
-}
-
 // Wrapper message for `int32`.
 //
 // The JSON representation for `Int32Value` is JSON number.
 type Int32Value struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	// The int32 value.
-	Value                int32    `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
+	Value int32 `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"`
 }
 
-func (m *Int32Value) Reset()         { *m = Int32Value{} }
-func (m *Int32Value) String() string { return proto.CompactTextString(m) }
-func (*Int32Value) ProtoMessage()    {}
+func (x *Int32Value) Reset() {
+	*x = Int32Value{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_targeting_proto_msgTypes[5]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Int32Value) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Int32Value) ProtoMessage() {}
+
+func (x *Int32Value) ProtoReflect() protoreflect.Message {
+	mi := &file_targeting_proto_msgTypes[5]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Int32Value.ProtoReflect.Descriptor instead.
 func (*Int32Value) Descriptor() ([]byte, []int) {
-	return fileDescriptor_df45b505afdf471e, []int{5}
+	return file_targeting_proto_rawDescGZIP(), []int{5}
 }
 
-func (m *Int32Value) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_Int32Value.Unmarshal(m, b)
-}
-func (m *Int32Value) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_Int32Value.Marshal(b, m, deterministic)
-}
-func (m *Int32Value) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_Int32Value.Merge(m, src)
-}
-func (m *Int32Value) XXX_Size() int {
-	return xxx_messageInfo_Int32Value.Size(m)
-}
-func (m *Int32Value) XXX_DiscardUnknown() {
-	xxx_messageInfo_Int32Value.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_Int32Value proto.InternalMessageInfo
-
-func (m *Int32Value) GetValue() int32 {
-	if m != nil {
-		return m.Value
+func (x *Int32Value) GetValue() int32 {
+	if x != nil {
+		return x.Value
 	}
 	return 0
 }
 
 type SdkVersion struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	// Inclusive.
-	Min                  *Int32Value `protobuf:"bytes,1,opt,name=min,proto3" json:"min,omitempty"`
-	XXX_NoUnkeyedLiteral struct{}    `json:"-"`
-	XXX_unrecognized     []byte      `json:"-"`
-	XXX_sizecache        int32       `json:"-"`
+	Min *Int32Value `protobuf:"bytes,1,opt,name=min,proto3" json:"min,omitempty"`
 }
 
-func (m *SdkVersion) Reset()         { *m = SdkVersion{} }
-func (m *SdkVersion) String() string { return proto.CompactTextString(m) }
-func (*SdkVersion) ProtoMessage()    {}
+func (x *SdkVersion) Reset() {
+	*x = SdkVersion{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_targeting_proto_msgTypes[6]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *SdkVersion) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SdkVersion) ProtoMessage() {}
+
+func (x *SdkVersion) ProtoReflect() protoreflect.Message {
+	mi := &file_targeting_proto_msgTypes[6]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use SdkVersion.ProtoReflect.Descriptor instead.
 func (*SdkVersion) Descriptor() ([]byte, []int) {
-	return fileDescriptor_df45b505afdf471e, []int{6}
+	return file_targeting_proto_rawDescGZIP(), []int{6}
 }
 
-func (m *SdkVersion) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_SdkVersion.Unmarshal(m, b)
-}
-func (m *SdkVersion) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_SdkVersion.Marshal(b, m, deterministic)
-}
-func (m *SdkVersion) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_SdkVersion.Merge(m, src)
-}
-func (m *SdkVersion) XXX_Size() int {
-	return xxx_messageInfo_SdkVersion.Size(m)
-}
-func (m *SdkVersion) XXX_DiscardUnknown() {
-	xxx_messageInfo_SdkVersion.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_SdkVersion proto.InternalMessageInfo
-
-func (m *SdkVersion) GetMin() *Int32Value {
-	if m != nil {
-		return m.Min
+func (x *SdkVersion) GetMin() *Int32Value {
+	if x != nil {
+		return x.Min
 	}
 	return nil
 }
 
 type GraphicsApi struct {
-	// Types that are valid to be assigned to ApiOneof:
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Types that are assignable to ApiOneof:
 	//	*GraphicsApi_MinOpenGlVersion
 	//	*GraphicsApi_MinVulkanVersion
-	ApiOneof             isGraphicsApi_ApiOneof `protobuf_oneof:"api_oneof"`
-	XXX_NoUnkeyedLiteral struct{}               `json:"-"`
-	XXX_unrecognized     []byte                 `json:"-"`
-	XXX_sizecache        int32                  `json:"-"`
+	ApiOneof isGraphicsApi_ApiOneof `protobuf_oneof:"api_oneof"`
 }
 
-func (m *GraphicsApi) Reset()         { *m = GraphicsApi{} }
-func (m *GraphicsApi) String() string { return proto.CompactTextString(m) }
-func (*GraphicsApi) ProtoMessage()    {}
+func (x *GraphicsApi) Reset() {
+	*x = GraphicsApi{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_targeting_proto_msgTypes[7]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *GraphicsApi) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*GraphicsApi) ProtoMessage() {}
+
+func (x *GraphicsApi) ProtoReflect() protoreflect.Message {
+	mi := &file_targeting_proto_msgTypes[7]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use GraphicsApi.ProtoReflect.Descriptor instead.
 func (*GraphicsApi) Descriptor() ([]byte, []int) {
-	return fileDescriptor_df45b505afdf471e, []int{7}
+	return file_targeting_proto_rawDescGZIP(), []int{7}
 }
 
-func (m *GraphicsApi) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_GraphicsApi.Unmarshal(m, b)
-}
-func (m *GraphicsApi) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_GraphicsApi.Marshal(b, m, deterministic)
-}
-func (m *GraphicsApi) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_GraphicsApi.Merge(m, src)
-}
-func (m *GraphicsApi) XXX_Size() int {
-	return xxx_messageInfo_GraphicsApi.Size(m)
-}
-func (m *GraphicsApi) XXX_DiscardUnknown() {
-	xxx_messageInfo_GraphicsApi.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_GraphicsApi proto.InternalMessageInfo
-
-type isGraphicsApi_ApiOneof interface {
-	isGraphicsApi_ApiOneof()
-}
-
-type GraphicsApi_MinOpenGlVersion struct {
-	MinOpenGlVersion *OpenGlVersion `protobuf:"bytes,1,opt,name=min_open_gl_version,json=minOpenGlVersion,proto3,oneof"`
-}
-
-type GraphicsApi_MinVulkanVersion struct {
-	MinVulkanVersion *VulkanVersion `protobuf:"bytes,2,opt,name=min_vulkan_version,json=minVulkanVersion,proto3,oneof"`
-}
-
-func (*GraphicsApi_MinOpenGlVersion) isGraphicsApi_ApiOneof() {}
-
-func (*GraphicsApi_MinVulkanVersion) isGraphicsApi_ApiOneof() {}
-
 func (m *GraphicsApi) GetApiOneof() isGraphicsApi_ApiOneof {
 	if m != nil {
 		return m.ApiOneof
@@ -683,874 +812,1028 @@
 	return nil
 }
 
-func (m *GraphicsApi) GetMinOpenGlVersion() *OpenGlVersion {
-	if x, ok := m.GetApiOneof().(*GraphicsApi_MinOpenGlVersion); ok {
+func (x *GraphicsApi) GetMinOpenGlVersion() *OpenGlVersion {
+	if x, ok := x.GetApiOneof().(*GraphicsApi_MinOpenGlVersion); ok {
 		return x.MinOpenGlVersion
 	}
 	return nil
 }
 
-func (m *GraphicsApi) GetMinVulkanVersion() *VulkanVersion {
-	if x, ok := m.GetApiOneof().(*GraphicsApi_MinVulkanVersion); ok {
+func (x *GraphicsApi) GetMinVulkanVersion() *VulkanVersion {
+	if x, ok := x.GetApiOneof().(*GraphicsApi_MinVulkanVersion); ok {
 		return x.MinVulkanVersion
 	}
 	return nil
 }
 
-// XXX_OneofWrappers is for the internal use of the proto package.
-func (*GraphicsApi) XXX_OneofWrappers() []interface{} {
-	return []interface{}{
-		(*GraphicsApi_MinOpenGlVersion)(nil),
-		(*GraphicsApi_MinVulkanVersion)(nil),
+type isGraphicsApi_ApiOneof interface {
+	isGraphicsApi_ApiOneof()
+}
+
+type GraphicsApi_MinOpenGlVersion struct {
+	// Inclusive.
+	MinOpenGlVersion *OpenGlVersion `protobuf:"bytes,1,opt,name=min_open_gl_version,json=minOpenGlVersion,proto3,oneof"`
+}
+
+type GraphicsApi_MinVulkanVersion struct {
+	// Inclusive.
+	MinVulkanVersion *VulkanVersion `protobuf:"bytes,2,opt,name=min_vulkan_version,json=minVulkanVersion,proto3,oneof"`
+}
+
+func (*GraphicsApi_MinOpenGlVersion) isGraphicsApi_ApiOneof() {}
+
+func (*GraphicsApi_MinVulkanVersion) isGraphicsApi_ApiOneof() {}
+
+type VulkanVersion struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Major int32 `protobuf:"varint,1,opt,name=major,proto3" json:"major,omitempty"` // VK_VERSION_MAJOR
+	Minor int32 `protobuf:"varint,2,opt,name=minor,proto3" json:"minor,omitempty"` // VK_VERSION_MINOR
+}
+
+func (x *VulkanVersion) Reset() {
+	*x = VulkanVersion{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_targeting_proto_msgTypes[8]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
 	}
 }
 
-type VulkanVersion struct {
-	Major                int32    `protobuf:"varint,1,opt,name=major,proto3" json:"major,omitempty"`
-	Minor                int32    `protobuf:"varint,2,opt,name=minor,proto3" json:"minor,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
+func (x *VulkanVersion) String() string {
+	return protoimpl.X.MessageStringOf(x)
 }
 
-func (m *VulkanVersion) Reset()         { *m = VulkanVersion{} }
-func (m *VulkanVersion) String() string { return proto.CompactTextString(m) }
-func (*VulkanVersion) ProtoMessage()    {}
+func (*VulkanVersion) ProtoMessage() {}
+
+func (x *VulkanVersion) ProtoReflect() protoreflect.Message {
+	mi := &file_targeting_proto_msgTypes[8]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use VulkanVersion.ProtoReflect.Descriptor instead.
 func (*VulkanVersion) Descriptor() ([]byte, []int) {
-	return fileDescriptor_df45b505afdf471e, []int{8}
+	return file_targeting_proto_rawDescGZIP(), []int{8}
 }
 
-func (m *VulkanVersion) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_VulkanVersion.Unmarshal(m, b)
-}
-func (m *VulkanVersion) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_VulkanVersion.Marshal(b, m, deterministic)
-}
-func (m *VulkanVersion) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_VulkanVersion.Merge(m, src)
-}
-func (m *VulkanVersion) XXX_Size() int {
-	return xxx_messageInfo_VulkanVersion.Size(m)
-}
-func (m *VulkanVersion) XXX_DiscardUnknown() {
-	xxx_messageInfo_VulkanVersion.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_VulkanVersion proto.InternalMessageInfo
-
-func (m *VulkanVersion) GetMajor() int32 {
-	if m != nil {
-		return m.Major
+func (x *VulkanVersion) GetMajor() int32 {
+	if x != nil {
+		return x.Major
 	}
 	return 0
 }
 
-func (m *VulkanVersion) GetMinor() int32 {
-	if m != nil {
-		return m.Minor
+func (x *VulkanVersion) GetMinor() int32 {
+	if x != nil {
+		return x.Minor
 	}
 	return 0
 }
 
 type OpenGlVersion struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	// e.g. OpenGL ES 3.2 is represented as { major: 3, minor: 2 }
-	Major                int32    `protobuf:"varint,1,opt,name=major,proto3" json:"major,omitempty"`
-	Minor                int32    `protobuf:"varint,2,opt,name=minor,proto3" json:"minor,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
+	Major int32 `protobuf:"varint,1,opt,name=major,proto3" json:"major,omitempty"` // GL_MAJOR_VERSION
+	Minor int32 `protobuf:"varint,2,opt,name=minor,proto3" json:"minor,omitempty"` // GL_MINOR_VERSION
 }
 
-func (m *OpenGlVersion) Reset()         { *m = OpenGlVersion{} }
-func (m *OpenGlVersion) String() string { return proto.CompactTextString(m) }
-func (*OpenGlVersion) ProtoMessage()    {}
+func (x *OpenGlVersion) Reset() {
+	*x = OpenGlVersion{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_targeting_proto_msgTypes[9]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *OpenGlVersion) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*OpenGlVersion) ProtoMessage() {}
+
+func (x *OpenGlVersion) ProtoReflect() protoreflect.Message {
+	mi := &file_targeting_proto_msgTypes[9]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use OpenGlVersion.ProtoReflect.Descriptor instead.
 func (*OpenGlVersion) Descriptor() ([]byte, []int) {
-	return fileDescriptor_df45b505afdf471e, []int{9}
+	return file_targeting_proto_rawDescGZIP(), []int{9}
 }
 
-func (m *OpenGlVersion) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_OpenGlVersion.Unmarshal(m, b)
-}
-func (m *OpenGlVersion) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_OpenGlVersion.Marshal(b, m, deterministic)
-}
-func (m *OpenGlVersion) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_OpenGlVersion.Merge(m, src)
-}
-func (m *OpenGlVersion) XXX_Size() int {
-	return xxx_messageInfo_OpenGlVersion.Size(m)
-}
-func (m *OpenGlVersion) XXX_DiscardUnknown() {
-	xxx_messageInfo_OpenGlVersion.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_OpenGlVersion proto.InternalMessageInfo
-
-func (m *OpenGlVersion) GetMajor() int32 {
-	if m != nil {
-		return m.Major
+func (x *OpenGlVersion) GetMajor() int32 {
+	if x != nil {
+		return x.Major
 	}
 	return 0
 }
 
-func (m *OpenGlVersion) GetMinor() int32 {
-	if m != nil {
-		return m.Minor
+func (x *OpenGlVersion) GetMinor() int32 {
+	if x != nil {
+		return x.Minor
 	}
 	return 0
 }
 
 type TextureCompressionFormat struct {
-	Alias                TextureCompressionFormat_TextureCompressionFormatAlias `protobuf:"varint,1,opt,name=alias,proto3,enum=android.bundle.TextureCompressionFormat_TextureCompressionFormatAlias" json:"alias,omitempty"`
-	XXX_NoUnkeyedLiteral struct{}                                               `json:"-"`
-	XXX_unrecognized     []byte                                                 `json:"-"`
-	XXX_sizecache        int32                                                  `json:"-"`
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Alias TextureCompressionFormat_TextureCompressionFormatAlias `protobuf:"varint,1,opt,name=alias,proto3,enum=android.bundle.TextureCompressionFormat_TextureCompressionFormatAlias" json:"alias,omitempty"`
 }
 
-func (m *TextureCompressionFormat) Reset()         { *m = TextureCompressionFormat{} }
-func (m *TextureCompressionFormat) String() string { return proto.CompactTextString(m) }
-func (*TextureCompressionFormat) ProtoMessage()    {}
+func (x *TextureCompressionFormat) Reset() {
+	*x = TextureCompressionFormat{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_targeting_proto_msgTypes[10]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *TextureCompressionFormat) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*TextureCompressionFormat) ProtoMessage() {}
+
+func (x *TextureCompressionFormat) ProtoReflect() protoreflect.Message {
+	mi := &file_targeting_proto_msgTypes[10]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use TextureCompressionFormat.ProtoReflect.Descriptor instead.
 func (*TextureCompressionFormat) Descriptor() ([]byte, []int) {
-	return fileDescriptor_df45b505afdf471e, []int{10}
+	return file_targeting_proto_rawDescGZIP(), []int{10}
 }
 
-func (m *TextureCompressionFormat) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_TextureCompressionFormat.Unmarshal(m, b)
-}
-func (m *TextureCompressionFormat) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_TextureCompressionFormat.Marshal(b, m, deterministic)
-}
-func (m *TextureCompressionFormat) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_TextureCompressionFormat.Merge(m, src)
-}
-func (m *TextureCompressionFormat) XXX_Size() int {
-	return xxx_messageInfo_TextureCompressionFormat.Size(m)
-}
-func (m *TextureCompressionFormat) XXX_DiscardUnknown() {
-	xxx_messageInfo_TextureCompressionFormat.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_TextureCompressionFormat proto.InternalMessageInfo
-
-func (m *TextureCompressionFormat) GetAlias() TextureCompressionFormat_TextureCompressionFormatAlias {
-	if m != nil {
-		return m.Alias
+func (x *TextureCompressionFormat) GetAlias() TextureCompressionFormat_TextureCompressionFormatAlias {
+	if x != nil {
+		return x.Alias
 	}
 	return TextureCompressionFormat_UNSPECIFIED_TEXTURE_COMPRESSION_FORMAT
 }
 
 type Abi struct {
-	Alias                Abi_AbiAlias `protobuf:"varint,1,opt,name=alias,proto3,enum=android.bundle.Abi_AbiAlias" json:"alias,omitempty"`
-	XXX_NoUnkeyedLiteral struct{}     `json:"-"`
-	XXX_unrecognized     []byte       `json:"-"`
-	XXX_sizecache        int32        `json:"-"`
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Alias Abi_AbiAlias `protobuf:"varint,1,opt,name=alias,proto3,enum=android.bundle.Abi_AbiAlias" json:"alias,omitempty"`
 }
 
-func (m *Abi) Reset()         { *m = Abi{} }
-func (m *Abi) String() string { return proto.CompactTextString(m) }
-func (*Abi) ProtoMessage()    {}
+func (x *Abi) Reset() {
+	*x = Abi{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_targeting_proto_msgTypes[11]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Abi) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Abi) ProtoMessage() {}
+
+func (x *Abi) ProtoReflect() protoreflect.Message {
+	mi := &file_targeting_proto_msgTypes[11]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Abi.ProtoReflect.Descriptor instead.
 func (*Abi) Descriptor() ([]byte, []int) {
-	return fileDescriptor_df45b505afdf471e, []int{11}
+	return file_targeting_proto_rawDescGZIP(), []int{11}
 }
 
-func (m *Abi) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_Abi.Unmarshal(m, b)
-}
-func (m *Abi) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_Abi.Marshal(b, m, deterministic)
-}
-func (m *Abi) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_Abi.Merge(m, src)
-}
-func (m *Abi) XXX_Size() int {
-	return xxx_messageInfo_Abi.Size(m)
-}
-func (m *Abi) XXX_DiscardUnknown() {
-	xxx_messageInfo_Abi.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_Abi proto.InternalMessageInfo
-
-func (m *Abi) GetAlias() Abi_AbiAlias {
-	if m != nil {
-		return m.Alias
+func (x *Abi) GetAlias() Abi_AbiAlias {
+	if x != nil {
+		return x.Alias
 	}
 	return Abi_UNSPECIFIED_CPU_ARCHITECTURE
 }
 
 type MultiAbi struct {
-	Abi                  []*Abi   `protobuf:"bytes,1,rep,name=abi,proto3" json:"abi,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Abi []*Abi `protobuf:"bytes,1,rep,name=abi,proto3" json:"abi,omitempty"`
 }
 
-func (m *MultiAbi) Reset()         { *m = MultiAbi{} }
-func (m *MultiAbi) String() string { return proto.CompactTextString(m) }
-func (*MultiAbi) ProtoMessage()    {}
+func (x *MultiAbi) Reset() {
+	*x = MultiAbi{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_targeting_proto_msgTypes[12]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *MultiAbi) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*MultiAbi) ProtoMessage() {}
+
+func (x *MultiAbi) ProtoReflect() protoreflect.Message {
+	mi := &file_targeting_proto_msgTypes[12]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use MultiAbi.ProtoReflect.Descriptor instead.
 func (*MultiAbi) Descriptor() ([]byte, []int) {
-	return fileDescriptor_df45b505afdf471e, []int{12}
+	return file_targeting_proto_rawDescGZIP(), []int{12}
 }
 
-func (m *MultiAbi) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_MultiAbi.Unmarshal(m, b)
-}
-func (m *MultiAbi) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_MultiAbi.Marshal(b, m, deterministic)
-}
-func (m *MultiAbi) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_MultiAbi.Merge(m, src)
-}
-func (m *MultiAbi) XXX_Size() int {
-	return xxx_messageInfo_MultiAbi.Size(m)
-}
-func (m *MultiAbi) XXX_DiscardUnknown() {
-	xxx_messageInfo_MultiAbi.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_MultiAbi proto.InternalMessageInfo
-
-func (m *MultiAbi) GetAbi() []*Abi {
-	if m != nil {
-		return m.Abi
+func (x *MultiAbi) GetAbi() []*Abi {
+	if x != nil {
+		return x.Abi
 	}
 	return nil
 }
 
 type Sanitizer struct {
-	Alias                Sanitizer_SanitizerAlias `protobuf:"varint,1,opt,name=alias,proto3,enum=android.bundle.Sanitizer_SanitizerAlias" json:"alias,omitempty"`
-	XXX_NoUnkeyedLiteral struct{}                 `json:"-"`
-	XXX_unrecognized     []byte                   `json:"-"`
-	XXX_sizecache        int32                    `json:"-"`
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Alias Sanitizer_SanitizerAlias `protobuf:"varint,1,opt,name=alias,proto3,enum=android.bundle.Sanitizer_SanitizerAlias" json:"alias,omitempty"`
 }
 
-func (m *Sanitizer) Reset()         { *m = Sanitizer{} }
-func (m *Sanitizer) String() string { return proto.CompactTextString(m) }
-func (*Sanitizer) ProtoMessage()    {}
+func (x *Sanitizer) Reset() {
+	*x = Sanitizer{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_targeting_proto_msgTypes[13]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Sanitizer) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Sanitizer) ProtoMessage() {}
+
+func (x *Sanitizer) ProtoReflect() protoreflect.Message {
+	mi := &file_targeting_proto_msgTypes[13]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Sanitizer.ProtoReflect.Descriptor instead.
 func (*Sanitizer) Descriptor() ([]byte, []int) {
-	return fileDescriptor_df45b505afdf471e, []int{13}
+	return file_targeting_proto_rawDescGZIP(), []int{13}
 }
 
-func (m *Sanitizer) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_Sanitizer.Unmarshal(m, b)
-}
-func (m *Sanitizer) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_Sanitizer.Marshal(b, m, deterministic)
-}
-func (m *Sanitizer) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_Sanitizer.Merge(m, src)
-}
-func (m *Sanitizer) XXX_Size() int {
-	return xxx_messageInfo_Sanitizer.Size(m)
-}
-func (m *Sanitizer) XXX_DiscardUnknown() {
-	xxx_messageInfo_Sanitizer.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_Sanitizer proto.InternalMessageInfo
-
-func (m *Sanitizer) GetAlias() Sanitizer_SanitizerAlias {
-	if m != nil {
-		return m.Alias
+func (x *Sanitizer) GetAlias() Sanitizer_SanitizerAlias {
+	if x != nil {
+		return x.Alias
 	}
 	return Sanitizer_NONE
 }
 
 type DeviceFeature struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	FeatureName string `protobuf:"bytes,1,opt,name=feature_name,json=featureName,proto3" json:"feature_name,omitempty"`
 	// Equivalent of android:glEsVersion or android:version in <uses-feature>.
-	FeatureVersion       int32    `protobuf:"varint,2,opt,name=feature_version,json=featureVersion,proto3" json:"feature_version,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
+	FeatureVersion int32 `protobuf:"varint,2,opt,name=feature_version,json=featureVersion,proto3" json:"feature_version,omitempty"`
 }
 
-func (m *DeviceFeature) Reset()         { *m = DeviceFeature{} }
-func (m *DeviceFeature) String() string { return proto.CompactTextString(m) }
-func (*DeviceFeature) ProtoMessage()    {}
+func (x *DeviceFeature) Reset() {
+	*x = DeviceFeature{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_targeting_proto_msgTypes[14]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *DeviceFeature) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DeviceFeature) ProtoMessage() {}
+
+func (x *DeviceFeature) ProtoReflect() protoreflect.Message {
+	mi := &file_targeting_proto_msgTypes[14]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use DeviceFeature.ProtoReflect.Descriptor instead.
 func (*DeviceFeature) Descriptor() ([]byte, []int) {
-	return fileDescriptor_df45b505afdf471e, []int{14}
+	return file_targeting_proto_rawDescGZIP(), []int{14}
 }
 
-func (m *DeviceFeature) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_DeviceFeature.Unmarshal(m, b)
-}
-func (m *DeviceFeature) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_DeviceFeature.Marshal(b, m, deterministic)
-}
-func (m *DeviceFeature) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_DeviceFeature.Merge(m, src)
-}
-func (m *DeviceFeature) XXX_Size() int {
-	return xxx_messageInfo_DeviceFeature.Size(m)
-}
-func (m *DeviceFeature) XXX_DiscardUnknown() {
-	xxx_messageInfo_DeviceFeature.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_DeviceFeature proto.InternalMessageInfo
-
-func (m *DeviceFeature) GetFeatureName() string {
-	if m != nil {
-		return m.FeatureName
+func (x *DeviceFeature) GetFeatureName() string {
+	if x != nil {
+		return x.FeatureName
 	}
 	return ""
 }
 
-func (m *DeviceFeature) GetFeatureVersion() int32 {
-	if m != nil {
-		return m.FeatureVersion
+func (x *DeviceFeature) GetFeatureVersion() int32 {
+	if x != nil {
+		return x.FeatureVersion
 	}
 	return 0
 }
 
 // Targeting specific for directories under assets/.
 type AssetsDirectoryTargeting struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	Abi                      *AbiTargeting                      `protobuf:"bytes,1,opt,name=abi,proto3" json:"abi,omitempty"`
 	GraphicsApi              *GraphicsApiTargeting              `protobuf:"bytes,2,opt,name=graphics_api,json=graphicsApi,proto3" json:"graphics_api,omitempty"`
 	TextureCompressionFormat *TextureCompressionFormatTargeting `protobuf:"bytes,3,opt,name=texture_compression_format,json=textureCompressionFormat,proto3" json:"texture_compression_format,omitempty"`
 	Language                 *LanguageTargeting                 `protobuf:"bytes,4,opt,name=language,proto3" json:"language,omitempty"`
-	XXX_NoUnkeyedLiteral     struct{}                           `json:"-"`
-	XXX_unrecognized         []byte                             `json:"-"`
-	XXX_sizecache            int32                              `json:"-"`
 }
 
-func (m *AssetsDirectoryTargeting) Reset()         { *m = AssetsDirectoryTargeting{} }
-func (m *AssetsDirectoryTargeting) String() string { return proto.CompactTextString(m) }
-func (*AssetsDirectoryTargeting) ProtoMessage()    {}
+func (x *AssetsDirectoryTargeting) Reset() {
+	*x = AssetsDirectoryTargeting{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_targeting_proto_msgTypes[15]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *AssetsDirectoryTargeting) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*AssetsDirectoryTargeting) ProtoMessage() {}
+
+func (x *AssetsDirectoryTargeting) ProtoReflect() protoreflect.Message {
+	mi := &file_targeting_proto_msgTypes[15]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use AssetsDirectoryTargeting.ProtoReflect.Descriptor instead.
 func (*AssetsDirectoryTargeting) Descriptor() ([]byte, []int) {
-	return fileDescriptor_df45b505afdf471e, []int{15}
+	return file_targeting_proto_rawDescGZIP(), []int{15}
 }
 
-func (m *AssetsDirectoryTargeting) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_AssetsDirectoryTargeting.Unmarshal(m, b)
-}
-func (m *AssetsDirectoryTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_AssetsDirectoryTargeting.Marshal(b, m, deterministic)
-}
-func (m *AssetsDirectoryTargeting) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_AssetsDirectoryTargeting.Merge(m, src)
-}
-func (m *AssetsDirectoryTargeting) XXX_Size() int {
-	return xxx_messageInfo_AssetsDirectoryTargeting.Size(m)
-}
-func (m *AssetsDirectoryTargeting) XXX_DiscardUnknown() {
-	xxx_messageInfo_AssetsDirectoryTargeting.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_AssetsDirectoryTargeting proto.InternalMessageInfo
-
-func (m *AssetsDirectoryTargeting) GetAbi() *AbiTargeting {
-	if m != nil {
-		return m.Abi
+func (x *AssetsDirectoryTargeting) GetAbi() *AbiTargeting {
+	if x != nil {
+		return x.Abi
 	}
 	return nil
 }
 
-func (m *AssetsDirectoryTargeting) GetGraphicsApi() *GraphicsApiTargeting {
-	if m != nil {
-		return m.GraphicsApi
+func (x *AssetsDirectoryTargeting) GetGraphicsApi() *GraphicsApiTargeting {
+	if x != nil {
+		return x.GraphicsApi
 	}
 	return nil
 }
 
-func (m *AssetsDirectoryTargeting) GetTextureCompressionFormat() *TextureCompressionFormatTargeting {
-	if m != nil {
-		return m.TextureCompressionFormat
+func (x *AssetsDirectoryTargeting) GetTextureCompressionFormat() *TextureCompressionFormatTargeting {
+	if x != nil {
+		return x.TextureCompressionFormat
 	}
 	return nil
 }
 
-func (m *AssetsDirectoryTargeting) GetLanguage() *LanguageTargeting {
-	if m != nil {
-		return m.Language
+func (x *AssetsDirectoryTargeting) GetLanguage() *LanguageTargeting {
+	if x != nil {
+		return x.Language
 	}
 	return nil
 }
 
 // Targeting specific for directories under lib/.
 type NativeDirectoryTargeting struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	Abi                      *Abi                      `protobuf:"bytes,1,opt,name=abi,proto3" json:"abi,omitempty"`
 	GraphicsApi              *GraphicsApi              `protobuf:"bytes,2,opt,name=graphics_api,json=graphicsApi,proto3" json:"graphics_api,omitempty"`
 	TextureCompressionFormat *TextureCompressionFormat `protobuf:"bytes,3,opt,name=texture_compression_format,json=textureCompressionFormat,proto3" json:"texture_compression_format,omitempty"`
 	Sanitizer                *Sanitizer                `protobuf:"bytes,4,opt,name=sanitizer,proto3" json:"sanitizer,omitempty"`
-	XXX_NoUnkeyedLiteral     struct{}                  `json:"-"`
-	XXX_unrecognized         []byte                    `json:"-"`
-	XXX_sizecache            int32                     `json:"-"`
 }
 
-func (m *NativeDirectoryTargeting) Reset()         { *m = NativeDirectoryTargeting{} }
-func (m *NativeDirectoryTargeting) String() string { return proto.CompactTextString(m) }
-func (*NativeDirectoryTargeting) ProtoMessage()    {}
+func (x *NativeDirectoryTargeting) Reset() {
+	*x = NativeDirectoryTargeting{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_targeting_proto_msgTypes[16]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *NativeDirectoryTargeting) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*NativeDirectoryTargeting) ProtoMessage() {}
+
+func (x *NativeDirectoryTargeting) ProtoReflect() protoreflect.Message {
+	mi := &file_targeting_proto_msgTypes[16]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use NativeDirectoryTargeting.ProtoReflect.Descriptor instead.
 func (*NativeDirectoryTargeting) Descriptor() ([]byte, []int) {
-	return fileDescriptor_df45b505afdf471e, []int{16}
+	return file_targeting_proto_rawDescGZIP(), []int{16}
 }
 
-func (m *NativeDirectoryTargeting) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_NativeDirectoryTargeting.Unmarshal(m, b)
-}
-func (m *NativeDirectoryTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_NativeDirectoryTargeting.Marshal(b, m, deterministic)
-}
-func (m *NativeDirectoryTargeting) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_NativeDirectoryTargeting.Merge(m, src)
-}
-func (m *NativeDirectoryTargeting) XXX_Size() int {
-	return xxx_messageInfo_NativeDirectoryTargeting.Size(m)
-}
-func (m *NativeDirectoryTargeting) XXX_DiscardUnknown() {
-	xxx_messageInfo_NativeDirectoryTargeting.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_NativeDirectoryTargeting proto.InternalMessageInfo
-
-func (m *NativeDirectoryTargeting) GetAbi() *Abi {
-	if m != nil {
-		return m.Abi
+func (x *NativeDirectoryTargeting) GetAbi() *Abi {
+	if x != nil {
+		return x.Abi
 	}
 	return nil
 }
 
-func (m *NativeDirectoryTargeting) GetGraphicsApi() *GraphicsApi {
-	if m != nil {
-		return m.GraphicsApi
+func (x *NativeDirectoryTargeting) GetGraphicsApi() *GraphicsApi {
+	if x != nil {
+		return x.GraphicsApi
 	}
 	return nil
 }
 
-func (m *NativeDirectoryTargeting) GetTextureCompressionFormat() *TextureCompressionFormat {
-	if m != nil {
-		return m.TextureCompressionFormat
+func (x *NativeDirectoryTargeting) GetTextureCompressionFormat() *TextureCompressionFormat {
+	if x != nil {
+		return x.TextureCompressionFormat
 	}
 	return nil
 }
 
-func (m *NativeDirectoryTargeting) GetSanitizer() *Sanitizer {
-	if m != nil {
-		return m.Sanitizer
+func (x *NativeDirectoryTargeting) GetSanitizer() *Sanitizer {
+	if x != nil {
+		return x.Sanitizer
 	}
 	return nil
 }
 
 // Targeting specific for image files under apex/.
 type ApexImageTargeting struct {
-	MultiAbi             *MultiAbiTargeting `protobuf:"bytes,1,opt,name=multi_abi,json=multiAbi,proto3" json:"multi_abi,omitempty"`
-	XXX_NoUnkeyedLiteral struct{}           `json:"-"`
-	XXX_unrecognized     []byte             `json:"-"`
-	XXX_sizecache        int32              `json:"-"`
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	MultiAbi *MultiAbiTargeting `protobuf:"bytes,1,opt,name=multi_abi,json=multiAbi,proto3" json:"multi_abi,omitempty"`
 }
 
-func (m *ApexImageTargeting) Reset()         { *m = ApexImageTargeting{} }
-func (m *ApexImageTargeting) String() string { return proto.CompactTextString(m) }
-func (*ApexImageTargeting) ProtoMessage()    {}
+func (x *ApexImageTargeting) Reset() {
+	*x = ApexImageTargeting{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_targeting_proto_msgTypes[17]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ApexImageTargeting) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ApexImageTargeting) ProtoMessage() {}
+
+func (x *ApexImageTargeting) ProtoReflect() protoreflect.Message {
+	mi := &file_targeting_proto_msgTypes[17]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ApexImageTargeting.ProtoReflect.Descriptor instead.
 func (*ApexImageTargeting) Descriptor() ([]byte, []int) {
-	return fileDescriptor_df45b505afdf471e, []int{17}
+	return file_targeting_proto_rawDescGZIP(), []int{17}
 }
 
-func (m *ApexImageTargeting) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_ApexImageTargeting.Unmarshal(m, b)
-}
-func (m *ApexImageTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_ApexImageTargeting.Marshal(b, m, deterministic)
-}
-func (m *ApexImageTargeting) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_ApexImageTargeting.Merge(m, src)
-}
-func (m *ApexImageTargeting) XXX_Size() int {
-	return xxx_messageInfo_ApexImageTargeting.Size(m)
-}
-func (m *ApexImageTargeting) XXX_DiscardUnknown() {
-	xxx_messageInfo_ApexImageTargeting.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_ApexImageTargeting proto.InternalMessageInfo
-
-func (m *ApexImageTargeting) GetMultiAbi() *MultiAbiTargeting {
-	if m != nil {
-		return m.MultiAbi
+func (x *ApexImageTargeting) GetMultiAbi() *MultiAbiTargeting {
+	if x != nil {
+		return x.MultiAbi
 	}
 	return nil
 }
 
 type AbiTargeting struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	Value []*Abi `protobuf:"bytes,1,rep,name=value,proto3" json:"value,omitempty"`
 	// Targeting of other sibling directories that were in the Bundle.
 	// For master splits this is targeting of other master splits.
-	Alternatives         []*Abi   `protobuf:"bytes,2,rep,name=alternatives,proto3" json:"alternatives,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
+	Alternatives []*Abi `protobuf:"bytes,2,rep,name=alternatives,proto3" json:"alternatives,omitempty"`
 }
 
-func (m *AbiTargeting) Reset()         { *m = AbiTargeting{} }
-func (m *AbiTargeting) String() string { return proto.CompactTextString(m) }
-func (*AbiTargeting) ProtoMessage()    {}
+func (x *AbiTargeting) Reset() {
+	*x = AbiTargeting{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_targeting_proto_msgTypes[18]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *AbiTargeting) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*AbiTargeting) ProtoMessage() {}
+
+func (x *AbiTargeting) ProtoReflect() protoreflect.Message {
+	mi := &file_targeting_proto_msgTypes[18]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use AbiTargeting.ProtoReflect.Descriptor instead.
 func (*AbiTargeting) Descriptor() ([]byte, []int) {
-	return fileDescriptor_df45b505afdf471e, []int{18}
+	return file_targeting_proto_rawDescGZIP(), []int{18}
 }
 
-func (m *AbiTargeting) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_AbiTargeting.Unmarshal(m, b)
-}
-func (m *AbiTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_AbiTargeting.Marshal(b, m, deterministic)
-}
-func (m *AbiTargeting) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_AbiTargeting.Merge(m, src)
-}
-func (m *AbiTargeting) XXX_Size() int {
-	return xxx_messageInfo_AbiTargeting.Size(m)
-}
-func (m *AbiTargeting) XXX_DiscardUnknown() {
-	xxx_messageInfo_AbiTargeting.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_AbiTargeting proto.InternalMessageInfo
-
-func (m *AbiTargeting) GetValue() []*Abi {
-	if m != nil {
-		return m.Value
+func (x *AbiTargeting) GetValue() []*Abi {
+	if x != nil {
+		return x.Value
 	}
 	return nil
 }
 
-func (m *AbiTargeting) GetAlternatives() []*Abi {
-	if m != nil {
-		return m.Alternatives
+func (x *AbiTargeting) GetAlternatives() []*Abi {
+	if x != nil {
+		return x.Alternatives
 	}
 	return nil
 }
 
 type MultiAbiTargeting struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	Value []*MultiAbi `protobuf:"bytes,1,rep,name=value,proto3" json:"value,omitempty"`
 	// Targeting of other sibling directories that were in the Bundle.
 	// For master splits this is targeting of other master splits.
-	Alternatives         []*MultiAbi `protobuf:"bytes,2,rep,name=alternatives,proto3" json:"alternatives,omitempty"`
-	XXX_NoUnkeyedLiteral struct{}    `json:"-"`
-	XXX_unrecognized     []byte      `json:"-"`
-	XXX_sizecache        int32       `json:"-"`
+	Alternatives []*MultiAbi `protobuf:"bytes,2,rep,name=alternatives,proto3" json:"alternatives,omitempty"`
 }
 
-func (m *MultiAbiTargeting) Reset()         { *m = MultiAbiTargeting{} }
-func (m *MultiAbiTargeting) String() string { return proto.CompactTextString(m) }
-func (*MultiAbiTargeting) ProtoMessage()    {}
+func (x *MultiAbiTargeting) Reset() {
+	*x = MultiAbiTargeting{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_targeting_proto_msgTypes[19]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *MultiAbiTargeting) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*MultiAbiTargeting) ProtoMessage() {}
+
+func (x *MultiAbiTargeting) ProtoReflect() protoreflect.Message {
+	mi := &file_targeting_proto_msgTypes[19]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use MultiAbiTargeting.ProtoReflect.Descriptor instead.
 func (*MultiAbiTargeting) Descriptor() ([]byte, []int) {
-	return fileDescriptor_df45b505afdf471e, []int{19}
+	return file_targeting_proto_rawDescGZIP(), []int{19}
 }
 
-func (m *MultiAbiTargeting) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_MultiAbiTargeting.Unmarshal(m, b)
-}
-func (m *MultiAbiTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_MultiAbiTargeting.Marshal(b, m, deterministic)
-}
-func (m *MultiAbiTargeting) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_MultiAbiTargeting.Merge(m, src)
-}
-func (m *MultiAbiTargeting) XXX_Size() int {
-	return xxx_messageInfo_MultiAbiTargeting.Size(m)
-}
-func (m *MultiAbiTargeting) XXX_DiscardUnknown() {
-	xxx_messageInfo_MultiAbiTargeting.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_MultiAbiTargeting proto.InternalMessageInfo
-
-func (m *MultiAbiTargeting) GetValue() []*MultiAbi {
-	if m != nil {
-		return m.Value
+func (x *MultiAbiTargeting) GetValue() []*MultiAbi {
+	if x != nil {
+		return x.Value
 	}
 	return nil
 }
 
-func (m *MultiAbiTargeting) GetAlternatives() []*MultiAbi {
-	if m != nil {
-		return m.Alternatives
+func (x *MultiAbiTargeting) GetAlternatives() []*MultiAbi {
+	if x != nil {
+		return x.Alternatives
 	}
 	return nil
 }
 
 type ScreenDensityTargeting struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	Value []*ScreenDensity `protobuf:"bytes,1,rep,name=value,proto3" json:"value,omitempty"`
 	// Targeting of other sibling directories that were in the Bundle.
 	// For master splits this is targeting of other master splits.
-	Alternatives         []*ScreenDensity `protobuf:"bytes,2,rep,name=alternatives,proto3" json:"alternatives,omitempty"`
-	XXX_NoUnkeyedLiteral struct{}         `json:"-"`
-	XXX_unrecognized     []byte           `json:"-"`
-	XXX_sizecache        int32            `json:"-"`
+	Alternatives []*ScreenDensity `protobuf:"bytes,2,rep,name=alternatives,proto3" json:"alternatives,omitempty"`
 }
 
-func (m *ScreenDensityTargeting) Reset()         { *m = ScreenDensityTargeting{} }
-func (m *ScreenDensityTargeting) String() string { return proto.CompactTextString(m) }
-func (*ScreenDensityTargeting) ProtoMessage()    {}
+func (x *ScreenDensityTargeting) Reset() {
+	*x = ScreenDensityTargeting{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_targeting_proto_msgTypes[20]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ScreenDensityTargeting) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ScreenDensityTargeting) ProtoMessage() {}
+
+func (x *ScreenDensityTargeting) ProtoReflect() protoreflect.Message {
+	mi := &file_targeting_proto_msgTypes[20]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ScreenDensityTargeting.ProtoReflect.Descriptor instead.
 func (*ScreenDensityTargeting) Descriptor() ([]byte, []int) {
-	return fileDescriptor_df45b505afdf471e, []int{20}
+	return file_targeting_proto_rawDescGZIP(), []int{20}
 }
 
-func (m *ScreenDensityTargeting) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_ScreenDensityTargeting.Unmarshal(m, b)
-}
-func (m *ScreenDensityTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_ScreenDensityTargeting.Marshal(b, m, deterministic)
-}
-func (m *ScreenDensityTargeting) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_ScreenDensityTargeting.Merge(m, src)
-}
-func (m *ScreenDensityTargeting) XXX_Size() int {
-	return xxx_messageInfo_ScreenDensityTargeting.Size(m)
-}
-func (m *ScreenDensityTargeting) XXX_DiscardUnknown() {
-	xxx_messageInfo_ScreenDensityTargeting.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_ScreenDensityTargeting proto.InternalMessageInfo
-
-func (m *ScreenDensityTargeting) GetValue() []*ScreenDensity {
-	if m != nil {
-		return m.Value
+func (x *ScreenDensityTargeting) GetValue() []*ScreenDensity {
+	if x != nil {
+		return x.Value
 	}
 	return nil
 }
 
-func (m *ScreenDensityTargeting) GetAlternatives() []*ScreenDensity {
-	if m != nil {
-		return m.Alternatives
+func (x *ScreenDensityTargeting) GetAlternatives() []*ScreenDensity {
+	if x != nil {
+		return x.Alternatives
 	}
 	return nil
 }
 
 type LanguageTargeting struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	// ISO-639: 2 or 3 letter language code.
 	Value []string `protobuf:"bytes,1,rep,name=value,proto3" json:"value,omitempty"`
 	// Targeting of other sibling directories that were in the Bundle.
 	// For master splits this is targeting of other master splits.
-	Alternatives         []string `protobuf:"bytes,2,rep,name=alternatives,proto3" json:"alternatives,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
+	Alternatives []string `protobuf:"bytes,2,rep,name=alternatives,proto3" json:"alternatives,omitempty"`
 }
 
-func (m *LanguageTargeting) Reset()         { *m = LanguageTargeting{} }
-func (m *LanguageTargeting) String() string { return proto.CompactTextString(m) }
-func (*LanguageTargeting) ProtoMessage()    {}
+func (x *LanguageTargeting) Reset() {
+	*x = LanguageTargeting{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_targeting_proto_msgTypes[21]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *LanguageTargeting) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*LanguageTargeting) ProtoMessage() {}
+
+func (x *LanguageTargeting) ProtoReflect() protoreflect.Message {
+	mi := &file_targeting_proto_msgTypes[21]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use LanguageTargeting.ProtoReflect.Descriptor instead.
 func (*LanguageTargeting) Descriptor() ([]byte, []int) {
-	return fileDescriptor_df45b505afdf471e, []int{21}
+	return file_targeting_proto_rawDescGZIP(), []int{21}
 }
 
-func (m *LanguageTargeting) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_LanguageTargeting.Unmarshal(m, b)
-}
-func (m *LanguageTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_LanguageTargeting.Marshal(b, m, deterministic)
-}
-func (m *LanguageTargeting) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_LanguageTargeting.Merge(m, src)
-}
-func (m *LanguageTargeting) XXX_Size() int {
-	return xxx_messageInfo_LanguageTargeting.Size(m)
-}
-func (m *LanguageTargeting) XXX_DiscardUnknown() {
-	xxx_messageInfo_LanguageTargeting.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_LanguageTargeting proto.InternalMessageInfo
-
-func (m *LanguageTargeting) GetValue() []string {
-	if m != nil {
-		return m.Value
+func (x *LanguageTargeting) GetValue() []string {
+	if x != nil {
+		return x.Value
 	}
 	return nil
 }
 
-func (m *LanguageTargeting) GetAlternatives() []string {
-	if m != nil {
-		return m.Alternatives
+func (x *LanguageTargeting) GetAlternatives() []string {
+	if x != nil {
+		return x.Alternatives
 	}
 	return nil
 }
 
 type GraphicsApiTargeting struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	Value []*GraphicsApi `protobuf:"bytes,1,rep,name=value,proto3" json:"value,omitempty"`
 	// Targeting of other sibling directories that were in the Bundle.
 	// For master splits this is targeting of other master splits.
-	Alternatives         []*GraphicsApi `protobuf:"bytes,2,rep,name=alternatives,proto3" json:"alternatives,omitempty"`
-	XXX_NoUnkeyedLiteral struct{}       `json:"-"`
-	XXX_unrecognized     []byte         `json:"-"`
-	XXX_sizecache        int32          `json:"-"`
+	Alternatives []*GraphicsApi `protobuf:"bytes,2,rep,name=alternatives,proto3" json:"alternatives,omitempty"`
 }
 
-func (m *GraphicsApiTargeting) Reset()         { *m = GraphicsApiTargeting{} }
-func (m *GraphicsApiTargeting) String() string { return proto.CompactTextString(m) }
-func (*GraphicsApiTargeting) ProtoMessage()    {}
+func (x *GraphicsApiTargeting) Reset() {
+	*x = GraphicsApiTargeting{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_targeting_proto_msgTypes[22]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *GraphicsApiTargeting) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*GraphicsApiTargeting) ProtoMessage() {}
+
+func (x *GraphicsApiTargeting) ProtoReflect() protoreflect.Message {
+	mi := &file_targeting_proto_msgTypes[22]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use GraphicsApiTargeting.ProtoReflect.Descriptor instead.
 func (*GraphicsApiTargeting) Descriptor() ([]byte, []int) {
-	return fileDescriptor_df45b505afdf471e, []int{22}
+	return file_targeting_proto_rawDescGZIP(), []int{22}
 }
 
-func (m *GraphicsApiTargeting) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_GraphicsApiTargeting.Unmarshal(m, b)
-}
-func (m *GraphicsApiTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_GraphicsApiTargeting.Marshal(b, m, deterministic)
-}
-func (m *GraphicsApiTargeting) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_GraphicsApiTargeting.Merge(m, src)
-}
-func (m *GraphicsApiTargeting) XXX_Size() int {
-	return xxx_messageInfo_GraphicsApiTargeting.Size(m)
-}
-func (m *GraphicsApiTargeting) XXX_DiscardUnknown() {
-	xxx_messageInfo_GraphicsApiTargeting.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_GraphicsApiTargeting proto.InternalMessageInfo
-
-func (m *GraphicsApiTargeting) GetValue() []*GraphicsApi {
-	if m != nil {
-		return m.Value
+func (x *GraphicsApiTargeting) GetValue() []*GraphicsApi {
+	if x != nil {
+		return x.Value
 	}
 	return nil
 }
 
-func (m *GraphicsApiTargeting) GetAlternatives() []*GraphicsApi {
-	if m != nil {
-		return m.Alternatives
+func (x *GraphicsApiTargeting) GetAlternatives() []*GraphicsApi {
+	if x != nil {
+		return x.Alternatives
 	}
 	return nil
 }
 
 type SdkVersionTargeting struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	Value []*SdkVersion `protobuf:"bytes,1,rep,name=value,proto3" json:"value,omitempty"`
 	// Targeting of other sibling directories that were in the Bundle.
 	// For master splits this is targeting of other master splits.
-	Alternatives         []*SdkVersion `protobuf:"bytes,2,rep,name=alternatives,proto3" json:"alternatives,omitempty"`
-	XXX_NoUnkeyedLiteral struct{}      `json:"-"`
-	XXX_unrecognized     []byte        `json:"-"`
-	XXX_sizecache        int32         `json:"-"`
+	Alternatives []*SdkVersion `protobuf:"bytes,2,rep,name=alternatives,proto3" json:"alternatives,omitempty"`
 }
 
-func (m *SdkVersionTargeting) Reset()         { *m = SdkVersionTargeting{} }
-func (m *SdkVersionTargeting) String() string { return proto.CompactTextString(m) }
-func (*SdkVersionTargeting) ProtoMessage()    {}
+func (x *SdkVersionTargeting) Reset() {
+	*x = SdkVersionTargeting{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_targeting_proto_msgTypes[23]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *SdkVersionTargeting) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SdkVersionTargeting) ProtoMessage() {}
+
+func (x *SdkVersionTargeting) ProtoReflect() protoreflect.Message {
+	mi := &file_targeting_proto_msgTypes[23]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use SdkVersionTargeting.ProtoReflect.Descriptor instead.
 func (*SdkVersionTargeting) Descriptor() ([]byte, []int) {
-	return fileDescriptor_df45b505afdf471e, []int{23}
+	return file_targeting_proto_rawDescGZIP(), []int{23}
 }
 
-func (m *SdkVersionTargeting) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_SdkVersionTargeting.Unmarshal(m, b)
-}
-func (m *SdkVersionTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_SdkVersionTargeting.Marshal(b, m, deterministic)
-}
-func (m *SdkVersionTargeting) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_SdkVersionTargeting.Merge(m, src)
-}
-func (m *SdkVersionTargeting) XXX_Size() int {
-	return xxx_messageInfo_SdkVersionTargeting.Size(m)
-}
-func (m *SdkVersionTargeting) XXX_DiscardUnknown() {
-	xxx_messageInfo_SdkVersionTargeting.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_SdkVersionTargeting proto.InternalMessageInfo
-
-func (m *SdkVersionTargeting) GetValue() []*SdkVersion {
-	if m != nil {
-		return m.Value
+func (x *SdkVersionTargeting) GetValue() []*SdkVersion {
+	if x != nil {
+		return x.Value
 	}
 	return nil
 }
 
-func (m *SdkVersionTargeting) GetAlternatives() []*SdkVersion {
-	if m != nil {
-		return m.Alternatives
+func (x *SdkVersionTargeting) GetAlternatives() []*SdkVersion {
+	if x != nil {
+		return x.Alternatives
 	}
 	return nil
 }
 
 type TextureCompressionFormatTargeting struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	Value []*TextureCompressionFormat `protobuf:"bytes,1,rep,name=value,proto3" json:"value,omitempty"`
 	// Targeting of other sibling directories that were in the Bundle.
 	// For master splits this is targeting of other master splits.
-	Alternatives         []*TextureCompressionFormat `protobuf:"bytes,2,rep,name=alternatives,proto3" json:"alternatives,omitempty"`
-	XXX_NoUnkeyedLiteral struct{}                    `json:"-"`
-	XXX_unrecognized     []byte                      `json:"-"`
-	XXX_sizecache        int32                       `json:"-"`
+	Alternatives []*TextureCompressionFormat `protobuf:"bytes,2,rep,name=alternatives,proto3" json:"alternatives,omitempty"`
 }
 
-func (m *TextureCompressionFormatTargeting) Reset()         { *m = TextureCompressionFormatTargeting{} }
-func (m *TextureCompressionFormatTargeting) String() string { return proto.CompactTextString(m) }
-func (*TextureCompressionFormatTargeting) ProtoMessage()    {}
+func (x *TextureCompressionFormatTargeting) Reset() {
+	*x = TextureCompressionFormatTargeting{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_targeting_proto_msgTypes[24]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *TextureCompressionFormatTargeting) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*TextureCompressionFormatTargeting) ProtoMessage() {}
+
+func (x *TextureCompressionFormatTargeting) ProtoReflect() protoreflect.Message {
+	mi := &file_targeting_proto_msgTypes[24]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use TextureCompressionFormatTargeting.ProtoReflect.Descriptor instead.
 func (*TextureCompressionFormatTargeting) Descriptor() ([]byte, []int) {
-	return fileDescriptor_df45b505afdf471e, []int{24}
+	return file_targeting_proto_rawDescGZIP(), []int{24}
 }
 
-func (m *TextureCompressionFormatTargeting) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_TextureCompressionFormatTargeting.Unmarshal(m, b)
-}
-func (m *TextureCompressionFormatTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_TextureCompressionFormatTargeting.Marshal(b, m, deterministic)
-}
-func (m *TextureCompressionFormatTargeting) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_TextureCompressionFormatTargeting.Merge(m, src)
-}
-func (m *TextureCompressionFormatTargeting) XXX_Size() int {
-	return xxx_messageInfo_TextureCompressionFormatTargeting.Size(m)
-}
-func (m *TextureCompressionFormatTargeting) XXX_DiscardUnknown() {
-	xxx_messageInfo_TextureCompressionFormatTargeting.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_TextureCompressionFormatTargeting proto.InternalMessageInfo
-
-func (m *TextureCompressionFormatTargeting) GetValue() []*TextureCompressionFormat {
-	if m != nil {
-		return m.Value
+func (x *TextureCompressionFormatTargeting) GetValue() []*TextureCompressionFormat {
+	if x != nil {
+		return x.Value
 	}
 	return nil
 }
 
-func (m *TextureCompressionFormatTargeting) GetAlternatives() []*TextureCompressionFormat {
-	if m != nil {
-		return m.Alternatives
+func (x *TextureCompressionFormatTargeting) GetAlternatives() []*TextureCompressionFormat {
+	if x != nil {
+		return x.Alternatives
 	}
 	return nil
 }
 
 type SanitizerTargeting struct {
-	Value                []*Sanitizer `protobuf:"bytes,1,rep,name=value,proto3" json:"value,omitempty"`
-	XXX_NoUnkeyedLiteral struct{}     `json:"-"`
-	XXX_unrecognized     []byte       `json:"-"`
-	XXX_sizecache        int32        `json:"-"`
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Value []*Sanitizer `protobuf:"bytes,1,rep,name=value,proto3" json:"value,omitempty"`
 }
 
-func (m *SanitizerTargeting) Reset()         { *m = SanitizerTargeting{} }
-func (m *SanitizerTargeting) String() string { return proto.CompactTextString(m) }
-func (*SanitizerTargeting) ProtoMessage()    {}
+func (x *SanitizerTargeting) Reset() {
+	*x = SanitizerTargeting{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_targeting_proto_msgTypes[25]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *SanitizerTargeting) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SanitizerTargeting) ProtoMessage() {}
+
+func (x *SanitizerTargeting) ProtoReflect() protoreflect.Message {
+	mi := &file_targeting_proto_msgTypes[25]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use SanitizerTargeting.ProtoReflect.Descriptor instead.
 func (*SanitizerTargeting) Descriptor() ([]byte, []int) {
-	return fileDescriptor_df45b505afdf471e, []int{25}
+	return file_targeting_proto_rawDescGZIP(), []int{25}
 }
 
-func (m *SanitizerTargeting) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_SanitizerTargeting.Unmarshal(m, b)
-}
-func (m *SanitizerTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_SanitizerTargeting.Marshal(b, m, deterministic)
-}
-func (m *SanitizerTargeting) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_SanitizerTargeting.Merge(m, src)
-}
-func (m *SanitizerTargeting) XXX_Size() int {
-	return xxx_messageInfo_SanitizerTargeting.Size(m)
-}
-func (m *SanitizerTargeting) XXX_DiscardUnknown() {
-	xxx_messageInfo_SanitizerTargeting.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_SanitizerTargeting proto.InternalMessageInfo
-
-func (m *SanitizerTargeting) GetValue() []*Sanitizer {
-	if m != nil {
-		return m.Value
+func (x *SanitizerTargeting) GetValue() []*Sanitizer {
+	if x != nil {
+		return x.Value
 	}
 	return nil
 }
@@ -1559,176 +1842,829 @@
 // the DeviceFeatureTargeting represents only one device feature to retain
 // that convention.
 type DeviceFeatureTargeting struct {
-	RequiredFeature      *DeviceFeature `protobuf:"bytes,1,opt,name=required_feature,json=requiredFeature,proto3" json:"required_feature,omitempty"`
-	XXX_NoUnkeyedLiteral struct{}       `json:"-"`
-	XXX_unrecognized     []byte         `json:"-"`
-	XXX_sizecache        int32          `json:"-"`
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	RequiredFeature *DeviceFeature `protobuf:"bytes,1,opt,name=required_feature,json=requiredFeature,proto3" json:"required_feature,omitempty"`
 }
 
-func (m *DeviceFeatureTargeting) Reset()         { *m = DeviceFeatureTargeting{} }
-func (m *DeviceFeatureTargeting) String() string { return proto.CompactTextString(m) }
-func (*DeviceFeatureTargeting) ProtoMessage()    {}
+func (x *DeviceFeatureTargeting) Reset() {
+	*x = DeviceFeatureTargeting{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_targeting_proto_msgTypes[26]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *DeviceFeatureTargeting) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*DeviceFeatureTargeting) ProtoMessage() {}
+
+func (x *DeviceFeatureTargeting) ProtoReflect() protoreflect.Message {
+	mi := &file_targeting_proto_msgTypes[26]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use DeviceFeatureTargeting.ProtoReflect.Descriptor instead.
 func (*DeviceFeatureTargeting) Descriptor() ([]byte, []int) {
-	return fileDescriptor_df45b505afdf471e, []int{26}
+	return file_targeting_proto_rawDescGZIP(), []int{26}
 }
 
-func (m *DeviceFeatureTargeting) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_DeviceFeatureTargeting.Unmarshal(m, b)
-}
-func (m *DeviceFeatureTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_DeviceFeatureTargeting.Marshal(b, m, deterministic)
-}
-func (m *DeviceFeatureTargeting) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_DeviceFeatureTargeting.Merge(m, src)
-}
-func (m *DeviceFeatureTargeting) XXX_Size() int {
-	return xxx_messageInfo_DeviceFeatureTargeting.Size(m)
-}
-func (m *DeviceFeatureTargeting) XXX_DiscardUnknown() {
-	xxx_messageInfo_DeviceFeatureTargeting.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_DeviceFeatureTargeting proto.InternalMessageInfo
-
-func (m *DeviceFeatureTargeting) GetRequiredFeature() *DeviceFeature {
-	if m != nil {
-		return m.RequiredFeature
+func (x *DeviceFeatureTargeting) GetRequiredFeature() *DeviceFeature {
+	if x != nil {
+		return x.RequiredFeature
 	}
 	return nil
 }
 
-func init() {
-	proto.RegisterEnum("android.bundle.ScreenDensity_DensityAlias", ScreenDensity_DensityAlias_name, ScreenDensity_DensityAlias_value)
-	proto.RegisterEnum("android.bundle.TextureCompressionFormat_TextureCompressionFormatAlias", TextureCompressionFormat_TextureCompressionFormatAlias_name, TextureCompressionFormat_TextureCompressionFormatAlias_value)
-	proto.RegisterEnum("android.bundle.Abi_AbiAlias", Abi_AbiAlias_name, Abi_AbiAlias_value)
-	proto.RegisterEnum("android.bundle.Sanitizer_SanitizerAlias", Sanitizer_SanitizerAlias_name, Sanitizer_SanitizerAlias_value)
-	proto.RegisterType((*VariantTargeting)(nil), "android.bundle.VariantTargeting")
-	proto.RegisterType((*ApkTargeting)(nil), "android.bundle.ApkTargeting")
-	proto.RegisterType((*ModuleTargeting)(nil), "android.bundle.ModuleTargeting")
-	proto.RegisterType((*UserCountriesTargeting)(nil), "android.bundle.UserCountriesTargeting")
-	proto.RegisterType((*ScreenDensity)(nil), "android.bundle.ScreenDensity")
-	proto.RegisterType((*Int32Value)(nil), "android.bundle.Int32Value")
-	proto.RegisterType((*SdkVersion)(nil), "android.bundle.SdkVersion")
-	proto.RegisterType((*GraphicsApi)(nil), "android.bundle.GraphicsApi")
-	proto.RegisterType((*VulkanVersion)(nil), "android.bundle.VulkanVersion")
-	proto.RegisterType((*OpenGlVersion)(nil), "android.bundle.OpenGlVersion")
-	proto.RegisterType((*TextureCompressionFormat)(nil), "android.bundle.TextureCompressionFormat")
-	proto.RegisterType((*Abi)(nil), "android.bundle.Abi")
-	proto.RegisterType((*MultiAbi)(nil), "android.bundle.MultiAbi")
-	proto.RegisterType((*Sanitizer)(nil), "android.bundle.Sanitizer")
-	proto.RegisterType((*DeviceFeature)(nil), "android.bundle.DeviceFeature")
-	proto.RegisterType((*AssetsDirectoryTargeting)(nil), "android.bundle.AssetsDirectoryTargeting")
-	proto.RegisterType((*NativeDirectoryTargeting)(nil), "android.bundle.NativeDirectoryTargeting")
-	proto.RegisterType((*ApexImageTargeting)(nil), "android.bundle.ApexImageTargeting")
-	proto.RegisterType((*AbiTargeting)(nil), "android.bundle.AbiTargeting")
-	proto.RegisterType((*MultiAbiTargeting)(nil), "android.bundle.MultiAbiTargeting")
-	proto.RegisterType((*ScreenDensityTargeting)(nil), "android.bundle.ScreenDensityTargeting")
-	proto.RegisterType((*LanguageTargeting)(nil), "android.bundle.LanguageTargeting")
-	proto.RegisterType((*GraphicsApiTargeting)(nil), "android.bundle.GraphicsApiTargeting")
-	proto.RegisterType((*SdkVersionTargeting)(nil), "android.bundle.SdkVersionTargeting")
-	proto.RegisterType((*TextureCompressionFormatTargeting)(nil), "android.bundle.TextureCompressionFormatTargeting")
-	proto.RegisterType((*SanitizerTargeting)(nil), "android.bundle.SanitizerTargeting")
-	proto.RegisterType((*DeviceFeatureTargeting)(nil), "android.bundle.DeviceFeatureTargeting")
+var File_targeting_proto protoreflect.FileDescriptor
+
+var file_targeting_proto_rawDesc = []byte{
+	0x0a, 0x0f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x12, 0x0e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c,
+	0x65, 0x22, 0xe8, 0x03, 0x0a, 0x10, 0x56, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x54, 0x61, 0x72,
+	0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x57, 0x0a, 0x15, 0x73, 0x64, 0x6b, 0x5f, 0x76, 0x65,
+	0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e,
+	0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x53, 0x64, 0x6b, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f,
+	0x6e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x13, 0x73, 0x64, 0x6b, 0x56,
+	0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x12,
+	0x41, 0x0a, 0x0d, 0x61, 0x62, 0x69, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67,
+	0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
+	0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x41, 0x62, 0x69, 0x54, 0x61, 0x72, 0x67, 0x65,
+	0x74, 0x69, 0x6e, 0x67, 0x52, 0x0c, 0x61, 0x62, 0x69, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69,
+	0x6e, 0x67, 0x12, 0x60, 0x0a, 0x18, 0x73, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x5f, 0x64, 0x65, 0x6e,
+	0x73, 0x69, 0x74, 0x79, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x03,
+	0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62,
+	0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x53, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x44, 0x65, 0x6e, 0x73,
+	0x69, 0x74, 0x79, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x16, 0x73, 0x63,
+	0x72, 0x65, 0x65, 0x6e, 0x44, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x79, 0x54, 0x61, 0x72, 0x67, 0x65,
+	0x74, 0x69, 0x6e, 0x67, 0x12, 0x51, 0x0a, 0x13, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x5f, 0x61, 0x62,
+	0x69, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28,
+	0x0b, 0x32, 0x21, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64,
+	0x6c, 0x65, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x41, 0x62, 0x69, 0x54, 0x61, 0x72, 0x67, 0x65,
+	0x74, 0x69, 0x6e, 0x67, 0x52, 0x11, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x41, 0x62, 0x69, 0x54, 0x61,
+	0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x82, 0x01, 0x0a, 0x24, 0x74, 0x65, 0x78, 0x74,
+	0x75, 0x72, 0x65, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f,
+	0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67,
+	0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
+	0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x54, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x43,
+	0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74,
+	0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x21, 0x74, 0x65, 0x78, 0x74, 0x75,
+	0x72, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x46, 0x6f, 0x72,
+	0x6d, 0x61, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x22, 0xe7, 0x05, 0x0a,
+	0x0c, 0x41, 0x70, 0x6b, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x41, 0x0a,
+	0x0d, 0x61, 0x62, 0x69, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62,
+	0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x41, 0x62, 0x69, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69,
+	0x6e, 0x67, 0x52, 0x0c, 0x61, 0x62, 0x69, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67,
+	0x12, 0x5a, 0x0a, 0x16, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x5f, 0x61, 0x70, 0x69,
+	0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b,
+	0x32, 0x24, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c,
+	0x65, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x41, 0x70, 0x69, 0x54, 0x61, 0x72,
+	0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x14, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73,
+	0x41, 0x70, 0x69, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x50, 0x0a, 0x12,
+	0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69,
+	0x6e, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f,
+	0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61,
+	0x67, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x11, 0x6c, 0x61, 0x6e,
+	0x67, 0x75, 0x61, 0x67, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x60,
+	0x0a, 0x18, 0x73, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x5f, 0x64, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x79,
+	0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b,
+	0x32, 0x26, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c,
+	0x65, 0x2e, 0x53, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x44, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x79, 0x54,
+	0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x16, 0x73, 0x63, 0x72, 0x65, 0x65, 0x6e,
+	0x44, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x79, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67,
+	0x12, 0x57, 0x0a, 0x15, 0x73, 0x64, 0x6b, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f,
+	0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32,
+	0x23, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65,
+	0x2e, 0x53, 0x64, 0x6b, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x61, 0x72, 0x67, 0x65,
+	0x74, 0x69, 0x6e, 0x67, 0x52, 0x13, 0x73, 0x64, 0x6b, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
+	0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x82, 0x01, 0x0a, 0x24, 0x74, 0x65,
+	0x78, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f,
+	0x6e, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69,
+	0x6e, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f,
+	0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x54, 0x65, 0x78, 0x74, 0x75, 0x72,
+	0x65, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x46, 0x6f, 0x72, 0x6d,
+	0x61, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x21, 0x74, 0x65, 0x78,
+	0x74, 0x75, 0x72, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x46,
+	0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x51,
+	0x0a, 0x13, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x5f, 0x61, 0x62, 0x69, 0x5f, 0x74, 0x61, 0x72, 0x67,
+	0x65, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x61, 0x6e,
+	0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x4d, 0x75, 0x6c,
+	0x74, 0x69, 0x41, 0x62, 0x69, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x11,
+	0x6d, 0x75, 0x6c, 0x74, 0x69, 0x41, 0x62, 0x69, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e,
+	0x67, 0x12, 0x53, 0x0a, 0x13, 0x73, 0x61, 0x6e, 0x69, 0x74, 0x69, 0x7a, 0x65, 0x72, 0x5f, 0x74,
+	0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22,
+	0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e,
+	0x53, 0x61, 0x6e, 0x69, 0x74, 0x69, 0x7a, 0x65, 0x72, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69,
+	0x6e, 0x67, 0x52, 0x12, 0x73, 0x61, 0x6e, 0x69, 0x74, 0x69, 0x7a, 0x65, 0x72, 0x54, 0x61, 0x72,
+	0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x22, 0xae, 0x02, 0x0a, 0x0f, 0x4d, 0x6f, 0x64, 0x75, 0x6c,
+	0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x57, 0x0a, 0x15, 0x73, 0x64,
+	0x6b, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74,
+	0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x61, 0x6e, 0x64, 0x72,
+	0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x53, 0x64, 0x6b, 0x56, 0x65,
+	0x72, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x13,
+	0x73, 0x64, 0x6b, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74,
+	0x69, 0x6e, 0x67, 0x12, 0x60, 0x0a, 0x18, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x66, 0x65,
+	0x61, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x18,
+	0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e,
+	0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x46, 0x65, 0x61,
+	0x74, 0x75, 0x72, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x16, 0x64,
+	0x65, 0x76, 0x69, 0x63, 0x65, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x54, 0x61, 0x72, 0x67,
+	0x65, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x60, 0x0a, 0x18, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x63, 0x6f,
+	0x75, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x5f, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e,
+	0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69,
+	0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x55, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x75,
+	0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x52,
+	0x16, 0x75, 0x73, 0x65, 0x72, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x54, 0x61,
+	0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x22, 0x57, 0x0a, 0x16, 0x55, 0x73, 0x65, 0x72, 0x43,
+	0x6f, 0x75, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e,
+	0x67, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x64,
+	0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72,
+	0x79, 0x43, 0x6f, 0x64, 0x65, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64,
+	0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65,
+	0x22, 0x97, 0x02, 0x0a, 0x0d, 0x53, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x44, 0x65, 0x6e, 0x73, 0x69,
+	0x74, 0x79, 0x12, 0x51, 0x0a, 0x0d, 0x64, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x79, 0x5f, 0x61, 0x6c,
+	0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2a, 0x2e, 0x61, 0x6e, 0x64, 0x72,
+	0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x53, 0x63, 0x72, 0x65, 0x65,
+	0x6e, 0x44, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x79, 0x2e, 0x44, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x79,
+	0x41, 0x6c, 0x69, 0x61, 0x73, 0x48, 0x00, 0x52, 0x0c, 0x64, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x79,
+	0x41, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x21, 0x0a, 0x0b, 0x64, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x79,
+	0x5f, 0x64, 0x70, 0x69, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x48, 0x00, 0x52, 0x0a, 0x64, 0x65,
+	0x6e, 0x73, 0x69, 0x74, 0x79, 0x44, 0x70, 0x69, 0x22, 0x7f, 0x0a, 0x0c, 0x44, 0x65, 0x6e, 0x73,
+	0x69, 0x74, 0x79, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x17, 0x0a, 0x13, 0x44, 0x45, 0x4e, 0x53,
+	0x49, 0x54, 0x59, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10,
+	0x00, 0x12, 0x09, 0x0a, 0x05, 0x4e, 0x4f, 0x44, 0x50, 0x49, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04,
+	0x4c, 0x44, 0x50, 0x49, 0x10, 0x02, 0x12, 0x08, 0x0a, 0x04, 0x4d, 0x44, 0x50, 0x49, 0x10, 0x03,
+	0x12, 0x09, 0x0a, 0x05, 0x54, 0x56, 0x44, 0x50, 0x49, 0x10, 0x04, 0x12, 0x08, 0x0a, 0x04, 0x48,
+	0x44, 0x50, 0x49, 0x10, 0x05, 0x12, 0x09, 0x0a, 0x05, 0x58, 0x48, 0x44, 0x50, 0x49, 0x10, 0x06,
+	0x12, 0x0a, 0x0a, 0x06, 0x58, 0x58, 0x48, 0x44, 0x50, 0x49, 0x10, 0x07, 0x12, 0x0b, 0x0a, 0x07,
+	0x58, 0x58, 0x58, 0x48, 0x44, 0x50, 0x49, 0x10, 0x08, 0x42, 0x0f, 0x0a, 0x0d, 0x64, 0x65, 0x6e,
+	0x73, 0x69, 0x74, 0x79, 0x5f, 0x6f, 0x6e, 0x65, 0x6f, 0x66, 0x22, 0x22, 0x0a, 0x0a, 0x49, 0x6e,
+	0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75,
+	0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x3a,
+	0x0a, 0x0a, 0x53, 0x64, 0x6b, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2c, 0x0a, 0x03,
+	0x6d, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x6e, 0x64, 0x72,
+	0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x49, 0x6e, 0x74, 0x33, 0x32,
+	0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x03, 0x6d, 0x69, 0x6e, 0x22, 0xb9, 0x01, 0x0a, 0x0b, 0x47,
+	0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x41, 0x70, 0x69, 0x12, 0x4e, 0x0a, 0x13, 0x6d, 0x69,
+	0x6e, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x5f, 0x67, 0x6c, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
+	0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69,
+	0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x4f, 0x70, 0x65, 0x6e, 0x47, 0x6c, 0x56,
+	0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x10, 0x6d, 0x69, 0x6e, 0x4f, 0x70, 0x65,
+	0x6e, 0x47, 0x6c, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x4d, 0x0a, 0x12, 0x6d, 0x69,
+	0x6e, 0x5f, 0x76, 0x75, 0x6c, 0x6b, 0x61, 0x6e, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
+	0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
+	0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x56, 0x75, 0x6c, 0x6b, 0x61, 0x6e, 0x56, 0x65,
+	0x72, 0x73, 0x69, 0x6f, 0x6e, 0x48, 0x00, 0x52, 0x10, 0x6d, 0x69, 0x6e, 0x56, 0x75, 0x6c, 0x6b,
+	0x61, 0x6e, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x0b, 0x0a, 0x09, 0x61, 0x70, 0x69,
+	0x5f, 0x6f, 0x6e, 0x65, 0x6f, 0x66, 0x22, 0x3b, 0x0a, 0x0d, 0x56, 0x75, 0x6c, 0x6b, 0x61, 0x6e,
+	0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x61, 0x6a, 0x6f, 0x72,
+	0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x12, 0x14, 0x0a,
+	0x05, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6d, 0x69,
+	0x6e, 0x6f, 0x72, 0x22, 0x3b, 0x0a, 0x0d, 0x4f, 0x70, 0x65, 0x6e, 0x47, 0x6c, 0x56, 0x65, 0x72,
+	0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x18, 0x01, 0x20,
+	0x01, 0x28, 0x05, 0x52, 0x05, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x69,
+	0x6e, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6d, 0x69, 0x6e, 0x6f, 0x72,
+	0x22, 0xb7, 0x02, 0x0a, 0x18, 0x54, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x43, 0x6f, 0x6d, 0x70,
+	0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x5c, 0x0a,
+	0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x46, 0x2e, 0x61,
+	0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x54, 0x65,
+	0x78, 0x74, 0x75, 0x72, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e,
+	0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x2e, 0x54, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x43, 0x6f,
+	0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x41,
+	0x6c, 0x69, 0x61, 0x73, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x22, 0xbc, 0x01, 0x0a, 0x1d,
+	0x54, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69,
+	0x6f, 0x6e, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x2a, 0x0a,
+	0x26, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x5f, 0x54, 0x45, 0x58,
+	0x54, 0x55, 0x52, 0x45, 0x5f, 0x43, 0x4f, 0x4d, 0x50, 0x52, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e,
+	0x5f, 0x46, 0x4f, 0x52, 0x4d, 0x41, 0x54, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x45, 0x54, 0x43,
+	0x31, 0x5f, 0x52, 0x47, 0x42, 0x38, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x50, 0x41, 0x4c, 0x45,
+	0x54, 0x54, 0x45, 0x44, 0x10, 0x02, 0x12, 0x0c, 0x0a, 0x08, 0x54, 0x48, 0x52, 0x45, 0x45, 0x5f,
+	0x44, 0x43, 0x10, 0x03, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x54, 0x43, 0x10, 0x04, 0x12, 0x08, 0x0a,
+	0x04, 0x4c, 0x41, 0x54, 0x43, 0x10, 0x05, 0x12, 0x08, 0x0a, 0x04, 0x44, 0x58, 0x54, 0x31, 0x10,
+	0x06, 0x12, 0x08, 0x0a, 0x04, 0x53, 0x33, 0x54, 0x43, 0x10, 0x07, 0x12, 0x09, 0x0a, 0x05, 0x50,
+	0x56, 0x52, 0x54, 0x43, 0x10, 0x08, 0x12, 0x08, 0x0a, 0x04, 0x41, 0x53, 0x54, 0x43, 0x10, 0x09,
+	0x12, 0x08, 0x0a, 0x04, 0x45, 0x54, 0x43, 0x32, 0x10, 0x0a, 0x22, 0xc0, 0x01, 0x0a, 0x03, 0x41,
+	0x62, 0x69, 0x12, 0x32, 0x0a, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28,
+	0x0e, 0x32, 0x1c, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64,
+	0x6c, 0x65, 0x2e, 0x41, 0x62, 0x69, 0x2e, 0x41, 0x62, 0x69, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52,
+	0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x22, 0x84, 0x01, 0x0a, 0x08, 0x41, 0x62, 0x69, 0x41, 0x6c,
+	0x69, 0x61, 0x73, 0x12, 0x20, 0x0a, 0x1c, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49,
+	0x45, 0x44, 0x5f, 0x43, 0x50, 0x55, 0x5f, 0x41, 0x52, 0x43, 0x48, 0x49, 0x54, 0x45, 0x43, 0x54,
+	0x55, 0x52, 0x45, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x41, 0x52, 0x4d, 0x45, 0x41, 0x42, 0x49,
+	0x10, 0x01, 0x12, 0x0f, 0x0a, 0x0b, 0x41, 0x52, 0x4d, 0x45, 0x41, 0x42, 0x49, 0x5f, 0x56, 0x37,
+	0x41, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x41, 0x52, 0x4d, 0x36, 0x34, 0x5f, 0x56, 0x38, 0x41,
+	0x10, 0x03, 0x12, 0x07, 0x0a, 0x03, 0x58, 0x38, 0x36, 0x10, 0x04, 0x12, 0x0a, 0x0a, 0x06, 0x58,
+	0x38, 0x36, 0x5f, 0x36, 0x34, 0x10, 0x05, 0x12, 0x08, 0x0a, 0x04, 0x4d, 0x49, 0x50, 0x53, 0x10,
+	0x06, 0x12, 0x0a, 0x0a, 0x06, 0x4d, 0x49, 0x50, 0x53, 0x36, 0x34, 0x10, 0x07, 0x22, 0x31, 0x0a,
+	0x08, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x41, 0x62, 0x69, 0x12, 0x25, 0x0a, 0x03, 0x61, 0x62, 0x69,
+	0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
+	0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x41, 0x62, 0x69, 0x52, 0x03, 0x61, 0x62, 0x69,
+	0x22, 0x76, 0x0a, 0x09, 0x53, 0x61, 0x6e, 0x69, 0x74, 0x69, 0x7a, 0x65, 0x72, 0x12, 0x3e, 0x0a,
+	0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x28, 0x2e, 0x61,
+	0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x53, 0x61,
+	0x6e, 0x69, 0x74, 0x69, 0x7a, 0x65, 0x72, 0x2e, 0x53, 0x61, 0x6e, 0x69, 0x74, 0x69, 0x7a, 0x65,
+	0x72, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x52, 0x05, 0x61, 0x6c, 0x69, 0x61, 0x73, 0x22, 0x29, 0x0a,
+	0x0e, 0x53, 0x61, 0x6e, 0x69, 0x74, 0x69, 0x7a, 0x65, 0x72, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x12,
+	0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x48, 0x57, 0x41,
+	0x44, 0x44, 0x52, 0x45, 0x53, 0x53, 0x10, 0x01, 0x22, 0x5b, 0x0a, 0x0d, 0x44, 0x65, 0x76, 0x69,
+	0x63, 0x65, 0x46, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x66, 0x65, 0x61,
+	0x74, 0x75, 0x72, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x0b, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x27, 0x0a, 0x0f,
+	0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18,
+	0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0e, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x56, 0x65,
+	0x72, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0xc3, 0x02, 0x0a, 0x18, 0x41, 0x73, 0x73, 0x65, 0x74, 0x73,
+	0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69,
+	0x6e, 0x67, 0x12, 0x2e, 0x0a, 0x03, 0x61, 0x62, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
+	0x1c, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65,
+	0x2e, 0x41, 0x62, 0x69, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x03, 0x61,
+	0x62, 0x69, 0x12, 0x47, 0x0a, 0x0c, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x5f, 0x61,
+	0x70, 0x69, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f,
+	0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, 0x69,
+	0x63, 0x73, 0x41, 0x70, 0x69, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x0b,
+	0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x41, 0x70, 0x69, 0x12, 0x6f, 0x0a, 0x1a, 0x74,
+	0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69,
+	0x6f, 0x6e, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32,
+	0x31, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65,
+	0x2e, 0x54, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73,
+	0x69, 0x6f, 0x6e, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69,
+	0x6e, 0x67, 0x52, 0x18, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x72,
+	0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x3d, 0x0a, 0x08,
+	0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21,
+	0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e,
+	0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e,
+	0x67, 0x52, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x22, 0xa2, 0x02, 0x0a, 0x18,
+	0x4e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x54,
+	0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x25, 0x0a, 0x03, 0x61, 0x62, 0x69, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e,
+	0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x41, 0x62, 0x69, 0x52, 0x03, 0x61, 0x62, 0x69, 0x12,
+	0x3e, 0x0a, 0x0c, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x5f, 0x61, 0x70, 0x69, 0x18,
+	0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e,
+	0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x41,
+	0x70, 0x69, 0x52, 0x0b, 0x67, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x41, 0x70, 0x69, 0x12,
+	0x66, 0x0a, 0x1a, 0x74, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x5f, 0x63, 0x6f, 0x6d, 0x70, 0x72,
+	0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x18, 0x03, 0x20,
+	0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75,
+	0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x54, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x43, 0x6f, 0x6d, 0x70,
+	0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x52, 0x18, 0x74,
+	0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f,
+	0x6e, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x12, 0x37, 0x0a, 0x09, 0x73, 0x61, 0x6e, 0x69, 0x74,
+	0x69, 0x7a, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x6e, 0x64,
+	0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x53, 0x61, 0x6e, 0x69,
+	0x74, 0x69, 0x7a, 0x65, 0x72, 0x52, 0x09, 0x73, 0x61, 0x6e, 0x69, 0x74, 0x69, 0x7a, 0x65, 0x72,
+	0x22, 0x54, 0x0a, 0x12, 0x41, 0x70, 0x65, 0x78, 0x49, 0x6d, 0x61, 0x67, 0x65, 0x54, 0x61, 0x72,
+	0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x3e, 0x0a, 0x09, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x5f,
+	0x61, 0x62, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x61, 0x6e, 0x64, 0x72,
+	0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69,
+	0x41, 0x62, 0x69, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x08, 0x6d, 0x75,
+	0x6c, 0x74, 0x69, 0x41, 0x62, 0x69, 0x22, 0x72, 0x0a, 0x0c, 0x41, 0x62, 0x69, 0x54, 0x61, 0x72,
+	0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x29, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18,
+	0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e,
+	0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x41, 0x62, 0x69, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75,
+	0x65, 0x12, 0x37, 0x0a, 0x0c, 0x61, 0x6c, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65,
+	0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69,
+	0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x41, 0x62, 0x69, 0x52, 0x0c, 0x61, 0x6c,
+	0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x73, 0x22, 0x81, 0x01, 0x0a, 0x11, 0x4d,
+	0x75, 0x6c, 0x74, 0x69, 0x41, 0x62, 0x69, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67,
+	0x12, 0x2e, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
+	0x18, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65,
+	0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x41, 0x62, 0x69, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
+	0x12, 0x3c, 0x0a, 0x0c, 0x61, 0x6c, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x73,
+	0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
+	0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x41, 0x62, 0x69,
+	0x52, 0x0c, 0x61, 0x6c, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x73, 0x22, 0x90,
+	0x01, 0x0a, 0x16, 0x53, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x44, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x79,
+	0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x33, 0x0a, 0x05, 0x76, 0x61, 0x6c,
+	0x75, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f,
+	0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x53, 0x63, 0x72, 0x65, 0x65, 0x6e,
+	0x44, 0x65, 0x6e, 0x73, 0x69, 0x74, 0x79, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x41,
+	0x0a, 0x0c, 0x61, 0x6c, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x73, 0x18, 0x02,
+	0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62,
+	0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x53, 0x63, 0x72, 0x65, 0x65, 0x6e, 0x44, 0x65, 0x6e, 0x73,
+	0x69, 0x74, 0x79, 0x52, 0x0c, 0x61, 0x6c, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65,
+	0x73, 0x22, 0x4d, 0x0a, 0x11, 0x4c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x54, 0x61, 0x72,
+	0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18,
+	0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x22, 0x0a, 0x0c,
+	0x61, 0x6c, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03,
+	0x28, 0x09, 0x52, 0x0c, 0x61, 0x6c, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x73,
+	0x22, 0x8a, 0x01, 0x0a, 0x14, 0x47, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x41, 0x70, 0x69,
+	0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x31, 0x0a, 0x05, 0x76, 0x61, 0x6c,
+	0x75, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f,
+	0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, 0x69,
+	0x63, 0x73, 0x41, 0x70, 0x69, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x3f, 0x0a, 0x0c,
+	0x61, 0x6c, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03,
+	0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e,
+	0x64, 0x6c, 0x65, 0x2e, 0x47, 0x72, 0x61, 0x70, 0x68, 0x69, 0x63, 0x73, 0x41, 0x70, 0x69, 0x52,
+	0x0c, 0x61, 0x6c, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x73, 0x22, 0x87, 0x01,
+	0x0a, 0x13, 0x53, 0x64, 0x6b, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x61, 0x72, 0x67,
+	0x65, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x30, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01,
+	0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62,
+	0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x53, 0x64, 0x6b, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
+	0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x3e, 0x0a, 0x0c, 0x61, 0x6c, 0x74, 0x65, 0x72,
+	0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e,
+	0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x53,
+	0x64, 0x6b, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x61, 0x6c, 0x74, 0x65, 0x72,
+	0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x73, 0x22, 0xb1, 0x01, 0x0a, 0x21, 0x54, 0x65, 0x78, 0x74,
+	0x75, 0x72, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x46, 0x6f,
+	0x72, 0x6d, 0x61, 0x74, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x3e, 0x0a,
+	0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x61,
+	0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x54, 0x65,
+	0x78, 0x74, 0x75, 0x72, 0x65, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e,
+	0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x4c, 0x0a,
+	0x0c, 0x61, 0x6c, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x73, 0x18, 0x02, 0x20,
+	0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75,
+	0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x54, 0x65, 0x78, 0x74, 0x75, 0x72, 0x65, 0x43, 0x6f, 0x6d, 0x70,
+	0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x52, 0x0c, 0x61,
+	0x6c, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x74, 0x69, 0x76, 0x65, 0x73, 0x22, 0x45, 0x0a, 0x12, 0x53,
+	0x61, 0x6e, 0x69, 0x74, 0x69, 0x7a, 0x65, 0x72, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e,
+	0x67, 0x12, 0x2f, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b,
+	0x32, 0x19, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c,
+	0x65, 0x2e, 0x53, 0x61, 0x6e, 0x69, 0x74, 0x69, 0x7a, 0x65, 0x72, 0x52, 0x05, 0x76, 0x61, 0x6c,
+	0x75, 0x65, 0x22, 0x62, 0x0a, 0x16, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x46, 0x65, 0x61, 0x74,
+	0x75, 0x72, 0x65, 0x54, 0x61, 0x72, 0x67, 0x65, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x48, 0x0a, 0x10,
+	0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x5f, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65,
+	0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
+	0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x2e, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x46, 0x65,
+	0x61, 0x74, 0x75, 0x72, 0x65, 0x52, 0x0f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x46,
+	0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x42, 0x41, 0x0a, 0x12, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x6e,
+	0x64, 0x72, 0x6f, 0x69, 0x64, 0x2e, 0x62, 0x75, 0x6e, 0x64, 0x6c, 0x65, 0x5a, 0x2b, 0x61, 0x6e,
+	0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x63, 0x6d, 0x64, 0x2f,
+	0x65, 0x78, 0x74, 0x72, 0x61, 0x63, 0x74, 0x5f, 0x61, 0x70, 0x6b, 0x73, 0x2f, 0x62, 0x75, 0x6e,
+	0x64, 0x6c, 0x65, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x33,
 }
 
-func init() {
-	proto.RegisterFile("targeting.proto", fileDescriptor_df45b505afdf471e)
+var (
+	file_targeting_proto_rawDescOnce sync.Once
+	file_targeting_proto_rawDescData = file_targeting_proto_rawDesc
+)
+
+func file_targeting_proto_rawDescGZIP() []byte {
+	file_targeting_proto_rawDescOnce.Do(func() {
+		file_targeting_proto_rawDescData = protoimpl.X.CompressGZIP(file_targeting_proto_rawDescData)
+	})
+	return file_targeting_proto_rawDescData
 }
 
-var fileDescriptor_df45b505afdf471e = []byte{
-	// 1504 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x58, 0x5b, 0x6f, 0xe3, 0xc4,
-	0x17, 0xaf, 0x93, 0xa6, 0x4d, 0x4e, 0x92, 0xd6, 0x3b, 0xe9, 0xbf, 0xff, 0xb0, 0xec, 0x4a, 0x5b,
-	0xef, 0x85, 0xee, 0x0a, 0x05, 0xda, 0xae, 0xba, 0x15, 0x97, 0x22, 0xd7, 0x71, 0x9b, 0x48, 0x4d,
-	0x9a, 0x75, 0xdc, 0x6c, 0x59, 0x90, 0xbc, 0x4e, 0x3c, 0x0d, 0xa6, 0x89, 0x1d, 0x6c, 0xa7, 0xda,
-	0xe5, 0x05, 0x81, 0x90, 0x90, 0x78, 0xe2, 0x8d, 0x77, 0x3e, 0x00, 0x12, 0x4f, 0x08, 0x89, 0x07,
-	0x24, 0x3e, 0x0c, 0x7c, 0x0c, 0x34, 0xbe, 0x24, 0x9e, 0xc4, 0x4e, 0xb3, 0x2c, 0xf0, 0x50, 0xe5,
-	0xcc, 0xf1, 0x39, 0xbf, 0x73, 0x99, 0x73, 0x66, 0xce, 0x14, 0x56, 0x1d, 0xd5, 0xea, 0x62, 0x47,
-	0x37, 0xba, 0xa5, 0x81, 0x65, 0x3a, 0x26, 0x5a, 0x51, 0x0d, 0xcd, 0x32, 0x75, 0xad, 0xd4, 0x1e,
-	0x1a, 0x5a, 0x0f, 0x73, 0x7f, 0x26, 0x81, 0x6d, 0xa9, 0x96, 0xae, 0x1a, 0x8e, 0x1c, 0x88, 0xa2,
-	0x27, 0xf0, 0x3f, 0x5b, 0xbb, 0x50, 0x2e, 0xb1, 0x65, 0xeb, 0xa6, 0xa1, 0x8c, 0x30, 0x8a, 0xcc,
-	0x2d, 0x66, 0x33, 0xbb, 0x7d, 0xbb, 0x44, 0x83, 0x94, 0x9a, 0xda, 0x45, 0xcb, 0x93, 0x1d, 0x61,
-	0x48, 0x05, 0x7b, 0x9a, 0x89, 0x78, 0xc8, 0xab, 0x6d, 0x3d, 0x04, 0x98, 0x70, 0x01, 0x6f, 0x4c,
-	0x02, 0xf2, 0x6d, 0x7d, 0x8c, 0x94, 0x53, 0x43, 0x2b, 0xf4, 0x0c, 0x8a, 0x76, 0xc7, 0xc2, 0xd8,
-	0x50, 0x34, 0x6c, 0xd8, 0xba, 0xf3, 0x22, 0x84, 0x96, 0x74, 0xd1, 0xee, 0x4d, 0xb9, 0xe7, 0xca,
-	0x97, 0x3d, 0xf1, 0x31, 0xee, 0xba, 0x1d, 0xc9, 0x47, 0x8f, 0xa1, 0xd0, 0x1f, 0xf6, 0x1c, 0x5d,
-	0xa1, 0x5d, 0x5d, 0x74, 0xc1, 0x37, 0x26, 0xc1, 0x6b, 0x44, 0x94, 0xf2, 0xf7, 0x5a, 0x7f, 0x92,
-	0x85, 0xbe, 0x62, 0xe0, 0x8e, 0x83, 0x9f, 0x3b, 0x43, 0x0b, 0x2b, 0x1d, 0xb3, 0x3f, 0xb0, 0xb0,
-	0xed, 0x66, 0xf6, 0xdc, 0xb4, 0xfa, 0xaa, 0x13, 0x32, 0x92, 0x72, 0x8d, 0x6c, 0x4d, 0x1a, 0x91,
-	0x3d, 0x5d, 0x61, 0xac, 0x7a, 0xe8, 0x6a, 0x8e, 0x8d, 0x6e, 0x38, 0x57, 0x89, 0x70, 0x7f, 0xa4,
-	0x20, 0xc7, 0x0f, 0x2e, 0x66, 0xec, 0x06, 0xf3, 0xd2, 0xbb, 0xf1, 0x14, 0xd6, 0xbb, 0x96, 0x3a,
-	0xf8, 0x44, 0xef, 0xd8, 0x8a, 0x3a, 0x98, 0xde, 0xd9, 0x3b, 0x93, 0x58, 0x47, 0xbe, 0x34, 0x3f,
-	0x08, 0x61, 0xae, 0x75, 0x23, 0xb8, 0xa8, 0x01, 0xa8, 0xa7, 0x1a, 0xdd, 0xa1, 0xda, 0xc5, 0x53,
-	0x7b, 0x3c, 0xb5, 0x0d, 0xc7, 0xbe, 0x64, 0x68, 0x1b, 0x7a, 0x93, 0xac, 0x99, 0xb5, 0xb3, 0xf8,
-	0x8f, 0xd4, 0x4e, 0x6c, 0xe7, 0xa4, 0x5e, 0xb1, 0x73, 0xe6, 0xae, 0xa0, 0xa5, 0x7f, 0xaf, 0x82,
-	0xe2, 0x3a, 0x63, 0xf9, 0x15, 0x3a, 0xa3, 0x09, 0x05, 0x5b, 0x35, 0x74, 0x47, 0xff, 0x1c, 0x5b,
-	0x21, 0xc8, 0xb4, 0x0b, 0xc9, 0x4d, 0xa5, 0x2b, 0x10, 0x1d, 0x63, 0x22, 0x7b, 0x8a, 0xc7, 0xfd,
-	0x98, 0x80, 0xd5, 0x9a, 0xa9, 0x0d, 0x7b, 0xf8, 0x3f, 0x38, 0xd3, 0x9e, 0x41, 0x51, 0xc3, 0x97,
-	0x7a, 0x07, 0x2b, 0xe7, 0x58, 0x75, 0xf7, 0x27, 0xdc, 0x04, 0xc9, 0xa8, 0xa2, 0x2a, 0xbb, 0xf2,
-	0x87, 0x9e, 0x78, 0xa8, 0xa8, 0xb4, 0x48, 0x3e, 0xb1, 0x30, 0xb4, 0xb1, 0xa5, 0x74, 0xcc, 0xa1,
-	0xe1, 0x58, 0x3a, 0xb6, 0xaf, 0x3e, 0xf2, 0x4e, 0x6d, 0x6c, 0x09, 0x81, 0x78, 0xc8, 0xc2, 0x30,
-	0x92, 0xcf, 0x3d, 0x81, 0xf5, 0x68, 0x0d, 0x74, 0x1b, 0xf2, 0x9e, 0xd9, 0x17, 0x4a, 0xc7, 0xd4,
-	0xb0, 0x5d, 0x64, 0x6e, 0x25, 0x37, 0x33, 0x52, 0xce, 0x67, 0x0a, 0x84, 0x87, 0x8a, 0xb0, 0x8c,
-	0x9f, 0x77, 0x7a, 0x43, 0x0d, 0xbb, 0x6d, 0x9f, 0x96, 0x82, 0x25, 0xf7, 0x7d, 0x02, 0xf2, 0x54,
-	0x0b, 0xa1, 0xc7, 0x90, 0x0f, 0x9a, 0x4f, 0xed, 0xe9, 0xaa, 0xed, 0xe6, 0x7f, 0x65, 0xfb, 0xc1,
-	0xcc, 0xc6, 0x2b, 0xf9, 0xbf, 0x3c, 0xd1, 0xa8, 0x2c, 0x48, 0x39, 0x2d, 0xb4, 0x46, 0x1b, 0x90,
-	0x0d, 0x20, 0xb5, 0x81, 0xee, 0xba, 0x90, 0xaa, 0x2c, 0x48, 0xe0, 0x33, 0xcb, 0x03, 0x9d, 0xfb,
-	0x02, 0x72, 0x61, 0x08, 0xf4, 0x7f, 0x28, 0x94, 0xc5, 0x7a, 0xb3, 0x2a, 0x7f, 0xa8, 0x9c, 0xd6,
-	0x9b, 0x0d, 0x51, 0xa8, 0x1e, 0x56, 0xc5, 0x32, 0xbb, 0x80, 0x32, 0x90, 0xaa, 0x9f, 0x94, 0x1b,
-	0x55, 0x96, 0x41, 0x69, 0x58, 0x3c, 0x26, 0x54, 0x82, 0x50, 0x35, 0x42, 0x25, 0xc9, 0x67, 0xb9,
-	0x45, 0xc8, 0x45, 0xc2, 0xac, 0x10, 0x2a, 0x45, 0x98, 0x67, 0x2e, 0xb9, 0x84, 0x00, 0x96, 0xce,
-	0x3c, 0x7a, 0x19, 0x65, 0x61, 0xf9, 0xcc, 0x5f, 0xa4, 0x0f, 0x56, 0xc7, 0x61, 0x9b, 0x06, 0x36,
-	0xcf, 0x39, 0x0e, 0xa0, 0x6a, 0x38, 0x3b, 0xdb, 0x2d, 0xb5, 0x37, 0xc4, 0x68, 0x0d, 0x52, 0x97,
-	0x84, 0x70, 0xb3, 0x91, 0x92, 0xbc, 0x05, 0xf7, 0x0e, 0xc0, 0xb8, 0x0c, 0xd1, 0x9b, 0x90, 0xec,
-	0xeb, 0x86, 0x5f, 0xaf, 0xd7, 0x27, 0xf3, 0x35, 0x06, 0x93, 0x88, 0x18, 0xf7, 0x0b, 0x03, 0xd9,
-	0xd0, 0x61, 0x8b, 0xea, 0x50, 0xe8, 0xeb, 0x86, 0x62, 0x0e, 0xb0, 0xa1, 0x74, 0x7b, 0x41, 0x1f,
-	0xf8, 0x68, 0x37, 0x27, 0xd1, 0x4e, 0x06, 0xd8, 0x38, 0xea, 0xf9, 0x96, 0x2b, 0x0b, 0x12, 0xdb,
-	0xd7, 0x0d, 0x8a, 0x87, 0x6a, 0x80, 0x08, 0xde, 0xe5, 0xb0, 0x77, 0xa1, 0x1a, 0x23, 0xb8, 0x44,
-	0x34, 0x5c, 0xcb, 0x95, 0xa2, 0xe1, 0x28, 0xde, 0x41, 0x16, 0x32, 0xe4, 0xfe, 0xf0, 0x72, 0xf3,
-	0x2e, 0xe4, 0xa9, 0xaf, 0x24, 0x3d, 0x7d, 0xf5, 0x53, 0xd3, 0x0a, 0xd2, 0xe3, 0x2e, 0x5c, 0xae,
-	0x6e, 0x98, 0x96, 0xb7, 0xe3, 0x92, 0xb7, 0x20, 0xca, 0xb4, 0xa7, 0x2f, 0xa3, 0xfc, 0x73, 0x02,
-	0x8a, 0x71, 0x47, 0x25, 0xfa, 0x18, 0x52, 0xe1, 0x92, 0x3d, 0x9c, 0xf7, 0x8c, 0x8d, 0xfd, 0xe0,
-	0xd6, 0xa2, 0xe4, 0x81, 0x72, 0xbf, 0x32, 0x70, 0x73, 0xa6, 0x20, 0x7a, 0x00, 0xf7, 0x42, 0xc5,
-	0xaa, 0xc8, 0xe2, 0x99, 0x7c, 0x2a, 0x89, 0x8a, 0x70, 0x52, 0x6b, 0x48, 0x62, 0xb3, 0x59, 0x3d,
-	0xa9, 0x2b, 0x87, 0x27, 0x52, 0x8d, 0x97, 0xd9, 0x05, 0x94, 0x87, 0x8c, 0x28, 0x0b, 0x5b, 0x8a,
-	0x74, 0x74, 0xb0, 0xc7, 0x32, 0x28, 0x07, 0xe9, 0x06, 0x7f, 0x2c, 0xca, 0xb2, 0x58, 0x66, 0x13,
-	0x64, 0x25, 0x57, 0x24, 0x51, 0x54, 0xca, 0x02, 0x9b, 0x44, 0xcb, 0x90, 0xe4, 0x65, 0xc1, 0xab,
-	0xe8, 0x63, 0x42, 0xa5, 0x08, 0x55, 0x3e, 0x93, 0xb7, 0xd8, 0x25, 0x42, 0x35, 0x77, 0x64, 0x81,
-	0x5d, 0x26, 0x55, 0xde, 0x68, 0x49, 0xb2, 0xc0, 0xa6, 0x09, 0x93, 0x6f, 0xca, 0x02, 0x9b, 0x21,
-	0x94, 0x28, 0x0b, 0xdb, 0x2c, 0x70, 0xbf, 0x31, 0x90, 0xe4, 0xdb, 0x3a, 0xda, 0xa6, 0x93, 0x14,
-	0x35, 0x4c, 0x90, 0x3f, 0x2a, 0xf4, 0xaf, 0x19, 0x48, 0x07, 0x3c, 0x74, 0x0b, 0x6e, 0x84, 0xa3,
-	0x14, 0x1a, 0xa7, 0x0a, 0x2f, 0x09, 0x95, 0xaa, 0x2c, 0x0a, 0x24, 0x5c, 0x76, 0x81, 0x34, 0x16,
-	0x2f, 0xd5, 0x44, 0xfe, 0x80, 0x74, 0xe9, 0x2a, 0x64, 0xfd, 0x85, 0xd2, 0x7a, 0xc4, 0xb3, 0x09,
-	0x12, 0x39, 0x2f, 0xd5, 0x76, 0x1f, 0x2a, 0xad, 0x3d, 0xde, 0x8b, 0xee, 0x6c, 0x6f, 0x97, 0x5d,
-	0x74, 0x5b, 0x73, 0x6f, 0x57, 0xd9, 0x7d, 0xe8, 0xc5, 0x57, 0xab, 0x36, 0x9a, 0x5e, 0xc3, 0x12,
-	0x6a, 0xf7, 0x21, 0xbb, 0xcc, 0x6d, 0x41, 0x3a, 0xb8, 0xb3, 0xd0, 0x5d, 0x48, 0xaa, 0x6d, 0xdd,
-	0x3d, 0xed, 0xb2, 0xdb, 0x85, 0x88, 0x20, 0x24, 0xf2, 0x9d, 0xbb, 0x84, 0xcc, 0xe8, 0x4e, 0x42,
-	0xfb, 0x74, 0xe8, 0x9b, 0xb1, 0xb7, 0xd7, 0x98, 0xa2, 0xd2, 0x70, 0x1f, 0x56, 0xe8, 0x0f, 0xc4,
-	0xcf, 0xfa, 0x49, 0x5d, 0xf4, 0xf6, 0xb3, 0xf2, 0x84, 0x2f, 0x97, 0xc9, 0x46, 0xb3, 0x0c, 0xf7,
-	0x11, 0xe4, 0xa9, 0x4b, 0x04, 0x6d, 0x40, 0x2e, 0xb8, 0x7e, 0x0c, 0xb5, 0xef, 0x9d, 0x23, 0x19,
-	0x29, 0xeb, 0xf3, 0xea, 0x6a, 0x1f, 0xa3, 0x37, 0x60, 0x35, 0x10, 0x09, 0xb7, 0x6b, 0x4a, 0x5a,
-	0xf1, 0xd9, 0x7e, 0xc3, 0x70, 0xbf, 0x27, 0xa0, 0xc8, 0xdb, 0x36, 0x76, 0xec, 0xb2, 0x6e, 0xe1,
-	0x8e, 0x63, 0x5a, 0xa1, 0x09, 0xa7, 0x14, 0x24, 0xe6, 0xea, 0x51, 0x91, 0x08, 0xa2, 0x23, 0xc8,
-	0x85, 0x27, 0xc4, 0x97, 0x9a, 0x0b, 0xb3, 0xa1, 0xb9, 0x10, 0x99, 0x70, 0x3d, 0x7e, 0x00, 0xf2,
-	0xef, 0xc1, 0xbf, 0x31, 0xf6, 0x14, 0xe3, 0xc6, 0x1e, 0xf4, 0x3e, 0xa4, 0x83, 0x11, 0x32, 0x6e,
-	0xf8, 0x9f, 0x9e, 0x3a, 0x47, 0x2a, 0xdc, 0x0f, 0x09, 0x28, 0xd6, 0x55, 0x47, 0xbf, 0xc4, 0x11,
-	0x59, 0xbc, 0x1b, 0xce, 0x62, 0x6c, 0x79, 0xa1, 0xfd, 0xc8, 0xe4, 0xbd, 0x3e, 0x23, 0x79, 0x74,
-	0xce, 0xce, 0xe7, 0xc8, 0xd9, 0xe6, 0xbc, 0x39, 0x9b, 0x91, 0xaa, 0x47, 0x90, 0x19, 0x8d, 0x61,
-	0x7e, 0xae, 0x5e, 0x8b, 0xad, 0x7e, 0x69, 0x2c, 0xcb, 0xc9, 0x80, 0xf8, 0x01, 0x7e, 0x5e, 0xed,
-	0x53, 0x73, 0xfa, 0x3e, 0x64, 0x46, 0x73, 0xa6, 0x9f, 0xa3, 0x39, 0xa6, 0xcb, 0x74, 0x30, 0x5d,
-	0x72, 0x16, 0xe4, 0xa8, 0x21, 0xf3, 0xfe, 0xf8, 0x76, 0x8d, 0x6d, 0x67, 0x4f, 0x02, 0x3d, 0x82,
-	0x9c, 0xda, 0x73, 0xb0, 0x65, 0xb8, 0x3b, 0x67, 0xfb, 0x13, 0x5c, 0xa4, 0x06, 0x25, 0xc8, 0x7d,
-	0xc9, 0xc0, 0xb5, 0x29, 0x9f, 0x50, 0x89, 0xb6, 0x5c, 0x8c, 0x8b, 0x22, 0x30, 0xff, 0x5e, 0xa4,
-	0xf9, 0x78, 0x35, 0xda, 0x87, 0xef, 0x18, 0x58, 0x8f, 0x7e, 0xb0, 0xa0, 0x1d, 0xda, 0x91, 0x9b,
-	0x33, 0xc7, 0xad, 0xc0, 0x1b, 0x3e, 0xd2, 0x9b, 0x2b, 0x74, 0x69, 0x97, 0x6a, 0x70, 0x6d, 0xaa,
-	0x49, 0xc2, 0xd3, 0x0e, 0x19, 0x26, 0x7d, 0x6b, 0x5c, 0x84, 0xb5, 0xcc, 0x04, 0xdc, 0xb7, 0x0c,
-	0xac, 0x45, 0x1d, 0x15, 0x68, 0x8b, 0x8e, 0x6f, 0x66, 0x8b, 0xf8, 0xf6, 0x3e, 0x88, 0x8c, 0x6e,
-	0xa6, 0x26, 0xed, 0xcc, 0x37, 0x0c, 0x14, 0x22, 0x9e, 0x09, 0xe8, 0x6d, 0xda, 0x97, 0xeb, 0xf1,
-	0x4f, 0x8b, 0xc0, 0x95, 0xfd, 0x48, 0x57, 0x66, 0x29, 0xd2, 0x9e, 0xfc, 0xc4, 0xc0, 0xc6, 0x95,
-	0x47, 0x1d, 0xb9, 0x9f, 0xc2, 0x7e, 0xcd, 0xdf, 0xf8, 0xbe, 0x97, 0xc7, 0x91, 0x5e, 0xce, 0x0f,
-	0x43, 0xfb, 0x2c, 0x02, 0x9a, 0x7e, 0xce, 0xa1, 0xb7, 0x68, 0x1f, 0x67, 0x9c, 0x22, 0xfe, 0x8c,
-	0xdc, 0x86, 0xf5, 0xe8, 0xe7, 0x14, 0xaa, 0x00, 0x6b, 0xe1, 0xcf, 0x86, 0xba, 0x85, 0xb5, 0xe0,
-	0x69, 0x16, 0x37, 0xee, 0x52, 0x08, 0xd2, 0x6a, 0xa0, 0xe6, 0x33, 0x0e, 0x1e, 0x00, 0xea, 0x98,
-	0xfd, 0x09, 0xa5, 0xa7, 0x6b, 0xfe, 0x5a, 0xf1, 0xd6, 0x8a, 0xfb, 0x0f, 0xb6, 0xf6, 0x92, 0xfb,
-	0xb3, 0xf3, 0x57, 0x00, 0x00, 0x00, 0xff, 0xff, 0x47, 0xe0, 0x85, 0xa8, 0x7a, 0x13, 0x00, 0x00,
+var file_targeting_proto_enumTypes = make([]protoimpl.EnumInfo, 4)
+var file_targeting_proto_msgTypes = make([]protoimpl.MessageInfo, 27)
+var file_targeting_proto_goTypes = []interface{}{
+	(ScreenDensity_DensityAlias)(0),                             // 0: android.bundle.ScreenDensity.DensityAlias
+	(TextureCompressionFormat_TextureCompressionFormatAlias)(0), // 1: android.bundle.TextureCompressionFormat.TextureCompressionFormatAlias
+	(Abi_AbiAlias)(0),                         // 2: android.bundle.Abi.AbiAlias
+	(Sanitizer_SanitizerAlias)(0),             // 3: android.bundle.Sanitizer.SanitizerAlias
+	(*VariantTargeting)(nil),                  // 4: android.bundle.VariantTargeting
+	(*ApkTargeting)(nil),                      // 5: android.bundle.ApkTargeting
+	(*ModuleTargeting)(nil),                   // 6: android.bundle.ModuleTargeting
+	(*UserCountriesTargeting)(nil),            // 7: android.bundle.UserCountriesTargeting
+	(*ScreenDensity)(nil),                     // 8: android.bundle.ScreenDensity
+	(*Int32Value)(nil),                        // 9: android.bundle.Int32Value
+	(*SdkVersion)(nil),                        // 10: android.bundle.SdkVersion
+	(*GraphicsApi)(nil),                       // 11: android.bundle.GraphicsApi
+	(*VulkanVersion)(nil),                     // 12: android.bundle.VulkanVersion
+	(*OpenGlVersion)(nil),                     // 13: android.bundle.OpenGlVersion
+	(*TextureCompressionFormat)(nil),          // 14: android.bundle.TextureCompressionFormat
+	(*Abi)(nil),                               // 15: android.bundle.Abi
+	(*MultiAbi)(nil),                          // 16: android.bundle.MultiAbi
+	(*Sanitizer)(nil),                         // 17: android.bundle.Sanitizer
+	(*DeviceFeature)(nil),                     // 18: android.bundle.DeviceFeature
+	(*AssetsDirectoryTargeting)(nil),          // 19: android.bundle.AssetsDirectoryTargeting
+	(*NativeDirectoryTargeting)(nil),          // 20: android.bundle.NativeDirectoryTargeting
+	(*ApexImageTargeting)(nil),                // 21: android.bundle.ApexImageTargeting
+	(*AbiTargeting)(nil),                      // 22: android.bundle.AbiTargeting
+	(*MultiAbiTargeting)(nil),                 // 23: android.bundle.MultiAbiTargeting
+	(*ScreenDensityTargeting)(nil),            // 24: android.bundle.ScreenDensityTargeting
+	(*LanguageTargeting)(nil),                 // 25: android.bundle.LanguageTargeting
+	(*GraphicsApiTargeting)(nil),              // 26: android.bundle.GraphicsApiTargeting
+	(*SdkVersionTargeting)(nil),               // 27: android.bundle.SdkVersionTargeting
+	(*TextureCompressionFormatTargeting)(nil), // 28: android.bundle.TextureCompressionFormatTargeting
+	(*SanitizerTargeting)(nil),                // 29: android.bundle.SanitizerTargeting
+	(*DeviceFeatureTargeting)(nil),            // 30: android.bundle.DeviceFeatureTargeting
+}
+var file_targeting_proto_depIdxs = []int32{
+	27, // 0: android.bundle.VariantTargeting.sdk_version_targeting:type_name -> android.bundle.SdkVersionTargeting
+	22, // 1: android.bundle.VariantTargeting.abi_targeting:type_name -> android.bundle.AbiTargeting
+	24, // 2: android.bundle.VariantTargeting.screen_density_targeting:type_name -> android.bundle.ScreenDensityTargeting
+	23, // 3: android.bundle.VariantTargeting.multi_abi_targeting:type_name -> android.bundle.MultiAbiTargeting
+	28, // 4: android.bundle.VariantTargeting.texture_compression_format_targeting:type_name -> android.bundle.TextureCompressionFormatTargeting
+	22, // 5: android.bundle.ApkTargeting.abi_targeting:type_name -> android.bundle.AbiTargeting
+	26, // 6: android.bundle.ApkTargeting.graphics_api_targeting:type_name -> android.bundle.GraphicsApiTargeting
+	25, // 7: android.bundle.ApkTargeting.language_targeting:type_name -> android.bundle.LanguageTargeting
+	24, // 8: android.bundle.ApkTargeting.screen_density_targeting:type_name -> android.bundle.ScreenDensityTargeting
+	27, // 9: android.bundle.ApkTargeting.sdk_version_targeting:type_name -> android.bundle.SdkVersionTargeting
+	28, // 10: android.bundle.ApkTargeting.texture_compression_format_targeting:type_name -> android.bundle.TextureCompressionFormatTargeting
+	23, // 11: android.bundle.ApkTargeting.multi_abi_targeting:type_name -> android.bundle.MultiAbiTargeting
+	29, // 12: android.bundle.ApkTargeting.sanitizer_targeting:type_name -> android.bundle.SanitizerTargeting
+	27, // 13: android.bundle.ModuleTargeting.sdk_version_targeting:type_name -> android.bundle.SdkVersionTargeting
+	30, // 14: android.bundle.ModuleTargeting.device_feature_targeting:type_name -> android.bundle.DeviceFeatureTargeting
+	7,  // 15: android.bundle.ModuleTargeting.user_countries_targeting:type_name -> android.bundle.UserCountriesTargeting
+	0,  // 16: android.bundle.ScreenDensity.density_alias:type_name -> android.bundle.ScreenDensity.DensityAlias
+	9,  // 17: android.bundle.SdkVersion.min:type_name -> android.bundle.Int32Value
+	13, // 18: android.bundle.GraphicsApi.min_open_gl_version:type_name -> android.bundle.OpenGlVersion
+	12, // 19: android.bundle.GraphicsApi.min_vulkan_version:type_name -> android.bundle.VulkanVersion
+	1,  // 20: android.bundle.TextureCompressionFormat.alias:type_name -> android.bundle.TextureCompressionFormat.TextureCompressionFormatAlias
+	2,  // 21: android.bundle.Abi.alias:type_name -> android.bundle.Abi.AbiAlias
+	15, // 22: android.bundle.MultiAbi.abi:type_name -> android.bundle.Abi
+	3,  // 23: android.bundle.Sanitizer.alias:type_name -> android.bundle.Sanitizer.SanitizerAlias
+	22, // 24: android.bundle.AssetsDirectoryTargeting.abi:type_name -> android.bundle.AbiTargeting
+	26, // 25: android.bundle.AssetsDirectoryTargeting.graphics_api:type_name -> android.bundle.GraphicsApiTargeting
+	28, // 26: android.bundle.AssetsDirectoryTargeting.texture_compression_format:type_name -> android.bundle.TextureCompressionFormatTargeting
+	25, // 27: android.bundle.AssetsDirectoryTargeting.language:type_name -> android.bundle.LanguageTargeting
+	15, // 28: android.bundle.NativeDirectoryTargeting.abi:type_name -> android.bundle.Abi
+	11, // 29: android.bundle.NativeDirectoryTargeting.graphics_api:type_name -> android.bundle.GraphicsApi
+	14, // 30: android.bundle.NativeDirectoryTargeting.texture_compression_format:type_name -> android.bundle.TextureCompressionFormat
+	17, // 31: android.bundle.NativeDirectoryTargeting.sanitizer:type_name -> android.bundle.Sanitizer
+	23, // 32: android.bundle.ApexImageTargeting.multi_abi:type_name -> android.bundle.MultiAbiTargeting
+	15, // 33: android.bundle.AbiTargeting.value:type_name -> android.bundle.Abi
+	15, // 34: android.bundle.AbiTargeting.alternatives:type_name -> android.bundle.Abi
+	16, // 35: android.bundle.MultiAbiTargeting.value:type_name -> android.bundle.MultiAbi
+	16, // 36: android.bundle.MultiAbiTargeting.alternatives:type_name -> android.bundle.MultiAbi
+	8,  // 37: android.bundle.ScreenDensityTargeting.value:type_name -> android.bundle.ScreenDensity
+	8,  // 38: android.bundle.ScreenDensityTargeting.alternatives:type_name -> android.bundle.ScreenDensity
+	11, // 39: android.bundle.GraphicsApiTargeting.value:type_name -> android.bundle.GraphicsApi
+	11, // 40: android.bundle.GraphicsApiTargeting.alternatives:type_name -> android.bundle.GraphicsApi
+	10, // 41: android.bundle.SdkVersionTargeting.value:type_name -> android.bundle.SdkVersion
+	10, // 42: android.bundle.SdkVersionTargeting.alternatives:type_name -> android.bundle.SdkVersion
+	14, // 43: android.bundle.TextureCompressionFormatTargeting.value:type_name -> android.bundle.TextureCompressionFormat
+	14, // 44: android.bundle.TextureCompressionFormatTargeting.alternatives:type_name -> android.bundle.TextureCompressionFormat
+	17, // 45: android.bundle.SanitizerTargeting.value:type_name -> android.bundle.Sanitizer
+	18, // 46: android.bundle.DeviceFeatureTargeting.required_feature:type_name -> android.bundle.DeviceFeature
+	47, // [47:47] is the sub-list for method output_type
+	47, // [47:47] is the sub-list for method input_type
+	47, // [47:47] is the sub-list for extension type_name
+	47, // [47:47] is the sub-list for extension extendee
+	0,  // [0:47] is the sub-list for field type_name
+}
+
+func init() { file_targeting_proto_init() }
+func file_targeting_proto_init() {
+	if File_targeting_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_targeting_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*VariantTargeting); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_targeting_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ApkTargeting); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_targeting_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ModuleTargeting); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_targeting_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*UserCountriesTargeting); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_targeting_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ScreenDensity); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_targeting_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Int32Value); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_targeting_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*SdkVersion); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_targeting_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*GraphicsApi); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_targeting_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*VulkanVersion); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_targeting_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*OpenGlVersion); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_targeting_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*TextureCompressionFormat); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_targeting_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Abi); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_targeting_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*MultiAbi); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_targeting_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Sanitizer); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_targeting_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*DeviceFeature); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_targeting_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*AssetsDirectoryTargeting); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_targeting_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*NativeDirectoryTargeting); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_targeting_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ApexImageTargeting); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_targeting_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*AbiTargeting); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_targeting_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*MultiAbiTargeting); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_targeting_proto_msgTypes[20].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ScreenDensityTargeting); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_targeting_proto_msgTypes[21].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*LanguageTargeting); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_targeting_proto_msgTypes[22].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*GraphicsApiTargeting); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_targeting_proto_msgTypes[23].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*SdkVersionTargeting); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_targeting_proto_msgTypes[24].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*TextureCompressionFormatTargeting); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_targeting_proto_msgTypes[25].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*SanitizerTargeting); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_targeting_proto_msgTypes[26].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*DeviceFeatureTargeting); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	file_targeting_proto_msgTypes[4].OneofWrappers = []interface{}{
+		(*ScreenDensity_DensityAlias_)(nil),
+		(*ScreenDensity_DensityDpi)(nil),
+	}
+	file_targeting_proto_msgTypes[7].OneofWrappers = []interface{}{
+		(*GraphicsApi_MinOpenGlVersion)(nil),
+		(*GraphicsApi_MinVulkanVersion)(nil),
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_targeting_proto_rawDesc,
+			NumEnums:      4,
+			NumMessages:   27,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_targeting_proto_goTypes,
+		DependencyIndexes: file_targeting_proto_depIdxs,
+		EnumInfos:         file_targeting_proto_enumTypes,
+		MessageInfos:      file_targeting_proto_msgTypes,
+	}.Build()
+	File_targeting_proto = out.File
+	file_targeting_proto_rawDesc = nil
+	file_targeting_proto_goTypes = nil
+	file_targeting_proto_depIdxs = nil
 }
diff --git a/cmd/extract_apks/bundle_proto/targeting.proto b/cmd/extract_apks/bundle_proto/targeting.proto
index cdc910b..a33edc7 100644
--- a/cmd/extract_apks/bundle_proto/targeting.proto
+++ b/cmd/extract_apks/bundle_proto/targeting.proto
@@ -6,7 +6,7 @@
 
 package android.bundle;
 
-option go_package = "android_bundle_proto";
+option go_package = "android/soong/cmd/extract_apks/bundle_proto";
 option java_package = "com.android.bundle";
 
 // Targeting on the level of variants.
diff --git a/cmd/extract_apks/main.go b/cmd/extract_apks/main.go
index db54ffb..6e51a28 100644
--- a/cmd/extract_apks/main.go
+++ b/cmd/extract_apks/main.go
@@ -27,9 +27,9 @@
 	"sort"
 	"strings"
 
-	"github.com/golang/protobuf/proto"
+	"google.golang.org/protobuf/proto"
 
-	"android/soong/cmd/extract_apks/bundle_proto"
+	android_bundle_proto "android/soong/cmd/extract_apks/bundle_proto"
 	"android/soong/third_party/zip"
 )
 
diff --git a/cmd/extract_apks/main_test.go b/cmd/extract_apks/main_test.go
index c3e6a2d..9fcf324 100644
--- a/cmd/extract_apks/main_test.go
+++ b/cmd/extract_apks/main_test.go
@@ -19,7 +19,7 @@
 	"reflect"
 	"testing"
 
-	"github.com/golang/protobuf/proto"
+	"google.golang.org/protobuf/encoding/prototext"
 
 	bp "android/soong/cmd/extract_apks/bundle_proto"
 	"android/soong/third_party/zip"
@@ -253,7 +253,7 @@
 	}
 	for _, testCase := range testCases {
 		var toc bp.BuildApksResult
-		if err := proto.UnmarshalText(testCase.protoText, &toc); err != nil {
+		if err := prototext.Unmarshal([]byte(testCase.protoText), &toc); err != nil {
 			t.Fatal(err)
 		}
 		for _, config := range testCase.configs {
@@ -407,7 +407,7 @@
 	}
 	for _, testCase := range testCases {
 		var toc bp.BuildApksResult
-		if err := proto.UnmarshalText(testCase.protoText, &toc); err != nil {
+		if err := prototext.Unmarshal([]byte(testCase.protoText), &toc); err != nil {
 			t.Fatal(err)
 		}
 		for _, config := range testCase.configs {
diff --git a/cmd/extract_linker/main.go b/cmd/extract_linker/main.go
index ea0bf4e..1280553 100644
--- a/cmd/extract_linker/main.go
+++ b/cmd/extract_linker/main.go
@@ -13,7 +13,7 @@
 // limitations under the License.
 
 // This tool extracts ELF LOAD segments from our linker binary, and produces an
-// assembly file and linker flags which will embed those segments as sections
+// assembly file and linker script which will embed those segments as sections
 // in another binary.
 package main
 
@@ -26,15 +26,15 @@
 	"io/ioutil"
 	"log"
 	"os"
-	"strings"
+	"strconv"
 )
 
 func main() {
 	var asmPath string
-	var flagsPath string
+	var scriptPath string
 
 	flag.StringVar(&asmPath, "s", "", "Path to save the assembly file")
-	flag.StringVar(&flagsPath, "f", "", "Path to save the linker flags")
+	flag.StringVar(&scriptPath, "T", "", "Path to save the linker script")
 	flag.Parse()
 
 	f, err := os.Open(flag.Arg(0))
@@ -49,20 +49,26 @@
 	}
 
 	asm := &bytes.Buffer{}
+	script := &bytes.Buffer{}
 	baseLoadAddr := uint64(0x1000)
 	load := 0
-	linkFlags := []string{}
 
 	fmt.Fprintln(asm, ".globl __dlwrap_linker_offset")
 	fmt.Fprintf(asm, ".set __dlwrap_linker_offset, 0x%x\n", baseLoadAddr)
 
+	fmt.Fprintln(script, "ENTRY(__dlwrap__start)")
+	fmt.Fprintln(script, "SECTIONS {")
+
+	progsWithFlagsCount := make(map[string]int)
+
 	for _, prog := range ef.Progs {
 		if prog.Type != elf.PT_LOAD {
 			continue
 		}
 
-		sectionName := fmt.Sprintf(".linker.sect%d", load)
-		symName := fmt.Sprintf("__dlwrap_linker_sect%d", load)
+		progName := progNameFromFlags(prog.Flags, progsWithFlagsCount)
+		sectionName := ".linker_" + progName
+		symName := "__dlwrap_linker_" + progName
 
 		flags := ""
 		if prog.Flags&elf.PF_W != 0 {
@@ -73,12 +79,17 @@
 		}
 		fmt.Fprintf(asm, ".section %s, \"a%s\"\n", sectionName, flags)
 
+		if load == 0 {
+			fmt.Fprintln(asm, ".globl __dlwrap_linker")
+			fmt.Fprintln(asm, "__dlwrap_linker:")
+			fmt.Fprintln(asm)
+		}
+
 		fmt.Fprintf(asm, ".globl %s\n%s:\n\n", symName, symName)
 
-		linkFlags = append(linkFlags,
-			fmt.Sprintf("-Wl,--undefined=%s", symName),
-			fmt.Sprintf("-Wl,--section-start=%s=0x%x",
-				sectionName, baseLoadAddr+prog.Vaddr))
+		fmt.Fprintf(script, "  %s 0x%x : {\n", sectionName, baseLoadAddr+prog.Vaddr)
+		fmt.Fprintf(script, "    KEEP(*(%s));\n", sectionName)
+		fmt.Fprintln(script, "  }")
 
 		buffer, _ := ioutil.ReadAll(prog.Open())
 		bytesToAsm(asm, buffer)
@@ -97,16 +108,24 @@
 		load += 1
 	}
 
+	fmt.Fprintln(asm, ".globl __dlwrap_linker_end")
+	fmt.Fprintln(asm, "__dlwrap_linker_end:")
+	fmt.Fprintln(asm)
+
+	fmt.Fprintln(asm, `.section .note.android.embedded_linker,"a",%note`)
+
+	fmt.Fprintln(script, "}")
+	fmt.Fprintln(script, "INSERT BEFORE .note.android.embedded_linker;")
+
 	if asmPath != "" {
 		if err := ioutil.WriteFile(asmPath, asm.Bytes(), 0777); err != nil {
 			log.Fatalf("Unable to write %q: %v", asmPath, err)
 		}
 	}
 
-	if flagsPath != "" {
-		flags := strings.Join(linkFlags, " ")
-		if err := ioutil.WriteFile(flagsPath, []byte(flags), 0777); err != nil {
-			log.Fatalf("Unable to write %q: %v", flagsPath, err)
+	if scriptPath != "" {
+		if err := ioutil.WriteFile(scriptPath, script.Bytes(), 0777); err != nil {
+			log.Fatalf("Unable to write %q: %v", scriptPath, err)
 		}
 	}
 }
@@ -125,3 +144,26 @@
 	}
 	fmt.Fprintln(asm)
 }
+
+func progNameFromFlags(flags elf.ProgFlag, progsWithFlagsCount map[string]int) string {
+	s := ""
+	if flags&elf.PF_R != 0 {
+		s += "r"
+	}
+	if flags&elf.PF_W != 0 {
+		s += "w"
+	}
+	if flags&elf.PF_X != 0 {
+		s += "x"
+	}
+
+	count := progsWithFlagsCount[s]
+	count++
+	progsWithFlagsCount[s] = count
+
+	if count > 1 {
+		s += strconv.Itoa(count)
+	}
+
+	return s
+}
diff --git a/cmd/host_bionic_inject/Android.bp b/cmd/go2bp/Android.bp
similarity index 76%
copy from cmd/host_bionic_inject/Android.bp
copy to cmd/go2bp/Android.bp
index 16bc179..53d70b6 100644
--- a/cmd/host_bionic_inject/Android.bp
+++ b/cmd/go2bp/Android.bp
@@ -1,4 +1,4 @@
-// Copyright 2018 Google Inc. All rights reserved.
+// Copyright 2021 Google Inc. All rights reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -17,8 +17,10 @@
 }
 
 blueprint_go_binary {
-    name: "host_bionic_inject",
-    deps: ["soong-symbol_inject"],
-    srcs: ["host_bionic_inject.go"],
-    testSrcs: ["host_bionic_inject_test.go"],
+    name: "go2bp",
+    deps: [
+        "blueprint-proptools",
+        "bpfix-lib",
+    ],
+    srcs: ["go2bp.go"],
 }
diff --git a/cmd/go2bp/go2bp.go b/cmd/go2bp/go2bp.go
new file mode 100644
index 0000000..07cb5df
--- /dev/null
+++ b/cmd/go2bp/go2bp.go
@@ -0,0 +1,410 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+	"bufio"
+	"bytes"
+	"encoding/json"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"sort"
+	"strings"
+	"text/template"
+
+	"github.com/google/blueprint/proptools"
+
+	"android/soong/bpfix/bpfix"
+)
+
+type RewriteNames []RewriteName
+type RewriteName struct {
+	prefix string
+	repl   string
+}
+
+func (r *RewriteNames) String() string {
+	return ""
+}
+
+func (r *RewriteNames) Set(v string) error {
+	split := strings.SplitN(v, "=", 2)
+	if len(split) != 2 {
+		return fmt.Errorf("Must be in the form of <prefix>=<replace>")
+	}
+	*r = append(*r, RewriteName{
+		prefix: split[0],
+		repl:   split[1],
+	})
+	return nil
+}
+
+func (r *RewriteNames) GoToBp(name string) string {
+	ret := name
+	for _, r := range *r {
+		prefix := r.prefix
+		if name == prefix {
+			ret = r.repl
+			break
+		}
+		prefix += "/"
+		if strings.HasPrefix(name, prefix) {
+			ret = r.repl + "-" + strings.TrimPrefix(name, prefix)
+		}
+	}
+	return strings.ReplaceAll(ret, "/", "-")
+}
+
+var rewriteNames = RewriteNames{}
+
+type Exclude map[string]bool
+
+func (e Exclude) String() string {
+	return ""
+}
+
+func (e Exclude) Set(v string) error {
+	e[v] = true
+	return nil
+}
+
+var excludes = make(Exclude)
+var excludeDeps = make(Exclude)
+var excludeSrcs = make(Exclude)
+
+type StringList []string
+
+func (l *StringList) String() string {
+	return strings.Join(*l, " ")
+}
+
+func (l *StringList) Set(v string) error {
+	*l = append(*l, strings.Fields(v)...)
+	return nil
+}
+
+type GoModule struct {
+	Dir string
+}
+
+type GoPackage struct {
+	ExportToAndroid bool
+
+	Dir         string
+	ImportPath  string
+	Name        string
+	Imports     []string
+	GoFiles     []string
+	TestGoFiles []string
+	TestImports []string
+
+	Module *GoModule
+}
+
+func (g GoPackage) IsCommand() bool {
+	return g.Name == "main"
+}
+
+func (g GoPackage) BpModuleType() string {
+	if g.IsCommand() {
+		return "blueprint_go_binary"
+	}
+	return "bootstrap_go_package"
+}
+
+func (g GoPackage) BpName() string {
+	if g.IsCommand() {
+		return rewriteNames.GoToBp(filepath.Base(g.ImportPath))
+	}
+	return rewriteNames.GoToBp(g.ImportPath)
+}
+
+func (g GoPackage) BpDeps(deps []string) []string {
+	var ret []string
+	for _, d := range deps {
+		// Ignore stdlib dependencies
+		if !strings.Contains(d, ".") {
+			continue
+		}
+		if _, ok := excludeDeps[d]; ok {
+			continue
+		}
+		name := rewriteNames.GoToBp(d)
+		ret = append(ret, name)
+	}
+	return ret
+}
+
+func (g GoPackage) BpSrcs(srcs []string) []string {
+	var ret []string
+	prefix, err := filepath.Rel(g.Module.Dir, g.Dir)
+	if err != nil {
+		panic(err)
+	}
+	for _, f := range srcs {
+		f = filepath.Join(prefix, f)
+		if _, ok := excludeSrcs[f]; ok {
+			continue
+		}
+		ret = append(ret, f)
+	}
+	return ret
+}
+
+// AllImports combines Imports and TestImports, as blueprint does not differentiate these.
+func (g GoPackage) AllImports() []string {
+	imports := append([]string(nil), g.Imports...)
+	imports = append(imports, g.TestImports...)
+
+	if len(imports) == 0 {
+		return nil
+	}
+
+	// Sort and de-duplicate
+	sort.Strings(imports)
+	j := 0
+	for i := 1; i < len(imports); i++ {
+		if imports[i] == imports[j] {
+			continue
+		}
+		j++
+		imports[j] = imports[i]
+	}
+	return imports[:j+1]
+}
+
+var bpTemplate = template.Must(template.New("bp").Parse(`
+{{.BpModuleType}} {
+    name: "{{.BpName}}",
+    {{- if not .IsCommand}}
+    pkgPath: "{{.ImportPath}}",
+    {{- end}}
+    {{- if .BpDeps .AllImports}}
+    deps: [
+        {{- range .BpDeps .AllImports}}
+        "{{.}}",
+        {{- end}}
+    ],
+    {{- end}}
+    {{- if .BpSrcs .GoFiles}}
+    srcs: [
+        {{- range .BpSrcs .GoFiles}}
+        "{{.}}",
+        {{- end}}
+    ],
+    {{- end}}
+    {{- if .BpSrcs .TestGoFiles}}
+    testSrcs: [
+    	{{- range .BpSrcs .TestGoFiles}}
+        "{{.}}",
+       {{- end}}
+    ],
+    {{- end}}
+}
+`))
+
+func rerunForRegen(filename string) error {
+	buf, err := ioutil.ReadFile(filename)
+	if err != nil {
+		return err
+	}
+
+	scanner := bufio.NewScanner(bytes.NewBuffer(buf))
+
+	// Skip the first line in the file
+	for i := 0; i < 2; i++ {
+		if !scanner.Scan() {
+			if scanner.Err() != nil {
+				return scanner.Err()
+			} else {
+				return fmt.Errorf("unexpected EOF")
+			}
+		}
+	}
+
+	// Extract the old args from the file
+	line := scanner.Text()
+	if strings.HasPrefix(line, "// go2bp ") {
+		line = strings.TrimPrefix(line, "// go2bp ")
+	} else {
+		return fmt.Errorf("unexpected second line: %q", line)
+	}
+	args := strings.Split(line, " ")
+	lastArg := args[len(args)-1]
+	args = args[:len(args)-1]
+
+	// Append all current command line args except -regen <file> to the ones from the file
+	for i := 1; i < len(os.Args); i++ {
+		if os.Args[i] == "-regen" || os.Args[i] == "--regen" {
+			i++
+		} else {
+			args = append(args, os.Args[i])
+		}
+	}
+	args = append(args, lastArg)
+
+	cmd := os.Args[0] + " " + strings.Join(args, " ")
+	// Re-exec pom2bp with the new arguments
+	output, err := exec.Command("/bin/sh", "-c", cmd).Output()
+	if exitErr, _ := err.(*exec.ExitError); exitErr != nil {
+		return fmt.Errorf("failed to run %s\n%s", cmd, string(exitErr.Stderr))
+	} else if err != nil {
+		return err
+	}
+
+	return ioutil.WriteFile(filename, output, 0666)
+}
+
+func main() {
+	flag.Usage = func() {
+		fmt.Fprintf(os.Stderr, `go2bp, a tool to create Android.bp files from go modules
+
+The tool will extract the necessary information from Go files to create an Android.bp that can
+compile them. This needs to be run from the same directory as the go.mod file.
+
+Usage: %s [--rewrite <pkg-prefix>=<replace>] [-exclude <package>] [-regen <file>]
+
+  -rewrite <pkg-prefix>=<replace>
+     rewrite can be used to specify mappings between go package paths and Android.bp modules. The -rewrite
+     option can be specified multiple times. When determining the Android.bp module for a given Go
+     package, mappings are searched in the order they were specified. The first <pkg-prefix> matching
+     either the package directly, or as the prefix '<pkg-prefix>/' will be replaced with <replace>.
+     After all replacements are finished, all '/' characters are replaced with '-'.
+  -exclude <package>
+     Don't put the specified go package in the Android.bp file.
+  -exclude-deps <package>
+     Don't put the specified go package in the dependency lists.
+  -exclude-srcs <module>
+     Don't put the specified source files in srcs or testSrcs lists.
+  -limit <package>
+     If set, limit the output to the specified packages and their dependencies.
+  -skip-tests
+     If passed, don't write out any test srcs or dependencies to the Android.bp output.
+  -regen <file>
+     Read arguments from <file> and overwrite it.
+
+`, os.Args[0])
+	}
+
+	var regen string
+	var skipTests bool
+	limit := StringList{}
+
+	flag.Var(&excludes, "exclude", "Exclude go package")
+	flag.Var(&excludeDeps, "exclude-dep", "Exclude go package from deps")
+	flag.Var(&excludeSrcs, "exclude-src", "Exclude go file from source lists")
+	flag.Var(&rewriteNames, "rewrite", "Regex(es) to rewrite artifact names")
+	flag.Var(&limit, "limit", "If set, only includes the dependencies of the listed packages")
+	flag.BoolVar(&skipTests, "skip-tests", false, "Whether to skip test sources")
+	flag.StringVar(&regen, "regen", "", "Rewrite specified file")
+	flag.Parse()
+
+	if regen != "" {
+		err := rerunForRegen(regen)
+		if err != nil {
+			fmt.Fprintln(os.Stderr, err)
+			os.Exit(1)
+		}
+		os.Exit(0)
+	}
+
+	if flag.NArg() != 0 {
+		fmt.Fprintf(os.Stderr, "Unused argument detected: %v\n", flag.Args())
+		os.Exit(1)
+	}
+
+	if _, err := os.Stat("go.mod"); err != nil {
+		fmt.Fprintln(os.Stderr, "go.mod file not found")
+		os.Exit(1)
+	}
+
+	cmd := exec.Command("go", "list", "-json", "./...")
+	output, err := cmd.Output()
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "Failed to dump the go packages: %v\n", err)
+		os.Exit(1)
+	}
+	decoder := json.NewDecoder(bytes.NewReader(output))
+
+	pkgs := []*GoPackage{}
+	pkgMap := map[string]*GoPackage{}
+	for decoder.More() {
+		pkg := GoPackage{}
+		err := decoder.Decode(&pkg)
+		if err != nil {
+			fmt.Fprintf(os.Stderr, "Failed to parse json: %v\n", err)
+			os.Exit(1)
+		}
+		if len(limit) == 0 {
+			pkg.ExportToAndroid = true
+		}
+		if skipTests {
+			pkg.TestGoFiles = nil
+			pkg.TestImports = nil
+		}
+		pkgs = append(pkgs, &pkg)
+		pkgMap[pkg.ImportPath] = &pkg
+	}
+
+	buf := &bytes.Buffer{}
+
+	fmt.Fprintln(buf, "// Automatically generated with:")
+	fmt.Fprintln(buf, "// go2bp", strings.Join(proptools.ShellEscapeList(os.Args[1:]), " "))
+
+	var mark func(string)
+	mark = func(pkgName string) {
+		if excludes[pkgName] {
+			return
+		}
+		if pkg, ok := pkgMap[pkgName]; ok && !pkg.ExportToAndroid {
+			pkg.ExportToAndroid = true
+			for _, dep := range pkg.AllImports() {
+				if !excludeDeps[dep] {
+					mark(dep)
+				}
+			}
+		}
+	}
+
+	for _, pkgName := range limit {
+		mark(pkgName)
+	}
+
+	for _, pkg := range pkgs {
+		if !pkg.ExportToAndroid || excludes[pkg.ImportPath] {
+			continue
+		}
+		if len(pkg.BpSrcs(pkg.GoFiles)) == 0 && len(pkg.BpSrcs(pkg.TestGoFiles)) == 0 {
+			continue
+		}
+		err := bpTemplate.Execute(buf, pkg)
+		if err != nil {
+			fmt.Fprintln(os.Stderr, "Error writing", pkg.Name, err)
+			os.Exit(1)
+		}
+	}
+
+	out, err := bpfix.Reformat(buf.String())
+	if err != nil {
+		fmt.Fprintln(os.Stderr, "Error formatting output", err)
+		os.Exit(1)
+	}
+
+	os.Stdout.WriteString(out)
+}
diff --git a/cmd/host_bionic_inject/Android.bp b/cmd/host_bionic_verify/Android.bp
similarity index 82%
rename from cmd/host_bionic_inject/Android.bp
rename to cmd/host_bionic_verify/Android.bp
index 16bc179..4e7c379 100644
--- a/cmd/host_bionic_inject/Android.bp
+++ b/cmd/host_bionic_verify/Android.bp
@@ -17,8 +17,7 @@
 }
 
 blueprint_go_binary {
-    name: "host_bionic_inject",
-    deps: ["soong-symbol_inject"],
-    srcs: ["host_bionic_inject.go"],
-    testSrcs: ["host_bionic_inject_test.go"],
+    name: "host_bionic_verify",
+    srcs: ["host_bionic_verify.go"],
+    testSrcs: ["host_bionic_verify_test.go"],
 }
diff --git a/cmd/host_bionic_inject/host_bionic_inject.go b/cmd/host_bionic_verify/host_bionic_verify.go
similarity index 68%
rename from cmd/host_bionic_inject/host_bionic_inject.go
rename to cmd/host_bionic_verify/host_bionic_verify.go
index ce8b062..52400a3 100644
--- a/cmd/host_bionic_inject/host_bionic_inject.go
+++ b/cmd/host_bionic_verify/host_bionic_verify.go
@@ -12,8 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-// Verifies a host bionic executable with an embedded linker, then injects
-// the address of the _start function for the linker_wrapper to use.
+// Verifies a host bionic executable with an embedded linker.
 package main
 
 import (
@@ -22,19 +21,16 @@
 	"fmt"
 	"io"
 	"os"
-
-	"android/soong/symbol_inject"
 )
 
 func main() {
-	var inputFile, linkerFile, outputFile string
+	var inputFile, linkerFile string
 
 	flag.StringVar(&inputFile, "i", "", "Input file")
 	flag.StringVar(&linkerFile, "l", "", "Linker file")
-	flag.StringVar(&outputFile, "o", "", "Output file")
 	flag.Parse()
 
-	if inputFile == "" || linkerFile == "" || outputFile == "" || flag.NArg() != 0 {
+	if inputFile == "" || linkerFile == "" || flag.NArg() != 0 {
 		flag.Usage()
 		os.Exit(1)
 	}
@@ -46,75 +42,52 @@
 	}
 	defer r.Close()
 
-	file, err := symbol_inject.OpenFile(r)
-	if err != nil {
-		fmt.Fprintln(os.Stderr, err.Error())
-		os.Exit(3)
-	}
-
 	linker, err := elf.Open(linkerFile)
 	if err != nil {
 		fmt.Fprintln(os.Stderr, err.Error())
 		os.Exit(4)
 	}
 
-	startAddr, err := parseElf(r, linker)
+	err = checkElf(r, linker)
 	if err != nil {
 		fmt.Fprintln(os.Stderr, err.Error())
 		os.Exit(5)
 	}
-
-	w, err := os.OpenFile(outputFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777)
-	if err != nil {
-		fmt.Fprintln(os.Stderr, err.Error())
-		os.Exit(6)
-	}
-	defer w.Close()
-
-	err = symbol_inject.InjectUint64Symbol(file, w, "__dlwrap_original_start", startAddr)
-	if err != nil {
-		fmt.Fprintln(os.Stderr, err.Error())
-		os.Exit(7)
-	}
 }
 
 // Check the ELF file, and return the address to the _start function
-func parseElf(r io.ReaderAt, linker *elf.File) (uint64, error) {
+func checkElf(r io.ReaderAt, linker *elf.File) error {
 	file, err := elf.NewFile(r)
 	if err != nil {
-		return 0, err
+		return err
 	}
 
 	symbols, err := file.Symbols()
 	if err != nil {
-		return 0, err
+		return err
 	}
 
 	for _, prog := range file.Progs {
 		if prog.Type == elf.PT_INTERP {
-			return 0, fmt.Errorf("File should not have a PT_INTERP header")
+			return fmt.Errorf("File should not have a PT_INTERP header")
 		}
 	}
 
 	if dlwrap_start, err := findSymbol(symbols, "__dlwrap__start"); err != nil {
-		return 0, err
+		return err
 	} else if dlwrap_start.Value != file.Entry {
-		return 0, fmt.Errorf("Expected file entry(0x%x) to point to __dlwrap_start(0x%x)",
+		return fmt.Errorf("Expected file entry(0x%x) to point to __dlwrap_start(0x%x)",
 			file.Entry, dlwrap_start.Value)
 	}
 
 	err = checkLinker(file, linker, symbols)
 	if err != nil {
-		return 0, fmt.Errorf("Linker executable failed verification against app embedded linker: %s\n"+
+		return fmt.Errorf("Linker executable failed verification against app embedded linker: %s\n"+
 			"linker might not be in sync with crtbegin_dynamic.o.",
 			err)
 	}
 
-	start, err := findSymbol(symbols, "_start")
-	if err != nil {
-		return 0, fmt.Errorf("Failed to find _start symbol")
-	}
-	return start.Value, nil
+	return nil
 }
 
 func findSymbol(symbols []elf.Symbol, name string) (elf.Symbol, error) {
diff --git a/cmd/host_bionic_inject/host_bionic_inject_test.go b/cmd/host_bionic_verify/host_bionic_verify_test.go
similarity index 100%
rename from cmd/host_bionic_inject/host_bionic_inject_test.go
rename to cmd/host_bionic_verify/host_bionic_verify_test.go
diff --git a/cmd/multiproduct_kati/Android.bp b/cmd/multiproduct_kati/Android.bp
index 21d8e21..e5be6c0 100644
--- a/cmd/multiproduct_kati/Android.bp
+++ b/cmd/multiproduct_kati/Android.bp
@@ -19,8 +19,8 @@
 blueprint_go_binary {
     name: "multiproduct_kati",
     deps: [
-        "soong-ui-build",
         "soong-ui-logger",
+        "soong-ui-signal",
         "soong-ui-terminal",
         "soong-ui-tracer",
         "soong-zip",
diff --git a/cmd/multiproduct_kati/main.go b/cmd/multiproduct_kati/main.go
index 55a5470..fa63b46 100644
--- a/cmd/multiproduct_kati/main.go
+++ b/cmd/multiproduct_kati/main.go
@@ -15,22 +15,25 @@
 package main
 
 import (
+	"bufio"
 	"context"
 	"flag"
 	"fmt"
 	"io"
 	"io/ioutil"
+	"log"
 	"os"
+	"os/exec"
 	"path/filepath"
+	"regexp"
 	"runtime"
 	"strings"
 	"sync"
 	"syscall"
 	"time"
 
-	"android/soong/finder"
-	"android/soong/ui/build"
 	"android/soong/ui/logger"
+	"android/soong/ui/signal"
 	"android/soong/ui/status"
 	"android/soong/ui/terminal"
 	"android/soong/ui/tracer"
@@ -155,28 +158,82 @@
 }
 
 type mpContext struct {
-	Context context.Context
-	Logger  logger.Logger
-	Status  status.ToolStatus
-	Tracer  tracer.Tracer
-	Finder  *finder.Finder
-	Config  build.Config
+	Logger logger.Logger
+	Status status.ToolStatus
 
-	LogsDir string
+	SoongUi     string
+	MainOutDir  string
+	MainLogsDir string
+}
+
+func detectTotalRAM() uint64 {
+	var info syscall.Sysinfo_t
+	err := syscall.Sysinfo(&info)
+	if err != nil {
+		panic(err)
+	}
+	return info.Totalram * uint64(info.Unit)
+}
+
+func findNamedProducts(soongUi string, log logger.Logger) []string {
+	cmd := exec.Command(soongUi, "--dumpvars-mode", "--vars=all_named_products")
+	output, err := cmd.Output()
+	if err != nil {
+		log.Fatalf("Cannot determine named products: %v", err)
+	}
+
+	rx := regexp.MustCompile(`^all_named_products='(.*)'$`)
+	match := rx.FindStringSubmatch(strings.TrimSpace(string(output)))
+	return strings.Fields(match[1])
+}
+
+// ensureEmptyFileExists ensures that the containing directory exists, and the
+// specified file exists. If it doesn't exist, it will write an empty file.
+func ensureEmptyFileExists(file string, log logger.Logger) {
+	if _, err := os.Stat(file); os.IsNotExist(err) {
+		f, err := os.Create(file)
+		if err != nil {
+			log.Fatalf("Error creating %s: %q\n", file, err)
+		}
+		f.Close()
+	} else if err != nil {
+		log.Fatalf("Error checking %s: %q\n", file, err)
+	}
+}
+
+func outDirBase() string {
+	outDirBase := os.Getenv("OUT_DIR")
+	if outDirBase == "" {
+		return "out"
+	} else {
+		return outDirBase
+	}
+}
+
+func distDir(outDir string) string {
+	if distDir := os.Getenv("DIST_DIR"); distDir != "" {
+		return filepath.Clean(distDir)
+	} else {
+		return filepath.Join(outDir, "dist")
+	}
 }
 
 func main() {
 	stdio := terminal.StdioImpl{}
 
-	output := terminal.NewStatusOutput(stdio.Stdout(), "", false,
-		build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD"))
-
+	output := terminal.NewStatusOutput(stdio.Stdout(), "", false, false)
 	log := logger.New(output)
 	defer log.Cleanup()
 
+	for _, v := range os.Environ() {
+		log.Println("Environment: " + v)
+	}
+
+	log.Printf("Argv: %v\n", os.Args)
+
 	flag.Parse()
 
-	ctx, cancel := context.WithCancel(context.Background())
+	_, cancel := context.WithCancel(context.Background())
 	defer cancel()
 
 	trace := tracer.New(log)
@@ -189,61 +246,55 @@
 	var failures failureCount
 	stat.AddOutput(&failures)
 
-	build.SetupSignals(log, cancel, func() {
+	signal.SetupSignals(log, cancel, func() {
 		trace.Close()
 		log.Cleanup()
 		stat.Finish()
 	})
 
-	buildCtx := build.Context{ContextImpl: &build.ContextImpl{
-		Context: ctx,
-		Logger:  log,
-		Tracer:  trace,
-		Writer:  output,
-		Status:  stat,
-	}}
+	soongUi := "build/soong/soong_ui.bash"
 
-	args := ""
-	if *alternateResultDir {
-		args = "dist"
-	}
-	config := build.NewConfig(buildCtx, args)
-	if *outDir == "" {
+	var outputDir string
+	if *outDir != "" {
+		outputDir = *outDir
+	} else {
 		name := "multiproduct"
 		if !*incremental {
 			name += "-" + time.Now().Format("20060102150405")
 		}
-
-		*outDir = filepath.Join(config.OutDir(), name)
-
-		// Ensure the empty files exist in the output directory
-		// containing our output directory too. This is mostly for
-		// safety, but also triggers the ninja_build file so that our
-		// build servers know that they can parse the output as if it
-		// was ninja output.
-		build.SetupOutDir(buildCtx, config)
-
-		if err := os.MkdirAll(*outDir, 0777); err != nil {
-			log.Fatalf("Failed to create tempdir: %v", err)
-		}
+		outputDir = filepath.Join(outDirBase(), name)
 	}
-	config.Environment().Set("OUT_DIR", *outDir)
-	log.Println("Output directory:", *outDir)
 
-	logsDir := filepath.Join(config.OutDir(), "logs")
+	log.Println("Output directory:", outputDir)
+
+	// The ninja_build file is used by our buildbots to understand that the output
+	// can be parsed as ninja output.
+	if err := os.MkdirAll(outputDir, 0777); err != nil {
+		log.Fatalf("Failed to create output directory: %v", err)
+	}
+	ensureEmptyFileExists(filepath.Join(outputDir, "ninja_build"), log)
+
+	logsDir := filepath.Join(outputDir, "logs")
 	os.MkdirAll(logsDir, 0777)
 
-	build.SetupOutDir(buildCtx, config)
+	var configLogsDir string
+	if *alternateResultDir {
+		configLogsDir = filepath.Join(distDir(outDirBase()), "logs")
+	} else {
+		configLogsDir = outputDir
+	}
 
-	os.MkdirAll(config.LogsDir(), 0777)
-	log.SetOutput(filepath.Join(config.LogsDir(), "soong.log"))
-	trace.SetOutput(filepath.Join(config.LogsDir(), "build.trace"))
+	log.Println("Logs dir: " + configLogsDir)
+
+	os.MkdirAll(configLogsDir, 0777)
+	log.SetOutput(filepath.Join(configLogsDir, "soong.log"))
+	trace.SetOutput(filepath.Join(configLogsDir, "build.trace"))
 
 	var jobs = *numJobs
 	if jobs < 1 {
 		jobs = runtime.NumCPU() / 4
 
-		ramGb := int(config.TotalRAM() / 1024 / 1024 / 1024)
+		ramGb := int(detectTotalRAM() / (1024 * 1024 * 1024))
 		if ramJobs := ramGb / 25; ramGb > 0 && jobs > ramJobs {
 			jobs = ramJobs
 		}
@@ -256,17 +307,8 @@
 
 	setMaxFiles(log)
 
-	finder := build.NewSourceFinder(buildCtx, config)
-	defer finder.Shutdown()
-
-	build.FindSources(buildCtx, config, finder)
-
-	vars, err := build.DumpMakeVars(buildCtx, config, nil, []string{"all_named_products"})
-	if err != nil {
-		log.Fatal(err)
-	}
+	allProducts := findNamedProducts(soongUi, log)
 	var productsList []string
-	allProducts := strings.Fields(vars["all_named_products"])
 
 	if len(includeProducts) > 0 {
 		var missingProducts []string
@@ -314,19 +356,15 @@
 
 	log.Verbose("Got product list: ", finalProductsList)
 
-	s := buildCtx.Status.StartTool()
+	s := stat.StartTool()
 	s.SetTotalActions(len(finalProductsList))
 
 	mpCtx := &mpContext{
-		Context: ctx,
-		Logger:  log,
-		Status:  s,
-		Tracer:  trace,
-
-		Finder: finder,
-		Config: config,
-
-		LogsDir: logsDir,
+		Logger:      log,
+		Status:      s,
+		SoongUi:     soongUi,
+		MainOutDir:  outputDir,
+		MainLogsDir: logsDir,
 	}
 
 	products := make(chan string, len(productsList))
@@ -348,7 +386,7 @@
 					if product == "" {
 						return
 					}
-					buildProduct(mpCtx, product)
+					runSoongUiForProduct(mpCtx, product)
 				}
 			}
 		}()
@@ -360,10 +398,11 @@
 			FileArgs: []zip.FileArg{
 				{GlobDir: logsDir, SourcePrefixToStrip: logsDir},
 			},
-			OutputFilePath:   filepath.Join(config.RealDistDir(), "logs.zip"),
+			OutputFilePath:   filepath.Join(distDir(outDirBase()), "logs.zip"),
 			NumParallelJobs:  runtime.NumCPU(),
 			CompressionLevel: 5,
 		}
+		log.Printf("Logs zip: %v\n", args.OutputFilePath)
 		if err := zip.Zip(args); err != nil {
 			log.Fatalf("Error zipping logs: %v", err)
 		}
@@ -380,11 +419,33 @@
 	}
 }
 
-func buildProduct(mpctx *mpContext, product string) {
-	var stdLog string
+func cleanupAfterProduct(outDir, productZip string) {
+	if *keepArtifacts {
+		args := zip.ZipArgs{
+			FileArgs: []zip.FileArg{
+				{
+					GlobDir:             outDir,
+					SourcePrefixToStrip: outDir,
+				},
+			},
+			OutputFilePath:   productZip,
+			NumParallelJobs:  runtime.NumCPU(),
+			CompressionLevel: 5,
+		}
+		if err := zip.Zip(args); err != nil {
+			log.Fatalf("Error zipping artifacts: %v", err)
+		}
+	}
+	if !*incremental {
+		os.RemoveAll(outDir)
+	}
+}
 
-	outDir := filepath.Join(mpctx.Config.OutDir(), product)
-	logsDir := filepath.Join(mpctx.LogsDir, product)
+func runSoongUiForProduct(mpctx *mpContext, product string) {
+	outDir := filepath.Join(mpctx.MainOutDir, product)
+	logsDir := filepath.Join(mpctx.MainLogsDir, product)
+	productZip := filepath.Join(mpctx.MainOutDir, product+".zip")
+	consoleLogPath := filepath.Join(logsDir, "std.log")
 
 	if err := os.MkdirAll(outDir, 0777); err != nil {
 		mpctx.Logger.Fatalf("Error creating out directory: %v", err)
@@ -393,98 +454,74 @@
 		mpctx.Logger.Fatalf("Error creating log directory: %v", err)
 	}
 
-	stdLog = filepath.Join(logsDir, "std.log")
-	f, err := os.Create(stdLog)
+	consoleLogFile, err := os.Create(consoleLogPath)
 	if err != nil {
-		mpctx.Logger.Fatalf("Error creating std.log: %v", err)
+		mpctx.Logger.Fatalf("Error creating console log file: %v", err)
 	}
-	defer f.Close()
+	defer consoleLogFile.Close()
 
-	log := logger.New(f)
-	defer log.Cleanup()
-	log.SetOutput(filepath.Join(logsDir, "soong.log"))
+	consoleLogWriter := bufio.NewWriter(consoleLogFile)
+	defer consoleLogWriter.Flush()
+
+	args := []string{"--make-mode", "--skip-soong-tests", "--skip-ninja"}
+
+	if !*keepArtifacts {
+		args = append(args, "--empty-ninja-file")
+	}
+
+	if *onlyConfig {
+		args = append(args, "--config-only")
+	} else if *onlySoong {
+		args = append(args, "--soong-only")
+	}
+
+	cmd := exec.Command(mpctx.SoongUi, args...)
+	cmd.Stdout = consoleLogWriter
+	cmd.Stderr = consoleLogWriter
+	cmd.Env = append(os.Environ(),
+		"OUT_DIR="+outDir,
+		"TARGET_PRODUCT="+product,
+		"TARGET_BUILD_VARIANT="+*buildVariant,
+		"TARGET_BUILD_TYPE=release",
+		"TARGET_BUILD_APPS=",
+		"TARGET_BUILD_UNBUNDLED=")
+
+	if *alternateResultDir {
+		cmd.Env = append(cmd.Env,
+			"DIST_DIR="+filepath.Join(distDir(outDirBase()), "products/"+product))
+	}
 
 	action := &status.Action{
 		Description: product,
 		Outputs:     []string{product},
 	}
+
 	mpctx.Status.StartAction(action)
-	defer logger.Recover(func(err error) {
-		mpctx.Status.FinishAction(status.ActionResult{
-			Action: action,
-			Error:  err,
-			Output: errMsgFromLog(stdLog),
-		})
-	})
-
-	ctx := build.Context{ContextImpl: &build.ContextImpl{
-		Context: mpctx.Context,
-		Logger:  log,
-		Tracer:  mpctx.Tracer,
-		Writer:  f,
-		Thread:  mpctx.Tracer.NewThread(product),
-		Status:  &status.Status{},
-	}}
-	ctx.Status.AddOutput(terminal.NewStatusOutput(ctx.Writer, "", false,
-		build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD")))
-
-	args := append([]string(nil), flag.Args()...)
-	args = append(args, "--skip-soong-tests")
-	config := build.NewConfig(ctx, args...)
-	config.Environment().Set("OUT_DIR", outDir)
-	if !*keepArtifacts {
-		config.SetEmptyNinjaFile(true)
-	}
-	build.FindSources(ctx, config, mpctx.Finder)
-	config.Lunch(ctx, product, *buildVariant)
-
-	defer func() {
-		if *keepArtifacts {
-			args := zip.ZipArgs{
-				FileArgs: []zip.FileArg{
-					{
-						GlobDir:             outDir,
-						SourcePrefixToStrip: outDir,
-					},
-				},
-				OutputFilePath:   filepath.Join(mpctx.Config.OutDir(), product+".zip"),
-				NumParallelJobs:  runtime.NumCPU(),
-				CompressionLevel: 5,
-			}
-			if err := zip.Zip(args); err != nil {
-				log.Fatalf("Error zipping artifacts: %v", err)
-			}
-		}
-		if !*incremental {
-			os.RemoveAll(outDir)
-		}
-	}()
-
-	config.SetSkipNinja(true)
-
-	buildWhat := build.RunProductConfig
-	if !*onlyConfig {
-		buildWhat |= build.RunSoong
-		if !*onlySoong {
-			buildWhat |= build.RunKati
-		}
-	}
+	defer cleanupAfterProduct(outDir, productZip)
 
 	before := time.Now()
-	build.Build(ctx, config)
+	err = cmd.Run()
 
-	// Save std_full.log if Kati re-read the makefiles
-	if buildWhat&build.RunKati != 0 {
-		if after, err := os.Stat(config.KatiBuildNinjaFile()); err == nil && after.ModTime().After(before) {
-			err := copyFile(stdLog, filepath.Join(filepath.Dir(stdLog), "std_full.log"))
+	if !*onlyConfig && !*onlySoong {
+		katiBuildNinjaFile := filepath.Join(outDir, "build-"+product+".ninja")
+		if after, err := os.Stat(katiBuildNinjaFile); err == nil && after.ModTime().After(before) {
+			err := copyFile(consoleLogPath, filepath.Join(filepath.Dir(consoleLogPath), "std_full.log"))
 			if err != nil {
 				log.Fatalf("Error copying log file: %s", err)
 			}
 		}
 	}
+	var errOutput string
+	if err == nil {
+		errOutput = ""
+	} else {
+		errOutput = errMsgFromLog(consoleLogPath)
+	}
 
 	mpctx.Status.FinishAction(status.ActionResult{
 		Action: action,
+		Error:  err,
+		Output: errOutput,
 	})
 }
 
diff --git a/cmd/run_with_timeout/run_with_timeout.go b/cmd/run_with_timeout/run_with_timeout.go
index f2caaab..deaa842 100644
--- a/cmd/run_with_timeout/run_with_timeout.go
+++ b/cmd/run_with_timeout/run_with_timeout.go
@@ -47,7 +47,7 @@
 	flag.Parse()
 
 	if flag.NArg() < 1 {
-		fmt.Fprintln(os.Stderr, "command is required")
+		fmt.Fprintf(os.Stderr, "%s: error: command is required\n", os.Args[0])
 		usage()
 	}
 
@@ -55,9 +55,9 @@
 		os.Stdin, os.Stdout, os.Stderr)
 	if err != nil {
 		if exitErr, ok := err.(*exec.ExitError); ok {
-			fmt.Fprintln(os.Stderr, "process exited with error:", exitErr.Error())
+			fmt.Fprintf(os.Stderr, "%s: process exited with error: %s\n", os.Args[0], exitErr.Error())
 		} else {
-			fmt.Fprintln(os.Stderr, "error:", err.Error())
+			fmt.Fprintf(os.Stderr, "%s: error: %s\n", os.Args[0], err.Error())
 		}
 		os.Exit(1)
 	}
@@ -115,6 +115,7 @@
 	if timeout > 0 {
 		timeoutCh = time.After(timeout)
 	}
+	startTime := time.Now()
 
 	select {
 	case err := <-waitCh:
@@ -126,10 +127,12 @@
 		// Continue below.
 	}
 
+	fmt.Fprintf(concurrentStderr, "%s: process timed out after %s\n", os.Args[0], time.Since(startTime))
 	// Process timed out before exiting.
 	defer cmd.Process.Signal(syscall.SIGKILL)
 
 	if onTimeoutCmdStr != "" {
+		fmt.Fprintf(concurrentStderr, "%s: running on_timeout command `%s`\n", os.Args[0], onTimeoutCmdStr)
 		onTimeoutCmd := exec.Command("sh", "-c", onTimeoutCmdStr)
 		onTimeoutCmd.Stdin, onTimeoutCmd.Stdout, onTimeoutCmd.Stderr = stdin, concurrentStdout, concurrentStderr
 		onTimeoutCmd.Env = append(os.Environ(), fmt.Sprintf("PID=%d", cmd.Process.Pid))
diff --git a/cmd/run_with_timeout/run_with_timeout_test.go b/cmd/run_with_timeout/run_with_timeout_test.go
index aebd336..ed6ec11 100644
--- a/cmd/run_with_timeout/run_with_timeout_test.go
+++ b/cmd/run_with_timeout/run_with_timeout_test.go
@@ -17,6 +17,7 @@
 import (
 	"bytes"
 	"io"
+	"regexp"
 	"testing"
 	"time"
 )
@@ -60,7 +61,8 @@
 				args:    []string{"-c", "sleep 1 && echo foo"},
 				timeout: 1 * time.Millisecond,
 			},
-			wantErr: true,
+			wantStderr: ".*: process timed out after .*\n",
+			wantErr:    true,
 		},
 		{
 			name: "on_timeout command",
@@ -71,6 +73,7 @@
 				onTimeoutCmd: "echo bar",
 			},
 			wantStdout: "bar\n",
+			wantStderr: ".*: process timed out after .*\n.*: running on_timeout command `echo bar`\n",
 			wantErr:    true,
 		},
 	}
@@ -86,7 +89,7 @@
 			if gotStdout := stdout.String(); gotStdout != tt.wantStdout {
 				t.Errorf("runWithTimeout() gotStdout = %v, want %v", gotStdout, tt.wantStdout)
 			}
-			if gotStderr := stderr.String(); gotStderr != tt.wantStderr {
+			if gotStderr := stderr.String(); !regexp.MustCompile(tt.wantStderr).MatchString(gotStderr) {
 				t.Errorf("runWithTimeout() gotStderr = %v, want %v", gotStderr, tt.wantStderr)
 			}
 		})
diff --git a/cmd/sbox/Android.bp b/cmd/sbox/Android.bp
index b8d75ed..454cfd8 100644
--- a/cmd/sbox/Android.bp
+++ b/cmd/sbox/Android.bp
@@ -19,6 +19,7 @@
 blueprint_go_binary {
     name: "sbox",
     deps: [
+        "golang-protobuf-encoding-prototext",
         "sbox_proto",
         "soong-makedeps",
         "soong-response",
@@ -31,7 +32,10 @@
 bootstrap_go_package {
     name: "sbox_proto",
     pkgPath: "android/soong/cmd/sbox/sbox_proto",
-    deps: ["golang-protobuf-proto"],
+    deps: [
+        "golang-protobuf-reflect-protoreflect",
+        "golang-protobuf-runtime-protoimpl",
+    ],
     srcs: [
         "sbox_proto/sbox.pb.go",
     ],
diff --git a/cmd/sbox/sbox.go b/cmd/sbox/sbox.go
index f124e40..c7f3f6a 100644
--- a/cmd/sbox/sbox.go
+++ b/cmd/sbox/sbox.go
@@ -34,7 +34,7 @@
 	"android/soong/makedeps"
 	"android/soong/response"
 
-	"github.com/golang/protobuf/proto"
+	"google.golang.org/protobuf/encoding/prototext"
 )
 
 var (
@@ -203,7 +203,7 @@
 
 	manifest := sbox_proto.Manifest{}
 
-	err = proto.UnmarshalText(string(manifestData), &manifest)
+	err = prototext.Unmarshal(manifestData, &manifest)
 	if err != nil {
 		return nil, fmt.Errorf("error parsing manifest %q: %w", file, err)
 	}
diff --git a/cmd/sbox/sbox_proto/sbox.pb.go b/cmd/sbox/sbox_proto/sbox.pb.go
index b996481..7c84f2c 100644
--- a/cmd/sbox/sbox_proto/sbox.pb.go
+++ b/cmd/sbox/sbox_proto/sbox.pb.go
@@ -1,78 +1,104 @@
+// Copyright 2020 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 // Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.26.0
+// 	protoc        v3.9.1
 // source: sbox.proto
 
 package sbox_proto
 
 import (
-	fmt "fmt"
-	proto "github.com/golang/protobuf/proto"
-	math "math"
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
 )
 
-// Reference imports to suppress errors if they are not otherwise used.
-var _ = proto.Marshal
-var _ = fmt.Errorf
-var _ = math.Inf
-
-// This is a compile-time assertion to ensure that this generated file
-// is compatible with the proto package it is being compiled against.
-// A compilation error at this line likely means your copy of the
-// proto package needs to be updated.
-const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
 
 // A set of commands to run in a sandbox.
 type Manifest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	// A list of commands to run in the sandbox.
 	Commands []*Command `protobuf:"bytes,1,rep,name=commands" json:"commands,omitempty"`
 	// If set, GCC-style dependency files from any command that references __SBOX_DEPFILE__ will be
 	// merged into the given output file relative to the $PWD when sbox was started.
-	OutputDepfile        *string  `protobuf:"bytes,2,opt,name=output_depfile,json=outputDepfile" json:"output_depfile,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
+	OutputDepfile *string `protobuf:"bytes,2,opt,name=output_depfile,json=outputDepfile" json:"output_depfile,omitempty"`
 }
 
-func (m *Manifest) Reset()         { *m = Manifest{} }
-func (m *Manifest) String() string { return proto.CompactTextString(m) }
-func (*Manifest) ProtoMessage()    {}
+func (x *Manifest) Reset() {
+	*x = Manifest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_sbox_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Manifest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Manifest) ProtoMessage() {}
+
+func (x *Manifest) ProtoReflect() protoreflect.Message {
+	mi := &file_sbox_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Manifest.ProtoReflect.Descriptor instead.
 func (*Manifest) Descriptor() ([]byte, []int) {
-	return fileDescriptor_9d0425bf0de86ed1, []int{0}
+	return file_sbox_proto_rawDescGZIP(), []int{0}
 }
 
-func (m *Manifest) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_Manifest.Unmarshal(m, b)
-}
-func (m *Manifest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_Manifest.Marshal(b, m, deterministic)
-}
-func (m *Manifest) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_Manifest.Merge(m, src)
-}
-func (m *Manifest) XXX_Size() int {
-	return xxx_messageInfo_Manifest.Size(m)
-}
-func (m *Manifest) XXX_DiscardUnknown() {
-	xxx_messageInfo_Manifest.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_Manifest proto.InternalMessageInfo
-
-func (m *Manifest) GetCommands() []*Command {
-	if m != nil {
-		return m.Commands
+func (x *Manifest) GetCommands() []*Command {
+	if x != nil {
+		return x.Commands
 	}
 	return nil
 }
 
-func (m *Manifest) GetOutputDepfile() string {
-	if m != nil && m.OutputDepfile != nil {
-		return *m.OutputDepfile
+func (x *Manifest) GetOutputDepfile() string {
+	if x != nil && x.OutputDepfile != nil {
+		return *x.OutputDepfile
 	}
 	return ""
 }
 
 // SandboxManifest describes a command to run in the sandbox.
 type Command struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	// A list of copy rules to run before the sandboxed command.  The from field is relative to the
 	// $PWD when sbox was run, the to field is relative to the top of the temporary sandbox directory.
 	CopyBefore []*Copy `protobuf:"bytes,1,rep,name=copy_before,json=copyBefore" json:"copy_before,omitempty"`
@@ -89,75 +115,79 @@
 	InputHash *string `protobuf:"bytes,5,opt,name=input_hash,json=inputHash" json:"input_hash,omitempty"`
 	// A list of files that will be copied before the sandboxed command, and whose contents should be
 	// copied as if they were listed in copy_before.
-	RspFiles             []*RspFile `protobuf:"bytes,6,rep,name=rsp_files,json=rspFiles" json:"rsp_files,omitempty"`
-	XXX_NoUnkeyedLiteral struct{}   `json:"-"`
-	XXX_unrecognized     []byte     `json:"-"`
-	XXX_sizecache        int32      `json:"-"`
+	RspFiles []*RspFile `protobuf:"bytes,6,rep,name=rsp_files,json=rspFiles" json:"rsp_files,omitempty"`
 }
 
-func (m *Command) Reset()         { *m = Command{} }
-func (m *Command) String() string { return proto.CompactTextString(m) }
-func (*Command) ProtoMessage()    {}
+func (x *Command) Reset() {
+	*x = Command{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_sbox_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Command) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Command) ProtoMessage() {}
+
+func (x *Command) ProtoReflect() protoreflect.Message {
+	mi := &file_sbox_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Command.ProtoReflect.Descriptor instead.
 func (*Command) Descriptor() ([]byte, []int) {
-	return fileDescriptor_9d0425bf0de86ed1, []int{1}
+	return file_sbox_proto_rawDescGZIP(), []int{1}
 }
 
-func (m *Command) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_Command.Unmarshal(m, b)
-}
-func (m *Command) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_Command.Marshal(b, m, deterministic)
-}
-func (m *Command) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_Command.Merge(m, src)
-}
-func (m *Command) XXX_Size() int {
-	return xxx_messageInfo_Command.Size(m)
-}
-func (m *Command) XXX_DiscardUnknown() {
-	xxx_messageInfo_Command.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_Command proto.InternalMessageInfo
-
-func (m *Command) GetCopyBefore() []*Copy {
-	if m != nil {
-		return m.CopyBefore
+func (x *Command) GetCopyBefore() []*Copy {
+	if x != nil {
+		return x.CopyBefore
 	}
 	return nil
 }
 
-func (m *Command) GetChdir() bool {
-	if m != nil && m.Chdir != nil {
-		return *m.Chdir
+func (x *Command) GetChdir() bool {
+	if x != nil && x.Chdir != nil {
+		return *x.Chdir
 	}
 	return false
 }
 
-func (m *Command) GetCommand() string {
-	if m != nil && m.Command != nil {
-		return *m.Command
+func (x *Command) GetCommand() string {
+	if x != nil && x.Command != nil {
+		return *x.Command
 	}
 	return ""
 }
 
-func (m *Command) GetCopyAfter() []*Copy {
-	if m != nil {
-		return m.CopyAfter
+func (x *Command) GetCopyAfter() []*Copy {
+	if x != nil {
+		return x.CopyAfter
 	}
 	return nil
 }
 
-func (m *Command) GetInputHash() string {
-	if m != nil && m.InputHash != nil {
-		return *m.InputHash
+func (x *Command) GetInputHash() string {
+	if x != nil && x.InputHash != nil {
+		return *x.InputHash
 	}
 	return ""
 }
 
-func (m *Command) GetRspFiles() []*RspFile {
-	if m != nil {
-		return m.RspFiles
+func (x *Command) GetRspFiles() []*RspFile {
+	if x != nil {
+		return x.RspFiles
 	}
 	return nil
 }
@@ -166,193 +196,341 @@
 // are relative to is specific to the context the Copy is used in and will be different for
 // from and to.
 type Copy struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	From *string `protobuf:"bytes,1,req,name=from" json:"from,omitempty"`
 	To   *string `protobuf:"bytes,2,req,name=to" json:"to,omitempty"`
 	// If true, make the file executable after copying it.
-	Executable           *bool    `protobuf:"varint,3,opt,name=executable" json:"executable,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
+	Executable *bool `protobuf:"varint,3,opt,name=executable" json:"executable,omitempty"`
 }
 
-func (m *Copy) Reset()         { *m = Copy{} }
-func (m *Copy) String() string { return proto.CompactTextString(m) }
-func (*Copy) ProtoMessage()    {}
+func (x *Copy) Reset() {
+	*x = Copy{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_sbox_proto_msgTypes[2]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Copy) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Copy) ProtoMessage() {}
+
+func (x *Copy) ProtoReflect() protoreflect.Message {
+	mi := &file_sbox_proto_msgTypes[2]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Copy.ProtoReflect.Descriptor instead.
 func (*Copy) Descriptor() ([]byte, []int) {
-	return fileDescriptor_9d0425bf0de86ed1, []int{2}
+	return file_sbox_proto_rawDescGZIP(), []int{2}
 }
 
-func (m *Copy) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_Copy.Unmarshal(m, b)
-}
-func (m *Copy) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_Copy.Marshal(b, m, deterministic)
-}
-func (m *Copy) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_Copy.Merge(m, src)
-}
-func (m *Copy) XXX_Size() int {
-	return xxx_messageInfo_Copy.Size(m)
-}
-func (m *Copy) XXX_DiscardUnknown() {
-	xxx_messageInfo_Copy.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_Copy proto.InternalMessageInfo
-
-func (m *Copy) GetFrom() string {
-	if m != nil && m.From != nil {
-		return *m.From
+func (x *Copy) GetFrom() string {
+	if x != nil && x.From != nil {
+		return *x.From
 	}
 	return ""
 }
 
-func (m *Copy) GetTo() string {
-	if m != nil && m.To != nil {
-		return *m.To
+func (x *Copy) GetTo() string {
+	if x != nil && x.To != nil {
+		return *x.To
 	}
 	return ""
 }
 
-func (m *Copy) GetExecutable() bool {
-	if m != nil && m.Executable != nil {
-		return *m.Executable
+func (x *Copy) GetExecutable() bool {
+	if x != nil && x.Executable != nil {
+		return *x.Executable
 	}
 	return false
 }
 
 // RspFile describes an rspfile that should be copied into the sandbox directory.
 type RspFile struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	// The path to the rsp file.
 	File *string `protobuf:"bytes,1,req,name=file" json:"file,omitempty"`
 	// A list of path mappings that should be applied to each file listed in the rsp file.
-	PathMappings         []*PathMapping `protobuf:"bytes,2,rep,name=path_mappings,json=pathMappings" json:"path_mappings,omitempty"`
-	XXX_NoUnkeyedLiteral struct{}       `json:"-"`
-	XXX_unrecognized     []byte         `json:"-"`
-	XXX_sizecache        int32          `json:"-"`
+	PathMappings []*PathMapping `protobuf:"bytes,2,rep,name=path_mappings,json=pathMappings" json:"path_mappings,omitempty"`
 }
 
-func (m *RspFile) Reset()         { *m = RspFile{} }
-func (m *RspFile) String() string { return proto.CompactTextString(m) }
-func (*RspFile) ProtoMessage()    {}
+func (x *RspFile) Reset() {
+	*x = RspFile{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_sbox_proto_msgTypes[3]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *RspFile) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*RspFile) ProtoMessage() {}
+
+func (x *RspFile) ProtoReflect() protoreflect.Message {
+	mi := &file_sbox_proto_msgTypes[3]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use RspFile.ProtoReflect.Descriptor instead.
 func (*RspFile) Descriptor() ([]byte, []int) {
-	return fileDescriptor_9d0425bf0de86ed1, []int{3}
+	return file_sbox_proto_rawDescGZIP(), []int{3}
 }
 
-func (m *RspFile) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_RspFile.Unmarshal(m, b)
-}
-func (m *RspFile) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_RspFile.Marshal(b, m, deterministic)
-}
-func (m *RspFile) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_RspFile.Merge(m, src)
-}
-func (m *RspFile) XXX_Size() int {
-	return xxx_messageInfo_RspFile.Size(m)
-}
-func (m *RspFile) XXX_DiscardUnknown() {
-	xxx_messageInfo_RspFile.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_RspFile proto.InternalMessageInfo
-
-func (m *RspFile) GetFile() string {
-	if m != nil && m.File != nil {
-		return *m.File
+func (x *RspFile) GetFile() string {
+	if x != nil && x.File != nil {
+		return *x.File
 	}
 	return ""
 }
 
-func (m *RspFile) GetPathMappings() []*PathMapping {
-	if m != nil {
-		return m.PathMappings
+func (x *RspFile) GetPathMappings() []*PathMapping {
+	if x != nil {
+		return x.PathMappings
 	}
 	return nil
 }
 
 // PathMapping describes a mapping from a path outside the sandbox to the path inside the sandbox.
 type PathMapping struct {
-	From                 *string  `protobuf:"bytes,1,req,name=from" json:"from,omitempty"`
-	To                   *string  `protobuf:"bytes,2,req,name=to" json:"to,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	From *string `protobuf:"bytes,1,req,name=from" json:"from,omitempty"`
+	To   *string `protobuf:"bytes,2,req,name=to" json:"to,omitempty"`
 }
 
-func (m *PathMapping) Reset()         { *m = PathMapping{} }
-func (m *PathMapping) String() string { return proto.CompactTextString(m) }
-func (*PathMapping) ProtoMessage()    {}
+func (x *PathMapping) Reset() {
+	*x = PathMapping{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_sbox_proto_msgTypes[4]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *PathMapping) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*PathMapping) ProtoMessage() {}
+
+func (x *PathMapping) ProtoReflect() protoreflect.Message {
+	mi := &file_sbox_proto_msgTypes[4]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use PathMapping.ProtoReflect.Descriptor instead.
 func (*PathMapping) Descriptor() ([]byte, []int) {
-	return fileDescriptor_9d0425bf0de86ed1, []int{4}
+	return file_sbox_proto_rawDescGZIP(), []int{4}
 }
 
-func (m *PathMapping) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_PathMapping.Unmarshal(m, b)
-}
-func (m *PathMapping) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_PathMapping.Marshal(b, m, deterministic)
-}
-func (m *PathMapping) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_PathMapping.Merge(m, src)
-}
-func (m *PathMapping) XXX_Size() int {
-	return xxx_messageInfo_PathMapping.Size(m)
-}
-func (m *PathMapping) XXX_DiscardUnknown() {
-	xxx_messageInfo_PathMapping.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_PathMapping proto.InternalMessageInfo
-
-func (m *PathMapping) GetFrom() string {
-	if m != nil && m.From != nil {
-		return *m.From
+func (x *PathMapping) GetFrom() string {
+	if x != nil && x.From != nil {
+		return *x.From
 	}
 	return ""
 }
 
-func (m *PathMapping) GetTo() string {
-	if m != nil && m.To != nil {
-		return *m.To
+func (x *PathMapping) GetTo() string {
+	if x != nil && x.To != nil {
+		return *x.To
 	}
 	return ""
 }
 
-func init() {
-	proto.RegisterType((*Manifest)(nil), "sbox.Manifest")
-	proto.RegisterType((*Command)(nil), "sbox.Command")
-	proto.RegisterType((*Copy)(nil), "sbox.Copy")
-	proto.RegisterType((*RspFile)(nil), "sbox.RspFile")
-	proto.RegisterType((*PathMapping)(nil), "sbox.PathMapping")
+var File_sbox_proto protoreflect.FileDescriptor
+
+var file_sbox_proto_rawDesc = []byte{
+	0x0a, 0x0a, 0x73, 0x62, 0x6f, 0x78, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x73, 0x62,
+	0x6f, 0x78, 0x22, 0x5c, 0x0a, 0x08, 0x4d, 0x61, 0x6e, 0x69, 0x66, 0x65, 0x73, 0x74, 0x12, 0x29,
+	0x0a, 0x08, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b,
+	0x32, 0x0d, 0x2e, 0x73, 0x62, 0x6f, 0x78, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52,
+	0x08, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x6f, 0x75, 0x74,
+	0x70, 0x75, 0x74, 0x5f, 0x64, 0x65, 0x70, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x0d, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x44, 0x65, 0x70, 0x66, 0x69, 0x6c, 0x65,
+	0x22, 0xdc, 0x01, 0x0a, 0x07, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x2b, 0x0a, 0x0b,
+	0x63, 0x6f, 0x70, 0x79, 0x5f, 0x62, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x18, 0x01, 0x20, 0x03, 0x28,
+	0x0b, 0x32, 0x0a, 0x2e, 0x73, 0x62, 0x6f, 0x78, 0x2e, 0x43, 0x6f, 0x70, 0x79, 0x52, 0x0a, 0x63,
+	0x6f, 0x70, 0x79, 0x42, 0x65, 0x66, 0x6f, 0x72, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x68, 0x64,
+	0x69, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x63, 0x68, 0x64, 0x69, 0x72, 0x12,
+	0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x03, 0x20, 0x02, 0x28, 0x09,
+	0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x29, 0x0a, 0x0a, 0x63, 0x6f, 0x70,
+	0x79, 0x5f, 0x61, 0x66, 0x74, 0x65, 0x72, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0a, 0x2e,
+	0x73, 0x62, 0x6f, 0x78, 0x2e, 0x43, 0x6f, 0x70, 0x79, 0x52, 0x09, 0x63, 0x6f, 0x70, 0x79, 0x41,
+	0x66, 0x74, 0x65, 0x72, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x68, 0x61,
+	0x73, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x48,
+	0x61, 0x73, 0x68, 0x12, 0x2a, 0x0a, 0x09, 0x72, 0x73, 0x70, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73,
+	0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x73, 0x62, 0x6f, 0x78, 0x2e, 0x52, 0x73,
+	0x70, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x08, 0x72, 0x73, 0x70, 0x46, 0x69, 0x6c, 0x65, 0x73, 0x22,
+	0x4a, 0x0a, 0x04, 0x43, 0x6f, 0x70, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18,
+	0x01, 0x20, 0x02, 0x28, 0x09, 0x52, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x74,
+	0x6f, 0x18, 0x02, 0x20, 0x02, 0x28, 0x09, 0x52, 0x02, 0x74, 0x6f, 0x12, 0x1e, 0x0a, 0x0a, 0x65,
+	0x78, 0x65, 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52,
+	0x0a, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x22, 0x55, 0x0a, 0x07, 0x52,
+	0x73, 0x70, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01,
+	0x20, 0x02, 0x28, 0x09, 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x36, 0x0a, 0x0d, 0x70, 0x61,
+	0x74, 0x68, 0x5f, 0x6d, 0x61, 0x70, 0x70, 0x69, 0x6e, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28,
+	0x0b, 0x32, 0x11, 0x2e, 0x73, 0x62, 0x6f, 0x78, 0x2e, 0x50, 0x61, 0x74, 0x68, 0x4d, 0x61, 0x70,
+	0x70, 0x69, 0x6e, 0x67, 0x52, 0x0c, 0x70, 0x61, 0x74, 0x68, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e,
+	0x67, 0x73, 0x22, 0x31, 0x0a, 0x0b, 0x50, 0x61, 0x74, 0x68, 0x4d, 0x61, 0x70, 0x70, 0x69, 0x6e,
+	0x67, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x01, 0x20, 0x02, 0x28, 0x09, 0x52,
+	0x04, 0x66, 0x72, 0x6f, 0x6d, 0x12, 0x0e, 0x0a, 0x02, 0x74, 0x6f, 0x18, 0x02, 0x20, 0x02, 0x28,
+	0x09, 0x52, 0x02, 0x74, 0x6f, 0x42, 0x23, 0x5a, 0x21, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64,
+	0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x63, 0x6d, 0x64, 0x2f, 0x73, 0x62, 0x6f, 0x78, 0x2f,
+	0x73, 0x62, 0x6f, 0x78, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
 }
 
-func init() {
-	proto.RegisterFile("sbox.proto", fileDescriptor_9d0425bf0de86ed1)
+var (
+	file_sbox_proto_rawDescOnce sync.Once
+	file_sbox_proto_rawDescData = file_sbox_proto_rawDesc
+)
+
+func file_sbox_proto_rawDescGZIP() []byte {
+	file_sbox_proto_rawDescOnce.Do(func() {
+		file_sbox_proto_rawDescData = protoimpl.X.CompressGZIP(file_sbox_proto_rawDescData)
+	})
+	return file_sbox_proto_rawDescData
 }
 
-var fileDescriptor_9d0425bf0de86ed1 = []byte{
-	// 342 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0x5f, 0x4b, 0xc3, 0x30,
-	0x14, 0xc5, 0x69, 0xd7, 0xb9, 0xf6, 0xee, 0x0f, 0x18, 0x7c, 0xc8, 0x8b, 0x32, 0x0a, 0xc2, 0xa6,
-	0x30, 0xd0, 0x07, 0xdf, 0x9d, 0x22, 0x22, 0x0c, 0x24, 0xe0, 0x8b, 0x08, 0x25, 0xeb, 0x52, 0x5b,
-	0x58, 0x9b, 0x90, 0x64, 0xb0, 0x7d, 0x57, 0x3f, 0x8c, 0xe4, 0xa6, 0xd3, 0x82, 0x2f, 0xbe, 0xdd,
-	0x7b, 0x0e, 0xf7, 0xdc, 0x5f, 0xc2, 0x05, 0x30, 0x6b, 0xb9, 0x5f, 0x28, 0x2d, 0xad, 0x24, 0x91,
-	0xab, 0xd3, 0x0f, 0x88, 0x57, 0xbc, 0xa9, 0x0a, 0x61, 0x2c, 0x99, 0x43, 0x9c, 0xcb, 0xba, 0xe6,
-	0xcd, 0xc6, 0xd0, 0x60, 0xda, 0x9b, 0x0d, 0x6f, 0xc7, 0x0b, 0x1c, 0x78, 0xf0, 0x2a, 0xfb, 0xb1,
-	0xc9, 0x25, 0x4c, 0xe4, 0xce, 0xaa, 0x9d, 0xcd, 0x36, 0x42, 0x15, 0xd5, 0x56, 0xd0, 0x70, 0x1a,
-	0xcc, 0x12, 0x36, 0xf6, 0xea, 0xa3, 0x17, 0xd3, 0xaf, 0x00, 0x06, 0xed, 0x30, 0xb9, 0x86, 0x61,
-	0x2e, 0xd5, 0x21, 0x5b, 0x8b, 0x42, 0x6a, 0xd1, 0x2e, 0x80, 0xe3, 0x02, 0x75, 0x60, 0xe0, 0xec,
-	0x25, 0xba, 0xe4, 0x0c, 0xfa, 0x79, 0xb9, 0xa9, 0x34, 0xc6, 0xc6, 0xcc, 0x37, 0x84, 0xc2, 0xa0,
-	0x25, 0xa0, 0xbd, 0x69, 0x38, 0x4b, 0xd8, 0xb1, 0x25, 0x73, 0xc0, 0xe9, 0x8c, 0x17, 0x56, 0x68,
-	0x1a, 0xfd, 0xc9, 0x4e, 0x9c, 0x7b, 0xef, 0x4c, 0x72, 0x0e, 0x50, 0x35, 0x8e, 0xbc, 0xe4, 0xa6,
-	0xa4, 0x7d, 0xc4, 0x4e, 0x50, 0x79, 0xe6, 0xa6, 0x24, 0x57, 0x90, 0x68, 0xa3, 0x32, 0x87, 0x6f,
-	0xe8, 0x49, 0xf7, 0x17, 0x98, 0x51, 0x4f, 0xd5, 0x56, 0xb0, 0x58, 0xfb, 0xc2, 0xa4, 0x2f, 0x10,
-	0xb9, 0x74, 0x42, 0x20, 0x2a, 0xb4, 0xac, 0x69, 0x80, 0x50, 0x58, 0x93, 0x09, 0x84, 0x56, 0xd2,
-	0x10, 0x95, 0xd0, 0x4a, 0x72, 0x01, 0x20, 0xf6, 0x22, 0xdf, 0x59, 0xbe, 0xde, 0x0a, 0xda, 0xc3,
-	0x67, 0x75, 0x94, 0xf4, 0x0d, 0x06, 0xed, 0x02, 0x8c, 0x73, 0x5f, 0x7a, 0x8c, 0x73, 0xda, 0x1d,
-	0x8c, 0x15, 0xb7, 0x65, 0x56, 0x73, 0xa5, 0xaa, 0xe6, 0xd3, 0xd0, 0x10, 0xd1, 0x4e, 0x3d, 0xda,
-	0x2b, 0xb7, 0xe5, 0xca, 0x3b, 0x6c, 0xa4, 0x7e, 0x1b, 0x93, 0xde, 0xc0, 0xb0, 0x63, 0xfe, 0x87,
-	0x74, 0x39, 0x7a, 0xc7, 0x33, 0xc9, 0xf0, 0x4c, 0xbe, 0x03, 0x00, 0x00, 0xff, 0xff, 0x83, 0x82,
-	0xb0, 0xc3, 0x33, 0x02, 0x00, 0x00,
+var file_sbox_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
+var file_sbox_proto_goTypes = []interface{}{
+	(*Manifest)(nil),    // 0: sbox.Manifest
+	(*Command)(nil),     // 1: sbox.Command
+	(*Copy)(nil),        // 2: sbox.Copy
+	(*RspFile)(nil),     // 3: sbox.RspFile
+	(*PathMapping)(nil), // 4: sbox.PathMapping
+}
+var file_sbox_proto_depIdxs = []int32{
+	1, // 0: sbox.Manifest.commands:type_name -> sbox.Command
+	2, // 1: sbox.Command.copy_before:type_name -> sbox.Copy
+	2, // 2: sbox.Command.copy_after:type_name -> sbox.Copy
+	3, // 3: sbox.Command.rsp_files:type_name -> sbox.RspFile
+	4, // 4: sbox.RspFile.path_mappings:type_name -> sbox.PathMapping
+	5, // [5:5] is the sub-list for method output_type
+	5, // [5:5] is the sub-list for method input_type
+	5, // [5:5] is the sub-list for extension type_name
+	5, // [5:5] is the sub-list for extension extendee
+	0, // [0:5] is the sub-list for field type_name
+}
+
+func init() { file_sbox_proto_init() }
+func file_sbox_proto_init() {
+	if File_sbox_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_sbox_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Manifest); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_sbox_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Command); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_sbox_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Copy); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_sbox_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*RspFile); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_sbox_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*PathMapping); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_sbox_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   5,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_sbox_proto_goTypes,
+		DependencyIndexes: file_sbox_proto_depIdxs,
+		MessageInfos:      file_sbox_proto_msgTypes,
+	}.Build()
+	File_sbox_proto = out.File
+	file_sbox_proto_rawDesc = nil
+	file_sbox_proto_goTypes = nil
+	file_sbox_proto_depIdxs = nil
 }
diff --git a/cmd/sbox/sbox_proto/sbox.proto b/cmd/sbox/sbox_proto/sbox.proto
index bdf92c6..2f0dcf0 100644
--- a/cmd/sbox/sbox_proto/sbox.proto
+++ b/cmd/sbox/sbox_proto/sbox.proto
@@ -15,7 +15,7 @@
 syntax = "proto2";
 
 package sbox;
-option go_package = "sbox_proto";
+option go_package = "android/soong/cmd/sbox/sbox_proto";
 
 // A set of commands to run in a sandbox.
 message Manifest {
diff --git a/cmd/soong_build/Android.bp b/cmd/soong_build/Android.bp
index 9f09224..703a875 100644
--- a/cmd/soong_build/Android.bp
+++ b/cmd/soong_build/Android.bp
@@ -16,7 +16,7 @@
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
-bootstrap_go_binary {
+blueprint_go_binary {
     name: "soong_build",
     deps: [
         "blueprint",
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 044689e..16a4e1a 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -28,6 +28,7 @@
 
 	"github.com/google/blueprint/bootstrap"
 	"github.com/google/blueprint/deptools"
+	"github.com/google/blueprint/pathtools"
 
 	"android/soong/android"
 )
@@ -38,12 +39,17 @@
 	availableEnvFile string
 	usedEnvFile      string
 
+	globFile    string
+	globListDir string
 	delveListen string
 	delvePath   string
 
+	moduleGraphFile   string
 	docFile           string
 	bazelQueryViewDir string
 	bp2buildMarker    string
+
+	cmdlineArgs bootstrap.Args
 )
 
 func init() {
@@ -52,15 +58,32 @@
 	flag.StringVar(&outDir, "out", "", "Soong output directory (usually $TOP/out/soong)")
 	flag.StringVar(&availableEnvFile, "available_env", "", "File containing available environment variables")
 	flag.StringVar(&usedEnvFile, "used_env", "", "File containing used environment variables")
+	flag.StringVar(&globFile, "globFile", "build-globs.ninja", "the Ninja file of globs to output")
+	flag.StringVar(&globListDir, "globListDir", "", "the directory containing the glob list files")
+	flag.StringVar(&cmdlineArgs.SoongOutDir, "b", ".", "the build output directory")
+	flag.StringVar(&cmdlineArgs.OutDir, "n", "", "the ninja builddir directory")
+	flag.StringVar(&cmdlineArgs.ModuleListFile, "l", "", "file that lists filepaths to parse")
 
 	// Debug flags
 	flag.StringVar(&delveListen, "delve_listen", "", "Delve port to listen on for debugging")
 	flag.StringVar(&delvePath, "delve_path", "", "Path to Delve. Only used if --delve_listen is set")
+	flag.StringVar(&cmdlineArgs.Cpuprofile, "cpuprofile", "", "write cpu profile to file")
+	flag.StringVar(&cmdlineArgs.TraceFile, "trace", "", "write trace to file")
+	flag.StringVar(&cmdlineArgs.Memprofile, "memprofile", "", "write memory profile to file")
+	flag.BoolVar(&cmdlineArgs.NoGC, "nogc", false, "turn off GC for debugging")
 
 	// Flags representing various modes soong_build can run in
+	flag.StringVar(&moduleGraphFile, "module_graph_file", "", "JSON module graph file to output")
 	flag.StringVar(&docFile, "soong_docs", "", "build documentation file to output")
 	flag.StringVar(&bazelQueryViewDir, "bazel_queryview_dir", "", "path to the bazel queryview directory relative to --top")
 	flag.StringVar(&bp2buildMarker, "bp2build_marker", "", "If set, run bp2build, touch the specified marker file then exit")
+	flag.StringVar(&cmdlineArgs.OutFile, "o", "build.ninja", "the Ninja file to output")
+	flag.BoolVar(&cmdlineArgs.EmptyNinjaFile, "empty-ninja-file", false, "write out a 0-byte ninja file")
+
+	// Flags that probably shouldn't be flags of soong_build but we haven't found
+	// the time to remove them yet
+	flag.BoolVar(&cmdlineArgs.RunGoTests, "t", false, "build and run go tests during bootstrap")
+	flag.BoolVar(&cmdlineArgs.UseValidations, "use-validations", false, "use validations to depend on go tests")
 }
 
 func newNameResolver(config android.Config) *android.NameResolver {
@@ -90,8 +113,8 @@
 	return ctx
 }
 
-func newConfig(srcDir, outDir string, availableEnv map[string]string) android.Config {
-	configuration, err := android.NewConfig(srcDir, outDir, bootstrap.CmdlineArgs.ModuleListFile, availableEnv)
+func newConfig(outDir string, availableEnv map[string]string) android.Config {
+	configuration, err := android.NewConfig(outDir, cmdlineArgs.ModuleListFile, availableEnv)
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "%s", err)
 		os.Exit(1)
@@ -107,7 +130,7 @@
 func runMixedModeBuild(configuration android.Config, firstCtx *android.Context, extraNinjaDeps []string) {
 	var firstArgs, secondArgs bootstrap.Args
 
-	firstArgs = bootstrap.CmdlineArgs
+	firstArgs = cmdlineArgs
 	configuration.SetStopBefore(bootstrap.StopBeforeWriteNinja)
 	bootstrap.RunBlueprint(firstArgs, firstCtx.Context, configuration)
 
@@ -123,14 +146,14 @@
 		os.Exit(1)
 	}
 	secondCtx := newContext(secondConfig, true)
-	secondArgs = bootstrap.CmdlineArgs
+	secondArgs = cmdlineArgs
 	ninjaDeps := bootstrap.RunBlueprint(secondArgs, secondCtx.Context, secondConfig)
 	ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
-	err = deptools.WriteDepFile(shared.JoinPath(topDir, secondArgs.DepFile), secondArgs.OutFile, ninjaDeps)
-	if err != nil {
-		fmt.Fprintf(os.Stderr, "Error writing depfile '%s': %s\n", secondArgs.DepFile, err)
-		os.Exit(1)
-	}
+
+	globListFiles := writeBuildGlobsNinjaFile(secondCtx.SrcDir(), configuration.SoongOutDir(), secondCtx.Globs, configuration)
+	ninjaDeps = append(ninjaDeps, globListFiles...)
+
+	writeDepFile(secondArgs.OutFile, ninjaDeps)
 }
 
 // Run the code-generation phase to convert BazelTargetModules to BUILD files.
@@ -145,7 +168,7 @@
 
 func runSoongDocs(configuration android.Config) {
 	ctx := newContext(configuration, false)
-	soongDocsArgs := bootstrap.CmdlineArgs
+	soongDocsArgs := cmdlineArgs
 	bootstrap.RunBlueprint(soongDocsArgs, ctx.Context, configuration)
 	if err := writeDocs(ctx, configuration, docFile); err != nil {
 		fmt.Fprintf(os.Stderr, "%s", err)
@@ -154,7 +177,7 @@
 }
 
 func writeMetrics(configuration android.Config) {
-	metricsFile := filepath.Join(configuration.BuildDir(), "soong_build_metrics.pb")
+	metricsFile := filepath.Join(configuration.SoongOutDir(), "soong_build_metrics.pb")
 	err := android.WriteMetrics(configuration, metricsFile)
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "error writing soong_build metrics %s: %s", metricsFile, err)
@@ -162,8 +185,8 @@
 	}
 }
 
-func writeJsonModuleGraph(configuration android.Config, ctx *android.Context, path string, extraNinjaDeps []string) {
-	f, err := os.Create(path)
+func writeJsonModuleGraph(ctx *android.Context, path string) {
+	f, err := os.Create(shared.JoinPath(topDir, path))
 	if err != nil {
 		fmt.Fprintf(os.Stderr, "%s", err)
 		os.Exit(1)
@@ -171,26 +194,43 @@
 
 	defer f.Close()
 	ctx.Context.PrintJSONGraph(f)
-	writeFakeNinjaFile(extraNinjaDeps, configuration.BuildDir())
 }
 
+func writeBuildGlobsNinjaFile(srcDir, buildDir string, globs func() pathtools.MultipleGlobResults, config interface{}) []string {
+	globDir := bootstrap.GlobDirectory(buildDir, globListDir)
+	bootstrap.WriteBuildGlobsNinjaFile(&bootstrap.GlobSingleton{
+		GlobLister: globs,
+		GlobFile:   globFile,
+		GlobDir:    globDir,
+		SrcDir:     srcDir,
+	}, config)
+	return bootstrap.GlobFileListFiles(globDir)
+}
+
+func writeDepFile(outputFile string, ninjaDeps []string) {
+	depFile := shared.JoinPath(topDir, outputFile+".d")
+	err := deptools.WriteDepFile(depFile, outputFile, ninjaDeps)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "Error writing depfile '%s': %s\n", depFile, err)
+		os.Exit(1)
+	}
+}
+
+// doChosenActivity runs Soong for a specific activity, like bp2build, queryview
+// or the actual Soong build for the build.ninja file. Returns the top level
+// output file of the specific activity.
 func doChosenActivity(configuration android.Config, extraNinjaDeps []string) string {
 	bazelConversionRequested := bp2buildMarker != ""
 	mixedModeBuild := configuration.BazelContext.BazelEnabled()
 	generateQueryView := bazelQueryViewDir != ""
-	jsonModuleFile := configuration.Getenv("SOONG_DUMP_JSON_MODULE_GRAPH")
 
-	blueprintArgs := bootstrap.CmdlineArgs
-	prepareBuildActions := !generateQueryView && jsonModuleFile == ""
+	blueprintArgs := cmdlineArgs
+	prepareBuildActions := !generateQueryView && moduleGraphFile == ""
 	if bazelConversionRequested {
 		// Run the alternate pipeline of bp2build mutators and singleton to convert
 		// Blueprint to BUILD files before everything else.
 		runBp2Build(configuration, extraNinjaDeps)
-		if bp2buildMarker != "" {
-			return bp2buildMarker
-		} else {
-			return bootstrap.CmdlineArgs.OutFile
-		}
+		return bp2buildMarker
 	}
 
 	ctx := newContext(configuration, prepareBuildActions)
@@ -199,26 +239,27 @@
 	} else {
 		ninjaDeps := bootstrap.RunBlueprint(blueprintArgs, ctx.Context, configuration)
 		ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
-		err := deptools.WriteDepFile(shared.JoinPath(topDir, blueprintArgs.DepFile), blueprintArgs.OutFile, ninjaDeps)
-		if err != nil {
-			fmt.Fprintf(os.Stderr, "Error writing depfile '%s': %s\n", blueprintArgs.DepFile, err)
-			os.Exit(1)
+
+		globListFiles := writeBuildGlobsNinjaFile(ctx.SrcDir(), configuration.SoongOutDir(), ctx.Globs, configuration)
+		ninjaDeps = append(ninjaDeps, globListFiles...)
+
+		// Convert the Soong module graph into Bazel BUILD files.
+		if generateQueryView {
+			runQueryView(configuration, ctx)
+			return cmdlineArgs.OutFile // TODO: This is a lie
+		} else if moduleGraphFile != "" {
+			writeJsonModuleGraph(ctx, moduleGraphFile)
+			writeDepFile(moduleGraphFile, ninjaDeps)
+			return moduleGraphFile
+		} else {
+			// The actual output (build.ninja) was written in the RunBlueprint() call
+			// above
+			writeDepFile(cmdlineArgs.OutFile, ninjaDeps)
 		}
 	}
 
-	// Convert the Soong module graph into Bazel BUILD files.
-	if generateQueryView {
-		runQueryView(configuration, ctx)
-		return bootstrap.CmdlineArgs.OutFile // TODO: This is a lie
-	}
-
-	if jsonModuleFile != "" {
-		writeJsonModuleGraph(configuration, ctx, jsonModuleFile, extraNinjaDeps)
-		return bootstrap.CmdlineArgs.OutFile // TODO: This is a lie
-	}
-
 	writeMetrics(configuration)
-	return bootstrap.CmdlineArgs.OutFile
+	return cmdlineArgs.OutFile
 }
 
 // soong_ui dumps the available environment variables to
@@ -257,9 +298,7 @@
 
 	availableEnv := parseAvailableEnv()
 
-	// The top-level Blueprints file is passed as the first argument.
-	srcDir := filepath.Dir(flag.Arg(0))
-	configuration := newConfig(srcDir, outDir, availableEnv)
+	configuration := newConfig(outDir, availableEnv)
 	extraNinjaDeps := []string{
 		configuration.ProductVariablesFileName,
 		usedEnvFile,
@@ -272,7 +311,7 @@
 	if shared.IsDebugging() {
 		// Add a non-existent file to the dependencies so that soong_build will rerun when the debugger is
 		// enabled even if it completed successfully.
-		extraNinjaDeps = append(extraNinjaDeps, filepath.Join(configuration.BuildDir(), "always_rerun_for_delve"))
+		extraNinjaDeps = append(extraNinjaDeps, filepath.Join(configuration.SoongOutDir(), "always_rerun_for_delve"))
 	}
 
 	if docFile != "" {
@@ -313,29 +352,6 @@
 	touch(shared.JoinPath(topDir, finalOutputFile))
 }
 
-// Workarounds to support running bp2build in a clean AOSP checkout with no
-// prior builds, and exiting early as soon as the BUILD files get generated,
-// therefore not creating build.ninja files that soong_ui and callers of
-// soong_build expects.
-//
-// These files are: build.ninja and build.ninja.d. Since Kati hasn't been
-// ran as well, and `nothing` is defined in a .mk file, there isn't a ninja
-// target called `nothing`, so we manually create it here.
-func writeFakeNinjaFile(extraNinjaDeps []string, buildDir string) {
-	extraNinjaDepsString := strings.Join(extraNinjaDeps, " \\\n ")
-
-	ninjaFileName := "build.ninja"
-	ninjaFile := shared.JoinPath(topDir, buildDir, ninjaFileName)
-	ninjaFileD := shared.JoinPath(topDir, buildDir, ninjaFileName)
-	// A workaround to create the 'nothing' ninja target so `m nothing` works,
-	// since bp2build runs without Kati, and the 'nothing' target is declared in
-	// a Makefile.
-	ioutil.WriteFile(ninjaFile, []byte("build nothing: phony\n  phony_output = true\n"), 0666)
-	ioutil.WriteFile(ninjaFileD,
-		[]byte(fmt.Sprintf("%s: \\\n %s\n", ninjaFileName, extraNinjaDepsString)),
-		0666)
-}
-
 func touch(path string) {
 	f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
 	if err != nil {
@@ -357,6 +373,83 @@
 	}
 }
 
+// Find BUILD files in the srcDir which...
+//
+// - are not on the allow list (android/bazel.go#ShouldKeepExistingBuildFileForDir())
+//
+// - won't be overwritten by corresponding bp2build generated files
+//
+// And return their paths so they can be left out of the Bazel workspace dir (i.e. ignored)
+func getPathsToIgnoredBuildFiles(topDir string, generatedRoot string, srcDirBazelFiles []string) []string {
+	paths := make([]string, 0)
+
+	for _, srcDirBazelFileRelativePath := range srcDirBazelFiles {
+		srcDirBazelFileFullPath := shared.JoinPath(topDir, srcDirBazelFileRelativePath)
+		fileInfo, err := os.Stat(srcDirBazelFileFullPath)
+		if err != nil {
+			// Warn about error, but continue trying to check files
+			fmt.Fprintf(os.Stderr, "WARNING: Error accessing path '%s', err: %s\n", srcDirBazelFileFullPath, err)
+			continue
+		}
+		if fileInfo.IsDir() {
+			// Don't ignore entire directories
+			continue
+		}
+		if !(fileInfo.Name() == "BUILD" || fileInfo.Name() == "BUILD.bazel") {
+			// Don't ignore this file - it is not a build file
+			continue
+		}
+		srcDirBazelFileDir := filepath.Dir(srcDirBazelFileRelativePath)
+		if android.ShouldKeepExistingBuildFileForDir(srcDirBazelFileDir) {
+			// Don't ignore this existing build file
+			continue
+		}
+		correspondingBp2BuildFile := shared.JoinPath(topDir, generatedRoot, srcDirBazelFileRelativePath)
+		if _, err := os.Stat(correspondingBp2BuildFile); err == nil {
+			// If bp2build generated an alternate BUILD file, don't exclude this workspace path
+			// BUILD file clash resolution happens later in the symlink forest creation
+			continue
+		}
+		fmt.Fprintf(os.Stderr, "Ignoring existing BUILD file: %s\n", srcDirBazelFileRelativePath)
+		paths = append(paths, srcDirBazelFileRelativePath)
+	}
+
+	return paths
+}
+
+// Returns temporary symlink forest excludes necessary for bazel build //external/... (and bazel build //frameworks/...) to work
+func getTemporaryExcludes() []string {
+	excludes := make([]string, 0)
+
+	// FIXME: 'autotest_lib' is a symlink back to external/autotest, and this causes an infinite symlink expansion error for Bazel
+	excludes = append(excludes, "external/autotest/venv/autotest_lib")
+
+	// FIXME: The external/google-fruit/extras/bazel_root/third_party/fruit dir is poison
+	// It contains several symlinks back to real source dirs, and those source dirs contain BUILD files we want to ignore
+	excludes = append(excludes, "external/google-fruit/extras/bazel_root/third_party/fruit")
+
+	// FIXME: 'frameworks/compile/slang' has a filegroup error due to an escaping issue
+	excludes = append(excludes, "frameworks/compile/slang")
+
+	return excludes
+}
+
+// Read the bazel.list file that the Soong Finder already dumped earlier (hopefully)
+// It contains the locations of BUILD files, BUILD.bazel files, etc. in the source dir
+func getExistingBazelRelatedFiles(topDir string) ([]string, error) {
+	bazelFinderFile := filepath.Join(filepath.Dir(cmdlineArgs.ModuleListFile), "bazel.list")
+	if !filepath.IsAbs(bazelFinderFile) {
+		// Assume this was a relative path under topDir
+		bazelFinderFile = filepath.Join(topDir, bazelFinderFile)
+	}
+	data, err := ioutil.ReadFile(bazelFinderFile)
+	if err != nil {
+		return nil, err
+	}
+	files := strings.Split(strings.TrimSpace(string(data)), "\n")
+	return files, nil
+}
+
 // Run Soong in the bp2build mode. This creates a standalone context that registers
 // an alternate pipeline of mutators and singletons specifically for generating
 // Bazel BUILD files instead of Ninja files.
@@ -375,8 +468,8 @@
 	// Android.bp files. It must not depend on the values of per-build product
 	// configurations or variables, since those will generate different BUILD
 	// files based on how the user has configured their tree.
-	bp2buildCtx.SetModuleListFile(bootstrap.CmdlineArgs.ModuleListFile)
-	modulePaths, err := bp2buildCtx.ListModulePaths(configuration.SrcDir())
+	bp2buildCtx.SetModuleListFile(cmdlineArgs.ModuleListFile)
+	modulePaths, err := bp2buildCtx.ListModulePaths(".")
 	if err != nil {
 		panic(err)
 	}
@@ -389,19 +482,20 @@
 	// Run the loading and analysis pipeline to prepare the graph of regular
 	// Modules parsed from Android.bp files, and the BazelTargetModules mapped
 	// from the regular Modules.
-	blueprintArgs := bootstrap.CmdlineArgs
+	blueprintArgs := cmdlineArgs
 	ninjaDeps := bootstrap.RunBlueprint(blueprintArgs, bp2buildCtx.Context, configuration)
 	ninjaDeps = append(ninjaDeps, extraNinjaDeps...)
 
-	ninjaDeps = append(ninjaDeps, bootstrap.GlobFileListFiles(configuration)...)
+	globListFiles := writeBuildGlobsNinjaFile(bp2buildCtx.SrcDir(), configuration.SoongOutDir(), bp2buildCtx.Globs, configuration)
+	ninjaDeps = append(ninjaDeps, globListFiles...)
 
 	// Run the code-generation phase to convert BazelTargetModules to BUILD files
 	// and print conversion metrics to the user.
 	codegenContext := bp2build.NewCodegenContext(configuration, *bp2buildCtx, bp2build.Bp2Build)
 	metrics := bp2build.Codegen(codegenContext)
 
-	generatedRoot := shared.JoinPath(configuration.BuildDir(), "bp2build")
-	workspaceRoot := shared.JoinPath(configuration.BuildDir(), "workspace")
+	generatedRoot := shared.JoinPath(configuration.SoongOutDir(), "bp2build")
+	workspaceRoot := shared.JoinPath(configuration.SoongOutDir(), "workspace")
 
 	excludes := []string{
 		"bazel-bin",
@@ -411,12 +505,23 @@
 		"bazel-" + filepath.Base(topDir),
 	}
 
-	if bootstrap.CmdlineArgs.NinjaBuildDir[0] != '/' {
-		excludes = append(excludes, bootstrap.CmdlineArgs.NinjaBuildDir)
+	if cmdlineArgs.OutDir[0] != '/' {
+		excludes = append(excludes, cmdlineArgs.OutDir)
 	}
 
+	existingBazelRelatedFiles, err := getExistingBazelRelatedFiles(topDir)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "Error determining existing Bazel-related files: %s\n", err)
+		os.Exit(1)
+	}
+
+	pathsToIgnoredBuildFiles := getPathsToIgnoredBuildFiles(topDir, generatedRoot, existingBazelRelatedFiles)
+	excludes = append(excludes, pathsToIgnoredBuildFiles...)
+
+	excludes = append(excludes, getTemporaryExcludes()...)
+
 	symlinkForestDeps := bp2build.PlantSymlinkForest(
-		topDir, workspaceRoot, generatedRoot, configuration.SrcDir(), excludes)
+		topDir, workspaceRoot, generatedRoot, ".", excludes)
 
 	// Only report metrics when in bp2build mode. The metrics aren't relevant
 	// for queryview, since that's a total repo-wide conversion and there's a
@@ -426,16 +531,8 @@
 	ninjaDeps = append(ninjaDeps, codegenContext.AdditionalNinjaDeps()...)
 	ninjaDeps = append(ninjaDeps, symlinkForestDeps...)
 
-	depFile := bp2buildMarker + ".d"
-	err = deptools.WriteDepFile(shared.JoinPath(topDir, depFile), bp2buildMarker, ninjaDeps)
-	if err != nil {
-		fmt.Fprintf(os.Stderr, "Cannot write depfile '%s': %s\n", depFile, err)
-		os.Exit(1)
-	}
+	writeDepFile(bp2buildMarker, ninjaDeps)
 
-	if bp2buildMarker != "" {
-		touch(shared.JoinPath(topDir, bp2buildMarker))
-	} else {
-		writeFakeNinjaFile(extraNinjaDeps, codegenContext.Config().BuildDir())
-	}
+	// Create an empty bp2build marker file.
+	touch(shared.JoinPath(topDir, bp2buildMarker))
 }
diff --git a/cmd/soong_build/queryview.go b/cmd/soong_build/queryview.go
index e2ce772..a8602de 100644
--- a/cmd/soong_build/queryview.go
+++ b/cmd/soong_build/queryview.go
@@ -25,9 +25,10 @@
 func createBazelQueryView(ctx *bp2build.CodegenContext, bazelQueryViewDir string) error {
 	ruleShims := bp2build.CreateRuleShims(android.ModuleTypeFactories())
 
-	// Ignore metrics reporting for queryview, since queryview is already a full-repo
-	// conversion and can use data from bazel query directly.
-	buildToTargets, _ := bp2build.GenerateBazelTargets(ctx, true)
+	// Ignore metrics reporting and compat layers for queryview, since queryview
+	// is already a full-repo conversion and can use data from bazel query
+	// directly.
+	buildToTargets, _, _ := bp2build.GenerateBazelTargets(ctx, true)
 
 	filesToWrite := bp2build.CreateBazelFiles(ruleShims, buildToTargets, bp2build.QueryView)
 	for _, f := range filesToWrite {
diff --git a/cmd/soong_ui/Android.bp b/cmd/soong_ui/Android.bp
index 4f5eea9..9f10b82 100644
--- a/cmd/soong_ui/Android.bp
+++ b/cmd/soong_ui/Android.bp
@@ -20,6 +20,7 @@
     name: "soong_ui",
     deps: [
         "soong-ui-build",
+        "soong-ui-signal",
         "soong-ui-logger",
         "soong-ui-terminal",
         "soong-ui-tracer",
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index 8bcc712..d709787 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -30,6 +30,7 @@
 	"android/soong/ui/build"
 	"android/soong/ui/logger"
 	"android/soong/ui/metrics"
+	"android/soong/ui/signal"
 	"android/soong/ui/status"
 	"android/soong/ui/terminal"
 	"android/soong/ui/tracer"
@@ -190,7 +191,7 @@
 	stat.AddOutput(trace.StatusTracer())
 
 	// Set up a cleanup procedure in case the normal termination process doesn't work.
-	build.SetupSignals(log, cancel, func() {
+	signal.SetupSignals(log, cancel, func() {
 		trace.Close()
 		log.Cleanup()
 		stat.Finish()
@@ -205,13 +206,12 @@
 		Status:  stat,
 	}}
 
-	config := c.config(buildCtx, args...)
-
 	if err := loadEnvConfig(); err != nil {
 		fmt.Fprintf(os.Stderr, "failed to parse env config files: %v", err)
 		os.Exit(1)
 	}
 
+	config := c.config(buildCtx, args...)
 
 	build.SetupOutDir(buildCtx, config)
 
diff --git a/cuj/Android.bp b/cuj/Android.bp
index a2da6e6..f9cfdc1 100644
--- a/cuj/Android.bp
+++ b/cuj/Android.bp
@@ -7,6 +7,7 @@
     deps: [
         "soong-ui-build",
         "soong-ui-logger",
+        "soong-ui-signal",
         "soong-ui-terminal",
         "soong-ui-tracer",
     ],
diff --git a/cuj/cuj.go b/cuj/cuj.go
index b4ae9a2..413f423 100644
--- a/cuj/cuj.go
+++ b/cuj/cuj.go
@@ -27,6 +27,7 @@
 	"android/soong/ui/build"
 	"android/soong/ui/logger"
 	"android/soong/ui/metrics"
+	"android/soong/ui/signal"
 	"android/soong/ui/status"
 	"android/soong/ui/terminal"
 	"android/soong/ui/tracer"
@@ -65,7 +66,7 @@
 	stat.AddOutput(output)
 	stat.AddOutput(trace.StatusTracer())
 
-	build.SetupSignals(log, cancel, func() {
+	signal.SetupSignals(log, cancel, func() {
 		trace.Close()
 		log.Cleanup()
 		stat.Finish()
diff --git a/dexpreopt/OWNERS b/dexpreopt/OWNERS
index 166472f..5a2a198 100644
--- a/dexpreopt/OWNERS
+++ b/dexpreopt/OWNERS
@@ -1 +1 @@
-per-file * = ngeoffray@google.com,calin@google.com,mathieuc@google.com
+per-file * = ngeoffray@google.com,calin@google.com,skvadrik@google.com
diff --git a/dexpreopt/class_loader_context.go b/dexpreopt/class_loader_context.go
index 57b4c4f..1bdd040 100644
--- a/dexpreopt/class_loader_context.go
+++ b/dexpreopt/class_loader_context.go
@@ -15,6 +15,7 @@
 package dexpreopt
 
 import (
+	"encoding/json"
 	"fmt"
 	"sort"
 	"strconv"
@@ -192,6 +193,13 @@
 	// The name of the library.
 	Name string
 
+	// If the library is optional or required.
+	Optional bool
+
+	// If the library is implicitly infered by Soong (as opposed to explicitly added via `uses_libs`
+	// or `optional_uses_libs`.
+	Implicit bool
+
 	// On-host build path to the library dex file (used in dex2oat argument --class-loader-context).
 	Host android.Path
 
@@ -254,8 +262,9 @@
 const AnySdkVersion int = android.FutureApiLevelInt
 
 // Add class loader context for the given library to the map entry for the given SDK version.
-func (clcMap ClassLoaderContextMap) addContext(ctx android.ModuleInstallPathContext, sdkVer int, lib string,
-	hostPath, installPath android.Path, nestedClcMap ClassLoaderContextMap) error {
+func (clcMap ClassLoaderContextMap) addContext(ctx android.ModuleInstallPathContext, sdkVer int,
+	lib string, optional, implicit bool, hostPath, installPath android.Path,
+	nestedClcMap ClassLoaderContextMap) error {
 
 	// For prebuilts, library should have the same name as the source module.
 	lib = android.RemoveOptionalPrebuiltPrefix(lib)
@@ -285,16 +294,26 @@
 	}
 	subcontexts := nestedClcMap[AnySdkVersion]
 
-	// If the library with this name is already present as one of the unconditional top-level
-	// components, do not re-add it.
+	// Check if the library with this name is already present in unconditional top-level CLC.
 	for _, clc := range clcMap[sdkVer] {
-		if clc.Name == lib {
+		if clc.Name != lib {
+			// Ok, a different library.
+		} else if clc.Host == hostPath && clc.Device == devicePath {
+			// Ok, the same library with the same paths. Don't re-add it, but don't raise an error
+			// either, as the same library may be reachable via different transitional dependencies.
 			return nil
+		} else {
+			// Fail, as someone is trying to add the same library with different paths. This likely
+			// indicates an error somewhere else, like trying to add a stub library.
+			return fmt.Errorf("a <uses-library> named %q is already in class loader context,"+
+				"but the library paths are different:\t\n", lib)
 		}
 	}
 
 	clcMap[sdkVer] = append(clcMap[sdkVer], &ClassLoaderContext{
 		Name:        lib,
+		Optional:    optional,
+		Implicit:    implicit,
 		Host:        hostPath,
 		Device:      devicePath,
 		Subcontexts: subcontexts,
@@ -307,9 +326,10 @@
 // about paths). For the subset of libraries that are used in dexpreopt, their build/install paths
 // are validated later before CLC is used (in validateClassLoaderContext).
 func (clcMap ClassLoaderContextMap) AddContext(ctx android.ModuleInstallPathContext, sdkVer int,
-	lib string, hostPath, installPath android.Path, nestedClcMap ClassLoaderContextMap) {
+	lib string, optional, implicit bool, hostPath, installPath android.Path,
+	nestedClcMap ClassLoaderContextMap) {
 
-	err := clcMap.addContext(ctx, sdkVer, lib, hostPath, installPath, nestedClcMap)
+	err := clcMap.addContext(ctx, sdkVer, lib, optional, implicit, hostPath, installPath, nestedClcMap)
 	if err != nil {
 		ctx.ModuleErrorf(err.Error())
 	}
@@ -352,15 +372,40 @@
 // Returns top-level libraries in the CLC (conditional CLC, i.e. compatibility libraries are not
 // included). This is the list of libraries that should be in the <uses-library> tags in the
 // manifest. Some of them may be present in the source manifest, others are added by manifest_fixer.
-func (clcMap ClassLoaderContextMap) UsesLibs() (ulibs []string) {
+// Required and optional libraries are in separate lists.
+func (clcMap ClassLoaderContextMap) usesLibs(implicit bool) (required []string, optional []string) {
 	if clcMap != nil {
 		clcs := clcMap[AnySdkVersion]
-		ulibs = make([]string, 0, len(clcs))
+		required = make([]string, 0, len(clcs))
+		optional = make([]string, 0, len(clcs))
 		for _, clc := range clcs {
-			ulibs = append(ulibs, clc.Name)
+			if implicit && !clc.Implicit {
+				// Skip, this is an explicit library and we need only the implicit ones.
+			} else if clc.Optional {
+				optional = append(optional, clc.Name)
+			} else {
+				required = append(required, clc.Name)
+			}
 		}
 	}
-	return ulibs
+	return required, optional
+}
+
+func (clcMap ClassLoaderContextMap) UsesLibs() ([]string, []string) {
+	return clcMap.usesLibs(false)
+}
+
+func (clcMap ClassLoaderContextMap) ImplicitUsesLibs() ([]string, []string) {
+	return clcMap.usesLibs(true)
+}
+
+func (clcMap ClassLoaderContextMap) Dump() string {
+	jsonCLC := toJsonClassLoaderContext(clcMap)
+	bytes, err := json.MarshalIndent(jsonCLC, "", "  ")
+	if err != nil {
+		panic(err)
+	}
+	return string(bytes)
 }
 
 // Now that the full unconditional context is known, reconstruct conditional context.
@@ -370,7 +415,8 @@
 // TODO(b/132357300): remove "android.hidl.manager" and "android.hidl.base" for non-system apps.
 //
 func fixClassLoaderContext(clcMap ClassLoaderContextMap) {
-	usesLibs := clcMap.UsesLibs()
+	required, optional := clcMap.UsesLibs()
+	usesLibs := append(required, optional...)
 
 	for sdkVer, clcs := range clcMap {
 		if sdkVer == AnySdkVersion {
@@ -495,6 +541,8 @@
 // the same as Soong representation except that SDK versions and paths are represented with strings.
 type jsonClassLoaderContext struct {
 	Name        string
+	Optional    bool
+	Implicit    bool
 	Host        string
 	Device      string
 	Subcontexts []*jsonClassLoaderContext
@@ -526,6 +574,8 @@
 	for _, clc := range jClcs {
 		clcs = append(clcs, &ClassLoaderContext{
 			Name:        clc.Name,
+			Optional:    clc.Optional,
+			Implicit:    clc.Implicit,
 			Host:        constructPath(ctx, clc.Host),
 			Device:      clc.Device,
 			Subcontexts: fromJsonClassLoaderContextRec(ctx, clc.Subcontexts),
@@ -550,6 +600,8 @@
 	for i, clc := range clcs {
 		jClcs[i] = &jsonClassLoaderContext{
 			Name:        clc.Name,
+			Optional:    clc.Optional,
+			Implicit:    clc.Implicit,
 			Host:        clc.Host.String(),
 			Device:      clc.Device,
 			Subcontexts: toJsonClassLoaderContextRec(clc.Subcontexts),
diff --git a/dexpreopt/class_loader_context_test.go b/dexpreopt/class_loader_context_test.go
index 610a4c9..d81ac2c 100644
--- a/dexpreopt/class_loader_context_test.go
+++ b/dexpreopt/class_loader_context_test.go
@@ -49,32 +49,35 @@
 	//
 	ctx := testContext()
 
+	optional := false
+	implicit := true
+
 	m := make(ClassLoaderContextMap)
 
-	m.AddContext(ctx, AnySdkVersion, "a", buildPath(ctx, "a"), installPath(ctx, "a"), nil)
-	m.AddContext(ctx, AnySdkVersion, "b", buildPath(ctx, "b"), installPath(ctx, "b"), nil)
-	m.AddContext(ctx, AnySdkVersion, "c", buildPath(ctx, "c"), installPath(ctx, "c"), nil)
+	m.AddContext(ctx, AnySdkVersion, "a", optional, implicit, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
+	m.AddContext(ctx, AnySdkVersion, "b", optional, implicit, buildPath(ctx, "b"), installPath(ctx, "b"), nil)
+	m.AddContext(ctx, AnySdkVersion, "c", optional, implicit, buildPath(ctx, "c"), installPath(ctx, "c"), nil)
 
 	// Add some libraries with nested subcontexts.
 
 	m1 := make(ClassLoaderContextMap)
-	m1.AddContext(ctx, AnySdkVersion, "a1", buildPath(ctx, "a1"), installPath(ctx, "a1"), nil)
-	m1.AddContext(ctx, AnySdkVersion, "b1", buildPath(ctx, "b1"), installPath(ctx, "b1"), nil)
+	m1.AddContext(ctx, AnySdkVersion, "a1", optional, implicit, buildPath(ctx, "a1"), installPath(ctx, "a1"), nil)
+	m1.AddContext(ctx, AnySdkVersion, "b1", optional, implicit, buildPath(ctx, "b1"), installPath(ctx, "b1"), nil)
 
 	m2 := make(ClassLoaderContextMap)
-	m2.AddContext(ctx, AnySdkVersion, "a2", buildPath(ctx, "a2"), installPath(ctx, "a2"), nil)
-	m2.AddContext(ctx, AnySdkVersion, "b2", buildPath(ctx, "b2"), installPath(ctx, "b2"), nil)
-	m2.AddContext(ctx, AnySdkVersion, "c2", buildPath(ctx, "c2"), installPath(ctx, "c2"), m1)
+	m2.AddContext(ctx, AnySdkVersion, "a2", optional, implicit, buildPath(ctx, "a2"), installPath(ctx, "a2"), nil)
+	m2.AddContext(ctx, AnySdkVersion, "b2", optional, implicit, buildPath(ctx, "b2"), installPath(ctx, "b2"), nil)
+	m2.AddContext(ctx, AnySdkVersion, "c2", optional, implicit, buildPath(ctx, "c2"), installPath(ctx, "c2"), m1)
 
 	m3 := make(ClassLoaderContextMap)
-	m3.AddContext(ctx, AnySdkVersion, "a3", buildPath(ctx, "a3"), installPath(ctx, "a3"), nil)
-	m3.AddContext(ctx, AnySdkVersion, "b3", buildPath(ctx, "b3"), installPath(ctx, "b3"), nil)
+	m3.AddContext(ctx, AnySdkVersion, "a3", optional, implicit, buildPath(ctx, "a3"), installPath(ctx, "a3"), nil)
+	m3.AddContext(ctx, AnySdkVersion, "b3", optional, implicit, buildPath(ctx, "b3"), installPath(ctx, "b3"), nil)
 
-	m.AddContext(ctx, AnySdkVersion, "d", buildPath(ctx, "d"), installPath(ctx, "d"), m2)
+	m.AddContext(ctx, AnySdkVersion, "d", optional, implicit, buildPath(ctx, "d"), installPath(ctx, "d"), m2)
 	// When the same library is both in conditional and unconditional context, it should be removed
 	// from conditional context.
-	m.AddContext(ctx, 42, "f", buildPath(ctx, "f"), installPath(ctx, "f"), nil)
-	m.AddContext(ctx, AnySdkVersion, "f", buildPath(ctx, "f"), installPath(ctx, "f"), nil)
+	m.AddContext(ctx, 42, "f", optional, implicit, buildPath(ctx, "f"), installPath(ctx, "f"), nil)
+	m.AddContext(ctx, AnySdkVersion, "f", optional, implicit, buildPath(ctx, "f"), installPath(ctx, "f"), nil)
 
 	// Merge map with implicit root library that is among toplevel contexts => does nothing.
 	m.AddContextMap(m1, "c")
@@ -83,12 +86,12 @@
 	m.AddContextMap(m3, "m_g")
 
 	// Compatibility libraries with unknown install paths get default paths.
-	m.AddContext(ctx, 29, AndroidHidlManager, buildPath(ctx, AndroidHidlManager), nil, nil)
-	m.AddContext(ctx, 29, AndroidHidlBase, buildPath(ctx, AndroidHidlBase), nil, nil)
+	m.AddContext(ctx, 29, AndroidHidlManager, optional, implicit, buildPath(ctx, AndroidHidlManager), nil, nil)
+	m.AddContext(ctx, 29, AndroidHidlBase, optional, implicit, buildPath(ctx, AndroidHidlBase), nil, nil)
 
 	// Add "android.test.mock" to conditional CLC, observe that is gets removed because it is only
 	// needed as a compatibility library if "android.test.runner" is in CLC as well.
-	m.AddContext(ctx, 30, AndroidTestMock, buildPath(ctx, AndroidTestMock), nil, nil)
+	m.AddContext(ctx, 30, AndroidTestMock, optional, implicit, buildPath(ctx, AndroidTestMock), nil, nil)
 
 	valid, validationError := validateClassLoaderContext(m)
 
@@ -96,10 +99,10 @@
 
 	var haveStr string
 	var havePaths android.Paths
-	var haveUsesLibs []string
+	var haveUsesLibsReq, haveUsesLibsOpt []string
 	if valid && validationError == nil {
 		haveStr, havePaths = ComputeClassLoaderContext(m)
-		haveUsesLibs = m.UsesLibs()
+		haveUsesLibsReq, haveUsesLibsOpt = m.UsesLibs()
 	}
 
 	// Test that validation is successful (all paths are known).
@@ -148,20 +151,26 @@
 
 	// Test for libraries that are added by the manifest_fixer.
 	t.Run("uses libs", func(t *testing.T) {
-		wantUsesLibs := []string{"a", "b", "c", "d", "f", "a3", "b3"}
-		if !reflect.DeepEqual(wantUsesLibs, haveUsesLibs) {
-			t.Errorf("\nwant uses libs: %s\nhave uses libs: %s", wantUsesLibs, haveUsesLibs)
+		wantUsesLibsReq := []string{"a", "b", "c", "d", "f", "a3", "b3"}
+		wantUsesLibsOpt := []string{}
+		if !reflect.DeepEqual(wantUsesLibsReq, haveUsesLibsReq) {
+			t.Errorf("\nwant required uses libs: %s\nhave required uses libs: %s", wantUsesLibsReq, haveUsesLibsReq)
+		}
+		if !reflect.DeepEqual(wantUsesLibsOpt, haveUsesLibsOpt) {
+			t.Errorf("\nwant optional uses libs: %s\nhave optional uses libs: %s", wantUsesLibsOpt, haveUsesLibsOpt)
 		}
 	})
 }
 
 func TestCLCJson(t *testing.T) {
 	ctx := testContext()
+	optional := false
+	implicit := true
 	m := make(ClassLoaderContextMap)
-	m.AddContext(ctx, 28, "a", buildPath(ctx, "a"), installPath(ctx, "a"), nil)
-	m.AddContext(ctx, 29, "b", buildPath(ctx, "b"), installPath(ctx, "b"), nil)
-	m.AddContext(ctx, 30, "c", buildPath(ctx, "c"), installPath(ctx, "c"), nil)
-	m.AddContext(ctx, AnySdkVersion, "d", buildPath(ctx, "d"), installPath(ctx, "d"), nil)
+	m.AddContext(ctx, 28, "a", optional, implicit, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
+	m.AddContext(ctx, 29, "b", optional, implicit, buildPath(ctx, "b"), installPath(ctx, "b"), nil)
+	m.AddContext(ctx, 30, "c", optional, implicit, buildPath(ctx, "c"), installPath(ctx, "c"), nil)
+	m.AddContext(ctx, AnySdkVersion, "d", optional, implicit, buildPath(ctx, "d"), installPath(ctx, "d"), nil)
 	jsonCLC := toJsonClassLoaderContext(m)
 	restored := fromJsonClassLoaderContext(ctx, jsonCLC)
 	android.AssertIntEquals(t, "The size of the maps should be the same.", len(m), len(restored))
@@ -181,20 +190,26 @@
 // Test that unknown library paths cause a validation error.
 func testCLCUnknownPath(t *testing.T, whichPath string) {
 	ctx := testContext()
+	optional := false
+	implicit := true
 
 	m := make(ClassLoaderContextMap)
 	if whichPath == "build" {
-		m.AddContext(ctx, AnySdkVersion, "a", nil, nil, nil)
+		m.AddContext(ctx, AnySdkVersion, "a", optional, implicit, nil, nil, nil)
 	} else {
-		m.AddContext(ctx, AnySdkVersion, "a", buildPath(ctx, "a"), nil, nil)
+		m.AddContext(ctx, AnySdkVersion, "a", optional, implicit, buildPath(ctx, "a"), nil, nil)
 	}
 
 	// The library should be added to <uses-library> tags by the manifest_fixer.
 	t.Run("uses libs", func(t *testing.T) {
-		haveUsesLibs := m.UsesLibs()
-		wantUsesLibs := []string{"a"}
-		if !reflect.DeepEqual(wantUsesLibs, haveUsesLibs) {
-			t.Errorf("\nwant uses libs: %s\nhave uses libs: %s", wantUsesLibs, haveUsesLibs)
+		haveUsesLibsReq, haveUsesLibsOpt := m.UsesLibs()
+		wantUsesLibsReq := []string{"a"}
+		wantUsesLibsOpt := []string{}
+		if !reflect.DeepEqual(wantUsesLibsReq, haveUsesLibsReq) {
+			t.Errorf("\nwant required uses libs: %s\nhave required uses libs: %s", wantUsesLibsReq, haveUsesLibsReq)
+		}
+		if !reflect.DeepEqual(wantUsesLibsOpt, haveUsesLibsOpt) {
+			t.Errorf("\nwant optional uses libs: %s\nhave optional uses libs: %s", wantUsesLibsOpt, haveUsesLibsOpt)
 		}
 	})
 
@@ -216,10 +231,12 @@
 // An attempt to add conditional nested subcontext should fail.
 func TestCLCNestedConditional(t *testing.T) {
 	ctx := testContext()
+	optional := false
+	implicit := true
 	m1 := make(ClassLoaderContextMap)
-	m1.AddContext(ctx, 42, "a", buildPath(ctx, "a"), installPath(ctx, "a"), nil)
+	m1.AddContext(ctx, 42, "a", optional, implicit, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
 	m := make(ClassLoaderContextMap)
-	err := m.addContext(ctx, AnySdkVersion, "b", buildPath(ctx, "b"), installPath(ctx, "b"), m1)
+	err := m.addContext(ctx, AnySdkVersion, "b", optional, implicit, buildPath(ctx, "b"), installPath(ctx, "b"), m1)
 	checkError(t, err, "nested class loader context shouldn't have conditional part")
 }
 
@@ -227,11 +244,13 @@
 // they end up in the order that agrees with PackageManager.
 func TestCLCSdkVersionOrder(t *testing.T) {
 	ctx := testContext()
+	optional := false
+	implicit := true
 	m := make(ClassLoaderContextMap)
-	m.AddContext(ctx, 28, "a", buildPath(ctx, "a"), installPath(ctx, "a"), nil)
-	m.AddContext(ctx, 29, "b", buildPath(ctx, "b"), installPath(ctx, "b"), nil)
-	m.AddContext(ctx, 30, "c", buildPath(ctx, "c"), installPath(ctx, "c"), nil)
-	m.AddContext(ctx, AnySdkVersion, "d", buildPath(ctx, "d"), installPath(ctx, "d"), nil)
+	m.AddContext(ctx, 28, "a", optional, implicit, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
+	m.AddContext(ctx, 29, "b", optional, implicit, buildPath(ctx, "b"), installPath(ctx, "b"), nil)
+	m.AddContext(ctx, 30, "c", optional, implicit, buildPath(ctx, "c"), installPath(ctx, "c"), nil)
+	m.AddContext(ctx, AnySdkVersion, "d", optional, implicit, buildPath(ctx, "d"), installPath(ctx, "d"), nil)
 
 	valid, validationError := validateClassLoaderContext(m)
 
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index 0bcec17..de3666a 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -44,15 +44,15 @@
 	DisableGenerateProfile bool   // don't generate profiles
 	ProfileDir             string // directory to find profiles in
 
-	BootJars          android.ConfiguredJarList // modules for jars that form the boot class path
-	UpdatableBootJars android.ConfiguredJarList // jars within apex that form the boot class path
+	BootJars     android.ConfiguredJarList // modules for jars that form the boot class path
+	ApexBootJars android.ConfiguredJarList // jars within apex that form the boot class path
 
 	ArtApexJars android.ConfiguredJarList // modules for jars that are in the ART APEX
 
-	SystemServerJars          android.ConfiguredJarList // jars that form the system server
-	SystemServerApps          []string                  // apps that are loaded into system server
-	UpdatableSystemServerJars android.ConfiguredJarList // jars within apex that are loaded into system server
-	SpeedApps                 []string                  // apps that should be speed optimized
+	SystemServerJars     android.ConfiguredJarList // jars that form the system server
+	SystemServerApps     []string                  // apps that are loaded into system server
+	ApexSystemServerJars android.ConfiguredJarList // jars within apex that are loaded into system server
+	SpeedApps            []string                  // apps that should be speed optimized
 
 	BrokenSuboptimalOrderOfSystemServerJars bool // if true, sub-optimal order does not cause a build error
 
@@ -159,7 +159,7 @@
 }
 
 func constructPath(ctx android.PathContext, path string) android.Path {
-	buildDirPrefix := ctx.Config().BuildDir() + "/"
+	buildDirPrefix := ctx.Config().SoongOutDir() + "/"
 	if path == "" {
 		return nil
 	} else if strings.HasPrefix(path, buildDirPrefix) {
@@ -531,7 +531,7 @@
 	return config, nil
 }
 
-// checkBootJarsConfigConsistency checks the consistency of BootJars and UpdatableBootJars fields in
+// checkBootJarsConfigConsistency checks the consistency of BootJars and ApexBootJars fields in
 // DexpreoptGlobalConfig and Config.productVariables.
 func checkBootJarsConfigConsistency(ctx android.SingletonContext, dexpreoptConfig *GlobalConfig, config android.Config) {
 	compareBootJars := func(property string, dexpreoptJars, variableJars android.ConfiguredJarList) {
@@ -545,8 +545,8 @@
 		}
 	}
 
-	compareBootJars("BootJars", dexpreoptConfig.BootJars, config.NonUpdatableBootJars())
-	compareBootJars("UpdatableBootJars", dexpreoptConfig.UpdatableBootJars, config.UpdatableBootJars())
+	compareBootJars("BootJars", dexpreoptConfig.BootJars, config.NonApexBootJars())
+	compareBootJars("ApexBootJars", dexpreoptConfig.ApexBootJars, config.ApexBootJars())
 }
 
 func (s *globalSoongConfigSingleton) GenerateBuildActions(ctx android.SingletonContext) {
@@ -614,11 +614,11 @@
 		DisableGenerateProfile:             false,
 		ProfileDir:                         "",
 		BootJars:                           android.EmptyConfiguredJarList(),
-		UpdatableBootJars:                  android.EmptyConfiguredJarList(),
+		ApexBootJars:                       android.EmptyConfiguredJarList(),
 		ArtApexJars:                        android.EmptyConfiguredJarList(),
 		SystemServerJars:                   android.EmptyConfiguredJarList(),
 		SystemServerApps:                   nil,
-		UpdatableSystemServerJars:          android.EmptyConfiguredJarList(),
+		ApexSystemServerJars:               android.EmptyConfiguredJarList(),
 		SpeedApps:                          nil,
 		PreoptFlags:                        nil,
 		DefaultCompilerFilter:              "",
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index da015a3..1401c75 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -111,7 +111,7 @@
 	}
 
 	// Don't preopt system server jars that are updatable.
-	if global.UpdatableSystemServerJars.ContainsJar(module.Name) {
+	if global.ApexSystemServerJars.ContainsJar(module.Name) {
 		return true
 	}
 
@@ -234,7 +234,7 @@
 
 	invocationPath := odexPath.ReplaceExtension(ctx, "invocation")
 
-	systemServerJars := NonUpdatableSystemServerJars(ctx, global)
+	systemServerJars := NonApexSystemServerJars(ctx, global)
 
 	rule.Command().FlagWithArg("mkdir -p ", filepath.Dir(odexPath.String()))
 	rule.Command().FlagWithOutput("rm -f ", odexPath)
@@ -430,11 +430,6 @@
 		}
 	}
 
-	// Never enable on eng.
-	if global.IsEng {
-		debugInfo = false
-	}
-
 	if debugInfo {
 		cmd.Flag("--generate-mini-debug-info")
 	} else {
@@ -523,13 +518,13 @@
 	}
 }
 
-var nonUpdatableSystemServerJarsKey = android.NewOnceKey("nonUpdatableSystemServerJars")
+var nonApexSystemServerJarsKey = android.NewOnceKey("nonApexSystemServerJars")
 
 // TODO: eliminate the superficial global config parameter by moving global config definition
 // from java subpackage to dexpreopt.
-func NonUpdatableSystemServerJars(ctx android.PathContext, global *GlobalConfig) []string {
-	return ctx.Config().Once(nonUpdatableSystemServerJarsKey, func() interface{} {
-		return android.RemoveListFromList(global.SystemServerJars.CopyOfJars(), global.UpdatableSystemServerJars.CopyOfJars())
+func NonApexSystemServerJars(ctx android.PathContext, global *GlobalConfig) []string {
+	return ctx.Config().Once(nonApexSystemServerJarsKey, func() interface{} {
+		return android.RemoveListFromList(global.SystemServerJars.CopyOfJars(), global.ApexSystemServerJars.CopyOfJars())
 	}).([]string)
 }
 
@@ -556,7 +551,7 @@
 	mctx, isModule := ctx.(android.ModuleContext)
 	if isModule {
 		config := GetGlobalConfig(ctx)
-		jars := NonUpdatableSystemServerJars(ctx, config)
+		jars := NonApexSystemServerJars(ctx, config)
 		mctx.WalkDeps(func(dep android.Module, parent android.Module) bool {
 			depIndex := android.IndexList(dep.Name(), jars)
 			if jarIndex < depIndex && !config.BrokenSuboptimalOrderOfSystemServerJars {
diff --git a/dexpreopt/testing.go b/dexpreopt/testing.go
index c0ba5ca..8f5c315 100644
--- a/dexpreopt/testing.go
+++ b/dexpreopt/testing.go
@@ -118,10 +118,24 @@
 	})
 }
 
-// FixtureSetUpdatableBootJars sets the UpdatableBootJars property in the global config.
-func FixtureSetUpdatableBootJars(bootJars ...string) android.FixturePreparer {
+// FixtureSetApexBootJars sets the ApexBootJars property in the global config.
+func FixtureSetApexBootJars(bootJars ...string) android.FixturePreparer {
 	return FixtureModifyGlobalConfig(func(dexpreoptConfig *GlobalConfig) {
-		dexpreoptConfig.UpdatableBootJars = android.CreateTestConfiguredJarList(bootJars)
+		dexpreoptConfig.ApexBootJars = android.CreateTestConfiguredJarList(bootJars)
+	})
+}
+
+// FixtureSetSystemServerJars sets the SystemServerJars property.
+func FixtureSetSystemServerJars(jars ...string) android.FixturePreparer {
+	return FixtureModifyGlobalConfig(func(dexpreoptConfig *GlobalConfig) {
+		dexpreoptConfig.SystemServerJars = android.CreateTestConfiguredJarList(jars)
+	})
+}
+
+// FixtureSetApexSystemServerJars sets the ApexSystemServerJars property in the global config.
+func FixtureSetApexSystemServerJars(jars ...string) android.FixturePreparer {
+	return FixtureModifyGlobalConfig(func(dexpreoptConfig *GlobalConfig) {
+		dexpreoptConfig.ApexSystemServerJars = android.CreateTestConfiguredJarList(jars)
 	})
 }
 
diff --git a/docs/map_files.md b/docs/map_files.md
index 192530f..1388059 100644
--- a/docs/map_files.md
+++ b/docs/map_files.md
@@ -70,9 +70,11 @@
 
 ### apex
 
-Indicates that the version or symbol is to be exposed in the APEX stubs rather
-than the NDK. May be used in combination with `llndk` if the symbol is exposed
-to both APEX and the LL-NDK.
+Indicates that the version or symbol is to be exposed by an APEX rather than the
+NDK. For APIs exposed by the platform *for* APEX, use `systemapi`.
+
+May be used in combination with `llndk` if the symbol is exposed to both APEX
+and the LL-NDK.
 
 ### future
 
@@ -144,6 +146,12 @@
 preferable to keep such APIs in an entirely separate library to protect them
 from access via `dlsym`, but this is not always possible.
 
+### systemapi
+
+This is a synonym of the `apex` tag. It should be used to clarify that the API
+is an API exposed by the system for an APEX, whereas `apex` should be used for
+APIs exposed by an APEX to the platform or another APEX.
+
 ### var
 
 Used to define a public global variable. By default all symbols are exposed as
diff --git a/etc/Android.bp b/etc/Android.bp
index cab7389..c670236 100644
--- a/etc/Android.bp
+++ b/etc/Android.bp
@@ -9,12 +9,15 @@
         "blueprint",
         "soong",
         "soong-android",
+        "soong-snapshot",
     ],
     srcs: [
         "prebuilt_etc.go",
+        "snapshot_etc.go",
     ],
     testSrcs: [
         "prebuilt_etc_test.go",
+        "snapshot_etc_test.go",
     ],
     pluginFor: ["soong_build"],
 }
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index 4dd383d..3213e5c 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -28,12 +28,16 @@
 // various `prebuilt_*` mutators.
 
 import (
+	"encoding/json"
 	"fmt"
+	"path/filepath"
 	"strings"
 
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
+	"android/soong/bazel"
+	"android/soong/snapshot"
 )
 
 var pctx = android.NewPackageContext("android/soong/etc")
@@ -43,6 +47,7 @@
 func init() {
 	pctx.Import("android/soong/android")
 	RegisterPrebuiltEtcBuildComponents(android.InitRegistrationContext)
+	snapshot.RegisterSnapshotAction(generatePrebuiltSnapshot)
 }
 
 func RegisterPrebuiltEtcBuildComponents(ctx android.RegistrationContext) {
@@ -57,6 +62,8 @@
 	ctx.RegisterModuleType("prebuilt_rfsa", PrebuiltRFSAFactory)
 
 	ctx.RegisterModuleType("prebuilt_defaults", defaultsFactory)
+
+	android.RegisterBp2BuildMutator("prebuilt_etc", PrebuiltEtcBp2Build)
 }
 
 var PrepareForTestWithPrebuiltEtc = android.FixtureRegisterWithContext(RegisterPrebuiltEtcBuildComponents)
@@ -127,6 +134,10 @@
 type PrebuiltEtc struct {
 	android.ModuleBase
 	android.DefaultableModuleBase
+	android.BazelModuleBase
+
+	snapshot.VendorSnapshotModuleInterface
+	snapshot.RecoverySnapshotModuleInterface
 
 	properties       prebuiltEtcProperties
 	subdirProperties prebuiltSubdirProperties
@@ -183,7 +194,7 @@
 	return p.inDebugRamdisk()
 }
 
-func (p *PrebuiltEtc) inRecovery() bool {
+func (p *PrebuiltEtc) InRecovery() bool {
 	return p.ModuleBase.InRecovery() || p.ModuleBase.InstallInRecovery()
 }
 
@@ -192,7 +203,7 @@
 }
 
 func (p *PrebuiltEtc) InstallInRecovery() bool {
-	return p.inRecovery()
+	return p.InRecovery()
 }
 
 var _ android.ImageInterface = (*PrebuiltEtc)(nil)
@@ -271,6 +282,18 @@
 	return p.properties.Installable == nil || proptools.Bool(p.properties.Installable)
 }
 
+func (p *PrebuiltEtc) InVendor() bool {
+	return p.ModuleBase.InstallInVendor()
+}
+
+func (p *PrebuiltEtc) ExcludeFromVendorSnapshot() bool {
+	return false
+}
+
+func (p *PrebuiltEtc) ExcludeFromRecoverySnapshot() bool {
+	return false
+}
+
 func (p *PrebuiltEtc) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	if p.properties.Src == nil {
 		ctx.PropertyErrorf("src", "missing prebuilt source file")
@@ -344,7 +367,7 @@
 	if p.inDebugRamdisk() && !p.onlyInDebugRamdisk() {
 		nameSuffix = ".debug_ramdisk"
 	}
-	if p.inRecovery() && !p.onlyInRecovery() {
+	if p.InRecovery() && !p.onlyInRecovery() {
 		nameSuffix = ".recovery"
 	}
 	return []android.AndroidMkEntries{android.AndroidMkEntries{
@@ -387,6 +410,7 @@
 	// This module is device-only
 	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
 	android.InitDefaultableModule(module)
+	android.InitBazelModule(module)
 	return module
 }
 
@@ -494,3 +518,198 @@
 	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
 	return module
 }
+
+// Flags to be included in the snapshot
+type snapshotJsonFlags struct {
+	ModuleName          string `json:",omitempty"`
+	Filename            string `json:",omitempty"`
+	RelativeInstallPath string `json:",omitempty"`
+}
+
+// Copy file into the snapshot
+func copyFile(ctx android.SingletonContext, path android.Path, out string, fake bool) android.OutputPath {
+	if fake {
+		// Create empty file instead for the fake snapshot
+		return snapshot.WriteStringToFileRule(ctx, "", out)
+	} else {
+		return snapshot.CopyFileRule(pctx, ctx, path, out)
+	}
+}
+
+// Check if the module is target of the snapshot
+func isSnapshotAware(ctx android.SingletonContext, m *PrebuiltEtc, image snapshot.SnapshotImage) bool {
+	if !m.Enabled() {
+		return false
+	}
+
+	// Skip if the module is not included in the image
+	if !image.InImage(m)() {
+		return false
+	}
+
+	// When android/prebuilt.go selects between source and prebuilt, it sets
+	// HideFromMake on the other one to avoid duplicate install rules in make.
+	if m.IsHideFromMake() {
+		return false
+	}
+
+	// There are some prebuilt_etc module with multiple definition of same name.
+	// Check if the target would be included from the build
+	if !m.ExportedToMake() {
+		return false
+	}
+
+	// Skip if the module is in the predefined path list to skip
+	if image.IsProprietaryPath(ctx.ModuleDir(m), ctx.DeviceConfig()) {
+		return false
+	}
+
+	// Skip if the module should be excluded
+	if image.ExcludeFromSnapshot(m) || image.ExcludeFromDirectedSnapshot(ctx.DeviceConfig(), m.BaseModuleName()) {
+		return false
+	}
+
+	// Skip from other exceptional cases
+	if m.Target().Os.Class != android.Device {
+		return false
+	}
+	if m.Target().NativeBridge == android.NativeBridgeEnabled {
+		return false
+	}
+
+	return true
+}
+
+func generatePrebuiltSnapshot(s snapshot.SnapshotSingleton, ctx android.SingletonContext, snapshotArchDir string) android.Paths {
+	/*
+		Snapshot zipped artifacts directory structure for etc modules:
+		{SNAPSHOT_ARCH}/
+			arch-{TARGET_ARCH}-{TARGET_ARCH_VARIANT}/
+				etc/
+					(prebuilt etc files)
+			arch-{TARGET_2ND_ARCH}-{TARGET_2ND_ARCH_VARIANT}/
+				etc/
+					(prebuilt etc files)
+			NOTICE_FILES/
+				(notice files)
+	*/
+	var snapshotOutputs android.Paths
+	noticeDir := filepath.Join(snapshotArchDir, "NOTICE_FILES")
+	installedNotices := make(map[string]bool)
+
+	ctx.VisitAllModules(func(module android.Module) {
+		m, ok := module.(*PrebuiltEtc)
+		if !ok {
+			return
+		}
+
+		if !isSnapshotAware(ctx, m, s.Image) {
+			return
+		}
+
+		targetArch := "arch-" + m.Target().Arch.ArchType.String()
+
+		snapshotLibOut := filepath.Join(snapshotArchDir, targetArch, "etc", m.BaseModuleName())
+		snapshotOutputs = append(snapshotOutputs, copyFile(ctx, m.OutputFile(), snapshotLibOut, s.Fake))
+
+		prop := snapshotJsonFlags{}
+		propOut := snapshotLibOut + ".json"
+		prop.ModuleName = m.BaseModuleName()
+		if m.subdirProperties.Relative_install_path != nil {
+			prop.RelativeInstallPath = *m.subdirProperties.Relative_install_path
+		}
+
+		if m.properties.Filename != nil {
+			prop.Filename = *m.properties.Filename
+		}
+
+		j, err := json.Marshal(prop)
+		if err != nil {
+			ctx.Errorf("json marshal to %q failed: %#v", propOut, err)
+			return
+		}
+		snapshotOutputs = append(snapshotOutputs, snapshot.WriteStringToFileRule(ctx, string(j), propOut))
+
+		if len(m.EffectiveLicenseFiles()) > 0 {
+			noticeName := ctx.ModuleName(m) + ".txt"
+			noticeOut := filepath.Join(noticeDir, noticeName)
+			// skip already copied notice file
+			if !installedNotices[noticeOut] {
+				installedNotices[noticeOut] = true
+
+				noticeOutPath := android.PathForOutput(ctx, noticeOut)
+				ctx.Build(pctx, android.BuildParams{
+					Rule:        android.Cat,
+					Inputs:      m.EffectiveLicenseFiles(),
+					Output:      noticeOutPath,
+					Description: "combine notices for " + noticeOut,
+				})
+				snapshotOutputs = append(snapshotOutputs, noticeOutPath)
+			}
+		}
+
+	})
+
+	return snapshotOutputs
+}
+
+// For Bazel / bp2build
+
+type bazelPrebuiltEtcAttributes struct {
+	Src         bazel.LabelAttribute
+	Filename    string
+	Sub_dir     string
+	Installable bazel.BoolAttribute
+}
+
+func PrebuiltEtcBp2Build(ctx android.TopDownMutatorContext) {
+	module, ok := ctx.Module().(*PrebuiltEtc)
+	if !ok {
+		// Not an prebuilt_etc
+		return
+	}
+	if !module.ConvertWithBp2build(ctx) {
+		return
+	}
+	if ctx.ModuleType() != "prebuilt_etc" {
+		return
+	}
+
+	prebuiltEtcBp2BuildInternal(ctx, module)
+}
+
+func prebuiltEtcBp2BuildInternal(ctx android.TopDownMutatorContext, module *PrebuiltEtc) {
+	var srcLabelAttribute bazel.LabelAttribute
+	if module.properties.Src != nil {
+		srcLabelAttribute.SetValue(android.BazelLabelForModuleSrcSingle(ctx, *module.properties.Src))
+	}
+
+	var filename string
+	if module.properties.Filename != nil {
+		filename = *module.properties.Filename
+	}
+
+	var subDir string
+	if module.subdirProperties.Sub_dir != nil {
+		subDir = *module.subdirProperties.Sub_dir
+	}
+
+	var installableBoolAttribute bazel.BoolAttribute
+	if module.properties.Installable != nil {
+		installableBoolAttribute.Value = module.properties.Installable
+	}
+
+	attrs := &bazelPrebuiltEtcAttributes{
+		Src:         srcLabelAttribute,
+		Filename:    filename,
+		Sub_dir:     subDir,
+		Installable: installableBoolAttribute,
+	}
+
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class:        "prebuilt_etc",
+		Bzl_load_location: "//build/bazel/rules:prebuilt_etc.bzl",
+	}
+
+	ctx.CreateBazelTargetModule(module.Name(), props, attrs)
+}
diff --git a/etc/prebuilt_etc_test.go b/etc/prebuilt_etc_test.go
index 354f6bb..cf1f6d7 100644
--- a/etc/prebuilt_etc_test.go
+++ b/etc/prebuilt_etc_test.go
@@ -15,11 +15,15 @@
 package etc
 
 import (
+	"fmt"
 	"os"
 	"path/filepath"
 	"testing"
 
+	"github.com/google/blueprint/proptools"
+
 	"android/soong/android"
+	"android/soong/snapshot"
 )
 
 func TestMain(m *testing.M) {
@@ -36,6 +40,18 @@
 	}),
 )
 
+var prepareForPrebuiltEtcSnapshotTest = android.GroupFixturePreparers(
+	prepareForPrebuiltEtcTest,
+	android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+		snapshot.VendorSnapshotImageSingleton.Init(ctx)
+		snapshot.RecoverySnapshotImageSingleton.Init(ctx)
+	}),
+	android.FixtureModifyConfig(func(config android.Config) {
+		config.TestProductVariables.DeviceVndkVersion = proptools.StringPtr("current")
+		config.TestProductVariables.RecoverySnapshotVersion = proptools.StringPtr("current")
+	}),
+)
+
 func TestPrebuiltEtcVariants(t *testing.T) {
 	result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
 		prebuilt_etc {
@@ -172,7 +188,7 @@
 		}
 	`)
 
-	buildOS := android.BuildOs.String()
+	buildOS := result.Config.BuildOS.String()
 	p := result.Module("foo.conf", buildOS+"_common").(*PrebuiltEtc)
 	if !p.Host() {
 		t.Errorf("host bit is not set for a prebuilt_etc_host module.")
@@ -226,7 +242,7 @@
 		}
 	`)
 
-	buildOS := android.BuildOs.String()
+	buildOS := result.Config.BuildOS.String()
 	p := result.Module("foo.conf", buildOS+"_common").(*PrebuiltEtc)
 	expected := filepath.Join("out/soong/host", result.Config.PrebuiltOS(), "usr", "share", "bar")
 	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath)
@@ -346,3 +362,110 @@
 		})
 	}
 }
+
+func checkIfSnapshotTaken(t *testing.T, result *android.TestResult, image string, moduleName string) {
+	checkIfSnapshotExistAsExpected(t, result, image, moduleName, true)
+}
+
+func checkIfSnapshotNotTaken(t *testing.T, result *android.TestResult, image string, moduleName string) {
+	checkIfSnapshotExistAsExpected(t, result, image, moduleName, false)
+}
+
+func checkIfSnapshotExistAsExpected(t *testing.T, result *android.TestResult, image string, moduleName string, expectToExist bool) {
+	snapshotSingleton := result.SingletonForTests(image + "-snapshot")
+	archType := "arm64"
+	archVariant := "armv8-a"
+	archDir := fmt.Sprintf("arch-%s", archType)
+
+	snapshotDir := fmt.Sprintf("%s-snapshot", image)
+	snapshotVariantPath := filepath.Join(snapshotDir, archType)
+	outputDir := filepath.Join(snapshotVariantPath, archDir, "etc")
+	imageVariant := ""
+	if image == "recovery" {
+		imageVariant = "recovery_"
+	}
+	mod := result.ModuleForTests(moduleName, fmt.Sprintf("android_%s%s_%s", imageVariant, archType, archVariant))
+	outputFiles := mod.OutputFiles(t, "")
+	if len(outputFiles) != 1 {
+		t.Errorf("%q must have single output\n", moduleName)
+		return
+	}
+	snapshotPath := filepath.Join(outputDir, moduleName)
+
+	if expectToExist {
+		out := snapshotSingleton.Output(snapshotPath)
+
+		if out.Input.String() != outputFiles[0].String() {
+			t.Errorf("The input of snapshot %q must be %q, but %q", "prebuilt_vendor", out.Input.String(), outputFiles[0])
+		}
+
+		snapshotJsonPath := snapshotPath + ".json"
+
+		if snapshotSingleton.MaybeOutput(snapshotJsonPath).Rule == nil {
+			t.Errorf("%q expected but not found", snapshotJsonPath)
+		}
+	} else {
+		out := snapshotSingleton.MaybeOutput(snapshotPath)
+		if out.Rule != nil {
+			t.Errorf("There must be no rule for module %q output file %q", moduleName, outputFiles[0])
+		}
+	}
+}
+
+func TestPrebuiltTakeSnapshot(t *testing.T) {
+	var testBp = `
+	prebuilt_etc {
+		name: "prebuilt_vendor",
+		src: "foo.conf",
+		vendor: true,
+	}
+
+	prebuilt_etc {
+		name: "prebuilt_vendor_indirect",
+		src: "foo.conf",
+		vendor: true,
+	}
+
+	prebuilt_etc {
+		name: "prebuilt_recovery",
+		src: "bar.conf",
+		recovery: true,
+	}
+
+	prebuilt_etc {
+		name: "prebuilt_recovery_indirect",
+		src: "bar.conf",
+		recovery: true,
+	}
+	`
+
+	t.Run("prebuilt: vendor and recovery snapshot", func(t *testing.T) {
+		result := prepareForPrebuiltEtcSnapshotTest.RunTestWithBp(t, testBp)
+
+		checkIfSnapshotTaken(t, result, "vendor", "prebuilt_vendor")
+		checkIfSnapshotTaken(t, result, "vendor", "prebuilt_vendor_indirect")
+		checkIfSnapshotTaken(t, result, "recovery", "prebuilt_recovery")
+		checkIfSnapshotTaken(t, result, "recovery", "prebuilt_recovery_indirect")
+	})
+
+	t.Run("prebuilt: directed snapshot", func(t *testing.T) {
+		prepareForPrebuiltEtcDirectedSnapshotTest := android.GroupFixturePreparers(
+			prepareForPrebuiltEtcSnapshotTest,
+			android.FixtureModifyConfig(func(config android.Config) {
+				config.TestProductVariables.DirectedVendorSnapshot = true
+				config.TestProductVariables.VendorSnapshotModules = make(map[string]bool)
+				config.TestProductVariables.VendorSnapshotModules["prebuilt_vendor"] = true
+				config.TestProductVariables.DirectedRecoverySnapshot = true
+				config.TestProductVariables.RecoverySnapshotModules = make(map[string]bool)
+				config.TestProductVariables.RecoverySnapshotModules["prebuilt_recovery"] = true
+			}),
+		)
+
+		result := prepareForPrebuiltEtcDirectedSnapshotTest.RunTestWithBp(t, testBp)
+
+		checkIfSnapshotTaken(t, result, "vendor", "prebuilt_vendor")
+		checkIfSnapshotNotTaken(t, result, "vendor", "prebuilt_vendor_indirect")
+		checkIfSnapshotTaken(t, result, "recovery", "prebuilt_recovery")
+		checkIfSnapshotNotTaken(t, result, "recovery", "prebuilt_recovery_indirect")
+	})
+}
diff --git a/etc/snapshot_etc.go b/etc/snapshot_etc.go
new file mode 100644
index 0000000..9a25d5a
--- /dev/null
+++ b/etc/snapshot_etc.go
@@ -0,0 +1,186 @@
+// Copyright 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package etc
+
+// This file implements snapshot module of 'prebuilt_etc' type
+// 'snapshot_etc' module defines android.PrebuiltInterface so it can be handled
+// as prebuilt of 'prebuilt_etc' type.
+// Properties of 'snapshot_etc' follows properties from snapshotJsonFlags type
+
+import (
+	"android/soong/android"
+	"fmt"
+	"strings"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+)
+
+func RegisterSnapshotEtcModule(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("snapshot_etc", SnapshotEtcFactory)
+}
+
+func init() {
+	RegisterSnapshotEtcModule(android.InitRegistrationContext)
+}
+
+// snapshot_etc is a prebuilt module type to be installed under etc which is auto-generated by
+// development/vendor_snapshot/update.py. This module will override prebuilt_etc module with same
+// name when 'prefer' property is true.
+func SnapshotEtcFactory() android.Module {
+	module := &SnapshotEtc{}
+	module.AddProperties(&module.properties)
+
+	var srcsSupplier = func(_ android.BaseModuleContext, prebuilt android.Module) []string {
+		s, ok := prebuilt.(*SnapshotEtc)
+		if !ok || s.properties.Src == nil {
+			return []string{}
+		}
+
+		return []string{*s.properties.Src}
+	}
+
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+	android.InitPrebuiltModuleWithSrcSupplier(module, srcsSupplier, "src")
+	return module
+}
+
+type snapshotEtcProperties struct {
+	Src                   *string `android:"path,arch_variant"` // Source of snapshot_etc file
+	Filename              *string `android:"arch_variant"`      // Target file name when it differs from module name
+	Relative_install_path *string `android:"arch_variant"`      // Relative install path when it should be installed subdirectory of etc
+}
+
+type SnapshotEtc struct {
+	android.ModuleBase
+	prebuilt   android.Prebuilt
+	properties snapshotEtcProperties
+
+	outputFilePath android.OutputPath
+	installDirPath android.InstallPath
+}
+
+func (s *SnapshotEtc) Prebuilt() *android.Prebuilt {
+	return &s.prebuilt
+}
+
+func (s *SnapshotEtc) Name() string {
+	return s.prebuilt.Name(s.BaseModuleName())
+}
+
+func (s *SnapshotEtc) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	if s.properties.Src == nil {
+		ctx.PropertyErrorf("src", "missing prebuilt source file")
+		return
+	}
+
+	sourceFilePath := s.prebuilt.SingleSourcePath(ctx)
+
+	// Determine the output file basename.
+	// If Filename is set, use the name specified by the property.
+	// Otherwise use the module name.
+	filename := proptools.String(s.properties.Filename)
+	if filename == "" {
+		filename = ctx.ModuleName()
+	}
+
+	s.outputFilePath = android.PathForModuleOut(ctx, filename).OutputPath
+
+	if strings.Contains(filename, "/") {
+		ctx.PropertyErrorf("filename", "filename cannot contain separator '/'")
+		return
+	}
+
+	subDir := ""
+	if s.properties.Relative_install_path != nil {
+		subDir = *s.properties.Relative_install_path
+	}
+
+	s.installDirPath = android.PathForModuleInstall(ctx, "etc", subDir)
+
+	// This ensures that outputFilePath has the correct name for others to
+	// use, as the source file may have a different name.
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        android.Cp,
+		Input:       sourceFilePath,
+		Output:      s.outputFilePath,
+		Description: "Install snapshot etc module " + s.BaseModuleName(),
+	})
+
+	ctx.InstallFile(s.installDirPath, s.outputFilePath.Base(), sourceFilePath)
+}
+
+func (p *SnapshotEtc) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{{
+		Class:      "ETC",
+		OutputFile: android.OptionalPathForPath(p.outputFilePath),
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+				entries.SetString("LOCAL_MODULE_TAGS", "optional")
+				entries.SetString("LOCAL_MODULE_PATH", p.installDirPath.ToMakePath().String())
+				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.outputFilePath.Base())
+			},
+		},
+	}}
+}
+
+type snapshotEtcDependencyTag struct {
+	blueprint.DependencyTag
+}
+
+var tag = snapshotEtcDependencyTag{}
+
+func (s *SnapshotEtc) CoreVariantNeeded(ctx android.BaseModuleContext) bool {
+	return !s.ModuleBase.InstallInRecovery() && !s.ModuleBase.InstallInRamdisk() &&
+		!s.ModuleBase.InstallInVendorRamdisk() && !s.ModuleBase.InstallInDebugRamdisk()
+}
+
+func (p *SnapshotEtc) RamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+	return p.ModuleBase.InstallInRamdisk()
+}
+
+func (p *SnapshotEtc) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+	return p.ModuleBase.InstallInVendorRamdisk()
+}
+
+func (p *SnapshotEtc) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
+	return p.ModuleBase.InstallInDebugRamdisk()
+}
+
+func (p *SnapshotEtc) RecoveryVariantNeeded(ctx android.BaseModuleContext) bool {
+	return p.ModuleBase.InstallInRecovery()
+}
+
+func (p *SnapshotEtc) ExtraImageVariations(ctx android.BaseModuleContext) []string {
+	return nil
+}
+
+func (p *SnapshotEtc) SetImageVariation(ctx android.BaseModuleContext, variation string, module android.Module) {
+}
+
+func (p *SnapshotEtc) ImageMutatorBegin(ctx android.BaseModuleContext) {}
+
+func (p *SnapshotEtc) OutputFiles(tag string) (android.Paths, error) {
+	switch tag {
+	case "":
+		return android.Paths{p.outputFilePath}, nil
+	default:
+		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+	}
+
+}
+
+var _ android.PrebuiltInterface = (*SnapshotEtc)(nil)
+var _ android.ImageInterface = (*SnapshotEtc)(nil)
+var _ android.OutputFileProducer = (*SnapshotEtc)(nil)
diff --git a/etc/snapshot_etc_test.go b/etc/snapshot_etc_test.go
new file mode 100644
index 0000000..b9d5504
--- /dev/null
+++ b/etc/snapshot_etc_test.go
@@ -0,0 +1,185 @@
+// Copyright 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package etc
+
+import (
+	"android/soong/android"
+	"testing"
+
+	"github.com/google/blueprint"
+)
+
+var registerSourceModule = func(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("source", newSourceModule)
+}
+
+type sourceModuleProperties struct {
+	Deps []string `android:"path,arch_variant"`
+}
+
+type sourceModule struct {
+	android.ModuleBase
+	android.OverridableModuleBase
+
+	properties                                     sourceModuleProperties
+	dependsOnSourceModule, dependsOnPrebuiltModule bool
+	deps                                           android.Paths
+	src                                            android.Path
+}
+
+func newSourceModule() android.Module {
+	m := &sourceModule{}
+	m.AddProperties(&m.properties)
+	android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibFirst)
+	android.InitOverridableModule(m, nil)
+	return m
+}
+
+func (s *sourceModule) OverridablePropertiesDepsMutator(ctx android.BottomUpMutatorContext) {
+	// s.properties.Deps are annotated with android:path, so they are
+	// automatically added to the dependency by pathDeps mutator
+}
+
+func (s *sourceModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	s.deps = android.PathsForModuleSrc(ctx, s.properties.Deps)
+	s.src = android.PathForModuleSrc(ctx, "source_file")
+}
+
+func (s *sourceModule) Srcs() android.Paths {
+	return android.Paths{s.src}
+}
+
+var prepareForSnapshotEtcTest = android.GroupFixturePreparers(
+	android.PrepareForTestWithArchMutator,
+	android.PrepareForTestWithPrebuilts,
+	PrepareForTestWithPrebuiltEtc,
+	android.FixtureRegisterWithContext(RegisterSnapshotEtcModule),
+	android.FixtureRegisterWithContext(registerSourceModule),
+	android.FixtureMergeMockFs(android.MockFS{
+		"foo.conf": nil,
+		"bar.conf": nil,
+	}),
+)
+
+func TestSnapshotWithFilename(t *testing.T) {
+	var androidBp = `
+	snapshot_etc {
+		name: "etc_module",
+		src: "foo.conf",
+		filename: "bar.conf",
+	}
+	`
+
+	result := prepareForSnapshotEtcTest.RunTestWithBp(t, androidBp)
+	for _, variant := range result.ModuleVariantsForTests("etc_module") {
+		module := result.ModuleForTests("etc_module", variant)
+		s, ok := module.Module().(*SnapshotEtc)
+		if !ok {
+			t.Errorf("Expected snapshot_etc module type")
+		}
+		if s.outputFilePath.Base() != "bar.conf" {
+			t.Errorf("Output file path does not match with specified filename")
+		}
+	}
+}
+
+func TestSnapshotEtcWithOrigin(t *testing.T) {
+	var androidBp = `
+	prebuilt_etc {
+		name: "etc_module",
+		src: "foo.conf",
+	}
+
+	snapshot_etc {
+		name: "etc_module",
+		src: "bar.conf",
+	}
+
+	source {
+		name: "source",
+		deps: [":etc_module"],
+	}
+	`
+
+	result := prepareForSnapshotEtcTest.RunTestWithBp(t, androidBp)
+
+	for _, variant := range result.ModuleVariantsForTests("source") {
+		source := result.ModuleForTests("source", variant)
+
+		result.VisitDirectDeps(source.Module(), func(m blueprint.Module) {
+			if _, ok := m.(*PrebuiltEtc); !ok {
+				t.Errorf("Original prebuilt_etc module expected.")
+			}
+		})
+	}
+}
+
+func TestSnapshotEtcWithOriginAndPrefer(t *testing.T) {
+	var androidBp = `
+	prebuilt_etc {
+		name: "etc_module",
+		src: "foo.conf",
+	}
+
+	snapshot_etc {
+		name: "etc_module",
+		src: "bar.conf",
+		prefer: true,
+	}
+
+	source {
+		name: "source",
+		deps: [":etc_module"],
+	}
+	`
+
+	result := prepareForSnapshotEtcTest.RunTestWithBp(t, androidBp)
+
+	for _, variant := range result.ModuleVariantsForTests("source") {
+		source := result.ModuleForTests("source", variant)
+
+		result.VisitDirectDeps(source.Module(), func(m blueprint.Module) {
+			if _, ok := m.(*SnapshotEtc); !ok {
+				t.Errorf("Preferred snapshot_etc module expected.")
+			}
+		})
+	}
+}
+
+func TestSnapshotEtcWithoutOrigin(t *testing.T) {
+	var androidBp = `
+	snapshot_etc {
+		name: "etc_module",
+		src: "bar.conf",
+	}
+
+	source {
+		name: "source",
+		deps: [":etc_module"],
+	}
+	`
+
+	result := prepareForSnapshotEtcTest.RunTestWithBp(t, androidBp)
+
+	for _, variant := range result.ModuleVariantsForTests("source") {
+		source := result.ModuleForTests("source", variant)
+
+		result.VisitDirectDeps(source.Module(), func(m blueprint.Module) {
+			if _, ok := m.(*SnapshotEtc); !ok {
+				t.Errorf("Only source snapshot_etc module expected.")
+			}
+		})
+	}
+}
diff --git a/filesystem/bootimg.go b/filesystem/bootimg.go
index 29a8a39..73d807d 100644
--- a/filesystem/bootimg.go
+++ b/filesystem/bootimg.go
@@ -60,8 +60,8 @@
 	// https://source.android.com/devices/bootloader/partitions/vendor-boot-partitions
 	Vendor_boot *bool
 
-	// Optional kernel commandline
-	Cmdline *string `android:"arch_variant"`
+	// Optional kernel commandline arguments
+	Cmdline []string `android:"arch_variant"`
 
 	// File that contains bootconfig parameters. This can be set only when `vendor_boot` is true
 	// and `header_version` is greater than or equal to 4.
@@ -152,7 +152,7 @@
 	dtb := android.PathForModuleSrc(ctx, dtbName)
 	cmd.FlagWithInput("--dtb ", dtb)
 
-	cmdline := proptools.String(b.properties.Cmdline)
+	cmdline := strings.Join(b.properties.Cmdline, " ")
 	if cmdline != "" {
 		flag := "--cmdline "
 		if vendor {
diff --git a/fuzz/Android.bp b/fuzz/Android.bp
new file mode 100644
index 0000000..9d96e48
--- /dev/null
+++ b/fuzz/Android.bp
@@ -0,0 +1,15 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+    name: "soong-fuzz",
+    pkgPath: "android/soong/fuzz",
+    deps: [
+        "soong-android",
+    ],
+    srcs: [
+        "fuzz_common.go",
+    ],
+    pluginFor: ["soong_build"],
+}
diff --git a/fuzz/fuzz_common.go b/fuzz/fuzz_common.go
new file mode 100644
index 0000000..ccadc0f
--- /dev/null
+++ b/fuzz/fuzz_common.go
@@ -0,0 +1,253 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package fuzz
+
+// This file contains the common code for compiling C/C++ and Rust fuzzers for Android.
+
+import (
+	"encoding/json"
+	"sort"
+	"strings"
+
+	"github.com/google/blueprint/proptools"
+
+	"android/soong/android"
+)
+
+type Lang string
+
+const (
+	Cc   Lang = ""
+	Rust Lang = "rust"
+)
+
+var BoolDefault = proptools.BoolDefault
+
+type FuzzModule struct {
+	android.ModuleBase
+	android.DefaultableModuleBase
+	android.ApexModuleBase
+}
+
+type FuzzPackager struct {
+	Packages    android.Paths
+	FuzzTargets map[string]bool
+}
+
+type FileToZip struct {
+	SourceFilePath        android.Path
+	DestinationPathPrefix string
+}
+
+type ArchOs struct {
+	HostOrTarget string
+	Arch         string
+	Dir          string
+}
+
+type FuzzConfig struct {
+	// Email address of people to CC on bugs or contact about this fuzz target.
+	Cc []string `json:"cc,omitempty"`
+	// Specify whether to enable continuous fuzzing on devices. Defaults to true.
+	Fuzz_on_haiku_device *bool `json:"fuzz_on_haiku_device,omitempty"`
+	// Specify whether to enable continuous fuzzing on host. Defaults to true.
+	Fuzz_on_haiku_host *bool `json:"fuzz_on_haiku_host,omitempty"`
+	// Component in Google's bug tracking system that bugs should be filed to.
+	Componentid *int64 `json:"componentid,omitempty"`
+	// Hotlists in Google's bug tracking system that bugs should be marked with.
+	Hotlists []string `json:"hotlists,omitempty"`
+	// Specify whether this fuzz target was submitted by a researcher. Defaults
+	// to false.
+	Researcher_submitted *bool `json:"researcher_submitted,omitempty"`
+	// Specify who should be acknowledged for CVEs in the Android Security
+	// Bulletin.
+	Acknowledgement []string `json:"acknowledgement,omitempty"`
+	// Additional options to be passed to libfuzzer when run in Haiku.
+	Libfuzzer_options []string `json:"libfuzzer_options,omitempty"`
+	// Additional options to be passed to HWASAN when running on-device in Haiku.
+	Hwasan_options []string `json:"hwasan_options,omitempty"`
+	// Additional options to be passed to HWASAN when running on host in Haiku.
+	Asan_options []string `json:"asan_options,omitempty"`
+}
+
+type FuzzProperties struct {
+	// Optional list of seed files to be installed to the fuzz target's output
+	// directory.
+	Corpus []string `android:"path"`
+	// Optional list of data files to be installed to the fuzz target's output
+	// directory. Directory structure relative to the module is preserved.
+	Data []string `android:"path"`
+	// Optional dictionary to be installed to the fuzz target's output directory.
+	Dictionary *string `android:"path"`
+	// Config for running the target on fuzzing infrastructure.
+	Fuzz_config *FuzzConfig
+}
+
+type FuzzPackagedModule struct {
+	FuzzProperties        FuzzProperties
+	Dictionary            android.Path
+	Corpus                android.Paths
+	CorpusIntermediateDir android.Path
+	Config                android.Path
+	Data                  android.Paths
+	DataIntermediateDir   android.Path
+}
+
+func IsValid(fuzzModule FuzzModule) bool {
+	// Discard ramdisk + vendor_ramdisk + recovery modules, they're duplicates of
+	// fuzz targets we're going to package anyway.
+	if !fuzzModule.Enabled() || fuzzModule.InRamdisk() || fuzzModule.InVendorRamdisk() || fuzzModule.InRecovery() {
+		return false
+	}
+
+	// Discard modules that are in an unavailable namespace.
+	if !fuzzModule.ExportedToMake() {
+		return false
+	}
+
+	return true
+}
+
+func (s *FuzzPackager) PackageArtifacts(ctx android.SingletonContext, module android.Module, fuzzModule FuzzPackagedModule, archDir android.OutputPath, builder *android.RuleBuilder) []FileToZip {
+	// Package the corpora into a zipfile.
+	var files []FileToZip
+	if fuzzModule.Corpus != nil {
+		corpusZip := archDir.Join(ctx, module.Name()+"_seed_corpus.zip")
+		command := builder.Command().BuiltTool("soong_zip").
+			Flag("-j").
+			FlagWithOutput("-o ", corpusZip)
+		rspFile := corpusZip.ReplaceExtension(ctx, "rsp")
+		command.FlagWithRspFileInputList("-r ", rspFile, fuzzModule.Corpus)
+		files = append(files, FileToZip{corpusZip, ""})
+	}
+
+	// Package the data into a zipfile.
+	if fuzzModule.Data != nil {
+		dataZip := archDir.Join(ctx, module.Name()+"_data.zip")
+		command := builder.Command().BuiltTool("soong_zip").
+			FlagWithOutput("-o ", dataZip)
+		for _, f := range fuzzModule.Data {
+			intermediateDir := strings.TrimSuffix(f.String(), f.Rel())
+			command.FlagWithArg("-C ", intermediateDir)
+			command.FlagWithInput("-f ", f)
+		}
+		files = append(files, FileToZip{dataZip, ""})
+	}
+
+	// The dictionary.
+	if fuzzModule.Dictionary != nil {
+		files = append(files, FileToZip{fuzzModule.Dictionary, ""})
+	}
+
+	// Additional fuzz config.
+	if fuzzModule.Config != nil {
+		files = append(files, FileToZip{fuzzModule.Config, ""})
+	}
+
+	return files
+}
+
+func (s *FuzzPackager) BuildZipFile(ctx android.SingletonContext, module android.Module, fuzzModule FuzzPackagedModule, files []FileToZip, builder *android.RuleBuilder, archDir android.OutputPath, archString string, hostOrTargetString string, archOs ArchOs, archDirs map[ArchOs][]FileToZip) ([]FileToZip, bool) {
+	fuzzZip := archDir.Join(ctx, module.Name()+".zip")
+
+	command := builder.Command().BuiltTool("soong_zip").
+		Flag("-j").
+		FlagWithOutput("-o ", fuzzZip)
+
+	for _, file := range files {
+		if file.DestinationPathPrefix != "" {
+			command.FlagWithArg("-P ", file.DestinationPathPrefix)
+		} else {
+			command.Flag("-P ''")
+		}
+		command.FlagWithInput("-f ", file.SourceFilePath)
+	}
+
+	builder.Build("create-"+fuzzZip.String(),
+		"Package "+module.Name()+" for "+archString+"-"+hostOrTargetString)
+
+	// Don't add modules to 'make haiku-rust' that are set to not be
+	// exported to the fuzzing infrastructure.
+	if config := fuzzModule.FuzzProperties.Fuzz_config; config != nil {
+		if strings.Contains(hostOrTargetString, "host") && !BoolDefault(config.Fuzz_on_haiku_host, true) {
+			return archDirs[archOs], false
+		} else if !BoolDefault(config.Fuzz_on_haiku_device, true) {
+			return archDirs[archOs], false
+		}
+	}
+
+	s.FuzzTargets[module.Name()] = true
+	archDirs[archOs] = append(archDirs[archOs], FileToZip{fuzzZip, ""})
+
+	return archDirs[archOs], true
+}
+
+func (f *FuzzConfig) String() string {
+	b, err := json.Marshal(f)
+	if err != nil {
+		panic(err)
+	}
+
+	return string(b)
+}
+
+func (s *FuzzPackager) CreateFuzzPackage(ctx android.SingletonContext, archDirs map[ArchOs][]FileToZip, lang Lang, pctx android.PackageContext) {
+	var archOsList []ArchOs
+	for archOs := range archDirs {
+		archOsList = append(archOsList, archOs)
+	}
+	sort.Slice(archOsList, func(i, j int) bool { return archOsList[i].Dir < archOsList[j].Dir })
+
+	for _, archOs := range archOsList {
+		filesToZip := archDirs[archOs]
+		arch := archOs.Arch
+		hostOrTarget := archOs.HostOrTarget
+		builder := android.NewRuleBuilder(pctx, ctx)
+		zipFileName := "fuzz-" + hostOrTarget + "-" + arch + ".zip"
+		if lang == Rust {
+			zipFileName = "fuzz-rust-" + hostOrTarget + "-" + arch + ".zip"
+		}
+		outputFile := android.PathForOutput(ctx, zipFileName)
+
+		s.Packages = append(s.Packages, outputFile)
+
+		command := builder.Command().BuiltTool("soong_zip").
+			Flag("-j").
+			FlagWithOutput("-o ", outputFile).
+			Flag("-L 0") // No need to try and re-compress the zipfiles.
+
+		for _, fileToZip := range filesToZip {
+
+			if fileToZip.DestinationPathPrefix != "" {
+				command.FlagWithArg("-P ", fileToZip.DestinationPathPrefix)
+			} else {
+				command.Flag("-P ''")
+			}
+			command.FlagWithInput("-f ", fileToZip.SourceFilePath)
+
+		}
+		builder.Build("create-fuzz-package-"+arch+"-"+hostOrTarget,
+			"Create fuzz target packages for "+arch+"-"+hostOrTarget)
+	}
+}
+
+func (s *FuzzPackager) PreallocateSlice(ctx android.MakeVarsContext, targets string) {
+	fuzzTargets := make([]string, 0, len(s.FuzzTargets))
+	for target, _ := range s.FuzzTargets {
+		fuzzTargets = append(fuzzTargets, target)
+	}
+	sort.Strings(fuzzTargets)
+	ctx.Strict(targets, strings.Join(fuzzTargets, " "))
+}
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 77dae75..bde6e97 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -68,7 +68,6 @@
 		ctx.BottomUp("genrule_tool_deps", toolDepsMutator).Parallel()
 	})
 
-	android.DepsBp2BuildMutators(RegisterGenruleBp2BuildDeps)
 	android.RegisterBp2BuildMutator("genrule", GenruleBp2Build)
 }
 
@@ -113,16 +112,17 @@
 	label string
 }
 type generatorProperties struct {
-	// The command to run on one or more input files. Cmd supports substitution of a few variables
+	// The command to run on one or more input files. Cmd supports substitution of a few variables.
 	//
 	// Available variables for substitution:
 	//
-	//  $(location): the path to the first entry in tools or tool_files
-	//  $(location <label>): the path to the tool, tool_file, input or output with name <label>
-	//  $(in): one or more input files
-	//  $(out): a single output file
-	//  $(depfile): a file to which dependencies will be written, if the depfile property is set to true
-	//  $(genDir): the sandbox directory for this tool; contains $(out)
+	//  $(location): the path to the first entry in tools or tool_files.
+	//  $(location <label>): the path to the tool, tool_file, input or output with name <label>. Use $(location) if <label> refers to a rule that outputs exactly one file.
+	//  $(locations <label>): the paths to the tools, tool_files, inputs or outputs with name <label>. Use $(locations) if <label> refers to a rule that outputs two or more files.
+	//  $(in): one or more input files.
+	//  $(out): a single output file.
+	//  $(depfile): a file to which dependencies will be written, if the depfile property is set to true.
+	//  $(genDir): the sandbox directory for this tool; contains $(out).
 	//  $$: a literal $
 	Cmd *string
 
@@ -211,6 +211,22 @@
 	return g.outputDeps
 }
 
+func (g *Module) OutputFiles(tag string) (android.Paths, error) {
+	if tag == "" {
+		return append(android.Paths{}, g.outputFiles...), nil
+	}
+	// otherwise, tag should match one of outputs
+	for _, outputFile := range g.outputFiles {
+		if outputFile.Rel() == tag {
+			return android.Paths{outputFile}, nil
+		}
+	}
+	return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+}
+
+var _ android.SourceFileProducer = (*Module)(nil)
+var _ android.OutputFileProducer = (*Module)(nil)
+
 func toolDepsMutator(ctx android.BottomUpMutatorContext) {
 	if g, ok := ctx.Module().(*Module); ok {
 		for _, tool := range g.properties.Tools {
@@ -224,7 +240,7 @@
 }
 
 // Returns true if information was available from Bazel, false if bazel invocation still needs to occur.
-func (c *Module) generateBazelBuildActions(ctx android.ModuleContext, label string) bool {
+func (c *Module) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool {
 	bazelCtx := ctx.Config().BazelContext
 	filePaths, ok := bazelCtx.GetOutputFiles(label, ctx.Arch().ArchType)
 	if ok {
@@ -544,7 +560,7 @@
 	bazelModuleLabel := g.GetBazelLabel(ctx, g)
 	bazelActionsUsed := false
 	if g.MixedBuildsEnabled(ctx) {
-		bazelActionsUsed = g.generateBazelBuildActions(ctx, bazelModuleLabel)
+		bazelActionsUsed = g.GenerateBazelBuildActions(ctx, bazelModuleLabel)
 	}
 	if !bazelActionsUsed {
 		// For <= 6 outputs, just embed those directly in the users. Right now, that covers >90% of
@@ -810,18 +826,6 @@
 	Cmd   string
 }
 
-type bazelGenrule struct {
-	android.BazelTargetModuleBase
-	bazelGenruleAttributes
-}
-
-func BazelGenruleFactory() android.Module {
-	module := &bazelGenrule{}
-	module.AddProperties(&module.bazelGenruleAttributes)
-	android.InitBazelTargetModule(module)
-	return module
-}
-
 func GenruleBp2Build(ctx android.TopDownMutatorContext) {
 	m, ok := ctx.Module().(*Module)
 	if !ok || !m.ConvertWithBp2build(ctx) {
@@ -888,15 +892,9 @@
 	}
 
 	// Create the BazelTargetModule.
-	ctx.CreateBazelTargetModule(BazelGenruleFactory, m.Name(), props, attrs)
+	ctx.CreateBazelTargetModule(m.Name(), props, attrs)
 }
 
-func (m *bazelGenrule) Name() string {
-	return m.BaseModuleName()
-}
-
-func (m *bazelGenrule) GenerateAndroidBuildActions(ctx android.ModuleContext) {}
-
 var Bool = proptools.Bool
 var String = proptools.String
 
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index 3ce4f85..714d2f8 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -31,12 +31,12 @@
 var prepareForGenRuleTest = android.GroupFixturePreparers(
 	android.PrepareForTestWithArchMutator,
 	android.PrepareForTestWithDefaults,
-
 	android.PrepareForTestWithFilegroup,
 	PrepareForTestWithGenRuleBuildComponents,
 	android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
 		ctx.RegisterModuleType("tool", toolFactory)
 		ctx.RegisterModuleType("output", outputProducerFactory)
+		ctx.RegisterModuleType("use_source", useSourceFactory)
 	}),
 	android.FixtureMergeMockFs(android.MockFS{
 		"tool":       nil,
@@ -684,6 +684,42 @@
 	}
 }
 
+func TestGenruleOutputFiles(t *testing.T) {
+	bp := `
+				genrule {
+					name: "gen",
+					out: ["foo", "sub/bar"],
+					cmd: "echo foo > $(location foo) && echo bar > $(location sub/bar)",
+				}
+				use_source {
+					name: "gen_foo",
+					srcs: [":gen{foo}"],
+				}
+				use_source {
+					name: "gen_bar",
+					srcs: [":gen{sub/bar}"],
+				}
+				use_source {
+					name: "gen_all",
+					srcs: [":gen"],
+				}
+			`
+
+	result := prepareForGenRuleTest.RunTestWithBp(t, testGenruleBp()+bp)
+	android.AssertPathsRelativeToTopEquals(t,
+		"genrule.tag with output",
+		[]string{"out/soong/.intermediates/gen/gen/foo"},
+		result.ModuleForTests("gen_foo", "").Module().(*useSource).srcs)
+	android.AssertPathsRelativeToTopEquals(t,
+		"genrule.tag with output in subdir",
+		[]string{"out/soong/.intermediates/gen/gen/sub/bar"},
+		result.ModuleForTests("gen_bar", "").Module().(*useSource).srcs)
+	android.AssertPathsRelativeToTopEquals(t,
+		"genrule.tag with all",
+		[]string{"out/soong/.intermediates/gen/gen/foo", "out/soong/.intermediates/gen/gen/sub/bar"},
+		result.ModuleForTests("gen_all", "").Module().(*useSource).srcs)
+}
+
 func TestGenruleWithBazel(t *testing.T) {
 	bp := `
 		genrule {
@@ -750,3 +786,22 @@
 }
 
 var _ android.OutputFileProducer = (*testOutputProducer)(nil)
+
+type useSource struct {
+	android.ModuleBase
+	props struct {
+		Srcs []string `android:"path"`
+	}
+	srcs android.Paths
+}
+
+func (s *useSource) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	s.srcs = android.PathsForModuleSrc(ctx, s.props.Srcs)
+}
+
+func useSourceFactory() android.Module {
+	module := &useSource{}
+	module.AddProperties(&module.props)
+	android.InitAndroidModule(module)
+	return module
+}
diff --git a/go.mod b/go.mod
index 7297dea..14444b3 100644
--- a/go.mod
+++ b/go.mod
@@ -1,11 +1,19 @@
 module android/soong
 
-require github.com/golang/protobuf v0.0.0
+require google.golang.org/protobuf v0.0.0
 
 require github.com/google/blueprint v0.0.0
 
-replace github.com/golang/protobuf v0.0.0 => ../../external/golang-protobuf
+replace google.golang.org/protobuf v0.0.0 => ../../external/golang-protobuf
 
 replace github.com/google/blueprint v0.0.0 => ../blueprint
 
+// Indirect deps from golang-protobuf
+exclude github.com/golang/protobuf v1.5.0
+
+replace github.com/google/go-cmp v0.5.5 => ../../external/go-cmp
+
+// Indirect dep from go-cmp
+exclude golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
+
 go 1.15
diff --git a/java/Android.bp b/java/Android.bp
index 5952602..9ffa123 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -10,6 +10,7 @@
         "blueprint-pathtools",
         "soong",
         "soong-android",
+        "soong-bazel",
         "soong-cc",
         "soong-dexpreopt",
         "soong-genrule",
@@ -93,6 +94,7 @@
         "plugin_test.go",
         "rro_test.go",
         "sdk_test.go",
+        "sdk_library_test.go",
         "system_modules_test.go",
         "systemserver_classpath_fragment_test.go",
     ],
diff --git a/java/OWNERS b/java/OWNERS
index 16ef4d8..5242712 100644
--- a/java/OWNERS
+++ b/java/OWNERS
@@ -1 +1 @@
-per-file dexpreopt*.go = ngeoffray@google.com,calin@google.com,mathieuc@google.com
+per-file dexpreopt*.go = ngeoffray@google.com,calin@google.com,skvadrik@google.com
diff --git a/java/aar.go b/java/aar.go
index 04727e4..afbaea2 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -502,11 +502,12 @@
 	if sdkDep.hasFrameworkLibs() {
 		a.aapt.deps(ctx, sdkDep)
 	}
+	a.usesLibrary.deps(ctx, sdkDep.hasFrameworkLibs())
 }
 
 func (a *AndroidLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	a.aapt.isLibrary = true
-	a.classLoaderContexts = make(dexpreopt.ClassLoaderContextMap)
+	a.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
 	a.aapt.buildActions(ctx, android.SdkContext(a), a.classLoaderContexts)
 
 	a.hideApexVariantFromMake = !ctx.Provider(android.ApexInfoProvider).(android.ApexInfo).IsForPlatform()
diff --git a/java/android_manifest.go b/java/android_manifest.go
index 331f941..38065f1 100644
--- a/java/android_manifest.go
+++ b/java/android_manifest.go
@@ -71,12 +71,14 @@
 		args = append(args, "--use-embedded-dex")
 	}
 
-	for _, usesLib := range classLoaderContexts.UsesLibs() {
-		if inList(usesLib, dexpreopt.OptionalCompatUsesLibs) {
-			args = append(args, "--optional-uses-library", usesLib)
-		} else {
-			args = append(args, "--uses-library", usesLib)
-		}
+	// manifest_fixer should add only the implicit SDK libraries inferred by Soong, not those added
+	// explicitly via `uses_libs`/`optional_uses_libs`.
+	requiredUsesLibs, optionalUsesLibs := classLoaderContexts.ImplicitUsesLibs()
+	for _, usesLib := range requiredUsesLibs {
+		args = append(args, "--uses-library", usesLib)
+	}
+	for _, usesLib := range optionalUsesLibs {
+		args = append(args, "--optional-uses-library", usesLib)
 	}
 
 	if hasNoCode {
diff --git a/java/androidmk.go b/java/androidmk.go
index 04357e0..68ccd82 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -114,7 +114,8 @@
 						entries.SetPath("LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR", library.jacocoReportClassesFile)
 					}
 
-					entries.AddStrings("LOCAL_EXPORT_SDK_LIBRARIES", library.classLoaderContexts.UsesLibs()...)
+					requiredUsesLibs, optionalUsesLibs := library.classLoaderContexts.UsesLibs()
+					entries.AddStrings("LOCAL_EXPORT_SDK_LIBRARIES", append(requiredUsesLibs, optionalUsesLibs...)...)
 
 					if len(library.additionalCheckedModules) != 0 {
 						entries.AddStrings("LOCAL_ADDITIONAL_CHECKED_MODULE", library.additionalCheckedModules.Strings()...)
diff --git a/java/app.go b/java/app.go
index fc1ace0..5104f07 100755
--- a/java/app.go
+++ b/java/app.go
@@ -26,6 +26,7 @@
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
+	"android/soong/bazel"
 	"android/soong/cc"
 	"android/soong/dexpreopt"
 	"android/soong/tradefed"
@@ -42,6 +43,8 @@
 	ctx.RegisterModuleType("android_app_certificate", AndroidAppCertificateFactory)
 	ctx.RegisterModuleType("override_android_app", OverrideAndroidAppModuleFactory)
 	ctx.RegisterModuleType("override_android_test", OverrideAndroidTestModuleFactory)
+
+	android.RegisterBp2BuildMutator("android_app_certificate", AndroidAppCertificateBp2Build)
 }
 
 // AndroidManifest.xml merging
@@ -646,8 +649,12 @@
 	a.usesLibrary.freezeEnforceUsesLibraries()
 
 	// Add implicit SDK libraries to <uses-library> list.
-	for _, usesLib := range a.classLoaderContexts.UsesLibs() {
-		a.usesLibrary.addLib(usesLib, inList(usesLib, dexpreopt.OptionalCompatUsesLibs))
+	requiredUsesLibs, optionalUsesLibs := a.classLoaderContexts.UsesLibs()
+	for _, usesLib := range requiredUsesLibs {
+		a.usesLibrary.addLib(usesLib, false)
+	}
+	for _, usesLib := range optionalUsesLibs {
+		a.usesLibrary.addLib(usesLib, true)
 	}
 
 	// Check that the <uses-library> list is coherent with the manifest.
@@ -1104,6 +1111,8 @@
 
 type AndroidAppCertificate struct {
 	android.ModuleBase
+	android.BazelModuleBase
+
 	properties  AndroidAppCertificateProperties
 	Certificate Certificate
 }
@@ -1119,6 +1128,7 @@
 	module := &AndroidAppCertificate{}
 	module.AddProperties(&module.properties)
 	android.InitAndroidModule(module)
+	android.InitBazelModule(module)
 	return module
 }
 
@@ -1213,18 +1223,29 @@
 }
 
 func (u *usesLibrary) deps(ctx android.BottomUpMutatorContext, hasFrameworkLibs bool) {
-	if !ctx.Config().UnbundledBuild() {
-		ctx.AddVariationDependencies(nil, usesLibTag, u.usesLibraryProperties.Uses_libs...)
-		ctx.AddVariationDependencies(nil, usesLibTag, u.presentOptionalUsesLibs(ctx)...)
+	if !ctx.Config().UnbundledBuild() || ctx.Config().UnbundledBuildImage() {
+		reqTag := makeUsesLibraryDependencyTag(dexpreopt.AnySdkVersion, false, false)
+		ctx.AddVariationDependencies(nil, reqTag, u.usesLibraryProperties.Uses_libs...)
+
+		optTag := makeUsesLibraryDependencyTag(dexpreopt.AnySdkVersion, true, false)
+		ctx.AddVariationDependencies(nil, optTag, u.presentOptionalUsesLibs(ctx)...)
+
 		// Only add these extra dependencies if the module depends on framework libs. This avoids
 		// creating a cyclic dependency:
 		//     e.g. framework-res -> org.apache.http.legacy -> ... -> framework-res.
 		if hasFrameworkLibs {
-			// Dexpreopt needs paths to the dex jars of these libraries in order to construct
-			// class loader context for dex2oat. Add them as a dependency with a special tag.
-			ctx.AddVariationDependencies(nil, usesLibCompat29Tag, dexpreopt.CompatUsesLibs29...)
-			ctx.AddVariationDependencies(nil, usesLibCompat28Tag, dexpreopt.OptionalCompatUsesLibs28...)
-			ctx.AddVariationDependencies(nil, usesLibCompat30Tag, dexpreopt.OptionalCompatUsesLibs30...)
+			// Add implicit <uses-library> dependencies on compatibility libraries. Some of them are
+			// optional, and some required --- this depends on the most common usage of the library
+			// and may be wrong for some apps (they need explicit `uses_libs`/`optional_uses_libs`).
+
+			compat28OptTag := makeUsesLibraryDependencyTag(28, true, true)
+			ctx.AddVariationDependencies(nil, compat28OptTag, dexpreopt.OptionalCompatUsesLibs28...)
+
+			compat29ReqTag := makeUsesLibraryDependencyTag(29, false, true)
+			ctx.AddVariationDependencies(nil, compat29ReqTag, dexpreopt.CompatUsesLibs29...)
+
+			compat30OptTag := makeUsesLibraryDependencyTag(30, true, true)
+			ctx.AddVariationDependencies(nil, compat30OptTag, dexpreopt.OptionalCompatUsesLibs30...)
 		}
 	}
 }
@@ -1245,36 +1266,52 @@
 	}
 }
 
-// Returns a map of module names of shared library dependencies to the paths
-// to their dex jars on host and on device.
+// Returns a map of module names of shared library dependencies to the paths to their dex jars on
+// host and on device.
 func (u *usesLibrary) classLoaderContextForUsesLibDeps(ctx android.ModuleContext) dexpreopt.ClassLoaderContextMap {
 	clcMap := make(dexpreopt.ClassLoaderContextMap)
 
-	if !ctx.Config().UnbundledBuild() {
-		ctx.VisitDirectDeps(func(m android.Module) {
-			if tag, ok := ctx.OtherModuleDependencyTag(m).(usesLibraryDependencyTag); ok {
-				dep := ctx.OtherModuleName(m)
-				if lib, ok := m.(UsesLibraryDependency); ok {
-					libName := dep
-					if ulib, ok := m.(ProvidesUsesLib); ok && ulib.ProvidesUsesLib() != nil {
-						libName = *ulib.ProvidesUsesLib()
-						// Replace module name with library name in `uses_libs`/`optional_uses_libs`
-						// in order to pass verify_uses_libraries check (which compares these
-						// properties against library names written in the manifest).
-						replaceInList(u.usesLibraryProperties.Uses_libs, dep, libName)
-						replaceInList(u.usesLibraryProperties.Optional_uses_libs, dep, libName)
-					}
-					clcMap.AddContext(ctx, tag.sdkVersion, libName,
-						lib.DexJarBuildPath(), lib.DexJarInstallPath(), lib.ClassLoaderContexts())
-				} else if ctx.Config().AllowMissingDependencies() {
-					ctx.AddMissingDependencies([]string{dep})
-				} else {
-					ctx.ModuleErrorf("module %q in uses_libs or optional_uses_libs must be a java library", dep)
-				}
-			}
-		})
+	// Skip when UnbundledBuild() is true, but UnbundledBuildImage() is false. With
+	// UnbundledBuildImage() it is necessary to generate dexpreopt.config for post-dexpreopting.
+	if ctx.Config().UnbundledBuild() && !ctx.Config().UnbundledBuildImage() {
+		return clcMap
 	}
 
+	ctx.VisitDirectDeps(func(m android.Module) {
+		tag, isUsesLibTag := ctx.OtherModuleDependencyTag(m).(usesLibraryDependencyTag)
+		if !isUsesLibTag {
+			return
+		}
+
+		dep := android.RemoveOptionalPrebuiltPrefix(ctx.OtherModuleName(m))
+
+		// Skip stub libraries. A dependency on the implementation library has been added earlier,
+		// so it will be added to CLC, but the stub shouldn't be. Stub libraries can be distingushed
+		// from implementation libraries by their name, which is different as it has a suffix.
+		if comp, ok := m.(SdkLibraryComponentDependency); ok {
+			if impl := comp.OptionalSdkLibraryImplementation(); impl != nil && *impl != dep {
+				return
+			}
+		}
+
+		if lib, ok := m.(UsesLibraryDependency); ok {
+			libName := dep
+			if ulib, ok := m.(ProvidesUsesLib); ok && ulib.ProvidesUsesLib() != nil {
+				libName = *ulib.ProvidesUsesLib()
+				// Replace module name with library name in `uses_libs`/`optional_uses_libs` in
+				// order to pass verify_uses_libraries check (which compares these properties
+				// against library names written in the manifest).
+				replaceInList(u.usesLibraryProperties.Uses_libs, dep, libName)
+				replaceInList(u.usesLibraryProperties.Optional_uses_libs, dep, libName)
+			}
+			clcMap.AddContext(ctx, tag.sdkVersion, libName, tag.optional, tag.implicit,
+				lib.DexJarBuildPath(), lib.DexJarInstallPath(), lib.ClassLoaderContexts())
+		} else if ctx.Config().AllowMissingDependencies() {
+			ctx.AddMissingDependencies([]string{dep})
+		} else {
+			ctx.ModuleErrorf("module %q in uses_libs or optional_uses_libs must be a java library", dep)
+		}
+	})
 	return clcMap
 }
 
@@ -1352,3 +1389,43 @@
 	outputFile := android.PathForModuleOut(ctx, "verify_uses_libraries", apk.Base())
 	return outputFile
 }
+
+// For Bazel / bp2build
+
+type bazelAndroidAppCertificateAttributes struct {
+	Certificate string
+}
+
+func AndroidAppCertificateBp2Build(ctx android.TopDownMutatorContext) {
+	module, ok := ctx.Module().(*AndroidAppCertificate)
+	if !ok {
+		// Not an Android app certificate
+		return
+	}
+	if !module.ConvertWithBp2build(ctx) {
+		return
+	}
+	if ctx.ModuleType() != "android_app_certificate" {
+		return
+	}
+
+	androidAppCertificateBp2BuildInternal(ctx, module)
+}
+
+func androidAppCertificateBp2BuildInternal(ctx android.TopDownMutatorContext, module *AndroidAppCertificate) {
+	var certificate string
+	if module.properties.Certificate != nil {
+		certificate = *module.properties.Certificate
+	}
+
+	attrs := &bazelAndroidAppCertificateAttributes{
+		Certificate: certificate,
+	}
+
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class:        "android_app_certificate",
+		Bzl_load_location: "//build/bazel/rules:android_app_certificate.bzl",
+	}
+
+	ctx.CreateBazelTargetModule(module.Name(), props, attrs)
+}
diff --git a/java/app_import.go b/java/app_import.go
index 3371e8e..b5a6084 100644
--- a/java/app_import.go
+++ b/java/app_import.go
@@ -61,7 +61,7 @@
 
 type AndroidAppImportProperties struct {
 	// A prebuilt apk to import
-	Apk *string
+	Apk *string `android:"path"`
 
 	// The name of a certificate in the default certificate directory or an android_app_certificate
 	// module name in the form ":module". Should be empty if presigned or default_dev_cert is set.
@@ -99,6 +99,9 @@
 	// If set, create package-export.apk, which other packages can
 	// use to get PRODUCT-agnostic resource data like IDs and type definitions.
 	Export_package_resources *bool
+
+	// Optional. Install to a subdirectory of the default install path for the module
+	Relative_install_path *string
 }
 
 func (a *AndroidAppImport) IsInstallable() bool {
@@ -263,20 +266,25 @@
 	jnisUncompressed := android.PathForModuleOut(ctx, "jnis-uncompressed", ctx.ModuleName()+".apk")
 	a.uncompressEmbeddedJniLibs(ctx, srcApk, jnisUncompressed.OutputPath)
 
-	var installDir android.InstallPath
+	var pathFragments []string
+	relInstallPath := String(a.properties.Relative_install_path)
 
 	if a.isPrebuiltFrameworkRes() {
 		// framework-res.apk is installed as system/framework/framework-res.apk
-		installDir = android.PathForModuleInstall(ctx, "framework")
+		if relInstallPath != "" {
+			ctx.PropertyErrorf("relative_install_path", "Relative_install_path cannot be set for framework-res")
+		}
+		pathFragments = []string{"framework"}
 		a.preprocessed = true
 	} else if Bool(a.properties.Privileged) {
-		installDir = android.PathForModuleInstall(ctx, "priv-app", a.BaseModuleName())
+		pathFragments = []string{"priv-app", relInstallPath, a.BaseModuleName()}
 	} else if ctx.InstallInTestcases() {
-		installDir = android.PathForModuleInstall(ctx, a.BaseModuleName(), ctx.DeviceConfig().DeviceArch())
+		pathFragments = []string{relInstallPath, a.BaseModuleName(), ctx.DeviceConfig().DeviceArch()}
 	} else {
-		installDir = android.PathForModuleInstall(ctx, "app", a.BaseModuleName())
+		pathFragments = []string{"app", relInstallPath, a.BaseModuleName()}
 	}
 
+	installDir := android.PathForModuleInstall(ctx, pathFragments...)
 	a.dexpreopter.isApp = true
 	a.dexpreopter.installPath = installDir.Join(ctx, a.BaseModuleName()+".apk")
 	a.dexpreopter.isPresignedPrebuilt = Bool(a.properties.Presigned)
diff --git a/java/app_import_test.go b/java/app_import_test.go
index 147ae45..024a3df 100644
--- a/java/app_import_test.go
+++ b/java/app_import_test.go
@@ -493,6 +493,69 @@
 	}
 }
 
+func TestAndroidAppImport_relativeInstallPath(t *testing.T) {
+	bp := `
+		android_app_import {
+			name: "no_relative_install_path",
+			apk: "prebuilts/apk/app.apk",
+			presigned: true,
+		}
+
+		android_app_import {
+			name: "relative_install_path",
+			apk: "prebuilts/apk/app.apk",
+			presigned: true,
+			relative_install_path: "my/path",
+		}
+
+		android_app_import {
+			name: "framework-res",
+			apk: "prebuilts/apk/app.apk",
+			presigned: true,
+			prefer: true,
+		}
+
+		android_app_import {
+			name: "privileged_relative_install_path",
+			apk: "prebuilts/apk/app.apk",
+			presigned: true,
+			privileged: true,
+			relative_install_path: "my/path"
+		}
+		`
+	testCases := []struct {
+		name                string
+		expectedInstallPath string
+		errorMessage        string
+	}{
+		{
+			name:                "no_relative_install_path",
+			expectedInstallPath: "out/soong/target/product/test_device/system/app/no_relative_install_path/no_relative_install_path.apk",
+			errorMessage:        "Install path is not correct when relative_install_path is missing",
+		},
+		{
+			name:                "relative_install_path",
+			expectedInstallPath: "out/soong/target/product/test_device/system/app/my/path/relative_install_path/relative_install_path.apk",
+			errorMessage:        "Install path is not correct for app when relative_install_path is present",
+		},
+		{
+			name:                "prebuilt_framework-res",
+			expectedInstallPath: "out/soong/target/product/test_device/system/framework/framework-res.apk",
+			errorMessage:        "Install path is not correct for framework-res",
+		},
+		{
+			name:                "privileged_relative_install_path",
+			expectedInstallPath: "out/soong/target/product/test_device/system/priv-app/my/path/privileged_relative_install_path/privileged_relative_install_path.apk",
+			errorMessage:        "Install path is not correct for privileged app when relative_install_path is present",
+		},
+	}
+	for _, testCase := range testCases {
+		ctx, _ := testJava(t, bp)
+		mod := ctx.ModuleForTests(testCase.name, "android_common").Module().(*AndroidAppImport)
+		android.AssertPathRelativeToTopEquals(t, testCase.errorMessage, testCase.expectedInstallPath, mod.installPath)
+	}
+}
+
 func TestAndroidTestImport(t *testing.T) {
 	ctx, _ := testJava(t, `
 		android_test_import {
diff --git a/java/app_test.go b/java/app_test.go
index a99ac62..07439fc 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -1737,7 +1737,7 @@
 
 			foo := result.ModuleForTests("foo", "android_common")
 
-			outSoongDir := result.Config.BuildDir()
+			outSoongDir := result.Config.SoongOutDir()
 
 			outputs := foo.AllOutputs()
 			outputMap := make(map[string]bool)
@@ -2285,6 +2285,49 @@
 			sdk_version: "current",
 		}
 
+		java_library {
+			name: "runtime-required-x",
+			srcs: ["a.java"],
+			installable: true,
+			sdk_version: "current",
+		}
+
+		java_library {
+			name: "runtime-optional-x",
+			srcs: ["a.java"],
+			installable: true,
+			sdk_version: "current",
+		}
+
+		android_library {
+			name: "static-x",
+			uses_libs: ["runtime-required-x"],
+			optional_uses_libs: ["runtime-optional-x"],
+			sdk_version: "current",
+		}
+
+		java_library {
+			name: "runtime-required-y",
+			srcs: ["a.java"],
+			installable: true,
+			sdk_version: "current",
+		}
+
+		java_library {
+			name: "runtime-optional-y",
+			srcs: ["a.java"],
+			installable: true,
+			sdk_version: "current",
+		}
+
+		java_library {
+			name: "static-y",
+			srcs: ["a.java"],
+			uses_libs: ["runtime-required-y"],
+			optional_uses_libs: ["runtime-optional-y"],
+			sdk_version: "current",
+		}
+
 		// A library that has to use "provides_uses_lib", because:
 		//    - it is not an SDK library
 		//    - its library name is different from its module name
@@ -2307,6 +2350,8 @@
 				// statically linked component libraries should not pull their SDK libraries,
 				// so "fred" should not be added to class loader context
 				"fred.stubs",
+				"static-x",
+				"static-y",
 			],
 			uses_libs: [
 				"foo",
@@ -2353,9 +2398,6 @@
 	expectManifestFixerArgs := `--extract-native-libs=true ` +
 		`--uses-library qux ` +
 		`--uses-library quuz ` +
-		`--uses-library foo ` + // TODO(b/132357300): "foo" should not be passed to manifest_fixer
-		`--uses-library com.non.sdk.lib ` + // TODO(b/132357300): "com.non.sdk.lib" should not be passed to manifest_fixer
-		`--uses-library bar ` + // TODO(b/132357300): "bar" should not be passed to manifest_fixer
 		`--uses-library runtime-library`
 	android.AssertStringEquals(t, "manifest_fixer args", expectManifestFixerArgs, actualManifestFixerArgs)
 
@@ -2366,8 +2408,12 @@
 		`--uses-library qux ` +
 		`--uses-library quuz ` +
 		`--uses-library runtime-library ` +
+		`--uses-library runtime-required-x ` +
+		`--uses-library runtime-required-y ` +
 		`--optional-uses-library bar ` +
-		`--optional-uses-library baz `
+		`--optional-uses-library baz ` +
+		`--optional-uses-library runtime-optional-x ` +
+		`--optional-uses-library runtime-optional-y `
 	android.AssertStringDoesContain(t, "verify cmd args", verifyCmd, verifyArgs)
 
 	// Test that all libraries are verified for an APK (library order matters).
@@ -2387,7 +2433,11 @@
 		`PCL[/system/framework/foo.jar]#` +
 		`PCL[/system/framework/non-sdk-lib.jar]#` +
 		`PCL[/system/framework/bar.jar]#` +
-		`PCL[/system/framework/runtime-library.jar]`
+		`PCL[/system/framework/runtime-library.jar]#` +
+		`PCL[/system/framework/runtime-required-x.jar]#` +
+		`PCL[/system/framework/runtime-optional-x.jar]#` +
+		`PCL[/system/framework/runtime-required-y.jar]#` +
+		`PCL[/system/framework/runtime-optional-y.jar] `
 	android.AssertStringDoesContain(t, "dexpreopt app cmd args", cmd, w)
 
 	// Test conditional context for target SDK version 28.
@@ -2471,7 +2521,7 @@
 				PrepareForTestWithJavaSdkLibraryFiles,
 				FixtureWithLastReleaseApis("runtime-library", "foo", "bar"),
 				dexpreopt.FixtureSetBootJars("platform:foo"),
-				dexpreopt.FixtureSetUpdatableBootJars("platform:bar"),
+				dexpreopt.FixtureSetApexBootJars("platform:bar"),
 				dexpreopt.FixtureSetPreoptWithUpdatableBcp(test.with),
 			).RunTestWithBp(t, bp)
 
diff --git a/java/base.go b/java/base.go
index df70efb..86022c3 100644
--- a/java/base.go
+++ b/java/base.go
@@ -155,6 +155,13 @@
 
 		// List of java_plugin modules that provide extra errorprone checks.
 		Extra_check_modules []string
+
+		// This property can be in 3 states. When set to true, errorprone will
+		// be run during the regular build. When set to false, errorprone will
+		// never be run. When unset, errorprone will be run when the RUN_ERROR_PRONE
+		// environment variable is true. Setting this to false will improve build
+		// performance more than adding -XepDisableAllChecks in javacflags.
+		Enabled *bool
 	}
 
 	Proto struct {
@@ -598,7 +605,10 @@
 		if dep != nil {
 			if component, ok := dep.(SdkLibraryComponentDependency); ok {
 				if lib := component.OptionalSdkLibraryImplementation(); lib != nil {
-					ctx.AddVariationDependencies(nil, usesLibTag, *lib)
+					// Add library as optional if it's one of the optional compatibility libs.
+					optional := android.InList(*lib, dexpreopt.OptionalCompatUsesLibs)
+					tag := makeUsesLibraryDependencyTag(dexpreopt.AnySdkVersion, optional, true)
+					ctx.AddVariationDependencies(nil, tag, *lib)
 				}
 			}
 		}
@@ -701,7 +711,8 @@
 	// javaVersion flag.
 	flags.javaVersion = getJavaVersion(ctx, String(j.properties.Java_version), android.SdkContext(j))
 
-	if ctx.Config().RunErrorProne() {
+	epEnabled := j.properties.Errorprone.Enabled
+	if (ctx.Config().RunErrorProne() && epEnabled == nil) || Bool(epEnabled) {
 		if config.ErrorProneClasspath == nil && ctx.Config().TestProductVariables == nil {
 			ctx.ModuleErrorf("cannot build with Error Prone, missing external/error_prone?")
 		}
@@ -780,7 +791,7 @@
 			// Manually specify build directory in case it is not under the repo root.
 			// (javac doesn't seem to expand into symbolic links when searching for patch-module targets, so
 			// just adding a symlink under the root doesn't help.)
-			patchPaths := []string{".", ctx.Config().BuildDir()}
+			patchPaths := []string{".", ctx.Config().SoongOutDir()}
 
 			// b/150878007
 			//
@@ -862,6 +873,7 @@
 	if aaptSrcJar != nil {
 		srcJars = append(srcJars, aaptSrcJar)
 	}
+	srcFiles = srcFiles.FilterOutByExt(".srcjar")
 
 	if j.properties.Jarjar_rules != nil {
 		j.expandJarjarRules = android.PathForModuleSrc(ctx, *j.properties.Jarjar_rules)
@@ -889,8 +901,8 @@
 		kotlincFlags := j.properties.Kotlincflags
 		CheckKotlincFlags(ctx, kotlincFlags)
 
-		// Dogfood the JVM_IR backend.
-		kotlincFlags = append(kotlincFlags, "-Xuse-ir")
+		// Workaround for KT-46512
+		kotlincFlags = append(kotlincFlags, "-Xsam-conversions=class")
 
 		// If there are kotlin files, compile them first but pass all the kotlin and java files
 		// kotlinc will use the java files to resolve types referenced by the kotlin files, but
@@ -972,14 +984,23 @@
 	}
 	if len(uniqueSrcFiles) > 0 || len(srcJars) > 0 {
 		var extraJarDeps android.Paths
-		if ctx.Config().RunErrorProne() {
-			// If error-prone is enabled, add an additional rule to compile the java files into
-			// a separate set of classes (so that they don't overwrite the normal ones and require
-			// a rebuild when error-prone is turned off).
-			// TODO(ccross): Once we always compile with javac9 we may be able to conditionally
-			//    enable error-prone without affecting the output class files.
+		if Bool(j.properties.Errorprone.Enabled) {
+			// If error-prone is enabled, enable errorprone flags on the regular
+			// build.
+			flags = enableErrorproneFlags(flags)
+		} else if ctx.Config().RunErrorProne() && j.properties.Errorprone.Enabled == nil {
+			// Otherwise, if the RUN_ERROR_PRONE environment variable is set, create
+			// a new jar file just for compiling with the errorprone compiler to.
+			// This is because we don't want to cause the java files to get completely
+			// rebuilt every time the state of the RUN_ERROR_PRONE variable changes.
+			// We also don't want to run this if errorprone is enabled by default for
+			// this module, or else we could have duplicated errorprone messages.
+			errorproneFlags := enableErrorproneFlags(flags)
 			errorprone := android.PathForModuleOut(ctx, "errorprone", jarName)
-			RunErrorProne(ctx, errorprone, uniqueSrcFiles, srcJars, flags)
+
+			transformJavaToClasses(ctx, errorprone, -1, uniqueSrcFiles, srcJars, errorproneFlags, nil,
+				"errorprone", "errorprone")
+
 			extraJarDeps = append(extraJarDeps, errorprone)
 		}
 
@@ -1212,7 +1233,7 @@
 			}
 			// Dex compilation
 			var dexOutputFile android.OutputPath
-			dexOutputFile = j.dexer.compileDex(ctx, flags, j.MinSdkVersion(ctx), outputFile, jarName)
+			dexOutputFile = j.dexer.compileDex(ctx, flags, j.MinSdkVersion(ctx), implementationAndResourcesJar, jarName)
 			if ctx.Failed() {
 				return
 			}
@@ -1304,6 +1325,21 @@
 	j.outputFile = outputFile.WithoutRel()
 }
 
+// Returns a copy of the supplied flags, but with all the errorprone-related
+// fields copied to the regular build's fields.
+func enableErrorproneFlags(flags javaBuilderFlags) javaBuilderFlags {
+	flags.processorPath = append(flags.errorProneProcessorPath, flags.processorPath...)
+
+	if len(flags.errorProneExtraJavacFlags) > 0 {
+		if len(flags.javacFlags) > 0 {
+			flags.javacFlags += " " + flags.errorProneExtraJavacFlags
+		} else {
+			flags.javacFlags = flags.errorProneExtraJavacFlags
+		}
+	}
+	return flags
+}
+
 func (j *Module) compileJavaClasses(ctx android.ModuleContext, jarName string, idx int,
 	srcFiles, srcJars android.Paths, flags javaBuilderFlags, extraJarDeps android.Paths) android.WritablePath {
 
@@ -1482,12 +1518,8 @@
 	if sdkSpec.Kind == android.SdkCore {
 		return nil
 	}
-	ver, err := sdkSpec.EffectiveVersion(ctx)
-	if err != nil {
-		return err
-	}
-	if ver.GreaterThan(sdkVersion) {
-		return fmt.Errorf("newer SDK(%v)", ver)
+	if sdkSpec.ApiLevel.GreaterThan(sdkVersion) {
+		return fmt.Errorf("newer SDK(%v)", sdkSpec.ApiLevel)
 	}
 	return nil
 }
diff --git a/java/boot_jars.go b/java/boot_jars.go
index 86ebe36..5d40ec3 100644
--- a/java/boot_jars.go
+++ b/java/boot_jars.go
@@ -31,13 +31,18 @@
 // buildRuleForBootJarsPackageCheck generates the build rule to perform the boot jars package
 // check.
 func buildRuleForBootJarsPackageCheck(ctx android.ModuleContext, bootDexJarByModule bootDexJarByModule) {
+	bootDexJars := bootDexJarByModule.bootDexJarsWithoutCoverage()
+	if len(bootDexJars) == 0 {
+		return
+	}
+
 	timestamp := android.PathForOutput(ctx, "boot-jars-package-check/stamp")
 
 	rule := android.NewRuleBuilder(pctx, ctx)
 	rule.Command().BuiltTool("check_boot_jars").
 		Input(ctx.Config().HostToolPath(ctx, "dexdump")).
 		Input(android.PathForSource(ctx, "build/soong/scripts/check_boot_jars/package_allowed_list.txt")).
-		Inputs(bootDexJarByModule.bootDexJarsWithoutCoverage()).
+		Inputs(bootDexJars).
 		Text("&& touch").Output(timestamp)
 	rule.Build("boot_jars_package_check", "check boot jar packages")
 
diff --git a/java/bootclasspath.go b/java/bootclasspath.go
index d754fe6..4108770 100644
--- a/java/bootclasspath.go
+++ b/java/bootclasspath.go
@@ -144,6 +144,8 @@
 
 // ApexVariantReference specifies a particular apex variant of a module.
 type ApexVariantReference struct {
+	android.BpPrintableBase
+
 	// The name of the module apex variant, i.e. the apex containing the module variant.
 	//
 	// If this is not specified then it defaults to "platform" which will cause a dependency to be
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index a039964..f7561b4 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -146,6 +146,9 @@
 	ClasspathFragmentBase
 
 	properties bootclasspathFragmentProperties
+
+	// Collect the module directory for IDE info in java/jdeps.go.
+	modulePaths []string
 }
 
 // commonBootclasspathFragment defines the methods that are implemented by both source and prebuilt
@@ -422,6 +425,9 @@
 	// Generate classpaths.proto config
 	b.generateClasspathProtoBuildActions(ctx)
 
+	// Collect the module directory for IDE info in java/jdeps.go.
+	b.modulePaths = append(b.modulePaths, ctx.ModuleDir())
+
 	// Gather the bootclasspath fragment's contents.
 	var contents []android.Module
 	ctx.VisitDirectDeps(func(module android.Module) {
@@ -514,35 +520,38 @@
 // generateClasspathProtoBuildActions generates all required build actions for classpath.proto config
 func (b *BootclasspathFragmentModule) generateClasspathProtoBuildActions(ctx android.ModuleContext) {
 	var classpathJars []classpathJar
+	configuredJars := b.configuredJars(ctx)
 	if "art" == proptools.String(b.properties.Image_name) {
 		// ART and platform boot jars must have a corresponding entry in DEX2OATBOOTCLASSPATH
-		classpathJars = configuredJarListToClasspathJars(ctx, b.ClasspathFragmentToConfiguredJarList(ctx), BOOTCLASSPATH, DEX2OATBOOTCLASSPATH)
+		classpathJars = configuredJarListToClasspathJars(ctx, configuredJars, BOOTCLASSPATH, DEX2OATBOOTCLASSPATH)
 	} else {
-		classpathJars = configuredJarListToClasspathJars(ctx, b.ClasspathFragmentToConfiguredJarList(ctx), b.classpathType)
+		classpathJars = configuredJarListToClasspathJars(ctx, configuredJars, b.classpathType)
 	}
-	b.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, classpathJars)
+	b.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, configuredJars, classpathJars)
 }
 
-func (b *BootclasspathFragmentModule) ClasspathFragmentToConfiguredJarList(ctx android.ModuleContext) android.ConfiguredJarList {
+func (b *BootclasspathFragmentModule) configuredJars(ctx android.ModuleContext) android.ConfiguredJarList {
 	if "art" == proptools.String(b.properties.Image_name) {
 		return b.getImageConfig(ctx).modules
 	}
 
 	global := dexpreopt.GetGlobalConfig(ctx)
 
-	possibleUpdatableModules := gatherPossibleUpdatableModuleNamesAndStems(ctx, b.properties.Contents, bootclasspathFragmentContentDepTag)
-
-	// Only create configs for updatable boot jars. Non-updatable boot jars must be part of the
-	// platform_bootclasspath's classpath proto config to guarantee that they come before any
-	// updatable jars at runtime.
-	jars := global.UpdatableBootJars.Filter(possibleUpdatableModules)
+	possibleUpdatableModules := gatherPossibleApexModuleNamesAndStems(ctx, b.properties.Contents, bootclasspathFragmentContentDepTag)
+	jars, unknown := global.ApexBootJars.Filter(possibleUpdatableModules)
 
 	// TODO(satayev): for apex_test we want to include all contents unconditionally to classpaths
-	// config. However, any test specific jars would not be present in UpdatableBootJars. Instead,
+	// config. However, any test specific jars would not be present in ApexBootJars. Instead,
 	// we should check if we are creating a config for apex_test via ApexInfo and amend the values.
 	// This is an exception to support end-to-end test for SdkExtensions, until such support exists.
 	if android.InList("test_framework-sdkextensions", possibleUpdatableModules) {
 		jars = jars.Append("com.android.sdkext", "test_framework-sdkextensions")
+	} else if global.ApexBootJars.Len() != 0 && !android.IsModuleInVersionedSdk(ctx.Module()) {
+		unknown = android.RemoveListFromList(unknown, b.properties.Coverage.Contents)
+		_, unknown = android.RemoveFromList("core-icu4j", unknown)
+		if len(unknown) > 0 {
+			ctx.ModuleErrorf("%s in contents must also be declared in PRODUCT_APEX_BOOT_JARS", unknown)
+		}
 	}
 	return jars
 }
@@ -576,6 +585,14 @@
 	common := ctx.Module().(commonBootclasspathFragment)
 	output := common.produceHiddenAPIOutput(ctx, contents, input)
 
+	// If the source or prebuilts module does not provide a signature patterns file then generate one
+	// from the flags.
+	// TODO(b/192868581): Remove once the source and prebuilts provide a signature patterns file of
+	//  their own.
+	if output.SignaturePatternsPath == nil {
+		output.SignaturePatternsPath = buildRuleSignaturePatternsFile(ctx, output.AllFlagsPath)
+	}
+
 	// Initialize a HiddenAPIInfo structure.
 	hiddenAPIInfo := HiddenAPIInfo{
 		// The monolithic hidden API processing needs access to the flag files that override the default
@@ -592,7 +609,7 @@
 
 	// The monolithic hidden API processing also needs access to all the output files produced by
 	// hidden API processing of this fragment.
-	hiddenAPIInfo.HiddenAPIFlagOutput = (*output).HiddenAPIFlagOutput
+	hiddenAPIInfo.HiddenAPIFlagOutput = output.HiddenAPIFlagOutput
 
 	//  Provide it for use by other modules.
 	ctx.SetProvider(HiddenAPIInfoProvider, hiddenAPIInfo)
@@ -691,12 +708,18 @@
 	return androidBootImageFilesByArch
 }
 
+// Collect information for opening IDE project files in java/jdeps.go.
+func (b *BootclasspathFragmentModule) IDEInfo(dpInfo *android.IdeInfo) {
+	dpInfo.Deps = append(dpInfo.Deps, b.properties.Contents...)
+	dpInfo.Paths = append(dpInfo.Paths, b.modulePaths...)
+}
+
 type bootclasspathFragmentMemberType struct {
 	android.SdkMemberTypeBase
 }
 
-func (b *bootclasspathFragmentMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
-	mctx.AddVariationDependencies(nil, dependencyTag, names...)
+func (b *bootclasspathFragmentMemberType) AddDependencies(ctx android.SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) {
+	ctx.AddVariationDependencies(nil, dependencyTag, names...)
 }
 
 func (b *bootclasspathFragmentMemberType) IsInstance(module android.Module) bool {
@@ -729,12 +752,12 @@
 	Stub_libs               []string
 	Core_platform_stub_libs []string
 
+	// Fragment properties
+	Fragments []ApexVariantReference
+
 	// Flag files by *hiddenAPIFlagFileCategory
 	Flag_files_by_category FlagFilesByCategory
 
-	// The path to the generated stub-flags.csv file.
-	Stub_flags_path android.OptionalPath
-
 	// The path to the generated annotation-flags.csv file.
 	Annotation_flags_path android.OptionalPath
 
@@ -744,6 +767,12 @@
 	// The path to the generated index.csv file.
 	Index_path android.OptionalPath
 
+	// The path to the generated signature-patterns.csv file.
+	Signature_patterns_path android.OptionalPath
+
+	// The path to the generated stub-flags.csv file.
+	Stub_flags_path android.OptionalPath
+
 	// The path to the generated all-flags.csv file.
 	All_flags_path android.OptionalPath
 }
@@ -760,15 +789,20 @@
 	b.Flag_files_by_category = hiddenAPIInfo.FlagFilesByCategory
 
 	// Copy all the generated file paths.
-	b.Stub_flags_path = android.OptionalPathForPath(hiddenAPIInfo.StubFlagsPath)
 	b.Annotation_flags_path = android.OptionalPathForPath(hiddenAPIInfo.AnnotationFlagsPath)
 	b.Metadata_path = android.OptionalPathForPath(hiddenAPIInfo.MetadataPath)
 	b.Index_path = android.OptionalPathForPath(hiddenAPIInfo.IndexPath)
+
+	b.Signature_patterns_path = android.OptionalPathForPath(hiddenAPIInfo.SignaturePatternsPath)
+	b.Stub_flags_path = android.OptionalPathForPath(hiddenAPIInfo.StubFlagsPath)
 	b.All_flags_path = android.OptionalPathForPath(hiddenAPIInfo.AllFlagsPath)
 
 	// Copy stub_libs properties.
 	b.Stub_libs = module.properties.Api.Stub_libs
 	b.Core_platform_stub_libs = module.properties.Core_platform_api.Stub_libs
+
+	// Copy fragment properties.
+	b.Fragments = module.properties.Fragments
 }
 
 func (b *bootclasspathFragmentSdkMemberProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) {
@@ -791,6 +825,9 @@
 		corePlatformApiPropertySet := propertySet.AddPropertySet("core_platform_api")
 		corePlatformApiPropertySet.AddPropertyWithTag("stub_libs", b.Core_platform_stub_libs, requiredMemberDependency)
 	}
+	if len(b.Fragments) > 0 {
+		propertySet.AddProperty("fragments", b.Fragments)
+	}
 
 	hiddenAPISet := propertySet.AddPropertySet("hidden_api")
 	hiddenAPIDir := "hiddenapi"
@@ -821,10 +858,11 @@
 	}
 
 	// Copy all the generated files, if available.
-	copyOptionalPath(b.Stub_flags_path, "stub_flags")
 	copyOptionalPath(b.Annotation_flags_path, "annotation_flags")
 	copyOptionalPath(b.Metadata_path, "metadata")
 	copyOptionalPath(b.Index_path, "index")
+	copyOptionalPath(b.Signature_patterns_path, "signature_patterns")
+	copyOptionalPath(b.Stub_flags_path, "stub_flags")
 	copyOptionalPath(b.All_flags_path, "all_flags")
 }
 
@@ -834,9 +872,6 @@
 // specific properties.
 type prebuiltBootclasspathFragmentProperties struct {
 	Hidden_api struct {
-		// The path to the stub-flags.csv file created by the bootclasspath_fragment.
-		Stub_flags *string `android:"path"`
-
 		// The path to the annotation-flags.csv file created by the bootclasspath_fragment.
 		Annotation_flags *string `android:"path"`
 
@@ -846,6 +881,12 @@
 		// The path to the index.csv file created by the bootclasspath_fragment.
 		Index *string `android:"path"`
 
+		// The path to the signature-patterns.csv file created by the bootclasspath_fragment.
+		Signature_patterns *string `android:"path"`
+
+		// The path to the stub-flags.csv file created by the bootclasspath_fragment.
+		Stub_flags *string `android:"path"`
+
 		// The path to the all-flags.csv file created by the bootclasspath_fragment.
 		All_flags *string `android:"path"`
 	}
@@ -876,11 +917,17 @@
 func (module *prebuiltBootclasspathFragmentModule) produceHiddenAPIOutput(ctx android.ModuleContext, contents []android.Module, input HiddenAPIFlagInput) *HiddenAPIOutput {
 	pathForOptionalSrc := func(src *string) android.Path {
 		if src == nil {
-			// TODO(b/179354495): Fail if this is not provided once prebuilts have been updated.
 			return nil
 		}
 		return android.PathForModuleSrc(ctx, *src)
 	}
+	pathForSrc := func(property string, src *string) android.Path {
+		if src == nil {
+			ctx.PropertyErrorf(property, "is required but was not specified")
+			return android.PathForModuleSrc(ctx, "missing", property)
+		}
+		return android.PathForModuleSrc(ctx, *src)
+	}
 
 	// Retrieve the dex files directly from the content modules. They in turn should retrieve the
 	// encoded dex jars from the prebuilt .apex files.
@@ -888,11 +935,12 @@
 
 	output := HiddenAPIOutput{
 		HiddenAPIFlagOutput: HiddenAPIFlagOutput{
-			StubFlagsPath:       pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Stub_flags),
-			AnnotationFlagsPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Annotation_flags),
-			MetadataPath:        pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Metadata),
-			IndexPath:           pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Index),
-			AllFlagsPath:        pathForOptionalSrc(module.prebuiltProperties.Hidden_api.All_flags),
+			AnnotationFlagsPath:   pathForSrc("hidden_api.annotation_flags", module.prebuiltProperties.Hidden_api.Annotation_flags),
+			MetadataPath:          pathForSrc("hidden_api.metadata", module.prebuiltProperties.Hidden_api.Metadata),
+			IndexPath:             pathForSrc("hidden_api.index", module.prebuiltProperties.Hidden_api.Index),
+			SignaturePatternsPath: pathForOptionalSrc(module.prebuiltProperties.Hidden_api.Signature_patterns),
+			StubFlagsPath:         pathForSrc("hidden_api.stub_flags", module.prebuiltProperties.Hidden_api.Stub_flags),
+			AllFlagsPath:          pathForSrc("hidden_api.all_flags", module.prebuiltProperties.Hidden_api.All_flags),
 		},
 		EncodedBootDexFilesByModule: encodedBootDexJarsByModule,
 	}
@@ -907,15 +955,11 @@
 	}
 
 	var deapexerModule android.Module
-	ctx.VisitDirectDeps(func(to android.Module) {
-		tag := ctx.OtherModuleDependencyTag(to)
+	ctx.VisitDirectDeps(func(module android.Module) {
+		tag := ctx.OtherModuleDependencyTag(module)
 		// Save away the `deapexer` module on which this depends, if any.
 		if tag == android.DeapexerTag {
-			if deapexerModule != nil {
-				ctx.ModuleErrorf("Ambiguous duplicate deapexer module dependencies %q and %q",
-					deapexerModule.Name(), to.Name())
-			}
-			deapexerModule = to
+			deapexerModule = module
 		}
 	})
 
diff --git a/java/bootclasspath_fragment_test.go b/java/bootclasspath_fragment_test.go
index 3d0e155..d3de675 100644
--- a/java/bootclasspath_fragment_test.go
+++ b/java/bootclasspath_fragment_test.go
@@ -164,6 +164,7 @@
 		prepareForTestWithBootclasspathFragment,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("mysdklibrary", "mycoveragestubs"),
+		FixtureConfigureApexBootJars("someapex:mybootlib"),
 		prepareWithBp,
 	)
 
@@ -186,6 +187,7 @@
 		prepareForTestWithBootclasspathFragment,
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("mysdklibrary", "myothersdklibrary", "mycoreplatform"),
+		FixtureConfigureApexBootJars("someapex:mysdklibrary"),
 	).RunTestWithBp(t, `
 		bootclasspath_fragment {
 			name: "myfragment",
diff --git a/java/builder.go b/java/builder.go
index cde8731..ea011b8 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -279,23 +279,6 @@
 	transformJavaToClasses(ctx, outputFile, shardIdx, srcFiles, srcJars, flags, deps, "javac", desc)
 }
 
-func RunErrorProne(ctx android.ModuleContext, outputFile android.WritablePath,
-	srcFiles, srcJars android.Paths, flags javaBuilderFlags) {
-
-	flags.processorPath = append(flags.errorProneProcessorPath, flags.processorPath...)
-
-	if len(flags.errorProneExtraJavacFlags) > 0 {
-		if len(flags.javacFlags) > 0 {
-			flags.javacFlags += " " + flags.errorProneExtraJavacFlags
-		} else {
-			flags.javacFlags = flags.errorProneExtraJavacFlags
-		}
-	}
-
-	transformJavaToClasses(ctx, outputFile, -1, srcFiles, srcJars, flags, nil,
-		"errorprone", "errorprone")
-}
-
 // Emits the rule to generate Xref input file (.kzip file) for the given set of source files and source jars
 // to compile with given set of builder flags, etc.
 func emitXrefRule(ctx android.ModuleContext, xrefFile android.WritablePath, idx int,
diff --git a/java/classpath_fragment.go b/java/classpath_fragment.go
index ecfdfb7..f63d81d 100644
--- a/java/classpath_fragment.go
+++ b/java/classpath_fragment.go
@@ -19,6 +19,7 @@
 import (
 	"fmt"
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
 	"strings"
 
 	"android/soong/android"
@@ -44,6 +45,11 @@
 }
 
 type classpathFragmentProperties struct {
+	// Whether to generated classpaths.proto config instance for the fragment. If the config is not
+	// generated, then relevant boot jars are added to platform classpath, i.e. platform_bootclasspath
+	// or platform_systemserverclasspath. This is useful for non-updatable APEX boot jars, to keep
+	// them as part of dexopt on device. Defaults to true.
+	Generate_classpaths_proto *bool
 }
 
 // classpathFragment interface is implemented by a module that contributes jars to a *CLASSPATH
@@ -52,10 +58,6 @@
 	android.Module
 
 	classpathFragmentBase() *ClasspathFragmentBase
-
-	// ClasspathFragmentToConfiguredJarList returns android.ConfiguredJarList representation of all
-	// the jars in this classpath fragment.
-	ClasspathFragmentToConfiguredJarList(ctx android.ModuleContext) android.ConfiguredJarList
 }
 
 // ClasspathFragmentBase is meant to be embedded in any module types that implement classpathFragment;
@@ -89,8 +91,8 @@
 	maxSdkVersion int32
 }
 
-// gatherPossibleUpdatableModuleNamesAndStems returns a set of module and stem names from the
-// supplied contents that may be in the updatable boot jars.
+// gatherPossibleApexModuleNamesAndStems returns a set of module and stem names from the
+// supplied contents that may be in the apex boot jars.
 //
 // The module names are included because sometimes the stem is set to just change the name of
 // the installed file and it expects the configuration to still use the actual module name.
@@ -98,7 +100,7 @@
 // The stem names are included because sometimes the stem is set to change the effective name of the
 // module that is used in the configuration as well,e .g. when a test library is overriding an
 // actual boot jar
-func gatherPossibleUpdatableModuleNamesAndStems(ctx android.ModuleContext, contents []string, tag blueprint.DependencyTag) []string {
+func gatherPossibleApexModuleNamesAndStems(ctx android.ModuleContext, contents []string, tag blueprint.DependencyTag) []string {
 	set := map[string]struct{}{}
 	for _, name := range contents {
 		dep := ctx.GetDirectDepWithTag(name, tag)
@@ -127,25 +129,30 @@
 	return jars
 }
 
-func (c *ClasspathFragmentBase) generateClasspathProtoBuildActions(ctx android.ModuleContext, jars []classpathJar) {
-	outputFilename := strings.ToLower(c.classpathType.String()) + ".pb"
-	c.outputFilepath = android.PathForModuleOut(ctx, outputFilename).OutputPath
-	c.installDirPath = android.PathForModuleInstall(ctx, "etc", "classpaths")
+func (c *ClasspathFragmentBase) generateClasspathProtoBuildActions(ctx android.ModuleContext, configuredJars android.ConfiguredJarList, jars []classpathJar) {
+	generateProto := proptools.BoolDefault(c.properties.Generate_classpaths_proto, true)
+	if generateProto {
+		outputFilename := strings.ToLower(c.classpathType.String()) + ".pb"
+		c.outputFilepath = android.PathForModuleOut(ctx, outputFilename).OutputPath
+		c.installDirPath = android.PathForModuleInstall(ctx, "etc", "classpaths")
 
-	generatedJson := android.PathForModuleOut(ctx, outputFilename+".json")
-	writeClasspathsJson(ctx, generatedJson, jars)
+		generatedJson := android.PathForModuleOut(ctx, outputFilename+".json")
+		writeClasspathsJson(ctx, generatedJson, jars)
 
-	rule := android.NewRuleBuilder(pctx, ctx)
-	rule.Command().
-		BuiltTool("conv_classpaths_proto").
-		Flag("encode").
-		Flag("--format=json").
-		FlagWithInput("--input=", generatedJson).
-		FlagWithOutput("--output=", c.outputFilepath)
+		rule := android.NewRuleBuilder(pctx, ctx)
+		rule.Command().
+			BuiltTool("conv_classpaths_proto").
+			Flag("encode").
+			Flag("--format=json").
+			FlagWithInput("--input=", generatedJson).
+			FlagWithOutput("--output=", c.outputFilepath)
 
-	rule.Build("classpath_fragment", "Compiling "+c.outputFilepath.String())
+		rule.Build("classpath_fragment", "Compiling "+c.outputFilepath.String())
+	}
 
 	classpathProtoInfo := ClasspathFragmentProtoContentInfo{
+		ClasspathFragmentProtoGenerated:  generateProto,
+		ClasspathFragmentProtoContents:   configuredJars,
 		ClasspathFragmentProtoInstallDir: c.installDirPath,
 		ClasspathFragmentProtoOutput:     c.outputFilepath,
 	}
@@ -191,6 +198,12 @@
 var ClasspathFragmentProtoContentInfoProvider = blueprint.NewProvider(ClasspathFragmentProtoContentInfo{})
 
 type ClasspathFragmentProtoContentInfo struct {
+	// Whether the classpaths.proto config is generated for the fragment.
+	ClasspathFragmentProtoGenerated bool
+
+	// ClasspathFragmentProtoContents contains a list of jars that are part of this classpath fragment.
+	ClasspathFragmentProtoContents android.ConfiguredJarList
+
 	// ClasspathFragmentProtoOutput is an output path for the generated classpaths.proto config of this module.
 	//
 	// The file should be copied to a relevant place on device, see ClasspathFragmentProtoInstallDir
diff --git a/java/config/config.go b/java/config/config.go
index 273084c..30c6f91 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -69,8 +69,6 @@
 	pctx.StaticVariable("JavacHeapSize", "2048M")
 	pctx.StaticVariable("JavacHeapFlags", "-J-Xmx${JavacHeapSize}")
 	pctx.StaticVariable("DexFlags", "-JXX:OnError='cat hs_err_pid%p.log' -JXX:CICompilerCount=6 -JXX:+UseDynamicNumberOfGCThreads")
-	// TODO(b/181095653): remove duplicated flags.
-	pctx.StaticVariable("DexJavaFlags", "-XX:OnError='cat hs_err_pid%p.log' -XX:CICompilerCount=6 -XX:+UseDynamicNumberOfGCThreads -Xmx2G")
 
 	pctx.StaticVariable("CommonJdkFlags", strings.Join([]string{
 		`-Xmaxerrs 9999999`,
diff --git a/java/core-libraries/Android.bp b/java/core-libraries/Android.bp
new file mode 100644
index 0000000..b198c24
--- /dev/null
+++ b/java/core-libraries/Android.bp
@@ -0,0 +1,198 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//
+// Definitions for building the Android core libraries, i.e. ART, I18n and
+// Conscrypt.
+//
+// These are here as the definitions are used by the build itself and include
+// parts from all three of those modules.
+//
+
+// A stubs target containing the parts of the public SDK API provided by the
+// core libraries.
+//
+// Don't use this directly, use "sdk_version: core_current".
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+java_library {
+    name: "core.current.stubs",
+    visibility: ["//visibility:public"],
+    static_libs: [
+        "art.module.public.api.stubs",
+        "conscrypt.module.public.api.stubs",
+        "i18n.module.public.api.stubs",
+    ],
+    sdk_version: "none",
+    system_modules: "none",
+
+    dist: {
+        targets: [
+            "sdk",
+            "win_sdk",
+        ],
+    },
+}
+
+// Distributed with the SDK for turning into system modules to compile apps
+// against.
+java_library {
+    name: "core-current-stubs-for-system-modules",
+    visibility: ["//development/sdk"],
+    static_libs: [
+        "core.current.stubs",
+        // This one is not on device but it's needed when javac compiles code
+        // containing lambdas.
+        "core-lambda-stubs-for-system-modules",
+        // This one is not on device but it's needed when javac compiles code
+        // containing @Generated annotations produced by some code generation
+        // tools.
+        // See http://b/123891440.
+        "core-generated-annotation-stubs",
+    ],
+    sdk_version: "none",
+    system_modules: "none",
+    dist: {
+        dest: "core-for-system-modules.jar",
+        targets: [
+            "sdk",
+            "win_sdk",
+        ],
+    },
+}
+
+// Used when compiling higher-level code against core.current.stubs.
+java_system_modules {
+    name: "core-current-stubs-system-modules",
+    visibility: ["//visibility:public"],
+    libs: [
+        "core-current-stubs-for-system-modules",
+    ],
+}
+
+// A stubs target containing the parts of the public SDK & @SystemApi(MODULE_LIBRARIES) API
+// provided by the core libraries.
+//
+// Don't use this directly, use "sdk_version: module_current".
+java_library {
+    name: "core.module_lib.stubs",
+    static_libs: [
+        "art.module.public.api.stubs.module_lib",
+
+        // Replace the following with the module-lib correspondence when Conscrypt or i18N module
+        // provides @SystemApi(MODULE_LIBRARIES). Currently, assume that only ART module provides
+        // @SystemApi(MODULE_LIBRARIES).
+        "conscrypt.module.public.api.stubs",
+        "i18n.module.public.api.stubs",
+    ],
+    sdk_version: "none",
+    system_modules: "none",
+    visibility: ["//visibility:private"],
+}
+
+// Used when compiling higher-level code with sdk_version "module_current"
+java_system_modules {
+    name: "core-module-lib-stubs-system-modules",
+    libs: [
+        "core.module_lib.stubs",
+        // This one is not on device but it's needed when javac compiles code
+        // containing lambdas.
+        "core-lambda-stubs-for-system-modules",
+        // This one is not on device but it's needed when javac compiles code
+        // containing @Generated annotations produced by some code generation
+        // tools.
+        // See http://b/123891440.
+        "core-generated-annotation-stubs",
+    ],
+    visibility: ["//visibility:public"],
+}
+
+// Ideally this should be a restricted allowlist but there are hundreds of modules that depend on
+// this.
+// TODO(http://b/134561230) - limit the number of dependents on this.
+core_platform_visibility = ["//visibility:public"]
+
+// Libraries containing the core platform API stubs for the core libraries.
+//
+// Although this stubs library is primarily used by the Java compiler / build to indicate
+// the core platform API surface area, compile_dex: true is used so that the Core Platform
+// API annotations are available to the dex tools that enable enforcement of runtime
+// accessibility. b/119068555
+java_library {
+    name: "legacy.core.platform.api.stubs",
+    visibility: core_platform_visibility,
+    hostdex: true,
+    compile_dex: true,
+
+    sdk_version: "none",
+    system_modules: "none",
+    static_libs: [
+        "art.module.public.api.stubs.module_lib",
+        "conscrypt.module.platform.api.stubs",
+        "legacy.i18n.module.platform.api.stubs",
+    ],
+    patch_module: "java.base",
+}
+
+java_library {
+    name: "stable.core.platform.api.stubs",
+    visibility: core_platform_visibility,
+    hostdex: true,
+    compile_dex: true,
+
+    sdk_version: "none",
+    system_modules: "none",
+    static_libs: [
+        "art.module.public.api.stubs.module_lib",
+        // conscrypt only has a stable version, so it is okay to depend on it here:
+        "conscrypt.module.platform.api.stubs",
+        "stable.i18n.module.platform.api.stubs",
+    ],
+    patch_module: "java.base",
+}
+
+// Used when compiling higher-level code against *.core.platform.api.stubs.
+java_system_modules {
+    name: "legacy-core-platform-api-stubs-system-modules",
+    visibility: core_platform_visibility,
+    libs: [
+        "legacy.core.platform.api.stubs",
+        // This one is not on device but it's needed when javac compiles code
+        // containing lambdas.
+        "core-lambda-stubs-for-system-modules",
+        // This one is not on device but it's needed when javac compiles code
+        // containing @Generated annotations produced by some code generation
+        // tools.
+        // See http://b/123891440.
+        "core-generated-annotation-stubs",
+    ],
+}
+
+java_system_modules {
+    name: "stable-core-platform-api-stubs-system-modules",
+    visibility: core_platform_visibility,
+    libs: [
+        "stable.core.platform.api.stubs",
+        // This one is not on device but it's needed when javac compiles code
+        // containing lambdas.
+        "core-lambda-stubs-for-system-modules",
+        // This one is not on device but it's needed when javac compiles code
+        // containing @Generated annotations produced by some code generation
+        // tools.
+        // See http://b/123891440.
+        "core-generated-annotation-stubs",
+    ],
+}
diff --git a/java/core-libraries/OWNERS b/java/core-libraries/OWNERS
new file mode 100644
index 0000000..bb3546a
--- /dev/null
+++ b/java/core-libraries/OWNERS
@@ -0,0 +1,3 @@
+include platform/external/icu:/OWNERS
+include platform/external/conscrypt:/OWNERS
+include platform/libcore:/OWNERS
diff --git a/java/dex.go b/java/dex.go
index 6bf0143..667800f 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -84,19 +84,17 @@
 	return BoolDefault(d.dexProperties.Optimize.Enabled, d.dexProperties.Optimize.EnabledByDefault)
 }
 
-func init() {
-	pctx.HostBinToolVariable("runWithTimeoutCmd", "run_with_timeout")
-	pctx.SourcePathVariable("jstackCmd", "${config.JavaToolchain}/jstack")
-}
-
 var d8, d8RE = pctx.MultiCommandRemoteStaticRules("d8",
 	blueprint.RuleParams{
 		Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
-			`$d8Template${config.D8Cmd} ${config.DexFlags} --output $outDir $d8Flags $in && ` +
+			`mkdir -p $$(dirname $tmpJar) && ` +
+			`${config.Zip2ZipCmd} -i $in -o $tmpJar -x '**/*.dex' && ` +
+			`$d8Template${config.D8Cmd} ${config.DexFlags} --output $outDir $d8Flags $tmpJar && ` +
 			`$zipTemplate${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
 			`${config.MergeZipsCmd} -D -stripFile "**/*.class" $out $outDir/classes.dex.jar $in`,
 		CommandDeps: []string{
 			"${config.D8Cmd}",
+			"${config.Zip2ZipCmd}",
 			"${config.SoongZipCmd}",
 			"${config.MergeZipsCmd}",
 		},
@@ -115,17 +113,16 @@
 			ExecStrategy: "${config.RED8ExecStrategy}",
 			Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
 		},
-	}, []string{"outDir", "d8Flags", "zipFlags"}, nil)
+	}, []string{"outDir", "d8Flags", "zipFlags", "tmpJar"}, nil)
 
 var r8, r8RE = pctx.MultiCommandRemoteStaticRules("r8",
 	blueprint.RuleParams{
 		Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
 			`rm -f "$outDict" && rm -rf "${outUsageDir}" && ` +
 			`mkdir -p $$(dirname ${outUsage}) && ` +
-			// TODO(b/181095653): remove R8 timeout and go back to config.R8Cmd.
-			`${runWithTimeoutCmd} -timeout 30m -on_timeout '${jstackCmd} $$PID' -- ` +
-			`$r8Template${config.JavaCmd} ${config.DexJavaFlags} -cp ${config.R8Jar} ` +
-			`com.android.tools.r8.compatproguard.CompatProguard -injars $in --output $outDir ` +
+			`mkdir -p $$(dirname $tmpJar) && ` +
+			`${config.Zip2ZipCmd} -i $in -o $tmpJar -x '**/*.dex' && ` +
+			`$r8Template${config.R8Cmd} ${config.DexFlags} -injars $tmpJar --output $outDir ` +
 			`--no-data-resources ` +
 			`-printmapping ${outDict} ` +
 			`-printusage ${outUsage} ` +
@@ -136,10 +133,10 @@
 			`$zipTemplate${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
 			`${config.MergeZipsCmd} -D -stripFile "**/*.class" $out $outDir/classes.dex.jar $in`,
 		CommandDeps: []string{
-			"${config.R8Jar}",
+			"${config.R8Cmd}",
+			"${config.Zip2ZipCmd}",
 			"${config.SoongZipCmd}",
 			"${config.MergeZipsCmd}",
-			"${runWithTimeoutCmd}",
 		},
 	}, map[string]*remoteexec.REParams{
 		"$r8Template": &remoteexec.REParams{
@@ -165,7 +162,7 @@
 			Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
 		},
 	}, []string{"outDir", "outDict", "outUsage", "outUsageZip", "outUsageDir",
-		"r8Flags", "zipFlags"}, []string{"implicits"})
+		"r8Flags", "zipFlags", "tmpJar"}, []string{"implicits"})
 
 func (d *dexer) dexCommonFlags(ctx android.ModuleContext, minSdkVersion android.SdkSpec) []string {
 	flags := d.dexProperties.Dxflags
@@ -282,6 +279,7 @@
 	// Compile classes.jar into classes.dex and then javalib.jar
 	javalibJar := android.PathForModuleOut(ctx, "dex", jarName).OutputPath
 	outDir := android.PathForModuleOut(ctx, "dex")
+	tmpJar := android.PathForModuleOut(ctx, "withres-withoutdex", jarName)
 
 	zipFlags := "--ignore_missing_files"
 	if proptools.Bool(d.dexProperties.Uncompress_dex) {
@@ -309,6 +307,7 @@
 			"outUsage":    proguardUsage.String(),
 			"outUsageZip": proguardUsageZip.String(),
 			"outDir":      outDir.String(),
+			"tmpJar":      tmpJar.String(),
 		}
 		if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_R8") {
 			rule = r8RE
@@ -339,6 +338,7 @@
 				"d8Flags":  strings.Join(append(commonFlags, d8Flags...), " "),
 				"zipFlags": zipFlags,
 				"outDir":   outDir.String(),
+				"tmpJar":   tmpJar.String(),
 			},
 		})
 	}
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index 2e46d74..0faae36 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -141,10 +141,9 @@
 		}
 	}
 
-	// If it is neither app nor test, make config files regardless of its dexpreopt setting.
+	// If it is test, make config files regardless of its dexpreopt setting.
 	// The config files are required for apps defined in make which depend on the lib.
-	// TODO(b/158843648): The config for apps should be generated as well regardless of setting.
-	if (d.isApp || d.isTest) && d.dexpreoptDisabled(ctx) {
+	if d.isTest && d.dexpreoptDisabled(ctx) {
 		return
 	}
 
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index 19c65ca..946092c 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -154,12 +154,23 @@
 // PRODUCT_BOOT_JARS_EXTRA variables. The AOSP makefiles specify some common Framework libraries,
 // but more product-specific libraries can be added in the product makefiles.
 //
-// Each component of the PRODUCT_BOOT_JARS and PRODUCT_BOOT_JARS_EXTRA variables is either a simple
-// name (if the library is a part of the Platform), or a colon-separated pair <apex, name> (if the
-// library is a part of a non-updatable APEX).
+// Each component of the PRODUCT_BOOT_JARS and PRODUCT_BOOT_JARS_EXTRA variables is a
+// colon-separated pair <apex>:<library>, where <apex> is the variant name of a non-updatable APEX,
+// "platform" if the library is a part of the platform in the system partition, or "system_ext" if
+// it's in the system_ext partition.
 //
-// A related variable PRODUCT_UPDATABLE_BOOT_JARS contains bootclasspath libraries that are in
-// updatable APEXes. They are not included in the boot image.
+// In these variables APEXes are identified by their "variant names", i.e. the names they get
+// mounted as in /apex on device. In Soong modules that is the name set in the "apex_name"
+// properties, which default to the "name" values. For example, many APEXes have both
+// com.android.xxx and com.google.android.xxx modules in Soong, but take the same place
+// /apex/com.android.xxx at runtime. In these cases the variant name is always com.android.xxx,
+// regardless which APEX goes into the product. See also android.ApexInfo.ApexVariationName and
+// apex.apexBundleProperties.Apex_name.
+//
+// A related variable PRODUCT_APEX_BOOT_JARS contains bootclasspath libraries that are in APEXes.
+// They are not included in the boot image. The only exception here are ART jars and core-icu4j.jar
+// that have been historically part of the boot image and are now in apexes; they are in boot images
+// and core-icu4j.jar is generally treated as being part of PRODUCT_BOOT_JARS.
 //
 // One exception to the above rules are "coverage" builds (a special build flavor which requires
 // setting environment variable EMMA_INSTRUMENT_FRAMEWORK=true). In coverage builds the Java code in
@@ -489,7 +500,11 @@
 		dst := dstBootJarsByModule[name]
 
 		if src == nil {
-			ctx.ModuleErrorf("module %s does not provide a dex boot jar", name)
+			if !ctx.Config().AllowMissingDependencies() {
+				ctx.ModuleErrorf("module %s does not provide a dex boot jar", name)
+			} else {
+				ctx.AddMissingDependencies([]string{name})
+			}
 		} else if dst == nil {
 			ctx.ModuleErrorf("module %s is not part of the boot configuration", name)
 		} else {
@@ -512,14 +527,14 @@
 }
 
 // buildBootImageVariantsForBuildOs generates rules to build the boot image variants for the
-// android.BuildOs OsType, i.e. the type of OS on which the build is being running.
+// config.BuildOS OsType, i.e. the type of OS on which the build is being running.
 //
 // The files need to be generated into their predefined location because they are used from there
 // both within Soong and outside, e.g. for ART based host side testing and also for use by some
 // cloud based tools. However, they are not needed by callers of this function and so the paths do
 // not need to be returned from this func, unlike the buildBootImageVariantsForAndroidOs func.
 func buildBootImageVariantsForBuildOs(ctx android.ModuleContext, image *bootImageConfig, profile android.WritablePath) {
-	buildBootImageForOsType(ctx, image, profile, android.BuildOs)
+	buildBootImageForOsType(ctx, image, profile, ctx.Config().BuildOS)
 }
 
 // buildBootImageForOsType takes a bootImageConfig, a profile file and an android.OsType
@@ -799,10 +814,10 @@
 
 // generateUpdatableBcpPackagesRule generates the rule to create the updatable-bcp-packages.txt file
 // and returns a path to the generated file.
-func generateUpdatableBcpPackagesRule(ctx android.ModuleContext, image *bootImageConfig, updatableModules []android.Module) android.WritablePath {
+func generateUpdatableBcpPackagesRule(ctx android.ModuleContext, image *bootImageConfig, apexModules []android.Module) android.WritablePath {
 	// Collect `permitted_packages` for updatable boot jars.
 	var updatablePackages []string
-	for _, module := range updatableModules {
+	for _, module := range apexModules {
 		if j, ok := module.(PermittedPackagesForUpdatableBootJars); ok {
 			pp := j.PermittedPackagesForUpdatableBootJars()
 			if len(pp) > 0 {
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index b13955f..415a1d4 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -32,7 +32,7 @@
 		}
 	}
 	// We may also need the images on host in order to run host-based tests.
-	for _, target := range ctx.Config().Targets[android.BuildOs] {
+	for _, target := range ctx.Config().Targets[ctx.Config().BuildOS] {
 		targets = append(targets, target)
 	}
 
@@ -142,14 +142,14 @@
 	return genBootImageConfigs(ctx)[frameworkBootImageName]
 }
 
-// Updatable boot config allows to access build/install paths of updatable boot jars without going
+// Apex boot config allows to access build/install paths of apex boot jars without going
 // through the usual trouble of registering dependencies on those modules and extracting build paths
 // from those dependencies.
-type updatableBootConfig struct {
-	// A list of updatable boot jars.
+type apexBootConfig struct {
+	// A list of apex boot jars.
 	modules android.ConfiguredJarList
 
-	// A list of predefined build paths to updatable boot jars. They are configured very early,
+	// A list of predefined build paths to apex boot jars. They are configured very early,
 	// before the modules for these jars are processed and the actual paths are generated, and
 	// later on a singleton adds commands to copy actual jars to the predefined paths.
 	dexPaths android.WritablePaths
@@ -161,21 +161,21 @@
 	dexLocations []string
 }
 
-var updatableBootConfigKey = android.NewOnceKey("updatableBootConfig")
+var updatableBootConfigKey = android.NewOnceKey("apexBootConfig")
 
-// Returns updatable boot config.
-func GetUpdatableBootConfig(ctx android.PathContext) updatableBootConfig {
+// Returns apex boot config.
+func GetApexBootConfig(ctx android.PathContext) apexBootConfig {
 	return ctx.Config().Once(updatableBootConfigKey, func() interface{} {
-		updatableBootJars := dexpreopt.GetGlobalConfig(ctx).UpdatableBootJars
+		apexBootJars := dexpreopt.GetGlobalConfig(ctx).ApexBootJars
 
-		dir := android.PathForOutput(ctx, ctx.Config().DeviceName(), "updatable_bootjars")
-		dexPaths := updatableBootJars.BuildPaths(ctx, dir)
-		dexPathsByModuleName := updatableBootJars.BuildPathsByModule(ctx, dir)
+		dir := android.PathForOutput(ctx, ctx.Config().DeviceName(), "apex_bootjars")
+		dexPaths := apexBootJars.BuildPaths(ctx, dir)
+		dexPathsByModuleName := apexBootJars.BuildPathsByModule(ctx, dir)
 
-		dexLocations := updatableBootJars.DevicePaths(ctx.Config(), android.Android)
+		dexLocations := apexBootJars.DevicePaths(ctx.Config(), android.Android)
 
-		return updatableBootConfig{updatableBootJars, dexPaths, dexPathsByModuleName, dexLocations}
-	}).(updatableBootConfig)
+		return apexBootConfig{apexBootJars, dexPaths, dexPathsByModuleName, dexLocations}
+	}).(apexBootConfig)
 }
 
 // Returns a list of paths and a list of locations for the boot jars used in dexpreopt (to be
@@ -188,10 +188,10 @@
 	dexLocations := bootImage.getAnyAndroidVariant().dexLocationsDeps
 
 	if withUpdatable {
-		// Updatable boot jars (they are used only in dexpreopt, but not in the boot image).
-		updBootConfig := GetUpdatableBootConfig(ctx)
-		dexPaths = append(dexPaths, updBootConfig.dexPaths...)
-		dexLocations = append(dexLocations, updBootConfig.dexLocations...)
+		// Apex boot jars (they are used only in dexpreopt, but not in the boot image).
+		apexBootConfig := GetApexBootConfig(ctx)
+		dexPaths = append(dexPaths, apexBootConfig.dexPaths...)
+		dexLocations = append(dexLocations, apexBootConfig.dexLocations...)
 	}
 
 	return dexPaths, dexLocations
diff --git a/java/dexpreopt_test.go b/java/dexpreopt_test.go
index b25dece..8dc7b79 100644
--- a/java/dexpreopt_test.go
+++ b/java/dexpreopt_test.go
@@ -16,6 +16,7 @@
 
 import (
 	"fmt"
+	"runtime"
 	"testing"
 
 	"android/soong/android"
@@ -173,9 +174,9 @@
 }
 
 func TestDex2oatToolDeps(t *testing.T) {
-	if android.BuildOs != android.Linux {
+	if runtime.GOOS != "linux" {
 		// The host binary paths checked below are build OS dependent.
-		t.Skipf("Unsupported build OS %s", android.BuildOs)
+		t.Skipf("Unsupported build OS %s", runtime.GOOS)
 	}
 
 	preparers := android.GroupFixturePreparers(
diff --git a/java/droidstubs.go b/java/droidstubs.go
index c756815..ec1b04a 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -128,12 +128,15 @@
 	// whicih can be used for scheduling purposes
 	High_mem *bool
 
-	// is set to true, Metalava will allow framework SDK to contain API levels annotations.
+	// if set to true, Metalava will allow framework SDK to contain API levels annotations.
 	Api_levels_annotations_enabled *bool
 
 	// the dirs which Metalava extracts API levels annotations from.
 	Api_levels_annotations_dirs []string
 
+	// the sdk kind which Metalava extracts API levels annotations from. Supports 'public' and 'system' for now; defaults to public.
+	Api_levels_sdk_type *string
+
 	// the filename which Metalava extracts API levels annotations from. Defaults to android.jar.
 	Api_levels_jar_filename *string
 
@@ -367,6 +370,7 @@
 
 	filename := proptools.StringDefault(d.properties.Api_levels_jar_filename, "android.jar")
 
+	var dirs []string
 	ctx.VisitDirectDepsWithTag(metalavaAPILevelsAnnotationsDirTag, func(m android.Module) {
 		if t, ok := m.(*ExportedDroiddocDir); ok {
 			for _, dep := range t.deps {
@@ -383,12 +387,32 @@
 					cmd.Implicit(dep)
 				}
 			}
-			cmd.FlagWithArg("--android-jar-pattern ", t.dir.String()+"/%/public/"+filename)
+
+			dirs = append(dirs, t.dir.String())
 		} else {
 			ctx.PropertyErrorf("api_levels_annotations_dirs",
 				"module %q is not a metalava api-levels-annotations dir", ctx.OtherModuleName(m))
 		}
 	})
+
+	// Add all relevant --android-jar-pattern patterns for Metalava.
+	// When parsing a stub jar for a specific version, Metalava picks the first pattern that defines
+	// an actual file present on disk (in the order the patterns were passed). For system APIs for
+	// privileged apps that are only defined since API level 21 (Lollipop), fallback to public stubs
+	// for older releases.
+	if sdkType := proptools.StringDefault(d.properties.Api_levels_sdk_type, "public"); sdkType != "public" {
+		if sdkType != "system" {
+			ctx.PropertyErrorf("api_levels_sdk_type", "only 'public' and 'system' are supported")
+		}
+		// If building non public stubs, add all sdkType patterns first...
+		for _, dir := range dirs {
+			cmd.FlagWithArg("--android-jar-pattern ", fmt.Sprintf("%s/%%/%s/%s", dir, sdkType, filename))
+		}
+	}
+	for _, dir := range dirs {
+		// ... and fallback to public ones, for Metalava to use if needed.
+		cmd.FlagWithArg("--android-jar-pattern ", fmt.Sprintf("%s/%%/%s/%s", dir, "public", filename))
+	}
 }
 
 func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, javaVersion javaVersion, srcs android.Paths,
@@ -530,7 +554,8 @@
 			`\n` +
 			`If it is not possible to do so, there are workarounds:\n` +
 			`\n` +
-			`1. You can suppress the errors with @SuppressLint("<id>")\n`
+			`1. You can suppress the errors with @SuppressLint("<id>")\n` +
+			`   where the <id> is given in brackets in the error message above.\n`
 
 		if baselineFile.Valid() {
 			cmd.FlagWithInput("--baseline:api-lint ", baselineFile.Path())
diff --git a/java/droidstubs_test.go b/java/droidstubs_test.go
index db664c1..60d0bea 100644
--- a/java/droidstubs_test.go
+++ b/java/droidstubs_test.go
@@ -16,6 +16,7 @@
 
 import (
 	"reflect"
+	"regexp"
 	"strings"
 	"testing"
 
@@ -81,6 +82,46 @@
 	}
 }
 
+func TestSystemDroidstubs(t *testing.T) {
+	ctx, _ := testJavaWithFS(t, `
+		droiddoc_exported_dir {
+			name: "some-exported-dir",
+			path: "somedir",
+		}
+
+		droiddoc_exported_dir {
+			name: "some-other-exported-dir",
+			path: "someotherdir",
+		}
+
+		droidstubs {
+			name: "foo-stubs",
+			srcs: ["foo-doc/a.java"],
+			api_levels_annotations_dirs: [
+				"some-exported-dir",
+				"some-other-exported-dir",
+			],
+			api_levels_annotations_enabled: true,
+            api_levels_sdk_type: "system",
+		}
+		`,
+		map[string][]byte{
+			"foo-doc/a.java": nil,
+		})
+
+	m := ctx.ModuleForTests("foo-stubs", "android_common")
+	manifest := m.Output("metalava.sbox.textproto")
+	cmd := String(android.RuleBuilderSboxProtoForTests(t, manifest).Commands[0].Command)
+	r := regexp.MustCompile(`--android-jar-pattern [^ ]+/android.jar`)
+	matches := r.FindAllString(cmd, -1)
+	android.AssertArrayString(t, "order of patterns", []string{
+		"--android-jar-pattern somedir/%/system/android.jar",
+		"--android-jar-pattern someotherdir/%/system/android.jar",
+		"--android-jar-pattern somedir/%/public/android.jar",
+		"--android-jar-pattern someotherdir/%/public/android.jar",
+	}, matches)
+}
+
 func TestDroidstubsSandbox(t *testing.T) {
 	ctx, _ := testJavaWithFS(t, `
 		genrule {
diff --git a/java/hiddenapi.go b/java/hiddenapi.go
index f901434..30683da 100644
--- a/java/hiddenapi.go
+++ b/java/hiddenapi.go
@@ -118,11 +118,11 @@
 }
 
 func isModuleInBootClassPath(ctx android.BaseModuleContext, module android.Module) bool {
-	// Get the configured non-updatable and updatable boot jars.
-	nonUpdatableBootJars := ctx.Config().NonUpdatableBootJars()
-	updatableBootJars := ctx.Config().UpdatableBootJars()
-	active := isModuleInConfiguredList(ctx, module, nonUpdatableBootJars) ||
-		isModuleInConfiguredList(ctx, module, updatableBootJars)
+	// Get the configured platform and apex boot jars.
+	nonApexBootJars := ctx.Config().NonApexBootJars()
+	apexBootJars := ctx.Config().ApexBootJars()
+	active := isModuleInConfiguredList(ctx, module, nonApexBootJars) ||
+		isModuleInConfiguredList(ctx, module, apexBootJars)
 	return active
 }
 
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index 654ebb7..8e39f40 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -297,7 +297,7 @@
 //
 // The rule is initialized but not built so that the caller can modify it and select an appropriate
 // name.
-func buildRuleToGenerateHiddenAPIStubFlagsFile(ctx android.BuilderContext, name, desc string, outputPath android.WritablePath, bootDexJars android.Paths, input HiddenAPIFlagInput, moduleStubFlagsPaths android.Paths) {
+func buildRuleToGenerateHiddenAPIStubFlagsFile(ctx android.BuilderContext, name, desc string, outputPath android.WritablePath, bootDexJars android.Paths, input HiddenAPIFlagInput, stubFlagSubsets SignatureCsvSubsets) {
 	// Singleton rule which applies hiddenapi on all boot class path dex files.
 	rule := android.NewRuleBuilder(pctx, ctx)
 
@@ -317,7 +317,7 @@
 
 	// If no module stub flags paths are provided then this must be being called for a
 	// bootclasspath_fragment and not the whole platform_bootclasspath.
-	if moduleStubFlagsPaths == nil {
+	if stubFlagSubsets == nil {
 		// This is being run on a fragment of the bootclasspath.
 		command.Flag("--fragment")
 	}
@@ -342,8 +342,8 @@
 
 	// If there are stub flag files that have been generated by fragments on which this depends then
 	// use them to validate the stub flag file generated by the rules created by this method.
-	if len(moduleStubFlagsPaths) > 0 {
-		validFile := buildRuleValidateOverlappingCsvFiles(ctx, name, desc, outputPath, moduleStubFlagsPaths)
+	if len(stubFlagSubsets) > 0 {
+		validFile := buildRuleValidateOverlappingCsvFiles(ctx, name, desc, outputPath, stubFlagSubsets)
 
 		// Add the file that indicates that the file generated by this is valid.
 		//
@@ -510,14 +510,6 @@
 	}
 }
 
-// dedup removes duplicates in the flag files, while maintaining the order in which they were
-// appended.
-func (s FlagFilesByCategory) dedup() {
-	for category, paths := range s {
-		s[category] = android.FirstUniquePaths(paths)
-	}
-}
-
 // HiddenAPIInfo contains information provided by the hidden API processing.
 //
 // That includes paths resolved from HiddenAPIFlagFileProperties and also generated by hidden API
@@ -554,6 +546,20 @@
 	}
 }
 
+// StubFlagSubset returns a SignatureCsvSubset that contains a path to a stub-flags.csv file and a
+// path to a signature-patterns.csv file that defines a subset of the monolithic stub flags file,
+// i.e. out/soong/hiddenapi/hiddenapi-stub-flags.txt, against which it will be compared.
+func (i *HiddenAPIInfo) StubFlagSubset() SignatureCsvSubset {
+	return SignatureCsvSubset{i.StubFlagsPath, i.SignaturePatternsPath}
+}
+
+// FlagSubset returns a SignatureCsvSubset that contains a path to an all-flags.csv file and a
+// path to a signature-patterns.csv file that defines a subset of the monolithic flags file, i.e.
+// out/soong/hiddenapi/hiddenapi-flags.csv, against which it will be compared.
+func (i *HiddenAPIInfo) FlagSubset() SignatureCsvSubset {
+	return SignatureCsvSubset{i.AllFlagsPath, i.SignaturePatternsPath}
+}
+
 var HiddenAPIInfoProvider = blueprint.NewProvider(HiddenAPIInfo{})
 
 // ModuleStubDexJars contains the stub dex jars provided by a single module.
@@ -790,6 +796,10 @@
 
 	// The path to the generated all-flags.csv file.
 	AllFlagsPath android.Path
+
+	// The path to the generated signature-patterns.txt file which defines the subset of the
+	// monolithic hidden API files provided in this.
+	SignaturePatternsPath android.Path
 }
 
 // bootDexJarByModule is a map from base module name (without prebuilt_ prefix) to the boot dex
@@ -856,7 +866,7 @@
 // the annotationFlags.
 func buildRuleToGenerateHiddenApiFlags(ctx android.BuilderContext, name, desc string,
 	outputPath android.WritablePath, baseFlagsPath android.Path, annotationFlagPaths android.Paths,
-	flagFilesByCategory FlagFilesByCategory, allFlagsPaths android.Paths, generatedRemovedDexSignatures android.OptionalPath) {
+	flagFilesByCategory FlagFilesByCategory, flagSubsets SignatureCsvSubsets, generatedRemovedDexSignatures android.OptionalPath) {
 
 	// Create the rule that will generate the flag files.
 	tempPath := tempPathForRestat(ctx, outputPath)
@@ -885,8 +895,8 @@
 
 	// If there are flag files that have been generated by fragments on which this depends then use
 	// them to validate the flag file generated by the rules created by this method.
-	if len(allFlagsPaths) > 0 {
-		validFile := buildRuleValidateOverlappingCsvFiles(ctx, name, desc, outputPath, allFlagsPaths)
+	if len(flagSubsets) > 0 {
+		validFile := buildRuleValidateOverlappingCsvFiles(ctx, name, desc, outputPath, flagSubsets)
 
 		// Add the file that indicates that the file generated by this is valid.
 		//
@@ -898,20 +908,82 @@
 	rule.Build(name, desc)
 }
 
+// SignatureCsvSubset describes a subset of a monolithic flags file, i.e. either
+// out/soong/hiddenapi/hiddenapi-stub-flags.txt or out/soong/hiddenapi/hiddenapi-flags.csv
+type SignatureCsvSubset struct {
+	// The path to the CSV file containing hidden API flags.
+	//
+	// It has the dex member signature as the first column, with flags, one per column, in the
+	// subsequent columns.
+	CsvFile android.Path
+
+	// The path to the CSV file containing the signature patterns.
+	//
+	// It is a single column CSV file with the column containing a signature pattern.
+	SignaturePatternsFile android.Path
+}
+
+type SignatureCsvSubsets []SignatureCsvSubset
+
+func (s SignatureCsvSubsets) RelativeToTop() []string {
+	result := []string{}
+	for _, subset := range s {
+		result = append(result, fmt.Sprintf("%s:%s", subset.CsvFile.RelativeToTop(), subset.SignaturePatternsFile.RelativeToTop()))
+	}
+	return result
+}
+
+// buildRuleSignaturePatternsFile creates a rule to generate a file containing the set of signature
+// patterns that will select a subset of the monolithic flags.
+func buildRuleSignaturePatternsFile(ctx android.ModuleContext, flagsPath android.Path) android.Path {
+	patternsFile := android.PathForModuleOut(ctx, "modular-hiddenapi", "signature-patterns.csv")
+	// Create a rule to validate the output from the following rule.
+	rule := android.NewRuleBuilder(pctx, ctx)
+	rule.Command().
+		BuiltTool("signature_patterns").
+		FlagWithInput("--flags ", flagsPath).
+		FlagWithOutput("--output ", patternsFile)
+	rule.Build("hiddenAPISignaturePatterns", "hidden API signature patterns")
+
+	return patternsFile
+}
+
+// buildRuleRemoveBlockedFlag creates a rule that will remove entries from the input path which
+// only have blocked flags. It will not remove entries that have blocked as well as other flags,
+// e.g. blocked,core-platform-api.
+func buildRuleRemoveBlockedFlag(ctx android.BuilderContext, name string, desc string, inputPath android.Path, filteredPath android.WritablePath) {
+	rule := android.NewRuleBuilder(pctx, ctx)
+	rule.Command().
+		Text(`grep -vE "^[^,]+,blocked$"`).Input(inputPath).Text(">").Output(filteredPath).
+		// Grep's exit code depends on whether it finds anything. It is 0 (build success) when it finds
+		// something and 1 (build failure) when it does not and 2 (when it encounters an error).
+		// However, while it is unlikely it is not an error if this does not find any matches. The
+		// following will only run if the grep does not find something and in that case it will treat
+		// an exit code of 1 as success and anything else as failure.
+		Text("|| test $? -eq 1")
+	rule.Build(name, desc)
+}
+
 // buildRuleValidateOverlappingCsvFiles checks that the modular CSV files, i.e. the files generated
 // by the individual bootclasspath_fragment modules are subsets of the monolithic CSV file.
-func buildRuleValidateOverlappingCsvFiles(ctx android.BuilderContext, name string, desc string, monolithicFilePath android.WritablePath, modularFilePaths android.Paths) android.WritablePath {
+func buildRuleValidateOverlappingCsvFiles(ctx android.BuilderContext, name string, desc string, monolithicFilePath android.WritablePath, csvSubsets SignatureCsvSubsets) android.WritablePath {
 	// The file which is used to record that the flags file is valid.
 	validFile := pathForValidation(ctx, monolithicFilePath)
 
 	// Create a rule to validate the output from the following rule.
 	rule := android.NewRuleBuilder(pctx, ctx)
-	rule.Command().
+	command := rule.Command().
 		BuiltTool("verify_overlaps").
-		Input(monolithicFilePath).
-		Inputs(modularFilePaths).
-		// If validation passes then update the file that records that.
-		Text("&& touch").Output(validFile)
+		Input(monolithicFilePath)
+
+	for _, subset := range csvSubsets {
+		command.
+			Textf("%s:%s", subset.CsvFile, subset.SignaturePatternsFile).
+			Implicit(subset.CsvFile).Implicit(subset.SignaturePatternsFile)
+	}
+
+	// If validation passes then update the file that records that.
+	command.Text("&& touch").Output(validFile)
 	rule.Build(name+"Validation", desc+" validation")
 
 	return validFile
@@ -980,14 +1052,24 @@
 		encodedBootDexJarsByModule[name] = encodedDex
 	}
 
+	// Generate the filtered-stub-flags.csv file which contains the filtered stub flags that will be
+	// compared against the monolithic stub flags.
+	filteredStubFlagsCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "filtered-stub-flags.csv")
+	buildRuleRemoveBlockedFlag(ctx, "modularHiddenApiFilteredStubFlags", "modular hiddenapi filtered stub flags", stubFlagsCSV, filteredStubFlagsCSV)
+
+	// Generate the filtered-flags.csv file which contains the filtered flags that will be compared
+	// against the monolithic flags.
+	filteredFlagsCSV := android.PathForModuleOut(ctx, hiddenApiSubDir, "filtered-flags.csv")
+	buildRuleRemoveBlockedFlag(ctx, "modularHiddenApiFilteredFlags", "modular hiddenapi filtered flags", allFlagsCSV, filteredFlagsCSV)
+
 	// Store the paths in the info for use by other modules and sdk snapshot generation.
 	output := HiddenAPIOutput{
 		HiddenAPIFlagOutput: HiddenAPIFlagOutput{
-			StubFlagsPath:       stubFlagsCSV,
+			StubFlagsPath:       filteredStubFlagsCSV,
 			AnnotationFlagsPath: annotationFlagsCSV,
 			MetadataPath:        metadataCSV,
 			IndexPath:           indexCSV,
-			AllFlagsPath:        allFlagsCSV,
+			AllFlagsPath:        filteredFlagsCSV,
 		},
 		EncodedBootDexFilesByModule: encodedBootDexJarsByModule,
 	}
diff --git a/java/hiddenapi_monolithic.go b/java/hiddenapi_monolithic.go
index 52f0770..5956e3c 100644
--- a/java/hiddenapi_monolithic.go
+++ b/java/hiddenapi_monolithic.go
@@ -29,9 +29,6 @@
 	// that category.
 	FlagsFilesByCategory FlagFilesByCategory
 
-	// The paths to the generated stub-flags.csv files.
-	StubFlagsPaths android.Paths
-
 	// The paths to the generated annotation-flags.csv files.
 	AnnotationFlagsPaths android.Paths
 
@@ -41,8 +38,13 @@
 	// The paths to the generated index.csv files.
 	IndexPaths android.Paths
 
-	// The paths to the generated all-flags.csv files.
-	AllFlagsPaths android.Paths
+	// The subsets of the monolithic hiddenapi-stubs-flags.txt file that are provided by each
+	// bootclasspath_fragment modules.
+	StubFlagSubsets SignatureCsvSubsets
+
+	// The subsets of the monolithic hiddenapi-flags.csv file that are provided by each
+	// bootclasspath_fragment modules.
+	FlagSubsets SignatureCsvSubsets
 
 	// The classes jars from the libraries on the platform bootclasspath.
 	ClassesJars android.Paths
@@ -58,68 +60,34 @@
 	// Merge all the information from the classpathElements. The fragments form a DAG so it is possible that
 	// this will introduce duplicates so they will be resolved after processing all the classpathElements.
 	for _, element := range classpathElements {
-		var classesJars android.Paths
 		switch e := element.(type) {
 		case *ClasspathLibraryElement:
-			classesJars = retrieveClassesJarsFromModule(e.Module())
+			classesJars := retrieveClassesJarsFromModule(e.Module())
+			monolithicInfo.ClassesJars = append(monolithicInfo.ClassesJars, classesJars...)
 
 		case *ClasspathFragmentElement:
 			fragment := e.Module()
 			if ctx.OtherModuleHasProvider(fragment, HiddenAPIInfoProvider) {
 				info := ctx.OtherModuleProvider(fragment, HiddenAPIInfoProvider).(HiddenAPIInfo)
 				monolithicInfo.append(&info)
-
-				// If the bootclasspath fragment actually perform hidden API processing itself then use the
-				// CSV files it provides and do not bother processing the classesJars files. This ensures
-				// consistent behavior between source and prebuilt as prebuilt modules do not provide
-				// classesJars.
-				if info.AllFlagsPath != nil {
-					continue
-				}
+			} else {
+				ctx.ModuleErrorf("%s does not provide hidden API information", fragment)
 			}
-
-			classesJars = extractClassesJarsFromModules(e.Contents)
 		}
-
-		monolithicInfo.ClassesJars = append(monolithicInfo.ClassesJars, classesJars...)
 	}
 
-	// Dedup paths.
-	monolithicInfo.dedup()
-
 	return monolithicInfo
 }
 
 // append appends all the files from the supplied info to the corresponding files in this struct.
 func (i *MonolithicHiddenAPIInfo) append(other *HiddenAPIInfo) {
 	i.FlagsFilesByCategory.append(other.FlagFilesByCategory)
+	i.AnnotationFlagsPaths = append(i.AnnotationFlagsPaths, other.AnnotationFlagsPath)
+	i.MetadataPaths = append(i.MetadataPaths, other.MetadataPath)
+	i.IndexPaths = append(i.IndexPaths, other.IndexPath)
 
-	// The output may not be set if the bootclasspath_fragment has not yet been updated to support
-	// hidden API processing.
-	// TODO(b/179354495): Switch back to append once all bootclasspath_fragment modules have been
-	//  updated to support hidden API processing properly.
-	appendIfNotNil := func(paths android.Paths, path android.Path) android.Paths {
-		if path == nil {
-			return paths
-		}
-		return append(paths, path)
-	}
-	i.StubFlagsPaths = appendIfNotNil(i.StubFlagsPaths, other.StubFlagsPath)
-	i.AnnotationFlagsPaths = appendIfNotNil(i.AnnotationFlagsPaths, other.AnnotationFlagsPath)
-	i.MetadataPaths = appendIfNotNil(i.MetadataPaths, other.MetadataPath)
-	i.IndexPaths = appendIfNotNil(i.IndexPaths, other.IndexPath)
-	i.AllFlagsPaths = appendIfNotNil(i.AllFlagsPaths, other.AllFlagsPath)
-}
-
-// dedup removes duplicates in all the paths, while maintaining the order in which they were
-// appended.
-func (i *MonolithicHiddenAPIInfo) dedup() {
-	i.FlagsFilesByCategory.dedup()
-	i.StubFlagsPaths = android.FirstUniquePaths(i.StubFlagsPaths)
-	i.AnnotationFlagsPaths = android.FirstUniquePaths(i.AnnotationFlagsPaths)
-	i.MetadataPaths = android.FirstUniquePaths(i.MetadataPaths)
-	i.IndexPaths = android.FirstUniquePaths(i.IndexPaths)
-	i.AllFlagsPaths = android.FirstUniquePaths(i.AllFlagsPaths)
+	i.StubFlagSubsets = append(i.StubFlagSubsets, other.StubFlagSubset())
+	i.FlagSubsets = append(i.FlagSubsets, other.FlagSubset())
 }
 
 var MonolithicHiddenAPIInfoProvider = blueprint.NewProvider(MonolithicHiddenAPIInfo{})
diff --git a/java/jacoco.go b/java/jacoco.go
index 9162161..e11c2ce 100644
--- a/java/jacoco.go
+++ b/java/jacoco.go
@@ -94,7 +94,7 @@
 	if len(includes) > 0 {
 		specs += strings.Join(includes, " ")
 	} else {
-		specs += "**/*.class"
+		specs += "'**/*.class'"
 	}
 	return specs
 }
diff --git a/java/jacoco_test.go b/java/jacoco_test.go
index 91f0553..1882908 100644
--- a/java/jacoco_test.go
+++ b/java/jacoco_test.go
@@ -74,7 +74,7 @@
 		{
 			name:     "implicit wildcard",
 			includes: []string{},
-			out:      "**/*.class",
+			out:      "'**/*.class'",
 		},
 		{
 			name:     "only include",
diff --git a/java/java.go b/java/java.go
index bbed42d..1a052b4 100644
--- a/java/java.go
+++ b/java/java.go
@@ -21,7 +21,6 @@
 import (
 	"fmt"
 	"path/filepath"
-	"strings"
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
@@ -131,11 +130,19 @@
 			PropertyName: "java_boot_libs",
 			SupportsSdk:  true,
 		},
-		// Temporarily export implementation classes jar for java_boot_libs as it is required for the
-		// hiddenapi processing.
-		// TODO(b/179354495): Revert once hiddenapi processing has been modularized.
-		exportImplementationClassesJar,
-		sdkSnapshotFilePathForJar,
+		func(ctx android.SdkMemberContext, j *Library) android.Path {
+			// Java boot libs are only provided in the SDK to provide access to their dex implementation
+			// jar for use by dexpreopting and boot jars package check. They do not need to provide an
+			// actual implementation jar but the java_import will need a file that exists so just copy an
+			// empty file. Any attempt to use that file as a jar will cause a build error.
+			return ctx.SnapshotBuilder().EmptyFile()
+		},
+		func(osPrefix, name string) string {
+			// Create a special name for the implementation jar to try and provide some useful information
+			// to a developer that attempts to compile against this.
+			// TODO(b/175714559): Provide a proper error message in Soong not ninja.
+			return filepath.Join(osPrefix, "java_boot_libs", "snapshot", "jars", "are", "invalid", name+jarFileSuffix)
+		},
 		onlyCopyJarToSnapshot,
 	}
 
@@ -241,13 +248,24 @@
 
 type usesLibraryDependencyTag struct {
 	dependencyTag
-	sdkVersion int // SDK version in which the library appared as a standalone library.
+
+	// SDK version in which the library appared as a standalone library.
+	sdkVersion int
+
+	// If the dependency is optional or required.
+	optional bool
+
+	// Whether this is an implicit dependency inferred by Soong, or an explicit one added via
+	// `uses_libs`/`optional_uses_libs` properties.
+	implicit bool
 }
 
-func makeUsesLibraryDependencyTag(sdkVersion int) usesLibraryDependencyTag {
+func makeUsesLibraryDependencyTag(sdkVersion int, optional bool, implicit bool) usesLibraryDependencyTag {
 	return usesLibraryDependencyTag{
 		dependencyTag: dependencyTag{name: fmt.Sprintf("uses-library-%d", sdkVersion)},
 		sdkVersion:    sdkVersion,
+		optional:      optional,
+		implicit:      implicit,
 	}
 }
 
@@ -276,10 +294,6 @@
 	syspropPublicStubDepTag = dependencyTag{name: "sysprop public stub"}
 	jniInstallTag           = installDependencyTag{name: "jni install"}
 	binaryInstallTag        = installDependencyTag{name: "binary install"}
-	usesLibTag              = makeUsesLibraryDependencyTag(dexpreopt.AnySdkVersion)
-	usesLibCompat28Tag      = makeUsesLibraryDependencyTag(28)
-	usesLibCompat29Tag      = makeUsesLibraryDependencyTag(29)
-	usesLibCompat30Tag      = makeUsesLibraryDependencyTag(30)
 )
 
 func IsLibDepTag(depTag blueprint.DependencyTag) bool {
@@ -450,7 +464,7 @@
 
 var _ android.ApexModule = (*Library)(nil)
 
-// Provides access to the list of permitted packages from updatable boot jars.
+// Provides access to the list of permitted packages from apex boot jars.
 type PermittedPackagesForUpdatableBootJars interface {
 	PermittedPackagesForUpdatableBootJars() []string
 }
@@ -501,7 +515,7 @@
 		j.dexProperties.Uncompress_dex = proptools.BoolPtr(shouldUncompressDex(ctx, &j.dexpreopter))
 	}
 	j.dexpreopter.uncompressedDex = *j.dexProperties.Uncompress_dex
-	j.classLoaderContexts = make(dexpreopt.ClassLoaderContextMap)
+	j.classLoaderContexts = j.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
 	j.compile(ctx, nil)
 
 	// Collect the module directory for IDE info in java/jdeps.go.
@@ -520,6 +534,7 @@
 
 func (j *Library) DepsMutator(ctx android.BottomUpMutatorContext) {
 	j.deps(ctx)
+	j.usesLibrary.deps(ctx, false)
 }
 
 const (
@@ -559,8 +574,8 @@
 	copyEverythingToSnapshot = false
 )
 
-func (mt *librarySdkMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
-	mctx.AddVariationDependencies(nil, dependencyTag, names...)
+func (mt *librarySdkMemberType) AddDependencies(ctx android.SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) {
+	ctx.AddVariationDependencies(nil, dependencyTag, names...)
 }
 
 func (mt *librarySdkMemberType) IsInstance(module android.Module) bool {
@@ -860,8 +875,8 @@
 	android.SdkMemberTypeBase
 }
 
-func (mt *testSdkMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
-	mctx.AddVariationDependencies(nil, dependencyTag, names...)
+func (mt *testSdkMemberType) AddDependencies(ctx android.SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) {
+	ctx.AddVariationDependencies(nil, dependencyTag, names...)
 }
 
 func (mt *testSdkMemberType) IsInstance(module android.Module) bool {
@@ -1180,7 +1195,8 @@
 	properties ImportProperties
 
 	// output file containing classes.dex and resources
-	dexJarFile android.Path
+	dexJarFile        android.Path
+	dexJarInstallFile android.Path
 
 	combinedClasspathFile android.Path
 	classLoaderContexts   dexpreopt.ClassLoaderContextMap
@@ -1303,10 +1319,6 @@
 
 		// Save away the `deapexer` module on which this depends, if any.
 		if tag == android.DeapexerTag {
-			if deapexerModule != nil {
-				ctx.ModuleErrorf("Ambiguous duplicate deapexer module dependencies %q and %q",
-					deapexerModule.Name(), module.Name())
-			}
 			deapexerModule = module
 		}
 	})
@@ -1335,6 +1347,7 @@
 			di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo)
 			if dexOutputPath := di.PrebuiltExportPath(apexRootRelativePathToJavaLib(j.BaseModuleName())); dexOutputPath != nil {
 				j.dexJarFile = dexOutputPath
+				j.dexJarInstallFile = android.PathForModuleInPartitionInstall(ctx, "apex", ai.ApexVariationName, apexRootRelativePathToJavaLib(j.BaseModuleName()))
 
 				// Initialize the hiddenapi structure.
 				j.initHiddenAPI(ctx, dexOutputPath, outputFile, nil)
@@ -1375,6 +1388,7 @@
 			dexOutputFile = j.hiddenAPIEncodeDex(ctx, dexOutputFile)
 
 			j.dexJarFile = dexOutputFile
+			j.dexJarInstallFile = android.PathForModuleInstall(ctx, "framework", jarName)
 		}
 	}
 
@@ -1416,7 +1430,7 @@
 }
 
 func (j *Import) DexJarInstallPath() android.Path {
-	return nil
+	return j.dexJarInstallFile
 }
 
 func (j *Import) ClassLoaderContexts() dexpreopt.ClassLoaderContextMap {
@@ -1440,12 +1454,8 @@
 	if sdkSpec.Kind == android.SdkCore {
 		return nil
 	}
-	ver, err := sdkSpec.EffectiveVersion(ctx)
-	if err != nil {
-		return err
-	}
-	if ver.GreaterThan(sdkVersion) {
-		return fmt.Errorf("newer SDK(%v)", ver)
+	if sdkSpec.ApiLevel.GreaterThan(sdkVersion) {
+		return fmt.Errorf("newer SDK(%v)", sdkSpec.ApiLevel)
 	}
 	return nil
 }
@@ -1490,11 +1500,7 @@
 	// TODO(b/113562217): Extract the base module name from the Import name, often the Import name
 	// has a prefix "prebuilt_". Remove the prefix explicitly if needed until we find a better
 	// solution to get the Import name.
-	name := j.Name()
-	if strings.HasPrefix(name, removedPrefix) {
-		name = strings.TrimPrefix(name, removedPrefix)
-	}
-	return name
+	return android.RemoveOptionalPrebuiltPrefix(j.Name())
 }
 
 var _ android.PrebuiltInterface = (*Import)(nil)
@@ -1796,32 +1802,30 @@
 		return
 	}
 
-	// Find out if the dependency is either an SDK library or an ordinary library that is disguised
-	// as an SDK library by the means of `provides_uses_lib` property. If yes, the library is itself
-	// a <uses-library> and should be added as a node in the CLC tree, and its CLC should be added
-	// as subtree of that node. Otherwise the library is not a <uses_library> and should not be
-	// added to CLC, but the transitive <uses-library> dependencies from its CLC should be added to
-	// the current CLC.
-	var implicitSdkLib *string
-	comp, isComp := depModule.(SdkLibraryComponentDependency)
-	if isComp {
-		implicitSdkLib = comp.OptionalImplicitSdkLibrary()
-		// OptionalImplicitSdkLibrary() may be nil so need to fall through to ProvidesUsesLib().
-	}
-	if implicitSdkLib == nil {
-		if ulib, ok := depModule.(ProvidesUsesLib); ok {
-			implicitSdkLib = ulib.ProvidesUsesLib()
-		}
+	depName := android.RemoveOptionalPrebuiltPrefix(ctx.OtherModuleName(depModule))
+
+	var sdkLib *string
+	if lib, ok := depModule.(SdkLibraryDependency); ok && lib.sharedLibrary() {
+		// A shared SDK library. This should be added as a top-level CLC element.
+		sdkLib = &depName
+	} else if ulib, ok := depModule.(ProvidesUsesLib); ok {
+		// A non-SDK library disguised as an SDK library by the means of `provides_uses_lib`
+		// property. This should be handled in the same way as a shared SDK library.
+		sdkLib = ulib.ProvidesUsesLib()
 	}
 
 	depTag := ctx.OtherModuleDependencyTag(depModule)
-	if depTag == libTag || depTag == usesLibTag {
+	if depTag == libTag {
 		// Ok, propagate <uses-library> through non-static library dependencies.
+	} else if tag, ok := depTag.(usesLibraryDependencyTag); ok &&
+		tag.sdkVersion == dexpreopt.AnySdkVersion && tag.implicit {
+		// Ok, propagate <uses-library> through non-compatibility implicit <uses-library>
+		// dependencies.
 	} else if depTag == staticLibTag {
 		// Propagate <uses-library> through static library dependencies, unless it is a component
 		// library (such as stubs). Component libraries have a dependency on their SDK library,
 		// which should not be pulled just because of a static component library.
-		if implicitSdkLib != nil {
+		if sdkLib != nil {
 			return
 		}
 	} else {
@@ -1829,11 +1833,14 @@
 		return
 	}
 
-	if implicitSdkLib != nil {
-		clcMap.AddContext(ctx, dexpreopt.AnySdkVersion, *implicitSdkLib,
+	// If this is an SDK (or SDK-like) library, then it should be added as a node in the CLC tree,
+	// and its CLC should be added as subtree of that node. Otherwise the library is not a
+	// <uses_library> and should not be added to CLC, but the transitive <uses-library> dependencies
+	// from its CLC should be added to the current CLC.
+	if sdkLib != nil {
+		clcMap.AddContext(ctx, dexpreopt.AnySdkVersion, *sdkLib, false, true,
 			dep.DexJarBuildPath(), dep.DexJarInstallPath(), dep.ClassLoaderContexts())
 	} else {
-		depName := ctx.OtherModuleName(depModule)
 		clcMap.AddContextMap(dep.ClassLoaderContexts(), depName)
 	}
 }
diff --git a/java/java_test.go b/java/java_test.go
index bd373c1..8bb017f 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -441,7 +441,7 @@
 		}
 	`)
 
-	buildOS := android.BuildOs.String()
+	buildOS := ctx.Config().BuildOS.String()
 
 	bar := ctx.ModuleForTests("bar", buildOS+"_common")
 	barJar := bar.Output("bar.jar").Output.String()
@@ -478,7 +478,7 @@
 		}
 	`)
 
-	buildOS := android.BuildOs.String()
+	buildOS := ctx.Config().BuildOS.String()
 
 	foo := ctx.ModuleForTests("foo", buildOS+"_common").Module().(*TestHost)
 
@@ -523,7 +523,7 @@
 	}
 
 	// check that -g is not overridden for host modules
-	buildOS := android.BuildOs.String()
+	buildOS := result.Config.BuildOS.String()
 	hostBinary := result.ModuleForTests("host_binary", buildOS+"_common")
 	hostJavaFlags := hostBinary.Module().VariablesForTests()["javacFlags"]
 	if strings.Contains(hostJavaFlags, "-g:source,lines") {
@@ -1183,7 +1183,7 @@
 			break
 		}
 	}
-	if expected != android.StringPathRelativeToTop(ctx.Config().BuildDir(), got) {
+	if expected != android.StringPathRelativeToTop(ctx.Config().SoongOutDir(), got) {
 		t.Errorf("Unexpected patch-module flag for module %q - expected %q, but got %q", moduleName, expected, got)
 	}
 }
@@ -1371,7 +1371,7 @@
 		}
 	`)
 
-	buildOS := android.BuildOs.String()
+	buildOS := ctx.Config().BuildOS.String()
 
 	test := ctx.ModuleForTests("foo", buildOS+"_common").Module().(*TestHost)
 	entries := android.AndroidMkEntriesForTest(t, ctx, test)[0]
@@ -1387,8 +1387,100 @@
 		}
 	`)
 
-	buildOS := android.BuildOs.String()
+	buildOS := ctx.Config().BuildOS.String()
 	module := ctx.ModuleForTests("foo", buildOS+"_common").Module().(*TestHost)
 	assertDeepEquals(t, "Default installable value should be true.", proptools.BoolPtr(true),
 		module.properties.Installable)
 }
+
+func TestErrorproneEnabled(t *testing.T) {
+	ctx, _ := testJava(t, `
+		java_library {
+			name: "foo",
+			srcs: ["a.java"],
+			errorprone: {
+				enabled: true,
+			},
+		}
+	`)
+
+	javac := ctx.ModuleForTests("foo", "android_common").Description("javac")
+
+	// Test that the errorprone plugins are passed to javac
+	expectedSubstring := "-Xplugin:ErrorProne"
+	if !strings.Contains(javac.Args["javacFlags"], expectedSubstring) {
+		t.Errorf("expected javacFlags to contain %q, got %q", expectedSubstring, javac.Args["javacFlags"])
+	}
+
+	// Modules with errorprone { enabled: true } will include errorprone checks
+	// in the main javac build rule. Only when RUN_ERROR_PRONE is true will
+	// the explicit errorprone build rule be created.
+	errorprone := ctx.ModuleForTests("foo", "android_common").MaybeDescription("errorprone")
+	if errorprone.RuleParams.Description != "" {
+		t.Errorf("expected errorprone build rule to not exist, but it did")
+	}
+}
+
+func TestErrorproneDisabled(t *testing.T) {
+	bp := `
+		java_library {
+			name: "foo",
+			srcs: ["a.java"],
+			errorprone: {
+				enabled: false,
+			},
+		}
+	`
+	ctx := android.GroupFixturePreparers(
+		PrepareForTestWithJavaDefaultModules,
+		android.FixtureMergeEnv(map[string]string{
+			"RUN_ERROR_PRONE": "true",
+		}),
+	).RunTestWithBp(t, bp)
+
+	javac := ctx.ModuleForTests("foo", "android_common").Description("javac")
+
+	// Test that the errorprone plugins are not passed to javac, like they would
+	// be if enabled was true.
+	expectedSubstring := "-Xplugin:ErrorProne"
+	if strings.Contains(javac.Args["javacFlags"], expectedSubstring) {
+		t.Errorf("expected javacFlags to not contain %q, got %q", expectedSubstring, javac.Args["javacFlags"])
+	}
+
+	// Check that no errorprone build rule is created, like there would be
+	// if enabled was unset and RUN_ERROR_PRONE was true.
+	errorprone := ctx.ModuleForTests("foo", "android_common").MaybeDescription("errorprone")
+	if errorprone.RuleParams.Description != "" {
+		t.Errorf("expected errorprone build rule to not exist, but it did")
+	}
+}
+
+func TestErrorproneEnabledOnlyByEnvironmentVariable(t *testing.T) {
+	bp := `
+		java_library {
+			name: "foo",
+			srcs: ["a.java"],
+		}
+	`
+	ctx := android.GroupFixturePreparers(
+		PrepareForTestWithJavaDefaultModules,
+		android.FixtureMergeEnv(map[string]string{
+			"RUN_ERROR_PRONE": "true",
+		}),
+	).RunTestWithBp(t, bp)
+
+	javac := ctx.ModuleForTests("foo", "android_common").Description("javac")
+	errorprone := ctx.ModuleForTests("foo", "android_common").Description("errorprone")
+
+	// Check that the errorprone plugins are not passed to javac, because they
+	// will instead be passed to the separate errorprone compilation
+	expectedSubstring := "-Xplugin:ErrorProne"
+	if strings.Contains(javac.Args["javacFlags"], expectedSubstring) {
+		t.Errorf("expected javacFlags to not contain %q, got %q", expectedSubstring, javac.Args["javacFlags"])
+	}
+
+	// Check that the errorprone plugin is enabled
+	if !strings.Contains(errorprone.Args["javacFlags"], expectedSubstring) {
+		t.Errorf("expected errorprone to contain %q, got %q", expectedSubstring, javac.Args["javacFlags"])
+	}
+}
diff --git a/java/kotlin_test.go b/java/kotlin_test.go
index 1c146a1..db30696 100644
--- a/java/kotlin_test.go
+++ b/java/kotlin_test.go
@@ -15,10 +15,11 @@
 package java
 
 import (
-	"android/soong/android"
 	"strconv"
 	"strings"
 	"testing"
+
+	"android/soong/android"
 )
 
 func TestKotlin(t *testing.T) {
@@ -114,7 +115,7 @@
 	t.Run("", func(t *testing.T) {
 		ctx, _ := testJava(t, bp)
 
-		buildOS := android.BuildOs.String()
+		buildOS := ctx.Config().BuildOS.String()
 
 		kapt := ctx.ModuleForTests("foo", "android_common").Rule("kapt")
 		kotlinc := ctx.ModuleForTests("foo", "android_common").Rule("kotlinc")
@@ -182,10 +183,9 @@
 			android.FixtureMergeEnv(env),
 		).RunTestWithBp(t, bp)
 
-		buildOS := android.BuildOs.String()
+		buildOS := result.Config.BuildOS.String()
 
 		kapt := result.ModuleForTests("foo", "android_common").Rule("kapt")
-		//kotlinc := ctx.ModuleForTests("foo", "android_common").Rule("kotlinc")
 		javac := result.ModuleForTests("foo", "android_common").Description("javac")
 		errorprone := result.ModuleForTests("foo", "android_common").Description("errorprone")
 
diff --git a/java/lint_defaults.txt b/java/lint_defaults.txt
index 2de05b0..4bc0c5f 100644
--- a/java/lint_defaults.txt
+++ b/java/lint_defaults.txt
@@ -1,10 +1,37 @@
 # Treat LintError as fatal to catch invocation errors
 --fatal_check LintError
 
+# Checks which do not apply to the platform (implementation
+# in lint assumes that it's running on app code)
+
+--disable_check AnimatorKeep
+--disable_check AppBundleLocaleChanges
+--disable_check BlockedPrivateApi
+--disable_check CustomSplashScreen
+--disable_check CustomX509TrustManager
+--disable_check Deprecated
+--disable_check ExifInterface
+--disable_check HardwareIds
+--disable_check InvalidWakeLockTag
+--disable_check LibraryCustomView
+--disable_check MissingPermission
+--disable_check NonConstantResourceId
+--disable_check OldTargetApi
+--disable_check Override
+--disable_check PackageManagerGetSignatures
+--disable_check PrivateApi
+--disable_check ProtectedPermissions
+--disable_check QueryPermissionsNeeded
+--disable_check ScopedStorage
+--disable_check ServiceCast
+--disable_check SoonBlockedPrivateApi
+--disable_check SuspiciousImport
+--disable_check UnusedResources
+--disable_check ViewConstructor
+
 # Downgrade existing errors to warnings
 --warning_check AppCompatResource                  # 55 occurences in 10 modules
 --warning_check AppLinkUrlError                    # 111 occurences in 53 modules
---warning_check BlockedPrivateApi                  # 2 occurences in 2 modules
 --warning_check ByteOrderMark                      # 2 occurences in 2 modules
 --warning_check DuplicateActivity                  # 3 occurences in 3 modules
 --warning_check DuplicateDefinition                # 3623 occurences in 48 modules
@@ -22,9 +49,7 @@
 --warning_check Instantiatable                     # 145 occurences in 19 modules
 --warning_check InvalidPermission                  # 6 occurences in 4 modules
 --warning_check InvalidUsesTagAttribute            # 6 occurences in 2 modules
---warning_check InvalidWakeLockTag                 # 111 occurences in 37 modules
 --warning_check JavascriptInterface                # 3 occurences in 2 modules
---warning_check LibraryCustomView                  # 9 occurences in 4 modules
 --warning_check LogTagMismatch                     # 81 occurences in 13 modules
 --warning_check LongLogTag                         # 249 occurences in 12 modules
 --warning_check MenuTitle                          # 5 occurences in 4 modules
@@ -35,7 +60,6 @@
 --warning_check MissingLeanbackLauncher            # 3 occurences in 3 modules
 --warning_check MissingLeanbackSupport             # 2 occurences in 2 modules
 --warning_check MissingOnPlayFromSearch            # 1 occurences in 1 modules
---warning_check MissingPermission                  # 2071 occurences in 150 modules
 --warning_check MissingPrefix                      # 46 occurences in 41 modules
 --warning_check MissingQuantity                    # 100 occurences in 1 modules
 --warning_check MissingSuperCall                   # 121 occurences in 36 modules
@@ -47,9 +71,7 @@
 --warning_check ObjectAnimatorBinding              # 14 occurences in 5 modules
 --warning_check OnClick                            # 49 occurences in 21 modules
 --warning_check Orientation                        # 77 occurences in 19 modules
---warning_check Override                           # 385 occurences in 36 modules
 --warning_check ParcelCreator                      # 23 occurences in 2 modules
---warning_check ProtectedPermissions               # 2413 occurences in 381 modules
 --warning_check Range                              # 80 occurences in 28 modules
 --warning_check RecyclerView                       # 1 occurences in 1 modules
 --warning_check ReferenceType                      # 4 occurences in 1 modules
@@ -60,8 +82,6 @@
 --warning_check ResourceType                       # 137 occurences in 36 modules
 --warning_check RestrictedApi                      # 28 occurences in 5 modules
 --warning_check RtlCompat                          # 9 occurences in 6 modules
---warning_check ServiceCast                        # 3 occurences in 1 modules
---warning_check SoonBlockedPrivateApi              # 5 occurences in 3 modules
 --warning_check StringFormatInvalid                # 148 occurences in 11 modules
 --warning_check StringFormatMatches                # 4800 occurences in 30 modules
 --warning_check UnknownId                          # 8 occurences in 7 modules
@@ -74,7 +94,9 @@
 --warning_check WrongThread                        # 14 occurences in 6 modules
 --warning_check WrongViewCast                      # 1 occurences in 1 modules
 
-# TODO(b/158390965): remove this when lint doesn't crash
---disable_check HardcodedDebugMode
-
+--warning_check CoarseFineLocation
+--warning_check IntentFilterExportedReceiver
 --warning_check QueryAllPackagesPermission
+--warning_check RemoteViewLayout
+--warning_check SupportAnnotationUsage
+--warning_check UniqueConstants
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
index 10739b0..36baf7e 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -32,9 +32,9 @@
 // The tags used for the dependencies between the platform bootclasspath and any configured boot
 // jars.
 var (
-	platformBootclasspathArtBootJarDepTag          = bootclasspathDependencyTag{name: "art-boot-jar"}
-	platformBootclasspathNonUpdatableBootJarDepTag = bootclasspathDependencyTag{name: "non-updatable-boot-jar"}
-	platformBootclasspathUpdatableBootJarDepTag    = bootclasspathDependencyTag{name: "updatable-boot-jar"}
+	platformBootclasspathArtBootJarDepTag  = bootclasspathDependencyTag{name: "art-boot-jar"}
+	platformBootclasspathBootJarDepTag     = bootclasspathDependencyTag{name: "platform-boot-jar"}
+	platformBootclasspathApexBootJarDepTag = bootclasspathDependencyTag{name: "apex-boot-jar"}
 )
 
 type platformBootclasspathModule struct {
@@ -131,11 +131,11 @@
 	// Add dependencies on all the non-updatable module configured in the "boot" boot image. That does
 	// not include modules configured in the "art" boot image.
 	bootImageConfig := b.getImageConfig(ctx)
-	addDependenciesOntoBootImageModules(ctx, bootImageConfig.modules, platformBootclasspathNonUpdatableBootJarDepTag)
+	addDependenciesOntoBootImageModules(ctx, bootImageConfig.modules, platformBootclasspathBootJarDepTag)
 
-	// Add dependencies on all the updatable modules.
-	updatableModules := dexpreopt.GetGlobalConfig(ctx).UpdatableBootJars
-	addDependenciesOntoBootImageModules(ctx, updatableModules, platformBootclasspathUpdatableBootJarDepTag)
+	// Add dependencies on all the apex jars.
+	apexJars := dexpreopt.GetGlobalConfig(ctx).ApexBootJars
+	addDependenciesOntoBootImageModules(ctx, apexJars, platformBootclasspathApexBootJarDepTag)
 
 	// Add dependencies on all the fragments.
 	b.properties.BootclasspathFragmentsDepsProperties.addDependenciesOntoFragments(ctx)
@@ -163,16 +163,16 @@
 }
 
 func (b *platformBootclasspathModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	// Gather all the dependencies from the art, updatable and non-updatable boot jars.
+	// Gather all the dependencies from the art, platform, and apex boot jars.
 	artModules := gatherApexModulePairDepsWithTag(ctx, platformBootclasspathArtBootJarDepTag)
-	nonUpdatableModules := gatherApexModulePairDepsWithTag(ctx, platformBootclasspathNonUpdatableBootJarDepTag)
-	updatableModules := gatherApexModulePairDepsWithTag(ctx, platformBootclasspathUpdatableBootJarDepTag)
+	platformModules := gatherApexModulePairDepsWithTag(ctx, platformBootclasspathBootJarDepTag)
+	apexModules := gatherApexModulePairDepsWithTag(ctx, platformBootclasspathApexBootJarDepTag)
 
 	// Concatenate them all, in order as they would appear on the bootclasspath.
 	var allModules []android.Module
 	allModules = append(allModules, artModules...)
-	allModules = append(allModules, nonUpdatableModules...)
-	allModules = append(allModules, updatableModules...)
+	allModules = append(allModules, platformModules...)
+	allModules = append(allModules, apexModules...)
 	b.configuredModules = allModules
 
 	// Gather all the fragments dependencies.
@@ -180,8 +180,8 @@
 
 	// Check the configuration of the boot modules.
 	// ART modules are checked by the art-bootclasspath-fragment.
-	b.checkNonUpdatableModules(ctx, nonUpdatableModules)
-	b.checkUpdatableModules(ctx, updatableModules)
+	b.checkPlatformModules(ctx, platformModules)
+	b.checkApexModules(ctx, apexModules)
 
 	b.generateClasspathProtoBuildActions(ctx)
 
@@ -193,23 +193,40 @@
 		return
 	}
 
-	b.generateBootImageBuildActions(ctx, nonUpdatableModules, updatableModules)
+	b.generateBootImageBuildActions(ctx, platformModules, apexModules)
 }
 
 // Generate classpaths.proto config
 func (b *platformBootclasspathModule) generateClasspathProtoBuildActions(ctx android.ModuleContext) {
+	configuredJars := b.configuredJars(ctx)
 	// ART and platform boot jars must have a corresponding entry in DEX2OATBOOTCLASSPATH
-	classpathJars := configuredJarListToClasspathJars(ctx, b.ClasspathFragmentToConfiguredJarList(ctx), BOOTCLASSPATH, DEX2OATBOOTCLASSPATH)
-	b.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, classpathJars)
+	classpathJars := configuredJarListToClasspathJars(ctx, configuredJars, BOOTCLASSPATH, DEX2OATBOOTCLASSPATH)
+	b.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, configuredJars, classpathJars)
 }
 
-func (b *platformBootclasspathModule) ClasspathFragmentToConfiguredJarList(ctx android.ModuleContext) android.ConfiguredJarList {
-	return b.getImageConfig(ctx).modules
+func (b *platformBootclasspathModule) configuredJars(ctx android.ModuleContext) android.ConfiguredJarList {
+	// Include all non APEX jars
+	jars := b.getImageConfig(ctx).modules
+
+	// Include jars from APEXes that don't populate their classpath proto config.
+	remainingJars := dexpreopt.GetGlobalConfig(ctx).ApexBootJars
+	for _, fragment := range b.fragments {
+		info := ctx.OtherModuleProvider(fragment, ClasspathFragmentProtoContentInfoProvider).(ClasspathFragmentProtoContentInfo)
+		if info.ClasspathFragmentProtoGenerated {
+			remainingJars = remainingJars.RemoveList(info.ClasspathFragmentProtoContents)
+		}
+	}
+	for i := 0; i < remainingJars.Len(); i++ {
+		jars = jars.Append(remainingJars.Apex(i), remainingJars.Jar(i))
+	}
+
+	return jars
 }
 
-// checkNonUpdatableModules ensures that the non-updatable modules supplied are not part of an
-// updatable module.
-func (b *platformBootclasspathModule) checkNonUpdatableModules(ctx android.ModuleContext, modules []android.Module) {
+// checkPlatformModules ensures that the non-updatable modules supplied are not part of an
+// apex module.
+func (b *platformBootclasspathModule) checkPlatformModules(ctx android.ModuleContext, modules []android.Module) {
+	// TODO(satayev): change this check to only allow core-icu4j, all apex jars should not be here.
 	for _, m := range modules {
 		apexInfo := ctx.OtherModuleProvider(m, android.ApexInfoProvider).(android.ApexInfo)
 		fromUpdatableApex := apexInfo.Updatable
@@ -222,8 +239,8 @@
 	}
 }
 
-// checkUpdatableModules ensures that the updatable modules supplied are not from the platform.
-func (b *platformBootclasspathModule) checkUpdatableModules(ctx android.ModuleContext, modules []android.Module) {
+// checkApexModules ensures that the apex modules supplied are not from the platform.
+func (b *platformBootclasspathModule) checkApexModules(ctx android.ModuleContext, modules []android.Module) {
 	for _, m := range modules {
 		apexInfo := ctx.OtherModuleProvider(m, android.ApexInfoProvider).(android.ApexInfo)
 		fromUpdatableApex := apexInfo.Updatable
@@ -239,12 +256,12 @@
 				//  modules is complete.
 				if !ctx.Config().AlwaysUsePrebuiltSdks() {
 					// error: this jar is part of the platform
-					ctx.ModuleErrorf("module %q from platform is not allowed in the updatable boot jars list", name)
+					ctx.ModuleErrorf("module %q from platform is not allowed in the apex boot jars list", name)
 				}
 			} else {
 				// TODO(b/177892522): Treat this as an error.
 				// Cannot do that at the moment because framework-wifi and framework-tethering are in the
-				// PRODUCT_UPDATABLE_BOOT_JARS but not marked as updatable in AOSP.
+				// PRODUCT_APEX_BOOT_JARS but not marked as updatable in AOSP.
 			}
 		}
 	}
@@ -299,7 +316,7 @@
 
 	// Generate the monolithic stub-flags.csv file.
 	stubFlags := hiddenAPISingletonPaths(ctx).stubFlags
-	buildRuleToGenerateHiddenAPIStubFlagsFile(ctx, "platform-bootclasspath-monolithic-hiddenapi-stub-flags", "monolithic hidden API stub flags", stubFlags, bootDexJarByModule.bootDexJars(), input, monolithicInfo.StubFlagsPaths)
+	buildRuleToGenerateHiddenAPIStubFlagsFile(ctx, "platform-bootclasspath-monolithic-hiddenapi-stub-flags", "monolithic hidden API stub flags", stubFlags, bootDexJarByModule.bootDexJars(), input, monolithicInfo.StubFlagSubsets)
 
 	// Generate the annotation-flags.csv file from all the module annotations.
 	annotationFlags := android.PathForModuleOut(ctx, "hiddenapi-monolithic", "annotation-flags-from-classes.csv")
@@ -312,7 +329,7 @@
 	allAnnotationFlagFiles := android.Paths{annotationFlags}
 	allAnnotationFlagFiles = append(allAnnotationFlagFiles, monolithicInfo.AnnotationFlagsPaths...)
 	allFlags := hiddenAPISingletonPaths(ctx).flags
-	buildRuleToGenerateHiddenApiFlags(ctx, "hiddenAPIFlagsFile", "monolithic hidden API flags", allFlags, stubFlags, allAnnotationFlagFiles, monolithicInfo.FlagsFilesByCategory, monolithicInfo.AllFlagsPaths, android.OptionalPath{})
+	buildRuleToGenerateHiddenApiFlags(ctx, "hiddenAPIFlagsFile", "monolithic hidden API flags", allFlags, stubFlags, allAnnotationFlagFiles, monolithicInfo.FlagsFilesByCategory, monolithicInfo.FlagSubsets, android.OptionalPath{})
 
 	// Generate an intermediate monolithic hiddenapi-metadata.csv file directly from the annotations
 	// in the source code.
@@ -389,7 +406,7 @@
 }
 
 // generateBootImageBuildActions generates ninja rules related to the boot image creation.
-func (b *platformBootclasspathModule) generateBootImageBuildActions(ctx android.ModuleContext, nonUpdatableModules, updatableModules []android.Module) {
+func (b *platformBootclasspathModule) generateBootImageBuildActions(ctx android.ModuleContext, platformModules, apexModules []android.Module) {
 	// Force the GlobalSoongConfig to be created and cached for use by the dex_bootjars
 	// GenerateSingletonBuildActions method as it cannot create it for itself.
 	dexpreopt.GetGlobalSoongConfig(ctx)
@@ -407,17 +424,22 @@
 	// Generate the framework profile rule
 	bootFrameworkProfileRule(ctx, imageConfig)
 
-	// Generate the updatable bootclasspath packages rule.
-	generateUpdatableBcpPackagesRule(ctx, imageConfig, updatableModules)
+	// If always using prebuilt sdks then do not generate the updatable-bcp-packages.txt file as it
+	// will break because the prebuilts do not yet specify a permitted_packages property.
+	// TODO(b/193889859): Remove when the prebuilts have been updated.
+	if !ctx.Config().AlwaysUsePrebuiltSdks() {
+		// Generate the updatable bootclasspath packages rule.
+		generateUpdatableBcpPackagesRule(ctx, imageConfig, apexModules)
+	}
 
-	// Copy non-updatable module dex jars to their predefined locations.
-	nonUpdatableBootDexJarsByModule := extractEncodedDexJarsFromModules(ctx, nonUpdatableModules)
-	copyBootJarsToPredefinedLocations(ctx, nonUpdatableBootDexJarsByModule, imageConfig.dexPathsByModule)
+	// Copy platform module dex jars to their predefined locations.
+	platformBootDexJarsByModule := extractEncodedDexJarsFromModules(ctx, platformModules)
+	copyBootJarsToPredefinedLocations(ctx, platformBootDexJarsByModule, imageConfig.dexPathsByModule)
 
-	// Copy updatable module dex jars to their predefined locations.
-	config := GetUpdatableBootConfig(ctx)
-	updatableBootDexJarsByModule := extractEncodedDexJarsFromModules(ctx, updatableModules)
-	copyBootJarsToPredefinedLocations(ctx, updatableBootDexJarsByModule, config.dexPathsByModule)
+	// Copy apex module dex jars to their predefined locations.
+	config := GetApexBootConfig(ctx)
+	apexBootDexJarsByModule := extractEncodedDexJarsFromModules(ctx, apexModules)
+	copyBootJarsToPredefinedLocations(ctx, apexBootDexJarsByModule, config.dexPathsByModule)
 
 	// Build a profile for the image config and then use that to build the boot image.
 	profile := bootImageProfileRule(ctx, imageConfig)
diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go
index 712c2a2..0d8ebac 100644
--- a/java/platform_compat_config.go
+++ b/java/platform_compat_config.go
@@ -134,8 +134,8 @@
 	android.SdkMemberTypeBase
 }
 
-func (b *compatConfigMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
-	mctx.AddVariationDependencies(nil, dependencyTag, names...)
+func (b *compatConfigMemberType) AddDependencies(ctx android.SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) {
+	ctx.AddVariationDependencies(nil, dependencyTag, names...)
 }
 
 func (b *compatConfigMemberType) IsInstance(module android.Module) bool {
diff --git a/java/plugin_test.go b/java/plugin_test.go
index c7913d3..dc29b1c 100644
--- a/java/plugin_test.go
+++ b/java/plugin_test.go
@@ -15,7 +15,6 @@
 package java
 
 import (
-	"android/soong/android"
 	"testing"
 )
 
@@ -58,7 +57,7 @@
 		}
 	`)
 
-	buildOS := android.BuildOs.String()
+	buildOS := ctx.Config().BuildOS.String()
 
 	javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
 	turbine := ctx.ModuleForTests("foo", "android_common").MaybeRule("turbine")
@@ -98,7 +97,7 @@
 		}
 	`)
 
-	buildOS := android.BuildOs.String()
+	buildOS := ctx.Config().BuildOS.String()
 
 	javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
 	turbine := ctx.ModuleForTests("foo", "android_common").MaybeRule("turbine")
diff --git a/java/robolectric.go b/java/robolectric.go
index 9fe1f0e..f216425 100644
--- a/java/robolectric.go
+++ b/java/robolectric.go
@@ -83,6 +83,9 @@
 
 	testConfig android.Path
 	data       android.Paths
+
+	forceOSType   android.OsType
+	forceArchType android.ArchType
 }
 
 func (r *robolectricTest) TestSuites() []string {
@@ -115,6 +118,9 @@
 }
 
 func (r *robolectricTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	r.forceOSType = ctx.Config().BuildOS
+	r.forceArchType = ctx.Config().BuildArch
+
 	r.testConfig = tradefed.AutoGenRobolectricTestConfig(ctx, r.testProperties.Test_config,
 		r.testProperties.Test_config_template, r.testProperties.Test_suites,
 		r.testProperties.Auto_gen_config)
@@ -179,7 +185,7 @@
 			continue
 		} else if strings.HasSuffix(s, "/BaseRobolectricTest.java") {
 			continue
-		} else if strings.HasPrefix(s, "src/") {
+		} else {
 			s = strings.TrimPrefix(s, "src/")
 		}
 		r.tests = append(r.tests, s)
@@ -345,7 +351,7 @@
 func (r *robolectricTest) InstallBypassMake() bool  { return true }
 func (r *robolectricTest) InstallInTestcases() bool { return true }
 func (r *robolectricTest) InstallForceOS() (*android.OsType, *android.ArchType) {
-	return &android.BuildOs, &android.BuildArch
+	return &r.forceOSType, &r.forceArchType
 }
 
 func robolectricRuntimesFactory() android.Module {
@@ -366,6 +372,9 @@
 	props robolectricRuntimesProperties
 
 	runtimes []android.InstallPath
+
+	forceOSType   android.OsType
+	forceArchType android.ArchType
 }
 
 func (r *robolectricRuntimes) TestSuites() []string {
@@ -385,6 +394,9 @@
 		return
 	}
 
+	r.forceOSType = ctx.Config().BuildOS
+	r.forceArchType = ctx.Config().BuildArch
+
 	files := android.PathsForModuleSrc(ctx, r.props.Jars)
 
 	androidAllDir := android.PathForModuleInstall(ctx, "android-all")
@@ -415,5 +427,5 @@
 func (r *robolectricRuntimes) InstallBypassMake() bool  { return true }
 func (r *robolectricRuntimes) InstallInTestcases() bool { return true }
 func (r *robolectricRuntimes) InstallForceOS() (*android.OsType, *android.ArchType) {
-	return &android.BuildOs, &android.BuildArch
+	return &r.forceOSType, &r.forceArchType
 }
diff --git a/java/rro.go b/java/rro.go
index 2e58c04..0b4d091 100644
--- a/java/rro.go
+++ b/java/rro.go
@@ -90,6 +90,22 @@
 	Theme() string
 }
 
+// RRO's partition logic is different from the partition logic of other modules defined in soong/android/paths.go
+// The default partition for RRO is "/product" and not "/system"
+func rroPartition(ctx android.ModuleContext) string {
+	var partition string
+	if ctx.DeviceSpecific() {
+		partition = ctx.DeviceConfig().OdmPath()
+	} else if ctx.SocSpecific() {
+		partition = ctx.DeviceConfig().VendorPath()
+	} else if ctx.SystemExtSpecific() {
+		partition = ctx.DeviceConfig().SystemExtPath()
+	} else {
+		partition = ctx.DeviceConfig().ProductPath()
+	}
+	return partition
+}
+
 func (r *RuntimeResourceOverlay) DepsMutator(ctx android.BottomUpMutatorContext) {
 	sdkDep := decodeSdkDep(ctx, android.SdkContext(r))
 	if sdkDep.hasFrameworkLibs() {
@@ -137,7 +153,8 @@
 	r.certificate = certificates[0]
 
 	r.outputFile = signed
-	r.installDir = android.PathForModuleInstall(ctx, "overlay", String(r.properties.Theme))
+	partition := rroPartition(ctx)
+	r.installDir = android.PathForModuleInPartitionInstall(ctx, partition, "overlay", String(r.properties.Theme))
 	ctx.InstallFile(r.installDir, r.outputFile.Base(), r.outputFile)
 }
 
diff --git a/java/rro_test.go b/java/rro_test.go
index bad60bc..27abbe4 100644
--- a/java/rro_test.go
+++ b/java/rro_test.go
@@ -177,7 +177,7 @@
 
 	// Check device location.
 	path = android.AndroidMkEntriesForTest(t, ctx, m.Module())[0].EntryMap["LOCAL_MODULE_PATH"]
-	expectedPath = []string{shared.JoinPath("out/target/product/test_device/system/overlay")}
+	expectedPath = []string{shared.JoinPath("out/target/product/test_device/product/overlay")}
 	android.AssertStringPathsRelativeToTopEquals(t, "LOCAL_MODULE_PATH", config, expectedPath, path)
 }
 
@@ -343,3 +343,57 @@
 		})
 	}
 }
+
+func TestRuntimeResourceOverlayPartition(t *testing.T) {
+	bp := `
+		runtime_resource_overlay {
+			name: "device_specific",
+			device_specific: true,
+		}
+		runtime_resource_overlay {
+			name: "soc_specific",
+			soc_specific: true,
+		}
+		runtime_resource_overlay {
+			name: "system_ext_specific",
+			system_ext_specific: true,
+		}
+		runtime_resource_overlay {
+			name: "product_specific",
+			product_specific: true,
+		}
+		runtime_resource_overlay {
+			name: "default"
+		}
+	`
+	testCases := []struct {
+		name         string
+		expectedPath string
+	}{
+		{
+			name:         "device_specific",
+			expectedPath: "out/soong/target/product/test_device/odm/overlay",
+		},
+		{
+			name:         "soc_specific",
+			expectedPath: "out/soong/target/product/test_device/vendor/overlay",
+		},
+		{
+			name:         "system_ext_specific",
+			expectedPath: "out/soong/target/product/test_device/system_ext/overlay",
+		},
+		{
+			name:         "product_specific",
+			expectedPath: "out/soong/target/product/test_device/product/overlay",
+		},
+		{
+			name:         "default",
+			expectedPath: "out/soong/target/product/test_device/product/overlay",
+		},
+	}
+	for _, testCase := range testCases {
+		ctx, _ := testJava(t, bp)
+		mod := ctx.ModuleForTests(testCase.name, "android_common").Module().(*RuntimeResourceOverlay)
+		android.AssertPathRelativeToTopEquals(t, "Install dir is not correct for "+testCase.name, testCase.expectedPath, mod.installDir)
+	}
+}
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 8c66438..ce8f179 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -419,7 +419,7 @@
 	// local files that are used within user customized droiddoc options.
 	Droiddoc_option_files []string
 
-	// additional droiddoc options
+	// additional droiddoc options.
 	// Available variables for substitution:
 	//
 	//  $(location <label>): the path to the droiddoc_option_files with name <label>
@@ -981,13 +981,15 @@
 }
 
 // to satisfy SdkLibraryComponentDependency
-func (e *EmbeddableSdkLibraryComponent) OptionalImplicitSdkLibrary() *string {
-	return e.sdkLibraryComponentProperties.SdkLibraryToImplicitlyTrack
-}
-
-// to satisfy SdkLibraryComponentDependency
 func (e *EmbeddableSdkLibraryComponent) OptionalSdkLibraryImplementation() *string {
-	// Currently implementation library name is the same as the SDK library name.
+	// For shared libraries, this is the same as the SDK library name. If a Java library or app
+	// depends on a component library (e.g. a stub library) it still needs to know the name of the
+	// run-time library and the corresponding module that provides the implementation. This name is
+	// passed to manifest_fixer (to be added to AndroidManifest.xml) and added to CLC (to be used
+	// in dexpreopt).
+	//
+	// For non-shared SDK (component or not) libraries this returns `nil`, as they are not
+	// <uses-library> and should not be added to the manifest or to CLC.
 	return e.sdkLibraryComponentProperties.SdkLibraryToImplicitlyTrack
 }
 
@@ -999,12 +1001,6 @@
 	// SdkLibraryName returns the name of the java_sdk_library/_import module.
 	SdkLibraryName() *string
 
-	// The optional name of the sdk library that should be implicitly added to the
-	// AndroidManifest of an app that contains code which references the sdk library.
-	//
-	// Returns the name of the optional implicit SDK library or nil, if there isn't one.
-	OptionalImplicitSdkLibrary() *string
-
 	// The name of the implementation library for the optional SDK library or nil, if there isn't one.
 	OptionalSdkLibraryImplementation() *string
 }
@@ -1700,8 +1696,12 @@
 			path := path.Join(mctx.ModuleDir(), apiDir, scope.apiFilePrefix+api)
 			p := android.ExistentPathForSource(mctx, path)
 			if !p.Valid() {
-				mctx.ModuleErrorf("Current api file %#v doesn't exist", path)
-				missingCurrentApi = true
+				if mctx.Config().AllowMissingDependencies() {
+					mctx.AddMissingDependencies([]string{path})
+				} else {
+					mctx.ModuleErrorf("Current api file %#v doesn't exist", path)
+					missingCurrentApi = true
+				}
 			}
 		}
 	}
@@ -1923,8 +1923,12 @@
 	// Is nil if the source module does not exist.
 	xmlPermissionsFileModule *sdkLibraryXml
 
-	// Path to the dex implementation jar obtained from the prebuilt_apex, if any.
+	// Build path to the dex implementation jar obtained from the prebuilt_apex, if any.
 	dexJarFile android.Path
+
+	// Expected install file path of the source module(sdk_library)
+	// or dex implementation jar obtained from the prebuilt_apex, if any.
+	installFile android.Path
 }
 
 var _ SdkLibraryDependency = (*SdkLibraryImport)(nil)
@@ -2142,6 +2146,9 @@
 
 	var deapexerModule android.Module
 
+	// Assume that source module(sdk_library) is installed in /<sdk_library partition>/framework
+	module.installFile = android.PathForModuleInstall(ctx, "framework", module.Stem()+".jar")
+
 	// Record the paths to the prebuilt stubs library and stubs source.
 	ctx.VisitDirectDeps(func(to android.Module) {
 		tag := ctx.OtherModuleDependencyTag(to)
@@ -2170,10 +2177,6 @@
 
 		// Save away the `deapexer` module on which this depends, if any.
 		if tag == android.DeapexerTag {
-			if deapexerModule != nil {
-				ctx.ModuleErrorf("Ambiguous duplicate deapexer module dependencies %q and %q",
-					deapexerModule.Name(), to.Name())
-			}
 			deapexerModule = to
 		}
 	})
@@ -2205,6 +2208,7 @@
 			di := ctx.OtherModuleProvider(deapexerModule, android.DeapexerProvider).(android.DeapexerInfo)
 			if dexOutputPath := di.PrebuiltExportPath(apexRootRelativePathToJavaLib(module.BaseModuleName())); dexOutputPath != nil {
 				module.dexJarFile = dexOutputPath
+				module.installFile = android.PathForModuleInPartitionInstall(ctx, "apex", ai.ApexVariationName, apexRootRelativePathToJavaLib(module.BaseModuleName()))
 				module.initHiddenAPI(ctx, dexOutputPath, module.findScopePaths(apiScopePublic).stubsImplPath[0], nil)
 			} else {
 				// This should never happen as a variant for a prebuilt_apex is only created if the
@@ -2259,11 +2263,7 @@
 
 // to satisfy UsesLibraryDependency interface
 func (module *SdkLibraryImport) DexJarInstallPath() android.Path {
-	if module.implLibraryModule == nil {
-		return nil
-	} else {
-		return module.implLibraryModule.DexJarInstallPath()
-	}
+	return module.installFile
 }
 
 // to satisfy UsesLibraryDependency interface
@@ -2471,8 +2471,8 @@
 	android.SdkMemberTypeBase
 }
 
-func (s *sdkLibrarySdkMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
-	mctx.AddVariationDependencies(nil, dependencyTag, names...)
+func (s *sdkLibrarySdkMemberType) AddDependencies(ctx android.SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) {
+	ctx.AddVariationDependencies(nil, dependencyTag, names...)
 }
 
 func (s *sdkLibrarySdkMemberType) IsInstance(module android.Module) bool {
diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go
index 65af953..938bb28 100644
--- a/java/sdk_library_test.go
+++ b/java/sdk_library_test.go
@@ -122,7 +122,7 @@
 	result.ModuleForTests("foo.api.system.28", "")
 	result.ModuleForTests("foo.api.test.28", "")
 
-	exportedComponentsInfo := result.ModuleProvider(foo.Module(), ExportedComponentsInfoProvider).(ExportedComponentsInfo)
+	exportedComponentsInfo := result.ModuleProvider(foo.Module(), android.ExportedComponentsInfoProvider).(android.ExportedComponentsInfo)
 	expectedFooExportedComponents := []string{
 		"foo.stubs",
 		"foo.stubs.source",
@@ -156,8 +156,9 @@
 	// test if baz has exported SDK lib names foo and bar to qux
 	qux := result.ModuleForTests("qux", "android_common")
 	if quxLib, ok := qux.Module().(*Library); ok {
-		sdkLibs := quxLib.ClassLoaderContexts().UsesLibs()
-		android.AssertDeepEquals(t, "qux exports", []string{"foo", "bar", "fred", "quuz"}, sdkLibs)
+		requiredSdkLibs, optionalSdkLibs := quxLib.ClassLoaderContexts().UsesLibs()
+		android.AssertDeepEquals(t, "qux exports (required)", []string{"fred", "quuz", "foo", "bar"}, requiredSdkLibs)
+		android.AssertDeepEquals(t, "qux exports (optional)", []string{}, optionalSdkLibs)
 	}
 }
 
diff --git a/java/sdk_test.go b/java/sdk_test.go
index bb595a5..6d62130 100644
--- a/java/sdk_test.go
+++ b/java/sdk_test.go
@@ -255,9 +255,11 @@
 				` + testcase.properties + `
 			}`
 
-			variant := "android_common"
-			if testcase.host == android.Host {
-				variant = android.BuildOs.String() + "_common"
+			variant := func(result *android.TestResult) string {
+				if testcase.host == android.Host {
+					return result.Config.BuildOS.String() + "_common"
+				}
+				return "android_common"
 			}
 
 			convertModulesToPaths := func(cp []string) []string {
@@ -312,7 +314,7 @@
 			}
 
 			checkClasspath := func(t *testing.T, result *android.TestResult, isJava8 bool) {
-				foo := result.ModuleForTests("foo", variant)
+				foo := result.ModuleForTests("foo", variant(result))
 				javac := foo.Rule("javac")
 				var deps []string
 
@@ -376,7 +378,7 @@
 				checkClasspath(t, result, true /* isJava8 */)
 
 				if testcase.host != android.Host {
-					aidl := result.ModuleForTests("foo", variant).Rule("aidl")
+					aidl := result.ModuleForTests("foo", variant(result)).Rule("aidl")
 
 					android.AssertStringDoesContain(t, "aidl command", aidl.RuleParams.Command, testcase.aidl+" -I.")
 				}
@@ -389,7 +391,7 @@
 				checkClasspath(t, result, false /* isJava8 */)
 
 				if testcase.host != android.Host {
-					aidl := result.ModuleForTests("foo", variant).Rule("aidl")
+					aidl := result.ModuleForTests("foo", variant(result)).Rule("aidl")
 
 					android.AssertStringDoesContain(t, "aidl command", aidl.RuleParams.Command, testcase.aidl+" -I.")
 				}
diff --git a/java/system_modules.go b/java/system_modules.go
index d0dc74a..fec8eba 100644
--- a/java/system_modules.go
+++ b/java/system_modules.go
@@ -245,8 +245,8 @@
 	android.SdkMemberTypeBase
 }
 
-func (mt *systemModulesSdkMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
-	mctx.AddVariationDependencies(nil, dependencyTag, names...)
+func (mt *systemModulesSdkMemberType) AddDependencies(ctx android.SdkDependencyContext, dependencyTag blueprint.DependencyTag, names []string) {
+	ctx.AddVariationDependencies(nil, dependencyTag, names...)
 }
 
 func (mt *systemModulesSdkMemberType) IsInstance(module android.Module) bool {
diff --git a/java/systemserver_classpath_fragment.go b/java/systemserver_classpath_fragment.go
index a2006b7..003021c 100644
--- a/java/systemserver_classpath_fragment.go
+++ b/java/systemserver_classpath_fragment.go
@@ -48,13 +48,14 @@
 }
 
 func (p *platformSystemServerClasspathModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	classpathJars := configuredJarListToClasspathJars(ctx, p.ClasspathFragmentToConfiguredJarList(ctx), p.classpathType)
-	p.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, classpathJars)
+	configuredJars := p.configuredJars(ctx)
+	classpathJars := configuredJarListToClasspathJars(ctx, configuredJars, p.classpathType)
+	p.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, configuredJars, classpathJars)
 }
 
-func (p *platformSystemServerClasspathModule) ClasspathFragmentToConfiguredJarList(ctx android.ModuleContext) android.ConfiguredJarList {
-	global := dexpreopt.GetGlobalConfig(ctx)
-	return global.SystemServerJars
+func (p *platformSystemServerClasspathModule) configuredJars(ctx android.ModuleContext) android.ConfiguredJarList {
+	// TODO(satayev): include any apex jars that don't populate their classpath proto config.
+	return dexpreopt.GetGlobalConfig(ctx).SystemServerJars
 }
 
 type SystemServerClasspathModule struct {
@@ -64,6 +65,9 @@
 	ClasspathFragmentBase
 
 	properties systemServerClasspathFragmentProperties
+
+	// Collect the module directory for IDE info in java/jdeps.go.
+	modulePaths []string
 }
 
 func (s *SystemServerClasspathModule) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion android.ApiLevel) error {
@@ -91,19 +95,28 @@
 		ctx.PropertyErrorf("contents", "empty contents are not allowed")
 	}
 
-	classpathJars := configuredJarListToClasspathJars(ctx, s.ClasspathFragmentToConfiguredJarList(ctx), s.classpathType)
-	s.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, classpathJars)
+	configuredJars := s.configuredJars(ctx)
+	classpathJars := configuredJarListToClasspathJars(ctx, configuredJars, s.classpathType)
+	s.classpathFragmentBase().generateClasspathProtoBuildActions(ctx, configuredJars, classpathJars)
+
+	// Collect the module directory for IDE info in java/jdeps.go.
+	s.modulePaths = append(s.modulePaths, ctx.ModuleDir())
 }
 
-func (s *SystemServerClasspathModule) ClasspathFragmentToConfiguredJarList(ctx android.ModuleContext) android.ConfiguredJarList {
+func (s *SystemServerClasspathModule) configuredJars(ctx android.ModuleContext) android.ConfiguredJarList {
 	global := dexpreopt.GetGlobalConfig(ctx)
 
-	possibleUpdatableModules := gatherPossibleUpdatableModuleNamesAndStems(ctx, s.properties.Contents, systemServerClasspathFragmentContentDepTag)
+	possibleUpdatableModules := gatherPossibleApexModuleNamesAndStems(ctx, s.properties.Contents, systemServerClasspathFragmentContentDepTag)
+	jars, unknown := global.ApexSystemServerJars.Filter(possibleUpdatableModules)
+	// TODO(satayev): remove geotz ssc_fragment, since geotz is not part of SSCP anymore.
+	_, unknown = android.RemoveFromList("geotz", unknown)
 
-	// Only create configs for updatable boot jars. Non-updatable system server jars must be part of the
-	// platform_systemserverclasspath's classpath proto config to guarantee that they come before any
-	// updatable jars at runtime.
-	return global.UpdatableSystemServerJars.Filter(possibleUpdatableModules)
+	// For non test apexes, make sure that all contents are actually declared in make.
+	if global.ApexSystemServerJars.Len() > 0 && len(unknown) > 0 {
+		ctx.ModuleErrorf("%s in contents must also be declared in PRODUCT_UPDATABLE_SYSTEM_SERVER_JARS", unknown)
+	}
+
+	return jars
 }
 
 type systemServerClasspathFragmentContentDependencyTag struct {
@@ -135,3 +148,9 @@
 		ctx.AddDependency(module, systemServerClasspathFragmentContentDepTag, name)
 	}
 }
+
+// Collect information for opening IDE project files in java/jdeps.go.
+func (s *SystemServerClasspathModule) IDEInfo(dpInfo *android.IdeInfo) {
+	dpInfo.Deps = append(dpInfo.Deps, s.properties.Contents...)
+	dpInfo.Paths = append(dpInfo.Paths, s.modulePaths...)
+}
diff --git a/java/testing.go b/java/testing.go
index 3ef51bd..8860b45 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -214,15 +214,15 @@
 	)
 }
 
-// FixtureConfigureUpdatableBootJars configures the updatable boot jars in both the
+// FixtureConfigureApexBootJars configures the apex boot jars in both the
 // dexpreopt.GlobalConfig and Config.productVariables structs. As a side effect that enables
 // dexpreopt.
-func FixtureConfigureUpdatableBootJars(bootJars ...string) android.FixturePreparer {
+func FixtureConfigureApexBootJars(bootJars ...string) android.FixturePreparer {
 	return android.GroupFixturePreparers(
 		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.UpdatableBootJars = android.CreateTestConfiguredJarList(bootJars)
+			variables.ApexBootJars = android.CreateTestConfiguredJarList(bootJars)
 		}),
-		dexpreopt.FixtureSetUpdatableBootJars(bootJars...),
+		dexpreopt.FixtureSetApexBootJars(bootJars...),
 
 		// Add a fake dex2oatd module.
 		dexpreopt.PrepareForTestWithFakeDex2oatd,
@@ -364,6 +364,17 @@
 	android.AssertDeepEquals(t, fmt.Sprintf("%s modules", "platform-bootclasspath"), expected, pairs)
 }
 
+func CheckClasspathFragmentProtoContentInfoProvider(t *testing.T, result *android.TestResult, generated bool, contents, outputFilename, installDir string) {
+	t.Helper()
+	p := result.Module("platform-bootclasspath", "android_common").(*platformBootclasspathModule)
+	info := result.ModuleProvider(p, ClasspathFragmentProtoContentInfoProvider).(ClasspathFragmentProtoContentInfo)
+
+	android.AssertBoolEquals(t, "classpath proto generated", generated, info.ClasspathFragmentProtoGenerated)
+	android.AssertStringEquals(t, "classpath proto contents", contents, info.ClasspathFragmentProtoContents.String())
+	android.AssertStringEquals(t, "output filepath", outputFilename, info.ClasspathFragmentProtoOutput.Base())
+	android.AssertPathRelativeToTopEquals(t, "install filepath", installDir, info.ClasspathFragmentProtoInstallDir)
+}
+
 // ApexNamePairsFromModules returns the apex:module pair for the supplied modules.
 func ApexNamePairsFromModules(ctx *android.TestContext, modules []android.Module) []string {
 	pairs := []string{}
diff --git a/mk2rbc/Android.bp b/mk2rbc/Android.bp
new file mode 100644
index 0000000..4fa3eb6
--- /dev/null
+++ b/mk2rbc/Android.bp
@@ -0,0 +1,43 @@
+//
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+blueprint_go_binary {
+    name: "mk2rbc",
+    srcs: ["cmd/mk2rbc.go"],
+    deps: [
+        "mk2rbc-lib",
+        "androidmk-parser",
+    ],
+}
+
+bootstrap_go_package {
+    name: "mk2rbc-lib",
+    pkgPath: "android/soong/mk2rbc",
+    srcs: [
+        "android_products.go",
+        "config_variables.go",
+        "expr.go",
+        "mk2rbc.go",
+        "node.go",
+        "soong_variables.go",
+        "types.go",
+        "variable.go",
+    ],
+    deps: ["androidmk-parser"],
+}
diff --git a/mk2rbc/TODO b/mk2rbc/TODO
new file mode 100644
index 0000000..731deb6
--- /dev/null
+++ b/mk2rbc/TODO
@@ -0,0 +1,14 @@
+* Checking filter/filter-out results is incorrect if pattern contains '%'
+* Need heuristics to recognize that a variable is local. Propose to use lowercase.
+* Need heuristics for the local variable type. Propose '_list' suffix
+* Internal source tree has variables in the inherit-product macro argument. Handle it
+* Enumerate all environment variables that configuration files use.
+* Break mk2rbc.go into multiple files.
+* If variable's type is not yet known, try to divine it from the value assigned to it
+  (it may be a variable of the known type, or a function result)
+* ifneq (,$(VAR)) should translate to
+    if getattr(<>, "VAR", <default>):
+* Launcher file needs to have same suffix as the rest of the generated files
+* Implement $(shell) function
+* Write execution tests
+* Review all TODOs in mk2rbc.go
\ No newline at end of file
diff --git a/mk2rbc/android_products.go b/mk2rbc/android_products.go
new file mode 100644
index 0000000..de38391
--- /dev/null
+++ b/mk2rbc/android_products.go
@@ -0,0 +1,110 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package mk2rbc
+
+import (
+	"bytes"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"strings"
+
+	mkparser "android/soong/androidmk/parser"
+)
+
+// Implements mkparser.Scope, to be used by mkparser.Value.Value()
+type localDirEval struct {
+	localDir  string
+	hasErrors bool
+}
+
+func (l *localDirEval) Get(name string) string {
+	if name == "LOCAL_DIR" {
+		return l.localDir
+	}
+	l.hasErrors = true
+	return fmt.Sprintf("$(%s)", name)
+}
+
+func (l *localDirEval) Set(_, _ string) {
+}
+
+func (l *localDirEval) Call(_ string, _ []string) []string {
+	l.hasErrors = true
+	return []string{"$(call ...)"}
+}
+
+func (l *localDirEval) SetFunc(_ string, _ func([]string) []string) {
+}
+
+// UpdateProductConfigMap builds product configuration map.
+// The product configuration map maps a product name (i.e., the value of the
+// TARGET_PRODUCT variable) to the top-level configuration file.
+// In the Android's Make-based build machinery, the equivalent of the
+// product configuration map is $(PRODUCT_MAKEFILES), which is the list
+// of <product>:<configuration makefile> pairs (if <product>: is missing,
+// <product> is the basename of the configuration makefile).
+// UpdateProductConfigMap emulates this build logic by processing the
+// assignments to PRODUCT_MAKEFILES in the file passed to it.
+func UpdateProductConfigMap(configMap map[string]string, configMakefile string) error {
+	contents, err := ioutil.ReadFile(configMakefile)
+	if err != nil {
+		return err
+	}
+	parser := mkparser.NewParser(configMakefile, bytes.NewBuffer(contents))
+	nodes, errs := parser.Parse()
+	if len(errs) > 0 {
+		for _, e := range errs {
+			fmt.Fprintln(os.Stderr, "ERROR:", e)
+		}
+		return fmt.Errorf("cannot parse %s", configMakefile)
+	}
+
+	ldEval := &localDirEval{localDir: filepath.Dir(configMakefile)}
+
+	for _, node := range nodes {
+		// We are interested in assignments to 'PRODUCT_MAKEFILES'
+		asgn, ok := node.(*mkparser.Assignment)
+		if !ok {
+			continue
+		}
+		if !(asgn.Name.Const() && asgn.Name.Strings[0] == "PRODUCT_MAKEFILES") {
+			continue
+		}
+
+		// Resolve the references to $(LOCAL_DIR) in $(PRODUCT_MAKEFILES).
+		ldEval.hasErrors = false
+		value := asgn.Value.Value(ldEval)
+		if ldEval.hasErrors {
+			return fmt.Errorf("cannot evaluate %s", asgn.Value.Dump())
+		}
+		// Each item is either <product>:<configuration makefile>, or
+		// just <configuration makefile>
+		for _, token := range strings.Fields(value) {
+			var product, config_path string
+			if n := strings.Index(token, ":"); n >= 0 {
+				product = token[0:n]
+				config_path = token[n+1:]
+			} else {
+				config_path = token
+				product = filepath.Base(config_path)
+				product = strings.TrimSuffix(product, filepath.Ext(product))
+			}
+			configMap[product] = config_path
+		}
+	}
+	return nil
+}
diff --git a/mk2rbc/android_products_test.go b/mk2rbc/android_products_test.go
new file mode 100644
index 0000000..f8c930a
--- /dev/null
+++ b/mk2rbc/android_products_test.go
@@ -0,0 +1,38 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package mk2rbc
+
+import (
+	"path/filepath"
+	"reflect"
+	"testing"
+)
+
+func TestProductsMakefile(t *testing.T) {
+	testDir := getTestDirectory()
+	abspath := func(relPath string) string { return filepath.Join(testDir, relPath) }
+	actualProducts := make(map[string]string)
+	if err := UpdateProductConfigMap(actualProducts, abspath("android_products.mk.test")); err != nil {
+		t.Fatal(err)
+	}
+	expectedProducts := map[string]string{
+		"aosp_cf_x86_tv": abspath("vsoc_x86/tv/device.mk"),
+		"aosp_tv_arm":    abspath("aosp_tv_arm.mk"),
+		"aosp_tv_arm64":  abspath("aosp_tv_arm64.mk"),
+	}
+	if !reflect.DeepEqual(actualProducts, expectedProducts) {
+		t.Errorf("\nExpected: %v\n  Actual: %v", expectedProducts, actualProducts)
+	}
+}
diff --git a/mk2rbc/cmd/mk2rbc.go b/mk2rbc/cmd/mk2rbc.go
new file mode 100644
index 0000000..72525c4
--- /dev/null
+++ b/mk2rbc/cmd/mk2rbc.go
@@ -0,0 +1,547 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// The application to convert product configuration makefiles to Starlark.
+// Converts either given list of files (and optionally the dependent files
+// of the same kind), or all all product configuration makefiles in the
+// given source tree.
+// Previous version of a converted file can be backed up.
+// Optionally prints detailed statistics at the end.
+package main
+
+import (
+	"bufio"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"regexp"
+	"runtime/debug"
+	"runtime/pprof"
+	"sort"
+	"strings"
+	"time"
+
+	"android/soong/androidmk/parser"
+	"android/soong/mk2rbc"
+)
+
+var (
+	rootDir = flag.String("root", ".", "the value of // for load paths")
+	// TODO(asmundak): remove this option once there is a consensus on suffix
+	suffix   = flag.String("suffix", ".rbc", "generated files' suffix")
+	dryRun   = flag.Bool("dry_run", false, "dry run")
+	recurse  = flag.Bool("convert_dependents", false, "convert all dependent files")
+	mode     = flag.String("mode", "", `"backup" to back up existing files, "write" to overwrite them`)
+	warn     = flag.Bool("warnings", false, "warn about partially failed conversions")
+	verbose  = flag.Bool("v", false, "print summary")
+	errstat  = flag.Bool("error_stat", false, "print error statistics")
+	traceVar = flag.String("trace", "", "comma-separated list of variables to trace")
+	// TODO(asmundak): this option is for debugging
+	allInSource           = flag.Bool("all", false, "convert all product config makefiles in the tree under //")
+	outputTop             = flag.String("outdir", "", "write output files into this directory hierarchy")
+	launcher              = flag.String("launcher", "", "generated launcher path. If set, the non-flag argument is _product_name_")
+	printProductConfigMap = flag.Bool("print_product_config_map", false, "print product config map and exit")
+	cpuProfile            = flag.String("cpu_profile", "", "write cpu profile to file")
+	traceCalls            = flag.Bool("trace_calls", false, "trace function calls")
+)
+
+func init() {
+	// Poor man's flag aliasing: works, but the usage string is ugly and
+	// both flag and its alias can be present on the command line
+	flagAlias := func(target string, alias string) {
+		if f := flag.Lookup(target); f != nil {
+			flag.Var(f.Value, alias, "alias for --"+f.Name)
+			return
+		}
+		quit("cannot alias unknown flag " + target)
+	}
+	flagAlias("suffix", "s")
+	flagAlias("root", "d")
+	flagAlias("dry_run", "n")
+	flagAlias("convert_dependents", "r")
+	flagAlias("warnings", "w")
+	flagAlias("error_stat", "e")
+}
+
+var backupSuffix string
+var tracedVariables []string
+var errorLogger = errorsByType{data: make(map[string]datum)}
+var makefileFinder = &LinuxMakefileFinder{}
+
+func main() {
+	flag.Usage = func() {
+		cmd := filepath.Base(os.Args[0])
+		fmt.Fprintf(flag.CommandLine.Output(),
+			"Usage: %[1]s flags file...\n"+
+				"or:    %[1]s flags --launcher=PATH PRODUCT\n", cmd)
+		flag.PrintDefaults()
+	}
+	flag.Parse()
+
+	// Delouse
+	if *suffix == ".mk" {
+		quit("cannot use .mk as generated file suffix")
+	}
+	if *suffix == "" {
+		quit("suffix cannot be empty")
+	}
+	if *outputTop != "" {
+		if err := os.MkdirAll(*outputTop, os.ModeDir+os.ModePerm); err != nil {
+			quit(err)
+		}
+		s, err := filepath.Abs(*outputTop)
+		if err != nil {
+			quit(err)
+		}
+		*outputTop = s
+	}
+	if *allInSource && len(flag.Args()) > 0 {
+		quit("file list cannot be specified when -all is present")
+	}
+	if *allInSource && *launcher != "" {
+		quit("--all and --launcher are mutually exclusive")
+	}
+
+	// Flag-driven adjustments
+	if (*suffix)[0] != '.' {
+		*suffix = "." + *suffix
+	}
+	if *mode == "backup" {
+		backupSuffix = time.Now().Format("20060102150405")
+	}
+	if *traceVar != "" {
+		tracedVariables = strings.Split(*traceVar, ",")
+	}
+
+	if *cpuProfile != "" {
+		f, err := os.Create(*cpuProfile)
+		if err != nil {
+			quit(err)
+		}
+		pprof.StartCPUProfile(f)
+		defer pprof.StopCPUProfile()
+	}
+	// Find out global variables
+	getConfigVariables()
+	getSoongVariables()
+
+	if *printProductConfigMap {
+		productConfigMap := buildProductConfigMap()
+		var products []string
+		for p := range productConfigMap {
+			products = append(products, p)
+		}
+		sort.Strings(products)
+		for _, p := range products {
+			fmt.Println(p, productConfigMap[p])
+		}
+		os.Exit(0)
+	}
+
+	// Convert!
+	ok := true
+	if *launcher != "" {
+		if len(flag.Args()) != 1 {
+			quit(fmt.Errorf("a launcher can be generated only for a single product"))
+		}
+		product := flag.Args()[0]
+		productConfigMap := buildProductConfigMap()
+		path, found := productConfigMap[product]
+		if !found {
+			quit(fmt.Errorf("cannot generate configuration launcher for %s, it is not a known product",
+				product))
+		}
+		ok = convertOne(path) && ok
+		err := writeGenerated(*launcher, mk2rbc.Launcher(outputFilePath(path), mk2rbc.MakePath2ModuleName(path)))
+		if err != nil {
+			fmt.Fprintf(os.Stderr, "%s:%s", path, err)
+			ok = false
+		}
+
+	} else {
+		files := flag.Args()
+		if *allInSource {
+			productConfigMap := buildProductConfigMap()
+			for _, path := range productConfigMap {
+				files = append(files, path)
+			}
+		}
+		for _, mkFile := range files {
+			ok = convertOne(mkFile) && ok
+		}
+	}
+
+	printStats()
+	if *errstat {
+		errorLogger.printStatistics()
+	}
+	if !ok {
+		os.Exit(1)
+	}
+}
+
+func quit(s interface{}) {
+	fmt.Fprintln(os.Stderr, s)
+	os.Exit(2)
+}
+
+func buildProductConfigMap() map[string]string {
+	const androidProductsMk = "AndroidProducts.mk"
+	// Build the list of AndroidProducts.mk files: it's
+	// build/make/target/product/AndroidProducts.mk plus
+	// device/**/AndroidProducts.mk
+	targetAndroidProductsFile := filepath.Join(*rootDir, "build", "make", "target", "product", androidProductsMk)
+	if _, err := os.Stat(targetAndroidProductsFile); err != nil {
+		fmt.Fprintf(os.Stderr, "%s: %s\n(hint: %s is not a source tree root)\n",
+			targetAndroidProductsFile, err, *rootDir)
+	}
+	productConfigMap := make(map[string]string)
+	if err := mk2rbc.UpdateProductConfigMap(productConfigMap, targetAndroidProductsFile); err != nil {
+		fmt.Fprintf(os.Stderr, "%s: %s\n", targetAndroidProductsFile, err)
+	}
+	_ = filepath.Walk(filepath.Join(*rootDir, "device"),
+		func(path string, info os.FileInfo, err error) error {
+			if info.IsDir() || filepath.Base(path) != androidProductsMk {
+				return nil
+			}
+			if err2 := mk2rbc.UpdateProductConfigMap(productConfigMap, path); err2 != nil {
+				fmt.Fprintf(os.Stderr, "%s: %s\n", path, err)
+				// Keep going, we want to find all such errors in a single run
+			}
+			return nil
+		})
+	return productConfigMap
+}
+
+func getConfigVariables() {
+	path := filepath.Join(*rootDir, "build", "make", "core", "product.mk")
+	if err := mk2rbc.FindConfigVariables(path, mk2rbc.KnownVariables); err != nil {
+		quit(fmt.Errorf("%s\n(check --root[=%s], it should point to the source root)",
+			err, *rootDir))
+	}
+}
+
+// Implements mkparser.Scope, to be used by mkparser.Value.Value()
+type fileNameScope struct {
+	mk2rbc.ScopeBase
+}
+
+func (s fileNameScope) Get(name string) string {
+	if name != "BUILD_SYSTEM" {
+		return fmt.Sprintf("$(%s)", name)
+	}
+	return filepath.Join(*rootDir, "build", "make", "core")
+}
+
+func getSoongVariables() {
+	path := filepath.Join(*rootDir, "build", "make", "core", "soong_config.mk")
+	err := mk2rbc.FindSoongVariables(path, fileNameScope{}, mk2rbc.KnownVariables)
+	if err != nil {
+		quit(err)
+	}
+}
+
+var converted = make(map[string]*mk2rbc.StarlarkScript)
+
+//goland:noinspection RegExpRepeatedSpace
+var cpNormalizer = regexp.MustCompile(
+	"#  Copyright \\(C\\) 20.. The Android Open Source Project")
+
+const cpNormalizedCopyright = "#  Copyright (C) 20xx The Android Open Source Project"
+const copyright = `#
+#  Copyright (C) 20xx The Android Open Source Project
+#
+#  Licensed under the Apache License, Version 2.0 (the "License");
+#  you may not use this file except in compliance with the License.
+#  You may obtain a copy of the License at
+#
+#       http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+#
+`
+
+// Convert a single file.
+// Write the result either to the same directory, to the same place in
+// the output hierarchy, or to the stdout.
+// Optionally, recursively convert the files this one includes by
+// $(call inherit-product) or an include statement.
+func convertOne(mkFile string) (ok bool) {
+	if v, ok := converted[mkFile]; ok {
+		return v != nil
+	}
+	converted[mkFile] = nil
+	defer func() {
+		if r := recover(); r != nil {
+			ok = false
+			fmt.Fprintf(os.Stderr, "%s: panic while converting: %s\n%s\n", mkFile, r, debug.Stack())
+		}
+	}()
+
+	mk2starRequest := mk2rbc.Request{
+		MkFile:             mkFile,
+		Reader:             nil,
+		RootDir:            *rootDir,
+		OutputDir:          *outputTop,
+		OutputSuffix:       *suffix,
+		TracedVariables:    tracedVariables,
+		TraceCalls:         *traceCalls,
+		WarnPartialSuccess: *warn,
+		SourceFS:           os.DirFS(*rootDir),
+		MakefileFinder:     makefileFinder,
+	}
+	if *errstat {
+		mk2starRequest.ErrorLogger = errorLogger
+	}
+	ss, err := mk2rbc.Convert(mk2starRequest)
+	if err != nil {
+		fmt.Fprintln(os.Stderr, mkFile, ": ", err)
+		return false
+	}
+	script := ss.String()
+	outputPath := outputFilePath(mkFile)
+
+	if *dryRun {
+		fmt.Printf("==== %s ====\n", outputPath)
+		// Print generated script after removing the copyright header
+		outText := cpNormalizer.ReplaceAllString(script, cpNormalizedCopyright)
+		fmt.Println(strings.TrimPrefix(outText, copyright))
+	} else {
+		if err := maybeBackup(outputPath); err != nil {
+			fmt.Fprintln(os.Stderr, err)
+			return false
+		}
+		if err := writeGenerated(outputPath, script); err != nil {
+			fmt.Fprintln(os.Stderr, err)
+			return false
+		}
+	}
+	ok = true
+	if *recurse {
+		for _, sub := range ss.SubConfigFiles() {
+			// File may be absent if it is a conditional load
+			if _, err := os.Stat(sub); os.IsNotExist(err) {
+				continue
+			}
+			ok = convertOne(sub) && ok
+		}
+	}
+	converted[mkFile] = ss
+	return ok
+}
+
+// Optionally saves the previous version of the generated file
+func maybeBackup(filename string) error {
+	stat, err := os.Stat(filename)
+	if os.IsNotExist(err) {
+		return nil
+	}
+	if !stat.Mode().IsRegular() {
+		return fmt.Errorf("%s exists and is not a regular file", filename)
+	}
+	switch *mode {
+	case "backup":
+		return os.Rename(filename, filename+backupSuffix)
+	case "write":
+		return os.Remove(filename)
+	default:
+		return fmt.Errorf("%s already exists, use --mode option", filename)
+	}
+}
+
+func outputFilePath(mkFile string) string {
+	path := strings.TrimSuffix(mkFile, filepath.Ext(mkFile)) + *suffix
+	if *outputTop != "" {
+		path = filepath.Join(*outputTop, path)
+	}
+	return path
+}
+
+func writeGenerated(path string, contents string) error {
+	if err := os.MkdirAll(filepath.Dir(path), os.ModeDir|os.ModePerm); err != nil {
+		return err
+	}
+	if err := ioutil.WriteFile(path, []byte(contents), 0644); err != nil {
+		return err
+	}
+	return nil
+}
+
+func printStats() {
+	var sortedFiles []string
+	if !*warn && !*verbose {
+		return
+	}
+	for p := range converted {
+		sortedFiles = append(sortedFiles, p)
+	}
+	sort.Strings(sortedFiles)
+
+	nOk, nPartial, nFailed := 0, 0, 0
+	for _, f := range sortedFiles {
+		if converted[f] == nil {
+			nFailed++
+		} else if converted[f].HasErrors() {
+			nPartial++
+		} else {
+			nOk++
+		}
+	}
+	if *warn {
+		if nPartial > 0 {
+			fmt.Fprintf(os.Stderr, "Conversion was partially successful for:\n")
+			for _, f := range sortedFiles {
+				if ss := converted[f]; ss != nil && ss.HasErrors() {
+					fmt.Fprintln(os.Stderr, "  ", f)
+				}
+			}
+		}
+
+		if nFailed > 0 {
+			fmt.Fprintf(os.Stderr, "Conversion failed for files:\n")
+			for _, f := range sortedFiles {
+				if converted[f] == nil {
+					fmt.Fprintln(os.Stderr, "  ", f)
+				}
+			}
+		}
+	}
+	if *verbose {
+		fmt.Fprintf(os.Stderr, "%-16s%5d\n", "Succeeded:", nOk)
+		fmt.Fprintf(os.Stderr, "%-16s%5d\n", "Partial:", nPartial)
+		fmt.Fprintf(os.Stderr, "%-16s%5d\n", "Failed:", nFailed)
+	}
+}
+
+type datum struct {
+	count          int
+	formattingArgs []string
+}
+
+type errorsByType struct {
+	data map[string]datum
+}
+
+func (ebt errorsByType) NewError(message string, node parser.Node, args ...interface{}) {
+	v, exists := ebt.data[message]
+	if exists {
+		v.count++
+	} else {
+		v = datum{1, nil}
+	}
+	if strings.Contains(message, "%s") {
+		var newArg1 string
+		if len(args) == 0 {
+			panic(fmt.Errorf(`%s has %%s but args are missing`, message))
+		}
+		newArg1 = fmt.Sprint(args[0])
+		if message == "unsupported line" {
+			newArg1 = node.Dump()
+		} else if message == "unsupported directive %s" {
+			if newArg1 == "include" || newArg1 == "-include" {
+				newArg1 = node.Dump()
+			}
+		}
+		v.formattingArgs = append(v.formattingArgs, newArg1)
+	}
+	ebt.data[message] = v
+}
+
+func (ebt errorsByType) printStatistics() {
+	if len(ebt.data) > 0 {
+		fmt.Fprintln(os.Stderr, "Error counts:")
+	}
+	for message, data := range ebt.data {
+		if len(data.formattingArgs) == 0 {
+			fmt.Fprintf(os.Stderr, "%4d %s\n", data.count, message)
+			continue
+		}
+		itemsByFreq, count := stringsWithFreq(data.formattingArgs, 30)
+		fmt.Fprintf(os.Stderr, "%4d %s [%d unique items]:\n", data.count, message, count)
+		fmt.Fprintln(os.Stderr, "      ", itemsByFreq)
+	}
+}
+
+func stringsWithFreq(items []string, topN int) (string, int) {
+	freq := make(map[string]int)
+	for _, item := range items {
+		freq[strings.TrimPrefix(strings.TrimSuffix(item, "]"), "[")]++
+	}
+	var sorted []string
+	for item := range freq {
+		sorted = append(sorted, item)
+	}
+	sort.Slice(sorted, func(i int, j int) bool {
+		return freq[sorted[i]] > freq[sorted[j]]
+	})
+	sep := ""
+	res := ""
+	for i, item := range sorted {
+		if i >= topN {
+			res += " ..."
+			break
+		}
+		count := freq[item]
+		if count > 1 {
+			res += fmt.Sprintf("%s%s(%d)", sep, item, count)
+		} else {
+			res += fmt.Sprintf("%s%s", sep, item)
+		}
+		sep = ", "
+	}
+	return res, len(sorted)
+}
+
+type LinuxMakefileFinder struct {
+	cachedRoot      string
+	cachedMakefiles []string
+}
+
+func (l *LinuxMakefileFinder) Find(root string) []string {
+	if l.cachedMakefiles != nil && l.cachedRoot == root {
+		return l.cachedMakefiles
+	}
+	l.cachedRoot = root
+	l.cachedMakefiles = make([]string, 0)
+
+	// Return all *.mk files but not in hidden directories.
+
+	// NOTE(asmundak): as it turns out, even the WalkDir (which is an _optimized_ directory tree walker)
+	// is about twice slower than running `find` command (14s vs 6s on the internal Android source tree).
+	common_args := []string{"!", "-type", "d", "-name", "*.mk", "!", "-path", "*/.*/*"}
+	if root != "" {
+		common_args = append([]string{root}, common_args...)
+	}
+	cmd := exec.Command("/usr/bin/find", common_args...)
+	stdout, err := cmd.StdoutPipe()
+	if err == nil {
+		err = cmd.Start()
+	}
+	if err != nil {
+		panic(fmt.Errorf("cannot get the output from %s: %s", cmd, err))
+	}
+	scanner := bufio.NewScanner(stdout)
+	for scanner.Scan() {
+		l.cachedMakefiles = append(l.cachedMakefiles, strings.TrimPrefix(scanner.Text(), "./"))
+	}
+	stdout.Close()
+	return l.cachedMakefiles
+}
diff --git a/mk2rbc/config_variables.go b/mk2rbc/config_variables.go
new file mode 100644
index 0000000..dac509c
--- /dev/null
+++ b/mk2rbc/config_variables.go
@@ -0,0 +1,67 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package mk2rbc
+
+import (
+	"bytes"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"strings"
+
+	mkparser "android/soong/androidmk/parser"
+)
+
+// Extracts the list of product config variables from a file, calling
+// given registrar for each variable.
+func FindConfigVariables(mkFile string, vr variableRegistrar) error {
+	mkContents, err := ioutil.ReadFile(mkFile)
+	if err != nil {
+		return err
+	}
+	parser := mkparser.NewParser(mkFile, bytes.NewBuffer(mkContents))
+	nodes, errs := parser.Parse()
+	if len(errs) > 0 {
+		for _, e := range errs {
+			fmt.Fprintln(os.Stderr, "ERROR:", e)
+		}
+		return fmt.Errorf("cannot parse %s", mkFile)
+	}
+	for _, node := range nodes {
+		asgn, ok := node.(*mkparser.Assignment)
+		if !ok {
+			continue
+		}
+		// We are looking for a variable called '_product_list_vars'
+		// or '_product_single_value_vars'.
+		if !asgn.Name.Const() {
+			continue
+		}
+		varName := asgn.Name.Strings[0]
+		var starType starlarkType
+		if varName == "_product_list_vars" {
+			starType = starlarkTypeList
+		} else if varName == "_product_single_value_vars" {
+			starType = starlarkTypeUnknown
+		} else {
+			continue
+		}
+		for _, name := range strings.Fields(asgn.Value.Dump()) {
+			vr.NewVariable(name, VarClassConfig, starType)
+		}
+
+	}
+	return nil
+}
diff --git a/mk2rbc/config_variables_test.go b/mk2rbc/config_variables_test.go
new file mode 100644
index 0000000..f5a5180
--- /dev/null
+++ b/mk2rbc/config_variables_test.go
@@ -0,0 +1,60 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package mk2rbc
+
+import (
+	"path/filepath"
+	"reflect"
+	"runtime"
+	"testing"
+)
+
+type testVar struct {
+	name string
+	cl   varClass
+	ty   starlarkType
+}
+
+type testVariables struct {
+	v []testVar
+}
+
+func (v *testVariables) NewVariable(name string, varClass varClass, valueType starlarkType) {
+	v.v = append(v.v, testVar{name, varClass, valueType})
+}
+
+// getTestDirectory returns the test directory, which should be the test/ subdirectory
+func getTestDirectory() string {
+	_, myFile, _, _ := runtime.Caller(1)
+	return filepath.Join(filepath.Dir(myFile), "test")
+}
+
+func TestConfigVariables(t *testing.T) {
+	testFile := filepath.Join(getTestDirectory(), "config_variables.mk.test")
+	var actual testVariables
+	if err := FindConfigVariables(testFile, &actual); err != nil {
+		t.Fatal(err)
+	}
+	expected := testVariables{[]testVar{
+		{"PRODUCT_NAME", VarClassConfig, starlarkTypeUnknown},
+		{"PRODUCT_MODEL", VarClassConfig, starlarkTypeUnknown},
+		{"PRODUCT_LOCALES", VarClassConfig, starlarkTypeList},
+		{"PRODUCT_AAPT_CONFIG", VarClassConfig, starlarkTypeList},
+		{"PRODUCT_AAPT_PREF_CONFIG", VarClassConfig, starlarkTypeUnknown},
+	}}
+	if !reflect.DeepEqual(expected, actual) {
+		t.Errorf("\nExpected: %v\n  Actual: %v", expected, actual)
+	}
+}
diff --git a/mk2rbc/expr.go b/mk2rbc/expr.go
new file mode 100644
index 0000000..0bb8b95
--- /dev/null
+++ b/mk2rbc/expr.go
@@ -0,0 +1,591 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package mk2rbc
+
+import (
+	"fmt"
+	"strconv"
+	"strings"
+
+	mkparser "android/soong/androidmk/parser"
+)
+
+// Represents an expression in the Starlark code. An expression has
+// a type, and it can be evaluated.
+type starlarkExpr interface {
+	starlarkNode
+	typ() starlarkType
+	// Try to substitute variable values. Return substitution result
+	// and whether it is the same as the original expression.
+	eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool)
+	// Emit the code to copy the expression, otherwise we will end up
+	// with source and target pointing to the same list.
+	emitListVarCopy(gctx *generationContext)
+}
+
+func maybeString(expr starlarkExpr) (string, bool) {
+	if x, ok := expr.(*stringLiteralExpr); ok {
+		return x.literal, true
+	}
+	return "", false
+}
+
+type stringLiteralExpr struct {
+	literal string
+}
+
+func (s *stringLiteralExpr) eval(_ map[string]starlarkExpr) (res starlarkExpr, same bool) {
+	res = s
+	same = true
+	return
+}
+
+func (s *stringLiteralExpr) emit(gctx *generationContext) {
+	gctx.writef("%q", s.literal)
+}
+
+func (_ *stringLiteralExpr) typ() starlarkType {
+	return starlarkTypeString
+}
+
+func (s *stringLiteralExpr) emitListVarCopy(gctx *generationContext) {
+	s.emit(gctx)
+}
+
+// Integer literal
+type intLiteralExpr struct {
+	literal int
+}
+
+func (s *intLiteralExpr) eval(_ map[string]starlarkExpr) (res starlarkExpr, same bool) {
+	res = s
+	same = true
+	return
+}
+
+func (s *intLiteralExpr) emit(gctx *generationContext) {
+	gctx.writef("%d", s.literal)
+}
+
+func (_ *intLiteralExpr) typ() starlarkType {
+	return starlarkTypeInt
+}
+
+func (s *intLiteralExpr) emitListVarCopy(gctx *generationContext) {
+	s.emit(gctx)
+}
+
+// interpolateExpr represents Starlark's interpolation operator <string> % list
+// we break <string> into a list of chunks, i.e., "first%second%third" % (X, Y)
+// will have chunks = ["first", "second", "third"] and args = [X, Y]
+type interpolateExpr struct {
+	chunks []string // string chunks, separated by '%'
+	args   []starlarkExpr
+}
+
+func (xi *interpolateExpr) emit(gctx *generationContext) {
+	if len(xi.chunks) != len(xi.args)+1 {
+		panic(fmt.Errorf("malformed interpolateExpr: #chunks(%d) != #args(%d)+1",
+			len(xi.chunks), len(xi.args)))
+	}
+	// Generate format as join of chunks, but first escape '%' in them
+	format := strings.ReplaceAll(xi.chunks[0], "%", "%%")
+	for _, chunk := range xi.chunks[1:] {
+		format += "%s" + strings.ReplaceAll(chunk, "%", "%%")
+	}
+	gctx.writef("%q %% ", format)
+	emitarg := func(arg starlarkExpr) {
+		if arg.typ() == starlarkTypeList {
+			gctx.write(`" ".join(`)
+			arg.emit(gctx)
+			gctx.write(`)`)
+		} else {
+			arg.emit(gctx)
+		}
+	}
+	if len(xi.args) == 1 {
+		emitarg(xi.args[0])
+	} else {
+		sep := "("
+		for _, arg := range xi.args {
+			gctx.write(sep)
+			emitarg(arg)
+			sep = ", "
+		}
+		gctx.write(")")
+	}
+}
+
+func (xi *interpolateExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
+	same = true
+	newChunks := []string{xi.chunks[0]}
+	var newArgs []starlarkExpr
+	for i, arg := range xi.args {
+		newArg, sameArg := arg.eval(valueMap)
+		same = same && sameArg
+		switch x := newArg.(type) {
+		case *stringLiteralExpr:
+			newChunks[len(newChunks)-1] += x.literal + xi.chunks[i+1]
+			same = false
+			continue
+		case *intLiteralExpr:
+			newChunks[len(newChunks)-1] += strconv.Itoa(x.literal) + xi.chunks[i+1]
+			same = false
+			continue
+		default:
+			newChunks = append(newChunks, xi.chunks[i+1])
+			newArgs = append(newArgs, newArg)
+		}
+	}
+	if same {
+		res = xi
+	} else if len(newChunks) == 1 {
+		res = &stringLiteralExpr{newChunks[0]}
+	} else {
+		res = &interpolateExpr{chunks: newChunks, args: newArgs}
+	}
+	return
+}
+
+func (_ *interpolateExpr) typ() starlarkType {
+	return starlarkTypeString
+}
+
+func (xi *interpolateExpr) emitListVarCopy(gctx *generationContext) {
+	xi.emit(gctx)
+}
+
+type variableRefExpr struct {
+	ref       variable
+	isDefined bool
+}
+
+func (v *variableRefExpr) eval(map[string]starlarkExpr) (res starlarkExpr, same bool) {
+	predefined, ok := v.ref.(*predefinedVariable)
+	if same = !ok; same {
+		res = v
+	} else {
+		res = predefined.value
+	}
+	return
+}
+
+func (v *variableRefExpr) emit(gctx *generationContext) {
+	v.ref.emitGet(gctx, v.isDefined)
+}
+
+func (v *variableRefExpr) typ() starlarkType {
+	return v.ref.valueType()
+}
+
+func (v *variableRefExpr) emitListVarCopy(gctx *generationContext) {
+	v.emit(gctx)
+	if v.typ() == starlarkTypeList {
+		gctx.write("[:]") // this will copy the list
+	}
+}
+
+type notExpr struct {
+	expr starlarkExpr
+}
+
+func (n *notExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
+	if x, same := n.expr.eval(valueMap); same {
+		res = n
+	} else {
+		res = &notExpr{expr: x}
+	}
+	return
+}
+
+func (n *notExpr) emit(ctx *generationContext) {
+	ctx.write("not ")
+	n.expr.emit(ctx)
+}
+
+func (_ *notExpr) typ() starlarkType {
+	return starlarkTypeBool
+}
+
+func (n *notExpr) emitListVarCopy(gctx *generationContext) {
+	n.emit(gctx)
+}
+
+type eqExpr struct {
+	left, right starlarkExpr
+	isEq        bool // if false, it's !=
+}
+
+func (eq *eqExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
+	xLeft, sameLeft := eq.left.eval(valueMap)
+	xRight, sameRight := eq.right.eval(valueMap)
+	if same = sameLeft && sameRight; same {
+		res = eq
+	} else {
+		res = &eqExpr{left: xLeft, right: xRight, isEq: eq.isEq}
+	}
+	return
+}
+
+func (eq *eqExpr) emit(gctx *generationContext) {
+	emitSimple := func(expr starlarkExpr) {
+		if eq.isEq {
+			gctx.write("not ")
+		}
+		expr.emit(gctx)
+	}
+	// Are we checking that a variable is empty?
+	if isEmptyString(eq.left) {
+		emitSimple(eq.right)
+		return
+	} else if isEmptyString(eq.right) {
+		emitSimple(eq.left)
+		return
+
+	}
+	// General case
+	eq.left.emit(gctx)
+	if eq.isEq {
+		gctx.write(" == ")
+	} else {
+		gctx.write(" != ")
+	}
+	eq.right.emit(gctx)
+}
+
+func (_ *eqExpr) typ() starlarkType {
+	return starlarkTypeBool
+}
+
+func (eq *eqExpr) emitListVarCopy(gctx *generationContext) {
+	eq.emit(gctx)
+}
+
+// variableDefinedExpr corresponds to Make's ifdef VAR
+type variableDefinedExpr struct {
+	v variable
+}
+
+func (v *variableDefinedExpr) eval(_ map[string]starlarkExpr) (res starlarkExpr, same bool) {
+	res = v
+	same = true
+	return
+
+}
+
+func (v *variableDefinedExpr) emit(gctx *generationContext) {
+	if v.v != nil {
+		v.v.emitDefined(gctx)
+		return
+	}
+	gctx.writef("%s(%q)", cfnWarning, "TODO(VAR)")
+}
+
+func (_ *variableDefinedExpr) typ() starlarkType {
+	return starlarkTypeBool
+}
+
+func (v *variableDefinedExpr) emitListVarCopy(gctx *generationContext) {
+	v.emit(gctx)
+}
+
+type listExpr struct {
+	items []starlarkExpr
+}
+
+func (l *listExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
+	newItems := make([]starlarkExpr, len(l.items))
+	same = true
+	for i, item := range l.items {
+		var sameItem bool
+		newItems[i], sameItem = item.eval(valueMap)
+		same = same && sameItem
+	}
+	if same {
+		res = l
+	} else {
+		res = &listExpr{newItems}
+	}
+	return
+}
+
+func (l *listExpr) emit(gctx *generationContext) {
+	if !gctx.inAssignment || len(l.items) < 2 {
+		gctx.write("[")
+		sep := ""
+		for _, item := range l.items {
+			gctx.write(sep)
+			item.emit(gctx)
+			sep = ", "
+		}
+		gctx.write("]")
+		return
+	}
+
+	gctx.write("[")
+	gctx.indentLevel += 2
+
+	for _, item := range l.items {
+		gctx.newLine()
+		item.emit(gctx)
+		gctx.write(",")
+	}
+	gctx.indentLevel -= 2
+	gctx.newLine()
+	gctx.write("]")
+}
+
+func (_ *listExpr) typ() starlarkType {
+	return starlarkTypeList
+}
+
+func (l *listExpr) emitListVarCopy(gctx *generationContext) {
+	l.emit(gctx)
+}
+
+func newStringListExpr(items []string) *listExpr {
+	v := listExpr{}
+	for _, item := range items {
+		v.items = append(v.items, &stringLiteralExpr{item})
+	}
+	return &v
+}
+
+// concatExpr generates epxr1 + expr2 + ... + exprN in Starlark.
+type concatExpr struct {
+	items []starlarkExpr
+}
+
+func (c *concatExpr) emit(gctx *generationContext) {
+	if len(c.items) == 1 {
+		c.items[0].emit(gctx)
+		return
+	}
+
+	if !gctx.inAssignment {
+		c.items[0].emit(gctx)
+		for _, item := range c.items[1:] {
+			gctx.write(" + ")
+			item.emit(gctx)
+		}
+		return
+	}
+	gctx.write("(")
+	c.items[0].emit(gctx)
+	gctx.indentLevel += 2
+	for _, item := range c.items[1:] {
+		gctx.write(" +")
+		gctx.newLine()
+		item.emit(gctx)
+	}
+	gctx.write(")")
+	gctx.indentLevel -= 2
+}
+
+func (c *concatExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
+	same = true
+	xConcat := &concatExpr{items: make([]starlarkExpr, len(c.items))}
+	for i, item := range c.items {
+		var sameItem bool
+		xConcat.items[i], sameItem = item.eval(valueMap)
+		same = same && sameItem
+	}
+	if same {
+		res = c
+	} else {
+		res = xConcat
+	}
+	return
+}
+
+func (_ *concatExpr) typ() starlarkType {
+	return starlarkTypeList
+}
+
+func (c *concatExpr) emitListVarCopy(gctx *generationContext) {
+	c.emit(gctx)
+}
+
+// inExpr generates <expr> [not] in <list>
+type inExpr struct {
+	expr  starlarkExpr
+	list  starlarkExpr
+	isNot bool
+}
+
+func (i *inExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
+	x := &inExpr{isNot: i.isNot}
+	var sameExpr, sameList bool
+	x.expr, sameExpr = i.expr.eval(valueMap)
+	x.list, sameList = i.list.eval(valueMap)
+	if same = sameExpr && sameList; same {
+		res = i
+	} else {
+		res = x
+	}
+	return
+}
+
+func (i *inExpr) emit(gctx *generationContext) {
+	i.expr.emit(gctx)
+	if i.isNot {
+		gctx.write(" not in ")
+	} else {
+		gctx.write(" in ")
+	}
+	i.list.emit(gctx)
+}
+
+func (_ *inExpr) typ() starlarkType {
+	return starlarkTypeBool
+}
+
+func (i *inExpr) emitListVarCopy(gctx *generationContext) {
+	i.emit(gctx)
+}
+
+type indexExpr struct {
+	array starlarkExpr
+	index starlarkExpr
+}
+
+func (ix indexExpr) emit(gctx *generationContext) {
+	ix.array.emit(gctx)
+	gctx.write("[")
+	ix.index.emit(gctx)
+	gctx.write("]")
+}
+
+func (ix indexExpr) typ() starlarkType {
+	return starlarkTypeString
+}
+
+func (ix indexExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
+	newArray, isSameArray := ix.array.eval(valueMap)
+	newIndex, isSameIndex := ix.index.eval(valueMap)
+	if same = isSameArray && isSameIndex; same {
+		res = ix
+	} else {
+		res = &indexExpr{newArray, newIndex}
+	}
+	return
+}
+
+func (ix indexExpr) emitListVarCopy(gctx *generationContext) {
+	ix.emit(gctx)
+}
+
+type callExpr struct {
+	object     starlarkExpr // nil if static call
+	name       string
+	args       []starlarkExpr
+	returnType starlarkType
+}
+
+func (cx *callExpr) eval(valueMap map[string]starlarkExpr) (res starlarkExpr, same bool) {
+	newCallExpr := &callExpr{name: cx.name, args: make([]starlarkExpr, len(cx.args)),
+		returnType: cx.returnType}
+	if cx.object != nil {
+		newCallExpr.object, same = cx.object.eval(valueMap)
+	} else {
+		same = true
+	}
+	for i, args := range cx.args {
+		var s bool
+		newCallExpr.args[i], s = args.eval(valueMap)
+		same = same && s
+	}
+	if same {
+		res = cx
+	} else {
+		res = newCallExpr
+	}
+	return
+}
+
+func (cx *callExpr) emit(gctx *generationContext) {
+	sep := ""
+	if cx.object != nil {
+		gctx.write("(")
+		cx.object.emit(gctx)
+		gctx.write(")")
+		gctx.write(".", cx.name, "(")
+	} else {
+		kf, found := knownFunctions[cx.name]
+		if !found {
+			panic(fmt.Errorf("callExpr with unknown function %q", cx.name))
+		}
+		if kf.runtimeName[0] == '!' {
+			panic(fmt.Errorf("callExpr for %q should not be there", cx.name))
+		}
+		gctx.write(kf.runtimeName, "(")
+		if kf.hiddenArg == hiddenArgGlobal {
+			gctx.write("g")
+			sep = ", "
+		} else if kf.hiddenArg == hiddenArgConfig {
+			gctx.write("cfg")
+			sep = ", "
+		}
+	}
+	for _, arg := range cx.args {
+		gctx.write(sep)
+		arg.emit(gctx)
+		sep = ", "
+	}
+	gctx.write(")")
+}
+
+func (cx *callExpr) typ() starlarkType {
+	return cx.returnType
+}
+
+func (cx *callExpr) emitListVarCopy(gctx *generationContext) {
+	cx.emit(gctx)
+}
+
+type badExpr struct {
+	node    mkparser.Node
+	message string
+}
+
+func (b *badExpr) eval(_ map[string]starlarkExpr) (res starlarkExpr, same bool) {
+	res = b
+	same = true
+	return
+}
+
+func (b *badExpr) emit(_ *generationContext) {
+	panic("implement me")
+}
+
+func (_ *badExpr) typ() starlarkType {
+	return starlarkTypeUnknown
+}
+
+func (b *badExpr) emitListVarCopy(gctx *generationContext) {
+	panic("implement me")
+}
+
+func maybeConvertToStringList(expr starlarkExpr) starlarkExpr {
+	if xString, ok := expr.(*stringLiteralExpr); ok {
+		return newStringListExpr(strings.Fields(xString.literal))
+	}
+	return expr
+}
+
+func isEmptyString(expr starlarkExpr) bool {
+	x, ok := expr.(*stringLiteralExpr)
+	return ok && x.literal == ""
+}
diff --git a/mk2rbc/find_mockfs.go b/mk2rbc/find_mockfs.go
new file mode 100644
index 0000000..73eff07
--- /dev/null
+++ b/mk2rbc/find_mockfs.go
@@ -0,0 +1,121 @@
+package mk2rbc
+
+import (
+	"io/fs"
+	"os"
+	"path/filepath"
+	"time"
+)
+
+// Mock FS. Maps a directory name to an array of entries.
+// An entry implements fs.DirEntry, fs.FIleInfo and fs.File interface
+type FindMockFS struct {
+	dirs map[string][]myFileInfo
+}
+
+func (m FindMockFS) locate(name string) (myFileInfo, bool) {
+	if name == "." {
+		return myFileInfo{".", true}, true
+	}
+	dir := filepath.Dir(name)
+	base := filepath.Base(name)
+	if entries, ok := m.dirs[dir]; ok {
+		for _, e := range entries {
+			if e.name == base {
+				return e, true
+			}
+		}
+	}
+	return myFileInfo{}, false
+}
+
+func (m FindMockFS) create(name string, isDir bool) {
+	dir := filepath.Dir(name)
+	m.dirs[dir] = append(m.dirs[dir], myFileInfo{filepath.Base(name), isDir})
+}
+
+func (m FindMockFS) Stat(name string) (fs.FileInfo, error) {
+	if fi, ok := m.locate(name); ok {
+		return fi, nil
+	}
+	return nil, os.ErrNotExist
+}
+
+type myFileInfo struct {
+	name  string
+	isDir bool
+}
+
+func (m myFileInfo) Info() (fs.FileInfo, error) {
+	panic("implement me")
+}
+
+func (m myFileInfo) Size() int64 {
+	panic("implement me")
+}
+
+func (m myFileInfo) Mode() fs.FileMode {
+	panic("implement me")
+}
+
+func (m myFileInfo) ModTime() time.Time {
+	panic("implement me")
+}
+
+func (m myFileInfo) Sys() interface{} {
+	return nil
+}
+
+func (m myFileInfo) Stat() (fs.FileInfo, error) {
+	return m, nil
+}
+
+func (m myFileInfo) Read(bytes []byte) (int, error) {
+	panic("implement me")
+}
+
+func (m myFileInfo) Close() error {
+	panic("implement me")
+}
+
+func (m myFileInfo) Name() string {
+	return m.name
+}
+
+func (m myFileInfo) IsDir() bool {
+	return m.isDir
+}
+
+func (m myFileInfo) Type() fs.FileMode {
+	return m.Mode()
+}
+
+func (m FindMockFS) Open(name string) (fs.File, error) {
+	panic("implement me")
+}
+
+func (m FindMockFS) ReadDir(name string) ([]fs.DirEntry, error) {
+	if d, ok := m.dirs[name]; ok {
+		var res []fs.DirEntry
+		for _, e := range d {
+			res = append(res, e)
+		}
+		return res, nil
+	}
+	return nil, os.ErrNotExist
+}
+
+func NewFindMockFS(files []string) FindMockFS {
+	myfs := FindMockFS{make(map[string][]myFileInfo)}
+	for _, f := range files {
+		isDir := false
+		for f != "." {
+			if _, ok := myfs.locate(f); !ok {
+				myfs.create(f, isDir)
+			}
+			isDir = true
+			f = filepath.Dir(f)
+		}
+	}
+	return myfs
+}
diff --git a/mk2rbc/mk2rbc.go b/mk2rbc/mk2rbc.go
new file mode 100644
index 0000000..b05d340
--- /dev/null
+++ b/mk2rbc/mk2rbc.go
@@ -0,0 +1,1632 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Convert makefile containing device configuration to Starlark file
+// The conversion can handle the following constructs in a makefile:
+//   * comments
+//   * simple variable assignments
+//   * $(call init-product,<file>)
+//   * $(call inherit-product-if-exists
+//   * if directives
+// All other constructs are carried over to the output starlark file as comments.
+//
+package mk2rbc
+
+import (
+	"bytes"
+	"fmt"
+	"io"
+	"io/fs"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"regexp"
+	"strconv"
+	"strings"
+	"text/scanner"
+
+	mkparser "android/soong/androidmk/parser"
+)
+
+const (
+	baseUri = "//build/make/core:product_config.rbc"
+	// The name of the struct exported by the product_config.rbc
+	// that contains the functions and variables available to
+	// product configuration Starlark files.
+	baseName = "rblf"
+
+	// And here are the functions and variables:
+	cfnGetCfg          = baseName + ".cfg"
+	cfnMain            = baseName + ".product_configuration"
+	cfnPrintVars       = baseName + ".printvars"
+	cfnWarning         = baseName + ".warning"
+	cfnLocalAppend     = baseName + ".local_append"
+	cfnLocalSetDefault = baseName + ".local_set_default"
+	cfnInherit         = baseName + ".inherit"
+	cfnSetListDefault  = baseName + ".setdefault"
+)
+
+const (
+	// Phony makefile functions, they are eventually rewritten
+	// according to knownFunctions map
+	addSoongNamespace      = "add_soong_config_namespace"
+	addSoongConfigVarValue = "add_soong_config_var_value"
+	fileExistsPhony        = "$file_exists"
+	wildcardExistsPhony    = "$wildcard_exists"
+)
+
+const (
+	callLoadAlways = "inherit-product"
+	callLoadIf     = "inherit-product-if-exists"
+)
+
+var knownFunctions = map[string]struct {
+	// The name of the runtime function this function call in makefiles maps to.
+	// If it starts with !, then this makefile function call is rewritten to
+	// something else.
+	runtimeName string
+	returnType  starlarkType
+	hiddenArg   hiddenArgType
+}{
+	"abspath":                             {baseName + ".abspath", starlarkTypeString, hiddenArgNone},
+	fileExistsPhony:                       {baseName + ".file_exists", starlarkTypeBool, hiddenArgNone},
+	wildcardExistsPhony:                   {baseName + ".file_wildcard_exists", starlarkTypeBool, hiddenArgNone},
+	addSoongNamespace:                     {baseName + ".add_soong_config_namespace", starlarkTypeVoid, hiddenArgGlobal},
+	addSoongConfigVarValue:                {baseName + ".add_soong_config_var_value", starlarkTypeVoid, hiddenArgGlobal},
+	"add-to-product-copy-files-if-exists": {baseName + ".copy_if_exists", starlarkTypeList, hiddenArgNone},
+	"addprefix":                           {baseName + ".addprefix", starlarkTypeList, hiddenArgNone},
+	"addsuffix":                           {baseName + ".addsuffix", starlarkTypeList, hiddenArgNone},
+	"copy-files":                          {baseName + ".copy_files", starlarkTypeList, hiddenArgNone},
+	"dir":                                 {baseName + ".dir", starlarkTypeList, hiddenArgNone},
+	"enforce-product-packages-exist":      {baseName + ".enforce_product_packages_exist", starlarkTypeVoid, hiddenArgNone},
+	"error":                               {baseName + ".mkerror", starlarkTypeVoid, hiddenArgNone},
+	"findstring":                          {"!findstring", starlarkTypeInt, hiddenArgNone},
+	"find-copy-subdir-files":              {baseName + ".find_and_copy", starlarkTypeList, hiddenArgNone},
+	"find-word-in-list":                   {"!find-word-in-list", starlarkTypeUnknown, hiddenArgNone}, // internal macro
+	"filter":                              {baseName + ".filter", starlarkTypeList, hiddenArgNone},
+	"filter-out":                          {baseName + ".filter_out", starlarkTypeList, hiddenArgNone},
+	"firstword":                           {"!firstword", starlarkTypeString, hiddenArgNone},
+	"get-vendor-board-platforms":          {"!get-vendor-board-platforms", starlarkTypeList, hiddenArgNone}, // internal macro, used by is-board-platform, etc.
+	"info":                                {baseName + ".mkinfo", starlarkTypeVoid, hiddenArgNone},
+	"is-android-codename":                 {"!is-android-codename", starlarkTypeBool, hiddenArgNone},         // unused by product config
+	"is-android-codename-in-list":         {"!is-android-codename-in-list", starlarkTypeBool, hiddenArgNone}, // unused by product config
+	"is-board-platform":                   {"!is-board-platform", starlarkTypeBool, hiddenArgNone},
+	"is-board-platform-in-list":           {"!is-board-platform-in-list", starlarkTypeBool, hiddenArgNone},
+	"is-chipset-in-board-platform":        {"!is-chipset-in-board-platform", starlarkTypeUnknown, hiddenArgNone},     // unused by product config
+	"is-chipset-prefix-in-board-platform": {"!is-chipset-prefix-in-board-platform", starlarkTypeBool, hiddenArgNone}, // unused by product config
+	"is-not-board-platform":               {"!is-not-board-platform", starlarkTypeBool, hiddenArgNone},               // defined but never used
+	"is-platform-sdk-version-at-least":    {"!is-platform-sdk-version-at-least", starlarkTypeBool, hiddenArgNone},    // unused by product config
+	"is-product-in-list":                  {"!is-product-in-list", starlarkTypeBool, hiddenArgNone},
+	"is-vendor-board-platform":            {"!is-vendor-board-platform", starlarkTypeBool, hiddenArgNone},
+	callLoadAlways:                        {"!inherit-product", starlarkTypeVoid, hiddenArgNone},
+	callLoadIf:                            {"!inherit-product-if-exists", starlarkTypeVoid, hiddenArgNone},
+	"lastword":                            {"!lastword", starlarkTypeString, hiddenArgNone},
+	"match-prefix":                        {"!match-prefix", starlarkTypeUnknown, hiddenArgNone},       // internal macro
+	"match-word":                          {"!match-word", starlarkTypeUnknown, hiddenArgNone},         // internal macro
+	"match-word-in-list":                  {"!match-word-in-list", starlarkTypeUnknown, hiddenArgNone}, // internal macro
+	"notdir":                              {baseName + ".notdir", starlarkTypeString, hiddenArgNone},
+	"my-dir":                              {"!my-dir", starlarkTypeString, hiddenArgNone},
+	"patsubst":                            {baseName + ".mkpatsubst", starlarkTypeString, hiddenArgNone},
+	"product-copy-files-by-pattern":       {baseName + ".product_copy_files_by_pattern", starlarkTypeList, hiddenArgNone},
+	"require-artifacts-in-path":           {baseName + ".require_artifacts_in_path", starlarkTypeVoid, hiddenArgNone},
+	"require-artifacts-in-path-relaxed":   {baseName + ".require_artifacts_in_path_relaxed", starlarkTypeVoid, hiddenArgNone},
+	// TODO(asmundak): remove it once all calls are removed from configuration makefiles. see b/183161002
+	"shell":      {baseName + ".shell", starlarkTypeString, hiddenArgNone},
+	"strip":      {baseName + ".mkstrip", starlarkTypeString, hiddenArgNone},
+	"tb-modules": {"!tb-modules", starlarkTypeUnknown, hiddenArgNone}, // defined in hardware/amlogic/tb_modules/tb_detect.mk, unused
+	"subst":      {baseName + ".mksubst", starlarkTypeString, hiddenArgNone},
+	"warning":    {baseName + ".mkwarning", starlarkTypeVoid, hiddenArgNone},
+	"word":       {baseName + "!word", starlarkTypeString, hiddenArgNone},
+	"wildcard":   {baseName + ".expand_wildcard", starlarkTypeList, hiddenArgNone},
+}
+
+var builtinFuncRex = regexp.MustCompile(
+	"^(addprefix|addsuffix|abspath|and|basename|call|dir|error|eval" +
+		"|flavor|foreach|file|filter|filter-out|findstring|firstword|guile" +
+		"|if|info|join|lastword|notdir|or|origin|patsubst|realpath" +
+		"|shell|sort|strip|subst|suffix|value|warning|word|wordlist|words" +
+		"|wildcard)")
+
+// Conversion request parameters
+type Request struct {
+	MkFile             string    // file to convert
+	Reader             io.Reader // if set, read input from this stream instead
+	RootDir            string    // root directory path used to resolve included files
+	OutputSuffix       string    // generated Starlark files suffix
+	OutputDir          string    // if set, root of the output hierarchy
+	ErrorLogger        ErrorMonitorCB
+	TracedVariables    []string // trace assignment to these variables
+	TraceCalls         bool
+	WarnPartialSuccess bool
+	SourceFS           fs.FS
+	MakefileFinder     MakefileFinder
+}
+
+// An error sink allowing to gather error statistics.
+// NewError is called on every error encountered during processing.
+type ErrorMonitorCB interface {
+	NewError(s string, node mkparser.Node, args ...interface{})
+}
+
+// Derives module name for a given file. It is base name
+// (file name without suffix), with some characters replaced to make it a Starlark identifier
+func moduleNameForFile(mkFile string) string {
+	base := strings.TrimSuffix(filepath.Base(mkFile), filepath.Ext(mkFile))
+	// TODO(asmundak): what else can be in the product file names?
+	return strings.NewReplacer("-", "_", ".", "_").Replace(base)
+
+}
+
+func cloneMakeString(mkString *mkparser.MakeString) *mkparser.MakeString {
+	r := &mkparser.MakeString{StringPos: mkString.StringPos}
+	r.Strings = append(r.Strings, mkString.Strings...)
+	r.Variables = append(r.Variables, mkString.Variables...)
+	return r
+}
+
+func isMakeControlFunc(s string) bool {
+	return s == "error" || s == "warning" || s == "info"
+}
+
+// Starlark output generation context
+type generationContext struct {
+	buf          strings.Builder
+	starScript   *StarlarkScript
+	indentLevel  int
+	inAssignment bool
+	tracedCount  int
+}
+
+func NewGenerateContext(ss *StarlarkScript) *generationContext {
+	return &generationContext{starScript: ss}
+}
+
+// emit returns generated script
+func (gctx *generationContext) emit() string {
+	ss := gctx.starScript
+
+	// The emitted code has the following layout:
+	//    <initial comments>
+	//    preamble, i.e.,
+	//      load statement for the runtime support
+	//      load statement for each unique submodule pulled in by this one
+	//    def init(g, handle):
+	//      cfg = rblf.cfg(handle)
+	//      <statements>
+	//      <warning if conversion was not clean>
+
+	iNode := len(ss.nodes)
+	for i, node := range ss.nodes {
+		if _, ok := node.(*commentNode); !ok {
+			iNode = i
+			break
+		}
+		node.emit(gctx)
+	}
+
+	gctx.emitPreamble()
+
+	gctx.newLine()
+	// The arguments passed to the init function are the global dictionary
+	// ('g') and the product configuration dictionary ('cfg')
+	gctx.write("def init(g, handle):")
+	gctx.indentLevel++
+	if gctx.starScript.traceCalls {
+		gctx.newLine()
+		gctx.writef(`print(">%s")`, gctx.starScript.mkFile)
+	}
+	gctx.newLine()
+	gctx.writef("cfg = %s(handle)", cfnGetCfg)
+	for _, node := range ss.nodes[iNode:] {
+		node.emit(gctx)
+	}
+
+	if ss.hasErrors && ss.warnPartialSuccess {
+		gctx.newLine()
+		gctx.writef("%s(%q, %q)", cfnWarning, filepath.Base(ss.mkFile), "partially successful conversion")
+	}
+	if gctx.starScript.traceCalls {
+		gctx.newLine()
+		gctx.writef(`print("<%s")`, gctx.starScript.mkFile)
+	}
+	gctx.indentLevel--
+	gctx.write("\n")
+	return gctx.buf.String()
+}
+
+func (gctx *generationContext) emitPreamble() {
+	gctx.newLine()
+	gctx.writef("load(%q, %q)", baseUri, baseName)
+	// Emit exactly one load statement for each URI.
+	loadedSubConfigs := make(map[string]string)
+	for _, sc := range gctx.starScript.inherited {
+		uri := sc.path
+		if m, ok := loadedSubConfigs[uri]; ok {
+			// No need to emit load statement, but fix module name.
+			sc.moduleLocalName = m
+			continue
+		}
+		if sc.optional {
+			uri += "|init"
+		}
+		gctx.newLine()
+		gctx.writef("load(%q, %s = \"init\")", uri, sc.entryName())
+		loadedSubConfigs[uri] = sc.moduleLocalName
+	}
+	gctx.write("\n")
+}
+
+func (gctx *generationContext) emitPass() {
+	gctx.newLine()
+	gctx.write("pass")
+}
+
+func (gctx *generationContext) write(ss ...string) {
+	for _, s := range ss {
+		gctx.buf.WriteString(s)
+	}
+}
+
+func (gctx *generationContext) writef(format string, args ...interface{}) {
+	gctx.write(fmt.Sprintf(format, args...))
+}
+
+func (gctx *generationContext) newLine() {
+	if gctx.buf.Len() == 0 {
+		return
+	}
+	gctx.write("\n")
+	gctx.writef("%*s", 2*gctx.indentLevel, "")
+}
+
+type knownVariable struct {
+	name      string
+	class     varClass
+	valueType starlarkType
+}
+
+type knownVariables map[string]knownVariable
+
+func (pcv knownVariables) NewVariable(name string, varClass varClass, valueType starlarkType) {
+	v, exists := pcv[name]
+	if !exists {
+		pcv[name] = knownVariable{name, varClass, valueType}
+		return
+	}
+	// Conflict resolution:
+	//    * config class trumps everything
+	//    * any type trumps unknown type
+	match := varClass == v.class
+	if !match {
+		if varClass == VarClassConfig {
+			v.class = VarClassConfig
+			match = true
+		} else if v.class == VarClassConfig {
+			match = true
+		}
+	}
+	if valueType != v.valueType {
+		if valueType != starlarkTypeUnknown {
+			if v.valueType == starlarkTypeUnknown {
+				v.valueType = valueType
+			} else {
+				match = false
+			}
+		}
+	}
+	if !match {
+		fmt.Fprintf(os.Stderr, "cannot redefine %s as %v/%v (already defined as %v/%v)\n",
+			name, varClass, valueType, v.class, v.valueType)
+	}
+}
+
+// All known product variables.
+var KnownVariables = make(knownVariables)
+
+func init() {
+	for _, kv := range []string{
+		// Kernel-related variables that we know are lists.
+		"BOARD_VENDOR_KERNEL_MODULES",
+		"BOARD_VENDOR_RAMDISK_KERNEL_MODULES",
+		"BOARD_VENDOR_RAMDISK_KERNEL_MODULES_LOAD",
+		"BOARD_RECOVERY_KERNEL_MODULES",
+		// Other variables we knwo are lists
+		"ART_APEX_JARS",
+	} {
+		KnownVariables.NewVariable(kv, VarClassSoong, starlarkTypeList)
+	}
+}
+
+type nodeReceiver interface {
+	newNode(node starlarkNode)
+}
+
+// Information about the generated Starlark script.
+type StarlarkScript struct {
+	mkFile             string
+	moduleName         string
+	mkPos              scanner.Position
+	nodes              []starlarkNode
+	inherited          []*moduleInfo
+	hasErrors          bool
+	topDir             string
+	traceCalls         bool // print enter/exit each init function
+	warnPartialSuccess bool
+	sourceFS           fs.FS
+	makefileFinder     MakefileFinder
+}
+
+func (ss *StarlarkScript) newNode(node starlarkNode) {
+	ss.nodes = append(ss.nodes, node)
+}
+
+// varAssignmentScope points to the last assignment for each variable
+// in the current block. It is used during the parsing to chain
+// the assignments to a variable together.
+type varAssignmentScope struct {
+	outer *varAssignmentScope
+	vars  map[string]*assignmentNode
+}
+
+// parseContext holds the script we are generating and all the ephemeral data
+// needed during the parsing.
+type parseContext struct {
+	script           *StarlarkScript
+	nodes            []mkparser.Node // Makefile as parsed by mkparser
+	currentNodeIndex int             // Node in it we are processing
+	ifNestLevel      int
+	moduleNameCount  map[string]int // count of imported modules with given basename
+	fatalError       error
+	builtinMakeVars  map[string]starlarkExpr
+	outputSuffix     string
+	errorLogger      ErrorMonitorCB
+	tracedVariables  map[string]bool // variables to be traced in the generated script
+	variables        map[string]variable
+	varAssignments   *varAssignmentScope
+	receiver         nodeReceiver // receptacle for the generated starlarkNode's
+	receiverStack    []nodeReceiver
+	outputDir        string
+	dependentModules map[string]*moduleInfo
+	soongNamespaces  map[string]map[string]bool
+}
+
+func newParseContext(ss *StarlarkScript, nodes []mkparser.Node) *parseContext {
+	topdir, _ := filepath.Split(filepath.Join(ss.topDir, "foo"))
+	predefined := []struct{ name, value string }{
+		{"SRC_TARGET_DIR", filepath.Join("build", "make", "target")},
+		{"LOCAL_PATH", filepath.Dir(ss.mkFile)},
+		{"TOPDIR", topdir},
+		// TODO(asmundak): maybe read it from build/make/core/envsetup.mk?
+		{"TARGET_COPY_OUT_SYSTEM", "system"},
+		{"TARGET_COPY_OUT_SYSTEM_OTHER", "system_other"},
+		{"TARGET_COPY_OUT_DATA", "data"},
+		{"TARGET_COPY_OUT_ASAN", filepath.Join("data", "asan")},
+		{"TARGET_COPY_OUT_OEM", "oem"},
+		{"TARGET_COPY_OUT_RAMDISK", "ramdisk"},
+		{"TARGET_COPY_OUT_DEBUG_RAMDISK", "debug_ramdisk"},
+		{"TARGET_COPY_OUT_VENDOR_DEBUG_RAMDISK", "vendor_debug_ramdisk"},
+		{"TARGET_COPY_OUT_TEST_HARNESS_RAMDISK", "test_harness_ramdisk"},
+		{"TARGET_COPY_OUT_ROOT", "root"},
+		{"TARGET_COPY_OUT_RECOVERY", "recovery"},
+		{"TARGET_COPY_OUT_VENDOR_RAMDISK", "vendor_ramdisk"},
+		// TODO(asmundak): to process internal config files, we need the following variables:
+		//    BOARD_CONFIG_VENDOR_PATH
+		//    TARGET_VENDOR
+		//    target_base_product
+		//
+
+		// the following utility variables are set in build/make/common/core.mk:
+		{"empty", ""},
+		{"space", " "},
+		{"comma", ","},
+		{"newline", "\n"},
+		{"pound", "#"},
+		{"backslash", "\\"},
+	}
+	ctx := &parseContext{
+		script:           ss,
+		nodes:            nodes,
+		currentNodeIndex: 0,
+		ifNestLevel:      0,
+		moduleNameCount:  make(map[string]int),
+		builtinMakeVars:  map[string]starlarkExpr{},
+		variables:        make(map[string]variable),
+		dependentModules: make(map[string]*moduleInfo),
+		soongNamespaces:  make(map[string]map[string]bool),
+	}
+	ctx.pushVarAssignments()
+	for _, item := range predefined {
+		ctx.variables[item.name] = &predefinedVariable{
+			baseVariable: baseVariable{nam: item.name, typ: starlarkTypeString},
+			value:        &stringLiteralExpr{item.value},
+		}
+	}
+
+	return ctx
+}
+
+func (ctx *parseContext) lastAssignment(name string) *assignmentNode {
+	for va := ctx.varAssignments; va != nil; va = va.outer {
+		if v, ok := va.vars[name]; ok {
+			return v
+		}
+	}
+	return nil
+}
+
+func (ctx *parseContext) setLastAssignment(name string, asgn *assignmentNode) {
+	ctx.varAssignments.vars[name] = asgn
+}
+
+func (ctx *parseContext) pushVarAssignments() {
+	va := &varAssignmentScope{
+		outer: ctx.varAssignments,
+		vars:  make(map[string]*assignmentNode),
+	}
+	ctx.varAssignments = va
+}
+
+func (ctx *parseContext) popVarAssignments() {
+	ctx.varAssignments = ctx.varAssignments.outer
+}
+
+func (ctx *parseContext) pushReceiver(rcv nodeReceiver) {
+	ctx.receiverStack = append(ctx.receiverStack, ctx.receiver)
+	ctx.receiver = rcv
+}
+
+func (ctx *parseContext) popReceiver() {
+	last := len(ctx.receiverStack) - 1
+	if last < 0 {
+		panic(fmt.Errorf("popReceiver: receiver stack empty"))
+	}
+	ctx.receiver = ctx.receiverStack[last]
+	ctx.receiverStack = ctx.receiverStack[0:last]
+}
+
+func (ctx *parseContext) hasNodes() bool {
+	return ctx.currentNodeIndex < len(ctx.nodes)
+}
+
+func (ctx *parseContext) getNode() mkparser.Node {
+	if !ctx.hasNodes() {
+		return nil
+	}
+	node := ctx.nodes[ctx.currentNodeIndex]
+	ctx.currentNodeIndex++
+	return node
+}
+
+func (ctx *parseContext) backNode() {
+	if ctx.currentNodeIndex <= 0 {
+		panic("Cannot back off")
+	}
+	ctx.currentNodeIndex--
+}
+
+func (ctx *parseContext) handleAssignment(a *mkparser.Assignment) {
+	// Handle only simple variables
+	if !a.Name.Const() {
+		ctx.errorf(a, "Only simple variables are handled")
+		return
+	}
+	name := a.Name.Strings[0]
+	const soongNsPrefix = "SOONG_CONFIG_"
+	// Soong confuguration
+	if strings.HasPrefix(name, soongNsPrefix) {
+		ctx.handleSoongNsAssignment(strings.TrimPrefix(name, soongNsPrefix), a)
+		return
+	}
+	lhs := ctx.addVariable(name)
+	if lhs == nil {
+		ctx.errorf(a, "unknown variable %s", name)
+		return
+	}
+	_, isTraced := ctx.tracedVariables[name]
+	asgn := &assignmentNode{lhs: lhs, mkValue: a.Value, isTraced: isTraced}
+	if lhs.valueType() == starlarkTypeUnknown {
+		// Try to divine variable type from the RHS
+		asgn.value = ctx.parseMakeString(a, a.Value)
+		if xBad, ok := asgn.value.(*badExpr); ok {
+			ctx.wrapBadExpr(xBad)
+			return
+		}
+		inferred_type := asgn.value.typ()
+		if inferred_type != starlarkTypeUnknown {
+			lhs.setValueType(inferred_type)
+		}
+	}
+	if lhs.valueType() == starlarkTypeList {
+		xConcat := ctx.buildConcatExpr(a)
+		if xConcat == nil {
+			return
+		}
+		switch len(xConcat.items) {
+		case 0:
+			asgn.value = &listExpr{}
+		case 1:
+			asgn.value = xConcat.items[0]
+		default:
+			asgn.value = xConcat
+		}
+	} else {
+		asgn.value = ctx.parseMakeString(a, a.Value)
+		if xBad, ok := asgn.value.(*badExpr); ok {
+			ctx.wrapBadExpr(xBad)
+			return
+		}
+	}
+
+	// TODO(asmundak): move evaluation to a separate pass
+	asgn.value, _ = asgn.value.eval(ctx.builtinMakeVars)
+
+	asgn.previous = ctx.lastAssignment(name)
+	ctx.setLastAssignment(name, asgn)
+	switch a.Type {
+	case "=", ":=":
+		asgn.flavor = asgnSet
+	case "+=":
+		if asgn.previous == nil && !asgn.lhs.isPreset() {
+			asgn.flavor = asgnMaybeAppend
+		} else {
+			asgn.flavor = asgnAppend
+		}
+	case "?=":
+		asgn.flavor = asgnMaybeSet
+	default:
+		panic(fmt.Errorf("unexpected assignment type %s", a.Type))
+	}
+
+	ctx.receiver.newNode(asgn)
+}
+
+func (ctx *parseContext) handleSoongNsAssignment(name string, asgn *mkparser.Assignment) {
+	val := ctx.parseMakeString(asgn, asgn.Value)
+	if xBad, ok := val.(*badExpr); ok {
+		ctx.wrapBadExpr(xBad)
+		return
+	}
+	val, _ = val.eval(ctx.builtinMakeVars)
+
+	// Unfortunately, Soong namespaces can be set up by directly setting corresponding Make
+	// variables instead of via add_soong_config_namespace + add_soong_config_var_value.
+	// Try to divine the call from the assignment as follows:
+	if name == "NAMESPACES" {
+		// Upon seeng
+		//      SOONG_CONFIG_NAMESPACES += foo
+		//    remember that there is a namespace `foo` and act as we saw
+		//      $(call add_soong_config_namespace,foo)
+		s, ok := maybeString(val)
+		if !ok {
+			ctx.errorf(asgn, "cannot handle variables in SOONG_CONFIG_NAMESPACES assignment, please use add_soong_config_namespace instead")
+			return
+		}
+		for _, ns := range strings.Fields(s) {
+			ctx.addSoongNamespace(ns)
+			ctx.receiver.newNode(&exprNode{&callExpr{
+				name:       addSoongNamespace,
+				args:       []starlarkExpr{&stringLiteralExpr{ns}},
+				returnType: starlarkTypeVoid,
+			}})
+		}
+	} else {
+		// Upon seeing
+		//      SOONG_CONFIG_x_y = v
+		// find a namespace called `x` and act as if we encountered
+		//      $(call add_config_var_value(x,y,v)
+		// or check that `x_y` is a namespace, and then add the RHS of this assignment as variables in
+		// it.
+		// Emit an error in the ambiguous situation (namespaces `foo_bar` with a variable `baz`
+		// and `foo` with a variable `bar_baz`.
+		namespaceName := ""
+		if ctx.hasSoongNamespace(name) {
+			namespaceName = name
+		}
+		var varName string
+		for pos, ch := range name {
+			if !(ch == '_' && ctx.hasSoongNamespace(name[0:pos])) {
+				continue
+			}
+			if namespaceName != "" {
+				ctx.errorf(asgn, "ambiguous soong namespace (may be either `%s` or  `%s`)", namespaceName, name[0:pos])
+				return
+			}
+			namespaceName = name[0:pos]
+			varName = name[pos+1:]
+		}
+		if namespaceName == "" {
+			ctx.errorf(asgn, "cannot figure out Soong namespace, please use add_soong_config_var_value macro instead")
+			return
+		}
+		if varName == "" {
+			// Remember variables in this namespace
+			s, ok := maybeString(val)
+			if !ok {
+				ctx.errorf(asgn, "cannot handle variables in SOONG_CONFIG_ assignment, please use add_soong_config_var_value instead")
+				return
+			}
+			ctx.updateSoongNamespace(asgn.Type != "+=", namespaceName, strings.Fields(s))
+			return
+		}
+
+		// Finally, handle assignment to a namespace variable
+		if !ctx.hasNamespaceVar(namespaceName, varName) {
+			ctx.errorf(asgn, "no %s variable in %s namespace, please use add_soong_config_var_value instead", varName, namespaceName)
+			return
+		}
+		ctx.receiver.newNode(&exprNode{&callExpr{
+			name:       addSoongConfigVarValue,
+			args:       []starlarkExpr{&stringLiteralExpr{namespaceName}, &stringLiteralExpr{varName}, val},
+			returnType: starlarkTypeVoid,
+		}})
+	}
+}
+
+func (ctx *parseContext) buildConcatExpr(a *mkparser.Assignment) *concatExpr {
+	xConcat := &concatExpr{}
+	var xItemList *listExpr
+	addToItemList := func(x ...starlarkExpr) {
+		if xItemList == nil {
+			xItemList = &listExpr{[]starlarkExpr{}}
+		}
+		xItemList.items = append(xItemList.items, x...)
+	}
+	finishItemList := func() {
+		if xItemList != nil {
+			xConcat.items = append(xConcat.items, xItemList)
+			xItemList = nil
+		}
+	}
+
+	items := a.Value.Words()
+	for _, item := range items {
+		// A function call in RHS is supposed to return a list, all other item
+		// expressions return individual elements.
+		switch x := ctx.parseMakeString(a, item).(type) {
+		case *badExpr:
+			ctx.wrapBadExpr(x)
+			return nil
+		case *stringLiteralExpr:
+			addToItemList(maybeConvertToStringList(x).(*listExpr).items...)
+		default:
+			switch x.typ() {
+			case starlarkTypeList:
+				finishItemList()
+				xConcat.items = append(xConcat.items, x)
+			case starlarkTypeString:
+				finishItemList()
+				xConcat.items = append(xConcat.items, &callExpr{
+					object:     x,
+					name:       "split",
+					args:       nil,
+					returnType: starlarkTypeList,
+				})
+			default:
+				addToItemList(x)
+			}
+		}
+	}
+	if xItemList != nil {
+		xConcat.items = append(xConcat.items, xItemList)
+	}
+	return xConcat
+}
+
+func (ctx *parseContext) newDependentModule(path string, optional bool) *moduleInfo {
+	modulePath := ctx.loadedModulePath(path)
+	if mi, ok := ctx.dependentModules[modulePath]; ok {
+		mi.optional = mi.optional || optional
+		return mi
+	}
+	moduleName := moduleNameForFile(path)
+	moduleLocalName := "_" + moduleName
+	n, found := ctx.moduleNameCount[moduleName]
+	if found {
+		moduleLocalName += fmt.Sprintf("%d", n)
+	}
+	ctx.moduleNameCount[moduleName] = n + 1
+	mi := &moduleInfo{
+		path:            modulePath,
+		originalPath:    path,
+		moduleLocalName: moduleLocalName,
+		optional:        optional,
+	}
+	ctx.dependentModules[modulePath] = mi
+	ctx.script.inherited = append(ctx.script.inherited, mi)
+	return mi
+}
+
+func (ctx *parseContext) handleSubConfig(
+	v mkparser.Node, pathExpr starlarkExpr, loadAlways bool, processModule func(inheritedModule)) {
+	pathExpr, _ = pathExpr.eval(ctx.builtinMakeVars)
+
+	// In a simple case, the name of a module to inherit/include is known statically.
+	if path, ok := maybeString(pathExpr); ok {
+		if strings.Contains(path, "*") {
+			if paths, err := fs.Glob(ctx.script.sourceFS, path); err == nil {
+				for _, p := range paths {
+					processModule(inheritedStaticModule{ctx.newDependentModule(p, !loadAlways), loadAlways})
+				}
+			} else {
+				ctx.errorf(v, "cannot glob wildcard argument")
+			}
+		} else {
+			processModule(inheritedStaticModule{ctx.newDependentModule(path, !loadAlways), loadAlways})
+		}
+		return
+	}
+
+	// If module path references variables (e.g., $(v1)/foo/$(v2)/device-config.mk), find all the paths in the
+	// source tree that may be a match and the corresponding variable values. For instance, if the source tree
+	// contains vendor1/foo/abc/dev.mk and vendor2/foo/def/dev.mk, the first one will be inherited when
+	// (v1, v2) == ('vendor1', 'abc'), and the second one when (v1, v2) == ('vendor2', 'def').
+	// We then emit the code that loads all of them, e.g.:
+	//    load("//vendor1/foo/abc:dev.rbc", _dev1_init="init")
+	//    load("//vendor2/foo/def/dev.rbc", _dev2_init="init")
+	// And then inherit it as follows:
+	//    _e = {
+	//       "vendor1/foo/abc/dev.mk": ("vendor1/foo/abc/dev", _dev1_init),
+	//       "vendor2/foo/def/dev.mk": ("vendor2/foo/def/dev", _dev_init2) }.get("%s/foo/%s/dev.mk" % (v1, v2))
+	//    if _e:
+	//       rblf.inherit(handle, _e[0], _e[1])
+	//
+	var matchingPaths []string
+	varPath, ok := pathExpr.(*interpolateExpr)
+	if !ok {
+		ctx.errorf(v, "inherit-product/include argument is too complex")
+		return
+	}
+
+	pathPattern := []string{varPath.chunks[0]}
+	for _, chunk := range varPath.chunks[1:] {
+		if chunk != "" {
+			pathPattern = append(pathPattern, chunk)
+		}
+	}
+	if pathPattern[0] != "" {
+		matchingPaths = ctx.findMatchingPaths(pathPattern)
+	} else {
+		// Heuristics -- if pattern starts from top, restrict it to the directories where
+		// we know inherit-product uses dynamically calculated path. Restrict it even further
+		// for certain path which would yield too many useless matches
+		if len(varPath.chunks) == 2 && varPath.chunks[1] == "/BoardConfigVendor.mk" {
+			pathPattern[0] = "vendor/google_devices"
+			matchingPaths = ctx.findMatchingPaths(pathPattern)
+		} else {
+			for _, t := range []string{"vendor/qcom", "vendor/google_devices"} {
+				pathPattern[0] = t
+				matchingPaths = append(matchingPaths, ctx.findMatchingPaths(pathPattern)...)
+			}
+		}
+	}
+	// Safeguard against $(call inherit-product,$(PRODUCT_PATH))
+	const maxMatchingFiles = 150
+	if len(matchingPaths) > maxMatchingFiles {
+		ctx.errorf(v, "there are >%d files matching the pattern, please rewrite it", maxMatchingFiles)
+		return
+	}
+	res := inheritedDynamicModule{*varPath, []*moduleInfo{}, loadAlways}
+	for _, p := range matchingPaths {
+		// A product configuration files discovered dynamically may attempt to inherit
+		// from another one which does not exist in this source tree. Prevent load errors
+		// by always loading the dynamic files as optional.
+		res.candidateModules = append(res.candidateModules, ctx.newDependentModule(p, true))
+	}
+	processModule(res)
+}
+
+func (ctx *parseContext) findMatchingPaths(pattern []string) []string {
+	files := ctx.script.makefileFinder.Find(ctx.script.topDir)
+	if len(pattern) == 0 {
+		return files
+	}
+
+	// Create regular expression from the pattern
+	s_regexp := "^" + regexp.QuoteMeta(pattern[0])
+	for _, s := range pattern[1:] {
+		s_regexp += ".*" + regexp.QuoteMeta(s)
+	}
+	s_regexp += "$"
+	rex := regexp.MustCompile(s_regexp)
+
+	// Now match
+	var res []string
+	for _, p := range files {
+		if rex.MatchString(p) {
+			res = append(res, p)
+		}
+	}
+	return res
+}
+
+func (ctx *parseContext) handleInheritModule(v mkparser.Node, pathExpr starlarkExpr, loadAlways bool) {
+	ctx.handleSubConfig(v, pathExpr, loadAlways, func(im inheritedModule) {
+		ctx.receiver.newNode(&inheritNode{im})
+	})
+}
+
+func (ctx *parseContext) handleInclude(v mkparser.Node, pathExpr starlarkExpr, loadAlways bool) {
+	ctx.handleSubConfig(v, pathExpr, loadAlways, func(im inheritedModule) {
+		ctx.receiver.newNode(&includeNode{im})
+	})
+}
+
+func (ctx *parseContext) handleVariable(v *mkparser.Variable) {
+	// Handle:
+	//   $(call inherit-product,...)
+	//   $(call inherit-product-if-exists,...)
+	//   $(info xxx)
+	//   $(warning xxx)
+	//   $(error xxx)
+	expr := ctx.parseReference(v, v.Name)
+	switch x := expr.(type) {
+	case *callExpr:
+		if x.name == callLoadAlways || x.name == callLoadIf {
+			ctx.handleInheritModule(v, x.args[0], x.name == callLoadAlways)
+		} else if isMakeControlFunc(x.name) {
+			// File name is the first argument
+			args := []starlarkExpr{
+				&stringLiteralExpr{ctx.script.mkFile},
+				x.args[0],
+			}
+			ctx.receiver.newNode(&exprNode{
+				&callExpr{name: x.name, args: args, returnType: starlarkTypeUnknown},
+			})
+		} else {
+			ctx.receiver.newNode(&exprNode{expr})
+		}
+	case *badExpr:
+		ctx.wrapBadExpr(x)
+		return
+	default:
+		ctx.errorf(v, "cannot handle %s", v.Dump())
+		return
+	}
+}
+
+func (ctx *parseContext) handleDefine(directive *mkparser.Directive) {
+	macro_name := strings.Fields(directive.Args.Strings[0])[0]
+	// Ignore the macros that we handle
+	if _, ok := knownFunctions[macro_name]; !ok {
+		ctx.errorf(directive, "define is not supported: %s", macro_name)
+	}
+}
+
+func (ctx *parseContext) handleIfBlock(ifDirective *mkparser.Directive) {
+	ssSwitch := &switchNode{}
+	ctx.pushReceiver(ssSwitch)
+	for ctx.processBranch(ifDirective); ctx.hasNodes() && ctx.fatalError == nil; {
+		node := ctx.getNode()
+		switch x := node.(type) {
+		case *mkparser.Directive:
+			switch x.Name {
+			case "else", "elifdef", "elifndef", "elifeq", "elifneq":
+				ctx.processBranch(x)
+			case "endif":
+				ctx.popReceiver()
+				ctx.receiver.newNode(ssSwitch)
+				return
+			default:
+				ctx.errorf(node, "unexpected directive %s", x.Name)
+			}
+		default:
+			ctx.errorf(ifDirective, "unexpected statement")
+		}
+	}
+	if ctx.fatalError == nil {
+		ctx.fatalError = fmt.Errorf("no matching endif for %s", ifDirective.Dump())
+	}
+	ctx.popReceiver()
+}
+
+// processBranch processes a single branch (if/elseif/else) until the next directive
+// on the same level.
+func (ctx *parseContext) processBranch(check *mkparser.Directive) {
+	block := switchCase{gate: ctx.parseCondition(check)}
+	defer func() {
+		ctx.popVarAssignments()
+		ctx.ifNestLevel--
+
+	}()
+	ctx.pushVarAssignments()
+	ctx.ifNestLevel++
+
+	ctx.pushReceiver(&block)
+	for ctx.hasNodes() {
+		node := ctx.getNode()
+		if ctx.handleSimpleStatement(node) {
+			continue
+		}
+		switch d := node.(type) {
+		case *mkparser.Directive:
+			switch d.Name {
+			case "else", "elifdef", "elifndef", "elifeq", "elifneq", "endif":
+				ctx.popReceiver()
+				ctx.receiver.newNode(&block)
+				ctx.backNode()
+				return
+			case "ifdef", "ifndef", "ifeq", "ifneq":
+				ctx.handleIfBlock(d)
+			default:
+				ctx.errorf(d, "unexpected directive %s", d.Name)
+			}
+		default:
+			ctx.errorf(node, "unexpected statement")
+		}
+	}
+	ctx.fatalError = fmt.Errorf("no matching endif for %s", check.Dump())
+	ctx.popReceiver()
+}
+
+func (ctx *parseContext) newIfDefinedNode(check *mkparser.Directive) (starlarkExpr, bool) {
+	if !check.Args.Const() {
+		return ctx.newBadExpr(check, "ifdef variable ref too complex: %s", check.Args.Dump()), false
+	}
+	v := ctx.addVariable(check.Args.Strings[0])
+	return &variableDefinedExpr{v}, true
+}
+
+func (ctx *parseContext) parseCondition(check *mkparser.Directive) starlarkNode {
+	switch check.Name {
+	case "ifdef", "ifndef", "elifdef", "elifndef":
+		v, ok := ctx.newIfDefinedNode(check)
+		if ok && strings.HasSuffix(check.Name, "ndef") {
+			v = &notExpr{v}
+		}
+		return &ifNode{
+			isElif: strings.HasPrefix(check.Name, "elif"),
+			expr:   v,
+		}
+	case "ifeq", "ifneq", "elifeq", "elifneq":
+		return &ifNode{
+			isElif: strings.HasPrefix(check.Name, "elif"),
+			expr:   ctx.parseCompare(check),
+		}
+	case "else":
+		return &elseNode{}
+	default:
+		panic(fmt.Errorf("%s: unknown directive: %s", ctx.script.mkFile, check.Dump()))
+	}
+}
+
+func (ctx *parseContext) newBadExpr(node mkparser.Node, text string, args ...interface{}) starlarkExpr {
+	message := fmt.Sprintf(text, args...)
+	if ctx.errorLogger != nil {
+		ctx.errorLogger.NewError(text, node, args)
+	}
+	ctx.script.hasErrors = true
+	return &badExpr{node, message}
+}
+
+func (ctx *parseContext) parseCompare(cond *mkparser.Directive) starlarkExpr {
+	// Strip outer parentheses
+	mkArg := cloneMakeString(cond.Args)
+	mkArg.Strings[0] = strings.TrimLeft(mkArg.Strings[0], "( ")
+	n := len(mkArg.Strings)
+	mkArg.Strings[n-1] = strings.TrimRight(mkArg.Strings[n-1], ") ")
+	args := mkArg.Split(",")
+	// TODO(asmundak): handle the case where the arguments are in quotes and space-separated
+	if len(args) != 2 {
+		return ctx.newBadExpr(cond, "ifeq/ifneq len(args) != 2 %s", cond.Dump())
+	}
+	args[0].TrimRightSpaces()
+	args[1].TrimLeftSpaces()
+
+	isEq := !strings.HasSuffix(cond.Name, "neq")
+	switch xLeft := ctx.parseMakeString(cond, args[0]).(type) {
+	case *stringLiteralExpr, *variableRefExpr:
+		switch xRight := ctx.parseMakeString(cond, args[1]).(type) {
+		case *stringLiteralExpr, *variableRefExpr:
+			return &eqExpr{left: xLeft, right: xRight, isEq: isEq}
+		case *badExpr:
+			return xRight
+		default:
+			expr, ok := ctx.parseCheckFunctionCallResult(cond, xLeft, args[1])
+			if ok {
+				return expr
+			}
+			return ctx.newBadExpr(cond, "right operand is too complex: %s", args[1].Dump())
+		}
+	case *badExpr:
+		return xLeft
+	default:
+		switch xRight := ctx.parseMakeString(cond, args[1]).(type) {
+		case *stringLiteralExpr, *variableRefExpr:
+			expr, ok := ctx.parseCheckFunctionCallResult(cond, xRight, args[0])
+			if ok {
+				return expr
+			}
+			return ctx.newBadExpr(cond, "left operand is too complex: %s", args[0].Dump())
+		case *badExpr:
+			return xRight
+		default:
+			return ctx.newBadExpr(cond, "operands are too complex: (%s,%s)", args[0].Dump(), args[1].Dump())
+		}
+	}
+}
+
+func (ctx *parseContext) parseCheckFunctionCallResult(directive *mkparser.Directive, xValue starlarkExpr,
+	varArg *mkparser.MakeString) (starlarkExpr, bool) {
+	mkSingleVar, ok := varArg.SingleVariable()
+	if !ok {
+		return nil, false
+	}
+	expr := ctx.parseReference(directive, mkSingleVar)
+	negate := strings.HasSuffix(directive.Name, "neq")
+	checkIsSomethingFunction := func(xCall *callExpr) starlarkExpr {
+		s, ok := maybeString(xValue)
+		if !ok || s != "true" {
+			return ctx.newBadExpr(directive,
+				fmt.Sprintf("the result of %s can be compared only to 'true'", xCall.name))
+		}
+		if len(xCall.args) < 1 {
+			return ctx.newBadExpr(directive, "%s requires an argument", xCall.name)
+		}
+		return nil
+	}
+	switch x := expr.(type) {
+	case *callExpr:
+		switch x.name {
+		case "filter":
+			return ctx.parseCompareFilterFuncResult(directive, x, xValue, !negate), true
+		case "filter-out":
+			return ctx.parseCompareFilterFuncResult(directive, x, xValue, negate), true
+		case "wildcard":
+			return ctx.parseCompareWildcardFuncResult(directive, x, xValue, negate), true
+		case "findstring":
+			return ctx.parseCheckFindstringFuncResult(directive, x, xValue, negate), true
+		case "strip":
+			return ctx.parseCompareStripFuncResult(directive, x, xValue, negate), true
+		case "is-board-platform":
+			if xBad := checkIsSomethingFunction(x); xBad != nil {
+				return xBad, true
+			}
+			return &eqExpr{
+				left:  &variableRefExpr{ctx.addVariable("TARGET_BOARD_PLATFORM"), false},
+				right: x.args[0],
+				isEq:  !negate,
+			}, true
+		case "is-board-platform-in-list":
+			if xBad := checkIsSomethingFunction(x); xBad != nil {
+				return xBad, true
+			}
+			return &inExpr{
+				expr:  &variableRefExpr{ctx.addVariable("TARGET_BOARD_PLATFORM"), false},
+				list:  maybeConvertToStringList(x.args[0]),
+				isNot: negate,
+			}, true
+		case "is-product-in-list":
+			if xBad := checkIsSomethingFunction(x); xBad != nil {
+				return xBad, true
+			}
+			return &inExpr{
+				expr:  &variableRefExpr{ctx.addVariable("TARGET_PRODUCT"), true},
+				list:  maybeConvertToStringList(x.args[0]),
+				isNot: negate,
+			}, true
+		case "is-vendor-board-platform":
+			if xBad := checkIsSomethingFunction(x); xBad != nil {
+				return xBad, true
+			}
+			s, ok := maybeString(x.args[0])
+			if !ok {
+				return ctx.newBadExpr(directive, "cannot handle non-constant argument to is-vendor-board-platform"), true
+			}
+			return &inExpr{
+				expr:  &variableRefExpr{ctx.addVariable("TARGET_BOARD_PLATFORM"), false},
+				list:  &variableRefExpr{ctx.addVariable(s + "_BOARD_PLATFORMS"), true},
+				isNot: negate,
+			}, true
+		default:
+			return ctx.newBadExpr(directive, "Unknown function in ifeq: %s", x.name), true
+		}
+	case *badExpr:
+		return x, true
+	default:
+		return nil, false
+	}
+}
+
+func (ctx *parseContext) parseCompareFilterFuncResult(cond *mkparser.Directive,
+	filterFuncCall *callExpr, xValue starlarkExpr, negate bool) starlarkExpr {
+	// We handle:
+	// *  ifeq/ifneq (,$(filter v1 v2 ..., EXPR) becomes if EXPR not in/in ["v1", "v2", ...]
+	// *  ifeq/ifneq (,$(filter EXPR, v1 v2 ...) becomes if EXPR not in/in ["v1", "v2", ...]
+	// *  ifeq/ifneq ($(VAR),$(filter $(VAR), v1 v2 ...) becomes if VAR in/not in ["v1", "v2"]
+	// TODO(Asmundak): check the last case works for filter-out, too.
+	xPattern := filterFuncCall.args[0]
+	xText := filterFuncCall.args[1]
+	var xInList *stringLiteralExpr
+	var expr starlarkExpr
+	var ok bool
+	switch x := xValue.(type) {
+	case *stringLiteralExpr:
+		if x.literal != "" {
+			return ctx.newBadExpr(cond, "filter comparison to non-empty value: %s", xValue)
+		}
+		// Either pattern or text should be const, and the
+		// non-const one should be varRefExpr
+		if xInList, ok = xPattern.(*stringLiteralExpr); ok {
+			expr = xText
+		} else if xInList, ok = xText.(*stringLiteralExpr); ok {
+			expr = xPattern
+		} else {
+			return &callExpr{
+				object:     nil,
+				name:       filterFuncCall.name,
+				args:       filterFuncCall.args,
+				returnType: starlarkTypeBool,
+			}
+		}
+	case *variableRefExpr:
+		if v, ok := xPattern.(*variableRefExpr); ok {
+			if xInList, ok = xText.(*stringLiteralExpr); ok && v.ref.name() == x.ref.name() {
+				// ifeq/ifneq ($(VAR),$(filter $(VAR), v1 v2 ...), flip negate,
+				// it's the opposite to what is done when comparing to empty.
+				expr = xPattern
+				negate = !negate
+			}
+		}
+	}
+	if expr != nil && xInList != nil {
+		slExpr := newStringListExpr(strings.Fields(xInList.literal))
+		// Generate simpler code for the common cases:
+		if expr.typ() == starlarkTypeList {
+			if len(slExpr.items) == 1 {
+				// Checking that a string belongs to list
+				return &inExpr{isNot: negate, list: expr, expr: slExpr.items[0]}
+			} else {
+				// TODO(asmundak):
+				panic("TBD")
+			}
+		} else if len(slExpr.items) == 1 {
+			return &eqExpr{left: expr, right: slExpr.items[0], isEq: !negate}
+		} else {
+			return &inExpr{isNot: negate, list: newStringListExpr(strings.Fields(xInList.literal)), expr: expr}
+		}
+	}
+	return ctx.newBadExpr(cond, "filter arguments are too complex: %s", cond.Dump())
+}
+
+func (ctx *parseContext) parseCompareWildcardFuncResult(directive *mkparser.Directive,
+	xCall *callExpr, xValue starlarkExpr, negate bool) starlarkExpr {
+	if !isEmptyString(xValue) {
+		return ctx.newBadExpr(directive, "wildcard result can be compared only to empty: %s", xValue)
+	}
+	callFunc := wildcardExistsPhony
+	if s, ok := xCall.args[0].(*stringLiteralExpr); ok && !strings.ContainsAny(s.literal, "*?{[") {
+		callFunc = fileExistsPhony
+	}
+	var cc starlarkExpr = &callExpr{name: callFunc, args: xCall.args, returnType: starlarkTypeBool}
+	if !negate {
+		cc = &notExpr{cc}
+	}
+	return cc
+}
+
+func (ctx *parseContext) parseCheckFindstringFuncResult(directive *mkparser.Directive,
+	xCall *callExpr, xValue starlarkExpr, negate bool) starlarkExpr {
+	if isEmptyString(xValue) {
+		return &eqExpr{
+			left: &callExpr{
+				object:     xCall.args[1],
+				name:       "find",
+				args:       []starlarkExpr{xCall.args[0]},
+				returnType: starlarkTypeInt,
+			},
+			right: &intLiteralExpr{-1},
+			isEq:  !negate,
+		}
+	}
+	return ctx.newBadExpr(directive, "findstring result can be compared only to empty: %s", xValue)
+}
+
+func (ctx *parseContext) parseCompareStripFuncResult(directive *mkparser.Directive,
+	xCall *callExpr, xValue starlarkExpr, negate bool) starlarkExpr {
+	if _, ok := xValue.(*stringLiteralExpr); !ok {
+		return ctx.newBadExpr(directive, "strip result can be compared only to string: %s", xValue)
+	}
+	return &eqExpr{
+		left: &callExpr{
+			name:       "strip",
+			args:       xCall.args,
+			returnType: starlarkTypeString,
+		},
+		right: xValue, isEq: !negate}
+}
+
+// parses $(...), returning an expression
+func (ctx *parseContext) parseReference(node mkparser.Node, ref *mkparser.MakeString) starlarkExpr {
+	ref.TrimLeftSpaces()
+	ref.TrimRightSpaces()
+	refDump := ref.Dump()
+
+	// Handle only the case where the first (or only) word is constant
+	words := ref.SplitN(" ", 2)
+	if !words[0].Const() {
+		return ctx.newBadExpr(node, "reference is too complex: %s", refDump)
+	}
+
+	// If it is a single word, it can be a simple variable
+	// reference or a function call
+	if len(words) == 1 {
+		if isMakeControlFunc(refDump) || refDump == "shell" {
+			return &callExpr{
+				name:       refDump,
+				args:       []starlarkExpr{&stringLiteralExpr{""}},
+				returnType: starlarkTypeUnknown,
+			}
+		}
+		if v := ctx.addVariable(refDump); v != nil {
+			return &variableRefExpr{v, ctx.lastAssignment(v.name()) != nil}
+		}
+		return ctx.newBadExpr(node, "unknown variable %s", refDump)
+	}
+
+	expr := &callExpr{name: words[0].Dump(), returnType: starlarkTypeUnknown}
+	args := words[1]
+	args.TrimLeftSpaces()
+	// Make control functions and shell need special treatment as everything
+	// after the name is a single text argument
+	if isMakeControlFunc(expr.name) || expr.name == "shell" {
+		x := ctx.parseMakeString(node, args)
+		if xBad, ok := x.(*badExpr); ok {
+			return xBad
+		}
+		expr.args = []starlarkExpr{x}
+		return expr
+	}
+	if expr.name == "call" {
+		words = args.SplitN(",", 2)
+		if words[0].Empty() || !words[0].Const() {
+			return ctx.newBadExpr(node, "cannot handle %s", refDump)
+		}
+		expr.name = words[0].Dump()
+		if len(words) < 2 {
+			args = &mkparser.MakeString{}
+		} else {
+			args = words[1]
+		}
+	}
+	if kf, found := knownFunctions[expr.name]; found {
+		expr.returnType = kf.returnType
+	} else {
+		return ctx.newBadExpr(node, "cannot handle invoking %s", expr.name)
+	}
+	switch expr.name {
+	case "word":
+		return ctx.parseWordFunc(node, args)
+	case "firstword", "lastword":
+		return ctx.parseFirstOrLastwordFunc(node, expr.name, args)
+	case "my-dir":
+		return &variableRefExpr{ctx.addVariable("LOCAL_PATH"), true}
+	case "subst", "patsubst":
+		return ctx.parseSubstFunc(node, expr.name, args)
+	default:
+		for _, arg := range args.Split(",") {
+			arg.TrimLeftSpaces()
+			arg.TrimRightSpaces()
+			x := ctx.parseMakeString(node, arg)
+			if xBad, ok := x.(*badExpr); ok {
+				return xBad
+			}
+			expr.args = append(expr.args, x)
+		}
+	}
+	return expr
+}
+
+func (ctx *parseContext) parseSubstFunc(node mkparser.Node, fname string, args *mkparser.MakeString) starlarkExpr {
+	words := args.Split(",")
+	if len(words) != 3 {
+		return ctx.newBadExpr(node, "%s function should have 3 arguments", fname)
+	}
+	if !words[0].Const() || !words[1].Const() {
+		return ctx.newBadExpr(node, "%s function's from and to arguments should be constant", fname)
+	}
+	from := words[0].Strings[0]
+	to := words[1].Strings[0]
+	words[2].TrimLeftSpaces()
+	words[2].TrimRightSpaces()
+	obj := ctx.parseMakeString(node, words[2])
+	typ := obj.typ()
+	if typ == starlarkTypeString && fname == "subst" {
+		// Optimization: if it's $(subst from, to, string), emit string.replace(from, to)
+		return &callExpr{
+			object:     obj,
+			name:       "replace",
+			args:       []starlarkExpr{&stringLiteralExpr{from}, &stringLiteralExpr{to}},
+			returnType: typ,
+		}
+	}
+	return &callExpr{
+		name:       fname,
+		args:       []starlarkExpr{&stringLiteralExpr{from}, &stringLiteralExpr{to}, obj},
+		returnType: obj.typ(),
+	}
+}
+
+func (ctx *parseContext) parseWordFunc(node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
+	words := args.Split(",")
+	if len(words) != 2 {
+		return ctx.newBadExpr(node, "word function should have 2 arguments")
+	}
+	var index uint64 = 0
+	if words[0].Const() {
+		index, _ = strconv.ParseUint(strings.TrimSpace(words[0].Strings[0]), 10, 64)
+	}
+	if index < 1 {
+		return ctx.newBadExpr(node, "word index should be constant positive integer")
+	}
+	words[1].TrimLeftSpaces()
+	words[1].TrimRightSpaces()
+	array := ctx.parseMakeString(node, words[1])
+	if xBad, ok := array.(*badExpr); ok {
+		return xBad
+	}
+	if array.typ() != starlarkTypeList {
+		array = &callExpr{object: array, name: "split", returnType: starlarkTypeList}
+	}
+	return indexExpr{array, &intLiteralExpr{int(index - 1)}}
+}
+
+func (ctx *parseContext) parseFirstOrLastwordFunc(node mkparser.Node, name string, args *mkparser.MakeString) starlarkExpr {
+	arg := ctx.parseMakeString(node, args)
+	if bad, ok := arg.(*badExpr); ok {
+		return bad
+	}
+	index := &intLiteralExpr{0}
+	if name == "lastword" {
+		if v, ok := arg.(*variableRefExpr); ok && v.ref.name() == "MAKEFILE_LIST" {
+			return &stringLiteralExpr{ctx.script.mkFile}
+		}
+		index.literal = -1
+	}
+	if arg.typ() == starlarkTypeList {
+		return &indexExpr{arg, index}
+	}
+	return &indexExpr{&callExpr{object: arg, name: "split", returnType: starlarkTypeList}, index}
+}
+
+func (ctx *parseContext) parseMakeString(node mkparser.Node, mk *mkparser.MakeString) starlarkExpr {
+	if mk.Const() {
+		return &stringLiteralExpr{mk.Dump()}
+	}
+	if mkRef, ok := mk.SingleVariable(); ok {
+		return ctx.parseReference(node, mkRef)
+	}
+	// If we reached here, it's neither string literal nor a simple variable,
+	// we need a full-blown interpolation node that will generate
+	// "a%b%c" % (X, Y) for a$(X)b$(Y)c
+	xInterp := &interpolateExpr{args: make([]starlarkExpr, len(mk.Variables))}
+	for i, ref := range mk.Variables {
+		arg := ctx.parseReference(node, ref.Name)
+		if x, ok := arg.(*badExpr); ok {
+			return x
+		}
+		xInterp.args[i] = arg
+	}
+	xInterp.chunks = append(xInterp.chunks, mk.Strings...)
+	return xInterp
+}
+
+// Handles the statements whose treatment is the same in all contexts: comment,
+// assignment, variable (which is a macro call in reality) and all constructs that
+// do not handle in any context ('define directive and any unrecognized stuff).
+// Return true if we handled it.
+func (ctx *parseContext) handleSimpleStatement(node mkparser.Node) bool {
+	handled := true
+	switch x := node.(type) {
+	case *mkparser.Comment:
+		ctx.insertComment("#" + x.Comment)
+	case *mkparser.Assignment:
+		ctx.handleAssignment(x)
+	case *mkparser.Variable:
+		ctx.handleVariable(x)
+	case *mkparser.Directive:
+		switch x.Name {
+		case "define":
+			ctx.handleDefine(x)
+		case "include", "-include":
+			ctx.handleInclude(node, ctx.parseMakeString(node, x.Args), x.Name[0] != '-')
+		default:
+			handled = false
+		}
+	default:
+		ctx.errorf(x, "unsupported line %s", x.Dump())
+	}
+	return handled
+}
+
+func (ctx *parseContext) insertComment(s string) {
+	ctx.receiver.newNode(&commentNode{strings.TrimSpace(s)})
+}
+
+func (ctx *parseContext) carryAsComment(failedNode mkparser.Node) {
+	for _, line := range strings.Split(failedNode.Dump(), "\n") {
+		ctx.insertComment("# " + line)
+	}
+}
+
+// records that the given node failed to be converted and includes an explanatory message
+func (ctx *parseContext) errorf(failedNode mkparser.Node, message string, args ...interface{}) {
+	if ctx.errorLogger != nil {
+		ctx.errorLogger.NewError(message, failedNode, args...)
+	}
+	message = fmt.Sprintf(message, args...)
+	ctx.insertComment(fmt.Sprintf("# MK2RBC TRANSLATION ERROR: %s", message))
+	ctx.carryAsComment(failedNode)
+	ctx.script.hasErrors = true
+}
+
+func (ctx *parseContext) wrapBadExpr(xBad *badExpr) {
+	ctx.insertComment(fmt.Sprintf("# MK2RBC TRANSLATION ERROR: %s", xBad.message))
+	ctx.carryAsComment(xBad.node)
+}
+
+func (ctx *parseContext) loadedModulePath(path string) string {
+	// During the transition to Roboleaf some of the product configuration files
+	// will be converted and checked in while the others will be generated on the fly
+	// and run. The runner  (rbcrun application) accommodates this by allowing three
+	// different ways to specify the loaded file location:
+	//  1) load(":<file>",...) loads <file> from the same directory
+	//  2) load("//path/relative/to/source/root:<file>", ...) loads <file> source tree
+	//  3) load("/absolute/path/to/<file> absolute path
+	// If the file being generated and the file it wants to load are in the same directory,
+	// generate option 1.
+	// Otherwise, if output directory is not specified, generate 2)
+	// Finally, if output directory has been specified and the file being generated and
+	// the file it wants to load from are in the different directories, generate 2) or 3):
+	//  * if the file being loaded exists in the source tree, generate 2)
+	//  * otherwise, generate 3)
+	// Finally, figure out the loaded module path and name and create a node for it
+	loadedModuleDir := filepath.Dir(path)
+	base := filepath.Base(path)
+	loadedModuleName := strings.TrimSuffix(base, filepath.Ext(base)) + ctx.outputSuffix
+	if loadedModuleDir == filepath.Dir(ctx.script.mkFile) {
+		return ":" + loadedModuleName
+	}
+	if ctx.outputDir == "" {
+		return fmt.Sprintf("//%s:%s", loadedModuleDir, loadedModuleName)
+	}
+	if _, err := os.Stat(filepath.Join(loadedModuleDir, loadedModuleName)); err == nil {
+		return fmt.Sprintf("//%s:%s", loadedModuleDir, loadedModuleName)
+	}
+	return filepath.Join(ctx.outputDir, loadedModuleDir, loadedModuleName)
+}
+
+func (ctx *parseContext) addSoongNamespace(ns string) {
+	if _, ok := ctx.soongNamespaces[ns]; ok {
+		return
+	}
+	ctx.soongNamespaces[ns] = make(map[string]bool)
+}
+
+func (ctx *parseContext) hasSoongNamespace(name string) bool {
+	_, ok := ctx.soongNamespaces[name]
+	return ok
+}
+
+func (ctx *parseContext) updateSoongNamespace(replace bool, namespaceName string, varNames []string) {
+	ctx.addSoongNamespace(namespaceName)
+	vars := ctx.soongNamespaces[namespaceName]
+	if replace {
+		vars = make(map[string]bool)
+		ctx.soongNamespaces[namespaceName] = vars
+	}
+	for _, v := range varNames {
+		vars[v] = true
+	}
+}
+
+func (ctx *parseContext) hasNamespaceVar(namespaceName string, varName string) bool {
+	vars, ok := ctx.soongNamespaces[namespaceName]
+	if ok {
+		_, ok = vars[varName]
+	}
+	return ok
+}
+
+func (ss *StarlarkScript) String() string {
+	return NewGenerateContext(ss).emit()
+}
+
+func (ss *StarlarkScript) SubConfigFiles() []string {
+
+	var subs []string
+	for _, src := range ss.inherited {
+		subs = append(subs, src.originalPath)
+	}
+	return subs
+}
+
+func (ss *StarlarkScript) HasErrors() bool {
+	return ss.hasErrors
+}
+
+// Convert reads and parses a makefile. If successful, parsed tree
+// is returned and then can be passed to String() to get the generated
+// Starlark file.
+func Convert(req Request) (*StarlarkScript, error) {
+	reader := req.Reader
+	if reader == nil {
+		mkContents, err := ioutil.ReadFile(req.MkFile)
+		if err != nil {
+			return nil, err
+		}
+		reader = bytes.NewBuffer(mkContents)
+	}
+	parser := mkparser.NewParser(req.MkFile, reader)
+	nodes, errs := parser.Parse()
+	if len(errs) > 0 {
+		for _, e := range errs {
+			fmt.Fprintln(os.Stderr, "ERROR:", e)
+		}
+		return nil, fmt.Errorf("bad makefile %s", req.MkFile)
+	}
+	starScript := &StarlarkScript{
+		moduleName:         moduleNameForFile(req.MkFile),
+		mkFile:             req.MkFile,
+		topDir:             req.RootDir,
+		traceCalls:         req.TraceCalls,
+		warnPartialSuccess: req.WarnPartialSuccess,
+		sourceFS:           req.SourceFS,
+		makefileFinder:     req.MakefileFinder,
+	}
+	ctx := newParseContext(starScript, nodes)
+	ctx.outputSuffix = req.OutputSuffix
+	ctx.outputDir = req.OutputDir
+	ctx.errorLogger = req.ErrorLogger
+	if len(req.TracedVariables) > 0 {
+		ctx.tracedVariables = make(map[string]bool)
+		for _, v := range req.TracedVariables {
+			ctx.tracedVariables[v] = true
+		}
+	}
+	ctx.pushReceiver(starScript)
+	for ctx.hasNodes() && ctx.fatalError == nil {
+		node := ctx.getNode()
+		if ctx.handleSimpleStatement(node) {
+			continue
+		}
+		switch x := node.(type) {
+		case *mkparser.Directive:
+			switch x.Name {
+			case "ifeq", "ifneq", "ifdef", "ifndef":
+				ctx.handleIfBlock(x)
+			default:
+				ctx.errorf(x, "unexpected directive %s", x.Name)
+			}
+		default:
+			ctx.errorf(x, "unsupported line")
+		}
+	}
+	if ctx.fatalError != nil {
+		return nil, ctx.fatalError
+	}
+	return starScript, nil
+}
+
+func Launcher(path, name string) string {
+	var buf bytes.Buffer
+	fmt.Fprintf(&buf, "load(%q, %q)\n", baseUri, baseName)
+	fmt.Fprintf(&buf, "load(%q, \"init\")\n", path)
+	fmt.Fprintf(&buf, "g, config = %s(%q, init)\n", cfnMain, name)
+	fmt.Fprintf(&buf, "%s(g, config)\n", cfnPrintVars)
+	return buf.String()
+}
+
+func MakePath2ModuleName(mkPath string) string {
+	return strings.TrimSuffix(mkPath, filepath.Ext(mkPath))
+}
diff --git a/mk2rbc/mk2rbc_test.go b/mk2rbc/mk2rbc_test.go
new file mode 100644
index 0000000..46212ee
--- /dev/null
+++ b/mk2rbc/mk2rbc_test.go
@@ -0,0 +1,1009 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package mk2rbc
+
+import (
+	"bytes"
+	"io/fs"
+	"path/filepath"
+	"strings"
+	"testing"
+)
+
+var testCases = []struct {
+	desc     string
+	mkname   string
+	in       string
+	expected string
+}{
+	{
+		desc:   "Comment",
+		mkname: "product.mk",
+		in: `
+# Comment
+# FOO= a\
+     b
+`,
+		expected: `# Comment
+# FOO= a
+#     b
+load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+`,
+	},
+	{
+		desc:   "Name conversion",
+		mkname: "path/bar-baz.mk",
+		in: `
+# Comment
+`,
+		expected: `# Comment
+load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+`,
+	},
+	{
+		desc:   "Item variable",
+		mkname: "pixel3.mk",
+		in: `
+PRODUCT_NAME := Pixel 3
+PRODUCT_MODEL :=
+local_var = foo
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  cfg["PRODUCT_NAME"] = "Pixel 3"
+  cfg["PRODUCT_MODEL"] = ""
+  _local_var = "foo"
+`,
+	},
+	{
+		desc:   "List variable",
+		mkname: "pixel4.mk",
+		in: `
+PRODUCT_PACKAGES = package1  package2
+PRODUCT_COPY_FILES += file2:target
+PRODUCT_PACKAGES += package3
+PRODUCT_COPY_FILES =
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  cfg["PRODUCT_PACKAGES"] = [
+      "package1",
+      "package2",
+  ]
+  rblf.setdefault(handle, "PRODUCT_COPY_FILES")
+  cfg["PRODUCT_COPY_FILES"] += ["file2:target"]
+  cfg["PRODUCT_PACKAGES"] += ["package3"]
+  cfg["PRODUCT_COPY_FILES"] = []
+`,
+	},
+	{
+		desc:   "Unknown function",
+		mkname: "product.mk",
+		in: `
+PRODUCT_NAME := $(call foo1, bar)
+PRODUCT_NAME := $(call foo0)
+`,
+		expected: `# MK2RBC TRANSLATION ERROR: cannot handle invoking foo1
+# PRODUCT_NAME := $(call foo1, bar)
+# MK2RBC TRANSLATION ERROR: cannot handle invoking foo0
+# PRODUCT_NAME := $(call foo0)
+load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  rblf.warning("product.mk", "partially successful conversion")
+`,
+	},
+	{
+		desc:   "Inherit configuration always",
+		mkname: "product.mk",
+		in: `
+ifdef PRODUCT_NAME
+$(call inherit-product, part.mk)
+else # Comment
+$(call inherit-product, $(LOCAL_PATH)/part.mk)
+endif
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+load(":part.star", _part_init = "init")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  if g.get("PRODUCT_NAME") != None:
+    rblf.inherit(handle, "part", _part_init)
+  else:
+    # Comment
+    rblf.inherit(handle, "part", _part_init)
+`,
+	},
+	{
+		desc:   "Inherit configuration if it exists",
+		mkname: "product.mk",
+		in: `
+$(call inherit-product-if-exists, part.mk)
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+load(":part.star|init", _part_init = "init")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  if _part_init:
+    rblf.inherit(handle, "part", _part_init)
+`,
+	},
+
+	{
+		desc:   "Include configuration",
+		mkname: "product.mk",
+		in: `
+ifdef PRODUCT_NAME
+include part.mk
+else
+-include $(LOCAL_PATH)/part.mk)
+endif
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+load(":part.star|init", _part_init = "init")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  if g.get("PRODUCT_NAME") != None:
+    _part_init(g, handle)
+  else:
+    if _part_init != None:
+      _part_init(g, handle)
+`,
+	},
+
+	{
+		desc:   "Synonymous inherited configurations",
+		mkname: "path/product.mk",
+		in: `
+$(call inherit-product, */font.mk)
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+load("//foo:font.star", _font_init = "init")
+load("//bar:font.star", _font1_init = "init")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  rblf.inherit(handle, "foo/font", _font_init)
+  rblf.inherit(handle, "bar/font", _font1_init)
+`,
+	},
+	{
+		desc:   "Directive define",
+		mkname: "product.mk",
+		in: `
+define some-macro
+    $(info foo)
+endef
+`,
+		expected: `# MK2RBC TRANSLATION ERROR: define is not supported: some-macro
+# define  some-macro
+#     $(info foo)
+# endef
+load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  rblf.warning("product.mk", "partially successful conversion")
+`,
+	},
+	{
+		desc:   "Ifdef",
+		mkname: "product.mk",
+		in: `
+ifdef  PRODUCT_NAME
+  PRODUCT_NAME = gizmo
+else
+endif
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  if g.get("PRODUCT_NAME") != None:
+    cfg["PRODUCT_NAME"] = "gizmo"
+  else:
+    pass
+`,
+	},
+	{
+		desc:   "Simple functions",
+		mkname: "product.mk",
+		in: `
+$(warning this is the warning)
+$(warning)
+$(info this is the info)
+$(error this is the error)
+PRODUCT_NAME:=$(shell echo *)
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  rblf.mkwarning("product.mk", "this is the warning")
+  rblf.mkwarning("product.mk", "")
+  rblf.mkinfo("product.mk", "this is the info")
+  rblf.mkerror("product.mk", "this is the error")
+  cfg["PRODUCT_NAME"] = rblf.shell("echo *")
+`,
+	},
+	{
+		desc:   "Empty if",
+		mkname: "product.mk",
+		in: `
+ifdef PRODUCT_NAME
+# Comment
+else
+  TARGET_COPY_OUT_RECOVERY := foo
+endif
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  if g.get("PRODUCT_NAME") != None:
+    # Comment
+    pass
+  else:
+    # MK2RBC TRANSLATION ERROR: cannot set predefined variable TARGET_COPY_OUT_RECOVERY to "foo", its value should be "recovery"
+    pass
+  rblf.warning("product.mk", "partially successful conversion")
+`,
+	},
+	{
+		desc:   "if/else/endif",
+		mkname: "product.mk",
+		in: `
+ifndef PRODUCT_NAME
+  PRODUCT_NAME=gizmo1
+else
+  PRODUCT_NAME=gizmo2
+endif
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  if not g.get("PRODUCT_NAME") != None:
+    cfg["PRODUCT_NAME"] = "gizmo1"
+  else:
+    cfg["PRODUCT_NAME"] = "gizmo2"
+`,
+	},
+	{
+		desc:   "else if",
+		mkname: "product.mk",
+		in: `
+ifdef  PRODUCT_NAME
+  PRODUCT_NAME = gizmo
+else ifndef PRODUCT_PACKAGES   # Comment
+endif
+	`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  if g.get("PRODUCT_NAME") != None:
+    cfg["PRODUCT_NAME"] = "gizmo"
+  elif not g.get("PRODUCT_PACKAGES") != None:
+    # Comment
+    pass
+`,
+	},
+	{
+		desc:   "ifeq / ifneq",
+		mkname: "product.mk",
+		in: `
+ifeq (aosp_arm, $(TARGET_PRODUCT))
+  PRODUCT_MODEL = pix2
+else
+  PRODUCT_MODEL = pix21
+endif
+ifneq (aosp_x86, $(TARGET_PRODUCT))
+  PRODUCT_MODEL = pix3
+endif
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  if "aosp_arm" == g["TARGET_PRODUCT"]:
+    cfg["PRODUCT_MODEL"] = "pix2"
+  else:
+    cfg["PRODUCT_MODEL"] = "pix21"
+  if "aosp_x86" != g["TARGET_PRODUCT"]:
+    cfg["PRODUCT_MODEL"] = "pix3"
+`,
+	},
+	{
+		desc:   "Check filter result",
+		mkname: "product.mk",
+		in: `
+ifeq (,$(filter userdebug eng, $(TARGET_BUILD_VARIANT)))
+endif
+ifneq (,$(filter userdebug,$(TARGET_BUILD_VARIANT))
+endif
+ifneq (,$(filter plaf,$(PLATFORM_LIST)))
+endif
+ifeq ($(TARGET_BUILD_VARIANT), $(filter $(TARGET_BUILD_VARIANT), userdebug eng))
+endif
+ifneq (,$(filter true, $(v1)$(v2)))
+endif
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  if g["TARGET_BUILD_VARIANT"] not in ["userdebug", "eng"]:
+    pass
+  if g["TARGET_BUILD_VARIANT"] == "userdebug":
+    pass
+  if "plaf" in g.get("PLATFORM_LIST", []):
+    pass
+  if g["TARGET_BUILD_VARIANT"] in ["userdebug", "eng"]:
+    pass
+  if "%s%s" % (_v1, _v2) == "true":
+    pass
+`,
+	},
+	{
+		desc:   "Get filter result",
+		mkname: "product.mk",
+		in: `
+PRODUCT_LIST2=$(filter-out %/foo.ko,$(wildcard path/*.ko))
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  cfg["PRODUCT_LIST2"] = rblf.filter_out("%/foo.ko", rblf.expand_wildcard("path/*.ko"))
+`,
+	},
+	{
+		desc:   "filter $(VAR), values",
+		mkname: "product.mk",
+		in: `
+ifeq (,$(filter $(TARGET_PRODUCT), yukawa_gms yukawa_sei510_gms)
+  ifneq (,$(filter $(TARGET_PRODUCT), yukawa_gms)
+  endif
+endif
+
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  if g["TARGET_PRODUCT"] not in ["yukawa_gms", "yukawa_sei510_gms"]:
+    if g["TARGET_PRODUCT"] == "yukawa_gms":
+      pass
+`,
+	},
+	{
+		desc:   "filter $(V1), $(V2)",
+		mkname: "product.mk",
+		in: `
+ifneq (, $(filter $(PRODUCT_LIST), $(TARGET_PRODUCT)))
+endif
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  if rblf.filter(g.get("PRODUCT_LIST", ""), g["TARGET_PRODUCT"]):
+    pass
+`,
+	},
+	{
+		desc:   "ifeq",
+		mkname: "product.mk",
+		in: `
+ifeq (aosp, $(TARGET_PRODUCT)) # Comment
+else ifneq (, $(TARGET_PRODUCT))
+endif
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  if "aosp" == g["TARGET_PRODUCT"]:
+    # Comment
+    pass
+  elif g["TARGET_PRODUCT"]:
+    pass
+`,
+	},
+	{
+		desc:   "Nested if",
+		mkname: "product.mk",
+		in: `
+ifdef PRODUCT_NAME
+  PRODUCT_PACKAGES = pack-if0
+  ifdef PRODUCT_MODEL
+    PRODUCT_PACKAGES = pack-if-if
+  else ifdef PRODUCT_NAME
+    PRODUCT_PACKAGES = pack-if-elif
+  else
+    PRODUCT_PACKAGES = pack-if-else
+  endif
+  PRODUCT_PACKAGES = pack-if
+else ifneq (,$(TARGET_PRODUCT))
+  PRODUCT_PACKAGES = pack-elif
+else
+  PRODUCT_PACKAGES = pack-else
+endif
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  if g.get("PRODUCT_NAME") != None:
+    cfg["PRODUCT_PACKAGES"] = ["pack-if0"]
+    if g.get("PRODUCT_MODEL") != None:
+      cfg["PRODUCT_PACKAGES"] = ["pack-if-if"]
+    elif g.get("PRODUCT_NAME") != None:
+      cfg["PRODUCT_PACKAGES"] = ["pack-if-elif"]
+    else:
+      cfg["PRODUCT_PACKAGES"] = ["pack-if-else"]
+    cfg["PRODUCT_PACKAGES"] = ["pack-if"]
+  elif g["TARGET_PRODUCT"]:
+    cfg["PRODUCT_PACKAGES"] = ["pack-elif"]
+  else:
+    cfg["PRODUCT_PACKAGES"] = ["pack-else"]
+`,
+	},
+	{
+		desc:   "Wildcard",
+		mkname: "product.mk",
+		in: `
+ifeq (,$(wildcard foo.mk))
+endif
+ifneq (,$(wildcard foo*.mk))
+endif
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  if not rblf.file_exists("foo.mk"):
+    pass
+  if rblf.file_wildcard_exists("foo*.mk"):
+    pass
+`,
+	},
+	{
+		desc:   "ifneq $(X),true",
+		mkname: "product.mk",
+		in: `
+ifneq ($(VARIABLE),true)
+endif
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  if g.get("VARIABLE", "") != "true":
+    pass
+`,
+	},
+	{
+		desc:   "Const neq",
+		mkname: "product.mk",
+		in: `
+ifneq (1,0)
+endif
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  if "1" != "0":
+    pass
+`,
+	},
+	{
+		desc:   "is-board calls",
+		mkname: "product.mk",
+		in: `
+ifeq ($(call is-board-platform-in-list,msm8998), true)
+else ifneq ($(call is-board-platform,copper),true)
+else ifneq ($(call is-vendor-board-platform,QCOM),true)
+else ifeq ($(call is-product-in-list, $(PLATFORM_LIST)), true)
+endif
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  if g.get("TARGET_BOARD_PLATFORM", "") in ["msm8998"]:
+    pass
+  elif g.get("TARGET_BOARD_PLATFORM", "") != "copper":
+    pass
+  elif g.get("TARGET_BOARD_PLATFORM", "") not in g["QCOM_BOARD_PLATFORMS"]:
+    pass
+  elif g["TARGET_PRODUCT"] in g.get("PLATFORM_LIST", []):
+    pass
+`,
+	},
+	{
+		desc:   "findstring call",
+		mkname: "product.mk",
+		in: `
+ifneq ($(findstring foo,$(PRODUCT_PACKAGES)),)
+endif
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  if (cfg.get("PRODUCT_PACKAGES", [])).find("foo") != -1:
+    pass
+`,
+	},
+	{
+		desc:   "rhs call",
+		mkname: "product.mk",
+		in: `
+PRODUCT_COPY_FILES = $(call add-to-product-copy-files-if-exists, path:distpath) \
+ $(call find-copy-subdir-files, *, fromdir, todir) $(wildcard foo.*)
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  cfg["PRODUCT_COPY_FILES"] = (rblf.copy_if_exists("path:distpath") +
+      rblf.find_and_copy("*", "fromdir", "todir") +
+      rblf.expand_wildcard("foo.*"))
+`,
+	},
+	{
+		desc:   "inferred type",
+		mkname: "product.mk",
+		in: `
+HIKEY_MODS := $(wildcard foo/*.ko)
+BOARD_VENDOR_KERNEL_MODULES += $(HIKEY_MODS)
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  g["HIKEY_MODS"] = rblf.expand_wildcard("foo/*.ko")
+  g.setdefault("BOARD_VENDOR_KERNEL_MODULES", [])
+  g["BOARD_VENDOR_KERNEL_MODULES"] += g["HIKEY_MODS"]
+`,
+	},
+	{
+		desc:   "list with vars",
+		mkname: "product.mk",
+		in: `
+PRODUCT_COPY_FILES += path1:$(TARGET_PRODUCT)/path1 $(PRODUCT_MODEL)/path2:$(TARGET_PRODUCT)/path2
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  rblf.setdefault(handle, "PRODUCT_COPY_FILES")
+  cfg["PRODUCT_COPY_FILES"] += (("path1:%s/path1" % g["TARGET_PRODUCT"]).split() +
+      ("%s/path2:%s/path2" % (cfg.get("PRODUCT_MODEL", ""), g["TARGET_PRODUCT"])).split())
+`,
+	},
+	{
+		desc:   "misc calls",
+		mkname: "product.mk",
+		in: `
+$(call enforce-product-packages-exist,)
+$(call enforce-product-packages-exist, foo)
+$(call require-artifacts-in-path, foo, bar)
+$(call require-artifacts-in-path-relaxed, foo, bar)
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  rblf.enforce_product_packages_exist("")
+  rblf.enforce_product_packages_exist("foo")
+  rblf.require_artifacts_in_path("foo", "bar")
+  rblf.require_artifacts_in_path_relaxed("foo", "bar")
+`,
+	},
+	{
+		desc:   "list with functions",
+		mkname: "product.mk",
+		in: `
+PRODUCT_COPY_FILES := $(call find-copy-subdir-files,*.kl,from1,to1) \
+ $(call find-copy-subdir-files,*.kc,from2,to2) \
+ foo bar
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  cfg["PRODUCT_COPY_FILES"] = (rblf.find_and_copy("*.kl", "from1", "to1") +
+      rblf.find_and_copy("*.kc", "from2", "to2") +
+      [
+          "foo",
+          "bar",
+      ])
+`,
+	},
+	{
+		desc:   "Text functions",
+		mkname: "product.mk",
+		in: `
+PRODUCT_COPY_FILES := $(addprefix pfx-,a b c)
+PRODUCT_COPY_FILES := $(addsuffix .sff, a b c)
+PRODUCT_NAME := $(word 1, $(subst ., ,$(TARGET_BOARD_PLATFORM)))
+$(info $(patsubst %.pub,%,$(PRODUCT_ADB_KEYS)))
+$(info $(dir foo/bar))
+$(info $(firstword $(PRODUCT_COPY_FILES)))
+$(info $(lastword $(PRODUCT_COPY_FILES)))
+$(info $(dir $(lastword $(MAKEFILE_LIST))))
+$(info $(dir $(lastword $(PRODUCT_COPY_FILES))))
+$(info $(dir $(lastword $(foobar))))
+$(info $(abspath foo/bar))
+$(info $(notdir foo/bar))
+$(call add_soong_config_namespace,snsconfig)
+$(call add_soong_config_var_value,snsconfig,imagetype,odm_image)
+PRODUCT_COPY_FILES := $(call copy-files,$(wildcard foo*.mk),etc)
+PRODUCT_COPY_FILES := $(call product-copy-files-by-pattern,from/%,to/%,a b c)
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  cfg["PRODUCT_COPY_FILES"] = rblf.addprefix("pfx-", "a b c")
+  cfg["PRODUCT_COPY_FILES"] = rblf.addsuffix(".sff", "a b c")
+  cfg["PRODUCT_NAME"] = ((g.get("TARGET_BOARD_PLATFORM", "")).replace(".", " ")).split()[0]
+  rblf.mkinfo("product.mk", rblf.mkpatsubst("%.pub", "%", g.get("PRODUCT_ADB_KEYS", "")))
+  rblf.mkinfo("product.mk", rblf.dir("foo/bar"))
+  rblf.mkinfo("product.mk", cfg["PRODUCT_COPY_FILES"][0])
+  rblf.mkinfo("product.mk", cfg["PRODUCT_COPY_FILES"][-1])
+  rblf.mkinfo("product.mk", rblf.dir("product.mk"))
+  rblf.mkinfo("product.mk", rblf.dir(cfg["PRODUCT_COPY_FILES"][-1]))
+  rblf.mkinfo("product.mk", rblf.dir((_foobar).split()[-1]))
+  rblf.mkinfo("product.mk", rblf.abspath("foo/bar"))
+  rblf.mkinfo("product.mk", rblf.notdir("foo/bar"))
+  rblf.add_soong_config_namespace(g, "snsconfig")
+  rblf.add_soong_config_var_value(g, "snsconfig", "imagetype", "odm_image")
+  cfg["PRODUCT_COPY_FILES"] = rblf.copy_files(rblf.expand_wildcard("foo*.mk"), "etc")
+  cfg["PRODUCT_COPY_FILES"] = rblf.product_copy_files_by_pattern("from/%", "to/%", "a b c")
+`,
+	},
+	{
+		desc:   "subst in list",
+		mkname: "product.mk",
+		in: `
+files = $(call find-copy-subdir-files,*,from,to)
+PRODUCT_COPY_FILES += $(subst foo,bar,$(files))
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  _files = rblf.find_and_copy("*", "from", "to")
+  rblf.setdefault(handle, "PRODUCT_COPY_FILES")
+  cfg["PRODUCT_COPY_FILES"] += rblf.mksubst("foo", "bar", _files)
+`,
+	},
+	{
+		desc:   "assignment flavors",
+		mkname: "product.mk",
+		in: `
+PRODUCT_LIST1 := a
+PRODUCT_LIST2 += a
+PRODUCT_LIST1 += b
+PRODUCT_LIST2 += b
+PRODUCT_LIST3 ?= a
+PRODUCT_LIST1 = c
+PLATFORM_LIST += x
+PRODUCT_PACKAGES := $(PLATFORM_LIST)
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  cfg["PRODUCT_LIST1"] = ["a"]
+  rblf.setdefault(handle, "PRODUCT_LIST2")
+  cfg["PRODUCT_LIST2"] += ["a"]
+  cfg["PRODUCT_LIST1"] += ["b"]
+  cfg["PRODUCT_LIST2"] += ["b"]
+  if cfg.get("PRODUCT_LIST3") == None:
+    cfg["PRODUCT_LIST3"] = ["a"]
+  cfg["PRODUCT_LIST1"] = ["c"]
+  g.setdefault("PLATFORM_LIST", [])
+  g["PLATFORM_LIST"] += ["x"]
+  cfg["PRODUCT_PACKAGES"] = g["PLATFORM_LIST"][:]
+`,
+	},
+	{
+		desc:   "assigment flavors2",
+		mkname: "product.mk",
+		in: `
+PRODUCT_LIST1 = a
+ifeq (0,1)
+  PRODUCT_LIST1 += b
+  PRODUCT_LIST2 += b
+endif
+PRODUCT_LIST1 += c
+PRODUCT_LIST2 += c
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  cfg["PRODUCT_LIST1"] = ["a"]
+  if "0" == "1":
+    cfg["PRODUCT_LIST1"] += ["b"]
+    rblf.setdefault(handle, "PRODUCT_LIST2")
+    cfg["PRODUCT_LIST2"] += ["b"]
+  cfg["PRODUCT_LIST1"] += ["c"]
+  rblf.setdefault(handle, "PRODUCT_LIST2")
+  cfg["PRODUCT_LIST2"] += ["c"]
+`,
+	},
+	{
+		desc:   "soong namespace assignments",
+		mkname: "product.mk",
+		in: `
+SOONG_CONFIG_NAMESPACES += cvd
+SOONG_CONFIG_cvd += launch_configs
+SOONG_CONFIG_cvd_launch_configs += cvd_config_auto.json
+SOONG_CONFIG_cvd += grub_config
+SOONG_CONFIG_cvd_grub_config += grub.cfg
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  rblf.add_soong_config_namespace(g, "cvd")
+  rblf.add_soong_config_var_value(g, "cvd", "launch_configs", "cvd_config_auto.json")
+  rblf.add_soong_config_var_value(g, "cvd", "grub_config", "grub.cfg")
+`,
+	},
+	{
+		desc:   "string split",
+		mkname: "product.mk",
+		in: `
+PRODUCT_LIST1 = a
+local = b
+local += c
+FOO = d
+FOO += e
+PRODUCT_LIST1 += $(local)
+PRODUCT_LIST1 += $(FOO)
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  cfg["PRODUCT_LIST1"] = ["a"]
+  _local = "b"
+  _local += " " + "c"
+  g["FOO"] = "d"
+  g["FOO"] += " " + "e"
+  cfg["PRODUCT_LIST1"] += (_local).split()
+  cfg["PRODUCT_LIST1"] += (g["FOO"]).split()
+`,
+	},
+	{
+		desc:   "apex_jars",
+		mkname: "product.mk",
+		in: `
+PRODUCT_BOOT_JARS := $(ART_APEX_JARS) framework-minus-apex
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  cfg["PRODUCT_BOOT_JARS"] = (g.get("ART_APEX_JARS", []) +
+      ["framework-minus-apex"])
+`,
+	},
+	{
+		desc:   "strip function",
+		mkname: "product.mk",
+		in: `
+ifeq ($(filter hwaddress,$(PRODUCT_PACKAGES)),)
+   PRODUCT_PACKAGES := $(strip $(PRODUCT_PACKAGES) hwaddress)
+endif
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  if "hwaddress" not in cfg.get("PRODUCT_PACKAGES", []):
+    cfg["PRODUCT_PACKAGES"] = (rblf.mkstrip("%s hwaddress" % " ".join(cfg.get("PRODUCT_PACKAGES", [])))).split()
+`,
+	},
+	{
+		desc:   "strip func in condition",
+		mkname: "product.mk",
+		in: `
+ifneq ($(strip $(TARGET_VENDOR)),)
+endif
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  if rblf.mkstrip(g.get("TARGET_VENDOR", "")):
+    pass
+`,
+	},
+	{
+		desc:   "ref after set",
+		mkname: "product.mk",
+		in: `
+PRODUCT_ADB_KEYS:=value
+FOO := $(PRODUCT_ADB_KEYS)
+ifneq (,$(PRODUCT_ADB_KEYS))
+endif
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  g["PRODUCT_ADB_KEYS"] = "value"
+  g["FOO"] = g["PRODUCT_ADB_KEYS"]
+  if g["PRODUCT_ADB_KEYS"]:
+    pass
+`,
+	},
+	{
+		desc:   "ref before set",
+		mkname: "product.mk",
+		in: `
+V1 := $(PRODUCT_ADB_KEYS)
+ifeq (,$(PRODUCT_ADB_KEYS))
+  V2 := $(PRODUCT_ADB_KEYS)
+  PRODUCT_ADB_KEYS:=foo
+  V3 := $(PRODUCT_ADB_KEYS)
+endif`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  g["V1"] = g.get("PRODUCT_ADB_KEYS", "")
+  if not g.get("PRODUCT_ADB_KEYS", ""):
+    g["V2"] = g.get("PRODUCT_ADB_KEYS", "")
+    g["PRODUCT_ADB_KEYS"] = "foo"
+    g["V3"] = g["PRODUCT_ADB_KEYS"]
+`,
+	},
+	{
+		desc:   "Dynamic inherit path",
+		mkname: "product.mk",
+		in: `
+MY_PATH=foo
+$(call inherit-product,vendor/$(MY_PATH)/cfg.mk)
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+load("//vendor/foo1:cfg.star|init", _cfg_init = "init")
+load("//vendor/bar/baz:cfg.star|init", _cfg1_init = "init")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  g["MY_PATH"] = "foo"
+  _entry = {
+    "vendor/foo1/cfg.mk": ("_cfg", _cfg_init),
+    "vendor/bar/baz/cfg.mk": ("_cfg1", _cfg1_init),
+  }.get("vendor/%s/cfg.mk" % g["MY_PATH"])
+  (_varmod, _varmod_init) = _entry if _entry else (None, None)
+  if not _varmod_init:
+    rblf.mkerror("cannot")
+  rblf.inherit(handle, _varmod, _varmod_init)
+`,
+	},
+}
+
+var known_variables = []struct {
+	name  string
+	class varClass
+	starlarkType
+}{
+	{"PRODUCT_NAME", VarClassConfig, starlarkTypeString},
+	{"PRODUCT_MODEL", VarClassConfig, starlarkTypeString},
+	{"PRODUCT_PACKAGES", VarClassConfig, starlarkTypeList},
+	{"PRODUCT_BOOT_JARS", VarClassConfig, starlarkTypeList},
+	{"PRODUCT_COPY_FILES", VarClassConfig, starlarkTypeList},
+	{"PRODUCT_IS_64BIT", VarClassConfig, starlarkTypeString},
+	{"PRODUCT_LIST1", VarClassConfig, starlarkTypeList},
+	{"PRODUCT_LIST2", VarClassConfig, starlarkTypeList},
+	{"PRODUCT_LIST3", VarClassConfig, starlarkTypeList},
+	{"TARGET_PRODUCT", VarClassSoong, starlarkTypeString},
+	{"TARGET_BUILD_VARIANT", VarClassSoong, starlarkTypeString},
+	{"TARGET_BOARD_PLATFORM", VarClassSoong, starlarkTypeString},
+	{"QCOM_BOARD_PLATFORMS", VarClassSoong, starlarkTypeString},
+	{"PLATFORM_LIST", VarClassSoong, starlarkTypeList}, // TODO(asmundak): make it local instead of soong
+}
+
+type testMakefileFinder struct {
+	fs    fs.FS
+	root  string
+	files []string
+}
+
+func (t *testMakefileFinder) Find(root string) []string {
+	if t.files != nil || root == t.root {
+		return t.files
+	}
+	t.files = make([]string, 0)
+	fs.WalkDir(t.fs, root, func(path string, d fs.DirEntry, err error) error {
+		if err != nil {
+			return err
+		}
+		if d.IsDir() {
+			base := filepath.Base(path)
+			if base[0] == '.' && len(base) > 1 {
+				return fs.SkipDir
+			}
+			return nil
+		}
+		if strings.HasSuffix(path, ".mk") {
+			t.files = append(t.files, path)
+		}
+		return nil
+	})
+	return t.files
+}
+
+func TestGood(t *testing.T) {
+	for _, v := range known_variables {
+		KnownVariables.NewVariable(v.name, v.class, v.starlarkType)
+	}
+	fs := NewFindMockFS([]string{
+		"vendor/foo1/cfg.mk",
+		"vendor/bar/baz/cfg.mk",
+		"part.mk",
+		"foo/font.mk",
+		"bar/font.mk",
+	})
+	for _, test := range testCases {
+		t.Run(test.desc,
+			func(t *testing.T) {
+				ss, err := Convert(Request{
+					MkFile:             test.mkname,
+					Reader:             bytes.NewBufferString(test.in),
+					RootDir:            ".",
+					OutputSuffix:       ".star",
+					WarnPartialSuccess: true,
+					SourceFS:           fs,
+					MakefileFinder:     &testMakefileFinder{fs: fs},
+				})
+				if err != nil {
+					t.Error(err)
+					return
+				}
+				got := ss.String()
+				if got != test.expected {
+					t.Errorf("%q failed\nExpected:\n%s\nActual:\n%s\n", test.desc,
+						strings.ReplaceAll(test.expected, "\n", "␤\n"),
+						strings.ReplaceAll(got, "\n", "␤\n"))
+				}
+			})
+	}
+}
diff --git a/mk2rbc/node.go b/mk2rbc/node.go
new file mode 100644
index 0000000..8efe207
--- /dev/null
+++ b/mk2rbc/node.go
@@ -0,0 +1,306 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package mk2rbc
+
+import (
+	"fmt"
+	"strings"
+
+	mkparser "android/soong/androidmk/parser"
+)
+
+// A parsed node for which starlark code will be generated
+// by calling emit().
+type starlarkNode interface {
+	emit(ctx *generationContext)
+}
+
+// Types used to keep processed makefile data:
+type commentNode struct {
+	text string
+}
+
+func (c *commentNode) emit(gctx *generationContext) {
+	chunks := strings.Split(c.text, "\\\n")
+	gctx.newLine()
+	gctx.write(chunks[0]) // It has '#' at the beginning already.
+	for _, chunk := range chunks[1:] {
+		gctx.newLine()
+		gctx.write("#", chunk)
+	}
+}
+
+type moduleInfo struct {
+	path            string // Converted Starlark file path
+	originalPath    string // Makefile file path
+	moduleLocalName string
+	optional        bool
+}
+
+func (im moduleInfo) entryName() string {
+	return im.moduleLocalName + "_init"
+}
+
+type inheritedModule interface {
+	name() string
+	entryName() string
+	emitSelect(gctx *generationContext)
+	isLoadAlways() bool
+}
+
+type inheritedStaticModule struct {
+	*moduleInfo
+	loadAlways bool
+}
+
+func (im inheritedStaticModule) name() string {
+	return fmt.Sprintf("%q", MakePath2ModuleName(im.originalPath))
+}
+
+func (im inheritedStaticModule) emitSelect(_ *generationContext) {
+}
+
+func (im inheritedStaticModule) isLoadAlways() bool {
+	return im.loadAlways
+}
+
+type inheritedDynamicModule struct {
+	path             interpolateExpr
+	candidateModules []*moduleInfo
+	loadAlways       bool
+}
+
+func (i inheritedDynamicModule) name() string {
+	return "_varmod"
+}
+
+func (i inheritedDynamicModule) entryName() string {
+	return i.name() + "_init"
+}
+
+func (i inheritedDynamicModule) emitSelect(gctx *generationContext) {
+	gctx.newLine()
+	gctx.writef("_entry = {")
+	gctx.indentLevel++
+	for _, mi := range i.candidateModules {
+		gctx.newLine()
+		gctx.writef(`"%s": (%q, %s),`, mi.originalPath, mi.moduleLocalName, mi.entryName())
+	}
+	gctx.indentLevel--
+	gctx.newLine()
+	gctx.write("}.get(")
+	i.path.emit(gctx)
+	gctx.write(")")
+	gctx.newLine()
+	gctx.writef("(%s, %s) = _entry if _entry else (None, None)", i.name(), i.entryName())
+	if i.loadAlways {
+		gctx.newLine()
+		gctx.writef("if not %s:", i.entryName())
+		gctx.indentLevel++
+		gctx.newLine()
+		gctx.write(`rblf.mkerror("cannot")`)
+		gctx.indentLevel--
+	}
+}
+
+func (i inheritedDynamicModule) isLoadAlways() bool {
+	return i.loadAlways
+}
+
+type inheritNode struct {
+	module inheritedModule
+}
+
+func (inn *inheritNode) emit(gctx *generationContext) {
+	// Unconditional case:
+	//    rblf.inherit(handle, <module>, module_init)
+	// Conditional case:
+	//    if <module>_init != None:
+	//      same as above
+	inn.module.emitSelect(gctx)
+
+	name := inn.module.name()
+	entry := inn.module.entryName()
+	gctx.newLine()
+	if inn.module.isLoadAlways() {
+		gctx.writef("%s(handle, %s, %s)", cfnInherit, name, entry)
+		return
+	}
+
+	gctx.writef("if %s:", entry)
+	gctx.indentLevel++
+	gctx.newLine()
+	gctx.writef("%s(handle, %s, %s)", cfnInherit, name, entry)
+	gctx.indentLevel--
+}
+
+type includeNode struct {
+	module inheritedModule
+}
+
+func (inn *includeNode) emit(gctx *generationContext) {
+	inn.module.emitSelect(gctx)
+	entry := inn.module.entryName()
+	gctx.newLine()
+	if inn.module.isLoadAlways() {
+		gctx.writef("%s(g, handle)", entry)
+		return
+	}
+
+	gctx.writef("if %s != None:", entry)
+	gctx.indentLevel++
+	gctx.newLine()
+	gctx.writef("%s(g, handle)", entry)
+	gctx.indentLevel--
+}
+
+type assignmentFlavor int
+
+const (
+	// Assignment flavors
+	asgnSet         assignmentFlavor = iota // := or =
+	asgnMaybeSet    assignmentFlavor = iota // ?= and variable may be unset
+	asgnAppend      assignmentFlavor = iota // += and variable has been set before
+	asgnMaybeAppend assignmentFlavor = iota // += and variable may be unset
+)
+
+type assignmentNode struct {
+	lhs      variable
+	value    starlarkExpr
+	mkValue  *mkparser.MakeString
+	flavor   assignmentFlavor
+	isTraced bool
+	previous *assignmentNode
+}
+
+func (asgn *assignmentNode) emit(gctx *generationContext) {
+	gctx.newLine()
+	gctx.inAssignment = true
+	asgn.lhs.emitSet(gctx, asgn)
+	gctx.inAssignment = false
+
+	if asgn.isTraced {
+		gctx.newLine()
+		gctx.tracedCount++
+		gctx.writef(`print("%s.%d: %s := ", `, gctx.starScript.mkFile, gctx.tracedCount, asgn.lhs.name())
+		asgn.lhs.emitGet(gctx, true)
+		gctx.writef(")")
+	}
+}
+
+type exprNode struct {
+	expr starlarkExpr
+}
+
+func (exn *exprNode) emit(gctx *generationContext) {
+	gctx.newLine()
+	exn.expr.emit(gctx)
+}
+
+type ifNode struct {
+	isElif bool // true if this is 'elif' statement
+	expr   starlarkExpr
+}
+
+func (in *ifNode) emit(gctx *generationContext) {
+	ifElif := "if "
+	if in.isElif {
+		ifElif = "elif "
+	}
+
+	gctx.newLine()
+	if bad, ok := in.expr.(*badExpr); ok {
+		gctx.write("# MK2STAR ERROR converting:")
+		gctx.newLine()
+		gctx.writef("#   %s", bad.node.Dump())
+		gctx.newLine()
+		gctx.writef("# %s", bad.message)
+		gctx.newLine()
+		// The init function emits a warning if the conversion was not
+		// fullly successful, so here we (arbitrarily) take the false path.
+		gctx.writef("%sFalse:", ifElif)
+		return
+	}
+	gctx.write(ifElif)
+	in.expr.emit(gctx)
+	gctx.write(":")
+}
+
+type elseNode struct{}
+
+func (br *elseNode) emit(gctx *generationContext) {
+	gctx.newLine()
+	gctx.write("else:")
+}
+
+// switchCase represents as single if/elseif/else branch. All the necessary
+// info about flavor (if/elseif/else) is supposed to be kept in `gate`.
+type switchCase struct {
+	gate  starlarkNode
+	nodes []starlarkNode
+}
+
+func (cb *switchCase) newNode(node starlarkNode) {
+	cb.nodes = append(cb.nodes, node)
+}
+
+func (cb *switchCase) emit(gctx *generationContext) {
+	cb.gate.emit(gctx)
+	gctx.indentLevel++
+	hasStatements := false
+	emitNode := func(node starlarkNode) {
+		if _, ok := node.(*commentNode); !ok {
+			hasStatements = true
+		}
+		node.emit(gctx)
+	}
+	if len(cb.nodes) > 0 {
+		emitNode(cb.nodes[0])
+		for _, node := range cb.nodes[1:] {
+			emitNode(node)
+		}
+		if !hasStatements {
+			gctx.emitPass()
+		}
+	} else {
+		gctx.emitPass()
+	}
+	gctx.indentLevel--
+}
+
+// A single complete if ... elseif ... else ... endif sequences
+type switchNode struct {
+	ssCases []*switchCase
+}
+
+func (ssw *switchNode) newNode(node starlarkNode) {
+	switch br := node.(type) {
+	case *switchCase:
+		ssw.ssCases = append(ssw.ssCases, br)
+	default:
+		panic(fmt.Errorf("expected switchCase node, got %t", br))
+	}
+}
+
+func (ssw *switchNode) emit(gctx *generationContext) {
+	if len(ssw.ssCases) == 0 {
+		gctx.emitPass()
+	} else {
+		ssw.ssCases[0].emit(gctx)
+		for _, ssCase := range ssw.ssCases[1:] {
+			ssCase.emit(gctx)
+		}
+	}
+}
diff --git a/mk2rbc/soong_variables.go b/mk2rbc/soong_variables.go
new file mode 100644
index 0000000..de46925
--- /dev/null
+++ b/mk2rbc/soong_variables.go
@@ -0,0 +1,151 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package mk2rbc
+
+import (
+	"bytes"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"regexp"
+	"strings"
+
+	mkparser "android/soong/androidmk/parser"
+)
+
+type context struct {
+	includeFileScope mkparser.Scope
+	registrar        variableRegistrar
+}
+
+// Scans the makefile Soong uses to generate soong.variables file,
+// collecting variable names and types from the lines that look like this:
+//    $(call add_json_XXX,  <...>,             $(VAR))
+//
+func FindSoongVariables(mkFile string, includeFileScope mkparser.Scope, registrar variableRegistrar) error {
+	ctx := context{includeFileScope, registrar}
+	return ctx.doFind(mkFile)
+}
+
+func (ctx *context) doFind(mkFile string) error {
+	mkContents, err := ioutil.ReadFile(mkFile)
+	if err != nil {
+		return err
+	}
+	parser := mkparser.NewParser(mkFile, bytes.NewBuffer(mkContents))
+	nodes, errs := parser.Parse()
+	if len(errs) > 0 {
+		for _, e := range errs {
+			fmt.Fprintln(os.Stderr, "ERROR:", e)
+		}
+		return fmt.Errorf("cannot parse %s", mkFile)
+	}
+	for _, node := range nodes {
+		switch t := node.(type) {
+		case *mkparser.Variable:
+			ctx.handleVariable(t)
+		case *mkparser.Directive:
+			ctx.handleInclude(t)
+		}
+	}
+	return nil
+}
+
+func (ctx context) NewSoongVariable(name, typeString string) {
+	var valueType starlarkType
+	switch typeString {
+	case "bool":
+		valueType = starlarkTypeBool
+	case "csv":
+		// Only PLATFORM_VERSION_ALL_CODENAMES, and it's a list
+		valueType = starlarkTypeList
+	case "list":
+		valueType = starlarkTypeList
+	case "str":
+		valueType = starlarkTypeString
+	case "val":
+		// Only PLATFORM_SDK_VERSION uses this, and it's integer
+		valueType = starlarkTypeInt
+	default:
+		panic(fmt.Errorf("unknown Soong variable type %s", typeString))
+	}
+
+	ctx.registrar.NewVariable(name, VarClassSoong, valueType)
+}
+
+func (ctx context) handleInclude(t *mkparser.Directive) {
+	if t.Name != "include" && t.Name != "-include" {
+		return
+	}
+	includedPath := t.Args.Value(ctx.includeFileScope)
+	err := ctx.doFind(includedPath)
+	if err != nil && t.Name == "include" {
+		fmt.Fprintf(os.Stderr, "cannot include %s: %s", includedPath, err)
+	}
+}
+
+var callFuncRex = regexp.MustCompile("^call +add_json_(str|val|bool|csv|list) *,")
+
+func (ctx context) handleVariable(t *mkparser.Variable) {
+	// From the variable reference looking as follows:
+	//  $(call json_add_TYPE,arg1,$(VAR))
+	// we infer that the type of $(VAR) is TYPE
+	// VAR can be a simple variable name, or another call
+	// (e.g., $(call invert_bool, $(X)), from which we can infer
+	// that the type of X is bool
+	if prefix, v, ok := prefixedVariable(t.Name); ok && strings.HasPrefix(prefix, "call add_json") {
+		if match := callFuncRex.FindStringSubmatch(prefix); match != nil {
+			ctx.inferSoongVariableType(match[1], v)
+			// NOTE(asmundak): sometimes arg1 (the name of the Soong variable defined
+			// in this statement) may indicate that there is a Make counterpart. E.g, from
+			//     $(call add_json_bool, DisablePreopt, $(call invert_bool,$(ENABLE_PREOPT)))
+			// it may be inferred that there is a Make boolean variable DISABLE_PREOPT.
+			// Unfortunately, Soong variable names have no 1:1 correspondence to Make variables,
+			// for instance,
+			//       $(call add_json_list, PatternsOnSystemOther, $(SYSTEM_OTHER_ODEX_FILTER))
+			// does not mean that there is PATTERNS_ON_SYSTEM_OTHER
+			// Our main interest lies in finding the variables whose values are lists, and
+			// so far there are none that can be found this way, so it is not important.
+		} else {
+			panic(fmt.Errorf("cannot match the call: %s", prefix))
+		}
+	}
+}
+
+var (
+	callInvertBoolRex = regexp.MustCompile("^call +invert_bool *, *$")
+	callFilterBoolRex = regexp.MustCompile("^(filter|filter-out) +(true|false), *$")
+)
+
+func (ctx context) inferSoongVariableType(vType string, n *mkparser.MakeString) {
+	if n.Const() {
+		ctx.NewSoongVariable(n.Strings[0], vType)
+		return
+	}
+	if prefix, v, ok := prefixedVariable(n); ok {
+		if callInvertBoolRex.MatchString(prefix) || callFilterBoolRex.MatchString(prefix) {
+			// It is $(call invert_bool, $(VAR)) or $(filter[-out] [false|true],$(VAR))
+			ctx.inferSoongVariableType("bool", v)
+		}
+	}
+}
+
+// If MakeString is foo$(BAR), returns 'foo', BAR(as *MakeString) and true
+func prefixedVariable(s *mkparser.MakeString) (string, *mkparser.MakeString, bool) {
+	if len(s.Strings) != 2 || s.Strings[1] != "" {
+		return "", nil, false
+	}
+	return s.Strings[0], s.Variables[0].Name, true
+}
diff --git a/mk2rbc/soong_variables_test.go b/mk2rbc/soong_variables_test.go
new file mode 100644
index 0000000..c883882
--- /dev/null
+++ b/mk2rbc/soong_variables_test.go
@@ -0,0 +1,51 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package mk2rbc
+
+import (
+	"fmt"
+	"path/filepath"
+	"reflect"
+	"testing"
+)
+
+type dirResolverForTest struct {
+	ScopeBase
+}
+
+func (t dirResolverForTest) Get(name string) string {
+	if name != "BUILD_SYSTEM" {
+		return fmt.Sprintf("$(%s)", name)
+	}
+	return getTestDirectory()
+}
+
+func TestSoongVariables(t *testing.T) {
+	testFile := filepath.Join(getTestDirectory(), "soong_variables.mk.test")
+	var actual testVariables
+	if err := FindSoongVariables(testFile, dirResolverForTest{}, &actual); err != nil {
+		t.Fatal(err)
+	}
+	expected := testVariables{[]testVar{
+		{"BUILD_ID", VarClassSoong, starlarkTypeString},
+		{"PLATFORM_SDK_VERSION", VarClassSoong, starlarkTypeInt},
+		{"DEVICE_PACKAGE_OVERLAYS", VarClassSoong, starlarkTypeList},
+		{"ENABLE_CFI", VarClassSoong, starlarkTypeBool},
+		{"ENABLE_PREOPT", VarClassSoong, starlarkTypeBool},
+	}}
+	if !reflect.DeepEqual(expected, actual) {
+		t.Errorf("\nExpected: %v\n  Actual: %v", expected, actual)
+	}
+}
diff --git a/mk2rbc/test/android_products.mk.test b/mk2rbc/test/android_products.mk.test
new file mode 100644
index 0000000..a2220ed
--- /dev/null
+++ b/mk2rbc/test/android_products.mk.test
@@ -0,0 +1,4 @@
+PRODUCT_MAKEFILES := \
+    $(LOCAL_DIR)/aosp_tv_arm.mk \
+    $(LOCAL_DIR)/aosp_tv_arm64.mk \
+    aosp_cf_x86_tv:$(LOCAL_DIR)/vsoc_x86/tv/device.mk
\ No newline at end of file
diff --git a/mk2rbc/test/config_variables.mk.test b/mk2rbc/test/config_variables.mk.test
new file mode 100644
index 0000000..e5cd0e9
--- /dev/null
+++ b/mk2rbc/test/config_variables.mk.test
@@ -0,0 +1,12 @@
+_product_single_value_vars :=
+
+# Variables that are lists of values.
+_product_list_vars :=
+
+_product_single_value_vars += PRODUCT_NAME
+_product_single_value_vars += PRODUCT_MODEL
+
+# The resoure configuration options to use for this product.
+_product_list_vars += PRODUCT_LOCALES
+_product_list_vars += PRODUCT_AAPT_CONFIG
+_product_single_value_vars += PRODUCT_AAPT_PREF_CONFIG
diff --git a/mk2rbc/test/soong_included.mk.test b/mk2rbc/test/soong_included.mk.test
new file mode 100644
index 0000000..255ecc5
--- /dev/null
+++ b/mk2rbc/test/soong_included.mk.test
@@ -0,0 +1 @@
+  $(call add_json_bool, DisablePreopt,                           $(call invert_bool,$(ENABLE_PREOPT)))
diff --git a/mk2rbc/test/soong_variables.mk.test b/mk2rbc/test/soong_variables.mk.test
new file mode 100644
index 0000000..ca60c9c
--- /dev/null
+++ b/mk2rbc/test/soong_variables.mk.test
@@ -0,0 +1,5 @@
+$(call add_json_str,  BuildId,                           $(BUILD_ID))
+$(call add_json_val,  Platform_sdk_version,              $(PLATFORM_SDK_VERSION))
+$(call add_json_list, DeviceResourceOverlays,            $(DEVICE_PACKAGE_OVERLAYS))
+$(call add_json_bool, EnableCFI,                         $(call invert_bool,$(filter false,$(ENABLE_CFI))))
+include $(BUILD_SYSTEM)/soong_included.mk.test
diff --git a/mk2rbc/types.go b/mk2rbc/types.go
new file mode 100644
index 0000000..ebd52d8
--- /dev/null
+++ b/mk2rbc/types.go
@@ -0,0 +1,75 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package mk2rbc
+
+// Starlark expression types we use
+type starlarkType int
+
+const (
+	// Variable types. Initially we only know the types of the  product
+	// configuration variables that are lists, and the types of some
+	// hardwired variables. The remaining variables are first entered as
+	// having an unknown type and treated as strings, but sometimes we
+	//  can infer variable's type from the value assigned to it.
+	starlarkTypeUnknown starlarkType = iota
+	starlarkTypeList    starlarkType = iota
+	starlarkTypeString  starlarkType = iota
+	starlarkTypeInt     starlarkType = iota
+	starlarkTypeBool    starlarkType = iota
+	starlarkTypeVoid    starlarkType = iota
+)
+
+type hiddenArgType int
+
+const (
+	// Some functions have an implicitly emitted first argument, which may be
+	// a global ('g') or configuration ('cfg') variable.
+	hiddenArgNone   hiddenArgType = iota
+	hiddenArgGlobal hiddenArgType = iota
+	hiddenArgConfig hiddenArgType = iota
+)
+
+type varClass int
+
+const (
+	VarClassConfig varClass = iota
+	VarClassSoong  varClass = iota
+	VarClassLocal  varClass = iota
+)
+
+type variableRegistrar interface {
+	NewVariable(name string, varClass varClass, valueType starlarkType)
+}
+
+// ScopeBase is a dummy implementation of the mkparser.Scope.
+// All our scopes are read-only and resolve only simple variables.
+type ScopeBase struct{}
+
+func (s ScopeBase) Set(_, _ string) {
+	panic("implement me")
+}
+
+func (s ScopeBase) Call(_ string, _ []string) []string {
+	panic("implement me")
+}
+
+func (s ScopeBase) SetFunc(_ string, _ func([]string) []string) {
+	panic("implement me")
+}
+
+// Used to find all makefiles in the source tree
+type MakefileFinder interface {
+	Find(root string) []string
+}
diff --git a/mk2rbc/variable.go b/mk2rbc/variable.go
new file mode 100644
index 0000000..88d63c9
--- /dev/null
+++ b/mk2rbc/variable.go
@@ -0,0 +1,307 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package mk2rbc
+
+import (
+	"fmt"
+	"strings"
+)
+
+type variable interface {
+	name() string
+	emitGet(gctx *generationContext, isDefined bool)
+	emitSet(gctx *generationContext, asgn *assignmentNode)
+	emitDefined(gctx *generationContext)
+	valueType() starlarkType
+	setValueType(t starlarkType)
+	defaultValueString() string
+	isPreset() bool
+}
+
+type baseVariable struct {
+	nam    string
+	typ    starlarkType
+	preset bool // true if it has been initialized at startup
+}
+
+func (v baseVariable) name() string {
+	return v.nam
+}
+
+func (v baseVariable) valueType() starlarkType {
+	return v.typ
+}
+
+func (v *baseVariable) setValueType(t starlarkType) {
+	v.typ = t
+}
+
+func (v baseVariable) isPreset() bool {
+	return v.preset
+}
+
+var defaultValuesByType = map[starlarkType]string{
+	starlarkTypeUnknown: `""`,
+	starlarkTypeList:    "[]",
+	starlarkTypeString:  `""`,
+	starlarkTypeInt:     "0",
+	starlarkTypeBool:    "False",
+	starlarkTypeVoid:    "None",
+}
+
+func (v baseVariable) defaultValueString() string {
+	if v, ok := defaultValuesByType[v.valueType()]; ok {
+		return v
+	}
+	panic(fmt.Errorf("%s has unknown type %q", v.name(), v.valueType()))
+}
+
+type productConfigVariable struct {
+	baseVariable
+}
+
+func (pcv productConfigVariable) emitSet(gctx *generationContext, asgn *assignmentNode) {
+	emitAssignment := func() {
+		pcv.emitGet(gctx, true)
+		gctx.write(" = ")
+		asgn.value.emitListVarCopy(gctx)
+	}
+	emitAppend := func() {
+		pcv.emitGet(gctx, true)
+		gctx.write(" += ")
+		if pcv.valueType() == starlarkTypeString {
+			gctx.writef(`" " + `)
+		}
+		asgn.value.emit(gctx)
+	}
+
+	switch asgn.flavor {
+	case asgnSet:
+		emitAssignment()
+	case asgnAppend:
+		emitAppend()
+	case asgnMaybeAppend:
+		// If we are not sure variable has been assigned before, emit setdefault
+		if pcv.typ == starlarkTypeList {
+			gctx.writef("%s(handle, %q)", cfnSetListDefault, pcv.name())
+		} else {
+			gctx.writef("cfg.setdefault(%q, %s)", pcv.name(), pcv.defaultValueString())
+		}
+		gctx.newLine()
+		emitAppend()
+	case asgnMaybeSet:
+		gctx.writef("if cfg.get(%q) == None:", pcv.nam)
+		gctx.indentLevel++
+		gctx.newLine()
+		emitAssignment()
+		gctx.indentLevel--
+	}
+}
+
+func (pcv productConfigVariable) emitGet(gctx *generationContext, isDefined bool) {
+	if isDefined || pcv.isPreset() {
+		gctx.writef("cfg[%q]", pcv.nam)
+	} else {
+		gctx.writef("cfg.get(%q, %s)", pcv.nam, pcv.defaultValueString())
+	}
+}
+
+func (pcv productConfigVariable) emitDefined(gctx *generationContext) {
+	gctx.writef("g.get(%q) != None", pcv.name())
+}
+
+type otherGlobalVariable struct {
+	baseVariable
+}
+
+func (scv otherGlobalVariable) emitSet(gctx *generationContext, asgn *assignmentNode) {
+	emitAssignment := func() {
+		scv.emitGet(gctx, true)
+		gctx.write(" = ")
+		asgn.value.emitListVarCopy(gctx)
+	}
+
+	emitAppend := func() {
+		scv.emitGet(gctx, true)
+		gctx.write(" += ")
+		if scv.valueType() == starlarkTypeString {
+			gctx.writef(`" " + `)
+		}
+		asgn.value.emit(gctx)
+	}
+
+	switch asgn.flavor {
+	case asgnSet:
+		emitAssignment()
+	case asgnAppend:
+		emitAppend()
+	case asgnMaybeAppend:
+		// If we are not sure variable has been assigned before, emit setdefault
+		gctx.writef("g.setdefault(%q, %s)", scv.name(), scv.defaultValueString())
+		gctx.newLine()
+		emitAppend()
+	case asgnMaybeSet:
+		gctx.writef("if g.get(%q) == None:", scv.nam)
+		gctx.indentLevel++
+		gctx.newLine()
+		emitAssignment()
+		gctx.indentLevel--
+	}
+}
+
+func (scv otherGlobalVariable) emitGet(gctx *generationContext, isDefined bool) {
+	if isDefined || scv.isPreset() {
+		gctx.writef("g[%q]", scv.nam)
+	} else {
+		gctx.writef("g.get(%q, %s)", scv.nam, scv.defaultValueString())
+	}
+}
+
+func (scv otherGlobalVariable) emitDefined(gctx *generationContext) {
+	gctx.writef("g.get(%q) != None", scv.name())
+}
+
+type localVariable struct {
+	baseVariable
+}
+
+func (lv localVariable) emitDefined(_ *generationContext) {
+	panic("implement me")
+}
+
+func (lv localVariable) String() string {
+	return "_" + lv.nam
+}
+
+func (lv localVariable) emitSet(gctx *generationContext, asgn *assignmentNode) {
+	switch asgn.flavor {
+	case asgnSet:
+		gctx.writef("%s = ", lv)
+		asgn.value.emitListVarCopy(gctx)
+	case asgnAppend:
+		lv.emitGet(gctx, false)
+		gctx.write(" += ")
+		if lv.valueType() == starlarkTypeString {
+			gctx.writef(`" " + `)
+		}
+		asgn.value.emit(gctx)
+	case asgnMaybeAppend:
+		gctx.writef("%s(%q, ", cfnLocalAppend, lv)
+		asgn.value.emit(gctx)
+		gctx.write(")")
+	case asgnMaybeSet:
+		gctx.writef("%s(%q, ", cfnLocalSetDefault, lv)
+		asgn.value.emit(gctx)
+		gctx.write(")")
+	}
+}
+
+func (lv localVariable) emitGet(gctx *generationContext, _ bool) {
+	gctx.writef("%s", lv)
+}
+
+type predefinedVariable struct {
+	baseVariable
+	value starlarkExpr
+}
+
+func (pv predefinedVariable) emitGet(gctx *generationContext, _ bool) {
+	pv.value.emit(gctx)
+}
+
+func (pv predefinedVariable) emitSet(gctx *generationContext, asgn *assignmentNode) {
+	if expectedValue, ok1 := maybeString(pv.value); ok1 {
+		actualValue, ok2 := maybeString(asgn.value)
+		if ok2 {
+			if actualValue == expectedValue {
+				return
+			}
+			gctx.writef("# MK2RBC TRANSLATION ERROR: cannot set predefined variable %s to %q, its value should be %q",
+				pv.name(), actualValue, expectedValue)
+			gctx.newLine()
+			gctx.write("pass")
+			gctx.starScript.hasErrors = true
+			return
+		}
+	}
+	panic(fmt.Errorf("cannot set predefined variable %s to %q", pv.name(), asgn.mkValue.Dump()))
+}
+
+func (pv predefinedVariable) emitDefined(gctx *generationContext) {
+	gctx.write("True")
+}
+
+var localProductConfigVariables = map[string]string{
+	"LOCAL_AUDIO_PRODUCT_PACKAGE":         "PRODUCT_PACKAGES",
+	"LOCAL_AUDIO_PRODUCT_COPY_FILES":      "PRODUCT_COPY_FILES",
+	"LOCAL_AUDIO_DEVICE_PACKAGE_OVERLAYS": "DEVICE_PACKAGE_OVERLAYS",
+	"LOCAL_DUMPSTATE_PRODUCT_PACKAGE":     "PRODUCT_PACKAGES",
+	"LOCAL_GATEKEEPER_PRODUCT_PACKAGE":    "PRODUCT_PACKAGES",
+	"LOCAL_HEALTH_PRODUCT_PACKAGE":        "PRODUCT_PACKAGES",
+	"LOCAL_SENSOR_PRODUCT_PACKAGE":        "PRODUCT_PACKAGES",
+	"LOCAL_KEYMASTER_PRODUCT_PACKAGE":     "PRODUCT_PACKAGES",
+	"LOCAL_KEYMINT_PRODUCT_PACKAGE":       "PRODUCT_PACKAGES",
+}
+
+var presetVariables = map[string]bool{
+	"BUILD_ID":                  true,
+	"HOST_ARCH":                 true,
+	"HOST_OS":                   true,
+	"HOST_BUILD_TYPE":           true,
+	"OUT_DIR":                   true,
+	"PLATFORM_VERSION_CODENAME": true,
+	"PLATFORM_VERSION":          true,
+	"TARGET_ARCH":               true,
+	"TARGET_ARCH_VARIANT":       true,
+	"TARGET_BUILD_TYPE":         true,
+	"TARGET_BUILD_VARIANT":      true,
+	"TARGET_PRODUCT":            true,
+}
+
+// addVariable returns a variable with a given name. A variable is
+// added if it does not exist yet.
+func (ctx *parseContext) addVariable(name string) variable {
+	v, found := ctx.variables[name]
+	if !found {
+		_, preset := presetVariables[name]
+		if vi, found := KnownVariables[name]; found {
+			switch vi.class {
+			case VarClassConfig:
+				v = &productConfigVariable{baseVariable{nam: name, typ: vi.valueType, preset: preset}}
+			case VarClassSoong:
+				v = &otherGlobalVariable{baseVariable{nam: name, typ: vi.valueType, preset: preset}}
+			}
+		} else if name == strings.ToLower(name) {
+			// Heuristics: if variable's name is all lowercase, consider it local
+			// string variable.
+			v = &localVariable{baseVariable{nam: name, typ: starlarkTypeUnknown}}
+		} else {
+			vt := starlarkTypeUnknown
+			if strings.HasPrefix(name, "LOCAL_") {
+				// Heuristics: local variables that contribute to corresponding config variables
+				if cfgVarName, found := localProductConfigVariables[name]; found {
+					vi, found2 := KnownVariables[cfgVarName]
+					if !found2 {
+						panic(fmt.Errorf("unknown config variable %s for %s", cfgVarName, name))
+					}
+					vt = vi.valueType
+				}
+			}
+			v = &otherGlobalVariable{baseVariable{nam: name, typ: vt}}
+		}
+		ctx.variables[name] = v
+	}
+	return v
+}
diff --git a/python/binary.go b/python/binary.go
index e955492..bc2768c 100644
--- a/python/binary.go
+++ b/python/binary.go
@@ -38,27 +38,10 @@
 	Main           string
 	Srcs           bazel.LabelListAttribute
 	Data           bazel.LabelListAttribute
+	Deps           bazel.LabelListAttribute
 	Python_version string
 }
 
-type bazelPythonBinary struct {
-	android.BazelTargetModuleBase
-	bazelPythonBinaryAttributes
-}
-
-func BazelPythonBinaryFactory() android.Module {
-	module := &bazelPythonBinary{}
-	module.AddProperties(&module.bazelPythonBinaryAttributes)
-	android.InitBazelTargetModule(module)
-	return module
-}
-
-func (m *bazelPythonBinary) Name() string {
-	return m.BaseModuleName()
-}
-
-func (m *bazelPythonBinary) GenerateAndroidBuildActions(ctx android.ModuleContext) {}
-
 func PythonBinaryBp2Build(ctx android.TopDownMutatorContext) {
 	m, ok := ctx.Module().(*Module)
 	if !ok || !m.ConvertWithBp2build(ctx) {
@@ -99,11 +82,13 @@
 
 	srcs := android.BazelLabelForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs)
 	data := android.BazelLabelForModuleSrc(ctx, m.properties.Data)
+	deps := android.BazelLabelForModuleDeps(ctx, m.properties.Libs)
 
 	attrs := &bazelPythonBinaryAttributes{
 		Main:           main,
 		Srcs:           bazel.MakeLabelListAttribute(srcs),
 		Data:           bazel.MakeLabelListAttribute(data),
+		Deps:           bazel.MakeLabelListAttribute(deps),
 		Python_version: python_version,
 	}
 
@@ -112,7 +97,7 @@
 		Rule_class: "py_binary",
 	}
 
-	ctx.CreateBazelTargetModule(BazelPythonBinaryFactory, m.Name(), props, attrs)
+	ctx.CreateBazelTargetModule(m.Name(), props, attrs)
 }
 
 type BinaryProperties struct {
diff --git a/python/library.go b/python/library.go
index 9663b3c..a132216 100644
--- a/python/library.go
+++ b/python/library.go
@@ -17,11 +17,17 @@
 // This file contains the module types for building Python library.
 
 import (
+	"fmt"
+
 	"android/soong/android"
+	"android/soong/bazel"
+	"github.com/google/blueprint/proptools"
 )
 
 func init() {
 	registerPythonLibraryComponents(android.InitRegistrationContext)
+	android.RegisterBp2BuildMutator("python_library_host", PythonLibraryHostBp2Build)
+	android.RegisterBp2BuildMutator("python_library", PythonLibraryBp2Build)
 }
 
 func registerPythonLibraryComponents(ctx android.RegistrationContext) {
@@ -32,11 +38,79 @@
 func PythonLibraryHostFactory() android.Module {
 	module := newModule(android.HostSupported, android.MultilibFirst)
 
+	android.InitBazelModule(module)
+
 	return module.init()
 }
 
+type bazelPythonLibraryAttributes struct {
+	Srcs         bazel.LabelListAttribute
+	Data         bazel.LabelListAttribute
+	Deps         bazel.LabelListAttribute
+	Srcs_version string
+}
+
+func PythonLibraryHostBp2Build(ctx android.TopDownMutatorContext) {
+	pythonLibBp2Build(ctx, "python_library_host")
+}
+
+func PythonLibraryBp2Build(ctx android.TopDownMutatorContext) {
+	pythonLibBp2Build(ctx, "python_library")
+}
+
+func pythonLibBp2Build(ctx android.TopDownMutatorContext, modType string) {
+	m, ok := ctx.Module().(*Module)
+	if !ok || !m.ConvertWithBp2build(ctx) {
+		return
+	}
+
+	// a Module can be something other than a `modType`
+	if ctx.ModuleType() != modType {
+		return
+	}
+
+	// TODO(b/182306917): this doesn't fully handle all nested props versioned
+	// by the python version, which would have been handled by the version split
+	// mutator. This is sufficient for very simple python_library modules under
+	// Bionic.
+	py3Enabled := proptools.BoolDefault(m.properties.Version.Py3.Enabled, true)
+	py2Enabled := proptools.BoolDefault(m.properties.Version.Py2.Enabled, false)
+	var python_version string
+	if py2Enabled && !py3Enabled {
+		python_version = "PY2"
+	} else if !py2Enabled && py3Enabled {
+		python_version = "PY3"
+	} else if !py2Enabled && !py3Enabled {
+		panic(fmt.Errorf(
+			"error for '%s' module: bp2build's %s converter doesn't understand having "+
+				"neither py2 nor py3 enabled", m.Name(), modType))
+	} else {
+		// do nothing, since python_version defaults to PY2ANDPY3
+	}
+
+	srcs := android.BazelLabelForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs)
+	data := android.BazelLabelForModuleSrc(ctx, m.properties.Data)
+	deps := android.BazelLabelForModuleDeps(ctx, m.properties.Libs)
+
+	attrs := &bazelPythonLibraryAttributes{
+		Srcs:         bazel.MakeLabelListAttribute(srcs),
+		Data:         bazel.MakeLabelListAttribute(data),
+		Deps:         bazel.MakeLabelListAttribute(deps),
+		Srcs_version: python_version,
+	}
+
+	props := bazel.BazelTargetModuleProperties{
+		// Use the native py_library rule.
+		Rule_class: "py_library",
+	}
+
+	ctx.CreateBazelTargetModule(m.Name(), props, attrs)
+}
+
 func PythonLibraryFactory() android.Module {
 	module := newModule(android.HostAndDeviceSupported, android.MultilibBoth)
 
+	android.InitBazelModule(module)
+
 	return module.init()
 }
diff --git a/python/python.go b/python/python.go
index 0f5b788..83844e6 100644
--- a/python/python.go
+++ b/python/python.go
@@ -675,7 +675,7 @@
 		if !isPythonLibModule(child) {
 			ctx.PropertyErrorf("libs",
 				"the dependency %q of module %q is not Python library!",
-				ctx.ModuleName(), ctx.OtherModuleName(child))
+				ctx.OtherModuleName(child), ctx.ModuleName())
 		}
 		// collect source and data paths, checking that there are no duplicate output file conflicts
 		if dep, ok := child.(pythonDependency); ok {
diff --git a/python/test.go b/python/test.go
index 6713189..7413782 100644
--- a/python/test.go
+++ b/python/test.go
@@ -15,6 +15,8 @@
 package python
 
 import (
+	"github.com/google/blueprint/proptools"
+
 	"android/soong/android"
 	"android/soong/tradefed"
 )
@@ -102,6 +104,9 @@
 	binary.pythonInstaller = NewPythonInstaller("nativetest", "nativetest64")
 
 	test := &testDecorator{binaryDecorator: binary}
+	if hod == android.HostSupportedNoCross && test.testProperties.Test_options.Unit_test == nil {
+		test.testProperties.Test_options.Unit_test = proptools.BoolPtr(true)
+	}
 
 	module.bootstrapper = test
 	module.installer = test
diff --git a/rust/Android.bp b/rust/Android.bp
index b611672..221014e 100644
--- a/rust/Android.bp
+++ b/rust/Android.bp
@@ -11,6 +11,7 @@
         "soong-bloaty",
         "soong-cc",
         "soong-rust-config",
+        "soong-snapshot",
     ],
     srcs: [
         "androidmk.go",
@@ -32,6 +33,7 @@
         "rust.go",
         "sanitize.go",
         "source_provider.go",
+        "snapshot_prebuilt.go",
         "snapshot_utils.go",
         "strip.go",
         "test.go",
@@ -53,6 +55,7 @@
         "rust_test.go",
         "source_provider_test.go",
         "test_test.go",
+        "vendor_snapshot_test.go",
     ],
     pluginFor: ["soong_build"],
 }
diff --git a/rust/androidmk.go b/rust/androidmk.go
index ea45ebd..630805a 100644
--- a/rust/androidmk.go
+++ b/rust/androidmk.go
@@ -205,24 +205,24 @@
 	ctx.SubAndroidMk(entries, fuzz.binaryDecorator)
 
 	var fuzzFiles []string
-	for _, d := range fuzz.corpus {
+	for _, d := range fuzz.fuzzPackagedModule.Corpus {
 		fuzzFiles = append(fuzzFiles,
-			filepath.Dir(fuzz.corpusIntermediateDir.String())+":corpus/"+d.Base())
+			filepath.Dir(fuzz.fuzzPackagedModule.CorpusIntermediateDir.String())+":corpus/"+d.Base())
 	}
 
-	for _, d := range fuzz.data {
+	for _, d := range fuzz.fuzzPackagedModule.Data {
 		fuzzFiles = append(fuzzFiles,
-			filepath.Dir(fuzz.dataIntermediateDir.String())+":data/"+d.Rel())
+			filepath.Dir(fuzz.fuzzPackagedModule.DataIntermediateDir.String())+":data/"+d.Rel())
 	}
 
-	if fuzz.dictionary != nil {
+	if fuzz.fuzzPackagedModule.Dictionary != nil {
 		fuzzFiles = append(fuzzFiles,
-			filepath.Dir(fuzz.dictionary.String())+":"+fuzz.dictionary.Base())
+			filepath.Dir(fuzz.fuzzPackagedModule.Dictionary.String())+":"+fuzz.fuzzPackagedModule.Dictionary.Base())
 	}
 
-	if fuzz.config != nil {
+	if fuzz.fuzzPackagedModule.Config != nil {
 		fuzzFiles = append(fuzzFiles,
-			filepath.Dir(fuzz.config.String())+":config.json")
+			filepath.Dir(fuzz.fuzzPackagedModule.Config.String())+":config.json")
 	}
 
 	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext,
diff --git a/rust/benchmark.go b/rust/benchmark.go
index b89f5cd..0e84243 100644
--- a/rust/benchmark.go
+++ b/rust/benchmark.go
@@ -101,6 +101,7 @@
 func (benchmark *benchmarkDecorator) compilerDeps(ctx DepsContext, deps Deps) Deps {
 	deps = benchmark.binaryDecorator.compilerDeps(ctx, deps)
 
+	deps.Rustlibs = append(deps.Rustlibs, "libtest")
 	deps.Rustlibs = append(deps.Rustlibs, "libcriterion")
 
 	return deps
diff --git a/rust/binary.go b/rust/binary.go
index ffc0413..2c3f548 100644
--- a/rust/binary.go
+++ b/rust/binary.go
@@ -137,6 +137,9 @@
 	// Binaries default to dylib dependencies for device, rlib for host.
 	if binary.preferRlib() {
 		return rlibAutoDep
+	} else if mod, ok := ctx.Module().(*Module); ok && mod.InVendor() {
+		// Vendor Rust binaries should prefer rlibs.
+		return rlibAutoDep
 	} else if ctx.Device() {
 		return dylibAutoDep
 	} else {
@@ -147,10 +150,8 @@
 func (binary *binaryDecorator) stdLinkage(ctx *depsContext) RustLinkage {
 	if binary.preferRlib() {
 		return RlibLinkage
+	} else if ctx.RustModule().InVendor() {
+		return RlibLinkage
 	}
 	return binary.baseCompiler.stdLinkage(ctx)
 }
-
-func (binary *binaryDecorator) isDependencyRoot() bool {
-	return true
-}
diff --git a/rust/binary_test.go b/rust/binary_test.go
index 86f50d3..968c0c1 100644
--- a/rust/binary_test.go
+++ b/rust/binary_test.go
@@ -114,6 +114,23 @@
 	}
 }
 
+// Test that the bootstrap property sets the appropriate linker
+func TestBootstrap(t *testing.T) {
+	ctx := testRust(t, `
+		rust_binary {
+			name: "foo",
+			srcs: ["foo.rs"],
+			bootstrap: true,
+		}`)
+
+	foo := ctx.ModuleForTests("foo", "android_arm64_armv8-a").Rule("rustc")
+
+	flag := "-Wl,-dynamic-linker,/system/bin/bootstrap/linker64"
+	if !strings.Contains(foo.Args["linkFlags"], flag) {
+		t.Errorf("missing link flag to use bootstrap linker, expecting %#v, linkFlags: %#v", flag, foo.Args["linkFlags"])
+	}
+}
+
 func TestStaticBinaryFlags(t *testing.T) {
 	ctx := testRust(t, `
 		rust_binary {
diff --git a/rust/bindgen.go b/rust/bindgen.go
index f9e6cd0..be9e71e 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -29,7 +29,7 @@
 	defaultBindgenFlags = []string{""}
 
 	// bindgen should specify its own Clang revision so updating Clang isn't potentially blocked on bindgen failures.
-	bindgenClangVersion = "clang-r416183b"
+	bindgenClangVersion = "clang-r428724"
 
 	_ = pctx.VariableFunc("bindgenClangVersion", func(ctx android.PackageVarContext) string {
 		if override := ctx.Config().Getenv("LLVM_BINDGEN_PREBUILTS_VERSION"); override != "" {
@@ -137,15 +137,15 @@
 	implicits = append(implicits, deps.depGeneratedHeaders...)
 
 	// Default clang flags
-	cflags = append(cflags, "${cc_config.CommonClangGlobalCflags}")
+	cflags = append(cflags, "${cc_config.CommonGlobalCflags}")
 	if ctx.Device() {
-		cflags = append(cflags, "${cc_config.DeviceClangGlobalCflags}")
+		cflags = append(cflags, "${cc_config.DeviceGlobalCflags}")
 	}
 
 	// Toolchain clang flags
 	cflags = append(cflags, "-target "+ccToolchain.ClangTriple())
-	cflags = append(cflags, strings.ReplaceAll(ccToolchain.ClangCflags(), "${config.", "${cc_config."))
-	cflags = append(cflags, strings.ReplaceAll(ccToolchain.ToolchainClangCflags(), "${config.", "${cc_config."))
+	cflags = append(cflags, strings.ReplaceAll(ccToolchain.Cflags(), "${config.", "${cc_config."))
+	cflags = append(cflags, strings.ReplaceAll(ccToolchain.ToolchainCflags(), "${config.", "${cc_config."))
 
 	// Dependency clang flags and include paths
 	cflags = append(cflags, deps.depClangFlags...)
diff --git a/rust/builder.go b/rust/builder.go
index 6db508d..426a569 100644
--- a/rust/builder.go
+++ b/rust/builder.go
@@ -220,6 +220,15 @@
 	linkFlags = append(linkFlags, flags.GlobalLinkFlags...)
 	linkFlags = append(linkFlags, flags.LinkFlags...)
 
+	// Check if this module needs to use the bootstrap linker
+	if ctx.RustModule().Bootstrap() && !ctx.RustModule().InRecovery() && !ctx.RustModule().InRamdisk() && !ctx.RustModule().InVendorRamdisk() {
+		dynamicLinker := "-Wl,-dynamic-linker,/system/bin/bootstrap/linker"
+		if ctx.toolchain().Is64Bit() {
+			dynamicLinker += "64"
+		}
+		linkFlags = append(linkFlags, dynamicLinker)
+	}
+
 	libFlags := makeLibFlags(deps)
 
 	// Collect dependencies
@@ -260,6 +269,17 @@
 
 	envVars = append(envVars, "ANDROID_RUST_VERSION="+config.RustDefaultVersion)
 
+	if ctx.RustModule().compiler.CargoEnvCompat() {
+		if _, ok := ctx.RustModule().compiler.(*binaryDecorator); ok {
+			envVars = append(envVars, "CARGO_BIN_NAME="+strings.TrimSuffix(outputFile.Base(), outputFile.Ext()))
+		}
+		envVars = append(envVars, "CARGO_CRATE_NAME="+ctx.RustModule().CrateName())
+		pkgVersion := ctx.RustModule().compiler.CargoPkgVersion()
+		if pkgVersion != "" {
+			envVars = append(envVars, "CARGO_PKG_VERSION="+pkgVersion)
+		}
+	}
+
 	if flags.Clippy {
 		clippyFile := android.PathForModuleOut(ctx, outputFile.Base()+".clippy")
 		ctx.Build(pctx, android.BuildParams{
@@ -323,6 +343,12 @@
 	rustdocFlags = append(rustdocFlags, makeLibFlags(deps)...)
 	docTimestampFile := android.PathForModuleOut(ctx, "rustdoc.timestamp")
 
+	// Silence warnings about renamed lints for third-party crates
+	modulePath := android.PathForModuleSrc(ctx).String()
+	if android.IsThirdPartyPath(modulePath) {
+		rustdocFlags = append(rustdocFlags, " -A renamed_and_removed_lints")
+	}
+
 	// Yes, the same out directory is used simultaneously by all rustdoc builds.
 	// This is what cargo does. The docs for individual crates get generated to
 	// a subdirectory named for the crate, and rustdoc synchronizes writes to
diff --git a/rust/compiler.go b/rust/compiler.go
index 1598ebf..7bd9af4 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"path/filepath"
+	"strings"
 
 	"github.com/google/blueprint/proptools"
 
@@ -64,7 +65,11 @@
 )
 
 type BaseCompilerProperties struct {
-	// path to the source file that is the main entry point of the program (e.g. main.rs or lib.rs)
+	// path to the source file that is the main entry point of the program (e.g. main.rs or lib.rs).
+	// Only a single source file can be defined. Modules which generate source can be included by prefixing
+	// the module name with ":", for example ":libfoo_bindgen"
+	//
+	// If no source file is defined, a single generated source module can be defined to be used as the main source.
 	Srcs []string `android:"path,arch_variant"`
 
 	// name of the lint set that should be used to validate this module.
@@ -153,6 +158,14 @@
 	// linkage if all dependencies of the root binary module do not link against libstd\
 	// the same way.
 	Prefer_rlib *bool `android:"arch_variant"`
+
+	// Enables emitting certain Cargo environment variables. Only intended to be used for compatibility purposes.
+	// Will set CARGO_CRATE_NAME to the crate_name property's value.
+	// Will set CARGO_BIN_NAME to the output filename value without the extension.
+	Cargo_env_compat *bool
+
+	// If cargo_env_compat is true, sets the CARGO_PKG_VERSION env var to this value.
+	Cargo_pkg_version *string
 }
 
 type baseCompiler struct {
@@ -235,6 +248,25 @@
 	if err != nil {
 		ctx.PropertyErrorf("lints", err.Error())
 	}
+
+	// linkage-related flags are disallowed.
+	for _, s := range compiler.Properties.Ld_flags {
+		if strings.HasPrefix(s, "-Wl,-l") || strings.HasPrefix(s, "-Wl,-L") {
+			ctx.PropertyErrorf("ld_flags", "'-Wl,-l' and '-Wl,-L' flags cannot be manually specified")
+		}
+	}
+	for _, s := range compiler.Properties.Flags {
+		if strings.HasPrefix(s, "-l") || strings.HasPrefix(s, "-L") {
+			ctx.PropertyErrorf("flags", "'-l' and '-L' flags cannot be manually specified")
+		}
+		if strings.HasPrefix(s, "--extern") {
+			ctx.PropertyErrorf("flags", "'--extern' flag cannot be manually specified")
+		}
+		if strings.HasPrefix(s, "-Clink-args=") || strings.HasPrefix(s, "-C link-args=") {
+			ctx.PropertyErrorf("flags", "'-C link-args' flag cannot be manually specified")
+		}
+	}
+
 	flags.RustFlags = append(flags.RustFlags, lintFlags)
 	flags.RustFlags = append(flags.RustFlags, compiler.Properties.Flags...)
 	flags.RustFlags = append(flags.RustFlags, compiler.cfgsToFlags()...)
@@ -289,8 +321,12 @@
 	return android.OptionalPathForPath(compiler.cargoOutDir)
 }
 
-func (compiler *baseCompiler) isDependencyRoot() bool {
-	return false
+func (compiler *baseCompiler) CargoEnvCompat() bool {
+	return Bool(compiler.Properties.Cargo_env_compat)
+}
+
+func (compiler *baseCompiler) CargoPkgVersion() string {
+	return String(compiler.Properties.Cargo_pkg_version)
 }
 
 func (compiler *baseCompiler) strippedOutputFilePath() android.OptionalPath {
@@ -309,7 +345,7 @@
 	if !Bool(compiler.Properties.No_stdlibs) {
 		for _, stdlib := range config.Stdlibs {
 			// If we're building for the primary arch of the build host, use the compiler's stdlibs
-			if ctx.Target().Os == android.BuildOs {
+			if ctx.Target().Os == ctx.Config().BuildOS {
 				stdlib = stdlib + "_" + ctx.toolchain().RustTriple()
 			}
 			deps.Stdlibs = append(deps.Stdlibs, stdlib)
@@ -330,7 +366,9 @@
 	} else {
 		deps.SharedLibs = append(deps.SharedLibs, bionicLibs...)
 	}
-
+	if ctx.RustModule().StaticExecutable() {
+		deps.StaticLibs = append(deps.StaticLibs, "libunwind")
+	}
 	if libRuntimeBuiltins := config.BuiltinsRuntimeLibrary(ctx.toolchain()); libRuntimeBuiltins != "" {
 		deps.StaticLibs = append(deps.StaticLibs, libRuntimeBuiltins)
 	}
@@ -359,8 +397,15 @@
 	}
 
 	if compiler.location == InstallInData && ctx.RustModule().UseVndk() {
-		dir = filepath.Join(dir, "vendor")
+		if ctx.RustModule().InProduct() {
+			dir = filepath.Join(dir, "product")
+		} else if ctx.RustModule().InVendor() {
+			dir = filepath.Join(dir, "vendor")
+		} else {
+			ctx.ModuleErrorf("Unknown data+VNDK installation kind")
+		}
 	}
+
 	return android.PathForModuleInstall(ctx, dir, compiler.subDir,
 		compiler.relativeInstallPath(), compiler.relative)
 }
@@ -404,12 +449,18 @@
 			srcIndex = i
 		}
 	}
-	if numSrcs != 1 {
+	if numSrcs > 1 {
 		ctx.PropertyErrorf("srcs", incorrectSourcesError)
 	}
+
+	// If a main source file is not provided we expect only a single SourceProvider module to be defined
+	// within srcs, with the expectation that the first source it provides is the entry point.
 	if srcIndex != 0 {
 		ctx.PropertyErrorf("srcs", "main source file must be the first in srcs")
+	} else if numSrcs > 1 {
+		ctx.PropertyErrorf("srcs", "only a single generated source module can be defined without a main source file.")
 	}
+
 	paths := android.PathsForModuleSrc(ctx, srcs)
 	return paths[srcIndex], paths[1:]
 }
diff --git a/rust/compiler_test.go b/rust/compiler_test.go
index 5ca9e7f..f589b69 100644
--- a/rust/compiler_test.go
+++ b/rust/compiler_test.go
@@ -98,6 +98,30 @@
 		}`)
 }
 
+// Test environment vars for Cargo compat are set.
+func TestCargoCompat(t *testing.T) {
+	ctx := testRust(t, `
+		rust_binary {
+			name: "fizz",
+			srcs: ["foo.rs"],
+			crate_name: "foo",
+			cargo_env_compat: true,
+			cargo_pkg_version: "1.0.0"
+		}`)
+
+	fizz := ctx.ModuleForTests("fizz", "android_arm64_armv8-a").Rule("rustc")
+
+	if !strings.Contains(fizz.Args["envVars"], "CARGO_BIN_NAME=fizz") {
+		t.Fatalf("expected 'CARGO_BIN_NAME=fizz' in envVars, actual envVars: %#v", fizz.Args["envVars"])
+	}
+	if !strings.Contains(fizz.Args["envVars"], "CARGO_CRATE_NAME=foo") {
+		t.Fatalf("expected 'CARGO_CRATE_NAME=foo' in envVars, actual envVars: %#v", fizz.Args["envVars"])
+	}
+	if !strings.Contains(fizz.Args["envVars"], "CARGO_PKG_VERSION=1.0.0") {
+		t.Fatalf("expected 'CARGO_PKG_VERSION=1.0.0' in envVars, actual envVars: %#v", fizz.Args["envVars"])
+	}
+}
+
 func TestInstallDir(t *testing.T) {
 	ctx := testRust(t, `
 		rust_library_dylib {
@@ -208,3 +232,73 @@
 		t.Errorf("libstd is not linked dynamically for dylibs")
 	}
 }
+
+// Ensure that manual link flags are disallowed.
+func TestManualLinkageRejection(t *testing.T) {
+	// rustc flags
+	testRustError(t, ".* cannot be manually specified", `
+		rust_binary {
+			name: "foo",
+			srcs: [
+				"foo.rs",
+			],
+			flags: ["-lbar"],
+		}
+	`)
+	testRustError(t, ".* cannot be manually specified", `
+		rust_binary {
+			name: "foo",
+			srcs: [
+				"foo.rs",
+			],
+			flags: ["--extern=foo"],
+		}
+	`)
+	testRustError(t, ".* cannot be manually specified", `
+		rust_binary {
+			name: "foo",
+			srcs: [
+				"foo.rs",
+			],
+			flags: ["-Clink-args=foo"],
+		}
+	`)
+	testRustError(t, ".* cannot be manually specified", `
+		rust_binary {
+			name: "foo",
+			srcs: [
+				"foo.rs",
+			],
+			flags: ["-C link-args=foo"],
+		}
+	`)
+	testRustError(t, ".* cannot be manually specified", `
+		rust_binary {
+			name: "foo",
+			srcs: [
+				"foo.rs",
+			],
+			flags: ["-L foo/"],
+		}
+	`)
+
+	// lld flags
+	testRustError(t, ".* cannot be manually specified", `
+		rust_binary {
+			name: "foo",
+			srcs: [
+				"foo.rs",
+			],
+			ld_flags: ["-Wl,-L bar/"],
+		}
+	`)
+	testRustError(t, ".* cannot be manually specified", `
+		rust_binary {
+			name: "foo",
+			srcs: [
+				"foo.rs",
+			],
+			ld_flags: ["-Wl,-lbar"],
+		}
+	`)
+}
diff --git a/rust/config/allowed_list.go b/rust/config/allowed_list.go
index 394fcc5..63a8f04 100644
--- a/rust/config/allowed_list.go
+++ b/rust/config/allowed_list.go
@@ -12,6 +12,8 @@
 		"external/libchromeos-rs",
 		"external/minijail",
 		"external/rust",
+		"external/selinux/libselinux",
+		"external/uwb",
 		"external/vm_tools/p9",
 		"frameworks/native/libs/binder/rust",
 		"frameworks/proto_logging/stats",
@@ -23,12 +25,24 @@
 		"system/extras/profcollectd",
 		"system/extras/simpleperf",
 		"system/hardware/interfaces/keystore2",
+		"system/librustutils",
+		"system/logging/liblog",
 		"system/logging/rust",
 		"system/security",
 		"system/tools/aidl",
+		"tools/security/fuzzing/example_rust_fuzzer",
+		"vendor/",
+	}
+
+	DownstreamRustAllowedPaths = []string{
+		// Add downstream allowed Rust paths here.
 	}
 
 	RustModuleTypes = []string{
+		// Don't add rust_bindgen or rust_protobuf as these are code generation modules
+		// and can be expected to be in paths without Rust code.
+		"rust_benchmark",
+		"rust_benchmark_host",
 		"rust_binary",
 		"rust_binary_host",
 		"rust_library",
@@ -37,6 +51,7 @@
 		"rust_ffi",
 		"rust_ffi_shared",
 		"rust_ffi_static",
+		"rust_fuzz",
 		"rust_library_host",
 		"rust_library_host_dylib",
 		"rust_library_host_rlib",
diff --git a/rust/config/global.go b/rust/config/global.go
index 43b49d1..e5b334d 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -24,12 +24,11 @@
 var pctx = android.NewPackageContext("android/soong/rust/config")
 
 var (
-	RustDefaultVersion = "1.51.0"
+	RustDefaultVersion = "1.54.0"
 	RustDefaultBase    = "prebuilts/rust/"
 	DefaultEdition     = "2018"
 	Stdlibs            = []string{
 		"libstd",
-		"libtest",
 	}
 
 	// Mapping between Soong internal arch types and std::env constants.
diff --git a/rust/config/x86_darwin_host.go b/rust/config/x86_darwin_host.go
index ddd93e8..8ff0dd4 100644
--- a/rust/config/x86_darwin_host.go
+++ b/rust/config/x86_darwin_host.go
@@ -78,7 +78,7 @@
 
 func (t *toolchainDarwinX8664) ToolchainLinkFlags() string {
 	// Prepend the lld flags from cc_config so we stay in sync with cc
-	return "${cc_config.DarwinClangLldflags} ${config.DarwinToolchainLinkFlags} ${config.DarwinToolchainX8664LinkFlags}"
+	return "${cc_config.DarwinLldflags} ${config.DarwinToolchainLinkFlags} ${config.DarwinToolchainX8664LinkFlags}"
 }
 
 func (t *toolchainDarwinX8664) ToolchainRustFlags() string {
diff --git a/rust/config/x86_device.go b/rust/config/x86_device.go
index aae1125..5ae30e7 100644
--- a/rust/config/x86_device.go
+++ b/rust/config/x86_device.go
@@ -65,7 +65,7 @@
 
 func (t *toolchainX86) ToolchainLinkFlags() string {
 	// Prepend the lld flags from cc_config so we stay in sync with cc
-	return "${config.DeviceGlobalLinkFlags} ${cc_config.X86ClangLldflags} ${config.X86ToolchainLinkFlags}"
+	return "${config.DeviceGlobalLinkFlags} ${cc_config.X86Lldflags} ${config.X86ToolchainLinkFlags}"
 }
 
 func (t *toolchainX86) ToolchainRustFlags() string {
diff --git a/rust/config/x86_linux_host.go b/rust/config/x86_linux_host.go
index b63e14d..0aa534f 100644
--- a/rust/config/x86_linux_host.go
+++ b/rust/config/x86_linux_host.go
@@ -37,6 +37,10 @@
 	registerToolchainFactory(android.Linux, android.X86_64, linuxX8664ToolchainFactory)
 	registerToolchainFactory(android.Linux, android.X86, linuxX86ToolchainFactory)
 
+	// TODO: musl rust support
+	registerToolchainFactory(android.LinuxMusl, android.X86_64, linuxX8664ToolchainFactory)
+	registerToolchainFactory(android.LinuxMusl, android.X86, linuxX86ToolchainFactory)
+
 	pctx.StaticVariable("LinuxToolchainRustFlags", strings.Join(LinuxRustFlags, " "))
 	pctx.StaticVariable("LinuxToolchainLinkFlags", strings.Join(LinuxRustLinkFlags, " "))
 	pctx.StaticVariable("LinuxToolchainX86RustFlags", strings.Join(linuxX86Rustflags, " "))
@@ -79,7 +83,7 @@
 
 func (t *toolchainLinuxX8664) ToolchainLinkFlags() string {
 	// Prepend the lld flags from cc_config so we stay in sync with cc
-	return "${cc_config.LinuxClangLldflags} ${cc_config.LinuxX8664ClangLldflags} " +
+	return "${cc_config.LinuxLldflags} ${cc_config.LinuxX8664Lldflags} " +
 		"${config.LinuxToolchainLinkFlags} ${config.LinuxToolchainX8664LinkFlags}"
 }
 
@@ -117,7 +121,7 @@
 
 func (t *toolchainLinuxX86) ToolchainLinkFlags() string {
 	// Prepend the lld flags from cc_config so we stay in sync with cc
-	return "${cc_config.LinuxClangLldflags} ${cc_config.LinuxX86ClangLldflags} " +
+	return "${cc_config.LinuxLldflags} ${cc_config.LinuxX86Lldflags} " +
 		"${config.LinuxToolchainLinkFlags} ${config.LinuxToolchainX86LinkFlags}"
 }
 
diff --git a/rust/coverage.go b/rust/coverage.go
index dac526a..050b811 100644
--- a/rust/coverage.go
+++ b/rust/coverage.go
@@ -55,7 +55,7 @@
 		flags.Coverage = true
 		coverage := ctx.GetDirectDepWithTag(CovLibraryName, cc.CoverageDepTag).(cc.LinkableInterface)
 		flags.RustFlags = append(flags.RustFlags,
-			"-Z instrument-coverage", "-g", "-C link-dead-code")
+			"-Z instrument-coverage", "-g")
 		flags.LinkFlags = append(flags.LinkFlags,
 			profileInstrFlag, "-g", coverage.OutputFile().Path().String(), "-Wl,--wrap,open")
 		deps.StaticLibs = append(deps.StaticLibs, coverage.OutputFile().Path())
diff --git a/rust/coverage_test.go b/rust/coverage_test.go
index 4b6c9d4..f3cd375 100644
--- a/rust/coverage_test.go
+++ b/rust/coverage_test.go
@@ -56,7 +56,7 @@
 	fizzCov := ctx.ModuleForTests("fizz_cov", "android_arm64_armv8-a_cov").Rule("rustc")
 	buzzNoCov := ctx.ModuleForTests("buzzNoCov", "android_arm64_armv8-a").Rule("rustc")
 
-	rustcCoverageFlags := []string{"-Z instrument-coverage", " -g ", "-C link-dead-code"}
+	rustcCoverageFlags := []string{"-Z instrument-coverage", " -g "}
 	for _, flag := range rustcCoverageFlags {
 		missingErrorStr := "missing rustc flag '%s' for '%s' module with coverage enabled; rustcFlags: %#v"
 		containsErrorStr := "contains rustc flag '%s' for '%s' module with coverage disabled; rustcFlags: %#v"
diff --git a/rust/doc.go b/rust/doc.go
index e7f1371..fe3581b 100644
--- a/rust/doc.go
+++ b/rust/doc.go
@@ -29,6 +29,14 @@
 type rustdocSingleton struct{}
 
 func (n *rustdocSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+	docDir := android.PathForOutput(ctx, "rustdoc")
+	docZip := android.PathForOutput(ctx, "rustdoc.zip")
+	rule := android.NewRuleBuilder(pctx, ctx)
+	zipCmd := rule.Command().BuiltTool("soong_zip").
+		FlagWithOutput("-o ", docZip).
+		FlagWithArg("-C ", docDir.String()).
+		FlagWithArg("-D ", docDir.String())
+
 	ctx.VisitAllModules(func(module android.Module) {
 		if !module.Enabled() {
 			return
@@ -36,8 +44,10 @@
 
 		if m, ok := module.(*Module); ok {
 			if m.docTimestampFile.Valid() {
-				ctx.Phony("rustdoc", m.docTimestampFile.Path())
+				zipCmd.Implicit(m.docTimestampFile.Path())
 			}
 		}
 	})
+	rule.Build("rustdoc-zip", "Zipping all built Rust documentation...")
+	ctx.Phony("rustdoc", docZip)
 }
diff --git a/rust/fuzz.go b/rust/fuzz.go
index 7e1c55a..5fb56ff 100644
--- a/rust/fuzz.go
+++ b/rust/fuzz.go
@@ -21,6 +21,7 @@
 
 	"android/soong/android"
 	"android/soong/cc"
+	"android/soong/fuzz"
 	"android/soong/rust/config"
 )
 
@@ -32,13 +33,7 @@
 type fuzzDecorator struct {
 	*binaryDecorator
 
-	Properties            cc.FuzzProperties
-	dictionary            android.Path
-	corpus                android.Paths
-	corpusIntermediateDir android.Path
-	config                android.Path
-	data                  android.Paths
-	dataIntermediateDir   android.Path
+	fuzzPackagedModule fuzz.FuzzPackagedModule
 }
 
 var _ compiler = (*binaryDecorator)(nil)
@@ -88,7 +83,7 @@
 
 func (fuzzer *fuzzDecorator) compilerProps() []interface{} {
 	return append(fuzzer.binaryDecorator.compilerProps(),
-		&fuzzer.Properties)
+		&fuzzer.fuzzPackagedModule.FuzzProperties)
 }
 
 func (fuzzer *fuzzDecorator) stdLinkage(ctx *depsContext) RustLinkage {
@@ -102,32 +97,19 @@
 // Responsible for generating GNU Make rules that package fuzz targets into
 // their architecture & target/host specific zip file.
 type rustFuzzPackager struct {
-	packages    android.Paths
-	fuzzTargets map[string]bool
+	fuzz.FuzzPackager
 }
 
 func rustFuzzPackagingFactory() android.Singleton {
 	return &rustFuzzPackager{}
 }
 
-type fileToZip struct {
-	SourceFilePath        android.Path
-	DestinationPathPrefix string
-}
-
-type archOs struct {
-	hostOrTarget string
-	arch         string
-	dir          string
-}
-
 func (s *rustFuzzPackager) GenerateBuildActions(ctx android.SingletonContext) {
-
 	// Map between each architecture + host/device combination.
-	archDirs := make(map[archOs][]fileToZip)
+	archDirs := make(map[fuzz.ArchOs][]fuzz.FileToZip)
 
 	// List of individual fuzz targets.
-	s.fuzzTargets = make(map[string]bool)
+	s.FuzzTargets = make(map[string]bool)
 
 	ctx.VisitAllModules(func(module android.Module) {
 		// Discard non-fuzz targets.
@@ -136,23 +118,15 @@
 			return
 		}
 
+		if ok := fuzz.IsValid(rustModule.FuzzModule); !ok || rustModule.Properties.PreventInstall {
+			return
+		}
+
 		fuzzModule, ok := rustModule.compiler.(*fuzzDecorator)
 		if !ok {
 			return
 		}
 
-		// Discard ramdisk + vendor_ramdisk + recovery modules, they're duplicates of
-		// fuzz targets we're going to package anyway.
-		if !rustModule.Enabled() || rustModule.Properties.PreventInstall ||
-			rustModule.InRamdisk() || rustModule.InVendorRamdisk() || rustModule.InRecovery() {
-			return
-		}
-
-		// Discard modules that are in an unavailable namespace.
-		if !rustModule.ExportedToMake() {
-			return
-		}
-
 		hostOrTargetString := "target"
 		if rustModule.Host() {
 			hostOrTargetString = "host"
@@ -160,126 +134,34 @@
 
 		archString := rustModule.Arch().ArchType.String()
 		archDir := android.PathForIntermediates(ctx, "fuzz", hostOrTargetString, archString)
-		archOs := archOs{hostOrTarget: hostOrTargetString, arch: archString, dir: archDir.String()}
+		archOs := fuzz.ArchOs{HostOrTarget: hostOrTargetString, Arch: archString, Dir: archDir.String()}
 
-		var files []fileToZip
+		var files []fuzz.FileToZip
 		builder := android.NewRuleBuilder(pctx, ctx)
 
-		// Package the corpora into a zipfile.
-		if fuzzModule.corpus != nil {
-			corpusZip := archDir.Join(ctx, module.Name()+"_seed_corpus.zip")
-			command := builder.Command().BuiltTool("soong_zip").
-				Flag("-j").
-				FlagWithOutput("-o ", corpusZip)
-			rspFile := corpusZip.ReplaceExtension(ctx, "rsp")
-			command.FlagWithRspFileInputList("-r ", rspFile, fuzzModule.corpus)
-			files = append(files, fileToZip{corpusZip, ""})
-		}
-
-		// Package the data into a zipfile.
-		if fuzzModule.data != nil {
-			dataZip := archDir.Join(ctx, module.Name()+"_data.zip")
-			command := builder.Command().BuiltTool("soong_zip").
-				FlagWithOutput("-o ", dataZip)
-			for _, f := range fuzzModule.data {
-				intermediateDir := strings.TrimSuffix(f.String(), f.Rel())
-				command.FlagWithArg("-C ", intermediateDir)
-				command.FlagWithInput("-f ", f)
-			}
-			files = append(files, fileToZip{dataZip, ""})
-		}
+		// Package the artifacts (data, corpus, config and dictionary into a zipfile.
+		files = s.PackageArtifacts(ctx, module, fuzzModule.fuzzPackagedModule, archDir, builder)
 
 		// The executable.
-		files = append(files, fileToZip{rustModule.unstrippedOutputFile.Path(), ""})
+		files = append(files, fuzz.FileToZip{rustModule.unstrippedOutputFile.Path(), ""})
 
-		// The dictionary.
-		if fuzzModule.dictionary != nil {
-			files = append(files, fileToZip{fuzzModule.dictionary, ""})
+		archDirs[archOs], ok = s.BuildZipFile(ctx, module, fuzzModule.fuzzPackagedModule, files, builder, archDir, archString, hostOrTargetString, archOs, archDirs)
+		if !ok {
+			return
 		}
 
-		// Additional fuzz config.
-		if fuzzModule.config != nil {
-			files = append(files, fileToZip{fuzzModule.config, ""})
-		}
-
-		fuzzZip := archDir.Join(ctx, module.Name()+".zip")
-
-		command := builder.Command().BuiltTool("soong_zip").
-			Flag("-j").
-			FlagWithOutput("-o ", fuzzZip)
-
-		for _, file := range files {
-			if file.DestinationPathPrefix != "" {
-				command.FlagWithArg("-P ", file.DestinationPathPrefix)
-			} else {
-				command.Flag("-P ''")
-			}
-			command.FlagWithInput("-f ", file.SourceFilePath)
-		}
-
-		builder.Build("create-"+fuzzZip.String(),
-			"Package "+module.Name()+" for "+archString+"-"+hostOrTargetString)
-
-		// Don't add modules to 'make haiku-rust' that are set to not be
-		// exported to the fuzzing infrastructure.
-		if config := fuzzModule.Properties.Fuzz_config; config != nil {
-			if rustModule.Host() && !BoolDefault(config.Fuzz_on_haiku_host, true) {
-				return
-			} else if !BoolDefault(config.Fuzz_on_haiku_device, true) {
-				return
-			}
-		}
-
-		s.fuzzTargets[module.Name()] = true
-		archDirs[archOs] = append(archDirs[archOs], fileToZip{fuzzZip, ""})
 	})
-
-	var archOsList []archOs
-	for archOs := range archDirs {
-		archOsList = append(archOsList, archOs)
-	}
-	sort.Slice(archOsList, func(i, j int) bool { return archOsList[i].dir < archOsList[j].dir })
-
-	for _, archOs := range archOsList {
-		filesToZip := archDirs[archOs]
-		arch := archOs.arch
-		hostOrTarget := archOs.hostOrTarget
-		builder := android.NewRuleBuilder(pctx, ctx)
-		outputFile := android.PathForOutput(ctx, "fuzz-rust-"+hostOrTarget+"-"+arch+".zip")
-		s.packages = append(s.packages, outputFile)
-
-		command := builder.Command().BuiltTool("soong_zip").
-			Flag("-j").
-			FlagWithOutput("-o ", outputFile).
-			Flag("-L 0") // No need to try and re-compress the zipfiles.
-
-		for _, fileToZip := range filesToZip {
-			if fileToZip.DestinationPathPrefix != "" {
-				command.FlagWithArg("-P ", fileToZip.DestinationPathPrefix)
-			} else {
-				command.Flag("-P ''")
-			}
-			command.FlagWithInput("-f ", fileToZip.SourceFilePath)
-		}
-		builder.Build("create-fuzz-package-"+arch+"-"+hostOrTarget,
-			"Create fuzz target packages for "+arch+"-"+hostOrTarget)
-	}
-
+	s.CreateFuzzPackage(ctx, archDirs, fuzz.Rust, pctx)
 }
 
 func (s *rustFuzzPackager) MakeVars(ctx android.MakeVarsContext) {
-	packages := s.packages.Strings()
+	packages := s.Packages.Strings()
 	sort.Strings(packages)
 
 	ctx.Strict("SOONG_RUST_FUZZ_PACKAGING_ARCH_MODULES", strings.Join(packages, " "))
 
-	// 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_RUST_FUZZ_TARGETS", strings.Join(fuzzTargets, " "))
+	// Preallocate the slice of fuzz targets to minimize memory allocations.
+	s.PreallocateSlice(ctx, "ALL_RUST_FUZZ_TARGETS")
 }
 
 func (fuzz *fuzzDecorator) install(ctx ModuleContext) {
@@ -289,13 +171,13 @@
 		"fuzz", ctx.Target().Arch.ArchType.String(), ctx.ModuleName())
 	fuzz.binaryDecorator.baseCompiler.install(ctx)
 
-	if fuzz.Properties.Corpus != nil {
-		fuzz.corpus = android.PathsForModuleSrc(ctx, fuzz.Properties.Corpus)
+	if fuzz.fuzzPackagedModule.FuzzProperties.Corpus != nil {
+		fuzz.fuzzPackagedModule.Corpus = android.PathsForModuleSrc(ctx, fuzz.fuzzPackagedModule.FuzzProperties.Corpus)
 	}
-	if fuzz.Properties.Data != nil {
-		fuzz.data = android.PathsForModuleSrc(ctx, fuzz.Properties.Data)
+	if fuzz.fuzzPackagedModule.FuzzProperties.Data != nil {
+		fuzz.fuzzPackagedModule.Data = android.PathsForModuleSrc(ctx, fuzz.fuzzPackagedModule.FuzzProperties.Data)
 	}
-	if fuzz.Properties.Dictionary != nil {
-		fuzz.dictionary = android.PathForModuleSrc(ctx, *fuzz.Properties.Dictionary)
+	if fuzz.fuzzPackagedModule.FuzzProperties.Dictionary != nil {
+		fuzz.fuzzPackagedModule.Dictionary = android.PathForModuleSrc(ctx, *fuzz.fuzzPackagedModule.FuzzProperties.Dictionary)
 	}
 }
diff --git a/rust/image.go b/rust/image.go
index 1b2df1d..5d57f15 100644
--- a/rust/image.go
+++ b/rust/image.go
@@ -34,11 +34,11 @@
 }
 
 func (mod *Module) ProductAvailable() bool {
-	return false
+	return Bool(mod.VendorProperties.Product_available)
 }
 
 func (mod *Module) RamdiskAvailable() bool {
-	return false
+	return Bool(mod.Properties.Ramdisk_available)
 }
 
 func (mod *Module) VendorRamdiskAvailable() bool {
@@ -50,7 +50,7 @@
 }
 
 func (mod *Module) RecoveryAvailable() bool {
-	return false
+	return Bool(mod.Properties.Recovery_available)
 }
 
 func (mod *Module) ExtraVariants() []string {
@@ -62,9 +62,7 @@
 }
 
 func (mod *Module) SetRamdiskVariantNeeded(b bool) {
-	if b {
-		panic("Setting ramdisk variant needed for Rust module is unsupported: " + mod.BaseModuleName())
-	}
+	mod.Properties.RamdiskVariantNeeded = b
 }
 
 func (mod *Module) SetVendorRamdiskVariantNeeded(b bool) {
@@ -72,9 +70,7 @@
 }
 
 func (mod *Module) SetRecoveryVariantNeeded(b bool) {
-	if b {
-		panic("Setting recovery variant needed for Rust module is unsupported: " + mod.BaseModuleName())
-	}
+	mod.Properties.RecoveryVariantNeeded = b
 }
 
 func (mod *Module) SetCoreVariantNeeded(b bool) {
@@ -82,7 +78,12 @@
 }
 
 func (mod *Module) SnapshotVersion(mctx android.BaseModuleContext) string {
-	panic("Rust modules do not support snapshotting: " + mod.BaseModuleName())
+	if snapshot, ok := mod.compiler.(cc.SnapshotInterface); ok {
+		return snapshot.Version()
+	} else {
+		mctx.ModuleErrorf("version is unknown for snapshot prebuilt")
+		return ""
+	}
 }
 
 func (mod *Module) VendorRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
@@ -94,7 +95,7 @@
 }
 
 func (mod *Module) RamdiskVariantNeeded(android.BaseModuleContext) bool {
-	return mod.InRamdisk()
+	return mod.Properties.RamdiskVariantNeeded
 }
 
 func (mod *Module) DebugRamdiskVariantNeeded(ctx android.BaseModuleContext) bool {
@@ -102,7 +103,7 @@
 }
 
 func (mod *Module) RecoveryVariantNeeded(android.BaseModuleContext) bool {
-	return mod.InRecovery()
+	return mod.Properties.RecoveryVariantNeeded
 }
 
 func (mod *Module) ExtraImageVariations(android.BaseModuleContext) []string {
@@ -110,7 +111,9 @@
 }
 
 func (mod *Module) IsSnapshotPrebuilt() bool {
-	// Rust does not support prebuilts in its snapshots
+	if p, ok := mod.compiler.(cc.SnapshotInterface); ok {
+		return p.IsSnapshotPrebuilt()
+	}
 	return false
 }
 
@@ -133,12 +136,17 @@
 }
 
 func (ctx *moduleContext) ProductSpecific() bool {
-	return false
+	return ctx.ModuleContext.ProductSpecific() || ctx.RustModule().productSpecificModuleContext()
+}
+
+func (c *Module) productSpecificModuleContext() bool {
+	// Additionally check if this module is inProduct() that means it is a "product" variant of a
+	// module. As well as product specific modules, product variants must be installed to /product.
+	return c.InProduct()
 }
 
 func (mod *Module) InRecovery() bool {
-	// TODO(b/165791368)
-	return false
+	return mod.ModuleBase.InRecovery() || mod.ModuleBase.InstallInRecovery()
 }
 
 func (mod *Module) InVendorRamdisk() bool {
@@ -159,6 +167,11 @@
 	return false
 }
 
+func (mod *Module) OnlyInProduct() bool {
+	//TODO(b/165791368)
+	return false
+}
+
 // Returns true when this module is configured to have core and vendor variants.
 func (mod *Module) HasVendorVariant() bool {
 	return Bool(mod.VendorProperties.Vendor_available) || Bool(mod.VendorProperties.Odm_available)
@@ -174,7 +187,7 @@
 }
 
 func (mod *Module) InProduct() bool {
-	return false
+	return mod.Properties.ImageVariationPrefix == cc.ProductVariationPrefix
 }
 
 // Returns true if the module is "vendor" variant. Usually these modules are installed in /vendor
@@ -186,6 +199,8 @@
 	m := module.(*Module)
 	if variant == android.VendorRamdiskVariation {
 		m.MakeAsPlatform()
+	} else if variant == android.RecoveryVariation {
+		m.MakeAsPlatform()
 	} else if strings.HasPrefix(variant, cc.VendorVariationPrefix) {
 		m.Properties.ImageVariationPrefix = cc.VendorVariationPrefix
 		m.Properties.VndkVersion = strings.TrimPrefix(variant, cc.VendorVariationPrefix)
@@ -197,15 +212,17 @@
 			m.Properties.HideFromMake = true
 			m.HideFromMake()
 		}
+	} else if strings.HasPrefix(variant, cc.ProductVariationPrefix) {
+		m.Properties.ImageVariationPrefix = cc.ProductVariationPrefix
+		m.Properties.VndkVersion = strings.TrimPrefix(variant, cc.ProductVariationPrefix)
 	}
 }
 
 func (mod *Module) ImageMutatorBegin(mctx android.BaseModuleContext) {
 	// Rust does not support installing to the product image yet.
-	if Bool(mod.VendorProperties.Product_available) {
-		mctx.PropertyErrorf("product_available",
-			"Rust modules do not yet support being available to the product image")
-	} else if mctx.ProductSpecific() {
+	vendorSpecific := mctx.SocSpecific() || mctx.DeviceSpecific()
+
+	if mctx.ProductSpecific() {
 		mctx.PropertyErrorf("product_specific",
 			"Rust modules do not yet support installing to the product image.")
 	} else if Bool(mod.VendorProperties.Double_loadable) {
@@ -217,11 +234,10 @@
 			mctx.PropertyErrorf("vendor_ramdisk_available", "cannot be set for rust_ffi or rust_ffi_shared modules.")
 		}
 	}
-	vendorSpecific := mctx.SocSpecific() || mctx.DeviceSpecific()
 	if vendorSpecific {
-		mctx.PropertyErrorf("vendor or soc_specific",
-			"Rust modules do not yet support soc-specific modules")
-
+		if lib, ok := mod.compiler.(libraryInterface); ok && lib.buildDylib() {
+			mctx.PropertyErrorf("vendor", "Vendor-only dylibs are not yet supported, use rust_library_rlib.")
+		}
 	}
 
 	cc.MutateImage(mctx, mod)
diff --git a/rust/library.go b/rust/library.go
index 1bdf83a..8c10e29 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -21,6 +21,7 @@
 
 	"android/soong/android"
 	"android/soong/cc"
+	"android/soong/snapshot"
 )
 
 var (
@@ -99,6 +100,8 @@
 	MutatedProperties LibraryMutatedProperties
 	includeDirs       android.Paths
 	sourceProvider    SourceProvider
+
+	collectedSnapshotHeaders android.Paths
 }
 
 type libraryInterface interface {
@@ -122,7 +125,8 @@
 	setStatic()
 	setSource()
 
-	// Set libstd linkage
+	// libstd linkage functions
+	rlibStd() bool
 	setRlibStd()
 	setDylibStd()
 
@@ -193,6 +197,10 @@
 	library.MutatedProperties.VariantIsShared = false
 }
 
+func (library *libraryDecorator) rlibStd() bool {
+	return library.MutatedProperties.VariantIsStaticStd
+}
+
 func (library *libraryDecorator) setRlibStd() {
 	library.MutatedProperties.VariantIsStaticStd = true
 }
@@ -220,7 +228,10 @@
 }
 
 func (library *libraryDecorator) autoDep(ctx android.BottomUpMutatorContext) autoDep {
-	if library.preferRlib() {
+	if ctx.Module().(*Module).InVendor() {
+		// Vendor modules should statically link libstd.
+		return rlibAutoDep
+	} else if library.preferRlib() {
 		return rlibAutoDep
 	} else if library.rlib() || library.static() {
 		return rlibAutoDep
@@ -236,7 +247,10 @@
 }
 
 func (library *libraryDecorator) stdLinkage(ctx *depsContext) RustLinkage {
-	if library.static() || library.MutatedProperties.VariantIsStaticStd {
+	if ctx.RustModule().InVendor() {
+		// Vendor modules should statically link libstd.
+		return RlibLinkage
+	} else if library.static() || library.MutatedProperties.VariantIsStaticStd {
 		return RlibLinkage
 	} else if library.baseCompiler.preferRlib() {
 		return RlibLinkage
@@ -418,6 +432,12 @@
 
 func (library *libraryDecorator) compilerFlags(ctx ModuleContext, flags Flags) Flags {
 	flags.RustFlags = append(flags.RustFlags, "-C metadata="+ctx.ModuleName())
+	if library.dylib() {
+		// We need to add a dependency on std in order to link crates as dylibs.
+		// The hack to add this dependency is guarded by the following cfg so
+		// that we don't force a dependency when it isn't needed.
+		library.baseCompiler.Properties.Cfgs = append(library.baseCompiler.Properties.Cfgs, "android_dylib")
+	}
 	flags = library.baseCompiler.compilerFlags(ctx, flags)
 	if library.shared() || library.static() {
 		library.includeDirs = append(library.includeDirs, android.PathsForModuleSrc(ctx, library.Properties.Include_dirs)...)
@@ -490,9 +510,8 @@
 
 	if library.shared() {
 		ctx.SetProvider(cc.SharedLibraryInfoProvider, cc.SharedLibraryInfo{
-			SharedLibrary:           outputFile,
-			UnstrippedSharedLibrary: outputFile,
-			Target:                  ctx.Target(),
+			SharedLibrary: outputFile,
+			Target:        ctx.Target(),
 		})
 	}
 
@@ -623,6 +642,19 @@
 				// Disable dylib Vendor Ramdisk variations until we support these.
 				v.(*Module).Disable()
 			}
+
+			variation := v.(*Module).ModuleBase.ImageVariation().Variation
+			if strings.HasPrefix(variation, cc.VendorVariationPrefix) &&
+				m.HasVendorVariant() &&
+				!snapshot.IsVendorProprietaryModule(mctx) &&
+				strings.TrimPrefix(variation, cc.VendorVariationPrefix) == mctx.DeviceConfig().VndkVersion() {
+
+				// cc.MutateImage runs before LibraryMutator, so vendor variations which are meant for rlibs only are
+				// produced for Dylibs; however, dylibs should not be enabled for boardVndkVersion for
+				// non-vendor proprietary modules.
+				v.(*Module).Disable()
+			}
+
 		case "source":
 			v.(*Module).compiler.(libraryInterface).setSource()
 			// The source variant does not produce any library.
@@ -659,9 +691,10 @@
 				dylib := modules[1].(*Module)
 				rlib.compiler.(libraryInterface).setRlibStd()
 				dylib.compiler.(libraryInterface).setDylibStd()
-				if dylib.ModuleBase.ImageVariation().Variation == android.VendorRamdiskVariation {
+				if dylib.ModuleBase.ImageVariation().Variation == android.VendorRamdiskVariation ||
+					strings.HasPrefix(dylib.ModuleBase.ImageVariation().Variation, cc.VendorVariationPrefix) {
 					// TODO(b/165791368)
-					// Disable rlibs that link against dylib-std on vendor ramdisk variations until those dylib
+					// Disable rlibs that link against dylib-std on vendor and vendor ramdisk variations until those dylib
 					// variants are properly supported.
 					dylib.Disable()
 				}
@@ -671,3 +704,54 @@
 		}
 	}
 }
+
+func (l *libraryDecorator) snapshotHeaders() android.Paths {
+	if l.collectedSnapshotHeaders == nil {
+		panic("snapshotHeaders() must be called after collectHeadersForSnapshot()")
+	}
+	return l.collectedSnapshotHeaders
+}
+
+// collectHeadersForSnapshot collects all exported headers from library.
+// It globs header files in the source tree for exported include directories,
+// and tracks generated header files separately.
+//
+// This is to be called from GenerateAndroidBuildActions, and then collected
+// header files can be retrieved by snapshotHeaders().
+func (l *libraryDecorator) collectHeadersForSnapshot(ctx android.ModuleContext, deps PathDeps) {
+	ret := android.Paths{}
+
+	// Glob together the headers from the modules include_dirs property
+	for _, path := range android.CopyOfPaths(l.includeDirs) {
+		dir := path.String()
+		glob, err := ctx.GlobWithDeps(dir+"/**/*", nil)
+		if err != nil {
+			ctx.ModuleErrorf("glob failed: %#v", err)
+			return
+		}
+
+		for _, header := range glob {
+			// Filter out only the files with extensions that are headers.
+			found := false
+			for _, ext := range cc.HeaderExts {
+				if strings.HasSuffix(header, ext) {
+					found = true
+					break
+				}
+			}
+			if !found {
+				continue
+			}
+			ret = append(ret, android.PathForSource(ctx, header))
+		}
+	}
+
+	// Glob together the headers from C dependencies as well, starting with non-generated headers.
+	ret = append(ret, cc.GlobHeadersForSnapshot(ctx, append(android.CopyOfPaths(deps.depIncludePaths), deps.depSystemIncludePaths...))...)
+
+	// Collect generated headers from C dependencies.
+	ret = append(ret, cc.GlobGeneratedHeadersForSnapshot(ctx, deps.depGeneratedHeaders)...)
+
+	// TODO(185577950): If support for generated headers is added, they need to be collected here as well.
+	l.collectedSnapshotHeaders = ret
+}
diff --git a/rust/library_test.go b/rust/library_test.go
index 54cd2a5..cb4ef7e 100644
--- a/rust/library_test.go
+++ b/rust/library_test.go
@@ -85,6 +85,22 @@
 	}
 }
 
+// Check that we are passing the android_dylib config flag
+func TestAndroidDylib(t *testing.T) {
+	ctx := testRust(t, `
+		rust_library_host_dylib {
+			name: "libfoo",
+			srcs: ["foo.rs"],
+			crate_name: "foo",
+		}`)
+
+	libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Output("libfoo.dylib.so")
+
+	if !strings.Contains(libfooDylib.Args["rustcFlags"], "--cfg 'android_dylib'") {
+		t.Errorf("missing android_dylib cfg flag for libfoo dylib, rustcFlags: %#v", libfooDylib.Args["rustcFlags"])
+	}
+}
+
 func TestValidateLibraryStem(t *testing.T) {
 	testRustError(t, "crate_name must be defined.", `
 			rust_library_host {
diff --git a/rust/project_json.go b/rust/project_json.go
index c28bc7b..faa7db5 100644
--- a/rust/project_json.go
+++ b/rust/project_json.go
@@ -54,7 +54,6 @@
 }
 
 type rustProjectJson struct {
-	Roots  []string           `json:"roots"`
 	Crates []rustProjectCrate `json:"crates"`
 }
 
@@ -248,9 +247,6 @@
 	idx := len(singleton.project.Crates)
 	singleton.knownCrates[rModule.Name()] = crateInfo{Idx: idx, Deps: deps}
 	singleton.project.Crates = append(singleton.project.Crates, crate)
-	// rust-analyzer requires that all crates belong to at least one root:
-	// https://github.com/rust-analyzer/rust-analyzer/issues/4735.
-	singleton.project.Roots = append(singleton.project.Roots, path.Dir(crate.RootModule))
 	return idx, true
 }
 
diff --git a/rust/project_json_test.go b/rust/project_json_test.go
index 09d30db..f7b6681 100644
--- a/rust/project_json_test.go
+++ b/rust/project_json_test.go
@@ -36,7 +36,7 @@
 	// The JSON file is generated via WriteFileToOutputDir. Therefore, it
 	// won't appear in the Output of the TestingSingleton. Manually verify
 	// it exists.
-	content, err := ioutil.ReadFile(filepath.Join(result.Config.BuildDir(), rustProjectJsonFileName))
+	content, err := ioutil.ReadFile(filepath.Join(result.Config.SoongOutDir(), rustProjectJsonFileName))
 	if err != nil {
 		t.Errorf("rust-project.json has not been generated")
 	}
@@ -176,6 +176,8 @@
 }
 
 func TestProjectJsonBindGen(t *testing.T) {
+	buildOS := android.TestConfig(t.TempDir(), nil, "", nil).BuildOS
+
 	bp := `
 	rust_library {
 		name: "libd",
@@ -214,9 +216,9 @@
 		if strings.Contains(rootModule, "libbindings1") && !strings.Contains(rootModule, "android_arm64") {
 			t.Errorf("The source path for libbindings1 does not contain android_arm64, got %v", rootModule)
 		}
-		if strings.Contains(rootModule, "libbindings2") && !strings.Contains(rootModule, android.BuildOs.String()) {
+		if strings.Contains(rootModule, "libbindings2") && !strings.Contains(rootModule, buildOS.String()) {
 			t.Errorf("The source path for libbindings2 does not contain the BuildOs, got %v; want %v",
-				rootModule, android.BuildOs.String())
+				rootModule, buildOS.String())
 		}
 		// Check that libbindings1 does not depend on itself.
 		if strings.Contains(rootModule, "libbindings1") {
diff --git a/rust/rust.go b/rust/rust.go
index f068b3d..0cd299d 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -25,6 +25,7 @@
 	"android/soong/bloaty"
 	"android/soong/cc"
 	cc_config "android/soong/cc/config"
+	"android/soong/fuzz"
 	"android/soong/rust/config"
 )
 
@@ -35,7 +36,7 @@
 
 	android.AddNeverAllowRules(
 		android.NeverAllow().
-			NotIn(config.RustAllowedPaths...).
+			NotIn(append(config.RustAllowedPaths, config.DownstreamRustAllowedPaths...)...).
 			ModuleType(config.RustModuleTypes...))
 
 	android.RegisterModuleType("rust_defaults", defaultsFactory)
@@ -83,15 +84,49 @@
 	// Set by imageMutator
 	CoreVariantNeeded          bool     `blueprint:"mutated"`
 	VendorRamdiskVariantNeeded bool     `blueprint:"mutated"`
+	RamdiskVariantNeeded       bool     `blueprint:"mutated"`
+	RecoveryVariantNeeded      bool     `blueprint:"mutated"`
 	ExtraVariants              []string `blueprint:"mutated"`
 
+	// Allows this module to use non-APEX version of libraries. Useful
+	// for building binaries that are started before APEXes are activated.
+	Bootstrap *bool
+
+	// Used by vendor snapshot to record dependencies from snapshot modules.
+	SnapshotSharedLibs []string `blueprint:"mutated"`
+	SnapshotStaticLibs []string `blueprint:"mutated"`
+
+	// Make this module available when building for ramdisk.
+	// On device without a dedicated recovery partition, the module is only
+	// available after switching root into
+	// /first_stage_ramdisk. To expose the module before switching root, install
+	// the recovery variant instead.
+	Ramdisk_available *bool
+
 	// Make this module available when building for vendor ramdisk.
 	// On device without a dedicated recovery partition, the module is only
 	// available after switching root into
 	// /first_stage_ramdisk. To expose the module before switching root, install
-	// the recovery variant instead (TODO(b/165791368) recovery not yet supported)
+	// the recovery variant instead
 	Vendor_ramdisk_available *bool
 
+	// Normally Soong uses the directory structure to decide which modules
+	// should be included (framework) or excluded (non-framework) from the
+	// different snapshots (vendor, recovery, etc.), but this property
+	// allows a partner to exclude a module normally thought of as a
+	// framework module from the vendor snapshot.
+	Exclude_from_vendor_snapshot *bool
+
+	// Normally Soong uses the directory structure to decide which modules
+	// should be included (framework) or excluded (non-framework) from the
+	// different snapshots (vendor, recovery, etc.), but this property
+	// allows a partner to exclude a module normally thought of as a
+	// framework module from the recovery snapshot.
+	Exclude_from_recovery_snapshot *bool
+
+	// Make this module available when building for recovery
+	Recovery_available *bool
+
 	// Minimum sdk version that the artifact should support when it runs as part of mainline modules(APEX).
 	Min_sdk_version *string
 
@@ -101,9 +136,7 @@
 }
 
 type Module struct {
-	android.ModuleBase
-	android.DefaultableModuleBase
-	android.ApexModuleBase
+	fuzz.FuzzModule
 
 	VendorProperties cc.VendorProperties
 
@@ -154,13 +187,6 @@
 	return mod.sanitize != nil && mod.compiler != nil
 }
 
-func (mod *Module) IsDependencyRoot() bool {
-	if mod.compiler != nil {
-		return mod.compiler.isDependencyRoot()
-	}
-	panic("IsDependencyRoot called on a non-compiler Rust module")
-}
-
 func (mod *Module) IsPrebuilt() bool {
 	if _, ok := mod.compiler.(*prebuiltLibraryDecorator); ok {
 		return true
@@ -242,6 +268,13 @@
 	return false
 }
 
+func (mod *Module) StaticExecutable() bool {
+	if !mod.Binary() {
+		return false
+	}
+	return Bool(mod.compiler.(*binaryDecorator).Properties.Static_executable)
+}
+
 func (mod *Module) Object() bool {
 	// Rust has no modules which produce only object files.
 	return false
@@ -271,6 +304,10 @@
 	return mod.Properties.VndkVersion != ""
 }
 
+func (mod *Module) Bootstrap() bool {
+	return Bool(mod.Properties.Bootstrap)
+}
+
 func (mod *Module) MustUseVendorVariant() bool {
 	return true
 }
@@ -409,6 +446,12 @@
 	// copied. This is equivalent to Cargo's OUT_DIR variable.
 	CargoOutDir() android.OptionalPath
 
+	// CargoPkgVersion returns the value of the Cargo_pkg_version property.
+	CargoPkgVersion() string
+
+	// CargoEnvCompat returns whether Cargo environment variables should be used.
+	CargoEnvCompat() bool
+
 	inData() bool
 	install(ctx ModuleContext)
 	relativeInstallPath() string
@@ -420,7 +463,6 @@
 	SetDisabled()
 
 	stdLinkage(ctx *depsContext) RustLinkage
-	isDependencyRoot() bool
 
 	strippedOutputFilePath() android.OptionalPath
 }
@@ -619,6 +661,10 @@
 }
 
 func (mod *Module) installable(apexInfo android.ApexInfo) bool {
+	if !mod.EverInstallable() {
+		return false
+	}
+
 	// The apex variant is not installable because it is included in the APEX and won't appear
 	// in the system partition as a standalone file.
 	if !apexInfo.IsForPlatform() {
@@ -728,6 +774,10 @@
 }
 
 func (mod *Module) nativeCoverage() bool {
+	// Bug: http://b/137883967 - native-bridge modules do not currently work with coverage
+	if mod.Target().NativeBridge == android.NativeBridgeEnabled {
+		return false
+	}
 	return mod.compiler != nil && mod.compiler.nativeCoverage()
 }
 
@@ -770,9 +820,21 @@
 
 	// Differentiate static libraries that are vendor available
 	if mod.UseVndk() {
-		mod.Properties.SubName += cc.VendorSuffix
+		if mod.InProduct() && !mod.OnlyInProduct() {
+			mod.Properties.SubName += cc.ProductSuffix
+		} else {
+			mod.Properties.SubName += cc.VendorSuffix
+		}
+	} else if mod.InRamdisk() && !mod.OnlyInRamdisk() {
+		mod.Properties.SubName += cc.RamdiskSuffix
 	} else if mod.InVendorRamdisk() && !mod.OnlyInVendorRamdisk() {
 		mod.Properties.SubName += cc.VendorRamdiskSuffix
+	} else if mod.InRecovery() && !mod.OnlyInRecovery() {
+		mod.Properties.SubName += cc.RecoverySuffix
+	}
+
+	if mod.Target().NativeBridge == android.NativeBridgeEnabled {
+		mod.Properties.SubName += cc.NativeBridgeSuffix
 	}
 
 	if !toolchain.Supported() {
@@ -822,10 +884,20 @@
 
 		mod.docTimestampFile = mod.compiler.rustdoc(ctx, flags, deps)
 
+		// glob exported headers for snapshot, if BOARD_VNDK_VERSION is current or
+		// RECOVERY_SNAPSHOT_VERSION is current.
+		if lib, ok := mod.compiler.(snapshotLibraryInterface); ok {
+			if cc.ShouldCollectHeadersForSnapshot(ctx, mod, apexInfo) {
+				lib.collectHeadersForSnapshot(ctx, deps)
+			}
+		}
+
 		apexInfo := actx.Provider(android.ApexInfoProvider).(android.ApexInfo)
 		if mod.installable(apexInfo) {
 			mod.compiler.install(ctx)
 		}
+
+		ctx.Phony("rust", ctx.RustModule().OutputFile().Path())
 	}
 }
 
@@ -923,7 +995,7 @@
 	directRlibDeps := []*Module{}
 	directDylibDeps := []*Module{}
 	directProcMacroDeps := []*Module{}
-	directSharedLibDeps := [](cc.LinkableInterface){}
+	directSharedLibDeps := []cc.SharedLibraryInfo{}
 	directStaticLibDeps := [](cc.LinkableInterface){}
 	directSrcProvidersDeps := []*Module{}
 	directSrcDeps := [](android.SourceFileProducer){}
@@ -957,7 +1029,9 @@
 			case procMacroDepTag:
 				directProcMacroDeps = append(directProcMacroDeps, rustDep)
 				mod.Properties.AndroidMkProcMacroLibs = append(mod.Properties.AndroidMkProcMacroLibs, makeLibName)
-			case android.SourceDepTag:
+			}
+
+			if android.IsSourceDepTagWithOutputTag(depTag, "") {
 				// Since these deps are added in path_properties.go via AddDependencies, we need to ensure the correct
 				// OS/Arch variant is used.
 				var helper string
@@ -1044,14 +1118,28 @@
 				directStaticLibDeps = append(directStaticLibDeps, ccDep)
 				mod.Properties.AndroidMkStaticLibs = append(mod.Properties.AndroidMkStaticLibs, makeLibName)
 			case cc.IsSharedDepTag(depTag):
+				// For the shared lib dependencies, we may link to the stub variant
+				// of the dependency depending on the context (e.g. if this
+				// dependency crosses the APEX boundaries).
+				sharedLibraryInfo, exportedInfo := cc.ChooseStubOrImpl(ctx, dep)
+
+				// Re-get linkObject as ChooseStubOrImpl actually tells us which
+				// object (either from stub or non-stub) to use.
+				linkObject = android.OptionalPathForPath(sharedLibraryInfo.SharedLibrary)
+				linkPath = linkPathFromFilePath(linkObject.Path())
+
 				depPaths.linkDirs = append(depPaths.linkDirs, linkPath)
 				depPaths.linkObjects = append(depPaths.linkObjects, linkObject.String())
-				exportedInfo := ctx.OtherModuleProvider(dep, cc.FlagExporterInfoProvider).(cc.FlagExporterInfo)
 				depPaths.depIncludePaths = append(depPaths.depIncludePaths, exportedInfo.IncludeDirs...)
 				depPaths.depSystemIncludePaths = append(depPaths.depSystemIncludePaths, exportedInfo.SystemIncludeDirs...)
 				depPaths.depClangFlags = append(depPaths.depClangFlags, exportedInfo.Flags...)
 				depPaths.depGeneratedHeaders = append(depPaths.depGeneratedHeaders, exportedInfo.GeneratedHeaders...)
-				directSharedLibDeps = append(directSharedLibDeps, ccDep)
+				directSharedLibDeps = append(directSharedLibDeps, sharedLibraryInfo)
+
+				// Record baseLibName for snapshots.
+				mod.Properties.SnapshotSharedLibs = append(mod.Properties.SnapshotSharedLibs, cc.BaseLibName(depName))
+				mod.Properties.SnapshotStaticLibs = append(mod.Properties.SnapshotStaticLibs, cc.BaseLibName(depName))
+
 				mod.Properties.AndroidMkSharedLibs = append(mod.Properties.AndroidMkSharedLibs, makeLibName)
 				exportDep = true
 			case cc.IsHeaderDepTag(depTag):
@@ -1073,8 +1161,7 @@
 		}
 
 		if srcDep, ok := dep.(android.SourceFileProducer); ok {
-			switch depTag {
-			case android.SourceDepTag:
+			if android.IsSourceDepTagWithOutputTag(depTag, "") {
 				// These are usually genrules which don't have per-target variants.
 				directSrcDeps = append(directSrcDeps, srcDep)
 			}
@@ -1102,11 +1189,11 @@
 	var sharedLibFiles android.Paths
 	var sharedLibDepFiles android.Paths
 	for _, dep := range directSharedLibDeps {
-		sharedLibFiles = append(sharedLibFiles, dep.OutputFile().Path())
-		if dep.Toc().Valid() {
-			sharedLibDepFiles = append(sharedLibDepFiles, dep.Toc().Path())
+		sharedLibFiles = append(sharedLibFiles, dep.SharedLibrary)
+		if dep.TableOfContents.Valid() {
+			sharedLibDepFiles = append(sharedLibDepFiles, dep.TableOfContents.Path())
 		} else {
-			sharedLibDepFiles = append(sharedLibDepFiles, dep.OutputFile().Path())
+			sharedLibDepFiles = append(sharedLibDepFiles, dep.SharedLibrary)
 		}
 	}
 
@@ -1146,6 +1233,18 @@
 	return mod.compiler.inData()
 }
 
+func (mod *Module) InstallInRamdisk() bool {
+	return mod.InRamdisk()
+}
+
+func (mod *Module) InstallInVendorRamdisk() bool {
+	return mod.InVendorRamdisk()
+}
+
+func (mod *Module) InstallInRecovery() bool {
+	return mod.InRecovery()
+}
+
 func linkPathFromFilePath(filepath android.Path) string {
 	return strings.Split(filepath.String(), filepath.Base())[0]
 }
@@ -1157,6 +1256,11 @@
 
 	deps := mod.deps(ctx)
 	var commonDepVariations []blueprint.Variation
+	var snapshotInfo *cc.SnapshotInfo
+
+	if ctx.Os() == android.Android {
+		deps.SharedLibs, _ = cc.RewriteLibs(mod, &snapshotInfo, actx, ctx.Config(), deps.SharedLibs)
+	}
 
 	stdLinkage := "dylib-std"
 	if mod.compiler.stdLinkage(ctx) == RlibLinkage {
@@ -1164,61 +1268,101 @@
 	}
 
 	rlibDepVariations := commonDepVariations
+
 	if lib, ok := mod.compiler.(libraryInterface); !ok || !lib.sysroot() {
 		rlibDepVariations = append(rlibDepVariations,
 			blueprint.Variation{Mutator: "rust_stdlinkage", Variation: stdLinkage})
 	}
 
-	actx.AddVariationDependencies(
-		append(rlibDepVariations, []blueprint.Variation{
-			{Mutator: "rust_libraries", Variation: rlibVariation}}...),
-		rlibDepTag, deps.Rlibs...)
+	// rlibs
+	for _, lib := range deps.Rlibs {
+		depTag := rlibDepTag
+		lib = cc.RewriteSnapshotLib(lib, cc.GetSnapshot(mod, &snapshotInfo, actx).Rlibs)
+
+		actx.AddVariationDependencies(append(rlibDepVariations, []blueprint.Variation{
+			{Mutator: "rust_libraries", Variation: rlibVariation},
+		}...), depTag, lib)
+	}
+
+	// dylibs
 	actx.AddVariationDependencies(
 		append(commonDepVariations, []blueprint.Variation{
 			{Mutator: "rust_libraries", Variation: dylibVariation}}...),
 		dylibDepTag, deps.Dylibs...)
 
+	// rustlibs
 	if deps.Rustlibs != nil && !mod.compiler.Disabled() {
 		autoDep := mod.compiler.(autoDeppable).autoDep(ctx)
 		if autoDep.depTag == rlibDepTag {
-			actx.AddVariationDependencies(
-				append(rlibDepVariations, blueprint.Variation{Mutator: "rust_libraries", Variation: autoDep.variation}),
-				autoDep.depTag, deps.Rustlibs...)
+			for _, lib := range deps.Rustlibs {
+				depTag := autoDep.depTag
+				lib = cc.RewriteSnapshotLib(lib, cc.GetSnapshot(mod, &snapshotInfo, actx).Rlibs)
+				actx.AddVariationDependencies(append(rlibDepVariations, []blueprint.Variation{
+					{Mutator: "rust_libraries", Variation: autoDep.variation},
+				}...), depTag, lib)
+			}
 		} else {
 			actx.AddVariationDependencies(
 				append(commonDepVariations, blueprint.Variation{Mutator: "rust_libraries", Variation: autoDep.variation}),
 				autoDep.depTag, deps.Rustlibs...)
 		}
 	}
+
+	// stdlibs
 	if deps.Stdlibs != nil {
 		if mod.compiler.stdLinkage(ctx) == RlibLinkage {
-			actx.AddVariationDependencies(
-				append(commonDepVariations, blueprint.Variation{Mutator: "rust_libraries", Variation: "rlib"}),
-				rlibDepTag, deps.Stdlibs...)
+			for _, lib := range deps.Stdlibs {
+				depTag := rlibDepTag
+				lib = cc.RewriteSnapshotLib(lib, cc.GetSnapshot(mod, &snapshotInfo, actx).Rlibs)
+
+				actx.AddVariationDependencies(append(commonDepVariations, []blueprint.Variation{{Mutator: "rust_libraries", Variation: "rlib"}}...),
+					depTag, lib)
+			}
 		} else {
 			actx.AddVariationDependencies(
 				append(commonDepVariations, blueprint.Variation{Mutator: "rust_libraries", Variation: "dylib"}),
 				dylibDepTag, deps.Stdlibs...)
 		}
 	}
-	actx.AddVariationDependencies(append(commonDepVariations,
-		blueprint.Variation{Mutator: "link", Variation: "shared"}),
-		cc.SharedDepTag(), deps.SharedLibs...)
-	actx.AddVariationDependencies(append(commonDepVariations,
-		blueprint.Variation{Mutator: "link", Variation: "static"}),
-		cc.StaticDepTag(false), deps.StaticLibs...)
-	actx.AddVariationDependencies(append(commonDepVariations,
-		blueprint.Variation{Mutator: "link", Variation: "static"}),
-		cc.StaticDepTag(true), deps.WholeStaticLibs...)
+
+	for _, lib := range deps.SharedLibs {
+		depTag := cc.SharedDepTag()
+		name, version := cc.StubsLibNameAndVersion(lib)
+
+		variations := []blueprint.Variation{
+			{Mutator: "link", Variation: "shared"},
+		}
+		cc.AddSharedLibDependenciesWithVersions(ctx, mod, variations, depTag, name, version, false)
+	}
+
+	for _, lib := range deps.WholeStaticLibs {
+		depTag := cc.StaticDepTag(true)
+		lib = cc.RewriteSnapshotLib(lib, cc.GetSnapshot(mod, &snapshotInfo, actx).StaticLibs)
+
+		actx.AddVariationDependencies([]blueprint.Variation{
+			{Mutator: "link", Variation: "static"},
+		}, depTag, lib)
+	}
+
+	for _, lib := range deps.StaticLibs {
+		depTag := cc.StaticDepTag(false)
+		lib = cc.RewriteSnapshotLib(lib, cc.GetSnapshot(mod, &snapshotInfo, actx).StaticLibs)
+
+		actx.AddVariationDependencies([]blueprint.Variation{
+			{Mutator: "link", Variation: "static"},
+		}, depTag, lib)
+	}
 
 	actx.AddVariationDependencies(nil, cc.HeaderDepTag(), deps.HeaderLibs...)
 
 	crtVariations := cc.GetCrtVariations(ctx, mod)
 	if deps.CrtBegin != "" {
-		actx.AddVariationDependencies(crtVariations, cc.CrtBeginDepTag, deps.CrtBegin)
+		actx.AddVariationDependencies(crtVariations, cc.CrtBeginDepTag,
+			cc.RewriteSnapshotLib(deps.CrtBegin, cc.GetSnapshot(mod, &snapshotInfo, actx).Objects))
 	}
 	if deps.CrtEnd != "" {
-		actx.AddVariationDependencies(crtVariations, cc.CrtEndDepTag, deps.CrtEnd)
+		actx.AddVariationDependencies(crtVariations, cc.CrtEndDepTag,
+			cc.RewriteSnapshotLib(deps.CrtEnd, cc.GetSnapshot(mod, &snapshotInfo, actx).Objects))
 	}
 
 	if mod.sourceProvider != nil {
@@ -1228,6 +1372,7 @@
 				bindgen.Properties.Custom_bindgen)
 		}
 	}
+
 	// proc_macros are compiler plugins, and so we need the host arch variant as a dependendcy.
 	actx.AddFarVariationDependencies(ctx.Config().BuildOSTarget.Variations(), procMacroDepTag, deps.ProcMacros...)
 }
diff --git a/rust/rust_test.go b/rust/rust_test.go
index 6ae05d9..80f693e 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -37,21 +37,29 @@
 
 	genrule.PrepareForTestWithGenRuleBuildComponents,
 
-	PrepareForIntegrationTestWithRust,
+	PrepareForTestWithRustIncludeVndk,
+	android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+		variables.DeviceVndkVersion = StringPtr("current")
+		variables.ProductVndkVersion = StringPtr("current")
+		variables.Platform_vndk_version = StringPtr("29")
+	}),
 )
 
 var rustMockedFiles = android.MockFS{
-	"foo.rs":          nil,
-	"foo.c":           nil,
-	"src/bar.rs":      nil,
-	"src/any.h":       nil,
-	"proto.proto":     nil,
-	"proto/buf.proto": nil,
-	"buf.proto":       nil,
-	"foo.proto":       nil,
-	"liby.so":         nil,
-	"libz.so":         nil,
-	"data.txt":        nil,
+	"foo.rs":                       nil,
+	"foo.c":                        nil,
+	"src/bar.rs":                   nil,
+	"src/any.h":                    nil,
+	"c_includes/c_header.h":        nil,
+	"rust_includes/rust_headers.h": nil,
+	"proto.proto":                  nil,
+	"proto/buf.proto":              nil,
+	"buf.proto":                    nil,
+	"foo.proto":                    nil,
+	"liby.so":                      nil,
+	"libz.so":                      nil,
+	"data.txt":                     nil,
+	"liblog.map.txt":               nil,
 }
 
 // testRust returns a TestContext in which a basic environment has been setup.
@@ -67,19 +75,33 @@
 }
 
 func testRustVndk(t *testing.T, bp string) *android.TestContext {
+	return testRustVndkFs(t, bp, rustMockedFiles)
+}
+
+const (
+	sharedVendorVariant = "android_vendor.29_arm64_armv8-a_shared"
+	rlibVendorVariant   = "android_vendor.29_arm64_armv8-a_rlib_rlib-std"
+)
+
+func testRustVndkFs(t *testing.T, bp string, fs android.MockFS) *android.TestContext {
+	return testRustVndkFsVersions(t, bp, fs, "current", "current", "29")
+}
+
+func testRustVndkFsVersions(t *testing.T, bp string, fs android.MockFS, device_version, product_version, vndk_version string) *android.TestContext {
 	skipTestIfOsNotSupported(t)
 	result := android.GroupFixturePreparers(
 		prepareForRustTest,
-		rustMockedFiles.AddToFixture(),
+		fs.AddToFixture(),
 		android.FixtureModifyProductVariables(
 			func(variables android.FixtureProductVariables) {
-				variables.DeviceVndkVersion = StringPtr("current")
-				variables.ProductVndkVersion = StringPtr("current")
-				variables.Platform_vndk_version = StringPtr("29")
+				variables.DeviceVndkVersion = StringPtr(device_version)
+				variables.ProductVndkVersion = StringPtr(product_version)
+				variables.Platform_vndk_version = StringPtr(vndk_version)
 			},
 		),
 	).RunTestWithBp(t, bp)
 	return result.TestContext
+
 }
 
 // testRustCov returns a TestContext in which a basic environment has been
@@ -115,10 +137,14 @@
 
 // testRustVndkError is similar to testRustError, but can be used to test VNDK-related errors.
 func testRustVndkError(t *testing.T, pattern string, bp string) {
+	testRustVndkFsError(t, pattern, bp, rustMockedFiles)
+}
+
+func testRustVndkFsError(t *testing.T, pattern string, bp string, fs android.MockFS) {
 	skipTestIfOsNotSupported(t)
 	android.GroupFixturePreparers(
 		prepareForRustTest,
-		rustMockedFiles.AddToFixture(),
+		fs.AddToFixture(),
 		android.FixtureModifyProductVariables(
 			func(variables android.FixtureProductVariables) {
 				variables.DeviceVndkVersion = StringPtr("current")
diff --git a/rust/sanitize.go b/rust/sanitize.go
index 3d14d51..a4ba4bd 100644
--- a/rust/sanitize.go
+++ b/rust/sanitize.go
@@ -47,6 +47,9 @@
 	"-C llvm-args=-sanitizer-coverage-trace-geps",
 	"-C llvm-args=-sanitizer-coverage-prune-blocks=0",
 
+	// See https://github.com/rust-fuzz/cargo-fuzz/pull/193
+	"-C link-dead-code",
+
 	// Sancov breaks with lto
 	// TODO: Remove when https://bugs.llvm.org/show_bug.cgi?id=41734 is resolved and sancov works with LTO
 	"-C lto=no",
diff --git a/rust/snapshot_prebuilt.go b/rust/snapshot_prebuilt.go
new file mode 100644
index 0000000..79eaab3
--- /dev/null
+++ b/rust/snapshot_prebuilt.go
@@ -0,0 +1,122 @@
+// Copyright 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rust
+
+import (
+	"android/soong/android"
+	"android/soong/cc"
+
+	"github.com/google/blueprint/proptools"
+)
+
+const (
+	snapshotRlibSuffix = "_rlib."
+)
+
+type snapshotLibraryDecorator struct {
+	cc.BaseSnapshotDecorator
+	*libraryDecorator
+	properties          cc.SnapshotLibraryProperties
+	sanitizerProperties struct {
+		CfiEnabled bool `blueprint:"mutated"`
+
+		// Library flags for cfi variant.
+		Cfi cc.SnapshotLibraryProperties `android:"arch_variant"`
+	}
+}
+
+func init() {
+	registerRustSnapshotModules(android.InitRegistrationContext)
+}
+
+func registerRustSnapshotModules(ctx android.RegistrationContext) {
+	cc.VendorSnapshotImageSingleton.RegisterAdditionalModule(ctx,
+		"vendor_snapshot_rlib", VendorSnapshotRlibFactory)
+}
+
+func snapshotLibraryFactory(image cc.SnapshotImage, moduleSuffix string) (*Module, *snapshotLibraryDecorator) {
+	module, library := NewRustLibrary(android.DeviceSupported)
+
+	module.sanitize = nil
+	library.stripper.StripProperties.Strip.None = proptools.BoolPtr(true)
+
+	prebuilt := &snapshotLibraryDecorator{
+		libraryDecorator: library,
+	}
+
+	module.compiler = prebuilt
+
+	prebuilt.Init(module, image, moduleSuffix)
+	module.AddProperties(
+		&prebuilt.properties,
+		&prebuilt.sanitizerProperties,
+	)
+
+	return module, prebuilt
+}
+
+func (library *snapshotLibraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) android.Path {
+	var variant string
+	if library.static() {
+		variant = cc.SnapshotStaticSuffix
+	} else if library.shared() {
+		variant = cc.SnapshotSharedSuffix
+	} else if library.rlib() {
+		variant = cc.SnapshotRlibSuffix
+	}
+
+	if !library.dylib() {
+		// TODO(184042776): Remove this check when dylibs are supported in snapshots.
+		library.SetSnapshotAndroidMkSuffix(ctx, variant)
+	}
+
+	if !library.MatchesWithDevice(ctx.DeviceConfig()) {
+		return nil
+	}
+
+	return android.PathForModuleSrc(ctx, *library.properties.Src)
+}
+
+func (library *snapshotLibraryDecorator) rustdoc(ctx ModuleContext, flags Flags, deps PathDeps) android.OptionalPath {
+	return android.OptionalPath{}
+}
+
+// vendor_snapshot_rlib is a special prebuilt rlib library which is auto-generated by
+// development/vendor_snapshot/update.py. As a part of vendor snapshot, vendor_snapshot_rlib
+// overrides the vendor variant of the rust rlib library with the same name, if BOARD_VNDK_VERSION
+// is set.
+func VendorSnapshotRlibFactory() android.Module {
+	module, prebuilt := snapshotLibraryFactory(cc.VendorSnapshotImageSingleton, cc.SnapshotRlibSuffix)
+	prebuilt.libraryDecorator.BuildOnlyRlib()
+	prebuilt.libraryDecorator.setNoStdlibs()
+	return module.Init()
+}
+
+func (library *snapshotLibraryDecorator) MatchesWithDevice(config android.DeviceConfig) bool {
+	arches := config.Arches()
+	if len(arches) == 0 || arches[0].ArchType.String() != library.Arch() {
+		return false
+	}
+	if library.properties.Src == nil {
+		return false
+	}
+	return true
+}
+
+func (library *snapshotLibraryDecorator) IsSnapshotPrebuilt() bool {
+	return true
+}
+
+var _ cc.SnapshotInterface = (*snapshotLibraryDecorator)(nil)
diff --git a/rust/snapshot_utils.go b/rust/snapshot_utils.go
index 943c790..8dabd9b 100644
--- a/rust/snapshot_utils.go
+++ b/rust/snapshot_utils.go
@@ -18,18 +18,33 @@
 	"android/soong/android"
 )
 
+// snapshotLibraryInterface is an interface for libraries captured to VNDK / vendor snapshots.
+type snapshotLibraryInterface interface {
+	libraryInterface
+
+	// collectHeadersForSnapshot is called in GenerateAndroidBuildActions for snapshot aware
+	// modules (See isSnapshotAware below).
+	// This function should gather all headers needed for snapshot.
+	collectHeadersForSnapshot(ctx android.ModuleContext, deps PathDeps)
+
+	// snapshotHeaders should return collected headers by collectHeadersForSnapshot.
+	// Calling snapshotHeaders before collectHeadersForSnapshot is an error.
+	snapshotHeaders() android.Paths
+}
+
 func (mod *Module) ExcludeFromVendorSnapshot() bool {
-	// TODO Rust does not yet support snapshotting
-	return false
+	return Bool(mod.Properties.Exclude_from_vendor_snapshot)
 }
 
 func (mod *Module) ExcludeFromRecoverySnapshot() bool {
-	// TODO Rust does not yet support snapshotting
-	return false
+	return Bool(mod.Properties.Exclude_from_recovery_snapshot)
 }
 
 func (mod *Module) IsSnapshotLibrary() bool {
-	// TODO Rust does not yet support snapshotting
+	if lib, ok := mod.compiler.(libraryInterface); ok {
+		// Rust-native dylibs are not snapshot supported yet. Only snapshot the rlib-std variants of rlibs.
+		return lib.shared() || lib.static() || (lib.rlib() && lib.rlibStd())
+	}
 	return false
 }
 
@@ -39,8 +54,11 @@
 }
 
 func (mod *Module) SnapshotSharedLibs() []string {
-	// TODO Rust does not yet support snapshotting
-	return []string{}
+	return mod.Properties.SnapshotSharedLibs
+}
+
+func (mod *Module) SnapshotStaticLibs() []string {
+	return mod.Properties.SnapshotStaticLibs
 }
 
 func (mod *Module) Symlinks() []string {
@@ -49,6 +67,8 @@
 }
 
 func (m *Module) SnapshotHeaders() android.Paths {
-	// TODO Rust does not yet support snapshotting
+	if l, ok := m.compiler.(snapshotLibraryInterface); ok {
+		return l.snapshotHeaders()
+	}
 	return android.Paths{}
 }
diff --git a/rust/test.go b/rust/test.go
index 6caa7b1..e95b47c 100644
--- a/rust/test.go
+++ b/rust/test.go
@@ -169,3 +169,11 @@
 func (test *testDecorator) stdLinkage(ctx *depsContext) RustLinkage {
 	return RlibLinkage
 }
+
+func (test *testDecorator) compilerDeps(ctx DepsContext, deps Deps) Deps {
+	deps = test.binaryDecorator.compilerDeps(ctx, deps)
+
+	deps.Rustlibs = append(deps.Rustlibs, "libtest")
+
+	return deps
+}
diff --git a/rust/testing.go b/rust/testing.go
index a0f86b2..94cdd9d 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -45,6 +45,11 @@
 	PrepareForTestWithRustDefaultModules,
 )
 
+var PrepareForTestWithRustIncludeVndk = android.GroupFixturePreparers(
+	PrepareForIntegrationTestWithRust,
+	cc.PrepareForTestWithCcIncludeVndk,
+)
+
 func GatherRequiredDepsForTest() string {
 	bp := `
 		rust_prebuilt_library {
@@ -130,6 +135,9 @@
 			apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
 			min_sdk_version: "29",
 			vendor_available: true,
+			llndk: {
+				symbol_file: "liblog.map.txt",
+			},
 		}
 		cc_library {
 			name: "libprotobuf-cpp-full",
@@ -162,12 +170,10 @@
 			name: "libtest",
 			crate_name: "test",
 			srcs: ["foo.rs"],
-			no_stdlibs: true,
 			host_supported: true,
 			vendor_available: true,
 			vendor_ramdisk_available: true,
 			native_coverage: false,
-			sysroot: true,
 			apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
 			min_sdk_version: "29",
 		}
@@ -240,4 +246,5 @@
 		ctx.BottomUp("rust_begin", BeginMutator).Parallel()
 	})
 	ctx.RegisterSingletonType("rust_project_generator", rustProjectGeneratorSingleton)
+	registerRustSnapshotModules(ctx)
 }
diff --git a/rust/vendor_snapshot_test.go b/rust/vendor_snapshot_test.go
new file mode 100644
index 0000000..60ddb65
--- /dev/null
+++ b/rust/vendor_snapshot_test.go
@@ -0,0 +1,996 @@
+// Copyright 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rust
+
+import (
+	"fmt"
+	"path/filepath"
+	"reflect"
+	"strings"
+	"testing"
+
+	"android/soong/android"
+	"android/soong/cc"
+)
+
+func TestVendorSnapshotCapture(t *testing.T) {
+	bp := `
+	rust_ffi {
+		name: "libffivendor_available",
+		crate_name: "ffivendor_available",
+		srcs: ["lib.rs"],
+		vendor_available: true,
+		include_dirs: ["rust_headers/"],
+	}
+
+	rust_ffi {
+		name: "libffivendor",
+		crate_name: "ffivendor",
+		srcs: ["lib.rs"],
+		vendor: true,
+		include_dirs: ["rust_headers/"],
+	}
+
+	rust_library {
+		name: "librustvendor_available",
+		crate_name: "rustvendor_available",
+		srcs: ["lib.rs"],
+		vendor_available: true,
+		include_dirs: ["rust_headers/"],
+	}
+
+	rust_library_rlib {
+		name: "librustvendor",
+		crate_name: "rustvendor",
+		srcs: ["lib.rs"],
+		vendor: true,
+		include_dirs: ["rust_headers/"],
+	}
+
+	rust_binary {
+		name: "vendor_available_bin",
+		vendor_available: true,
+		srcs: ["srcs/lib.rs"],
+	}
+
+	rust_binary {
+		name: "vendor_bin",
+		vendor: true,
+		srcs: ["srcs/lib.rs"],
+	}
+    `
+	skipTestIfOsNotSupported(t)
+	result := android.GroupFixturePreparers(
+		prepareForRustTest,
+		rustMockedFiles.AddToFixture(),
+		android.FixtureModifyProductVariables(
+			func(variables android.FixtureProductVariables) {
+				variables.DeviceVndkVersion = StringPtr("current")
+				variables.Platform_vndk_version = StringPtr("29")
+			},
+		),
+	).RunTestWithBp(t, bp)
+	ctx := result.TestContext
+
+	// Check Vendor snapshot output.
+
+	snapshotDir := "vendor-snapshot"
+	snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
+	snapshotSingleton := ctx.SingletonForTests("vendor-snapshot")
+	var jsonFiles []string
+	for _, arch := range [][]string{
+		[]string{"arm64", "armv8-a"},
+		[]string{"arm", "armv7-a-neon"},
+	} {
+		archType := arch[0]
+		archVariant := arch[1]
+		archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
+
+		// For shared libraries, only non-VNDK vendor_available modules are captured
+		sharedVariant := fmt.Sprintf("android_vendor.29_%s_%s_shared", archType, archVariant)
+		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
+		cc.CheckSnapshot(t, ctx, snapshotSingleton, "libffivendor_available", "libffivendor_available.so", sharedDir, sharedVariant)
+		jsonFiles = append(jsonFiles,
+			filepath.Join(sharedDir, "libffivendor_available.so.json"))
+
+		// For static libraries, all vendor:true and vendor_available modules (including VNDK) are captured.
+		staticVariant := fmt.Sprintf("android_vendor.29_%s_%s_static", archType, archVariant)
+		staticDir := filepath.Join(snapshotVariantPath, archDir, "static")
+		cc.CheckSnapshot(t, ctx, snapshotSingleton, "libffivendor_available", "libffivendor_available.a", staticDir, staticVariant)
+		cc.CheckSnapshot(t, ctx, snapshotSingleton, "libffivendor", "libffivendor.a", staticDir, staticVariant)
+		jsonFiles = append(jsonFiles,
+			filepath.Join(staticDir, "libffivendor_available.a.json"))
+		jsonFiles = append(jsonFiles,
+			filepath.Join(staticDir, "libffivendor.a.json"))
+
+		// For rlib libraries, all vendor:true and vendor_available modules (including VNDK) are captured.
+		rlibVariant := fmt.Sprintf("android_vendor.29_%s_%s_rlib_rlib-std", archType, archVariant)
+		rlibDir := filepath.Join(snapshotVariantPath, archDir, "rlib")
+		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librustvendor_available", "librustvendor_available.rlib", rlibDir, rlibVariant)
+		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librustvendor", "librustvendor.rlib", rlibDir, rlibVariant)
+		jsonFiles = append(jsonFiles,
+			filepath.Join(rlibDir, "librustvendor_available.rlib.json"))
+		jsonFiles = append(jsonFiles,
+			filepath.Join(rlibDir, "librustvendor.rlib.json"))
+
+		// For binary executables, all vendor:true and vendor_available modules are captured.
+		if archType == "arm64" {
+			binaryVariant := fmt.Sprintf("android_vendor.29_%s_%s", archType, archVariant)
+			binaryDir := filepath.Join(snapshotVariantPath, archDir, "binary")
+			cc.CheckSnapshot(t, ctx, snapshotSingleton, "vendor_available_bin", "vendor_available_bin", binaryDir, binaryVariant)
+			cc.CheckSnapshot(t, ctx, snapshotSingleton, "vendor_bin", "vendor_bin", binaryDir, binaryVariant)
+			jsonFiles = append(jsonFiles,
+				filepath.Join(binaryDir, "vendor_available_bin.json"))
+			jsonFiles = append(jsonFiles,
+				filepath.Join(binaryDir, "vendor_bin.json"))
+		}
+	}
+
+	for _, jsonFile := range jsonFiles {
+		// verify all json files exist
+		if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
+			t.Errorf("%q expected but not found; #%v", jsonFile, jsonFiles)
+		}
+	}
+
+	// fake snapshot should have all outputs in the normal snapshot.
+	fakeSnapshotSingleton := ctx.SingletonForTests("vendor-fake-snapshot")
+
+	for _, output := range snapshotSingleton.AllOutputs() {
+		fakeOutput := strings.Replace(output, "/vendor-snapshot/", "/fake/vendor-snapshot/", 1)
+		if fakeSnapshotSingleton.MaybeOutput(fakeOutput).Rule == nil {
+			t.Errorf("%q expected but not found", fakeOutput)
+		}
+	}
+}
+
+func TestVendorSnapshotDirected(t *testing.T) {
+	bp := `
+	rust_ffi_shared {
+		name: "libffivendor_available",
+		crate_name: "ffivendor_available",
+		srcs: ["lib.rs"],
+		vendor_available: true,
+	}
+
+	rust_library {
+		name: "librustvendor_available",
+		crate_name: "rustvendor_available",
+		srcs: ["lib.rs"],
+		vendor_available: true,
+	}
+
+	rust_ffi_shared {
+		name: "libffivendor_exclude",
+		crate_name: "ffivendor_exclude",
+		srcs: ["lib.rs"],
+		vendor_available: true,
+	}
+
+	rust_library {
+		name: "librustvendor_exclude",
+		crate_name: "rustvendor_exclude",
+		srcs: ["lib.rs"],
+		vendor_available: true,
+	}
+`
+	ctx := testRustVndk(t, bp)
+	ctx.Config().TestProductVariables.VendorSnapshotModules = make(map[string]bool)
+	ctx.Config().TestProductVariables.VendorSnapshotModules["librustvendor_available"] = true
+	ctx.Config().TestProductVariables.VendorSnapshotModules["libffivendor_available"] = true
+	ctx.Config().TestProductVariables.DirectedVendorSnapshot = true
+
+	// Check Vendor snapshot output.
+
+	snapshotDir := "vendor-snapshot"
+	snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
+	snapshotSingleton := ctx.SingletonForTests("vendor-snapshot")
+
+	var includeJsonFiles []string
+
+	for _, arch := range [][]string{
+		[]string{"arm64", "armv8-a"},
+		[]string{"arm", "armv7-a-neon"},
+	} {
+		archType := arch[0]
+		archVariant := arch[1]
+		archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
+
+		sharedVariant := fmt.Sprintf("android_vendor.29_%s_%s_shared", archType, archVariant)
+		rlibVariant := fmt.Sprintf("android_vendor.29_%s_%s_rlib_rlib-std", archType, archVariant)
+		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
+		rlibDir := filepath.Join(snapshotVariantPath, archDir, "rlib")
+
+		// Included modules
+		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librustvendor_available", "librustvendor_available.rlib", rlibDir, rlibVariant)
+		cc.CheckSnapshot(t, ctx, snapshotSingleton, "libffivendor_available", "libffivendor_available.so", sharedDir, sharedVariant)
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(rlibDir, "librustvendor_available.rlib.json"))
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libffivendor_available.so.json"))
+
+		// Excluded modules. Modules not included in the directed vendor snapshot
+		// are still include as fake modules.
+		cc.CheckSnapshotRule(t, ctx, snapshotSingleton, "librustvendor_exclude", "librustvendor_exclude.rlib", rlibDir, rlibVariant)
+		cc.CheckSnapshotRule(t, ctx, snapshotSingleton, "libffivendor_exclude", "libffivendor_exclude.so", sharedDir, sharedVariant)
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(rlibDir, "librustvendor_exclude.rlib.json"))
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libffivendor_exclude.so.json"))
+	}
+
+	// Verify that each json file for an included module has a rule.
+	for _, jsonFile := range includeJsonFiles {
+		if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
+			t.Errorf("include json file %q not found", jsonFile)
+		}
+	}
+}
+
+func TestVendorSnapshotExclude(t *testing.T) {
+
+	// This test verifies that the exclude_from_vendor_snapshot property
+	// makes its way from the Android.bp source file into the module data
+	// structure. It also verifies that modules are correctly included or
+	// excluded in the vendor snapshot based on their path (framework or
+	// vendor) and the exclude_from_vendor_snapshot property.
+
+	frameworkBp := `
+		rust_ffi_shared {
+			name: "libinclude",
+			crate_name: "include",
+			srcs: ["include.rs"],
+			vendor_available: true,
+		}
+
+		rust_ffi_shared {
+			name: "libexclude",
+			crate_name: "exclude",
+			srcs: ["exclude.rs"],
+			vendor: true,
+			exclude_from_vendor_snapshot: true,
+		}
+
+		rust_ffi_shared {
+			name: "libavailable_exclude",
+			crate_name: "available_exclude",
+			srcs: ["lib.rs"],
+			vendor_available: true,
+			exclude_from_vendor_snapshot: true,
+		}
+
+		rust_library {
+			name: "librust_include",
+			crate_name: "rust_include",
+			srcs: ["include.rs"],
+			vendor_available: true,
+		}
+
+		rust_library_rlib {
+			name: "librust_exclude",
+			crate_name: "rust_exclude",
+			srcs: ["exclude.rs"],
+			vendor: true,
+			exclude_from_vendor_snapshot: true,
+		}
+
+		rust_library {
+			name: "librust_available_exclude",
+			crate_name: "rust_available_exclude",
+			srcs: ["lib.rs"],
+			vendor_available: true,
+			exclude_from_vendor_snapshot: true,
+		}
+	`
+
+	mockFS := map[string][]byte{
+		"framework/Android.bp": []byte(frameworkBp),
+		"framework/include.rs": nil,
+		"framework/exclude.rs": nil,
+	}
+
+	ctx := testRustVndkFs(t, "", mockFS)
+
+	// Test an include and exclude framework module.
+	cc.AssertExcludeFromVendorSnapshotIs(t, ctx, "libinclude", false, sharedVendorVariant)
+	cc.AssertExcludeFromVendorSnapshotIs(t, ctx, "libexclude", true, sharedVendorVariant)
+	cc.AssertExcludeFromVendorSnapshotIs(t, ctx, "libavailable_exclude", true, sharedVendorVariant)
+
+	cc.AssertExcludeFromVendorSnapshotIs(t, ctx, "librust_include", false, rlibVendorVariant)
+	cc.AssertExcludeFromVendorSnapshotIs(t, ctx, "librust_exclude", true, rlibVendorVariant)
+	cc.AssertExcludeFromVendorSnapshotIs(t, ctx, "librust_available_exclude", true, rlibVendorVariant)
+
+	// Verify the content of the vendor snapshot.
+
+	snapshotDir := "vendor-snapshot"
+	snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
+	snapshotSingleton := ctx.SingletonForTests("vendor-snapshot")
+
+	var includeJsonFiles []string
+	var excludeJsonFiles []string
+
+	for _, arch := range [][]string{
+		[]string{"arm64", "armv8-a"},
+		[]string{"arm", "armv7-a-neon"},
+	} {
+		archType := arch[0]
+		archVariant := arch[1]
+		archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
+
+		sharedVariant := fmt.Sprintf("android_vendor.29_%s_%s_shared", archType, archVariant)
+		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
+		rlibVariant := fmt.Sprintf("android_vendor.29_%s_%s_rlib_rlib-std", archType, archVariant)
+		rlibDir := filepath.Join(snapshotVariantPath, archDir, "rlib")
+
+		// Included modules
+		cc.CheckSnapshot(t, ctx, snapshotSingleton, "libinclude", "libinclude.so", sharedDir, sharedVariant)
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(sharedDir, "libinclude.so.json"))
+		cc.CheckSnapshot(t, ctx, snapshotSingleton, "librust_include", "librust_include.rlib", rlibDir, rlibVariant)
+		includeJsonFiles = append(includeJsonFiles, filepath.Join(rlibDir, "librust_include.rlib.json"))
+
+		// Excluded modules
+		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "libexclude", "libexclude.so", sharedDir, sharedVariant)
+		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libexclude.so.json"))
+		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "libavailable_exclude", "libavailable_exclude.so", sharedDir, sharedVariant)
+		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(sharedDir, "libavailable_exclude.so.json"))
+		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "librust_exclude", "librust_exclude.rlib", rlibDir, rlibVariant)
+		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(rlibDir, "librust_exclude.rlib.json"))
+		cc.CheckSnapshotExclude(t, ctx, snapshotSingleton, "librust_available_exclude", "librust_available_exclude.rlib", rlibDir, rlibVariant)
+		excludeJsonFiles = append(excludeJsonFiles, filepath.Join(rlibDir, "librust_available_exclude.rlib.json"))
+	}
+
+	// Verify that each json file for an included module has a rule.
+	for _, jsonFile := range includeJsonFiles {
+		if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
+			t.Errorf("include json file %q not found", jsonFile)
+		}
+	}
+
+	// Verify that each json file for an excluded module has no rule.
+	for _, jsonFile := range excludeJsonFiles {
+		if snapshotSingleton.MaybeOutput(jsonFile).Rule != nil {
+			t.Errorf("exclude json file %q found", jsonFile)
+		}
+	}
+}
+
+func TestVendorSnapshotUse(t *testing.T) {
+	frameworkBp := `
+	cc_library {
+		name: "libvndk",
+		vendor_available: true,
+		product_available: true,
+		vndk: {
+			enabled: true,
+		},
+		nocrt: true,
+	}
+
+	cc_library {
+		name: "libvendor",
+		vendor: true,
+		nocrt: true,
+		no_libcrt: true,
+		stl: "none",
+		system_shared_libs: [],
+	}
+
+	cc_library {
+		name: "libvendor_available",
+		vendor_available: true,
+		nocrt: true,
+		no_libcrt: true,
+		stl: "none",
+		system_shared_libs: [],
+	}
+
+	cc_library {
+		name: "lib32",
+		vendor: true,
+		nocrt: true,
+		no_libcrt: true,
+		stl: "none",
+		system_shared_libs: [],
+		compile_multilib: "32",
+	}
+
+	cc_library {
+		name: "lib64",
+		vendor: true,
+		nocrt: true,
+		no_libcrt: true,
+		stl: "none",
+		system_shared_libs: [],
+		compile_multilib: "64",
+	}
+
+	rust_binary {
+		name: "bin",
+		vendor: true,
+		srcs: ["bin.rs"],
+	}
+
+	rust_binary {
+		name: "bin32",
+		vendor: true,
+		compile_multilib: "32",
+		srcs: ["bin.rs"],
+	}
+`
+
+	vndkBp := `
+	vndk_prebuilt_shared {
+		name: "libvndk",
+		version: "30",
+		target_arch: "arm64",
+		vendor_available: true,
+		product_available: true,
+		vndk: {
+			enabled: true,
+		},
+		arch: {
+			arm64: {
+				srcs: ["libvndk.so"],
+				export_include_dirs: ["include/libvndk"],
+			},
+			arm: {
+				srcs: ["libvndk.so"],
+				export_include_dirs: ["include/libvndk"],
+			},
+		},
+	}
+
+	// old snapshot module which has to be ignored
+	vndk_prebuilt_shared {
+		name: "libvndk",
+		version: "26",
+		target_arch: "arm64",
+		vendor_available: true,
+		product_available: true,
+		vndk: {
+			enabled: true,
+		},
+		arch: {
+			arm64: {
+				srcs: ["libvndk.so"],
+				export_include_dirs: ["include/libvndk"],
+			},
+			arm: {
+				srcs: ["libvndk.so"],
+				export_include_dirs: ["include/libvndk"],
+			},
+		},
+	}
+
+	// different arch snapshot which has to be ignored
+	vndk_prebuilt_shared {
+		name: "libvndk",
+		version: "30",
+		target_arch: "arm",
+		vendor_available: true,
+		product_available: true,
+		vndk: {
+			enabled: true,
+		},
+		arch: {
+			arm: {
+				srcs: ["libvndk.so"],
+				export_include_dirs: ["include/libvndk"],
+			},
+		},
+	}
+`
+
+	vendorProprietaryBp := `
+	cc_library {
+		name: "libvendor_without_snapshot",
+		vendor: true,
+		nocrt: true,
+		no_libcrt: true,
+		stl: "none",
+		system_shared_libs: [],
+	}
+
+	rust_library {
+		name: "librust_vendor_available",
+		crate_name: "rust_vendor",
+		vendor_available: true,
+		srcs: ["client.rs"],
+	}
+
+	rust_ffi_shared {
+		name: "libclient",
+		crate_name: "client",
+		vendor: true,
+		shared_libs: ["libvndk", "libvendor_available"],
+		static_libs: ["libvendor", "libvendor_without_snapshot"],
+		rustlibs: ["librust_vendor_available"],
+		arch: {
+			arm64: {
+				shared_libs: ["lib64"],
+			},
+			arm: {
+				shared_libs: ["lib32"],
+			},
+		},
+		srcs: ["client.rs"],
+	}
+
+	rust_library_rlib {
+		name: "libclient_rust",
+		crate_name: "client_rust",
+		vendor: true,
+		shared_libs: ["libvndk", "libvendor_available"],
+		static_libs: ["libvendor", "libvendor_without_snapshot"],
+		rustlibs: ["librust_vendor_available"],
+		arch: {
+			arm64: {
+				shared_libs: ["lib64"],
+			},
+			arm: {
+				shared_libs: ["lib32"],
+			},
+		},
+		srcs: ["client.rs"],
+	}
+
+	rust_binary {
+		name: "bin_without_snapshot",
+		vendor: true,
+		static_libs: ["libvndk"],
+		srcs: ["bin.rs"],
+		rustlibs: ["librust_vendor_available"],
+	}
+
+	vendor_snapshot {
+		name: "vendor_snapshot",
+		version: "30",
+		arch: {
+			arm64: {
+				vndk_libs: [
+					"libvndk",
+				],
+				static_libs: [
+					"libvendor",
+					"libvndk",
+					"libclang_rt.builtins-aarch64-android",
+				],
+				shared_libs: [
+					"libvendor_available",
+					"lib64",
+				],
+				rlibs: [
+					"libstd",
+					"librust_vendor_available",
+				],
+				binaries: [
+					"bin",
+				],
+                objects: [
+				    "crtend_so",
+					"crtbegin_so",
+					"crtbegin_dynamic",
+					"crtend_android"
+				],
+			},
+			arm: {
+				vndk_libs: [
+					"libvndk",
+				],
+				static_libs: [
+					"libvendor",
+					"libvndk",
+					"libclang_rt.builtins-arm-android",
+				],
+				shared_libs: [
+					"libvendor_available",
+					"lib32",
+				],
+				rlibs: [
+					"libstd",
+					"librust_vendor_available",
+				],
+				binaries: [
+					"bin32",
+				],
+                objects: [
+				    "crtend_so",
+					"crtbegin_so",
+					"crtbegin_dynamic",
+					"crtend_android"
+				],
+
+			},
+		}
+	}
+
+	vendor_snapshot_object {
+		name: "crtend_so",
+		version: "30",
+		target_arch: "arm64",
+		vendor: true,
+		stl: "none",
+		crt: true,
+		arch: {
+			arm64: {
+				src: "crtend_so.o",
+			},
+			arm: {
+				src: "crtend_so.o",
+			},
+		},
+	}
+
+	vendor_snapshot_object {
+		name: "crtbegin_so",
+		version: "30",
+		target_arch: "arm64",
+		vendor: true,
+		stl: "none",
+		crt: true,
+		arch: {
+			arm64: {
+				src: "crtbegin_so.o",
+			},
+			arm: {
+				src: "crtbegin_so.o",
+			},
+		},
+	}
+
+	vendor_snapshot_rlib {
+		name: "libstd",
+		version: "30",
+		target_arch: "arm64",
+		vendor: true,
+		sysroot: true,
+		arch: {
+			arm64: {
+				src: "libstd.rlib",
+			},
+			arm: {
+				src: "libstd.rlib",
+			},
+		},
+	}
+
+	vendor_snapshot_rlib {
+		name: "librust_vendor_available",
+		version: "30",
+		target_arch: "arm64",
+		vendor: true,
+		arch: {
+			arm64: {
+				src: "librust_vendor_available.rlib",
+			},
+			arm: {
+				src: "librust_vendor_available.rlib",
+			},
+		},
+	}
+
+	vendor_snapshot_object {
+		name: "crtend_android",
+		version: "30",
+		target_arch: "arm64",
+		vendor: true,
+		stl: "none",
+		crt: true,
+		arch: {
+			arm64: {
+				src: "crtend_so.o",
+			},
+			arm: {
+				src: "crtend_so.o",
+			},
+		},
+	}
+
+	vendor_snapshot_object {
+		name: "crtbegin_dynamic",
+		version: "30",
+		target_arch: "arm64",
+		vendor: true,
+		stl: "none",
+		crt: true,
+		arch: {
+			arm64: {
+				src: "crtbegin_so.o",
+			},
+			arm: {
+				src: "crtbegin_so.o",
+			},
+		},
+	}
+
+	vendor_snapshot_static {
+		name: "libvndk",
+		version: "30",
+		target_arch: "arm64",
+		compile_multilib: "both",
+		vendor: true,
+		arch: {
+			arm64: {
+				src: "libvndk.a",
+			},
+			arm: {
+				src: "libvndk.a",
+			},
+		},
+		shared_libs: ["libvndk"],
+		export_shared_lib_headers: ["libvndk"],
+	}
+
+	vendor_snapshot_static {
+		name: "libclang_rt.builtins-aarch64-android",
+		version: "30",
+		target_arch: "arm64",
+		vendor: true,
+		arch: {
+			arm64: {
+				src: "libclang_rt.builtins-aarch64-android.a",
+			},
+		},
+    }
+
+    vendor_snapshot_static {
+		name: "libclang_rt.builtins-arm-android",
+		version: "30",
+		target_arch: "arm64",
+		vendor: true,
+		arch: {
+			arm: {
+				src: "libclang_rt.builtins-arm-android.a",
+			},
+		},
+    }
+
+	vendor_snapshot_shared {
+		name: "lib32",
+		version: "30",
+		target_arch: "arm64",
+		compile_multilib: "32",
+		vendor: true,
+		arch: {
+			arm: {
+				src: "lib32.so",
+			},
+		},
+	}
+
+	vendor_snapshot_shared {
+		name: "lib64",
+		version: "30",
+		target_arch: "arm64",
+		compile_multilib: "64",
+		vendor: true,
+		arch: {
+			arm64: {
+				src: "lib64.so",
+			},
+		},
+	}
+	vendor_snapshot_shared {
+		name: "liblog",
+		version: "30",
+		target_arch: "arm64",
+		compile_multilib: "64",
+		vendor: true,
+		arch: {
+			arm64: {
+				src: "liblog.so",
+			},
+		},
+	}
+
+	vendor_snapshot_static {
+		name: "libvendor",
+		version: "30",
+		target_arch: "arm64",
+		compile_multilib: "both",
+		vendor: true,
+		arch: {
+			arm64: {
+				src: "libvendor.a",
+				export_include_dirs: ["include/libvendor"],
+			},
+			arm: {
+				src: "libvendor.a",
+				export_include_dirs: ["include/libvendor"],
+			},
+		},
+	}
+
+	vendor_snapshot_shared {
+		name: "libvendor_available",
+		version: "30",
+		target_arch: "arm64",
+		compile_multilib: "both",
+		vendor: true,
+		arch: {
+			arm64: {
+				src: "libvendor_available.so",
+				export_include_dirs: ["include/libvendor"],
+			},
+			arm: {
+				src: "libvendor_available.so",
+				export_include_dirs: ["include/libvendor"],
+			},
+		},
+	}
+
+	vendor_snapshot_binary {
+		name: "bin",
+		version: "30",
+		target_arch: "arm64",
+		compile_multilib: "64",
+		vendor: true,
+		arch: {
+			arm64: {
+				src: "bin",
+			},
+		},
+	}
+
+	vendor_snapshot_binary {
+		name: "bin32",
+		version: "30",
+		target_arch: "arm64",
+		compile_multilib: "32",
+		vendor: true,
+		arch: {
+			arm: {
+				src: "bin32",
+			},
+		},
+	}
+
+	// old snapshot module which has to be ignored
+	vendor_snapshot_binary {
+		name: "bin",
+		version: "26",
+		target_arch: "arm64",
+		compile_multilib: "first",
+		vendor: true,
+		arch: {
+			arm64: {
+				src: "bin",
+			},
+		},
+	}
+
+	// different arch snapshot which has to be ignored
+	vendor_snapshot_binary {
+		name: "bin",
+		version: "30",
+		target_arch: "arm",
+		compile_multilib: "first",
+		vendor: true,
+		arch: {
+			arm64: {
+				src: "bin",
+			},
+		},
+	}
+`
+
+	mockFS := android.MockFS{
+		"framework/Android.bp":                          []byte(frameworkBp),
+		"framework/bin.rs":                              nil,
+		"vendor/Android.bp":                             []byte(vendorProprietaryBp),
+		"vendor/bin":                                    nil,
+		"vendor/bin32":                                  nil,
+		"vendor/bin.rs":                                 nil,
+		"vendor/client.rs":                              nil,
+		"vendor/include/libvndk/a.h":                    nil,
+		"vendor/include/libvendor/b.h":                  nil,
+		"vendor/libvndk.a":                              nil,
+		"vendor/libvendor.a":                            nil,
+		"vendor/libvendor.so":                           nil,
+		"vendor/lib32.so":                               nil,
+		"vendor/lib64.so":                               nil,
+		"vendor/liblog.so":                              nil,
+		"vendor/libstd.rlib":                            nil,
+		"vendor/librust_vendor_available.rlib":          nil,
+		"vendor/crtbegin_so.o":                          nil,
+		"vendor/crtend_so.o":                            nil,
+		"vendor/libclang_rt.builtins-aarch64-android.a": nil,
+		"vendor/libclang_rt.builtins-arm-android.a":     nil,
+		"vndk/Android.bp":                               []byte(vndkBp),
+		"vndk/include/libvndk/a.h":                      nil,
+		"vndk/libvndk.so":                               nil,
+	}
+
+	sharedVariant := "android_vendor.30_arm64_armv8-a_shared"
+	rlibVariant := "android_vendor.30_arm64_armv8-a_rlib_rlib-std"
+	staticVariant := "android_vendor.30_arm64_armv8-a_static"
+	binaryVariant := "android_vendor.30_arm64_armv8-a"
+
+	shared32Variant := "android_vendor.30_arm_armv7-a-neon_shared"
+	binary32Variant := "android_vendor.30_arm_armv7-a-neon"
+
+	ctx := testRustVndkFsVersions(t, "", mockFS, "30", "current", "31")
+
+	// libclient uses libvndk.vndk.30.arm64, libvendor.vendor_static.30.arm64, libvendor_without_snapshot
+	libclientLdFlags := ctx.ModuleForTests("libclient", sharedVariant).Rule("rustc").Args["linkFlags"]
+	for _, input := range [][]string{
+		[]string{sharedVariant, "libvndk.vndk.30.arm64"},
+		[]string{staticVariant, "libvendor.vendor_static.30.arm64"},
+		[]string{staticVariant, "libvendor_without_snapshot"},
+	} {
+		outputPaths := cc.GetOutputPaths(ctx, input[0] /* variant */, []string{input[1]} /* module name */)
+		if !strings.Contains(libclientLdFlags, outputPaths[0].String()) {
+			t.Errorf("libflags for libclient must contain %#v, but was %#v", outputPaths[0], libclientLdFlags)
+		}
+	}
+
+	libclientAndroidMkSharedLibs := ctx.ModuleForTests("libclient", sharedVariant).Module().(*Module).Properties.AndroidMkSharedLibs
+	if g, w := libclientAndroidMkSharedLibs, []string{"libvndk.vendor", "libvendor_available.vendor", "lib64", "liblog.vendor", "libc.vendor", "libm.vendor", "libdl.vendor"}; !reflect.DeepEqual(g, w) {
+		t.Errorf("wanted libclient AndroidMkSharedLibs %q, got %q", w, g)
+	}
+
+	libclientAndroidMkStaticLibs := ctx.ModuleForTests("libclient", sharedVariant).Module().(*Module).Properties.AndroidMkStaticLibs
+	if g, w := libclientAndroidMkStaticLibs, []string{"libvendor", "libvendor_without_snapshot", "libclang_rt.builtins-aarch64-android.vendor"}; !reflect.DeepEqual(g, w) {
+		t.Errorf("wanted libclient AndroidMkStaticLibs %q, got %q", w, g)
+	}
+
+	libclientAndroidMkRlibs := ctx.ModuleForTests("libclient", sharedVariant).Module().(*Module).Properties.AndroidMkRlibs
+	if g, w := libclientAndroidMkRlibs, []string{"librust_vendor_available.vendor_rlib.30.arm64.rlib-std", "libstd.vendor_rlib.30.arm64"}; !reflect.DeepEqual(g, w) {
+		t.Errorf("wanted libclient libclientAndroidMkRlibs %q, got %q", w, g)
+	}
+
+	libclientAndroidMkDylibs := ctx.ModuleForTests("libclient", sharedVariant).Module().(*Module).Properties.AndroidMkDylibs
+	if len(libclientAndroidMkDylibs) > 0 {
+		t.Errorf("wanted libclient libclientAndroidMkDylibs [], got %q", libclientAndroidMkDylibs)
+	}
+
+	libclient32AndroidMkSharedLibs := ctx.ModuleForTests("libclient", shared32Variant).Module().(*Module).Properties.AndroidMkSharedLibs
+	if g, w := libclient32AndroidMkSharedLibs, []string{"libvndk.vendor", "libvendor_available.vendor", "lib32", "liblog.vendor", "libc.vendor", "libm.vendor", "libdl.vendor"}; !reflect.DeepEqual(g, w) {
+		t.Errorf("wanted libclient32 AndroidMkSharedLibs %q, got %q", w, g)
+	}
+
+	libclientRustAndroidMkRlibs := ctx.ModuleForTests("libclient_rust", rlibVariant).Module().(*Module).Properties.AndroidMkRlibs
+	if g, w := libclientRustAndroidMkRlibs, []string{"librust_vendor_available.vendor_rlib.30.arm64.rlib-std", "libstd.vendor_rlib.30.arm64"}; !reflect.DeepEqual(g, w) {
+		t.Errorf("wanted libclient libclientAndroidMkRlibs %q, got %q", w, g)
+	}
+
+	binWithoutSnapshotLdFlags := ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Rule("rustc").Args["linkFlags"]
+	libVndkStaticOutputPaths := cc.GetOutputPaths(ctx, staticVariant, []string{"libvndk.vendor_static.30.arm64"})
+	if !strings.Contains(binWithoutSnapshotLdFlags, libVndkStaticOutputPaths[0].String()) {
+		t.Errorf("libflags for bin_without_snapshot must contain %#v, but was %#v",
+			libVndkStaticOutputPaths[0], binWithoutSnapshotLdFlags)
+	}
+
+	// bin is installed by bin.vendor_binary.30.arm64
+	ctx.ModuleForTests("bin.vendor_binary.30.arm64", binaryVariant).Output("bin")
+
+	// bin32 is installed by bin32.vendor_binary.30.arm64
+	ctx.ModuleForTests("bin32.vendor_binary.30.arm64", binary32Variant).Output("bin32")
+
+	// bin_without_snapshot is installed by bin_without_snapshot
+	ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Output("bin_without_snapshot")
+
+	// libvendor, libvendor_available and bin don't have vendor.30 variant
+	libvendorVariants := ctx.ModuleVariantsForTests("libvendor")
+	if android.InList(sharedVariant, libvendorVariants) {
+		t.Errorf("libvendor must not have variant %#v, but it does", sharedVariant)
+	}
+
+	libvendorAvailableVariants := ctx.ModuleVariantsForTests("libvendor_available")
+	if android.InList(sharedVariant, libvendorAvailableVariants) {
+		t.Errorf("libvendor_available must not have variant %#v, but it does", sharedVariant)
+	}
+
+	binVariants := ctx.ModuleVariantsForTests("bin")
+	if android.InList(binaryVariant, binVariants) {
+		t.Errorf("bin must not have variant %#v, but it does", sharedVariant)
+	}
+}
diff --git a/scripts/Android.bp b/scripts/Android.bp
index 1c02bd0..635be10 100644
--- a/scripts/Android.bp
+++ b/scripts/Android.bp
@@ -265,3 +265,19 @@
         "linker_config_proto",
     ],
 }
+
+python_binary_host {
+    name: "get_clang_version",
+    main: "get_clang_version.py",
+    srcs: [
+        "get_clang_version.py",
+    ],
+    version: {
+        py2: {
+            enabled: false,
+        },
+        py3: {
+            enabled: true,
+        },
+    },
+}
diff --git a/scripts/OWNERS b/scripts/OWNERS
index 2b9c2de..1830a18 100644
--- a/scripts/OWNERS
+++ b/scripts/OWNERS
@@ -1,6 +1,6 @@
 per-file system-clang-format,system-clang-format-2 = enh@google.com,smoreland@google.com
 per-file build-mainline-modules.sh = ngeoffray@google.com,paulduffin@google.com,mast@google.com
 per-file build-aml-prebuilts.sh = ngeoffray@google.com,paulduffin@google.com,mast@google.com
-per-file construct_context.py = ngeoffray@google.com,calin@google.com,mathieuc@google.com,skvadrik@google.com
+per-file construct_context.py = ngeoffray@google.com,calin@google.com,skvadrik@google.com
 per-file conv_linker_config.py = kiyoungkim@google.com, jiyong@google.com, jooyung@google.com
 per-file gen_ndk*.sh = sophiez@google.com, allenhair@google.com
diff --git a/scripts/build-mainline-modules.sh b/scripts/build-mainline-modules.sh
index 7d49492..b05861b 100755
--- a/scripts/build-mainline-modules.sh
+++ b/scripts/build-mainline-modules.sh
@@ -28,7 +28,6 @@
   runtime-module-host-exports
   runtime-module-sdk
   statsd-module-sdk
-  statsd-module-sdk-for-art
   tzdata-module-test-exports
 )
 
diff --git a/scripts/build-rustdocs.sh b/scripts/build-rustdocs.sh
new file mode 100755
index 0000000..fda9688
--- /dev/null
+++ b/scripts/build-rustdocs.sh
@@ -0,0 +1,31 @@
+#!/bin/bash -ex
+
+# Copyright 2021 Google Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Builds the platform rustdocs and copies them to the dist directory to provide
+# online docs for each build.
+
+if [ -z "${OUT_DIR}" ]; then
+    echo Must set OUT_DIR
+    exit 1
+fi
+
+source build/envsetup.sh
+m rustdoc
+
+if [ -n "${DIST_DIR}" ]; then
+    mkdir -p ${DIST_DIR}
+    cp ${OUT_DIR}/soong/rustdoc.zip $DIST_DIR
+fi
diff --git a/scripts/construct_context.py b/scripts/construct_context.py
index f0658ba..3f601c3 100755
--- a/scripts/construct_context.py
+++ b/scripts/construct_context.py
@@ -25,57 +25,78 @@
 
 
 def parse_args(args):
-  """Parse commandline arguments."""
-  parser = argparse.ArgumentParser()
-  parser.add_argument('--target-sdk-version', default='', dest='sdk',
-    help='specify target SDK version (as it appears in the manifest)')
-  parser.add_argument('--host-context-for-sdk', dest='host_contexts',
-    action='append', nargs=2, metavar=('sdk','context'),
-    help='specify context on host for a given SDK version or "any" version')
-  parser.add_argument('--target-context-for-sdk', dest='target_contexts',
-    action='append', nargs=2, metavar=('sdk','context'),
-    help='specify context on target for a given SDK version or "any" version')
-  return parser.parse_args(args)
+    """Parse commandline arguments."""
+    parser = argparse.ArgumentParser()
+    parser.add_argument(
+        '--target-sdk-version',
+        default='',
+        dest='sdk',
+        help='specify target SDK version (as it appears in the manifest)')
+    parser.add_argument(
+        '--host-context-for-sdk',
+        dest='host_contexts',
+        action='append',
+        nargs=2,
+        metavar=('sdk', 'context'),
+        help='specify context on host for a given SDK version or "any" version')
+    parser.add_argument(
+        '--target-context-for-sdk',
+        dest='target_contexts',
+        action='append',
+        nargs=2,
+        metavar=('sdk', 'context'),
+        help='specify context on target for a given SDK version or "any" '
+        'version'
+    )
+    return parser.parse_args(args)
+
 
 # Special keyword that means that the context should be added to class loader
 # context regardless of the target SDK version.
 any_sdk = 'any'
 
+
 # We assume that the order of context arguments passed to this script is
 # correct (matches the order computed by package manager). It is possible to
 # sort them here, but Soong needs to use deterministic order anyway, so it can
 # as well use the correct order.
 def construct_context(versioned_contexts, target_sdk):
-  context = []
-  for [sdk, ctx] in versioned_contexts:
-    if sdk == any_sdk or compare_version_gt(sdk, target_sdk):
-      context.append(ctx)
-  return context
+    context = []
+    for [sdk, ctx] in versioned_contexts:
+        if sdk == any_sdk or compare_version_gt(sdk, target_sdk):
+            context.append(ctx)
+    return context
+
 
 def construct_contexts(args):
-  host_context = construct_context(args.host_contexts, args.sdk)
-  target_context = construct_context(args.target_contexts, args.sdk)
-  context_sep = '#'
-  return ('class_loader_context_arg=--class-loader-context=PCL[]{%s} ; ' % context_sep.join(host_context) +
-    'stored_class_loader_context_arg=--stored-class-loader-context=PCL[]{%s}' % context_sep.join(target_context))
+    host_context = construct_context(args.host_contexts, args.sdk)
+    target_context = construct_context(args.target_contexts, args.sdk)
+    context_sep = '#'
+    return (
+        'class_loader_context_arg=--class-loader-context=PCL[]{%s} ; ' %
+        context_sep.join(host_context) +
+        'stored_class_loader_context_arg=--stored-class-loader-context=PCL[]{%s}' #pylint: disable=line-too-long
+        % context_sep.join(target_context))
+
 
 def main():
-  """Program entry point."""
-  try:
-    args = parse_args(sys.argv[1:])
-    if not args.sdk:
-      raise SystemExit('target sdk version is not set')
-    if not args.host_contexts:
-      args.host_contexts = []
-    if not args.target_contexts:
-      args.target_contexts = []
+    """Program entry point."""
+    try:
+        args = parse_args(sys.argv[1:])
+        if not args.sdk:
+            raise SystemExit('target sdk version is not set')
+        if not args.host_contexts:
+            args.host_contexts = []
+        if not args.target_contexts:
+            args.target_contexts = []
 
-    print(construct_contexts(args))
+        print(construct_contexts(args))
 
-  # pylint: disable=broad-except
-  except Exception as err:
-    print('error: ' + str(err), file=sys.stderr)
-    sys.exit(-1)
+    # pylint: disable=broad-except
+    except Exception as err:
+        print('error: ' + str(err), file=sys.stderr)
+        sys.exit(-1)
+
 
 if __name__ == '__main__':
-  main()
+    main()
diff --git a/scripts/construct_context_test.py b/scripts/construct_context_test.py
index 3b05f90..2ff5ac5 100755
--- a/scripts/construct_context_test.py
+++ b/scripts/construct_context_test.py
@@ -23,53 +23,63 @@
 
 sys.dont_write_bytecode = True
 
+
 def construct_contexts(arglist):
-  args = cc.parse_args(arglist)
-  return cc.construct_contexts(args)
+    args = cc.parse_args(arglist)
+    return cc.construct_contexts(args)
+
 
 contexts = [
-  '--host-context-for-sdk', '28', 'PCL[out/zdir/z.jar]',
-  '--target-context-for-sdk', '28', 'PCL[/system/z.jar]',
-  '--host-context-for-sdk', '29', 'PCL[out/xdir/x.jar]#PCL[out/ydir/y.jar]',
-  '--target-context-for-sdk', '29', 'PCL[/system/x.jar]#PCL[/product/y.jar]',
-  '--host-context-for-sdk', 'any', 'PCL[out/adir/a.jar]#PCL[out/bdir/b.jar]',
-  '--target-context-for-sdk', 'any', 'PCL[/system/a.jar]#PCL[/product/b.jar]',
+    '--host-context-for-sdk',
+    '28',
+    'PCL[out/zdir/z.jar]',
+    '--target-context-for-sdk',
+    '28',
+    'PCL[/system/z.jar]',
+    '--host-context-for-sdk',
+    '29',
+    'PCL[out/xdir/x.jar]#PCL[out/ydir/y.jar]',
+    '--target-context-for-sdk',
+    '29',
+    'PCL[/system/x.jar]#PCL[/product/y.jar]',
+    '--host-context-for-sdk',
+    'any',
+    'PCL[out/adir/a.jar]#PCL[out/bdir/b.jar]',
+    '--target-context-for-sdk',
+    'any',
+    'PCL[/system/a.jar]#PCL[/product/b.jar]',
 ]
 
+#pylint: disable=line-too-long
 class ConstructContextTest(unittest.TestCase):
-  def test_construct_context_28(self):
-    args = ['--target-sdk-version', '28'] + contexts
-    result = construct_contexts(args)
-    expect = ('class_loader_context_arg=--class-loader-context=PCL[]{PCL[out/xdir/x.jar]'
-      '#PCL[out/ydir/y.jar]'
-      '#PCL[out/adir/a.jar]'
-      '#PCL[out/bdir/b.jar]}'
-      ' ; '
-      'stored_class_loader_context_arg=--stored-class-loader-context=PCL[]{PCL[/system/x.jar]'
-      '#PCL[/product/y.jar]'
-      '#PCL[/system/a.jar]'
-      '#PCL[/product/b.jar]}')
-    self.assertEqual(result, expect)
 
-  def test_construct_context_29(self):
-    args = ['--target-sdk-version', '29'] + contexts
-    result = construct_contexts(args)
-    expect = ('class_loader_context_arg=--class-loader-context=PCL[]{PCL[out/adir/a.jar]'
-      '#PCL[out/bdir/b.jar]}'
-      ' ; '
-      'stored_class_loader_context_arg=--stored-class-loader-context=PCL[]{PCL[/system/a.jar]'
-      '#PCL[/product/b.jar]}')
-    self.assertEqual(result, expect)
+    def test_construct_context_28(self):
+        args = ['--target-sdk-version', '28'] + contexts
+        result = construct_contexts(args)
+        expect = (
+            'class_loader_context_arg=--class-loader-context=PCL[]{PCL[out/xdir/x.jar]#PCL[out/ydir/y.jar]#PCL[out/adir/a.jar]#PCL[out/bdir/b.jar]}'
+            ' ; '
+            'stored_class_loader_context_arg=--stored-class-loader-context=PCL[]{PCL[/system/x.jar]#PCL[/product/y.jar]#PCL[/system/a.jar]#PCL[/product/b.jar]}')
+        self.assertEqual(result, expect)
 
-  def test_construct_context_S(self):
-    args = ['--target-sdk-version', 'S'] + contexts
-    result = construct_contexts(args)
-    expect = ('class_loader_context_arg=--class-loader-context=PCL[]{PCL[out/adir/a.jar]'
-      '#PCL[out/bdir/b.jar]}'
-      ' ; '
-      'stored_class_loader_context_arg=--stored-class-loader-context=PCL[]{PCL[/system/a.jar]'
-      '#PCL[/product/b.jar]}')
-    self.assertEqual(result, expect)
+    def test_construct_context_29(self):
+        args = ['--target-sdk-version', '29'] + contexts
+        result = construct_contexts(args)
+        expect = (
+            'class_loader_context_arg=--class-loader-context=PCL[]{PCL[out/adir/a.jar]#PCL[out/bdir/b.jar]}'
+            ' ; '
+            'stored_class_loader_context_arg=--stored-class-loader-context=PCL[]{PCL[/system/a.jar]#PCL[/product/b.jar]}')
+        self.assertEqual(result, expect)
+
+    def test_construct_context_S(self):
+        args = ['--target-sdk-version', 'S'] + contexts
+        result = construct_contexts(args)
+        expect = (
+            'class_loader_context_arg=--class-loader-context=PCL[]{PCL[out/adir/a.jar]#PCL[out/bdir/b.jar]}'
+            ' ; '
+            'stored_class_loader_context_arg=--stored-class-loader-context=PCL[]{PCL[/system/a.jar]#PCL[/product/b.jar]}')
+        self.assertEqual(result, expect)
+#pylint: enable=line-too-long
 
 if __name__ == '__main__':
-  unittest.main(verbosity=2)
+    unittest.main(verbosity=2)
diff --git a/scripts/conv_linker_config.py b/scripts/conv_linker_config.py
index 92f79da..e46efe4 100644
--- a/scripts/conv_linker_config.py
+++ b/scripts/conv_linker_config.py
@@ -20,178 +20,181 @@
 import json
 import os
 
-import linker_config_pb2
+import linker_config_pb2 #pylint: disable=import-error
 from google.protobuf.descriptor import FieldDescriptor
 from google.protobuf.json_format import ParseDict
 from google.protobuf.text_format import MessageToString
 
 
 def Proto(args):
-  json_content = ''
-  with open(args.source) as f:
-    for line in f:
-      if not line.lstrip().startswith('//'):
-        json_content += line
-  obj = json.loads(json_content, object_pairs_hook=collections.OrderedDict)
-  pb = ParseDict(obj, linker_config_pb2.LinkerConfig())
-  with open(args.output, 'wb') as f:
-    f.write(pb.SerializeToString())
+    json_content = ''
+    with open(args.source) as f:
+        for line in f:
+            if not line.lstrip().startswith('//'):
+                json_content += line
+    obj = json.loads(json_content, object_pairs_hook=collections.OrderedDict)
+    pb = ParseDict(obj, linker_config_pb2.LinkerConfig())
+    with open(args.output, 'wb') as f:
+        f.write(pb.SerializeToString())
 
 
 def Print(args):
-  with open(args.source, 'rb') as f:
-    pb = linker_config_pb2.LinkerConfig()
-    pb.ParseFromString(f.read())
-  print(MessageToString(pb))
+    with open(args.source, 'rb') as f:
+        pb = linker_config_pb2.LinkerConfig()
+        pb.ParseFromString(f.read())
+    print(MessageToString(pb))
 
 
 def SystemProvide(args):
-  pb = linker_config_pb2.LinkerConfig()
-  with open(args.source, 'rb') as f:
-    pb.ParseFromString(f.read())
-  libraries = args.value.split()
+    pb = linker_config_pb2.LinkerConfig()
+    with open(args.source, 'rb') as f:
+        pb.ParseFromString(f.read())
+    libraries = args.value.split()
 
-  def IsInLibPath(lib_name):
-    lib_path = os.path.join(args.system, 'lib', lib_name)
-    lib64_path = os.path.join(args.system, 'lib64', lib_name)
-    return os.path.exists(lib_path) or os.path.islink(lib_path) or os.path.exists(lib64_path) or os.path.islink(lib64_path)
+    def IsInLibPath(lib_name):
+        lib_path = os.path.join(args.system, 'lib', lib_name)
+        lib64_path = os.path.join(args.system, 'lib64', lib_name)
+        return os.path.exists(lib_path) or os.path.islink(
+            lib_path) or os.path.exists(lib64_path) or os.path.islink(
+                lib64_path)
 
-  installed_libraries = list(filter(IsInLibPath, libraries))
-  for item in installed_libraries:
-    if item not in getattr(pb, 'provideLibs'):
-      getattr(pb, 'provideLibs').append(item)
-  with open(args.output, 'wb') as f:
-    f.write(pb.SerializeToString())
+    installed_libraries = [lib for lib in libraries if IsInLibPath(lib)]
+    for item in installed_libraries:
+        if item not in getattr(pb, 'provideLibs'):
+            getattr(pb, 'provideLibs').append(item)
+    with open(args.output, 'wb') as f:
+        f.write(pb.SerializeToString())
 
 
 def Append(args):
-  pb = linker_config_pb2.LinkerConfig()
-  with open(args.source, 'rb') as f:
-    pb.ParseFromString(f.read())
+    pb = linker_config_pb2.LinkerConfig()
+    with open(args.source, 'rb') as f:
+        pb.ParseFromString(f.read())
 
-  if getattr(type(pb), args.key).DESCRIPTOR.label == FieldDescriptor.LABEL_REPEATED:
-    for value in args.value.split():
-      getattr(pb, args.key).append(value)
-  else:
-    setattr(pb, args.key, args.value)
+    if getattr(type(pb),
+               args.key).DESCRIPTOR.label == FieldDescriptor.LABEL_REPEATED:
+        for value in args.value.split():
+            getattr(pb, args.key).append(value)
+    else:
+        setattr(pb, args.key, args.value)
 
-  with open(args.output, 'wb') as f:
-    f.write(pb.SerializeToString())
+    with open(args.output, 'wb') as f:
+        f.write(pb.SerializeToString())
+
 
 def Merge(args):
-  pb = linker_config_pb2.LinkerConfig()
-  for other in args.input:
-    with open(other, 'rb') as f:
-      pb.MergeFromString(f.read())
+    pb = linker_config_pb2.LinkerConfig()
+    for other in args.input:
+        with open(other, 'rb') as f:
+            pb.MergeFromString(f.read())
 
-  with open(args.out, 'wb') as f:
-    f.write(pb.SerializeToString())
+    with open(args.out, 'wb') as f:
+        f.write(pb.SerializeToString())
+
 
 def GetArgParser():
-  parser = argparse.ArgumentParser()
-  subparsers = parser.add_subparsers()
+    parser = argparse.ArgumentParser()
+    subparsers = parser.add_subparsers()
 
-  parser_proto = subparsers.add_parser(
-      'proto', help='Convert the input JSON configuration file into protobuf.')
-  parser_proto.add_argument(
-      '-s',
-      '--source',
-      required=True,
-      type=str,
-      help='Source linker configuration file in JSON.')
-  parser_proto.add_argument(
-      '-o',
-      '--output',
-      required=True,
-      type=str,
-      help='Target path to create protobuf file.')
-  parser_proto.set_defaults(func=Proto)
+    parser_proto = subparsers.add_parser(
+        'proto',
+        help='Convert the input JSON configuration file into protobuf.')
+    parser_proto.add_argument(
+        '-s',
+        '--source',
+        required=True,
+        type=str,
+        help='Source linker configuration file in JSON.')
+    parser_proto.add_argument(
+        '-o',
+        '--output',
+        required=True,
+        type=str,
+        help='Target path to create protobuf file.')
+    parser_proto.set_defaults(func=Proto)
 
-  print_proto = subparsers.add_parser(
-      'print', help='Print configuration in human-readable text format.')
-  print_proto.add_argument(
-      '-s',
-      '--source',
-      required=True,
-      type=str,
-      help='Source linker configuration file in protobuf.')
-  print_proto.set_defaults(func=Print)
+    print_proto = subparsers.add_parser(
+        'print', help='Print configuration in human-readable text format.')
+    print_proto.add_argument(
+        '-s',
+        '--source',
+        required=True,
+        type=str,
+        help='Source linker configuration file in protobuf.')
+    print_proto.set_defaults(func=Print)
 
-  system_provide_libs = subparsers.add_parser(
-      'systemprovide', help='Append system provide libraries into the configuration.')
-  system_provide_libs.add_argument(
-      '-s',
-      '--source',
-      required=True,
-      type=str,
-      help='Source linker configuration file in protobuf.')
-  system_provide_libs.add_argument(
-      '-o',
-      '--output',
-      required=True,
-      type=str,
-      help='Target linker configuration file to write in protobuf.')
-  system_provide_libs.add_argument(
-      '--value',
-      required=True,
-      type=str,
-      help='Values of the libraries to append. If there are more than one it should be separated by empty space')
-  system_provide_libs.add_argument(
-      '--system',
-      required=True,
-      type=str,
-      help='Path of the system image.')
-  system_provide_libs.set_defaults(func=SystemProvide)
+    system_provide_libs = subparsers.add_parser(
+        'systemprovide',
+        help='Append system provide libraries into the configuration.')
+    system_provide_libs.add_argument(
+        '-s',
+        '--source',
+        required=True,
+        type=str,
+        help='Source linker configuration file in protobuf.')
+    system_provide_libs.add_argument(
+        '-o',
+        '--output',
+        required=True,
+        type=str,
+        help='Target linker configuration file to write in protobuf.')
+    system_provide_libs.add_argument(
+        '--value',
+        required=True,
+        type=str,
+        help='Values of the libraries to append. If there are more than one '
+        'it should be separated by empty space'
+    )
+    system_provide_libs.add_argument(
+        '--system', required=True, type=str, help='Path of the system image.')
+    system_provide_libs.set_defaults(func=SystemProvide)
 
-  append = subparsers.add_parser(
-      'append', help='Append value(s) to given key.')
-  append.add_argument(
-      '-s',
-      '--source',
-      required=True,
-      type=str,
-      help='Source linker configuration file in protobuf.')
-  append.add_argument(
-      '-o',
-      '--output',
-      required=True,
-      type=str,
-      help='Target linker configuration file to write in protobuf.')
-  append.add_argument(
-      '--key',
-      required=True,
-      type=str,
-      help='.')
-  append.add_argument(
-      '--value',
-      required=True,
-      type=str,
-      help='Values of the libraries to append. If there are more than one it should be separated by empty space')
-  append.set_defaults(func=Append)
+    append = subparsers.add_parser(
+        'append', help='Append value(s) to given key.')
+    append.add_argument(
+        '-s',
+        '--source',
+        required=True,
+        type=str,
+        help='Source linker configuration file in protobuf.')
+    append.add_argument(
+        '-o',
+        '--output',
+        required=True,
+        type=str,
+        help='Target linker configuration file to write in protobuf.')
+    append.add_argument('--key', required=True, type=str, help='.')
+    append.add_argument(
+        '--value',
+        required=True,
+        type=str,
+        help='Values of the libraries to append. If there are more than one'
+        'it should be separated by empty space'
+    )
+    append.set_defaults(func=Append)
 
-  append = subparsers.add_parser(
-      'merge', help='Merge configurations')
-  append.add_argument(
-      '-o',
-      '--out',
-      required=True,
-      type=str,
-      help='Ouptut linker configuration file to write in protobuf.')
-  append.add_argument(
-      '-i',
-      '--input',
-      nargs='+',
-      type=str,
-      help='Linker configuration files to merge.')
-  append.set_defaults(func=Merge)
+    append = subparsers.add_parser('merge', help='Merge configurations')
+    append.add_argument(
+        '-o',
+        '--out',
+        required=True,
+        type=str,
+        help='Output linker configuration file to write in protobuf.')
+    append.add_argument(
+        '-i',
+        '--input',
+        nargs='+',
+        type=str,
+        help='Linker configuration files to merge.')
+    append.set_defaults(func=Merge)
 
-  return parser
+    return parser
 
 
 def main():
-  args = GetArgParser().parse_args()
-  args.func(args)
+    args = GetArgParser().parse_args()
+    args.func(args)
 
 
 if __name__ == '__main__':
-  main()
+    main()
diff --git a/scripts/gen_sorted_bss_symbols.sh b/scripts/gen_sorted_bss_symbols.sh
deleted file mode 100755
index a9b61a1..0000000
--- a/scripts/gen_sorted_bss_symbols.sh
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/bin/bash -e
-
-# Copyright 2019 Google Inc. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-# Script to generate a symbol ordering file that sorts bss section symbols by
-# their sizes.
-# Inputs:
-#  Environment:
-#   CLANG_BIN: path to the clang bin directory
-#  Arguments:
-#   $1: Input ELF file
-#   $2: Output symbol ordering file
-
-set -o pipefail
-
-${CLANG_BIN}/llvm-nm --size-sort $1 | awk '{if ($2 == "b" || $2 == "B") print $3}' > $2
diff --git a/scripts/get_clang_version.py b/scripts/get_clang_version.py
new file mode 100755
index 0000000..17bc88b
--- /dev/null
+++ b/scripts/get_clang_version.py
@@ -0,0 +1,46 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""A tool to report the current clang version used during build"""
+
+import os
+import re
+import sys
+
+
+ANDROID_BUILD_TOP = os.environ.get("ANDROID_BUILD_TOP", ".")
+
+def get_clang_prebuilts_version(global_go):
+  # TODO(b/187231324): Get clang version from the json file once it is no longer
+  # hard-coded in global.go
+  if global_go is None:
+      global_go = ANDROID_BUILD_TOP + '/build/soong/cc/config/global.go'
+  with open(global_go) as infile:
+    contents = infile.read()
+
+  regex_rev = r'\tClangDefaultVersion\s+= "(?P<rev>clang-r\d+[a-z]?\d?)"'
+  match_rev = re.search(regex_rev, contents)
+  if match_rev is None:
+    raise RuntimeError('Parsing clang info failed')
+  return match_rev.group('rev')
+
+
+def main():
+  global_go = sys.argv[1] if len(sys.argv) > 1 else None
+  print(get_clang_prebuilts_version(global_go));
+
+
+if __name__ == '__main__':
+  main()
diff --git a/scripts/get_clang_version_test.py b/scripts/get_clang_version_test.py
new file mode 100644
index 0000000..e57df6c
--- /dev/null
+++ b/scripts/get_clang_version_test.py
@@ -0,0 +1,32 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""Unit tests for get_clang_version.py."""
+
+import unittest
+
+import get_clang_version
+
+class GetClangVersionTest(unittest.TestCase):
+  """Unit tests for get_clang_version."""
+
+  def test_get_clang_version(self):
+    """Test parsing of clang prebuilts version."""
+    self.assertIsNotNone(get_clang_version.get_clang_prebuilts_version())
+
+
+if __name__ == '__main__':
+  unittest.main(verbosity=2)
diff --git a/scripts/hiddenapi/Android.bp b/scripts/hiddenapi/Android.bp
index 7472f52..7ffda62 100644
--- a/scripts/hiddenapi/Android.bp
+++ b/scripts/hiddenapi/Android.bp
@@ -48,6 +48,27 @@
     },
 }
 
+python_test_host {
+    name: "generate_hiddenapi_lists_test",
+    main: "generate_hiddenapi_lists_test.py",
+    srcs: [
+        "generate_hiddenapi_lists.py",
+        "generate_hiddenapi_lists_test.py",
+    ],
+    version: {
+        py2: {
+            enabled: false,
+        },
+        py3: {
+            enabled: true,
+            embedded_launcher: true,
+        },
+    },
+    test_options: {
+        unit_test: true,
+    },
+}
+
 python_binary_host {
     name: "verify_overlaps",
     main: "verify_overlaps.py",
@@ -62,3 +83,60 @@
         },
     },
 }
+
+python_test_host {
+    name: "verify_overlaps_test",
+    main: "verify_overlaps_test.py",
+    srcs: [
+        "verify_overlaps.py",
+        "verify_overlaps_test.py",
+    ],
+    version: {
+        py2: {
+            enabled: false,
+        },
+        py3: {
+            enabled: true,
+            embedded_launcher: true,
+        },
+    },
+    test_options: {
+        unit_test: true,
+    },
+}
+
+python_binary_host {
+    name: "signature_patterns",
+    main: "signature_patterns.py",
+    srcs: ["signature_patterns.py"],
+    version: {
+        py2: {
+            enabled: false,
+        },
+        py3: {
+            enabled: true,
+            embedded_launcher: true,
+        },
+    },
+}
+
+python_test_host {
+    name: "signature_patterns_test",
+    main: "signature_patterns_test.py",
+    srcs: [
+        "signature_patterns.py",
+        "signature_patterns_test.py",
+    ],
+    version: {
+        py2: {
+            enabled: false,
+        },
+        py3: {
+            enabled: true,
+            embedded_launcher: true,
+        },
+    },
+    test_options: {
+        unit_test: true,
+    },
+}
diff --git a/scripts/hiddenapi/generate_hiddenapi_lists.py b/scripts/hiddenapi/generate_hiddenapi_lists.py
index 5ab93d1..35e0948 100755
--- a/scripts/hiddenapi/generate_hiddenapi_lists.py
+++ b/scripts/hiddenapi/generate_hiddenapi_lists.py
@@ -16,8 +16,6 @@
 """Generate API lists for non-SDK API enforcement."""
 import argparse
 from collections import defaultdict, namedtuple
-import functools
-import os
 import re
 import sys
 
@@ -60,15 +58,15 @@
 # For example, the max-target-P list is checked in as it was in P,
 # but signatures have changes since then. The flag instructs this
 # script to skip any entries which do not exist any more.
-FLAG_IGNORE_CONFLICTS = "ignore-conflicts"
+FLAG_IGNORE_CONFLICTS = 'ignore-conflicts'
 
 # Option specified after one of FLAGS_API_LIST to express that all
 # apis within a given set of packages should be assign the given flag.
-FLAG_PACKAGES = "packages"
+FLAG_PACKAGES = 'packages'
 
 # Option specified after one of FLAGS_API_LIST to indicate an extra
 # tag that should be added to the matching APIs.
-FLAG_TAG = "tag"
+FLAG_TAG = 'tag'
 
 # Regex patterns of fields/methods used in serialization. These are
 # considered public API despite being hidden.
@@ -84,24 +82,30 @@
 
 # Single regex used to match serialization API. It combines all the
 # SERIALIZATION_PATTERNS into a single regular expression.
-SERIALIZATION_REGEX = re.compile(r'.*->(' + '|'.join(SERIALIZATION_PATTERNS) + r')$')
+SERIALIZATION_REGEX = re.compile(r'.*->(' + '|'.join(SERIALIZATION_PATTERNS) +
+                                 r')$')
 
 # Predicates to be used with filter_apis.
-HAS_NO_API_LIST_ASSIGNED = lambda api, flags: not FLAGS_API_LIST_SET.intersection(flags)
+HAS_NO_API_LIST_ASSIGNED = \
+    lambda api,flags: not FLAGS_API_LIST_SET.intersection(flags)
+
 IS_SERIALIZATION = lambda api, flags: SERIALIZATION_REGEX.match(api)
 
 
 class StoreOrderedOptions(argparse.Action):
-    """An argparse action that stores a number of option arguments in the order that
-    they were specified.
+    """An argparse action that stores a number of option arguments in the order
+
+    that they were specified.
     """
-    def __call__(self, parser, args, values, option_string = None):
+
+    def __call__(self, parser, args, values, option_string=None):
         items = getattr(args, self.dest, None)
         if items is None:
             items = []
         items.append([option_string.lstrip('-'), values])
         setattr(args, self.dest, items)
 
+
 def get_args():
     """Parses command line arguments.
 
@@ -110,22 +114,43 @@
     """
     parser = argparse.ArgumentParser()
     parser.add_argument('--output', required=True)
-    parser.add_argument('--csv', nargs='*', default=[], metavar='CSV_FILE',
+    parser.add_argument(
+        '--csv',
+        nargs='*',
+        default=[],
+        metavar='CSV_FILE',
         help='CSV files to be merged into output')
 
     for flag in ALL_FLAGS:
-        parser.add_argument('--' + flag, dest='ordered_flags', metavar='TXT_FILE',
-            action=StoreOrderedOptions, help='lists of entries with flag "' + flag + '"')
-    parser.add_argument('--' + FLAG_IGNORE_CONFLICTS, dest='ordered_flags', nargs=0,
-        action=StoreOrderedOptions, help='Indicates that only known and otherwise unassigned '
-        'entries should be assign the given flag. Must follow a list of entries and applies '
-        'to the preceding such list.')
-    parser.add_argument('--' + FLAG_PACKAGES, dest='ordered_flags', nargs=0,
-        action=StoreOrderedOptions, help='Indicates that the previous list of entries '
-        'is a list of packages. All members in those packages will be given the flag. '
-        'Must follow a list of entries and applies to the preceding such list.')
-    parser.add_argument('--' + FLAG_TAG, dest='ordered_flags', nargs=1,
-        action=StoreOrderedOptions, help='Adds an extra tag to the previous list of entries. '
+        parser.add_argument(
+            '--' + flag,
+            dest='ordered_flags',
+            metavar='TXT_FILE',
+            action=StoreOrderedOptions,
+            help='lists of entries with flag "' + flag + '"')
+    parser.add_argument(
+        '--' + FLAG_IGNORE_CONFLICTS,
+        dest='ordered_flags',
+        nargs=0,
+        action=StoreOrderedOptions,
+        help='Indicates that only known and otherwise unassigned '
+        'entries should be assign the given flag. Must follow a list of '
+        'entries and applies to the preceding such list.')
+    parser.add_argument(
+        '--' + FLAG_PACKAGES,
+        dest='ordered_flags',
+        nargs=0,
+        action=StoreOrderedOptions,
+        help='Indicates that the previous list of entries '
+        'is a list of packages. All members in those packages will be given '
+        'the flag. Must follow a list of entries and applies to the preceding '
+        'such list.')
+    parser.add_argument(
+        '--' + FLAG_TAG,
+        dest='ordered_flags',
+        nargs=1,
+        action=StoreOrderedOptions,
+        help='Adds an extra tag to the previous list of entries. '
         'Must follow a list of entries and applies to the preceding such list.')
 
     return parser.parse_args()
@@ -143,9 +168,9 @@
         Lines of the file as a list of string.
     """
     with open(filename, 'r') as f:
-        lines = f.readlines();
-    lines = filter(lambda line: not line.startswith('#'), lines)
-    lines = map(lambda line: line.strip(), lines)
+        lines = f.readlines()
+    lines = [line for line in lines if not line.startswith('#')]
+    lines = [line.strip() for line in lines]
     return set(lines)
 
 
@@ -156,7 +181,7 @@
         filename (string): Path to the file to be writing into.
         lines (list): List of strings to write into the file.
     """
-    lines = map(lambda line: line + '\n', lines)
+    lines = [line + '\n' for line in lines]
     with open(filename, 'w') as f:
         f.writelines(lines)
 
@@ -170,17 +195,19 @@
     Returns:
         The package name of the class containing the field/method.
     """
-    full_class_name = signature.split(";->")[0]
+    full_class_name = signature.split(';->')[0]
     # Example: Landroid/hardware/radio/V1_2/IRadio$Proxy
-    if (full_class_name[0] != "L"):
-        raise ValueError("Expected to start with 'L': %s" % full_class_name)
+    if full_class_name[0] != 'L':
+        raise ValueError("Expected to start with 'L': %s"
+                         % full_class_name)
     full_class_name = full_class_name[1:]
     # If full_class_name doesn't contain '/', then package_name will be ''.
-    package_name = full_class_name.rpartition("/")[0]
+    package_name = full_class_name.rpartition('/')[0]
     return package_name.replace('/', '.')
 
 
 class FlagsDict:
+
     def __init__(self):
         self._dict_keyset = set()
         self._dict = defaultdict(set)
@@ -188,37 +215,43 @@
     def _check_entries_set(self, keys_subset, source):
         assert isinstance(keys_subset, set)
         assert keys_subset.issubset(self._dict_keyset), (
-            "Error: {} specifies signatures not present in code:\n"
-            "{}"
-            "Please visit go/hiddenapi for more information.").format(
-                source, "".join(map(lambda x: "  " + str(x) + "\n", keys_subset - self._dict_keyset)))
+            'Error: {} specifies signatures not present in code:\n'
+            '{}'
+            'Please visit go/hiddenapi for more information.').format(
+                source, ''.join(
+                    ['  ' + str(x) + '\n' for x in
+                     keys_subset - self._dict_keyset]))
 
     def _check_flags_set(self, flags_subset, source):
         assert isinstance(flags_subset, set)
         assert flags_subset.issubset(ALL_FLAGS_SET), (
-            "Error processing: {}\n"
-            "The following flags were not recognized: \n"
-            "{}\n"
-            "Please visit go/hiddenapi for more information.").format(
-                source, "\n".join(flags_subset - ALL_FLAGS_SET))
+            'Error processing: {}\n'
+            'The following flags were not recognized: \n'
+            '{}\n'
+            'Please visit go/hiddenapi for more information.').format(
+                source, '\n'.join(flags_subset - ALL_FLAGS_SET))
 
     def filter_apis(self, filter_fn):
         """Returns APIs which match a given predicate.
 
-        This is a helper function which allows to filter on both signatures (keys) and
-        flags (values). The built-in filter() invokes the lambda only with dict's keys.
+        This is a helper function which allows to filter on both signatures
+        (keys) and
+        flags (values). The built-in filter() invokes the lambda only with
+        dict's keys.
 
         Args:
-            filter_fn : Function which takes two arguments (signature/flags) and returns a boolean.
+            filter_fn : Function which takes two arguments (signature/flags) and
+              returns a boolean.
 
         Returns:
             A set of APIs which match the predicate.
         """
-        return set(filter(lambda x: filter_fn(x, self._dict[x]), self._dict_keyset))
+        return {x for x in self._dict_keyset if filter_fn(x, self._dict[x])}
 
     def get_valid_subset_of_unassigned_apis(self, api_subset):
-        """Sanitizes a key set input to only include keys which exist in the dictionary
-        and have not been assigned any API list flags.
+        """Sanitizes a key set input to only include keys which exist in the
+
+        dictionary and have not been assigned any API list flags.
 
         Args:
             entries_subset (set/list): Key set to be sanitized.
@@ -227,7 +260,8 @@
             Sanitized key set.
         """
         assert isinstance(api_subset, set)
-        return api_subset.intersection(self.filter_apis(HAS_NO_API_LIST_ASSIGNED))
+        return api_subset.intersection(
+            self.filter_apis(HAS_NO_API_LIST_ASSIGNED))
 
     def generate_csv(self):
         """Constructs CSV entries from a dictionary.
@@ -235,15 +269,16 @@
         Old versions of flags are used to generate the file.
 
         Returns:
-            List of lines comprising a CSV file. See "parse_and_merge_csv" for format description.
+            List of lines comprising a CSV file. See "parse_and_merge_csv" for
+            format description.
         """
         lines = []
         for api in self._dict:
-          flags = sorted(self._dict[api])
-          lines.append(",".join([api] + flags))
+            flags = sorted(self._dict[api])
+            lines.append(','.join([api] + flags))
         return sorted(lines)
 
-    def parse_and_merge_csv(self, csv_lines, source = "<unknown>"):
+    def parse_and_merge_csv(self, csv_lines, source='<unknown>'):
         """Parses CSV entries and merges them into a given dictionary.
 
         The expected CSV format is:
@@ -251,21 +286,20 @@
 
         Args:
             csv_lines (list of strings): Lines read from a CSV file.
-            source (string): Origin of `csv_lines`. Will be printed in error messages.
-
-        Throws:
-            AssertionError if parsed flags are invalid.
+            source (string): Origin of `csv_lines`. Will be printed in error
+              messages.
+        Throws: AssertionError if parsed flags are invalid.
         """
         # Split CSV lines into arrays of values.
-        csv_values = [ line.split(',') for line in csv_lines ]
+        csv_values = [line.split(',') for line in csv_lines]
 
         # Update the full set of API signatures.
-        self._dict_keyset.update([ csv[0] for csv in csv_values ])
+        self._dict_keyset.update([csv[0] for csv in csv_values])
 
         # Check that all flags are known.
         csv_flags = set()
         for csv in csv_values:
-          csv_flags.update(csv[1:])
+            csv_flags.update(csv[1:])
         self._check_flags_set(csv_flags, source)
 
         # Iterate over all CSV lines, find entry in dict and append flags to it.
@@ -275,47 +309,53 @@
                 flags.append(FLAG_SDK)
             self._dict[csv[0]].update(flags)
 
-    def assign_flag(self, flag, apis, source="<unknown>", tag = None):
+    def assign_flag(self, flag, apis, source='<unknown>', tag=None):
         """Assigns a flag to given subset of entries.
 
         Args:
             flag (string): One of ALL_FLAGS.
             apis (set): Subset of APIs to receive the flag.
-            source (string): Origin of `entries_subset`. Will be printed in error messages.
-
-        Throws:
-            AssertionError if parsed API signatures of flags are invalid.
+            source (string): Origin of `entries_subset`. Will be printed in
+              error messages.
+        Throws: AssertionError if parsed API signatures of flags are invalid.
         """
         # Check that all APIs exist in the dict.
         self._check_entries_set(apis, source)
 
         # Check that the flag is known.
-        self._check_flags_set(set([ flag ]), source)
+        self._check_flags_set(set([flag]), source)
 
-        # Iterate over the API subset, find each entry in dict and assign the flag to it.
+        # Iterate over the API subset, find each entry in dict and assign the
+        # flag to it.
         for api in apis:
             self._dict[api].add(flag)
             if tag:
                 self._dict[api].add(tag)
 
 
-FlagFile = namedtuple('FlagFile', ('flag', 'file', 'ignore_conflicts', 'packages', 'tag'))
+FlagFile = namedtuple('FlagFile',
+                      ('flag', 'file', 'ignore_conflicts', 'packages', 'tag'))
+
 
 def parse_ordered_flags(ordered_flags):
     r = []
-    currentflag, file, ignore_conflicts, packages, tag = None, None, False, False, None
+    currentflag, file, ignore_conflicts, packages, tag = None, None, False, \
+        False, None
     for flag_value in ordered_flags:
         flag, value = flag_value[0], flag_value[1]
         if flag in ALL_FLAGS_SET:
             if currentflag:
-                r.append(FlagFile(currentflag, file, ignore_conflicts, packages, tag))
+                r.append(
+                    FlagFile(currentflag, file, ignore_conflicts, packages,
+                             tag))
                 ignore_conflicts, packages, tag = False, False, None
             currentflag = flag
             file = value
         else:
             if currentflag is None:
-                raise argparse.ArgumentError('--%s is only allowed after one of %s' % (
-                    flag, ' '.join(['--%s' % f for f in ALL_FLAGS_SET])))
+                raise argparse.ArgumentError( #pylint: disable=no-value-for-parameter
+                    '--%s is only allowed after one of %s' %
+                    (flag, ' '.join(['--%s' % f for f in ALL_FLAGS_SET])))
             if flag == FLAG_IGNORE_CONFLICTS:
                 ignore_conflicts = True
             elif flag == FLAG_PACKAGES:
@@ -323,13 +363,12 @@
             elif flag == FLAG_TAG:
                 tag = value[0]
 
-
     if currentflag:
         r.append(FlagFile(currentflag, file, ignore_conflicts, packages, tag))
     return r
 
 
-def main(argv):
+def main(argv): #pylint: disable=unused-argument
     # Parse arguments.
     args = vars(get_args())
     flagfiles = parse_ordered_flags(args['ordered_flags'] or [])
@@ -342,7 +381,7 @@
     # contain the full set of APIs. Subsequent additions from text files
     # will be able to detect invalid entries, and/or filter all as-yet
     # unassigned entries.
-    for filename in args["csv"]:
+    for filename in args['csv']:
         flags.parse_and_merge_csv(read_lines(filename), filename)
 
     # Combine inputs which do not require any particular order.
@@ -352,24 +391,28 @@
     # (2) Merge text files with a known flag into the dictionary.
     for info in flagfiles:
         if (not info.ignore_conflicts) and (not info.packages):
-            flags.assign_flag(info.flag, read_lines(info.file), info.file, info.tag)
+            flags.assign_flag(info.flag, read_lines(info.file), info.file,
+                              info.tag)
 
     # Merge text files where conflicts should be ignored.
     # This will only assign the given flag if:
     # (a) the entry exists, and
     # (b) it has not been assigned any other flag.
-    # Because of (b), this must run after all strict assignments have been performed.
+    # Because of (b), this must run after all strict assignments have been
+    # performed.
     for info in flagfiles:
         if info.ignore_conflicts:
-            valid_entries = flags.get_valid_subset_of_unassigned_apis(read_lines(info.file))
-            flags.assign_flag(info.flag, valid_entries, filename, info.tag)
+            valid_entries = flags.get_valid_subset_of_unassigned_apis(
+                read_lines(info.file))
+            flags.assign_flag(info.flag, valid_entries, filename, info.tag) #pylint: disable=undefined-loop-variable
 
-    # All members in the specified packages will be assigned the appropriate flag.
+    # All members in the specified packages will be assigned the appropriate
+    # flag.
     for info in flagfiles:
         if info.packages:
             packages_needing_list = set(read_lines(info.file))
-            should_add_signature_to_list = lambda sig,lists: extract_package(
-                sig) in packages_needing_list and not lists
+            should_add_signature_to_list = lambda sig, lists: extract_package(
+                sig) in packages_needing_list and not lists #pylint: disable=cell-var-from-loop
             valid_entries = flags.filter_apis(should_add_signature_to_list)
             flags.assign_flag(info.flag, valid_entries, info.file, info.tag)
 
@@ -377,7 +420,8 @@
     flags.assign_flag(FLAG_BLOCKED, flags.filter_apis(HAS_NO_API_LIST_ASSIGNED))
 
     # Write output.
-    write_lines(args["output"], flags.generate_csv())
+    write_lines(args['output'], flags.generate_csv())
 
-if __name__ == "__main__":
+
+if __name__ == '__main__':
     main(sys.argv)
diff --git a/scripts/hiddenapi/generate_hiddenapi_lists_test.py b/scripts/hiddenapi/generate_hiddenapi_lists_test.py
index ff3d708..204de97 100755
--- a/scripts/hiddenapi/generate_hiddenapi_lists_test.py
+++ b/scripts/hiddenapi/generate_hiddenapi_lists_test.py
@@ -15,34 +15,39 @@
 # limitations under the License.
 """Unit tests for Hidden API list generation."""
 import unittest
-from generate_hiddenapi_lists import *
+from generate_hiddenapi_lists import *  # pylint: disable=wildcard-import,unused-wildcard-import
+
 
 class TestHiddenapiListGeneration(unittest.TestCase):
-
     def test_filter_apis(self):
         # Initialize flags so that A and B are put on the allow list and
         # C, D, E are left unassigned. Try filtering for the unassigned ones.
         flags = FlagsDict()
-        flags.parse_and_merge_csv(['A,' + FLAG_SDK, 'B,' + FLAG_SDK,
-                        'C', 'D', 'E'])
+        flags.parse_and_merge_csv(
+            ['A,' + FLAG_SDK, 'B,' + FLAG_SDK, 'C', 'D', 'E']
+        )
         filter_set = flags.filter_apis(lambda api, flags: not flags)
         self.assertTrue(isinstance(filter_set, set))
-        self.assertEqual(filter_set, set([ 'C', 'D', 'E' ]))
+        self.assertEqual(filter_set, set(['C', 'D', 'E']))
 
     def test_get_valid_subset_of_unassigned_keys(self):
         # Create flags where only A is unassigned.
         flags = FlagsDict()
         flags.parse_and_merge_csv(['A,' + FLAG_SDK, 'B', 'C'])
         flags.assign_flag(FLAG_UNSUPPORTED, set(['C']))
-        self.assertEqual(flags.generate_csv(),
-            [ 'A,' + FLAG_SDK, 'B', 'C,' + FLAG_UNSUPPORTED ])
+        self.assertEqual(
+            flags.generate_csv(),
+            ['A,' + FLAG_SDK, 'B', 'C,' + FLAG_UNSUPPORTED],
+        )
 
         # Check three things:
         # (1) B is selected as valid unassigned
         # (2) A is not selected because it is assigned to the allow list
         # (3) D is not selected because it is not a valid key
         self.assertEqual(
-            flags.get_valid_subset_of_unassigned_apis(set(['A', 'B', 'D'])), set([ 'B' ]))
+            flags.get_valid_subset_of_unassigned_apis(set(['A', 'B', 'D'])),
+            set(['B']),
+        )
 
     def test_parse_and_merge_csv(self):
         flags = FlagsDict()
@@ -51,41 +56,48 @@
         self.assertEqual(flags.generate_csv(), [])
 
         # Test new additions.
-        flags.parse_and_merge_csv([
-            'A,' + FLAG_UNSUPPORTED,
-            'B,' + FLAG_BLOCKED + ',' + FLAG_MAX_TARGET_O,
-            'C,' + FLAG_SDK + ',' + FLAG_SYSTEM_API,
-            'D,' + FLAG_UNSUPPORTED + ',' + FLAG_TEST_API,
-            'E,' + FLAG_BLOCKED + ',' + FLAG_TEST_API,
-        ])
-        self.assertEqual(flags.generate_csv(), [
-            'A,' + FLAG_UNSUPPORTED,
-            'B,' + FLAG_BLOCKED + "," + FLAG_MAX_TARGET_O,
-            'C,' + FLAG_SDK + ',' + FLAG_SYSTEM_API,
-            'D,' + FLAG_TEST_API + ',' + FLAG_UNSUPPORTED,
-            'E,' + FLAG_BLOCKED + ',' + FLAG_TEST_API,
-        ])
+        flags.parse_and_merge_csv(
+            [
+                'A,' + FLAG_UNSUPPORTED,
+                'B,' + FLAG_BLOCKED + ',' + FLAG_MAX_TARGET_O,
+                'C,' + FLAG_SDK + ',' + FLAG_SYSTEM_API,
+                'D,' + FLAG_UNSUPPORTED + ',' + FLAG_TEST_API,
+                'E,' + FLAG_BLOCKED + ',' + FLAG_TEST_API,
+            ]
+        )
+        self.assertEqual(
+            flags.generate_csv(),
+            [
+                'A,' + FLAG_UNSUPPORTED,
+                'B,' + FLAG_BLOCKED + "," + FLAG_MAX_TARGET_O,
+                'C,' + FLAG_SDK + ',' + FLAG_SYSTEM_API,
+                'D,' + FLAG_TEST_API + ',' + FLAG_UNSUPPORTED,
+                'E,' + FLAG_BLOCKED + ',' + FLAG_TEST_API,
+            ],
+        )
 
         # Test unknown flag.
         with self.assertRaises(AssertionError):
-            flags.parse_and_merge_csv([ 'Z,foo' ])
+            flags.parse_and_merge_csv(['Z,foo'])
 
     def test_assign_flag(self):
         flags = FlagsDict()
         flags.parse_and_merge_csv(['A,' + FLAG_SDK, 'B'])
 
         # Test new additions.
-        flags.assign_flag(FLAG_UNSUPPORTED, set([ 'A', 'B' ]))
-        self.assertEqual(flags.generate_csv(),
-            [ 'A,' + FLAG_SDK + "," + FLAG_UNSUPPORTED, 'B,' + FLAG_UNSUPPORTED ])
+        flags.assign_flag(FLAG_UNSUPPORTED, set(['A', 'B']))
+        self.assertEqual(
+            flags.generate_csv(),
+            ['A,' + FLAG_SDK + "," + FLAG_UNSUPPORTED, 'B,' + FLAG_UNSUPPORTED],
+        )
 
         # Test invalid API signature.
         with self.assertRaises(AssertionError):
-            flags.assign_flag(FLAG_SDK, set([ 'C' ]))
+            flags.assign_flag(FLAG_SDK, set(['C']))
 
         # Test invalid flag.
         with self.assertRaises(AssertionError):
-            flags.assign_flag('foo', set([ 'A' ]))
+            flags.assign_flag('foo', set(['A']))
 
     def test_extract_package(self):
         signature = 'Lcom/foo/bar/Baz;->method1()Lcom/bar/Baz;'
@@ -100,5 +112,6 @@
         expected_package = 'com.foo_bar.baz'
         self.assertEqual(extract_package(signature), expected_package)
 
+
 if __name__ == '__main__':
-    unittest.main()
+    unittest.main(verbosity=2)
diff --git a/scripts/hiddenapi/merge_csv.py b/scripts/hiddenapi/merge_csv.py
index a65326c..c17ec25 100755
--- a/scripts/hiddenapi/merge_csv.py
+++ b/scripts/hiddenapi/merge_csv.py
@@ -13,8 +13,7 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-"""
-Merge multiple CSV files, possibly with different columns.
+"""Merge multiple CSV files, possibly with different columns.
 """
 
 import argparse
@@ -26,34 +25,52 @@
 
 from zipfile import ZipFile
 
-args_parser = argparse.ArgumentParser(description='Merge given CSV files into a single one.')
-args_parser.add_argument('--header', help='Comma separated field names; '
-                                          'if missing determines the header from input files.')
-args_parser.add_argument('--zip_input', help='Treat files as ZIP archives containing CSV files to merge.',
-                         action="store_true")
-args_parser.add_argument('--key_field', help='The name of the field by which the rows should be sorted. '
-                                             'Must be in the field names. '
-                                             'Will be the first field in the output. '
-                                             'All input files must be sorted by that field.')
-args_parser.add_argument('--output', help='Output file for merged CSV.',
-                         default='-', type=argparse.FileType('w'))
+args_parser = argparse.ArgumentParser(
+    description='Merge given CSV files into a single one.'
+)
+args_parser.add_argument(
+    '--header',
+    help='Comma separated field names; '
+    'if missing determines the header from input files.',
+)
+args_parser.add_argument(
+    '--zip_input',
+    help='Treat files as ZIP archives containing CSV files to merge.',
+    action="store_true",
+)
+args_parser.add_argument(
+    '--key_field',
+    help='The name of the field by which the rows should be sorted. '
+    'Must be in the field names. '
+    'Will be the first field in the output. '
+    'All input files must be sorted by that field.',
+)
+args_parser.add_argument(
+    '--output',
+    help='Output file for merged CSV.',
+    default='-',
+    type=argparse.FileType('w'),
+)
 args_parser.add_argument('files', nargs=argparse.REMAINDER)
 args = args_parser.parse_args()
 
 
-def dict_reader(input):
-    return csv.DictReader(input, delimiter=',', quotechar='|')
+def dict_reader(csvfile):
+    return csv.DictReader(csvfile, delimiter=',', quotechar='|')
+
 
 csv_readers = []
-if not(args.zip_input):
+if not args.zip_input:
     for file in args.files:
         csv_readers.append(dict_reader(open(file, 'r')))
 else:
     for file in args.files:
-        with ZipFile(file) as zip:
-            for entry in zip.namelist():
+        with ZipFile(file) as zipfile:
+            for entry in zipfile.namelist():
                 if entry.endswith('.uau'):
-                    csv_readers.append(dict_reader(io.TextIOWrapper(zip.open(entry, 'r'))))
+                    csv_readers.append(
+                        dict_reader(io.TextIOWrapper(zipfile.open(entry, 'r')))
+                    )
 
 if args.header:
     fieldnames = args.header.split(',')
@@ -73,8 +90,8 @@
     keyField = args.key_field
     if keyField:
         assert keyField in fieldnames, (
-            "--key_field {} not found, must be one of {}\n").format(
-            keyField, ",".join(fieldnames))
+            "--key_field {} not found, must be one of {}\n"
+        ).format(keyField, ",".join(fieldnames))
         # Make the key field the first field in the output
         keyFieldIndex = fieldnames.index(args.key_field)
         fieldnames.insert(0, fieldnames.pop(keyFieldIndex))
@@ -83,11 +100,17 @@
         all_rows = heapq.merge(*csv_readers, key=operator.itemgetter(keyField))
 
 # Write all rows from the input files to the output:
-writer = csv.DictWriter(args.output, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL,
-                        dialect='unix', fieldnames=fieldnames)
+writer = csv.DictWriter(
+    args.output,
+    delimiter=',',
+    quotechar='|',
+    quoting=csv.QUOTE_MINIMAL,
+    dialect='unix',
+    fieldnames=fieldnames,
+)
 writer.writeheader()
 
 # Read all the rows from the input and write them to the output in the correct
 # order:
 for row in all_rows:
-  writer.writerow(row)
+    writer.writerow(row)
diff --git a/scripts/hiddenapi/signature_patterns.py b/scripts/hiddenapi/signature_patterns.py
new file mode 100755
index 0000000..0acb2a0
--- /dev/null
+++ b/scripts/hiddenapi/signature_patterns.py
@@ -0,0 +1,79 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Generate a set of signature patterns from the modular flags generated by a
+bootclasspath_fragment that can be used to select a subset of monolithic flags
+against which the modular flags can be compared.
+"""
+
+import argparse
+import csv
+import sys
+
+def dict_reader(csvfile):
+    return csv.DictReader(
+        csvfile, delimiter=',', quotechar='|', fieldnames=['signature']
+    )
+
+
+def produce_patterns_from_file(file):
+    with open(file, 'r') as f:
+        return produce_patterns_from_stream(f)
+
+
+def produce_patterns_from_stream(stream):
+    # Read in all the signatures into a list and remove member names.
+    patterns = set()
+    for row in dict_reader(stream):
+        signature = row['signature']
+        text = signature.removeprefix("L")
+        # Remove the class specific member signature
+        pieces = text.split(";->")
+        qualifiedClassName = pieces[0]
+        # Remove inner class names as they cannot be separated
+        # from the containing outer class.
+        pieces = qualifiedClassName.split("$", maxsplit=1)
+        pattern = pieces[0]
+        patterns.add(pattern)
+
+    patterns = list(patterns) #pylint: disable=redefined-variable-type
+    patterns.sort()
+    return patterns
+
+
+def main(args):
+    args_parser = argparse.ArgumentParser(
+        description='Generate a set of signature patterns '
+        'that select a subset of monolithic hidden API files.'
+    )
+    args_parser.add_argument(
+        '--flags',
+        help='The stub flags file which contains an entry for every dex member',
+    )
+    args_parser.add_argument('--output', help='Generated signature prefixes')
+    args = args_parser.parse_args(args)
+
+    # Read in all the patterns into a list.
+    patterns = produce_patterns_from_file(args.flags)
+
+    # Write out all the patterns.
+    with open(args.output, 'w') as outputFile:
+        for pattern in patterns:
+            outputFile.write(pattern)
+            outputFile.write("\n")
+
+
+if __name__ == "__main__":
+    main(sys.argv[1:])
diff --git a/scripts/hiddenapi/signature_patterns_test.py b/scripts/hiddenapi/signature_patterns_test.py
new file mode 100755
index 0000000..3babe54
--- /dev/null
+++ b/scripts/hiddenapi/signature_patterns_test.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Unit tests for signature_patterns.py."""
+import io
+import unittest
+
+from signature_patterns import * #pylint: disable=unused-wildcard-import,wildcard-import
+
+
+class TestGeneratedPatterns(unittest.TestCase):
+    def produce_patterns_from_string(self, csvdata):
+        with io.StringIO(csvdata) as f:
+            return produce_patterns_from_stream(f)
+
+    def test_generate(self):
+        #pylint: disable=line-too-long
+        patterns = self.produce_patterns_from_string(
+            '''
+Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V,blocked
+Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;,public-api
+Ljava/lang/Object;->hashCode()I,public-api,system-api,test-api
+Ljava/lang/Object;->toString()Ljava/lang/String;,blocked
+'''
+        )
+        #pylint: enable=line-too-long
+        expected = [
+            "java/lang/Character",
+            "java/lang/Object",
+            "java/lang/ProcessBuilder",
+        ]
+        self.assertEqual(expected, patterns)
+
+
+if __name__ == '__main__':
+    unittest.main(verbosity=2)
diff --git a/scripts/hiddenapi/verify_overlaps.py b/scripts/hiddenapi/verify_overlaps.py
index bb0917e..4cd7e63 100755
--- a/scripts/hiddenapi/verify_overlaps.py
+++ b/scripts/hiddenapi/verify_overlaps.py
@@ -13,57 +13,398 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-"""
-Verify that one set of hidden API flags is a subset of another.
+"""Verify that one set of hidden API flags is a subset of another.
 """
 
 import argparse
 import csv
+import sys
+from itertools import chain
 
-args_parser = argparse.ArgumentParser(description='Verify that one set of hidden API flags is a subset of another.')
-args_parser.add_argument('all', help='All the flags')
-args_parser.add_argument('subsets', nargs=argparse.REMAINDER, help='Subsets of the flags')
-args = args_parser.parse_args()
+#pylint: disable=line-too-long
+class InteriorNode:
+    """An interior node in a trie.
+
+    Each interior node has a dict that maps from an element of a signature to
+    either another interior node or a leaf. Each interior node represents either
+    a package, class or nested class. Class members are represented by a Leaf.
+
+    Associating the set of flags [public-api] with the signature
+    "Ljava/lang/Object;->String()Ljava/lang/String;" will cause the following
+    nodes to be created:
+    Node()
+    ^- package:java -> Node()
+       ^- package:lang -> Node()
+           ^- class:Object -> Node()
+              ^- member:String()Ljava/lang/String; -> Leaf([public-api])
+
+    Associating the set of flags [blocked,core-platform-api] with the signature
+    "Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;"
+    will cause the following nodes to be created:
+    Node()
+    ^- package:java -> Node()
+       ^- package:lang -> Node()
+           ^- class:Character -> Node()
+              ^- class:UnicodeScript -> Node()
+                 ^- member:of(I)Ljava/lang/Character$UnicodeScript;
+                    -> Leaf([blocked,core-platform-api])
+
+    Attributes:
+        nodes: a dict from an element of the signature to the Node/Leaf
+          containing the next element/value.
+    """
+    #pylint: enable=line-too-long
+
+    def __init__(self):
+        self.nodes = {}
+
+    #pylint: disable=line-too-long
+    def signatureToElements(self, signature):
+        """Split a signature or a prefix into a number of elements:
+        1. The packages (excluding the leading L preceding the first package).
+        2. The class names, from outermost to innermost.
+        3. The member signature.
+        e.g.
+        Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;
+        will be broken down into these elements:
+        1. package:java
+        2. package:lang
+        3. class:Character
+        4. class:UnicodeScript
+        5. member:of(I)Ljava/lang/Character$UnicodeScript;
+        """
+        # Remove the leading L.
+        #  - java/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;
+        text = signature.removeprefix("L")
+        # Split the signature between qualified class name and the class member
+        # signature.
+        #  0 - java/lang/Character$UnicodeScript
+        #  1 - of(I)Ljava/lang/Character$UnicodeScript;
+        parts = text.split(";->")
+        member = parts[1:]
+        # Split the qualified class name into packages, and class name.
+        #  0 - java
+        #  1 - lang
+        #  2 - Character$UnicodeScript
+        elements = parts[0].split("/")
+        packages = elements[0:-1]
+        className = elements[-1]
+        if className in ("*" , "**"): #pylint: disable=no-else-return
+            # Cannot specify a wildcard and target a specific member
+            if len(member) != 0:
+                raise Exception(
+                    "Invalid signature %s: contains wildcard %s and member " \
+                    "signature %s"
+                    % (signature, className, member[0]))
+            wildcard = [className]
+            # Assemble the parts into a single list, adding prefixes to identify
+            # the different parts.
+            #  0 - package:java
+            #  1 - package:lang
+            #  2 - *
+            return list(
+                chain(["package:" + x for x in packages], wildcard))
+        else:
+            # Split the class name into outer / inner classes
+            #  0 - Character
+            #  1 - UnicodeScript
+            classes = className.split("$")
+            # Assemble the parts into a single list, adding prefixes to identify
+            # the different parts.
+            #  0 - package:java
+            #  1 - package:lang
+            #  2 - class:Character
+            #  3 - class:UnicodeScript
+            #  4 - member:of(I)Ljava/lang/Character$UnicodeScript;
+            return list(
+                chain(
+                    ["package:" + x for x in packages],
+                    ["class:" + x for x in classes],
+                    ["member:" + x for x in member]))
+    #pylint: enable=line-too-long
+
+    def add(self, signature, value):
+        """Associate the value with the specific signature.
+
+        :param signature: the member signature
+        :param value: the value to associated with the signature
+        :return: n/a
+        """
+        # Split the signature into elements.
+        elements = self.signatureToElements(signature)
+        # Find the Node associated with the deepest class.
+        node = self
+        for element in elements[:-1]:
+            if element in node.nodes:
+                node = node.nodes[element]
+            else:
+                next_node = InteriorNode()
+                node.nodes[element] = next_node
+                node = next_node
+        # Add a Leaf containing the value and associate it with the member
+        # signature within the class.
+        lastElement = elements[-1]
+        if not lastElement.startswith("member:"):
+            raise Exception(
+                "Invalid signature: %s, does not identify a specific member" %
+                signature)
+        if lastElement in node.nodes:
+            raise Exception("Duplicate signature: %s" % signature)
+        node.nodes[lastElement] = Leaf(value)
+
+    def getMatchingRows(self, pattern):
+        """Get the values (plural) associated with the pattern.
+
+        e.g. If the pattern is a full signature then this will return a list
+        containing the value associated with that signature.
+
+        If the pattern is a class then this will return a list containing the
+        values associated with all members of that class.
+
+        If the pattern is a package then this will return a list containing the
+        values associated with all the members of all the classes in that
+        package and sub-packages.
+
+        If the pattern ends with "*" then the preceding part is treated as a
+        package and this will return a list containing the values associated
+        with all the members of all the classes in that package.
+
+        If the pattern ends with "**" then the preceding part is treated
+        as a package and this will return a list containing the values
+        associated with all the members of all the classes in that package and
+        all sub-packages.
+
+        :param pattern: the pattern which could be a complete signature or a
+        class, or package wildcard.
+        :return: an iterable containing all the values associated with the
+        pattern.
+        """
+        elements = self.signatureToElements(pattern)
+        node = self
+        # Include all values from this node and all its children.
+        selector = lambda x: True
+        lastElement = elements[-1]
+        if lastElement in ("*", "**"):
+            elements = elements[:-1]
+            if lastElement == "*":
+                # Do not include values from sub-packages.
+                selector = lambda x: not x.startswith("package:")
+        for element in elements:
+            if element in node.nodes:
+                node = node.nodes[element]
+            else:
+                return []
+        return chain.from_iterable(node.values(selector))
+
+    def values(self, selector):
+        """:param selector: a function that can be applied to a key in the nodes
+        attribute to determine whether to return its values.
+
+        :return: A list of iterables of all the values associated with
+        this node and its children.
+        """
+        values = []
+        self.appendValues(values, selector)
+        return values
+
+    def appendValues(self, values, selector):
+        """Append the values associated with this node and its children to the
+        list.
+
+        For each item (key, child) in nodes the child node's values are returned
+        if and only if the selector returns True when called on its key. A child
+        node's values are all the values associated with it and all its
+        descendant nodes.
+
+        :param selector: a function that can be applied to a key in the nodes
+        attribute to determine whether to return its values.
+        :param values: a list of a iterables of values.
+        """
+        for key, node in self.nodes.items():
+            if selector(key):
+                node.appendValues(values, lambda x: True)
 
 
-def dict_reader(input):
-    return csv.DictReader(input, delimiter=',', quotechar='|', fieldnames=['signature'])
+class Leaf:
+    """A leaf of the trie
 
-# Read in all the flags into a dict indexed by signature
-allFlagsBySignature = {}
-with open(args.all, 'r') as allFlagsFile:
-    allFlagsReader = dict_reader(allFlagsFile)
-    for row in allFlagsReader:
-        signature = row['signature']
-        allFlagsBySignature[signature]=row
+    Attributes:
+        value: the value associated with this leaf.
+    """
 
-failed = False
-for subsetPath in args.subsets:
+    def __init__(self, value):
+        self.value = value
+
+    def values(self, selector): #pylint: disable=unused-argument
+        """:return: A list of a list of the value associated with this node.
+        """
+        return [[self.value]]
+
+    def appendValues(self, values, selector): #pylint: disable=unused-argument
+        """Appends a list of the value associated with this node to the list.
+
+        :param values: a list of a iterables of values.
+        """
+        values.append([self.value])
+
+
+def dict_reader(csvfile):
+    return csv.DictReader(
+        csvfile, delimiter=",", quotechar="|", fieldnames=["signature"])
+
+
+def read_flag_trie_from_file(file):
+    with open(file, "r") as stream:
+        return read_flag_trie_from_stream(stream)
+
+
+def read_flag_trie_from_stream(stream):
+    trie = InteriorNode()
+    reader = dict_reader(stream)
+    for row in reader:
+        signature = row["signature"]
+        trie.add(signature, row)
+    return trie
+
+
+def extract_subset_from_monolithic_flags_as_dict_from_file(
+        monolithicTrie, patternsFile):
+    """Extract a subset of flags from the dict containing all the monolithic
+    flags.
+
+    :param monolithicFlagsDict: the dict containing all the monolithic flags.
+    :param patternsFile: a file containing a list of signature patterns that
+    define the subset.
+    :return: the dict from signature to row.
+    """
+    with open(patternsFile, "r") as stream:
+        return extract_subset_from_monolithic_flags_as_dict_from_stream(
+            monolithicTrie, stream)
+
+
+def extract_subset_from_monolithic_flags_as_dict_from_stream(
+        monolithicTrie, stream):
+    """Extract a subset of flags from the trie containing all the monolithic
+    flags.
+
+    :param monolithicTrie: the trie containing all the monolithic flags.
+    :param stream: a stream containing a list of signature patterns that define
+    the subset.
+    :return: the dict from signature to row.
+    """
+    dict_signature_to_row = {}
+    for pattern in stream:
+        pattern = pattern.rstrip()
+        rows = monolithicTrie.getMatchingRows(pattern)
+        for row in rows:
+            signature = row["signature"]
+            dict_signature_to_row[signature] = row
+    return dict_signature_to_row
+
+
+def read_signature_csv_from_stream_as_dict(stream):
+    """Read the csv contents from the stream into a dict. The first column is
+    assumed to be the signature and used as the key.
+
+    The whole row is stored as the value.
+    :param stream: the csv contents to read
+    :return: the dict from signature to row.
+    """
+    dict_signature_to_row = {}
+    reader = dict_reader(stream)
+    for row in reader:
+        signature = row["signature"]
+        dict_signature_to_row[signature] = row
+    return dict_signature_to_row
+
+
+def read_signature_csv_from_file_as_dict(csvFile):
+    """Read the csvFile into a dict. The first column is assumed to be the
+    signature and used as the key.
+
+    The whole row is stored as the value.
+    :param csvFile: the csv file to read
+    :return: the dict from signature to row.
+    """
+    with open(csvFile, "r") as f:
+        return read_signature_csv_from_stream_as_dict(f)
+
+
+def compare_signature_flags(monolithicFlagsDict, modularFlagsDict):
+    """Compare the signature flags between the two dicts.
+
+    :param monolithicFlagsDict: the dict containing the subset of the monolithic
+    flags that should be equal to the modular flags.
+    :param modularFlagsDict:the dict containing the flags produced by a single
+    bootclasspath_fragment module.
+    :return: list of mismatches., each mismatch is a tuple where the first item
+    is the signature, and the second and third items are lists of the flags from
+    modular dict, and monolithic dict respectively.
+    """
     mismatchingSignatures = []
-    with open(subsetPath, 'r') as subsetFlagsFile:
-        subsetReader = dict_reader(subsetFlagsFile)
-        for row in subsetReader:
-            signature = row['signature']
-            if signature in allFlagsBySignature:
-                allFlags = allFlagsBySignature.get(signature)
-                if allFlags != row:
-                    mismatchingSignatures.append((signature, row.get(None, []), allFlags.get(None, [])))
-            else:
-                mismatchingSignatures.append((signature, row.get(None, []), []))
+    # Create a sorted set of all the signatures from both the monolithic and
+    # modular dicts.
+    allSignatures = sorted(
+        set(chain(monolithicFlagsDict.keys(), modularFlagsDict.keys())))
+    for signature in allSignatures:
+        monolithicRow = monolithicFlagsDict.get(signature, {})
+        monolithicFlags = monolithicRow.get(None, [])
+        if signature in modularFlagsDict:
+            modularRow = modularFlagsDict.get(signature, {})
+            modularFlags = modularRow.get(None, [])
+        else:
+            modularFlags = ["blocked"]
+        if monolithicFlags != modularFlags:
+            mismatchingSignatures.append(
+                (signature, modularFlags, monolithicFlags))
+    return mismatchingSignatures
 
 
-    if mismatchingSignatures:
-        failed = True
-        print("ERROR: Hidden API flags are inconsistent:")
-        print("< " + subsetPath)
-        print("> " + args.all)
-        for mismatch in mismatchingSignatures:
-            print()
-            print("< " + mismatch[0] + "," + ",".join(mismatch[1]))
-            if mismatch[2] != []:
-                print("> " + mismatch[0] + "," + ",".join(mismatch[2]))
-            else:
-                print("> " + mismatch[0] + " - missing")
+def main(argv):
+    args_parser = argparse.ArgumentParser(
+        description="Verify that sets of hidden API flags are each a subset of "
+        "the monolithic flag file."
+    )
+    args_parser.add_argument("monolithicFlags", help="The monolithic flag file")
+    args_parser.add_argument(
+        "modularFlags",
+        nargs=argparse.REMAINDER,
+        help="Flags produced by individual bootclasspath_fragment modules")
+    args = args_parser.parse_args(argv[1:])
 
-if failed:
-    sys.exit(1)
+    # Read in all the flags into the trie
+    monolithicFlagsPath = args.monolithicFlags
+    monolithicTrie = read_flag_trie_from_file(monolithicFlagsPath)
+
+    # For each subset specified on the command line, create dicts for the flags
+    # provided by the subset and the corresponding flags from the complete set
+    # of flags and compare them.
+    failed = False
+    for modularPair in args.modularFlags:
+        parts = modularPair.split(":")
+        modularFlagsPath = parts[0]
+        modularPatternsPath = parts[1]
+        modularFlagsDict = read_signature_csv_from_file_as_dict(
+            modularFlagsPath)
+        monolithicFlagsSubsetDict = \
+            extract_subset_from_monolithic_flags_as_dict_from_file(
+            monolithicTrie, modularPatternsPath)
+        mismatchingSignatures = compare_signature_flags(
+            monolithicFlagsSubsetDict, modularFlagsDict)
+        if mismatchingSignatures:
+            failed = True
+            print("ERROR: Hidden API flags are inconsistent:")
+            print("< " + modularFlagsPath)
+            print("> " + monolithicFlagsPath)
+            for mismatch in mismatchingSignatures:
+                signature = mismatch[0]
+                print()
+                print("< " + ",".join([signature] + mismatch[1]))
+                print("> " + ",".join([signature] + mismatch[2]))
+
+    if failed:
+        sys.exit(1)
+
+
+if __name__ == "__main__":
+    main(sys.argv)
diff --git a/scripts/hiddenapi/verify_overlaps_test.py b/scripts/hiddenapi/verify_overlaps_test.py
new file mode 100755
index 0000000..22a1cdf
--- /dev/null
+++ b/scripts/hiddenapi/verify_overlaps_test.py
@@ -0,0 +1,375 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2021 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+"""Unit tests for verify_overlaps_test.py."""
+import io
+import unittest
+
+from verify_overlaps import * #pylint: disable=unused-wildcard-import,wildcard-import
+
+
+class TestSignatureToElements(unittest.TestCase):
+
+    def signatureToElements(self, signature):
+        return InteriorNode().signatureToElements(signature)
+
+    def test_signatureToElements_1(self):
+        expected = [
+            'package:java',
+            'package:lang',
+            'class:ProcessBuilder',
+            'class:Redirect',
+            'class:1',
+            'member:<init>()V',
+        ]
+        self.assertEqual(
+            expected,
+            self.signatureToElements(
+                'Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V'))
+
+    def test_signatureToElements_2(self):
+        expected = [
+            'package:java',
+            'package:lang',
+            'class:Object',
+            'member:hashCode()I',
+        ]
+        self.assertEqual(
+            expected,
+            self.signatureToElements('Ljava/lang/Object;->hashCode()I'))
+
+    def test_signatureToElements_3(self):
+        expected = [
+            'package:java',
+            'package:lang',
+            'class:CharSequence',
+            'class:',
+            'class:ExternalSyntheticLambda0',
+            'member:<init>(Ljava/lang/CharSequence;)V',
+        ]
+        self.assertEqual(
+            expected,
+            self.signatureToElements(
+                'Ljava/lang/CharSequence$$ExternalSyntheticLambda0;'
+                '-><init>(Ljava/lang/CharSequence;)V'))
+
+#pylint: disable=line-too-long
+class TestDetectOverlaps(unittest.TestCase):
+
+    def read_flag_trie_from_string(self, csvdata):
+        with io.StringIO(csvdata) as f:
+            return read_flag_trie_from_stream(f)
+
+    def read_signature_csv_from_string_as_dict(self, csvdata):
+        with io.StringIO(csvdata) as f:
+            return read_signature_csv_from_stream_as_dict(f)
+
+    def extract_subset_from_monolithic_flags_as_dict_from_string(
+            self, monolithic, patterns):
+        with io.StringIO(patterns) as f:
+            return extract_subset_from_monolithic_flags_as_dict_from_stream(
+                monolithic, f)
+
+    extractInput = """
+Ljava/lang/Object;->hashCode()I,public-api,system-api,test-api
+Ljava/lang/Object;->toString()Ljava/lang/String;,blocked
+Ljava/util/zip/ZipFile;-><clinit>()V,blocked
+Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;,blocked
+Ljava/lang/Character;->serialVersionUID:J,sdk
+Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V,blocked
+"""
+
+    def test_extract_subset_signature(self):
+        monolithic = self.read_flag_trie_from_string(
+            TestDetectOverlaps.extractInput)
+
+        patterns = 'Ljava/lang/Object;->hashCode()I'
+
+        subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(
+            monolithic, patterns)
+        expected = {
+            'Ljava/lang/Object;->hashCode()I': {
+                None: ['public-api', 'system-api', 'test-api'],
+                'signature': 'Ljava/lang/Object;->hashCode()I',
+            },
+        }
+        self.assertEqual(expected, subset)
+
+    def test_extract_subset_class(self):
+        monolithic = self.read_flag_trie_from_string(
+            TestDetectOverlaps.extractInput)
+
+        patterns = 'java/lang/Object'
+
+        subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(
+            monolithic, patterns)
+        expected = {
+            'Ljava/lang/Object;->hashCode()I': {
+                None: ['public-api', 'system-api', 'test-api'],
+                'signature': 'Ljava/lang/Object;->hashCode()I',
+            },
+            'Ljava/lang/Object;->toString()Ljava/lang/String;': {
+                None: ['blocked'],
+                'signature': 'Ljava/lang/Object;->toString()Ljava/lang/String;',
+            },
+        }
+        self.assertEqual(expected, subset)
+
+    def test_extract_subset_outer_class(self):
+        monolithic = self.read_flag_trie_from_string(
+            TestDetectOverlaps.extractInput)
+
+        patterns = 'java/lang/Character'
+
+        subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(
+            monolithic, patterns)
+        expected = {
+            'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;':
+                {
+                    None: ['blocked'],
+                    'signature':
+                        'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;',
+                },
+            'Ljava/lang/Character;->serialVersionUID:J': {
+                None: ['sdk'],
+                'signature': 'Ljava/lang/Character;->serialVersionUID:J',
+            },
+        }
+        self.assertEqual(expected, subset)
+
+    def test_extract_subset_nested_class(self):
+        monolithic = self.read_flag_trie_from_string(
+            TestDetectOverlaps.extractInput)
+
+        patterns = 'java/lang/Character$UnicodeScript'
+
+        subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(
+            monolithic, patterns)
+        expected = {
+            'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;':
+                {
+                    None: ['blocked'],
+                    'signature':
+                        'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;',
+                },
+        }
+        self.assertEqual(expected, subset)
+
+    def test_extract_subset_package(self):
+        monolithic = self.read_flag_trie_from_string(
+            TestDetectOverlaps.extractInput)
+
+        patterns = 'java/lang/*'
+
+        subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(
+            monolithic, patterns)
+        expected = {
+            'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;':
+                {
+                    None: ['blocked'],
+                    'signature':
+                        'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;',
+                },
+            'Ljava/lang/Character;->serialVersionUID:J': {
+                None: ['sdk'],
+                'signature': 'Ljava/lang/Character;->serialVersionUID:J',
+            },
+            'Ljava/lang/Object;->hashCode()I': {
+                None: ['public-api', 'system-api', 'test-api'],
+                'signature': 'Ljava/lang/Object;->hashCode()I',
+            },
+            'Ljava/lang/Object;->toString()Ljava/lang/String;': {
+                None: ['blocked'],
+                'signature': 'Ljava/lang/Object;->toString()Ljava/lang/String;',
+            },
+            'Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V': {
+                None: ['blocked'],
+                'signature': 'Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V',
+            },
+        }
+        self.assertEqual(expected, subset)
+
+    def test_extract_subset_recursive_package(self):
+        monolithic = self.read_flag_trie_from_string(
+            TestDetectOverlaps.extractInput)
+
+        patterns = 'java/**'
+
+        subset = self.extract_subset_from_monolithic_flags_as_dict_from_string(
+            monolithic, patterns)
+        expected = {
+            'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;':
+                {
+                    None: ['blocked'],
+                    'signature':
+                        'Ljava/lang/Character$UnicodeScript;->of(I)Ljava/lang/Character$UnicodeScript;',
+                },
+            'Ljava/lang/Character;->serialVersionUID:J': {
+                None: ['sdk'],
+                'signature': 'Ljava/lang/Character;->serialVersionUID:J',
+            },
+            'Ljava/lang/Object;->hashCode()I': {
+                None: ['public-api', 'system-api', 'test-api'],
+                'signature': 'Ljava/lang/Object;->hashCode()I',
+            },
+            'Ljava/lang/Object;->toString()Ljava/lang/String;': {
+                None: ['blocked'],
+                'signature': 'Ljava/lang/Object;->toString()Ljava/lang/String;',
+            },
+            'Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V': {
+                None: ['blocked'],
+                'signature': 'Ljava/lang/ProcessBuilder$Redirect$1;-><init>()V',
+            },
+            'Ljava/util/zip/ZipFile;-><clinit>()V': {
+                None: ['blocked'],
+                'signature': 'Ljava/util/zip/ZipFile;-><clinit>()V',
+            },
+        }
+        self.assertEqual(expected, subset)
+
+    def test_extract_subset_invalid_pattern_wildcard_and_member(self):
+        monolithic = self.read_flag_trie_from_string(
+            TestDetectOverlaps.extractInput)
+
+        patterns = 'Ljava/lang/*;->hashCode()I'
+
+        with self.assertRaises(Exception) as context:
+            self.extract_subset_from_monolithic_flags_as_dict_from_string(
+                monolithic, patterns)
+        self.assertTrue('contains wildcard * and member signature hashCode()I'
+                        in str(context.exception))
+
+    def test_read_trie_duplicate(self):
+        with self.assertRaises(Exception) as context:
+            self.read_flag_trie_from_string("""
+Ljava/lang/Object;->hashCode()I,public-api,system-api,test-api
+Ljava/lang/Object;->hashCode()I,blocked
+""")
+        self.assertTrue('Duplicate signature: Ljava/lang/Object;->hashCode()I'
+                        in str(context.exception))
+
+    def test_read_trie_missing_member(self):
+        with self.assertRaises(Exception) as context:
+            self.read_flag_trie_from_string("""
+Ljava/lang/Object,public-api,system-api,test-api
+""")
+        self.assertTrue(
+            'Invalid signature: Ljava/lang/Object, does not identify a specific member'
+            in str(context.exception))
+
+    def test_match(self):
+        monolithic = self.read_signature_csv_from_string_as_dict("""
+Ljava/lang/Object;->hashCode()I,public-api,system-api,test-api
+""")
+        modular = self.read_signature_csv_from_string_as_dict("""
+Ljava/lang/Object;->hashCode()I,public-api,system-api,test-api
+""")
+        mismatches = compare_signature_flags(monolithic, modular)
+        expected = []
+        self.assertEqual(expected, mismatches)
+
+    def test_mismatch_overlapping_flags(self):
+        monolithic = self.read_signature_csv_from_string_as_dict("""
+Ljava/lang/Object;->toString()Ljava/lang/String;,public-api
+""")
+        modular = self.read_signature_csv_from_string_as_dict("""
+Ljava/lang/Object;->toString()Ljava/lang/String;,public-api,system-api,test-api
+""")
+        mismatches = compare_signature_flags(monolithic, modular)
+        expected = [
+            (
+                'Ljava/lang/Object;->toString()Ljava/lang/String;',
+                ['public-api', 'system-api', 'test-api'],
+                ['public-api'],
+            ),
+        ]
+        self.assertEqual(expected, mismatches)
+
+    def test_mismatch_monolithic_blocked(self):
+        monolithic = self.read_signature_csv_from_string_as_dict("""
+Ljava/lang/Object;->toString()Ljava/lang/String;,blocked
+""")
+        modular = self.read_signature_csv_from_string_as_dict("""
+Ljava/lang/Object;->toString()Ljava/lang/String;,public-api,system-api,test-api
+""")
+        mismatches = compare_signature_flags(monolithic, modular)
+        expected = [
+            (
+                'Ljava/lang/Object;->toString()Ljava/lang/String;',
+                ['public-api', 'system-api', 'test-api'],
+                ['blocked'],
+            ),
+        ]
+        self.assertEqual(expected, mismatches)
+
+    def test_mismatch_modular_blocked(self):
+        monolithic = self.read_signature_csv_from_string_as_dict("""
+Ljava/lang/Object;->toString()Ljava/lang/String;,public-api,system-api,test-api
+""")
+        modular = self.read_signature_csv_from_string_as_dict("""
+Ljava/lang/Object;->toString()Ljava/lang/String;,blocked
+""")
+        mismatches = compare_signature_flags(monolithic, modular)
+        expected = [
+            (
+                'Ljava/lang/Object;->toString()Ljava/lang/String;',
+                ['blocked'],
+                ['public-api', 'system-api', 'test-api'],
+            ),
+        ]
+        self.assertEqual(expected, mismatches)
+
+    def test_match_treat_missing_from_modular_as_blocked(self):
+        monolithic = self.read_signature_csv_from_string_as_dict('')
+        modular = self.read_signature_csv_from_string_as_dict("""
+Ljava/lang/Object;->toString()Ljava/lang/String;,public-api,system-api,test-api
+""")
+        mismatches = compare_signature_flags(monolithic, modular)
+        expected = [
+            (
+                'Ljava/lang/Object;->toString()Ljava/lang/String;',
+                ['public-api', 'system-api', 'test-api'],
+                [],
+            ),
+        ]
+        self.assertEqual(expected, mismatches)
+
+    def test_mismatch_treat_missing_from_modular_as_blocked(self):
+        monolithic = self.read_signature_csv_from_string_as_dict("""
+Ljava/lang/Object;->hashCode()I,public-api,system-api,test-api
+""")
+        modular = {}
+        mismatches = compare_signature_flags(monolithic, modular)
+        expected = [
+            (
+                'Ljava/lang/Object;->hashCode()I',
+                ['blocked'],
+                ['public-api', 'system-api', 'test-api'],
+            ),
+        ]
+        self.assertEqual(expected, mismatches)
+
+    def test_blocked_missing_from_modular(self):
+        monolithic = self.read_signature_csv_from_string_as_dict("""
+Ljava/lang/Object;->hashCode()I,blocked
+""")
+        modular = {}
+        mismatches = compare_signature_flags(monolithic, modular)
+        expected = []
+        self.assertEqual(expected, mismatches)
+#pylint: enable=line-too-long
+
+if __name__ == '__main__':
+    unittest.main(verbosity=2)
diff --git a/scripts/manifest_check.py b/scripts/manifest_check.py
index 8168fbf..4ef4399 100755
--- a/scripts/manifest_check.py
+++ b/scripts/manifest_check.py
@@ -90,6 +90,15 @@
   else:
     manifest_required, manifest_optional, tags = extract_uses_libs_xml(manifest)
 
+  # Trim namespace component. Normally Soong does that automatically when it
+  # handles module names specified in Android.bp properties. However not all
+  # <uses-library> entries in the manifest correspond to real modules: some of
+  # the optional libraries may be missing at build time. Therefor this script
+  # accepts raw module names as spelled in Android.bp/Amdroid.mk and trims the
+  # optional namespace part manually.
+  required = trim_namespace_parts(required)
+  optional = trim_namespace_parts(optional)
+
   if manifest_required == required and manifest_optional == optional:
     return None
 
@@ -118,6 +127,17 @@
   return errmsg
 
 
+MODULE_NAMESPACE = re.compile("^//[^:]+:")
+
+def trim_namespace_parts(modules):
+  """Trim the namespace part of each module, if present. Leave only the name."""
+
+  trimmed = []
+  for module in modules:
+    trimmed.append(MODULE_NAMESPACE.sub('', module))
+  return trimmed
+
+
 def extract_uses_libs_apk(badging):
   """Extract <uses-library> tags from the manifest of an APK."""
 
diff --git a/scripts/manifest_check_test.py b/scripts/manifest_check_test.py
index 7159bdd..e3e8ac4 100755
--- a/scripts/manifest_check_test.py
+++ b/scripts/manifest_check_test.py
@@ -183,6 +183,15 @@
                             optional_uses_libraries=['bar'])
     self.assertTrue(matches)
 
+  def test_mixed_with_namespace(self):
+    xml = self.xml_tmpl % ('\n'.join([uses_library_xml('foo'),
+                                      uses_library_xml('bar', required_xml(False))]))
+    apk = self.apk_tmpl % ('\n'.join([uses_library_apk('foo'),
+                                      uses_library_apk('bar', required_apk(False))]))
+    matches = self.run_test(xml, apk, uses_libraries=['//x/y/z:foo'],
+                            optional_uses_libraries=['//x/y/z:bar'])
+    self.assertTrue(matches)
+
 
 class ExtractTargetSdkVersionTest(unittest.TestCase):
   def run_test(self, xml, apk, version):
diff --git a/scripts/microfactory.bash b/scripts/microfactory.bash
index 4bb6058..5e702e0 100644
--- a/scripts/microfactory.bash
+++ b/scripts/microfactory.bash
@@ -59,7 +59,7 @@
     BUILDDIR=$(getoutdir) \
       SRCDIR=${TOP} \
       BLUEPRINTDIR=${TOP}/build/blueprint \
-      EXTRA_ARGS="-pkg-path android/soong=${TOP}/build/soong -pkg-path github.com/golang/protobuf=${TOP}/external/golang-protobuf" \
+      EXTRA_ARGS="-pkg-path android/soong=${TOP}/build/soong -pkg-path google.golang.org/protobuf=${TOP}/external/golang-protobuf" \
       build_go $@
 }
 
diff --git a/scripts/rbc-run b/scripts/rbc-run
new file mode 100755
index 0000000..e2fa6d1
--- /dev/null
+++ b/scripts/rbc-run
@@ -0,0 +1,16 @@
+#! /bin/bash
+# Convert and run one configuration
+# Args: <product>-<variant>
+[[ $# -eq 1 && "$1" =~ ^(.*)-(.*)$ ]] || { echo Usage: ${0##*/} PRODUCT-VARIANT >&2; exit 1; }
+declare -r product="${BASH_REMATCH[1]:-aosp_arm}"
+declare -r variant="${BASH_REMATCH[2]:-eng}"
+set -eu
+declare -r output_root=${OUT_DIR:-out}
+declare -r runner="$output_root/soong/.bootstrap/bin/rbcrun"
+declare -r converter="$output_root/soong/.bootstrap/bin/mk2rbc"
+declare -r launcher=$output_root/launchers/run.rbc
+$converter -mode=write -r --outdir $output_root --launcher=$launcher $product
+printf "#TARGET_PRODUCT=$product TARGET_BUILD_VARIANT=$variant\n"
+env TARGET_PRODUCT=$product TARGET_BUILD_VARIANT=$variant \
+  $runner RBC_OUT="make,global" RBC_DEBUG="${RBC_DEBUG:-}" $launcher
+
diff --git a/scripts/setup_go_workspace_for_soong.sh b/scripts/setup_go_workspace_for_soong.sh
index 479d09c..434d2fb 100755
--- a/scripts/setup_go_workspace_for_soong.sh
+++ b/scripts/setup_go_workspace_for_soong.sh
@@ -346,7 +346,7 @@
   "${ANDROID_PATH}/build/blueprint|${OUTPUT_PATH}/src/github.com/google/blueprint"
   "${ANDROID_PATH}/build/soong|${OUTPUT_PATH}/src/android/soong"
   "${ANDROID_PATH}/art/build|${OUTPUT_PATH}/src/android/soong/art"
-  "${ANDROID_PATH}/external/golang-protobuf|${OUTPUT_PATH}/src/github.com/golang/protobuf"
+  "${ANDROID_PATH}/external/golang-protobuf|${OUTPUT_PATH}/src/google.golang.org/protobuf"
   "${ANDROID_PATH}/external/llvm/soong|${OUTPUT_PATH}/src/android/soong/llvm"
   "${ANDROID_PATH}/external/clang/soong|${OUTPUT_PATH}/src/android/soong/clang"
   "${ANDROID_PATH}/external/robolectric-shadows/soong|${OUTPUT_PATH}/src/android/soong/robolectric"
diff --git a/sdk/Android.bp b/sdk/Android.bp
index 368c03a..0c9bf27 100644
--- a/sdk/Android.bp
+++ b/sdk/Android.bp
@@ -16,6 +16,7 @@
     srcs: [
         "bp.go",
         "exports.go",
+        "member_type.go",
         "sdk.go",
         "update.go",
     ],
diff --git a/sdk/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go
index 412e806..bed2ecf 100644
--- a/sdk/bootclasspath_fragment_sdk_test.go
+++ b/sdk/bootclasspath_fragment_sdk_test.go
@@ -134,11 +134,12 @@
     image_name: "art",
     contents: ["mybootlib"],
     hidden_api: {
-        stub_flags: "hiddenapi/stub-flags.csv",
         annotation_flags: "hiddenapi/annotation-flags.csv",
         metadata: "hiddenapi/metadata.csv",
         index: "hiddenapi/index.csv",
-        all_flags: "hiddenapi/all-flags.csv",
+        signature_patterns: "hiddenapi/signature-patterns.csv",
+        stub_flags: "hiddenapi/filtered-stub-flags.csv",
+        all_flags: "hiddenapi/filtered-flags.csv",
     },
 }
 
@@ -147,7 +148,7 @@
     prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["com.android.art"],
-    jars: ["java/mybootlib.jar"],
+    jars: ["java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar"],
 }
 `),
 		checkVersionedAndroidBpContents(`
@@ -161,11 +162,12 @@
     image_name: "art",
     contents: ["mysdk_mybootlib@current"],
     hidden_api: {
-        stub_flags: "hiddenapi/stub-flags.csv",
         annotation_flags: "hiddenapi/annotation-flags.csv",
         metadata: "hiddenapi/metadata.csv",
         index: "hiddenapi/index.csv",
-        all_flags: "hiddenapi/all-flags.csv",
+        signature_patterns: "hiddenapi/signature-patterns.csv",
+        stub_flags: "hiddenapi/filtered-stub-flags.csv",
+        all_flags: "hiddenapi/filtered-flags.csv",
     },
 }
 
@@ -174,7 +176,7 @@
     sdk_member_name: "mybootlib",
     visibility: ["//visibility:public"],
     apex_available: ["com.android.art"],
-    jars: ["java/mybootlib.jar"],
+    jars: ["java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar"],
 }
 
 sdk_snapshot {
@@ -185,12 +187,13 @@
 }
 `),
 		checkAllCopyRules(`
-.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/stub-flags.csv -> hiddenapi/stub-flags.csv
 .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/annotation-flags.csv -> hiddenapi/annotation-flags.csv
 .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/metadata.csv -> hiddenapi/metadata.csv
 .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/index.csv -> hiddenapi/index.csv
-.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/all-flags.csv -> hiddenapi/all-flags.csv
-.intermediates/mybootlib/android_common/javac/mybootlib.jar -> java/mybootlib.jar
+.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/signature-patterns.csv -> hiddenapi/signature-patterns.csv
+.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-stub-flags.csv -> hiddenapi/filtered-stub-flags.csv
+.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-flags.csv -> hiddenapi/filtered-flags.csv
+.intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar
 		`),
 		snapshotTestPreparer(checkSnapshotWithoutSource, preparerForSnapshot),
 
@@ -224,7 +227,7 @@
 		java.PrepareForTestWithJavaDefaultModules,
 		java.PrepareForTestWithJavaSdkLibraryFiles,
 		java.FixtureWithLastReleaseApis("mysdklibrary", "myothersdklibrary", "mycoreplatform"),
-		java.FixtureConfigureUpdatableBootJars("myapex:mybootlib", "myapex:myothersdklibrary"),
+		java.FixtureConfigureApexBootJars("myapex:mybootlib", "myapex:myothersdklibrary"),
 		prepareForSdkTestWithApex,
 
 		// Add a platform_bootclasspath that depends on the fragment.
@@ -332,11 +335,12 @@
         stub_libs: ["mycoreplatform"],
     },
     hidden_api: {
-        stub_flags: "hiddenapi/stub-flags.csv",
         annotation_flags: "hiddenapi/annotation-flags.csv",
         metadata: "hiddenapi/metadata.csv",
         index: "hiddenapi/index.csv",
-        all_flags: "hiddenapi/all-flags.csv",
+        signature_patterns: "hiddenapi/signature-patterns.csv",
+        stub_flags: "hiddenapi/filtered-stub-flags.csv",
+        all_flags: "hiddenapi/filtered-flags.csv",
     },
 }
 
@@ -345,7 +349,7 @@
     prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["myapex"],
-    jars: ["java/mybootlib.jar"],
+    jars: ["java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar"],
     permitted_packages: ["mybootlib"],
 }
 
@@ -416,11 +420,12 @@
         stub_libs: ["mysdk_mycoreplatform@current"],
     },
     hidden_api: {
-        stub_flags: "hiddenapi/stub-flags.csv",
         annotation_flags: "hiddenapi/annotation-flags.csv",
         metadata: "hiddenapi/metadata.csv",
         index: "hiddenapi/index.csv",
-        all_flags: "hiddenapi/all-flags.csv",
+        signature_patterns: "hiddenapi/signature-patterns.csv",
+        stub_flags: "hiddenapi/filtered-stub-flags.csv",
+        all_flags: "hiddenapi/filtered-flags.csv",
     },
 }
 
@@ -429,7 +434,7 @@
     sdk_member_name: "mybootlib",
     visibility: ["//visibility:public"],
     apex_available: ["myapex"],
-    jars: ["java/mybootlib.jar"],
+    jars: ["java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar"],
     permitted_packages: ["mybootlib"],
 }
 
@@ -494,12 +499,13 @@
 }
 		`),
 		checkAllCopyRules(`
-.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/stub-flags.csv -> hiddenapi/stub-flags.csv
 .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/annotation-flags.csv -> hiddenapi/annotation-flags.csv
 .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/metadata.csv -> hiddenapi/metadata.csv
 .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/index.csv -> hiddenapi/index.csv
-.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/all-flags.csv -> hiddenapi/all-flags.csv
-.intermediates/mybootlib/android_common/javac/mybootlib.jar -> java/mybootlib.jar
+.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/signature-patterns.csv -> hiddenapi/signature-patterns.csv
+.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-stub-flags.csv -> hiddenapi/filtered-stub-flags.csv
+.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-flags.csv -> hiddenapi/filtered-flags.csv
+.intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar
 .intermediates/myothersdklibrary.stubs/android_common/javac/myothersdklibrary.stubs.jar -> sdk_library/public/myothersdklibrary-stubs.jar
 .intermediates/myothersdklibrary.stubs.source/android_common/metalava/myothersdklibrary.stubs.source_api.txt -> sdk_library/public/myothersdklibrary.txt
 .intermediates/myothersdklibrary.stubs.source/android_common/metalava/myothersdklibrary.stubs.source_removed.txt -> sdk_library/public/myothersdklibrary-removed.txt
@@ -538,8 +544,139 @@
 			rule = module.Output("updatable-bcp-packages.txt")
 			expectedContents := `'mybootlib\nmyothersdklibrary\n'`
 			android.AssertStringEquals(t, "updatable-bcp-packages.txt", expectedContents, rule.Args["content"])
+
+			rule = module.Output("out/soong/hiddenapi/hiddenapi-flags.csv.valid")
+			android.AssertStringDoesContain(t, "verify-overlaps", rule.RuleParams.Command, " snapshot/hiddenapi/filtered-flags.csv:snapshot/hiddenapi/signature-patterns.csv ")
 		}),
 		snapshotTestPreparer(checkSnapshotWithSourcePreferred, preparerForSnapshot),
+		snapshotTestChecker(checkSnapshotWithSourcePreferred, func(t *testing.T, result *android.TestResult) {
+			module := result.ModuleForTests("platform-bootclasspath", "android_common")
+			rule := module.Output("out/soong/hiddenapi/hiddenapi-flags.csv.valid")
+			android.AssertStringDoesContain(t, "verify-overlaps", rule.RuleParams.Command, " out/soong/.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/filtered-flags.csv:out/soong/.intermediates/mybootclasspathfragment/android_common_myapex/modular-hiddenapi/signature-patterns.csv ")
+		}),
+		snapshotTestPreparer(checkSnapshotPreferredWithSource, preparerForSnapshot),
+	)
+}
+
+// TestSnapshotWithBootClasspathFragment_Fragments makes sure that the fragments property of a
+// bootclasspath_fragment is correctly output to the sdk snapshot.
+func TestSnapshotWithBootClasspathFragment_Fragments(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForSdkTestWithJava,
+		java.PrepareForTestWithJavaDefaultModules,
+		java.PrepareForTestWithJavaSdkLibraryFiles,
+		java.FixtureWithLastReleaseApis("mysdklibrary", "myothersdklibrary"),
+		java.FixtureConfigureApexBootJars("someapex:mysdklibrary", "myotherapex:myotherlib"),
+		prepareForSdkTestWithApex,
+
+		// Some additional files needed for the myotherapex.
+		android.FixtureMergeMockFs(android.MockFS{
+			"system/sepolicy/apex/myotherapex-file_contexts": nil,
+			"myotherapex/apex_manifest.json":                 nil,
+			"myotherapex/Test.java":                          nil,
+		}),
+
+		android.FixtureAddTextFile("myotherapex/Android.bp", `
+			apex {
+				name: "myotherapex",
+				key: "myapex.key",
+				min_sdk_version: "2",
+				bootclasspath_fragments: ["myotherbootclasspathfragment"],
+			}
+
+			bootclasspath_fragment {
+				name: "myotherbootclasspathfragment",
+				apex_available: ["myotherapex"],
+				contents: [
+					"myotherlib",
+				],
+			}
+
+			java_library {
+				name: "myotherlib",
+				apex_available: ["myotherapex"],
+				srcs: ["Test.java"],
+				min_sdk_version: "2",
+				permitted_packages: ["myothersdklibrary"],
+				compile_dex: true,
+			}
+		`),
+
+		android.FixtureWithRootAndroidBp(`
+			sdk {
+				name: "mysdk",
+				bootclasspath_fragments: ["mybootclasspathfragment"],
+			}
+
+			bootclasspath_fragment {
+				name: "mybootclasspathfragment",
+				contents: [
+					"mysdklibrary",
+				],
+				fragments: [
+					{
+						apex: "myotherapex",
+						module: "myotherbootclasspathfragment"
+					},
+				],
+			}
+
+			java_sdk_library {
+				name: "mysdklibrary",
+				srcs: ["Test.java"],
+				shared_library: false,
+				public: {enabled: true},
+				min_sdk_version: "2",
+			}
+		`),
+	).RunTest(t)
+
+	// A preparer to update the test fixture used when processing an unpackage snapshot.
+	preparerForSnapshot := fixtureAddPrebuiltApexForBootclasspathFragment("myapex", "mybootclasspathfragment")
+
+	CheckSnapshot(t, result, "mysdk", "",
+		checkUnversionedAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+prebuilt_bootclasspath_fragment {
+    name: "mybootclasspathfragment",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    contents: ["mysdklibrary"],
+    fragments: [
+        {
+            apex: "myotherapex",
+            module: "myotherbootclasspathfragment",
+        },
+    ],
+    hidden_api: {
+        annotation_flags: "hiddenapi/annotation-flags.csv",
+        metadata: "hiddenapi/metadata.csv",
+        index: "hiddenapi/index.csv",
+        signature_patterns: "hiddenapi/signature-patterns.csv",
+        stub_flags: "hiddenapi/filtered-stub-flags.csv",
+        all_flags: "hiddenapi/filtered-flags.csv",
+    },
+}
+
+java_sdk_library_import {
+    name: "mysdklibrary",
+    prefer: false,
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    shared_library: false,
+    public: {
+        jars: ["sdk_library/public/mysdklibrary-stubs.jar"],
+        stub_srcs: ["sdk_library/public/mysdklibrary_stub_sources"],
+        current_api: "sdk_library/public/mysdklibrary.txt",
+        removed_api: "sdk_library/public/mysdklibrary-removed.txt",
+        sdk_version: "current",
+    },
+}
+		`),
+		snapshotTestPreparer(checkSnapshotWithoutSource, preparerForSnapshot),
+		snapshotTestPreparer(checkSnapshotWithSourcePreferred, preparerForSnapshot),
 		snapshotTestPreparer(checkSnapshotPreferredWithSource, preparerForSnapshot),
 	)
 }
@@ -607,7 +744,7 @@
 		java.PrepareForTestWithJavaDefaultModules,
 		java.PrepareForTestWithJavaSdkLibraryFiles,
 		java.FixtureWithLastReleaseApis("mysdklibrary"),
-		java.FixtureConfigureUpdatableBootJars("myapex:mybootlib"),
+		java.FixtureConfigureApexBootJars("myapex:mybootlib"),
 		prepareForSdkTestWithApex,
 
 		// Add a platform_bootclasspath that depends on the fragment.
@@ -717,11 +854,12 @@
         max_target_o_low_priority: ["hiddenapi/my-max-target-o-low-priority.txt"],
         blocked: ["hiddenapi/my-blocked.txt"],
         unsupported_packages: ["hiddenapi/my-unsupported-packages.txt"],
-        stub_flags: "hiddenapi/stub-flags.csv",
         annotation_flags: "hiddenapi/annotation-flags.csv",
         metadata: "hiddenapi/metadata.csv",
         index: "hiddenapi/index.csv",
-        all_flags: "hiddenapi/all-flags.csv",
+        signature_patterns: "hiddenapi/signature-patterns.csv",
+        stub_flags: "hiddenapi/filtered-stub-flags.csv",
+        all_flags: "hiddenapi/filtered-flags.csv",
     },
 }
 
@@ -730,7 +868,7 @@
     prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["myapex"],
-    jars: ["java/mybootlib.jar"],
+    jars: ["java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar"],
     permitted_packages: ["mybootlib"],
 }
 
@@ -760,12 +898,13 @@
 my-max-target-o-low-priority.txt -> hiddenapi/my-max-target-o-low-priority.txt
 my-blocked.txt -> hiddenapi/my-blocked.txt
 my-unsupported-packages.txt -> hiddenapi/my-unsupported-packages.txt
-.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/stub-flags.csv -> hiddenapi/stub-flags.csv
 .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/annotation-flags.csv -> hiddenapi/annotation-flags.csv
 .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/metadata.csv -> hiddenapi/metadata.csv
 .intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/index.csv -> hiddenapi/index.csv
-.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/all-flags.csv -> hiddenapi/all-flags.csv
-.intermediates/mybootlib/android_common/javac/mybootlib.jar -> java/mybootlib.jar
+.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/signature-patterns.csv -> hiddenapi/signature-patterns.csv
+.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-stub-flags.csv -> hiddenapi/filtered-stub-flags.csv
+.intermediates/mybootclasspathfragment/android_common/modular-hiddenapi/filtered-flags.csv -> hiddenapi/filtered-flags.csv
+.intermediates/mysdk/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/mybootlib.jar
 .intermediates/mysdklibrary.stubs/android_common/javac/mysdklibrary.stubs.jar -> sdk_library/public/mysdklibrary-stubs.jar
 .intermediates/mysdklibrary.stubs.source/android_common/metalava/mysdklibrary.stubs.source_api.txt -> sdk_library/public/mysdklibrary.txt
 .intermediates/mysdklibrary.stubs.source/android_common/metalava/mysdklibrary.stubs.source_removed.txt -> sdk_library/public/mysdklibrary-removed.txt
diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go
index 31555c0..25e35fc 100644
--- a/sdk/cc_sdk_test.go
+++ b/sdk/cc_sdk_test.go
@@ -347,6 +347,7 @@
 		cc_object {
 			name: "crtobj",
 			stl: "none",
+			system_shared_libs: [],
 			sanitize: {
 				never: true,
 			},
@@ -364,6 +365,7 @@
     apex_available: ["//apex_available:platform"],
     stl: "none",
     compile_multilib: "both",
+    system_shared_libs: [],
     sanitize: {
         never: true,
     },
@@ -388,6 +390,7 @@
     apex_available: ["//apex_available:platform"],
     stl: "none",
     compile_multilib: "both",
+    system_shared_libs: [],
     sanitize: {
         never: true,
     },
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 813dcfd..9efb3a4 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -453,7 +453,7 @@
     prefer: false,
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
-    jars: ["java/myjavalib.jar"],
+    jars: ["java_boot_libs/snapshot/jars/are/invalid/myjavalib.jar"],
     permitted_packages: ["pkg.myjavalib"],
 }
 `),
@@ -465,7 +465,7 @@
     sdk_member_name: "myjavalib",
     visibility: ["//visibility:public"],
     apex_available: ["//apex_available:platform"],
-    jars: ["java/myjavalib.jar"],
+    jars: ["java_boot_libs/snapshot/jars/are/invalid/myjavalib.jar"],
     permitted_packages: ["pkg.myjavalib"],
 }
 
@@ -474,9 +474,10 @@
     visibility: ["//visibility:public"],
     java_boot_libs: ["myexports_myjavalib@current"],
 }
+
 `),
 		checkAllCopyRules(`
-.intermediates/myjavalib/android_common/withres/myjavalib.jar -> java/myjavalib.jar
+.intermediates/myexports/common_os/empty -> java_boot_libs/snapshot/jars/are/invalid/myjavalib.jar
 `),
 	)
 }
diff --git a/sdk/member_type.go b/sdk/member_type.go
new file mode 100644
index 0000000..ee27c86
--- /dev/null
+++ b/sdk/member_type.go
@@ -0,0 +1,164 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package sdk
+
+import (
+	"reflect"
+
+	"android/soong/android"
+	"github.com/google/blueprint/proptools"
+)
+
+// Contains information about the sdk properties that list sdk members by type, e.g.
+// Java_header_libs.
+type sdkMemberTypeListProperty struct {
+	// getter for the list of member names
+	getter func(properties interface{}) []string
+
+	// setter for the list of member names
+	setter func(properties interface{}, list []string)
+
+	// the type of member referenced in the list
+	memberType android.SdkMemberType
+
+	// the dependency tag used for items in this list that can be used to determine the memberType
+	// for a resolved dependency.
+	dependencyTag android.SdkMemberTypeDependencyTag
+}
+
+func (p *sdkMemberTypeListProperty) propertyName() string {
+	return p.memberType.SdkPropertyName()
+}
+
+// Cache of dynamically generated dynamicSdkMemberTypes objects. The key is the pointer
+// to a slice of SdkMemberType instances held in android.SdkMemberTypes.
+var dynamicSdkMemberTypesMap android.OncePer
+
+// A dynamically generated set of member list properties and associated structure type.
+type dynamicSdkMemberTypes struct {
+	// The dynamically generated structure type.
+	//
+	// Contains one []string exported field for each android.SdkMemberTypes. The name of the field
+	// is the exported form of the value returned by SdkMemberType.SdkPropertyName().
+	propertiesStructType reflect.Type
+
+	// Information about each of the member type specific list properties.
+	memberTypeListProperties []*sdkMemberTypeListProperty
+
+	memberTypeToProperty map[android.SdkMemberType]*sdkMemberTypeListProperty
+}
+
+func (d *dynamicSdkMemberTypes) createMemberTypeListProperties() interface{} {
+	return reflect.New(d.propertiesStructType).Interface()
+}
+
+func getDynamicSdkMemberTypes(registry *android.SdkMemberTypesRegistry) *dynamicSdkMemberTypes {
+
+	// Get a key that uniquely identifies the registry contents.
+	key := registry.UniqueOnceKey()
+
+	// Get the registered types.
+	registeredTypes := registry.RegisteredTypes()
+
+	// Get the cached value, creating new instance if necessary.
+	return dynamicSdkMemberTypesMap.Once(key, func() interface{} {
+		return createDynamicSdkMemberTypes(registeredTypes)
+	}).(*dynamicSdkMemberTypes)
+}
+
+// Create the dynamicSdkMemberTypes from the list of registered member types.
+//
+// A struct is created which contains one exported field per member type corresponding to
+// the SdkMemberType.SdkPropertyName() value.
+//
+// A list of sdkMemberTypeListProperty instances is created, one per member type that provides:
+// * a reference to the member type.
+// * a getter for the corresponding field in the properties struct.
+// * a dependency tag that identifies the member type of a resolved dependency.
+//
+func createDynamicSdkMemberTypes(sdkMemberTypes []android.SdkMemberType) *dynamicSdkMemberTypes {
+
+	var listProperties []*sdkMemberTypeListProperty
+	memberTypeToProperty := map[android.SdkMemberType]*sdkMemberTypeListProperty{}
+	var fields []reflect.StructField
+
+	// Iterate over the member types creating StructField and sdkMemberTypeListProperty objects.
+	nextFieldIndex := 0
+	for _, memberType := range sdkMemberTypes {
+
+		p := memberType.SdkPropertyName()
+
+		var getter func(properties interface{}) []string
+		var setter func(properties interface{}, list []string)
+		if memberType.RequiresBpProperty() {
+			// Create a dynamic exported field for the member type's property.
+			fields = append(fields, reflect.StructField{
+				Name: proptools.FieldNameForProperty(p),
+				Type: reflect.TypeOf([]string{}),
+				Tag:  `android:"arch_variant"`,
+			})
+
+			// Copy the field index for use in the getter func as using the loop variable directly will
+			// cause all funcs to use the last value.
+			fieldIndex := nextFieldIndex
+			nextFieldIndex += 1
+
+			getter = func(properties interface{}) []string {
+				// The properties is expected to be of the following form (where
+				// <Module_types> is the name of an SdkMemberType.SdkPropertyName().
+				//     properties *struct {<Module_types> []string, ....}
+				//
+				// Although it accesses the field by index the following reflection code is equivalent to:
+				//    *properties.<Module_types>
+				//
+				list := reflect.ValueOf(properties).Elem().Field(fieldIndex).Interface().([]string)
+				return list
+			}
+
+			setter = func(properties interface{}, list []string) {
+				// The properties is expected to be of the following form (where
+				// <Module_types> is the name of an SdkMemberType.SdkPropertyName().
+				//     properties *struct {<Module_types> []string, ....}
+				//
+				// Although it accesses the field by index the following reflection code is equivalent to:
+				//    *properties.<Module_types> = list
+				//
+				reflect.ValueOf(properties).Elem().Field(fieldIndex).Set(reflect.ValueOf(list))
+			}
+		}
+
+		// Create an sdkMemberTypeListProperty for the member type.
+		memberListProperty := &sdkMemberTypeListProperty{
+			getter:     getter,
+			setter:     setter,
+			memberType: memberType,
+
+			// Dependencies added directly from member properties are always exported.
+			dependencyTag: android.DependencyTagForSdkMemberType(memberType, true),
+		}
+
+		memberTypeToProperty[memberType] = memberListProperty
+		listProperties = append(listProperties, memberListProperty)
+	}
+
+	// Create a dynamic struct from the collated fields.
+	propertiesStructType := reflect.StructOf(fields)
+
+	return &dynamicSdkMemberTypes{
+		memberTypeListProperties: listProperties,
+		memberTypeToProperty:     memberTypeToProperty,
+		propertiesStructType:     propertiesStructType,
+	}
+}
diff --git a/sdk/sdk.go b/sdk/sdk.go
index b1c8aeb..6dea752 100644
--- a/sdk/sdk.go
+++ b/sdk/sdk.go
@@ -17,7 +17,6 @@
 import (
 	"fmt"
 	"io"
-	"reflect"
 	"strconv"
 
 	"github.com/google/blueprint"
@@ -50,7 +49,7 @@
 	// The dynamically generated information about the registered SdkMemberType
 	dynamicSdkMemberTypes *dynamicSdkMemberTypes
 
-	// The dynamically created instance of the properties struct containing the sdk member
+	// The dynamically created instance of the properties struct containing the sdk member type
 	// list properties, e.g. java_libs.
 	dynamicMemberTypeListProperties interface{}
 
@@ -95,148 +94,6 @@
 	Prebuilt_visibility []string
 }
 
-// Contains information about the sdk properties that list sdk members, e.g.
-// Java_header_libs.
-type sdkMemberListProperty struct {
-	// getter for the list of member names
-	getter func(properties interface{}) []string
-
-	// setter for the list of member names
-	setter func(properties interface{}, list []string)
-
-	// the type of member referenced in the list
-	memberType android.SdkMemberType
-
-	// the dependency tag used for items in this list that can be used to determine the memberType
-	// for a resolved dependency.
-	dependencyTag android.SdkMemberTypeDependencyTag
-}
-
-func (p *sdkMemberListProperty) propertyName() string {
-	return p.memberType.SdkPropertyName()
-}
-
-// Cache of dynamically generated dynamicSdkMemberTypes objects. The key is the pointer
-// to a slice of SdkMemberType instances held in android.SdkMemberTypes.
-var dynamicSdkMemberTypesMap android.OncePer
-
-// A dynamically generated set of member list properties and associated structure type.
-type dynamicSdkMemberTypes struct {
-	// The dynamically generated structure type.
-	//
-	// Contains one []string exported field for each android.SdkMemberTypes. The name of the field
-	// is the exported form of the value returned by SdkMemberType.SdkPropertyName().
-	propertiesStructType reflect.Type
-
-	// Information about each of the member type specific list properties.
-	memberListProperties []*sdkMemberListProperty
-
-	memberTypeToProperty map[android.SdkMemberType]*sdkMemberListProperty
-}
-
-func (d *dynamicSdkMemberTypes) createMemberListProperties() interface{} {
-	return reflect.New(d.propertiesStructType).Interface()
-}
-
-func getDynamicSdkMemberTypes(registry *android.SdkMemberTypesRegistry) *dynamicSdkMemberTypes {
-
-	// Get a key that uniquely identifies the registry contents.
-	key := registry.UniqueOnceKey()
-
-	// Get the registered types.
-	registeredTypes := registry.RegisteredTypes()
-
-	// Get the cached value, creating new instance if necessary.
-	return dynamicSdkMemberTypesMap.Once(key, func() interface{} {
-		return createDynamicSdkMemberTypes(registeredTypes)
-	}).(*dynamicSdkMemberTypes)
-}
-
-// Create the dynamicSdkMemberTypes from the list of registered member types.
-//
-// A struct is created which contains one exported field per member type corresponding to
-// the SdkMemberType.SdkPropertyName() value.
-//
-// A list of sdkMemberListProperty instances is created, one per member type that provides:
-// * a reference to the member type.
-// * a getter for the corresponding field in the properties struct.
-// * a dependency tag that identifies the member type of a resolved dependency.
-//
-func createDynamicSdkMemberTypes(sdkMemberTypes []android.SdkMemberType) *dynamicSdkMemberTypes {
-
-	var listProperties []*sdkMemberListProperty
-	memberTypeToProperty := map[android.SdkMemberType]*sdkMemberListProperty{}
-	var fields []reflect.StructField
-
-	// Iterate over the member types creating StructField and sdkMemberListProperty objects.
-	nextFieldIndex := 0
-	for _, memberType := range sdkMemberTypes {
-
-		p := memberType.SdkPropertyName()
-
-		var getter func(properties interface{}) []string
-		var setter func(properties interface{}, list []string)
-		if memberType.RequiresBpProperty() {
-			// Create a dynamic exported field for the member type's property.
-			fields = append(fields, reflect.StructField{
-				Name: proptools.FieldNameForProperty(p),
-				Type: reflect.TypeOf([]string{}),
-				Tag:  `android:"arch_variant"`,
-			})
-
-			// Copy the field index for use in the getter func as using the loop variable directly will
-			// cause all funcs to use the last value.
-			fieldIndex := nextFieldIndex
-			nextFieldIndex += 1
-
-			getter = func(properties interface{}) []string {
-				// The properties is expected to be of the following form (where
-				// <Module_types> is the name of an SdkMemberType.SdkPropertyName().
-				//     properties *struct {<Module_types> []string, ....}
-				//
-				// Although it accesses the field by index the following reflection code is equivalent to:
-				//    *properties.<Module_types>
-				//
-				list := reflect.ValueOf(properties).Elem().Field(fieldIndex).Interface().([]string)
-				return list
-			}
-
-			setter = func(properties interface{}, list []string) {
-				// The properties is expected to be of the following form (where
-				// <Module_types> is the name of an SdkMemberType.SdkPropertyName().
-				//     properties *struct {<Module_types> []string, ....}
-				//
-				// Although it accesses the field by index the following reflection code is equivalent to:
-				//    *properties.<Module_types> = list
-				//
-				reflect.ValueOf(properties).Elem().Field(fieldIndex).Set(reflect.ValueOf(list))
-			}
-		}
-
-		// Create an sdkMemberListProperty for the member type.
-		memberListProperty := &sdkMemberListProperty{
-			getter:     getter,
-			setter:     setter,
-			memberType: memberType,
-
-			// Dependencies added directly from member properties are always exported.
-			dependencyTag: android.DependencyTagForSdkMemberType(memberType, true),
-		}
-
-		memberTypeToProperty[memberType] = memberListProperty
-		listProperties = append(listProperties, memberListProperty)
-	}
-
-	// Create a dynamic struct from the collated fields.
-	propertiesStructType := reflect.StructOf(fields)
-
-	return &dynamicSdkMemberTypes{
-		memberListProperties: listProperties,
-		memberTypeToProperty: memberTypeToProperty,
-		propertiesStructType: propertiesStructType,
-	}
-}
-
 // sdk defines an SDK which is a logical group of modules (e.g. native libs, headers, java libs, etc.)
 // which Mainline modules like APEX can choose to build with.
 func SdkModuleFactory() android.Module {
@@ -247,16 +104,16 @@
 	s := &sdk{}
 	s.properties.Module_exports = moduleExports
 	// Get the dynamic sdk member type data for the currently registered sdk member types.
-	var registry *android.SdkMemberTypesRegistry
+	var typeRegistry *android.SdkMemberTypesRegistry
 	if moduleExports {
-		registry = android.ModuleExportsMemberTypes
+		typeRegistry = android.ModuleExportsMemberTypes
 	} else {
-		registry = android.SdkMemberTypes
+		typeRegistry = android.SdkMemberTypes
 	}
-	s.dynamicSdkMemberTypes = getDynamicSdkMemberTypes(registry)
+	s.dynamicSdkMemberTypes = getDynamicSdkMemberTypes(typeRegistry)
 	// Create an instance of the dynamically created struct that contains all the
 	// properties for the member type specific list properties.
-	s.dynamicMemberTypeListProperties = s.dynamicSdkMemberTypes.createMemberListProperties()
+	s.dynamicMemberTypeListProperties = s.dynamicSdkMemberTypes.createMemberTypeListProperties()
 	s.AddProperties(&s.properties, s.dynamicMemberTypeListProperties)
 
 	// Make sure that the prebuilt visibility property is verified for errors.
@@ -280,11 +137,11 @@
 	return s
 }
 
-func (s *sdk) memberListProperties() []*sdkMemberListProperty {
-	return s.dynamicSdkMemberTypes.memberListProperties
+func (s *sdk) memberTypeListProperties() []*sdkMemberTypeListProperty {
+	return s.dynamicSdkMemberTypes.memberTypeListProperties
 }
 
-func (s *sdk) memberListProperty(memberType android.SdkMemberType) *sdkMemberListProperty {
+func (s *sdk) memberTypeListProperty(memberType android.SdkMemberType) *sdkMemberTypeListProperty {
 	return s.dynamicSdkMemberTypes.memberTypeToProperty[memberType]
 }
 
@@ -341,6 +198,19 @@
 	}}
 }
 
+// newDependencyContext creates a new SdkDependencyContext for this sdk.
+func (s *sdk) newDependencyContext(mctx android.BottomUpMutatorContext) android.SdkDependencyContext {
+	return &dependencyContext{
+		BottomUpMutatorContext: mctx,
+	}
+}
+
+type dependencyContext struct {
+	android.BottomUpMutatorContext
+}
+
+var _ android.SdkDependencyContext = (*dependencyContext)(nil)
+
 // RegisterPreDepsMutators registers pre-deps mutators to support modules implementing SdkAware
 // interface and the sdk module type. This function has been made public to be called by tests
 // outside of the sdk package
@@ -410,14 +280,15 @@
 	if s, ok := mctx.Module().(*sdk); ok {
 		// Add dependencies from enabled and non CommonOS variants to the sdk member variants.
 		if s.Enabled() && !s.IsCommonOSVariant() {
-			for _, memberListProperty := range s.memberListProperties() {
+			ctx := s.newDependencyContext(mctx)
+			for _, memberListProperty := range s.memberTypeListProperties() {
 				if memberListProperty.getter == nil {
 					continue
 				}
 				names := memberListProperty.getter(s.dynamicMemberTypeListProperties)
 				if len(names) > 0 {
 					tag := memberListProperty.dependencyTag
-					memberListProperty.memberType.AddDependencies(mctx, tag, names)
+					memberListProperty.memberType.AddDependencies(ctx, tag, names)
 				}
 			}
 		}
diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go
index a13b0d7..85e3d87 100644
--- a/sdk/sdk_test.go
+++ b/sdk/sdk_test.go
@@ -15,19 +15,21 @@
 package sdk
 
 import (
-	"android/soong/android"
 	"log"
 	"os"
+	"runtime"
 	"testing"
 
+	"android/soong/android"
+
 	"github.com/google/blueprint/proptools"
 )
 
 // Needed in an _test.go file in this package to ensure tests run correctly, particularly in IDE.
 func TestMain(m *testing.M) {
-	if android.BuildOs != android.Linux {
+	if runtime.GOOS != "linux" {
 		// b/145598135 - Generating host snapshots for anything other than linux is not supported.
-		log.Printf("Skipping as sdk snapshot generation is only supported on %s not %s", android.Linux, android.BuildOs)
+		log.Printf("Skipping as sdk snapshot generation is only supported on linux not %s", runtime.GOOS)
 		os.Exit(0)
 	}
 
@@ -565,6 +567,49 @@
 		)
 	})
 
+	t.Run("SOONG_SDK_SNAPSHOT_USE_SOURCE_CONFIG_VAR=module:build_from_source", func(t *testing.T) {
+		result := android.GroupFixturePreparers(
+			preparer,
+			android.FixtureMergeEnv(map[string]string{
+				"SOONG_SDK_SNAPSHOT_USE_SOURCE_CONFIG_VAR": "module:build_from_source",
+			}),
+		).RunTest(t)
+
+		checkZipFile(t, result, "out/soong/.intermediates/mysdk/common_os/mysdk-current.zip")
+
+		CheckSnapshot(t, result, "mysdk", "",
+			checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_import {
+    name: "mysdk_myjavalib@current",
+    sdk_member_name: "myjavalib",
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    jars: ["java/myjavalib.jar"],
+}
+
+java_import {
+    name: "myjavalib",
+    prefer: false,
+    use_source_config_var: {
+        config_namespace: "module",
+        var_name: "build_from_source",
+    },
+    visibility: ["//visibility:public"],
+    apex_available: ["//apex_available:platform"],
+    jars: ["java/myjavalib.jar"],
+}
+
+sdk_snapshot {
+    name: "mysdk@current",
+    visibility: ["//visibility:public"],
+    java_header_libs: ["mysdk_myjavalib@current"],
+}
+			`),
+		)
+	})
+
 	t.Run("SOONG_SDK_SNAPSHOT_VERSION=unversioned", func(t *testing.T) {
 		result := android.GroupFixturePreparers(
 			preparer,
diff --git a/sdk/update.go b/sdk/update.go
index 84f0e4e..96a6e69 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -22,6 +22,7 @@
 
 	"android/soong/apex"
 	"android/soong/cc"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 
@@ -35,6 +36,37 @@
 //     By default every unversioned module in the generated snapshot has prefer: false. Building it
 //     with SOONG_SDK_SNAPSHOT_PREFER=true will force them to use prefer: true.
 //
+// SOONG_SDK_SNAPSHOT_USE_SOURCE_CONFIG_VAR
+//     If set this specifies the Soong config var that can be used to control whether the prebuilt
+//     modules from the generated snapshot or the original source modules. Values must be a colon
+//     separated pair of strings, the first of which is the Soong config namespace, and the second
+//     is the name of the variable within that namespace.
+//
+//     The config namespace and var name are used to set the `use_source_config_var` property. That
+//     in turn will cause the generated prebuilts to use the soong config variable to select whether
+//     source or the prebuilt is used.
+//     e.g. If an sdk snapshot is built using:
+//       m SOONG_SDK_SNAPSHOT_USE_SOURCE_CONFIG_VAR=acme:build_from_source sdkextensions-sdk
+//     Then the resulting snapshot will include:
+//       use_source_config_var: {
+//         config_namespace: "acme",
+//         var_name: "build_from_source",
+//       }
+//
+//     Assuming that the config variable is defined in .mk using something like:
+//       $(call add_soong_config_namespace,acme)
+//       $(call add_soong_config_var_value,acme,build_from_source,true)
+//
+//     Then when the snapshot is unpacked in the repository it will have the following behavior:
+//       m droid - will use the sdkextensions-sdk prebuilts if present. Otherwise, it will use the
+//           sources.
+//       m SOONG_CONFIG_acme_build_from_source=true droid - will use the sdkextensions-sdk
+//            sources, if present. Otherwise, it will use the prebuilts.
+//
+//     This is a temporary mechanism to control the prefer flags and will be removed once a more
+//     maintainable solution has been implemented.
+//     TODO(b/174997203): Remove when no longer necessary.
+//
 // SOONG_SDK_SNAPSHOT_VERSION
 //     This provides control over the version of the generated snapshot.
 //
@@ -113,8 +145,16 @@
 	gc.indentLevel--
 }
 
-func (gc *generatedContents) Printfln(format string, args ...interface{}) {
-	fmt.Fprintf(&(gc.content), strings.Repeat("    ", gc.indentLevel)+format+"\n", args...)
+// IndentedPrintf will add spaces to indent the line to the appropriate level before printing the
+// arguments.
+func (gc *generatedContents) IndentedPrintf(format string, args ...interface{}) {
+	fmt.Fprintf(&(gc.content), strings.Repeat("    ", gc.indentLevel)+format, args...)
+}
+
+// UnindentedPrintf does not add spaces to indent the line to the appropriate level before printing
+// the arguments.
+func (gc *generatedContents) UnindentedPrintf(format string, args ...interface{}) {
+	fmt.Fprintf(&(gc.content), format, args...)
 }
 
 func (gf *generatedFile) build(pctx android.PackageContext, ctx android.BuilderContext, implicits android.Paths) {
@@ -211,7 +251,7 @@
 	}
 
 	var members []*sdkMember
-	for _, memberListProperty := range s.memberListProperties() {
+	for _, memberListProperty := range s.memberTypeListProperties() {
 		membersOfType := byType[memberListProperty.memberType]
 		members = append(members, membersOfType...)
 	}
@@ -627,7 +667,7 @@
 		staticProperties := &snapshotModuleStaticProperties{
 			Compile_multilib: sdkVariant.multilibUsages.String(),
 		}
-		dynamicProperties := s.dynamicSdkMemberTypes.createMemberListProperties()
+		dynamicProperties := s.dynamicSdkMemberTypes.createMemberTypeListProperties()
 
 		combinedProperties := &combinedSnapshotModuleProperties{
 			sdkVariant:        sdkVariant,
@@ -647,7 +687,7 @@
 		}
 
 		combined := sdkVariantToCombinedProperties[memberVariantDep.sdkVariant]
-		memberListProperty := s.memberListProperty(memberVariantDep.memberType)
+		memberListProperty := s.memberTypeListProperty(memberVariantDep.memberType)
 		memberName := ctx.OtherModuleName(memberVariantDep.variant)
 
 		if memberListProperty.getter == nil {
@@ -677,7 +717,7 @@
 	}
 
 	// Extract the common members, removing them from the original properties.
-	commonDynamicProperties := s.dynamicSdkMemberTypes.createMemberListProperties()
+	commonDynamicProperties := s.dynamicSdkMemberTypes.createMemberTypeListProperties()
 	extractor := newCommonValueExtractor(commonDynamicProperties)
 	extractCommonProperties(ctx, extractor, commonDynamicProperties, propertyContainers)
 
@@ -710,7 +750,7 @@
 	}
 
 	dynamicMemberTypeListProperties := combined.dynamicProperties
-	for _, memberListProperty := range s.memberListProperties() {
+	for _, memberListProperty := range s.memberTypeListProperties() {
 		if memberListProperty.getter == nil {
 			continue
 		}
@@ -752,6 +792,8 @@
 	module.insertAfter("name", "sdk_member_name", name)
 	// Remove the prefer property if present as versioned modules never need marking with prefer.
 	module.removeProperty("prefer")
+	// Ditto for use_source_config_var
+	module.removeProperty("use_source_config_var")
 	return module
 }
 
@@ -809,13 +851,13 @@
 }
 
 func generateFilteredBpContents(contents *generatedContents, bpFile *bpFile, moduleFilter func(module *bpModule) bool) {
-	contents.Printfln("// This is auto-generated. DO NOT EDIT.")
+	contents.IndentedPrintf("// This is auto-generated. DO NOT EDIT.\n")
 	for _, bpModule := range bpFile.order {
 		if moduleFilter(bpModule) {
-			contents.Printfln("")
-			contents.Printfln("%s {", bpModule.moduleType)
+			contents.IndentedPrintf("\n")
+			contents.IndentedPrintf("%s {\n", bpModule.moduleType)
 			outputPropertySet(contents, bpModule.bpPropertySet)
-			contents.Printfln("}")
+			contents.IndentedPrintf("}\n")
 		}
 	}
 }
@@ -826,7 +868,7 @@
 	addComment := func(name string) {
 		if text, ok := set.comments[name]; ok {
 			for _, line := range strings.Split(text, "\n") {
-				contents.Printfln("// %s", line)
+				contents.IndentedPrintf("// %s\n", line)
 			}
 		}
 	}
@@ -843,29 +885,8 @@
 		}
 
 		addComment(name)
-		switch v := value.(type) {
-		case []string:
-			length := len(v)
-			if length > 1 {
-				contents.Printfln("%s: [", name)
-				contents.Indent()
-				for i := 0; i < length; i = i + 1 {
-					contents.Printfln("%q,", v[i])
-				}
-				contents.Dedent()
-				contents.Printfln("],")
-			} else if length == 0 {
-				contents.Printfln("%s: [],", name)
-			} else {
-				contents.Printfln("%s: [%q],", name, v[0])
-			}
-
-		case bool:
-			contents.Printfln("%s: %t,", name, v)
-
-		default:
-			contents.Printfln("%s: %q,", name, value)
-		}
+		reflectValue := reflect.ValueOf(value)
+		outputNamedValue(contents, name, reflectValue)
 	}
 
 	for _, name := range set.order {
@@ -875,15 +896,94 @@
 		switch v := value.(type) {
 		case *bpPropertySet:
 			addComment(name)
-			contents.Printfln("%s: {", name)
+			contents.IndentedPrintf("%s: {\n", name)
 			outputPropertySet(contents, v)
-			contents.Printfln("},")
+			contents.IndentedPrintf("},\n")
 		}
 	}
 
 	contents.Dedent()
 }
 
+// outputNamedValue outputs a value that has an associated name. The name will be indented, followed
+// by the value and then followed by a , and a newline.
+func outputNamedValue(contents *generatedContents, name string, value reflect.Value) {
+	contents.IndentedPrintf("%s: ", name)
+	outputUnnamedValue(contents, value)
+	contents.UnindentedPrintf(",\n")
+}
+
+// outputUnnamedValue outputs a single value. The value is not indented and is not followed by
+// either a , or a newline. With multi-line values, e.g. slices, all but the first line will be
+// indented and all but the last line will end with a newline.
+func outputUnnamedValue(contents *generatedContents, value reflect.Value) {
+	valueType := value.Type()
+	switch valueType.Kind() {
+	case reflect.Bool:
+		contents.UnindentedPrintf("%t", value.Bool())
+
+	case reflect.String:
+		contents.UnindentedPrintf("%q", value)
+
+	case reflect.Ptr:
+		outputUnnamedValue(contents, value.Elem())
+
+	case reflect.Slice:
+		length := value.Len()
+		if length == 0 {
+			contents.UnindentedPrintf("[]")
+		} else {
+			firstValue := value.Index(0)
+			if length == 1 && !multiLineValue(firstValue) {
+				contents.UnindentedPrintf("[")
+				outputUnnamedValue(contents, firstValue)
+				contents.UnindentedPrintf("]")
+			} else {
+				contents.UnindentedPrintf("[\n")
+				contents.Indent()
+				for i := 0; i < length; i++ {
+					itemValue := value.Index(i)
+					contents.IndentedPrintf("")
+					outputUnnamedValue(contents, itemValue)
+					contents.UnindentedPrintf(",\n")
+				}
+				contents.Dedent()
+				contents.IndentedPrintf("]")
+			}
+		}
+
+	case reflect.Struct:
+		// Avoid unlimited recursion by requiring every structure to implement android.BpPrintable.
+		v := value.Interface()
+		if _, ok := v.(android.BpPrintable); !ok {
+			panic(fmt.Errorf("property value %#v of type %T does not implement android.BpPrintable", v, v))
+		}
+		contents.UnindentedPrintf("{\n")
+		contents.Indent()
+		for f := 0; f < valueType.NumField(); f++ {
+			fieldType := valueType.Field(f)
+			if fieldType.Anonymous {
+				continue
+			}
+			fieldValue := value.Field(f)
+			fieldName := fieldType.Name
+			propertyName := proptools.PropertyNameForField(fieldName)
+			outputNamedValue(contents, propertyName, fieldValue)
+		}
+		contents.Dedent()
+		contents.IndentedPrintf("}")
+
+	default:
+		panic(fmt.Errorf("Unknown type: %T of value %#v", value, value))
+	}
+}
+
+// multiLineValue returns true if the supplied value may require multiple lines in the output.
+func multiLineValue(value reflect.Value) bool {
+	kind := value.Kind()
+	return kind == reflect.Slice || kind == reflect.Struct
+}
+
 func (s *sdk) GetAndroidBpContentsForTests() string {
 	contents := &generatedContents{}
 	generateBpContents(contents, s.builderForTests.bpFile)
@@ -930,6 +1030,9 @@
 	filesToZip  android.Paths
 	zipsToMerge android.Paths
 
+	// The path to an empty file.
+	emptyFile android.WritablePath
+
 	prebuiltModules map[string]*bpModule
 	prebuiltOrder   []*bpModule
 
@@ -980,6 +1083,19 @@
 	s.zipsToMerge = append(s.zipsToMerge, tmpZipPath)
 }
 
+func (s *snapshotBuilder) EmptyFile() android.Path {
+	if s.emptyFile == nil {
+		ctx := s.ctx
+		s.emptyFile = android.PathForModuleOut(ctx, "empty")
+		s.ctx.Build(pctx, android.BuildParams{
+			Rule:   android.Touch,
+			Output: s.emptyFile,
+		})
+	}
+
+	return s.emptyFile
+}
+
 func (s *snapshotBuilder) AddPrebuiltModule(member android.SdkMember, moduleType string) android.BpModule {
 	name := member.Name()
 	if s.prebuiltModules[name] != nil {
@@ -1561,13 +1677,24 @@
 		// snapshot to be created that sets prefer: true.
 		// TODO(b/174997203): Remove once the ability to select the modules to prefer can be done
 		//  dynamically at build time not at snapshot generation time.
-		prefer := ctx.sdkMemberContext.Config().IsEnvTrue("SOONG_SDK_SNAPSHOT_PREFER")
+		config := ctx.sdkMemberContext.Config()
+		prefer := config.IsEnvTrue("SOONG_SDK_SNAPSHOT_PREFER")
 
 		// Set prefer. Setting this to false is not strictly required as that is the default but it does
 		// provide a convenient hook to post-process the generated Android.bp file, e.g. in tests to
 		// check the behavior when a prebuilt is preferred. It also makes it explicit what the default
 		// behavior is for the module.
 		bpModule.insertAfter("name", "prefer", prefer)
+
+		configVar := config.Getenv("SOONG_SDK_SNAPSHOT_USE_SOURCE_CONFIG_VAR")
+		if configVar != "" {
+			parts := strings.Split(configVar, ":")
+			cfp := android.ConfigVarProperties{
+				Config_namespace: proptools.StringPtr(parts[0]),
+				Var_name:         proptools.StringPtr(parts[1]),
+			}
+			bpModule.insertAfter("prefer", "use_source_config_var", cfp)
+		}
 	}
 
 	// Group the variants by os type.
@@ -1648,7 +1775,7 @@
 	var osTypes []android.OsType
 	for _, osType := range android.OsTypeList() {
 		if s.DeviceSupported() {
-			if osType.Class == android.Device && osType != android.Fuchsia {
+			if osType.Class == android.Device {
 				osTypes = append(osTypes, osType)
 			}
 		}
diff --git a/sh/sh_binary.go b/sh/sh_binary.go
index 42d5680..c6e98c7 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -104,6 +104,12 @@
 	Recovery_available *bool
 }
 
+// Test option struct.
+type TestOptions struct {
+	// If the test is a hostside(no device required) unittest that shall be run during presubmit check.
+	Unit_test *bool
+}
+
 type TestProperties struct {
 	// list of compatibility suites (for example "cts", "vts") that the module should be
 	// installed into.
@@ -143,6 +149,9 @@
 	// list of device library modules that should be installed alongside the test.
 	// Only available for host sh_test modules.
 	Data_device_libs []string `android:"path,arch_variant"`
+
+	// Test options.
+	Test_options TestOptions
 }
 
 type ShBinary struct {
@@ -440,6 +449,9 @@
 					dir := strings.TrimSuffix(s.dataModules[relPath].String(), relPath)
 					entries.AddStrings("LOCAL_TEST_DATA", dir+":"+relPath)
 				}
+				if Bool(s.testProperties.Test_options.Unit_test) {
+					entries.SetBool("LOCAL_IS_UNIT_TEST", true)
+				}
 			},
 		},
 	}}
@@ -483,6 +495,10 @@
 	module := &ShTest{}
 	InitShBinaryModule(&module.ShBinary)
 	module.AddProperties(&module.testProperties)
+	// Default sh_test_host to unit_tests = true
+	if module.testProperties.Test_options.Unit_test == nil {
+		module.testProperties.Test_options.Unit_test = proptools.BoolPtr(true)
+	}
 
 	android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
 	return module
@@ -511,18 +527,6 @@
 	// visibility
 }
 
-type bazelShBinary struct {
-	android.BazelTargetModuleBase
-	bazelShBinaryAttributes
-}
-
-func BazelShBinaryFactory() android.Module {
-	module := &bazelShBinary{}
-	module.AddProperties(&module.bazelShBinaryAttributes)
-	android.InitBazelTargetModule(module)
-	return module
-}
-
 func ShBinaryBp2Build(ctx android.TopDownMutatorContext) {
 	m, ok := ctx.Module().(*ShBinary)
 	if !ok || !m.ConvertWithBp2build(ctx) {
@@ -540,13 +544,7 @@
 		Rule_class: "sh_binary",
 	}
 
-	ctx.CreateBazelTargetModule(BazelShBinaryFactory, m.Name(), props, attrs)
+	ctx.CreateBazelTargetModule(m.Name(), props, attrs)
 }
 
-func (m *bazelShBinary) Name() string {
-	return m.BaseModuleName()
-}
-
-func (m *bazelShBinary) GenerateAndroidBuildActions(ctx android.ModuleContext) {}
-
 var Bool = proptools.Bool
diff --git a/sh/sh_binary_test.go b/sh/sh_binary_test.go
index 9e7e594..865d5f3 100644
--- a/sh/sh_binary_test.go
+++ b/sh/sh_binary_test.go
@@ -3,6 +3,7 @@
 import (
 	"os"
 	"path/filepath"
+	"strconv"
 	"testing"
 
 	"android/soong/android"
@@ -114,7 +115,7 @@
 		}
 	`)
 
-	buildOS := android.BuildOs.String()
+	buildOS := config.BuildOS.String()
 	arches := []string{"android_arm64_armv8-a", buildOS + "_x86_64"}
 	for _, arch := range arches {
 		variant := ctx.ModuleForTests("foo", arch)
@@ -148,14 +149,20 @@
 				"testdata/data1",
 				"testdata/sub/data2",
 			],
+			test_options: {
+				unit_test: true,
+			},
 		}
 	`)
 
-	buildOS := android.BuildOs.String()
+	buildOS := ctx.Config().BuildOS.String()
 	mod := ctx.ModuleForTests("foo", buildOS+"_x86_64").Module().(*ShTest)
 	if !mod.Host() {
 		t.Errorf("host bit is not set for a sh_test_host module.")
 	}
+	entries := android.AndroidMkEntriesForTest(t, ctx, mod)[0]
+	actualData, _ := strconv.ParseBool(entries.EntryMap["LOCAL_IS_UNIT_TEST"][0])
+	android.AssertBoolEquals(t, "LOCAL_IS_UNIT_TEST", true, actualData)
 }
 
 func TestShTestHost_dataDeviceModules(t *testing.T) {
@@ -185,7 +192,7 @@
 		}
 	`)
 
-	buildOS := android.BuildOs.String()
+	buildOS := config.BuildOS.String()
 	variant := ctx.ModuleForTests("foo", buildOS+"_x86_64")
 
 	relocated := variant.Output("relocated/lib64/libbar.so")
diff --git a/snapshot/Android.bp b/snapshot/Android.bp
new file mode 100644
index 0000000..f17ac53
--- /dev/null
+++ b/snapshot/Android.bp
@@ -0,0 +1,22 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+    name: "soong-snapshot",
+    pkgPath: "android/soong/snapshot",
+    deps: [
+        "blueprint",
+        "blueprint-pathtools",
+        "soong",
+        "soong-android",
+    ],
+    srcs: [
+        "recovery_snapshot.go",
+        "snapshot.go",
+        "snapshot_base.go",
+        "util.go",
+        "vendor_snapshot.go",
+    ],
+    pluginFor: ["soong_build"],
+}
diff --git a/snapshot/recovery_snapshot.go b/snapshot/recovery_snapshot.go
new file mode 100644
index 0000000..9b3919c
--- /dev/null
+++ b/snapshot/recovery_snapshot.go
@@ -0,0 +1,130 @@
+// Copyright 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package snapshot
+
+import "android/soong/android"
+
+// Interface for modules which can be captured in the recovery snapshot.
+type RecoverySnapshotModuleInterface interface {
+	SnapshotModuleInterfaceBase
+	InRecovery() bool
+	ExcludeFromRecoverySnapshot() bool
+}
+
+var recoverySnapshotSingleton = SnapshotSingleton{
+	"recovery",                     // name
+	"SOONG_RECOVERY_SNAPSHOT_ZIP",  // makeVar
+	android.OptionalPath{},         // snapshotZipFile
+	RecoverySnapshotImageSingleton, // Image
+	false,                          // Fake
+}
+
+func RecoverySnapshotSingleton() android.Singleton {
+	return &recoverySnapshotSingleton
+}
+
+// Determine if a dir under source tree is an SoC-owned proprietary directory based
+// on recovery snapshot configuration
+// Examples: device/, vendor/
+func isRecoveryProprietaryPath(dir string, deviceConfig android.DeviceConfig) bool {
+	return RecoverySnapshotSingleton().(*SnapshotSingleton).Image.IsProprietaryPath(dir, deviceConfig)
+}
+
+func IsRecoveryProprietaryModule(ctx android.BaseModuleContext) bool {
+
+	// Any module in a recovery proprietary path is a recovery proprietary
+	// module.
+	if isRecoveryProprietaryPath(ctx.ModuleDir(), ctx.DeviceConfig()) {
+		return true
+	}
+
+	// However if the module is not in a recovery proprietary path, it may
+	// still be a recovery proprietary module. This happens for cc modules
+	// that are excluded from the recovery snapshot, and it means that the
+	// vendor has assumed control of the framework-provided module.
+
+	if c, ok := ctx.Module().(RecoverySnapshotModuleInterface); ok {
+		if c.ExcludeFromRecoverySnapshot() {
+			return true
+		}
+	}
+
+	return false
+}
+
+var RecoverySnapshotImageName = "recovery"
+
+type RecoverySnapshotImage struct{}
+
+func (RecoverySnapshotImage) Init(ctx android.RegistrationContext) {
+	ctx.RegisterSingletonType("recovery-snapshot", RecoverySnapshotSingleton)
+}
+
+func (RecoverySnapshotImage) shouldGenerateSnapshot(ctx android.SingletonContext) bool {
+	// RECOVERY_SNAPSHOT_VERSION must be set to 'current' in order to generate a
+	// snapshot.
+	return ctx.DeviceConfig().RecoverySnapshotVersion() == "current"
+}
+
+func (RecoverySnapshotImage) InImage(m SnapshotModuleInterfaceBase) func() bool {
+	r, ok := m.(RecoverySnapshotModuleInterface)
+
+	if !ok {
+		// This module does not support recovery snapshot
+		return func() bool { return false }
+	}
+	return r.InRecovery
+}
+
+func (RecoverySnapshotImage) IsProprietaryPath(dir string, deviceConfig android.DeviceConfig) bool {
+	return isDirectoryExcluded(dir, deviceConfig.RecoverySnapshotDirsExcludedMap(), deviceConfig.RecoverySnapshotDirsIncludedMap())
+}
+
+func (RecoverySnapshotImage) ExcludeFromSnapshot(m SnapshotModuleInterfaceBase) bool {
+	r, ok := m.(RecoverySnapshotModuleInterface)
+
+	if !ok {
+		// This module does not support recovery snapshot
+		return true
+	}
+	return r.ExcludeFromRecoverySnapshot()
+}
+
+func (RecoverySnapshotImage) IsUsingSnapshot(cfg android.DeviceConfig) bool {
+	recoverySnapshotVersion := cfg.RecoverySnapshotVersion()
+	return recoverySnapshotVersion != "current" && recoverySnapshotVersion != ""
+}
+
+func (RecoverySnapshotImage) TargetSnapshotVersion(cfg android.DeviceConfig) string {
+	return cfg.RecoverySnapshotVersion()
+}
+
+func (RecoverySnapshotImage) ExcludeFromDirectedSnapshot(cfg android.DeviceConfig, name string) bool {
+	// If we're using full snapshot, not directed snapshot, capture every module
+	if !cfg.DirectedRecoverySnapshot() {
+		return false
+	}
+	// Else, checks if name is in RECOVERY_SNAPSHOT_MODULES.
+	return !cfg.RecoverySnapshotModules()[name]
+}
+
+func (RecoverySnapshotImage) ImageName() string {
+	return RecoverySnapshotImageName
+}
+
+var RecoverySnapshotImageSingleton RecoverySnapshotImage
+
+func init() {
+	RecoverySnapshotImageSingleton.Init(android.InitRegistrationContext)
+}
diff --git a/snapshot/snapshot.go b/snapshot/snapshot.go
new file mode 100644
index 0000000..294f8b6
--- /dev/null
+++ b/snapshot/snapshot.go
@@ -0,0 +1,122 @@
+// Copyright 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package snapshot
+
+import (
+	"path/filepath"
+	"sort"
+
+	"android/soong/android"
+)
+
+// This file contains singletons to capture snapshots. This singleton will generate snapshot of each target
+// image, and capturing snapshot module will be delegated to each module which implements GenerateSnapshotAction
+// function and register with RegisterSnapshotAction.
+
+var pctx = android.NewPackageContext("android/soong/snapshot")
+
+type SnapshotSingleton struct {
+	// Name, e.g., "vendor", "recovery", "ramdisk".
+	name string
+
+	// Make variable that points to the snapshot file, e.g.,
+	// "SOONG_RECOVERY_SNAPSHOT_ZIP".
+	makeVar string
+
+	// Path to the snapshot zip file.
+	snapshotZipFile android.OptionalPath
+
+	// Implementation of the image interface specific to the image
+	// associated with this snapshot (e.g., specific to the vendor image,
+	// recovery image, etc.).
+	Image SnapshotImage
+
+	// Whether this singleton is for fake snapshot or not.
+	// Fake snapshot is a snapshot whose prebuilt binaries and headers are empty.
+	// It is much faster to generate, and can be used to inspect dependencies.
+	Fake bool
+}
+
+// Interface of function to capture snapshot from each module
+type GenerateSnapshotAction func(snapshot SnapshotSingleton, ctx android.SingletonContext, snapshotArchDir string) android.Paths
+
+var snapshotActionList []GenerateSnapshotAction
+
+// Register GenerateSnapshotAction function so it can be called while generating snapshot
+func RegisterSnapshotAction(x GenerateSnapshotAction) {
+	snapshotActionList = append(snapshotActionList, x)
+}
+
+func (c *SnapshotSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+	if !c.Image.shouldGenerateSnapshot(ctx) {
+		return
+	}
+
+	var snapshotOutputs android.Paths
+
+	// Snapshot zipped artifacts will be captured under {SNAPSHOT_ARCH} directory
+
+	snapshotDir := c.name + "-snapshot"
+	if c.Fake {
+		// If this is a fake snapshot singleton, place all files under fake/ subdirectory to avoid
+		// collision with real snapshot files
+		snapshotDir = filepath.Join("fake", snapshotDir)
+	}
+	snapshotArchDir := filepath.Join(snapshotDir, ctx.DeviceConfig().DeviceArch())
+
+	for _, f := range snapshotActionList {
+		snapshotOutputs = append(snapshotOutputs, f(*c, ctx, snapshotArchDir)...)
+	}
+
+	// All artifacts are ready. Sort them to normalize ninja and then zip.
+	sort.Slice(snapshotOutputs, func(i, j int) bool {
+		return snapshotOutputs[i].String() < snapshotOutputs[j].String()
+	})
+
+	zipPath := android.PathForOutput(
+		ctx,
+		snapshotDir,
+		c.name+"-"+ctx.Config().DeviceName()+".zip")
+	zipRule := android.NewRuleBuilder(pctx, ctx)
+
+	// filenames in rspfile from FlagWithRspFileInputList might be single-quoted. Remove it with tr
+	snapshotOutputList := android.PathForOutput(
+		ctx,
+		snapshotDir,
+		c.name+"-"+ctx.Config().DeviceName()+"_list")
+	rspFile := snapshotOutputList.ReplaceExtension(ctx, "rsp")
+	zipRule.Command().
+		Text("tr").
+		FlagWithArg("-d ", "\\'").
+		FlagWithRspFileInputList("< ", rspFile, snapshotOutputs).
+		FlagWithOutput("> ", snapshotOutputList)
+
+	zipRule.Temporary(snapshotOutputList)
+
+	zipRule.Command().
+		BuiltTool("soong_zip").
+		FlagWithOutput("-o ", zipPath).
+		FlagWithArg("-C ", android.PathForOutput(ctx, snapshotDir).String()).
+		FlagWithInput("-l ", snapshotOutputList)
+
+	zipRule.Build(zipPath.String(), c.name+" snapshot "+zipPath.String())
+	zipRule.DeleteTemporaryFiles()
+	c.snapshotZipFile = android.OptionalPathForPath(zipPath)
+}
+
+func (c *SnapshotSingleton) MakeVars(ctx android.MakeVarsContext) {
+	ctx.Strict(
+		c.makeVar,
+		c.snapshotZipFile.String())
+}
diff --git a/snapshot/snapshot_base.go b/snapshot/snapshot_base.go
new file mode 100644
index 0000000..de93f3e
--- /dev/null
+++ b/snapshot/snapshot_base.go
@@ -0,0 +1,104 @@
+// Copyright 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package snapshot
+
+import (
+	"android/soong/android"
+	"path/filepath"
+)
+
+// Interface for modules which can be captured in the snapshot.
+type SnapshotModuleInterfaceBase interface{}
+
+// Defines the specifics of different images to which the snapshot process is applicable, e.g.,
+// vendor, recovery, ramdisk.
+type SnapshotImage interface {
+	// Returns true if a snapshot should be generated for this image.
+	shouldGenerateSnapshot(ctx android.SingletonContext) bool
+
+	// Function that returns true if the module is included in this image.
+	// Using a function return instead of a value to prevent early
+	// evalution of a function that may be not be defined.
+	InImage(m SnapshotModuleInterfaceBase) func() bool
+
+	// Returns true if a dir under source tree is an SoC-owned proprietary
+	// directory, such as device/, vendor/, etc.
+	//
+	// For a given snapshot (e.g., vendor, recovery, etc.) if
+	// isProprietaryPath(dir, deviceConfig) returns true, then the module in dir
+	// will be built from sources.
+	IsProprietaryPath(dir string, deviceConfig android.DeviceConfig) bool
+
+	// Whether a given module has been explicitly excluded from the
+	// snapshot, e.g., using the exclude_from_vendor_snapshot or
+	// exclude_from_recovery_snapshot properties.
+	ExcludeFromSnapshot(m SnapshotModuleInterfaceBase) bool
+
+	// Returns true if the build is using a snapshot for this image.
+	IsUsingSnapshot(cfg android.DeviceConfig) bool
+
+	// Returns a version of which the snapshot should be used in this target.
+	// This will only be meaningful when isUsingSnapshot is true.
+	TargetSnapshotVersion(cfg android.DeviceConfig) string
+
+	// Whether to exclude a given module from the directed snapshot or not.
+	// If the makefile variable DIRECTED_{IMAGE}_SNAPSHOT is true, directed snapshot is turned on,
+	// and only modules listed in {IMAGE}_SNAPSHOT_MODULES will be captured.
+	ExcludeFromDirectedSnapshot(cfg android.DeviceConfig, name string) bool
+
+	// Returns target image name
+	ImageName() string
+}
+
+type directoryMap map[string]bool
+
+var (
+	// Modules under following directories are ignored. They are OEM's and vendor's
+	// proprietary modules(device/, kernel/, vendor/, and hardware/).
+	defaultDirectoryExcludedMap = directoryMap{
+		"device":   true,
+		"hardware": true,
+		"kernel":   true,
+		"vendor":   true,
+	}
+
+	// Modules under following directories are included as they are in AOSP,
+	// although hardware/ and kernel/ are normally for vendor's own.
+	defaultDirectoryIncludedMap = directoryMap{
+		"kernel/configs":              true,
+		"kernel/prebuilts":            true,
+		"kernel/tests":                true,
+		"hardware/interfaces":         true,
+		"hardware/libhardware":        true,
+		"hardware/libhardware_legacy": true,
+		"hardware/ril":                true,
+	}
+)
+
+func isDirectoryExcluded(dir string, excludedMap directoryMap, includedMap directoryMap) bool {
+	if dir == "." || dir == "/" {
+		return false
+	}
+	if includedMap[dir] {
+		return false
+	} else if excludedMap[dir] {
+		return true
+	} else if defaultDirectoryIncludedMap[dir] {
+		return false
+	} else if defaultDirectoryExcludedMap[dir] {
+		return true
+	} else {
+		return isDirectoryExcluded(filepath.Dir(dir), excludedMap, includedMap)
+	}
+}
diff --git a/snapshot/util.go b/snapshot/util.go
new file mode 100644
index 0000000..2297dfc
--- /dev/null
+++ b/snapshot/util.go
@@ -0,0 +1,36 @@
+// Copyright 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package snapshot
+
+import "android/soong/android"
+
+func WriteStringToFileRule(ctx android.SingletonContext, content, out string) android.OutputPath {
+	outPath := android.PathForOutput(ctx, out)
+	android.WriteFileRule(ctx, outPath, content)
+	return outPath
+}
+
+func CopyFileRule(pctx android.PackageContext, ctx android.SingletonContext, path android.Path, out string) android.OutputPath {
+	outPath := android.PathForOutput(ctx, out)
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        android.Cp,
+		Input:       path,
+		Output:      outPath,
+		Description: "copy " + path.String() + " -> " + out,
+		Args: map[string]string{
+			"cpFlags": "-f -L",
+		},
+	})
+	return outPath
+}
diff --git a/snapshot/vendor_snapshot.go b/snapshot/vendor_snapshot.go
new file mode 100644
index 0000000..9bd26c2
--- /dev/null
+++ b/snapshot/vendor_snapshot.go
@@ -0,0 +1,147 @@
+// Copyright 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package snapshot
+
+import "android/soong/android"
+
+// Interface for modules which can be captured in the vendor snapshot.
+type VendorSnapshotModuleInterface interface {
+	SnapshotModuleInterfaceBase
+	InVendor() bool
+	ExcludeFromVendorSnapshot() bool
+}
+
+var vendorSnapshotSingleton = SnapshotSingleton{
+	"vendor",                     // name
+	"SOONG_VENDOR_SNAPSHOT_ZIP",  // makeVar
+	android.OptionalPath{},       // snapshotZipFile
+	VendorSnapshotImageSingleton, // Image
+	false,                        // Fake
+}
+
+var vendorFakeSnapshotSingleton = SnapshotSingleton{
+	"vendor",                         // name
+	"SOONG_VENDOR_FAKE_SNAPSHOT_ZIP", // makeVar
+	android.OptionalPath{},           // snapshotZipFile
+	VendorSnapshotImageSingleton,     // Image
+	true,                             // Fake
+}
+
+func VendorSnapshotSingleton() android.Singleton {
+	return &vendorSnapshotSingleton
+}
+
+func VendorFakeSnapshotSingleton() android.Singleton {
+	return &vendorFakeSnapshotSingleton
+}
+
+// Determine if a dir under source tree is an SoC-owned proprietary directory based
+// on vendor snapshot configuration
+// Examples: device/, vendor/
+func isVendorProprietaryPath(dir string, deviceConfig android.DeviceConfig) bool {
+	return VendorSnapshotSingleton().(*SnapshotSingleton).Image.IsProprietaryPath(dir, deviceConfig)
+}
+
+func IsVendorProprietaryModule(ctx android.BaseModuleContext) bool {
+	// Any module in a vendor proprietary path is a vendor proprietary
+	// module.
+	if isVendorProprietaryPath(ctx.ModuleDir(), ctx.DeviceConfig()) {
+		return true
+	}
+
+	// However if the module is not in a vendor proprietary path, it may
+	// still be a vendor proprietary module. This happens for cc modules
+	// that are excluded from the vendor snapshot, and it means that the
+	// vendor has assumed control of the framework-provided module.
+	if c, ok := ctx.Module().(VendorSnapshotModuleInterface); ok {
+		if c.ExcludeFromVendorSnapshot() {
+			return true
+		}
+	}
+
+	return false
+}
+
+var VendorSnapshotImageName = "vendor"
+
+type VendorSnapshotImage struct{}
+
+func (VendorSnapshotImage) Init(ctx android.RegistrationContext) {
+	ctx.RegisterSingletonType("vendor-snapshot", VendorSnapshotSingleton)
+	ctx.RegisterSingletonType("vendor-fake-snapshot", VendorFakeSnapshotSingleton)
+}
+
+func (VendorSnapshotImage) RegisterAdditionalModule(ctx android.RegistrationContext, name string, factory android.ModuleFactory) {
+	ctx.RegisterModuleType(name, factory)
+}
+
+func (VendorSnapshotImage) shouldGenerateSnapshot(ctx android.SingletonContext) bool {
+	// BOARD_VNDK_VERSION must be set to 'current' in order to generate a snapshot.
+	return ctx.DeviceConfig().VndkVersion() == "current"
+}
+
+func (VendorSnapshotImage) InImage(m SnapshotModuleInterfaceBase) func() bool {
+	v, ok := m.(VendorSnapshotModuleInterface)
+
+	if !ok {
+		// This module does not support Vendor snapshot
+		return func() bool { return false }
+	}
+
+	return v.InVendor
+}
+
+func (VendorSnapshotImage) IsProprietaryPath(dir string, deviceConfig android.DeviceConfig) bool {
+	return isDirectoryExcluded(dir, deviceConfig.VendorSnapshotDirsExcludedMap(), deviceConfig.VendorSnapshotDirsIncludedMap())
+}
+
+func (VendorSnapshotImage) ExcludeFromSnapshot(m SnapshotModuleInterfaceBase) bool {
+	v, ok := m.(VendorSnapshotModuleInterface)
+
+	if !ok {
+		// This module does not support Vendor snapshot
+		return true
+	}
+
+	return v.ExcludeFromVendorSnapshot()
+}
+
+func (VendorSnapshotImage) IsUsingSnapshot(cfg android.DeviceConfig) bool {
+	vndkVersion := cfg.VndkVersion()
+	return vndkVersion != "current" && vndkVersion != ""
+}
+
+func (VendorSnapshotImage) TargetSnapshotVersion(cfg android.DeviceConfig) string {
+	return cfg.VndkVersion()
+}
+
+// returns true iff a given module SHOULD BE EXCLUDED, false if included
+func (VendorSnapshotImage) ExcludeFromDirectedSnapshot(cfg android.DeviceConfig, name string) bool {
+	// If we're using full snapshot, not directed snapshot, capture every module
+	if !cfg.DirectedVendorSnapshot() {
+		return false
+	}
+	// Else, checks if name is in VENDOR_SNAPSHOT_MODULES.
+	return !cfg.VendorSnapshotModules()[name]
+}
+
+func (VendorSnapshotImage) ImageName() string {
+	return VendorSnapshotImageName
+}
+
+var VendorSnapshotImageSingleton VendorSnapshotImage
+
+func init() {
+	VendorSnapshotImageSingleton.Init(android.InitRegistrationContext)
+}
diff --git a/sysprop/sysprop_library.go b/sysprop/sysprop_library.go
index f1c2d0d..a29d4c3 100644
--- a/sysprop/sysprop_library.go
+++ b/sysprop/sysprop_library.go
@@ -145,6 +145,9 @@
 	// If set to true, allow this module to be dexed and installed on devices.
 	Installable *bool
 
+	// Make this module available when building for ramdisk
+	Ramdisk_available *bool
+
 	// Make this module available when building for recovery
 	Recovery_available *bool
 
@@ -396,6 +399,7 @@
 	Recovery_available *bool
 	Vendor_available   *bool
 	Product_available  *bool
+	Ramdisk_available  *bool
 	Host_supported     *bool
 	Apex_available     []string
 	Min_sdk_version    *string
@@ -475,6 +479,7 @@
 	ccProps.Recovery_available = m.properties.Recovery_available
 	ccProps.Vendor_available = m.properties.Vendor_available
 	ccProps.Product_available = m.properties.Product_available
+	ccProps.Ramdisk_available = m.properties.Ramdisk_available
 	ccProps.Host_supported = m.properties.Host_supported
 	ccProps.Apex_available = m.ApexProperties.Apex_available
 	ccProps.Min_sdk_version = m.properties.Cpp.Min_sdk_version
diff --git a/tests/bootstrap_test.sh b/tests/bootstrap_test.sh
index 3f51114..b37a7f8 100755
--- a/tests/bootstrap_test.sh
+++ b/tests/bootstrap_test.sh
@@ -7,6 +7,8 @@
 
 source "$(dirname "$0")/lib.sh"
 
+readonly GENERATED_BUILD_FILE_NAME="BUILD.bazel"
+
 function test_smoke {
   setup
   run_soong
@@ -142,7 +144,7 @@
   run_soong
   local ninja_mtime1=$(stat -c "%y" out/soong/build.ninja)
 
-  local glob_deps_file=out/soong/.primary/globs/0.d
+  local glob_deps_file=out/soong/globs/build/0.d
 
   if [ -e "$glob_deps_file" ]; then
     fail "Glob deps file unexpectedly written on first build"
@@ -475,7 +477,8 @@
   run_soong
   local mtime1=$(stat -c "%y" out/soong/build.ninja)
 
-  prebuilts/build-tools/linux-x86/bin/ninja -f out/soong/build.ninja soong_docs
+  prebuilts/build-tools/linux-x86/bin/ninja -f out/combined.ninja soong_docs
+
   run_soong
   local mtime2=$(stat -c "%y" out/soong/build.ninja)
 
@@ -484,6 +487,39 @@
   fi
 }
 
+function test_write_to_source_tree {
+  setup
+  mkdir -p a
+  cat > a/Android.bp <<EOF
+genrule {
+  name: "write_to_source_tree",
+  out: ["write_to_source_tree"],
+  cmd: "touch file_in_source_tree && touch \$(out)",
+}
+EOF
+  readonly EXPECTED_OUT=out/soong/.intermediates/a/write_to_source_tree/gen/write_to_source_tree
+  readonly ERROR_LOG=${MOCK_TOP}/out/error.log
+  readonly ERROR_MSG="Read-only file system"
+  readonly ERROR_HINT_PATTERN="BUILD_BROKEN_SRC_DIR"
+  # Test in ReadOnly source tree
+  run_ninja BUILD_BROKEN_SRC_DIR_IS_WRITABLE=false ${EXPECTED_OUT} &> /dev/null && \
+    fail "Write to source tree should not work in a ReadOnly source tree"
+
+  if grep -q "${ERROR_MSG}" ${ERROR_LOG} && grep -q "${ERROR_HINT_PATTERN}" ${ERROR_LOG} ; then
+    echo Error message and error hint found in logs >/dev/null
+  else
+    fail "Did not find Read-only error AND error hint in error.log"
+  fi
+
+  # Test in ReadWrite source tree
+  run_ninja BUILD_BROKEN_SRC_DIR_IS_WRITABLE=true ${EXPECTED_OUT} &> /dev/null || \
+    fail "Write to source tree did not succeed in a ReadWrite source tree"
+
+  if  grep -q "${ERROR_MSG}\|${ERROR_HINT_PATTERN}" ${ERROR_LOG} ; then
+    fail "Found read-only error OR error hint in error.log"
+  fi
+}
+
 function test_bp2build_smoke {
   setup
   GENERATE_BAZEL_FILES=1 run_soong
@@ -491,6 +527,17 @@
   [[ -e out/soong/workspace ]] || fail "Bazel workspace not created"
 }
 
+function test_bp2build_generates_marker_file {
+  setup
+  create_mock_bazel
+
+  run_bp2build
+
+  if [[ ! -f "./out/soong/.bootstrap/bp2build_workspace_marker" ]]; then
+    fail "Marker file was not generated"
+  fi
+}
+
 function test_bp2build_add_android_bp {
   setup
 
@@ -505,8 +552,8 @@
 EOF
 
   GENERATE_BAZEL_FILES=1 run_soong
-  [[ -e out/soong/bp2build/a/BUILD ]] || fail "a/BUILD not created"
-  [[ -L out/soong/workspace/a/BUILD ]] || fail "a/BUILD not symlinked"
+  [[ -e out/soong/bp2build/a/${GENERATED_BUILD_FILE_NAME} ]] || fail "a/${GENERATED_BUILD_FILE_NAME} not created"
+  [[ -L out/soong/workspace/a/${GENERATED_BUILD_FILE_NAME} ]] || fail "a/${GENERATED_BUILD_FILE_NAME} not symlinked"
 
   mkdir -p b
   touch b/b.txt
@@ -519,18 +566,18 @@
 EOF
 
   GENERATE_BAZEL_FILES=1 run_soong
-  [[ -e out/soong/bp2build/b/BUILD ]] || fail "a/BUILD not created"
-  [[ -L out/soong/workspace/b/BUILD ]] || fail "a/BUILD not symlinked"
+  [[ -e out/soong/bp2build/b/${GENERATED_BUILD_FILE_NAME} ]] || fail "a/${GENERATED_BUILD_FILE_NAME} not created"
+  [[ -L out/soong/workspace/b/${GENERATED_BUILD_FILE_NAME} ]] || fail "a/${GENERATED_BUILD_FILE_NAME} not symlinked"
 }
 
 function test_bp2build_null_build {
   setup
 
   GENERATE_BAZEL_FILES=1 run_soong
-  local mtime1=$(stat -c "%y" out/soong/build.ninja)
+  local mtime1=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
 
   GENERATE_BAZEL_FILES=1 run_soong
-  local mtime2=$(stat -c "%y" out/soong/build.ninja)
+  local mtime2=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
 
   if [[ "$mtime1" != "$mtime2" ]]; then
     fail "Output Ninja file changed on null build"
@@ -551,21 +598,45 @@
 EOF
 
   GENERATE_BAZEL_FILES=1 run_soong
-  grep -q a1.txt out/soong/bp2build/a/BUILD || fail "a1.txt not in BUILD file"
+  grep -q a1.txt "out/soong/bp2build/a/${GENERATED_BUILD_FILE_NAME}" || fail "a1.txt not in ${GENERATED_BUILD_FILE_NAME} file"
 
   touch a/a2.txt
   GENERATE_BAZEL_FILES=1 run_soong
-  grep -q a2.txt out/soong/bp2build/a/BUILD || fail "a2.txt not in BUILD file"
+  grep -q a2.txt "out/soong/bp2build/a/${GENERATED_BUILD_FILE_NAME}" || fail "a2.txt not in ${GENERATED_BUILD_FILE_NAME} file"
 }
 
 function test_dump_json_module_graph() {
   setup
-  SOONG_DUMP_JSON_MODULE_GRAPH="$MOCK_TOP/modules.json" run_soong
-  if [[ ! -r "$MOCK_TOP/modules.json" ]]; then
+  GENERATE_JSON_MODULE_GRAPH=1 run_soong
+  if [[ ! -r "out/soong//module-graph.json" ]]; then
     fail "JSON file was not created"
   fi
 }
 
+function test_json_module_graph_back_and_forth_null_build() {
+  setup
+
+  run_soong
+  local ninja_mtime1=$(stat -c "%y" out/soong/build.ninja)
+
+  GENERATE_JSON_MODULE_GRAPH=1 run_soong
+  local json_mtime1=$(stat -c "%y" out/soong/module-graph.json)
+
+  run_soong
+  local ninja_mtime2=$(stat -c "%y" out/soong/build.ninja)
+  if [[ "$ninja_mtime1" != "$ninja_mtime2" ]]; then
+    fail "Output Ninja file changed after writing JSON module graph"
+  fi
+
+  GENERATE_JSON_MODULE_GRAPH=1 run_soong
+  local json_mtime2=$(stat -c "%y" out/soong/module-graph.json)
+  if [[ "$json_mtime1" != "$json_mtime2" ]]; then
+    fail "JSON module graph file changed after writing Ninja file"
+  fi
+
+}
+
+
 function test_bp2build_bazel_workspace_structure {
   setup
 
@@ -583,8 +654,8 @@
   GENERATE_BAZEL_FILES=1 run_soong
   [[ -e out/soong/workspace ]] || fail "Bazel workspace not created"
   [[ -d out/soong/workspace/a/b ]] || fail "module directory not a directory"
-  [[ -L out/soong/workspace/a/b/BUILD ]] || fail "BUILD file not symlinked"
-  [[ "$(readlink -f out/soong/workspace/a/b/BUILD)" =~ bp2build/a/b/BUILD$ ]] \
+  [[ -L "out/soong/workspace/a/b/${GENERATED_BUILD_FILE_NAME}" ]] || fail "${GENERATED_BUILD_FILE_NAME} file not symlinked"
+  [[ "$(readlink -f out/soong/workspace/a/b/${GENERATED_BUILD_FILE_NAME})" =~ "bp2build/a/b/${GENERATED_BUILD_FILE_NAME}"$ ]] \
     || fail "BUILD files symlinked at the wrong place"
   [[ -L out/soong/workspace/a/b/b.txt ]] || fail "a/b/b.txt not symlinked"
   [[ -L out/soong/workspace/a/a.txt ]] || fail "a/b/a.txt not symlinked"
@@ -616,7 +687,7 @@
 
   mkdir -p a
   touch a/a.txt
-  touch a/BUILD
+  touch a/${GENERATED_BUILD_FILE_NAME}
   cat > a/Android.bp <<EOF
 filegroup {
   name: "a",
@@ -626,15 +697,15 @@
 EOF
 
   GENERATE_BAZEL_FILES=1 run_soong
-  [[ -L out/soong/workspace/a/BUILD ]] || fail "BUILD file not symlinked"
-  [[ "$(readlink -f out/soong/workspace/a/BUILD)" =~ bp2build/a/BUILD$ ]] \
-    || fail "BUILD files symlinked to the wrong place"
+  [[ -L "out/soong/workspace/a/${GENERATED_BUILD_FILE_NAME}" ]] || fail "${GENERATED_BUILD_FILE_NAME} file not symlinked"
+  [[ "$(readlink -f out/soong/workspace/a/${GENERATED_BUILD_FILE_NAME})" =~ "bp2build/a/${GENERATED_BUILD_FILE_NAME}"$ ]] \
+    || fail "${GENERATED_BUILD_FILE_NAME} files symlinked to the wrong place"
 }
 
 function test_bp2build_reports_multiple_errors {
   setup
 
-  mkdir -p a/BUILD
+  mkdir -p "a/${GENERATED_BUILD_FILE_NAME}"
   touch a/a.txt
   cat > a/Android.bp <<EOF
 filegroup {
@@ -644,7 +715,7 @@
 }
 EOF
 
-  mkdir -p b/BUILD
+  mkdir -p "b/${GENERATED_BUILD_FILE_NAME}"
   touch b/b.txt
   cat > b/Android.bp <<EOF
 filegroup {
@@ -658,8 +729,43 @@
     fail "Build should have failed"
   fi
 
-  grep -q "a/BUILD' exist" "$MOCK_TOP/errors" || fail "Error for a/BUILD not found"
-  grep -q "b/BUILD' exist" "$MOCK_TOP/errors" || fail "Error for b/BUILD not found"
+  grep -q "a/${GENERATED_BUILD_FILE_NAME}' exist" "$MOCK_TOP/errors" || fail "Error for a/${GENERATED_BUILD_FILE_NAME} not found"
+  grep -q "b/${GENERATED_BUILD_FILE_NAME}' exist" "$MOCK_TOP/errors" || fail "Error for b/${GENERATED_BUILD_FILE_NAME} not found"
+}
+
+function test_bp2build_back_and_forth_null_build {
+  setup
+
+  run_soong
+  local output_mtime1=$(stat -c "%y" out/soong/build.ninja)
+
+  GENERATE_BAZEL_FILES=1 run_soong
+  local output_mtime2=$(stat -c "%y" out/soong/build.ninja)
+  if [[ "$output_mtime1" != "$output_mtime2" ]]; then
+    fail "Output Ninja file changed when switching to bp2build"
+  fi
+
+  local marker_mtime1=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
+
+  run_soong
+  local output_mtime3=$(stat -c "%y" out/soong/build.ninja)
+  local marker_mtime2=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
+  if [[ "$output_mtime1" != "$output_mtime3" ]]; then
+    fail "Output Ninja file changed when switching to regular build from bp2build"
+  fi
+  if [[ "$marker_mtime1" != "$marker_mtime2" ]]; then
+    fail "bp2build marker file changed when switching to regular build from bp2build"
+  fi
+
+  GENERATE_BAZEL_FILES=1 run_soong
+  local output_mtime4=$(stat -c "%y" out/soong/build.ninja)
+  local marker_mtime3=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
+  if [[ "$output_mtime1" != "$output_mtime4" ]]; then
+    fail "Output Ninja file changed when switching back to bp2build"
+  fi
+  if [[ "$marker_mtime1" != "$marker_mtime3" ]]; then
+    fail "bp2build marker file changed when switching back to bp2build"
+  fi
 }
 
 test_smoke
@@ -675,8 +781,12 @@
 test_glob_during_bootstrapping
 test_soong_build_rerun_iff_environment_changes
 test_dump_json_module_graph
+test_json_module_graph_back_and_forth_null_build
+test_write_to_source_tree
 test_bp2build_smoke
+test_bp2build_generates_marker_file
 test_bp2build_null_build
+test_bp2build_back_and_forth_null_build
 test_bp2build_add_android_bp
 test_bp2build_add_to_glob
 test_bp2build_bazel_workspace_structure
diff --git a/tests/bp2build_bazel_test.sh b/tests/bp2build_bazel_test.sh
index 082cd06..9bd85a4 100755
--- a/tests/bp2build_bazel_test.sh
+++ b/tests/bp2build_bazel_test.sh
@@ -6,6 +6,48 @@
 
 source "$(dirname "$0")/lib.sh"
 
+readonly GENERATED_BUILD_FILE_NAME="BUILD.bazel"
+
+function test_bp2build_null_build() {
+  setup
+  run_bp2build
+  local output_mtime1=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
+
+  run_bp2build
+  local output_mtime2=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
+
+  if [[ "$output_mtime1" != "$output_mtime2" ]]; then
+    fail "Output bp2build marker file changed on null build"
+  fi
+}
+
+test_bp2build_null_build
+
+function test_bp2build_null_build_with_globs() {
+  setup
+
+  mkdir -p foo/bar
+  cat > foo/bar/Android.bp <<'EOF'
+filegroup {
+    name: "globs",
+    srcs: ["*.txt"],
+  }
+EOF
+  touch foo/bar/a.txt foo/bar/b.txt
+
+  run_bp2build
+  local output_mtime1=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
+
+  run_bp2build
+  local output_mtime2=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
+
+  if [[ "$output_mtime1" != "$output_mtime2" ]]; then
+    fail "Output bp2build marker file changed on null build"
+  fi
+}
+
+test_bp2build_null_build_with_globs
+
 function test_bp2build_generates_all_buildfiles {
   setup
   create_mock_bazel
@@ -40,24 +82,24 @@
 
   run_bp2build
 
-  if [[ ! -f "./out/soong/workspace/foo/convertible_soong_module/BUILD" ]]; then
-    fail "./out/soong/workspace/foo/convertible_soong_module/BUILD was not generated"
+  if [[ ! -f "./out/soong/workspace/foo/convertible_soong_module/${GENERATED_BUILD_FILE_NAME}" ]]; then
+    fail "./out/soong/workspace/foo/convertible_soong_module/${GENERATED_BUILD_FILE_NAME} was not generated"
   fi
 
-  if [[ ! -f "./out/soong/workspace/foo/unconvertible_soong_module/BUILD" ]]; then
-    fail "./out/soong/workspace/foo/unconvertible_soong_module/BUILD was not generated"
+  if [[ ! -f "./out/soong/workspace/foo/unconvertible_soong_module/${GENERATED_BUILD_FILE_NAME}" ]]; then
+    fail "./out/soong/workspace/foo/unconvertible_soong_module/${GENERATED_BUILD_FILE_NAME} was not generated"
   fi
 
-  if ! grep "the_answer" "./out/soong/workspace/foo/convertible_soong_module/BUILD"; then
-    fail "missing BUILD target the_answer in convertible_soong_module/BUILD"
+  if ! grep "the_answer" "./out/soong/workspace/foo/convertible_soong_module/${GENERATED_BUILD_FILE_NAME}"; then
+    fail "missing BUILD target the_answer in convertible_soong_module/${GENERATED_BUILD_FILE_NAME}"
   fi
 
-  if grep "not_the_answer" "./out/soong/workspace/foo/unconvertible_soong_module/BUILD"; then
-    fail "found unexpected BUILD target not_the_answer in unconvertible_soong_module/BUILD"
+  if grep "not_the_answer" "./out/soong/workspace/foo/unconvertible_soong_module/${GENERATED_BUILD_FILE_NAME}"; then
+    fail "found unexpected BUILD target not_the_answer in unconvertible_soong_module/${GENERATED_BUILD_FILE_NAME}"
   fi
 
-  if ! grep "filegroup" "./out/soong/workspace/foo/unconvertible_soong_module/BUILD"; then
-    fail "missing filegroup in unconvertible_soong_module/BUILD"
+  if ! grep "filegroup" "./out/soong/workspace/foo/unconvertible_soong_module/${GENERATED_BUILD_FILE_NAME}"; then
+    fail "missing filegroup in unconvertible_soong_module/${GENERATED_BUILD_FILE_NAME}"
   fi
 
   # NOTE: We don't actually use the extra BUILD file for anything here
diff --git a/tests/lib.sh b/tests/lib.sh
index e561a3d..813a9dd 100644
--- a/tests/lib.sh
+++ b/tests/lib.sh
@@ -86,6 +86,7 @@
 
   symlink_directory prebuilts/go
   symlink_directory prebuilts/build-tools
+  symlink_directory external/go-cmp
   symlink_directory external/golang-protobuf
 
   touch "$MOCK_TOP/Android.bp"
@@ -112,8 +113,10 @@
 
   symlink_directory prebuilts/bazel
   symlink_directory prebuilts/jdk
+  symlink_directory external/bazel-skylib
 
   symlink_file WORKSPACE
+  symlink_file BUILD
   symlink_file tools/bazel
 }
 
@@ -125,6 +128,10 @@
   GENERATE_BAZEL_FILES=true build/soong/soong_ui.bash --make-mode --skip-ninja --skip-make --skip-soong-tests nothing
 }
 
+run_ninja() {
+  build/soong/soong_ui.bash --make-mode --skip-make --skip-soong-tests "$@"
+}
+
 info "Starting Soong integration test suite $(basename $0)"
 info "Mock top: $MOCK_TOP"
 
diff --git a/tests/mixed_mode_test.sh b/tests/mixed_mode_test.sh
index 80774bf..b408fd3 100755
--- a/tests/mixed_mode_test.sh
+++ b/tests/mixed_mode_test.sh
@@ -14,7 +14,7 @@
   setup
   create_mock_bazel
 
-  run_bazel info
+  STANDALONE_BAZEL=true run_bazel info
 }
 
 test_bazel_smoke
diff --git a/tests/run_integration_tests.sh b/tests/run_integration_tests.sh
index 8399573..6304a11 100755
--- a/tests/run_integration_tests.sh
+++ b/tests/run_integration_tests.sh
@@ -6,3 +6,4 @@
 "$TOP/build/soong/tests/bootstrap_test.sh"
 "$TOP/build/soong/tests/mixed_mode_test.sh"
 "$TOP/build/soong/tests/bp2build_bazel_test.sh"
+"$TOP/build/soong/tests/soong_test.sh"
diff --git a/tests/soong_test.sh b/tests/soong_test.sh
new file mode 100755
index 0000000..905d708
--- /dev/null
+++ b/tests/soong_test.sh
@@ -0,0 +1,22 @@
+#!/bin/bash -eu
+
+set -o pipefail
+
+# Tests of Soong functionality
+
+source "$(dirname "$0")/lib.sh"
+
+function test_m_clean_works {
+  setup
+
+  # Create a directory with files that cannot be removed
+  mkdir -p out/bad_directory_permissions
+  touch out/bad_directory_permissions/unremovable_file
+  # File permissions are fine but directory permissions are bad
+  chmod a+rwx out/bad_directory_permissions/unremovable_file
+  chmod a-rwx out/bad_directory_permissions
+
+  run_soong clean
+}
+
+test_m_clean_works
diff --git a/ui/build/Android.bp b/ui/build/Android.bp
index d17b464..3dc87f5 100644
--- a/ui/build/Android.bp
+++ b/ui/build/Android.bp
@@ -60,7 +60,7 @@
         "path.go",
         "proc_sync.go",
         "rbe.go",
-        "signal.go",
+        "sandbox_config.go",
         "soong.go",
         "test_build.go",
         "upload.go",
@@ -86,5 +86,8 @@
             "config_linux.go",
             "sandbox_linux.go",
         ],
+        testSrcs: [
+            "sandbox_linux_test.go",
+        ],
     },
 }
diff --git a/ui/build/build.go b/ui/build/build.go
index 8f050d9..d869bf0 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -28,16 +28,20 @@
 func SetupOutDir(ctx Context, config Config) {
 	ensureEmptyFileExists(ctx, filepath.Join(config.OutDir(), "Android.mk"))
 	ensureEmptyFileExists(ctx, filepath.Join(config.OutDir(), "CleanSpec.mk"))
-	if !config.SkipKati() {
-		// Run soong_build with Kati for a hybrid build, e.g. running the
-		// AndroidMk singleton and postinstall commands. Communicate this to
-		// soong_build by writing an empty .soong.kati_enabled marker file in the
-		// soong_build output directory for the soong_build primary builder to
-		// know if the user wants to run Kati after.
-		//
-		// This does not preclude running Kati for *product configuration purposes*.
-		ensureEmptyFileExists(ctx, filepath.Join(config.SoongOutDir(), ".soong.kati_enabled"))
+
+	// Potentially write a marker file for whether kati is enabled. This is used by soong_build to
+	// potentially run the AndroidMk singleton and postinstall commands.
+	// Note that the absence of the  file does not not preclude running Kati for product
+	// configuration purposes.
+	katiEnabledMarker := filepath.Join(config.SoongOutDir(), ".soong.kati_enabled")
+	if config.SkipKatiNinja() {
+		os.Remove(katiEnabledMarker)
+		// Note that we can not remove the file for SkipKati builds yet -- some continuous builds
+		// --skip-make builds rely on kati targets being defined.
+	} else if !config.SkipKati() {
+		ensureEmptyFileExists(ctx, katiEnabledMarker)
 	}
+
 	// The ninja_build file is used by our buildbots to understand that the output
 	// can be parsed as ninja output.
 	ensureEmptyFileExists(ctx, filepath.Join(config.OutDir(), "ninja_build"))
@@ -234,6 +238,11 @@
 		ctx.Verboseln("Skipping use of Kati ninja as requested")
 		what = what &^ RunKatiNinja
 	}
+	if config.SkipSoong() {
+		ctx.Verboseln("Skipping use of Soong as requested")
+		what = what &^ RunSoong
+	}
+
 	if config.SkipNinja() {
 		ctx.Verboseln("Skipping Ninja as requested")
 		what = what &^ RunNinja
@@ -274,6 +283,11 @@
 			// Return early, if we're using Soong as solely the generator of BUILD files.
 			return
 		}
+
+		if config.bazelBuildMode() == generateJsonModuleGraph {
+			// Return early, if we're using Soong as solely the generator of the JSON module graph
+			return
+		}
 	}
 
 	if what&RunKati != 0 {
diff --git a/ui/build/cleanbuild.go b/ui/build/cleanbuild.go
index 19b5690..65b91bc 100644
--- a/ui/build/cleanbuild.go
+++ b/ui/build/cleanbuild.go
@@ -17,6 +17,7 @@
 import (
 	"bytes"
 	"fmt"
+	"io/fs"
 	"io/ioutil"
 	"os"
 	"path/filepath"
@@ -46,9 +47,49 @@
 	}
 }
 
+// Based on https://stackoverflow.com/questions/28969455/how-to-properly-instantiate-os-filemode
+// Because Go doesn't provide a nice way to set bits on a filemode
+const (
+	FILEMODE_READ         = 04
+	FILEMODE_WRITE        = 02
+	FILEMODE_EXECUTE      = 01
+	FILEMODE_USER_SHIFT   = 6
+	FILEMODE_USER_READ    = FILEMODE_READ << FILEMODE_USER_SHIFT
+	FILEMODE_USER_WRITE   = FILEMODE_WRITE << FILEMODE_USER_SHIFT
+	FILEMODE_USER_EXECUTE = FILEMODE_EXECUTE << FILEMODE_USER_SHIFT
+)
+
+// Ensures that files and directories in the out dir can be deleted.
+// For example, Bazen can generate output directories where the write bit isn't set, causing 'm' clean' to fail.
+func ensureOutDirRemovable(ctx Context, config Config) {
+	err := filepath.WalkDir(config.OutDir(), func(path string, d fs.DirEntry, err error) error {
+		if err != nil {
+			return err
+		}
+		if d.IsDir() {
+			info, err := d.Info()
+			if err != nil {
+				return err
+			}
+			// Equivalent to running chmod u+rwx on each directory
+			newMode := info.Mode() | FILEMODE_USER_READ | FILEMODE_USER_WRITE | FILEMODE_USER_EXECUTE
+			if err := os.Chmod(path, newMode); err != nil {
+				return err
+			}
+		}
+		// Continue walking the out dir...
+		return nil
+	})
+	if err != nil && !os.IsNotExist(err) {
+		// Display the error, but don't crash.
+		ctx.Println(err.Error())
+	}
+}
+
 // Remove everything under the out directory. Don't remove the out directory
 // itself in case it's a symlink.
 func clean(ctx Context, config Config) {
+	ensureOutDirRemovable(ctx, config)
 	removeGlobs(ctx, filepath.Join(config.OutDir(), "*"))
 	ctx.Println("Entire build directory removed.")
 }
diff --git a/ui/build/config.go b/ui/build/config.go
index 862e09f..6de7a05 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -25,7 +25,7 @@
 
 	"android/soong/shared"
 
-	"github.com/golang/protobuf/proto"
+	"google.golang.org/protobuf/proto"
 
 	smpb "android/soong/ui/metrics/metrics_proto"
 )
@@ -49,6 +49,7 @@
 	skipConfig     bool
 	skipKati       bool
 	skipKatiNinja  bool
+	skipSoong      bool
 	skipNinja      bool
 	skipSoongTests bool
 
@@ -58,6 +59,7 @@
 	katiSuffix      string
 	targetDevice    string
 	targetDeviceDir string
+	sandboxConfig   *SandboxConfig
 
 	// Autodetected
 	totalRAM uint64
@@ -107,6 +109,9 @@
 	// Only generate build files (in a subdirectory of the out directory) and exit.
 	generateBuildFiles
 
+	// Only generate the Soong json module graph for use with jq, and exit.
+	generateJsonModuleGraph
+
 	// Generate synthetic build files and incorporate these files into a build which
 	// partially uses Bazel. Build metadata may come from Android.bp or BUILD files.
 	mixedBuild
@@ -124,7 +129,8 @@
 
 func NewConfig(ctx Context, args ...string) Config {
 	ret := &configImpl{
-		environ: OsEnvironment(),
+		environ:       OsEnvironment(),
+		sandboxConfig: &SandboxConfig{},
 	}
 
 	// Default matching ninja
@@ -156,6 +162,10 @@
 		ret.distDir = filepath.Join(ret.OutDir(), "dist")
 	}
 
+	if srcDirIsWritable, ok := ret.environ.Get("BUILD_BROKEN_SRC_DIR_IS_WRITABLE"); ok {
+		ret.sandboxConfig.SetSrcDirIsRO(srcDirIsWritable == "false")
+	}
+
 	ret.environ.Unset(
 		// We're already using it
 		"USE_SOONG_UI",
@@ -336,12 +346,7 @@
 		return
 	}
 
-	b := &smpb.BuildConfig{
-		ForceUseGoma: proto.Bool(config.ForceUseGoma()),
-		UseGoma:      proto.Bool(config.UseGoma()),
-		UseRbe:       proto.Bool(config.UseRBE()),
-	}
-	ctx.Metrics.BuildConfig(b)
+	ctx.Metrics.BuildConfig(buildConfig(config))
 
 	s := &smpb.SystemResourceInfo{
 		TotalPhysicalMemory: proto.Uint64(config.TotalRAM()),
@@ -350,6 +355,16 @@
 	ctx.Metrics.SystemResourceInfo(s)
 }
 
+func buildConfig(config Config) *smpb.BuildConfig {
+	return &smpb.BuildConfig{
+		ForceUseGoma:    proto.Bool(config.ForceUseGoma()),
+		UseGoma:         proto.Bool(config.UseGoma()),
+		UseRbe:          proto.Bool(config.UseRBE()),
+		BazelAsNinja:    proto.Bool(config.UseBazel()),
+		BazelMixedBuild: proto.Bool(config.bazelBuildMode() == mixedBuild),
+	}
+}
+
 // getConfigArgs processes the command arguments based on the build action and creates a set of new
 // arguments to be accepted by Config.
 func getConfigArgs(action BuildAction, dir string, ctx Context, args []string) []string {
@@ -568,9 +583,14 @@
 		arg := strings.TrimSpace(args[i])
 		if arg == "showcommands" {
 			c.verbose = true
+		} else if arg == "--empty-ninja-file" {
+			c.emptyNinjaFile = true
 		} else if arg == "--skip-ninja" {
 			c.skipNinja = true
 		} else if arg == "--skip-make" {
+			// TODO(ccross): deprecate this, it has confusing behaviors.  It doesn't run kati,
+			//   but it does run a Kati ninja file if the .kati_enabled marker file was created
+			//   by a previous build.
 			c.skipConfig = true
 			c.skipKati = true
 		} else if arg == "--skip-kati" {
@@ -579,6 +599,12 @@
 		} else if arg == "--soong-only" {
 			c.skipKati = true
 			c.skipKatiNinja = true
+		} else if arg == "--config-only" {
+			c.skipKati = true
+			c.skipKatiNinja = true
+			c.skipSoong = true
+		} else if arg == "--skip-config" {
+			c.skipConfig = true
 		} else if arg == "--skip-soong-tests" {
 			c.skipSoongTests = true
 		} else if len(arg) > 0 && arg[0] == '-' {
@@ -671,54 +697,6 @@
 	}
 }
 
-// Lunch configures the environment for a specific product similarly to the
-// `lunch` bash function.
-func (c *configImpl) Lunch(ctx Context, product, variant string) {
-	if variant != "eng" && variant != "userdebug" && variant != "user" {
-		ctx.Fatalf("Invalid variant %q. Must be one of 'user', 'userdebug' or 'eng'", variant)
-	}
-
-	c.environ.Set("TARGET_PRODUCT", product)
-	c.environ.Set("TARGET_BUILD_VARIANT", variant)
-	c.environ.Set("TARGET_BUILD_TYPE", "release")
-	c.environ.Unset("TARGET_BUILD_APPS")
-	c.environ.Unset("TARGET_BUILD_UNBUNDLED")
-}
-
-// Tapas configures the environment to build one or more unbundled apps,
-// similarly to the `tapas` bash function.
-func (c *configImpl) Tapas(ctx Context, apps []string, arch, variant string) {
-	if len(apps) == 0 {
-		apps = []string{"all"}
-	}
-	if variant == "" {
-		variant = "eng"
-	}
-
-	if variant != "eng" && variant != "userdebug" && variant != "user" {
-		ctx.Fatalf("Invalid variant %q. Must be one of 'user', 'userdebug' or 'eng'", variant)
-	}
-
-	var product string
-	switch arch {
-	case "arm", "":
-		product = "aosp_arm"
-	case "arm64":
-		product = "aosm_arm64"
-	case "x86":
-		product = "aosp_x86"
-	case "x86_64":
-		product = "aosp_x86_64"
-	default:
-		ctx.Fatalf("Invalid architecture: %q", arch)
-	}
-
-	c.environ.Set("TARGET_PRODUCT", product)
-	c.environ.Set("TARGET_BUILD_VARIANT", variant)
-	c.environ.Set("TARGET_BUILD_TYPE", "release")
-	c.environ.Set("TARGET_BUILD_APPS", strings.Join(apps, " "))
-}
-
 func (c *configImpl) Environment() *Environment {
 	return c.environ
 }
@@ -761,6 +739,32 @@
 	return filepath.Join(c.OutDir(), "soong")
 }
 
+func (c *configImpl) PrebuiltOS() string {
+	switch runtime.GOOS {
+	case "linux":
+		return "linux-x86"
+	case "darwin":
+		return "darwin-x86"
+	default:
+		panic("Unknown GOOS")
+	}
+}
+func (c *configImpl) HostToolDir() string {
+	return filepath.Join(c.SoongOutDir(), "host", c.PrebuiltOS(), "bin")
+}
+
+func (c *configImpl) MainNinjaFile() string {
+	return shared.JoinPath(c.SoongOutDir(), "build.ninja")
+}
+
+func (c *configImpl) Bp2BuildMarkerFile() string {
+	return shared.JoinPath(c.SoongOutDir(), ".bootstrap/bp2build_workspace_marker")
+}
+
+func (c *configImpl) ModuleGraphFile() string {
+	return shared.JoinPath(c.SoongOutDir(), "module-graph.json")
+}
+
 func (c *configImpl) TempDir() string {
 	return shared.TempDirForOutDir(c.SoongOutDir())
 }
@@ -798,6 +802,10 @@
 	return c.skipKatiNinja
 }
 
+func (c *configImpl) SkipSoong() bool {
+	return c.skipSoong
+}
+
 func (c *configImpl) SkipNinja() bool {
 	return c.skipNinja
 }
@@ -929,6 +937,8 @@
 		return mixedBuild
 	} else if c.Environment().IsEnvTrue("GENERATE_BAZEL_FILES") {
 		return generateBuildFiles
+	} else if c.Environment().IsEnvTrue("GENERATE_JSON_MODULE_GRAPH") {
+		return generateJsonModuleGraph
 	} else {
 		return noBazel
 	}
diff --git a/ui/build/config_test.go b/ui/build/config_test.go
index 7b14c47..1f2158b 100644
--- a/ui/build/config_test.go
+++ b/ui/build/config_test.go
@@ -26,7 +26,10 @@
 	"testing"
 
 	"android/soong/ui/logger"
+	smpb "android/soong/ui/metrics/metrics_proto"
 	"android/soong/ui/status"
+
+	"google.golang.org/protobuf/proto"
 )
 
 func testContext() Context {
@@ -995,3 +998,111 @@
 		})
 	}
 }
+
+func TestBuildConfig(t *testing.T) {
+	tests := []struct {
+		name                string
+		environ             Environment
+		useBazel            bool
+		expectedBuildConfig *smpb.BuildConfig
+	}{
+		{
+			name:    "none set",
+			environ: Environment{},
+			expectedBuildConfig: &smpb.BuildConfig{
+				ForceUseGoma:    proto.Bool(false),
+				UseGoma:         proto.Bool(false),
+				UseRbe:          proto.Bool(false),
+				BazelAsNinja:    proto.Bool(false),
+				BazelMixedBuild: proto.Bool(false),
+			},
+		},
+		{
+			name:    "force use goma",
+			environ: Environment{"FORCE_USE_GOMA=1"},
+			expectedBuildConfig: &smpb.BuildConfig{
+				ForceUseGoma:    proto.Bool(true),
+				UseGoma:         proto.Bool(false),
+				UseRbe:          proto.Bool(false),
+				BazelAsNinja:    proto.Bool(false),
+				BazelMixedBuild: proto.Bool(false),
+			},
+		},
+		{
+			name:    "use goma",
+			environ: Environment{"USE_GOMA=1"},
+			expectedBuildConfig: &smpb.BuildConfig{
+				ForceUseGoma:    proto.Bool(false),
+				UseGoma:         proto.Bool(true),
+				UseRbe:          proto.Bool(false),
+				BazelAsNinja:    proto.Bool(false),
+				BazelMixedBuild: proto.Bool(false),
+			},
+		},
+		{
+			name:    "use rbe",
+			environ: Environment{"USE_RBE=1"},
+			expectedBuildConfig: &smpb.BuildConfig{
+				ForceUseGoma:    proto.Bool(false),
+				UseGoma:         proto.Bool(false),
+				UseRbe:          proto.Bool(true),
+				BazelAsNinja:    proto.Bool(false),
+				BazelMixedBuild: proto.Bool(false),
+			},
+		},
+		{
+			name:     "use bazel as ninja",
+			environ:  Environment{},
+			useBazel: true,
+			expectedBuildConfig: &smpb.BuildConfig{
+				ForceUseGoma:    proto.Bool(false),
+				UseGoma:         proto.Bool(false),
+				UseRbe:          proto.Bool(false),
+				BazelAsNinja:    proto.Bool(true),
+				BazelMixedBuild: proto.Bool(false),
+			},
+		},
+		{
+			name:    "bazel mixed build",
+			environ: Environment{"USE_BAZEL_ANALYSIS=1"},
+			expectedBuildConfig: &smpb.BuildConfig{
+				ForceUseGoma:    proto.Bool(false),
+				UseGoma:         proto.Bool(false),
+				UseRbe:          proto.Bool(false),
+				BazelAsNinja:    proto.Bool(false),
+				BazelMixedBuild: proto.Bool(true),
+			},
+		},
+		{
+			name: "all set",
+			environ: Environment{
+				"FORCE_USE_GOMA=1",
+				"USE_GOMA=1",
+				"USE_RBE=1",
+				"USE_BAZEL_ANALYSIS=1",
+			},
+			useBazel: true,
+			expectedBuildConfig: &smpb.BuildConfig{
+				ForceUseGoma:    proto.Bool(true),
+				UseGoma:         proto.Bool(true),
+				UseRbe:          proto.Bool(true),
+				BazelAsNinja:    proto.Bool(true),
+				BazelMixedBuild: proto.Bool(true),
+			},
+		},
+	}
+
+	for _, tc := range tests {
+		t.Run(tc.name, func(t *testing.T) {
+			c := &configImpl{
+				environ:  &tc.environ,
+				useBazel: tc.useBazel,
+			}
+			config := Config{c}
+			actualBuildConfig := buildConfig(config)
+			if expected := tc.expectedBuildConfig; !proto.Equal(expected, actualBuildConfig) {
+				t.Errorf("Expected build config != actual build config: %#v != %#v", *expected, *actualBuildConfig)
+			}
+		})
+	}
+}
diff --git a/ui/build/context.go b/ui/build/context.go
index 43e1e0f..f5e987e 100644
--- a/ui/build/context.go
+++ b/ui/build/context.go
@@ -20,7 +20,7 @@
 
 	"android/soong/ui/logger"
 	"android/soong/ui/metrics"
-	"android/soong/ui/metrics/metrics_proto"
+	soong_metrics_proto "android/soong/ui/metrics/metrics_proto"
 	"android/soong/ui/status"
 	"android/soong/ui/tracer"
 )
diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go
index 54aeda0..3d16073 100644
--- a/ui/build/dumpvars.go
+++ b/ui/build/dumpvars.go
@@ -163,6 +163,7 @@
 	"AUX_OS_VARIANT_LIST",
 	"PRODUCT_SOONG_NAMESPACES",
 	"SOONG_SDK_SNAPSHOT_PREFER",
+	"SOONG_SDK_SNAPSHOT_USE_SOURCE_CONFIG_VAR",
 	"SOONG_SDK_SNAPSHOT_VERSION",
 }
 
@@ -225,6 +226,10 @@
 		// Extra environment variables to be exported to ninja
 		"BUILD_BROKEN_NINJA_USES_ENV_VARS",
 
+		// Used to restrict write access to source tree
+		"BUILD_BROKEN_SRC_DIR_IS_WRITABLE",
+		"BUILD_BROKEN_SRC_DIR_RW_ALLOWLIST",
+
 		// Not used, but useful to be in the soong.log
 		"BOARD_VNDK_VERSION",
 
@@ -256,6 +261,12 @@
 		"BUILD_BROKEN_USES_BUILD_STATIC_LIBRARY",
 	}, exportEnvVars...), BannerVars...)
 
+	// We need Roboleaf converter and runner in the mixed mode
+	runMicrofactory(ctx, config, ".bootstrap/bin/mk2rbc", "android/soong/mk2rbc/cmd",
+		map[string]string{"android/soong": "build/soong"})
+	runMicrofactory(ctx, config, ".bootstrap/bin/rbcrun", "rbcrun/cmd",
+		map[string]string{"go.starlark.net": "external/starlark-go", "rbcrun": "build/make/tools/rbcrun"})
+
 	makeVars, err := dumpMakeVars(ctx, config, config.Arguments(), allVars, true, "")
 	if err != nil {
 		ctx.Fatalln("Error dumping make vars:", err)
@@ -280,6 +291,8 @@
 	config.SetNinjaArgs(strings.Fields(makeVars["NINJA_GOALS"]))
 	config.SetTargetDevice(makeVars["TARGET_DEVICE"])
 	config.SetTargetDeviceDir(makeVars["TARGET_DEVICE_DIR"])
+	config.sandboxConfig.SetSrcDirIsRO(makeVars["BUILD_BROKEN_SRC_DIR_IS_WRITABLE"] == "false")
+	config.sandboxConfig.SetSrcDirRWAllowlist(strings.Fields(makeVars["BUILD_BROKEN_SRC_DIR_RW_ALLOWLIST"]))
 
 	config.SetBuildBrokenDupRules(makeVars["BUILD_BROKEN_DUP_RULES"] == "true")
 	config.SetBuildBrokenUsesNetwork(makeVars["BUILD_BROKEN_USES_NETWORK"] == "true")
diff --git a/ui/build/finder.go b/ui/build/finder.go
index 2eb84ca..09d53cc 100644
--- a/ui/build/finder.go
+++ b/ui/build/finder.go
@@ -76,6 +76,8 @@
 			"Blueprints",
 			// Bazel build definitions.
 			"BUILD.bazel",
+			// Bazel build definitions.
+			"BUILD",
 			// Kati clean definitions.
 			"CleanSpec.mk",
 			// Ownership definition.
@@ -102,7 +104,7 @@
 func findBazelFiles(entries finder.DirEntries) (dirNames []string, fileNames []string) {
 	matches := []string{}
 	for _, foundName := range entries.FileNames {
-		if foundName == "BUILD.bazel" || foundName == "WORKSPACE" || strings.HasSuffix(foundName, ".bzl") {
+		if foundName == "BUILD.bazel" || foundName == "BUILD" || foundName == "WORKSPACE" || strings.HasSuffix(foundName, ".bzl") {
 			matches = append(matches, foundName)
 		}
 	}
diff --git a/ui/build/sandbox_config.go b/ui/build/sandbox_config.go
new file mode 100644
index 0000000..1b46459
--- /dev/null
+++ b/ui/build/sandbox_config.go
@@ -0,0 +1,36 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package build
+
+type SandboxConfig struct {
+	srcDirIsRO        bool
+	srcDirRWAllowlist []string
+}
+
+func (sc *SandboxConfig) SetSrcDirIsRO(ro bool) {
+	sc.srcDirIsRO = ro
+}
+
+func (sc *SandboxConfig) SrcDirIsRO() bool {
+	return sc.srcDirIsRO
+}
+
+func (sc *SandboxConfig) SetSrcDirRWAllowlist(allowlist []string) {
+	sc.srcDirRWAllowlist = allowlist
+}
+
+func (sc *SandboxConfig) SrcDirRWAllowlist() []string {
+	return sc.srcDirRWAllowlist
+}
diff --git a/ui/build/sandbox_linux.go b/ui/build/sandbox_linux.go
index dab0e75..5b2046e 100644
--- a/ui/build/sandbox_linux.go
+++ b/ui/build/sandbox_linux.go
@@ -97,8 +97,11 @@
 			"-u", "nobody",
 			"-g", sandboxConfig.group,
 			"-R", "/",
-			"-B", sandboxConfig.srcDir,
+			// Mount tmp before srcDir
+			// srcDir is /tmp/.* in integration tests, which is a child dir of /tmp
+			// nsjail throws an error if a child dir is mounted before its parent
 			"-B", "/tmp",
+			"-B", sandboxConfig.srcDir,
 			"-B", sandboxConfig.outDir,
 		}
 
@@ -145,6 +148,13 @@
 func (c *Cmd) wrapSandbox() {
 	wd, _ := os.Getwd()
 
+	var srcDirMountFlag string
+	if c.config.sandboxConfig.SrcDirIsRO() {
+		srcDirMountFlag = "-R"
+	} else {
+		srcDirMountFlag = "-B" //Read-Write
+	}
+
 	sandboxArgs := []string{
 		// The executable to run
 		"-x", c.Path,
@@ -184,8 +194,8 @@
 		// Mount a writable tmp dir
 		"-B", "/tmp",
 
-		// Mount source are read-write
-		"-B", sandboxConfig.srcDir,
+		// Mount source
+		srcDirMountFlag, sandboxConfig.srcDir,
 
 		//Mount out dir as read-write
 		"-B", sandboxConfig.outDir,
@@ -198,6 +208,18 @@
 		"-q",
 	}
 
+	// Mount srcDir RW allowlists as Read-Write
+	if len(c.config.sandboxConfig.SrcDirRWAllowlist()) > 0 && !c.config.sandboxConfig.SrcDirIsRO() {
+		errMsg := `Product source tree has been set as ReadWrite, RW allowlist not necessary.
+			To recover, either
+			1. Unset BUILD_BROKEN_SRC_DIR_IS_WRITABLE #or
+			2. Unset BUILD_BROKEN_SRC_DIR_RW_ALLOWLIST`
+		c.ctx.Fatalln(errMsg)
+	}
+	for _, srcDirChild := range c.config.sandboxConfig.SrcDirRWAllowlist() {
+		sandboxArgs = append(sandboxArgs, "-B", srcDirChild)
+	}
+
 	if _, err := os.Stat(sandboxConfig.distDir); !os.IsNotExist(err) {
 		//Mount dist dir as read-write if it already exists
 		sandboxArgs = append(sandboxArgs, "-B", sandboxConfig.distDir)
diff --git a/ui/build/sandbox_linux_test.go b/ui/build/sandbox_linux_test.go
new file mode 100644
index 0000000..7bfd750
--- /dev/null
+++ b/ui/build/sandbox_linux_test.go
@@ -0,0 +1,104 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package build
+
+import (
+	"os"
+	"testing"
+)
+
+func TestMain(m *testing.M) {
+	// set src dir of sandbox
+	sandboxConfig.srcDir = "/my/src/dir"
+	os.Exit(m.Run())
+}
+
+func TestMountFlagsSrcDir(t *testing.T) {
+	testCases := []struct {
+		srcDirIsRO         bool
+		expectedSrcDirFlag string
+	}{
+		{
+			srcDirIsRO:         false,
+			expectedSrcDirFlag: "-B",
+		},
+		{
+			srcDirIsRO:         true,
+			expectedSrcDirFlag: "-R",
+		},
+	}
+	for _, testCase := range testCases {
+		c := testCmd()
+		c.config.sandboxConfig.SetSrcDirIsRO(testCase.srcDirIsRO)
+		c.wrapSandbox()
+		if !isExpectedMountFlag(c.Args, sandboxConfig.srcDir, testCase.expectedSrcDirFlag) {
+			t.Error("Mount flag of srcDir is not correct")
+		}
+	}
+}
+
+func TestMountFlagsSrcDirRWAllowlist(t *testing.T) {
+	testCases := []struct {
+		srcDirRWAllowlist []string
+	}{
+		{
+			srcDirRWAllowlist: []string{},
+		},
+		{
+			srcDirRWAllowlist: []string{"my/path"},
+		},
+		{
+			srcDirRWAllowlist: []string{"my/path1", "my/path2"},
+		},
+	}
+	for _, testCase := range testCases {
+		c := testCmd()
+		c.config.sandboxConfig.SetSrcDirIsRO(true)
+		c.config.sandboxConfig.SetSrcDirRWAllowlist(testCase.srcDirRWAllowlist)
+		c.wrapSandbox()
+		for _, allowlistPath := range testCase.srcDirRWAllowlist {
+			if !isExpectedMountFlag(c.Args, allowlistPath, "-B") {
+				t.Error("Mount flag of srcDirRWAllowlist is not correct, expect -B")
+			}
+		}
+	}
+}
+
+// utils for setting up test
+func testConfig() Config {
+	// create a minimal testConfig
+	env := Environment([]string{})
+	sandboxConfig := SandboxConfig{}
+	return Config{&configImpl{environ: &env,
+		sandboxConfig: &sandboxConfig}}
+}
+
+func testCmd() *Cmd {
+	return Command(testContext(), testConfig(), "sandbox_test", "path/to/nsjail")
+}
+
+func isExpectedMountFlag(cmdArgs []string, dirName string, expectedFlag string) bool {
+	indexOfSrcDir := index(cmdArgs, dirName)
+	return cmdArgs[indexOfSrcDir-1] == expectedFlag
+}
+
+func index(arr []string, target string) int {
+	for idx, element := range arr {
+		if element == target {
+			return idx
+		}
+	}
+	panic("element could not be located in input array")
+}
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 87818e3..a627dae 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -20,18 +20,18 @@
 	"path/filepath"
 	"strconv"
 
-	"android/soong/shared"
-	"github.com/google/blueprint/deptools"
-
+	"android/soong/ui/metrics"
 	soong_metrics_proto "android/soong/ui/metrics/metrics_proto"
+	"android/soong/ui/status"
+
+	"android/soong/shared"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/bootstrap"
-
-	"github.com/golang/protobuf/proto"
+	"github.com/google/blueprint/deptools"
 	"github.com/google/blueprint/microfactory"
 
-	"android/soong/ui/metrics"
-	"android/soong/ui/status"
+	"google.golang.org/protobuf/proto"
 )
 
 const (
@@ -71,22 +71,22 @@
 // A tiny struct used to tell Blueprint that it's in bootstrap mode. It would
 // probably be nicer to use a flag in bootstrap.Args instead.
 type BlueprintConfig struct {
-	srcDir           string
-	buildDir         string
-	ninjaBuildDir    string
+	toolDir          string
+	soongOutDir      string
+	outDir           string
 	debugCompilation bool
 }
 
-func (c BlueprintConfig) SrcDir() string {
-	return "."
+func (c BlueprintConfig) HostToolDir() string {
+	return c.toolDir
 }
 
-func (c BlueprintConfig) BuildDir() string {
-	return c.buildDir
+func (c BlueprintConfig) SoongOutDir() string {
+	return c.soongOutDir
 }
 
-func (c BlueprintConfig) NinjaBuildDir() string {
-	return c.ninjaBuildDir
+func (c BlueprintConfig) OutDir() string {
+	return c.outDir
 }
 
 func (c BlueprintConfig) DebugCompilation() bool {
@@ -99,26 +99,48 @@
 		"--used_env", shared.JoinPath(config.SoongOutDir(), usedEnvFile+suffix),
 	}
 }
-func bootstrapBlueprint(ctx Context, config Config, integratedBp2Build bool) {
+
+func writeEmptyGlobFile(ctx Context, path string) {
+	err := os.MkdirAll(filepath.Dir(path), 0777)
+	if err != nil {
+		ctx.Fatalf("Failed to create parent directories of empty ninja glob file '%s': %s", path, err)
+	}
+
+	if _, err := os.Stat(path); os.IsNotExist(err) {
+		err = ioutil.WriteFile(path, nil, 0666)
+		if err != nil {
+			ctx.Fatalf("Failed to create empty ninja glob file '%s': %s", path, err)
+		}
+	}
+}
+
+func bootstrapBlueprint(ctx Context, config Config) {
 	ctx.BeginTrace(metrics.RunSoong, "blueprint bootstrap")
 	defer ctx.EndTrace()
 
 	var args bootstrap.Args
 
-	mainNinjaFile := shared.JoinPath(config.SoongOutDir(), "build.ninja")
-	globFile := shared.JoinPath(config.SoongOutDir(), ".bootstrap/soong-build-globs.ninja")
 	bootstrapGlobFile := shared.JoinPath(config.SoongOutDir(), ".bootstrap/build-globs.ninja")
+	bp2buildGlobFile := shared.JoinPath(config.SoongOutDir(), ".bootstrap/build-globs.bp2build.ninja")
+	moduleGraphGlobFile := shared.JoinPath(config.SoongOutDir(), ".bootstrap/build-globs.modulegraph.ninja")
+
+	// The glob .ninja files are subninja'd. However, they are generated during
+	// the build itself so we write an empty file so that the subninja doesn't
+	// fail on clean builds
+	writeEmptyGlobFile(ctx, bootstrapGlobFile)
+	writeEmptyGlobFile(ctx, bp2buildGlobFile)
 	bootstrapDepFile := shared.JoinPath(config.SoongOutDir(), ".bootstrap/build.ninja.d")
 
 	args.RunGoTests = !config.skipSoongTests
 	args.UseValidations = true // Use validations to depend on tests
-	args.BuildDir = config.SoongOutDir()
-	args.NinjaBuildDir = config.OutDir()
-	args.TopFile = "Android.bp"
+	args.SoongOutDir = config.SoongOutDir()
+	args.OutDir = config.OutDir()
 	args.ModuleListFile = filepath.Join(config.FileListDir(), "Android.bp.list")
 	args.OutFile = shared.JoinPath(config.SoongOutDir(), ".bootstrap/build.ninja")
-	args.GlobFile = globFile
-	args.GeneratingPrimaryBuilder = true
+	// The primary builder (aka soong_build) will use bootstrapGlobFile as the globFile to generate build.ninja(.d)
+	// Building soong_build does not require a glob file
+	// Using "" instead of "<soong_build_glob>.ninja" will ensure that an unused glob file is not written to out/soong/.bootstrap during StagePrimary
+	args.Subninjas = []string{bootstrapGlobFile, bp2buildGlobFile}
 	args.EmptyNinjaFile = config.EmptyNinjaFile()
 
 	args.DelveListen = os.Getenv("SOONG_DELVE")
@@ -126,53 +148,76 @@
 		args.DelvePath = shared.ResolveDelveBinary()
 	}
 
-	commonArgs := bootstrap.PrimaryBuilderExtraFlags(args, bootstrapGlobFile, mainNinjaFile)
-	bp2BuildMarkerFile := shared.JoinPath(config.SoongOutDir(), ".bootstrap/bp2build_workspace_marker")
+	commonArgs := bootstrap.PrimaryBuilderExtraFlags(args, config.MainNinjaFile())
 	mainSoongBuildInputs := []string{"Android.bp"}
 
-	if integratedBp2Build {
-		mainSoongBuildInputs = append(mainSoongBuildInputs, bp2BuildMarkerFile)
+	if config.bazelBuildMode() == mixedBuild {
+		mainSoongBuildInputs = append(mainSoongBuildInputs, config.Bp2BuildMarkerFile())
 	}
 
-	soongBuildArgs := make([]string, 0)
+	soongBuildArgs := []string{
+		"--globListDir", "build",
+		"--globFile", bootstrapGlobFile,
+	}
+
 	soongBuildArgs = append(soongBuildArgs, commonArgs...)
 	soongBuildArgs = append(soongBuildArgs, environmentArgs(config, "")...)
 	soongBuildArgs = append(soongBuildArgs, "Android.bp")
 
 	mainSoongBuildInvocation := bootstrap.PrimaryBuilderInvocation{
 		Inputs:  mainSoongBuildInputs,
-		Outputs: []string{mainNinjaFile},
+		Outputs: []string{config.MainNinjaFile()},
 		Args:    soongBuildArgs,
 	}
 
-	if integratedBp2Build {
-		bp2buildArgs := []string{"--bp2build_marker", bp2BuildMarkerFile}
-		bp2buildArgs = append(bp2buildArgs, commonArgs...)
-		bp2buildArgs = append(bp2buildArgs, environmentArgs(config, ".bp2build")...)
-		bp2buildArgs = append(bp2buildArgs, "Android.bp")
+	bp2buildArgs := []string{
+		"--bp2build_marker", config.Bp2BuildMarkerFile(),
+		"--globListDir", "bp2build",
+		"--globFile", bp2buildGlobFile,
+	}
 
-		bp2buildInvocation := bootstrap.PrimaryBuilderInvocation{
-			Inputs:  []string{"Android.bp"},
-			Outputs: []string{bp2BuildMarkerFile},
-			Args:    bp2buildArgs,
-		}
-		args.PrimaryBuilderInvocations = []bootstrap.PrimaryBuilderInvocation{
-			bp2buildInvocation,
-			mainSoongBuildInvocation,
-		}
-	} else {
-		args.PrimaryBuilderInvocations = []bootstrap.PrimaryBuilderInvocation{mainSoongBuildInvocation}
+	bp2buildArgs = append(bp2buildArgs, commonArgs...)
+	bp2buildArgs = append(bp2buildArgs, environmentArgs(config, ".bp2build")...)
+	bp2buildArgs = append(bp2buildArgs, "Android.bp")
+
+	bp2buildInvocation := bootstrap.PrimaryBuilderInvocation{
+		Inputs:  []string{"Android.bp"},
+		Outputs: []string{config.Bp2BuildMarkerFile()},
+		Args:    bp2buildArgs,
+	}
+
+	moduleGraphArgs := []string{
+		"--module_graph_file", config.ModuleGraphFile(),
+		"--globListDir", "modulegraph",
+		"--globFile", moduleGraphGlobFile,
+	}
+
+	moduleGraphArgs = append(moduleGraphArgs, commonArgs...)
+	moduleGraphArgs = append(moduleGraphArgs, environmentArgs(config, ".modulegraph")...)
+	moduleGraphArgs = append(moduleGraphArgs, "Android.bp")
+
+	moduleGraphInvocation := bootstrap.PrimaryBuilderInvocation{
+		Inputs:  []string{"Android.bp"},
+		Outputs: []string{config.ModuleGraphFile()},
+		Args:    moduleGraphArgs,
+	}
+
+	args.PrimaryBuilderInvocations = []bootstrap.PrimaryBuilderInvocation{
+		bp2buildInvocation,
+		mainSoongBuildInvocation,
+		moduleGraphInvocation,
 	}
 
 	blueprintCtx := blueprint.NewContext()
 	blueprintCtx.SetIgnoreUnknownModuleTypes(true)
 	blueprintConfig := BlueprintConfig{
-		srcDir:           os.Getenv("TOP"),
-		buildDir:         config.SoongOutDir(),
-		ninjaBuildDir:    config.OutDir(),
+		soongOutDir:      config.SoongOutDir(),
+		toolDir:          config.HostToolDir(),
+		outDir:           config.OutDir(),
 		debugCompilation: os.Getenv("SOONG_DELVE") != "",
 	}
 
+	args.EmptyNinjaFile = false
 	bootstrapDeps := bootstrap.RunBlueprint(args, blueprintCtx, blueprintConfig)
 	err := deptools.WriteDepFile(bootstrapDepFile, args.OutFile, bootstrapDeps)
 	if err != nil {
@@ -200,18 +245,16 @@
 	// unused variables were changed?
 	envFile := filepath.Join(config.SoongOutDir(), availableEnvFile)
 
-	for _, n := range []string{".bootstrap", ".minibootstrap"} {
-		dir := filepath.Join(config.SoongOutDir(), n)
-		if err := os.MkdirAll(dir, 0755); err != nil {
-			ctx.Fatalf("Cannot mkdir " + dir)
-		}
+	dir := filepath.Join(config.SoongOutDir(), ".bootstrap")
+	if err := os.MkdirAll(dir, 0755); err != nil {
+		ctx.Fatalf("Cannot mkdir " + dir)
 	}
 
 	buildMode := config.bazelBuildMode()
 	integratedBp2Build := (buildMode == mixedBuild) || (buildMode == generateBuildFiles)
 
 	// This is done unconditionally, but does not take a measurable amount of time
-	bootstrapBlueprint(ctx, config, integratedBp2Build)
+	bootstrapBlueprint(ctx, config)
 
 	soongBuildEnv := config.Environment().Copy()
 	soongBuildEnv.Set("TOP", os.Getenv("TOP"))
@@ -245,22 +288,10 @@
 		}
 	}()
 
-	var cfg microfactory.Config
-	cfg.Map("github.com/google/blueprint", "build/blueprint")
+	runMicrofactory(ctx, config, filepath.Join(config.HostToolDir(), "bpglob"), "github.com/google/blueprint/bootstrap/bpglob",
+		map[string]string{"github.com/google/blueprint": "build/blueprint"})
 
-	cfg.TrimPath = absPath(ctx, ".")
-
-	func() {
-		ctx.BeginTrace(metrics.RunSoong, "bpglob")
-		defer ctx.EndTrace()
-
-		bpglob := filepath.Join(config.SoongOutDir(), ".minibootstrap/bpglob")
-		if _, err := microfactory.Build(&cfg, bpglob, "github.com/google/blueprint/bootstrap/bpglob"); err != nil {
-			ctx.Fatalln("Failed to build bpglob:", err)
-		}
-	}()
-
-	ninja := func(name, file string) {
+	ninja := func(name, ninjaFile string, targets ...string) {
 		ctx.BeginTrace(metrics.RunSoong, name)
 		defer ctx.EndTrace()
 
@@ -268,8 +299,7 @@
 		nr := status.NewNinjaReader(ctx, ctx.Status.StartTool(), fifo)
 		defer nr.Close()
 
-		cmd := Command(ctx, config, "soong "+name,
-			config.PrebuiltBuildTool("ninja"),
+		ninjaArgs := []string{
 			"-d", "keepdepfile",
 			"-d", "stats",
 			"-o", "usesphonyoutputs=yes",
@@ -279,7 +309,12 @@
 			"-w", "missingoutfile=err",
 			"-j", strconv.Itoa(config.Parallel()),
 			"--frontend_file", fifo,
-			"-f", filepath.Join(config.SoongOutDir(), file))
+			"-f", filepath.Join(config.SoongOutDir(), ninjaFile),
+		}
+
+		ninjaArgs = append(ninjaArgs, targets...)
+		cmd := Command(ctx, config, "soong "+name,
+			config.PrebuiltBuildTool("ninja"), ninjaArgs...)
 
 		var ninjaEnv Environment
 
@@ -291,8 +326,19 @@
 		cmd.Sandbox = soongSandbox
 		cmd.RunAndStreamOrFatal()
 	}
-	// This build generates <builddir>/build.ninja, which is used later by build/soong/ui/build/build.go#Build().
-	ninja("bootstrap", ".bootstrap/build.ninja")
+
+	var target string
+
+	if config.bazelBuildMode() == generateBuildFiles {
+		target = config.Bp2BuildMarkerFile()
+	} else if config.bazelBuildMode() == generateJsonModuleGraph {
+		target = config.ModuleGraphFile()
+	} else {
+		// This build generates <builddir>/build.ninja, which is used later by build/soong/ui/build/build.go#Build().
+		target = config.MainNinjaFile()
+	}
+
+	ninja("bootstrap", ".bootstrap/build.ninja", target)
 
 	var soongBuildMetrics *soong_metrics_proto.SoongBuildMetrics
 	if shouldCollectBuildSoongMetrics(config) {
@@ -312,9 +358,29 @@
 	}
 }
 
+func runMicrofactory(ctx Context, config Config, relExePath string, pkg string, mapping map[string]string) {
+	name := filepath.Base(relExePath)
+	ctx.BeginTrace(metrics.RunSoong, name)
+	defer ctx.EndTrace()
+	cfg := microfactory.Config{TrimPath: absPath(ctx, ".")}
+	for pkgPrefix, pathPrefix := range mapping {
+		cfg.Map(pkgPrefix, pathPrefix)
+	}
+
+	exePath := filepath.Join(config.SoongOutDir(), relExePath)
+	dir := filepath.Dir(exePath)
+	if err := os.MkdirAll(dir, 0777); err != nil {
+		ctx.Fatalf("cannot create %s: %s", dir, err)
+	}
+	if _, err := microfactory.Build(&cfg, exePath, pkg); err != nil {
+		ctx.Fatalf("failed to build %s: %s", name, err)
+	}
+}
+
 func shouldCollectBuildSoongMetrics(config Config) bool {
-	// Do not collect metrics protobuf if the soong_build binary ran as the bp2build converter.
-	return config.bazelBuildMode() != generateBuildFiles
+	// Do not collect metrics protobuf if the soong_build binary ran as the
+	// bp2build converter or the JSON graph dump.
+	return config.bazelBuildMode() != generateBuildFiles && config.bazelBuildMode() != generateJsonModuleGraph
 }
 
 func loadSoongBuildMetrics(ctx Context, config Config) *soong_metrics_proto.SoongBuildMetrics {
diff --git a/ui/build/test_build.go b/ui/build/test_build.go
index a910c06..f9a60b6 100644
--- a/ui/build/test_build.go
+++ b/ui/build/test_build.go
@@ -51,7 +51,6 @@
 	executable := config.PrebuiltBuildTool("ninja")
 
 	commonArgs := []string{}
-	commonArgs = append(commonArgs, config.NinjaArgs()...)
 	commonArgs = append(commonArgs, "-f", config.CombinedNinjaFile())
 	args := append(commonArgs, "-t", "targets", "rule")
 
@@ -65,7 +64,6 @@
 
 	outDir := config.OutDir()
 	bootstrapDir := filepath.Join(outDir, "soong", ".bootstrap")
-	miniBootstrapDir := filepath.Join(outDir, "soong", ".minibootstrap")
 	modulePathsDir := filepath.Join(outDir, ".module_paths")
 	variablesFilePath := filepath.Join(outDir, "soong", "soong.variables")
 
@@ -89,7 +87,6 @@
 			continue
 		}
 		if strings.HasPrefix(line, bootstrapDir) ||
-			strings.HasPrefix(line, miniBootstrapDir) ||
 			strings.HasPrefix(line, modulePathsDir) ||
 			line == variablesFilePath ||
 			line == dexpreoptConfigFilePath ||
diff --git a/ui/build/upload.go b/ui/build/upload.go
index 55ca800..55ada33 100644
--- a/ui/build/upload.go
+++ b/ui/build/upload.go
@@ -24,7 +24,8 @@
 	"time"
 
 	"android/soong/ui/metrics"
-	"github.com/golang/protobuf/proto"
+
+	"google.golang.org/protobuf/proto"
 
 	upload_proto "android/soong/ui/metrics/upload_proto"
 )
diff --git a/ui/metrics/Android.bp b/ui/metrics/Android.bp
index c428ec4..1590ab0 100644
--- a/ui/metrics/Android.bp
+++ b/ui/metrics/Android.bp
@@ -37,7 +37,10 @@
 bootstrap_go_package {
     name: "soong-ui-metrics_proto",
     pkgPath: "android/soong/ui/metrics/metrics_proto",
-    deps: ["golang-protobuf-proto"],
+    deps: [
+        "golang-protobuf-reflect-protoreflect",
+        "golang-protobuf-runtime-protoimpl",
+    ],
     srcs: [
         "metrics_proto/metrics.pb.go",
     ],
@@ -46,7 +49,10 @@
 bootstrap_go_package {
     name: "soong-ui-metrics_upload_proto",
     pkgPath: "android/soong/ui/metrics/upload_proto",
-    deps: ["golang-protobuf-proto"],
+    deps: [
+        "golang-protobuf-reflect-protoreflect",
+        "golang-protobuf-runtime-protoimpl",
+    ],
     srcs: [
         "upload_proto/upload.pb.go",
     ],
diff --git a/ui/metrics/event.go b/ui/metrics/event.go
index 87c1b84..c3367e3 100644
--- a/ui/metrics/event.go
+++ b/ui/metrics/event.go
@@ -30,10 +30,10 @@
 	"syscall"
 	"time"
 
-	"android/soong/ui/metrics/metrics_proto"
+	soong_metrics_proto "android/soong/ui/metrics/metrics_proto"
 	"android/soong/ui/tracer"
 
-	"github.com/golang/protobuf/proto"
+	"google.golang.org/protobuf/proto"
 )
 
 // _now wraps the time.Now() function. _now is declared for unit testing purpose.
diff --git a/ui/metrics/metrics.go b/ui/metrics/metrics.go
index efb572c..ccf9bd8 100644
--- a/ui/metrics/metrics.go
+++ b/ui/metrics/metrics.go
@@ -43,9 +43,9 @@
 	"strings"
 	"time"
 
-	"github.com/golang/protobuf/proto"
+	"google.golang.org/protobuf/proto"
 
-	"android/soong/ui/metrics/metrics_proto"
+	soong_metrics_proto "android/soong/ui/metrics/metrics_proto"
 )
 
 const (
diff --git a/ui/metrics/metrics_proto/metrics.pb.go b/ui/metrics/metrics_proto/metrics.pb.go
index fc2cfa4..697e954 100644
--- a/ui/metrics/metrics_proto/metrics.pb.go
+++ b/ui/metrics/metrics_proto/metrics.pb.go
@@ -1,24 +1,38 @@
+// Copyright 2018 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 // Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.26.0
+// 	protoc        v3.9.1
 // source: metrics.proto
 
-package soong_metrics_proto
+package metrics_proto
 
 import (
-	fmt "fmt"
-	proto "github.com/golang/protobuf/proto"
-	math "math"
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
 )
 
-// Reference imports to suppress errors if they are not otherwise used.
-var _ = proto.Marshal
-var _ = fmt.Errorf
-var _ = math.Inf
-
-// This is a compile-time assertion to ensure that this generated file
-// is compatible with the proto package it is being compiled against.
-// A compilation error at this line likely means your copy of the
-// proto package needs to be updated.
-const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
 
 type MetricsBase_BuildVariant int32
 
@@ -28,17 +42,19 @@
 	MetricsBase_ENG       MetricsBase_BuildVariant = 2
 )
 
-var MetricsBase_BuildVariant_name = map[int32]string{
-	0: "USER",
-	1: "USERDEBUG",
-	2: "ENG",
-}
-
-var MetricsBase_BuildVariant_value = map[string]int32{
-	"USER":      0,
-	"USERDEBUG": 1,
-	"ENG":       2,
-}
+// Enum value maps for MetricsBase_BuildVariant.
+var (
+	MetricsBase_BuildVariant_name = map[int32]string{
+		0: "USER",
+		1: "USERDEBUG",
+		2: "ENG",
+	}
+	MetricsBase_BuildVariant_value = map[string]int32{
+		"USER":      0,
+		"USERDEBUG": 1,
+		"ENG":       2,
+	}
+)
 
 func (x MetricsBase_BuildVariant) Enum() *MetricsBase_BuildVariant {
 	p := new(MetricsBase_BuildVariant)
@@ -47,20 +63,34 @@
 }
 
 func (x MetricsBase_BuildVariant) String() string {
-	return proto.EnumName(MetricsBase_BuildVariant_name, int32(x))
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
 }
 
-func (x *MetricsBase_BuildVariant) UnmarshalJSON(data []byte) error {
-	value, err := proto.UnmarshalJSONEnum(MetricsBase_BuildVariant_value, data, "MetricsBase_BuildVariant")
+func (MetricsBase_BuildVariant) Descriptor() protoreflect.EnumDescriptor {
+	return file_metrics_proto_enumTypes[0].Descriptor()
+}
+
+func (MetricsBase_BuildVariant) Type() protoreflect.EnumType {
+	return &file_metrics_proto_enumTypes[0]
+}
+
+func (x MetricsBase_BuildVariant) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Do not use.
+func (x *MetricsBase_BuildVariant) UnmarshalJSON(b []byte) error {
+	num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b)
 	if err != nil {
 		return err
 	}
-	*x = MetricsBase_BuildVariant(value)
+	*x = MetricsBase_BuildVariant(num)
 	return nil
 }
 
+// Deprecated: Use MetricsBase_BuildVariant.Descriptor instead.
 func (MetricsBase_BuildVariant) EnumDescriptor() ([]byte, []int) {
-	return fileDescriptor_6039342a2ba47b72, []int{0, 0}
+	return file_metrics_proto_rawDescGZIP(), []int{0, 0}
 }
 
 type MetricsBase_Arch int32
@@ -73,21 +103,23 @@
 	MetricsBase_X86_64  MetricsBase_Arch = 4
 )
 
-var MetricsBase_Arch_name = map[int32]string{
-	0: "UNKNOWN",
-	1: "ARM",
-	2: "ARM64",
-	3: "X86",
-	4: "X86_64",
-}
-
-var MetricsBase_Arch_value = map[string]int32{
-	"UNKNOWN": 0,
-	"ARM":     1,
-	"ARM64":   2,
-	"X86":     3,
-	"X86_64":  4,
-}
+// Enum value maps for MetricsBase_Arch.
+var (
+	MetricsBase_Arch_name = map[int32]string{
+		0: "UNKNOWN",
+		1: "ARM",
+		2: "ARM64",
+		3: "X86",
+		4: "X86_64",
+	}
+	MetricsBase_Arch_value = map[string]int32{
+		"UNKNOWN": 0,
+		"ARM":     1,
+		"ARM64":   2,
+		"X86":     3,
+		"X86_64":  4,
+	}
+)
 
 func (x MetricsBase_Arch) Enum() *MetricsBase_Arch {
 	p := new(MetricsBase_Arch)
@@ -96,20 +128,34 @@
 }
 
 func (x MetricsBase_Arch) String() string {
-	return proto.EnumName(MetricsBase_Arch_name, int32(x))
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
 }
 
-func (x *MetricsBase_Arch) UnmarshalJSON(data []byte) error {
-	value, err := proto.UnmarshalJSONEnum(MetricsBase_Arch_value, data, "MetricsBase_Arch")
+func (MetricsBase_Arch) Descriptor() protoreflect.EnumDescriptor {
+	return file_metrics_proto_enumTypes[1].Descriptor()
+}
+
+func (MetricsBase_Arch) Type() protoreflect.EnumType {
+	return &file_metrics_proto_enumTypes[1]
+}
+
+func (x MetricsBase_Arch) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Do not use.
+func (x *MetricsBase_Arch) UnmarshalJSON(b []byte) error {
+	num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b)
 	if err != nil {
 		return err
 	}
-	*x = MetricsBase_Arch(value)
+	*x = MetricsBase_Arch(num)
 	return nil
 }
 
+// Deprecated: Use MetricsBase_Arch.Descriptor instead.
 func (MetricsBase_Arch) EnumDescriptor() ([]byte, []int) {
-	return fileDescriptor_6039342a2ba47b72, []int{0, 1}
+	return file_metrics_proto_rawDescGZIP(), []int{0, 1}
 }
 
 type ModuleTypeInfo_BuildSystem int32
@@ -120,17 +166,19 @@
 	ModuleTypeInfo_MAKE    ModuleTypeInfo_BuildSystem = 2
 )
 
-var ModuleTypeInfo_BuildSystem_name = map[int32]string{
-	0: "UNKNOWN",
-	1: "SOONG",
-	2: "MAKE",
-}
-
-var ModuleTypeInfo_BuildSystem_value = map[string]int32{
-	"UNKNOWN": 0,
-	"SOONG":   1,
-	"MAKE":    2,
-}
+// Enum value maps for ModuleTypeInfo_BuildSystem.
+var (
+	ModuleTypeInfo_BuildSystem_name = map[int32]string{
+		0: "UNKNOWN",
+		1: "SOONG",
+		2: "MAKE",
+	}
+	ModuleTypeInfo_BuildSystem_value = map[string]int32{
+		"UNKNOWN": 0,
+		"SOONG":   1,
+		"MAKE":    2,
+	}
+)
 
 func (x ModuleTypeInfo_BuildSystem) Enum() *ModuleTypeInfo_BuildSystem {
 	p := new(ModuleTypeInfo_BuildSystem)
@@ -139,23 +187,41 @@
 }
 
 func (x ModuleTypeInfo_BuildSystem) String() string {
-	return proto.EnumName(ModuleTypeInfo_BuildSystem_name, int32(x))
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
 }
 
-func (x *ModuleTypeInfo_BuildSystem) UnmarshalJSON(data []byte) error {
-	value, err := proto.UnmarshalJSONEnum(ModuleTypeInfo_BuildSystem_value, data, "ModuleTypeInfo_BuildSystem")
+func (ModuleTypeInfo_BuildSystem) Descriptor() protoreflect.EnumDescriptor {
+	return file_metrics_proto_enumTypes[2].Descriptor()
+}
+
+func (ModuleTypeInfo_BuildSystem) Type() protoreflect.EnumType {
+	return &file_metrics_proto_enumTypes[2]
+}
+
+func (x ModuleTypeInfo_BuildSystem) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Do not use.
+func (x *ModuleTypeInfo_BuildSystem) UnmarshalJSON(b []byte) error {
+	num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b)
 	if err != nil {
 		return err
 	}
-	*x = ModuleTypeInfo_BuildSystem(value)
+	*x = ModuleTypeInfo_BuildSystem(num)
 	return nil
 }
 
+// Deprecated: Use ModuleTypeInfo_BuildSystem.Descriptor instead.
 func (ModuleTypeInfo_BuildSystem) EnumDescriptor() ([]byte, []int) {
-	return fileDescriptor_6039342a2ba47b72, []int{5, 0}
+	return file_metrics_proto_rawDescGZIP(), []int{5, 0}
 }
 
 type MetricsBase struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	// Timestamp generated when the build starts.
 	BuildDateTimestamp *int64 `protobuf:"varint,1,opt,name=build_date_timestamp,json=buildDateTimestamp" json:"build_date_timestamp,omitempty"`
 	// It is usually used to specify the branch name [and release candidate].
@@ -207,336 +273,382 @@
 	// The build command that the user entered to the build system.
 	BuildCommand *string `protobuf:"bytes,26,opt,name=build_command,json=buildCommand" json:"build_command,omitempty"`
 	// The metrics for calling Bazel.
-	BazelRuns            []*PerfInfo `protobuf:"bytes,27,rep,name=bazel_runs,json=bazelRuns" json:"bazel_runs,omitempty"`
-	XXX_NoUnkeyedLiteral struct{}    `json:"-"`
-	XXX_unrecognized     []byte      `json:"-"`
-	XXX_sizecache        int32       `json:"-"`
+	BazelRuns []*PerfInfo `protobuf:"bytes,27,rep,name=bazel_runs,json=bazelRuns" json:"bazel_runs,omitempty"`
 }
 
-func (m *MetricsBase) Reset()         { *m = MetricsBase{} }
-func (m *MetricsBase) String() string { return proto.CompactTextString(m) }
-func (*MetricsBase) ProtoMessage()    {}
+// Default values for MetricsBase fields.
+const (
+	Default_MetricsBase_TargetBuildVariant = MetricsBase_ENG
+	Default_MetricsBase_TargetArch         = MetricsBase_UNKNOWN
+	Default_MetricsBase_HostArch           = MetricsBase_UNKNOWN
+	Default_MetricsBase_Host_2NdArch       = MetricsBase_UNKNOWN
+)
+
+func (x *MetricsBase) Reset() {
+	*x = MetricsBase{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_metrics_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *MetricsBase) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*MetricsBase) ProtoMessage() {}
+
+func (x *MetricsBase) ProtoReflect() protoreflect.Message {
+	mi := &file_metrics_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use MetricsBase.ProtoReflect.Descriptor instead.
 func (*MetricsBase) Descriptor() ([]byte, []int) {
-	return fileDescriptor_6039342a2ba47b72, []int{0}
+	return file_metrics_proto_rawDescGZIP(), []int{0}
 }
 
-func (m *MetricsBase) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_MetricsBase.Unmarshal(m, b)
-}
-func (m *MetricsBase) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_MetricsBase.Marshal(b, m, deterministic)
-}
-func (m *MetricsBase) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_MetricsBase.Merge(m, src)
-}
-func (m *MetricsBase) XXX_Size() int {
-	return xxx_messageInfo_MetricsBase.Size(m)
-}
-func (m *MetricsBase) XXX_DiscardUnknown() {
-	xxx_messageInfo_MetricsBase.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_MetricsBase proto.InternalMessageInfo
-
-const Default_MetricsBase_TargetBuildVariant MetricsBase_BuildVariant = MetricsBase_ENG
-const Default_MetricsBase_TargetArch MetricsBase_Arch = MetricsBase_UNKNOWN
-const Default_MetricsBase_HostArch MetricsBase_Arch = MetricsBase_UNKNOWN
-const Default_MetricsBase_Host_2NdArch MetricsBase_Arch = MetricsBase_UNKNOWN
-
-func (m *MetricsBase) GetBuildDateTimestamp() int64 {
-	if m != nil && m.BuildDateTimestamp != nil {
-		return *m.BuildDateTimestamp
+func (x *MetricsBase) GetBuildDateTimestamp() int64 {
+	if x != nil && x.BuildDateTimestamp != nil {
+		return *x.BuildDateTimestamp
 	}
 	return 0
 }
 
-func (m *MetricsBase) GetBuildId() string {
-	if m != nil && m.BuildId != nil {
-		return *m.BuildId
+func (x *MetricsBase) GetBuildId() string {
+	if x != nil && x.BuildId != nil {
+		return *x.BuildId
 	}
 	return ""
 }
 
-func (m *MetricsBase) GetPlatformVersionCodename() string {
-	if m != nil && m.PlatformVersionCodename != nil {
-		return *m.PlatformVersionCodename
+func (x *MetricsBase) GetPlatformVersionCodename() string {
+	if x != nil && x.PlatformVersionCodename != nil {
+		return *x.PlatformVersionCodename
 	}
 	return ""
 }
 
-func (m *MetricsBase) GetTargetProduct() string {
-	if m != nil && m.TargetProduct != nil {
-		return *m.TargetProduct
+func (x *MetricsBase) GetTargetProduct() string {
+	if x != nil && x.TargetProduct != nil {
+		return *x.TargetProduct
 	}
 	return ""
 }
 
-func (m *MetricsBase) GetTargetBuildVariant() MetricsBase_BuildVariant {
-	if m != nil && m.TargetBuildVariant != nil {
-		return *m.TargetBuildVariant
+func (x *MetricsBase) GetTargetBuildVariant() MetricsBase_BuildVariant {
+	if x != nil && x.TargetBuildVariant != nil {
+		return *x.TargetBuildVariant
 	}
 	return Default_MetricsBase_TargetBuildVariant
 }
 
-func (m *MetricsBase) GetTargetArch() MetricsBase_Arch {
-	if m != nil && m.TargetArch != nil {
-		return *m.TargetArch
+func (x *MetricsBase) GetTargetArch() MetricsBase_Arch {
+	if x != nil && x.TargetArch != nil {
+		return *x.TargetArch
 	}
 	return Default_MetricsBase_TargetArch
 }
 
-func (m *MetricsBase) GetTargetArchVariant() string {
-	if m != nil && m.TargetArchVariant != nil {
-		return *m.TargetArchVariant
+func (x *MetricsBase) GetTargetArchVariant() string {
+	if x != nil && x.TargetArchVariant != nil {
+		return *x.TargetArchVariant
 	}
 	return ""
 }
 
-func (m *MetricsBase) GetTargetCpuVariant() string {
-	if m != nil && m.TargetCpuVariant != nil {
-		return *m.TargetCpuVariant
+func (x *MetricsBase) GetTargetCpuVariant() string {
+	if x != nil && x.TargetCpuVariant != nil {
+		return *x.TargetCpuVariant
 	}
 	return ""
 }
 
-func (m *MetricsBase) GetHostArch() MetricsBase_Arch {
-	if m != nil && m.HostArch != nil {
-		return *m.HostArch
+func (x *MetricsBase) GetHostArch() MetricsBase_Arch {
+	if x != nil && x.HostArch != nil {
+		return *x.HostArch
 	}
 	return Default_MetricsBase_HostArch
 }
 
-func (m *MetricsBase) GetHost_2NdArch() MetricsBase_Arch {
-	if m != nil && m.Host_2NdArch != nil {
-		return *m.Host_2NdArch
+func (x *MetricsBase) GetHost_2NdArch() MetricsBase_Arch {
+	if x != nil && x.Host_2NdArch != nil {
+		return *x.Host_2NdArch
 	}
 	return Default_MetricsBase_Host_2NdArch
 }
 
-func (m *MetricsBase) GetHostOs() string {
-	if m != nil && m.HostOs != nil {
-		return *m.HostOs
+func (x *MetricsBase) GetHostOs() string {
+	if x != nil && x.HostOs != nil {
+		return *x.HostOs
 	}
 	return ""
 }
 
-func (m *MetricsBase) GetHostOsExtra() string {
-	if m != nil && m.HostOsExtra != nil {
-		return *m.HostOsExtra
+func (x *MetricsBase) GetHostOsExtra() string {
+	if x != nil && x.HostOsExtra != nil {
+		return *x.HostOsExtra
 	}
 	return ""
 }
 
-func (m *MetricsBase) GetHostCrossOs() string {
-	if m != nil && m.HostCrossOs != nil {
-		return *m.HostCrossOs
+func (x *MetricsBase) GetHostCrossOs() string {
+	if x != nil && x.HostCrossOs != nil {
+		return *x.HostCrossOs
 	}
 	return ""
 }
 
-func (m *MetricsBase) GetHostCrossArch() string {
-	if m != nil && m.HostCrossArch != nil {
-		return *m.HostCrossArch
+func (x *MetricsBase) GetHostCrossArch() string {
+	if x != nil && x.HostCrossArch != nil {
+		return *x.HostCrossArch
 	}
 	return ""
 }
 
-func (m *MetricsBase) GetHostCross_2NdArch() string {
-	if m != nil && m.HostCross_2NdArch != nil {
-		return *m.HostCross_2NdArch
+func (x *MetricsBase) GetHostCross_2NdArch() string {
+	if x != nil && x.HostCross_2NdArch != nil {
+		return *x.HostCross_2NdArch
 	}
 	return ""
 }
 
-func (m *MetricsBase) GetOutDir() string {
-	if m != nil && m.OutDir != nil {
-		return *m.OutDir
+func (x *MetricsBase) GetOutDir() string {
+	if x != nil && x.OutDir != nil {
+		return *x.OutDir
 	}
 	return ""
 }
 
-func (m *MetricsBase) GetSetupTools() []*PerfInfo {
-	if m != nil {
-		return m.SetupTools
+func (x *MetricsBase) GetSetupTools() []*PerfInfo {
+	if x != nil {
+		return x.SetupTools
 	}
 	return nil
 }
 
-func (m *MetricsBase) GetKatiRuns() []*PerfInfo {
-	if m != nil {
-		return m.KatiRuns
+func (x *MetricsBase) GetKatiRuns() []*PerfInfo {
+	if x != nil {
+		return x.KatiRuns
 	}
 	return nil
 }
 
-func (m *MetricsBase) GetSoongRuns() []*PerfInfo {
-	if m != nil {
-		return m.SoongRuns
+func (x *MetricsBase) GetSoongRuns() []*PerfInfo {
+	if x != nil {
+		return x.SoongRuns
 	}
 	return nil
 }
 
-func (m *MetricsBase) GetNinjaRuns() []*PerfInfo {
-	if m != nil {
-		return m.NinjaRuns
+func (x *MetricsBase) GetNinjaRuns() []*PerfInfo {
+	if x != nil {
+		return x.NinjaRuns
 	}
 	return nil
 }
 
-func (m *MetricsBase) GetTotal() *PerfInfo {
-	if m != nil {
-		return m.Total
+func (x *MetricsBase) GetTotal() *PerfInfo {
+	if x != nil {
+		return x.Total
 	}
 	return nil
 }
 
-func (m *MetricsBase) GetSoongBuildMetrics() *SoongBuildMetrics {
-	if m != nil {
-		return m.SoongBuildMetrics
+func (x *MetricsBase) GetSoongBuildMetrics() *SoongBuildMetrics {
+	if x != nil {
+		return x.SoongBuildMetrics
 	}
 	return nil
 }
 
-func (m *MetricsBase) GetBuildConfig() *BuildConfig {
-	if m != nil {
-		return m.BuildConfig
+func (x *MetricsBase) GetBuildConfig() *BuildConfig {
+	if x != nil {
+		return x.BuildConfig
 	}
 	return nil
 }
 
-func (m *MetricsBase) GetHostname() string {
-	if m != nil && m.Hostname != nil {
-		return *m.Hostname
+func (x *MetricsBase) GetHostname() string {
+	if x != nil && x.Hostname != nil {
+		return *x.Hostname
 	}
 	return ""
 }
 
-func (m *MetricsBase) GetSystemResourceInfo() *SystemResourceInfo {
-	if m != nil {
-		return m.SystemResourceInfo
+func (x *MetricsBase) GetSystemResourceInfo() *SystemResourceInfo {
+	if x != nil {
+		return x.SystemResourceInfo
 	}
 	return nil
 }
 
-func (m *MetricsBase) GetBuildCommand() string {
-	if m != nil && m.BuildCommand != nil {
-		return *m.BuildCommand
+func (x *MetricsBase) GetBuildCommand() string {
+	if x != nil && x.BuildCommand != nil {
+		return *x.BuildCommand
 	}
 	return ""
 }
 
-func (m *MetricsBase) GetBazelRuns() []*PerfInfo {
-	if m != nil {
-		return m.BazelRuns
+func (x *MetricsBase) GetBazelRuns() []*PerfInfo {
+	if x != nil {
+		return x.BazelRuns
 	}
 	return nil
 }
 
 type BuildConfig struct {
-	UseGoma              *bool    `protobuf:"varint,1,opt,name=use_goma,json=useGoma" json:"use_goma,omitempty"`
-	UseRbe               *bool    `protobuf:"varint,2,opt,name=use_rbe,json=useRbe" json:"use_rbe,omitempty"`
-	ForceUseGoma         *bool    `protobuf:"varint,3,opt,name=force_use_goma,json=forceUseGoma" json:"force_use_goma,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	UseGoma      *bool `protobuf:"varint,1,opt,name=use_goma,json=useGoma" json:"use_goma,omitempty"`
+	UseRbe       *bool `protobuf:"varint,2,opt,name=use_rbe,json=useRbe" json:"use_rbe,omitempty"`
+	ForceUseGoma *bool `protobuf:"varint,3,opt,name=force_use_goma,json=forceUseGoma" json:"force_use_goma,omitempty"`
+	// Whether the Bazel is acting as the Ninja executor for this build.
+	BazelAsNinja *bool `protobuf:"varint,4,opt,name=bazel_as_ninja,json=bazelAsNinja" json:"bazel_as_ninja,omitempty"`
+	// Whether build is occurring in a mixed build mode, where Bazel maintains the
+	// definition and build of some modules in cooperation with Soong.
+	BazelMixedBuild *bool `protobuf:"varint,5,opt,name=bazel_mixed_build,json=bazelMixedBuild" json:"bazel_mixed_build,omitempty"`
 }
 
-func (m *BuildConfig) Reset()         { *m = BuildConfig{} }
-func (m *BuildConfig) String() string { return proto.CompactTextString(m) }
-func (*BuildConfig) ProtoMessage()    {}
+func (x *BuildConfig) Reset() {
+	*x = BuildConfig{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_metrics_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *BuildConfig) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*BuildConfig) ProtoMessage() {}
+
+func (x *BuildConfig) ProtoReflect() protoreflect.Message {
+	mi := &file_metrics_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use BuildConfig.ProtoReflect.Descriptor instead.
 func (*BuildConfig) Descriptor() ([]byte, []int) {
-	return fileDescriptor_6039342a2ba47b72, []int{1}
+	return file_metrics_proto_rawDescGZIP(), []int{1}
 }
 
-func (m *BuildConfig) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_BuildConfig.Unmarshal(m, b)
-}
-func (m *BuildConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_BuildConfig.Marshal(b, m, deterministic)
-}
-func (m *BuildConfig) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_BuildConfig.Merge(m, src)
-}
-func (m *BuildConfig) XXX_Size() int {
-	return xxx_messageInfo_BuildConfig.Size(m)
-}
-func (m *BuildConfig) XXX_DiscardUnknown() {
-	xxx_messageInfo_BuildConfig.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_BuildConfig proto.InternalMessageInfo
-
-func (m *BuildConfig) GetUseGoma() bool {
-	if m != nil && m.UseGoma != nil {
-		return *m.UseGoma
+func (x *BuildConfig) GetUseGoma() bool {
+	if x != nil && x.UseGoma != nil {
+		return *x.UseGoma
 	}
 	return false
 }
 
-func (m *BuildConfig) GetUseRbe() bool {
-	if m != nil && m.UseRbe != nil {
-		return *m.UseRbe
+func (x *BuildConfig) GetUseRbe() bool {
+	if x != nil && x.UseRbe != nil {
+		return *x.UseRbe
 	}
 	return false
 }
 
-func (m *BuildConfig) GetForceUseGoma() bool {
-	if m != nil && m.ForceUseGoma != nil {
-		return *m.ForceUseGoma
+func (x *BuildConfig) GetForceUseGoma() bool {
+	if x != nil && x.ForceUseGoma != nil {
+		return *x.ForceUseGoma
+	}
+	return false
+}
+
+func (x *BuildConfig) GetBazelAsNinja() bool {
+	if x != nil && x.BazelAsNinja != nil {
+		return *x.BazelAsNinja
+	}
+	return false
+}
+
+func (x *BuildConfig) GetBazelMixedBuild() bool {
+	if x != nil && x.BazelMixedBuild != nil {
+		return *x.BazelMixedBuild
 	}
 	return false
 }
 
 type SystemResourceInfo struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	// The total physical memory in bytes.
 	TotalPhysicalMemory *uint64 `protobuf:"varint,1,opt,name=total_physical_memory,json=totalPhysicalMemory" json:"total_physical_memory,omitempty"`
 	// The total of available cores for building
-	AvailableCpus        *int32   `protobuf:"varint,2,opt,name=available_cpus,json=availableCpus" json:"available_cpus,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
+	AvailableCpus *int32 `protobuf:"varint,2,opt,name=available_cpus,json=availableCpus" json:"available_cpus,omitempty"`
 }
 
-func (m *SystemResourceInfo) Reset()         { *m = SystemResourceInfo{} }
-func (m *SystemResourceInfo) String() string { return proto.CompactTextString(m) }
-func (*SystemResourceInfo) ProtoMessage()    {}
+func (x *SystemResourceInfo) Reset() {
+	*x = SystemResourceInfo{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_metrics_proto_msgTypes[2]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *SystemResourceInfo) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SystemResourceInfo) ProtoMessage() {}
+
+func (x *SystemResourceInfo) ProtoReflect() protoreflect.Message {
+	mi := &file_metrics_proto_msgTypes[2]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use SystemResourceInfo.ProtoReflect.Descriptor instead.
 func (*SystemResourceInfo) Descriptor() ([]byte, []int) {
-	return fileDescriptor_6039342a2ba47b72, []int{2}
+	return file_metrics_proto_rawDescGZIP(), []int{2}
 }
 
-func (m *SystemResourceInfo) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_SystemResourceInfo.Unmarshal(m, b)
-}
-func (m *SystemResourceInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_SystemResourceInfo.Marshal(b, m, deterministic)
-}
-func (m *SystemResourceInfo) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_SystemResourceInfo.Merge(m, src)
-}
-func (m *SystemResourceInfo) XXX_Size() int {
-	return xxx_messageInfo_SystemResourceInfo.Size(m)
-}
-func (m *SystemResourceInfo) XXX_DiscardUnknown() {
-	xxx_messageInfo_SystemResourceInfo.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_SystemResourceInfo proto.InternalMessageInfo
-
-func (m *SystemResourceInfo) GetTotalPhysicalMemory() uint64 {
-	if m != nil && m.TotalPhysicalMemory != nil {
-		return *m.TotalPhysicalMemory
+func (x *SystemResourceInfo) GetTotalPhysicalMemory() uint64 {
+	if x != nil && x.TotalPhysicalMemory != nil {
+		return *x.TotalPhysicalMemory
 	}
 	return 0
 }
 
-func (m *SystemResourceInfo) GetAvailableCpus() int32 {
-	if m != nil && m.AvailableCpus != nil {
-		return *m.AvailableCpus
+func (x *SystemResourceInfo) GetAvailableCpus() int32 {
+	if x != nil && x.AvailableCpus != nil {
+		return *x.AvailableCpus
 	}
 	return 0
 }
 
 type PerfInfo struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	// The description for the phase/action/part while the tool running.
 	Desc *string `protobuf:"bytes,1,opt,name=desc" json:"desc,omitempty"`
 	// The name for the running phase/action/part.
@@ -548,83 +660,93 @@
 	// The number of nanoseconds elapsed since start_time.
 	RealTime *uint64 `protobuf:"varint,4,opt,name=real_time,json=realTime" json:"real_time,omitempty"`
 	// The number of MB for memory use (deprecated as it is too generic).
-	MemoryUse *uint64 `protobuf:"varint,5,opt,name=memory_use,json=memoryUse" json:"memory_use,omitempty"` // Deprecated: Do not use.
+	//
+	// Deprecated: Do not use.
+	MemoryUse *uint64 `protobuf:"varint,5,opt,name=memory_use,json=memoryUse" json:"memory_use,omitempty"`
 	// The resource information of each executed process.
 	ProcessesResourceInfo []*ProcessResourceInfo `protobuf:"bytes,6,rep,name=processes_resource_info,json=processesResourceInfo" json:"processes_resource_info,omitempty"`
-	XXX_NoUnkeyedLiteral  struct{}               `json:"-"`
-	XXX_unrecognized      []byte                 `json:"-"`
-	XXX_sizecache         int32                  `json:"-"`
 }
 
-func (m *PerfInfo) Reset()         { *m = PerfInfo{} }
-func (m *PerfInfo) String() string { return proto.CompactTextString(m) }
-func (*PerfInfo) ProtoMessage()    {}
+func (x *PerfInfo) Reset() {
+	*x = PerfInfo{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_metrics_proto_msgTypes[3]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *PerfInfo) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*PerfInfo) ProtoMessage() {}
+
+func (x *PerfInfo) ProtoReflect() protoreflect.Message {
+	mi := &file_metrics_proto_msgTypes[3]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use PerfInfo.ProtoReflect.Descriptor instead.
 func (*PerfInfo) Descriptor() ([]byte, []int) {
-	return fileDescriptor_6039342a2ba47b72, []int{3}
+	return file_metrics_proto_rawDescGZIP(), []int{3}
 }
 
-func (m *PerfInfo) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_PerfInfo.Unmarshal(m, b)
-}
-func (m *PerfInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_PerfInfo.Marshal(b, m, deterministic)
-}
-func (m *PerfInfo) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_PerfInfo.Merge(m, src)
-}
-func (m *PerfInfo) XXX_Size() int {
-	return xxx_messageInfo_PerfInfo.Size(m)
-}
-func (m *PerfInfo) XXX_DiscardUnknown() {
-	xxx_messageInfo_PerfInfo.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_PerfInfo proto.InternalMessageInfo
-
-func (m *PerfInfo) GetDesc() string {
-	if m != nil && m.Desc != nil {
-		return *m.Desc
+func (x *PerfInfo) GetDesc() string {
+	if x != nil && x.Desc != nil {
+		return *x.Desc
 	}
 	return ""
 }
 
-func (m *PerfInfo) GetName() string {
-	if m != nil && m.Name != nil {
-		return *m.Name
+func (x *PerfInfo) GetName() string {
+	if x != nil && x.Name != nil {
+		return *x.Name
 	}
 	return ""
 }
 
-func (m *PerfInfo) GetStartTime() uint64 {
-	if m != nil && m.StartTime != nil {
-		return *m.StartTime
+func (x *PerfInfo) GetStartTime() uint64 {
+	if x != nil && x.StartTime != nil {
+		return *x.StartTime
 	}
 	return 0
 }
 
-func (m *PerfInfo) GetRealTime() uint64 {
-	if m != nil && m.RealTime != nil {
-		return *m.RealTime
+func (x *PerfInfo) GetRealTime() uint64 {
+	if x != nil && x.RealTime != nil {
+		return *x.RealTime
 	}
 	return 0
 }
 
 // Deprecated: Do not use.
-func (m *PerfInfo) GetMemoryUse() uint64 {
-	if m != nil && m.MemoryUse != nil {
-		return *m.MemoryUse
+func (x *PerfInfo) GetMemoryUse() uint64 {
+	if x != nil && x.MemoryUse != nil {
+		return *x.MemoryUse
 	}
 	return 0
 }
 
-func (m *PerfInfo) GetProcessesResourceInfo() []*ProcessResourceInfo {
-	if m != nil {
-		return m.ProcessesResourceInfo
+func (x *PerfInfo) GetProcessesResourceInfo() []*ProcessResourceInfo {
+	if x != nil {
+		return x.ProcessesResourceInfo
 	}
 	return nil
 }
 
 type ProcessResourceInfo struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	// The name of the process for identification.
 	Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
 	// The amount of time spent executing in user space in microseconds.
@@ -644,257 +766,292 @@
 	// The number of voluntary context switches
 	VoluntaryContextSwitches *uint64 `protobuf:"varint,9,opt,name=voluntary_context_switches,json=voluntaryContextSwitches" json:"voluntary_context_switches,omitempty"`
 	// The number of involuntary context switches
-	InvoluntaryContextSwitches *uint64  `protobuf:"varint,10,opt,name=involuntary_context_switches,json=involuntaryContextSwitches" json:"involuntary_context_switches,omitempty"`
-	XXX_NoUnkeyedLiteral       struct{} `json:"-"`
-	XXX_unrecognized           []byte   `json:"-"`
-	XXX_sizecache              int32    `json:"-"`
+	InvoluntaryContextSwitches *uint64 `protobuf:"varint,10,opt,name=involuntary_context_switches,json=involuntaryContextSwitches" json:"involuntary_context_switches,omitempty"`
 }
 
-func (m *ProcessResourceInfo) Reset()         { *m = ProcessResourceInfo{} }
-func (m *ProcessResourceInfo) String() string { return proto.CompactTextString(m) }
-func (*ProcessResourceInfo) ProtoMessage()    {}
+func (x *ProcessResourceInfo) Reset() {
+	*x = ProcessResourceInfo{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_metrics_proto_msgTypes[4]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ProcessResourceInfo) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ProcessResourceInfo) ProtoMessage() {}
+
+func (x *ProcessResourceInfo) ProtoReflect() protoreflect.Message {
+	mi := &file_metrics_proto_msgTypes[4]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ProcessResourceInfo.ProtoReflect.Descriptor instead.
 func (*ProcessResourceInfo) Descriptor() ([]byte, []int) {
-	return fileDescriptor_6039342a2ba47b72, []int{4}
+	return file_metrics_proto_rawDescGZIP(), []int{4}
 }
 
-func (m *ProcessResourceInfo) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_ProcessResourceInfo.Unmarshal(m, b)
-}
-func (m *ProcessResourceInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_ProcessResourceInfo.Marshal(b, m, deterministic)
-}
-func (m *ProcessResourceInfo) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_ProcessResourceInfo.Merge(m, src)
-}
-func (m *ProcessResourceInfo) XXX_Size() int {
-	return xxx_messageInfo_ProcessResourceInfo.Size(m)
-}
-func (m *ProcessResourceInfo) XXX_DiscardUnknown() {
-	xxx_messageInfo_ProcessResourceInfo.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_ProcessResourceInfo proto.InternalMessageInfo
-
-func (m *ProcessResourceInfo) GetName() string {
-	if m != nil && m.Name != nil {
-		return *m.Name
+func (x *ProcessResourceInfo) GetName() string {
+	if x != nil && x.Name != nil {
+		return *x.Name
 	}
 	return ""
 }
 
-func (m *ProcessResourceInfo) GetUserTimeMicros() uint64 {
-	if m != nil && m.UserTimeMicros != nil {
-		return *m.UserTimeMicros
+func (x *ProcessResourceInfo) GetUserTimeMicros() uint64 {
+	if x != nil && x.UserTimeMicros != nil {
+		return *x.UserTimeMicros
 	}
 	return 0
 }
 
-func (m *ProcessResourceInfo) GetSystemTimeMicros() uint64 {
-	if m != nil && m.SystemTimeMicros != nil {
-		return *m.SystemTimeMicros
+func (x *ProcessResourceInfo) GetSystemTimeMicros() uint64 {
+	if x != nil && x.SystemTimeMicros != nil {
+		return *x.SystemTimeMicros
 	}
 	return 0
 }
 
-func (m *ProcessResourceInfo) GetMaxRssKb() uint64 {
-	if m != nil && m.MaxRssKb != nil {
-		return *m.MaxRssKb
+func (x *ProcessResourceInfo) GetMaxRssKb() uint64 {
+	if x != nil && x.MaxRssKb != nil {
+		return *x.MaxRssKb
 	}
 	return 0
 }
 
-func (m *ProcessResourceInfo) GetMinorPageFaults() uint64 {
-	if m != nil && m.MinorPageFaults != nil {
-		return *m.MinorPageFaults
+func (x *ProcessResourceInfo) GetMinorPageFaults() uint64 {
+	if x != nil && x.MinorPageFaults != nil {
+		return *x.MinorPageFaults
 	}
 	return 0
 }
 
-func (m *ProcessResourceInfo) GetMajorPageFaults() uint64 {
-	if m != nil && m.MajorPageFaults != nil {
-		return *m.MajorPageFaults
+func (x *ProcessResourceInfo) GetMajorPageFaults() uint64 {
+	if x != nil && x.MajorPageFaults != nil {
+		return *x.MajorPageFaults
 	}
 	return 0
 }
 
-func (m *ProcessResourceInfo) GetIoInputKb() uint64 {
-	if m != nil && m.IoInputKb != nil {
-		return *m.IoInputKb
+func (x *ProcessResourceInfo) GetIoInputKb() uint64 {
+	if x != nil && x.IoInputKb != nil {
+		return *x.IoInputKb
 	}
 	return 0
 }
 
-func (m *ProcessResourceInfo) GetIoOutputKb() uint64 {
-	if m != nil && m.IoOutputKb != nil {
-		return *m.IoOutputKb
+func (x *ProcessResourceInfo) GetIoOutputKb() uint64 {
+	if x != nil && x.IoOutputKb != nil {
+		return *x.IoOutputKb
 	}
 	return 0
 }
 
-func (m *ProcessResourceInfo) GetVoluntaryContextSwitches() uint64 {
-	if m != nil && m.VoluntaryContextSwitches != nil {
-		return *m.VoluntaryContextSwitches
+func (x *ProcessResourceInfo) GetVoluntaryContextSwitches() uint64 {
+	if x != nil && x.VoluntaryContextSwitches != nil {
+		return *x.VoluntaryContextSwitches
 	}
 	return 0
 }
 
-func (m *ProcessResourceInfo) GetInvoluntaryContextSwitches() uint64 {
-	if m != nil && m.InvoluntaryContextSwitches != nil {
-		return *m.InvoluntaryContextSwitches
+func (x *ProcessResourceInfo) GetInvoluntaryContextSwitches() uint64 {
+	if x != nil && x.InvoluntaryContextSwitches != nil {
+		return *x.InvoluntaryContextSwitches
 	}
 	return 0
 }
 
 type ModuleTypeInfo struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	// The build system, eg. Soong or Make.
 	BuildSystem *ModuleTypeInfo_BuildSystem `protobuf:"varint,1,opt,name=build_system,json=buildSystem,enum=soong_build_metrics.ModuleTypeInfo_BuildSystem,def=0" json:"build_system,omitempty"`
 	// The module type, eg. java_library, cc_binary, and etc.
 	ModuleType *string `protobuf:"bytes,2,opt,name=module_type,json=moduleType" json:"module_type,omitempty"`
 	// The number of logical modules.
-	NumOfModules         *uint32  `protobuf:"varint,3,opt,name=num_of_modules,json=numOfModules" json:"num_of_modules,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
+	NumOfModules *uint32 `protobuf:"varint,3,opt,name=num_of_modules,json=numOfModules" json:"num_of_modules,omitempty"`
 }
 
-func (m *ModuleTypeInfo) Reset()         { *m = ModuleTypeInfo{} }
-func (m *ModuleTypeInfo) String() string { return proto.CompactTextString(m) }
-func (*ModuleTypeInfo) ProtoMessage()    {}
+// Default values for ModuleTypeInfo fields.
+const (
+	Default_ModuleTypeInfo_BuildSystem = ModuleTypeInfo_UNKNOWN
+)
+
+func (x *ModuleTypeInfo) Reset() {
+	*x = ModuleTypeInfo{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_metrics_proto_msgTypes[5]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *ModuleTypeInfo) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*ModuleTypeInfo) ProtoMessage() {}
+
+func (x *ModuleTypeInfo) ProtoReflect() protoreflect.Message {
+	mi := &file_metrics_proto_msgTypes[5]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use ModuleTypeInfo.ProtoReflect.Descriptor instead.
 func (*ModuleTypeInfo) Descriptor() ([]byte, []int) {
-	return fileDescriptor_6039342a2ba47b72, []int{5}
+	return file_metrics_proto_rawDescGZIP(), []int{5}
 }
 
-func (m *ModuleTypeInfo) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_ModuleTypeInfo.Unmarshal(m, b)
-}
-func (m *ModuleTypeInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_ModuleTypeInfo.Marshal(b, m, deterministic)
-}
-func (m *ModuleTypeInfo) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_ModuleTypeInfo.Merge(m, src)
-}
-func (m *ModuleTypeInfo) XXX_Size() int {
-	return xxx_messageInfo_ModuleTypeInfo.Size(m)
-}
-func (m *ModuleTypeInfo) XXX_DiscardUnknown() {
-	xxx_messageInfo_ModuleTypeInfo.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_ModuleTypeInfo proto.InternalMessageInfo
-
-const Default_ModuleTypeInfo_BuildSystem ModuleTypeInfo_BuildSystem = ModuleTypeInfo_UNKNOWN
-
-func (m *ModuleTypeInfo) GetBuildSystem() ModuleTypeInfo_BuildSystem {
-	if m != nil && m.BuildSystem != nil {
-		return *m.BuildSystem
+func (x *ModuleTypeInfo) GetBuildSystem() ModuleTypeInfo_BuildSystem {
+	if x != nil && x.BuildSystem != nil {
+		return *x.BuildSystem
 	}
 	return Default_ModuleTypeInfo_BuildSystem
 }
 
-func (m *ModuleTypeInfo) GetModuleType() string {
-	if m != nil && m.ModuleType != nil {
-		return *m.ModuleType
+func (x *ModuleTypeInfo) GetModuleType() string {
+	if x != nil && x.ModuleType != nil {
+		return *x.ModuleType
 	}
 	return ""
 }
 
-func (m *ModuleTypeInfo) GetNumOfModules() uint32 {
-	if m != nil && m.NumOfModules != nil {
-		return *m.NumOfModules
+func (x *ModuleTypeInfo) GetNumOfModules() uint32 {
+	if x != nil && x.NumOfModules != nil {
+		return *x.NumOfModules
 	}
 	return 0
 }
 
 type CriticalUserJourneyMetrics struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	// The name of a critical user journey test.
 	Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
 	// The metrics produced when running the critical user journey test.
-	Metrics              *MetricsBase `protobuf:"bytes,2,opt,name=metrics" json:"metrics,omitempty"`
-	XXX_NoUnkeyedLiteral struct{}     `json:"-"`
-	XXX_unrecognized     []byte       `json:"-"`
-	XXX_sizecache        int32        `json:"-"`
+	Metrics *MetricsBase `protobuf:"bytes,2,opt,name=metrics" json:"metrics,omitempty"`
 }
 
-func (m *CriticalUserJourneyMetrics) Reset()         { *m = CriticalUserJourneyMetrics{} }
-func (m *CriticalUserJourneyMetrics) String() string { return proto.CompactTextString(m) }
-func (*CriticalUserJourneyMetrics) ProtoMessage()    {}
+func (x *CriticalUserJourneyMetrics) Reset() {
+	*x = CriticalUserJourneyMetrics{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_metrics_proto_msgTypes[6]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *CriticalUserJourneyMetrics) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CriticalUserJourneyMetrics) ProtoMessage() {}
+
+func (x *CriticalUserJourneyMetrics) ProtoReflect() protoreflect.Message {
+	mi := &file_metrics_proto_msgTypes[6]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use CriticalUserJourneyMetrics.ProtoReflect.Descriptor instead.
 func (*CriticalUserJourneyMetrics) Descriptor() ([]byte, []int) {
-	return fileDescriptor_6039342a2ba47b72, []int{6}
+	return file_metrics_proto_rawDescGZIP(), []int{6}
 }
 
-func (m *CriticalUserJourneyMetrics) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_CriticalUserJourneyMetrics.Unmarshal(m, b)
-}
-func (m *CriticalUserJourneyMetrics) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_CriticalUserJourneyMetrics.Marshal(b, m, deterministic)
-}
-func (m *CriticalUserJourneyMetrics) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_CriticalUserJourneyMetrics.Merge(m, src)
-}
-func (m *CriticalUserJourneyMetrics) XXX_Size() int {
-	return xxx_messageInfo_CriticalUserJourneyMetrics.Size(m)
-}
-func (m *CriticalUserJourneyMetrics) XXX_DiscardUnknown() {
-	xxx_messageInfo_CriticalUserJourneyMetrics.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_CriticalUserJourneyMetrics proto.InternalMessageInfo
-
-func (m *CriticalUserJourneyMetrics) GetName() string {
-	if m != nil && m.Name != nil {
-		return *m.Name
+func (x *CriticalUserJourneyMetrics) GetName() string {
+	if x != nil && x.Name != nil {
+		return *x.Name
 	}
 	return ""
 }
 
-func (m *CriticalUserJourneyMetrics) GetMetrics() *MetricsBase {
-	if m != nil {
-		return m.Metrics
+func (x *CriticalUserJourneyMetrics) GetMetrics() *MetricsBase {
+	if x != nil {
+		return x.Metrics
 	}
 	return nil
 }
 
 type CriticalUserJourneysMetrics struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	// A set of metrics from a run of the critical user journey tests.
-	Cujs                 []*CriticalUserJourneyMetrics `protobuf:"bytes,1,rep,name=cujs" json:"cujs,omitempty"`
-	XXX_NoUnkeyedLiteral struct{}                      `json:"-"`
-	XXX_unrecognized     []byte                        `json:"-"`
-	XXX_sizecache        int32                         `json:"-"`
+	Cujs []*CriticalUserJourneyMetrics `protobuf:"bytes,1,rep,name=cujs" json:"cujs,omitempty"`
 }
 
-func (m *CriticalUserJourneysMetrics) Reset()         { *m = CriticalUserJourneysMetrics{} }
-func (m *CriticalUserJourneysMetrics) String() string { return proto.CompactTextString(m) }
-func (*CriticalUserJourneysMetrics) ProtoMessage()    {}
+func (x *CriticalUserJourneysMetrics) Reset() {
+	*x = CriticalUserJourneysMetrics{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_metrics_proto_msgTypes[7]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *CriticalUserJourneysMetrics) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*CriticalUserJourneysMetrics) ProtoMessage() {}
+
+func (x *CriticalUserJourneysMetrics) ProtoReflect() protoreflect.Message {
+	mi := &file_metrics_proto_msgTypes[7]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use CriticalUserJourneysMetrics.ProtoReflect.Descriptor instead.
 func (*CriticalUserJourneysMetrics) Descriptor() ([]byte, []int) {
-	return fileDescriptor_6039342a2ba47b72, []int{7}
+	return file_metrics_proto_rawDescGZIP(), []int{7}
 }
 
-func (m *CriticalUserJourneysMetrics) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_CriticalUserJourneysMetrics.Unmarshal(m, b)
-}
-func (m *CriticalUserJourneysMetrics) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_CriticalUserJourneysMetrics.Marshal(b, m, deterministic)
-}
-func (m *CriticalUserJourneysMetrics) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_CriticalUserJourneysMetrics.Merge(m, src)
-}
-func (m *CriticalUserJourneysMetrics) XXX_Size() int {
-	return xxx_messageInfo_CriticalUserJourneysMetrics.Size(m)
-}
-func (m *CriticalUserJourneysMetrics) XXX_DiscardUnknown() {
-	xxx_messageInfo_CriticalUserJourneysMetrics.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_CriticalUserJourneysMetrics proto.InternalMessageInfo
-
-func (m *CriticalUserJourneysMetrics) GetCujs() []*CriticalUserJourneyMetrics {
-	if m != nil {
-		return m.Cujs
+func (x *CriticalUserJourneysMetrics) GetCujs() []*CriticalUserJourneyMetrics {
+	if x != nil {
+		return x.Cujs
 	}
 	return nil
 }
 
 type SoongBuildMetrics struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	// The number of modules handled by soong_build.
 	Modules *uint32 `protobuf:"varint,1,opt,name=modules" json:"modules,omitempty"`
 	// The total number of variants handled by soong_build.
@@ -904,178 +1061,475 @@
 	// The total size of allocations in soong_build in bytes.
 	TotalAllocSize *uint64 `protobuf:"varint,4,opt,name=total_alloc_size,json=totalAllocSize" json:"total_alloc_size,omitempty"`
 	// The approximate maximum size of the heap in soong_build in bytes.
-	MaxHeapSize          *uint64  `protobuf:"varint,5,opt,name=max_heap_size,json=maxHeapSize" json:"max_heap_size,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
+	MaxHeapSize *uint64 `protobuf:"varint,5,opt,name=max_heap_size,json=maxHeapSize" json:"max_heap_size,omitempty"`
 }
 
-func (m *SoongBuildMetrics) Reset()         { *m = SoongBuildMetrics{} }
-func (m *SoongBuildMetrics) String() string { return proto.CompactTextString(m) }
-func (*SoongBuildMetrics) ProtoMessage()    {}
+func (x *SoongBuildMetrics) Reset() {
+	*x = SoongBuildMetrics{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_metrics_proto_msgTypes[8]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *SoongBuildMetrics) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*SoongBuildMetrics) ProtoMessage() {}
+
+func (x *SoongBuildMetrics) ProtoReflect() protoreflect.Message {
+	mi := &file_metrics_proto_msgTypes[8]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use SoongBuildMetrics.ProtoReflect.Descriptor instead.
 func (*SoongBuildMetrics) Descriptor() ([]byte, []int) {
-	return fileDescriptor_6039342a2ba47b72, []int{8}
+	return file_metrics_proto_rawDescGZIP(), []int{8}
 }
 
-func (m *SoongBuildMetrics) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_SoongBuildMetrics.Unmarshal(m, b)
-}
-func (m *SoongBuildMetrics) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_SoongBuildMetrics.Marshal(b, m, deterministic)
-}
-func (m *SoongBuildMetrics) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_SoongBuildMetrics.Merge(m, src)
-}
-func (m *SoongBuildMetrics) XXX_Size() int {
-	return xxx_messageInfo_SoongBuildMetrics.Size(m)
-}
-func (m *SoongBuildMetrics) XXX_DiscardUnknown() {
-	xxx_messageInfo_SoongBuildMetrics.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_SoongBuildMetrics proto.InternalMessageInfo
-
-func (m *SoongBuildMetrics) GetModules() uint32 {
-	if m != nil && m.Modules != nil {
-		return *m.Modules
+func (x *SoongBuildMetrics) GetModules() uint32 {
+	if x != nil && x.Modules != nil {
+		return *x.Modules
 	}
 	return 0
 }
 
-func (m *SoongBuildMetrics) GetVariants() uint32 {
-	if m != nil && m.Variants != nil {
-		return *m.Variants
+func (x *SoongBuildMetrics) GetVariants() uint32 {
+	if x != nil && x.Variants != nil {
+		return *x.Variants
 	}
 	return 0
 }
 
-func (m *SoongBuildMetrics) GetTotalAllocCount() uint64 {
-	if m != nil && m.TotalAllocCount != nil {
-		return *m.TotalAllocCount
+func (x *SoongBuildMetrics) GetTotalAllocCount() uint64 {
+	if x != nil && x.TotalAllocCount != nil {
+		return *x.TotalAllocCount
 	}
 	return 0
 }
 
-func (m *SoongBuildMetrics) GetTotalAllocSize() uint64 {
-	if m != nil && m.TotalAllocSize != nil {
-		return *m.TotalAllocSize
+func (x *SoongBuildMetrics) GetTotalAllocSize() uint64 {
+	if x != nil && x.TotalAllocSize != nil {
+		return *x.TotalAllocSize
 	}
 	return 0
 }
 
-func (m *SoongBuildMetrics) GetMaxHeapSize() uint64 {
-	if m != nil && m.MaxHeapSize != nil {
-		return *m.MaxHeapSize
+func (x *SoongBuildMetrics) GetMaxHeapSize() uint64 {
+	if x != nil && x.MaxHeapSize != nil {
+		return *x.MaxHeapSize
 	}
 	return 0
 }
 
-func init() {
-	proto.RegisterEnum("soong_build_metrics.MetricsBase_BuildVariant", MetricsBase_BuildVariant_name, MetricsBase_BuildVariant_value)
-	proto.RegisterEnum("soong_build_metrics.MetricsBase_Arch", MetricsBase_Arch_name, MetricsBase_Arch_value)
-	proto.RegisterEnum("soong_build_metrics.ModuleTypeInfo_BuildSystem", ModuleTypeInfo_BuildSystem_name, ModuleTypeInfo_BuildSystem_value)
-	proto.RegisterType((*MetricsBase)(nil), "soong_build_metrics.MetricsBase")
-	proto.RegisterType((*BuildConfig)(nil), "soong_build_metrics.BuildConfig")
-	proto.RegisterType((*SystemResourceInfo)(nil), "soong_build_metrics.SystemResourceInfo")
-	proto.RegisterType((*PerfInfo)(nil), "soong_build_metrics.PerfInfo")
-	proto.RegisterType((*ProcessResourceInfo)(nil), "soong_build_metrics.ProcessResourceInfo")
-	proto.RegisterType((*ModuleTypeInfo)(nil), "soong_build_metrics.ModuleTypeInfo")
-	proto.RegisterType((*CriticalUserJourneyMetrics)(nil), "soong_build_metrics.CriticalUserJourneyMetrics")
-	proto.RegisterType((*CriticalUserJourneysMetrics)(nil), "soong_build_metrics.CriticalUserJourneysMetrics")
-	proto.RegisterType((*SoongBuildMetrics)(nil), "soong_build_metrics.SoongBuildMetrics")
+var File_metrics_proto protoreflect.FileDescriptor
+
+var file_metrics_proto_rawDesc = []byte{
+	0x0a, 0x0d, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
+	0x13, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74,
+	0x72, 0x69, 0x63, 0x73, 0x22, 0xd8, 0x0c, 0x0a, 0x0b, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
+	0x42, 0x61, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x64, 0x61,
+	0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01,
+	0x28, 0x03, 0x52, 0x12, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x44, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d,
+	0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f,
+	0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x49,
+	0x64, 0x12, 0x3a, 0x0a, 0x19, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x5f, 0x76, 0x65,
+	0x72, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x17, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x56, 0x65,
+	0x72, 0x73, 0x69, 0x6f, 0x6e, 0x43, 0x6f, 0x64, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a,
+	0x0e, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x64, 0x75, 0x63, 0x74, 0x18,
+	0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x50, 0x72, 0x6f,
+	0x64, 0x75, 0x63, 0x74, 0x12, 0x64, 0x0a, 0x14, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x62,
+	0x75, 0x69, 0x6c, 0x64, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01,
+	0x28, 0x0e, 0x32, 0x2d, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64,
+	0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
+	0x42, 0x61, 0x73, 0x65, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x56, 0x61, 0x72, 0x69, 0x61, 0x6e,
+	0x74, 0x3a, 0x03, 0x45, 0x4e, 0x47, 0x52, 0x12, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x42, 0x75,
+	0x69, 0x6c, 0x64, 0x56, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x12, 0x4f, 0x0a, 0x0b, 0x74, 0x61,
+	0x72, 0x67, 0x65, 0x74, 0x5f, 0x61, 0x72, 0x63, 0x68, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0e, 0x32,
+	0x25, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65,
+	0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x42, 0x61, 0x73,
+	0x65, 0x2e, 0x41, 0x72, 0x63, 0x68, 0x3a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x52,
+	0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x41, 0x72, 0x63, 0x68, 0x12, 0x2e, 0x0a, 0x13, 0x74,
+	0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x61, 0x72, 0x63, 0x68, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61,
+	0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74,
+	0x41, 0x72, 0x63, 0x68, 0x56, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x12, 0x2c, 0x0a, 0x12, 0x74,
+	0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x63, 0x70, 0x75, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e,
+	0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x43,
+	0x70, 0x75, 0x56, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x12, 0x4b, 0x0a, 0x09, 0x68, 0x6f, 0x73,
+	0x74, 0x5f, 0x61, 0x72, 0x63, 0x68, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x25, 0x2e, 0x73,
+	0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69,
+	0x63, 0x73, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x42, 0x61, 0x73, 0x65, 0x2e, 0x41,
+	0x72, 0x63, 0x68, 0x3a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x52, 0x08, 0x68, 0x6f,
+	0x73, 0x74, 0x41, 0x72, 0x63, 0x68, 0x12, 0x52, 0x0a, 0x0d, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x32,
+	0x6e, 0x64, 0x5f, 0x61, 0x72, 0x63, 0x68, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x25, 0x2e,
+	0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72,
+	0x69, 0x63, 0x73, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x42, 0x61, 0x73, 0x65, 0x2e,
+	0x41, 0x72, 0x63, 0x68, 0x3a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x52, 0x0b, 0x68,
+	0x6f, 0x73, 0x74, 0x32, 0x6e, 0x64, 0x41, 0x72, 0x63, 0x68, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x6f,
+	0x73, 0x74, 0x5f, 0x6f, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x68, 0x6f, 0x73,
+	0x74, 0x4f, 0x73, 0x12, 0x22, 0x0a, 0x0d, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x6f, 0x73, 0x5f, 0x65,
+	0x78, 0x74, 0x72, 0x61, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x68, 0x6f, 0x73, 0x74,
+	0x4f, 0x73, 0x45, 0x78, 0x74, 0x72, 0x61, 0x12, 0x22, 0x0a, 0x0d, 0x68, 0x6f, 0x73, 0x74, 0x5f,
+	0x63, 0x72, 0x6f, 0x73, 0x73, 0x5f, 0x6f, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b,
+	0x68, 0x6f, 0x73, 0x74, 0x43, 0x72, 0x6f, 0x73, 0x73, 0x4f, 0x73, 0x12, 0x26, 0x0a, 0x0f, 0x68,
+	0x6f, 0x73, 0x74, 0x5f, 0x63, 0x72, 0x6f, 0x73, 0x73, 0x5f, 0x61, 0x72, 0x63, 0x68, 0x18, 0x0e,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x68, 0x6f, 0x73, 0x74, 0x43, 0x72, 0x6f, 0x73, 0x73, 0x41,
+	0x72, 0x63, 0x68, 0x12, 0x2d, 0x0a, 0x13, 0x68, 0x6f, 0x73, 0x74, 0x5f, 0x63, 0x72, 0x6f, 0x73,
+	0x73, 0x5f, 0x32, 0x6e, 0x64, 0x5f, 0x61, 0x72, 0x63, 0x68, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x10, 0x68, 0x6f, 0x73, 0x74, 0x43, 0x72, 0x6f, 0x73, 0x73, 0x32, 0x6e, 0x64, 0x41, 0x72,
+	0x63, 0x68, 0x12, 0x17, 0x0a, 0x07, 0x6f, 0x75, 0x74, 0x5f, 0x64, 0x69, 0x72, 0x18, 0x10, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x44, 0x69, 0x72, 0x12, 0x3e, 0x0a, 0x0b, 0x73,
+	0x65, 0x74, 0x75, 0x70, 0x5f, 0x74, 0x6f, 0x6f, 0x6c, 0x73, 0x18, 0x11, 0x20, 0x03, 0x28, 0x0b,
+	0x32, 0x1d, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d,
+	0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x50, 0x65, 0x72, 0x66, 0x49, 0x6e, 0x66, 0x6f, 0x52,
+	0x0a, 0x73, 0x65, 0x74, 0x75, 0x70, 0x54, 0x6f, 0x6f, 0x6c, 0x73, 0x12, 0x3a, 0x0a, 0x09, 0x6b,
+	0x61, 0x74, 0x69, 0x5f, 0x72, 0x75, 0x6e, 0x73, 0x18, 0x12, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d,
+	0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74,
+	0x72, 0x69, 0x63, 0x73, 0x2e, 0x50, 0x65, 0x72, 0x66, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x08, 0x6b,
+	0x61, 0x74, 0x69, 0x52, 0x75, 0x6e, 0x73, 0x12, 0x3c, 0x0a, 0x0a, 0x73, 0x6f, 0x6f, 0x6e, 0x67,
+	0x5f, 0x72, 0x75, 0x6e, 0x73, 0x18, 0x13, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x73, 0x6f,
+	0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
+	0x73, 0x2e, 0x50, 0x65, 0x72, 0x66, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x09, 0x73, 0x6f, 0x6f, 0x6e,
+	0x67, 0x52, 0x75, 0x6e, 0x73, 0x12, 0x3c, 0x0a, 0x0a, 0x6e, 0x69, 0x6e, 0x6a, 0x61, 0x5f, 0x72,
+	0x75, 0x6e, 0x73, 0x18, 0x14, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x73, 0x6f, 0x6f, 0x6e,
+	0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e,
+	0x50, 0x65, 0x72, 0x66, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x09, 0x6e, 0x69, 0x6e, 0x6a, 0x61, 0x52,
+	0x75, 0x6e, 0x73, 0x12, 0x33, 0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x15, 0x20, 0x01,
+	0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64,
+	0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x50, 0x65, 0x72, 0x66, 0x49, 0x6e, 0x66,
+	0x6f, 0x52, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x12, 0x56, 0x0a, 0x13, 0x73, 0x6f, 0x6f, 0x6e,
+	0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18,
+	0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75,
+	0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x53, 0x6f, 0x6f, 0x6e,
+	0x67, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x11, 0x73,
+	0x6f, 0x6f, 0x6e, 0x67, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
+	0x12, 0x43, 0x0a, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
+	0x18, 0x17, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62,
+	0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x42, 0x75, 0x69,
+	0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0b, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x43,
+	0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1a, 0x0a, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d,
+	0x65, 0x18, 0x18, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d,
+	0x65, 0x12, 0x59, 0x0a, 0x14, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x72, 0x65, 0x73, 0x6f,
+	0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x19, 0x20, 0x01, 0x28, 0x0b, 0x32,
+	0x27, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65,
+	0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x73, 0x6f,
+	0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x12, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d,
+	0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x23, 0x0a, 0x0d,
+	0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x1a, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e,
+	0x64, 0x12, 0x3c, 0x0a, 0x0a, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x5f, 0x72, 0x75, 0x6e, 0x73, 0x18,
+	0x1b, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75,
+	0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x50, 0x65, 0x72, 0x66,
+	0x49, 0x6e, 0x66, 0x6f, 0x52, 0x09, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x52, 0x75, 0x6e, 0x73, 0x22,
+	0x30, 0x0a, 0x0c, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x56, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x12,
+	0x08, 0x0a, 0x04, 0x55, 0x53, 0x45, 0x52, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x55, 0x53, 0x45,
+	0x52, 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x45, 0x4e, 0x47, 0x10,
+	0x02, 0x22, 0x3c, 0x0a, 0x04, 0x41, 0x72, 0x63, 0x68, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b,
+	0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x52, 0x4d, 0x10, 0x01, 0x12,
+	0x09, 0x0a, 0x05, 0x41, 0x52, 0x4d, 0x36, 0x34, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x58, 0x38,
+	0x36, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x58, 0x38, 0x36, 0x5f, 0x36, 0x34, 0x10, 0x04, 0x22,
+	0xb9, 0x01, 0x0a, 0x0b, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12,
+	0x19, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x5f, 0x67, 0x6f, 0x6d, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28,
+	0x08, 0x52, 0x07, 0x75, 0x73, 0x65, 0x47, 0x6f, 0x6d, 0x61, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73,
+	0x65, 0x5f, 0x72, 0x62, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x75, 0x73, 0x65,
+	0x52, 0x62, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x5f, 0x75, 0x73, 0x65,
+	0x5f, 0x67, 0x6f, 0x6d, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x66, 0x6f, 0x72,
+	0x63, 0x65, 0x55, 0x73, 0x65, 0x47, 0x6f, 0x6d, 0x61, 0x12, 0x24, 0x0a, 0x0e, 0x62, 0x61, 0x7a,
+	0x65, 0x6c, 0x5f, 0x61, 0x73, 0x5f, 0x6e, 0x69, 0x6e, 0x6a, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28,
+	0x08, 0x52, 0x0c, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x41, 0x73, 0x4e, 0x69, 0x6e, 0x6a, 0x61, 0x12,
+	0x2a, 0x0a, 0x11, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x5f, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x62,
+	0x75, 0x69, 0x6c, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x62, 0x61, 0x7a, 0x65,
+	0x6c, 0x4d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x22, 0x6f, 0x0a, 0x12, 0x53,
+	0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66,
+	0x6f, 0x12, 0x32, 0x0a, 0x15, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x70, 0x68, 0x79, 0x73, 0x69,
+	0x63, 0x61, 0x6c, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04,
+	0x52, 0x13, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x50, 0x68, 0x79, 0x73, 0x69, 0x63, 0x61, 0x6c, 0x4d,
+	0x65, 0x6d, 0x6f, 0x72, 0x79, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62,
+	0x6c, 0x65, 0x5f, 0x63, 0x70, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0d, 0x61,
+	0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x70, 0x75, 0x73, 0x22, 0xf3, 0x01, 0x0a,
+	0x08, 0x50, 0x65, 0x72, 0x66, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x65, 0x73,
+	0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64, 0x65, 0x73, 0x63, 0x12, 0x12, 0x0a,
+	0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d,
+	0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18,
+	0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65,
+	0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x61, 0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04, 0x20,
+	0x01, 0x28, 0x04, 0x52, 0x08, 0x72, 0x65, 0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x21, 0x0a,
+	0x0a, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x75, 0x73, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28,
+	0x04, 0x42, 0x02, 0x18, 0x01, 0x52, 0x09, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x55, 0x73, 0x65,
+	0x12, 0x60, 0x0a, 0x17, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x73, 0x5f, 0x72, 0x65,
+	0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x06, 0x20, 0x03, 0x28,
+	0x0b, 0x32, 0x28, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f,
+	0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52,
+	0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x15, 0x70, 0x72, 0x6f,
+	0x63, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e,
+	0x66, 0x6f, 0x22, 0xb9, 0x03, 0x0a, 0x13, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65,
+	0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61,
+	0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x28,
+	0x0a, 0x10, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x63, 0x72,
+	0x6f, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x75, 0x73, 0x65, 0x72, 0x54, 0x69,
+	0x6d, 0x65, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x79, 0x73, 0x74,
+	0x65, 0x6d, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18, 0x03,
+	0x20, 0x01, 0x28, 0x04, 0x52, 0x10, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x54, 0x69, 0x6d, 0x65,
+	0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x12, 0x1c, 0x0a, 0x0a, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x73,
+	0x73, 0x5f, 0x6b, 0x62, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x52,
+	0x73, 0x73, 0x4b, 0x62, 0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x5f, 0x70, 0x61,
+	0x67, 0x65, 0x5f, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52,
+	0x0f, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x50, 0x61, 0x67, 0x65, 0x46, 0x61, 0x75, 0x6c, 0x74, 0x73,
+	0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x66,
+	0x61, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x6d, 0x61, 0x6a,
+	0x6f, 0x72, 0x50, 0x61, 0x67, 0x65, 0x46, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x1e, 0x0a, 0x0b,
+	0x69, 0x6f, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x6b, 0x62, 0x18, 0x07, 0x20, 0x01, 0x28,
+	0x04, 0x52, 0x09, 0x69, 0x6f, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x4b, 0x62, 0x12, 0x20, 0x0a, 0x0c,
+	0x69, 0x6f, 0x5f, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x6b, 0x62, 0x18, 0x08, 0x20, 0x01,
+	0x28, 0x04, 0x52, 0x0a, 0x69, 0x6f, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x4b, 0x62, 0x12, 0x3c,
+	0x0a, 0x1a, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x74,
+	0x65, 0x78, 0x74, 0x5f, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01,
+	0x28, 0x04, 0x52, 0x18, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6e,
+	0x74, 0x65, 0x78, 0x74, 0x53, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x12, 0x40, 0x0a, 0x1c,
+	0x69, 0x6e, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x74,
+	0x65, 0x78, 0x74, 0x5f, 0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x01,
+	0x28, 0x04, 0x52, 0x1a, 0x69, 0x6e, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x43,
+	0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x53, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x22, 0xe5,
+	0x01, 0x0a, 0x0e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x49, 0x6e, 0x66,
+	0x6f, 0x12, 0x5b, 0x0a, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65,
+	0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2f, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f,
+	0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x4d, 0x6f,
+	0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x42, 0x75, 0x69,
+	0x6c, 0x64, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x3a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57,
+	0x4e, 0x52, 0x0b, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f,
+	0x0a, 0x0b, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12,
+	0x24, 0x0a, 0x0e, 0x6e, 0x75, 0x6d, 0x5f, 0x6f, 0x66, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65,
+	0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0c, 0x6e, 0x75, 0x6d, 0x4f, 0x66, 0x4d, 0x6f,
+	0x64, 0x75, 0x6c, 0x65, 0x73, 0x22, 0x2f, 0x0a, 0x0b, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x79,
+	0x73, 0x74, 0x65, 0x6d, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10,
+	0x00, 0x12, 0x09, 0x0a, 0x05, 0x53, 0x4f, 0x4f, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04,
+	0x4d, 0x41, 0x4b, 0x45, 0x10, 0x02, 0x22, 0x6c, 0x0a, 0x1a, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63,
+	0x61, 0x6c, 0x55, 0x73, 0x65, 0x72, 0x4a, 0x6f, 0x75, 0x72, 0x6e, 0x65, 0x79, 0x4d, 0x65, 0x74,
+	0x72, 0x69, 0x63, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3a, 0x0a, 0x07, 0x6d, 0x65, 0x74, 0x72,
+	0x69, 0x63, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x73, 0x6f, 0x6f, 0x6e,
+	0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e,
+	0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x42, 0x61, 0x73, 0x65, 0x52, 0x07, 0x6d, 0x65, 0x74,
+	0x72, 0x69, 0x63, 0x73, 0x22, 0x62, 0x0a, 0x1b, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c,
+	0x55, 0x73, 0x65, 0x72, 0x4a, 0x6f, 0x75, 0x72, 0x6e, 0x65, 0x79, 0x73, 0x4d, 0x65, 0x74, 0x72,
+	0x69, 0x63, 0x73, 0x12, 0x43, 0x0a, 0x04, 0x63, 0x75, 0x6a, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28,
+	0x0b, 0x32, 0x2f, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f,
+	0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c,
+	0x55, 0x73, 0x65, 0x72, 0x4a, 0x6f, 0x75, 0x72, 0x6e, 0x65, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69,
+	0x63, 0x73, 0x52, 0x04, 0x63, 0x75, 0x6a, 0x73, 0x22, 0xc3, 0x01, 0x0a, 0x11, 0x53, 0x6f, 0x6f,
+	0x6e, 0x67, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x18,
+	0x0a, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52,
+	0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x76, 0x61, 0x72, 0x69,
+	0x61, 0x6e, 0x74, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x61, 0x72, 0x69,
+	0x61, 0x6e, 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x61, 0x6c,
+	0x6c, 0x6f, 0x63, 0x5f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52,
+	0x0f, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x43, 0x6f, 0x75, 0x6e, 0x74,
+	0x12, 0x28, 0x0a, 0x10, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x5f,
+	0x73, 0x69, 0x7a, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x74, 0x6f, 0x74, 0x61,
+	0x6c, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x61,
+	0x78, 0x5f, 0x68, 0x65, 0x61, 0x70, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28,
+	0x04, 0x52, 0x0b, 0x6d, 0x61, 0x78, 0x48, 0x65, 0x61, 0x70, 0x53, 0x69, 0x7a, 0x65, 0x42, 0x28,
+	0x5a, 0x26, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f,
+	0x75, 0x69, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69,
+	0x63, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
 }
 
-func init() {
-	proto.RegisterFile("metrics.proto", fileDescriptor_6039342a2ba47b72)
+var (
+	file_metrics_proto_rawDescOnce sync.Once
+	file_metrics_proto_rawDescData = file_metrics_proto_rawDesc
+)
+
+func file_metrics_proto_rawDescGZIP() []byte {
+	file_metrics_proto_rawDescOnce.Do(func() {
+		file_metrics_proto_rawDescData = protoimpl.X.CompressGZIP(file_metrics_proto_rawDescData)
+	})
+	return file_metrics_proto_rawDescData
 }
 
-var fileDescriptor_6039342a2ba47b72 = []byte{
-	// 1380 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x57, 0xef, 0x52, 0x1b, 0x37,
-	0x10, 0x8f, 0xc1, 0x60, 0x7b, 0xfd, 0x07, 0x23, 0xa0, 0x5c, 0x48, 0xd2, 0x52, 0xb7, 0x49, 0x99,
-	0x4e, 0x43, 0x32, 0x34, 0xc3, 0x64, 0x98, 0x4c, 0xa7, 0xe0, 0xd0, 0x34, 0x65, 0xc0, 0x8c, 0x08,
-	0x69, 0xda, 0x7e, 0x50, 0xe5, 0xb3, 0x0c, 0x97, 0xdc, 0x9d, 0x6e, 0x24, 0x1d, 0xc5, 0x79, 0xb3,
-	0x7e, 0xee, 0x4b, 0xf4, 0x05, 0xfa, 0x04, 0x7d, 0x81, 0x8e, 0x56, 0x77, 0xe6, 0x20, 0x6e, 0xc2,
-	0xe4, 0xdb, 0xe9, 0xb7, 0xbf, 0xdf, 0x6a, 0xb5, 0xd2, 0xee, 0xda, 0xd0, 0x8c, 0x84, 0x51, 0x81,
-	0xaf, 0xd7, 0x13, 0x25, 0x8d, 0x24, 0x0b, 0x5a, 0xca, 0xf8, 0x84, 0xf5, 0xd3, 0x20, 0x1c, 0xb0,
-	0xcc, 0xd4, 0xf9, 0xbb, 0x01, 0xf5, 0x7d, 0xf7, 0xbd, 0xc3, 0xb5, 0x20, 0x0f, 0x61, 0xd1, 0x11,
-	0x06, 0xdc, 0x08, 0x66, 0x82, 0x48, 0x68, 0xc3, 0xa3, 0xc4, 0x2b, 0xad, 0x96, 0xd6, 0xa6, 0x29,
-	0x41, 0xdb, 0x53, 0x6e, 0xc4, 0x8b, 0xdc, 0x42, 0x6e, 0x42, 0xd5, 0x29, 0x82, 0x81, 0x37, 0xb5,
-	0x5a, 0x5a, 0xab, 0xd1, 0x0a, 0xae, 0x9f, 0x0f, 0xc8, 0x16, 0xdc, 0x4c, 0x42, 0x6e, 0x86, 0x52,
-	0x45, 0xec, 0x4c, 0x28, 0x1d, 0xc8, 0x98, 0xf9, 0x72, 0x20, 0x62, 0x1e, 0x09, 0x6f, 0x1a, 0xb9,
-	0xcb, 0x39, 0xe1, 0xa5, 0xb3, 0x77, 0x33, 0x33, 0xb9, 0x0b, 0x2d, 0xc3, 0xd5, 0x89, 0x30, 0x2c,
-	0x51, 0x72, 0x90, 0xfa, 0xc6, 0x2b, 0xa3, 0xa0, 0xe9, 0xd0, 0x43, 0x07, 0x92, 0x01, 0x2c, 0x66,
-	0x34, 0x17, 0xc4, 0x19, 0x57, 0x01, 0x8f, 0x8d, 0x37, 0xb3, 0x5a, 0x5a, 0x6b, 0x6d, 0xdc, 0x5f,
-	0x9f, 0x70, 0xe6, 0xf5, 0xc2, 0x79, 0xd7, 0x77, 0xac, 0xe5, 0xa5, 0x13, 0x6d, 0x4d, 0xef, 0x1e,
-	0x3c, 0xa3, 0xc4, 0xf9, 0x2b, 0x1a, 0x48, 0x0f, 0xea, 0xd9, 0x2e, 0x5c, 0xf9, 0xa7, 0xde, 0x2c,
-	0x3a, 0xbf, 0xfb, 0x41, 0xe7, 0xdb, 0xca, 0x3f, 0xdd, 0xaa, 0x1c, 0x1f, 0xec, 0x1d, 0xf4, 0x7e,
-	0x3e, 0xa0, 0xe0, 0x5c, 0x58, 0x90, 0xac, 0xc3, 0x42, 0xc1, 0xe1, 0x38, 0xea, 0x0a, 0x1e, 0x71,
-	0xfe, 0x82, 0x98, 0x07, 0xf0, 0x0d, 0x64, 0x61, 0x31, 0x3f, 0x49, 0xc7, 0xf4, 0x2a, 0xd2, 0xdb,
-	0xce, 0xd2, 0x4d, 0xd2, 0x9c, 0xbd, 0x07, 0xb5, 0x53, 0xa9, 0xb3, 0x60, 0x6b, 0x1f, 0x15, 0x6c,
-	0xd5, 0x3a, 0xc0, 0x50, 0x29, 0x34, 0xd1, 0xd9, 0x46, 0x3c, 0x70, 0x0e, 0xe1, 0xa3, 0x1c, 0xd6,
-	0xad, 0x93, 0x8d, 0x78, 0x80, 0x3e, 0x97, 0xa1, 0x82, 0x3e, 0xa5, 0xf6, 0xea, 0x78, 0x86, 0x59,
-	0xbb, 0xec, 0x69, 0xd2, 0xc9, 0x36, 0x93, 0x9a, 0x89, 0x73, 0xa3, 0xb8, 0xd7, 0x40, 0x73, 0xdd,
-	0x99, 0x77, 0x2d, 0x34, 0xe6, 0xf8, 0x4a, 0x6a, 0x6d, 0x5d, 0x34, 0x2f, 0x38, 0x5d, 0x8b, 0xf5,
-	0x34, 0xb9, 0x07, 0x73, 0x05, 0x0e, 0x86, 0xdd, 0x72, 0xcf, 0x67, 0xcc, 0xc2, 0x40, 0xee, 0xc3,
-	0x42, 0x81, 0x37, 0x3e, 0xe2, 0x9c, 0x4b, 0xec, 0x98, 0x5b, 0x88, 0x5b, 0xa6, 0x86, 0x0d, 0x02,
-	0xe5, 0xb5, 0x5d, 0xdc, 0x32, 0x35, 0x4f, 0x03, 0x45, 0xbe, 0x83, 0xba, 0x16, 0x26, 0x4d, 0x98,
-	0x91, 0x32, 0xd4, 0xde, 0xfc, 0xea, 0xf4, 0x5a, 0x7d, 0xe3, 0xce, 0xc4, 0x14, 0x1d, 0x0a, 0x35,
-	0x7c, 0x1e, 0x0f, 0x25, 0x05, 0x54, 0xbc, 0xb0, 0x02, 0xb2, 0x05, 0xb5, 0x37, 0xdc, 0x04, 0x4c,
-	0xa5, 0xb1, 0xf6, 0xc8, 0x75, 0xd4, 0x55, 0xcb, 0xa7, 0x69, 0xac, 0xc9, 0x13, 0x00, 0xc7, 0x44,
-	0xf1, 0xc2, 0x75, 0xc4, 0x35, 0xb4, 0xe6, 0xea, 0x38, 0x88, 0x5f, 0x73, 0xa7, 0x5e, 0xbc, 0x96,
-	0x1a, 0x05, 0xa8, 0xfe, 0x16, 0x66, 0x8c, 0x34, 0x3c, 0xf4, 0x96, 0x56, 0x4b, 0x1f, 0x16, 0x3a,
-	0x2e, 0x79, 0x09, 0x93, 0x5a, 0x91, 0xf7, 0x09, 0xba, 0xb8, 0x37, 0xd1, 0xc5, 0x91, 0xc5, 0xb0,
-	0x24, 0xb3, 0x17, 0x46, 0xe7, 0xf5, 0x55, 0x88, 0x74, 0xa1, 0xe1, 0x54, 0xbe, 0x8c, 0x87, 0xc1,
-	0x89, 0xb7, 0x8c, 0x0e, 0x57, 0x27, 0x3a, 0x44, 0x61, 0x17, 0x79, 0xb4, 0xde, 0xbf, 0x58, 0x90,
-	0x15, 0xc0, 0xa7, 0x8f, 0x2d, 0xca, 0xc3, 0x3b, 0x1e, 0xaf, 0xc9, 0x2f, 0xb0, 0xa8, 0x47, 0xda,
-	0x88, 0x88, 0x29, 0xa1, 0x65, 0xaa, 0x7c, 0xc1, 0x82, 0x78, 0x28, 0xbd, 0x9b, 0xb8, 0xd1, 0x57,
-	0x93, 0x23, 0x47, 0x01, 0xcd, 0xf8, 0x98, 0x06, 0xa2, 0xdf, 0xc1, 0xc8, 0x17, 0xd0, 0xcc, 0x63,
-	0x8f, 0x22, 0x1e, 0x0f, 0xbc, 0x15, 0xdc, 0xbb, 0x91, 0x85, 0x86, 0x98, 0xbd, 0xab, 0x3e, 0x7f,
-	0x2b, 0x42, 0x77, 0x57, 0xb7, 0xae, 0x75, 0x57, 0x28, 0xb0, 0x77, 0xd5, 0x79, 0x08, 0x8d, 0x4b,
-	0x4d, 0xad, 0x0a, 0xe5, 0xe3, 0xa3, 0x5d, 0xda, 0xbe, 0x41, 0x9a, 0x50, 0xb3, 0x5f, 0x4f, 0x77,
-	0x77, 0x8e, 0x9f, 0xb5, 0x4b, 0xa4, 0x02, 0xb6, 0x11, 0xb6, 0xa7, 0x3a, 0x4f, 0xa0, 0x8c, 0xcf,
-	0xbe, 0x0e, 0x79, 0x19, 0xb7, 0x6f, 0x58, 0xeb, 0x36, 0xdd, 0x6f, 0x97, 0x48, 0x0d, 0x66, 0xb6,
-	0xe9, 0xfe, 0xe6, 0xa3, 0xf6, 0x94, 0xc5, 0x5e, 0x3d, 0xde, 0x6c, 0x4f, 0x13, 0x80, 0xd9, 0x57,
-	0x8f, 0x37, 0xd9, 0xe6, 0xa3, 0x76, 0xb9, 0x73, 0x02, 0xf5, 0x42, 0x96, 0xed, 0x9c, 0x48, 0xb5,
-	0x60, 0x27, 0x32, 0xe2, 0x38, 0x4d, 0xaa, 0xb4, 0x92, 0x6a, 0xf1, 0x4c, 0x46, 0xdc, 0x96, 0x95,
-	0x35, 0xa9, 0xbe, 0xc0, 0x09, 0x52, 0xa5, 0xb3, 0xa9, 0x16, 0xb4, 0x2f, 0xc8, 0x97, 0xd0, 0x1a,
-	0x4a, 0x9b, 0xe6, 0xb1, 0x72, 0x1a, 0xed, 0x0d, 0x44, 0x8f, 0x9d, 0xbc, 0x23, 0x81, 0xbc, 0x9b,
-	0x65, 0xb2, 0x01, 0x4b, 0xf8, 0xdc, 0x58, 0x72, 0x3a, 0xd2, 0x81, 0xcf, 0x43, 0x16, 0x89, 0x48,
-	0xaa, 0x11, 0x6e, 0x5e, 0xa6, 0x0b, 0x68, 0x3c, 0xcc, 0x6c, 0xfb, 0x68, 0xb2, 0x43, 0x87, 0x9f,
-	0xf1, 0x20, 0xe4, 0xfd, 0x50, 0xd8, 0x4e, 0xab, 0x31, 0x9e, 0x19, 0xda, 0x1c, 0xa3, 0xdd, 0x24,
-	0xd5, 0x9d, 0x7f, 0x4b, 0x50, 0xcd, 0x33, 0x4c, 0x08, 0x94, 0x07, 0x42, 0xfb, 0xe8, 0xb6, 0x46,
-	0xf1, 0xdb, 0x62, 0xf8, 0x80, 0xdc, 0x3c, 0xc4, 0x6f, 0x72, 0x07, 0x40, 0x1b, 0xae, 0x0c, 0x0e,
-	0x55, 0x3c, 0x47, 0x99, 0xd6, 0x10, 0xb1, 0xb3, 0x94, 0xdc, 0x82, 0x9a, 0x12, 0x3c, 0x74, 0xd6,
-	0x32, 0x5a, 0xab, 0x16, 0x40, 0xe3, 0xe7, 0x00, 0x2e, 0x78, 0x9b, 0x08, 0x9c, 0x6d, 0xe5, 0x9d,
-	0x29, 0xaf, 0x44, 0x6b, 0x0e, 0x3d, 0xd6, 0x82, 0xfc, 0x0e, 0xcb, 0x89, 0x92, 0xbe, 0xd0, 0x5a,
-	0xe8, 0x2b, 0xcf, 0x73, 0x16, 0x1f, 0xca, 0xda, 0xe4, 0x87, 0xe2, 0x34, 0x97, 0xde, 0xe7, 0xd2,
-	0xd8, 0x51, 0x11, 0xee, 0xfc, 0x39, 0x0d, 0x0b, 0x13, 0xe8, 0xe3, 0xc3, 0x96, 0x0a, 0x87, 0x5d,
-	0x83, 0x76, 0xaa, 0x85, 0xc2, 0xd3, 0xb0, 0x28, 0xb0, 0xed, 0x15, 0x93, 0x51, 0xa6, 0x2d, 0x8b,
-	0xdb, 0x43, 0xed, 0x23, 0x6a, 0x27, 0x5b, 0x56, 0x53, 0x45, 0xae, 0x4b, 0x4f, 0xdb, 0x59, 0x0a,
-	0xec, 0xdb, 0x00, 0x11, 0x3f, 0x67, 0x4a, 0x6b, 0xf6, 0xa6, 0x9f, 0xa7, 0x29, 0xe2, 0xe7, 0x54,
-	0xeb, 0xbd, 0x3e, 0xf9, 0x1a, 0xe6, 0xa3, 0x20, 0x96, 0x8a, 0x25, 0xfc, 0x44, 0xb0, 0x21, 0x4f,
-	0x43, 0xa3, 0x5d, 0xb6, 0xe8, 0x1c, 0x1a, 0x0e, 0xf9, 0x89, 0xf8, 0x01, 0x61, 0xe4, 0xf2, 0xd7,
-	0x57, 0xb8, 0xb3, 0x19, 0xd7, 0x1a, 0x0a, 0xdc, 0x4f, 0xa1, 0x1e, 0x48, 0x16, 0xc4, 0x49, 0x6a,
-	0xec, 0xb6, 0x15, 0x77, 0x77, 0x81, 0x7c, 0x6e, 0x91, 0xbd, 0x3e, 0x59, 0x85, 0x46, 0x20, 0x99,
-	0x4c, 0x4d, 0x46, 0xa8, 0x22, 0x01, 0x02, 0xd9, 0x43, 0x68, 0xaf, 0x4f, 0x9e, 0xc0, 0xca, 0x99,
-	0x0c, 0xd3, 0xd8, 0x70, 0x35, 0xb2, 0xed, 0xc9, 0x88, 0x73, 0xc3, 0xf4, 0x1f, 0x81, 0xf1, 0x4f,
-	0x85, 0xc6, 0x11, 0x5d, 0xa6, 0xde, 0x98, 0xd1, 0x75, 0x84, 0xa3, 0xcc, 0x4e, 0xbe, 0x87, 0xdb,
-	0x41, 0xfc, 0x1e, 0x3d, 0xa0, 0x7e, 0xa5, 0xc0, 0xb9, 0xe2, 0xa1, 0xf3, 0x4f, 0x09, 0x5a, 0xfb,
-	0x72, 0x90, 0x86, 0xe2, 0xc5, 0x28, 0x71, 0xd7, 0xf6, 0x5b, 0xde, 0x2d, 0x5d, 0x92, 0xf1, 0xfa,
-	0x5a, 0x1b, 0x0f, 0x26, 0x8f, 0xf5, 0x4b, 0x52, 0xd7, 0x3c, 0x5d, 0xc9, 0x15, 0x06, 0x7c, 0xff,
-	0x02, 0x25, 0x9f, 0x41, 0x3d, 0x42, 0x0d, 0x33, 0xa3, 0x24, 0xaf, 0x03, 0x88, 0xc6, 0x6e, 0x6c,
-	0x65, 0xc7, 0x69, 0xc4, 0xe4, 0x90, 0x39, 0xd0, 0x5d, 0x79, 0x93, 0x36, 0xe2, 0x34, 0xea, 0x0d,
-	0xdd, 0x7e, 0xba, 0xf3, 0x20, 0x6b, 0x21, 0x99, 0xd7, 0x4b, 0x7d, 0xa8, 0x06, 0x33, 0x47, 0xbd,
-	0xde, 0x81, 0x6d, 0x58, 0x55, 0x28, 0xef, 0x6f, 0xef, 0xed, 0xb6, 0xa7, 0x3a, 0x21, 0xac, 0x74,
-	0x55, 0x60, 0x6c, 0x49, 0x1f, 0x6b, 0xa1, 0x7e, 0x92, 0xa9, 0x8a, 0xc5, 0x28, 0x1f, 0x10, 0x93,
-	0x5e, 0xea, 0x16, 0x54, 0xf2, 0x01, 0x34, 0xf5, 0x9e, 0x79, 0x51, 0xf8, 0x61, 0x43, 0x73, 0x41,
-	0xa7, 0x0f, 0xb7, 0x26, 0xec, 0xa6, 0x2f, 0xe6, 0x51, 0xd9, 0x4f, 0x5f, 0x6b, 0xaf, 0x84, 0xf5,
-	0x37, 0x39, 0xb3, 0xff, 0x1f, 0x2d, 0x45, 0x71, 0xe7, 0xaf, 0x12, 0xcc, 0xbf, 0x33, 0xfd, 0x88,
-	0x07, 0x95, 0x3c, 0x6f, 0x25, 0xcc, 0x5b, 0xbe, 0xb4, 0xf3, 0x2b, 0xfb, 0x79, 0xe8, 0x0e, 0xd4,
-	0xa4, 0xe3, 0xb5, 0x7d, 0xf3, 0xae, 0x25, 0xf2, 0x30, 0x94, 0x3e, 0xf3, 0x65, 0x1a, 0x9b, 0xac,
-	0xd4, 0xe6, 0xd0, 0xb0, 0x6d, 0xf1, 0xae, 0x85, 0x6d, 0x05, 0x17, 0xb9, 0x3a, 0x78, 0x9b, 0xb7,
-	0xa5, 0xd6, 0x05, 0xf5, 0x28, 0x78, 0x2b, 0xec, 0xef, 0x31, 0x5b, 0x93, 0xa7, 0x82, 0x27, 0x8e,
-	0xe6, 0x2a, 0xae, 0x1e, 0xf1, 0xf3, 0x1f, 0x05, 0x4f, 0x2c, 0x67, 0x67, 0xe9, 0xd7, 0x6c, 0xe4,
-	0x67, 0xe7, 0x66, 0xf8, 0x97, 0xe4, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcb, 0xfb, 0x8e, 0xf5,
-	0xa2, 0x0c, 0x00, 0x00,
+var file_metrics_proto_enumTypes = make([]protoimpl.EnumInfo, 3)
+var file_metrics_proto_msgTypes = make([]protoimpl.MessageInfo, 9)
+var file_metrics_proto_goTypes = []interface{}{
+	(MetricsBase_BuildVariant)(0),       // 0: soong_build_metrics.MetricsBase.BuildVariant
+	(MetricsBase_Arch)(0),               // 1: soong_build_metrics.MetricsBase.Arch
+	(ModuleTypeInfo_BuildSystem)(0),     // 2: soong_build_metrics.ModuleTypeInfo.BuildSystem
+	(*MetricsBase)(nil),                 // 3: soong_build_metrics.MetricsBase
+	(*BuildConfig)(nil),                 // 4: soong_build_metrics.BuildConfig
+	(*SystemResourceInfo)(nil),          // 5: soong_build_metrics.SystemResourceInfo
+	(*PerfInfo)(nil),                    // 6: soong_build_metrics.PerfInfo
+	(*ProcessResourceInfo)(nil),         // 7: soong_build_metrics.ProcessResourceInfo
+	(*ModuleTypeInfo)(nil),              // 8: soong_build_metrics.ModuleTypeInfo
+	(*CriticalUserJourneyMetrics)(nil),  // 9: soong_build_metrics.CriticalUserJourneyMetrics
+	(*CriticalUserJourneysMetrics)(nil), // 10: soong_build_metrics.CriticalUserJourneysMetrics
+	(*SoongBuildMetrics)(nil),           // 11: soong_build_metrics.SoongBuildMetrics
+}
+var file_metrics_proto_depIdxs = []int32{
+	0,  // 0: soong_build_metrics.MetricsBase.target_build_variant:type_name -> soong_build_metrics.MetricsBase.BuildVariant
+	1,  // 1: soong_build_metrics.MetricsBase.target_arch:type_name -> soong_build_metrics.MetricsBase.Arch
+	1,  // 2: soong_build_metrics.MetricsBase.host_arch:type_name -> soong_build_metrics.MetricsBase.Arch
+	1,  // 3: soong_build_metrics.MetricsBase.host_2nd_arch:type_name -> soong_build_metrics.MetricsBase.Arch
+	6,  // 4: soong_build_metrics.MetricsBase.setup_tools:type_name -> soong_build_metrics.PerfInfo
+	6,  // 5: soong_build_metrics.MetricsBase.kati_runs:type_name -> soong_build_metrics.PerfInfo
+	6,  // 6: soong_build_metrics.MetricsBase.soong_runs:type_name -> soong_build_metrics.PerfInfo
+	6,  // 7: soong_build_metrics.MetricsBase.ninja_runs:type_name -> soong_build_metrics.PerfInfo
+	6,  // 8: soong_build_metrics.MetricsBase.total:type_name -> soong_build_metrics.PerfInfo
+	11, // 9: soong_build_metrics.MetricsBase.soong_build_metrics:type_name -> soong_build_metrics.SoongBuildMetrics
+	4,  // 10: soong_build_metrics.MetricsBase.build_config:type_name -> soong_build_metrics.BuildConfig
+	5,  // 11: soong_build_metrics.MetricsBase.system_resource_info:type_name -> soong_build_metrics.SystemResourceInfo
+	6,  // 12: soong_build_metrics.MetricsBase.bazel_runs:type_name -> soong_build_metrics.PerfInfo
+	7,  // 13: soong_build_metrics.PerfInfo.processes_resource_info:type_name -> soong_build_metrics.ProcessResourceInfo
+	2,  // 14: soong_build_metrics.ModuleTypeInfo.build_system:type_name -> soong_build_metrics.ModuleTypeInfo.BuildSystem
+	3,  // 15: soong_build_metrics.CriticalUserJourneyMetrics.metrics:type_name -> soong_build_metrics.MetricsBase
+	9,  // 16: soong_build_metrics.CriticalUserJourneysMetrics.cujs:type_name -> soong_build_metrics.CriticalUserJourneyMetrics
+	17, // [17:17] is the sub-list for method output_type
+	17, // [17:17] is the sub-list for method input_type
+	17, // [17:17] is the sub-list for extension type_name
+	17, // [17:17] is the sub-list for extension extendee
+	0,  // [0:17] is the sub-list for field type_name
+}
+
+func init() { file_metrics_proto_init() }
+func file_metrics_proto_init() {
+	if File_metrics_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_metrics_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*MetricsBase); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_metrics_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*BuildConfig); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_metrics_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*SystemResourceInfo); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_metrics_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*PerfInfo); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_metrics_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ProcessResourceInfo); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_metrics_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ModuleTypeInfo); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_metrics_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*CriticalUserJourneyMetrics); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_metrics_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*CriticalUserJourneysMetrics); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_metrics_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*SoongBuildMetrics); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_metrics_proto_rawDesc,
+			NumEnums:      3,
+			NumMessages:   9,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_metrics_proto_goTypes,
+		DependencyIndexes: file_metrics_proto_depIdxs,
+		EnumInfos:         file_metrics_proto_enumTypes,
+		MessageInfos:      file_metrics_proto_msgTypes,
+	}.Build()
+	File_metrics_proto = out.File
+	file_metrics_proto_rawDesc = nil
+	file_metrics_proto_goTypes = nil
+	file_metrics_proto_depIdxs = nil
 }
diff --git a/ui/metrics/metrics_proto/metrics.proto b/ui/metrics/metrics_proto/metrics.proto
index 91d8dd9..ef42f54 100644
--- a/ui/metrics/metrics_proto/metrics.proto
+++ b/ui/metrics/metrics_proto/metrics.proto
@@ -15,7 +15,7 @@
 syntax = "proto2";
 
 package soong_build_metrics;
-option go_package = "soong_metrics_proto";
+option go_package = "android/soong/ui/metrics/metrics_proto";
 
 message MetricsBase {
   // Timestamp generated when the build starts.
@@ -116,6 +116,13 @@
   optional bool use_rbe = 2;
 
   optional bool force_use_goma = 3;
+
+  // Whether the Bazel is acting as the Ninja executor for this build.
+  optional bool bazel_as_ninja = 4;
+
+  // Whether build is occurring in a mixed build mode, where Bazel maintains the
+  // definition and build of some modules in cooperation with Soong.
+  optional bool bazel_mixed_build = 5;
 }
 
 message SystemResourceInfo {
diff --git a/ui/metrics/upload_proto/upload.pb.go b/ui/metrics/upload_proto/upload.pb.go
index 614d4c7..9c8fb06 100644
--- a/ui/metrics/upload_proto/upload.pb.go
+++ b/ui/metrics/upload_proto/upload.pb.go
@@ -1,26 +1,44 @@
+// Copyright 2020 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 // Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.26.0
+// 	protoc        v3.9.1
 // source: upload.proto
 
-package soong_metrics_upload_proto
+package upload_proto
 
 import (
-	fmt "fmt"
-	proto "github.com/golang/protobuf/proto"
-	math "math"
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
 )
 
-// Reference imports to suppress errors if they are not otherwise used.
-var _ = proto.Marshal
-var _ = fmt.Errorf
-var _ = math.Inf
-
-// This is a compile-time assertion to ensure that this generated file
-// is compatible with the proto package it is being compiled against.
-// A compilation error at this line likely means your copy of the
-// proto package needs to be updated.
-const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
 
 type Upload struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	// The timestamp in milliseconds that the build was created.
 	CreationTimestampMs *uint64 `protobuf:"varint,1,opt,name=creation_timestamp_ms,json=creationTimestampMs" json:"creation_timestamp_ms,omitempty"`
 	// The timestamp in milliseconds when the build was completed.
@@ -33,102 +51,169 @@
 	MetricsFiles []string `protobuf:"bytes,5,rep,name=metrics_files,json=metricsFiles" json:"metrics_files,omitempty"`
 	// A list of directories to delete after the copy of metrics files
 	// is completed for uploading.
-	DirectoriesToDelete  []string `protobuf:"bytes,6,rep,name=directories_to_delete,json=directoriesToDelete" json:"directories_to_delete,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
+	DirectoriesToDelete []string `protobuf:"bytes,6,rep,name=directories_to_delete,json=directoriesToDelete" json:"directories_to_delete,omitempty"`
 }
 
-func (m *Upload) Reset()         { *m = Upload{} }
-func (m *Upload) String() string { return proto.CompactTextString(m) }
-func (*Upload) ProtoMessage()    {}
+func (x *Upload) Reset() {
+	*x = Upload{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_upload_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Upload) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Upload) ProtoMessage() {}
+
+func (x *Upload) ProtoReflect() protoreflect.Message {
+	mi := &file_upload_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Upload.ProtoReflect.Descriptor instead.
 func (*Upload) Descriptor() ([]byte, []int) {
-	return fileDescriptor_91b94b655bd2a7e5, []int{0}
+	return file_upload_proto_rawDescGZIP(), []int{0}
 }
 
-func (m *Upload) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_Upload.Unmarshal(m, b)
-}
-func (m *Upload) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_Upload.Marshal(b, m, deterministic)
-}
-func (m *Upload) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_Upload.Merge(m, src)
-}
-func (m *Upload) XXX_Size() int {
-	return xxx_messageInfo_Upload.Size(m)
-}
-func (m *Upload) XXX_DiscardUnknown() {
-	xxx_messageInfo_Upload.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_Upload proto.InternalMessageInfo
-
-func (m *Upload) GetCreationTimestampMs() uint64 {
-	if m != nil && m.CreationTimestampMs != nil {
-		return *m.CreationTimestampMs
+func (x *Upload) GetCreationTimestampMs() uint64 {
+	if x != nil && x.CreationTimestampMs != nil {
+		return *x.CreationTimestampMs
 	}
 	return 0
 }
 
-func (m *Upload) GetCompletionTimestampMs() uint64 {
-	if m != nil && m.CompletionTimestampMs != nil {
-		return *m.CompletionTimestampMs
+func (x *Upload) GetCompletionTimestampMs() uint64 {
+	if x != nil && x.CompletionTimestampMs != nil {
+		return *x.CompletionTimestampMs
 	}
 	return 0
 }
 
-func (m *Upload) GetBranchName() string {
-	if m != nil && m.BranchName != nil {
-		return *m.BranchName
+func (x *Upload) GetBranchName() string {
+	if x != nil && x.BranchName != nil {
+		return *x.BranchName
 	}
 	return ""
 }
 
-func (m *Upload) GetTargetName() string {
-	if m != nil && m.TargetName != nil {
-		return *m.TargetName
+func (x *Upload) GetTargetName() string {
+	if x != nil && x.TargetName != nil {
+		return *x.TargetName
 	}
 	return ""
 }
 
-func (m *Upload) GetMetricsFiles() []string {
-	if m != nil {
-		return m.MetricsFiles
+func (x *Upload) GetMetricsFiles() []string {
+	if x != nil {
+		return x.MetricsFiles
 	}
 	return nil
 }
 
-func (m *Upload) GetDirectoriesToDelete() []string {
-	if m != nil {
-		return m.DirectoriesToDelete
+func (x *Upload) GetDirectoriesToDelete() []string {
+	if x != nil {
+		return x.DirectoriesToDelete
 	}
 	return nil
 }
 
-func init() {
-	proto.RegisterType((*Upload)(nil), "soong_metrics_upload.Upload")
+var File_upload_proto protoreflect.FileDescriptor
+
+var file_upload_proto_rawDesc = []byte{
+	0x0a, 0x0c, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x14,
+	0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x75, 0x70,
+	0x6c, 0x6f, 0x61, 0x64, 0x22, 0x8f, 0x02, 0x0a, 0x06, 0x55, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x12,
+	0x32, 0x0a, 0x15, 0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65,
+	0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x6d, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x13,
+	0x63, 0x72, 0x65, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d,
+	0x70, 0x4d, 0x73, 0x12, 0x36, 0x0a, 0x17, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x69, 0x6f,
+	0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x5f, 0x6d, 0x73, 0x18, 0x02,
+	0x20, 0x01, 0x28, 0x04, 0x52, 0x15, 0x63, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x69, 0x6f, 0x6e,
+	0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x4d, 0x73, 0x12, 0x1f, 0x0a, 0x0b, 0x62,
+	0x72, 0x61, 0x6e, 0x63, 0x68, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x0a, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1f, 0x0a, 0x0b,
+	0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x0a, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x23, 0x0a,
+	0x0d, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x05,
+	0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x46, 0x69, 0x6c,
+	0x65, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x69, 0x65,
+	0x73, 0x5f, 0x74, 0x6f, 0x5f, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x18, 0x06, 0x20, 0x03, 0x28,
+	0x09, 0x52, 0x13, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x69, 0x65, 0x73, 0x54, 0x6f,
+	0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x42, 0x27, 0x5a, 0x25, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69,
+	0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x75, 0x69, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69,
+	0x63, 0x73, 0x2f, 0x75, 0x70, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
 }
 
-func init() {
-	proto.RegisterFile("upload.proto", fileDescriptor_91b94b655bd2a7e5)
+var (
+	file_upload_proto_rawDescOnce sync.Once
+	file_upload_proto_rawDescData = file_upload_proto_rawDesc
+)
+
+func file_upload_proto_rawDescGZIP() []byte {
+	file_upload_proto_rawDescOnce.Do(func() {
+		file_upload_proto_rawDescData = protoimpl.X.CompressGZIP(file_upload_proto_rawDescData)
+	})
+	return file_upload_proto_rawDescData
 }
 
-var fileDescriptor_91b94b655bd2a7e5 = []byte{
-	// 230 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x90, 0xb1, 0x4a, 0x04, 0x31,
-	0x10, 0x86, 0xd9, 0xbb, 0xf3, 0xe0, 0xe2, 0xd9, 0xec, 0x79, 0x18, 0x44, 0x70, 0xd1, 0x66, 0x2b,
-	0x0b, 0x0b, 0x1f, 0x40, 0xc4, 0x4e, 0x8b, 0xe5, 0x6c, 0x6c, 0x86, 0x98, 0x1d, 0xd7, 0x40, 0x92,
-	0x09, 0xc9, 0xf8, 0x1c, 0xbe, 0xb2, 0x6c, 0xe2, 0xe2, 0x82, 0x76, 0xc3, 0xff, 0x7d, 0x7f, 0x31,
-	0xbf, 0xd8, 0x7e, 0x06, 0x4b, 0xaa, 0xbf, 0x09, 0x91, 0x98, 0xea, 0xd3, 0x44, 0xe4, 0x07, 0x70,
-	0xc8, 0xd1, 0xe8, 0x04, 0x85, 0x5d, 0x7d, 0x2d, 0xc4, 0xfa, 0x25, 0x9f, 0xf5, 0xad, 0xd8, 0xeb,
-	0x88, 0x8a, 0x0d, 0x79, 0x60, 0xe3, 0x30, 0xb1, 0x72, 0x01, 0x5c, 0x92, 0x55, 0x53, 0xb5, 0xab,
-	0x6e, 0x37, 0xc1, 0xc3, 0xc4, 0x9e, 0x52, 0x7d, 0x27, 0xce, 0x34, 0xb9, 0x60, 0xf1, 0x6f, 0x6b,
-	0x91, 0x5b, 0xfb, 0x5f, 0x3c, 0xef, 0x5d, 0x8a, 0xe3, 0xb7, 0xa8, 0xbc, 0xfe, 0x00, 0xaf, 0x1c,
-	0xca, 0x65, 0x53, 0xb5, 0x9b, 0x4e, 0x94, 0xe8, 0x59, 0x39, 0x1c, 0x05, 0x56, 0x71, 0x40, 0x2e,
-	0xc2, 0xaa, 0x08, 0x25, 0xca, 0xc2, 0xb5, 0x38, 0x99, 0x5e, 0x79, 0x37, 0x16, 0x93, 0x3c, 0x6a,
-	0x96, 0xed, 0xa6, 0xdb, 0xfe, 0x84, 0x8f, 0x63, 0x36, 0xbe, 0xd4, 0x9b, 0x88, 0x9a, 0x29, 0x1a,
-	0x4c, 0xc0, 0x04, 0x3d, 0x5a, 0x64, 0x94, 0xeb, 0x2c, 0xef, 0x66, 0xf0, 0x40, 0x0f, 0x19, 0xdd,
-	0x5f, 0xbc, 0x9e, 0xff, 0xb7, 0x14, 0xe4, 0x15, 0xbf, 0x03, 0x00, 0x00, 0xff, 0xff, 0x64, 0x04,
-	0xa8, 0xf4, 0x54, 0x01, 0x00, 0x00,
+var file_upload_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
+var file_upload_proto_goTypes = []interface{}{
+	(*Upload)(nil), // 0: soong_metrics_upload.Upload
+}
+var file_upload_proto_depIdxs = []int32{
+	0, // [0:0] is the sub-list for method output_type
+	0, // [0:0] is the sub-list for method input_type
+	0, // [0:0] is the sub-list for extension type_name
+	0, // [0:0] is the sub-list for extension extendee
+	0, // [0:0] is the sub-list for field type_name
+}
+
+func init() { file_upload_proto_init() }
+func file_upload_proto_init() {
+	if File_upload_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_upload_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Upload); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_upload_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   1,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_upload_proto_goTypes,
+		DependencyIndexes: file_upload_proto_depIdxs,
+		MessageInfos:      file_upload_proto_msgTypes,
+	}.Build()
+	File_upload_proto = out.File
+	file_upload_proto_rawDesc = nil
+	file_upload_proto_goTypes = nil
+	file_upload_proto_depIdxs = nil
 }
diff --git a/ui/metrics/upload_proto/upload.proto b/ui/metrics/upload_proto/upload.proto
index bcd0ab2..e621fd1 100644
--- a/ui/metrics/upload_proto/upload.proto
+++ b/ui/metrics/upload_proto/upload.proto
@@ -15,7 +15,7 @@
 syntax = "proto2";
 
 package soong_metrics_upload;
-option go_package = "soong_metrics_upload_proto";
+option go_package = "android/soong/ui/metrics/upload_proto";
 
 message Upload {
   // The timestamp in milliseconds that the build was created.
diff --git a/cmd/host_bionic_inject/Android.bp b/ui/signal/Android.bp
similarity index 73%
copy from cmd/host_bionic_inject/Android.bp
copy to ui/signal/Android.bp
index 16bc179..08933a1 100644
--- a/cmd/host_bionic_inject/Android.bp
+++ b/ui/signal/Android.bp
@@ -1,4 +1,4 @@
-// Copyright 2018 Google Inc. All rights reserved.
+// Copyright 2021 Google Inc. All rights reserved.
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -16,9 +16,13 @@
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
-blueprint_go_binary {
-    name: "host_bionic_inject",
-    deps: ["soong-symbol_inject"],
-    srcs: ["host_bionic_inject.go"],
-    testSrcs: ["host_bionic_inject_test.go"],
+bootstrap_go_package {
+    name: "soong-ui-signal",
+    pkgPath: "android/soong/ui/signal",
+    srcs: [
+        "signal.go",
+    ],
+    deps: [
+        "soong-ui-logger",
+    ],
 }
diff --git a/ui/build/signal.go b/ui/signal/signal.go
similarity index 99%
rename from ui/build/signal.go
rename to ui/signal/signal.go
index 6643e2f..4929a7b 100644
--- a/ui/build/signal.go
+++ b/ui/signal/signal.go
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-package build
+package signal
 
 import (
 	"os"
diff --git a/ui/status/Android.bp b/ui/status/Android.bp
index ac31390..a46a007 100644
--- a/ui/status/Android.bp
+++ b/ui/status/Android.bp
@@ -44,7 +44,10 @@
 bootstrap_go_package {
     name: "soong-ui-status-ninja_frontend",
     pkgPath: "android/soong/ui/status/ninja_frontend",
-    deps: ["golang-protobuf-proto"],
+    deps: [
+        "golang-protobuf-reflect-protoreflect",
+        "golang-protobuf-runtime-protoimpl",
+    ],
     srcs: [
         "ninja_frontend/frontend.pb.go",
     ],
@@ -53,7 +56,10 @@
 bootstrap_go_package {
     name: "soong-ui-status-build_error_proto",
     pkgPath: "android/soong/ui/status/build_error_proto",
-    deps: ["golang-protobuf-proto"],
+    deps: [
+        "golang-protobuf-reflect-protoreflect",
+        "golang-protobuf-runtime-protoimpl",
+    ],
     srcs: [
         "build_error_proto/build_error.pb.go",
     ],
@@ -62,7 +68,10 @@
 bootstrap_go_package {
     name: "soong-ui-status-build_progress_proto",
     pkgPath: "android/soong/ui/status/build_progress_proto",
-    deps: ["golang-protobuf-proto"],
+    deps: [
+        "golang-protobuf-reflect-protoreflect",
+        "golang-protobuf-runtime-protoimpl",
+    ],
     srcs: [
         "build_progress_proto/build_progress.pb.go",
     ],
diff --git a/ui/status/build_error_proto/build_error.pb.go b/ui/status/build_error_proto/build_error.pb.go
index d4d0a6e..22125c2 100644
--- a/ui/status/build_error_proto/build_error.pb.go
+++ b/ui/status/build_error_proto/build_error.pb.go
@@ -1,71 +1,93 @@
+// Copyright 2019 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 // Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.26.0
+// 	protoc        v3.9.1
 // source: build_error.proto
 
-package soong_build_error_proto
+package build_error_proto
 
 import (
-	fmt "fmt"
-	proto "github.com/golang/protobuf/proto"
-	math "math"
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
 )
 
-// Reference imports to suppress errors if they are not otherwise used.
-var _ = proto.Marshal
-var _ = fmt.Errorf
-var _ = math.Inf
-
-// This is a compile-time assertion to ensure that this generated file
-// is compatible with the proto package it is being compiled against.
-// A compilation error at this line likely means your copy of the
-// proto package needs to be updated.
-const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
 
 type BuildError struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	// List of error messages of the overall build. The error messages
 	// are not associated with a build action.
 	ErrorMessages []string `protobuf:"bytes,1,rep,name=error_messages,json=errorMessages" json:"error_messages,omitempty"`
 	// List of build action errors.
-	ActionErrors         []*BuildActionError `protobuf:"bytes,2,rep,name=action_errors,json=actionErrors" json:"action_errors,omitempty"`
-	XXX_NoUnkeyedLiteral struct{}            `json:"-"`
-	XXX_unrecognized     []byte              `json:"-"`
-	XXX_sizecache        int32               `json:"-"`
+	ActionErrors []*BuildActionError `protobuf:"bytes,2,rep,name=action_errors,json=actionErrors" json:"action_errors,omitempty"`
 }
 
-func (m *BuildError) Reset()         { *m = BuildError{} }
-func (m *BuildError) String() string { return proto.CompactTextString(m) }
-func (*BuildError) ProtoMessage()    {}
+func (x *BuildError) Reset() {
+	*x = BuildError{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_build_error_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *BuildError) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*BuildError) ProtoMessage() {}
+
+func (x *BuildError) ProtoReflect() protoreflect.Message {
+	mi := &file_build_error_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use BuildError.ProtoReflect.Descriptor instead.
 func (*BuildError) Descriptor() ([]byte, []int) {
-	return fileDescriptor_a2e15b05802a5501, []int{0}
+	return file_build_error_proto_rawDescGZIP(), []int{0}
 }
 
-func (m *BuildError) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_BuildError.Unmarshal(m, b)
-}
-func (m *BuildError) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_BuildError.Marshal(b, m, deterministic)
-}
-func (m *BuildError) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_BuildError.Merge(m, src)
-}
-func (m *BuildError) XXX_Size() int {
-	return xxx_messageInfo_BuildError.Size(m)
-}
-func (m *BuildError) XXX_DiscardUnknown() {
-	xxx_messageInfo_BuildError.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_BuildError proto.InternalMessageInfo
-
-func (m *BuildError) GetErrorMessages() []string {
-	if m != nil {
-		return m.ErrorMessages
+func (x *BuildError) GetErrorMessages() []string {
+	if x != nil {
+		return x.ErrorMessages
 	}
 	return nil
 }
 
-func (m *BuildError) GetActionErrors() []*BuildActionError {
-	if m != nil {
-		return m.ActionErrors
+func (x *BuildError) GetActionErrors() []*BuildActionError {
+	if x != nil {
+		return x.ActionErrors
 	}
 	return nil
 }
@@ -73,6 +95,10 @@
 // Build is composed of a list of build action. There can be a set of build
 // actions that can failed.
 type BuildActionError struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	// Description of the command.
 	Description *string `protobuf:"bytes,1,opt,name=description" json:"description,omitempty"`
 	// The command name that raised the error.
@@ -82,94 +108,177 @@
 	// List of artifacts (i.e. files) that was produced by the command.
 	Artifacts []string `protobuf:"bytes,4,rep,name=artifacts" json:"artifacts,omitempty"`
 	// The error string produced by the build action.
-	Error                *string  `protobuf:"bytes,5,opt,name=error" json:"error,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
+	Error *string `protobuf:"bytes,5,opt,name=error" json:"error,omitempty"`
 }
 
-func (m *BuildActionError) Reset()         { *m = BuildActionError{} }
-func (m *BuildActionError) String() string { return proto.CompactTextString(m) }
-func (*BuildActionError) ProtoMessage()    {}
+func (x *BuildActionError) Reset() {
+	*x = BuildActionError{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_build_error_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *BuildActionError) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*BuildActionError) ProtoMessage() {}
+
+func (x *BuildActionError) ProtoReflect() protoreflect.Message {
+	mi := &file_build_error_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use BuildActionError.ProtoReflect.Descriptor instead.
 func (*BuildActionError) Descriptor() ([]byte, []int) {
-	return fileDescriptor_a2e15b05802a5501, []int{1}
+	return file_build_error_proto_rawDescGZIP(), []int{1}
 }
 
-func (m *BuildActionError) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_BuildActionError.Unmarshal(m, b)
-}
-func (m *BuildActionError) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_BuildActionError.Marshal(b, m, deterministic)
-}
-func (m *BuildActionError) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_BuildActionError.Merge(m, src)
-}
-func (m *BuildActionError) XXX_Size() int {
-	return xxx_messageInfo_BuildActionError.Size(m)
-}
-func (m *BuildActionError) XXX_DiscardUnknown() {
-	xxx_messageInfo_BuildActionError.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_BuildActionError proto.InternalMessageInfo
-
-func (m *BuildActionError) GetDescription() string {
-	if m != nil && m.Description != nil {
-		return *m.Description
+func (x *BuildActionError) GetDescription() string {
+	if x != nil && x.Description != nil {
+		return *x.Description
 	}
 	return ""
 }
 
-func (m *BuildActionError) GetCommand() string {
-	if m != nil && m.Command != nil {
-		return *m.Command
+func (x *BuildActionError) GetCommand() string {
+	if x != nil && x.Command != nil {
+		return *x.Command
 	}
 	return ""
 }
 
-func (m *BuildActionError) GetOutput() string {
-	if m != nil && m.Output != nil {
-		return *m.Output
+func (x *BuildActionError) GetOutput() string {
+	if x != nil && x.Output != nil {
+		return *x.Output
 	}
 	return ""
 }
 
-func (m *BuildActionError) GetArtifacts() []string {
-	if m != nil {
-		return m.Artifacts
+func (x *BuildActionError) GetArtifacts() []string {
+	if x != nil {
+		return x.Artifacts
 	}
 	return nil
 }
 
-func (m *BuildActionError) GetError() string {
-	if m != nil && m.Error != nil {
-		return *m.Error
+func (x *BuildActionError) GetError() string {
+	if x != nil && x.Error != nil {
+		return *x.Error
 	}
 	return ""
 }
 
-func init() {
-	proto.RegisterType((*BuildError)(nil), "soong_build_error.BuildError")
-	proto.RegisterType((*BuildActionError)(nil), "soong_build_error.BuildActionError")
+var File_build_error_proto protoreflect.FileDescriptor
+
+var file_build_error_proto_rawDesc = []byte{
+	0x0a, 0x11, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x2e, 0x70, 0x72,
+	0x6f, 0x74, 0x6f, 0x12, 0x11, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64,
+	0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x22, 0x7d, 0x0a, 0x0a, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x45,
+	0x72, 0x72, 0x6f, 0x72, 0x12, 0x25, 0x0a, 0x0e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x6d, 0x65,
+	0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0d, 0x65, 0x72,
+	0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x73, 0x12, 0x48, 0x0a, 0x0d, 0x61,
+	0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x18, 0x02, 0x20, 0x03,
+	0x28, 0x0b, 0x32, 0x23, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64,
+	0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x41, 0x63, 0x74, 0x69,
+	0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x52, 0x0c, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45,
+	0x72, 0x72, 0x6f, 0x72, 0x73, 0x22, 0x9a, 0x01, 0x0a, 0x10, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x41,
+	0x63, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65,
+	0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07,
+	0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63,
+	0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74,
+	0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x1c,
+	0x0a, 0x09, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28,
+	0x09, 0x52, 0x09, 0x61, 0x72, 0x74, 0x69, 0x66, 0x61, 0x63, 0x74, 0x73, 0x12, 0x14, 0x0a, 0x05,
+	0x65, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72,
+	0x6f, 0x72, 0x42, 0x2b, 0x5a, 0x29, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f,
+	0x6f, 0x6e, 0x67, 0x2f, 0x75, 0x69, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2f, 0x62, 0x75,
+	0x69, 0x6c, 0x64, 0x5f, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
 }
 
-func init() { proto.RegisterFile("build_error.proto", fileDescriptor_a2e15b05802a5501) }
+var (
+	file_build_error_proto_rawDescOnce sync.Once
+	file_build_error_proto_rawDescData = file_build_error_proto_rawDesc
+)
 
-var fileDescriptor_a2e15b05802a5501 = []byte{
-	// 229 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x90, 0xc1, 0x4a, 0xc3, 0x40,
-	0x10, 0x86, 0x49, 0x63, 0x95, 0x4c, 0xad, 0xd8, 0x41, 0x74, 0x04, 0x0f, 0xa1, 0x22, 0xe4, 0x94,
-	0x83, 0x6f, 0x60, 0x41, 0xf0, 0xe2, 0x25, 0x47, 0x2f, 0x61, 0xdd, 0xac, 0x65, 0xc1, 0x64, 0xc2,
-	0xce, 0xe6, 0xe8, 0x8b, 0xf8, 0xb4, 0x92, 0x69, 0xa5, 0xa5, 0x39, 0x7e, 0xdf, 0x3f, 0xfb, 0xef,
-	0xce, 0xc2, 0xea, 0x73, 0xf0, 0xdf, 0x4d, 0xed, 0x42, 0xe0, 0x50, 0xf6, 0x81, 0x23, 0xe3, 0x4a,
-	0x98, 0xbb, 0x6d, 0x7d, 0x14, 0xac, 0x7f, 0x00, 0x36, 0x23, 0xbe, 0x8e, 0x84, 0x4f, 0x70, 0xa5,
-	0xba, 0x6e, 0x9d, 0x88, 0xd9, 0x3a, 0xa1, 0x24, 0x4f, 0x8b, 0xac, 0x5a, 0xaa, 0x7d, 0xdf, 0x4b,
-	0x7c, 0x83, 0xa5, 0xb1, 0xd1, 0x73, 0xb7, 0x2b, 0x11, 0x9a, 0xe5, 0x69, 0xb1, 0x78, 0x7e, 0x2c,
-	0x27, 0xfd, 0xa5, 0x96, 0xbf, 0xe8, 0xb0, 0x5e, 0x51, 0x5d, 0x9a, 0x03, 0xc8, 0xfa, 0x37, 0x81,
-	0xeb, 0xd3, 0x11, 0xcc, 0x61, 0xd1, 0x38, 0xb1, 0xc1, 0xf7, 0xa3, 0xa3, 0x24, 0x4f, 0x8a, 0xac,
-	0x3a, 0x56, 0x48, 0x70, 0x61, 0xb9, 0x6d, 0x4d, 0xd7, 0xd0, 0x4c, 0xd3, 0x7f, 0xc4, 0x5b, 0x38,
-	0xe7, 0x21, 0xf6, 0x43, 0xa4, 0x54, 0x83, 0x3d, 0xe1, 0x03, 0x64, 0x26, 0x44, 0xff, 0x65, 0x6c,
-	0x14, 0x3a, 0xd3, 0xa5, 0x0e, 0x02, 0x6f, 0x60, 0xae, 0xcf, 0xa5, 0xb9, 0x1e, 0xda, 0xc1, 0xe6,
-	0xfe, 0xe3, 0x6e, 0xb2, 0x50, 0xad, 0x3f, 0xf9, 0x17, 0x00, 0x00, 0xff, 0xff, 0xb6, 0x18, 0x9e,
-	0x17, 0x5d, 0x01, 0x00, 0x00,
+func file_build_error_proto_rawDescGZIP() []byte {
+	file_build_error_proto_rawDescOnce.Do(func() {
+		file_build_error_proto_rawDescData = protoimpl.X.CompressGZIP(file_build_error_proto_rawDescData)
+	})
+	return file_build_error_proto_rawDescData
+}
+
+var file_build_error_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
+var file_build_error_proto_goTypes = []interface{}{
+	(*BuildError)(nil),       // 0: soong_build_error.BuildError
+	(*BuildActionError)(nil), // 1: soong_build_error.BuildActionError
+}
+var file_build_error_proto_depIdxs = []int32{
+	1, // 0: soong_build_error.BuildError.action_errors:type_name -> soong_build_error.BuildActionError
+	1, // [1:1] is the sub-list for method output_type
+	1, // [1:1] is the sub-list for method input_type
+	1, // [1:1] is the sub-list for extension type_name
+	1, // [1:1] is the sub-list for extension extendee
+	0, // [0:1] is the sub-list for field type_name
+}
+
+func init() { file_build_error_proto_init() }
+func file_build_error_proto_init() {
+	if File_build_error_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_build_error_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*BuildError); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_build_error_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*BuildActionError); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_build_error_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   2,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_build_error_proto_goTypes,
+		DependencyIndexes: file_build_error_proto_depIdxs,
+		MessageInfos:      file_build_error_proto_msgTypes,
+	}.Build()
+	File_build_error_proto = out.File
+	file_build_error_proto_rawDesc = nil
+	file_build_error_proto_goTypes = nil
+	file_build_error_proto_depIdxs = nil
 }
diff --git a/ui/status/build_error_proto/build_error.proto b/ui/status/build_error_proto/build_error.proto
index 9c8470d..ecd6074 100644
--- a/ui/status/build_error_proto/build_error.proto
+++ b/ui/status/build_error_proto/build_error.proto
@@ -15,7 +15,7 @@
 syntax = "proto2";
 
 package soong_build_error;
-option go_package = "soong_build_error_proto";
+option go_package = "android/soong/ui/status/build_error_proto";
 
 message BuildError {
   // List of error messages of the overall build. The error messages
diff --git a/ui/status/build_progress_proto/build_progress.pb.go b/ui/status/build_progress_proto/build_progress.pb.go
index f63c157..e243fe0 100644
--- a/ui/status/build_progress_proto/build_progress.pb.go
+++ b/ui/status/build_progress_proto/build_progress.pb.go
@@ -1,26 +1,44 @@
+// Copyright 2020 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 // Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.26.0
+// 	protoc        v3.9.1
 // source: build_progress.proto
 
-package soong_build_progress_proto
+package build_progress_proto
 
 import (
-	fmt "fmt"
-	proto "github.com/golang/protobuf/proto"
-	math "math"
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
 )
 
-// Reference imports to suppress errors if they are not otherwise used.
-var _ = proto.Marshal
-var _ = fmt.Errorf
-var _ = math.Inf
-
-// This is a compile-time assertion to ensure that this generated file
-// is compatible with the proto package it is being compiled against.
-// A compilation error at this line likely means your copy of the
-// proto package needs to be updated.
-const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
 
 type BuildProgress struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	// Total number of actions in a build. The total actions will increase
 	// and might decrease during the course of a build.
 	TotalActions *uint64 `protobuf:"varint,1,opt,name=total_actions,json=totalActions" json:"total_actions,omitempty"`
@@ -34,82 +52,150 @@
 	// build and current_actions + finished_actions <= total_actions.
 	CurrentActions *uint64 `protobuf:"varint,3,opt,name=current_actions,json=currentActions" json:"current_actions,omitempty"`
 	// Total number of actions that reported as a failure.
-	FailedActions        *uint64  `protobuf:"varint,4,opt,name=failed_actions,json=failedActions" json:"failed_actions,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
+	FailedActions *uint64 `protobuf:"varint,4,opt,name=failed_actions,json=failedActions" json:"failed_actions,omitempty"`
 }
 
-func (m *BuildProgress) Reset()         { *m = BuildProgress{} }
-func (m *BuildProgress) String() string { return proto.CompactTextString(m) }
-func (*BuildProgress) ProtoMessage()    {}
+func (x *BuildProgress) Reset() {
+	*x = BuildProgress{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_build_progress_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *BuildProgress) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*BuildProgress) ProtoMessage() {}
+
+func (x *BuildProgress) ProtoReflect() protoreflect.Message {
+	mi := &file_build_progress_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use BuildProgress.ProtoReflect.Descriptor instead.
 func (*BuildProgress) Descriptor() ([]byte, []int) {
-	return fileDescriptor_a8a463f8e30dab2e, []int{0}
+	return file_build_progress_proto_rawDescGZIP(), []int{0}
 }
 
-func (m *BuildProgress) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_BuildProgress.Unmarshal(m, b)
-}
-func (m *BuildProgress) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_BuildProgress.Marshal(b, m, deterministic)
-}
-func (m *BuildProgress) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_BuildProgress.Merge(m, src)
-}
-func (m *BuildProgress) XXX_Size() int {
-	return xxx_messageInfo_BuildProgress.Size(m)
-}
-func (m *BuildProgress) XXX_DiscardUnknown() {
-	xxx_messageInfo_BuildProgress.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_BuildProgress proto.InternalMessageInfo
-
-func (m *BuildProgress) GetTotalActions() uint64 {
-	if m != nil && m.TotalActions != nil {
-		return *m.TotalActions
+func (x *BuildProgress) GetTotalActions() uint64 {
+	if x != nil && x.TotalActions != nil {
+		return *x.TotalActions
 	}
 	return 0
 }
 
-func (m *BuildProgress) GetFinishedActions() uint64 {
-	if m != nil && m.FinishedActions != nil {
-		return *m.FinishedActions
+func (x *BuildProgress) GetFinishedActions() uint64 {
+	if x != nil && x.FinishedActions != nil {
+		return *x.FinishedActions
 	}
 	return 0
 }
 
-func (m *BuildProgress) GetCurrentActions() uint64 {
-	if m != nil && m.CurrentActions != nil {
-		return *m.CurrentActions
+func (x *BuildProgress) GetCurrentActions() uint64 {
+	if x != nil && x.CurrentActions != nil {
+		return *x.CurrentActions
 	}
 	return 0
 }
 
-func (m *BuildProgress) GetFailedActions() uint64 {
-	if m != nil && m.FailedActions != nil {
-		return *m.FailedActions
+func (x *BuildProgress) GetFailedActions() uint64 {
+	if x != nil && x.FailedActions != nil {
+		return *x.FailedActions
 	}
 	return 0
 }
 
-func init() {
-	proto.RegisterType((*BuildProgress)(nil), "soong_build_progress.BuildProgress")
+var File_build_progress_proto protoreflect.FileDescriptor
+
+var file_build_progress_proto_rawDesc = []byte{
+	0x0a, 0x14, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73,
+	0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x14, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75,
+	0x69, 0x6c, 0x64, 0x5f, 0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x22, 0xaf, 0x01, 0x0a,
+	0x0d, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x50, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x12, 0x23,
+	0x0a, 0x0d, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0c, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x41, 0x63, 0x74, 0x69,
+	0x6f, 0x6e, 0x73, 0x12, 0x29, 0x0a, 0x10, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x5f,
+	0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x66,
+	0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x27,
+	0x0a, 0x0f, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e,
+	0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74,
+	0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x66, 0x61, 0x69, 0x6c, 0x65,
+	0x64, 0x5f, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52,
+	0x0d, 0x66, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x2e,
+	0x5a, 0x2c, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f,
+	0x75, 0x69, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f,
+	0x70, 0x72, 0x6f, 0x67, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
 }
 
-func init() { proto.RegisterFile("build_progress.proto", fileDescriptor_a8a463f8e30dab2e) }
+var (
+	file_build_progress_proto_rawDescOnce sync.Once
+	file_build_progress_proto_rawDescData = file_build_progress_proto_rawDesc
+)
 
-var fileDescriptor_a8a463f8e30dab2e = []byte{
-	// 165 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x49, 0x2a, 0xcd, 0xcc,
-	0x49, 0x89, 0x2f, 0x28, 0xca, 0x4f, 0x2f, 0x4a, 0x2d, 0x2e, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9,
-	0x17, 0x12, 0x29, 0xce, 0xcf, 0xcf, 0x4b, 0x8f, 0x47, 0x95, 0x53, 0x5a, 0xcf, 0xc8, 0xc5, 0xeb,
-	0x04, 0x12, 0x0a, 0x80, 0x8a, 0x08, 0x29, 0x73, 0xf1, 0x96, 0xe4, 0x97, 0x24, 0xe6, 0xc4, 0x27,
-	0x26, 0x97, 0x64, 0xe6, 0xe7, 0x15, 0x4b, 0x30, 0x2a, 0x30, 0x6a, 0xb0, 0x04, 0xf1, 0x80, 0x05,
-	0x1d, 0x21, 0x62, 0x42, 0x9a, 0x5c, 0x02, 0x69, 0x99, 0x79, 0x99, 0xc5, 0x19, 0xa9, 0x29, 0x70,
-	0x75, 0x4c, 0x60, 0x75, 0xfc, 0x30, 0x71, 0x98, 0x52, 0x75, 0x2e, 0xfe, 0xe4, 0xd2, 0xa2, 0xa2,
-	0xd4, 0xbc, 0x12, 0xb8, 0x4a, 0x66, 0xb0, 0x4a, 0x3e, 0xa8, 0x30, 0x4c, 0xa1, 0x2a, 0x17, 0x5f,
-	0x5a, 0x62, 0x66, 0x0e, 0x92, 0x89, 0x2c, 0x60, 0x75, 0xbc, 0x10, 0x51, 0xa8, 0x32, 0x27, 0x99,
-	0x28, 0x29, 0x6c, 0x3e, 0x89, 0x07, 0xfb, 0x12, 0x10, 0x00, 0x00, 0xff, 0xff, 0x3f, 0x6e, 0xc1,
-	0xef, 0xfc, 0x00, 0x00, 0x00,
+func file_build_progress_proto_rawDescGZIP() []byte {
+	file_build_progress_proto_rawDescOnce.Do(func() {
+		file_build_progress_proto_rawDescData = protoimpl.X.CompressGZIP(file_build_progress_proto_rawDescData)
+	})
+	return file_build_progress_proto_rawDescData
+}
+
+var file_build_progress_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
+var file_build_progress_proto_goTypes = []interface{}{
+	(*BuildProgress)(nil), // 0: soong_build_progress.BuildProgress
+}
+var file_build_progress_proto_depIdxs = []int32{
+	0, // [0:0] is the sub-list for method output_type
+	0, // [0:0] is the sub-list for method input_type
+	0, // [0:0] is the sub-list for extension type_name
+	0, // [0:0] is the sub-list for extension extendee
+	0, // [0:0] is the sub-list for field type_name
+}
+
+func init() { file_build_progress_proto_init() }
+func file_build_progress_proto_init() {
+	if File_build_progress_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_build_progress_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*BuildProgress); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_build_progress_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   1,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_build_progress_proto_goTypes,
+		DependencyIndexes: file_build_progress_proto_depIdxs,
+		MessageInfos:      file_build_progress_proto_msgTypes,
+	}.Build()
+	File_build_progress_proto = out.File
+	file_build_progress_proto_rawDesc = nil
+	file_build_progress_proto_goTypes = nil
+	file_build_progress_proto_depIdxs = nil
 }
diff --git a/ui/status/build_progress_proto/build_progress.proto b/ui/status/build_progress_proto/build_progress.proto
index d78060a..2672622 100644
--- a/ui/status/build_progress_proto/build_progress.proto
+++ b/ui/status/build_progress_proto/build_progress.proto
@@ -15,7 +15,7 @@
 syntax = "proto2";
 
 package soong_build_progress;
-option go_package = "soong_build_progress_proto";
+option go_package = "android/soong/ui/status/build_progress_proto";
 
 message BuildProgress {
   // Total number of actions in a build. The total actions will increase
diff --git a/ui/status/log.go b/ui/status/log.go
index 4a08acb..14df346 100644
--- a/ui/status/log.go
+++ b/ui/status/log.go
@@ -23,11 +23,11 @@
 	"os"
 	"strings"
 
-	"github.com/golang/protobuf/proto"
+	"google.golang.org/protobuf/proto"
 
 	"android/soong/ui/logger"
-	"android/soong/ui/status/build_error_proto"
-	"android/soong/ui/status/build_progress_proto"
+	soong_build_error_proto "android/soong/ui/status/build_error_proto"
+	soong_build_progress_proto "android/soong/ui/status/build_progress_proto"
 )
 
 type verboseLog struct {
diff --git a/ui/status/ninja.go b/ui/status/ninja.go
index 765679f..4d99621 100644
--- a/ui/status/ninja.go
+++ b/ui/status/ninja.go
@@ -19,10 +19,12 @@
 	"fmt"
 	"io"
 	"os"
+	"regexp"
+	"strings"
 	"syscall"
 	"time"
 
-	"github.com/golang/protobuf/proto"
+	"google.golang.org/protobuf/proto"
 
 	"android/soong/ui/logger"
 	"android/soong/ui/status/ninja_frontend"
@@ -158,9 +160,10 @@
 					err = fmt.Errorf("exited with code: %d", exitCode)
 				}
 
+				outputWithErrorHint := errorHintGenerator.GetOutputWithErrorHint(msg.EdgeFinished.GetOutput(), exitCode)
 				n.status.FinishAction(ActionResult{
 					Action: started,
-					Output: msg.EdgeFinished.GetOutput(),
+					Output: outputWithErrorHint,
 					Error:  err,
 					Stats: ActionResultStats{
 						UserTime:                   msg.EdgeFinished.GetUserTime(),
@@ -219,3 +222,53 @@
 
 	return ret, nil
 }
+
+// key is pattern in stdout/stderr
+// value is error hint
+var allErrorHints = map[string]string{
+	"Read-only file system": `\nWrite to a read-only file system detected. Possible fixes include
+1. Generate file directly to out/ which is ReadWrite, #recommend solution
+2. BUILD_BROKEN_SRC_DIR_RW_ALLOWLIST := <my/path/1> <my/path/2> #discouraged, subset of source tree will be RW
+3. BUILD_BROKEN_SRC_DIR_IS_WRITABLE := true #highly discouraged, entire source tree will be RW
+`,
+}
+var errorHintGenerator = *newErrorHintGenerator(allErrorHints)
+
+type ErrorHintGenerator struct {
+	allErrorHints                map[string]string
+	allErrorHintPatternsCompiled *regexp.Regexp
+}
+
+func newErrorHintGenerator(allErrorHints map[string]string) *ErrorHintGenerator {
+	var allErrorHintPatterns []string
+	for errorHintPattern, _ := range allErrorHints {
+		allErrorHintPatterns = append(allErrorHintPatterns, errorHintPattern)
+	}
+	allErrorHintPatternsRegex := strings.Join(allErrorHintPatterns[:], "|")
+	re := regexp.MustCompile(allErrorHintPatternsRegex)
+	return &ErrorHintGenerator{
+		allErrorHints:                allErrorHints,
+		allErrorHintPatternsCompiled: re,
+	}
+}
+
+func (errorHintGenerator *ErrorHintGenerator) GetOutputWithErrorHint(rawOutput string, buildExitCode int) string {
+	if buildExitCode == 0 {
+		return rawOutput
+	}
+	errorHint := errorHintGenerator.getErrorHint(rawOutput)
+	if errorHint == nil {
+		return rawOutput
+	}
+	return rawOutput + *errorHint
+}
+
+// Returns the error hint corresponding to the FIRST match in raw output
+func (errorHintGenerator *ErrorHintGenerator) getErrorHint(rawOutput string) *string {
+	firstMatch := errorHintGenerator.allErrorHintPatternsCompiled.FindString(rawOutput)
+	if _, found := errorHintGenerator.allErrorHints[firstMatch]; found {
+		errorHint := errorHintGenerator.allErrorHints[firstMatch]
+		return &errorHint
+	}
+	return nil
+}
diff --git a/ui/status/ninja_frontend/frontend.pb.go b/ui/status/ninja_frontend/frontend.pb.go
index 86e474b..bcadc67 100644
--- a/ui/status/ninja_frontend/frontend.pb.go
+++ b/ui/status/ninja_frontend/frontend.pb.go
@@ -1,24 +1,38 @@
+// Copyright 2017 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
 // Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.26.0
+// 	protoc        v3.9.1
 // source: frontend.proto
 
 package ninja_frontend
 
 import (
-	fmt "fmt"
-	proto "github.com/golang/protobuf/proto"
-	math "math"
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
 )
 
-// Reference imports to suppress errors if they are not otherwise used.
-var _ = proto.Marshal
-var _ = fmt.Errorf
-var _ = math.Inf
-
-// This is a compile-time assertion to ensure that this generated file
-// is compatible with the proto package it is being compiled against.
-// A compilation error at this line likely means your copy of the
-// proto package needs to be updated.
-const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
 
 type Status_Message_Level int32
 
@@ -29,19 +43,21 @@
 	Status_Message_DEBUG   Status_Message_Level = 3
 )
 
-var Status_Message_Level_name = map[int32]string{
-	0: "INFO",
-	1: "WARNING",
-	2: "ERROR",
-	3: "DEBUG",
-}
-
-var Status_Message_Level_value = map[string]int32{
-	"INFO":    0,
-	"WARNING": 1,
-	"ERROR":   2,
-	"DEBUG":   3,
-}
+// Enum value maps for Status_Message_Level.
+var (
+	Status_Message_Level_name = map[int32]string{
+		0: "INFO",
+		1: "WARNING",
+		2: "ERROR",
+		3: "DEBUG",
+	}
+	Status_Message_Level_value = map[string]int32{
+		"INFO":    0,
+		"WARNING": 1,
+		"ERROR":   2,
+		"DEBUG":   3,
+	}
+)
 
 func (x Status_Message_Level) Enum() *Status_Message_Level {
 	p := new(Status_Message_Level)
@@ -50,222 +66,271 @@
 }
 
 func (x Status_Message_Level) String() string {
-	return proto.EnumName(Status_Message_Level_name, int32(x))
+	return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
 }
 
-func (x *Status_Message_Level) UnmarshalJSON(data []byte) error {
-	value, err := proto.UnmarshalJSONEnum(Status_Message_Level_value, data, "Status_Message_Level")
+func (Status_Message_Level) Descriptor() protoreflect.EnumDescriptor {
+	return file_frontend_proto_enumTypes[0].Descriptor()
+}
+
+func (Status_Message_Level) Type() protoreflect.EnumType {
+	return &file_frontend_proto_enumTypes[0]
+}
+
+func (x Status_Message_Level) Number() protoreflect.EnumNumber {
+	return protoreflect.EnumNumber(x)
+}
+
+// Deprecated: Do not use.
+func (x *Status_Message_Level) UnmarshalJSON(b []byte) error {
+	num, err := protoimpl.X.UnmarshalJSONEnum(x.Descriptor(), b)
 	if err != nil {
 		return err
 	}
-	*x = Status_Message_Level(value)
+	*x = Status_Message_Level(num)
 	return nil
 }
 
+// Deprecated: Use Status_Message_Level.Descriptor instead.
 func (Status_Message_Level) EnumDescriptor() ([]byte, []int) {
-	return fileDescriptor_eca3873955a29cfe, []int{0, 5, 0}
+	return file_frontend_proto_rawDescGZIP(), []int{0, 5, 0}
 }
 
 type Status struct {
-	TotalEdges           *Status_TotalEdges    `protobuf:"bytes,1,opt,name=total_edges,json=totalEdges" json:"total_edges,omitempty"`
-	BuildStarted         *Status_BuildStarted  `protobuf:"bytes,2,opt,name=build_started,json=buildStarted" json:"build_started,omitempty"`
-	BuildFinished        *Status_BuildFinished `protobuf:"bytes,3,opt,name=build_finished,json=buildFinished" json:"build_finished,omitempty"`
-	EdgeStarted          *Status_EdgeStarted   `protobuf:"bytes,4,opt,name=edge_started,json=edgeStarted" json:"edge_started,omitempty"`
-	EdgeFinished         *Status_EdgeFinished  `protobuf:"bytes,5,opt,name=edge_finished,json=edgeFinished" json:"edge_finished,omitempty"`
-	Message              *Status_Message       `protobuf:"bytes,6,opt,name=message" json:"message,omitempty"`
-	XXX_NoUnkeyedLiteral struct{}              `json:"-"`
-	XXX_unrecognized     []byte                `json:"-"`
-	XXX_sizecache        int32                 `json:"-"`
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	TotalEdges    *Status_TotalEdges    `protobuf:"bytes,1,opt,name=total_edges,json=totalEdges" json:"total_edges,omitempty"`
+	BuildStarted  *Status_BuildStarted  `protobuf:"bytes,2,opt,name=build_started,json=buildStarted" json:"build_started,omitempty"`
+	BuildFinished *Status_BuildFinished `protobuf:"bytes,3,opt,name=build_finished,json=buildFinished" json:"build_finished,omitempty"`
+	EdgeStarted   *Status_EdgeStarted   `protobuf:"bytes,4,opt,name=edge_started,json=edgeStarted" json:"edge_started,omitempty"`
+	EdgeFinished  *Status_EdgeFinished  `protobuf:"bytes,5,opt,name=edge_finished,json=edgeFinished" json:"edge_finished,omitempty"`
+	Message       *Status_Message       `protobuf:"bytes,6,opt,name=message" json:"message,omitempty"`
 }
 
-func (m *Status) Reset()         { *m = Status{} }
-func (m *Status) String() string { return proto.CompactTextString(m) }
-func (*Status) ProtoMessage()    {}
+func (x *Status) Reset() {
+	*x = Status{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_frontend_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Status) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Status) ProtoMessage() {}
+
+func (x *Status) ProtoReflect() protoreflect.Message {
+	mi := &file_frontend_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Status.ProtoReflect.Descriptor instead.
 func (*Status) Descriptor() ([]byte, []int) {
-	return fileDescriptor_eca3873955a29cfe, []int{0}
+	return file_frontend_proto_rawDescGZIP(), []int{0}
 }
 
-func (m *Status) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_Status.Unmarshal(m, b)
-}
-func (m *Status) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_Status.Marshal(b, m, deterministic)
-}
-func (m *Status) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_Status.Merge(m, src)
-}
-func (m *Status) XXX_Size() int {
-	return xxx_messageInfo_Status.Size(m)
-}
-func (m *Status) XXX_DiscardUnknown() {
-	xxx_messageInfo_Status.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_Status proto.InternalMessageInfo
-
-func (m *Status) GetTotalEdges() *Status_TotalEdges {
-	if m != nil {
-		return m.TotalEdges
+func (x *Status) GetTotalEdges() *Status_TotalEdges {
+	if x != nil {
+		return x.TotalEdges
 	}
 	return nil
 }
 
-func (m *Status) GetBuildStarted() *Status_BuildStarted {
-	if m != nil {
-		return m.BuildStarted
+func (x *Status) GetBuildStarted() *Status_BuildStarted {
+	if x != nil {
+		return x.BuildStarted
 	}
 	return nil
 }
 
-func (m *Status) GetBuildFinished() *Status_BuildFinished {
-	if m != nil {
-		return m.BuildFinished
+func (x *Status) GetBuildFinished() *Status_BuildFinished {
+	if x != nil {
+		return x.BuildFinished
 	}
 	return nil
 }
 
-func (m *Status) GetEdgeStarted() *Status_EdgeStarted {
-	if m != nil {
-		return m.EdgeStarted
+func (x *Status) GetEdgeStarted() *Status_EdgeStarted {
+	if x != nil {
+		return x.EdgeStarted
 	}
 	return nil
 }
 
-func (m *Status) GetEdgeFinished() *Status_EdgeFinished {
-	if m != nil {
-		return m.EdgeFinished
+func (x *Status) GetEdgeFinished() *Status_EdgeFinished {
+	if x != nil {
+		return x.EdgeFinished
 	}
 	return nil
 }
 
-func (m *Status) GetMessage() *Status_Message {
-	if m != nil {
-		return m.Message
+func (x *Status) GetMessage() *Status_Message {
+	if x != nil {
+		return x.Message
 	}
 	return nil
 }
 
 type Status_TotalEdges struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	// New value for total edges in the build.
-	TotalEdges           *uint32  `protobuf:"varint,1,opt,name=total_edges,json=totalEdges" json:"total_edges,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
+	TotalEdges *uint32 `protobuf:"varint,1,opt,name=total_edges,json=totalEdges" json:"total_edges,omitempty"`
 }
 
-func (m *Status_TotalEdges) Reset()         { *m = Status_TotalEdges{} }
-func (m *Status_TotalEdges) String() string { return proto.CompactTextString(m) }
-func (*Status_TotalEdges) ProtoMessage()    {}
+func (x *Status_TotalEdges) Reset() {
+	*x = Status_TotalEdges{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_frontend_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Status_TotalEdges) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Status_TotalEdges) ProtoMessage() {}
+
+func (x *Status_TotalEdges) ProtoReflect() protoreflect.Message {
+	mi := &file_frontend_proto_msgTypes[1]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Status_TotalEdges.ProtoReflect.Descriptor instead.
 func (*Status_TotalEdges) Descriptor() ([]byte, []int) {
-	return fileDescriptor_eca3873955a29cfe, []int{0, 0}
+	return file_frontend_proto_rawDescGZIP(), []int{0, 0}
 }
 
-func (m *Status_TotalEdges) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_Status_TotalEdges.Unmarshal(m, b)
-}
-func (m *Status_TotalEdges) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_Status_TotalEdges.Marshal(b, m, deterministic)
-}
-func (m *Status_TotalEdges) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_Status_TotalEdges.Merge(m, src)
-}
-func (m *Status_TotalEdges) XXX_Size() int {
-	return xxx_messageInfo_Status_TotalEdges.Size(m)
-}
-func (m *Status_TotalEdges) XXX_DiscardUnknown() {
-	xxx_messageInfo_Status_TotalEdges.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_Status_TotalEdges proto.InternalMessageInfo
-
-func (m *Status_TotalEdges) GetTotalEdges() uint32 {
-	if m != nil && m.TotalEdges != nil {
-		return *m.TotalEdges
+func (x *Status_TotalEdges) GetTotalEdges() uint32 {
+	if x != nil && x.TotalEdges != nil {
+		return *x.TotalEdges
 	}
 	return 0
 }
 
 type Status_BuildStarted struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	// Number of jobs Ninja will run in parallel.
 	Parallelism *uint32 `protobuf:"varint,1,opt,name=parallelism" json:"parallelism,omitempty"`
 	// Verbose value passed to ninja.
-	Verbose              *bool    `protobuf:"varint,2,opt,name=verbose" json:"verbose,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
+	Verbose *bool `protobuf:"varint,2,opt,name=verbose" json:"verbose,omitempty"`
 }
 
-func (m *Status_BuildStarted) Reset()         { *m = Status_BuildStarted{} }
-func (m *Status_BuildStarted) String() string { return proto.CompactTextString(m) }
-func (*Status_BuildStarted) ProtoMessage()    {}
+func (x *Status_BuildStarted) Reset() {
+	*x = Status_BuildStarted{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_frontend_proto_msgTypes[2]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Status_BuildStarted) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Status_BuildStarted) ProtoMessage() {}
+
+func (x *Status_BuildStarted) ProtoReflect() protoreflect.Message {
+	mi := &file_frontend_proto_msgTypes[2]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Status_BuildStarted.ProtoReflect.Descriptor instead.
 func (*Status_BuildStarted) Descriptor() ([]byte, []int) {
-	return fileDescriptor_eca3873955a29cfe, []int{0, 1}
+	return file_frontend_proto_rawDescGZIP(), []int{0, 1}
 }
 
-func (m *Status_BuildStarted) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_Status_BuildStarted.Unmarshal(m, b)
-}
-func (m *Status_BuildStarted) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_Status_BuildStarted.Marshal(b, m, deterministic)
-}
-func (m *Status_BuildStarted) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_Status_BuildStarted.Merge(m, src)
-}
-func (m *Status_BuildStarted) XXX_Size() int {
-	return xxx_messageInfo_Status_BuildStarted.Size(m)
-}
-func (m *Status_BuildStarted) XXX_DiscardUnknown() {
-	xxx_messageInfo_Status_BuildStarted.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_Status_BuildStarted proto.InternalMessageInfo
-
-func (m *Status_BuildStarted) GetParallelism() uint32 {
-	if m != nil && m.Parallelism != nil {
-		return *m.Parallelism
+func (x *Status_BuildStarted) GetParallelism() uint32 {
+	if x != nil && x.Parallelism != nil {
+		return *x.Parallelism
 	}
 	return 0
 }
 
-func (m *Status_BuildStarted) GetVerbose() bool {
-	if m != nil && m.Verbose != nil {
-		return *m.Verbose
+func (x *Status_BuildStarted) GetVerbose() bool {
+	if x != nil && x.Verbose != nil {
+		return *x.Verbose
 	}
 	return false
 }
 
 type Status_BuildFinished struct {
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
 }
 
-func (m *Status_BuildFinished) Reset()         { *m = Status_BuildFinished{} }
-func (m *Status_BuildFinished) String() string { return proto.CompactTextString(m) }
-func (*Status_BuildFinished) ProtoMessage()    {}
+func (x *Status_BuildFinished) Reset() {
+	*x = Status_BuildFinished{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_frontend_proto_msgTypes[3]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Status_BuildFinished) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Status_BuildFinished) ProtoMessage() {}
+
+func (x *Status_BuildFinished) ProtoReflect() protoreflect.Message {
+	mi := &file_frontend_proto_msgTypes[3]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Status_BuildFinished.ProtoReflect.Descriptor instead.
 func (*Status_BuildFinished) Descriptor() ([]byte, []int) {
-	return fileDescriptor_eca3873955a29cfe, []int{0, 2}
+	return file_frontend_proto_rawDescGZIP(), []int{0, 2}
 }
 
-func (m *Status_BuildFinished) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_Status_BuildFinished.Unmarshal(m, b)
-}
-func (m *Status_BuildFinished) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_Status_BuildFinished.Marshal(b, m, deterministic)
-}
-func (m *Status_BuildFinished) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_Status_BuildFinished.Merge(m, src)
-}
-func (m *Status_BuildFinished) XXX_Size() int {
-	return xxx_messageInfo_Status_BuildFinished.Size(m)
-}
-func (m *Status_BuildFinished) XXX_DiscardUnknown() {
-	xxx_messageInfo_Status_BuildFinished.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_Status_BuildFinished proto.InternalMessageInfo
-
 type Status_EdgeStarted struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	// Edge identification number, unique to a Ninja run.
 	Id *uint32 `protobuf:"varint,1,opt,name=id" json:"id,omitempty"`
 	// Edge start time in milliseconds since Ninja started.
@@ -279,87 +344,95 @@
 	// Command field from the edge.
 	Command *string `protobuf:"bytes,6,opt,name=command" json:"command,omitempty"`
 	// Edge uses console.
-	Console              *bool    `protobuf:"varint,7,opt,name=console" json:"console,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
+	Console *bool `protobuf:"varint,7,opt,name=console" json:"console,omitempty"`
 }
 
-func (m *Status_EdgeStarted) Reset()         { *m = Status_EdgeStarted{} }
-func (m *Status_EdgeStarted) String() string { return proto.CompactTextString(m) }
-func (*Status_EdgeStarted) ProtoMessage()    {}
+func (x *Status_EdgeStarted) Reset() {
+	*x = Status_EdgeStarted{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_frontend_proto_msgTypes[4]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Status_EdgeStarted) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Status_EdgeStarted) ProtoMessage() {}
+
+func (x *Status_EdgeStarted) ProtoReflect() protoreflect.Message {
+	mi := &file_frontend_proto_msgTypes[4]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Status_EdgeStarted.ProtoReflect.Descriptor instead.
 func (*Status_EdgeStarted) Descriptor() ([]byte, []int) {
-	return fileDescriptor_eca3873955a29cfe, []int{0, 3}
+	return file_frontend_proto_rawDescGZIP(), []int{0, 3}
 }
 
-func (m *Status_EdgeStarted) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_Status_EdgeStarted.Unmarshal(m, b)
-}
-func (m *Status_EdgeStarted) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_Status_EdgeStarted.Marshal(b, m, deterministic)
-}
-func (m *Status_EdgeStarted) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_Status_EdgeStarted.Merge(m, src)
-}
-func (m *Status_EdgeStarted) XXX_Size() int {
-	return xxx_messageInfo_Status_EdgeStarted.Size(m)
-}
-func (m *Status_EdgeStarted) XXX_DiscardUnknown() {
-	xxx_messageInfo_Status_EdgeStarted.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_Status_EdgeStarted proto.InternalMessageInfo
-
-func (m *Status_EdgeStarted) GetId() uint32 {
-	if m != nil && m.Id != nil {
-		return *m.Id
+func (x *Status_EdgeStarted) GetId() uint32 {
+	if x != nil && x.Id != nil {
+		return *x.Id
 	}
 	return 0
 }
 
-func (m *Status_EdgeStarted) GetStartTime() uint32 {
-	if m != nil && m.StartTime != nil {
-		return *m.StartTime
+func (x *Status_EdgeStarted) GetStartTime() uint32 {
+	if x != nil && x.StartTime != nil {
+		return *x.StartTime
 	}
 	return 0
 }
 
-func (m *Status_EdgeStarted) GetInputs() []string {
-	if m != nil {
-		return m.Inputs
+func (x *Status_EdgeStarted) GetInputs() []string {
+	if x != nil {
+		return x.Inputs
 	}
 	return nil
 }
 
-func (m *Status_EdgeStarted) GetOutputs() []string {
-	if m != nil {
-		return m.Outputs
+func (x *Status_EdgeStarted) GetOutputs() []string {
+	if x != nil {
+		return x.Outputs
 	}
 	return nil
 }
 
-func (m *Status_EdgeStarted) GetDesc() string {
-	if m != nil && m.Desc != nil {
-		return *m.Desc
+func (x *Status_EdgeStarted) GetDesc() string {
+	if x != nil && x.Desc != nil {
+		return *x.Desc
 	}
 	return ""
 }
 
-func (m *Status_EdgeStarted) GetCommand() string {
-	if m != nil && m.Command != nil {
-		return *m.Command
+func (x *Status_EdgeStarted) GetCommand() string {
+	if x != nil && x.Command != nil {
+		return *x.Command
 	}
 	return ""
 }
 
-func (m *Status_EdgeStarted) GetConsole() bool {
-	if m != nil && m.Console != nil {
-		return *m.Console
+func (x *Status_EdgeStarted) GetConsole() bool {
+	if x != nil && x.Console != nil {
+		return *x.Console
 	}
 	return false
 }
 
 type Status_EdgeFinished struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	// Edge identification number, unique to a Ninja run.
 	Id *uint32 `protobuf:"varint,1,opt,name=id" json:"id,omitempty"`
 	// Edge end time in milliseconds since Ninja started.
@@ -385,237 +458,434 @@
 	// Voluntary context switches
 	VoluntaryContextSwitches *uint64 `protobuf:"varint,12,opt,name=voluntary_context_switches,json=voluntaryContextSwitches" json:"voluntary_context_switches,omitempty"`
 	// Involuntary context switches
-	InvoluntaryContextSwitches *uint64  `protobuf:"varint,13,opt,name=involuntary_context_switches,json=involuntaryContextSwitches" json:"involuntary_context_switches,omitempty"`
-	XXX_NoUnkeyedLiteral       struct{} `json:"-"`
-	XXX_unrecognized           []byte   `json:"-"`
-	XXX_sizecache              int32    `json:"-"`
+	InvoluntaryContextSwitches *uint64 `protobuf:"varint,13,opt,name=involuntary_context_switches,json=involuntaryContextSwitches" json:"involuntary_context_switches,omitempty"`
 }
 
-func (m *Status_EdgeFinished) Reset()         { *m = Status_EdgeFinished{} }
-func (m *Status_EdgeFinished) String() string { return proto.CompactTextString(m) }
-func (*Status_EdgeFinished) ProtoMessage()    {}
+func (x *Status_EdgeFinished) Reset() {
+	*x = Status_EdgeFinished{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_frontend_proto_msgTypes[5]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Status_EdgeFinished) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Status_EdgeFinished) ProtoMessage() {}
+
+func (x *Status_EdgeFinished) ProtoReflect() protoreflect.Message {
+	mi := &file_frontend_proto_msgTypes[5]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Status_EdgeFinished.ProtoReflect.Descriptor instead.
 func (*Status_EdgeFinished) Descriptor() ([]byte, []int) {
-	return fileDescriptor_eca3873955a29cfe, []int{0, 4}
+	return file_frontend_proto_rawDescGZIP(), []int{0, 4}
 }
 
-func (m *Status_EdgeFinished) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_Status_EdgeFinished.Unmarshal(m, b)
-}
-func (m *Status_EdgeFinished) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_Status_EdgeFinished.Marshal(b, m, deterministic)
-}
-func (m *Status_EdgeFinished) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_Status_EdgeFinished.Merge(m, src)
-}
-func (m *Status_EdgeFinished) XXX_Size() int {
-	return xxx_messageInfo_Status_EdgeFinished.Size(m)
-}
-func (m *Status_EdgeFinished) XXX_DiscardUnknown() {
-	xxx_messageInfo_Status_EdgeFinished.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_Status_EdgeFinished proto.InternalMessageInfo
-
-func (m *Status_EdgeFinished) GetId() uint32 {
-	if m != nil && m.Id != nil {
-		return *m.Id
+func (x *Status_EdgeFinished) GetId() uint32 {
+	if x != nil && x.Id != nil {
+		return *x.Id
 	}
 	return 0
 }
 
-func (m *Status_EdgeFinished) GetEndTime() uint32 {
-	if m != nil && m.EndTime != nil {
-		return *m.EndTime
+func (x *Status_EdgeFinished) GetEndTime() uint32 {
+	if x != nil && x.EndTime != nil {
+		return *x.EndTime
 	}
 	return 0
 }
 
-func (m *Status_EdgeFinished) GetStatus() int32 {
-	if m != nil && m.Status != nil {
-		return *m.Status
+func (x *Status_EdgeFinished) GetStatus() int32 {
+	if x != nil && x.Status != nil {
+		return *x.Status
 	}
 	return 0
 }
 
-func (m *Status_EdgeFinished) GetOutput() string {
-	if m != nil && m.Output != nil {
-		return *m.Output
+func (x *Status_EdgeFinished) GetOutput() string {
+	if x != nil && x.Output != nil {
+		return *x.Output
 	}
 	return ""
 }
 
-func (m *Status_EdgeFinished) GetUserTime() uint32 {
-	if m != nil && m.UserTime != nil {
-		return *m.UserTime
+func (x *Status_EdgeFinished) GetUserTime() uint32 {
+	if x != nil && x.UserTime != nil {
+		return *x.UserTime
 	}
 	return 0
 }
 
-func (m *Status_EdgeFinished) GetSystemTime() uint32 {
-	if m != nil && m.SystemTime != nil {
-		return *m.SystemTime
+func (x *Status_EdgeFinished) GetSystemTime() uint32 {
+	if x != nil && x.SystemTime != nil {
+		return *x.SystemTime
 	}
 	return 0
 }
 
-func (m *Status_EdgeFinished) GetMaxRssKb() uint64 {
-	if m != nil && m.MaxRssKb != nil {
-		return *m.MaxRssKb
+func (x *Status_EdgeFinished) GetMaxRssKb() uint64 {
+	if x != nil && x.MaxRssKb != nil {
+		return *x.MaxRssKb
 	}
 	return 0
 }
 
-func (m *Status_EdgeFinished) GetMinorPageFaults() uint64 {
-	if m != nil && m.MinorPageFaults != nil {
-		return *m.MinorPageFaults
+func (x *Status_EdgeFinished) GetMinorPageFaults() uint64 {
+	if x != nil && x.MinorPageFaults != nil {
+		return *x.MinorPageFaults
 	}
 	return 0
 }
 
-func (m *Status_EdgeFinished) GetMajorPageFaults() uint64 {
-	if m != nil && m.MajorPageFaults != nil {
-		return *m.MajorPageFaults
+func (x *Status_EdgeFinished) GetMajorPageFaults() uint64 {
+	if x != nil && x.MajorPageFaults != nil {
+		return *x.MajorPageFaults
 	}
 	return 0
 }
 
-func (m *Status_EdgeFinished) GetIoInputKb() uint64 {
-	if m != nil && m.IoInputKb != nil {
-		return *m.IoInputKb
+func (x *Status_EdgeFinished) GetIoInputKb() uint64 {
+	if x != nil && x.IoInputKb != nil {
+		return *x.IoInputKb
 	}
 	return 0
 }
 
-func (m *Status_EdgeFinished) GetIoOutputKb() uint64 {
-	if m != nil && m.IoOutputKb != nil {
-		return *m.IoOutputKb
+func (x *Status_EdgeFinished) GetIoOutputKb() uint64 {
+	if x != nil && x.IoOutputKb != nil {
+		return *x.IoOutputKb
 	}
 	return 0
 }
 
-func (m *Status_EdgeFinished) GetVoluntaryContextSwitches() uint64 {
-	if m != nil && m.VoluntaryContextSwitches != nil {
-		return *m.VoluntaryContextSwitches
+func (x *Status_EdgeFinished) GetVoluntaryContextSwitches() uint64 {
+	if x != nil && x.VoluntaryContextSwitches != nil {
+		return *x.VoluntaryContextSwitches
 	}
 	return 0
 }
 
-func (m *Status_EdgeFinished) GetInvoluntaryContextSwitches() uint64 {
-	if m != nil && m.InvoluntaryContextSwitches != nil {
-		return *m.InvoluntaryContextSwitches
+func (x *Status_EdgeFinished) GetInvoluntaryContextSwitches() uint64 {
+	if x != nil && x.InvoluntaryContextSwitches != nil {
+		return *x.InvoluntaryContextSwitches
 	}
 	return 0
 }
 
 type Status_Message struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
 	// Message priority level (DEBUG, INFO, WARNING, ERROR).
 	Level *Status_Message_Level `protobuf:"varint,1,opt,name=level,enum=ninja.Status_Message_Level,def=0" json:"level,omitempty"`
 	// Info/warning/error message from Ninja.
-	Message              *string  `protobuf:"bytes,2,opt,name=message" json:"message,omitempty"`
-	XXX_NoUnkeyedLiteral struct{} `json:"-"`
-	XXX_unrecognized     []byte   `json:"-"`
-	XXX_sizecache        int32    `json:"-"`
+	Message *string `protobuf:"bytes,2,opt,name=message" json:"message,omitempty"`
 }
 
-func (m *Status_Message) Reset()         { *m = Status_Message{} }
-func (m *Status_Message) String() string { return proto.CompactTextString(m) }
-func (*Status_Message) ProtoMessage()    {}
+// Default values for Status_Message fields.
+const (
+	Default_Status_Message_Level = Status_Message_INFO
+)
+
+func (x *Status_Message) Reset() {
+	*x = Status_Message{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_frontend_proto_msgTypes[6]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *Status_Message) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*Status_Message) ProtoMessage() {}
+
+func (x *Status_Message) ProtoReflect() protoreflect.Message {
+	mi := &file_frontend_proto_msgTypes[6]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use Status_Message.ProtoReflect.Descriptor instead.
 func (*Status_Message) Descriptor() ([]byte, []int) {
-	return fileDescriptor_eca3873955a29cfe, []int{0, 5}
+	return file_frontend_proto_rawDescGZIP(), []int{0, 5}
 }
 
-func (m *Status_Message) XXX_Unmarshal(b []byte) error {
-	return xxx_messageInfo_Status_Message.Unmarshal(m, b)
-}
-func (m *Status_Message) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_Status_Message.Marshal(b, m, deterministic)
-}
-func (m *Status_Message) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_Status_Message.Merge(m, src)
-}
-func (m *Status_Message) XXX_Size() int {
-	return xxx_messageInfo_Status_Message.Size(m)
-}
-func (m *Status_Message) XXX_DiscardUnknown() {
-	xxx_messageInfo_Status_Message.DiscardUnknown(m)
-}
-
-var xxx_messageInfo_Status_Message proto.InternalMessageInfo
-
-const Default_Status_Message_Level Status_Message_Level = Status_Message_INFO
-
-func (m *Status_Message) GetLevel() Status_Message_Level {
-	if m != nil && m.Level != nil {
-		return *m.Level
+func (x *Status_Message) GetLevel() Status_Message_Level {
+	if x != nil && x.Level != nil {
+		return *x.Level
 	}
 	return Default_Status_Message_Level
 }
 
-func (m *Status_Message) GetMessage() string {
-	if m != nil && m.Message != nil {
-		return *m.Message
+func (x *Status_Message) GetMessage() string {
+	if x != nil && x.Message != nil {
+		return *x.Message
 	}
 	return ""
 }
 
-func init() {
-	proto.RegisterEnum("ninja.Status_Message_Level", Status_Message_Level_name, Status_Message_Level_value)
-	proto.RegisterType((*Status)(nil), "ninja.Status")
-	proto.RegisterType((*Status_TotalEdges)(nil), "ninja.Status.TotalEdges")
-	proto.RegisterType((*Status_BuildStarted)(nil), "ninja.Status.BuildStarted")
-	proto.RegisterType((*Status_BuildFinished)(nil), "ninja.Status.BuildFinished")
-	proto.RegisterType((*Status_EdgeStarted)(nil), "ninja.Status.EdgeStarted")
-	proto.RegisterType((*Status_EdgeFinished)(nil), "ninja.Status.EdgeFinished")
-	proto.RegisterType((*Status_Message)(nil), "ninja.Status.Message")
+var File_frontend_proto protoreflect.FileDescriptor
+
+var file_frontend_proto_rawDesc = []byte{
+	0x0a, 0x0e, 0x66, 0x72, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x12, 0x05, 0x6e, 0x69, 0x6e, 0x6a, 0x61, 0x22, 0xb4, 0x0a, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74,
+	0x75, 0x73, 0x12, 0x39, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x65, 0x64, 0x67, 0x65,
+	0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x6e, 0x69, 0x6e, 0x6a, 0x61, 0x2e,
+	0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x45, 0x64, 0x67, 0x65,
+	0x73, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x45, 0x64, 0x67, 0x65, 0x73, 0x12, 0x3f, 0x0a,
+	0x0d, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x18, 0x02,
+	0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6e, 0x69, 0x6e, 0x6a, 0x61, 0x2e, 0x53, 0x74, 0x61,
+	0x74, 0x75, 0x73, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64,
+	0x52, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x12, 0x42,
+	0x0a, 0x0e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64,
+	0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6e, 0x69, 0x6e, 0x6a, 0x61, 0x2e, 0x53,
+	0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x46, 0x69, 0x6e, 0x69, 0x73,
+	0x68, 0x65, 0x64, 0x52, 0x0d, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68,
+	0x65, 0x64, 0x12, 0x3c, 0x0a, 0x0c, 0x65, 0x64, 0x67, 0x65, 0x5f, 0x73, 0x74, 0x61, 0x72, 0x74,
+	0x65, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x6e, 0x69, 0x6e, 0x6a, 0x61,
+	0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x45, 0x64, 0x67, 0x65, 0x53, 0x74, 0x61, 0x72,
+	0x74, 0x65, 0x64, 0x52, 0x0b, 0x65, 0x64, 0x67, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64,
+	0x12, 0x3f, 0x0a, 0x0d, 0x65, 0x64, 0x67, 0x65, 0x5f, 0x66, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65,
+	0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x6e, 0x69, 0x6e, 0x6a, 0x61, 0x2e,
+	0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x45, 0x64, 0x67, 0x65, 0x46, 0x69, 0x6e, 0x69, 0x73,
+	0x68, 0x65, 0x64, 0x52, 0x0c, 0x65, 0x64, 0x67, 0x65, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65,
+	0x64, 0x12, 0x2f, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x06, 0x20, 0x01,
+	0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6e, 0x69, 0x6e, 0x6a, 0x61, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75,
+	0x73, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61,
+	0x67, 0x65, 0x1a, 0x2d, 0x0a, 0x0a, 0x54, 0x6f, 0x74, 0x61, 0x6c, 0x45, 0x64, 0x67, 0x65, 0x73,
+	0x12, 0x1f, 0x0a, 0x0b, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x65, 0x64, 0x67, 0x65, 0x73, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x45, 0x64, 0x67, 0x65,
+	0x73, 0x1a, 0x4a, 0x0a, 0x0c, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x74, 0x61, 0x72, 0x74, 0x65,
+	0x64, 0x12, 0x20, 0x0a, 0x0b, 0x70, 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c, 0x69, 0x73, 0x6d,
+	0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x70, 0x61, 0x72, 0x61, 0x6c, 0x6c, 0x65, 0x6c,
+	0x69, 0x73, 0x6d, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x18, 0x02,
+	0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x76, 0x65, 0x72, 0x62, 0x6f, 0x73, 0x65, 0x1a, 0x0f, 0x0a,
+	0x0d, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x1a, 0xb6,
+	0x01, 0x0a, 0x0b, 0x45, 0x64, 0x67, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x65, 0x64, 0x12, 0x0e,
+	0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x1d,
+	0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01,
+	0x28, 0x0d, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x16, 0x0a,
+	0x06, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x69,
+	0x6e, 0x70, 0x75, 0x74, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73,
+	0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x12,
+	0x12, 0x0a, 0x04, 0x64, 0x65, 0x73, 0x63, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x64,
+	0x65, 0x73, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x06,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x18, 0x0a,
+	0x07, 0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07,
+	0x63, 0x6f, 0x6e, 0x73, 0x6f, 0x6c, 0x65, 0x1a, 0xdf, 0x03, 0x0a, 0x0c, 0x45, 0x64, 0x67, 0x65,
+	0x46, 0x69, 0x6e, 0x69, 0x73, 0x68, 0x65, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x5f,
+	0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x65, 0x6e, 0x64, 0x54,
+	0x69, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x03, 0x20,
+	0x01, 0x28, 0x11, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x6f,
+	0x75, 0x74, 0x70, 0x75, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x6f, 0x75, 0x74,
+	0x70, 0x75, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x74, 0x69, 0x6d, 0x65,
+	0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x75, 0x73, 0x65, 0x72, 0x54, 0x69, 0x6d, 0x65,
+	0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18,
+	0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x54, 0x69, 0x6d,
+	0x65, 0x12, 0x1c, 0x0a, 0x0a, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x73, 0x73, 0x5f, 0x6b, 0x62, 0x18,
+	0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x52, 0x73, 0x73, 0x4b, 0x62, 0x12,
+	0x2a, 0x0a, 0x11, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x66, 0x61,
+	0x75, 0x6c, 0x74, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x6d, 0x69, 0x6e, 0x6f,
+	0x72, 0x50, 0x61, 0x67, 0x65, 0x46, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6d,
+	0x61, 0x6a, 0x6f, 0x72, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73,
+	0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x50, 0x61, 0x67,
+	0x65, 0x46, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x1e, 0x0a, 0x0b, 0x69, 0x6f, 0x5f, 0x69, 0x6e,
+	0x70, 0x75, 0x74, 0x5f, 0x6b, 0x62, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x69, 0x6f,
+	0x49, 0x6e, 0x70, 0x75, 0x74, 0x4b, 0x62, 0x12, 0x20, 0x0a, 0x0c, 0x69, 0x6f, 0x5f, 0x6f, 0x75,
+	0x74, 0x70, 0x75, 0x74, 0x5f, 0x6b, 0x62, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x69,
+	0x6f, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x4b, 0x62, 0x12, 0x3c, 0x0a, 0x1a, 0x76, 0x6f, 0x6c,
+	0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x73,
+	0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x04, 0x52, 0x18, 0x76,
+	0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x53,
+	0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x12, 0x40, 0x0a, 0x1c, 0x69, 0x6e, 0x76, 0x6f, 0x6c,
+	0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x73,
+	0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x04, 0x52, 0x1a, 0x69,
+	0x6e, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78,
+	0x74, 0x53, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x1a, 0x92, 0x01, 0x0a, 0x07, 0x4d, 0x65,
+	0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x37, 0x0a, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x0e, 0x32, 0x1b, 0x2e, 0x6e, 0x69, 0x6e, 0x6a, 0x61, 0x2e, 0x53, 0x74, 0x61,
+	0x74, 0x75, 0x73, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x4c, 0x65, 0x76, 0x65,
+	0x6c, 0x3a, 0x04, 0x49, 0x4e, 0x46, 0x4f, 0x52, 0x05, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x12, 0x18,
+	0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0x34, 0x0a, 0x05, 0x4c, 0x65, 0x76, 0x65,
+	0x6c, 0x12, 0x08, 0x0a, 0x04, 0x49, 0x4e, 0x46, 0x4f, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x57,
+	0x41, 0x52, 0x4e, 0x49, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f,
+	0x52, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x03, 0x42, 0x2a,
+	0x48, 0x03, 0x5a, 0x26, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e,
+	0x67, 0x2f, 0x75, 0x69, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2f, 0x6e, 0x69, 0x6e, 0x6a,
+	0x61, 0x5f, 0x66, 0x72, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x64,
 }
 
-func init() {
-	proto.RegisterFile("frontend.proto", fileDescriptor_eca3873955a29cfe)
+var (
+	file_frontend_proto_rawDescOnce sync.Once
+	file_frontend_proto_rawDescData = file_frontend_proto_rawDesc
+)
+
+func file_frontend_proto_rawDescGZIP() []byte {
+	file_frontend_proto_rawDescOnce.Do(func() {
+		file_frontend_proto_rawDescData = protoimpl.X.CompressGZIP(file_frontend_proto_rawDescData)
+	})
+	return file_frontend_proto_rawDescData
 }
 
-var fileDescriptor_eca3873955a29cfe = []byte{
-	// 678 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x94, 0xff, 0x4e, 0xd4, 0x40,
-	0x10, 0xc7, 0xbd, 0xdf, 0xd7, 0xe9, 0xdd, 0x71, 0x6c, 0xa2, 0x29, 0x05, 0xe5, 0xc2, 0x5f, 0xc4,
-	0xc4, 0x33, 0x31, 0x26, 0x46, 0x43, 0xa2, 0x9e, 0x02, 0x22, 0x0a, 0x66, 0xc1, 0x98, 0xf8, 0x4f,
-	0xb3, 0xbd, 0x2e, 0xb0, 0xd8, 0x76, 0x2f, 0xdd, 0x2d, 0xc2, 0x6b, 0xf8, 0x2c, 0xc6, 0xd7, 0xf1,
-	0x55, 0xcc, 0xce, 0xb6, 0x47, 0x0f, 0x88, 0xff, 0x75, 0x66, 0x3e, 0xf3, 0x9d, 0xd9, 0x99, 0xed,
-	0xc2, 0xe0, 0x24, 0x93, 0xa9, 0xe6, 0x69, 0x34, 0x9e, 0x65, 0x52, 0x4b, 0xd2, 0x4a, 0x45, 0x7a,
-	0xce, 0x36, 0x7e, 0x03, 0xb4, 0x8f, 0x34, 0xd3, 0xb9, 0x22, 0x2f, 0xc1, 0xd5, 0x52, 0xb3, 0x38,
-	0xe0, 0xd1, 0x29, 0x57, 0x5e, 0x6d, 0x54, 0xdb, 0x74, 0x9f, 0x79, 0x63, 0xe4, 0xc6, 0x96, 0x19,
-	0x1f, 0x1b, 0x60, 0xdb, 0xc4, 0x29, 0xe8, 0xf9, 0x37, 0x79, 0x0d, 0xfd, 0x30, 0x17, 0x71, 0x14,
-	0x28, 0xcd, 0x32, 0xcd, 0x23, 0xaf, 0x8e, 0xc9, 0xfe, 0x62, 0xf2, 0xc4, 0x20, 0x47, 0x96, 0xa0,
-	0xbd, 0xb0, 0x62, 0x91, 0x09, 0x0c, 0xac, 0xc0, 0x89, 0x48, 0x85, 0x3a, 0xe3, 0x91, 0xd7, 0x40,
-	0x85, 0xd5, 0x3b, 0x14, 0x76, 0x0a, 0x84, 0xda, 0x9a, 0xa5, 0x49, 0xb6, 0xa0, 0x67, 0x3a, 0x9f,
-	0xf7, 0xd0, 0x44, 0x85, 0x95, 0x45, 0x05, 0xd3, 0x6f, 0xd9, 0x82, 0xcb, 0xaf, 0x0d, 0x73, 0x04,
-	0xcc, 0x9e, 0x37, 0xd0, 0xba, 0xeb, 0x08, 0x26, 0x7d, 0x5e, 0x1f, 0xcb, 0xcd, 0xcb, 0x3f, 0x85,
-	0x4e, 0xc2, 0x95, 0x62, 0xa7, 0xdc, 0x6b, 0x63, 0xea, 0xfd, 0xc5, 0xd4, 0xcf, 0x36, 0x48, 0x4b,
-	0xca, 0x7f, 0x02, 0x70, 0x3d, 0x4e, 0xb2, 0x7e, 0x7b, 0xfa, 0xfd, 0xea, 0x8c, 0xfd, 0x8f, 0xd0,
-	0xab, 0x0e, 0x90, 0x8c, 0xc0, 0x9d, 0xb1, 0x8c, 0xc5, 0x31, 0x8f, 0x85, 0x4a, 0x8a, 0x84, 0xaa,
-	0x8b, 0x78, 0xd0, 0xb9, 0xe0, 0x59, 0x28, 0x15, 0xc7, 0x7d, 0x74, 0x69, 0x69, 0xfa, 0x4b, 0xd0,
-	0x5f, 0x18, 0xa5, 0xff, 0xa7, 0x06, 0x6e, 0x65, 0x34, 0x64, 0x00, 0x75, 0x11, 0x15, 0x9a, 0x75,
-	0x11, 0x91, 0x87, 0x00, 0x38, 0xd6, 0x40, 0x8b, 0xc4, 0xaa, 0xf5, 0xa9, 0x83, 0x9e, 0x63, 0x91,
-	0x70, 0xf2, 0x00, 0xda, 0x22, 0x9d, 0xe5, 0x5a, 0x79, 0x8d, 0x51, 0x63, 0xd3, 0xa1, 0x85, 0x65,
-	0x3a, 0x90, 0xb9, 0xc6, 0x40, 0x13, 0x03, 0xa5, 0x49, 0x08, 0x34, 0x23, 0xae, 0xa6, 0x38, 0x65,
-	0x87, 0xe2, 0xb7, 0xa1, 0xa7, 0x32, 0x49, 0x58, 0x1a, 0xe1, 0x04, 0x1d, 0x5a, 0x9a, 0x36, 0x92,
-	0x2a, 0x19, 0x73, 0xaf, 0x63, 0x4f, 0x52, 0x98, 0xfe, 0xdf, 0x06, 0xf4, 0xaa, 0x4b, 0xb9, 0xd5,
-	0xf9, 0x0a, 0x74, 0x79, 0x1a, 0x55, 0xfb, 0xee, 0xf0, 0x34, 0x2a, 0xbb, 0x56, 0xb8, 0x1b, 0xbc,
-	0x6c, 0xcb, 0xb4, 0xb0, 0x8c, 0xdf, 0xb6, 0x89, 0x57, 0xc8, 0xa1, 0x85, 0x45, 0x56, 0xc1, 0xc9,
-	0x15, 0xcf, 0xac, 0x56, 0x0b, 0xb5, 0xba, 0xc6, 0x81, 0x62, 0xeb, 0xe0, 0xaa, 0x2b, 0xa5, 0x79,
-	0x62, 0xc3, 0x6d, 0xbb, 0x3f, 0xeb, 0x42, 0x60, 0x0d, 0x20, 0x61, 0x97, 0x41, 0xa6, 0x54, 0xf0,
-	0x23, 0xc4, 0x63, 0x34, 0x69, 0x37, 0x61, 0x97, 0x54, 0xa9, 0xfd, 0x90, 0x3c, 0x86, 0xe5, 0x44,
-	0xa4, 0x32, 0x0b, 0x66, 0xcc, 0x5c, 0x42, 0x96, 0xc7, 0x5a, 0x79, 0x5d, 0x84, 0x96, 0x30, 0xf0,
-	0x85, 0x9d, 0xf2, 0x1d, 0x74, 0x23, 0xcb, 0xce, 0x6f, 0xb0, 0x4e, 0xc1, 0x9a, 0x40, 0x85, 0x7d,
-	0x04, 0xae, 0x90, 0x01, 0xae, 0xc3, 0x94, 0x05, 0xa4, 0x1c, 0x21, 0xf7, 0x8c, 0x67, 0x3f, 0x24,
-	0x23, 0xe8, 0x09, 0x19, 0xd8, 0x03, 0x1a, 0xc0, 0x45, 0x00, 0x84, 0x3c, 0x44, 0xd7, 0x7e, 0x48,
-	0xb6, 0xc0, 0xbf, 0x90, 0x71, 0x9e, 0x6a, 0x96, 0x5d, 0x05, 0x53, 0xf3, 0x86, 0x5c, 0xea, 0x40,
-	0xfd, 0x14, 0x7a, 0x7a, 0xc6, 0x95, 0xd7, 0x43, 0xde, 0x9b, 0x13, 0xef, 0x2c, 0x70, 0x54, 0xc4,
-	0xc9, 0x1b, 0x58, 0x13, 0xe9, 0x7f, 0xf2, 0xfb, 0x98, 0xef, 0x57, 0x98, 0x1b, 0x0a, 0xfe, 0xaf,
-	0x1a, 0x74, 0x8a, 0x7f, 0x87, 0xbc, 0x80, 0x56, 0xcc, 0x2f, 0x78, 0x8c, 0xfb, 0x1d, 0xdc, 0x7c,
-	0x1d, 0x0a, 0x6a, 0xfc, 0xc9, 0x20, 0xaf, 0x9a, 0x7b, 0x07, 0x3b, 0x87, 0xd4, 0xf2, 0xe6, 0x02,
-	0x95, 0x3f, 0x67, 0xdd, 0x5e, 0xad, 0xc2, 0xdc, 0x78, 0x0e, 0x2d, 0xe4, 0x49, 0x17, 0x30, 0x63,
-	0x78, 0x8f, 0xb8, 0xd0, 0xf9, 0xf6, 0x96, 0x1e, 0xec, 0x1d, 0xec, 0x0e, 0x6b, 0xc4, 0x81, 0xd6,
-	0x36, 0xa5, 0x87, 0x74, 0x58, 0x37, 0x9f, 0xef, 0xb7, 0x27, 0x5f, 0x77, 0x87, 0x8d, 0x09, 0xf9,
-	0xd0, 0xf8, 0x3e, 0xc0, 0xe2, 0x41, 0xf9, 0xae, 0xfe, 0x0b, 0x00, 0x00, 0xff, 0xff, 0x2f, 0x7a,
-	0x33, 0x13, 0x62, 0x05, 0x00, 0x00,
+var file_frontend_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
+var file_frontend_proto_msgTypes = make([]protoimpl.MessageInfo, 7)
+var file_frontend_proto_goTypes = []interface{}{
+	(Status_Message_Level)(0),    // 0: ninja.Status.Message.Level
+	(*Status)(nil),               // 1: ninja.Status
+	(*Status_TotalEdges)(nil),    // 2: ninja.Status.TotalEdges
+	(*Status_BuildStarted)(nil),  // 3: ninja.Status.BuildStarted
+	(*Status_BuildFinished)(nil), // 4: ninja.Status.BuildFinished
+	(*Status_EdgeStarted)(nil),   // 5: ninja.Status.EdgeStarted
+	(*Status_EdgeFinished)(nil),  // 6: ninja.Status.EdgeFinished
+	(*Status_Message)(nil),       // 7: ninja.Status.Message
+}
+var file_frontend_proto_depIdxs = []int32{
+	2, // 0: ninja.Status.total_edges:type_name -> ninja.Status.TotalEdges
+	3, // 1: ninja.Status.build_started:type_name -> ninja.Status.BuildStarted
+	4, // 2: ninja.Status.build_finished:type_name -> ninja.Status.BuildFinished
+	5, // 3: ninja.Status.edge_started:type_name -> ninja.Status.EdgeStarted
+	6, // 4: ninja.Status.edge_finished:type_name -> ninja.Status.EdgeFinished
+	7, // 5: ninja.Status.message:type_name -> ninja.Status.Message
+	0, // 6: ninja.Status.Message.level:type_name -> ninja.Status.Message.Level
+	7, // [7:7] is the sub-list for method output_type
+	7, // [7:7] is the sub-list for method input_type
+	7, // [7:7] is the sub-list for extension type_name
+	7, // [7:7] is the sub-list for extension extendee
+	0, // [0:7] is the sub-list for field type_name
+}
+
+func init() { file_frontend_proto_init() }
+func file_frontend_proto_init() {
+	if File_frontend_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_frontend_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Status); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_frontend_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Status_TotalEdges); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_frontend_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Status_BuildStarted); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_frontend_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Status_BuildFinished); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_frontend_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Status_EdgeStarted); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_frontend_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Status_EdgeFinished); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_frontend_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*Status_Message); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_frontend_proto_rawDesc,
+			NumEnums:      1,
+			NumMessages:   7,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_frontend_proto_goTypes,
+		DependencyIndexes: file_frontend_proto_depIdxs,
+		EnumInfos:         file_frontend_proto_enumTypes,
+		MessageInfos:      file_frontend_proto_msgTypes,
+	}.Build()
+	File_frontend_proto = out.File
+	file_frontend_proto_rawDesc = nil
+	file_frontend_proto_goTypes = nil
+	file_frontend_proto_depIdxs = nil
 }
diff --git a/ui/status/ninja_frontend/frontend.proto b/ui/status/ninja_frontend/frontend.proto
index e5e5d9f..5730388 100644
--- a/ui/status/ninja_frontend/frontend.proto
+++ b/ui/status/ninja_frontend/frontend.proto
@@ -17,7 +17,7 @@
 option optimize_for = LITE_RUNTIME;
 
 package ninja;
-option go_package = "ninja_frontend";
+option go_package = "android/soong/ui/status/ninja_frontend";
 
 message Status {
   message TotalEdges {
diff --git a/ui/status/ninja_test.go b/ui/status/ninja_test.go
index c400c97..f3638b3 100644
--- a/ui/status/ninja_test.go
+++ b/ui/status/ninja_test.go
@@ -43,3 +43,53 @@
 		t.Errorf("nr.Close timed out, %s > %s", g, w)
 	}
 }
+
+// Test that error hint is added to output if available
+func TestNinjaReader_CorrectErrorHint(t *testing.T) {
+	errorPattern1 := "pattern-1 in input"
+	errorHint1 := "\n Fix by doing task 1"
+	errorPattern2 := "pattern-2 in input"
+	errorHint2 := "\n Fix by doing task 2"
+	mockErrorHints := make(map[string]string)
+	mockErrorHints[errorPattern1] = errorHint1
+	mockErrorHints[errorPattern2] = errorHint2
+
+	errorHintGenerator := *newErrorHintGenerator(mockErrorHints)
+	testCases := []struct {
+		rawOutput            string
+		buildExitCode        int
+		expectedFinalOutput  string
+		testCaseErrorMessage string
+	}{
+		{
+			rawOutput:            "ninja build was successful",
+			buildExitCode:        0,
+			expectedFinalOutput:  "ninja build was successful",
+			testCaseErrorMessage: "raw output changed when build was successful",
+		},
+		{
+			rawOutput:            "ninja build failed",
+			buildExitCode:        1,
+			expectedFinalOutput:  "ninja build failed",
+			testCaseErrorMessage: "raw output changed even when no error hint pattern was found",
+		},
+		{
+			rawOutput:            "ninja build failed: " + errorPattern1 + "some footnotes",
+			buildExitCode:        1,
+			expectedFinalOutput:  "ninja build failed: " + errorPattern1 + "some footnotes" + errorHint1,
+			testCaseErrorMessage: "error hint not added despite pattern match",
+		},
+		{
+			rawOutput:            "ninja build failed: " + errorPattern2 + errorPattern1,
+			buildExitCode:        1,
+			expectedFinalOutput:  "ninja build failed: " + errorPattern2 + errorPattern1 + errorHint2,
+			testCaseErrorMessage: "error hint should be added for first pattern match in raw output",
+		},
+	}
+	for _, testCase := range testCases {
+		actualFinalOutput := errorHintGenerator.GetOutputWithErrorHint(testCase.rawOutput, testCase.buildExitCode)
+		if actualFinalOutput != testCase.expectedFinalOutput {
+			t.Errorf(testCase.testCaseErrorMessage+"\nexpected: %s\ngot: %s", testCase.expectedFinalOutput, actualFinalOutput)
+		}
+	}
+}