Merge "Make PrebuiltStubsSources support srcjar properly"
diff --git a/Android.bp b/Android.bp
index b5ddaa4..0e8d86d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -127,3 +127,7 @@
     srcs: ["cc/config/global.go"],
     out: ["clang-prebuilts-version.txt"],
 }
+
+dexpreopt_systemserver_check {
+    name: "dexpreopt_systemserver_check",
+}
diff --git a/README.md b/README.md
index a67c393..b820fd1 100644
--- a/README.md
+++ b/README.md
@@ -561,27 +561,41 @@
 
 ## Developing for Soong
 
-To load Soong code in a Go-aware IDE, create a directory outside your android tree and then:
-```bash
-apt install bindfs
-export GOPATH=<path to the directory you created>
-build/soong/scripts/setup_go_workspace_for_soong.sh
-```
+To load the code of Soong in IntelliJ:
 
-This will bind mount the Soong source directories into the directory in the layout expected by
-the IDE.
-
+* File -> Open, open the `build/soong` directory. It will be opened as a new
+  project.
+* File -> Settings, then Languages & Frameworks -> Go -> GOROOT, then set it to
+  `prebuilts/go/linux-x86`
+* File -> Project Structure, then, Project Settings -> Modules, then Add
+  Content Root, then add the `build/blueprint` directory.
+* Optional: also add the `external/golang-protobuf` directory. In practice,
+  IntelliJ seems to work well enough without this, too.
 ### Running Soong in a debugger
 
-To run the soong_build process in a debugger, install `dlv` and then start the build with
-`SOONG_DELVE=<listen addr>` in the environment.
+To make `soong_build` wait for a debugger connection, install `dlv` and then
+start the build with `SOONG_DELVE=<listen addr>` in the environment.
 For example:
 ```bash
-SOONG_DELVE=:1234 m nothing
+SOONG_DELVE=:5006 m nothing
 ```
-and then in another terminal:
+
+To make `soong_ui` wait for a debugger connection, use the `SOONG_UI_DELVE`
+variable:
+
 ```
-dlv connect :1234
+SOONG_UI_DELVE=:5006 m nothing
+```
+
+
+setting or unsetting `SOONG_DELVE` causes a recompilation of `soong_build`. This
+is because in order to debug the binary, it needs to be built with debug
+symbols.
+
+To test the debugger connection, run this command:
+
+```
+dlv connect :5006
 ```
 
 If you see an error:
@@ -596,6 +610,21 @@
 sudo sysctl -w kernel.yama.ptrace_scope=0
 ```
 
+To connect to the process using IntelliJ:
+
+* Run -> Edit Configurations...
+* Choose "Go Remote" on the left
+* Click on the "+" buttion on the top-left
+* Give it a nice name and set "Host" to localhost and "Port" to the port in the
+  environment variable
+
+Debugging works far worse than debugging Java, but is sometimes useful.
+
+Sometimes the `dlv` process hangs on connection. A symptom of this is `dlv`
+spinning a core or two. In that case, `kill -9` `dlv` and try again.
+Anecdotally, it _feels_ like waiting a minute after the start of `soong_build`
+helps.
+
 ## Contact
 
 Email android-building@googlegroups.com (external) for any questions, or see
diff --git a/android/androidmk.go b/android/androidmk.go
index f48c06b..b6b04a6 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -478,8 +478,9 @@
 
 func (a *AndroidMkEntries) fillInEntries(ctx fillInEntriesContext, mod blueprint.Module) {
 	a.EntryMap = make(map[string][]string)
-	amod := mod.(Module).base()
-	name := amod.BaseModuleName()
+	amod := mod.(Module)
+	base := amod.base()
+	name := base.BaseModuleName()
 	if a.OverrideName != "" {
 		name = a.OverrideName
 	}
@@ -487,9 +488,9 @@
 	if a.Include == "" {
 		a.Include = "$(BUILD_PREBUILT)"
 	}
-	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()...)
+	a.Required = append(a.Required, amod.RequiredModuleNames()...)
+	a.Host_required = append(a.Host_required, amod.HostRequiredModuleNames()...)
+	a.Target_required = append(a.Target_required, amod.TargetRequiredModuleNames()...)
 
 	for _, distString := range a.GetDistForGoals(mod) {
 		fmt.Fprintf(&a.header, distString)
@@ -500,14 +501,14 @@
 	// Collect make variable assignment entries.
 	a.SetString("LOCAL_PATH", ctx.ModuleDir(mod))
 	a.SetString("LOCAL_MODULE", name+a.SubName)
-	a.AddStrings("LOCAL_LICENSE_KINDS", amod.commonProperties.Effective_license_kinds...)
-	a.AddStrings("LOCAL_LICENSE_CONDITIONS", amod.commonProperties.Effective_license_conditions...)
-	a.AddStrings("LOCAL_NOTICE_FILE", amod.commonProperties.Effective_license_text.Strings()...)
+	a.AddStrings("LOCAL_LICENSE_KINDS", base.commonProperties.Effective_license_kinds...)
+	a.AddStrings("LOCAL_LICENSE_CONDITIONS", base.commonProperties.Effective_license_conditions...)
+	a.AddStrings("LOCAL_NOTICE_FILE", base.commonProperties.Effective_license_text.Strings()...)
 	// TODO(b/151177513): Does this code need to set LOCAL_MODULE_IS_CONTAINER ?
-	if amod.commonProperties.Effective_package_name != nil {
-		a.SetString("LOCAL_LICENSE_PACKAGE_NAME", *amod.commonProperties.Effective_package_name)
-	} else if len(amod.commonProperties.Effective_licenses) > 0 {
-		a.SetString("LOCAL_LICENSE_PACKAGE_NAME", strings.Join(amod.commonProperties.Effective_licenses, " "))
+	if base.commonProperties.Effective_package_name != nil {
+		a.SetString("LOCAL_LICENSE_PACKAGE_NAME", *base.commonProperties.Effective_package_name)
+	} else if len(base.commonProperties.Effective_licenses) > 0 {
+		a.SetString("LOCAL_LICENSE_PACKAGE_NAME", strings.Join(base.commonProperties.Effective_licenses, " "))
 	}
 	a.SetString("LOCAL_MODULE_CLASS", a.Class)
 	a.SetString("LOCAL_PREBUILT_MODULE_FILE", a.OutputFile.String())
@@ -515,31 +516,41 @@
 	a.AddStrings("LOCAL_HOST_REQUIRED_MODULES", a.Host_required...)
 	a.AddStrings("LOCAL_TARGET_REQUIRED_MODULES", a.Target_required...)
 
+	// If the install rule was generated by Soong tell Make about it.
+	if amod.InstallBypassMake() && len(base.katiInstalls) > 0 {
+		// Assume the primary install file is last since it probably needs to depend on any other
+		// installed files.  If that is not the case we can add a method to specify the primary
+		// installed file.
+		a.SetPath("LOCAL_SOONG_INSTALLED_MODULE", base.katiInstalls[len(base.katiInstalls)-1].to)
+		a.SetString("LOCAL_SOONG_INSTALL_PAIRS", base.katiInstalls.BuiltInstalled())
+		a.SetPaths("LOCAL_SOONG_INSTALL_SYMLINKS", base.katiSymlinks.InstallPaths().Paths())
+	}
+
 	if am, ok := mod.(ApexModule); ok {
 		a.SetBoolIfTrue("LOCAL_NOT_AVAILABLE_FOR_PLATFORM", am.NotAvailableForPlatform())
 	}
 
-	archStr := amod.Arch().ArchType.String()
+	archStr := base.Arch().ArchType.String()
 	host := false
-	switch amod.Os().Class {
+	switch base.Os().Class {
 	case Host:
-		if amod.Target().HostCross {
+		if base.Target().HostCross {
 			// Make cannot identify LOCAL_MODULE_HOST_CROSS_ARCH:= common.
-			if amod.Arch().ArchType != Common {
+			if base.Arch().ArchType != Common {
 				a.SetString("LOCAL_MODULE_HOST_CROSS_ARCH", archStr)
 			}
 		} else {
 			// Make cannot identify LOCAL_MODULE_HOST_ARCH:= common.
-			if amod.Arch().ArchType != Common {
+			if base.Arch().ArchType != Common {
 				a.SetString("LOCAL_MODULE_HOST_ARCH", archStr)
 			}
 		}
 		host = true
 	case Device:
 		// Make cannot identify LOCAL_MODULE_TARGET_ARCH:= common.
-		if amod.Arch().ArchType != Common {
-			if amod.Target().NativeBridge {
-				hostArchStr := amod.Target().NativeBridgeHostArchName
+		if base.Arch().ArchType != Common {
+			if base.Target().NativeBridge {
+				hostArchStr := base.Target().NativeBridgeHostArchName
 				if hostArchStr != "" {
 					a.SetString("LOCAL_MODULE_TARGET_ARCH", hostArchStr)
 				}
@@ -548,31 +559,31 @@
 			}
 		}
 
-		if !amod.InRamdisk() && !amod.InVendorRamdisk() {
-			a.AddPaths("LOCAL_FULL_INIT_RC", amod.initRcPaths)
+		if !base.InRamdisk() && !base.InVendorRamdisk() {
+			a.AddPaths("LOCAL_FULL_INIT_RC", base.initRcPaths)
 		}
-		if len(amod.vintfFragmentsPaths) > 0 {
-			a.AddPaths("LOCAL_FULL_VINTF_FRAGMENTS", amod.vintfFragmentsPaths)
+		if len(base.vintfFragmentsPaths) > 0 {
+			a.AddPaths("LOCAL_FULL_VINTF_FRAGMENTS", base.vintfFragmentsPaths)
 		}
-		a.SetBoolIfTrue("LOCAL_PROPRIETARY_MODULE", Bool(amod.commonProperties.Proprietary))
-		if Bool(amod.commonProperties.Vendor) || Bool(amod.commonProperties.Soc_specific) {
+		a.SetBoolIfTrue("LOCAL_PROPRIETARY_MODULE", Bool(base.commonProperties.Proprietary))
+		if Bool(base.commonProperties.Vendor) || Bool(base.commonProperties.Soc_specific) {
 			a.SetString("LOCAL_VENDOR_MODULE", "true")
 		}
-		a.SetBoolIfTrue("LOCAL_ODM_MODULE", Bool(amod.commonProperties.Device_specific))
-		a.SetBoolIfTrue("LOCAL_PRODUCT_MODULE", Bool(amod.commonProperties.Product_specific))
-		a.SetBoolIfTrue("LOCAL_SYSTEM_EXT_MODULE", Bool(amod.commonProperties.System_ext_specific))
-		if amod.commonProperties.Owner != nil {
-			a.SetString("LOCAL_MODULE_OWNER", *amod.commonProperties.Owner)
+		a.SetBoolIfTrue("LOCAL_ODM_MODULE", Bool(base.commonProperties.Device_specific))
+		a.SetBoolIfTrue("LOCAL_PRODUCT_MODULE", Bool(base.commonProperties.Product_specific))
+		a.SetBoolIfTrue("LOCAL_SYSTEM_EXT_MODULE", Bool(base.commonProperties.System_ext_specific))
+		if base.commonProperties.Owner != nil {
+			a.SetString("LOCAL_MODULE_OWNER", *base.commonProperties.Owner)
 		}
 	}
 
-	if len(amod.noticeFiles) > 0 {
-		a.SetString("LOCAL_NOTICE_FILE", strings.Join(amod.noticeFiles.Strings(), " "))
+	if len(base.noticeFiles) > 0 {
+		a.SetString("LOCAL_NOTICE_FILE", strings.Join(base.noticeFiles.Strings(), " "))
 	}
 
 	if host {
-		makeOs := amod.Os().String()
-		if amod.Os() == Linux || amod.Os() == LinuxBionic || amod.Os() == LinuxMusl {
+		makeOs := base.Os().String()
+		if base.Os() == Linux || base.Os() == LinuxBionic || base.Os() == LinuxMusl {
 			makeOs = "linux"
 		}
 		a.SetString("LOCAL_MODULE_HOST_OS", makeOs)
@@ -580,10 +591,10 @@
 	}
 
 	prefix := ""
-	if amod.ArchSpecific() {
-		switch amod.Os().Class {
+	if base.ArchSpecific() {
+		switch base.Os().Class {
 		case Host:
-			if amod.Target().HostCross {
+			if base.Target().HostCross {
 				prefix = "HOST_CROSS_"
 			} else {
 				prefix = "HOST_"
@@ -593,7 +604,7 @@
 
 		}
 
-		if amod.Arch().ArchType != ctx.Config().Targets[amod.Os()][0].Arch.ArchType {
+		if base.Arch().ArchType != ctx.Config().Targets[base.Os()][0].Arch.ArchType {
 			prefix = "2ND_" + prefix
 		}
 	}
@@ -910,6 +921,11 @@
 		return true
 	}
 
+	// Only expose the primary Darwin target, as Make does not understand Darwin+Arm64
+	if module.Os() == Darwin && module.Target().HostCross {
+		return true
+	}
+
 	return !module.Enabled() ||
 		module.commonProperties.HideFromMake ||
 		// Make does not understand LinuxBionic
diff --git a/android/api_levels.go b/android/api_levels.go
index 93583bc..c1b3ba2 100644
--- a/android/api_levels.go
+++ b/android/api_levels.go
@@ -177,6 +177,10 @@
 // libandroid_support.
 var FirstNonLibAndroidSupportVersion = uncheckedFinalApiLevel(21)
 
+// LastWithoutModuleLibCoreSystemModules is the last API level where prebuilts/sdk does not contain
+// a core-for-system-modules.jar for the module-lib API scope.
+var LastWithoutModuleLibCoreSystemModules = uncheckedFinalApiLevel(31)
+
 // If the `raw` input is the codename of an API level has been finalized, this
 // function returns the API level number associated with that API level. If the
 // input is *not* a finalized codename, the input is returned unmodified.
@@ -236,6 +240,27 @@
 	return apiLevel, nil
 }
 
+// ApiLevelForTest returns an ApiLevel constructed from the supplied raw string.
+//
+// This only supports "current" and numeric levels, code names are not supported.
+func ApiLevelForTest(raw string) ApiLevel {
+	if raw == "" {
+		panic("API level string must be non-empty")
+	}
+
+	if raw == "current" {
+		return FutureApiLevel
+	}
+
+	asInt, err := strconv.Atoi(raw)
+	if err != nil {
+		panic(fmt.Errorf("%q could not be parsed as an integer and is not a recognized codename", raw))
+	}
+
+	apiLevel := uncheckedFinalApiLevel(asInt)
+	return apiLevel
+}
+
 // Converts an API level string `raw` into an ApiLevel in the same method as
 // `ApiLevelFromUser`, but the input is assumed to have no errors and any errors
 // will panic instead of returning an error.
diff --git a/android/arch.go b/android/arch.go
index 5e3e920..3bf54b7 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -308,7 +308,7 @@
 	// 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)
+	Darwin = newOsType("darwin", Host, false, Arm64, X86_64)
 	// LinuxBionic is the OS for the Linux kernel plus the Bionic libc runtime, but without the
 	// rest of Android.
 	LinuxBionic = newOsType("linux_bionic", Host, false, Arm64, X86_64)
@@ -696,6 +696,11 @@
 	for i, m := range modules {
 		addTargetProperties(m, targets[i], multiTargets, i == 0)
 		m.base().setArchProperties(mctx)
+
+		// Install support doesn't understand Darwin+Arm64
+		if os == Darwin && targets[i].HostCross {
+			m.base().commonProperties.SkipInstall = true
+		}
 	}
 }
 
diff --git a/android/bazel.go b/android/bazel.go
index 91ca969..cf27cb4 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -60,8 +60,8 @@
 	HasHandcraftedLabel() bool
 	HandcraftedLabel() string
 	GetBazelLabel(ctx BazelConversionPathContext, module blueprint.Module) string
-	ConvertWithBp2build(ctx BazelConversionPathContext) bool
-	convertWithBp2build(ctx BazelConversionPathContext, module blueprint.Module) bool
+	ConvertWithBp2build(ctx BazelConversionContext) bool
+	convertWithBp2build(ctx BazelConversionContext, module blueprint.Module) bool
 	GetBazelBuildFileContents(c Config, path, name string) (string, error)
 }
 
@@ -249,10 +249,14 @@
 		"build_tools_source_properties",
 
 		// //external/libcap/...
-		"libcap",      // http://b/198595332, depends on _makenames, a cc_binary
-		"cap_names.h", // http://b/198596102, depends on _makenames, a cc_binary
+		"cap_names.h", // http://b/196105070 host toolchain misconfigurations for mixed builds
+		"libcap",      // http://b/196105070 host toolchain misconfigurations for mixed builds
 
 		"libminijail", // depends on unconverted modules: libcap
+		"getcap",      // depends on unconverted modules: libcap
+		"setcap",      // depends on unconverted modules: libcap
+		"minijail0",   // depends on unconverted modules: libcap, libminijail
+		"drop_privs",  // depends on unconverted modules: libminijail
 
 		// Tests. Handle later.
 		"libbionic_tests_headers_posix", // http://b/186024507, cc_library_static, sched.h, time.h not found
@@ -279,6 +283,20 @@
 
 		"libgtest_ndk_c++",      // b/201816222: Requires sdk_version support.
 		"libgtest_main_ndk_c++", // b/201816222: Requires sdk_version support.
+
+		"abb",                     // depends on unconverted modules: libadbd_core, libadbd_services, libcmd, libbinder, libutils, libselinux
+		"adb",                     // depends on unconverted modules: bin2c_fastdeployagent, libadb_crypto, libadb_host, libadb_pairing_connection, libadb_protos, libandroidfw, libapp_processes_protos_full, libfastdeploy_host, libmdnssd, libopenscreen-discovery, libopenscreen-platform-impl, libusb, libutils, libziparchive, libzstd, AdbWinApi
+		"adbd",                    // depends on unconverted modules: libadb_crypto, libadb_pairing_connection, libadb_protos, libadbd, libadbd_core, libapp_processes_protos_lite, libmdnssd, libzstd, libadbd_services, libcap, libminijail, libselinux
+		"bionic_tests_zipalign",   // depends on unconverted modules: libziparchive, libutils
+		"linker",                  // depends on unconverted modules: liblinker_debuggerd_stub, libdebuggerd_handler_fallback, libziparchive, liblinker_main, liblinker_malloc
+		"linker_reloc_bench_main", // depends on unconverted modules: liblinker_reloc_bench_*
+		"sefcontext_compile",      // depends on unconverted modules: libsepol
+		"versioner",               // depends on unconverted modules: libclang_cxx_host, libLLVM_host
+
+		"linkerconfig", // http://b/202876379 has arch-variant static_executable
+		"mdnsd",        // http://b/202876379 has arch-variant static_executable
+
+		"acvp_modulewrapper", // disabled for android x86/x86_64
 	}
 
 	// Per-module denylist of cc_library modules to only generate the static
@@ -359,7 +377,7 @@
 }
 
 // ConvertedToBazel returns whether this module has been converted (with bp2build or manually) to Bazel.
-func convertedToBazel(ctx BazelConversionPathContext, module blueprint.Module) bool {
+func convertedToBazel(ctx BazelConversionContext, module blueprint.Module) bool {
 	b, ok := module.(Bazelable)
 	if !ok {
 		return false
@@ -368,11 +386,11 @@
 }
 
 // ConvertWithBp2build returns whether the given BazelModuleBase should be converted with bp2build.
-func (b *BazelModuleBase) ConvertWithBp2build(ctx BazelConversionPathContext) bool {
+func (b *BazelModuleBase) ConvertWithBp2build(ctx BazelConversionContext) bool {
 	return b.convertWithBp2build(ctx, ctx.Module())
 }
 
-func (b *BazelModuleBase) convertWithBp2build(ctx BazelConversionPathContext, module blueprint.Module) bool {
+func (b *BazelModuleBase) convertWithBp2build(ctx BazelConversionContext, module blueprint.Module) bool {
 	if bp2buildModuleDoNotConvert[module.Name()] {
 		return false
 	}
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index 06712a1..3c6212e 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -48,11 +48,17 @@
 	StarlarkFunctionBody() string
 }
 
+// Portion of cquery map key to describe target configuration.
+type configKey struct {
+	archType ArchType
+	osType   OsType
+}
+
 // Map key to describe bazel cquery requests.
 type cqueryKey struct {
 	label       string
 	requestType cqueryRequest
-	archType    ArchType
+	configKey   configKey
 }
 
 // bazelHandler is the interface for a helper object related to deferring to Bazel for
@@ -72,14 +78,14 @@
 	// has been queued to be run later.
 
 	// Returns result files built by building the given bazel target label.
-	GetOutputFiles(label string, archType ArchType) ([]string, bool)
+	GetOutputFiles(label string, cfgKey configKey) ([]string, bool)
 
 	// TODO(cparsons): Other cquery-related methods should be added here.
 	// Returns the results of GetOutputFiles and GetCcObjectFiles in a single query (in that order).
-	GetCcInfo(label string, archType ArchType) (cquery.CcInfo, bool, error)
+	GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, bool, error)
 
 	// Returns the executable binary resultant from building together the python sources
-	GetPythonBinary(label string, archType ArchType) (string, bool)
+	GetPythonBinary(label string, cfgKey configKey) (string, bool)
 
 	// ** End cquery methods
 
@@ -140,17 +146,17 @@
 	LabelToPythonBinary map[string]string
 }
 
-func (m MockBazelContext) GetOutputFiles(label string, archType ArchType) ([]string, bool) {
+func (m MockBazelContext) GetOutputFiles(label string, cfgKey configKey) ([]string, bool) {
 	result, ok := m.LabelToOutputFiles[label]
 	return result, ok
 }
 
-func (m MockBazelContext) GetCcInfo(label string, archType ArchType) (cquery.CcInfo, bool, error) {
+func (m MockBazelContext) GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, bool, error) {
 	result, ok := m.LabelToCcInfo[label]
 	return result, ok, nil
 }
 
-func (m MockBazelContext) GetPythonBinary(label string, archType ArchType) (string, bool) {
+func (m MockBazelContext) GetPythonBinary(label string, cfgKey configKey) (string, bool) {
 	result, ok := m.LabelToPythonBinary[label]
 	return result, ok
 }
@@ -171,8 +177,8 @@
 
 var _ BazelContext = MockBazelContext{}
 
-func (bazelCtx *bazelContext) GetOutputFiles(label string, archType ArchType) ([]string, bool) {
-	rawString, ok := bazelCtx.cquery(label, cquery.GetOutputFiles, archType)
+func (bazelCtx *bazelContext) GetOutputFiles(label string, cfgKey configKey) ([]string, bool) {
+	rawString, ok := bazelCtx.cquery(label, cquery.GetOutputFiles, cfgKey)
 	var ret []string
 	if ok {
 		bazelOutput := strings.TrimSpace(rawString)
@@ -181,8 +187,8 @@
 	return ret, ok
 }
 
-func (bazelCtx *bazelContext) GetCcInfo(label string, archType ArchType) (cquery.CcInfo, bool, error) {
-	result, ok := bazelCtx.cquery(label, cquery.GetCcInfo, archType)
+func (bazelCtx *bazelContext) GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, bool, error) {
+	result, ok := bazelCtx.cquery(label, cquery.GetCcInfo, cfgKey)
 	if !ok {
 		return cquery.CcInfo{}, ok, nil
 	}
@@ -192,8 +198,8 @@
 	return ret, ok, err
 }
 
-func (bazelCtx *bazelContext) GetPythonBinary(label string, archType ArchType) (string, bool) {
-	rawString, ok := bazelCtx.cquery(label, cquery.GetPythonBinary, archType)
+func (bazelCtx *bazelContext) GetPythonBinary(label string, cfgKey configKey) (string, bool) {
+	rawString, ok := bazelCtx.cquery(label, cquery.GetPythonBinary, cfgKey)
 	var ret string
 	if ok {
 		bazelOutput := strings.TrimSpace(rawString)
@@ -202,19 +208,15 @@
 	return ret, ok
 }
 
-func (n noopBazelContext) GetOutputFiles(label string, archType ArchType) ([]string, bool) {
+func (n noopBazelContext) GetOutputFiles(label string, cfgKey configKey) ([]string, bool) {
 	panic("unimplemented")
 }
 
-func (n noopBazelContext) GetCcInfo(label string, archType ArchType) (cquery.CcInfo, bool, error) {
+func (n noopBazelContext) GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, bool, error) {
 	panic("unimplemented")
 }
 
-func (n noopBazelContext) GetPythonBinary(label string, archType ArchType) (string, bool) {
-	panic("unimplemented")
-}
-
-func (n noopBazelContext) GetPrebuiltCcStaticLibraryFiles(label string, archType ArchType) ([]string, bool) {
+func (n noopBazelContext) GetPythonBinary(label string, cfgKey configKey) (string, bool) {
 	panic("unimplemented")
 }
 
@@ -303,8 +305,8 @@
 // returns (result, true). If the request is queued but no results are available,
 // then returns ("", false).
 func (context *bazelContext) cquery(label string, requestType cqueryRequest,
-	archType ArchType) (string, bool) {
-	key := cqueryKey{label, requestType, archType}
+	cfgKey configKey) (string, bool) {
+	key := cqueryKey{label, requestType, cfgKey}
 	if result, ok := context.results[key]; ok {
 		return result, true
 	} else {
@@ -419,7 +421,7 @@
 
 def _config_node_transition_impl(settings, attr):
     return {
-        "//command_line_option:platforms": "@//build/bazel/platforms:android_%s" % attr.arch,
+        "//command_line_option:platforms": "@//build/bazel/platforms:%s_%s" % (attr.os, attr.arch),
     }
 
 _config_node_transition = transition(
@@ -437,7 +439,8 @@
     implementation = _passthrough_rule_impl,
     attrs = {
         "arch" : attr.string(mandatory = True),
-        "deps" : attr.label_list(cfg = _config_node_transition),
+        "os"   : attr.string(mandatory = True),
+        "deps" : attr.label_list(cfg = _config_node_transition, allow_files = True),
         "_allowlist_function_transition": attr.label(default = "@bazel_tools//tools/allowlists/function_transition_allowlist"),
     },
 )
@@ -488,38 +491,32 @@
 	configNodeFormatString := `
 config_node(name = "%s",
     arch = "%s",
+    os = "%s",
     deps = [%s],
 )
 `
 
-	commonArchFilegroupString := `
-filegroup(name = "common",
-    srcs = [%s],
-)
-`
-
 	configNodesSection := ""
 
-	labelsByArch := map[string][]string{}
+	labelsByConfig := map[string][]string{}
 	for val, _ := range context.requests {
 		labelString := fmt.Sprintf("\"@%s\"", val.label)
-		archString := getArchString(val)
-		labelsByArch[archString] = append(labelsByArch[archString], labelString)
+		configString := getConfigString(val)
+		labelsByConfig[configString] = append(labelsByConfig[configString], labelString)
 	}
 
 	allLabels := []string{}
-	for archString, labels := range labelsByArch {
-		if archString == "common" {
-			// arch-less labels (e.g. filegroups) don't need a config_node
-			allLabels = append(allLabels, "\":common\"")
-			labelsString := strings.Join(labels, ",\n            ")
-			configNodesSection += fmt.Sprintf(commonArchFilegroupString, labelsString)
-		} else {
-			// Create a config_node, and add the config_node's label to allLabels
-			allLabels = append(allLabels, fmt.Sprintf("\":%s\"", archString))
-			labelsString := strings.Join(labels, ",\n            ")
-			configNodesSection += fmt.Sprintf(configNodeFormatString, archString, archString, labelsString)
+	for configString, labels := range labelsByConfig {
+		configTokens := strings.Split(configString, "|")
+		if len(configTokens) != 2 {
+			panic(fmt.Errorf("Unexpected config string format: %s", configString))
 		}
+		archString := configTokens[0]
+		osString := configTokens[1]
+		targetString := fmt.Sprintf("%s_%s", osString, archString)
+		allLabels = append(allLabels, fmt.Sprintf("\":%s\"", targetString))
+		labelsString := strings.Join(labels, ",\n            ")
+		configNodesSection += fmt.Sprintf(configNodeFormatString, targetString, archString, osString, labelsString)
 	}
 
 	return []byte(fmt.Sprintf(formatString, configNodesSection, strings.Join(allLabels, ",\n            ")))
@@ -587,11 +584,15 @@
 %s
 
 def get_arch(target):
+  # TODO(b/199363072): filegroups and file targets aren't associated with any
+  # specific platform architecture in mixed builds. This is consistent with how
+  # Soong treats filegroups, but it may not be the case with manually-written
+  # filegroup BUILD targets.
   buildoptions = build_options(target)
   if buildoptions == None:
     # File targets do not have buildoptions. File targets aren't associated with
-    #  any specific platform architecture in mixed builds.
-    return "common"
+    #  any specific platform architecture in mixed builds, so use the host.
+    return "x86_64|linux"
   platforms = build_options(target)["//command_line_option:platforms"]
   if len(platforms) != 1:
     # An individual configured target should have only one platform architecture.
@@ -601,10 +602,12 @@
   platform_name = build_options(target)["//command_line_option:platforms"][0].name
   if platform_name == "host":
     return "HOST"
+  elif platform_name.startswith("linux_glibc_"):
+    return platform_name[len("linux_glibc_"):] + "|" + platform_name[:len("linux_glibc_")-1]
   elif platform_name.startswith("android_"):
-    return platform_name[len("android_"):]
+    return platform_name[len("android_"):] + "|" + platform_name[:len("android_")-1]
   elif platform_name.startswith("linux_"):
-    return platform_name[len("linux_"):]
+    return platform_name[len("linux_"):] + "|" + platform_name[:len("linux_")-1]
   else:
     fail("expected platform name of the form 'android_<arch>' or 'linux_<arch>', but was " + str(platforms))
     return "UNKNOWN"
@@ -852,14 +855,23 @@
 }
 
 func getCqueryId(key cqueryKey) string {
-	return key.label + "|" + getArchString(key)
+	return key.label + "|" + getConfigString(key)
 }
 
-func getArchString(key cqueryKey) string {
-	arch := key.archType.Name
-	if len(arch) > 0 {
-		return arch
-	} else {
-		return "x86_64"
+func getConfigString(key cqueryKey) string {
+	arch := key.configKey.archType.Name
+	if len(arch) == 0 || arch == "common" {
+		// Use host platform, which is currently hardcoded to be x86_64.
+		arch = "x86_64"
 	}
+	os := key.configKey.osType.Name
+	if len(os) == 0 || os == "common_os" {
+		// Use host OS, which is currently hardcoded to be linux.
+		os = "linux"
+	}
+	return arch + "|" + os
+}
+
+func GetConfigKey(ctx ModuleContext) configKey {
+	return configKey{archType: ctx.Arch().ArchType, osType: ctx.Os()}
 }
diff --git a/android/bazel_handler_test.go b/android/bazel_handler_test.go
index fe66a90..ad5b63b 100644
--- a/android/bazel_handler_test.go
+++ b/android/bazel_handler_test.go
@@ -9,11 +9,11 @@
 
 func TestRequestResultsAfterInvokeBazel(t *testing.T) {
 	label := "//foo:bar"
-	arch := Arm64
+	cfg := configKey{Arm64, Android}
 	bazelContext, _ := testBazelContext(t, map[bazelCommand]string{
-		bazelCommand{command: "cquery", expression: "deps(@soong_injection//mixed_builds:buildroot, 2)"}: `//foo:bar|arm64>>out/foo/bar.txt`,
+		bazelCommand{command: "cquery", expression: "deps(@soong_injection//mixed_builds:buildroot, 2)"}: `//foo:bar|arm64|android>>out/foo/bar.txt`,
 	})
-	g, ok := bazelContext.GetOutputFiles(label, arch)
+	g, ok := bazelContext.GetOutputFiles(label, cfg)
 	if ok {
 		t.Errorf("Did not expect cquery results prior to running InvokeBazel(), but got %s", g)
 	}
@@ -21,7 +21,7 @@
 	if err != nil {
 		t.Fatalf("Did not expect error invoking Bazel, but got %s", err)
 	}
-	g, ok = bazelContext.GetOutputFiles(label, arch)
+	g, ok = bazelContext.GetOutputFiles(label, cfg)
 	if !ok {
 		t.Errorf("Expected cquery results after running InvokeBazel(), but got none")
 	} else if w := []string{"out/foo/bar.txt"}; !reflect.DeepEqual(w, g) {
diff --git a/android/bazel_paths.go b/android/bazel_paths.go
index 9957369..729c73c 100644
--- a/android/bazel_paths.go
+++ b/android/bazel_paths.go
@@ -68,24 +68,36 @@
 //   cannot be resolved,the function will panic. This is often due to the dependency not being added
 //   via an AddDependency* method.
 
-// A subset of the ModuleContext methods which are sufficient to resolve references to paths/deps in
-// order to form a Bazel-compatible label for conversion.
-type BazelConversionPathContext interface {
-	EarlyModulePathContext
+// A minimal context interface to check if a module should be converted by bp2build,
+// with functions containing information to match against allowlists and denylists.
+// If a module is deemed to be convertible by bp2build, then it should rely on a
+// BazelConversionPathContext for more functions for dep/path features.
+type BazelConversionContext interface {
+	Config() Config
 
-	GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag)
-	ModuleFromName(name string) (blueprint.Module, bool)
 	Module() Module
 	OtherModuleType(m blueprint.Module) string
 	OtherModuleName(m blueprint.Module) string
 	OtherModuleDir(m blueprint.Module) string
+}
+
+// A subset of the ModuleContext methods which are sufficient to resolve references to paths/deps in
+// order to form a Bazel-compatible label for conversion.
+type BazelConversionPathContext interface {
+	EarlyModulePathContext
+	BazelConversionContext
+
+	ModuleErrorf(fmt string, args ...interface{})
+	PropertyErrorf(property, fmt string, args ...interface{})
+	GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag)
+	ModuleFromName(name string) (blueprint.Module, bool)
 	AddUnconvertedBp2buildDep(string)
 }
 
 // BazelLabelForModuleDeps expects a list of reference to other modules, ("<module>"
 // or ":<module>") and returns a Bazel-compatible label which corresponds to dependencies on the
 // module within the given ctx.
-func BazelLabelForModuleDeps(ctx TopDownMutatorContext, modules []string) bazel.LabelList {
+func BazelLabelForModuleDeps(ctx BazelConversionPathContext, modules []string) bazel.LabelList {
 	return BazelLabelForModuleDepsWithFn(ctx, modules, BazelModuleLabel)
 }
 
@@ -95,15 +107,15 @@
 // 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 BazelLabelForModuleDepsExcludes(ctx TopDownMutatorContext, modules, excludes []string) bazel.LabelList {
+func BazelLabelForModuleDepsExcludes(ctx BazelConversionPathContext, modules, excludes []string) bazel.LabelList {
 	return BazelLabelForModuleDepsExcludesWithFn(ctx, modules, excludes, BazelModuleLabel)
 }
 
 // BazelLabelForModuleDepsWithFn expects a list of reference to other modules, ("<module>"
 // or ":<module>") and applies moduleToLabelFn to determine and return a Bazel-compatible label
 // which corresponds to dependencies on the module within the given ctx.
-func BazelLabelForModuleDepsWithFn(ctx TopDownMutatorContext, modules []string,
-	moduleToLabelFn func(TopDownMutatorContext, blueprint.Module) string) bazel.LabelList {
+func BazelLabelForModuleDepsWithFn(ctx BazelConversionPathContext, modules []string,
+	moduleToLabelFn func(BazelConversionPathContext, blueprint.Module) string) 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 {
@@ -131,8 +143,8 @@
 // to other modules, ("<module>" or ":<module>"). It applies moduleToLabelFn to determine and return a
 // Bazel-compatible label list which corresponds to dependencies on the module within the given ctx, and
 // the excluded dependencies.
-func BazelLabelForModuleDepsExcludesWithFn(ctx TopDownMutatorContext, modules, excludes []string,
-	moduleToLabelFn func(TopDownMutatorContext, blueprint.Module) string) bazel.LabelList {
+func BazelLabelForModuleDepsExcludesWithFn(ctx BazelConversionPathContext, modules, excludes []string,
+	moduleToLabelFn func(BazelConversionPathContext, blueprint.Module) string) bazel.LabelList {
 	moduleLabels := BazelLabelForModuleDepsWithFn(ctx, RemoveListFromList(modules, excludes), moduleToLabelFn)
 	if len(excludes) == 0 {
 		return moduleLabels
@@ -144,11 +156,11 @@
 	}
 }
 
-func BazelLabelForModuleSrcSingle(ctx TopDownMutatorContext, path string) bazel.Label {
+func BazelLabelForModuleSrcSingle(ctx BazelConversionPathContext, path string) bazel.Label {
 	return BazelLabelForModuleSrcExcludes(ctx, []string{path}, []string(nil)).Includes[0]
 }
 
-func BazelLabelForModuleDepSingle(ctx TopDownMutatorContext, path string) bazel.Label {
+func BazelLabelForModuleDepSingle(ctx BazelConversionPathContext, path string) bazel.Label {
 	return BazelLabelForModuleDepsExcludes(ctx, []string{path}, []string(nil)).Includes[0]
 }
 
@@ -158,7 +170,7 @@
 // relative if within the same package).
 // Properties must have been annotated with struct tag `android:"path"` so that dependencies modules
 // will have already been handled by the path_deps mutator.
-func BazelLabelForModuleSrc(ctx TopDownMutatorContext, paths []string) bazel.LabelList {
+func BazelLabelForModuleSrc(ctx BazelConversionPathContext, paths []string) bazel.LabelList {
 	return BazelLabelForModuleSrcExcludes(ctx, paths, []string(nil))
 }
 
@@ -168,7 +180,7 @@
 // (absolute if in a different package or relative if within the same package).
 // Properties must have been annotated with struct tag `android:"path"` so that dependencies modules
 // will have already been handled by the path_deps mutator.
-func BazelLabelForModuleSrcExcludes(ctx TopDownMutatorContext, paths, excludes []string) bazel.LabelList {
+func BazelLabelForModuleSrcExcludes(ctx BazelConversionPathContext, paths, excludes []string) bazel.LabelList {
 	excludeLabels := expandSrcsForBazel(ctx, excludes, []string(nil))
 	excluded := make([]string, 0, len(excludeLabels.Includes))
 	for _, e := range excludeLabels.Includes {
@@ -288,7 +300,7 @@
 // Properties passed as the paths or excludes argument must have been annotated with struct tag
 // `android:"path"` so that dependencies on other modules will have already been handled by the
 // path_deps mutator.
-func expandSrcsForBazel(ctx TopDownMutatorContext, paths, expandedExcludes []string) bazel.LabelList {
+func expandSrcsForBazel(ctx BazelConversionPathContext, paths, expandedExcludes []string) bazel.LabelList {
 	if paths == nil {
 		return bazel.LabelList{}
 	}
@@ -336,8 +348,8 @@
 // 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 TopDownMutatorContext, dep, tag string,
-	labelFromModule func(TopDownMutatorContext, blueprint.Module) string) bazel.Label {
+func getOtherModuleLabel(ctx BazelConversionPathContext, dep, tag string,
+	labelFromModule func(BazelConversionPathContext, blueprint.Module) string) bazel.Label {
 	m, _ := ctx.ModuleFromName(dep)
 	if m == nil {
 		panic(fmt.Errorf("No module named %q found, but was a direct dep of %q", dep, ctx.Module().Name()))
@@ -359,7 +371,7 @@
 	}
 }
 
-func BazelModuleLabel(ctx TopDownMutatorContext, module blueprint.Module) string {
+func BazelModuleLabel(ctx BazelConversionPathContext, module blueprint.Module) string {
 	// TODO(b/165114590): Convert tag (":name{.tag}") to corresponding Bazel implicit output targets.
 	if !convertedToBazel(ctx, module) {
 		return bp2buildModuleLabel(ctx, module)
@@ -370,11 +382,17 @@
 
 func bazelShortLabel(label string) string {
 	i := strings.Index(label, ":")
+	if i == -1 {
+		panic(fmt.Errorf("Could not find the ':' character in '%s', expected a fully qualified label.", label))
+	}
 	return label[i:]
 }
 
 func bazelPackage(label string) string {
 	i := strings.Index(label, ":")
+	if i == -1 {
+		panic(fmt.Errorf("Could not find the ':' character in '%s', expected a fully qualified label.", label))
+	}
 	return label[0:i]
 }
 
@@ -382,7 +400,7 @@
 	return bazelPackage(label1) == bazelPackage(label2)
 }
 
-func bp2buildModuleLabel(ctx BazelConversionPathContext, module blueprint.Module) string {
+func bp2buildModuleLabel(ctx BazelConversionContext, module blueprint.Module) string {
 	moduleName := ctx.OtherModuleName(module)
 	moduleDir := ctx.OtherModuleDir(module)
 	return fmt.Sprintf("//%s:%s", moduleDir, moduleName)
diff --git a/android/config.go b/android/config.go
index c8d7cfd..78d43c6 100644
--- a/android/config.go
+++ b/android/config.go
@@ -355,14 +355,14 @@
 
 	config.bp2buildModuleTypeConfig = map[string]bool{}
 
+	determineBuildOS(config)
+
 	return Config{config}
 }
 
 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},
@@ -568,15 +568,17 @@
 }
 
 func (c *config) HostToolPath(ctx PathContext, tool string) Path {
-	return PathForOutput(ctx, "host", c.PrebuiltOS(), "bin", tool)
+	path := pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "bin", false, tool)
+	return path
 }
 
-func (c *config) HostJNIToolPath(ctx PathContext, path string) Path {
+func (c *config) HostJNIToolPath(ctx PathContext, lib string) Path {
 	ext := ".so"
 	if runtime.GOOS == "darwin" {
 		ext = ".dylib"
 	}
-	return PathForOutput(ctx, "host", c.PrebuiltOS(), "lib64", path+ext)
+	path := pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "lib64", false, lib+ext)
+	return path
 }
 
 func (c *config) HostJavaToolPath(ctx PathContext, path string) Path {
@@ -1578,6 +1580,10 @@
 	return c.config.productVariables.SepolicyFreezeTestExtraPrebuiltDirs
 }
 
+func (c *deviceConfig) GenerateAidlNdkPlatformBackend() bool {
+	return c.config.productVariables.GenerateAidlNdkPlatformBackend
+}
+
 // The ConfiguredJarList struct provides methods for handling a list of (apex, jar) pairs.
 // Such lists are used in the build system for things like bootclasspath jars or system server jars.
 // The apex part is either an apex name, or a special names "platform" or "system_ext". Jar is a
diff --git a/android/filegroup.go b/android/filegroup.go
index f8f0955..a79374d 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -118,14 +118,16 @@
 	}
 
 	archVariant := ctx.Arch().ArchType
+	osVariant := ctx.Os()
 	if len(fg.Srcs()) == 1 && fg.Srcs()[0].Base() == fg.Name() {
 		// This will be a regular file target, not filegroup, in Bazel.
 		// See FilegroupBp2Build for more information.
 		archVariant = Common
+		osVariant = CommonOS
 	}
 
 	bazelCtx := ctx.Config().BazelContext
-	filePaths, ok := bazelCtx.GetOutputFiles(fg.GetBazelLabel(ctx, fg), archVariant)
+	filePaths, ok := bazelCtx.GetOutputFiles(fg.GetBazelLabel(ctx, fg), configKey{archVariant, osVariant})
 	if !ok {
 		return
 	}
diff --git a/android/licenses.go b/android/licenses.go
index bcd85f9..e9e271b 100644
--- a/android/licenses.go
+++ b/android/licenses.go
@@ -308,3 +308,12 @@
 }
 
 var LicenseInfoProvider = blueprint.NewProvider(LicenseInfo{})
+
+func init() {
+	RegisterMakeVarsProvider(pctx, licensesMakeVarsProvider)
+}
+
+func licensesMakeVarsProvider(ctx MakeVarsContext) {
+	ctx.Strict("BUILD_LICENSE_METADATA",
+		ctx.Config().HostToolPath(ctx, "build_license_metadata").String())
+}
diff --git a/android/makevars.go b/android/makevars.go
index 40c0ccd..20db65a 100644
--- a/android/makevars.go
+++ b/android/makevars.go
@@ -17,6 +17,8 @@
 import (
 	"bytes"
 	"fmt"
+	"path/filepath"
+	"runtime"
 	"sort"
 	"strings"
 
@@ -222,6 +224,9 @@
 	lateOutFile := absolutePath(PathForOutput(ctx,
 		"late"+proptools.String(ctx.Config().productVariables.Make_suffix)+".mk").String())
 
+	installsFile := absolutePath(PathForOutput(ctx,
+		"installs"+proptools.String(ctx.Config().productVariables.Make_suffix)+".mk").String())
+
 	if ctx.Failed() {
 		return
 	}
@@ -229,6 +234,8 @@
 	var vars []makeVarsVariable
 	var dists []dist
 	var phonies []phony
+	var katiInstalls []katiInstall
+	var katiSymlinks []katiInstall
 
 	providers := append([]makeVarsProvider(nil), makeVarsInitProviders...)
 	providers = append(providers, *ctx.Config().Get(singletonMakeVarsProvidersKey).(*[]makeVarsProvider)...)
@@ -258,6 +265,11 @@
 			phonies = append(phonies, mctx.phonies...)
 			dists = append(dists, mctx.dists...)
 		}
+
+		if m.ExportedToMake() {
+			katiInstalls = append(katiInstalls, m.base().katiInstalls...)
+			katiSymlinks = append(katiSymlinks, m.base().katiSymlinks...)
+		}
 	})
 
 	if ctx.Failed() {
@@ -297,6 +309,10 @@
 		ctx.Errorf(err.Error())
 	}
 
+	installsBytes := s.writeInstalls(katiInstalls, katiSymlinks)
+	if err := pathtools.WriteFileIfChanged(installsFile, installsBytes, 0666); err != nil {
+		ctx.Errorf(err.Error())
+	}
 }
 
 func (s *makeVarsSingleton) writeVars(vars []makeVarsVariable) []byte {
@@ -405,6 +421,84 @@
 	return buf.Bytes()
 }
 
+// writeInstalls writes the list of install rules generated by Soong to a makefile.  The rules
+// are exported to Make instead of written directly to the ninja file so that main.mk can add
+// the dependencies from the `required` property that are hard to resolve in Soong.
+func (s *makeVarsSingleton) writeInstalls(installs, symlinks []katiInstall) []byte {
+	buf := &bytes.Buffer{}
+
+	fmt.Fprint(buf, `# Autogenerated file
+
+# Values written by Soong to generate install rules that can be amended by Kati.
+
+
+`)
+
+	preserveSymlinksFlag := "-d"
+	if runtime.GOOS == "darwin" {
+		preserveSymlinksFlag = "-R"
+	}
+
+	for _, install := range installs {
+		// Write a rule for each install request in the form:
+		//  to: from [ deps ] [ | order only deps ]
+		//       cp -f -d $< $@ [ && chmod +x $@ ]
+		fmt.Fprintf(buf, "%s: %s", install.to.String(), install.from.String())
+		for _, dep := range install.implicitDeps {
+			fmt.Fprintf(buf, " %s", dep.String())
+		}
+		if len(install.orderOnlyDeps) > 0 {
+			fmt.Fprintf(buf, " |")
+		}
+		for _, dep := range install.orderOnlyDeps {
+			fmt.Fprintf(buf, " %s", dep.String())
+		}
+		fmt.Fprintln(buf)
+
+		fmt.Fprintf(buf, "\trm -f $@ && cp -f %s $< $@", preserveSymlinksFlag)
+		if install.executable {
+			fmt.Fprintf(buf, " && chmod +x $@")
+		}
+		fmt.Fprintln(buf)
+		fmt.Fprintln(buf)
+	}
+
+	for _, symlink := range symlinks {
+		fmt.Fprintf(buf, "%s:", symlink.to.String())
+		for _, dep := range symlink.implicitDeps {
+			fmt.Fprintf(buf, " %s", dep.String())
+		}
+		if symlink.from != nil || len(symlink.orderOnlyDeps) > 0 {
+			fmt.Fprintf(buf, " |")
+		}
+		if symlink.from != nil {
+			fmt.Fprintf(buf, " %s", symlink.from.String())
+		}
+		for _, dep := range symlink.orderOnlyDeps {
+			fmt.Fprintf(buf, " %s", dep.String())
+		}
+		fmt.Fprintln(buf)
+
+		fromStr := ""
+		if symlink.from != nil {
+			rel, err := filepath.Rel(filepath.Dir(symlink.to.String()), symlink.from.String())
+			if err != nil {
+				panic(fmt.Errorf("failed to find relative path for symlink from %q to %q: %w",
+					symlink.from.String(), symlink.to.String(), err))
+			}
+			fromStr = rel
+		} else {
+			fromStr = symlink.absFrom
+		}
+
+		fmt.Fprintf(buf, "\trm -f $@ && ln -sfn %s $@", fromStr)
+		fmt.Fprintln(buf)
+		fmt.Fprintln(buf)
+	}
+
+	return buf.Bytes()
+}
+
 func (c *makeVarsContext) DeviceConfig() DeviceConfig {
 	return DeviceConfig{c.Config().deviceConfig}
 }
diff --git a/android/module.go b/android/module.go
index 02706ec..3447f2b 100644
--- a/android/module.go
+++ b/android/module.go
@@ -409,7 +409,7 @@
 	PackageFile(installPath InstallPath, name string, srcPath Path) PackagingSpec
 
 	CheckbuildFile(srcPath Path)
-	TidyFile(srcPath Path)
+	TidyFile(srcPath WritablePath)
 
 	InstallInData() bool
 	InstallInTestcases() bool
@@ -1190,11 +1190,14 @@
 	installFiles         InstallPaths
 	installFilesDepSet   *installPathsDepSet
 	checkbuildFiles      Paths
-	tidyFiles            Paths
+	tidyFiles            WritablePaths
 	packagingSpecs       []PackagingSpec
 	packagingSpecsDepSet *packagingSpecsDepSet
 	noticeFiles          Paths
-	phonies              map[string]Paths
+	// katiInstalls tracks the install rules that were created by Soong but are being exported
+	// to Make to convert to ninja rules so that Make can add additional dependencies.
+	katiInstalls katiInstalls
+	katiSymlinks katiInstalls
 
 	// The files to copy to the dist as explicitly specified in the .bp file.
 	distFiles TaggedDistFiles
@@ -1764,12 +1767,18 @@
 func (m *ModuleBase) generateModuleTarget(ctx ModuleContext) {
 	var allInstalledFiles InstallPaths
 	var allCheckbuildFiles Paths
-	var allTidyFiles Paths
+	var allTidyFiles WritablePaths
 	ctx.VisitAllModuleVariants(func(module Module) {
 		a := module.base()
 		allInstalledFiles = append(allInstalledFiles, a.installFiles...)
-		allCheckbuildFiles = append(allCheckbuildFiles, a.checkbuildFiles...)
-		allTidyFiles = append(allTidyFiles, a.tidyFiles...)
+		// A module's -{checkbuild,tidy} phony targets should
+		// not be created if the module is not exported to make.
+		// Those could depend on the build target and fail to compile
+		// for the current build target.
+		if !ctx.Config().KatiEnabled() || !shouldSkipAndroidMkProcessing(a) {
+			allCheckbuildFiles = append(allCheckbuildFiles, a.checkbuildFiles...)
+			allTidyFiles = append(allTidyFiles, a.tidyFiles...)
+		}
 	})
 
 	var deps Paths
@@ -1795,7 +1804,7 @@
 
 	if len(allTidyFiles) > 0 {
 		name := namespacePrefix + ctx.ModuleName() + "-tidy"
-		ctx.Phony(name, allTidyFiles...)
+		ctx.Phony(name, allTidyFiles.Paths()...)
 		m.tidyTarget = PathForPhony(ctx, name)
 		deps = append(deps, m.tidyTarget)
 	}
@@ -2010,9 +2019,8 @@
 		m.checkbuildFiles = append(m.checkbuildFiles, ctx.checkbuildFiles...)
 		m.tidyFiles = append(m.tidyFiles, ctx.tidyFiles...)
 		m.packagingSpecs = append(m.packagingSpecs, ctx.packagingSpecs...)
-		for k, v := range ctx.phonies {
-			m.phonies[k] = append(m.phonies[k], v...)
-		}
+		m.katiInstalls = append(m.katiInstalls, ctx.katiInstalls...)
+		m.katiSymlinks = append(m.katiSymlinks, ctx.katiSymlinks...)
 	} else if ctx.Config().AllowMissingDependencies() {
 		// If the module is not enabled it will not create any build rules, nothing will call
 		// ctx.GetMissingDependencies(), and blueprint will consider the missing dependencies to be unhandled
@@ -2207,16 +2215,56 @@
 	packagingSpecs  []PackagingSpec
 	installFiles    InstallPaths
 	checkbuildFiles Paths
-	tidyFiles       Paths
+	tidyFiles       WritablePaths
 	module          Module
 	phonies         map[string]Paths
 
+	katiInstalls []katiInstall
+	katiSymlinks []katiInstall
+
 	// For tests
 	buildParams []BuildParams
 	ruleParams  map[blueprint.Rule]blueprint.RuleParams
 	variables   map[string]string
 }
 
+// katiInstall stores a request from Soong to Make to create an install rule.
+type katiInstall struct {
+	from          Path
+	to            InstallPath
+	implicitDeps  Paths
+	orderOnlyDeps Paths
+	executable    bool
+
+	absFrom string
+}
+
+type katiInstalls []katiInstall
+
+// BuiltInstalled returns the katiInstalls in the form used by $(call copy-many-files) in Make, a
+// space separated list of from:to tuples.
+func (installs katiInstalls) BuiltInstalled() string {
+	sb := strings.Builder{}
+	for i, install := range installs {
+		if i != 0 {
+			sb.WriteRune(' ')
+		}
+		sb.WriteString(install.from.String())
+		sb.WriteRune(':')
+		sb.WriteString(install.to.String())
+	}
+	return sb.String()
+}
+
+// InstallPaths returns the install path of each entry.
+func (installs katiInstalls) InstallPaths() InstallPaths {
+	paths := make(InstallPaths, 0, len(installs))
+	for _, install := range installs {
+		paths = append(paths, install.to)
+	}
+	return paths
+}
+
 func (m *moduleContext) ninjaError(params BuildParams, err error) (PackageContext, BuildParams) {
 	return pctx, BuildParams{
 		Rule:            ErrorRule,
@@ -2848,20 +2896,33 @@
 			orderOnlyDeps = deps
 		}
 
-		rule := Cp
-		if executable {
-			rule = CpExecutable
-		}
+		if m.Config().KatiEnabled() && m.InstallBypassMake() {
+			// When creating the install rule in Soong but embedding in Make, write the rule to a
+			// makefile instead of directly to the ninja file so that main.mk can add the
+			// dependencies from the `required` property that are hard to resolve in Soong.
+			m.katiInstalls = append(m.katiInstalls, katiInstall{
+				from:          srcPath,
+				to:            fullInstallPath,
+				implicitDeps:  implicitDeps,
+				orderOnlyDeps: orderOnlyDeps,
+				executable:    executable,
+			})
+		} else {
+			rule := Cp
+			if executable {
+				rule = CpExecutable
+			}
 
-		m.Build(pctx, BuildParams{
-			Rule:        rule,
-			Description: "install " + fullInstallPath.Base(),
-			Output:      fullInstallPath,
-			Input:       srcPath,
-			Implicits:   implicitDeps,
-			OrderOnly:   orderOnlyDeps,
-			Default:     !m.Config().KatiEnabled(),
-		})
+			m.Build(pctx, BuildParams{
+				Rule:        rule,
+				Description: "install " + fullInstallPath.Base(),
+				Output:      fullInstallPath,
+				Input:       srcPath,
+				Implicits:   implicitDeps,
+				OrderOnly:   orderOnlyDeps,
+				Default:     !m.Config().KatiEnabled(),
+			})
+		}
 
 		m.installFiles = append(m.installFiles, fullInstallPath)
 	}
@@ -2883,16 +2944,26 @@
 	}
 	if !m.skipInstall() {
 
-		m.Build(pctx, BuildParams{
-			Rule:        Symlink,
-			Description: "install symlink " + fullInstallPath.Base(),
-			Output:      fullInstallPath,
-			Input:       srcPath,
-			Default:     !m.Config().KatiEnabled(),
-			Args: map[string]string{
-				"fromPath": relPath,
-			},
-		})
+		if m.Config().KatiEnabled() && m.InstallBypassMake() {
+			// When creating the symlink rule in Soong but embedding in Make, write the rule to a
+			// makefile instead of directly to the ninja file so that main.mk can add the
+			// dependencies from the `required` property that are hard to resolve in Soong.
+			m.katiSymlinks = append(m.katiSymlinks, katiInstall{
+				from: srcPath,
+				to:   fullInstallPath,
+			})
+		} else {
+			m.Build(pctx, BuildParams{
+				Rule:        Symlink,
+				Description: "install symlink " + fullInstallPath.Base(),
+				Output:      fullInstallPath,
+				Input:       srcPath,
+				Default:     !m.Config().KatiEnabled(),
+				Args: map[string]string{
+					"fromPath": relPath,
+				},
+			})
+		}
 
 		m.installFiles = append(m.installFiles, fullInstallPath)
 		m.checkbuildFiles = append(m.checkbuildFiles, srcPath)
@@ -2915,15 +2986,25 @@
 	m.module.base().hooks.runInstallHooks(m, nil, fullInstallPath, true)
 
 	if !m.skipInstall() {
-		m.Build(pctx, BuildParams{
-			Rule:        Symlink,
-			Description: "install symlink " + fullInstallPath.Base() + " -> " + absPath,
-			Output:      fullInstallPath,
-			Default:     !m.Config().KatiEnabled(),
-			Args: map[string]string{
-				"fromPath": absPath,
-			},
-		})
+		if m.Config().KatiEnabled() && m.InstallBypassMake() {
+			// When creating the symlink rule in Soong but embedding in Make, write the rule to a
+			// makefile instead of directly to the ninja file so that main.mk can add the
+			// dependencies from the `required` property that are hard to resolve in Soong.
+			m.katiSymlinks = append(m.katiSymlinks, katiInstall{
+				absFrom: absPath,
+				to:      fullInstallPath,
+			})
+		} else {
+			m.Build(pctx, BuildParams{
+				Rule:        Symlink,
+				Description: "install symlink " + fullInstallPath.Base() + " -> " + absPath,
+				Output:      fullInstallPath,
+				Default:     !m.Config().KatiEnabled(),
+				Args: map[string]string{
+					"fromPath": absPath,
+				},
+			})
+		}
 
 		m.installFiles = append(m.installFiles, fullInstallPath)
 	}
@@ -2942,7 +3023,7 @@
 	m.checkbuildFiles = append(m.checkbuildFiles, srcPath)
 }
 
-func (m *moduleContext) TidyFile(srcPath Path) {
+func (m *moduleContext) TidyFile(srcPath WritablePath) {
 	m.tidyFiles = append(m.tidyFiles, srcPath)
 }
 
diff --git a/android/neverallow.go b/android/neverallow.go
index 9098a71..04366d3 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -27,7 +27,7 @@
 // "neverallow" rules for the build system.
 //
 // This allows things which aren't related to the build system and are enforced
-// for sanity, in progress code refactors, or policy to be expressed in a
+// against assumptions, in progress code refactors, or policy to be expressed in a
 // straightforward away disjoint from implementations and tests which should
 // work regardless of these restrictions.
 //
diff --git a/android/paths.go b/android/paths.go
index 2e378ba..69ab5f7 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -462,6 +462,13 @@
 	return ret
 }
 
+// PathForGoBinary returns the path to the installed location of a bootstrap_go_binary module.
+func PathForGoBinary(ctx PathContext, goBinary bootstrap.GoBinaryTool) Path {
+	goBinaryInstallDir := pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "bin", false)
+	rel := Rel(ctx, goBinaryInstallDir.String(), goBinary.InstallPath())
+	return goBinaryInstallDir.Join(ctx, rel)
+}
+
 // Expands Paths to a SourceFileProducer or OutputFileProducer module dependency referenced via ":name" or ":name{.tag}" syntax.
 // 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.
@@ -482,11 +489,8 @@
 	} else if tag != "" {
 		return nil, fmt.Errorf("path dependency %q is not an output file producing module", path)
 	} else if goBinary, ok := module.(bootstrap.GoBinaryTool); ok {
-		if rel, err := filepath.Rel(PathForOutput(ctx).String(), goBinary.InstallPath()); err == nil {
-			return Paths{PathForOutput(ctx, rel).WithoutRel()}, nil
-		} else {
-			return nil, fmt.Errorf("cannot find output path for %q: %w", goBinary.InstallPath(), err)
-		}
+		goBinaryPath := PathForGoBinary(ctx, goBinary)
+		return Paths{goBinaryPath}, nil
 	} else if srcProducer, ok := module.(SourceFileProducer); ok {
 		return srcProducer.Srcs(), nil
 	} else {
@@ -1571,6 +1575,8 @@
 	// For example, it is host/<os>-<arch> for host modules, and target/product/<device>/<partition> for device modules.
 	partitionDir string
 
+	partition string
+
 	// makePath indicates whether this path is for Soong (false) or Make (true).
 	makePath bool
 }
@@ -1716,6 +1722,7 @@
 		basePath:     basePath{partionPath, ""},
 		soongOutDir:  ctx.Config().soongOutDir,
 		partitionDir: partionPath,
+		partition:    partition,
 		makePath:     false,
 	}
 
@@ -1741,8 +1748,7 @@
 }
 
 func InstallPathToOnDevicePath(ctx PathContext, path InstallPath) string {
-	rel := Rel(ctx, PathForOutput(ctx, "target", "product", ctx.Config().DeviceName()).String(), path.String())
-
+	rel := Rel(ctx, strings.TrimSuffix(path.PartitionDir(), path.partition), path.String())
 	return "/" + rel
 }
 
diff --git a/android/rule_builder.go b/android/rule_builder.go
index c9a9ddd..1c6b1c0 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -833,10 +833,11 @@
 
 func sboxPathForToolRel(ctx BuilderContext, path Path) string {
 	// Errors will be handled in RuleBuilder.Build where we have a context to report them
-	relOut, isRelOut, _ := maybeRelErr(PathForOutput(ctx, "host", ctx.Config().PrebuiltOS()).String(), path.String())
-	if isRelOut {
-		// The tool is in the output directory, it will be copied to __SBOX_OUT_DIR__/tools/out
-		return filepath.Join(sboxToolsSubDir, "out", relOut)
+	toolDir := pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, "", false)
+	relOutSoong, isRelOutSoong, _ := maybeRelErr(toolDir.String(), path.String())
+	if isRelOutSoong {
+		// The tool is in the Soong output directory, it will be copied to __SBOX_OUT_DIR__/tools/out
+		return filepath.Join(sboxToolsSubDir, "out", relOutSoong)
 	}
 	// The tool is in the source directory, it will be copied to __SBOX_OUT_DIR__/tools/src
 	return filepath.Join(sboxToolsSubDir, "src", path.String())
diff --git a/android/sdk_version.go b/android/sdk_version.go
index c6c75a3..1813e7e 100644
--- a/android/sdk_version.go
+++ b/android/sdk_version.go
@@ -157,7 +157,7 @@
 		return ctx.Config().AlwaysUsePrebuiltSdks()
 	} else if !s.ApiLevel.IsPreview() {
 		// validation check
-		if s.Kind != SdkPublic && s.Kind != SdkSystem && s.Kind != SdkTest && s.Kind != SdkModule {
+		if s.Kind != SdkPublic && s.Kind != SdkSystem && s.Kind != SdkTest && s.Kind != SdkModule && s.Kind != SdkSystemServer {
 			panic(fmt.Errorf("prebuilt SDK is not not available for SdkKind=%q", s.Kind))
 			return false
 		}
diff --git a/android/variable.go b/android/variable.go
index 5c54e94..e943640 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -419,6 +419,8 @@
 
 	SepolicyFreezeTestExtraDirs         []string `json:",omitempty"`
 	SepolicyFreezeTestExtraPrebuiltDirs []string `json:",omitempty"`
+
+	GenerateAidlNdkPlatformBackend bool `json:",omitempty"`
 }
 
 func boolPtr(v bool) *bool {
@@ -496,7 +498,7 @@
 
 // ProductVariableProperties returns a ProductConfigProperties containing only the properties which
 // have been set for the module in the given context.
-func ProductVariableProperties(ctx BaseMutatorContext) ProductConfigProperties {
+func ProductVariableProperties(ctx BazelConversionPathContext) ProductConfigProperties {
 	module := ctx.Module()
 	moduleBase := module.base()
 
diff --git a/apex/apex.go b/apex/apex.go
index 0bca093..33188cb 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -178,6 +178,10 @@
 	// used in tests.
 	Test_only_force_compression *bool
 
+	// Put extra tags (signer=<value>) to apexkeys.txt, so that release tools can sign this apex
+	// with the tool to sign payload contents.
+	Custom_sign_tool *string
+
 	// Canonical name of this APEX bundle. Used to determine the path to the
 	// activated APEX on device (i.e. /apex/<apexVariationName>), and used for the
 	// apex mutator variations. For override_apex modules, this is the name of the
@@ -348,7 +352,6 @@
 	// Flags for special variants of APEX
 	testApex bool
 	vndkApex bool
-	artApex  bool
 
 	// Tells whether this variant of the APEX bundle is the primary one or not. Only the primary
 	// one gets installed to the device.
@@ -754,13 +757,6 @@
 	ctx.AddFarVariationDependencies(commonVariation, fsTag, a.properties.Filesystems...)
 	ctx.AddFarVariationDependencies(commonVariation, compatConfigTag, a.properties.Compat_configs...)
 
-	if a.artApex {
-		// With EMMA_INSTRUMENT_FRAMEWORK=true the ART boot image includes jacoco library.
-		if ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") {
-			ctx.AddFarVariationDependencies(commonVariation, javaLibTag, "jacocoagent")
-		}
-	}
-
 	// Marks that this APEX (in fact all the modules in it) has to be built with the given SDKs.
 	// This field currently isn't used.
 	// TODO(jiyong): consider dropping this feature
@@ -1462,12 +1458,7 @@
 
 func apexFileForGoBinary(ctx android.BaseModuleContext, depName string, gb bootstrap.GoBinaryTool) apexFile {
 	dirInApex := "bin"
-	s, err := filepath.Rel(android.PathForOutput(ctx).String(), gb.InstallPath())
-	if err != nil {
-		ctx.ModuleErrorf("Unable to use compiled binary at %s", gb.InstallPath())
-		return apexFile{}
-	}
-	fileToCopy := android.PathForOutput(ctx, s)
+	fileToCopy := android.PathForGoBinary(ctx, gb)
 	// NB: Since go binaries are static we don't need the module for anything here, which is
 	// good since the go tool is a blueprint.Module not an android.Module like we would
 	// normally use.
@@ -2203,10 +2194,9 @@
 	return module
 }
 
-func ApexBundleFactory(testApex bool, artApex bool) android.Module {
+func ApexBundleFactory(testApex bool) android.Module {
 	bundle := newApexBundle()
 	bundle.testApex = testApex
-	bundle.artApex = artApex
 	return bundle
 }
 
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 78a6bb8..8aaa31a 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -7683,6 +7683,28 @@
 			name: "myapex",
 			key: "myapex.key",
 			updatable: false,
+			custom_sign_tool: "sign_myapex",
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+	`)
+
+	apexKeysText := ctx.SingletonForTests("apex_keys_text")
+	content := apexKeysText.MaybeDescription("apexkeys.txt").BuildParams.Args["content"]
+	ensureContains(t, content, `name="myapex.apex" public_key="vendor/foo/devkeys/testkey.avbpubkey" private_key="vendor/foo/devkeys/testkey.pem" container_certificate="vendor/foo/devkeys/test.x509.pem" container_private_key="vendor/foo/devkeys/test.pk8" partition="system_ext" sign_tool="sign_myapex"`)
+}
+
+func TestApexKeysTxtOverrides(t *testing.T) {
+	ctx := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			updatable: false,
+			custom_sign_tool: "sign_myapex",
 		}
 
 		apex_key {
diff --git a/apex/builder.go b/apex/builder.go
index 702b6fc..e22d694 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -106,7 +106,7 @@
 		Description: "convert ${in}=>${out}",
 	})
 
-	// TODO(b/113233103): make sure that file_contexts is sane, i.e., validate
+	// TODO(b/113233103): make sure that file_contexts is as expected, i.e., validate
 	// against the binary policy using sefcontext_compiler -p <policy>.
 
 	// TODO(b/114327326): automate the generation of file_contexts
@@ -524,7 +524,7 @@
 	}
 
 	unsignedOutputFile := android.PathForModuleOut(ctx, a.Name()+suffix+".unsigned")
-	outHostBinDir := android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "bin").String()
+	outHostBinDir := ctx.Config().HostToolPath(ctx, "").String()
 	prebuiltSdkToolsBinDir := filepath.Join("prebuilts", "sdk", "tools", runtime.GOOS, "bin")
 
 	// Figure out if need to compress apex.
@@ -716,12 +716,10 @@
 			}
 		}
 		apisBackedbyOutputFile := android.PathForModuleOut(ctx, a.Name()+"_backing.txt")
-		ndkLibraryList := android.PathForSource(ctx, "system/core/rootdir/etc/public.libraries.android.txt")
 		rule := android.NewRuleBuilder(pctx, ctx)
 		rule.Command().
 			Tool(android.PathForSource(ctx, "build/soong/scripts/gen_ndk_backedby_apex.sh")).
 			Output(apisBackedbyOutputFile).
-			Input(ndkLibraryList).
 			Flags(libNames)
 		rule.Build("ndk_backedby_list", "Generate API libraries backed by Apex")
 		a.apisBackedByModuleFile = apisBackedbyOutputFile
diff --git a/apex/key.go b/apex/key.go
index e2695d7..259060f 100644
--- a/apex/key.go
+++ b/apex/key.go
@@ -123,13 +123,18 @@
 		containerCertificate string
 		containerPrivateKey  string
 		partition            string
+		signTool             string
 	}
 	toString := func(e apexKeyEntry) string {
-		format := "name=%q public_key=%q private_key=%q container_certificate=%q container_private_key=%q partition=%q\n"
+		signTool := ""
+		if e.signTool != "" {
+			signTool = fmt.Sprintf(" sign_tool=%q", e.signTool)
+		}
+		format := "name=%q public_key=%q private_key=%q container_certificate=%q container_private_key=%q partition=%q%s\n"
 		if e.presigned {
-			return fmt.Sprintf(format, e.name, "PRESIGNED", "PRESIGNED", "PRESIGNED", "PRESIGNED", e.partition)
+			return fmt.Sprintf(format, e.name, "PRESIGNED", "PRESIGNED", "PRESIGNED", "PRESIGNED", e.partition, signTool)
 		} else {
-			return fmt.Sprintf(format, e.name, e.publicKey, e.privateKey, e.containerCertificate, e.containerPrivateKey, e.partition)
+			return fmt.Sprintf(format, e.name, e.publicKey, e.privateKey, e.containerCertificate, e.containerPrivateKey, e.partition, signTool)
 		}
 	}
 
@@ -145,6 +150,7 @@
 				containerCertificate: pem.String(),
 				containerPrivateKey:  key.String(),
 				partition:            m.PartitionTag(ctx.DeviceConfig()),
+				signTool:             proptools.String(m.properties.Custom_sign_tool),
 			}
 		}
 	})
diff --git a/bazel/configurability.go b/bazel/configurability.go
index e9641e7..f05c8e5 100644
--- a/bazel/configurability.go
+++ b/bazel/configurability.go
@@ -39,6 +39,7 @@
 	osArchAndroidArm64      = "android_arm64"
 	osArchAndroidX86        = "android_x86"
 	osArchAndroidX86_64     = "android_x86_64"
+	osArchDarwinArm64       = "darwin_arm64"
 	osArchDarwinX86_64      = "darwin_x86_64"
 	osArchLinuxX86          = "linux_glibc_x86"
 	osArchLinuxX86_64       = "linux_glibc_x86_64"
@@ -96,6 +97,7 @@
 		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",
+		osArchDarwinArm64:          "//build/bazel/platforms/os_arch:darwin_arm64",
 		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",
diff --git a/bazel/properties.go b/bazel/properties.go
index 6a06c1b..facbedd 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -534,9 +534,13 @@
 			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)
+		// Now that the Value list is finalized for this axis, compare it with
+		// the original list, and union the difference with the default
+		// condition for the axis.
+		difference := SubtractBazelLabelList(baseLabels, lla.Value)
+		existingDefaults := lla.ConfigurableValues[axis][ConditionsDefaultConfigKey]
+		existingDefaults.Append(difference)
+		lla.ConfigurableValues[axis][ConditionsDefaultConfigKey] = FirstUniqueBazelLabelList(existingDefaults)
 
 		// if everything ends up without includes, just delete the axis
 		if !lla.ConfigurableValues[axis].HasConfigurableValues() {
diff --git a/bazel/properties_test.go b/bazel/properties_test.go
index f53fdc1..7a7d6f3 100644
--- a/bazel/properties_test.go
+++ b/bazel/properties_test.go
@@ -236,8 +236,9 @@
 		),
 		ConfigurableValues: configurableLabelLists{
 			ArchConfigurationAxis: labelListSelectValues{
-				"arm": makeLabelList([]string{}, []string{"arm_exclude"}),
-				"x86": makeLabelList([]string{"x86_include"}, []string{}),
+				"arm":                      makeLabelList([]string{}, []string{"arm_exclude"}),
+				"x86":                      makeLabelList([]string{"x86_include"}, []string{}),
+				ConditionsDefaultConfigKey: makeLabelList([]string{"default_include"}, []string{}),
 			},
 			OsConfigurationAxis: labelListSelectValues{
 				"android": makeLabelList([]string{}, []string{"android_exclude"}),
@@ -246,7 +247,13 @@
 			OsArchConfigurationAxis: labelListSelectValues{
 				"linux_x86": makeLabelList([]string{"linux_x86_include"}, []string{}),
 			},
-			ProductVariableConfigurationAxis("a"): labelListSelectValues{
+			ProductVariableConfigurationAxis("product_with_defaults"): labelListSelectValues{
+				"a":                        makeLabelList([]string{}, []string{"not_in_value"}),
+				"b":                        makeLabelList([]string{"b_val"}, []string{}),
+				"c":                        makeLabelList([]string{"c_val"}, []string{}),
+				ConditionsDefaultConfigKey: makeLabelList([]string{"c_val", "default", "default2"}, []string{}),
+			},
+			ProductVariableConfigurationAxis("product_only_with_excludes"): labelListSelectValues{
 				"a": makeLabelList([]string{}, []string{"not_in_value"}),
 			},
 		},
@@ -254,25 +261,31 @@
 
 	attr.ResolveExcludes()
 
-	expectedBaseIncludes := []Label{Label{Label: "all_include"}}
+	expectedBaseIncludes := []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"),
+		ArchConfigurationAxis: {
+			"arm":                      nilLabels,
+			"x86":                      makeLabels("arm_exclude", "x86_include"),
+			ConditionsDefaultConfigKey: makeLabels("arm_exclude", "default_include"),
 		},
-		OsConfigurationAxis: map[string][]Label{
-			"android":            nilLabels,
-			"linux":              makeLabels("android_exclude", "linux_include"),
-			"conditions_default": makeLabels("android_exclude"),
+		OsConfigurationAxis: {
+			"android":                  nilLabels,
+			"linux":                    makeLabels("android_exclude", "linux_include"),
+			ConditionsDefaultConfigKey: makeLabels("android_exclude"),
 		},
-		OsArchConfigurationAxis: map[string][]Label{
-			"linux_x86":          makeLabels("linux_x86_include"),
-			"conditions_default": nilLabels,
+		OsArchConfigurationAxis: {
+			"linux_x86":                makeLabels("linux_x86_include"),
+			ConditionsDefaultConfigKey: nilLabels,
+		},
+		ProductVariableConfigurationAxis("product_with_defaults"): {
+			"a":                        nilLabels,
+			"b":                        makeLabels("b_val"),
+			"c":                        makeLabels("c_val"),
+			ConditionsDefaultConfigKey: makeLabels("c_val", "default", "default2"),
 		},
 	}
 	for _, axis := range attr.SortedConfigurationAxes() {
@@ -288,7 +301,7 @@
 		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)
+					t.Errorf("For %s,\nexpected: %#v\ngot %#v", axis, expected, value.Includes)
 				}
 			} else {
 				t.Errorf("Got unexpected config %q for %s", config, axis)
diff --git a/bootstrap.bash b/bootstrap.bash
index 4db8539..726692a 100755
--- a/bootstrap.bash
+++ b/bootstrap.bash
@@ -15,9 +15,9 @@
 # limitations under the License.
 
 echo '==== ERROR: bootstrap.bash & ./soong are obsolete ====' >&2
-echo 'Use `m --skip-make` with a standalone OUT_DIR instead.' >&2
+echo 'Use `m --soong-only` with a standalone OUT_DIR instead.' >&2
 echo 'Without envsetup.sh, use:' >&2
-echo '  build/soong/soong_ui.bash --make-mode --skip-make' >&2
+echo '  build/soong/soong_ui.bash --make-mode --soong-only' >&2
 echo '======================================================' >&2
 exit 1
 
diff --git a/bp2build/bp2build.go b/bp2build/bp2build.go
index 45a3cb6..b0c3899 100644
--- a/bp2build/bp2build.go
+++ b/bp2build/bp2build.go
@@ -15,11 +15,12 @@
 package bp2build
 
 import (
-	"android/soong/android"
-	"android/soong/bazel"
 	"fmt"
 	"os"
 	"strings"
+
+	"android/soong/android"
+	"android/soong/bazel"
 )
 
 // Codegen is the backend of bp2build. The code generator is responsible for
@@ -43,7 +44,7 @@
 	writeFiles(ctx, bp2buildDir, bp2buildFiles)
 
 	soongInjectionDir := android.PathForOutput(ctx, bazel.SoongInjectionDirName)
-	writeFiles(ctx, soongInjectionDir, CreateSoongInjectionFiles(res.metrics))
+	writeFiles(ctx, soongInjectionDir, CreateSoongInjectionFiles(ctx.Config(), res.metrics))
 
 	return res.metrics
 }
diff --git a/bp2build/cc_binary_conversion_test.go b/bp2build/cc_binary_conversion_test.go
new file mode 100644
index 0000000..0b71d89
--- /dev/null
+++ b/bp2build/cc_binary_conversion_test.go
@@ -0,0 +1,435 @@
+// 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/cc"
+	"android/soong/genrule"
+	"fmt"
+	"strings"
+	"testing"
+)
+
+const (
+	ccBinaryTypePlaceHolder   = "{rule_name}"
+	compatibleWithPlaceHolder = "{target_compatible_with}"
+)
+
+func registerCcBinaryModuleTypes(ctx android.RegistrationContext) {
+	cc.RegisterCCBuildComponents(ctx)
+	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
+	ctx.RegisterModuleType("cc_library_static", cc.LibraryStaticFactory)
+	ctx.RegisterModuleType("cc_library", cc.LibraryFactory)
+	ctx.RegisterModuleType("genrule", genrule.GenRuleFactory)
+}
+
+var binaryReplacer = strings.NewReplacer(ccBinaryTypePlaceHolder, "cc_binary", compatibleWithPlaceHolder, "")
+var hostBinaryReplacer = strings.NewReplacer(ccBinaryTypePlaceHolder, "cc_binary_host", compatibleWithPlaceHolder, `
+    target_compatible_with = select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    }),`)
+
+func runCcBinaryTests(t *testing.T, tc bp2buildTestCase) {
+	t.Helper()
+	runCcBinaryTestCase(t, tc)
+	runCcHostBinaryTestCase(t, tc)
+}
+
+func runCcBinaryTestCase(t *testing.T, tc bp2buildTestCase) {
+	t.Helper()
+	testCase := tc
+	testCase.expectedBazelTargets = append([]string{}, tc.expectedBazelTargets...)
+	testCase.moduleTypeUnderTest = "cc_binary"
+	testCase.moduleTypeUnderTestFactory = cc.BinaryFactory
+	testCase.moduleTypeUnderTestBp2BuildMutator = cc.BinaryBp2build
+	testCase.description = fmt.Sprintf("%s %s", testCase.moduleTypeUnderTest, testCase.description)
+	testCase.blueprint = binaryReplacer.Replace(testCase.blueprint)
+	for i, et := range testCase.expectedBazelTargets {
+		testCase.expectedBazelTargets[i] = binaryReplacer.Replace(et)
+	}
+	t.Run(testCase.description, func(t *testing.T) {
+		runBp2BuildTestCase(t, registerCcBinaryModuleTypes, testCase)
+	})
+}
+
+func runCcHostBinaryTestCase(t *testing.T, tc bp2buildTestCase) {
+	t.Helper()
+	testCase := tc
+	testCase.expectedBazelTargets = append([]string{}, tc.expectedBazelTargets...)
+	testCase.moduleTypeUnderTest = "cc_binary_host"
+	testCase.moduleTypeUnderTestFactory = cc.BinaryHostFactory
+	testCase.moduleTypeUnderTestBp2BuildMutator = cc.BinaryHostBp2build
+	testCase.description = fmt.Sprintf("%s %s", testCase.moduleTypeUnderTest, testCase.description)
+	testCase.blueprint = hostBinaryReplacer.Replace(testCase.blueprint)
+	for i, et := range testCase.expectedBazelTargets {
+		testCase.expectedBazelTargets[i] = hostBinaryReplacer.Replace(et)
+	}
+	t.Run(testCase.description, func(t *testing.T) {
+		runBp2BuildTestCase(t, registerCcBinaryModuleTypes, testCase)
+	})
+}
+
+func TestBasicCcBinary(t *testing.T) {
+	runCcBinaryTests(t, bp2buildTestCase{
+		description: "basic -- properties -> attrs with little/no transformation",
+		blueprint: `
+{rule_name} {
+    name: "foo",
+    srcs: ["a.cc"],
+    local_include_dirs: ["dir"],
+    include_dirs: ["absolute_dir"],
+    cflags: ["-Dcopt"],
+    cppflags: ["-Dcppflag"],
+    conlyflags: ["-Dconlyflag"],
+    asflags: ["-Dasflag"],
+    ldflags: ["ld-flag"],
+    rtti: true,
+    strip: {
+        all: true,
+        keep_symbols: true,
+        keep_symbols_and_debug_frame: true,
+        keep_symbols_list: ["symbol"],
+        none: true,
+    },
+}
+`,
+		expectedBazelTargets: []string{`cc_binary(
+    name = "foo",
+    absolute_includes = ["absolute_dir"],
+    asflags = ["-Dasflag"],
+    conlyflags = ["-Dconlyflag"],
+    copts = ["-Dcopt"],
+    cppflags = ["-Dcppflag"],
+    linkopts = ["ld-flag"],
+    local_includes = [
+        "dir",
+        ".",
+    ],
+    rtti = True,
+    srcs = ["a.cc"],
+    strip = {
+        "all": True,
+        "keep_symbols": True,
+        "keep_symbols_and_debug_frame": True,
+        "keep_symbols_list": ["symbol"],
+        "none": True,
+    },{target_compatible_with}
+)`},
+	})
+}
+
+func TestCcBinaryWithSharedLdflagDisableFeature(t *testing.T) {
+	runCcBinaryTests(t, bp2buildTestCase{
+		description: `ldflag "-shared" disables static_flag feature`,
+		blueprint: `
+{rule_name} {
+    name: "foo",
+    ldflags: ["-shared"],
+    include_build_directory: false,
+}
+`,
+		expectedBazelTargets: []string{`cc_binary(
+    name = "foo",
+    features = ["-static_flag"],
+    linkopts = ["-shared"],{target_compatible_with}
+)`},
+	})
+}
+
+func TestCcBinaryWithLinkStatic(t *testing.T) {
+	runCcBinaryTests(t, bp2buildTestCase{
+		description: "link static",
+		blueprint: `
+{rule_name} {
+    name: "foo",
+    static_executable: true,
+    include_build_directory: false,
+}
+`,
+		expectedBazelTargets: []string{`cc_binary(
+    name = "foo",
+    linkshared = False,{target_compatible_with}
+)`},
+	})
+}
+
+func TestCcBinaryVersionScript(t *testing.T) {
+	runCcBinaryTests(t, bp2buildTestCase{
+		description: `version script`,
+		blueprint: `
+{rule_name} {
+    name: "foo",
+    include_build_directory: false,
+    version_script: "vs",
+}
+`,
+		expectedBazelTargets: []string{`cc_binary(
+    name = "foo",
+    additional_linker_inputs = ["vs"],
+    linkopts = ["-Wl,--version-script,$(location vs)"],{target_compatible_with}
+)`},
+	})
+}
+
+func TestCcBinarySplitSrcsByLang(t *testing.T) {
+	runCcHostBinaryTestCase(t, bp2buildTestCase{
+		description: "split srcs by lang",
+		blueprint: `
+{rule_name} {
+    name: "foo",
+    srcs: [
+        "asonly.S",
+        "conly.c",
+        "cpponly.cpp",
+        ":fg_foo",
+    ],
+    include_build_directory: false,
+}
+` + simpleModuleDoNotConvertBp2build("filegroup", "fg_foo"),
+		expectedBazelTargets: []string{`cc_binary(
+    name = "foo",
+    srcs = [
+        "cpponly.cpp",
+        ":fg_foo_cpp_srcs",
+    ],
+    srcs_as = [
+        "asonly.S",
+        ":fg_foo_as_srcs",
+    ],
+    srcs_c = [
+        "conly.c",
+        ":fg_foo_c_srcs",
+    ],{target_compatible_with}
+)`},
+	})
+}
+
+func TestCcBinaryDoNotDistinguishBetweenDepsAndImplementationDeps(t *testing.T) {
+	runCcBinaryTestCase(t, bp2buildTestCase{
+		description: "no implementation deps",
+		blueprint: `
+genrule {
+    name: "generated_hdr",
+    cmd: "nothing to see here",
+}
+
+genrule {
+    name: "export_generated_hdr",
+    cmd: "nothing to see here",
+}
+
+{rule_name} {
+    name: "foo",
+    srcs: ["foo.cpp"],
+    shared_libs: ["implementation_shared_dep", "shared_dep"],
+    export_shared_lib_headers: ["shared_dep"],
+    static_libs: ["implementation_static_dep", "static_dep"],
+    export_static_lib_headers: ["static_dep", "whole_static_dep"],
+    whole_static_libs: ["not_explicitly_exported_whole_static_dep", "whole_static_dep"],
+    include_build_directory: false,
+    generated_headers: ["generated_hdr", "export_generated_hdr"],
+    export_generated_headers: ["export_generated_hdr"],
+}
+` +
+			simpleModuleDoNotConvertBp2build("cc_library_static", "static_dep") +
+			simpleModuleDoNotConvertBp2build("cc_library_static", "implementation_static_dep") +
+			simpleModuleDoNotConvertBp2build("cc_library_static", "whole_static_dep") +
+			simpleModuleDoNotConvertBp2build("cc_library_static", "not_explicitly_exported_whole_static_dep") +
+			simpleModuleDoNotConvertBp2build("cc_library", "shared_dep") +
+			simpleModuleDoNotConvertBp2build("cc_library", "implementation_shared_dep"),
+		expectedBazelTargets: []string{`cc_binary(
+    name = "foo",
+    deps = [
+        ":implementation_static_dep",
+        ":static_dep",
+    ],
+    dynamic_deps = [
+        ":implementation_shared_dep",
+        ":shared_dep",
+    ],
+    srcs = [
+        "foo.cpp",
+        ":generated_hdr",
+        ":export_generated_hdr",
+    ],{target_compatible_with}
+    whole_archive_deps = [
+        ":not_explicitly_exported_whole_static_dep",
+        ":whole_static_dep",
+    ],
+)`},
+	})
+}
+
+func TestCcBinaryNocrtTests(t *testing.T) {
+	baseTestCases := []struct {
+		description   string
+		soongProperty string
+		bazelAttr     string
+	}{
+		{
+			description:   "nocrt: true",
+			soongProperty: `nocrt: true,`,
+			bazelAttr:     `    link_crt = False,`,
+		},
+		{
+			description:   "nocrt: false",
+			soongProperty: `nocrt: false,`,
+		},
+		{
+			description: "nocrt: not set",
+		},
+	}
+
+	baseBlueprint := `{rule_name} {
+    name: "foo",%s
+    include_build_directory: false,
+}
+`
+
+	baseBazelTarget := `cc_binary(
+    name = "foo",%s{target_compatible_with}
+)`
+
+	for _, btc := range baseTestCases {
+		prop := btc.soongProperty
+		if len(prop) > 0 {
+			prop = "\n" + prop
+		}
+		attr := btc.bazelAttr
+		if len(attr) > 0 {
+			attr = "\n" + attr
+		}
+		runCcBinaryTests(t, bp2buildTestCase{
+			description: btc.description,
+			blueprint:   fmt.Sprintf(baseBlueprint, prop),
+			expectedBazelTargets: []string{
+				fmt.Sprintf(baseBazelTarget, attr),
+			},
+		})
+	}
+}
+
+func TestCcBinaryNo_libcrtTests(t *testing.T) {
+	baseTestCases := []struct {
+		description   string
+		soongProperty string
+		bazelAttr     string
+	}{
+		{
+			description:   "no_libcrt: true",
+			soongProperty: `no_libcrt: true,`,
+			bazelAttr:     `    use_libcrt = False,`,
+		},
+		{
+			description:   "no_libcrt: false",
+			soongProperty: `no_libcrt: false,`,
+			bazelAttr:     `    use_libcrt = True,`,
+		},
+		{
+			description: "no_libcrt: not set",
+		},
+	}
+
+	baseBlueprint := `{rule_name} {
+    name: "foo",%s
+    include_build_directory: false,
+}
+`
+
+	baseBazelTarget := `cc_binary(
+    name = "foo",{target_compatible_with}%s
+)`
+
+	for _, btc := range baseTestCases {
+		prop := btc.soongProperty
+		if len(prop) > 0 {
+			prop = "\n" + prop
+		}
+		attr := btc.bazelAttr
+		if len(attr) > 0 {
+			attr = "\n" + attr
+		}
+		runCcBinaryTests(t, bp2buildTestCase{
+			description: btc.description,
+			blueprint:   fmt.Sprintf(baseBlueprint, prop),
+			expectedBazelTargets: []string{
+				fmt.Sprintf(baseBazelTarget, attr),
+			},
+		})
+	}
+}
+
+func TestCcBinaryPropertiesToFeatures(t *testing.T) {
+	baseTestCases := []struct {
+		description   string
+		soongProperty string
+		bazelAttr     string
+	}{
+		{
+			description:   "pack_relocation: true",
+			soongProperty: `pack_relocations: true,`,
+		},
+		{
+			description:   "pack_relocations: false",
+			soongProperty: `pack_relocations: false,`,
+			bazelAttr:     `    features = ["disable_pack_relocations"],`,
+		},
+		{
+			description: "pack_relocations: not set",
+		},
+		{
+			description:   "pack_relocation: true",
+			soongProperty: `allow_undefined_symbols: true,`,
+			bazelAttr:     `    features = ["-no_undefined_symbols"],`,
+		},
+		{
+			description:   "allow_undefined_symbols: false",
+			soongProperty: `allow_undefined_symbols: false,`,
+		},
+		{
+			description: "allow_undefined_symbols: not set",
+		},
+	}
+
+	baseBlueprint := `{rule_name} {
+    name: "foo",%s
+    include_build_directory: false,
+}
+`
+
+	baseBazelTarget := `cc_binary(
+    name = "foo",%s{target_compatible_with}
+)`
+
+	for _, btc := range baseTestCases {
+		prop := btc.soongProperty
+		if len(prop) > 0 {
+			prop = "\n" + prop
+		}
+		attr := btc.bazelAttr
+		if len(attr) > 0 {
+			attr = "\n" + attr
+		}
+		runCcBinaryTests(t, bp2buildTestCase{
+			description: btc.description,
+			blueprint:   fmt.Sprintf(baseBlueprint, prop),
+			expectedBazelTargets: []string{
+				fmt.Sprintf(baseBazelTarget, attr),
+			},
+		})
+	}
+}
diff --git a/bp2build/cc_library_static_conversion_test.go b/bp2build/cc_library_static_conversion_test.go
index 9f6f450..9887811 100644
--- a/bp2build/cc_library_static_conversion_test.go
+++ b/bp2build/cc_library_static_conversion_test.go
@@ -986,6 +986,48 @@
 	})
 }
 
+func TestCcLibraryStaticGeneratedHeadersAllPartitions(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		moduleTypeUnderTest:                "cc_library_static",
+		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		blueprint: soongCcLibraryStaticPreamble + `
+genrule {
+    name: "generated_hdr",
+    cmd: "nothing to see here",
+}
+
+genrule {
+    name: "export_generated_hdr",
+    cmd: "nothing to see here",
+}
+
+cc_library_static {
+    name: "foo_static",
+    srcs: ["cpp_src.cpp", "as_src.S", "c_src.c"],
+    generated_headers: ["generated_hdr", "export_generated_hdr"],
+    export_generated_headers: ["export_generated_hdr"],
+    include_build_directory: false,
+}`,
+		expectedBazelTargets: []string{`cc_library_static(
+    name = "foo_static",
+    hdrs = [":export_generated_hdr"],
+    srcs = [
+        "cpp_src.cpp",
+        ":generated_hdr",
+    ],
+    srcs_as = [
+        "as_src.S",
+        ":generated_hdr",
+    ],
+    srcs_c = [
+        "c_src.c",
+        ":generated_hdr",
+    ],
+)`},
+	})
+}
+
 func TestCcLibraryStaticArchSrcsExcludeSrcsGeneratedFiles(t *testing.T) {
 	runCcLibraryStaticTestCase(t, bp2buildTestCase{
 		description:                        "cc_library_static arch srcs/exclude_srcs with generated files",
@@ -1067,10 +1109,10 @@
     name = "foo_static3",
     srcs = [
         "common.cpp",
-        ":generated_hdr",
-        "//dep:generated_hdr_other_pkg",
         ":generated_src",
         "//dep:generated_src_other_pkg",
+        ":generated_hdr",
+        "//dep:generated_hdr_other_pkg",
     ] + select({
         "//build/bazel/platforms/arch:x86": [
             "for-x86.cpp",
@@ -1082,8 +1124,8 @@
         ],
     }) + select({
         "//build/bazel/platforms/os:android": [
-            "//dep:generated_hdr_other_pkg_android",
             ":generated_src_android",
+            "//dep:generated_hdr_other_pkg_android",
         ],
         "//conditions:default": [],
     }),
diff --git a/bp2build/cc_prebuilt_library_shared_test.go b/bp2build/cc_prebuilt_library_shared_test.go
new file mode 100644
index 0000000..97545c8
--- /dev/null
+++ b/bp2build/cc_prebuilt_library_shared_test.go
@@ -0,0 +1,65 @@
+package bp2build
+
+import (
+	"testing"
+
+	"android/soong/cc"
+)
+
+func TestSharedPrebuiltLibrary(t *testing.T) {
+	runBp2BuildTestCaseSimple(t,
+		bp2buildTestCase{
+			description:                        "prebuilt library shared simple",
+			moduleTypeUnderTest:                "cc_prebuilt_library_shared",
+			moduleTypeUnderTestFactory:         cc.PrebuiltSharedLibraryFactory,
+			moduleTypeUnderTestBp2BuildMutator: cc.PrebuiltLibrarySharedBp2Build,
+			filesystem: map[string]string{
+				"libf.so": "",
+			},
+			blueprint: `
+cc_prebuilt_library_shared {
+	name: "libtest",
+	srcs: ["libf.so"],
+	bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{
+				`prebuilt_library_shared(
+    name = "libtest",
+    shared_library = "libf.so",
+)`,
+			},
+		})
+}
+
+func TestSharedPrebuiltLibraryWithArchVariance(t *testing.T) {
+	runBp2BuildTestCaseSimple(t,
+		bp2buildTestCase{
+			description:                        "prebuilt library shared with arch variance",
+			moduleTypeUnderTest:                "cc_prebuilt_library_shared",
+			moduleTypeUnderTestFactory:         cc.PrebuiltSharedLibraryFactory,
+			moduleTypeUnderTestBp2BuildMutator: cc.PrebuiltLibrarySharedBp2Build,
+			filesystem: map[string]string{
+				"libf.so": "",
+				"libg.so": "",
+			},
+			blueprint: `
+cc_prebuilt_library_shared {
+	name: "libtest",
+	arch: {
+		arm64: { srcs: ["libf.so"], },
+		arm: { srcs: ["libg.so"], },
+	},
+	bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{
+				`prebuilt_library_shared(
+    name = "libtest",
+    shared_library = select({
+        "//build/bazel/platforms/arch:arm": "libg.so",
+        "//build/bazel/platforms/arch:arm64": "libf.so",
+        "//conditions:default": None,
+    }),
+)`,
+			},
+		})
+}
diff --git a/bp2build/conversion.go b/bp2build/conversion.go
index d34a4ba..944cb83 100644
--- a/bp2build/conversion.go
+++ b/bp2build/conversion.go
@@ -17,11 +17,11 @@
 	Contents string
 }
 
-func CreateSoongInjectionFiles(metrics CodegenMetrics) []BazelFile {
+func CreateSoongInjectionFiles(cfg android.Config, metrics CodegenMetrics) []BazelFile {
 	var files []BazelFile
 
 	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("cc_toolchain", "constants.bzl", config.BazelCcToolchainVars(cfg)))
 
 	files = append(files, newFile("metrics", "converted_modules.txt", strings.Join(metrics.convertedModules, "\n")))
 
diff --git a/bp2build/conversion_test.go b/bp2build/conversion_test.go
index dfa1a9e..d09238a 100644
--- a/bp2build/conversion_test.go
+++ b/bp2build/conversion_test.go
@@ -17,6 +17,8 @@
 import (
 	"sort"
 	"testing"
+
+	"android/soong/android"
 )
 
 type bazelFilepath struct {
@@ -80,7 +82,7 @@
 }
 
 func TestCreateBazelFiles_Bp2Build_CreatesDefaultFiles(t *testing.T) {
-	files := CreateSoongInjectionFiles(CodegenMetrics{})
+	files := CreateSoongInjectionFiles(android.Config{}, CodegenMetrics{})
 
 	expectedFilePaths := []bazelFilepath{
 		{
diff --git a/cc/binary.go b/cc/binary.go
index b423c50..a5afb07 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -20,6 +20,7 @@
 	"github.com/google/blueprint"
 
 	"android/soong/android"
+	"android/soong/bazel"
 )
 
 type BinaryLinkerProperties struct {
@@ -62,7 +63,7 @@
 
 func RegisterBinaryBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("cc_binary", BinaryFactory)
-	ctx.RegisterModuleType("cc_binary_host", binaryHostFactory)
+	ctx.RegisterModuleType("cc_binary_host", BinaryHostFactory)
 }
 
 // cc_binary produces a binary that is runnable on a device.
@@ -72,7 +73,7 @@
 }
 
 // cc_binary_host produces a binary that is runnable on a host.
-func binaryHostFactory() android.Module {
+func BinaryHostFactory() android.Module {
 	module, _ := NewBinary(android.HostSupported)
 	return module.Init()
 }
@@ -412,7 +413,7 @@
 		linkerDeps = append(linkerDeps, deps.LateSharedLibsDeps...)
 	}
 
-	linkerDeps = append(linkerDeps, objs.tidyFiles...)
+	validations = append(validations, objs.tidyFiles...)
 	linkerDeps = append(linkerDeps, flags.LdFlagsDeps...)
 
 	// Register link action.
@@ -541,3 +542,124 @@
 		},
 	})
 }
+
+func init() {
+	android.RegisterBp2BuildMutator("cc_binary", BinaryBp2build)
+	android.RegisterBp2BuildMutator("cc_binary_host", BinaryHostBp2build)
+}
+
+func BinaryBp2build(ctx android.TopDownMutatorContext) {
+	binaryBp2build(ctx, "cc_binary")
+}
+
+func BinaryHostBp2build(ctx android.TopDownMutatorContext) {
+	binaryBp2build(ctx, "cc_binary_host")
+}
+
+func binaryBp2build(ctx android.TopDownMutatorContext, typ string) {
+	m, ok := ctx.Module().(*Module)
+	if !ok {
+		// Not a cc module
+		return
+	}
+	if !m.ConvertWithBp2build(ctx) {
+		return
+	}
+
+	if ctx.ModuleType() != typ {
+		return
+	}
+
+	var compatibleWith bazel.StringListAttribute
+	if typ == "cc_binary_host" {
+		//incompatible with android OS
+		compatibleWith.SetSelectValue(bazel.OsConfigurationAxis, android.Android.Name, []string{"@platforms//:incompatible"})
+		compatibleWith.SetSelectValue(bazel.OsConfigurationAxis, bazel.ConditionsDefaultConfigKey, []string{})
+	}
+
+	baseAttrs := bp2BuildParseBaseProps(ctx, m)
+
+	attrs := &binaryAttributes{
+		binaryLinkerAttrs: bp2buildBinaryLinkerProps(ctx, m),
+
+		Srcs:    baseAttrs.srcs,
+		Srcs_c:  baseAttrs.cSrcs,
+		Srcs_as: baseAttrs.asSrcs,
+
+		Copts:      baseAttrs.copts,
+		Cppflags:   baseAttrs.cppFlags,
+		Conlyflags: baseAttrs.conlyFlags,
+		Asflags:    baseAttrs.asFlags,
+
+		Deps:               baseAttrs.implementationDeps,
+		Dynamic_deps:       baseAttrs.implementationDynamicDeps,
+		Whole_archive_deps: baseAttrs.wholeArchiveDeps,
+		System_deps:        baseAttrs.systemDynamicDeps,
+
+		Local_includes:    baseAttrs.localIncludes,
+		Absolute_includes: baseAttrs.absoluteIncludes,
+		Linkopts:          baseAttrs.linkopts,
+		Link_crt:          baseAttrs.linkCrt,
+		Use_libcrt:        baseAttrs.useLibcrt,
+		Rtti:              baseAttrs.rtti,
+		Stl:               baseAttrs.stl,
+		Cpp_std:           baseAttrs.cppStd,
+
+		Additional_linker_inputs: baseAttrs.additionalLinkerInputs,
+
+		Strip: stripAttributes{
+			Keep_symbols:                 baseAttrs.stripKeepSymbols,
+			Keep_symbols_and_debug_frame: baseAttrs.stripKeepSymbolsAndDebugFrame,
+			Keep_symbols_list:            baseAttrs.stripKeepSymbolsList,
+			All:                          baseAttrs.stripAll,
+			None:                         baseAttrs.stripNone,
+		},
+
+		Target_compatible_with: compatibleWith,
+		Features:               baseAttrs.features,
+	}
+
+	ctx.CreateBazelTargetModule(bazel.BazelTargetModuleProperties{
+		Rule_class:        "cc_binary",
+		Bzl_load_location: "//build/bazel/rules:cc_binary.bzl",
+	},
+		android.CommonAttributes{Name: m.Name()},
+		attrs)
+}
+
+// binaryAttributes contains Bazel attributes corresponding to a cc binary
+type binaryAttributes struct {
+	binaryLinkerAttrs
+	Srcs    bazel.LabelListAttribute
+	Srcs_c  bazel.LabelListAttribute
+	Srcs_as bazel.LabelListAttribute
+
+	Copts      bazel.StringListAttribute
+	Cppflags   bazel.StringListAttribute
+	Conlyflags bazel.StringListAttribute
+	Asflags    bazel.StringListAttribute
+
+	Deps               bazel.LabelListAttribute
+	Dynamic_deps       bazel.LabelListAttribute
+	Whole_archive_deps bazel.LabelListAttribute
+	System_deps        bazel.LabelListAttribute
+
+	Local_includes    bazel.StringListAttribute
+	Absolute_includes bazel.StringListAttribute
+
+	Linkopts                 bazel.StringListAttribute
+	Additional_linker_inputs bazel.LabelListAttribute
+
+	Link_crt   bazel.BoolAttribute
+	Use_libcrt bazel.BoolAttribute
+
+	Rtti    bazel.BoolAttribute
+	Stl     *string
+	Cpp_std *string
+
+	Strip stripAttributes
+
+	Features bazel.StringListAttribute
+
+	Target_compatible_with bazel.StringListAttribute
+}
diff --git a/cc/bp2build.go b/cc/bp2build.go
index c078096..1b13854 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -26,12 +26,19 @@
 	"github.com/google/blueprint/proptools"
 )
 
+const (
+	cSrcPartition   = "c"
+	asSrcPartition  = "as"
+	cppSrcPartition = "cpp"
+)
+
 // 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
+	Hdrs    bazel.LabelListAttribute
 	Copts   bazel.StringListAttribute
 
 	Deps                        bazel.LabelListAttribute
@@ -43,7 +50,7 @@
 	System_dynamic_deps bazel.LabelListAttribute
 }
 
-func groupSrcsByExtension(ctx android.TopDownMutatorContext, srcs bazel.LabelListAttribute) (cppSrcs, cSrcs, asSrcs bazel.LabelListAttribute) {
+func groupSrcsByExtension(ctx android.BazelConversionPathContext, srcs bazel.LabelListAttribute) bazel.PartitionToLabelListAttribute {
 	// Check that a module is a filegroup type named <label>.
 	isFilegroupNamed := func(m android.Module, fullLabel string) bool {
 		if ctx.OtherModuleType(m) != "filegroup" {
@@ -75,21 +82,18 @@
 
 	// TODO(b/190006308): Handle language detection of sources in a Bazel rule.
 	partitioned := bazel.PartitionLabelListAttribute(ctx, &srcs, bazel.LabelPartitions{
-		"c":  bazel.LabelPartition{Extensions: []string{".c"}, LabelMapper: addSuffixForFilegroup("_c_srcs")},
-		"as": bazel.LabelPartition{Extensions: []string{".s", ".S"}, LabelMapper: addSuffixForFilegroup("_as_srcs")},
+		cSrcPartition:  bazel.LabelPartition{Extensions: []string{".c"}, LabelMapper: addSuffixForFilegroup("_c_srcs")},
+		asSrcPartition: bazel.LabelPartition{Extensions: []string{".s", ".S"}, LabelMapper: addSuffixForFilegroup("_as_srcs")},
 		// 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.
-		"cpp": bazel.LabelPartition{Extensions: []string{".cpp", ".cc", ".cxx", ".mm"}, LabelMapper: addSuffixForFilegroup("_cpp_srcs"), Keep_remainder: true},
+		cppSrcPartition: bazel.LabelPartition{Extensions: []string{".cpp", ".cc", ".cxx", ".mm"}, LabelMapper: addSuffixForFilegroup("_cpp_srcs"), Keep_remainder: true},
 	})
 
-	cSrcs = partitioned["c"]
-	asSrcs = partitioned["as"]
-	cppSrcs = partitioned["cpp"]
-	return
+	return partitioned
 }
 
 // bp2BuildParseLibProps returns the attributes for a variant of a cc_library.
-func bp2BuildParseLibProps(ctx android.TopDownMutatorContext, module *Module, isStatic bool) staticOrSharedAttributes {
+func bp2BuildParseLibProps(ctx android.BazelConversionPathContext, module *Module, isStatic bool) staticOrSharedAttributes {
 	lib, ok := module.compiler.(*libraryDecorator)
 	if !ok {
 		return staticOrSharedAttributes{}
@@ -98,12 +102,12 @@
 }
 
 // bp2buildParseSharedProps returns the attributes for the shared variant of a cc_library.
-func bp2BuildParseSharedProps(ctx android.TopDownMutatorContext, module *Module) staticOrSharedAttributes {
+func bp2BuildParseSharedProps(ctx android.BazelConversionPathContext, module *Module) staticOrSharedAttributes {
 	return bp2BuildParseLibProps(ctx, module, false)
 }
 
 // bp2buildParseStaticProps returns the attributes for the static variant of a cc_library.
-func bp2BuildParseStaticProps(ctx android.TopDownMutatorContext, module *Module) staticOrSharedAttributes {
+func bp2BuildParseStaticProps(ctx android.BazelConversionPathContext, module *Module) staticOrSharedAttributes {
 	return bp2BuildParseLibProps(ctx, module, true)
 }
 
@@ -112,9 +116,15 @@
 	implementation bazel.LabelList
 }
 
-type bazelLabelForDepsFn func(android.TopDownMutatorContext, []string) bazel.LabelList
+type bazelLabelForDepsFn func(android.BazelConversionPathContext, []string) bazel.LabelList
 
-func partitionExportedAndImplementationsDeps(ctx android.TopDownMutatorContext, allDeps, exportedDeps []string, fn bazelLabelForDepsFn) depsPartition {
+func maybePartitionExportedAndImplementationsDeps(ctx android.BazelConversionPathContext, exportsDeps bool, allDeps, exportedDeps []string, fn bazelLabelForDepsFn) depsPartition {
+	if !exportsDeps {
+		return depsPartition{
+			implementation: fn(ctx, allDeps),
+		}
+	}
+
 	implementation, export := android.FilterList(allDeps, exportedDeps)
 
 	return depsPartition{
@@ -123,9 +133,14 @@
 	}
 }
 
-type bazelLabelForDepsExcludesFn func(android.TopDownMutatorContext, []string, []string) bazel.LabelList
+type bazelLabelForDepsExcludesFn func(android.BazelConversionPathContext, []string, []string) bazel.LabelList
 
-func partitionExportedAndImplementationsDepsExcludes(ctx android.TopDownMutatorContext, allDeps, excludes, exportedDeps []string, fn bazelLabelForDepsExcludesFn) depsPartition {
+func maybePartitionExportedAndImplementationsDepsExcludes(ctx android.BazelConversionPathContext, exportsDeps bool, allDeps, excludes, exportedDeps []string, fn bazelLabelForDepsExcludesFn) depsPartition {
+	if !exportsDeps {
+		return depsPartition{
+			implementation: fn(ctx, allDeps, excludes),
+		}
+	}
 	implementation, export := android.FilterList(allDeps, exportedDeps)
 
 	return depsPartition{
@@ -134,7 +149,7 @@
 	}
 }
 
-func bp2buildParseStaticOrSharedProps(ctx android.TopDownMutatorContext, module *Module, lib *libraryDecorator, isStatic bool) staticOrSharedAttributes {
+func bp2buildParseStaticOrSharedProps(ctx android.BazelConversionPathContext, module *Module, lib *libraryDecorator, isStatic bool) staticOrSharedAttributes {
 	attrs := staticOrSharedAttributes{}
 
 	setAttrs := func(axis bazel.ConfigurationAxis, config string, props StaticOrSharedProperties) {
@@ -142,11 +157,11 @@
 		attrs.Srcs.SetSelectValue(axis, config, android.BazelLabelForModuleSrc(ctx, props.Srcs))
 		attrs.System_dynamic_deps.SetSelectValue(axis, config, bazelLabelForSharedDeps(ctx, props.System_shared_libs))
 
-		staticDeps := partitionExportedAndImplementationsDeps(ctx, props.Static_libs, props.Export_static_lib_headers, bazelLabelForStaticDeps)
+		staticDeps := maybePartitionExportedAndImplementationsDeps(ctx, true, props.Static_libs, props.Export_static_lib_headers, bazelLabelForStaticDeps)
 		attrs.Deps.SetSelectValue(axis, config, staticDeps.export)
 		attrs.Implementation_deps.SetSelectValue(axis, config, staticDeps.implementation)
 
-		sharedDeps := partitionExportedAndImplementationsDeps(ctx, props.Shared_libs, props.Export_shared_lib_headers, bazelLabelForSharedDeps)
+		sharedDeps := maybePartitionExportedAndImplementationsDeps(ctx, true, props.Shared_libs, props.Export_shared_lib_headers, bazelLabelForSharedDeps)
 		attrs.Dynamic_deps.SetSelectValue(axis, config, sharedDeps.export)
 		attrs.Implementation_dynamic_deps.SetSelectValue(axis, config, sharedDeps.implementation)
 
@@ -175,10 +190,10 @@
 		}
 	}
 
-	cppSrcs, cSrcs, asSrcs := groupSrcsByExtension(ctx, attrs.Srcs)
-	attrs.Srcs = cppSrcs
-	attrs.Srcs_c = cSrcs
-	attrs.Srcs_as = asSrcs
+	partitionedSrcs := groupSrcsByExtension(ctx, attrs.Srcs)
+	attrs.Srcs = partitionedSrcs[cppSrcPartition]
+	attrs.Srcs_c = partitionedSrcs[cSrcPartition]
+	attrs.Srcs_as = partitionedSrcs[asSrcPartition]
 
 	return attrs
 }
@@ -189,7 +204,7 @@
 }
 
 // NOTE: Used outside of Soong repo project, in the clangprebuilts.go bootstrap_go_package
-func Bp2BuildParsePrebuiltLibraryProps(ctx android.TopDownMutatorContext, module *Module) prebuiltAttributes {
+func Bp2BuildParsePrebuiltLibraryProps(ctx android.BazelConversionPathContext, module *Module) prebuiltAttributes {
 	var srcLabelAttribute bazel.LabelAttribute
 
 	for axis, configToProps := range module.GetArchVariantProperties(ctx, &prebuiltLinkerProperties{}) {
@@ -212,6 +227,11 @@
 	}
 }
 
+type baseAttributes struct {
+	compilerAttributes
+	linkerAttributes
+}
+
 // Convenience struct to hold all attributes parsed from compiler properties.
 type compilerAttributes struct {
 	// Options for all languages
@@ -226,6 +246,8 @@
 	cppFlags bazel.StringListAttribute
 	srcs     bazel.LabelListAttribute
 
+	hdrs bazel.LabelListAttribute
+
 	rtti bazel.BoolAttribute
 
 	// Not affected by arch variants
@@ -236,108 +258,67 @@
 	absoluteIncludes bazel.StringListAttribute
 }
 
-// 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
-	var localIncludes bazel.StringListAttribute
-	var absoluteIncludes bazel.StringListAttribute
-	var stl *string = nil
-	var cppStd *string = nil
+func parseCommandLineFlags(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.
+		result = append(result, strings.Split(flag, " ")...)
+	}
+	return result
+}
 
-	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.
-			result = append(result, strings.Split(flag, " ")...)
-		}
-		return result
+func (ca *compilerAttributes) bp2buildForAxisAndConfig(ctx android.BazelConversionPathContext, axis bazel.ConfigurationAxis, config string, props *BaseCompilerProperties) {
+	// 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 srcsList, ok := parseSrcs(ctx, props); ok {
+		ca.srcs.SetSelectValue(axis, config, srcsList)
 	}
 
-	// Parse srcs from an arch or OS's props value.
-	parseSrcs := func(props *BaseCompilerProperties) (bazel.LabelList, bool) {
-		anySrcs := false
-		// Add srcs-like dependencies such as generated files.
-		// First create a LabelList containing these dependencies, then merge the values with srcs.
-		generatedHdrsAndSrcs := props.Generated_headers
-		generatedHdrsAndSrcs = append(generatedHdrsAndSrcs, props.Generated_sources...)
-		generatedHdrsAndSrcsLabelList := android.BazelLabelForModuleDepsExcludes(ctx, generatedHdrsAndSrcs, props.Exclude_generated_sources)
-		if len(generatedHdrsAndSrcs) > 0 || len(props.Exclude_generated_sources) > 0 {
-			anySrcs = true
-		}
+	localIncludeDirs := props.Local_include_dirs
+	if axis == bazel.NoConfigAxis {
+		ca.cppStd = bp2buildResolveCppStdValue(props.Cpp_std, props.Gnu_extensions)
 
-		allSrcsLabelList := android.BazelLabelForModuleSrcExcludes(ctx, props.Srcs, props.Exclude_srcs)
-		if len(props.Srcs) > 0 || len(props.Exclude_srcs) > 0 {
-			anySrcs = true
+		if includeBuildDirectory(props.Include_build_directory) {
+			localIncludeDirs = append(localIncludeDirs, ".")
 		}
-		return bazel.AppendBazelLabelLists(allSrcsLabelList, generatedHdrsAndSrcsLabelList), anySrcs
 	}
 
-	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 srcsList, ok := parseSrcs(baseCompilerProps); ok {
-					srcs.SetSelectValue(axis, config, srcsList)
+	ca.absoluteIncludes.SetSelectValue(axis, config, props.Include_dirs)
+	ca.localIncludes.SetSelectValue(axis, config, localIncludeDirs)
+
+	ca.copts.SetSelectValue(axis, config, parseCommandLineFlags(props.Cflags))
+	ca.asFlags.SetSelectValue(axis, config, parseCommandLineFlags(props.Asflags))
+	ca.conlyFlags.SetSelectValue(axis, config, parseCommandLineFlags(props.Conlyflags))
+	ca.cppFlags.SetSelectValue(axis, config, parseCommandLineFlags(props.Cppflags))
+	ca.rtti.SetSelectValue(axis, config, props.Rtti)
+}
+
+func (ca *compilerAttributes) convertStlProps(ctx android.ArchVariantContext, module *Module) {
+	stlPropsByArch := module.GetArchVariantProperties(ctx, &StlProperties{})
+	for _, configToProps := range stlPropsByArch {
+		for _, props := range configToProps {
+			if stlProps, ok := props.(*StlProperties); ok {
+				if stlProps.Stl == nil {
+					continue
 				}
-
-				if axis == bazel.NoConfigAxis {
-					// If cpp_std is not specified, don't generate it in the
-					// BUILD file. For readability purposes, cpp_std and gnu_extensions are
-					// combined into a single -std=<version> copt, except in the
-					// default case where cpp_std is nil and gnu_extensions is true or unspecified,
-					// then the toolchain's default "gnu++17" will be used.
-					if baseCompilerProps.Cpp_std != nil {
-						// TODO(b/202491296): Handle C_std.
-						// These transformations are shared with compiler.go.
-						cppStdVal := parseCppStd(baseCompilerProps.Cpp_std)
-						_, cppStdVal = maybeReplaceGnuToC(baseCompilerProps.Gnu_extensions, "", cppStdVal)
-						cppStd = &cppStdVal
-					} else if baseCompilerProps.Gnu_extensions != nil && !*baseCompilerProps.Gnu_extensions {
-						cppStdVal := "c++17"
-						cppStd = &cppStdVal
-					}
+				if ca.stl == nil {
+					ca.stl = stlProps.Stl
+				} else if ca.stl != stlProps.Stl {
+					ctx.ModuleErrorf("Unsupported conversion: module with different stl for different variants: %s and %s", *ca.stl, stlProps.Stl)
 				}
-
-				var archVariantCopts []string
-				archVariantCopts = append(archVariantCopts, parseCommandLineFlags(baseCompilerProps.Cflags)...)
-				archVariantAsflags := parseCommandLineFlags(baseCompilerProps.Asflags)
-
-				localIncludeDirs := baseCompilerProps.Local_include_dirs
-				if axis == bazel.NoConfigAxis && includeBuildDirectory(baseCompilerProps.Include_build_directory) {
-					localIncludeDirs = append(localIncludeDirs, ".")
-				}
-
-				absoluteIncludes.SetSelectValue(axis, config, baseCompilerProps.Include_dirs)
-				localIncludes.SetSelectValue(axis, config, localIncludeDirs)
-
-				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)
 			}
 		}
 	}
+}
 
-	srcs.ResolveExcludes()
-	absoluteIncludes.DeduplicateAxesFromBase()
-	localIncludes.DeduplicateAxesFromBase()
-
+func (ca *compilerAttributes) convertProductVariables(ctx android.BazelConversionPathContext, productVariableProps android.ProductConfigProperties) {
 	productVarPropNameToAttribute := map[string]*bazel.StringListAttribute{
-		"Cflags":   &copts,
-		"Asflags":  &asFlags,
-		"CppFlags": &cppFlags,
+		"Cflags":   &ca.copts,
+		"Asflags":  &ca.asFlags,
+		"CppFlags": &ca.cppFlags,
 	}
-	productVariableProps := android.ProductVariableProperties(ctx)
 	for propName, attr := range productVarPropNameToAttribute {
 		if props, exists := productVariableProps[propName]; exists {
 			for _, prop := range props {
@@ -350,39 +331,126 @@
 			}
 		}
 	}
+}
 
-	srcs, cSrcs, asSrcs := groupSrcsByExtension(ctx, srcs)
+func (ca *compilerAttributes) finalize(ctx android.BazelConversionPathContext, implementationHdrs bazel.LabelListAttribute) {
+	ca.srcs.ResolveExcludes()
+	partitionedSrcs := groupSrcsByExtension(ctx, ca.srcs)
 
-	stlPropsByArch := module.GetArchVariantProperties(ctx, &StlProperties{})
-	for _, configToProps := range stlPropsByArch {
-		for _, props := range configToProps {
-			if stlProps, ok := props.(*StlProperties); ok {
-				if stlProps.Stl != nil {
-					if stl == nil {
-						stl = stlProps.Stl
-					} else {
-						if stl != stlProps.Stl {
-							ctx.ModuleErrorf("Unsupported conversion: module with different stl for different variants: %s and %s", *stl, stlProps.Stl)
-						}
-					}
-				}
+	for p, lla := range partitionedSrcs {
+		// if there are no sources, there is no need for headers
+		if lla.IsEmpty() {
+			continue
+		}
+		lla.Append(implementationHdrs)
+		partitionedSrcs[p] = lla
+	}
+
+	ca.srcs = partitionedSrcs[cppSrcPartition]
+	ca.cSrcs = partitionedSrcs[cSrcPartition]
+	ca.asSrcs = partitionedSrcs[asSrcPartition]
+
+	ca.absoluteIncludes.DeduplicateAxesFromBase()
+	ca.localIncludes.DeduplicateAxesFromBase()
+}
+
+// Parse srcs from an arch or OS's props value.
+func parseSrcs(ctx android.BazelConversionPathContext, props *BaseCompilerProperties) (bazel.LabelList, bool) {
+	anySrcs := false
+	// Add srcs-like dependencies such as generated files.
+	// First create a LabelList containing these dependencies, then merge the values with srcs.
+	generatedSrcsLabelList := android.BazelLabelForModuleDepsExcludes(ctx, props.Generated_sources, props.Exclude_generated_sources)
+	if len(props.Generated_sources) > 0 || len(props.Exclude_generated_sources) > 0 {
+		anySrcs = true
+	}
+
+	allSrcsLabelList := android.BazelLabelForModuleSrcExcludes(ctx, props.Srcs, props.Exclude_srcs)
+	if len(props.Srcs) > 0 || len(props.Exclude_srcs) > 0 {
+		anySrcs = true
+	}
+	return bazel.AppendBazelLabelLists(allSrcsLabelList, generatedSrcsLabelList), anySrcs
+}
+
+func bp2buildResolveCppStdValue(cpp_std *string, gnu_extensions *bool) *string {
+	var cppStd *string
+	// If cpp_std is not specified, don't generate it in the
+	// BUILD file. For readability purposes, cpp_std and gnu_extensions are
+	// combined into a single -std=<version> copt, except in the
+	// default case where cpp_std is nil and gnu_extensions is true or unspecified,
+	// then the toolchain's default "gnu++17" will be used.
+	if cpp_std != nil {
+		// TODO(b/202491296): Handle C_std.
+		// These transformations are shared with compiler.go.
+		cppStdVal := parseCppStd(cpp_std)
+		_, cppStdVal = maybeReplaceGnuToC(gnu_extensions, "", cppStdVal)
+		cppStd = &cppStdVal
+	} else if gnu_extensions != nil && !*gnu_extensions {
+		cppStdVal := "c++17"
+		cppStd = &cppStdVal
+	}
+	return cppStd
+}
+
+// bp2BuildParseCompilerProps returns copts, srcs and hdrs and other attributes.
+func bp2BuildParseBaseProps(ctx android.BazelConversionPathContext, module *Module) baseAttributes {
+	archVariantCompilerProps := module.GetArchVariantProperties(ctx, &BaseCompilerProperties{})
+	archVariantLinkerProps := module.GetArchVariantProperties(ctx, &BaseLinkerProperties{})
+
+	var implementationHdrs bazel.LabelListAttribute
+
+	axisToConfigs := map[bazel.ConfigurationAxis]map[string]bool{}
+	allAxesAndConfigs := func(cp android.ConfigurationAxisToArchVariantProperties) {
+		for axis, configMap := range cp {
+			if _, ok := axisToConfigs[axis]; !ok {
+				axisToConfigs[axis] = map[string]bool{}
+			}
+			for config, _ := range configMap {
+				axisToConfigs[axis][config] = true
 			}
 		}
 	}
+	allAxesAndConfigs(archVariantCompilerProps)
+	allAxesAndConfigs(archVariantLinkerProps)
 
-	return compilerAttributes{
-		copts:            copts,
-		srcs:             srcs,
-		asFlags:          asFlags,
-		asSrcs:           asSrcs,
-		cSrcs:            cSrcs,
-		conlyFlags:       conlyFlags,
-		cppFlags:         cppFlags,
-		rtti:             rtti,
-		stl:              stl,
-		cppStd:           cppStd,
-		localIncludes:    localIncludes,
-		absoluteIncludes: absoluteIncludes,
+	compilerAttrs := compilerAttributes{}
+	linkerAttrs := linkerAttributes{}
+
+	for axis, configs := range axisToConfigs {
+		for config, _ := range configs {
+			var allHdrs []string
+			if baseCompilerProps, ok := archVariantCompilerProps[axis][config].(*BaseCompilerProperties); ok {
+				allHdrs = baseCompilerProps.Generated_headers
+
+				(&compilerAttrs).bp2buildForAxisAndConfig(ctx, axis, config, baseCompilerProps)
+			}
+
+			var exportHdrs []string
+
+			if baseLinkerProps, ok := archVariantLinkerProps[axis][config].(*BaseLinkerProperties); ok {
+				exportHdrs = baseLinkerProps.Export_generated_headers
+
+				(&linkerAttrs).bp2buildForAxisAndConfig(ctx, module.Binary(), axis, config, baseLinkerProps)
+			}
+			headers := maybePartitionExportedAndImplementationsDeps(ctx, !module.Binary(), allHdrs, exportHdrs, android.BazelLabelForModuleDeps)
+			implementationHdrs.SetSelectValue(axis, config, headers.implementation)
+			compilerAttrs.hdrs.SetSelectValue(axis, config, headers.export)
+		}
+	}
+
+	compilerAttrs.convertStlProps(ctx, module)
+	(&linkerAttrs).convertStripProps(ctx, module)
+
+	productVariableProps := android.ProductVariableProperties(ctx)
+
+	(&compilerAttrs).convertProductVariables(ctx, productVariableProps)
+	(&linkerAttrs).convertProductVariables(ctx, productVariableProps)
+
+	(&compilerAttrs).finalize(ctx, implementationHdrs)
+	(&linkerAttrs).finalize()
+
+	return baseAttributes{
+		compilerAttrs,
+		linkerAttrs,
 	}
 }
 
@@ -407,121 +475,94 @@
 	features                      bazel.StringListAttribute
 }
 
-// bp2BuildParseLinkerProps parses the linker properties of a module, including
-// configurable attribute values.
-func bp2BuildParseLinkerProps(ctx android.TopDownMutatorContext, module *Module) linkerAttributes {
+func (la *linkerAttributes) bp2buildForAxisAndConfig(ctx android.BazelConversionPathContext, isBinary bool, axis bazel.ConfigurationAxis, config string, props *BaseLinkerProperties) {
+	// Use a single variable to capture usage of nocrt in arch variants, so there's only 1 error message for this module
+	var axisFeatures []string
 
-	var headerDeps bazel.LabelListAttribute
-	var implementationHeaderDeps bazel.LabelListAttribute
-	var deps bazel.LabelListAttribute
-	var implementationDeps bazel.LabelListAttribute
-	var dynamicDeps bazel.LabelListAttribute
-	var implementationDynamicDeps bazel.LabelListAttribute
-	var wholeArchiveDeps bazel.LabelListAttribute
-	systemSharedDeps := bazel.LabelListAttribute{ForceSpecifyEmptyList: true}
+	// 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(props.Static_libs)
+	staticDeps := maybePartitionExportedAndImplementationsDepsExcludes(ctx, !isBinary, staticLibs, props.Exclude_static_libs, props.Export_static_lib_headers, bazelLabelForStaticDepsExcludes)
 
-	var linkopts bazel.StringListAttribute
-	var linkCrt bazel.BoolAttribute
-	var additionalLinkerInputs bazel.LabelListAttribute
-	var useLibcrt bazel.BoolAttribute
+	headerLibs := android.FirstUniqueStrings(props.Header_libs)
+	hDeps := maybePartitionExportedAndImplementationsDeps(ctx, !isBinary, headerLibs, props.Export_header_lib_headers, bazelLabelForHeaderDeps)
 
-	var stripKeepSymbols bazel.BoolAttribute
-	var stripKeepSymbolsAndDebugFrame bazel.BoolAttribute
-	var stripKeepSymbolsList bazel.StringListAttribute
-	var stripAll bazel.BoolAttribute
-	var stripNone bazel.BoolAttribute
+	(&hDeps.export).Append(staticDeps.export)
+	la.deps.SetSelectValue(axis, config, hDeps.export)
 
-	var features bazel.StringListAttribute
+	(&hDeps.implementation).Append(staticDeps.implementation)
+	la.implementationDeps.SetSelectValue(axis, config, hDeps.implementation)
 
+	wholeStaticLibs := android.FirstUniqueStrings(props.Whole_static_libs)
+	la.wholeArchiveDeps.SetSelectValue(axis, config, bazelLabelForWholeDepsExcludes(ctx, wholeStaticLibs, props.Exclude_static_libs))
+
+	systemSharedLibs := props.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)
+	}
+	la.systemDynamicDeps.SetSelectValue(axis, config, bazelLabelForSharedDeps(ctx, systemSharedLibs))
+
+	sharedLibs := android.FirstUniqueStrings(props.Shared_libs)
+	sharedDeps := maybePartitionExportedAndImplementationsDepsExcludes(ctx, !isBinary, sharedLibs, props.Exclude_shared_libs, props.Export_shared_lib_headers, bazelLabelForSharedDepsExcludes)
+	la.dynamicDeps.SetSelectValue(axis, config, sharedDeps.export)
+	la.implementationDynamicDeps.SetSelectValue(axis, config, sharedDeps.implementation)
+
+	if !BoolDefault(props.Pack_relocations, packRelocationsDefault) {
+		axisFeatures = append(axisFeatures, "disable_pack_relocations")
+	}
+
+	if Bool(props.Allow_undefined_symbols) {
+		axisFeatures = append(axisFeatures, "-no_undefined_symbols")
+	}
+
+	var linkerFlags []string
+	if len(props.Ldflags) > 0 {
+		linkerFlags = append(linkerFlags, props.Ldflags...)
+		// binaries remove static flag if -shared is in the linker flags
+		if isBinary && android.InList("-shared", linkerFlags) {
+			axisFeatures = append(axisFeatures, "-static_flag")
+		}
+	}
+	if props.Version_script != nil {
+		label := android.BazelLabelForModuleSrcSingle(ctx, *props.Version_script)
+		la.additionalLinkerInputs.SetSelectValue(axis, config, bazel.LabelList{Includes: []bazel.Label{label}})
+		linkerFlags = append(linkerFlags, fmt.Sprintf("-Wl,--version-script,$(location %s)", label.Label))
+	}
+	la.linkopts.SetSelectValue(axis, config, linkerFlags)
+	la.useLibcrt.SetSelectValue(axis, config, props.libCrt())
+
+	// it's very unlikely for nocrt to be arch variant, so bp2build doesn't support it.
+	if props.crt() != nil {
+		if axis == bazel.NoConfigAxis {
+			la.linkCrt.SetSelectValue(axis, config, props.crt())
+		} else if axis == bazel.ArchConfigurationAxis {
+			ctx.ModuleErrorf("nocrt is not supported for arch variants")
+		}
+	}
+
+	if axisFeatures != nil {
+		la.features.SetSelectValue(axis, config, axisFeatures)
+	}
+}
+
+func (la *linkerAttributes) convertStripProps(ctx android.BazelConversionPathContext, module *Module) {
 	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)
+				la.stripKeepSymbols.SetSelectValue(axis, config, stripProperties.Strip.Keep_symbols)
+				la.stripKeepSymbolsList.SetSelectValue(axis, config, stripProperties.Strip.Keep_symbols_list)
+				la.stripKeepSymbolsAndDebugFrame.SetSelectValue(axis, config, stripProperties.Strip.Keep_symbols_and_debug_frame)
+				la.stripAll.SetSelectValue(axis, config, stripProperties.Strip.All)
+				la.stripNone.SetSelectValue(axis, config, stripProperties.Strip.None)
 			}
 		}
 	}
+}
 
-	// Use a single variable to capture usage of nocrt in arch variants, so there's only 1 error message for this module
-	var disallowedArchVariantCrt bool
-
-	for axis, configToProps := range module.GetArchVariantProperties(ctx, &BaseLinkerProperties{}) {
-		for config, props := range configToProps {
-			if baseLinkerProps, ok := props.(*BaseLinkerProperties); ok {
-				var axisFeatures []string
-
-				// 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 := partitionExportedAndImplementationsDepsExcludes(ctx, staticLibs, baseLinkerProps.Exclude_static_libs, baseLinkerProps.Export_static_lib_headers, bazelLabelForStaticDepsExcludes)
-				deps.SetSelectValue(axis, config, staticDeps.export)
-				implementationDeps.SetSelectValue(axis, config, staticDeps.implementation)
-
-				wholeStaticLibs := android.FirstUniqueStrings(baseLinkerProps.Whole_static_libs)
-				wholeArchiveDeps.SetSelectValue(axis, config, bazelLabelForWholeDepsExcludes(ctx, wholeStaticLibs, 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, bazelLabelForSharedDeps(ctx, systemSharedLibs))
-
-				sharedLibs := android.FirstUniqueStrings(baseLinkerProps.Shared_libs)
-				sharedDeps := partitionExportedAndImplementationsDepsExcludes(ctx, sharedLibs, baseLinkerProps.Exclude_shared_libs, baseLinkerProps.Export_shared_lib_headers, bazelLabelForSharedDepsExcludes)
-				dynamicDeps.SetSelectValue(axis, config, sharedDeps.export)
-				implementationDynamicDeps.SetSelectValue(axis, config, sharedDeps.implementation)
-
-				headerLibs := android.FirstUniqueStrings(baseLinkerProps.Header_libs)
-				hDeps := partitionExportedAndImplementationsDeps(ctx, headerLibs, baseLinkerProps.Export_header_lib_headers, bazelLabelForHeaderDeps)
-
-				headerDeps.SetSelectValue(axis, config, hDeps.export)
-				implementationHeaderDeps.SetSelectValue(axis, config, hDeps.implementation)
-
-				if !BoolDefault(baseLinkerProps.Pack_relocations, packRelocationsDefault) {
-					axisFeatures = append(axisFeatures, "disable_pack_relocations")
-				}
-
-				if Bool(baseLinkerProps.Allow_undefined_symbols) {
-					axisFeatures = append(axisFeatures, "-no_undefined_symbols")
-				}
-
-				var linkerFlags []string
-				if len(baseLinkerProps.Ldflags) > 0 {
-					linkerFlags = append(linkerFlags, baseLinkerProps.Ldflags...)
-				}
-				if baseLinkerProps.Version_script != nil {
-					label := android.BazelLabelForModuleSrcSingle(ctx, *baseLinkerProps.Version_script)
-					additionalLinkerInputs.SetSelectValue(axis, config, bazel.LabelList{Includes: []bazel.Label{label}})
-					linkerFlags = append(linkerFlags, fmt.Sprintf("-Wl,--version-script,$(location %s)", label.Label))
-				}
-				linkopts.SetSelectValue(axis, config, linkerFlags)
-				useLibcrt.SetSelectValue(axis, config, baseLinkerProps.libCrt())
-
-				// it's very unlikely for nocrt to be arch variant, so bp2build doesn't support it.
-				if baseLinkerProps.crt() != nil {
-					if axis == bazel.NoConfigAxis {
-						linkCrt.SetSelectValue(axis, config, baseLinkerProps.crt())
-					} else if axis == bazel.ArchConfigurationAxis {
-						disallowedArchVariantCrt = true
-					}
-				}
-
-				if axisFeatures != nil {
-					features.SetSelectValue(axis, config, axisFeatures)
-				}
-			}
-		}
-	}
-
-	if disallowedArchVariantCrt {
-		ctx.ModuleErrorf("nocrt is not supported for arch variants")
-	}
+func (la *linkerAttributes) convertProductVariables(ctx android.BazelConversionPathContext, productVariableProps android.ProductConfigProperties) {
 
 	type productVarDep struct {
 		// the name of the corresponding excludes field, if one exists
@@ -529,17 +570,16 @@
 		// reference to the bazel attribute that should be set for the given product variable config
 		attribute *bazel.LabelListAttribute
 
-		depResolutionFunc func(ctx android.TopDownMutatorContext, modules, excludes []string) bazel.LabelList
+		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: &implementationDynamicDeps, depResolutionFunc: bazelLabelForSharedDepsExcludes},
-		"Static_libs":       productVarDep{"Exclude_static_libs", &implementationDeps, bazelLabelForStaticDepsExcludes},
-		"Whole_static_libs": productVarDep{"Exclude_static_libs", &wholeArchiveDeps, bazelLabelForWholeDepsExcludes},
+		"Shared_libs":       {attribute: &la.implementationDynamicDeps, depResolutionFunc: bazelLabelForSharedDepsExcludes},
+		"Static_libs":       {"Exclude_static_libs", &la.implementationDeps, bazelLabelForStaticDepsExcludes},
+		"Whole_static_libs": {"Exclude_static_libs", &la.wholeArchiveDeps, bazelLabelForWholeDepsExcludes},
 	}
 
-	productVariableProps := android.ProductVariableProperties(ctx)
 	for name, dep := range productVarToDepFields {
 		props, exists := productVariableProps[name]
 		excludeProps, excludesExists := productVariableProps[dep.excludesField]
@@ -574,38 +614,15 @@
 			dep.attribute.SetSelectValue(bazel.ProductVariableConfigurationAxis(config), config, dep.depResolutionFunc(ctx, android.FirstUniqueStrings(includes), excludes))
 		}
 	}
+}
 
-	headerDeps.Append(deps)
-	implementationHeaderDeps.Append(implementationDeps)
-
-	headerDeps.ResolveExcludes()
-	implementationHeaderDeps.ResolveExcludes()
-	dynamicDeps.ResolveExcludes()
-	implementationDynamicDeps.ResolveExcludes()
-	wholeArchiveDeps.ResolveExcludes()
-
-	return linkerAttributes{
-		deps:                      headerDeps,
-		implementationDeps:        implementationHeaderDeps,
-		dynamicDeps:               dynamicDeps,
-		implementationDynamicDeps: implementationDynamicDeps,
-		wholeArchiveDeps:          wholeArchiveDeps,
-		systemDynamicDeps:         systemSharedDeps,
-
-		linkCrt:                linkCrt,
-		linkopts:               linkopts,
-		useLibcrt:              useLibcrt,
-		additionalLinkerInputs: additionalLinkerInputs,
-
-		// Strip properties
-		stripKeepSymbols:              stripKeepSymbols,
-		stripKeepSymbolsAndDebugFrame: stripKeepSymbolsAndDebugFrame,
-		stripKeepSymbolsList:          stripKeepSymbolsList,
-		stripAll:                      stripAll,
-		stripNone:                     stripNone,
-
-		features: features,
-	}
+func (la *linkerAttributes) finalize() {
+	la.deps.ResolveExcludes()
+	la.implementationDeps.ResolveExcludes()
+	la.dynamicDeps.ResolveExcludes()
+	la.implementationDynamicDeps.ResolveExcludes()
+	la.wholeArchiveDeps.ResolveExcludes()
+	la.systemDynamicDeps.ForceSpecifyEmptyList = true
 }
 
 // Relativize a list of root-relative paths with respect to the module's
@@ -638,14 +655,14 @@
 	SystemIncludes bazel.StringListAttribute
 }
 
-func bp2BuildParseExportedIncludes(ctx android.TopDownMutatorContext, module *Module) BazelIncludes {
+func bp2BuildParseExportedIncludes(ctx android.BazelConversionPathContext, module *Module) BazelIncludes {
 	libraryDecorator := module.linker.(*libraryDecorator)
 	return bp2BuildParseExportedIncludesHelper(ctx, module, libraryDecorator)
 }
 
 // Bp2buildParseExportedIncludesForPrebuiltLibrary returns a BazelIncludes with Bazel-ified values
 // to export includes from the underlying module's properties.
-func Bp2BuildParseExportedIncludesForPrebuiltLibrary(ctx android.TopDownMutatorContext, module *Module) BazelIncludes {
+func Bp2BuildParseExportedIncludesForPrebuiltLibrary(ctx android.BazelConversionPathContext, module *Module) BazelIncludes {
 	prebuiltLibraryLinker := module.linker.(*prebuiltLibraryLinker)
 	libraryDecorator := prebuiltLibraryLinker.libraryDecorator
 	return bp2BuildParseExportedIncludesHelper(ctx, module, libraryDecorator)
@@ -653,7 +670,7 @@
 
 // bp2BuildParseExportedIncludes creates a string list attribute contains the
 // exported included directories of a module.
-func bp2BuildParseExportedIncludesHelper(ctx android.TopDownMutatorContext, module *Module, libraryDecorator *libraryDecorator) BazelIncludes {
+func bp2BuildParseExportedIncludesHelper(ctx android.BazelConversionPathContext, module *Module, libraryDecorator *libraryDecorator) BazelIncludes {
 	exported := BazelIncludes{}
 	for axis, configToProps := range module.GetArchVariantProperties(ctx, &FlagExporterProperties{}) {
 		for config, props := range configToProps {
@@ -673,7 +690,7 @@
 	return exported
 }
 
-func bazelLabelForStaticModule(ctx android.TopDownMutatorContext, m blueprint.Module) string {
+func bazelLabelForStaticModule(ctx android.BazelConversionPathContext, m blueprint.Module) string {
 	label := android.BazelModuleLabel(ctx, m)
 	if aModule, ok := m.(android.Module); ok {
 		if ctx.OtherModuleType(aModule) == "cc_library" && !android.GenerateCcLibraryStaticOnly(m.Name()) {
@@ -683,13 +700,13 @@
 	return label
 }
 
-func bazelLabelForSharedModule(ctx android.TopDownMutatorContext, m blueprint.Module) string {
+func bazelLabelForSharedModule(ctx android.BazelConversionPathContext, m blueprint.Module) string {
 	// cc_library, at it's root name, propagates the shared library, which depends on the static
 	// library.
 	return android.BazelModuleLabel(ctx, m)
 }
 
-func bazelLabelForStaticWholeModuleDeps(ctx android.TopDownMutatorContext, m blueprint.Module) string {
+func bazelLabelForStaticWholeModuleDeps(ctx android.BazelConversionPathContext, m blueprint.Module) string {
 	label := bazelLabelForStaticModule(ctx, m)
 	if aModule, ok := m.(android.Module); ok {
 		if android.IsModulePrebuilt(aModule) {
@@ -699,32 +716,58 @@
 	return label
 }
 
-func bazelLabelForWholeDeps(ctx android.TopDownMutatorContext, modules []string) bazel.LabelList {
+func bazelLabelForWholeDeps(ctx android.BazelConversionPathContext, modules []string) bazel.LabelList {
 	return android.BazelLabelForModuleDepsWithFn(ctx, modules, bazelLabelForStaticWholeModuleDeps)
 }
 
-func bazelLabelForWholeDepsExcludes(ctx android.TopDownMutatorContext, modules, excludes []string) bazel.LabelList {
+func bazelLabelForWholeDepsExcludes(ctx android.BazelConversionPathContext, modules, excludes []string) bazel.LabelList {
 	return android.BazelLabelForModuleDepsExcludesWithFn(ctx, modules, excludes, bazelLabelForStaticWholeModuleDeps)
 }
 
-func bazelLabelForStaticDepsExcludes(ctx android.TopDownMutatorContext, modules, excludes []string) bazel.LabelList {
+func bazelLabelForStaticDepsExcludes(ctx android.BazelConversionPathContext, modules, excludes []string) bazel.LabelList {
 	return android.BazelLabelForModuleDepsExcludesWithFn(ctx, modules, excludes, bazelLabelForStaticModule)
 }
 
-func bazelLabelForStaticDeps(ctx android.TopDownMutatorContext, modules []string) bazel.LabelList {
+func bazelLabelForStaticDeps(ctx android.BazelConversionPathContext, modules []string) bazel.LabelList {
 	return android.BazelLabelForModuleDepsWithFn(ctx, modules, bazelLabelForStaticModule)
 }
 
-func bazelLabelForSharedDeps(ctx android.TopDownMutatorContext, modules []string) bazel.LabelList {
+func bazelLabelForSharedDeps(ctx android.BazelConversionPathContext, modules []string) bazel.LabelList {
 	return android.BazelLabelForModuleDepsWithFn(ctx, modules, bazelLabelForSharedModule)
 }
 
-func bazelLabelForHeaderDeps(ctx android.TopDownMutatorContext, modules []string) bazel.LabelList {
+func bazelLabelForHeaderDeps(ctx android.BazelConversionPathContext, modules []string) bazel.LabelList {
 	// This is not elegant, but bp2build's shared library targets only propagate
 	// their header information as part of the normal C++ provider.
 	return bazelLabelForSharedDeps(ctx, modules)
 }
 
-func bazelLabelForSharedDepsExcludes(ctx android.TopDownMutatorContext, modules, excludes []string) bazel.LabelList {
+func bazelLabelForSharedDepsExcludes(ctx android.BazelConversionPathContext, modules, excludes []string) bazel.LabelList {
 	return android.BazelLabelForModuleDepsExcludesWithFn(ctx, modules, excludes, bazelLabelForSharedModule)
 }
+
+type binaryLinkerAttrs struct {
+	Linkshared *bool
+}
+
+func bp2buildBinaryLinkerProps(ctx android.BazelConversionPathContext, m *Module) binaryLinkerAttrs {
+	attrs := binaryLinkerAttrs{}
+	archVariantProps := m.GetArchVariantProperties(ctx, &BinaryLinkerProperties{})
+	for axis, configToProps := range archVariantProps {
+		for _, p := range configToProps {
+			props := p.(*BinaryLinkerProperties)
+			staticExecutable := props.Static_executable
+			if axis == bazel.NoConfigAxis {
+				if linkBinaryShared := !proptools.Bool(staticExecutable); !linkBinaryShared {
+					attrs.Linkshared = &linkBinaryShared
+				}
+			} else if staticExecutable != nil {
+				// TODO(b/202876379): Static_executable is arch-variant; however, linkshared is a
+				// nonconfigurable attribute. Only 4 AOSP modules use this feature, defer handling
+				ctx.ModuleErrorf("bp2build cannot migrate a module with arch/target-specific static_executable values")
+			}
+		}
+	}
+
+	return attrs
+}
diff --git a/cc/builder.go b/cc/builder.go
index b494f7b..abd5f1d 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -238,14 +238,6 @@
 		},
 		"asFlags")
 
-	// Rule to invoke windres, for interaction with Windows resources.
-	windres = pctx.AndroidStaticRule("windres",
-		blueprint.RuleParams{
-			Command:     "$windresCmd $flags -I$$(dirname $in) -i $in -o $out --preprocessor \"${config.ClangBin}/clang -E -xc-header -DRC_INVOKED\"",
-			CommandDeps: []string{"$windresCmd"},
-		},
-		"windresCmd", "flags")
-
 	_ = pctx.SourcePathVariable("sAbiDumper", "prebuilts/clang-tools/${config.HostPrebuiltTag}/bin/header-abi-dumper")
 
 	// -w has been added since header-abi-dumper does not need to produce any sort of diagnostic information.
@@ -421,7 +413,7 @@
 // Objects is a collection of file paths corresponding to outputs for C++ related build statements.
 type Objects struct {
 	objFiles      android.Paths
-	tidyFiles     android.Paths
+	tidyFiles     android.WritablePaths
 	coverageFiles android.Paths
 	sAbiDumpFiles android.Paths
 	kytheFiles    android.Paths
@@ -430,7 +422,7 @@
 func (a Objects) Copy() Objects {
 	return Objects{
 		objFiles:      append(android.Paths{}, a.objFiles...),
-		tidyFiles:     append(android.Paths{}, a.tidyFiles...),
+		tidyFiles:     append(android.WritablePaths{}, a.tidyFiles...),
 		coverageFiles: append(android.Paths{}, a.coverageFiles...),
 		sAbiDumpFiles: append(android.Paths{}, a.sAbiDumpFiles...),
 		kytheFiles:    append(android.Paths{}, a.kytheFiles...),
@@ -459,11 +451,11 @@
 
 	// Source files are one-to-one with tidy, coverage, or kythe files, if enabled.
 	objFiles := make(android.Paths, len(srcFiles))
-	var tidyFiles android.Paths
+	var tidyFiles android.WritablePaths
 	noTidySrcsMap := make(map[android.Path]bool)
 	var tidyVars string
 	if flags.tidy {
-		tidyFiles = make(android.Paths, 0, len(srcFiles))
+		tidyFiles = make(android.WritablePaths, 0, len(srcFiles))
 		for _, path := range noTidySrcs {
 			noTidySrcsMap[path] = true
 		}
@@ -577,20 +569,6 @@
 				},
 			})
 			continue
-		case ".rc":
-			ctx.Build(pctx, android.BuildParams{
-				Rule:        windres,
-				Description: "windres " + srcFile.Rel(),
-				Output:      objFile,
-				Input:       srcFile,
-				Implicits:   cFlagsDeps,
-				OrderOnly:   pathDeps,
-				Args: map[string]string{
-					"windresCmd": mingwCmd(flags.toolchain, "windres"),
-					"flags":      shareFlags("flags", flags.toolchain.WindresFlags()),
-				},
-			})
-			continue
 		case ".o":
 			objFiles[i] = srcFile
 			continue
@@ -741,7 +719,7 @@
 // Generate a rule for compiling multiple .o files to a static library (.a)
 func transformObjToStaticLib(ctx android.ModuleContext,
 	objFiles android.Paths, wholeStaticLibs android.Paths,
-	flags builderFlags, outputFile android.ModuleOutPath, deps android.Paths) {
+	flags builderFlags, outputFile android.ModuleOutPath, deps android.Paths, validations android.WritablePaths) {
 
 	arCmd := "${config.ClangBin}/llvm-ar"
 	arFlags := ""
@@ -756,6 +734,7 @@
 			Output:      outputFile,
 			Inputs:      objFiles,
 			Implicits:   deps,
+			Validations: validations.Paths(),
 			Args: map[string]string{
 				"arFlags": "crsPD" + arFlags,
 				"arCmd":   arCmd,
diff --git a/cc/cc.go b/cc/cc.go
index 1c65549..32652c1 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -325,7 +325,7 @@
 	SnapshotStaticLibs  []string `blueprint:"mutated"`
 	SnapshotRuntimeLibs []string `blueprint:"mutated"`
 
-	Installable *bool
+	Installable *bool `android:"arch_variant"`
 
 	// Set by factories of module types that can only be referenced from variants compiled against
 	// the SDK.
@@ -1865,7 +1865,7 @@
 }
 
 func (c *Module) maybeInstall(ctx ModuleContext, apexInfo android.ApexInfo) {
-	if !proptools.BoolDefault(c.Properties.Installable, true) {
+	if !proptools.BoolDefault(c.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
 		// as the output path to install will not be specified. Not all uninstallable
@@ -3260,6 +3260,11 @@
 }
 
 func (c *Module) Installable() *bool {
+	if c.library != nil {
+		if i := c.library.installable(); i != nil {
+			return i
+		}
+	}
 	return c.Properties.Installable
 }
 
diff --git a/cc/compiler.go b/cc/compiler.go
index 2ac7bf3..00df669 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -537,11 +537,6 @@
 			"-I"+android.PathForModuleGen(ctx, "yacc", ctx.ModuleDir()).String())
 	}
 
-	if compiler.hasSrcExt(".mc") {
-		flags.Local.CommonFlags = append(flags.Local.CommonFlags,
-			"-I"+android.PathForModuleGen(ctx, "windmc", ctx.ModuleDir()).String())
-	}
-
 	if compiler.hasSrcExt(".aidl") {
 		flags.aidlFlags = append(flags.aidlFlags, compiler.Properties.Aidl.Flags...)
 		if len(compiler.Properties.Aidl.Local_include_dirs) > 0 {
diff --git a/cc/config/Android.bp b/cc/config/Android.bp
index 3e8ee48..7b7ee28 100644
--- a/cc/config/Android.bp
+++ b/cc/config/Android.bp
@@ -24,7 +24,7 @@
         "x86_device.go",
         "x86_64_device.go",
 
-        "x86_darwin_host.go",
+        "darwin_host.go",
         "x86_linux_host.go",
         "x86_linux_bionic_host.go",
         "x86_windows_host.go",
diff --git a/cc/config/bp2build.go b/cc/config/bp2build.go
index d19f5ac..4797acc 100644
--- a/cc/config/bp2build.go
+++ b/cc/config/bp2build.go
@@ -16,9 +16,13 @@
 
 import (
 	"fmt"
+	"reflect"
 	"regexp"
 	"sort"
 	"strings"
+
+	"android/soong/android"
+	"github.com/google/blueprint"
 )
 
 const (
@@ -26,18 +30,27 @@
 )
 
 type bazelVarExporter interface {
-	asBazel(exportedStringVariables, exportedStringListVariables) []bazelConstant
+	asBazel(android.Config, exportedStringVariables, exportedStringListVariables, exportedConfigDependingVariables) []bazelConstant
 }
 
 // Helpers for exporting cc configuration information to Bazel.
 var (
-	// Map containing toolchain variables that are independent of the
+	// Maps containing toolchain variables that are independent of the
 	// environment variables of the build.
 	exportedStringListVars     = exportedStringListVariables{}
 	exportedStringVars         = exportedStringVariables{}
 	exportedStringListDictVars = exportedStringListDictVariables{}
+
+	/// Maps containing variables that are dependent on the build config.
+	exportedConfigDependingVars = exportedConfigDependingVariables{}
 )
 
+type exportedConfigDependingVariables map[string]interface{}
+
+func (m exportedConfigDependingVariables) Set(k string, v interface{}) {
+	m[k] = v
+}
+
 // 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`, `"`, `\`} {
@@ -74,10 +87,14 @@
 	return strings.Join(list, "\n")
 }
 
-func (m exportedStringVariables) asBazel(stringScope exportedStringVariables, stringListScope exportedStringListVariables) []bazelConstant {
+func (m exportedStringVariables) asBazel(config android.Config,
+	stringVars exportedStringVariables, stringListVars exportedStringListVariables, cfgDepVars exportedConfigDependingVariables) []bazelConstant {
 	ret := make([]bazelConstant, 0, len(m))
 	for k, variableValue := range m {
-		expandedVar := expandVar(variableValue, exportedStringVars, exportedStringListVars)
+		expandedVar, err := expandVar(config, variableValue, stringVars, stringListVars, cfgDepVars)
+		if err != nil {
+			panic(fmt.Errorf("error expanding config variable %s: %s", k, err))
+		}
 		if len(expandedVar) > 1 {
 			panic(fmt.Errorf("%s expands to more than one string value: %s", variableValue, expandedVar))
 		}
@@ -101,7 +118,9 @@
 	m[k] = v
 }
 
-func (m exportedStringListVariables) asBazel(stringScope exportedStringVariables, stringListScope exportedStringListVariables) []bazelConstant {
+func (m exportedStringListVariables) asBazel(config android.Config,
+	stringScope exportedStringVariables, stringListScope exportedStringListVariables,
+	exportedVars exportedConfigDependingVariables) []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
@@ -109,7 +128,11 @@
 	for k, variableValue := range m {
 		var expandedVars []string
 		for _, v := range variableValue {
-			expandedVars = append(expandedVars, expandVar(v, stringScope, stringListScope)...)
+			expandedVar, err := expandVar(config, v, stringScope, stringListScope, exportedVars)
+			if err != nil {
+				panic(fmt.Errorf("Error expanding config variable %s=%s: %s", k, v, err))
+			}
+			expandedVars = append(expandedVars, expandedVar...)
 		}
 		// Assign the list as a bzl-private variable; this variable will be exported
 		// out through a constants struct later.
@@ -121,6 +144,18 @@
 	return ret
 }
 
+// Convenience function to declare a static "source path" variable and export it to Bazel's cc_toolchain.
+func exportVariableConfigMethod(name string, method interface{}) blueprint.Variable {
+	exportedConfigDependingVars.Set(name, method)
+	return pctx.VariableConfigMethod(name, method)
+}
+
+// Convenience function to declare a static "source path" variable and export it to Bazel's cc_toolchain.
+func exportSourcePathVariable(name string, value string) {
+	pctx.SourcePathVariable(name, value)
+	exportedStringVars.Set(name, value)
+}
+
 // 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, " "))
@@ -145,7 +180,8 @@
 }
 
 // Since dictionaries are not supported in Ninja, we do not expand variables for dictionaries
-func (m exportedStringListDictVariables) asBazel(_ exportedStringVariables, _ exportedStringListVariables) []bazelConstant {
+func (m exportedStringListDictVariables) asBazel(_ android.Config, _ exportedStringVariables,
+	_ exportedStringListVariables, _ exportedConfigDependingVariables) []bazelConstant {
 	ret := make([]bazelConstant, 0, len(m))
 	for k, dict := range m {
 		ret = append(ret, bazelConstant{
@@ -158,19 +194,20 @@
 
 // BazelCcToolchainVars generates bzl file content containing variables for
 // Bazel's cc_toolchain configuration.
-func BazelCcToolchainVars() string {
+func BazelCcToolchainVars(config android.Config) string {
 	return bazelToolchainVars(
+		config,
 		exportedStringListDictVars,
 		exportedStringListVars,
 		exportedStringVars)
 }
 
-func bazelToolchainVars(vars ...bazelVarExporter) string {
+func bazelToolchainVars(config android.Config, vars ...bazelVarExporter) string {
 	ret := "# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.\n\n"
 
 	results := []bazelConstant{}
 	for _, v := range vars {
-		results = append(results, v.asBazel(exportedStringVars, exportedStringListVars)...)
+		results = append(results, v.asBazel(config, exportedStringVars, exportedStringListVars, exportedConfigDependingVars)...)
 	}
 
 	sort.Slice(results, func(i, j int) bool { return results[i].variableName < results[j].variableName })
@@ -201,34 +238,35 @@
 // 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, stringScope exportedStringVariables, stringListScope exportedStringListVariables) []string {
+func expandVar(config android.Config, toExpand string, stringScope exportedStringVariables,
+	stringListScope exportedStringListVariables, exportedVars exportedConfigDependingVariables) ([]string, error) {
 	// e.g. "${ExternalCflags}"
 	r := regexp.MustCompile(`\${([a-zA-Z0-9_]+)}`)
 
 	// Internal recursive function.
-	var expandVarInternal func(string, map[string]bool) []string
-	expandVarInternal = func(toExpand string, seenVars map[string]bool) []string {
-		var ret []string
-		for _, v := range strings.Split(toExpand, " ") {
-			matches := r.FindStringSubmatch(v)
+	var expandVarInternal func(string, map[string]bool) (string, error)
+	expandVarInternal = func(toExpand string, seenVars map[string]bool) (string, error) {
+		var ret string
+		remainingString := toExpand
+		for len(remainingString) > 0 {
+			matches := r.FindStringSubmatch(remainingString)
 			if len(matches) == 0 {
-				return []string{v}
+				return ret + remainingString, nil
 			}
-
 			if len(matches) != 2 {
-				panic(fmt.Errorf(
-					"Expected to only match 1 subexpression in %s, got %d",
-					v,
-					len(matches)-1))
+				panic(fmt.Errorf("Expected to only match 1 subexpression in %s, got %d", remainingString, len(matches)-1))
 			}
+			matchIndex := strings.Index(remainingString, matches[0])
+			ret += remainingString[:matchIndex]
+			remainingString = remainingString[matchIndex+len(matches[0]):]
 
 			// Index 1 of FindStringSubmatch contains the subexpression match
 			// (variable name) of the capture group.
 			variable := matches[1]
 			// toExpand contains a variable.
 			if _, ok := seenVars[variable]; ok {
-				panic(fmt.Errorf(
-					"Unbounded recursive interpolation of variable: %s", variable))
+				return ret, fmt.Errorf(
+					"Unbounded recursive interpolation of variable: %s", variable)
 			}
 			// A map is passed-by-reference. Create a new map for
 			// this scope to prevent variables seen in one depth-first expansion
@@ -239,15 +277,65 @@
 			}
 			newSeenVars[variable] = true
 			if unexpandedVars, ok := stringListScope[variable]; ok {
+				expandedVars := []string{}
 				for _, unexpandedVar := range unexpandedVars {
-					ret = append(ret, expandVarInternal(unexpandedVar, newSeenVars)...)
+					expandedVar, err := expandVarInternal(unexpandedVar, newSeenVars)
+					if err != nil {
+						return ret, err
+					}
+					expandedVars = append(expandedVars, expandedVar)
 				}
+				ret += strings.Join(expandedVars, " ")
 			} else if unexpandedVar, ok := stringScope[variable]; ok {
-				ret = append(ret, expandVarInternal(unexpandedVar, newSeenVars)...)
+				expandedVar, err := expandVarInternal(unexpandedVar, newSeenVars)
+				if err != nil {
+					return ret, err
+				}
+				ret += expandedVar
+			} else if unevaluatedVar, ok := exportedVars[variable]; ok {
+				evalFunc := reflect.ValueOf(unevaluatedVar)
+				validateVariableMethod(variable, evalFunc)
+				evaluatedResult := evalFunc.Call([]reflect.Value{reflect.ValueOf(config)})
+				evaluatedValue := evaluatedResult[0].Interface().(string)
+				expandedVar, err := expandVarInternal(evaluatedValue, newSeenVars)
+				if err != nil {
+					return ret, err
+				}
+				ret += expandedVar
+			} else {
+				return "", fmt.Errorf("Unbound config variable %s", variable)
 			}
 		}
-		return ret
+		return ret, nil
+	}
+	var ret []string
+	for _, v := range strings.Split(toExpand, " ") {
+		val, err := expandVarInternal(v, map[string]bool{})
+		if err != nil {
+			return ret, err
+		}
+		ret = append(ret, val)
 	}
 
-	return expandVarInternal(toExpand, map[string]bool{})
+	return ret, nil
+}
+
+func validateVariableMethod(name string, methodValue reflect.Value) {
+	methodType := methodValue.Type()
+	if methodType.Kind() != reflect.Func {
+		panic(fmt.Errorf("method given for variable %s is not a function",
+			name))
+	}
+	if n := methodType.NumIn(); n != 1 {
+		panic(fmt.Errorf("method for variable %s has %d inputs (should be 1)",
+			name, n))
+	}
+	if n := methodType.NumOut(); n != 1 {
+		panic(fmt.Errorf("method for variable %s has %d outputs (should be 1)",
+			name, n))
+	}
+	if kind := methodType.Out(0).Kind(); kind != reflect.String {
+		panic(fmt.Errorf("method for variable %s does not return a string",
+			name))
+	}
 }
diff --git a/cc/config/bp2build_test.go b/cc/config/bp2build_test.go
index 883597a..3118df1 100644
--- a/cc/config/bp2build_test.go
+++ b/cc/config/bp2build_test.go
@@ -16,13 +16,21 @@
 
 import (
 	"testing"
+
+	"android/soong/android"
 )
 
 func TestExpandVars(t *testing.T) {
+	android_arm64_config := android.TestConfig("out", nil, "", nil)
+	android_arm64_config.BuildOS = android.Android
+	android_arm64_config.BuildArch = android.Arm64
+
 	testCases := []struct {
 		description     string
+		config          android.Config
 		stringScope     exportedStringVariables
 		stringListScope exportedStringListVariables
+		configVars      exportedConfigDependingVariables
 		toExpand        string
 		expectedValues  []string
 	}{
@@ -57,7 +65,7 @@
 				"bar": []string{"baz", "${qux}"},
 			},
 			toExpand:       "${foo}",
-			expectedValues: []string{"baz", "hello"},
+			expectedValues: []string{"baz hello"},
 		},
 		{
 			description: "double level expansion",
@@ -75,7 +83,7 @@
 				"b": []string{"d"},
 			},
 			toExpand:       "${a}",
-			expectedValues: []string{"d", "c"},
+			expectedValues: []string{"d c"},
 		},
 		{
 			description: "double level expansion, with two variables in a string",
@@ -85,7 +93,7 @@
 				"c": []string{"e"},
 			},
 			toExpand:       "${a}",
-			expectedValues: []string{"d", "e"},
+			expectedValues: []string{"d e"},
 		},
 		{
 			description: "triple level expansion with two variables in a string",
@@ -96,13 +104,38 @@
 				"d": []string{"foo"},
 			},
 			toExpand:       "${a}",
-			expectedValues: []string{"foo", "foo", "foo"},
+			expectedValues: []string{"foo foo foo"},
+		},
+		{
+			description: "expansion with config depending vars",
+			configVars: exportedConfigDependingVariables{
+				"a": func(c android.Config) string { return c.BuildOS.String() },
+				"b": func(c android.Config) string { return c.BuildArch.String() },
+			},
+			config:         android_arm64_config,
+			toExpand:       "${a}-${b}",
+			expectedValues: []string{"android-arm64"},
+		},
+		{
+			description: "double level multi type expansion",
+			stringListScope: exportedStringListVariables{
+				"platform": []string{"${os}-${arch}"},
+				"const":    []string{"const"},
+			},
+			configVars: exportedConfigDependingVariables{
+				"os":   func(c android.Config) string { return c.BuildOS.String() },
+				"arch": func(c android.Config) string { return c.BuildArch.String() },
+				"foo":  func(c android.Config) string { return "foo" },
+			},
+			config:         android_arm64_config,
+			toExpand:       "${const}/${platform}/${foo}",
+			expectedValues: []string{"const/android-arm64/foo"},
 		},
 	}
 
 	for _, testCase := range testCases {
 		t.Run(testCase.description, func(t *testing.T) {
-			output := expandVar(testCase.toExpand, testCase.stringScope, testCase.stringListScope)
+			output, _ := expandVar(testCase.config, testCase.toExpand, testCase.stringScope, testCase.stringListScope, testCase.configVars)
 			if len(output) != len(testCase.expectedValues) {
 				t.Errorf("Expected %d values, got %d", len(testCase.expectedValues), len(output))
 			}
@@ -119,6 +152,7 @@
 func TestBazelToolchainVars(t *testing.T) {
 	testCases := []struct {
 		name        string
+		config      android.Config
 		vars        []bazelVarExporter
 		expectedOut string
 	}{
@@ -248,7 +282,7 @@
 
 	for _, tc := range testCases {
 		t.Run(tc.name, func(t *testing.T) {
-			out := bazelToolchainVars(tc.vars...)
+			out := bazelToolchainVars(tc.config, tc.vars...)
 			if out != tc.expectedOut {
 				t.Errorf("Expected \n%s, got \n%s", tc.expectedOut, out)
 			}
diff --git a/cc/config/x86_darwin_host.go b/cc/config/darwin_host.go
similarity index 73%
rename from cc/config/x86_darwin_host.go
rename to cc/config/darwin_host.go
index ecdcbde..318acb4 100644
--- a/cc/config/x86_darwin_host.go
+++ b/cc/config/darwin_host.go
@@ -53,12 +53,7 @@
 	}
 
 	darwinSupportedSdkVersions = []string{
-		"10.13",
-		"10.14",
-		"10.15",
-		"11.0",
-		"11.1",
-		"11.3",
+		"11",
 	}
 
 	darwinAvailableLibraries = append(
@@ -138,7 +133,7 @@
 				return ""
 			}
 
-			bytes, err := exec.Command(xcrunTool, args...).Output()
+			bytes, err := exec.Command(xcrunTool, append([]string{"--sdk", "macosx"}, args...)...).Output()
 			if err != nil {
 				macTools.err = fmt.Errorf("xcrun %q failed with: %q", args, err)
 				return ""
@@ -147,27 +142,19 @@
 			return strings.TrimSpace(string(bytes))
 		}
 
-		xcrunSdk := func(arg string) string {
-			if selected := ctx.Config().Getenv("MAC_SDK_VERSION"); selected != "" {
-				if !inList(selected, darwinSupportedSdkVersions) {
-					macTools.err = fmt.Errorf("MAC_SDK_VERSION %s isn't supported: %q", selected, darwinSupportedSdkVersions)
-					return ""
-				}
-
-				return xcrun("--sdk", "macosx"+selected, arg)
+		sdkVersion := xcrun("--show-sdk-version")
+		sdkVersionSupported := false
+		for _, version := range darwinSupportedSdkVersions {
+			if version == sdkVersion || strings.HasPrefix(sdkVersion, version+".") {
+				sdkVersionSupported = true
 			}
-
-			for _, sdk := range darwinSupportedSdkVersions {
-				bytes, err := exec.Command(xcrunTool, "--sdk", "macosx"+sdk, arg).Output()
-				if err == nil {
-					return strings.TrimSpace(string(bytes))
-				}
-			}
-			macTools.err = fmt.Errorf("Could not find a supported mac sdk: %q", darwinSupportedSdkVersions)
-			return ""
+		}
+		if !sdkVersionSupported {
+			macTools.err = fmt.Errorf("Unsupported macOS SDK version %q not in %v", sdkVersion, darwinSupportedSdkVersions)
+			return
 		}
 
-		macTools.sdkRoot = xcrunSdk("--show-sdk-path")
+		macTools.sdkRoot = xcrun("--show-sdk-path")
 
 		macTools.arPath = xcrun("--find", "ar")
 		macTools.stripPath = xcrun("--find", "strip")
@@ -184,19 +171,43 @@
 	toolchain64Bit
 }
 
-func (t *toolchainDarwin) Name() string {
+type toolchainDarwinX86 struct {
+	toolchainDarwin
+}
+
+type toolchainDarwinArm struct {
+	toolchainDarwin
+}
+
+func (t *toolchainDarwinArm) Name() string {
+	return "arm64"
+}
+
+func (t *toolchainDarwinX86) Name() string {
 	return "x86_64"
 }
 
-func (t *toolchainDarwin) GccRoot() string {
+func (t *toolchainDarwinArm) GccRoot() string {
+	panic("unimplemented")
+}
+
+func (t *toolchainDarwinArm) GccTriple() string {
+	panic("unimplemented")
+}
+
+func (t *toolchainDarwinArm) GccVersion() string {
+	panic("unimplemented")
+}
+
+func (t *toolchainDarwinX86) GccRoot() string {
 	return "${config.DarwinGccRoot}"
 }
 
-func (t *toolchainDarwin) GccTriple() string {
+func (t *toolchainDarwinX86) GccTriple() string {
 	return "${config.DarwinGccTriple}"
 }
 
-func (t *toolchainDarwin) GccVersion() string {
+func (t *toolchainDarwinX86) GccVersion() string {
 	return darwinGccVersion
 }
 
@@ -204,7 +215,11 @@
 	return ""
 }
 
-func (t *toolchainDarwin) ClangTriple() string {
+func (t *toolchainDarwinArm) ClangTriple() string {
+	return "aarch64-apple-darwin"
+}
+
+func (t *toolchainDarwinX86) ClangTriple() string {
 	return "x86_64-apple-darwin"
 }
 
@@ -240,12 +255,18 @@
 	return "${config.MacToolPath}"
 }
 
-var toolchainDarwinSingleton Toolchain = &toolchainDarwin{}
+var toolchainDarwinArmSingleton Toolchain = &toolchainDarwinArm{}
+var toolchainDarwinX86Singleton Toolchain = &toolchainDarwinX86{}
 
-func darwinToolchainFactory(arch android.Arch) Toolchain {
-	return toolchainDarwinSingleton
+func darwinArmToolchainFactory(arch android.Arch) Toolchain {
+	return toolchainDarwinArmSingleton
+}
+
+func darwinX86ToolchainFactory(arch android.Arch) Toolchain {
+	return toolchainDarwinX86Singleton
 }
 
 func init() {
-	registerToolchainFactory(android.Darwin, android.X86_64, darwinToolchainFactory)
+	registerToolchainFactory(android.Darwin, android.Arm64, darwinArmToolchainFactory)
+	registerToolchainFactory(android.Darwin, android.X86_64, darwinX86ToolchainFactory)
 }
diff --git a/cc/config/global.go b/cc/config/global.go
index 6108d3d..7abd2fc 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -404,7 +404,7 @@
 	pctx.StaticVariableWithEnvOverride("REAbiLinkerExecStrategy", "RBE_ABI_LINKER_EXEC_STRATEGY", remoteexec.LocalExecStrategy)
 }
 
-var HostPrebuiltTag = pctx.VariableConfigMethod("HostPrebuiltTag", android.Config.PrebuiltOS)
+var HostPrebuiltTag = exportVariableConfigMethod("HostPrebuiltTag", android.Config.PrebuiltOS)
 
 func ClangPath(ctx android.PathContext, file string) android.SourcePath {
 	type clangToolKey string
diff --git a/cc/config/toolchain.go b/cc/config/toolchain.go
index 20384a8..6320dbb 100644
--- a/cc/config/toolchain.go
+++ b/cc/config/toolchain.go
@@ -95,8 +95,6 @@
 
 	YasmFlags() string
 
-	WindresFlags() string
-
 	Is64Bit() bool
 
 	ShlibSuffix() string
@@ -169,10 +167,6 @@
 	return ""
 }
 
-func (toolchainBase) WindresFlags() string {
-	return ""
-}
-
 func (toolchainBase) LibclangRuntimeLibraryArch() string {
 	return ""
 }
diff --git a/cc/config/vndk.go b/cc/config/vndk.go
index b2e164f..9577a8c 100644
--- a/cc/config/vndk.go
+++ b/cc/config/vndk.go
@@ -30,6 +30,8 @@
 	"android.hardware.gnss-V1-ndk_platform",
 	"android.hardware.gnss-ndk_platform",
 	"android.hardware.gnss-unstable-ndk_platform",
+	"android.hardware.health-V1-ndk",
+	"android.hardware.health-ndk",
 	"android.hardware.health.storage-V1-ndk",
 	"android.hardware.health.storage-V1-ndk_platform",
 	"android.hardware.health.storage-ndk_platform",
@@ -101,6 +103,7 @@
 	"android.system.keystore2-V1-ndk",
 	"android.hardware.wifi.hostapd-V1-ndk",
 	"android.hardware.wifi.hostapd-V1-ndk_platform",
+	"android.hardware.wifi.supplicant-V1-ndk",
 	"android.system.keystore2-V1-ndk_platform",
 	"android.system.keystore2-ndk_platform",
 	"android.system.keystore2-unstable-ndk_platform",
diff --git a/cc/config/x86_linux_host.go b/cc/config/x86_linux_host.go
index ac5d5f7..43333fa 100644
--- a/cc/config/x86_linux_host.go
+++ b/cc/config/x86_linux_host.go
@@ -120,40 +120,40 @@
 )
 
 func init() {
-	pctx.StaticVariable("LinuxGccVersion", linuxGccVersion)
-	pctx.StaticVariable("LinuxGlibcVersion", linuxGlibcVersion)
+	exportStringStaticVariable("LinuxGccVersion", linuxGccVersion)
+	exportStringStaticVariable("LinuxGlibcVersion", linuxGlibcVersion)
+
 	// Most places use the full GCC version. A few only use up to the first two numbers.
 	if p := strings.Split(linuxGccVersion, "."); len(p) > 2 {
-		pctx.StaticVariable("ShortLinuxGccVersion", strings.Join(p[:2], "."))
+		exportStringStaticVariable("ShortLinuxGccVersion", strings.Join(p[:2], "."))
 	} else {
-		pctx.StaticVariable("ShortLinuxGccVersion", linuxGccVersion)
+		exportStringStaticVariable("ShortLinuxGccVersion", linuxGccVersion)
 	}
 
-	pctx.SourcePathVariable("LinuxGccRoot",
-		"prebuilts/gcc/${HostPrebuiltTag}/host/x86_64-linux-glibc${LinuxGlibcVersion}-${ShortLinuxGccVersion}")
+	exportSourcePathVariable("LinuxGccRoot",
+		"prebuilts/gcc/linux-x86/host/x86_64-linux-glibc${LinuxGlibcVersion}-${ShortLinuxGccVersion}")
 
-	pctx.StaticVariable("LinuxGccTriple", "x86_64-linux")
+	exportStringListStaticVariable("LinuxGccTriple", []string{"x86_64-linux"})
 
-	pctx.StaticVariable("LinuxCflags", strings.Join(linuxCflags, " "))
-	pctx.StaticVariable("LinuxLdflags", strings.Join(linuxLdflags, " "))
-	pctx.StaticVariable("LinuxLldflags", strings.Join(linuxLdflags, " "))
+	exportStringListStaticVariable("LinuxCflags", linuxCflags)
+	exportStringListStaticVariable("LinuxLdflags", linuxLdflags)
+	exportStringListStaticVariable("LinuxLldflags", linuxLdflags)
+	exportStringListStaticVariable("LinuxGlibcCflags", linuxGlibcCflags)
+	exportStringListStaticVariable("LinuxGlibcLdflags", linuxGlibcLdflags)
+	exportStringListStaticVariable("LinuxGlibcLldflags", linuxGlibcLdflags)
+	exportStringListStaticVariable("LinuxMuslCflags", linuxMuslCflags)
+	exportStringListStaticVariable("LinuxMuslLdflags", linuxMuslLdflags)
+	exportStringListStaticVariable("LinuxMuslLldflags", linuxMuslLdflags)
 
-	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, " "))
+	exportStringListStaticVariable("LinuxX86Cflags", linuxX86Cflags)
+	exportStringListStaticVariable("LinuxX8664Cflags", linuxX8664Cflags)
+	exportStringListStaticVariable("LinuxX86Ldflags", linuxX86Ldflags)
+	exportStringListStaticVariable("LinuxX86Lldflags", linuxX86Ldflags)
+	exportStringListStaticVariable("LinuxX8664Ldflags", linuxX8664Ldflags)
+	exportStringListStaticVariable("LinuxX8664Lldflags", 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, " "))
+	exportStringListStaticVariable("LinuxX86YasmFlags", []string{"-f elf32 -m x86"})
+	exportStringListStaticVariable("LinuxX8664YasmFlags", []string{"-f elf64 -m amd64"})
 }
 
 type toolchainLinux struct {
diff --git a/cc/config/x86_windows_host.go b/cc/config/x86_windows_host.go
index d9a7537..9daf40f 100644
--- a/cc/config/x86_windows_host.go
+++ b/cc/config/x86_windows_host.go
@@ -188,14 +188,6 @@
 	return "${config.WindowsIncludeFlags}"
 }
 
-func (t *toolchainWindowsX86) WindresFlags() string {
-	return "-F pe-i386"
-}
-
-func (t *toolchainWindowsX8664) WindresFlags() string {
-	return "-F pe-x86-64"
-}
-
 func (t *toolchainWindowsX86) ClangTriple() string {
 	return "i686-windows-gnu"
 }
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 40f16f3..e987fe4 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -123,7 +123,7 @@
 		}
 		// Discard installable:false libraries because they are expected to be absent
 		// in runtime.
-		if !proptools.BoolDefault(ccLibrary.Properties.Installable, true) {
+		if !proptools.BoolDefault(ccLibrary.Installable(), true) {
 			return false
 		}
 	}
diff --git a/cc/gen.go b/cc/gen.go
index 3a1a0e2..8f62363 100644
--- a/cc/gen.go
+++ b/cc/gen.go
@@ -45,13 +45,6 @@
 			CommandDeps: []string{"$syspropCmd"},
 		},
 		"headerOutDir", "publicOutDir", "srcOutDir", "includeName")
-
-	windmc = pctx.AndroidStaticRule("windmc",
-		blueprint.RuleParams{
-			Command:     "$windmcCmd -r$$(dirname $out) -h$$(dirname $out) $in",
-			CommandDeps: []string{"$windmcCmd"},
-		},
-		"windmcCmd")
 )
 
 type YaccProperties struct {
@@ -200,26 +193,6 @@
 	return cppFile, headers.Paths()
 }
 
-func genWinMsg(ctx android.ModuleContext, srcFile android.Path, flags builderFlags) (android.Path, android.Path) {
-	headerFile := android.GenPathWithExt(ctx, "windmc", srcFile, "h")
-	rcFile := android.GenPathWithExt(ctx, "windmc", srcFile, "rc")
-
-	windmcCmd := mingwCmd(flags.toolchain, "windmc")
-
-	ctx.Build(pctx, android.BuildParams{
-		Rule:           windmc,
-		Description:    "windmc " + srcFile.Rel(),
-		Output:         rcFile,
-		ImplicitOutput: headerFile,
-		Input:          srcFile,
-		Args: map[string]string{
-			"windmcCmd": windmcCmd,
-		},
-	})
-
-	return rcFile, headerFile
-}
-
 // Used to communicate information from the genSources method back to the library code that uses
 // it.
 type generatedSourceInfo struct {
@@ -305,10 +278,6 @@
 			cppFile := rsGeneratedCppFile(ctx, srcFile)
 			rsFiles = append(rsFiles, srcFiles[i])
 			srcFiles[i] = cppFile
-		case ".mc":
-			rcFile, headerFile := genWinMsg(ctx, srcFile, buildFlags)
-			srcFiles[i] = rcFile
-			deps = append(deps, headerFile)
 		case ".sysprop":
 			cppFile, headerFiles := genSysprop(ctx, srcFile)
 			srcFiles[i] = cppFile
diff --git a/cc/installer.go b/cc/installer.go
index f95b493..2522610 100644
--- a/cc/installer.go
+++ b/cc/installer.go
@@ -29,6 +29,9 @@
 	// 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"`
+
+	// Install output directly in {partition}/xbin
+	Install_in_xbin *bool `android:"arch_vvariant"`
 }
 
 type installLocation int
@@ -73,6 +76,8 @@
 
 	if installer.installInRoot() {
 		dir = ""
+	} else if installer.installInXbin() {
+		dir = "xbin"
 	}
 
 	if ctx.Target().NativeBridge == android.NativeBridgeEnabled {
@@ -123,3 +128,7 @@
 func (installer *baseInstaller) installInRoot() bool {
 	return Bool(installer.Properties.Install_in_root)
 }
+
+func (installer *baseInstaller) installInXbin() bool {
+	return Bool(installer.Properties.Install_in_xbin)
+}
diff --git a/cc/library.go b/cc/library.go
index ed4d3d2..c3f7305 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -159,6 +159,8 @@
 	Export_static_lib_headers []string `android:"arch_variant"`
 
 	Apex_available []string `android:"arch_variant"`
+
+	Installable *bool `android:"arch_variant"`
 }
 
 type LibraryMutatedProperties struct {
@@ -293,8 +295,9 @@
 
 	sharedAttrs := bp2BuildParseSharedProps(ctx, m)
 	staticAttrs := bp2BuildParseStaticProps(ctx, m)
-	compilerAttrs := bp2BuildParseCompilerProps(ctx, m)
-	linkerAttrs := bp2BuildParseLinkerProps(ctx, m)
+	baseAttributes := bp2BuildParseBaseProps(ctx, m)
+	compilerAttrs := baseAttributes.compilerAttributes
+	linkerAttrs := baseAttributes.linkerAttributes
 	exportedIncludes := bp2BuildParseExportedIncludes(ctx, m)
 
 	srcs := compilerAttrs.srcs
@@ -309,6 +312,7 @@
 		Srcs:    srcs,
 		Srcs_c:  compilerAttrs.cSrcs,
 		Srcs_as: compilerAttrs.asSrcs,
+		Hdrs:    compilerAttrs.hdrs,
 
 		Copts:      compilerAttrs.copts,
 		Cppflags:   compilerAttrs.cppFlags,
@@ -639,7 +643,7 @@
 
 func (handler *ccLibraryBazelHandler) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool {
 	bazelCtx := ctx.Config().BazelContext
-	ccInfo, ok, err := bazelCtx.GetCcInfo(label, ctx.Arch().ArchType)
+	ccInfo, ok, err := bazelCtx.GetCcInfo(label, android.GetConfigKey(ctx))
 	if err != nil {
 		ctx.ModuleErrorf("Error getting Bazel CcInfo: %s", err)
 		return false
@@ -1046,6 +1050,8 @@
 	availableFor(string) bool
 
 	getAPIListCoverageXMLPath() android.ModuleOutPath
+
+	installable() *bool
 }
 
 type versionedInterface interface {
@@ -1288,7 +1294,7 @@
 		}
 	}
 
-	transformObjToStaticLib(ctx, library.objects.objFiles, deps.WholeStaticLibsFromPrebuilts, builderFlags, outputFile, objs.tidyFiles)
+	transformObjToStaticLib(ctx, library.objects.objFiles, deps.WholeStaticLibsFromPrebuilts, builderFlags, outputFile, nil, objs.tidyFiles)
 
 	library.coverageOutputFile = transformCoverageFilesToZip(ctx, library.objects, ctx.ModuleName())
 
@@ -1417,10 +1423,9 @@
 	linkerDeps = append(linkerDeps, deps.EarlySharedLibsDeps...)
 	linkerDeps = append(linkerDeps, deps.SharedLibsDeps...)
 	linkerDeps = append(linkerDeps, deps.LateSharedLibsDeps...)
-	linkerDeps = append(linkerDeps, objs.tidyFiles...)
 	transformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs,
 		deps.StaticLibs, deps.LateStaticLibs, deps.WholeStaticLibs,
-		linkerDeps, deps.CrtBegin, deps.CrtEnd, false, builderFlags, outputFile, implicitOutputs, nil)
+		linkerDeps, deps.CrtBegin, deps.CrtEnd, false, builderFlags, outputFile, implicitOutputs, objs.tidyFiles)
 
 	objs.coverageFiles = append(objs.coverageFiles, deps.StaticLibObjs.coverageFiles...)
 	objs.coverageFiles = append(objs.coverageFiles, deps.WholeStaticLibObjs.coverageFiles...)
@@ -1971,6 +1976,15 @@
 	return android.CheckAvailableForApex(what, list)
 }
 
+func (library *libraryDecorator) installable() *bool {
+	if library.static() {
+		return library.StaticProperties.Static.Installable
+	} else if library.shared() {
+		return library.SharedProperties.Shared.Installable
+	}
+	return nil
+}
+
 func (library *libraryDecorator) makeUninstallable(mod *Module) {
 	if library.static() && library.buildStatic() && !library.buildStubs() {
 		// If we're asked to make a static library uninstallable we don't do
@@ -2357,8 +2371,10 @@
 	}
 	isStatic := modType == "cc_library_static"
 
-	compilerAttrs := bp2BuildParseCompilerProps(ctx, module)
-	linkerAttrs := bp2BuildParseLinkerProps(ctx, module)
+	baseAttributes := bp2BuildParseBaseProps(ctx, module)
+	compilerAttrs := baseAttributes.compilerAttributes
+	linkerAttrs := baseAttributes.linkerAttributes
+
 	exportedIncludes := bp2BuildParseExportedIncludes(ctx, module)
 
 	// Append shared/static{} stanza properties. These won't be specified on
@@ -2388,6 +2404,7 @@
 		Srcs_c:  compilerAttrs.cSrcs,
 		Srcs_as: compilerAttrs.asSrcs,
 		Copts:   compilerAttrs.copts,
+		Hdrs:    compilerAttrs.hdrs,
 
 		Deps:                        linkerAttrs.deps,
 		Implementation_deps:         linkerAttrs.implementationDeps,
diff --git a/cc/library_headers.go b/cc/library_headers.go
index 51c1eb8..ede6ab3 100644
--- a/cc/library_headers.go
+++ b/cc/library_headers.go
@@ -57,7 +57,7 @@
 
 func (h *libraryHeaderBazelHander) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool {
 	bazelCtx := ctx.Config().BazelContext
-	ccInfo, ok, err := bazelCtx.GetCcInfo(label, ctx.Arch().ArchType)
+	ccInfo, ok, err := bazelCtx.GetCcInfo(label, android.GetConfigKey(ctx))
 	if err != nil {
 		ctx.ModuleErrorf("Error getting Bazel CcInfo: %s", err)
 		return false
@@ -132,7 +132,8 @@
 	}
 
 	exportedIncludes := bp2BuildParseExportedIncludes(ctx, module)
-	linkerAttrs := bp2BuildParseLinkerProps(ctx, module)
+	baseAttributes := bp2BuildParseBaseProps(ctx, module)
+	linkerAttrs := baseAttributes.linkerAttributes
 
 	attrs := &bazelCcLibraryHeadersAttributes{
 		Export_includes:        exportedIncludes.Includes,
@@ -140,6 +141,7 @@
 		Implementation_deps:    linkerAttrs.implementationDeps,
 		Deps:                   linkerAttrs.deps,
 		System_dynamic_deps:    linkerAttrs.systemDynamicDeps,
+		Hdrs:                   baseAttributes.hdrs,
 	}
 
 	props := bazel.BazelTargetModuleProperties{
diff --git a/cc/ndk_sysroot.go b/cc/ndk_sysroot.go
index 2726b1a..fd458d9 100644
--- a/cc/ndk_sysroot.go
+++ b/cc/ndk_sysroot.go
@@ -33,7 +33,7 @@
 // Component 3: Stub Libraries
 // The shared libraries in the NDK are not the actual shared libraries they
 // refer to (to prevent people from accidentally loading them), but stub
-// libraries with dummy implementations of everything for use at build time
+// libraries with placeholder implementations of everything for use at build time
 // only.
 //
 // Since we don't actually need to know anything about the stub libraries aside
diff --git a/cc/object.go b/cc/object.go
index d8bb08f..0327a45 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -54,7 +54,7 @@
 
 func (handler *objectBazelHandler) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool {
 	bazelCtx := ctx.Config().BazelContext
-	objPaths, ok := bazelCtx.GetOutputFiles(label, ctx.Arch().ArchType)
+	objPaths, ok := bazelCtx.GetOutputFiles(label, android.GetConfigKey(ctx))
 	if ok {
 		if len(objPaths) != 1 {
 			ctx.ModuleErrorf("expected exactly one object file for '%s', but got %s", label, objPaths)
@@ -154,7 +154,8 @@
 	}
 
 	// Set arch-specific configurable attributes
-	compilerAttrs := bp2BuildParseCompilerProps(ctx, m)
+	baseAttributes := bp2BuildParseBaseProps(ctx, m)
+	compilerAttrs := baseAttributes.compilerAttributes
 	var deps bazel.LabelListAttribute
 	systemDynamicDeps := bazel.LabelListAttribute{ForceSpecifyEmptyList: true}
 
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index d324241..3401e36 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -16,9 +16,9 @@
 
 import (
 	"path/filepath"
-	"strings"
 
 	"android/soong/android"
+	"android/soong/bazel"
 )
 
 func init() {
@@ -32,6 +32,8 @@
 	ctx.RegisterModuleType("cc_prebuilt_test_library_shared", PrebuiltSharedTestLibraryFactory)
 	ctx.RegisterModuleType("cc_prebuilt_object", prebuiltObjectFactory)
 	ctx.RegisterModuleType("cc_prebuilt_binary", prebuiltBinaryFactory)
+
+	android.RegisterBp2BuildMutator("cc_prebuilt_library_shared", PrebuiltLibrarySharedBp2Build)
 }
 
 type prebuiltLinkerInterface interface {
@@ -232,7 +234,7 @@
 
 // Implements versionedInterface
 func (p *prebuiltLibraryLinker) implementationModuleName(name string) string {
-	return strings.TrimPrefix(name, "prebuilt_")
+	return android.RemoveOptionalPrebuiltPrefix(name)
 }
 
 func NewPrebuiltLibrary(hod android.HostOrDeviceSupported) (*Module, *libraryDecorator) {
@@ -310,6 +312,42 @@
 	return module, library
 }
 
+type bazelPrebuiltLibrarySharedAttributes struct {
+	Shared_library bazel.LabelAttribute
+}
+
+func PrebuiltLibrarySharedBp2Build(ctx android.TopDownMutatorContext) {
+	module, ok := ctx.Module().(*Module)
+	if !ok {
+		// Not a cc module
+		return
+	}
+	if !module.ConvertWithBp2build(ctx) {
+		return
+	}
+	if ctx.ModuleType() != "cc_prebuilt_library_shared" {
+		return
+	}
+
+	prebuiltLibrarySharedBp2BuildInternal(ctx, module)
+}
+
+func prebuiltLibrarySharedBp2BuildInternal(ctx android.TopDownMutatorContext, module *Module) {
+	prebuiltAttrs := Bp2BuildParsePrebuiltLibraryProps(ctx, module)
+
+	attrs := &bazelPrebuiltLibrarySharedAttributes{
+		Shared_library: prebuiltAttrs.Src,
+	}
+
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class:        "prebuilt_library_shared",
+		Bzl_load_location: "//build/bazel/rules:prebuilt_library_shared.bzl",
+	}
+
+	name := android.RemoveOptionalPrebuiltPrefix(module.Name())
+	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: name}, attrs)
+}
+
 type prebuiltObjectProperties struct {
 	Srcs []string `android:"path,arch_variant"`
 }
@@ -330,7 +368,7 @@
 
 func (h *prebuiltStaticLibraryBazelHandler) GenerateBazelBuildActions(ctx android.ModuleContext, label string) bool {
 	bazelCtx := ctx.Config().BazelContext
-	ccInfo, ok, err := bazelCtx.GetCcInfo(label, ctx.Arch().ArchType)
+	ccInfo, ok, err := bazelCtx.GetCcInfo(label, android.GetConfigKey(ctx))
 	if err != nil {
 		ctx.ModuleErrorf("Error getting Bazel CcInfo: %s", err)
 	}
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index c5e8896..e9eabd3 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -217,15 +217,6 @@
 	generateModuleGraphFile := moduleGraphFile != ""
 	generateDocFile := docFile != ""
 
-	blueprintArgs := cmdlineArgs
-
-	var stopBefore bootstrap.StopBefore
-	if !generateModuleGraphFile && !generateQueryView && !generateDocFile {
-		stopBefore = bootstrap.DoEverything
-	} else {
-		stopBefore = bootstrap.StopBeforePrepareBuildActions
-	}
-
 	if generateBazelWorkspace {
 		// Run the alternate pipeline of bp2build mutators and singleton to convert
 		// Blueprint to BUILD files before everything else.
@@ -233,6 +224,19 @@
 		return bp2buildMarker
 	}
 
+	blueprintArgs := cmdlineArgs
+
+	var stopBefore bootstrap.StopBefore
+	if generateModuleGraphFile {
+		stopBefore = bootstrap.StopBeforeWriteNinja
+	} else if generateQueryView {
+		stopBefore = bootstrap.StopBeforePrepareBuildActions
+	} else if generateDocFile {
+		stopBefore = bootstrap.StopBeforePrepareBuildActions
+	} else {
+		stopBefore = bootstrap.DoEverything
+	}
+
 	ctx := newContext(configuration)
 	if mixedModeBuild {
 		runMixedModeBuild(configuration, ctx, extraNinjaDeps)
diff --git a/compliance/build_license_metadata/Android.bp b/compliance/build_license_metadata/Android.bp
new file mode 100644
index 0000000..5000346
--- /dev/null
+++ b/compliance/build_license_metadata/Android.bp
@@ -0,0 +1,29 @@
+// 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+blueprint_go_binary {
+    name: "build_license_metadata",
+    srcs: [
+        "build_license_metadata.go",
+    ],
+    deps: [
+        "license_metadata_proto",
+        "golang-protobuf-proto",
+        "golang-protobuf-encoding-prototext",
+    ],
+}
diff --git a/compliance/build_license_metadata/build_license_metadata.go b/compliance/build_license_metadata/build_license_metadata.go
new file mode 100644
index 0000000..ba3cc3e
--- /dev/null
+++ b/compliance/build_license_metadata/build_license_metadata.go
@@ -0,0 +1,174 @@
+// 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 (
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"path/filepath"
+	"strings"
+
+	"google.golang.org/protobuf/encoding/prototext"
+	"google.golang.org/protobuf/proto"
+
+	"android/soong/compliance/license_metadata_proto"
+)
+
+var (
+	packageName  = flag.String("p", "", "license package name")
+	moduleType   = newMultiString("mt", "module type")
+	moduleClass  = newMultiString("mc", "module class")
+	kinds        = newMultiString("k", "license kinds")
+	conditions   = newMultiString("c", "license conditions")
+	notices      = newMultiString("n", "license notice file")
+	deps         = newMultiString("d", "license metadata file dependency")
+	sources      = newMultiString("s", "source (input) dependency")
+	built        = newMultiString("t", "built targets")
+	installed    = newMultiString("i", "installed targets")
+	roots        = newMultiString("r", "root directory of project")
+	installedMap = newMultiString("m", "map dependent targets to their installed names")
+	isContainer  = flag.Bool("is_container", false, "preserved dependent target name when given")
+	outFile      = flag.String("o", "", "output file")
+)
+
+func newMultiString(name, usage string) *multiString {
+	var f multiString
+	flag.Var(&f, name, usage)
+	return &f
+}
+
+type multiString []string
+
+func (ms *multiString) String() string     { return strings.Join(*ms, ", ") }
+func (ms *multiString) Set(s string) error { *ms = append(*ms, s); return nil }
+
+func main() {
+	flag.Parse()
+
+	metadata := license_metadata_proto.LicenseMetadata{}
+	metadata.PackageName = proto.String(*packageName)
+	metadata.ModuleTypes = *moduleType
+	metadata.ModuleClasses = *moduleClass
+	metadata.IsContainer = proto.Bool(*isContainer)
+	metadata.Projects = findGitRoots(*roots)
+	metadata.LicenseKinds = *kinds
+	metadata.LicenseConditions = *conditions
+	metadata.LicenseTexts = *notices
+	metadata.Built = *built
+	metadata.Installed = *installed
+	metadata.InstallMap = convertInstalledMap(*installedMap)
+	metadata.Sources = *sources
+	metadata.Deps = convertDependencies(*deps)
+
+	err := writeMetadata(*outFile, &metadata)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "error: %s\n", err.Error())
+		os.Exit(2)
+	}
+}
+
+func findGitRoots(dirs []string) []string {
+	ret := make([]string, len(dirs))
+	for i, dir := range dirs {
+		ret[i] = findGitRoot(dir)
+	}
+	return ret
+}
+
+// findGitRoot finds the directory at or above dir that contains a ".git" directory.  This isn't
+// guaranteed to exist, for example during remote execution, when sandboxed, when building from
+// infrastructure that doesn't use git, or when the .git directory has been removed to save space,
+// but it should be good enough for local builds.  If no .git directory is found the original value
+// is returned.
+func findGitRoot(dir string) string {
+	orig := dir
+	for dir != "" && dir != "." && dir != "/" {
+		_, err := os.Stat(filepath.Join(dir, ".git"))
+		if err == nil {
+			// Found dir/.git, return dir.
+			return dir
+		} else if !os.IsNotExist(err) {
+			// Error finding .git, return original input.
+			return orig
+		}
+		dir, _ = filepath.Split(dir)
+		dir = strings.TrimSuffix(dir, "/")
+	}
+	return orig
+}
+
+// convertInstalledMap converts a list of colon-separated from:to pairs into InstallMap proto
+// messages.
+func convertInstalledMap(installMaps []string) []*license_metadata_proto.InstallMap {
+	var ret []*license_metadata_proto.InstallMap
+
+	for _, installMap := range installMaps {
+		components := strings.Split(installMap, ":")
+		if len(components) != 2 {
+			panic(fmt.Errorf("install map entry %q contains %d colons, expected 1", len(components)-1))
+		}
+		ret = append(ret, &license_metadata_proto.InstallMap{
+			FromPath:      proto.String(components[0]),
+			ContainerPath: proto.String(components[1]),
+		})
+	}
+
+	return ret
+}
+
+// convertDependencies converts a colon-separated tuple of dependency:annotation:annotation...
+// into AnnotatedDependency proto messages.
+func convertDependencies(deps []string) []*license_metadata_proto.AnnotatedDependency {
+	var ret []*license_metadata_proto.AnnotatedDependency
+
+	for _, d := range deps {
+		components := strings.Split(d, ":")
+		dep := components[0]
+		components = components[1:]
+		ad := &license_metadata_proto.AnnotatedDependency{
+			File: proto.String(dep),
+			Annotations: make([]string, 0, len(components)),
+		}
+		for _, ann := range components {
+			if len(ann) == 0 {
+				continue
+			}
+			ad.Annotations = append(ad.Annotations, ann)
+		}
+		ret = append(ret, ad)
+	}
+
+	return ret
+}
+
+func writeMetadata(file string, metadata *license_metadata_proto.LicenseMetadata) error {
+	buf, err := prototext.MarshalOptions{Multiline: true}.Marshal(metadata)
+	if err != nil {
+		return fmt.Errorf("error marshalling textproto: %w", err)
+	}
+
+	if file != "" {
+		err = ioutil.WriteFile(file, buf, 0666)
+		if err != nil {
+			return fmt.Errorf("error writing textproto %q: %w", file, err)
+		}
+	} else {
+		_, _ = os.Stdout.Write(buf)
+	}
+
+	return nil
+}
diff --git a/compliance/license_metadata_proto/Android.bp b/compliance/license_metadata_proto/Android.bp
new file mode 100644
index 0000000..3c041e4
--- /dev/null
+++ b/compliance/license_metadata_proto/Android.bp
@@ -0,0 +1,27 @@
+// 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 {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+    name: "license_metadata_proto",
+    pkgPath: "android/soong/compliance/license_metadata_proto",
+    srcs: ["license_metadata.pb.go"],
+    deps: [
+        "golang-protobuf-reflect-protoreflect",
+        "golang-protobuf-runtime-protoimpl",
+    ],
+}
diff --git a/compliance/license_metadata_proto/license_metadata.pb.go b/compliance/license_metadata_proto/license_metadata.pb.go
new file mode 100644
index 0000000..44dbc78
--- /dev/null
+++ b/compliance/license_metadata_proto/license_metadata.pb.go
@@ -0,0 +1,451 @@
+// 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.
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.27.1
+// 	protoc        v3.19.0
+// source: license_metadata.proto
+
+package license_metadata_proto
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+)
+
+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 LicenseMetadata struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// package_name identifies the source package. License texts are named relative to the package name.
+	PackageName   *string  `protobuf:"bytes,1,opt,name=package_name,json=packageName" json:"package_name,omitempty"`
+	ModuleTypes   []string `protobuf:"bytes,2,rep,name=module_types,json=moduleTypes" json:"module_types,omitempty"`
+	ModuleClasses []string `protobuf:"bytes,3,rep,name=module_classes,json=moduleClasses" json:"module_classes,omitempty"`
+	// projects identifies the git project(s) containing the associated source code.
+	Projects []string `protobuf:"bytes,4,rep,name=projects" json:"projects,omitempty"`
+	// license_kinds lists the kinds of licenses. e.g. SPDX-license-identifier-Apache-2.0 or legacy_notice.
+	LicenseKinds []string `protobuf:"bytes,5,rep,name=license_kinds,json=licenseKinds" json:"license_kinds,omitempty"`
+	// license_conditions lists the conditions that apply to the license kinds. e.g. notice or restricted.
+	LicenseConditions []string `protobuf:"bytes,6,rep,name=license_conditions,json=licenseConditions" json:"license_conditions,omitempty"`
+	// license_texts lists the filenames of the associated license text(s).
+	LicenseTexts []string `protobuf:"bytes,7,rep,name=license_texts,json=licenseTexts" json:"license_texts,omitempty"`
+	// is_container is true for target types that merely aggregate. e.g. .img or .zip files.
+	IsContainer *bool `protobuf:"varint,8,opt,name=is_container,json=isContainer" json:"is_container,omitempty"`
+	// built lists the built targets.
+	Built []string `protobuf:"bytes,9,rep,name=built" json:"built,omitempty"`
+	// installed lists the installed targets.
+	Installed []string `protobuf:"bytes,10,rep,name=installed" json:"installed,omitempty"`
+	// installMap identifies the substitutions to make to path names when moving into installed location.
+	InstallMap []*InstallMap `protobuf:"bytes,11,rep,name=install_map,json=installMap" json:"install_map,omitempty"`
+	// sources lists the targets depended on.
+	Sources []string `protobuf:"bytes,12,rep,name=sources" json:"sources,omitempty"`
+	// deps lists the license metadata files depended on.
+	Deps []*AnnotatedDependency `protobuf:"bytes,13,rep,name=deps" json:"deps,omitempty"`
+}
+
+func (x *LicenseMetadata) Reset() {
+	*x = LicenseMetadata{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_license_metadata_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *LicenseMetadata) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*LicenseMetadata) ProtoMessage() {}
+
+func (x *LicenseMetadata) ProtoReflect() protoreflect.Message {
+	mi := &file_license_metadata_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 LicenseMetadata.ProtoReflect.Descriptor instead.
+func (*LicenseMetadata) Descriptor() ([]byte, []int) {
+	return file_license_metadata_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *LicenseMetadata) GetPackageName() string {
+	if x != nil && x.PackageName != nil {
+		return *x.PackageName
+	}
+	return ""
+}
+
+func (x *LicenseMetadata) GetModuleTypes() []string {
+	if x != nil {
+		return x.ModuleTypes
+	}
+	return nil
+}
+
+func (x *LicenseMetadata) GetModuleClasses() []string {
+	if x != nil {
+		return x.ModuleClasses
+	}
+	return nil
+}
+
+func (x *LicenseMetadata) GetProjects() []string {
+	if x != nil {
+		return x.Projects
+	}
+	return nil
+}
+
+func (x *LicenseMetadata) GetLicenseKinds() []string {
+	if x != nil {
+		return x.LicenseKinds
+	}
+	return nil
+}
+
+func (x *LicenseMetadata) GetLicenseConditions() []string {
+	if x != nil {
+		return x.LicenseConditions
+	}
+	return nil
+}
+
+func (x *LicenseMetadata) GetLicenseTexts() []string {
+	if x != nil {
+		return x.LicenseTexts
+	}
+	return nil
+}
+
+func (x *LicenseMetadata) GetIsContainer() bool {
+	if x != nil && x.IsContainer != nil {
+		return *x.IsContainer
+	}
+	return false
+}
+
+func (x *LicenseMetadata) GetBuilt() []string {
+	if x != nil {
+		return x.Built
+	}
+	return nil
+}
+
+func (x *LicenseMetadata) GetInstalled() []string {
+	if x != nil {
+		return x.Installed
+	}
+	return nil
+}
+
+func (x *LicenseMetadata) GetInstallMap() []*InstallMap {
+	if x != nil {
+		return x.InstallMap
+	}
+	return nil
+}
+
+func (x *LicenseMetadata) GetSources() []string {
+	if x != nil {
+		return x.Sources
+	}
+	return nil
+}
+
+func (x *LicenseMetadata) GetDeps() []*AnnotatedDependency {
+	if x != nil {
+		return x.Deps
+	}
+	return nil
+}
+
+// InstallMap messages describe the mapping from an input filesystem file to the path to the file
+// in a container.
+type InstallMap struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The input path on the filesystem.
+	FromPath *string `protobuf:"bytes,1,opt,name=from_path,json=fromPath" json:"from_path,omitempty"`
+	// The path to the file inside the container or installed location.
+	ContainerPath *string `protobuf:"bytes,2,opt,name=container_path,json=containerPath" json:"container_path,omitempty"`
+}
+
+func (x *InstallMap) Reset() {
+	*x = InstallMap{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_license_metadata_proto_msgTypes[1]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *InstallMap) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*InstallMap) ProtoMessage() {}
+
+func (x *InstallMap) ProtoReflect() protoreflect.Message {
+	mi := &file_license_metadata_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 InstallMap.ProtoReflect.Descriptor instead.
+func (*InstallMap) Descriptor() ([]byte, []int) {
+	return file_license_metadata_proto_rawDescGZIP(), []int{1}
+}
+
+func (x *InstallMap) GetFromPath() string {
+	if x != nil && x.FromPath != nil {
+		return *x.FromPath
+	}
+	return ""
+}
+
+func (x *InstallMap) GetContainerPath() string {
+	if x != nil && x.ContainerPath != nil {
+		return *x.ContainerPath
+	}
+	return ""
+}
+
+// AnnotateDepencency messages describe edges in the build graph.
+type AnnotatedDependency struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// The path to the dependency license metadata file.
+	File *string `protobuf:"bytes,1,opt,name=file" json:"file,omitempty"`
+	// The annotations attached to the dependency.
+	Annotations []string `protobuf:"bytes,2,rep,name=annotations" json:"annotations,omitempty"`
+}
+
+func (x *AnnotatedDependency) Reset() {
+	*x = AnnotatedDependency{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_license_metadata_proto_msgTypes[2]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *AnnotatedDependency) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*AnnotatedDependency) ProtoMessage() {}
+
+func (x *AnnotatedDependency) ProtoReflect() protoreflect.Message {
+	mi := &file_license_metadata_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 AnnotatedDependency.ProtoReflect.Descriptor instead.
+func (*AnnotatedDependency) Descriptor() ([]byte, []int) {
+	return file_license_metadata_proto_rawDescGZIP(), []int{2}
+}
+
+func (x *AnnotatedDependency) GetFile() string {
+	if x != nil && x.File != nil {
+		return *x.File
+	}
+	return ""
+}
+
+func (x *AnnotatedDependency) GetAnnotations() []string {
+	if x != nil {
+		return x.Annotations
+	}
+	return nil
+}
+
+var File_license_metadata_proto protoreflect.FileDescriptor
+
+var file_license_metadata_proto_rawDesc = []byte{
+	0x0a, 0x16, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61,
+	0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x16, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f,
+	0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
+	0x22, 0x8a, 0x04, 0x0a, 0x0f, 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x4d, 0x65, 0x74, 0x61,
+	0x64, 0x61, 0x74, 0x61, 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, 0x21, 0x0a, 0x0c, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
+	0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0b, 0x6d,
+	0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x73, 0x12, 0x25, 0x0a, 0x0e, 0x6d, 0x6f,
+	0x64, 0x75, 0x6c, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03,
+	0x28, 0x09, 0x52, 0x0d, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x65,
+	0x73, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x18, 0x04, 0x20,
+	0x03, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x12, 0x23, 0x0a,
+	0x0d, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x6b, 0x69, 0x6e, 0x64, 0x73, 0x18, 0x05,
+	0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x4b, 0x69, 0x6e,
+	0x64, 0x73, 0x12, 0x2d, 0x0a, 0x12, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x63, 0x6f,
+	0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11,
+	0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x43, 0x6f, 0x6e, 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e,
+	0x73, 0x12, 0x23, 0x0a, 0x0d, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x74, 0x65, 0x78,
+	0x74, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73,
+	0x65, 0x54, 0x65, 0x78, 0x74, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x69, 0x73, 0x5f, 0x63, 0x6f, 0x6e,
+	0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x69, 0x73,
+	0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x14, 0x0a, 0x05, 0x62, 0x75, 0x69,
+	0x6c, 0x74, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x05, 0x62, 0x75, 0x69, 0x6c, 0x74, 0x12,
+	0x1c, 0x0a, 0x09, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x18, 0x0a, 0x20, 0x03,
+	0x28, 0x09, 0x52, 0x09, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x65, 0x64, 0x12, 0x43, 0x0a,
+	0x0b, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x5f, 0x6d, 0x61, 0x70, 0x18, 0x0b, 0x20, 0x03,
+	0x28, 0x0b, 0x32, 0x22, 0x2e, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6c, 0x69, 0x63, 0x65, 0x6e,
+	0x73, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x49, 0x6e, 0x73, 0x74,
+	0x61, 0x6c, 0x6c, 0x4d, 0x61, 0x70, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x4d,
+	0x61, 0x70, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x0c, 0x20,
+	0x03, 0x28, 0x09, 0x52, 0x07, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3f, 0x0a, 0x04,
+	0x64, 0x65, 0x70, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x62, 0x75, 0x69,
+	0x6c, 0x64, 0x5f, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64,
+	0x61, 0x74, 0x61, 0x2e, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x64, 0x44, 0x65, 0x70,
+	0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x52, 0x04, 0x64, 0x65, 0x70, 0x73, 0x22, 0x50, 0x0a,
+	0x0a, 0x49, 0x6e, 0x73, 0x74, 0x61, 0x6c, 0x6c, 0x4d, 0x61, 0x70, 0x12, 0x1b, 0x0a, 0x09, 0x66,
+	0x72, 0x6f, 0x6d, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08,
+	0x66, 0x72, 0x6f, 0x6d, 0x50, 0x61, 0x74, 0x68, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x74,
+	0x61, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x50, 0x61, 0x74, 0x68, 0x22,
+	0x4b, 0x0a, 0x13, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x64, 0x44, 0x65, 0x70, 0x65,
+	0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x6e,
+	0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52,
+	0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x42, 0x31, 0x5a, 0x2f,
+	0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x63, 0x6f,
+	0x6d, 0x70, 0x6c, 0x69, 0x61, 0x6e, 0x63, 0x65, 0x2f, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65,
+	0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+}
+
+var (
+	file_license_metadata_proto_rawDescOnce sync.Once
+	file_license_metadata_proto_rawDescData = file_license_metadata_proto_rawDesc
+)
+
+func file_license_metadata_proto_rawDescGZIP() []byte {
+	file_license_metadata_proto_rawDescOnce.Do(func() {
+		file_license_metadata_proto_rawDescData = protoimpl.X.CompressGZIP(file_license_metadata_proto_rawDescData)
+	})
+	return file_license_metadata_proto_rawDescData
+}
+
+var file_license_metadata_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
+var file_license_metadata_proto_goTypes = []interface{}{
+	(*LicenseMetadata)(nil),     // 0: build_license_metadata.LicenseMetadata
+	(*InstallMap)(nil),          // 1: build_license_metadata.InstallMap
+	(*AnnotatedDependency)(nil), // 2: build_license_metadata.AnnotatedDependency
+}
+var file_license_metadata_proto_depIdxs = []int32{
+	1, // 0: build_license_metadata.LicenseMetadata.install_map:type_name -> build_license_metadata.InstallMap
+	2, // 1: build_license_metadata.LicenseMetadata.deps:type_name -> build_license_metadata.AnnotatedDependency
+	2, // [2:2] is the sub-list for method output_type
+	2, // [2:2] is the sub-list for method input_type
+	2, // [2:2] is the sub-list for extension type_name
+	2, // [2:2] is the sub-list for extension extendee
+	0, // [0:2] is the sub-list for field type_name
+}
+
+func init() { file_license_metadata_proto_init() }
+func file_license_metadata_proto_init() {
+	if File_license_metadata_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_license_metadata_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*LicenseMetadata); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_license_metadata_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*InstallMap); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_license_metadata_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*AnnotatedDependency); 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_license_metadata_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   3,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_license_metadata_proto_goTypes,
+		DependencyIndexes: file_license_metadata_proto_depIdxs,
+		MessageInfos:      file_license_metadata_proto_msgTypes,
+	}.Build()
+	File_license_metadata_proto = out.File
+	file_license_metadata_proto_rawDesc = nil
+	file_license_metadata_proto_goTypes = nil
+	file_license_metadata_proto_depIdxs = nil
+}
diff --git a/compliance/license_metadata_proto/license_metadata.proto b/compliance/license_metadata_proto/license_metadata.proto
new file mode 100644
index 0000000..1b4f34f
--- /dev/null
+++ b/compliance/license_metadata_proto/license_metadata.proto
@@ -0,0 +1,76 @@
+// 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.
+
+syntax = "proto2";
+
+package build_license_metadata;
+option go_package = "android/soong/compliance/license_metadata_proto";
+
+message LicenseMetadata {
+  // package_name identifies the source package. License texts are named relative to the package name.
+  optional string package_name = 1;
+
+  repeated string module_types = 2;
+
+  repeated string module_classes = 3;
+
+  // projects identifies the git project(s) containing the associated source code.
+  repeated string projects = 4;
+
+  // license_kinds lists the kinds of licenses. e.g. SPDX-license-identifier-Apache-2.0 or legacy_notice.
+  repeated string license_kinds = 5;
+
+  // license_conditions lists the conditions that apply to the license kinds. e.g. notice or restricted.
+  repeated string license_conditions = 6;
+
+  // license_texts lists the filenames of the associated license text(s).
+  repeated string license_texts = 7;
+
+  // is_container is true for target types that merely aggregate. e.g. .img or .zip files.
+  optional bool is_container = 8;
+
+  // built lists the built targets.
+  repeated string built = 9;
+
+  // installed lists the installed targets.
+  repeated string installed = 10;
+
+  // installMap identifies the substitutions to make to path names when moving into installed location.
+  repeated InstallMap install_map = 11;
+
+  // sources lists the targets depended on.
+  repeated string sources = 12;
+
+  // deps lists the license metadata files depended on.
+  repeated AnnotatedDependency deps = 13;
+}
+
+// InstallMap messages describe the mapping from an input filesystem file to the path to the file
+// in a container.
+message InstallMap {
+  // The input path on the filesystem.
+  optional string from_path = 1;
+
+  // The path to the file inside the container or installed location.
+  optional string container_path = 2;
+}
+
+// AnnotateDepencency messages describe edges in the build graph.
+message AnnotatedDependency {
+  // The path to the dependency license metadata file.
+  optional string file = 1;
+
+  // The annotations attached to the dependency.
+  repeated string annotations = 2;
+}
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index 965b755..3145315 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -204,6 +204,17 @@
 	return fmt.Sprintf("/system/framework/%s.jar", lib)
 }
 
+// Returns the location to the odex file for the dex file at `path`.
+func ToOdexPath(path string, arch android.ArchType) string {
+	if strings.HasPrefix(path, "/apex/") {
+		return filepath.Join("/system/framework/oat", arch.String(),
+			strings.ReplaceAll(path[1:], "/", "@")+"@classes.odex")
+	}
+
+	return filepath.Join(filepath.Dir(path), "oat", arch.String(),
+		pathtools.ReplaceExtension(filepath.Base(path), "odex"))
+}
+
 func dexpreoptCommand(ctx android.PathContext, globalSoong *GlobalSoongConfig, global *GlobalConfig,
 	module *ModuleConfig, rule *android.RuleBuilder, archIdx int, profile android.WritablePath,
 	appImage bool, generateDM bool) {
@@ -218,23 +229,8 @@
 		base = "package.apk"
 	}
 
-	toOdexPath := func(path string) string {
-		if global.ApexSystemServerJars.ContainsJar(module.Name) {
-			return filepath.Join(
-				"/system/framework/oat",
-				arch.String(),
-				strings.ReplaceAll(path[1:], "/", "@")+"@classes.odex")
-		}
-
-		return filepath.Join(
-			filepath.Dir(path),
-			"oat",
-			arch.String(),
-			pathtools.ReplaceExtension(filepath.Base(path), "odex"))
-	}
-
 	odexPath := module.BuildPath.InSameDir(ctx, "oat", arch.String(), pathtools.ReplaceExtension(base, "odex"))
-	odexInstallPath := toOdexPath(module.DexLocation)
+	odexInstallPath := ToOdexPath(module.DexLocation, arch)
 	if odexOnSystemOther(module, global) {
 		odexInstallPath = filepath.Join(SystemOtherPartition, odexInstallPath)
 	}
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 4dd2135..c9bf958 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -248,7 +248,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 {
 	bazelCtx := ctx.Config().BazelContext
-	filePaths, ok := bazelCtx.GetOutputFiles(label, ctx.Arch().ArchType)
+	filePaths, ok := bazelCtx.GetOutputFiles(label, android.GetConfigKey(ctx))
 	if ok {
 		var bazelOutputFiles android.Paths
 		exportIncludeDirs := map[string]bool{}
@@ -336,14 +336,9 @@
 					}
 				case bootstrap.GoBinaryTool:
 					// A GoBinaryTool provides the install path to a tool, which will be copied.
-					if s, err := filepath.Rel(android.PathForOutput(ctx).String(), t.InstallPath()); err == nil {
-						toolPath := android.PathForOutput(ctx, s)
-						tools = append(tools, toolPath)
-						addLocationLabel(tag.label, toolLocation{android.Paths{toolPath}})
-					} else {
-						ctx.ModuleErrorf("cannot find path for %q: %v", tool, err)
-						return
-					}
+					p := android.PathForGoBinary(ctx, t)
+					tools = append(tools, p)
+					addLocationLabel(tag.label, toolLocation{android.Paths{p}})
 				default:
 					ctx.ModuleErrorf("%q is not a host tool provider", tool)
 					return
diff --git a/java/Android.bp b/java/Android.bp
index 9ffa123..8835b44 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -40,6 +40,7 @@
         "dex.go",
         "dexpreopt.go",
         "dexpreopt_bootjars.go",
+        "dexpreopt_check.go",
         "dexpreopt_config.go",
         "droiddoc.go",
         "droidstubs.go",
@@ -92,6 +93,7 @@
         "platform_bootclasspath_test.go",
         "platform_compat_config_test.go",
         "plugin_test.go",
+        "prebuilt_apis_test.go",
         "rro_test.go",
         "sdk_test.go",
         "sdk_library_test.go",
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index 8f18790..bfa6838 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -616,6 +616,8 @@
 	// 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 android.InList("test_framework-apexd", possibleUpdatableModules) {
+		jars = jars.Append("com.android.apex.test_package", "test_framework-apexd")
 	} else if global.ApexBootJars.Len() != 0 && !android.IsModuleInVersionedSdk(ctx.Module()) {
 		unknown = android.RemoveListFromList(unknown, b.properties.Coverage.Contents)
 		_, unknown = android.RemoveFromList("core-icu4j", unknown)
diff --git a/java/core-libraries/Android.bp b/java/core-libraries/Android.bp
index b198c24..cf39746 100644
--- a/java/core-libraries/Android.bp
+++ b/java/core-libraries/Android.bp
@@ -28,6 +28,11 @@
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
+dist_targets = [
+    "sdk",
+    "win_sdk",
+]
+
 java_library {
     name: "core.current.stubs",
     visibility: ["//visibility:public"],
@@ -40,15 +45,16 @@
     system_modules: "none",
 
     dist: {
-        targets: [
-            "sdk",
-            "win_sdk",
-        ],
+        targets: dist_targets,
     },
 }
 
 // Distributed with the SDK for turning into system modules to compile apps
 // against.
+//
+// Also, produces dist files that are used by the
+// prebuilts/sdk/update_prebuilts.py script to update the prebuilts/sdk
+// directory.
 java_library {
     name: "core-current-stubs-for-system-modules",
     visibility: ["//development/sdk"],
@@ -65,18 +71,22 @@
     ],
     sdk_version: "none",
     system_modules: "none",
-    dist: {
-        dest: "core-for-system-modules.jar",
-        targets: [
-            "sdk",
-            "win_sdk",
-        ],
-    },
+    dists: [
+        {
+            // Legacy dist location for the public file.
+            dest: "core-for-system-modules.jar",
+            targets: dist_targets,
+        },
+        {
+            dest: "system-modules/public/core-for-system-modules.jar",
+            targets: dist_targets,
+        },
+    ],
 }
 
 // Used when compiling higher-level code against core.current.stubs.
 java_system_modules {
-    name: "core-current-stubs-system-modules",
+    name: "core-public-stubs-system-modules",
     visibility: ["//visibility:public"],
     libs: [
         "core-current-stubs-for-system-modules",
@@ -103,10 +113,13 @@
     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: [
+// Produces a dist file that is used by the
+// prebuilts/sdk/update_prebuilts.py script to update the prebuilts/sdk
+// directory.
+java_library {
+    name: "core-module-lib-stubs-for-system-modules",
+    visibility: ["//visibility:private"],
+    static_libs: [
         "core.module_lib.stubs",
         // This one is not on device but it's needed when javac compiles code
         // containing lambdas.
@@ -117,6 +130,20 @@
         // See http://b/123891440.
         "core-generated-annotation-stubs",
     ],
+    sdk_version: "none",
+    system_modules: "none",
+    dist: {
+        dest: "system-modules/module-lib/core-for-system-modules.jar",
+        targets: dist_targets,
+    },
+}
+
+// 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-for-system-modules",
+    ],
     visibility: ["//visibility:public"],
 }
 
diff --git a/java/dexpreopt_check.go b/java/dexpreopt_check.go
new file mode 100644
index 0000000..565901d
--- /dev/null
+++ b/java/dexpreopt_check.go
@@ -0,0 +1,96 @@
+// 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 java
+
+import (
+	"strings"
+
+	"android/soong/android"
+	"android/soong/dexpreopt"
+
+	"github.com/google/blueprint/pathtools"
+)
+
+func init() {
+	RegisterDexpreoptCheckBuildComponents(android.InitRegistrationContext)
+}
+
+func RegisterDexpreoptCheckBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterSingletonModuleType("dexpreopt_systemserver_check", dexpreoptSystemserverCheckFactory)
+}
+
+// A build-time check to verify if all compilation artifacts of system server jars are installed
+// into the system image. When the check fails, it means that dexpreopting is not working for some
+// system server jars and needs to be fixed.
+// This singleton module generates a list of the paths to the artifacts based on
+// PRODUCT_SYSTEM_SERVER_JARS and PRODUCT_APEX_SYSTEM_SERVER_JARS, and passes it to Make via a
+// variable. Make will then do the actual check.
+// Currently, it only checks artifacts of modules defined in Soong. Artifacts of modules defined in
+// Makefile are generated by a script generated by dexpreopt_gen, and their existence is unknown to
+// Make and Ninja.
+type dexpreoptSystemserverCheck struct {
+	android.SingletonModuleBase
+
+	// Mapping from the module name to the install paths to the compilation artifacts.
+	artifactsByModuleName map[string][]string
+
+	// The install paths to the compilation artifacts.
+	artifacts []string
+}
+
+func dexpreoptSystemserverCheckFactory() android.SingletonModule {
+	m := &dexpreoptSystemserverCheck{}
+	m.artifactsByModuleName = make(map[string][]string)
+	android.InitAndroidArchModule(m, android.DeviceSupported, android.MultilibCommon)
+	return m
+}
+
+func getInstallPath(ctx android.ModuleContext, location string) android.InstallPath {
+	return android.PathForModuleInPartitionInstall(
+		ctx, "", strings.TrimPrefix(location, "/")).ToMakePath()
+}
+
+func (m *dexpreoptSystemserverCheck) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	global := dexpreopt.GetGlobalConfig(ctx)
+	targets := ctx.Config().Targets[android.Android]
+
+	// The check should be skipped on unbundled builds because system server jars are not preopted on
+	// unbundled builds since the artifacts are installed into the system image, not the APEXes.
+	if global.DisablePreopt || len(targets) == 0 || ctx.Config().UnbundledBuild() {
+		return
+	}
+
+	systemServerJars := dexpreopt.AllSystemServerJars(ctx, global)
+	for _, jar := range systemServerJars.CopyOfJars() {
+		dexLocation := dexpreopt.GetSystemServerDexLocation(global, jar)
+		odexLocation := dexpreopt.ToOdexPath(dexLocation, targets[0].Arch.ArchType)
+		odexPath := getInstallPath(ctx, odexLocation)
+		vdexPath := getInstallPath(ctx, pathtools.ReplaceExtension(odexLocation, "vdex"))
+		m.artifactsByModuleName[jar] = []string{odexPath.String(), vdexPath.String()}
+	}
+}
+
+func (m *dexpreoptSystemserverCheck) GenerateSingletonBuildActions(ctx android.SingletonContext) {
+	// Only keep modules defined in Soong.
+	ctx.VisitAllModules(func(module android.Module) {
+		if artifacts, ok := m.artifactsByModuleName[module.Name()]; ok {
+			m.artifacts = append(m.artifacts, artifacts...)
+		}
+	})
+}
+
+func (m *dexpreoptSystemserverCheck) MakeVars(ctx android.MakeVarsContext) {
+	ctx.Strict("DEXPREOPT_SYSTEMSERVER_ARTIFACTS", strings.Join(m.artifacts, " "))
+}
diff --git a/java/java.go b/java/java.go
index 29f31e5..854097b 100644
--- a/java/java.go
+++ b/java/java.go
@@ -1540,9 +1540,6 @@
 var _ android.IDECustomizedModuleName = (*Import)(nil)
 
 // Collect information for opening IDE project files in java/jdeps.go.
-const (
-	removedPrefix = "prebuilt_"
-)
 
 func (j *Import) IDEInfo(dpInfo *android.IdeInfo) {
 	dpInfo.Jars = append(dpInfo.Jars, j.PrebuiltSrcs()...)
diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go
index c33e6c2..c67e2bd 100644
--- a/java/prebuilt_apis.go
+++ b/java/prebuilt_apis.go
@@ -167,30 +167,24 @@
 		localPath := strings.TrimPrefix(f, mydir)
 		module, apiver, scope := parseJarPath(localPath)
 		createImport(mctx, module, scope, apiver, localPath, sdkVersion, compileDex)
+
+		if module == "core-for-system-modules" {
+			createSystemModules(mctx, apiver, scope)
+		}
 	}
 }
 
-func createSystemModules(mctx android.LoadHookContext, apiver string) {
+func createSystemModules(mctx android.LoadHookContext, apiver string, scope string) {
 	props := struct {
 		Name *string
 		Libs []string
 	}{}
-	props.Name = proptools.StringPtr(prebuiltApiModuleName(mctx, "system_modules", "public", apiver))
-	props.Libs = append(props.Libs, prebuiltApiModuleName(mctx, "core-for-system-modules", "public", apiver))
+	props.Name = proptools.StringPtr(prebuiltApiModuleName(mctx, "system_modules", scope, apiver))
+	props.Libs = append(props.Libs, prebuiltApiModuleName(mctx, "core-for-system-modules", scope, apiver))
 
 	mctx.CreateModule(systemModulesImportFactory, &props)
 }
 
-func prebuiltSdkSystemModules(mctx android.LoadHookContext, p *prebuiltApis) {
-	for _, apiver := range p.properties.Api_dirs {
-		jar := android.ExistentPathForSource(mctx,
-			mctx.ModuleDir(), apiver, "public", "core-for-system-modules.jar")
-		if jar.Valid() {
-			createSystemModules(mctx, apiver)
-		}
-	}
-}
-
 func prebuiltApiFiles(mctx android.LoadHookContext, p *prebuiltApis) {
 	mydir := mctx.ModuleDir() + "/"
 	// <apiver>/<scope>/api/<module>.txt
@@ -273,7 +267,6 @@
 	if p, ok := mctx.Module().(*prebuiltApis); ok {
 		prebuiltApiFiles(mctx, p)
 		prebuiltSdkStubs(mctx, p)
-		prebuiltSdkSystemModules(mctx, p)
 	}
 }
 
diff --git a/java/prebuilt_apis_test.go b/java/prebuilt_apis_test.go
new file mode 100644
index 0000000..79f4225
--- /dev/null
+++ b/java/prebuilt_apis_test.go
@@ -0,0 +1,56 @@
+// 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 java
+
+import (
+	"sort"
+	"strings"
+	"testing"
+
+	"android/soong/android"
+	"github.com/google/blueprint"
+)
+
+func TestPrebuiltApis_SystemModulesCreation(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForJavaTest,
+		FixtureWithPrebuiltApis(map[string][]string{
+			"31":      {},
+			"32":      {},
+			"current": {},
+		}),
+	).RunTest(t)
+
+	sdkSystemModules := []string{}
+	result.VisitAllModules(func(module blueprint.Module) {
+		name := android.RemoveOptionalPrebuiltPrefix(module.Name())
+		if strings.HasPrefix(name, "sdk_") && strings.HasSuffix(name, "_system_modules") {
+			sdkSystemModules = append(sdkSystemModules, name)
+		}
+	})
+	sort.Strings(sdkSystemModules)
+	expected := []string{
+		// 31 only has public system modules.
+		"sdk_public_31_system_modules",
+
+		// 32 and current both have public and module-lib system modules.
+		"sdk_public_32_system_modules",
+		"sdk_module-lib_32_system_modules",
+		"sdk_public_current_system_modules",
+		"sdk_module-lib_current_system_modules",
+	}
+	sort.Strings(expected)
+	android.AssertArrayString(t, "sdk system modules", expected, sdkSystemModules)
+}
diff --git a/java/proto.go b/java/proto.go
index 2fa22b6..ab913d8 100644
--- a/java/proto.go
+++ b/java/proto.go
@@ -102,6 +102,9 @@
 	if String(p.Proto.Plugin) == "" {
 		var typeToPlugin string
 		switch String(p.Proto.Type) {
+		case "stream":
+			flags.proto.OutTypeFlag = "--javastream_out"
+			typeToPlugin = "javastream"
 		case "micro":
 			flags.proto.OutTypeFlag = "--javamicro_out"
 			typeToPlugin = "javamicro"
diff --git a/java/robolectric.go b/java/robolectric.go
index a3603ad..16af546 100644
--- a/java/robolectric.go
+++ b/java/robolectric.go
@@ -212,13 +212,7 @@
 		installDeps = append(installDeps, installedData)
 	}
 
-	installed := ctx.InstallFile(installPath, ctx.ModuleName()+".jar", r.combinedJar, installDeps...)
-
-	if r.ExportedToMake() {
-		// Soong handles installation here, but Make is usually what creates the phony rule that atest
-		// uses to build the module.  Create it here for now.
-		ctx.Phony(ctx.ModuleName(), installed)
-	}
+	r.installFile = ctx.InstallFile(installPath, ctx.ModuleName()+".jar", r.combinedJar, installDeps...)
 }
 
 func generateRoboTestConfig(ctx android.ModuleContext, outputFile android.WritablePath,
@@ -282,6 +276,10 @@
 func (r *robolectricTest) AndroidMkEntries() []android.AndroidMkEntries {
 	entriesList := r.Library.AndroidMkEntries()
 	entries := &entriesList[0]
+	entries.ExtraEntries = append(entries.ExtraEntries,
+		func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+			entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
+		})
 
 	entries.ExtraFooters = []android.AndroidMkExtraFootersFunc{
 		func(w io.Writer, name, prefix, moduleDir string) {
diff --git a/java/sdk.go b/java/sdk.go
index 80f2d6a..e6bf220 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -60,6 +60,30 @@
 	}
 }
 
+// systemModuleKind returns the kind of system modules to use for the supplied combination of sdk
+// kind and API level.
+func systemModuleKind(sdkKind android.SdkKind, apiLevel android.ApiLevel) android.SdkKind {
+	systemModuleKind := sdkKind
+	if apiLevel.LessThanOrEqualTo(android.LastWithoutModuleLibCoreSystemModules) {
+		// API levels less than or equal to 31 did not provide a core-for-system-modules.jar
+		// specifically for the module-lib API. So, always use the public system modules for them.
+		systemModuleKind = android.SdkPublic
+	} else if systemModuleKind == android.SdkCore {
+		// Core is by definition what is included in the system module for the public API so should
+		// just use its system modules.
+		systemModuleKind = android.SdkPublic
+	} else if systemModuleKind == android.SdkSystem || systemModuleKind == android.SdkTest {
+		// The core system and test APIs are currently the same as the public API so they should use
+		// its system modules.
+		systemModuleKind = android.SdkPublic
+	} else if systemModuleKind == android.SdkSystemServer {
+		// The core system server API is the same as the core module-lib API.
+		systemModuleKind = android.SdkModule
+	}
+
+	return systemModuleKind
+}
+
 func decodeSdkDep(ctx android.EarlyModuleContext, sdkContext android.SdkContext) sdkDep {
 	sdkVersion := sdkContext.SdkVersion(ctx)
 	if !sdkVersion.Valid() {
@@ -105,7 +129,8 @@
 
 		var systemModules string
 		if defaultJavaLanguageVersion(ctx, sdkVersion).usesJavaModules() {
-			systemModules = "sdk_public_" + sdkVersion.ApiLevel.String() + "_system_modules"
+			systemModuleKind := systemModuleKind(sdkVersion.Kind, sdkVersion.ApiLevel)
+			systemModules = fmt.Sprintf("sdk_%s_%s_system_modules", systemModuleKind, sdkVersion.ApiLevel)
 		}
 
 		return sdkDep{
@@ -116,13 +141,15 @@
 		}
 	}
 
-	toModule := func(modules []string, res string, aidl android.Path) sdkDep {
+	toModule := func(module string, aidl android.Path) sdkDep {
+		// Select the kind of system modules needed for the sdk version.
+		systemModulesKind := systemModuleKind(sdkVersion.Kind, android.FutureApiLevel)
 		return sdkDep{
 			useModule:          true,
-			bootclasspath:      append(modules, config.DefaultLambdaStubsLibrary),
-			systemModules:      "core-current-stubs-system-modules",
-			java9Classpath:     modules,
-			frameworkResModule: res,
+			bootclasspath:      []string{module, config.DefaultLambdaStubsLibrary},
+			systemModules:      fmt.Sprintf("core-%s-stubs-system-modules", systemModulesKind),
+			java9Classpath:     []string{module},
+			frameworkResModule: "framework-res",
 			aidl:               android.OptionalPathForPath(aidl),
 		}
 	}
@@ -161,38 +188,24 @@
 			noFrameworksLibs: true,
 		}
 	case android.SdkPublic:
-		return toModule([]string{"android_stubs_current"}, "framework-res", sdkFrameworkAidlPath(ctx))
+		return toModule("android_stubs_current", sdkFrameworkAidlPath(ctx))
 	case android.SdkSystem:
-		return toModule([]string{"android_system_stubs_current"}, "framework-res", sdkFrameworkAidlPath(ctx))
+		return toModule("android_system_stubs_current", sdkFrameworkAidlPath(ctx))
 	case android.SdkTest:
-		return toModule([]string{"android_test_stubs_current"}, "framework-res", sdkFrameworkAidlPath(ctx))
+		return toModule("android_test_stubs_current", sdkFrameworkAidlPath(ctx))
 	case android.SdkCore:
 		return sdkDep{
 			useModule:        true,
 			bootclasspath:    []string{"core.current.stubs", config.DefaultLambdaStubsLibrary},
-			systemModules:    "core-current-stubs-system-modules",
+			systemModules:    "core-public-stubs-system-modules",
 			noFrameworksLibs: true,
 		}
 	case android.SdkModule:
 		// TODO(146757305): provide .apk and .aidl that have more APIs for modules
-		return sdkDep{
-			useModule:          true,
-			bootclasspath:      []string{"android_module_lib_stubs_current", config.DefaultLambdaStubsLibrary},
-			systemModules:      "core-module-lib-stubs-system-modules",
-			java9Classpath:     []string{"android_module_lib_stubs_current"},
-			frameworkResModule: "framework-res",
-			aidl:               android.OptionalPathForPath(nonUpdatableFrameworkAidlPath(ctx)),
-		}
+		return toModule("android_module_lib_stubs_current", nonUpdatableFrameworkAidlPath(ctx))
 	case android.SdkSystemServer:
 		// TODO(146757305): provide .apk and .aidl that have more APIs for modules
-		return sdkDep{
-			useModule:          true,
-			bootclasspath:      []string{"android_system_server_stubs_current", config.DefaultLambdaStubsLibrary},
-			systemModules:      "core-module-lib-stubs-system-modules",
-			java9Classpath:     []string{"android_system_server_stubs_current"},
-			frameworkResModule: "framework-res",
-			aidl:               android.OptionalPathForPath(sdkFrameworkAidlPath(ctx)),
-		}
+		return toModule("android_system_server_stubs_current", sdkFrameworkAidlPath(ctx))
 	default:
 		panic(fmt.Errorf("invalid sdk %q", sdkVersion.Raw))
 	}
diff --git a/java/sdk_test.go b/java/sdk_test.go
index 6d62130..9e8ba6e 100644
--- a/java/sdk_test.go
+++ b/java/sdk_test.go
@@ -25,27 +25,36 @@
 	"android/soong/java/config"
 )
 
+type classpathTestCase struct {
+	name       string
+	unbundled  bool
+	moduleType string
+	host       android.OsClass
+	properties string
+
+	// for java 8
+	bootclasspath  []string
+	java8classpath []string
+
+	// for java 9
+	system         string
+	java9classpath []string
+
+	forces8 bool // if set, javac will always be called with java 8 arguments
+
+	aidl string
+
+	// Indicates how this test case is affected by the setting of Always_use_prebuilt_sdks.
+	//
+	// If this is nil then the test case is unaffected by the setting of Always_use_prebuilt_sdks.
+	// Otherwise, the test case can only be used when
+	// Always_use_prebuilt_sdks=*forAlwaysUsePrebuiltSdks.
+	forAlwaysUsePrebuiltSdks *bool
+}
+
 func TestClasspath(t *testing.T) {
 	const frameworkAidl = "-I" + defaultJavaDir + "/framework/aidl"
-	var classpathTestcases = []struct {
-		name       string
-		unbundled  bool
-		moduleType string
-		host       android.OsClass
-		properties string
-
-		// for java 8
-		bootclasspath  []string
-		java8classpath []string
-
-		// for java 9
-		system         string
-		java9classpath []string
-
-		forces8 bool // if set, javac will always be called with java 8 arguments
-
-		aidl string
-	}{
+	var classpathTestcases = []classpathTestCase{
 		{
 			name:           "default",
 			bootclasspath:  config.StableCorePlatformBootclasspathLibraries,
@@ -91,25 +100,52 @@
 			aidl:           "-pprebuilts/sdk/30/public/framework.aidl",
 		},
 		{
+			// Test case only applies when Always_use_prebuilt_sdks=false (the default).
+			forAlwaysUsePrebuiltSdks: proptools.BoolPtr(false),
 
 			name:           "current",
 			properties:     `sdk_version: "current",`,
 			bootclasspath:  []string{"android_stubs_current", "core-lambda-stubs"},
-			system:         "core-current-stubs-system-modules",
+			system:         "core-public-stubs-system-modules",
 			java9classpath: []string{"android_stubs_current"},
 			aidl:           "-pout/soong/framework.aidl",
 		},
 		{
+			// Test case only applies when Always_use_prebuilt_sdks=true.
+			forAlwaysUsePrebuiltSdks: proptools.BoolPtr(true),
+
+			name:           "current",
+			properties:     `sdk_version: "current",`,
+			bootclasspath:  []string{`""`},
+			system:         "sdk_public_current_system_modules",
+			java8classpath: []string{"prebuilts/sdk/current/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			java9classpath: []string{"prebuilts/sdk/current/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			aidl:           "-pprebuilts/sdk/current/public/framework.aidl",
+		},
+		{
+			// Test case only applies when Always_use_prebuilt_sdks=false (the default).
+			forAlwaysUsePrebuiltSdks: proptools.BoolPtr(false),
 
 			name:           "system_current",
 			properties:     `sdk_version: "system_current",`,
 			bootclasspath:  []string{"android_system_stubs_current", "core-lambda-stubs"},
-			system:         "core-current-stubs-system-modules",
+			system:         "core-public-stubs-system-modules",
 			java9classpath: []string{"android_system_stubs_current"},
 			aidl:           "-pout/soong/framework.aidl",
 		},
 		{
+			// Test case only applies when Always_use_prebuilt_sdks=true.
+			forAlwaysUsePrebuiltSdks: proptools.BoolPtr(true),
 
+			name:           "system_current",
+			properties:     `sdk_version: "system_current",`,
+			bootclasspath:  []string{`""`},
+			system:         "sdk_public_current_system_modules",
+			java8classpath: []string{"prebuilts/sdk/current/system/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			java9classpath: []string{"prebuilts/sdk/current/system/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			aidl:           "-pprebuilts/sdk/current/public/framework.aidl",
+		},
+		{
 			name:           "system_29",
 			properties:     `sdk_version: "system_29",`,
 			bootclasspath:  []string{`""`},
@@ -118,7 +154,6 @@
 			aidl:           "-pprebuilts/sdk/29/public/framework.aidl",
 		},
 		{
-
 			name:           "system_30",
 			properties:     `sdk_version: "system_30",`,
 			bootclasspath:  []string{`""`},
@@ -128,20 +163,57 @@
 			aidl:           "-pprebuilts/sdk/30/public/framework.aidl",
 		},
 		{
+			// Test case only applies when Always_use_prebuilt_sdks=false (the default).
+			forAlwaysUsePrebuiltSdks: proptools.BoolPtr(false),
 
 			name:           "test_current",
 			properties:     `sdk_version: "test_current",`,
 			bootclasspath:  []string{"android_test_stubs_current", "core-lambda-stubs"},
-			system:         "core-current-stubs-system-modules",
+			system:         "core-public-stubs-system-modules",
 			java9classpath: []string{"android_test_stubs_current"},
 			aidl:           "-pout/soong/framework.aidl",
 		},
 		{
+			// Test case only applies when Always_use_prebuilt_sdks=true.
+			forAlwaysUsePrebuiltSdks: proptools.BoolPtr(true),
+
+			name:           "test_current",
+			properties:     `sdk_version: "test_current",`,
+			bootclasspath:  []string{`""`},
+			system:         "sdk_public_current_system_modules",
+			java8classpath: []string{"prebuilts/sdk/current/test/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			java9classpath: []string{"prebuilts/sdk/current/test/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			aidl:           "-pprebuilts/sdk/current/public/framework.aidl",
+		},
+		{
+			name:           "test_30",
+			properties:     `sdk_version: "test_30",`,
+			bootclasspath:  []string{`""`},
+			system:         "sdk_public_30_system_modules",
+			java8classpath: []string{"prebuilts/sdk/30/test/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			java9classpath: []string{"prebuilts/sdk/30/test/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			aidl:           "-pprebuilts/sdk/30/public/framework.aidl",
+		},
+		{
+			// Test case only applies when Always_use_prebuilt_sdks=false (the default).
+			forAlwaysUsePrebuiltSdks: proptools.BoolPtr(false),
 
 			name:          "core_current",
 			properties:    `sdk_version: "core_current",`,
 			bootclasspath: []string{"core.current.stubs", "core-lambda-stubs"},
-			system:        "core-current-stubs-system-modules",
+			system:        "core-public-stubs-system-modules",
+		},
+		{
+			// Test case only applies when Always_use_prebuilt_sdks=true.
+			forAlwaysUsePrebuiltSdks: proptools.BoolPtr(true),
+
+			name:           "core_current",
+			properties:     `sdk_version: "core_current",`,
+			bootclasspath:  []string{`""`},
+			system:         "sdk_public_current_system_modules",
+			java8classpath: []string{"prebuilts/sdk/current/core/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			java9classpath: []string{"prebuilts/sdk/current/core/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			aidl:           "-pprebuilts/sdk/current/public/framework.aidl",
 		},
 		{
 
@@ -214,8 +286,10 @@
 			java9classpath: []string{"prebuilts/sdk/current/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
 			aidl:           "-pprebuilts/sdk/current/public/framework.aidl",
 		},
-
 		{
+			// Test case only applies when Always_use_prebuilt_sdks=false (the default).
+			forAlwaysUsePrebuiltSdks: proptools.BoolPtr(false),
+
 			name:           "module_current",
 			properties:     `sdk_version: "module_current",`,
 			bootclasspath:  []string{"android_module_lib_stubs_current", "core-lambda-stubs"},
@@ -224,6 +298,48 @@
 			aidl:           "-pout/soong/framework_non_updatable.aidl",
 		},
 		{
+			// Test case only applies when Always_use_prebuilt_sdks=true.
+			forAlwaysUsePrebuiltSdks: proptools.BoolPtr(true),
+
+			name:           "module_current",
+			properties:     `sdk_version: "module_current",`,
+			bootclasspath:  []string{`""`},
+			system:         "sdk_module-lib_current_system_modules",
+			java8classpath: []string{"prebuilts/sdk/current/module-lib/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			java9classpath: []string{"prebuilts/sdk/current/module-lib/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			aidl:           "-pprebuilts/sdk/current/public/framework.aidl",
+		},
+		{
+			name:           "module_30",
+			properties:     `sdk_version: "module_30",`,
+			bootclasspath:  []string{`""`},
+			system:         "sdk_public_30_system_modules",
+			java8classpath: []string{"prebuilts/sdk/30/module-lib/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			java9classpath: []string{"prebuilts/sdk/30/module-lib/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			aidl:           "-pprebuilts/sdk/30/public/framework.aidl",
+		},
+		{
+			name:           "module_31",
+			properties:     `sdk_version: "module_31",`,
+			bootclasspath:  []string{`""`},
+			system:         "sdk_public_31_system_modules",
+			java8classpath: []string{"prebuilts/sdk/31/module-lib/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			java9classpath: []string{"prebuilts/sdk/31/module-lib/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			aidl:           "-pprebuilts/sdk/31/public/framework.aidl",
+		},
+		{
+			name:           "module_32",
+			properties:     `sdk_version: "module_32",`,
+			bootclasspath:  []string{`""`},
+			system:         "sdk_module-lib_32_system_modules",
+			java8classpath: []string{"prebuilts/sdk/32/module-lib/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			java9classpath: []string{"prebuilts/sdk/32/module-lib/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			aidl:           "-pprebuilts/sdk/32/public/framework.aidl",
+		},
+		{
+			// Test case only applies when Always_use_prebuilt_sdks=false (the default).
+			forAlwaysUsePrebuiltSdks: proptools.BoolPtr(false),
+
 			name:           "system_server_current",
 			properties:     `sdk_version: "system_server_current",`,
 			bootclasspath:  []string{"android_system_server_stubs_current", "core-lambda-stubs"},
@@ -231,9 +347,62 @@
 			java9classpath: []string{"android_system_server_stubs_current"},
 			aidl:           "-pout/soong/framework.aidl",
 		},
+		{
+			// Test case only applies when Always_use_prebuilt_sdks=true.
+			forAlwaysUsePrebuiltSdks: proptools.BoolPtr(true),
+
+			name:           "system_server_current",
+			properties:     `sdk_version: "system_server_current",`,
+			bootclasspath:  []string{`""`},
+			system:         "sdk_module-lib_current_system_modules",
+			java8classpath: []string{"prebuilts/sdk/current/system-server/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			java9classpath: []string{"prebuilts/sdk/current/system-server/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			aidl:           "-pprebuilts/sdk/current/public/framework.aidl",
+		},
+		{
+			name:           "system_server_30",
+			properties:     `sdk_version: "system_server_30",`,
+			bootclasspath:  []string{`""`},
+			system:         "sdk_public_30_system_modules",
+			java8classpath: []string{"prebuilts/sdk/30/system-server/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			java9classpath: []string{"prebuilts/sdk/30/system-server/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			aidl:           "-pprebuilts/sdk/30/public/framework.aidl",
+		},
+		{
+			name:           "system_server_31",
+			properties:     `sdk_version: "system_server_31",`,
+			bootclasspath:  []string{`""`},
+			system:         "sdk_public_31_system_modules",
+			java8classpath: []string{"prebuilts/sdk/31/system-server/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			java9classpath: []string{"prebuilts/sdk/31/system-server/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			aidl:           "-pprebuilts/sdk/31/public/framework.aidl",
+		},
+		{
+			name:           "system_server_32",
+			properties:     `sdk_version: "system_server_32",`,
+			bootclasspath:  []string{`""`},
+			system:         "sdk_module-lib_32_system_modules",
+			java8classpath: []string{"prebuilts/sdk/32/system-server/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			java9classpath: []string{"prebuilts/sdk/32/system-server/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			aidl:           "-pprebuilts/sdk/32/public/framework.aidl",
+		},
 	}
 
+	t.Run("basic", func(t *testing.T) {
+		testClasspathTestCases(t, classpathTestcases, false)
+	})
+
+	t.Run("Always_use_prebuilt_sdks=true", func(t *testing.T) {
+		testClasspathTestCases(t, classpathTestcases, true)
+	})
+}
+
+func testClasspathTestCases(t *testing.T, classpathTestcases []classpathTestCase, alwaysUsePrebuiltSdks bool) {
 	for _, testcase := range classpathTestcases {
+		if testcase.forAlwaysUsePrebuiltSdks != nil && *testcase.forAlwaysUsePrebuiltSdks != alwaysUsePrebuiltSdks {
+			continue
+		}
+
 		t.Run(testcase.name, func(t *testing.T) {
 			moduleType := "java_library"
 			if testcase.moduleType != "" {
@@ -299,7 +468,9 @@
 				system = "--system=none"
 			} else if testcase.system != "" {
 				dir := ""
-				if strings.HasPrefix(testcase.system, "sdk_public_") {
+				// If the system modules name starts with sdk_ then it is a prebuilt module and so comes
+				// from the prebuilt directory.
+				if strings.HasPrefix(testcase.system, "sdk_") {
 					dir = "prebuilts/sdk"
 				} else {
 					dir = defaultJavaDir
@@ -351,11 +522,20 @@
 				android.AssertPathsRelativeToTopEquals(t, "implicits", deps, javac.Implicits)
 			}
 
+			preparer := android.NullFixturePreparer
+			if alwaysUsePrebuiltSdks {
+				preparer = android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+					variables.Always_use_prebuilt_sdks = proptools.BoolPtr(true)
+				})
+			}
+
 			fixtureFactory := android.GroupFixturePreparers(
 				prepareForJavaTest,
 				FixtureWithPrebuiltApis(map[string][]string{
 					"29":      {},
 					"30":      {},
+					"31":      {},
+					"32":      {},
 					"current": {},
 				}),
 				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
@@ -369,6 +549,7 @@
 						env["ANDROID_JAVA8_HOME"] = "jdk8"
 					}
 				}),
+				preparer,
 			)
 
 			// Test with legacy javac -source 1.8 -target 1.8
diff --git a/java/systemserver_classpath_fragment.go b/java/systemserver_classpath_fragment.go
index f209f4a..d75635c 100644
--- a/java/systemserver_classpath_fragment.go
+++ b/java/systemserver_classpath_fragment.go
@@ -120,9 +120,19 @@
 	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)
+	// This module only exists in car products.
+	// So ignore it even if it is not in PRODUCT_APEX_SYSTEM_SERVER_JARS.
+	// TODO(b/203233647): Add better mechanism to make it optional.
+	_, unknown = android.RemoveFromList("car-frameworks-service-module", unknown)
 
-	// For non test apexes, make sure that all contents are actually declared in make.
-	if global.ApexSystemServerJars.Len() > 0 && len(unknown) > 0 && !android.IsModuleInVersionedSdk(ctx.Module()) {
+	// 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 ApexSystemServerJars. 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 ApexdUnitTests, until such support exists.
+	if android.InList("test_service-apexd", possibleUpdatableModules) {
+		jars = jars.Append("com.android.apex.test_package", "test_service-apexd")
+	} else if global.ApexSystemServerJars.Len() > 0 && len(unknown) > 0 && !android.IsModuleInVersionedSdk(ctx.Module()) {
+		// For non test apexes, make sure that all contents are actually declared in make.
 		ctx.ModuleErrorf("%s in contents must also be declared in PRODUCT_APEX_SYSTEM_SERVER_JARS", unknown)
 	}
 
diff --git a/java/testing.go b/java/testing.go
index 99d55a0..7441e44 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -159,8 +159,7 @@
 		`, strings.Join(android.SortedStringKeys(release2Modules), `", "`))
 
 	for release, modules := range release2Modules {
-		libs := append([]string{"android", "core-for-system-modules"}, modules...)
-		mockFS.Merge(prebuiltApisFilesForLibs([]string{release}, libs))
+		mockFS.Merge(prebuiltApisFilesForModules([]string{release}, modules))
 	}
 	return android.GroupFixturePreparers(
 		android.FixtureAddTextFile(path, bp),
@@ -168,19 +167,32 @@
 	)
 }
 
-func prebuiltApisFilesForLibs(apiLevels []string, sdkLibs []string) map[string][]byte {
+func prebuiltApisFilesForModules(apiLevels []string, modules []string) map[string][]byte {
+	libs := append([]string{"android"}, modules...)
+
 	fs := make(map[string][]byte)
 	for _, level := range apiLevels {
-		for _, lib := range sdkLibs {
-			for _, scope := range []string{"public", "system", "module-lib", "system-server", "test"} {
-				fs[fmt.Sprintf("prebuilts/sdk/%s/%s/%s.jar", level, scope, lib)] = nil
+		apiLevel := android.ApiLevelForTest(level)
+		for _, sdkKind := range []android.SdkKind{android.SdkPublic, android.SdkSystem, android.SdkModule, android.SdkSystemServer, android.SdkTest} {
+			// A core-for-system-modules file must only be created for the sdk kind that supports it.
+			if sdkKind == systemModuleKind(sdkKind, apiLevel) {
+				fs[fmt.Sprintf("prebuilts/sdk/%s/%s/core-for-system-modules.jar", level, sdkKind)] = nil
+			}
+
+			for _, lib := range libs {
+				// Create a jar file for every library.
+				fs[fmt.Sprintf("prebuilts/sdk/%s/%s/%s.jar", level, sdkKind, lib)] = nil
+
 				// No finalized API files for "current"
 				if level != "current" {
-					fs[fmt.Sprintf("prebuilts/sdk/%s/%s/api/%s.txt", level, scope, lib)] = nil
-					fs[fmt.Sprintf("prebuilts/sdk/%s/%s/api/%s-removed.txt", level, scope, lib)] = nil
+					fs[fmt.Sprintf("prebuilts/sdk/%s/%s/api/%s.txt", level, sdkKind, lib)] = nil
+					fs[fmt.Sprintf("prebuilts/sdk/%s/%s/api/%s-removed.txt", level, sdkKind, lib)] = nil
 				}
 			}
 		}
+		if level == "current" {
+			fs["prebuilts/sdk/current/core/android.jar"] = nil
+		}
 		fs[fmt.Sprintf("prebuilts/sdk/%s/public/framework.aidl", level)] = nil
 	}
 	return fs
@@ -332,7 +344,7 @@
 		}`
 
 	systemModules := []string{
-		"core-current-stubs-system-modules",
+		"core-public-stubs-system-modules",
 		"core-module-lib-stubs-system-modules",
 		"legacy-core-platform-api-stubs-system-modules",
 		"stable-core-platform-api-stubs-system-modules",
diff --git a/mk2rbc/cmd/mk2rbc.go b/mk2rbc/cmd/mk2rbc.go
index d8e7018..b089f91 100644
--- a/mk2rbc/cmd/mk2rbc.go
+++ b/mk2rbc/cmd/mk2rbc.go
@@ -60,7 +60,7 @@
 )
 
 func init() {
-	// Poor man's flag aliasing: works, but the usage string is ugly and
+	// Simplistic 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 {
diff --git a/mk2rbc/mk2rbc.go b/mk2rbc/mk2rbc.go
index b377fd8..6227164 100644
--- a/mk2rbc/mk2rbc.go
+++ b/mk2rbc/mk2rbc.go
@@ -40,12 +40,15 @@
 )
 
 const (
-	baseUri = "//build/make/core:product_config.rbc"
+	annotationCommentPrefix = "RBC#"
+	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"
 
+	soongNsPrefix = "SOONG_CONFIG_"
+
 	// And here are the functions and variables:
 	cfnGetCfg          = baseName + ".cfg"
 	cfnMain            = baseName + ".product_configuration"
@@ -60,10 +63,14 @@
 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"
+	fileExistsPhony = "$file_exists"
+	// The following two macros are obsolete, and will we deleted once
+	// there are deleted from the makefiles:
+	soongConfigNamespaceOld = "add_soong_config_namespace"
+	soongConfigVarSetOld    = "add_soong_config_var_value"
+	soongConfigAppend       = "soong_config_append"
+	soongConfigAssign       = "soong_config_set"
+	wildcardExistsPhony     = "$wildcard_exists"
 )
 
 const (
@@ -82,8 +89,10 @@
 	"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},
+	soongConfigNamespaceOld:               {baseName + ".soong_config_namespace", starlarkTypeVoid, hiddenArgGlobal},
+	soongConfigVarSetOld:                  {baseName + ".soong_config_set", starlarkTypeVoid, hiddenArgGlobal},
+	soongConfigAssign:                     {baseName + ".soong_config_set", starlarkTypeVoid, hiddenArgGlobal},
+	soongConfigAppend:                     {baseName + ".soong_config_append", 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},
@@ -102,13 +111,16 @@
 	"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-platform2":                  {baseName + ".board_platform_is", starlarkTypeBool, hiddenArgGlobal},
 	"is-board-platform-in-list":           {"!is-board-platform-in-list", starlarkTypeBool, hiddenArgNone},
+	"is-board-platform-in-list2":          {baseName + ".board_platform_in", starlarkTypeBool, hiddenArgGlobal},
 	"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},
+	"is-vendor-board-qcom":                {"!is-vendor-board-qcom", starlarkTypeBool, hiddenArgNone},
 	callLoadAlways:                        {"!inherit-product", starlarkTypeVoid, hiddenArgNone},
 	callLoadIf:                            {"!inherit-product-if-exists", starlarkTypeVoid, hiddenArgNone},
 	"lastword":                            {"!lastword", starlarkTypeString, hiddenArgNone},
@@ -399,6 +411,7 @@
 	outputDir        string
 	dependentModules map[string]*moduleInfo
 	soongNamespaces  map[string]map[string]bool
+	includeTops      []string
 }
 
 func newParseContext(ss *StarlarkScript, nodes []mkparser.Node) *parseContext {
@@ -444,6 +457,7 @@
 		variables:        make(map[string]variable),
 		dependentModules: make(map[string]*moduleInfo),
 		soongNamespaces:  make(map[string]map[string]bool),
+		includeTops:      []string{"vendor/google-devices"},
 	}
 	ctx.pushVarAssignments()
 	for _, item := range predefined {
@@ -522,7 +536,6 @@
 		return
 	}
 	name := a.Name.Strings[0]
-	const soongNsPrefix = "SOONG_CONFIG_"
 	// Soong confuguration
 	if strings.HasPrefix(name, soongNsPrefix) {
 		ctx.handleSoongNsAssignment(strings.TrimPrefix(name, soongNsPrefix), a)
@@ -615,7 +628,7 @@
 		for _, ns := range strings.Fields(s) {
 			ctx.addSoongNamespace(ns)
 			ctx.receiver.newNode(&exprNode{&callExpr{
-				name:       addSoongNamespace,
+				name:       soongConfigNamespaceOld,
 				args:       []starlarkExpr{&stringLiteralExpr{ns}},
 				returnType: starlarkTypeVoid,
 			}})
@@ -665,8 +678,12 @@
 			ctx.errorf(asgn, "no %s variable in %s namespace, please use add_soong_config_var_value instead", varName, namespaceName)
 			return
 		}
+		fname := soongConfigVarSetOld
+		if asgn.Type == "+=" {
+			fname = soongConfigAppend
+		}
 		ctx.receiver.newNode(&exprNode{&callExpr{
-			name:       addSoongConfigVarValue,
+			name:       fname,
 			args:       []starlarkExpr{&stringLiteralExpr{namespaceName}, &stringLiteralExpr{varName}, val},
 			returnType: starlarkTypeVoid,
 		}})
@@ -799,21 +816,15 @@
 			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)...)
-			}
+	if pathPattern[0] == "" {
+		// If pattern starts from the top. restrict it to the directories where
+		// we know inherit-product uses dynamically calculated path.
+		for _, p := range ctx.includeTops {
+			pathPattern[0] = p
+			matchingPaths = append(matchingPaths, ctx.findMatchingPaths(pathPattern)...)
 		}
+	} else {
+		matchingPaths = ctx.findMatchingPaths(pathPattern)
 	}
 	// Safeguard against $(call inherit-product,$(PRODUCT_PATH))
 	const maxMatchingFiles = 150
@@ -1133,6 +1144,34 @@
 				list:  &variableRefExpr{ctx.addVariable(s + "_BOARD_PLATFORMS"), true},
 				isNot: negate,
 			}, true
+
+		case "is-board-platform2", "is-board-platform-in-list2":
+			if s, ok := maybeString(xValue); !ok || s != "" {
+				return ctx.newBadExpr(directive,
+					fmt.Sprintf("the result of %s can be compared only to empty", x.name)), true
+			}
+			if len(x.args) != 1 {
+				return ctx.newBadExpr(directive, "%s requires an argument", x.name), true
+			}
+			cc := &callExpr{
+				name:       x.name,
+				args:       []starlarkExpr{x.args[0]},
+				returnType: starlarkTypeBool,
+			}
+			if !negate {
+				return &notExpr{cc}, true
+			}
+			return cc, true
+		case "is-vendor-board-qcom":
+			if s, ok := maybeString(xValue); !ok || s != "" {
+				return ctx.newBadExpr(directive,
+					fmt.Sprintf("the result of %s can be compared only to empty", x.name)), true
+			}
+			return &inExpr{
+				expr:  &variableRefExpr{ctx.addVariable("TARGET_BOARD_PLATFORM"), false},
+				list:  &variableRefExpr{ctx.addVariable("QCOM_BOARD_PLATFORMS"), true},
+				isNot: negate,
+			}, true
 		default:
 			return ctx.newBadExpr(directive, "Unknown function in ifeq: %s", x.name), true
 		}
@@ -1277,6 +1316,10 @@
 				returnType: starlarkTypeUnknown,
 			}
 		}
+		if strings.HasPrefix(refDump, soongNsPrefix) {
+			// TODO (asmundak): if we find many, maybe handle them.
+			return ctx.newBadExpr(node, "SOONG_CONFIG_ variables cannot be referenced: %s", refDump)
+		}
 		if v := ctx.addVariable(refDump); v != nil {
 			return &variableRefExpr{v, ctx.lastAssignment(v.name()) != nil}
 		}
@@ -1438,6 +1481,7 @@
 	handled := true
 	switch x := node.(type) {
 	case *mkparser.Comment:
+		ctx.maybeHandleAnnotation(x)
 		ctx.insertComment("#" + x.Comment)
 	case *mkparser.Assignment:
 		ctx.handleAssignment(x)
@@ -1453,11 +1497,33 @@
 			handled = false
 		}
 	default:
-		ctx.errorf(x, "unsupported line %s", x.Dump())
+		ctx.errorf(x, "unsupported line %s", strings.ReplaceAll(x.Dump(), "\n", "\n#"))
 	}
 	return handled
 }
 
+// Processes annotation. An annotation is a comment that starts with #RBC# and provides
+// a conversion hint -- say, where to look for the dynamically calculated inherit/include
+// paths.
+func (ctx *parseContext) maybeHandleAnnotation(cnode *mkparser.Comment) {
+	maybeTrim := func(s, prefix string) (string, bool) {
+		if strings.HasPrefix(s, prefix) {
+			return strings.TrimSpace(strings.TrimPrefix(s, prefix)), true
+		}
+		return s, false
+	}
+	annotation, ok := maybeTrim(cnode.Comment, annotationCommentPrefix)
+	if !ok {
+		return
+	}
+	if p, ok := maybeTrim(annotation, "include_top"); ok {
+		ctx.includeTops = append(ctx.includeTops, p)
+		return
+	}
+	ctx.errorf(cnode, "unsupported annotation %s", cnode.Comment)
+
+}
+
 func (ctx *parseContext) insertComment(s string) {
 	ctx.receiver.newNode(&commentNode{strings.TrimSpace(s)})
 }
diff --git a/mk2rbc/mk2rbc_test.go b/mk2rbc/mk2rbc_test.go
index fe1fa08..511bb0f 100644
--- a/mk2rbc/mk2rbc_test.go
+++ b/mk2rbc/mk2rbc_test.go
@@ -563,6 +563,27 @@
 `,
 	},
 	{
+		desc:   "new is-board calls",
+		mkname: "product.mk",
+		in: `
+ifneq (,$(call is-board-platform-in-list2,msm8998 $(X))
+else ifeq (,$(call is-board-platform2,copper)
+else ifneq (,$(call is-vendor-board-qcom))
+endif
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  if rblf.board_platform_in(g, "msm8998 %s" % g.get("X", "")):
+    pass
+  elif not rblf.board_platform_is(g, "copper"):
+    pass
+  elif g.get("TARGET_BOARD_PLATFORM", "") not in g["QCOM_BOARD_PLATFORMS"]:
+    pass
+`,
+	},
+	{
 		desc:   "findstring call",
 		mkname: "product.mk",
 		in: `
@@ -681,6 +702,8 @@
 $(info $(notdir foo/bar))
 $(call add_soong_config_namespace,snsconfig)
 $(call add_soong_config_var_value,snsconfig,imagetype,odm_image)
+$(call soong_config_set, snsconfig, foo, foo_value)
+$(call soong_config_append, snsconfig, bar, bar_value)
 PRODUCT_COPY_FILES := $(call copy-files,$(wildcard foo*.mk),etc)
 PRODUCT_COPY_FILES := $(call product-copy-files-by-pattern,from/%,to/%,a b c)
 `,
@@ -700,8 +723,10 @@
   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")
+  rblf.soong_config_namespace(g, "snsconfig")
+  rblf.soong_config_set(g, "snsconfig", "imagetype", "odm_image")
+  rblf.soong_config_set(g, "snsconfig", "foo", "foo_value")
+  rblf.soong_config_append(g, "snsconfig", "bar", "bar_value")
   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")
 `,
@@ -784,17 +809,21 @@
 		in: `
 SOONG_CONFIG_NAMESPACES += cvd
 SOONG_CONFIG_cvd += launch_configs
-SOONG_CONFIG_cvd_launch_configs += cvd_config_auto.json
+SOONG_CONFIG_cvd_launch_configs = cvd_config_auto.json
 SOONG_CONFIG_cvd += grub_config
 SOONG_CONFIG_cvd_grub_config += grub.cfg
+x := $(SOONG_CONFIG_cvd_grub_config)
 `,
 		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")
+  rblf.soong_config_namespace(g, "cvd")
+  rblf.soong_config_set(g, "cvd", "launch_configs", "cvd_config_auto.json")
+  rblf.soong_config_append(g, "cvd", "grub_config", "grub.cfg")
+  # MK2RBC TRANSLATION ERROR: SOONG_CONFIG_ variables cannot be referenced: SOONG_CONFIG_cvd_grub_config
+  # x := $(SOONG_CONFIG_cvd_grub_config)
+  rblf.warning("product.mk", "partially successful conversion")
 `,
 	},
 	{
@@ -911,7 +940,7 @@
 		desc:   "Dynamic inherit path",
 		mkname: "product.mk",
 		in: `
-MY_PATH=foo
+MY_PATH:=foo
 $(call inherit-product,vendor/$(MY_PATH)/cfg.mk)
 `,
 		expected: `load("//build/make/core:product_config.rbc", "rblf")
@@ -931,6 +960,47 @@
   rblf.inherit(handle, _varmod, _varmod_init)
 `,
 	},
+	{
+		desc:   "Dynamic inherit with hint",
+		mkname: "product.mk",
+		in: `
+MY_PATH:=foo
+#RBC# include_top vendor/foo1
+$(call inherit-product,$(MY_PATH)/cfg.mk)
+`,
+		expected: `load("//build/make/core:product_config.rbc", "rblf")
+load("//vendor/foo1:cfg.star|init", _cfg_init = "init")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  g["MY_PATH"] = "foo"
+  #RBC# include_top vendor/foo1
+  _entry = {
+    "vendor/foo1/cfg.mk": ("_cfg", _cfg_init),
+  }.get("%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)
+`,
+	},
+	{
+		desc:   "Ignore make rules",
+		mkname: "product.mk",
+		in: `
+foo: foo.c
+	gcc -o $@ $*`,
+		expected: `# MK2RBC TRANSLATION ERROR: unsupported line rule:       foo: foo.c
+#gcc -o $@ $*
+# rule:       foo: foo.c
+# gcc -o $@ $*
+load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  rblf.warning("product.mk", "partially successful conversion")
+`,
+	},
 }
 
 var known_variables = []struct {
diff --git a/mk2rbc/types.go b/mk2rbc/types.go
index ebd52d8..46c6aa9 100644
--- a/mk2rbc/types.go
+++ b/mk2rbc/types.go
@@ -53,7 +53,7 @@
 	NewVariable(name string, varClass varClass, valueType starlarkType)
 }
 
-// ScopeBase is a dummy implementation of the mkparser.Scope.
+// ScopeBase is a placeholder implementation of the mkparser.Scope.
 // All our scopes are read-only and resolve only simple variables.
 type ScopeBase struct{}
 
diff --git a/mk2rbc/version_defaults.go b/mk2rbc/version_defaults.go
index 27e8198..64645d7 100644
--- a/mk2rbc/version_defaults.go
+++ b/mk2rbc/version_defaults.go
@@ -15,7 +15,6 @@
 package mk2rbc
 
 import (
-	mkparser "android/soong/androidmk/parser"
 	"bytes"
 	"fmt"
 	"io/ioutil"
@@ -23,6 +22,8 @@
 	"sort"
 	"strconv"
 	"strings"
+
+	mkparser "android/soong/androidmk/parser"
 )
 
 const codenamePrefix = "PLATFORM_VERSION_CODENAME."
@@ -97,7 +98,10 @@
 				strings.ToLower(name), genericValue(value)))
 		}
 	}
+
 	sort.Strings(lines)
+	sort.Strings(codenames)
+
 	sink.WriteString("version_defaults = struct(\n")
 	for _, l := range lines {
 		sink.WriteString(l)
diff --git a/rust/binary.go b/rust/binary.go
index 2c3f548..7c18730 100644
--- a/rust/binary.go
+++ b/rust/binary.go
@@ -31,6 +31,11 @@
 	Static_executable *bool `android:"arch_variant"`
 }
 
+type binaryInterface interface {
+	binary() bool
+	staticallyLinked() bool
+}
+
 type binaryDecorator struct {
 	*baseCompiler
 	stripper Stripper
@@ -155,3 +160,11 @@
 	}
 	return binary.baseCompiler.stdLinkage(ctx)
 }
+
+func (binary *binaryDecorator) binary() bool {
+	return true
+}
+
+func (binary *binaryDecorator) staticallyLinked() bool {
+	return Bool(binary.Properties.Static_executable)
+}
diff --git a/rust/builder.go b/rust/builder.go
index 426a569..f79cf9b 100644
--- a/rust/builder.go
+++ b/rust/builder.go
@@ -95,7 +95,7 @@
 
 func TransformSrcToBinary(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
 	outputFile android.WritablePath) buildOutput {
-	flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto")
+	flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin")
 
 	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "bin")
 }
@@ -112,13 +112,13 @@
 
 func TransformSrctoStatic(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
 	outputFile android.WritablePath) buildOutput {
-	flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto")
+	flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin")
 	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "staticlib")
 }
 
 func TransformSrctoShared(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
 	outputFile android.WritablePath) buildOutput {
-	flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto")
+	flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin")
 	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "cdylib")
 }
 
diff --git a/rust/config/Android.bp b/rust/config/Android.bp
index 5b121c3..7757c79 100644
--- a/rust/config/Android.bp
+++ b/rust/config/Android.bp
@@ -16,7 +16,7 @@
         "lints.go",
         "toolchain.go",
         "allowed_list.go",
-        "x86_darwin_host.go",
+        "darwin_host.go",
         "x86_linux_bionic_host.go",
         "x86_linux_host.go",
         "x86_device.go",
diff --git a/rust/config/allowed_list.go b/rust/config/allowed_list.go
index 47ca3a7..8182c32 100644
--- a/rust/config/allowed_list.go
+++ b/rust/config/allowed_list.go
@@ -18,6 +18,7 @@
 		"frameworks/native/libs/binder/rust",
 		"frameworks/proto_logging/stats",
 		"packages/modules/DnsResolver",
+		"packages/modules/Uwb",
 		"packages/modules/Virtualization",
 		"prebuilts/rust",
 		"system/bt",
@@ -32,6 +33,7 @@
 		"system/security",
 		"system/tools/aidl",
 		"tools/security/fuzzing/example_rust_fuzzer",
+		"tools/security/fuzzing/orphans",
 		"vendor/",
 	}
 
diff --git a/rust/config/x86_darwin_host.go b/rust/config/darwin_host.go
similarity index 67%
rename from rust/config/x86_darwin_host.go
rename to rust/config/darwin_host.go
index 8ff0dd4..03bea82 100644
--- a/rust/config/x86_darwin_host.go
+++ b/rust/config/darwin_host.go
@@ -25,41 +25,64 @@
 	DarwinRustLinkFlags = []string{
 		"-B${cc_config.MacToolPath}",
 	}
+	darwinArm64Rustflags = []string{}
+	darwinArm64Linkflags = []string{}
 	darwinX8664Rustflags = []string{}
 	darwinX8664Linkflags = []string{}
 )
 
 func init() {
+	registerToolchainFactory(android.Darwin, android.Arm64, darwinArm64ToolchainFactory)
 	registerToolchainFactory(android.Darwin, android.X86_64, darwinX8664ToolchainFactory)
+
 	pctx.StaticVariable("DarwinToolchainRustFlags", strings.Join(DarwinRustFlags, " "))
 	pctx.StaticVariable("DarwinToolchainLinkFlags", strings.Join(DarwinRustLinkFlags, " "))
+
+	pctx.StaticVariable("DarwinToolchainArm64RustFlags", strings.Join(darwinArm64Rustflags, " "))
+	pctx.StaticVariable("DarwinToolchainArm64LinkFlags", strings.Join(darwinArm64Linkflags, " "))
 	pctx.StaticVariable("DarwinToolchainX8664RustFlags", strings.Join(darwinX8664Rustflags, " "))
 	pctx.StaticVariable("DarwinToolchainX8664LinkFlags", strings.Join(darwinX8664Linkflags, " "))
 
 }
 
 type toolchainDarwin struct {
+	toolchain64Bit
 	toolchainRustFlags string
 	toolchainLinkFlags string
 }
 
-type toolchainDarwinX8664 struct {
-	toolchain64Bit
+type toolchainDarwinArm64 struct {
 	toolchainDarwin
 }
 
+type toolchainDarwinX8664 struct {
+	toolchainDarwin
+}
+
+func (toolchainDarwinArm64) Supported() bool {
+	return true
+}
+
 func (toolchainDarwinX8664) Supported() bool {
 	return true
 }
 
-func (toolchainDarwinX8664) Bionic() bool {
+func (toolchainDarwin) Bionic() bool {
 	return false
 }
 
+func (t *toolchainDarwinArm64) Name() string {
+	return "arm64"
+}
+
 func (t *toolchainDarwinX8664) Name() string {
 	return "x86_64"
 }
 
+func (t *toolchainDarwinArm64) RustTriple() string {
+	return "aarch64-apple-darwin"
+}
+
 func (t *toolchainDarwinX8664) RustTriple() string {
 	return "x86_64-apple-darwin"
 }
@@ -76,6 +99,15 @@
 	return ".dylib"
 }
 
+func (t *toolchainDarwinArm64) ToolchainLinkFlags() string {
+	// Prepend the lld flags from cc_config so we stay in sync with cc
+	return "${cc_config.DarwinLldflags} ${config.DarwinToolchainLinkFlags} ${config.DarwinToolchainArm64LinkFlags}"
+}
+
+func (t *toolchainDarwinArm64) ToolchainRustFlags() string {
+	return "${config.DarwinToolchainRustFlags} ${config.DarwinToolchainArm64RustFlags}"
+}
+
 func (t *toolchainDarwinX8664) ToolchainLinkFlags() string {
 	// Prepend the lld flags from cc_config so we stay in sync with cc
 	return "${cc_config.DarwinLldflags} ${config.DarwinToolchainLinkFlags} ${config.DarwinToolchainX8664LinkFlags}"
@@ -85,8 +117,13 @@
 	return "${config.DarwinToolchainRustFlags} ${config.DarwinToolchainX8664RustFlags}"
 }
 
+func darwinArm64ToolchainFactory(arch android.Arch) Toolchain {
+	return toolchainDarwinArm64Singleton
+}
+
 func darwinX8664ToolchainFactory(arch android.Arch) Toolchain {
 	return toolchainDarwinX8664Singleton
 }
 
+var toolchainDarwinArm64Singleton Toolchain = &toolchainDarwinArm64{}
 var toolchainDarwinX8664Singleton Toolchain = &toolchainDarwinX8664{}
diff --git a/rust/rust.go b/rust/rust.go
index 13169f1..c465cb6 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -261,10 +261,8 @@
 }
 
 func (mod *Module) Binary() bool {
-	if mod.compiler != nil {
-		if _, ok := mod.compiler.(*binaryDecorator); ok {
-			return true
-		}
+	if binary, ok := mod.compiler.(binaryInterface); ok {
+		return binary.binary()
 	}
 	return false
 }
@@ -273,7 +271,7 @@
 	if !mod.Binary() {
 		return false
 	}
-	return Bool(mod.compiler.(*binaryDecorator).Properties.Static_executable)
+	return mod.StaticallyLinked()
 }
 
 func (mod *Module) Object() bool {
@@ -1123,7 +1121,12 @@
 				if cc.IsWholeStaticLib(depTag) {
 					// rustc will bundle static libraries when they're passed with "-lstatic=<lib>". This will fail
 					// if the library is not prefixed by "lib".
-					if libName, ok := libNameFromFilePath(linkObject.Path()); ok {
+					if mod.Binary() {
+						// Binaries may sometimes need to link whole static libraries that don't start with 'lib'.
+						// Since binaries don't need to 'rebundle' these like libraries and only use these for the
+						// final linkage, pass the args directly to the linker to handle these cases.
+						depPaths.depLinkFlags = append(depPaths.depLinkFlags, []string{"-Wl,--whole-archive", linkObject.Path().String(), "-Wl,--no-whole-archive"}...)
+					} else if libName, ok := libNameFromFilePath(linkObject.Path()); ok {
 						depPaths.depFlags = append(depPaths.depFlags, "-lstatic="+libName)
 					} else {
 						ctx.ModuleErrorf("'%q' cannot be listed as a whole_static_library in Rust modules unless the output is prefixed by 'lib'", depName, ctx.ModuleName())
diff --git a/rust/sanitize.go b/rust/sanitize.go
index a4ba4bd..baa383d 100644
--- a/rust/sanitize.go
+++ b/rust/sanitize.go
@@ -311,8 +311,8 @@
 func (mod *Module) StaticallyLinked() bool {
 	if lib, ok := mod.compiler.(libraryInterface); ok {
 		return lib.rlib() || lib.static()
-	} else if binary, ok := mod.compiler.(*binaryDecorator); ok {
-		return Bool(binary.Properties.Static_executable)
+	} else if binary, ok := mod.compiler.(binaryInterface); ok {
+		return binary.staticallyLinked()
 	}
 	return false
 }
diff --git a/scripts/build-aml-prebuilts.sh b/scripts/build-aml-prebuilts.sh
index 8a5513e..1a16f7c 100755
--- a/scripts/build-aml-prebuilts.sh
+++ b/scripts/build-aml-prebuilts.sh
@@ -23,10 +23,10 @@
 export OUT_DIR=${OUT_DIR:-out}
 
 if [ -e ${OUT_DIR}/soong/.soong.kati_enabled ]; then
-  # If ${OUT_DIR} has been created without --skip-make, Soong will create an
+  # If ${OUT_DIR} has been created without --soong-only, Soong will create an
   # ${OUT_DIR}/soong/build.ninja that leaves out many targets which are
   # expected to be supplied by the .mk files, and that might cause errors in
-  # "m --skip-make" below. We therefore default to a different out dir
+  # "m --soong-only" below. We therefore default to a different out dir
   # location in that case.
   AML_OUT_DIR=out/aml
   echo "Avoiding in-make OUT_DIR '${OUT_DIR}' - building in '${AML_OUT_DIR}' instead"
diff --git a/scripts/build-mainline-modules.sh b/scripts/build-mainline-modules.sh
index b05861b..f183c05 100755
--- a/scripts/build-mainline-modules.sh
+++ b/scripts/build-mainline-modules.sh
@@ -17,6 +17,7 @@
 MODULES_SDK_AND_EXPORTS=(
   art-module-sdk
   art-module-test-exports
+  compos-module-sdk
   conscrypt-module-host-exports
   conscrypt-module-sdk
   conscrypt-module-test-exports
@@ -98,7 +99,7 @@
 export FORCE_BUILD_LLVM_COMPONENTS=true
 
 # Create multi-archs SDKs in a different out directory. The multi-arch script
-# uses Soong in --skip-make mode which cannot use the same directory as normal
+# uses Soong in --soong-only mode which cannot use the same directory as normal
 # mode with make.
 export OUT_DIR=${OUT_DIR}/aml
 echo_and_run build/soong/scripts/build-aml-prebuilts.sh \
diff --git a/scripts/build-ndk-prebuilts.sh b/scripts/build-ndk-prebuilts.sh
index a1fa48d..4783037 100755
--- a/scripts/build-ndk-prebuilts.sh
+++ b/scripts/build-ndk-prebuilts.sh
@@ -64,7 +64,7 @@
     "MissingUsesLibraries": ${MISSING_USES_LIBRARIES}
 }
 EOF
-m --skip-make ${SOONG_OUT}/ndk.timestamp
+m --soong-only --skip-config ${SOONG_OUT}/ndk.timestamp
 
 if [ -n "${DIST_DIR}" ]; then
     mkdir -p ${DIST_DIR} || true
diff --git a/scripts/check_boot_jars/package_allowed_list.txt b/scripts/check_boot_jars/package_allowed_list.txt
index b1b1e7e..ed63651 100644
--- a/scripts/check_boot_jars/package_allowed_list.txt
+++ b/scripts/check_boot_jars/package_allowed_list.txt
@@ -69,6 +69,7 @@
 javax\.xml\.transform\.stream
 javax\.xml\.validation
 javax\.xml\.xpath
+jdk\.internal
 jdk\.internal\.math
 jdk\.internal\.misc
 jdk\.internal\.reflect
diff --git a/scripts/gen_ndk_backedby_apex.sh b/scripts/gen_ndk_backedby_apex.sh
index 4abaaba..212362e 100755
--- a/scripts/gen_ndk_backedby_apex.sh
+++ b/scripts/gen_ndk_backedby_apex.sh
@@ -21,52 +21,29 @@
 # After the parse function below "dlopen" would be write to the output file.
 printHelp() {
     echo "**************************** Usage Instructions ****************************"
-    echo "This script is used to generate the Mainline modules backed-by NDK symbols."
+    echo "This script is used to generate the native libraries backed by Mainline modules."
     echo ""
-    echo "To run this script use: ./gen_ndk_backed_by_apex.sh \$OUTPUT_FILE_PATH \$NDK_LIB_NAME_LIST \$MODULE_LIB1 \$MODULE_LIB2..."
+    echo "To run this script use: ./gen_ndk_backed_by_apex.sh \$OUTPUT_FILE_PATH \$MODULE_LIB1 \$MODULE_LIB2..."
     echo "For example: If output write to /backedby.txt then the command would be:"
-    echo "./gen_ndk_backed_by_apex.sh /backedby.txt /ndkLibList.txt lib1.so lib2.so"
+    echo "./gen_ndk_backed_by_apex.sh /backedby.txt lib1.so lib2.so"
     echo "If the module1 is backing lib1 then the backedby.txt would contains: "
-    echo "lib1"
+    echo "lib1.so lib2.so"
 }
 
-contains() {
-  val="$1"
-  shift
-  for x in "$@"; do
-    if [ "$x" = "$val" ]; then
-      return 0
-    fi
-  done
-  return 1
-}
-
-
-genBackedByList() {
+genAllBackedByList() {
   out="$1"
   shift
-  ndk_list="$1"
-  shift
   rm -f "$out"
   touch "$out"
-  while IFS= read -r line
-  do
-    soFileName=$(echo "$line" | sed 's/\(.*so\).*/\1/')
-    if [[ ! -z "$soFileName" && "$soFileName" != *"#"* ]]
-    then
-      if contains "$soFileName" "$@"; then
-        echo "$soFileName" >> "$out"
-      fi
-    fi
-  done < "$ndk_list"
+  echo "$@" >> "$out"
 }
 
 if [[ "$1" == "help" ]]
 then
   printHelp
-elif [[ "$#" -lt 2 ]]
+elif [[ "$#" -lt 1 ]]
 then
-  echo "Wrong argument length. Expecting at least 2 argument representing output path, path to ndk library list, followed by a list of libraries in the Mainline module."
+  echo "Wrong argument length. Expecting at least 1 argument representing output path, followed by a list of libraries in the Mainline module."
 else
-  genBackedByList "$@"
+  genAllBackedByList "$@"
 fi
diff --git a/scripts/manifest_check.py b/scripts/manifest_check.py
index b4936b8..8bed52a 100755
--- a/scripts/manifest_check.py
+++ b/scripts/manifest_check.py
@@ -84,6 +84,13 @@
     return parser.parse_args()
 
 
+C_RED = "\033[1;31m"
+C_GREEN = "\033[1;32m"
+C_BLUE = "\033[1;34m"
+C_OFF = "\033[0m"
+C_BOLD = "\033[1m"
+
+
 def enforce_uses_libraries(manifest, required, optional, relax, is_apk, path):
     """Verify that the <uses-library> tags in the manifest match those provided
 
@@ -119,22 +126,21 @@
     errmsg = ''.join([
         'mismatch in the <uses-library> tags between the build system and the '
         'manifest:\n',
-        '\t- required libraries in build system: [%s]\n' % ', '.join(required),
-        '\t                 vs. in the manifest: [%s]\n' %
-        ', '.join(manifest_required),
-        '\t- optional libraries in build system: [%s]\n' % ', '.join(optional),
-        '\t                 vs. in the manifest: [%s]\n' %
-        ', '.join(manifest_optional),
+        '\t- required libraries in build system: %s[%s]%s\n' % (C_RED, ', '.join(required), C_OFF),
+        '\t                 vs. in the manifest: %s[%s]%s\n' % (C_RED, ', '.join(manifest_required), C_OFF),
+        '\t- optional libraries in build system: %s[%s]%s\n' % (C_RED, ', '.join(optional), C_OFF),
+        '\t                 vs. in the manifest: %s[%s]%s\n' % (C_RED, ', '.join(manifest_optional), C_OFF),
         '\t- tags in the manifest (%s):\n' % path,
         '\t\t%s\n' % '\t\t'.join(tags),
-        'note: the following options are available:\n',
+        '%snote:%s the following options are available:\n' % (C_BLUE, C_OFF),
         '\t- to temporarily disable the check on command line, rebuild with ',
-        'RELAX_USES_LIBRARY_CHECK=true (this will set compiler filter "verify" ',
-        'and disable AOT-compilation in dexpreopt)\n',
+        '%sRELAX_USES_LIBRARY_CHECK=true%s' % (C_BOLD, C_OFF),
+        ' (this will set compiler filter "verify" and disable AOT-compilation in dexpreopt)\n',
         '\t- to temporarily disable the check for the whole product, set ',
-        'PRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true in the product makefiles\n',
-        '\t- to fix the check, make build system properties coherent with the '
-        'manifest\n', '\t- see build/make/Changes.md for details\n'
+        '%sPRODUCT_BROKEN_VERIFY_USES_LIBRARIES := true%s in the product makefiles\n' % (C_BOLD, C_OFF),
+        '\t- to fix the check, make build system properties coherent with the manifest\n',
+        '\t- for details, see %sbuild/make/Changes.md%s' % (C_GREEN, C_OFF),
+        ' and %shttps://source.android.com/devices/tech/dalvik/art-class-loader-context%s\n' % (C_GREEN, C_OFF)
     ])
     #pylint: enable=line-too-long
 
@@ -380,7 +386,7 @@
 
     # pylint: disable=broad-except
     except Exception as err:
-        print('error: ' + str(err), file=sys.stderr)
+        print('%serror:%s ' % (C_RED, C_OFF) + str(err), file=sys.stderr)
         sys.exit(-1)
 
 
diff --git a/scripts/rbc-run b/scripts/rbc-run
index a0907cf..9d30199 100755
--- a/scripts/rbc-run
+++ b/scripts/rbc-run
@@ -4,12 +4,26 @@
 #       that will be passed to rbcrun.
 [[ $# -gt 0 && -f "$1" ]] || { echo "Usage: ${0##*/} product.mk [Additional rbcrun arguments]" >&2; exit 1; }
 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
-declare -r makefile=$1
+
+case $(uname -s) in
+  Linux)
+    declare -r os="linux-x86";
+    ;;
+  Darwin)
+    declare -r os="darwin-x86";
+    ;;
+  *)
+    echo "Unknown OS: $(uname -s)" >&2;
+    exit 1;
+    ;;
+esac
+
+declare -r output_root="${OUT_DIR:-out}"
+declare -r runner="${output_root}/soong/host/${os}/bin/rbcrun"
+declare -r converter="${output_root}/soong/host/${os}/bin/mk2rbc"
+declare -r launcher="$output_root/launchers/run.rbc"
+declare -r makefile="$1"
 shift
-$converter -mode=write -r --outdir $output_root --launcher=$launcher $makefile
-$runner RBC_OUT="make,global" RBC_DEBUG="${RBC_DEBUG:-}" $@ $launcher
+"$converter" -mode=write -r --outdir "$output_root" --launcher="$launcher" "$makefile"
+"$runner" RBC_OUT="make,global" RBC_DEBUG="${RBC_DEBUG:-}" $@ "$launcher"
 
diff --git a/soong.bash b/soong.bash
index 8cf2ec6..db7af7c 100755
--- a/soong.bash
+++ b/soong.bash
@@ -15,8 +15,8 @@
 # limitations under the License.
 
 echo '==== ERROR: bootstrap.bash & ./soong are obsolete ====' >&2
-echo 'Use `m --skip-make` with a standalone OUT_DIR instead.' >&2
+echo 'Use `m --soong-only` with a standalone OUT_DIR instead.' >&2
 echo 'Without envsetup.sh, use:' >&2
-echo '  build/soong/soong_ui.bash --make-mode --skip-make' >&2
+echo '  build/soong/soong_ui.bash --make-mode --soong-only' >&2
 echo '======================================================' >&2
 exit 1
diff --git a/tests/bootstrap_test.sh b/tests/bootstrap_test.sh
index a22adc5..e92a561 100755
--- a/tests/bootstrap_test.sh
+++ b/tests/bootstrap_test.sh
@@ -17,10 +17,10 @@
 function test_null_build() {
   setup
   run_soong
-  local bootstrap_mtime1=$(stat -c "%y" out/soong/.bootstrap/build.ninja)
+  local bootstrap_mtime1=$(stat -c "%y" out/soong/bootstrap.ninja)
   local output_mtime1=$(stat -c "%y" out/soong/build.ninja)
   run_soong
-  local bootstrap_mtime2=$(stat -c "%y" out/soong/.bootstrap/build.ninja)
+  local bootstrap_mtime2=$(stat -c "%y" out/soong/bootstrap.ninja)
   local output_mtime2=$(stat -c "%y" out/soong/build.ninja)
 
   if [[ "$bootstrap_mtime1" == "$bootstrap_mtime2" ]]; then
@@ -36,12 +36,12 @@
 function test_soong_build_rebuilt_if_blueprint_changes() {
   setup
   run_soong
-  local mtime1=$(stat -c "%y" out/soong/.bootstrap/build.ninja)
+  local mtime1=$(stat -c "%y" out/soong/bootstrap.ninja)
 
   sed -i 's/pluginGenSrcCmd/pluginGenSrcCmd2/g' build/blueprint/bootstrap/bootstrap.go
 
   run_soong
-  local mtime2=$(stat -c "%y" out/soong/.bootstrap/build.ninja)
+  local mtime2=$(stat -c "%y" out/soong/bootstrap.ninja)
 
   if [[ "$mtime1" == "$mtime2" ]]; then
     fail "Bootstrap Ninja file did not change"
@@ -541,7 +541,7 @@
 function test_bp2build_smoke {
   setup
   run_soong bp2build
-  [[ -e out/soong/.bootstrap/bp2build_workspace_marker ]] || fail "bp2build marker file not created"
+  [[ -e out/soong/bp2build_workspace_marker ]] || fail "bp2build marker file not created"
   [[ -e out/soong/workspace ]] || fail "Bazel workspace not created"
 }
 
@@ -551,7 +551,7 @@
 
   run_soong bp2build
 
-  if [[ ! -f "./out/soong/.bootstrap/bp2build_workspace_marker" ]]; then
+  if [[ ! -f "./out/soong/bp2build_workspace_marker" ]]; then
     fail "Marker file was not generated"
   fi
 }
@@ -592,10 +592,10 @@
   setup
 
   run_soong bp2build
-  local mtime1=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
+  local mtime1=$(stat -c "%y" out/soong/bp2build_workspace_marker)
 
   run_soong bp2build
-  local mtime2=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
+  local mtime2=$(stat -c "%y" out/soong/bp2build_workspace_marker)
 
   if [[ "$mtime1" != "$mtime2" ]]; then
     fail "Output Ninja file changed on null build"
@@ -626,7 +626,7 @@
 function test_multiple_soong_build_modes() {
   setup
   run_soong json-module-graph bp2build nothing
-  if [[ ! -f "out/soong/.bootstrap/bp2build_workspace_marker" ]]; then
+  if [[ ! -f "out/soong/bp2build_workspace_marker" ]]; then
     fail "bp2build marker file was not generated"
   fi
 
@@ -780,11 +780,11 @@
     fail "Output Ninja file changed when switching to bp2build"
   fi
 
-  local marker_mtime1=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
+  local marker_mtime1=$(stat -c "%y" out/soong/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)
+  local marker_mtime2=$(stat -c "%y" out/soong/bp2build_workspace_marker)
   if [[ "$output_mtime1" != "$output_mtime3" ]]; then
     fail "Output Ninja file changed when switching to regular build from bp2build"
   fi
@@ -794,7 +794,7 @@
 
   run_soong bp2build
   local output_mtime4=$(stat -c "%y" out/soong/build.ninja)
-  local marker_mtime3=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
+  local marker_mtime3=$(stat -c "%y" out/soong/bp2build_workspace_marker)
   if [[ "$output_mtime1" != "$output_mtime4" ]]; then
     fail "Output Ninja file changed when switching back to bp2build"
   fi
diff --git a/tests/bp2build_bazel_test.sh b/tests/bp2build_bazel_test.sh
index 379eb65..01b4760 100755
--- a/tests/bp2build_bazel_test.sh
+++ b/tests/bp2build_bazel_test.sh
@@ -11,10 +11,10 @@
 function test_bp2build_null_build() {
   setup
   run_soong bp2build
-  local output_mtime1=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
+  local output_mtime1=$(stat -c "%y" out/soong/bp2build_workspace_marker)
 
   run_soong bp2build
-  local output_mtime2=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
+  local output_mtime2=$(stat -c "%y" out/soong/bp2build_workspace_marker)
 
   if [[ "$output_mtime1" != "$output_mtime2" ]]; then
     fail "Output bp2build marker file changed on null build"
@@ -36,10 +36,10 @@
   touch foo/bar/a.txt foo/bar/b.txt
 
   run_soong bp2build
-  local output_mtime1=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
+  local output_mtime1=$(stat -c "%y" out/soong/bp2build_workspace_marker)
 
   run_soong bp2build
-  local output_mtime2=$(stat -c "%y" out/soong/.bootstrap/bp2build_workspace_marker)
+  local output_mtime2=$(stat -c "%y" out/soong/bp2build_workspace_marker)
 
   if [[ "$output_mtime1" != "$output_mtime2" ]]; then
     fail "Output bp2build marker file changed on null build"
diff --git a/tests/lib.sh b/tests/lib.sh
index e777820..e6074f8 100644
--- a/tests/lib.sh
+++ b/tests/lib.sh
@@ -105,7 +105,7 @@
 }
 
 function run_soong() {
-  build/soong/soong_ui.bash --make-mode --skip-ninja --skip-make --skip-soong-tests "$@"
+  build/soong/soong_ui.bash --make-mode --skip-ninja --skip-config --soong-only --skip-soong-tests "$@"
 }
 
 function create_mock_bazel() {
@@ -125,7 +125,7 @@
 }
 
 run_ninja() {
-  build/soong/soong_ui.bash --make-mode --skip-make --skip-soong-tests "$@"
+  build/soong/soong_ui.bash --make-mode --skip-config --soong-only --skip-soong-tests "$@"
 }
 
 info "Starting Soong integration test suite $(basename $0)"
diff --git a/third_party/zip/reader_test.go b/third_party/zip/reader_test.go
index dfaae78..11c6d6e 100644
--- a/third_party/zip/reader_test.go
+++ b/third_party/zip/reader_test.go
@@ -786,7 +786,7 @@
 	}
 }
 
-// Verify the number of files is sane.
+// Verify the number of files is within expected bounds
 func TestIssue10956(t *testing.T) {
 	data := []byte("PK\x06\x06PK\x06\a0000\x00\x00\x00\x00\x00\x00\x00\x00" +
 		"0000PK\x05\x06000000000000" +
diff --git a/ui/build/config.go b/ui/build/config.go
index 7837cc4..e0fa503 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -784,24 +784,21 @@
 		panic("Unknown GOOS")
 	}
 }
+
 func (c *configImpl) HostToolDir() string {
 	return filepath.Join(c.SoongOutDir(), "host", c.PrebuiltOS(), "bin")
 }
 
 func (c *configImpl) NamedGlobFile(name string) string {
-	return shared.JoinPath(c.SoongOutDir(), ".bootstrap/build-globs."+name+".ninja")
+	return shared.JoinPath(c.SoongOutDir(), "globs-"+name+".ninja")
 }
 
 func (c *configImpl) UsedEnvFile(tag string) string {
 	return shared.JoinPath(c.SoongOutDir(), usedEnvFile+"."+tag)
 }
 
-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")
+	return shared.JoinPath(c.SoongOutDir(), "bp2build_workspace_marker")
 }
 
 func (c *configImpl) SoongDocsHtml() string {
diff --git a/ui/build/dumpvars.go b/ui/build/dumpvars.go
index afec829..3f10f75 100644
--- a/ui/build/dumpvars.go
+++ b/ui/build/dumpvars.go
@@ -263,9 +263,9 @@
 	}, exportEnvVars...), BannerVars...)
 
 	// We need Roboleaf converter and runner in the mixed mode
-	runMicrofactory(ctx, config, ".bootstrap/bin/mk2rbc", "android/soong/mk2rbc/cmd",
+	runMicrofactory(ctx, config, "mk2rbc", "android/soong/mk2rbc/cmd",
 		map[string]string{"android/soong": "build/soong"})
-	runMicrofactory(ctx, config, ".bootstrap/bin/rbcrun", "rbcrun/cmd",
+	runMicrofactory(ctx, config, "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, "")
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 617d293..c7e1435 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -15,6 +15,7 @@
 package build
 
 import (
+	"fmt"
 	"io/ioutil"
 	"os"
 	"path/filepath"
@@ -43,6 +44,12 @@
 	jsonModuleGraphTag = "modulegraph"
 	queryviewTag       = "queryview"
 	soongDocsTag       = "soong_docs"
+
+	// bootstrapEpoch is used to determine if an incremental build is incompatible with the current
+	// version of bootstrap and needs cleaning before continuing the build.  Increment this for
+	// incompatible changes, for example when moving the location of the bpglob binary that is
+	// executed during bootstrap before the primary builder has had a chance to update the path.
+	bootstrapEpoch = 0
 )
 
 func writeEnvironmentFile(ctx Context, envFile string, envDeps map[string]string) error {
@@ -121,20 +128,31 @@
 	}
 }
 
-func writeEmptyGlobFile(ctx Context, path string) {
+func writeEmptyFile(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)
+		ctx.Fatalf("Failed to create parent directories of empty file '%s': %s", path, err)
 	}
 
-	if _, err := os.Stat(path); os.IsNotExist(err) {
+	if exists, err := fileExists(path); err != nil {
+		ctx.Fatalf("Failed to check if file '%s' exists: %s", path, err)
+	} else if !exists {
 		err = ioutil.WriteFile(path, nil, 0666)
 		if err != nil {
-			ctx.Fatalf("Failed to create empty ninja glob file '%s': %s", path, err)
+			ctx.Fatalf("Failed to create empty file '%s': %s", path, err)
 		}
 	}
 }
 
+func fileExists(path string) (bool, error) {
+	if _, err := os.Stat(path); os.IsNotExist(err) {
+		return false, nil
+	} else if err != nil {
+		return false, err
+	}
+	return true, nil
+}
+
 func primaryBuilderInvocation(config Config, name string, output string, specificArgs []string) bootstrap.PrimaryBuilderInvocation {
 	commonArgs := make([]string, 0, 0)
 
@@ -166,11 +184,46 @@
 	}
 }
 
+// bootstrapEpochCleanup deletes files used by bootstrap during incremental builds across
+// incompatible changes.  Incompatible changes are marked by incrementing the bootstrapEpoch
+// constant.  A tree is considered out of date for the current epoch of the
+// .soong.bootstrap.epoch.<epoch> file doesn't exist.
+func bootstrapEpochCleanup(ctx Context, config Config) {
+	epochFile := fmt.Sprintf(".soong.bootstrap.epoch.%d", bootstrapEpoch)
+	epochPath := filepath.Join(config.SoongOutDir(), epochFile)
+	if exists, err := fileExists(epochPath); err != nil {
+		ctx.Fatalf("failed to check if bootstrap epoch file %q exists: %q", epochPath, err)
+	} else if !exists {
+		// The tree is out of date for the current epoch, delete files used by bootstrap
+		// and force the primary builder to rerun.
+		os.Remove(filepath.Join(config.SoongOutDir(), "build.ninja"))
+		for _, globFile := range bootstrapGlobFileList(config) {
+			os.Remove(globFile)
+		}
+
+		// Mark the tree as up to date with the current epoch by writing the epoch marker file.
+		writeEmptyFile(ctx, epochPath)
+	}
+}
+
+func bootstrapGlobFileList(config Config) []string {
+	return []string{
+		config.NamedGlobFile(soongBuildTag),
+		config.NamedGlobFile(bp2buildTag),
+		config.NamedGlobFile(jsonModuleGraphTag),
+		config.NamedGlobFile(queryviewTag),
+		config.NamedGlobFile(soongDocsTag),
+	}
+}
+
 func bootstrapBlueprint(ctx Context, config Config) {
 	ctx.BeginTrace(metrics.RunSoong, "blueprint bootstrap")
 	defer ctx.EndTrace()
 
-	mainSoongBuildExtraArgs := []string{"-o", config.MainNinjaFile()}
+	// Clean up some files for incremental builds across incompatible changes.
+	bootstrapEpochCleanup(ctx, config)
+
+	mainSoongBuildExtraArgs := []string{"-o", config.SoongNinjaFile()}
 	if config.EmptyNinjaFile() {
 		mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--empty-ninja-file")
 	}
@@ -178,7 +231,7 @@
 	mainSoongBuildInvocation := primaryBuilderInvocation(
 		config,
 		soongBuildTag,
-		config.MainNinjaFile(),
+		config.SoongNinjaFile(),
 		mainSoongBuildExtraArgs)
 
 	if config.bazelBuildMode() == mixedBuild {
@@ -232,14 +285,14 @@
 	// The glob .ninja files are subninja'd. However, they are generated during
 	// the build itself so we write an empty file if the file does not exist yet
 	// so that the subninja doesn't fail on clean builds
-	for _, globFile := range globFiles {
-		writeEmptyGlobFile(ctx, globFile)
+	for _, globFile := range bootstrapGlobFileList(config) {
+		writeEmptyFile(ctx, globFile)
 	}
 
 	var blueprintArgs bootstrap.Args
 
 	blueprintArgs.ModuleListFile = filepath.Join(config.FileListDir(), "Android.bp.list")
-	blueprintArgs.OutFile = shared.JoinPath(config.SoongOutDir(), ".bootstrap/build.ninja")
+	blueprintArgs.OutFile = shared.JoinPath(config.SoongOutDir(), "bootstrap.ninja")
 	blueprintArgs.EmptyNinjaFile = false
 
 	blueprintCtx := blueprint.NewContext()
@@ -261,7 +314,7 @@
 	}
 
 	bootstrapDeps := bootstrap.RunBlueprint(blueprintArgs, bootstrap.DoEverything, blueprintCtx, blueprintConfig)
-	bootstrapDepFile := shared.JoinPath(config.SoongOutDir(), ".bootstrap/build.ninja.d")
+	bootstrapDepFile := shared.JoinPath(config.SoongOutDir(), "bootstrap.ninja.d")
 	err := deptools.WriteDepFile(bootstrapDepFile, blueprintArgs.OutFile, bootstrapDeps)
 	if err != nil {
 		ctx.Fatalf("Error writing depfile '%s': %s", bootstrapDepFile, err)
@@ -289,11 +342,6 @@
 	// unused variables were changed?
 	envFile := filepath.Join(config.SoongOutDir(), availableEnvFile)
 
-	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
 
@@ -342,7 +390,7 @@
 		}
 	}()
 
-	runMicrofactory(ctx, config, filepath.Join(config.HostToolDir(), "bpglob"), "github.com/google/blueprint/bootstrap/bpglob",
+	runMicrofactory(ctx, config, "bpglob", "github.com/google/blueprint/bootstrap/bpglob",
 		map[string]string{"github.com/google/blueprint": "build/blueprint"})
 
 	ninja := func(name, ninjaFile string, targets ...string) {
@@ -401,10 +449,10 @@
 
 	if config.SoongBuildInvocationNeeded() {
 		// This build generates <builddir>/build.ninja, which is used later by build/soong/ui/build/build.go#Build().
-		targets = append(targets, config.MainNinjaFile())
+		targets = append(targets, config.SoongNinjaFile())
 	}
 
-	ninja("bootstrap", ".bootstrap/build.ninja", targets...)
+	ninja("bootstrap", "bootstrap.ninja", targets...)
 
 	var soongBuildMetrics *soong_metrics_proto.SoongBuildMetrics
 	if shouldCollectBuildSoongMetrics(config) {
@@ -424,8 +472,7 @@
 	}
 }
 
-func runMicrofactory(ctx Context, config Config, relExePath string, pkg string, mapping map[string]string) {
-	name := filepath.Base(relExePath)
+func runMicrofactory(ctx Context, config Config, name string, pkg string, mapping map[string]string) {
 	ctx.BeginTrace(metrics.RunSoong, name)
 	defer ctx.EndTrace()
 	cfg := microfactory.Config{TrimPath: absPath(ctx, ".")}
@@ -433,7 +480,7 @@
 		cfg.Map(pkgPrefix, pathPrefix)
 	}
 
-	exePath := filepath.Join(config.SoongOutDir(), relExePath)
+	exePath := filepath.Join(config.SoongOutDir(), name)
 	dir := filepath.Dir(exePath)
 	if err := os.MkdirAll(dir, 0777); err != nil {
 		ctx.Fatalf("cannot create %s: %s", dir, err)
diff --git a/ui/build/test_build.go b/ui/build/test_build.go
index f9a60b6..83007be 100644
--- a/ui/build/test_build.go
+++ b/ui/build/test_build.go
@@ -63,7 +63,6 @@
 	cmd.StartOrFatal()
 
 	outDir := config.OutDir()
-	bootstrapDir := filepath.Join(outDir, "soong", ".bootstrap")
 	modulePathsDir := filepath.Join(outDir, ".module_paths")
 	variablesFilePath := filepath.Join(outDir, "soong", "soong.variables")
 
@@ -86,8 +85,7 @@
 			// Leaf node is not in the out directory.
 			continue
 		}
-		if strings.HasPrefix(line, bootstrapDir) ||
-			strings.HasPrefix(line, modulePathsDir) ||
+		if strings.HasPrefix(line, modulePathsDir) ||
 			line == variablesFilePath ||
 			line == dexpreoptConfigFilePath ||
 			line == buildDatetimeFilePath {