Merge "Revert "Add aidl.deps prop to include aidl implicit deps explicitly in Android.bp""
diff --git a/OWNERS b/OWNERS
index 964e27a..9221d3e 100644
--- a/OWNERS
+++ b/OWNERS
@@ -3,7 +3,6 @@
 
 # AMER
 agespino@google.com
-alexmarquez@google.com
 ccross@android.com
 colefaust@google.com
 cparsons@google.com
@@ -11,10 +10,12 @@
 delmerico@google.com
 dwillemsen@google.com
 eakammer@google.com
+jihoonkang@google.com
 jobredeaux@google.com
 joeo@google.com
 juu@google.com
 lamontjones@google.com
+mrziwang@google.com
 spandandas@google.com
 tradical@google.com
 usta@google.com
diff --git a/README.md b/README.md
index 70311cb..2d8f0af 100644
--- a/README.md
+++ b/README.md
@@ -565,6 +565,12 @@
 by all of the vendor's other modules using the normal namespace and visibility
 rules.
 
+`soongConfigTraceMutator` enables modules affected by soong config variables to
+write outputs into a hashed directory path. It does this by recording accesses
+to soong config variables on each module, and then accumulating records of each
+module's all dependencies. `m soong_config_trace` builds information about
+hashes to `$OUT_DIR/soong/soong_config_trace.json`.
+
 ## Build logic
 
 The build logic is written in Go using the
diff --git a/aidl_library/Android.bp b/aidl_library/Android.bp
new file mode 100644
index 0000000..ec21504
--- /dev/null
+++ b/aidl_library/Android.bp
@@ -0,0 +1,32 @@
+// Copyright 2023 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: "soong-aidl-library",
+    pkgPath: "android/soong/aidl_library",
+    deps: [
+        "soong-android",
+    ],
+    srcs: [
+        "aidl_library.go",
+    ],
+    testSrcs: [
+        "aidl_library_test.go",
+    ],
+    pluginFor: ["soong_build"],
+}
diff --git a/aidl_library/aidl_library.go b/aidl_library/aidl_library.go
new file mode 100644
index 0000000..5985103
--- /dev/null
+++ b/aidl_library/aidl_library.go
@@ -0,0 +1,187 @@
+// Copyright 2023 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 aidl_library
+
+import (
+	"android/soong/android"
+	"android/soong/bazel"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+)
+
+var PrepareForTestWithAidlLibrary = android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+	registerAidlLibraryBuildComponents(ctx)
+})
+
+func init() {
+	registerAidlLibraryBuildComponents(android.InitRegistrationContext)
+}
+
+func registerAidlLibraryBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("aidl_library", AidlLibraryFactory)
+}
+
+type aidlLibraryProperties struct {
+	// srcs lists files that are included in this module for aidl compilation
+	Srcs []string `android:"path"`
+
+	// hdrs lists the headers that are imported by srcs but are not compiled by aidl to language binding code
+	// hdrs is provided to support Bazel migration. It is a no-op until
+	// we enable input sandbox in aidl compilation action
+	Hdrs []string `android:"path"`
+
+	// The prefix to strip from the paths of the .aidl files
+	// The remaining path is the package path of the aidl interface
+	Strip_import_prefix *string
+
+	// List of aidl files or aidl_library depended on by the module
+	Deps []string `android:"arch_variant"`
+}
+
+type AidlLibrary struct {
+	android.ModuleBase
+	android.BazelModuleBase
+	properties aidlLibraryProperties
+}
+
+type bazelAidlLibraryAttributes struct {
+	Srcs                bazel.LabelListAttribute
+	Hdrs                bazel.LabelListAttribute
+	Strip_import_prefix *string
+	Deps                bazel.LabelListAttribute
+}
+
+func (lib *AidlLibrary) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+	srcs := bazel.MakeLabelListAttribute(
+		android.BazelLabelForModuleSrc(
+			ctx,
+			lib.properties.Srcs,
+		),
+	)
+
+	hdrs := bazel.MakeLabelListAttribute(
+		android.BazelLabelForModuleSrc(
+			ctx,
+			lib.properties.Hdrs,
+		),
+	)
+
+	tags := []string{"apex_available=//apex_available:anyapex"}
+	deps := bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, lib.properties.Deps))
+
+	attrs := &bazelAidlLibraryAttributes{
+		Srcs:                srcs,
+		Hdrs:                hdrs,
+		Strip_import_prefix: lib.properties.Strip_import_prefix,
+		Deps:                deps,
+	}
+
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class:        "aidl_library",
+		Bzl_load_location: "//build/bazel/rules/aidl:aidl_library.bzl",
+	}
+
+	ctx.CreateBazelTargetModule(
+		props,
+		android.CommonAttributes{
+			Name: lib.Name(),
+			Tags: bazel.MakeStringListAttribute(tags),
+		},
+		attrs,
+	)
+}
+
+type AidlLibraryInfo struct {
+	// The direct aidl files of the module
+	Srcs android.Paths
+	// The include dirs to the direct aidl files and those provided from transitive aidl_library deps
+	IncludeDirs android.DepSet
+	// The direct hdrs and hdrs from transitive deps
+	Hdrs android.DepSet
+}
+
+// AidlLibraryProvider provides the srcs and the transitive include dirs
+var AidlLibraryProvider = blueprint.NewProvider(AidlLibraryInfo{})
+
+func (lib *AidlLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	includeDirsDepSetBuilder := android.NewDepSetBuilder(android.PREORDER)
+	hdrsDepSetBuilder := android.NewDepSetBuilder(android.PREORDER)
+
+	if len(lib.properties.Srcs) == 0 && len(lib.properties.Hdrs) == 0 {
+		ctx.ModuleErrorf("at least srcs or hdrs prop must be non-empty")
+	}
+
+	srcs := android.PathsForModuleSrc(ctx, lib.properties.Srcs)
+	hdrs := android.PathsForModuleSrc(ctx, lib.properties.Hdrs)
+
+	if lib.properties.Strip_import_prefix != nil {
+		srcs = android.PathsWithModuleSrcSubDir(
+			ctx,
+			srcs,
+			android.String(lib.properties.Strip_import_prefix),
+		)
+
+		hdrs = android.PathsWithModuleSrcSubDir(
+			ctx,
+			hdrs,
+			android.String(lib.properties.Strip_import_prefix),
+		)
+	}
+	hdrsDepSetBuilder.Direct(hdrs...)
+
+	includeDir := android.PathForModuleSrc(
+		ctx,
+		proptools.StringDefault(lib.properties.Strip_import_prefix, ""),
+	)
+	includeDirsDepSetBuilder.Direct(includeDir)
+
+	for _, dep := range ctx.GetDirectDepsWithTag(aidlLibraryTag) {
+		if ctx.OtherModuleHasProvider(dep, AidlLibraryProvider) {
+			info := ctx.OtherModuleProvider(dep, AidlLibraryProvider).(AidlLibraryInfo)
+			includeDirsDepSetBuilder.Transitive(&info.IncludeDirs)
+			hdrsDepSetBuilder.Transitive(&info.Hdrs)
+		}
+	}
+
+	ctx.SetProvider(AidlLibraryProvider, AidlLibraryInfo{
+		Srcs:        srcs,
+		IncludeDirs: *includeDirsDepSetBuilder.Build(),
+		Hdrs:        *hdrsDepSetBuilder.Build(),
+	})
+}
+
+// aidl_library contains a list of .aidl files and the strip_import_prefix to
+// to strip from the paths of the .aidl files. The sub-path left-over after stripping
+// corresponds to the aidl package path the aidl interfaces are scoped in
+func AidlLibraryFactory() android.Module {
+	module := &AidlLibrary{}
+	module.AddProperties(&module.properties)
+	android.InitAndroidModule(module)
+	android.InitBazelModule(module)
+	return module
+}
+
+type aidlDependencyTag struct {
+	blueprint.BaseDependencyTag
+}
+
+var aidlLibraryTag = aidlDependencyTag{}
+
+func (lib *AidlLibrary) DepsMutator(ctx android.BottomUpMutatorContext) {
+	for _, dep := range lib.properties.Deps {
+		ctx.AddDependency(lib, aidlLibraryTag, dep)
+	}
+}
diff --git a/aidl_library/aidl_library_test.go b/aidl_library/aidl_library_test.go
new file mode 100644
index 0000000..d9dd245
--- /dev/null
+++ b/aidl_library/aidl_library_test.go
@@ -0,0 +1,136 @@
+// Copyright 2023 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 aidl_library
+
+import (
+	"android/soong/android"
+	"testing"
+)
+
+func TestAidlLibrary(t *testing.T) {
+	t.Parallel()
+	ctx := android.GroupFixturePreparers(
+		PrepareForTestWithAidlLibrary,
+		android.MockFS{
+			"package_bar/Android.bp": []byte(`
+			aidl_library {
+					name: "bar",
+					srcs: ["x/y/Bar.aidl"],
+					strip_import_prefix: "x",
+				}
+			`),
+		}.AddToFixture(),
+		android.MockFS{
+			"package_foo/Android.bp": []byte(`
+			aidl_library {
+					name: "foo",
+					srcs: ["a/b/Foo.aidl"],
+					hdrs: ["a/Header.aidl"],
+					strip_import_prefix: "a",
+					deps: ["bar"],
+				}
+			`),
+		}.AddToFixture(),
+	).RunTest(t).TestContext
+
+	foo := ctx.ModuleForTests("foo", "").Module().(*AidlLibrary)
+	actualInfo := ctx.ModuleProvider(foo, AidlLibraryProvider).(AidlLibraryInfo)
+
+	android.AssertArrayString(
+		t,
+		"aidl include dirs",
+		[]string{"package_foo/a", "package_bar/x"},
+		actualInfo.IncludeDirs.ToList().Strings(),
+	)
+
+	android.AssertPathsRelativeToTopEquals(
+		t,
+		"aidl srcs paths",
+		[]string{"package_foo/a/b/Foo.aidl"},
+		actualInfo.Srcs,
+	)
+
+	android.AssertPathsRelativeToTopEquals(
+		t,
+		"aidl hdrs paths",
+		[]string{"package_foo/a/Header.aidl"},
+		actualInfo.Hdrs.ToList(),
+	)
+}
+
+func TestAidlLibraryWithoutStripImportPrefix(t *testing.T) {
+	t.Parallel()
+	ctx := android.GroupFixturePreparers(
+		PrepareForTestWithAidlLibrary,
+		android.MockFS{
+			"package_bar/Android.bp": []byte(`
+			aidl_library {
+					name: "bar",
+					srcs: ["x/y/Bar.aidl"],
+					hdrs: ["BarHeader.aidl"],
+				}
+			`),
+		}.AddToFixture(),
+		android.MockFS{
+			"package_foo/Android.bp": []byte(`
+			aidl_library {
+					name: "foo",
+					srcs: ["a/b/Foo.aidl"],
+					deps: ["bar"],
+				}
+			`),
+		}.AddToFixture(),
+	).RunTest(t).TestContext
+
+	foo := ctx.ModuleForTests("foo", "").Module().(*AidlLibrary)
+	actualInfo := ctx.ModuleProvider(foo, AidlLibraryProvider).(AidlLibraryInfo)
+
+	android.AssertArrayString(
+		t,
+		"aidl include dirs",
+		[]string{"package_foo", "package_bar"},
+		actualInfo.IncludeDirs.ToList().Strings(),
+	)
+
+	android.AssertPathsRelativeToTopEquals(
+		t,
+		"aidl srcs paths",
+		[]string{"package_foo/a/b/Foo.aidl"},
+		actualInfo.Srcs,
+	)
+
+	android.AssertPathsRelativeToTopEquals(
+		t,
+		"aidl hdrs paths",
+		[]string{"package_bar/BarHeader.aidl"},
+		actualInfo.Hdrs.ToList(),
+	)
+}
+
+func TestAidlLibraryWithNoSrcsHdrsDeps(t *testing.T) {
+	t.Parallel()
+	android.GroupFixturePreparers(
+		PrepareForTestWithAidlLibrary,
+		android.MockFS{
+			"package_bar/Android.bp": []byte(`
+			aidl_library {
+					name: "bar",
+				}
+			`),
+		}.AddToFixture(),
+	).
+		ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern("at least srcs or hdrs prop must be non-empty")).
+		RunTest(t)
+}
diff --git a/android/Android.bp b/android/Android.bp
index 641c438..94d2c04 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -17,6 +17,7 @@
         "soong-remoteexec",
         "soong-response",
         "soong-shared",
+        "soong-starlark",
         "soong-starlark-format",
         "soong-ui-metrics_proto",
         "soong-android-allowlists",
@@ -60,7 +61,6 @@
         "license_metadata.go",
         "license_sdk_member.go",
         "licenses.go",
-        "makefile_goal.go",
         "makevars.go",
         "metrics.go",
         "module.go",
@@ -77,6 +77,7 @@
         "path_properties.go",
         "paths.go",
         "phony.go",
+        "plugin.go",
         "prebuilt.go",
         "prebuilt_build_tool.go",
         "proto.go",
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go
index 14c713f..f826f5c 100644
--- a/android/allowlists/allowlists.go
+++ b/android/allowlists/allowlists.go
@@ -38,7 +38,13 @@
 	// allows modules to opt-in.
 	Bp2BuildDefaultFalseRecursively
 
-	DEFAULT_NINJA_WEIGHT = 1000
+	// Modules with build time of more than half a minute should have high priority.
+	DEFAULT_PRIORITIZED_WEIGHT = 1000
+	// Modules with build time of more than a few minute should have higher priority.
+	HIGH_PRIORITIZED_WEIGHT = 10 * DEFAULT_PRIORITIZED_WEIGHT
+	// Modules with inputs greater than the threshold should have high priority.
+	// Adjust this threshold if there are lots of wrong predictions.
+	INPUT_SIZE_THRESHOLD = 50
 )
 
 var (
@@ -59,7 +65,9 @@
 
 		"build/bazel":                        Bp2BuildDefaultTrueRecursively,
 		"build/make/target/product/security": Bp2BuildDefaultTrue,
+		"build/make/tools/protos":            Bp2BuildDefaultTrue,
 		"build/make/tools/releasetools":      Bp2BuildDefaultTrue,
+		"build/make/tools/sbom":              Bp2BuildDefaultTrue,
 		"build/make/tools/signapk":           Bp2BuildDefaultTrue,
 		"build/make/tools/zipalign":          Bp2BuildDefaultTrueRecursively,
 		"build/soong":                        Bp2BuildDefaultTrue,
@@ -77,6 +85,7 @@
 		"development/apps/DevelopmentSettings":        Bp2BuildDefaultTrue,
 		"development/apps/Fallback":                   Bp2BuildDefaultTrue,
 		"development/apps/WidgetPreview":              Bp2BuildDefaultTrue,
+		"development/python-packages/adb":             Bp2BuildDefaultTrueRecursively,
 		"development/samples/BasicGLSurfaceView":      Bp2BuildDefaultTrue,
 		"development/samples/BluetoothChat":           Bp2BuildDefaultTrue,
 		"development/samples/BrokenKeyDerivation":     Bp2BuildDefaultTrue,
@@ -132,6 +141,7 @@
 		"external/f2fs-tools":                    Bp2BuildDefaultTrue,
 		"external/flac":                          Bp2BuildDefaultTrueRecursively,
 		"external/fmtlib":                        Bp2BuildDefaultTrueRecursively,
+		"external/fsverity-utils":                Bp2BuildDefaultTrueRecursively,
 		"external/guava":                         Bp2BuildDefaultTrueRecursively,
 		"external/google-benchmark":              Bp2BuildDefaultTrueRecursively,
 		"external/googletest":                    Bp2BuildDefaultTrueRecursively,
@@ -183,6 +193,7 @@
 		"external/selinux/libselinux":            Bp2BuildDefaultTrueRecursively,
 		"external/selinux/libsepol":              Bp2BuildDefaultTrueRecursively,
 		"external/speex":                         Bp2BuildDefaultTrueRecursively,
+		"external/sqlite":                        Bp2BuildDefaultTrueRecursively,
 		"external/tinyalsa":                      Bp2BuildDefaultTrueRecursively,
 		"external/tinyalsa_new":                  Bp2BuildDefaultTrueRecursively,
 		"external/toybox":                        Bp2BuildDefaultTrueRecursively,
@@ -191,15 +202,20 @@
 		"external/zstd":                          Bp2BuildDefaultTrueRecursively,
 
 		"frameworks/av": Bp2BuildDefaultTrue,
+		"frameworks/av/media/audioaidlconversion":            Bp2BuildDefaultTrueRecursively,
 		"frameworks/av/media/codec2/components/aom":          Bp2BuildDefaultTrueRecursively,
 		"frameworks/av/media/codecs":                         Bp2BuildDefaultTrueRecursively,
 		"frameworks/av/media/liberror":                       Bp2BuildDefaultTrueRecursively,
+		"frameworks/av/media/libmediahelper":                 Bp2BuildDefaultTrue,
 		"frameworks/av/media/libshmem":                       Bp2BuildDefaultTrueRecursively,
-		"frameworks/av/media/audioaidlconversion":            Bp2BuildDefaultTrueRecursively,
+		"frameworks/av/media/module/codecs":                  Bp2BuildDefaultTrueRecursively,
+		"frameworks/av/media/module/foundation":              Bp2BuildDefaultTrueRecursively,
 		"frameworks/av/media/module/minijail":                Bp2BuildDefaultTrueRecursively,
 		"frameworks/av/services/minijail":                    Bp2BuildDefaultTrueRecursively,
 		"frameworks/base/libs/androidfw":                     Bp2BuildDefaultTrue,
+		"frameworks/base/libs/services":                      Bp2BuildDefaultTrue,
 		"frameworks/base/media/tests/MediaDump":              Bp2BuildDefaultTrue,
+		"frameworks/base/proto":                              Bp2BuildDefaultTrue,
 		"frameworks/base/services/tests/servicestests/aidl":  Bp2BuildDefaultTrue,
 		"frameworks/base/startop/apps/test":                  Bp2BuildDefaultTrue,
 		"frameworks/base/tests/appwidgets/AppWidgetHostTest": Bp2BuildDefaultTrueRecursively,
@@ -207,18 +223,20 @@
 		"frameworks/base/tools/codegen":                      Bp2BuildDefaultTrueRecursively,
 		"frameworks/base/tools/streaming_proto":              Bp2BuildDefaultTrueRecursively,
 		"frameworks/hardware/interfaces/stats/aidl":          Bp2BuildDefaultTrue,
+		"frameworks/libs/modules-utils/build":                Bp2BuildDefaultTrueRecursively,
+		"frameworks/libs/net/common/native":                  Bp2BuildDefaultTrueRecursively,
 		"frameworks/native/libs/adbd_auth":                   Bp2BuildDefaultTrueRecursively,
 		"frameworks/native/libs/arect":                       Bp2BuildDefaultTrueRecursively,
 		"frameworks/native/libs/gui":                         Bp2BuildDefaultTrue,
 		"frameworks/native/libs/math":                        Bp2BuildDefaultTrueRecursively,
 		"frameworks/native/libs/nativebase":                  Bp2BuildDefaultTrueRecursively,
+		"frameworks/native/libs/permission":                  Bp2BuildDefaultTrueRecursively,
 		"frameworks/native/libs/vr":                          Bp2BuildDefaultTrueRecursively,
 		"frameworks/native/opengl/tests/gl2_cameraeye":       Bp2BuildDefaultTrue,
 		"frameworks/native/opengl/tests/gl2_java":            Bp2BuildDefaultTrue,
 		"frameworks/native/opengl/tests/testLatency":         Bp2BuildDefaultTrue,
 		"frameworks/native/opengl/tests/testPauseResume":     Bp2BuildDefaultTrue,
 		"frameworks/native/opengl/tests/testViewport":        Bp2BuildDefaultTrue,
-		"frameworks/native/libs/permission":                  Bp2BuildDefaultTrue,
 		"frameworks/native/services/batteryservice":          Bp2BuildDefaultTrue,
 		"frameworks/proto_logging/stats":                     Bp2BuildDefaultTrueRecursively,
 
@@ -290,20 +308,25 @@
 		"packages/modules/Gki/libkver":                       Bp2BuildDefaultTrue,
 		"packages/modules/NetworkStack/common/captiveportal": Bp2BuildDefaultTrue,
 		"packages/modules/NeuralNetworks/apex":               Bp2BuildDefaultTrue,
+		"packages/modules/NeuralNetworks/apex/testing":       Bp2BuildDefaultTrue,
 		"packages/providers/MediaProvider/tools/dialogs":     Bp2BuildDefaultFalse, // TODO(b/242834374)
 		"packages/screensavers/Basic":                        Bp2BuildDefaultTrue,
 		"packages/services/Car/tests/SampleRearViewCamera":   Bp2BuildDefaultFalse, // TODO(b/242834321)
 
 		"platform_testing/tests/example": Bp2BuildDefaultTrueRecursively,
 
-		"prebuilts/clang/host/linux-x86":           Bp2BuildDefaultTrueRecursively,
-		"prebuilts/gradle-plugin":                  Bp2BuildDefaultTrueRecursively,
-		"prebuilts/runtime/mainline/platform/sdk":  Bp2BuildDefaultTrueRecursively,
-		"prebuilts/sdk/current/androidx":           Bp2BuildDefaultTrue,
-		"prebuilts/sdk/current/extras/app-toolkit": Bp2BuildDefaultTrue,
-		"prebuilts/sdk/current/support":            Bp2BuildDefaultTrue,
-		"prebuilts/tools":                          Bp2BuildDefaultTrue,
-		"prebuilts/tools/common/m2":                Bp2BuildDefaultTrue,
+		"prebuilts/clang/host/linux-x86":                   Bp2BuildDefaultTrueRecursively,
+		"prebuilts/gradle-plugin":                          Bp2BuildDefaultTrueRecursively,
+		"prebuilts/runtime/mainline/platform/sdk":          Bp2BuildDefaultTrueRecursively,
+		"prebuilts/sdk":                                    Bp2BuildDefaultTrue,
+		"prebuilts/sdk/current/androidx":                   Bp2BuildDefaultTrue,
+		"prebuilts/sdk/current/androidx-legacy":            Bp2BuildDefaultTrue,
+		"prebuilts/sdk/current/extras/constraint-layout-x": Bp2BuildDefaultTrue,
+		"prebuilts/sdk/current/extras/material-design-x":   Bp2BuildDefaultTrue,
+		"prebuilts/sdk/current/extras/app-toolkit":         Bp2BuildDefaultTrue,
+		"prebuilts/sdk/current/support":                    Bp2BuildDefaultTrue,
+		"prebuilts/tools":                                  Bp2BuildDefaultTrue,
+		"prebuilts/tools/common/m2":                        Bp2BuildDefaultTrue,
 
 		"sdk/dumpeventlog":  Bp2BuildDefaultTrue,
 		"sdk/eventanalyzer": Bp2BuildDefaultTrue,
@@ -324,6 +347,7 @@
 		"system/core/libprocessgroup":                            Bp2BuildDefaultTrue,
 		"system/core/libprocessgroup/cgrouprc":                   Bp2BuildDefaultTrue,
 		"system/core/libprocessgroup/cgrouprc_format":            Bp2BuildDefaultTrue,
+		"system/core/libsparse":                                  Bp2BuildDefaultTrueRecursively,
 		"system/core/libsuspend":                                 Bp2BuildDefaultTrue,
 		"system/core/libsystem":                                  Bp2BuildDefaultTrueRecursively,
 		"system/core/libsysutils":                                Bp2BuildDefaultTrueRecursively,
@@ -332,7 +356,9 @@
 		"system/core/mkbootfs":                                   Bp2BuildDefaultTrueRecursively,
 		"system/core/property_service/libpropertyinfoparser":     Bp2BuildDefaultTrueRecursively,
 		"system/core/property_service/libpropertyinfoserializer": Bp2BuildDefaultTrueRecursively,
+		"system/extras/f2fs_utils":                               Bp2BuildDefaultTrueRecursively,
 		"system/extras/toolchain-extras":                         Bp2BuildDefaultTrue,
+		"system/extras/verity":                                   Bp2BuildDefaultTrueRecursively,
 		"system/hardware/interfaces/media":                       Bp2BuildDefaultTrueRecursively,
 		"system/incremental_delivery/incfs":                      Bp2BuildDefaultTrue,
 		"system/libartpalette":                                   Bp2BuildDefaultTrueRecursively,
@@ -362,6 +388,7 @@
 		"system/memory/libion":                                   Bp2BuildDefaultTrueRecursively,
 		"system/memory/libmemunreachable":                        Bp2BuildDefaultTrueRecursively,
 		"system/sepolicy/apex":                                   Bp2BuildDefaultTrueRecursively,
+		"system/security/fsverity":                               Bp2BuildDefaultTrueRecursively,
 		"system/testing/gtest_extras":                            Bp2BuildDefaultTrueRecursively,
 		"system/timezone/apex":                                   Bp2BuildDefaultTrueRecursively,
 		"system/timezone/output_data":                            Bp2BuildDefaultTrueRecursively,
@@ -375,6 +402,7 @@
 		"system/tools/xsdc/utils":                                Bp2BuildDefaultTrueRecursively,
 		"system/unwinding/libunwindstack":                        Bp2BuildDefaultTrueRecursively,
 
+		"tools/apifinder":                            Bp2BuildDefaultTrue,
 		"tools/apksig":                               Bp2BuildDefaultTrue,
 		"tools/external_updater":                     Bp2BuildDefaultTrueRecursively,
 		"tools/metalava":                             Bp2BuildDefaultTrue,
@@ -433,16 +461,17 @@
 
 		// TODO(b/266459895): remove this and the placeholder BUILD file after re-enabling libunwindstack
 		"external/rust/crates/rustc-demangle-capi":/* recursive = */ false,
+
+		// Used for testing purposes only. Should not actually exist in the real source tree.
+		"testpkg/keep_build_file":/* recursive = */ false,
 	}
 
 	Bp2buildModuleAlwaysConvertList = []string{
 		"libidmap2_policies",
 		"libSurfaceFlingerProp",
 		// cc mainline modules
-		"code_coverage.policy",
-		"code_coverage.policy.other",
-		"codec2_soft_exports",
-		"codecs_g711dec",
+
+		// com.android.media.swcodec
 		"com.android.media.swcodec",
 		"com.android.media.swcodec-androidManifest",
 		"com.android.media.swcodec-ld.config.txt",
@@ -450,11 +479,17 @@
 		"com.android.media.swcodec-mediaswcodec.rc",
 		"com.android.media.swcodec.certificate",
 		"com.android.media.swcodec.key",
+		"test_com.android.media.swcodec",
+
+		// deps
+		"code_coverage.policy",
+		"code_coverage.policy.other",
+		"codec2_soft_exports",
+		"compatibility_matrix_schema",
 		"flatbuffer_headers",
 		"framework-connectivity-protos",
 		"gemmlowp_headers",
 		"gl_headers",
-		"ipconnectivity-proto-src",
 		"libandroid_runtime_lazy",
 		"libandroid_runtime_vm_headers",
 		"libaudioclient_aidl_conversion_util",
@@ -488,20 +523,6 @@
 		"libandroidio",
 		"libandroidio_srcs",
 		"libserviceutils",
-		"libstagefright_amrnbenc",
-		"libstagefright_amrnbdec",
-		"libstagefright_amrwbdec",
-		"libstagefright_amrwbenc",
-		"libstagefright_amrnb_common",
-		"libstagefright_enc_common",
-		"libstagefright_flacdec",
-		"libstagefright_foundation",
-		"libstagefright_foundation_headers",
-		"libstagefright_headers",
-		"libstagefright_m4vh263dec",
-		"libstagefright_m4vh263enc",
-		"libstagefright_mp3dec",
-		"libstagefright_mp3dec_headers",
 		"libsurfaceflinger_headers",
 		"libsync",
 		"libtextclassifier_hash_headers",
@@ -541,6 +562,14 @@
 		"liblp",
 		"libstorage_literals_headers",
 
+		"PluginCoreLib",
+		"dagger2",
+		"dagger2-android-annotation-stubs",
+		"dagger2-bootstrap-compiler",
+		"dagger2-producers",
+		"okio-lib",
+		"setupdesign-strings",
+
 		//external/avb
 		"avbtool",
 		"libavb",
@@ -553,12 +582,14 @@
 		//external/fec
 		"libfec_rs",
 
-		//system/core/libsparse
-		"libsparse",
+		//frameworks/base/core/java
+		"IDropBoxManagerService_aidl",
 
 		//system/extras/ext4_utils
 		"libext4_utils",
 		"mke2fs_conf",
+		"mkuserimg_mke2fs",
+		"blk_alloc_to_base_fs",
 
 		//system/extras/libfec
 		"libfec",
@@ -566,10 +597,6 @@
 		//system/extras/squashfs_utils
 		"libsquashfs_utils",
 
-		//system/extras/verity/fec
-		"fec",
-		"boot_signer",
-
 		//packages/apps/Car/libs/car-ui-lib/car-ui-androidx
 		// genrule dependencies for java_imports
 		"car-ui-androidx-annotation-nodeps",
@@ -714,11 +741,12 @@
 		"api_fingerprint",
 
 		// allowlisting for kotlinx_coroutines
+		"annotations",
+		"kotlinx-coroutines-android-annotation-stubs",
+		"kotlinx-coroutines-core",
 		"kotlinx_coroutines",
 		"kotlinx_coroutines-device",
 		"kotlinx_coroutines-host",
-		"annotations",
-		"kotlinx-coroutines-android-annotation-stubs",
 
 		// for building com.android.neuralnetworks
 		"libimapper_stablec",
@@ -726,6 +754,46 @@
 
 		// min_sdk_version in android_app
 		"CtsShimUpgrade",
+
+		"art_cmdlineparser_headers",
+
+		// Mainline Module Apps
+		"CaptivePortalLogin",
+
+		"libstagefright_headers",
+
+		// aidl
+		"aidl",
+		"libaidl-common",
+
+		// java_resources containing only a single filegroup
+		"libauto_value_plugin",
+		"auto_value_plugin_resources",
+		"auto_value_extension",
+
+		// Used by xsd_config
+		"xsdc",
+
+		// cc_test that can be run by b test
+		"binderRpcWireProtocolTest",
+		"binderUnitTest",
+		"cpu_features-bit_utils_test",
+		"liblp_test",
+		"android.hardware.audio.common.test.utility_tests",
+		"HalAudioStreamWorkerTest",
+		"libjavacore-unit-tests",
+		"NeuralNetworksTest_utils",
+		"NeuralNetworksTest_logtag",
+		"NeuralNetworksTest_operations",
+		"nanoapp_chqts_shared_tests",
+		"fakeservicemanager_test",
+		"tristate_test",
+		"binderUtilsHostTest",
+		"run_dex2oat_test",
+		"bluetooth-address-unit-tests",
+
+		// for platform_compat_config
+		"process-compat-config",
 	}
 
 	Bp2buildModuleTypeAlwaysConvertList = []string{
@@ -738,6 +806,7 @@
 		"java_import_host",
 		"java_sdk_library",
 		"sysprop_library",
+		"xsd_config",
 	}
 
 	// Add the names of modules that bp2build should never convert, if it is
@@ -757,7 +826,6 @@
 		"buffer_hub_queue_producer-test",
 
 		// cc bugs
-		"libactivitymanager_aidl", // TODO(b/207426160): Unsupported use of aidl sources (via Dactivity_manager_procstate_aidl) in a cc_library
 
 		// TODO(b/198619163) module has same name as source
 		"logtagd.rc",
@@ -770,21 +838,16 @@
 		"libcutils_test_static",
 		"KernelLibcutilsTest",
 
-		"linker",                 // TODO(b/228316882): cc_binary uses link_crt
-		"versioner",              // TODO(b/228313961):  depends on prebuilt shared library libclang-cpp_host as a shared library, which does not supply expected providers for a shared library
-		"art_libartbase_headers", // TODO(b/236268577): Header libraries do not support export_shared_libs_headers
-		"apexer_test",            // Requires aapt2
-		"apexer_test_host_tools",
-		"host_apex_verifier",
-		"tjbench", // TODO(b/240563612): Stem property
+		"linker",    // TODO(b/228316882): cc_binary uses link_crt
+		"versioner", // TODO(b/228313961):  depends on prebuilt shared library libclang-cpp_host as a shared library, which does not supply expected providers for a shared library
+
+		// requires host tools for apexer
+		"apexer_test", "apexer_test_host_tools", "host_apex_verifier",
 
 		// java bugs
 		"libbase_ndk",  // TODO(b/186826477): fails to link libctscamera2_jni for device (required for CtsCameraTestCases)
 		"bouncycastle", // TODO(b/274474005): Need support for custom system_modules.
 
-		// python protos
-		"libprotobuf-python", // Has a handcrafted alternative
-
 		// genrule incompatibilities
 		"brotli-fuzzer-corpus",                                       // TODO(b/202015218): outputs are in location incompatible with bazel genrule handling.
 		"platform_tools_properties", "build_tools_source_properties", // TODO(b/203369847): multiple genrules in the same package creating the same file
@@ -802,7 +865,6 @@
 		"libprotobuf-internal-python-srcs", // TODO(b/210751803), we don't handle path property for filegroups
 		"libprotobuf-java-full",            // TODO(b/210751803), we don't handle path property for filegroups
 		"libprotobuf-java-util-full",       // TODO(b/210751803), we don't handle path property for filegroups
-		"auto_value_plugin_resources",      // TODO(b/210751803), we don't handle path property for filegroups
 
 		// go deps:
 		"analyze_bcpf",              // depends on bpmodify a blueprint_go_binary.
@@ -810,9 +872,6 @@
 		"host_bionic_linker_asm",    // depends on extract_linker, a go binary.
 		"host_bionic_linker_script", // depends on extract_linker, a go binary.
 
-		// in cmd attribute of genrule rule //system/timezone/output_data:robolectric_tzdata: label '//system/timezone/output_data:iana/tzdata' in $(location) expression is not a declared prerequisite of this rule
-		"robolectric_tzdata",
-
 		// rust support
 		"libtombstoned_client_rust_bridge_code", "libtombstoned_client_wrapper", // rust conversions are not supported
 
@@ -860,8 +919,6 @@
 		"static_crasher",                // depends on unconverted modules: libdebuggerd_handler
 		"test_fips",                     // depends on unconverted modules: adb
 		"timezone-host",                 // depends on unconverted modules: art.module.api.annotations
-		"truth-host-prebuilt",           // depends on unconverted modules: truth-prebuilt
-		"truth-prebuilt",                // depends on unconverted modules: asm-7.0, guava
 
 		// '//bionic/libc:libc_bp2build_cc_library_static' is duplicated in the 'deps' attribute of rule
 		"toybox-static",
@@ -908,12 +965,10 @@
 		"svcenc", "svcdec",
 
 		// Failing host cc_tests
-		"memunreachable_unit_test",
 		"libprocinfo_test",
 		"ziparchive-tests",
 		"gtest_isolated_tests",
 		"libunwindstack_unit_test",
-		"task_profiles_test",
 		"power_tests", // failing test on server, but not on host
 
 		// reflect: call of reflect.Value.NumField on interface Value
@@ -932,15 +987,18 @@
 
 		"libandroidfw_tests", "aapt2_tests", // failing due to data path issues
 
+		// error: overriding commands for target
+		// `out/host/linux-x86/nativetest64/gmock_tests/gmock_tests__cc_runner_test',
+		// previously defined at out/soong/installs-aosp_arm.mk:64919`
+		"gmock_tests",
+
 		// cc_test with unconverted deps, or are device-only (and not verified to pass yet)
 		"AMRWBEncTest",
-		"AmrnbDecoderTest",     // depends on unconverted modules: libaudioutils, libsndfile
-		"AmrnbEncoderTest",     // depends on unconverted modules: libaudioutils, libsndfile
-		"AmrwbDecoderTest",     // depends on unconverted modules: libsndfile, libaudioutils
-		"AmrwbEncoderTest",     // depends on unconverted modules: libaudioutils, libsndfile
-		"Mp3DecoderTest",       // depends on unconverted modules: libsndfile, libaudioutils
-		"Mpeg4H263DecoderTest", // depends on unconverted modules: libstagefright_foundation
-		"Mpeg4H263EncoderTest",
+		"AmrnbDecoderTest", // depends on unconverted modules: libaudioutils, libsndfile
+		"AmrnbEncoderTest", // depends on unconverted modules: libaudioutils, libsndfile
+		"AmrwbDecoderTest", // depends on unconverted modules: libsndfile, libaudioutils
+		"AmrwbEncoderTest", // depends on unconverted modules: libaudioutils, libsndfile
+		"Mp3DecoderTest",   // depends on unconverted modules: libsndfile, libaudioutils
 		"avcdec",
 		"avcenc",
 		"bionic-benchmarks-tests",
@@ -990,7 +1048,6 @@
 		"libBionicLoaderTests",  // depends on unconverted modules: libmeminfo
 		"libapexutil_tests",     // depends on unconverted modules: apex-info-list-tinyxml, libapexutil
 		"libcutils_sockets_test",
-		"libexpectedutils_test",
 		"libhwbinder_latency",
 		"liblog-host-test", // failing tests
 		"libminijail_test",
@@ -1027,7 +1084,6 @@
 		"scudo_unit_tests",
 		"stats-log-api-gen-test", // depends on unconverted modules: libstats_proto_host
 		"syscall_filter_unittest_gtest",
-		"sysprop_test", // depends on unconverted modules: libcom.android.sysprop.tests
 		"thread_exit_cb_helper",
 		"tls_properties_helper",
 		"ulp",
@@ -1058,7 +1114,7 @@
 		"libcfi-test",
 		"libcfi-test-bad",
 		"libcrash_test",
-		// "libcrypto_fuzz_unsafe",
+		"libcrypto_fuzz_unsafe",
 		"libdl_preempt_test_1",
 		"libdl_preempt_test_2",
 		"libdl_test_df_1_global",
@@ -1269,7 +1325,7 @@
 		"librelocations-fat",
 		"libsegment_gap_inner",
 		"libsegment_gap_outer",
-		// "libssl_fuzz_unsafe",
+		"libssl_fuzz_unsafe",
 		"libstatssocket_private",
 		"libsysv-hash-table-library",
 		"libtest_atexit",
@@ -1371,16 +1427,12 @@
 		"merge_ota",
 
 		// releasetools
-		"releasetools_fsverity_metadata_generator",
 		"verity_utils",
 		"check_ota_package_signature",
 		"check_target_files_vintf",
 		"releasetools_check_target_files_vintf",
-		"releasetools_verity_utils",
-		"build_image",
 		"ota_from_target_files",
 		"releasetools_ota_from_target_files",
-		"releasetools_build_image",
 		"add_img_to_target_files",
 		"releasetools_add_img_to_target_files",
 		"fsverity_metadata_generator",
@@ -1420,6 +1472,20 @@
 		"styleprotoslite",
 		"CtsPkgInstallerConstants",
 		"guava-android-testlib",
+
+		"MetaDataBaseUnitTest", // depends on libstagefright
+		"AVCUtilsUnitTest",     // depends on libstagefright
+		"ColorUtilsTest",       // depends on libmediandk
+
+		// python_test_host with test data
+		"sbom_writers_test",
+
+		// TODO(B/283193845): tradefed and its java_test_host dependents
+		"tradefed",
+		"permissive_mte_test",
+		"ICU4CTestRunner",
+
+		"HelloWorldHostTest", // TODO(b/280452825): Convert HelloWorldHostTest to b test
 	}
 
 	MixedBuildsDisabledList = []string{
@@ -1439,9 +1505,6 @@
 		"libadb_pairing_connection_static",
 		"libadb_pairing_server", "libadb_pairing_server_static",
 
-		// TODO(b/240563612) Needing `stem` selection support for cc_binary
-		"crasher",
-
 		// java_import[_host] issues
 		// tradefed prebuilts depend on libprotobuf
 		"prebuilt_tradefed",
@@ -1509,6 +1572,11 @@
 		"adb_tls_connection_test",
 		// M9: mixed builds for mainline trains launch
 		"api_fingerprint",
+		// M11: neuralnetworks launch
+		"com.android.neuralnetworks",
+		"test_com.android.neuralnetworks",
+		"libneuralnetworks",
+		"libneuralnetworks_static",
 	}
 
 	// Staging-mode allowlist. Modules in this list are only built
@@ -1517,9 +1585,11 @@
 	// It is implicit that all modules in ProdMixedBuildsEnabledList will
 	// also be built - do not add them to this list.
 	StagingMixedBuildsEnabledList = []string{
-		"com.android.neuralnetworks",
-		"libneuralnetworks",
-		"libneuralnetworks_static",
+		// M13: media.swcodec launch
+		"com.android.media.swcodec",
+		"test_com.android.media.swcodec",
+		"libstagefright_foundation",
+		"libcodec2_hidl@1.0",
 	}
 
 	// These should be the libs that are included by the apexes in the ProdMixedBuildsEnabledList
@@ -1531,274 +1601,20 @@
 	}
 
 	// These should be the libs that are included by the apexes in the StagingMixedBuildsEnabledList
-	StagingDclaMixedBuildsEnabledList = []string{}
+	StagingDclaMixedBuildsEnabledList = []string{
+		"libstagefright_flacdec",
+		"libutils",
+	}
 
 	// TODO(b/269342245): Enable the rest of the DCLA libs
 	// "libssl",
-	// "libstagefright_flacdec",
-	// "libutils",
 
-	// TODO(b/273282046): Make this list customizable to support various targets.
-	// The list of modules which are expected to spend lots of build time.
-	// With `--ninja_weight_source=soong`, ninja builds these modules and deps first.
-	HugeModulesMap = map[string]int{
-		"AccountManagementApp":                          DEFAULT_NINJA_WEIGHT,
-		"ActivityManagerPerfTestsStubApp1":              DEFAULT_NINJA_WEIGHT,
-		"ActivityManagerPerfTestsStubApp2":              DEFAULT_NINJA_WEIGHT,
-		"ActivityManagerPerfTestsStubApp3":              DEFAULT_NINJA_WEIGHT,
-		"api-stubs-docs-non-updatable":                  DEFAULT_NINJA_WEIGHT,
-		"AppCompatibilityTest":                          DEFAULT_NINJA_WEIGHT,
-		"AppTransitionTests":                            DEFAULT_NINJA_WEIGHT,
-		"art_compiler_tests":                            DEFAULT_NINJA_WEIGHT,
-		"art.module.intra.core.api.stubs.source":        DEFAULT_NINJA_WEIGHT,
-		"art.module.public.api.stubs.source":            DEFAULT_NINJA_WEIGHT,
-		"AttestationVerificationTest":                   DEFAULT_NINJA_WEIGHT,
-		"BatteryUsageStatsProtoTests":                   DEFAULT_NINJA_WEIGHT,
-		"bluetooth_test_gd_unit":                        DEFAULT_NINJA_WEIGHT,
-		"Bluetooth":                                     DEFAULT_NINJA_WEIGHT,
-		"BluetoothInstrumentationTests":                 DEFAULT_NINJA_WEIGHT,
-		"Calendar":                                      DEFAULT_NINJA_WEIGHT,
-		"CalendarProvider":                              DEFAULT_NINJA_WEIGHT,
-		"Camera2":                                       DEFAULT_NINJA_WEIGHT,
-		"CarRotaryControllerUnitTests":                  DEFAULT_NINJA_WEIGHT,
-		"CarSettingsForUnitTesting":                     DEFAULT_NINJA_WEIGHT,
-		"CarSettingsUnitTests":                          DEFAULT_NINJA_WEIGHT,
-		"CarSystemUI-tests":                             DEFAULT_NINJA_WEIGHT,
-		"CellBroadcastApp":                              DEFAULT_NINJA_WEIGHT,
-		"CellBroadcastLegacyApp":                        DEFAULT_NINJA_WEIGHT,
-		"CellBroadcastReceiverOemUnitTests":             DEFAULT_NINJA_WEIGHT,
-		"CellBroadcastServiceModule":                    DEFAULT_NINJA_WEIGHT,
-		"CompanionDeviceManager":                        DEFAULT_NINJA_WEIGHT,
-		"ConnectivityChecker":                           DEFAULT_NINJA_WEIGHT,
-		"Contacts":                                      DEFAULT_NINJA_WEIGHT,
-		"ContactsProvider":                              DEFAULT_NINJA_WEIGHT,
-		"ContentCapturePerfTests":                       DEFAULT_NINJA_WEIGHT,
-		"CorePerfTests":                                 DEFAULT_NINJA_WEIGHT,
-		"crosvm":                                        DEFAULT_NINJA_WEIGHT,
-		"CtsDomainVerificationDeviceMultiUserTestCases": DEFAULT_NINJA_WEIGHT,
-		"CtsLogdTestCases":                              DEFAULT_NINJA_WEIGHT,
-		"CtsMediaProviderTranscodeTests":                DEFAULT_NINJA_WEIGHT,
-		"CtsRollbackManagerHostTestHelperApp":           DEFAULT_NINJA_WEIGHT,
-		"CtsRollbackManagerHostTestHelperApp2":          DEFAULT_NINJA_WEIGHT,
-		"CtsRootPackageInstallerTestCases":              DEFAULT_NINJA_WEIGHT,
-		"CtsRootRollbackManagerHostTestHelperApp":       DEFAULT_NINJA_WEIGHT,
-		"CtsTranscodeTestAppSupportsHevc":               DEFAULT_NINJA_WEIGHT,
-		"CtsTranscodeTestAppSupportsSlowMotion":         DEFAULT_NINJA_WEIGHT,
-		"CuttlefishDisplayHotplugHelperApp":             DEFAULT_NINJA_WEIGHT,
-		"cvd-host_package":                              DEFAULT_NINJA_WEIGHT,
-		"DelegateTestApp":                               DEFAULT_NINJA_WEIGHT,
-		"DeskClock":                                     DEFAULT_NINJA_WEIGHT,
-		"Development":                                   DEFAULT_NINJA_WEIGHT,
-		"DeviceAdminTestApp":                            DEFAULT_NINJA_WEIGHT,
-		"DevicePolicyManagementRoleHolderTestApp":       DEFAULT_NINJA_WEIGHT,
-		"dex2oatd":                                      DEFAULT_NINJA_WEIGHT,
-		"DocumentsUI":                                   DEFAULT_NINJA_WEIGHT,
-		"EasterEgg":                                     DEFAULT_NINJA_WEIGHT,
-		"EffectProxyTest":                               DEFAULT_NINJA_WEIGHT,
-		"EmergencyInfo":                                 DEFAULT_NINJA_WEIGHT,
-		"EmptyTestApp":                                  DEFAULT_NINJA_WEIGHT,
-		"ExtServices":                                   DEFAULT_NINJA_WEIGHT,
-		"FacebookAppsScenarioTests":                     DEFAULT_NINJA_WEIGHT,
-		"flickerlib-core":                               DEFAULT_NINJA_WEIGHT,
-		"flickerlib":                                    DEFAULT_NINJA_WEIGHT,
-		"FlickerLibTest":                                DEFAULT_NINJA_WEIGHT,
-		"FlickerTests":                                  DEFAULT_NINJA_WEIGHT,
-		"framework-minus-apex":                          DEFAULT_NINJA_WEIGHT,
-		"framework-res":                                 DEFAULT_NINJA_WEIGHT,
-		"FrameworksCoreTests":                           DEFAULT_NINJA_WEIGHT,
-		"FrameworksMockingCoreTests":                    DEFAULT_NINJA_WEIGHT,
-		"FrameworksMockingServicesTests":                DEFAULT_NINJA_WEIGHT,
-		"FrameworksNetSmokeTests":                       DEFAULT_NINJA_WEIGHT,
-		"FrameworksNetTests":                            DEFAULT_NINJA_WEIGHT,
-		"FrameworksServicesTests":                       DEFAULT_NINJA_WEIGHT,
-		"FrameworksTelephonyTests":                      DEFAULT_NINJA_WEIGHT,
-		"FrameworksUiServicesTests":                     DEFAULT_NINJA_WEIGHT,
-		"FrameworksVcnTests":                            DEFAULT_NINJA_WEIGHT,
-		"Gallery2":                                      DEFAULT_NINJA_WEIGHT,
-		"GameCoreDevice":                                DEFAULT_NINJA_WEIGHT,
-		"GoogleBluetoothInstrumentationTests":           DEFAULT_NINJA_WEIGHT,
-		"guice_munged_srcs":                             DEFAULT_NINJA_WEIGHT,
-		"HalfSheetUX":                                   DEFAULT_NINJA_WEIGHT,
-		"ImePerfTests":                                  DEFAULT_NINJA_WEIGHT,
-		"imgdiag":                                       DEFAULT_NINJA_WEIGHT,
-		"ImsServiceEntitlement":                         DEFAULT_NINJA_WEIGHT,
-		"ImsServiceEntitlementUnitTests":                DEFAULT_NINJA_WEIGHT,
-		"InputTests":                                    DEFAULT_NINJA_WEIGHT,
-		"InstallTest":                                   DEFAULT_NINJA_WEIGHT,
-		"IntentResolver":                                DEFAULT_NINJA_WEIGHT,
-		"JankBench":                                     DEFAULT_NINJA_WEIGHT,
-		"jsilver":                                       DEFAULT_NINJA_WEIGHT,
-		"KeyChain":                                      DEFAULT_NINJA_WEIGHT,
-		"KeyChainTests":                                 DEFAULT_NINJA_WEIGHT,
-		"keystore2":                                     DEFAULT_NINJA_WEIGHT,
-		"LargeResourcesCompressed":                      DEFAULT_NINJA_WEIGHT,
-		"LatinIME":                                      DEFAULT_NINJA_WEIGHT,
-		"Launcher3QuickStepLib":                         DEFAULT_NINJA_WEIGHT,
-		"libaom":                                        DEFAULT_NINJA_WEIGHT,
-		"libart-broken":                                 DEFAULT_NINJA_WEIGHT,
-		"libart-compiler":                               DEFAULT_NINJA_WEIGHT,
-		"libart-disassembler":                           DEFAULT_NINJA_WEIGHT,
-		"libart":                                        DEFAULT_NINJA_WEIGHT,
-		"libartd":                                       DEFAULT_NINJA_WEIGHT,
-		"libaudiohal@7.1":                               DEFAULT_NINJA_WEIGHT,
-		"libbluetooth_core_rs":                          DEFAULT_NINJA_WEIGHT,
-		"libbluetooth_gd_unit_tests":                    DEFAULT_NINJA_WEIGHT,
-		"libbluetooth_gd":                               DEFAULT_NINJA_WEIGHT,
-		"libbluetooth_rs":                               DEFAULT_NINJA_WEIGHT,
-		"libbluetooth-for-tests":                        DEFAULT_NINJA_WEIGHT,
-		"libbt_common":                                  DEFAULT_NINJA_WEIGHT,
-		"libbt_packets_nonapex":                         DEFAULT_NINJA_WEIGHT,
-		"libbt_packets":                                 DEFAULT_NINJA_WEIGHT,
-		"libbt_shim_ffi":                                DEFAULT_NINJA_WEIGHT,
-		"libbt_shim":                                    DEFAULT_NINJA_WEIGHT,
-		"libbt-audio-hal-interface":                     DEFAULT_NINJA_WEIGHT,
-		"libbt-bta-core":                                DEFAULT_NINJA_WEIGHT,
-		"libbt-bta":                                     DEFAULT_NINJA_WEIGHT,
-		"libbt-common":                                  DEFAULT_NINJA_WEIGHT,
-		"libbt-hci":                                     DEFAULT_NINJA_WEIGHT,
-		"libbt-platform-protos-lite":                    DEFAULT_NINJA_WEIGHT,
-		"libbt-protos-lite":                             DEFAULT_NINJA_WEIGHT,
-		"libbt-sbc-decoder":                             DEFAULT_NINJA_WEIGHT,
-		"libc":                                          DEFAULT_NINJA_WEIGHT,
-		"libclap":                                       DEFAULT_NINJA_WEIGHT,
-		"libcodec2_soft_av1dec_gav1":                    DEFAULT_NINJA_WEIGHT,
-		"libcompositionengine_test":                     DEFAULT_NINJA_WEIGHT,
-		"libdevices":                                    DEFAULT_NINJA_WEIGHT,
-		"libfrontend_proto":                             DEFAULT_NINJA_WEIGHT,
-		"libhwtrust":                                    DEFAULT_NINJA_WEIGHT,
-		"libjni":                                        DEFAULT_NINJA_WEIGHT,
-		"libkeystore2":                                  DEFAULT_NINJA_WEIGHT,
-		"libkmr_ta":                                     DEFAULT_NINJA_WEIGHT,
-		"liblmp":                                        DEFAULT_NINJA_WEIGHT,
-		"libopenjdkjvmtid":                              DEFAULT_NINJA_WEIGHT,
-		"libprotobuf_deprecated":                        DEFAULT_NINJA_WEIGHT,
-		"libprotobuf":                                   DEFAULT_NINJA_WEIGHT,
-		"libregex":                                      DEFAULT_NINJA_WEIGHT,
-		"LibStatsPullTests":                             DEFAULT_NINJA_WEIGHT,
-		"libstd":                                        DEFAULT_NINJA_WEIGHT,
-		"libsurfaceflinger_unittest":                    DEFAULT_NINJA_WEIGHT,
-		"libsyn":                                        DEFAULT_NINJA_WEIGHT,
-		"libtokio":                                      DEFAULT_NINJA_WEIGHT,
-		"libuwb_core":                                   DEFAULT_NINJA_WEIGHT,
-		"libuwb_uci_jni_rust":                           DEFAULT_NINJA_WEIGHT,
-		"libuwb_uci_packets":                            DEFAULT_NINJA_WEIGHT,
-		"libvpx":                                        DEFAULT_NINJA_WEIGHT,
-		"libvulkan_enc":                                 DEFAULT_NINJA_WEIGHT,
-		"libwebrtc":                                     DEFAULT_NINJA_WEIGHT,
-		"LiveWallpapersPicker":                          DEFAULT_NINJA_WEIGHT,
-		"LockTaskApp":                                   DEFAULT_NINJA_WEIGHT,
-		"LongevityPlatformLibTests":                     DEFAULT_NINJA_WEIGHT,
-		"ManagedProvisioning":                           DEFAULT_NINJA_WEIGHT,
-		"ManagedProvisioningTests":                      DEFAULT_NINJA_WEIGHT,
-		"MediaProvider":                                 DEFAULT_NINJA_WEIGHT,
-		"MediaProviderClientTests":                      DEFAULT_NINJA_WEIGHT,
-		"MediaProviderLegacy":                           DEFAULT_NINJA_WEIGHT,
-		"messaging":                                     DEFAULT_NINJA_WEIGHT,
-		"metalava":                                      DEFAULT_NINJA_WEIGHT,
-		"MicrobenchmarkRunnerTests":                     DEFAULT_NINJA_WEIGHT,
-		"microdroid_manager":                            DEFAULT_NINJA_WEIGHT,
-		"minikin_tests":                                 DEFAULT_NINJA_WEIGHT,
-		"MLCTestApp":                                    DEFAULT_NINJA_WEIGHT,
-		"MmsService":                                    DEFAULT_NINJA_WEIGHT,
-		"MmsServiceTests":                               DEFAULT_NINJA_WEIGHT,
-		"module-lib-api-stubs-docs-non-updatable":       DEFAULT_NINJA_WEIGHT,
-		"motion_tool_lib_tests":                         DEFAULT_NINJA_WEIGHT,
-		"MtpService":                                    DEFAULT_NINJA_WEIGHT,
-		"MultiUserTests":                                DEFAULT_NINJA_WEIGHT,
-		"NearbyIntegrationUiTests":                      DEFAULT_NINJA_WEIGHT,
-		"net_test_bluetooth":                            DEFAULT_NINJA_WEIGHT,
-		"net_test_btif":                                 DEFAULT_NINJA_WEIGHT,
-		"net_test_main_shim":                            DEFAULT_NINJA_WEIGHT,
-		"net_test_stack":                                DEFAULT_NINJA_WEIGHT,
-		"net-tests-utils":                               DEFAULT_NINJA_WEIGHT,
-		"NetworkStackCoverageTests":                     DEFAULT_NINJA_WEIGHT,
-		"NetworkStackIntegrationTests":                  DEFAULT_NINJA_WEIGHT,
-		"NetworkStackNext":                              DEFAULT_NINJA_WEIGHT,
-		"NfcNci":                                        DEFAULT_NINJA_WEIGHT,
-		"NfcNciUnitTests":                               DEFAULT_NINJA_WEIGHT,
-		"NotEmptyTestApp":                               DEFAULT_NINJA_WEIGHT,
-		"NotificationFunctionalTests":                   DEFAULT_NINJA_WEIGHT,
-		"oatdumpd":                                      DEFAULT_NINJA_WEIGHT,
-		"OsuLogin":                                      DEFAULT_NINJA_WEIGHT,
-		"PackageInstaller":                              DEFAULT_NINJA_WEIGHT,
-		"PackageManagerComponentOverrideTests":          DEFAULT_NINJA_WEIGHT,
-		"PackageManagerPerfTests":                       DEFAULT_NINJA_WEIGHT,
-		"PackageManagerServiceServerTests":              DEFAULT_NINJA_WEIGHT,
-		"PackageManagerServiceUnitTests":                DEFAULT_NINJA_WEIGHT,
-		"PackageWatchdogTest":                           DEFAULT_NINJA_WEIGHT,
-		"PandoraServerLib":                              DEFAULT_NINJA_WEIGHT,
-		"pdl":                                           DEFAULT_NINJA_WEIGHT,
-		"perfetto_trace_java_protos":                    DEFAULT_NINJA_WEIGHT,
-		"perfetto_trace-full":                           DEFAULT_NINJA_WEIGHT,
-		"PermissionController":                          DEFAULT_NINJA_WEIGHT,
-		"PermissionControllerMockingTests":              DEFAULT_NINJA_WEIGHT,
-		"PixelAppCompTests":                             DEFAULT_NINJA_WEIGHT,
-		"platform-bootclasspath":                        DEFAULT_NINJA_WEIGHT,
-		"PlatformCommonScenarioTests":                   DEFAULT_NINJA_WEIGHT,
-		"PlatformComposeCoreTests":                      DEFAULT_NINJA_WEIGHT,
-		"platformprotoslite":                            DEFAULT_NINJA_WEIGHT,
-		"PlatformRuleTests":                             DEFAULT_NINJA_WEIGHT,
-		"precompiled_sepolicy-without_apex":             DEFAULT_NINJA_WEIGHT,
-		"PresencePolling":                               DEFAULT_NINJA_WEIGHT,
-		"PrintSpooler":                                  DEFAULT_NINJA_WEIGHT,
-		"QuickSearchBox":                                DEFAULT_NINJA_WEIGHT,
-		"RemoteDPCTestApp":                              DEFAULT_NINJA_WEIGHT,
-		"RemoteProvisioningServiceTests":                DEFAULT_NINJA_WEIGHT,
-		"RkpdAppUnitTests":                              DEFAULT_NINJA_WEIGHT,
-		"Robolectric_shadows_framework":                 DEFAULT_NINJA_WEIGHT,
-		"RoleHolderApp":                                 DEFAULT_NINJA_WEIGHT,
-		"SdkSandbox":                                    DEFAULT_NINJA_WEIGHT,
-		"service-appsearch":                             DEFAULT_NINJA_WEIGHT,
-		"service-connectivity":                          DEFAULT_NINJA_WEIGHT,
-		"service-uwb":                                   DEFAULT_NINJA_WEIGHT,
-		"service-wifi":                                  DEFAULT_NINJA_WEIGHT,
-		"services-non-updatable-stubs":                  DEFAULT_NINJA_WEIGHT,
-		"services":                                      DEFAULT_NINJA_WEIGHT,
-		"Settings-core":                                 DEFAULT_NINJA_WEIGHT,
-		"Settings":                                      DEFAULT_NINJA_WEIGHT,
-		"SettingsIntelligence":                          DEFAULT_NINJA_WEIGHT,
-		"SettingsLibTests":                              DEFAULT_NINJA_WEIGHT,
-		"SettingsProvider":                              DEFAULT_NINJA_WEIGHT,
-		"Shell":                                         DEFAULT_NINJA_WEIGHT,
-		"SimAppDialog":                                  DEFAULT_NINJA_WEIGHT,
-		"sl4a":                                          DEFAULT_NINJA_WEIGHT,
-		"SmsApp":                                        DEFAULT_NINJA_WEIGHT,
-		"SoundPicker":                                   DEFAULT_NINJA_WEIGHT,
-		"StagedInstallTest":                             DEFAULT_NINJA_WEIGHT,
-		"StatementService":                              DEFAULT_NINJA_WEIGHT,
-		"StatsdFrameworkTestApp":                        DEFAULT_NINJA_WEIGHT,
-		"StatsdFrameworkTestAppNoPermission":            DEFAULT_NINJA_WEIGHT,
-		"statsdprotolite":                               DEFAULT_NINJA_WEIGHT,
-		"Stk":                                           DEFAULT_NINJA_WEIGHT,
-		"StorageManager":                                DEFAULT_NINJA_WEIGHT,
-		"system-api-stubs-docs-non-updatable":           DEFAULT_NINJA_WEIGHT,
-		"SystemUI-core":                                 DEFAULT_NINJA_WEIGHT,
-		"SystemUI-tests-base":                           DEFAULT_NINJA_WEIGHT,
-		"SystemUI-tests":                                DEFAULT_NINJA_WEIGHT,
-		"SystemUI":                                      DEFAULT_NINJA_WEIGHT,
-		"SystemUIComposeFeatures":                       DEFAULT_NINJA_WEIGHT,
-		"SystemUIComposeFeaturesTests":                  DEFAULT_NINJA_WEIGHT,
-		"SystemUITests":                                 DEFAULT_NINJA_WEIGHT,
-		"Tag":                                           DEFAULT_NINJA_WEIGHT,
-		"Telecom":                                       DEFAULT_NINJA_WEIGHT,
-		"TelecomUnitTests":                              DEFAULT_NINJA_WEIGHT,
-		"telephony-common":                              DEFAULT_NINJA_WEIGHT,
-		"TelephonyProvider":                             DEFAULT_NINJA_WEIGHT,
-		"TeleService":                                   DEFAULT_NINJA_WEIGHT,
-		"test-api-stubs-docs-non-updatable":             DEFAULT_NINJA_WEIGHT,
-		"TetheringIntegrationTests":                     DEFAULT_NINJA_WEIGHT,
-		"TetheringNext":                                 DEFAULT_NINJA_WEIGHT,
-		"ThemePickerTests":                              DEFAULT_NINJA_WEIGHT,
-		"Traceur":                                       DEFAULT_NINJA_WEIGHT,
-		"UsbManagerTests":                               DEFAULT_NINJA_WEIGHT,
-		"UsbTests":                                      DEFAULT_NINJA_WEIGHT,
-		"virtmgr":                                       DEFAULT_NINJA_WEIGHT,
-		"WallpaperPicker2TestLib":                       DEFAULT_NINJA_WEIGHT,
-		"WallpaperPicker2Tests":                         DEFAULT_NINJA_WEIGHT,
-		"WifiDialog":                                    DEFAULT_NINJA_WEIGHT,
-		"wm-proto-parsers":                              DEFAULT_NINJA_WEIGHT,
-		"WMShellFlickerTests":                           DEFAULT_NINJA_WEIGHT,
-		"WmTests":                                       DEFAULT_NINJA_WEIGHT,
-		"wpa_supplicant":                                DEFAULT_NINJA_WEIGHT,
+	// The list of module types which are expected to spend lots of build time.
+	// With `--ninja_weight_source=soong`, ninja builds these module types and deps first.
+	HugeModuleTypePrefixMap = map[string]int{
+		"rust_":       HIGH_PRIORITIZED_WEIGHT,
+		"droidstubs":  DEFAULT_PRIORITIZED_WEIGHT,
+		"art_":        DEFAULT_PRIORITIZED_WEIGHT,
+		"ndk_library": DEFAULT_PRIORITIZED_WEIGHT,
 	}
 )
diff --git a/android/androidmk.go b/android/androidmk.go
index aa411d1..62f82f2 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -42,7 +42,7 @@
 }
 
 func RegisterAndroidMkBuildComponents(ctx RegistrationContext) {
-	ctx.RegisterSingletonType("androidmk", AndroidMkSingleton)
+	ctx.RegisterParallelSingletonType("androidmk", AndroidMkSingleton)
 }
 
 // Enable androidmk support.
diff --git a/android/apex.go b/android/apex.go
index 87bff74..6119b08 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -84,6 +84,9 @@
 	//
 	// See Prebuilt.ApexInfoMutator for more information.
 	ForPrebuiltApex bool
+
+	// Returns the name of the test apexes that this module is included in.
+	TestApexes []string
 }
 
 var ApexInfoProvider = blueprint.NewMutatorProvider(ApexInfo{}, "apex")
@@ -287,6 +290,9 @@
 
 	// See ApexModule.UniqueApexVariants()
 	UniqueApexVariationsForDeps bool `blueprint:"mutated"`
+
+	// The test apexes that includes this apex variant
+	TestApexes []string `blueprint:"mutated"`
 }
 
 // Marker interface that identifies dependencies that are excluded from APEX contents.
@@ -356,9 +362,18 @@
 	return m
 }
 
+var (
+	availableToPlatformList = []string{AvailableToPlatform}
+)
+
 // Implements ApexModule
 func (m *ApexModuleBase) ApexAvailable() []string {
-	return m.ApexProperties.Apex_available
+	aa := m.ApexProperties.Apex_available
+	if len(aa) > 0 {
+		return aa
+	}
+	// Default is availability to platform
+	return CopyOf(availableToPlatformList)
 }
 
 // Implements ApexModule
@@ -420,6 +435,11 @@
 	return nil
 }
 
+// Returns the test apexes that this module is included in.
+func (m *ApexModuleBase) TestApexes() []string {
+	return m.ApexProperties.TestApexes
+}
+
 // Implements ApexModule
 func (m *ApexModuleBase) UniqueApexVariations() bool {
 	// If needed, this will bel overridden by concrete types inheriting
@@ -442,6 +462,14 @@
 	AvailableToGkiApex  = "com.android.gki.*"
 )
 
+var (
+	AvailableToRecognziedWildcards = []string{
+		AvailableToPlatform,
+		AvailableToAnyApex,
+		AvailableToGkiApex,
+	}
+)
+
 // CheckAvailableForApex provides the default algorithm for checking the apex availability. When the
 // availability is empty, it defaults to ["//apex_available:platform"] which means "available to the
 // platform but not available to any APEX". When the list is not empty, `what` is matched against
@@ -540,12 +568,14 @@
 			// Platform APIs is allowed for this module only when all APEXes containing
 			// the module are with `use_platform_apis: true`.
 			merged[index].UsePlatformApis = merged[index].UsePlatformApis && apexInfo.UsePlatformApis
+			merged[index].TestApexes = append(merged[index].TestApexes, apexInfo.TestApexes...)
 		} else {
 			seen[mergedName] = len(merged)
 			apexInfo.ApexVariationName = mergedName
 			apexInfo.InApexVariants = CopyOf(apexInfo.InApexVariants)
 			apexInfo.InApexModules = CopyOf(apexInfo.InApexModules)
 			apexInfo.ApexContents = append([]*ApexContents(nil), apexInfo.ApexContents...)
+			apexInfo.TestApexes = CopyOf(apexInfo.TestApexes)
 			merged = append(merged, apexInfo)
 		}
 		aliases = append(aliases, [2]string{variantName, mergedName})
@@ -593,8 +623,10 @@
 	mctx.SetDefaultDependencyVariation(&defaultVariation)
 
 	variations := []string{defaultVariation}
+	testApexes := []string{}
 	for _, a := range apexInfos {
 		variations = append(variations, a.ApexVariationName)
+		testApexes = append(testApexes, a.TestApexes...)
 	}
 	modules := mctx.CreateVariations(variations...)
 	for i, mod := range modules {
@@ -608,6 +640,9 @@
 		if !platformVariation {
 			mctx.SetVariationProvider(mod, ApexInfoProvider, apexInfos[i-1])
 		}
+		// Set the value of TestApexes in every single apex variant.
+		// This allows each apex variant to be aware of the test apexes in the user provided apex_available.
+		mod.(ApexModule).apexModuleBase().ApexProperties.TestApexes = testApexes
 	}
 
 	for _, alias := range aliases {
@@ -898,3 +933,9 @@
 		return true
 	})
 }
+
+// Implemented by apexBundle.
+type ApexTestInterface interface {
+	// Return true if the apex bundle is an apex_test
+	IsTestApex() bool
+}
diff --git a/android/apex_test.go b/android/apex_test.go
index 0bf4c9c..ddc730d 100644
--- a/android/apex_test.go
+++ b/android/apex_test.go
@@ -33,10 +33,10 @@
 		{
 			name: "single",
 			in: []ApexInfo{
-				{"foo", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
+				{"foo", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex, nil},
 			},
 			wantMerged: []ApexInfo{
-				{"apex10000", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
+				{"apex10000", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex, nil},
 			},
 			wantAliases: [][2]string{
 				{"foo", "apex10000"},
@@ -45,11 +45,11 @@
 		{
 			name: "merge",
 			in: []ApexInfo{
-				{"foo", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
-				{"bar", FutureApiLevel, false, false, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
+				{"foo", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex, nil},
+				{"bar", FutureApiLevel, false, false, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex, nil},
 			},
 			wantMerged: []ApexInfo{
-				{"apex10000", FutureApiLevel, false, false, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, false}},
+				{"apex10000", FutureApiLevel, false, false, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, false, nil}},
 			wantAliases: [][2]string{
 				{"bar", "apex10000"},
 				{"foo", "apex10000"},
@@ -58,12 +58,12 @@
 		{
 			name: "don't merge version",
 			in: []ApexInfo{
-				{"foo", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
-				{"bar", uncheckedFinalApiLevel(30), false, false, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
+				{"foo", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex, nil},
+				{"bar", uncheckedFinalApiLevel(30), false, false, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex, nil},
 			},
 			wantMerged: []ApexInfo{
-				{"apex30", uncheckedFinalApiLevel(30), false, false, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
-				{"apex10000", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
+				{"apex30", uncheckedFinalApiLevel(30), false, false, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex, nil},
+				{"apex10000", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex, nil},
 			},
 			wantAliases: [][2]string{
 				{"bar", "apex30"},
@@ -73,11 +73,11 @@
 		{
 			name: "merge updatable",
 			in: []ApexInfo{
-				{"foo", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
-				{"bar", FutureApiLevel, true, false, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
+				{"foo", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex, nil},
+				{"bar", FutureApiLevel, true, false, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex, nil},
 			},
 			wantMerged: []ApexInfo{
-				{"apex10000", FutureApiLevel, true, false, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex},
+				{"apex10000", FutureApiLevel, true, false, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex, nil},
 			},
 			wantAliases: [][2]string{
 				{"bar", "apex10000"},
@@ -87,15 +87,15 @@
 		{
 			name: "don't merge when for prebuilt_apex",
 			in: []ApexInfo{
-				{"foo", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
-				{"bar", FutureApiLevel, true, false, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
+				{"foo", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex, nil},
+				{"bar", FutureApiLevel, true, false, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex, nil},
 				// This one should not be merged in with the others because it is for
 				// a prebuilt_apex.
-				{"baz", FutureApiLevel, true, false, []string{"baz"}, []string{"baz"}, nil, ForPrebuiltApex},
+				{"baz", FutureApiLevel, true, false, []string{"baz"}, []string{"baz"}, nil, ForPrebuiltApex, nil},
 			},
 			wantMerged: []ApexInfo{
-				{"apex10000", FutureApiLevel, true, false, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex},
-				{"baz", FutureApiLevel, true, false, []string{"baz"}, []string{"baz"}, nil, ForPrebuiltApex},
+				{"apex10000", FutureApiLevel, true, false, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex, nil},
+				{"baz", FutureApiLevel, true, false, []string{"baz"}, []string{"baz"}, nil, ForPrebuiltApex, nil},
 			},
 			wantAliases: [][2]string{
 				{"bar", "apex10000"},
@@ -105,11 +105,11 @@
 		{
 			name: "merge different UsePlatformApis but don't allow using platform api",
 			in: []ApexInfo{
-				{"foo", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
-				{"bar", FutureApiLevel, false, true, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
+				{"foo", FutureApiLevel, false, false, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex, nil},
+				{"bar", FutureApiLevel, false, true, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex, nil},
 			},
 			wantMerged: []ApexInfo{
-				{"apex10000", FutureApiLevel, false, false, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex},
+				{"apex10000", FutureApiLevel, false, false, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex, nil},
 			},
 			wantAliases: [][2]string{
 				{"bar", "apex10000"},
@@ -119,11 +119,11 @@
 		{
 			name: "merge same UsePlatformApis and allow using platform api",
 			in: []ApexInfo{
-				{"foo", FutureApiLevel, false, true, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex},
-				{"bar", FutureApiLevel, false, true, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex},
+				{"foo", FutureApiLevel, false, true, []string{"foo"}, []string{"foo"}, nil, NotForPrebuiltApex, nil},
+				{"bar", FutureApiLevel, false, true, []string{"bar"}, []string{"bar"}, nil, NotForPrebuiltApex, nil},
 			},
 			wantMerged: []ApexInfo{
-				{"apex10000", FutureApiLevel, false, true, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex},
+				{"apex10000", FutureApiLevel, false, true, []string{"bar", "foo"}, []string{"bar", "foo"}, nil, NotForPrebuiltApex, nil},
 			},
 			wantAliases: [][2]string{
 				{"bar", "apex10000"},
diff --git a/android/api_levels.go b/android/api_levels.go
index 7214ccb..2391e6c 100644
--- a/android/api_levels.go
+++ b/android/api_levels.go
@@ -15,16 +15,14 @@
 package android
 
 import (
+	"android/soong/starlark_import"
 	"encoding/json"
 	"fmt"
 	"strconv"
-
-	"android/soong/bazel"
-	"android/soong/starlark_fmt"
 )
 
 func init() {
-	RegisterSingletonType("api_levels", ApiLevelsSingleton)
+	RegisterParallelSingletonType("api_levels", ApiLevelsSingleton)
 }
 
 const previewAPILevelBase = 9000
@@ -288,13 +286,17 @@
 // ReplaceFinalizedCodenames returns the API level number associated with that API level
 // if the `raw` input is the codename of an API level has been finalized.
 // If the input is *not* a finalized codename, the input is returned unmodified.
-func ReplaceFinalizedCodenames(config Config, raw string) string {
-	num, ok := getFinalCodenamesMap(config)[raw]
+func ReplaceFinalizedCodenames(config Config, raw string) (string, error) {
+	finalCodenamesMap, err := getFinalCodenamesMap(config)
+	if err != nil {
+		return raw, err
+	}
+	num, ok := finalCodenamesMap[raw]
 	if !ok {
-		return raw
+		return raw, nil
 	}
 
-	return strconv.Itoa(num)
+	return strconv.Itoa(num), nil
 }
 
 // ApiLevelFrom converts the given string `raw` to an ApiLevel.
@@ -344,7 +346,11 @@
 		}
 	}
 
-	canonical, ok := getApiLevelsMapReleasedVersions()[raw]
+	apiLevelsReleasedVersions, err := getApiLevelsMapReleasedVersions()
+	if err != nil {
+		return NoneApiLevel, err
+	}
+	canonical, ok := apiLevelsReleasedVersions[raw]
 	if !ok {
 		asInt, err := strconv.Atoi(raw)
 		if err != nil {
@@ -410,37 +416,21 @@
 	return PathForOutput(ctx, "api_levels.json")
 }
 
-func getApiLevelsMapReleasedVersions() map[string]int {
-	return map[string]int{
-		"G":        9,
-		"I":        14,
-		"J":        16,
-		"J-MR1":    17,
-		"J-MR2":    18,
-		"K":        19,
-		"L":        21,
-		"L-MR1":    22,
-		"M":        23,
-		"N":        24,
-		"N-MR1":    25,
-		"O":        26,
-		"O-MR1":    27,
-		"P":        28,
-		"Q":        29,
-		"R":        30,
-		"S":        31,
-		"S-V2":     32,
-		"Tiramisu": 33,
-	}
+func getApiLevelsMapReleasedVersions() (map[string]int, error) {
+	return starlark_import.GetStarlarkValue[map[string]int]("api_levels_released_versions")
 }
 
 var finalCodenamesMapKey = NewOnceKey("FinalCodenamesMap")
 
-func getFinalCodenamesMap(config Config) map[string]int {
+func getFinalCodenamesMap(config Config) (map[string]int, error) {
+	type resultStruct struct {
+		result map[string]int
+		err    error
+	}
 	// This logic is replicated in starlark, if changing logic here update starlark code too
 	// https://cs.android.com/android/platform/superproject/+/master:build/bazel/rules/common/api.bzl;l=30;drc=231c7e8c8038fd478a79eb68aa5b9f5c64e0e061
-	return config.Once(finalCodenamesMapKey, func() interface{} {
-		apiLevelsMap := getApiLevelsMapReleasedVersions()
+	result := config.Once(finalCodenamesMapKey, func() interface{} {
+		apiLevelsMap, err := getApiLevelsMapReleasedVersions()
 
 		// TODO: Differentiate "current" and "future".
 		// The code base calls it FutureApiLevel, but the spelling is "current",
@@ -453,41 +443,44 @@
 		// added in S, both of these are usable when building for "current" when
 		// neither R nor S are final, but the S APIs stop being available in a
 		// final R build.
-		if Bool(config.productVariables.Platform_sdk_final) {
+		if err == nil && Bool(config.productVariables.Platform_sdk_final) {
 			apiLevelsMap["current"] = config.PlatformSdkVersion().FinalOrFutureInt()
 		}
 
-		return apiLevelsMap
-	}).(map[string]int)
+		return resultStruct{apiLevelsMap, err}
+	}).(resultStruct)
+	return result.result, result.err
 }
 
 var apiLevelsMapKey = NewOnceKey("ApiLevelsMap")
 
 // ApiLevelsMap has entries for preview API levels
-func GetApiLevelsMap(config Config) map[string]int {
+func GetApiLevelsMap(config Config) (map[string]int, error) {
+	type resultStruct struct {
+		result map[string]int
+		err    error
+	}
 	// This logic is replicated in starlark, if changing logic here update starlark code too
 	// https://cs.android.com/android/platform/superproject/+/master:build/bazel/rules/common/api.bzl;l=23;drc=231c7e8c8038fd478a79eb68aa5b9f5c64e0e061
-	return config.Once(apiLevelsMapKey, func() interface{} {
-		apiLevelsMap := getApiLevelsMapReleasedVersions()
-		for i, codename := range config.PlatformVersionAllPreviewCodenames() {
-			apiLevelsMap[codename] = previewAPILevelBase + i
+	result := config.Once(apiLevelsMapKey, func() interface{} {
+		apiLevelsMap, err := getApiLevelsMapReleasedVersions()
+		if err == nil {
+			for i, codename := range config.PlatformVersionAllPreviewCodenames() {
+				apiLevelsMap[codename] = previewAPILevelBase + i
+			}
 		}
 
-		return apiLevelsMap
-	}).(map[string]int)
+		return resultStruct{apiLevelsMap, err}
+	}).(resultStruct)
+	return result.result, result.err
 }
 
 func (a *apiLevelsSingleton) GenerateBuildActions(ctx SingletonContext) {
-	apiLevelsMap := GetApiLevelsMap(ctx.Config())
+	apiLevelsMap, err := GetApiLevelsMap(ctx.Config())
+	if err != nil {
+		ctx.Errorf("%s\n", err)
+		return
+	}
 	apiLevelsJson := GetApiLevelsJson(ctx)
 	createApiLevelsJson(ctx, apiLevelsJson, apiLevelsMap)
 }
-
-func StarlarkApiLevelConfigs(config Config) string {
-	return fmt.Sprintf(bazel.GeneratedBazelFileWarning+`
-_api_levels_released_versions = %s
-
-api_levels_released_versions = _api_levels_released_versions
-`, starlark_fmt.PrintStringIntDict(getApiLevelsMapReleasedVersions(), 0),
-	)
-}
diff --git a/android/bazel.go b/android/bazel.go
index 1646883..e631ed4 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -32,6 +32,26 @@
 	Bp2BuildTopLevel = "."
 )
 
+type MixedBuildEnabledStatus int
+
+const (
+	// This module can be mixed_built.
+	MixedBuildEnabled = iota
+
+	// There is a technical incompatibility preventing this module from being
+	// bazel-analyzed. Note: the module might also be incompatible.
+	TechnicalIncompatibility
+
+	// This module cannot be mixed_built due to some incompatibility with it
+	// that is not a platform incompatibility. Example: the module-type is not
+	// enabled, or is not bp2build-converted.
+	ModuleIncompatibility
+
+	// Missing dependencies. We can't query Bazel for modules if it has missing dependencies, there
+	// will be failures.
+	ModuleMissingDeps
+)
+
 // FileGroupAsLibrary describes a filegroup module that is converted to some library
 // such as aidl_library or proto_library.
 type FileGroupAsLibrary interface {
@@ -55,7 +75,7 @@
 	MissingDeps []string `blueprint:"mutated"`
 }
 
-type bazelModuleProperties struct {
+type BazelModuleProperties struct {
 	// The label of the Bazel target replacing this Soong module. When run in conversion mode, this
 	// will import the handcrafted build target into the autogenerated file. Note: this may result in
 	// a conflict due to duplicate targets if bp2build_available is also set.
@@ -80,7 +100,7 @@
 type properties struct {
 	// In "Bazel mixed build" mode, this represents the Bazel target replacing
 	// this Soong module.
-	Bazel_module bazelModuleProperties
+	Bazel_module BazelModuleProperties
 }
 
 // namespacedVariableProperties is a map from a string representing a Soong
@@ -346,24 +366,41 @@
 	}).(Bp2BuildConversionAllowlist)
 }
 
-// MixedBuildsEnabled returns true if a module is ready to be replaced by a
-// converted or handcrafted Bazel target. As a side effect, calling this
-// method will also log whether this module is mixed build enabled for
-// metrics reporting.
-func MixedBuildsEnabled(ctx BaseModuleContext) bool {
+// MixedBuildsEnabled returns a MixedBuildEnabledStatus regarding whether
+// a module is ready to be replaced by a converted or handcrafted Bazel target.
+// As a side effect, calling this method will also log whether this module is
+// mixed build enabled for metrics reporting.
+func MixedBuildsEnabled(ctx BaseModuleContext) MixedBuildEnabledStatus {
+	platformIncompatible := isPlatformIncompatible(ctx.Os(), ctx.Arch().ArchType)
+	if platformIncompatible {
+		ctx.Config().LogMixedBuild(ctx, false)
+		return TechnicalIncompatibility
+	}
+
+	if ctx.Config().AllowMissingDependencies() {
+		missingDeps := ctx.getMissingDependencies()
+		// If there are missing dependencies, querying Bazel will fail. Soong instead fails at execution
+		// time, not loading/analysis. disable mixed builds and fall back to Soong to maintain that
+		// behavior.
+		if len(missingDeps) > 0 {
+			ctx.Config().LogMixedBuild(ctx, false)
+			return ModuleMissingDeps
+		}
+	}
+
 	module := ctx.Module()
 	apexInfo := ctx.Provider(ApexInfoProvider).(ApexInfo)
 	withinApex := !apexInfo.IsForPlatform()
 	mixedBuildEnabled := ctx.Config().IsMixedBuildsEnabled() &&
-		ctx.Os() != Windows && // Windows toolchains are not currently supported.
-		ctx.Os() != LinuxBionic && // Linux Bionic toolchains are not currently supported.
-		ctx.Os() != LinuxMusl && // Linux musl toolchains are not currently supported (b/259266326).
-		ctx.Arch().ArchType != Riscv64 && // TODO(b/262192655) Riscv64 toolchains are not currently supported.
 		module.Enabled() &&
 		convertedToBazel(ctx, module) &&
 		ctx.Config().BazelContext.IsModuleNameAllowed(module.Name(), withinApex)
 	ctx.Config().LogMixedBuild(ctx, mixedBuildEnabled)
-	return mixedBuildEnabled
+
+	if mixedBuildEnabled {
+		return MixedBuildEnabled
+	}
+	return ModuleIncompatibility
 }
 
 // ConvertedToBazel returns whether this module has been converted (with bp2build or manually) to Bazel.
@@ -388,6 +425,13 @@
 	OtherModuleDir(m blueprint.Module) string
 }
 
+func isPlatformIncompatible(osType OsType, arch ArchType) bool {
+	return osType == Windows || // Windows toolchains are not currently supported.
+		osType == LinuxBionic || // Linux Bionic toolchains are not currently supported.
+		osType == LinuxMusl || // Linux musl toolchains are not currently supported (b/259266326).
+		arch == Riscv64 // TODO(b/262192655) Riscv64 toolchains are not currently supported.
+}
+
 func (b *BazelModuleBase) shouldConvertWithBp2build(ctx bazelOtherModuleContext, module blueprint.Module) bool {
 	if !b.bazelProps().Bazel_module.CanConvertToBazel {
 		return false
@@ -401,7 +445,7 @@
 	}
 
 	propValue := b.bazelProperties.Bazel_module.Bp2build_available
-	packagePath := ctx.OtherModuleDir(module)
+	packagePath := moduleDirWithPossibleOverride(ctx, module)
 
 	// Modules in unit tests which are enabled in the allowlist by type or name
 	// trigger this conditional because unit tests run under the "." package path
@@ -410,7 +454,7 @@
 		return true
 	}
 
-	moduleName := module.Name()
+	moduleName := moduleNameWithPossibleOverride(ctx, module)
 	allowlist := ctx.Config().Bp2buildPackageConfig
 	moduleNameAllowed := allowlist.moduleAlwaysConvert[moduleName]
 	moduleTypeAllowed := allowlist.moduleTypeAlwaysConvert[ctx.OtherModuleType(module)]
@@ -493,6 +537,12 @@
 }
 
 func convertWithBp2build(ctx TopDownMutatorContext) {
+	if ctx.Config().HasBazelBuildTargetInSource(ctx) {
+		// Defer to the BUILD target. Generating an additional target would
+		// cause a BUILD file conflict.
+		return
+	}
+
 	bModule, ok := ctx.Module().(Bazelable)
 	if !ok || !bModule.shouldConvertWithBp2build(ctx, ctx.Module()) {
 		return
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index ade2c49..4645e6b 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -18,7 +18,6 @@
 	"bytes"
 	"fmt"
 	"os"
-	"os/exec"
 	"path"
 	"path/filepath"
 	"runtime"
@@ -61,8 +60,12 @@
 		"AUTO_UNINITIALIZE",
 		"USE_CCACHE",
 		"LLVM_NEXT",
+		"LLVM_PREBUILTS_VERSION",
+		"LLVM_RELEASE_VERSION",
 		"ALLOW_UNKNOWN_WARNING_OPTION",
 
+		"UNBUNDLED_BUILD_TARGET_SDK_WITH_API_FINGERPRINT",
+
 		// Overrides the version in the apex_manifest.json. The version is unique for
 		// each branch (internal, aosp, mainline releases, dessert releases).  This
 		// enables modules built on an older branch to be installed against a newer
@@ -71,25 +74,21 @@
 	}
 )
 
-func init() {
-	RegisterMixedBuildsMutator(InitRegistrationContext)
+func registerMixedBuildsMutator(ctx RegisterMutatorsContext) {
+	ctx.BottomUp("mixed_builds_prep", mixedBuildsPrepareMutator).Parallel()
 }
 
 func RegisterMixedBuildsMutator(ctx RegistrationContext) {
-	ctx.FinalDepsMutators(func(ctx RegisterMutatorsContext) {
-		ctx.BottomUp("mixed_builds_prep", mixedBuildsPrepareMutator).Parallel()
-	})
+	ctx.FinalDepsMutators(registerMixedBuildsMutator)
 }
 
 func mixedBuildsPrepareMutator(ctx BottomUpMutatorContext) {
 	if m := ctx.Module(); m.Enabled() {
 		if mixedBuildMod, ok := m.(MixedBuildBuildable); ok {
-			queueMixedBuild := mixedBuildMod.IsMixedBuildSupported(ctx) && MixedBuildsEnabled(ctx)
+			mixedBuildEnabled := MixedBuildsEnabled(ctx)
+			queueMixedBuild := mixedBuildMod.IsMixedBuildSupported(ctx) && mixedBuildEnabled == MixedBuildEnabled
 			if queueMixedBuild {
 				mixedBuildMod.QueueBazelCall(ctx)
-			} else if _, ok := ctx.Config().bazelForceEnabledModules[m.Name()]; ok {
-				// TODO(b/273910287) - remove this once --ensure_allowlist_integrity is added
-				ctx.ModuleErrorf("Attempted to force enable an unready module: %s. Did you forget to Bp2BuildDefaultTrue its directory?\n", m.Name())
 			}
 		}
 	}
@@ -182,16 +181,15 @@
 	// Returns the results of GetOutputFiles and GetCcObjectFiles in a single query (in that order).
 	GetCcInfo(label string, cfgKey configKey) (cquery.CcInfo, error)
 
-	// Returns the executable binary resultant from building together the python sources
-	// TODO(b/232976601): Remove.
-	GetPythonBinary(label string, cfgKey configKey) (string, error)
-
 	// Returns the results of the GetApexInfo query (including output files)
 	GetApexInfo(label string, cfgkey configKey) (cquery.ApexInfo, error)
 
 	// Returns the results of the GetCcUnstrippedInfo query
 	GetCcUnstrippedInfo(label string, cfgkey configKey) (cquery.CcUnstrippedInfo, error)
 
+	// Returns the results of the GetPrebuiltFileInfo query
+	GetPrebuiltFileInfo(label string, cfgKey configKey) (cquery.PrebuiltFileInfo, error)
+
 	// ** end Cquery Results Retrieval Functions
 
 	// Issues commands to Bazel to receive results for all cquery requests
@@ -218,8 +216,7 @@
 }
 
 type bazelRunner interface {
-	createBazelCommand(config Config, paths *bazelPaths, runName bazel.RunName, command bazelCommand, extraFlags ...string) *exec.Cmd
-	issueBazelCommand(bazelCmd *exec.Cmd, eventHandler *metrics.EventHandler) (output string, errorMessage string, error error)
+	issueBazelCommand(cmdRequest bazel.CmdRequest, paths *bazelPaths, eventHandler *metrics.EventHandler) (output string, errorMessage string, error error)
 }
 
 type bazelPaths struct {
@@ -278,11 +275,12 @@
 type MockBazelContext struct {
 	OutputBaseDir string
 
-	LabelToOutputFiles  map[string][]string
-	LabelToCcInfo       map[string]cquery.CcInfo
-	LabelToPythonBinary map[string]string
-	LabelToApexInfo     map[string]cquery.ApexInfo
-	LabelToCcBinary     map[string]cquery.CcUnstrippedInfo
+	LabelToOutputFiles      map[string][]string
+	LabelToCcInfo           map[string]cquery.CcInfo
+	LabelToPythonBinary     map[string]string
+	LabelToApexInfo         map[string]cquery.ApexInfo
+	LabelToCcBinary         map[string]cquery.CcUnstrippedInfo
+	LabelToPrebuiltFileInfo map[string]cquery.PrebuiltFileInfo
 
 	BazelRequests map[string]bool
 }
@@ -315,14 +313,6 @@
 	return result, nil
 }
 
-func (m MockBazelContext) GetPythonBinary(label string, _ configKey) (string, error) {
-	result, ok := m.LabelToPythonBinary[label]
-	if !ok {
-		return "", fmt.Errorf("no target with label %q in LabelToPythonBinary", label)
-	}
-	return result, nil
-}
-
 func (m MockBazelContext) GetApexInfo(label string, _ configKey) (cquery.ApexInfo, error) {
 	result, ok := m.LabelToApexInfo[label]
 	if !ok {
@@ -339,6 +329,14 @@
 	return result, nil
 }
 
+func (m MockBazelContext) GetPrebuiltFileInfo(label string, _ configKey) (cquery.PrebuiltFileInfo, error) {
+	result, ok := m.LabelToPrebuiltFileInfo[label]
+	if !ok {
+		return cquery.PrebuiltFileInfo{}, fmt.Errorf("no target with label %q in LabelToPrebuiltFileInfo", label)
+	}
+	return result, nil
+}
+
 func (m MockBazelContext) InvokeBazel(_ Config, _ invokeBazelContext) error {
 	panic("unimplemented")
 }
@@ -431,15 +429,6 @@
 	return cquery.CcInfo{}, fmt.Errorf("no bazel response found for %v", key)
 }
 
-func (bazelCtx *mixedBuildBazelContext) GetPythonBinary(label string, cfgKey configKey) (string, error) {
-	key := makeCqueryKey(label, cquery.GetPythonBinary, cfgKey)
-	if rawString, ok := bazelCtx.results[key]; ok {
-		bazelOutput := strings.TrimSpace(rawString)
-		return cquery.GetPythonBinary.ParseResult(bazelOutput), nil
-	}
-	return "", fmt.Errorf("no bazel response found for %v", key)
-}
-
 func (bazelCtx *mixedBuildBazelContext) GetApexInfo(label string, cfgKey configKey) (cquery.ApexInfo, error) {
 	key := makeCqueryKey(label, cquery.GetApexInfo, cfgKey)
 	if rawString, ok := bazelCtx.results[key]; ok {
@@ -456,6 +445,14 @@
 	return cquery.CcUnstrippedInfo{}, fmt.Errorf("no bazel response for %s", key)
 }
 
+func (bazelCtx *mixedBuildBazelContext) GetPrebuiltFileInfo(label string, cfgKey configKey) (cquery.PrebuiltFileInfo, error) {
+	key := makeCqueryKey(label, cquery.GetPrebuiltFileInfo, cfgKey)
+	if rawString, ok := bazelCtx.results[key]; ok {
+		return cquery.GetPrebuiltFileInfo.ParseResult(strings.TrimSpace(rawString))
+	}
+	return cquery.PrebuiltFileInfo{}, fmt.Errorf("no bazel response for %s", key)
+}
+
 func (n noopBazelContext) QueueBazelRequest(_ string, _ cqueryRequest, _ configKey) {
 	panic("unimplemented")
 }
@@ -468,10 +465,6 @@
 	panic("unimplemented")
 }
 
-func (n noopBazelContext) GetPythonBinary(_ string, _ configKey) (string, error) {
-	panic("unimplemented")
-}
-
 func (n noopBazelContext) GetApexInfo(_ string, _ configKey) (cquery.ApexInfo, error) {
 	panic("unimplemented")
 }
@@ -481,6 +474,10 @@
 	panic("implement me")
 }
 
+func (n noopBazelContext) GetPrebuiltFileInfo(_ string, _ configKey) (cquery.PrebuiltFileInfo, error) {
+	panic("implement me")
+}
+
 func (n noopBazelContext) InvokeBazel(_ Config, _ invokeBazelContext) error {
 	panic("unimplemented")
 }
@@ -505,7 +502,7 @@
 	return []bazel.AqueryDepset{}
 }
 
-func addToStringSet(set map[string]bool, items []string) {
+func AddToStringSet(set map[string]bool, items []string) {
 	for _, item := range items {
 		set[item] = true
 	}
@@ -517,19 +514,19 @@
 
 	switch buildMode {
 	case BazelProdMode:
-		addToStringSet(enabledModules, allowlists.ProdMixedBuildsEnabledList)
+		AddToStringSet(enabledModules, allowlists.ProdMixedBuildsEnabledList)
 		for enabledAdHocModule := range forceEnabled {
 			enabledModules[enabledAdHocModule] = true
 		}
 	case BazelStagingMode:
 		// Staging mode includes all prod modules plus all staging modules.
-		addToStringSet(enabledModules, allowlists.ProdMixedBuildsEnabledList)
-		addToStringSet(enabledModules, allowlists.StagingMixedBuildsEnabledList)
+		AddToStringSet(enabledModules, allowlists.ProdMixedBuildsEnabledList)
+		AddToStringSet(enabledModules, allowlists.StagingMixedBuildsEnabledList)
 		for enabledAdHocModule := range forceEnabled {
 			enabledModules[enabledAdHocModule] = true
 		}
 	case BazelDevMode:
-		addToStringSet(disabledModules, allowlists.MixedBuildsDisabledList)
+		AddToStringSet(disabledModules, allowlists.MixedBuildsDisabledList)
 	default:
 		panic("Expected BazelProdMode, BazelStagingMode, or BazelDevMode")
 	}
@@ -609,7 +606,7 @@
 			allowlists.StagingDclaMixedBuildsEnabledList...)
 	}
 	dclaEnabledModules := map[string]bool{}
-	addToStringSet(dclaEnabledModules, dclaMixedBuildsEnabledList)
+	AddToStringSet(dclaEnabledModules, dclaMixedBuildsEnabledList)
 	return &mixedBuildBazelContext{
 		bazelRunner:             &builtinBazelRunner{c.UseBazelProxy, absolutePath(c.outDir)},
 		paths:                   &paths,
@@ -658,36 +655,6 @@
 	expression string
 }
 
-type mockBazelRunner struct {
-	bazelCommandResults map[bazelCommand]string
-	// use *exec.Cmd as a key to get the bazelCommand, the map will be used in issueBazelCommand()
-	// Register createBazelCommand() invocations. Later, an
-	// issueBazelCommand() invocation can be mapped to the *exec.Cmd instance
-	// and then to the expected result via bazelCommandResults
-	tokens     map[*exec.Cmd]bazelCommand
-	commands   []bazelCommand
-	extraFlags []string
-}
-
-func (r *mockBazelRunner) createBazelCommand(_ Config, _ *bazelPaths, _ bazel.RunName,
-	command bazelCommand, extraFlags ...string) *exec.Cmd {
-	r.commands = append(r.commands, command)
-	r.extraFlags = append(r.extraFlags, strings.Join(extraFlags, " "))
-	cmd := &exec.Cmd{}
-	if r.tokens == nil {
-		r.tokens = make(map[*exec.Cmd]bazelCommand)
-	}
-	r.tokens[cmd] = command
-	return cmd
-}
-
-func (r *mockBazelRunner) issueBazelCommand(bazelCmd *exec.Cmd, _ *metrics.EventHandler) (string, string, error) {
-	if command, ok := r.tokens[bazelCmd]; ok {
-		return r.bazelCommandResults[command], "", nil
-	}
-	return "", "", nil
-}
-
 type builtinBazelRunner struct {
 	useBazelProxy bool
 	outDir        string
@@ -697,17 +664,12 @@
 // Returns (stdout, stderr, error). The first and second return values are strings
 // containing the stdout and stderr of the run command, and an error is returned if
 // the invocation returned an error code.
-func (r *builtinBazelRunner) issueBazelCommand(bazelCmd *exec.Cmd, eventHandler *metrics.EventHandler) (string, string, error) {
+func (r *builtinBazelRunner) issueBazelCommand(cmdRequest bazel.CmdRequest, paths *bazelPaths, eventHandler *metrics.EventHandler) (string, string, error) {
 	if r.useBazelProxy {
 		eventHandler.Begin("client_proxy")
 		defer eventHandler.End("client_proxy")
 		proxyClient := bazel.NewProxyClient(r.outDir)
-		// Omit the arg containing the Bazel binary, as that is handled by the proxy
-		// server.
-		bazelFlags := bazelCmd.Args[1:]
-		// TODO(b/270989498): Refactor these functions to not take exec.Cmd, as its
-		// not actually executed for client proxying.
-		resp, err := proxyClient.IssueCommand(bazel.CmdRequest{bazelFlags, bazelCmd.Env})
+		resp, err := proxyClient.IssueCommand(cmdRequest)
 
 		if err != nil {
 			return "", "", err
@@ -719,32 +681,28 @@
 	} else {
 		eventHandler.Begin("bazel command")
 		defer eventHandler.End("bazel command")
-		stderr := &bytes.Buffer{}
-		bazelCmd.Stderr = stderr
-		if output, err := bazelCmd.Output(); err != nil {
-			return "", string(stderr.Bytes()),
-				fmt.Errorf("bazel command failed: %s\n---command---\n%s\n---env---\n%s\n---stderr---\n%s---",
-					err, bazelCmd, strings.Join(bazelCmd.Env, "\n"), stderr)
-		} else {
-			return string(output), string(stderr.Bytes()), nil
-		}
+
+		stdout, stderr, err := bazel.ExecBazel(paths.bazelPath, absolutePath(paths.syntheticWorkspaceDir()), cmdRequest)
+		return string(stdout), string(stderr), err
 	}
 }
 
-func (r *builtinBazelRunner) createBazelCommand(config Config, paths *bazelPaths, runName bazel.RunName, command bazelCommand,
-	extraFlags ...string) *exec.Cmd {
+func (context *mixedBuildBazelContext) createBazelCommand(config Config, runName bazel.RunName, command bazelCommand,
+	extraFlags ...string) bazel.CmdRequest {
+	if runtime.GOOS != "linux" && runtime.GOOS != "darwin" {
+		panic("Unknown GOOS: " + runtime.GOOS)
+	}
 	cmdFlags := []string{
-		"--output_base=" + absolutePath(paths.outputBase),
+		"--output_base=" + absolutePath(context.paths.outputBase),
 		command.command,
 		command.expression,
-		// TODO(asmundak): is it needed in every build?
-		"--profile=" + shared.BazelMetricsFilename(paths, runName),
+		"--profile=" + shared.BazelMetricsFilename(context.paths, runName),
 
-		// We don't need to set --host_platforms because it's set in bazelrc files
-		// that the bazel shell script wrapper passes
-
-		// Explicitly disable downloading rules (such as canonical C++ and Java rules) from the network.
-		"--experimental_repository_disable_download",
+		"--host_platform=@soong_injection//product_config_platforms:mixed_builds_product-" + context.targetBuildVariant + "_" + runtime.GOOS + "_x86_64",
+		// Don't specify --platforms, because on some products/branches (like kernel-build-tools)
+		// the main platform for mixed_builds_product-variant doesn't exist because an arch isn't
+		// specified in product config. The derivative platforms that config_node transitions into
+		// will still work.
 
 		// Suppress noise
 		"--ui_event_filters=-INFO",
@@ -753,15 +711,13 @@
 	}
 	cmdFlags = append(cmdFlags, extraFlags...)
 
-	bazelCmd := exec.Command(paths.bazelPath, cmdFlags...)
-	bazelCmd.Dir = absolutePath(paths.syntheticWorkspaceDir())
 	extraEnv := []string{
-		"HOME=" + paths.homeDir,
+		"HOME=" + context.paths.homeDir,
 		pwdPrefix(),
-		"BUILD_DIR=" + absolutePath(paths.soongOutDir),
+		"BUILD_DIR=" + absolutePath(context.paths.soongOutDir),
 		// Make OUT_DIR absolute here so build/bazel/bin/bazel uses the correct
 		// OUT_DIR at <root>/out, instead of <root>/out/soong/workspace/out.
-		"OUT_DIR=" + absolutePath(paths.outDir()),
+		"OUT_DIR=" + absolutePath(context.paths.outDir()),
 		// Disables local host detection of gcc; toolchain information is defined
 		// explicitly in BUILD files.
 		"BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1",
@@ -773,15 +729,15 @@
 		}
 		extraEnv = append(extraEnv, fmt.Sprintf("%s=%s", envvar, val))
 	}
-	bazelCmd.Env = append(os.Environ(), extraEnv...)
+	envVars := append(os.Environ(), extraEnv...)
 
-	return bazelCmd
+	return bazel.CmdRequest{cmdFlags, envVars}
 }
 
-func printableCqueryCommand(bazelCmd *exec.Cmd) string {
-	outputString := strings.Join(bazelCmd.Env, " ") + " \"" + strings.Join(bazelCmd.Args, "\" \"") + "\""
+func (context *mixedBuildBazelContext) printableCqueryCommand(bazelCmd bazel.CmdRequest) string {
+	args := append([]string{context.paths.bazelPath}, bazelCmd.Argv...)
+	outputString := strings.Join(bazelCmd.Env, " ") + " \"" + strings.Join(args, "\" \"") + "\""
 	return outputString
-
 }
 
 func (context *mixedBuildBazelContext) mainBzlFileContents() []byte {
@@ -793,9 +749,9 @@
 #####################################################
 def _config_node_transition_impl(settings, attr):
     if attr.os == "android" and attr.arch == "target":
-        target = "{PRODUCT}-{VARIANT}"
+        target = "mixed_builds_product-{VARIANT}"
     else:
-        target = "{PRODUCT}-{VARIANT}_%s_%s" % (attr.os, attr.arch)
+        target = "mixed_builds_product-{VARIANT}_%s_%s" % (attr.os, attr.arch)
     apex_name = ""
     if attr.within_apex:
         # //build/bazel/rules/apex:apex_name has to be set to a non_empty value,
@@ -806,7 +762,7 @@
         # value here.
         apex_name = "dcla_apex"
     outputs = {
-        "//command_line_option:platforms": "@soong_injection//product_config_platforms/products/{PRODUCT}-{VARIANT}:%s" % target,
+        "//command_line_option:platforms": "@soong_injection//product_config_platforms:%s" % target,
         "@//build/bazel/rules/apex:within_apex": attr.within_apex,
         "@//build/bazel/rules/apex:min_sdk_version": attr.apex_sdk_version,
         "@//build/bazel/rules/apex:apex_name": apex_name,
@@ -1032,9 +988,9 @@
   platform_name = platforms[0].name
   if platform_name == "host":
     return "HOST"
-  if not platform_name.startswith("{TARGET_PRODUCT}-{TARGET_BUILD_VARIANT}"):
-    fail("expected platform name of the form '{TARGET_PRODUCT}-{TARGET_BUILD_VARIANT}_android_<arch>' or '{TARGET_PRODUCT}-{TARGET_BUILD_VARIANT}_linux_<arch>', but was " + str(platforms))
-  platform_name = platform_name.removeprefix("{TARGET_PRODUCT}-{TARGET_BUILD_VARIANT}").removeprefix("_")
+  if not platform_name.startswith("mixed_builds_product-{TARGET_BUILD_VARIANT}"):
+    fail("expected platform name of the form 'mixed_builds_product-{TARGET_BUILD_VARIANT}_android_<arch>' or 'mixed_builds_product-{TARGET_BUILD_VARIANT}_linux_<arch>', but was " + str(platforms))
+  platform_name = platform_name.removeprefix("mixed_builds_product-{TARGET_BUILD_VARIANT}").removeprefix("_")
   config_key = ""
   if not platform_name:
     config_key = "target|android"
@@ -1043,7 +999,7 @@
   elif platform_name.startswith("linux_"):
     config_key = platform_name.removeprefix("linux_") + "|linux"
   else:
-    fail("expected platform name of the form '{TARGET_PRODUCT}-{TARGET_BUILD_VARIANT}_android_<arch>' or '{TARGET_PRODUCT}-{TARGET_BUILD_VARIANT}_linux_<arch>', but was " + str(platforms))
+    fail("expected platform name of the form 'mixed_builds_product-{TARGET_BUILD_VARIANT}_android_<arch>' or 'mixed_builds_product-{TARGET_BUILD_VARIANT}_linux_<arch>', but was " + str(platforms))
 
   within_apex = buildoptions.get("//build/bazel/rules/apex:within_apex")
   apex_sdk_version = buildoptions.get("//build/bazel/rules/apex:min_sdk_version")
@@ -1104,9 +1060,10 @@
 const buildrootLabel = "@soong_injection//mixed_builds:buildroot"
 
 var (
-	cqueryCmd = bazelCommand{"cquery", fmt.Sprintf("deps(%s, 2)", buildrootLabel)}
-	aqueryCmd = bazelCommand{"aquery", fmt.Sprintf("deps(%s)", buildrootLabel)}
-	buildCmd  = bazelCommand{"build", "@soong_injection//mixed_builds:phonyroot"}
+	cqueryCmd        = bazelCommand{"cquery", fmt.Sprintf("deps(%s, 2)", buildrootLabel)}
+	aqueryCmd        = bazelCommand{"aquery", fmt.Sprintf("deps(%s)", buildrootLabel)}
+	buildCmd         = bazelCommand{"build", "@soong_injection//mixed_builds:phonyroot"}
+	allBazelCommands = []bazelCommand{aqueryCmd, cqueryCmd, buildCmd}
 )
 
 // Issues commands to Bazel to receive results for all cquery requests
@@ -1168,12 +1125,12 @@
 		extraFlags = append(extraFlags, "--collect_code_coverage")
 	}
 
-	cqueryCommandWithFlag := context.createBazelCommand(config, context.paths, bazel.CqueryBuildRootRunName, cqueryCmd, extraFlags...)
-	cqueryOutput, cqueryErrorMessage, cqueryErr := context.issueBazelCommand(cqueryCommandWithFlag, eventHandler)
+	cqueryCmdRequest := context.createBazelCommand(config, bazel.CqueryBuildRootRunName, cqueryCmd, extraFlags...)
+	cqueryOutput, cqueryErrorMessage, cqueryErr := context.issueBazelCommand(cqueryCmdRequest, context.paths, eventHandler)
 	if cqueryErr != nil {
 		return cqueryErr
 	}
-	cqueryCommandPrint := fmt.Sprintf("cquery command line:\n  %s \n\n\n", printableCqueryCommand(cqueryCommandWithFlag))
+	cqueryCommandPrint := fmt.Sprintf("cquery command line:\n  %s \n\n\n", context.printableCqueryCommand(cqueryCmdRequest))
 	if err := os.WriteFile(filepath.Join(soongInjectionPath, "cquery.out"), []byte(cqueryCommandPrint+cqueryOutput), 0666); err != nil {
 		return err
 	}
@@ -1231,8 +1188,8 @@
 			extraFlags = append(extraFlags, "--instrumentation_filter="+strings.Join(paths, ","))
 		}
 	}
-	aqueryOutput, _, err := context.issueBazelCommand(context.createBazelCommand(config, context.paths, bazel.AqueryBuildRootRunName, aqueryCmd,
-		extraFlags...), eventHandler)
+	aqueryOutput, _, err := context.issueBazelCommand(context.createBazelCommand(config, bazel.AqueryBuildRootRunName, aqueryCmd,
+		extraFlags...), context.paths, eventHandler)
 	if err != nil {
 		return err
 	}
@@ -1247,7 +1204,7 @@
 	// Issue a build command of the phony root to generate symlink forests for dependencies of the
 	// Bazel build. This is necessary because aquery invocations do not generate this symlink forest,
 	// but some of symlinks may be required to resolve source dependencies of the build.
-	_, _, err := context.issueBazelCommand(context.createBazelCommand(config, context.paths, bazel.BazelBuildPhonyRootRunName, buildCmd), eventHandler)
+	_, _, err := context.issueBazelCommand(context.createBazelCommand(config, bazel.BazelBuildPhonyRootRunName, buildCmd), context.paths, eventHandler)
 	return err
 }
 
diff --git a/android/bazel_handler_test.go b/android/bazel_handler_test.go
index c67d7fb..b17b765 100644
--- a/android/bazel_handler_test.go
+++ b/android/bazel_handler_test.go
@@ -8,6 +8,7 @@
 	"strings"
 	"testing"
 
+	"android/soong/bazel"
 	"android/soong/bazel/cquery"
 	analysis_v2_proto "prebuilts/bazel/common/proto/analysis_v2"
 
@@ -19,6 +20,34 @@
 
 type testInvokeBazelContext struct{}
 
+type mockBazelRunner struct {
+	testHelper *testing.T
+	// Stores mock behavior. If an issueBazelCommand request is made for command
+	// k, and {k:v} is present in this map, then the mock will return v.
+	bazelCommandResults map[bazelCommand]string
+	// Requests actually made of the mockBazelRunner with issueBazelCommand,
+	// keyed by the command they represent.
+	bazelCommandRequests map[bazelCommand]bazel.CmdRequest
+}
+
+func (r *mockBazelRunner) bazelCommandForRequest(cmdRequest bazel.CmdRequest) bazelCommand {
+	for _, arg := range cmdRequest.Argv {
+		for _, cmdType := range allBazelCommands {
+			if arg == cmdType.command {
+				return cmdType
+			}
+		}
+	}
+	r.testHelper.Fatalf("Unrecognized bazel request: %s", cmdRequest)
+	return cqueryCmd
+}
+
+func (r *mockBazelRunner) issueBazelCommand(cmdRequest bazel.CmdRequest, paths *bazelPaths, eventHandler *metrics.EventHandler) (string, string, error) {
+	command := r.bazelCommandForRequest(cmdRequest)
+	r.bazelCommandRequests[command] = cmdRequest
+	return r.bazelCommandResults[command], "", nil
+}
+
 func (t *testInvokeBazelContext) GetEventHandler() *metrics.EventHandler {
 	return &metrics.EventHandler{}
 }
@@ -36,9 +65,7 @@
 		`@//foo:foo|arm64_armv8-a|android|within_apex|29>>out/foo/foo.txt`,
 		`@//foo:bar|arm64_armv8-a|android>>out/foo/bar.txt`,
 	}
-	bazelContext, _ := testBazelContext(t, map[bazelCommand]string{
-		bazelCommand{command: "cquery", expression: "deps(@soong_injection//mixed_builds:buildroot, 2)"}: strings.Join(cmd_results, "\n"),
-	})
+	bazelContext, _ := testBazelContext(t, map[bazelCommand]string{cqueryCmd: strings.Join(cmd_results, "\n")})
 
 	bazelContext.QueueBazelRequest(label_foo, cquery.GetOutputFiles, cfg_foo)
 	bazelContext.QueueBazelRequest(label_bar, cquery.GetOutputFiles, cfg_bar)
@@ -139,8 +166,7 @@
 		if err != nil {
 			t.Error(err)
 		}
-		bazelContext, _ := testBazelContext(t, map[bazelCommand]string{
-			bazelCommand{command: "aquery", expression: "deps(@soong_injection//mixed_builds:buildroot)"}: string(data)})
+		bazelContext, _ := testBazelContext(t, map[bazelCommand]string{aqueryCmd: string(data)})
 
 		err = bazelContext.InvokeBazel(testConfig, &testInvokeBazelContext{})
 		if err != nil {
@@ -166,30 +192,26 @@
 
 	testConfig.productVariables.NativeCoveragePaths = []string{"foo1", "foo2"}
 	testConfig.productVariables.NativeCoverageExcludePaths = []string{"bar1", "bar2"}
-	verifyExtraFlags(t, testConfig, `--collect_code_coverage --instrumentation_filter=+foo1,+foo2,-bar1,-bar2`)
+	verifyAqueryContainsFlags(t, testConfig, "--collect_code_coverage", "--instrumentation_filter=+foo1,+foo2,-bar1,-bar2")
 
 	testConfig.productVariables.NativeCoveragePaths = []string{"foo1"}
 	testConfig.productVariables.NativeCoverageExcludePaths = []string{"bar1"}
-	verifyExtraFlags(t, testConfig, `--collect_code_coverage --instrumentation_filter=+foo1,-bar1`)
+	verifyAqueryContainsFlags(t, testConfig, "--collect_code_coverage", "--instrumentation_filter=+foo1,-bar1")
 
 	testConfig.productVariables.NativeCoveragePaths = []string{"foo1"}
 	testConfig.productVariables.NativeCoverageExcludePaths = nil
-	verifyExtraFlags(t, testConfig, `--collect_code_coverage --instrumentation_filter=+foo1`)
+	verifyAqueryContainsFlags(t, testConfig, "--collect_code_coverage", "--instrumentation_filter=+foo1")
 
 	testConfig.productVariables.NativeCoveragePaths = nil
 	testConfig.productVariables.NativeCoverageExcludePaths = []string{"bar1"}
-	verifyExtraFlags(t, testConfig, `--collect_code_coverage --instrumentation_filter=-bar1`)
+	verifyAqueryContainsFlags(t, testConfig, "--collect_code_coverage", "--instrumentation_filter=-bar1")
 
 	testConfig.productVariables.NativeCoveragePaths = []string{"*"}
 	testConfig.productVariables.NativeCoverageExcludePaths = nil
-	verifyExtraFlags(t, testConfig, `--collect_code_coverage --instrumentation_filter=+.*`)
+	verifyAqueryContainsFlags(t, testConfig, "--collect_code_coverage", "--instrumentation_filter=+.*")
 
 	testConfig.productVariables.ClangCoverage = boolPtr(false)
-	actual := verifyExtraFlags(t, testConfig, ``)
-	if strings.Contains(actual, "--collect_code_coverage") ||
-		strings.Contains(actual, "--instrumentation_filter=") {
-		t.Errorf("Expected code coverage disabled, but got %#v", actual)
-	}
+	verifyAqueryDoesNotContainSubstrings(t, testConfig, "collect_code_coverage", "instrumentation_filter")
 }
 
 func TestBazelRequestsSorted(t *testing.T) {
@@ -268,7 +290,8 @@
 	}
 }
 
-func verifyExtraFlags(t *testing.T, config Config, expected string) string {
+func verifyAqueryContainsFlags(t *testing.T, config Config, expected ...string) {
+	t.Helper()
 	bazelContext, _ := testBazelContext(t, map[bazelCommand]string{})
 
 	err := bazelContext.InvokeBazel(config, &testInvokeBazelContext{})
@@ -276,17 +299,49 @@
 		t.Fatalf("Did not expect error invoking Bazel, but got %s", err)
 	}
 
-	flags := bazelContext.bazelRunner.(*mockBazelRunner).extraFlags
-	if expected := 3; len(flags) != expected {
-		t.Errorf("Expected %d extra flags got %#v", expected, flags)
+	sliceContains := func(slice []string, x string) bool {
+		for _, s := range slice {
+			if s == x {
+				return true
+			}
+		}
+		return false
 	}
 
-	actual := flags[1]
-	if !strings.Contains(actual, expected) {
-		t.Errorf("Expected %#v got %#v", expected, actual)
+	aqueryArgv := bazelContext.bazelRunner.(*mockBazelRunner).bazelCommandRequests[aqueryCmd].Argv
+
+	for _, expectedFlag := range expected {
+		if !sliceContains(aqueryArgv, expectedFlag) {
+			t.Errorf("aquery does not contain expected flag %#v. Argv was: %#v", expectedFlag, aqueryArgv)
+		}
+	}
+}
+
+func verifyAqueryDoesNotContainSubstrings(t *testing.T, config Config, substrings ...string) {
+	t.Helper()
+	bazelContext, _ := testBazelContext(t, map[bazelCommand]string{})
+
+	err := bazelContext.InvokeBazel(config, &testInvokeBazelContext{})
+	if err != nil {
+		t.Fatalf("Did not expect error invoking Bazel, but got %s", err)
 	}
 
-	return actual
+	sliceContainsSubstring := func(slice []string, substring string) bool {
+		for _, s := range slice {
+			if strings.Contains(s, substring) {
+				return true
+			}
+		}
+		return false
+	}
+
+	aqueryArgv := bazelContext.bazelRunner.(*mockBazelRunner).bazelCommandRequests[aqueryCmd].Argv
+
+	for _, substring := range substrings {
+		if sliceContainsSubstring(aqueryArgv, substring) {
+			t.Errorf("aquery contains unexpected substring %#v. Argv was: %#v", substring, aqueryArgv)
+		}
+	}
 }
 
 func testBazelContext(t *testing.T, bazelCommandResults map[bazelCommand]string) (*mixedBuildBazelContext, string) {
@@ -296,11 +351,14 @@
 		outputBase:   "outputbase",
 		workspaceDir: "workspace_dir",
 	}
-	aqueryCommand := bazelCommand{command: "aquery", expression: "deps(@soong_injection//mixed_builds:buildroot)"}
-	if _, exists := bazelCommandResults[aqueryCommand]; !exists {
-		bazelCommandResults[aqueryCommand] = ""
+	if _, exists := bazelCommandResults[aqueryCmd]; !exists {
+		bazelCommandResults[aqueryCmd] = ""
 	}
-	runner := &mockBazelRunner{bazelCommandResults: bazelCommandResults}
+	runner := &mockBazelRunner{
+		testHelper:           t,
+		bazelCommandResults:  bazelCommandResults,
+		bazelCommandRequests: map[bazelCommand]bazel.CmdRequest{},
+	}
 	return &mixedBuildBazelContext{
 		bazelRunner: runner,
 		paths:       &p,
diff --git a/android/bazel_paths.go b/android/bazel_paths.go
index bad7baf..872e908 100644
--- a/android/bazel_paths.go
+++ b/android/bazel_paths.go
@@ -242,6 +242,11 @@
 
 	// Don't transform OriginalModuleName
 	newPath.OriginalModuleName = path.OriginalModuleName
+	// if it wasn't a module, store the original path. We may need the original path to replace
+	// references if it is actually in another package
+	if path.OriginalModuleName == "" {
+		newPath.OriginalModuleName = path.Label
+	}
 
 	if strings.HasPrefix(path.Label, "//") {
 		// Assume absolute labels are already correct (e.g. //path/to/some/package:foo.h)
@@ -453,8 +458,8 @@
 }
 
 func bp2buildModuleLabel(ctx BazelConversionContext, module blueprint.Module) string {
-	moduleName := ctx.OtherModuleName(module)
-	moduleDir := ctx.OtherModuleDir(module)
+	moduleName := moduleNameWithPossibleOverride(ctx, module)
+	moduleDir := moduleDirWithPossibleOverride(ctx, module)
 	if moduleDir == Bp2BuildTopLevel {
 		moduleDir = ""
 	}
diff --git a/android/bazel_test.go b/android/bazel_test.go
index 87b2c8f..13fd408 100644
--- a/android/bazel_test.go
+++ b/android/bazel_test.go
@@ -218,7 +218,7 @@
 
 var bazelableBazelModuleBase = BazelModuleBase{
 	bazelProperties: properties{
-		Bazel_module: bazelModuleProperties{
+		Bazel_module: BazelModuleProperties{
 			CanConvertToBazel: true,
 		},
 	},
@@ -344,7 +344,7 @@
 				},
 				BazelModuleBase: BazelModuleBase{
 					bazelProperties: properties{
-						Bazel_module: bazelModuleProperties{
+						Bazel_module: BazelModuleProperties{
 							CanConvertToBazel:  true,
 							Bp2build_available: proptools.BoolPtr(true),
 						},
@@ -436,3 +436,150 @@
 		}
 	}
 }
+
+type mixedBuildModule struct {
+	ModuleBase
+	BazelModuleBase
+	props struct {
+		Deps                     []string
+		Mixed_build_incompatible *bool
+		QueuedBazelCall          bool `blueprint:"mutated"`
+	}
+}
+
+type mixedBuildModuleInfo struct {
+	QueuedBazelCall bool
+}
+
+var mixedBuildModuleProvider = blueprint.NewProvider(mixedBuildModuleInfo{})
+
+func mixedBuildModuleFactory() Module {
+	m := &mixedBuildModule{}
+	m.AddProperties(&m.props)
+	InitAndroidArchModule(m, HostAndDeviceDefault, MultilibBoth)
+	InitBazelModule(m)
+
+	return m
+}
+
+func (m *mixedBuildModule) ConvertWithBp2build(ctx TopDownMutatorContext) {
+}
+
+func (m *mixedBuildModule) DepsMutator(ctx BottomUpMutatorContext) {
+	ctx.AddDependency(ctx.Module(), installDepTag{}, m.props.Deps...)
+}
+
+func (m *mixedBuildModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+}
+
+func (m *mixedBuildModule) IsMixedBuildSupported(ctx BaseModuleContext) bool {
+	return !proptools.Bool(m.props.Mixed_build_incompatible)
+}
+
+func (m *mixedBuildModule) QueueBazelCall(ctx BaseModuleContext) {
+	m.props.QueuedBazelCall = true
+}
+
+func (m *mixedBuildModule) ProcessBazelQueryResponse(ctx ModuleContext) {
+	ctx.SetProvider(mixedBuildModuleProvider, mixedBuildModuleInfo{
+		QueuedBazelCall: m.props.QueuedBazelCall,
+	})
+}
+
+var prepareForMixedBuildTests = FixtureRegisterWithContext(func(ctx RegistrationContext) {
+	ctx.RegisterModuleType("deps", mixedBuildModuleFactory)
+	RegisterMixedBuildsMutator(ctx)
+})
+
+func TestMixedBuildsEnabledForType(t *testing.T) {
+	baseBp := `
+	deps {
+		name: "foo",
+		deps: ["bar"],
+		target: { windows: { enabled: true } },
+		%s
+	}
+`
+	depBp := `
+	deps {
+		name: "bar",
+		target: {
+			windows: {
+				enabled: true,
+			},
+		},
+	}
+`
+	testCases := []struct {
+		desc               string
+		variant            *string
+		missingDeps        bool
+		extraBpInfo        string
+		mixedBuildsEnabled bool
+	}{
+		{
+			desc:               "mixed builds works",
+			mixedBuildsEnabled: true,
+			extraBpInfo:        `bazel_module: { bp2build_available: true },`,
+		},
+		{
+			desc:               "missing deps",
+			missingDeps:        true,
+			mixedBuildsEnabled: false,
+			extraBpInfo:        `bazel_module: { bp2build_available: true },`,
+		},
+		{
+			desc:               "windows no mixed builds",
+			mixedBuildsEnabled: false,
+			variant:            proptools.StringPtr("windows_x86"),
+			extraBpInfo:        `bazel_module: { bp2build_available: true },`,
+		},
+		{
+			desc:               "mixed builds disabled by type",
+			mixedBuildsEnabled: false,
+			extraBpInfo: `mixed_build_incompatible: true,
+		bazel_module: { bp2build_available: true },`,
+		},
+		{
+			desc:               "mixed builds not bp2build available",
+			mixedBuildsEnabled: false,
+			extraBpInfo:        `bazel_module: { bp2build_available: false },`,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.desc, func(t *testing.T) {
+			handlers := GroupFixturePreparers(
+				prepareForMixedBuildTests,
+				PrepareForTestWithArchMutator,
+				FixtureModifyConfig(func(config Config) {
+					config.BazelContext = MockBazelContext{
+						OutputBaseDir: "base",
+					}
+					config.Targets[Windows] = []Target{
+						{Windows, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", "", true},
+						{Windows, Arch{ArchType: X86}, NativeBridgeDisabled, "", "", true},
+					}
+				}),
+			)
+			bp := fmt.Sprintf(baseBp, tc.extraBpInfo)
+			if tc.missingDeps {
+				handlers = GroupFixturePreparers(
+					handlers,
+					PrepareForTestWithAllowMissingDependencies,
+				)
+			} else {
+				bp += depBp
+			}
+			result := handlers.RunTestWithBp(t, bp)
+
+			variant := proptools.StringDefault(tc.variant, "android_arm64_armv8-a")
+
+			m := result.ModuleForTests("foo", variant)
+			mixedBuildModuleInfo := result.TestContext.ModuleProvider(m.Module(), mixedBuildModuleProvider).(mixedBuildModuleInfo)
+			if w, g := tc.mixedBuildsEnabled, mixedBuildModuleInfo.QueuedBazelCall; w != g {
+				t.Errorf("Expected mixed builds enabled %t, got mixed builds enabled %t", w, g)
+			}
+		})
+	}
+}
diff --git a/android/buildinfo_prop.go b/android/buildinfo_prop.go
index 46f6488..8e19ad5 100644
--- a/android/buildinfo_prop.go
+++ b/android/buildinfo_prop.go
@@ -23,7 +23,7 @@
 
 func init() {
 	ctx := InitRegistrationContext
-	ctx.RegisterSingletonModuleType("buildinfo_prop", buildinfoPropFactory)
+	ctx.RegisterParallelSingletonModuleType("buildinfo_prop", buildinfoPropFactory)
 }
 
 type buildinfoPropProperties struct {
diff --git a/android/config.go b/android/config.go
index 032172d..90dbe0b 100644
--- a/android/config.go
+++ b/android/config.go
@@ -80,9 +80,10 @@
 
 type CmdArgs struct {
 	bootstrap.Args
-	RunGoTests  bool
-	OutDir      string
-	SoongOutDir string
+	RunGoTests     bool
+	OutDir         string
+	SoongOutDir    string
+	SoongVariables string
 
 	SymlinkForestMarker string
 	Bp2buildMarker      string
@@ -102,6 +103,8 @@
 	UseBazelProxy bool
 
 	BuildFromTextStub bool
+
+	EnsureAllowlistIntegrity bool
 }
 
 // Build modes that soong_build can run as.
@@ -174,6 +177,23 @@
 	return c.config.TestProductVariables != nil
 }
 
+// MaxPageSizeSupported returns the max page size supported by the device. This
+// value will define the ELF segment alignment for binaries (executables and
+// shared libraries).
+func (c Config) MaxPageSizeSupported() string {
+	return String(c.config.productVariables.DeviceMaxPageSizeSupported)
+}
+
+// The release version passed to aconfig, derived from RELEASE_VERSION
+func (c Config) ReleaseVersion() string {
+	return c.config.productVariables.ReleaseVersion
+}
+
+// The flag values files passed to aconfig, derived from RELEASE_VERSION
+func (c Config) ReleaseDeviceConfigValueSets() []string {
+	return c.config.productVariables.ReleaseDeviceConfigValueSets
+}
+
 // A DeviceConfig object represents the configuration for a particular device
 // being built. For now there will only be one of these, but in the future there
 // may be multiple devices being built.
@@ -271,6 +291,10 @@
 	// "--bazel-force-enabled-modules"
 	bazelForceEnabledModules map[string]struct{}
 
+	// Names of Bazel targets as defined by BUILD files in the source tree,
+	// keyed by the directory in which they are defined.
+	bazelTargetsByDir map[string][]string
+
 	// If true, for any requests to Bazel, communicate with a Bazel proxy using
 	// unix sockets, instead of spawning Bazel as a subprocess.
 	UseBazelProxy bool
@@ -278,6 +302,14 @@
 	// If buildFromTextStub is true then the Java API stubs are
 	// built from the signature text files, not the source Java files.
 	buildFromTextStub bool
+
+	// If ensureAllowlistIntegrity is true, then the presence of any allowlisted
+	// modules that aren't mixed-built for at least one variant will cause a build
+	// failure
+	ensureAllowlistIntegrity bool
+
+	// List of Api libraries that contribute to Api surfaces.
+	apiLibraries map[string]struct{}
 }
 
 type deviceConfig struct {
@@ -464,7 +496,7 @@
 func NewConfig(cmdArgs CmdArgs, availableEnv map[string]string) (Config, error) {
 	// Make a config with default options.
 	config := &config{
-		ProductVariablesFileName: filepath.Join(cmdArgs.SoongOutDir, productVariablesFileName),
+		ProductVariablesFileName: cmdArgs.SoongVariables,
 
 		env: availableEnv,
 
@@ -489,6 +521,8 @@
 		config: config,
 	}
 
+	config.productVariables.Build_from_text_stub = boolPtr(config.buildFromTextStub)
+
 	// Soundness check of the build and source directories. This won't catch strange
 	// configurations with symlinks, but at least checks the obvious case.
 	absBuildDir, err := filepath.Abs(cmdArgs.SoongOutDir)
@@ -592,15 +626,49 @@
 	setBazelMode(cmdArgs.BazelMode, "--bazel-mode", BazelProdMode)
 	setBazelMode(cmdArgs.BazelModeStaging, "--bazel-mode-staging", BazelStagingMode)
 
-	for _, module := range strings.Split(cmdArgs.BazelForceEnabledModules, ",") {
+	for _, module := range getForceEnabledModulesFromFlag(cmdArgs.BazelForceEnabledModules) {
 		config.bazelForceEnabledModules[module] = struct{}{}
 	}
 	config.BazelContext, err = NewBazelContext(config)
 	config.Bp2buildPackageConfig = GetBp2BuildAllowList()
 
+	// TODO(b/276958307): Replace the hardcoded list to a sdk_library local prop.
+	config.apiLibraries = map[string]struct{}{
+		"android.net.ipsec.ike":             {},
+		"art.module.public.api":             {},
+		"conscrypt.module.public.api":       {},
+		"framework-adservices":              {},
+		"framework-appsearch":               {},
+		"framework-bluetooth":               {},
+		"framework-connectivity":            {},
+		"framework-connectivity-t":          {},
+		"framework-graphics":                {},
+		"framework-media":                   {},
+		"framework-mediaprovider":           {},
+		"framework-ondevicepersonalization": {},
+		"framework-permission":              {},
+		"framework-permission-s":            {},
+		"framework-scheduling":              {},
+		"framework-sdkextensions":           {},
+		"framework-statsd":                  {},
+		"framework-sdksandbox":              {},
+		"framework-tethering":               {},
+		"framework-uwb":                     {},
+		"framework-virtualization":          {},
+		"framework-wifi":                    {},
+		"i18n.module.public.api":            {},
+	}
+
 	return Config{config}, err
 }
 
+func getForceEnabledModulesFromFlag(forceEnabledFlag string) []string {
+	if forceEnabledFlag == "" {
+		return []string{}
+	}
+	return strings.Split(forceEnabledFlag, ",")
+}
+
 // mockFileSystem replaces all reads with accesses to the provided map of
 // filenames to contents stored as a byte slice.
 func (c *config) mockFileSystem(bp string, fs map[string][]byte) {
@@ -695,6 +763,14 @@
 	return path
 }
 
+func (c *config) HostCcSharedLibPath(ctx PathContext, lib string) Path {
+	libDir := "lib"
+	if ctx.Config().BuildArch.Multilib == "lib64" {
+		libDir = "lib64"
+	}
+	return pathForInstall(ctx, ctx.Config().BuildOS, ctx.Config().BuildArch, libDir, false, lib+".so")
+}
+
 // PrebuiltOS returns the name of the host OS used in prebuilts directories.
 func (c *config) PrebuiltOS() string {
 	switch runtime.GOOS {
@@ -1557,6 +1633,13 @@
 	return HasAnyPrefix(path, c.productVariables.MemtagHeapSyncIncludePaths) && !c.MemtagHeapDisabledForPath(path)
 }
 
+func (c *config) HWASanEnabledForPath(path string) bool {
+	if len(c.productVariables.HWASanIncludePaths) == 0 {
+		return false
+	}
+	return HasAnyPrefix(path, c.productVariables.HWASanIncludePaths)
+}
+
 func (c *config) VendorConfig(name string) VendorConfig {
 	return soongconfig.Config(c.productVariables.VendorVars[name])
 }
@@ -1633,10 +1716,6 @@
 	return c.productVariables.ProductPrivateSepolicyDirs
 }
 
-func (c *config) MissingUsesLibraries() []string {
-	return c.productVariables.MissingUsesLibraries
-}
-
 func (c *config) TargetMultitreeUpdateMeta() bool {
 	return c.productVariables.MultitreeUpdateMeta
 }
@@ -1812,6 +1891,10 @@
 	return uncheckedFinalApiLevel(apiLevel)
 }
 
+func (c *deviceConfig) BuildBrokenPluginValidation() []string {
+	return c.config.productVariables.BuildBrokenPluginValidation
+}
+
 func (c *deviceConfig) BuildBrokenClangAsFlags() bool {
 	return c.config.productVariables.BuildBrokenClangAsFlags
 }
@@ -1848,8 +1931,12 @@
 	return InList(name, c.config.productVariables.BuildBrokenInputDirModules)
 }
 
-func (c *deviceConfig) BuildBrokenDepfile() bool {
-	return Bool(c.config.productVariables.BuildBrokenDepfile)
+func (c *config) BuildWarningBadOptionalUsesLibsAllowlist() []string {
+	return c.productVariables.BuildWarningBadOptionalUsesLibsAllowlist
+}
+
+func (c *deviceConfig) GenruleSandboxing() bool {
+	return Bool(c.config.productVariables.GenruleSandboxing)
 }
 
 func (c *deviceConfig) RequiresInsecureExecmemForSwiftshader() bool {
@@ -1860,10 +1947,6 @@
 	return c.productVariables.SelinuxIgnoreNeverallows
 }
 
-func (c *deviceConfig) SepolicySplit() bool {
-	return c.config.productVariables.SepolicySplit
-}
-
 func (c *deviceConfig) SepolicyFreezeTestExtraDirs() []string {
 	return c.config.productVariables.SepolicyFreezeTestExtraDirs
 }
@@ -1904,6 +1987,14 @@
 	return Bool(c.productVariables.HostMusl)
 }
 
+func (c *config) GetMixedBuildsEnabledModules() map[string]struct{} {
+	return c.mixedBuildEnabledModules
+}
+
+func (c *config) GetMixedBuildsDisabledModules() map[string]struct{} {
+	return c.mixedBuildDisabledModules
+}
+
 func (c *config) LogMixedBuild(ctx BaseModuleContext, useBazel bool) {
 	moduleName := ctx.Module().Name()
 	c.mixedBuildsLock.Lock()
@@ -1915,6 +2006,20 @@
 	}
 }
 
+func (c *config) HasBazelBuildTargetInSource(ctx BaseModuleContext) bool {
+	moduleName := ctx.Module().Name()
+	for _, buildTarget := range c.bazelTargetsByDir[ctx.ModuleDir()] {
+		if moduleName == buildTarget {
+			return true
+		}
+	}
+	return false
+}
+
+func (c *config) SetBazelBuildFileTargets(bazelTargetsByDir map[string][]string) {
+	c.bazelTargetsByDir = bazelTargetsByDir
+}
+
 // ApiSurfaces directory returns the source path inside the api_surfaces repo
 // (relative to workspace root).
 func (c *config) ApiSurfacesDir(s ApiSurface, version string) string {
@@ -1932,9 +2037,22 @@
 
 func (c *config) SetBuildFromTextStub(b bool) {
 	c.buildFromTextStub = b
+	c.productVariables.Build_from_text_stub = boolPtr(b)
 }
+
 func (c *config) AddForceEnabledModules(forceEnabled []string) {
 	for _, forceEnabledModule := range forceEnabled {
 		c.bazelForceEnabledModules[forceEnabledModule] = struct{}{}
 	}
 }
+
+func (c *config) SetApiLibraries(libs []string) {
+	c.apiLibraries = make(map[string]struct{})
+	for _, lib := range libs {
+		c.apiLibraries[lib] = struct{}{}
+	}
+}
+
+func (c *config) GetApiLibraries() map[string]struct{} {
+	return c.apiLibraries
+}
diff --git a/android/config_bp2build.go b/android/config_bp2build.go
index 830890d..2beeb51 100644
--- a/android/config_bp2build.go
+++ b/android/config_bp2build.go
@@ -95,15 +95,6 @@
 	return ev.pctx.VariableConfigMethod(name, method)
 }
 
-func (ev ExportedVariables) ExportStringStaticVariableWithEnvOverride(name, envVar, defaultVal string) {
-	ev.ExportVariableConfigMethod(name, func(config Config) string {
-		if override := config.Getenv(envVar); override != "" {
-			return override
-		}
-		return defaultVal
-	})
-}
-
 // ExportSourcePathVariable declares a static "source path" variable and exports
 // it to Bazel's toolchain.
 func (ev ExportedVariables) ExportSourcePathVariable(name string, value string) {
diff --git a/android/deptag.go b/android/deptag.go
index be5c35c..a15443b 100644
--- a/android/deptag.go
+++ b/android/deptag.go
@@ -34,10 +34,10 @@
 
 var _ InstallNeededDependencyTag = InstallAlwaysNeededDependencyTag{}
 
-// IsInstallDepNeeded returns true if the dependency tag implements the InstallNeededDependencyTag
+// IsInstallDepNeededTag returns true if the dependency tag implements the InstallNeededDependencyTag
 // interface and the InstallDepNeeded returns true, meaning that the installed files of the parent
 // should depend on the installed files of the child.
-func IsInstallDepNeeded(tag blueprint.DependencyTag) bool {
+func IsInstallDepNeededTag(tag blueprint.DependencyTag) bool {
 	if i, ok := tag.(InstallNeededDependencyTag); ok {
 		return i.InstallDepNeeded()
 	}
diff --git a/android/filegroup.go b/android/filegroup.go
index c259f21..3522f80 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -75,7 +75,8 @@
 
 // https://docs.bazel.build/versions/master/be/general.html#filegroup
 type bazelFilegroupAttributes struct {
-	Srcs bazel.LabelListAttribute
+	Srcs                bazel.LabelListAttribute
+	Applicable_licenses bazel.LabelListAttribute
 }
 
 type bazelAidlLibraryAttributes struct {
@@ -172,6 +173,17 @@
 	}
 }
 
+type FileGroupPath interface {
+	GetPath(ctx TopDownMutatorContext) string
+}
+
+func (fg *fileGroup) GetPath(ctx TopDownMutatorContext) string {
+	if fg.properties.Path != nil {
+		return *fg.properties.Path
+	}
+	return ""
+}
+
 type fileGroupProperties struct {
 	// srcs lists files that will be included in this filegroup
 	Srcs []string `android:"path"`
@@ -194,6 +206,7 @@
 	BazelModuleBase
 	DefaultableModuleBase
 	FileGroupAsLibrary
+	FileGroupPath
 	properties fileGroupProperties
 	srcs       Paths
 }
@@ -201,6 +214,7 @@
 var _ MixedBuildBuildable = (*fileGroup)(nil)
 var _ SourceFileProducer = (*fileGroup)(nil)
 var _ FileGroupAsLibrary = (*fileGroup)(nil)
+var _ FileGroupPath = (*fileGroup)(nil)
 
 // filegroup contains a list of files that are referenced by other modules
 // properties (such as "srcs") using the syntax ":<name>". filegroup are
diff --git a/android/gen_notice.go b/android/gen_notice.go
index 091345b..1acc638 100644
--- a/android/gen_notice.go
+++ b/android/gen_notice.go
@@ -28,7 +28,7 @@
 
 // Register the gen_notice module type.
 func RegisterGenNoticeBuildComponents(ctx RegistrationContext) {
-	ctx.RegisterSingletonType("gen_notice_build_rules", GenNoticeBuildRulesFactory)
+	ctx.RegisterParallelSingletonType("gen_notice_build_rules", GenNoticeBuildRulesFactory)
 	ctx.RegisterModuleType("gen_notice", GenNoticeFactory)
 }
 
diff --git a/android/license_metadata.go b/android/license_metadata.go
index 18b63d3..73000a9 100644
--- a/android/license_metadata.go
+++ b/android/license_metadata.go
@@ -74,7 +74,7 @@
 		if ctx.OtherModuleHasProvider(dep, LicenseMetadataProvider) {
 			info := ctx.OtherModuleProvider(dep, LicenseMetadataProvider).(*LicenseMetadataInfo)
 			allDepMetadataFiles = append(allDepMetadataFiles, info.LicenseMetadataPath)
-			if isContainer || IsInstallDepNeeded(ctx.OtherModuleDependencyTag(dep)) {
+			if isContainer || isInstallDepNeeded(dep, ctx.OtherModuleDependencyTag(dep)) {
 				allDepMetadataDepSets = append(allDepMetadataDepSets, info.LicenseMetadataDepSet)
 			}
 
diff --git a/android/makefile_goal.go b/android/makefile_goal.go
deleted file mode 100644
index 07354a6..0000000
--- a/android/makefile_goal.go
+++ /dev/null
@@ -1,98 +0,0 @@
-// Copyright 2020 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package android
-
-import (
-	"fmt"
-	"io"
-	"path/filepath"
-
-	"github.com/google/blueprint/proptools"
-)
-
-func init() {
-	RegisterModuleType("makefile_goal", MakefileGoalFactory)
-}
-
-type makefileGoalProperties struct {
-	// Sources.
-
-	// Makefile goal output file path, relative to PRODUCT_OUT.
-	Product_out_path *string
-}
-
-type makefileGoal struct {
-	ModuleBase
-
-	properties makefileGoalProperties
-
-	// Destination. Output file path of this module.
-	outputFilePath OutputPath
-}
-
-var _ AndroidMkEntriesProvider = (*makefileGoal)(nil)
-var _ OutputFileProducer = (*makefileGoal)(nil)
-
-// Input file of this makefile_goal module. Nil if none specified. May use variable names in makefiles.
-func (p *makefileGoal) inputPath() *string {
-	if p.properties.Product_out_path != nil {
-		return proptools.StringPtr(filepath.Join("$(PRODUCT_OUT)", proptools.String(p.properties.Product_out_path)))
-	}
-	return nil
-}
-
-// OutputFileProducer
-func (p *makefileGoal) OutputFiles(tag string) (Paths, error) {
-	if tag != "" {
-		return nil, fmt.Errorf("unsupported tag %q", tag)
-	}
-	return Paths{p.outputFilePath}, nil
-}
-
-// AndroidMkEntriesProvider
-func (p *makefileGoal) DepsMutator(ctx BottomUpMutatorContext) {
-	if p.inputPath() == nil {
-		ctx.PropertyErrorf("product_out_path", "Path relative to PRODUCT_OUT required")
-	}
-}
-
-func (p *makefileGoal) GenerateAndroidBuildActions(ctx ModuleContext) {
-	filename := filepath.Base(proptools.String(p.inputPath()))
-	p.outputFilePath = PathForModuleOut(ctx, filename).OutputPath
-
-	ctx.InstallFile(PathForModuleInstall(ctx, "etc"), ctx.ModuleName(), p.outputFilePath)
-}
-
-func (p *makefileGoal) AndroidMkEntries() []AndroidMkEntries {
-	return []AndroidMkEntries{AndroidMkEntries{
-		Class:      "ETC",
-		OutputFile: OptionalPathForPath(p.outputFilePath),
-		ExtraFooters: []AndroidMkExtraFootersFunc{
-			func(w io.Writer, name, prefix, moduleDir string) {
-				// Can't use Cp because inputPath() is not a valid Path.
-				fmt.Fprintf(w, "$(eval $(call copy-one-file,%s,%s))\n", proptools.String(p.inputPath()), p.outputFilePath)
-			},
-		},
-	}}
-}
-
-// Import a Makefile goal to Soong by copying the file built by
-// the goal to a path visible to Soong. This rule only works on boot images.
-func MakefileGoalFactory() Module {
-	module := &makefileGoal{}
-	module.AddProperties(&module.properties)
-	InitAndroidModule(module)
-	return module
-}
diff --git a/android/metrics.go b/android/metrics.go
index 3d41a1d..63c72cd 100644
--- a/android/metrics.go
+++ b/android/metrics.go
@@ -42,7 +42,7 @@
 }
 
 func init() {
-	RegisterSingletonType("soong_metrics", soongMetricsSingletonFactory)
+	RegisterParallelSingletonType("soong_metrics", soongMetricsSingletonFactory)
 }
 
 func soongMetricsSingletonFactory() Singleton { return soongMetricsSingleton{} }
diff --git a/android/module.go b/android/module.go
index ba47453..ba32710 100644
--- a/android/module.go
+++ b/android/module.go
@@ -15,6 +15,9 @@
 package android
 
 import (
+	"crypto/md5"
+	"encoding/hex"
+	"encoding/json"
 	"fmt"
 	"net/url"
 	"os"
@@ -354,6 +357,10 @@
 
 	AddMissingDependencies(missingDeps []string)
 
+	// getMissingDependencies returns the list of missing dependencies.
+	// Calling this function prevents adding new dependencies.
+	getMissingDependencies() []string
+
 	// AddUnconvertedBp2buildDep stores module name of a direct dependency that was not converted via bp2build
 	AddUnconvertedBp2buildDep(dep string)
 
@@ -710,6 +717,31 @@
 	return l[:k+1]
 }
 
+// soongConfigTrace holds all references to VendorVars. Uses []string for blueprint:"mutated"
+type soongConfigTrace struct {
+	Bools   []string `json:",omitempty"`
+	Strings []string `json:",omitempty"`
+	IsSets  []string `json:",omitempty"`
+}
+
+func (c *soongConfigTrace) isEmpty() bool {
+	return len(c.Bools) == 0 && len(c.Strings) == 0 && len(c.IsSets) == 0
+}
+
+// Returns hash of serialized trace records (empty string if there's no trace recorded)
+func (c *soongConfigTrace) hash() string {
+	// Use MD5 for speed. We don't care collision or preimage attack
+	if c.isEmpty() {
+		return ""
+	}
+	j, err := json.Marshal(c)
+	if err != nil {
+		panic(fmt.Errorf("json marshal of %#v failed: %#v", *c, err))
+	}
+	hash := md5.Sum(j)
+	return hex.EncodeToString(hash[:])
+}
+
 type nameProperties struct {
 	// The name of the module.  Must be unique across all modules.
 	Name *string
@@ -925,6 +957,12 @@
 	// and don't create a rule to install the file.
 	SkipInstall bool `blueprint:"mutated"`
 
+	// UninstallableApexPlatformVariant is set by MakeUninstallable called by the apex
+	// mutator.  MakeUninstallable also sets HideFromMake.  UninstallableApexPlatformVariant
+	// is used to avoid adding install or packaging dependencies into libraries provided
+	// by apexes.
+	UninstallableApexPlatformVariant bool `blueprint:"mutated"`
+
 	// Whether the module has been replaced by a prebuilt
 	ReplacedByPrebuilt bool `blueprint:"mutated"`
 
@@ -933,7 +971,8 @@
 
 	NamespaceExportedToMake bool `blueprint:"mutated"`
 
-	MissingDeps []string `blueprint:"mutated"`
+	MissingDeps        []string `blueprint:"mutated"`
+	CheckedMissingDeps bool     `blueprint:"mutated"`
 
 	// Name and variant strings stored by mutators to enable Module.String()
 	DebugName       string   `blueprint:"mutated"`
@@ -947,6 +986,10 @@
 
 	// Bazel conversion status
 	BazelConversionStatus BazelConversionStatus `blueprint:"mutated"`
+
+	// SoongConfigTrace records accesses to VendorVars (soong_config)
+	SoongConfigTrace     soongConfigTrace `blueprint:"mutated"`
+	SoongConfigTraceHash string           `blueprint:"mutated"`
 }
 
 // CommonAttributes represents the common Bazel attributes from which properties
@@ -2009,6 +2052,7 @@
 // have other side effects, in particular when it adds a NOTICE file target,
 // which other install targets might depend on.
 func (m *ModuleBase) MakeUninstallable() {
+	m.commonProperties.UninstallableApexPlatformVariant = true
 	m.HideFromMake()
 }
 
@@ -2038,13 +2082,19 @@
 }
 
 // computeInstallDeps finds the installed paths of all dependencies that have a dependency
-// tag that is annotated as needing installation via the IsInstallDepNeeded method.
+// tag that is annotated as needing installation via the isInstallDepNeeded method.
 func (m *ModuleBase) computeInstallDeps(ctx ModuleContext) ([]*installPathsDepSet, []*packagingSpecsDepSet) {
 	var installDeps []*installPathsDepSet
 	var packagingSpecs []*packagingSpecsDepSet
 	ctx.VisitDirectDeps(func(dep Module) {
-		if IsInstallDepNeeded(ctx.OtherModuleDependencyTag(dep)) && !dep.IsHideFromMake() && !dep.IsSkipInstall() {
-			installDeps = append(installDeps, dep.base().installFilesDepSet)
+		if isInstallDepNeeded(dep, ctx.OtherModuleDependencyTag(dep)) {
+			// Installation is still handled by Make, so anything hidden from Make is not
+			// installable.
+			if !dep.IsHideFromMake() && !dep.IsSkipInstall() {
+				installDeps = append(installDeps, dep.base().installFilesDepSet)
+			}
+			// Add packaging deps even when the dependency is not installed so that uninstallable
+			// modules can still be packaged.  Often the package will be installed instead.
 			packagingSpecs = append(packagingSpecs, dep.base().packagingSpecsDepSet)
 		}
 	})
@@ -2052,6 +2102,17 @@
 	return installDeps, packagingSpecs
 }
 
+// isInstallDepNeeded returns true if installing the output files of the current module
+// should also install the output files of the given dependency and dependency tag.
+func isInstallDepNeeded(dep Module, tag blueprint.DependencyTag) bool {
+	// Don't add a dependency from the platform to a library provided by an apex.
+	if dep.base().commonProperties.UninstallableApexPlatformVariant {
+		return false
+	}
+	// Only install modules if the dependency tag is an InstallDepNeeded tag.
+	return IsInstallDepNeededTag(tag)
+}
+
 func (m *ModuleBase) FilesToInstall() InstallPaths {
 	return m.installFiles
 }
@@ -2438,7 +2499,7 @@
 
 func (m *ModuleBase) isHandledByBazel(ctx ModuleContext) (MixedBuildBuildable, bool) {
 	if mixedBuildMod, ok := m.module.(MixedBuildBuildable); ok {
-		if mixedBuildMod.IsMixedBuildSupported(ctx) && MixedBuildsEnabled(ctx) {
+		if mixedBuildMod.IsMixedBuildSupported(ctx) && (MixedBuildsEnabled(ctx) == MixedBuildEnabled) {
 			return mixedBuildMod, true
 		}
 	}
@@ -2838,6 +2899,20 @@
 	}
 }
 
+func (b *baseModuleContext) checkedMissingDeps() bool {
+	return b.Module().base().commonProperties.CheckedMissingDeps
+}
+
+func (b *baseModuleContext) getMissingDependencies() []string {
+	checked := &b.Module().base().commonProperties.CheckedMissingDeps
+	*checked = true
+	var missingDeps []string
+	missingDeps = append(missingDeps, b.Module().base().commonProperties.MissingDeps...)
+	missingDeps = append(missingDeps, b.bp.EarlyGetMissingDependencies()...)
+	missingDeps = FirstUniqueStrings(missingDeps)
+	return missingDeps
+}
+
 type AllowDisabledModuleDependency interface {
 	blueprint.DependencyTag
 	AllowDisabledModuleDependency(target Module) bool
@@ -3117,6 +3192,10 @@
 	return m.bp.ModuleSubDir()
 }
 
+func (m *moduleContext) ModuleSoongConfigHash() string {
+	return m.module.base().commonProperties.SoongConfigTraceHash
+}
+
 func (b *baseModuleContext) Target() Target {
 	return b.target
 }
@@ -3700,7 +3779,9 @@
 }
 
 func init() {
-	RegisterSingletonType("buildtarget", BuildTargetSingleton)
+	RegisterParallelSingletonType("buildtarget", BuildTargetSingleton)
+	RegisterParallelSingletonType("soongconfigtrace", soongConfigTraceSingletonFunc)
+	FinalDepsMutators(registerSoongConfigTraceMutator)
 }
 
 func BuildTargetSingleton() Singleton {
@@ -3882,3 +3963,104 @@
 	}
 	return d.depSet.ToList().(InstallPaths)
 }
+
+func registerSoongConfigTraceMutator(ctx RegisterMutatorsContext) {
+	ctx.BottomUp("soongconfigtrace", soongConfigTraceMutator).Parallel()
+}
+
+// soongConfigTraceMutator accumulates recorded soong_config trace from children. Also it normalizes
+// SoongConfigTrace to make it consistent.
+func soongConfigTraceMutator(ctx BottomUpMutatorContext) {
+	trace := &ctx.Module().base().commonProperties.SoongConfigTrace
+	ctx.VisitDirectDeps(func(m Module) {
+		childTrace := &m.base().commonProperties.SoongConfigTrace
+		trace.Bools = append(trace.Bools, childTrace.Bools...)
+		trace.Strings = append(trace.Strings, childTrace.Strings...)
+		trace.IsSets = append(trace.IsSets, childTrace.IsSets...)
+	})
+	trace.Bools = SortedUniqueStrings(trace.Bools)
+	trace.Strings = SortedUniqueStrings(trace.Strings)
+	trace.IsSets = SortedUniqueStrings(trace.IsSets)
+
+	ctx.Module().base().commonProperties.SoongConfigTraceHash = trace.hash()
+}
+
+// soongConfigTraceSingleton writes a map from each module's config hash value to trace data.
+func soongConfigTraceSingletonFunc() Singleton {
+	return &soongConfigTraceSingleton{}
+}
+
+type soongConfigTraceSingleton struct {
+}
+
+func (s *soongConfigTraceSingleton) GenerateBuildActions(ctx SingletonContext) {
+	outFile := PathForOutput(ctx, "soong_config_trace.json")
+
+	traces := make(map[string]*soongConfigTrace)
+	ctx.VisitAllModules(func(module Module) {
+		trace := &module.base().commonProperties.SoongConfigTrace
+		if !trace.isEmpty() {
+			hash := module.base().commonProperties.SoongConfigTraceHash
+			traces[hash] = trace
+		}
+	})
+
+	j, err := json.Marshal(traces)
+	if err != nil {
+		ctx.Errorf("json marshal to %q failed: %#v", outFile, err)
+		return
+	}
+
+	WriteFileRule(ctx, outFile, string(j))
+	ctx.Phony("soong_config_trace", outFile)
+}
+
+// Interface implemented by xsd_config which has 1:many mappings in bp2build workspace
+// This interface exists because we want to
+// 1. Determine the name of the additional targets generated by the primary soong module
+// 2. Enable distinguishing an xsd_config module from other Soong modules using type assertion
+type XsdConfigBp2buildTargets interface {
+	CppBp2buildTargetName() string
+	JavaBp2buildTargetName() string
+}
+
+// PartitionXsdSrcs partitions srcs into xsd_config modules and others
+// Since xsd_config are soong modules, we cannot use file extension for partitioning
+func PartitionXsdSrcs(ctx BazelConversionPathContext, srcs []string) ([]string, []string) {
+	//isXsd returns true if src is a soong module of type xsd_config
+	isXsd := func(src string) bool {
+		mod, exists := ctx.ModuleFromName(src)
+		if !exists {
+			return false
+		}
+		_, _isXsd := mod.(XsdConfigBp2buildTargets)
+		return _isXsd
+	}
+	nonXsd := []string{}
+	xsd := []string{}
+
+	for _, src := range srcs {
+		if isXsd(src) {
+			xsd = append(xsd, src)
+		} else {
+			nonXsd = append(nonXsd, src)
+		}
+	}
+
+	return nonXsd, xsd
+}
+
+// Replaces //a/b/my_xsd_config with //a/b/my_xsd_config-{cpp|java}
+// The new target name is provided by the `targetName` callback function
+func XsdConfigBp2buildTarget(ctx BazelConversionPathContext, mod blueprint.Module, targetName func(xsd XsdConfigBp2buildTargets) string) string {
+	xsd, isXsd := mod.(XsdConfigBp2buildTargets)
+	if !isXsd {
+		ctx.ModuleErrorf("xsdConfigJavaTarget called on %v, which is not an xsd_config", mod)
+	}
+	ret := BazelModuleLabel(ctx, mod)
+	// Remove the base module name
+	ret = strings.TrimSuffix(ret, mod.Name())
+	// Append the language specific target name
+	ret += targetName(xsd)
+	return ret
+}
diff --git a/android/mutator.go b/android/mutator.go
index 676f8a5..4185315 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -67,6 +67,8 @@
 // collateGloballyRegisteredMutators constructs the list of mutators that have been registered
 // with the InitRegistrationContext and will be used at runtime.
 func collateGloballyRegisteredMutators() sortableComponents {
+	// ensure mixed builds mutator is the last mutator
+	finalDeps = append(finalDeps, registerMixedBuildsMutator)
 	return collateRegisteredMutators(preArch, preDeps, postDeps, finalDeps)
 }
 
@@ -273,6 +275,12 @@
 	// This function can be used to create alias definitions in a directory that is different
 	// from the directory of the visited Soong module.
 	CreateBazelTargetAliasInDir(dir string, name string, actual bazel.Label)
+
+	// CreateBazelConfigSetting creates a config_setting in <dir>/BUILD.bazel
+	// build/bazel has several static config_setting(s) that are used in Bazel builds.
+	// This function can be used to createa additional config_setting(s) based on the build graph
+	// (e.g. a config_setting specific to an apex variant)
+	CreateBazelConfigSetting(csa bazel.ConfigSettingAttributes, ca CommonAttributes, dir string)
 }
 
 type topDownMutatorContext struct {
@@ -738,6 +746,23 @@
 	mod.base().addBp2buildInfo(info)
 }
 
+func (t *topDownMutatorContext) CreateBazelConfigSetting(
+	csa bazel.ConfigSettingAttributes,
+	ca CommonAttributes,
+	dir string) {
+	mod := t.Module()
+	info := bp2buildInfo{
+		Dir: dir,
+		BazelProps: bazel.BazelTargetModuleProperties{
+			Rule_class: "config_setting",
+		},
+		CommonAttrs:     ca,
+		ConstraintAttrs: constraintAttributes{},
+		Attrs:           &csa,
+	}
+	mod.base().addBp2buildInfo(info)
+}
+
 // ApexAvailableTags converts the apex_available property value of an ApexModule
 // module and returns it as a list of keyed tags.
 func ApexAvailableTags(mod Module) bazel.StringListAttribute {
@@ -747,11 +772,46 @@
 		// TODO(b/218841706): hidl_interface has the apex_available prop, but it's
 		// defined directly as a prop and not via ApexModule, so this doesn't
 		// pick those props up.
-		attr.Value = ConvertApexAvailableToTags(am.apexModuleBase().ApexAvailable())
+		apexAvailable := am.apexModuleBase().ApexAvailable()
+		// If a user does not specify apex_available in Android.bp, then soong provides a default.
+		// To avoid verbosity of BUILD files, remove this default from user-facing BUILD files.
+		if len(am.apexModuleBase().ApexProperties.Apex_available) == 0 {
+			apexAvailable = []string{}
+		}
+		attr.Value = ConvertApexAvailableToTags(apexAvailable)
 	}
 	return attr
 }
 
+func ApexAvailableTagsWithoutTestApexes(ctx BaseModuleContext, mod Module) bazel.StringListAttribute {
+	attr := bazel.StringListAttribute{}
+	if am, ok := mod.(ApexModule); ok {
+		apexAvailableWithoutTestApexes := removeTestApexes(ctx, am.apexModuleBase().ApexAvailable())
+		// If a user does not specify apex_available in Android.bp, then soong provides a default.
+		// To avoid verbosity of BUILD files, remove this default from user-facing BUILD files.
+		if len(am.apexModuleBase().ApexProperties.Apex_available) == 0 {
+			apexAvailableWithoutTestApexes = []string{}
+		}
+		attr.Value = ConvertApexAvailableToTags(apexAvailableWithoutTestApexes)
+	}
+	return attr
+}
+
+func removeTestApexes(ctx BaseModuleContext, apex_available []string) []string {
+	testApexes := []string{}
+	for _, aa := range apex_available {
+		// ignore the wildcards
+		if InList(aa, AvailableToRecognziedWildcards) {
+			continue
+		}
+		mod, _ := ctx.ModuleFromName(aa)
+		if apex, ok := mod.(ApexTestInterface); ok && apex.IsTestApex() {
+			testApexes = append(testApexes, aa)
+		}
+	}
+	return RemoveListFromList(CopyOf(apex_available), testApexes)
+}
+
 func ConvertApexAvailableToTags(apexAvailable []string) []string {
 	if len(apexAvailable) == 0 {
 		// We need nil specifically to make bp2build not add the tags property at all,
@@ -765,6 +825,13 @@
 	return result
 }
 
+// ConvertApexAvailableToTagsWithoutTestApexes converts a list of apex names to a list of bazel tags
+// This function drops any test apexes from the input.
+func ConvertApexAvailableToTagsWithoutTestApexes(ctx BaseModuleContext, apexAvailable []string) []string {
+	noTestApexes := removeTestApexes(ctx, apexAvailable)
+	return ConvertApexAvailableToTags(noTestApexes)
+}
+
 func (t *topDownMutatorContext) createBazelTargetModule(
 	bazelProps bazel.BazelTargetModuleProperties,
 	commonAttrs CommonAttributes,
@@ -820,10 +887,16 @@
 }
 
 func (b *bottomUpMutatorContext) AddDependency(module blueprint.Module, tag blueprint.DependencyTag, name ...string) []blueprint.Module {
+	if b.baseModuleContext.checkedMissingDeps() {
+		panic("Adding deps not allowed after checking for missing deps")
+	}
 	return b.bp.AddDependency(module, tag, name...)
 }
 
 func (b *bottomUpMutatorContext) AddReverseDependency(module blueprint.Module, tag blueprint.DependencyTag, name string) {
+	if b.baseModuleContext.checkedMissingDeps() {
+		panic("Adding deps not allowed after checking for missing deps")
+	}
 	b.bp.AddReverseDependency(module, tag, name)
 }
 
@@ -873,11 +946,17 @@
 
 func (b *bottomUpMutatorContext) AddVariationDependencies(variations []blueprint.Variation, tag blueprint.DependencyTag,
 	names ...string) []blueprint.Module {
+	if b.baseModuleContext.checkedMissingDeps() {
+		panic("Adding deps not allowed after checking for missing deps")
+	}
 	return b.bp.AddVariationDependencies(variations, tag, names...)
 }
 
 func (b *bottomUpMutatorContext) AddFarVariationDependencies(variations []blueprint.Variation,
 	tag blueprint.DependencyTag, names ...string) []blueprint.Module {
+	if b.baseModuleContext.checkedMissingDeps() {
+		panic("Adding deps not allowed after checking for missing deps")
+	}
 
 	return b.bp.AddFarVariationDependencies(variations, tag, names...)
 }
@@ -887,10 +966,16 @@
 }
 
 func (b *bottomUpMutatorContext) ReplaceDependencies(name string) {
+	if b.baseModuleContext.checkedMissingDeps() {
+		panic("Adding deps not allowed after checking for missing deps")
+	}
 	b.bp.ReplaceDependencies(name)
 }
 
 func (b *bottomUpMutatorContext) ReplaceDependenciesIf(name string, predicate blueprint.ReplaceDependencyPredicate) {
+	if b.baseModuleContext.checkedMissingDeps() {
+		panic("Adding deps not allowed after checking for missing deps")
+	}
 	b.bp.ReplaceDependenciesIf(name, predicate)
 }
 
diff --git a/android/neverallow.go b/android/neverallow.go
index 2139c3c..f2e8c85 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -55,11 +55,11 @@
 	AddNeverAllowRules(createJavaDeviceForHostRules()...)
 	AddNeverAllowRules(createCcSdkVariantRules()...)
 	AddNeverAllowRules(createUncompressDexRules()...)
-	AddNeverAllowRules(createMakefileGoalRules()...)
 	AddNeverAllowRules(createInitFirstStageRules()...)
 	AddNeverAllowRules(createProhibitFrameworkAccessRules()...)
 	AddNeverAllowRules(createBp2BuildRule())
 	AddNeverAllowRules(createCcStubsRule())
+	AddNeverAllowRules(createJavaExcludeStaticLibsRule())
 }
 
 // Add a NeverAllow rule to the set of rules to apply.
@@ -236,20 +236,6 @@
 	}
 }
 
-func createMakefileGoalRules() []Rule {
-	allowlist := []string{
-		// libwifi_hal uses makefile_goal for its dependencies
-		"frameworks/opt/net/wifi/libwifi_hal",
-	}
-	return []Rule{
-		NeverAllow().
-			ModuleType("makefile_goal").
-			WithoutMatcher("product_out_path", Regexp("^boot[0-9a-zA-Z.-]*[.]img$")).
-			NotIn(allowlist...).
-			Because("Only boot images may be imported as a makefile goal if not in allowed projects"),
-	}
-}
-
 func createInitFirstStageRules() []Rule {
 	return []Rule{
 		NeverAllow().
@@ -268,6 +254,14 @@
 	}
 }
 
+func createJavaExcludeStaticLibsRule() Rule {
+	return NeverAllow().
+		NotIn("build/soong").
+		ModuleType("java_library").
+		WithMatcher("exclude_static_libs", isSetMatcherInstance).
+		Because("exclude_static_libs property is only allowed for java modules defined in build/soong")
+}
+
 func neverallowMutator(ctx BottomUpMutatorContext) {
 	m, ok := ctx.Module().(Module)
 	if !ok {
diff --git a/android/neverallow_test.go b/android/neverallow_test.go
index 5f5f9a1..1639bbf 100644
--- a/android/neverallow_test.go
+++ b/android/neverallow_test.go
@@ -313,45 +313,6 @@
 			"module \"outside_art_libraries\": violates neverallow",
 		},
 	},
-	{
-		name: "disallowed makefile_goal",
-		fs: map[string][]byte{
-			"Android.bp": []byte(`
-				makefile_goal {
-					name: "foo",
-					product_out_path: "boot/trap.img"
-				}
-			`),
-		},
-		expectedErrors: []string{
-			"Only boot images.* may be imported as a makefile goal",
-		},
-	},
-	{
-		name: "disallowed makefile_goal outside external",
-		fs: map[string][]byte{
-			"project/Android.bp": []byte(`
-				makefile_goal {
-					name: "foo",
-					product_out_path: "obj/EXE/foo",
-				}
-			`),
-		},
-		expectedErrors: []string{
-			"not in allowed projects",
-		},
-	},
-	{
-		name: "allow makefile_goal within external",
-		fs: map[string][]byte{
-			"frameworks/opt/net/wifi/libwifi_hal/Android.bp": []byte(`
-				makefile_goal {
-					name: "foo",
-					product_out_path: "obj/EXE/foo",
-				}
-			`),
-		},
-	},
 	// Tests for the rule prohibiting the use of framework
 	{
 		name: "prohibit framework",
@@ -383,6 +344,23 @@
 			`module "outside_allowed_list": violates neverallow`,
 		},
 	},
+	// Test for the rule restricting use of exclude_static_libs
+	{
+		name: `"exclude_static_libs" outside allowed directory`,
+		fs: map[string][]byte{
+			"a/b/Android.bp": []byte(`
+				java_library {
+					name: "baz",
+					exclude_static_libs: [
+						"bar",
+					],
+				}
+			`),
+		},
+		expectedErrors: []string{
+			`exclude_static_libs property is only allowed for java modules defined in build/soong`,
+		},
+	},
 }
 
 var prepareForNeverAllowTest = GroupFixturePreparers(
@@ -391,7 +369,6 @@
 		ctx.RegisterModuleType("java_library", newMockJavaLibraryModule)
 		ctx.RegisterModuleType("java_library_host", newMockJavaLibraryModule)
 		ctx.RegisterModuleType("java_device_for_host", newMockJavaLibraryModule)
-		ctx.RegisterModuleType("makefile_goal", newMockMakefileGoalModule)
 	}),
 )
 
@@ -470,9 +447,10 @@
 }
 
 type mockJavaLibraryProperties struct {
-	Libs           []string
-	Sdk_version    *string
-	Uncompress_dex *bool
+	Libs                []string
+	Sdk_version         *string
+	Uncompress_dex      *bool
+	Exclude_static_libs []string
 }
 
 type mockJavaLibraryModule struct {
@@ -489,22 +467,3 @@
 
 func (p *mockJavaLibraryModule) GenerateAndroidBuildActions(ModuleContext) {
 }
-
-type mockMakefileGoalProperties struct {
-	Product_out_path *string
-}
-
-type mockMakefileGoalModule struct {
-	ModuleBase
-	properties mockMakefileGoalProperties
-}
-
-func newMockMakefileGoalModule() Module {
-	m := &mockMakefileGoalModule{}
-	m.AddProperties(&m.properties)
-	InitAndroidModule(m)
-	return m
-}
-
-func (p *mockMakefileGoalModule) GenerateAndroidBuildActions(ModuleContext) {
-}
diff --git a/android/ninja_deps.go b/android/ninja_deps.go
index 2f442d5..1d50a47 100644
--- a/android/ninja_deps.go
+++ b/android/ninja_deps.go
@@ -14,7 +14,10 @@
 
 package android
 
-import "sort"
+import (
+	"android/soong/starlark_import"
+	"sort"
+)
 
 func (c *config) addNinjaFileDeps(deps ...string) {
 	for _, dep := range deps {
@@ -40,4 +43,11 @@
 
 func (ninjaDepsSingleton) GenerateBuildActions(ctx SingletonContext) {
 	ctx.AddNinjaFileDeps(ctx.Config().ninjaFileDeps()...)
+
+	deps, err := starlark_import.GetNinjaDeps()
+	if err != nil {
+		ctx.Errorf("Error running starlark code: %s", err)
+	} else {
+		ctx.AddNinjaFileDeps(deps...)
+	}
 }
diff --git a/android/override_module.go b/android/override_module.go
index 2d30a85..a4b7431 100644
--- a/android/override_module.go
+++ b/android/override_module.go
@@ -28,7 +28,6 @@
 // module based on it.
 
 import (
-	"fmt"
 	"sort"
 	"sync"
 
@@ -47,8 +46,8 @@
 
 	// Internal funcs to handle interoperability between override modules and prebuilts.
 	// i.e. cases where an overriding module, too, is overridden by a prebuilt module.
-	setOverriddenByPrebuilt(overridden bool)
-	getOverriddenByPrebuilt() bool
+	setOverriddenByPrebuilt(prebuilt Module)
+	getOverriddenByPrebuilt() Module
 
 	// Directory containing the Blueprint definition of the overriding module
 	setModuleDir(string)
@@ -61,7 +60,7 @@
 
 	overridingProperties []interface{}
 
-	overriddenByPrebuilt bool
+	overriddenByPrebuilt Module
 
 	moduleDir string
 }
@@ -97,11 +96,11 @@
 	return proptools.String(o.moduleProperties.Base)
 }
 
-func (o *OverrideModuleBase) setOverriddenByPrebuilt(overridden bool) {
-	o.overriddenByPrebuilt = overridden
+func (o *OverrideModuleBase) setOverriddenByPrebuilt(prebuilt Module) {
+	o.overriddenByPrebuilt = prebuilt
 }
 
-func (o *OverrideModuleBase) getOverriddenByPrebuilt() bool {
+func (o *OverrideModuleBase) getOverriddenByPrebuilt() Module {
 	return o.overriddenByPrebuilt
 }
 
@@ -121,7 +120,7 @@
 	addOverride(o OverrideModule)
 	getOverrides() []OverrideModule
 
-	override(ctx BaseModuleContext, o OverrideModule)
+	override(ctx BaseModuleContext, m Module, o OverrideModule)
 	GetOverriddenBy() string
 	GetOverriddenByModuleDir() string
 
@@ -192,7 +191,8 @@
 }
 
 // Overrides a base module with the given OverrideModule.
-func (b *OverridableModuleBase) override(ctx BaseModuleContext, o OverrideModule) {
+func (b *OverridableModuleBase) override(ctx BaseModuleContext, m Module, o OverrideModule) {
+
 	for _, p := range b.overridableProperties {
 		for _, op := range o.getOverridingProperties() {
 			if proptools.TypeEqual(p, op) {
@@ -214,6 +214,17 @@
 	}
 	b.overridableModuleProperties.OverriddenBy = o.Name()
 	b.overridableModuleProperties.OverriddenByModuleDir = o.ModuleDir()
+
+	if oBazelable, ok := o.base().module.(Bazelable); ok {
+		if bBazelable, ok := m.(Bazelable); ok {
+			oProps := oBazelable.bazelProps()
+			bProps := bBazelable.bazelProps()
+			bProps.Bazel_module.Bp2build_available = oProps.Bazel_module.Bp2build_available
+			bProps.Bazel_module.Label = oProps.Bazel_module.Label
+		} else {
+			ctx.ModuleErrorf("Override type cannot be Bazelable if original module type is not Bazelable %v %v.", o.Name(), m.Name())
+		}
+	}
 }
 
 // GetOverriddenBy returns the name of the override module that has overridden this module.
@@ -270,7 +281,7 @@
 				panic("PrebuiltDepTag leads to a non-prebuilt module " + dep.Name())
 			}
 			if prebuilt.UsePrebuilt() {
-				module.setOverriddenByPrebuilt(true)
+				module.setOverriddenByPrebuilt(dep)
 				return
 			}
 		})
@@ -302,10 +313,13 @@
 		// is specified.
 		ctx.AliasVariation(variants[0])
 		for i, o := range overrides {
-			mods[i+1].(OverridableModule).override(ctx, o)
-			if o.getOverriddenByPrebuilt() {
-				// The overriding module itself, too, is overridden by a prebuilt. Skip its installation.
-				mods[i+1].HideFromMake()
+			mods[i+1].(OverridableModule).override(ctx, mods[i+1], o)
+			if prebuilt := o.getOverriddenByPrebuilt(); prebuilt != nil {
+				// The overriding module itself, too, is overridden by a prebuilt.
+				// Perform the same check for replacement
+				checkInvariantsForSourceAndPrebuilt(ctx, mods[i+1], prebuilt)
+				// Copy the flag and hide it in make
+				mods[i+1].ReplacedByPrebuilt()
 			}
 		}
 	} else if o, ok := ctx.Module().(OverrideModule); ok {
@@ -339,34 +353,26 @@
 // variant of this OverridableModule, or ctx.ModuleName() if this module is not an OverridableModule
 // or if this variant is not overridden.
 func ModuleNameWithPossibleOverride(ctx BazelConversionContext) string {
-	if overridable, ok := ctx.Module().(OverridableModule); ok {
+	return moduleNameWithPossibleOverride(ctx, ctx.Module())
+}
+
+func moduleNameWithPossibleOverride(ctx bazelOtherModuleContext, module blueprint.Module) string {
+	if overridable, ok := module.(OverridableModule); ok {
 		if o := overridable.GetOverriddenBy(); o != "" {
 			return o
 		}
 	}
-	return ctx.OtherModuleName(ctx.Module())
+	return ctx.OtherModuleName(module)
 }
 
-// ModuleDirWithPossibleOverride returns the dir of the OverrideModule that overrides the current
-// variant of this OverridableModule, or ctx.ModuleName() if this module is not an OverridableModule
-// or if this variant is not overridden.
-func moduleDirWithPossibleOverride(ctx BazelConversionContext) string {
-	if overridable, ok := ctx.Module().(OverridableModule); ok {
+// moduleDirWithPossibleOverride returns the dir of the OverrideModule that overrides the current
+// variant of the given OverridableModule, or ctx.OtherModuleName() if the module is not an
+// OverridableModule or if the variant is not overridden.
+func moduleDirWithPossibleOverride(ctx bazelOtherModuleContext, module blueprint.Module) string {
+	if overridable, ok := module.(OverridableModule); ok {
 		if o := overridable.GetOverriddenByModuleDir(); o != "" {
 			return o
 		}
 	}
-	return ctx.OtherModuleDir(ctx.Module())
-}
-
-// MaybeBp2buildLabelOfOverridingModule returns the bazel label of the
-// overriding module of an OverridableModule (e.g. override_apex label of a base
-// apex), or the module's label itself if not overridden.
-func MaybeBp2buildLabelOfOverridingModule(ctx BazelConversionContext) string {
-	moduleName := ModuleNameWithPossibleOverride(ctx)
-	moduleDir := moduleDirWithPossibleOverride(ctx)
-	if moduleDir == Bp2BuildTopLevel {
-		moduleDir = ""
-	}
-	return fmt.Sprintf("//%s:%s", moduleDir, moduleName)
+	return ctx.OtherModuleDir(module)
 }
diff --git a/android/package.go b/android/package.go
index 2bf6521..7fbc700 100644
--- a/android/package.go
+++ b/android/package.go
@@ -15,6 +15,8 @@
 package android
 
 import (
+	"path/filepath"
+
 	"android/soong/bazel"
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
@@ -39,8 +41,8 @@
 }
 
 type bazelPackageAttributes struct {
-	Default_visibility          []string
-	Default_applicable_licenses bazel.LabelListAttribute
+	Default_visibility       []string
+	Default_package_metadata bazel.LabelListAttribute
 }
 
 type packageModule struct {
@@ -53,13 +55,32 @@
 var _ Bazelable = &packageModule{}
 
 func (p *packageModule) ConvertWithBp2build(ctx TopDownMutatorContext) {
+	defaultPackageMetadata := bazel.MakeLabelListAttribute(BazelLabelForModuleDeps(ctx, p.properties.Default_applicable_licenses))
+	// If METADATA file exists in the package, add it to package(default_package_metadata=) using a
+	// filegroup(name="default_metadata_file") which can be accessed later on each module in Bazel
+	// using attribute "applicable_licenses".
+	// Attribute applicable_licenses of filegroup "default_metadata_file" has to be set to [],
+	// otherwise Bazel reports cyclic reference error.
+	if existed, _, _ := ctx.Config().fs.Exists(filepath.Join(ctx.ModuleDir(), "METADATA")); existed {
+		ctx.CreateBazelTargetModule(
+			bazel.BazelTargetModuleProperties{
+				Rule_class: "filegroup",
+			},
+			CommonAttributes{Name: "default_metadata_file"},
+			&bazelFilegroupAttributes{
+				Srcs:                bazel.MakeLabelListAttribute(BazelLabelForModuleSrc(ctx, []string{"METADATA"})),
+				Applicable_licenses: bazel.LabelListAttribute{Value: bazel.LabelList{Includes: []bazel.Label{}}, EmitEmptyList: true},
+			})
+		defaultPackageMetadata.Value.Add(&bazel.Label{Label: ":default_metadata_file"})
+	}
+
 	ctx.CreateBazelTargetModule(
 		bazel.BazelTargetModuleProperties{
 			Rule_class: "package",
 		},
 		CommonAttributes{},
 		&bazelPackageAttributes{
-			Default_applicable_licenses: bazel.MakeLabelListAttribute(BazelLabelForModuleDeps(ctx, p.properties.Default_applicable_licenses)),
+			Default_package_metadata: defaultPackageMetadata,
 			// FIXME(asmundak): once b/221436821 is resolved
 			Default_visibility: []string{"//visibility:public"},
 		})
diff --git a/android/packaging_test.go b/android/packaging_test.go
index 91ac1f3..3833437 100644
--- a/android/packaging_test.go
+++ b/android/packaging_test.go
@@ -373,7 +373,7 @@
 
 func TestPackagingWithSkipInstallDeps(t *testing.T) {
 	// package -[dep]-> foo -[dep]-> bar      -[dep]-> baz
-	//                  OK           SKIPPED
+	// Packaging should continue transitively through modules that are not installed.
 	multiTarget := false
 	runPackagingTest(t, multiTarget,
 		`
@@ -396,5 +396,5 @@
 			name: "package",
 			deps: ["foo"],
 		}
-		`, []string{"lib64/foo"})
+		`, []string{"lib64/foo", "lib64/bar", "lib64/baz"})
 }
diff --git a/android/paths.go b/android/paths.go
index eaa6a8d..0f3d972 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -1475,7 +1475,11 @@
 }
 
 func pathForModuleOut(ctx ModuleOutPathContext) OutputPath {
-	return PathForOutput(ctx, ".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir())
+	soongConfigHash := ""
+	if i, ok := ctx.(interface{ ModuleSoongConfigHash() string }); ok {
+		soongConfigHash = i.ModuleSoongConfigHash()
+	}
+	return PathForOutput(ctx, ".intermediates", ctx.ModuleDir(), ctx.ModuleName(), ctx.ModuleSubDir(), soongConfigHash)
 }
 
 // PathForModuleOut returns a Path representing the paths... under the module's
diff --git a/android/plugin.go b/android/plugin.go
new file mode 100644
index 0000000..4672453
--- /dev/null
+++ b/android/plugin.go
@@ -0,0 +1,140 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"strings"
+
+	"github.com/google/blueprint"
+)
+
+func init() {
+	RegisterPluginSingletonBuildComponents(InitRegistrationContext)
+}
+
+func RegisterPluginSingletonBuildComponents(ctx RegistrationContext) {
+	ctx.RegisterParallelSingletonType("plugins", pluginSingletonFactory)
+}
+
+// pluginSingleton is a singleton to handle allowlisting of the final Android-<product_name>.mk file
+// output.
+func pluginSingletonFactory() Singleton {
+	return &pluginSingleton{}
+}
+
+type pluginSingleton struct{}
+
+var allowedPluginsByName = map[string]bool{
+	"aidl-soong-rules":                       true,
+	"arm_compute_library_nn_driver":          true,
+	"cuttlefish-soong-rules":                 true,
+	"gki-soong-rules":                        true,
+	"hidl-soong-rules":                       true,
+	"kernel-config-soong-rules":              true,
+	"soong-angle-codegen":                    true,
+	"soong-api":                              true,
+	"soong-art":                              true,
+	"soong-ca-certificates":                  true,
+	"soong-ca-certificates-apex":             true,
+	"soong-clang":                            true,
+	"soong-clang-prebuilts":                  true,
+	"soong-csuite":                           true,
+	"soong-fluoride":                         true,
+	"soong-fs_config":                        true,
+	"soong-icu":                              true,
+	"soong-java-config-error_prone":          true,
+	"soong-libchrome":                        true,
+	"soong-llvm":                             true,
+	"soong-robolectric":                      true,
+	"soong-rust-prebuilts":                   true,
+	"soong-selinux":                          true,
+	"soong-wayland-protocol-codegen":         true,
+	"treble_report_app":                      true,
+	"treble_report_local":                    true,
+	"treble_report_module":                   true,
+	"vintf-compatibility-matrix-soong-rules": true,
+	"xsdc-soong-rules":                       true,
+}
+
+const (
+	internalPluginsPath = "vendor/google/build/soong/internal_plugins.json"
+)
+
+type pluginProvider interface {
+	IsPluginFor(string) bool
+}
+
+func maybeAddInternalPluginsToAllowlist(ctx SingletonContext) {
+	if path := ExistentPathForSource(ctx, internalPluginsPath); path.Valid() {
+		ctx.AddNinjaFileDeps(path.String())
+		absPath := absolutePath(path.String())
+		var moreAllowed map[string]bool
+		data, err := ioutil.ReadFile(absPath)
+		if err != nil {
+			ctx.Errorf("Failed to open internal plugins path %q %q", internalPluginsPath, err)
+		}
+		if err := json.Unmarshal(data, &moreAllowed); err != nil {
+			fmt.Fprintf(os.Stderr, "Internal plugins file %q did not parse correctly: %q", data, err)
+		}
+		for k, v := range moreAllowed {
+			allowedPluginsByName[k] = v
+		}
+	}
+}
+
+func (p *pluginSingleton) GenerateBuildActions(ctx SingletonContext) {
+	for _, p := range ctx.DeviceConfig().BuildBrokenPluginValidation() {
+		allowedPluginsByName[p] = true
+	}
+	maybeAddInternalPluginsToAllowlist(ctx)
+
+	disallowedPlugins := map[string]bool{}
+	ctx.VisitAllModulesBlueprint(func(module blueprint.Module) {
+		if ctx.ModuleType(module) != "bootstrap_go_package" {
+			return
+		}
+
+		p, ok := module.(pluginProvider)
+		if !ok || !p.IsPluginFor("soong_build") {
+			return
+		}
+
+		name := ctx.ModuleName(module)
+		if _, ok := allowedPluginsByName[name]; ok {
+			return
+		}
+
+		dir := ctx.ModuleDir(module)
+
+		// allow use of plugins within Soong to not allowlist everything
+		if strings.HasPrefix(dir, "build/soong") {
+			return
+		}
+
+		// allow third party users outside of external to create new plugins, i.e. non-google paths
+		// under vendor or hardware
+		if !strings.HasPrefix(dir, "external/") && IsThirdPartyPath(dir) {
+			return
+		}
+		disallowedPlugins[name] = true
+	})
+	if len(disallowedPlugins) > 0 {
+		ctx.Errorf("New plugins are not supported; however %q were found. Please reach out to the build team or use BUILD_BROKEN_PLUGIN_VALIDATION (see Changes.md for more info).", SortedStringKeys(disallowedPlugins))
+	}
+}
diff --git a/android/prebuilt.go b/android/prebuilt.go
index 9b5c0e9..95b772d 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -419,6 +419,20 @@
 	}
 }
 
+// checkInvariantsForSourceAndPrebuilt checks if invariants are kept when replacing
+// source with prebuilt. Note that the current module for the context is the source module.
+func checkInvariantsForSourceAndPrebuilt(ctx BaseModuleContext, s, p Module) {
+	if _, ok := s.(OverrideModule); ok {
+		// skip the check when the source module is `override_X` because it's only a placeholder
+		// for the actual source module. The check will be invoked for the actual module.
+		return
+	}
+	if sourcePartition, prebuiltPartition := s.PartitionTag(ctx.DeviceConfig()), p.PartitionTag(ctx.DeviceConfig()); sourcePartition != prebuiltPartition {
+		ctx.OtherModuleErrorf(p, "partition is different: %s(%s) != %s(%s)",
+			sourcePartition, ctx.ModuleName(), prebuiltPartition, ctx.OtherModuleName(p))
+	}
+}
+
 // PrebuiltSelectModuleMutator marks prebuilts that are used, either overriding source modules or
 // because the source module doesn't exist.  It also disables installing overridden source modules.
 func PrebuiltSelectModuleMutator(ctx TopDownMutatorContext) {
@@ -434,6 +448,8 @@
 		ctx.VisitDirectDepsWithTag(PrebuiltDepTag, func(prebuiltModule Module) {
 			p := GetEmbeddedPrebuilt(prebuiltModule)
 			if p.usePrebuilt(ctx, s, prebuiltModule) {
+				checkInvariantsForSourceAndPrebuilt(ctx, s, prebuiltModule)
+
 				p.properties.UsePrebuilt = true
 				s.ReplacedByPrebuilt()
 			}
diff --git a/android/prebuilt_test.go b/android/prebuilt_test.go
index fa40d1f..fc47cfd 100644
--- a/android/prebuilt_test.go
+++ b/android/prebuilt_test.go
@@ -497,6 +497,52 @@
 	}
 }
 
+func testPrebuiltError(t *testing.T, expectedError, bp string) {
+	t.Helper()
+	fs := MockFS{
+		"prebuilt_file": nil,
+	}
+	GroupFixturePreparers(
+		PrepareForTestWithArchMutator,
+		PrepareForTestWithPrebuilts,
+		PrepareForTestWithOverrides,
+		fs.AddToFixture(),
+		FixtureRegisterWithContext(registerTestPrebuiltModules),
+	).
+		ExtendWithErrorHandler(FixtureExpectsAtLeastOneErrorMatchingPattern(expectedError)).
+		RunTestWithBp(t, bp)
+}
+
+func TestPrebuiltShouldNotChangePartition(t *testing.T) {
+	testPrebuiltError(t, `partition is different`, `
+		source {
+			name: "foo",
+			vendor: true,
+		}
+		prebuilt {
+			name: "foo",
+			prefer: true,
+			srcs: ["prebuilt_file"],
+		}`)
+}
+
+func TestPrebuiltShouldNotChangePartition_WithOverride(t *testing.T) {
+	testPrebuiltError(t, `partition is different`, `
+		source {
+			name: "foo",
+			vendor: true,
+		}
+		override_source {
+			name: "bar",
+			base: "foo",
+		}
+		prebuilt {
+			name: "bar",
+			prefer: true,
+			srcs: ["prebuilt_file"],
+		}`)
+}
+
 func registerTestPrebuiltBuildComponents(ctx RegistrationContext) {
 	registerTestPrebuiltModules(ctx)
 
diff --git a/android/proto.go b/android/proto.go
index 09e50c8..cebbd59 100644
--- a/android/proto.go
+++ b/android/proto.go
@@ -234,7 +234,7 @@
 			}
 		}
 
-		tags := ApexAvailableTags(ctx.Module())
+		tags := ApexAvailableTagsWithoutTestApexes(ctx.(TopDownMutatorContext), ctx.Module())
 
 		ctx.CreateBazelTargetModule(
 			bazel.BazelTargetModuleProperties{Rule_class: "proto_library"},
diff --git a/android/register.go b/android/register.go
index 1a3db9d..64b0207 100644
--- a/android/register.go
+++ b/android/register.go
@@ -65,16 +65,19 @@
 	// True if this should be registered as a pre-singleton, false otherwise.
 	pre bool
 
+	// True if this should be registered as a parallel singleton.
+	parallel bool
+
 	name    string
 	factory SingletonFactory
 }
 
-func newSingleton(name string, factory SingletonFactory) singleton {
-	return singleton{false, name, factory}
+func newSingleton(name string, factory SingletonFactory, parallel bool) singleton {
+	return singleton{pre: false, parallel: parallel, name: name, factory: factory}
 }
 
 func newPreSingleton(name string, factory SingletonFactory) singleton {
-	return singleton{true, name, factory}
+	return singleton{pre: true, parallel: false, name: name, factory: factory}
 }
 
 func (s singleton) componentName() string {
@@ -86,7 +89,7 @@
 	if s.pre {
 		ctx.RegisterPreSingletonType(s.name, adaptor)
 	} else {
-		ctx.RegisterSingletonType(s.name, adaptor)
+		ctx.RegisterSingletonType(s.name, adaptor, s.parallel)
 	}
 }
 
@@ -145,8 +148,16 @@
 	moduleTypeByFactory[factory] = name
 }
 
+func registerSingletonType(name string, factory SingletonFactory, parallel bool) {
+	singletons = append(singletons, newSingleton(name, factory, parallel))
+}
+
 func RegisterSingletonType(name string, factory SingletonFactory) {
-	singletons = append(singletons, newSingleton(name, factory))
+	registerSingletonType(name, factory, false)
+}
+
+func RegisterParallelSingletonType(name string, factory SingletonFactory) {
+	registerSingletonType(name, factory, true)
 }
 
 func RegisterPreSingletonType(name string, factory SingletonFactory) {
@@ -220,17 +231,17 @@
 func collateGloballyRegisteredSingletons() sortableComponents {
 	allSingletons := append(sortableComponents(nil), singletons...)
 	allSingletons = append(allSingletons,
-		singleton{false, "bazeldeps", BazelSingleton},
+		singleton{pre: false, parallel: true, name: "bazeldeps", factory: BazelSingleton},
 
 		// Register phony just before makevars so it can write out its phony rules as Make rules
-		singleton{false, "phony", phonySingletonFactory},
+		singleton{pre: false, parallel: false, name: "phony", factory: phonySingletonFactory},
 
 		// Register makevars after other singletons so they can export values through makevars
-		singleton{false, "makevars", makeVarsSingletonFunc},
+		singleton{pre: false, parallel: false, name: "makevars", factory: makeVarsSingletonFunc},
 
 		// Register env and ninjadeps last so that they can track all used environment variables and
 		// Ninja file dependencies stored in the config.
-		singleton{false, "ninjadeps", ninjaDepsSingletonFactory},
+		singleton{pre: false, parallel: false, name: "ninjadeps", factory: ninjaDepsSingletonFactory},
 	)
 
 	return allSingletons
@@ -259,7 +270,9 @@
 type RegistrationContext interface {
 	RegisterModuleType(name string, factory ModuleFactory)
 	RegisterSingletonModuleType(name string, factory SingletonModuleFactory)
+	RegisterParallelSingletonModuleType(name string, factory SingletonModuleFactory)
 	RegisterPreSingletonType(name string, factory SingletonFactory)
+	RegisterParallelSingletonType(name string, factory SingletonFactory)
 	RegisterSingletonType(name string, factory SingletonFactory)
 	PreArchMutators(f RegisterMutatorFunc)
 
@@ -315,8 +328,15 @@
 }
 
 func (ctx *initRegistrationContext) RegisterSingletonModuleType(name string, factory SingletonModuleFactory) {
+	ctx.registerSingletonModuleType(name, factory, false)
+}
+func (ctx *initRegistrationContext) RegisterParallelSingletonModuleType(name string, factory SingletonModuleFactory) {
+	ctx.registerSingletonModuleType(name, factory, true)
+}
+
+func (ctx *initRegistrationContext) registerSingletonModuleType(name string, factory SingletonModuleFactory, parallel bool) {
 	s, m := SingletonModuleFactoryAdaptor(name, factory)
-	ctx.RegisterSingletonType(name, s)
+	ctx.registerSingletonType(name, s, parallel)
 	ctx.RegisterModuleType(name, m)
 	// Overwrite moduleTypesForDocs with the original factory instead of the lambda returned by
 	// SingletonModuleFactoryAdaptor so that docs can find the module type documentation on the
@@ -324,12 +344,20 @@
 	RegisterModuleTypeForDocs(name, reflect.ValueOf(factory))
 }
 
-func (ctx *initRegistrationContext) RegisterSingletonType(name string, factory SingletonFactory) {
+func (ctx *initRegistrationContext) registerSingletonType(name string, factory SingletonFactory, parallel bool) {
 	if _, present := ctx.singletonTypes[name]; present {
 		panic(fmt.Sprintf("singleton type %q is already registered", name))
 	}
 	ctx.singletonTypes[name] = factory
-	RegisterSingletonType(name, factory)
+	registerSingletonType(name, factory, parallel)
+}
+
+func (ctx *initRegistrationContext) RegisterSingletonType(name string, factory SingletonFactory) {
+	ctx.registerSingletonType(name, factory, false)
+}
+
+func (ctx *initRegistrationContext) RegisterParallelSingletonType(name string, factory SingletonFactory) {
+	ctx.registerSingletonType(name, factory, true)
 }
 
 func (ctx *initRegistrationContext) RegisterPreSingletonType(name string, factory SingletonFactory) {
diff --git a/android/sdk.go b/android/sdk.go
index 63e0bbe..6b598ab 100644
--- a/android/sdk.go
+++ b/android/sdk.go
@@ -830,6 +830,9 @@
 	// IsTargetBuildBeforeTiramisu return true if the target build release for which this snapshot is
 	// being generated is before Tiramisu, i.e. S.
 	IsTargetBuildBeforeTiramisu() bool
+
+	// ModuleErrorf reports an error at the line number of the module type in the module definition.
+	ModuleErrorf(fmt string, args ...interface{})
 }
 
 // ExportedComponentsInfo contains information about the components that this module exports to an
diff --git a/android/sdk_version.go b/android/sdk_version.go
index 0ae8073..80aeb2e 100644
--- a/android/sdk_version.go
+++ b/android/sdk_version.go
@@ -84,12 +84,6 @@
 	}
 }
 
-// JavaLibraryName returns the soong module containing the Java APIs of that API surface.
-func (k SdkKind) JavaLibraryName(c Config) string {
-	name := k.DefaultJavaLibraryName()
-	return JavaApiLibraryName(c, name)
-}
-
 // JavaApiLibraryName returns the name of .txt equivalent of a java_library, but does
 // not check if either module exists.
 // TODO: Return .txt (single-tree or multi-tree equivalents) based on config
@@ -100,6 +94,15 @@
 	return name
 }
 
+// JavaApiLibraryNames applies JavaApiLibraryName to the list of java_library names.
+func JavaApiLibraryNames(c Config, names []string) []string {
+	apiLibs := make([]string, len(names))
+	for i, name := range names {
+		apiLibs[i] = JavaApiLibraryName(c, name)
+	}
+	return apiLibs
+}
+
 func (k SdkKind) DefaultJavaLibraryName() string {
 	switch k {
 	case SdkPublic:
@@ -326,11 +329,10 @@
 
 // Export the name of the soong modules representing the various Java API surfaces.
 func javaSdkMakeVars(ctx MakeVarsContext) {
-	ctx.Strict("ANDROID_PUBLIC_STUBS", SdkPublic.JavaLibraryName(ctx.Config()))
-	ctx.Strict("ANDROID_SYSTEM_STUBS", SdkSystem.JavaLibraryName(ctx.Config()))
-	ctx.Strict("ANDROID_TEST_STUBS", SdkTest.JavaLibraryName(ctx.Config()))
-	ctx.Strict("ANDROID_MODULE_LIB_STUBS", SdkModule.JavaLibraryName(ctx.Config()))
-	ctx.Strict("ANDROID_SYSTEM_SERVER_STUBS", SdkSystemServer.JavaLibraryName(ctx.Config()))
-	// TODO (jihoonkang): Create a .txt equivalent for core.current.stubs
-	ctx.Strict("ANDROID_CORE_STUBS", SdkCore.JavaLibraryName(ctx.Config()))
+	ctx.Strict("ANDROID_PUBLIC_STUBS", SdkPublic.DefaultJavaLibraryName())
+	ctx.Strict("ANDROID_SYSTEM_STUBS", SdkSystem.DefaultJavaLibraryName())
+	ctx.Strict("ANDROID_TEST_STUBS", SdkTest.DefaultJavaLibraryName())
+	ctx.Strict("ANDROID_MODULE_LIB_STUBS", SdkModule.DefaultJavaLibraryName())
+	ctx.Strict("ANDROID_SYSTEM_SERVER_STUBS", SdkSystemServer.DefaultJavaLibraryName())
+	ctx.Strict("ANDROID_CORE_STUBS", SdkCore.DefaultJavaLibraryName())
 }
diff --git a/android/soong_config_modules.go b/android/soong_config_modules.go
index 5fa6012..0246a08 100644
--- a/android/soong_config_modules.go
+++ b/android/soong_config_modules.go
@@ -421,6 +421,57 @@
 	}).(map[string]blueprint.ModuleFactory)
 }
 
+// tracingConfig is a wrapper to soongconfig.SoongConfig which records all accesses to SoongConfig.
+type tracingConfig struct {
+	config    soongconfig.SoongConfig
+	boolSet   map[string]bool
+	stringSet map[string]string
+	isSetSet  map[string]bool
+}
+
+func (c *tracingConfig) Bool(name string) bool {
+	c.boolSet[name] = c.config.Bool(name)
+	return c.boolSet[name]
+}
+
+func (c *tracingConfig) String(name string) string {
+	c.stringSet[name] = c.config.String(name)
+	return c.stringSet[name]
+}
+
+func (c *tracingConfig) IsSet(name string) bool {
+	c.isSetSet[name] = c.config.IsSet(name)
+	return c.isSetSet[name]
+}
+
+func (c *tracingConfig) getTrace() soongConfigTrace {
+	ret := soongConfigTrace{}
+
+	for k, v := range c.boolSet {
+		ret.Bools = append(ret.Bools, fmt.Sprintf("%q:%t", k, v))
+	}
+	for k, v := range c.stringSet {
+		ret.Strings = append(ret.Strings, fmt.Sprintf("%q:%q", k, v))
+	}
+	for k, v := range c.isSetSet {
+		ret.IsSets = append(ret.IsSets, fmt.Sprintf("%q:%t", k, v))
+	}
+
+	return ret
+}
+
+func newTracingConfig(config soongconfig.SoongConfig) *tracingConfig {
+	c := tracingConfig{
+		config:    config,
+		boolSet:   make(map[string]bool),
+		stringSet: make(map[string]string),
+		isSetSet:  make(map[string]bool),
+	}
+	return &c
+}
+
+var _ soongconfig.SoongConfig = (*tracingConfig)(nil)
+
 // configModuleFactory takes an existing soongConfigModuleFactory and a
 // ModuleType to create a new ModuleFactory that uses a custom loadhook.
 func configModuleFactory(factory blueprint.ModuleFactory, moduleType *soongconfig.ModuleType, bp2build bool) blueprint.ModuleFactory {
@@ -485,8 +536,8 @@
 			// conditional on Soong config variables by reading the product
 			// config variables from Make.
 			AddLoadHook(module, func(ctx LoadHookContext) {
-				config := ctx.Config().VendorConfig(moduleType.ConfigNamespace)
-				newProps, err := soongconfig.PropertiesToApply(moduleType, conditionalProps, config)
+				tracingConfig := newTracingConfig(ctx.Config().VendorConfig(moduleType.ConfigNamespace))
+				newProps, err := soongconfig.PropertiesToApply(moduleType, conditionalProps, tracingConfig)
 				if err != nil {
 					ctx.ModuleErrorf("%s", err)
 					return
@@ -494,6 +545,8 @@
 				for _, ps := range newProps {
 					ctx.AppendProperties(ps)
 				}
+
+				module.(Module).base().commonProperties.SoongConfigTrace = tracingConfig.getTrace()
 			})
 		}
 		return module, props
diff --git a/android/soong_config_modules_test.go b/android/soong_config_modules_test.go
index cab3e2d..79bdeb8 100644
--- a/android/soong_config_modules_test.go
+++ b/android/soong_config_modules_test.go
@@ -16,6 +16,7 @@
 
 import (
 	"fmt"
+	"path/filepath"
 	"testing"
 )
 
@@ -34,7 +35,8 @@
 type soongConfigTestModule struct {
 	ModuleBase
 	DefaultableModuleBase
-	props soongConfigTestModuleProperties
+	props      soongConfigTestModuleProperties
+	outputPath ModuleOutPath
 }
 
 type soongConfigTestModuleProperties struct {
@@ -49,7 +51,9 @@
 	return m
 }
 
-func (t soongConfigTestModule) GenerateAndroidBuildActions(ModuleContext) {}
+func (t *soongConfigTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+	t.outputPath = PathForModuleOut(ctx, "test")
+}
 
 var prepareForSoongConfigTestModule = FixtureRegisterWithContext(func(ctx RegistrationContext) {
 	ctx.RegisterModuleType("test_defaults", soongConfigTestDefaultsModuleFactory)
@@ -503,3 +507,197 @@
 		})
 	}
 }
+
+func TestSoongConfigModuleTrace(t *testing.T) {
+	bp := `
+		soong_config_module_type {
+			name: "acme_test",
+			module_type: "test",
+			config_namespace: "acme",
+			variables: ["board", "feature1", "FEATURE3", "unused_string_var"],
+			bool_variables: ["feature2", "unused_feature", "always_true"],
+			value_variables: ["size", "unused_size"],
+			properties: ["cflags", "srcs", "defaults"],
+		}
+
+		soong_config_module_type {
+			name: "acme_test_defaults",
+			module_type: "test_defaults",
+			config_namespace: "acme",
+			variables: ["board", "feature1", "FEATURE3", "unused_string_var"],
+			bool_variables: ["feature2", "unused_feature", "always_true"],
+			value_variables: ["size", "unused_size"],
+			properties: ["cflags", "srcs", "defaults"],
+		}
+
+		soong_config_string_variable {
+			name: "board",
+			values: ["soc_a", "soc_b", "soc_c"],
+		}
+
+		soong_config_string_variable {
+			name: "unused_string_var",
+			values: ["a", "b"],
+		}
+
+		soong_config_bool_variable {
+			name: "feature1",
+		}
+
+		soong_config_bool_variable {
+			name: "FEATURE3",
+		}
+
+		test_defaults {
+			name: "test_defaults",
+			cflags: ["DEFAULT"],
+		}
+
+		test {
+			name: "normal",
+			defaults: ["test_defaults"],
+		}
+
+		acme_test {
+			name: "board_1",
+			defaults: ["test_defaults"],
+			soong_config_variables: {
+				board: {
+					soc_a: {
+						cflags: ["-DSOC_A"],
+					},
+				},
+			},
+		}
+
+		acme_test {
+			name: "board_2",
+			defaults: ["test_defaults"],
+			soong_config_variables: {
+				board: {
+					soc_a: {
+						cflags: ["-DSOC_A"],
+					},
+				},
+			},
+		}
+
+		acme_test {
+			name: "size",
+			defaults: ["test_defaults"],
+			soong_config_variables: {
+				size: {
+					cflags: ["-DSIZE=%s"],
+				},
+			},
+		}
+
+		acme_test {
+			name: "board_and_size",
+			defaults: ["test_defaults"],
+			soong_config_variables: {
+				board: {
+					soc_a: {
+						cflags: ["-DSOC_A"],
+					},
+				},
+				size: {
+					cflags: ["-DSIZE=%s"],
+				},
+			},
+		}
+
+		acme_test_defaults {
+			name: "board_defaults",
+			soong_config_variables: {
+				board: {
+					soc_a: {
+						cflags: ["-DSOC_A"],
+					},
+				},
+			},
+		}
+
+		acme_test_defaults {
+			name: "size_defaults",
+			soong_config_variables: {
+				size: {
+					cflags: ["-DSIZE=%s"],
+				},
+			},
+		}
+
+		test {
+			name: "board_and_size_with_defaults",
+			defaults: ["board_defaults", "size_defaults"],
+		}
+    `
+
+	fixtureForVendorVars := func(vars map[string]map[string]string) FixturePreparer {
+		return FixtureModifyProductVariables(func(variables FixtureProductVariables) {
+			variables.VendorVars = vars
+		})
+	}
+
+	preparer := fixtureForVendorVars(map[string]map[string]string{
+		"acme": {
+			"board":    "soc_a",
+			"size":     "42",
+			"feature1": "true",
+			"feature2": "false",
+			// FEATURE3 unset
+			"unused_feature":    "true", // unused
+			"unused_size":       "1",    // unused
+			"unused_string_var": "a",    // unused
+			"always_true":       "true",
+		},
+	})
+
+	t.Run("soong config trace hash", func(t *testing.T) {
+		result := GroupFixturePreparers(
+			preparer,
+			PrepareForTestWithDefaults,
+			PrepareForTestWithSoongConfigModuleBuildComponents,
+			prepareForSoongConfigTestModule,
+			FixtureRegisterWithContext(func(ctx RegistrationContext) {
+				ctx.FinalDepsMutators(registerSoongConfigTraceMutator)
+			}),
+			FixtureWithRootAndroidBp(bp),
+		).RunTest(t)
+
+		// Hashes of modules not using soong config should be empty
+		normal := result.ModuleForTests("normal", "").Module().(*soongConfigTestModule)
+		AssertDeepEquals(t, "normal hash", normal.base().commonProperties.SoongConfigTraceHash, "")
+		AssertDeepEquals(t, "normal hash out", normal.outputPath.RelativeToTop().String(), "out/soong/.intermediates/normal/test")
+
+		board1 := result.ModuleForTests("board_1", "").Module().(*soongConfigTestModule)
+		board2 := result.ModuleForTests("board_2", "").Module().(*soongConfigTestModule)
+		size := result.ModuleForTests("size", "").Module().(*soongConfigTestModule)
+
+		// Trace mutator sets soong config trace hash correctly
+		board1Hash := board1.base().commonProperties.SoongConfigTrace.hash()
+		board1Output := board1.outputPath.RelativeToTop().String()
+		AssertDeepEquals(t, "board hash calc", board1Hash, board1.base().commonProperties.SoongConfigTraceHash)
+		AssertDeepEquals(t, "board hash path", board1Output, filepath.Join("out/soong/.intermediates/board_1", board1Hash, "test"))
+
+		sizeHash := size.base().commonProperties.SoongConfigTrace.hash()
+		sizeOutput := size.outputPath.RelativeToTop().String()
+		AssertDeepEquals(t, "size hash calc", sizeHash, size.base().commonProperties.SoongConfigTraceHash)
+		AssertDeepEquals(t, "size hash path", sizeOutput, filepath.Join("out/soong/.intermediates/size", sizeHash, "test"))
+
+		// Trace should be identical for modules using the same set of variables
+		AssertDeepEquals(t, "board trace", board1.base().commonProperties.SoongConfigTrace, board2.base().commonProperties.SoongConfigTrace)
+		AssertDeepEquals(t, "board hash", board1.base().commonProperties.SoongConfigTraceHash, board2.base().commonProperties.SoongConfigTraceHash)
+
+		// Trace hash should be different for different sets of soong variables
+		AssertBoolEquals(t, "board hash not equal to size hash", board1.base().commonProperties.SoongConfigTraceHash == size.commonProperties.SoongConfigTraceHash, false)
+
+		boardSize := result.ModuleForTests("board_and_size", "").Module().(*soongConfigTestModule)
+		boardSizeDefaults := result.ModuleForTests("board_and_size_with_defaults", "").Module()
+
+		// Trace should propagate
+		AssertDeepEquals(t, "board_size hash calc", boardSize.base().commonProperties.SoongConfigTrace.hash(), boardSize.base().commonProperties.SoongConfigTraceHash)
+		AssertDeepEquals(t, "board_size trace", boardSize.base().commonProperties.SoongConfigTrace, boardSizeDefaults.base().commonProperties.SoongConfigTrace)
+		AssertDeepEquals(t, "board_size hash", boardSize.base().commonProperties.SoongConfigTraceHash, boardSizeDefaults.base().commonProperties.SoongConfigTraceHash)
+	})
+}
diff --git a/android/test_asserts.go b/android/test_asserts.go
index 4143f15..3a2cb1a 100644
--- a/android/test_asserts.go
+++ b/android/test_asserts.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"reflect"
+	"regexp"
 	"strings"
 	"testing"
 )
@@ -137,6 +138,20 @@
 	}
 }
 
+// AssertStringMatches checks if the string matches the given regular expression. If it does not match,
+// then an error is reported with the supplied message including a reason for why it failed.
+func AssertStringMatches(t *testing.T, message, s, expectedRex string) {
+	t.Helper()
+	ok, err := regexp.MatchString(expectedRex, s)
+	if err != nil {
+		t.Fatalf("regexp failure trying to match %s against `%s` expression: %s", s, expectedRex, err)
+		return
+	}
+	if !ok {
+		t.Errorf("%s does not match regular expression %s", s, expectedRex)
+	}
+}
+
 // AssertStringListContains checks if the list of strings contains the expected string. If it does
 // not then it reports an error prefixed with the supplied message and including a reason for why it
 // failed.
@@ -185,6 +200,22 @@
 	}
 }
 
+// Asserts that each of the Paths in actual end with the corresponding string
+// from expected. Useful to test that output paths contain expected items without
+// hard-coding where intermediate files might be located.
+func AssertPathsEndWith(t *testing.T, message string, expected []string, actual []Path) {
+	t.Helper()
+	if len(expected) != len(actual) {
+		t.Errorf("%s (length): expected %d, actual %d", message, len(expected), len(actual))
+		return
+	}
+	for i := range expected {
+		if !strings.HasSuffix(actual[i].String(), expected[i]) {
+			t.Errorf("%s (item %d): expected '%s', actual '%s'", message, i, expected[i], actual[i].String())
+		}
+	}
+}
+
 // AssertDeepEquals checks if the expected and actual values are equal using reflect.DeepEqual and
 // if they are not then it reports an error prefixed with the supplied message and including a
 // reason for why it failed.
diff --git a/android/test_suites.go b/android/test_suites.go
index b570b23..b48d71a 100644
--- a/android/test_suites.go
+++ b/android/test_suites.go
@@ -15,7 +15,7 @@
 package android
 
 func init() {
-	RegisterSingletonType("testsuites", testSuiteFilesFactory)
+	RegisterParallelSingletonType("testsuites", testSuiteFilesFactory)
 }
 
 func testSuiteFilesFactory() Singleton {
diff --git a/android/testing.go b/android/testing.go
index fc39a9c..5ad7ad0 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -495,8 +495,18 @@
 	ctx.RegisterModuleType(name, m)
 }
 
+func (ctx *TestContext) RegisterParallelSingletonModuleType(name string, factory SingletonModuleFactory) {
+	s, m := SingletonModuleFactoryAdaptor(name, factory)
+	ctx.RegisterParallelSingletonType(name, s)
+	ctx.RegisterModuleType(name, m)
+}
+
 func (ctx *TestContext) RegisterSingletonType(name string, factory SingletonFactory) {
-	ctx.singletons = append(ctx.singletons, newSingleton(name, factory))
+	ctx.singletons = append(ctx.singletons, newSingleton(name, factory, false))
+}
+
+func (ctx *TestContext) RegisterParallelSingletonType(name string, factory SingletonFactory) {
+	ctx.singletons = append(ctx.singletons, newSingleton(name, factory, true))
 }
 
 func (ctx *TestContext) RegisterPreSingletonType(name string, factory SingletonFactory) {
@@ -813,6 +823,20 @@
 	return path.RelativeToTop()
 }
 
+func allOutputs(p BuildParams) []string {
+	outputs := append(WritablePaths(nil), p.Outputs...)
+	outputs = append(outputs, p.ImplicitOutputs...)
+	if p.Output != nil {
+		outputs = append(outputs, p.Output)
+	}
+	return outputs.Strings()
+}
+
+// AllOutputs returns all 'BuildParams.Output's and 'BuildParams.Outputs's in their full path string forms.
+func (p TestingBuildParams) AllOutputs() []string {
+	return allOutputs(p.BuildParams)
+}
+
 // baseTestingComponent provides functionality common to both TestingModule and TestingSingleton.
 type baseTestingComponent struct {
 	config   Config
@@ -954,12 +978,7 @@
 func (b baseTestingComponent) allOutputs() []string {
 	var outputFullPaths []string
 	for _, p := range b.provider.BuildParamsForTests() {
-		outputs := append(WritablePaths(nil), p.Outputs...)
-		outputs = append(outputs, p.ImplicitOutputs...)
-		if p.Output != nil {
-			outputs = append(outputs, p.Output)
-		}
-		outputFullPaths = append(outputFullPaths, outputs.Strings()...)
+		outputFullPaths = append(outputFullPaths, allOutputs(p)...)
 	}
 	return outputFullPaths
 }
diff --git a/android/util.go b/android/util.go
index 38e0a4d..08a3521 100644
--- a/android/util.go
+++ b/android/util.go
@@ -26,7 +26,11 @@
 
 // CopyOf returns a new slice that has the same contents as s.
 func CopyOf(s []string) []string {
-	return append([]string(nil), s...)
+	// If the input is nil, return nil and not an empty list
+	if s == nil {
+		return s
+	}
+	return append([]string{}, s...)
 }
 
 // Concat returns a new slice concatenated from the two input slices. It does not change the input
@@ -276,6 +280,8 @@
 // FirstUniqueStrings returns all unique elements of a slice of strings, keeping the first copy of
 // each.  It modifies the slice contents in place, and returns a subslice of the original slice.
 func FirstUniqueStrings(list []string) []string {
+	// Do not moodify the input in-place, operate on a copy instead.
+	list = CopyOf(list)
 	// 128 was chosen based on BenchmarkFirstUniqueStrings results.
 	if len(list) > 128 {
 		return firstUniqueStringsMap(list)
@@ -332,6 +338,7 @@
 
 // SortedUniqueStrings returns what the name says
 func SortedUniqueStrings(list []string) []string {
+	// FirstUniqueStrings creates a copy of `list`, so the input remains untouched.
 	unique := FirstUniqueStrings(list)
 	sort.Strings(unique)
 	return unique
diff --git a/android/util_test.go b/android/util_test.go
index 5584b38..a2ef589 100644
--- a/android/util_test.go
+++ b/android/util_test.go
@@ -381,6 +381,14 @@
 	}
 }
 
+func TestCopyOfEmptyAndNil(t *testing.T) {
+	emptyList := []string{}
+	copyOfEmptyList := CopyOf(emptyList)
+	AssertBoolEquals(t, "Copy of an empty list should be an empty list and not nil", true, copyOfEmptyList != nil)
+	copyOfNilList := CopyOf(nil)
+	AssertBoolEquals(t, "Copy of a nil list should be a nil list and not an empty list", true, copyOfNilList == nil)
+}
+
 func ExampleCopyOf() {
 	a := []string{"1", "2", "3"}
 	b := CopyOf(a)
diff --git a/android/variable.go b/android/variable.go
index 249d53b..6362953 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -109,6 +109,11 @@
 			Cflags []string
 		}
 
+		Build_from_text_stub struct {
+			Static_libs         []string
+			Exclude_static_libs []string
+		}
+
 		// debuggable is true for eng and userdebug builds, and can be used to turn on additional
 		// debugging features that don't significantly impact runtime behavior.  userdebug builds
 		// are used for dogfooding and performance testing, and should be as similar to user builds
@@ -218,6 +223,7 @@
 	DeviceVndkVersion                     *string  `json:",omitempty"`
 	DeviceCurrentApiLevelForVendorModules *string  `json:",omitempty"`
 	DeviceSystemSdkVersions               []string `json:",omitempty"`
+	DeviceMaxPageSizeSupported            *string  `json:",omitempty"`
 
 	RecoverySnapshotVersion *string `json:",omitempty"`
 
@@ -285,6 +291,7 @@
 	Uml                          *bool    `json:",omitempty"`
 	Arc                          *bool    `json:",omitempty"`
 	MinimizeJavaDebugInfo        *bool    `json:",omitempty"`
+	Build_from_text_stub         *bool    `json:",omitempty"`
 
 	Check_elf_files *bool `json:",omitempty"`
 
@@ -306,6 +313,8 @@
 	MemtagHeapAsyncIncludePaths []string `json:",omitempty"`
 	MemtagHeapSyncIncludePaths  []string `json:",omitempty"`
 
+	HWASanIncludePaths []string `json:",omitempty"`
+
 	VendorPath    *string `json:",omitempty"`
 	OdmPath       *string `json:",omitempty"`
 	ProductPath   *string `json:",omitempty"`
@@ -417,8 +426,6 @@
 
 	TargetFSConfigGen []string `json:",omitempty"`
 
-	MissingUsesLibraries []string `json:",omitempty"`
-
 	EnforceProductPartitionInterface *bool `json:",omitempty"`
 
 	EnforceInterPartitionJavaSdkLibrary *bool    `json:",omitempty"`
@@ -437,24 +444,25 @@
 
 	ShippingApiLevel *string `json:",omitempty"`
 
+	BuildBrokenPluginValidation        []string `json:",omitempty"`
 	BuildBrokenClangAsFlags            bool     `json:",omitempty"`
 	BuildBrokenClangCFlags             bool     `json:",omitempty"`
 	BuildBrokenClangProperty           bool     `json:",omitempty"`
-	BuildBrokenDepfile                 *bool    `json:",omitempty"`
+	GenruleSandboxing                  *bool    `json:",omitempty"`
 	BuildBrokenEnforceSyspropOwner     bool     `json:",omitempty"`
 	BuildBrokenTrebleSyspropNeverallow bool     `json:",omitempty"`
 	BuildBrokenUsesSoongPython2Modules bool     `json:",omitempty"`
 	BuildBrokenVendorPropertyNamespace bool     `json:",omitempty"`
 	BuildBrokenInputDirModules         []string `json:",omitempty"`
 
+	BuildWarningBadOptionalUsesLibsAllowlist []string `json:",omitempty"`
+
 	BuildDebugfsRestrictionsEnabled bool `json:",omitempty"`
 
 	RequiresInsecureExecmemForSwiftshader bool `json:",omitempty"`
 
 	SelinuxIgnoreNeverallows bool `json:",omitempty"`
 
-	SepolicySplit bool `json:",omitempty"`
-
 	SepolicyFreezeTestExtraDirs         []string `json:",omitempty"`
 	SepolicyFreezeTestExtraPrebuiltDirs []string `json:",omitempty"`
 
@@ -466,6 +474,13 @@
 	SourceRootDirs []string `json:",omitempty"`
 
 	AfdoProfiles []string `json:",omitempty"`
+
+	ProductManufacturer string   `json:",omitempty"`
+	ProductBrand        string   `json:",omitempty"`
+	BuildVersionTags    []string `json:",omitempty"`
+
+	ReleaseVersion               string   `json:",omitempty"`
+	ReleaseDeviceConfigValueSets []string `json:",omitempty"`
 }
 
 func boolPtr(v bool) *bool {
@@ -505,6 +520,7 @@
 		DeviceSecondaryArchVariant: stringPtr("armv8-a"),
 		DeviceSecondaryCpuVariant:  stringPtr("generic"),
 		DeviceSecondaryAbi:         []string{"armeabi-v7a", "armeabi"},
+		DeviceMaxPageSizeSupported: stringPtr("4096"),
 
 		AAPTConfig:          []string{"normal", "large", "xlarge", "hdpi", "xhdpi", "xxhdpi"},
 		AAPTPreferredConfig: stringPtr("xhdpi"),
@@ -516,6 +532,7 @@
 		Malloc_pattern_fill_contents: boolPtr(false),
 		Safestack:                    boolPtr(false),
 		TrimmedApex:                  boolPtr(false),
+		Build_from_text_stub:         boolPtr(false),
 
 		BootJars:     ConfiguredJarList{apexes: []string{}, jars: []string{}},
 		ApexBootJars: ConfiguredJarList{apexes: []string{}, jars: []string{}},
@@ -533,124 +550,102 @@
 	Module() Module
 }
 
-// ProductConfigProperty contains the information for a single property (may be a struct) paired
-// with the appropriate ProductConfigVariable.
+// ProductConfigOrSoongConfigProperty represents either a soong config variable + its value
+// or a product config variable. You can get both a ConfigurationAxis and a SelectKey from it
+// for use in bazel attributes. ProductVariableProperties() will return a map from properties ->
+// this interface -> property structs for use in bp2build converters
+type ProductConfigOrSoongConfigProperty interface {
+	// Name of the product variable or soong config variable
+	Name() string
+	// AlwaysEmit returns true for soong config variables but false for product variables. This
+	// is intended to indicate if we need to always emit empty lists in the select statements.
+	AlwaysEmit() bool
+	// ConfigurationAxis returns the bazel.ConfigurationAxis that represents this variable. The
+	// configuration axis will change depending on the variable and whether it's arch/os variant
+	// as well.
+	ConfigurationAxis() bazel.ConfigurationAxis
+	// SelectKey returns a string that represents the key of a select branch, however, it is not
+	// actually the real label written out to the build file.
+	// this.ConfigurationAxis().SelectKey(this.SelectKey()) will give the actual label.
+	SelectKey() string
+}
+
+// ProductConfigProperty represents a product config variable, and if it is arch-variant or not.
 type ProductConfigProperty struct {
 	// The name of the product variable, e.g. "safestack", "malloc_not_svelte",
 	// "board"
-	Name string
+	name string
 
-	// Namespace of the variable, if this is a soong_config_module_type variable
-	// e.g. "acme", "ANDROID", "vendor_name"
-	Namespace string
-
-	// Unique configuration to identify this product config property (i.e. a
-	// primary key), as just using the product variable name is not sufficient.
-	//
-	// For product variables, this is the product variable name + optional
-	// archvariant information. e.g.
-	//
-	// product_variables: {
-	//     foo: {
-	//         cflags: ["-Dfoo"],
-	//     },
-	// },
-	//
-	// FullConfig would be "foo".
-	//
-	// target: {
-	//     android: {
-	//         product_variables: {
-	//             foo: {
-	//                 cflags: ["-Dfoo-android"],
-	//             },
-	//         },
-	//     },
-	// },
-	//
-	// FullConfig would be "foo-android".
-	//
-	// For soong config variables, this is the namespace + product variable name
-	// + value of the variable, if applicable. The value can also be
-	// conditions_default.
-	//
-	// e.g.
-	//
-	// soong_config_variables: {
-	//     feature1: {
-	//         conditions_default: {
-	//             cflags: ["-DDEFAULT1"],
-	//         },
-	//         cflags: ["-DFEATURE1"],
-	//     },
-	// }
-	//
-	// where feature1 is created in the "acme" namespace, so FullConfig would be
-	// "acme__feature1" and "acme__feature1__conditions_default".
-	//
-	// e.g.
-	//
-	// soong_config_variables: {
-	//     board: {
-	//         soc_a: {
-	//             cflags: ["-DSOC_A"],
-	//         },
-	//         soc_b: {
-	//             cflags: ["-DSOC_B"],
-	//         },
-	//         soc_c: {},
-	//         conditions_default: {
-	//             cflags: ["-DSOC_DEFAULT"]
-	//         },
-	//     },
-	// }
-	//
-	// where board is created in the "acme" namespace, so FullConfig would be
-	// "acme__board__soc_a", "acme__board__soc_b", and
-	// "acme__board__conditions_default"
-	FullConfig string
-
-	// keeps track of whether this product variable is nested under an arch variant
-	OuterAxis bazel.ConfigurationAxis
+	arch string
 }
 
-func (p *ProductConfigProperty) AlwaysEmit() bool {
-	return p.Namespace != ""
+func (p ProductConfigProperty) Name() string {
+	return p.name
 }
 
-func (p *ProductConfigProperty) ConfigurationAxis() bazel.ConfigurationAxis {
-	if p.Namespace == "" {
-		return bazel.ProductVariableConfigurationAxis(p.FullConfig, p.OuterAxis)
+func (p ProductConfigProperty) AlwaysEmit() bool {
+	return false
+}
+
+func (p ProductConfigProperty) ConfigurationAxis() bazel.ConfigurationAxis {
+	return bazel.ProductVariableConfigurationAxis(p.arch != "", p.name+"__"+p.arch)
+}
+
+func (p ProductConfigProperty) SelectKey() string {
+	if p.arch == "" {
+		return strings.ToLower(p.name)
 	} else {
-		// Soong config variables can be uniquely identified by the namespace
-		// (e.g. acme, android) and the product variable name (e.g. board, size)
-		return bazel.ProductVariableConfigurationAxis(p.Namespace+"__"+p.Name, bazel.NoConfigAxis)
+		return strings.ToLower(p.name + "-" + p.arch)
 	}
 }
 
+// SoongConfigProperty represents a soong config variable, its value if it's a string variable,
+// and if it's dependent on the OS or not
+type SoongConfigProperty struct {
+	name      string
+	namespace string
+	// Can be an empty string for bool/value soong config variables
+	value string
+	// If there is a target: field inside a soong config property struct, the os that it selects
+	// on will be represented here.
+	os string
+}
+
+func (p SoongConfigProperty) Name() string {
+	return p.name
+}
+
+func (p SoongConfigProperty) AlwaysEmit() bool {
+	return true
+}
+
+func (p SoongConfigProperty) ConfigurationAxis() bazel.ConfigurationAxis {
+	return bazel.ProductVariableConfigurationAxis(false, p.namespace+"__"+p.name+"__"+p.os)
+}
+
 // SelectKey returns the literal string that represents this variable in a BUILD
 // select statement.
-func (p *ProductConfigProperty) SelectKey() string {
-	if p.Namespace == "" {
-		return strings.ToLower(p.FullConfig)
-	}
-
-	if p.FullConfig == bazel.ConditionsDefaultConfigKey {
+func (p SoongConfigProperty) SelectKey() string {
+	// p.value being conditions_default can happen with or without a desired os. When not using
+	// an os, we want to emit literally just //conditions:default in the select statement, but
+	// when using an os, we want to emit namespace__name__conditions_default__os, so that
+	// the branch is only taken if the variable is not set, and we're on the desired os.
+	// ConfigurationAxis#SelectKey will map the conditions_default result of this function to
+	// //conditions:default.
+	if p.value == bazel.ConditionsDefaultConfigKey && p.os == "" {
 		return bazel.ConditionsDefaultConfigKey
 	}
 
-	value := p.FullConfig
-	if value == p.Name {
-		value = ""
+	parts := []string{p.namespace, p.name}
+	if p.value != "" && p.value != bazel.ConditionsDefaultSelectKey {
+		parts = append(parts, p.value)
+	}
+	if p.os != "" {
+		parts = append(parts, p.os)
 	}
 
-	// e.g. acme__feature1, android__board__soc_a
-	selectKey := strings.ToLower(strings.Join([]string{p.Namespace, p.Name}, "__"))
-	if value != "" {
-		selectKey = strings.ToLower(strings.Join([]string{selectKey, value}, "__"))
-	}
-
-	return selectKey
+	// e.g. acme__feature1, android__board__soc_a, my_namespace__my_variables__my_value__my_os
+	return strings.ToLower(strings.Join(parts, "__"))
 }
 
 // ProductConfigProperties is a map of maps to group property values according
@@ -666,7 +661,7 @@
 //
 // The value of the map is the interface{} representing the value of the
 // property, like ["-DDEFINES"] for cflags.
-type ProductConfigProperties map[string]map[ProductConfigProperty]interface{}
+type ProductConfigProperties map[string]map[ProductConfigOrSoongConfigProperty]interface{}
 
 // ProductVariableProperties returns a ProductConfigProperties containing only the properties which
 // have been set for the given module.
@@ -677,26 +672,10 @@
 
 	if moduleBase.variableProperties != nil {
 		productVariablesProperty := proptools.FieldNameForProperty("product_variables")
-		productVariableValues(
-			productVariablesProperty,
-			moduleBase.variableProperties,
-			"",
-			"",
-			&productConfigProperties,
-			bazel.ConfigurationAxis{},
-		)
-
-		for axis, configToProps := range moduleBase.GetArchVariantProperties(ctx, moduleBase.variableProperties) {
+		for /* axis */ _, configToProps := range moduleBase.GetArchVariantProperties(ctx, moduleBase.variableProperties) {
 			for config, props := range configToProps {
-				// GetArchVariantProperties is creating an instance of the requested type
-				// and productVariablesValues expects an interface, so no need to cast
-				productVariableValues(
-					productVariablesProperty,
-					props,
-					"",
-					config,
-					&productConfigProperties,
-					axis)
+				variableValues := reflect.ValueOf(props).Elem().FieldByName(productVariablesProperty)
+				productConfigProperties.AddProductConfigProperties(variableValues, config)
 			}
 		}
 	}
@@ -704,13 +683,8 @@
 	if m, ok := module.(Bazelable); ok && m.namespacedVariableProps() != nil {
 		for namespace, namespacedVariableProps := range m.namespacedVariableProps() {
 			for _, namespacedVariableProp := range namespacedVariableProps {
-				productVariableValues(
-					soongconfig.SoongConfigProperty,
-					namespacedVariableProp,
-					namespace,
-					"",
-					&productConfigProperties,
-					bazel.NoConfigAxis)
+				variableValues := reflect.ValueOf(namespacedVariableProp).Elem().FieldByName(soongconfig.SoongConfigProperty)
+				productConfigProperties.AddSoongConfigProperties(namespace, variableValues)
 			}
 		}
 	}
@@ -719,30 +693,49 @@
 }
 
 func (p *ProductConfigProperties) AddProductConfigProperty(
-	propertyName, namespace, productVariableName, config string, property interface{}, outerAxis bazel.ConfigurationAxis) {
-	if (*p)[propertyName] == nil {
-		(*p)[propertyName] = make(map[ProductConfigProperty]interface{})
-	}
+	propertyName, productVariableName, arch string, propertyValue interface{}) {
 
 	productConfigProp := ProductConfigProperty{
-		Namespace:  namespace,           // e.g. acme, android
-		Name:       productVariableName, // e.g. size, feature1, feature2, FEATURE3, board
-		FullConfig: config,              // e.g. size, feature1-x86, size__conditions_default
-		OuterAxis:  outerAxis,
+		name: productVariableName, // e.g. size, feature1, feature2, FEATURE3, board
+		arch: arch,                // e.g. "", x86, arm64
 	}
 
-	if existing, ok := (*p)[propertyName][productConfigProp]; ok && namespace != "" {
+	p.AddEitherProperty(propertyName, productConfigProp, propertyValue)
+}
+
+func (p *ProductConfigProperties) AddSoongConfigProperty(
+	propertyName, namespace, variableName, value, os string, propertyValue interface{}) {
+
+	soongConfigProp := SoongConfigProperty{
+		namespace: namespace,
+		name:      variableName, // e.g. size, feature1, feature2, FEATURE3, board
+		value:     value,
+		os:        os, // e.g. android, linux_x86
+	}
+
+	p.AddEitherProperty(propertyName, soongConfigProp, propertyValue)
+}
+
+func (p *ProductConfigProperties) AddEitherProperty(
+	propertyName string, key ProductConfigOrSoongConfigProperty, propertyValue interface{}) {
+	if (*p)[propertyName] == nil {
+		(*p)[propertyName] = make(map[ProductConfigOrSoongConfigProperty]interface{})
+	}
+
+	if existing, ok := (*p)[propertyName][key]; ok {
 		switch dst := existing.(type) {
 		case []string:
-			if src, ok := property.([]string); ok {
-				dst = append(dst, src...)
-				(*p)[propertyName][productConfigProp] = dst
+			src, ok := propertyValue.([]string)
+			if !ok {
+				panic("Conflicting types")
 			}
+			dst = append(dst, src...)
+			(*p)[propertyName][key] = dst
 		default:
-			panic(fmt.Errorf("TODO: handle merging value %s", existing))
+			panic(fmt.Errorf("TODO: handle merging value %#v", existing))
 		}
 	} else {
-		(*p)[propertyName][productConfigProp] = property
+		(*p)[propertyName][key] = propertyValue
 	}
 }
 
@@ -779,10 +772,7 @@
 	return v, true
 }
 
-func (productConfigProperties *ProductConfigProperties) AddProductConfigProperties(namespace, suffix string, variableValues reflect.Value, outerAxis bazel.ConfigurationAxis) {
-	// variableValues can either be a product_variables or
-	// soong_config_variables struct.
-	//
+func (productConfigProperties *ProductConfigProperties) AddProductConfigProperties(variableValues reflect.Value, arch string) {
 	// Example of product_variables:
 	//
 	// product_variables: {
@@ -795,35 +785,7 @@
 	//         ],
 	//     },
 	// },
-	//
-	// Example of soong_config_variables:
-	//
-	// soong_config_variables: {
-	//      feature1: {
-	//        	conditions_default: {
-	//               ...
-	//          },
-	//          cflags: ...
-	//      },
-	//      feature2: {
-	//          cflags: ...
-	//        	conditions_default: {
-	//               ...
-	//          },
-	//      },
-	//      board: {
-	//         soc_a: {
-	//             ...
-	//         },
-	//         soc_a: {
-	//             ...
-	//         },
-	//         soc_c: {},
-	//         conditions_default: {
-	//              ...
-	//         },
-	//      },
-	// }
+
 	for i := 0; i < variableValues.NumField(); i++ {
 		// e.g. Platform_sdk_version, Unbundled_build, Malloc_not_svelte, etc.
 		productVariableName := variableValues.Type().Field(i).Name
@@ -835,25 +797,81 @@
 			continue
 		}
 
-		// Unlike product variables, config variables require a few more
-		// indirections to extract the struct from the reflect.Value.
-		if v, ok := maybeExtractConfigVarProp(variableValue); ok {
-			variableValue = v
-		}
-
 		for j := 0; j < variableValue.NumField(); j++ {
 			property := variableValue.Field(j)
 			// e.g. Asflags, Cflags, Enabled, etc.
 			propertyName := variableValue.Type().Field(j).Name
-			// config can also be "conditions_default".
-			config := proptools.PropertyNameForField(propertyName)
+			if property.Kind() != reflect.Interface {
+				productConfigProperties.AddProductConfigProperty(propertyName, productVariableName, arch, property.Interface())
+			}
+		}
+	}
+
+}
+
+func (productConfigProperties *ProductConfigProperties) AddSoongConfigProperties(namespace string, soongConfigVariablesStruct reflect.Value) {
+	//
+	// Example of soong_config_variables:
+	//
+	// soong_config_variables: {
+	//      feature1: {
+	//          conditions_default: {
+	//               ...
+	//          },
+	//          cflags: ...
+	//      },
+	//      feature2: {
+	//          cflags: ...
+	//          conditions_default: {
+	//               ...
+	//          },
+	//      },
+	//      board: {
+	//         soc_a: {
+	//             ...
+	//         },
+	//         soc_b: {
+	//             ...
+	//         },
+	//         soc_c: {},
+	//         conditions_default: {
+	//              ...
+	//         },
+	//      },
+	// }
+	for i := 0; i < soongConfigVariablesStruct.NumField(); i++ {
+		// e.g. feature1, feature2, board
+		variableName := soongConfigVariablesStruct.Type().Field(i).Name
+		variableStruct := soongConfigVariablesStruct.Field(i)
+		// Check if any properties were set for the module
+		if variableStruct.IsZero() {
+			// e.g. feature1: {}
+			continue
+		}
+
+		// Unlike product variables, config variables require a few more
+		// indirections to extract the struct from the reflect.Value.
+		if v, ok := maybeExtractConfigVarProp(variableStruct); ok {
+			variableStruct = v
+		} else if !v.IsValid() {
+			// Skip invalid variables which may not used, else leads to panic
+			continue
+		}
+
+		for j := 0; j < variableStruct.NumField(); j++ {
+			propertyOrStruct := variableStruct.Field(j)
+			// propertyOrValueName can either be:
+			//  - A property, like: Asflags, Cflags, Enabled, etc.
+			//  - A soong config string variable's value, like soc_a, soc_b, soc_c in the example above
+			//  - "conditions_default"
+			propertyOrValueName := variableStruct.Type().Field(j).Name
 
 			// If the property wasn't set, no need to pass it along
-			if property.IsZero() {
+			if propertyOrStruct.IsZero() {
 				continue
 			}
 
-			if v, ok := maybeExtractConfigVarProp(property); ok {
+			if v, ok := maybeExtractConfigVarProp(propertyOrStruct); ok {
 				// The field is a struct, which is used by:
 				// 1) soong_config_string_variables
 				//
@@ -871,6 +889,9 @@
 				//     cflags: ...,
 				//     static_libs: ...
 				// }
+				//
+				// This means that propertyOrValueName is either conditions_default, or a soong
+				// config string variable's value.
 				field := v
 				// Iterate over fields of this struct prop.
 				for k := 0; k < field.NumField(); k++ {
@@ -880,47 +901,59 @@
 					if field.Field(k).IsZero() && namespace == "" {
 						continue
 					}
-					actualPropertyName := field.Type().Field(k).Name
 
-					productConfigProperties.AddProductConfigProperty(
-						actualPropertyName,  // e.g. cflags, static_libs
-						namespace,           // e.g. acme, android
-						productVariableName, // e.g. size, feature1, FEATURE2, board
-						config,
-						field.Field(k).Interface(), // e.g. ["-DDEFAULT"], ["foo", "bar"],
-						outerAxis,
-					)
+					propertyName := field.Type().Field(k).Name
+					if propertyName == "Target" {
+						productConfigProperties.AddSoongConfigPropertiesFromTargetStruct(namespace, variableName, proptools.PropertyNameForField(propertyOrValueName), field.Field(k))
+					} else if propertyName == "Arch" || propertyName == "Multilib" {
+						panic("Arch/Multilib are not currently supported in soong config variable structs")
+					} else {
+						productConfigProperties.AddSoongConfigProperty(propertyName, namespace, variableName, proptools.PropertyNameForField(propertyOrValueName), "", field.Field(k).Interface())
+					}
 				}
-			} else if property.Kind() != reflect.Interface {
+			} else if propertyOrStruct.Kind() != reflect.Interface {
 				// If not an interface, then this is not a conditions_default or
-				// a struct prop. That is, this is a regular product variable,
-				// or a bool/value config variable.
-				config := productVariableName + suffix
-				productConfigProperties.AddProductConfigProperty(
-					propertyName,
-					namespace,
-					productVariableName,
-					config,
-					property.Interface(),
-					outerAxis,
-				)
+				// a struct prop. That is, this is a bool/value config variable.
+				if propertyOrValueName == "Target" {
+					productConfigProperties.AddSoongConfigPropertiesFromTargetStruct(namespace, variableName, "", propertyOrStruct)
+				} else if propertyOrValueName == "Arch" || propertyOrValueName == "Multilib" {
+					panic("Arch/Multilib are not currently supported in soong config variable structs")
+				} else {
+					productConfigProperties.AddSoongConfigProperty(propertyOrValueName, namespace, variableName, "", "", propertyOrStruct.Interface())
+				}
 			}
 		}
 	}
 }
 
-// productVariableValues uses reflection to convert a property struct for
-// product_variables and soong_config_variables to structs that can be generated
-// as select statements.
-func productVariableValues(
-	fieldName string, variableProps interface{}, namespace, suffix string, productConfigProperties *ProductConfigProperties, outerAxis bazel.ConfigurationAxis) {
-	if suffix != "" {
-		suffix = "-" + suffix
-	}
+func (productConfigProperties *ProductConfigProperties) AddSoongConfigPropertiesFromTargetStruct(namespace, soongConfigVariableName string, soongConfigVariableValue string, targetStruct reflect.Value) {
+	// targetStruct will be a struct with fields like "android", "host", "arm", "x86",
+	// "android_arm", etc. The values of each of those fields will be a regular property struct.
+	for i := 0; i < targetStruct.NumField(); i++ {
+		targetFieldName := targetStruct.Type().Field(i).Name
+		archOrOsSpecificStruct := targetStruct.Field(i)
+		for j := 0; j < archOrOsSpecificStruct.NumField(); j++ {
+			property := archOrOsSpecificStruct.Field(j)
+			// e.g. Asflags, Cflags, Enabled, etc.
+			propertyName := archOrOsSpecificStruct.Type().Field(j).Name
 
-	// variableValues represent the product_variables or soong_config_variables struct.
-	variableValues := reflect.ValueOf(variableProps).Elem().FieldByName(fieldName)
-	productConfigProperties.AddProductConfigProperties(namespace, suffix, variableValues, outerAxis)
+			if targetFieldName == "Android" {
+				productConfigProperties.AddSoongConfigProperty(propertyName, namespace, soongConfigVariableName, soongConfigVariableValue, "android", property.Interface())
+			} else if targetFieldName == "Host" {
+				for _, os := range osTypeList {
+					if os.Class == Host {
+						productConfigProperties.AddSoongConfigProperty(propertyName, namespace, soongConfigVariableName, soongConfigVariableValue, os.Name, property.Interface())
+					}
+				}
+			} else {
+				// One problem with supporting additional fields is that if multiple branches of
+				// "target" overlap, we don't want them to be in the same select statement (aka
+				// configuration axis). "android" and "host" are disjoint, so it's ok that we only
+				// have 2 axes right now. (soongConfigVariables and soongConfigVariablesPlusOs)
+				panic("TODO: support other target types in soong config variable structs: " + targetFieldName)
+			}
+		}
+	}
 }
 
 func VariableMutator(mctx BottomUpMutatorContext) {
diff --git a/android_sdk/sdk_repo_host.go b/android_sdk/sdk_repo_host.go
index 61058df..9623a8b 100644
--- a/android_sdk/sdk_repo_host.go
+++ b/android_sdk/sdk_repo_host.go
@@ -242,7 +242,7 @@
 			fmt.Fprintln(w, ".PHONY:", name, "sdk_repo", "sdk-repo-"+name)
 			fmt.Fprintln(w, "sdk_repo", "sdk-repo-"+name+":", strings.Join(s.FilesToInstall().Strings(), " "))
 
-			fmt.Fprintf(w, "$(call dist-for-goals,sdk_repo sdk-repo-%s,%s:%s-$(FILE_NAME_TAG).zip)\n\n", s.BaseModuleName(), s.outputFile.String(), s.outputBaseName)
+			fmt.Fprintf(w, "$(call dist-for-goals,sdk_repo sdk-repo-%s,%s:%s-FILE_NAME_TAG_PLACEHOLDER.zip)\n\n", s.BaseModuleName(), s.outputFile.String(), s.outputBaseName)
 		},
 	}
 }
diff --git a/apex/Android.bp b/apex/Android.bp
index 7ffca0e..0791497 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -29,7 +29,6 @@
         "bp2build.go",
         "deapexer.go",
         "key.go",
-        "metadata.go",
         "prebuilt.go",
         "testing.go",
         "vndk.go",
@@ -38,7 +37,7 @@
         "apex_test.go",
         "bootclasspath_fragment_test.go",
         "classpath_element_test.go",
-        "metadata_test.go",
+        "dexpreopt_bootjars_test.go",
         "platform_bootclasspath_test.go",
         "systemserver_classpath_fragment_test.go",
         "vndk_test.go",
diff --git a/apex/apex.go b/apex/apex.go
index 5451a04..32d7250 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -137,6 +137,10 @@
 	// List of filesystem images that are embedded inside this APEX bundle.
 	Filesystems []string
 
+	// The minimum SDK version that this APEX must support at minimum. This is usually set to
+	// the SDK version that the APEX was first introduced.
+	Min_sdk_version *string
+
 	// Whether this APEX is considered updatable or not. When set to true, this will enforce
 	// additional rules for making sure that the APEX is truly updatable. To be updatable,
 	// min_sdk_version should be set as well. This will also disable the size optimizations like
@@ -216,6 +220,13 @@
 	// imageApex or flattenedApex depending on Config.FlattenApex(). When payload_type is zip,
 	// this becomes zipApex.
 	ApexType apexPackaging `blueprint:"mutated"`
+
+	// Name that dependencies can specify in their apex_available properties to refer to this module.
+	// If not specified, this defaults to Soong module name. This must be the name of a Soong module.
+	Apex_available_name *string
+
+	// Variant version of the mainline module. Must be an integer between 0-9
+	Variant_version *string
 }
 
 type ApexNativeDependencies struct {
@@ -380,10 +391,6 @@
 
 	// Trim against a specific Dynamic Common Lib APEX
 	Trim_against *string
-
-	// The minimum SDK version that this APEX must support at minimum. This is usually set to
-	// the SDK version that the APEX was first introduced.
-	Min_sdk_version *string
 }
 
 type apexBundle struct {
@@ -404,7 +411,7 @@
 	///////////////////////////////////////////////////////////////////////////////////////////
 	// Inputs
 
-	// Keys for apex_paylaod.img
+	// Keys for apex_payload.img
 	publicKeyFile  android.Path
 	privateKeyFile android.Path
 
@@ -1064,6 +1071,10 @@
 
 	apexVariationName := mctx.ModuleName() // could be com.android.foo
 	a.properties.ApexVariationName = apexVariationName
+	testApexes := []string{}
+	if a.testApex {
+		testApexes = []string{apexVariationName}
+	}
 	apexInfo := android.ApexInfo{
 		ApexVariationName: apexVariationName,
 		MinSdkVersion:     minSdkVersion,
@@ -1072,6 +1083,7 @@
 		InApexVariants:    []string{apexVariationName},
 		InApexModules:     []string{a.Name()}, // could be com.mycompany.android.foo
 		ApexContents:      []*android.ApexContents{apexContents},
+		TestApexes:        testApexes,
 	}
 	mctx.WalkDeps(func(child, parent android.Module) bool {
 		if !continueApexDepsWalk(child, parent) {
@@ -1659,7 +1671,6 @@
 	if ccMod.Target().NativeBridge == android.NativeBridgeEnabled {
 		dirInApex = filepath.Join(dirInApex, ccMod.Target().NativeBridgeRelativePath)
 	}
-	dirInApex = filepath.Join(dirInApex, ccMod.RelativeInstallPath())
 	if handleSpecialLibs && cc.InstallToBootstrap(ccMod.BaseModuleName(), ctx.Config()) {
 		// Special case for Bionic libs and other libs installed with them. This is to
 		// prevent those libs from being included in the search path
@@ -1672,6 +1683,10 @@
 		// loading of them, which isn't supported.
 		dirInApex = filepath.Join(dirInApex, "bionic")
 	}
+	// This needs to go after the runtime APEX handling because otherwise we would get
+	// weird paths like lib64/rel_install_path/bionic rather than
+	// lib64/bionic/rel_install_path.
+	dirInApex = filepath.Join(dirInApex, ccMod.RelativeInstallPath())
 
 	fileToCopy := android.OutputFileForModule(ctx, ccMod, "")
 	androidMkModuleName := ccMod.BaseModuleName() + ccMod.Properties.SubName
@@ -1820,6 +1835,7 @@
 	Certificate() java.Certificate
 	BaseModuleName() string
 	LintDepSets() java.LintDepSets
+	PrivAppAllowlist() android.OptionalPath
 }
 
 var _ androidApp = (*java.AndroidApp)(nil)
@@ -1840,7 +1856,7 @@
 	return buildId
 }
 
-func apexFileForAndroidApp(ctx android.BaseModuleContext, aapp androidApp) apexFile {
+func apexFilesForAndroidApp(ctx android.BaseModuleContext, aapp androidApp) []apexFile {
 	appDir := "app"
 	if aapp.Privileged() {
 		appDir = "priv-app"
@@ -1862,7 +1878,18 @@
 	}); ok {
 		af.overriddenPackageName = app.OverriddenManifestPackageName()
 	}
-	return af
+
+	apexFiles := []apexFile{}
+
+	if allowlist := aapp.PrivAppAllowlist(); allowlist.Valid() {
+		dirInApex := filepath.Join("etc", "permissions")
+		privAppAllowlist := newApexFile(ctx, allowlist.Path(), aapp.BaseModuleName()+"_privapp", dirInApex, etc, aapp)
+		apexFiles = append(apexFiles, privAppAllowlist)
+	}
+
+	apexFiles = append(apexFiles, af)
+
+	return apexFiles
 }
 
 func apexFileForRuntimeResourceOverlay(ctx android.BaseModuleContext, rro java.RuntimeResourceOverlayModule) apexFile {
@@ -1956,9 +1983,6 @@
 // overridden by different override_apex modules (e.g. Google or Go variants),
 // which is handled by the overrides mutators.
 func (a *apexBundle) GetBazelLabel(ctx android.BazelConversionPathContext, module blueprint.Module) string {
-	if _, ok := ctx.Module().(android.OverridableModule); ok {
-		return android.MaybeBp2buildLabelOfOverridingModule(ctx)
-	}
 	return a.BazelModuleBase.GetBazelLabel(ctx, a)
 }
 
@@ -2273,16 +2297,13 @@
 				ctx.PropertyErrorf("sh_binaries", "%q is not a sh_binary module", depName)
 			}
 		case bcpfTag:
-			bcpfModule, ok := child.(*java.BootclasspathFragmentModule)
+			_, ok := child.(*java.BootclasspathFragmentModule)
 			if !ok {
 				ctx.PropertyErrorf("bootclasspath_fragments", "%q is not a bootclasspath_fragment module", depName)
 				return false
 			}
 
 			vctx.filesInfo = append(vctx.filesInfo, apexBootclasspathFragmentFiles(ctx, child)...)
-			for _, makeModuleName := range bcpfModule.BootImageDeviceInstallMakeModules() {
-				a.makeModulesToInstall = append(a.makeModulesToInstall, makeModuleName)
-			}
 			return true
 		case sscpfTag:
 			if _, ok := child.(*java.SystemServerClasspathModule); !ok {
@@ -2310,12 +2331,12 @@
 		case androidAppTag:
 			switch ap := child.(type) {
 			case *java.AndroidApp:
-				vctx.filesInfo = append(vctx.filesInfo, apexFileForAndroidApp(ctx, ap))
+				vctx.filesInfo = append(vctx.filesInfo, apexFilesForAndroidApp(ctx, ap)...)
 				return true // track transitive dependencies
 			case *java.AndroidAppImport:
-				vctx.filesInfo = append(vctx.filesInfo, apexFileForAndroidApp(ctx, ap))
+				vctx.filesInfo = append(vctx.filesInfo, apexFilesForAndroidApp(ctx, ap)...)
 			case *java.AndroidTestHelperApp:
-				vctx.filesInfo = append(vctx.filesInfo, apexFileForAndroidApp(ctx, ap))
+				vctx.filesInfo = append(vctx.filesInfo, apexFilesForAndroidApp(ctx, ap)...)
 			case *java.AndroidAppSet:
 				appDir := "app"
 				if ap.Privileged() {
@@ -2595,8 +2616,45 @@
 	ctx.WalkDepsBlueprint(func(child, parent blueprint.Module) bool { return a.depVisitor(&vctx, ctx, child, parent) })
 	vctx.normalizeFileInfo(ctx)
 	if a.privateKeyFile == nil {
-		ctx.PropertyErrorf("key", "private_key for %q could not be found", String(a.overridableProperties.Key))
-		return
+		if ctx.Config().AllowMissingDependencies() {
+			// TODO(b/266099037): a better approach for slim manifests.
+			ctx.AddMissingDependencies([]string{String(a.overridableProperties.Key)})
+			// Create placeholder paths for later stages that expect to see those paths,
+			// though they won't be used.
+			var unusedPath = android.PathForModuleOut(ctx, "nonexistentprivatekey")
+			ctx.Build(pctx, android.BuildParams{
+				Rule:   android.ErrorRule,
+				Output: unusedPath,
+				Args: map[string]string{
+					"error": "Private key not available",
+				},
+			})
+			a.privateKeyFile = unusedPath
+		} else {
+			ctx.PropertyErrorf("key", "private_key for %q could not be found", String(a.overridableProperties.Key))
+			return
+		}
+	}
+
+	if a.publicKeyFile == nil {
+		if ctx.Config().AllowMissingDependencies() {
+			// TODO(b/266099037): a better approach for slim manifests.
+			ctx.AddMissingDependencies([]string{String(a.overridableProperties.Key)})
+			// Create placeholder paths for later stages that expect to see those paths,
+			// though they won't be used.
+			var unusedPath = android.PathForModuleOut(ctx, "nonexistentpublickey")
+			ctx.Build(pctx, android.BuildParams{
+				Rule:   android.ErrorRule,
+				Output: unusedPath,
+				Args: map[string]string{
+					"error": "Public key not available",
+				},
+			})
+			a.publicKeyFile = unusedPath
+		} else {
+			ctx.PropertyErrorf("key", "public_key for %q could not be found", String(a.overridableProperties.Key))
+			return
+		}
 	}
 
 	////////////////////////////////////////////////////////////////////////////////////////////
@@ -2648,26 +2706,13 @@
 	bootclasspathFragmentInfo := ctx.OtherModuleProvider(module, java.BootclasspathFragmentApexContentInfoProvider).(java.BootclasspathFragmentApexContentInfo)
 	var filesToAdd []apexFile
 
-	// Add the boot image files, e.g. .art, .oat and .vdex files.
-	if bootclasspathFragmentInfo.ShouldInstallBootImageInApex() {
-		for arch, files := range bootclasspathFragmentInfo.AndroidBootImageFilesByArchType() {
-			dirInApex := filepath.Join("javalib", arch.String())
-			for _, f := range files {
-				androidMkModuleName := "javalib_" + arch.String() + "_" + filepath.Base(f.String())
-				// TODO(b/177892522) - consider passing in the bootclasspath fragment module here instead of nil
-				af := newApexFile(ctx, f, androidMkModuleName, dirInApex, etc, nil)
-				filesToAdd = append(filesToAdd, af)
-			}
-		}
-	}
-
 	// Add classpaths.proto config.
 	if af := apexClasspathFragmentProtoFile(ctx, module); af != nil {
 		filesToAdd = append(filesToAdd, *af)
 	}
 
 	pathInApex := bootclasspathFragmentInfo.ProfileInstallPathInApex()
-	if pathInApex != "" && !java.SkipDexpreoptBootJars(ctx) {
+	if pathInApex != "" {
 		pathOnHost := bootclasspathFragmentInfo.ProfilePathOnHost()
 		tempPath := android.PathForModuleOut(ctx, "boot_image_profile", pathInApex)
 
@@ -2937,7 +2982,7 @@
 	// Only override the minSdkVersion value on Apexes which already specify
 	// a min_sdk_version (it's optional for non-updatable apexes), and that its
 	// min_sdk_version value is lower than the one to override with.
-	minApiLevel := minSdkVersionFromValue(ctx, proptools.String(a.overridableProperties.Min_sdk_version))
+	minApiLevel := minSdkVersionFromValue(ctx, proptools.String(a.properties.Min_sdk_version))
 	if minApiLevel.IsNone() {
 		return ""
 	}
@@ -3096,6 +3141,13 @@
 		}
 
 		apexName := ctx.ModuleName()
+		for _, props := range ctx.Module().GetProperties() {
+			if apexProps, ok := props.(*apexBundleProperties); ok {
+				if apexProps.Apex_available_name != nil {
+					apexName = *apexProps.Apex_available_name
+				}
+			}
+		}
 		fromName := ctx.OtherModuleName(from)
 		toName := ctx.OtherModuleName(to)
 
@@ -3228,7 +3280,6 @@
 	//
 	m["com.android.appsearch"] = []string{
 		"icing-java-proto-lite",
-		"libprotobuf-java-lite",
 	}
 	//
 	// Module separator
@@ -3239,16 +3290,13 @@
 	//
 	// Module separator
 	//
-	m["com.android.cellbroadcast"] = []string{"CellBroadcastApp", "CellBroadcastServiceModule"}
+	m["com.android.cellbroadcast"] = []string{}
 	//
 	// Module separator
 	//
 	m["com.android.extservices"] = []string{
-		"error_prone_annotations",
 		"ExtServices-core",
-		"ExtServices",
 		"libtextclassifier-java",
-		"libz_current",
 		"textclassifier-statsd",
 		"TextClassifierNotificationLibNoManifest",
 		"TextClassifierServiceLibNoManifest",
@@ -3266,8 +3314,6 @@
 		"android.hidl.memory@1.0",
 		"android.hidl.safe_union@1.0",
 		"libarect",
-		"libbuildversion",
-		"libmath",
 		"libprocpartition",
 	}
 	//
@@ -3297,15 +3343,12 @@
 	// Module separator
 	//
 	m["com.android.runtime"] = []string{
-		"bionic_libc_platform_headers",
-		"libarm-optimized-routines-math",
 		"libc_aeabi",
 		"libc_bionic",
 		"libc_bionic_ndk",
 		"libc_bootstrap",
 		"libc_common",
 		"libc_common_shared",
-		"libc_common_static",
 		"libc_dns",
 		"libc_dynamic_dispatch",
 		"libc_fortify",
@@ -3322,19 +3365,16 @@
 		"libc_openbsd_large_stack",
 		"libc_openbsd_ndk",
 		"libc_pthread",
-		"libc_static_dispatch",
 		"libc_syscalls",
 		"libc_tzcode",
 		"libc_unwind_static",
 		"libdebuggerd",
 		"libdebuggerd_common_headers",
 		"libdebuggerd_handler_core",
-		"libdebuggerd_handler_fallback",
 		"libdl_static",
 		"libjemalloc5",
 		"liblinker_main",
 		"liblinker_malloc",
-		"liblz4",
 		"liblzma",
 		"libprocinfo",
 		"libpropertyinfoparser",
@@ -3352,17 +3392,7 @@
 	m["com.android.tethering"] = []string{
 		"android.hardware.tetheroffload.config-V1.0-java",
 		"android.hardware.tetheroffload.control-V1.0-java",
-		"android.hidl.base-V1.0-java",
-		"libcgrouprc",
-		"libcgrouprc_format",
-		"libtetherutilsjni",
-		"libvndksupport",
 		"net-utils-framework-common",
-		"netd_aidl_interface-V3-java",
-		"netlink-client",
-		"networkstack-aidl-interfaces-java",
-		"tethering-aidl-interfaces-java",
-		"TetheringApiCurrentLib",
 	}
 	//
 	// Module separator
@@ -3382,57 +3412,22 @@
 		"android.hardware.wifi.supplicant-V1.1-java",
 		"android.hardware.wifi.supplicant-V1.2-java",
 		"android.hardware.wifi.supplicant-V1.3-java",
-		"android.hidl.base-V1.0-java",
-		"android.hidl.manager-V1.0-java",
-		"android.hidl.manager-V1.1-java",
-		"android.hidl.manager-V1.2-java",
 		"bouncycastle-unbundled",
-		"dnsresolver_aidl_interface-V2-java",
-		"error_prone_annotations",
-		"framework-wifi-pre-jarjar",
 		"framework-wifi-util-lib",
-		"ipmemorystore-aidl-interfaces-V3-java",
-		"ipmemorystore-aidl-interfaces-java",
 		"ksoap2",
 		"libnanohttpd",
-		"libwifi-jni",
-		"net-utils-services-common",
-		"netd_aidl_interface-V2-java",
-		"netd_aidl_interface-unstable-java",
-		"netd_event_listener_interface-java",
-		"netlink-client",
-		"networkstack-client",
-		"services.net",
 		"wifi-lite-protos",
 		"wifi-nano-protos",
 		"wifi-service-pre-jarjar",
-		"wifi-service-resources",
-	}
-	//
-	// Module separator
-	//
-	m["com.android.os.statsd"] = []string{
-		"libstatssocket",
 	}
 	//
 	// Module separator
 	//
 	m[android.AvailableToAnyApex] = []string{
-		// TODO(b/156996905) Set apex_available/min_sdk_version for androidx/extras support libraries
-		"androidx",
-		"androidx-constraintlayout_constraintlayout",
-		"androidx-constraintlayout_constraintlayout-nodeps",
-		"androidx-constraintlayout_constraintlayout-solver",
-		"androidx-constraintlayout_constraintlayout-solver-nodeps",
-		"com.google.android.material_material",
-		"com.google.android.material_material-nodeps",
-
-		"libclang_rt",
 		"libprofile-clang-extras",
 		"libprofile-clang-extras_ndk",
 		"libprofile-extras",
 		"libprofile-extras_ndk",
-		"libunwind",
 	}
 	return m
 }
@@ -3535,6 +3530,8 @@
 	Logging_parent        *string
 	Tests                 bazel.LabelListAttribute
 	Base_apex_name        *string
+	Apex_available_name   *string
+	Variant_version       *string
 }
 
 type convertedNativeSharedLibs struct {
@@ -3588,8 +3585,8 @@
 	// TODO(b/219503907) this would need to be set to a.MinSdkVersionValue(ctx) but
 	// given it's coming via config, we probably don't want to put it in here.
 	var minSdkVersion bazel.StringAttribute
-	if a.overridableProperties.Min_sdk_version != nil {
-		minSdkVersion.SetValue(*a.overridableProperties.Min_sdk_version)
+	if a.properties.Min_sdk_version != nil {
+		minSdkVersion.SetValue(*a.properties.Min_sdk_version)
 	}
 	if props, ok := productVariableProps[minSdkVersionPropName]; ok {
 		for c, p := range props {
@@ -3685,6 +3682,8 @@
 		Package_name:          packageName,
 		Logging_parent:        loggingParent,
 		Tests:                 testsAttrs,
+		Apex_available_name:   a.properties.Apex_available_name,
+		Variant_version:       a.properties.Variant_version,
 	}
 
 	props := bazel.BazelTargetModuleProperties{
@@ -3695,6 +3694,8 @@
 	commonAttrs := android.CommonAttributes{}
 	if a.testApex {
 		commonAttrs.Testonly = proptools.BoolPtr(true)
+		// Set the api_domain of the test apex
+		attrs.Base_apex_name = proptools.StringPtr(cc.GetApiDomain(a.Name()))
 	}
 
 	return attrs, props, commonAttrs
@@ -3803,3 +3804,7 @@
 func invalidCompileMultilib(ctx android.TopDownMutatorContext, value string) {
 	ctx.PropertyErrorf("compile_multilib", "Invalid value: %s", value)
 }
+
+func (a *apexBundle) IsTestApex() bool {
+	return a.testApex
+}
diff --git a/apex/apex_singleton.go b/apex/apex_singleton.go
index 1581949..a63344f 100644
--- a/apex/apex_singleton.go
+++ b/apex/apex_singleton.go
@@ -23,7 +23,11 @@
 )
 
 func init() {
-	android.RegisterSingletonType("apex_depsinfo_singleton", apexDepsInfoSingletonFactory)
+	registerApexDepsInfoComponents(android.InitRegistrationContext)
+}
+
+func registerApexDepsInfoComponents(ctx android.RegistrationContext) {
+	ctx.RegisterParallelSingletonType("apex_depsinfo_singleton", apexDepsInfoSingletonFactory)
 }
 
 type apexDepsInfoSingleton struct {
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 557265c..149b177 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -33,6 +33,7 @@
 	"android/soong/cc"
 	"android/soong/dexpreopt"
 	prebuilt_etc "android/soong/etc"
+	"android/soong/filesystem"
 	"android/soong/java"
 	"android/soong/rust"
 	"android/soong/sh"
@@ -146,7 +147,7 @@
 	android.PrepareForTestWithAndroidBuildComponents,
 	bpf.PrepareForTestWithBpf,
 	cc.PrepareForTestWithCcBuildComponents,
-	java.PrepareForTestWithJavaDefaultModules,
+	java.PrepareForTestWithDexpreopt,
 	prebuilt_etc.PrepareForTestWithPrebuiltEtc,
 	rust.PrepareForTestWithRustDefaultModules,
 	sh.PrepareForTestWithShBuildComponents,
@@ -785,18 +786,16 @@
 }
 
 func TestFileContexts(t *testing.T) {
-	for _, useFileContextsAsIs := range []bool{true, false} {
+	for _, vendor := range []bool{true, false} {
 		prop := ""
-		if useFileContextsAsIs {
-			prop = "use_file_contexts_as_is: true,\n"
+		if vendor {
+			prop = "vendor: true,\n"
 		}
 		ctx := testApex(t, `
 			apex {
 				name: "myapex",
 				key: "myapex.key",
-				file_contexts: "file_contexts",
 				updatable: false,
-				vendor: true,
 				`+prop+`
 			}
 
@@ -805,18 +804,17 @@
 				public_key: "testkey.avbpubkey",
 				private_key: "testkey.pem",
 			}
-		`, withFiles(map[string][]byte{
-			"file_contexts": nil,
-		}))
+		`)
 
 		rule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Output("file_contexts")
-		forceLabellingCommand := "apex_manifest\\\\.pb u:object_r:system_file:s0"
-		if useFileContextsAsIs {
-			android.AssertStringDoesNotContain(t, "should force-label",
-				rule.RuleParams.Command, forceLabellingCommand)
+		if vendor {
+			android.AssertStringDoesContain(t, "should force-label as vendor_apex_metadata_file",
+				rule.RuleParams.Command,
+				"apex_manifest\\\\.pb u:object_r:vendor_apex_metadata_file:s0")
 		} else {
-			android.AssertStringDoesContain(t, "shouldn't force-label",
-				rule.RuleParams.Command, forceLabellingCommand)
+			android.AssertStringDoesContain(t, "should force-label as system_file",
+				rule.RuleParams.Command,
+				"apex_manifest\\\\.pb u:object_r:system_file:s0")
 		}
 	}
 }
@@ -1367,6 +1365,8 @@
 		cc_library {
 			name: "mylib",
 			srcs: ["mylib.cpp"],
+			static_libs: ["libstatic"],
+			shared_libs: ["libshared"],
 			runtime_libs: ["libfoo", "libbar"],
 			system_shared_libs: [],
 			stl: "none",
@@ -1391,6 +1391,39 @@
 			apex_available: [ "myapex" ],
 		}
 
+		cc_library {
+			name: "libstatic",
+			srcs: ["mylib.cpp"],
+			system_shared_libs: [],
+			stl: "none",
+			apex_available: [ "myapex" ],
+			runtime_libs: ["libstatic_to_runtime"],
+		}
+
+		cc_library {
+			name: "libshared",
+			srcs: ["mylib.cpp"],
+			system_shared_libs: [],
+			stl: "none",
+			apex_available: [ "myapex" ],
+			runtime_libs: ["libshared_to_runtime"],
+		}
+
+		cc_library {
+			name: "libstatic_to_runtime",
+			srcs: ["mylib.cpp"],
+			system_shared_libs: [],
+			stl: "none",
+			apex_available: [ "myapex" ],
+		}
+
+		cc_library {
+			name: "libshared_to_runtime",
+			srcs: ["mylib.cpp"],
+			system_shared_libs: [],
+			stl: "none",
+			apex_available: [ "myapex" ],
+		}
 	`)
 
 	apexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexRule")
@@ -1404,11 +1437,14 @@
 
 	// Ensure that runtime_libs dep in included
 	ensureContains(t, copyCmds, "image.apex/lib64/libbar.so")
+	ensureContains(t, copyCmds, "image.apex/lib64/libshared.so")
+	ensureContains(t, copyCmds, "image.apex/lib64/libshared_to_runtime.so")
+
+	ensureNotContains(t, copyCmds, "image.apex/lib64/libstatic_to_runtime.so")
 
 	apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexManifestRule")
 	ensureListEmpty(t, names(apexManifestRule.Args["provideNativeLibs"]))
 	ensureListContains(t, names(apexManifestRule.Args["requireNativeLibs"]), "libfoo.so")
-
 }
 
 var prepareForTestOfRuntimeApexWithHwasan = android.GroupFixturePreparers(
@@ -1460,6 +1496,10 @@
 			sanitize: {
 				never: true,
 			},
+			apex_available: [
+				"//apex_available:anyapex",
+				"//apex_available:platform",
+			],
 		}	`)
 	ctx := result.TestContext
 
@@ -1508,6 +1548,10 @@
 			sanitize: {
 				never: true,
 			},
+			apex_available: [
+				"//apex_available:anyapex",
+				"//apex_available:platform",
+			],
 		}
 		`)
 	ctx := result.TestContext
@@ -1980,6 +2024,93 @@
 	android.AssertStringDoesContain(t, "cflags", cflags, "-target aarch64-linux-android29")
 }
 
+func TestTrackAllowedDeps(t *testing.T) {
+	ctx := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			updatable: true,
+			native_shared_libs: [
+				"mylib",
+				"yourlib",
+			],
+			min_sdk_version: "29",
+		}
+
+		apex {
+			name: "myapex2",
+			key: "myapex.key",
+			updatable: false,
+			native_shared_libs: ["yourlib"],
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "mylib",
+			srcs: ["mylib.cpp"],
+			shared_libs: ["libbar"],
+			min_sdk_version: "29",
+			apex_available: ["myapex"],
+		}
+
+		cc_library {
+			name: "libbar",
+			stubs: { versions: ["29", "30"] },
+		}
+
+		cc_library {
+			name: "yourlib",
+			srcs: ["mylib.cpp"],
+			min_sdk_version: "29",
+			apex_available: ["myapex", "myapex2", "//apex_available:platform"],
+		}
+	`, withFiles(android.MockFS{
+		"packages/modules/common/build/allowed_deps.txt": nil,
+	}))
+
+	depsinfo := ctx.SingletonForTests("apex_depsinfo_singleton")
+	inputs := depsinfo.Rule("generateApexDepsInfoFilesRule").BuildParams.Inputs.Strings()
+	android.AssertStringListContains(t, "updatable myapex should generate depsinfo file", inputs,
+		"out/soong/.intermediates/myapex/android_common_myapex_image/depsinfo/flatlist.txt")
+	android.AssertStringListDoesNotContain(t, "non-updatable myapex2 should not generate depsinfo file", inputs,
+		"out/soong/.intermediates/myapex2/android_common_myapex2_image/depsinfo/flatlist.txt")
+
+	myapex := ctx.ModuleForTests("myapex", "android_common_myapex_image")
+	flatlist := strings.Split(myapex.Output("depsinfo/flatlist.txt").BuildParams.Args["content"], "\\n")
+	android.AssertStringListContains(t, "deps with stubs should be tracked in depsinfo as external dep",
+		flatlist, "libbar(minSdkVersion:(no version)) (external)")
+	android.AssertStringListDoesNotContain(t, "do not track if not available for platform",
+		flatlist, "mylib:(minSdkVersion:29)")
+	android.AssertStringListContains(t, "track platform-available lib",
+		flatlist, "yourlib(minSdkVersion:29)")
+}
+
+func TestTrackAllowedDeps_SkipWithoutAllowedDepsTxt(t *testing.T) {
+	ctx := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			updatable: true,
+			min_sdk_version: "29",
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+	`)
+	depsinfo := ctx.SingletonForTests("apex_depsinfo_singleton")
+	if nil != depsinfo.MaybeRule("generateApexDepsInfoFilesRule").Output {
+		t.Error("apex_depsinfo_singleton shouldn't run when allowed_deps.txt doesn't exist")
+	}
+}
+
 func TestPlatformUsesLatestStubsFromApexes(t *testing.T) {
 	ctx := testApex(t, `
 		apex {
@@ -3131,10 +3262,7 @@
 			stubs: {
 				versions: ["1", "2", "3"],
 			},
-			apex_available: [
-				"//apex_available:platform",
-				"myapex",
-			],
+			apex_available: ["myapex"],
 		}
 
 		cc_binary {
@@ -4102,6 +4230,174 @@
 	})
 }
 
+func TestVendorApexWithVndkPrebuilts(t *testing.T) {
+	ctx := testApex(t, "",
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.DeviceVndkVersion = proptools.StringPtr("27")
+		}),
+		android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+			cc.RegisterVendorSnapshotModules(ctx)
+		}),
+		withFiles(map[string][]byte{
+			"vendor/foo/Android.bp": []byte(`
+				apex {
+					name: "myapex",
+					binaries: ["foo"],
+					key: "myapex.key",
+					min_sdk_version: "27",
+					vendor: true,
+				}
+
+				cc_binary {
+					name: "foo",
+					vendor: true,
+					srcs: ["abc.cpp"],
+					shared_libs: [
+						"libllndk",
+						"libvndk",
+					],
+					nocrt: true,
+					system_shared_libs: [],
+					min_sdk_version: "27",
+				}
+
+				apex_key {
+					name: "myapex.key",
+					public_key: "testkey.avbpubkey",
+					private_key: "testkey.pem",
+				}
+			`),
+			// Simulate VNDK prebuilts with vendor_snapshot
+			"prebuilts/vndk/Android.bp": []byte(`
+				vndk_prebuilt_shared {
+					name: "libllndk",
+					version: "27",
+					vendor_available: true,
+					product_available: true,
+					target_arch: "arm64",
+					arch: {
+						arm64: {
+							srcs: ["libllndk.so"],
+						},
+					},
+				}
+
+				vndk_prebuilt_shared {
+					name: "libvndk",
+					version: "27",
+					vendor_available: true,
+					product_available: true,
+					target_arch: "arm64",
+					arch: {
+						arm64: {
+							srcs: ["libvndk.so"],
+						},
+					},
+					vndk: {
+						enabled: true,
+					},
+					min_sdk_version: "27",
+				}
+
+				vndk_prebuilt_shared {
+					name: "libc++",
+					version: "27",
+					target_arch: "arm64",
+					vendor_available: true,
+					product_available: true,
+					vndk: {
+						enabled: true,
+						support_system_process: true,
+					},
+					arch: {
+						arm64: {
+							srcs: ["libc++.so"],
+						},
+					},
+					min_sdk_version: "apex_inherit",
+				}
+
+				vendor_snapshot {
+					name: "vendor_snapshot",
+					version: "27",
+					arch: {
+						arm64: {
+							vndk_libs: [
+								"libc++",
+								"libllndk",
+								"libvndk",
+							],
+							static_libs: [
+								"libc++demangle",
+								"libclang_rt.builtins",
+								"libunwind",
+							],
+						},
+					}
+				}
+
+				vendor_snapshot_static {
+					name: "libclang_rt.builtins",
+					version: "27",
+					target_arch: "arm64",
+					vendor: true,
+					arch: {
+						arm64: {
+							src: "libclang_rt.builtins-aarch64-android.a",
+						},
+					},
+				}
+
+				vendor_snapshot_static {
+					name: "libc++demangle",
+					version: "27",
+					target_arch: "arm64",
+					compile_multilib: "64",
+					vendor: true,
+					arch: {
+						arm64: {
+							src: "libc++demangle.a",
+						},
+					},
+					min_sdk_version: "apex_inherit",
+				}
+
+				vendor_snapshot_static {
+					name: "libunwind",
+					version: "27",
+					target_arch: "arm64",
+					compile_multilib: "64",
+					vendor: true,
+					arch: {
+						arm64: {
+							src: "libunwind.a",
+						},
+					},
+					min_sdk_version: "apex_inherit",
+				}
+			`),
+		}))
+
+	// Should embed the prebuilt VNDK libraries in the apex
+	ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
+		"bin/foo",
+		"prebuilts/vndk/libc++.so:lib64/libc++.so",
+		"prebuilts/vndk/libvndk.so:lib64/libvndk.so",
+	})
+
+	// Should link foo with prebuilt libraries (shared/static)
+	ldRule := ctx.ModuleForTests("foo", "android_vendor.27_arm64_armv8-a_myapex").Rule("ld")
+	android.AssertStringDoesContain(t, "should link to prebuilt llndk", ldRule.Args["libFlags"], "prebuilts/vndk/libllndk.so")
+	android.AssertStringDoesContain(t, "should link to prebuilt vndk", ldRule.Args["libFlags"], "prebuilts/vndk/libvndk.so")
+	android.AssertStringDoesContain(t, "should link to prebuilt libc++demangle", ldRule.Args["libFlags"], "prebuilts/vndk/libc++demangle.a")
+	android.AssertStringDoesContain(t, "should link to prebuilt libunwind", ldRule.Args["libFlags"], "prebuilts/vndk/libunwind.a")
+
+	// Should declare the LLNDK library as a "required" external dependency
+	manifestRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexManifestRule")
+	requireNativeLibs := names(manifestRule.Args["requireNativeLibs"])
+	ensureListContains(t, requireNativeLibs, "libllndk.so")
+}
+
 func TestDependenciesInApexManifest(t *testing.T) {
 	ctx := testApex(t, `
 		apex {
@@ -4134,7 +4430,7 @@
 		apex {
 			name: "myapex_selfcontained",
 			key: "myapex.key",
-			native_shared_libs: ["lib_dep", "libfoo"],
+			native_shared_libs: ["lib_dep_on_bar", "libbar"],
 			compile_multilib: "both",
 			file_contexts: ":myapex-file_contexts",
 			updatable: false,
@@ -4168,6 +4464,18 @@
 		}
 
 		cc_library {
+			name: "lib_dep_on_bar",
+			srcs: ["mylib.cpp"],
+			shared_libs: ["libbar"],
+			system_shared_libs: [],
+			stl: "none",
+			apex_available: [
+				"myapex_selfcontained",
+			],
+		}
+
+
+		cc_library {
 			name: "libfoo",
 			srcs: ["mytest.cpp"],
 			stubs: {
@@ -4177,9 +4485,22 @@
 			stl: "none",
 			apex_available: [
 				"myapex_provider",
+			],
+		}
+
+		cc_library {
+			name: "libbar",
+			srcs: ["mytest.cpp"],
+			stubs: {
+				versions: ["1"],
+			},
+			system_shared_libs: [],
+			stl: "none",
+			apex_available: [
 				"myapex_selfcontained",
 			],
 		}
+
 	`)
 
 	var apexManifestRule android.TestingBuildParams
@@ -4206,7 +4527,7 @@
 	apexManifestRule = ctx.ModuleForTests("myapex_selfcontained", "android_common_myapex_selfcontained_image").Rule("apexManifestRule")
 	provideNativeLibs = names(apexManifestRule.Args["provideNativeLibs"])
 	requireNativeLibs = names(apexManifestRule.Args["requireNativeLibs"])
-	ensureListContains(t, provideNativeLibs, "libfoo.so")
+	ensureListContains(t, provideNativeLibs, "libbar.so")
 	ensureListEmpty(t, requireNativeLibs)
 }
 
@@ -5281,7 +5602,16 @@
 		apex_set {
 			name: "myapex",
 			set: "myapex.apks",
+			exported_java_libs: ["myjavalib"],
 			exported_bootclasspath_fragments: ["my-bootclasspath-fragment"],
+			exported_systemserverclasspath_fragments: ["my-systemserverclasspath-fragment"],
+		}
+
+		java_import {
+			name: "myjavalib",
+			jars: ["myjavalib.jar"],
+			apex_available: ["myapex"],
+			permitted_packages: ["javalib"],
 		}
 
 		prebuilt_bootclasspath_fragment {
@@ -5298,6 +5628,12 @@
 			},
 		}
 
+		prebuilt_systemserverclasspath_fragment {
+			name: "my-systemserverclasspath-fragment",
+			contents: ["libbaz"],
+			apex_available: ["myapex"],
+		}
+
 		java_import {
 			name: "libfoo",
 			jars: ["libfoo.jar"],
@@ -5314,6 +5650,16 @@
 			shared_library: false,
 			permitted_packages: ["bar"],
 		}
+
+		java_sdk_library_import {
+			name: "libbaz",
+			public: {
+				jars: ["libbaz.jar"],
+			},
+			apex_available: ["myapex"],
+			shared_library: false,
+			permitted_packages: ["baz"],
+		}
 	`
 
 		ctx := testDexpreoptWithApexes(t, bp, "", preparer, fragment)
@@ -5326,6 +5672,24 @@
 			my-bootclasspath-fragment/index.csv
 			out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/hiddenapi-monolithic/index-from-classes.csv
 		`)
+
+		myApex := ctx.ModuleForTests("myapex", "android_common_myapex").Module()
+
+		overrideNames := []string{
+			"",
+			"myjavalib.myapex",
+			"libfoo.myapex",
+			"libbar.myapex",
+			"libbaz.myapex",
+		}
+		mkEntries := android.AndroidMkEntriesForTest(t, ctx, myApex)
+		for i, e := range mkEntries {
+			g := e.OverrideName
+			if w := overrideNames[i]; w != g {
+				t.Errorf("Expected override name %q, got %q", w, g)
+			}
+		}
+
 	})
 
 	t.Run("prebuilt with source library preferred", func(t *testing.T) {
@@ -5893,6 +6257,7 @@
 			sdk_version: "current",
 			system_modules: "none",
 			privileged: true,
+			privapp_allowlist: "privapp_allowlist_com.android.AppFooPriv.xml",
 			stl: "none",
 			apex_available: [ "myapex" ],
 		}
@@ -5922,6 +6287,7 @@
 
 	ensureContains(t, copyCmds, "image.apex/app/AppFoo@TEST.BUILD_ID/AppFoo.apk")
 	ensureContains(t, copyCmds, "image.apex/priv-app/AppFooPriv@TEST.BUILD_ID/AppFooPriv.apk")
+	ensureContains(t, copyCmds, "image.apex/etc/permissions/privapp_allowlist_com.android.AppFooPriv.xml")
 
 	appZipRule := ctx.ModuleForTests("AppFoo", "android_common_apex10000").Description("zip jni libs")
 	// JNI libraries are uncompressed
@@ -5936,6 +6302,18 @@
 		// ... and not directly inside the APEX
 		ensureNotContains(t, copyCmds, "image.apex/lib64/"+jni+".so")
 	}
+
+	apexBundle := module.Module().(*apexBundle)
+	data := android.AndroidMkDataForTest(t, ctx, apexBundle)
+	var builder strings.Builder
+	data.Custom(&builder, apexBundle.Name(), "TARGET_", "", data)
+	androidMk := builder.String()
+	ensureContains(t, androidMk, "LOCAL_MODULE := AppFooPriv.myapex")
+	ensureContains(t, androidMk, "LOCAL_MODULE := AppFoo.myapex")
+	ensureMatches(t, androidMk, "LOCAL_SOONG_INSTALLED_MODULE := \\S+AppFooPriv.apk")
+	ensureMatches(t, androidMk, "LOCAL_SOONG_INSTALLED_MODULE := \\S+AppFoo.apk")
+	ensureMatches(t, androidMk, "LOCAL_SOONG_INSTALL_PAIRS := \\S+AppFooPriv.apk")
+	ensureContains(t, androidMk, "LOCAL_SOONG_INSTALL_PAIRS := privapp_allowlist_com.android.AppFooPriv.xml:$(PRODUCT_OUT)/apex/myapex/etc/permissions/privapp_allowlist_com.android.AppFooPriv.xml")
 }
 
 func TestApexWithAppImportBuildId(t *testing.T) {
@@ -6209,6 +6587,72 @@
 	}`)
 }
 
+func TestApexAvailable_IndirectStaticDep(t *testing.T) {
+	testApex(t, `
+	apex {
+		name: "myapex",
+		key: "myapex.key",
+		native_shared_libs: ["libfoo"],
+		updatable: false,
+	}
+
+	apex_key {
+		name: "myapex.key",
+		public_key: "testkey.avbpubkey",
+		private_key: "testkey.pem",
+	}
+
+	cc_library {
+		name: "libfoo",
+		stl: "none",
+		static_libs: ["libbar"],
+		system_shared_libs: [],
+		apex_available: ["myapex"],
+	}
+
+	cc_library {
+		name: "libbar",
+		stl: "none",
+		shared_libs: ["libbaz"],
+		system_shared_libs: [],
+		apex_available: ["myapex"],
+	}
+
+	cc_library {
+		name: "libbaz",
+		stl: "none",
+		system_shared_libs: [],
+	}`)
+
+	testApexError(t, `requires "libbar" that doesn't list the APEX under 'apex_available'.`, `
+	apex {
+		name: "myapex",
+		key: "myapex.key",
+		native_shared_libs: ["libfoo"],
+		updatable: false,
+	}
+
+	apex_key {
+		name: "myapex.key",
+		public_key: "testkey.avbpubkey",
+		private_key: "testkey.pem",
+	}
+
+	cc_library {
+		name: "libfoo",
+		stl: "none",
+		static_libs: ["libbar"],
+		system_shared_libs: [],
+		apex_available: ["myapex"],
+	}
+
+	cc_library {
+		name: "libbar",
+		stl: "none",
+		system_shared_libs: [],
+	}`)
+}
+
 func TestApexAvailable_InvalidApexName(t *testing.T) {
 	testApexError(t, "\"otherapex\" is not a valid module name", `
 	apex {
@@ -6270,6 +6714,266 @@
 	}`)
 }
 
+func TestApexAvailable_ApexAvailableNameWithVersionCodeError(t *testing.T) {
+	t.Run("negative variant_version produces error", func(t *testing.T) {
+		testApexError(t, "expected an integer between 0-9; got -1", `
+			apex {
+				name: "myapex",
+				key: "myapex.key",
+				apex_available_name: "com.android.foo",
+				variant_version: "-1",
+				updatable: false,
+			}
+			apex_key {
+				name: "myapex.key",
+				public_key: "testkey.avbpubkey",
+				private_key: "testkey.pem",
+			}
+		`)
+	})
+
+	t.Run("variant_version greater than 9 produces error", func(t *testing.T) {
+		testApexError(t, "expected an integer between 0-9; got 10", `
+			apex {
+				name: "myapex",
+				key: "myapex.key",
+				apex_available_name: "com.android.foo",
+				variant_version: "10",
+				updatable: false,
+			}
+			apex_key {
+				name: "myapex.key",
+				public_key: "testkey.avbpubkey",
+				private_key: "testkey.pem",
+			}
+		`)
+	})
+}
+
+func TestApexAvailable_ApexAvailableNameWithVersionCode(t *testing.T) {
+	context := android.GroupFixturePreparers(
+		android.PrepareForIntegrationTestWithAndroid,
+		PrepareForTestWithApexBuildComponents,
+		android.FixtureMergeMockFs(android.MockFS{
+			"system/sepolicy/apex/foo-file_contexts": nil,
+			"system/sepolicy/apex/bar-file_contexts": nil,
+		}),
+	)
+	result := context.RunTestWithBp(t, `
+		apex {
+			name: "foo",
+			key: "myapex.key",
+			apex_available_name: "com.android.foo",
+			variant_version: "0",
+			updatable: false,
+		}
+		apex {
+			name: "bar",
+			key: "myapex.key",
+			apex_available_name: "com.android.foo",
+			variant_version: "3",
+			updatable: false,
+		}
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+	`)
+
+	fooManifestRule := result.ModuleForTests("foo", "android_common_foo_image").Rule("apexManifestRule")
+	fooExpectedDefaultVersion := android.DefaultUpdatableModuleVersion
+	fooActualDefaultVersion := fooManifestRule.Args["default_version"]
+	if fooActualDefaultVersion != fooExpectedDefaultVersion {
+		t.Errorf("expected to find defaultVersion %q; got %q", fooExpectedDefaultVersion, fooActualDefaultVersion)
+	}
+
+	barManifestRule := result.ModuleForTests("bar", "android_common_bar_image").Rule("apexManifestRule")
+	defaultVersionInt, _ := strconv.Atoi(android.DefaultUpdatableModuleVersion)
+	barExpectedDefaultVersion := fmt.Sprint(defaultVersionInt + 3)
+	barActualDefaultVersion := barManifestRule.Args["default_version"]
+	if barActualDefaultVersion != barExpectedDefaultVersion {
+		t.Errorf("expected to find defaultVersion %q; got %q", barExpectedDefaultVersion, barActualDefaultVersion)
+	}
+}
+
+func TestApexAvailable_ApexAvailableName(t *testing.T) {
+	t.Run("using name of apex that sets apex_available_name is not allowed", func(t *testing.T) {
+		testApexError(t, "Consider adding \"myapex\" to 'apex_available' property of \"AppFoo\"", `
+			apex {
+				name: "myapex_sminus",
+				key: "myapex.key",
+				apps: ["AppFoo"],
+				apex_available_name: "myapex",
+				updatable: false,
+			}
+			apex {
+				name: "myapex",
+				key: "myapex.key",
+				apps: ["AppFoo"],
+				updatable: false,
+			}
+			apex_key {
+				name: "myapex.key",
+				public_key: "testkey.avbpubkey",
+				private_key: "testkey.pem",
+			}
+			android_app {
+				name: "AppFoo",
+				srcs: ["foo/bar/MyClass.java"],
+				sdk_version: "none",
+				system_modules: "none",
+				apex_available: [ "myapex_sminus" ],
+			}`,
+			android.FixtureMergeMockFs(android.MockFS{
+				"system/sepolicy/apex/myapex_sminus-file_contexts": nil,
+			}),
+		)
+	})
+
+	t.Run("apex_available_name allows module to be used in two different apexes", func(t *testing.T) {
+		testApex(t, `
+			apex {
+				name: "myapex_sminus",
+				key: "myapex.key",
+				apps: ["AppFoo"],
+				apex_available_name: "myapex",
+				updatable: false,
+			}
+			apex {
+				name: "myapex",
+				key: "myapex.key",
+				apps: ["AppFoo"],
+				updatable: false,
+			}
+			apex_key {
+				name: "myapex.key",
+				public_key: "testkey.avbpubkey",
+				private_key: "testkey.pem",
+			}
+			android_app {
+				name: "AppFoo",
+				srcs: ["foo/bar/MyClass.java"],
+				sdk_version: "none",
+				system_modules: "none",
+				apex_available: [ "myapex" ],
+			}`,
+			android.FixtureMergeMockFs(android.MockFS{
+				"system/sepolicy/apex/myapex_sminus-file_contexts": nil,
+			}),
+		)
+	})
+
+	t.Run("override_apexes work with apex_available_name", func(t *testing.T) {
+		testApex(t, `
+			override_apex {
+				name: "myoverrideapex_sminus",
+				base: "myapex_sminus",
+				key: "myapex.key",
+				apps: ["AppFooOverride"],
+			}
+			override_apex {
+				name: "myoverrideapex",
+				base: "myapex",
+				key: "myapex.key",
+				apps: ["AppFooOverride"],
+			}
+			apex {
+				name: "myapex_sminus",
+				key: "myapex.key",
+				apps: ["AppFoo"],
+				apex_available_name: "myapex",
+				updatable: false,
+			}
+			apex {
+				name: "myapex",
+				key: "myapex.key",
+				apps: ["AppFoo"],
+				updatable: false,
+			}
+			apex_key {
+				name: "myapex.key",
+				public_key: "testkey.avbpubkey",
+				private_key: "testkey.pem",
+			}
+			android_app {
+				name: "AppFooOverride",
+				srcs: ["foo/bar/MyClass.java"],
+				sdk_version: "none",
+				system_modules: "none",
+				apex_available: [ "myapex" ],
+			}
+			android_app {
+				name: "AppFoo",
+				srcs: ["foo/bar/MyClass.java"],
+				sdk_version: "none",
+				system_modules: "none",
+				apex_available: [ "myapex" ],
+			}`,
+			android.FixtureMergeMockFs(android.MockFS{
+				"system/sepolicy/apex/myapex_sminus-file_contexts": nil,
+			}),
+		)
+	})
+}
+
+func TestApexAvailable_ApexAvailableNameWithOverrides(t *testing.T) {
+	context := android.GroupFixturePreparers(
+		android.PrepareForIntegrationTestWithAndroid,
+		PrepareForTestWithApexBuildComponents,
+		java.PrepareForTestWithDexpreopt,
+		android.FixtureMergeMockFs(android.MockFS{
+			"system/sepolicy/apex/myapex-file_contexts":        nil,
+			"system/sepolicy/apex/myapex_sminus-file_contexts": nil,
+		}),
+		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+			variables.BuildId = proptools.StringPtr("buildid")
+		}),
+	)
+	context.RunTestWithBp(t, `
+	override_apex {
+		name: "myoverrideapex_sminus",
+		base: "myapex_sminus",
+	}
+	override_apex {
+		name: "myoverrideapex",
+		base: "myapex",
+	}
+	apex {
+		name: "myapex",
+		key: "myapex.key",
+		apps: ["AppFoo"],
+		updatable: false,
+	}
+	apex {
+		name: "myapex_sminus",
+		apex_available_name: "myapex",
+		key: "myapex.key",
+		apps: ["AppFoo_sminus"],
+		updatable: false,
+	}
+	apex_key {
+		name: "myapex.key",
+		public_key: "testkey.avbpubkey",
+		private_key: "testkey.pem",
+	}
+	android_app {
+		name: "AppFoo",
+		srcs: ["foo/bar/MyClass.java"],
+		sdk_version: "none",
+		system_modules: "none",
+		apex_available: [ "myapex" ],
+	}
+	android_app {
+		name: "AppFoo_sminus",
+		srcs: ["foo/bar/MyClass.java"],
+		sdk_version: "none",
+		min_sdk_version: "29",
+		system_modules: "none",
+		apex_available: [ "myapex" ],
+	}`)
+}
+
 func TestApexAvailable_CheckForPlatform(t *testing.T) {
 	ctx := testApex(t, `
 	apex {
@@ -8017,8 +8721,8 @@
 		testNoUpdatableJarsInBootImage(t, "", preparer, fragments...)
 	})
 
-	t.Run("updatable jar from ART apex in the framework boot image => error", func(t *testing.T) {
-		err := `module "some-art-lib" from updatable apexes \["com.android.art.debug"\] is not allowed in the framework boot image`
+	t.Run("updatable jar from ART apex in the platform bootclasspath => error", func(t *testing.T) {
+		err := `module "some-art-lib" from updatable apexes \["com.android.art.debug"\] is not allowed in the platform bootclasspath`
 		// Update the dexpreopt BootJars directly.
 		preparer := android.GroupFixturePreparers(
 			prepareSetBootJars("com.android.art.debug:some-art-lib"),
@@ -8041,8 +8745,8 @@
 		testNoUpdatableJarsInBootImage(t, err, preparer)
 	})
 
-	t.Run("updatable jar from some other apex in the framework boot image => error", func(t *testing.T) {
-		err := `module "some-updatable-apex-lib" from updatable apexes \["some-updatable-apex"\] is not allowed in the framework boot image`
+	t.Run("updatable jar from some other apex in the platform bootclasspath => error", func(t *testing.T) {
+		err := `module "some-updatable-apex-lib" from updatable apexes \["some-updatable-apex"\] is not allowed in the platform bootclasspath`
 		preparer := android.GroupFixturePreparers(
 			java.FixtureConfigureBootJars("some-updatable-apex:some-updatable-apex-lib"),
 			java.FixtureConfigureApexBootJars("some-non-updatable-apex:some-non-updatable-apex-lib"),
@@ -8050,7 +8754,7 @@
 		testNoUpdatableJarsInBootImage(t, err, preparer)
 	})
 
-	t.Run("non-updatable jar from some other apex in the framework boot image => ok", func(t *testing.T) {
+	t.Run("non-updatable jar from some other apex in the platform bootclasspath => ok", func(t *testing.T) {
 		preparer := java.FixtureConfigureApexBootJars("some-non-updatable-apex:some-non-updatable-apex-lib")
 		fragment := java.ApexVariantReference{
 			Apex:   proptools.StringPtr("some-non-updatable-apex"),
@@ -8065,7 +8769,7 @@
 		testNoUpdatableJarsInBootImage(t, err, preparer)
 	})
 
-	t.Run("nonexistent jar in the framework boot image => error", func(t *testing.T) {
+	t.Run("nonexistent jar in the platform bootclasspath => error", func(t *testing.T) {
 		err := `"platform-bootclasspath" depends on undefined module "nonexistent"`
 		preparer := java.FixtureConfigureBootJars("platform:nonexistent")
 		testNoUpdatableJarsInBootImage(t, err, preparer)
@@ -8078,7 +8782,7 @@
 		testNoUpdatableJarsInBootImage(t, err, preparer)
 	})
 
-	t.Run("platform jar in the framework boot image => ok", func(t *testing.T) {
+	t.Run("platform jar in the platform bootclasspath => ok", func(t *testing.T) {
 		preparer := android.GroupFixturePreparers(
 			java.FixtureConfigureBootJars("platform:some-platform-lib"),
 			java.FixtureConfigureApexBootJars("some-non-updatable-apex:some-non-updatable-apex-lib"),
@@ -8445,14 +9149,14 @@
 		apex {
 			name: "com.android.art",
 			key: "myapex.key",
-			native_shared_libs: ["mylib"],
+			native_shared_libs: ["libnativebridge"],
 			updatable: false,
 		}
 
 		apex {
 			name: "com.android.art.debug",
 			key: "myapex.key",
-			native_shared_libs: ["mylib", "mytestlib"],
+			native_shared_libs: ["libnativebridge", "libnativebrdige_test"],
 			updatable: false,
 		}
 
@@ -8463,8 +9167,8 @@
 		}
 
 		cc_library {
-			name: "mylib",
-			srcs: ["mylib.cpp"],
+			name: "libnativebridge",
+			srcs: ["libnativebridge.cpp"],
 			system_shared_libs: [],
 			stl: "none",
 			stubs: {
@@ -8474,10 +9178,10 @@
 		}
 
 		cc_library {
-			name: "mytestlib",
+			name: "libnativebrdige_test",
 			srcs: ["mylib.cpp"],
 			system_shared_libs: [],
-			shared_libs: ["mylib"],
+			shared_libs: ["libnativebridge"],
 			stl: "none",
 			apex_available: ["com.android.art.debug"],
 			test_for: ["com.android.art"],
@@ -10149,3 +10853,128 @@
 	// Ensure that canned_fs_config has "cat my_config" at the end
 	ensureContains(t, cmd, `( echo '/ 1000 1000 0755'; echo '/apex_manifest.json 1000 1000 0644'; echo '/apex_manifest.pb 1000 1000 0644'; cat my_config ) >`)
 }
+
+func TestStubLibrariesMultipleApexViolation(t *testing.T) {
+	testCases := []struct {
+		desc          string
+		hasStubs      bool
+		apexAvailable string
+		expectedError string
+	}{
+		{
+			desc:          "non-stub library can have multiple apex_available",
+			hasStubs:      false,
+			apexAvailable: `["myapex", "otherapex"]`,
+		},
+		{
+			desc:          "stub library should not be available to anyapex",
+			hasStubs:      true,
+			apexAvailable: `["//apex_available:anyapex"]`,
+			expectedError: "Stub libraries should have a single apex_available.*anyapex",
+		},
+		{
+			desc:          "stub library should not be available to multiple apexes",
+			hasStubs:      true,
+			apexAvailable: `["myapex", "otherapex"]`,
+			expectedError: "Stub libraries should have a single apex_available.*myapex.*otherapex",
+		},
+		{
+			desc:          "stub library can be available to a core apex and a test apex",
+			hasStubs:      true,
+			apexAvailable: `["myapex", "test_myapex"]`,
+		},
+	}
+	bpTemplate := `
+		cc_library {
+			name: "libfoo",
+			%v
+			apex_available: %v,
+		}
+		apex {
+			name: "myapex",
+			key: "apex.key",
+			updatable: false,
+			native_shared_libs: ["libfoo"],
+		}
+		apex {
+			name: "otherapex",
+			key: "apex.key",
+			updatable: false,
+		}
+		apex_test {
+			name: "test_myapex",
+			key: "apex.key",
+			updatable: false,
+			native_shared_libs: ["libfoo"],
+		}
+		apex_key {
+			name: "apex.key",
+		}
+	`
+	for _, tc := range testCases {
+		stubs := ""
+		if tc.hasStubs {
+			stubs = `stubs: {symbol_file: "libfoo.map.txt"},`
+		}
+		bp := fmt.Sprintf(bpTemplate, stubs, tc.apexAvailable)
+		mockFsFixturePreparer := android.FixtureModifyMockFS(func(fs android.MockFS) {
+			fs["system/sepolicy/apex/test_myapex-file_contexts"] = nil
+		})
+		if tc.expectedError == "" {
+			testApex(t, bp, mockFsFixturePreparer)
+		} else {
+			testApexError(t, tc.expectedError, bp, mockFsFixturePreparer)
+		}
+	}
+}
+
+func TestFileSystemShouldSkipApexLibraries(t *testing.T) {
+	context := android.GroupFixturePreparers(
+		android.PrepareForIntegrationTestWithAndroid,
+		cc.PrepareForIntegrationTestWithCc,
+		PrepareForTestWithApexBuildComponents,
+		prepareForTestWithMyapex,
+		filesystem.PrepareForTestWithFilesystemBuildComponents,
+	)
+	result := context.RunTestWithBp(t, `
+		android_system_image {
+			name: "myfilesystem",
+			deps: [
+				"libfoo",
+			],
+			linker_config_src: "linker.config.json",
+		}
+
+		cc_library {
+			name: "libfoo",
+			shared_libs: [
+				"libbar",
+			],
+			stl: "none",
+		}
+
+		cc_library {
+			name: "libbar",
+			stl: "none",
+			apex_available: ["myapex"],
+		}
+
+		apex {
+			name: "myapex",
+			native_shared_libs: ["libbar"],
+			key: "myapex.key",
+			updatable: false,
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+	`)
+
+	inputs := result.ModuleForTests("myfilesystem", "android_common").Output("deps.zip").Implicits
+	android.AssertStringListDoesNotContain(t, "filesystem should not have libbar",
+		inputs.Strings(),
+		"out/soong/.intermediates/libbar/android_arm64_armv8-a_shared/libbar.so")
+}
diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go
index 2ddfd03..1b52886 100644
--- a/apex/bootclasspath_fragment_test.go
+++ b/apex/bootclasspath_fragment_test.go
@@ -115,20 +115,7 @@
 
 	// Make sure that the art-bootclasspath-fragment is using the correct configuration.
 	checkBootclasspathFragment(t, result, "art-bootclasspath-fragment", "android_common_apex10000",
-		"com.android.art:baz,com.android.art:quuz", `
-test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art
-test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.oat
-test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.vdex
-test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-quuz.art
-test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-quuz.oat
-test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-quuz.vdex
-test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art
-test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.oat
-test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.vdex
-test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-quuz.art
-test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-quuz.oat
-test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-quuz.vdex
-`)
+		"com.android.art:baz,com.android.art:quuz")
 }
 
 func TestBootclasspathFragments_FragmentDependency(t *testing.T) {
@@ -261,7 +248,7 @@
 	checkAPIScopeStubs("other", otherInfo, java.CorePlatformHiddenAPIScope)
 }
 
-func checkBootclasspathFragment(t *testing.T, result *android.TestResult, moduleName, variantName string, expectedConfiguredModules string, expectedBootclasspathFragmentFiles string) {
+func checkBootclasspathFragment(t *testing.T, result *android.TestResult, moduleName, variantName string, expectedConfiguredModules string) {
 	t.Helper()
 
 	bootclasspathFragment := result.ModuleForTests(moduleName, variantName).Module().(*java.BootclasspathFragmentModule)
@@ -269,19 +256,6 @@
 	bootclasspathFragmentInfo := result.ModuleProvider(bootclasspathFragment, java.BootclasspathFragmentApexContentInfoProvider).(java.BootclasspathFragmentApexContentInfo)
 	modules := bootclasspathFragmentInfo.Modules()
 	android.AssertStringEquals(t, "invalid modules for "+moduleName, expectedConfiguredModules, modules.String())
-
-	// Get a list of all the paths in the boot image sorted by arch type.
-	allPaths := []string{}
-	bootImageFilesByArchType := bootclasspathFragmentInfo.AndroidBootImageFilesByArchType()
-	for _, archType := range android.ArchTypeList() {
-		if paths, ok := bootImageFilesByArchType[archType]; ok {
-			for _, path := range paths {
-				allPaths = append(allPaths, android.NormalizePathForTesting(path))
-			}
-		}
-	}
-
-	android.AssertTrimmedStringEquals(t, "invalid paths for "+moduleName, expectedBootclasspathFragmentFiles, strings.Join(allPaths, "\n"))
 }
 
 func TestBootclasspathFragmentInArtApex(t *testing.T) {
@@ -420,18 +394,6 @@
 		ensureExactContents(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
 			"etc/boot-image.prof",
 			"etc/classpaths/bootclasspath.pb",
-			"javalib/arm/boot.art",
-			"javalib/arm/boot.oat",
-			"javalib/arm/boot.vdex",
-			"javalib/arm/boot-bar.art",
-			"javalib/arm/boot-bar.oat",
-			"javalib/arm/boot-bar.vdex",
-			"javalib/arm64/boot.art",
-			"javalib/arm64/boot.oat",
-			"javalib/arm64/boot.vdex",
-			"javalib/arm64/boot-bar.art",
-			"javalib/arm64/boot-bar.oat",
-			"javalib/arm64/boot-bar.vdex",
 			"javalib/bar.jar",
 			"javalib/foo.jar",
 		})
@@ -441,29 +403,13 @@
 			`mybootclasspathfragment`,
 		})
 
-		// The boot images are installed in the APEX by Soong, so there shouldn't be any dexpreopt-related Make modules.
-		ensureDoesNotContainRequiredDeps(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
-			"mybootclasspathfragment-dexpreopt-arm64-boot.art",
-			"mybootclasspathfragment-dexpreopt-arm64-boot.oat",
-			"mybootclasspathfragment-dexpreopt-arm64-boot.vdex",
-			"mybootclasspathfragment-dexpreopt-arm64-boot-bar.art",
-			"mybootclasspathfragment-dexpreopt-arm64-boot-bar.oat",
-			"mybootclasspathfragment-dexpreopt-arm64-boot-bar.vdex",
-			"mybootclasspathfragment-dexpreopt-arm-boot.art",
-			"mybootclasspathfragment-dexpreopt-arm-boot.oat",
-			"mybootclasspathfragment-dexpreopt-arm-boot.vdex",
-			"mybootclasspathfragment-dexpreopt-arm-boot-bar.art",
-			"mybootclasspathfragment-dexpreopt-arm-boot-bar.oat",
-			"mybootclasspathfragment-dexpreopt-arm-boot-bar.vdex",
-		})
-
 		// Make sure that the source bootclasspath_fragment copies its dex files to the predefined
 		// locations for the art image.
 		module := result.ModuleForTests("mybootclasspathfragment", "android_common_apex10000")
 		checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo")
 	})
 
-	t.Run("boot image files from source no boot image in apex", func(t *testing.T) {
+	t.Run("generate boot image profile even if dexpreopt is disabled", func(t *testing.T) {
 		result := android.GroupFixturePreparers(
 			commonPreparer,
 
@@ -472,6 +418,7 @@
 			java.FixtureConfigureBootJars("com.android.art:foo", "com.android.art:bar"),
 			addSource("foo", "bar"),
 			java.FixtureSetBootImageInstallDirOnDevice("art", "system/framework"),
+			dexpreopt.FixtureDisableDexpreoptBootImages(true),
 		).RunTest(t)
 
 		ensureExactContents(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
@@ -480,21 +427,6 @@
 			"javalib/bar.jar",
 			"javalib/foo.jar",
 		})
-
-		ensureContainsRequiredDeps(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
-			"mybootclasspathfragment-dexpreopt-arm64-boot.art",
-			"mybootclasspathfragment-dexpreopt-arm64-boot.oat",
-			"mybootclasspathfragment-dexpreopt-arm64-boot.vdex",
-			"mybootclasspathfragment-dexpreopt-arm64-boot-bar.art",
-			"mybootclasspathfragment-dexpreopt-arm64-boot-bar.oat",
-			"mybootclasspathfragment-dexpreopt-arm64-boot-bar.vdex",
-			"mybootclasspathfragment-dexpreopt-arm-boot.art",
-			"mybootclasspathfragment-dexpreopt-arm-boot.oat",
-			"mybootclasspathfragment-dexpreopt-arm-boot.vdex",
-			"mybootclasspathfragment-dexpreopt-arm-boot-bar.art",
-			"mybootclasspathfragment-dexpreopt-arm-boot-bar.oat",
-			"mybootclasspathfragment-dexpreopt-arm-boot-bar.vdex",
-		})
 	})
 
 	t.Run("boot image disable generate profile", func(t *testing.T) {
@@ -532,18 +464,6 @@
 
 		ensureExactDeapexedContents(t, result.TestContext, "com.android.art", "android_common", []string{
 			"etc/boot-image.prof",
-			"javalib/arm/boot.art",
-			"javalib/arm/boot.oat",
-			"javalib/arm/boot.vdex",
-			"javalib/arm/boot-bar.art",
-			"javalib/arm/boot-bar.oat",
-			"javalib/arm/boot-bar.vdex",
-			"javalib/arm64/boot.art",
-			"javalib/arm64/boot.oat",
-			"javalib/arm64/boot.vdex",
-			"javalib/arm64/boot-bar.art",
-			"javalib/arm64/boot-bar.oat",
-			"javalib/arm64/boot-bar.vdex",
 			"javalib/bar.jar",
 			"javalib/foo.jar",
 		})
@@ -554,65 +474,12 @@
 			`prebuilt_com.android.art`,
 		})
 
-		// The boot images are installed in the APEX by Soong, so there shouldn't be any dexpreopt-related Make modules.
-		ensureDoesNotContainRequiredDeps(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
-			"mybootclasspathfragment-dexpreopt-arm64-boot.art",
-			"mybootclasspathfragment-dexpreopt-arm64-boot.oat",
-			"mybootclasspathfragment-dexpreopt-arm64-boot.vdex",
-			"mybootclasspathfragment-dexpreopt-arm64-boot-bar.art",
-			"mybootclasspathfragment-dexpreopt-arm64-boot-bar.oat",
-			"mybootclasspathfragment-dexpreopt-arm64-boot-bar.vdex",
-			"mybootclasspathfragment-dexpreopt-arm-boot.art",
-			"mybootclasspathfragment-dexpreopt-arm-boot.oat",
-			"mybootclasspathfragment-dexpreopt-arm-boot.vdex",
-			"mybootclasspathfragment-dexpreopt-arm-boot-bar.art",
-			"mybootclasspathfragment-dexpreopt-arm-boot-bar.oat",
-			"mybootclasspathfragment-dexpreopt-arm-boot-bar.vdex",
-		})
-
 		// Make sure that the prebuilt bootclasspath_fragment copies its dex files to the predefined
 		// locations for the art image.
 		module := result.ModuleForTests("prebuilt_mybootclasspathfragment", "android_common_com.android.art")
 		checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo")
 	})
 
-	t.Run("boot image files from preferred prebuilt no boot image in apex", func(t *testing.T) {
-		result := android.GroupFixturePreparers(
-			commonPreparer,
-
-			// Configure some libraries in the art bootclasspath_fragment that match the source
-			// bootclasspath_fragment's contents property.
-			java.FixtureConfigureBootJars("com.android.art:foo", "com.android.art:bar"),
-			addSource("foo", "bar"),
-
-			// Make sure that a preferred prebuilt with consistent contents doesn't affect the apex.
-			addPrebuilt(true, "foo", "bar"),
-
-			java.FixtureSetBootImageInstallDirOnDevice("art", "system/framework"),
-		).RunTest(t)
-
-		ensureExactDeapexedContents(t, result.TestContext, "com.android.art", "android_common", []string{
-			"etc/boot-image.prof",
-			"javalib/bar.jar",
-			"javalib/foo.jar",
-		})
-
-		ensureContainsRequiredDeps(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
-			"mybootclasspathfragment-dexpreopt-arm64-boot.art",
-			"mybootclasspathfragment-dexpreopt-arm64-boot.oat",
-			"mybootclasspathfragment-dexpreopt-arm64-boot.vdex",
-			"mybootclasspathfragment-dexpreopt-arm64-boot-bar.art",
-			"mybootclasspathfragment-dexpreopt-arm64-boot-bar.oat",
-			"mybootclasspathfragment-dexpreopt-arm64-boot-bar.vdex",
-			"mybootclasspathfragment-dexpreopt-arm-boot.art",
-			"mybootclasspathfragment-dexpreopt-arm-boot.oat",
-			"mybootclasspathfragment-dexpreopt-arm-boot.vdex",
-			"mybootclasspathfragment-dexpreopt-arm-boot-bar.art",
-			"mybootclasspathfragment-dexpreopt-arm-boot-bar.oat",
-			"mybootclasspathfragment-dexpreopt-arm-boot-bar.vdex",
-		})
-	})
-
 	t.Run("source with inconsistency between config and contents", func(t *testing.T) {
 		android.GroupFixturePreparers(
 			commonPreparer,
@@ -762,10 +629,6 @@
 
 		module := result.ModuleForTests("mybootclasspathfragment", "android_common_com.android.art")
 		checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo")
-
-		// Check that the right deapexer module was chosen for a boot image.
-		param := module.Output("out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art")
-		android.AssertStringDoesContain(t, "didn't find the expected deapexer in the input path", param.Input.String(), "/com.android.art.deapexer")
 	})
 
 	t.Run("enabled alternative APEX", func(t *testing.T) {
@@ -782,7 +645,7 @@
 	bootJarLocations := []string{}
 	for _, output := range module.AllOutputs() {
 		output = android.StringRelativeToTop(config, output)
-		if strings.HasPrefix(output, "out/soong/test_device/dex_artjars_input/") {
+		if strings.HasPrefix(output, "out/soong/dexpreopt_arm64/dex_artjars_input/") {
 			bootJarLocations = append(bootJarLocations, output)
 		}
 	}
@@ -790,7 +653,7 @@
 	sort.Strings(bootJarLocations)
 	expected := []string{}
 	for _, m := range modules {
-		expected = append(expected, fmt.Sprintf("out/soong/test_device/dex_artjars_input/%s.jar", m))
+		expected = append(expected, fmt.Sprintf("out/soong/dexpreopt_arm64/dex_artjars_input/%s.jar", m))
 	}
 	sort.Strings(expected)
 
@@ -1230,7 +1093,7 @@
 func TestBootclasspathFragment_AndroidNonUpdatable_AlwaysUsePrebuiltSdks(t *testing.T) {
 	result := android.GroupFixturePreparers(
 		prepareForTestWithBootclasspathFragment,
-		java.PrepareForTestWithJavaDefaultModules,
+		java.PrepareForTestWithDexpreopt,
 		prepareForTestWithArtApex,
 		prepareForTestWithMyapex,
 		// Configure bootclasspath jars to ensure that hidden API encoding is performed on them.
diff --git a/apex/bp2build_test.go b/apex/bp2build_test.go
index 2a0f6e9..b1b6a75 100644
--- a/apex/bp2build_test.go
+++ b/apex/bp2build_test.go
@@ -15,7 +15,10 @@
 
 import (
 	"android/soong/android"
+	"android/soong/android/allowlists"
 	"android/soong/bazel/cquery"
+	"fmt"
+	"path/filepath"
 	"strings"
 	"testing"
 )
@@ -326,7 +329,7 @@
 }
 
 func TestOverrideApexImageInMixedBuilds(t *testing.T) {
-	bp := `
+	originalBp := `
 apex_key{
 	name: "foo_key",
 }
@@ -340,122 +343,203 @@
 	min_sdk_version: "31",
 	package_name: "pkg_name",
 	file_contexts: ":myapex-file_contexts",
-	bazel_module: { label: "//:foo" },
-}
+	%s
+}`
+	overrideBp := `
 override_apex {
 	name: "override_foo",
 	key: "override_foo_key",
 	package_name: "override_pkg_name",
 	base: "foo",
-	bazel_module: { label: "//:override_foo" },
+	%s
 }
 `
 
-	outputBaseDir := "out/bazel"
-	result := android.GroupFixturePreparers(
-		prepareForApexTest,
-		android.FixtureModifyConfig(func(config android.Config) {
-			config.BazelContext = android.MockBazelContext{
-				OutputBaseDir: outputBaseDir,
-				LabelToApexInfo: map[string]cquery.ApexInfo{
-					"//:foo": cquery.ApexInfo{
-						// ApexInfo Starlark provider
-						SignedOutput:          "signed_out.apex",
-						UnsignedOutput:        "unsigned_out.apex",
-						BundleKeyInfo:         []string{"public_key", "private_key"},
-						ContainerKeyInfo:      []string{"container_cert", "container_private"},
-						SymbolsUsedByApex:     "foo_using.txt",
-						JavaSymbolsUsedByApex: "foo_using.xml",
-						BundleFile:            "apex_bundle.zip",
-						InstalledFiles:        "installed-files.txt",
-						RequiresLibs:          []string{"//path/c:c", "//path/d:d"},
+	originalApexBpDir := "original"
+	originalApexName := "foo"
+	overrideApexBpDir := "override"
+	overrideApexName := "override_foo"
 
-						// unused
-						PackageName:  "pkg_name",
-						ProvidesLibs: []string{"a", "b"},
+	defaultApexLabel := fmt.Sprintf("//%s:%s", originalApexBpDir, originalApexName)
+	defaultOverrideApexLabel := fmt.Sprintf("//%s:%s", overrideApexBpDir, overrideApexName)
 
-						// ApexMkInfo Starlark provider
-						MakeModulesToInstall: []string{"c"}, // d deliberately omitted
-					},
-					"//:override_foo": cquery.ApexInfo{
-						// ApexInfo Starlark provider
-						SignedOutput:          "override_signed_out.apex",
-						UnsignedOutput:        "override_unsigned_out.apex",
-						BundleKeyInfo:         []string{"override_public_key", "override_private_key"},
-						ContainerKeyInfo:      []string{"override_container_cert", "override_container_private"},
-						SymbolsUsedByApex:     "override_foo_using.txt",
-						JavaSymbolsUsedByApex: "override_foo_using.xml",
-						BundleFile:            "override_apex_bundle.zip",
-						InstalledFiles:        "override_installed-files.txt",
-						RequiresLibs:          []string{"//path/c:c", "//path/d:d"},
+	testCases := []struct {
+		desc                    string
+		bazelModuleProp         string
+		apexLabel               string
+		overrideBazelModuleProp string
+		overrideApexLabel       string
+		bp2buildConfiguration   android.Bp2BuildConversionAllowlist
+	}{
+		{
+			desc:                    "both explicit labels",
+			bazelModuleProp:         `bazel_module: { label: "//:foo" },`,
+			apexLabel:               "//:foo",
+			overrideBazelModuleProp: `bazel_module: { label: "//:override_foo" },`,
+			overrideApexLabel:       "//:override_foo",
+			bp2buildConfiguration:   android.NewBp2BuildAllowlist(),
+		},
+		{
+			desc:                    "both explicitly allowed",
+			bazelModuleProp:         `bazel_module: { bp2build_available: true },`,
+			apexLabel:               defaultApexLabel,
+			overrideBazelModuleProp: `bazel_module: { bp2build_available: true },`,
+			overrideApexLabel:       defaultOverrideApexLabel,
+			bp2buildConfiguration:   android.NewBp2BuildAllowlist(),
+		},
+		{
+			desc:              "original allowed by dir, override allowed by name",
+			apexLabel:         defaultApexLabel,
+			overrideApexLabel: defaultOverrideApexLabel,
+			bp2buildConfiguration: android.NewBp2BuildAllowlist().SetDefaultConfig(
+				map[string]allowlists.BazelConversionConfigEntry{
+					originalApexBpDir: allowlists.Bp2BuildDefaultTrue,
+				}).SetModuleAlwaysConvertList([]string{
+				overrideApexName,
+			}),
+		},
+		{
+			desc:              "both allowed by name",
+			apexLabel:         defaultApexLabel,
+			overrideApexLabel: defaultOverrideApexLabel,
+			bp2buildConfiguration: android.NewBp2BuildAllowlist().SetModuleAlwaysConvertList([]string{
+				originalApexName,
+				overrideApexName,
+			}),
+		},
+		{
+			desc:              "override allowed by name",
+			apexLabel:         defaultApexLabel,
+			overrideApexLabel: defaultOverrideApexLabel,
+			bp2buildConfiguration: android.NewBp2BuildAllowlist().SetModuleAlwaysConvertList([]string{
+				overrideApexName,
+			}),
+		},
+		{
+			desc:              "override allowed by dir",
+			apexLabel:         defaultApexLabel,
+			overrideApexLabel: defaultOverrideApexLabel,
+			bp2buildConfiguration: android.NewBp2BuildAllowlist().SetDefaultConfig(
+				map[string]allowlists.BazelConversionConfigEntry{
+					overrideApexBpDir: allowlists.Bp2BuildDefaultTrue,
+				}).SetModuleAlwaysConvertList([]string{}),
+		},
+	}
 
-						// unused
-						PackageName:  "override_pkg_name",
-						ProvidesLibs: []string{"a", "b"},
+	for _, tc := range testCases {
+		t.Run(tc.desc, func(t *testing.T) {
+			outputBaseDir := "out/bazel"
+			result := android.GroupFixturePreparers(
+				prepareForApexTest,
+				android.FixtureAddTextFile(filepath.Join(originalApexBpDir, "Android.bp"), fmt.Sprintf(originalBp, tc.bazelModuleProp)),
+				android.FixtureAddTextFile(filepath.Join(overrideApexBpDir, "Android.bp"), fmt.Sprintf(overrideBp, tc.overrideBazelModuleProp)),
+				android.FixtureModifyContext(func(ctx *android.TestContext) {
+					ctx.RegisterBp2BuildConfig(tc.bp2buildConfiguration)
+				}),
+				android.FixtureModifyConfig(func(config android.Config) {
+					config.BazelContext = android.MockBazelContext{
+						OutputBaseDir: outputBaseDir,
+						LabelToApexInfo: map[string]cquery.ApexInfo{
+							tc.apexLabel: cquery.ApexInfo{
+								// ApexInfo Starlark provider
+								SignedOutput:          "signed_out.apex",
+								UnsignedOutput:        "unsigned_out.apex",
+								BundleKeyInfo:         []string{"public_key", "private_key"},
+								ContainerKeyInfo:      []string{"container_cert", "container_private"},
+								SymbolsUsedByApex:     "foo_using.txt",
+								JavaSymbolsUsedByApex: "foo_using.xml",
+								BundleFile:            "apex_bundle.zip",
+								InstalledFiles:        "installed-files.txt",
+								RequiresLibs:          []string{"//path/c:c", "//path/d:d"},
 
-						// ApexMkInfo Starlark provider
-						MakeModulesToInstall: []string{"c"}, // d deliberately omitted
-					},
-				},
+								// unused
+								PackageName:  "pkg_name",
+								ProvidesLibs: []string{"a", "b"},
+
+								// ApexMkInfo Starlark provider
+								MakeModulesToInstall: []string{"c"}, // d deliberately omitted
+							},
+							tc.overrideApexLabel: cquery.ApexInfo{
+								// ApexInfo Starlark provider
+								SignedOutput:          "override_signed_out.apex",
+								UnsignedOutput:        "override_unsigned_out.apex",
+								BundleKeyInfo:         []string{"override_public_key", "override_private_key"},
+								ContainerKeyInfo:      []string{"override_container_cert", "override_container_private"},
+								SymbolsUsedByApex:     "override_foo_using.txt",
+								JavaSymbolsUsedByApex: "override_foo_using.xml",
+								BundleFile:            "override_apex_bundle.zip",
+								InstalledFiles:        "override_installed-files.txt",
+								RequiresLibs:          []string{"//path/c:c", "//path/d:d"},
+
+								// unused
+								PackageName:  "override_pkg_name",
+								ProvidesLibs: []string{"a", "b"},
+
+								// ApexMkInfo Starlark provider
+								MakeModulesToInstall: []string{"c"}, // d deliberately omitted
+							},
+						},
+					}
+				}),
+			).RunTest(t)
+
+			m := result.ModuleForTests("foo", "android_common_override_foo_foo_image").Module()
+			ab, ok := m.(*apexBundle)
+			if !ok {
+				t.Fatalf("Expected module to be an apexBundle, was not")
 			}
-		}),
-	).RunTestWithBp(t, bp)
 
-	m := result.ModuleForTests("foo", "android_common_override_foo_foo_image").Module()
-	ab, ok := m.(*apexBundle)
-	if !ok {
-		t.Fatalf("Expected module to be an apexBundle, was not")
-	}
+			if w, g := "out/bazel/execroot/__main__/override_public_key", ab.publicKeyFile.String(); w != g {
+				t.Errorf("Expected public key %q, got %q", w, g)
+			}
 
-	if w, g := "out/bazel/execroot/__main__/override_public_key", ab.publicKeyFile.String(); w != g {
-		t.Errorf("Expected public key %q, got %q", w, g)
-	}
+			if w, g := "out/bazel/execroot/__main__/override_private_key", ab.privateKeyFile.String(); w != g {
+				t.Errorf("Expected private key %q, got %q", w, g)
+			}
 
-	if w, g := "out/bazel/execroot/__main__/override_private_key", ab.privateKeyFile.String(); w != g {
-		t.Errorf("Expected private key %q, got %q", w, g)
-	}
+			if w, g := "out/bazel/execroot/__main__/override_container_cert", ab.containerCertificateFile; g != nil && w != g.String() {
+				t.Errorf("Expected public container key %q, got %q", w, g)
+			}
 
-	if w, g := "out/bazel/execroot/__main__/override_container_cert", ab.containerCertificateFile.String(); w != g {
-		t.Errorf("Expected public container key %q, got %q", w, g)
-	}
+			if w, g := "out/bazel/execroot/__main__/override_container_private", ab.containerPrivateKeyFile; g != nil && w != g.String() {
+				t.Errorf("Expected private container key %q, got %q", w, g)
+			}
 
-	if w, g := "out/bazel/execroot/__main__/override_container_private", ab.containerPrivateKeyFile.String(); w != g {
-		t.Errorf("Expected private container key %q, got %q", w, g)
-	}
+			if w, g := "out/bazel/execroot/__main__/override_signed_out.apex", ab.outputFile.String(); w != g {
+				t.Errorf("Expected output file %q, got %q", w, g)
+			}
 
-	if w, g := "out/bazel/execroot/__main__/override_signed_out.apex", ab.outputFile.String(); w != g {
-		t.Errorf("Expected output file %q, got %q", w, g)
-	}
+			if w, g := "out/bazel/execroot/__main__/override_foo_using.txt", ab.nativeApisUsedByModuleFile.String(); w != g {
+				t.Errorf("Expected output file %q, got %q", w, g)
+			}
 
-	if w, g := "out/bazel/execroot/__main__/override_foo_using.txt", ab.nativeApisUsedByModuleFile.String(); w != g {
-		t.Errorf("Expected output file %q, got %q", w, g)
-	}
+			if w, g := "out/bazel/execroot/__main__/override_foo_using.xml", ab.javaApisUsedByModuleFile.String(); w != g {
+				t.Errorf("Expected output file %q, got %q", w, g)
+			}
 
-	if w, g := "out/bazel/execroot/__main__/override_foo_using.xml", ab.javaApisUsedByModuleFile.String(); w != g {
-		t.Errorf("Expected output file %q, got %q", w, g)
-	}
+			if w, g := "out/bazel/execroot/__main__/override_installed-files.txt", ab.installedFilesFile.String(); w != g {
+				t.Errorf("Expected installed-files.txt %q, got %q", w, g)
+			}
 
-	if w, g := "out/bazel/execroot/__main__/override_installed-files.txt", ab.installedFilesFile.String(); w != g {
-		t.Errorf("Expected installed-files.txt %q, got %q", w, g)
-	}
+			mkData := android.AndroidMkDataForTest(t, result.TestContext, m)
+			var builder strings.Builder
+			mkData.Custom(&builder, "override_foo", "BAZEL_TARGET_", "", mkData)
 
-	mkData := android.AndroidMkDataForTest(t, result.TestContext, m)
-	var builder strings.Builder
-	mkData.Custom(&builder, "override_foo", "BAZEL_TARGET_", "", mkData)
+			data := builder.String()
+			if w := "ALL_MODULES.$(my_register_name).BUNDLE := out/bazel/execroot/__main__/override_apex_bundle.zip"; !strings.Contains(data, w) {
+				t.Errorf("Expected %q in androidmk data, but did not find %q", w, data)
+			}
+			if w := "$(call dist-for-goals,checkbuild,out/bazel/execroot/__main__/override_installed-files.txt:override_foo-installed-files.txt)"; !strings.Contains(data, w) {
+				t.Errorf("Expected %q in androidmk data, but did not find %q", w, data)
+			}
 
-	data := builder.String()
-	if w := "ALL_MODULES.$(my_register_name).BUNDLE := out/bazel/execroot/__main__/override_apex_bundle.zip"; !strings.Contains(data, w) {
-		t.Errorf("Expected %q in androidmk data, but did not find %q", w, data)
-	}
-	if w := "$(call dist-for-goals,checkbuild,out/bazel/execroot/__main__/override_installed-files.txt:override_foo-installed-files.txt)"; !strings.Contains(data, w) {
-		t.Errorf("Expected %q in androidmk data, but did not find %q", w, data)
-	}
-
-	// make modules to be installed to system
-	if len(ab.makeModulesToInstall) != 1 && ab.makeModulesToInstall[0] != "c" {
-		t.Errorf("Expected makeModulestoInstall slice to only contain 'c', got %q", ab.makeModulesToInstall)
-	}
-	if w := "LOCAL_REQUIRED_MODULES := c"; !strings.Contains(data, w) {
-		t.Errorf("Expected %q in androidmk data, but did not find it in %q", w, data)
+			// make modules to be installed to system
+			if len(ab.makeModulesToInstall) != 1 || ab.makeModulesToInstall[0] != "c" {
+				t.Errorf("Expected makeModulestoInstall slice to only contain 'c', got %q", ab.makeModulesToInstall)
+			}
+			if w := "LOCAL_REQUIRED_MODULES := c"; !strings.Contains(data, w) {
+				t.Errorf("Expected %q in androidmk data, but did not find it in %q", w, data)
+			}
+		})
 	}
 }
diff --git a/apex/builder.go b/apex/builder.go
index 94aef49..a2fe2a0 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -231,7 +231,7 @@
 
 	apexSepolicyTestsRule = pctx.StaticRule("apexSepolicyTestsRule", blueprint.RuleParams{
 		Command: `${deapexer} --debugfs_path ${debugfs_static} list -Z ${in} > ${out}.fc` +
-			`&& ${apex_sepolicy_tests} -f ${out}.fc && touch ${out}`,
+			` && ${apex_sepolicy_tests} -f ${out}.fc && touch ${out}`,
 		CommandDeps: []string{"${apex_sepolicy_tests}", "${deapexer}", "${debugfs_static}"},
 		Description: "run apex_sepolicy_tests",
 	})
@@ -275,6 +275,22 @@
 
 	manifestJsonFullOut := android.PathForModuleOut(ctx, "apex_manifest_full.json")
 	defaultVersion := android.DefaultUpdatableModuleVersion
+	if a.properties.Variant_version != nil {
+		defaultVersionInt, err := strconv.Atoi(defaultVersion)
+		if err != nil {
+			ctx.ModuleErrorf("expected DefaultUpdatableModuleVersion to be an int, but got %s", defaultVersion)
+		}
+		if defaultVersionInt%10 != 0 {
+			ctx.ModuleErrorf("expected DefaultUpdatableModuleVersion to end in a zero, but got %s", defaultVersion)
+		}
+		variantVersion := []rune(*a.properties.Variant_version)
+		if len(variantVersion) != 1 || variantVersion[0] < '0' || variantVersion[0] > '9' {
+			ctx.PropertyErrorf("variant_version", "expected an integer between 0-9; got %s", *a.properties.Variant_version)
+		}
+		defaultVersionRunes := []rune(defaultVersion)
+		defaultVersionRunes[len(defaultVersion)-1] = []rune(variantVersion)[0]
+		defaultVersion = string(defaultVersionRunes)
+	}
 	if override := ctx.Config().Getenv("OVERRIDE_APEX_MANIFEST_DEFAULT_VERSION"); override != "" {
 		defaultVersion = override
 	}
@@ -314,7 +330,7 @@
 // buildFileContexts create build rules to append an entry for apex_manifest.pb to the file_contexts
 // file for this APEX which is either from /systme/sepolicy/apex/<apexname>-file_contexts or from
 // the file_contexts property of this APEX. This is to make sure that the manifest file is correctly
-// labeled as system_file.
+// labeled as system_file or vendor_apex_metadata_file.
 func (a *apexBundle) buildFileContexts(ctx android.ModuleContext) android.OutputPath {
 	var fileContexts android.Path
 	var fileContextsDir string
@@ -346,6 +362,13 @@
 	output := android.PathForModuleOut(ctx, "file_contexts")
 	rule := android.NewRuleBuilder(pctx, ctx)
 
+	forceLabel := "u:object_r:system_file:s0"
+	if a.SocSpecific() && !a.vndkApex {
+		// APEX on /vendor should label ./ and ./apex_manifest.pb as vendor_apex_metadata_file.
+		// The reason why we skip VNDK APEX is that aosp_{pixel device} targets install VNDK APEX on /vendor
+		// even though VNDK APEX is supposed to be installed on /system. (See com.android.vndk.current.on_vendor)
+		forceLabel = "u:object_r:vendor_apex_metadata_file:s0"
+	}
 	switch a.properties.ApexType {
 	case imageApex:
 		// remove old file
@@ -355,9 +378,9 @@
 		// new line
 		rule.Command().Text("echo").Text(">>").Output(output)
 		if !useFileContextsAsIs {
-			// force-label /apex_manifest.pb and / as system_file so that apexd can read them
-			rule.Command().Text("echo").Flag("/apex_manifest\\\\.pb u:object_r:system_file:s0").Text(">>").Output(output)
-			rule.Command().Text("echo").Flag("/ u:object_r:system_file:s0").Text(">>").Output(output)
+			// force-label /apex_manifest.pb and /
+			rule.Command().Text("echo").Text("/apex_manifest\\\\.pb").Text(forceLabel).Text(">>").Output(output)
+			rule.Command().Text("echo").Text("/").Text(forceLabel).Text(">>").Output(output)
 		}
 	case flattenedApex:
 		// For flattened apexes, install path should be prepended.
@@ -372,9 +395,9 @@
 		// new line
 		rule.Command().Text("echo").Text(">>").Output(output)
 		if !useFileContextsAsIs {
-			// force-label /apex_manifest.pb and / as system_file so that apexd can read them
-			rule.Command().Text("echo").Flag(apexPath + `/apex_manifest\\.pb u:object_r:system_file:s0`).Text(">>").Output(output)
-			rule.Command().Text("echo").Flag(apexPath + "/ u:object_r:system_file:s0").Text(">>").Output(output)
+			// force-label /apex_manifest.pb and /
+			rule.Command().Text("echo").Text(apexPath + "/apex_manifest\\\\.pb").Text(forceLabel).Text(">>").Output(output)
+			rule.Command().Text("echo").Text(apexPath + "/").Text(forceLabel).Text(">>").Output(output)
 		}
 	default:
 		panic(fmt.Errorf("unsupported type %v", a.properties.ApexType))
@@ -513,9 +536,6 @@
 				}
 			}
 			implicitInputs = append(implicitInputs, fi.builtFile)
-			if installSymbolFiles {
-				implicitInputs = append(implicitInputs, installedPath)
-			}
 
 			// Create additional symlinks pointing the file inside the APEX (if any). Note that
 			// this is independent from the symlink optimization.
@@ -523,8 +543,7 @@
 				symlinkDest := imageDir.Join(ctx, symlinkPath).String()
 				copyCommands = append(copyCommands, "ln -sfn "+filepath.Base(destPath)+" "+symlinkDest)
 				if installSymbolFiles {
-					installedSymlink := ctx.InstallSymlink(apexDir.Join(ctx, filepath.Dir(symlinkPath)), filepath.Base(symlinkPath), installedPath)
-					implicitInputs = append(implicitInputs, installedSymlink)
+					ctx.InstallSymlink(apexDir.Join(ctx, filepath.Dir(symlinkPath)), filepath.Base(symlinkPath), installedPath)
 				}
 			}
 
@@ -549,11 +568,6 @@
 		installMapSet[installMapPath.String()+":"+fi.installDir+"/"+fi.builtFile.Base()] = true
 	}
 	implicitInputs = append(implicitInputs, a.manifestPbOut)
-	if installSymbolFiles {
-		installedManifest := ctx.InstallFile(apexDir, "apex_manifest.pb", a.manifestPbOut)
-		installedKey := ctx.InstallFile(apexDir, "apex_pubkey", a.publicKeyFile)
-		implicitInputs = append(implicitInputs, installedManifest, installedKey)
-	}
 
 	if len(installMapSet) > 0 {
 		var installs []string
@@ -881,7 +895,8 @@
 		args["outCommaList"] = signedOutputFile.String()
 	}
 	var validations android.Paths
-	if suffix == imageApexSuffix {
+	// TODO(b/279688635) deapexer supports [ext4]
+	if suffix == imageApexSuffix && ext4 == a.payloadFsType {
 		validations = append(validations, runApexSepolicyTests(ctx, unsignedOutputFile.OutputPath))
 	}
 	ctx.Build(pctx, android.BuildParams{
diff --git a/apex/dexpreopt_bootjars_test.go b/apex/dexpreopt_bootjars_test.go
new file mode 100644
index 0000000..bba8bb6
--- /dev/null
+++ b/apex/dexpreopt_bootjars_test.go
@@ -0,0 +1,254 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package apex
+
+import (
+	"fmt"
+	"path/filepath"
+	"sort"
+	"testing"
+
+	"android/soong/android"
+	"android/soong/java"
+)
+
+func testDexpreoptBoot(t *testing.T, ruleFile string, expectedInputs, expectedOutputs []string, preferPrebuilt bool) {
+	bp := `
+		// Platform.
+
+		java_sdk_library {
+			name: "foo",
+			srcs: ["a.java"],
+			api_packages: ["foo"],
+		}
+
+		java_library {
+			name: "bar",
+			srcs: ["b.java"],
+			installable: true,
+			system_ext_specific: true,
+		}
+
+		dex_import {
+			name: "baz",
+			jars: ["a.jar"],
+		}
+
+		platform_bootclasspath {
+			name: "platform-bootclasspath",
+			fragments: [
+				{
+					apex: "com.android.art",
+					module: "art-bootclasspath-fragment",
+				},
+			],
+		}
+
+		// Source ART APEX.
+
+		java_library {
+			name: "core-oj",
+			srcs: ["core-oj.java"],
+			installable: true,
+			apex_available: [
+				"com.android.art",
+			],
+		}
+
+		bootclasspath_fragment {
+			name: "art-bootclasspath-fragment",
+			image_name: "art",
+			contents: ["core-oj"],
+			apex_available: [
+				"com.android.art",
+			],
+			hidden_api: {
+				split_packages: ["*"],
+			},
+		}
+
+		apex_key {
+			name: "com.android.art.key",
+			public_key: "com.android.art.avbpubkey",
+			private_key: "com.android.art.pem",
+		}
+
+		apex {
+			name: "com.android.art",
+			key: "com.android.art.key",
+			bootclasspath_fragments: ["art-bootclasspath-fragment"],
+			updatable: false,
+		}
+
+		// Prebuilt ART APEX.
+
+		java_import {
+			name: "core-oj",
+			prefer: %[1]t,
+			jars: ["core-oj.jar"],
+			apex_available: [
+				"com.android.art",
+			],
+		}
+
+		prebuilt_bootclasspath_fragment {
+			name: "art-bootclasspath-fragment",
+			prefer: %[1]t,
+			image_name: "art",
+			contents: ["core-oj"],
+			hidden_api: {
+				annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv",
+				metadata: "my-bootclasspath-fragment/metadata.csv",
+				index: "my-bootclasspath-fragment/index.csv",
+				stub_flags: "my-bootclasspath-fragment/stub-flags.csv",
+				all_flags: "my-bootclasspath-fragment/all-flags.csv",
+			},
+			apex_available: [
+				"com.android.art",
+			],
+		}
+
+		prebuilt_apex {
+			name: "com.android.art",
+			prefer: %[1]t,
+			apex_name: "com.android.art",
+			src: "com.android.art-arm.apex",
+			exported_bootclasspath_fragments: ["art-bootclasspath-fragment"],
+		}
+	`
+
+	result := android.GroupFixturePreparers(
+		java.PrepareForTestWithDexpreopt,
+		java.PrepareForTestWithJavaSdkLibraryFiles,
+		java.FixtureWithLastReleaseApis("foo"),
+		java.FixtureConfigureBootJars("com.android.art:core-oj", "platform:foo", "system_ext:bar", "platform:baz"),
+		PrepareForTestWithApexBuildComponents,
+		prepareForTestWithArtApex,
+	).RunTestWithBp(t, fmt.Sprintf(bp, preferPrebuilt))
+
+	platformBootclasspath := result.ModuleForTests("platform-bootclasspath", "android_common")
+	rule := platformBootclasspath.Output(ruleFile)
+
+	inputs := rule.Implicits.Strings()
+	sort.Strings(inputs)
+	sort.Strings(expectedInputs)
+
+	outputs := append(android.WritablePaths{rule.Output}, rule.ImplicitOutputs...).Strings()
+	sort.Strings(outputs)
+	sort.Strings(expectedOutputs)
+
+	android.AssertStringPathsRelativeToTopEquals(t, "inputs", result.Config, expectedInputs, inputs)
+
+	android.AssertStringPathsRelativeToTopEquals(t, "outputs", result.Config, expectedOutputs, outputs)
+}
+
+func TestDexpreoptBootJarsWithSourceArtApex(t *testing.T) {
+	ruleFile := "boot.art"
+
+	expectedInputs := []string{
+		"out/soong/dexpreopt_arm64/dex_bootjars_input/core-oj.jar",
+		"out/soong/dexpreopt_arm64/dex_bootjars_input/foo.jar",
+		"out/soong/dexpreopt_arm64/dex_bootjars_input/bar.jar",
+		"out/soong/dexpreopt_arm64/dex_bootjars_input/baz.jar",
+		"out/soong/dexpreopt_arm64/dex_artjars/boot.prof",
+		"out/soong/dexpreopt_arm64/dex_bootjars/boot.prof",
+	}
+
+	expectedOutputs := []string{
+		"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.invocation",
+		"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.art",
+		"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-foo.art",
+		"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-bar.art",
+		"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-baz.art",
+		"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.oat",
+		"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-foo.oat",
+		"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-bar.oat",
+		"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-baz.oat",
+		"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.vdex",
+		"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-foo.vdex",
+		"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-bar.vdex",
+		"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-baz.vdex",
+		"out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm64/boot.oat",
+		"out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm64/boot-foo.oat",
+		"out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm64/boot-bar.oat",
+		"out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm64/boot-baz.oat",
+	}
+
+	testDexpreoptBoot(t, ruleFile, expectedInputs, expectedOutputs, false)
+}
+
+// The only difference is that the ART profile should be deapexed from the prebuilt APEX. Other
+// inputs and outputs should be the same as above.
+func TestDexpreoptBootJarsWithPrebuiltArtApex(t *testing.T) {
+	ruleFile := "boot.art"
+
+	expectedInputs := []string{
+		"out/soong/dexpreopt_arm64/dex_bootjars_input/core-oj.jar",
+		"out/soong/dexpreopt_arm64/dex_bootjars_input/foo.jar",
+		"out/soong/dexpreopt_arm64/dex_bootjars_input/bar.jar",
+		"out/soong/dexpreopt_arm64/dex_bootjars_input/baz.jar",
+		"out/soong/.intermediates/com.android.art.deapexer/android_common/deapexer/etc/boot-image.prof",
+		"out/soong/dexpreopt_arm64/dex_bootjars/boot.prof",
+	}
+
+	expectedOutputs := []string{
+		"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.invocation",
+		"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.art",
+		"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-foo.art",
+		"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-bar.art",
+		"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-baz.art",
+		"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.oat",
+		"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-foo.oat",
+		"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-bar.oat",
+		"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-baz.oat",
+		"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.vdex",
+		"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-foo.vdex",
+		"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-bar.vdex",
+		"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-baz.vdex",
+		"out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm64/boot.oat",
+		"out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm64/boot-foo.oat",
+		"out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm64/boot-bar.oat",
+		"out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm64/boot-baz.oat",
+	}
+
+	testDexpreoptBoot(t, ruleFile, expectedInputs, expectedOutputs, true)
+}
+
+// Changes to the boot.zip structure may break the ART APK scanner.
+func TestDexpreoptBootZip(t *testing.T) {
+	ruleFile := "boot.zip"
+
+	ctx := android.PathContextForTesting(android.TestArchConfig("", nil, "", nil))
+	expectedInputs := []string{}
+	for _, target := range ctx.Config().Targets[android.Android] {
+		for _, ext := range []string{".art", ".oat", ".vdex"} {
+			for _, suffix := range []string{"", "-foo", "-bar", "-baz"} {
+				expectedInputs = append(expectedInputs,
+					filepath.Join(
+						"out/soong/dexpreopt_arm64/dex_bootjars",
+						target.Os.String(),
+						"system/framework",
+						target.Arch.ArchType.String(),
+						"boot"+suffix+ext))
+			}
+		}
+	}
+
+	expectedOutputs := []string{
+		"out/soong/dexpreopt_arm64/dex_bootjars/boot.zip",
+	}
+
+	testDexpreoptBoot(t, ruleFile, expectedInputs, expectedOutputs, false)
+}
diff --git a/apex/key.go b/apex/key.go
index 0a7e80f..3010d76 100644
--- a/apex/key.go
+++ b/apex/key.go
@@ -33,7 +33,7 @@
 
 func registerApexKeyBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("apex_key", ApexKeyFactory)
-	ctx.RegisterSingletonType("apex_keys_text", apexKeysTextFactory)
+	ctx.RegisterParallelSingletonType("apex_keys_text", apexKeysTextFactory)
 }
 
 type apexKey struct {
diff --git a/apex/metadata.go b/apex/metadata.go
deleted file mode 100644
index b1dff3e..0000000
--- a/apex/metadata.go
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package apex
-
-import (
-	"encoding/json"
-
-	"github.com/google/blueprint"
-
-	"android/soong/android"
-)
-
-var (
-	mtctx = android.NewPackageContext("android/soong/multitree_apex")
-)
-
-func init() {
-	RegisterModulesSingleton(android.InitRegistrationContext)
-}
-
-func RegisterModulesSingleton(ctx android.RegistrationContext) {
-	ctx.RegisterSingletonType("apex_multitree_singleton", multitreeAnalysisSingletonFactory)
-}
-
-var PrepareForTestWithApexMultitreeSingleton = android.FixtureRegisterWithContext(RegisterModulesSingleton)
-
-func multitreeAnalysisSingletonFactory() android.Singleton {
-	return &multitreeAnalysisSingleton{}
-}
-
-type multitreeAnalysisSingleton struct {
-	multitreeApexMetadataPath android.OutputPath
-}
-
-type ApexMultitreeMetadataEntry struct {
-	// The name of the apex.
-	Name string
-
-	// TODO: Add other properties as needed.
-}
-
-type ApexMultitreeMetadata struct {
-	// Information about the installable apexes.
-	Apexes map[string]ApexMultitreeMetadataEntry
-}
-
-func (p *multitreeAnalysisSingleton) GenerateBuildActions(context android.SingletonContext) {
-	data := ApexMultitreeMetadata{
-		Apexes: make(map[string]ApexMultitreeMetadataEntry, 0),
-	}
-	context.VisitAllModules(func(module android.Module) {
-		// If this module is not being installed, ignore it.
-		if !module.Enabled() || module.IsSkipInstall() {
-			return
-		}
-		// Actual apexes provide ApexBundleInfoProvider.
-		if _, ok := context.ModuleProvider(module, ApexBundleInfoProvider).(ApexBundleInfo); !ok {
-			return
-		}
-		bundle, ok := module.(*apexBundle)
-		if ok && !bundle.testApex && !bundle.vndkApex && bundle.primaryApexType {
-			name := module.Name()
-			entry := ApexMultitreeMetadataEntry{
-				Name: name,
-			}
-			data.Apexes[name] = entry
-		}
-	})
-	p.multitreeApexMetadataPath = android.PathForOutput(context, "multitree_apex_metadata.json")
-
-	jsonStr, err := json.Marshal(data)
-	if err != nil {
-		context.Errorf(err.Error())
-	}
-	android.WriteFileRule(context, p.multitreeApexMetadataPath, string(jsonStr))
-	// This seems cleaner, but doesn't emit the phony rule in testing.
-	// context.Phony("multitree_apex_metadata", p.multitreeApexMetadataPath)
-
-	context.Build(mtctx, android.BuildParams{
-		Rule:        blueprint.Phony,
-		Description: "phony rule for multitree_apex_metadata",
-		Inputs:      []android.Path{p.multitreeApexMetadataPath},
-		Output:      android.PathForPhony(context, "multitree_apex_metadata"),
-	})
-}
diff --git a/apex/metadata_test.go b/apex/metadata_test.go
deleted file mode 100644
index f6ead42..0000000
--- a/apex/metadata_test.go
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package apex
-
-import (
-	"strings"
-	"testing"
-
-	"android/soong/android"
-	"android/soong/java"
-)
-
-func TestModulesSingleton(t *testing.T) {
-	result := android.GroupFixturePreparers(
-		PrepareForTestWithApexMultitreeSingleton,
-		java.PrepareForTestWithDexpreopt,
-		PrepareForTestWithApexBuildComponents,
-		java.FixtureConfigureApexBootJars("myapex:foo"),
-		java.PrepareForTestWithJavaSdkLibraryFiles,
-	).RunTestWithBp(t, `
-		prebuilt_apex {
-			name: "myapex",
-			src: "myapex.apex",
-			exported_bootclasspath_fragments: ["mybootclasspath-fragment"],
-		}
-
-		// A prebuilt java_sdk_library_import that is not preferred by default but will be preferred
-		// because AlwaysUsePrebuiltSdks() is true.
-		java_sdk_library_import {
-			name: "foo",
-			prefer: false,
-			shared_library: false,
-			permitted_packages: ["foo"],
-			public: {
-				jars: ["sdk_library/public/foo-stubs.jar"],
-				stub_srcs: ["sdk_library/public/foo_stub_sources"],
-				current_api: "sdk_library/public/foo.txt",
-				removed_api: "sdk_library/public/foo-removed.txt",
-				sdk_version: "current",
-			},
-			apex_available: ["myapex"],
-		}
-
-		prebuilt_bootclasspath_fragment {
-			name: "mybootclasspath-fragment",
-			apex_available: [
-				"myapex",
-			],
-			contents: [
-				"foo",
-			],
-			hidden_api: {
-				stub_flags: "prebuilt-stub-flags.csv",
-				annotation_flags: "prebuilt-annotation-flags.csv",
-				metadata: "prebuilt-metadata.csv",
-				index: "prebuilt-index.csv",
-				all_flags: "prebuilt-all-flags.csv",
-			},
-		}
-
-		platform_bootclasspath {
-			name: "myplatform-bootclasspath",
-			fragments: [
-				{
-					apex: "myapex",
-					module:"mybootclasspath-fragment",
-				},
-			],
-		}
-`,
-	)
-
-	outputs := result.SingletonForTests("apex_multitree_singleton").AllOutputs()
-	for _, output := range outputs {
-		testingBuildParam := result.SingletonForTests("apex_multitree_singleton").Output(output)
-		switch {
-		case strings.Contains(output, "soong/multitree_apex_metadata.json"):
-			android.AssertStringEquals(t, "Invalid build rule", "android/soong/android.writeFile", testingBuildParam.Rule.String())
-			android.AssertIntEquals(t, "Invalid input", len(testingBuildParam.Inputs), 0)
-			android.AssertStringDoesContain(t, "Invalid output path", output, "soong/multitree_apex_metadata.json")
-
-		case strings.HasSuffix(output, "multitree_apex_metadata"):
-			android.AssertStringEquals(t, "Invalid build rule", "<builtin>:phony", testingBuildParam.Rule.String())
-			android.AssertStringEquals(t, "Invalid input", testingBuildParam.Inputs[0].String(), "out/soong/multitree_apex_metadata.json")
-			android.AssertStringEquals(t, "Invalid output path", output, "multitree_apex_metadata")
-			android.AssertIntEquals(t, "Invalid args", len(testingBuildParam.Args), 0)
-		}
-	}
-}
diff --git a/apex/platform_bootclasspath_test.go b/apex/platform_bootclasspath_test.go
index 4b48da8..05bb136 100644
--- a/apex/platform_bootclasspath_test.go
+++ b/apex/platform_bootclasspath_test.go
@@ -20,6 +20,7 @@
 	"testing"
 
 	"android/soong/android"
+	"android/soong/dexpreopt"
 	"android/soong/java"
 
 	"github.com/google/blueprint"
@@ -30,7 +31,7 @@
 // apexes.
 
 var prepareForTestWithPlatformBootclasspath = android.GroupFixturePreparers(
-	java.PrepareForTestWithDexpreopt,
+	java.PrepareForTestWithJavaDefaultModules,
 	PrepareForTestWithApexBuildComponents,
 )
 
@@ -249,6 +250,8 @@
 		java.FixtureConfigureApexBootJars("myapex:bar"),
 		java.PrepareForTestWithJavaSdkLibraryFiles,
 		java.FixtureWithLastReleaseApis("foo"),
+		java.PrepareForTestWithDexpreopt,
+		dexpreopt.FixtureDisableDexpreoptBootImages(false),
 	).RunTestWithBp(t, `
 		apex {
 			name: "com.android.art",
@@ -539,9 +542,6 @@
 		// Not a prebuilt as no prebuilt existed when it was added.
 		"platform:legacy.core.platform.api.stubs",
 
-		// Needed for generating the boot image.
-		"platform:dex2oatd",
-
 		// The platform_bootclasspath intentionally adds dependencies on both source and prebuilt
 		// modules when available as it does not know which one will be preferred.
 		"myapex:foo",
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index cae507e..0d83830 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -199,14 +199,11 @@
 				p.apexFilesForAndroidMk = append(p.apexFilesForAndroidMk, af)
 			}
 		} else if tag == exportedBootclasspathFragmentTag {
-			bcpfModule, ok := child.(*java.PrebuiltBootclasspathFragmentModule)
+			_, ok := child.(*java.PrebuiltBootclasspathFragmentModule)
 			if !ok {
 				ctx.PropertyErrorf("exported_bootclasspath_fragments", "%q is not a prebuilt_bootclasspath_fragment module", name)
 				return false
 			}
-			for _, makeModuleName := range bcpfModule.BootImageDeviceInstallMakeModules() {
-				p.requiredModuleNames = append(p.requiredModuleNames, makeModuleName)
-			}
 			// Visit the children of the bootclasspath_fragment.
 			return true
 		} else if tag == exportedSystemserverclasspathFragmentTag {
@@ -316,31 +313,29 @@
 	}
 }
 
-func (p *prebuiltCommon) getExportedDependencies() map[string]exportedDependencyTag {
-	dependencies := make(map[string]exportedDependencyTag)
-
-	for _, dep := range p.prebuiltCommonProperties.Exported_java_libs {
-		dependencies[dep] = exportedJavaLibTag
-	}
-
-	for _, dep := range p.prebuiltCommonProperties.Exported_bootclasspath_fragments {
-		dependencies[dep] = exportedBootclasspathFragmentTag
-	}
-
-	for _, dep := range p.prebuiltCommonProperties.Exported_systemserverclasspath_fragments {
-		dependencies[dep] = exportedSystemserverclasspathFragmentTag
-	}
-
-	return dependencies
+func (p *prebuiltCommon) hasExportedDeps() bool {
+	return len(p.prebuiltCommonProperties.Exported_java_libs) > 0 ||
+		len(p.prebuiltCommonProperties.Exported_bootclasspath_fragments) > 0 ||
+		len(p.prebuiltCommonProperties.Exported_systemserverclasspath_fragments) > 0
 }
 
 // prebuiltApexContentsDeps adds dependencies onto the prebuilt apex module's contents.
 func (p *prebuiltCommon) prebuiltApexContentsDeps(ctx android.BottomUpMutatorContext) {
 	module := ctx.Module()
 
-	for dep, tag := range p.getExportedDependencies() {
+	for _, dep := range p.prebuiltCommonProperties.Exported_java_libs {
 		prebuiltDep := android.PrebuiltNameFromSource(dep)
-		ctx.AddDependency(module, tag, prebuiltDep)
+		ctx.AddDependency(module, exportedJavaLibTag, prebuiltDep)
+	}
+
+	for _, dep := range p.prebuiltCommonProperties.Exported_bootclasspath_fragments {
+		prebuiltDep := android.PrebuiltNameFromSource(dep)
+		ctx.AddDependency(module, exportedBootclasspathFragmentTag, prebuiltDep)
+	}
+
+	for _, dep := range p.prebuiltCommonProperties.Exported_systemserverclasspath_fragments {
+		prebuiltDep := android.PrebuiltNameFromSource(dep)
+		ctx.AddDependency(module, exportedSystemserverclasspathFragmentTag, prebuiltDep)
 	}
 }
 
@@ -608,7 +603,7 @@
 // the listed modules need access to files from within the prebuilt .apex file.
 func (p *prebuiltCommon) createDeapexerModuleIfNeeded(ctx android.TopDownMutatorContext, deapexerName string, apexFileSource string) {
 	// Only create the deapexer module if it is needed.
-	if len(p.getExportedDependencies()) == 0 {
+	if !p.hasExportedDeps() {
 		return
 	}
 
diff --git a/apex/testing.go b/apex/testing.go
index 69bd73e..3b200f0 100644
--- a/apex/testing.go
+++ b/apex/testing.go
@@ -19,6 +19,7 @@
 var PrepareForTestWithApexBuildComponents = android.GroupFixturePreparers(
 	android.FixtureRegisterWithContext(registerApexBuildComponents),
 	android.FixtureRegisterWithContext(registerApexKeyBuildComponents),
+	android.FixtureRegisterWithContext(registerApexDepsInfoComponents),
 	// Additional files needed in tests that disallow non-existent source files.
 	// This includes files that are needed by all, or at least most, instances of an apex module type.
 	android.MockFS{
diff --git a/bazel/bazel_proxy.go b/bazel/bazel_proxy.go
index d7f5e64..229818d 100644
--- a/bazel/bazel_proxy.go
+++ b/bazel/bazel_proxy.go
@@ -26,10 +26,11 @@
 	"time"
 )
 
-// Logs fatal events of ProxyServer.
+// Logs events of ProxyServer.
 type ServerLogger interface {
 	Fatal(v ...interface{})
 	Fatalf(format string, v ...interface{})
+	Println(v ...interface{})
 }
 
 // CmdRequest is a request to the Bazel Proxy server.
@@ -71,9 +72,10 @@
 // The ProxyServer will only live as long as soong_ui does; the
 // underlying Bazel server will live past the duration of the build.
 type ProxyServer struct {
-	logger       ServerLogger
-	outDir       string
-	workspaceDir string
+	logger          ServerLogger
+	outDir          string
+	workspaceDir    string
+	bazeliskVersion string
 	// The server goroutine will listen on this channel and stop handling requests
 	// once it is written to.
 	done chan struct{}
@@ -119,13 +121,36 @@
 }
 
 // NewProxyServer is a constructor for a ProxyServer.
-func NewProxyServer(logger ServerLogger, outDir string, workspaceDir string) *ProxyServer {
-	return &ProxyServer{
-		logger:       logger,
-		outDir:       outDir,
-		workspaceDir: workspaceDir,
-		done:         make(chan struct{}),
+func NewProxyServer(logger ServerLogger, outDir string, workspaceDir string, bazeliskVersion string) *ProxyServer {
+	if len(bazeliskVersion) > 0 {
+		logger.Println("** Using Bazelisk for this build, due to env var USE_BAZEL_VERSION=" + bazeliskVersion + " **")
 	}
+
+	return &ProxyServer{
+		logger:          logger,
+		outDir:          outDir,
+		workspaceDir:    workspaceDir,
+		done:            make(chan struct{}),
+		bazeliskVersion: bazeliskVersion,
+	}
+}
+
+func ExecBazel(bazelPath string, workspaceDir string, request CmdRequest) (stdout []byte, stderr []byte, cmdErr error) {
+	bazelCmd := exec.Command(bazelPath, request.Argv...)
+	bazelCmd.Dir = workspaceDir
+	bazelCmd.Env = request.Env
+
+	stderrBuffer := &bytes.Buffer{}
+	bazelCmd.Stderr = stderrBuffer
+
+	if output, err := bazelCmd.Output(); err != nil {
+		cmdErr = fmt.Errorf("bazel command failed: %s\n---command---\n%s\n---env---\n%s\n---stderr---\n%s---",
+			err, bazelCmd, strings.Join(bazelCmd.Env, "\n"), stderrBuffer)
+	} else {
+		stdout = output
+	}
+	stderr = stderrBuffer.Bytes()
+	return
 }
 
 func (b *ProxyServer) handleRequest(conn net.Conn) error {
@@ -137,23 +162,16 @@
 		return fmt.Errorf("Error decoding request: %s", err)
 	}
 
-	bazelCmd := exec.Command("./build/bazel/bin/bazel", req.Argv...)
-	bazelCmd.Dir = b.workspaceDir
-	bazelCmd.Env = req.Env
-
-	stderr := &bytes.Buffer{}
-	bazelCmd.Stderr = stderr
-	var stdout string
-	var bazelErrString string
-
-	if output, err := bazelCmd.Output(); err != nil {
-		bazelErrString = fmt.Sprintf("bazel command failed: %s\n---command---\n%s\n---env---\n%s\n---stderr---\n%s---",
-			err, bazelCmd, strings.Join(bazelCmd.Env, "\n"), stderr)
-	} else {
-		stdout = string(output)
+	if len(b.bazeliskVersion) > 0 {
+		req.Env = append(req.Env, "USE_BAZEL_VERSION="+b.bazeliskVersion)
+	}
+	stdout, stderr, cmdErr := ExecBazel("./build/bazel/bin/bazel", b.workspaceDir, req)
+	errorString := ""
+	if cmdErr != nil {
+		errorString = cmdErr.Error()
 	}
 
-	resp := CmdResponse{stdout, string(stderr.Bytes()), bazelErrString}
+	resp := CmdResponse{string(stdout), string(stderr), errorString}
 	enc := gob.NewEncoder(conn)
 	if err := enc.Encode(&resp); err != nil {
 		return fmt.Errorf("Error encoding response: %s", err)
diff --git a/bazel/configurability.go b/bazel/configurability.go
index 4680256..8f63ec4 100644
--- a/bazel/configurability.go
+++ b/bazel/configurability.go
@@ -31,11 +31,11 @@
 
 	// OsType names in arch.go
 	OsAndroid     = "android"
-	osDarwin      = "darwin"
-	osLinux       = "linux_glibc"
+	OsDarwin      = "darwin"
+	OsLinux       = "linux_glibc"
 	osLinuxMusl   = "linux_musl"
 	osLinuxBionic = "linux_bionic"
-	osWindows     = "windows"
+	OsWindows     = "windows"
 
 	// Targets in arch.go
 	osArchAndroidArm        = "android_arm"
@@ -69,8 +69,8 @@
 
 	productVariableBazelPackage = "//build/bazel/product_variables"
 
-	AndroidAndInApex  = "android-in_apex"
-	AndroidAndNonApex = "android-non_apex"
+	AndroidAndInApex = "android-in_apex"
+	AndroidPlatform  = "system"
 
 	InApex  = "in_apex"
 	NonApex = "non_apex"
@@ -156,11 +156,11 @@
 	// constraint_value for the @platforms//os:os constraint_setting
 	platformOsMap = map[string]string{
 		OsAndroid:                  "//build/bazel/platforms/os:android",
-		osDarwin:                   "//build/bazel/platforms/os:darwin",
-		osLinux:                    "//build/bazel/platforms/os:linux_glibc",
+		OsDarwin:                   "//build/bazel/platforms/os:darwin",
+		OsLinux:                    "//build/bazel/platforms/os:linux_glibc",
 		osLinuxMusl:                "//build/bazel/platforms/os:linux_musl",
 		osLinuxBionic:              "//build/bazel/platforms/os:linux_bionic",
-		osWindows:                  "//build/bazel/platforms/os:windows",
+		OsWindows:                  "//build/bazel/platforms/os:windows",
 		ConditionsDefaultConfigKey: ConditionsDefaultSelectKey, // The default condition of an os select map.
 	}
 
@@ -192,22 +192,22 @@
 	// in a cyclic dependency.
 	osToArchMap = map[string][]string{
 		OsAndroid:     {archArm, archArm64, archRiscv64, archX86, archX86_64},
-		osLinux:       {archX86, archX86_64},
+		OsLinux:       {archX86, archX86_64},
 		osLinuxMusl:   {archX86, archX86_64},
-		osDarwin:      {archArm64, archX86_64},
+		OsDarwin:      {archArm64, archX86_64},
 		osLinuxBionic: {archArm64, archX86_64},
 		// TODO(cparsons): According to arch.go, this should contain archArm, archArm64, as well.
-		osWindows: {archX86, archX86_64},
+		OsWindows: {archX86, archX86_64},
 	}
 
 	osAndInApexMap = map[string]string{
 		AndroidAndInApex:           "//build/bazel/rules/apex:android-in_apex",
-		AndroidAndNonApex:          "//build/bazel/rules/apex:android-non_apex",
-		osDarwin:                   "//build/bazel/platforms/os:darwin",
-		osLinux:                    "//build/bazel/platforms/os:linux_glibc",
+		AndroidPlatform:            "//build/bazel/rules/apex:system",
+		OsDarwin:                   "//build/bazel/platforms/os:darwin",
+		OsLinux:                    "//build/bazel/platforms/os:linux_glibc",
 		osLinuxMusl:                "//build/bazel/platforms/os:linux_musl",
 		osLinuxBionic:              "//build/bazel/platforms/os:linux_bionic",
-		osWindows:                  "//build/bazel/platforms/os:windows",
+		OsWindows:                  "//build/bazel/platforms/os:windows",
 		ConditionsDefaultConfigKey: ConditionsDefaultSelectKey,
 	}
 
@@ -268,9 +268,8 @@
 	case productVariables:
 		// do nothing
 	case osAndInApex:
-		if _, ok := osAndInApexMap[config]; !ok {
-			panic(fmt.Errorf("Unknown os+in_apex config: %s", config))
-		}
+		// do nothing
+		// this axis can contain additional per-apex keys
 	case inApex:
 		if _, ok := inApexMap[config]; !ok {
 			panic(fmt.Errorf("Unknown in_apex config: %s", config))
@@ -293,13 +292,15 @@
 	case osArch:
 		return platformOsArchMap[config]
 	case productVariables:
-		if strings.HasSuffix(config, ConditionsDefaultConfigKey) {
-			// e.g. "acme__feature1__conditions_default" or "android__board__conditions_default"
+		if config == ConditionsDefaultConfigKey {
 			return ConditionsDefaultSelectKey
 		}
 		return fmt.Sprintf("%s:%s", productVariableBazelPackage, config)
 	case osAndInApex:
-		return osAndInApexMap[config]
+		if ret, exists := osAndInApexMap[config]; exists {
+			return ret
+		}
+		return config
 	case inApex:
 		return inApexMap[config]
 	default:
@@ -323,11 +324,11 @@
 )
 
 // ProductVariableConfigurationAxis returns an axis for the given product variable
-func ProductVariableConfigurationAxis(variable string, outerAxis ConfigurationAxis) ConfigurationAxis {
+func ProductVariableConfigurationAxis(archVariant bool, variable string) ConfigurationAxis {
 	return ConfigurationAxis{
 		configurationType: productVariables,
 		subType:           variable,
-		outerAxisType:     outerAxis.configurationType,
+		archVariant:       archVariant,
 	}
 }
 
@@ -338,8 +339,8 @@
 	// some configuration types (e.g. productVariables) have multiple independent axes, subType helps
 	// distinguish between them without needing to list all 17 product variables.
 	subType string
-	// used to keep track of which product variables are arch variant
-	outerAxisType configurationType
+
+	archVariant bool
 }
 
 func (ca *ConfigurationAxis) less(other ConfigurationAxis) bool {
diff --git a/bazel/cquery/request_type.go b/bazel/cquery/request_type.go
index 6a3b3c8..791c6bc 100644
--- a/bazel/cquery/request_type.go
+++ b/bazel/cquery/request_type.go
@@ -8,10 +8,10 @@
 
 var (
 	GetOutputFiles      = &getOutputFilesRequestType{}
-	GetPythonBinary     = &getPythonBinaryRequestType{}
 	GetCcInfo           = &getCcInfoType{}
 	GetApexInfo         = &getApexInfoType{}
 	GetCcUnstrippedInfo = &getCcUnstrippedInfoType{}
+	GetPrebuiltFileInfo = &getPrebuiltFileInfo{}
 )
 
 type CcAndroidMkInfo struct {
@@ -45,8 +45,6 @@
 
 type getOutputFilesRequestType struct{}
 
-type getPythonBinaryRequestType struct{}
-
 // Name returns a string name for this request type. Such request type names must be unique,
 // and must only consist of alphanumeric characters.
 func (g getOutputFilesRequestType) Name() string {
@@ -72,31 +70,6 @@
 	return splitOrEmpty(rawString, ", ")
 }
 
-// Name returns a string name for this request type. Such request type names must be unique,
-// and must only consist of alphanumeric characters.
-func (g getPythonBinaryRequestType) Name() string {
-	return "getPythonBinary"
-}
-
-// StarlarkFunctionBody returns a starlark function body to process this request type.
-// The returned string is the body of a Starlark function which obtains
-// all request-relevant information about a target and returns a string containing
-// this information.
-// The function should have the following properties:
-//   - The arguments are `target` (a configured target) and `id_string` (the label + configuration).
-//   - The return value must be a string.
-//   - The function body should not be indented outside of its own scope.
-func (g getPythonBinaryRequestType) StarlarkFunctionBody() string {
-	return "return providers(target)['FilesToRunProvider'].executable.path"
-}
-
-// ParseResult returns a value obtained by parsing the result of the request's Starlark function.
-// The given rawString must correspond to the string output which was created by evaluating the
-// Starlark given in StarlarkFunctionBody.
-func (g getPythonBinaryRequestType) ParseResult(rawString string) string {
-	return rawString
-}
-
 type getCcInfoType struct{}
 
 // Name returns a string name for this request type. Such request type names must be unique,
@@ -403,3 +376,51 @@
 	}
 	return nil
 }
+
+type getPrebuiltFileInfo struct{}
+
+// Name returns a string name for this request type. Such request type names must be unique,
+// and must only consist of alphanumeric characters.
+func (g getPrebuiltFileInfo) Name() string {
+	return "getPrebuiltFileInfo"
+}
+
+// StarlarkFunctionBody returns a starlark function body to process this request type.
+// The returned string is the body of a Starlark function which obtains
+// all request-relevant information about a target and returns a string containing
+// this information.
+// The function should have the following properties:
+//   - The arguments are `target` (a configured target) and `id_string` (the label + configuration).
+//   - The return value must be a string.
+//   - The function body should not be indented outside of its own scope.
+func (g getPrebuiltFileInfo) StarlarkFunctionBody() string {
+	return `
+p = providers(target)
+prebuilt_file_info = p.get("//build/bazel/rules:prebuilt_file.bzl%PrebuiltFileInfo")
+if not prebuilt_file_info:
+  fail("%s did not provide PrebuiltFileInfo" % id_string)
+
+return json.encode({
+	"Src": prebuilt_file_info.src.path,
+	"Dir": prebuilt_file_info.dir,
+	"Filename": prebuilt_file_info.filename,
+	"Installable": prebuilt_file_info.installable,
+})`
+}
+
+type PrebuiltFileInfo struct {
+	// TODO: b/207489266 - Fully support all properties in prebuilt_file
+	Src         string
+	Dir         string
+	Filename    string
+	Installable bool
+}
+
+// ParseResult returns a value obtained by parsing the result of the request's Starlark function.
+// The given rawString must correspond to the string output which was created by evaluating the
+// Starlark given in StarlarkFunctionBody.
+func (g getPrebuiltFileInfo) ParseResult(rawString string) (PrebuiltFileInfo, error) {
+	var info PrebuiltFileInfo
+	err := parseJson(rawString, &info)
+	return info, err
+}
diff --git a/bazel/cquery/request_type_test.go b/bazel/cquery/request_type_test.go
index 7003ce1..e772bb7 100644
--- a/bazel/cquery/request_type_test.go
+++ b/bazel/cquery/request_type_test.go
@@ -40,34 +40,6 @@
 	}
 }
 
-func TestGetPythonBinaryParseResults(t *testing.T) {
-	t.Parallel()
-	testCases := []struct {
-		description    string
-		input          string
-		expectedOutput string
-	}{
-		{
-			description:    "no result",
-			input:          "",
-			expectedOutput: "",
-		},
-		{
-			description:    "one result",
-			input:          "test",
-			expectedOutput: "test",
-		},
-	}
-	for _, tc := range testCases {
-		t.Run(tc.description, func(t *testing.T) {
-			actualOutput := GetPythonBinary.ParseResult(tc.input)
-			if !reflect.DeepEqual(tc.expectedOutput, actualOutput) {
-				t.Errorf("expected %#v != actual %#v", tc.expectedOutput, actualOutput)
-			}
-		})
-	}
-}
-
 func TestGetCcInfoParseResults(t *testing.T) {
 	t.Parallel()
 	testCases := []struct {
diff --git a/bazel/properties.go b/bazel/properties.go
index 40d0ba3..e22f4db 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -334,7 +334,7 @@
 			if containsArch {
 				allProductVariablesAreArchVariant := true
 				for k := range la.ConfigurableValues {
-					if k.configurationType == productVariables && k.outerAxisType != arch {
+					if k.configurationType == productVariables && !k.archVariant {
 						allProductVariablesAreArchVariant = false
 					}
 				}
@@ -1424,3 +1424,16 @@
 	sub := productVariableSubstitutionPattern.ReplaceAllString(s, "$("+productVariable+")")
 	return sub, s != sub
 }
+
+// StringMapAttribute is a map of strings.
+// The use case for this is storing the flag_values in a config_setting object.
+// Bazel rules do not support map attributes, and this should NOT be used in Bazel rules.
+type StringMapAttribute map[string]string
+
+// ConfigSettingAttributes stores the keys of a config_setting object.
+type ConfigSettingAttributes struct {
+	// Each key in Flag_values is a label to a custom string_setting
+	Flag_values StringMapAttribute
+	// Each element in Constraint_values is a label to a constraint_value
+	Constraint_values LabelListAttribute
+}
diff --git a/bazel/properties_test.go b/bazel/properties_test.go
index cf03eb5..c56d11f 100644
--- a/bazel/properties_test.go
+++ b/bazel/properties_test.go
@@ -248,13 +248,13 @@
 			OsArchConfigurationAxis: labelListSelectValues{
 				"linux_x86": makeLabelList([]string{"linux_x86_include"}, []string{}),
 			},
-			ProductVariableConfigurationAxis("product_with_defaults", NoConfigAxis): labelListSelectValues{
+			ProductVariableConfigurationAxis(false, "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", "all_exclude"}, []string{}),
 			},
-			ProductVariableConfigurationAxis("product_only_with_excludes", NoConfigAxis): labelListSelectValues{
+			ProductVariableConfigurationAxis(false, "product_only_with_excludes"): labelListSelectValues{
 				"a": makeLabelList([]string{}, []string{"product_config_exclude"}),
 			},
 		},
@@ -282,13 +282,13 @@
 			"linux_x86":                makeLabels("linux_x86_include"),
 			ConditionsDefaultConfigKey: nilLabels,
 		},
-		ProductVariableConfigurationAxis("product_with_defaults", NoConfigAxis): {
+		ProductVariableConfigurationAxis(false, "product_with_defaults"): {
 			"a":                        nilLabels,
 			"b":                        makeLabels("b_val"),
 			"c":                        makeLabels("c_val"),
 			ConditionsDefaultConfigKey: makeLabels("c_val", "default", "default2"),
 		},
-		ProductVariableConfigurationAxis("product_only_with_excludes", NoConfigAxis): {
+		ProductVariableConfigurationAxis(false, "product_only_with_excludes"): {
 			"a":                        nilLabels,
 			ConditionsDefaultConfigKey: makeLabels("product_config_exclude"),
 		},
@@ -679,7 +679,7 @@
 			OsArchConfigurationAxis: stringListSelectValues{
 				"linux_x86": {"linux_x86_include"},
 			},
-			ProductVariableConfigurationAxis("a", NoConfigAxis): stringListSelectValues{
+			ProductVariableConfigurationAxis(false, "a"): stringListSelectValues{
 				"a": []string{"not_in_value"},
 			},
 		},
@@ -704,7 +704,7 @@
 			"linux": []string{"linux_include"},
 		},
 		OsArchConfigurationAxis: stringListSelectValues{},
-		ProductVariableConfigurationAxis("a", NoConfigAxis): stringListSelectValues{
+		ProductVariableConfigurationAxis(false, "a"): stringListSelectValues{
 			"a": []string{"not_in_value"},
 		},
 	}
diff --git a/bloaty/bloaty.go b/bloaty/bloaty.go
index 50f241f..3cff60f 100644
--- a/bloaty/bloaty.go
+++ b/bloaty/bloaty.go
@@ -51,7 +51,7 @@
 	pctx.VariableConfigMethod("hostPrebuiltTag", android.Config.PrebuiltOS)
 	pctx.SourcePathVariable("bloaty", "prebuilts/build-tools/${hostPrebuiltTag}/bin/bloaty")
 	pctx.HostBinToolVariable("bloatyMerger", "bloaty_merger")
-	android.RegisterSingletonType("file_metrics", fileSizesSingleton)
+	android.RegisterParallelSingletonType("file_metrics", fileSizesSingleton)
 	fileSizeMeasurerKey = blueprint.NewProvider(measuredFiles{})
 }
 
diff --git a/bp2build/Android.bp b/bp2build/Android.bp
index b6635c4..782a88c 100644
--- a/bp2build/Android.bp
+++ b/bp2build/Android.bp
@@ -19,6 +19,7 @@
         "testing.go",
     ],
     deps: [
+        "soong-aidl-library",
         "soong-android",
         "soong-android-allowlists",
         "soong-android-soongconfig",
@@ -37,6 +38,7 @@
     ],
     testSrcs: [
         "aar_conversion_test.go",
+        "aidl_library_conversion_test.go",
         "android_app_certificate_conversion_test.go",
         "android_app_conversion_test.go",
         "apex_conversion_test.go",
@@ -70,9 +72,9 @@
         "license_conversion_test.go",
         "license_kind_conversion_test.go",
         "linker_config_conversion_test.go",
-        "ndk_headers_conversion_test.go",
         "package_conversion_test.go",
         "performance_test.go",
+        "platform_compat_config_conversion_test.go",
         "prebuilt_etc_conversion_test.go",
         "python_binary_conversion_test.go",
         "python_library_conversion_test.go",
diff --git a/bp2build/aidl_library_conversion_test.go b/bp2build/aidl_library_conversion_test.go
new file mode 100644
index 0000000..0522da4
--- /dev/null
+++ b/bp2build/aidl_library_conversion_test.go
@@ -0,0 +1,119 @@
+// Copyright 2023 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 (
+	"testing"
+
+	"android/soong/aidl_library"
+	"android/soong/android"
+)
+
+func runAidlLibraryTestCase(t *testing.T, tc Bp2buildTestCase) {
+	t.Helper()
+	(&tc).ModuleTypeUnderTest = "aidl_library"
+	(&tc).ModuleTypeUnderTestFactory = aidl_library.AidlLibraryFactory
+	RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, tc)
+}
+
+func TestAidlLibrary(t *testing.T) {
+	testcases := []struct {
+		name               string
+		bp                 string
+		expectedBazelAttrs AttrNameToString
+	}{
+		{
+			name: "aidl_library with strip_import_prefix",
+			bp: `
+	aidl_library {
+		name: "foo",
+		srcs: ["aidl/foo.aidl"],
+		hdrs: ["aidl/header.aidl"],
+		strip_import_prefix: "aidl",
+	}`,
+			expectedBazelAttrs: AttrNameToString{
+				"srcs":                `["aidl/foo.aidl"]`,
+				"hdrs":                `["aidl/header.aidl"]`,
+				"strip_import_prefix": `"aidl"`,
+				"tags":                `["apex_available=//apex_available:anyapex"]`,
+			},
+		},
+		{
+			name: "aidl_library without strip_import_prefix",
+			bp: `
+	aidl_library {
+		name: "foo",
+		srcs: ["aidl/foo.aidl"],
+		hdrs: ["aidl/header.aidl"],
+	}`,
+			expectedBazelAttrs: AttrNameToString{
+				"srcs": `["aidl/foo.aidl"]`,
+				"hdrs": `["aidl/header.aidl"]`,
+				"tags": `["apex_available=//apex_available:anyapex"]`,
+			},
+		},
+	}
+
+	for _, test := range testcases {
+		t.Run(test.name, func(t *testing.T) {
+			expectedBazelTargets := []string{
+				MakeBazelTargetNoRestrictions("aidl_library", "foo", test.expectedBazelAttrs),
+			}
+			runAidlLibraryTestCase(t, Bp2buildTestCase{
+				Description:          test.name,
+				Blueprint:            test.bp,
+				ExpectedBazelTargets: expectedBazelTargets,
+			})
+		})
+	}
+}
+
+func TestAidlLibraryWithDeps(t *testing.T) {
+	bp := `
+	aidl_library {
+		name: "bar",
+		srcs: ["Bar.aidl"],
+		hdrs: ["aidl/BarHeader.aidl"],
+	}
+	aidl_library {
+		name: "foo",
+		srcs: ["aidl/Foo.aidl"],
+		hdrs: ["aidl/FooHeader.aidl"],
+		strip_import_prefix: "aidl",
+		deps: ["bar"],
+	}`
+
+	t.Run("aidl_library with deps", func(t *testing.T) {
+		expectedBazelTargets := []string{
+			MakeBazelTargetNoRestrictions("aidl_library", "bar", AttrNameToString{
+				"srcs": `["Bar.aidl"]`,
+				"hdrs": `["aidl/BarHeader.aidl"]`,
+				"tags": `["apex_available=//apex_available:anyapex"]`,
+			}),
+			MakeBazelTargetNoRestrictions("aidl_library", "foo", AttrNameToString{
+				"srcs":                `["aidl/Foo.aidl"]`,
+				"hdrs":                `["aidl/FooHeader.aidl"]`,
+				"strip_import_prefix": `"aidl"`,
+				"deps":                `[":bar"]`,
+				"tags":                `["apex_available=//apex_available:anyapex"]`,
+			}),
+		}
+		runAidlLibraryTestCase(t, Bp2buildTestCase{
+			Description:          "aidl_library with deps",
+			Blueprint:            bp,
+			ExpectedBazelTargets: expectedBazelTargets,
+		})
+	})
+}
diff --git a/bp2build/android_app_conversion_test.go b/bp2build/android_app_conversion_test.go
index 928a1f2..7f7aa6a 100644
--- a/bp2build/android_app_conversion_test.go
+++ b/bp2build/android_app_conversion_test.go
@@ -80,6 +80,7 @@
         static_libs: ["static_lib_dep"],
         java_version: "7",
         certificate: "foocert",
+        required: ["static_lib_dep"],
 }
 `,
 		ExpectedBazelTargets: []string{
diff --git a/bp2build/apex_conversion_test.go b/bp2build/apex_conversion_test.go
index 1cc3f22..84c7ea2 100644
--- a/bp2build/apex_conversion_test.go
+++ b/bp2build/apex_conversion_test.go
@@ -123,6 +123,7 @@
 	name: "com.android.apogee",
 	manifest: "apogee_manifest.json",
 	androidManifest: "ApogeeAndroidManifest.xml",
+	apex_available_name: "apogee_apex_name",
 	file_contexts: ":com.android.apogee-file_contexts",
 	min_sdk_version: "29",
 	key: "com.android.apogee.key",
@@ -144,11 +145,13 @@
 	],
 	package_name: "com.android.apogee.test.package",
 	logging_parent: "logging.parent",
+	variant_version: "3",
 }
 `,
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("apex", "com.android.apogee", AttrNameToString{
-				"android_manifest": `"ApogeeAndroidManifest.xml"`,
+				"android_manifest":    `"ApogeeAndroidManifest.xml"`,
+				"apex_available_name": `"apogee_apex_name"`,
 				"binaries": `[
         ":cc_binary_1",
         ":sh_binary_2",
@@ -185,10 +188,11 @@
         ":prebuilt_1",
         ":prebuilt_2",
     ]`,
-				"updatable":      "False",
-				"compressible":   "False",
-				"package_name":   `"com.android.apogee.test.package"`,
-				"logging_parent": `"logging.parent"`,
+				"updatable":       "False",
+				"compressible":    "False",
+				"package_name":    `"com.android.apogee.test.package"`,
+				"logging_parent":  `"logging.parent"`,
+				"variant_version": `"3"`,
 			}),
 		}})
 }
@@ -1475,10 +1479,11 @@
 `,
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("apex", "test_com.android.apogee", AttrNameToString{
-				"file_contexts": `"file_contexts_file"`,
-				"manifest":      `"apex_manifest.json"`,
-				"testonly":      `True`,
-				"tests":         `[":cc_test_1"]`,
+				"file_contexts":  `"file_contexts_file"`,
+				"base_apex_name": `"com.android.apogee"`,
+				"manifest":       `"apex_manifest.json"`,
+				"testonly":       `True`,
+				"tests":          `[":cc_test_1"]`,
 			}),
 		}})
 }
diff --git a/bp2build/bp2build.go b/bp2build/bp2build.go
index d1dfb9d..b22cb28 100644
--- a/bp2build/bp2build.go
+++ b/bp2build/bp2build.go
@@ -15,6 +15,7 @@
 package bp2build
 
 import (
+	"android/soong/starlark_import"
 	"fmt"
 	"os"
 	"path/filepath"
@@ -93,6 +94,12 @@
 		os.Exit(1)
 	}
 	writeFiles(ctx, android.PathForOutput(ctx, bazel.SoongInjectionDirName), injectionFiles)
+	starlarkDeps, err := starlark_import.GetNinjaDeps()
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "%s\n", err)
+		os.Exit(1)
+	}
+	ctx.AddNinjaFileDeps(starlarkDeps...)
 	return &res.metrics
 }
 
diff --git a/bp2build/bp2build_product_config.go b/bp2build/bp2build_product_config.go
index 3abef9d..7224496 100644
--- a/bp2build/bp2build_product_config.go
+++ b/bp2build/bp2build_product_config.go
@@ -68,6 +68,19 @@
 	"@//build/bazel/product_config:__subpackages__",
 	"@soong_injection//product_config_platforms:__subpackages__",
 ])
+
+load("//{PRODUCT_FOLDER}:soong.variables.bzl", _soong_variables = "variables")
+load("@//build/bazel/product_config:android_product.bzl", "android_product")
+
+# Bazel will qualify its outputs by the platform name. When switching between products, this
+# means that soong-built files that depend on bazel-built files will suddenly get different
+# dependency files, because the path changes, and they will be rebuilt. In order to avoid this
+# extra rebuilding, make mixed builds always use a single platform so that the bazel artifacts
+# are always under the same path.
+android_product(
+    name = "mixed_builds_product-{VARIANT}",
+    soong_variables = _soong_variables,
+)
 `)),
 		newFile(
 			"product_config_platforms",
@@ -78,6 +91,7 @@
 # TODO: When we start generating the platforms for more than just the
 # currently lunched product, they should all be listed here
 product_labels = [
+  "@soong_injection//product_config_platforms:mixed_builds_product-{VARIANT}",
   "@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}"
 ]
 `)),
@@ -105,14 +119,6 @@
 			productReplacer.Replace(`
 build --host_platform @soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_darwin_x86_64
 `)),
-		newFile(
-			"product_config_platforms",
-			"platform_mappings",
-			productReplacer.Replace(`
-flags:
-  --cpu=k8
-    @soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}
-`)),
 	}
 
 	return result, nil
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index b7678a4..a860484 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -600,6 +600,11 @@
 		// TODO(b/164227191): implement pretty print for interfaces.
 		// Interfaces are used for for arch, multilib and target properties.
 		return "", nil
+	case reflect.Map:
+		if v, ok := propertyValue.Interface().(bazel.StringMapAttribute); ok {
+			return starlark_fmt.PrintStringStringDict(v, indent), nil
+		}
+		return "", fmt.Errorf("bp2build expects map of type map[string]string for field: %s", propertyValue)
 	default:
 		return "", fmt.Errorf(
 			"unexpected kind for property struct field: %s", propertyValue.Kind())
diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go
index 73ee26b..e127fd5 100644
--- a/bp2build/build_conversion_test.go
+++ b/bp2build/build_conversion_test.go
@@ -21,6 +21,7 @@
 
 	"android/soong/android"
 	"android/soong/android/allowlists"
+	"android/soong/bazel"
 	"android/soong/python"
 )
 
@@ -1898,3 +1899,50 @@
 		Description:          "Generating API contribution Bazel targets for custom module",
 	})
 }
+
+func TestGenerateConfigSetting(t *testing.T) {
+	bp := `
+	custom {
+		name: "foo",
+		test_config_setting: true,
+	}
+	`
+	expectedBazelTargets := []string{
+		MakeBazelTargetNoRestrictions(
+			"config_setting",
+			"foo_config_setting",
+			AttrNameToString{
+				"flag_values": `{
+        "//build/bazel/rules/my_string_setting": "foo",
+    }`,
+			},
+		),
+		MakeBazelTarget(
+			"custom",
+			"foo",
+			AttrNameToString{},
+		),
+	}
+	registerCustomModule := func(ctx android.RegistrationContext) {
+		ctx.RegisterModuleType("custom", customModuleFactoryHostAndDevice)
+	}
+	RunBp2BuildTestCase(t, registerCustomModule, Bp2buildTestCase{
+		Blueprint:            bp,
+		ExpectedBazelTargets: expectedBazelTargets,
+		Description:          "Generating API contribution Bazel targets for custom module",
+	})
+}
+
+// If values of all keys in an axis are equal to //conditions:default, drop the axis and print the common value
+func TestPrettyPrintSelectMapEqualValues(t *testing.T) {
+	lla := bazel.LabelListAttribute{
+		Value: bazel.LabelList{},
+	}
+	libFooImplLabel := bazel.Label{
+		Label: ":libfoo.impl",
+	}
+	lla.SetSelectValue(bazel.OsAndInApexAxis, bazel.AndroidPlatform, bazel.MakeLabelList([]bazel.Label{libFooImplLabel}))
+	lla.SetSelectValue(bazel.OsAndInApexAxis, bazel.ConditionsDefaultConfigKey, bazel.MakeLabelList([]bazel.Label{libFooImplLabel}))
+	actual, _ := prettyPrintAttribute(lla, 0)
+	android.AssertStringEquals(t, "Print the common value if all keys in an axis have the same value", `[":libfoo.impl"]`, actual)
+}
diff --git a/bp2build/bzl_conversion_test.go b/bp2build/bzl_conversion_test.go
index a8e557d..fa1bf8a 100644
--- a/bp2build/bzl_conversion_test.go
+++ b/bp2build/bzl_conversion_test.go
@@ -108,6 +108,7 @@
         "string_literal_prop": attr.string(),
         "string_prop": attr.string(),
         "string_ptr_prop": attr.string(),
+        "test_config_setting": attr.bool(),
     },
 )
 
@@ -139,6 +140,7 @@
         "string_literal_prop": attr.string(),
         "string_prop": attr.string(),
         "string_ptr_prop": attr.string(),
+        "test_config_setting": attr.bool(),
     },
 )
 
@@ -170,6 +172,7 @@
         "string_literal_prop": attr.string(),
         "string_prop": attr.string(),
         "string_ptr_prop": attr.string(),
+        "test_config_setting": attr.bool(),
         # test_prop start
 #         "test_string_prop": attr.string(),
         # test_prop end
diff --git a/bp2build/cc_binary_conversion_test.go b/bp2build/cc_binary_conversion_test.go
index 0315732..d37722b 100644
--- a/bp2build/cc_binary_conversion_test.go
+++ b/bp2build/cc_binary_conversion_test.go
@@ -222,6 +222,7 @@
         "-Wl,--version-script,$(location vs)",
         "-Wl,--dynamic-list,$(location dynamic.list)",
     ]`,
+				"features": `["android_cfi_exports_map"]`,
 			},
 			},
 		},
@@ -249,6 +250,7 @@
         "version_script",
         "dynamic.list",
     ]`,
+				"features": `["android_cfi_exports_map"]`,
 				"linkopts": `[
         "--nospace_flag",
         "-z",
@@ -644,10 +646,7 @@
 		targets: []testBazelTarget{
 			{"cc_binary", "foo", AttrNameToString{
 				"features": `select({
-        "//build/bazel/platforms/arch:arm": [
-            "arm_isa_arm",
-            "-arm_isa_thumb",
-        ],
+        "//build/bazel/platforms/arch:arm": ["arm_isa_arm"],
         "//conditions:default": [],
     })`,
 				"local_includes": `["."]`,
@@ -996,3 +995,151 @@
 		},
 	})
 }
+
+func TestCcBinaryHiddenVisibilityConvertedToFeature(t *testing.T) {
+	runCcBinaryTestCase(t, ccBinaryBp2buildTestCase{
+		description: "cc_binary changes hidden visibility to feature",
+		blueprint: `
+{rule_name} {
+	name: "foo",
+	cflags: ["-fvisibility=hidden"],
+}`,
+		targets: []testBazelTarget{
+			{"cc_binary", "foo", AttrNameToString{
+				"local_includes": `["."]`,
+				"features":       `["visibility_hidden"]`,
+			}},
+		},
+	})
+}
+
+func TestCcBinaryHiddenVisibilityConvertedToFeatureOsSpecific(t *testing.T) {
+	runCcBinaryTestCase(t, ccBinaryBp2buildTestCase{
+		description: "cc_binary changes hidden visibility to feature for specific os",
+		blueprint: `
+{rule_name} {
+	name: "foo",
+	target: {
+		android: {
+			cflags: ["-fvisibility=hidden"],
+		},
+	},
+}`,
+		targets: []testBazelTarget{
+			{"cc_binary", "foo", AttrNameToString{
+				"local_includes": `["."]`,
+				"features": `select({
+        "//build/bazel/platforms/os:android": ["visibility_hidden"],
+        "//conditions:default": [],
+    })`,
+			}},
+		},
+	})
+}
+
+func TestCcBinaryWithCfi(t *testing.T) {
+	runCcBinaryTestCase(t, ccBinaryBp2buildTestCase{
+		description: "cc_binary has correct features when cfi is enabled",
+		blueprint: `
+{rule_name} {
+	name: "foo",
+	sanitize: {
+		cfi: true,
+	},
+}`,
+		targets: []testBazelTarget{
+			{"cc_binary", "foo", AttrNameToString{
+				"features":       `["android_cfi"]`,
+				"local_includes": `["."]`,
+			}},
+		},
+	})
+}
+
+func TestCcBinaryWithCfiOsSpecific(t *testing.T) {
+	runCcBinaryTestCase(t, ccBinaryBp2buildTestCase{
+		description: "cc_binary has correct features when cfi is enabled for specific variants",
+		blueprint: `
+{rule_name} {
+	name: "foo",
+	target: {
+		android: {
+			sanitize: {
+				cfi: true,
+			},
+		},
+	},
+}`,
+		targets: []testBazelTarget{
+			{"cc_binary", "foo", AttrNameToString{
+				"features": `select({
+        "//build/bazel/platforms/os:android": ["android_cfi"],
+        "//conditions:default": [],
+    })`,
+				"local_includes": `["."]`,
+			}},
+		},
+	})
+}
+
+func TestCcBinaryWithCfiAndCfiAssemblySupport(t *testing.T) {
+	runCcBinaryTestCase(t, ccBinaryBp2buildTestCase{
+		description: "cc_binary has correct features when cfi is enabled with cfi assembly support",
+		blueprint: `
+{rule_name} {
+	name: "foo",
+	sanitize: {
+		cfi: true,
+		config: {
+			cfi_assembly_support: true,
+		},
+	},
+}`,
+		targets: []testBazelTarget{
+			{"cc_binary", "foo", AttrNameToString{
+				"features": `[
+        "android_cfi",
+        "android_cfi_assembly_support",
+    ]`,
+				"local_includes": `["."]`,
+			}},
+		},
+	})
+}
+
+func TestCcBinaryStem(t *testing.T) {
+	runCcBinaryTestCase(t, ccBinaryBp2buildTestCase{
+		description: "cc_binary with stem property",
+		blueprint: `
+cc_binary {
+	name: "foo_with_stem_simple",
+	stem: "foo",
+}
+cc_binary {
+	name: "foo_with_arch_variant_stem",
+	arch: {
+		arm: {
+			stem: "foo-arm",
+		},
+		arm64: {
+			stem: "foo-arm64",
+		},
+	},
+}
+`,
+		targets: []testBazelTarget{
+			{"cc_binary", "foo_with_stem_simple", AttrNameToString{
+				"stem":           `"foo"`,
+				"local_includes": `["."]`,
+			}},
+			{"cc_binary", "foo_with_arch_variant_stem", AttrNameToString{
+				"stem": `select({
+        "//build/bazel/platforms/arch:arm": "foo-arm",
+        "//build/bazel/platforms/arch:arm64": "foo-arm64",
+        "//conditions:default": None,
+    })`,
+				"local_includes": `["."]`,
+			}},
+		},
+	})
+}
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index d2c463d..20f3bf6 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -18,6 +18,7 @@
 	"fmt"
 	"testing"
 
+	"android/soong/aidl_library"
 	"android/soong/android"
 	"android/soong/cc"
 )
@@ -63,6 +64,7 @@
 	ctx.RegisterModuleType("cc_library_static", cc.LibraryStaticFactory)
 	ctx.RegisterModuleType("cc_prebuilt_library_static", cc.PrebuiltStaticLibraryFactory)
 	ctx.RegisterModuleType("cc_library_headers", cc.LibraryHeaderFactory)
+	ctx.RegisterModuleType("aidl_library", aidl_library.AidlLibraryFactory)
 }
 
 func TestCcLibrarySimple(t *testing.T) {
@@ -153,10 +155,10 @@
         "//build/bazel/platforms/os:linux_glibc": ["linux.cpp"],
         "//conditions:default": [],
     })`,
-			"sdk_version":                       `"current"`,
-			"min_sdk_version":                   `"29"`,
-			"use_version_lib":                   `True`,
-			"implementation_whole_archive_deps": `["//build/soong/cc/libbuildversion:libbuildversion"]`,
+			"sdk_version":        `"current"`,
+			"min_sdk_version":    `"29"`,
+			"use_version_lib":    `True`,
+			"whole_archive_deps": `["//build/soong/cc/libbuildversion:libbuildversion"]`,
 		}),
 	})
 }
@@ -898,7 +900,8 @@
         "-Wl,--version-script,$(location v.map)",
         "-Wl,--dynamic-list,$(location dynamic.list)",
     ]`,
-			"srcs": `["a.cpp"]`,
+			"srcs":     `["a.cpp"]`,
+			"features": `["android_cfi_exports_map"]`,
 		}),
 	},
 	)
@@ -956,6 +959,11 @@
         "//conditions:default": [],
     })`,
 			"srcs": `["a.cpp"]`,
+			"features": `select({
+        "//build/bazel/platforms/arch:arm": ["android_cfi_exports_map"],
+        "//build/bazel/platforms/arch:arm64": ["android_cfi_exports_map"],
+        "//conditions:default": [],
+    })`,
 		}),
 	},
 	)
@@ -983,12 +991,15 @@
 }
 `,
 		ExpectedBazelTargets: []string{
-			MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{}),
+			MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+				"features": `["android_cfi_exports_map"]`,
+			}),
 			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
 				"additional_linker_inputs": `[
         "version_script",
         "dynamic.list",
     ]`,
+				"features": `["android_cfi_exports_map"]`,
 				"linkopts": `[
         "--nospace_flag",
         "-z",
@@ -3042,7 +3053,8 @@
 }`,
 		ExpectedBazelTargets: makeCcLibraryTargets("foolib", AttrNameToString{
 			"implementation_dynamic_deps": `select({
-        "//build/bazel/rules/apex:android-in_apex": ["@api_surfaces//module-libapi/current:barlib"],
+        "//build/bazel/rules/apex:foo": ["@api_surfaces//module-libapi/current:barlib"],
+        "//build/bazel/rules/apex:system": ["@api_surfaces//module-libapi/current:barlib"],
         "//conditions:default": [":barlib"],
     })`,
 			"local_includes": `["."]`,
@@ -3096,7 +3108,11 @@
         "//build/bazel/platforms/os:linux_glibc": [":quxlib"],
         "//build/bazel/platforms/os:linux_musl": [":quxlib"],
         "//build/bazel/platforms/os:windows": [":quxlib"],
-        "//build/bazel/rules/apex:android-in_apex": [
+        "//build/bazel/rules/apex:foo": [
+            "@api_surfaces//module-libapi/current:barlib",
+            "@api_surfaces//module-libapi/current:quxlib",
+        ],
+        "//build/bazel/rules/apex:system": [
             "@api_surfaces//module-libapi/current:barlib",
             "@api_surfaces//module-libapi/current:quxlib",
         ],
@@ -3214,10 +3230,7 @@
 `,
 		ExpectedBazelTargets: makeCcLibraryTargets("foo", AttrNameToString{
 			"features": `select({
-        "//build/bazel/platforms/arch:arm": [
-            "arm_isa_arm",
-            "-arm_isa_thumb",
-        ],
+        "//build/bazel/platforms/arch:arm": ["arm_isa_arm"],
         "//conditions:default": [],
     })`,
 			"local_includes": `["."]`,
@@ -3310,6 +3323,51 @@
 	})
 }
 
+func TestCcLibraryWithAidlLibrary(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		Description:                "cc_library with aidl_library",
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Blueprint: `
+aidl_library {
+    name: "A_aidl",
+    srcs: ["aidl/A.aidl"],
+	hdrs: ["aidl/Header.aidl"],
+	strip_import_prefix: "aidl",
+}
+cc_library {
+	name: "foo",
+	aidl: {
+		libs: ["A_aidl"],
+	},
+	export_include_dirs: ["include"],
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTargetNoRestrictions("aidl_library", "A_aidl", AttrNameToString{
+				"srcs":                `["aidl/A.aidl"]`,
+				"hdrs":                `["aidl/Header.aidl"]`,
+				"strip_import_prefix": `"aidl"`,
+				"tags":                `["apex_available=//apex_available:anyapex"]`,
+			}),
+			MakeBazelTarget("cc_aidl_library", "foo_cc_aidl_library", AttrNameToString{
+				"deps":            `[":A_aidl"]`,
+				"local_includes":  `["."]`,
+				"export_includes": `["include"]`,
+			}),
+			MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+				"implementation_whole_archive_deps": `[":foo_cc_aidl_library"]`,
+				"local_includes":                    `["."]`,
+				"export_includes":                   `["include"]`,
+			}),
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+				"implementation_whole_archive_deps": `[":foo_cc_aidl_library"]`,
+				"local_includes":                    `["."]`,
+				"export_includes":                   `["include"]`,
+			}),
+		},
+	})
+}
+
 func TestCcLibraryWithAidlSrcs(t *testing.T) {
 	runCcLibraryTestCase(t, Bp2buildTestCase{
 		Description:                "cc_library with aidl srcs",
@@ -3338,6 +3396,7 @@
 				"srcs": `["B.aidl"]`,
 			}),
 			MakeBazelTarget("cc_aidl_library", "foo_cc_aidl_library", AttrNameToString{
+				"local_includes": `["."]`,
 				"deps": `[
         ":A_aidl",
         ":foo_aidl_library",
@@ -3377,7 +3436,8 @@
 }`,
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("cc_aidl_library", "foo_cc_aidl_library", AttrNameToString{
-				"deps": `["//path/to/A:A_aidl"]`,
+				"local_includes": `["."]`,
+				"deps":           `["//path/to/A:A_aidl"]`,
 			}),
 			MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
 				"implementation_whole_archive_deps": `[":foo_cc_aidl_library"]`,
@@ -3392,37 +3452,78 @@
 }
 
 func TestCcLibraryWithExportAidlHeaders(t *testing.T) {
-	runCcLibraryTestCase(t, Bp2buildTestCase{
-		Description:                "cc_library with export aidl headers",
-		ModuleTypeUnderTest:        "cc_library",
-		ModuleTypeUnderTestFactory: cc.LibraryFactory,
-		Blueprint: `
-cc_library {
-    name: "foo",
-    srcs: [
-        "Foo.aidl",
-    ],
-    aidl: {
-        export_aidl_headers: true,
-    }
-}`,
-		ExpectedBazelTargets: []string{
-			MakeBazelTarget("aidl_library", "foo_aidl_library", AttrNameToString{
-				"srcs": `["Foo.aidl"]`,
-			}),
-			MakeBazelTarget("cc_aidl_library", "foo_cc_aidl_library", AttrNameToString{
-				"deps": `[":foo_aidl_library"]`,
-			}),
-			MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
-				"whole_archive_deps": `[":foo_cc_aidl_library"]`,
-				"local_includes":     `["."]`,
-			}),
-			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
-				"whole_archive_deps": `[":foo_cc_aidl_library"]`,
-				"local_includes":     `["."]`,
-			}),
+	t.Parallel()
+
+	expectedBazelTargets := []string{
+		MakeBazelTarget("cc_aidl_library", "foo_cc_aidl_library", AttrNameToString{
+			"local_includes": `["."]`,
+			"deps":           `[":foo_aidl_library"]`,
+		}),
+		MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+			"whole_archive_deps": `[":foo_cc_aidl_library"]`,
+			"local_includes":     `["."]`,
+		}),
+		MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+			"whole_archive_deps": `[":foo_cc_aidl_library"]`,
+			"local_includes":     `["."]`,
+		}),
+	}
+	testCases := []struct {
+		description          string
+		bp                   string
+		expectedBazelTargets []string
+	}{
+		{
+			description: "cc_library with aidl srcs and aidl.export_aidl_headers set",
+			bp: `
+			cc_library {
+				name: "foo",
+				srcs: [
+					"Foo.aidl",
+				],
+				aidl: {
+					export_aidl_headers: true,
+				}
+			}`,
+			expectedBazelTargets: append(
+				expectedBazelTargets,
+				MakeBazelTarget("aidl_library", "foo_aidl_library", AttrNameToString{
+					"srcs": `["Foo.aidl"]`,
+				})),
 		},
-	})
+		{
+			description: "cc_library with aidl.libs and aidl.export_aidl_headers set",
+			bp: `
+			aidl_library {
+				name: "foo_aidl_library",
+				srcs: ["Foo.aidl"],
+			}
+			cc_library {
+				name: "foo",
+				aidl: {
+					libs: ["foo_aidl_library"],
+					export_aidl_headers: true,
+				}
+			}`,
+			expectedBazelTargets: append(
+				expectedBazelTargets,
+				MakeBazelTargetNoRestrictions("aidl_library", "foo_aidl_library", AttrNameToString{
+					"srcs": `["Foo.aidl"]`,
+					"tags": `["apex_available=//apex_available:anyapex"]`,
+				}),
+			),
+		},
+	}
+
+	for _, testCase := range testCases {
+		runCcLibraryTestCase(t, Bp2buildTestCase{
+			Description:                "cc_library with export aidl headers",
+			ModuleTypeUnderTest:        "cc_library",
+			ModuleTypeUnderTestFactory: cc.LibraryFactory,
+			Blueprint:                  testCase.bp,
+			ExpectedBazelTargets:       testCase.expectedBazelTargets,
+		})
+	}
 }
 
 func TestCcLibraryWithTargetApex(t *testing.T) {
@@ -3637,7 +3738,8 @@
 				"srcs": `["Foo.aidl"]`,
 			}),
 			MakeBazelTarget("cc_aidl_library", "foo_cc_aidl_library", AttrNameToString{
-				"deps": `[":foo_aidl_library"]`,
+				"local_includes": `["."]`,
+				"deps":           `[":foo_aidl_library"]`,
 				"implementation_deps": `[
         ":baz-static",
         ":bar-static",
@@ -4139,44 +4241,34 @@
 	name: "barlib",
 	stubs: { symbol_file: "bar.map.txt", versions: ["28", "29", "current"] },
 	bazel_module: { bp2build_available: false },
+	apex_available: ["//apex_available:platform",],
 }
 cc_library {
 	name: "bazlib",
 	stubs: { symbol_file: "bar.map.txt", versions: ["28", "29", "current"] },
 	bazel_module: { bp2build_available: false },
+	apex_available: ["//apex_available:platform",],
 }
 cc_library {
     name: "foo",
 	  shared_libs: ["barlib", "bazlib"],
     export_shared_lib_headers: ["bazlib"],
     apex_available: [
-        "apex_available:platform",
+        "//apex_available:platform",
     ],
 }`,
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
-				"implementation_dynamic_deps": `select({
-        "//build/bazel/rules/apex:android-in_apex": ["@api_surfaces//module-libapi/current:barlib"],
-        "//conditions:default": [":barlib"],
-    })`,
-				"dynamic_deps": `select({
-        "//build/bazel/rules/apex:android-in_apex": ["@api_surfaces//module-libapi/current:bazlib"],
-        "//conditions:default": [":bazlib"],
-    })`,
-				"local_includes": `["."]`,
-				"tags":           `["apex_available=apex_available:platform"]`,
+				"implementation_dynamic_deps": `[":barlib"]`,
+				"dynamic_deps":                `[":bazlib"]`,
+				"local_includes":              `["."]`,
+				"tags":                        `["apex_available=//apex_available:platform"]`,
 			}),
 			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
-				"implementation_dynamic_deps": `select({
-        "//build/bazel/rules/apex:android-in_apex": ["@api_surfaces//module-libapi/current:barlib"],
-        "//conditions:default": [":barlib"],
-    })`,
-				"dynamic_deps": `select({
-        "//build/bazel/rules/apex:android-in_apex": ["@api_surfaces//module-libapi/current:bazlib"],
-        "//conditions:default": [":bazlib"],
-    })`,
-				"local_includes": `["."]`,
-				"tags":           `["apex_available=apex_available:platform"]`,
+				"implementation_dynamic_deps": `[":barlib"]`,
+				"dynamic_deps":                `[":bazlib"]`,
+				"local_includes":              `["."]`,
+				"tags":                        `["apex_available=//apex_available:platform"]`,
 			}),
 		},
 	})
@@ -4350,3 +4442,412 @@
 		},
 	})
 }
+
+func TestCcLibraryHiddenVisibilityConvertedToFeature(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		Description:                "cc_library changes hidden visibility flag to feature",
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Blueprint: `
+cc_library {
+	name: "foo",
+	cflags: ["-fvisibility=hidden"],
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+				"features":       `["visibility_hidden"]`,
+				"local_includes": `["."]`,
+			}),
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+				"features":       `["visibility_hidden"]`,
+				"local_includes": `["."]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibraryHiddenVisibilityConvertedToFeatureSharedSpecific(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		Description:                "cc_library changes hidden visibility flag to feature when specific to shared variant",
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Blueprint: `
+cc_library {
+	name: "foo",
+	shared: {
+		cflags: ["-fvisibility=hidden"],
+	},
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+				"local_includes": `["."]`,
+			}),
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+				"features":       `["visibility_hidden"]`,
+				"local_includes": `["."]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibraryHiddenVisibilityConvertedToFeatureStaticSpecific(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		Description:                "cc_library changes hidden visibility flag to feature when specific to static variant",
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Blueprint: `
+cc_library {
+	name: "foo",
+	static: {
+		cflags: ["-fvisibility=hidden"],
+	},
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+				"features":       `["visibility_hidden"]`,
+				"local_includes": `["."]`,
+			}),
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+				"local_includes": `["."]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibraryHiddenVisibilityConvertedToFeatureOsSpecific(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		Description:                "cc_library changes hidden visibility flag to feature when specific to an os",
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Blueprint: `
+cc_library {
+	name: "foo",
+	target: {
+		android: {
+			cflags: ["-fvisibility=hidden"],
+		},
+	},
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+				"features": `select({
+        "//build/bazel/platforms/os:android": ["visibility_hidden"],
+        "//conditions:default": [],
+    })`,
+				"local_includes": `["."]`,
+			}),
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+				"features": `select({
+        "//build/bazel/platforms/os:android": ["visibility_hidden"],
+        "//conditions:default": [],
+    })`,
+				"local_includes": `["."]`,
+			}),
+		},
+	})
+}
+
+// Test that a config_setting specific to an apex is created by cc_library.
+func TestCcLibraryCreatesInApexConfigSetting(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		Description:                "cc_library creates a config_setting for each apex in apex_available",
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Dir:                        "build/bazel/rules/apex",
+		Blueprint: `
+cc_library {
+	name: "foo",
+	apex_available: [
+	"//apex_available:platform", // This will be skipped, since it is equivalent to //build/bazel/rules/apex:android-non_apex
+	"myapex"
+	],
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTargetNoRestrictions(
+				"config_setting",
+				"myapex",
+				AttrNameToString{
+					"flag_values": `{
+        "//build/bazel/rules/apex:api_domain": "myapex",
+    }`,
+					"constraint_values": `["//build/bazel/platforms/os:android"]`,
+				},
+			),
+		},
+	})
+}
+
+func TestCcLibraryCppFlagsInProductVariables(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		Description:                "cc_library cppflags in product variables",
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Blueprint: soongCcLibraryPreamble + `cc_library {
+    name: "a",
+    srcs: ["a.cpp"],
+    cppflags: [
+        "-Wextra",
+        "-DDEBUG_ONLY_CODE=0",
+    ],
+    product_variables: {
+        eng: {
+            cppflags: [
+                "-UDEBUG_ONLY_CODE",
+                "-DDEBUG_ONLY_CODE=1",
+            ],
+        },
+    },
+    include_build_directory: false,
+}
+`,
+		ExpectedBazelTargets: makeCcLibraryTargets("a", AttrNameToString{
+			"cppflags": `[
+        "-Wextra",
+        "-DDEBUG_ONLY_CODE=0",
+    ] + select({
+        "//build/bazel/product_variables:eng": [
+            "-UDEBUG_ONLY_CODE",
+            "-DDEBUG_ONLY_CODE=1",
+        ],
+        "//conditions:default": [],
+    })`,
+			"srcs": `["a.cpp"]`,
+		}),
+	},
+	)
+}
+
+func TestCcLibraryYaccConversion(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		Description:                "cc_library is built from .y/.yy files",
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Blueprint: soongCcLibraryPreamble + `cc_library {
+    name: "a",
+    srcs: [
+	"a.cpp",
+	"a.yy",
+    ],
+    shared_libs: ["sharedlib"],
+    static_libs: ["staticlib"],
+    yacc: {
+	    flags: ["someYaccFlag"],
+	    gen_location_hh: true,
+	    gen_position_hh: true,
+	},
+}
+cc_library_static {
+	name: "staticlib",
+	bazel_module: { bp2build_available: false },
+}
+cc_library {
+	name: "sharedlib",
+	bazel_module: { bp2build_available: false },
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_yacc_static_library", "a_yacc", AttrNameToString{
+				"src":                         `"a.yy"`,
+				"implementation_deps":         `[":staticlib"]`,
+				"implementation_dynamic_deps": `[":sharedlib"]`,
+				"flags":                       `["someYaccFlag"]`,
+				"gen_location_hh":             "True",
+				"gen_position_hh":             "True",
+				"local_includes":              `["."]`,
+			}),
+			MakeBazelTarget("cc_library_shared", "a", AttrNameToString{
+				"srcs":                              `["a.cpp"]`,
+				"implementation_deps":               `[":staticlib"]`,
+				"implementation_dynamic_deps":       `[":sharedlib"]`,
+				"implementation_whole_archive_deps": `[":a_yacc"]`,
+				"local_includes":                    `["."]`,
+			}),
+			MakeBazelTarget("cc_library_static", "a_bp2build_cc_library_static", AttrNameToString{
+				"srcs":                              `["a.cpp"]`,
+				"implementation_deps":               `[":staticlib"]`,
+				"implementation_dynamic_deps":       `[":sharedlib"]`,
+				"implementation_whole_archive_deps": `[":a_yacc"]`,
+				"local_includes":                    `["."]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibraryHostLdLibs(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		Description:                "cc_binary linker flags for host_ldlibs",
+		ModuleTypeUnderTest:        "cc_binary",
+		ModuleTypeUnderTestFactory: cc.BinaryFactory,
+		Blueprint: soongCcLibraryPreamble + `cc_binary {
+    name: "a",
+    host_supported: true,
+    ldflags: ["-lcommon"],
+    target: {
+	linux: {
+		host_ldlibs: [
+			"-llinux",
+		],
+	},
+	darwin: {
+		ldflags: ["-ldarwinadditional"],
+		host_ldlibs: [
+			"-ldarwin",
+		],
+	},
+	windows: {
+		host_ldlibs: [
+			"-lwindows",
+		],
+	},
+    },
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTargetNoRestrictions("cc_binary", "a", AttrNameToString{
+				"linkopts": `["-lcommon"] + select({
+        "//build/bazel/platforms/os:darwin": [
+            "-ldarwinadditional",
+            "-ldarwin",
+        ],
+        "//build/bazel/platforms/os:linux_glibc": ["-llinux"],
+        "//build/bazel/platforms/os:windows": ["-lwindows"],
+        "//conditions:default": [],
+    })`,
+				"local_includes": `["."]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibraryWithCfi(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		Description:                "cc_library has correct features when cfi is enabled",
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Blueprint: `
+cc_library {
+	name: "foo",
+	sanitize: {
+		cfi: true,
+	},
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+				"features":       `["android_cfi"]`,
+				"local_includes": `["."]`,
+			}),
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+				"features":       `["android_cfi"]`,
+				"local_includes": `["."]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibraryWithCfiOsSpecific(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		Description:                "cc_library has correct features when cfi is enabled for specific variants",
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Blueprint: `
+cc_library {
+	name: "foo",
+	target: {
+		android: {
+			sanitize: {
+				cfi: true,
+			},
+		},
+	},
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+				"features": `select({
+        "//build/bazel/platforms/os:android": ["android_cfi"],
+        "//conditions:default": [],
+    })`,
+				"local_includes": `["."]`,
+			}),
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+				"features": `select({
+        "//build/bazel/platforms/os:android": ["android_cfi"],
+        "//conditions:default": [],
+    })`,
+				"local_includes": `["."]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibraryWithCfiAndCfiAssemblySupport(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		Description:                "cc_library has correct features when cfi is enabled with cfi_assembly_support",
+		ModuleTypeUnderTest:        "cc_library",
+		ModuleTypeUnderTestFactory: cc.LibraryFactory,
+		Blueprint: `
+cc_library {
+	name: "foo",
+	sanitize: {
+		cfi: true,
+		config: {
+			cfi_assembly_support: true,
+		},
+	},
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_static", "foo_bp2build_cc_library_static", AttrNameToString{
+				"features": `[
+        "android_cfi",
+        "android_cfi_assembly_support",
+    ]`,
+				"local_includes": `["."]`,
+			}),
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+				"features": `[
+        "android_cfi",
+        "android_cfi_assembly_support",
+    ]`,
+				"local_includes": `["."]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibraryWithStem(t *testing.T) {
+	runCcLibraryTestCase(t, Bp2buildTestCase{
+		Description:                "cc_library with stem property",
+		ModuleTypeUnderTest:        "cc_library_shared",
+		ModuleTypeUnderTestFactory: cc.LibrarySharedFactory,
+		Blueprint: soongCcLibraryPreamble + `
+cc_library_shared {
+	name: "foo_with_stem_simple",
+	stem: "foo",
+}
+cc_library_shared {
+	name: "foo_with_arch_variant_stem",
+	arch: {
+		arm: {
+			stem: "foo-arm",
+		},
+		arm64: {
+			stem: "foo-arm64",
+		},
+	},
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_shared", "foo_with_stem_simple", AttrNameToString{
+				"stem":           `"foo"`,
+				"local_includes": `["."]`,
+			}),
+			MakeBazelTarget("cc_library_shared", "foo_with_arch_variant_stem", AttrNameToString{
+				"stem": `select({
+        "//build/bazel/platforms/arch:arm": "foo-arm",
+        "//build/bazel/platforms/arch:arm64": "foo-arm64",
+        "//conditions:default": None,
+    })`,
+				"local_includes": `["."]`,
+			}),
+		},
+	})
+}
diff --git a/bp2build/cc_library_shared_conversion_test.go b/bp2build/cc_library_shared_conversion_test.go
index d78c4a4..7f0ba44 100644
--- a/bp2build/cc_library_shared_conversion_test.go
+++ b/bp2build/cc_library_shared_conversion_test.go
@@ -36,6 +36,7 @@
 
 func runCcLibrarySharedTestCase(t *testing.T, tc Bp2buildTestCase) {
 	t.Helper()
+	t.Parallel()
 	(&tc).ModuleTypeUnderTest = "cc_library_shared"
 	(&tc).ModuleTypeUnderTestFactory = cc.LibrarySharedFactory
 	RunBp2BuildTestCase(t, registerCcLibrarySharedModuleTypes, tc)
@@ -361,6 +362,7 @@
         "-Wl,--version-script,$(location version_script)",
         "-Wl,--dynamic-list,$(location dynamic.list)",
     ]`,
+				"features": `["android_cfi_exports_map"]`,
 			}),
 		},
 	})
@@ -397,6 +399,7 @@
         "-Wl,--version-script,$(location version_script)",
         "-Wl,--dynamic-list,$(location dynamic.list)",
     ]`,
+				"features": `["android_cfi_exports_map"]`,
 			}),
 		},
 	})
@@ -515,8 +518,8 @@
 }`,
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
-				"use_version_lib":                   "True",
-				"implementation_whole_archive_deps": `["//build/soong/cc/libbuildversion:libbuildversion"]`,
+				"use_version_lib":    "True",
+				"whole_archive_deps": `["//build/soong/cc/libbuildversion:libbuildversion"]`,
 			}),
 		},
 	})
@@ -608,7 +611,8 @@
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("cc_library_shared", "b", AttrNameToString{
 				"implementation_dynamic_deps": `select({
-        "//build/bazel/rules/apex:android-in_apex": ["@api_surfaces//module-libapi/current:a"],
+        "//build/bazel/rules/apex:apex_b": ["@api_surfaces//module-libapi/current:a"],
+        "//build/bazel/rules/apex:system": ["@api_surfaces//module-libapi/current:a"],
         "//conditions:default": [":a"],
     })`,
 				"tags": `["apex_available=apex_b"]`,
@@ -617,6 +621,64 @@
 	})
 }
 
+// Tests that library in apexfoo links against stubs of platform_lib and otherapex_lib
+func TestCcLibrarySharedStubs_UseStubsFromMultipleApiDomains(t *testing.T) {
+	runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+		Description:                "cc_library_shared stubs",
+		ModuleTypeUnderTest:        "cc_library_shared",
+		ModuleTypeUnderTestFactory: cc.LibrarySharedFactory,
+		Blueprint: soongCcLibrarySharedPreamble + `
+cc_library_shared {
+	name: "libplatform_stable",
+	stubs: { symbol_file: "libplatform_stable.map.txt", versions: ["28", "29", "current"] },
+	apex_available: ["//apex_available:platform"],
+	bazel_module: { bp2build_available: false },
+	include_build_directory: false,
+}
+cc_library_shared {
+	name: "libapexfoo_stable",
+	stubs: { symbol_file: "libapexfoo_stable.map.txt", versions: ["28", "29", "current"] },
+	apex_available: ["apexfoo"],
+	bazel_module: { bp2build_available: false },
+	include_build_directory: false,
+}
+cc_library_shared {
+	name: "libutils",
+	shared_libs: ["libplatform_stable", "libapexfoo_stable",],
+	apex_available: ["//apex_available:platform", "apexfoo", "apexbar"],
+	include_build_directory: false,
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_shared", "libutils", AttrNameToString{
+				"implementation_dynamic_deps": `select({
+        "//build/bazel/rules/apex:apexbar": [
+            "@api_surfaces//module-libapi/current:libplatform_stable",
+            "@api_surfaces//module-libapi/current:libapexfoo_stable",
+        ],
+        "//build/bazel/rules/apex:apexfoo": [
+            "@api_surfaces//module-libapi/current:libplatform_stable",
+            ":libapexfoo_stable",
+        ],
+        "//build/bazel/rules/apex:system": [
+            "@api_surfaces//module-libapi/current:libplatform_stable",
+            "@api_surfaces//module-libapi/current:libapexfoo_stable",
+        ],
+        "//conditions:default": [
+            ":libplatform_stable",
+            ":libapexfoo_stable",
+        ],
+    })`,
+				"tags": `[
+        "apex_available=//apex_available:platform",
+        "apex_available=apexfoo",
+        "apex_available=apexbar",
+    ]`,
+			}),
+		},
+	})
+}
+
 func TestCcLibrarySharedStubs_IgnorePlatformAvailable(t *testing.T) {
 	runCcLibrarySharedTestCase(t, Bp2buildTestCase{
 		Description:                "cc_library_shared stubs",
@@ -640,7 +702,8 @@
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("cc_library_shared", "b", AttrNameToString{
 				"implementation_dynamic_deps": `select({
-        "//build/bazel/rules/apex:android-in_apex": ["@api_surfaces//module-libapi/current:a"],
+        "//build/bazel/rules/apex:apex_b": ["@api_surfaces//module-libapi/current:a"],
+        "//build/bazel/rules/apex:system": ["@api_surfaces//module-libapi/current:a"],
         "//conditions:default": [":a"],
     })`,
 				"tags": `[
@@ -652,6 +715,34 @@
 	})
 }
 
+func TestCcLibraryDoesNotDropStubDepIfNoVariationAcrossAxis(t *testing.T) {
+	runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+		Description:                "cc_library depeends on impl for all configurations",
+		ModuleTypeUnderTest:        "cc_library_shared",
+		ModuleTypeUnderTestFactory: cc.LibrarySharedFactory,
+		Blueprint: soongCcLibrarySharedPreamble + `
+cc_library_shared {
+	name: "a",
+	stubs: { symbol_file: "a.map.txt", versions: ["28", "29", "current"] },
+	bazel_module: { bp2build_available: false },
+	apex_available: ["//apex_available:platform"],
+}
+cc_library_shared {
+	name: "b",
+	shared_libs: [":a"],
+	include_build_directory: false,
+	apex_available: ["//apex_available:platform"],
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_shared", "b", AttrNameToString{
+				"implementation_dynamic_deps": `[":a"]`,
+				"tags":                        `["apex_available=//apex_available:platform"]`,
+			}),
+		},
+	})
+}
+
 func TestCcLibrarySharedStubs_MultipleApexAvailable(t *testing.T) {
 	runCcLibrarySharedTestCase(t, Bp2buildTestCase{
 		ModuleTypeUnderTest:        "cc_library_shared",
@@ -681,7 +772,7 @@
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("cc_library_shared", "b", AttrNameToString{
 				"implementation_dynamic_deps": `select({
-        "//build/bazel/rules/apex:android-in_apex": ["@api_surfaces//module-libapi/current:a"],
+        "//build/bazel/rules/apex:system": ["@api_surfaces//module-libapi/current:a"],
         "//conditions:default": [":a"],
     })`,
 				"tags": `[
@@ -824,6 +915,7 @@
         "header.h",
     ]`,
 				"linkopts": `["-Wl,--version-script,$(location version_script)"]`,
+				"features": `["android_cfi_exports_map"]`,
 			}),
 		},
 	})
@@ -1250,6 +1342,47 @@
 	})
 }
 
+func TestCcLibrarySharedHiddenVisibilityConvertedToFeature(t *testing.T) {
+	runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+		Description: "cc_library_shared changes hidden visibility flag to feature",
+		Blueprint: `
+cc_library_shared{
+	name: "foo",
+	cflags: ["-fvisibility=hidden"],
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+				"features":       `["visibility_hidden"]`,
+				"local_includes": `["."]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibrarySharedHiddenVisibilityConvertedToFeatureOsSpecific(t *testing.T) {
+	runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+		Description: "cc_library_shared changes hidden visibility flag to feature for specific os",
+		Blueprint: `
+cc_library_shared{
+	name: "foo",
+	target: {
+		android: {
+			cflags: ["-fvisibility=hidden"],
+		},
+	},
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+				"features": `select({
+        "//build/bazel/platforms/os:android": ["visibility_hidden"],
+        "//conditions:default": [],
+    })`,
+				"local_includes": `["."]`,
+			}),
+		},
+	})
+}
+
 func TestCcLibrarySharedStubsDessertVersionConversion(t *testing.T) {
 	runCcLibrarySharedTestCase(t, Bp2buildTestCase{
 		Description: "cc_library_shared converts dessert codename versions to numerical versions",
@@ -1263,6 +1396,18 @@
 			"Q",
 			"R",
 			"31",
+		],
+	},
+}
+cc_library_shared {
+	name: "b",
+	include_build_directory: false,
+	stubs: {
+		symbol_file: "b.map.txt",
+		versions: [
+			"Q",
+			"R",
+			"31",
 			"current",
 		],
 	},
@@ -1283,6 +1428,90 @@
 			MakeBazelTarget("cc_library_shared", "a", AttrNameToString{
 				"stubs_symbol_file": `"a.map.txt"`,
 			}),
+			makeCcStubSuiteTargets("b", AttrNameToString{
+				"soname":               `"b.so"`,
+				"source_library_label": `"//:b"`,
+				"stubs_symbol_file":    `"b.map.txt"`,
+				"stubs_versions": `[
+        "29",
+        "30",
+        "31",
+        "current",
+    ]`,
+			}),
+			MakeBazelTarget("cc_library_shared", "b", AttrNameToString{
+				"stubs_symbol_file": `"b.map.txt"`,
+			}),
+		},
+	})
+}
+
+func TestCcLibrarySharedWithCfi(t *testing.T) {
+	runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+		Description: "cc_library_shared has correct features when cfi is enabled for specific variants",
+		Blueprint: `
+cc_library_shared {
+	name: "foo",
+	sanitize: {
+		cfi: true,
+	},
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+				"features":       `["android_cfi"]`,
+				"local_includes": `["."]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibrarySharedWithCfiOsSpecific(t *testing.T) {
+	runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+		Description: "cc_library_shared has correct features when cfi is enabled",
+		Blueprint: `
+cc_library_shared {
+	name: "foo",
+	target: {
+		android: {
+			sanitize: {
+				cfi: true,
+			},
+		},
+	},
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_shared", "foo", AttrNameToString{
+				"features": `select({
+        "//build/bazel/platforms/os:android": ["android_cfi"],
+        "//conditions:default": [],
+    })`,
+				"local_includes": `["."]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibrarySharedWithCfiAndCfiAssemblySupport(t *testing.T) {
+	runCcLibrarySharedTestCase(t, Bp2buildTestCase{
+		Description: "cc_library_shared has correct features when cfi is enabled with cfi assembly support",
+		Blueprint: `
+cc_library_static {
+	name: "foo",
+	sanitize: {
+		cfi: true,
+		config: {
+			cfi_assembly_support: true,
+		},
+	},
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
+				"features": `[
+        "android_cfi",
+        "android_cfi_assembly_support",
+    ]`,
+				"local_includes": `["."]`,
+			}),
 		},
 	})
 }
diff --git a/bp2build/cc_library_static_conversion_test.go b/bp2build/cc_library_static_conversion_test.go
index cd4cf51..f537871 100644
--- a/bp2build/cc_library_static_conversion_test.go
+++ b/bp2build/cc_library_static_conversion_test.go
@@ -1504,6 +1504,7 @@
         versions: ["current"],
     },
     bazel_module: { bp2build_available: false },
+    apex_available: ["com.android.runtime"],
 }
 
 cc_library_static {
@@ -1561,7 +1562,8 @@
 			}),
 			MakeBazelTarget("cc_library_static", "keep_with_stubs", AttrNameToString{
 				"implementation_dynamic_deps": `select({
-        "//build/bazel/rules/apex:android-in_apex": ["@api_surfaces//module-libapi/current:libm"],
+        "//build/bazel/rules/apex:foo": ["@api_surfaces//module-libapi/current:libm"],
+        "//build/bazel/rules/apex:system": ["@api_surfaces//module-libapi/current:libm"],
         "//conditions:default": [":libm"],
     })`,
 				"system_dynamic_deps": `[]`,
@@ -1613,7 +1615,7 @@
 }`,
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
-				"implementation_whole_archive_deps": `["//build/soong/cc/libbuildversion:libbuildversion"]`,
+				"whole_archive_deps": `["//build/soong/cc/libbuildversion:libbuildversion"]`,
 			}),
 		},
 	})
@@ -2032,3 +2034,114 @@
 		},
 	})
 }
+
+func TestCcLibraryStaticHiddenVisibilityConvertedToFeature(t *testing.T) {
+	runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+		Description: "cc_library_static changes hidden visibility flag to feature",
+		Blueprint: `
+cc_library_static {
+	name: "foo",
+	cflags: ["-fvisibility=hidden"],
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
+				"features":       `["visibility_hidden"]`,
+				"local_includes": `["."]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibraryStaticHiddenVisibilityConvertedToFeatureOsSpecific(t *testing.T) {
+	runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+		Description: "cc_library_static changes hidden visibility flag to feature for specific os",
+		Blueprint: `
+cc_library_static {
+	name: "foo",
+	target: {
+		android: {
+			cflags: ["-fvisibility=hidden"],
+		},
+	},
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
+				"features": `select({
+        "//build/bazel/platforms/os:android": ["visibility_hidden"],
+        "//conditions:default": [],
+    })`,
+				"local_includes": `["."]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibraryStaticWithCfi(t *testing.T) {
+	runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+		Description: "cc_library_static has correct features when cfi is enabled",
+		Blueprint: `
+cc_library_static {
+	name: "foo",
+	sanitize: {
+		cfi: true,
+	},
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
+				"features":       `["android_cfi"]`,
+				"local_includes": `["."]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibraryStaticWithCfiOsSpecific(t *testing.T) {
+	runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+		Description: "cc_library_static has correct features when cfi is enabled for specific variants",
+		Blueprint: `
+cc_library_static {
+	name: "foo",
+	target: {
+		android: {
+			sanitize: {
+				cfi: true,
+			},
+		},
+	},
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
+				"features": `select({
+        "//build/bazel/platforms/os:android": ["android_cfi"],
+        "//conditions:default": [],
+    })`,
+				"local_includes": `["."]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibraryStaticWithCfiAndCfiAssemblySupport(t *testing.T) {
+	runCcLibraryStaticTestCase(t, Bp2buildTestCase{
+		Description: "cc_library_static has correct features when cfi is enabled with cfi_assembly_support",
+		Blueprint: `
+cc_library_static {
+	name: "foo",
+	sanitize: {
+		cfi: true,
+		config: {
+			cfi_assembly_support: true,
+		},
+	},
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_library_static", "foo", AttrNameToString{
+				"features": `[
+        "android_cfi",
+        "android_cfi_assembly_support",
+    ]`,
+				"local_includes": `["."]`,
+			}),
+		},
+	})
+}
diff --git a/bp2build/cc_prebuilt_library_conversion_test.go b/bp2build/cc_prebuilt_library_conversion_test.go
index c5a6dfd..b88960e 100644
--- a/bp2build/cc_prebuilt_library_conversion_test.go
+++ b/bp2build/cc_prebuilt_library_conversion_test.go
@@ -20,12 +20,17 @@
 	"android/soong/cc"
 )
 
+func runCcPrebuiltLibraryTestCase(t *testing.T, tc Bp2buildTestCase) {
+	t.Helper()
+	(&tc).ModuleTypeUnderTest = "cc_prebuilt_library"
+	(&tc).ModuleTypeUnderTestFactory = cc.PrebuiltLibraryFactory
+	RunBp2BuildTestCaseSimple(t, tc)
+}
+
 func TestPrebuiltLibraryStaticAndSharedSimple(t *testing.T) {
-	RunBp2BuildTestCaseSimple(t,
+	runCcPrebuiltLibraryTestCase(t,
 		Bp2buildTestCase{
-			Description:                "prebuilt library static and shared simple",
-			ModuleTypeUnderTest:        "cc_prebuilt_library",
-			ModuleTypeUnderTestFactory: cc.PrebuiltLibraryFactory,
+			Description: "prebuilt library static and shared simple",
 			Filesystem: map[string]string{
 				"libf.so": "",
 			},
@@ -51,11 +56,9 @@
 }
 
 func TestPrebuiltLibraryWithArchVariance(t *testing.T) {
-	RunBp2BuildTestCaseSimple(t,
+	runCcPrebuiltLibraryTestCase(t,
 		Bp2buildTestCase{
-			Description:                "prebuilt library with arch variance",
-			ModuleTypeUnderTest:        "cc_prebuilt_library",
-			ModuleTypeUnderTestFactory: cc.PrebuiltLibraryFactory,
+			Description: "prebuilt library with arch variance",
 			Filesystem: map[string]string{
 				"libf.so": "",
 				"libg.so": "",
@@ -95,11 +98,9 @@
 }
 
 func TestPrebuiltLibraryAdditionalAttrs(t *testing.T) {
-	RunBp2BuildTestCaseSimple(t,
+	runCcPrebuiltLibraryTestCase(t,
 		Bp2buildTestCase{
-			Description:                "prebuilt library additional attributes",
-			ModuleTypeUnderTest:        "cc_prebuilt_library",
-			ModuleTypeUnderTestFactory: cc.PrebuiltLibraryFactory,
+			Description: "prebuilt library additional attributes",
 			Filesystem: map[string]string{
 				"libf.so":             "",
 				"testdir/1/include.h": "",
@@ -125,20 +126,19 @@
 					"export_system_includes": `["testdir/2/"]`,
 					"alwayslink":             "True",
 				}),
-				// TODO(b/229374533): When fixed, update this test
 				MakeBazelTarget("cc_prebuilt_library_shared", "libtest", AttrNameToString{
-					"shared_library": `"libf.so"`,
+					"shared_library":         `"libf.so"`,
+					"export_includes":        `["testdir/1/"]`,
+					"export_system_includes": `["testdir/2/"]`,
 				}),
 			},
 		})
 }
 
 func TestPrebuiltLibrarySharedStanzaFails(t *testing.T) {
-	RunBp2BuildTestCaseSimple(t,
+	runCcPrebuiltLibraryTestCase(t,
 		Bp2buildTestCase{
-			Description:                "prebuilt library with shared stanza fails because multiple sources",
-			ModuleTypeUnderTest:        "cc_prebuilt_library",
-			ModuleTypeUnderTestFactory: cc.PrebuiltLibraryFactory,
+			Description: "prebuilt library with shared stanza fails because multiple sources",
 			Filesystem: map[string]string{
 				"libf.so": "",
 				"libg.so": "",
@@ -180,11 +180,9 @@
 }
 
 func TestPrebuiltLibrarySharedAndStaticStanzas(t *testing.T) {
-	RunBp2BuildTestCaseSimple(t,
+	runCcPrebuiltLibraryTestCase(t,
 		Bp2buildTestCase{
-			Description:                "prebuilt library with both shared and static stanzas",
-			ModuleTypeUnderTest:        "cc_prebuilt_library",
-			ModuleTypeUnderTestFactory: cc.PrebuiltLibraryFactory,
+			Description: "prebuilt library with both shared and static stanzas",
 			Filesystem: map[string]string{
 				"libf.so": "",
 				"libg.so": "",
@@ -217,11 +215,9 @@
 
 // TODO(b/228623543): When this bug is fixed, enable this test
 //func TestPrebuiltLibraryOnlyShared(t *testing.T) {
-//	RunBp2BuildTestCaseSimple(t,
+//	runCcPrebuiltLibraryTestCase(t,
 //		bp2buildTestCase{
 //			description:                "prebuilt library shared only",
-//			moduleTypeUnderTest:        "cc_prebuilt_library",
-//			moduleTypeUnderTestFactory: cc.PrebuiltLibraryFactory,
 //			filesystem: map[string]string{
 //				"libf.so": "",
 //			},
@@ -244,11 +240,9 @@
 
 // TODO(b/228623543): When this bug is fixed, enable this test
 //func TestPrebuiltLibraryOnlyStatic(t *testing.T) {
-//	RunBp2BuildTestCaseSimple(t,
+//	runCcPrebuiltLibraryTestCase(t,
 //		bp2buildTestCase{
 //			description:                "prebuilt library static only",
-//			moduleTypeUnderTest:        "cc_prebuilt_library",
-//			moduleTypeUnderTestFactory: cc.PrebuiltLibraryFactory,
 //			filesystem: map[string]string{
 //				"libf.so": "",
 //			},
@@ -272,3 +266,97 @@
 //			},
 //		})
 //}
+
+func TestPrebuiltLibraryWithExportIncludesArchVariant(t *testing.T) {
+	runCcPrebuiltLibraryTestCase(t, Bp2buildTestCase{
+		Description: "cc_prebuilt_library correctly translates export_includes with arch variance",
+		Filesystem: map[string]string{
+			"libf.so": "",
+			"libg.so": "",
+		},
+		Blueprint: `
+cc_prebuilt_library {
+	name: "libtest",
+	srcs: ["libf.so"],
+	arch: {
+		arm: { export_include_dirs: ["testdir/1/"], },
+		arm64: { export_include_dirs: ["testdir/2/"], },
+	},
+	bazel_module: { bp2build_available: true },
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_prebuilt_library_shared", "libtest", AttrNameToString{
+				"shared_library": `"libf.so"`,
+				"export_includes": `select({
+        "//build/bazel/platforms/arch:arm": ["testdir/1/"],
+        "//build/bazel/platforms/arch:arm64": ["testdir/2/"],
+        "//conditions:default": [],
+    })`,
+			}),
+			MakeBazelTarget("cc_prebuilt_library_static", "libtest_bp2build_cc_library_static", AttrNameToString{
+				"static_library": `"libf.so"`,
+				"export_includes": `select({
+        "//build/bazel/platforms/arch:arm": ["testdir/1/"],
+        "//build/bazel/platforms/arch:arm64": ["testdir/2/"],
+        "//conditions:default": [],
+    })`,
+			}),
+			MakeBazelTarget("cc_prebuilt_library_static", "libtest_bp2build_cc_library_static_alwayslink", AttrNameToString{
+				"alwayslink":     "True",
+				"static_library": `"libf.so"`,
+				"export_includes": `select({
+        "//build/bazel/platforms/arch:arm": ["testdir/1/"],
+        "//build/bazel/platforms/arch:arm64": ["testdir/2/"],
+        "//conditions:default": [],
+    })`,
+			}),
+		},
+	})
+}
+
+func TestPrebuiltLibraryWithExportSystemIncludesArchVariant(t *testing.T) {
+	runCcPrebuiltLibraryTestCase(t, Bp2buildTestCase{
+		Description: "cc_prebuilt_ibrary correctly translates export_system_includes with arch variance",
+		Filesystem: map[string]string{
+			"libf.so": "",
+			"libg.so": "",
+		},
+		Blueprint: `
+cc_prebuilt_library {
+	name: "libtest",
+	srcs: ["libf.so"],
+	arch: {
+		arm: { export_system_include_dirs: ["testdir/1/"], },
+		arm64: { export_system_include_dirs: ["testdir/2/"], },
+	},
+	bazel_module: { bp2build_available: true },
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_prebuilt_library_shared", "libtest", AttrNameToString{
+				"shared_library": `"libf.so"`,
+				"export_system_includes": `select({
+        "//build/bazel/platforms/arch:arm": ["testdir/1/"],
+        "//build/bazel/platforms/arch:arm64": ["testdir/2/"],
+        "//conditions:default": [],
+    })`,
+			}),
+			MakeBazelTarget("cc_prebuilt_library_static", "libtest_bp2build_cc_library_static", AttrNameToString{
+				"static_library": `"libf.so"`,
+				"export_system_includes": `select({
+        "//build/bazel/platforms/arch:arm": ["testdir/1/"],
+        "//build/bazel/platforms/arch:arm64": ["testdir/2/"],
+        "//conditions:default": [],
+    })`,
+			}),
+			MakeBazelTarget("cc_prebuilt_library_static", "libtest_bp2build_cc_library_static_alwayslink", AttrNameToString{
+				"alwayslink":     "True",
+				"static_library": `"libf.so"`,
+				"export_system_includes": `select({
+        "//build/bazel/platforms/arch:arm": ["testdir/1/"],
+        "//build/bazel/platforms/arch:arm64": ["testdir/2/"],
+        "//conditions:default": [],
+    })`,
+			}),
+		},
+	})
+}
diff --git a/bp2build/cc_prebuilt_library_shared_conversion_test.go b/bp2build/cc_prebuilt_library_shared_conversion_test.go
new file mode 100644
index 0000000..9e975ae
--- /dev/null
+++ b/bp2build/cc_prebuilt_library_shared_conversion_test.go
@@ -0,0 +1,165 @@
+// Copyright 2022 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 (
+	"testing"
+
+	"android/soong/cc"
+)
+
+func runCcPrebuiltLibrarySharedTestCase(t *testing.T, tc Bp2buildTestCase) {
+	t.Parallel()
+	t.Helper()
+	(&tc).ModuleTypeUnderTest = "cc_prebuilt_library_shared"
+	(&tc).ModuleTypeUnderTestFactory = cc.PrebuiltSharedLibraryFactory
+	RunBp2BuildTestCaseSimple(t, tc)
+}
+
+func TestPrebuiltLibrarySharedSimple(t *testing.T) {
+	runCcPrebuiltLibrarySharedTestCase(t,
+		Bp2buildTestCase{
+			Description: "prebuilt library shared simple",
+			Filesystem: map[string]string{
+				"libf.so": "",
+			},
+			Blueprint: `
+cc_prebuilt_library_shared {
+	name: "libtest",
+	srcs: ["libf.so"],
+	bazel_module: { bp2build_available: true },
+}`,
+			ExpectedBazelTargets: []string{
+				MakeBazelTarget("cc_prebuilt_library_shared", "libtest", AttrNameToString{
+					"shared_library": `"libf.so"`,
+				}),
+			},
+		})
+}
+
+func TestPrebuiltLibrarySharedWithArchVariance(t *testing.T) {
+	runCcPrebuiltLibrarySharedTestCase(t,
+		Bp2buildTestCase{
+			Description: "prebuilt library shared with arch variance",
+			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{
+				MakeBazelTarget("cc_prebuilt_library_shared", "libtest", AttrNameToString{
+					"shared_library": `select({
+        "//build/bazel/platforms/arch:arm": "libg.so",
+        "//build/bazel/platforms/arch:arm64": "libf.so",
+        "//conditions:default": None,
+    })`,
+				}),
+			},
+		})
+}
+
+func TestPrebuiltLibrarySharedAdditionalAttrs(t *testing.T) {
+	runCcPrebuiltLibrarySharedTestCase(t,
+		Bp2buildTestCase{
+			Description: "prebuilt library shared additional attributes",
+			Filesystem: map[string]string{
+				"libf.so":             "",
+				"testdir/1/include.h": "",
+				"testdir/2/other.h":   "",
+			},
+			Blueprint: `
+cc_prebuilt_library_shared {
+	name: "libtest",
+	srcs: ["libf.so"],
+	export_include_dirs: ["testdir/1/"],
+	export_system_include_dirs: ["testdir/2/"],
+	bazel_module: { bp2build_available: true },
+}`,
+			ExpectedBazelTargets: []string{
+				MakeBazelTarget("cc_prebuilt_library_shared", "libtest", AttrNameToString{
+					"shared_library":         `"libf.so"`,
+					"export_includes":        `["testdir/1/"]`,
+					"export_system_includes": `["testdir/2/"]`,
+				}),
+			},
+		})
+}
+
+func TestPrebuiltLibrarySharedWithExportIncludesArchVariant(t *testing.T) {
+	runCcPrebuiltLibrarySharedTestCase(t, Bp2buildTestCase{
+		Description: "cc_prebuilt_library_shared correctly translates export_includes with arch variance",
+		Filesystem: map[string]string{
+			"libf.so": "",
+			"libg.so": "",
+		},
+		Blueprint: `
+cc_prebuilt_library_shared {
+	name: "libtest",
+	srcs: ["libf.so"],
+	arch: {
+		arm: { export_include_dirs: ["testdir/1/"], },
+		arm64: { export_include_dirs: ["testdir/2/"], },
+	},
+	bazel_module: { bp2build_available: true },
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_prebuilt_library_shared", "libtest", AttrNameToString{
+				"shared_library": `"libf.so"`,
+				"export_includes": `select({
+        "//build/bazel/platforms/arch:arm": ["testdir/1/"],
+        "//build/bazel/platforms/arch:arm64": ["testdir/2/"],
+        "//conditions:default": [],
+    })`,
+			}),
+		},
+	})
+}
+
+func TestPrebuiltLibrarySharedWithExportSystemIncludesArchVariant(t *testing.T) {
+	runCcPrebuiltLibrarySharedTestCase(t, Bp2buildTestCase{
+		Description: "cc_prebuilt_library_shared correctly translates export_system_includes with arch variance",
+		Filesystem: map[string]string{
+			"libf.so": "",
+			"libg.so": "",
+		},
+		Blueprint: `
+cc_prebuilt_library_shared {
+	name: "libtest",
+	srcs: ["libf.so"],
+	arch: {
+		arm: { export_system_include_dirs: ["testdir/1/"], },
+		arm64: { export_system_include_dirs: ["testdir/2/"], },
+	},
+	bazel_module: { bp2build_available: true },
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_prebuilt_library_shared", "libtest", AttrNameToString{
+				"shared_library": `"libf.so"`,
+				"export_system_includes": `select({
+        "//build/bazel/platforms/arch:arm": ["testdir/1/"],
+        "//build/bazel/platforms/arch:arm64": ["testdir/2/"],
+        "//conditions:default": [],
+    })`,
+			}),
+		},
+	})
+}
diff --git a/bp2build/cc_prebuilt_library_static_conversion_test.go b/bp2build/cc_prebuilt_library_static_conversion_test.go
new file mode 100644
index 0000000..77562e7
--- /dev/null
+++ b/bp2build/cc_prebuilt_library_static_conversion_test.go
@@ -0,0 +1,199 @@
+// Copyright 2022 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 (
+	"testing"
+
+	"android/soong/cc"
+)
+
+func runCcPrebuiltLibraryStaticTestCase(t *testing.T, tc Bp2buildTestCase) {
+	t.Parallel()
+	t.Helper()
+	(&tc).ModuleTypeUnderTest = "cc_prebuilt_library_static"
+	(&tc).ModuleTypeUnderTestFactory = cc.PrebuiltStaticLibraryFactory
+	RunBp2BuildTestCaseSimple(t, tc)
+}
+
+func TestPrebuiltLibraryStaticSimple(t *testing.T) {
+	runCcPrebuiltLibraryStaticTestCase(t,
+		Bp2buildTestCase{
+			Description: "prebuilt library static simple",
+			Filesystem: map[string]string{
+				"libf.so": "",
+			},
+			Blueprint: `
+cc_prebuilt_library_static {
+	name: "libtest",
+	srcs: ["libf.so"],
+	bazel_module: { bp2build_available: true },
+}`,
+			ExpectedBazelTargets: []string{
+				MakeBazelTarget("cc_prebuilt_library_static", "libtest", AttrNameToString{
+					"static_library": `"libf.so"`,
+				}),
+				MakeBazelTarget("cc_prebuilt_library_static", "libtest_alwayslink", AttrNameToString{
+					"static_library": `"libf.so"`,
+					"alwayslink":     "True",
+				}),
+			},
+		})
+}
+
+func TestPrebuiltLibraryStaticWithArchVariance(t *testing.T) {
+	runCcPrebuiltLibraryStaticTestCase(t,
+		Bp2buildTestCase{
+			Description: "prebuilt library with arch variance",
+			Filesystem: map[string]string{
+				"libf.so": "",
+				"libg.so": "",
+			},
+			Blueprint: `
+cc_prebuilt_library_static {
+	name: "libtest",
+	arch: {
+		arm64: { srcs: ["libf.so"], },
+		arm: { srcs: ["libg.so"], },
+	},
+	bazel_module: { bp2build_available: true },
+}`,
+			ExpectedBazelTargets: []string{
+				MakeBazelTarget("cc_prebuilt_library_static", "libtest", AttrNameToString{
+					"static_library": `select({
+        "//build/bazel/platforms/arch:arm": "libg.so",
+        "//build/bazel/platforms/arch:arm64": "libf.so",
+        "//conditions:default": None,
+    })`}),
+				MakeBazelTarget("cc_prebuilt_library_static", "libtest_alwayslink", AttrNameToString{
+					"alwayslink": "True",
+					"static_library": `select({
+        "//build/bazel/platforms/arch:arm": "libg.so",
+        "//build/bazel/platforms/arch:arm64": "libf.so",
+        "//conditions:default": None,
+    })`}),
+			},
+		})
+}
+
+func TestPrebuiltLibraryStaticAdditionalAttrs(t *testing.T) {
+	runCcPrebuiltLibraryStaticTestCase(t,
+		Bp2buildTestCase{
+			Description: "prebuilt library additional attributes",
+			Filesystem: map[string]string{
+				"libf.so":             "",
+				"testdir/1/include.h": "",
+				"testdir/2/other.h":   "",
+			},
+			Blueprint: `
+cc_prebuilt_library_static {
+	name: "libtest",
+	srcs: ["libf.so"],
+	export_include_dirs: ["testdir/1/"],
+	export_system_include_dirs: ["testdir/2/"],
+	bazel_module: { bp2build_available: true },
+}`,
+			ExpectedBazelTargets: []string{
+				MakeBazelTarget("cc_prebuilt_library_static", "libtest", AttrNameToString{
+					"static_library":         `"libf.so"`,
+					"export_includes":        `["testdir/1/"]`,
+					"export_system_includes": `["testdir/2/"]`,
+				}),
+				MakeBazelTarget("cc_prebuilt_library_static", "libtest_alwayslink", AttrNameToString{
+					"static_library":         `"libf.so"`,
+					"export_includes":        `["testdir/1/"]`,
+					"export_system_includes": `["testdir/2/"]`,
+					"alwayslink":             "True",
+				}),
+			},
+		})
+}
+
+func TestPrebuiltLibraryStaticWithExportIncludesArchVariant(t *testing.T) {
+	runCcPrebuiltLibraryStaticTestCase(t, Bp2buildTestCase{
+		Description: "cc_prebuilt_library_static correctly translates export_includes with arch variance",
+		Filesystem: map[string]string{
+			"libf.so": "",
+			"libg.so": "",
+		},
+		Blueprint: `
+cc_prebuilt_library_static {
+	name: "libtest",
+	srcs: ["libf.so"],
+	arch: {
+		arm: { export_include_dirs: ["testdir/1/"], },
+		arm64: { export_include_dirs: ["testdir/2/"], },
+	},
+	bazel_module: { bp2build_available: true },
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_prebuilt_library_static", "libtest", AttrNameToString{
+				"static_library": `"libf.so"`,
+				"export_includes": `select({
+        "//build/bazel/platforms/arch:arm": ["testdir/1/"],
+        "//build/bazel/platforms/arch:arm64": ["testdir/2/"],
+        "//conditions:default": [],
+    })`,
+			}),
+			MakeBazelTarget("cc_prebuilt_library_static", "libtest_alwayslink", AttrNameToString{
+				"alwayslink":     "True",
+				"static_library": `"libf.so"`,
+				"export_includes": `select({
+        "//build/bazel/platforms/arch:arm": ["testdir/1/"],
+        "//build/bazel/platforms/arch:arm64": ["testdir/2/"],
+        "//conditions:default": [],
+    })`,
+			}),
+		},
+	})
+}
+
+func TestPrebuiltLibraryStaticWithExportSystemIncludesArchVariant(t *testing.T) {
+	runCcPrebuiltLibraryStaticTestCase(t, Bp2buildTestCase{
+		Description: "cc_prebuilt_library_static correctly translates export_system_includes with arch variance",
+		Filesystem: map[string]string{
+			"libf.so": "",
+			"libg.so": "",
+		},
+		Blueprint: `
+cc_prebuilt_library_static {
+	name: "libtest",
+	srcs: ["libf.so"],
+	arch: {
+		arm: { export_system_include_dirs: ["testdir/1/"], },
+		arm64: { export_system_include_dirs: ["testdir/2/"], },
+	},
+	bazel_module: { bp2build_available: true },
+}`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("cc_prebuilt_library_static", "libtest", AttrNameToString{
+				"static_library": `"libf.so"`,
+				"export_system_includes": `select({
+        "//build/bazel/platforms/arch:arm": ["testdir/1/"],
+        "//build/bazel/platforms/arch:arm64": ["testdir/2/"],
+        "//conditions:default": [],
+    })`,
+			}),
+			MakeBazelTarget("cc_prebuilt_library_static", "libtest_alwayslink", AttrNameToString{
+				"alwayslink":     "True",
+				"static_library": `"libf.so"`,
+				"export_system_includes": `select({
+        "//build/bazel/platforms/arch:arm": ["testdir/1/"],
+        "//build/bazel/platforms/arch:arm64": ["testdir/2/"],
+        "//conditions:default": [],
+    })`,
+			}),
+		},
+	})
+}
diff --git a/bp2build/cc_test_conversion_test.go b/bp2build/cc_test_conversion_test.go
index 20adddb..4df4d4d 100644
--- a/bp2build/cc_test_conversion_test.go
+++ b/bp2build/cc_test_conversion_test.go
@@ -76,17 +76,28 @@
             static_libs: ["hostlib"],
         },
     },
+    static_libs: ["cc_test_lib1"],
+    shared_libs: ["cc_test_lib2"],
     data: [":data_mod", "file.txt"],
     data_bins: [":cc_bin"],
     data_libs: [":cc_lib"],
     cflags: ["-Wall"],
 }
+
+cc_test_library {
+    name: "cc_test_lib1",
+    host_supported: true,
+    include_build_directory: false,
+}
 ` + simpleModuleDoNotConvertBp2build("cc_library", "foolib") +
 			simpleModuleDoNotConvertBp2build("cc_library_static", "hostlib") +
 			simpleModuleDoNotConvertBp2build("genrule", "data_mod") +
 			simpleModuleDoNotConvertBp2build("cc_binary", "cc_bin") +
-			simpleModuleDoNotConvertBp2build("cc_test_library", "cc_lib"),
+			simpleModuleDoNotConvertBp2build("cc_library", "cc_lib") +
+			simpleModuleDoNotConvertBp2build("cc_test_library", "cc_test_lib2"),
 		targets: []testBazelTarget{
+			{"cc_library_shared", "cc_test_lib1", AttrNameToString{}},
+			{"cc_library_static", "cc_test_lib1_bp2build_cc_library_static", AttrNameToString{}},
 			{"cc_test", "mytest", AttrNameToString{
 				"copts": `["-Wall"]`,
 				"data": `[
@@ -95,7 +106,7 @@
         ":cc_bin",
         ":cc_lib",
     ]`,
-				"deps": `select({
+				"deps": `[":cc_test_lib1_bp2build_cc_library_static"] + select({
         "//build/bazel/platforms/os:darwin": [":hostlib"],
         "//build/bazel/platforms/os:linux_bionic": [":hostlib"],
         "//build/bazel/platforms/os:linux_glibc": [":hostlib"],
@@ -106,7 +117,7 @@
 				"gtest":          "True",
 				"isolated":       "True",
 				"local_includes": `["."]`,
-				"dynamic_deps": `select({
+				"dynamic_deps": `[":cc_test_lib2"] + select({
         "//build/bazel/platforms/os:android": [":foolib"],
         "//conditions:default": [],
     })`,
diff --git a/bp2build/configurability.go b/bp2build/configurability.go
index 8e17103..3d9f0a2 100644
--- a/bp2build/configurability.go
+++ b/bp2build/configurability.go
@@ -279,6 +279,10 @@
 	}
 
 	if len(selects) == 0 {
+		// If there is a default value, and there are no selects for this axis, print that without any selects.
+		if val, exists := selectMap[bazel.ConditionsDefaultSelectKey]; exists {
+			return prettyPrint(val, indent, emitZeroValues)
+		}
 		// No conditions (or all values are empty lists), so no need for a map.
 		return "", nil
 	}
diff --git a/bp2build/conversion.go b/bp2build/conversion.go
index 608fcd8..f598332 100644
--- a/bp2build/conversion.go
+++ b/bp2build/conversion.go
@@ -59,14 +59,17 @@
 
 	files = append(files, newFile("product_config", "arch_configuration.bzl", android.StarlarkArchConfigurations()))
 
-	apiLevelsContent, err := json.Marshal(android.GetApiLevelsMap(cfg))
+	apiLevelsMap, err := android.GetApiLevelsMap(cfg)
+	if err != nil {
+		return nil, err
+	}
+	apiLevelsContent, err := json.Marshal(apiLevelsMap)
 	if err != nil {
 		return nil, err
 	}
 	files = append(files, newFile("api_levels", GeneratedBuildFileName, `exports_files(["api_levels.json"])`))
 	// TODO(b/269691302)  value of apiLevelsContent is product variable dependent and should be avoided for soong injection
 	files = append(files, newFile("api_levels", "api_levels.json", string(apiLevelsContent)))
-	files = append(files, newFile("api_levels", "api_levels.bzl", android.StarlarkApiLevelConfigs(cfg)))
 	files = append(files, newFile("api_levels", "platform_versions.bzl", platformVersionContents(cfg)))
 
 	files = append(files, newFile("allowlists", GeneratedBuildFileName, ""))
diff --git a/bp2build/conversion_test.go b/bp2build/conversion_test.go
index 2f5dc3c..379f83b 100644
--- a/bp2build/conversion_test.go
+++ b/bp2build/conversion_test.go
@@ -159,10 +159,6 @@
 		},
 		{
 			dir:      "api_levels",
-			basename: "api_levels.bzl",
-		},
-		{
-			dir:      "api_levels",
 			basename: "platform_versions.bzl",
 		},
 		{
diff --git a/bp2build/genrule_conversion_test.go b/bp2build/genrule_conversion_test.go
index 3490881..5cf4fb2 100644
--- a/bp2build/genrule_conversion_test.go
+++ b/bp2build/genrule_conversion_test.go
@@ -49,6 +49,7 @@
     srcs: ["other_tool.in"],
     cmd: "cp $(in) $(out)",
 }`, genruleTarget, genruleTarget),
+		"other/file.txt": "",
 	}
 }
 
@@ -293,17 +294,20 @@
 	bp := `%s {
     name: "foo",
     out: ["foo.out"],
-    srcs: [":other.tool"],
+    srcs: [":other.tool", "other/file.txt",],
     tool_files: [":foo.tool"],
-    cmd: "$(locations :foo.tool) -s $(out) $(location :other.tool)",
+    cmd: "$(locations :foo.tool) $(location other/file.txt) -s $(out) $(location :other.tool)",
     bazel_module: { bp2build_available: true },
 }`
 
 	for _, tc := range testCases {
 		moduleAttrs := AttrNameToString{
-			"cmd":   `"$(locations //other:foo.tool) -s $(OUTS) $(location //other:other.tool)"`,
-			"outs":  `["foo.out"]`,
-			"srcs":  `["//other:other.tool"]`,
+			"cmd":  `"$(locations //other:foo.tool) $(location //other:file.txt) -s $(OUTS) $(location //other:other.tool)"`,
+			"outs": `["foo.out"]`,
+			"srcs": `[
+        "//other:other.tool",
+        "//other:file.txt",
+    ]`,
 			"tools": `["//other:foo.tool"]`,
 		}
 
diff --git a/bp2build/gensrcs_conversion_test.go b/bp2build/gensrcs_conversion_test.go
index 4845973..e808340 100644
--- a/bp2build/gensrcs_conversion_test.go
+++ b/bp2build/gensrcs_conversion_test.go
@@ -33,7 +33,8 @@
                 name: "foo",
                 srcs: ["test/input.txt", ":external_files"],
                 tool_files: ["program.py"],
-                cmd: "$(location program.py) $(in) $(out)",
+                cmd: "$(location program.py) $(in) $(out) $(location foo/file.txt) $(location :external_files)",
+                data: ["foo/file.txt", ":external_files"],
                 output_extension: "out",
                 bazel_module: { bp2build_available: true },
 			}`,
@@ -44,7 +45,11 @@
     ]`,
 				"tools":            `["program.py"]`,
 				"output_extension": `"out"`,
-				"cmd":              `"$(location program.py) $(SRC) $(OUT)"`,
+				"cmd":              `"$(location program.py) $(SRC) $(OUT) $(location foo/file.txt) $(location :external_files__BP2BUILD__MISSING__DEP)"`,
+				"data": `[
+        "foo/file.txt",
+        ":external_files__BP2BUILD__MISSING__DEP",
+    ]`,
 			},
 		},
 		{
diff --git a/bp2build/java_binary_host_conversion_test.go b/bp2build/java_binary_host_conversion_test.go
index c821f59..39e55c4 100644
--- a/bp2build/java_binary_host_conversion_test.go
+++ b/bp2build/java_binary_host_conversion_test.go
@@ -68,7 +68,7 @@
 			}),
 			MakeBazelTarget("java_binary", "java-binary-host-1", AttrNameToString{
 				"main_class": `"com.android.test.MainClass"`,
-				"jvm_flags":  `["-Djava.library.path=$${RUNPATH}other"]`,
+				"jvm_flags":  `["-Djava.library.path=$${RUNPATH}other/jni-lib-1"]`,
 				"target_compatible_with": `select({
         "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
         "//conditions:default": [],
@@ -259,17 +259,20 @@
 	runJavaBinaryHostTestCase(t, Bp2buildTestCase{
 		Description: "java_binary_host with srcs, libs, resources.",
 		Filesystem: map[string]string{
-			"test.mf":   "Main-Class: com.android.test.MainClass",
-			"res/a.res": "",
-			"res/b.res": "",
-		},
-		Blueprint: `java_binary_host {
+			"adir/test.mf":   "Main-Class: com.android.test.MainClass",
+			"adir/res/a.res": "",
+			"adir/res/b.res": "",
+			"adir/Android.bp": `java_binary_host {
     name: "java-binary-host",
     manifest: "test.mf",
     srcs: ["a.java", "b.kt"],
     java_resources: ["res/a.res", "res/b.res"],
+    bazel_module: { bp2build_available: true },
 }
 `,
+		},
+		Dir:       "adir",
+		Blueprint: "",
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("kt_jvm_library", "java-binary-host_lib", AttrNameToString{
 				"srcs": `[
@@ -280,6 +283,7 @@
         "res/a.res",
         "res/b.res",
     ]`,
+				"resource_strip_prefix": `"adir"`,
 				"target_compatible_with": `select({
         "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
         "//conditions:default": [],
diff --git a/bp2build/java_library_conversion_test.go b/bp2build/java_library_conversion_test.go
index 24b763b..fd92e95 100644
--- a/bp2build/java_library_conversion_test.go
+++ b/bp2build/java_library_conversion_test.go
@@ -283,21 +283,25 @@
 
 func TestJavaLibraryResources(t *testing.T) {
 	runJavaLibraryTestCase(t, Bp2buildTestCase{
+		Dir: "adir",
 		Filesystem: map[string]string{
-			"res/a.res":      "",
-			"res/b.res":      "",
-			"res/dir1/b.res": "",
-		},
-		Blueprint: `java_library {
+			"adir/res/a.res":      "",
+			"adir/res/b.res":      "",
+			"adir/res/dir1/b.res": "",
+			"adir/Android.bp": `java_library {
     name: "java-lib-1",
-	java_resources: ["res/a.res", "res/b.res"],
+    java_resources: ["res/a.res", "res/b.res"],
+    bazel_module: { bp2build_available: true },
 }`,
+		},
+		Blueprint: "",
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("java_library", "java-lib-1", AttrNameToString{
 				"resources": `[
         "res/a.res",
         "res/b.res",
     ]`,
+				"resource_strip_prefix": `"adir"`,
 			}),
 			MakeNeverlinkDuplicateTarget("java_library", "java-lib-1"),
 		},
@@ -822,3 +826,43 @@
 		},
 	})
 }
+
+func TestJavaLibraryJavaResourcesSingleFilegroup(t *testing.T) {
+	runJavaLibraryTestCaseWithRegistrationCtxFunc(t, Bp2buildTestCase{
+		Filesystem: map[string]string{
+			"res/a.res":      "",
+			"res/b.res":      "",
+			"res/dir1/b.res": "",
+		},
+		Description: "java_library",
+		Blueprint: `java_library {
+    name: "java-lib-1",
+    srcs: ["a.java"],
+    java_resources: [":filegroup1"],
+    bazel_module: { bp2build_available: true },
+}
+
+filegroup {
+    name: "filegroup1",
+    path: "foo",
+    srcs: ["foo/a", "foo/b"],
+}
+
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("java_library", "java-lib-1", AttrNameToString{
+				"srcs":                  `["a.java"]`,
+				"resources":             `[":filegroup1"]`,
+				"resource_strip_prefix": `"foo"`,
+			}),
+			MakeNeverlinkDuplicateTarget("java_library", "java-lib-1"),
+			MakeBazelTargetNoRestrictions("filegroup", "filegroup1", AttrNameToString{
+				"srcs": `[
+        "foo/a",
+        "foo/b",
+    ]`}),
+		},
+	}, func(ctx android.RegistrationContext) {
+		ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
+	})
+}
diff --git a/bp2build/java_test_host_conversion_test.go b/bp2build/java_test_host_conversion_test.go
new file mode 100644
index 0000000..f411ffb
--- /dev/null
+++ b/bp2build/java_test_host_conversion_test.go
@@ -0,0 +1,149 @@
+// Copyright 2023 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 (
+	"testing"
+
+	"android/soong/android"
+	"android/soong/java"
+)
+
+func runJavaTestHostTestCase(t *testing.T, tc Bp2buildTestCase) {
+	t.Helper()
+	(&tc).ModuleTypeUnderTest = "java_test_host"
+	(&tc).ModuleTypeUnderTestFactory = java.TestHostFactory
+	RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {
+		ctx.RegisterModuleType("java_library", java.LibraryFactory)
+	}, tc)
+}
+
+func TestJavaTestHostGeneral(t *testing.T) {
+	runJavaTestHostTestCase(t, Bp2buildTestCase{
+		Description: "java_test_host general",
+		Filesystem:  map[string]string{},
+		Blueprint: `
+java_test_host {
+    name: "java_test_host-1",
+    srcs: ["a.java", "b.java"],
+    libs: ["lib_a"],
+    static_libs: ["static_libs_a"],
+    exclude_srcs: ["b.java"],
+    javacflags: ["-Xdoclint:all/protected"],
+    java_version: "8",
+}
+
+java_library {
+    name: "lib_a",
+    bazel_module: { bp2build_available: false },
+}
+
+java_library {
+    name: "static_libs_a",
+    bazel_module: { bp2build_available: false },
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("java_library", "java_test_host-1_lib", AttrNameToString{
+				"deps": `[
+        ":lib_a-neverlink",
+        ":static_libs_a",
+    ]`,
+				"java_version": `"8"`,
+				"javacopts":    `["-Xdoclint:all/protected"]`,
+				"srcs":         `["a.java"]`,
+				"target_compatible_with": `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`,
+			}),
+			MakeBazelTarget("java_test", "java_test_host-1", AttrNameToString{
+				"runtime_deps": `[":java_test_host-1_lib"]`,
+				"target_compatible_with": `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`,
+			}),
+		},
+	})
+}
+
+func TestJavaTestHostNoSrcs(t *testing.T) {
+	runJavaTestHostTestCase(t, Bp2buildTestCase{
+		Description: "java_test_host without srcs",
+		Filesystem:  map[string]string{},
+		Blueprint: `
+java_test_host {
+    name: "java_test_host-1",
+    libs: ["lib_a"],
+    static_libs: ["static_libs_a"],
+}
+
+java_library {
+    name: "lib_a",
+    bazel_module: { bp2build_available: false },
+}
+
+java_library {
+    name: "static_libs_a",
+    bazel_module: { bp2build_available: false },
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("java_test", "java_test_host-1", AttrNameToString{
+				"runtime_deps": `[
+        ":lib_a-neverlink",
+        ":static_libs_a",
+    ]`,
+				"target_compatible_with": `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`,
+			}),
+		},
+	})
+}
+
+func TestJavaTestHostKotlinSrcs(t *testing.T) {
+	runJavaTestHostTestCase(t, Bp2buildTestCase{
+		Description: "java_test_host with .kt in srcs",
+		Filesystem:  map[string]string{},
+		Blueprint: `
+java_test_host {
+    name: "java_test_host-1",
+    srcs: ["a.java", "b.kt"],
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("java_test", "java_test_host-1", AttrNameToString{
+				"runtime_deps": `[":java_test_host-1_lib"]`,
+				"target_compatible_with": `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`,
+			}),
+			MakeBazelTarget("kt_jvm_library", "java_test_host-1_lib", AttrNameToString{
+				"srcs": `[
+        "a.java",
+        "b.kt",
+    ]`,
+				"target_compatible_with": `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`,
+			}),
+		},
+	})
+}
diff --git a/bp2build/ndk_headers_conversion_test.go b/bp2build/ndk_headers_conversion_test.go
deleted file mode 100644
index 9d0f1f2..0000000
--- a/bp2build/ndk_headers_conversion_test.go
+++ /dev/null
@@ -1,164 +0,0 @@
-// Copyright 2022 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 (
-	"fmt"
-	"testing"
-
-	"android/soong/cc"
-)
-
-func TestNdkHeaderFilepaths(t *testing.T) {
-	bpTemplate := `
-	ndk_headers {
-		name: "foo",
-		srcs: %v,
-		exclude_srcs: %v,
-	}
-	`
-	testCases := []struct {
-		desc         string
-		srcs         string
-		excludeSrcs  string
-		expectedHdrs string
-	}{
-		{
-			desc:         "Single header file",
-			srcs:         `["foo.h"]`,
-			excludeSrcs:  `[]`,
-			expectedHdrs: `["foo.h"]`,
-		},
-		{
-			desc:        "Multiple header files",
-			srcs:        `["foo.h", "foo_other.h"]`,
-			excludeSrcs: `[]`,
-			expectedHdrs: `[
-        "foo.h",
-        "foo_other.h",
-    ]`,
-		},
-		{
-			desc:         "Multiple header files with excludes",
-			srcs:         `["foo.h", "foo_other.h"]`,
-			excludeSrcs:  `["foo_other.h"]`,
-			expectedHdrs: `["foo.h"]`,
-		},
-		{
-			desc:        "Multiple header files via Soong-supported globs",
-			srcs:        `["*.h"]`,
-			excludeSrcs: `[]`,
-			expectedHdrs: `[
-        "foo.h",
-        "foo_other.h",
-    ]`,
-		},
-	}
-	for _, testCase := range testCases {
-		fs := map[string]string{
-			"foo.h":       "",
-			"foo_other.h": "",
-		}
-		expectedApiContributionTargetName := "foo.contribution"
-		expectedBazelTarget := MakeBazelTargetNoRestrictions(
-			"cc_api_headers",
-			expectedApiContributionTargetName,
-			AttrNameToString{
-				"hdrs": testCase.expectedHdrs,
-			},
-		)
-		RunApiBp2BuildTestCase(t, cc.RegisterNdkModuleTypes, Bp2buildTestCase{
-			Description:          testCase.desc,
-			Blueprint:            fmt.Sprintf(bpTemplate, testCase.srcs, testCase.excludeSrcs),
-			ExpectedBazelTargets: []string{expectedBazelTarget},
-			Filesystem:           fs,
-		})
-	}
-}
-
-func TestNdkHeaderIncludeDir(t *testing.T) {
-	bpTemplate := `
-	ndk_headers {
-		name: "foo",
-		from: %v,
-		to: "this/value/is/ignored",
-	}
-	`
-	testCases := []struct {
-		desc               string
-		from               string
-		expectedIncludeDir string
-	}{
-		{
-			desc:               "Empty `from` value",
-			from:               `""`,
-			expectedIncludeDir: `""`,
-		},
-		{
-			desc:               "Non-Empty `from` value",
-			from:               `"include"`,
-			expectedIncludeDir: `"include"`,
-		},
-	}
-	for _, testCase := range testCases {
-		expectedApiContributionTargetName := "foo.contribution"
-		expectedBazelTarget := MakeBazelTargetNoRestrictions(
-			"cc_api_headers",
-			expectedApiContributionTargetName,
-			AttrNameToString{
-				"include_dir": testCase.expectedIncludeDir,
-			},
-		)
-		RunApiBp2BuildTestCase(t, cc.RegisterNdkModuleTypes, Bp2buildTestCase{
-			Description:          testCase.desc,
-			Blueprint:            fmt.Sprintf(bpTemplate, testCase.from),
-			ExpectedBazelTargets: []string{expectedBazelTarget},
-		})
-	}
-}
-
-func TestVersionedNdkHeaderFilepaths(t *testing.T) {
-	bp := `
-	versioned_ndk_headers {
-		name: "common_libc",
-		from: "include"
-	}
-	`
-	fs := map[string]string{
-		"include/math.h":    "",
-		"include/stdio.h":   "",
-		"include/arm/arm.h": "",
-		"include/x86/x86.h": "",
-	}
-	expectedApiContributionTargetName := "common_libc.contribution"
-	expectedBazelTarget := MakeBazelTargetNoRestrictions(
-		"cc_api_headers",
-		expectedApiContributionTargetName,
-		AttrNameToString{
-			"include_dir": `"include"`,
-			"hdrs": `[
-        "include/math.h",
-        "include/stdio.h",
-        "include/arm/arm.h",
-        "include/x86/x86.h",
-    ]`,
-		},
-	)
-	RunApiBp2BuildTestCase(t, cc.RegisterNdkModuleTypes, Bp2buildTestCase{
-		Blueprint:            bp,
-		Filesystem:           fs,
-		ExpectedBazelTargets: []string{expectedBazelTarget},
-	})
-}
diff --git a/bp2build/ndk_library_conversion_test.go b/bp2build/ndk_library_conversion_test.go
deleted file mode 100644
index 819ab25..0000000
--- a/bp2build/ndk_library_conversion_test.go
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2022 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 (
-	"testing"
-
-	"android/soong/cc"
-)
-
-func TestNdkLibraryContributionSymbolFile(t *testing.T) {
-	bp := `
-	ndk_library {
-		name: "libfoo",
-		symbol_file: "libfoo.map.txt",
-	}
-	`
-	expectedBazelTarget := MakeBazelTargetNoRestrictions(
-		"cc_api_contribution",
-		"libfoo.ndk.contribution",
-		AttrNameToString{
-			"api":                    `"libfoo.map.txt"`,
-			"api_surfaces":           `["publicapi"]`,
-			"library_name":           `"libfoo"`,
-			"target_compatible_with": `["//build/bazel/platforms/os:android"]`,
-		},
-	)
-	RunApiBp2BuildTestCase(t, cc.RegisterNdkModuleTypes, Bp2buildTestCase{
-		Blueprint:            bp,
-		ExpectedBazelTargets: []string{expectedBazelTarget},
-	})
-}
-
-func TestNdkLibraryContributionHeaders(t *testing.T) {
-	bp := `
-	ndk_library {
-		name: "libfoo",
-		symbol_file: "libfoo.map.txt",
-		export_header_libs: ["libfoo_headers"],
-	}
-	`
-	fs := map[string]string{
-		"header_directory/Android.bp": `
-		ndk_headers {
-			name: "libfoo_headers",
-		}
-		`,
-	}
-	expectedBazelTarget := MakeBazelTargetNoRestrictions(
-		"cc_api_contribution",
-		"libfoo.ndk.contribution",
-		AttrNameToString{
-			"api":                    `"libfoo.map.txt"`,
-			"api_surfaces":           `["publicapi"]`,
-			"library_name":           `"libfoo"`,
-			"hdrs":                   `["//header_directory:libfoo_headers.contribution"]`,
-			"target_compatible_with": `["//build/bazel/platforms/os:android"]`,
-		},
-	)
-	RunApiBp2BuildTestCase(t, cc.RegisterNdkModuleTypes, Bp2buildTestCase{
-		Blueprint:            bp,
-		Filesystem:           fs,
-		ExpectedBazelTargets: []string{expectedBazelTarget},
-	})
-}
diff --git a/bp2build/package_conversion_test.go b/bp2build/package_conversion_test.go
index 3704b2d..ce848e4 100644
--- a/bp2build/package_conversion_test.go
+++ b/bp2build/package_conversion_test.go
@@ -15,9 +15,10 @@
 package bp2build
 
 import (
+	"testing"
+
 	"android/soong/android"
 	"android/soong/genrule"
-	"testing"
 )
 
 func registerDependentModules(ctx android.RegistrationContext) {
@@ -29,6 +30,7 @@
 	tests := []struct {
 		description string
 		modules     string
+		fs          map[string]string
 		expected    []ExpectedRuleTarget
 	}{
 		{
@@ -50,8 +52,8 @@
 					"package",
 					"",
 					AttrNameToString{
-						"default_applicable_licenses": `[":my_license"]`,
-						"default_visibility":          `["//visibility:public"]`,
+						"default_package_metadata": `[":my_license"]`,
+						"default_visibility":       `["//visibility:public"]`,
 					},
 					android.HostAndDeviceDefault,
 				},
@@ -67,6 +69,57 @@
 				},
 			},
 		},
+		{
+			description: "package has METADATA file",
+			fs: map[string]string{
+				"METADATA": ``,
+			},
+			modules: `
+license {
+  name: "my_license",
+  visibility: [":__subpackages__"],
+  license_kinds: ["SPDX-license-identifier-Apache-2.0"],
+  license_text: ["NOTICE"],
+}
+
+package {
+  default_applicable_licenses: ["my_license"],
+}
+`,
+			expected: []ExpectedRuleTarget{
+				{
+					"package",
+					"",
+					AttrNameToString{
+						"default_package_metadata": `[
+        ":my_license",
+        ":default_metadata_file",
+    ]`,
+						"default_visibility": `["//visibility:public"]`,
+					},
+					android.HostAndDeviceDefault,
+				},
+				{
+					"android_license",
+					"my_license",
+					AttrNameToString{
+						"license_kinds": `["SPDX-license-identifier-Apache-2.0"]`,
+						"license_text":  `"NOTICE"`,
+						"visibility":    `[":__subpackages__"]`,
+					},
+					android.HostAndDeviceDefault,
+				},
+				{
+					"filegroup",
+					"default_metadata_file",
+					AttrNameToString{
+						"applicable_licenses": `[]`,
+						"srcs":                `["METADATA"]`,
+					},
+					android.HostAndDeviceDefault,
+				},
+			},
+		},
 	}
 	for _, test := range tests {
 		expected := make([]string, 0, len(test.expected))
@@ -80,6 +133,7 @@
 				ModuleTypeUnderTestFactory: android.PackageFactory,
 				Blueprint:                  test.modules,
 				ExpectedBazelTargets:       expected,
+				Filesystem:                 test.fs,
 			})
 	}
 }
diff --git a/bp2build/platform_compat_config_conversion_test.go b/bp2build/platform_compat_config_conversion_test.go
new file mode 100644
index 0000000..4dfcce3
--- /dev/null
+++ b/bp2build/platform_compat_config_conversion_test.go
@@ -0,0 +1,53 @@
+// Copyright 2023 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 (
+	"testing"
+
+	"android/soong/android"
+	"android/soong/java"
+)
+
+func runPlatformCompatConfigTestCase(t *testing.T, tc Bp2buildTestCase) {
+	t.Helper()
+	RunBp2BuildTestCase(t, func(ctx android.RegistrationContext) {
+		ctx.RegisterModuleType("java_library", java.LibraryFactory)
+		ctx.RegisterModuleType("platform_compat_config", java.PlatformCompatConfigFactory)
+	}, tc)
+}
+
+func TestPlatformCompatConfig(t *testing.T) {
+	runPlatformCompatConfigTestCase(t, Bp2buildTestCase{
+		Description: "platform_compat_config - conversion test",
+		Blueprint: `
+		platform_compat_config {
+			name: "foo",
+			src: ":lib",
+		}`,
+		Filesystem: map[string]string{
+			"a/b/Android.bp": `
+			java_library {
+				name: "lib",
+				srcs: ["a.java"],
+			}`,
+		},
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("platform_compat_config", "foo", AttrNameToString{
+				"src": `"//a/b:lib"`,
+			}),
+		},
+	})
+}
diff --git a/bp2build/soong_config_module_type_conversion_test.go b/bp2build/soong_config_module_type_conversion_test.go
index ba42f34..813773d 100644
--- a/bp2build/soong_config_module_type_conversion_test.go
+++ b/bp2build/soong_config_module_type_conversion_test.go
@@ -200,6 +200,53 @@
 )`}})
 }
 
+func TestSoongConfigModuleType_MultipleBoolVar_PartialUseNotPanic(t *testing.T) {
+	bp := `
+soong_config_bool_variable {
+	name: "feature1",
+}
+
+soong_config_bool_variable {
+	name: "feature2",
+}
+
+soong_config_module_type {
+	name: "custom_cc_library_static",
+	module_type: "cc_library_static",
+	config_namespace: "acme",
+	variables: ["feature1", "feature2",],
+	properties: ["cflags"],
+}
+
+custom_cc_library_static {
+	name: "foo",
+	bazel_module: { bp2build_available: true },
+	host_supported: true,
+	soong_config_variables: {
+		feature1: {
+			conditions_default: {
+				cflags: ["-DDEFAULT1"],
+			},
+			cflags: ["-DFEATURE1"],
+		},
+	},
+}`
+
+	runSoongConfigModuleTypeTest(t, Bp2buildTestCase{
+		Description:                "soong config variables - used part of multiple bool variable do not panic",
+		ModuleTypeUnderTest:        "cc_library_static",
+		ModuleTypeUnderTestFactory: cc.LibraryStaticFactory,
+		Blueprint:                  bp,
+		ExpectedBazelTargets: []string{`cc_library_static(
+    name = "foo",
+    copts = select({
+        "//build/bazel/product_variables:acme__feature1": ["-DFEATURE1"],
+        "//conditions:default": ["-DDEFAULT1"],
+    }),
+    local_includes = ["."],
+)`}})
+}
+
 func TestSoongConfigModuleType_StringAndBoolVar(t *testing.T) {
 	bp := `
 soong_config_bool_variable {
@@ -1251,3 +1298,111 @@
     srcs = ["main.cc"],
 )`}})
 }
+
+func TestSoongConfigModuleType_CombinedWithArchVariantProperties(t *testing.T) {
+	bp := `
+soong_config_bool_variable {
+    name: "my_bool_variable",
+}
+
+soong_config_string_variable {
+    name: "my_string_variable",
+    values: [
+        "value1",
+        "value2",
+    ],
+}
+
+soong_config_module_type {
+    name: "special_build_cc_defaults",
+    module_type: "cc_defaults",
+    config_namespace: "my_namespace",
+    bool_variables: ["my_bool_variable"],
+    variables: ["my_string_variable"],
+    properties: ["target.android.cflags", "cflags"],
+}
+
+special_build_cc_defaults {
+    name: "sample_cc_defaults",
+    target: {
+        android: {
+            cflags: ["-DFOO"],
+        },
+    },
+    soong_config_variables: {
+        my_bool_variable: {
+            target: {
+                android: {
+                    cflags: ["-DBAR"],
+                },
+            },
+            conditions_default: {
+                target: {
+                    android: {
+                        cflags: ["-DBAZ"],
+                    },
+                },
+            },
+        },
+        my_string_variable: {
+            value1: {
+                cflags: ["-DVALUE1_NOT_ANDROID"],
+                target: {
+                    android: {
+                        cflags: ["-DVALUE1"],
+                    },
+                },
+            },
+            value2: {
+                target: {
+                    android: {
+                        cflags: ["-DVALUE2"],
+                    },
+                },
+            },
+            conditions_default: {
+                target: {
+                    android: {
+                        cflags: ["-DSTRING_VAR_CONDITIONS_DEFAULT"],
+                    },
+                },
+            },
+        },
+    },
+}
+
+cc_binary {
+    name: "my_binary",
+    srcs: ["main.cc"],
+    defaults: ["sample_cc_defaults"],
+}`
+
+	runSoongConfigModuleTypeTest(t, Bp2buildTestCase{
+		Description:                "soong config variables - generates selects for library_linking_strategy",
+		ModuleTypeUnderTest:        "cc_binary",
+		ModuleTypeUnderTestFactory: cc.BinaryFactory,
+		Blueprint:                  bp,
+		Filesystem:                 map[string]string{},
+		ExpectedBazelTargets: []string{`cc_binary(
+    name = "my_binary",
+    copts = select({
+        "//build/bazel/platforms/os:android": ["-DFOO"],
+        "//conditions:default": [],
+    }) + select({
+        "//build/bazel/product_variables:my_namespace__my_bool_variable__android": ["-DBAR"],
+        "//build/bazel/product_variables:my_namespace__my_bool_variable__conditions_default__android": ["-DBAZ"],
+        "//conditions:default": [],
+    }) + select({
+        "//build/bazel/product_variables:my_namespace__my_string_variable__value1": ["-DVALUE1_NOT_ANDROID"],
+        "//conditions:default": [],
+    }) + select({
+        "//build/bazel/product_variables:my_namespace__my_string_variable__conditions_default__android": ["-DSTRING_VAR_CONDITIONS_DEFAULT"],
+        "//build/bazel/product_variables:my_namespace__my_string_variable__value1__android": ["-DVALUE1"],
+        "//build/bazel/product_variables:my_namespace__my_string_variable__value2__android": ["-DVALUE2"],
+        "//conditions:default": [],
+    }),
+    local_includes = ["."],
+    srcs = ["main.cc"],
+    target_compatible_with = ["//build/bazel/platforms/os:android"],
+)`}})
+}
diff --git a/bp2build/testing.go b/bp2build/testing.go
index 6e919db..fd99ff0 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -317,6 +317,8 @@
 	One_to_many_prop *bool
 
 	Api *string // File describing the APIs of this module
+
+	Test_config_setting *bool // Used to test generation of config_setting targets
 }
 
 type customModule struct {
@@ -490,6 +492,27 @@
 	}
 
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs)
+
+	if proptools.Bool(m.props.Test_config_setting) {
+		m.createConfigSetting(ctx)
+	}
+
+}
+
+func (m *customModule) createConfigSetting(ctx android.TopDownMutatorContext) {
+	csa := bazel.ConfigSettingAttributes{
+		Flag_values: bazel.StringMapAttribute{
+			"//build/bazel/rules/my_string_setting": m.Name(),
+		},
+	}
+	ca := android.CommonAttributes{
+		Name: m.Name() + "_config_setting",
+	}
+	ctx.CreateBazelConfigSetting(
+		csa,
+		ca,
+		ctx.ModuleDir(),
+	)
 }
 
 var _ android.ApiProvider = (*customModule)(nil)
diff --git a/cc/Android.bp b/cc/Android.bp
index be2cc5a..f49dc1a 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -9,6 +9,7 @@
         "blueprint",
         "blueprint-pathtools",
         "soong",
+        "soong-aidl-library",
         "soong-android",
         "soong-bazel",
         "soong-cc-config",
@@ -22,7 +23,6 @@
     srcs: [
         "afdo.go",
         "fdo_profile.go",
-
         "androidmk.go",
         "api_level.go",
         "bp2build.go",
diff --git a/cc/afdo.go b/cc/afdo.go
index be4f50a..137ea97 100644
--- a/cc/afdo.go
+++ b/cc/afdo.go
@@ -34,7 +34,7 @@
 
 var afdoProfileProjectsConfigKey = android.NewOnceKey("AfdoProfileProjects")
 
-const afdoCFlagsFormat = "-funique-internal-linkage-names -fprofile-sample-accurate -fprofile-sample-use=%s"
+const afdoCFlagsFormat = "-fprofile-sample-use=%s"
 
 func recordMissingAfdoProfileFile(ctx android.BaseModuleContext, missing string) {
 	getNamedMapForConfig(ctx.Config(), modulesMissingProfileFileKey).Store(missing, true)
@@ -66,10 +66,24 @@
 // afdoEnabled returns true for binaries and shared libraries
 // that set afdo prop to True and there is a profile available
 func (afdo *afdo) afdoEnabled() bool {
-	return afdo != nil && afdo.Properties.Afdo && afdo.Properties.FdoProfilePath != nil
+	return afdo != nil && afdo.Properties.Afdo
 }
 
 func (afdo *afdo) flags(ctx ModuleContext, flags Flags) Flags {
+	if afdo.Properties.Afdo {
+		// We use `-funique-internal-linkage-names` to associate profiles to the right internal
+		// functions. This option should be used before generating a profile. Because a profile
+		// generated for a binary without unique names doesn't work well building a binary with
+		// unique names (they have different internal function names).
+		// To avoid a chicken-and-egg problem, we enable `-funique-internal-linkage-names` when
+		// `afdo=true`, whether a profile exists or not.
+		// The profile can take effect in three steps:
+		// 1. Add `afdo: true` in Android.bp, and build the binary.
+		// 2. Collect an AutoFDO profile for the binary.
+		// 3. Make the profile searchable by the build system. So it's used the next time the binary
+		//	  is built.
+		flags.Local.CFlags = append([]string{"-funique-internal-linkage-names"}, flags.Local.CFlags...)
+	}
 	if path := afdo.Properties.FdoProfilePath; path != nil {
 		// The flags are prepended to allow overriding.
 		profileUseFlag := fmt.Sprintf(afdoCFlagsFormat, *path)
@@ -129,42 +143,41 @@
 // Propagate afdo requirements down from binaries and shared libraries
 func afdoDepsMutator(mctx android.TopDownMutatorContext) {
 	if m, ok := mctx.Module().(*Module); ok && m.afdo.afdoEnabled() {
-		if path := m.afdo.Properties.FdoProfilePath; path != nil {
-			mctx.WalkDeps(func(dep android.Module, parent android.Module) bool {
-				tag := mctx.OtherModuleDependencyTag(dep)
-				libTag, isLibTag := tag.(libraryDependencyTag)
+		path := m.afdo.Properties.FdoProfilePath
+		mctx.WalkDeps(func(dep android.Module, parent android.Module) bool {
+			tag := mctx.OtherModuleDependencyTag(dep)
+			libTag, isLibTag := tag.(libraryDependencyTag)
 
-				// Do not recurse down non-static dependencies
-				if isLibTag {
-					if !libTag.static() {
-						return false
-					}
-				} else {
-					if tag != objDepTag && tag != reuseObjTag {
-						return false
-					}
+			// Do not recurse down non-static dependencies
+			if isLibTag {
+				if !libTag.static() {
+					return false
 				}
-
-				if dep, ok := dep.(*Module); ok {
-					dep.afdo.Properties.AfdoRDeps = append(
-						dep.afdo.Properties.AfdoRDeps,
-						afdoRdep{
-							VariationName: proptools.StringPtr(encodeTarget(m.Name())),
-							ProfilePath:   path,
-						},
-					)
+			} else {
+				if tag != objDepTag && tag != reuseObjTag {
+					return false
 				}
+			}
 
-				return true
-			})
-		}
+			if dep, ok := dep.(*Module); ok {
+				dep.afdo.Properties.AfdoRDeps = append(
+					dep.afdo.Properties.AfdoRDeps,
+					afdoRdep{
+						VariationName: proptools.StringPtr(encodeTarget(m.Name())),
+						ProfilePath:   path,
+					},
+				)
+			}
+
+			return true
+		})
 	}
 }
 
 // Create afdo variants for modules that need them
 func afdoMutator(mctx android.BottomUpMutatorContext) {
 	if m, ok := mctx.Module().(*Module); ok && m.afdo != nil {
-		if !m.static() && m.afdo.Properties.Afdo && m.afdo.Properties.FdoProfilePath != nil {
+		if !m.static() && m.afdo.Properties.Afdo {
 			mctx.SetDependencyVariation(encodeTarget(m.Name()))
 			return
 		}
@@ -182,7 +195,7 @@
 			//                   \                      ^
 			//                    ----------------------|
 			// We only need to create one variant per unique rdep
-			if variantNameToProfilePath[variantName] == nil {
+			if _, exists := variantNameToProfilePath[variantName]; !exists {
 				variationNames = append(variationNames, variantName)
 				variantNameToProfilePath[variantName] = afdoRDep.ProfilePath
 			}
@@ -197,6 +210,7 @@
 				variation := modules[i].(*Module)
 				variation.Properties.PreventInstall = true
 				variation.Properties.HideFromMake = true
+				variation.afdo.Properties.Afdo = true
 				variation.afdo.Properties.FdoProfilePath = variantNameToProfilePath[name]
 			}
 		}
diff --git a/cc/afdo_test.go b/cc/afdo_test.go
index 1c20bfc..b250ad1 100644
--- a/cc/afdo_test.go
+++ b/cc/afdo_test.go
@@ -379,3 +379,85 @@
 		t.Errorf("libFoo missing dependency on non-afdo variant of libBar")
 	}
 }
+
+func TestAfdoDepsWithoutProfile(t *testing.T) {
+	t.Parallel()
+	bp := `
+	cc_library_shared {
+		name: "libTest",
+		srcs: ["test.c"],
+		static_libs: ["libFoo"],
+		afdo: true,
+	}
+
+	cc_library_static {
+		name: "libFoo",
+		srcs: ["foo.c"],
+		static_libs: ["libBar"],
+	}
+
+	cc_library_static {
+		name: "libBar",
+		srcs: ["bar.c"],
+	}
+	`
+
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithFdoProfile,
+		prepareForCcTest,
+	).RunTestWithBp(t, bp)
+
+	// Even without a profile path, the afdo enabled libraries should be built with
+	// -funique-internal-linkage-names.
+	expectedCFlag := "-funique-internal-linkage-names"
+
+	libTest := result.ModuleForTests("libTest", "android_arm64_armv8-a_shared")
+	libFooAfdoVariant := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static_afdo-libTest")
+	libBarAfdoVariant := result.ModuleForTests("libBar", "android_arm64_armv8-a_static_afdo-libTest")
+
+	// Check cFlags of afdo-enabled module and the afdo-variant of its static deps
+	cFlags := libTest.Rule("cc").Args["cFlags"]
+	if !strings.Contains(cFlags, expectedCFlag) {
+		t.Errorf("Expected 'libTest' to enable afdo, but did not find %q in cflags %q", expectedCFlag, cFlags)
+	}
+
+	cFlags = libFooAfdoVariant.Rule("cc").Args["cFlags"]
+	if !strings.Contains(cFlags, expectedCFlag) {
+		t.Errorf("Expected 'libFooAfdoVariant' to enable afdo, but did not find %q in cflags %q", expectedCFlag, cFlags)
+	}
+
+	cFlags = libBarAfdoVariant.Rule("cc").Args["cFlags"]
+	if !strings.Contains(cFlags, expectedCFlag) {
+		t.Errorf("Expected 'libBarAfdoVariant' to enable afdo, but did not find %q in cflags %q", expectedCFlag, cFlags)
+	}
+	// Check dependency edge from afdo-enabled module to static deps
+	if !hasDirectDep(result, libTest.Module(), libFooAfdoVariant.Module()) {
+		t.Errorf("libTest missing dependency on afdo variant of libFoo")
+	}
+
+	if !hasDirectDep(result, libFooAfdoVariant.Module(), libBarAfdoVariant.Module()) {
+		t.Errorf("libTest missing dependency on afdo variant of libBar")
+	}
+
+	// Verify non-afdo variant exists and doesn't contain afdo
+	libFoo := result.ModuleForTests("libFoo", "android_arm64_armv8-a_static")
+	libBar := result.ModuleForTests("libBar", "android_arm64_armv8-a_static")
+
+	cFlags = libFoo.Rule("cc").Args["cFlags"]
+	if strings.Contains(cFlags, expectedCFlag) {
+		t.Errorf("Expected 'libFoo' to not enable afdo, but found %q in cflags %q", expectedCFlag, cFlags)
+	}
+	cFlags = libBar.Rule("cc").Args["cFlags"]
+	if strings.Contains(cFlags, expectedCFlag) {
+		t.Errorf("Expected 'libBar' to not enable afdo, but found %q in cflags %q", expectedCFlag, cFlags)
+	}
+
+	// Check dependency edges of static deps
+	if hasDirectDep(result, libTest.Module(), libFoo.Module()) {
+		t.Errorf("libTest should not depend on non-afdo variant of libFoo")
+	}
+
+	if !hasDirectDep(result, libFoo.Module(), libBar.Module()) {
+		t.Errorf("libFoo missing dependency on non-afdo variant of libBar")
+	}
+}
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 980dd07..ce35b5c 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -124,17 +124,14 @@
 						}
 					}
 				}
-				if c.Properties.IsSdkVariant {
+				if c.Properties.IsSdkVariant && c.Properties.SdkAndPlatformVariantVisibleToMake {
 					// Make the SDK variant uninstallable so that there are not two rules to install
 					// to the same location.
 					entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
-
-					if c.Properties.SdkAndPlatformVariantVisibleToMake {
-						// Add the unsuffixed name to SOONG_SDK_VARIANT_MODULES so that Make can rewrite
-						// dependencies to the .sdk suffix when building a module that uses the SDK.
-						entries.SetString("SOONG_SDK_VARIANT_MODULES",
-							"$(SOONG_SDK_VARIANT_MODULES) $(patsubst %.sdk,%,$(LOCAL_MODULE))")
-					}
+					// Add the unsuffixed name to SOONG_SDK_VARIANT_MODULES so that Make can rewrite
+					// dependencies to the .sdk suffix when building a module that uses the SDK.
+					entries.SetString("SOONG_SDK_VARIANT_MODULES",
+						"$(SOONG_SDK_VARIANT_MODULES) $(patsubst %.sdk,%,$(LOCAL_MODULE))")
 				}
 			},
 		},
diff --git a/cc/binary.go b/cc/binary.go
index 097f822..98b9923 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -661,7 +661,7 @@
 	// shared with cc_test
 	binaryAttrs := binaryBp2buildAttrs(ctx, m)
 
-	tags := android.ApexAvailableTags(m)
+	tags := android.ApexAvailableTagsWithoutTestApexes(ctx, m)
 	ctx.CreateBazelTargetModule(bazel.BazelTargetModuleProperties{
 		Rule_class:        "cc_binary",
 		Bzl_load_location: "//build/bazel/rules/cc:cc_binary.bzl",
diff --git a/cc/bp2build.go b/cc/bp2build.go
index 4ac1c20..1e83ca3 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -17,6 +17,7 @@
 	"fmt"
 	"path/filepath"
 	"strings"
+	"sync"
 
 	"android/soong/android"
 	"android/soong/bazel"
@@ -37,6 +38,7 @@
 	protoSrcPartition   = "proto"
 	aidlSrcPartition    = "aidl"
 	syspropSrcPartition = "sysprop"
+	yaccSrcPartition    = "yacc"
 
 	stubsSuffix = "_stub_libs_current"
 )
@@ -67,6 +69,8 @@
 
 	Apex_available []string
 
+	Features bazel.StringListAttribute
+
 	sdkAttributes
 
 	tidyAttributes
@@ -151,6 +155,7 @@
 		// know the language of these sources until the genrule is executed.
 		cppSrcPartition:     bazel.LabelPartition{Extensions: []string{".cpp", ".cc", ".cxx", ".mm"}, LabelMapper: addSuffixForFilegroup("_cpp_srcs"), Keep_remainder: true},
 		syspropSrcPartition: bazel.LabelPartition{Extensions: []string{".sysprop"}},
+		yaccSrcPartition:    bazel.LabelPartition{Extensions: []string{".y", "yy"}},
 	}
 
 	return bazel.PartitionLabelListAttribute(ctx, &srcs, labels)
@@ -226,7 +231,7 @@
 	attrs := staticOrSharedAttributes{}
 
 	setAttrs := func(axis bazel.ConfigurationAxis, config string, props StaticOrSharedProperties) {
-		attrs.Copts.SetSelectValue(axis, config, parseCommandLineFlags(props.Cflags, filterOutStdFlag))
+		attrs.Copts.SetSelectValue(axis, config, parseCommandLineFlags(props.Cflags, filterOutStdFlag, filterOutHiddenVisibility))
 		attrs.Srcs.SetSelectValue(axis, config, android.BazelLabelForModuleSrc(ctx, props.Srcs))
 		attrs.System_dynamic_deps.SetSelectValue(axis, config, bazelLabelForSharedDeps(ctx, props.System_shared_libs))
 
@@ -268,7 +273,9 @@
 	attrs.Srcs_c = partitionedSrcs[cSrcPartition]
 	attrs.Srcs_as = partitionedSrcs[asSrcPartition]
 
-	attrs.Apex_available = android.ConvertApexAvailableToTags(apexAvailable)
+	attrs.Apex_available = android.ConvertApexAvailableToTagsWithoutTestApexes(ctx.(android.TopDownMutatorContext), apexAvailable)
+
+	attrs.Features.Append(convertHiddenVisibilityToFeatureStaticOrShared(ctx, module, isStatic))
 
 	if !partitionedSrcs[protoSrcPartition].IsEmpty() {
 		// TODO(b/208815215): determine whether this is used and add support if necessary
@@ -399,6 +406,12 @@
 	// Sysprop sources
 	syspropSrcs bazel.LabelListAttribute
 
+	// Yacc sources
+	yaccSrc               *bazel.LabelAttribute
+	yaccFlags             bazel.StringListAttribute
+	yaccGenLocationHeader bazel.BoolAttribute
+	yaccGenPositionHeader bazel.BoolAttribute
+
 	hdrs bazel.LabelListAttribute
 
 	rtti bazel.BoolAttribute
@@ -421,6 +434,7 @@
 
 	features bazel.StringListAttribute
 
+	stem   bazel.StringAttribute
 	suffix bazel.StringAttribute
 
 	fdoProfile bazel.LabelAttribute
@@ -428,6 +442,12 @@
 
 type filterOutFn func(string) bool
 
+// filterOutHiddenVisibility removes the flag specifying hidden visibility as
+// this flag is converted to a toolchain feature
+func filterOutHiddenVisibility(flag string) bool {
+	return flag == config.VisibilityHiddenFlag
+}
+
 func filterOutStdFlag(flag string) bool {
 	return strings.HasPrefix(flag, "-std=")
 }
@@ -481,7 +501,7 @@
 
 	instructionSet := proptools.StringDefault(props.Instruction_set, "")
 	if instructionSet == "arm" {
-		ca.features.SetSelectValue(axis, config, []string{"arm_isa_arm", "-arm_isa_thumb"})
+		ca.features.SetSelectValue(axis, config, []string{"arm_isa_arm"})
 	} else if instructionSet != "" && instructionSet != "thumb" {
 		ctx.ModuleErrorf("Unknown value for instruction_set: %s", instructionSet)
 	}
@@ -490,7 +510,7 @@
 	// overridden. In Bazel we always allow overriding, via flags; however, this can cause
 	// incompatibilities, so we remove "-std=" flags from Cflag properties while leaving it in other
 	// cases.
-	ca.copts.SetSelectValue(axis, config, parseCommandLineFlags(props.Cflags, filterOutStdFlag, filterOutClangUnknownCflags))
+	ca.copts.SetSelectValue(axis, config, parseCommandLineFlags(props.Cflags, filterOutStdFlag, filterOutClangUnknownCflags, filterOutHiddenVisibility))
 	ca.asFlags.SetSelectValue(axis, config, parseCommandLineFlags(props.Asflags, nil))
 	ca.conlyFlags.SetSelectValue(axis, config, parseCommandLineFlags(props.Conlyflags, filterOutClangUnknownCflags))
 	ca.cppFlags.SetSelectValue(axis, config, parseCommandLineFlags(props.Cppflags, filterOutClangUnknownCflags))
@@ -517,7 +537,7 @@
 	productVarPropNameToAttribute := map[string]*bazel.StringListAttribute{
 		"Cflags":   &ca.copts,
 		"Asflags":  &ca.asFlags,
-		"CppFlags": &ca.cppFlags,
+		"Cppflags": &ca.cppFlags,
 	}
 	for propName, attr := range productVarPropNameToAttribute {
 		if productConfigProps, exists := productVariableProps[propName]; exists {
@@ -526,7 +546,7 @@
 				if !ok {
 					ctx.ModuleErrorf("Could not convert product variable %s property", proptools.PropertyNameForField(propName))
 				}
-				newFlags, _ := bazel.TryVariableSubstitutions(flags, productConfigProp.Name)
+				newFlags, _ := bazel.TryVariableSubstitutions(flags, productConfigProp.Name())
 				attr.SetSelectValue(productConfigProp.ConfigurationAxis(), productConfigProp.SelectKey(), newFlags)
 			}
 		}
@@ -555,6 +575,12 @@
 	ca.asmSrcs = partitionedSrcs[asmSrcPartition]
 	ca.lSrcs = partitionedSrcs[lSrcPartition]
 	ca.llSrcs = partitionedSrcs[llSrcPartition]
+	if yacc := partitionedSrcs[yaccSrcPartition]; !yacc.IsEmpty() {
+		if len(yacc.Value.Includes) > 1 {
+			ctx.PropertyErrorf("srcs", "Found multiple yacc (.y/.yy) files in library")
+		}
+		ca.yaccSrc = bazel.MakeLabelAttribute(yacc.Value.Includes[0].Label)
+	}
 	ca.syspropSrcs = partitionedSrcs[syspropSrcPartition]
 
 	ca.absoluteIncludes.DeduplicateAxesFromBase()
@@ -566,7 +592,8 @@
 	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)
+	genSrcs, _ := android.PartitionXsdSrcs(ctx, props.Generated_sources)
+	generatedSrcsLabelList := android.BazelLabelForModuleDepsExcludes(ctx, genSrcs, props.Exclude_generated_sources)
 	if len(props.Generated_sources) > 0 || len(props.Exclude_generated_sources) > 0 {
 		anySrcs = true
 	}
@@ -691,6 +718,14 @@
 	return ret
 }
 
+// Replaces //a/b/my_xsd_config with //a/b/my_xsd_config-cpp
+func xsdConfigCppTarget(ctx android.BazelConversionPathContext, mod blueprint.Module) string {
+	callback := func(xsd android.XsdConfigBp2buildTargets) string {
+		return xsd.CppBp2buildTargetName()
+	}
+	return android.XsdConfigBp2buildTarget(ctx, mod, callback)
+}
+
 // bp2BuildParseBaseProps returns all compiler, linker, library attributes of a cc module..
 func bp2BuildParseBaseProps(ctx android.Bp2buildMutatorContext, module *Module) baseAttributes {
 	archVariantCompilerProps := module.GetArchVariantProperties(ctx, &BaseCompilerProperties{})
@@ -717,6 +752,8 @@
 	compilerAttrs := compilerAttributes{}
 	linkerAttrs := linkerAttributes{}
 
+	var aidlLibs bazel.LabelList
+
 	// Iterate through these axes in a deterministic order. This is required
 	// because processing certain dependencies may result in concatenating
 	// elements along other axes. (For example, processing NoConfig may result
@@ -727,11 +764,24 @@
 		for cfg := range configs {
 			var allHdrs []string
 			if baseCompilerProps, ok := archVariantCompilerProps[axis][cfg].(*BaseCompilerProperties); ok {
-				allHdrs = baseCompilerProps.Generated_headers
+				ah, allHdrsXsd := android.PartitionXsdSrcs(ctx, baseCompilerProps.Generated_headers)
+				allHdrs = ah
+				// in the synthetic bp2build workspace, xsd sources are compiled to a static library
+				xsdCppConfigLibraryLabels := android.BazelLabelForModuleDepsWithFn(ctx, allHdrsXsd, xsdConfigCppTarget)
+				iwad := linkerAttrs.implementationWholeArchiveDeps.SelectValue(axis, cfg)
+				(&iwad).Append(xsdCppConfigLibraryLabels)
+				linkerAttrs.implementationWholeArchiveDeps.SetSelectValue(axis, cfg, bazel.FirstUniqueBazelLabelList(iwad))
+
 				if baseCompilerProps.Lex != nil {
 					compilerAttrs.lexopts.SetSelectValue(axis, cfg, baseCompilerProps.Lex.Flags)
 				}
+				if baseCompilerProps.Yacc != nil {
+					compilerAttrs.yaccFlags.SetSelectValue(axis, cfg, baseCompilerProps.Yacc.Flags)
+					compilerAttrs.yaccGenLocationHeader.SetSelectValue(axis, cfg, baseCompilerProps.Yacc.Gen_location_hh)
+					compilerAttrs.yaccGenPositionHeader.SetSelectValue(axis, cfg, baseCompilerProps.Yacc.Gen_position_hh)
+				}
 				(&compilerAttrs).bp2buildForAxisAndConfig(ctx, axis, cfg, baseCompilerProps)
+				aidlLibs.Append(android.BazelLabelForModuleDeps(ctx, baseCompilerProps.Aidl.Libs))
 			}
 
 			var exportHdrs []string
@@ -762,10 +812,16 @@
 
 			if libraryProps, ok := archVariantLibraryProperties[axis][cfg].(*LibraryProperties); ok {
 				if axis == bazel.NoConfigAxis {
-					versions := android.CopyOf(libraryProps.Stubs.Versions)
-					normalizeVersions(ctx, versions)
-					compilerAttrs.stubsSymbolFile = libraryProps.Stubs.Symbol_file
-					compilerAttrs.stubsVersions.SetSelectValue(axis, cfg, versions)
+					if libraryProps.Stubs.Symbol_file != nil {
+						compilerAttrs.stubsSymbolFile = libraryProps.Stubs.Symbol_file
+						versions := android.CopyOf(libraryProps.Stubs.Versions)
+						normalizeVersions(ctx, versions)
+						versions = addCurrentVersionIfNotPresent(versions)
+						compilerAttrs.stubsVersions.SetSelectValue(axis, cfg, versions)
+					}
+				}
+				if stem := libraryProps.Stem; stem != nil {
+					compilerAttrs.stem.SetSelectValue(axis, cfg, stem)
 				}
 				if suffix := libraryProps.Suffix; suffix != nil {
 					compilerAttrs.suffix.SetSelectValue(axis, cfg, suffix)
@@ -801,7 +857,15 @@
 	(&linkerAttrs).wholeArchiveDeps.Add(protoDep.wholeStaticLib)
 	(&linkerAttrs).implementationWholeArchiveDeps.Add(protoDep.implementationWholeStaticLib)
 
-	aidlDep := bp2buildCcAidlLibrary(ctx, module, compilerAttrs.aidlSrcs, linkerAttrs)
+	aidlDep := bp2buildCcAidlLibrary(
+		ctx, module,
+		compilerAttrs.aidlSrcs,
+		bazel.LabelListAttribute{
+			Value: aidlLibs,
+		},
+		linkerAttrs,
+		compilerAttrs,
+	)
 	if aidlDep != nil {
 		if lib, ok := module.linker.(*libraryDecorator); ok {
 			if proptools.Bool(lib.Properties.Aidl.Export_aidl_headers) {
@@ -812,6 +876,12 @@
 		}
 	}
 
+	// Create a cc_yacc_static_library if srcs contains .y/.yy files
+	// This internal target will produce an .a file that will be statically linked to the parent library
+	if yaccDep := bp2buildCcYaccLibrary(ctx, compilerAttrs, linkerAttrs); yaccDep != nil {
+		(&linkerAttrs).implementationWholeArchiveDeps.Add(yaccDep)
+	}
+
 	convertedLSrcs := bp2BuildLex(ctx, module.Name(), compilerAttrs)
 	(&compilerAttrs).srcs.Add(&convertedLSrcs.srcName)
 	(&compilerAttrs).cSrcs.Add(&convertedLSrcs.cSrcName)
@@ -835,6 +905,7 @@
 
 	features := compilerAttrs.features.Clone().Append(linkerAttrs.features).Append(bp2buildSanitizerFeatures(ctx, module))
 	features = features.Append(bp2buildLtoFeatures(ctx, module))
+	features = features.Append(convertHiddenVisibilityToFeatureBase(ctx, module))
 	features.DeduplicateAxesFromBase()
 
 	addMuslSystemDynamicDeps(ctx, linkerAttrs)
@@ -849,6 +920,48 @@
 	}
 }
 
+type ccYaccLibraryAttributes struct {
+	Src                         bazel.LabelAttribute
+	Flags                       bazel.StringListAttribute
+	Gen_location_hh             bazel.BoolAttribute
+	Gen_position_hh             bazel.BoolAttribute
+	Local_includes              bazel.StringListAttribute
+	Implementation_deps         bazel.LabelListAttribute
+	Implementation_dynamic_deps bazel.LabelListAttribute
+}
+
+func bp2buildCcYaccLibrary(ctx android.Bp2buildMutatorContext, ca compilerAttributes, la linkerAttributes) *bazel.LabelAttribute {
+	if ca.yaccSrc == nil {
+		return nil
+	}
+	yaccLibraryLabel := ctx.Module().Name() + "_yacc"
+	ctx.CreateBazelTargetModule(
+		bazel.BazelTargetModuleProperties{
+			Rule_class:        "cc_yacc_static_library",
+			Bzl_load_location: "//build/bazel/rules/cc:cc_yacc_library.bzl",
+		},
+		android.CommonAttributes{
+			Name: yaccLibraryLabel,
+		},
+		&ccYaccLibraryAttributes{
+			Src:                         *ca.yaccSrc,
+			Flags:                       ca.yaccFlags,
+			Gen_location_hh:             ca.yaccGenLocationHeader,
+			Gen_position_hh:             ca.yaccGenPositionHeader,
+			Local_includes:              ca.localIncludes,
+			Implementation_deps:         la.implementationDeps,
+			Implementation_dynamic_deps: la.implementationDynamicDeps,
+		},
+	)
+
+	yaccLibrary := &bazel.LabelAttribute{
+		Value: &bazel.Label{
+			Label: ":" + yaccLibraryLabel,
+		},
+	}
+	return yaccLibrary
+}
+
 // As a workaround for b/261657184, we are manually adding the default value
 // of system_dynamic_deps for the linux_musl os.
 // TODO: Solve this properly
@@ -897,11 +1010,16 @@
 func bp2buildCcAidlLibrary(
 	ctx android.Bp2buildMutatorContext,
 	m *Module,
-	aidlLabelList bazel.LabelListAttribute,
+	aidlSrcs bazel.LabelListAttribute,
+	aidlLibs bazel.LabelListAttribute,
 	linkerAttrs linkerAttributes,
+	compilerAttrs compilerAttributes,
 ) *bazel.LabelAttribute {
-	if !aidlLabelList.IsEmpty() {
-		aidlLibs, aidlSrcs := aidlLabelList.Partition(func(src bazel.Label) bool {
+	var aidlLibsFromSrcs, aidlFiles bazel.LabelListAttribute
+	apexAvailableTags := android.ApexAvailableTagsWithoutTestApexes(ctx.(android.TopDownMutatorContext), ctx.Module())
+
+	if !aidlSrcs.IsEmpty() {
+		aidlLibsFromSrcs, aidlFiles = aidlSrcs.Partition(func(src bazel.Label) bool {
 			if fg, ok := android.ToFileGroupAsLibrary(ctx, src.OriginalModuleName); ok &&
 				fg.ShouldConvertToAidlLibrary(ctx) {
 				return true
@@ -909,57 +1027,71 @@
 			return false
 		})
 
-		apexAvailableTags := android.ApexAvailableTags(ctx.Module())
-		sdkAttrs := bp2BuildParseSdkAttributes(m)
-
-		if !aidlSrcs.IsEmpty() {
+		if !aidlFiles.IsEmpty() {
 			aidlLibName := m.Name() + "_aidl_library"
 			ctx.CreateBazelTargetModule(
 				bazel.BazelTargetModuleProperties{
 					Rule_class:        "aidl_library",
 					Bzl_load_location: "//build/bazel/rules/aidl:aidl_library.bzl",
 				},
-				android.CommonAttributes{Name: aidlLibName},
-				&aidlLibraryAttributes{
-					Srcs: aidlSrcs,
+				android.CommonAttributes{
+					Name: aidlLibName,
 					Tags: apexAvailableTags,
 				},
-			)
-			aidlLibs.Add(&bazel.LabelAttribute{Value: &bazel.Label{Label: ":" + aidlLibName}})
-		}
-
-		if !aidlLibs.IsEmpty() {
-			ccAidlLibrarylabel := m.Name() + "_cc_aidl_library"
-			// Since parent cc_library already has these dependencies, we can add them as implementation
-			// deps so that they don't re-export
-			implementationDeps := linkerAttrs.deps.Clone()
-			implementationDeps.Append(linkerAttrs.implementationDeps)
-			implementationDynamicDeps := linkerAttrs.dynamicDeps.Clone()
-			implementationDynamicDeps.Append(linkerAttrs.implementationDynamicDeps)
-
-			ctx.CreateBazelTargetModule(
-				bazel.BazelTargetModuleProperties{
-					Rule_class:        "cc_aidl_library",
-					Bzl_load_location: "//build/bazel/rules/cc:cc_aidl_library.bzl",
-				},
-				android.CommonAttributes{Name: ccAidlLibrarylabel},
-				&ccAidlLibraryAttributes{
-					Deps:                        aidlLibs,
-					Implementation_deps:         *implementationDeps,
-					Implementation_dynamic_deps: *implementationDynamicDeps,
-					Tags:                        apexAvailableTags,
-					sdkAttributes:               sdkAttrs,
+				&aidlLibraryAttributes{
+					Srcs: aidlFiles,
 				},
 			)
-			label := &bazel.LabelAttribute{
-				Value: &bazel.Label{
-					Label: ":" + ccAidlLibrarylabel,
-				},
-			}
-			return label
+			aidlLibsFromSrcs.Add(&bazel.LabelAttribute{Value: &bazel.Label{Label: ":" + aidlLibName}})
 		}
 	}
 
+	allAidlLibs := aidlLibs.Clone()
+	allAidlLibs.Append(aidlLibsFromSrcs)
+
+	if !allAidlLibs.IsEmpty() {
+		ccAidlLibrarylabel := m.Name() + "_cc_aidl_library"
+		// Since parent cc_library already has these dependencies, we can add them as implementation
+		// deps so that they don't re-export
+		implementationDeps := linkerAttrs.deps.Clone()
+		implementationDeps.Append(linkerAttrs.implementationDeps)
+		implementationDynamicDeps := linkerAttrs.dynamicDeps.Clone()
+		implementationDynamicDeps.Append(linkerAttrs.implementationDynamicDeps)
+
+		sdkAttrs := bp2BuildParseSdkAttributes(m)
+
+		exportedIncludes := bp2BuildParseExportedIncludes(ctx, m, &compilerAttrs.includes)
+		includeAttrs := includesAttributes{
+			Export_includes:          exportedIncludes.Includes,
+			Export_absolute_includes: exportedIncludes.AbsoluteIncludes,
+			Export_system_includes:   exportedIncludes.SystemIncludes,
+			Local_includes:           compilerAttrs.localIncludes,
+			Absolute_includes:        compilerAttrs.absoluteIncludes,
+		}
+
+		ctx.CreateBazelTargetModule(
+			bazel.BazelTargetModuleProperties{
+				Rule_class:        "cc_aidl_library",
+				Bzl_load_location: "//build/bazel/rules/cc:cc_aidl_library.bzl",
+			},
+			android.CommonAttributes{Name: ccAidlLibrarylabel},
+			&ccAidlLibraryAttributes{
+				Deps:                        *allAidlLibs,
+				Implementation_deps:         *implementationDeps,
+				Implementation_dynamic_deps: *implementationDynamicDeps,
+				Tags:                        apexAvailableTags,
+				sdkAttributes:               sdkAttrs,
+				includesAttributes:          includeAttrs,
+			},
+		)
+		label := &bazel.LabelAttribute{
+			Value: &bazel.Label{
+				Label: ":" + ccAidlLibrarylabel,
+			},
+		}
+		return label
+	}
+
 	return nil
 }
 
@@ -1038,11 +1170,7 @@
 			_, staticLibs = android.RemoveFromList(versionLib, staticLibs)
 			// only add the dep if it is not in progress
 			if !versionLibAlreadyInDeps {
-				if isBinary {
-					wholeStaticLibs = append(wholeStaticLibs, versionLib)
-				} else {
-					la.implementationWholeArchiveDeps.SetSelectValue(axis, config, bazelLabelForWholeDepsExcludes(ctx, []string{versionLib}, props.Exclude_static_libs))
-				}
+				wholeStaticLibs = append(wholeStaticLibs, versionLib)
 			}
 		}
 	}
@@ -1109,8 +1237,11 @@
 		// having stubs or not, so Bazel select() statement can be used to choose
 		// source/stub variants of them.
 		apexAvailable := module.ApexAvailable()
-		setStubsForDynamicDeps(ctx, axis, config, apexAvailable, sharedDeps.export, &la.dynamicDeps, 0)
-		setStubsForDynamicDeps(ctx, axis, config, apexAvailable, sharedDeps.implementation, &la.implementationDynamicDeps, 1)
+		setStubsForDynamicDeps(ctx, axis, config, apexAvailable, sharedDeps.export, &la.dynamicDeps, 0, false)
+		setStubsForDynamicDeps(ctx, axis, config, apexAvailable, sharedDeps.implementation, &la.implementationDynamicDeps, 1, false)
+		if len(systemSharedLibs) > 0 {
+			setStubsForDynamicDeps(ctx, axis, config, apexAvailable, bazelLabelForSharedDeps(ctx, systemSharedLibs), &la.systemDynamicDeps, 2, true)
+		}
 	}
 
 	if !BoolDefault(props.Pack_relocations, packRelocationsDefault) {
@@ -1146,6 +1277,7 @@
 		label := android.BazelLabelForModuleSrcSingle(ctx, *props.Version_script)
 		additionalLinkerInputs.Add(&label)
 		linkerFlags = append(linkerFlags, fmt.Sprintf("-Wl,--version-script,$(location %s)", label.Label))
+		axisFeatures = append(axisFeatures, "android_cfi_exports_map")
 	}
 
 	if props.Dynamic_list != nil {
@@ -1155,6 +1287,9 @@
 	}
 
 	la.additionalLinkerInputs.SetSelectValue(axis, config, additionalLinkerInputs)
+	if axis == bazel.OsConfigurationAxis && (config == bazel.OsDarwin || config == bazel.OsLinux || config == bazel.OsWindows) {
+		linkerFlags = append(linkerFlags, props.Host_ldlibs...)
+	}
 	la.linkopts.SetSelectValue(axis, config, linkerFlags)
 
 	if axisFeatures != nil {
@@ -1179,45 +1314,185 @@
 	return !differ
 }
 
-func setStubsForDynamicDeps(ctx android.BazelConversionPathContext, axis bazel.ConfigurationAxis,
-	config string, apexAvailable []string, dynamicLibs bazel.LabelList, dynamicDeps *bazel.LabelListAttribute, ind int) {
+var (
+	apiDomainConfigSettingKey  = android.NewOnceKey("apiDomainConfigSettingKey")
+	apiDomainConfigSettingLock sync.Mutex
+)
 
-	depsWithStubs := []bazel.Label{}
-	for _, l := range dynamicLibs.Includes {
-		dep, _ := ctx.ModuleFromName(l.OriginalModuleName)
-		if d, ok := dep.(*Module); ok && d.HasStubsVariants() {
-			depApexAvailable := d.ApexAvailable()
-			if !availableToSameApexes(apexAvailable, depApexAvailable) {
-				depsWithStubs = append(depsWithStubs, l)
-			}
+func getApiDomainConfigSettingMap(config android.Config) *map[string]bool {
+	return config.Once(apiDomainConfigSettingKey, func() interface{} {
+		return &map[string]bool{}
+	}).(*map[string]bool)
+}
+
+var (
+	testApexNameToApiDomain = map[string]string{
+		"test_broken_com.android.art": "com.android.art",
+	}
+)
+
+// GetApiDomain returns the canonical name of the apex. This is synonymous to the apex_name definition.
+// https://cs.android.com/android/_/android/platform/build/soong/+/e3f0281b8897da1fe23b2f4f3a05f1dc87bcc902:apex/prebuilt.go;l=81-83;drc=2dc7244af985a6ad701b22f1271e606cabba527f;bpv=1;bpt=0
+// For test apexes, it uses a naming convention heuristic to determine the api domain.
+// TODO (b/281548611): Move this build/soong/android
+func GetApiDomain(apexName string) string {
+	if apiDomain, exists := testApexNameToApiDomain[apexName]; exists {
+		return apiDomain
+	}
+	// Remove `test_` prefix
+	return strings.TrimPrefix(apexName, "test_")
+}
+
+// Create a config setting for this apex in build/bazel/rules/apex
+// The use case for this is stub/impl selection in cc libraries
+// Long term, these config_setting(s) should be colocated with the respective apex definitions.
+// Note that this is an anti-pattern: The config_setting should be created from the apex definition
+// and not from a cc_library.
+// This anti-pattern is needed today since not all apexes have been allowlisted.
+func createInApexConfigSetting(ctx android.TopDownMutatorContext, apexName string) {
+	if apexName == android.AvailableToPlatform || apexName == android.AvailableToAnyApex {
+		// These correspond to android-non_apex and android-in_apex
+		return
+	}
+	apiDomainConfigSettingLock.Lock()
+	defer apiDomainConfigSettingLock.Unlock()
+
+	// Return if a config_setting has already been created
+	apiDomain := GetApiDomain(apexName)
+	acsm := getApiDomainConfigSettingMap(ctx.Config())
+	if _, exists := (*acsm)[apiDomain]; exists {
+		return
+	}
+	(*acsm)[apiDomain] = true
+
+	csa := bazel.ConfigSettingAttributes{
+		Flag_values: bazel.StringMapAttribute{
+			"//build/bazel/rules/apex:api_domain": apiDomain,
+		},
+		// Constraint this to android
+		Constraint_values: bazel.MakeLabelListAttribute(
+			bazel.MakeLabelList(
+				[]bazel.Label{
+					bazel.Label{Label: "//build/bazel/platforms/os:android"},
+				},
+			),
+		),
+	}
+	ca := android.CommonAttributes{
+		Name: apiDomain,
+	}
+	ctx.CreateBazelConfigSetting(
+		csa,
+		ca,
+		"build/bazel/rules/apex",
+	)
+}
+
+func inApexConfigSetting(apexAvailable string) string {
+	if apexAvailable == android.AvailableToPlatform {
+		return bazel.AndroidPlatform
+	}
+	if apexAvailable == android.AvailableToAnyApex {
+		return bazel.AndroidAndInApex
+	}
+	apiDomain := GetApiDomain(apexAvailable)
+	return "//build/bazel/rules/apex:" + apiDomain
+}
+
+// Inputs to stub vs impl selection.
+type stubSelectionInfo struct {
+	// Label of the implementation library (e.g. //bionic/libc:libc)
+	impl bazel.Label
+	// Axis containing the implementation library
+	axis bazel.ConfigurationAxis
+	// Axis key containing the implementation library
+	config string
+	// API domain of the apex
+	// For test apexes (test_com.android.foo), this will be the source apex (com.android.foo)
+	apiDomain string
+	// List of dep labels
+	dynamicDeps *bazel.LabelListAttribute
+	// Boolean value for determining if the dep is in the same api domain
+	// If false, the label will be rewritten to to the stub label
+	sameApiDomain bool
+}
+
+func useStubOrImplInApexWithName(ssi stubSelectionInfo) {
+	lib := ssi.impl
+	if !ssi.sameApiDomain {
+		lib = bazel.Label{
+			Label: apiSurfaceModuleLibCurrentPackage + strings.TrimPrefix(lib.OriginalModuleName, ":"),
 		}
 	}
-	if len(depsWithStubs) > 0 {
-		implDynamicDeps := bazel.SubtractBazelLabelList(dynamicLibs, bazel.MakeLabelList(depsWithStubs))
-		dynamicDeps.SetSelectValue(axis, config, implDynamicDeps)
+	// Create a select statement specific to this apex
+	inApexSelectValue := ssi.dynamicDeps.SelectValue(bazel.OsAndInApexAxis, inApexConfigSetting(ssi.apiDomain))
+	(&inApexSelectValue).Append(bazel.MakeLabelList([]bazel.Label{lib}))
+	ssi.dynamicDeps.SetSelectValue(bazel.OsAndInApexAxis, inApexConfigSetting(ssi.apiDomain), bazel.FirstUniqueBazelLabelList(inApexSelectValue))
+	// Delete the library from the common config for this apex
+	implDynamicDeps := ssi.dynamicDeps.SelectValue(ssi.axis, ssi.config)
+	implDynamicDeps = bazel.SubtractBazelLabelList(implDynamicDeps, bazel.MakeLabelList([]bazel.Label{ssi.impl}))
+	ssi.dynamicDeps.SetSelectValue(ssi.axis, ssi.config, implDynamicDeps)
+	if ssi.axis == bazel.NoConfigAxis {
+		// Set defaults. Defaults (i.e. host) should use impl and not stubs.
+		defaultSelectValue := ssi.dynamicDeps.SelectValue(bazel.OsAndInApexAxis, bazel.ConditionsDefaultConfigKey)
+		(&defaultSelectValue).Append(bazel.MakeLabelList([]bazel.Label{ssi.impl}))
+		ssi.dynamicDeps.SetSelectValue(bazel.OsAndInApexAxis, bazel.ConditionsDefaultConfigKey, bazel.FirstUniqueBazelLabelList(defaultSelectValue))
+	}
+}
 
-		stubLibLabels := []bazel.Label{}
-		for _, l := range depsWithStubs {
-			stubLabelInApiSurfaces := bazel.Label{
-				Label: apiSurfaceModuleLibCurrentPackage + strings.TrimPrefix(l.OriginalModuleName, ":"),
-			}
-			stubLibLabels = append(stubLibLabels, stubLabelInApiSurfaces)
+func setStubsForDynamicDeps(ctx android.BazelConversionPathContext, axis bazel.ConfigurationAxis,
+	config string, apexAvailable []string, dynamicLibs bazel.LabelList, dynamicDeps *bazel.LabelListAttribute, ind int, buildNonApexWithStubs bool) {
+
+	// Create a config_setting for each apex_available.
+	// This will be used to select impl of a dep if dep is available to the same apex.
+	for _, aa := range apexAvailable {
+		createInApexConfigSetting(ctx.(android.TopDownMutatorContext), aa)
+	}
+
+	apiDomainForSelects := []string{}
+	for _, apex := range apexAvailable {
+		apiDomainForSelects = append(apiDomainForSelects, GetApiDomain(apex))
+	}
+	// Always emit a select statement for the platform variant.
+	// This ensures that b build //foo --config=android works
+	// Soong always creates a platform variant even when the library might not be available to platform.
+	if !android.InList(android.AvailableToPlatform, apiDomainForSelects) {
+		apiDomainForSelects = append(apiDomainForSelects, android.AvailableToPlatform)
+	}
+	apiDomainForSelects = android.SortedUniqueStrings(apiDomainForSelects)
+
+	// Create a select for each apex this library could be included in.
+	for _, l := range dynamicLibs.Includes {
+		dep, _ := ctx.ModuleFromName(l.OriginalModuleName)
+		if c, ok := dep.(*Module); !ok || !c.HasStubsVariants() {
+			continue
 		}
-		inApexSelectValue := dynamicDeps.SelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndInApex)
-		nonApexSelectValue := dynamicDeps.SelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndNonApex)
-		defaultSelectValue := dynamicDeps.SelectValue(bazel.OsAndInApexAxis, bazel.ConditionsDefaultConfigKey)
-		if axis == bazel.NoConfigAxis {
-			(&inApexSelectValue).Append(bazel.MakeLabelList(stubLibLabels))
-			(&nonApexSelectValue).Append(bazel.MakeLabelList(depsWithStubs))
-			(&defaultSelectValue).Append(bazel.MakeLabelList(depsWithStubs))
-			dynamicDeps.SetSelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndInApex, bazel.FirstUniqueBazelLabelList(inApexSelectValue))
-			dynamicDeps.SetSelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndNonApex, bazel.FirstUniqueBazelLabelList(nonApexSelectValue))
-			dynamicDeps.SetSelectValue(bazel.OsAndInApexAxis, bazel.ConditionsDefaultConfigKey, bazel.FirstUniqueBazelLabelList(defaultSelectValue))
-		} else if config == bazel.OsAndroid {
-			(&inApexSelectValue).Append(bazel.MakeLabelList(stubLibLabels))
-			(&nonApexSelectValue).Append(bazel.MakeLabelList(depsWithStubs))
-			dynamicDeps.SetSelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndInApex, bazel.FirstUniqueBazelLabelList(inApexSelectValue))
-			dynamicDeps.SetSelectValue(bazel.OsAndInApexAxis, bazel.AndroidAndNonApex, bazel.FirstUniqueBazelLabelList(nonApexSelectValue))
+		// TODO (b/280339069): Decrease the verbosity of the generated BUILD files
+		for _, apiDomain := range apiDomainForSelects {
+			var sameApiDomain bool
+			if apiDomain == android.AvailableToPlatform {
+				// Platform variants in Soong use equality of apex_available for stub/impl selection.
+				// https://cs.android.com/android/_/android/platform/build/soong/+/316b0158fe57ee7764235923e7c6f3d530da39c6:cc/cc.go;l=3393-3404;drc=176271a426496fa2688efe2b40d5c74340c63375;bpv=1;bpt=0
+				// One of the factors behind this design choice is cc_test
+				// Tests only have a platform variant, and using equality of apex_available ensures
+				// that tests of an apex library gets its implementation and not stubs.
+				// TODO (b/280343104): Discuss if we can drop this special handling for platform variants.
+				sameApiDomain = availableToSameApexes(apexAvailable, dep.(*Module).ApexAvailable())
+				if linkable, ok := ctx.Module().(LinkableInterface); ok && linkable.Bootstrap() {
+					sameApiDomain = true
+				}
+			} else {
+				sameApiDomain = android.InList(apiDomain, dep.(*Module).ApexAvailable())
+			}
+			ssi := stubSelectionInfo{
+				impl:          l,
+				axis:          axis,
+				config:        config,
+				apiDomain:     apiDomain,
+				dynamicDeps:   dynamicDeps,
+				sameApiDomain: sameApiDomain,
+			}
+			useStubOrImplInApexWithName(ssi)
 		}
 	}
 }
@@ -1268,7 +1543,7 @@
 		// Collect all the configurations that an include or exclude property exists for.
 		// We want to iterate all configurations rather than either the include or exclude because, for a
 		// particular configuration, we may have either only an include or an exclude to handle.
-		productConfigProps := make(map[android.ProductConfigProperty]bool, len(props)+len(excludeProps))
+		productConfigProps := make(map[android.ProductConfigOrSoongConfigProperty]bool, len(props)+len(excludeProps))
 		for p := range props {
 			productConfigProps[p] = true
 		}
@@ -1314,7 +1589,6 @@
 		la.implementationDynamicDeps.Exclude(bazel.OsConfigurationAxis, "linux_bionic", toRemove)
 
 		la.implementationDynamicDeps.Exclude(bazel.OsAndInApexAxis, bazel.ConditionsDefaultConfigKey, toRemove)
-		la.implementationDynamicDeps.Exclude(bazel.OsAndInApexAxis, bazel.AndroidAndNonApex, toRemove)
 		stubsToRemove := make([]bazel.Label, 0, len(la.usedSystemDynamicDepAsDynamicDep))
 		for _, lib := range toRemove.Includes {
 			stubLabelInApiSurfaces := bazel.Label{
@@ -1322,7 +1596,12 @@
 			}
 			stubsToRemove = append(stubsToRemove, stubLabelInApiSurfaces)
 		}
-		la.implementationDynamicDeps.Exclude(bazel.OsAndInApexAxis, bazel.AndroidAndInApex, bazel.MakeLabelList(stubsToRemove))
+		// system libraries (e.g. libc, libm, libdl) belong the com.android.runtime api domain
+		// dedupe the stubs of these libraries from the other api domains (platform, other_apexes...)
+		for _, aa := range ctx.Module().(*Module).ApexAvailable() {
+			la.implementationDynamicDeps.Exclude(bazel.OsAndInApexAxis, inApexConfigSetting(aa), bazel.MakeLabelList(stubsToRemove))
+		}
+		la.implementationDynamicDeps.Exclude(bazel.OsAndInApexAxis, bazel.AndroidPlatform, bazel.MakeLabelList(stubsToRemove))
 	}
 
 	la.deps.ResolveExcludes()
@@ -1462,6 +1741,7 @@
 
 type binaryLinkerAttrs struct {
 	Linkshared *bool
+	Stem       bazel.StringAttribute
 	Suffix     bazel.StringAttribute
 }
 
@@ -1479,6 +1759,9 @@
 			// 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")
 		}
+		if stem := linkerProps.Stem; stem != nil {
+			attrs.Stem.SetSelectValue(axis, config, stem)
+		}
 		if suffix := linkerProps.Suffix; suffix != nil {
 			attrs.Suffix.SetSelectValue(axis, config, suffix)
 		}
@@ -1498,6 +1781,12 @@
 			for _, sanitizer := range sanitizerProps.Sanitize.Misc_undefined {
 				features = append(features, "ubsan_"+sanitizer)
 			}
+			if proptools.Bool(sanitizerProps.Sanitize.Cfi) {
+				features = append(features, "android_cfi")
+				if proptools.Bool(sanitizerProps.Sanitize.Config.Cfi_assembly_support) {
+					features = append(features, "android_cfi_assembly_support")
+				}
+			}
 			sanitizerFeatures.SetSelectValue(axis, config, features)
 		}
 	})
@@ -1549,3 +1838,38 @@
 	}
 	return ltoStringFeatures
 }
+
+func convertHiddenVisibilityToFeatureBase(ctx android.BazelConversionPathContext, m *Module) bazel.StringListAttribute {
+	visibilityHiddenFeature := bazel.StringListAttribute{}
+	bp2BuildPropParseHelper(ctx, m, &BaseCompilerProperties{}, func(axis bazel.ConfigurationAxis, configString string, props interface{}) {
+		if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
+			convertHiddenVisibilityToFeatureHelper(&visibilityHiddenFeature, axis, configString, baseCompilerProps.Cflags)
+		}
+	})
+	return visibilityHiddenFeature
+}
+
+func convertHiddenVisibilityToFeatureStaticOrShared(ctx android.BazelConversionPathContext, m *Module, isStatic bool) bazel.StringListAttribute {
+	visibilityHiddenFeature := bazel.StringListAttribute{}
+	if isStatic {
+		bp2BuildPropParseHelper(ctx, m, &StaticProperties{}, func(axis bazel.ConfigurationAxis, configString string, props interface{}) {
+			if staticProps, ok := props.(*StaticProperties); ok {
+				convertHiddenVisibilityToFeatureHelper(&visibilityHiddenFeature, axis, configString, staticProps.Static.Cflags)
+			}
+		})
+	} else {
+		bp2BuildPropParseHelper(ctx, m, &SharedProperties{}, func(axis bazel.ConfigurationAxis, configString string, props interface{}) {
+			if sharedProps, ok := props.(*SharedProperties); ok {
+				convertHiddenVisibilityToFeatureHelper(&visibilityHiddenFeature, axis, configString, sharedProps.Shared.Cflags)
+			}
+		})
+	}
+
+	return visibilityHiddenFeature
+}
+
+func convertHiddenVisibilityToFeatureHelper(feature *bazel.StringListAttribute, axis bazel.ConfigurationAxis, configString string, cflags []string) {
+	if inList(config.VisibilityHiddenFlag, cflags) {
+		feature.SetSelectValue(axis, configString, []string{"visibility_hidden"})
+	}
+}
diff --git a/cc/builder.go b/cc/builder.go
index fef00d4..f5e0dcc 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -125,6 +125,14 @@
 		},
 		"objcopyCmd", "prefix")
 
+	// Rule to run objcopy --remove-section=.llvm_addrsig on a partially linked object
+	noAddrSig = pctx.AndroidStaticRule("noAddrSig",
+		blueprint.RuleParams{
+			Command:     "rm -f ${out} && $objcopyCmd --remove-section=.llvm_addrsig ${in} ${out}",
+			CommandDeps: []string{"$objcopyCmd"},
+		},
+		"objcopyCmd")
+
 	_ = pctx.SourcePathVariable("stripPath", "build/soong/scripts/strip.sh")
 	_ = pctx.SourcePathVariable("xzCmd", "prebuilts/build-tools/${config.HostPrebuiltTag}/bin/xz")
 	_ = pctx.SourcePathVariable("createMiniDebugInfo", "prebuilts/build-tools/${config.HostPrebuiltTag}/bin/create_minidebuginfo")
@@ -1008,6 +1016,21 @@
 	})
 }
 
+// Generate a rule for running objcopy --remove-section=.llvm_addrsig on a partially linked object
+func transformObjectNoAddrSig(ctx android.ModuleContext, inputFile android.Path, outputFile android.WritablePath) {
+	objcopyCmd := "${config.ClangBin}/llvm-objcopy"
+
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        noAddrSig,
+		Description: "remove addrsig " + outputFile.Base(),
+		Output:      outputFile,
+		Input:       inputFile,
+		Args: map[string]string{
+			"objcopyCmd": objcopyCmd,
+		},
+	})
+}
+
 // Registers a build statement to invoke `strip` (to discard symbols and data from object files).
 func transformStrip(ctx android.ModuleContext, inputFile android.Path,
 	outputFile android.WritablePath, flags StripFlags) {
diff --git a/cc/cc.go b/cc/cc.go
index 4641480..f2c8525 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -27,6 +27,7 @@
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 
+	"android/soong/aidl_library"
 	"android/soong/android"
 	"android/soong/bazel/cquery"
 	"android/soong/cc/config"
@@ -82,7 +83,7 @@
 		ctx.TopDown("sabi_deps", sabiDepsMutator)
 	})
 
-	ctx.RegisterSingletonType("kythe_extract_all", kytheExtractAllFactory)
+	ctx.RegisterParallelSingletonType("kythe_extract_all", kytheExtractAllFactory)
 }
 
 // Deps is a struct containing module names of dependencies, separated by the kind of dependency.
@@ -110,6 +111,9 @@
 	// Used by DepsMutator to pass system_shared_libs information to check_elf_file.py.
 	SystemSharedLibs []string
 
+	// Used by DepMutator to pass aidl_library modules to aidl compiler
+	AidlLibs []string
+
 	// If true, statically link the unwinder into native libraries/binaries.
 	StaticUnwinderIfLegacy bool
 
@@ -182,6 +186,9 @@
 	// For Darwin builds, the path to the second architecture's output that should
 	// be combined with this architectures's output into a FAT MachO file.
 	DarwinSecondArchOutput android.OptionalPath
+
+	// Paths to direct srcs and transitive include dirs from direct aidl_library deps
+	AidlLibraryInfos []aidl_library.AidlLibraryInfo
 }
 
 // LocalOrGlobalFlags contains flags that need to have values set globally by the build system or locally by the module
@@ -517,6 +524,7 @@
 	isAfdoCompile() bool
 	isPgoCompile() bool
 	isCfi() bool
+	isFuzzer() bool
 	isNDKStubLibrary() bool
 	useClangLld(actx ModuleContext) bool
 	isForPlatform() bool
@@ -765,6 +773,7 @@
 	stubImplDepTag        = dependencyTag{name: "stub_impl"}
 	JniFuzzLibTag         = dependencyTag{name: "jni_fuzz_lib_tag"}
 	FdoProfileTag         = dependencyTag{name: "fdo_profile"}
+	aidlLibraryTag        = dependencyTag{name: "aidl_library"}
 )
 
 func IsSharedDepTag(depTag blueprint.DependencyTag) bool {
@@ -1086,7 +1095,7 @@
 	panic(fmt.Errorf("FuzzPackagedModule called on non-fuzz module: %q", c.BaseModuleName()))
 }
 
-func (c *Module) FuzzSharedLibraries() android.Paths {
+func (c *Module) FuzzSharedLibraries() android.RuleBuilderInstalls {
 	if fuzzer, ok := c.compiler.(*fuzzBinary); ok {
 		return fuzzer.sharedLibraries
 	}
@@ -1357,6 +1366,13 @@
 	return false
 }
 
+func (c *Module) isFuzzer() bool {
+	if sanitize := c.sanitize; sanitize != nil {
+		return Bool(sanitize.Properties.SanitizeMutated.Fuzzer)
+	}
+	return false
+}
+
 func (c *Module) isNDKStubLibrary() bool {
 	if _, ok := c.compiler.(*stubDecorator); ok {
 		return true
@@ -1475,7 +1491,7 @@
 func InstallToBootstrap(name string, config android.Config) bool {
 	// NOTE: also update //build/bazel/rules/apex/cc.bzl#_installed_to_bootstrap
 	// if this list is updated.
-	if name == "libclang_rt.hwasan" {
+	if name == "libclang_rt.hwasan" || name == "libc_hwasan" {
 		return true
 	}
 	return isBionic(name)
@@ -1652,6 +1668,10 @@
 	return ctx.mod.isCfi()
 }
 
+func (ctx *moduleContextImpl) isFuzzer() bool {
+	return ctx.mod.isFuzzer()
+}
+
 func (ctx *moduleContextImpl) isNDKStubLibrary() bool {
 	return ctx.mod.isNDKStubLibrary()
 }
@@ -1890,37 +1910,55 @@
 	c.bazelHandler.QueueBazelCall(ctx, c.getBazelModuleLabel(ctx))
 }
 
-var (
-	mixedBuildSupportedCcTest = []string{
-		"adbd_test",
-		"adb_crypto_test",
-		"adb_pairing_auth_test",
-		"adb_pairing_connection_test",
-		"adb_tls_connection_test",
-	}
-)
-
 // IsMixedBuildSupported returns true if the module should be analyzed by Bazel
-// in any of the --bazel-mode(s). This filters at the module level and takes
-// precedence over the allowlists in allowlists/allowlists.go.
+// in any of the --bazel-mode(s).
 func (c *Module) IsMixedBuildSupported(ctx android.BaseModuleContext) bool {
-	_, isForTesting := ctx.Config().BazelContext.(android.MockBazelContext)
-	if c.testBinary() && !android.InList(c.Name(), mixedBuildSupportedCcTest) && !isForTesting {
-		// Per-module rollout of mixed-builds for cc_test modules.
+	if !allEnabledSanitizersSupportedByBazel(ctx, c) {
+		//TODO(b/278772861) support sanitizers in Bazel rules
 		return false
 	}
-
-	// TODO(b/261058727): Remove this (enable mixed builds for modules with UBSan)
-	// Currently we can only support ubsan when minimum runtime is used.
-	return c.bazelHandler != nil && (!isUbsanEnabled(c) || c.MinimalRuntimeNeeded())
+	return c.bazelHandler != nil
 }
 
-func isUbsanEnabled(c *Module) bool {
+func allEnabledSanitizersSupportedByBazel(ctx android.BaseModuleContext, c *Module) bool {
 	if c.sanitize == nil {
-		return false
+		return true
 	}
 	sanitizeProps := &c.sanitize.Properties.SanitizeMutated
-	return Bool(sanitizeProps.Integer_overflow) || len(sanitizeProps.Misc_undefined) > 0
+
+	unsupportedSanitizers := []*bool{
+		sanitizeProps.Safestack,
+		sanitizeProps.Scudo,
+		BoolPtr(len(c.sanitize.Properties.Sanitize.Recover) > 0),
+		BoolPtr(c.sanitize.Properties.Sanitize.Blocklist != nil),
+	}
+	for _, san := range unsupportedSanitizers {
+		if Bool(san) {
+			return false
+		}
+	}
+
+	for _, san := range Sanitizers {
+		if san == intOverflow {
+			// TODO(b/261058727): enable mixed builds for all modules with UBSan
+			// Currently we can only support ubsan when minimum runtime is used.
+			ubsanEnabled := Bool(sanitizeProps.Integer_overflow) || len(sanitizeProps.Misc_undefined) > 0
+			if !ubsanEnabled || c.MinimalRuntimeNeeded() {
+				continue
+			}
+		} else if san == cfi {
+			apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+			// Only allow cfi if this is an apex variant
+			if !apexInfo.IsForPlatform() {
+				continue
+			}
+		}
+		if c.sanitize.isSanitizerEnabled(san) {
+			return false
+		}
+	}
+
+	return true
 }
 
 func GetApexConfigKey(ctx android.BaseModuleContext) *android.ApexConfigKey {
@@ -1975,6 +2013,56 @@
 	return ctx
 }
 
+// TODO (b/277651159): Remove this allowlist
+var (
+	skipStubLibraryMultipleApexViolation = map[string]bool{
+		"libclang_rt.asan":   true,
+		"libclang_rt.hwasan": true,
+		// runtime apex
+		"libc":          true,
+		"libc_hwasan":   true,
+		"libdl_android": true,
+		"libm":          true,
+		"libdl":         true,
+		// art apex
+		"libandroidio":    true,
+		"libdexfile":      true,
+		"libnativebridge": true,
+		"libnativehelper": true,
+		"libnativeloader": true,
+		"libsigchain":     true,
+	}
+)
+
+// Returns true if a stub library could be installed in multiple apexes
+func (c *Module) stubLibraryMultipleApexViolation(ctx android.ModuleContext) bool {
+	// If this is not an apex variant, no check necessary
+	if !c.InAnyApex() {
+		return false
+	}
+	// If this is not a stub library, no check necessary
+	if !c.HasStubsVariants() {
+		return false
+	}
+	// Skip the allowlist
+	// Use BaseModuleName so that this matches prebuilts.
+	if _, exists := skipStubLibraryMultipleApexViolation[c.BaseModuleName()]; exists {
+		return false
+	}
+
+	_, aaWithoutTestApexes, _ := android.ListSetDifference(c.ApexAvailable(), c.TestApexes())
+	// Stub libraries should not have more than one apex_available
+	if len(aaWithoutTestApexes) > 1 {
+		return true
+	}
+	// Stub libraries should not use the wildcard
+	if aaWithoutTestApexes[0] == android.AvailableToAnyApex {
+		return true
+	}
+	// Default: no violation
+	return false
+}
+
 func (c *Module) GenerateAndroidBuildActions(actx android.ModuleContext) {
 	// Handle the case of a test module split by `test_per_src` mutator.
 	//
@@ -2001,6 +2089,10 @@
 		return
 	}
 
+	if c.stubLibraryMultipleApexViolation(actx) {
+		actx.PropertyErrorf("apex_available",
+			"Stub libraries should have a single apex_available (test apexes excluded). Got %v", c.ApexAvailable())
+	}
 	if c.Properties.Clang != nil && *c.Properties.Clang == false {
 		ctx.PropertyErrorf("clang", "false (GCC) is no longer supported")
 	} else if c.Properties.Clang != nil && !ctx.DeviceConfig().BuildBrokenClangProperty() {
@@ -2685,6 +2777,14 @@
 		}
 	}
 
+	if len(deps.AidlLibs) > 0 {
+		actx.AddDependency(
+			c,
+			aidlLibraryTag,
+			deps.AidlLibs...,
+		)
+	}
+
 	updateImportedLibraryDependency(ctx)
 }
 
@@ -2989,6 +3089,17 @@
 			return
 		}
 
+		if depTag == aidlLibraryTag {
+			if ctx.OtherModuleHasProvider(dep, aidl_library.AidlLibraryProvider) {
+				depPaths.AidlLibraryInfos = append(
+					depPaths.AidlLibraryInfos,
+					ctx.OtherModuleProvider(
+						dep,
+						aidl_library.AidlLibraryProvider).(aidl_library.AidlLibraryInfo),
+				)
+			}
+		}
+
 		ccDep, ok := dep.(LinkableInterface)
 		if !ok {
 
@@ -3923,8 +4034,8 @@
 		// TODO(b/244431896) properly convert cc_test_library to its own macro. This
 		// will let them add implicit compile deps on gtest, for example.
 		//
-		// For now, treat them as regular shared libraries.
-		return sharedLibrary
+		// For now, treat them as regular libraries.
+		return fullLibrary
 	} else if c.CcLibrary() {
 		static := false
 		shared := false
@@ -4007,8 +4118,6 @@
 		// Aggressively generate api targets for all header modules
 		// This is necessary since the header module does not know if it is a dep of API surface stub library
 		apiLibraryHeadersBp2Build(ctx, c)
-	case ndkLibrary:
-		ndkLibraryBp2build(ctx, c)
 	}
 }
 
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 3ae4b15..701c3bb 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -24,6 +24,7 @@
 	"strings"
 	"testing"
 
+	"android/soong/aidl_library"
 	"android/soong/android"
 	"android/soong/bazel/cquery"
 )
@@ -59,7 +60,7 @@
 func mixedBuildsPrepareMutator(ctx android.BottomUpMutatorContext) {
 	if m := ctx.Module(); m.Enabled() {
 		if mixedBuildMod, ok := m.(android.MixedBuildBuildable); ok {
-			if mixedBuildMod.IsMixedBuildSupported(ctx) && android.MixedBuildsEnabled(ctx) {
+			if mixedBuildMod.IsMixedBuildSupported(ctx) && android.MixedBuildsEnabled(ctx) == android.MixedBuildEnabled {
 				mixedBuildMod.QueueBazelCall(ctx)
 			}
 		}
@@ -3114,6 +3115,11 @@
 			whole_static_libs: ["whole_static_dep"],
 			shared_libs: ["shared_dep"],
 			gtest: false,
+			sanitize: {
+				// cc_test modules default to memtag_heap: true,
+				// but this adds extra dependencies that we don't care about
+				never: true,
+			}
 		}
 		cc_binary {
 			name: "binary",
@@ -3573,9 +3579,6 @@
 }
 
 func TestStubsForLibraryInMultipleApexes(t *testing.T) {
-	// TODO(b/275313114): Test exposes non-determinism which should be corrected and the test
-	// reenabled.
-	t.Skip()
 	t.Parallel()
 	ctx := testCc(t, `
 		cc_library_shared {
@@ -3681,9 +3684,6 @@
 }
 
 func TestMixedBuildUsesStubs(t *testing.T) {
-	// TODO(b/275313114): Test exposes non-determinism which should be corrected and the test
-	// reenabled.
-	t.Skip()
 	t.Parallel()
 	bp := `
 		cc_library_shared {
@@ -4419,9 +4419,80 @@
 	}
 }
 
+func TestAidlLibraryWithHeaders(t *testing.T) {
+	t.Parallel()
+	ctx := android.GroupFixturePreparers(
+		prepareForCcTest,
+		aidl_library.PrepareForTestWithAidlLibrary,
+		android.MockFS{
+			"package_bar/Android.bp": []byte(`
+			aidl_library {
+				name: "bar",
+				srcs: ["x/y/Bar.aidl"],
+				hdrs: ["x/HeaderBar.aidl"],
+				strip_import_prefix: "x",
+			}
+			`)}.AddToFixture(),
+		android.MockFS{
+			"package_foo/Android.bp": []byte(`
+			aidl_library {
+				name: "foo",
+				srcs: ["a/b/Foo.aidl"],
+				hdrs: ["a/HeaderFoo.aidl"],
+				strip_import_prefix: "a",
+				deps: ["bar"],
+			}
+			cc_library {
+				name: "libfoo",
+				aidl: {
+					libs: ["foo"],
+				}
+			}
+			`),
+		}.AddToFixture(),
+	).RunTest(t).TestContext
+
+	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_static")
+
+	android.AssertPathsRelativeToTopEquals(
+		t,
+		"aidl headers",
+		[]string{
+			"package_bar/x/HeaderBar.aidl",
+			"package_foo/a/HeaderFoo.aidl",
+			"package_foo/a/b/Foo.aidl",
+			"out/soong/.intermediates/package_foo/libfoo/android_arm64_armv8-a_static/gen/aidl_library.sbox.textproto",
+		},
+		libfoo.Rule("aidl_library").Implicits,
+	)
+
+	manifest := android.RuleBuilderSboxProtoForTests(t, libfoo.Output("aidl_library.sbox.textproto"))
+	aidlCommand := manifest.Commands[0].GetCommand()
+
+	expectedAidlFlags := "-Ipackage_foo/a -Ipackage_bar/x"
+	if !strings.Contains(aidlCommand, expectedAidlFlags) {
+		t.Errorf("aidl command %q does not contain %q", aidlCommand, expectedAidlFlags)
+	}
+
+	outputs := strings.Join(libfoo.AllOutputs(), " ")
+
+	android.AssertStringDoesContain(t, "aidl-generated header", outputs, "gen/aidl_library/b/BpFoo.h")
+	android.AssertStringDoesContain(t, "aidl-generated header", outputs, "gen/aidl_library/b/BnFoo.h")
+	android.AssertStringDoesContain(t, "aidl-generated header", outputs, "gen/aidl_library/b/Foo.h")
+	android.AssertStringDoesContain(t, "aidl-generated cpp", outputs, "b/Foo.cpp")
+	// Confirm that the aidl header doesn't get compiled to cpp and h files
+	android.AssertStringDoesNotContain(t, "aidl-generated header", outputs, "gen/aidl_library/y/BpBar.h")
+	android.AssertStringDoesNotContain(t, "aidl-generated header", outputs, "gen/aidl_library/y/BnBar.h")
+	android.AssertStringDoesNotContain(t, "aidl-generated header", outputs, "gen/aidl_library/y/Bar.h")
+	android.AssertStringDoesNotContain(t, "aidl-generated cpp", outputs, "y/Bar.cpp")
+}
+
 func TestAidlFlagsPassedToTheAidlCompiler(t *testing.T) {
 	t.Parallel()
-	ctx := testCc(t, `
+	ctx := android.GroupFixturePreparers(
+		prepareForCcTest,
+		aidl_library.PrepareForTestWithAidlLibrary,
+	).RunTestWithBp(t, `
 		cc_library {
 			name: "libfoo",
 			srcs: ["a/Foo.aidl"],
@@ -4491,6 +4562,55 @@
 	}
 }
 
+func TestInvalidAidlProp(t *testing.T) {
+	t.Parallel()
+
+	testCases := []struct {
+		description string
+		bp          string
+	}{
+		{
+			description: "Invalid use of aidl.libs and aidl.include_dirs",
+			bp: `
+			cc_library {
+				name: "foo",
+				aidl: {
+					libs: ["foo_aidl"],
+					include_dirs: ["bar/include"],
+				}
+			}
+			`,
+		},
+		{
+			description: "Invalid use of aidl.libs and aidl.local_include_dirs",
+			bp: `
+			cc_library {
+				name: "foo",
+				aidl: {
+					libs: ["foo_aidl"],
+					local_include_dirs: ["include"],
+				}
+			}
+			`,
+		},
+	}
+
+	for _, testCase := range testCases {
+		t.Run(testCase.description, func(t *testing.T) {
+			bp := `
+			aidl_library {
+				name: "foo_aidl",
+				srcs: ["Foo.aidl"],
+			} ` + testCase.bp
+			android.GroupFixturePreparers(
+				prepareForCcTest,
+				aidl_library.PrepareForTestWithAidlLibrary.
+					ExtendWithErrorHandler(android.FixtureExpectsOneErrorPattern("For aidl headers, please only use aidl.libs prop")),
+			).RunTestWithBp(t, bp)
+		})
+	}
+}
+
 func TestMinSdkVersionInClangTriple(t *testing.T) {
 	t.Parallel()
 	ctx := testCc(t, `
@@ -4706,7 +4826,15 @@
 	})
 
 	t.Run("ensure only aidl headers are exported", func(t *testing.T) {
-		ctx := testCc(t, genRuleModules+`
+		ctx := android.GroupFixturePreparers(
+			prepareForCcTest,
+			aidl_library.PrepareForTestWithAidlLibrary,
+		).RunTestWithBp(t, `
+		aidl_library {
+			name: "libfoo_aidl",
+			srcs: ["x/y/Bar.aidl"],
+			strip_import_prefix: "x",
+		}
 		cc_library_shared {
 			name: "libfoo",
 			srcs: [
@@ -4715,25 +4843,33 @@
 				"a.proto",
 			],
 			aidl: {
+				libs: ["libfoo_aidl"],
 				export_aidl_headers: true,
 			}
 		}
-		`)
+		`).TestContext
 		foo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Module()
 		checkIncludeDirs(t, ctx, foo,
 			expectedIncludeDirs(`
 				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl
+				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl_library
 			`),
 			expectedSystemIncludeDirs(``),
 			expectedGeneratedHeaders(`
 				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl/b.h
 				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl/Bnb.h
 				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl/Bpb.h
+				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl_library/y/Bar.h
+				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl_library/y/BnBar.h
+				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl_library/y/BpBar.h
 			`),
 			expectedOrderOnlyDeps(`
 				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl/b.h
 				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl/Bnb.h
 				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl/Bpb.h
+				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl_library/y/Bar.h
+				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl_library/y/BnBar.h
+				.intermediates/libfoo/android_arm64_armv8-a_shared/gen/aidl_library/y/BpBar.h
 			`),
 		)
 	})
@@ -5228,3 +5364,256 @@
 	expectedOutputFiles := []string{"outputbase/execroot/__main__/foo.so"}
 	android.AssertDeepEquals(t, "output files", expectedOutputFiles, outputFiles.Strings())
 }
+
+func TestDisableSanitizerVariantsInMixedBuilds(t *testing.T) {
+	t.Parallel()
+	bp := `
+		cc_library_static {
+			name: "foo_ubsan_minimal",
+			srcs: ["foo.cc"],
+			bazel_module: { label: "//foo_ubsan_minimal" },
+			sanitize: {
+				all_undefined: true,
+				integer_overflow: true,
+			},
+		}
+		cc_library_static {
+			name: "foo",
+			srcs: ["foo.cc"],
+			bazel_module: { label: "//foo" },
+			sanitize: {
+				address: true,
+				hwaddress: true,
+				fuzzer: true,
+				integer_overflow: true,
+				scs: true,
+			},
+		}
+		cc_library_static {
+			name: "foo_tsan",
+			srcs: ["foo.cc"],
+			bazel_module: { label: "//foo_tsan" },
+			sanitize: {
+				thread: true,
+			},
+		}
+		cc_library_static {
+			name: "foo_cfi",
+			srcs: ["foo.cc"],
+			bazel_module: { label: "//foo_cfi" },
+			sanitize: {
+				cfi: true,
+			},
+		}
+		cc_library_static {
+			name: "foo_memtag_stack",
+			srcs: ["foo.cc"],
+			bazel_module: { label: "//foo_memtag_stack" },
+			sanitize: {
+				memtag_stack: true,
+			},
+		}
+		cc_library_static {
+			name: "foo_memtag_heap",
+			srcs: ["foo.cc"],
+			bazel_module: { label: "//foo_memtag_heap" },
+			sanitize: {
+				memtag_heap: true,
+			},
+		}
+		cc_library_static {
+			name: "foo_safestack",
+			srcs: ["foo.cc"],
+			bazel_module: { label: "//foo_safestack" },
+			sanitize: {
+				safestack: true,
+			},
+		}
+		cc_library_static {
+			name: "foo_scudo",
+			srcs: ["foo.cc"],
+			bazel_module: { label: "//foo_scudo" },
+			sanitize: {
+				scudo: true,
+			},
+		}
+	`
+	testcases := []struct {
+		name                string
+		variant             string
+		expectedOutputPaths []string
+	}{
+		{
+			name:    "foo_ubsan_minimal",
+			variant: "android_arm64_armv8-a_static_apex28",
+			expectedOutputPaths: []string{
+				"outputbase/execroot/__main__/foo_ubsan_minimal.a",
+			},
+		},
+		{
+			name:    "foo",
+			variant: "android_arm64_armv8-a_static_apex28",
+			expectedOutputPaths: []string{
+				"outputbase/execroot/__main__/foo.a",
+			},
+		},
+		{
+			name:    "foo",
+			variant: "android_arm_armv7-a-neon_static_asan_apex28",
+			expectedOutputPaths: []string{
+				"out/soong/.intermediates/foo/android_arm_armv7-a-neon_static_asan_apex28/foo.a",
+			},
+		},
+		{
+			name:    "foo",
+			variant: "android_arm64_armv8-a_static_hwasan_apex28",
+			expectedOutputPaths: []string{
+				"out/soong/.intermediates/foo/android_arm64_armv8-a_static_hwasan_apex28/foo.a",
+			},
+		},
+		{
+			name:    "foo",
+			variant: "android_arm64_armv8-a_static_fuzzer_apex28",
+			expectedOutputPaths: []string{
+				"out/soong/.intermediates/foo/android_arm64_armv8-a_static_fuzzer_apex28/foo.a",
+			},
+		},
+		{
+			name:    "foo",
+			variant: "android_arm_armv7-a-neon_static_asan_fuzzer_apex28",
+			expectedOutputPaths: []string{
+				"out/soong/.intermediates/foo/android_arm_armv7-a-neon_static_asan_fuzzer_apex28/foo.a",
+			},
+		},
+		{
+			name:    "foo",
+			variant: "android_arm64_armv8-a_static_hwasan_fuzzer_apex28",
+			expectedOutputPaths: []string{
+				"out/soong/.intermediates/foo/android_arm64_armv8-a_static_hwasan_fuzzer_apex28/foo.a",
+			},
+		},
+		{
+			name:    "foo",
+			variant: "android_arm64_armv8-a_static_scs_apex28",
+			expectedOutputPaths: []string{
+				"out/soong/.intermediates/foo/android_arm64_armv8-a_static_scs_apex28/foo.a",
+			},
+		},
+		{
+			name:    "foo",
+			variant: "android_arm64_armv8-a_static_hwasan_scs_apex28",
+			expectedOutputPaths: []string{
+				"out/soong/.intermediates/foo/android_arm64_armv8-a_static_hwasan_scs_apex28/foo.a",
+			},
+		},
+		{
+			name:    "foo",
+			variant: "android_arm64_armv8-a_static_hwasan_scs_fuzzer_apex28",
+			expectedOutputPaths: []string{
+				"out/soong/.intermediates/foo/android_arm64_armv8-a_static_hwasan_scs_fuzzer_apex28/foo.a",
+			},
+		},
+		{
+			name:    "foo_tsan",
+			variant: "android_arm64_armv8-a_static_apex28",
+			expectedOutputPaths: []string{
+				"outputbase/execroot/__main__/foo_tsan.a",
+			},
+		},
+		{
+			name:    "foo_tsan",
+			variant: "android_arm64_armv8-a_static_tsan_apex28",
+			expectedOutputPaths: []string{
+				"out/soong/.intermediates/foo_tsan/android_arm64_armv8-a_static_tsan_apex28/foo_tsan.a",
+			},
+		},
+		{
+			name:    "foo_cfi",
+			variant: "android_arm64_armv8-a_static_apex28",
+			expectedOutputPaths: []string{
+				"outputbase/execroot/__main__/foo_cfi.a",
+			},
+		},
+		{
+			name:    "foo_cfi",
+			variant: "android_arm64_armv8-a_static_cfi_apex28",
+			expectedOutputPaths: []string{
+				"outputbase/execroot/__main__/foo_cfi.a",
+			},
+		},
+		{
+			name:    "foo_memtag_stack",
+			variant: "android_arm64_armv8-a_static_apex28",
+			expectedOutputPaths: []string{
+				"out/soong/.intermediates/foo_memtag_stack/android_arm64_armv8-a_static_apex28/foo_memtag_stack.a",
+			},
+		},
+		{
+			name:    "foo_memtag_heap",
+			variant: "android_arm64_armv8-a_static_apex28",
+			expectedOutputPaths: []string{
+				"out/soong/.intermediates/foo_memtag_heap/android_arm64_armv8-a_static_apex28/foo_memtag_heap.a",
+			},
+		},
+		{
+			name:    "foo_safestack",
+			variant: "android_arm64_armv8-a_static_apex28",
+			expectedOutputPaths: []string{
+				"out/soong/.intermediates/foo_safestack/android_arm64_armv8-a_static_apex28/foo_safestack.a",
+			},
+		},
+		{
+			name:    "foo_scudo",
+			variant: "android_arm64_armv8-a_static_apex28",
+			expectedOutputPaths: []string{
+				"out/soong/.intermediates/foo_scudo/android_arm64_armv8-a_static_apex28/foo_scudo.a",
+			},
+		},
+	}
+
+	ctx := android.GroupFixturePreparers(
+		prepareForCcTest,
+		prepareForAsanTest,
+		android.FixtureRegisterWithContext(registerTestMutators),
+		android.FixtureModifyConfig(func(config android.Config) {
+			config.BazelContext = android.MockBazelContext{
+				OutputBaseDir: "outputbase",
+				LabelToCcInfo: map[string]cquery.CcInfo{
+					"//foo_ubsan_minimal": {
+						RootStaticArchives: []string{"foo_ubsan_minimal.a"},
+					},
+					"//foo": {
+						RootStaticArchives: []string{"foo.a"},
+					},
+					"//foo_tsan": {
+						RootStaticArchives: []string{"foo_tsan.a"},
+					},
+					"//foo_cfi": {
+						RootStaticArchives: []string{"foo_cfi.a"},
+					},
+					"//foo_memtag_stack": {
+						RootStaticArchives: []string{"INVALID_ARCHIVE.a"},
+					},
+					"//foo_memtag_heap": {
+						RootStaticArchives: []string{"INVALID_ARCHIVE.a"},
+					},
+					"//foo_safestack": {
+						RootStaticArchives: []string{"INVALID_ARCHIVE.a"},
+					},
+					"//foo_scudo": {
+						RootStaticArchives: []string{"INVALID_ARCHIVE.a"},
+					},
+				},
+			}
+		}),
+	).RunTestWithBp(t, bp).TestContext
+
+	for _, tc := range testcases {
+		fooMod := ctx.ModuleForTests(tc.name, tc.variant).Module()
+		outputFiles, err := fooMod.(android.OutputFileProducer).OutputFiles("")
+		if err != nil {
+			t.Errorf("Unexpected error getting cc_object outputfiles %s", err)
+		}
+		android.AssertPathsRelativeToTopEquals(t, "output files", tc.expectedOutputPaths, outputFiles)
+	}
+}
diff --git a/cc/ccdeps.go b/cc/ccdeps.go
index 75e1faf..d30abba 100644
--- a/cc/ccdeps.go
+++ b/cc/ccdeps.go
@@ -30,7 +30,7 @@
 // The info file is generated in $OUT/module_bp_cc_depend.json.
 
 func init() {
-	android.RegisterSingletonType("ccdeps_generator", ccDepsGeneratorSingleton)
+	android.RegisterParallelSingletonType("ccdeps_generator", ccDepsGeneratorSingleton)
 }
 
 func ccDepsGeneratorSingleton() android.Singleton {
diff --git a/cc/cmakelists.go b/cc/cmakelists.go
index ad130ba..0f3f02d 100644
--- a/cc/cmakelists.go
+++ b/cc/cmakelists.go
@@ -29,7 +29,7 @@
 // structure (see variable CLionOutputProjectsDirectory for root).
 
 func init() {
-	android.RegisterSingletonType("cmakelists_generator", cMakeListsGeneratorSingleton)
+	android.RegisterParallelSingletonType("cmakelists_generator", cMakeListsGeneratorSingleton)
 }
 
 func cMakeListsGeneratorSingleton() android.Singleton {
diff --git a/cc/compdb.go b/cc/compdb.go
index ea12443..617be1a 100644
--- a/cc/compdb.go
+++ b/cc/compdb.go
@@ -32,7 +32,7 @@
 // make SOONG_GEN_COMPDB=1 nothing to get all targets.
 
 func init() {
-	android.RegisterSingletonType("compdb_generator", compDBGeneratorSingleton)
+	android.RegisterParallelSingletonType("compdb_generator", compDBGeneratorSingleton)
 }
 
 func compDBGeneratorSingleton() android.Singleton {
diff --git a/cc/compiler.go b/cc/compiler.go
index 88985b6..16f4a6e 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -120,6 +120,9 @@
 	Lex  *LexProperties
 
 	Aidl struct {
+		// List of aidl_library modules
+		Libs []string
+
 		// list of directories that will be added to the aidl include paths.
 		Include_dirs []string
 
@@ -272,6 +275,7 @@
 	deps.GeneratedSources = append(deps.GeneratedSources, compiler.Properties.Generated_sources...)
 	deps.GeneratedSources = removeListFromList(deps.GeneratedSources, compiler.Properties.Exclude_generated_sources)
 	deps.GeneratedHeaders = append(deps.GeneratedHeaders, compiler.Properties.Generated_headers...)
+	deps.AidlLibs = append(deps.AidlLibs, compiler.Properties.Aidl.Libs...)
 
 	android.ProtoDeps(ctx, &compiler.Proto)
 	if compiler.hasSrcExt(".proto") {
@@ -561,7 +565,12 @@
 			"-I"+android.PathForModuleGen(ctx, "yacc", ctx.ModuleDir()).String())
 	}
 
-	if compiler.hasSrcExt(".aidl") {
+	if len(compiler.Properties.Aidl.Libs) > 0 &&
+		(len(compiler.Properties.Aidl.Include_dirs) > 0 || len(compiler.Properties.Aidl.Local_include_dirs) > 0) {
+		ctx.ModuleErrorf("aidl.libs and (aidl.include_dirs or aidl.local_include_dirs) can't be set at the same time. For aidl headers, please only use aidl.libs prop")
+	}
+
+	if compiler.hasAidl(deps) {
 		flags.aidlFlags = append(flags.aidlFlags, compiler.Properties.Aidl.Flags...)
 		if len(compiler.Properties.Aidl.Local_include_dirs) > 0 {
 			localAidlIncludeDirs := android.PathsForModuleSrc(ctx, compiler.Properties.Aidl.Local_include_dirs)
@@ -572,6 +581,14 @@
 			flags.aidlFlags = append(flags.aidlFlags, includeDirsToFlags(rootAidlIncludeDirs))
 		}
 
+		var rootAidlIncludeDirs android.Paths
+		for _, aidlLibraryInfo := range deps.AidlLibraryInfos {
+			rootAidlIncludeDirs = append(rootAidlIncludeDirs, aidlLibraryInfo.IncludeDirs.ToList()...)
+		}
+		if len(rootAidlIncludeDirs) > 0 {
+			flags.aidlFlags = append(flags.aidlFlags, includeDirsToFlags(rootAidlIncludeDirs))
+		}
+
 		if proptools.BoolDefault(compiler.Properties.Aidl.Generate_traces, true) {
 			flags.aidlFlags = append(flags.aidlFlags, "-t")
 		}
@@ -582,8 +599,14 @@
 		}
 		flags.aidlFlags = append(flags.aidlFlags, "--min_sdk_version="+aidlMinSdkVersion)
 
-		flags.Local.CommonFlags = append(flags.Local.CommonFlags,
-			"-I"+android.PathForModuleGen(ctx, "aidl").String())
+		if compiler.hasSrcExt(".aidl") {
+			flags.Local.CommonFlags = append(flags.Local.CommonFlags,
+				"-I"+android.PathForModuleGen(ctx, "aidl").String())
+		}
+		if len(deps.AidlLibraryInfos) > 0 {
+			flags.Local.CommonFlags = append(flags.Local.CommonFlags,
+				"-I"+android.PathForModuleGen(ctx, "aidl_library").String())
+		}
 	}
 
 	if compiler.hasSrcExt(".rscript") || compiler.hasSrcExt(".fs") {
@@ -660,6 +683,10 @@
 	return nil
 }
 
+func (compiler *baseCompiler) hasAidl(deps PathDeps) bool {
+	return len(deps.AidlLibraryInfos) > 0 || compiler.hasSrcExt(".aidl")
+}
+
 func (compiler *baseCompiler) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
 	pathDeps := deps.GeneratedDeps
 	pathDeps = append(pathDeps, ndkPathDeps(ctx)...)
@@ -668,7 +695,7 @@
 
 	srcs := append(android.Paths(nil), compiler.srcsBeforeGen...)
 
-	srcs, genDeps, info := genSources(ctx, srcs, buildFlags)
+	srcs, genDeps, info := genSources(ctx, deps.AidlLibraryInfos, srcs, buildFlags)
 	pathDeps = append(pathDeps, genDeps...)
 
 	compiler.pathDeps = pathDeps
diff --git a/cc/config/arm64_device.go b/cc/config/arm64_device.go
index 28f3682..ca2e05f 100644
--- a/cc/config/arm64_device.go
+++ b/cc/config/arm64_device.go
@@ -53,8 +53,7 @@
 		"-Wl,-z,separate-code",
 	}
 
-	arm64Lldflags = append(arm64Ldflags,
-		"-Wl,-z,max-page-size=4096")
+	arm64Lldflags = arm64Ldflags
 
 	arm64Cppflags = []string{}
 
@@ -93,7 +92,13 @@
 
 func init() {
 	exportedVars.ExportStringListStaticVariable("Arm64Ldflags", arm64Ldflags)
-	exportedVars.ExportStringListStaticVariable("Arm64Lldflags", arm64Lldflags)
+
+	exportedVars.ExportStringList("Arm64Lldflags", arm64Lldflags)
+	pctx.VariableFunc("Arm64Lldflags", func(ctx android.PackageVarContext) string {
+		maxPageSizeFlag := "-Wl,-z,max-page-size=" + ctx.Config().MaxPageSizeSupported()
+		flags := append(arm64Lldflags, maxPageSizeFlag)
+		return strings.Join(flags, " ")
+	})
 
 	exportedVars.ExportStringListStaticVariable("Arm64Cflags", arm64Cflags)
 	exportedVars.ExportStringListStaticVariable("Arm64Cppflags", arm64Cppflags)
diff --git a/cc/config/arm_device.go b/cc/config/arm_device.go
index 0704550..dec2b45 100644
--- a/cc/config/arm_device.go
+++ b/cc/config/arm_device.go
@@ -185,7 +185,12 @@
 	exportedVars.ExportString("ArmClangTriple", clangTriple)
 
 	exportedVars.ExportStringListStaticVariable("ArmLdflags", armLdflags)
-	exportedVars.ExportStringListStaticVariable("ArmLldflags", armLldflags)
+	exportedVars.ExportStringList("ArmLldflags", armLldflags)
+	pctx.VariableFunc("ArmLldflags", func(ctx android.PackageVarContext) string {
+		maxPageSizeFlag := "-Wl,-z,max-page-size=" + ctx.Config().MaxPageSizeSupported()
+		flags := append(armLldflags, maxPageSizeFlag)
+		return strings.Join(flags, " ")
+	})
 
 	exportedVars.ExportStringListStaticVariable("ArmFixCortexA8LdFlags", armFixCortexA8LdFlags)
 	exportedVars.ExportStringListStaticVariable("ArmNoFixCortexA8LdFlags", armNoFixCortexA8LdFlags)
diff --git a/cc/config/global.go b/cc/config/global.go
index a2d5cf4..d63e324 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -111,9 +111,6 @@
 
 		// Turn off FMA which got enabled by default in clang-r445002 (http://b/218805949)
 		"-ffp-contract=off",
-
-		// Turn off stack protector check for noreturn calls. (http://b/264965700)
-		"-mllvm -disable-check-noreturn-call",
 	}
 
 	commonGlobalConlyflags = []string{}
@@ -150,9 +147,6 @@
 	commonGlobalLldflags = []string{
 		"-fuse-ld=lld",
 		"-Wl,--icf=safe",
-
-		// Turn off stack protector check for noreturn calls. (http://b/264965700)
-		"-Wl,-mllvm,-disable-check-noreturn-call",
 	}
 
 	deviceGlobalCppflags = []string{
@@ -311,7 +305,7 @@
 
 	// prebuilts/clang default settings.
 	ClangDefaultBase         = "prebuilts/clang/host"
-	ClangDefaultVersion      = "clang-r487747"
+	ClangDefaultVersion      = "clang-r487747c"
 	ClangDefaultShortVersion = "17"
 
 	// Directories with warnings from Android.bp files.
@@ -360,14 +354,14 @@
 		// Automatically initialize any uninitialized stack variables.
 		// Prefer zero-init if multiple options are set.
 		if ctx.Config().IsEnvTrue("AUTO_ZERO_INITIALIZE") {
-			flags = append(flags, "-ftrivial-auto-var-init=zero -enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang -Wno-unused-command-line-argument")
+			flags = append(flags, "-ftrivial-auto-var-init=zero")
 		} else if ctx.Config().IsEnvTrue("AUTO_PATTERN_INITIALIZE") {
 			flags = append(flags, "-ftrivial-auto-var-init=pattern")
 		} else if ctx.Config().IsEnvTrue("AUTO_UNINITIALIZE") {
 			flags = append(flags, "-ftrivial-auto-var-init=uninitialized")
 		} else {
 			// Default to zero initialization.
-			flags = append(flags, "-ftrivial-auto-var-init=zero -enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang -Wno-unused-command-line-argument")
+			flags = append(flags, "-ftrivial-auto-var-init=zero")
 		}
 
 		// Workaround for ccache with clang.
@@ -433,14 +427,19 @@
 	exportedVars.ExportStringList("CommonGlobalIncludes", commonGlobalIncludes)
 	pctx.PrefixedExistentPathsForSourcesVariable("CommonGlobalIncludes", "-I", commonGlobalIncludes)
 
+	exportedVars.ExportStringStaticVariable("CLANG_DEFAULT_VERSION", ClangDefaultVersion)
+	exportedVars.ExportStringStaticVariable("CLANG_DEFAULT_SHORT_VERSION", ClangDefaultShortVersion)
+
 	pctx.StaticVariableWithEnvOverride("ClangBase", "LLVM_PREBUILTS_BASE", ClangDefaultBase)
-	exportedVars.ExportStringStaticVariableWithEnvOverride("ClangVersion", "LLVM_PREBUILTS_VERSION", ClangDefaultVersion)
+	pctx.StaticVariableWithEnvOverride("ClangVersion", "LLVM_PREBUILTS_VERSION", ClangDefaultVersion)
 	pctx.StaticVariable("ClangPath", "${ClangBase}/${HostPrebuiltTag}/${ClangVersion}")
 	pctx.StaticVariable("ClangBin", "${ClangPath}/bin")
 
-	exportedVars.ExportStringStaticVariableWithEnvOverride("ClangShortVersion", "LLVM_RELEASE_VERSION", ClangDefaultShortVersion)
+	pctx.StaticVariableWithEnvOverride("ClangShortVersion", "LLVM_RELEASE_VERSION", ClangDefaultShortVersion)
 	pctx.StaticVariable("ClangAsanLibDir", "${ClangBase}/linux-x86/${ClangVersion}/lib/clang/${ClangShortVersion}/lib/linux")
 
+	exportedVars.ExportStringListStaticVariable("WarningAllowedProjects", WarningAllowedProjects)
+
 	// These are tied to the version of LLVM directly in external/llvm, so they might trail the host prebuilts
 	// being used for the rest of the build process.
 	pctx.SourcePathVariable("RSClangBase", "prebuilts/clang/host")
diff --git a/cc/config/riscv64_device.go b/cc/config/riscv64_device.go
index 76c8e5d..d66697c 100644
--- a/cc/config/riscv64_device.go
+++ b/cc/config/riscv64_device.go
@@ -26,19 +26,17 @@
 		// Help catch common 32/64-bit errors.
 		"-Werror=implicit-function-declaration",
 		"-fno-emulated-tls",
-		// For -fsanitize=shadow-call-stack.
-		"-ffixed-x18",
 		// A temporary fix for SExtWRemoval miscompilation bug.
 		"-mllvm",
 		"-riscv-disable-sextw-removal=true",
+		"-march=rv64gc_zbb",
 	}
 
 	riscv64ArchVariantCflags = map[string][]string{}
 
 	riscv64Ldflags = []string{
 		"-Wl,--hash-style=gnu",
-		// For -fsanitize=shadow-call-stack.
-		"-ffixed-x18",
+		"-march=rv64gc_zbb",
 	}
 
 	riscv64Lldflags = append(riscv64Ldflags,
diff --git a/cc/config/tidy.go b/cc/config/tidy.go
index d55a13d..efa4549 100644
--- a/cc/config/tidy.go
+++ b/cc/config/tidy.go
@@ -42,6 +42,8 @@
 		"-bugprone-unchecked-optional-access",
 		// http://b/265438407
 		"-misc-use-anonymous-namespace",
+		// http://b/285005947
+		"-performance-avoid-endl",
 	}
 
 	// Some clang-tidy checks are included in some tidy_checks_as_errors lists,
@@ -56,6 +58,14 @@
 		"-bugprone-signed-char-misuse",
 		// http://b/241819232
 		"-misc-const-correctness",
+		// http://b/285356805
+		"-bugprone-unsafe-functions",
+		"-cert-msc24-c",
+		"-cert-msc33-c",
+		// http://b/285356799
+		"-modernize-type-traits",
+		// http://b/285361108
+		"-readability-avoid-unconditional-preprocessor-if",
 	}
 
 	extraArgFlags = []string{
diff --git a/cc/config/toolchain.go b/cc/config/toolchain.go
index 6a10e14..a0ef575 100644
--- a/cc/config/toolchain.go
+++ b/cc/config/toolchain.go
@@ -20,6 +20,12 @@
 	"android/soong/android"
 )
 
+func init() {
+	exportedVars.ExportStringListStaticVariable("DarwinAvailableLibraries", darwinAvailableLibraries)
+	exportedVars.ExportStringListStaticVariable("LinuxAvailableLibraries", linuxAvailableLibraries)
+	exportedVars.ExportStringListStaticVariable("WindowsAvailableLibraries", windowsAvailableLibraries)
+}
+
 type toolchainFactory func(arch android.Arch) Toolchain
 
 var toolchainFactories = make(map[android.OsType]map[android.ArchType]toolchainFactory)
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 7aa8b91..c897501 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -28,7 +28,7 @@
 
 func init() {
 	android.RegisterModuleType("cc_fuzz", LibFuzzFactory)
-	android.RegisterSingletonType("cc_fuzz_packaging", fuzzPackagingFactory)
+	android.RegisterParallelSingletonType("cc_fuzz_packaging", fuzzPackagingFactory)
 }
 
 type FuzzProperties struct {
@@ -103,7 +103,7 @@
 	*baseCompiler
 	fuzzPackagedModule  fuzz.FuzzPackagedModule
 	installedSharedDeps []string
-	sharedLibraries     android.Paths
+	sharedLibraries     android.RuleBuilderInstalls
 }
 
 func (fuzz *fuzzBinary) fuzzBinary() bool {
@@ -213,19 +213,19 @@
 }
 
 func SharedLibraryInstallLocation(
-	libraryPath android.Path, isHost bool, fuzzDir string, archString string) string {
+	libraryBase string, isHost bool, fuzzDir string, archString string) string {
 	installLocation := "$(PRODUCT_OUT)/data"
 	if isHost {
 		installLocation = "$(HOST_OUT)"
 	}
 	installLocation = filepath.Join(
-		installLocation, fuzzDir, archString, "lib", libraryPath.Base())
+		installLocation, fuzzDir, archString, "lib", libraryBase)
 	return installLocation
 }
 
 // Get the device-only shared library symbols install directory.
-func SharedLibrarySymbolsInstallLocation(libraryPath android.Path, fuzzDir string, archString string) string {
-	return filepath.Join("$(PRODUCT_OUT)/symbols/data/", fuzzDir, archString, "/lib/", libraryPath.Base())
+func SharedLibrarySymbolsInstallLocation(libraryBase string, fuzzDir string, archString string) string {
+	return filepath.Join("$(PRODUCT_OUT)/symbols/data/", fuzzDir, archString, "/lib/", libraryBase)
 }
 
 func (fuzzBin *fuzzBinary) install(ctx ModuleContext, file android.Path) {
@@ -242,15 +242,16 @@
 	// Grab the list of required shared libraries.
 	fuzzBin.sharedLibraries, _ = CollectAllSharedDependencies(ctx)
 
-	for _, lib := range fuzzBin.sharedLibraries {
+	for _, ruleBuilderInstall := range fuzzBin.sharedLibraries {
+		install := ruleBuilderInstall.To
 		fuzzBin.installedSharedDeps = append(fuzzBin.installedSharedDeps,
 			SharedLibraryInstallLocation(
-				lib, ctx.Host(), installBase, ctx.Arch().ArchType.String()))
+				install, ctx.Host(), installBase, ctx.Arch().ArchType.String()))
 
 		// Also add the dependency on the shared library symbols dir.
 		if !ctx.Host() {
 			fuzzBin.installedSharedDeps = append(fuzzBin.installedSharedDeps,
-				SharedLibrarySymbolsInstallLocation(lib, installBase, ctx.Arch().ArchType.String()))
+				SharedLibrarySymbolsInstallLocation(install, installBase, ctx.Arch().ArchType.String()))
 		}
 	}
 }
@@ -397,7 +398,9 @@
 		}
 
 		hostOrTargetString := "target"
-		if ccModule.Host() {
+		if ccModule.Target().HostCross {
+			hostOrTargetString = "host_cross"
+		} else if ccModule.Host() {
 			hostOrTargetString = "host"
 		}
 
@@ -422,7 +425,7 @@
 		files = append(files, GetSharedLibsToZip(ccModule.FuzzSharedLibraries(), ccModule, &s.FuzzPackager, archString, sharedLibsInstallDirPrefix, &sharedLibraryInstalled)...)
 
 		// The executable.
-		files = append(files, fuzz.FileToZip{android.OutputFileForModule(ctx, ccModule, "unstripped"), ""})
+		files = append(files, fuzz.FileToZip{SourceFilePath: android.OutputFileForModule(ctx, ccModule, "unstripped")})
 
 		archDirs[archOs], ok = s.BuildZipFile(ctx, module, fpm, files, builder, archDir, archString, hostOrTargetString, archOs, archDirs)
 		if !ok {
@@ -453,19 +456,25 @@
 
 // GetSharedLibsToZip finds and marks all the transiently-dependent shared libraries for
 // packaging.
-func GetSharedLibsToZip(sharedLibraries android.Paths, module LinkableInterface, s *fuzz.FuzzPackager, archString string, destinationPathPrefix string, sharedLibraryInstalled *map[string]bool) []fuzz.FileToZip {
+func GetSharedLibsToZip(sharedLibraries android.RuleBuilderInstalls, module LinkableInterface, s *fuzz.FuzzPackager, archString string, destinationPathPrefix string, sharedLibraryInstalled *map[string]bool) []fuzz.FileToZip {
 	var files []fuzz.FileToZip
 
 	fuzzDir := "fuzz"
 
-	for _, library := range sharedLibraries {
-		files = append(files, fuzz.FileToZip{library, destinationPathPrefix})
+	for _, ruleBuilderInstall := range sharedLibraries {
+		library := ruleBuilderInstall.From
+		install := ruleBuilderInstall.To
+		files = append(files, fuzz.FileToZip{
+			SourceFilePath:        library,
+			DestinationPathPrefix: destinationPathPrefix,
+			DestinationPath:       install,
+		})
 
 		// For each architecture-specific shared library dependency, we need to
 		// install it to the output directory. Setup the install destination here,
 		// which will be used by $(copy-many-files) in the Make backend.
 		installDestination := SharedLibraryInstallLocation(
-			library, module.Host(), fuzzDir, archString)
+			install, module.Host(), fuzzDir, archString)
 		if (*sharedLibraryInstalled)[installDestination] {
 			continue
 		}
@@ -483,7 +492,7 @@
 		// we want symbolization tools (like `stack`) to be able to find the symbols
 		// in $ANDROID_PRODUCT_OUT/symbols automagically.
 		if !module.Host() {
-			symbolsInstallDestination := SharedLibrarySymbolsInstallLocation(library, fuzzDir, archString)
+			symbolsInstallDestination := SharedLibrarySymbolsInstallLocation(install, fuzzDir, archString)
 			symbolsInstallDestination = strings.ReplaceAll(symbolsInstallDestination, "$", "$$")
 			s.SharedLibInstallStrings = append(s.SharedLibInstallStrings,
 				library.String()+":"+symbolsInstallDestination)
@@ -497,12 +506,12 @@
 // VisitDirectDeps is used first to avoid incorrectly using the core libraries (sanitizer
 // runtimes, libc, libdl, etc.) from a dependency. This may cause issues when dependencies
 // have explicit sanitizer tags, as we may get a dependency on an unsanitized libc, etc.
-func CollectAllSharedDependencies(ctx android.ModuleContext) (android.Paths, []android.Module) {
+func CollectAllSharedDependencies(ctx android.ModuleContext) (android.RuleBuilderInstalls, []android.Module) {
 	seen := make(map[string]bool)
 	recursed := make(map[string]bool)
 	deps := []android.Module{}
 
-	var sharedLibraries android.Paths
+	var sharedLibraries android.RuleBuilderInstalls
 
 	// Enumerate the first level of dependencies, as we discard all non-library
 	// modules in the BFS loop below.
@@ -510,22 +519,36 @@
 		if !IsValidSharedDependency(dep) {
 			return
 		}
+		if !ctx.OtherModuleHasProvider(dep, SharedLibraryInfoProvider) {
+			return
+		}
 		if seen[ctx.OtherModuleName(dep)] {
 			return
 		}
 		seen[ctx.OtherModuleName(dep)] = true
 		deps = append(deps, dep)
-		sharedLibraries = append(sharedLibraries, android.OutputFileForModule(ctx, dep, "unstripped"))
+
+		sharedLibraryInfo := ctx.OtherModuleProvider(dep, SharedLibraryInfoProvider).(SharedLibraryInfo)
+		installDestination := sharedLibraryInfo.SharedLibrary.Base()
+		ruleBuilderInstall := android.RuleBuilderInstall{android.OutputFileForModule(ctx, dep, "unstripped"), installDestination}
+		sharedLibraries = append(sharedLibraries, ruleBuilderInstall)
 	})
 
 	ctx.WalkDeps(func(child, parent android.Module) bool {
 		if !IsValidSharedDependency(child) {
 			return false
 		}
+		if !ctx.OtherModuleHasProvider(child, SharedLibraryInfoProvider) {
+			return false
+		}
 		if !seen[ctx.OtherModuleName(child)] {
 			seen[ctx.OtherModuleName(child)] = true
 			deps = append(deps, child)
-			sharedLibraries = append(sharedLibraries, android.OutputFileForModule(ctx, child, "unstripped"))
+
+			sharedLibraryInfo := ctx.OtherModuleProvider(child, SharedLibraryInfoProvider).(SharedLibraryInfo)
+			installDestination := sharedLibraryInfo.SharedLibrary.Base()
+			ruleBuilderInstall := android.RuleBuilderInstall{android.OutputFileForModule(ctx, child, "unstripped"), installDestination}
+			sharedLibraries = append(sharedLibraries, ruleBuilderInstall)
 		}
 
 		if recursed[ctx.OtherModuleName(child)] {
diff --git a/cc/gen.go b/cc/gen.go
index dfbb177..b15f164 100644
--- a/cc/gen.go
+++ b/cc/gen.go
@@ -18,7 +18,9 @@
 	"path/filepath"
 	"strings"
 
+	"android/soong/aidl_library"
 	"android/soong/bazel"
+
 	"github.com/google/blueprint"
 
 	"android/soong/android"
@@ -105,7 +107,14 @@
 	return ret
 }
 
-func genAidl(ctx android.ModuleContext, rule *android.RuleBuilder, aidlFile android.Path, aidlFlags string) (cppFile android.OutputPath, headerFiles android.Paths) {
+func genAidl(
+	ctx android.ModuleContext,
+	rule *android.RuleBuilder,
+	outDirBase string,
+	aidlFile android.Path,
+	aidlHdrs android.Paths,
+	aidlFlags string,
+) (cppFile android.OutputPath, headerFiles android.Paths) {
 	aidlPackage := strings.TrimSuffix(aidlFile.Rel(), aidlFile.Base())
 	baseName := strings.TrimSuffix(aidlFile.Base(), aidlFile.Ext())
 	shortName := baseName
@@ -117,20 +126,17 @@
 		shortName = strings.TrimPrefix(baseName, "I")
 	}
 
-	outDir := android.PathForModuleGen(ctx, "aidl")
+	outDir := android.PathForModuleGen(ctx, outDirBase)
 	cppFile = outDir.Join(ctx, aidlPackage, baseName+".cpp")
 	depFile := outDir.Join(ctx, aidlPackage, baseName+".cpp.d")
 	headerI := outDir.Join(ctx, aidlPackage, baseName+".h")
 	headerBn := outDir.Join(ctx, aidlPackage, "Bn"+shortName+".h")
 	headerBp := outDir.Join(ctx, aidlPackage, "Bp"+shortName+".h")
 
-	baseDir := strings.TrimSuffix(aidlFile.String(), aidlFile.Rel())
-	if baseDir != "" {
-		aidlFlags += " -I" + baseDir
-	}
-
 	cmd := rule.Command()
 	cmd.BuiltTool("aidl-cpp").
+		// libc++ is default stl for aidl-cpp (a cc_binary_host module)
+		ImplicitTool(ctx.Config().HostCcSharedLibPath(ctx, "libc++")).
 		FlagWithDepFile("-d", depFile).
 		Flag("--ninja").
 		Flag(aidlFlags).
@@ -143,6 +149,10 @@
 			headerBp,
 		})
 
+	if aidlHdrs != nil {
+		cmd.Implicits(aidlHdrs)
+	}
+
 	return cppFile, android.Paths{
 		headerI,
 		headerBn,
@@ -282,15 +292,23 @@
 	syspropOrderOnlyDeps android.Paths
 }
 
-func genSources(ctx android.ModuleContext, srcFiles android.Paths,
-	buildFlags builderFlags) (android.Paths, android.Paths, generatedSourceInfo) {
+func genSources(
+	ctx android.ModuleContext,
+	aidlLibraryInfos []aidl_library.AidlLibraryInfo,
+	srcFiles android.Paths,
+	buildFlags builderFlags,
+) (android.Paths, android.Paths, generatedSourceInfo) {
 
 	var info generatedSourceInfo
 
 	var deps android.Paths
 	var rsFiles android.Paths
 
+	// aidlRule supports compiling aidl files from srcs prop while aidlLibraryRule supports
+	// compiling aidl files from aidl_library modules specified in aidl.libs prop.
+	// The rules are separated so that they don't wipe out the other's outputDir
 	var aidlRule *android.RuleBuilder
+	var aidlLibraryRule *android.RuleBuilder
 
 	var yaccRule_ *android.RuleBuilder
 	yaccRule := func() *android.RuleBuilder {
@@ -330,7 +348,15 @@
 				aidlRule = android.NewRuleBuilder(pctx, ctx).Sbox(android.PathForModuleGen(ctx, "aidl"),
 					android.PathForModuleGen(ctx, "aidl.sbox.textproto"))
 			}
-			cppFile, aidlHeaders := genAidl(ctx, aidlRule, srcFile, buildFlags.aidlFlags)
+			baseDir := strings.TrimSuffix(srcFile.String(), srcFile.Rel())
+			cppFile, aidlHeaders := genAidl(
+				ctx,
+				aidlRule,
+				"aidl",
+				srcFile,
+				nil,
+				buildFlags.aidlFlags+" -I"+baseDir,
+			)
 			srcFiles[i] = cppFile
 
 			info.aidlHeaders = append(info.aidlHeaders, aidlHeaders...)
@@ -352,10 +378,40 @@
 		}
 	}
 
+	for _, aidlLibraryInfo := range aidlLibraryInfos {
+		if aidlLibraryRule == nil {
+			aidlLibraryRule = android.NewRuleBuilder(pctx, ctx).Sbox(
+				android.PathForModuleGen(ctx, "aidl_library"),
+				android.PathForModuleGen(ctx, "aidl_library.sbox.textproto"),
+			).SandboxInputs()
+		}
+		for _, aidlSrc := range aidlLibraryInfo.Srcs {
+			cppFile, aidlHeaders := genAidl(
+				ctx,
+				aidlLibraryRule,
+				"aidl_library",
+				aidlSrc,
+				aidlLibraryInfo.Hdrs.ToList(),
+				buildFlags.aidlFlags,
+			)
+
+			srcFiles = append(srcFiles, cppFile)
+			info.aidlHeaders = append(info.aidlHeaders, aidlHeaders...)
+			// Use the generated headers as order only deps to ensure that they are up to date when
+			// needed.
+			// TODO: Reduce the size of the ninja file by using one order only dep for the whole rule
+			info.aidlOrderOnlyDeps = append(info.aidlOrderOnlyDeps, aidlHeaders...)
+		}
+	}
+
 	if aidlRule != nil {
 		aidlRule.Build("aidl", "gen aidl")
 	}
 
+	if aidlLibraryRule != nil {
+		aidlLibraryRule.Build("aidl_library", "gen aidl_library")
+	}
+
 	if yaccRule_ != nil {
 		yaccRule_.Build("yacc", "gen yacc")
 	}
diff --git a/cc/library.go b/cc/library.go
index a7f3a05..47df53e 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -271,7 +271,9 @@
 	Implementation_deps         bazel.LabelListAttribute
 	Implementation_dynamic_deps bazel.LabelListAttribute
 	Tags                        bazel.StringListAttribute
+
 	sdkAttributes
+	includesAttributes
 }
 
 type stripAttributes struct {
@@ -311,6 +313,11 @@
 		asFlags = bazel.MakeStringListAttribute(nil)
 	}
 
+	sharedFeatures := baseAttributes.features.Clone().Append(sharedAttrs.Features)
+	sharedFeatures.DeduplicateAxesFromBase()
+	staticFeatures := baseAttributes.features.Clone().Append(staticAttrs.Features)
+	staticFeatures.DeduplicateAxesFromBase()
+
 	staticCommonAttrs := staticOrSharedAttributes{
 		Srcs:    *srcs.Clone().Append(staticAttrs.Srcs),
 		Srcs_c:  *compilerAttrs.cSrcs.Clone().Append(staticAttrs.Srcs_c),
@@ -330,6 +337,14 @@
 		Native_coverage:                   baseAttributes.Native_coverage,
 	}
 
+	includeAttrs := includesAttributes{
+		Export_includes:          exportedIncludes.Includes,
+		Export_absolute_includes: exportedIncludes.AbsoluteIncludes,
+		Export_system_includes:   exportedIncludes.SystemIncludes,
+		Local_includes:           compilerAttrs.localIncludes,
+		Absolute_includes:        compilerAttrs.absoluteIncludes,
+	}
+
 	sharedCommonAttrs := staticOrSharedAttributes{
 		Srcs:    *srcs.Clone().Append(sharedAttrs.Srcs),
 		Srcs_c:  *compilerAttrs.cSrcs.Clone().Append(sharedAttrs.Srcs_c),
@@ -351,46 +366,39 @@
 
 	staticTargetAttrs := &bazelCcLibraryStaticAttributes{
 		staticOrSharedAttributes: staticCommonAttrs,
+		includesAttributes:       includeAttrs,
 
 		Cppflags:   compilerAttrs.cppFlags,
 		Conlyflags: compilerAttrs.conlyFlags,
 		Asflags:    asFlags,
 
-		Export_includes:          exportedIncludes.Includes,
-		Export_absolute_includes: exportedIncludes.AbsoluteIncludes,
-		Export_system_includes:   exportedIncludes.SystemIncludes,
-		Local_includes:           compilerAttrs.localIncludes,
-		Absolute_includes:        compilerAttrs.absoluteIncludes,
-		Rtti:                     compilerAttrs.rtti,
-		Stl:                      compilerAttrs.stl,
-		Cpp_std:                  compilerAttrs.cppStd,
-		C_std:                    compilerAttrs.cStd,
+		Rtti:    compilerAttrs.rtti,
+		Stl:     compilerAttrs.stl,
+		Cpp_std: compilerAttrs.cppStd,
+		C_std:   compilerAttrs.cStd,
 
-		Features: baseAttributes.features,
+		Features: *staticFeatures,
 	}
 
 	sharedTargetAttrs := &bazelCcLibrarySharedAttributes{
 		staticOrSharedAttributes: sharedCommonAttrs,
-		Cppflags:                 compilerAttrs.cppFlags,
-		Conlyflags:               compilerAttrs.conlyFlags,
-		Asflags:                  asFlags,
+		includesAttributes:       includeAttrs,
 
-		Export_includes:          exportedIncludes.Includes,
-		Export_absolute_includes: exportedIncludes.AbsoluteIncludes,
-		Export_system_includes:   exportedIncludes.SystemIncludes,
-		Local_includes:           compilerAttrs.localIncludes,
-		Absolute_includes:        compilerAttrs.absoluteIncludes,
-		Linkopts:                 linkerAttrs.linkopts,
-		Rtti:                     compilerAttrs.rtti,
-		Stl:                      compilerAttrs.stl,
-		Cpp_std:                  compilerAttrs.cppStd,
-		C_std:                    compilerAttrs.cStd,
-		Use_version_lib:          linkerAttrs.useVersionLib,
+		Cppflags:   compilerAttrs.cppFlags,
+		Conlyflags: compilerAttrs.conlyFlags,
+		Asflags:    asFlags,
+
+		Linkopts:        linkerAttrs.linkopts,
+		Rtti:            compilerAttrs.rtti,
+		Stl:             compilerAttrs.stl,
+		Cpp_std:         compilerAttrs.cppStd,
+		C_std:           compilerAttrs.cStd,
+		Use_version_lib: linkerAttrs.useVersionLib,
 
 		Additional_linker_inputs: linkerAttrs.additionalLinkerInputs,
 
 		Strip:                             stripAttrsFromLinkerAttrs(&linkerAttrs),
-		Features:                          baseAttributes.features,
+		Features:                          *sharedFeatures,
 		bazelCcHeaderAbiCheckerAttributes: bp2buildParseAbiCheckerProps(ctx, m),
 
 		Fdo_profile: compilerAttrs.fdoProfile,
@@ -400,6 +408,7 @@
 		sharedTargetAttrs.Stubs_symbol_file = compilerAttrs.stubsSymbolFile
 	}
 
+	sharedTargetAttrs.Stem = compilerAttrs.stem
 	sharedTargetAttrs.Suffix = compilerAttrs.suffix
 
 	for axis, configToProps := range m.GetArchVariantProperties(ctx, &LibraryProperties{}) {
@@ -428,11 +437,11 @@
 
 	var tagsForStaticVariant bazel.StringListAttribute
 	if compilerAttrs.stubsSymbolFile == nil && len(compilerAttrs.stubsVersions.Value) == 0 {
-		tagsForStaticVariant = android.ApexAvailableTags(m)
+		tagsForStaticVariant = android.ApexAvailableTagsWithoutTestApexes(ctx, m)
 	}
 	tagsForStaticVariant.Append(bazel.StringListAttribute{Value: staticAttrs.Apex_available})
 
-	tagsForSharedVariant := android.ApexAvailableTags(m)
+	tagsForSharedVariant := android.ApexAvailableTagsWithoutTestApexes(ctx, m)
 	tagsForSharedVariant.Append(bazel.StringListAttribute{Value: sharedAttrs.Apex_available})
 
 	ctx.CreateBazelTargetModuleWithRestrictions(staticProps,
@@ -2109,9 +2118,15 @@
 
 	// Optionally export aidl headers.
 	if Bool(library.Properties.Aidl.Export_aidl_headers) {
-		if library.baseCompiler.hasSrcExt(".aidl") {
-			dir := android.PathForModuleGen(ctx, "aidl")
-			library.reexportDirs(dir)
+		if library.baseCompiler.hasAidl(deps) {
+			if library.baseCompiler.hasSrcExt(".aidl") {
+				dir := android.PathForModuleGen(ctx, "aidl")
+				library.reexportDirs(dir)
+			}
+			if len(deps.AidlLibraryInfos) > 0 {
+				dir := android.PathForModuleGen(ctx, "aidl_library")
+				library.reexportDirs(dir)
+			}
 
 			library.reexportDeps(library.baseCompiler.aidlOrderOnlyDeps...)
 			library.addExportedGeneratedHeaders(library.baseCompiler.aidlHeaders...)
@@ -2207,7 +2222,16 @@
 func (library *libraryDecorator) installSymlinkToRuntimeApex(ctx ModuleContext, file android.Path) {
 	dir := library.baseInstaller.installDir(ctx)
 	dirOnDevice := android.InstallPathToOnDevicePath(ctx, dir)
-	target := "/" + filepath.Join("apex", "com.android.runtime", dir.Base(), "bionic", file.Base())
+	// libc_hwasan has relative_install_dir set, which would mess up the dir.Base() logic.
+	// hardcode here because it's the only target, if we have other targets that use this
+	// we can generalise this.
+	var target string
+	if ctx.baseModuleName() == "libc_hwasan" {
+		target = "/" + filepath.Join("apex", "com.android.runtime", "lib64", "bionic", "hwasan", file.Base())
+	} else {
+		base := dir.Base()
+		target = "/" + filepath.Join("apex", "com.android.runtime", base, "bionic", file.Base())
+	}
 	ctx.InstallAbsoluteSymlink(dir, file.Base(), target)
 	library.postInstallCmds = append(library.postInstallCmds, makeSymlinkCmd(dirOnDevice, file.Base(), target))
 }
@@ -2397,7 +2421,10 @@
 	}
 
 	// Future API level is implicitly added if there isn't
-	vers := library.Properties.Stubs.Versions
+	return addCurrentVersionIfNotPresent(library.Properties.Stubs.Versions)
+}
+
+func addCurrentVersionIfNotPresent(vers []string) []string {
 	if inList(android.FutureApiLevel.String(), vers) {
 		return vers
 	}
@@ -2863,6 +2890,13 @@
 	linkerAttrs := baseAttributes.linkerAttributes
 
 	exportedIncludes := bp2BuildParseExportedIncludes(ctx, module, &compilerAttrs.includes)
+	includeAttrs := includesAttributes{
+		Export_includes:          exportedIncludes.Includes,
+		Export_absolute_includes: exportedIncludes.AbsoluteIncludes,
+		Export_system_includes:   exportedIncludes.SystemIncludes,
+		Local_includes:           compilerAttrs.localIncludes,
+		Absolute_includes:        compilerAttrs.absoluteIncludes,
+	}
 
 	// Append shared/static{} stanza properties. These won't be specified on
 	// cc_library_* itself, but may be specified in cc_defaults that this module
@@ -2886,6 +2920,9 @@
 		asFlags = bazel.MakeStringListAttribute(nil)
 	}
 
+	features := baseAttributes.features.Clone().Append(libSharedOrStaticAttrs.Features)
+	features.DeduplicateAxesFromBase()
+
 	commonAttrs := staticOrSharedAttributes{
 		Srcs:    compilerAttrs.srcs,
 		Srcs_c:  compilerAttrs.cSrcs,
@@ -2917,17 +2954,13 @@
 			Cpp_std:                  compilerAttrs.cppStd,
 			C_std:                    compilerAttrs.cStd,
 
-			Export_includes:          exportedIncludes.Includes,
-			Export_absolute_includes: exportedIncludes.AbsoluteIncludes,
-			Export_system_includes:   exportedIncludes.SystemIncludes,
-			Local_includes:           compilerAttrs.localIncludes,
-			Absolute_includes:        compilerAttrs.absoluteIncludes,
+			includesAttributes: includeAttrs,
 
 			Cppflags:   compilerAttrs.cppFlags,
 			Conlyflags: compilerAttrs.conlyFlags,
 			Asflags:    asFlags,
 
-			Features: baseAttributes.features,
+			Features: *features,
 		}
 	} else {
 		commonAttrs.Dynamic_deps.Add(baseAttributes.protoDependency)
@@ -2947,17 +2980,15 @@
 			Cpp_std: compilerAttrs.cppStd,
 			C_std:   compilerAttrs.cStd,
 
-			Export_includes:          exportedIncludes.Includes,
-			Export_absolute_includes: exportedIncludes.AbsoluteIncludes,
-			Export_system_includes:   exportedIncludes.SystemIncludes,
-			Local_includes:           compilerAttrs.localIncludes,
-			Absolute_includes:        compilerAttrs.absoluteIncludes,
+			includesAttributes: includeAttrs,
+
 			Additional_linker_inputs: linkerAttrs.additionalLinkerInputs,
 
 			Strip: stripAttrsFromLinkerAttrs(&linkerAttrs),
 
-			Features: baseAttributes.features,
+			Features: *features,
 
+			Stem:   compilerAttrs.stem,
 			Suffix: compilerAttrs.suffix,
 
 			bazelCcHeaderAbiCheckerAttributes: bp2buildParseAbiCheckerProps(ctx, module),
@@ -2982,14 +3013,23 @@
 		Bzl_load_location: fmt.Sprintf("//build/bazel/rules/cc:%s.bzl", modType),
 	}
 
-	tags := android.ApexAvailableTags(module)
+	tags := android.ApexAvailableTagsWithoutTestApexes(ctx, module)
 
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name(), Tags: tags}, attrs)
 }
 
+type includesAttributes struct {
+	Export_includes          bazel.StringListAttribute
+	Export_absolute_includes bazel.StringListAttribute
+	Export_system_includes   bazel.StringListAttribute
+	Local_includes           bazel.StringListAttribute
+	Absolute_includes        bazel.StringListAttribute
+}
+
 // TODO(b/199902614): Can this be factored to share with the other Attributes?
 type bazelCcLibraryStaticAttributes struct {
 	staticOrSharedAttributes
+	includesAttributes
 
 	Use_version_lib bazel.BoolAttribute
 	Rtti            bazel.BoolAttribute
@@ -2997,12 +3037,7 @@
 	Cpp_std         *string
 	C_std           *string
 
-	Export_includes          bazel.StringListAttribute
-	Export_absolute_includes bazel.StringListAttribute
-	Export_system_includes   bazel.StringListAttribute
-	Local_includes           bazel.StringListAttribute
-	Absolute_includes        bazel.StringListAttribute
-	Hdrs                     bazel.LabelListAttribute
+	Hdrs bazel.LabelListAttribute
 
 	Cppflags   bazel.StringListAttribute
 	Conlyflags bazel.StringListAttribute
@@ -3014,6 +3049,7 @@
 // TODO(b/199902614): Can this be factored to share with the other Attributes?
 type bazelCcLibrarySharedAttributes struct {
 	staticOrSharedAttributes
+	includesAttributes
 
 	Linkopts        bazel.StringListAttribute
 	Use_version_lib bazel.BoolAttribute
@@ -3023,12 +3059,7 @@
 	Cpp_std *string
 	C_std   *string
 
-	Export_includes          bazel.StringListAttribute
-	Export_absolute_includes bazel.StringListAttribute
-	Export_system_includes   bazel.StringListAttribute
-	Local_includes           bazel.StringListAttribute
-	Absolute_includes        bazel.StringListAttribute
-	Hdrs                     bazel.LabelListAttribute
+	Hdrs bazel.LabelListAttribute
 
 	Strip                    stripAttributes
 	Additional_linker_inputs bazel.LabelListAttribute
@@ -3043,6 +3074,7 @@
 
 	Inject_bssl_hash bazel.BoolAttribute
 
+	Stem   bazel.StringAttribute
 	Suffix bazel.StringAttribute
 
 	bazelCcHeaderAbiCheckerAttributes
diff --git a/cc/library_headers.go b/cc/library_headers.go
index 1dee726..ce9c4aa 100644
--- a/cc/library_headers.go
+++ b/cc/library_headers.go
@@ -151,7 +151,7 @@
 		Bzl_load_location: "//build/bazel/rules/cc:cc_library_headers.bzl",
 	}
 
-	tags := android.ApexAvailableTags(module)
+	tags := android.ApexAvailableTagsWithoutTestApexes(ctx, module)
 
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{
 		Name: module.Name(),
diff --git a/cc/linkable.go b/cc/linkable.go
index 9578807..557f5d2 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -130,7 +130,7 @@
 
 	// FuzzSharedLibraries returns the shared library dependencies for this module.
 	// Expects that IsFuzzModule returns true.
-	FuzzSharedLibraries() android.Paths
+	FuzzSharedLibraries() android.RuleBuilderInstalls
 
 	Device() bool
 	Host() bool
diff --git a/cc/lto.go b/cc/lto.go
index 1afa1dd..878c21f 100644
--- a/cc/lto.go
+++ b/cc/lto.go
@@ -43,21 +43,15 @@
 	// referred to in blueprint files as "lto"
 	Lto struct {
 		Never *bool `android:"arch_variant"`
-		Full  *bool `android:"arch_variant"`
 		Thin  *bool `android:"arch_variant"`
 	} `android:"arch_variant"`
 
+	LtoEnabled bool `blueprint:"mutated"`
+
 	// Dep properties indicate that this module needs to be built with LTO
 	// since it is an object dependency of an LTO module.
-	FullEnabled  bool `blueprint:"mutated"`
-	ThinEnabled  bool `blueprint:"mutated"`
-	NoLtoEnabled bool `blueprint:"mutated"`
-	FullDep      bool `blueprint:"mutated"`
-	ThinDep      bool `blueprint:"mutated"`
-	NoLtoDep     bool `blueprint:"mutated"`
-
-	// Use clang lld instead of gnu ld.
-	Use_clang_lld *bool
+	LtoDep   bool `blueprint:"mutated"`
+	NoLtoDep bool `blueprint:"mutated"`
 
 	// Use -fwhole-program-vtables cflag.
 	Whole_program_vtables *bool
@@ -72,37 +66,20 @@
 }
 
 func (lto *lto) begin(ctx BaseModuleContext) {
-	if ctx.Config().IsEnvTrue("DISABLE_LTO") {
-		lto.Properties.NoLtoEnabled = true
-	}
-}
-
-func (lto *lto) useClangLld(ctx BaseModuleContext) bool {
-	if lto.Properties.Use_clang_lld != nil {
-		return Bool(lto.Properties.Use_clang_lld)
-	}
-	return true
+	lto.Properties.LtoEnabled = lto.LTO(ctx)
 }
 
 func (lto *lto) flags(ctx BaseModuleContext, flags Flags) Flags {
-	// TODO(b/131771163): Disable LTO when using explicit fuzzing configurations.
-	// LTO breaks fuzzer builds.
-	if inList("-fsanitize=fuzzer-no-link", flags.Local.CFlags) {
+	// TODO(b/131771163): CFI and Fuzzer controls LTO flags by themselves.
+	// This has be checked late because these properties can be mutated.
+	if ctx.isCfi() || ctx.isFuzzer() {
 		return flags
 	}
-
-	// TODO(b/254713216): LTO doesn't work on riscv64 yet.
-	if ctx.Arch().ArchType == android.Riscv64 {
-		return flags
-	}
-
-	if lto.LTO(ctx) {
+	if lto.Properties.LtoEnabled {
 		var ltoCFlag string
 		var ltoLdFlag string
 		if lto.ThinLTO() {
 			ltoCFlag = "-flto=thin -fsplit-lto-unit"
-		} else if lto.FullLTO() {
-			ltoCFlag = "-flto"
 		} else {
 			ltoCFlag = "-flto=thin -fsplit-lto-unit"
 			ltoLdFlag = "-Wl,--lto-O0"
@@ -117,7 +94,7 @@
 			flags.Local.CFlags = append(flags.Local.CFlags, "-fwhole-program-vtables")
 		}
 
-		if (lto.DefaultThinLTO(ctx) || lto.ThinLTO()) && ctx.Config().IsEnvTrue("USE_THINLTO_CACHE") && lto.useClangLld(ctx) {
+		if ctx.Config().IsEnvTrue("USE_THINLTO_CACHE") {
 			// Set appropriate ThinLTO cache policy
 			cacheDirFormat := "-Wl,--thinlto-cache-dir="
 			cacheDir := android.PathForOutput(ctx, "thinlto-cache").String()
@@ -140,34 +117,40 @@
 	return flags
 }
 
+// Determine which LTO mode to use for the given module.
 func (lto *lto) LTO(ctx BaseModuleContext) bool {
-	return lto.ThinLTO() || lto.FullLTO() || lto.DefaultThinLTO(ctx)
-}
-
-func (lto *lto) DefaultThinLTO(ctx BaseModuleContext) bool {
+	if lto.Never() {
+		return false
+	}
+	if ctx.Config().IsEnvTrue("DISABLE_LTO") {
+		return false
+	}
+	// Module explicitly requests for LTO.
+	if lto.ThinLTO() {
+		return true
+	}
 	// LP32 has many subtle issues and less test coverage.
-	lib32 := ctx.Arch().ArchType.Multilib == "lib32"
-	// CFI enables full LTO.
-	cfi := ctx.isCfi()
+	if ctx.Arch().ArchType.Multilib == "lib32" {
+		return false
+	}
 	// Performance and binary size are less important for host binaries and tests.
-	host := ctx.Host()
-	test := ctx.testBinary() || ctx.testLibrary()
+	if ctx.Host() || ctx.testBinary() || ctx.testLibrary() {
+		return false
+	}
 	// FIXME: ThinLTO for VNDK produces different output.
 	// b/169217596
-	vndk := ctx.isVndk()
-	return GlobalThinLTO(ctx) && !lto.Never() && !lib32 && !cfi && !host && !test && !vndk
-}
-
-func (lto *lto) FullLTO() bool {
-	return lto != nil && (proptools.Bool(lto.Properties.Lto.Full) || lto.Properties.FullEnabled)
+	if ctx.isVndk() {
+		return false
+	}
+	return GlobalThinLTO(ctx)
 }
 
 func (lto *lto) ThinLTO() bool {
-	return lto != nil && (proptools.Bool(lto.Properties.Lto.Thin) || lto.Properties.ThinEnabled)
+	return lto != nil && proptools.Bool(lto.Properties.Lto.Thin)
 }
 
 func (lto *lto) Never() bool {
-	return lto != nil && (proptools.Bool(lto.Properties.Lto.Never) || lto.Properties.NoLtoEnabled)
+	return lto != nil && proptools.Bool(lto.Properties.Lto.Never)
 }
 
 func GlobalThinLTO(ctx android.BaseModuleContext) bool {
@@ -176,14 +159,11 @@
 
 // Propagate lto requirements down from binaries
 func ltoDepsMutator(mctx android.TopDownMutatorContext) {
-	globalThinLTO := GlobalThinLTO(mctx)
+	defaultLTOMode := GlobalThinLTO(mctx)
 
 	if m, ok := mctx.Module().(*Module); ok {
-		full := m.lto.FullLTO()
-		thin := m.lto.ThinLTO()
-		never := m.lto.Never()
-		if full && thin {
-			mctx.PropertyErrorf("LTO", "FullLTO and ThinLTO are mutually exclusive")
+		if m.lto == nil || m.lto.Properties.LtoEnabled == defaultLTOMode {
+			return
 		}
 
 		mctx.WalkDeps(func(dep android.Module, parent android.Module) bool {
@@ -202,13 +182,9 @@
 			}
 
 			if dep, ok := dep.(*Module); ok {
-				if full && !dep.lto.FullLTO() {
-					dep.lto.Properties.FullDep = true
-				}
-				if !globalThinLTO && thin && !dep.lto.ThinLTO() {
-					dep.lto.Properties.ThinDep = true
-				}
-				if globalThinLTO && never && !dep.lto.Never() {
+				if m.lto.Properties.LtoEnabled {
+					dep.lto.Properties.LtoDep = true
+				} else {
 					dep.lto.Properties.NoLtoDep = true
 				}
 			}
@@ -227,28 +203,19 @@
 		// Create variations for LTO types required as static
 		// dependencies
 		variationNames := []string{""}
-		if m.lto.Properties.FullDep && !m.lto.FullLTO() {
-			variationNames = append(variationNames, "lto-full")
-		}
-		if !globalThinLTO && m.lto.Properties.ThinDep && !m.lto.ThinLTO() {
+		if m.lto.Properties.LtoDep {
 			variationNames = append(variationNames, "lto-thin")
 		}
-		if globalThinLTO && m.lto.Properties.NoLtoDep && !m.lto.Never() {
+		if m.lto.Properties.NoLtoDep {
 			variationNames = append(variationNames, "lto-none")
 		}
 
-		// Use correct dependencies if LTO property is explicitly set
-		// (mutually exclusive)
-		if m.lto.FullLTO() {
-			mctx.SetDependencyVariation("lto-full")
-		}
-		if !globalThinLTO && m.lto.ThinLTO() {
-			mctx.SetDependencyVariation("lto-thin")
-		}
-		// Never must be the last, it overrides Thin or Full.
-		if globalThinLTO && m.lto.Never() {
+		if globalThinLTO && !m.lto.Properties.LtoEnabled {
 			mctx.SetDependencyVariation("lto-none")
 		}
+		if !globalThinLTO && m.lto.Properties.LtoEnabled {
+			mctx.SetDependencyVariation("lto-thin")
+		}
 
 		if len(variationNames) > 1 {
 			modules := mctx.CreateVariations(variationNames...)
@@ -262,19 +229,15 @@
 				}
 
 				// LTO properties for dependencies
-				if name == "lto-full" {
-					variation.lto.Properties.FullEnabled = true
-				}
 				if name == "lto-thin" {
-					variation.lto.Properties.ThinEnabled = true
+					variation.lto.Properties.LtoEnabled = true
 				}
 				if name == "lto-none" {
-					variation.lto.Properties.NoLtoEnabled = true
+					variation.lto.Properties.LtoEnabled = false
 				}
 				variation.Properties.PreventInstall = true
 				variation.Properties.HideFromMake = true
-				variation.lto.Properties.FullDep = false
-				variation.lto.Properties.ThinDep = false
+				variation.lto.Properties.LtoDep = false
 				variation.lto.Properties.NoLtoDep = false
 			}
 		}
diff --git a/cc/ndk_abi.go b/cc/ndk_abi.go
index 3456c32..86166dc 100644
--- a/cc/ndk_abi.go
+++ b/cc/ndk_abi.go
@@ -19,8 +19,8 @@
 )
 
 func init() {
-	android.RegisterSingletonType("ndk_abi_dump", NdkAbiDumpSingleton)
-	android.RegisterSingletonType("ndk_abi_diff", NdkAbiDiffSingleton)
+	android.RegisterParallelSingletonType("ndk_abi_dump", NdkAbiDumpSingleton)
+	android.RegisterParallelSingletonType("ndk_abi_diff", NdkAbiDiffSingleton)
 }
 
 func getNdkAbiDumpInstallBase(ctx android.PathContext) android.OutputPath {
diff --git a/cc/ndk_headers.go b/cc/ndk_headers.go
index 7354be9..d0ae4a5 100644
--- a/cc/ndk_headers.go
+++ b/cc/ndk_headers.go
@@ -19,10 +19,8 @@
 	"path/filepath"
 
 	"github.com/google/blueprint"
-	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
-	"android/soong/bazel"
 )
 
 var (
@@ -81,7 +79,6 @@
 
 type headerModule struct {
 	android.ModuleBase
-	android.BazelModuleBase
 
 	properties headerProperties
 
@@ -147,39 +144,6 @@
 	}
 }
 
-// TODO(b/243196151): Populate `system` and `arch` metadata
-type bazelCcApiHeadersAttributes struct {
-	Hdrs        bazel.LabelListAttribute
-	Include_dir *string
-}
-
-func createCcApiHeadersTarget(ctx android.TopDownMutatorContext, includes []string, excludes []string, include_dir *string) {
-	props := bazel.BazelTargetModuleProperties{
-		Rule_class:        "cc_api_headers",
-		Bzl_load_location: "//build/bazel/rules/apis:cc_api_contribution.bzl",
-	}
-	attrs := &bazelCcApiHeadersAttributes{
-		Hdrs: bazel.MakeLabelListAttribute(
-			android.BazelLabelForModuleSrcExcludes(
-				ctx,
-				includes,
-				excludes,
-			),
-		),
-		Include_dir: include_dir,
-	}
-	ctx.CreateBazelTargetModule(props, android.CommonAttributes{
-		Name: android.ApiContributionTargetName(ctx.ModuleName()),
-	}, attrs)
-}
-
-var _ android.ApiProvider = (*headerModule)(nil)
-
-func (h *headerModule) ConvertWithApiBp2build(ctx android.TopDownMutatorContext) {
-	// Generate `cc_api_headers` target for Multi-tree API export
-	createCcApiHeadersTarget(ctx, h.properties.Srcs, h.properties.Exclude_srcs, h.properties.From)
-}
-
 // ndk_headers installs the sets of ndk headers defined in the srcs property
 // to the sysroot base + "usr/include" + to directory + directory component.
 // ndk_headers requires the license file to be specified. Example:
@@ -226,7 +190,6 @@
 // Note that this is really only built to handle bionic/libc/include.
 type versionedHeaderModule struct {
 	android.ModuleBase
-	android.BazelModuleBase
 
 	properties versionedHeaderProperties
 
@@ -264,15 +227,6 @@
 	processHeadersWithVersioner(ctx, fromSrcPath, toOutputPath, srcFiles, installPaths)
 }
 
-var _ android.ApiProvider = (*versionedHeaderModule)(nil)
-
-func (h *versionedHeaderModule) ConvertWithApiBp2build(ctx android.TopDownMutatorContext) {
-	// Glob all .h files under `From`
-	includePattern := headerGlobPattern(proptools.String(h.properties.From))
-	// Generate `cc_api_headers` target for Multi-tree API export
-	createCcApiHeadersTarget(ctx, []string{includePattern}, []string{}, h.properties.From)
-}
-
 func processHeadersWithVersioner(ctx android.ModuleContext, srcDir, outDir android.Path,
 	srcFiles android.Paths, installPaths []android.WritablePath) android.Path {
 	// The versioner depends on a dependencies directory to simplify determining include paths
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index a824361..f0b7cc5 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -342,7 +342,14 @@
 	if android.InList("hwaddress", config.SanitizeDevice()) {
 		return false
 	}
-	return true
+	// http://b/156513478
+	// http://b/277624006
+	// This step is expensive. We're not able to do anything with the outputs of
+	// this step yet (canDiffAbi is flagged off because libabigail isn't able to
+	// handle all our libraries), disable it. There's no sense in protecting
+	// against checking in code that breaks abidw since by the time any of this
+	// can be turned on we'll need to migrate to STG anyway.
+	return false
 }
 
 // Feature flag to disable diffing against prebuilts.
@@ -592,24 +599,3 @@
 	}
 	return android.BazelLabelForModuleDepsWithFn(ctx, hdrLibs, addSuffix)
 }
-
-func ndkLibraryBp2build(ctx android.TopDownMutatorContext, m *Module) {
-	props := bazel.BazelTargetModuleProperties{
-		Rule_class:        "cc_api_contribution",
-		Bzl_load_location: "//build/bazel/rules/apis:cc_api_contribution.bzl",
-	}
-	stubLibrary := m.compiler.(*stubDecorator)
-	attrs := &bazelCcApiContributionAttributes{
-		Library_name: stubLibrary.implementationModuleName(m.Name()),
-		Api_surfaces: bazel.MakeStringListAttribute(
-			[]string{android.PublicApi.String()}),
-	}
-	if symbolFile := stubLibrary.properties.Symbol_file; symbolFile != nil {
-		apiLabel := android.BazelLabelForModuleSrcSingle(ctx, proptools.String(symbolFile)).Label
-		attrs.Api = *bazel.MakeLabelAttribute(apiLabel)
-	}
-	apiHeaders := apiHeaderLabels(ctx, stubLibrary.properties.Export_header_libs)
-	attrs.Hdrs = bazel.MakeLabelListAttribute(apiHeaders)
-	apiContributionTargetName := android.ApiContributionTargetName(ctx.ModuleName())
-	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: apiContributionTargetName}, attrs)
-}
diff --git a/cc/ndk_sysroot.go b/cc/ndk_sysroot.go
index dffc6c6..0cf21b6 100644
--- a/cc/ndk_sysroot.go
+++ b/cc/ndk_sysroot.go
@@ -66,7 +66,7 @@
 	ctx.RegisterModuleType("ndk_library", NdkLibraryFactory)
 	ctx.RegisterModuleType("versioned_ndk_headers", versionedNdkHeadersFactory)
 	ctx.RegisterModuleType("preprocessed_ndk_headers", preprocessedNdkHeadersFactory)
-	ctx.RegisterSingletonType("ndk", NdkSingleton)
+	ctx.RegisterParallelSingletonType("ndk", NdkSingleton)
 }
 
 func getNdkInstallBase(ctx android.PathContext) android.InstallPath {
diff --git a/cc/object.go b/cc/object.go
index d65cdea..ca14845 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -226,7 +226,7 @@
 		Bzl_load_location: "//build/bazel/rules/cc:cc_object.bzl",
 	}
 
-	tags := android.ApexAvailableTags(m)
+	tags := android.ApexAvailableTagsWithoutTestApexes(ctx, m)
 
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{
 		Name: m.Name(),
@@ -309,6 +309,8 @@
 			})
 		}
 	} else {
+		outputAddrSig := android.PathForModuleOut(ctx, "addrsig", outputName)
+
 		if String(object.Properties.Prefix_symbols) != "" {
 			input := android.PathForModuleOut(ctx, "unprefixed", outputName)
 			transformBinaryPrefixSymbols(ctx, String(object.Properties.Prefix_symbols), input,
@@ -316,7 +318,12 @@
 			output = input
 		}
 
-		transformObjsToObj(ctx, objs.objFiles, builderFlags, output, flags.LdFlagsDeps)
+		transformObjsToObj(ctx, objs.objFiles, builderFlags, outputAddrSig, flags.LdFlagsDeps)
+
+		// ld -r reorders symbols and invalidates the .llvm_addrsig section, which then causes warnings
+		// if the resulting object is used with ld --icf=safe.  Strip the .llvm_addrsig section to
+		// prevent the warnings.
+		transformObjectNoAddrSig(ctx, outputAddrSig, output)
 	}
 
 	ctx.CheckbuildFile(outputFile)
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index 4470f54..44cd0d7 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -376,6 +376,7 @@
 		Static_library:         prebuiltAttrs.Src,
 		Export_includes:        exportedIncludes.Includes,
 		Export_system_includes: exportedIncludes.SystemIncludes,
+		// TODO: ¿Alwayslink?
 	}
 
 	props := bazel.BazelTargetModuleProperties{
@@ -388,7 +389,7 @@
 		name += "_bp2build_cc_library_static"
 	}
 
-	tags := android.ApexAvailableTags(module)
+	tags := android.ApexAvailableTagsWithoutTestApexes(ctx, module)
 	ctx.CreateBazelTargetModuleWithRestrictions(props, android.CommonAttributes{Name: name, Tags: tags}, attrs, prebuiltAttrs.Enabled)
 
 	_true := true
@@ -398,14 +399,19 @@
 }
 
 type bazelPrebuiltLibrarySharedAttributes struct {
-	Shared_library bazel.LabelAttribute
+	Shared_library         bazel.LabelAttribute
+	Export_includes        bazel.StringListAttribute
+	Export_system_includes bazel.StringListAttribute
 }
 
 func prebuiltLibrarySharedBp2Build(ctx android.TopDownMutatorContext, module *Module) {
 	prebuiltAttrs := Bp2BuildParsePrebuiltLibraryProps(ctx, module, false)
+	exportedIncludes := bp2BuildParseExportedIncludes(ctx, module, nil)
 
 	attrs := &bazelPrebuiltLibrarySharedAttributes{
-		Shared_library: prebuiltAttrs.Src,
+		Shared_library:         prebuiltAttrs.Src,
+		Export_includes:        exportedIncludes.Includes,
+		Export_system_includes: exportedIncludes.SystemIncludes,
 	}
 
 	props := bazel.BazelTargetModuleProperties{
@@ -414,7 +420,7 @@
 	}
 
 	name := android.RemoveOptionalPrebuiltPrefix(module.Name())
-	tags := android.ApexAvailableTags(module)
+	tags := android.ApexAvailableTagsWithoutTestApexes(ctx, module)
 	ctx.CreateBazelTargetModuleWithRestrictions(props, android.CommonAttributes{Name: name, Tags: tags}, attrs, prebuiltAttrs.Enabled)
 }
 
@@ -644,7 +650,7 @@
 	}
 
 	name := android.RemoveOptionalPrebuiltPrefix(module.Name())
-	tags := android.ApexAvailableTags(module)
+	tags := android.ApexAvailableTagsWithoutTestApexes(ctx, module)
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: name, Tags: tags}, attrs)
 }
 
@@ -807,7 +813,7 @@
 	}
 
 	name := android.RemoveOptionalPrebuiltPrefix(module.Name())
-	tags := android.ApexAvailableTags(module)
+	tags := android.ApexAvailableTagsWithoutTestApexes(ctx, module)
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: name, Tags: tags}, attrs)
 }
 
diff --git a/cc/proto.go b/cc/proto.go
index 97470e5..5d9aef6 100644
--- a/cc/proto.go
+++ b/cc/proto.go
@@ -207,7 +207,7 @@
 	protoAttrs.Min_sdk_version = m.Properties.Min_sdk_version
 
 	name := m.Name() + suffix
-	tags := android.ApexAvailableTags(m)
+	tags := android.ApexAvailableTagsWithoutTestApexes(ctx.(android.TopDownMutatorContext), m)
 	ctx.CreateBazelTargetModule(
 		bazel.BazelTargetModuleProperties{
 			Rule_class:        rule_class,
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 4601ee6..a5691ee 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -64,13 +64,14 @@
 
 	cfiBlocklistPath     = "external/compiler-rt/lib/cfi"
 	cfiBlocklistFilename = "cfi_blocklist.txt"
+	cfiEnableFlag        = "-fsanitize=cfi"
 	cfiCrossDsoFlag      = "-fsanitize-cfi-cross-dso"
 	cfiCflags            = []string{"-flto", cfiCrossDsoFlag,
 		"-fsanitize-ignorelist=" + cfiBlocklistPath + "/" + cfiBlocklistFilename}
 	// -flto and -fvisibility are required by clang when -fsanitize=cfi is
 	// used, but have no effect on assembly files
 	cfiAsflags = []string{"-flto", "-fvisibility=default"}
-	cfiLdflags = []string{"-flto", cfiCrossDsoFlag, "-fsanitize=cfi",
+	cfiLdflags = []string{"-flto", cfiCrossDsoFlag, cfiEnableFlag,
 		"-Wl,-plugin-opt,O1"}
 	cfiExportsMapPath      = "build/soong/cc/config"
 	cfiExportsMapFilename  = "cfi_exports.map"
@@ -395,8 +396,8 @@
 
 	// Leave out "-flto" from the slices exported to bazel, as we will use the
 	// dedicated LTO feature for this. For C Flags and Linker Flags, also leave
-	// out the cross DSO flag which will be added separately by transitions.
-	exportedVars.ExportStringList("CfiCFlags", cfiCflags[2:])
+	// out the cross DSO flag which will be added separately under the correct conditions.
+	exportedVars.ExportStringList("CfiCFlags", append(cfiCflags[2:], cfiEnableFlag))
 	exportedVars.ExportStringList("CfiLdFlags", cfiLdflags[2:])
 	exportedVars.ExportStringList("CfiAsFlags", cfiAsflags[1:])
 
@@ -593,6 +594,12 @@
 		}
 	}
 
+	// Enable HWASan for all components in the include paths (for Aarch64 only)
+	if s.Hwaddress == nil && ctx.Config().HWASanEnabledForPath(ctx.ModuleDir()) &&
+		ctx.Arch().ArchType == android.Arm64 && ctx.toolchain().Bionic() {
+		s.Hwaddress = proptools.BoolPtr(true)
+	}
+
 	// Enable CFI for non-host components in the include paths
 	if s.Cfi == nil && ctx.Config().CFIEnabledForPath(ctx.ModuleDir()) && !ctx.Host() {
 		s.Cfi = proptools.BoolPtr(true)
@@ -659,6 +666,21 @@
 		s.Diag.Cfi = nil
 	}
 
+	// TODO(b/280478629): runtimes don't exist for musl arm64 yet.
+	if ctx.toolchain().Musl() && ctx.Arch().ArchType == android.Arm64 {
+		s.Address = nil
+		s.Hwaddress = nil
+		s.Thread = nil
+		s.Scudo = nil
+		s.Fuzzer = nil
+		s.Cfi = nil
+		s.Diag.Cfi = nil
+		s.Misc_undefined = nil
+		s.Undefined = nil
+		s.All_undefined = nil
+		s.Integer_overflow = nil
+	}
+
 	// Also disable CFI for VNDK variants of components
 	if ctx.isVndk() && ctx.useVndk() {
 		s.Cfi = nil
@@ -792,6 +814,13 @@
 		if Bool(sanProps.Writeonly) {
 			flags.Local.CFlags = append(flags.Local.CFlags, "-mllvm", "-hwasan-instrument-reads=0")
 		}
+		if !ctx.staticBinary() && !ctx.Host() {
+			if ctx.bootstrap() {
+				flags.DynamicLinker = "/system/bin/bootstrap/linker_hwasan64"
+			} else {
+				flags.DynamicLinker = "/system/bin/linker_hwasan64"
+			}
+		}
 	}
 
 	if Bool(sanProps.Fuzzer) {
diff --git a/cc/sdk.go b/cc/sdk.go
index 4f361eb..6341926 100644
--- a/cc/sdk.go
+++ b/cc/sdk.go
@@ -47,16 +47,16 @@
 
 			// Mark the SDK variant.
 			modules[1].(*Module).Properties.IsSdkVariant = true
-			// SDK variant is not supposed to be installed
-			modules[1].(*Module).Properties.PreventInstall = true
 
 			if ctx.Config().UnbundledBuildApps() {
 				// For an unbundled apps build, hide the platform variant from Make.
 				modules[0].(*Module).Properties.HideFromMake = true
+				modules[0].(*Module).Properties.PreventInstall = true
 			} else {
 				// For a platform build, mark the SDK variant so that it gets a ".sdk" suffix when
 				// exposed to Make.
 				modules[1].(*Module).Properties.SdkAndPlatformVariantVisibleToMake = true
+				modules[1].(*Module).Properties.PreventInstall = true
 			}
 			ctx.AliasVariation("")
 		} else if isCcModule && ccModule.isImportedApiLibrary() {
@@ -74,8 +74,8 @@
 					if apiLibrary.hasApexStubs() {
 						// For an unbundled apps build, hide the platform variant from Make.
 						modules[1].(*Module).Properties.HideFromMake = true
-						modules[1].(*Module).Properties.PreventInstall = true
 					}
+					modules[1].(*Module).Properties.PreventInstall = true
 				} else {
 					// For a platform build, mark the SDK variant so that it gets a ".sdk" suffix when
 					// exposed to Make.
diff --git a/cc/sdk_test.go b/cc/sdk_test.go
index 790440c..61925e3 100644
--- a/cc/sdk_test.go
+++ b/cc/sdk_test.go
@@ -101,95 +101,3 @@
 	assertDep(t, libsdkNDK, libcxxNDK)
 	assertDep(t, libsdkPlatform, libcxxPlatform)
 }
-
-func TestMakeModuleNameForSdkVariant(t *testing.T) {
-	bp := `
-		cc_library {
-			name: "libfoo",
-			srcs: ["main_test.cpp"],
-			sdk_version: "current",
-			stl: "none",
-		}
-	`
-	platformVariant := "android_arm64_armv8-a_shared"
-	sdkVariant := "android_arm64_armv8-a_sdk_shared"
-	testCases := []struct {
-		name              string
-		unbundledApps     []string
-		variant           string
-		skipInstall       bool // soong skips install
-		hideFromMake      bool // no make entry
-		makeUninstallable bool // make skips install
-		makeModuleName    string
-	}{
-		{
-			name:          "platform variant in normal builds",
-			unbundledApps: nil,
-			variant:       platformVariant,
-			// installable in soong
-			skipInstall: false,
-			// visiable in Make as "libfoo"
-			hideFromMake:   false,
-			makeModuleName: "libfoo",
-			// installable in Make
-			makeUninstallable: false,
-		},
-		{
-			name:          "sdk variant in normal builds",
-			unbundledApps: nil,
-			variant:       sdkVariant,
-			// soong doesn't install
-			skipInstall: true,
-			// visible in Make as "libfoo.sdk"
-			hideFromMake:   false,
-			makeModuleName: "libfoo.sdk",
-			// but not installed
-			makeUninstallable: true,
-		},
-		{
-			name:          "platform variant in unbunded builds",
-			unbundledApps: []string{"bar"},
-			variant:       platformVariant,
-			// installable in soong
-			skipInstall: false,
-			// hidden from make
-			hideFromMake: true,
-		},
-		{
-			name:          "sdk variant in unbunded builds",
-			unbundledApps: []string{"bar"},
-			variant:       sdkVariant,
-			// soong doesn't install
-			skipInstall: true,
-			// visible in Make as "libfoo"
-			hideFromMake:   false,
-			makeModuleName: "libfoo",
-			// but not installed
-			makeUninstallable: true,
-		},
-	}
-	for _, tc := range testCases {
-		t.Run(tc.name, func(t *testing.T) {
-			fixture := android.GroupFixturePreparers(prepareForCcTest,
-				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-					variables.Unbundled_build_apps = tc.unbundledApps
-				}),
-			)
-			ctx := fixture.RunTestWithBp(t, bp).TestContext
-			module := ctx.ModuleForTests("libfoo", tc.variant).Module().(*Module)
-			android.AssertBoolEquals(t, "IsSkipInstall", tc.skipInstall, module.IsSkipInstall())
-			android.AssertBoolEquals(t, "HideFromMake", tc.hideFromMake, module.HiddenFromMake())
-			if !tc.hideFromMake {
-				entries := android.AndroidMkEntriesForTest(t, ctx, module)[0]
-				android.AssertStringEquals(t, "LOCAL_MODULE",
-					tc.makeModuleName, entries.EntryMap["LOCAL_MODULE"][0])
-				actualUninstallable := false
-				if actual, ok := entries.EntryMap["LOCAL_UNINSTALLABLE_MODULE"]; ok {
-					actualUninstallable = "true" == actual[0]
-				}
-				android.AssertBoolEquals(t, "LOCAL_UNINSTALLABLE_MODULE",
-					tc.makeUninstallable, actualUninstallable)
-			}
-		})
-	}
-}
diff --git a/cc/stub_library.go b/cc/stub_library.go
index f324dcc..3a6d0ae 100644
--- a/cc/stub_library.go
+++ b/cc/stub_library.go
@@ -23,7 +23,7 @@
 
 func init() {
 	// Use singleton type to gather all generated soong modules.
-	android.RegisterSingletonType("stublibraries", stubLibrariesSingleton)
+	android.RegisterParallelSingletonType("stublibraries", stubLibrariesSingleton)
 }
 
 type stubLibraries struct {
diff --git a/cc/sysprop.go b/cc/sysprop.go
index 0df290a..7ddd476 100644
--- a/cc/sysprop.go
+++ b/cc/sysprop.go
@@ -38,7 +38,7 @@
 }
 
 func Bp2buildSysprop(ctx android.Bp2buildMutatorContext, labels SyspropLibraryLabels, srcs bazel.LabelListAttribute, minSdkVersion *string) {
-	apexAvailableTags := android.ApexAvailableTags(ctx.Module())
+	apexAvailableTags := android.ApexAvailableTagsWithoutTestApexes(ctx.(android.TopDownMutatorContext), ctx.Module())
 	ctx.CreateBazelTargetModule(
 		bazel.BazelTargetModuleProperties{
 			Rule_class:        "sysprop_library",
diff --git a/cc/test.go b/cc/test.go
index 27de06b..3f5f710 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -70,6 +70,10 @@
 	// Add MinApiLevelModuleController with ro.vndk.version property. If ro.vndk.version has an
 	// integer value and the value is less than the min_vndk_version, skip this module.
 	Min_vndk_version *int64
+
+	// Extra <option> tags to add to the auto generated test xml file under the test runner, e.g., GTest.
+	// The "key" is optional in each of these.
+	Test_runner_options []tradefed.Option
 }
 
 type TestBinaryProperties struct {
@@ -398,6 +402,7 @@
 		TestConfigTemplateProp: test.Properties.Test_config_template,
 		TestSuites:             test.testDecorator.InstallerProperties.Test_suites,
 		Config:                 configs,
+		TestRunnerOptions:      test.Properties.Test_options.Test_runner_options,
 		AutoGenConfig:          test.Properties.Auto_gen_config,
 		TestInstallBase:        testInstallBase,
 		DeviceTemplate:         "${NativeTestConfigTemplate}",
diff --git a/cc/testing.go b/cc/testing.go
index ced0929..d346739 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -82,6 +82,10 @@
 			sanitize: {
 				never: true,
 			},
+			apex_available: [
+				"//apex_available:anyapex",
+				"//apex_available:platform",
+			],
 		}
 
 		cc_prebuilt_library_static {
diff --git a/cc/tidy.go b/cc/tidy.go
index bbcaece..7b123cb 100644
--- a/cc/tidy.go
+++ b/cc/tidy.go
@@ -201,7 +201,7 @@
 }
 
 func init() {
-	android.RegisterSingletonType("tidy_phony_targets", TidyPhonySingleton)
+	android.RegisterParallelSingletonType("tidy_phony_targets", TidyPhonySingleton)
 }
 
 // This TidyPhonySingleton generates both tidy-* and obj-* phony targets for C/C++ files.
diff --git a/cc/util.go b/cc/util.go
index aa0f6b5..6d8ac43 100644
--- a/cc/util.go
+++ b/cc/util.go
@@ -100,17 +100,6 @@
 		"ln -sf " + target + " " + filepath.Join(dir, linkName)
 }
 
-func combineNoticesRule(ctx android.SingletonContext, paths android.Paths, out string) android.OutputPath {
-	outPath := android.PathForOutput(ctx, out)
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        android.Cat,
-		Inputs:      paths,
-		Output:      outPath,
-		Description: "combine notices for " + out,
-	})
-	return outPath
-}
-
 // Dump a map to a list file as:
 //
 // {key1} {value1}
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index 9b12bfa..d2531c0 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -157,9 +157,10 @@
 	// extra config files
 	InitRc         []string `json:",omitempty"`
 	VintfFragments []string `json:",omitempty"`
+	MinSdkVersion  string   `json:",omitempty"`
 }
 
-var ccSnapshotAction snapshot.GenerateSnapshotAction = func(s snapshot.SnapshotSingleton, ctx android.SingletonContext, snapshotArchDir string) android.Paths {
+var ccSnapshotAction snapshot.GenerateSnapshotAction = func(s snapshot.SnapshotSingleton, ctx android.SingletonContext, snapshotArchDir string) snapshot.SnapshotPaths {
 	/*
 		Vendor snapshot zipped artifacts directory structure for cc modules:
 		{SNAPSHOT_ARCH}/
@@ -194,10 +195,10 @@
 	*/
 
 	var snapshotOutputs android.Paths
+	var snapshotNotices android.Paths
 
 	includeDir := filepath.Join(snapshotArchDir, "include")
 	configsDir := filepath.Join(snapshotArchDir, "configs")
-	noticeDir := filepath.Join(snapshotArchDir, "NOTICE_FILES")
 
 	installedNotices := make(map[string]bool)
 	installedConfigs := make(map[string]bool)
@@ -227,7 +228,7 @@
 		prop := snapshotJsonFlags{}
 
 		// Common properties among snapshots.
-		prop.ModuleName = ctx.ModuleName(m)
+		prop.InitBaseSnapshotPropsWithName(m, ctx.ModuleName(m))
 		if supportsVndkExt(s.Image) && m.IsVndkExt() {
 			// vndk exts are installed to /vendor/lib(64)?/vndk(-sp)?
 			if m.IsVndkSp() {
@@ -249,6 +250,11 @@
 		for _, path := range m.VintfFragments() {
 			prop.VintfFragments = append(prop.VintfFragments, filepath.Join("configs", path.Base()))
 		}
+		if m.IsPrebuilt() {
+			prop.MinSdkVersion = "apex_inherit"
+		} else {
+			prop.MinSdkVersion = m.MinSdkVersion()
+		}
 
 		// install config files. ignores any duplicates.
 		for _, path := range append(m.InitRc(), m.VintfFragments()...) {
@@ -322,13 +328,13 @@
 						}
 					}
 				}
-				snapshotLibOut := filepath.Join(snapshotArchDir, targetArch, libType, stem)
+				snapshotLibOut := filepath.Join(snapshotArchDir, targetArch, libType, m.RelativeInstallPath(), stem)
 				ret = append(ret, copyFile(ctx, libPath, snapshotLibOut, fake))
 			} else {
 				stem = ctx.ModuleName(m)
 			}
 
-			propOut = filepath.Join(snapshotArchDir, targetArch, libType, stem+".json")
+			propOut = filepath.Join(snapshotArchDir, targetArch, libType, m.RelativeInstallPath(), stem+".json")
 		} else if m.Binary() {
 			// binary flags
 			prop.Symlinks = m.Symlinks()
@@ -404,13 +410,10 @@
 			headers = append(headers, m.SnapshotHeaders()...)
 		}
 
-		if len(m.EffectiveLicenseFiles()) > 0 {
-			noticeName := ctx.ModuleName(m) + ".txt"
-			noticeOut := filepath.Join(noticeDir, noticeName)
-			// skip already copied notice file
-			if !installedNotices[noticeOut] {
-				installedNotices[noticeOut] = true
-				snapshotOutputs = append(snapshotOutputs, combineNoticesRule(ctx, m.EffectiveLicenseFiles(), noticeOut))
+		for _, notice := range m.EffectiveLicenseFiles() {
+			if _, ok := installedNotices[notice.String()]; !ok {
+				installedNotices[notice.String()] = true
+				snapshotNotices = append(snapshotNotices, notice)
 			}
 		}
 	})
@@ -420,7 +423,7 @@
 		snapshotOutputs = append(snapshotOutputs, copyFile(ctx, header, filepath.Join(includeDir, header.String()), s.Fake))
 	}
 
-	return snapshotOutputs
+	return snapshot.SnapshotPaths{OutputFiles: snapshotOutputs, NoticeFiles: snapshotNotices}
 }
 
 func init() {
diff --git a/cc/vendor_snapshot_test.go b/cc/vendor_snapshot_test.go
index 619500e..c5431b3 100644
--- a/cc/vendor_snapshot_test.go
+++ b/cc/vendor_snapshot_test.go
@@ -23,6 +23,17 @@
 	"testing"
 )
 
+func checkJsonContents(t *testing.T, ctx android.TestingSingleton, jsonPath string, key string, value string) {
+	jsonOut := ctx.MaybeOutput(jsonPath)
+	if jsonOut.Rule == nil {
+		t.Errorf("%q expected but not found", jsonPath)
+		return
+	}
+	if !strings.Contains(jsonOut.Args["content"], fmt.Sprintf("%q:%q", key, value)) {
+		t.Errorf("%q must include %q:%q but it only has %v", jsonPath, key, value, jsonOut.Args["content"])
+	}
+}
+
 func TestVendorSnapshotCapture(t *testing.T) {
 	bp := `
 	cc_library {
@@ -52,6 +63,7 @@
 		name: "libvendor_available",
 		vendor_available: true,
 		nocrt: true,
+		min_sdk_version: "29",
 	}
 
 	cc_library_headers {
@@ -155,6 +167,9 @@
 			filepath.Join(staticDir, "libvendor_available.a.json"),
 			filepath.Join(staticDir, "libvendor_available.cfi.a.json"))
 
+		checkJsonContents(t, snapshotSingleton, filepath.Join(staticDir, "libb.a.json"), "MinSdkVersion", "apex_inherit")
+		checkJsonContents(t, snapshotSingleton, filepath.Join(staticDir, "libvendor_available.a.json"), "MinSdkVersion", "29")
+
 		// For binary executables, all vendor:true and vendor_available modules are captured.
 		if archType == "arm64" {
 			binaryVariant := fmt.Sprintf("android_vendor.29_%s_%s", archType, archVariant)
@@ -1657,3 +1672,69 @@
 		}
 	}
 }
+
+func TestSnapshotInRelativeInstallPath(t *testing.T) {
+	bp := `
+	cc_library {
+		name: "libvendor_available",
+		vendor_available: true,
+		nocrt: true,
+	}
+
+	cc_library {
+		name: "libvendor_available_var",
+		vendor_available: true,
+		stem: "libvendor_available",
+		relative_install_path: "var",
+		nocrt: true,
+	}
+`
+
+	config := TestConfig(t.TempDir(), android.Android, nil, bp, nil)
+	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
+	config.TestProductVariables.Platform_vndk_version = StringPtr("29")
+	ctx := testCcWithConfig(t, config)
+
+	// Check Vendor snapshot output.
+
+	snapshotDir := "vendor-snapshot"
+	snapshotVariantPath := filepath.Join("out/soong", snapshotDir, "arm64")
+	snapshotSingleton := ctx.SingletonForTests("vendor-snapshot")
+
+	var jsonFiles []string
+
+	for _, arch := range [][]string{
+		[]string{"arm64", "armv8-a"},
+		[]string{"arm", "armv7-a-neon"},
+	} {
+		archType := arch[0]
+		archVariant := arch[1]
+		archDir := fmt.Sprintf("arch-%s-%s", archType, archVariant)
+
+		// For shared libraries, only non-VNDK vendor_available modules are captured
+		sharedVariant := fmt.Sprintf("android_vendor.29_%s_%s_shared", archType, archVariant)
+		sharedDir := filepath.Join(snapshotVariantPath, archDir, "shared")
+		sharedDirVar := filepath.Join(sharedDir, "var")
+		CheckSnapshot(t, ctx, snapshotSingleton, "libvendor_available", "libvendor_available.so", sharedDir, sharedVariant)
+		CheckSnapshot(t, ctx, snapshotSingleton, "libvendor_available_var", "libvendor_available.so", sharedDirVar, sharedVariant)
+		jsonFiles = append(jsonFiles,
+			filepath.Join(sharedDir, "libvendor_available.so.json"),
+			filepath.Join(sharedDirVar, "libvendor_available.so.json"))
+	}
+
+	for _, jsonFile := range jsonFiles {
+		// verify all json files exist
+		if snapshotSingleton.MaybeOutput(jsonFile).Rule == nil {
+			t.Errorf("%q expected but not found", jsonFile)
+		}
+	}
+
+	// fake snapshot should have all outputs in the normal snapshot.
+	fakeSnapshotSingleton := ctx.SingletonForTests("vendor-fake-snapshot")
+	for _, output := range snapshotSingleton.AllOutputs() {
+		fakeOutput := strings.Replace(output, "/vendor-snapshot/", "/fake/vendor-snapshot/", 1)
+		if fakeSnapshotSingleton.MaybeOutput(fakeOutput).Rule == nil {
+			t.Errorf("%q expected but not found", fakeOutput)
+		}
+	}
+}
diff --git a/cc/vndk.go b/cc/vndk.go
index be66cd7..7a2286e 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -241,7 +241,7 @@
 func vndkModuleLister(predicate func(*Module) bool) moduleListerFunc {
 	return func(ctx android.SingletonContext) (moduleNames, fileNames []string) {
 		ctx.VisitAllModules(func(m android.Module) {
-			if c, ok := m.(*Module); ok && predicate(c) {
+			if c, ok := m.(*Module); ok && predicate(c) && !c.IsVndkPrebuiltLibrary() {
 				filename, err := getVndkFileName(c)
 				if err != nil {
 					ctx.ModuleErrorf(m, "%s", err)
@@ -402,6 +402,11 @@
 		m.VendorProperties.IsVNDKPrivate = Bool(prebuiltLib.Properties.Llndk.Private)
 	}
 
+	if m.IsVndkPrebuiltLibrary() && !m.IsVndk() {
+		m.VendorProperties.IsLLNDK = true
+		// TODO(b/280697209): copy "llndk.private" flag to vndk_prebuilt_shared
+	}
+
 	if (isLib && lib.buildShared()) || (isPrebuiltLib && prebuiltLib.buildShared()) {
 		if m.vndkdep != nil && m.vndkdep.isVndk() && !m.vndkdep.isVndkExt() {
 			processVndkLibrary(mctx, m)
@@ -412,16 +417,16 @@
 
 func init() {
 	RegisterVndkLibraryTxtTypes(android.InitRegistrationContext)
-	android.RegisterSingletonType("vndk-snapshot", VndkSnapshotSingleton)
+	android.RegisterParallelSingletonType("vndk-snapshot", VndkSnapshotSingleton)
 }
 
 func RegisterVndkLibraryTxtTypes(ctx android.RegistrationContext) {
-	ctx.RegisterSingletonModuleType("llndk_libraries_txt", llndkLibrariesTxtFactory)
-	ctx.RegisterSingletonModuleType("vndksp_libraries_txt", vndkSPLibrariesTxtFactory)
-	ctx.RegisterSingletonModuleType("vndkcore_libraries_txt", vndkCoreLibrariesTxtFactory)
-	ctx.RegisterSingletonModuleType("vndkprivate_libraries_txt", vndkPrivateLibrariesTxtFactory)
-	ctx.RegisterSingletonModuleType("vndkproduct_libraries_txt", vndkProductLibrariesTxtFactory)
-	ctx.RegisterSingletonModuleType("vndkcorevariant_libraries_txt", vndkUsingCoreVariantLibrariesTxtFactory)
+	ctx.RegisterParallelSingletonModuleType("llndk_libraries_txt", llndkLibrariesTxtFactory)
+	ctx.RegisterParallelSingletonModuleType("vndksp_libraries_txt", vndkSPLibrariesTxtFactory)
+	ctx.RegisterParallelSingletonModuleType("vndkcore_libraries_txt", vndkCoreLibrariesTxtFactory)
+	ctx.RegisterParallelSingletonModuleType("vndkprivate_libraries_txt", vndkPrivateLibrariesTxtFactory)
+	ctx.RegisterParallelSingletonModuleType("vndkproduct_libraries_txt", vndkProductLibrariesTxtFactory)
+	ctx.RegisterParallelSingletonModuleType("vndkcorevariant_libraries_txt", vndkUsingCoreVariantLibrariesTxtFactory)
 }
 
 type vndkLibrariesTxt struct {
@@ -706,6 +711,7 @@
 
 		// json struct to export snapshot information
 		prop := struct {
+			MinSdkVersion       string   `json:",omitempty"`
 			LicenseKinds        []string `json:",omitempty"`
 			LicenseTexts        []string `json:",omitempty"`
 			ExportedDirs        []string `json:",omitempty"`
@@ -716,6 +722,7 @@
 
 		prop.LicenseKinds = m.EffectiveLicenseKinds()
 		prop.LicenseTexts = m.EffectiveLicenseFiles().Strings()
+		prop.MinSdkVersion = m.MinSdkVersion()
 
 		if ctx.Config().VndkSnapshotBuildArtifacts() {
 			exportedInfo := ctx.ModuleProvider(m, FlagExporterInfoProvider).(FlagExporterInfo)
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 79a5ce4..e903c0a 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -16,10 +16,12 @@
 
 import (
 	"bytes"
+	"errors"
 	"flag"
 	"fmt"
 	"os"
 	"path/filepath"
+	"regexp"
 	"strings"
 	"time"
 
@@ -30,6 +32,7 @@
 	"android/soong/shared"
 	"android/soong/ui/metrics/bp2build_metrics_proto"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/bootstrap"
 	"github.com/google/blueprint/deptools"
 	"github.com/google/blueprint/metrics"
@@ -77,6 +80,7 @@
 	flag.StringVar(&cmdlineArgs.Bp2buildMarker, "bp2build_marker", "", "If set, run bp2build, touch the specified marker file then exit")
 	flag.StringVar(&cmdlineArgs.SymlinkForestMarker, "symlink_forest_marker", "", "If set, create the bp2build symlink forest, touch the specified marker file, then exit")
 	flag.StringVar(&cmdlineArgs.OutFile, "o", "build.ninja", "the Ninja file to output")
+	flag.StringVar(&cmdlineArgs.SoongVariables, "soong_variables", "soong.variables", "the file contains all build variables")
 	flag.StringVar(&cmdlineArgs.BazelForceEnabledModules, "bazel-force-enabled-modules", "", "additional modules to build with Bazel. Comma-delimited")
 	flag.BoolVar(&cmdlineArgs.EmptyNinjaFile, "empty-ninja-file", false, "write out a 0-byte ninja file")
 	flag.BoolVar(&cmdlineArgs.MultitreeBuild, "multitree-build", false, "this is a multitree build")
@@ -85,7 +89,7 @@
 	flag.BoolVar(&cmdlineArgs.BazelModeDev, "bazel-mode-dev", false, "use bazel for analysis of a large number of modules (less stable)")
 	flag.BoolVar(&cmdlineArgs.UseBazelProxy, "use-bazel-proxy", false, "communicate with bazel using unix socket proxy instead of spawning subprocesses")
 	flag.BoolVar(&cmdlineArgs.BuildFromTextStub, "build-from-text-stub", false, "build Java stubs from API text files instead of source files")
-
+	flag.BoolVar(&cmdlineArgs.EnsureAllowlistIntegrity, "ensure-allowlist-integrity", false, "verify that allowlisted modules are mixed-built")
 	// Flags that probably shouldn't be flags of soong_build, but we haven't found
 	// the time to remove them yet
 	flag.BoolVar(&cmdlineArgs.RunGoTests, "t", false, "build and run go tests during bootstrap")
@@ -132,9 +136,25 @@
 	ninjaDeps = append(ninjaDeps, writeBuildGlobsNinjaFile(ctx)...)
 
 	writeDepFile(cmdlineArgs.OutFile, ctx.EventHandler, ninjaDeps)
+
+	if needToWriteNinjaHint(ctx) {
+		writeNinjaHint(ctx)
+	}
 	return cmdlineArgs.OutFile
 }
 
+func needToWriteNinjaHint(ctx *android.Context) bool {
+	switch ctx.Config().GetenvWithDefault("SOONG_GENERATES_NINJA_HINT", "") {
+	case "always":
+		return true
+	case "depend":
+		if _, err := os.Stat(filepath.Join(ctx.Config().OutDir(), ".ninja_log")); errors.Is(err, os.ErrNotExist) {
+			return true
+		}
+	}
+	return false
+}
+
 // Run the code-generation phase to convert BazelTargetModules to BUILD files.
 func runQueryView(queryviewDir, queryviewMarker string, ctx *android.Context) {
 	ctx.EventHandler.Begin("queryview")
@@ -256,18 +276,47 @@
 }
 
 func writeNinjaHint(ctx *android.Context) error {
-	wantModules := make([]string, len(allowlists.HugeModulesMap))
-	i := 0
-	for k := range allowlists.HugeModulesMap {
-		wantModules[i] = k
-		i += 1
-	}
-	outputsMap := ctx.Context.GetOutputsFromModuleNames(wantModules)
-	var outputBuilder strings.Builder
-	for k, v := range allowlists.HugeModulesMap {
-		for _, output := range outputsMap[k] {
-			outputBuilder.WriteString(fmt.Sprintf("%s,%d\n", output, v))
+	ctx.BeginEvent("ninja_hint")
+	defer ctx.EndEvent("ninja_hint")
+	// The current predictor focuses on reducing false negatives.
+	// If there are too many false positives (e.g., most modules are marked as positive),
+	// real long-running jobs cannot run early.
+	// Therefore, the model should be adjusted in this case.
+	// The model should also be adjusted if there are critical false negatives.
+	predicate := func(j *blueprint.JsonModule) (prioritized bool, weight int) {
+		prioritized = false
+		weight = 0
+		for prefix, w := range allowlists.HugeModuleTypePrefixMap {
+			if strings.HasPrefix(j.Type, prefix) {
+				prioritized = true
+				weight = w
+				return
+			}
 		}
+		dep_count := len(j.Deps)
+		src_count := 0
+		for _, a := range j.Module["Actions"].([]blueprint.JSONAction) {
+			src_count += len(a.Inputs)
+		}
+		input_size := dep_count + src_count
+
+		// Current threshold is an arbitrary value which only consider recall rather than accuracy.
+		if input_size > allowlists.INPUT_SIZE_THRESHOLD {
+			prioritized = true
+			weight += ((input_size) / allowlists.INPUT_SIZE_THRESHOLD) * allowlists.DEFAULT_PRIORITIZED_WEIGHT
+
+			// To prevent some modules from having too large a priority value.
+			if weight > allowlists.HIGH_PRIORITIZED_WEIGHT {
+				weight = allowlists.HIGH_PRIORITIZED_WEIGHT
+			}
+		}
+		return
+	}
+
+	outputsMap := ctx.Context.GetWeightedOutputsFromPredicate(predicate)
+	var outputBuilder strings.Builder
+	for output, weight := range outputsMap {
+		outputBuilder.WriteString(fmt.Sprintf("%s,%d\n", output, weight))
 	}
 	weightListFile := filepath.Join(topDir, ctx.Config().OutDir(), ".ninja_weight_list")
 
@@ -288,6 +337,67 @@
 	maybeQuit(err, "error writing soong_build metrics %s", metricsFile)
 }
 
+// Errors out if any modules expected to be mixed_built were not, unless
+// the modules did not exist.
+func checkForAllowlistIntegrityError(configuration android.Config, isStagingMode bool) error {
+	modules := findMisconfiguredModules(configuration, isStagingMode)
+	if len(modules) == 0 {
+		return nil
+	}
+
+	return fmt.Errorf("Error: expected the following modules to be mixed_built: %s", modules)
+}
+
+// Returns true if the given module has all of the following true:
+//  1. Is allowlisted to be built with Bazel.
+//  2. Has a variant which is *not* built with Bazel.
+//  3. Has no variant which is built with Bazel.
+//
+// This indicates the allowlisting of this variant had no effect.
+// TODO(b/280457637): Return true for nonexistent modules.
+func isAllowlistMisconfiguredForModule(module string, mixedBuildsEnabled map[string]struct{}, mixedBuildsDisabled map[string]struct{}) bool {
+	_, enabled := mixedBuildsEnabled[module]
+
+	if enabled {
+		return false
+	}
+
+	_, disabled := mixedBuildsDisabled[module]
+	return disabled
+
+}
+
+// Returns the list of modules that should have been mixed_built (per the
+// allowlists and cmdline flags) but were not.
+// Note: nonexistent modules are excluded from the list. See b/280457637
+func findMisconfiguredModules(configuration android.Config, isStagingMode bool) []string {
+	retval := []string{}
+	forceEnabledModules := configuration.BazelModulesForceEnabledByFlag()
+
+	mixedBuildsEnabled := configuration.GetMixedBuildsEnabledModules()
+	mixedBuildsDisabled := configuration.GetMixedBuildsDisabledModules()
+	for _, module := range allowlists.ProdMixedBuildsEnabledList {
+		if isAllowlistMisconfiguredForModule(module, mixedBuildsEnabled, mixedBuildsDisabled) {
+			retval = append(retval, module)
+		}
+	}
+
+	if isStagingMode {
+		for _, module := range allowlists.StagingMixedBuildsEnabledList {
+			if isAllowlistMisconfiguredForModule(module, mixedBuildsEnabled, mixedBuildsDisabled) {
+				retval = append(retval, module)
+			}
+		}
+	}
+
+	for module, _ := range forceEnabledModules {
+		if isAllowlistMisconfiguredForModule(module, mixedBuildsEnabled, mixedBuildsDisabled) {
+			retval = append(retval, module)
+		}
+	}
+	return retval
+}
+
 func writeJsonModuleGraphAndActions(ctx *android.Context, cmdArgs android.CmdArgs) {
 	graphFile, graphErr := os.Create(shared.JoinPath(topDir, cmdArgs.ModuleGraphFile))
 	maybeQuit(graphErr, "graph err")
@@ -364,6 +474,9 @@
 		// The actual output (build.ninja) was written in the RunBlueprint() call
 		// above
 		writeDepFile(cmdlineArgs.OutFile, ctx.EventHandler, ninjaDeps)
+		if needToWriteNinjaHint(ctx) {
+			writeNinjaHint(ctx)
+		}
 		return cmdlineArgs.OutFile
 	}
 }
@@ -419,6 +532,8 @@
 
 	var finalOutputFile string
 
+	writeSymlink := false
+
 	// Run Soong for a specific activity, like bp2build, queryview
 	// or the actual Soong build for the build.ninja file.
 	switch configuration.BuildMode {
@@ -433,20 +548,58 @@
 		writeMetrics(configuration, ctx.EventHandler, metricsDir)
 	default:
 		ctx.Register()
-		if configuration.IsMixedBuildsEnabled() {
+		isMixedBuildsEnabled := configuration.IsMixedBuildsEnabled()
+		if isMixedBuildsEnabled {
 			finalOutputFile = runMixedModeBuild(ctx, extraNinjaDeps)
+			if cmdlineArgs.EnsureAllowlistIntegrity {
+				if err := checkForAllowlistIntegrityError(configuration, cmdlineArgs.BazelModeStaging); err != nil {
+					maybeQuit(err, "")
+				}
+			}
+			writeSymlink = true
 		} else {
 			finalOutputFile = runSoongOnlyBuild(ctx, extraNinjaDeps)
-		}
-		if ctx.Config().IsEnvTrue("SOONG_GENERATES_NINJA_HINT") {
-			writeNinjaHint(ctx)
+
+			if configuration.BuildMode == android.AnalysisNoBazel {
+				writeSymlink = true
+			}
 		}
 		writeMetrics(configuration, ctx.EventHandler, metricsDir)
 	}
-	writeUsedEnvironmentFile(configuration, finalOutputFile)
+
+	// Register this environment variablesas being an implicit dependencies of
+	// soong_build. Changes to this environment variable will result in
+	// retriggering soong_build.
+	configuration.Getenv("USE_BAZEL_VERSION")
+
+	writeUsedEnvironmentFile(configuration)
+
+	// Touch the output file so that it's the newest file created by soong_build.
+	// This is necessary because, if soong_build generated any files which
+	// are ninja inputs to the main output file, then ninja would superfluously
+	// rebuild this output file on the next build invocation.
+	touch(shared.JoinPath(topDir, finalOutputFile))
+
+	// TODO(b/277029044): Remove this function once build.<product>.ninja lands
+	if writeSymlink {
+		writeBuildNinjaSymlink(configuration, finalOutputFile)
+	}
 }
 
-func writeUsedEnvironmentFile(configuration android.Config, finalOutputFile string) {
+// TODO(b/277029044): Remove this function once build.<product>.ninja lands
+func writeBuildNinjaSymlink(config android.Config, source string) {
+	targetPath := shared.JoinPath(topDir, config.SoongOutDir(), "build.ninja")
+	sourcePath := shared.JoinPath(topDir, source)
+
+	if targetPath == sourcePath {
+		return
+	}
+
+	os.Remove(targetPath)
+	os.Symlink(sourcePath, targetPath)
+}
+
+func writeUsedEnvironmentFile(configuration android.Config) {
 	if usedEnvFile == "" {
 		return
 	}
@@ -465,11 +618,6 @@
 	}
 	err = os.WriteFile(path, data, 0666)
 	maybeQuit(err, "error writing used environment file '%s'", usedEnvFile)
-
-	// Touch the output file so that it's not older than the file we just
-	// wrote. We can't write the environment file earlier because one an access
-	// new environment variables while writing it.
-	touch(shared.JoinPath(topDir, finalOutputFile))
 }
 
 func touch(path string) {
@@ -615,6 +763,43 @@
 	return excluded
 }
 
+// buildTargetsByPackage parses Bazel BUILD.bazel and BUILD files under
+// the workspace, and returns a map containing names of Bazel targets defined in
+// these BUILD files.
+// For example, maps "//foo/bar" to ["baz", "qux"] if `//foo/bar:{baz,qux}` exist.
+func buildTargetsByPackage(ctx *android.Context) map[string][]string {
+	existingBazelFiles, err := getExistingBazelRelatedFiles(topDir)
+	maybeQuit(err, "Error determining existing Bazel-related files")
+
+	result := map[string][]string{}
+
+	// Search for instances of `name = "$NAME"` (with arbitrary spacing).
+	targetNameRegex := regexp.MustCompile(`(?m)^\s*name\s*=\s*\"([^\"]+)\"`)
+
+	for _, path := range existingBazelFiles {
+		if !ctx.Config().Bp2buildPackageConfig.ShouldKeepExistingBuildFileForDir(filepath.Dir(path)) {
+			continue
+		}
+		fullPath := shared.JoinPath(topDir, path)
+		sourceDir := filepath.Dir(path)
+		fileInfo, err := os.Stat(fullPath)
+		maybeQuit(err, "Error accessing Bazel file '%s'", fullPath)
+
+		if !fileInfo.IsDir() &&
+			(fileInfo.Name() == "BUILD" || fileInfo.Name() == "BUILD.bazel") {
+			// Process this BUILD file.
+			buildFileContent, err := os.ReadFile(fullPath)
+			maybeQuit(err, "Error reading Bazel file '%s'", fullPath)
+
+			matches := targetNameRegex.FindAllStringSubmatch(string(buildFileContent), -1)
+			for _, match := range matches {
+				result[sourceDir] = append(result[sourceDir], match[1])
+			}
+		}
+	}
+	return result
+}
+
 // Run Soong in the bp2build mode. This creates a standalone context that registers
 // an alternate pipeline of mutators and singletons specifically for generating
 // Bazel BUILD files instead of Ninja files.
@@ -622,6 +807,10 @@
 	var codegenMetrics *bp2build.CodegenMetrics
 	ctx.EventHandler.Do("bp2build", func() {
 
+		ctx.EventHandler.Do("read_build", func() {
+			ctx.Config().SetBazelBuildFileTargets(buildTargetsByPackage(ctx))
+		})
+
 		// Propagate "allow misssing dependencies" bit. This is normally set in
 		// newContext(), but we create ctx without calling that method.
 		ctx.SetAllowMissingDependencies(ctx.Config().AllowMissingDependencies())
diff --git a/cmd/soong_build/queryview.go b/cmd/soong_build/queryview.go
index ce32184..67cb6cf 100644
--- a/cmd/soong_build/queryview.go
+++ b/cmd/soong_build/queryview.go
@@ -15,6 +15,7 @@
 package main
 
 import (
+	"android/soong/starlark_import"
 	"io/fs"
 	"io/ioutil"
 	"os"
@@ -47,6 +48,14 @@
 		}
 	}
 
+	// Add starlark deps here, so that they apply to both queryview and apibp2build which
+	// both run this function.
+	starlarkDeps, err2 := starlark_import.GetNinjaDeps()
+	if err2 != nil {
+		return err2
+	}
+	ctx.AddNinjaFileDeps(starlarkDeps...)
+
 	return nil
 }
 
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index 301246a..682fb75 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -92,14 +92,13 @@
 		stdio:       stdio,
 		run:         runMake,
 	}, {
-		flag:        "--upload-metrics-only",
-		description: "upload metrics without building anything",
-		config:      uploadOnlyConfig,
+		flag:        "--finalize-bazel-metrics",
+		description: "finalize b metrics and upload",
+		config:      build.UploadOnlyConfig,
 		stdio:       stdio,
-		// Upload-only mode mostly skips to the metrics-uploading phase of soong_ui.
-		// However, this invocation marks the true "end of the build", and thus we
-		// need to update the total runtime of the build to include this upload step.
-		run: updateTotalRealTime,
+		// Finalize-bazel-metrics mode updates metrics files and calls the metrics
+		// uploader. This marks the end of a b invocation.
+		run: finalizeBazelMetrics,
 	},
 }
 
@@ -203,8 +202,6 @@
 	bazelMetricsFile := filepath.Join(logsDir, c.logsPrefix+"bazel_metrics.pb")
 	soongBuildMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_build_metrics.pb")
 
-	//the profile file generated by Bazel"
-	bazelProfileFile := filepath.Join(logsDir, c.logsPrefix+"analyzed_bazel_profile.txt")
 	metricsFiles := []string{
 		buildErrorFile,           // build error strings
 		rbeMetricsFile,           // high level metrics related to remote build execution.
@@ -226,7 +223,7 @@
 		criticalPath.WriteToMetrics(met)
 		met.Dump(soongMetricsFile)
 		if !config.SkipMetricsUpload() {
-			build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, bazelProfileFile, bazelMetricsFile, metricsFiles...)
+			build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, metricsFiles...)
 		}
 	}()
 	c.run(buildCtx, config, args)
@@ -245,9 +242,10 @@
 	soongMetricsFile := filepath.Join(logsDir, logsPrefix+"soong_metrics")
 	bp2buildMetricsFile := filepath.Join(logsDir, logsPrefix+"bp2build_metrics.pb")
 	soongBuildMetricsFile := filepath.Join(logsDir, logsPrefix+"soong_build_metrics.pb")
+	bazelMetricsFile := filepath.Join(logsDir, logsPrefix+"bazel_metrics.pb")
 
 	//Delete the stale metrics files
-	staleFileSlice := []string{buildErrorFile, rbeMetricsFile, soongMetricsFile, bp2buildMetricsFile, soongBuildMetricsFile}
+	staleFileSlice := []string{buildErrorFile, rbeMetricsFile, soongMetricsFile, bp2buildMetricsFile, soongBuildMetricsFile, bazelMetricsFile}
 	if err := deleteStaleMetrics(staleFileSlice); err != nil {
 		log.Fatalln(err)
 	}
@@ -451,14 +449,6 @@
 	return build.NewConfig(ctx)
 }
 
-// uploadOnlyConfig explicitly requires no arguments.
-func uploadOnlyConfig(ctx build.Context, args ...string) build.Config {
-	if len(args) > 0 {
-		fmt.Printf("--upload-only does not require arguments.")
-	}
-	return build.UploadOnlyConfig(ctx)
-}
-
 func buildActionConfig(ctx build.Context, args ...string) build.Config {
 	flags := flag.NewFlagSet("build-mode", flag.ContinueOnError)
 	flags.SetOutput(ctx.Writer)
@@ -700,6 +690,15 @@
 	}
 }
 
+func finalizeBazelMetrics(ctx build.Context, config build.Config, args []string) {
+	updateTotalRealTime(ctx, config, args)
+
+	logsDir := config.LogsDir()
+	logsPrefix := config.GetLogsPrefix()
+	bazelMetricsFile := filepath.Join(logsDir, logsPrefix+"bazel_metrics.pb")
+	bazelProfileFile := filepath.Join(logsDir, logsPrefix+"analyzed_bazel_profile.txt")
+	build.ProcessBazelMetrics(bazelProfileFile, bazelMetricsFile, ctx, config)
+}
 func updateTotalRealTime(ctx build.Context, config build.Config, args []string) {
 	soongMetricsFile := filepath.Join(config.LogsDir(), "soong_metrics")
 
@@ -710,7 +709,7 @@
 	}
 	met := ctx.ContextImpl.Metrics
 
-	err = met.UpdateTotalRealTime(data)
+	err = met.UpdateTotalRealTimeAndNonZeroExit(data, config.BazelExitCode())
 	if err != nil {
 		ctx.Fatal(err)
 	}
diff --git a/cmd/zip2zip/zip2zip.go b/cmd/zip2zip/zip2zip.go
index 491267b..5ab9656 100644
--- a/cmd/zip2zip/zip2zip.go
+++ b/cmd/zip2zip/zip2zip.go
@@ -128,12 +128,6 @@
 	}
 
 	for _, arg := range args {
-		// Reserve escaping for future implementation, so make sure no
-		// one is using \ and expecting a certain behavior.
-		if strings.Contains(arg, "\\") {
-			return fmt.Errorf("\\ characters are not currently supported")
-		}
-
 		input, output := includeSplit(arg)
 
 		var includeMatches []pair
diff --git a/cmd/zip2zip/zip2zip_test.go b/cmd/zip2zip/zip2zip_test.go
index 2c4e005..c238098 100644
--- a/cmd/zip2zip/zip2zip_test.go
+++ b/cmd/zip2zip/zip2zip_test.go
@@ -38,13 +38,6 @@
 	storedFiles []string
 	err         error
 }{
-	{
-		name: "unsupported \\",
-
-		args: []string{"a\\b:b"},
-
-		err: fmt.Errorf("\\ characters are not currently supported"),
-	},
 	{ // This is modelled after the update package build rules in build/make/core/Makefile
 		name: "filter globs",
 
@@ -406,6 +399,13 @@
 			"b/a/b",
 		},
 	},
+	{
+		name: "escaping",
+
+		inputFiles:  []string{"a"},
+		args:        []string{"\\a"},
+		outputFiles: []string{"a"},
+	},
 }
 
 func errorString(e error) string {
diff --git a/device_config/Android.bp b/device_config/Android.bp
new file mode 100644
index 0000000..6c44454
--- /dev/null
+++ b/device_config/Android.bp
@@ -0,0 +1,32 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+    name: "soong-device_config",
+    pkgPath: "android/soong/device_config",
+    deps: [
+        "blueprint",
+        "blueprint-pathtools",
+        "sbox_proto",
+        "soong",
+        "soong-android",
+        "soong-bazel",
+        "soong-android",
+        "soong-java",
+    ],
+    srcs: [
+        "device_config_definitions.go",
+        "device_config_values.go",
+        "device_config_value_set.go",
+        "init.go",
+        "java_device_config_definitions_library.go",
+        "testing.go",
+    ],
+    testSrcs: [
+        "device_config_definitions_test.go",
+        "device_config_values_test.go",
+        "device_config_value_set_test.go",
+    ],
+    pluginFor: ["soong_build"],
+}
diff --git a/device_config/device_config_definitions.go b/device_config/device_config_definitions.go
new file mode 100644
index 0000000..bb78695
--- /dev/null
+++ b/device_config/device_config_definitions.go
@@ -0,0 +1,143 @@
+// Copyright 2023 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 device_config
+
+import (
+	"android/soong/android"
+	"fmt"
+	"github.com/google/blueprint"
+	"strings"
+)
+
+type DefinitionsModule struct {
+	android.ModuleBase
+	android.DefaultableModuleBase
+
+	// Properties for "device_config_definitions"
+	properties struct {
+		// aconfig files, relative to this Android.bp file
+		Srcs []string `android:"path"`
+
+		// Release config flag namespace
+		Namespace string
+
+		// Values from TARGET_RELEASE / RELEASE_DEVICE_CONFIG_VALUE_SETS
+		Values []string `blueprint:"mutated"`
+	}
+
+	intermediatePath android.WritablePath
+}
+
+func DefinitionsFactory() android.Module {
+	module := &DefinitionsModule{}
+
+	android.InitAndroidModule(module)
+	android.InitDefaultableModule(module)
+	module.AddProperties(&module.properties)
+	// TODO: bp2build
+	//android.InitBazelModule(module)
+
+	return module
+}
+
+type implicitValuesTagType struct {
+	blueprint.BaseDependencyTag
+}
+
+var implicitValuesTag = implicitValuesTagType{}
+
+func (module *DefinitionsModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+	// Validate Properties
+	if len(module.properties.Srcs) == 0 {
+		ctx.PropertyErrorf("srcs", "missing source files")
+		return
+	}
+	if len(module.properties.Namespace) == 0 {
+		ctx.PropertyErrorf("namespace", "missing namespace property")
+	}
+
+	// Add a dependency on the device_config_value_sets defined in
+	// RELEASE_DEVICE_CONFIG_VALUE_SETS, and add any device_config_values that
+	// match our namespace.
+	valuesFromConfig := ctx.Config().ReleaseDeviceConfigValueSets()
+	ctx.AddDependency(ctx.Module(), implicitValuesTag, valuesFromConfig...)
+}
+
+func (module *DefinitionsModule) OutputFiles(tag string) (android.Paths, error) {
+	switch tag {
+	case "":
+		// The default output of this module is the intermediates format, which is
+		// not installable and in a private format that no other rules can handle
+		// correctly.
+		return []android.Path{module.intermediatePath}, nil
+	default:
+		return nil, fmt.Errorf("unsupported device_config_definitions module reference tag %q", tag)
+	}
+}
+
+func joinAndPrefix(prefix string, values []string) string {
+	var sb strings.Builder
+	for _, v := range values {
+		sb.WriteString(prefix)
+		sb.WriteString(v)
+	}
+	return sb.String()
+}
+
+// Provider published by device_config_value_set
+type definitionsProviderData struct {
+	namespace        string
+	intermediatePath android.WritablePath
+}
+
+var definitionsProviderKey = blueprint.NewProvider(definitionsProviderData{})
+
+func (module *DefinitionsModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// Get the values that came from the global RELEASE_DEVICE_CONFIG_VALUE_SETS flag
+	ctx.VisitDirectDeps(func(dep android.Module) {
+		if !ctx.OtherModuleHasProvider(dep, valueSetProviderKey) {
+			// Other modules get injected as dependencies too, for example the license modules
+			return
+		}
+		depData := ctx.OtherModuleProvider(dep, valueSetProviderKey).(valueSetProviderData)
+		valuesFiles, ok := depData.AvailableNamespaces[module.properties.Namespace]
+		if ok {
+			for _, path := range valuesFiles {
+				module.properties.Values = append(module.properties.Values, path.String())
+			}
+		}
+	})
+
+	// Intermediate format
+	inputFiles := android.PathsForModuleSrc(ctx, module.properties.Srcs)
+	intermediatePath := android.PathForModuleOut(ctx, "intermediate.json")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        aconfigRule,
+		Inputs:      inputFiles,
+		Output:      intermediatePath,
+		Description: "device_config_definitions",
+		Args: map[string]string{
+			"release_version": ctx.Config().ReleaseVersion(),
+			"namespace":       module.properties.Namespace,
+			"values":          joinAndPrefix(" --values ", module.properties.Values),
+		},
+	})
+
+	ctx.SetProvider(definitionsProviderKey, definitionsProviderData{
+		namespace:        module.properties.Namespace,
+		intermediatePath: intermediatePath,
+	})
+
+}
diff --git a/device_config/device_config_definitions_test.go b/device_config/device_config_definitions_test.go
new file mode 100644
index 0000000..49afcc4
--- /dev/null
+++ b/device_config/device_config_definitions_test.go
@@ -0,0 +1,42 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package device_config
+
+import (
+	"strings"
+	"testing"
+
+	"android/soong/android"
+)
+
+func TestDeviceConfigDefinitions(t *testing.T) {
+	bp := `
+		device_config_definitions {
+			name: "module_name",
+			namespace: "com.example.package",
+			srcs: ["foo.aconfig"],
+		}
+	`
+	result := runTest(t, android.FixtureExpectsNoErrors, bp)
+
+	module := result.ModuleForTests("module_name", "").Module().(*DefinitionsModule)
+
+	// Check that the provider has the right contents
+	depData := result.ModuleProvider(module, definitionsProviderKey).(definitionsProviderData)
+	android.AssertStringEquals(t, "namespace", depData.namespace, "com.example.package")
+	if !strings.HasSuffix(depData.intermediatePath.String(), "/intermediate.json") {
+		t.Errorf("Missing intermediates path in provider: %s", depData.intermediatePath.String())
+	}
+}
diff --git a/device_config/device_config_value_set.go b/device_config/device_config_value_set.go
new file mode 100644
index 0000000..e406d20
--- /dev/null
+++ b/device_config/device_config_value_set.go
@@ -0,0 +1,92 @@
+// Copyright 2023 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 device_config
+
+import (
+	"android/soong/android"
+	"github.com/google/blueprint"
+)
+
+// Properties for "device_config_value_set"
+type ValueSetModule struct {
+	android.ModuleBase
+	android.DefaultableModuleBase
+
+	properties struct {
+		// device_config_values modules
+		Values []string
+	}
+}
+
+func ValueSetFactory() android.Module {
+	module := &ValueSetModule{}
+
+	android.InitAndroidModule(module)
+	android.InitDefaultableModule(module)
+	module.AddProperties(&module.properties)
+	// TODO: bp2build
+	//android.InitBazelModule(module)
+
+	return module
+}
+
+// Dependency tag for values property
+type valueSetType struct {
+	blueprint.BaseDependencyTag
+}
+
+var valueSetTag = valueSetType{}
+
+// Provider published by device_config_value_set
+type valueSetProviderData struct {
+	// The namespace of each of the
+	// (map of namespace --> device_config_module)
+	AvailableNamespaces map[string]android.Paths
+}
+
+var valueSetProviderKey = blueprint.NewProvider(valueSetProviderData{})
+
+func (module *ValueSetModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+	deps := ctx.AddDependency(ctx.Module(), valueSetTag, module.properties.Values...)
+	for _, dep := range deps {
+		_, ok := dep.(*ValuesModule)
+		if !ok {
+			ctx.PropertyErrorf("values", "values must be a device_config_values module")
+			return
+		}
+	}
+}
+
+func (module *ValueSetModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// Accumulate the namespaces of the values modules listed, and set that as an
+	// valueSetProviderKey provider that device_config modules can read and use
+	// to append values to their aconfig actions.
+	namespaces := make(map[string]android.Paths)
+	ctx.VisitDirectDeps(func(dep android.Module) {
+		if !ctx.OtherModuleHasProvider(dep, valuesProviderKey) {
+			// Other modules get injected as dependencies too, for example the license modules
+			return
+		}
+		depData := ctx.OtherModuleProvider(dep, valuesProviderKey).(valuesProviderData)
+
+		srcs := make([]android.Path, len(depData.Values))
+		copy(srcs, depData.Values)
+		namespaces[depData.Namespace] = srcs
+
+	})
+	ctx.SetProvider(valueSetProviderKey, valueSetProviderData{
+		AvailableNamespaces: namespaces,
+	})
+}
diff --git a/device_config/device_config_value_set_test.go b/device_config/device_config_value_set_test.go
new file mode 100644
index 0000000..f9e7c38
--- /dev/null
+++ b/device_config/device_config_value_set_test.go
@@ -0,0 +1,43 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package device_config
+
+import (
+	"testing"
+
+	"android/soong/android"
+)
+
+func TestDeviceConfigValueSet(t *testing.T) {
+	bp := `
+				device_config_values {
+					name: "one",
+					srcs: [ "blah.aconfig_values" ],
+					namespace: "foo.namespace"
+				}
+
+				device_config_value_set {
+					name: "module_name",
+          values: [ "one" ],
+				}
+			`
+	result := runTest(t, android.FixtureExpectsNoErrors, bp)
+
+	module := result.ModuleForTests("module_name", "").Module().(*ValueSetModule)
+
+	// Check that the provider has the right contents
+	depData := result.ModuleProvider(module, valueSetProviderKey).(valueSetProviderData)
+	android.AssertStringEquals(t, "AvailableNamespaces", "blah.aconfig_values", depData.AvailableNamespaces["foo.namespace"][0].String())
+}
diff --git a/device_config/device_config_values.go b/device_config/device_config_values.go
new file mode 100644
index 0000000..110f12a
--- /dev/null
+++ b/device_config/device_config_values.go
@@ -0,0 +1,70 @@
+// Copyright 2023 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 device_config
+
+import (
+	"android/soong/android"
+	"github.com/google/blueprint"
+)
+
+// Properties for "device_config_value"
+type ValuesModule struct {
+	android.ModuleBase
+	android.DefaultableModuleBase
+
+	properties struct {
+		// aconfig files, relative to this Android.bp file
+		Srcs []string `android:"path"`
+
+		// Release config flag namespace
+		Namespace string
+	}
+}
+
+func ValuesFactory() android.Module {
+	module := &ValuesModule{}
+
+	android.InitAndroidModule(module)
+	android.InitDefaultableModule(module)
+	module.AddProperties(&module.properties)
+	// TODO: bp2build
+	//android.InitBazelModule(module)
+
+	return module
+}
+
+// Provider published by device_config_value_set
+type valuesProviderData struct {
+	// The namespace that this values module values
+	Namespace string
+
+	// The values aconfig files, relative to the root of the tree
+	Values android.Paths
+}
+
+var valuesProviderKey = blueprint.NewProvider(valuesProviderData{})
+
+func (module *ValuesModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	if len(module.properties.Namespace) == 0 {
+		ctx.PropertyErrorf("namespace", "missing namespace property")
+	}
+
+	// Provide the our source files list to the device_config_value_set as a list of files
+	providerData := valuesProviderData{
+		Namespace: module.properties.Namespace,
+		Values:    android.PathsForModuleSrc(ctx, module.properties.Srcs),
+	}
+	ctx.SetProvider(valuesProviderKey, providerData)
+}
diff --git a/device_config/device_config_values_test.go b/device_config/device_config_values_test.go
new file mode 100644
index 0000000..64c57eb
--- /dev/null
+++ b/device_config/device_config_values_test.go
@@ -0,0 +1,39 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package device_config
+
+import (
+	"testing"
+
+	"android/soong/android"
+)
+
+func TestDeviceConfigValues(t *testing.T) {
+	bp := `
+				device_config_values {
+					name: "module_name",
+					srcs: [ "blah.aconfig_values" ],
+					namespace: "foo.namespace"
+				}
+			`
+	result := runTest(t, android.FixtureExpectsNoErrors, bp)
+
+	module := result.ModuleForTests("module_name", "").Module().(*ValuesModule)
+
+	// Check that the provider has the right contents
+	depData := result.ModuleProvider(module, valuesProviderKey).(valuesProviderData)
+	android.AssertStringEquals(t, "namespace", "foo.namespace", depData.Namespace)
+	android.AssertPathsEndWith(t, "srcs", []string{"blah.aconfig_values"}, depData.Values)
+}
diff --git a/device_config/init.go b/device_config/init.go
new file mode 100644
index 0000000..04bbab6
--- /dev/null
+++ b/device_config/init.go
@@ -0,0 +1,70 @@
+// Copyright 2023 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 device_config
+
+import (
+	"android/soong/android"
+	"github.com/google/blueprint"
+)
+
+var (
+	pctx = android.NewPackageContext("android/soong/device_config")
+
+	// For device_config_definitions: Generate cache file
+	aconfigRule = pctx.AndroidStaticRule("aconfig",
+		blueprint.RuleParams{
+			Command: `${aconfig} create-cache` +
+				` --package ${namespace}` +
+				` --declarations ${in}` +
+				` ${values}` +
+				` --cache ${out}.tmp` +
+				` && ( if cmp -s ${out}.tmp ; then rm ${out}.tmp ; else mv ${out}.tmp ${out} ; fi )`,
+			//				` --build-id ${release_version}` +
+			CommandDeps: []string{
+				"${aconfig}",
+			},
+			Restat: true,
+		}, "release_version", "namespace", "values")
+
+	// For java_device_config_definitions_library: Generate java file
+	srcJarRule = pctx.AndroidStaticRule("aconfig_srcjar",
+		blueprint.RuleParams{
+			Command: `rm -rf ${out}.tmp` +
+				` && mkdir -p ${out}.tmp` +
+				` && ${aconfig} create-java-lib` +
+				`    --cache ${in}` +
+				`    --out ${out}.tmp` +
+				` && $soong_zip -write_if_changed -jar -o ${out} -C ${out}.tmp -D ${out}.tmp` +
+				` && rm -rf ${out}.tmp`,
+			CommandDeps: []string{
+				"$aconfig",
+				"$soong_zip",
+			},
+			Restat: true,
+		})
+)
+
+func init() {
+	registerBuildComponents(android.InitRegistrationContext)
+	pctx.HostBinToolVariable("aconfig", "aconfig")
+	pctx.HostBinToolVariable("soong_zip", "soong_zip")
+}
+
+func registerBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("device_config_definitions", DefinitionsFactory)
+	ctx.RegisterModuleType("device_config_values", ValuesFactory)
+	ctx.RegisterModuleType("device_config_value_set", ValueSetFactory)
+	ctx.RegisterModuleType("java_device_config_definitions_library", JavaDefinitionsLibraryFactory)
+}
diff --git a/device_config/java_device_config_definitions_library.go b/device_config/java_device_config_definitions_library.go
new file mode 100644
index 0000000..6e48ece
--- /dev/null
+++ b/device_config/java_device_config_definitions_library.go
@@ -0,0 +1,71 @@
+// Copyright 2023 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 device_config
+
+import (
+	"android/soong/android"
+	"android/soong/java"
+	"fmt"
+	"github.com/google/blueprint"
+)
+
+type definitionsTagType struct {
+	blueprint.BaseDependencyTag
+}
+
+var definitionsTag = definitionsTagType{}
+
+type JavaDeviceConfigDefinitionsLibraryProperties struct {
+	// name of the device_config_definitions module to generate a library for
+	Device_config_definitions string
+}
+
+type JavaDeviceConfigDefinitionsLibraryCallbacks struct {
+	properties JavaDeviceConfigDefinitionsLibraryProperties
+}
+
+func JavaDefinitionsLibraryFactory() android.Module {
+	callbacks := &JavaDeviceConfigDefinitionsLibraryCallbacks{}
+	return java.GeneratedJavaLibraryModuleFactory("java_device_config_definitions_library", callbacks, &callbacks.properties)
+}
+
+func (callbacks *JavaDeviceConfigDefinitionsLibraryCallbacks) DepsMutator(module *java.GeneratedJavaLibraryModule, ctx android.BottomUpMutatorContext) {
+	definitions := callbacks.properties.Device_config_definitions
+	if len(definitions) == 0 {
+		// TODO: Add test for this case
+		ctx.PropertyErrorf("device_config_definitions", "device_config_definitions property required")
+	} else {
+		ctx.AddDependency(ctx.Module(), definitionsTag, definitions)
+	}
+}
+
+func (callbacks *JavaDeviceConfigDefinitionsLibraryCallbacks) GenerateSourceJarBuildActions(ctx android.ModuleContext) android.Path {
+	// Get the values that came from the global RELEASE_DEVICE_CONFIG_VALUE_SETS flag
+	definitionsModules := ctx.GetDirectDepsWithTag(definitionsTag)
+	if len(definitionsModules) != 1 {
+		panic(fmt.Errorf("Exactly one device_config_definitions property required"))
+	}
+	definitions := ctx.OtherModuleProvider(definitionsModules[0], definitionsProviderKey).(definitionsProviderData)
+
+	srcJarPath := android.PathForModuleGen(ctx, ctx.ModuleName()+".srcjar")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        srcJarRule,
+		Input:       definitions.intermediatePath,
+		Output:      srcJarPath,
+		Description: "device_config.srcjar",
+	})
+
+	return srcJarPath
+}
diff --git a/device_config/testing.go b/device_config/testing.go
new file mode 100644
index 0000000..284a7fa
--- /dev/null
+++ b/device_config/testing.go
@@ -0,0 +1,29 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package device_config
+
+import (
+	"testing"
+
+	"android/soong/android"
+)
+
+var PrepareForTestWithDeviceConfigBuildComponents = android.FixtureRegisterWithContext(registerBuildComponents)
+
+func runTest(t *testing.T, errorHandler android.FixtureErrorHandler, bp string) *android.TestResult {
+	return android.GroupFixturePreparers(PrepareForTestWithDeviceConfigBuildComponents).
+		ExtendWithErrorHandler(errorHandler).
+		RunTestWithBp(t, bp)
+}
diff --git a/dexpreopt/class_loader_context.go b/dexpreopt/class_loader_context.go
index afb3de3..6ead589 100644
--- a/dexpreopt/class_loader_context.go
+++ b/dexpreopt/class_loader_context.go
@@ -17,11 +17,11 @@
 import (
 	"encoding/json"
 	"fmt"
-	"sort"
 	"strconv"
-	"strings"
 
 	"android/soong/android"
+
+	"github.com/google/blueprint/proptools"
 )
 
 // This comment describes the following:
@@ -310,8 +310,8 @@
 	// Nested class loader context shouldn't have conditional part (it is allowed only at the top level).
 	for ver, _ := range nestedClcMap {
 		if ver != AnySdkVersion {
-			clcStr, _ := ComputeClassLoaderContext(nestedClcMap)
-			return fmt.Errorf("nested class loader context shouldn't have conditional part: %s", clcStr)
+			clcPaths := ComputeClassLoaderContextDependencies(nestedClcMap)
+			return fmt.Errorf("nested class loader context shouldn't have conditional part: %+v", clcPaths)
 		}
 	}
 	subcontexts := nestedClcMap[AnySdkVersion]
@@ -418,6 +418,15 @@
 	return string(bytes)
 }
 
+func (clcMap ClassLoaderContextMap) DumpForFlag() string {
+	jsonCLC := toJsonClassLoaderContext(clcMap)
+	bytes, err := json.Marshal(jsonCLC)
+	if err != nil {
+		panic(err)
+	}
+	return proptools.ShellEscapeIncludingSpaces(string(bytes))
+}
+
 // excludeLibsFromCLCList excludes the libraries from the ClassLoaderContext in this list.
 //
 // This treats the supplied list as being immutable (as it may come from a dependency). So, it
@@ -544,67 +553,27 @@
 	return true, nil
 }
 
-// Return the class loader context as a string, and a slice of build paths for all dependencies.
+// Returns a slice of build paths for all possible dependencies that the class loader context may
+// refer to.
 // Perform a depth-first preorder traversal of the class loader context tree for each SDK version.
-// Return the resulting string and a slice of on-host build paths to all library dependencies.
-func ComputeClassLoaderContext(clcMap ClassLoaderContextMap) (clcStr string, paths android.Paths) {
-	// CLC for different SDK versions should come in specific order that agrees with PackageManager.
-	// Since PackageManager processes SDK versions in ascending order and prepends compatibility
-	// libraries at the front, the required order is descending, except for AnySdkVersion that has
-	// numerically the largest order, but must be the last one. Example of correct order: [30, 29,
-	// 28, AnySdkVersion]. There are Soong tests to ensure that someone doesn't change this by
-	// accident, but there is no way to guard against changes in the PackageManager, except for
-	// grepping logcat on the first boot for absence of the following messages:
-	//
-	//   `logcat | grep -E 'ClassLoaderContext [a-z ]+ mismatch`
-	//
-	versions := make([]int, 0, len(clcMap))
-	for ver, _ := range clcMap {
-		if ver != AnySdkVersion {
-			versions = append(versions, ver)
-		}
-	}
-	sort.Sort(sort.Reverse(sort.IntSlice(versions))) // descending order
-	versions = append(versions, AnySdkVersion)
-
-	for _, sdkVer := range versions {
-		sdkVerStr := fmt.Sprintf("%d", sdkVer)
-		if sdkVer == AnySdkVersion {
-			sdkVerStr = "any" // a special keyword that means any SDK version
-		}
-		hostClc, targetClc, hostPaths := computeClassLoaderContextRec(clcMap[sdkVer])
-		if hostPaths != nil {
-			clcStr += fmt.Sprintf(" --host-context-for-sdk %s %s", sdkVerStr, hostClc)
-			clcStr += fmt.Sprintf(" --target-context-for-sdk %s %s", sdkVerStr, targetClc)
-		}
+func ComputeClassLoaderContextDependencies(clcMap ClassLoaderContextMap) android.Paths {
+	var paths android.Paths
+	for _, clcs := range clcMap {
+		hostPaths := ComputeClassLoaderContextDependenciesRec(clcs)
 		paths = append(paths, hostPaths...)
 	}
-	return clcStr, android.FirstUniquePaths(paths)
+	return android.FirstUniquePaths(paths)
 }
 
-// Helper function for ComputeClassLoaderContext() that handles recursion.
-func computeClassLoaderContextRec(clcs []*ClassLoaderContext) (string, string, android.Paths) {
+// Helper function for ComputeClassLoaderContextDependencies() that handles recursion.
+func ComputeClassLoaderContextDependenciesRec(clcs []*ClassLoaderContext) android.Paths {
 	var paths android.Paths
-	var clcsHost, clcsTarget []string
-
 	for _, clc := range clcs {
-		subClcHost, subClcTarget, subPaths := computeClassLoaderContextRec(clc.Subcontexts)
-		if subPaths != nil {
-			subClcHost = "{" + subClcHost + "}"
-			subClcTarget = "{" + subClcTarget + "}"
-		}
-
-		clcsHost = append(clcsHost, "PCL["+clc.Host.String()+"]"+subClcHost)
-		clcsTarget = append(clcsTarget, "PCL["+clc.Device+"]"+subClcTarget)
-
+		subPaths := ComputeClassLoaderContextDependenciesRec(clc.Subcontexts)
 		paths = append(paths, clc.Host)
 		paths = append(paths, subPaths...)
 	}
-
-	clcHost := strings.Join(clcsHost, "#")
-	clcTarget := strings.Join(clcsTarget, "#")
-
-	return clcHost, clcTarget, paths
+	return paths
 }
 
 // Class loader contexts that come from Make via JSON dexpreopt.config. JSON CLC representation is
diff --git a/dexpreopt/class_loader_context_test.go b/dexpreopt/class_loader_context_test.go
index 8b3c013..39b4652 100644
--- a/dexpreopt/class_loader_context_test.go
+++ b/dexpreopt/class_loader_context_test.go
@@ -20,6 +20,7 @@
 import (
 	"fmt"
 	"reflect"
+	"sort"
 	"strings"
 	"testing"
 
@@ -34,7 +35,7 @@
 	// │   └── android.hidl.base
 	// │
 	// └── any
-	//     ├── a
+	//     ├── a'  (a single quotation mark (') is there to test escaping)
 	//     ├── b
 	//     ├── c
 	//     ├── d
@@ -53,7 +54,7 @@
 
 	m := make(ClassLoaderContextMap)
 
-	m.AddContext(ctx, AnySdkVersion, "a", optional, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
+	m.AddContext(ctx, AnySdkVersion, "a'", optional, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
 	m.AddContext(ctx, AnySdkVersion, "b", optional, buildPath(ctx, "b"), installPath(ctx, "b"), nil)
 	m.AddContext(ctx, AnySdkVersion, "c", optional, buildPath(ctx, "c"), installPath(ctx, "c"), nil)
 
@@ -96,11 +97,10 @@
 
 	fixClassLoaderContext(m)
 
-	var haveStr string
 	var havePaths android.Paths
 	var haveUsesLibsReq, haveUsesLibsOpt []string
 	if valid && validationError == nil {
-		haveStr, havePaths = ComputeClassLoaderContext(m)
+		havePaths = ComputeClassLoaderContextDependencies(m)
 		haveUsesLibsReq, haveUsesLibsOpt = m.UsesLibs()
 	}
 
@@ -111,29 +111,6 @@
 		}
 	})
 
-	// Test that class loader context structure is correct.
-	t.Run("string", func(t *testing.T) {
-		wantStr := " --host-context-for-sdk 29 " +
-			"PCL[out/soong/" + AndroidHidlManager + ".jar]#" +
-			"PCL[out/soong/" + AndroidHidlBase + ".jar]" +
-			" --target-context-for-sdk 29 " +
-			"PCL[/system/framework/" + AndroidHidlManager + ".jar]#" +
-			"PCL[/system/framework/" + AndroidHidlBase + ".jar]" +
-			" --host-context-for-sdk any " +
-			"PCL[out/soong/a.jar]#PCL[out/soong/b.jar]#PCL[out/soong/c.jar]#PCL[out/soong/d.jar]" +
-			"{PCL[out/soong/a2.jar]#PCL[out/soong/b2.jar]#PCL[out/soong/c2.jar]" +
-			"{PCL[out/soong/a1.jar]#PCL[out/soong/b1.jar]}}#" +
-			"PCL[out/soong/f.jar]#PCL[out/soong/a3.jar]#PCL[out/soong/b3.jar]" +
-			" --target-context-for-sdk any " +
-			"PCL[/system/a.jar]#PCL[/system/b.jar]#PCL[/system/c.jar]#PCL[/system/d.jar]" +
-			"{PCL[/system/a2.jar]#PCL[/system/b2.jar]#PCL[/system/c2.jar]" +
-			"{PCL[/system/a1.jar]#PCL[/system/b1.jar]}}#" +
-			"PCL[/system/f.jar]#PCL[/system/a3.jar]#PCL[/system/b3.jar]"
-		if wantStr != haveStr {
-			t.Errorf("\nwant class loader context: %s\nhave class loader context: %s", wantStr, haveStr)
-		}
-	})
-
 	// Test that all expected build paths are gathered.
 	t.Run("paths", func(t *testing.T) {
 		wantPaths := []string{
@@ -143,14 +120,28 @@
 			"out/soong/a1.jar", "out/soong/b1.jar",
 			"out/soong/f.jar", "out/soong/a3.jar", "out/soong/b3.jar",
 		}
-		if !reflect.DeepEqual(wantPaths, havePaths.Strings()) {
-			t.Errorf("\nwant paths: %s\nhave paths: %s", wantPaths, havePaths)
-		}
+		actual := havePaths.Strings()
+		// The order does not matter.
+		sort.Strings(wantPaths)
+		sort.Strings(actual)
+		android.AssertArrayString(t, "", wantPaths, actual)
+	})
+
+	// Test the JSON passed to construct_context.py.
+	t.Run("json", func(t *testing.T) {
+		// The tree structure within each SDK version should be kept exactly the same when serialized
+		// to JSON. The order matters because the Python script keeps the order within each SDK version
+		// as is.
+		// The JSON is passed to the Python script as a commandline flag, so quotation ('') and escaping
+		// must be performed.
+		android.AssertStringEquals(t, "", strings.TrimSpace(`
+'{"29":[{"Name":"android.hidl.manager-V1.0-java","Optional":false,"Host":"out/soong/android.hidl.manager-V1.0-java.jar","Device":"/system/framework/android.hidl.manager-V1.0-java.jar","Subcontexts":[]},{"Name":"android.hidl.base-V1.0-java","Optional":false,"Host":"out/soong/android.hidl.base-V1.0-java.jar","Device":"/system/framework/android.hidl.base-V1.0-java.jar","Subcontexts":[]}],"30":[],"42":[],"any":[{"Name":"a'\''","Optional":false,"Host":"out/soong/a.jar","Device":"/system/a.jar","Subcontexts":[]},{"Name":"b","Optional":false,"Host":"out/soong/b.jar","Device":"/system/b.jar","Subcontexts":[]},{"Name":"c","Optional":false,"Host":"out/soong/c.jar","Device":"/system/c.jar","Subcontexts":[]},{"Name":"d","Optional":false,"Host":"out/soong/d.jar","Device":"/system/d.jar","Subcontexts":[{"Name":"a2","Optional":false,"Host":"out/soong/a2.jar","Device":"/system/a2.jar","Subcontexts":[]},{"Name":"b2","Optional":false,"Host":"out/soong/b2.jar","Device":"/system/b2.jar","Subcontexts":[]},{"Name":"c2","Optional":false,"Host":"out/soong/c2.jar","Device":"/system/c2.jar","Subcontexts":[{"Name":"a1","Optional":false,"Host":"out/soong/a1.jar","Device":"/system/a1.jar","Subcontexts":[]},{"Name":"b1","Optional":false,"Host":"out/soong/b1.jar","Device":"/system/b1.jar","Subcontexts":[]}]}]},{"Name":"f","Optional":false,"Host":"out/soong/f.jar","Device":"/system/f.jar","Subcontexts":[]},{"Name":"a3","Optional":false,"Host":"out/soong/a3.jar","Device":"/system/a3.jar","Subcontexts":[]},{"Name":"b3","Optional":false,"Host":"out/soong/b3.jar","Device":"/system/b3.jar","Subcontexts":[]}]}'
+`), m.DumpForFlag())
 	})
 
 	// Test for libraries that are added by the manifest_fixer.
 	t.Run("uses libs", func(t *testing.T) {
-		wantUsesLibsReq := []string{"a", "b", "c", "d", "f", "a3", "b3"}
+		wantUsesLibsReq := []string{"a'", "b", "c", "d", "f", "a3", "b3"}
 		wantUsesLibsOpt := []string{}
 		if !reflect.DeepEqual(wantUsesLibsReq, haveUsesLibsReq) {
 			t.Errorf("\nwant required uses libs: %s\nhave required uses libs: %s", wantUsesLibsReq, haveUsesLibsReq)
@@ -236,49 +227,6 @@
 	checkError(t, err, "nested class loader context shouldn't have conditional part")
 }
 
-// Test for SDK version order in conditional CLC: no matter in what order the libraries are added,
-// they end up in the order that agrees with PackageManager.
-func TestCLCSdkVersionOrder(t *testing.T) {
-	ctx := testContext()
-	optional := false
-	m := make(ClassLoaderContextMap)
-	m.AddContext(ctx, 28, "a", optional, buildPath(ctx, "a"), installPath(ctx, "a"), nil)
-	m.AddContext(ctx, 29, "b", optional, buildPath(ctx, "b"), installPath(ctx, "b"), nil)
-	m.AddContext(ctx, 30, "c", optional, buildPath(ctx, "c"), installPath(ctx, "c"), nil)
-	m.AddContext(ctx, AnySdkVersion, "d", optional, buildPath(ctx, "d"), installPath(ctx, "d"), nil)
-
-	valid, validationError := validateClassLoaderContext(m)
-
-	fixClassLoaderContext(m)
-
-	var haveStr string
-	if valid && validationError == nil {
-		haveStr, _ = ComputeClassLoaderContext(m)
-	}
-
-	// Test that validation is successful (all paths are known).
-	t.Run("validate", func(t *testing.T) {
-		if !(valid && validationError == nil) {
-			t.Errorf("invalid class loader context")
-		}
-	})
-
-	// Test that class loader context structure is correct.
-	t.Run("string", func(t *testing.T) {
-		wantStr := " --host-context-for-sdk 30 PCL[out/soong/c.jar]" +
-			" --target-context-for-sdk 30 PCL[/system/c.jar]" +
-			" --host-context-for-sdk 29 PCL[out/soong/b.jar]" +
-			" --target-context-for-sdk 29 PCL[/system/b.jar]" +
-			" --host-context-for-sdk 28 PCL[out/soong/a.jar]" +
-			" --target-context-for-sdk 28 PCL[/system/a.jar]" +
-			" --host-context-for-sdk any PCL[out/soong/d.jar]" +
-			" --target-context-for-sdk any PCL[/system/d.jar]"
-		if wantStr != haveStr {
-			t.Errorf("\nwant class loader context: %s\nhave class loader context: %s", wantStr, haveStr)
-		}
-	})
-}
-
 func TestCLCMExcludeLibs(t *testing.T) {
 	ctx := testContext()
 	const optional = false
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index 609a29c..e61ebe6 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -197,7 +197,7 @@
 
 func init() {
 	pctx.Import("android/soong/android")
-	android.RegisterSingletonType("dexpreopt-soong-config", func() android.Singleton {
+	android.RegisterParallelSingletonType("dexpreopt-soong-config", func() android.Singleton {
 		return &globalSoongConfigSingleton{}
 	})
 }
@@ -475,7 +475,16 @@
 	ctx.AddFarVariationDependencies(v, Dex2oatDepTag, dex2oatBin)
 }
 
+func IsDex2oatNeeded(ctx android.PathContext) bool {
+	global := GetGlobalConfig(ctx)
+	return !global.DisablePreopt || !global.DisablePreoptBootImages
+}
+
 func dex2oatPathFromDep(ctx android.ModuleContext) android.Path {
+	if !IsDex2oatNeeded(ctx) {
+		return nil
+	}
+
 	dex2oatBin := dex2oatModuleName(ctx.Config())
 
 	// Find the right dex2oat module, trying to follow PrebuiltDepTag from source
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index a590c72..20737e3 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -52,7 +52,8 @@
 // GenerateDexpreoptRule generates a set of commands that will preopt a module based on a GlobalConfig and a
 // ModuleConfig.  The produced files and their install locations will be available through rule.Installs().
 func GenerateDexpreoptRule(ctx android.BuilderContext, globalSoong *GlobalSoongConfig,
-	global *GlobalConfig, module *ModuleConfig) (rule *android.RuleBuilder, err error) {
+	global *GlobalConfig, module *ModuleConfig, productPackages android.Path) (
+	rule *android.RuleBuilder, err error) {
 
 	defer func() {
 		if r := recover(); r != nil {
@@ -92,7 +93,8 @@
 			generateDM := shouldGenerateDM(module, global)
 
 			for archIdx, _ := range module.Archs {
-				dexpreoptCommand(ctx, globalSoong, global, module, rule, archIdx, profile, appImage, generateDM)
+				dexpreoptCommand(ctx, globalSoong, global, module, rule, archIdx, profile, appImage,
+					generateDM, productPackages)
 			}
 		}
 	}
@@ -100,11 +102,19 @@
 	return rule, nil
 }
 
+// If dexpreopt is applicable to the module, returns whether dexpreopt is disabled. Otherwise, the
+// behavior is undefined.
+// When it returns true, dexpreopt artifacts will not be generated, but profile will still be
+// generated if profile-guided compilation is requested.
 func dexpreoptDisabled(ctx android.PathContext, global *GlobalConfig, module *ModuleConfig) bool {
 	if ctx.Config().UnbundledBuild() {
 		return true
 	}
 
+	if global.DisablePreopt {
+		return true
+	}
+
 	if contains(global.DisablePreoptModules, module.Name) {
 		return true
 	}
@@ -224,9 +234,9 @@
 		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) {
+func dexpreoptCommand(ctx android.BuilderContext, globalSoong *GlobalSoongConfig,
+	global *GlobalConfig, module *ModuleConfig, rule *android.RuleBuilder, archIdx int,
+	profile android.WritablePath, appImage bool, generateDM bool, productPackages android.Path) {
 
 	arch := module.Archs[archIdx]
 
@@ -343,11 +353,13 @@
 		}
 
 		// Generate command that saves host and target class loader context in shell variables.
-		clc, paths := ComputeClassLoaderContext(module.ClassLoaderContexts)
+		paths := ComputeClassLoaderContextDependencies(module.ClassLoaderContexts)
 		rule.Command().
 			Text(`eval "$(`).Tool(globalSoong.ConstructContext).
 			Text(` --target-sdk-version ${target_sdk_version}`).
-			Text(clc).Implicits(paths).
+			FlagWithArg("--context-json=", module.ClassLoaderContexts.DumpForFlag()).
+			FlagWithInput("--product-packages=", productPackages).
+			Implicits(paths).
 			Text(`)"`)
 	}
 
diff --git a/dexpreopt/dexpreopt_gen/dexpreopt_gen.go b/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
index ba05d94..8033b48 100644
--- a/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
+++ b/dexpreopt/dexpreopt_gen/dexpreopt_gen.go
@@ -42,7 +42,8 @@
 	// The flag is useful when running dex2oat on system image and vendor image which are built separately.
 	usesTargetFiles = flag.Bool("uses_target_files", false, "whether or not dexpreopt is running on target_files")
 	// basePath indicates the path where target_files.zip is extracted.
-	basePath = flag.String("base_path", ".", "base path where images and tools are extracted")
+	basePath            = flag.String("base_path", ".", "base path where images and tools are extracted")
+	productPackagesPath = flag.String("product_packages", "", "path to product_packages.txt")
 )
 
 type builderContext struct {
@@ -87,6 +88,10 @@
 		usage("--module configuration file is required")
 	}
 
+	if *productPackagesPath == "" {
+		usage("--product_packages configuration file is required")
+	}
+
 	// NOTE: duplicating --out_dir here is incorrect (one should be the another
 	// plus "/soong" but doing so apparently breaks dexpreopt
 	ctx := &builderContext{android.NullConfig(*outDir, *outDir)}
@@ -159,11 +164,12 @@
 			moduleConfig.DexPreoptImageLocationsOnHost[i] = *basePath + location
 		}
 	}
-	writeScripts(ctx, globalSoongConfig, globalConfig, moduleConfig, *dexpreoptScriptPath)
+	writeScripts(ctx, globalSoongConfig, globalConfig, moduleConfig, *dexpreoptScriptPath, *productPackagesPath)
 }
 
 func writeScripts(ctx android.BuilderContext, globalSoong *dexpreopt.GlobalSoongConfig,
-	global *dexpreopt.GlobalConfig, module *dexpreopt.ModuleConfig, dexpreoptScriptPath string) {
+	global *dexpreopt.GlobalConfig, module *dexpreopt.ModuleConfig, dexpreoptScriptPath string,
+	productPackagesPath string) {
 	write := func(rule *android.RuleBuilder, file string) {
 		script := &bytes.Buffer{}
 		script.WriteString(scriptHeader)
@@ -199,7 +205,8 @@
 			panic(err)
 		}
 	}
-	dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(ctx, globalSoong, global, module)
+	dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(
+		ctx, globalSoong, global, module, android.PathForTesting(productPackagesPath))
 	if err != nil {
 		panic(err)
 	}
diff --git a/dexpreopt/dexpreopt_test.go b/dexpreopt/dexpreopt_test.go
index 429b5ff..2b19c9d 100644
--- a/dexpreopt/dexpreopt_test.go
+++ b/dexpreopt/dexpreopt_test.go
@@ -100,8 +100,9 @@
 	globalSoong := globalSoongConfigForTests()
 	global := GlobalConfigForTests(ctx)
 	module := testSystemModuleConfig(ctx, "test")
+	productPackages := android.PathForTesting("product_packages.txt")
 
-	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module)
+	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -124,6 +125,7 @@
 	systemModule := testSystemModuleConfig(ctx, "Stest")
 	systemProductModule := testSystemProductModuleConfig(ctx, "SPtest")
 	productModule := testProductModuleConfig(ctx, "Ptest")
+	productPackages := android.PathForTesting("product_packages.txt")
 
 	global.HasSystemOther = true
 
@@ -157,7 +159,7 @@
 	for _, test := range tests {
 		global.PatternsOnSystemOther = test.patterns
 		for _, mt := range test.moduleTests {
-			rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, mt.module)
+			rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, mt.module, productPackages)
 			if err != nil {
 				t.Fatal(err)
 			}
@@ -182,11 +184,12 @@
 	globalSoong := globalSoongConfigForTests()
 	global := GlobalConfigForTests(ctx)
 	module := testApexModuleConfig(ctx, "service-A", "com.android.apex1")
+	productPackages := android.PathForTesting("product_packages.txt")
 
 	global.ApexSystemServerJars = android.CreateTestConfiguredJarList(
 		[]string{"com.android.apex1:service-A"})
 
-	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module)
+	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -205,11 +208,12 @@
 	globalSoong := globalSoongConfigForTests()
 	global := GlobalConfigForTests(ctx)
 	module := testPlatformSystemServerModuleConfig(ctx, "service-A")
+	productPackages := android.PathForTesting("product_packages.txt")
 
 	global.StandaloneSystemServerJars = android.CreateTestConfiguredJarList(
 		[]string{"platform:service-A"})
 
-	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module)
+	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -228,11 +232,12 @@
 	globalSoong := globalSoongConfigForTests()
 	global := GlobalConfigForTests(ctx)
 	module := testSystemExtSystemServerModuleConfig(ctx, "service-A")
+	productPackages := android.PathForTesting("product_packages.txt")
 
 	global.StandaloneSystemServerJars = android.CreateTestConfiguredJarList(
 		[]string{"system_ext:service-A"})
 
-	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module)
+	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -251,11 +256,12 @@
 	globalSoong := globalSoongConfigForTests()
 	global := GlobalConfigForTests(ctx)
 	module := testApexModuleConfig(ctx, "service-A", "com.android.apex1")
+	productPackages := android.PathForTesting("product_packages.txt")
 
 	global.ApexStandaloneSystemServerJars = android.CreateTestConfiguredJarList(
 		[]string{"com.android.apex1:service-A"})
 
-	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module)
+	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages)
 	if err != nil {
 		t.Fatal(err)
 	}
@@ -274,10 +280,11 @@
 	globalSoong := globalSoongConfigForTests()
 	global := GlobalConfigForTests(ctx)
 	module := testSystemModuleConfig(ctx, "test")
+	productPackages := android.PathForTesting("product_packages.txt")
 
 	module.ProfileClassListing = android.OptionalPathForPath(android.PathForTesting("profile"))
 
-	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module)
+	rule, err := GenerateDexpreoptRule(ctx, globalSoong, global, module, productPackages)
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/dexpreopt/testing.go b/dexpreopt/testing.go
index 47ae494..6ed0736 100644
--- a/dexpreopt/testing.go
+++ b/dexpreopt/testing.go
@@ -174,3 +174,17 @@
 		dexpreoptConfig.DisableGenerateProfile = disable
 	})
 }
+
+// FixtureDisableDexpreoptBootImages sets the DisablePreoptBootImages property in the global config.
+func FixtureDisableDexpreoptBootImages(disable bool) android.FixturePreparer {
+	return FixtureModifyGlobalConfig(func(_ android.PathContext, dexpreoptConfig *GlobalConfig) {
+		dexpreoptConfig.DisablePreoptBootImages = disable
+	})
+}
+
+// FixtureDisableDexpreopt sets the DisablePreopt property in the global config.
+func FixtureDisableDexpreopt(disable bool) android.FixturePreparer {
+	return FixtureModifyGlobalConfig(func(_ android.PathContext, dexpreoptConfig *GlobalConfig) {
+		dexpreoptConfig.DisablePreopt = disable
+	})
+}
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index dcd7fdc..370a423 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -38,6 +38,7 @@
 
 	"android/soong/android"
 	"android/soong/bazel"
+	"android/soong/bazel/cquery"
 	"android/soong/snapshot"
 )
 
@@ -329,7 +330,6 @@
 		ctx.PropertyErrorf("src", "missing prebuilt source file")
 		return
 	}
-	p.outputFilePath = android.PathForModuleOut(ctx, filename).OutputPath
 
 	if strings.Contains(filename, "/") {
 		ctx.PropertyErrorf("filename", "filename cannot contain separator '/'")
@@ -349,21 +349,42 @@
 	}
 	p.installDirPath = android.PathForModuleInstall(ctx, installBaseDir, p.SubDir())
 
-	// This ensures that outputFilePath has the correct name for others to
-	// use, as the source file may have a different name.
-	ctx.Build(pctx, android.BuildParams{
-		Rule:   android.Cp,
-		Output: p.outputFilePath,
-		Input:  p.sourceFilePath,
-	})
+	// Call InstallFile even when uninstallable to make the module included in the package
+	ip := installProperties{
+		installable:    p.Installable(),
+		filename:       filename,
+		sourceFilePath: p.sourceFilePath,
+		symlinks:       p.properties.Symlinks,
+	}
+	p.addInstallRules(ctx, ip)
+}
 
-	if !p.Installable() {
+type installProperties struct {
+	installable    bool
+	filename       string
+	sourceFilePath android.Path
+	symlinks       []string
+}
+
+// utility function to add install rules to the build graph.
+// Reduces code duplication between Soong and Mixed build analysis
+func (p *PrebuiltEtc) addInstallRules(ctx android.ModuleContext, ip installProperties) {
+	if !ip.installable {
 		p.SkipInstall()
 	}
 
-	// Call InstallFile even when uninstallable to make the module included in the package
-	installPath := ctx.InstallFile(p.installDirPath, p.outputFilePath.Base(), p.outputFilePath)
-	for _, sl := range p.properties.Symlinks {
+	// Copy the file from src to a location in out/ with the correct `filename`
+	// This ensures that outputFilePath has the correct name for others to
+	// use, as the source file may have a different name.
+	p.outputFilePath = android.PathForModuleOut(ctx, ip.filename).OutputPath
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   android.Cp,
+		Output: p.outputFilePath,
+		Input:  ip.sourceFilePath,
+	})
+
+	installPath := ctx.InstallFile(p.installDirPath, ip.filename, p.outputFilePath)
+	for _, sl := range ip.symlinks {
 		ctx.InstallSymlink(p.installDirPath, sl, installPath)
 	}
 }
@@ -617,7 +638,7 @@
 	return true
 }
 
-func generatePrebuiltSnapshot(s snapshot.SnapshotSingleton, ctx android.SingletonContext, snapshotArchDir string) android.Paths {
+func generatePrebuiltSnapshot(s snapshot.SnapshotSingleton, ctx android.SingletonContext, snapshotArchDir string) snapshot.SnapshotPaths {
 	/*
 		Snapshot zipped artifacts directory structure for etc modules:
 		{SNAPSHOT_ARCH}/
@@ -631,7 +652,7 @@
 				(notice files)
 	*/
 	var snapshotOutputs android.Paths
-	noticeDir := filepath.Join(snapshotArchDir, "NOTICE_FILES")
+	var snapshotNotices android.Paths
 	installedNotices := make(map[string]bool)
 
 	ctx.VisitAllModules(func(module android.Module) {
@@ -651,10 +672,8 @@
 
 		prop := snapshot.SnapshotJsonFlags{}
 		propOut := snapshotLibOut + ".json"
-		prop.ModuleName = m.BaseModuleName()
-		if m.subdirProperties.Relative_install_path != nil {
-			prop.RelativeInstallPath = *m.subdirProperties.Relative_install_path
-		}
+		prop.InitBaseSnapshotProps(m)
+		prop.RelativeInstallPath = m.SubDir()
 
 		if m.properties.Filename != nil {
 			prop.Filename = *m.properties.Filename
@@ -667,27 +686,16 @@
 		}
 		snapshotOutputs = append(snapshotOutputs, snapshot.WriteStringToFileRule(ctx, string(j), propOut))
 
-		if len(m.EffectiveLicenseFiles()) > 0 {
-			noticeName := ctx.ModuleName(m) + ".txt"
-			noticeOut := filepath.Join(noticeDir, noticeName)
-			// skip already copied notice file
-			if !installedNotices[noticeOut] {
-				installedNotices[noticeOut] = true
-
-				noticeOutPath := android.PathForOutput(ctx, noticeOut)
-				ctx.Build(pctx, android.BuildParams{
-					Rule:        android.Cat,
-					Inputs:      m.EffectiveLicenseFiles(),
-					Output:      noticeOutPath,
-					Description: "combine notices for " + noticeOut,
-				})
-				snapshotOutputs = append(snapshotOutputs, noticeOutPath)
+		for _, notice := range m.EffectiveLicenseFiles() {
+			if _, ok := installedNotices[notice.String()]; !ok {
+				installedNotices[notice.String()] = true
+				snapshotNotices = append(snapshotNotices, notice)
 			}
 		}
 
 	})
 
-	return snapshotOutputs
+	return snapshot.SnapshotPaths{OutputFiles: snapshotOutputs, NoticeFiles: snapshotNotices}
 }
 
 // For Bazel / bp2build
@@ -794,3 +802,38 @@
 
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name()}, attrs)
 }
+
+var _ android.MixedBuildBuildable = (*PrebuiltEtc)(nil)
+
+func (pe *PrebuiltEtc) IsMixedBuildSupported(ctx android.BaseModuleContext) bool {
+	return true
+}
+
+func (pe *PrebuiltEtc) QueueBazelCall(ctx android.BaseModuleContext) {
+	ctx.Config().BazelContext.QueueBazelRequest(
+		pe.GetBazelLabel(ctx, pe),
+		cquery.GetPrebuiltFileInfo,
+		android.GetConfigKey(ctx),
+	)
+}
+
+func (pe *PrebuiltEtc) ProcessBazelQueryResponse(ctx android.ModuleContext) {
+	bazelCtx := ctx.Config().BazelContext
+	pfi, err := bazelCtx.GetPrebuiltFileInfo(pe.GetBazelLabel(ctx, pe), android.GetConfigKey(ctx))
+	if err != nil {
+		ctx.ModuleErrorf(err.Error())
+		return
+	}
+
+	// Set properties for androidmk
+	pe.installDirPath = android.PathForModuleInstall(ctx, pfi.Dir)
+
+	// Installation rules
+	ip := installProperties{
+		installable:    pfi.Installable,
+		filename:       pfi.Filename,
+		sourceFilePath: android.PathForSource(ctx, pfi.Src),
+		// symlinks: pe.properties.Symlinks, // TODO: b/207489266 - Fully support all properties in prebuilt_file
+	}
+	pe.addInstallRules(ctx, ip)
+}
diff --git a/etc/prebuilt_etc_test.go b/etc/prebuilt_etc_test.go
index 0d44c31..df7664d 100644
--- a/etc/prebuilt_etc_test.go
+++ b/etc/prebuilt_etc_test.go
@@ -23,6 +23,7 @@
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
+	"android/soong/bazel/cquery"
 	"android/soong/snapshot"
 )
 
@@ -494,3 +495,41 @@
 		checkIfSnapshotNotTaken(t, result, "recovery", "prebuilt_recovery_indirect")
 	})
 }
+
+func TestPrebuiltEtcAndroidMkEntriesWithBazel(t *testing.T) {
+	t.Parallel()
+	bp := `
+prebuilt_etc {
+	name: "myetc",
+	src: "prebuilt_etc.rc", // filename in src tree
+	filename: "init.rc", // target filename on device
+	sub_dir: "subdir", // relative subdir for installation
+	bazel_module: { label: "//foo/bar:myetc" },
+}
+`
+	res := android.GroupFixturePreparers(
+		prepareForPrebuiltEtcTest,
+		android.FixtureModifyConfig(func(cfg android.Config) {
+			cfg.BazelContext = android.MockBazelContext{
+				LabelToPrebuiltFileInfo: map[string]cquery.PrebuiltFileInfo{
+					"//foo/bar:myetc": cquery.PrebuiltFileInfo{
+						Src:         "foo/bar/prebuilt_etc.rc",
+						Dir:         "etc/subdir",
+						Filename:    "init.rc",
+						Installable: true,
+					},
+				},
+			}
+		}),
+	).RunTestWithBp(t, bp)
+	ctx := res.ModuleForTests("myetc", "android_arm64_armv8-a")
+	mod := ctx.Module()
+	entries := android.AndroidMkEntriesForTest(t, res.TestContext, mod)[0]
+	// verify androidmk entries
+	android.AssertStringDoesContain(t, "LOCAL_MODULE_PATH should contain", entries.EntryMap["LOCAL_MODULE_PATH"][0], "etc/subdir")
+	android.AssertStringEquals(t, "LOCAL_INSTALLED_MODULE_STEM is incorrect", "init.rc", entries.EntryMap["LOCAL_INSTALLED_MODULE_STEM"][0])
+	// verify installation rules
+	install := ctx.Description("install")
+	android.AssertStringEquals(t, "Source location of prebuilt_etc installation", "out/soong/.intermediates/myetc/android_arm64_armv8-a/init.rc", install.Input.String())
+	android.AssertStringEquals(t, "Target location of prebuilt_etc installation", "out/soong/target/product/test_device/system/etc/subdir/init.rc", install.Output.String())
+}
diff --git a/fuzz/fuzz_common.go b/fuzz/fuzz_common.go
index 8175a37..b470304 100644
--- a/fuzz/fuzz_common.go
+++ b/fuzz/fuzz_common.go
@@ -61,6 +61,7 @@
 type FileToZip struct {
 	SourceFilePath        android.Path
 	DestinationPathPrefix string
+	DestinationPath       string
 }
 
 type ArchOs struct {
@@ -169,6 +170,27 @@
 	return false
 }
 
+type UsePlatformLibs string
+
+const (
+	unknown_use_platform_libs UsePlatformLibs = "unknown_use_platform_libs"
+	// Use the native libraries on the device, typically in /system directory
+	use_platform_libs = "use_platform_libs"
+	// Do not use any native libraries (ART will not be initialized)
+	use_none = "use_none"
+)
+
+func (use_platform_libs UsePlatformLibs) isValidUsePlatformLibs() bool {
+	switch use_platform_libs {
+	case "",
+		unknown_use_platform_libs,
+		use_platform_libs,
+		use_none:
+		return true
+	}
+	return false
+}
+
 type UserData string
 
 const (
@@ -283,6 +305,10 @@
 		if !config.Automatically_route_to.isValidAutomaticallyRouteTo() {
 			panic(fmt.Errorf("Invalid automatically_route_to in fuzz config in %s", moduleName))
 		}
+
+		if !config.Use_platform_libs.isValidUsePlatformLibs() {
+			panic(fmt.Errorf("Invalid use_platform_libs in fuzz config in %s", moduleName))
+		}
 	}
 	return true
 }
@@ -338,6 +364,10 @@
 	IsJni *bool `json:"is_jni,omitempty"`
 	// List of modules for monitoring coverage drops in directories (e.g. "libicu")
 	Target_modules []string `json:"target_modules,omitempty"`
+	// Specifies a bug assignee to replace default ISE assignment
+	Triage_assignee string `json:"triage_assignee,omitempty"`
+	// Specifies libs used to initialize ART (java only, 'use_none' for no initialization)
+	Use_platform_libs UsePlatformLibs `json:"use_platform_libs,omitempty"`
 }
 
 type FuzzFrameworks struct {
@@ -441,7 +471,7 @@
 			FlagWithOutput("-o ", corpusZip)
 		rspFile := corpusZip.ReplaceExtension(ctx, "rsp")
 		command.FlagWithRspFileInputList("-r ", rspFile, fuzzModule.Corpus)
-		files = append(files, FileToZip{corpusZip, ""})
+		files = append(files, FileToZip{SourceFilePath: corpusZip})
 	}
 
 	// Package the data into a zipfile.
@@ -454,17 +484,17 @@
 			command.FlagWithArg("-C ", intermediateDir)
 			command.FlagWithInput("-f ", f)
 		}
-		files = append(files, FileToZip{dataZip, ""})
+		files = append(files, FileToZip{SourceFilePath: dataZip})
 	}
 
 	// The dictionary.
 	if fuzzModule.Dictionary != nil {
-		files = append(files, FileToZip{fuzzModule.Dictionary, ""})
+		files = append(files, FileToZip{SourceFilePath: fuzzModule.Dictionary})
 	}
 
 	// Additional fuzz config.
 	if fuzzModule.Config != nil && IsValidConfig(fuzzModule, module.Name()) {
-		files = append(files, FileToZip{fuzzModule.Config, ""})
+		files = append(files, FileToZip{SourceFilePath: fuzzModule.Config})
 	}
 
 	return files
@@ -483,6 +513,9 @@
 		} else {
 			command.Flag("-P ''")
 		}
+		if file.DestinationPath != "" {
+			command.FlagWithArg("-e ", file.DestinationPath)
+		}
 		command.FlagWithInput("-f ", file.SourceFilePath)
 	}
 
@@ -500,7 +533,7 @@
 	}
 
 	s.FuzzTargets[module.Name()] = true
-	archDirs[archOs] = append(archDirs[archOs], FileToZip{fuzzZip, ""})
+	archDirs[archOs] = append(archDirs[archOs], FileToZip{SourceFilePath: fuzzZip})
 
 	return archDirs[archOs], true
 }
diff --git a/genrule/Android.bp b/genrule/Android.bp
index 8fb5c40..b201cae 100644
--- a/genrule/Android.bp
+++ b/genrule/Android.bp
@@ -15,6 +15,7 @@
         "soong-shared",
     ],
     srcs: [
+        "allowlists.go",
         "genrule.go",
         "locations.go",
     ],
diff --git a/genrule/allowlists.go b/genrule/allowlists.go
new file mode 100644
index 0000000..b0508cb
--- /dev/null
+++ b/genrule/allowlists.go
@@ -0,0 +1,132 @@
+// Copyright 2023 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 genrule
+
+var (
+	DepfileAllowList = []string{
+		"depfile_allowed_for_test",
+		"tflite_support_spm_config",
+		"tflite_support_spm_encoder_config",
+		"gen_uwb_core_proto",
+		"libtextclassifier_fbgen_utils_flatbuffers_flatbuffers_test",
+		"libtextclassifier_fbgen_utils_lua_utils_tests",
+		"libtextclassifier_fbgen_lang_id_common_flatbuffers_model",
+		"libtextclassifier_fbgen_lang_id_common_flatbuffers_embedding-network",
+		"libtextclassifier_fbgen_annotator_datetime_datetime",
+		"libtextclassifier_fbgen_annotator_model",
+		"libtextclassifier_fbgen_annotator_experimental_experimental",
+		"libtextclassifier_fbgen_annotator_entity-data",
+		"libtextclassifier_fbgen_annotator_person_name_person_name_model",
+		"libtextclassifier_fbgen_utils_tflite_text_encoder_config",
+		"libtextclassifier_fbgen_utils_codepoint-range",
+		"libtextclassifier_fbgen_utils_intents_intent-config",
+		"libtextclassifier_fbgen_utils_flatbuffers_flatbuffers",
+		"libtextclassifier_fbgen_utils_zlib_buffer",
+		"libtextclassifier_fbgen_utils_tokenizer",
+		"libtextclassifier_fbgen_utils_grammar_rules",
+		"libtextclassifier_fbgen_utils_grammar_semantics_expression",
+		"libtextclassifier_fbgen_utils_resources",
+		"libtextclassifier_fbgen_utils_i18n_language-tag",
+		"libtextclassifier_fbgen_utils_normalization",
+		"libtextclassifier_fbgen_utils_container_bit-vector",
+		"libtextclassifier_fbgen_actions_actions-entity-data",
+		"libtextclassifier_fbgen_actions_actions_model",
+		"libtextclassifier_fbgen_utils_grammar_testing_value",
+	}
+
+	SandboxingDenyModuleList = []string{
+		"RsBalls-rscript",
+		"CtsRsBlasTestCases-rscript",
+		"pvmfw_fdt_template_rs",
+		"RSTest_v14-rscript",
+		"com.android.apex.test.bar_stripped",
+		"com.android.apex.test.sharedlibs_secondary_generated",
+		"ImageProcessingJB-rscript",
+		"RSTest-rscript",
+		"BluetoothGeneratedDumpsysBinarySchema_bfbs",
+		"WmediumdServerProto_h",
+		"TracingVMProtoStub_h",
+		"FrontendStub_h",
+		"VehicleServerProtoStub_cc",
+		"AudioFocusControlProtoStub_cc",
+		"AudioFocusControlProtoStub_h",
+		"TracingVMProtoStub_cc",
+		"VehicleServerProtoStub_h",
+		"hidl2aidl_translate_cpp_test_gen_headers",
+		"hidl2aidl_translate_cpp_test_gen_src",
+		"hidl2aidl_translate_java_test_gen_src",
+		"hidl2aidl_translate_ndk_test_gen_headers",
+		"hidl2aidl_translate_ndk_test_gen_src",
+		"hidl_hash_test_gen",
+		"nos_app_avb_service_genc++",
+		"nos_app_avb_service_genc++_headers",
+		"nos_app_avb_service_genc++_mock",
+		"nos_app_identity_service_genc++",
+		"nos_app_keymaster_service_genc++",
+		"nos_generator_test_service_genc++_headers",
+		"nos_generator_test_service_genc++_mock",
+		"r8retrace-run-retrace",
+		"ltp_config_arm",
+		"ltp_config_arm_64_hwasan",
+		"ltp_config_arm_lowmem",
+		"ltp_config_arm_64",
+		"ltp_config_riscv_64",
+		"ltp_config_x86_64",
+		"vm-tests-tf-lib",
+		"hidl_cpp_impl_test_gen-headers",
+		"pandora_experimental-python-gen-src",
+		"Refocus-rscript",
+		"RSTest_v11-rscript",
+		"RSTest_v16-rscript",
+		"ScriptGroupTest-rscript",
+		"ImageProcessing2-rscript",
+		"ImageProcessing-rscript",
+		"com.android.apex.test.pony_stripped",
+		"com.android.apex.test.baz_stripped",
+		"com.android.apex.test.foo_stripped",
+		"com.android.apex.test.sharedlibs_generated",
+		"CtsRenderscriptTestCases-rscript",
+		"BlueberryFacadeAndCertGeneratedStub_py",
+		"BlueberryFacadeGeneratedStub_cc",
+		"BlueberryFacadeGeneratedStub_h",
+		"BluetoothGeneratedDumpsysDataSchema_h",
+		"FrontendStub_cc",
+		"OpenwrtControlServerProto_cc",
+		"OpenwrtControlServerProto_h",
+		"WmediumdServerProto_cc",
+		"c2hal_test_genc++",
+		"c2hal_test_genc++_headers",
+		"hidl2aidl_test_gen_aidl",
+		"hidl_error_test_gen",
+		"hidl_export_test_gen-headers",
+		"hidl_format_test_diff",
+		"hidl_hash_version_gen",
+		"libbt_topshim_facade_py_proto",
+		"nos_app_identity_service_genc++_headers",
+		"nos_app_identity_service_genc++_mock",
+		"nos_app_keymaster_service_genc++_headers",
+		"nos_app_keymaster_service_genc++_mock",
+		"nos_app_weaver_service_genc++",
+		"nos_app_weaver_service_genc++_headers",
+		"nos_app_weaver_service_genc++_mock",
+		"nos_generator_test_service_genc++",
+		"pandora-python-gen-src",
+	}
+
+	SandboxingDenyPathList = []string{
+		"art/test",
+		"external/perfetto",
+	}
+)
diff --git a/genrule/genrule.go b/genrule/genrule.go
index f5da50e..889bccd 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -24,6 +24,7 @@
 	"path/filepath"
 	"strconv"
 	"strings"
+	"sync"
 
 	"android/soong/bazel/cquery"
 
@@ -60,6 +61,12 @@
 	PrepareForTestWithGenRuleBuildComponents,
 )
 
+var DepfileAllowSet map[string]bool
+var SandboxingDenyModuleSet map[string]bool
+var SandboxingDenyPathSet map[string]bool
+var SandboxingDenyModuleSetLock sync.Mutex
+var DepfileAllowSetLock sync.Mutex
+
 func RegisterGenruleBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("genrule_defaults", defaultsFactory)
 
@@ -196,12 +203,13 @@
 type taskFunc func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) []generateTask
 
 type generateTask struct {
-	in         android.Paths
-	out        android.WritablePaths
-	depFile    android.WritablePath
-	copyTo     android.WritablePaths // For gensrcs to set on gensrcsMerge rule.
-	genDir     android.WritablePath
-	extraTools android.Paths // dependencies on tools used by the generator
+	in          android.Paths
+	out         android.WritablePaths
+	depFile     android.WritablePath
+	copyTo      android.WritablePaths // For gensrcs to set on gensrcsMerge rule.
+	genDir      android.WritablePath
+	extraTools  android.Paths // dependencies on tools used by the generator
+	extraInputs map[string][]string
 
 	cmd string
 	// For gensrsc sharding.
@@ -395,30 +403,35 @@
 		addLocationLabel(toolFile, toolLocation{paths})
 	}
 
-	includeDirInPaths := ctx.DeviceConfig().BuildBrokenInputDir(g.Name())
-	var srcFiles android.Paths
-	for _, in := range g.properties.Srcs {
-		paths, missingDeps := android.PathsAndMissingDepsRelativeToModuleSourceDir(android.SourceInput{
-			Context: ctx, Paths: []string{in}, ExcludePaths: g.properties.Exclude_srcs, IncludeDirs: includeDirInPaths,
-		})
-		if len(missingDeps) > 0 {
-			if !ctx.Config().AllowMissingDependencies() {
-				panic(fmt.Errorf("should never get here, the missing dependencies %q should have been reported in DepsMutator",
-					missingDeps))
-			}
+	addLabelsForInputs := func(propName string, include, exclude []string) android.Paths {
 
-			// If AllowMissingDependencies is enabled, the build will not have stopped when
-			// the dependency was added on a missing SourceFileProducer module, which will result in nonsensical
-			// "cmd: label ":..." has no files" errors later.  Add a placeholder file to the local label.
-			// The command that uses this placeholder file will never be executed because the rule will be
-			// replaced with an android.Error rule reporting the missing dependencies.
-			ctx.AddMissingDependencies(missingDeps)
-			addLocationLabel(in, errorLocation{"***missing srcs " + in + "***"})
-		} else {
-			srcFiles = append(srcFiles, paths...)
-			addLocationLabel(in, inputLocation{paths})
+		includeDirInPaths := ctx.DeviceConfig().BuildBrokenInputDir(g.Name())
+		var srcFiles android.Paths
+		for _, in := range include {
+			paths, missingDeps := android.PathsAndMissingDepsRelativeToModuleSourceDir(android.SourceInput{
+				Context: ctx, Paths: []string{in}, ExcludePaths: exclude, IncludeDirs: includeDirInPaths,
+			})
+			if len(missingDeps) > 0 {
+				if !ctx.Config().AllowMissingDependencies() {
+					panic(fmt.Errorf("should never get here, the missing dependencies %q should have been reported in DepsMutator",
+						missingDeps))
+				}
+
+				// If AllowMissingDependencies is enabled, the build will not have stopped when
+				// the dependency was added on a missing SourceFileProducer module, which will result in nonsensical
+				// "cmd: label ":..." has no files" errors later.  Add a placeholder file to the local label.
+				// The command that uses this placeholder file will never be executed because the rule will be
+				// replaced with an android.Error rule reporting the missing dependencies.
+				ctx.AddMissingDependencies(missingDeps)
+				addLocationLabel(in, errorLocation{"***missing " + propName + " " + in + "***"})
+			} else {
+				srcFiles = append(srcFiles, paths...)
+				addLocationLabel(in, inputLocation{paths})
+			}
 		}
+		return srcFiles
 	}
+	srcFiles := addLabelsForInputs("srcs", g.properties.Srcs, g.properties.Exclude_srcs)
 
 	var copyFrom android.Paths
 	var outputFiles android.WritablePaths
@@ -430,12 +443,20 @@
 	}
 
 	// Generate tasks, either from genrule or gensrcs.
-	for _, task := range g.taskGenerator(ctx, cmd, srcFiles) {
+	for i, task := range g.taskGenerator(ctx, cmd, srcFiles) {
 		if len(task.out) == 0 {
 			ctx.ModuleErrorf("must have at least one output file")
 			return
 		}
 
+		var extraInputs android.Paths
+		// Only handle extra inputs once as these currently are the same across all tasks
+		if i == 0 {
+			for name, values := range task.extraInputs {
+				extraInputs = append(extraInputs, addLabelsForInputs(name, values, []string{})...)
+			}
+		}
+
 		// Pick a unique path outside the task.genDir for the sbox manifest textproto,
 		// a unique rule name, and the user-visible description.
 		manifestName := "genrule.sbox.textproto"
@@ -452,7 +473,7 @@
 		manifestPath := android.PathForModuleOut(ctx, manifestName)
 
 		// Use a RuleBuilder to create a rule that runs the command inside an sbox sandbox.
-		rule := android.NewRuleBuilder(pctx, ctx).Sbox(task.genDir, manifestPath).SandboxTools()
+		rule := getSandboxedRuleBuilder(ctx, android.NewRuleBuilder(pctx, ctx).Sbox(task.genDir, manifestPath))
 		cmd := rule.Command()
 
 		for _, out := range task.out {
@@ -544,6 +565,8 @@
 		g.rawCommands = append(g.rawCommands, rawCommand)
 
 		cmd.Text(rawCommand)
+		cmd.Implicits(srcFiles) // need to be able to reference other srcs
+		cmd.Implicits(extraInputs)
 		cmd.ImplicitOutputs(task.out)
 		cmd.Implicits(task.in)
 		cmd.ImplicitTools(tools)
@@ -594,14 +617,22 @@
 func (g *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	// Allowlist genrule to use depfile until we have a solution to remove it.
 	// TODO(b/235582219): Remove allowlist for genrule
-	if ctx.ModuleType() == "gensrcs" &&
-		!ctx.DeviceConfig().BuildBrokenDepfile() &&
-		Bool(g.properties.Depfile) {
-		ctx.PropertyErrorf(
-			"depfile",
-			"Deprecated to ensure the module type is convertible to Bazel. "+
-				"Try specifying the dependencies explicitly so that there is no need to use depfile. "+
-				"If not possible, the escape hatch is to use BUILD_BROKEN_DEPFILE to bypass the error.")
+	if Bool(g.properties.Depfile) {
+		if DepfileAllowSet == nil {
+			DepfileAllowSetLock.Lock()
+			defer DepfileAllowSetLock.Unlock()
+			DepfileAllowSet = map[string]bool{}
+			android.AddToStringSet(DepfileAllowSet, DepfileAllowList)
+		}
+		// TODO(b/283852474): Checking the GenruleSandboxing flag is temporary in
+		// order to pass the presubmit before internal master is updated.
+		if ctx.DeviceConfig().GenruleSandboxing() && !DepfileAllowSet[g.Name()] {
+			ctx.PropertyErrorf(
+				"depfile",
+				"Deprecated to ensure the module type is convertible to Bazel. "+
+					"Try specifying the dependencies explicitly so that there is no need to use depfile. "+
+					"If not possible, the escape hatch is to add the module to allowlists.go to bypass the error.")
+		}
 	}
 
 	g.generateCommonBuildActions(ctx)
@@ -737,7 +768,7 @@
 			// TODO(ccross): this RuleBuilder is a hack to be able to call
 			// rule.Command().PathForOutput.  Replace this with passing the rule into the
 			// generator.
-			rule := android.NewRuleBuilder(pctx, ctx).Sbox(genDir, nil).SandboxTools()
+			rule := getSandboxedRuleBuilder(ctx, android.NewRuleBuilder(pctx, ctx).Sbox(genDir, nil))
 
 			for _, in := range shard {
 				outFile := android.GenPathWithExt(ctx, finalSubDir, in, String(properties.Output_extension))
@@ -805,6 +836,9 @@
 				shard:      i,
 				shards:     len(shards),
 				extraTools: extraTools,
+				extraInputs: map[string][]string{
+					"data": properties.Data,
+				},
 			})
 		}
 
@@ -829,6 +863,9 @@
 
 	// maximum number of files that will be passed on a single command line.
 	Shard_size *int64
+
+	// Additional files needed for build that are not tooling related.
+	Data []string `android:"path"`
 }
 
 type bazelGensrcsAttributes struct {
@@ -836,6 +873,7 @@
 	Output_extension *string
 	Tools            bazel.LabelListAttribute
 	Cmd              string
+	Data             bazel.LabelListAttribute
 }
 
 const defaultShardSize = 50
@@ -915,6 +953,23 @@
 	allReplacements.Append(tools.Value)
 	allReplacements.Append(bazel.FirstUniqueBazelLabelList(srcs_labels))
 
+	// The Output_extension prop is not in an immediately accessible field
+	// in the Module struct, so use GetProperties and cast it
+	// to the known struct prop.
+	var outputExtension *string
+	var data bazel.LabelListAttribute
+	if ctx.ModuleType() == "gensrcs" {
+		for _, propIntf := range m.GetProperties() {
+			if props, ok := propIntf.(*genSrcsProperties); ok {
+				outputExtension = props.Output_extension
+				dataFiles := android.BazelLabelForModuleSrc(ctx, props.Data)
+				allReplacements.Append(bazel.FirstUniqueBazelLabelList(dataFiles))
+				data = bazel.MakeLabelListAttribute(dataFiles)
+				break
+			}
+		}
+	}
+
 	// Replace in and out variables with $< and $@
 	var cmd string
 	if m.properties.Cmd != nil {
@@ -940,19 +995,9 @@
 		}
 	}
 
-	tags := android.ApexAvailableTags(m)
+	tags := android.ApexAvailableTagsWithoutTestApexes(ctx, m)
 
 	if ctx.ModuleType() == "gensrcs" {
-		// The Output_extension prop is not in an immediately accessible field
-		// in the Module struct, so use GetProperties and cast it
-		// to the known struct prop.
-		var outputExtension *string
-		for _, propIntf := range m.GetProperties() {
-			if props, ok := propIntf.(*genSrcsProperties); ok {
-				outputExtension = props.Output_extension
-				break
-			}
-		}
 		props := bazel.BazelTargetModuleProperties{
 			Rule_class:        "gensrcs",
 			Bzl_load_location: "//build/bazel/rules:gensrcs.bzl",
@@ -962,6 +1007,7 @@
 			Output_extension: outputExtension,
 			Cmd:              cmd,
 			Tools:            tools,
+			Data:             data,
 		}
 		ctx.CreateBazelTargetModule(props, android.CommonAttributes{
 			Name: m.Name(),
@@ -1020,3 +1066,22 @@
 
 	return module
 }
+
+func getSandboxedRuleBuilder(ctx android.ModuleContext, r *android.RuleBuilder) *android.RuleBuilder {
+	if !ctx.DeviceConfig().GenruleSandboxing() {
+		return r.SandboxTools()
+	}
+	if SandboxingDenyModuleSet == nil {
+		SandboxingDenyModuleSetLock.Lock()
+		defer SandboxingDenyModuleSetLock.Unlock()
+		SandboxingDenyModuleSet = map[string]bool{}
+		SandboxingDenyPathSet = map[string]bool{}
+		android.AddToStringSet(SandboxingDenyModuleSet, append(DepfileAllowList, SandboxingDenyModuleList...))
+		android.AddToStringSet(SandboxingDenyPathSet, SandboxingDenyPathList)
+	}
+
+	if SandboxingDenyPathSet[ctx.ModuleDir()] || SandboxingDenyModuleSet[ctx.ModuleName()] {
+		return r.SandboxTools()
+	}
+	return r.SandboxInputs()
+}
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index 63f8fa9..7c17db1 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -97,8 +97,9 @@
 
 func TestGenruleCmd(t *testing.T) {
 	testcases := []struct {
-		name string
-		prop string
+		name       string
+		moduleName string
+		prop       string
 
 		allowMissingDependencies bool
 
@@ -285,7 +286,8 @@
 			expect: "echo foo > __SBOX_SANDBOX_DIR__/out/out2",
 		},
 		{
-			name: "depfile",
+			name:       "depfile",
+			moduleName: "depfile_allowed_for_test",
 			prop: `
 				out: ["out"],
 				depfile: true,
@@ -397,7 +399,8 @@
 			err: "$(depfile) used without depfile property",
 		},
 		{
-			name: "error no depfile",
+			name:       "error no depfile",
+			moduleName: "depfile_allowed_for_test",
 			prop: `
 				out: ["out"],
 				depfile: true,
@@ -440,11 +443,15 @@
 
 	for _, test := range testcases {
 		t.Run(test.name, func(t *testing.T) {
-			bp := "genrule {\n"
-			bp += "name: \"gen\",\n"
-			bp += test.prop
-			bp += "}\n"
-
+			moduleName := "gen"
+			if test.moduleName != "" {
+				moduleName = test.moduleName
+			}
+			bp := fmt.Sprintf(`
+			genrule {
+			   name: "%s",
+			   %s
+			}`, moduleName, test.prop)
 			var expectedErrors []string
 			if test.err != "" {
 				expectedErrors = append(expectedErrors, regexp.QuoteMeta(test.err))
@@ -455,6 +462,9 @@
 				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
 					variables.Allow_missing_dependencies = proptools.BoolPtr(test.allowMissingDependencies)
 				}),
+				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+					variables.GenruleSandboxing = proptools.BoolPtr(true)
+				}),
 				android.FixtureModifyContext(func(ctx *android.TestContext) {
 					ctx.SetAllowMissingDependencies(test.allowMissingDependencies)
 				}),
@@ -466,7 +476,7 @@
 				return
 			}
 
-			gen := result.Module("gen", "").(*Module)
+			gen := result.Module(moduleName, "").(*Module)
 			android.AssertStringEquals(t, "raw commands", test.expect, gen.rawCommands[0])
 		})
 	}
@@ -594,6 +604,30 @@
 				"out/soong/.intermediates/gen/gen/gensrcs/in3.h",
 			},
 		},
+		{
+			name: "data",
+			prop: `
+				tools: ["tool"],
+				srcs: ["in1.txt", "in2.txt", "in3.txt"],
+				cmd: "$(location) $(in) --extra_input=$(location baz.txt) > $(out)",
+				data: ["baz.txt"],
+				shard_size: 2,
+			`,
+			cmds: []string{
+				"bash -c '__SBOX_SANDBOX_DIR__/tools/out/bin/tool in1.txt --extra_input=baz.txt > __SBOX_SANDBOX_DIR__/out/in1.h' && bash -c '__SBOX_SANDBOX_DIR__/tools/out/bin/tool in2.txt --extra_input=baz.txt > __SBOX_SANDBOX_DIR__/out/in2.h'",
+				"bash -c '__SBOX_SANDBOX_DIR__/tools/out/bin/tool in3.txt --extra_input=baz.txt > __SBOX_SANDBOX_DIR__/out/in3.h'",
+			},
+			deps: []string{
+				"out/soong/.intermediates/gen/gen/gensrcs/in1.h",
+				"out/soong/.intermediates/gen/gen/gensrcs/in2.h",
+				"out/soong/.intermediates/gen/gen/gensrcs/in3.h",
+			},
+			files: []string{
+				"out/soong/.intermediates/gen/gen/gensrcs/in1.h",
+				"out/soong/.intermediates/gen/gen/gensrcs/in2.h",
+				"out/soong/.intermediates/gen/gen/gensrcs/in3.h",
+			},
+		},
 	}
 
 	for _, test := range testcases {
@@ -627,53 +661,42 @@
 	}
 }
 
-func TestGensrcsBuildBrokenDepfile(t *testing.T) {
+func TestGenruleAllowlistingDepfile(t *testing.T) {
 	tests := []struct {
-		name               string
-		prop               string
-		BuildBrokenDepfile *bool
-		err                string
+		name       string
+		prop       string
+		err        string
+		moduleName string
 	}{
 		{
-			name: `error when BuildBrokenDepfile is set to false`,
+			name: `error when module is not allowlisted`,
 			prop: `
 				depfile: true,
 				cmd: "cat $(in) > $(out) && cat $(depfile)",
 			`,
-			BuildBrokenDepfile: proptools.BoolPtr(false),
-			err:                "depfile: Deprecated to ensure the module type is convertible to Bazel",
+			err: "depfile: Deprecated to ensure the module type is convertible to Bazel",
 		},
 		{
-			name: `error when BuildBrokenDepfile is not set`,
+			name: `no error when module is allowlisted`,
 			prop: `
 				depfile: true,
 				cmd: "cat $(in) > $(out) && cat $(depfile)",
 			`,
-			err: "depfile: Deprecated to ensure the module type is convertible to Bazel.",
-		},
-		{
-			name: `no error when BuildBrokenDepfile is explicitly set to true`,
-			prop: `
-				depfile: true,
-				cmd: "cat $(in) > $(out) && cat $(depfile)",
-			`,
-			BuildBrokenDepfile: proptools.BoolPtr(true),
-		},
-		{
-			name: `no error if depfile is not set`,
-			prop: `
-				cmd: "cat $(in) > $(out)",
-			`,
+			moduleName: `depfile_allowed_for_test`,
 		},
 	}
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
+			moduleName := "foo"
+			if test.moduleName != "" {
+				moduleName = test.moduleName
+			}
 			bp := fmt.Sprintf(`
 			gensrcs {
-			   name: "foo",
+			   name: "%s",
 			   srcs: ["data.txt"],
 			   %s
-			}`, test.prop)
+			}`, moduleName, test.prop)
 
 			var expectedErrors []string
 			if test.err != "" {
@@ -682,9 +705,7 @@
 			android.GroupFixturePreparers(
 				prepareForGenRuleTest,
 				android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-					if test.BuildBrokenDepfile != nil {
-						variables.BuildBrokenDepfile = test.BuildBrokenDepfile
-					}
+					variables.GenruleSandboxing = proptools.BoolPtr(true)
 				}),
 			).
 				ExtendWithErrorHandler(android.FixtureExpectsAllErrorsToMatchAPattern(expectedErrors)).
diff --git a/go.mod b/go.mod
index a5d9dd5..4a511c5 100644
--- a/go.mod
+++ b/go.mod
@@ -6,4 +6,5 @@
 	github.com/google/blueprint v0.0.0
 	google.golang.org/protobuf v0.0.0
 	prebuilts/bazel/common/proto/analysis_v2 v0.0.0
+	go.starlark.net v0.0.0
 )
diff --git a/go.work b/go.work
index 737a9df..67f6549 100644
--- a/go.work
+++ b/go.work
@@ -4,6 +4,7 @@
 	.
 	../../external/go-cmp
 	../../external/golang-protobuf
+	../../external/starlark-go
 	../../prebuilts/bazel/common/proto/analysis_v2
 	../../prebuilts/bazel/common/proto/build
 	../blueprint
@@ -16,4 +17,5 @@
 	google.golang.org/protobuf v0.0.0 => ../../external/golang-protobuf
 	prebuilts/bazel/common/proto/analysis_v2 v0.0.0 => ../../prebuilts/bazel/common/proto/analysis_v2
 	prebuilts/bazel/common/proto/build v0.0.0 => ../../prebuilts/bazel/common/proto/build
+	go.starlark.net v0.0.0 => ../../external/starlark-go
 )
diff --git a/java/Android.bp b/java/Android.bp
index 27a0a38..e079869 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -48,6 +48,7 @@
         "droidstubs.go",
         "fuzz.go",
         "gen.go",
+        "generated_java_library.go",
         "genrule.go",
         "hiddenapi.go",
         "hiddenapi_modular.go",
@@ -87,12 +88,12 @@
         "device_host_converter_test.go",
         "dex_test.go",
         "dexpreopt_test.go",
-        "dexpreopt_bootjars_test.go",
         "dexpreopt_config_test.go",
         "droiddoc_test.go",
         "droidstubs_test.go",
         "fuzz_test.go",
         "genrule_test.go",
+        "generated_java_library_test.go",
         "hiddenapi_singleton_test.go",
         "jacoco_test.go",
         "java_test.go",
diff --git a/java/aar.go b/java/aar.go
index f1b137d..29e86e6 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -1056,7 +1056,7 @@
 	ctx.CreateBazelTargetModule(
 		bazel.BazelTargetModuleProperties{
 			Rule_class:        "aar_import",
-			Bzl_load_location: "//build/bazel/rules/android:rules.bzl",
+			Bzl_load_location: "//build/bazel/rules/android:aar_import.bzl",
 		},
 		android.CommonAttributes{Name: name},
 		&bazelAndroidLibraryImport{
@@ -1086,7 +1086,7 @@
 func AndroidLibraryBazelTargetModuleProperties() bazel.BazelTargetModuleProperties {
 	return bazel.BazelTargetModuleProperties{
 		Rule_class:        "android_library",
-		Bzl_load_location: "//build/bazel/rules/android:rules.bzl",
+		Bzl_load_location: "//build/bazel/rules/android:android_library.bzl",
 	}
 }
 
diff --git a/java/android_manifest.go b/java/android_manifest.go
index dbcf098..f2ebfa6 100644
--- a/java/android_manifest.go
+++ b/java/android_manifest.go
@@ -63,9 +63,11 @@
 // 2. The module is run as part of MTS, and should be testable on stable branches
 // Do not return 10000 if we are enforcing default targetSdkVersion and sdk has been finalised
 func shouldReturnFinalOrFutureInt(ctx android.ModuleContext, targetSdkVersionLevel android.ApiLevel, enforceDefaultTargetSdkVersion bool) bool {
-	if enforceDefaultTargetSdkVersion && ctx.Config().PlatformSdkFinal() {
+	// If this is a REL branch, do not return 10000
+	if ctx.Config().PlatformSdkFinal() {
 		return false
 	}
+	// If this a module targeting an unreleased SDK (MTS or unbundled builds), return 10000
 	return targetSdkVersionLevel.IsPreview() && (ctx.Config().UnbundledBuildApps() || includedInMts(ctx.Module()))
 }
 
diff --git a/java/androidmk.go b/java/androidmk.go
index 148d7c2..9c21633 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -133,13 +133,19 @@
 	return entriesList
 }
 
-func (j *JavaFuzzLibrary) AndroidMkEntries() []android.AndroidMkEntries {
+func (j *JavaFuzzTest) AndroidMkEntries() []android.AndroidMkEntries {
 	entriesList := j.Library.AndroidMkEntries()
 	entries := &entriesList[0]
 	entries.ExtraEntries = append(entries.ExtraEntries, func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 		entries.AddStrings("LOCAL_COMPATIBILITY_SUITE", "null-suite")
-		androidMkWriteTestData(j.jniFilePaths, entries)
 		androidMkWriteTestData(android.Paths{j.implementationJarFile}, entries)
+		androidMkWriteTestData(j.jniFilePaths, entries)
+		if j.fuzzPackagedModule.Corpus != nil {
+			androidMkWriteTestData(j.fuzzPackagedModule.Corpus, entries)
+		}
+		if j.fuzzPackagedModule.Dictionary != nil {
+			androidMkWriteTestData(android.Paths{j.fuzzPackagedModule.Dictionary}, entries)
+		}
 	})
 	return entriesList
 }
diff --git a/java/app.go b/java/app.go
index 03e2330..d9272e4 100755
--- a/java/app.go
+++ b/java/app.go
@@ -18,6 +18,7 @@
 // related module types, including their override variants.
 
 import (
+	"fmt"
 	"path/filepath"
 	"strings"
 
@@ -33,8 +34,17 @@
 
 func init() {
 	RegisterAppBuildComponents(android.InitRegistrationContext)
+	pctx.HostBinToolVariable("ModifyAllowlistCmd", "modify_permissions_allowlist")
 }
 
+var (
+	modifyAllowlist = pctx.AndroidStaticRule("modifyAllowlist",
+		blueprint.RuleParams{
+			Command:     "${ModifyAllowlistCmd} $in $packageName $out",
+			CommandDeps: []string{"${ModifyAllowlistCmd}"},
+		}, "packageName")
+)
+
 func RegisterAppBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("android_app", AndroidAppFactory)
 	ctx.RegisterModuleType("android_test", AndroidTestFactory)
@@ -115,6 +125,9 @@
 	// Prefer using other specific properties if build behaviour must be changed; avoid using this
 	// flag for anything but neverallow rules (unless the behaviour change is invisible to owners).
 	Updatable *bool
+
+	// Specifies the file that contains the allowlist for this app.
+	Privapp_allowlist *string `android:"path"`
 }
 
 // android_app properties that can be overridden by override_android_app
@@ -179,6 +192,8 @@
 	android.ApexBundleDepsInfo
 
 	javaApiUsedByOutputFile android.ModuleOutPath
+
+	privAppAllowlist android.OptionalPath
 }
 
 func (a *AndroidApp) IsInstallable() bool {
@@ -205,6 +220,10 @@
 	return a.jniCoverageOutputs
 }
 
+func (a *AndroidApp) PrivAppAllowlist() android.OptionalPath {
+	return a.privAppAllowlist
+}
+
 var _ AndroidLibraryDependency = (*AndroidApp)(nil)
 
 type Certificate struct {
@@ -269,6 +288,16 @@
 		ctx.AddDependency(ctx.Module(), certificateTag, cert)
 	}
 
+	if a.appProperties.Privapp_allowlist != nil && !Bool(a.appProperties.Privileged) {
+		// There are a few uids that are explicitly considered privileged regardless of their
+		// app's location. Bluetooth is one such app. It should arguably be moved to priv-app,
+		// but for now, allow it not to be in priv-app.
+		privilegedBecauseOfUid := ctx.ModuleName() == "Bluetooth"
+		if !privilegedBecauseOfUid {
+			ctx.PropertyErrorf("privapp_allowlist", "privileged must be set in order to use privapp_allowlist (with a few exceptions)")
+		}
+	}
+
 	for _, cert := range a.appProperties.Additional_certificates {
 		cert = android.SrcIsModule(cert)
 		if cert != "" {
@@ -563,19 +592,6 @@
 		certificates = append([]Certificate{mainCert}, certificates...)
 	}
 
-	if !m.Platform() {
-		certPath := certificates[0].Pem.String()
-		systemCertPath := ctx.Config().DefaultAppCertificateDir(ctx).String()
-		if strings.HasPrefix(certPath, systemCertPath) {
-			enforceSystemCert := ctx.Config().EnforceSystemCertificate()
-			allowed := ctx.Config().EnforceSystemCertificateAllowList()
-
-			if enforceSystemCert && !inList(m.Name(), allowed) {
-				ctx.PropertyErrorf("certificate", "The module in product partition cannot be signed with certificate in system.")
-			}
-		}
-	}
-
 	if len(certificates) > 0 {
 		mainCertificate = certificates[0]
 	} else {
@@ -591,6 +607,19 @@
 		}
 	}
 
+	if !m.Platform() {
+		certPath := mainCertificate.Pem.String()
+		systemCertPath := ctx.Config().DefaultAppCertificateDir(ctx).String()
+		if strings.HasPrefix(certPath, systemCertPath) {
+			enforceSystemCert := ctx.Config().EnforceSystemCertificate()
+			allowed := ctx.Config().EnforceSystemCertificateAllowList()
+
+			if enforceSystemCert && !inList(m.Name(), allowed) {
+				ctx.PropertyErrorf("certificate", "The module in product partition cannot be signed with certificate in system.")
+			}
+		}
+	}
+
 	return mainCertificate, certificates
 }
 
@@ -598,6 +627,35 @@
 	return a.installApkName
 }
 
+func (a *AndroidApp) createPrivappAllowlist(ctx android.ModuleContext) android.Path {
+	if a.appProperties.Privapp_allowlist == nil {
+		return nil
+	}
+
+	isOverrideApp := a.GetOverriddenBy() != ""
+	if !isOverrideApp {
+		// if this is not an override, we don't need to rewrite the existing privapp allowlist
+		return android.PathForModuleSrc(ctx, *a.appProperties.Privapp_allowlist)
+	}
+
+	if a.overridableAppProperties.Package_name == nil {
+		ctx.PropertyErrorf("privapp_allowlist", "package_name must be set to use privapp_allowlist")
+	}
+
+	packageName := *a.overridableAppProperties.Package_name
+	fileName := "privapp_allowlist_" + packageName + ".xml"
+	outPath := android.PathForModuleOut(ctx, fileName).OutputPath
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   modifyAllowlist,
+		Input:  android.PathForModuleSrc(ctx, *a.appProperties.Privapp_allowlist),
+		Output: outPath,
+		Args: map[string]string{
+			"packageName": packageName,
+		},
+	})
+	return &outPath
+}
+
 func (a *AndroidApp) generateAndroidBuildActions(ctx android.ModuleContext) {
 	var apkDeps android.Paths
 
@@ -733,11 +791,21 @@
 	BuildBundleModule(ctx, bundleFile, a.exportPackage, jniJarFile, dexJarFile)
 	a.bundleFile = bundleFile
 
+	allowlist := a.createPrivappAllowlist(ctx)
+	if allowlist != nil {
+		a.privAppAllowlist = android.OptionalPathForPath(allowlist)
+	}
+
 	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
 
 	// Install the app package.
-	if (Bool(a.Module.properties.Installable) || ctx.Host()) && apexInfo.IsForPlatform() &&
-		!a.appProperties.PreventInstall {
+	shouldInstallAppPackage := (Bool(a.Module.properties.Installable) || ctx.Host()) && apexInfo.IsForPlatform() && !a.appProperties.PreventInstall
+	if shouldInstallAppPackage {
+		if a.privAppAllowlist.Valid() {
+			allowlistInstallPath := android.PathForModuleInstall(ctx, "etc", "permissions")
+			allowlistInstallFilename := a.installApkName + ".xml"
+			ctx.InstallFile(allowlistInstallPath, allowlistInstallFilename, a.privAppAllowlist.Path())
+		}
 
 		var extraInstalledPaths android.Paths
 		for _, extra := range a.extraOutputFiles {
@@ -924,6 +992,10 @@
 // For OutputFileProducer interface
 func (a *AndroidApp) OutputFiles(tag string) (android.Paths, error) {
 	switch tag {
+	// In some instances, it can be useful to reference the aapt-generated flags from another
+	// target, e.g., system server implements services declared in the framework-res manifest.
+	case ".aapt.proguardOptionsFile":
+		return []android.Path{a.proguardOptionsFile}, nil
 	case ".aapt.srcjar":
 		return []android.Path{a.aaptSrcJar}, nil
 	case ".export-package.apk":
@@ -1326,10 +1398,15 @@
 	}
 }
 
-// presentOptionalUsesLibs returns optional_uses_libs after filtering out MissingUsesLibraries, which don't exist in the
-// build.
+// presentOptionalUsesLibs returns optional_uses_libs after filtering out libraries that don't exist in the source tree.
 func (u *usesLibrary) presentOptionalUsesLibs(ctx android.BaseModuleContext) []string {
-	optionalUsesLibs, _ := android.FilterList(u.usesLibraryProperties.Optional_uses_libs, ctx.Config().MissingUsesLibraries())
+	optionalUsesLibs := android.FilterListPred(u.usesLibraryProperties.Optional_uses_libs, func(s string) bool {
+		exists := ctx.OtherModuleExists(s)
+		if !exists && !android.InList(ctx.ModuleName(), ctx.Config().BuildWarningBadOptionalUsesLibsAllowlist()) {
+			fmt.Printf("Warning: Module '%s' depends on non-existing optional_uses_libs '%s'\n", ctx.ModuleName(), s)
+		}
+		return exists
+	})
 	return optionalUsesLibs
 }
 
@@ -1461,10 +1538,8 @@
 
 // verifyUsesLibrariesAPK checks the <uses-library> tags in the manifest of an APK against the build
 // system and returns the path to a copy of the APK.
-func (u *usesLibrary) verifyUsesLibrariesAPK(ctx android.ModuleContext, apk android.Path) android.Path {
+func (u *usesLibrary) verifyUsesLibrariesAPK(ctx android.ModuleContext, apk android.Path) {
 	u.verifyUsesLibraries(ctx, apk, nil) // for APKs manifest_check does not write output file
-	outputFile := android.PathForModuleOut(ctx, "verify_uses_libraries", apk.Base())
-	return outputFile
 }
 
 // For Bazel / bp2build
@@ -1489,7 +1564,7 @@
 
 	props := bazel.BazelTargetModuleProperties{
 		Rule_class:        "android_app_certificate",
-		Bzl_load_location: "//build/bazel/rules/android:rules.bzl",
+		Bzl_load_location: "//build/bazel/rules/android:android_app_certificate.bzl",
 	}
 
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: module.Name()}, attrs)
@@ -1542,7 +1617,7 @@
 
 	props := bazel.BazelTargetModuleProperties{
 		Rule_class:        "android_binary",
-		Bzl_load_location: "//build/bazel/rules/android:rules.bzl",
+		Bzl_load_location: "//build/bazel/rules/android:android_binary.bzl",
 	}
 
 	if !bp2BuildInfo.hasKotlin {
@@ -1572,7 +1647,7 @@
 
 	ctx.CreateBazelTargetModule(
 		props,
-		android.CommonAttributes{Name: a.Name()},
+		android.CommonAttributes{Name: a.Name(), SkipData: proptools.BoolPtr(true)},
 		appAttrs,
 	)
 
diff --git a/java/app_import.go b/java/app_import.go
index 85b35eb..9c01960 100644
--- a/java/app_import.go
+++ b/java/app_import.go
@@ -49,6 +49,17 @@
 		CommandDeps: []string{"${config.Zip2ZipCmd}"},
 		Description: "Uncompress dex files",
 	})
+
+	checkJniAndDexLibsAreUncompressedRule = pctx.AndroidStaticRule("check-jni-and-dex-libs-are-uncompressed", blueprint.RuleParams{
+		// grep -v ' stor ' will search for lines that don't have ' stor '. stor means the file is stored uncompressed
+		Command: "if (zipinfo $in 'lib/*.so' '*.dex' 2>/dev/null | grep -v ' stor ' >/dev/null) ; then " +
+			"echo $in: Contains compressed JNI libraries and/or dex files >&2;" +
+			"exit 1; " +
+			"else " +
+			"touch $out; " +
+			"fi",
+		Description: "Check for compressed JNI libs or dex files",
+	})
 )
 
 func RegisterAppImportBuildComponents(ctx android.RegistrationContext) {
@@ -73,8 +84,6 @@
 
 	usesLibrary usesLibrary
 
-	preprocessed bool
-
 	installPath android.InstallPath
 
 	hideApexVariantFromMake bool
@@ -128,6 +137,13 @@
 
 	// Optional. Install to a subdirectory of the default install path for the module
 	Relative_install_path *string
+
+	// Whether the prebuilt apk can be installed without additional processing. Default is false.
+	Preprocessed *bool
+
+	// Whether or not to skip checking the preprocessed apk for proper alignment and uncompressed
+	// JNI libs and dex files. Default is false
+	Skip_preprocessed_apk_checks *bool
 }
 
 func (a *AndroidAppImport) IsInstallable() bool {
@@ -201,7 +217,7 @@
 	ctx android.ModuleContext, inputPath android.Path, outputPath android.OutputPath) {
 	// Test apps don't need their JNI libraries stored uncompressed. As a matter of fact, messing
 	// with them may invalidate pre-existing signature data.
-	if ctx.InstallInTestcases() && (Bool(a.properties.Presigned) || a.preprocessed) {
+	if ctx.InstallInTestcases() && (Bool(a.properties.Presigned) || Bool(a.properties.Preprocessed)) {
 		ctx.Build(pctx, android.BuildParams{
 			Rule:   android.Cp,
 			Output: outputPath,
@@ -219,7 +235,7 @@
 
 // Returns whether this module should have the dex file stored uncompressed in the APK.
 func (a *AndroidAppImport) shouldUncompressDex(ctx android.ModuleContext) bool {
-	if ctx.Config().UnbundledBuild() || a.preprocessed {
+	if ctx.Config().UnbundledBuild() || proptools.Bool(a.properties.Preprocessed) {
 		return false
 	}
 
@@ -297,7 +313,7 @@
 	a.dexpreopter.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
 
 	if a.usesLibrary.enforceUsesLibraries() {
-		srcApk = a.usesLibrary.verifyUsesLibrariesAPK(ctx, srcApk)
+		a.usesLibrary.verifyUsesLibrariesAPK(ctx, srcApk)
 	}
 
 	a.dexpreopter.dexpreopt(ctx, jnisUncompressed)
@@ -317,8 +333,14 @@
 
 	// Sign or align the package if package has not been preprocessed
 
-	if a.preprocessed {
-		a.outputFile = srcApk
+	if proptools.Bool(a.properties.Preprocessed) {
+		output := srcApk
+		if !proptools.Bool(a.properties.Skip_preprocessed_apk_checks) {
+			writableOutput := android.PathForModuleOut(ctx, "validated-prebuilt", apkFilename)
+			a.validatePreprocessedApk(ctx, srcApk, writableOutput)
+			output = writableOutput
+		}
+		a.outputFile = output
 		a.certificate = PresignedCertificate
 	} else if !Bool(a.properties.Presigned) {
 		// If the certificate property is empty at this point, default_dev_cert must be set to true.
@@ -352,6 +374,30 @@
 	// TODO: androidmk converter jni libs
 }
 
+func (a *AndroidAppImport) validatePreprocessedApk(ctx android.ModuleContext, srcApk android.Path, dstApk android.WritablePath) {
+	alignmentStamp := android.PathForModuleOut(ctx, "validated-prebuilt", "alignment.stamp")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   checkZipAlignment,
+		Input:  srcApk,
+		Output: alignmentStamp,
+	})
+	compressionStamp := android.PathForModuleOut(ctx, "validated-prebuilt", "compression.stamp")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   checkJniAndDexLibsAreUncompressedRule,
+		Input:  srcApk,
+		Output: compressionStamp,
+	})
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   android.Cp,
+		Input:  srcApk,
+		Output: dstApk,
+		Validations: []android.Path{
+			alignmentStamp,
+			compressionStamp,
+		},
+	})
+}
+
 func (a *AndroidAppImport) Prebuilt() *android.Prebuilt {
 	return &a.prebuilt
 }
@@ -376,6 +422,10 @@
 	return a.provenanceMetaDataFile
 }
 
+func (a *AndroidAppImport) PrivAppAllowlist() android.OptionalPath {
+	return android.OptionalPath{}
+}
+
 var dpiVariantGroupType reflect.Type
 var archVariantGroupType reflect.Type
 var supportedDpis = []string{"ldpi", "mdpi", "hdpi", "xhdpi", "xxhdpi", "xxxhdpi"}
@@ -487,11 +537,6 @@
 	return module
 }
 
-type androidTestImportProperties struct {
-	// Whether the prebuilt apk can be installed without additional processing. Default is false.
-	Preprocessed *bool
-}
-
 type AndroidTestImport struct {
 	AndroidAppImport
 
@@ -508,14 +553,10 @@
 		Per_testcase_directory *bool
 	}
 
-	testImportProperties androidTestImportProperties
-
 	data android.Paths
 }
 
 func (a *AndroidTestImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	a.preprocessed = Bool(a.testImportProperties.Preprocessed)
-
 	a.generateAndroidBuildActions(ctx)
 
 	a.data = android.PathsForModuleSrc(ctx, a.testProperties.Data)
@@ -532,7 +573,6 @@
 	module.AddProperties(&module.properties)
 	module.AddProperties(&module.dexpreoptProperties)
 	module.AddProperties(&module.testProperties)
-	module.AddProperties(&module.testImportProperties)
 	module.populateAllVariantStructs()
 	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
 		module.processVariants(ctx)
diff --git a/java/app_import_test.go b/java/app_import_test.go
index 8093024..bb8fab9 100644
--- a/java/app_import_test.go
+++ b/java/app_import_test.go
@@ -657,6 +657,29 @@
 	}
 }
 
+func TestAndroidAppImport_Preprocessed(t *testing.T) {
+	ctx, _ := testJava(t, `
+		android_app_import {
+			name: "foo",
+			apk: "prebuilts/apk/app.apk",
+			presigned: true,
+			preprocessed: true,
+		}
+		`)
+
+	apkName := "foo.apk"
+	variant := ctx.ModuleForTests("foo", "android_common")
+	outputBuildParams := variant.Output("validated-prebuilt/" + apkName).BuildParams
+	if outputBuildParams.Rule.String() != android.Cp.String() {
+		t.Errorf("Unexpected prebuilt android_app_import rule: " + outputBuildParams.Rule.String())
+	}
+
+	// Make sure compression and aligning were validated.
+	if len(outputBuildParams.Validations) != 2 {
+		t.Errorf("Expected compression/alignment validation rules, found %d validations", len(outputBuildParams.Validations))
+	}
+}
+
 func TestAndroidTestImport_UncompressDex(t *testing.T) {
 	testCases := []struct {
 		name string
diff --git a/java/app_test.go b/java/app_test.go
index 561be68..cf7d174 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -443,9 +443,9 @@
 	inputs := ctx.ModuleForTests("libjni", "android_arm64_armv8-a_sdk_shared").Description("link").Implicits
 	var crtbeginFound, crtendFound bool
 	expectedCrtBegin := ctx.ModuleForTests("crtbegin_so",
-		"android_arm64_armv8-a_sdk_29").Rule("partialLd").Output
+		"android_arm64_armv8-a_sdk_29").Rule("noAddrSig").Output
 	expectedCrtEnd := ctx.ModuleForTests("crtend_so",
-		"android_arm64_armv8-a_sdk_29").Rule("partialLd").Output
+		"android_arm64_armv8-a_sdk_29").Rule("noAddrSig").Output
 	implicits := []string{}
 	for _, input := range inputs {
 		implicits = append(implicits, input.String())
@@ -2645,7 +2645,7 @@
 		PrepareForTestWithJavaSdkLibraryFiles,
 		FixtureWithLastReleaseApis("runtime-library", "foo", "quuz", "qux", "bar", "fred"),
 		android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
-			variables.MissingUsesLibraries = []string{"baz"}
+			variables.BuildWarningBadOptionalUsesLibsAllowlist = []string{"app", "prebuilt"}
 		}),
 	).RunTestWithBp(t, bp)
 
@@ -2693,52 +2693,11 @@
 		`--optional-uses-library baz `
 	android.AssertStringDoesContain(t, "verify apk cmd args", verifyApkCmd, verifyApkArgs)
 
-	// Test that all present libraries are preopted, including implicit SDK dependencies, possibly stubs
+	// Test that necessary args are passed for constructing CLC in Ninja phase.
 	cmd := app.Rule("dexpreopt").RuleParams.Command
-	w := `--target-context-for-sdk any ` +
-		`PCL[/system/framework/qux.jar]#` +
-		`PCL[/system/framework/quuz.jar]#` +
-		`PCL[/system/framework/foo.jar]#` +
-		`PCL[/system/framework/non-sdk-lib.jar]#` +
-		`PCL[/system/framework/bar.jar]#` +
-		`PCL[/system/framework/runtime-library.jar]#` +
-		`PCL[/system/framework/runtime-required-x.jar]#` +
-		`PCL[/system/framework/runtime-optional-x.jar]#` +
-		`PCL[/system/framework/runtime-required-y.jar]#` +
-		`PCL[/system/framework/runtime-optional-y.jar] `
-	android.AssertStringDoesContain(t, "dexpreopt app cmd args", cmd, w)
-
-	// Test conditional context for target SDK version 28.
-	android.AssertStringDoesContain(t, "dexpreopt app cmd 28", cmd,
-		`--target-context-for-sdk 28`+
-			` PCL[/system/framework/org.apache.http.legacy.jar] `)
-
-	// Test conditional context for target SDK version 29.
-	android.AssertStringDoesContain(t, "dexpreopt app cmd 29", cmd,
-		`--target-context-for-sdk 29`+
-			` PCL[/system/framework/android.hidl.manager-V1.0-java.jar]`+
-			`#PCL[/system/framework/android.hidl.base-V1.0-java.jar] `)
-
-	// Test conditional context for target SDK version 30.
-	// "android.test.mock" is absent because "android.test.runner" is not used.
-	android.AssertStringDoesContain(t, "dexpreopt app cmd 30", cmd,
-		`--target-context-for-sdk 30`+
-			` PCL[/system/framework/android.test.base.jar] `)
-
-	cmd = prebuilt.Rule("dexpreopt").RuleParams.Command
-	android.AssertStringDoesContain(t, "dexpreopt prebuilt cmd", cmd,
-		`--target-context-for-sdk any`+
-			` PCL[/system/framework/foo.jar]`+
-			`#PCL[/system/framework/non-sdk-lib.jar]`+
-			`#PCL[/system/framework/android.test.runner.jar]`+
-			`#PCL[/system/framework/bar.jar] `)
-
-	// Test conditional context for target SDK version 30.
-	// "android.test.mock" is present because "android.test.runner" is used.
-	android.AssertStringDoesContain(t, "dexpreopt prebuilt cmd 30", cmd,
-		`--target-context-for-sdk 30`+
-			` PCL[/system/framework/android.test.base.jar]`+
-			`#PCL[/system/framework/android.test.mock.jar] `)
+	android.AssertStringDoesContain(t, "dexpreopt app cmd context", cmd, "--context-json=")
+	android.AssertStringDoesContain(t, "dexpreopt app cmd product_packages", cmd,
+		"--product-packages=out/soong/target/product/test_device/product_packages.txt")
 }
 
 func TestDexpreoptBcp(t *testing.T) {
@@ -3034,11 +2993,13 @@
 
 func TestTargetSdkVersionManifestFixer(t *testing.T) {
 	platform_sdk_codename := "Tiramisu"
+	platform_sdk_version := 33
 	testCases := []struct {
 		name                     string
 		targetSdkVersionInBp     string
 		targetSdkVersionExpected string
 		unbundledBuild           bool
+		platformSdkFinal         bool
 	}{
 		{
 			name:                     "Non-Unbundled build: Android.bp has targetSdkVersion",
@@ -3075,6 +3036,12 @@
 			targetSdkVersionExpected: "10000",
 			unbundledBuild:           true,
 		},
+		{
+			name:                     "Bundled build in REL branches",
+			targetSdkVersionExpected: "33",
+			unbundledBuild:           false,
+			platformSdkFinal:         true,
+		},
 	}
 	for _, testCase := range testCases {
 		targetSdkVersionTemplate := ""
@@ -3091,8 +3058,12 @@
 		fixture := android.GroupFixturePreparers(
 			prepareForJavaTest,
 			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				if testCase.platformSdkFinal {
+					variables.Platform_sdk_final = proptools.BoolPtr(true)
+				}
 				// explicitly set platform_sdk_codename to make the test deterministic
 				variables.Platform_sdk_codename = &platform_sdk_codename
+				variables.Platform_sdk_version = &platform_sdk_version
 				variables.Platform_version_active_codenames = []string{platform_sdk_codename}
 				// create a non-empty list if unbundledBuild==true
 				if testCase.unbundledBuild {
@@ -3370,6 +3341,14 @@
 			srcs: ["a.java"],
 			certificate: ":missing_certificate",
 			sdk_version: "current",
+		}
+
+		android_app {
+			name: "bar",
+			srcs: ["a.java"],
+			certificate: ":missing_certificate",
+			product_specific: true,
+			sdk_version: "current",
 		}`)
 
 	foo := result.ModuleForTests("foo", "android_common")
@@ -3527,3 +3506,124 @@
 		android.AssertStringDoesContain(t, testCase.desc, manifestFixerArgs, "--targetSdkVersion  "+testCase.targetSdkVersionExpected)
 	}
 }
+
+func TestPrivappAllowlist(t *testing.T) {
+	testJavaError(t, "privileged must be set in order to use privapp_allowlist", `
+		android_app {
+			name: "foo",
+			srcs: ["a.java"],
+			privapp_allowlist: "perms.xml",
+		}
+	`)
+
+	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(
+		t,
+		`
+		android_app {
+			name: "foo",
+			srcs: ["a.java"],
+			privapp_allowlist: "privapp_allowlist_com.android.foo.xml",
+			privileged: true,
+			sdk_version: "current",
+		}
+		override_android_app {
+			name: "bar",
+			base: "foo",
+			package_name: "com.google.android.foo",
+		}
+		`,
+	)
+	app := result.ModuleForTests("foo", "android_common")
+	overrideApp := result.ModuleForTests("foo", "android_common_bar")
+
+	// verify that privapp allowlist is created for override apps
+	overrideApp.Output("out/soong/.intermediates/foo/android_common_bar/privapp_allowlist_com.google.android.foo.xml")
+	expectedAllowlistInput := "privapp_allowlist_com.android.foo.xml"
+	overrideActualAllowlistInput := overrideApp.Rule("modifyAllowlist").Input.String()
+	if expectedAllowlistInput != overrideActualAllowlistInput {
+		t.Errorf("expected override allowlist to be %q; got %q", expectedAllowlistInput, overrideActualAllowlistInput)
+	}
+
+	// verify that permissions are copied to device
+	app.Output("out/soong/target/product/test_device/system/etc/permissions/foo.xml")
+	overrideApp.Output("out/soong/target/product/test_device/system/etc/permissions/bar.xml")
+}
+
+func TestPrivappAllowlistAndroidMk(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithJavaDefaultModules,
+		android.PrepareForTestWithAndroidMk,
+	).RunTestWithBp(
+		t,
+		`
+		android_app {
+			name: "foo",
+			srcs: ["a.java"],
+			privapp_allowlist: "privapp_allowlist_com.android.foo.xml",
+			privileged: true,
+			sdk_version: "current",
+		}
+		override_android_app {
+			name: "bar",
+			base: "foo",
+			package_name: "com.google.android.foo",
+		}
+		`,
+	)
+	baseApp := result.ModuleForTests("foo", "android_common")
+	overrideApp := result.ModuleForTests("foo", "android_common_bar")
+
+	baseAndroidApp := baseApp.Module().(*AndroidApp)
+	baseEntries := android.AndroidMkEntriesForTest(t, result.TestContext, baseAndroidApp)[0]
+	android.AssertStringMatches(
+		t,
+		"androidmk has incorrect LOCAL_SOONG_INSTALLED_MODULE; expected to find foo.apk",
+		baseEntries.EntryMap["LOCAL_SOONG_INSTALLED_MODULE"][0],
+		"\\S+foo.apk",
+	)
+	android.AssertStringMatches(
+		t,
+		"androidmk has incorrect LOCAL_SOONG_INSTALL_PAIRS; expected to it to include foo.apk",
+		baseEntries.EntryMap["LOCAL_SOONG_INSTALL_PAIRS"][0],
+		"\\S+foo.apk",
+	)
+	android.AssertStringMatches(
+		t,
+		"androidmk has incorrect LOCAL_SOONG_INSTALL_PAIRS; expected to it to include app",
+		baseEntries.EntryMap["LOCAL_SOONG_INSTALL_PAIRS"][0],
+		"\\S+foo.apk:\\S+/target/product/test_device/system/priv-app/foo/foo.apk",
+	)
+	android.AssertStringMatches(
+		t,
+		"androidmk has incorrect LOCAL_SOONG_INSTALL_PAIRS; expected to it to include privapp_allowlist",
+		baseEntries.EntryMap["LOCAL_SOONG_INSTALL_PAIRS"][0],
+		"privapp_allowlist_com.android.foo.xml:\\S+/target/product/test_device/system/etc/permissions/foo.xml",
+	)
+
+	overrideAndroidApp := overrideApp.Module().(*AndroidApp)
+	overrideEntries := android.AndroidMkEntriesForTest(t, result.TestContext, overrideAndroidApp)[0]
+	android.AssertStringMatches(
+		t,
+		"androidmk has incorrect LOCAL_SOONG_INSTALLED_MODULE; expected to find bar.apk",
+		overrideEntries.EntryMap["LOCAL_SOONG_INSTALLED_MODULE"][0],
+		"\\S+bar.apk",
+	)
+	android.AssertStringMatches(
+		t,
+		"androidmk has incorrect LOCAL_SOONG_INSTALL_PAIRS; expected to it to include bar.apk",
+		overrideEntries.EntryMap["LOCAL_SOONG_INSTALL_PAIRS"][0],
+		"\\S+bar.apk",
+	)
+	android.AssertStringMatches(
+		t,
+		"androidmk has incorrect LOCAL_SOONG_INSTALL_PAIRS; expected to it to include app",
+		overrideEntries.EntryMap["LOCAL_SOONG_INSTALL_PAIRS"][0],
+		"\\S+bar.apk:\\S+/target/product/test_device/system/priv-app/bar/bar.apk",
+	)
+	android.AssertStringMatches(
+		t,
+		"androidmk has incorrect LOCAL_SOONG_INSTALL_PAIRS; expected to it to include privapp_allowlist",
+		overrideEntries.EntryMap["LOCAL_SOONG_INSTALL_PAIRS"][0],
+		"\\S+soong/.intermediates/foo/android_common_bar/privapp_allowlist_com.google.android.foo.xml:\\S+/target/product/test_device/system/etc/permissions/bar.xml",
+	)
+}
diff --git a/java/base.go b/java/base.go
index 9911323..afb626a 100644
--- a/java/base.go
+++ b/java/base.go
@@ -79,6 +79,9 @@
 	// list of java libraries that will be compiled into the resulting jar
 	Static_libs []string `android:"arch_variant"`
 
+	// list of java libraries that should not be used to build this module
+	Exclude_static_libs []string `android:"arch_variant"`
+
 	// manifest file to be included in resulting jar
 	Manifest *string `android:"path"`
 
@@ -184,6 +187,9 @@
 
 	// A list of java_library instances that provide additional hiddenapi annotations for the library.
 	Hiddenapi_additional_annotations []string
+
+	// Additional srcJars tacked in by GeneratedJavaLibraryModule
+	Generated_srcjars []android.Path `android:"mutated"`
 }
 
 // Properties that are specific to device modules. Host module factories should not add these when
@@ -724,6 +730,8 @@
 	}
 
 	libDeps := ctx.AddVariationDependencies(nil, libTag, j.properties.Libs...)
+
+	j.properties.Static_libs = android.RemoveListFromList(j.properties.Static_libs, j.properties.Exclude_static_libs)
 	ctx.AddVariationDependencies(nil, staticLibTag, j.properties.Static_libs...)
 
 	// Add dependency on libraries that provide additional hidden api annotations.
@@ -1036,6 +1044,10 @@
 
 }
 
+func (module *Module) addGeneratedSrcJars(path android.Path) {
+	module.properties.Generated_srcjars = append(module.properties.Generated_srcjars, path)
+}
+
 func (j *Module) compile(ctx android.ModuleContext, aaptSrcJar android.Path) {
 	j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.deviceProperties.Aidl.Export_include_dirs)
 
@@ -1077,6 +1089,10 @@
 	if aaptSrcJar != nil {
 		srcJars = append(srcJars, aaptSrcJar)
 	}
+	srcJars = append(srcJars, j.properties.Generated_srcjars...)
+	if len(j.properties.Generated_srcjars) > 0 {
+		fmt.Printf("Java module %s Generated_srcjars: %v\n", ctx.ModuleName(), j.properties.Generated_srcjars)
+	}
 	srcFiles = srcFiles.FilterOutByExt(".srcjar")
 
 	if j.properties.Jarjar_rules != nil {
@@ -1923,19 +1939,22 @@
 
 func (m *Module) getSdkLinkType(ctx android.BaseModuleContext, name string) (ret sdkLinkType, stubs bool) {
 	switch name {
-	case android.SdkCore.JavaLibraryName(ctx.Config()), "legacy.core.platform.api.stubs", "stable.core.platform.api.stubs",
+	case android.SdkCore.DefaultJavaLibraryName(),
+		"legacy.core.platform.api.stubs",
+		"stable.core.platform.api.stubs",
 		"stub-annotations", "private-stub-annotations-jar",
-		"core-lambda-stubs", "core-generated-annotation-stubs":
+		"core-lambda-stubs",
+		"core-generated-annotation-stubs":
 		return javaCore, true
-	case android.SdkPublic.JavaLibraryName(ctx.Config()):
+	case android.SdkPublic.DefaultJavaLibraryName():
 		return javaSdk, true
-	case android.SdkSystem.JavaLibraryName(ctx.Config()):
+	case android.SdkSystem.DefaultJavaLibraryName():
 		return javaSystem, true
-	case android.SdkModule.JavaLibraryName(ctx.Config()):
+	case android.SdkModule.DefaultJavaLibraryName():
 		return javaModule, true
-	case android.SdkSystemServer.JavaLibraryName(ctx.Config()):
+	case android.SdkSystemServer.DefaultJavaLibraryName():
 		return javaSystemServer, true
-	case android.SdkTest.JavaLibraryName(ctx.Config()):
+	case android.SdkTest.DefaultJavaLibraryName():
 		return javaSystem, true
 	}
 
@@ -2185,5 +2204,9 @@
 		if binary, ok := ctx.Module().(*Binary); ok {
 			javaBinaryHostBp2Build(ctx, binary)
 		}
+	case "java_test_host":
+		if testHost, ok := ctx.Module().(*TestHost); ok {
+			javaTestHostBp2Build(ctx, testHost)
+		}
 	}
 }
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index f692563..108fdd4 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -241,10 +241,8 @@
 	// Collect the module directory for IDE info in java/jdeps.go.
 	modulePaths []string
 
-	// Installs for on-device boot image files. This list has entries only if the installs should be
-	// handled by Make (e.g., the boot image should be installed on the system partition, rather than
-	// in the APEX).
-	bootImageDeviceInstalls []dexpreopterInstall
+	// Path to the boot image profile.
+	profilePath android.Path
 }
 
 // commonBootclasspathFragment defines the methods that are implemented by both source and prebuilt
@@ -264,16 +262,16 @@
 	// If it could not create the files then it will return nil. Otherwise, it will return a map from
 	// android.ArchType to the predefined paths of the boot image files.
 	produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig) bootImageOutputs
+
+	// getImageName returns the `image_name` property of this fragment.
+	getImageName() *string
+
+	// getProfilePath returns the path to the boot image profile.
+	getProfilePath() android.Path
 }
 
 var _ commonBootclasspathFragment = (*BootclasspathFragmentModule)(nil)
 
-// bootImageFilesByArch is a map from android.ArchType to the paths to the boot image files.
-//
-// The paths include the .art, .oat and .vdex files, one for each of the modules from which the boot
-// image is created.
-type bootImageFilesByArch map[android.ArchType]android.Paths
-
 func bootclasspathFragmentFactory() android.Module {
 	m := &BootclasspathFragmentModule{}
 	m.AddProperties(&m.properties, &m.sourceOnlyProperties)
@@ -395,12 +393,6 @@
 	// set image_name: "art".
 	modules android.ConfiguredJarList
 
-	// Map from arch type to the boot image files.
-	bootImageFilesByArch bootImageFilesByArch
-
-	// True if the boot image should be installed in the APEX.
-	shouldInstallBootImageInApex bool
-
 	// Map from the base module name (without prebuilt_ prefix) of a fragment's contents module to the
 	// hidden API encoded dex jar path.
 	contentModuleDexJarPaths bootDexJarByModule
@@ -417,18 +409,6 @@
 	return i.modules
 }
 
-// Get a map from ArchType to the associated boot image's contents for Android.
-//
-// Extension boot images only return their own files, not the files of the boot images they extend.
-func (i BootclasspathFragmentApexContentInfo) AndroidBootImageFilesByArchType() bootImageFilesByArch {
-	return i.bootImageFilesByArch
-}
-
-// Return true if the boot image should be installed in the APEX.
-func (i *BootclasspathFragmentApexContentInfo) ShouldInstallBootImageInApex() bool {
-	return i.shouldInstallBootImageInApex
-}
-
 // DexBootJarPathForContentModule returns the path to the dex boot jar for specified module.
 //
 // The dex boot jar is one which has had hidden API encoding performed on it.
@@ -506,7 +486,7 @@
 		}
 	}
 
-	if SkipDexpreoptBootJars(ctx) {
+	if !dexpreopt.IsDex2oatNeeded(ctx) {
 		return
 	}
 
@@ -557,6 +537,7 @@
 		// Delegate the production of the boot image files to a module type specific method.
 		common := ctx.Module().(commonBootclasspathFragment)
 		bootImageFiles = common.produceBootImageFiles(ctx, imageConfig)
+		b.profilePath = bootImageFiles.profile
 
 		if shouldCopyBootFilesToPredefinedLocations(ctx, imageConfig) {
 			// Zip the boot image files up, if available. This will generate the zip file in a
@@ -566,25 +547,6 @@
 			// Copy the dex jars of this fragment's content modules to their predefined locations.
 			copyBootJarsToPredefinedLocations(ctx, hiddenAPIOutput.EncodedBootDexFilesByModule, imageConfig.dexPathsByModule)
 		}
-
-		for _, variant := range bootImageFiles.variants {
-			archType := variant.config.target.Arch.ArchType
-			arch := archType.String()
-			for _, install := range variant.deviceInstalls {
-				// Remove the "/" prefix because the path should be relative to $ANDROID_PRODUCT_OUT.
-				installDir := strings.TrimPrefix(filepath.Dir(install.To), "/")
-				installBase := filepath.Base(install.To)
-				installPath := android.PathForModuleInPartitionInstall(ctx, "", installDir)
-
-				b.bootImageDeviceInstalls = append(b.bootImageDeviceInstalls, dexpreopterInstall{
-					name:                arch + "-" + installBase,
-					moduleName:          b.Name(),
-					outputPathOnHost:    install.From,
-					installDirOnDevice:  installPath,
-					installFileOnDevice: installBase,
-				})
-			}
-		}
 	}
 
 	// A prebuilt fragment cannot contribute to an apex.
@@ -643,12 +605,8 @@
 			info.profilePathOnHost = bootImageFiles.profile
 			info.profileInstallPathInApex = imageConfig.profileInstallPathInApex
 		}
-
-		info.shouldInstallBootImageInApex = imageConfig.shouldInstallInApex()
 	}
 
-	info.bootImageFilesByArch = bootImageFiles.byArch
-
 	// Make the apex content info available for other modules.
 	ctx.SetProvider(BootclasspathFragmentApexContentInfoProvider, info)
 }
@@ -901,10 +859,6 @@
 
 // produceBootImageFiles builds the boot image files from the source if it is required.
 func (b *BootclasspathFragmentModule) produceBootImageFiles(ctx android.ModuleContext, imageConfig *bootImageConfig) bootImageOutputs {
-	if SkipDexpreoptBootJars(ctx) {
-		return bootImageOutputs{}
-	}
-
 	// Only generate the boot image if the configuration does not skip it.
 	return b.generateBootImageBuildActions(ctx, imageConfig)
 }
@@ -929,6 +883,13 @@
 	// Build a profile for the image config and then use that to build the boot image.
 	profile := bootImageProfileRule(ctx, imageConfig)
 
+	// If dexpreopt of boot image jars should be skipped, generate only a profile.
+	if SkipDexpreoptBootJars(ctx) {
+		return bootImageOutputs{
+			profile: profile,
+		}
+	}
+
 	// Build boot image files for the host variants.
 	buildBootImageVariantsForBuildOs(ctx, imageConfig, profile)
 
@@ -957,19 +918,15 @@
 			},
 		},
 	}}
-	for _, install := range b.bootImageDeviceInstalls {
-		entriesList = append(entriesList, install.ToMakeEntries())
-	}
 	return entriesList
 }
 
-// Returns the names of all Make modules that handle the installation of the boot image.
-func (b *BootclasspathFragmentModule) BootImageDeviceInstallMakeModules() []string {
-	var makeModules []string
-	for _, install := range b.bootImageDeviceInstalls {
-		makeModules = append(makeModules, install.FullModuleName())
-	}
-	return makeModules
+func (b *BootclasspathFragmentModule) getImageName() *string {
+	return b.properties.Image_name
+}
+
+func (b *BootclasspathFragmentModule) getProfilePath() android.Path {
+	return b.profilePath
 }
 
 // Collect information for opening IDE project files in java/jdeps.go.
@@ -1259,59 +1216,25 @@
 	// built without a profile as the prebuilt modules do not provide a profile.
 	buildBootImageVariantsForBuildOs(ctx, imageConfig, profile)
 
-	if imageConfig.shouldInstallInApex() {
-		// If the boot image files for the android variants are in the prebuilt apex, we must use those
-		// rather than building new ones because those boot image files are going to be used on device.
-		files := bootImageFilesByArch{}
-		bootImageFiles := bootImageOutputs{
-			byArch:  files,
-			profile: profile,
-		}
-		for _, variant := range imageConfig.apexVariants() {
-			arch := variant.target.Arch.ArchType
-			bootImageFiles.variants = append(bootImageFiles.variants, bootImageVariantOutputs{
-				variant,
-				// No device installs needed when installed in APEX.
-				nil,
-			})
-			for _, toPath := range variant.imagesDeps {
-				apexRelativePath := apexRootRelativePathToBootImageFile(arch, toPath.Base())
-				// Get the path to the file that the deapexer extracted from the prebuilt apex file.
-				fromPath := di.PrebuiltExportPath(apexRelativePath)
-
-				// Return the toPath as the calling code expects the paths in the returned map to be the
-				// paths predefined in the bootImageConfig.
-				files[arch] = append(files[arch], toPath)
-
-				// Copy the file to the predefined location.
-				ctx.Build(pctx, android.BuildParams{
-					Rule:   android.Cp,
-					Input:  fromPath,
-					Output: toPath,
-				})
-			}
-		}
-		return bootImageFiles
-	} else {
-		if profile == nil && imageConfig.isProfileGuided() {
-			ctx.ModuleErrorf("Unable to produce boot image files: neither boot image files nor profiles exists in the prebuilt apex")
-			return bootImageOutputs{}
-		}
-		// Build boot image files for the android variants from the dex files provided by the contents
-		// of this module.
-		return buildBootImageVariantsForAndroidOs(ctx, imageConfig, profile)
+	if profile == nil && imageConfig.isProfileGuided() {
+		ctx.ModuleErrorf("Unable to produce boot image files: profiles not found in the prebuilt apex")
+		return bootImageOutputs{}
 	}
+	// Build boot image files for the android variants from the dex files provided by the contents
+	// of this module.
+	return buildBootImageVariantsForAndroidOs(ctx, imageConfig, profile)
+}
+
+func (b *PrebuiltBootclasspathFragmentModule) getImageName() *string {
+	return b.properties.Image_name
+}
+
+func (b *PrebuiltBootclasspathFragmentModule) getProfilePath() android.Path {
+	return b.profilePath
 }
 
 var _ commonBootclasspathFragment = (*PrebuiltBootclasspathFragmentModule)(nil)
 
-// createBootImageTag creates the tag to uniquely identify the boot image file among all of the
-// files that a module requires from the prebuilt .apex file.
-func createBootImageTag(arch android.ArchType, baseName string) string {
-	tag := fmt.Sprintf(".bootimage-%s-%s", arch, baseName)
-	return tag
-}
-
 // RequiredFilesFromPrebuiltApex returns the list of all files the prebuilt_bootclasspath_fragment
 // requires from a prebuilt .apex file.
 //
@@ -1325,25 +1248,11 @@
 			// Add the boot image profile.
 			files = append(files, imageConfig.profileInstallPathInApex)
 		}
-		if imageConfig.shouldInstallInApex() {
-			// Add the boot image files, e.g. .art, .oat and .vdex files.
-			for _, variant := range imageConfig.apexVariants() {
-				arch := variant.target.Arch.ArchType
-				for _, path := range variant.imagesDeps.Paths() {
-					base := path.Base()
-					files = append(files, apexRootRelativePathToBootImageFile(arch, base))
-				}
-			}
-		}
 		return files
 	}
 	return nil
 }
 
-func apexRootRelativePathToBootImageFile(arch android.ArchType, base string) string {
-	return filepath.Join("javalib", arch.String(), base)
-}
-
 var _ android.RequiredFilesFromPrebuiltApex = (*PrebuiltBootclasspathFragmentModule)(nil)
 
 func prebuiltBootclasspathFragmentFactory() android.Module {
diff --git a/java/bootclasspath_fragment_test.go b/java/bootclasspath_fragment_test.go
index 2541f14..9bdef74 100644
--- a/java/bootclasspath_fragment_test.go
+++ b/java/bootclasspath_fragment_test.go
@@ -432,3 +432,39 @@
 	fragment = result.Module("a_test_fragment", "android_common").(*BootclasspathFragmentModule)
 	android.AssertBoolEquals(t, "is a test fragment by type", true, fragment.isTestFragment())
 }
+
+func TestBootclassFragment_LinkTextStub(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForJavaTest,
+		prepareForTestWithBootclasspathFragment,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithLastReleaseApis("mysdklibrary"),
+		android.FixtureModifyConfig(func(config android.Config) {
+			config.SetBuildFromTextStub(true)
+		}),
+	).RunTestWithBp(t, `
+        bootclasspath_fragment {
+            name: "myfragment",
+            contents: ["mysdklibrary"],
+            hidden_api: {split_packages: ["*"]},
+            additional_stubs: [
+                "android-non-updatable",
+            ],
+        }
+        java_sdk_library {
+            name: "mysdklibrary",
+            srcs: ["a.java"],
+            shared_library: false,
+            public: {enabled: true},
+            system: {enabled: true},
+        }
+    `)
+
+	fragment := result.ModuleForTests("myfragment", "android_common")
+	ruleCommand := fragment.Rule("modularHiddenAPIStubFlagsFile").RuleParams.Command
+	android.AssertStringDoesContain(t, "Command expected to contain library as dependency stub dex",
+		ruleCommand, "--dependency-stub-dex=out/soong/.intermediates/default/java/android-non-updatable.stubs.module_lib.from-text/android_common/dex/android-non-updatable.stubs.module_lib.from-text.jar")
+	android.AssertStringDoesNotContain(t,
+		"Command not expected to contain multiple api_library as dependency stub dex", ruleCommand,
+		"--dependency-stub-dex=out/soong/.intermediates/default/java/android-non-updatable.stubs.from-text/android_common/dex/android-non-updatable.stubs.from-text.jar")
+}
diff --git a/java/builder.go b/java/builder.go
index 4626267..c4395e9 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -246,6 +246,19 @@
 			CommandDeps: []string{"${config.ZipAlign}"},
 		},
 	)
+
+	checkZipAlignment = pctx.AndroidStaticRule("checkzipalign",
+		blueprint.RuleParams{
+			Command: "if ! ${config.ZipAlign} -c -p 4 $in > /dev/null; then " +
+				"echo $in: Improper package alignment >&2; " +
+				"exit 1; " +
+				"else " +
+				"touch $out; " +
+				"fi",
+			CommandDeps: []string{"${config.ZipAlign}"},
+			Description: "Check zip alignment",
+		},
+	)
 )
 
 func init() {
@@ -292,6 +305,12 @@
 	proto android.ProtoFlags
 }
 
+func DefaultJavaBuilderFlags() javaBuilderFlags {
+	return javaBuilderFlags{
+		javaVersion: JAVA_VERSION_8,
+	}
+}
+
 func TransformJavaToClasses(ctx android.ModuleContext, outputFile android.WritablePath, shardIdx int,
 	srcFiles, srcJars android.Paths, flags javaBuilderFlags, deps android.Paths) {
 
diff --git a/java/config/config.go b/java/config/config.go
index b82a137..195dae1 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -98,7 +98,7 @@
 		"-JDcom.android.tools.r8.emitRecordAnnotationsExInDex",
 	}, dexerJavaVmFlagsList...))
 	exportedVars.ExportStringListStaticVariable("R8Flags", append([]string{
-		"-JXmx2048M",
+		"-JXmx4096M",
 		"-JDcom.android.tools.r8.emitRecordAnnotationsInDex",
 		"-JDcom.android.tools.r8.emitPermittedSubclassesAnnotationsInDex",
 		"-JDcom.android.tools.r8.emitRecordAnnotationsExInDex",
diff --git a/java/config/makevars.go b/java/config/makevars.go
index 273aca0..d383d98 100644
--- a/java/config/makevars.go
+++ b/java/config/makevars.go
@@ -28,8 +28,11 @@
 	ctx.Strict("FRAMEWORK_LIBRARIES", strings.Join(FrameworkLibraries, " "))
 
 	// These are used by make when LOCAL_PRIVATE_PLATFORM_APIS is set (equivalent to platform_apis in blueprint):
-	ctx.Strict("LEGACY_CORE_PLATFORM_BOOTCLASSPATH_LIBRARIES", strings.Join(LegacyCorePlatformBootclasspathLibraries, " "))
-	ctx.Strict("LEGACY_CORE_PLATFORM_SYSTEM_MODULES", LegacyCorePlatformSystemModules)
+	ctx.Strict("LEGACY_CORE_PLATFORM_BOOTCLASSPATH_LIBRARIES",
+		strings.Join(android.JavaApiLibraryNames(ctx.Config(), LegacyCorePlatformBootclasspathLibraries), " "))
+	ctx.Strict("LEGACY_CORE_PLATFORM_SYSTEM_MODULES",
+		android.JavaApiLibraryName(ctx.Config(), LegacyCorePlatformSystemModules),
+	)
 
 	ctx.Strict("ANDROID_JAVA_HOME", "${JavaHome}")
 	ctx.Strict("ANDROID_JAVA8_HOME", "prebuilts/jdk/jdk8/${hostPrebuiltTag}")
diff --git a/java/core-libraries/Android.bp b/java/core-libraries/Android.bp
index 958f4ce..eadd9c6 100644
--- a/java/core-libraries/Android.bp
+++ b/java/core-libraries/Android.bp
@@ -33,19 +33,45 @@
     "win_sdk",
 ]
 
-java_library {
-    name: "core.current.stubs",
+java_defaults {
+    name: "core.current.stubs.defaults",
     visibility: ["//visibility:public"],
+    sdk_version: "none",
+    system_modules: "none",
+    dist: {
+        targets: dist_targets,
+    },
+}
+
+java_library {
+    name: "core.current.stubs.from-source",
+    defaults: [
+        "core.current.stubs.defaults",
+    ],
     static_libs: [
         "art.module.public.api.stubs",
         "conscrypt.module.public.api.stubs",
         "i18n.module.public.api.stubs",
     ],
-    sdk_version: "none",
-    system_modules: "none",
+}
 
-    dist: {
-        targets: dist_targets,
+java_library {
+    name: "core.current.stubs",
+    defaults: [
+        "core.current.stubs.defaults",
+    ],
+    static_libs: [
+        "core.current.stubs.from-source",
+    ],
+    product_variables: {
+        build_from_text_stub: {
+            static_libs: [
+                "core.current.stubs.from-text",
+            ],
+            exclude_static_libs: [
+                "core.current.stubs.from-source",
+            ],
+        },
     },
 }
 
@@ -199,18 +225,46 @@
 // API annotations are available to the dex tools that enable enforcement of runtime
 // accessibility. b/119068555
 java_library {
-    name: "legacy.core.platform.api.stubs",
+    name: "legacy.core.platform.api.stubs.from-source",
     visibility: core_platform_visibility,
-    hostdex: true,
-    compile_dex: true,
-
-    sdk_version: "none",
-    system_modules: "none",
+    defaults: [
+        "core.platform.api.stubs.defaults",
+    ],
     static_libs: [
         "art.module.public.api.stubs.module_lib",
         "conscrypt.module.platform.api.stubs",
         "legacy.i18n.module.platform.api.stubs",
     ],
+}
+
+java_library {
+    name: "legacy.core.platform.api.stubs",
+    visibility: core_platform_visibility,
+    defaults: [
+        "core.platform.api.stubs.defaults",
+    ],
+    static_libs: [
+        "legacy.core.platform.api.stubs.from-source",
+    ],
+    product_variables: {
+        build_from_text_stub: {
+            static_libs: [
+                "stable.core.platform.api.stubs.from-text",
+            ],
+            exclude_static_libs: [
+                "stable.core.platform.api.stubs.from-source",
+            ],
+        },
+    },
+}
+
+java_defaults {
+    name: "core.platform.api.stubs.defaults",
+    hostdex: true,
+    compile_dex: true,
+
+    sdk_version: "none",
+    system_modules: "none",
     patch_module: "java.base",
 }
 
@@ -233,20 +287,38 @@
 }
 
 java_library {
-    name: "stable.core.platform.api.stubs",
+    name: "stable.core.platform.api.stubs.from-source",
     visibility: core_platform_visibility,
-    hostdex: true,
-    compile_dex: true,
-
-    sdk_version: "none",
-    system_modules: "none",
+    defaults: [
+        "core.platform.api.stubs.defaults",
+    ],
     static_libs: [
         "art.module.public.api.stubs.module_lib",
         // conscrypt only has a stable version, so it is okay to depend on it here:
         "conscrypt.module.platform.api.stubs",
         "stable.i18n.module.platform.api.stubs",
     ],
-    patch_module: "java.base",
+}
+
+java_library {
+    name: "stable.core.platform.api.stubs",
+    visibility: core_platform_visibility,
+    defaults: [
+        "core.platform.api.stubs.defaults",
+    ],
+    static_libs: [
+        "stable.core.platform.api.stubs.from-source",
+    ],
+    product_variables: {
+        build_from_text_stub: {
+            static_libs: [
+                "stable.core.platform.api.stubs.from-text",
+            ],
+            exclude_static_libs: [
+                "stable.core.platform.api.stubs.from-source",
+            ],
+        },
+    },
 }
 
 // Same as stable.core.platform.api.stubs, but android annotations are
diff --git a/java/core-libraries/TxtStubLibraries.bp b/java/core-libraries/TxtStubLibraries.bp
index 813187e..0cf0f36 100644
--- a/java/core-libraries/TxtStubLibraries.bp
+++ b/java/core-libraries/TxtStubLibraries.bp
@@ -22,8 +22,6 @@
     libs: [
         "core-current-stubs-for-system-modules-no-annotations.from-text",
     ],
-    // TODO: Enable after stub generation from .txt file is available
-    enabled: false,
 }
 
 java_library {
@@ -36,8 +34,6 @@
         "core.current.stubs.from-text",
         "core-lambda-stubs.from-text",
     ],
-    // TODO: Enable after stub generation from .txt file is available
-    enabled: false,
 }
 
 // Same as core-module-lib-stubs-system-modules, but the stubs are generated from .txt files
@@ -47,8 +43,6 @@
     libs: [
         "core-module-lib-stubs-for-system-modules-no-annotations.from-text",
     ],
-    // TODO: Enable after stub generation from .txt file is available
-    enabled: false,
 }
 
 java_library {
@@ -61,8 +55,6 @@
         "core.module_lib.stubs.from-text",
         "core-lambda-stubs.from-text",
     ],
-    // TODO: Enable after stub generation from .txt file is available
-    enabled: false,
 }
 
 java_library {
@@ -79,8 +71,6 @@
     sdk_version: "none",
     system_modules: "none",
     visibility: ["//visibility:private"],
-    // TODO: Enable after stub generation from .txt file is available
-    enabled: false,
 }
 
 // Same as legacy-core-platform-api-stubs-system-modules, but the stubs are generated from .txt files
@@ -91,8 +81,6 @@
         "legacy.core.platform.api.no.annotations.stubs.from-text",
         "core-lambda-stubs.from-text",
     ],
-    // TODO: Enable after stub generation from .txt file is available
-    enabled: false,
 }
 
 java_library {
@@ -108,8 +96,6 @@
         "legacy.core.platform.api.stubs.from-text",
     ],
     patch_module: "java.base",
-    // TODO: Enable after stub generation from .txt file is available
-    enabled: false,
 }
 
 // Same as stable-core-platform-api-stubs-system-modules, but the stubs are generated from .txt files
@@ -120,8 +106,6 @@
         "stable.core.platform.api.no.annotations.stubs.from-text",
         "core-lambda-stubs.from-text",
     ],
-    // TODO: Enable after stub generation from .txt file is available
-    enabled: false,
 }
 
 java_library {
@@ -137,8 +121,6 @@
         "stable.core.platform.api.stubs.from-text",
     ],
     patch_module: "java.base",
-    // TODO: Enable after stub generation from .txt file is available
-    enabled: false,
 }
 
 java_api_library {
@@ -151,6 +133,4 @@
         // LambdaMetaFactory depends on CallSite etc. which is part of the Core API surface
         "core.current.stubs.from-text",
     ],
-    // TODO: Enable after stub generation from .txt file is available
-    enabled: false,
 }
diff --git a/java/dex.go b/java/dex.go
index 4d6aa34..7e7da00 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -140,7 +140,7 @@
 			`$r8Template${config.R8Cmd} ${config.R8Flags} -injars $tmpJar --output $outDir ` +
 			`--no-data-resources ` +
 			`-printmapping ${outDict} ` +
-			`--pg-conf-output ${outConfig} ` +
+			`-printconfiguration ${outConfig} ` +
 			`-printusage ${outUsage} ` +
 			`--deps-file ${out}.d ` +
 			`$r8Flags && ` +
@@ -161,7 +161,7 @@
 		"$r8Template": &remoteexec.REParams{
 			Labels:          map[string]string{"type": "compile", "compiler": "r8"},
 			Inputs:          []string{"$implicits", "${config.R8Jar}"},
-			OutputFiles:     []string{"${outUsage}"},
+			OutputFiles:     []string{"${outUsage}", "${outConfig}", "${outDict}"},
 			ExecStrategy:    "${config.RER8ExecStrategy}",
 			ToolchainInputs: []string{"${config.JavaCmd}"},
 			Platform:        map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
@@ -399,13 +399,16 @@
 			args["implicits"] = strings.Join(r8Deps.Strings(), ",")
 		}
 		ctx.Build(pctx, android.BuildParams{
-			Rule:            rule,
-			Description:     "r8",
-			Output:          javalibJar,
-			ImplicitOutputs: android.WritablePaths{proguardDictionary, proguardUsageZip},
-			Input:           dexParams.classesJar,
-			Implicits:       r8Deps,
-			Args:            args,
+			Rule:        rule,
+			Description: "r8",
+			Output:      javalibJar,
+			ImplicitOutputs: android.WritablePaths{
+				proguardDictionary,
+				proguardUsageZip,
+				proguardConfiguration},
+			Input:     dexParams.classesJar,
+			Implicits: r8Deps,
+			Args:      args,
 		})
 	} else {
 		d8Flags, d8Deps := d8Flags(dexParams.flags)
diff --git a/java/dex_test.go b/java/dex_test.go
index 97fc3d0..2ba3831 100644
--- a/java/dex_test.go
+++ b/java/dex_test.go
@@ -23,7 +23,7 @@
 )
 
 func TestR8(t *testing.T) {
-	result := PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd.RunTestWithBp(t, `
+	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, `
 		android_app {
 			name: "app",
 			srcs: ["foo.java"],
@@ -191,7 +191,7 @@
 
 	for _, tc := range testcases {
 		t.Run(tc.name, func(t *testing.T) {
-			fixturePreparer := PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd
+			fixturePreparer := PrepareForTestWithJavaDefaultModules
 			if tc.unbundled {
 				fixturePreparer = android.GroupFixturePreparers(
 					fixturePreparer,
@@ -258,7 +258,7 @@
 }
 
 func TestR8Flags(t *testing.T) {
-	result := PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd.RunTestWithBp(t, `
+	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, `
 		android_app {
 			name: "app",
 			srcs: ["foo.java"],
@@ -287,7 +287,7 @@
 }
 
 func TestD8(t *testing.T) {
-	result := PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd.RunTestWithBp(t, `
+	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, `
 		java_library {
 			name: "foo",
 			srcs: ["foo.java"],
@@ -328,7 +328,7 @@
 }
 
 func TestProguardFlagsInheritance(t *testing.T) {
-	result := PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd.RunTestWithBp(t, `
+	result := PrepareForTestWithJavaDefaultModules.RunTestWithBp(t, `
 		android_app {
 			name: "app",
 			static_libs: [
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index 0ffedf6..e588c9a 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -180,6 +180,8 @@
 	return android.RemoveOptionalPrebuiltPrefix(ctx.ModuleName())
 }
 
+// Returns whether dexpreopt is applicable to the module.
+// When it returns true, neither profile nor dexpreopt artifacts will be generated.
 func (d *dexpreopter) dexpreoptDisabled(ctx android.BaseModuleContext) bool {
 	if !ctx.Device() {
 		return true
@@ -205,14 +207,6 @@
 
 	global := dexpreopt.GetGlobalConfig(ctx)
 
-	if global.DisablePreopt {
-		return true
-	}
-
-	if inList(moduleName(ctx), global.DisablePreoptModules) {
-		return true
-	}
-
 	isApexSystemServerJar := global.AllApexSystemServerJars(ctx).ContainsJar(moduleName(ctx))
 	if isApexVariant(ctx) {
 		// Don't preopt APEX variant module unless the module is an APEX system server jar.
@@ -232,7 +226,7 @@
 }
 
 func dexpreoptToolDepsMutator(ctx android.BottomUpMutatorContext) {
-	if d, ok := ctx.Module().(DexpreopterInterface); !ok || d.dexpreoptDisabled(ctx) {
+	if d, ok := ctx.Module().(DexpreopterInterface); !ok || d.dexpreoptDisabled(ctx) || !dexpreopt.IsDex2oatNeeded(ctx) {
 		return
 	}
 	dexpreopt.RegisterToolDeps(ctx)
@@ -396,7 +390,11 @@
 
 	globalSoong := dexpreopt.GetGlobalSoongConfig(ctx)
 
-	dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(ctx, globalSoong, global, dexpreoptConfig)
+	// "product_packages.txt" is generated by `build/make/core/Makefile`.
+	productPackages := android.PathForModuleInPartitionInstall(ctx, "", "product_packages.txt")
+
+	dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(
+		ctx, globalSoong, global, dexpreoptConfig, productPackages)
 	if err != nil {
 		ctx.ModuleErrorf("error generating dexpreopt rule: %s", err.Error())
 		return
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index f4827ea..35f6097 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -250,11 +250,12 @@
 	// Output directory for the image files with debug symbols.
 	symbolsDir android.OutputPath
 
-	// Subdirectory where the image files are installed.
-	installDirOnHost string
-
-	// Subdirectory where the image files on device are installed.
-	installDirOnDevice string
+	// The relative location where the image files are installed. On host, the location is relative to
+	// $ANDROID_PRODUCT_OUT.
+	//
+	// Only the configs that are built by platform_bootclasspath are installable on device. On device,
+	// the location is relative to "/".
+	installDir string
 
 	// Install path of the boot image profile if it needs to be installed in the APEX, or empty if not
 	// needed.
@@ -294,6 +295,11 @@
 
 	// The "--single-image" argument.
 	singleImage bool
+
+	// Profiles imported from other boot image configs. Each element must represent a
+	// `bootclasspath_fragment` of an APEX (i.e., the `name` field of each element must refer to the
+	// `image_name` property of a `bootclasspath_fragment`).
+	profileImports []*bootImageConfig
 }
 
 // Target-dependent description of a boot image.
@@ -421,11 +427,6 @@
 	return variants
 }
 
-// Returns true if the boot image should be installed in the APEX.
-func (image *bootImageConfig) shouldInstallInApex() bool {
-	return strings.HasPrefix(image.installDirOnDevice, "apex/")
-}
-
 // Return boot image locations (as a list of symbolic paths).
 //
 // The image "location" is a symbolic path that, with multiarchitecture support, doesn't really
@@ -464,7 +465,7 @@
 }
 
 func RegisterDexpreoptBootJarsComponents(ctx android.RegistrationContext) {
-	ctx.RegisterSingletonModuleType("dex_bootjars", dexpreoptBootJarsFactory)
+	ctx.RegisterParallelSingletonModuleType("dex_bootjars", dexpreoptBootJarsFactory)
 }
 
 func SkipDexpreoptBootJars(ctx android.PathContext) bool {
@@ -500,15 +501,12 @@
 
 // Generate build rules for boot images.
 func (d *dexpreoptBootJars) GenerateSingletonBuildActions(ctx android.SingletonContext) {
-	if SkipDexpreoptBootJars(ctx) {
-		return
-	}
 	if dexpreopt.GetCachedGlobalSoongConfig(ctx) == nil {
 		// No module has enabled dexpreopting, so we assume there will be no boot image to make.
 		return
 	}
-
-	d.dexpreoptConfigForMake = android.PathForOutput(ctx, ctx.Config().DeviceName(), "dexpreopt.config")
+	archType := ctx.Config().Targets[android.Android][0].Arch.ArchType
+	d.dexpreoptConfigForMake = android.PathForOutput(ctx, toDexpreoptDirName(archType), "dexpreopt.config")
 	writeGlobalConfigForMake(ctx, d.dexpreoptConfigForMake)
 
 	global := dexpreopt.GetGlobalConfig(ctx)
@@ -599,6 +597,12 @@
 	buildBootImageForOsType(ctx, image, profile, ctx.Config().BuildOS)
 }
 
+// bootImageFilesByArch is a map from android.ArchType to the paths to the boot image files.
+//
+// The paths include the .art, .oat and .vdex files, one for each of the modules from which the boot
+// image is created.
+type bootImageFilesByArch map[android.ArchType]android.Paths
+
 // bootImageOutputs encapsulates information about boot images that were created/obtained by
 // commonBootclasspathFragment.produceBootImageFiles.
 type bootImageOutputs struct {
@@ -659,8 +663,7 @@
 }
 
 type bootImageVariantOutputs struct {
-	config         *bootImageVariant
-	deviceInstalls android.RuleBuilderInstalls
+	config *bootImageVariant
 }
 
 // Generate boot image build rules for a specific target.
@@ -671,9 +674,9 @@
 
 	arch := image.target.Arch.ArchType
 	os := image.target.Os.String() // We need to distinguish host-x86 and device-x86.
-	symbolsDir := image.symbolsDir.Join(ctx, os, image.installDirOnHost, arch.String())
+	symbolsDir := image.symbolsDir.Join(ctx, os, image.installDir, arch.String())
 	symbolsFile := symbolsDir.Join(ctx, image.stem+".oat")
-	outputDir := image.dir.Join(ctx, os, image.installDirOnHost, arch.String())
+	outputDir := image.dir.Join(ctx, os, image.installDir, arch.String())
 	outputPath := outputDir.Join(ctx, image.stem+".oat")
 	oatLocation := dexpreopt.PathToLocation(outputPath, arch)
 	imagePath := outputPath.ReplaceExtension(ctx, "art")
@@ -713,6 +716,34 @@
 		cmd.FlagWithInput("--profile-file=", profile)
 	}
 
+	fragments := make(map[string]commonBootclasspathFragment)
+	ctx.VisitDirectDepsWithTag(bootclasspathFragmentDepTag, func(child android.Module) {
+		fragment := child.(commonBootclasspathFragment)
+		if fragment.getImageName() != nil && android.IsModulePreferred(child) {
+			fragments[*fragment.getImageName()] = fragment
+		}
+	})
+
+	for _, profileImport := range image.profileImports {
+		fragment := fragments[profileImport.name]
+		if fragment == nil {
+			ctx.ModuleErrorf("Boot image config '%[1]s' imports profile from '%[2]s', but a "+
+				"bootclasspath_fragment with image name '%[2]s' doesn't exist or is not added as a "+
+				"dependency of '%[1]s'",
+				image.name,
+				profileImport.name)
+			return bootImageVariantOutputs{}
+		}
+		if fragment.getProfilePath() == nil {
+			ctx.ModuleErrorf("Boot image config '%[1]s' imports profile from '%[2]s', but '%[2]s' "+
+				"doesn't provide a profile",
+				image.name,
+				profileImport.name)
+			return bootImageVariantOutputs{}
+		}
+		cmd.FlagWithInput("--profile-file=", fragment.getProfilePath())
+	}
+
 	dirtyImageFile := "frameworks/base/config/dirty-image-objects"
 	dirtyImagePath := android.ExistentPathForSource(ctx, dirtyImageFile)
 	if dirtyImagePath.Valid() {
@@ -785,7 +816,7 @@
 		cmd.FlagWithArg("--instruction-set-features=", global.InstructionSetFeatures[arch])
 	}
 
-	if global.EnableUffdGc {
+	if global.EnableUffdGc && image.target.Os == android.Android {
 		cmd.Flag("--runtime-arg").Flag("-Xgc:CMC")
 	}
 
@@ -799,11 +830,10 @@
 
 	cmd.Textf(`|| ( echo %s ; false )`, proptools.ShellEscape(failureMessage))
 
-	installDir := filepath.Join("/", image.installDirOnHost, arch.String())
+	installDir := filepath.Dir(image.imagePathOnDevice)
 
 	var vdexInstalls android.RuleBuilderInstalls
 	var unstrippedInstalls android.RuleBuilderInstalls
-	var deviceInstalls android.RuleBuilderInstalls
 
 	for _, artOrOat := range image.moduleFiles(ctx, outputDir, ".art", ".oat") {
 		cmd.ImplicitOutput(artOrOat)
@@ -829,14 +859,6 @@
 			android.RuleBuilderInstall{unstrippedOat, filepath.Join(installDir, unstrippedOat.Base())})
 	}
 
-	if image.installDirOnHost != image.installDirOnDevice && !image.shouldInstallInApex() && !ctx.Config().UnbundledBuild() {
-		installDirOnDevice := filepath.Join("/", image.installDirOnDevice, arch.String())
-		for _, file := range image.moduleFiles(ctx, outputDir, ".art", ".oat", ".vdex") {
-			deviceInstalls = append(deviceInstalls,
-				android.RuleBuilderInstall{file, filepath.Join(installDirOnDevice, file.Base())})
-		}
-	}
-
 	rule.Build(image.name+"JarsDexpreopt_"+image.target.String(), "dexpreopt "+image.name+" jars "+arch.String())
 
 	// save output and installed files for makevars
@@ -852,7 +874,6 @@
 
 	return bootImageVariantOutputs{
 		image,
-		deviceInstalls,
 	}
 }
 
@@ -1002,7 +1023,7 @@
 // (make/core/dex_preopt_libart.mk) to generate install rules that copy boot image files to the
 // correct output directories.
 func (d *dexpreoptBootJars) MakeVars(ctx android.MakeVarsContext) {
-	if d.dexpreoptConfigForMake != nil {
+	if d.dexpreoptConfigForMake != nil && !SkipDexpreoptBootJars(ctx) {
 		ctx.Strict("DEX_PREOPT_CONFIG_FOR_MAKE", d.dexpreoptConfigForMake.String())
 		ctx.Strict("DEX_PREOPT_SOONG_CONFIG_FOR_MAKE", android.PathForOutput(ctx, "dexpreopt_soong.config").String())
 	}
@@ -1014,6 +1035,10 @@
 			ctx.Strict("DEXPREOPT_IMAGE_PROFILE_LICENSE_METADATA", image.profileLicenseMetadataFile.String())
 		}
 
+		if SkipDexpreoptBootJars(ctx) {
+			return
+		}
+
 		global := dexpreopt.GetGlobalConfig(ctx)
 		dexPaths, dexLocations := bcpForDexpreopt(ctx, global.PreoptWithUpdatableBcp)
 		ctx.Strict("DEXPREOPT_BOOTCLASSPATH_DEX_FILES", strings.Join(dexPaths.Strings(), " "))
diff --git a/java/dexpreopt_bootjars_test.go b/java/dexpreopt_bootjars_test.go
deleted file mode 100644
index bc7a55e..0000000
--- a/java/dexpreopt_bootjars_test.go
+++ /dev/null
@@ -1,130 +0,0 @@
-// Copyright 2019 Google Inc. All rights reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package java
-
-import (
-	"path/filepath"
-	"sort"
-	"testing"
-
-	"android/soong/android"
-)
-
-func testDexpreoptBoot(t *testing.T, ruleFile string, expectedInputs, expectedOutputs []string) {
-	bp := `
-		java_sdk_library {
-			name: "foo",
-			srcs: ["a.java"],
-			api_packages: ["foo"],
-		}
-
-		java_library {
-			name: "bar",
-			srcs: ["b.java"],
-			installable: true,
-			system_ext_specific: true,
-		}
-
-		dex_import {
-			name: "baz",
-			jars: ["a.jar"],
-		}
-
-		platform_bootclasspath {
-			name: "platform-bootclasspath",
-		}
-	`
-
-	result := android.GroupFixturePreparers(
-		prepareForJavaTest,
-		PrepareForTestWithJavaSdkLibraryFiles,
-		FixtureWithLastReleaseApis("foo"),
-		FixtureConfigureBootJars("platform:foo", "system_ext:bar", "platform:baz"),
-	).RunTestWithBp(t, bp)
-
-	platformBootclasspath := result.ModuleForTests("platform-bootclasspath", "android_common")
-	rule := platformBootclasspath.Output(ruleFile)
-
-	for i := range expectedInputs {
-		expectedInputs[i] = filepath.Join("out/soong/test_device", expectedInputs[i])
-	}
-
-	for i := range expectedOutputs {
-		expectedOutputs[i] = filepath.Join("out/soong/test_device", expectedOutputs[i])
-	}
-
-	inputs := rule.Implicits.Strings()
-	sort.Strings(inputs)
-	sort.Strings(expectedInputs)
-
-	outputs := append(android.WritablePaths{rule.Output}, rule.ImplicitOutputs...).Strings()
-	sort.Strings(outputs)
-	sort.Strings(expectedOutputs)
-
-	android.AssertStringPathsRelativeToTopEquals(t, "inputs", result.Config, expectedInputs, inputs)
-
-	android.AssertStringPathsRelativeToTopEquals(t, "outputs", result.Config, expectedOutputs, outputs)
-}
-
-func TestDexpreoptBootJars(t *testing.T) {
-	ruleFile := "boot-foo.art"
-
-	expectedInputs := []string{
-		"dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art",
-		"dex_bootjars_input/foo.jar",
-		"dex_bootjars_input/bar.jar",
-		"dex_bootjars_input/baz.jar",
-	}
-
-	expectedOutputs := []string{
-		"dex_bootjars/android/system/framework/arm64/boot.invocation",
-		"dex_bootjars/android/system/framework/arm64/boot-foo.art",
-		"dex_bootjars/android/system/framework/arm64/boot-bar.art",
-		"dex_bootjars/android/system/framework/arm64/boot-baz.art",
-		"dex_bootjars/android/system/framework/arm64/boot-foo.oat",
-		"dex_bootjars/android/system/framework/arm64/boot-bar.oat",
-		"dex_bootjars/android/system/framework/arm64/boot-baz.oat",
-		"dex_bootjars/android/system/framework/arm64/boot-foo.vdex",
-		"dex_bootjars/android/system/framework/arm64/boot-bar.vdex",
-		"dex_bootjars/android/system/framework/arm64/boot-baz.vdex",
-		"dex_bootjars_unstripped/android/system/framework/arm64/boot-foo.oat",
-		"dex_bootjars_unstripped/android/system/framework/arm64/boot-bar.oat",
-		"dex_bootjars_unstripped/android/system/framework/arm64/boot-baz.oat",
-	}
-
-	testDexpreoptBoot(t, ruleFile, expectedInputs, expectedOutputs)
-}
-
-// Changes to the boot.zip structure may break the ART APK scanner.
-func TestDexpreoptBootZip(t *testing.T) {
-	ruleFile := "boot.zip"
-
-	ctx := android.PathContextForTesting(android.TestArchConfig("", nil, "", nil))
-	expectedInputs := []string{}
-	for _, target := range ctx.Config().Targets[android.Android] {
-		for _, ext := range []string{".art", ".oat", ".vdex"} {
-			for _, jar := range []string{"foo", "bar", "baz"} {
-				expectedInputs = append(expectedInputs,
-					filepath.Join("dex_bootjars", target.Os.String(), "system/framework", target.Arch.ArchType.String(), "boot-"+jar+ext))
-			}
-		}
-	}
-
-	expectedOutputs := []string{
-		"dex_bootjars/boot.zip",
-	}
-
-	testDexpreoptBoot(t, ruleFile, expectedInputs, expectedOutputs)
-}
diff --git a/java/dexpreopt_check.go b/java/dexpreopt_check.go
index 83c088c..7499481 100644
--- a/java/dexpreopt_check.go
+++ b/java/dexpreopt_check.go
@@ -28,7 +28,7 @@
 }
 
 func RegisterDexpreoptCheckBuildComponents(ctx android.RegistrationContext) {
-	ctx.RegisterSingletonModuleType("dexpreopt_systemserver_check", dexpreoptSystemserverCheckFactory)
+	ctx.RegisterParallelSingletonModuleType("dexpreopt_systemserver_check", dexpreoptSystemserverCheckFactory)
 }
 
 // A build-time check to verify if all compilation artifacts of system server jars are installed
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index 8c62c33..9100e87 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -53,7 +53,7 @@
 		global := dexpreopt.GetGlobalConfig(ctx)
 
 		artModules := global.ArtApexJars
-		frameworkModules := global.BootJars.RemoveList(artModules)
+		frameworkModules := global.BootJars // This includes `artModules`.
 		mainlineBcpModules := global.ApexBootJars
 		frameworkSubdir := "system/framework"
 
@@ -62,8 +62,7 @@
 		artCfg := bootImageConfig{
 			name:                     artBootImageName,
 			stem:                     bootImageStem,
-			installDirOnHost:         "apex/art_boot_images/javalib",
-			installDirOnDevice:       frameworkSubdir,
+			installDir:               "apex/art_boot_images/javalib",
 			profileInstallPathInApex: "etc/boot-image.prof",
 			modules:                  artModules,
 			preloadedClassesFile:     "art/build/boot/preloaded-classes",
@@ -74,26 +73,24 @@
 		// Framework config for the boot image extension.
 		// It includes framework libraries and depends on the ART config.
 		frameworkCfg := bootImageConfig{
-			extends:              &artCfg,
 			name:                 frameworkBootImageName,
 			stem:                 bootImageStem,
-			installDirOnHost:     frameworkSubdir,
-			installDirOnDevice:   frameworkSubdir,
+			installDir:           frameworkSubdir,
 			modules:              frameworkModules,
 			preloadedClassesFile: "frameworks/base/config/preloaded-classes",
 			compilerFilter:       "speed-profile",
 			singleImage:          false,
+			profileImports:       []*bootImageConfig{&artCfg},
 		}
 
 		mainlineCfg := bootImageConfig{
-			extends:            &frameworkCfg,
-			name:               mainlineBootImageName,
-			stem:               bootImageStem,
-			installDirOnHost:   frameworkSubdir,
-			installDirOnDevice: frameworkSubdir,
-			modules:            mainlineBcpModules,
-			compilerFilter:     "verify",
-			singleImage:        true,
+			extends:        &frameworkCfg,
+			name:           mainlineBootImageName,
+			stem:           bootImageStem,
+			installDir:     frameworkSubdir,
+			modules:        mainlineBcpModules,
+			compilerFilter: "verify",
+			singleImage:    true,
 		}
 
 		return map[string]*bootImageConfig{
@@ -108,7 +105,8 @@
 func genBootImageConfigs(ctx android.PathContext) map[string]*bootImageConfig {
 	return ctx.Config().Once(bootImageConfigKey, func() interface{} {
 		targets := dexpreoptTargets(ctx)
-		deviceDir := android.PathForOutput(ctx, ctx.Config().DeviceName())
+		archType := ctx.Config().Targets[android.Android][0].Arch.ArchType
+		deviceDir := android.PathForOutput(ctx, toDexpreoptDirName(archType))
 
 		configs := genBootImageConfigRaw(ctx)
 
@@ -131,12 +129,12 @@
 			// Create target-specific variants.
 			for _, target := range targets {
 				arch := target.Arch.ArchType
-				imageDir := c.dir.Join(ctx, target.Os.String(), c.installDirOnHost, arch.String())
+				imageDir := c.dir.Join(ctx, target.Os.String(), c.installDir, arch.String())
 				variant := &bootImageVariant{
 					bootImageConfig:   c,
 					target:            target,
 					imagePathOnHost:   imageDir.Join(ctx, imageName),
-					imagePathOnDevice: filepath.Join("/", c.installDirOnDevice, arch.String(), imageName),
+					imagePathOnDevice: filepath.Join("/", c.installDir, arch.String(), imageName),
 					imagesDeps:        c.moduleFiles(ctx, imageDir, ".art", ".oat", ".vdex"),
 					dexLocations:      c.modules.DevicePaths(ctx.Config(), target.Os),
 				}
@@ -220,8 +218,8 @@
 func GetApexBootConfig(ctx android.PathContext) apexBootConfig {
 	return ctx.Config().Once(updatableBootConfigKey, func() interface{} {
 		apexBootJars := dexpreopt.GetGlobalConfig(ctx).ApexBootJars
-
-		dir := android.PathForOutput(ctx, ctx.Config().DeviceName(), "apex_bootjars")
+		archType := ctx.Config().Targets[android.Android][0].Arch.ArchType
+		dir := android.PathForOutput(ctx, toDexpreoptDirName(archType), "apex_bootjars")
 		dexPaths := apexBootJars.BuildPaths(ctx, dir)
 		dexPathsByModuleName := apexBootJars.BuildPathsByModule(ctx, dir)
 
@@ -261,3 +259,7 @@
 func dexpreoptConfigMakevars(ctx android.MakeVarsContext) {
 	ctx.Strict("DEXPREOPT_BOOT_JARS_MODULES", strings.Join(defaultBootImageConfig(ctx).modules.CopyOfApexJarPairs(), ":"))
 }
+
+func toDexpreoptDirName(arch android.ArchType) string {
+	return "dexpreopt_" + arch.String()
+}
diff --git a/java/dexpreopt_config_testing.go b/java/dexpreopt_config_testing.go
index 86dd329..6f3aa2b 100644
--- a/java/dexpreopt_config_testing.go
+++ b/java/dexpreopt_config_testing.go
@@ -147,8 +147,7 @@
 	stem                     string
 	dir                      string
 	symbolsDir               string
-	installDirOnDevice       string
-	installDirOnHost         string
+	installDir               string
 	profileInstallPathInApex string
 	modules                  android.ConfiguredJarList
 	dexPaths                 []string
@@ -207,65 +206,64 @@
 	expected := &expectedConfig{
 		name:                     "art",
 		stem:                     "boot",
-		dir:                      "out/soong/test_device/dex_artjars",
-		symbolsDir:               "out/soong/test_device/dex_artjars_unstripped",
-		installDirOnDevice:       "system/framework",
-		installDirOnHost:         "apex/art_boot_images/javalib",
+		dir:                      "out/soong/dexpreopt_arm64/dex_artjars",
+		symbolsDir:               "out/soong/dexpreopt_arm64/dex_artjars_unstripped",
+		installDir:               "apex/art_boot_images/javalib",
 		profileInstallPathInApex: "etc/boot-image.prof",
 		modules:                  android.CreateTestConfiguredJarList([]string{"com.android.art:core1", "com.android.art:core2"}),
-		dexPaths:                 []string{"out/soong/test_device/dex_artjars_input/core1.jar", "out/soong/test_device/dex_artjars_input/core2.jar"},
-		dexPathsDeps:             []string{"out/soong/test_device/dex_artjars_input/core1.jar", "out/soong/test_device/dex_artjars_input/core2.jar"},
-		zip:                      "out/soong/test_device/dex_artjars/art.zip",
+		dexPaths:                 []string{"out/soong/dexpreopt_arm64/dex_artjars_input/core1.jar", "out/soong/dexpreopt_arm64/dex_artjars_input/core2.jar"},
+		dexPathsDeps:             []string{"out/soong/dexpreopt_arm64/dex_artjars_input/core1.jar", "out/soong/dexpreopt_arm64/dex_artjars_input/core2.jar"},
+		zip:                      "out/soong/dexpreopt_arm64/dex_artjars/art.zip",
 		variants: []*expectedVariant{
 			{
 				archType:          android.Arm64,
 				dexLocations:      []string{"/apex/com.android.art/javalib/core1.jar", "/apex/com.android.art/javalib/core2.jar"},
 				dexLocationsDeps:  []string{"/apex/com.android.art/javalib/core1.jar", "/apex/com.android.art/javalib/core2.jar"},
-				imagePathOnHost:   "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art",
-				imagePathOnDevice: "/system/framework/arm64/boot.art",
+				imagePathOnHost:   "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art",
+				imagePathOnDevice: "/apex/art_boot_images/javalib/arm64/boot.art",
 				imagesDeps: []string{
-					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art",
-					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.oat",
-					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.vdex",
-					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.art",
-					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.oat",
-					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.vdex",
+					"out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art",
+					"out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.oat",
+					"out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.vdex",
+					"out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.art",
+					"out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.oat",
+					"out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.vdex",
 				},
 				installs: []normalizedInstall{
 					{
-						from: "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art",
+						from: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art",
 						to:   "/apex/art_boot_images/javalib/arm64/boot.art",
 					},
 					{
-						from: "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.oat",
+						from: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.oat",
 						to:   "/apex/art_boot_images/javalib/arm64/boot.oat",
 					},
 					{
-						from: "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.art",
+						from: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.art",
 						to:   "/apex/art_boot_images/javalib/arm64/boot-core2.art",
 					},
 					{
-						from: "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.oat",
+						from: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.oat",
 						to:   "/apex/art_boot_images/javalib/arm64/boot-core2.oat",
 					},
 				},
 				vdexInstalls: []normalizedInstall{
 					{
-						from: "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.vdex",
+						from: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.vdex",
 						to:   "/apex/art_boot_images/javalib/arm64/boot.vdex",
 					},
 					{
-						from: "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.vdex",
+						from: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.vdex",
 						to:   "/apex/art_boot_images/javalib/arm64/boot-core2.vdex",
 					},
 				},
 				unstrippedInstalls: []normalizedInstall{
 					{
-						from: "out/soong/test_device/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm64/boot.oat",
+						from: "out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm64/boot.oat",
 						to:   "/apex/art_boot_images/javalib/arm64/boot.oat",
 					},
 					{
-						from: "out/soong/test_device/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm64/boot-core2.oat",
+						from: "out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm64/boot-core2.oat",
 						to:   "/apex/art_boot_images/javalib/arm64/boot-core2.oat",
 					},
 				},
@@ -275,51 +273,51 @@
 				archType:          android.Arm,
 				dexLocations:      []string{"/apex/com.android.art/javalib/core1.jar", "/apex/com.android.art/javalib/core2.jar"},
 				dexLocationsDeps:  []string{"/apex/com.android.art/javalib/core1.jar", "/apex/com.android.art/javalib/core2.jar"},
-				imagePathOnHost:   "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art",
-				imagePathOnDevice: "/system/framework/arm/boot.art",
+				imagePathOnHost:   "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art",
+				imagePathOnDevice: "/apex/art_boot_images/javalib/arm/boot.art",
 				imagesDeps: []string{
-					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art",
-					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.oat",
-					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.vdex",
-					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.art",
-					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.oat",
-					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.vdex",
+					"out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art",
+					"out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.oat",
+					"out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.vdex",
+					"out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.art",
+					"out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.oat",
+					"out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.vdex",
 				},
 				installs: []normalizedInstall{
 					{
-						from: "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art",
+						from: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art",
 						to:   "/apex/art_boot_images/javalib/arm/boot.art",
 					},
 					{
-						from: "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.oat",
+						from: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.oat",
 						to:   "/apex/art_boot_images/javalib/arm/boot.oat",
 					},
 					{
-						from: "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.art",
+						from: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.art",
 						to:   "/apex/art_boot_images/javalib/arm/boot-core2.art",
 					},
 					{
-						from: "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.oat",
+						from: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.oat",
 						to:   "/apex/art_boot_images/javalib/arm/boot-core2.oat",
 					},
 				},
 				vdexInstalls: []normalizedInstall{
 					{
-						from: "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.vdex",
+						from: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.vdex",
 						to:   "/apex/art_boot_images/javalib/arm/boot.vdex",
 					},
 					{
-						from: "out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.vdex",
+						from: "out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.vdex",
 						to:   "/apex/art_boot_images/javalib/arm/boot-core2.vdex",
 					},
 				},
 				unstrippedInstalls: []normalizedInstall{
 					{
-						from: "out/soong/test_device/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm/boot.oat",
+						from: "out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm/boot.oat",
 						to:   "/apex/art_boot_images/javalib/arm/boot.oat",
 					},
 					{
-						from: "out/soong/test_device/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm/boot-core2.oat",
+						from: "out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm/boot-core2.oat",
 						to:   "/apex/art_boot_images/javalib/arm/boot-core2.oat",
 					},
 				},
@@ -329,49 +327,49 @@
 				archType:          android.X86_64,
 				dexLocations:      []string{"host/linux-x86/apex/com.android.art/javalib/core1.jar", "host/linux-x86/apex/com.android.art/javalib/core2.jar"},
 				dexLocationsDeps:  []string{"host/linux-x86/apex/com.android.art/javalib/core1.jar", "host/linux-x86/apex/com.android.art/javalib/core2.jar"},
-				imagePathOnHost:   "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art",
-				imagePathOnDevice: "/system/framework/x86_64/boot.art",
+				imagePathOnHost:   "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art",
+				imagePathOnDevice: "/apex/art_boot_images/javalib/x86_64/boot.art",
 				imagesDeps: []string{
-					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art",
-					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.oat",
-					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.vdex",
-					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.art",
-					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat",
-					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.vdex",
+					"out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art",
+					"out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.oat",
+					"out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.vdex",
+					"out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.art",
+					"out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat",
+					"out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.vdex",
 				},
 				installs: []normalizedInstall{
 					{
-						from: "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art",
+						from: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art",
 						to:   "/apex/art_boot_images/javalib/x86_64/boot.art",
 					}, {
-						from: "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.oat",
+						from: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.oat",
 						to:   "/apex/art_boot_images/javalib/x86_64/boot.oat",
 					},
 					{
-						from: "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.art",
+						from: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.art",
 						to:   "/apex/art_boot_images/javalib/x86_64/boot-core2.art",
 					}, {
-						from: "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat",
+						from: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat",
 						to:   "/apex/art_boot_images/javalib/x86_64/boot-core2.oat",
 					},
 				},
 				vdexInstalls: []normalizedInstall{
 					{
-						from: "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.vdex",
+						from: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.vdex",
 						to:   "/apex/art_boot_images/javalib/x86_64/boot.vdex",
 					},
 					{
-						from: "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.vdex",
+						from: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.vdex",
 						to:   "/apex/art_boot_images/javalib/x86_64/boot-core2.vdex",
 					},
 				},
 				unstrippedInstalls: []normalizedInstall{
 					{
-						from: "out/soong/test_device/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.oat",
+						from: "out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.oat",
 						to:   "/apex/art_boot_images/javalib/x86_64/boot.oat",
 					},
 					{
-						from: "out/soong/test_device/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat",
+						from: "out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat",
 						to:   "/apex/art_boot_images/javalib/x86_64/boot-core2.oat",
 					},
 				},
@@ -381,49 +379,49 @@
 				archType:          android.X86,
 				dexLocations:      []string{"host/linux-x86/apex/com.android.art/javalib/core1.jar", "host/linux-x86/apex/com.android.art/javalib/core2.jar"},
 				dexLocationsDeps:  []string{"host/linux-x86/apex/com.android.art/javalib/core1.jar", "host/linux-x86/apex/com.android.art/javalib/core2.jar"},
-				imagePathOnHost:   "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art",
-				imagePathOnDevice: "/system/framework/x86/boot.art",
+				imagePathOnHost:   "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art",
+				imagePathOnDevice: "/apex/art_boot_images/javalib/x86/boot.art",
 				imagesDeps: []string{
-					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art",
-					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat",
-					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.vdex",
-					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.art",
-					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat",
-					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.vdex",
+					"out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art",
+					"out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat",
+					"out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.vdex",
+					"out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.art",
+					"out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat",
+					"out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.vdex",
 				},
 				installs: []normalizedInstall{
 					{
-						from: "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art",
+						from: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art",
 						to:   "/apex/art_boot_images/javalib/x86/boot.art",
 					}, {
-						from: "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat",
+						from: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat",
 						to:   "/apex/art_boot_images/javalib/x86/boot.oat",
 					},
 					{
-						from: "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.art",
+						from: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.art",
 						to:   "/apex/art_boot_images/javalib/x86/boot-core2.art",
 					}, {
-						from: "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat",
+						from: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat",
 						to:   "/apex/art_boot_images/javalib/x86/boot-core2.oat",
 					},
 				},
 				vdexInstalls: []normalizedInstall{
 					{
-						from: "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.vdex",
+						from: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.vdex",
 						to:   "/apex/art_boot_images/javalib/x86/boot.vdex",
 					},
 					{
-						from: "out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.vdex",
+						from: "out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.vdex",
 						to:   "/apex/art_boot_images/javalib/x86/boot-core2.vdex",
 					},
 				},
 				unstrippedInstalls: []normalizedInstall{
 					{
-						from: "out/soong/test_device/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat",
+						from: "out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat",
 						to:   "/apex/art_boot_images/javalib/x86/boot.oat",
 					},
 					{
-						from: "out/soong/test_device/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat",
+						from: "out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat",
 						to:   "/apex/art_boot_images/javalib/x86/boot-core2.oat",
 					},
 				},
@@ -460,203 +458,346 @@
 	expected := &expectedConfig{
 		name:                     "boot",
 		stem:                     "boot",
-		dir:                      "out/soong/test_device/dex_bootjars",
-		symbolsDir:               "out/soong/test_device/dex_bootjars_unstripped",
-		installDirOnDevice:       "system/framework",
-		installDirOnHost:         "system/framework",
+		dir:                      "out/soong/dexpreopt_arm64/dex_bootjars",
+		symbolsDir:               "out/soong/dexpreopt_arm64/dex_bootjars_unstripped",
+		installDir:               "system/framework",
 		profileInstallPathInApex: "",
-		modules:                  android.CreateTestConfiguredJarList([]string{"platform:framework"}),
-		dexPaths:                 []string{"out/soong/test_device/dex_bootjars_input/framework.jar"},
-		dexPathsDeps:             []string{"out/soong/test_device/dex_artjars_input/core1.jar", "out/soong/test_device/dex_artjars_input/core2.jar", "out/soong/test_device/dex_bootjars_input/framework.jar"},
-		zip:                      "out/soong/test_device/dex_bootjars/boot.zip",
+		modules: android.CreateTestConfiguredJarList([]string{
+			"com.android.art:core1",
+			"com.android.art:core2",
+			"platform:framework",
+		}),
+		dexPaths: []string{
+			"out/soong/dexpreopt_arm64/dex_bootjars_input/core1.jar",
+			"out/soong/dexpreopt_arm64/dex_bootjars_input/core2.jar",
+			"out/soong/dexpreopt_arm64/dex_bootjars_input/framework.jar",
+		},
+		dexPathsDeps: []string{
+			"out/soong/dexpreopt_arm64/dex_bootjars_input/core1.jar",
+			"out/soong/dexpreopt_arm64/dex_bootjars_input/core2.jar",
+			"out/soong/dexpreopt_arm64/dex_bootjars_input/framework.jar",
+		},
+		zip: "out/soong/dexpreopt_arm64/dex_bootjars/boot.zip",
 		variants: []*expectedVariant{
 			{
-				archType:     android.Arm64,
-				dexLocations: []string{"/system/framework/framework.jar"},
+				archType: android.Arm64,
+				dexLocations: []string{
+					"/apex/com.android.art/javalib/core1.jar",
+					"/apex/com.android.art/javalib/core2.jar",
+					"/system/framework/framework.jar",
+				},
 				dexLocationsDeps: []string{
 					"/apex/com.android.art/javalib/core1.jar",
 					"/apex/com.android.art/javalib/core2.jar",
 					"/system/framework/framework.jar",
 				},
-				imagePathOnHost:   "out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.art",
-				imagePathOnDevice: "/system/framework/arm64/boot-framework.art",
+				imagePathOnHost:   "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.art",
+				imagePathOnDevice: "/system/framework/arm64/boot.art",
 				imagesDeps: []string{
-					"out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.art",
-					"out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.oat",
-					"out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.vdex",
-				},
-				baseImages: []string{"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art"},
-				baseImagesDeps: []string{
-					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art",
-					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.oat",
-					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.vdex",
-					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.art",
-					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.oat",
-					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.vdex",
+					"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.art",
+					"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.oat",
+					"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.vdex",
+					"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-core2.art",
+					"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-core2.oat",
+					"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-core2.vdex",
+					"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-framework.art",
+					"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-framework.oat",
+					"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-framework.vdex",
 				},
 				installs: []normalizedInstall{
 					{
-						from: "out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.art",
+						from: "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.art",
+						to:   "/system/framework/arm64/boot.art",
+					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.oat",
+						to:   "/system/framework/arm64/boot.oat",
+					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-core2.art",
+						to:   "/system/framework/arm64/boot-core2.art",
+					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-core2.oat",
+						to:   "/system/framework/arm64/boot-core2.oat",
+					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-framework.art",
 						to:   "/system/framework/arm64/boot-framework.art",
 					},
 					{
-						from: "out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.oat",
+						from: "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-framework.oat",
 						to:   "/system/framework/arm64/boot-framework.oat",
 					},
 				},
 				vdexInstalls: []normalizedInstall{
 					{
-						from: "out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.vdex",
+						from: "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.vdex",
+						to:   "/system/framework/arm64/boot.vdex",
+					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-core2.vdex",
+						to:   "/system/framework/arm64/boot-core2.vdex",
+					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-framework.vdex",
 						to:   "/system/framework/arm64/boot-framework.vdex",
 					},
 				},
 				unstrippedInstalls: []normalizedInstall{
 					{
-						from: "out/soong/test_device/dex_bootjars_unstripped/android/system/framework/arm64/boot-framework.oat",
+						from: "out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm64/boot.oat",
+						to:   "/system/framework/arm64/boot.oat",
+					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm64/boot-core2.oat",
+						to:   "/system/framework/arm64/boot-core2.oat",
+					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm64/boot-framework.oat",
 						to:   "/system/framework/arm64/boot-framework.oat",
 					},
 				},
 				licenseMetadataFile: expectedLicenseMetadataFile,
 			},
 			{
-				archType:     android.Arm,
-				dexLocations: []string{"/system/framework/framework.jar"},
+				archType: android.Arm,
+				dexLocations: []string{
+					"/apex/com.android.art/javalib/core1.jar",
+					"/apex/com.android.art/javalib/core2.jar",
+					"/system/framework/framework.jar",
+				},
 				dexLocationsDeps: []string{
 					"/apex/com.android.art/javalib/core1.jar",
 					"/apex/com.android.art/javalib/core2.jar",
 					"/system/framework/framework.jar",
 				},
-				imagePathOnHost:   "out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.art",
-				imagePathOnDevice: "/system/framework/arm/boot-framework.art",
+				imagePathOnHost:   "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.art",
+				imagePathOnDevice: "/system/framework/arm/boot.art",
 				imagesDeps: []string{
-					"out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.art",
-					"out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.oat",
-					"out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.vdex",
-				},
-				baseImages: []string{"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art"},
-				baseImagesDeps: []string{
-					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art",
-					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.oat",
-					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.vdex",
-					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.art",
-					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.oat",
-					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.vdex",
+					"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.art",
+					"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.oat",
+					"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.vdex",
+					"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-core2.art",
+					"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-core2.oat",
+					"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-core2.vdex",
+					"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-framework.art",
+					"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-framework.oat",
+					"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-framework.vdex",
 				},
 				installs: []normalizedInstall{
 					{
-						from: "out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.art",
+						from: "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.art",
+						to:   "/system/framework/arm/boot.art",
+					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.oat",
+						to:   "/system/framework/arm/boot.oat",
+					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-core2.art",
+						to:   "/system/framework/arm/boot-core2.art",
+					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-core2.oat",
+						to:   "/system/framework/arm/boot-core2.oat",
+					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-framework.art",
 						to:   "/system/framework/arm/boot-framework.art",
 					},
 					{
-						from: "out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.oat",
+						from: "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-framework.oat",
 						to:   "/system/framework/arm/boot-framework.oat",
 					},
 				},
 				vdexInstalls: []normalizedInstall{
 					{
-						from: "out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.vdex",
+						from: "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.vdex",
+						to:   "/system/framework/arm/boot.vdex",
+					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-core2.vdex",
+						to:   "/system/framework/arm/boot-core2.vdex",
+					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-framework.vdex",
 						to:   "/system/framework/arm/boot-framework.vdex",
 					},
 				},
 				unstrippedInstalls: []normalizedInstall{
 					{
-						from: "out/soong/test_device/dex_bootjars_unstripped/android/system/framework/arm/boot-framework.oat",
+						from: "out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm/boot.oat",
+						to:   "/system/framework/arm/boot.oat",
+					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm/boot-core2.oat",
+						to:   "/system/framework/arm/boot-core2.oat",
+					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm/boot-framework.oat",
 						to:   "/system/framework/arm/boot-framework.oat",
 					},
 				},
 				licenseMetadataFile: expectedLicenseMetadataFile,
 			},
 			{
-				archType:     android.X86_64,
-				dexLocations: []string{"host/linux-x86/system/framework/framework.jar"},
+				archType: android.X86_64,
+				dexLocations: []string{
+					"host/linux-x86/apex/com.android.art/javalib/core1.jar",
+					"host/linux-x86/apex/com.android.art/javalib/core2.jar",
+					"host/linux-x86/system/framework/framework.jar",
+				},
 				dexLocationsDeps: []string{
 					"host/linux-x86/apex/com.android.art/javalib/core1.jar",
 					"host/linux-x86/apex/com.android.art/javalib/core2.jar",
 					"host/linux-x86/system/framework/framework.jar",
 				},
-				imagePathOnHost:   "out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.art",
-				imagePathOnDevice: "/system/framework/x86_64/boot-framework.art",
+				imagePathOnHost:   "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot.art",
+				imagePathOnDevice: "/system/framework/x86_64/boot.art",
 				imagesDeps: []string{
-					"out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.art",
-					"out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.oat",
-					"out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.vdex",
-				},
-				baseImages: []string{"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art"},
-				baseImagesDeps: []string{
-					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art",
-					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.oat",
-					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.vdex",
-					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.art",
-					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat",
-					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.vdex",
+					"out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot.art",
+					"out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot.oat",
+					"out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot.vdex",
+					"out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-core2.art",
+					"out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-core2.oat",
+					"out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-core2.vdex",
+					"out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.art",
+					"out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.oat",
+					"out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.vdex",
 				},
 				installs: []normalizedInstall{
 					{
-						from: "out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.art",
+						from: "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot.art",
+						to:   "/system/framework/x86_64/boot.art",
+					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot.oat",
+						to:   "/system/framework/x86_64/boot.oat",
+					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-core2.art",
+						to:   "/system/framework/x86_64/boot-core2.art",
+					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-core2.oat",
+						to:   "/system/framework/x86_64/boot-core2.oat",
+					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.art",
 						to:   "/system/framework/x86_64/boot-framework.art",
 					},
 					{
-						from: "out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.oat",
+						from: "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.oat",
 						to:   "/system/framework/x86_64/boot-framework.oat",
 					},
 				},
 				vdexInstalls: []normalizedInstall{
 					{
-						from: "out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.vdex",
+						from: "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot.vdex",
+						to:   "/system/framework/x86_64/boot.vdex",
+					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-core2.vdex",
+						to:   "/system/framework/x86_64/boot-core2.vdex",
+					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.vdex",
 						to:   "/system/framework/x86_64/boot-framework.vdex",
 					},
 				},
 				unstrippedInstalls: []normalizedInstall{
 					{
-						from: "out/soong/test_device/dex_bootjars_unstripped/linux_glibc/system/framework/x86_64/boot-framework.oat",
+						from: "out/soong/dexpreopt_arm64/dex_bootjars_unstripped/linux_glibc/system/framework/x86_64/boot.oat",
+						to:   "/system/framework/x86_64/boot.oat",
+					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_bootjars_unstripped/linux_glibc/system/framework/x86_64/boot-core2.oat",
+						to:   "/system/framework/x86_64/boot-core2.oat",
+					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_bootjars_unstripped/linux_glibc/system/framework/x86_64/boot-framework.oat",
 						to:   "/system/framework/x86_64/boot-framework.oat",
 					},
 				},
 				licenseMetadataFile: expectedLicenseMetadataFile,
 			},
 			{
-				archType:     android.X86,
-				dexLocations: []string{"host/linux-x86/system/framework/framework.jar"},
+				archType: android.X86,
+				dexLocations: []string{
+					"host/linux-x86/apex/com.android.art/javalib/core1.jar",
+					"host/linux-x86/apex/com.android.art/javalib/core2.jar",
+					"host/linux-x86/system/framework/framework.jar",
+				},
 				dexLocationsDeps: []string{
 					"host/linux-x86/apex/com.android.art/javalib/core1.jar",
 					"host/linux-x86/apex/com.android.art/javalib/core2.jar",
 					"host/linux-x86/system/framework/framework.jar",
 				},
-				imagePathOnHost:   "out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.art",
-				imagePathOnDevice: "/system/framework/x86/boot-framework.art",
+				imagePathOnHost:   "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.art",
+				imagePathOnDevice: "/system/framework/x86/boot.art",
 				imagesDeps: []string{
-					"out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.art",
-					"out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.oat",
-					"out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.vdex",
-				},
-				baseImages: []string{"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art"},
-				baseImagesDeps: []string{
-					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art",
-					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat",
-					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.vdex",
-					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.art",
-					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat",
-					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.vdex",
+					"out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.art",
+					"out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.oat",
+					"out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.vdex",
+					"out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-core2.art",
+					"out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-core2.oat",
+					"out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-core2.vdex",
+					"out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.art",
+					"out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.oat",
+					"out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.vdex",
 				},
 				installs: []normalizedInstall{
 					{
-						from: "out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.art",
+						from: "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.art",
+						to:   "/system/framework/x86/boot.art",
+					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.oat",
+						to:   "/system/framework/x86/boot.oat",
+					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-core2.art",
+						to:   "/system/framework/x86/boot-core2.art",
+					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-core2.oat",
+						to:   "/system/framework/x86/boot-core2.oat",
+					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.art",
 						to:   "/system/framework/x86/boot-framework.art",
 					},
 					{
-						from: "out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.oat",
+						from: "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.oat",
 						to:   "/system/framework/x86/boot-framework.oat",
 					},
 				},
 				vdexInstalls: []normalizedInstall{
 					{
-						from: "out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.vdex",
+						from: "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.vdex",
+						to:   "/system/framework/x86/boot.vdex",
+					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-core2.vdex",
+						to:   "/system/framework/x86/boot-core2.vdex",
+					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.vdex",
 						to:   "/system/framework/x86/boot-framework.vdex",
 					},
 				},
 				unstrippedInstalls: []normalizedInstall{
 					{
-						from: "out/soong/test_device/dex_bootjars_unstripped/linux_glibc/system/framework/x86/boot-framework.oat",
+						from: "out/soong/dexpreopt_arm64/dex_bootjars_unstripped/linux_glibc/system/framework/x86/boot.oat",
+						to:   "/system/framework/x86/boot.oat",
+					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_bootjars_unstripped/linux_glibc/system/framework/x86/boot-core2.oat",
+						to:   "/system/framework/x86/boot-core2.oat",
+					},
+					{
+						from: "out/soong/dexpreopt_arm64/dex_bootjars_unstripped/linux_glibc/system/framework/x86/boot-framework.oat",
 						to:   "/system/framework/x86/boot-framework.oat",
 					},
 				},
@@ -664,8 +805,8 @@
 			},
 		},
 		profileInstalls: []normalizedInstall{
-			{from: "out/soong/test_device/dex_bootjars/boot.bprof", to: "/system/etc/boot-image.bprof"},
-			{from: "out/soong/test_device/dex_bootjars/boot.prof", to: "/system/etc/boot-image.prof"},
+			{from: "out/soong/dexpreopt_arm64/dex_bootjars/boot.bprof", to: "/system/etc/boot-image.bprof"},
+			{from: "out/soong/dexpreopt_arm64/dex_bootjars/boot.prof", to: "/system/etc/boot-image.prof"},
 		},
 		profileLicenseMetadataFile: expectedLicenseMetadataFile,
 	}
@@ -691,27 +832,26 @@
 	expected := &expectedConfig{
 		name:                     "mainline",
 		stem:                     "boot",
-		dir:                      "out/soong/test_device/dex_mainlinejars",
-		symbolsDir:               "out/soong/test_device/dex_mainlinejars_unstripped",
-		installDirOnDevice:       "system/framework",
-		installDirOnHost:         "system/framework",
+		dir:                      "out/soong/dexpreopt_arm64/dex_mainlinejars",
+		symbolsDir:               "out/soong/dexpreopt_arm64/dex_mainlinejars_unstripped",
+		installDir:               "system/framework",
 		profileInstallPathInApex: "",
 		modules: android.CreateTestConfiguredJarList([]string{
 			"com.android.foo:framework-foo",
 			"com.android.bar:framework-bar",
 		}),
 		dexPaths: []string{
-			"out/soong/test_device/dex_mainlinejars_input/framework-foo.jar",
-			"out/soong/test_device/dex_mainlinejars_input/framework-bar.jar",
+			"out/soong/dexpreopt_arm64/dex_mainlinejars_input/framework-foo.jar",
+			"out/soong/dexpreopt_arm64/dex_mainlinejars_input/framework-bar.jar",
 		},
 		dexPathsDeps: []string{
-			"out/soong/test_device/dex_artjars_input/core1.jar",
-			"out/soong/test_device/dex_artjars_input/core2.jar",
-			"out/soong/test_device/dex_bootjars_input/framework.jar",
-			"out/soong/test_device/dex_mainlinejars_input/framework-foo.jar",
-			"out/soong/test_device/dex_mainlinejars_input/framework-bar.jar",
+			"out/soong/dexpreopt_arm64/dex_bootjars_input/core1.jar",
+			"out/soong/dexpreopt_arm64/dex_bootjars_input/core2.jar",
+			"out/soong/dexpreopt_arm64/dex_bootjars_input/framework.jar",
+			"out/soong/dexpreopt_arm64/dex_mainlinejars_input/framework-foo.jar",
+			"out/soong/dexpreopt_arm64/dex_mainlinejars_input/framework-bar.jar",
 		},
-		zip: "out/soong/test_device/dex_mainlinejars/mainline.zip",
+		zip: "out/soong/dexpreopt_arm64/dex_mainlinejars/mainline.zip",
 		variants: []*expectedVariant{
 			{
 				archType: android.Arm64,
@@ -726,47 +866,46 @@
 					"/apex/com.android.foo/javalib/framework-foo.jar",
 					"/apex/com.android.bar/javalib/framework-bar.jar",
 				},
-				imagePathOnHost:   "out/soong/test_device/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.art",
+				imagePathOnHost:   "out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.art",
 				imagePathOnDevice: "/system/framework/arm64/boot-framework-foo.art",
 				imagesDeps: []string{
-					"out/soong/test_device/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.art",
-					"out/soong/test_device/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.oat",
-					"out/soong/test_device/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.vdex",
+					"out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.art",
+					"out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.oat",
+					"out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.vdex",
 				},
 				baseImages: []string{
-					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art",
-					"out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.art",
+					"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.art",
 				},
 				baseImagesDeps: []string{
-					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art",
-					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.oat",
-					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.vdex",
-					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.art",
-					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.oat",
-					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.vdex",
-					"out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.art",
-					"out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.oat",
-					"out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.vdex",
+					"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.art",
+					"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.oat",
+					"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.vdex",
+					"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-core2.art",
+					"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-core2.oat",
+					"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-core2.vdex",
+					"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-framework.art",
+					"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-framework.oat",
+					"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-framework.vdex",
 				},
 				installs: []normalizedInstall{
 					{
-						from: "out/soong/test_device/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.art",
+						from: "out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.art",
 						to:   "/system/framework/arm64/boot-framework-foo.art",
 					},
 					{
-						from: "out/soong/test_device/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.oat",
+						from: "out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.oat",
 						to:   "/system/framework/arm64/boot-framework-foo.oat",
 					},
 				},
 				vdexInstalls: []normalizedInstall{
 					{
-						from: "out/soong/test_device/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.vdex",
+						from: "out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.vdex",
 						to:   "/system/framework/arm64/boot-framework-foo.vdex",
 					},
 				},
 				unstrippedInstalls: []normalizedInstall{
 					{
-						from: "out/soong/test_device/dex_mainlinejars_unstripped/android/system/framework/arm64/boot-framework-foo.oat",
+						from: "out/soong/dexpreopt_arm64/dex_mainlinejars_unstripped/android/system/framework/arm64/boot-framework-foo.oat",
 						to:   "/system/framework/arm64/boot-framework-foo.oat",
 					},
 				},
@@ -785,47 +924,46 @@
 					"/apex/com.android.foo/javalib/framework-foo.jar",
 					"/apex/com.android.bar/javalib/framework-bar.jar",
 				},
-				imagePathOnHost:   "out/soong/test_device/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.art",
+				imagePathOnHost:   "out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.art",
 				imagePathOnDevice: "/system/framework/arm/boot-framework-foo.art",
 				imagesDeps: []string{
-					"out/soong/test_device/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.art",
-					"out/soong/test_device/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.oat",
-					"out/soong/test_device/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.vdex",
+					"out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.art",
+					"out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.oat",
+					"out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.vdex",
 				},
 				baseImages: []string{
-					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art",
-					"out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.art",
+					"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.art",
 				},
 				baseImagesDeps: []string{
-					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art",
-					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.oat",
-					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.vdex",
-					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.art",
-					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.oat",
-					"out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.vdex",
-					"out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.art",
-					"out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.oat",
-					"out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.vdex",
+					"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.art",
+					"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.oat",
+					"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.vdex",
+					"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-core2.art",
+					"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-core2.oat",
+					"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-core2.vdex",
+					"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-framework.art",
+					"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-framework.oat",
+					"out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-framework.vdex",
 				},
 				installs: []normalizedInstall{
 					{
-						from: "out/soong/test_device/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.art",
+						from: "out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.art",
 						to:   "/system/framework/arm/boot-framework-foo.art",
 					},
 					{
-						from: "out/soong/test_device/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.oat",
+						from: "out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.oat",
 						to:   "/system/framework/arm/boot-framework-foo.oat",
 					},
 				},
 				vdexInstalls: []normalizedInstall{
 					{
-						from: "out/soong/test_device/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.vdex",
+						from: "out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.vdex",
 						to:   "/system/framework/arm/boot-framework-foo.vdex",
 					},
 				},
 				unstrippedInstalls: []normalizedInstall{
 					{
-						from: "out/soong/test_device/dex_mainlinejars_unstripped/android/system/framework/arm/boot-framework-foo.oat",
+						from: "out/soong/dexpreopt_arm64/dex_mainlinejars_unstripped/android/system/framework/arm/boot-framework-foo.oat",
 						to:   "/system/framework/arm/boot-framework-foo.oat",
 					},
 				},
@@ -844,47 +982,46 @@
 					"host/linux-x86/apex/com.android.foo/javalib/framework-foo.jar",
 					"host/linux-x86/apex/com.android.bar/javalib/framework-bar.jar",
 				},
-				imagePathOnHost:   "out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.art",
+				imagePathOnHost:   "out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.art",
 				imagePathOnDevice: "/system/framework/x86_64/boot-framework-foo.art",
 				imagesDeps: []string{
-					"out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.art",
-					"out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.oat",
-					"out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.vdex",
+					"out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.art",
+					"out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.oat",
+					"out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.vdex",
 				},
 				baseImages: []string{
-					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art",
-					"out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.art",
+					"out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot.art",
 				},
 				baseImagesDeps: []string{
-					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art",
-					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.oat",
-					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.vdex",
-					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.art",
-					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat",
-					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.vdex",
-					"out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.art",
-					"out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.oat",
-					"out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.vdex",
+					"out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot.art",
+					"out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot.oat",
+					"out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot.vdex",
+					"out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-core2.art",
+					"out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-core2.oat",
+					"out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-core2.vdex",
+					"out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.art",
+					"out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.oat",
+					"out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.vdex",
 				},
 				installs: []normalizedInstall{
 					{
-						from: "out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.art",
+						from: "out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.art",
 						to:   "/system/framework/x86_64/boot-framework-foo.art",
 					},
 					{
-						from: "out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.oat",
+						from: "out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.oat",
 						to:   "/system/framework/x86_64/boot-framework-foo.oat",
 					},
 				},
 				vdexInstalls: []normalizedInstall{
 					{
-						from: "out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.vdex",
+						from: "out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.vdex",
 						to:   "/system/framework/x86_64/boot-framework-foo.vdex",
 					},
 				},
 				unstrippedInstalls: []normalizedInstall{
 					{
-						from: "out/soong/test_device/dex_mainlinejars_unstripped/linux_glibc/system/framework/x86_64/boot-framework-foo.oat",
+						from: "out/soong/dexpreopt_arm64/dex_mainlinejars_unstripped/linux_glibc/system/framework/x86_64/boot-framework-foo.oat",
 						to:   "/system/framework/x86_64/boot-framework-foo.oat",
 					},
 				},
@@ -903,47 +1040,46 @@
 					"host/linux-x86/apex/com.android.foo/javalib/framework-foo.jar",
 					"host/linux-x86/apex/com.android.bar/javalib/framework-bar.jar",
 				},
-				imagePathOnHost:   "out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.art",
+				imagePathOnHost:   "out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.art",
 				imagePathOnDevice: "/system/framework/x86/boot-framework-foo.art",
 				imagesDeps: []string{
-					"out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.art",
-					"out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.oat",
-					"out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.vdex",
+					"out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.art",
+					"out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.oat",
+					"out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.vdex",
 				},
 				baseImages: []string{
-					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art",
-					"out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.art",
+					"out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.art",
 				},
 				baseImagesDeps: []string{
-					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art",
-					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat",
-					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.vdex",
-					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.art",
-					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat",
-					"out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.vdex",
-					"out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.art",
-					"out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.oat",
-					"out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.vdex",
+					"out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.art",
+					"out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.oat",
+					"out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.vdex",
+					"out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-core2.art",
+					"out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-core2.oat",
+					"out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-core2.vdex",
+					"out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.art",
+					"out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.oat",
+					"out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.vdex",
 				},
 				installs: []normalizedInstall{
 					{
-						from: "out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.art",
+						from: "out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.art",
 						to:   "/system/framework/x86/boot-framework-foo.art",
 					},
 					{
-						from: "out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.oat",
+						from: "out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.oat",
 						to:   "/system/framework/x86/boot-framework-foo.oat",
 					},
 				},
 				vdexInstalls: []normalizedInstall{
 					{
-						from: "out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.vdex",
+						from: "out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.vdex",
 						to:   "/system/framework/x86/boot-framework-foo.vdex",
 					},
 				},
 				unstrippedInstalls: []normalizedInstall{
 					{
-						from: "out/soong/test_device/dex_mainlinejars_unstripped/linux_glibc/system/framework/x86/boot-framework-foo.oat",
+						from: "out/soong/dexpreopt_arm64/dex_mainlinejars_unstripped/linux_glibc/system/framework/x86/boot-framework-foo.oat",
 						to:   "/system/framework/x86/boot-framework-foo.oat",
 					},
 				},
@@ -999,8 +1135,7 @@
 	android.AssertStringEquals(t, "stem", expected.stem, imageConfig.stem)
 	android.AssertPathRelativeToTopEquals(t, "dir", expected.dir, imageConfig.dir)
 	android.AssertPathRelativeToTopEquals(t, "symbolsDir", expected.symbolsDir, imageConfig.symbolsDir)
-	android.AssertStringEquals(t, "installDirOnDevice", expected.installDirOnDevice, imageConfig.installDirOnDevice)
-	android.AssertStringEquals(t, "installDirOnHost", expected.installDirOnHost, imageConfig.installDirOnHost)
+	android.AssertStringEquals(t, "installDir", expected.installDir, imageConfig.installDir)
 	android.AssertStringEquals(t, "profileInstallPathInApex", expected.profileInstallPathInApex, imageConfig.profileInstallPathInApex)
 	android.AssertDeepEquals(t, "modules", expected.modules, imageConfig.modules)
 	android.AssertPathsRelativeToTopEquals(t, "dexPaths", expected.dexPaths, imageConfig.dexPaths.Paths())
@@ -1056,34 +1191,34 @@
 		fmt.Fprintf(out, "%s=%s\n", v.Name(), android.StringRelativeToTop(result.Config, v.Value()))
 	}
 	format := `
-DEXPREOPT_BOOTCLASSPATH_DEX_FILES=out/soong/test_device/dex_artjars_input/core1.jar out/soong/test_device/dex_artjars_input/core2.jar out/soong/test_device/dex_bootjars_input/framework.jar
+DEXPREOPT_BOOTCLASSPATH_DEX_FILES=out/soong/dexpreopt_arm64/dex_bootjars_input/core1.jar out/soong/dexpreopt_arm64/dex_bootjars_input/core2.jar out/soong/dexpreopt_arm64/dex_bootjars_input/framework.jar
 DEXPREOPT_BOOTCLASSPATH_DEX_LOCATIONS=/apex/com.android.art/javalib/core1.jar /apex/com.android.art/javalib/core2.jar /system/framework/framework.jar
-DEXPREOPT_BOOT_JARS_MODULES=platform:framework
+DEXPREOPT_BOOT_JARS_MODULES=com.android.art:core1:com.android.art:core2:platform:framework
 DEXPREOPT_GEN=out/host/linux-x86/bin/dexpreopt_gen
-DEXPREOPT_IMAGE_BUILT_INSTALLED_art_arm=out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art:/apex/art_boot_images/javalib/arm/boot.art out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.oat:/apex/art_boot_images/javalib/arm/boot.oat out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.art:/apex/art_boot_images/javalib/arm/boot-core2.art out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.oat:/apex/art_boot_images/javalib/arm/boot-core2.oat
-DEXPREOPT_IMAGE_BUILT_INSTALLED_art_arm64=out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art:/apex/art_boot_images/javalib/arm64/boot.art out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.oat:/apex/art_boot_images/javalib/arm64/boot.oat out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.art:/apex/art_boot_images/javalib/arm64/boot-core2.art out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.oat:/apex/art_boot_images/javalib/arm64/boot-core2.oat
-DEXPREOPT_IMAGE_BUILT_INSTALLED_art_host_x86=out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art:/apex/art_boot_images/javalib/x86/boot.art out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat:/apex/art_boot_images/javalib/x86/boot.oat out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.art:/apex/art_boot_images/javalib/x86/boot-core2.art out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat:/apex/art_boot_images/javalib/x86/boot-core2.oat
-DEXPREOPT_IMAGE_BUILT_INSTALLED_art_host_x86_64=out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art:/apex/art_boot_images/javalib/x86_64/boot.art out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.oat:/apex/art_boot_images/javalib/x86_64/boot.oat out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.art:/apex/art_boot_images/javalib/x86_64/boot-core2.art out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat:/apex/art_boot_images/javalib/x86_64/boot-core2.oat
-DEXPREOPT_IMAGE_BUILT_INSTALLED_boot_arm=out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.art:/system/framework/arm/boot-framework.art out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.oat:/system/framework/arm/boot-framework.oat
-DEXPREOPT_IMAGE_BUILT_INSTALLED_boot_arm64=out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.art:/system/framework/arm64/boot-framework.art out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.oat:/system/framework/arm64/boot-framework.oat
-DEXPREOPT_IMAGE_BUILT_INSTALLED_boot_host_x86=out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.art:/system/framework/x86/boot-framework.art out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.oat:/system/framework/x86/boot-framework.oat
-DEXPREOPT_IMAGE_BUILT_INSTALLED_boot_host_x86_64=out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.art:/system/framework/x86_64/boot-framework.art out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.oat:/system/framework/x86_64/boot-framework.oat
-DEXPREOPT_IMAGE_BUILT_INSTALLED_mainline_arm=out/soong/test_device/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.art:/system/framework/arm/boot-framework-foo.art out/soong/test_device/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.oat:/system/framework/arm/boot-framework-foo.oat
-DEXPREOPT_IMAGE_BUILT_INSTALLED_mainline_arm64=out/soong/test_device/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.art:/system/framework/arm64/boot-framework-foo.art out/soong/test_device/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.oat:/system/framework/arm64/boot-framework-foo.oat
-DEXPREOPT_IMAGE_BUILT_INSTALLED_mainline_host_x86=out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.art:/system/framework/x86/boot-framework-foo.art out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.oat:/system/framework/x86/boot-framework-foo.oat
-DEXPREOPT_IMAGE_BUILT_INSTALLED_mainline_host_x86_64=out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.art:/system/framework/x86_64/boot-framework-foo.art out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.oat:/system/framework/x86_64/boot-framework-foo.oat
-DEXPREOPT_IMAGE_DEPS_art_arm=out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.oat out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.vdex out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.art out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.oat out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.vdex
-DEXPREOPT_IMAGE_DEPS_art_arm64=out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.oat out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.vdex out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.art out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.oat out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.vdex
-DEXPREOPT_IMAGE_DEPS_art_host_x86=out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.vdex out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.art out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.vdex
-DEXPREOPT_IMAGE_DEPS_art_host_x86_64=out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.oat out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.vdex out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.art out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.vdex
-DEXPREOPT_IMAGE_DEPS_boot_arm=out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.art out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.oat out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.vdex
-DEXPREOPT_IMAGE_DEPS_boot_arm64=out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.art out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.oat out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.vdex
-DEXPREOPT_IMAGE_DEPS_boot_host_x86=out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.art out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.oat out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.vdex
-DEXPREOPT_IMAGE_DEPS_boot_host_x86_64=out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.art out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.oat out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.vdex
-DEXPREOPT_IMAGE_DEPS_mainline_arm=out/soong/test_device/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.art out/soong/test_device/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.oat out/soong/test_device/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.vdex
-DEXPREOPT_IMAGE_DEPS_mainline_arm64=out/soong/test_device/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.art out/soong/test_device/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.oat out/soong/test_device/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.vdex
-DEXPREOPT_IMAGE_DEPS_mainline_host_x86=out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.art out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.oat out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.vdex
-DEXPREOPT_IMAGE_DEPS_mainline_host_x86_64=out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.art out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.oat out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.vdex
+DEXPREOPT_IMAGE_BUILT_INSTALLED_art_arm=out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art:/apex/art_boot_images/javalib/arm/boot.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.oat:/apex/art_boot_images/javalib/arm/boot.oat out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.art:/apex/art_boot_images/javalib/arm/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.oat:/apex/art_boot_images/javalib/arm/boot-core2.oat
+DEXPREOPT_IMAGE_BUILT_INSTALLED_art_arm64=out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art:/apex/art_boot_images/javalib/arm64/boot.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.oat:/apex/art_boot_images/javalib/arm64/boot.oat out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.art:/apex/art_boot_images/javalib/arm64/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.oat:/apex/art_boot_images/javalib/arm64/boot-core2.oat
+DEXPREOPT_IMAGE_BUILT_INSTALLED_art_host_x86=out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art:/apex/art_boot_images/javalib/x86/boot.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat:/apex/art_boot_images/javalib/x86/boot.oat out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.art:/apex/art_boot_images/javalib/x86/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat:/apex/art_boot_images/javalib/x86/boot-core2.oat
+DEXPREOPT_IMAGE_BUILT_INSTALLED_art_host_x86_64=out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art:/apex/art_boot_images/javalib/x86_64/boot.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.oat:/apex/art_boot_images/javalib/x86_64/boot.oat out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.art:/apex/art_boot_images/javalib/x86_64/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat:/apex/art_boot_images/javalib/x86_64/boot-core2.oat
+DEXPREOPT_IMAGE_BUILT_INSTALLED_boot_arm=out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.art:/system/framework/arm/boot.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.oat:/system/framework/arm/boot.oat out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-core2.art:/system/framework/arm/boot-core2.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-core2.oat:/system/framework/arm/boot-core2.oat out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-framework.art:/system/framework/arm/boot-framework.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-framework.oat:/system/framework/arm/boot-framework.oat
+DEXPREOPT_IMAGE_BUILT_INSTALLED_boot_arm64=out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.art:/system/framework/arm64/boot.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.oat:/system/framework/arm64/boot.oat out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-core2.art:/system/framework/arm64/boot-core2.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-core2.oat:/system/framework/arm64/boot-core2.oat out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-framework.art:/system/framework/arm64/boot-framework.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-framework.oat:/system/framework/arm64/boot-framework.oat
+DEXPREOPT_IMAGE_BUILT_INSTALLED_boot_host_x86=out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.art:/system/framework/x86/boot.art out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.oat:/system/framework/x86/boot.oat out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-core2.art:/system/framework/x86/boot-core2.art out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-core2.oat:/system/framework/x86/boot-core2.oat out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.art:/system/framework/x86/boot-framework.art out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.oat:/system/framework/x86/boot-framework.oat
+DEXPREOPT_IMAGE_BUILT_INSTALLED_boot_host_x86_64=out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot.art:/system/framework/x86_64/boot.art out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot.oat:/system/framework/x86_64/boot.oat out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-core2.art:/system/framework/x86_64/boot-core2.art out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-core2.oat:/system/framework/x86_64/boot-core2.oat out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.art:/system/framework/x86_64/boot-framework.art out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.oat:/system/framework/x86_64/boot-framework.oat
+DEXPREOPT_IMAGE_BUILT_INSTALLED_mainline_arm=out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.art:/system/framework/arm/boot-framework-foo.art out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.oat:/system/framework/arm/boot-framework-foo.oat
+DEXPREOPT_IMAGE_BUILT_INSTALLED_mainline_arm64=out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.art:/system/framework/arm64/boot-framework-foo.art out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.oat:/system/framework/arm64/boot-framework-foo.oat
+DEXPREOPT_IMAGE_BUILT_INSTALLED_mainline_host_x86=out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.art:/system/framework/x86/boot-framework-foo.art out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.oat:/system/framework/x86/boot-framework-foo.oat
+DEXPREOPT_IMAGE_BUILT_INSTALLED_mainline_host_x86_64=out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.art:/system/framework/x86_64/boot-framework-foo.art out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.oat:/system/framework/x86_64/boot-framework-foo.oat
+DEXPREOPT_IMAGE_DEPS_art_arm=out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.oat out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.vdex
+DEXPREOPT_IMAGE_DEPS_art_arm64=out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.oat out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.vdex
+DEXPREOPT_IMAGE_DEPS_art_host_x86=out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.vdex
+DEXPREOPT_IMAGE_DEPS_art_host_x86_64=out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.oat out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.art out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.vdex
+DEXPREOPT_IMAGE_DEPS_boot_arm=out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.oat out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.vdex out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-core2.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-core2.oat out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-core2.vdex out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-framework.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-framework.oat out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-framework.vdex
+DEXPREOPT_IMAGE_DEPS_boot_arm64=out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.oat out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.vdex out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-core2.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-core2.oat out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-core2.vdex out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-framework.art out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-framework.oat out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-framework.vdex
+DEXPREOPT_IMAGE_DEPS_boot_host_x86=out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.art out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.oat out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.vdex out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-core2.art out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-core2.oat out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-core2.vdex out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.art out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.oat out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.vdex
+DEXPREOPT_IMAGE_DEPS_boot_host_x86_64=out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot.art out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot.oat out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot.vdex out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-core2.art out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-core2.oat out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-core2.vdex out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.art out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.oat out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.vdex
+DEXPREOPT_IMAGE_DEPS_mainline_arm=out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.art out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.oat out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.vdex
+DEXPREOPT_IMAGE_DEPS_mainline_arm64=out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.art out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.oat out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.vdex
+DEXPREOPT_IMAGE_DEPS_mainline_host_x86=out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.art out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.oat out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.vdex
+DEXPREOPT_IMAGE_DEPS_mainline_host_x86_64=out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.art out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.oat out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.vdex
 DEXPREOPT_IMAGE_LICENSE_METADATA_art_arm=%[1]s
 DEXPREOPT_IMAGE_LICENSE_METADATA_art_arm64=%[1]s
 DEXPREOPT_IMAGE_LICENSE_METADATA_art_host_x86=%[1]s
@@ -1096,54 +1231,54 @@
 DEXPREOPT_IMAGE_LICENSE_METADATA_mainline_arm64=out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic
 DEXPREOPT_IMAGE_LICENSE_METADATA_mainline_host_x86=out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic
 DEXPREOPT_IMAGE_LICENSE_METADATA_mainline_host_x86_64=out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic
-DEXPREOPT_IMAGE_LOCATIONS_ON_DEVICEart=/system/framework/boot.art
-DEXPREOPT_IMAGE_LOCATIONS_ON_DEVICEboot=/system/framework/boot.art:/system/framework/boot-framework.art
-DEXPREOPT_IMAGE_LOCATIONS_ON_DEVICEmainline=/system/framework/boot.art:/system/framework/boot-framework.art:/system/framework/boot-framework-foo.art
-DEXPREOPT_IMAGE_LOCATIONS_ON_HOSTart=out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/boot.art
-DEXPREOPT_IMAGE_LOCATIONS_ON_HOSTboot=out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/boot.art:out/soong/test_device/dex_bootjars/android/system/framework/boot-framework.art
-DEXPREOPT_IMAGE_LOCATIONS_ON_HOSTmainline=out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/boot.art:out/soong/test_device/dex_bootjars/android/system/framework/boot-framework.art:out/soong/test_device/dex_mainlinejars/android/system/framework/boot-framework-foo.art
+DEXPREOPT_IMAGE_LOCATIONS_ON_DEVICEart=/apex/art_boot_images/javalib/boot.art
+DEXPREOPT_IMAGE_LOCATIONS_ON_DEVICEboot=/system/framework/boot.art
+DEXPREOPT_IMAGE_LOCATIONS_ON_DEVICEmainline=/system/framework/boot.art:/system/framework/boot-framework-foo.art
+DEXPREOPT_IMAGE_LOCATIONS_ON_HOSTart=out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/boot.art
+DEXPREOPT_IMAGE_LOCATIONS_ON_HOSTboot=out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/boot.art
+DEXPREOPT_IMAGE_LOCATIONS_ON_HOSTmainline=out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/boot.art:out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/boot-framework-foo.art
 DEXPREOPT_IMAGE_NAMES=art boot mainline
-DEXPREOPT_IMAGE_PROFILE_BUILT_INSTALLED=out/soong/test_device/dex_bootjars/boot.bprof:/system/etc/boot-image.bprof out/soong/test_device/dex_bootjars/boot.prof:/system/etc/boot-image.prof
+DEXPREOPT_IMAGE_PROFILE_BUILT_INSTALLED=out/soong/dexpreopt_arm64/dex_bootjars/boot.bprof:/system/etc/boot-image.bprof out/soong/dexpreopt_arm64/dex_bootjars/boot.prof:/system/etc/boot-image.prof
 DEXPREOPT_IMAGE_PROFILE_LICENSE_METADATA=out/soong/.intermediates/frameworks/base/boot/platform-bootclasspath/android_common/meta_lic
-DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_art_arm=out/soong/test_device/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm/boot.oat:/apex/art_boot_images/javalib/arm/boot.oat out/soong/test_device/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm/boot-core2.oat:/apex/art_boot_images/javalib/arm/boot-core2.oat
-DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_art_arm64=out/soong/test_device/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm64/boot.oat:/apex/art_boot_images/javalib/arm64/boot.oat out/soong/test_device/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm64/boot-core2.oat:/apex/art_boot_images/javalib/arm64/boot-core2.oat
-DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_art_host_x86=out/soong/test_device/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat:/apex/art_boot_images/javalib/x86/boot.oat out/soong/test_device/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat:/apex/art_boot_images/javalib/x86/boot-core2.oat
-DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_art_host_x86_64=out/soong/test_device/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.oat:/apex/art_boot_images/javalib/x86_64/boot.oat out/soong/test_device/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat:/apex/art_boot_images/javalib/x86_64/boot-core2.oat
-DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_boot_arm=out/soong/test_device/dex_bootjars_unstripped/android/system/framework/arm/boot-framework.oat:/system/framework/arm/boot-framework.oat
-DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_boot_arm64=out/soong/test_device/dex_bootjars_unstripped/android/system/framework/arm64/boot-framework.oat:/system/framework/arm64/boot-framework.oat
-DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_boot_host_x86=out/soong/test_device/dex_bootjars_unstripped/linux_glibc/system/framework/x86/boot-framework.oat:/system/framework/x86/boot-framework.oat
-DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_boot_host_x86_64=out/soong/test_device/dex_bootjars_unstripped/linux_glibc/system/framework/x86_64/boot-framework.oat:/system/framework/x86_64/boot-framework.oat
-DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_mainline_arm=out/soong/test_device/dex_mainlinejars_unstripped/android/system/framework/arm/boot-framework-foo.oat:/system/framework/arm/boot-framework-foo.oat
-DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_mainline_arm64=out/soong/test_device/dex_mainlinejars_unstripped/android/system/framework/arm64/boot-framework-foo.oat:/system/framework/arm64/boot-framework-foo.oat
-DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_mainline_host_x86=out/soong/test_device/dex_mainlinejars_unstripped/linux_glibc/system/framework/x86/boot-framework-foo.oat:/system/framework/x86/boot-framework-foo.oat
-DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_mainline_host_x86_64=out/soong/test_device/dex_mainlinejars_unstripped/linux_glibc/system/framework/x86_64/boot-framework-foo.oat:/system/framework/x86_64/boot-framework-foo.oat
-DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_art_arm=out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.vdex:/apex/art_boot_images/javalib/arm/boot.vdex out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.vdex:/apex/art_boot_images/javalib/arm/boot-core2.vdex
-DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_art_arm64=out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.vdex:/apex/art_boot_images/javalib/arm64/boot.vdex out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.vdex:/apex/art_boot_images/javalib/arm64/boot-core2.vdex
-DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_art_host_x86=out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.vdex:/apex/art_boot_images/javalib/x86/boot.vdex out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.vdex:/apex/art_boot_images/javalib/x86/boot-core2.vdex
-DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_art_host_x86_64=out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.vdex:/apex/art_boot_images/javalib/x86_64/boot.vdex out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.vdex:/apex/art_boot_images/javalib/x86_64/boot-core2.vdex
-DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_boot_arm=out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.vdex:/system/framework/arm/boot-framework.vdex
-DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_boot_arm64=out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.vdex:/system/framework/arm64/boot-framework.vdex
-DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_boot_host_x86=out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.vdex:/system/framework/x86/boot-framework.vdex
-DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_boot_host_x86_64=out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.vdex:/system/framework/x86_64/boot-framework.vdex
-DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_mainline_arm=out/soong/test_device/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.vdex:/system/framework/arm/boot-framework-foo.vdex
-DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_mainline_arm64=out/soong/test_device/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.vdex:/system/framework/arm64/boot-framework-foo.vdex
-DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_mainline_host_x86=out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.vdex:/system/framework/x86/boot-framework-foo.vdex
-DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_mainline_host_x86_64=out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.vdex:/system/framework/x86_64/boot-framework-foo.vdex
-DEXPREOPT_IMAGE_ZIP_art=out/soong/test_device/dex_artjars/art.zip
-DEXPREOPT_IMAGE_ZIP_boot=out/soong/test_device/dex_bootjars/boot.zip
-DEXPREOPT_IMAGE_ZIP_mainline=out/soong/test_device/dex_mainlinejars/mainline.zip
-DEXPREOPT_IMAGE_art_arm=out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art
-DEXPREOPT_IMAGE_art_arm64=out/soong/test_device/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art
-DEXPREOPT_IMAGE_art_host_x86=out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art
-DEXPREOPT_IMAGE_art_host_x86_64=out/soong/test_device/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art
-DEXPREOPT_IMAGE_boot_arm=out/soong/test_device/dex_bootjars/android/system/framework/arm/boot-framework.art
-DEXPREOPT_IMAGE_boot_arm64=out/soong/test_device/dex_bootjars/android/system/framework/arm64/boot-framework.art
-DEXPREOPT_IMAGE_boot_host_x86=out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.art
-DEXPREOPT_IMAGE_boot_host_x86_64=out/soong/test_device/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.art
-DEXPREOPT_IMAGE_mainline_arm=out/soong/test_device/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.art
-DEXPREOPT_IMAGE_mainline_arm64=out/soong/test_device/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.art
-DEXPREOPT_IMAGE_mainline_host_x86=out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.art
-DEXPREOPT_IMAGE_mainline_host_x86_64=out/soong/test_device/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.art
+DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_art_arm=out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm/boot.oat:/apex/art_boot_images/javalib/arm/boot.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm/boot-core2.oat:/apex/art_boot_images/javalib/arm/boot-core2.oat
+DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_art_arm64=out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm64/boot.oat:/apex/art_boot_images/javalib/arm64/boot.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/android/apex/art_boot_images/javalib/arm64/boot-core2.oat:/apex/art_boot_images/javalib/arm64/boot-core2.oat
+DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_art_host_x86=out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86/boot.oat:/apex/art_boot_images/javalib/x86/boot.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.oat:/apex/art_boot_images/javalib/x86/boot-core2.oat
+DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_art_host_x86_64=out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.oat:/apex/art_boot_images/javalib/x86_64/boot.oat out/soong/dexpreopt_arm64/dex_artjars_unstripped/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.oat:/apex/art_boot_images/javalib/x86_64/boot-core2.oat
+DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_boot_arm=out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm/boot.oat:/system/framework/arm/boot.oat out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm/boot-core2.oat:/system/framework/arm/boot-core2.oat out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm/boot-framework.oat:/system/framework/arm/boot-framework.oat
+DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_boot_arm64=out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm64/boot.oat:/system/framework/arm64/boot.oat out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm64/boot-core2.oat:/system/framework/arm64/boot-core2.oat out/soong/dexpreopt_arm64/dex_bootjars_unstripped/android/system/framework/arm64/boot-framework.oat:/system/framework/arm64/boot-framework.oat
+DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_boot_host_x86=out/soong/dexpreopt_arm64/dex_bootjars_unstripped/linux_glibc/system/framework/x86/boot.oat:/system/framework/x86/boot.oat out/soong/dexpreopt_arm64/dex_bootjars_unstripped/linux_glibc/system/framework/x86/boot-core2.oat:/system/framework/x86/boot-core2.oat out/soong/dexpreopt_arm64/dex_bootjars_unstripped/linux_glibc/system/framework/x86/boot-framework.oat:/system/framework/x86/boot-framework.oat
+DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_boot_host_x86_64=out/soong/dexpreopt_arm64/dex_bootjars_unstripped/linux_glibc/system/framework/x86_64/boot.oat:/system/framework/x86_64/boot.oat out/soong/dexpreopt_arm64/dex_bootjars_unstripped/linux_glibc/system/framework/x86_64/boot-core2.oat:/system/framework/x86_64/boot-core2.oat out/soong/dexpreopt_arm64/dex_bootjars_unstripped/linux_glibc/system/framework/x86_64/boot-framework.oat:/system/framework/x86_64/boot-framework.oat
+DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_mainline_arm=out/soong/dexpreopt_arm64/dex_mainlinejars_unstripped/android/system/framework/arm/boot-framework-foo.oat:/system/framework/arm/boot-framework-foo.oat
+DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_mainline_arm64=out/soong/dexpreopt_arm64/dex_mainlinejars_unstripped/android/system/framework/arm64/boot-framework-foo.oat:/system/framework/arm64/boot-framework-foo.oat
+DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_mainline_host_x86=out/soong/dexpreopt_arm64/dex_mainlinejars_unstripped/linux_glibc/system/framework/x86/boot-framework-foo.oat:/system/framework/x86/boot-framework-foo.oat
+DEXPREOPT_IMAGE_UNSTRIPPED_BUILT_INSTALLED_mainline_host_x86_64=out/soong/dexpreopt_arm64/dex_mainlinejars_unstripped/linux_glibc/system/framework/x86_64/boot-framework-foo.oat:/system/framework/x86_64/boot-framework-foo.oat
+DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_art_arm=out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.vdex:/apex/art_boot_images/javalib/arm/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot-core2.vdex:/apex/art_boot_images/javalib/arm/boot-core2.vdex
+DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_art_arm64=out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.vdex:/apex/art_boot_images/javalib/arm64/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot-core2.vdex:/apex/art_boot_images/javalib/arm64/boot-core2.vdex
+DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_art_host_x86=out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.vdex:/apex/art_boot_images/javalib/x86/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot-core2.vdex:/apex/art_boot_images/javalib/x86/boot-core2.vdex
+DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_art_host_x86_64=out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.vdex:/apex/art_boot_images/javalib/x86_64/boot.vdex out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot-core2.vdex:/apex/art_boot_images/javalib/x86_64/boot-core2.vdex
+DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_boot_arm=out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.vdex:/system/framework/arm/boot.vdex out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-core2.vdex:/system/framework/arm/boot-core2.vdex out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot-framework.vdex:/system/framework/arm/boot-framework.vdex
+DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_boot_arm64=out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.vdex:/system/framework/arm64/boot.vdex out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-core2.vdex:/system/framework/arm64/boot-core2.vdex out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot-framework.vdex:/system/framework/arm64/boot-framework.vdex
+DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_boot_host_x86=out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.vdex:/system/framework/x86/boot.vdex out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-core2.vdex:/system/framework/x86/boot-core2.vdex out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot-framework.vdex:/system/framework/x86/boot-framework.vdex
+DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_boot_host_x86_64=out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot.vdex:/system/framework/x86_64/boot.vdex out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-core2.vdex:/system/framework/x86_64/boot-core2.vdex out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot-framework.vdex:/system/framework/x86_64/boot-framework.vdex
+DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_mainline_arm=out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.vdex:/system/framework/arm/boot-framework-foo.vdex
+DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_mainline_arm64=out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.vdex:/system/framework/arm64/boot-framework-foo.vdex
+DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_mainline_host_x86=out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.vdex:/system/framework/x86/boot-framework-foo.vdex
+DEXPREOPT_IMAGE_VDEX_BUILT_INSTALLED_mainline_host_x86_64=out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.vdex:/system/framework/x86_64/boot-framework-foo.vdex
+DEXPREOPT_IMAGE_ZIP_art=out/soong/dexpreopt_arm64/dex_artjars/art.zip
+DEXPREOPT_IMAGE_ZIP_boot=out/soong/dexpreopt_arm64/dex_bootjars/boot.zip
+DEXPREOPT_IMAGE_ZIP_mainline=out/soong/dexpreopt_arm64/dex_mainlinejars/mainline.zip
+DEXPREOPT_IMAGE_art_arm=out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm/boot.art
+DEXPREOPT_IMAGE_art_arm64=out/soong/dexpreopt_arm64/dex_artjars/android/apex/art_boot_images/javalib/arm64/boot.art
+DEXPREOPT_IMAGE_art_host_x86=out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86/boot.art
+DEXPREOPT_IMAGE_art_host_x86_64=out/soong/dexpreopt_arm64/dex_artjars/linux_glibc/apex/art_boot_images/javalib/x86_64/boot.art
+DEXPREOPT_IMAGE_boot_arm=out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm/boot.art
+DEXPREOPT_IMAGE_boot_arm64=out/soong/dexpreopt_arm64/dex_bootjars/android/system/framework/arm64/boot.art
+DEXPREOPT_IMAGE_boot_host_x86=out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86/boot.art
+DEXPREOPT_IMAGE_boot_host_x86_64=out/soong/dexpreopt_arm64/dex_bootjars/linux_glibc/system/framework/x86_64/boot.art
+DEXPREOPT_IMAGE_mainline_arm=out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm/boot-framework-foo.art
+DEXPREOPT_IMAGE_mainline_arm64=out/soong/dexpreopt_arm64/dex_mainlinejars/android/system/framework/arm64/boot-framework-foo.art
+DEXPREOPT_IMAGE_mainline_host_x86=out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86/boot-framework-foo.art
+DEXPREOPT_IMAGE_mainline_host_x86_64=out/soong/dexpreopt_arm64/dex_mainlinejars/linux_glibc/system/framework/x86_64/boot-framework-foo.art
 `
 	expected := strings.TrimSpace(fmt.Sprintf(format, expectedLicenseMetadataFile))
 	actual := strings.TrimSpace(out.String())
diff --git a/java/dexpreopt_test.go b/java/dexpreopt_test.go
index 3d2c5c3..fedd564 100644
--- a/java/dexpreopt_test.go
+++ b/java/dexpreopt_test.go
@@ -212,7 +212,7 @@
 	for _, test := range tests {
 		t.Run(test.name, func(t *testing.T) {
 			preparers := android.GroupFixturePreparers(
-				PrepareForTestWithJavaDefaultModules,
+				PrepareForTestWithDexpreopt,
 				PrepareForTestWithFakeApexMutator,
 				dexpreopt.FixtureSetApexSystemServerJars("com.android.apex1:service-foo"),
 			)
@@ -257,7 +257,7 @@
 
 	preparers := android.GroupFixturePreparers(
 		cc.PrepareForTestWithCcDefaultModules,
-		PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd,
+		PrepareForTestWithDexpreoptWithoutFakeDex2oatd,
 		dexpreopt.PrepareForTestByEnablingDexpreopt)
 
 	testDex2oatToolDep := func(sourceEnabled, prebuiltEnabled, prebuiltPreferred bool,
@@ -299,7 +299,7 @@
 
 func TestDexpreoptBuiltInstalledForApex(t *testing.T) {
 	preparers := android.GroupFixturePreparers(
-		PrepareForTestWithJavaDefaultModules,
+		PrepareForTestWithDexpreopt,
 		PrepareForTestWithFakeApexMutator,
 		dexpreopt.FixtureSetApexSystemServerJars("com.android.apex1:service-foo"),
 	)
@@ -386,7 +386,7 @@
 
 func TestAndroidMkEntriesForApex(t *testing.T) {
 	preparers := android.GroupFixturePreparers(
-		PrepareForTestWithJavaDefaultModules,
+		PrepareForTestWithDexpreopt,
 		PrepareForTestWithFakeApexMutator,
 		dexpreopt.FixtureSetApexSystemServerJars("com.android.apex1:service-foo"),
 	)
@@ -438,3 +438,28 @@
 
 	android.AssertIntEquals(t, "entries count", 0, len(entriesList))
 }
+
+func TestGenerateProfileEvenIfDexpreoptIsDisabled(t *testing.T) {
+	preparers := android.GroupFixturePreparers(
+		PrepareForTestWithJavaDefaultModules,
+		PrepareForTestWithFakeApexMutator,
+		dexpreopt.FixtureDisableDexpreopt(true),
+	)
+
+	result := preparers.RunTestWithBp(t, `
+		java_library {
+			name: "foo",
+			installable: true,
+			dex_preopt: {
+				profile: "art-profile",
+			},
+			srcs: ["a.java"],
+		}`)
+
+	ctx := result.TestContext
+	dexpreopt := ctx.ModuleForTests("foo", "android_common").MaybeRule("dexpreopt")
+
+	expected := []string{"out/soong/.intermediates/foo/android_common/dexpreopt/profile.prof"}
+
+	android.AssertArrayString(t, "outputs", expected, dexpreopt.AllOutputs())
+}
diff --git a/java/droiddoc.go b/java/droiddoc.go
index dbe021d..d4ead12 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -608,6 +608,11 @@
 		FlagWithArg("-Xmaxerrs ", "10").
 		FlagWithArg("-Xmaxwarns ", "10").
 		Flag("-J--add-exports=jdk.javadoc/jdk.javadoc.internal.doclets.formats.html=ALL-UNNAMED").
+		Flag("-J--add-exports=jdk.javadoc/jdk.javadoc.internal.tool=ALL-UNNAMED").
+		Flag("-J--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED").
+		Flag("-J--add-exports=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED").
+		Flag("-J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED").
+		Flag("-J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED").
 		Flag("-J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED").
 		FlagWithArg("-hdf page.build ", ctx.Config().BuildId()+"-$(cat "+buildNumberFile.String()+")").OrderOnly(buildNumberFile).
 		FlagWithArg("-hdf page.now ", `"$(date -d @$(cat `+ctx.Config().Getenv("BUILD_DATETIME_FILE")+`) "+%d %b %Y %k:%M")" `)
diff --git a/java/droidstubs.go b/java/droidstubs.go
index 8a521aa..151c94a 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -535,6 +535,14 @@
 		// b/223382732
 		FlagWithArg("--hide ", "ChangedDefault")
 
+	// Force metalava to ignore classes on the classpath when an API file contains missing classes.
+	// See b/285140653 for more information.
+	cmd.FlagWithArg("--api-class-resolution ", "api")
+
+	// Force metalava to sort overloaded methods by their order in the source code.
+	// See b/285312164 for more information.
+	cmd.FlagWithArg("--api-overloaded-method-order ", "source")
+
 	return cmd
 }
 
diff --git a/java/fuzz.go b/java/fuzz.go
index 1d6b913..b3c2fd4 100644
--- a/java/fuzz.go
+++ b/java/fuzz.go
@@ -19,26 +19,22 @@
 	"sort"
 	"strings"
 
-	"github.com/google/blueprint"
-	"github.com/google/blueprint/proptools"
-
 	"android/soong/android"
 	"android/soong/cc"
 	"android/soong/fuzz"
+
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
 )
 
 const (
 	hostString   = "host"
 	targetString = "target"
+	deviceString = "device"
 )
 
-type jniProperties struct {
-	// list of jni libs
-	Jni_libs []string
-
-	// sanitization
-	Sanitizers []string
-}
+// Any shared libs for these deps will also be packaged
+var artDeps = []string{"libdl_android"}
 
 func init() {
 	RegisterJavaFuzzBuildComponents(android.InitRegistrationContext)
@@ -46,33 +42,51 @@
 
 func RegisterJavaFuzzBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("java_fuzz", JavaFuzzFactory)
-	ctx.RegisterModuleType("java_fuzz_host", JavaFuzzHostFactory)
-	ctx.RegisterSingletonType("java_fuzz_host_packaging", javaFuzzHostPackagingFactory)
-	ctx.RegisterSingletonType("java_fuzz_device_packaging", javaFuzzDevicePackagingFactory)
+	ctx.RegisterParallelSingletonType("java_fuzz_packaging", javaFuzzPackagingFactory)
 }
 
-type JavaFuzzLibrary struct {
-	Library
+type JavaFuzzTest struct {
+	Test
 	fuzzPackagedModule fuzz.FuzzPackagedModule
-	jniProperties      jniProperties
 	jniFilePaths       android.Paths
 }
 
-// IsSanitizerEnabledForJni implemented to make JavaFuzzLibrary implement
-// cc.JniSanitizeable. It returns a bool for whether a cc dependency should be
-// sanitized for the given sanitizer or not.
-func (j *JavaFuzzLibrary) IsSanitizerEnabledForJni(ctx android.BaseModuleContext, sanitizerName string) bool {
-	// TODO: once b/231370928 is resolved, please uncomment the loop
-	//     for _, s := range j.jniProperties.Sanitizers {
-	//         if sanitizerName == s {
-	//             return true
-	//         }
-	//     }
-	return false
+// java_fuzz builds and links sources into a `.jar` file for the device.
+// This generates .class files in a jar which can then be instrumented before
+// fuzzing in Android Runtime (ART: Android OS on emulator or device)
+func JavaFuzzFactory() android.Module {
+	module := &JavaFuzzTest{}
+
+	module.addHostAndDeviceProperties()
+	module.AddProperties(&module.testProperties)
+	module.AddProperties(&module.fuzzPackagedModule.FuzzProperties)
+
+	module.Module.properties.Installable = proptools.BoolPtr(true)
+	module.Module.dexpreopter.isTest = true
+	module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true)
+
+	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
+		disableLinuxBionic := struct {
+			Target struct {
+				Linux_bionic struct {
+					Enabled *bool
+				}
+			}
+		}{}
+		disableLinuxBionic.Target.Linux_bionic.Enabled = proptools.BoolPtr(false)
+		ctx.AppendProperties(&disableLinuxBionic)
+	})
+
+	InitJavaModuleMultiTargets(module, android.HostAndDeviceSupported)
+	return module
 }
 
-func (j *JavaFuzzLibrary) DepsMutator(mctx android.BottomUpMutatorContext) {
-	if len(j.jniProperties.Jni_libs) > 0 {
+func (j *JavaFuzzTest) DepsMutator(ctx android.BottomUpMutatorContext) {
+	if j.Os().Class.String() == deviceString {
+		j.testProperties.Jni_libs = append(j.testProperties.Jni_libs, artDeps...)
+	}
+
+	if len(j.testProperties.Jni_libs) > 0 {
 		if j.fuzzPackagedModule.FuzzProperties.Fuzz_config == nil {
 			config := &fuzz.FuzzConfig{}
 			j.fuzzPackagedModule.FuzzProperties.Fuzz_config = config
@@ -80,15 +94,16 @@
 		// this will be used by the ingestion pipeline to determine the version
 		// of jazzer to add to the fuzzer package
 		j.fuzzPackagedModule.FuzzProperties.Fuzz_config.IsJni = proptools.BoolPtr(true)
-		for _, target := range mctx.MultiTargets() {
+		for _, target := range ctx.MultiTargets() {
 			sharedLibVariations := append(target.Variations(), blueprint.Variation{Mutator: "link", Variation: "shared"})
-			mctx.AddFarVariationDependencies(sharedLibVariations, cc.JniFuzzLibTag, j.jniProperties.Jni_libs...)
+			ctx.AddFarVariationDependencies(sharedLibVariations, jniLibTag, j.testProperties.Jni_libs...)
 		}
 	}
-	j.Library.DepsMutator(mctx)
+
+	j.deps(ctx)
 }
 
-func (j *JavaFuzzLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+func (j *JavaFuzzTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	if j.fuzzPackagedModule.FuzzProperties.Corpus != nil {
 		j.fuzzPackagedModule.Corpus = android.PathsForModuleSrc(ctx, j.fuzzPackagedModule.FuzzProperties.Corpus)
 	}
@@ -105,21 +120,15 @@
 	}
 
 	_, sharedDeps := cc.CollectAllSharedDependencies(ctx)
-
 	for _, dep := range sharedDeps {
 		sharedLibInfo := ctx.OtherModuleProvider(dep, cc.SharedLibraryInfoProvider).(cc.SharedLibraryInfo)
 		if sharedLibInfo.SharedLibrary != nil {
-			// The .class jars are output in slightly different locations
-			// relative to the jni libs. Therefore, for consistency across
-			// host and device fuzzers of jni lib location, we save it in a
-			// native_libs directory.
-			var relPath string
+			arch := "lib"
 			if sharedLibInfo.Target.Arch.ArchType.Multilib == "lib64" {
-				relPath = filepath.Join("lib64", sharedLibInfo.SharedLibrary.Base())
-			} else {
-				relPath = filepath.Join("lib", sharedLibInfo.SharedLibrary.Base())
+				arch = "lib64"
 			}
-			libPath := android.PathForModuleOut(ctx, relPath)
+
+			libPath := android.PathForModuleOut(ctx, filepath.Join(arch, sharedLibInfo.SharedLibrary.Base()))
 			ctx.Build(pctx, android.BuildParams{
 				Rule:   android.Cp,
 				Input:  sharedLibInfo.SharedLibrary,
@@ -129,104 +138,37 @@
 		} else {
 			ctx.PropertyErrorf("jni_libs", "%q of type %q is not supported", dep.Name(), ctx.OtherModuleType(dep))
 		}
+
 	}
 
-	j.Library.GenerateAndroidBuildActions(ctx)
+	j.Test.GenerateAndroidBuildActions(ctx)
 }
 
-// java_fuzz_host builds and links sources into a `.jar` file for the host.
-//
-// By default, a java_fuzz produces a `.jar` file containing `.class` files.
-// This jar is not suitable for installing on a device.
-func JavaFuzzHostFactory() android.Module {
-	module := &JavaFuzzLibrary{}
-	module.addHostProperties()
-	module.AddProperties(&module.jniProperties)
-	module.Module.properties.Installable = proptools.BoolPtr(true)
-	module.AddProperties(&module.fuzzPackagedModule.FuzzProperties)
-
-	// java_fuzz packaging rules collide when both linux_glibc and linux_bionic are enabled, disable the linux_bionic variants.
-	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
-		disableLinuxBionic := struct {
-			Target struct {
-				Linux_bionic struct {
-					Enabled *bool
-				}
-			}
-		}{}
-		disableLinuxBionic.Target.Linux_bionic.Enabled = proptools.BoolPtr(false)
-		ctx.AppendProperties(&disableLinuxBionic)
-	})
-
-	InitJavaModuleMultiTargets(module, android.HostSupportedNoCross)
-	return module
-}
-
-// java_fuzz builds and links sources into a `.jar` file for the device.
-// This generates .class files in a jar which can then be instrumented before
-// fuzzing in Android Runtime (ART: Android OS on emulator or device)
-func JavaFuzzFactory() android.Module {
-	module := &JavaFuzzLibrary{}
-	module.addHostAndDeviceProperties()
-	module.AddProperties(&module.jniProperties)
-	module.Module.properties.Installable = proptools.BoolPtr(true)
-	module.AddProperties(&module.fuzzPackagedModule.FuzzProperties)
-	module.Module.dexpreopter.isTest = true
-	module.Module.linter.properties.Lint.Test = proptools.BoolPtr(true)
-	InitJavaModuleMultiTargets(module, android.DeviceSupported)
-	return module
-}
-
-// Responsible for generating rules that package host fuzz targets into
-// a zip file.
-type javaFuzzHostPackager struct {
+type javaFuzzPackager struct {
 	fuzz.FuzzPackager
 }
 
-// Responsible for generating rules that package device fuzz targets into
-// a zip file.
-type javaFuzzDevicePackager struct {
-	fuzz.FuzzPackager
+func javaFuzzPackagingFactory() android.Singleton {
+	return &javaFuzzPackager{}
 }
 
-func javaFuzzHostPackagingFactory() android.Singleton {
-	return &javaFuzzHostPackager{}
-}
-
-func javaFuzzDevicePackagingFactory() android.Singleton {
-	return &javaFuzzDevicePackager{}
-}
-
-func (s *javaFuzzHostPackager) GenerateBuildActions(ctx android.SingletonContext) {
-	generateBuildActions(&s.FuzzPackager, hostString, ctx)
-}
-
-func (s *javaFuzzDevicePackager) GenerateBuildActions(ctx android.SingletonContext) {
-	generateBuildActions(&s.FuzzPackager, targetString, ctx)
-}
-
-func generateBuildActions(s *fuzz.FuzzPackager, hostOrTargetString string, ctx android.SingletonContext) {
+func (s *javaFuzzPackager) GenerateBuildActions(ctx android.SingletonContext) {
 	// Map between each architecture + host/device combination.
 	archDirs := make(map[fuzz.ArchOs][]fuzz.FileToZip)
 
-	// List of individual fuzz targets.
 	s.FuzzTargets = make(map[string]bool)
-
 	ctx.VisitAllModules(func(module android.Module) {
 		// Discard non-fuzz targets.
-		javaFuzzModule, ok := module.(*JavaFuzzLibrary)
+		javaFuzzModule, ok := module.(*JavaFuzzTest)
 		if !ok {
 			return
 		}
 
-		if hostOrTargetString == hostString {
-			if !javaFuzzModule.Host() {
-				return
-			}
-		} else if hostOrTargetString == targetString {
-			if javaFuzzModule.Host() || javaFuzzModule.Target().HostCross {
-				return
-			}
+		hostOrTargetString := "target"
+		if javaFuzzModule.Target().HostCross {
+			hostOrTargetString = "host_cross"
+		} else if javaFuzzModule.Host() {
+			hostOrTargetString = "host"
 		}
 
 		fuzzModuleValidator := fuzz.FuzzModule{
@@ -246,42 +188,33 @@
 		var files []fuzz.FileToZip
 		builder := android.NewRuleBuilder(pctx, ctx)
 
-		// Package the artifacts (data, corpus, config and dictionary into a zipfile.
+		// Package the artifacts (data, corpus, config and dictionary) into a zipfile.
 		files = s.PackageArtifacts(ctx, module, javaFuzzModule.fuzzPackagedModule, archDir, builder)
 
 		// Add .jar
-		files = append(files, fuzz.FileToZip{javaFuzzModule.implementationJarFile, ""})
+		if !javaFuzzModule.Host() {
+			files = append(files, fuzz.FileToZip{SourceFilePath: javaFuzzModule.implementationJarFile, DestinationPathPrefix: "classes"})
+		}
+
+		files = append(files, fuzz.FileToZip{SourceFilePath: javaFuzzModule.outputFile})
 
 		// Add jni .so files
 		for _, fPath := range javaFuzzModule.jniFilePaths {
-			files = append(files, fuzz.FileToZip{fPath, ""})
+			files = append(files, fuzz.FileToZip{SourceFilePath: fPath})
 		}
 
 		archDirs[archOs], ok = s.BuildZipFile(ctx, module, javaFuzzModule.fuzzPackagedModule, files, builder, archDir, archString, hostOrTargetString, archOs, archDirs)
 		if !ok {
 			return
 		}
-
 	})
 	s.CreateFuzzPackage(ctx, archDirs, fuzz.Java, pctx)
 }
 
-func (s *javaFuzzHostPackager) MakeVars(ctx android.MakeVarsContext) {
+func (s *javaFuzzPackager) MakeVars(ctx android.MakeVarsContext) {
 	packages := s.Packages.Strings()
 	sort.Strings(packages)
-
-	ctx.Strict("SOONG_JAVA_FUZZ_HOST_PACKAGING_ARCH_MODULES", strings.Join(packages, " "))
-
+	ctx.Strict("SOONG_JAVA_FUZZ_PACKAGING_ARCH_MODULES", strings.Join(packages, " "))
 	// Preallocate the slice of fuzz targets to minimize memory allocations.
-	s.PreallocateSlice(ctx, "ALL_JAVA_FUZZ_HOST_TARGETS")
-}
-
-func (s *javaFuzzDevicePackager) MakeVars(ctx android.MakeVarsContext) {
-	packages := s.Packages.Strings()
-	sort.Strings(packages)
-
-	ctx.Strict("SOONG_JAVA_FUZZ_DEVICE_PACKAGING_ARCH_MODULES", strings.Join(packages, " "))
-
-	// Preallocate the slice of fuzz targets to minimize memory allocations.
-	s.PreallocateSlice(ctx, "ALL_JAVA_FUZZ_DEVICE_TARGETS")
+	s.PreallocateSlice(ctx, "ALL_JAVA_FUZZ_TARGETS")
 }
diff --git a/java/fuzz_test.go b/java/fuzz_test.go
index 186c3aa..dd1e96b 100644
--- a/java/fuzz_test.go
+++ b/java/fuzz_test.go
@@ -31,18 +31,16 @@
 
 func TestJavaFuzz(t *testing.T) {
 	result := prepForJavaFuzzTest.RunTestWithBp(t, `
-		java_fuzz_host {
+		java_fuzz {
 			name: "foo",
 			srcs: ["a.java"],
+			host_supported: true,
+			device_supported: false,
 			libs: ["bar"],
 			static_libs: ["baz"],
             jni_libs: [
                 "libjni",
             ],
-            sanitizers: [
-                "address",
-                "fuzzer",
-            ],
 		}
 
 		java_library_host {
@@ -84,7 +82,7 @@
 	}
 
 	ctx := result.TestContext
-	foo := ctx.ModuleForTests("foo", osCommonTarget).Module().(*JavaFuzzLibrary)
+	foo := ctx.ModuleForTests("foo", osCommonTarget).Module().(*JavaFuzzTest)
 
 	expected := "lib64/libjni.so"
 	if runtime.GOOS == "darwin" {
diff --git a/java/generated_java_library.go b/java/generated_java_library.go
new file mode 100644
index 0000000..1b3de9f
--- /dev/null
+++ b/java/generated_java_library.go
@@ -0,0 +1,94 @@
+// Copyright 2023 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 (
+	"android/soong/android"
+)
+
+type GeneratedJavaLibraryModule struct {
+	Library
+	callbacks  GeneratedJavaLibraryCallbacks
+	moduleName string
+}
+
+type GeneratedJavaLibraryCallbacks interface {
+	// Called from inside DepsMutator, gives a chance to AddDependencies
+	DepsMutator(module *GeneratedJavaLibraryModule, ctx android.BottomUpMutatorContext)
+
+	// Called from inside GenerateAndroidBuildActions. Add the build rules to
+	// make the srcjar, and return the path to it.
+	GenerateSourceJarBuildActions(ctx android.ModuleContext) android.Path
+}
+
+// GeneratedJavaLibraryModuleFactory provides a utility for modules that are generated
+// source code, including ones outside the java package to build jar files
+// from that generated source.
+//
+// To use GeneratedJavaLibraryModule, call GeneratedJavaLibraryModuleFactory with
+// a callback interface and a properties object to add to the module.
+//
+// These modules will have some properties blocked, and it will be an error if
+// modules attempt to set them. See the list of property names in GeneratedAndroidBuildActions
+// for the list of those properties.
+func GeneratedJavaLibraryModuleFactory(moduleName string, callbacks GeneratedJavaLibraryCallbacks, properties interface{}) android.Module {
+	module := &GeneratedJavaLibraryModule{
+		callbacks:  callbacks,
+		moduleName: moduleName,
+	}
+	module.addHostAndDeviceProperties()
+	module.initModuleAndImport(module)
+	android.InitApexModule(module)
+	android.InitBazelModule(module)
+	InitJavaModule(module, android.HostAndDeviceSupported)
+	if properties != nil {
+		module.AddProperties(properties)
+	}
+	return module
+}
+
+func (module *GeneratedJavaLibraryModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+	module.callbacks.DepsMutator(module, ctx)
+	module.Library.DepsMutator(ctx)
+}
+
+func checkPropertyEmpty(ctx android.ModuleContext, module *GeneratedJavaLibraryModule, name string, value []string) {
+	if len(value) != 0 {
+		ctx.PropertyErrorf(name, "%s not allowed on %s", name, module.moduleName)
+	}
+}
+
+func (module *GeneratedJavaLibraryModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// These modules are all-generated, so disallow these properties to keep it simple.
+	// No additional sources
+	checkPropertyEmpty(ctx, module, "srcs", module.Library.properties.Srcs)
+	checkPropertyEmpty(ctx, module, "common_srcs", module.Library.properties.Common_srcs)
+	checkPropertyEmpty(ctx, module, "exclude_srcs", module.Library.properties.Exclude_srcs)
+	checkPropertyEmpty(ctx, module, "java_resource_dirs", module.Library.properties.Java_resource_dirs)
+	checkPropertyEmpty(ctx, module, "exclude_java_resource_dirs", module.Library.properties.Exclude_java_resource_dirs)
+	// No additional libraries. The generator should add anything necessary automatically
+	// by returning something from ____ (TODO: Additional libraries aren't needed now, so
+	// these are just blocked).
+	checkPropertyEmpty(ctx, module, "libs", module.Library.properties.Libs)
+	checkPropertyEmpty(ctx, module, "static_libs", module.Library.properties.Static_libs)
+	// Restrict these for no good reason other than to limit the surface area. If there's a
+	// good use case put them back.
+	checkPropertyEmpty(ctx, module, "plugins", module.Library.properties.Plugins)
+	checkPropertyEmpty(ctx, module, "exported_plugins", module.Library.properties.Exported_plugins)
+
+	srcJarPath := module.callbacks.GenerateSourceJarBuildActions(ctx)
+	module.Library.properties.Generated_srcjars = append(module.Library.properties.Generated_srcjars, srcJarPath)
+	module.Library.GenerateAndroidBuildActions(ctx)
+}
diff --git a/java/generated_java_library_test.go b/java/generated_java_library_test.go
new file mode 100644
index 0000000..68f1f7e
--- /dev/null
+++ b/java/generated_java_library_test.go
@@ -0,0 +1,65 @@
+// Copyright 2018 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+	"testing"
+
+	"android/soong/android"
+)
+
+func JavaGenLibTestFactory() android.Module {
+	callbacks := &JavaGenLibTestCallbacks{}
+	return GeneratedJavaLibraryModuleFactory("test_java_gen_lib", callbacks, &callbacks.properties)
+}
+
+type JavaGenLibTestProperties struct {
+	Foo string
+}
+
+type JavaGenLibTestCallbacks struct {
+	properties JavaGenLibTestProperties
+}
+
+func (callbacks *JavaGenLibTestCallbacks) DepsMutator(module *GeneratedJavaLibraryModule, ctx android.BottomUpMutatorContext) {
+}
+
+func (callbacks *JavaGenLibTestCallbacks) GenerateSourceJarBuildActions(ctx android.ModuleContext) android.Path {
+	return android.PathForOutput(ctx, "blah.srcjar")
+}
+
+func testGenLib(t *testing.T, errorHandler android.FixtureErrorHandler, bp string) *android.TestResult {
+	return android.GroupFixturePreparers(
+		PrepareForIntegrationTestWithJava,
+		android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+			ctx.RegisterModuleType("test_java_gen_lib", JavaGenLibTestFactory)
+		}),
+	).
+		ExtendWithErrorHandler(errorHandler).
+		RunTestWithBp(t, bp)
+}
+
+func TestGenLib(t *testing.T) {
+	bp := `
+				test_java_gen_lib {
+					name: "javagenlibtest",
+                    foo: "bar",  // Note: This won't parse if the property didn't get added
+				}
+			`
+	result := testGenLib(t, android.FixtureExpectsNoErrors, bp)
+
+	javagenlibtest := result.ModuleForTests("javagenlibtest", "android_common").Module().(*GeneratedJavaLibraryModule)
+	android.AssertPathsEndWith(t, "Generated_srcjars", []string{"/blah.srcjar"}, javagenlibtest.Library.properties.Generated_srcjars)
+}
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index c6176e6..da9c997 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -236,9 +236,9 @@
 		testStubModules = append(testStubModules, "sdk_test_current_android")
 	} else {
 		// Use stub modules built from source
-		publicStubModules = append(publicStubModules, android.SdkPublic.JavaLibraryName(config))
-		systemStubModules = append(systemStubModules, android.SdkSystem.JavaLibraryName(config))
-		testStubModules = append(testStubModules, android.SdkTest.JavaLibraryName(config))
+		publicStubModules = append(publicStubModules, android.SdkPublic.DefaultJavaLibraryName())
+		systemStubModules = append(systemStubModules, android.SdkSystem.DefaultJavaLibraryName())
+		testStubModules = append(testStubModules, android.SdkTest.DefaultJavaLibraryName())
 	}
 	// We do not have prebuilts of the core platform api yet
 	corePlatformStubModules = append(corePlatformStubModules, "legacy.core.platform.api.stubs")
@@ -647,7 +647,7 @@
 	// public version is provided by the art.module.public.api module. In those cases it is necessary
 	// to treat all those modules as they were the same name, otherwise it will result in multiple
 	// definitions of a single class being passed to hidden API processing which will cause an error.
-	if name == scope.nonUpdatablePrebuiltModule || name == scope.nonUpdatableSourceModule {
+	if name == scope.nonUpdatablePrebuiltModule || name == android.JavaApiLibraryName(ctx.Config(), scope.nonUpdatableSourceModule) {
 		// Treat all *android-non-updatable* modules as if they were part of an android-non-updatable
 		// java_sdk_library.
 		// TODO(b/192067200): Remove once android-non-updatable is a java_sdk_library or equivalent.
diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
index 52934a3..d4ee4fc 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -25,7 +25,7 @@
 }
 
 func RegisterHiddenApiSingletonComponents(ctx android.RegistrationContext) {
-	ctx.RegisterSingletonType("hiddenapi", hiddenAPISingletonFactory)
+	ctx.RegisterParallelSingletonType("hiddenapi", hiddenAPISingletonFactory)
 }
 
 var PrepareForTestWithHiddenApiBuildComponents = android.FixtureRegisterWithContext(RegisterHiddenApiSingletonComponents)
diff --git a/java/hiddenapi_singleton_test.go b/java/hiddenapi_singleton_test.go
index 75b7bb7..ef792f9 100644
--- a/java/hiddenapi_singleton_test.go
+++ b/java/hiddenapi_singleton_test.go
@@ -39,7 +39,9 @@
 `)
 
 var hiddenApiFixtureFactory = android.GroupFixturePreparers(
-	prepareForJavaTest, PrepareForTestWithHiddenApiBuildComponents)
+	PrepareForTestWithJavaDefaultModules,
+	PrepareForTestWithHiddenApiBuildComponents,
+)
 
 func TestHiddenAPISingleton(t *testing.T) {
 	result := android.GroupFixturePreparers(
diff --git a/java/java.go b/java/java.go
index 2de4ea9..a026610 100644
--- a/java/java.go
+++ b/java/java.go
@@ -73,8 +73,8 @@
 		ctx.BottomUp("jacoco_deps", jacocoDepsMutator).Parallel()
 	})
 
-	ctx.RegisterSingletonType("logtags", LogtagsSingleton)
-	ctx.RegisterSingletonType("kythe_java_extract", kytheExtractJavaFactory)
+	ctx.RegisterParallelSingletonType("logtags", LogtagsSingleton)
+	ctx.RegisterParallelSingletonType("kythe_java_extract", kytheExtractJavaFactory)
 }
 
 func RegisterJavaSdkMemberTypes() {
@@ -273,6 +273,8 @@
 	// JacocoReportClassesFile is the path to a jar containing uninstrumented classes that will be
 	// instrumented by jacoco.
 	JacocoReportClassesFile android.Path
+
+	// TODO: Add device config declarations here?
 }
 
 var JavaInfoProvider = blueprint.NewProvider(JavaInfo{})
@@ -456,7 +458,9 @@
 		ctx.AddVariationDependencies(nil, java9LibTag, sdkDep.java9Classpath...)
 		ctx.AddVariationDependencies(nil, sdkLibTag, sdkDep.classpath...)
 		if d.effectiveOptimizeEnabled() && sdkDep.hasStandardLibs() {
-			ctx.AddVariationDependencies(nil, proguardRaiseTag, config.LegacyCorePlatformBootclasspathLibraries...)
+			ctx.AddVariationDependencies(nil, proguardRaiseTag,
+				android.JavaApiLibraryNames(ctx.Config(), config.LegacyCorePlatformBootclasspathLibraries)...,
+			)
 		}
 		if d.effectiveOptimizeEnabled() && sdkDep.hasFrameworkLibs() {
 			ctx.AddVariationDependencies(nil, proguardRaiseTag, config.FrameworkLibraries...)
@@ -813,7 +817,10 @@
 	// If the min_sdk_version was set then add the canonical representation of the API level to the
 	// snapshot.
 	if j.deviceProperties.Min_sdk_version != nil {
-		canonical := android.ReplaceFinalizedCodenames(ctx.SdkModuleContext().Config(), j.minSdkVersion.String())
+		canonical, err := android.ReplaceFinalizedCodenames(ctx.SdkModuleContext().Config(), j.minSdkVersion.String())
+		if err != nil {
+			ctx.ModuleErrorf("%s", err)
+		}
 		p.MinSdkVersion = proptools.StringPtr(canonical)
 	}
 
@@ -931,6 +938,10 @@
 	// Extra <option> tags to add to the auto generated test xml file. The "key"
 	// is optional in each of these.
 	Tradefed_options []tradefed.Option
+
+	// Extra <option> tags to add to the auto generated test xml file under the test runner, e.g., AndroidJunitTest.
+	// The "key" is optional in each of these.
+	Test_runner_options []tradefed.Option
 }
 
 type testProperties struct {
@@ -1151,7 +1162,6 @@
 	}
 
 	j.addDataDeviceBinsDeps(ctx)
-
 	j.deps(ctx)
 }
 
@@ -1214,6 +1224,7 @@
 		TestSuites:              j.testProperties.Test_suites,
 		Config:                  configs,
 		OptionsForAutogenerated: j.testProperties.Test_options.Tradefed_options,
+		TestRunnerOptions:       j.testProperties.Test_options.Test_runner_options,
 		AutoGenConfig:           j.testProperties.Auto_gen_config,
 		UnitTest:                j.testProperties.Test_options.Unit_test,
 		DeviceTemplate:          "${JavaTestConfigTemplate}",
@@ -1412,6 +1423,8 @@
 		nil,
 		nil)
 
+	android.InitBazelModule(module)
+
 	InitJavaModuleMultiTargets(module, android.HostSupported)
 
 	return module
@@ -1646,7 +1659,7 @@
 	// list of api.txt files relative to this directory that contribute to the
 	// API surface.
 	// This is a list of relative paths
-	Api_files []string
+	Api_files []string `android:"path"`
 
 	// List of flags to be passed to the javac compiler to generate jar file
 	Javacflags []string
@@ -1724,6 +1737,14 @@
 		FlagWithArg("--hide ", "InvalidNullabilityOverride").
 		FlagWithArg("--hide ", "ChangedDefault")
 
+	// Force metalava to ignore classes on the classpath when an API file contains missing classes.
+	// See b/285140653 for more information.
+	cmd.FlagWithArg("--api-class-resolution ", "api")
+
+	// Force metalava to sort overloaded methods by their order in the source code.
+	// See b/285312164 for more information.
+	cmd.FlagWithArg("--api-overloaded-method-order ", "source")
+
 	return cmd
 }
 
@@ -1809,7 +1830,7 @@
 		case javaApiContributionTag:
 			provider := ctx.OtherModuleProvider(dep, JavaApiImportProvider).(JavaApiImportInfo)
 			providerApiFile := provider.ApiFile
-			if providerApiFile == nil {
+			if providerApiFile == nil && !ctx.Config().AllowMissingDependencies() {
 				ctx.ModuleErrorf("Error: %s has an empty api file.", dep.Name())
 			}
 			srcFiles = append(srcFiles, android.PathForSource(ctx, providerApiFile.String()))
@@ -1828,12 +1849,10 @@
 
 	// Add the api_files inputs
 	for _, api := range al.properties.Api_files {
-		// Use MaybeExistentPathForSource since the api file might not exist during analysis.
-		// This will be provided by the orchestrator in the combined execution.
-		srcFiles = append(srcFiles, android.MaybeExistentPathForSource(ctx, ctx.ModuleDir(), api))
+		srcFiles = append(srcFiles, android.PathForModuleSrc(ctx, api))
 	}
 
-	if srcFiles == nil {
+	if srcFiles == nil && !ctx.Config().AllowMissingDependencies() {
 		ctx.ModuleErrorf("Error: %s has an empty api file.", ctx.ModuleName())
 	}
 
@@ -2604,6 +2623,7 @@
 		&appProperties{},
 		&appTestProperties{},
 		&overridableAppProperties{},
+		&hostTestProperties{},
 		&testProperties{},
 		&ImportProperties{},
 		&AARImportProperties{},
@@ -2701,11 +2721,29 @@
 	Resource_strip_prefix *string
 }
 
+func (m *Library) javaResourcesGetSingleFilegroupStripPrefix(ctx android.TopDownMutatorContext) (string, bool) {
+	if otherM, ok := ctx.ModuleFromName(m.properties.Java_resources[0]); ok && len(m.properties.Java_resources) == 1 {
+		if fg, isFilegroup := otherM.(android.FileGroupPath); isFilegroup {
+			return filepath.Join(ctx.OtherModuleDir(otherM), fg.GetPath(ctx)), true
+		}
+	}
+	return "", false
+}
+
 func (m *Library) convertJavaResourcesAttributes(ctx android.TopDownMutatorContext) *javaResourcesAttributes {
 	var resources bazel.LabelList
 	var resourceStripPrefix *string
 
+	if m.properties.Java_resources != nil && len(m.properties.Java_resource_dirs) > 0 {
+		ctx.ModuleErrorf("bp2build doesn't support both java_resources and java_resource_dirs being set on the same module.")
+	}
+
 	if m.properties.Java_resources != nil {
+		if prefix, ok := m.javaResourcesGetSingleFilegroupStripPrefix(ctx); ok {
+			resourceStripPrefix = proptools.StringPtr(prefix)
+		} else {
+			resourceStripPrefix = proptools.StringPtr(ctx.ModuleDir())
+		}
 		resources.Append(android.BazelLabelForModuleSrc(ctx, m.properties.Java_resources))
 	}
 
@@ -2777,6 +2815,14 @@
 	hasKotlin bool
 }
 
+// Replaces //a/b/my_xsd_config with //a/b/my_xsd_config-java
+func xsdConfigJavaTarget(ctx android.BazelConversionPathContext, mod blueprint.Module) string {
+	callback := func(xsd android.XsdConfigBp2buildTargets) string {
+		return xsd.JavaBp2buildTargetName()
+	}
+	return android.XsdConfigBp2buildTarget(ctx, mod, callback)
+}
+
 // convertLibraryAttrsBp2Build returns a javaCommonAttributes struct with
 // converted attributes shared across java_* modules and a bp2BuildJavaInfo struct
 // which has other non-attribute information needed for bp2build conversion
@@ -2791,8 +2837,15 @@
 	for axis, configToProps := range archVariantProps {
 		for config, _props := range configToProps {
 			if archProps, ok := _props.(*CommonProperties); ok {
-				archSrcs := android.BazelLabelForModuleSrcExcludes(ctx, archProps.Srcs, archProps.Exclude_srcs)
+				srcsNonXsd, srcsXsd := android.PartitionXsdSrcs(ctx, archProps.Srcs)
+				excludeSrcsNonXsd, _ := android.PartitionXsdSrcs(ctx, archProps.Exclude_srcs)
+				archSrcs := android.BazelLabelForModuleSrcExcludes(ctx, srcsNonXsd, excludeSrcsNonXsd)
 				srcs.SetSelectValue(axis, config, archSrcs)
+
+				// Add to static deps
+				xsdJavaConfigLibraryLabels := android.BazelLabelForModuleDepsWithFn(ctx, srcsXsd, xsdConfigJavaTarget)
+				staticDeps.Append(xsdJavaConfigLibraryLabels)
+
 			}
 		}
 	}
@@ -2837,7 +2890,7 @@
 			return android.IsConvertedToAidlLibrary(ctx, src.OriginalModuleName)
 		})
 
-		apexAvailableTags := android.ApexAvailableTags(ctx.Module())
+		apexAvailableTags := android.ApexAvailableTagsWithoutTestApexes(ctx, ctx.Module())
 
 		if !aidlSrcs.IsEmpty() {
 			aidlLibName := m.Name() + "_aidl_library"
@@ -2963,14 +3016,14 @@
 func ktJvmLibraryBazelTargetModuleProperties() bazel.BazelTargetModuleProperties {
 	return bazel.BazelTargetModuleProperties{
 		Rule_class:        "kt_jvm_library",
-		Bzl_load_location: "//build/bazel/rules/kotlin:rules.bzl",
+		Bzl_load_location: "//build/bazel/rules/kotlin:kt_jvm_library.bzl",
 	}
 }
 
 func javaLibraryBazelTargetModuleProperties() bazel.BazelTargetModuleProperties {
 	return bazel.BazelTargetModuleProperties{
 		Rule_class:        "java_library",
-		Bzl_load_location: "//build/bazel/rules/java:rules.bzl",
+		Bzl_load_location: "//build/bazel/rules/java:library.bzl",
 	}
 }
 
@@ -3053,24 +3106,15 @@
 	// Attribute jvm_flags
 	var jvmFlags bazel.StringListAttribute
 	if m.binaryProperties.Jni_libs != nil {
-		jniLibPackages := map[string]bool{}
-		for _, jniLibLabel := range android.BazelLabelForModuleDeps(ctx, m.binaryProperties.Jni_libs).Includes {
-			jniLibPackage := jniLibLabel.Label
-			indexOfColon := strings.Index(jniLibLabel.Label, ":")
-			if indexOfColon > 0 {
-				// JNI lib from other package
-				jniLibPackage = jniLibLabel.Label[2:indexOfColon]
-			} else if indexOfColon == 0 {
-				// JNI lib in the same package of java_binary
-				packageOfCurrentModule := m.GetBazelLabel(ctx, m)
-				jniLibPackage = packageOfCurrentModule[2:strings.Index(packageOfCurrentModule, ":")]
-			}
-			if _, inMap := jniLibPackages[jniLibPackage]; !inMap {
-				jniLibPackages[jniLibPackage] = true
+		jniLibPackages := []string{}
+		for _, jniLib := range m.binaryProperties.Jni_libs {
+			if jniLibModule, exists := ctx.ModuleFromName(jniLib); exists {
+				otherDir := ctx.OtherModuleDir(jniLibModule)
+				jniLibPackages = append(jniLibPackages, filepath.Join(otherDir, jniLib))
 			}
 		}
 		jniLibPaths := []string{}
-		for jniLibPackage, _ := range jniLibPackages {
+		for _, jniLibPackage := range jniLibPackages {
 			// See cs/f:.*/third_party/bazel/.*java_stub_template.txt for the use of RUNPATH
 			jniLibPaths = append(jniLibPaths, "$${RUNPATH}"+jniLibPackage)
 		}
@@ -3079,7 +3123,7 @@
 
 	props := bazel.BazelTargetModuleProperties{
 		Rule_class:        "java_binary",
-		Bzl_load_location: "//build/bazel/rules/java:rules.bzl",
+		Bzl_load_location: "@rules_java//java:defs.bzl",
 	}
 	binAttrs := &javaBinaryHostAttributes{
 		Runtime_deps: runtimeDeps,
@@ -3093,23 +3137,89 @@
 		return
 	}
 
-	libName := m.Name() + "_lib"
+	libInfo := libraryCreationInfo{
+		deps:      deps,
+		attrs:     commonAttrs,
+		baseName:  m.Name(),
+		hasKotlin: bp2BuildInfo.hasKotlin,
+	}
+	libName := createLibraryTarget(ctx, libInfo)
+	binAttrs.Runtime_deps.Add(&bazel.LabelAttribute{Value: &bazel.Label{Label: ":" + libName}})
+
+	// Create the BazelTargetModule.
+	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, binAttrs)
+}
+
+type javaTestHostAttributes struct {
+	*javaCommonAttributes
+	Deps         bazel.LabelListAttribute
+	Runtime_deps bazel.LabelListAttribute
+}
+
+// javaTestHostBp2Build is for java_test_host bp2build.
+func javaTestHostBp2Build(ctx android.TopDownMutatorContext, m *TestHost) {
+	commonAttrs, bp2BuildInfo := m.convertLibraryAttrsBp2Build(ctx)
+	depLabels := bp2BuildInfo.DepLabels
+
+	deps := depLabels.Deps
+	deps.Append(depLabels.StaticDeps)
+
+	var runtimeDeps bazel.LabelListAttribute
+	attrs := &javaTestHostAttributes{
+		Runtime_deps: runtimeDeps,
+	}
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class:        "java_test",
+		Bzl_load_location: "//build/bazel/rules/java:test.bzl",
+	}
+
+	if commonAttrs.Srcs.IsEmpty() {
+		// if there are no sources, then the dependencies can only be used at runtime
+		attrs.Runtime_deps = deps
+		attrs.javaCommonAttributes = commonAttrs
+		ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs)
+		return
+	}
+
+	libInfo := libraryCreationInfo{
+		deps:      deps,
+		attrs:     commonAttrs,
+		baseName:  m.Name(),
+		hasKotlin: bp2BuildInfo.hasKotlin,
+	}
+	libName := createLibraryTarget(ctx, libInfo)
+	attrs.Runtime_deps.Add(&bazel.LabelAttribute{Value: &bazel.Label{Label: ":" + libName}})
+
+	// Create the BazelTargetModule.
+	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs)
+}
+
+// libraryCreationInfo encapsulates the info needed to create java_library target from
+// java_binary_host or java_test_host.
+type libraryCreationInfo struct {
+	deps      bazel.LabelListAttribute
+	attrs     *javaCommonAttributes
+	baseName  string
+	hasKotlin bool
+}
+
+// helper function that creates java_library target from java_binary_host or java_test_host,
+// and returns the library target name,
+func createLibraryTarget(ctx android.TopDownMutatorContext, libInfo libraryCreationInfo) string {
+	libName := libInfo.baseName + "_lib"
 	var libProps bazel.BazelTargetModuleProperties
-	if bp2BuildInfo.hasKotlin {
+	if libInfo.hasKotlin {
 		libProps = ktJvmLibraryBazelTargetModuleProperties()
 	} else {
 		libProps = javaLibraryBazelTargetModuleProperties()
 	}
 	libAttrs := &javaLibraryAttributes{
-		Deps:                 deps,
-		javaCommonAttributes: commonAttrs,
+		Deps:                 libInfo.deps,
+		javaCommonAttributes: libInfo.attrs,
 	}
 
 	ctx.CreateBazelTargetModule(libProps, android.CommonAttributes{Name: libName}, libAttrs)
-	binAttrs.Runtime_deps.Add(&bazel.LabelAttribute{Value: &bazel.Label{Label: ":" + libName}})
-
-	// Create the BazelTargetModule.
-	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, binAttrs)
+	return libName
 }
 
 type bazelJavaImportAttributes struct {
@@ -3135,7 +3245,7 @@
 	}
 	props := bazel.BazelTargetModuleProperties{
 		Rule_class:        "java_import",
-		Bzl_load_location: "//build/bazel/rules/java:rules.bzl",
+		Bzl_load_location: "//build/bazel/rules/java:import.bzl",
 	}
 
 	name := android.RemoveOptionalPrebuiltPrefix(i.Name())
diff --git a/java/java_test.go b/java/java_test.go
index 553b762..561b187 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -46,12 +46,11 @@
 	// Get the CC build components but not default modules.
 	cc.PrepareForTestWithCcBuildComponents,
 	// Include all the default java modules.
-	PrepareForTestWithJavaDefaultModules,
+	PrepareForTestWithDexpreopt,
 	PrepareForTestWithOverlayBuildComponents,
 	android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
 		ctx.RegisterPreSingletonType("sdk_versions", sdkPreSingletonFactory)
 	}),
-	PrepareForTestWithDexpreopt,
 )
 
 func TestMain(m *testing.M) {
@@ -2253,6 +2252,29 @@
 	android.AssertStringDoesContain(t, "Command expected to contain output files list text file flag", manifestCommand, "--out __SBOX_SANDBOX_DIR__/out/sources.txt")
 }
 
+func TestJavaApiLibraryFilegroupInput(t *testing.T) {
+	ctx, _ := testJavaWithFS(t, `
+	    filegroup {
+			name: "default_current.txt",
+			srcs: ["current.txt"],
+		}
+
+		java_api_library {
+			name: "foo",
+			api_files: [":default_current.txt"],
+		}
+		`,
+		map[string][]byte{
+			"current.txt": nil,
+		})
+
+	m := ctx.ModuleForTests("foo", "android_common")
+	outputs := fmt.Sprint(m.AllOutputs())
+	if !strings.Contains(outputs, "foo/foo.jar") {
+		t.Errorf("Module output does not contain expected jar %s", "foo/foo.jar")
+	}
+}
+
 func TestTradefedOptions(t *testing.T) {
 	result := PrepareForTestWithJavaBuildComponents.RunTestWithBp(t, `
 java_test_host {
@@ -2276,3 +2298,58 @@
 		t.Errorf("Expected args[\"extraConfigs\"] to equal %q, was %q", expected, args["extraConfigs"])
 	}
 }
+
+func TestTestRunnerOptions(t *testing.T) {
+	result := PrepareForTestWithJavaBuildComponents.RunTestWithBp(t, `
+java_test_host {
+	name: "foo",
+	test_options: {
+		test_runner_options: [
+			{
+				name: "test-timeout",
+				value: "10m"
+			}
+		]
+	}
+}
+`)
+
+	buildOS := result.Config.BuildOS.String()
+	args := result.ModuleForTests("foo", buildOS+"_common").
+		Output("out/soong/.intermediates/foo/" + buildOS + "_common/foo.config").Args
+	expected := proptools.NinjaAndShellEscape("<option name=\"test-timeout\" value=\"10m\" />\\n        ")
+	if args["extraTestRunnerConfigs"] != expected {
+		t.Errorf("Expected args[\"extraTestRunnerConfigs\"] to equal %q, was %q", expected, args["extraTestRunnerConfigs"])
+	}
+}
+
+func TestJavaExcludeStaticLib(t *testing.T) {
+	ctx, _ := testJava(t, `
+	java_library {
+		name: "bar",
+	}
+	java_library {
+		name: "foo",
+	}
+	java_library {
+		name: "baz",
+		static_libs: [
+			"foo",
+			"bar",
+		],
+		exclude_static_libs: [
+			"bar",
+		],
+	}
+	`)
+
+	// "bar" not included as dependency of "baz"
+	CheckModuleDependencies(t, ctx, "baz", "android_common", []string{
+		`core-lambda-stubs`,
+		`ext`,
+		`foo`,
+		`framework`,
+		`stable-core-platform-api-stubs-system-modules`,
+		`stable.core.platform.api.stubs`,
+	})
+}
diff --git a/java/jdeps.go b/java/jdeps.go
index a52b867..4c8c11c 100644
--- a/java/jdeps.go
+++ b/java/jdeps.go
@@ -26,7 +26,7 @@
 // called. Dependency info file is generated in $OUT/module_bp_java_depend.json.
 
 func init() {
-	android.RegisterSingletonType("jdeps_generator", jDepsGeneratorSingleton)
+	android.RegisterParallelSingletonType("jdeps_generator", jDepsGeneratorSingleton)
 }
 
 func jDepsGeneratorSingleton() android.Singleton {
diff --git a/java/legacy_core_platform_api_usage.go b/java/legacy_core_platform_api_usage.go
index 6cb549e..04c6d05 100644
--- a/java/legacy_core_platform_api_usage.go
+++ b/java/legacy_core_platform_api_usage.go
@@ -93,16 +93,16 @@
 
 func corePlatformSystemModules(ctx android.EarlyModuleContext) string {
 	if useLegacyCorePlatformApi(ctx, ctx.ModuleName()) {
-		return config.LegacyCorePlatformSystemModules
+		return android.JavaApiLibraryName(ctx.Config(), config.LegacyCorePlatformSystemModules)
 	} else {
-		return config.StableCorePlatformSystemModules
+		return android.JavaApiLibraryName(ctx.Config(), config.StableCorePlatformSystemModules)
 	}
 }
 
 func corePlatformBootclasspathLibraries(ctx android.EarlyModuleContext) []string {
 	if useLegacyCorePlatformApi(ctx, ctx.ModuleName()) {
-		return config.LegacyCorePlatformBootclasspathLibraries
+		return android.JavaApiLibraryNames(ctx.Config(), config.LegacyCorePlatformBootclasspathLibraries)
 	} else {
-		return config.StableCorePlatformBootclasspathLibraries
+		return android.JavaApiLibraryNames(ctx.Config(), config.StableCorePlatformBootclasspathLibraries)
 	}
 }
diff --git a/java/lint.go b/java/lint.go
index 40ef484..a0f9970 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -705,7 +705,7 @@
 var _ android.SingletonMakeVarsProvider = (*lintSingleton)(nil)
 
 func init() {
-	android.RegisterSingletonType("lint",
+	android.RegisterParallelSingletonType("lint",
 		func() android.Singleton { return &lintSingleton{} })
 
 	registerLintBuildComponents(android.InitRegistrationContext)
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
index 0ea3609..0d4db7c 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -26,7 +26,7 @@
 }
 
 func registerPlatformBootclasspathBuildComponents(ctx android.RegistrationContext) {
-	ctx.RegisterSingletonModuleType("platform_bootclasspath", platformBootclasspathFactory)
+	ctx.RegisterParallelSingletonModuleType("platform_bootclasspath", platformBootclasspathFactory)
 }
 
 // The tags used for the dependencies between the platform bootclasspath and any configured boot
@@ -103,7 +103,7 @@
 func (b *platformBootclasspathModule) DepsMutator(ctx android.BottomUpMutatorContext) {
 	b.hiddenAPIDepsMutator(ctx)
 
-	if SkipDexpreoptBootJars(ctx) {
+	if !dexpreopt.IsDex2oatNeeded(ctx) {
 		return
 	}
 
@@ -129,8 +129,7 @@
 
 	// Add dependencies on all the non-updatable module configured in the "boot" boot image. That does
 	// not include modules configured in the "art" boot image.
-	bootImageConfig := defaultBootImageConfig(ctx)
-	addDependenciesOntoBootImageModules(ctx, bootImageConfig.modules, platformBootclasspathBootJarDepTag)
+	addDependenciesOntoBootImageModules(ctx, b.platformJars(ctx), platformBootclasspathBootJarDepTag)
 
 	// Add dependencies on all the apex jars.
 	apexJars := dexpreopt.GetGlobalConfig(ctx).ApexBootJars
@@ -187,12 +186,8 @@
 	bootDexJarByModule := b.generateHiddenAPIBuildActions(ctx, b.configuredModules, b.fragments)
 	buildRuleForBootJarsPackageCheck(ctx, bootDexJarByModule)
 
-	// Nothing to do if skipping the dexpreopt of boot image jars.
-	if SkipDexpreoptBootJars(ctx) {
-		return
-	}
-
-	b.generateBootImageBuildActions(ctx, platformModules, apexModules)
+	b.generateBootImageBuildActions(ctx)
+	b.copyApexBootJarsForAppsDexpreopt(ctx, apexModules)
 }
 
 // Generate classpaths.proto config
@@ -205,7 +200,7 @@
 
 func (b *platformBootclasspathModule) configuredJars(ctx android.ModuleContext) android.ConfiguredJarList {
 	// Include all non APEX jars
-	jars := defaultBootImageConfig(ctx).modules
+	jars := b.platformJars(ctx)
 
 	// Include jars from APEXes that don't populate their classpath proto config.
 	remainingJars := dexpreopt.GetGlobalConfig(ctx).ApexBootJars
@@ -222,6 +217,10 @@
 	return jars
 }
 
+func (b *platformBootclasspathModule) platformJars(ctx android.PathContext) android.ConfiguredJarList {
+	return defaultBootImageConfig(ctx).modules.RemoveList(artBootImageConfig(ctx).modules)
+}
+
 // checkPlatformModules ensures that the non-updatable modules supplied are not part of an
 // apex module.
 func (b *platformBootclasspathModule) checkPlatformModules(ctx android.ModuleContext, modules []android.Module) {
@@ -231,7 +230,7 @@
 		fromUpdatableApex := apexInfo.Updatable
 		if fromUpdatableApex {
 			// error: this jar is part of an updatable apex
-			ctx.ModuleErrorf("module %q from updatable apexes %q is not allowed in the framework boot image", ctx.OtherModuleName(m), apexInfo.InApexVariants)
+			ctx.ModuleErrorf("module %q from updatable apexes %q is not allowed in the platform bootclasspath", ctx.OtherModuleName(m), apexInfo.InApexVariants)
 		} else {
 			// ok: this jar is part of the platform or a non-updatable apex
 		}
@@ -401,7 +400,7 @@
 }
 
 // generateBootImageBuildActions generates ninja rules related to the boot image creation.
-func (b *platformBootclasspathModule) generateBootImageBuildActions(ctx android.ModuleContext, platformModules, apexModules []android.Module) {
+func (b *platformBootclasspathModule) generateBootImageBuildActions(ctx android.ModuleContext) {
 	// Force the GlobalSoongConfig to be created and cached for use by the dex_bootjars
 	// GenerateSingletonBuildActions method as it cannot create it for itself.
 	dexpreopt.GetGlobalSoongConfig(ctx)
@@ -413,15 +412,16 @@
 
 	frameworkBootImageConfig := defaultBootImageConfig(ctx)
 	bootFrameworkProfileRule(ctx, frameworkBootImageConfig)
-	b.generateBootImage(ctx, frameworkBootImageName, platformModules)
-	b.generateBootImage(ctx, mainlineBootImageName, apexModules)
-	b.copyApexBootJarsForAppsDexpreopt(ctx, apexModules)
+	b.generateBootImage(ctx, frameworkBootImageName)
+	b.generateBootImage(ctx, mainlineBootImageName)
 	dumpOatRules(ctx, frameworkBootImageConfig)
 }
 
-func (b *platformBootclasspathModule) generateBootImage(ctx android.ModuleContext, imageName string, modules []android.Module) {
+func (b *platformBootclasspathModule) generateBootImage(ctx android.ModuleContext, imageName string) {
 	imageConfig := genBootImageConfigs(ctx)[imageName]
 
+	modules := b.getModulesForImage(ctx, imageConfig)
+
 	// Copy module dex jars to their predefined locations.
 	bootDexJarsByModule := extractEncodedDexJarsFromModules(ctx, modules)
 	copyBootJarsToPredefinedLocations(ctx, bootDexJarsByModule, imageConfig.dexPathsByModule)
@@ -429,6 +429,12 @@
 	// Build a profile for the image config and then use that to build the boot image.
 	profile := bootImageProfileRule(ctx, imageConfig)
 
+	// If dexpreopt of boot image jars should be skipped, generate only a profile.
+	global := dexpreopt.GetGlobalConfig(ctx)
+	if global.DisablePreoptBootImages {
+		return
+	}
+
 	// Build boot image files for the android variants.
 	androidBootImageFiles := buildBootImageVariantsForAndroidOs(ctx, imageConfig, profile)
 
@@ -445,3 +451,26 @@
 	apexBootDexJarsByModule := extractEncodedDexJarsFromModules(ctx, apexModules)
 	copyBootJarsToPredefinedLocations(ctx, apexBootDexJarsByModule, config.dexPathsByModule)
 }
+
+func (b *platformBootclasspathModule) getModulesForImage(ctx android.ModuleContext, imageConfig *bootImageConfig) []android.Module {
+	modules := make([]android.Module, 0, imageConfig.modules.Len())
+	for i := 0; i < imageConfig.modules.Len(); i++ {
+		found := false
+		for _, module := range b.configuredModules {
+			name := android.RemoveOptionalPrebuiltPrefix(module.Name())
+			if name == imageConfig.modules.Jar(i) {
+				modules = append(modules, module)
+				found = true
+				break
+			}
+		}
+		if !found && !ctx.Config().AllowMissingDependencies() {
+			ctx.ModuleErrorf(
+				"Boot image '%s' module '%s' not added as a dependency of platform_bootclasspath",
+				imageConfig.name,
+				imageConfig.modules.Jar(i))
+			return []android.Module{}
+		}
+	}
+	return modules
+}
diff --git a/java/platform_bootclasspath_test.go b/java/platform_bootclasspath_test.go
index 10c9187..ff2da4b 100644
--- a/java/platform_bootclasspath_test.go
+++ b/java/platform_bootclasspath_test.go
@@ -18,14 +18,12 @@
 	"testing"
 
 	"android/soong/android"
-	"android/soong/dexpreopt"
 )
 
 // Contains some simple tests for platform_bootclasspath.
 
 var prepareForTestWithPlatformBootclasspath = android.GroupFixturePreparers(
 	PrepareForTestWithJavaDefaultModules,
-	dexpreopt.PrepareForTestByEnablingDexpreopt,
 )
 
 func TestPlatformBootclasspath(t *testing.T) {
diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go
index d417291..1248275 100644
--- a/java/platform_compat_config.go
+++ b/java/platform_compat_config.go
@@ -19,7 +19,10 @@
 	"path/filepath"
 
 	"android/soong/android"
+	"android/soong/bazel"
+
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
 )
 
 func init() {
@@ -36,7 +39,7 @@
 }
 
 func registerPlatformCompatConfigBuildComponents(ctx android.RegistrationContext) {
-	ctx.RegisterSingletonType("platform_compat_config_singleton", platformCompatConfigSingletonFactory)
+	ctx.RegisterParallelSingletonType("platform_compat_config_singleton", platformCompatConfigSingletonFactory)
 	ctx.RegisterModuleType("platform_compat_config", PlatformCompatConfigFactory)
 	ctx.RegisterModuleType("prebuilt_platform_compat_config", prebuiltCompatConfigFactory)
 	ctx.RegisterModuleType("global_compat_config", globalCompatConfigFactory)
@@ -54,6 +57,7 @@
 
 type platformCompatConfig struct {
 	android.ModuleBase
+	android.BazelModuleBase
 
 	properties     platformCompatConfigProperties
 	installDirPath android.InstallPath
@@ -122,10 +126,29 @@
 	}}
 }
 
+type bazelPlatformCompatConfigAttributes struct {
+	Src bazel.LabelAttribute
+}
+
+func (p *platformCompatConfig) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class:        "platform_compat_config",
+		Bzl_load_location: "//build/bazel/rules/java:platform_compat_config.bzl",
+	}
+	attr := &bazelPlatformCompatConfigAttributes{
+		Src: *bazel.MakeLabelAttribute(
+			android.BazelLabelForModuleSrcSingle(ctx, proptools.String(p.properties.Src)).Label),
+	}
+	ctx.CreateBazelTargetModule(props, android.CommonAttributes{
+		Name: p.Name(),
+	}, attr)
+}
+
 func PlatformCompatConfigFactory() android.Module {
 	module := &platformCompatConfig{}
 	module.AddProperties(&module.properties)
 	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	android.InitBazelModule(module)
 	return module
 }
 
diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go
index 206d995..044802e 100644
--- a/java/prebuilt_apis.go
+++ b/java/prebuilt_apis.go
@@ -104,20 +104,51 @@
 	return fmt.Sprintf("%s_%s_%s_%s", mctx.ModuleName(), scope, version, module)
 }
 
+func hasBazelPrebuilt(module string) bool {
+	return module == "android" || module == "core-for-system-modules"
+}
+
+func bazelPrebuiltApiModuleName(module, scope, version string) string {
+	bazelModule := module
+	switch module {
+	case "android":
+		bazelModule = "android_jar"
+	case "core-for-system-modules":
+		bazelModule = "core_jar"
+	}
+	bazelVersion := version
+	if version == "current" {
+		bazelVersion = strconv.Itoa(android.FutureApiLevelInt)
+	}
+	bazelScope := scope
+	switch scope {
+	case "module-lib":
+		bazelScope = "module"
+	case "system-server":
+		bazelScope = "system_server"
+	}
+	return fmt.Sprintf("//prebuilts/sdk:%s_%s_%s", bazelScope, bazelVersion, bazelModule)
+}
+
 func createImport(mctx android.LoadHookContext, module, scope, version, path, sdkVersion string, compileDex bool) {
 	props := struct {
-		Name        *string
-		Jars        []string
-		Sdk_version *string
-		Installable *bool
-		Compile_dex *bool
-	}{}
-	props.Name = proptools.StringPtr(prebuiltApiModuleName(mctx, module, scope, version))
-	props.Jars = append(props.Jars, path)
-	props.Sdk_version = proptools.StringPtr(sdkVersion)
-	props.Installable = proptools.BoolPtr(false)
-	props.Compile_dex = proptools.BoolPtr(compileDex)
-
+		Name         *string
+		Jars         []string
+		Sdk_version  *string
+		Installable  *bool
+		Compile_dex  *bool
+		Bazel_module android.BazelModuleProperties
+	}{
+		Name:        proptools.StringPtr(prebuiltApiModuleName(mctx, module, scope, version)),
+		Jars:        []string{path},
+		Sdk_version: proptools.StringPtr(sdkVersion),
+		Installable: proptools.BoolPtr(false),
+		Compile_dex: proptools.BoolPtr(compileDex),
+	}
+	if hasBazelPrebuilt(module) {
+		props.Bazel_module = android.BazelModuleProperties{
+			Label: proptools.StringPtr(bazelPrebuiltApiModuleName(module, scope, version))}
+	}
 	mctx.CreateModule(ImportFactory, &props)
 }
 
@@ -135,6 +166,19 @@
 	mctx.CreateModule(genrule.GenRuleFactory, &genruleProps)
 }
 
+func createLatestApiModuleExtensionVersionFile(mctx android.LoadHookContext, name string, version string) {
+	genruleProps := struct {
+		Name *string
+		Srcs []string
+		Out  []string
+		Cmd  *string
+	}{}
+	genruleProps.Name = proptools.StringPtr(name)
+	genruleProps.Out = []string{name}
+	genruleProps.Cmd = proptools.StringPtr("echo " + version + " > $(out)")
+	mctx.CreateModule(genrule.GenRuleFactory, &genruleProps)
+}
+
 func createEmptyFile(mctx android.LoadHookContext, name string) {
 	props := struct {
 		Name *string
@@ -233,9 +277,10 @@
 	type latestApiInfo struct {
 		module, scope, path string
 		version             int
+		isExtensionApiFile  bool
 	}
 
-	getLatest := func(files []string) map[string]latestApiInfo {
+	getLatest := func(files []string, isExtensionApiFile bool) map[string]latestApiInfo {
 		m := make(map[string]latestApiInfo)
 		for _, f := range files {
 			module, version, scope := parseFinalizedPrebuiltPath(mctx, f)
@@ -245,16 +290,16 @@
 			key := module + "." + scope
 			info, exists := m[key]
 			if !exists || version > info.version {
-				m[key] = latestApiInfo{module, scope, f, version}
+				m[key] = latestApiInfo{module, scope, f, version, isExtensionApiFile}
 			}
 		}
 		return m
 	}
 
-	latest := getLatest(apiLevelFiles)
+	latest := getLatest(apiLevelFiles, false)
 	if p.properties.Extensions_dir != nil {
 		extensionApiFiles := globExtensionDirs(mctx, p, "api/*.txt")
-		for k, v := range getLatest(extensionApiFiles) {
+		for k, v := range getLatest(extensionApiFiles, true) {
 			if _, exists := latest[k]; !exists {
 				mctx.ModuleErrorf("Module %v finalized for extension %d but never during an API level; likely error", v.module, v.version)
 			}
@@ -267,6 +312,12 @@
 	for _, k := range android.SortedKeys(latest) {
 		info := latest[k]
 		name := PrebuiltApiModuleName(info.module, info.scope, "latest")
+		latestExtensionVersionModuleName := PrebuiltApiModuleName(info.module, info.scope, "latest.extension_version")
+		if info.isExtensionApiFile {
+			createLatestApiModuleExtensionVersionFile(mctx, latestExtensionVersionModuleName, strconv.Itoa(info.version))
+		} else {
+			createLatestApiModuleExtensionVersionFile(mctx, latestExtensionVersionModuleName, "-1")
+		}
 		createApiModule(mctx, name, info.path)
 	}
 
diff --git a/java/robolectric.go b/java/robolectric.go
index 008b8b1..6bbe872 100644
--- a/java/robolectric.go
+++ b/java/robolectric.go
@@ -144,29 +144,37 @@
 	roboTestConfig := android.PathForModuleGen(ctx, "robolectric").
 		Join(ctx, "com/android/tools/test_config.properties")
 
+	var ok bool
+	var instrumentedApp *AndroidApp
+
 	// TODO: this inserts paths to built files into the test, it should really be inserting the contents.
 	instrumented := ctx.GetDirectDepsWithTag(instrumentationForTag)
 
-	if len(instrumented) != 1 {
+	if len(instrumented) == 1 {
+		instrumentedApp, ok = instrumented[0].(*AndroidApp)
+		if !ok {
+			ctx.PropertyErrorf("instrumentation_for", "dependency must be an android_app")
+		}
+	} else if !ctx.Config().AllowMissingDependencies() {
 		panic(fmt.Errorf("expected exactly 1 instrumented dependency, got %d", len(instrumented)))
 	}
 
-	instrumentedApp, ok := instrumented[0].(*AndroidApp)
-	if !ok {
-		ctx.PropertyErrorf("instrumentation_for", "dependency must be an android_app")
+	if instrumentedApp != nil {
+		r.manifest = instrumentedApp.mergedManifestFile
+		r.resourceApk = instrumentedApp.outputFile
+
+		generateRoboTestConfig(ctx, roboTestConfig, instrumentedApp)
+		r.extraResources = android.Paths{roboTestConfig}
 	}
 
-	r.manifest = instrumentedApp.mergedManifestFile
-	r.resourceApk = instrumentedApp.outputFile
-
-	generateRoboTestConfig(ctx, roboTestConfig, instrumentedApp)
-	r.extraResources = android.Paths{roboTestConfig}
-
 	r.Library.GenerateAndroidBuildActions(ctx)
 
 	roboSrcJar := android.PathForModuleGen(ctx, "robolectric", ctx.ModuleName()+".srcjar")
-	r.generateRoboSrcJar(ctx, roboSrcJar, instrumentedApp)
-	r.roboSrcJar = roboSrcJar
+
+	if instrumentedApp != nil {
+		r.generateRoboSrcJar(ctx, roboSrcJar, instrumentedApp)
+		r.roboSrcJar = roboSrcJar
+	}
 
 	roboTestConfigJar := android.PathForModuleOut(ctx, "robolectric_samedir", "samedir_config.jar")
 	generateSameDirRoboTestConfigJar(ctx, roboTestConfigJar)
@@ -177,7 +185,10 @@
 		// once the Make test runner is removed.
 		roboTestConfigJar,
 		r.outputFile,
-		instrumentedApp.implementationAndResourcesJar,
+	}
+
+	if instrumentedApp != nil {
+		combinedJarJars = append(combinedJarJars, instrumentedApp.implementationAndResourcesJar)
 	}
 
 	handleLibDeps := func(dep android.Module) {
@@ -213,21 +224,28 @@
 		r.tests = append(r.tests, s)
 	}
 
-	r.data = append(r.data, r.manifest, r.resourceApk)
+	installPath := android.PathForModuleInstall(ctx, r.BaseModuleName())
+	var installDeps android.Paths
+
+	if r.manifest != nil {
+		r.data = append(r.data, r.manifest)
+		installedManifest := ctx.InstallFile(installPath, ctx.ModuleName()+"-AndroidManifest.xml", r.manifest)
+		installDeps = append(installDeps, installedManifest)
+	}
+
+	if r.resourceApk != nil {
+		r.data = append(r.data, r.resourceApk)
+		installedResourceApk := ctx.InstallFile(installPath, ctx.ModuleName()+".apk", r.resourceApk)
+		installDeps = append(installDeps, installedResourceApk)
+	}
 
 	runtimes := ctx.GetDirectDepWithTag("robolectric-android-all-prebuilts", roboRuntimesTag)
-
-	installPath := android.PathForModuleInstall(ctx, r.BaseModuleName())
-
-	installedResourceApk := ctx.InstallFile(installPath, ctx.ModuleName()+".apk", r.resourceApk)
-	installedManifest := ctx.InstallFile(installPath, ctx.ModuleName()+"-AndroidManifest.xml", r.manifest)
-	installedConfig := ctx.InstallFile(installPath, ctx.ModuleName()+".config", r.testConfig)
-
-	var installDeps android.Paths
 	for _, runtime := range runtimes.(*robolectricRuntimes).runtimes {
 		installDeps = append(installDeps, runtime)
 	}
-	installDeps = append(installDeps, installedResourceApk, installedManifest, installedConfig)
+
+	installedConfig := ctx.InstallFile(installPath, ctx.ModuleName()+".config", r.testConfig)
+	installDeps = append(installDeps, installedConfig)
 
 	for _, data := range android.PathsForModuleSrc(ctx, r.testProperties.Data) {
 		installedData := ctx.InstallFile(installPath, data.Rel(), data)
@@ -340,7 +358,9 @@
 	fmt.Fprintln(w, "LOCAL_MODULE :=", name)
 	android.AndroidMkEmitAssignList(w, "LOCAL_JAVA_LIBRARIES", []string{module}, r.libs)
 	fmt.Fprintln(w, "LOCAL_TEST_PACKAGE :=", String(r.robolectricProperties.Instrumentation_for))
-	fmt.Fprintln(w, "LOCAL_INSTRUMENT_SRCJARS :=", r.roboSrcJar.String())
+	if r.roboSrcJar != nil {
+		fmt.Fprintln(w, "LOCAL_INSTRUMENT_SRCJARS :=", r.roboSrcJar.String())
+	}
 	android.AndroidMkEmitAssignList(w, "LOCAL_ROBOTEST_FILES", tests)
 	if t := r.robolectricProperties.Test_options.Timeout; t != nil {
 		fmt.Fprintln(w, "LOCAL_ROBOTEST_TIMEOUT :=", *t)
diff --git a/java/sdk.go b/java/sdk.go
index 8b4918a..7699aab 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -28,7 +28,7 @@
 
 func init() {
 	android.RegisterPreSingletonType("sdk_versions", sdkPreSingletonFactory)
-	android.RegisterSingletonType("sdk", sdkSingletonFactory)
+	android.RegisterParallelSingletonType("sdk", sdkSingletonFactory)
 	android.RegisterMakeVarsProvider(pctx, sdkMakeVars)
 }
 
@@ -151,7 +151,7 @@
 		systemModules := android.JavaApiLibraryName(ctx.Config(), fmt.Sprintf("core-%s-stubs-system-modules", systemModulesKind))
 		return sdkDep{
 			useModule:          true,
-			bootclasspath:      []string{module, android.JavaApiLibraryName(ctx.Config(), config.DefaultLambdaStubsLibrary)},
+			bootclasspath:      []string{module, config.DefaultLambdaStubsLibrary},
 			systemModules:      systemModules,
 			java9Classpath:     []string{module},
 			frameworkResModule: "framework-res",
@@ -193,20 +193,20 @@
 			noFrameworksLibs: true,
 		}
 	case android.SdkPublic, android.SdkSystem, android.SdkTest:
-		return toModule(sdkVersion.Kind.JavaLibraryName(ctx.Config()), sdkFrameworkAidlPath(ctx))
+		return toModule(sdkVersion.Kind.DefaultJavaLibraryName(), sdkFrameworkAidlPath(ctx))
 	case android.SdkCore:
 		return sdkDep{
 			useModule:        true,
-			bootclasspath:    []string{android.SdkCore.JavaLibraryName(ctx.Config()), android.JavaApiLibraryName(ctx.Config(), config.DefaultLambdaStubsLibrary)},
+			bootclasspath:    []string{android.SdkCore.DefaultJavaLibraryName(), config.DefaultLambdaStubsLibrary},
 			systemModules:    android.JavaApiLibraryName(ctx.Config(), "core-public-stubs-system-modules"),
 			noFrameworksLibs: true,
 		}
 	case android.SdkModule:
 		// TODO(146757305): provide .apk and .aidl that have more APIs for modules
-		return toModule(sdkVersion.Kind.JavaLibraryName(ctx.Config()), nonUpdatableFrameworkAidlPath(ctx))
+		return toModule(sdkVersion.Kind.DefaultJavaLibraryName(), nonUpdatableFrameworkAidlPath(ctx))
 	case android.SdkSystemServer:
 		// TODO(146757305): provide .apk and .aidl that have more APIs for modules
-		return toModule(sdkVersion.Kind.JavaLibraryName(ctx.Config()), sdkFrameworkAidlPath(ctx))
+		return toModule(sdkVersion.Kind.DefaultJavaLibraryName(), sdkFrameworkAidlPath(ctx))
 	default:
 		panic(fmt.Errorf("invalid sdk %q", sdkVersion.Raw))
 	}
@@ -269,9 +269,9 @@
 // Create framework.aidl by extracting anything that implements android.os.Parcelable from the SDK stubs modules.
 func createSdkFrameworkAidl(ctx android.SingletonContext) {
 	stubsModules := []string{
-		android.SdkPublic.JavaLibraryName(ctx.Config()),
-		android.SdkTest.JavaLibraryName(ctx.Config()),
-		android.SdkSystem.JavaLibraryName(ctx.Config()),
+		android.SdkPublic.DefaultJavaLibraryName(),
+		android.SdkTest.DefaultJavaLibraryName(),
+		android.SdkSystem.DefaultJavaLibraryName(),
 	}
 
 	combinedAidl := sdkFrameworkAidlPath(ctx)
@@ -286,7 +286,7 @@
 
 // Creates a version of framework.aidl for the non-updatable part of the platform.
 func createNonUpdatableFrameworkAidl(ctx android.SingletonContext) {
-	stubsModules := []string{android.SdkModule.JavaLibraryName(ctx.Config())}
+	stubsModules := []string{android.SdkModule.DefaultJavaLibraryName()}
 
 	combinedAidl := nonUpdatableFrameworkAidlPath(ctx)
 	tempPath := tempPathForRestat(ctx, combinedAidl)
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 103f1ac..89da19a 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -156,6 +156,9 @@
 
 	// Whether the api scope can be treated as unstable, and should skip compat checks.
 	unstable bool
+
+	// Represents the SDK kind of this scope.
+	kind android.SdkKind
 }
 
 // Initialize a scope, creating and adding appropriate dependency tags
@@ -229,6 +232,10 @@
 	return ".stubs" + scope.moduleSuffix
 }
 
+func (scope *apiScope) apiLibraryModuleName(baseName string) string {
+	return scope.stubsLibraryModuleName(baseName) + ".from-text"
+}
+
 func (scope *apiScope) stubsLibraryModuleName(baseName string) string {
 	return baseName + scope.stubsLibraryModuleNameSuffix()
 }
@@ -289,6 +296,7 @@
 			return &module.sdkLibraryProperties.Public
 		},
 		sdkVersion: "current",
+		kind:       android.SdkPublic,
 	})
 	apiScopeSystem = initApiScope(&apiScope{
 		name:                "system",
@@ -301,6 +309,7 @@
 		moduleSuffix:  ".system",
 		sdkVersion:    "system_current",
 		annotation:    "android.annotation.SystemApi(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS)",
+		kind:          android.SdkSystem,
 	})
 	apiScopeTest = initApiScope(&apiScope{
 		name:                "test",
@@ -314,6 +323,7 @@
 		sdkVersion:    "test_current",
 		annotation:    "android.annotation.TestApi",
 		unstable:      true,
+		kind:          android.SdkTest,
 	})
 	apiScopeModuleLib = initApiScope(&apiScope{
 		name:    "module-lib",
@@ -331,6 +341,7 @@
 		moduleSuffix:  ".module_lib",
 		sdkVersion:    "module_current",
 		annotation:    "android.annotation.SystemApi(client=android.annotation.SystemApi.Client.MODULE_LIBRARIES)",
+		kind:          android.SdkModule,
 	})
 	apiScopeSystemServer = initApiScope(&apiScope{
 		name:    "system-server",
@@ -361,6 +372,7 @@
 			// com.android.* classes are okay in this interface"
 			"--hide", "InternalClasses",
 		},
+		kind: android.SdkSystemServer,
 	})
 	allApiScopes = apiScopes{
 		apiScopePublic,
@@ -842,6 +854,13 @@
 	return c.namingScheme.stubsSourceModuleName(apiScope, baseName)
 }
 
+// Name of the java_api_library module that generates the from-text stubs source
+// and compiles to a jar file.
+func (c *commonToSdkLibraryAndImport) apiLibraryModuleName(apiScope *apiScope) string {
+	baseName := c.module.BaseModuleName()
+	return c.namingScheme.apiLibraryModuleName(apiScope, baseName)
+}
+
 // The component names for different outputs of the java_sdk_library.
 //
 // They are similar to the names used for the child modules it creates
@@ -1269,7 +1288,9 @@
 		// Add dependencies to the stubs library
 		stubModuleName := module.stubsLibraryModuleName(apiScope)
 		// Use JavaApiLibraryName function to be redirected to stubs generated from .txt if applicable
-		stubModuleName = android.JavaApiLibraryName(ctx.Config(), stubModuleName)
+		if module.contributesToApiSurface(ctx.Config()) {
+			stubModuleName = android.JavaApiLibraryName(ctx.Config(), stubModuleName)
+		}
 		ctx.AddVariationDependencies(nil, apiScope.stubsTag, stubModuleName)
 
 		// Add a dependency on the stubs source in order to access both stubs source and api information.
@@ -1477,6 +1498,11 @@
 	return latestPrebuiltApiModuleName(module.distStem()+"-incompatibilities", apiScope)
 }
 
+func (module *SdkLibrary) contributesToApiSurface(c android.Config) bool {
+	_, exists := c.GetApiLibraries()[module.Name()]
+	return exists
+}
+
 func childModuleVisibility(childVisibility []string) []string {
 	if childVisibility == nil {
 		// No child visibility set. The child will use the visibility of the sdk_library.
@@ -1758,6 +1784,46 @@
 	mctx.CreateModule(DroidstubsFactory, &props).(*Droidstubs).CallHookIfAvailable(mctx)
 }
 
+func (module *SdkLibrary) createApiLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) {
+	props := struct {
+		Name              *string
+		Visibility        []string
+		Api_contributions []string
+		Libs              []string
+		Static_libs       []string
+		Dep_api_srcs      *string
+	}{}
+
+	props.Name = proptools.StringPtr(module.apiLibraryModuleName(apiScope))
+	props.Visibility = childModuleVisibility(module.sdkLibraryProperties.Stubs_library_visibility)
+
+	apiContributions := []string{}
+
+	// Api surfaces are not independent of each other, but have subset relationships,
+	// and so does the api files. To generate from-text stubs for api surfaces other than public,
+	// all subset api domains' api_contriubtions must be added as well.
+	scope := apiScope
+	for scope != nil {
+		apiContributions = append(apiContributions, module.stubsSourceModuleName(scope)+".api.contribution")
+		scope = scope.extends
+	}
+
+	props.Api_contributions = apiContributions
+	props.Libs = module.properties.Libs
+	props.Libs = append(props.Libs, module.sdkLibraryProperties.Stub_only_libs...)
+	props.Libs = append(props.Libs, "stub-annotations")
+	props.Static_libs = module.sdkLibraryProperties.Stub_only_static_libs
+	props.Dep_api_srcs = proptools.StringPtr(apiScope.kind.DefaultJavaLibraryName() + ".from-text")
+
+	// android_module_lib_stubs_current.from-text only comprises api contributions from art, conscrypt and i18n.
+	// Thus, replace with android_module_lib_stubs_current_full.from-text, which comprises every api domains.
+	if apiScope.kind == android.SdkModule {
+		props.Dep_api_srcs = proptools.StringPtr(apiScope.kind.DefaultJavaLibraryName() + "_full.from-text")
+	}
+
+	mctx.CreateModule(ApiLibraryFactory, &props)
+}
+
 func (module *SdkLibrary) compareAgainstLatestApi(apiScope *apiScope) bool {
 	return !(apiScope.unstable || module.sdkLibraryProperties.Unsafe_ignore_missing_latest_api)
 }
@@ -1954,6 +2020,10 @@
 		module.createStubsSourcesAndApi(mctx, scope, module.stubsSourceModuleName(scope), scope.droidstubsArgs)
 
 		module.createStubsLibrary(mctx, scope)
+
+		if module.contributesToApiSurface(mctx.Config()) {
+			module.createApiLibrary(mctx, scope)
+		}
 	}
 
 	if module.requiresRuntimeImplementationLibrary() {
@@ -2006,6 +2076,8 @@
 	stubsLibraryModuleName(scope *apiScope, baseName string) string
 
 	stubsSourceModuleName(scope *apiScope, baseName string) string
+
+	apiLibraryModuleName(scope *apiScope, baseName string) string
 }
 
 type defaultNamingScheme struct {
@@ -2019,6 +2091,10 @@
 	return scope.stubsSourceModuleName(baseName)
 }
 
+func (s *defaultNamingScheme) apiLibraryModuleName(scope *apiScope, baseName string) string {
+	return scope.apiLibraryModuleName(baseName)
+}
+
 var _ sdkLibraryComponentNamingScheme = (*defaultNamingScheme)(nil)
 
 func moduleStubLinkType(name string) (stub bool, ret sdkLinkType) {
diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go
index 1d0c13d..141e16b 100644
--- a/java/sdk_library_test.go
+++ b/java/sdk_library_test.go
@@ -35,6 +35,9 @@
 			"29": {"foo"},
 			"30": {"bar", "barney", "baz", "betty", "foo", "fred", "quuz", "wilma"},
 		}),
+		android.FixtureModifyConfig(func(config android.Config) {
+			config.SetApiLibraries([]string{"foo"})
+		}),
 	).RunTestWithBp(t, `
 		droiddoc_exported_dir {
 			name: "droiddoc-templates-sdk",
@@ -121,6 +124,7 @@
 	result.ModuleForTests(apiScopeSystem.stubsSourceModuleName("foo"), "android_common")
 	result.ModuleForTests(apiScopeTest.stubsSourceModuleName("foo"), "android_common")
 	result.ModuleForTests(apiScopePublic.stubsSourceModuleName("foo")+".api.contribution", "")
+	result.ModuleForTests(apiScopePublic.apiLibraryModuleName("foo"), "android_common")
 	result.ModuleForTests("foo"+sdkXmlFileSuffix, "android_common")
 	result.ModuleForTests("foo.api.public.28", "")
 	result.ModuleForTests("foo.api.system.28", "")
@@ -1412,3 +1416,62 @@
 	fooStubsSources := result.ModuleForTests("foo.stubs.source", "android_common").Module().(*Droidstubs)
 	android.AssertStringListContains(t, "foo stubs should depend on bar-lib", fooStubsSources.Javadoc.properties.Libs, "bar-lib")
 }
+
+func TestJavaSdkLibrary_ApiLibrary(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForJavaTest,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithLastReleaseApis("foo"),
+		android.FixtureModifyConfig(func(config android.Config) {
+			config.SetApiLibraries([]string{"foo"})
+		}),
+	).RunTestWithBp(t, `
+		java_sdk_library {
+			name: "foo",
+			srcs: ["a.java", "b.java"],
+			api_packages: ["foo"],
+			system: {
+				enabled: true,
+			},
+			module_lib: {
+				enabled: true,
+			},
+			test: {
+				enabled: true,
+			},
+		}
+	`)
+
+	testCases := []struct {
+		scope            *apiScope
+		apiContributions []string
+		depApiSrcs       string
+	}{
+		{
+			scope:            apiScopePublic,
+			apiContributions: []string{"foo.stubs.source.api.contribution"},
+			depApiSrcs:       "android_stubs_current.from-text",
+		},
+		{
+			scope:            apiScopeSystem,
+			apiContributions: []string{"foo.stubs.source.system.api.contribution", "foo.stubs.source.api.contribution"},
+			depApiSrcs:       "android_system_stubs_current.from-text",
+		},
+		{
+			scope:            apiScopeTest,
+			apiContributions: []string{"foo.stubs.source.test.api.contribution", "foo.stubs.source.system.api.contribution", "foo.stubs.source.api.contribution"},
+			depApiSrcs:       "android_test_stubs_current.from-text",
+		},
+		{
+			scope:            apiScopeModuleLib,
+			apiContributions: []string{"foo.stubs.source.module_lib.api.contribution", "foo.stubs.source.system.api.contribution", "foo.stubs.source.api.contribution"},
+			depApiSrcs:       "android_module_lib_stubs_current_full.from-text",
+		},
+	}
+
+	for _, c := range testCases {
+		m := result.ModuleForTests(c.scope.apiLibraryModuleName("foo"), "android_common").Module().(*ApiLibrary)
+		android.AssertArrayString(t, "Module expected to contain api contributions", c.apiContributions, m.properties.Api_contributions)
+		android.AssertStringEquals(t, "Module expected to contain full api surface api library", c.depApiSrcs, *m.properties.Dep_api_srcs)
+	}
+}
diff --git a/java/testing.go b/java/testing.go
index 0764d26..3a238d7 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -71,7 +71,12 @@
 		// Needed for framework
 		defaultJavaDir + "/framework/aidl": nil,
 		// Needed for various deps defined in GatherRequiredDepsForTest()
-		defaultJavaDir + "/a.java": nil,
+		defaultJavaDir + "/a.java":                        nil,
+		defaultJavaDir + "/api/current.txt":               nil,
+		defaultJavaDir + "/api/system-current.txt":        nil,
+		defaultJavaDir + "/api/test-current.txt":          nil,
+		defaultJavaDir + "/api/module-lib-current.txt":    nil,
+		defaultJavaDir + "/api/system-server-current.txt": nil,
 
 		// Needed for R8 rules on apps
 		"build/make/core/proguard.flags":             nil,
@@ -80,9 +85,7 @@
 	}.AddToFixture(),
 )
 
-// Test fixture preparer that will define all default java modules except the
-// fake_tool_binary for dex2oatd.
-var PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd = android.GroupFixturePreparers(
+var prepareForTestWithJavaDefaultModulesBase = android.GroupFixturePreparers(
 	// Make sure that all the module types used in the defaults are registered.
 	PrepareForTestWithJavaBuildComponents,
 	prepareForTestWithFrameworkDeps,
@@ -92,13 +95,21 @@
 
 // Test fixture preparer that will define default java modules, e.g. standard prebuilt modules.
 var PrepareForTestWithJavaDefaultModules = android.GroupFixturePreparers(
-	PrepareForTestWithJavaDefaultModulesWithoutFakeDex2oatd,
-	dexpreopt.PrepareForTestWithFakeDex2oatd,
+	prepareForTestWithJavaDefaultModulesBase,
+	dexpreopt.FixtureDisableDexpreoptBootImages(true),
+	dexpreopt.FixtureDisableDexpreopt(true),
 )
 
 // Provides everything needed by dexpreopt.
 var PrepareForTestWithDexpreopt = android.GroupFixturePreparers(
-	PrepareForTestWithJavaDefaultModules,
+	prepareForTestWithJavaDefaultModulesBase,
+	dexpreopt.PrepareForTestWithFakeDex2oatd,
+	dexpreopt.PrepareForTestByEnablingDexpreopt,
+)
+
+// Provides everything needed by dexpreopt except the fake_tool_binary for dex2oatd.
+var PrepareForTestWithDexpreoptWithoutFakeDex2oatd = android.GroupFixturePreparers(
+	prepareForTestWithJavaDefaultModulesBase,
 	dexpreopt.PrepareForTestByEnablingDexpreopt,
 )
 
@@ -389,15 +400,20 @@
 	}
 
 	extraApiLibraryModules := map[string]string{
-		"android_stubs_current.from-text":               "api/current.txt",
-		"android_system_stubs_current.from-text":        "api/system-current.txt",
-		"android_test_stubs_current.from-text":          "api/test-current.txt",
-		"android_module_lib_stubs_current.from-text":    "api/module-lib-current.txt",
-		"android_system_server_stubs_current.from-text": "api/system-server-current.txt",
-		"core.current.stubs.from-text":                  "api/current.txt",
-		"legacy.core.platform.api.stubs.from-text":      "api/current.txt",
-		"stable.core.platform.api.stubs.from-text":      "api/current.txt",
-		"core-lambda-stubs.from-text":                   "api/current.txt",
+		"android_stubs_current.from-text":                  "api/current.txt",
+		"android_system_stubs_current.from-text":           "api/system-current.txt",
+		"android_test_stubs_current.from-text":             "api/test-current.txt",
+		"android_module_lib_stubs_current.from-text":       "api/module-lib-current.txt",
+		"android_module_lib_stubs_current_full.from-text":  "api/module-lib-current.txt",
+		"android_system_server_stubs_current.from-text":    "api/system-server-current.txt",
+		"core.current.stubs.from-text":                     "api/current.txt",
+		"legacy.core.platform.api.stubs.from-text":         "api/current.txt",
+		"stable.core.platform.api.stubs.from-text":         "api/current.txt",
+		"core-lambda-stubs.from-text":                      "api/current.txt",
+		"android-non-updatable.stubs.from-text":            "api/current.txt",
+		"android-non-updatable.stubs.system.from-text":     "api/system-current.txt",
+		"android-non-updatable.stubs.test.from-text":       "api/test-current.txt",
+		"android-non-updatable.stubs.module_lib.from-text": "api/module-lib-current.txt",
 	}
 
 	for libName, apiFile := range extraApiLibraryModules {
@@ -603,9 +619,9 @@
 	})
 }
 
-// Sets the value of `installDirOnDevice` of the boot image config with the given name.
+// Sets the value of `installDir` of the boot image config with the given name.
 func FixtureSetBootImageInstallDirOnDevice(name string, installDir string) android.FixturePreparer {
 	return FixtureModifyBootImageConfig(name, func(config *bootImageConfig) {
-		config.installDirOnDevice = installDir
+		config.installDir = installDir
 	})
 }
diff --git a/licenses/Android.bp b/licenses/Android.bp
index 7267cf3..d045725 100644
--- a/licenses/Android.bp
+++ b/licenses/Android.bp
@@ -923,7 +923,10 @@
 license_kind {
     name: "SPDX-license-identifier-Linux-syscall-note",
     // expanding visibility requires approval from an OSPO lawyer or pcounsel
-    visibility: ["//external/libbpf:__subpackages__"],
+    visibility: [
+        "//external/libbpf:__subpackages__",
+        "//prebuilts/vsdk:__subpackages__",
+    ],
     conditions: ["permissive"],
     url: "https://spdx.org/licenses/Linux-syscall-note.html",
 }
@@ -1024,42 +1027,42 @@
 
 license_kind {
     name: "SPDX-license-identifier-OFL",
-    conditions: ["by_exception_only"],
+    conditions: ["notice"],
 }
 
 license_kind {
     name: "SPDX-license-identifier-OFL-1.0",
-    conditions: ["by_exception_only"],
+    conditions: ["notice"],
     url: "https://spdx.org/licenses/OFL-1.0.html",
 }
 
 license_kind {
     name: "SPDX-license-identifier-OFL-1.0-RFN",
-    conditions: ["by_exception_only"],
+    conditions: ["notice"],
     url: "https://spdx.org/licenses/OFL-1.0-RFN.html",
 }
 
 license_kind {
     name: "SPDX-license-identifier-OFL-1.0-no-RFN",
-    conditions: ["by_exception_only"],
+    conditions: ["notice"],
     url: "https://spdx.org/licenses/OFL-1.0-no-RFN.html",
 }
 
 license_kind {
     name: "SPDX-license-identifier-OFL-1.1",
-    conditions: ["by_exception_only"],
+    conditions: ["notice"],
     url: "https://spdx.org/licenses/OFL-1.1.html",
 }
 
 license_kind {
     name: "SPDX-license-identifier-OFL-1.1-RFN",
-    conditions: ["by_exception_only"],
+    conditions: ["notice"],
     url: "https://spdx.org/licenses/OFL-1.1-RFN.html",
 }
 
 license_kind {
     name: "SPDX-license-identifier-OFL-1.1-no-RFN",
-    conditions: ["by_exception_only"],
+    conditions: ["notice"],
     url: "https://spdx.org/licenses/OFL-1.1-no-RFN.html",
 }
 
diff --git a/mk2rbc/mk2rbc.go b/mk2rbc/mk2rbc.go
index 77394d9..8225df6 100644
--- a/mk2rbc/mk2rbc.go
+++ b/mk2rbc/mk2rbc.go
@@ -163,6 +163,21 @@
 
 var identifierFullMatchRegex = regexp.MustCompile("^[a-zA-Z_][a-zA-Z0-9_]*$")
 
+func RelativeToCwd(path string) (string, error) {
+	cwd, err := os.Getwd()
+	if err != nil {
+		return "", err
+	}
+	path, err = filepath.Rel(cwd, path)
+	if err != nil {
+		return "", err
+	}
+	if strings.HasPrefix(path, "../") {
+		return "", fmt.Errorf("Could not make path relative to current working directory: " + path)
+	}
+	return path, nil
+}
+
 // Conversion request parameters
 type Request struct {
 	MkFile          string    // file to convert
@@ -320,6 +335,14 @@
 	loadedSubConfigs := make(map[string]string)
 	for _, mi := range gctx.starScript.inherited {
 		uri := mi.path
+		if strings.HasPrefix(uri, "/") && !strings.HasPrefix(uri, "//") {
+			var err error
+			uri, err = RelativeToCwd(uri)
+			if err != nil {
+				panic(err)
+			}
+			uri = "//" + uri
+		}
 		if m, ok := loadedSubConfigs[uri]; ok {
 			// No need to emit load statement, but fix module name.
 			mi.moduleLocalName = m
diff --git a/mk2rbc/mk2rbc/mk2rbc.go b/mk2rbc/mk2rbc/mk2rbc.go
index cc83430..08c363f 100644
--- a/mk2rbc/mk2rbc/mk2rbc.go
+++ b/mk2rbc/mk2rbc/mk2rbc.go
@@ -187,7 +187,7 @@
 			quit(fmt.Errorf("the product launcher input variables file failed to convert"))
 		}
 
-		err := writeGenerated(*launcher, mk2rbc.Launcher(outputFilePath(files[0]), outputFilePath(*inputVariables),
+		err := writeGenerated(*launcher, mk2rbc.Launcher(outputModulePath(files[0]), outputModulePath(*inputVariables),
 			mk2rbc.MakePath2ModuleName(files[0])))
 		if err != nil {
 			fmt.Fprintf(os.Stderr, "%s: %s", files[0], err)
@@ -205,7 +205,7 @@
 			quit(fmt.Errorf("the board launcher input variables file failed to convert"))
 		}
 		err := writeGenerated(*boardlauncher, mk2rbc.BoardLauncher(
-			outputFilePath(files[0]), outputFilePath(*inputVariables)))
+			outputModulePath(files[0]), outputModulePath(*inputVariables)))
 		if err != nil {
 			fmt.Fprintf(os.Stderr, "%s: %s", files[0], err)
 			ok = false
@@ -402,6 +402,15 @@
 	return path
 }
 
+func outputModulePath(mkFile string) string {
+	path := outputFilePath(mkFile)
+	path, err := mk2rbc.RelativeToCwd(path)
+	if err != nil {
+		panic(err)
+	}
+	return "//" + path
+}
+
 func writeGenerated(path string, contents string) error {
 	if err := os.MkdirAll(filepath.Dir(path), os.ModeDir|os.ModePerm); err != nil {
 		return err
diff --git a/mk2rbc/soong_variables_test.go b/mk2rbc/soong_variables_test.go
index c883882..58e98f6 100644
--- a/mk2rbc/soong_variables_test.go
+++ b/mk2rbc/soong_variables_test.go
@@ -42,8 +42,8 @@
 		{"BUILD_ID", VarClassSoong, starlarkTypeString},
 		{"PLATFORM_SDK_VERSION", VarClassSoong, starlarkTypeInt},
 		{"DEVICE_PACKAGE_OVERLAYS", VarClassSoong, starlarkTypeList},
-		{"ENABLE_CFI", VarClassSoong, starlarkTypeBool},
-		{"ENABLE_PREOPT", VarClassSoong, starlarkTypeBool},
+		{"ENABLE_CFI", VarClassSoong, starlarkTypeString},
+		{"ENABLE_PREOPT", VarClassSoong, starlarkTypeString},
 	}}
 	if !reflect.DeepEqual(expected, actual) {
 		t.Errorf("\nExpected: %v\n  Actual: %v", expected, actual)
diff --git a/multitree/metadata.go b/multitree/metadata.go
index 3fd7215..0eb0efc 100644
--- a/multitree/metadata.go
+++ b/multitree/metadata.go
@@ -20,7 +20,7 @@
 )
 
 func init() {
-	android.RegisterSingletonType("update-meta", UpdateMetaSingleton)
+	android.RegisterParallelSingletonType("update-meta", UpdateMetaSingleton)
 }
 
 func UpdateMetaSingleton() android.Singleton {
diff --git a/provenance/provenance_singleton.go b/provenance/provenance_singleton.go
index fbb6212..5d27c0c 100644
--- a/provenance/provenance_singleton.go
+++ b/provenance/provenance_singleton.go
@@ -51,7 +51,7 @@
 }
 
 func RegisterProvenanceSingleton(ctx android.RegistrationContext) {
-	ctx.RegisterSingletonType("provenance_metadata_singleton", provenanceInfoSingletonFactory)
+	ctx.RegisterParallelSingletonType("provenance_metadata_singleton", provenanceInfoSingletonFactory)
 }
 
 var PrepareForTestWithProvenanceSingleton = android.FixtureRegisterWithContext(RegisterProvenanceSingleton)
diff --git a/python/python.go b/python/python.go
index 1a12973..8fde638 100644
--- a/python/python.go
+++ b/python/python.go
@@ -265,7 +265,6 @@
 			}
 			if proptools.BoolDefault(props.Version.Py2.Enabled, false) {
 				if !mctx.DeviceConfig().BuildBrokenUsesSoongPython2Modules() &&
-					mctx.ModuleName() != "par_test" &&
 					mctx.ModuleName() != "py2-cmd" &&
 					mctx.ModuleName() != "py2-stdlib" {
 					mctx.PropertyErrorf("version.py2.enabled", "Python 2 is no longer supported, please convert to python 3. This error can be temporarily overridden by setting BUILD_BROKEN_USES_SOONG_PYTHON2_MODULES := true in the product configuration")
diff --git a/python/test.go b/python/test.go
index 31da17e..6e23a44 100644
--- a/python/test.go
+++ b/python/test.go
@@ -39,7 +39,7 @@
 }
 
 func PythonTestHostFactory() android.Module {
-	return NewTest(android.HostSupportedNoCross).init()
+	return NewTest(android.HostSupported).init()
 }
 
 func PythonTestFactory() android.Module {
@@ -66,6 +66,10 @@
 
 	// Test options.
 	Test_options TestOptions
+
+	// list of device binary modules that should be installed alongside the test
+	// This property adds 64bit AND 32bit variants of the dependency
+	Data_device_bins_both []string `android:"arch_variant"`
 }
 
 type TestOptions struct {
@@ -98,12 +102,48 @@
 	android.InitAndroidArchModule(p, p.hod, p.multilib)
 	android.InitDefaultableModule(p)
 	android.InitBazelModule(p)
-	if p.hod == android.HostSupportedNoCross && p.testProperties.Test_options.Unit_test == nil {
+	if p.isTestHost() && p.testProperties.Test_options.Unit_test == nil {
 		p.testProperties.Test_options.Unit_test = proptools.BoolPtr(true)
 	}
 	return p
 }
 
+func (p *PythonTestModule) isTestHost() bool {
+	return p.hod == android.HostSupported
+}
+
+var dataDeviceBinsTag = dependencyTag{name: "dataDeviceBins"}
+
+// python_test_host DepsMutator uses this method to add multilib dependencies of
+// data_device_bin_both
+func (p *PythonTestModule) addDataDeviceBinsDeps(ctx android.BottomUpMutatorContext, filter string) {
+	if len(p.testProperties.Data_device_bins_both) < 1 {
+		return
+	}
+
+	var maybeAndroidTarget *android.Target
+	androidTargetList := android.FirstTarget(ctx.Config().Targets[android.Android], filter)
+	if len(androidTargetList) > 0 {
+		maybeAndroidTarget = &androidTargetList[0]
+	}
+
+	if maybeAndroidTarget != nil {
+		ctx.AddFarVariationDependencies(
+			maybeAndroidTarget.Variations(),
+			dataDeviceBinsTag,
+			p.testProperties.Data_device_bins_both...,
+		)
+	}
+}
+
+func (p *PythonTestModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+	p.PythonBinaryModule.DepsMutator(ctx)
+	if p.isTestHost() {
+		p.addDataDeviceBinsDeps(ctx, "lib32")
+		p.addDataDeviceBinsDeps(ctx, "lib64")
+	}
+}
+
 func (p *PythonTestModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	// We inherit from only the library's GenerateAndroidBuildActions, and then
 	// just use buildBinary() so that the binary is not installed into the location
@@ -153,6 +193,12 @@
 		p.data = append(p.data, android.DataPath{SrcPath: dataSrcPath})
 	}
 
+	if p.isTestHost() && len(p.testProperties.Data_device_bins_both) > 0 {
+		ctx.VisitDirectDepsWithTag(dataDeviceBinsTag, func(dep android.Module) {
+			p.data = append(p.data, android.DataPath{SrcPath: android.OutputFileForModule(ctx, dep, "")})
+		})
+	}
+
 	// Emulate the data property for java_data dependencies.
 	for _, javaData := range ctx.GetDirectDepsWithTag(javaDataTag) {
 		for _, javaDataSrcPath := range android.OutputFilesForModule(ctx, javaData, "") {
diff --git a/python/tests/Android.bp b/python/tests/Android.bp
index a656859..e5569ba 100644
--- a/python/tests/Android.bp
+++ b/python/tests/Android.bp
@@ -28,29 +28,6 @@
         unit_test: false,
     },
     version: {
-        py2: {
-            enabled: true,
-            embedded_launcher: true,
-        },
-        py3: {
-            enabled: false,
-            embedded_launcher: true,
-        },
-    },
-}
-
-python_test_host {
-    name: "par_test3",
-    main: "par_test.py",
-    srcs: [
-        "par_test.py",
-        "testpkg/par_test.py",
-    ],
-    // Is not implemented as a python unittest
-    test_options: {
-        unit_test: false,
-    },
-    version: {
         py3: {
             embedded_launcher: true,
         },
diff --git a/python/tests/py-cmd_test.py b/python/tests/py-cmd_test.py
index acda2d7..c7ba0ab 100644
--- a/python/tests/py-cmd_test.py
+++ b/python/tests/py-cmd_test.py
@@ -57,7 +57,7 @@
 
 if sys.version_info[0] == 2:
     assert_equal("len(sys.path)", len(sys.path), 4)
-    assert_equal("sys.path[0]", sys.path[0], os.path.dirname(__file__))
+    assert_equal("sys.path[0]", sys.path[0], os.path.abspath(os.path.dirname(__file__)))
     assert_equal("sys.path[1]", sys.path[1], "/extra")
     assert_equal("sys.path[2]", sys.path[2], os.path.join(sys.executable, "internal"))
     assert_equal("sys.path[3]", sys.path[3], os.path.join(sys.executable, "internal", "stdlib"))
diff --git a/python/tests/runtest.sh b/python/tests/runtest.sh
index 35941dc..f4abae5 100755
--- a/python/tests/runtest.sh
+++ b/python/tests/runtest.sh
@@ -24,10 +24,9 @@
 fi
 
 if [[ ( ! -f $ANDROID_HOST_OUT/nativetest64/par_test/par_test ) ||
-      ( ! -f $ANDROID_HOST_OUT/nativetest64/par_test3/par_test3 ) ||
       ( ! -f $ANDROID_HOST_OUT/bin/py2-cmd ) ||
       ( ! -f $ANDROID_HOST_OUT/bin/py3-cmd )]]; then
-  echo "Run 'm par_test par_test3 py2-cmd py3-cmd' first"
+  echo "Run 'm par_test py2-cmd py3-cmd' first"
   exit 1
 fi
 
@@ -41,12 +40,6 @@
 
 ARGTEST=true $ANDROID_HOST_OUT/nativetest64/par_test/par_test --arg1 arg2
 
-PYTHONHOME= PYTHONPATH= $ANDROID_HOST_OUT/nativetest64/par_test3/par_test3
-PYTHONHOME=/usr $ANDROID_HOST_OUT/nativetest64/par_test3/par_test3
-PYTHONPATH=/usr $ANDROID_HOST_OUT/nativetest64/par_test3/par_test3
-
-ARGTEST=true $ANDROID_HOST_OUT/nativetest64/par_test3/par_test3 --arg1 arg2
-
 cd $(dirname ${BASH_SOURCE[0]})
 
 PYTHONPATH=/extra $ANDROID_HOST_OUT/bin/py2-cmd py-cmd_test.py
diff --git a/python/tests/testpkg/par_test.py b/python/tests/testpkg/par_test.py
index b513409..e12c527 100644
--- a/python/tests/testpkg/par_test.py
+++ b/python/tests/testpkg/par_test.py
@@ -33,11 +33,7 @@
     fileName = fileName[:-1]
 assert_equal("__file__", fileName, os.path.join(archive, "testpkg/par_test.py"))
 
-# Python3 is returning None here for me, and I haven't found any problems caused by this.
-if sys.version_info[0] == 2:
-  assert_equal("__package__", __package__, "testpkg")
-else:
-  assert_equal("__package__", __package__, None)
+assert_equal("__package__", __package__, "testpkg")
 
 assert_equal("__loader__.archive", __loader__.archive, archive)
 assert_equal("__loader__.prefix", __loader__.prefix, "testpkg/")
diff --git a/rust/bindgen.go b/rust/bindgen.go
index 13fa81e..96645b0 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -29,7 +29,7 @@
 	defaultBindgenFlags = []string{""}
 
 	// bindgen should specify its own Clang revision so updating Clang isn't potentially blocked on bindgen failures.
-	bindgenClangVersion = "clang-r487747"
+	bindgenClangVersion = "clang-r487747c"
 
 	_ = pctx.VariableFunc("bindgenClangVersion", func(ctx android.PackageVarContext) string {
 		if override := ctx.Config().Getenv("LLVM_BINDGEN_PREBUILTS_VERSION"); override != "" {
diff --git a/rust/builder.go b/rust/builder.go
index 0aa2225..0dfaef4 100644
--- a/rust/builder.go
+++ b/rust/builder.go
@@ -261,7 +261,7 @@
 	// Disallow experimental features
 	modulePath := android.PathForModuleSrc(ctx).String()
 	if !(android.IsThirdPartyPath(modulePath) || strings.HasPrefix(modulePath, "prebuilts")) {
-		rustcFlags = append(rustcFlags, "-Zallow-features=\"custom_inner_attributes,mixed_integer_ops\"")
+		rustcFlags = append(rustcFlags, "-Zallow-features=\"\"")
 	}
 
 	// Collect linker flags
diff --git a/rust/config/global.go b/rust/config/global.go
index 8894938..60acc6e 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -24,7 +24,7 @@
 var pctx = android.NewPackageContext("android/soong/rust/config")
 
 var (
-	RustDefaultVersion = "1.68.0"
+	RustDefaultVersion = "1.69.0"
 	RustDefaultBase    = "prebuilts/rust/"
 	DefaultEdition     = "2021"
 	Stdlibs            = []string{
@@ -51,9 +51,6 @@
 		// Use v0 mangling to distinguish from C++ symbols
 		"-C symbol-mangling-version=v0",
 		"--color always",
-		// TODO (b/267698452): Temporary workaround until the "no unstable
-		// features" policy is enforced.
-		"-A stable-features",
 		"-Zdylib-lto",
 	}
 
@@ -104,7 +101,7 @@
 
 	pctx.ImportAs("cc_config", "android/soong/cc/config")
 	pctx.StaticVariable("RustLinker", "${cc_config.ClangBin}/clang++")
-	pctx.StaticVariable("RustLinkerArgs", "")
+	pctx.StaticVariable("RustLinkerArgs", "-Wl,--as-needed")
 
 	pctx.StaticVariable("DeviceGlobalLinkFlags", strings.Join(deviceGlobalLinkFlags, " "))
 
diff --git a/rust/doc.go b/rust/doc.go
index fe3581b..6970d79 100644
--- a/rust/doc.go
+++ b/rust/doc.go
@@ -19,7 +19,7 @@
 )
 
 func init() {
-	android.RegisterSingletonType("rustdoc", RustdocSingleton)
+	android.RegisterParallelSingletonType("rustdoc", RustdocSingleton)
 }
 
 func RustdocSingleton() android.Singleton {
diff --git a/rust/fuzz.go b/rust/fuzz.go
index d7e7ddf..c2b9405 100644
--- a/rust/fuzz.go
+++ b/rust/fuzz.go
@@ -31,7 +31,7 @@
 	*binaryDecorator
 
 	fuzzPackagedModule  fuzz.FuzzPackagedModule
-	sharedLibraries     android.Paths
+	sharedLibraries     android.RuleBuilderInstalls
 	installedSharedDeps []string
 }
 
@@ -119,15 +119,17 @@
 	// Grab the list of required shared libraries.
 	fuzz.sharedLibraries, _ = cc.CollectAllSharedDependencies(ctx)
 
-	for _, lib := range fuzz.sharedLibraries {
+	for _, ruleBuilderInstall := range fuzz.sharedLibraries {
+		install := ruleBuilderInstall.To
+
 		fuzz.installedSharedDeps = append(fuzz.installedSharedDeps,
 			cc.SharedLibraryInstallLocation(
-				lib, ctx.Host(), installBase, ctx.Arch().ArchType.String()))
+				install, ctx.Host(), installBase, ctx.Arch().ArchType.String()))
 
 		// Also add the dependency on the shared library symbols dir.
 		if !ctx.Host() {
 			fuzz.installedSharedDeps = append(fuzz.installedSharedDeps,
-				cc.SharedLibrarySymbolsInstallLocation(lib, installBase, ctx.Arch().ArchType.String()))
+				cc.SharedLibrarySymbolsInstallLocation(install, installBase, ctx.Arch().ArchType.String()))
 		}
 	}
 }
diff --git a/rust/fuzz_test.go b/rust/fuzz_test.go
index 865665e..7fa9f5c 100644
--- a/rust/fuzz_test.go
+++ b/rust/fuzz_test.go
@@ -46,18 +46,16 @@
 
 	// Check that compiler flags are set appropriately .
 	fuzz_libtest := ctx.ModuleForTests("fuzz_libtest", "android_arm64_armv8-a_fuzzer").Rule("rustc")
-	if !strings.Contains(fuzz_libtest.Args["rustcFlags"], "-Z sanitizer=hwaddress") ||
-		!strings.Contains(fuzz_libtest.Args["rustcFlags"], "-C passes='sancov-module'") ||
+	if !strings.Contains(fuzz_libtest.Args["rustcFlags"], "-C passes='sancov-module'") ||
 		!strings.Contains(fuzz_libtest.Args["rustcFlags"], "--cfg fuzzing") {
-		t.Errorf("rust_fuzz module does not contain the expected flags (sancov-module, cfg fuzzing, hwaddress sanitizer).")
+		t.Errorf("rust_fuzz module does not contain the expected flags (sancov-module, cfg fuzzing).")
 
 	}
 
 	// Check that dependencies have 'fuzzer' variants produced for them as well.
 	libtest_fuzzer := ctx.ModuleForTests("libtest_fuzzing", "android_arm64_armv8-a_rlib_rlib-std_fuzzer").Output("libtest_fuzzing.rlib")
-	if !strings.Contains(libtest_fuzzer.Args["rustcFlags"], "-Z sanitizer=hwaddress") ||
-		!strings.Contains(libtest_fuzzer.Args["rustcFlags"], "-C passes='sancov-module'") ||
+	if !strings.Contains(libtest_fuzzer.Args["rustcFlags"], "-C passes='sancov-module'") ||
 		!strings.Contains(libtest_fuzzer.Args["rustcFlags"], "--cfg fuzzing") {
-		t.Errorf("rust_fuzz dependent library does not contain the expected flags (sancov-module, cfg fuzzing, hwaddress sanitizer).")
+		t.Errorf("rust_fuzz dependent library does not contain the expected flags (sancov-module, cfg fuzzing).")
 	}
 }
diff --git a/rust/project_json.go b/rust/project_json.go
index fe259d6..40aa7c7 100644
--- a/rust/project_json.go
+++ b/rust/project_json.go
@@ -74,7 +74,7 @@
 }
 
 func init() {
-	android.RegisterSingletonType("rust_project_generator", rustProjectGeneratorSingleton)
+	android.RegisterParallelSingletonType("rust_project_generator", rustProjectGeneratorSingleton)
 }
 
 // sourceProviderVariantSource returns the path to the source file if this
diff --git a/rust/protobuf.go b/rust/protobuf.go
index e30f25d..0cf6e8c 100644
--- a/rust/protobuf.go
+++ b/rust/protobuf.go
@@ -52,6 +52,10 @@
 
 	// List of libraries which export include paths required for this module
 	Header_libs []string `android:"arch_variant,variant_prepend"`
+
+	// Use protobuf version 3.x. This will be deleted once we migrate all current users
+	// of protobuf off of 2.x.
+	Use_protobuf3 *bool
 }
 
 type protobufDecorator struct {
@@ -65,6 +69,10 @@
 	protoFlags     android.ProtoFlags
 }
 
+func (proto *protobufDecorator) useProtobuf3() bool {
+	return Bool(proto.Properties.Use_protobuf3)
+}
+
 func (proto *protobufDecorator) GenerateSource(ctx ModuleContext, deps PathDeps) android.Path {
 	var protoFlags android.ProtoFlags
 	var grpcProtoFlags android.ProtoFlags
@@ -73,7 +81,13 @@
 	outDir := android.PathForModuleOut(ctx)
 	protoFiles := android.PathsForModuleSrc(ctx, proto.Properties.Protos)
 	grpcFiles := android.PathsForModuleSrc(ctx, proto.Properties.Grpc_protos)
+
+	// For now protobuf2 (the deprecated version) remains the default. This will change in the
+	// future as we update the various users.
 	protoPluginPath := ctx.Config().HostToolPath(ctx, "protoc-gen-rust-deprecated")
+	if proto.useProtobuf3() == true {
+		protoPluginPath = ctx.Config().HostToolPath(ctx, "protoc-gen-rust")
+	}
 
 	commonProtoFlags = append(commonProtoFlags, defaultProtobufFlags...)
 	commonProtoFlags = append(commonProtoFlags, proto.Properties.Proto_flags...)
@@ -206,10 +220,20 @@
 
 func (proto *protobufDecorator) SourceProviderDeps(ctx DepsContext, deps Deps) Deps {
 	deps = proto.BaseSourceProvider.SourceProviderDeps(ctx, deps)
-	deps.Rustlibs = append(deps.Rustlibs, "libprotobuf_deprecated")
+	useProtobuf3 := proto.useProtobuf3()
+	if useProtobuf3 == true {
+		deps.Rustlibs = append(deps.Rustlibs, "libprotobuf")
+	} else {
+		deps.Rustlibs = append(deps.Rustlibs, "libprotobuf_deprecated")
+	}
 	deps.HeaderLibs = append(deps.SharedLibs, proto.Properties.Header_libs...)
 
 	if len(proto.Properties.Grpc_protos) > 0 {
+		if useProtobuf3 == true {
+			ctx.PropertyErrorf("protos", "rust_protobuf with grpc_protos defined must currently use "+
+				"`use_protobuf3: false,` in the Android.bp file. This is temporary until the "+
+				"grpcio crate is updated to use the current version of the protobuf crate.")
+		}
 		deps.Rustlibs = append(deps.Rustlibs, "libgrpcio", "libfutures")
 		deps.HeaderLibs = append(deps.HeaderLibs, "libprotobuf-cpp-full")
 	}
diff --git a/rust/protobuf_test.go b/rust/protobuf_test.go
index 0aa4549..b723f3f 100644
--- a/rust/protobuf_test.go
+++ b/rust/protobuf_test.go
@@ -69,6 +69,55 @@
 	}
 }
 
+func TestRustProtobuf3(t *testing.T) {
+	ctx := testRust(t, `
+		rust_protobuf {
+			name: "librust_proto",
+			protos: ["buf.proto", "proto.proto"],
+			crate_name: "rust_proto",
+			source_stem: "buf",
+            use_protobuf3: true,
+			shared_libs: ["libfoo_shared"],
+			static_libs: ["libfoo_static"],
+		}
+		cc_library_shared {
+			name: "libfoo_shared",
+			export_include_dirs: ["shared_include"],
+		}
+		cc_library_static {
+			name: "libfoo_static",
+			export_include_dirs: ["static_include"],
+		}
+	`)
+	// Check that libprotobuf is added as a dependency.
+	librust_proto := ctx.ModuleForTests("librust_proto", "android_arm64_armv8-a_dylib").Module().(*Module)
+	if !android.InList("libprotobuf", librust_proto.Properties.AndroidMkDylibs) {
+		t.Errorf("libprotobuf dependency missing for rust_protobuf (dependency missing from AndroidMkDylibs)")
+	}
+
+	// Make sure the correct plugin is being used.
+	librust_proto_out := ctx.ModuleForTests("librust_proto", "android_arm64_armv8-a_source").Output("buf.rs")
+	cmd := librust_proto_out.RuleParams.Command
+	if w := "protoc-gen-rust"; !strings.Contains(cmd, w) {
+		t.Errorf("expected %q in %q", w, cmd)
+	}
+
+	// Check exported include directories
+	if w := "-Ishared_include"; !strings.Contains(cmd, w) {
+		t.Errorf("expected %q in %q", w, cmd)
+	}
+	if w := "-Istatic_include"; !strings.Contains(cmd, w) {
+		t.Errorf("expected %q in %q", w, cmd)
+	}
+
+	// Check proto.rs, the second protobuf, is listed as an output
+	librust_proto_outputs := ctx.ModuleForTests("librust_proto", "android_arm64_armv8-a_source").AllOutputs()
+	if android.InList("proto.rs", librust_proto_outputs) {
+		t.Errorf("rust_protobuf is not producing multiple outputs; expected 'proto.rs' in list, got: %#v ",
+			librust_proto_outputs)
+	}
+}
+
 func TestRustGrpc(t *testing.T) {
 	ctx := testRust(t, `
 		rust_protobuf {
diff --git a/rust/rust.go b/rust/rust.go
index 7b520cd..4324ecb 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -45,7 +45,7 @@
 	})
 	pctx.Import("android/soong/rust/config")
 	pctx.ImportAs("cc_config", "android/soong/cc/config")
-	android.InitRegistrationContext.RegisterSingletonType("kythe_rust_extract", kytheExtractRustFactory)
+	android.InitRegistrationContext.RegisterParallelSingletonType("kythe_rust_extract", kytheExtractRustFactory)
 }
 
 type Flags struct {
@@ -649,7 +649,7 @@
 	panic(fmt.Errorf("FuzzPackagedModule called on non-fuzz module: %q", mod.BaseModuleName()))
 }
 
-func (mod *Module) FuzzSharedLibraries() android.Paths {
+func (mod *Module) FuzzSharedLibraries() android.RuleBuilderInstalls {
 	if fuzzer, ok := mod.compiler.(*fuzzDecorator); ok {
 		return fuzzer.sharedLibraries
 	}
diff --git a/rust/sanitize.go b/rust/sanitize.go
index c68137e..83cf055 100644
--- a/rust/sanitize.go
+++ b/rust/sanitize.go
@@ -226,11 +226,6 @@
 	}
 	if Bool(sanitize.Properties.Sanitize.Fuzzer) {
 		flags.RustFlags = append(flags.RustFlags, fuzzerFlags...)
-		if ctx.Arch().ArchType == android.Arm64 && ctx.Os().Bionic() {
-			flags.RustFlags = append(flags.RustFlags, hwasanFlags...)
-		} else {
-			flags.RustFlags = append(flags.RustFlags, asanFlags...)
-		}
 	} else if Bool(sanitize.Properties.Sanitize.Hwaddress) {
 		flags.RustFlags = append(flags.RustFlags, hwasanFlags...)
 	} else if Bool(sanitize.Properties.Sanitize.Address) {
@@ -424,14 +419,6 @@
 		return true
 	}
 
-	// TODO(b/178365482): Rust/CC interop doesn't work just yet; don't sanitize rust_ffi modules until
-	// linkage issues are resolved.
-	if lib, ok := mod.compiler.(libraryInterface); ok {
-		if lib.shared() || lib.static() {
-			return true
-		}
-	}
-
 	return mod.sanitize.isSanitizerExplicitlyDisabled(t)
 }
 
diff --git a/rust/testing.go b/rust/testing.go
index a33d948..7f30569 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -127,6 +127,12 @@
 			min_sdk_version: "29",
 		}
 		rust_library {
+			name: "libprotobuf",
+			crate_name: "protobuf",
+			srcs: ["foo.rs"],
+			host_supported: true,
+		}
+		rust_library {
 			name: "libprotobuf_deprecated",
 			crate_name: "protobuf",
 			srcs: ["foo.rs"],
@@ -194,8 +200,8 @@
 		ctx.BottomUp("rust_stdlinkage", LibstdMutator).Parallel()
 		ctx.BottomUp("rust_begin", BeginMutator).Parallel()
 	})
-	ctx.RegisterSingletonType("rust_project_generator", rustProjectGeneratorSingleton)
-	ctx.RegisterSingletonType("kythe_rust_extract", kytheExtractRustFactory)
+	ctx.RegisterParallelSingletonType("rust_project_generator", rustProjectGeneratorSingleton)
+	ctx.RegisterParallelSingletonType("kythe_rust_extract", kytheExtractRustFactory)
 	ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
 		ctx.BottomUp("rust_sanitizers", rustSanitizerRuntimeMutator).Parallel()
 	})
diff --git a/scripts/Android.bp b/scripts/Android.bp
index 9367ff0..97f6ab4 100644
--- a/scripts/Android.bp
+++ b/scripts/Android.bp
@@ -237,3 +237,20 @@
     name: "jars-to-module-info-java",
     src: "jars-to-module-info-java.sh",
 }
+
+python_binary_host {
+    name: "modify_permissions_allowlist",
+    main: "modify_permissions_allowlist.py",
+    srcs: [
+        "modify_permissions_allowlist.py",
+    ],
+}
+
+python_test_host {
+    name: "modify_permissions_allowlist_test",
+    main: "modify_permissions_allowlist_test.py",
+    srcs: [
+        "modify_permissions_allowlist_test.py",
+        "modify_permissions_allowlist.py",
+    ],
+}
diff --git a/scripts/check_boot_jars/package_allowed_list.txt b/scripts/check_boot_jars/package_allowed_list.txt
index 869fd3f..dad2b47 100644
--- a/scripts/check_boot_jars/package_allowed_list.txt
+++ b/scripts/check_boot_jars/package_allowed_list.txt
@@ -72,6 +72,7 @@
 javax\.xml\.validation
 javax\.xml\.xpath
 jdk\.internal
+jdk\.internal\.access
 jdk\.internal\.math
 jdk\.internal\.misc
 jdk\.internal\.ref
diff --git a/scripts/construct_context.py b/scripts/construct_context.py
index 3f601c3..fc3a89e 100755
--- a/scripts/construct_context.py
+++ b/scripts/construct_context.py
@@ -19,6 +19,7 @@
 from __future__ import print_function
 
 import argparse
+import json
 import sys
 
 from manifest import compare_version_gt
@@ -33,20 +34,14 @@
         dest='sdk',
         help='specify target SDK version (as it appears in the manifest)')
     parser.add_argument(
-        '--host-context-for-sdk',
-        dest='host_contexts',
-        action='append',
-        nargs=2,
-        metavar=('sdk', 'context'),
-        help='specify context on host for a given SDK version or "any" version')
+        '--context-json',
+        default='',
+        dest='context_json',
+    )
     parser.add_argument(
-        '--target-context-for-sdk',
-        dest='target_contexts',
-        action='append',
-        nargs=2,
-        metavar=('sdk', 'context'),
-        help='specify context on target for a given SDK version or "any" '
-        'version'
+        '--product-packages',
+        default='',
+        dest='product_packages_file',
     )
     return parser.parse_args(args)
 
@@ -55,28 +50,69 @@
 # context regardless of the target SDK version.
 any_sdk = 'any'
 
-
-# We assume that the order of context arguments passed to this script is
-# correct (matches the order computed by package manager). It is possible to
-# sort them here, but Soong needs to use deterministic order anyway, so it can
-# as well use the correct order.
-def construct_context(versioned_contexts, target_sdk):
-    context = []
-    for [sdk, ctx] in versioned_contexts:
-        if sdk == any_sdk or compare_version_gt(sdk, target_sdk):
-            context.append(ctx)
-    return context
+context_sep = '#'
 
 
-def construct_contexts(args):
-    host_context = construct_context(args.host_contexts, args.sdk)
-    target_context = construct_context(args.target_contexts, args.sdk)
-    context_sep = '#'
+def encode_class_loader(context, product_packages):
+    host_sub_contexts, target_sub_contexts = encode_class_loaders(
+            context['Subcontexts'], product_packages)
+
+    return ('PCL[%s]%s' % (context['Host'], host_sub_contexts),
+            'PCL[%s]%s' % (context['Device'], target_sub_contexts))
+
+
+def encode_class_loaders(contexts, product_packages):
+    host_contexts = []
+    target_contexts = []
+
+    for context in contexts:
+        if not context['Optional'] or context['Name'] in product_packages:
+            host_context, target_context = encode_class_loader(
+                    context, product_packages)
+            host_contexts.append(host_context)
+            target_contexts.append(target_context)
+
+    if host_contexts:
+        return ('{%s}' % context_sep.join(host_contexts),
+                '{%s}' % context_sep.join(target_contexts))
+    else:
+        return '', ''
+
+
+def construct_context_args(target_sdk, context_json, product_packages):
+    all_contexts = []
+
+    # CLC for different SDK versions should come in specific order that agrees
+    # with PackageManager. Since PackageManager processes SDK versions in
+    # ascending order and prepends compatibility libraries at the front, the
+    # required order is descending, except for any_sdk that has numerically
+    # the largest order, but must be the last one. Example of correct order:
+    # [30, 29, 28, any_sdk]. There are Python tests to ensure that someone
+    # doesn't change this by accident, but there is no way to guard against
+    # changes in the PackageManager, except for grepping logcat on the first
+    # boot for absence of the following messages:
+    #
+    #   `logcat | grep -E 'ClassLoaderContext [a-z ]+ mismatch`
+
+    for sdk, contexts in sorted(
+            ((sdk, contexts)
+             for sdk, contexts in context_json.items()
+             if sdk != any_sdk and compare_version_gt(sdk, target_sdk)),
+            key=lambda item: int(item[0]), reverse=True):
+        all_contexts += contexts
+
+    if any_sdk in context_json:
+        all_contexts += context_json[any_sdk]
+
+    host_contexts, target_contexts = encode_class_loaders(
+            all_contexts, product_packages)
+
     return (
-        'class_loader_context_arg=--class-loader-context=PCL[]{%s} ; ' %
-        context_sep.join(host_context) +
-        'stored_class_loader_context_arg=--stored-class-loader-context=PCL[]{%s}' #pylint: disable=line-too-long
-        % context_sep.join(target_context))
+        'class_loader_context_arg=--class-loader-context=PCL[]%s ; ' %
+        host_contexts +
+        'stored_class_loader_context_arg='
+        '--stored-class-loader-context=PCL[]%s'
+        % target_contexts)
 
 
 def main():
@@ -85,12 +121,12 @@
         args = parse_args(sys.argv[1:])
         if not args.sdk:
             raise SystemExit('target sdk version is not set')
-        if not args.host_contexts:
-            args.host_contexts = []
-        if not args.target_contexts:
-            args.target_contexts = []
 
-        print(construct_contexts(args))
+        context_json = json.loads(args.context_json)
+        with open(args.product_packages_file, 'r') as f:
+            product_packages = set(line.strip() for line in f if line.strip())
+
+        print(construct_context_args(args.sdk, context_json, product_packages))
 
     # pylint: disable=broad-except
     except Exception as err:
diff --git a/scripts/construct_context_test.py b/scripts/construct_context_test.py
index 2ff5ac5..34a0e16 100755
--- a/scripts/construct_context_test.py
+++ b/scripts/construct_context_test.py
@@ -24,62 +24,249 @@
 sys.dont_write_bytecode = True
 
 
-def construct_contexts(arglist):
-    args = cc.parse_args(arglist)
-    return cc.construct_contexts(args)
+CONTEXT_JSON = {
+    '28': [
+        {
+            'Name': 'z',
+            'Optional': False,
+            'Host': 'out/zdir/z.jar',
+            'Device': '/system/z.jar',
+            'Subcontexts': [],
+        },
+    ],
+    '29': [
+        {
+            'Name': 'x',
+            'Optional': False,
+            'Host': 'out/xdir/x.jar',
+            'Device': '/system/x.jar',
+            'Subcontexts': [],
+        },
+        {
+            'Name': 'y',
+            'Optional': False,
+            'Host': 'out/ydir/y.jar',
+            'Device': '/product/y.jar',
+            'Subcontexts': [],
+        },
+    ],
+    'any': [
+        {
+            'Name': 'a',
+            'Optional': False,
+            'Host': 'out/adir/a.jar',
+            'Device': '/system/a.jar',
+            'Subcontexts': [
+                {  # Not installed optional, being the only child.
+                    'Name': 'a1',
+                    'Optional': True,
+                    'Host': 'out/a1dir/a1.jar',
+                    'Device': '/product/a1.jar',
+                    'Subcontexts': [],
+                },
+            ],
+        },
+        {
+            'Name': 'b',
+            'Optional': True,
+            'Host': 'out/bdir/b.jar',
+            'Device': '/product/b.jar',
+            'Subcontexts': [
+                {  # Not installed but required.
+                    'Name': 'b1',
+                    'Optional': False,
+                    'Host': 'out/b1dir/b1.jar',
+                    'Device': '/product/b1.jar',
+                    'Subcontexts': [],
+                },
+                {  # Installed optional.
+                    'Name': 'b2',
+                    'Optional': True,
+                    'Host': 'out/b2dir/b2.jar',
+                    'Device': '/product/b2.jar',
+                    'Subcontexts': [],
+                },
+                {  # Not installed optional.
+                    'Name': 'b3',
+                    'Optional': True,
+                    'Host': 'out/b3dir/b3.jar',
+                    'Device': '/product/b3.jar',
+                    'Subcontexts': [],
+                },
+                {  # Installed optional with one more level of nested deps.
+                    'Name': 'b4',
+                    'Optional': True,
+                    'Host': 'out/b4dir/b4.jar',
+                    'Device': '/product/b4.jar',
+                    'Subcontexts': [
+                        {
+                            'Name': 'b41',
+                            'Optional': True,
+                            'Host': 'out/b41dir/b41.jar',
+                            'Device': '/product/b41.jar',
+                            'Subcontexts': [],
+                        },
+                        {
+                            'Name': 'b42',
+                            'Optional': True,
+                            'Host': 'out/b42dir/b42.jar',
+                            'Device': '/product/b42.jar',
+                            'Subcontexts': [],
+                        },
+                    ],
+                },
+            ],
+        },
+        {  # Not installed optional, at the top-level.
+            'Name': 'c',
+            'Optional': True,
+            'Host': 'out/cdir/c.jar',
+            'Device': '/product/c.jar',
+            'Subcontexts': [],
+        },
+    ],
+}
 
 
-contexts = [
-    '--host-context-for-sdk',
-    '28',
-    'PCL[out/zdir/z.jar]',
-    '--target-context-for-sdk',
-    '28',
-    'PCL[/system/z.jar]',
-    '--host-context-for-sdk',
-    '29',
-    'PCL[out/xdir/x.jar]#PCL[out/ydir/y.jar]',
-    '--target-context-for-sdk',
-    '29',
-    'PCL[/system/x.jar]#PCL[/product/y.jar]',
-    '--host-context-for-sdk',
-    'any',
-    'PCL[out/adir/a.jar]#PCL[out/bdir/b.jar]',
-    '--target-context-for-sdk',
-    'any',
-    'PCL[/system/a.jar]#PCL[/product/b.jar]',
-]
+PRODUCT_PACKAGES = ['a', 'b', 'b2', 'b4', 'b41', 'b42', 'x', 'y', 'z']
 
-#pylint: disable=line-too-long
+
+def construct_context_args(target_sdk):
+    return cc.construct_context_args(target_sdk, CONTEXT_JSON, PRODUCT_PACKAGES)
+
+
 class ConstructContextTest(unittest.TestCase):
+    def test_construct_context_27(self):
+        actual = construct_context_args('27')
+        # The order matters.
+        expected = (
+            'class_loader_context_arg='
+            '--class-loader-context=PCL[]{'
+            'PCL[out/xdir/x.jar]#'
+            'PCL[out/ydir/y.jar]#'
+            'PCL[out/zdir/z.jar]#'
+            'PCL[out/adir/a.jar]#'
+            'PCL[out/bdir/b.jar]{'
+            'PCL[out/b1dir/b1.jar]#'
+            'PCL[out/b2dir/b2.jar]#'
+            'PCL[out/b4dir/b4.jar]{'
+            'PCL[out/b41dir/b41.jar]#'
+            'PCL[out/b42dir/b42.jar]'
+            '}'
+            '}'
+            '}'
+            ' ; '
+            'stored_class_loader_context_arg='
+            '--stored-class-loader-context=PCL[]{'
+            'PCL[/system/x.jar]#'
+            'PCL[/product/y.jar]#'
+            'PCL[/system/z.jar]#'
+            'PCL[/system/a.jar]#'
+            'PCL[/product/b.jar]{'
+            'PCL[/product/b1.jar]#'
+            'PCL[/product/b2.jar]#'
+            'PCL[/product/b4.jar]{'
+            'PCL[/product/b41.jar]#'
+            'PCL[/product/b42.jar]'
+            '}'
+            '}'
+            '}')
+        self.assertEqual(actual, expected)
 
     def test_construct_context_28(self):
-        args = ['--target-sdk-version', '28'] + contexts
-        result = construct_contexts(args)
-        expect = (
-            'class_loader_context_arg=--class-loader-context=PCL[]{PCL[out/xdir/x.jar]#PCL[out/ydir/y.jar]#PCL[out/adir/a.jar]#PCL[out/bdir/b.jar]}'
+        actual = construct_context_args('28')
+        expected = (
+            'class_loader_context_arg='
+            '--class-loader-context=PCL[]{'
+            'PCL[out/xdir/x.jar]#'
+            'PCL[out/ydir/y.jar]#'
+            'PCL[out/adir/a.jar]#'
+            'PCL[out/bdir/b.jar]{'
+            'PCL[out/b1dir/b1.jar]#'
+            'PCL[out/b2dir/b2.jar]#'
+            'PCL[out/b4dir/b4.jar]{'
+            'PCL[out/b41dir/b41.jar]#'
+            'PCL[out/b42dir/b42.jar]'
+            '}'
+            '}'
+            '}'
             ' ; '
-            'stored_class_loader_context_arg=--stored-class-loader-context=PCL[]{PCL[/system/x.jar]#PCL[/product/y.jar]#PCL[/system/a.jar]#PCL[/product/b.jar]}')
-        self.assertEqual(result, expect)
+            'stored_class_loader_context_arg='
+            '--stored-class-loader-context=PCL[]{'
+            'PCL[/system/x.jar]#'
+            'PCL[/product/y.jar]#'
+            'PCL[/system/a.jar]#'
+            'PCL[/product/b.jar]{'
+            'PCL[/product/b1.jar]#'
+            'PCL[/product/b2.jar]#'
+            'PCL[/product/b4.jar]{'
+            'PCL[/product/b41.jar]#'
+            'PCL[/product/b42.jar]'
+            '}'
+            '}'
+            '}')
+        self.assertEqual(actual, expected)
 
     def test_construct_context_29(self):
-        args = ['--target-sdk-version', '29'] + contexts
-        result = construct_contexts(args)
-        expect = (
-            'class_loader_context_arg=--class-loader-context=PCL[]{PCL[out/adir/a.jar]#PCL[out/bdir/b.jar]}'
+        actual = construct_context_args('29')
+        expected = (
+            'class_loader_context_arg='
+            '--class-loader-context=PCL[]{'
+            'PCL[out/adir/a.jar]#'
+            'PCL[out/bdir/b.jar]{'
+            'PCL[out/b1dir/b1.jar]#'
+            'PCL[out/b2dir/b2.jar]#'
+            'PCL[out/b4dir/b4.jar]{'
+            'PCL[out/b41dir/b41.jar]#'
+            'PCL[out/b42dir/b42.jar]'
+            '}'
+            '}'
+            '}'
             ' ; '
-            'stored_class_loader_context_arg=--stored-class-loader-context=PCL[]{PCL[/system/a.jar]#PCL[/product/b.jar]}')
-        self.assertEqual(result, expect)
+            'stored_class_loader_context_arg='
+            '--stored-class-loader-context=PCL[]{'
+            'PCL[/system/a.jar]#'
+            'PCL[/product/b.jar]{'
+            'PCL[/product/b1.jar]#'
+            'PCL[/product/b2.jar]#'
+            'PCL[/product/b4.jar]{'
+            'PCL[/product/b41.jar]#'
+            'PCL[/product/b42.jar]'
+            '}'
+            '}'
+            '}')
+        self.assertEqual(actual, expected)
 
     def test_construct_context_S(self):
-        args = ['--target-sdk-version', 'S'] + contexts
-        result = construct_contexts(args)
-        expect = (
-            'class_loader_context_arg=--class-loader-context=PCL[]{PCL[out/adir/a.jar]#PCL[out/bdir/b.jar]}'
+        actual = construct_context_args('S')
+        expected = (
+            'class_loader_context_arg='
+            '--class-loader-context=PCL[]{'
+            'PCL[out/adir/a.jar]#'
+            'PCL[out/bdir/b.jar]{'
+            'PCL[out/b1dir/b1.jar]#'
+            'PCL[out/b2dir/b2.jar]#'
+            'PCL[out/b4dir/b4.jar]{'
+            'PCL[out/b41dir/b41.jar]#'
+            'PCL[out/b42dir/b42.jar]'
+            '}'
+            '}'
+            '}'
             ' ; '
-            'stored_class_loader_context_arg=--stored-class-loader-context=PCL[]{PCL[/system/a.jar]#PCL[/product/b.jar]}')
-        self.assertEqual(result, expect)
-#pylint: enable=line-too-long
+            'stored_class_loader_context_arg='
+            '--stored-class-loader-context=PCL[]{'
+            'PCL[/system/a.jar]#'
+            'PCL[/product/b.jar]{'
+            'PCL[/product/b1.jar]#'
+            'PCL[/product/b2.jar]#'
+            'PCL[/product/b4.jar]{'
+            'PCL[/product/b41.jar]#'
+            'PCL[/product/b42.jar]'
+            '}'
+            '}'
+            '}')
+        self.assertEqual(actual, expected)
+
 
 if __name__ == '__main__':
     unittest.main(verbosity=2)
diff --git a/scripts/modify_permissions_allowlist.py b/scripts/modify_permissions_allowlist.py
new file mode 100755
index 0000000..38ec7ec
--- /dev/null
+++ b/scripts/modify_permissions_allowlist.py
@@ -0,0 +1,70 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""A tool for modifying privileged permission allowlists."""
+
+from __future__ import print_function
+
+import argparse
+import sys
+from xml.dom import minidom
+
+
+class InvalidRootNodeException(Exception):
+  pass
+
+
+class InvalidNumberOfPrivappPermissionChildren(Exception):
+  pass
+
+
+def modify_allowlist(allowlist_dom, package_name):
+  if allowlist_dom.documentElement.tagName != 'permissions':
+    raise InvalidRootNodeException
+  nodes = allowlist_dom.getElementsByTagName('privapp-permissions')
+  if nodes.length != 1:
+    raise InvalidNumberOfPrivappPermissionChildren
+  privapp_permissions = nodes[0]
+  privapp_permissions.setAttribute('package', package_name)
+
+
+def parse_args():
+  """Parse commandline arguments."""
+
+  parser = argparse.ArgumentParser()
+  parser.add_argument('input', help='input allowlist template file')
+  parser.add_argument(
+      'package_name', help='package name to use in the allowlist'
+  )
+  parser.add_argument('output', help='output allowlist file')
+
+  return parser.parse_args()
+
+
+def main():
+  try:
+    args = parse_args()
+    doc = minidom.parse(args.input)
+    modify_allowlist(doc, args.package_name)
+    with open(args.output, 'w') as output_file:
+      doc.writexml(output_file, encoding='utf-8')
+  except Exception as err:
+    print('error: ' + str(err), file=sys.stderr)
+    sys.exit(-1)
+
+
+if __name__ == '__main__':
+  main()
diff --git a/scripts/modify_permissions_allowlist_test.py b/scripts/modify_permissions_allowlist_test.py
new file mode 100755
index 0000000..ee8b12c
--- /dev/null
+++ b/scripts/modify_permissions_allowlist_test.py
@@ -0,0 +1,76 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+"""Unit tests for modify_permissions_allowlist.py."""
+
+from __future__ import print_function
+
+import unittest
+
+from xml.dom import minidom
+
+from modify_permissions_allowlist import InvalidRootNodeException, InvalidNumberOfPrivappPermissionChildren, modify_allowlist
+
+
+class ModifyPermissionsAllowlistTest(unittest.TestCase):
+
+  def test_invalid_root(self):
+    xml_data = '<foo></foo>'
+    xml_dom = minidom.parseString(xml_data)
+    self.assertRaises(InvalidRootNodeException, modify_allowlist, xml_dom, 'x')
+
+  def test_no_packages(self):
+    xml_data = '<permissions></permissions>'
+    xml_dom = minidom.parseString(xml_data)
+    self.assertRaises(
+        InvalidNumberOfPrivappPermissionChildren, modify_allowlist, xml_dom, 'x'
+    )
+
+  def test_multiple_packages(self):
+    xml_data = (
+        '<permissions>'
+        '  <privapp-permissions package="foo.bar"></privapp-permissions>'
+        '  <privapp-permissions package="bar.baz"></privapp-permissions>'
+        '</permissions>'
+    )
+    xml_dom = minidom.parseString(xml_data)
+    self.assertRaises(
+        InvalidNumberOfPrivappPermissionChildren, modify_allowlist, xml_dom, 'x'
+    )
+
+  def test_modify_package_name(self):
+    xml_data = (
+        '<permissions>'
+        '  <privapp-permissions package="foo.bar">'
+        '    <permission name="myperm1"/>'
+        '  </privapp-permissions>'
+        '</permissions>'
+    )
+    xml_dom = minidom.parseString(xml_data)
+    modify_allowlist(xml_dom, 'bar.baz')
+    expected_data = (
+        '<?xml version="1.0" ?>'
+        '<permissions>'
+        '  <privapp-permissions package="bar.baz">'
+        '    <permission name="myperm1"/>'
+        '  </privapp-permissions>'
+        '</permissions>'
+    )
+    self.assertEqual(expected_data, xml_dom.toxml())
+
+
+if __name__ == '__main__':
+  unittest.main(verbosity=2)
diff --git a/scripts/rbc-run b/scripts/rbc-run
deleted file mode 100755
index 8d93f0e..0000000
--- a/scripts/rbc-run
+++ /dev/null
@@ -1,18 +0,0 @@
-#! /bin/bash
-# Convert and run one configuration
-# Args: a product/board makefile optionally followed by additional arguments
-#       that will be passed to rbcrun.
-[[ $# -gt 1 && -f "$1" && -f "$2" ]] || { echo "Usage: ${0##*/} product.mk input_variables.mk [Additional rbcrun arguments]" >&2; exit 1; }
-set -eu
-
-declare -r output_root="${OUT_DIR:-out}"
-declare -r runner="${output_root}/rbcrun"
-declare -r converter="${output_root}/mk2rbc"
-declare -r launcher="${output_root}/rbc/launcher.rbc"
-declare -r makefile_list="${output_root}/.module_paths/configuration.list"
-declare -r makefile="$1"
-declare -r input_variables="$2"
-shift 2
-"${converter}" -mode=write -r --outdir "${output_root}/rbc" --input_variables "${input_variables}" --launcher="${launcher}" --makefile_list="${makefile_list}" "${makefile}"
-"${runner}" RBC_OUT="make,global" RBC_DEBUG="${RBC_DEBUG:-}" $@ "${launcher}"
-
diff --git a/sdk/bootclasspath_fragment_sdk_test.go b/sdk/bootclasspath_fragment_sdk_test.go
index 0d6496d..bef82d6 100644
--- a/sdk/bootclasspath_fragment_sdk_test.go
+++ b/sdk/bootclasspath_fragment_sdk_test.go
@@ -74,7 +74,7 @@
 func TestSnapshotWithBootclasspathFragment_ImageName(t *testing.T) {
 	result := android.GroupFixturePreparers(
 		prepareForSdkTestWithJava,
-		java.PrepareForTestWithJavaDefaultModules,
+		java.PrepareForTestWithDexpreopt,
 		prepareForSdkTestWithApex,
 
 		// Some additional files needed for the art apex.
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 3a2ecc0..6159ea9 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -19,12 +19,14 @@
 	"testing"
 
 	"android/soong/android"
+	"android/soong/dexpreopt"
 	"android/soong/java"
 )
 
 var prepareForSdkTestWithJava = android.GroupFixturePreparers(
 	java.PrepareForTestWithJavaBuildComponents,
 	PrepareForTestWithSdkBuildComponents,
+	dexpreopt.PrepareForTestWithFakeDex2oatd,
 
 	// Ensure that all source paths are provided. This helps ensure that the snapshot generation is
 	// consistent and all files referenced from the snapshot's Android.bp file have actually been
diff --git a/sdk/update.go b/sdk/update.go
index d98ab68..d3c59b0 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -1963,6 +1963,10 @@
 	requiredTraits android.SdkMemberTraitSet
 }
 
+func (m *memberContext) ModuleErrorf(fmt string, args ...interface{}) {
+	m.sdkMemberContext.ModuleErrorf(fmt, args...)
+}
+
 func (m *memberContext) SdkModuleContext() android.ModuleContext {
 	return m.sdkMemberContext
 }
diff --git a/sh/sh_binary.go b/sh/sh_binary.go
index c921ca6..d2eede6 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -474,16 +474,18 @@
 	}}
 }
 
-func InitShBinaryModule(s *ShBinary) {
+func initShBinaryModule(s *ShBinary, useBazel bool) {
 	s.AddProperties(&s.properties)
-	android.InitBazelModule(s)
+	if useBazel {
+		android.InitBazelModule(s)
+	}
 }
 
 // sh_binary is for a shell script or batch file to be installed as an
 // executable binary to <partition>/bin.
 func ShBinaryFactory() android.Module {
 	module := &ShBinary{}
-	InitShBinaryModule(module)
+	initShBinaryModule(module, true)
 	android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibFirst)
 	return module
 }
@@ -492,7 +494,7 @@
 // to $(HOST_OUT)/bin.
 func ShBinaryHostFactory() android.Module {
 	module := &ShBinary{}
-	InitShBinaryModule(module)
+	initShBinaryModule(module, true)
 	android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
 	return module
 }
@@ -500,7 +502,7 @@
 // sh_test defines a shell script based test module.
 func ShTestFactory() android.Module {
 	module := &ShTest{}
-	InitShBinaryModule(&module.ShBinary)
+	initShBinaryModule(&module.ShBinary, false)
 	module.AddProperties(&module.testProperties)
 
 	android.InitAndroidArchModule(module, android.HostAndDeviceSupported, android.MultilibFirst)
@@ -510,7 +512,7 @@
 // sh_test_host defines a shell script based test module that runs on a host.
 func ShTestHostFactory() android.Module {
 	module := &ShTest{}
-	InitShBinaryModule(&module.ShBinary)
+	initShBinaryModule(&module.ShBinary, false)
 	module.AddProperties(&module.testProperties)
 	// Default sh_test_host to unit_tests = true
 	if module.testProperties.Test_options.Unit_test == nil {
diff --git a/shared/env.go b/shared/env.go
index b7d3baf..75190cc 100644
--- a/shared/env.go
+++ b/shared/env.go
@@ -55,20 +55,23 @@
 	return data, nil
 }
 
-// Reads and deserializes a Soong environment file located at the given file path to determine its
-// staleness. If any environment variable values have changed, it prints them out and returns true.
+// Reads and deserializes a Soong environment file located at the given file
+// path to determine its staleness. If any environment variable values have
+// changed, it prints and returns changed environment variable values and
+// returns true.
 // Failing to read or parse the file also causes it to return true.
-func StaleEnvFile(filepath string, getenv func(string) string) (bool, error) {
+func StaleEnvFile(filepath string, getenv func(string) string) (isStale bool,
+	changedEnvironmentVariable []string, err error) {
 	data, err := ioutil.ReadFile(filepath)
 	if err != nil {
-		return true, err
+		return true, nil, err
 	}
 
 	var contents envFileData
 
 	err = json.Unmarshal(data, &contents)
 	if err != nil {
-		return true, err
+		return true, nil, err
 	}
 
 	var changed []string
@@ -78,6 +81,7 @@
 		cur := getenv(key)
 		if old != cur {
 			changed = append(changed, fmt.Sprintf("%s (%q -> %q)", key, old, cur))
+			changedEnvironmentVariable = append(changedEnvironmentVariable, key)
 		}
 	}
 
@@ -86,10 +90,10 @@
 		for _, s := range changed {
 			fmt.Printf("   %s\n", s)
 		}
-		return true, nil
+		return true, changedEnvironmentVariable, nil
 	}
 
-	return false, nil
+	return false, nil, nil
 }
 
 // Deserializes and environment serialized by EnvFileContents() and returns it
diff --git a/snapshot/host_fake_snapshot.go b/snapshot/host_fake_snapshot.go
index b04657d..c4cfbb5 100644
--- a/snapshot/host_fake_snapshot.go
+++ b/snapshot/host_fake_snapshot.go
@@ -75,7 +75,7 @@
 }
 
 func registerHostSnapshotComponents(ctx android.RegistrationContext) {
-	ctx.RegisterSingletonType("host-fake-snapshot", HostToolsFakeAndroidSingleton)
+	ctx.RegisterParallelSingletonType("host-fake-snapshot", HostToolsFakeAndroidSingleton)
 }
 
 type hostFakeSingleton struct {
diff --git a/snapshot/host_snapshot.go b/snapshot/host_snapshot.go
index 9793218..edcc163 100644
--- a/snapshot/host_snapshot.go
+++ b/snapshot/host_snapshot.go
@@ -96,6 +96,7 @@
 	var jsonData []SnapshotJsonFlags
 	var metaPaths android.Paths
 
+	installedNotices := make(map[string]bool)
 	metaZipFile := android.PathForModuleOut(ctx, fileName).OutputPath
 
 	// Create JSON file based on the direct dependencies
@@ -104,12 +105,14 @@
 		if desc != nil {
 			jsonData = append(jsonData, *desc)
 		}
-		if len(dep.EffectiveLicenseFiles()) > 0 {
-			noticeFile := android.PathForModuleOut(ctx, "NOTICE_FILES", dep.Name()+".txt").OutputPath
-			android.CatFileRule(ctx, dep.EffectiveLicenseFiles(), noticeFile)
-			metaPaths = append(metaPaths, noticeFile)
+		for _, notice := range dep.EffectiveLicenseFiles() {
+			if _, ok := installedNotices[notice.String()]; !ok {
+				installedNotices[notice.String()] = true
+				noticeOut := android.PathForModuleOut(ctx, "NOTICE_FILES", notice.String()).OutputPath
+				CopyFileToOutputPathRule(pctx, ctx, notice, noticeOut)
+				metaPaths = append(metaPaths, noticeOut)
+			}
 		}
-
 	})
 	// Sort notice paths and json data for repeatble build
 	sort.Slice(jsonData, func(i, j int) bool {
@@ -220,8 +223,7 @@
 	}
 
 	if path.Valid() && path.String() != "" {
-		return &SnapshotJsonFlags{
-			ModuleName:          m.Name(),
+		props := &SnapshotJsonFlags{
 			ModuleStemName:      moduleStem,
 			Filename:            path.String(),
 			Required:            append(m.HostRequiredModuleNames(), m.RequiredModuleNames()...),
@@ -229,6 +231,8 @@
 			RustProcMacro:       procMacro,
 			CrateName:           crateName,
 		}
+		props.InitBaseSnapshotProps(m)
+		return props
 	}
 	return nil
 }
diff --git a/snapshot/recovery_snapshot.go b/snapshot/recovery_snapshot.go
index ac002be..8ff59cb 100644
--- a/snapshot/recovery_snapshot.go
+++ b/snapshot/recovery_snapshot.go
@@ -68,7 +68,7 @@
 type RecoverySnapshotImage struct{}
 
 func (RecoverySnapshotImage) Init(ctx android.RegistrationContext) {
-	ctx.RegisterSingletonType("recovery-snapshot", RecoverySnapshotSingleton)
+	ctx.RegisterParallelSingletonType("recovery-snapshot", RecoverySnapshotSingleton)
 }
 
 func (RecoverySnapshotImage) RegisterAdditionalModule(ctx android.RegistrationContext, name string, factory android.ModuleFactory) {
diff --git a/snapshot/snapshot.go b/snapshot/snapshot.go
index 206ecc9..c95a537 100644
--- a/snapshot/snapshot.go
+++ b/snapshot/snapshot.go
@@ -26,6 +26,10 @@
 
 var pctx = android.NewPackageContext("android/soong/snapshot")
 
+func init() {
+	pctx.Import("android/soong/android")
+}
+
 type SnapshotSingleton struct {
 	// Name, e.g., "vendor", "recovery", "ramdisk".
 	name string
@@ -48,8 +52,18 @@
 	Fake bool
 }
 
+// The output files to be included in the snapshot.
+type SnapshotPaths struct {
+	// All files to be included in the snapshot
+	OutputFiles android.Paths
+
+	// Notice files of the snapshot output files
+	NoticeFiles android.Paths
+}
+
 // Interface of function to capture snapshot from each module
-type GenerateSnapshotAction func(snapshot SnapshotSingleton, ctx android.SingletonContext, snapshotArchDir string) android.Paths
+// Returns snapshot ouputs and notice files.
+type GenerateSnapshotAction func(snapshot SnapshotSingleton, ctx android.SingletonContext, snapshotArchDir string) SnapshotPaths
 
 var snapshotActionList []GenerateSnapshotAction
 
@@ -74,9 +88,19 @@
 		snapshotDir = filepath.Join("fake", snapshotDir)
 	}
 	snapshotArchDir := filepath.Join(snapshotDir, ctx.DeviceConfig().DeviceArch())
+	noticeDir := filepath.Join(snapshotArchDir, "NOTICE_FILES")
+	installedNotices := make(map[string]bool)
 
 	for _, f := range snapshotActionList {
-		snapshotOutputs = append(snapshotOutputs, f(*c, ctx, snapshotArchDir)...)
+		snapshotPaths := f(*c, ctx, snapshotArchDir)
+		snapshotOutputs = append(snapshotOutputs, snapshotPaths.OutputFiles...)
+		for _, notice := range snapshotPaths.NoticeFiles {
+			if _, ok := installedNotices[notice.String()]; !ok {
+				installedNotices[notice.String()] = true
+				snapshotOutputs = append(snapshotOutputs, CopyFileRule(
+					pctx, ctx, notice, filepath.Join(noticeDir, notice.String())))
+			}
+		}
 	}
 
 	// All artifacts are ready. Sort them to normalize ninja and then zip.
diff --git a/snapshot/snapshot_base.go b/snapshot/snapshot_base.go
index 809ca3d..fb4ee0c 100644
--- a/snapshot/snapshot_base.go
+++ b/snapshot/snapshot_base.go
@@ -120,4 +120,19 @@
 	// dependencies
 	Required  []string `json:",omitempty"`
 	Overrides []string `json:",omitempty"`
+
+	// license information
+	LicenseKinds []string `json:",omitempty"`
+	LicenseTexts []string `json:",omitempty"`
+}
+
+func (prop *SnapshotJsonFlags) InitBaseSnapshotPropsWithName(m android.Module, name string) {
+	prop.ModuleName = name
+
+	prop.LicenseKinds = m.EffectiveLicenseKinds()
+	prop.LicenseTexts = m.EffectiveLicenseFiles().Strings()
+}
+
+func (prop *SnapshotJsonFlags) InitBaseSnapshotProps(m android.Module) {
+	prop.InitBaseSnapshotPropsWithName(m, m.Name())
 }
diff --git a/snapshot/util.go b/snapshot/util.go
index 806ac90..c87c508 100644
--- a/snapshot/util.go
+++ b/snapshot/util.go
@@ -21,17 +21,25 @@
 	return outPath
 }
 
-func CopyFileRule(pctx android.PackageContext, ctx android.SingletonContext, path android.Path, out string) android.OutputPath {
-	outPath := android.PathForOutput(ctx, out)
+type buildContext interface {
+	Build(pctx android.PackageContext, params android.BuildParams)
+}
+
+func CopyFileToOutputPathRule(pctx android.PackageContext, ctx buildContext, path android.Path, outPath android.OutputPath) {
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        android.Cp,
 		Input:       path,
 		Output:      outPath,
-		Description: "copy " + path.String() + " -> " + out,
+		Description: "copy " + path.String() + " -> " + outPath.String(),
 		Args: map[string]string{
-			"cpFlags": "-f -L",
+			"cpFlags": "-L",
 		},
 	})
+}
+
+func CopyFileRule(pctx android.PackageContext, ctx android.SingletonContext, path android.Path, out string) android.OutputPath {
+	outPath := android.PathForOutput(ctx, out)
+	CopyFileToOutputPathRule(pctx, ctx, path, outPath)
 	return outPath
 }
 
diff --git a/snapshot/vendor_snapshot.go b/snapshot/vendor_snapshot.go
index 8f7b8c2..4484c85 100644
--- a/snapshot/vendor_snapshot.go
+++ b/snapshot/vendor_snapshot.go
@@ -78,8 +78,8 @@
 type VendorSnapshotImage struct{}
 
 func (VendorSnapshotImage) Init(ctx android.RegistrationContext) {
-	ctx.RegisterSingletonType("vendor-snapshot", VendorSnapshotSingleton)
-	ctx.RegisterSingletonType("vendor-fake-snapshot", VendorFakeSnapshotSingleton)
+	ctx.RegisterParallelSingletonType("vendor-snapshot", VendorSnapshotSingleton)
+	ctx.RegisterParallelSingletonType("vendor-fake-snapshot", VendorFakeSnapshotSingleton)
 }
 
 func (VendorSnapshotImage) RegisterAdditionalModule(ctx android.RegistrationContext, name string, factory android.ModuleFactory) {
diff --git a/starlark_fmt/format.go b/starlark_fmt/format.go
index a97f71b..4209507 100644
--- a/starlark_fmt/format.go
+++ b/starlark_fmt/format.go
@@ -99,6 +99,16 @@
 	return PrintDict(valDict, indentLevel)
 }
 
+// PrintStringStringDict returns a Starlark-compatible string formatted as dictionary with
+// string keys and string values.
+func PrintStringStringDict(dict map[string]string, indentLevel int) string {
+	valDict := make(map[string]string, len(dict))
+	for k, v := range dict {
+		valDict[k] = fmt.Sprintf(`"%s"`, v)
+	}
+	return PrintDict(valDict, indentLevel)
+}
+
 // PrintDict returns a starlark-compatible string containing a dictionary with string keys and
 // values printed with no additional formatting.
 func PrintDict(dict map[string]string, indentLevel int) string {
diff --git a/starlark_import/Android.bp b/starlark_import/Android.bp
new file mode 100644
index 0000000..b43217b
--- /dev/null
+++ b/starlark_import/Android.bp
@@ -0,0 +1,36 @@
+// Copyright 2023 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: "soong-starlark",
+    pkgPath: "android/soong/starlark_import",
+    srcs: [
+        "starlark_import.go",
+        "unmarshal.go",
+    ],
+    testSrcs: [
+        "starlark_import_test.go",
+        "unmarshal_test.go",
+    ],
+    deps: [
+        "go-starlark-starlark",
+        "go-starlark-starlarkstruct",
+        "go-starlark-starlarkjson",
+        "go-starlark-starlarktest",
+    ],
+}
diff --git a/starlark_import/README.md b/starlark_import/README.md
new file mode 100644
index 0000000..e444759
--- /dev/null
+++ b/starlark_import/README.md
@@ -0,0 +1,14 @@
+# starlark_import package
+
+This allows soong to read constant information from starlark files. At package initialization
+time, soong will read `build/bazel/constants_exported_to_soong.bzl`, and then make the
+variables from that file available via `starlark_import.GetStarlarkValue()`. So to import
+a new variable, it must be added to `constants_exported_to_soong.bzl` and then it can
+be accessed by name.
+
+Only constant information can be read, since this is not a full bazel execution but a
+standalone starlark interpreter. This means you can't use bazel contructs like `rule`,
+`provider`, `select`, `glob`, etc.
+
+All starlark files that were loaded must be added as ninja deps that cause soong to rerun.
+The loaded files can be retrieved via `starlark_import.GetNinjaDeps()`.
diff --git a/starlark_import/starlark_import.go b/starlark_import/starlark_import.go
new file mode 100644
index 0000000..ebe4247
--- /dev/null
+++ b/starlark_import/starlark_import.go
@@ -0,0 +1,306 @@
+// Copyright 2023 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 starlark_import
+
+import (
+	"fmt"
+	"os"
+	"path/filepath"
+	"sort"
+	"strings"
+	"sync"
+	"time"
+
+	"go.starlark.net/starlark"
+	"go.starlark.net/starlarkjson"
+	"go.starlark.net/starlarkstruct"
+)
+
+func init() {
+	go func() {
+		startTime := time.Now()
+		v, d, err := runStarlarkFile("//build/bazel/constants_exported_to_soong.bzl")
+		endTime := time.Now()
+		//fmt.Fprintf(os.Stderr, "starlark run time: %s\n", endTime.Sub(startTime).String())
+		globalResult.Set(starlarkResult{
+			values:    v,
+			ninjaDeps: d,
+			err:       err,
+			startTime: startTime,
+			endTime:   endTime,
+		})
+	}()
+}
+
+type starlarkResult struct {
+	values    starlark.StringDict
+	ninjaDeps []string
+	err       error
+	startTime time.Time
+	endTime   time.Time
+}
+
+// setOnce wraps a value and exposes Set() and Get() accessors for it.
+// The Get() calls will block until a Set() has been called.
+// A second call to Set() will panic.
+// setOnce must be created using newSetOnce()
+type setOnce[T any] struct {
+	value T
+	lock  sync.Mutex
+	wg    sync.WaitGroup
+	isSet bool
+}
+
+func (o *setOnce[T]) Set(value T) {
+	o.lock.Lock()
+	defer o.lock.Unlock()
+	if o.isSet {
+		panic("Value already set")
+	}
+
+	o.value = value
+	o.isSet = true
+	o.wg.Done()
+}
+
+func (o *setOnce[T]) Get() T {
+	if !o.isSet {
+		o.wg.Wait()
+	}
+	return o.value
+}
+
+func newSetOnce[T any]() *setOnce[T] {
+	result := &setOnce[T]{}
+	result.wg.Add(1)
+	return result
+}
+
+var globalResult = newSetOnce[starlarkResult]()
+
+func GetStarlarkValue[T any](key string) (T, error) {
+	result := globalResult.Get()
+	if result.err != nil {
+		var zero T
+		return zero, result.err
+	}
+	if !result.values.Has(key) {
+		var zero T
+		return zero, fmt.Errorf("a starlark variable by that name wasn't found, did you update //build/bazel/constants_exported_to_soong.bzl?")
+	}
+	return Unmarshal[T](result.values[key])
+}
+
+func GetNinjaDeps() ([]string, error) {
+	result := globalResult.Get()
+	if result.err != nil {
+		return nil, result.err
+	}
+	return result.ninjaDeps, nil
+}
+
+func getTopDir() (string, error) {
+	// It's hard to communicate the top dir to this package in any other way than reading the
+	// arguments directly, because we need to know this at package initialization time. Many
+	// soong constants that we'd like to read from starlark are initialized during package
+	// initialization.
+	for i, arg := range os.Args {
+		if arg == "--top" {
+			if i < len(os.Args)-1 && os.Args[i+1] != "" {
+				return os.Args[i+1], nil
+			}
+		}
+	}
+
+	// When running tests, --top is not passed. Instead, search for the top dir manually
+	cwd, err := os.Getwd()
+	if err != nil {
+		return "", err
+	}
+	for cwd != "/" {
+		if _, err := os.Stat(filepath.Join(cwd, "build/soong/soong_ui.bash")); err == nil {
+			return cwd, nil
+		}
+		cwd = filepath.Dir(cwd)
+	}
+	return "", fmt.Errorf("could not find top dir")
+}
+
+const callerDirKey = "callerDir"
+
+type modentry struct {
+	globals starlark.StringDict
+	err     error
+}
+
+func unsupportedMethod(t *starlark.Thread, fn *starlark.Builtin, _ starlark.Tuple, _ []starlark.Tuple) (starlark.Value, error) {
+	return nil, fmt.Errorf("%sthis file is read by soong, and must therefore be pure starlark and include only constant information. %q is not allowed", t.CallStack().String(), fn.Name())
+}
+
+var builtins = starlark.StringDict{
+	"aspect":     starlark.NewBuiltin("aspect", unsupportedMethod),
+	"glob":       starlark.NewBuiltin("glob", unsupportedMethod),
+	"json":       starlarkjson.Module,
+	"provider":   starlark.NewBuiltin("provider", unsupportedMethod),
+	"rule":       starlark.NewBuiltin("rule", unsupportedMethod),
+	"struct":     starlark.NewBuiltin("struct", starlarkstruct.Make),
+	"select":     starlark.NewBuiltin("select", unsupportedMethod),
+	"transition": starlark.NewBuiltin("transition", unsupportedMethod),
+}
+
+// Takes a module name (the first argument to the load() function) and returns the path
+// it's trying to load, stripping out leading //, and handling leading :s.
+func cleanModuleName(moduleName string, callerDir string) (string, error) {
+	if strings.Count(moduleName, ":") > 1 {
+		return "", fmt.Errorf("at most 1 colon must be present in starlark path: %s", moduleName)
+	}
+
+	// We don't have full support for external repositories, but at least support skylib's dicts.
+	if moduleName == "@bazel_skylib//lib:dicts.bzl" {
+		return "external/bazel-skylib/lib/dicts.bzl", nil
+	}
+
+	localLoad := false
+	if strings.HasPrefix(moduleName, "@//") {
+		moduleName = moduleName[3:]
+	} else if strings.HasPrefix(moduleName, "//") {
+		moduleName = moduleName[2:]
+	} else if strings.HasPrefix(moduleName, ":") {
+		moduleName = moduleName[1:]
+		localLoad = true
+	} else {
+		return "", fmt.Errorf("load path must start with // or :")
+	}
+
+	if ix := strings.LastIndex(moduleName, ":"); ix >= 0 {
+		moduleName = moduleName[:ix] + string(os.PathSeparator) + moduleName[ix+1:]
+	}
+
+	if filepath.Clean(moduleName) != moduleName {
+		return "", fmt.Errorf("load path must be clean, found: %s, expected: %s", moduleName, filepath.Clean(moduleName))
+	}
+	if strings.HasPrefix(moduleName, "../") {
+		return "", fmt.Errorf("load path must not start with ../: %s", moduleName)
+	}
+	if strings.HasPrefix(moduleName, "/") {
+		return "", fmt.Errorf("load path starts with /, use // for a absolute path: %s", moduleName)
+	}
+
+	if localLoad {
+		return filepath.Join(callerDir, moduleName), nil
+	}
+
+	return moduleName, nil
+}
+
+// loader implements load statement. The format of the loaded module URI is
+//
+//	[//path]:base
+//
+// The file path is $ROOT/path/base if path is present, <caller_dir>/base otherwise.
+func loader(thread *starlark.Thread, module string, topDir string, moduleCache map[string]*modentry, moduleCacheLock *sync.Mutex, filesystem map[string]string) (starlark.StringDict, error) {
+	modulePath, err := cleanModuleName(module, thread.Local(callerDirKey).(string))
+	if err != nil {
+		return nil, err
+	}
+	moduleCacheLock.Lock()
+	e, ok := moduleCache[modulePath]
+	if e == nil {
+		if ok {
+			moduleCacheLock.Unlock()
+			return nil, fmt.Errorf("cycle in load graph")
+		}
+
+		// Add a placeholder to indicate "load in progress".
+		moduleCache[modulePath] = nil
+		moduleCacheLock.Unlock()
+
+		childThread := &starlark.Thread{Name: "exec " + module, Load: thread.Load}
+
+		// Cheating for the sake of testing:
+		// propagate starlarktest's Reporter key, otherwise testing
+		// the load function may cause panic in starlarktest code.
+		const testReporterKey = "Reporter"
+		if v := thread.Local(testReporterKey); v != nil {
+			childThread.SetLocal(testReporterKey, v)
+		}
+
+		childThread.SetLocal(callerDirKey, filepath.Dir(modulePath))
+
+		if filesystem != nil {
+			globals, err := starlark.ExecFile(childThread, filepath.Join(topDir, modulePath), filesystem[modulePath], builtins)
+			e = &modentry{globals, err}
+		} else {
+			globals, err := starlark.ExecFile(childThread, filepath.Join(topDir, modulePath), nil, builtins)
+			e = &modentry{globals, err}
+		}
+
+		// Update the cache.
+		moduleCacheLock.Lock()
+		moduleCache[modulePath] = e
+	}
+	moduleCacheLock.Unlock()
+	return e.globals, e.err
+}
+
+// Run runs the given starlark file and returns its global variables and a list of all starlark
+// files that were loaded. The top dir for starlark's // is found via getTopDir().
+func runStarlarkFile(filename string) (starlark.StringDict, []string, error) {
+	topDir, err := getTopDir()
+	if err != nil {
+		return nil, nil, err
+	}
+	return runStarlarkFileWithFilesystem(filename, topDir, nil)
+}
+
+func runStarlarkFileWithFilesystem(filename string, topDir string, filesystem map[string]string) (starlark.StringDict, []string, error) {
+	if !strings.HasPrefix(filename, "//") && !strings.HasPrefix(filename, ":") {
+		filename = "//" + filename
+	}
+	filename, err := cleanModuleName(filename, "")
+	if err != nil {
+		return nil, nil, err
+	}
+	moduleCache := make(map[string]*modentry)
+	moduleCache[filename] = nil
+	moduleCacheLock := &sync.Mutex{}
+	mainThread := &starlark.Thread{
+		Name: "main",
+		Print: func(_ *starlark.Thread, msg string) {
+			// Ignore prints
+		},
+		Load: func(thread *starlark.Thread, module string) (starlark.StringDict, error) {
+			return loader(thread, module, topDir, moduleCache, moduleCacheLock, filesystem)
+		},
+	}
+	mainThread.SetLocal(callerDirKey, filepath.Dir(filename))
+
+	var result starlark.StringDict
+	if filesystem != nil {
+		result, err = starlark.ExecFile(mainThread, filepath.Join(topDir, filename), filesystem[filename], builtins)
+	} else {
+		result, err = starlark.ExecFile(mainThread, filepath.Join(topDir, filename), nil, builtins)
+	}
+	return result, sortedStringKeys(moduleCache), err
+}
+
+func sortedStringKeys(m map[string]*modentry) []string {
+	s := make([]string, 0, len(m))
+	for k := range m {
+		s = append(s, k)
+	}
+	sort.Strings(s)
+	return s
+}
diff --git a/starlark_import/starlark_import_test.go b/starlark_import/starlark_import_test.go
new file mode 100644
index 0000000..8a58e3b
--- /dev/null
+++ b/starlark_import/starlark_import_test.go
@@ -0,0 +1,122 @@
+// Copyright 2023 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 starlark_import
+
+import (
+	"strings"
+	"testing"
+
+	"go.starlark.net/starlark"
+)
+
+func TestBasic(t *testing.T) {
+	globals, _, err := runStarlarkFileWithFilesystem("a.bzl", "", map[string]string{
+		"a.bzl": `
+my_string = "hello, world!"
+`})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	if globals["my_string"].(starlark.String) != "hello, world!" {
+		t.Errorf("Expected %q, got %q", "hello, world!", globals["my_string"].String())
+	}
+}
+
+func TestLoad(t *testing.T) {
+	globals, _, err := runStarlarkFileWithFilesystem("a.bzl", "", map[string]string{
+		"a.bzl": `
+load("//b.bzl", _b_string = "my_string")
+my_string = "hello, " + _b_string
+`,
+		"b.bzl": `
+my_string = "world!"
+`})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	if globals["my_string"].(starlark.String) != "hello, world!" {
+		t.Errorf("Expected %q, got %q", "hello, world!", globals["my_string"].String())
+	}
+}
+
+func TestLoadRelative(t *testing.T) {
+	globals, ninjaDeps, err := runStarlarkFileWithFilesystem("a.bzl", "", map[string]string{
+		"a.bzl": `
+load(":b.bzl", _b_string = "my_string")
+load("//foo/c.bzl", _c_string = "my_string")
+my_string = "hello, " + _b_string
+c_string = _c_string
+`,
+		"b.bzl": `
+my_string = "world!"
+`,
+		"foo/c.bzl": `
+load(":d.bzl", _d_string = "my_string")
+my_string = "hello, " + _d_string
+`,
+		"foo/d.bzl": `
+my_string = "world!"
+`})
+	if err != nil {
+		t.Error(err)
+		return
+	}
+
+	if globals["my_string"].(starlark.String) != "hello, world!" {
+		t.Errorf("Expected %q, got %q", "hello, world!", globals["my_string"].String())
+	}
+
+	expectedNinjaDeps := []string{
+		"a.bzl",
+		"b.bzl",
+		"foo/c.bzl",
+		"foo/d.bzl",
+	}
+	if !slicesEqual(ninjaDeps, expectedNinjaDeps) {
+		t.Errorf("Expected %v ninja deps, got %v", expectedNinjaDeps, ninjaDeps)
+	}
+}
+
+func TestLoadCycle(t *testing.T) {
+	_, _, err := runStarlarkFileWithFilesystem("a.bzl", "", map[string]string{
+		"a.bzl": `
+load(":b.bzl", _b_string = "my_string")
+my_string = "hello, " + _b_string
+`,
+		"b.bzl": `
+load(":a.bzl", _a_string = "my_string")
+my_string = "hello, " + _a_string
+`})
+	if err == nil || !strings.Contains(err.Error(), "cycle in load graph") {
+		t.Errorf("Expected cycle in load graph, got: %v", err)
+		return
+	}
+}
+
+func slicesEqual[T comparable](a []T, b []T) bool {
+	if len(a) != len(b) {
+		return false
+	}
+	for i := range a {
+		if a[i] != b[i] {
+			return false
+		}
+	}
+	return true
+}
diff --git a/starlark_import/unmarshal.go b/starlark_import/unmarshal.go
new file mode 100644
index 0000000..1b54437
--- /dev/null
+++ b/starlark_import/unmarshal.go
@@ -0,0 +1,288 @@
+// Copyright 2023 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 starlark_import
+
+import (
+	"fmt"
+	"math"
+	"reflect"
+	"unsafe"
+
+	"go.starlark.net/starlark"
+	"go.starlark.net/starlarkstruct"
+)
+
+func Unmarshal[T any](value starlark.Value) (T, error) {
+	var zero T
+	x, err := UnmarshalReflect(value, reflect.TypeOf(zero))
+	return x.Interface().(T), err
+}
+
+func UnmarshalReflect(value starlark.Value, ty reflect.Type) (reflect.Value, error) {
+	zero := reflect.Zero(ty)
+	var result reflect.Value
+	if ty.Kind() == reflect.Interface {
+		var err error
+		ty, err = typeOfStarlarkValue(value)
+		if err != nil {
+			return zero, err
+		}
+	}
+	if ty.Kind() == reflect.Map {
+		result = reflect.MakeMap(ty)
+	} else {
+		result = reflect.Indirect(reflect.New(ty))
+	}
+
+	switch v := value.(type) {
+	case starlark.String:
+		if result.Type().Kind() != reflect.String {
+			return zero, fmt.Errorf("starlark type was %s, but %s requested", v.Type(), result.Type().Kind().String())
+		}
+		result.SetString(v.GoString())
+	case starlark.Int:
+		signedValue, signedOk := v.Int64()
+		unsignedValue, unsignedOk := v.Uint64()
+		switch result.Type().Kind() {
+		case reflect.Int64:
+			if !signedOk {
+				return zero, fmt.Errorf("starlark int didn't fit in go int64")
+			}
+			result.SetInt(signedValue)
+		case reflect.Int32:
+			if !signedOk || signedValue > math.MaxInt32 || signedValue < math.MinInt32 {
+				return zero, fmt.Errorf("starlark int didn't fit in go int32")
+			}
+			result.SetInt(signedValue)
+		case reflect.Int16:
+			if !signedOk || signedValue > math.MaxInt16 || signedValue < math.MinInt16 {
+				return zero, fmt.Errorf("starlark int didn't fit in go int16")
+			}
+			result.SetInt(signedValue)
+		case reflect.Int8:
+			if !signedOk || signedValue > math.MaxInt8 || signedValue < math.MinInt8 {
+				return zero, fmt.Errorf("starlark int didn't fit in go int8")
+			}
+			result.SetInt(signedValue)
+		case reflect.Int:
+			if !signedOk || signedValue > math.MaxInt || signedValue < math.MinInt {
+				return zero, fmt.Errorf("starlark int didn't fit in go int")
+			}
+			result.SetInt(signedValue)
+		case reflect.Uint64:
+			if !unsignedOk {
+				return zero, fmt.Errorf("starlark int didn't fit in go uint64")
+			}
+			result.SetUint(unsignedValue)
+		case reflect.Uint32:
+			if !unsignedOk || unsignedValue > math.MaxUint32 {
+				return zero, fmt.Errorf("starlark int didn't fit in go uint32")
+			}
+			result.SetUint(unsignedValue)
+		case reflect.Uint16:
+			if !unsignedOk || unsignedValue > math.MaxUint16 {
+				return zero, fmt.Errorf("starlark int didn't fit in go uint16")
+			}
+			result.SetUint(unsignedValue)
+		case reflect.Uint8:
+			if !unsignedOk || unsignedValue > math.MaxUint8 {
+				return zero, fmt.Errorf("starlark int didn't fit in go uint8")
+			}
+			result.SetUint(unsignedValue)
+		case reflect.Uint:
+			if !unsignedOk || unsignedValue > math.MaxUint {
+				return zero, fmt.Errorf("starlark int didn't fit in go uint")
+			}
+			result.SetUint(unsignedValue)
+		default:
+			return zero, fmt.Errorf("starlark type was %s, but %s requested", v.Type(), result.Type().Kind().String())
+		}
+	case starlark.Float:
+		f := float64(v)
+		switch result.Type().Kind() {
+		case reflect.Float64:
+			result.SetFloat(f)
+		case reflect.Float32:
+			if f > math.MaxFloat32 || f < -math.MaxFloat32 {
+				return zero, fmt.Errorf("starlark float didn't fit in go float32")
+			}
+			result.SetFloat(f)
+		default:
+			return zero, fmt.Errorf("starlark type was %s, but %s requested", v.Type(), result.Type().Kind().String())
+		}
+	case starlark.Bool:
+		if result.Type().Kind() != reflect.Bool {
+			return zero, fmt.Errorf("starlark type was %s, but %s requested", v.Type(), result.Type().Kind().String())
+		}
+		result.SetBool(bool(v))
+	case starlark.Tuple:
+		if result.Type().Kind() != reflect.Slice {
+			return zero, fmt.Errorf("starlark type was %s, but %s requested", v.Type(), result.Type().Kind().String())
+		}
+		elemType := result.Type().Elem()
+		// TODO: Add this grow call when we're on go 1.20
+		//result.Grow(v.Len())
+		for i := 0; i < v.Len(); i++ {
+			elem, err := UnmarshalReflect(v.Index(i), elemType)
+			if err != nil {
+				return zero, err
+			}
+			result = reflect.Append(result, elem)
+		}
+	case *starlark.List:
+		if result.Type().Kind() != reflect.Slice {
+			return zero, fmt.Errorf("starlark type was %s, but %s requested", v.Type(), result.Type().Kind().String())
+		}
+		elemType := result.Type().Elem()
+		// TODO: Add this grow call when we're on go 1.20
+		//result.Grow(v.Len())
+		for i := 0; i < v.Len(); i++ {
+			elem, err := UnmarshalReflect(v.Index(i), elemType)
+			if err != nil {
+				return zero, err
+			}
+			result = reflect.Append(result, elem)
+		}
+	case *starlark.Dict:
+		if result.Type().Kind() != reflect.Map {
+			return zero, fmt.Errorf("starlark type was %s, but %s requested", v.Type(), result.Type().Kind().String())
+		}
+		keyType := result.Type().Key()
+		valueType := result.Type().Elem()
+		for _, pair := range v.Items() {
+			key := pair.Index(0)
+			value := pair.Index(1)
+
+			unmarshalledKey, err := UnmarshalReflect(key, keyType)
+			if err != nil {
+				return zero, err
+			}
+			unmarshalledValue, err := UnmarshalReflect(value, valueType)
+			if err != nil {
+				return zero, err
+			}
+
+			result.SetMapIndex(unmarshalledKey, unmarshalledValue)
+		}
+	case *starlarkstruct.Struct:
+		if result.Type().Kind() != reflect.Struct {
+			return zero, fmt.Errorf("starlark type was %s, but %s requested", v.Type(), result.Type().Kind().String())
+		}
+		if result.NumField() != len(v.AttrNames()) {
+			return zero, fmt.Errorf("starlark struct and go struct have different number of fields (%d and %d)", len(v.AttrNames()), result.NumField())
+		}
+		for _, attrName := range v.AttrNames() {
+			attr, err := v.Attr(attrName)
+			if err != nil {
+				return zero, err
+			}
+
+			// TODO(b/279787235): this should probably support tags to rename the field
+			resultField := result.FieldByName(attrName)
+			if resultField == (reflect.Value{}) {
+				return zero, fmt.Errorf("starlark struct had field %s, but requested struct type did not", attrName)
+			}
+			// This hack allows us to change unexported fields
+			resultField = reflect.NewAt(resultField.Type(), unsafe.Pointer(resultField.UnsafeAddr())).Elem()
+			x, err := UnmarshalReflect(attr, resultField.Type())
+			if err != nil {
+				return zero, err
+			}
+			resultField.Set(x)
+		}
+	default:
+		return zero, fmt.Errorf("unimplemented starlark type: %s", value.Type())
+	}
+
+	return result, nil
+}
+
+func typeOfStarlarkValue(value starlark.Value) (reflect.Type, error) {
+	var err error
+	switch v := value.(type) {
+	case starlark.String:
+		return reflect.TypeOf(""), nil
+	case *starlark.List:
+		innerType := reflect.TypeOf("")
+		if v.Len() > 0 {
+			innerType, err = typeOfStarlarkValue(v.Index(0))
+			if err != nil {
+				return nil, err
+			}
+		}
+		for i := 1; i < v.Len(); i++ {
+			innerTypeI, err := typeOfStarlarkValue(v.Index(i))
+			if err != nil {
+				return nil, err
+			}
+			if innerType != innerTypeI {
+				return nil, fmt.Errorf("List must contain elements of entirely the same type, found %v and %v", innerType, innerTypeI)
+			}
+		}
+		return reflect.SliceOf(innerType), nil
+	case *starlark.Dict:
+		keyType := reflect.TypeOf("")
+		valueType := reflect.TypeOf("")
+		keys := v.Keys()
+		if v.Len() > 0 {
+			firstKey := keys[0]
+			keyType, err = typeOfStarlarkValue(firstKey)
+			if err != nil {
+				return nil, err
+			}
+			firstValue, found, err := v.Get(firstKey)
+			if !found {
+				err = fmt.Errorf("value not found")
+			}
+			if err != nil {
+				return nil, err
+			}
+			valueType, err = typeOfStarlarkValue(firstValue)
+			if err != nil {
+				return nil, err
+			}
+		}
+		for _, key := range keys {
+			keyTypeI, err := typeOfStarlarkValue(key)
+			if err != nil {
+				return nil, err
+			}
+			if keyType != keyTypeI {
+				return nil, fmt.Errorf("dict must contain elements of entirely the same type, found %v and %v", keyType, keyTypeI)
+			}
+			value, found, err := v.Get(key)
+			if !found {
+				err = fmt.Errorf("value not found")
+			}
+			if err != nil {
+				return nil, err
+			}
+			valueTypeI, err := typeOfStarlarkValue(value)
+			if valueType.Kind() != reflect.Interface && valueTypeI != valueType {
+				// If we see conflicting value types, change the result value type to an empty interface
+				valueType = reflect.TypeOf([]interface{}{}).Elem()
+			}
+		}
+		return reflect.MapOf(keyType, valueType), nil
+	case starlark.Int:
+		return reflect.TypeOf(0), nil
+	case starlark.Float:
+		return reflect.TypeOf(0.0), nil
+	case starlark.Bool:
+		return reflect.TypeOf(true), nil
+	default:
+		return nil, fmt.Errorf("unimplemented starlark type: %s", value.Type())
+	}
+}
diff --git a/starlark_import/unmarshal_test.go b/starlark_import/unmarshal_test.go
new file mode 100644
index 0000000..ee7a9e3
--- /dev/null
+++ b/starlark_import/unmarshal_test.go
@@ -0,0 +1,133 @@
+// Copyright 2023 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 starlark_import
+
+import (
+	"reflect"
+	"testing"
+
+	"go.starlark.net/starlark"
+)
+
+func createStarlarkValue(t *testing.T, code string) starlark.Value {
+	t.Helper()
+	result, err := starlark.ExecFile(&starlark.Thread{}, "main.bzl", "x = "+code, builtins)
+	if err != nil {
+		panic(err)
+	}
+	return result["x"]
+}
+
+func TestUnmarshallConcreteType(t *testing.T) {
+	x, err := Unmarshal[string](createStarlarkValue(t, `"foo"`))
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	if x != "foo" {
+		t.Errorf(`Expected "foo", got %q`, x)
+	}
+}
+
+func TestUnmarshallConcreteTypeWithInterfaces(t *testing.T) {
+	x, err := Unmarshal[map[string]map[string]interface{}](createStarlarkValue(t,
+		`{"foo": {"foo2": "foo3"}, "bar": {"bar2": ["bar3"]}}`))
+	if err != nil {
+		t.Error(err)
+		return
+	}
+	expected := map[string]map[string]interface{}{
+		"foo": {"foo2": "foo3"},
+		"bar": {"bar2": []string{"bar3"}},
+	}
+	if !reflect.DeepEqual(x, expected) {
+		t.Errorf(`Expected %v, got %v`, expected, x)
+	}
+}
+
+func TestUnmarshall(t *testing.T) {
+	testCases := []struct {
+		input    string
+		expected interface{}
+	}{
+		{
+			input:    `"foo"`,
+			expected: "foo",
+		},
+		{
+			input:    `5`,
+			expected: 5,
+		},
+		{
+			input:    `["foo", "bar"]`,
+			expected: []string{"foo", "bar"},
+		},
+		{
+			input:    `("foo", "bar")`,
+			expected: []string{"foo", "bar"},
+		},
+		{
+			input:    `("foo",5)`,
+			expected: []interface{}{"foo", 5},
+		},
+		{
+			input:    `{"foo": 5, "bar": 10}`,
+			expected: map[string]int{"foo": 5, "bar": 10},
+		},
+		{
+			input:    `{"foo": ["qux"], "bar": []}`,
+			expected: map[string][]string{"foo": {"qux"}, "bar": nil},
+		},
+		{
+			input: `struct(Foo="foo", Bar=5)`,
+			expected: struct {
+				Foo string
+				Bar int
+			}{Foo: "foo", Bar: 5},
+		},
+		{
+			// Unexported fields version of the above
+			input: `struct(foo="foo", bar=5)`,
+			expected: struct {
+				foo string
+				bar int
+			}{foo: "foo", bar: 5},
+		},
+		{
+			input: `{"foo": "foo2", "bar": ["bar2"], "baz": 5, "qux": {"qux2": "qux3"}, "quux": {"quux2": "quux3", "quux4": 5}}`,
+			expected: map[string]interface{}{
+				"foo": "foo2",
+				"bar": []string{"bar2"},
+				"baz": 5,
+				"qux": map[string]string{"qux2": "qux3"},
+				"quux": map[string]interface{}{
+					"quux2": "quux3",
+					"quux4": 5,
+				},
+			},
+		},
+	}
+
+	for _, tc := range testCases {
+		x, err := UnmarshalReflect(createStarlarkValue(t, tc.input), reflect.TypeOf(tc.expected))
+		if err != nil {
+			t.Error(err)
+			continue
+		}
+		if !reflect.DeepEqual(x.Interface(), tc.expected) {
+			t.Errorf(`Expected %#v, got %#v`, tc.expected, x.Interface())
+		}
+	}
+}
diff --git a/tests/apex_comparison_tests.sh b/tests/apex_comparison_tests.sh
index e350323..8893060 100755
--- a/tests/apex_comparison_tests.sh
+++ b/tests/apex_comparison_tests.sh
@@ -29,7 +29,7 @@
 # Test Setup
 ############
 
-OUTPUT_DIR="$(mktemp -d tmp.XXXXXX)"
+OUTPUT_DIR="$(mktemp -d $(pwd)/tmp.XXXXXX)"
 SOONG_OUTPUT_DIR="$OUTPUT_DIR/soong"
 BAZEL_OUTPUT_DIR="$OUTPUT_DIR/bazel"
 
@@ -45,6 +45,12 @@
   call_bazel clean
   rm -rf "${OUTPUT_DIR}"
 }
+
+function deapexer() {
+  DEBUGFS_PATH="$(realpath $(call_bazel cquery --config=bp2build --config=linux_x86_64 --config=ci --output=files //external/e2fsprogs/debugfs))"
+  call_bazel run --config=bp2build //system/apex/tools:deapexer -- --debugfs_path=$DEBUGFS_PATH $@
+}
+
 trap cleanup EXIT
 
 ###########
@@ -72,10 +78,7 @@
 BAZEL_MINIMAL="$(realpath $(call_bazel cquery --config=bp2build --config=android --config=ci --output=files //build/bazel/examples/apex/minimal:build.bazel.examples.apex.minimal))"
 
 # # Build debugfs separately, as it's not a dep of apexer, but needs to be an explicit arg.
-call_bazel build --config=bp2build --config=linux_x86_64 //external/e2fsprogs/debugfs //system/apex/tools:deapexer
-DEBUGFS_PATH="$(realpath $(call_bazel cquery --config=bp2build --config=linux_x86_64 --config=ci --output=files //external/e2fsprogs/debugfs))"
-DEAPEXER="bazel-bin/system/apex/tools/deapexer"
-DEAPEXER="$DEAPEXER --debugfs_path=$DEBUGFS_PATH"
+call_bazel build --config=bp2build --config=linux_x86_64 //external/e2fsprogs/debugfs
 
 #######
 # Tests
@@ -91,8 +94,8 @@
   local SOONG_LIST="$OUTPUT_DIR/soong.list"
   local BAZEL_LIST="$OUTPUT_DIR/bazel.list"
 
-  $DEAPEXER list "$SOONG_APEX" > "$SOONG_LIST"
-  $DEAPEXER list "$BAZEL_APEX" > "$BAZEL_LIST"
+  deapexer list "$SOONG_APEX" > "$SOONG_LIST"
+  deapexer list "$BAZEL_APEX" > "$BAZEL_LIST"
 
   if cmp -s "$SOONG_LIST" "$BAZEL_LIST"
   then
diff --git a/tests/bootstrap_test.sh b/tests/bootstrap_test.sh
index fda5ca0..5fc05f8 100755
--- a/tests/bootstrap_test.sh
+++ b/tests/bootstrap_test.sh
@@ -207,8 +207,8 @@
 function test_soong_build_rerun_iff_environment_changes() {
   setup
 
-  mkdir -p cherry
-  cat > cherry/Android.bp <<'EOF'
+  mkdir -p build/soong/cherry
+  cat > build/soong/cherry/Android.bp <<'EOF'
 bootstrap_go_package {
   name: "cherry",
   pkgPath: "android/soong/cherry",
@@ -224,7 +224,7 @@
 }
 EOF
 
-  cat > cherry/cherry.go <<'EOF'
+  cat > build/soong/cherry/cherry.go <<'EOF'
 package cherry
 
 import (
@@ -317,8 +317,8 @@
   run_soong
   local -r mtime1=$(stat -c "%y" out/soong/build.ninja)
 
-  mkdir -p a
-  cat > a/Android.bp <<'EOF'
+  mkdir -p vendor/foo/picard
+  cat > vendor/foo/picard/Android.bp <<'EOF'
 bootstrap_go_package {
   name: "picard-soong-rules",
   pkgPath: "android/soong/picard",
@@ -334,7 +334,7 @@
 }
 EOF
 
-  cat > a/picard.go <<'EOF'
+  cat > vendor/foo/picard/picard.go <<'EOF'
 package picard
 
 import (
@@ -390,11 +390,11 @@
 function test_glob_during_bootstrapping() {
   setup
 
-  mkdir -p a
-  cat > a/Android.bp <<'EOF'
+  mkdir -p build/soong/picard
+  cat > build/soong/picard/Android.bp <<'EOF'
 build=["foo*.bp"]
 EOF
-  cat > a/fooa.bp <<'EOF'
+  cat > build/soong/picard/fooa.bp <<'EOF'
 bootstrap_go_package {
   name: "picard-soong-rules",
   pkgPath: "android/soong/picard",
@@ -410,7 +410,7 @@
 }
 EOF
 
-  cat > a/picard.go <<'EOF'
+  cat > build/soong/picard/picard.go <<'EOF'
 package picard
 
 import (
@@ -459,7 +459,7 @@
 
   grep -q "Make it so" out/soong/build.ninja || fail "Original action not present"
 
-  cat > a/foob.bp <<'EOF'
+  cat > build/soong/picard/foob.bp <<'EOF'
 bootstrap_go_package {
   name: "worf-soong-rules",
   pkgPath: "android/soong/worf",
@@ -476,7 +476,7 @@
 }
 EOF
 
-  cat > a/worf.go <<'EOF'
+  cat > build/soong/picard/worf.go <<'EOF'
 package worf
 
 import "android/soong/picard"
@@ -885,4 +885,37 @@
   fi
 }
 
+# This test verifies that adding a new glob to a blueprint file only
+# causes build.ninja to be regenerated on the *next* build, and *not*
+# the build after. (This is a regression test for a bug where globs
+# resulted in two successive regenerations.)
+function test_new_glob_incrementality {
+  setup
+
+  run_soong nothing
+  local -r mtime1=$(stat -c "%y" out/soong/build.ninja)
+
+  mkdir -p globdefpkg/
+  cat > globdefpkg/Android.bp <<'EOF'
+filegroup {
+  name: "fg_with_glob",
+  srcs: ["*.txt"],
+}
+EOF
+
+  run_soong nothing
+  local -r mtime2=$(stat -c "%y" out/soong/build.ninja)
+
+  if [[ "$mtime1" == "$mtime2" ]]; then
+    fail "Ninja file was not regenerated, despite a new bp file"
+  fi
+
+  run_soong nothing
+  local -r mtime3=$(stat -c "%y" out/soong/build.ninja)
+
+  if [[ "$mtime2" != "$mtime3" ]]; then
+    fail "Ninja file was regenerated despite no previous bp changes"
+  fi
+}
+
 scan_and_run_tests
diff --git a/tests/bp2build_bazel_test.sh b/tests/bp2build_bazel_test.sh
index 68d7f8d..090114b 100755
--- a/tests/bp2build_bazel_test.sh
+++ b/tests/bp2build_bazel_test.sh
@@ -53,6 +53,20 @@
   if [[ "$buildfile_mtime1" != "$buildfile_mtime2" ]]; then
     fail "BUILD.bazel was updated even though contents are same"
   fi
+
+  # Force bp2build to rerun by updating the timestamp of the constants_exported_to_soong.bzl file.
+  touch build/bazel/constants_exported_to_soong.bzl
+
+  run_soong bp2build
+  local -r buildfile_mtime3=$(stat -c "%y" out/soong/bp2build/pkg/BUILD.bazel)
+  local -r marker_mtime3=$(stat -c "%y" out/soong/bp2build_workspace_marker)
+
+  if [[ "$marker_mtime2" == "$marker_mtime3" ]]; then
+    fail "Expected bp2build marker file to change"
+  fi
+  if [[ "$buildfile_mtime2" != "$buildfile_mtime3" ]]; then
+    fail "BUILD.bazel was updated even though contents are same"
+  fi
 }
 
 # Tests that blueprint files that are deleted are not present when the
@@ -218,6 +232,63 @@
   eval "${_save_trap}"
 }
 
+function test_build_files_take_precedence {
+  _save_trap=$(trap -p EXIT)
+  trap '[[ $? -ne 0 ]] && echo Are you running this locally? Try changing --sandbox_tmpfs_path to something other than /tmp/ in build/bazel/linux.bazelrc.' EXIT
+  _build_files_take_precedence
+  eval "${_save_trap}"
+}
+
+function _build_files_take_precedence {
+  setup
+
+  # This specific directory is hardcoded in bp2build as being one
+  # where the BUILD file should be intentionally kept.
+  mkdir -p testpkg/keep_build_file
+  cat > testpkg/keep_build_file/Android.bp <<'EOF'
+genrule {
+    name: "print_origin",
+    cmd: "echo 'from_soong' > $(out)",
+    out: [
+        "origin.txt",
+    ],
+    bazel_module: {
+        bp2build_available: true,
+    },
+  }
+EOF
+
+  run_soong bp2build
+  run_bazel build --config=android --config=bp2build --config=ci //testpkg/keep_build_file:print_origin
+
+  local -r output_file="$(find -L bazel-out -name origin.txt)"
+  if [[ ! -f "${output_file}" ]]; then
+    fail "Expected origin.txt to be generated, but was missing"
+  fi
+  if ! grep from_soong "${output_file}"; then
+    fail "Expected to find 'from_soong' in '${output_file}'"
+  fi
+
+  cat > testpkg/keep_build_file/BUILD.bazel <<'EOF'
+genrule(
+    name = "print_origin",
+    outs = ["origin.txt"],
+    cmd = "echo 'from_bazel' > $@",
+)
+EOF
+
+  # Clean the workspace. There is a test infrastructure bug where run_bazel
+  # will symlink Android.bp files in the source directory again and thus
+  # pollute the workspace.
+  # TODO: b/286059878 - Remove this clean after the underlying bug is fixed.
+  run_soong clean
+  run_soong bp2build
+  run_bazel build --config=android --config=bp2build --config=ci //testpkg/keep_build_file:print_origin
+  if ! grep from_bazel "${output_file}"; then
+    fail "Expected to find 'from_bazel' in '${output_file}'"
+  fi
+}
+
 function test_bp2build_symlinks_files {
   setup
   mkdir -p foo
@@ -368,4 +439,39 @@
   run_bazel build --config=android --config=api_bp2build //foo:libfoo.contribution
 }
 
+function test_bazel_standalone_output_paths_contain_product_name {
+  setup
+  mkdir -p a
+  cat > a/Android.bp <<EOF
+cc_object {
+  name: "qq",
+  srcs: ["qq.cc"],
+  bazel_module: {
+    bp2build_available: true,
+  },
+  stl: "none",
+  system_shared_libs: [],
+}
+EOF
+
+  cat > a/qq.cc <<EOF
+#include "qq.h"
+int qq() {
+  return QQ;
+}
+EOF
+
+  cat > a/qq.h <<EOF
+#define QQ 1
+EOF
+
+  export TARGET_PRODUCT=aosp_arm; run_soong bp2build
+  local -r output=$(run_bazel cquery //a:qq --output=files --config=android --config=bp2build --config=ci)
+  if [[ ! $(echo ${output} | grep "bazel-out/aosp_arm") ]]; then
+    fail "Did not find the product name '${TARGET_PRODUCT}' in the output path. This can cause " \
+      "unnecessary rebuilds when toggling between products as bazel outputs for different products will " \
+      "clobber each other. Output paths are: \n${output}"
+  fi
+}
+
 scan_and_run_tests
diff --git a/tests/dcla_apex_comparison_test.sh b/tests/dcla_apex_comparison_test.sh
index 97ae97e..e3c189f 100755
--- a/tests/dcla_apex_comparison_test.sh
+++ b/tests/dcla_apex_comparison_test.sh
@@ -45,11 +45,18 @@
   com.android.tethering
 )
 
+BAZEL_TARGETS=(
+  //packages/modules/adb/apex:com.android.adbd
+  //frameworks/av/apex:com.android.media.swcodec
+)
+
 DCLA_LIBS=(
   libbase.so
   libc++.so
   libcrypto.so
   libcutils.so
+  libstagefright_flacdec.so
+  libutils.so
 )
 
 if [[ -z ${OUT_DIR+x} ]]; then
@@ -76,6 +83,10 @@
 ############
 OUTPUT_DIR="$(mktemp -d tmp.XXXXXX)"
 
+function call_bazel() {
+  build/bazel/bin/bazel $@
+}
+
 function cleanup {
   rm -rf "${OUTPUT_DIR}"
 }
@@ -87,7 +98,9 @@
 
 function extract_dcla_libs() {
   local product=$1; shift
-  for module in "${MODULES[@]}"; do
+  local modules=("$@"); shift
+
+  for module in "${modules[@]}"; do
     local apex="${OUTPUT_DIR}/${product}/${module}.apex"
     local extract_dir="${OUTPUT_DIR}/${product}/${module}/extract"
 
@@ -97,11 +110,12 @@
 
 function compare_dcla_libs() {
   local product=$1; shift
+  local modules=("$@"); shift
 
   for lib in "${DCLA_LIBS[@]}"; do
     for arch in lib lib64; do
       local prev_sha=""
-      for module in "${MODULES[@]}"; do
+      for module in "${modules[@]}"; do
         local file="${OUTPUT_DIR}/${product}/${module}/extract/${arch}/${lib}"
         if [[ ! -f "${file}" ]]; then
           # not all libs are present in a module
@@ -112,7 +126,7 @@
         sha="${sha% *}"
         if [ "${prev_sha}" == "" ]; then
           prev_sha="${sha}"
-        elif [ "${sha}" != "${prev_sha}" ] && { [ "${lib}" != "libcrypto.so" ] || [ "${module}" != "com.android.tethering" ]; }; then
+        elif [ "${sha}" != "${prev_sha}" ] && { [ "${lib}" != "libcrypto.so" ] || [[ "${module}" != *"com.android.tethering" ]]; }; then
           echo "Test failed, ${lib} has different hash value"
           exit 1
         fi
@@ -131,8 +145,22 @@
     --product "${product}" \
     --dist_dir "${OUTPUT_DIR}/${product}"
 
-  extract_dcla_libs "${product}"
-  compare_dcla_libs "${product}"
+  bazel_apexes=()
+  if [[ -n ${TEST_BAZEL+x} ]] && [ "${TEST_BAZEL}" = true ]; then
+    export TARGET_PRODUCT="${product/module/aosp}"
+    call_bazel build --config=bp2build --config=ci --config=android "${BAZEL_TARGETS[@]}"
+    for target in "${BAZEL_TARGETS[@]}"; do
+      apex_path="$(realpath $(call_bazel cquery --config=bp2build --config=android --config=ci --output=files $target))"
+      mkdir -p ${OUTPUT_DIR}/${product}
+      bazel_apex="bazel_$(basename $apex_path)"
+      mv $apex_path ${OUTPUT_DIR}/${product}/${bazel_apex}
+      bazel_apexes+=(${bazel_apex%".apex"})
+    done
+  fi
+
+  all_modeuls=(${MODULES[@]} ${bazel_apexes[@]})
+  extract_dcla_libs "${product}" "${all_modeuls[@]}"
+  compare_dcla_libs "${product}" "${all_modeuls[@]}"
 done
 
 echo "Test passed"
diff --git a/tests/genrule_sandbox_test.py b/tests/genrule_sandbox_test.py
new file mode 100755
index 0000000..697fc26
--- /dev/null
+++ b/tests/genrule_sandbox_test.py
@@ -0,0 +1,186 @@
+#!/usr/bin/env python3
+
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import argparse
+import collections
+import json
+import os.path
+import subprocess
+import tempfile
+
+SRC_ROOT_DIR = os.path.abspath(__file__ + "/../../../..")
+
+
+def _module_graph_path(out_dir):
+  return os.path.join(SRC_ROOT_DIR, out_dir, "soong", "module-actions.json")
+
+
+def _build_with_soong(targets, target_product, out_dir, extra_env={}):
+  env = {
+      "TARGET_PRODUCT": target_product,
+      "TARGET_BUILD_VARIANT": "userdebug",
+  }
+  env.update(os.environ)
+  env.update(extra_env)
+  args = [
+      "build/soong/soong_ui.bash",
+      "--make-mode",
+      "--skip-soong-tests",
+  ]
+  args.extend(targets)
+  try:
+    out = subprocess.check_output(
+        args,
+        cwd=SRC_ROOT_DIR,
+        env=env,
+    )
+  except subprocess.CalledProcessError as e:
+    print(e)
+    print(e.stdout)
+    print(e.stderr)
+    exit(1)
+
+
+def _find_outputs_for_modules(modules, out_dir, target_product):
+  module_path = os.path.join(
+      SRC_ROOT_DIR, out_dir, "soong", "module-actions.json"
+  )
+
+  if not os.path.exists(module_path):
+    _build_with_soong(["json-module-graph"], target_product, out_dir)
+
+  action_graph = json.load(open(_module_graph_path(out_dir)))
+
+  module_to_outs = collections.defaultdict(set)
+  for mod in action_graph:
+    name = mod["Name"]
+    if name in modules:
+      for act in mod["Module"]["Actions"]:
+        if "}generate " in act["Desc"]:
+          module_to_outs[name].update(act["Outputs"])
+  return module_to_outs
+
+
+def _store_outputs_to_tmp(output_files):
+  try:
+    tempdir = tempfile.TemporaryDirectory()
+    for f in output_files:
+      out = subprocess.check_output(
+          ["cp", "--parents", f, tempdir.name],
+          cwd=SRC_ROOT_DIR,
+      )
+    return tempdir
+  except subprocess.CalledProcessError as e:
+    print(e)
+    print(e.stdout)
+    print(e.stderr)
+
+
+def _diff_outs(file1, file2, show_diff):
+  base_args = ["diff"]
+  if not show_diff:
+    base_args.append("--brief")
+  try:
+    args = base_args + [file1, file2]
+    output = subprocess.check_output(
+        args,
+        cwd=SRC_ROOT_DIR,
+    )
+  except subprocess.CalledProcessError as e:
+    if e.returncode == 1:
+      if show_diff:
+        return output
+      return True
+  return None
+
+
+def _compare_outputs(module_to_outs, tempdir, show_diff):
+  different_modules = collections.defaultdict(list)
+  for module, outs in module_to_outs.items():
+    for out in outs:
+      output = None
+      diff = _diff_outs(os.path.join(tempdir.name, out), out, show_diff)
+      if diff:
+        different_modules[module].append(diff)
+
+  tempdir.cleanup()
+  return different_modules
+
+
+def main():
+  parser = argparse.ArgumentParser()
+  parser.add_argument(
+      "--target_product",
+      "-t",
+      default="aosp_cf_arm64_phone",
+      help="optional, target product, always runs as eng",
+  )
+  parser.add_argument(
+      "modules",
+      nargs="+",
+      help="modules to compare builds with genrule sandboxing enabled/not",
+  )
+  parser.add_argument(
+      "--show-diff",
+      "-d",
+      action="store_true",
+      required=False,
+      help="whether to display differing files",
+  )
+  parser.add_argument(
+      "--output-paths-only",
+      "-o",
+      action="store_true",
+      required=False,
+      help="Whether to only return the output paths per module",
+  )
+  args = parser.parse_args()
+
+  out_dir = os.environ.get("OUT_DIR", "out")
+  target_product = args.target_product
+  modules = set(args.modules)
+
+  module_to_outs = _find_outputs_for_modules(modules, out_dir, target_product)
+  if args.output_paths_only:
+    for m, o in module_to_outs.items():
+      print(f"{m} outputs: {o}")
+    exit(0)
+
+  all_outs = set()
+  for outs in module_to_outs.values():
+    all_outs.update(outs)
+  print("build without sandboxing")
+  _build_with_soong(list(all_outs), target_product, out_dir)
+  tempdir = _store_outputs_to_tmp(all_outs)
+  print("build with sandboxing")
+  _build_with_soong(
+      list(all_outs),
+      target_product,
+      out_dir,
+      extra_env={"GENRULE_SANDBOXING": "true"},
+  )
+  diffs = _compare_outputs(module_to_outs, tempdir, args.show_diff)
+  if len(diffs) == 0:
+    print("All modules are correct")
+  elif args.show_diff:
+    for m, d in diffs.items():
+      print(f"Module {m} has diffs {d}")
+  else:
+    print(f"Modules {list(diffs.keys())} have diffs")
+
+
+if __name__ == "__main__":
+  main()
diff --git a/tests/lib.sh b/tests/lib.sh
index 715eac1..7f3970e 100644
--- a/tests/lib.sh
+++ b/tests/lib.sh
@@ -99,6 +99,7 @@
   symlink_directory external/python
   symlink_directory external/sqlite
   symlink_directory external/spdx-tools
+  symlink_directory libcore
 
   touch "$MOCK_TOP/Android.bp"
 }
diff --git a/tests/mixed_mode_test.sh b/tests/mixed_mode_test.sh
index 05d3a66..ca63fdf 100755
--- a/tests/mixed_mode_test.sh
+++ b/tests/mixed_mode_test.sh
@@ -1,4 +1,4 @@
-#!/bin/bash -eu
+#!/bin/bash
 
 set -o pipefail
 
@@ -88,12 +88,12 @@
     fail "Bazel actions not found for force-enabled module"
   fi
 
-  local exit_code=`run_soong --bazel-force-enabled-modules=unenabled-touch-file nothing`
+  unused=`run_soong --bazel-force-enabled-modules=unenabled-touch-file --ensure-allowlist-integrity nothing >/dev/null`
 
-  if [[ $exit_code -ne 1 ]]; then
+  if [[ $? -ne 1 ]]; then
     fail "Expected failure due to force-enabling an unenabled module "
   fi
 }
 
 
-scan_and_run_tests
\ No newline at end of file
+scan_and_run_tests
diff --git a/tests/persistent_bazel_test.sh b/tests/persistent_bazel_test.sh
index 4e2982a..9b7b58f 100755
--- a/tests/persistent_bazel_test.sh
+++ b/tests/persistent_bazel_test.sh
@@ -73,8 +73,8 @@
 
   USE_PERSISTENT_BAZEL=1 run_soong nothing 1>out/failurelog.txt 2>&1 && fail "Expected build failure" || true
 
-  if ! grep -sq "'build/bazel/rules' is not a package" out/failurelog.txt ; then
-    fail "Expected error to contain 'build/bazel/rules' is not a package, instead got:\n$(cat out/failurelog.txt)"
+  if ! grep -sq "cannot load //build/bazel/rules/common/api_constants.bzl" out/failurelog.txt ; then
+    fail "Expected error to contain 'cannot load //build/bazel/rules/common/api_constants.bzl', instead got:\n$(cat out/failurelog.txt)"
   fi
 
   kill $(cat out/bazel/output/server/server.pid.txt) 2>/dev/null || true
diff --git a/tests/run_integration_tests.sh b/tests/run_integration_tests.sh
index e1aa70c..c77b903 100755
--- a/tests/run_integration_tests.sh
+++ b/tests/run_integration_tests.sh
@@ -15,10 +15,13 @@
 # mock client.
 "$TOP/build/soong/tests/apex_comparison_tests.sh"
 "$TOP/build/soong/tests/apex_comparison_tests.sh" "module_arm64only"
-extra_build_params=--bazel-mode-staging "$TOP/build/soong/tests/dcla_apex_comparison_test.sh"
-BUILD_BROKEN_DISABLE_BAZEL=true "$TOP/build/soong/tests/dcla_apex_comparison_test.sh"
+TEST_BAZEL=true extra_build_params=--bazel-mode-staging "$TOP/build/soong/tests/dcla_apex_comparison_test.sh"
+#BUILD_BROKEN_DISABLE_BAZEL=true "$TOP/build/soong/tests/dcla_apex_comparison_test.sh"
 "$TOP/build/soong/tests/apex_cc_module_arch_variant_tests.sh"
 "$TOP/build/soong/tests/apex_cc_module_arch_variant_tests.sh" "aosp_arm" "armv7-a"
 "$TOP/build/soong/tests/apex_cc_module_arch_variant_tests.sh" "aosp_cf_arm64_phone" "armv8-a" "cortex-a53"
 
 "$TOP/build/soong/tests/sbom_test.sh"
+
+"$TOP/build/bazel/ci/b_test.sh"
+
diff --git a/tests/sbom_test.sh b/tests/sbom_test.sh
index 2f154cd..94fe51d 100755
--- a/tests/sbom_test.sh
+++ b/tests/sbom_test.sh
@@ -90,10 +90,12 @@
  -I /system/lib64/android.hardware.security.sharedsecret-V1-ndk.so \
  -I /system/lib64/android.security.compat-ndk.so \
  -I /system/lib64/libkeymaster4_1support.so \
+ -I /system/lib64/libkeymaster4support.so \
  -I /system/lib64/libkeymint.so \
  -I /system/lib64/libkeystore2_aaid.so \
  -I /system/lib64/libkeystore2_apc_compat.so \
  -I /system/lib64/libkeystore2_crypto.so \
+ -I /system/lib64/libkeystore-attestation-application-id.so \
  -I /system/lib64/libkm_compat_service.so \
  -I /system/lib64/libkm_compat.so \
  -I /system/lib64/vndk-29 \
diff --git a/tests/soong_test.sh b/tests/soong_test.sh
index f7bee40..6779d8a 100755
--- a/tests/soong_test.sh
+++ b/tests/soong_test.sh
@@ -9,12 +9,8 @@
 function test_m_clean_works {
   setup
 
-  # Create a directory with files that cannot be removed
-  mkdir -p out/bad_directory_permissions
-  touch out/bad_directory_permissions/unremovable_file
-  # File permissions are fine but directory permissions are bad
-  chmod a+rwx out/bad_directory_permissions/unremovable_file
-  chmod a-rwx out/bad_directory_permissions
+  mkdir -p out/some_directory
+  touch out/some_directory/some_file
 
   run_soong clean
 }
diff --git a/tradefed/autogen.go b/tradefed/autogen.go
index 49b09af..3c55c51 100644
--- a/tradefed/autogen.go
+++ b/tradefed/autogen.go
@@ -40,9 +40,9 @@
 }
 
 var autogenTestConfig = pctx.StaticRule("autogenTestConfig", blueprint.RuleParams{
-	Command:     "sed 's&{MODULE}&${name}&g;s&{EXTRA_CONFIGS}&'${extraConfigs}'&g;s&{OUTPUT_FILENAME}&'${outputFileName}'&g;s&{TEST_INSTALL_BASE}&'${testInstallBase}'&g' $template > $out",
+	Command:     "sed 's&{MODULE}&${name}&g;s&{EXTRA_CONFIGS}&'${extraConfigs}'&g;s&{EXTRA_TEST_RUNNER_CONFIGS}&'${extraTestRunnerConfigs}'&g;s&{OUTPUT_FILENAME}&'${outputFileName}'&g;s&{TEST_INSTALL_BASE}&'${testInstallBase}'&g' $template > $out",
 	CommandDeps: []string{"$template"},
-}, "name", "template", "extraConfigs", "outputFileName", "testInstallBase")
+}, "name", "template", "extraConfigs", "outputFileName", "testInstallBase", "extraTestRunnerConfigs")
 
 func testConfigPath(ctx android.ModuleContext, prop *string, testSuites []string, autoGenConfig *bool, testConfigTemplateProp *string) (path android.Path, autogenPath android.WritablePath) {
 	p := getTestConfig(ctx, prop)
@@ -107,7 +107,7 @@
 
 }
 
-func autogenTemplate(ctx android.ModuleContext, name string, output android.WritablePath, template string, configs []Config, outputFileName string, testInstallBase string) {
+func autogenTemplate(ctx android.ModuleContext, name string, output android.WritablePath, template string, configs []Config, testRunnerConfigs []Option, outputFileName string, testInstallBase string) {
 	if template == "" {
 		ctx.ModuleErrorf("Empty template")
 	}
@@ -118,16 +118,27 @@
 	extraConfigs := strings.Join(configStrings, fmt.Sprintf("\\n%s", test_xml_indent))
 	extraConfigs = proptools.NinjaAndShellEscape(extraConfigs)
 
+	var testRunnerConfigStrings []string
+	for _, config := range testRunnerConfigs {
+		testRunnerConfigStrings = append(testRunnerConfigStrings, config.Config())
+	}
+	extraTestRunnerConfigs := strings.Join(testRunnerConfigStrings, fmt.Sprintf("\\n%s%s", test_xml_indent, test_xml_indent))
+	if len(extraTestRunnerConfigs) > 0 {
+		extraTestRunnerConfigs += fmt.Sprintf("\\n%s%s", test_xml_indent, test_xml_indent)
+	}
+	extraTestRunnerConfigs = proptools.NinjaAndShellEscape(extraTestRunnerConfigs)
+
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        autogenTestConfig,
 		Description: "test config",
 		Output:      output,
 		Args: map[string]string{
-			"name":            name,
-			"template":        template,
-			"extraConfigs":    extraConfigs,
-			"outputFileName":  outputFileName,
-			"testInstallBase": testInstallBase,
+			"name":                   name,
+			"template":               template,
+			"extraConfigs":           extraConfigs,
+			"outputFileName":         outputFileName,
+			"testInstallBase":        testInstallBase,
+			"extraTestRunnerConfigs": extraTestRunnerConfigs,
 		},
 	})
 }
@@ -142,6 +153,7 @@
 	TestSuites              []string
 	Config                  []Config
 	OptionsForAutogenerated []Option
+	TestRunnerOptions       []Option
 	AutoGenConfig           *bool
 	UnitTest                *bool
 	TestInstallBase         string
@@ -155,6 +167,7 @@
 	for _, c := range options.OptionsForAutogenerated {
 		configs = append(configs, c)
 	}
+	testRunnerConfigs := append([]Option{}, options.TestRunnerOptions...)
 	name := options.Name
 	if name == "" {
 		name = ctx.ModuleName()
@@ -163,15 +176,15 @@
 	if autogenPath != nil {
 		templatePath := getTestConfigTemplate(ctx, options.TestConfigTemplateProp)
 		if templatePath.Valid() {
-			autogenTemplate(ctx, name, autogenPath, templatePath.String(), configs, options.OutputFileName, options.TestInstallBase)
+			autogenTemplate(ctx, name, autogenPath, templatePath.String(), configs, testRunnerConfigs, options.OutputFileName, options.TestInstallBase)
 		} else {
 			if ctx.Device() {
-				autogenTemplate(ctx, name, autogenPath, options.DeviceTemplate, configs, options.OutputFileName, options.TestInstallBase)
+				autogenTemplate(ctx, name, autogenPath, options.DeviceTemplate, configs, testRunnerConfigs, options.OutputFileName, options.TestInstallBase)
 			} else {
 				if Bool(options.UnitTest) {
-					autogenTemplate(ctx, name, autogenPath, options.HostUnitTestTemplate, configs, options.OutputFileName, options.TestInstallBase)
+					autogenTemplate(ctx, name, autogenPath, options.HostUnitTestTemplate, configs, testRunnerConfigs, options.OutputFileName, options.TestInstallBase)
 				} else {
-					autogenTemplate(ctx, name, autogenPath, options.HostTemplate, configs, options.OutputFileName, options.TestInstallBase)
+					autogenTemplate(ctx, name, autogenPath, options.HostTemplate, configs, testRunnerConfigs, options.OutputFileName, options.TestInstallBase)
 				}
 			}
 		}
diff --git a/tradefed/suite_harness/tradefed_binary.go b/tradefed/suite_harness/tradefed_binary.go
index a421d8b..1ce94bc 100644
--- a/tradefed/suite_harness/tradefed_binary.go
+++ b/tradefed/suite_harness/tradefed_binary.go
@@ -78,7 +78,6 @@
 		// Add dependencies required by all tradefed_binary modules.
 		props.Libs = []string{
 			"tradefed",
-			"tradefed-test-framework",
 			"loganalysis",
 			"compatibility-host-util",
 		}
diff --git a/ui/build/Android.bp b/ui/build/Android.bp
index b79754c..959ae4c 100644
--- a/ui/build/Android.bp
+++ b/ui/build/Android.bp
@@ -46,6 +46,7 @@
         "soong-ui-tracer",
     ],
     srcs: [
+        "bazel_metrics.go",
         "build.go",
         "cleanbuild.go",
         "config.go",
diff --git a/ui/build/bazel_metrics.go b/ui/build/bazel_metrics.go
new file mode 100644
index 0000000..c0690c1
--- /dev/null
+++ b/ui/build/bazel_metrics.go
@@ -0,0 +1,135 @@
+// Copyright 2023 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//	http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package build
+
+// This file contains functionality to parse bazel profile data into
+// a bazel_metrics proto, defined in build/soong/ui/metrics/bazel_metrics_proto
+// These metrics are later uploaded in upload.go
+
+import (
+	"bufio"
+	"os"
+	"strconv"
+	"strings"
+
+	"android/soong/shared"
+	"google.golang.org/protobuf/proto"
+
+	bazel_metrics_proto "android/soong/ui/metrics/bazel_metrics_proto"
+)
+
+func parseTimingToNanos(str string) int64 {
+	millisString := removeDecimalPoint(str)
+	timingMillis, _ := strconv.ParseInt(millisString, 10, 64)
+	return timingMillis * 1000000
+}
+
+func parsePercentageToTenThousandths(str string) int32 {
+	percentageString := removeDecimalPoint(str)
+	//remove the % at the end of the string
+	percentage := strings.ReplaceAll(percentageString, "%", "")
+	percentagePortion, _ := strconv.ParseInt(percentage, 10, 32)
+	return int32(percentagePortion)
+}
+
+func removeDecimalPoint(numString string) string {
+	// The format is always 0.425 or 10.425
+	return strings.ReplaceAll(numString, ".", "")
+}
+
+func parseTotal(line string) int64 {
+	words := strings.Fields(line)
+	timing := words[3]
+	return parseTimingToNanos(timing)
+}
+
+func parsePhaseTiming(line string) bazel_metrics_proto.PhaseTiming {
+	words := strings.Fields(line)
+	getPhaseNameAndTimingAndPercentage := func([]string) (string, int64, int32) {
+		// Sample lines include:
+		// Total launch phase time   0.011 s    2.59%
+		// Total target pattern evaluation phase time  0.011 s    2.59%
+		var beginning int
+		var end int
+		for ind, word := range words {
+			if word == "Total" {
+				beginning = ind + 1
+			} else if beginning > 0 && word == "phase" {
+				end = ind
+				break
+			}
+		}
+		phaseName := strings.Join(words[beginning:end], " ")
+
+		// end is now "phase" - advance by 2 for timing and 4 for percentage
+		percentageString := words[end+4]
+		timingString := words[end+2]
+		timing := parseTimingToNanos(timingString)
+		percentagePortion := parsePercentageToTenThousandths(percentageString)
+		return phaseName, timing, percentagePortion
+	}
+
+	phaseName, timing, portion := getPhaseNameAndTimingAndPercentage(words)
+	phaseTiming := bazel_metrics_proto.PhaseTiming{}
+	phaseTiming.DurationNanos = &timing
+	phaseTiming.PortionOfBuildTime = &portion
+
+	phaseTiming.PhaseName = &phaseName
+	return phaseTiming
+}
+
+// This method takes a file created by bazel's --analyze-profile mode and
+// writes bazel metrics data to the provided filepath.
+func ProcessBazelMetrics(bazelProfileFile string, bazelMetricsFile string, ctx Context, config Config) {
+	if bazelProfileFile == "" {
+		return
+	}
+
+	readBazelProto := func(filepath string) bazel_metrics_proto.BazelMetrics {
+		//serialize the proto, write it
+		bazelMetrics := bazel_metrics_proto.BazelMetrics{}
+
+		file, err := os.ReadFile(filepath)
+		if err != nil {
+			ctx.Fatalln("Error reading metrics file\n", err)
+		}
+
+		scanner := bufio.NewScanner(strings.NewReader(string(file)))
+		scanner.Split(bufio.ScanLines)
+
+		var phaseTimings []*bazel_metrics_proto.PhaseTiming
+		for scanner.Scan() {
+			line := scanner.Text()
+			if strings.HasPrefix(line, "Total run time") {
+				total := parseTotal(line)
+				bazelMetrics.Total = &total
+			} else if strings.HasPrefix(line, "Total") {
+				phaseTiming := parsePhaseTiming(line)
+				phaseTimings = append(phaseTimings, &phaseTiming)
+			}
+		}
+		bazelMetrics.PhaseTimings = phaseTimings
+
+		return bazelMetrics
+	}
+
+	if _, err := os.Stat(bazelProfileFile); err != nil {
+		// We can assume bazel didn't run if the profile doesn't exist
+		return
+	}
+	bazelProto := readBazelProto(bazelProfileFile)
+	bazelProto.ExitCode = proto.Int32(config.bazelExitCode)
+	shared.Save(&bazelProto, bazelMetricsFile)
+}
diff --git a/ui/build/build.go b/ui/build/build.go
index edc595d..6874ef7 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -259,10 +259,16 @@
 		startGoma(ctx, config)
 	}
 
+	rbeCh := make(chan bool)
 	if config.StartRBE() {
 		cleanupRBELogsDir(ctx, config)
-		startRBE(ctx, config)
+		go func() {
+			startRBE(ctx, config)
+			close(rbeCh)
+		}()
 		defer DumpRBEMetrics(ctx, config, filepath.Join(config.LogsDir(), "rbe_metrics.pb"))
+	} else {
+		close(rbeCh)
 	}
 
 	if what&RunProductConfig != 0 {
@@ -315,11 +321,11 @@
 		testForDanglingRules(ctx, config)
 	}
 
+	<-rbeCh
 	if what&RunNinja != 0 {
 		if what&RunKati != 0 {
 			installCleanIfNecessary(ctx, config)
 		}
-
 		runNinjaForBuild(ctx, config)
 	}
 
diff --git a/ui/build/cleanbuild.go b/ui/build/cleanbuild.go
index fd60177..41cb5ab 100644
--- a/ui/build/cleanbuild.go
+++ b/ui/build/cleanbuild.go
@@ -17,7 +17,6 @@
 import (
 	"bytes"
 	"fmt"
-	"io/fs"
 	"io/ioutil"
 	"os"
 	"path/filepath"
@@ -59,37 +58,9 @@
 	FILEMODE_USER_EXECUTE = FILEMODE_EXECUTE << FILEMODE_USER_SHIFT
 )
 
-// Ensures that files and directories in the out dir can be deleted.
-// For example, Bazen can generate output directories where the write bit isn't set, causing 'm' clean' to fail.
-func ensureOutDirRemovable(ctx Context, config Config) {
-	err := filepath.WalkDir(config.OutDir(), func(path string, d fs.DirEntry, err error) error {
-		if err != nil {
-			return err
-		}
-		if d.IsDir() {
-			info, err := d.Info()
-			if err != nil {
-				return err
-			}
-			// Equivalent to running chmod u+rwx on each directory
-			newMode := info.Mode() | FILEMODE_USER_READ | FILEMODE_USER_WRITE | FILEMODE_USER_EXECUTE
-			if err := os.Chmod(path, newMode); err != nil {
-				return err
-			}
-		}
-		// Continue walking the out dir...
-		return nil
-	})
-	if err != nil && !os.IsNotExist(err) {
-		// Display the error, but don't crash.
-		ctx.Println(err.Error())
-	}
-}
-
 // Remove everything under the out directory. Don't remove the out directory
 // itself in case it's a symlink.
 func clean(ctx Context, config Config) {
-	ensureOutDirRemovable(ctx, config)
 	removeGlobs(ctx, filepath.Join(config.OutDir(), "*"))
 	ctx.Println("Entire build directory removed.")
 }
@@ -164,6 +135,7 @@
 		productOut("obj/NOTICE_FILES"),
 		productOut("obj/PACKAGING"),
 		productOut("ramdisk"),
+		productOut("ramdisk_16k"),
 		productOut("debug_ramdisk"),
 		productOut("vendor_ramdisk"),
 		productOut("vendor_debug_ramdisk"),
diff --git a/ui/build/config.go b/ui/build/config.go
index a755d14..c991777 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -17,6 +17,7 @@
 import (
 	"context"
 	"encoding/json"
+	"errors"
 	"fmt"
 	"io/ioutil"
 	"math/rand"
@@ -67,28 +68,30 @@
 	logsPrefix    string
 
 	// From the arguments
-	parallel          int
-	keepGoing         int
-	verbose           bool
-	checkbuild        bool
-	dist              bool
-	jsonModuleGraph   bool
-	apiBp2build       bool // Generate BUILD files for Soong modules that contribute APIs
-	bp2build          bool
-	queryview         bool
-	reportMkMetrics   bool // Collect and report mk2bp migration progress metrics.
-	soongDocs         bool
-	multitreeBuild    bool // This is a multitree build.
-	skipConfig        bool
-	skipKati          bool
-	skipKatiNinja     bool
-	skipSoong         bool
-	skipNinja         bool
-	skipSoongTests    bool
-	searchApiDir      bool // Scan the Android.bp files generated in out/api_surfaces
-	skipMetricsUpload bool
-	buildStartedTime  int64 // For metrics-upload-only - manually specify a build-started time
-	buildFromTextStub bool
+	parallel                 int
+	keepGoing                int
+	verbose                  bool
+	checkbuild               bool
+	dist                     bool
+	jsonModuleGraph          bool
+	apiBp2build              bool // Generate BUILD files for Soong modules that contribute APIs
+	bp2build                 bool
+	queryview                bool
+	reportMkMetrics          bool // Collect and report mk2bp migration progress metrics.
+	soongDocs                bool
+	multitreeBuild           bool // This is a multitree build.
+	skipConfig               bool
+	skipKati                 bool
+	skipKatiNinja            bool
+	skipSoong                bool
+	skipNinja                bool
+	skipSoongTests           bool
+	searchApiDir             bool // Scan the Android.bp files generated in out/api_surfaces
+	skipMetricsUpload        bool
+	buildStartedTime         int64 // For metrics-upload-only - manually specify a build-started time
+	buildFromTextStub        bool
+	ensureAllowlistIntegrity bool  // For CI builds - make sure modules are mixed-built
+	bazelExitCode            int32 // For b-runs - necessary for updating NonZeroExit
 
 	// From the product config
 	katiArgs        []string
@@ -138,6 +141,9 @@
 	EXTERNAL_FILE
 	// ninja uses a prioritized module list from Soong
 	HINT_FROM_SOONG
+	// If ninja log exists, use NINJA_LOG, if not, use HINT_FROM_SOONG instead.
+	// We can assume it is an incremental build if ninja log exists.
+	DEFAULT
 )
 const srcDirFileCheck = "build/soong/root.bp"
 
@@ -297,11 +303,12 @@
 	return true
 }
 
-func UploadOnlyConfig(ctx Context, _ ...string) Config {
+func UploadOnlyConfig(ctx Context, args ...string) Config {
 	ret := &configImpl{
 		environ:       OsEnvironment(),
 		sandboxConfig: &SandboxConfig{},
 	}
+	ret.parseArgs(ctx, args)
 	srcDir := absPath(ctx, ".")
 	bc := os.Getenv("ANDROID_BUILD_ENVIRONMENT_CONFIG")
 	if err := loadEnvConfig(ctx, ret, bc); err != nil {
@@ -313,8 +320,9 @@
 
 func NewConfig(ctx Context, args ...string) Config {
 	ret := &configImpl{
-		environ:       OsEnvironment(),
-		sandboxConfig: &SandboxConfig{},
+		environ:               OsEnvironment(),
+		sandboxConfig:         &SandboxConfig{},
+		ninjaWeightListSource: DEFAULT,
 	}
 
 	// Default matching ninja
@@ -325,8 +333,21 @@
 	ret.parseArgs(ctx, args)
 
 	if ret.ninjaWeightListSource == HINT_FROM_SOONG {
-		ret.environ.Set("SOONG_GENERATES_NINJA_HINT", "true")
+		ret.environ.Set("SOONG_GENERATES_NINJA_HINT", "always")
+	} else if ret.ninjaWeightListSource == DEFAULT {
+		defaultNinjaWeightListSource := NINJA_LOG
+		if _, err := os.Stat(filepath.Join(ret.OutDir(), ninjaLogFileName)); errors.Is(err, os.ErrNotExist) {
+			ctx.Verboseln("$OUT/.ninja_log doesn't exist, use HINT_FROM_SOONG instead")
+			defaultNinjaWeightListSource = HINT_FROM_SOONG
+		} else {
+			ctx.Verboseln("$OUT/.ninja_log exist, use NINJA_LOG")
+		}
+		ret.ninjaWeightListSource = defaultNinjaWeightListSource
+		// soong_build generates ninja hint depending on ninja log existence.
+		// Set it "depend" to avoid soong re-run due to env variable change.
+		ret.environ.Set("SOONG_GENERATES_NINJA_HINT", "depend")
 	}
+
 	// Make sure OUT_DIR is set appropriately
 	if outDir, ok := ret.environ.Get("OUT_DIR"); ok {
 		ret.environ.Set("OUT_DIR", filepath.Clean(outDir))
@@ -813,9 +834,6 @@
 			//   by a previous build.
 			c.skipConfig = true
 			c.skipKati = true
-		} else if arg == "--skip-kati" {
-			// TODO: remove --skip-kati once module builds have been migrated to --song-only
-			c.skipKati = true
 		} else if arg == "--soong-only" {
 			c.skipKati = true
 			c.skipKatiNinja = true
@@ -883,6 +901,16 @@
 			} else {
 				ctx.Fatalf("Error parsing build-time-started-unix-millis", err)
 			}
+		} else if arg == "--ensure-allowlist-integrity" {
+			c.ensureAllowlistIntegrity = true
+		} else if strings.HasPrefix(arg, "--bazel-exit-code=") {
+			bazelExitCodeStr := strings.TrimPrefix(arg, "--bazel-exit-code=")
+			val, err := strconv.Atoi(bazelExitCodeStr)
+			if err == nil {
+				c.bazelExitCode = int32(val)
+			} else {
+				ctx.Fatalf("Error parsing bazel-exit-code", err)
+			}
 		} else if len(arg) > 0 && arg[0] == '-' {
 			parseArgNum := func(def int) int {
 				if len(arg) > 2 {
@@ -1096,6 +1124,9 @@
 }
 
 func (c *configImpl) UsedEnvFile(tag string) string {
+	if v, ok := c.environ.Get("TARGET_PRODUCT"); ok {
+		return shared.JoinPath(c.SoongOutDir(), usedEnvFile+"."+v+"."+tag)
+	}
 	return shared.JoinPath(c.SoongOutDir(), usedEnvFile+"."+tag)
 }
 
@@ -1219,6 +1250,13 @@
 	panic("TARGET_PRODUCT is not defined")
 }
 
+func (c *configImpl) TargetProductOrErr() (string, error) {
+	if v, ok := c.environ.Get("TARGET_PRODUCT"); ok {
+		return v, nil
+	}
+	return "", fmt.Errorf("TARGET_PRODUCT is not defined")
+}
+
 func (c *configImpl) TargetDevice() string {
 	return c.targetDevice
 }
@@ -1374,6 +1412,15 @@
 	return filepath.Join(buildTmpDir, "rbe")
 }
 
+func (c *configImpl) rbeCacheDir() string {
+	for _, f := range []string{"RBE_cache_dir", "FLAG_cache_dir"} {
+		if v, ok := c.environ.Get(f); ok {
+			return v
+		}
+	}
+	return shared.JoinPath(c.SoongOutDir(), "rbe")
+}
+
 func (c *configImpl) shouldCleanupRBELogsDir() bool {
 	// Perform a log directory cleanup only when the log directory
 	// is auto created by the build rather than user-specified.
@@ -1467,7 +1514,7 @@
 	if googleProdCredsExistCache {
 		return googleProdCredsExistCache
 	}
-	if _, err := exec.Command("/usr/bin/prodcertstatus", "--simple_output", "--nocheck_loas").Output(); err != nil {
+	if _, err := exec.Command("/usr/bin/gcertstatus").Output(); err != nil {
 		return false
 	}
 	googleProdCredsExistCache = true
@@ -1535,11 +1582,21 @@
 }
 
 func (c *configImpl) SoongVarsFile() string {
-	return filepath.Join(c.SoongOutDir(), "soong.variables")
+	targetProduct, err := c.TargetProductOrErr()
+	if err != nil {
+		return filepath.Join(c.SoongOutDir(), "soong.variables")
+	} else {
+		return filepath.Join(c.SoongOutDir(), "soong."+targetProduct+".variables")
+	}
 }
 
 func (c *configImpl) SoongNinjaFile() string {
-	return filepath.Join(c.SoongOutDir(), "build.ninja")
+	targetProduct, err := c.TargetProductOrErr()
+	if err != nil {
+		return filepath.Join(c.SoongOutDir(), "build.ninja")
+	} else {
+		return filepath.Join(c.SoongOutDir(), "build."+targetProduct+".ninja")
+	}
 }
 
 func (c *configImpl) CombinedNinjaFile() string {
@@ -1693,6 +1750,16 @@
 	return c.Environment().IsEnvTrue("USE_PERSISTENT_BAZEL")
 }
 
+// GetBazeliskBazelVersion returns the Bazel version to use for this build,
+// or the empty string if the current canonical prod Bazel should be used.
+// This environment variable should only be set to debug the build system.
+// The Bazel version, if set, will be passed to Bazelisk, and Bazelisk will
+// handle downloading and invoking the correct Bazel binary.
+func (c *configImpl) GetBazeliskBazelVersion() string {
+	value, _ := c.Environment().Get("USE_BAZEL_VERSION")
+	return value
+}
+
 func (c *configImpl) BazelModulesForceEnabledByFlag() string {
 	return c.bazelForceEnabledModules
 }
@@ -1701,6 +1768,10 @@
 	return c.skipMetricsUpload
 }
 
+func (c *configImpl) EnsureAllowlistIntegrity() bool {
+	return c.ensureAllowlistIntegrity
+}
+
 // Returns a Time object if one was passed via a command-line flag.
 // Otherwise returns the passed default.
 func (c *configImpl) BuildStartedTimeOrDefault(defaultTime time.Time) time.Time {
@@ -1710,6 +1781,10 @@
 	return time.UnixMilli(c.buildStartedTime)
 }
 
+func (c *configImpl) BazelExitCode() int32 {
+	return c.bazelExitCode
+}
+
 func GetMetricsUploader(topDir string, env *Environment) string {
 	if p, ok := env.Get("METRICS_UPLOADER"); ok {
 		metricsUploader := filepath.Join(topDir, p)
diff --git a/ui/build/finder.go b/ui/build/finder.go
index 3f628cf..62079fe 100644
--- a/ui/build/finder.go
+++ b/ui/build/finder.go
@@ -87,6 +87,8 @@
 			"TEST_MAPPING",
 			// Bazel top-level file to mark a directory as a Bazel workspace.
 			"WORKSPACE",
+			// METADATA file of packages
+			"METADATA",
 		},
 		// Bazel Starlark configuration files and all .mk files for product/board configuration.
 		IncludeSuffixes: []string{".bzl", ".mk"},
@@ -189,6 +191,13 @@
 		ctx.Fatalf("Could not find OWNERS: %v", err)
 	}
 
+	// Recursively look for all METADATA files.
+	metadataFiles := f.FindNamedAt(".", "METADATA")
+	err = dumpListToFile(ctx, config, metadataFiles, filepath.Join(dumpDir, "METADATA.list"))
+	if err != nil {
+		ctx.Fatalf("Could not find METADATA: %v", err)
+	}
+
 	// Recursively look for all TEST_MAPPING files.
 	testMappings := f.FindNamedAt(".", "TEST_MAPPING")
 	err = dumpListToFile(ctx, config, testMappings, filepath.Join(dumpDir, "TEST_MAPPING.list"))
diff --git a/ui/build/kati.go b/ui/build/kati.go
index dad68fa..aea56d3 100644
--- a/ui/build/kati.go
+++ b/ui/build/kati.go
@@ -22,6 +22,7 @@
 	"os/user"
 	"path/filepath"
 	"strings"
+	"time"
 
 	"android/soong/ui/metrics"
 	"android/soong/ui/status"
@@ -66,6 +67,21 @@
 	}
 }
 
+func writeValueIfChanged(ctx Context, config Config, dir string, filename string, value string) {
+	filePath := filepath.Join(dir, filename)
+	previousValue := ""
+	rawPreviousValue, err := ioutil.ReadFile(filePath)
+	if err == nil {
+		previousValue = string(rawPreviousValue)
+	}
+
+	if previousValue != value {
+		if err = ioutil.WriteFile(filePath, []byte(value), 0666); err != nil {
+			ctx.Fatalf("Failed to write: %v", err)
+		}
+	}
+}
+
 // Base function to construct and run the Kati command line with additional
 // arguments, and a custom function closure to mutate the environment Kati runs
 // in.
@@ -157,28 +173,60 @@
 	}
 	cmd.Stderr = cmd.Stdout
 
-	// Apply the caller's function closure to mutate the environment variables.
-	envFunc(cmd.Environment)
-
+	var username string
 	// Pass on various build environment metadata to Kati.
-	if _, ok := cmd.Environment.Get("BUILD_USERNAME"); !ok {
-		username := "unknown"
+	if usernameFromEnv, ok := cmd.Environment.Get("BUILD_USERNAME"); !ok {
+		username = "unknown"
 		if u, err := user.Current(); err == nil {
 			username = u.Username
 		} else {
 			ctx.Println("Failed to get current user:", err)
 		}
 		cmd.Environment.Set("BUILD_USERNAME", username)
+	} else {
+		username = usernameFromEnv
 	}
 
-	if _, ok := cmd.Environment.Get("BUILD_HOSTNAME"); !ok {
-		hostname, err := os.Hostname()
+	hostname, ok := cmd.Environment.Get("BUILD_HOSTNAME")
+	// Unset BUILD_HOSTNAME during kati run to avoid kati rerun, kati will use BUILD_HOSTNAME from a file.
+	cmd.Environment.Unset("BUILD_HOSTNAME")
+	if !ok {
+		hostname, err = os.Hostname()
 		if err != nil {
 			ctx.Println("Failed to read hostname:", err)
 			hostname = "unknown"
 		}
-		cmd.Environment.Set("BUILD_HOSTNAME", hostname)
 	}
+	writeValueIfChanged(ctx, config, config.SoongOutDir(), "build_hostname.txt", hostname)
+
+	// BUILD_NUMBER should be set to the source control value that
+	// represents the current state of the source code.  E.g., a
+	// perforce changelist number or a git hash.  Can be an arbitrary string
+	// (to allow for source control that uses something other than numbers),
+	// but must be a single word and a valid file name.
+	//
+	// If no BUILD_NUMBER is set, create a useful "I am an engineering build
+	// from this date/time" value.  Make it start with a non-digit so that
+	// anyone trying to parse it as an integer will probably get "0".
+	cmd.Environment.Unset("HAS_BUILD_NUMBER")
+	buildNumber, ok := cmd.Environment.Get("BUILD_NUMBER")
+	// Unset BUILD_NUMBER during kati run to avoid kati rerun, kati will use BUILD_NUMBER from a file.
+	cmd.Environment.Unset("BUILD_NUMBER")
+	if ok {
+		cmd.Environment.Set("HAS_BUILD_NUMBER", "true")
+		writeValueIfChanged(ctx, config, config.OutDir(), "file_name_tag.txt", buildNumber)
+	} else {
+		buildNumber = fmt.Sprintf("eng.%.6s.%s", username, time.Now().Format("20060102.150405" /* YYYYMMDD.HHMMSS */))
+		cmd.Environment.Set("HAS_BUILD_NUMBER", "false")
+		writeValueIfChanged(ctx, config, config.OutDir(), "file_name_tag.txt", username)
+	}
+	// Write the build number to a file so it can be read back in
+	// without changing the command line every time.  Avoids rebuilds
+	// when using ninja.
+	writeValueIfChanged(ctx, config, config.SoongOutDir(), "build_number.txt", buildNumber)
+
+	// Apply the caller's function closure to mutate the environment variables.
+	envFunc(cmd.Environment)
 
 	cmd.StartOrFatal()
 	// Set up the ToolStatus command line reader for Kati for a consistent UI
@@ -336,6 +384,7 @@
 			"ANDROID_BUILD_SHELL",
 			"DIST_DIR",
 			"OUT_DIR",
+			"FILE_NAME_TAG",
 		}...)
 
 		if config.Dist() {
diff --git a/ui/build/ninja.go b/ui/build/ninja.go
index 5d56531..61aaad8 100644
--- a/ui/build/ninja.go
+++ b/ui/build/ninja.go
@@ -35,48 +35,6 @@
 	ninjaWeightListFileName = ".ninja_weight_list"
 )
 
-func useNinjaBuildLog(ctx Context, config Config, cmd *Cmd) {
-	ninjaLogFile := filepath.Join(config.OutDir(), ninjaLogFileName)
-	data, err := os.ReadFile(ninjaLogFile)
-	var outputBuilder strings.Builder
-	if err == nil {
-		lines := strings.Split(strings.TrimSpace(string(data)), "\n")
-		// ninja log: <start>	<end>	<restat>	<name>	<cmdhash>
-		// ninja weight list: <name>,<end-start+1>
-		for _, line := range lines {
-			if strings.HasPrefix(line, "#") {
-				continue
-			}
-			fields := strings.Split(line, "\t")
-			path := fields[3]
-			start, err := strconv.Atoi(fields[0])
-			if err != nil {
-				continue
-			}
-			end, err := strconv.Atoi(fields[1])
-			if err != nil {
-				continue
-			}
-			outputBuilder.WriteString(path)
-			outputBuilder.WriteString(",")
-			outputBuilder.WriteString(strconv.Itoa(end-start+1) + "\n")
-		}
-	} else {
-		// If there is no ninja log file, just pass empty ninja weight list.
-		// Because it is still efficient with critical path calculation logic even without weight.
-		ctx.Verbosef("There is an error during reading ninja log, so ninja will use empty weight list: %s", err)
-	}
-
-	weightListFile := filepath.Join(config.OutDir(), ninjaWeightListFileName)
-
-	err = os.WriteFile(weightListFile, []byte(outputBuilder.String()), 0644)
-	if err == nil {
-		cmd.Args = append(cmd.Args, "-o", "usesweightlist="+weightListFile)
-	} else {
-		ctx.Panicf("Could not write ninja weight list file %s", err)
-	}
-}
-
 // Constructs and runs the Ninja command line with a restricted set of
 // environment variables. It's important to restrict the environment Ninja runs
 // for hermeticity reasons, and to avoid spurious rebuilds.
@@ -131,7 +89,7 @@
 
 	switch config.NinjaWeightListSource() {
 	case NINJA_LOG:
-		useNinjaBuildLog(ctx, config, cmd)
+		cmd.Args = append(cmd.Args, "-o", "usesninjalogasweightlist=yes")
 	case EVENLY_DISTRIBUTED:
 		// pass empty weight list means ninja considers every tasks's weight as 1(default value).
 		cmd.Args = append(cmd.Args, "-o", "usesweightlist=/dev/null")
diff --git a/ui/build/paths/config.go b/ui/build/paths/config.go
index b3092ea..b3e871f 100644
--- a/ui/build/paths/config.go
+++ b/ui/build/paths/config.go
@@ -86,27 +86,29 @@
 // This list specifies whether a particular binary from $PATH is allowed to be
 // run during the build. For more documentation, see path_interposer.go .
 var Configuration = map[string]PathConfig{
-	"bash":    Allowed,
-	"dd":      Allowed,
-	"diff":    Allowed,
-	"dlv":     Allowed,
-	"expr":    Allowed,
-	"fuser":   Allowed,
-	"getopt":  Allowed,
-	"git":     Allowed,
-	"hexdump": Allowed,
-	"jar":     Allowed,
-	"java":    Allowed,
-	"javap":   Allowed,
-	"lsof":    Allowed,
-	"openssl": Allowed,
-	"pstree":  Allowed,
-	"rsync":   Allowed,
-	"sh":      Allowed,
-	"stubby":  Allowed,
-	"tr":      Allowed,
-	"unzip":   Allowed,
-	"zip":     Allowed,
+	"bash":           Allowed,
+	"dd":             Allowed,
+	"diff":           Allowed,
+	"dlv":            Allowed,
+	"expr":           Allowed,
+	"fuser":          Allowed,
+	"gcert":          Allowed,
+	"getopt":         Allowed,
+	"git":            Allowed,
+	"hexdump":        Allowed,
+	"jar":            Allowed,
+	"java":           Allowed,
+	"javap":          Allowed,
+	"lsof":           Allowed,
+	"openssl":        Allowed,
+	"prodcertstatus": Allowed,
+	"pstree":         Allowed,
+	"rsync":          Allowed,
+	"sh":             Allowed,
+	"stubby":         Allowed,
+	"tr":             Allowed,
+	"unzip":          Allowed,
+	"zip":            Allowed,
 
 	// Host toolchain is removed. In-tree toolchain should be used instead.
 	// GCC also can't find cc1 with this implementation.
diff --git a/ui/build/rbe.go b/ui/build/rbe.go
index 1d17216..3b9d301 100644
--- a/ui/build/rbe.go
+++ b/ui/build/rbe.go
@@ -60,6 +60,7 @@
 		"RBE_exec_root":     config.rbeExecRoot(),
 		"RBE_output_dir":    config.rbeProxyLogsDir(),
 		"RBE_proxy_log_dir": config.rbeProxyLogsDir(),
+		"RBE_cache_dir":     config.rbeCacheDir(),
 		"RBE_platform":      "container-image=" + remoteexec.DefaultImage,
 	}
 	if config.StartRBE() {
@@ -182,8 +183,6 @@
 		return
 	}
 
-	ctx.Status.Status("Dumping rbe metrics...")
-
 	outputDir := config.rbeProxyLogsDir()
 	if outputDir == "" {
 		ctx.Fatal("RBE output dir variable not defined. Aborting metrics dumping.")
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 9287731..b14208e 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -235,7 +235,7 @@
 	} 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"))
+		os.Remove(config.SoongNinjaFile())
 		for _, globFile := range bootstrapGlobFileList(config) {
 			os.Remove(globFile)
 		}
@@ -263,7 +263,9 @@
 	// Clean up some files for incremental builds across incompatible changes.
 	bootstrapEpochCleanup(ctx, config)
 
-	mainSoongBuildExtraArgs := []string{"-o", config.SoongNinjaFile()}
+	baseArgs := []string{"--soong_variables", config.SoongVarsFile()}
+
+	mainSoongBuildExtraArgs := append(baseArgs, "-o", config.SoongNinjaFile())
 	if config.EmptyNinjaFile() {
 		mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--empty-ninja-file")
 	}
@@ -288,6 +290,9 @@
 	if config.buildFromTextStub {
 		mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--build-from-text-stub")
 	}
+	if config.ensureAllowlistIntegrity {
+		mainSoongBuildExtraArgs = append(mainSoongBuildExtraArgs, "--ensure-allowlist-integrity")
+	}
 
 	queryviewDir := filepath.Join(config.SoongOutDir(), "queryview")
 	// The BUILD files will be generated in out/soong/.api_bp2build (no symlinks to src files)
@@ -303,49 +308,59 @@
 			specificArgs: mainSoongBuildExtraArgs,
 		},
 		{
-			name:         bp2buildFilesTag,
-			description:  fmt.Sprintf("converting Android.bp files to BUILD files at %s/bp2build", config.SoongOutDir()),
-			config:       config,
-			output:       config.Bp2BuildFilesMarkerFile(),
-			specificArgs: []string{"--bp2build_marker", config.Bp2BuildFilesMarkerFile()},
+			name:        bp2buildFilesTag,
+			description: fmt.Sprintf("converting Android.bp files to BUILD files at %s/bp2build", config.SoongOutDir()),
+			config:      config,
+			output:      config.Bp2BuildFilesMarkerFile(),
+			specificArgs: append(baseArgs,
+				"--bp2build_marker", config.Bp2BuildFilesMarkerFile(),
+			),
 		},
 		{
-			name:         bp2buildWorkspaceTag,
-			description:  "Creating Bazel symlink forest",
-			config:       config,
-			output:       config.Bp2BuildWorkspaceMarkerFile(),
-			specificArgs: []string{"--symlink_forest_marker", config.Bp2BuildWorkspaceMarkerFile()},
+			name:        bp2buildWorkspaceTag,
+			description: "Creating Bazel symlink forest",
+			config:      config,
+			output:      config.Bp2BuildWorkspaceMarkerFile(),
+			specificArgs: append(baseArgs,
+				"--symlink_forest_marker", config.Bp2BuildWorkspaceMarkerFile(),
+			),
 		},
 		{
 			name:        jsonModuleGraphTag,
 			description: fmt.Sprintf("generating the Soong module graph at %s", config.ModuleGraphFile()),
 			config:      config,
 			output:      config.ModuleGraphFile(),
-			specificArgs: []string{
+			specificArgs: append(baseArgs,
 				"--module_graph_file", config.ModuleGraphFile(),
 				"--module_actions_file", config.ModuleActionsFile(),
-			},
+			),
 		},
 		{
-			name:         queryviewTag,
-			description:  fmt.Sprintf("generating the Soong module graph as a Bazel workspace at %s", queryviewDir),
-			config:       config,
-			output:       config.QueryviewMarkerFile(),
-			specificArgs: []string{"--bazel_queryview_dir", queryviewDir},
+			name:        queryviewTag,
+			description: fmt.Sprintf("generating the Soong module graph as a Bazel workspace at %s", queryviewDir),
+			config:      config,
+			output:      config.QueryviewMarkerFile(),
+			specificArgs: append(baseArgs,
+				"--bazel_queryview_dir", queryviewDir,
+			),
 		},
 		{
-			name:         apiBp2buildTag,
-			description:  fmt.Sprintf("generating BUILD files for API contributions at %s", apiBp2buildDir),
-			config:       config,
-			output:       config.ApiBp2buildMarkerFile(),
-			specificArgs: []string{"--bazel_api_bp2build_dir", apiBp2buildDir},
+			name:        apiBp2buildTag,
+			description: fmt.Sprintf("generating BUILD files for API contributions at %s", apiBp2buildDir),
+			config:      config,
+			output:      config.ApiBp2buildMarkerFile(),
+			specificArgs: append(baseArgs,
+				"--bazel_api_bp2build_dir", apiBp2buildDir,
+			),
 		},
 		{
-			name:         soongDocsTag,
-			description:  fmt.Sprintf("generating Soong docs at %s", config.SoongDocsHtml()),
-			config:       config,
-			output:       config.SoongDocsHtml(),
-			specificArgs: []string{"--soong_docs", config.SoongDocsHtml()},
+			name:        soongDocsTag,
+			description: fmt.Sprintf("generating Soong docs at %s", config.SoongDocsHtml()),
+			config:      config,
+			output:      config.SoongDocsHtml(),
+			specificArgs: append(baseArgs,
+				"--soong_docs", config.SoongDocsHtml(),
+			),
 		},
 	}
 
@@ -397,6 +412,8 @@
 			pbi.Inputs = append(pbi.Inputs,
 				config.Bp2BuildFilesMarkerFile(),
 				filepath.Join(config.FileListDir(), "bazel.list"))
+		case bp2buildFilesTag:
+			pbi.Inputs = append(pbi.Inputs, filepath.Join(config.FileListDir(), "METADATA.list"))
 		}
 		invocations = append(invocations, pbi)
 	}
@@ -434,13 +451,17 @@
 	_ = bootstrap.RunBlueprint(blueprintArgs, bootstrap.DoEverything, blueprintCtx, blueprintConfig)
 }
 
-func checkEnvironmentFile(currentEnv *Environment, envFile string) {
+func checkEnvironmentFile(ctx Context, currentEnv *Environment, envFile string) {
 	getenv := func(k string) string {
 		v, _ := currentEnv.Get(k)
 		return v
 	}
 
-	if stale, _ := shared.StaleEnvFile(envFile, getenv); stale {
+	// Log the changed environment variables to ChangedEnvironmentVariable field
+	if stale, changedEnvironmentVariableList, _ := shared.StaleEnvFile(envFile, getenv); stale {
+		for _, changedEnvironmentVariable := range changedEnvironmentVariableList {
+			ctx.Metrics.AddChangedEnvironmentVariable(changedEnvironmentVariable)
+		}
 		os.Remove(envFile)
 	}
 }
@@ -485,26 +506,26 @@
 		ctx.BeginTrace(metrics.RunSoong, "environment check")
 		defer ctx.EndTrace()
 
-		checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(soongBuildTag))
+		checkEnvironmentFile(ctx, soongBuildEnv, config.UsedEnvFile(soongBuildTag))
 
 		if config.BazelBuildEnabled() || config.Bp2Build() {
-			checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(bp2buildFilesTag))
+			checkEnvironmentFile(ctx, soongBuildEnv, config.UsedEnvFile(bp2buildFilesTag))
 		}
 
 		if config.JsonModuleGraph() {
-			checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(jsonModuleGraphTag))
+			checkEnvironmentFile(ctx, soongBuildEnv, config.UsedEnvFile(jsonModuleGraphTag))
 		}
 
 		if config.Queryview() {
-			checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(queryviewTag))
+			checkEnvironmentFile(ctx, soongBuildEnv, config.UsedEnvFile(queryviewTag))
 		}
 
 		if config.ApiBp2build() {
-			checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(apiBp2buildTag))
+			checkEnvironmentFile(ctx, soongBuildEnv, config.UsedEnvFile(apiBp2buildTag))
 		}
 
 		if config.SoongDocs() {
-			checkEnvironmentFile(soongBuildEnv, config.UsedEnvFile(soongDocsTag))
+			checkEnvironmentFile(ctx, soongBuildEnv, config.UsedEnvFile(soongDocsTag))
 		}
 	}()
 
@@ -516,7 +537,7 @@
 		defer ctx.EndTrace()
 
 		if config.IsPersistentBazelEnabled() {
-			bazelProxy := bazel.NewProxyServer(ctx.Logger, config.OutDir(), filepath.Join(config.SoongOutDir(), "workspace"))
+			bazelProxy := bazel.NewProxyServer(ctx.Logger, config.OutDir(), filepath.Join(config.SoongOutDir(), "workspace"), config.GetBazeliskBazelVersion())
 			bazelProxy.Start()
 			defer bazelProxy.Close()
 		}
diff --git a/ui/build/upload.go b/ui/build/upload.go
index 9959e6f..9f14bdd 100644
--- a/ui/build/upload.go
+++ b/ui/build/upload.go
@@ -18,21 +18,16 @@
 // another.
 
 import (
-	"bufio"
 	"fmt"
 	"io/ioutil"
 	"os"
 	"path/filepath"
-	"strconv"
-	"strings"
 	"time"
 
-	"android/soong/shared"
 	"android/soong/ui/metrics"
 
 	"google.golang.org/protobuf/proto"
 
-	bazel_metrics_proto "android/soong/ui/metrics/bazel_metrics_proto"
 	upload_proto "android/soong/ui/metrics/upload_proto"
 )
 
@@ -78,113 +73,12 @@
 	return metricsFiles
 }
 
-func parseTimingToNanos(str string) int64 {
-	millisString := removeDecimalPoint(str)
-	timingMillis, _ := strconv.ParseInt(millisString, 10, 64)
-	return timingMillis * 1000000
-}
-
-func parsePercentageToTenThousandths(str string) int32 {
-	percentageString := removeDecimalPoint(str)
-	//remove the % at the end of the string
-	percentage := strings.ReplaceAll(percentageString, "%", "")
-	percentagePortion, _ := strconv.ParseInt(percentage, 10, 32)
-	return int32(percentagePortion)
-}
-
-func removeDecimalPoint(numString string) string {
-	// The format is always 0.425 or 10.425
-	return strings.ReplaceAll(numString, ".", "")
-}
-
-func parseTotal(line string) int64 {
-	words := strings.Fields(line)
-	timing := words[3]
-	return parseTimingToNanos(timing)
-}
-
-func parsePhaseTiming(line string) bazel_metrics_proto.PhaseTiming {
-	words := strings.Fields(line)
-	getPhaseNameAndTimingAndPercentage := func([]string) (string, int64, int32) {
-		// Sample lines include:
-		// Total launch phase time   0.011 s    2.59%
-		// Total target pattern evaluation phase time  0.011 s    2.59%
-		var beginning int
-		var end int
-		for ind, word := range words {
-			if word == "Total" {
-				beginning = ind + 1
-			} else if beginning > 0 && word == "phase" {
-				end = ind
-				break
-			}
-		}
-		phaseName := strings.Join(words[beginning:end], " ")
-
-		// end is now "phase" - advance by 2 for timing and 4 for percentage
-		percentageString := words[end+4]
-		timingString := words[end+2]
-		timing := parseTimingToNanos(timingString)
-		percentagePortion := parsePercentageToTenThousandths(percentageString)
-		return phaseName, timing, percentagePortion
-	}
-
-	phaseName, timing, portion := getPhaseNameAndTimingAndPercentage(words)
-	phaseTiming := bazel_metrics_proto.PhaseTiming{}
-	phaseTiming.DurationNanos = &timing
-	phaseTiming.PortionOfBuildTime = &portion
-
-	phaseTiming.PhaseName = &phaseName
-	return phaseTiming
-}
-
-func processBazelMetrics(bazelProfileFile string, bazelMetricsFile string, ctx Context) {
-	if bazelProfileFile == "" {
-		return
-	}
-
-	readBazelProto := func(filepath string) bazel_metrics_proto.BazelMetrics {
-		//serialize the proto, write it
-		bazelMetrics := bazel_metrics_proto.BazelMetrics{}
-
-		file, err := os.ReadFile(filepath)
-		if err != nil {
-			ctx.Fatalln("Error reading metrics file\n", err)
-		}
-
-		scanner := bufio.NewScanner(strings.NewReader(string(file)))
-		scanner.Split(bufio.ScanLines)
-
-		var phaseTimings []*bazel_metrics_proto.PhaseTiming
-		for scanner.Scan() {
-			line := scanner.Text()
-			if strings.HasPrefix(line, "Total run time") {
-				total := parseTotal(line)
-				bazelMetrics.Total = &total
-			} else if strings.HasPrefix(line, "Total") {
-				phaseTiming := parsePhaseTiming(line)
-				phaseTimings = append(phaseTimings, &phaseTiming)
-			}
-		}
-		bazelMetrics.PhaseTimings = phaseTimings
-
-		return bazelMetrics
-	}
-
-	if _, err := os.Stat(bazelProfileFile); err != nil {
-		// We can assume bazel didn't run if the profile doesn't exist
-		return
-	}
-	bazelProto := readBazelProto(bazelProfileFile)
-	shared.Save(&bazelProto, bazelMetricsFile)
-}
-
 // UploadMetrics uploads a set of metrics files to a server for analysis.
 // The metrics files are first copied to a temporary directory
 // and the uploader is then executed in the background to allow the user/system
 // to continue working. Soong communicates to the uploader through the
 // upload_proto raw protobuf file.
-func UploadMetrics(ctx Context, config Config, simpleOutput bool, buildStarted time.Time, bazelProfileFile string, bazelMetricsFile string, paths ...string) {
+func UploadMetrics(ctx Context, config Config, simpleOutput bool, buildStarted time.Time, paths ...string) {
 	ctx.BeginTrace(metrics.RunSetupTool, "upload_metrics")
 	defer ctx.EndTrace()
 
@@ -194,7 +88,6 @@
 		return
 	}
 
-	processBazelMetrics(bazelProfileFile, bazelMetricsFile, ctx)
 	// Several of the files might be directories.
 	metricsFiles := pruneMetricsFiles(paths)
 	if len(metricsFiles) == 0 {
diff --git a/ui/build/upload_test.go b/ui/build/upload_test.go
index 58d9237..1fcded9 100644
--- a/ui/build/upload_test.go
+++ b/ui/build/upload_test.go
@@ -166,7 +166,7 @@
 				metricsUploader: tt.uploader,
 			}}
 
-			UploadMetrics(ctx, config, false, time.Now(), "out/bazel_metrics.txt", "out/bazel_metrics.pb", metricsFiles...)
+			UploadMetrics(ctx, config, false, time.Now(), metricsFiles...)
 		})
 	}
 }
@@ -221,7 +221,7 @@
 				metricsUploader: "echo",
 			}}
 
-			UploadMetrics(ctx, config, true, time.Now(), "", "", metricsFile)
+			UploadMetrics(ctx, config, true, time.Now(), metricsFile)
 			t.Errorf("got nil, expecting %q as a failure", tt.expectedErr)
 		})
 	}
diff --git a/ui/metrics/BUILD.bazel b/ui/metrics/BUILD.bazel
index 15ebb88..ca39c59 100644
--- a/ui/metrics/BUILD.bazel
+++ b/ui/metrics/BUILD.bazel
@@ -16,13 +16,14 @@
 
 py_proto_library(
     name = "metrics-py-proto",
-    visibility = ["//build/bazel/scripts/incremental_build:__pkg__"],
+    visibility = ["//build/bazel/scripts:__subpackages__"],
     deps = [":metrics-proto"],
 )
 
 proto_library(
     name = "metrics-proto",
     srcs = [
+        "bazel_metrics_proto/bazel_metrics.proto",
         "bp2build_metrics_proto/bp2build_metrics.proto",
         "metrics_proto/metrics.proto",
     ],
diff --git a/ui/metrics/bazel_metrics_proto/bazel_metrics.pb.go b/ui/metrics/bazel_metrics_proto/bazel_metrics.pb.go
index f8b8fd6..bf5e80b 100644
--- a/ui/metrics/bazel_metrics_proto/bazel_metrics.pb.go
+++ b/ui/metrics/bazel_metrics_proto/bazel_metrics.pb.go
@@ -14,8 +14,8 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.28.0
-// 	protoc        v3.21.7
+// 	protoc-gen-go v1.30.0
+// 	protoc        v3.21.12
 // source: bazel_metrics.proto
 
 package bazel_metrics_proto
@@ -41,6 +41,7 @@
 
 	PhaseTimings []*PhaseTiming `protobuf:"bytes,1,rep,name=phase_timings,json=phaseTimings,proto3" json:"phase_timings,omitempty"`
 	Total        *int64         `protobuf:"varint,2,opt,name=total,proto3,oneof" json:"total,omitempty"`
+	ExitCode     *int32         `protobuf:"varint,3,opt,name=exit_code,json=exitCode,proto3,oneof" json:"exit_code,omitempty"`
 }
 
 func (x *BazelMetrics) Reset() {
@@ -89,6 +90,13 @@
 	return 0
 }
 
+func (x *BazelMetrics) GetExitCode() int32 {
+	if x != nil && x.ExitCode != nil {
+		return *x.ExitCode
+	}
+	return 0
+}
+
 type PhaseTiming struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -161,15 +169,18 @@
 	0x0a, 0x13, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e,
 	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x19, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69,
 	0x6c, 0x64, 0x5f, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
-	0x22, 0x80, 0x01, 0x0a, 0x0c, 0x42, 0x61, 0x7a, 0x65, 0x6c, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63,
+	0x22, 0xb0, 0x01, 0x0a, 0x0c, 0x42, 0x61, 0x7a, 0x65, 0x6c, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63,
 	0x73, 0x12, 0x4b, 0x0a, 0x0d, 0x70, 0x68, 0x61, 0x73, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x69, 0x6e,
 	0x67, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67,
 	0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x5f, 0x6d, 0x65, 0x74,
 	0x72, 0x69, 0x63, 0x73, 0x2e, 0x50, 0x68, 0x61, 0x73, 0x65, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67,
 	0x52, 0x0c, 0x70, 0x68, 0x61, 0x73, 0x65, 0x54, 0x69, 0x6d, 0x69, 0x6e, 0x67, 0x73, 0x12, 0x19,
 	0x0a, 0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x48, 0x00, 0x52,
-	0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x88, 0x01, 0x01, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x74, 0x6f,
-	0x74, 0x61, 0x6c, 0x22, 0xd1, 0x01, 0x0a, 0x0b, 0x50, 0x68, 0x61, 0x73, 0x65, 0x54, 0x69, 0x6d,
+	0x05, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x88, 0x01, 0x01, 0x12, 0x20, 0x0a, 0x09, 0x65, 0x78, 0x69,
+	0x74, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x48, 0x01, 0x52, 0x08,
+	0x65, 0x78, 0x69, 0x74, 0x43, 0x6f, 0x64, 0x65, 0x88, 0x01, 0x01, 0x42, 0x08, 0x0a, 0x06, 0x5f,
+	0x74, 0x6f, 0x74, 0x61, 0x6c, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x5f, 0x63,
+	0x6f, 0x64, 0x65, 0x22, 0xd1, 0x01, 0x0a, 0x0b, 0x50, 0x68, 0x61, 0x73, 0x65, 0x54, 0x69, 0x6d,
 	0x69, 0x6e, 0x67, 0x12, 0x22, 0x0a, 0x0a, 0x70, 0x68, 0x61, 0x73, 0x65, 0x5f, 0x6e, 0x61, 0x6d,
 	0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x09, 0x70, 0x68, 0x61, 0x73, 0x65,
 	0x4e, 0x61, 0x6d, 0x65, 0x88, 0x01, 0x01, 0x12, 0x2a, 0x0a, 0x0e, 0x64, 0x75, 0x72, 0x61, 0x74,
diff --git a/ui/metrics/bazel_metrics_proto/bazel_metrics.proto b/ui/metrics/bazel_metrics_proto/bazel_metrics.proto
index 57eed4c..9073080 100644
--- a/ui/metrics/bazel_metrics_proto/bazel_metrics.proto
+++ b/ui/metrics/bazel_metrics_proto/bazel_metrics.proto
@@ -20,6 +20,7 @@
 message BazelMetrics {
   repeated PhaseTiming phase_timings = 1;
   optional int64 total = 2;
+  optional int32 exit_code = 3;
 }
 
 message PhaseTiming {
diff --git a/ui/metrics/metrics.go b/ui/metrics/metrics.go
index 82d11ed..6d60316 100644
--- a/ui/metrics/metrics.go
+++ b/ui/metrics/metrics.go
@@ -228,7 +228,7 @@
 	m.metrics.BuildDateTimestamp = proto.Int64(buildTimestamp.UnixNano() / int64(time.Second))
 }
 
-func (m *Metrics) UpdateTotalRealTime(data []byte) error {
+func (m *Metrics) UpdateTotalRealTimeAndNonZeroExit(data []byte, bazelExitCode int32) error {
 	if err := proto.Unmarshal(data, &m.metrics); err != nil {
 		return fmt.Errorf("Failed to unmarshal proto", err)
 	}
@@ -236,6 +236,9 @@
 	endTime := uint64(time.Now().UnixNano())
 
 	*m.metrics.Total.RealTime = *proto.Uint64(endTime - startTime)
+
+	bazelError := bazelExitCode != 0
+	m.metrics.NonZeroExit = proto.Bool(bazelError)
 	return nil
 }
 
@@ -245,6 +248,13 @@
 	m.metrics.BuildCommand = proto.String(strings.Join(cmd, " "))
 }
 
+// AddChangedEnvironmentVariable adds the changed environment variable to
+// ChangedEnvironmentVariable field.
+func (m *Metrics) AddChangedEnvironmentVariable(ChangedEnvironmentVariable string) {
+	m.metrics.ChangedEnvironmentVariable = append(m.metrics.ChangedEnvironmentVariable,
+		ChangedEnvironmentVariable)
+}
+
 // Dump exports the collected metrics from the executed build to the file at
 // out path.
 func (m *Metrics) Dump(out string) error {
diff --git a/ui/metrics/metrics_proto/metrics.pb.go b/ui/metrics/metrics_proto/metrics.pb.go
index 32d4dc0..f3e677a 100644
--- a/ui/metrics/metrics_proto/metrics.pb.go
+++ b/ui/metrics/metrics_proto/metrics.pb.go
@@ -15,7 +15,7 @@
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
 // 	protoc-gen-go v1.30.0
-// 	protoc        v3.21.7
+// 	protoc        v3.21.12
 // source: metrics.proto
 
 package metrics_proto
@@ -420,6 +420,11 @@
 	Branch *string `protobuf:"bytes,32,opt,name=branch" json:"branch,omitempty"`
 	// The metric of critical path in build
 	CriticalPathInfo *CriticalPathInfo `protobuf:"bytes,33,opt,name=critical_path_info,json=criticalPathInfo" json:"critical_path_info,omitempty"`
+	// Environment variables that have changed value since the previous build,
+	// which were responsible for retriggering build analysis.
+	// Note that not all changed environment variables result in analysis retriggering.
+	// If there was no previous build, this list will be empty.
+	ChangedEnvironmentVariable []string `protobuf:"bytes,34,rep,name=changed_environment_variable,json=changedEnvironmentVariable" json:"changed_environment_variable,omitempty"`
 }
 
 // Default values for MetricsBase fields.
@@ -694,6 +699,13 @@
 	return nil
 }
 
+func (x *MetricsBase) GetChangedEnvironmentVariable() []string {
+	if x != nil {
+		return x.ChangedEnvironmentVariable
+	}
+	return nil
+}
+
 type BuildConfig struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -1659,7 +1671,7 @@
 var file_metrics_proto_rawDesc = []byte{
 	0x0a, 0x0d, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
 	0x13, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74,
-	0x72, 0x69, 0x63, 0x73, 0x22, 0x8a, 0x0f, 0x0a, 0x0b, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
+	0x72, 0x69, 0x63, 0x73, 0x22, 0xcc, 0x0f, 0x0a, 0x0b, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
 	0x42, 0x61, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x14, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x64, 0x61,
 	0x74, 0x65, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x01, 0x20, 0x01,
 	0x28, 0x03, 0x52, 0x12, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x44, 0x61, 0x74, 0x65, 0x54, 0x69, 0x6d,
@@ -1773,199 +1785,203 @@
 	0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x43,
 	0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x52,
 	0x10, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x49, 0x6e, 0x66,
-	0x6f, 0x22, 0x30, 0x0a, 0x0c, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x56, 0x61, 0x72, 0x69, 0x61, 0x6e,
-	0x74, 0x12, 0x08, 0x0a, 0x04, 0x55, 0x53, 0x45, 0x52, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x55,
-	0x53, 0x45, 0x52, 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x45, 0x4e,
-	0x47, 0x10, 0x02, 0x22, 0x3c, 0x0a, 0x04, 0x41, 0x72, 0x63, 0x68, 0x12, 0x0b, 0x0a, 0x07, 0x55,
-	0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x52, 0x4d, 0x10,
-	0x01, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x52, 0x4d, 0x36, 0x34, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03,
-	0x58, 0x38, 0x36, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x58, 0x38, 0x36, 0x5f, 0x36, 0x34, 0x10,
-	0x04, 0x22, 0x8a, 0x04, 0x0a, 0x0b, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69,
-	0x67, 0x12, 0x19, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x5f, 0x67, 0x6f, 0x6d, 0x61, 0x18, 0x01, 0x20,
-	0x01, 0x28, 0x08, 0x52, 0x07, 0x75, 0x73, 0x65, 0x47, 0x6f, 0x6d, 0x61, 0x12, 0x17, 0x0a, 0x07,
-	0x75, 0x73, 0x65, 0x5f, 0x72, 0x62, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x75,
-	0x73, 0x65, 0x52, 0x62, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x5f, 0x75,
-	0x73, 0x65, 0x5f, 0x67, 0x6f, 0x6d, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x66,
-	0x6f, 0x72, 0x63, 0x65, 0x55, 0x73, 0x65, 0x47, 0x6f, 0x6d, 0x61, 0x12, 0x24, 0x0a, 0x0e, 0x62,
-	0x61, 0x7a, 0x65, 0x6c, 0x5f, 0x61, 0x73, 0x5f, 0x6e, 0x69, 0x6e, 0x6a, 0x61, 0x18, 0x04, 0x20,
-	0x01, 0x28, 0x08, 0x52, 0x0c, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x41, 0x73, 0x4e, 0x69, 0x6e, 0x6a,
-	0x61, 0x12, 0x2a, 0x0a, 0x11, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x5f, 0x6d, 0x69, 0x78, 0x65, 0x64,
-	0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f, 0x62, 0x61,
-	0x7a, 0x65, 0x6c, 0x4d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x18, 0x0a,
-	0x07, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07,
-	0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x12, 0x44, 0x0a, 0x1f, 0x66, 0x6f, 0x72, 0x63, 0x65,
-	0x5f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x5f, 0x6d,
-	0x69, 0x78, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08,
-	0x52, 0x1b, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x42, 0x61,
-	0x7a, 0x65, 0x6c, 0x4d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x79, 0x0a,
-	0x18, 0x6e, 0x69, 0x6e, 0x6a, 0x61, 0x5f, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x5f, 0x6c, 0x69,
-	0x73, 0x74, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0e, 0x32,
-	0x36, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65,
-	0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e, 0x66, 0x69,
-	0x67, 0x2e, 0x4e, 0x69, 0x6e, 0x6a, 0x61, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x4c, 0x69, 0x73,
-	0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x3a, 0x08, 0x4e, 0x4f, 0x54, 0x5f, 0x55, 0x53, 0x45,
-	0x44, 0x52, 0x15, 0x6e, 0x69, 0x6e, 0x6a, 0x61, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x4c, 0x69,
-	0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x74, 0x0a, 0x15, 0x4e, 0x69, 0x6e, 0x6a,
-	0x61, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63,
-	0x65, 0x12, 0x0c, 0x0a, 0x08, 0x4e, 0x4f, 0x54, 0x5f, 0x55, 0x53, 0x45, 0x44, 0x10, 0x00, 0x12,
-	0x0d, 0x0a, 0x09, 0x4e, 0x49, 0x4e, 0x4a, 0x41, 0x5f, 0x4c, 0x4f, 0x47, 0x10, 0x01, 0x12, 0x16,
-	0x0a, 0x12, 0x45, 0x56, 0x45, 0x4e, 0x4c, 0x59, 0x5f, 0x44, 0x49, 0x53, 0x54, 0x52, 0x49, 0x42,
-	0x55, 0x54, 0x45, 0x44, 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x45, 0x58, 0x54, 0x45, 0x52, 0x4e,
-	0x41, 0x4c, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x03, 0x12, 0x13, 0x0a, 0x0f, 0x48, 0x49, 0x4e,
-	0x54, 0x5f, 0x46, 0x52, 0x4f, 0x4d, 0x5f, 0x53, 0x4f, 0x4f, 0x4e, 0x47, 0x10, 0x04, 0x22, 0x6f,
-	0x0a, 0x12, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
-	0x49, 0x6e, 0x66, 0x6f, 0x12, 0x32, 0x0a, 0x15, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x70, 0x68,
-	0x79, 0x73, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20,
-	0x01, 0x28, 0x04, 0x52, 0x13, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x50, 0x68, 0x79, 0x73, 0x69, 0x63,
-	0x61, 0x6c, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x76, 0x61, 0x69,
-	0x6c, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x63, 0x70, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05,
-	0x52, 0x0d, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x70, 0x75, 0x73, 0x22,
-	0xca, 0x02, 0x0a, 0x08, 0x50, 0x65, 0x72, 0x66, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x20, 0x0a, 0x0b,
-	0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28,
-	0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12,
-	0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
-	0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65,
-	0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54, 0x69, 0x6d,
-	0x65, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x61, 0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x04,
-	0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x72, 0x65, 0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x21,
-	0x0a, 0x0a, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x75, 0x73, 0x65, 0x18, 0x05, 0x20, 0x01,
-	0x28, 0x04, 0x42, 0x02, 0x18, 0x01, 0x52, 0x09, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x55, 0x73,
-	0x65, 0x12, 0x60, 0x0a, 0x17, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x73, 0x5f, 0x72,
-	0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x06, 0x20, 0x03,
-	0x28, 0x0b, 0x32, 0x28, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64,
-	0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73,
-	0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x15, 0x70, 0x72,
-	0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49,
-	0x6e, 0x66, 0x6f, 0x12, 0x22, 0x0a, 0x0d, 0x6e, 0x6f, 0x6e, 0x5f, 0x7a, 0x65, 0x72, 0x6f, 0x5f,
-	0x65, 0x78, 0x69, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x6e, 0x6f, 0x6e, 0x5a,
-	0x65, 0x72, 0x6f, 0x45, 0x78, 0x69, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72, 0x6f, 0x72,
-	0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c,
-	0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0xb9, 0x03, 0x0a,
-	0x13, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
-	0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01,
-	0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x75, 0x73, 0x65, 0x72,
-	0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18, 0x02, 0x20, 0x01,
-	0x28, 0x04, 0x52, 0x0e, 0x75, 0x73, 0x65, 0x72, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x69, 0x63, 0x72,
-	0x6f, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x74, 0x69, 0x6d,
-	0x65, 0x5f, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x10,
-	0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73,
-	0x12, 0x1c, 0x0a, 0x0a, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x73, 0x73, 0x5f, 0x6b, 0x62, 0x18, 0x04,
-	0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x52, 0x73, 0x73, 0x4b, 0x62, 0x12, 0x2a,
-	0x0a, 0x11, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x66, 0x61, 0x75,
-	0x6c, 0x74, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x6d, 0x69, 0x6e, 0x6f, 0x72,
-	0x50, 0x61, 0x67, 0x65, 0x46, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x61,
-	0x6a, 0x6f, 0x72, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x18,
-	0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x50, 0x61, 0x67, 0x65,
-	0x46, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x1e, 0x0a, 0x0b, 0x69, 0x6f, 0x5f, 0x69, 0x6e, 0x70,
-	0x75, 0x74, 0x5f, 0x6b, 0x62, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x69, 0x6f, 0x49,
-	0x6e, 0x70, 0x75, 0x74, 0x4b, 0x62, 0x12, 0x20, 0x0a, 0x0c, 0x69, 0x6f, 0x5f, 0x6f, 0x75, 0x74,
-	0x70, 0x75, 0x74, 0x5f, 0x6b, 0x62, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x69, 0x6f,
-	0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x4b, 0x62, 0x12, 0x3c, 0x0a, 0x1a, 0x76, 0x6f, 0x6c, 0x75,
-	0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x73, 0x77,
-	0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x18, 0x76, 0x6f,
-	0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x53, 0x77,
-	0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x12, 0x40, 0x0a, 0x1c, 0x69, 0x6e, 0x76, 0x6f, 0x6c, 0x75,
-	0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f, 0x73, 0x77,
-	0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x1a, 0x69, 0x6e,
+	0x6f, 0x12, 0x40, 0x0a, 0x1c, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x5f, 0x65, 0x6e, 0x76,
+	0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c,
+	0x65, 0x18, 0x22, 0x20, 0x03, 0x28, 0x09, 0x52, 0x1a, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64,
+	0x45, 0x6e, 0x76, 0x69, 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0x74, 0x56, 0x61, 0x72, 0x69, 0x61,
+	0x62, 0x6c, 0x65, 0x22, 0x30, 0x0a, 0x0c, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x56, 0x61, 0x72, 0x69,
+	0x61, 0x6e, 0x74, 0x12, 0x08, 0x0a, 0x04, 0x55, 0x53, 0x45, 0x52, 0x10, 0x00, 0x12, 0x0d, 0x0a,
+	0x09, 0x55, 0x53, 0x45, 0x52, 0x44, 0x45, 0x42, 0x55, 0x47, 0x10, 0x01, 0x12, 0x07, 0x0a, 0x03,
+	0x45, 0x4e, 0x47, 0x10, 0x02, 0x22, 0x3c, 0x0a, 0x04, 0x41, 0x72, 0x63, 0x68, 0x12, 0x0b, 0x0a,
+	0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x41, 0x52,
+	0x4d, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x52, 0x4d, 0x36, 0x34, 0x10, 0x02, 0x12, 0x07,
+	0x0a, 0x03, 0x58, 0x38, 0x36, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x58, 0x38, 0x36, 0x5f, 0x36,
+	0x34, 0x10, 0x04, 0x22, 0x8a, 0x04, 0x0a, 0x0b, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e,
+	0x66, 0x69, 0x67, 0x12, 0x19, 0x0a, 0x08, 0x75, 0x73, 0x65, 0x5f, 0x67, 0x6f, 0x6d, 0x61, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x75, 0x73, 0x65, 0x47, 0x6f, 0x6d, 0x61, 0x12, 0x17,
+	0x0a, 0x07, 0x75, 0x73, 0x65, 0x5f, 0x72, 0x62, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52,
+	0x06, 0x75, 0x73, 0x65, 0x52, 0x62, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x66, 0x6f, 0x72, 0x63, 0x65,
+	0x5f, 0x75, 0x73, 0x65, 0x5f, 0x67, 0x6f, 0x6d, 0x61, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52,
+	0x0c, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x55, 0x73, 0x65, 0x47, 0x6f, 0x6d, 0x61, 0x12, 0x24, 0x0a,
+	0x0e, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x5f, 0x61, 0x73, 0x5f, 0x6e, 0x69, 0x6e, 0x6a, 0x61, 0x18,
+	0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x41, 0x73, 0x4e, 0x69,
+	0x6e, 0x6a, 0x61, 0x12, 0x2a, 0x0a, 0x11, 0x62, 0x61, 0x7a, 0x65, 0x6c, 0x5f, 0x6d, 0x69, 0x78,
+	0x65, 0x64, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0f,
+	0x62, 0x61, 0x7a, 0x65, 0x6c, 0x4d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12,
+	0x18, 0x0a, 0x07, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x09,
+	0x52, 0x07, 0x74, 0x61, 0x72, 0x67, 0x65, 0x74, 0x73, 0x12, 0x44, 0x0a, 0x1f, 0x66, 0x6f, 0x72,
+	0x63, 0x65, 0x5f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x62, 0x61, 0x7a, 0x65, 0x6c,
+	0x5f, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x18, 0x07, 0x20, 0x01,
+	0x28, 0x08, 0x52, 0x1b, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65,
+	0x42, 0x61, 0x7a, 0x65, 0x6c, 0x4d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12,
+	0x79, 0x0a, 0x18, 0x6e, 0x69, 0x6e, 0x6a, 0x61, 0x5f, 0x77, 0x65, 0x69, 0x67, 0x68, 0x74, 0x5f,
+	0x6c, 0x69, 0x73, 0x74, 0x5f, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28,
+	0x0e, 0x32, 0x36, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f,
+	0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x43, 0x6f, 0x6e,
+	0x66, 0x69, 0x67, 0x2e, 0x4e, 0x69, 0x6e, 0x6a, 0x61, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x4c,
+	0x69, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x3a, 0x08, 0x4e, 0x4f, 0x54, 0x5f, 0x55,
+	0x53, 0x45, 0x44, 0x52, 0x15, 0x6e, 0x69, 0x6e, 0x6a, 0x61, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74,
+	0x4c, 0x69, 0x73, 0x74, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x22, 0x74, 0x0a, 0x15, 0x4e, 0x69,
+	0x6e, 0x6a, 0x61, 0x57, 0x65, 0x69, 0x67, 0x68, 0x74, 0x4c, 0x69, 0x73, 0x74, 0x53, 0x6f, 0x75,
+	0x72, 0x63, 0x65, 0x12, 0x0c, 0x0a, 0x08, 0x4e, 0x4f, 0x54, 0x5f, 0x55, 0x53, 0x45, 0x44, 0x10,
+	0x00, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x49, 0x4e, 0x4a, 0x41, 0x5f, 0x4c, 0x4f, 0x47, 0x10, 0x01,
+	0x12, 0x16, 0x0a, 0x12, 0x45, 0x56, 0x45, 0x4e, 0x4c, 0x59, 0x5f, 0x44, 0x49, 0x53, 0x54, 0x52,
+	0x49, 0x42, 0x55, 0x54, 0x45, 0x44, 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d, 0x45, 0x58, 0x54, 0x45,
+	0x52, 0x4e, 0x41, 0x4c, 0x5f, 0x46, 0x49, 0x4c, 0x45, 0x10, 0x03, 0x12, 0x13, 0x0a, 0x0f, 0x48,
+	0x49, 0x4e, 0x54, 0x5f, 0x46, 0x52, 0x4f, 0x4d, 0x5f, 0x53, 0x4f, 0x4f, 0x4e, 0x47, 0x10, 0x04,
+	0x22, 0x6f, 0x0a, 0x12, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72,
+	0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x32, 0x0a, 0x15, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f,
+	0x70, 0x68, 0x79, 0x73, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x13, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x50, 0x68, 0x79, 0x73,
+	0x69, 0x63, 0x61, 0x6c, 0x4d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x12, 0x25, 0x0a, 0x0e, 0x61, 0x76,
+	0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x63, 0x70, 0x75, 0x73, 0x18, 0x02, 0x20, 0x01,
+	0x28, 0x05, 0x52, 0x0d, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x6c, 0x65, 0x43, 0x70, 0x75,
+	0x73, 0x22, 0xca, 0x02, 0x0a, 0x08, 0x50, 0x65, 0x72, 0x66, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x20,
+	0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e,
+	0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
+	0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x72, 0x74, 0x5f, 0x74, 0x69,
+	0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x73, 0x74, 0x61, 0x72, 0x74, 0x54,
+	0x69, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x72, 0x65, 0x61, 0x6c, 0x5f, 0x74, 0x69, 0x6d, 0x65,
+	0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x72, 0x65, 0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65,
+	0x12, 0x21, 0x0a, 0x0a, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x5f, 0x75, 0x73, 0x65, 0x18, 0x05,
+	0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x18, 0x01, 0x52, 0x09, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79,
+	0x55, 0x73, 0x65, 0x12, 0x60, 0x0a, 0x17, 0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x73,
+	0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x06,
+	0x20, 0x03, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69,
+	0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x50, 0x72, 0x6f, 0x63, 0x65,
+	0x73, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x15,
+	0x70, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x65, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
+	0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x22, 0x0a, 0x0d, 0x6e, 0x6f, 0x6e, 0x5f, 0x7a, 0x65, 0x72,
+	0x6f, 0x5f, 0x65, 0x78, 0x69, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x6e, 0x6f,
+	0x6e, 0x5a, 0x65, 0x72, 0x6f, 0x45, 0x78, 0x69, 0x74, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x72, 0x72,
+	0x6f, 0x72, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0xb9,
+	0x03, 0x0a, 0x13, 0x50, 0x72, 0x6f, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72,
+	0x63, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x10, 0x75, 0x73,
+	0x65, 0x72, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18, 0x02,
+	0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x75, 0x73, 0x65, 0x72, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x69,
+	0x63, 0x72, 0x6f, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x5f, 0x74,
+	0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04,
+	0x52, 0x10, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x69, 0x63, 0x72,
+	0x6f, 0x73, 0x12, 0x1c, 0x0a, 0x0a, 0x6d, 0x61, 0x78, 0x5f, 0x72, 0x73, 0x73, 0x5f, 0x6b, 0x62,
+	0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6d, 0x61, 0x78, 0x52, 0x73, 0x73, 0x4b, 0x62,
+	0x12, 0x2a, 0x0a, 0x11, 0x6d, 0x69, 0x6e, 0x6f, 0x72, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x66,
+	0x61, 0x75, 0x6c, 0x74, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x6d, 0x69, 0x6e,
+	0x6f, 0x72, 0x50, 0x61, 0x67, 0x65, 0x46, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x2a, 0x0a, 0x11,
+	0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x66, 0x61, 0x75, 0x6c, 0x74,
+	0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x6d, 0x61, 0x6a, 0x6f, 0x72, 0x50, 0x61,
+	0x67, 0x65, 0x46, 0x61, 0x75, 0x6c, 0x74, 0x73, 0x12, 0x1e, 0x0a, 0x0b, 0x69, 0x6f, 0x5f, 0x69,
+	0x6e, 0x70, 0x75, 0x74, 0x5f, 0x6b, 0x62, 0x18, 0x07, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x69,
+	0x6f, 0x49, 0x6e, 0x70, 0x75, 0x74, 0x4b, 0x62, 0x12, 0x20, 0x0a, 0x0c, 0x69, 0x6f, 0x5f, 0x6f,
+	0x75, 0x74, 0x70, 0x75, 0x74, 0x5f, 0x6b, 0x62, 0x18, 0x08, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a,
+	0x69, 0x6f, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x4b, 0x62, 0x12, 0x3c, 0x0a, 0x1a, 0x76, 0x6f,
+	0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f,
+	0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x09, 0x20, 0x01, 0x28, 0x04, 0x52, 0x18,
 	0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74,
-	0x53, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x22, 0xe5, 0x01, 0x0a, 0x0e, 0x4d, 0x6f, 0x64,
-	0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x5b, 0x0a, 0x0c, 0x62,
-	0x75, 0x69, 0x6c, 0x64, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28,
-	0x0e, 0x32, 0x2f, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f,
-	0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79,
-	0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x79, 0x73, 0x74,
-	0x65, 0x6d, 0x3a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x52, 0x0b, 0x62, 0x75, 0x69,
-	0x6c, 0x64, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x6f, 0x64, 0x75,
-	0x6c, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d,
-	0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x6e, 0x75, 0x6d,
-	0x5f, 0x6f, 0x66, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28,
-	0x0d, 0x52, 0x0c, 0x6e, 0x75, 0x6d, 0x4f, 0x66, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x22,
-	0x2f, 0x0a, 0x0b, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x0b,
-	0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x53,
-	0x4f, 0x4f, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x4d, 0x41, 0x4b, 0x45, 0x10, 0x02,
-	0x22, 0x6c, 0x0a, 0x1a, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x55, 0x73, 0x65, 0x72,
-	0x4a, 0x6f, 0x75, 0x72, 0x6e, 0x65, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x12,
-	0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61,
-	0x6d, 0x65, 0x12, 0x3a, 0x0a, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18, 0x02, 0x20,
-	0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c,
-	0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63,
-	0x73, 0x42, 0x61, 0x73, 0x65, 0x52, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0x62,
-	0x0a, 0x1b, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x55, 0x73, 0x65, 0x72, 0x4a, 0x6f,
-	0x75, 0x72, 0x6e, 0x65, 0x79, 0x73, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x43, 0x0a,
-	0x04, 0x63, 0x75, 0x6a, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e, 0x73, 0x6f,
-	0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63,
-	0x73, 0x2e, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x55, 0x73, 0x65, 0x72, 0x4a, 0x6f,
-	0x75, 0x72, 0x6e, 0x65, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x04, 0x63, 0x75,
-	0x6a, 0x73, 0x22, 0xcc, 0x02, 0x0a, 0x11, 0x53, 0x6f, 0x6f, 0x6e, 0x67, 0x42, 0x75, 0x69, 0x6c,
-	0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x6f, 0x64, 0x75,
-	0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
-	0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x73, 0x18, 0x02,
-	0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x73, 0x12, 0x2a,
-	0x0a, 0x11, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x5f, 0x63, 0x6f,
-	0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x74, 0x6f, 0x74, 0x61, 0x6c,
-	0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x74, 0x6f,
-	0x74, 0x61, 0x6c, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x04,
-	0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x41, 0x6c, 0x6c, 0x6f, 0x63,
-	0x53, 0x69, 0x7a, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x61, 0x78, 0x5f, 0x68, 0x65, 0x61, 0x70,
-	0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6d, 0x61, 0x78,
-	0x48, 0x65, 0x61, 0x70, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x35, 0x0a, 0x06, 0x65, 0x76, 0x65, 0x6e,
-	0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67,
-	0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x50,
-	0x65, 0x72, 0x66, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12,
-	0x50, 0x0a, 0x11, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x5f,
-	0x69, 0x6e, 0x66, 0x6f, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x73, 0x6f, 0x6f,
+	0x53, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x12, 0x40, 0x0a, 0x1c, 0x69, 0x6e, 0x76, 0x6f,
+	0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x78, 0x74, 0x5f,
+	0x73, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x1a,
+	0x69, 0x6e, 0x76, 0x6f, 0x6c, 0x75, 0x6e, 0x74, 0x61, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x74, 0x65,
+	0x78, 0x74, 0x53, 0x77, 0x69, 0x74, 0x63, 0x68, 0x65, 0x73, 0x22, 0xe5, 0x01, 0x0a, 0x0e, 0x4d,
+	0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x5b, 0x0a,
+	0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x18, 0x01, 0x20,
+	0x01, 0x28, 0x0e, 0x32, 0x2f, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c,
+	0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65,
+	0x54, 0x79, 0x70, 0x65, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x79,
+	0x73, 0x74, 0x65, 0x6d, 0x3a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x52, 0x0b, 0x62,
+	0x75, 0x69, 0x6c, 0x64, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x6f,
+	0x64, 0x75, 0x6c, 0x65, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x0a, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x79, 0x70, 0x65, 0x12, 0x24, 0x0a, 0x0e, 0x6e,
+	0x75, 0x6d, 0x5f, 0x6f, 0x66, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20,
+	0x01, 0x28, 0x0d, 0x52, 0x0c, 0x6e, 0x75, 0x6d, 0x4f, 0x66, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65,
+	0x73, 0x22, 0x2f, 0x0a, 0x0b, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d,
+	0x12, 0x0b, 0x0a, 0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x09, 0x0a,
+	0x05, 0x53, 0x4f, 0x4f, 0x4e, 0x47, 0x10, 0x01, 0x12, 0x08, 0x0a, 0x04, 0x4d, 0x41, 0x4b, 0x45,
+	0x10, 0x02, 0x22, 0x6c, 0x0a, 0x1a, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x55, 0x73,
+	0x65, 0x72, 0x4a, 0x6f, 0x75, 0x72, 0x6e, 0x65, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
+	0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
+	0x6e, 0x61, 0x6d, 0x65, 0x12, 0x3a, 0x0a, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x18,
+	0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75,
+	0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x4d, 0x65, 0x74, 0x72,
+	0x69, 0x63, 0x73, 0x42, 0x61, 0x73, 0x65, 0x52, 0x07, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
+	0x22, 0x62, 0x0a, 0x1b, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x55, 0x73, 0x65, 0x72,
+	0x4a, 0x6f, 0x75, 0x72, 0x6e, 0x65, 0x79, 0x73, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12,
+	0x43, 0x0a, 0x04, 0x63, 0x75, 0x6a, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2f, 0x2e,
+	0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72,
+	0x69, 0x63, 0x73, 0x2e, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x55, 0x73, 0x65, 0x72,
+	0x4a, 0x6f, 0x75, 0x72, 0x6e, 0x65, 0x79, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x52, 0x04,
+	0x63, 0x75, 0x6a, 0x73, 0x22, 0xcc, 0x02, 0x0a, 0x11, 0x53, 0x6f, 0x6f, 0x6e, 0x67, 0x42, 0x75,
+	0x69, 0x6c, 0x64, 0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x6f,
+	0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x6d, 0x6f, 0x64,
+	0x75, 0x6c, 0x65, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x73,
+	0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x73,
+	0x12, 0x2a, 0x0a, 0x11, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x5f,
+	0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x74, 0x6f, 0x74,
+	0x61, 0x6c, 0x41, 0x6c, 0x6c, 0x6f, 0x63, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x28, 0x0a, 0x10,
+	0x74, 0x6f, 0x74, 0x61, 0x6c, 0x5f, 0x61, 0x6c, 0x6c, 0x6f, 0x63, 0x5f, 0x73, 0x69, 0x7a, 0x65,
+	0x18, 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0e, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x41, 0x6c, 0x6c,
+	0x6f, 0x63, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x6d, 0x61, 0x78, 0x5f, 0x68, 0x65,
+	0x61, 0x70, 0x5f, 0x73, 0x69, 0x7a, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x6d,
+	0x61, 0x78, 0x48, 0x65, 0x61, 0x70, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x35, 0x0a, 0x06, 0x65, 0x76,
+	0x65, 0x6e, 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x73, 0x6f, 0x6f,
 	0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
-	0x2e, 0x4d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x49, 0x6e, 0x66, 0x6f,
-	0x52, 0x0f, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x49, 0x6e, 0x66,
-	0x6f, 0x22, 0xdb, 0x01, 0x0a, 0x10, 0x45, 0x78, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x46,
-	0x65, 0x74, 0x63, 0x68, 0x65, 0x72, 0x12, 0x4a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73,
-	0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x32, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62,
-	0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x45, 0x78, 0x70,
-	0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x46, 0x65, 0x74, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x43, 0x6f,
-	0x6e, 0x66, 0x69, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74,
-	0x75, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02,
-	0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16,
-	0x0a, 0x06, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06,
-	0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x22, 0x47, 0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
-	0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x4f, 0x5f, 0x43, 0x4f, 0x4e,
-	0x46, 0x49, 0x47, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x10,
-	0x01, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x02, 0x12, 0x11, 0x0a, 0x0d,
-	0x4d, 0x49, 0x53, 0x53, 0x49, 0x4e, 0x47, 0x5f, 0x47, 0x43, 0x45, 0x52, 0x54, 0x10, 0x03, 0x22,
-	0x91, 0x01, 0x0a, 0x0f, 0x4d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x49,
-	0x6e, 0x66, 0x6f, 0x12, 0x3d, 0x0a, 0x1b, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x69,
-	0x6c, 0x64, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
-	0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x18, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x42,
-	0x75, 0x69, 0x6c, 0x64, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75, 0x6c,
-	0x65, 0x73, 0x12, 0x3f, 0x0a, 0x1c, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x69, 0x6c,
-	0x64, 0x5f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c,
-	0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x19, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x42,
-	0x75, 0x69, 0x6c, 0x64, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x4d, 0x6f, 0x64, 0x75,
-	0x6c, 0x65, 0x73, 0x22, 0x8a, 0x02, 0x0a, 0x10, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c,
-	0x50, 0x61, 0x74, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2e, 0x0a, 0x13, 0x65, 0x6c, 0x61, 0x70,
-	0x73, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18,
-	0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x65, 0x6c, 0x61, 0x70, 0x73, 0x65, 0x64, 0x54, 0x69,
-	0x6d, 0x65, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x12, 0x39, 0x0a, 0x19, 0x63, 0x72, 0x69, 0x74,
-	0x69, 0x63, 0x61, 0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d,
-	0x69, 0x63, 0x72, 0x6f, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x16, 0x63, 0x72, 0x69,
-	0x74, 0x69, 0x63, 0x61, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x69, 0x63,
-	0x72, 0x6f, 0x73, 0x12, 0x41, 0x0a, 0x0d, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x5f,
-	0x70, 0x61, 0x74, 0x68, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x73, 0x6f, 0x6f,
-	0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
-	0x2e, 0x4a, 0x6f, 0x62, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0c, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63,
-	0x61, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x12, 0x48, 0x0a, 0x11, 0x6c, 0x6f, 0x6e, 0x67, 0x5f, 0x72,
-	0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x6a, 0x6f, 0x62, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28,
-	0x0b, 0x32, 0x1c, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f,
-	0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x4a, 0x6f, 0x62, 0x49, 0x6e, 0x66, 0x6f, 0x52,
-	0x0f, 0x6c, 0x6f, 0x6e, 0x67, 0x52, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x4a, 0x6f, 0x62, 0x73,
-	0x22, 0x62, 0x0a, 0x07, 0x4a, 0x6f, 0x62, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2e, 0x0a, 0x13, 0x65,
-	0x6c, 0x61, 0x70, 0x73, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x63, 0x72,
-	0x6f, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x65, 0x6c, 0x61, 0x70, 0x73, 0x65,
-	0x64, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x12, 0x27, 0x0a, 0x0f, 0x6a,
-	0x6f, 0x62, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02,
-	0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6a, 0x6f, 0x62, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70,
-	0x74, 0x69, 0x6f, 0x6e, 0x42, 0x28, 0x5a, 0x26, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f,
-	0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x75, 0x69, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73,
-	0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x2e, 0x50, 0x65, 0x72, 0x66, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x06, 0x65, 0x76, 0x65, 0x6e, 0x74,
+	0x73, 0x12, 0x50, 0x0a, 0x11, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64,
+	0x73, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x73,
+	0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69,
+	0x63, 0x73, 0x2e, 0x4d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x49, 0x6e,
+	0x66, 0x6f, 0x52, 0x0f, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x73, 0x49,
+	0x6e, 0x66, 0x6f, 0x22, 0xdb, 0x01, 0x0a, 0x10, 0x45, 0x78, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69,
+	0x67, 0x46, 0x65, 0x74, 0x63, 0x68, 0x65, 0x72, 0x12, 0x4a, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74,
+	0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x32, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67,
+	0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x45,
+	0x78, 0x70, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x46, 0x65, 0x74, 0x63, 0x68, 0x65, 0x72, 0x2e,
+	0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x06, 0x73, 0x74,
+	0x61, 0x74, 0x75, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65,
+	0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65,
+	0x12, 0x16, 0x0a, 0x06, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04,
+	0x52, 0x06, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x22, 0x47, 0x0a, 0x0c, 0x43, 0x6f, 0x6e, 0x66,
+	0x69, 0x67, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x0d, 0x0a, 0x09, 0x4e, 0x4f, 0x5f, 0x43,
+	0x4f, 0x4e, 0x46, 0x49, 0x47, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x43, 0x4f, 0x4e, 0x46, 0x49,
+	0x47, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x02, 0x12, 0x11,
+	0x0a, 0x0d, 0x4d, 0x49, 0x53, 0x53, 0x49, 0x4e, 0x47, 0x5f, 0x47, 0x43, 0x45, 0x52, 0x54, 0x10,
+	0x03, 0x22, 0x91, 0x01, 0x0a, 0x0f, 0x4d, 0x69, 0x78, 0x65, 0x64, 0x42, 0x75, 0x69, 0x6c, 0x64,
+	0x73, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x3d, 0x0a, 0x1b, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x62,
+	0x75, 0x69, 0x6c, 0x64, 0x5f, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x5f, 0x6d, 0x6f, 0x64,
+	0x75, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x18, 0x6d, 0x69, 0x78, 0x65,
+	0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x4d, 0x6f, 0x64,
+	0x75, 0x6c, 0x65, 0x73, 0x12, 0x3f, 0x0a, 0x1c, 0x6d, 0x69, 0x78, 0x65, 0x64, 0x5f, 0x62, 0x75,
+	0x69, 0x6c, 0x64, 0x5f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x5f, 0x6d, 0x6f, 0x64,
+	0x75, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x19, 0x6d, 0x69, 0x78, 0x65,
+	0x64, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x4d, 0x6f,
+	0x64, 0x75, 0x6c, 0x65, 0x73, 0x22, 0x8a, 0x02, 0x0a, 0x10, 0x43, 0x72, 0x69, 0x74, 0x69, 0x63,
+	0x61, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2e, 0x0a, 0x13, 0x65, 0x6c,
+	0x61, 0x70, 0x73, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69, 0x63, 0x72, 0x6f,
+	0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x65, 0x6c, 0x61, 0x70, 0x73, 0x65, 0x64,
+	0x54, 0x69, 0x6d, 0x65, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x12, 0x39, 0x0a, 0x19, 0x63, 0x72,
+	0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x5f, 0x74, 0x69, 0x6d, 0x65,
+	0x5f, 0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x16, 0x63,
+	0x72, 0x69, 0x74, 0x69, 0x63, 0x61, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x54, 0x69, 0x6d, 0x65, 0x4d,
+	0x69, 0x63, 0x72, 0x6f, 0x73, 0x12, 0x41, 0x0a, 0x0d, 0x63, 0x72, 0x69, 0x74, 0x69, 0x63, 0x61,
+	0x6c, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x73,
+	0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69,
+	0x63, 0x73, 0x2e, 0x4a, 0x6f, 0x62, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x0c, 0x63, 0x72, 0x69, 0x74,
+	0x69, 0x63, 0x61, 0x6c, 0x50, 0x61, 0x74, 0x68, 0x12, 0x48, 0x0a, 0x11, 0x6c, 0x6f, 0x6e, 0x67,
+	0x5f, 0x72, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x5f, 0x6a, 0x6f, 0x62, 0x73, 0x18, 0x05, 0x20,
+	0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c,
+	0x64, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x4a, 0x6f, 0x62, 0x49, 0x6e, 0x66,
+	0x6f, 0x52, 0x0f, 0x6c, 0x6f, 0x6e, 0x67, 0x52, 0x75, 0x6e, 0x6e, 0x69, 0x6e, 0x67, 0x4a, 0x6f,
+	0x62, 0x73, 0x22, 0x62, 0x0a, 0x07, 0x4a, 0x6f, 0x62, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2e, 0x0a,
+	0x13, 0x65, 0x6c, 0x61, 0x70, 0x73, 0x65, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x69,
+	0x63, 0x72, 0x6f, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x11, 0x65, 0x6c, 0x61, 0x70,
+	0x73, 0x65, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x12, 0x27, 0x0a,
+	0x0f, 0x6a, 0x6f, 0x62, 0x5f, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e,
+	0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6a, 0x6f, 0x62, 0x44, 0x65, 0x73, 0x63, 0x72,
+	0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x28, 0x5a, 0x26, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69,
+	0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x75, 0x69, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69,
+	0x63, 0x73, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
 }
 
 var (
diff --git a/ui/metrics/metrics_proto/metrics.proto b/ui/metrics/metrics_proto/metrics.proto
index 6db83e9..b437da7 100644
--- a/ui/metrics/metrics_proto/metrics.proto
+++ b/ui/metrics/metrics_proto/metrics.proto
@@ -131,6 +131,12 @@
 
   // The metric of critical path in build
   optional CriticalPathInfo critical_path_info = 33;
+
+  // Environment variables that have changed value since the previous build,
+  // which were responsible for retriggering build analysis.
+  // Note that not all changed environment variables result in analysis retriggering.
+  // If there was no previous build, this list will be empty.
+  repeated string changed_environment_variable = 34;
 }
 
 message BuildConfig {
diff --git a/zip/cmd/main.go b/zip/cmd/main.go
index def76aa..a2ccc20 100644
--- a/zip/cmd/main.go
+++ b/zip/cmd/main.go
@@ -78,6 +78,15 @@
 	return nil
 }
 
+type explicitFile struct{}
+
+func (explicitFile) String() string { return `""` }
+
+func (explicitFile) Set(s string) error {
+	fileArgsBuilder.ExplicitPathInZip(s)
+	return nil
+}
+
 type dir struct{}
 
 func (dir) String() string { return `""` }
@@ -173,6 +182,7 @@
 	flags.Var(&nonDeflatedFiles, "s", "file path to be stored within the zip without compression")
 	flags.Var(&relativeRoot{}, "C", "path to use as relative root of files in following -f, -l, or -D arguments")
 	flags.Var(&junkPaths{}, "j", "junk paths, zip files without directory names")
+	flags.Var(&explicitFile{}, "e", "filename to use in the zip file for the next -f argument")
 
 	flags.Parse(expandedArgs[1:])
 
diff --git a/zip/zip.go b/zip/zip.go
index 6f1a8ad..5e1a104 100644
--- a/zip/zip.go
+++ b/zip/zip.go
@@ -80,6 +80,7 @@
 
 type FileArg struct {
 	PathPrefixInZip, SourcePrefixToStrip string
+	ExplicitPathInZip                    string
 	SourceFiles                          []string
 	JunkPaths                            bool
 	GlobDir                              string
@@ -124,6 +125,10 @@
 	arg := b.state
 	arg.SourceFiles = []string{name}
 	b.fileArgs = append(b.fileArgs, arg)
+
+	if b.state.ExplicitPathInZip != "" {
+		b.state.ExplicitPathInZip = ""
+	}
 	return b
 }
 
@@ -189,6 +194,12 @@
 	return b
 }
 
+// ExplicitPathInZip sets the path in the zip file for the next File call.
+func (b *FileArgsBuilder) ExplicitPathInZip(s string) *FileArgsBuilder {
+	b.state.ExplicitPathInZip = s
+	return b
+}
+
 func (b *FileArgsBuilder) Error() error {
 	if b == nil {
 		return nil
@@ -425,7 +436,9 @@
 
 	var dest string
 
-	if fa.JunkPaths {
+	if fa.ExplicitPathInZip != "" {
+		dest = fa.ExplicitPathInZip
+	} else if fa.JunkPaths {
 		dest = filepath.Base(src)
 	} else {
 		var err error
diff --git a/zip/zip_test.go b/zip/zip_test.go
index e7fdea8..c64c3f4 100644
--- a/zip/zip_test.go
+++ b/zip/zip_test.go
@@ -454,6 +454,60 @@
 				fhWithSHA256("c", fileC, zip.Deflate, sha256FileC),
 			},
 		},
+		{
+			name: "explicit path",
+			args: fileArgsBuilder().
+				ExplicitPathInZip("foo").
+				File("a/a/a").
+				File("a/a/b"),
+			compressionLevel: 9,
+
+			files: []zip.FileHeader{
+				fh("foo", fileA, zip.Deflate),
+				fh("a/a/b", fileB, zip.Deflate),
+			},
+		},
+		{
+			name: "explicit path with prefix",
+			args: fileArgsBuilder().
+				PathPrefixInZip("prefix").
+				ExplicitPathInZip("foo").
+				File("a/a/a").
+				File("a/a/b"),
+			compressionLevel: 9,
+
+			files: []zip.FileHeader{
+				fh("prefix/foo", fileA, zip.Deflate),
+				fh("prefix/a/a/b", fileB, zip.Deflate),
+			},
+		},
+		{
+			name: "explicit path with glob",
+			args: fileArgsBuilder().
+				ExplicitPathInZip("foo").
+				File("a/a/a*").
+				File("a/a/b"),
+			compressionLevel: 9,
+
+			files: []zip.FileHeader{
+				fh("foo", fileA, zip.Deflate),
+				fh("a/a/b", fileB, zip.Deflate),
+			},
+		},
+		{
+			name: "explicit path with junk paths",
+			args: fileArgsBuilder().
+				JunkPaths(true).
+				ExplicitPathInZip("foo/bar").
+				File("a/a/a*").
+				File("a/a/b"),
+			compressionLevel: 9,
+
+			files: []zip.FileHeader{
+				fh("foo/bar", fileA, zip.Deflate),
+				fh("b", fileB, zip.Deflate),
+			},
+		},
 
 		// errors
 		{
@@ -490,6 +544,22 @@
 				File("d/a/a"),
 			err: ConflictingFileError{},
 		},
+		{
+			name: "error explicit path conflicting",
+			args: fileArgsBuilder().
+				ExplicitPathInZip("foo").
+				File("a/a/a").
+				ExplicitPathInZip("foo").
+				File("a/a/b"),
+			err: ConflictingFileError{},
+		},
+		{
+			name: "error explicit path conflicting glob",
+			args: fileArgsBuilder().
+				ExplicitPathInZip("foo").
+				File("a/a/*"),
+			err: ConflictingFileError{},
+		},
 	}
 
 	for _, test := range testCases {