Merge "Remove min_sdk_version allowlist"
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go
index ccfad00..a5cea17 100644
--- a/android/allowlists/allowlists.go
+++ b/android/allowlists/allowlists.go
@@ -39,6 +39,7 @@
 	Bp2buildDefaultConfig = Bp2BuildConfig{
 		"prebuilts/runtime/mainline/platform/sdk":            Bp2BuildDefaultTrueRecursively,
 		"art/libartpalette":                                  Bp2BuildDefaultTrueRecursively,
+		"art/libartbase":                                     Bp2BuildDefaultTrueRecursively,
 		"art/libdexfile":                                     Bp2BuildDefaultTrueRecursively,
 		"art/libnativebridge":                                Bp2BuildDefaultTrueRecursively,
 		"art/runtime":                                        Bp2BuildDefaultTrueRecursively,
@@ -108,6 +109,7 @@
 		"external/eigen":                                     Bp2BuildDefaultTrueRecursively,
 		"external/erofs-utils":                               Bp2BuildDefaultTrueRecursively,
 		"external/error_prone":                               Bp2BuildDefaultTrueRecursively,
+		"external/expat":                                     Bp2BuildDefaultTrueRecursively,
 		"external/f2fs-tools":                                Bp2BuildDefaultTrue,
 		"external/flac":                                      Bp2BuildDefaultTrueRecursively,
 		"external/fmtlib":                                    Bp2BuildDefaultTrueRecursively,
@@ -146,7 +148,7 @@
 		"external/zlib":                                      Bp2BuildDefaultTrueRecursively,
 		"external/zopfli":                                    Bp2BuildDefaultTrueRecursively,
 		"external/zstd":                                      Bp2BuildDefaultTrueRecursively,
-		"frameworks/av/media/codecs/g711/decoder":            Bp2BuildDefaultTrueRecursively,
+		"frameworks/av/media/codecs":                         Bp2BuildDefaultTrueRecursively,
 		"frameworks/av/services/minijail":                    Bp2BuildDefaultTrueRecursively,
 		"frameworks/base/media/tests/MediaDump":              Bp2BuildDefaultTrue,
 		"frameworks/base/startop/apps/test":                  Bp2BuildDefaultTrue,
@@ -280,39 +282,49 @@
 		"flatbuffer_headers",
 		"gemmlowp_headers",
 		"gl_headers",
+		"libandroid_runtime_lazy",
 		"libandroid_runtime_vm_headers",
 		"libaudioclient_aidl_conversion_util",
 		"libaudioutils_fixedfft",
+		"libbinder_headers",
 		"libbinder_headers_platform_shared",
 		"libbluetooth-types-header",
+		"libbufferhub_headers",
+		"libcodec2",
 		"libcodec2_headers",
 		"libcodec2_internal",
 		"libdmabufheap",
+		"libdvr_headers",
 		"libgsm",
 		"libgui_bufferqueue_sources",
+		"libhardware",
 		"libhardware_headers",
+		"libincfs_headers",
 		"libnativeloader-headers",
 		"libnativewindow_headers",
 		"libneuralnetworks_headers",
 		"libopus",
+		"libpdx_headers",
 		"libprocpartition",
+		"libruy_static",
 		"libserviceutils",
-		"libstagefright_amrnb_common",
-		"libstagefright_amrwbdec",
 		"libstagefright_enc_common",
 		"libstagefright_foundation_headers",
 		"libstagefright_headers",
-		"libstagefright_m4vh263dec",
-		"libstagefright_m4vh263enc",
-		"libstagefright_mp3dec_headers",
+		"libsurfaceflinger_headers",
 		"libsync",
 		"libtextclassifier_hash_headers",
 		"libtextclassifier_hash_static",
+		"libtflite_kernel_utils",
+		"libtinyxml2",
 		"libui-types",
+		"libui_headers",
 		"libvorbisidec",
 		"media_ndk_headers",
+		"media_plugin_headers",
 		"mediaswcodec.policy",
 		"mediaswcodec.xml",
+		"philox_random",
 		"philox_random_headers",
 		"server_configurable_flags",
 		"tensorflow_headers",
@@ -361,10 +373,13 @@
 		"gen-kotlin-build-file.py",                  // TODO(b/198619163) module has same name as source
 		"libgtest_ndk_c++", "libgtest_main_ndk_c++", // TODO(b/201816222): Requires sdk_version support.
 		"linkerconfig", "mdnsd", // TODO(b/202876379): has arch-variant static_executable
-		"linker",       // TODO(b/228316882): cc_binary uses link_crt
-		"libdebuggerd", // TODO(b/228314770): support product variable-specific header_libs
-		"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
-		"apexer_test",  // Requires aapt2
+		"linker",            // TODO(b/228316882): cc_binary uses link_crt
+		"libdebuggerd",      // TODO(b/228314770): support product variable-specific header_libs
+		"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
+		"libspeexresampler", // TODO(b/231995978): Filter out unknown cflags
+		"libjpeg", "libvpx", // TODO(b/233948256): Convert .asm files
+		"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",
 
@@ -383,6 +398,9 @@
 		"prebuilt_platform-robolectric-4.4-prebuilt",   // aosp/1999250, needs .aar support in Jars
 		"prebuilt_platform-robolectric-4.5.1-prebuilt", // aosp/1999250, needs .aar support in Jars
 
+		// proto support
+		"libstats_proto_host", // TODO(b/236055697): handle protos from other packages
+
 		// path property for filegroups
 		"conscrypt",                        // TODO(b/210751803), we don't handle path property for filegroups
 		"conscrypt-for-host",               // TODO(b/210751803), we don't handle path property for filegroups
@@ -451,9 +469,11 @@
 		"stats-log-api-gen",                         // depends on unconverted modules: libstats_proto_host
 		"statslog.cpp", "statslog.h", "statslog.rs", // depends on unconverted modules: stats-log-api-gen
 		"statslog_art.cpp", "statslog_art.h", "statslog_header.rs", // depends on unconverted modules: stats-log-api-gen
-		"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
+		"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
+		"libartbase-art-gtest",  // depends on unconverted modules: libgtest_isolated, libart, libart-compiler, libdexfile, libprofile
+		"libartbased-art-gtest", // depends on unconverted modules: libgtest_isolated, libartd, libartd-compiler, libdexfiled, libprofiled
 
 		// b/215723302; awaiting tz{data,_version} to then rename targets conflicting with srcs
 		"tzdata",
@@ -463,6 +483,8 @@
 	Bp2buildCcLibraryStaticOnlyList = []string{}
 
 	MixedBuildsDisabledList = []string{
+		"libruy_static", "libtflite_kernel_utils", // TODO(b/237315968); Depend on prebuilt stl, not from source
+
 		"art_libdexfile_dex_instruction_list_header", // breaks libart_mterp.armng, header not found
 
 		"libbrotli",               // http://b/198585397, ld.lld: error: bionic/libc/arch-arm64/generic/bionic/memmove.S:95:(.text+0x10): relocation R_AARCH64_CONDBR19 out of range: -1404176 is not in [-1048576, 1048575]; references __memcpy
diff --git a/android/androidmk.go b/android/androidmk.go
index 6b675a6..832c7df 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -945,7 +945,10 @@
 	return !module.Enabled() ||
 		module.commonProperties.HideFromMake ||
 		// Make does not understand LinuxBionic
-		module.Os() == LinuxBionic
+		module.Os() == LinuxBionic ||
+		// Make does not understand LinuxMusl, except when we are building with USE_HOST_MUSL=true
+		// and all host binaries are LinuxMusl
+		(module.Os() == LinuxMusl && module.Target().HostCross)
 }
 
 // A utility func to format LOCAL_TEST_DATA outputs. See the comments on DataPath to understand how
diff --git a/android/api_levels.go b/android/api_levels.go
index 41b338c..da50b19 100644
--- a/android/api_levels.go
+++ b/android/api_levels.go
@@ -54,6 +54,14 @@
 	isPreview bool
 }
 
+func (this ApiLevel) FinalInt() int {
+	if this.IsPreview() {
+		panic("Requested a final int from a non-final ApiLevel")
+	} else {
+		return this.number
+	}
+}
+
 func (this ApiLevel) FinalOrFutureInt() int {
 	if this.IsPreview() {
 		return FutureApiLevelInt
diff --git a/android/arch.go b/android/arch.go
index 382a7df..e72614c 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -1825,7 +1825,9 @@
 	for _, t := range targets {
 		if _, found := set[t.Os.String()]; !found {
 			set[t.Os.String()] = true
-			ret = append(ret, commonTargetMap[t.Os.String()])
+			common := commonTargetMap[t.Os.String()]
+			common.HostCross = t.HostCross
+			ret = append(ret, common)
 		}
 	}
 
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index 27255d1..f1ec55e 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -879,12 +879,6 @@
 		}
 		rule := NewRuleBuilder(pctx, ctx)
 		createCommand(rule.Command(), buildStatement, executionRoot, bazelOutDir, ctx)
-		// This is required to silence warnings pertaining to unexpected timestamps. Particularly,
-		// some Bazel builtins (such as files in the bazel_tools directory) have far-future
-		// timestamps. Without restat, Ninja would emit warnings that the input files of a
-		// build statement have later timestamps than the outputs.
-		rule.Restat()
-
 		desc := fmt.Sprintf("%s: %s", buildStatement.Mnemonic, buildStatement.OutputPaths)
 		rule.Build(fmt.Sprintf("bazel %d", index), desc)
 	}
@@ -899,7 +893,7 @@
 	if len(buildStatement.OutputPaths) > 0 {
 		cmd.Text("rm -f")
 		for _, outputPath := range buildStatement.OutputPaths {
-			cmd.Text(outputPath)
+			cmd.Text(fmt.Sprintf("'%s'", outputPath))
 		}
 		cmd.Text("&&")
 	}
@@ -949,8 +943,15 @@
 func getConfigString(key cqueryKey) string {
 	arch := key.configKey.arch
 	if len(arch) == 0 || arch == "common" {
-		// Use host platform, which is currently hardcoded to be x86_64.
-		arch = "x86_64"
+		if key.configKey.osType.Class == Device {
+			// For the generic Android, the expected result is "target|android", which
+			// corresponds to the product_variable_config named "android_target" in
+			// build/bazel/platforms/BUILD.bazel.
+			arch = "target"
+		} else {
+			// Use host platform, which is currently hardcoded to be x86_64.
+			arch = "x86_64"
+		}
 	}
 	osName := key.configKey.osType.Name
 	if len(osName) == 0 || osName == "common_os" || osName == "linux_glibc" {
diff --git a/android/bazel_handler_test.go b/android/bazel_handler_test.go
index 935ce4e..ec2541b 100644
--- a/android/bazel_handler_test.go
+++ b/android/bazel_handler_test.go
@@ -65,13 +65,9 @@
 	var testCases = []testCase{
 		{`
 {
-  "artifacts": [{
-    "id": 1,
-    "pathFragmentId": 1
-  }, {
-    "id": 2,
-    "pathFragmentId": 2
-  }],
+  "artifacts": [
+    { "id": 1, "pathFragmentId": 1 },
+    { "id": 2, "pathFragmentId": 2 }],
   "actions": [{
     "targetId": 1,
     "actionKey": "x",
@@ -81,28 +77,18 @@
     "outputIds": [1],
     "primaryOutputId": 1
   }],
-  "depSetOfFiles": [{
-    "id": 1,
-    "directArtifactIds": [1, 2]
-  }],
-  "pathFragments": [{
-    "id": 1,
-    "label": "one"
-  }, {
-    "id": 2,
-    "label": "two"
-  }]
+  "depSetOfFiles": [
+    { "id": 1, "directArtifactIds": [1, 2] }],
+  "pathFragments": [
+    { "id": 1, "label": "one" },
+    { "id": 2, "label": "two" }]
 }`,
-			"cd 'er' && rm -f one && touch foo",
+			"cd 'test/exec_root' && rm -f 'one' && touch foo",
 		}, {`
 {
-  "artifacts": [{
-    "id": 1,
-    "pathFragmentId": 10
-  }, {
-    "id": 2,
-    "pathFragmentId": 20
-  }],
+  "artifacts": [
+    { "id": 1, "pathFragmentId": 10 },
+    { "id": 2, "pathFragmentId": 20 }],
   "actions": [{
     "targetId": 100,
     "actionKey": "x",
@@ -111,41 +97,33 @@
     "outputIds": [1, 2],
     "primaryOutputId": 1
   }],
-  "pathFragments": [{
-    "id": 10,
-    "label": "one",
-    "parentId": 30
-  }, {
-    "id": 20,
-    "label": "one.d",
-    "parentId": 30
-  }, {
-    "id": 30,
-    "label": "parent"
-  }]
+  "pathFragments": [
+    { "id": 10, "label": "one", "parentId": 30 },
+    { "id": 20, "label": "one.d", "parentId": 30 },
+    { "id": 30, "label": "parent" }]
 }`,
-			`cd 'er' && rm -f parent/one && bogus command && sed -i'' -E 's@(^|\s|")bazel-out/@\1bo/@g' 'parent/one.d'`,
+			`cd 'test/exec_root' && rm -f 'parent/one' && bogus command && sed -i'' -E 's@(^|\s|")bazel-out/@\1test/bazel_out/@g' 'parent/one.d'`,
 		},
 	}
 
-	for _, testCase := range testCases {
+	for i, testCase := range testCases {
 		bazelContext, _ := testBazelContext(t, map[bazelCommand]string{
 			bazelCommand{command: "aquery", expression: "deps(@soong_injection//mixed_builds:buildroot)"}: testCase.input})
 
 		err := bazelContext.InvokeBazel(testConfig)
 		if err != nil {
-			t.Fatalf("Did not expect error invoking Bazel, but got %s", err)
+			t.Fatalf("testCase #%d: did not expect error invoking Bazel, but got %s", i+1, err)
 		}
 
 		got := bazelContext.BuildStatementsToRegister()
 		if want := 1; len(got) != want {
-			t.Errorf("expected %d registered build statements, but got %#v", want, got)
+			t.Fatalf("expected %d registered build statements, but got %#v", want, got)
 		}
 
 		cmd := RuleBuilderCommand{}
-		createCommand(&cmd, got[0], "er", "bo", PathContextForTesting(TestConfig("out", nil, "", nil)))
-		if actual := cmd.buf.String(); testCase.command != actual {
-			t.Errorf("expected: [%s], actual: [%s]", testCase.command, actual)
+		createCommand(&cmd, got[0], "test/exec_root", "test/bazel_out", PathContextForTesting(TestConfig("out", nil, "", nil)))
+		if actual, expected := cmd.buf.String(), testCase.command; expected != actual {
+			t.Errorf("expected: [%s], actual: [%s]", expected, actual)
 		}
 	}
 }
diff --git a/apex/apex.go b/apex/apex.go
index 7e913e8..5678b06 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -1338,7 +1338,7 @@
 var _ android.DepIsInSameApex = (*apexBundle)(nil)
 
 // Implements android.DepInInSameApex
-func (a *apexBundle) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
+func (a *apexBundle) DepIsInSameApex(_ android.BaseModuleContext, _ android.Module) bool {
 	// direct deps of an APEX bundle are all part of the APEX bundle
 	// TODO(jiyong): shouldn't we look into the payload field of the dependencyTag?
 	return true
@@ -1474,7 +1474,7 @@
 	}
 
 	// Then follow the global setting
-	globalSanitizerNames := []string{}
+	var globalSanitizerNames []string
 	if a.Host() {
 		globalSanitizerNames = config.SanitizeHost()
 	} else {
@@ -1790,6 +1790,382 @@
 	}
 }
 
+type visitorContext struct {
+	// all the files that will be included in this APEX
+	filesInfo []apexFile
+
+	// native lib dependencies
+	provideNativeLibs []string
+	requireNativeLibs []string
+
+	handleSpecialLibs bool
+}
+
+func (vctx *visitorContext) normalizeFileInfo() {
+	encountered := make(map[string]apexFile)
+	for _, f := range vctx.filesInfo {
+		dest := filepath.Join(f.installDir, f.builtFile.Base())
+		if e, ok := encountered[dest]; !ok {
+			encountered[dest] = f
+		} else {
+			// If a module is directly included and also transitively depended on
+			// consider it as directly included.
+			e.transitiveDep = e.transitiveDep && f.transitiveDep
+			encountered[dest] = e
+		}
+	}
+	vctx.filesInfo = vctx.filesInfo[:0]
+	for _, v := range encountered {
+		vctx.filesInfo = append(vctx.filesInfo, v)
+	}
+	sort.Slice(vctx.filesInfo, func(i, j int) bool {
+		// Sort by destination path so as to ensure consistent ordering even if the source of the files
+		// changes.
+		return vctx.filesInfo[i].path() < vctx.filesInfo[j].path()
+	})
+}
+
+func (a *apexBundle) depVisitor(vctx *visitorContext, ctx android.ModuleContext, child, parent blueprint.Module) bool {
+	depTag := ctx.OtherModuleDependencyTag(child)
+	if _, ok := depTag.(android.ExcludeFromApexContentsTag); ok {
+		return false
+	}
+	if mod, ok := child.(android.Module); ok && !mod.Enabled() {
+		return false
+	}
+	depName := ctx.OtherModuleName(child)
+	if _, isDirectDep := parent.(*apexBundle); isDirectDep {
+		switch depTag {
+		case sharedLibTag, jniLibTag:
+			isJniLib := depTag == jniLibTag
+			switch ch := child.(type) {
+			case *cc.Module:
+				fi := apexFileForNativeLibrary(ctx, ch, vctx.handleSpecialLibs)
+				fi.isJniLib = isJniLib
+				vctx.filesInfo = append(vctx.filesInfo, fi)
+				// Collect the list of stub-providing libs except:
+				// - VNDK libs are only for vendors
+				// - bootstrap bionic libs are treated as provided by system
+				if ch.HasStubsVariants() && !a.vndkApex && !cc.InstallToBootstrap(ch.BaseModuleName(), ctx.Config()) {
+					vctx.provideNativeLibs = append(vctx.provideNativeLibs, fi.stem())
+				}
+				return true // track transitive dependencies
+			case *rust.Module:
+				fi := apexFileForRustLibrary(ctx, ch)
+				fi.isJniLib = isJniLib
+				vctx.filesInfo = append(vctx.filesInfo, fi)
+				return true // track transitive dependencies
+			default:
+				propertyName := "native_shared_libs"
+				if isJniLib {
+					propertyName = "jni_libs"
+				}
+				ctx.PropertyErrorf(propertyName, "%q is not a cc_library or cc_library_shared module", depName)
+			}
+		case executableTag:
+			switch ch := child.(type) {
+			case *cc.Module:
+				vctx.filesInfo = append(vctx.filesInfo, apexFileForExecutable(ctx, ch))
+				return true // track transitive dependencies
+			case *python.Module:
+				if ch.HostToolPath().Valid() {
+					vctx.filesInfo = append(vctx.filesInfo, apexFileForPyBinary(ctx, ch))
+				}
+			case bootstrap.GoBinaryTool:
+				if a.Host() {
+					vctx.filesInfo = append(vctx.filesInfo, apexFileForGoBinary(ctx, depName, ch))
+				}
+			case *rust.Module:
+				vctx.filesInfo = append(vctx.filesInfo, apexFileForRustExecutable(ctx, ch))
+				return true // track transitive dependencies
+			default:
+				ctx.PropertyErrorf("binaries",
+					"%q is neither cc_binary, rust_binary, (embedded) py_binary, (host) blueprint_go_binary, nor (host) bootstrap_go_binary", depName)
+			}
+		case shBinaryTag:
+			if csh, ok := child.(*sh.ShBinary); ok {
+				vctx.filesInfo = append(vctx.filesInfo, apexFileForShBinary(ctx, csh))
+			} else {
+				ctx.PropertyErrorf("sh_binaries", "%q is not a sh_binary module", depName)
+			}
+		case bcpfTag:
+			bcpfModule, 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.requiredDeps = append(a.requiredDeps, makeModuleName)
+			}
+			return true
+		case sscpfTag:
+			if _, ok := child.(*java.SystemServerClasspathModule); !ok {
+				ctx.PropertyErrorf("systemserverclasspath_fragments",
+					"%q is not a systemserverclasspath_fragment module", depName)
+				return false
+			}
+			if af := apexClasspathFragmentProtoFile(ctx, child); af != nil {
+				vctx.filesInfo = append(vctx.filesInfo, *af)
+			}
+			return true
+		case javaLibTag:
+			switch child.(type) {
+			case *java.Library, *java.SdkLibrary, *java.DexImport, *java.SdkLibraryImport, *java.Import:
+				af := apexFileForJavaModule(ctx, child.(javaModule))
+				if !af.ok() {
+					ctx.PropertyErrorf("java_libs", "%q is not configured to be compiled into dex", depName)
+					return false
+				}
+				vctx.filesInfo = append(vctx.filesInfo, af)
+				return true // track transitive dependencies
+			default:
+				ctx.PropertyErrorf("java_libs", "%q of type %q is not supported", depName, ctx.OtherModuleType(child))
+			}
+		case androidAppTag:
+			switch ap := child.(type) {
+			case *java.AndroidApp:
+				vctx.filesInfo = append(vctx.filesInfo, apexFileForAndroidApp(ctx, ap))
+				return true // track transitive dependencies
+			case *java.AndroidAppImport:
+				vctx.filesInfo = append(vctx.filesInfo, apexFileForAndroidApp(ctx, ap))
+			case *java.AndroidTestHelperApp:
+				vctx.filesInfo = append(vctx.filesInfo, apexFileForAndroidApp(ctx, ap))
+			case *java.AndroidAppSet:
+				appDir := "app"
+				if ap.Privileged() {
+					appDir = "priv-app"
+				}
+				// TODO(b/224589412, b/226559955): Ensure that the dirname is
+				// suffixed so that PackageManager correctly invalidates the
+				// existing installed apk in favour of the new APK-in-APEX.
+				// See bugs for more information.
+				appDirName := filepath.Join(appDir, ap.BaseModuleName()+"@"+sanitizedBuildIdForPath(ctx))
+				af := newApexFile(ctx, ap.OutputFile(), ap.BaseModuleName(), appDirName, appSet, ap)
+				af.certificate = java.PresignedCertificate
+				vctx.filesInfo = append(vctx.filesInfo, af)
+			default:
+				ctx.PropertyErrorf("apps", "%q is not an android_app module", depName)
+			}
+		case rroTag:
+			if rro, ok := child.(java.RuntimeResourceOverlayModule); ok {
+				vctx.filesInfo = append(vctx.filesInfo, apexFileForRuntimeResourceOverlay(ctx, rro))
+			} else {
+				ctx.PropertyErrorf("rros", "%q is not an runtime_resource_overlay module", depName)
+			}
+		case bpfTag:
+			if bpfProgram, ok := child.(bpf.BpfModule); ok {
+				filesToCopy, _ := bpfProgram.OutputFiles("")
+				apex_sub_dir := bpfProgram.SubDir()
+				for _, bpfFile := range filesToCopy {
+					vctx.filesInfo = append(vctx.filesInfo, apexFileForBpfProgram(ctx, bpfFile, apex_sub_dir, bpfProgram))
+				}
+			} else {
+				ctx.PropertyErrorf("bpfs", "%q is not a bpf module", depName)
+			}
+		case fsTag:
+			if fs, ok := child.(filesystem.Filesystem); ok {
+				vctx.filesInfo = append(vctx.filesInfo, apexFileForFilesystem(ctx, fs.OutputPath(), fs))
+			} else {
+				ctx.PropertyErrorf("filesystems", "%q is not a filesystem module", depName)
+			}
+		case prebuiltTag:
+			if prebuilt, ok := child.(prebuilt_etc.PrebuiltEtcModule); ok {
+				vctx.filesInfo = append(vctx.filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, depName))
+			} else {
+				ctx.PropertyErrorf("prebuilts", "%q is not a prebuilt_etc module", depName)
+			}
+		case compatConfigTag:
+			if compatConfig, ok := child.(java.PlatformCompatConfigIntf); ok {
+				vctx.filesInfo = append(vctx.filesInfo, apexFileForCompatConfig(ctx, compatConfig, depName))
+			} else {
+				ctx.PropertyErrorf("compat_configs", "%q is not a platform_compat_config module", depName)
+			}
+		case testTag:
+			if ccTest, ok := child.(*cc.Module); ok {
+				if ccTest.IsTestPerSrcAllTestsVariation() {
+					// Multiple-output test module (where `test_per_src: true`).
+					//
+					// `ccTest` is the "" ("all tests") variation of a `test_per_src` module.
+					// We do not add this variation to `filesInfo`, as it has no output;
+					// however, we do add the other variations of this module as indirect
+					// dependencies (see below).
+				} else {
+					// Single-output test module (where `test_per_src: false`).
+					af := apexFileForExecutable(ctx, ccTest)
+					af.class = nativeTest
+					vctx.filesInfo = append(vctx.filesInfo, af)
+				}
+				return true // track transitive dependencies
+			} else {
+				ctx.PropertyErrorf("tests", "%q is not a cc module", depName)
+			}
+		case keyTag:
+			if key, ok := child.(*apexKey); ok {
+				a.privateKeyFile = key.privateKeyFile
+				a.publicKeyFile = key.publicKeyFile
+			} else {
+				ctx.PropertyErrorf("key", "%q is not an apex_key module", depName)
+			}
+		case certificateTag:
+			if dep, ok := child.(*java.AndroidAppCertificate); ok {
+				a.containerCertificateFile = dep.Certificate.Pem
+				a.containerPrivateKeyFile = dep.Certificate.Key
+			} else {
+				ctx.ModuleErrorf("certificate dependency %q must be an android_app_certificate module", depName)
+			}
+		case android.PrebuiltDepTag:
+			// If the prebuilt is force disabled, remember to delete the prebuilt file
+			// that might have been installed in the previous builds
+			if prebuilt, ok := child.(prebuilt); ok && prebuilt.isForceDisabled() {
+				a.prebuiltFileToDelete = prebuilt.InstallFilename()
+			}
+		}
+		return false
+	}
+
+	if a.vndkApex {
+		return false
+	}
+
+	// indirect dependencies
+	am, ok := child.(android.ApexModule)
+	if !ok {
+		return false
+	}
+	// We cannot use a switch statement on `depTag` here as the checked
+	// tags used below are private (e.g. `cc.sharedDepTag`).
+	if cc.IsSharedDepTag(depTag) || cc.IsRuntimeDepTag(depTag) {
+		if ch, ok := child.(*cc.Module); ok {
+			if ch.UseVndk() && proptools.Bool(a.properties.Use_vndk_as_stable) && ch.IsVndk() {
+				vctx.requireNativeLibs = append(vctx.requireNativeLibs, ":vndk")
+				return false
+			}
+			af := apexFileForNativeLibrary(ctx, ch, vctx.handleSpecialLibs)
+			af.transitiveDep = true
+
+			// Always track transitive dependencies for host.
+			if a.Host() {
+				vctx.filesInfo = append(vctx.filesInfo, af)
+				return true
+			}
+
+			abInfo := ctx.Provider(ApexBundleInfoProvider).(ApexBundleInfo)
+			if !abInfo.Contents.DirectlyInApex(depName) && (ch.IsStubs() || ch.HasStubsVariants()) {
+				// If the dependency is a stubs lib, don't include it in this APEX,
+				// but make sure that the lib is installed on the device.
+				// In case no APEX is having the lib, the lib is installed to the system
+				// partition.
+				//
+				// Always include if we are a host-apex however since those won't have any
+				// system libraries.
+				if !am.DirectlyInAnyApex() {
+					// we need a module name for Make
+					name := ch.ImplementationModuleNameForMake(ctx) + ch.Properties.SubName
+					if !android.InList(name, a.requiredDeps) {
+						a.requiredDeps = append(a.requiredDeps, name)
+					}
+				}
+				vctx.requireNativeLibs = append(vctx.requireNativeLibs, af.stem())
+				// Don't track further
+				return false
+			}
+
+			// If the dep is not considered to be in the same
+			// apex, don't add it to filesInfo so that it is not
+			// included in this APEX.
+			// TODO(jiyong): move this to at the top of the
+			// else-if clause for the indirect dependencies.
+			// Currently, that's impossible because we would
+			// like to record requiredNativeLibs even when
+			// DepIsInSameAPex is false. We also shouldn't do
+			// this for host.
+			//
+			// TODO(jiyong): explain why the same module is passed in twice.
+			// Switching the first am to parent breaks lots of tests.
+			if !android.IsDepInSameApex(ctx, am, am) {
+				return false
+			}
+
+			vctx.filesInfo = append(vctx.filesInfo, af)
+			return true // track transitive dependencies
+		} else if rm, ok := child.(*rust.Module); ok {
+			af := apexFileForRustLibrary(ctx, rm)
+			af.transitiveDep = true
+			vctx.filesInfo = append(vctx.filesInfo, af)
+			return true // track transitive dependencies
+		}
+	} else if cc.IsTestPerSrcDepTag(depTag) {
+		if ch, ok := child.(*cc.Module); ok {
+			af := apexFileForExecutable(ctx, ch)
+			// Handle modules created as `test_per_src` variations of a single test module:
+			// use the name of the generated test binary (`fileToCopy`) instead of the name
+			// of the original test module (`depName`, shared by all `test_per_src`
+			// variations of that module).
+			af.androidMkModuleName = filepath.Base(af.builtFile.String())
+			// these are not considered transitive dep
+			af.transitiveDep = false
+			vctx.filesInfo = append(vctx.filesInfo, af)
+			return true // track transitive dependencies
+		}
+	} else if cc.IsHeaderDepTag(depTag) {
+		// nothing
+	} else if java.IsJniDepTag(depTag) {
+		// Because APK-in-APEX embeds jni_libs transitively, we don't need to track transitive deps
+	} else if java.IsXmlPermissionsFileDepTag(depTag) {
+		if prebuilt, ok := child.(prebuilt_etc.PrebuiltEtcModule); ok {
+			vctx.filesInfo = append(vctx.filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, depName))
+		}
+	} else if rust.IsDylibDepTag(depTag) {
+		if rustm, ok := child.(*rust.Module); ok && rustm.IsInstallableToApex() {
+			af := apexFileForRustLibrary(ctx, rustm)
+			af.transitiveDep = true
+			vctx.filesInfo = append(vctx.filesInfo, af)
+			return true // track transitive dependencies
+		}
+	} else if rust.IsRlibDepTag(depTag) {
+		// Rlib is statically linked, but it might have shared lib
+		// dependencies. Track them.
+		return true
+	} else if java.IsBootclasspathFragmentContentDepTag(depTag) {
+		// Add the contents of the bootclasspath fragment to the apex.
+		switch child.(type) {
+		case *java.Library, *java.SdkLibrary:
+			javaModule := child.(javaModule)
+			af := apexFileForBootclasspathFragmentContentModule(ctx, parent, javaModule)
+			if !af.ok() {
+				ctx.PropertyErrorf("bootclasspath_fragments",
+					"bootclasspath_fragment content %q is not configured to be compiled into dex", depName)
+				return false
+			}
+			vctx.filesInfo = append(vctx.filesInfo, af)
+			return true // track transitive dependencies
+		default:
+			ctx.PropertyErrorf("bootclasspath_fragments",
+				"bootclasspath_fragment content %q of type %q is not supported", depName, ctx.OtherModuleType(child))
+		}
+	} else if java.IsSystemServerClasspathFragmentContentDepTag(depTag) {
+		// Add the contents of the systemserverclasspath fragment to the apex.
+		switch child.(type) {
+		case *java.Library, *java.SdkLibrary:
+			af := apexFileForJavaModule(ctx, child.(javaModule))
+			vctx.filesInfo = append(vctx.filesInfo, af)
+			return true // track transitive dependencies
+		default:
+			ctx.PropertyErrorf("systemserverclasspath_fragments",
+				"systemserverclasspath_fragment content %q of type %q is not supported", depName, ctx.OtherModuleType(child))
+		}
+	} else if _, ok := depTag.(android.CopyDirectlyInAnyApexTag); ok {
+		// nothing
+	} else if depTag == android.DarwinUniversalVariantTag {
+		// nothing
+	} else if am.CanHaveApexVariants() && am.IsInstallableToApex() {
+		ctx.ModuleErrorf("unexpected tag %s for indirect dependency %q", android.PrettyPrintTag(depTag), depName)
+	}
+	return false
+}
+
 // Creates build rules for an APEX. It consists of the following major steps:
 //
 // 1) do some validity checks such as apex_available, min_sdk_version, etc.
@@ -1812,386 +2188,23 @@
 	////////////////////////////////////////////////////////////////////////////////////////////
 	// 2) traverse the dependency tree to collect apexFile structs from them.
 
-	// all the files that will be included in this APEX
-	var filesInfo []apexFile
-
-	// native lib dependencies
-	var provideNativeLibs []string
-	var requireNativeLibs []string
-
-	handleSpecialLibs := !android.Bool(a.properties.Ignore_system_library_special_case)
-
 	// Collect the module directory for IDE info in java/jdeps.go.
 	a.modulePaths = append(a.modulePaths, ctx.ModuleDir())
 
 	// TODO(jiyong): do this using WalkPayloadDeps
 	// TODO(jiyong): make this clean!!!
-	ctx.WalkDepsBlueprint(func(child, parent blueprint.Module) bool {
-		depTag := ctx.OtherModuleDependencyTag(child)
-		if _, ok := depTag.(android.ExcludeFromApexContentsTag); ok {
-			return false
-		}
-		if mod, ok := child.(android.Module); ok && !mod.Enabled() {
-			return false
-		}
-		depName := ctx.OtherModuleName(child)
-		if _, isDirectDep := parent.(*apexBundle); isDirectDep {
-			switch depTag {
-			case sharedLibTag, jniLibTag:
-				isJniLib := depTag == jniLibTag
-				if c, ok := child.(*cc.Module); ok {
-					fi := apexFileForNativeLibrary(ctx, c, handleSpecialLibs)
-					fi.isJniLib = isJniLib
-					filesInfo = append(filesInfo, fi)
-					// Collect the list of stub-providing libs except:
-					// - VNDK libs are only for vendors
-					// - bootstrap bionic libs are treated as provided by system
-					if c.HasStubsVariants() && !a.vndkApex && !cc.InstallToBootstrap(c.BaseModuleName(), ctx.Config()) {
-						provideNativeLibs = append(provideNativeLibs, fi.stem())
-					}
-					return true // track transitive dependencies
-				} else if r, ok := child.(*rust.Module); ok {
-					fi := apexFileForRustLibrary(ctx, r)
-					fi.isJniLib = isJniLib
-					filesInfo = append(filesInfo, fi)
-					return true // track transitive dependencies
-				} else {
-					propertyName := "native_shared_libs"
-					if isJniLib {
-						propertyName = "jni_libs"
-					}
-					ctx.PropertyErrorf(propertyName, "%q is not a cc_library or cc_library_shared module", depName)
-				}
-			case executableTag:
-				if cc, ok := child.(*cc.Module); ok {
-					filesInfo = append(filesInfo, apexFileForExecutable(ctx, cc))
-					return true // track transitive dependencies
-				} else if py, ok := child.(*python.Module); ok && py.HostToolPath().Valid() {
-					filesInfo = append(filesInfo, apexFileForPyBinary(ctx, py))
-				} else if gb, ok := child.(bootstrap.GoBinaryTool); ok && a.Host() {
-					filesInfo = append(filesInfo, apexFileForGoBinary(ctx, depName, gb))
-				} else if rust, ok := child.(*rust.Module); ok {
-					filesInfo = append(filesInfo, apexFileForRustExecutable(ctx, rust))
-					return true // track transitive dependencies
-				} else {
-					ctx.PropertyErrorf("binaries", "%q is neither cc_binary, rust_binary, (embedded) py_binary, (host) blueprint_go_binary, nor (host) bootstrap_go_binary", depName)
-				}
-			case shBinaryTag:
-				if sh, ok := child.(*sh.ShBinary); ok {
-					filesInfo = append(filesInfo, apexFileForShBinary(ctx, sh))
-				} else {
-					ctx.PropertyErrorf("sh_binaries", "%q is not a sh_binary module", depName)
-				}
-			case bcpfTag:
-				{
-					bcpfModule, ok := child.(*java.BootclasspathFragmentModule)
-					if !ok {
-						ctx.PropertyErrorf("bootclasspath_fragments", "%q is not a bootclasspath_fragment module", depName)
-						return false
-					}
-
-					filesToAdd := apexBootclasspathFragmentFiles(ctx, child)
-					filesInfo = append(filesInfo, filesToAdd...)
-					for _, makeModuleName := range bcpfModule.BootImageDeviceInstallMakeModules() {
-						a.requiredDeps = append(a.requiredDeps, makeModuleName)
-					}
-					return true
-				}
-			case sscpfTag:
-				{
-					if _, ok := child.(*java.SystemServerClasspathModule); !ok {
-						ctx.PropertyErrorf("systemserverclasspath_fragments", "%q is not a systemserverclasspath_fragment module", depName)
-						return false
-					}
-					if af := apexClasspathFragmentProtoFile(ctx, child); af != nil {
-						filesInfo = append(filesInfo, *af)
-					}
-					return true
-				}
-			case javaLibTag:
-				switch child.(type) {
-				case *java.Library, *java.SdkLibrary, *java.DexImport, *java.SdkLibraryImport, *java.Import:
-					af := apexFileForJavaModule(ctx, child.(javaModule))
-					if !af.ok() {
-						ctx.PropertyErrorf("java_libs", "%q is not configured to be compiled into dex", depName)
-						return false
-					}
-					filesInfo = append(filesInfo, af)
-					return true // track transitive dependencies
-				default:
-					ctx.PropertyErrorf("java_libs", "%q of type %q is not supported", depName, ctx.OtherModuleType(child))
-				}
-			case androidAppTag:
-				if ap, ok := child.(*java.AndroidApp); ok {
-					filesInfo = append(filesInfo, apexFileForAndroidApp(ctx, ap))
-					return true // track transitive dependencies
-				} else if ap, ok := child.(*java.AndroidAppImport); ok {
-					filesInfo = append(filesInfo, apexFileForAndroidApp(ctx, ap))
-				} else if ap, ok := child.(*java.AndroidTestHelperApp); ok {
-					filesInfo = append(filesInfo, apexFileForAndroidApp(ctx, ap))
-				} else if ap, ok := child.(*java.AndroidAppSet); ok {
-					appDir := "app"
-					if ap.Privileged() {
-						appDir = "priv-app"
-					}
-					// TODO(b/224589412, b/226559955): Ensure that the dirname is
-					// suffixed so that PackageManager correctly invalidates the
-					// existing installed apk in favour of the new APK-in-APEX.
-					// See bugs for more information.
-					appDirName := filepath.Join(appDir, ap.BaseModuleName()+"@"+sanitizedBuildIdForPath(ctx))
-					af := newApexFile(ctx, ap.OutputFile(), ap.BaseModuleName(), appDirName, appSet, ap)
-					af.certificate = java.PresignedCertificate
-					filesInfo = append(filesInfo, af)
-				} else {
-					ctx.PropertyErrorf("apps", "%q is not an android_app module", depName)
-				}
-			case rroTag:
-				if rro, ok := child.(java.RuntimeResourceOverlayModule); ok {
-					filesInfo = append(filesInfo, apexFileForRuntimeResourceOverlay(ctx, rro))
-				} else {
-					ctx.PropertyErrorf("rros", "%q is not an runtime_resource_overlay module", depName)
-				}
-			case bpfTag:
-				if bpfProgram, ok := child.(bpf.BpfModule); ok {
-					filesToCopy, _ := bpfProgram.OutputFiles("")
-					apex_sub_dir := bpfProgram.SubDir()
-					for _, bpfFile := range filesToCopy {
-						filesInfo = append(filesInfo, apexFileForBpfProgram(ctx, bpfFile, apex_sub_dir, bpfProgram))
-					}
-				} else {
-					ctx.PropertyErrorf("bpfs", "%q is not a bpf module", depName)
-				}
-			case fsTag:
-				if fs, ok := child.(filesystem.Filesystem); ok {
-					filesInfo = append(filesInfo, apexFileForFilesystem(ctx, fs.OutputPath(), fs))
-				} else {
-					ctx.PropertyErrorf("filesystems", "%q is not a filesystem module", depName)
-				}
-			case prebuiltTag:
-				if prebuilt, ok := child.(prebuilt_etc.PrebuiltEtcModule); ok {
-					filesInfo = append(filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, depName))
-				} else {
-					ctx.PropertyErrorf("prebuilts", "%q is not a prebuilt_etc module", depName)
-				}
-			case compatConfigTag:
-				if compatConfig, ok := child.(java.PlatformCompatConfigIntf); ok {
-					filesInfo = append(filesInfo, apexFileForCompatConfig(ctx, compatConfig, depName))
-				} else {
-					ctx.PropertyErrorf("compat_configs", "%q is not a platform_compat_config module", depName)
-				}
-			case testTag:
-				if ccTest, ok := child.(*cc.Module); ok {
-					if ccTest.IsTestPerSrcAllTestsVariation() {
-						// Multiple-output test module (where `test_per_src: true`).
-						//
-						// `ccTest` is the "" ("all tests") variation of a `test_per_src` module.
-						// We do not add this variation to `filesInfo`, as it has no output;
-						// however, we do add the other variations of this module as indirect
-						// dependencies (see below).
-					} else {
-						// Single-output test module (where `test_per_src: false`).
-						af := apexFileForExecutable(ctx, ccTest)
-						af.class = nativeTest
-						filesInfo = append(filesInfo, af)
-					}
-					return true // track transitive dependencies
-				} else {
-					ctx.PropertyErrorf("tests", "%q is not a cc module", depName)
-				}
-			case keyTag:
-				if key, ok := child.(*apexKey); ok {
-					a.privateKeyFile = key.privateKeyFile
-					a.publicKeyFile = key.publicKeyFile
-				} else {
-					ctx.PropertyErrorf("key", "%q is not an apex_key module", depName)
-				}
-				return false
-			case certificateTag:
-				if dep, ok := child.(*java.AndroidAppCertificate); ok {
-					a.containerCertificateFile = dep.Certificate.Pem
-					a.containerPrivateKeyFile = dep.Certificate.Key
-				} else {
-					ctx.ModuleErrorf("certificate dependency %q must be an android_app_certificate module", depName)
-				}
-			case android.PrebuiltDepTag:
-				// If the prebuilt is force disabled, remember to delete the prebuilt file
-				// that might have been installed in the previous builds
-				if prebuilt, ok := child.(prebuilt); ok && prebuilt.isForceDisabled() {
-					a.prebuiltFileToDelete = prebuilt.InstallFilename()
-				}
-			}
-		} else if !a.vndkApex {
-			// indirect dependencies
-			if am, ok := child.(android.ApexModule); ok {
-				// We cannot use a switch statement on `depTag` here as the checked
-				// tags used below are private (e.g. `cc.sharedDepTag`).
-				if cc.IsSharedDepTag(depTag) || cc.IsRuntimeDepTag(depTag) {
-					if cc, ok := child.(*cc.Module); ok {
-						if cc.UseVndk() && proptools.Bool(a.properties.Use_vndk_as_stable) && cc.IsVndk() {
-							requireNativeLibs = append(requireNativeLibs, ":vndk")
-							return false
-						}
-						af := apexFileForNativeLibrary(ctx, cc, handleSpecialLibs)
-						af.transitiveDep = true
-
-						// Always track transitive dependencies for host.
-						if a.Host() {
-							filesInfo = append(filesInfo, af)
-							return true
-						}
-
-						abInfo := ctx.Provider(ApexBundleInfoProvider).(ApexBundleInfo)
-						if !abInfo.Contents.DirectlyInApex(depName) && (cc.IsStubs() || cc.HasStubsVariants()) {
-							// If the dependency is a stubs lib, don't include it in this APEX,
-							// but make sure that the lib is installed on the device.
-							// In case no APEX is having the lib, the lib is installed to the system
-							// partition.
-							//
-							// Always include if we are a host-apex however since those won't have any
-							// system libraries.
-							if !am.DirectlyInAnyApex() {
-								// we need a module name for Make
-								name := cc.ImplementationModuleNameForMake(ctx) + cc.Properties.SubName
-								if !android.InList(name, a.requiredDeps) {
-									a.requiredDeps = append(a.requiredDeps, name)
-								}
-							}
-							requireNativeLibs = append(requireNativeLibs, af.stem())
-							// Don't track further
-							return false
-						}
-
-						// If the dep is not considered to be in the same
-						// apex, don't add it to filesInfo so that it is not
-						// included in this APEX.
-						// TODO(jiyong): move this to at the top of the
-						// else-if clause for the indirect dependencies.
-						// Currently, that's impossible because we would
-						// like to record requiredNativeLibs even when
-						// DepIsInSameAPex is false. We also shouldn't do
-						// this for host.
-						//
-						// TODO(jiyong): explain why the same module is passed in twice.
-						// Switching the first am to parent breaks lots of tests.
-						if !android.IsDepInSameApex(ctx, am, am) {
-							return false
-						}
-
-						filesInfo = append(filesInfo, af)
-						return true // track transitive dependencies
-					} else if rm, ok := child.(*rust.Module); ok {
-						af := apexFileForRustLibrary(ctx, rm)
-						af.transitiveDep = true
-						filesInfo = append(filesInfo, af)
-						return true // track transitive dependencies
-					}
-				} else if cc.IsTestPerSrcDepTag(depTag) {
-					if cc, ok := child.(*cc.Module); ok {
-						af := apexFileForExecutable(ctx, cc)
-						// Handle modules created as `test_per_src` variations of a single test module:
-						// use the name of the generated test binary (`fileToCopy`) instead of the name
-						// of the original test module (`depName`, shared by all `test_per_src`
-						// variations of that module).
-						af.androidMkModuleName = filepath.Base(af.builtFile.String())
-						// these are not considered transitive dep
-						af.transitiveDep = false
-						filesInfo = append(filesInfo, af)
-						return true // track transitive dependencies
-					}
-				} else if cc.IsHeaderDepTag(depTag) {
-					// nothing
-				} else if java.IsJniDepTag(depTag) {
-					// Because APK-in-APEX embeds jni_libs transitively, we don't need to track transitive deps
-					return false
-				} else if java.IsXmlPermissionsFileDepTag(depTag) {
-					if prebuilt, ok := child.(prebuilt_etc.PrebuiltEtcModule); ok {
-						filesInfo = append(filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, depName))
-					}
-				} else if rust.IsDylibDepTag(depTag) {
-					if rustm, ok := child.(*rust.Module); ok && rustm.IsInstallableToApex() {
-						af := apexFileForRustLibrary(ctx, rustm)
-						af.transitiveDep = true
-						filesInfo = append(filesInfo, af)
-						return true // track transitive dependencies
-					}
-				} else if rust.IsRlibDepTag(depTag) {
-					// Rlib is statically linked, but it might have shared lib
-					// dependencies. Track them.
-					return true
-				} else if java.IsBootclasspathFragmentContentDepTag(depTag) {
-					// Add the contents of the bootclasspath fragment to the apex.
-					switch child.(type) {
-					case *java.Library, *java.SdkLibrary:
-						javaModule := child.(javaModule)
-						af := apexFileForBootclasspathFragmentContentModule(ctx, parent, javaModule)
-						if !af.ok() {
-							ctx.PropertyErrorf("bootclasspath_fragments", "bootclasspath_fragment content %q is not configured to be compiled into dex", depName)
-							return false
-						}
-						filesInfo = append(filesInfo, af)
-						return true // track transitive dependencies
-					default:
-						ctx.PropertyErrorf("bootclasspath_fragments", "bootclasspath_fragment content %q of type %q is not supported", depName, ctx.OtherModuleType(child))
-					}
-				} else if java.IsSystemServerClasspathFragmentContentDepTag(depTag) {
-					// Add the contents of the systemserverclasspath fragment to the apex.
-					switch child.(type) {
-					case *java.Library, *java.SdkLibrary:
-						af := apexFileForJavaModule(ctx, child.(javaModule))
-						filesInfo = append(filesInfo, af)
-						return true // track transitive dependencies
-					default:
-						ctx.PropertyErrorf("systemserverclasspath_fragments", "systemserverclasspath_fragment content %q of type %q is not supported", depName, ctx.OtherModuleType(child))
-					}
-				} else if _, ok := depTag.(android.CopyDirectlyInAnyApexTag); ok {
-					// nothing
-				} else if depTag == android.DarwinUniversalVariantTag {
-					// nothing
-				} else if am.CanHaveApexVariants() && am.IsInstallableToApex() {
-					ctx.ModuleErrorf("unexpected tag %s for indirect dependency %q", android.PrettyPrintTag(depTag), depName)
-				}
-			}
-		}
-		return false
-	})
+	vctx := visitorContext{handleSpecialLibs: !android.Bool(a.properties.Ignore_system_library_special_case)}
+	ctx.WalkDepsBlueprint(func(child, parent blueprint.Module) bool { return a.depVisitor(&vctx, ctx, child, parent) })
+	vctx.normalizeFileInfo()
 	if a.privateKeyFile == nil {
 		ctx.PropertyErrorf("key", "private_key for %q could not be found", String(a.overridableProperties.Key))
 		return
 	}
 
-	// Remove duplicates in filesInfo
-	removeDup := func(filesInfo []apexFile) []apexFile {
-		encountered := make(map[string]apexFile)
-		for _, f := range filesInfo {
-			dest := filepath.Join(f.installDir, f.builtFile.Base())
-			if e, ok := encountered[dest]; !ok {
-				encountered[dest] = f
-			} else {
-				// If a module is directly included and also transitively depended on
-				// consider it as directly included.
-				e.transitiveDep = e.transitiveDep && f.transitiveDep
-				encountered[dest] = e
-			}
-		}
-		var result []apexFile
-		for _, v := range encountered {
-			result = append(result, v)
-		}
-		return result
-	}
-	filesInfo = removeDup(filesInfo)
-
-	// Sort to have consistent build rules
-	sort.Slice(filesInfo, func(i, j int) bool {
-		// Sort by destination path so as to ensure consistent ordering even if the source of the files
-		// changes.
-		return filesInfo[i].path() < filesInfo[j].path()
-	})
-
 	////////////////////////////////////////////////////////////////////////////////////////////
 	// 3) some fields in apexBundle struct are configured
 	a.installDir = android.PathForModuleInstall(ctx, "apex")
-	a.filesInfo = filesInfo
+	a.filesInfo = vctx.filesInfo
 
 	// Set suffix and primaryApexType depending on the ApexType
 	buildFlattenedAsDefault := ctx.Config().FlattenApex()
@@ -2267,7 +2280,7 @@
 
 	////////////////////////////////////////////////////////////////////////////////////////////
 	// 4) generate the build rules to create the APEX. This is done in builder.go.
-	a.buildManifest(ctx, provideNativeLibs, requireNativeLibs)
+	a.buildManifest(ctx, vctx.provideNativeLibs, vctx.requireNativeLibs)
 	if a.properties.ApexType == flattenedApex {
 		a.buildFlattenedApex(ctx)
 	} else {
@@ -2458,7 +2471,7 @@
 	android.BazelModuleBase
 }
 
-func (o *OverrideApex) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+func (o *OverrideApex) GenerateAndroidBuildActions(_ android.ModuleContext) {
 	// All the overrides happen in the base module.
 }
 
diff --git a/apex/builder.go b/apex/builder.go
index fc9bb3b..7e2b924 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -82,6 +82,11 @@
 		Description: "prepare ${out}",
 	}, "provideNativeLibs", "requireNativeLibs", "opt")
 
+	stripCommentsApexManifestRule = pctx.StaticRule("stripCommentsApexManifestRule", blueprint.RuleParams{
+		Command:     `sed '/^\s*\/\//d' $in > $out`,
+		Description: "strip lines starting with // ${in}=>${out}",
+	})
+
 	stripApexManifestRule = pctx.StaticRule("stripApexManifestRule", blueprint.RuleParams{
 		Command:     `rm -f $out && ${conv_apex_manifest} strip $in -o $out`,
 		CommandDeps: []string{"${conv_apex_manifest}"},
@@ -205,10 +210,17 @@
 		optCommands = append(optCommands, "-a jniLibs "+strings.Join(jniLibs, " "))
 	}
 
+	manifestJsonCommentsStripped := android.PathForModuleOut(ctx, "apex_manifest_comments_stripped.json")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   stripCommentsApexManifestRule,
+		Input:  src,
+		Output: manifestJsonCommentsStripped,
+	})
+
 	manifestJsonFullOut := android.PathForModuleOut(ctx, "apex_manifest_full.json")
 	ctx.Build(pctx, android.BuildParams{
 		Rule:   apexManifestRule,
-		Input:  src,
+		Input:  manifestJsonCommentsStripped,
 		Output: manifestJsonFullOut,
 		Args: map[string]string{
 			"provideNativeLibs": strings.Join(provideNativeLibs, " "),
diff --git a/bazel/aquery.go b/bazel/aquery.go
index 6829698..1d1f49c 100644
--- a/bazel/aquery.go
+++ b/bazel/aquery.go
@@ -115,6 +115,8 @@
 // A helper type for aquery processing which facilitates retrieval of path IDs from their
 // less readable Bazel structures (depset and path fragment).
 type aqueryArtifactHandler struct {
+	// Switches to true if any depset contains only `bazelToolsDependencySentinel`
+	bazelToolsDependencySentinelNeeded bool
 	// Maps depset id to AqueryDepset, a representation of depset which is
 	// post-processed for middleman artifact handling, unhandled artifact
 	// dropping, content hashing, etc.
@@ -143,6 +145,9 @@
 // The file name of py3wrapper.sh, which is used by py_binary targets.
 const py3wrapperFileName = "/py3wrapper.sh"
 
+// A file to be put into depsets that are otherwise empty
+const bazelToolsDependencySentinel = "BAZEL_TOOLS_DEPENDENCY_SENTINEL"
+
 func indexBy[K comparable, V any](values []V, keyFn func(v V) K) map[K]V {
 	m := map[K]V{}
 	for _, v := range values {
@@ -219,7 +224,9 @@
 		if depsetsToUse, isMiddleman := middlemanIdToDepsetIds[artifactId]; isMiddleman {
 			// Swap middleman artifacts with their corresponding depsets and drop the middleman artifacts.
 			transitiveDepsetIds = append(transitiveDepsetIds, depsetsToUse...)
-		} else if strings.HasSuffix(path, py3wrapperFileName) || manifestFilePattern.MatchString(path) {
+		} else if strings.HasSuffix(path, py3wrapperFileName) ||
+			manifestFilePattern.MatchString(path) ||
+			strings.HasPrefix(path, "../bazel_tools") {
 			// Drop these artifacts.
 			// See go/python-binary-host-mixed-build for more details.
 			// 1) For py3wrapper.sh, there is no action for creating py3wrapper.sh in the aquery output of
@@ -231,8 +238,9 @@
 			// since there is no build statement to create them, they should be removed from input paths.
 			// TODO(b/197135294): Clean up this custom runfiles handling logic when
 			// SourceSymlinkManifest and SymlinkTree actions are supported.
+			// 3) ../bazel_tools: they have MODIFY timestamp 10years in the future and would cause the
+			// containing depset to always be considered newer than their outputs.
 		} else {
-			// TODO(b/216194240): Filter out bazel tools.
 			directArtifactPaths = append(directArtifactPaths, path)
 		}
 	}
@@ -249,6 +257,13 @@
 		}
 		childDepsetHashes = append(childDepsetHashes, childAqueryDepset.ContentHash)
 	}
+	if len(directArtifactPaths) == 0 && len(childDepsetHashes) == 0 {
+		// We could omit this depset altogether but that requires cleanup on
+		// transitive dependents.
+		// As a simpler alternative, we use this sentinel file as a dependency.
+		directArtifactPaths = append(directArtifactPaths, bazelToolsDependencySentinel)
+		a.bazelToolsDependencySentinelNeeded = true
+	}
 	aqueryDepset := AqueryDepset{
 		ContentHash:            depsetContentHash(directArtifactPaths, childDepsetHashes),
 		DirectArtifacts:        directArtifactPaths,
@@ -317,6 +332,13 @@
 	}
 
 	var buildStatements []BuildStatement
+	if aqueryHandler.bazelToolsDependencySentinelNeeded {
+		buildStatements = append(buildStatements, BuildStatement{
+			Command:     fmt.Sprintf("touch '%s'", bazelToolsDependencySentinel),
+			OutputPaths: []string{bazelToolsDependencySentinel},
+			Mnemonic:    bazelToolsDependencySentinel,
+		})
+	}
 
 	for _, actionEntry := range aqueryResult.Actions {
 		if shouldSkipAction(actionEntry) {
@@ -445,7 +467,7 @@
 	}
 	command := strings.Join(proptools.ShellEscapeListIncludingSpaces(actionEntry.Arguments), " ")
 	inputPaths, command = removePy3wrapperScript(inputPaths, command)
-	command = addCommandForPyBinaryRunfilesDir(command, inputPaths[0], outputPaths[0])
+	command = addCommandForPyBinaryRunfilesDir(command, outputPaths[0])
 	// Add the python zip file as input of the corresponding python binary stub script in Ninja build statements.
 	// In Ninja build statements, the outputs of dependents of a python binary have python binary stub script as input,
 	// which is not sufficient without the python zip file from which runfiles directory is created for py_binary.
@@ -601,7 +623,7 @@
 // TODO(b/205879240) remove this after py3wrapper.sh could be created in the mixed build mode.
 func removePy3wrapperScript(inputPaths []string, command string) (newInputPaths []string, newCommand string) {
 	// Remove from inputs
-	filteredInputPaths := []string{}
+	var filteredInputPaths []string
 	for _, path := range inputPaths {
 		if !strings.HasSuffix(path, py3wrapperFileName) {
 			filteredInputPaths = append(filteredInputPaths, path)
@@ -622,10 +644,10 @@
 // so MANIFEST file could not be created, which also blocks the creation of runfiles directory.
 // See go/python-binary-host-mixed-build for more details.
 // TODO(b/197135294) create runfiles directory from MANIFEST file once it can be created from SourceSymlinkManifest action.
-func addCommandForPyBinaryRunfilesDir(oldCommand string, zipperCommandPath, zipFilePath string) string {
+func addCommandForPyBinaryRunfilesDir(oldCommand string, zipFilePath string) string {
 	// Unzip the zip file, zipFilePath looks like <python_binary>.zip
 	runfilesDirName := zipFilePath[0:len(zipFilePath)-4] + ".runfiles"
-	command := fmt.Sprintf("%s x %s -d %s", zipperCommandPath, zipFilePath, runfilesDirName)
+	command := fmt.Sprintf("%s x %s -d %s", "../bazel_tools/tools/zip/zipper/zipper", zipFilePath, runfilesDirName)
 	// Create a symbolic link in <python_binary>.runfiles/, which is the expected structure
 	// when running the python binary stub script.
 	command += fmt.Sprintf(" && ln -sf runfiles/__main__ %s", runfilesDirName)
diff --git a/bazel/aquery_test.go b/bazel/aquery_test.go
index 1da6340..c759d56 100644
--- a/bazel/aquery_test.go
+++ b/bazel/aquery_test.go
@@ -25,28 +25,14 @@
 	// This input string is retrieved from a real build of bionic-related genrules.
 	const inputString = `
 {
-  "artifacts": [{
-    "id": 1,
-    "pathFragmentId": 1
-  }, {
-    "id": 2,
-    "pathFragmentId": 6
-  }, {
-    "id": 3,
-    "pathFragmentId": 8
-  }, {
-    "id": 4,
-    "pathFragmentId": 12
-  }, {
-    "id": 5,
-    "pathFragmentId": 19
-  }, {
-    "id": 6,
-    "pathFragmentId": 20
-  }, {
-    "id": 7,
-    "pathFragmentId": 21
-  }],
+  "artifacts": [
+    { "id": 1, "pathFragmentId": 1 },
+    { "id": 2, "pathFragmentId": 6 },
+    { "id": 3, "pathFragmentId": 8 },
+    { "id": 4, "pathFragmentId": 12 },
+    { "id": 5, "pathFragmentId": 19 },
+    { "id": 6, "pathFragmentId": 20 },
+    { "id": 7, "pathFragmentId": 21 }],
   "actions": [{
     "targetId": 1,
     "actionKey": "ab53f6ecbdc2ee8cb8812613b63205464f1f5083f6dca87081a0a398c0f1ecf7",
@@ -100,129 +86,45 @@
     "outputIds": [7],
     "primaryOutputId": 7
   }],
-  "targets": [{
-    "id": 1,
-    "label": "@sourceroot//bionic/libc:syscalls-arm",
-    "ruleClassId": 1
-  }, {
-    "id": 2,
-    "label": "@sourceroot//bionic/libc:syscalls-x86",
-    "ruleClassId": 1
-  }, {
-    "id": 3,
-    "label": "@sourceroot//bionic/libc:syscalls-x86_64",
-    "ruleClassId": 1
-  }, {
-    "id": 4,
-    "label": "@sourceroot//bionic/libc:syscalls-arm64",
-    "ruleClassId": 1
-  }],
-  "depSetOfFiles": [{
-    "id": 1,
-    "directArtifactIds": [1, 2, 3]
-  }, {
-    "id": 2,
-    "directArtifactIds": [1, 2, 3]
-  }, {
-    "id": 3,
-    "directArtifactIds": [1, 2, 3]
-  }, {
-    "id": 4,
-    "directArtifactIds": [1, 2, 3]
-  }],
+  "targets": [
+    { "id": 1, "label": "@sourceroot//bionic/libc:syscalls-arm", "ruleClassId": 1 },
+    { "id": 2, "label": "@sourceroot//bionic/libc:syscalls-x86", "ruleClassId": 1 },
+    { "id": 3, "label": "@sourceroot//bionic/libc:syscalls-x86_64", "ruleClassId": 1 },
+    { "id": 4, "label": "@sourceroot//bionic/libc:syscalls-arm64", "ruleClassId": 1 }],
+  "depSetOfFiles": [
+    { "id": 1, "directArtifactIds": [1, 2, 3] },
+    { "id": 2, "directArtifactIds": [1, 2, 3] },
+    { "id": 3, "directArtifactIds": [1, 2, 3] },
+    { "id": 4, "directArtifactIds": [1, 2, 3] }],
   "configuration": [{
     "id": 1,
     "mnemonic": "k8-fastbuild",
     "platformName": "k8",
     "checksum": "485c362832c178e367d972177f68e69e0981e51e67ef1c160944473db53fe046"
   }],
-  "ruleClasses": [{
-    "id": 1,
-    "name": "genrule"
-  }],
-  "pathFragments": [{
-    "id": 5,
-    "label": ".."
-  }, {
-    "id": 4,
-    "label": "sourceroot",
-    "parentId": 5
-  }, {
-    "id": 3,
-    "label": "bionic",
-    "parentId": 4
-  }, {
-    "id": 2,
-    "label": "libc",
-    "parentId": 3
-  }, {
-    "id": 1,
-    "label": "SYSCALLS.TXT",
-    "parentId": 2
-  }, {
-    "id": 7,
-    "label": "tools",
-    "parentId": 2
-  }, {
-    "id": 6,
-    "label": "gensyscalls.py",
-    "parentId": 7
-  }, {
-    "id": 11,
-    "label": "bazel_tools",
-    "parentId": 5
-  }, {
-    "id": 10,
-    "label": "tools",
-    "parentId": 11
-  }, {
-    "id": 9,
-    "label": "genrule",
-    "parentId": 10
-  }, {
-    "id": 8,
-    "label": "genrule-setup.sh",
-    "parentId": 9
-  }, {
-    "id": 18,
-    "label": "bazel-out"
-  }, {
-    "id": 17,
-    "label": "sourceroot",
-    "parentId": 18
-  }, {
-    "id": 16,
-    "label": "k8-fastbuild",
-    "parentId": 17
-  }, {
-    "id": 15,
-    "label": "bin",
-    "parentId": 16
-  }, {
-    "id": 14,
-    "label": "bionic",
-    "parentId": 15
-  }, {
-    "id": 13,
-    "label": "libc",
-    "parentId": 14
-  }, {
-    "id": 12,
-    "label": "syscalls-arm.S",
-    "parentId": 13
-  }, {
-    "id": 19,
-    "label": "syscalls-x86.S",
-    "parentId": 13
-  }, {
-    "id": 20,
-    "label": "syscalls-x86_64.S",
-    "parentId": 13
-  }, {
-    "id": 21,
-    "label": "syscalls-arm64.S",
-    "parentId": 13
-  }]
+  "ruleClasses": [{ "id": 1, "name": "genrule"}],
+  "pathFragments": [
+    { "id": 5, "label": ".." },
+    { "id": 4, "label": "sourceroot", "parentId": 5 },
+    { "id": 3, "label": "bionic", "parentId": 4 },
+    { "id": 2, "label": "libc", "parentId": 3 },
+    { "id": 1, "label": "SYSCALLS.TXT", "parentId": 2 },
+    { "id": 7, "label": "tools", "parentId": 2 },
+    { "id": 6, "label": "gensyscalls.py", "parentId": 7 },
+    { "id": 11, "label": "bazel_tools", "parentId": 5 },
+    { "id": 10, "label": "tools", "parentId": 11 },
+    { "id": 9, "label": "genrule", "parentId": 10 },
+    { "id": 8, "label": "genrule-setup.sh", "parentId": 9 },
+    { "id": 18, "label": "bazel-out" },
+    { "id": 17, "label": "sourceroot", "parentId": 18 },
+    { "id": 16, "label": "k8-fastbuild", "parentId": 17 },
+    { "id": 15, "label": "bin", "parentId": 16 },
+    { "id": 14, "label": "bionic", "parentId": 15 },
+    { "id": 13, "label": "libc", "parentId": 14 },
+    { "id": 12, "label": "syscalls-arm.S", "parentId": 13 },
+    { "id": 19, "label": "syscalls-x86.S", "parentId": 13 },
+    { "id": 20, "label": "syscalls-x86_64.S", "parentId": 13 },
+    { "id": 21, "label": "syscalls-arm64.S", "parentId": 13 }]
 }`
 	actualbuildStatements, actualDepsets, _ := AqueryBuildStatements([]byte(inputString))
 	var expectedBuildStatements []BuildStatement
@@ -246,7 +148,6 @@
 	expectedFlattenedInputs := []string{
 		"../sourceroot/bionic/libc/SYSCALLS.TXT",
 		"../sourceroot/bionic/libc/tools/gensyscalls.py",
-		"../bazel_tools/tools/genrule/genrule-setup.sh",
 	}
 	// In this example, each depset should have the same expected inputs.
 	for _, actualDepset := range actualDepsets {
@@ -260,13 +161,9 @@
 func TestInvalidOutputId(t *testing.T) {
 	const inputString = `
 {
-  "artifacts": [{
-    "id": 1,
-    "pathFragmentId": 1
-  }, {
-    "id": 2,
-    "pathFragmentId": 2
-  }],
+  "artifacts": [
+    { "id": 1, "pathFragmentId": 1 },
+    { "id": 2, "pathFragmentId": 2 }],
   "actions": [{
     "targetId": 1,
     "actionKey": "x",
@@ -276,17 +173,11 @@
     "outputIds": [3],
     "primaryOutputId": 3
   }],
-  "depSetOfFiles": [{
-    "id": 1,
-    "directArtifactIds": [1, 2]
-  }],
-  "pathFragments": [{
-    "id": 1,
-    "label": "one"
-  }, {
-    "id": 2,
-    "label": "two"
-  }]
+  "depSetOfFiles": [
+    { "id": 1, "directArtifactIds": [1, 2] }],
+  "pathFragments": [
+    { "id": 1, "label": "one" },
+    { "id": 2, "label": "two" }]
 }`
 
 	_, _, err := AqueryBuildStatements([]byte(inputString))
@@ -296,13 +187,9 @@
 func TestInvalidInputDepsetIdFromAction(t *testing.T) {
 	const inputString = `
 {
-  "artifacts": [{
-    "id": 1,
-    "pathFragmentId": 1
-  }, {
-    "id": 2,
-    "pathFragmentId": 2
-  }],
+  "artifacts": [
+    { "id": 1, "pathFragmentId": 1 },
+    { "id": 2, "pathFragmentId": 2 }],
   "actions": [{
     "targetId": 1,
     "actionKey": "x",
@@ -312,17 +199,11 @@
     "outputIds": [1],
     "primaryOutputId": 1
   }],
-  "depSetOfFiles": [{
-    "id": 1,
-    "directArtifactIds": [1, 2]
-  }],
-  "pathFragments": [{
-    "id": 1,
-    "label": "one"
-  }, {
-    "id": 2,
-    "label": "two"
-  }]
+  "depSetOfFiles": [
+    { "id": 1, "directArtifactIds": [1, 2] }],
+  "pathFragments": [
+    { "id": 1, "label": "one" },
+    { "id": 2, "label": "two" }]
 }`
 
 	_, _, err := AqueryBuildStatements([]byte(inputString))
@@ -332,13 +213,9 @@
 func TestInvalidInputDepsetIdFromDepset(t *testing.T) {
 	const inputString = `
 {
-  "artifacts": [{
-    "id": 1,
-    "pathFragmentId": 1
-  }, {
-    "id": 2,
-    "pathFragmentId": 2
-  }],
+  "artifacts": [
+    { "id": 1, "pathFragmentId": 1 },
+    { "id": 2, "pathFragmentId": 2 }],
   "actions": [{
     "targetId": 1,
     "actionKey": "x",
@@ -348,18 +225,11 @@
     "outputIds": [1],
     "primaryOutputId": 1
   }],
-  "depSetOfFiles": [{
-    "id": 1,
-    "directArtifactIds": [1, 2],
-    "transitiveDepSetIds": [42]
-  }],
-  "pathFragments": [{
-    "id": 1,
-    "label": "one"
-  }, {
-    "id": 2,
-    "label": "two"
-  }]
+  "depSetOfFiles": [
+    { "id": 1, "directArtifactIds": [1, 2], "transitiveDepSetIds": [42] }],
+  "pathFragments": [
+    { "id": 1, "label": "one"},
+    { "id": 2, "label": "two" }]
 }`
 
 	_, _, err := AqueryBuildStatements([]byte(inputString))
@@ -369,13 +239,9 @@
 func TestInvalidInputArtifactId(t *testing.T) {
 	const inputString = `
 {
-  "artifacts": [{
-    "id": 1,
-    "pathFragmentId": 1
-  }, {
-    "id": 2,
-    "pathFragmentId": 2
-  }],
+  "artifacts": [
+    { "id": 1, "pathFragmentId": 1 },
+    { "id": 2, "pathFragmentId": 2 }],
   "actions": [{
     "targetId": 1,
     "actionKey": "x",
@@ -385,17 +251,11 @@
     "outputIds": [1],
     "primaryOutputId": 1
   }],
-  "depSetOfFiles": [{
-    "id": 1,
-    "directArtifactIds": [1, 3]
-  }],
-  "pathFragments": [{
-    "id": 1,
-    "label": "one"
-  }, {
-    "id": 2,
-    "label": "two"
-  }]
+  "depSetOfFiles": [
+    { "id": 1, "directArtifactIds": [1, 3] }],
+  "pathFragments": [
+    { "id": 1, "label": "one" },
+    { "id": 2, "label": "two" }]
 }`
 
 	_, _, err := AqueryBuildStatements([]byte(inputString))
@@ -405,13 +265,9 @@
 func TestInvalidPathFragmentId(t *testing.T) {
 	const inputString = `
 {
-  "artifacts": [{
-    "id": 1,
-    "pathFragmentId": 1
-  }, {
-    "id": 2,
-    "pathFragmentId": 2
-  }],
+  "artifacts": [
+    { "id": 1, "pathFragmentId": 1 },
+    { "id": 2, "pathFragmentId": 2 }],
   "actions": [{
     "targetId": 1,
     "actionKey": "x",
@@ -421,18 +277,11 @@
     "outputIds": [1],
     "primaryOutputId": 1
   }],
-  "depSetOfFiles": [{
-    "id": 1,
-    "directArtifactIds": [1, 2]
-  }],
-  "pathFragments": [{
-    "id": 1,
-    "label": "one"
-  }, {
-    "id": 2,
-    "label": "two",
-		"parentId": 3
-  }]
+  "depSetOfFiles": [
+     { "id": 1, "directArtifactIds": [1, 2] }],
+  "pathFragments": [
+    {  "id": 1, "label": "one" },
+    {  "id": 2, "label": "two", "parentId": 3 }]
 }`
 
 	_, _, err := AqueryBuildStatements([]byte(inputString))
@@ -442,16 +291,10 @@
 func TestDepfiles(t *testing.T) {
 	const inputString = `
 {
-  "artifacts": [{
-    "id": 1,
-    "pathFragmentId": 1
-  }, {
-    "id": 2,
-    "pathFragmentId": 2
-  }, {
-    "id": 3,
-    "pathFragmentId": 3
-  }],
+  "artifacts": [
+    { "id": 1, "pathFragmentId": 1 },
+    { "id": 2, "pathFragmentId": 2 },
+    { "id": 3, "pathFragmentId": 3 }],
   "actions": [{
     "targetId": 1,
     "actionKey": "x",
@@ -461,20 +304,12 @@
     "outputIds": [2, 3],
     "primaryOutputId": 2
   }],
-  "depSetOfFiles": [{
-    "id": 1,
-    "directArtifactIds": [1, 2, 3]
-  }],
-  "pathFragments": [{
-    "id": 1,
-    "label": "one"
-  }, {
-    "id": 2,
-    "label": "two"
-  }, {
-    "id": 3,
-    "label": "two.d"
-  }]
+  "depSetOfFiles": [
+    { "id": 1, "directArtifactIds": [1, 2, 3] }],
+  "pathFragments": [
+    { "id": 1, "label": "one" },
+    { "id": 2, "label": "two" },
+    { "id": 3, "label": "two.d" }]
 }`
 
 	actual, _, err := AqueryBuildStatements([]byte(inputString))
@@ -497,19 +332,11 @@
 func TestMultipleDepfiles(t *testing.T) {
 	const inputString = `
 {
-  "artifacts": [{
-    "id": 1,
-    "pathFragmentId": 1
-  }, {
-    "id": 2,
-    "pathFragmentId": 2
-  }, {
-    "id": 3,
-    "pathFragmentId": 3
-  }, {
-    "id": 4,
-    "pathFragmentId": 4
-  }],
+  "artifacts": [
+    { "id": 1, "pathFragmentId": 1 },
+    { "id": 2, "pathFragmentId": 2 },
+    { "id": 3, "pathFragmentId": 3 },
+    { "id": 4, "pathFragmentId": 4 }],
   "actions": [{
     "targetId": 1,
     "actionKey": "x",
@@ -523,19 +350,11 @@
     "id": 1,
     "directArtifactIds": [1, 2, 3, 4]
   }],
-  "pathFragments": [{
-    "id": 1,
-    "label": "one"
-  }, {
-    "id": 2,
-    "label": "two"
-  }, {
-    "id": 3,
-    "label": "two.d"
-  }, {
-    "id": 4,
-    "label": "other.d"
-  }]
+  "pathFragments": [
+    { "id": 1, "label": "one" },
+    { "id": 2, "label": "two" },
+    { "id": 3, "label": "two.d" },
+    { "id": 4, "label": "other.d" }]
 }`
 
 	_, _, err := AqueryBuildStatements([]byte(inputString))
@@ -547,70 +366,28 @@
 	// a single action with many inputs given via a deep depset.
 	const inputString = `
 {
-  "artifacts": [{
-    "id": 1,
-    "pathFragmentId": 1
-  }, {
-    "id": 2,
-    "pathFragmentId": 7
-  }, {
-    "id": 3,
-    "pathFragmentId": 8
-  }, {
-    "id": 4,
-    "pathFragmentId": 9
-  }, {
-    "id": 5,
-    "pathFragmentId": 10
-  }, {
-    "id": 6,
-    "pathFragmentId": 11
-  }, {
-    "id": 7,
-    "pathFragmentId": 12
-  }, {
-    "id": 8,
-    "pathFragmentId": 13
-  }, {
-    "id": 9,
-    "pathFragmentId": 14
-  }, {
-    "id": 10,
-    "pathFragmentId": 15
-  }, {
-    "id": 11,
-    "pathFragmentId": 16
-  }, {
-    "id": 12,
-    "pathFragmentId": 17
-  }, {
-    "id": 13,
-    "pathFragmentId": 18
-  }, {
-    "id": 14,
-    "pathFragmentId": 19
-  }, {
-    "id": 15,
-    "pathFragmentId": 20
-  }, {
-    "id": 16,
-    "pathFragmentId": 21
-  }, {
-    "id": 17,
-    "pathFragmentId": 22
-  }, {
-    "id": 18,
-    "pathFragmentId": 23
-  }, {
-    "id": 19,
-    "pathFragmentId": 24
-  }, {
-    "id": 20,
-    "pathFragmentId": 25
-  }, {
-    "id": 21,
-    "pathFragmentId": 26
-  }],
+  "artifacts": [
+   { "id": 1, "pathFragmentId": 1 },
+   { "id": 2, "pathFragmentId": 7 },
+   { "id": 3, "pathFragmentId": 8 },
+   { "id": 4, "pathFragmentId": 9 },
+   { "id": 5, "pathFragmentId": 10 },
+   { "id": 6, "pathFragmentId": 11 },
+   { "id": 7, "pathFragmentId": 12 },
+   { "id": 8, "pathFragmentId": 13 },
+   { "id": 9, "pathFragmentId": 14 },
+   { "id": 10, "pathFragmentId": 15 },
+   { "id": 11, "pathFragmentId": 16 },
+   { "id": 12, "pathFragmentId": 17 },
+   { "id": 13, "pathFragmentId": 18 },
+   { "id": 14, "pathFragmentId": 19 },
+   { "id": 15, "pathFragmentId": 20 },
+   { "id": 16, "pathFragmentId": 21 },
+   { "id": 17, "pathFragmentId": 22 },
+   { "id": 18, "pathFragmentId": 23 },
+   { "id": 19, "pathFragmentId": 24 },
+   { "id": 20, "pathFragmentId": 25 },
+   { "id": 21, "pathFragmentId": 26 }],
   "actions": [{
     "targetId": 1,
     "actionKey": "3b826d17fadbbbcd8313e456b90ec47c078c438088891dd45b4adbcd8889dc50",
@@ -621,128 +398,39 @@
     "outputIds": [21],
     "primaryOutputId": 21
   }],
-  "depSetOfFiles": [{
-    "id": 3,
-    "directArtifactIds": [1, 2, 3, 4, 5]
-  }, {
-    "id": 4,
-    "directArtifactIds": [6, 7, 8, 9, 10]
-  }, {
-    "id": 2,
-    "transitiveDepSetIds": [3, 4],
-    "directArtifactIds": [11, 12, 13, 14, 15]
-  }, {
-    "id": 5,
-    "directArtifactIds": [16, 17, 18, 19]
-  }, {
-    "id": 1,
-    "transitiveDepSetIds": [2, 5],
-    "directArtifactIds": [20]
-  }],
-  "pathFragments": [{
-    "id": 6,
-    "label": "bazel-out"
-  }, {
-    "id": 5,
-    "label": "sourceroot",
-    "parentId": 6
-  }, {
-    "id": 4,
-    "label": "k8-fastbuild",
-    "parentId": 5
-  }, {
-    "id": 3,
-    "label": "bin",
-    "parentId": 4
-  }, {
-    "id": 2,
-    "label": "testpkg",
-    "parentId": 3
-  }, {
-    "id": 1,
-    "label": "test_1",
-    "parentId": 2
-  }, {
-    "id": 7,
-    "label": "test_2",
-    "parentId": 2
-  }, {
-    "id": 8,
-    "label": "test_3",
-    "parentId": 2
-  }, {
-    "id": 9,
-    "label": "test_4",
-    "parentId": 2
-  }, {
-    "id": 10,
-    "label": "test_5",
-    "parentId": 2
-  }, {
-    "id": 11,
-    "label": "test_6",
-    "parentId": 2
-  }, {
-    "id": 12,
-    "label": "test_7",
-    "parentId": 2
-  }, {
-    "id": 13,
-    "label": "test_8",
-    "parentId": 2
-  }, {
-    "id": 14,
-    "label": "test_9",
-    "parentId": 2
-  }, {
-    "id": 15,
-    "label": "test_10",
-    "parentId": 2
-  }, {
-    "id": 16,
-    "label": "test_11",
-    "parentId": 2
-  }, {
-    "id": 17,
-    "label": "test_12",
-    "parentId": 2
-  }, {
-    "id": 18,
-    "label": "test_13",
-    "parentId": 2
-  }, {
-    "id": 19,
-    "label": "test_14",
-    "parentId": 2
-  }, {
-    "id": 20,
-    "label": "test_15",
-    "parentId": 2
-  }, {
-    "id": 21,
-    "label": "test_16",
-    "parentId": 2
-  }, {
-    "id": 22,
-    "label": "test_17",
-    "parentId": 2
-  }, {
-    "id": 23,
-    "label": "test_18",
-    "parentId": 2
-  }, {
-    "id": 24,
-    "label": "test_19",
-    "parentId": 2
-  }, {
-    "id": 25,
-    "label": "test_root",
-    "parentId": 2
-  }, {
-    "id": 26,
-    "label": "test_out",
-    "parentId": 2
-  }]
+  "depSetOfFiles": [
+    { "id": 3, "directArtifactIds": [1, 2, 3, 4, 5] },
+    { "id": 4, "directArtifactIds": [6, 7, 8, 9, 10] },
+    { "id": 2, "transitiveDepSetIds": [3, 4], "directArtifactIds": [11, 12, 13, 14, 15] },
+    { "id": 5, "directArtifactIds": [16, 17, 18, 19] },
+    { "id": 1, "transitiveDepSetIds": [2, 5], "directArtifactIds": [20] }],
+  "pathFragments": [
+    { "id": 6, "label": "bazel-out" },
+    { "id": 5, "label": "sourceroot", "parentId": 6 },
+    { "id": 4, "label": "k8-fastbuild", "parentId": 5 },
+    { "id": 3, "label": "bin", "parentId": 4 },
+    { "id": 2, "label": "testpkg", "parentId": 3 },
+    { "id": 1, "label": "test_1", "parentId": 2 },
+    { "id": 7, "label": "test_2", "parentId": 2 },
+    { "id": 8, "label": "test_3", "parentId": 2 },
+    { "id": 9, "label": "test_4", "parentId": 2 },
+    { "id": 10, "label": "test_5", "parentId": 2 },
+    { "id": 11, "label": "test_6", "parentId": 2 },
+    { "id": 12, "label": "test_7", "parentId": 2 },
+    { "id": 13, "label": "test_8", "parentId": 2 },
+    { "id": 14, "label": "test_9", "parentId": 2 },
+    { "id": 15, "label": "test_10", "parentId": 2 },
+    { "id": 16, "label": "test_11", "parentId": 2 },
+    { "id": 17, "label": "test_12", "parentId": 2 },
+    { "id": 18, "label": "test_13", "parentId": 2 },
+    { "id": 19, "label": "test_14", "parentId": 2 },
+    { "id": 20, "label": "test_15", "parentId": 2 },
+    { "id": 21, "label": "test_16", "parentId": 2 },
+    { "id": 22, "label": "test_17", "parentId": 2 },
+    { "id": 23, "label": "test_18", "parentId": 2 },
+    { "id": 24, "label": "test_19", "parentId": 2 },
+    { "id": 25, "label": "test_root", "parentId": 2 },
+    { "id": 26,"label": "test_out", "parentId": 2 }]
 }`
 
 	actualbuildStatements, actualDepsets, _ := AqueryBuildStatements([]byte(inputString))
@@ -772,54 +460,112 @@
 	}
 }
 
+func TestBazelOutRemovalFromInputDepsets(t *testing.T) {
+	const inputString = `{
+  "artifacts": [{
+    "id": 1,
+    "pathFragmentId": 10
+  }, {
+    "id": 2,
+    "pathFragmentId": 20
+  }, {
+    "id": 3,
+    "pathFragmentId": 30
+  }, {
+    "id": 4,
+    "pathFragmentId": 40
+  }],
+  "depSetOfFiles": [{
+    "id": 1111,
+    "directArtifactIds": [3 , 4]
+  }],
+  "actions": [{
+    "targetId": 100,
+    "actionKey": "x",
+    "inputDepSetIds": [1111],
+    "mnemonic": "x",
+    "arguments": ["bogus", "command"],
+    "outputIds": [2],
+    "primaryOutputId": 1
+  }],
+  "pathFragments": [{
+    "id": 10,
+    "label": "input"
+  }, {
+    "id": 20,
+    "label": "output"
+  }, {
+    "id": 30,
+    "label": "dep1",
+    "parentId": 50
+  }, {
+    "id": 40,
+    "label": "dep2",
+    "parentId": 60
+  }, {
+    "id": 50,
+    "label": "bazel_tools",
+    "parentId": 60
+  }, {
+    "id": 60,
+    "label": ".."
+  }]
+}`
+	actualBuildStatements, actualDepsets, _ := AqueryBuildStatements([]byte(inputString))
+	if len(actualDepsets) != 1 {
+		t.Errorf("expected 1 depset but found %#v", actualDepsets)
+		return
+	}
+	dep2Found := false
+	for _, dep := range flattenDepsets([]string{actualDepsets[0].ContentHash}, actualDepsets) {
+		if dep == "../bazel_tools/dep1" {
+			t.Errorf("dependency %s expected to be removed but still exists", dep)
+		} else if dep == "../dep2" {
+			dep2Found = true
+		}
+	}
+	if !dep2Found {
+		t.Errorf("dependency ../dep2 expected but not found")
+	}
+
+	expectedBuildStatement := BuildStatement{
+		Command:     "bogus command",
+		OutputPaths: []string{"output"},
+		Mnemonic:    "x",
+	}
+	buildStatementFound := false
+	for _, actualBuildStatement := range actualBuildStatements {
+		if buildStatementEquals(actualBuildStatement, expectedBuildStatement) == "" {
+			buildStatementFound = true
+			break
+		}
+	}
+	if !buildStatementFound {
+		t.Errorf("expected but missing %#v in %#v", expectedBuildStatement, actualBuildStatements)
+		return
+	}
+}
+
 func TestMiddlemenAction(t *testing.T) {
 	const inputString = `
 {
-  "artifacts": [{
-    "id": 1,
-    "pathFragmentId": 1
-  }, {
-    "id": 2,
-    "pathFragmentId": 2
-  }, {
-    "id": 3,
-    "pathFragmentId": 3
-  }, {
-    "id": 4,
-    "pathFragmentId": 4
-  }, {
-    "id": 5,
-    "pathFragmentId": 5
-  }, {
-    "id": 6,
-    "pathFragmentId": 6
-  }],
-  "pathFragments": [{
-    "id": 1,
-    "label": "middleinput_one"
-  }, {
-    "id": 2,
-    "label": "middleinput_two"
-  }, {
-    "id": 3,
-    "label": "middleman_artifact"
-  }, {
-    "id": 4,
-    "label": "maininput_one"
-  }, {
-    "id": 5,
-    "label": "maininput_two"
-  }, {
-    "id": 6,
-    "label": "output"
-  }],
-  "depSetOfFiles": [{
-    "id": 1,
-    "directArtifactIds": [1, 2]
-  }, {
-    "id": 2,
-    "directArtifactIds": [3, 4, 5]
-  }],
+  "artifacts": [
+    { "id": 1, "pathFragmentId": 1 },
+    { "id": 2, "pathFragmentId": 2 },
+    { "id": 3, "pathFragmentId": 3 },
+    { "id": 4, "pathFragmentId": 4 },
+    { "id": 5, "pathFragmentId": 5 },
+    { "id": 6, "pathFragmentId": 6 }],
+  "pathFragments": [
+    { "id": 1, "label": "middleinput_one" },
+    { "id": 2, "label": "middleinput_two" },
+    { "id": 3, "label": "middleman_artifact" },
+    { "id": 4, "label": "maininput_one" },
+    { "id": 5, "label": "maininput_two" },
+    { "id": 6, "label": "output" }],
+  "depSetOfFiles": [
+    { "id": 1, "directArtifactIds": [1, 2] },
+    { "id": 2, "directArtifactIds": [3, 4, 5] }],
   "actions": [{
     "targetId": 1,
     "actionKey": "x",
@@ -911,13 +657,9 @@
 func TestSimpleSymlink(t *testing.T) {
 	const inputString = `
 {
-  "artifacts": [{
-    "id": 1,
-    "pathFragmentId": 3
-  }, {
-    "id": 2,
-    "pathFragmentId": 5
-  }],
+  "artifacts": [
+    { "id": 1, "pathFragmentId": 3 },
+    { "id": 2, "pathFragmentId": 5 }],
   "actions": [{
     "targetId": 1,
     "actionKey": "x",
@@ -926,30 +668,14 @@
     "outputIds": [2],
     "primaryOutputId": 2
   }],
-  "depSetOfFiles": [{
-    "id": 1,
-    "directArtifactIds": [1]
-  }],
-  "pathFragments": [{
-    "id": 1,
-    "label": "one"
-  }, {
-    "id": 2,
-    "label": "file_subdir",
-    "parentId": 1
-  }, {
-    "id": 3,
-    "label": "file",
-    "parentId": 2
-  }, {
-    "id": 4,
-    "label": "symlink_subdir",
-    "parentId": 1
-  }, {
-    "id": 5,
-    "label": "symlink",
-    "parentId": 4
-  }]
+  "depSetOfFiles": [
+    { "id": 1, "directArtifactIds": [1] }],
+  "pathFragments": [
+    { "id": 1, "label": "one" },
+    { "id": 2, "label": "file_subdir", "parentId": 1 },
+    { "id": 3, "label": "file", "parentId": 2 },
+    { "id": 4, "label": "symlink_subdir", "parentId": 1 },
+    { "id": 5, "label": "symlink", "parentId": 4 }]
 }`
 
 	actual, _, err := AqueryBuildStatements([]byte(inputString))
@@ -975,13 +701,9 @@
 func TestSymlinkQuotesPaths(t *testing.T) {
 	const inputString = `
 {
-  "artifacts": [{
-    "id": 1,
-    "pathFragmentId": 3
-  }, {
-    "id": 2,
-    "pathFragmentId": 5
-  }],
+  "artifacts": [
+    { "id": 1, "pathFragmentId": 3 },
+    { "id": 2, "pathFragmentId": 5 }],
   "actions": [{
     "targetId": 1,
     "actionKey": "x",
@@ -990,30 +712,14 @@
     "outputIds": [2],
     "primaryOutputId": 2
   }],
-  "depSetOfFiles": [{
-    "id": 1,
-    "directArtifactIds": [1]
-  }],
-  "pathFragments": [{
-    "id": 1,
-    "label": "one"
-  }, {
-    "id": 2,
-    "label": "file subdir",
-    "parentId": 1
-  }, {
-    "id": 3,
-    "label": "file",
-    "parentId": 2
-  }, {
-    "id": 4,
-    "label": "symlink subdir",
-    "parentId": 1
-  }, {
-    "id": 5,
-    "label": "symlink",
-    "parentId": 4
-  }]
+  "depSetOfFiles": [
+    { "id": 1, "directArtifactIds": [1] }],
+  "pathFragments": [
+    { "id": 1, "label": "one" },
+    { "id": 2, "label": "file subdir", "parentId": 1 },
+    { "id": 3, "label": "file", "parentId": 2 },
+    { "id": 4, "label": "symlink subdir", "parentId": 1 },
+    { "id": 5, "label": "symlink", "parentId": 4 }]
 }`
 
 	actual, _, err := AqueryBuildStatements([]byte(inputString))
@@ -1039,16 +745,10 @@
 func TestSymlinkMultipleInputs(t *testing.T) {
 	const inputString = `
 {
-  "artifacts": [{
-    "id": 1,
-    "pathFragmentId": 1
-  }, {
-    "id": 2,
-    "pathFragmentId": 2
-  }, {
-    "id": 3,
-    "pathFragmentId": 3
-  }],
+  "artifacts": [
+    { "id": 1, "pathFragmentId": 1 },
+    { "id": 2, "pathFragmentId": 2 },
+    { "id": 3, "pathFragmentId": 3 }],
   "actions": [{
     "targetId": 1,
     "actionKey": "x",
@@ -1057,20 +757,11 @@
     "outputIds": [3],
     "primaryOutputId": 3
   }],
-  "depSetOfFiles": [{
-    "id": 1,
-    "directArtifactIds": [1,2]
-  }],
-  "pathFragments": [{
-    "id": 1,
-    "label": "file"
-  }, {
-    "id": 2,
-    "label": "other_file"
-  }, {
-    "id": 3,
-    "label": "symlink"
-  }]
+  "depSetOfFiles": [{ "id": 1, "directArtifactIds": [1,2] }],
+  "pathFragments": [
+    { "id": 1, "label": "file" },
+    { "id": 2, "label": "other_file" },
+    { "id": 3, "label": "symlink" }]
 }`
 
 	_, _, err := AqueryBuildStatements([]byte(inputString))
@@ -1080,16 +771,10 @@
 func TestSymlinkMultipleOutputs(t *testing.T) {
 	const inputString = `
 {
-  "artifacts": [{
-    "id": 1,
-    "pathFragmentId": 1
-  }, {
-    "id": 2,
-    "pathFragmentId": 2
-  }, {
-    "id": 3,
-    "pathFragmentId": 3
-  }],
+  "artifacts": [
+    { "id": 1, "pathFragmentId": 1 },
+    { "id": 2, "pathFragmentId": 2 },
+    { "id": 3, "pathFragmentId": 3 }],
   "actions": [{
     "targetId": 1,
     "actionKey": "x",
@@ -1098,20 +783,12 @@
     "outputIds": [2,3],
     "primaryOutputId": 2
   }],
-  "depSetOfFiles": [{
-    "id": 1,
-    "directArtifactIds": [1]
-  }],
-  "pathFragments": [{
-    "id": 1,
-    "label": "file"
-  }, {
-    "id": 2,
-    "label": "symlink"
-  }, {
-    "id": 3,
-    "label": "other_symlink"
-  }]
+  "depSetOfFiles": [
+    { "id": 1, "directArtifactIds": [1] }],
+  "pathFragments": [
+    { "id": 1, "label": "file" },
+    { "id": 2, "label": "symlink" },
+    { "id": 3,  "label": "other_symlink" }]
 }`
 
 	_, _, err := AqueryBuildStatements([]byte(inputString))
@@ -1134,18 +811,12 @@
     "primaryOutputId": 1,
     "executionPlatform": "//build/bazel/platforms:linux_x86_64",
     "templateContent": "Test template substitutions: %token1%, %python_binary%",
-    "substitutions": [{
-      "key": "%token1%",
-      "value": "abcd"
-    },{
-      "key": "%python_binary%",
-      "value": "python3"
-    }]
+    "substitutions": [
+      { "key": "%token1%", "value": "abcd" },
+      { "key": "%python_binary%", "value": "python3" }]
   }],
-  "pathFragments": [{
-    "id": 1,
-    "label": "template_file"
-  }]
+  "pathFragments": [
+    { "id": 1, "label": "template_file" }]
 }`
 
 	actual, _, err := AqueryBuildStatements([]byte(inputString))
@@ -1168,10 +839,8 @@
 func TestTemplateExpandActionNoOutput(t *testing.T) {
 	const inputString = `
 {
-  "artifacts": [{
-    "id": 1,
-    "pathFragmentId": 1
-  }],
+  "artifacts": [
+    { "id": 1, "pathFragmentId": 1 }],
   "actions": [{
     "targetId": 1,
     "actionKey": "x",
@@ -1180,18 +849,12 @@
     "primaryOutputId": 1,
     "executionPlatform": "//build/bazel/platforms:linux_x86_64",
     "templateContent": "Test template substitutions: %token1%, %python_binary%",
-    "substitutions": [{
-      "key": "%token1%",
-      "value": "abcd"
-    },{
-      "key": "%python_binary%",
-      "value": "python3"
-    }]
+    "substitutions": [
+      { "key": "%token1%", "value": "abcd" },
+      { "key": "%python_binary%", "value": "python3" }]
   }],
-  "pathFragments": [{
-    "id": 1,
-    "label": "template_file"
-  }]
+  "pathFragments": [
+    { "id": 1, "label": "template_file" }]
 }`
 
 	_, _, err := AqueryBuildStatements([]byte(inputString))
@@ -1201,25 +864,13 @@
 func TestPythonZipperActionSuccess(t *testing.T) {
 	const inputString = `
 {
-  "artifacts": [{
-    "id": 1,
-    "pathFragmentId": 1
-  },{
-    "id": 2,
-    "pathFragmentId": 2
-  },{
-    "id": 3,
-    "pathFragmentId": 3
-  },{
-    "id": 4,
-    "pathFragmentId": 4
-  },{
-    "id": 5,
-    "pathFragmentId": 10
-  },{
-    "id": 10,
-    "pathFragmentId": 20
-  }],
+  "artifacts": [
+    { "id": 1, "pathFragmentId": 1 },
+    { "id": 2, "pathFragmentId": 2 },
+    { "id": 3, "pathFragmentId": 3 },
+    { "id": 4, "pathFragmentId": 4 },
+    { "id": 5, "pathFragmentId": 10 },
+    { "id": 10, "pathFragmentId": 20 }],
   "actions": [{
     "targetId": 1,
     "actionKey": "x",
@@ -1246,73 +897,26 @@
     "inputDepSetIds": [1],
     "primaryOutputId": 2
   }],
-  "depSetOfFiles": [{
-    "id": 1,
-    "directArtifactIds": [4, 3, 5]
-  }],
-  "pathFragments": [{
-    "id": 1,
-    "label": "python_binary"
-  },{
-    "id": 2,
-    "label": "python_binary.zip"
-  },{
-    "id": 3,
-    "label": "python_binary.py"
-  },{
-    "id": 9,
-    "label": ".."
-  }, {
-    "id": 8,
-    "label": "bazel_tools",
-    "parentId": 9
-  }, {
-    "id": 7,
-    "label": "tools",
-    "parentId": 8
-  }, {
-    "id": 6,
-    "label": "zip",
-    "parentId": 7
-  }, {
-    "id": 5,
-    "label": "zipper",
-    "parentId": 6
-  }, {
-    "id": 4,
-    "label": "zipper",
-    "parentId": 5
-  },{
-    "id": 16,
-    "label": "bazel-out"
-  },{
-    "id": 15,
-    "label": "bazel_tools",
-    "parentId": 16
-  }, {
-    "id": 14,
-    "label": "k8-fastbuild",
-    "parentId": 15
-  }, {
-    "id": 13,
-    "label": "bin",
-    "parentId": 14
-  }, {
-    "id": 12,
-    "label": "tools",
-    "parentId": 13
-  }, {
-    "id": 11,
-    "label": "python",
-    "parentId": 12
-  }, {
-    "id": 10,
-    "label": "py3wrapper.sh",
-    "parentId": 11
-  },{
-    "id": 20,
-    "label": "python_binary"
-  }]
+  "depSetOfFiles": [
+    { "id": 1, "directArtifactIds": [4, 3, 5] }],
+  "pathFragments": [
+    { "id": 1, "label": "python_binary" },
+    { "id": 2, "label": "python_binary.zip" },
+    { "id": 3, "label": "python_binary.py" },
+    { "id": 9, "label": ".." },
+    { "id": 8, "label": "bazel_tools", "parentId": 9 },
+    { "id": 7, "label": "tools", "parentId": 8 },
+    { "id": 6, "label": "zip", "parentId": 7  },
+    { "id": 5, "label": "zipper", "parentId": 6 },
+    { "id": 4, "label": "zipper", "parentId": 5 },
+    { "id": 16, "label": "bazel-out" },
+    { "id": 15, "label": "bazel_tools", "parentId": 16 },
+    { "id": 14, "label": "k8-fastbuild", "parentId": 15 },
+    { "id": 13, "label": "bin", "parentId": 14 },
+    { "id": 12, "label": "tools", "parentId": 13 },
+    { "id": 11, "label": "python", "parentId": 12 },
+    { "id": 10, "label": "py3wrapper.sh", "parentId": 11 },
+    { "id": 20, "label": "python_binary" }]
 }`
 	actual, _, err := AqueryBuildStatements([]byte(inputString))
 
@@ -1332,7 +936,7 @@
 			Command: "../bazel_tools/tools/zip/zipper/zipper cC python_binary.zip __main__.py=bazel-out/k8-fastbuild/bin/python_binary.temp " +
 				"__init__.py= runfiles/__main__/__init__.py= runfiles/__main__/python_binary.py=python_binary.py  && " +
 				"../bazel_tools/tools/zip/zipper/zipper x python_binary.zip -d python_binary.runfiles && ln -sf runfiles/__main__ python_binary.runfiles",
-			InputPaths:  []string{"../bazel_tools/tools/zip/zipper/zipper", "python_binary.py"},
+			InputPaths:  []string{"python_binary.py"},
 			OutputPaths: []string{"python_binary.zip"},
 			Mnemonic:    "PythonZipper",
 		},
@@ -1343,13 +947,9 @@
 func TestPythonZipperActionNoInput(t *testing.T) {
 	const inputString = `
 {
-  "artifacts": [{
-    "id": 1,
-    "pathFragmentId": 1
-  },{
-    "id": 2,
-    "pathFragmentId": 2
-  }],
+  "artifacts": [
+    { "id": 1, "pathFragmentId": 1 },
+    { "id": 2, "pathFragmentId": 2 }],
   "actions": [{
     "targetId": 1,
     "actionKey": "x",
@@ -1359,13 +959,9 @@
     "outputIds": [2],
     "primaryOutputId": 2
   }],
-  "pathFragments": [{
-    "id": 1,
-    "label": "python_binary"
-  },{
-    "id": 2,
-    "label": "python_binary.zip"
-  }]
+  "pathFragments": [
+    { "id": 1, "label": "python_binary" },
+    { "id": 2, "label": "python_binary.zip" }]
 }`
 	_, _, err := AqueryBuildStatements([]byte(inputString))
 	assertError(t, err, `Expect 1+ input and 1 output to python zipper action, got: input [], output ["python_binary.zip"]`)
@@ -1374,22 +970,12 @@
 func TestPythonZipperActionNoOutput(t *testing.T) {
 	const inputString = `
 {
-  "artifacts": [{
-    "id": 1,
-    "pathFragmentId": 1
-  },{
-    "id": 2,
-    "pathFragmentId": 2
-  },{
-    "id": 3,
-    "pathFragmentId": 3
-  },{
-    "id": 4,
-    "pathFragmentId": 4
-  },{
-    "id": 5,
-    "pathFragmentId": 10
-  }],
+  "artifacts": [
+    { "id": 1, "pathFragmentId": 1 },
+    { "id": 2, "pathFragmentId": 2 },
+    { "id": 3, "pathFragmentId": 3 },
+    { "id": 4, "pathFragmentId": 4 },
+    { "id": 5, "pathFragmentId": 10 }],
   "actions": [{
     "targetId": 1,
     "actionKey": "x",
@@ -1398,73 +984,28 @@
     "arguments": ["../bazel_tools/tools/zip/zipper/zipper", "cC", "python_binary.zip", "__main__.py\u003dbazel-out/k8-fastbuild/bin/python_binary.temp", "__init__.py\u003d", "runfiles/__main__/__init__.py\u003d", "runfiles/__main__/python_binary.py\u003dpython_binary.py", "runfiles/bazel_tools/tools/python/py3wrapper.sh\u003dbazel-out/bazel_tools/k8-fastbuild/bin/tools/python/py3wrapper.sh"],
     "inputDepSetIds": [1]
   }],
-  "depSetOfFiles": [{
-    "id": 1,
-    "directArtifactIds": [4, 3, 5]
-  }],
-  "pathFragments": [{
-    "id": 1,
-    "label": "python_binary"
-  },{
-    "id": 2,
-    "label": "python_binary.zip"
-  },{
-    "id": 3,
-    "label": "python_binary.py"
-  },{
-    "id": 9,
-    "label": ".."
-  }, {
-    "id": 8,
-    "label": "bazel_tools",
-    "parentId": 9
-  }, {
-    "id": 7,
-    "label": "tools",
-    "parentId": 8
-  }, {
-    "id": 6,
-    "label": "zip",
-    "parentId": 7
-  }, {
-    "id": 5,
-    "label": "zipper",
-    "parentId": 6
-  }, {
-    "id": 4,
-    "label": "zipper",
-    "parentId": 5
-  },{
-    "id": 16,
-    "label": "bazel-out"
-  },{
-    "id": 15,
-    "label": "bazel_tools",
-    "parentId": 16
-  }, {
-    "id": 14,
-    "label": "k8-fastbuild",
-    "parentId": 15
-  }, {
-    "id": 13,
-    "label": "bin",
-    "parentId": 14
-  }, {
-    "id": 12,
-    "label": "tools",
-    "parentId": 13
-  }, {
-    "id": 11,
-    "label": "python",
-    "parentId": 12
-  }, {
-    "id": 10,
-    "label": "py3wrapper.sh",
-    "parentId": 11
-  }]
+  "depSetOfFiles": [
+    { "id": 1, "directArtifactIds": [4, 3, 5]}],
+  "pathFragments": [
+    { "id": 1, "label": "python_binary" },
+    { "id": 2, "label": "python_binary.zip" },
+    { "id": 3, "label": "python_binary.py" },
+    { "id": 9, "label": ".." },
+    { "id": 8, "label": "bazel_tools", "parentId": 9 },
+    { "id": 7, "label": "tools", "parentId": 8 },
+    { "id": 6, "label": "zip", "parentId": 7 },
+    { "id": 5, "label": "zipper", "parentId": 6 },
+    { "id": 4, "label": "zipper", "parentId": 5 },
+    { "id": 16, "label": "bazel-out" },
+    { "id": 15, "label": "bazel_tools", "parentId": 16 },
+    { "id": 14, "label": "k8-fastbuild", "parentId": 15 },
+    { "id": 13, "label": "bin", "parentId": 14 },
+    { "id": 12, "label": "tools", "parentId": 13 },
+    { "id": 11, "label": "python", "parentId": 12 },
+    { "id": 10, "label": "py3wrapper.sh", "parentId": 11 }]
 }`
 	_, _, err := AqueryBuildStatements([]byte(inputString))
-	assertError(t, err, `Expect 1+ input and 1 output to python zipper action, got: input ["../bazel_tools/tools/zip/zipper/zipper" "python_binary.py"], output []`)
+	assertError(t, err, `Expect 1+ input and 1 output to python zipper action, got: input ["python_binary.py"], output []`)
 }
 
 func assertError(t *testing.T, err error, expected string) {
@@ -1497,7 +1038,7 @@
 		expectedStatement := expected[i]
 		if differingField := buildStatementEquals(actualStatement, expectedStatement); differingField != "" {
 			t.Errorf("%s differs\nunexpected build statement %#v.\nexpected: %#v",
-				differingField, actualStatement, expected)
+				differingField, actualStatement, expectedStatement)
 			return
 		}
 	}
diff --git a/bp2build/cc_library_headers_conversion_test.go b/bp2build/cc_library_headers_conversion_test.go
index 2b54d45..641984b 100644
--- a/bp2build/cc_library_headers_conversion_test.go
+++ b/bp2build/cc_library_headers_conversion_test.go
@@ -324,3 +324,69 @@
 		},
 	})
 }
+
+func TestCcLibraryHeadersExportedStaticLibHeadersReexported(t *testing.T) {
+	runCcLibraryHeadersTestCase(t, bp2buildTestCase{
+		description:                "cc_library_headers exported_static_lib_headers is reexported",
+		moduleTypeUnderTest:        "cc_library_headers",
+		moduleTypeUnderTestFactory: cc.LibraryHeaderFactory,
+		filesystem:                 map[string]string{},
+		blueprint: soongCcLibraryHeadersPreamble + `
+cc_library_headers {
+		name: "foo_headers",
+		export_static_lib_headers: ["foo_export"],
+		static_libs: ["foo_export", "foo_no_reexport"],
+    bazel_module: { bp2build_available: true },
+}
+` + simpleModuleDoNotConvertBp2build("cc_library_headers", "foo_export"),
+		expectedBazelTargets: []string{
+			makeBazelTarget("cc_library_headers", "foo_headers", attrNameToString{
+				"deps": `[":foo_export"]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibraryHeadersExportedSharedLibHeadersReexported(t *testing.T) {
+	runCcLibraryHeadersTestCase(t, bp2buildTestCase{
+		description:                "cc_library_headers exported_shared_lib_headers is reexported",
+		moduleTypeUnderTest:        "cc_library_headers",
+		moduleTypeUnderTestFactory: cc.LibraryHeaderFactory,
+		filesystem:                 map[string]string{},
+		blueprint: soongCcLibraryHeadersPreamble + `
+cc_library_headers {
+		name: "foo_headers",
+		export_shared_lib_headers: ["foo_export"],
+		shared_libs: ["foo_export", "foo_no_reexport"],
+    bazel_module: { bp2build_available: true },
+}
+` + simpleModuleDoNotConvertBp2build("cc_library_headers", "foo_export"),
+		expectedBazelTargets: []string{
+			makeBazelTarget("cc_library_headers", "foo_headers", attrNameToString{
+				"deps": `[":foo_export"]`,
+			}),
+		},
+	})
+}
+
+func TestCcLibraryHeadersExportedHeaderLibHeadersReexported(t *testing.T) {
+	runCcLibraryHeadersTestCase(t, bp2buildTestCase{
+		description:                "cc_library_headers exported_header_lib_headers is reexported",
+		moduleTypeUnderTest:        "cc_library_headers",
+		moduleTypeUnderTestFactory: cc.LibraryHeaderFactory,
+		filesystem:                 map[string]string{},
+		blueprint: soongCcLibraryHeadersPreamble + `
+cc_library_headers {
+		name: "foo_headers",
+		export_header_lib_headers: ["foo_export"],
+		header_libs: ["foo_export", "foo_no_reexport"],
+    bazel_module: { bp2build_available: true },
+}
+` + simpleModuleDoNotConvertBp2build("cc_library_headers", "foo_export"),
+		expectedBazelTargets: []string{
+			makeBazelTarget("cc_library_headers", "foo_headers", attrNameToString{
+				"deps": `[":foo_export"]`,
+			}),
+		},
+	})
+}
diff --git a/bp2build/cc_library_static_conversion_test.go b/bp2build/cc_library_static_conversion_test.go
index be10e86..36c46a4 100644
--- a/bp2build/cc_library_static_conversion_test.go
+++ b/bp2build/cc_library_static_conversion_test.go
@@ -18,6 +18,7 @@
 	"android/soong/android"
 	"android/soong/cc"
 	"android/soong/genrule"
+	"fmt"
 
 	"testing"
 )
@@ -204,8 +205,8 @@
         ":whole_static_lib_1",
         ":whole_static_lib_2",
     ]`,
-        "sdk_version": `"current"`,
-        "min_sdk_version": `"29"`,
+				"sdk_version":     `"current"`,
+				"min_sdk_version": `"29"`,
 			}),
 		},
 	})
@@ -1489,3 +1490,72 @@
 		},
 	})
 }
+
+func TestCcLibraryStaticStl(t *testing.T) {
+	testCases := []struct {
+		desc string
+		prop string
+		attr attrNameToString
+	}{
+		{
+			desc: "c++_shared deduped to libc++",
+			prop: `stl: "c++_shared",`,
+			attr: attrNameToString{
+				"stl": `"libc++"`,
+			},
+		},
+		{
+			desc: "libc++ to libc++",
+			prop: `stl: "libc++",`,
+			attr: attrNameToString{
+				"stl": `"libc++"`,
+			},
+		},
+		{
+			desc: "c++_static to libc++_static",
+			prop: `stl: "c++_static",`,
+			attr: attrNameToString{
+				"stl": `"libc++_static"`,
+			},
+		},
+		{
+			desc: "libc++_static to libc++_static",
+			prop: `stl: "libc++_static",`,
+			attr: attrNameToString{
+				"stl": `"libc++_static"`,
+			},
+		},
+		{
+			desc: "system to system",
+			prop: `stl: "system",`,
+			attr: attrNameToString{
+				"stl": `"system"`,
+			},
+		},
+		{
+			desc: "none to none",
+			prop: `stl: "none",`,
+			attr: attrNameToString{
+				"stl": `"none"`,
+			},
+		},
+		{
+			desc: "empty to empty",
+			attr: attrNameToString{},
+		},
+	}
+	for _, tc := range testCases {
+		t.Run(tc.desc, func(*testing.T) {
+			runCcLibraryStaticTestCase(t, bp2buildTestCase{
+				blueprint: fmt.Sprintf(`cc_library_static {
+	name: "foo",
+	include_build_directory: false,
+	%s
+}`, tc.prop),
+				expectedBazelTargets: []string{
+					makeBazelTarget("cc_library_static", "foo", tc.attr),
+				},
+			})
+		})
+	}
+}
diff --git a/cc/bp2build.go b/cc/bp2build.go
index d891007..fa30d09 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -376,7 +376,8 @@
 				return
 			}
 			if ca.stl == nil {
-				ca.stl = stlProps.Stl
+				stl := deduplicateStlInput(*stlProps.Stl)
+				ca.stl = &stl
 			} else if ca.stl != stlProps.Stl {
 				ctx.ModuleErrorf("Unsupported conversion: module with different stl for different variants: %s and %s", *ca.stl, stlProps.Stl)
 			}
diff --git a/cc/library_headers.go b/cc/library_headers.go
index 77c2523..970d8d1 100644
--- a/cc/library_headers.go
+++ b/cc/library_headers.go
@@ -125,6 +125,7 @@
 	baseAttributes := bp2BuildParseBaseProps(ctx, module)
 	exportedIncludes := bp2BuildParseExportedIncludes(ctx, module, &baseAttributes.includes)
 	linkerAttrs := baseAttributes.linkerAttributes
+	(&linkerAttrs.deps).Append(linkerAttrs.dynamicDeps)
 
 	attrs := &bazelCcLibraryHeadersAttributes{
 		Export_includes:          exportedIncludes.Includes,
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 92987f7..86472a2 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -430,7 +430,7 @@
 	}
 
 	// Enable Memtag for all components in the include paths (for Aarch64 only)
-	if ctx.Arch().ArchType == android.Arm64 {
+	if ctx.Arch().ArchType == android.Arm64 && ctx.toolchain().Bionic() {
 		if ctx.Config().MemtagHeapSyncEnabledForPath(ctx.ModuleDir()) {
 			if s.Memtag_heap == nil {
 				s.Memtag_heap = proptools.BoolPtr(true)
@@ -460,17 +460,17 @@
 	}
 
 	// HWASan requires AArch64 hardware feature (top-byte-ignore).
-	if ctx.Arch().ArchType != android.Arm64 {
+	if ctx.Arch().ArchType != android.Arm64 || !ctx.toolchain().Bionic() {
 		s.Hwaddress = nil
 	}
 
 	// SCS is only implemented on AArch64.
-	if ctx.Arch().ArchType != android.Arm64 {
+	if ctx.Arch().ArchType != android.Arm64 || !ctx.toolchain().Bionic() {
 		s.Scs = nil
 	}
 
 	// Memtag_heap is only implemented on AArch64.
-	if ctx.Arch().ArchType != android.Arm64 {
+	if ctx.Arch().ArchType != android.Arm64 || !ctx.toolchain().Bionic() {
 		s.Memtag_heap = nil
 	}
 
@@ -710,8 +710,13 @@
 			// Host sanitizers only link symbols in the final executable, so
 			// there will always be undefined symbols in intermediate libraries.
 			_, flags.Global.LdFlags = removeFromList("-Wl,--no-undefined", flags.Global.LdFlags)
+		}
 
-			// non-Bionic toolchain prebuilts are missing UBSan's vptr and function san
+		if !ctx.toolchain().Bionic() {
+			// non-Bionic toolchain prebuilts are missing UBSan's vptr and function san.
+			// Musl toolchain prebuilts have vptr and function sanitizers, but enabling them
+			// implicitly enables RTTI which causes RTTI mismatch issues with dependencies.
+
 			flags.Local.CFlags = append(flags.Local.CFlags, "-fno-sanitize=vptr,function")
 		}
 
diff --git a/cc/stl.go b/cc/stl.go
index 0f2a878..85a06da 100644
--- a/cc/stl.go
+++ b/cc/stl.go
@@ -25,6 +25,16 @@
 	return family
 }
 
+func deduplicateStlInput(stl string) string {
+	switch stl {
+	case "c++_shared":
+		return "libc++"
+	case "c++_static":
+		return "libc++_static"
+	}
+	return stl
+}
+
 func getNdkStlFamilyAndLinkType(m LinkableInterface) (string, string) {
 	stl := m.SelectedStl()
 	switch stl {
@@ -66,18 +76,18 @@
 		} else if ctx.header() {
 			s = "none"
 		}
+		if s == "none" {
+			return ""
+		}
+		s = deduplicateStlInput(s)
 		if ctx.useSdk() && ctx.Device() {
 			switch s {
 			case "", "system":
 				return "ndk_system"
-			case "c++_shared", "c++_static":
-				return "ndk_lib" + s
 			case "libc++":
 				return "ndk_libc++_shared"
 			case "libc++_static":
 				return "ndk_libc++_static"
-			case "none":
-				return ""
 			default:
 				ctx.ModuleErrorf("stl: %q is not a supported STL with sdk_version set", s)
 				return ""
@@ -87,8 +97,6 @@
 			case "libc++", "libc++_static", "":
 				// Only use static libc++ for Windows.
 				return "libc++_static"
-			case "none":
-				return ""
 			default:
 				ctx.ModuleErrorf("stl: %q is not a supported STL for windows", s)
 				return ""
@@ -97,12 +105,6 @@
 			switch s {
 			case "libc++", "libc++_static":
 				return s
-			case "c++_shared":
-				return "libc++"
-			case "c++_static":
-				return "libc++_static"
-			case "none":
-				return ""
 			case "", "system":
 				if ctx.static() {
 					return "libc++_static"
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index a03a86a..16f994d 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -221,6 +221,7 @@
 		}
 		defer build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, files...)
 		defer met.Dump(soongMetricsFile)
+		defer build.CheckProdCreds(buildCtx, config)
 	}
 
 	// Read the time at the starting point.
diff --git a/java/base.go b/java/base.go
index 0900daa..c399c40 100644
--- a/java/base.go
+++ b/java/base.go
@@ -1418,17 +1418,18 @@
 	j.implementationAndResourcesJar = implementationAndResourcesJar
 
 	// Enable dex compilation for the APEX variants, unless it is disabled explicitly
+	compileDex := j.dexProperties.Compile_dex
 	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
 	if j.DirectlyInAnyApex() && !apexInfo.IsForPlatform() {
-		if j.dexProperties.Compile_dex == nil {
-			j.dexProperties.Compile_dex = proptools.BoolPtr(true)
+		if compileDex == nil {
+			compileDex = proptools.BoolPtr(true)
 		}
 		if j.deviceProperties.Hostdex == nil {
 			j.deviceProperties.Hostdex = proptools.BoolPtr(true)
 		}
 	}
 
-	if ctx.Device() && (Bool(j.properties.Installable) || Bool(j.dexProperties.Compile_dex)) {
+	if ctx.Device() && (Bool(j.properties.Installable) || Bool(compileDex)) {
 		if j.hasCode(ctx) {
 			if j.shouldInstrumentStatic(ctx) {
 				j.dexer.extraProguardFlagFiles = append(j.dexer.extraProguardFlagFiles,
@@ -1484,11 +1485,30 @@
 	}
 
 	if ctx.Device() {
-		lintSDKVersion := func(sdkSpec android.SdkSpec) android.ApiLevel {
+		lintSDKVersion := func(sdkSpec android.SdkSpec) int {
 			if v := sdkSpec.ApiLevel; !v.IsPreview() {
-				return v
+				return v.FinalInt()
 			} else {
-				return ctx.Config().DefaultAppTargetSdk(ctx)
+				// When running metalava, we pass --version-codename. When that value
+				// is not REL, metalava will add 1 to the --current-version argument.
+				// On old branches, PLATFORM_SDK_VERSION is the latest version (for that
+				// branch) and the codename is REL, except potentially on the most
+				// recent non-master branch. On that branch, it goes through two other
+				// phases before it gets to the phase previously described:
+				//  - PLATFORM_SDK_VERSION has not been updated yet, and the codename
+				//    is not rel. This happens for most of the internal branch's life
+				//    while the branch has been cut but is still under active development.
+				//  - PLATFORM_SDK_VERSION has been set, but the codename is still not
+				//    REL. This happens briefly during the release process. During this
+				//    state the code to add --current-version is commented out, and then
+				//    that commenting out is reverted after the codename is set to REL.
+				// On the master branch, the PLATFORM_SDK_VERSION always represents a
+				// prior version and the codename is always non-REL.
+				//
+				// We need to add one here to match metalava adding 1. Technically
+				// this means that in the state described in the second bullet point
+				// above, this number is 1 higher than it should be.
+				return ctx.Config().PlatformSdkVersion().FinalInt() + 1
 			}
 		}
 
diff --git a/java/fuzz.go b/java/fuzz.go
index b306991..d0f369f 100644
--- a/java/fuzz.go
+++ b/java/fuzz.go
@@ -171,6 +171,10 @@
 			return
 		}
 
+		if javaFuzzModule.Target().HostCross {
+			return
+		}
+
 		fuzzModuleValidator := fuzz.FuzzModule{
 			javaFuzzModule.ModuleBase,
 			javaFuzzModule.DefaultableModuleBase,
diff --git a/java/lint.go b/java/lint.go
index 22c9ec4..e276345 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"sort"
+	"strconv"
 	"strings"
 
 	"github.com/google/blueprint/proptools"
@@ -75,9 +76,9 @@
 	extraLintCheckJars      android.Paths
 	test                    bool
 	library                 bool
-	minSdkVersion           android.ApiLevel
-	targetSdkVersion        android.ApiLevel
-	compileSdkVersion       android.ApiLevel
+	minSdkVersion           int
+	targetSdkVersion        int
+	compileSdkVersion       int
 	compileSdkKind          android.SdkKind
 	javaLanguageLevel       string
 	kotlinLanguageLevel     string
@@ -299,8 +300,8 @@
 		Text(`echo "<?xml version='1.0' encoding='utf-8'?>" &&`).
 		Text(`echo "<manifest xmlns:android='http://schemas.android.com/apk/res/android'" &&`).
 		Text(`echo "    android:versionCode='1' android:versionName='1' >" &&`).
-		Textf(`echo "  <uses-sdk android:minSdkVersion='%s' android:targetSdkVersion='%s'/>" &&`,
-			l.minSdkVersion.String(), l.targetSdkVersion.String()).
+		Textf(`echo "  <uses-sdk android:minSdkVersion='%d' android:targetSdkVersion='%d'/>" &&`,
+			l.minSdkVersion, l.targetSdkVersion).
 		Text(`echo "</manifest>"`).
 		Text(") >").Output(manifestPath)
 
@@ -325,7 +326,7 @@
 		return
 	}
 
-	if l.minSdkVersion.CompareTo(l.compileSdkVersion) == -1 {
+	if l.minSdkVersion != l.compileSdkVersion {
 		l.extraMainlineLintErrors = append(l.extraMainlineLintErrors, updatabilityChecks...)
 		_, filtered := android.FilterList(l.properties.Lint.Warning_checks, updatabilityChecks)
 		if len(filtered) != 0 {
@@ -427,7 +428,7 @@
 		FlagWithOutput("--html ", html).
 		FlagWithOutput("--text ", text).
 		FlagWithOutput("--xml ", xml).
-		FlagWithArg("--compile-sdk-version ", l.compileSdkVersion.String()).
+		FlagWithArg("--compile-sdk-version ", strconv.Itoa(l.compileSdkVersion)).
 		FlagWithArg("--java-language-level ", l.javaLanguageLevel).
 		FlagWithArg("--kotlin-language-level ", l.kotlinLanguageLevel).
 		FlagWithArg("--url ", fmt.Sprintf(".=.,%s=out", android.PathForOutput(ctx).String())).
diff --git a/rust/bindgen.go b/rust/bindgen.go
index b4626a0..4d723d6 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -41,10 +41,25 @@
 
 	//TODO(b/160803703) Use a prebuilt bindgen instead of the built bindgen.
 	_ = pctx.HostBinToolVariable("bindgenCmd", "bindgen")
+	_ = pctx.VariableFunc("bindgenHostPrebuiltTag", func(ctx android.PackageVarContext) string {
+		if ctx.Config().UseHostMusl() {
+			// This is a hack to use the glibc bindgen binary until we have a musl version checked in.
+			return "linux-x86"
+		} else {
+			return "${config.HostPrebuiltTag}"
+		}
+	})
+	_ = pctx.VariableFunc("bindgenClangLibdir", func(ctx android.PackageVarContext) string {
+		if ctx.Config().UseHostMusl() {
+			return "musl/lib64/"
+		} else {
+			return "lib64/"
+		}
+	})
 	_ = pctx.SourcePathVariable("bindgenClang",
-		"${cc_config.ClangBase}/${config.HostPrebuiltTag}/${bindgenClangVersion}/bin/clang")
+		"${cc_config.ClangBase}/${bindgenHostPrebuiltTag}/${bindgenClangVersion}/bin/clang")
 	_ = pctx.SourcePathVariable("bindgenLibClang",
-		"${cc_config.ClangBase}/${config.HostPrebuiltTag}/${bindgenClangVersion}/lib64/")
+		"${cc_config.ClangBase}/${bindgenHostPrebuiltTag}/${bindgenClangVersion}/${bindgenClangLibdir}")
 
 	//TODO(ivanlozano) Switch this to RuleBuilder
 	bindgen = pctx.AndroidStaticRule("bindgen",
diff --git a/rust/compiler.go b/rust/compiler.go
index bcd58c8..bf6a488 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -371,8 +371,9 @@
 
 	if !Bool(compiler.Properties.No_stdlibs) {
 		for _, stdlib := range config.Stdlibs {
-			// If we're building for the build host, use the prebuilt stdlibs
-			if ctx.Target().Os == android.Linux || ctx.Target().Os == android.Darwin {
+			// If we're building for the build host, use the prebuilt stdlibs, unless the host
+			// is linux_bionic which doesn't have prebuilts.
+			if ctx.Host() && !ctx.Target().HostCross && ctx.Target().Os != android.LinuxBionic {
 				stdlib = "prebuilt_" + stdlib
 			}
 			deps.Stdlibs = append(deps.Stdlibs, stdlib)
diff --git a/rust/config/Android.bp b/rust/config/Android.bp
index 7757c79..ba40cb0 100644
--- a/rust/config/Android.bp
+++ b/rust/config/Android.bp
@@ -11,6 +11,7 @@
     ],
     srcs: [
         "arm_device.go",
+        "arm_linux_host.go",
         "arm64_device.go",
         "global.go",
         "lints.go",
diff --git a/rust/config/arm_linux_host.go b/rust/config/arm_linux_host.go
new file mode 100644
index 0000000..22bdaee
--- /dev/null
+++ b/rust/config/arm_linux_host.go
@@ -0,0 +1,147 @@
+// 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 config
+
+import (
+	"strings"
+
+	"android/soong/android"
+)
+
+var (
+	linuxArmRustflags   = []string{}
+	linuxArmLinkflags   = []string{}
+	linuxArm64Rustflags = []string{}
+	linuxArm64Linkflags = []string{}
+)
+
+func init() {
+	registerToolchainFactory(android.LinuxMusl, android.Arm64, linuxMuslArm64ToolchainFactory)
+	registerToolchainFactory(android.LinuxMusl, android.Arm, linuxMuslArmToolchainFactory)
+
+	pctx.StaticVariable("LinuxToolchainArmRustFlags", strings.Join(linuxArmRustflags, " "))
+	pctx.StaticVariable("LinuxToolchainArmLinkFlags", strings.Join(linuxArmLinkflags, " "))
+	pctx.StaticVariable("LinuxToolchainArm64RustFlags", strings.Join(linuxArm64Rustflags, " "))
+	pctx.StaticVariable("LinuxToolchainArm64LinkFlags", strings.Join(linuxArm64Linkflags, " "))
+}
+
+// Base 64-bit linux rust toolchain
+type toolchainLinuxArm64 struct {
+	toolchain64Bit
+}
+
+func (toolchainLinuxArm64) Supported() bool {
+	return true
+}
+
+func (toolchainLinuxArm64) Bionic() bool {
+	return false
+}
+
+func (t *toolchainLinuxArm64) Name() string {
+	return "arm64"
+}
+
+func (t *toolchainLinuxArm64) ToolchainLinkFlags() string {
+	// Prepend the lld flags from cc_config so we stay in sync with cc
+	return "${cc_config.LinuxLldflags} ${cc_config.LinuxArm64Lldflags} " +
+		"${config.LinuxToolchainLinkFlags} ${config.LinuxToolchainArm64LinkFlags}"
+}
+
+func (t *toolchainLinuxArm64) ToolchainRustFlags() string {
+	return "${config.LinuxToolchainRustFlags} ${config.LinuxToolchainArm64RustFlags}"
+}
+
+// Specialization of the 64-bit linux rust toolchain for musl.  Adds the musl rust triple and
+// linker flags to avoid using the host sysroot.
+type toolchainLinuxMuslArm64 struct {
+	toolchainLinuxArm64
+}
+
+func (t *toolchainLinuxMuslArm64) RustTriple() string {
+	return "aarch64-unknown-linux-musl"
+}
+
+func (t *toolchainLinuxMuslArm64) ToolchainLinkFlags() string {
+	return t.toolchainLinuxArm64.ToolchainLinkFlags() + " " + "${config.LinuxMuslToolchainLinkFlags}"
+}
+
+func (t *toolchainLinuxMuslArm64) ToolchainRustFlags() string {
+	return t.toolchainLinuxArm64.ToolchainRustFlags() + " " + "${config.LinuxMuslToolchainRustFlags}"
+}
+
+func linuxMuslArm64ToolchainFactory(arch android.Arch) Toolchain {
+	return toolchainLinuxMuslArm64Singleton
+}
+
+// Base 32-bit linux rust toolchain
+type toolchainLinuxArm struct {
+	toolchain32Bit
+}
+
+func (toolchainLinuxArm) Supported() bool {
+	return true
+}
+
+func (toolchainLinuxArm) Bionic() bool {
+	return false
+}
+
+func (t *toolchainLinuxArm) Name() string {
+	return "arm"
+}
+
+func (toolchainLinuxArm) LibclangRuntimeLibraryArch() string {
+	return "arm"
+}
+
+func (toolchainLinuxArm64) LibclangRuntimeLibraryArch() string {
+	return "arm64"
+}
+
+func (t *toolchainLinuxArm) ToolchainLinkFlags() string {
+	// Prepend the lld flags from cc_config so we stay in sync with cc
+	return "${cc_config.LinuxLldflags} ${cc_config.LinuxArmLldflags} " +
+		"${config.LinuxToolchainLinkFlags} ${config.LinuxToolchainArmLinkFlags}"
+}
+
+func (t *toolchainLinuxArm) ToolchainRustFlags() string {
+	return "${config.LinuxToolchainRustFlags} ${config.LinuxToolchainArmRustFlags}"
+}
+
+// Specialization of the 32-bit linux rust toolchain for musl.  Adds the musl rust triple and
+// linker flags to avoid using the host sysroot.
+type toolchainLinuxMuslArm struct {
+	toolchainLinuxArm
+}
+
+func (t *toolchainLinuxMuslArm) RustTriple() string {
+	return "arm-unknown-linux-musleabihf"
+}
+
+func (t *toolchainLinuxMuslArm) ToolchainLinkFlags() string {
+	return t.toolchainLinuxArm.ToolchainLinkFlags() + " " + "${config.LinuxMuslToolchainLinkFlags}"
+}
+
+func (t *toolchainLinuxMuslArm) ToolchainRustFlags() string {
+	return t.toolchainLinuxArm.ToolchainRustFlags() + " " + "${config.LinuxMuslToolchainRustFlags}"
+}
+
+func linuxMuslArmToolchainFactory(arch android.Arch) Toolchain {
+	return toolchainLinuxMuslArmSingleton
+}
+
+var toolchainLinuxMuslArm64Singleton Toolchain = &toolchainLinuxMuslArm64{}
+var toolchainLinuxMuslArmSingleton Toolchain = &toolchainLinuxMuslArm{}
diff --git a/rust/config/global.go b/rust/config/global.go
index e9751fd..3ef0ecb 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -78,7 +78,13 @@
 
 func init() {
 	pctx.SourcePathVariable("RustDefaultBase", RustDefaultBase)
-	pctx.VariableConfigMethod("HostPrebuiltTag", android.Config.PrebuiltOS)
+	pctx.VariableConfigMethod("HostPrebuiltTag", func(config android.Config) string {
+		if config.UseHostMusl() {
+			return "linux-musl-x86"
+		} else {
+			return config.PrebuiltOS()
+		}
+	})
 
 	pctx.VariableFunc("RustBase", func(ctx android.PackageVarContext) string {
 		if override := ctx.Config().Getenv("RUST_PREBUILTS_BASE"); override != "" {
diff --git a/rust/sanitize.go b/rust/sanitize.go
index 536fcbd..a3c5cb5 100644
--- a/rust/sanitize.go
+++ b/rust/sanitize.go
@@ -174,7 +174,7 @@
 	}
 
 	// Enable Memtag for all components in the include paths (for Aarch64 only)
-	if ctx.Arch().ArchType == android.Arm64 {
+	if ctx.Arch().ArchType == android.Arm64 && ctx.Os().Bionic() {
 		if ctx.Config().MemtagHeapSyncEnabledForPath(ctx.ModuleDir()) {
 			if s.Memtag_heap == nil {
 				s.Memtag_heap = proptools.BoolPtr(true)
@@ -200,7 +200,7 @@
 	}
 
 	// HWASan requires AArch64 hardware feature (top-byte-ignore).
-	if ctx.Arch().ArchType != android.Arm64 {
+	if ctx.Arch().ArchType != android.Arm64 || !ctx.Os().Bionic() {
 		s.Hwaddress = nil
 	}
 
@@ -215,7 +215,7 @@
 	}
 
 	// Memtag_heap is only implemented on AArch64.
-	if ctx.Arch().ArchType != android.Arm64 {
+	if ctx.Arch().ArchType != android.Arm64 || !ctx.Os().Bionic() {
 		s.Memtag_heap = nil
 	}
 
@@ -234,7 +234,7 @@
 	}
 	if Bool(sanitize.Properties.Sanitize.Fuzzer) {
 		flags.RustFlags = append(flags.RustFlags, fuzzerFlags...)
-		if ctx.Arch().ArchType == android.Arm64 {
+		if ctx.Arch().ArchType == android.Arm64 && ctx.Os().Bionic() {
 			flags.RustFlags = append(flags.RustFlags, hwasanFlags...)
 		} else {
 			flags.RustFlags = append(flags.RustFlags, asanFlags...)
@@ -282,13 +282,13 @@
 		var deps []string
 
 		if mod.IsSanitizerEnabled(cc.Asan) ||
-			(mod.IsSanitizerEnabled(cc.Fuzzer) && mctx.Arch().ArchType != android.Arm64) {
+			(mod.IsSanitizerEnabled(cc.Fuzzer) && (mctx.Arch().ArchType != android.Arm64 || !mctx.Os().Bionic())) {
 			variations = append(variations,
 				blueprint.Variation{Mutator: "link", Variation: "shared"})
 			depTag = cc.SharedDepTag()
 			deps = []string{config.LibclangRuntimeLibrary(mod.toolchain(mctx), "asan")}
 		} else if mod.IsSanitizerEnabled(cc.Hwasan) ||
-			(mod.IsSanitizerEnabled(cc.Fuzzer) && mctx.Arch().ArchType == android.Arm64) {
+			(mod.IsSanitizerEnabled(cc.Fuzzer) && mctx.Arch().ArchType == android.Arm64 && mctx.Os().Bionic()) {
 			// TODO(b/204776996): HWASan for static Rust binaries isn't supported yet.
 			if binary, ok := mod.compiler.(binaryInterface); ok {
 				if binary.staticallyLinked() {
diff --git a/tests/lib.sh b/tests/lib.sh
index 0c78cdf..69ad201 100644
--- a/tests/lib.sh
+++ b/tests/lib.sh
@@ -116,6 +116,7 @@
 
 function create_mock_bazel {
   copy_directory build/bazel
+  copy_directory build/bazel_common_rules
 
   symlink_directory prebuilts/bazel
   symlink_directory prebuilts/clang
diff --git a/ui/build/config.go b/ui/build/config.go
index 59b01b3..8874209 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -1242,10 +1242,40 @@
 	return "", fmt.Errorf("cannot generate a proxy socket address shorter than the limit of %v", maxNameLen)
 }
 
+// IsGooglerEnvironment returns true if the current build is running
+// on a Google developer machine and false otherwise.
+func (c *configImpl) IsGooglerEnvironment() bool {
+	cf := "ANDROID_BUILD_ENVIRONMENT_CONFIG"
+	if v, ok := c.environ.Get(cf); ok {
+		return v == "googler"
+	}
+	return false
+}
+
+// GoogleProdCredsExist determine whether credentials exist on the
+// Googler machine to use remote execution.
+func (c *configImpl) GoogleProdCredsExist() bool {
+	if _, err := exec.Command("/usr/bin/prodcertstatus", "--simple_output", "--nocheck_loas").Output(); err != nil {
+		return false
+	}
+	return true
+}
+
+// UseRemoteBuild indicates whether to use a remote build acceleration system
+// to speed up the build.
 func (c *configImpl) UseRemoteBuild() bool {
 	return c.UseGoma() || c.UseRBE()
 }
 
+// StubbyExists checks whether the stubby binary exists on the machine running
+// the build.
+func (c *configImpl) StubbyExists() bool {
+	if _, err := exec.LookPath("stubby"); err != nil {
+		return false
+	}
+	return true
+}
+
 // RemoteParallel controls how many remote jobs (i.e., commands which contain
 // gomacc) are run in parallel.  Note the parallelism of all other jobs is
 // still limited by Parallel()
diff --git a/ui/build/rbe.go b/ui/build/rbe.go
index 3e558f7..82fc15f 100644
--- a/ui/build/rbe.go
+++ b/ui/build/rbe.go
@@ -19,6 +19,7 @@
 	"os"
 	"path/filepath"
 	"runtime"
+	"strings"
 
 	"android/soong/ui/metrics"
 )
@@ -126,6 +127,34 @@
 	}
 }
 
+func prodCredsAuthType(config Config) bool {
+	authVar, val := config.rbeAuth()
+	if strings.Contains(authVar, "use_google_prod_creds") && val != "" && val != "false" {
+		return true
+	}
+	return false
+}
+
+// Check whether proper auth exists for RBE builds run within a
+// Google dev environment.
+func CheckProdCreds(ctx Context, config Config) {
+	if !config.IsGooglerEnvironment() {
+		return
+	}
+	if !config.StubbyExists() && prodCredsAuthType(config) {
+		fmt.Fprintln(ctx.Writer, "")
+		fmt.Fprintln(ctx.Writer, fmt.Sprintf("\033[33mWARNING: %q binary not found in $PATH, follow go/build-fast#opting-out-of-loas-credentials instead for authenticating with RBE.\033[0m", "stubby"))
+		fmt.Fprintln(ctx.Writer, "")
+		return
+	}
+	if config.GoogleProdCredsExist() {
+		return
+	}
+	fmt.Fprintln(ctx.Writer, "")
+	fmt.Fprintln(ctx.Writer, "\033[33mWARNING: Missing LOAS credentials, please run `gcert`. This will result in failing builds in the future, see go/rbe-android-default-announcement.\033[0m")
+	fmt.Fprintln(ctx.Writer, "")
+}
+
 // DumpRBEMetrics creates a metrics protobuf file containing RBE related metrics.
 // The protobuf file is created if RBE is enabled and the proxy service has
 // started. The proxy service is shutdown in order to dump the RBE metrics to the