Merge "Support sandboxing genrule"
diff --git a/android/Android.bp b/android/Android.bp
index 118087d..94d2c04 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -77,6 +77,7 @@
         "path_properties.go",
         "paths.go",
         "phony.go",
+        "plugin.go",
         "prebuilt.go",
         "prebuilt_build_tool.go",
         "proto.go",
diff --git a/android/androidmk.go b/android/androidmk.go
index aa411d1..62f82f2 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -42,7 +42,7 @@
 }
 
 func RegisterAndroidMkBuildComponents(ctx RegistrationContext) {
-	ctx.RegisterSingletonType("androidmk", AndroidMkSingleton)
+	ctx.RegisterParallelSingletonType("androidmk", AndroidMkSingleton)
 }
 
 // Enable androidmk support.
diff --git a/android/api_levels.go b/android/api_levels.go
index fa919fd..2391e6c 100644
--- a/android/api_levels.go
+++ b/android/api_levels.go
@@ -22,7 +22,7 @@
 )
 
 func init() {
-	RegisterSingletonType("api_levels", ApiLevelsSingleton)
+	RegisterParallelSingletonType("api_levels", ApiLevelsSingleton)
 }
 
 const previewAPILevelBase = 9000
diff --git a/android/bazel.go b/android/bazel.go
index 114b1f5..d326634 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -46,6 +46,10 @@
 	// that is not a platform incompatibility. Example: the module-type is not
 	// enabled, or is not bp2build-converted.
 	ModuleIncompatibility
+
+	// Missing dependencies. We can't query Bazel for modules if it has missing dependencies, there
+	// will be failures.
+	ModuleMissingDeps
 )
 
 // FileGroupAsLibrary describes a filegroup module that is converted to some library
@@ -367,16 +371,26 @@
 // As a side effect, calling this method will also log whether this module is
 // mixed build enabled for metrics reporting.
 func MixedBuildsEnabled(ctx BaseModuleContext) MixedBuildEnabledStatus {
-	module := ctx.Module()
-	apexInfo := ctx.Provider(ApexInfoProvider).(ApexInfo)
-	withinApex := !apexInfo.IsForPlatform()
-
 	platformIncompatible := isPlatformIncompatible(ctx.Os(), ctx.Arch().ArchType)
 	if platformIncompatible {
 		ctx.Config().LogMixedBuild(ctx, false)
 		return TechnicalIncompatibility
 	}
 
+	if ctx.Config().AllowMissingDependencies() {
+		missingDeps := ctx.getMissingDependencies()
+		// If there are missing dependencies, querying Bazel will fail. Soong instead fails at execution
+		// time, not loading/analysis. disable mixed builds and fall back to Soong to maintain that
+		// behavior.
+		if len(missingDeps) > 0 {
+			ctx.Config().LogMixedBuild(ctx, false)
+			return ModuleMissingDeps
+		}
+	}
+
+	module := ctx.Module()
+	apexInfo := ctx.Provider(ApexInfoProvider).(ApexInfo)
+	withinApex := !apexInfo.IsForPlatform()
 	mixedBuildEnabled := ctx.Config().IsMixedBuildsEnabled() &&
 		module.Enabled() &&
 		convertedToBazel(ctx, module) &&
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index 7e81792..c2e5617 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -74,14 +74,12 @@
 	}
 )
 
-func init() {
-	RegisterMixedBuildsMutator(InitRegistrationContext)
+func registerMixedBuildsMutator(ctx RegisterMutatorsContext) {
+	ctx.BottomUp("mixed_builds_prep", mixedBuildsPrepareMutator).Parallel()
 }
 
 func RegisterMixedBuildsMutator(ctx RegistrationContext) {
-	ctx.FinalDepsMutators(func(ctx RegisterMutatorsContext) {
-		ctx.BottomUp("mixed_builds_prep", mixedBuildsPrepareMutator).Parallel()
-	})
+	ctx.FinalDepsMutators(registerMixedBuildsMutator)
 }
 
 func mixedBuildsPrepareMutator(ctx BottomUpMutatorContext) {
diff --git a/android/bazel_test.go b/android/bazel_test.go
index 77e2515..13fd408 100644
--- a/android/bazel_test.go
+++ b/android/bazel_test.go
@@ -436,3 +436,150 @@
 		}
 	}
 }
+
+type mixedBuildModule struct {
+	ModuleBase
+	BazelModuleBase
+	props struct {
+		Deps                     []string
+		Mixed_build_incompatible *bool
+		QueuedBazelCall          bool `blueprint:"mutated"`
+	}
+}
+
+type mixedBuildModuleInfo struct {
+	QueuedBazelCall bool
+}
+
+var mixedBuildModuleProvider = blueprint.NewProvider(mixedBuildModuleInfo{})
+
+func mixedBuildModuleFactory() Module {
+	m := &mixedBuildModule{}
+	m.AddProperties(&m.props)
+	InitAndroidArchModule(m, HostAndDeviceDefault, MultilibBoth)
+	InitBazelModule(m)
+
+	return m
+}
+
+func (m *mixedBuildModule) ConvertWithBp2build(ctx TopDownMutatorContext) {
+}
+
+func (m *mixedBuildModule) DepsMutator(ctx BottomUpMutatorContext) {
+	ctx.AddDependency(ctx.Module(), installDepTag{}, m.props.Deps...)
+}
+
+func (m *mixedBuildModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+}
+
+func (m *mixedBuildModule) IsMixedBuildSupported(ctx BaseModuleContext) bool {
+	return !proptools.Bool(m.props.Mixed_build_incompatible)
+}
+
+func (m *mixedBuildModule) QueueBazelCall(ctx BaseModuleContext) {
+	m.props.QueuedBazelCall = true
+}
+
+func (m *mixedBuildModule) ProcessBazelQueryResponse(ctx ModuleContext) {
+	ctx.SetProvider(mixedBuildModuleProvider, mixedBuildModuleInfo{
+		QueuedBazelCall: m.props.QueuedBazelCall,
+	})
+}
+
+var prepareForMixedBuildTests = FixtureRegisterWithContext(func(ctx RegistrationContext) {
+	ctx.RegisterModuleType("deps", mixedBuildModuleFactory)
+	RegisterMixedBuildsMutator(ctx)
+})
+
+func TestMixedBuildsEnabledForType(t *testing.T) {
+	baseBp := `
+	deps {
+		name: "foo",
+		deps: ["bar"],
+		target: { windows: { enabled: true } },
+		%s
+	}
+`
+	depBp := `
+	deps {
+		name: "bar",
+		target: {
+			windows: {
+				enabled: true,
+			},
+		},
+	}
+`
+	testCases := []struct {
+		desc               string
+		variant            *string
+		missingDeps        bool
+		extraBpInfo        string
+		mixedBuildsEnabled bool
+	}{
+		{
+			desc:               "mixed builds works",
+			mixedBuildsEnabled: true,
+			extraBpInfo:        `bazel_module: { bp2build_available: true },`,
+		},
+		{
+			desc:               "missing deps",
+			missingDeps:        true,
+			mixedBuildsEnabled: false,
+			extraBpInfo:        `bazel_module: { bp2build_available: true },`,
+		},
+		{
+			desc:               "windows no mixed builds",
+			mixedBuildsEnabled: false,
+			variant:            proptools.StringPtr("windows_x86"),
+			extraBpInfo:        `bazel_module: { bp2build_available: true },`,
+		},
+		{
+			desc:               "mixed builds disabled by type",
+			mixedBuildsEnabled: false,
+			extraBpInfo: `mixed_build_incompatible: true,
+		bazel_module: { bp2build_available: true },`,
+		},
+		{
+			desc:               "mixed builds not bp2build available",
+			mixedBuildsEnabled: false,
+			extraBpInfo:        `bazel_module: { bp2build_available: false },`,
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.desc, func(t *testing.T) {
+			handlers := GroupFixturePreparers(
+				prepareForMixedBuildTests,
+				PrepareForTestWithArchMutator,
+				FixtureModifyConfig(func(config Config) {
+					config.BazelContext = MockBazelContext{
+						OutputBaseDir: "base",
+					}
+					config.Targets[Windows] = []Target{
+						{Windows, Arch{ArchType: X86_64}, NativeBridgeDisabled, "", "", true},
+						{Windows, Arch{ArchType: X86}, NativeBridgeDisabled, "", "", true},
+					}
+				}),
+			)
+			bp := fmt.Sprintf(baseBp, tc.extraBpInfo)
+			if tc.missingDeps {
+				handlers = GroupFixturePreparers(
+					handlers,
+					PrepareForTestWithAllowMissingDependencies,
+				)
+			} else {
+				bp += depBp
+			}
+			result := handlers.RunTestWithBp(t, bp)
+
+			variant := proptools.StringDefault(tc.variant, "android_arm64_armv8-a")
+
+			m := result.ModuleForTests("foo", variant)
+			mixedBuildModuleInfo := result.TestContext.ModuleProvider(m.Module(), mixedBuildModuleProvider).(mixedBuildModuleInfo)
+			if w, g := tc.mixedBuildsEnabled, mixedBuildModuleInfo.QueuedBazelCall; w != g {
+				t.Errorf("Expected mixed builds enabled %t, got mixed builds enabled %t", w, g)
+			}
+		})
+	}
+}
diff --git a/android/buildinfo_prop.go b/android/buildinfo_prop.go
index 46f6488..8e19ad5 100644
--- a/android/buildinfo_prop.go
+++ b/android/buildinfo_prop.go
@@ -23,7 +23,7 @@
 
 func init() {
 	ctx := InitRegistrationContext
-	ctx.RegisterSingletonModuleType("buildinfo_prop", buildinfoPropFactory)
+	ctx.RegisterParallelSingletonModuleType("buildinfo_prop", buildinfoPropFactory)
 }
 
 type buildinfoPropProperties struct {
diff --git a/android/config.go b/android/config.go
index 58ad4c5..df174bf 100644
--- a/android/config.go
+++ b/android/config.go
@@ -302,6 +302,9 @@
 	// modules that aren't mixed-built for at least one variant will cause a build
 	// failure
 	ensureAllowlistIntegrity bool
+
+	// List of Api libraries that contribute to Api surfaces.
+	apiLibraries map[string]struct{}
 }
 
 type deviceConfig struct {
@@ -622,6 +625,33 @@
 	config.BazelContext, err = NewBazelContext(config)
 	config.Bp2buildPackageConfig = GetBp2BuildAllowList()
 
+	// TODO(b/276958307): Replace the hardcoded list to a sdk_library local prop.
+	config.apiLibraries = map[string]struct{}{
+		"android.net.ipsec.ike":             {},
+		"art.module.public.api":             {},
+		"conscrypt.module.public.api":       {},
+		"framework-adservices":              {},
+		"framework-appsearch":               {},
+		"framework-bluetooth":               {},
+		"framework-connectivity":            {},
+		"framework-connectivity-t":          {},
+		"framework-graphics":                {},
+		"framework-media":                   {},
+		"framework-mediaprovider":           {},
+		"framework-ondevicepersonalization": {},
+		"framework-permission":              {},
+		"framework-permission-s":            {},
+		"framework-scheduling":              {},
+		"framework-sdkextensions":           {},
+		"framework-statsd":                  {},
+		"framework-sdksandbox":              {},
+		"framework-tethering":               {},
+		"framework-uwb":                     {},
+		"framework-virtualization":          {},
+		"framework-wifi":                    {},
+		"i18n.module.public.api":            {},
+	}
+
 	return Config{config}, err
 }
 
@@ -1850,6 +1880,10 @@
 	return uncheckedFinalApiLevel(apiLevel)
 }
 
+func (c *deviceConfig) BuildBrokenPluginValidation() []string {
+	return c.config.productVariables.BuildBrokenPluginValidation
+}
+
 func (c *deviceConfig) BuildBrokenClangAsFlags() bool {
 	return c.config.productVariables.BuildBrokenClangAsFlags
 }
@@ -1979,8 +2013,20 @@
 func (c *config) SetBuildFromTextStub(b bool) {
 	c.buildFromTextStub = b
 }
+
 func (c *config) AddForceEnabledModules(forceEnabled []string) {
 	for _, forceEnabledModule := range forceEnabled {
 		c.bazelForceEnabledModules[forceEnabledModule] = struct{}{}
 	}
 }
+
+func (c *config) SetApiLibraries(libs []string) {
+	c.apiLibraries = make(map[string]struct{})
+	for _, lib := range libs {
+		c.apiLibraries[lib] = struct{}{}
+	}
+}
+
+func (c *config) GetApiLibraries() map[string]struct{} {
+	return c.apiLibraries
+}
diff --git a/android/gen_notice.go b/android/gen_notice.go
index 091345b..1acc638 100644
--- a/android/gen_notice.go
+++ b/android/gen_notice.go
@@ -28,7 +28,7 @@
 
 // Register the gen_notice module type.
 func RegisterGenNoticeBuildComponents(ctx RegistrationContext) {
-	ctx.RegisterSingletonType("gen_notice_build_rules", GenNoticeBuildRulesFactory)
+	ctx.RegisterParallelSingletonType("gen_notice_build_rules", GenNoticeBuildRulesFactory)
 	ctx.RegisterModuleType("gen_notice", GenNoticeFactory)
 }
 
diff --git a/android/metrics.go b/android/metrics.go
index 3d41a1d..63c72cd 100644
--- a/android/metrics.go
+++ b/android/metrics.go
@@ -42,7 +42,7 @@
 }
 
 func init() {
-	RegisterSingletonType("soong_metrics", soongMetricsSingletonFactory)
+	RegisterParallelSingletonType("soong_metrics", soongMetricsSingletonFactory)
 }
 
 func soongMetricsSingletonFactory() Singleton { return soongMetricsSingleton{} }
diff --git a/android/module.go b/android/module.go
index db602a0..604ba24 100644
--- a/android/module.go
+++ b/android/module.go
@@ -354,6 +354,10 @@
 
 	AddMissingDependencies(missingDeps []string)
 
+	// getMissingDependencies returns the list of missing dependencies.
+	// Calling this function prevents adding new dependencies.
+	getMissingDependencies() []string
+
 	// AddUnconvertedBp2buildDep stores module name of a direct dependency that was not converted via bp2build
 	AddUnconvertedBp2buildDep(dep string)
 
@@ -939,7 +943,8 @@
 
 	NamespaceExportedToMake bool `blueprint:"mutated"`
 
-	MissingDeps []string `blueprint:"mutated"`
+	MissingDeps        []string `blueprint:"mutated"`
+	CheckedMissingDeps bool     `blueprint:"mutated"`
 
 	// Name and variant strings stored by mutators to enable Module.String()
 	DebugName       string   `blueprint:"mutated"`
@@ -2862,6 +2867,20 @@
 	}
 }
 
+func (b *baseModuleContext) checkedMissingDeps() bool {
+	return b.Module().base().commonProperties.CheckedMissingDeps
+}
+
+func (b *baseModuleContext) getMissingDependencies() []string {
+	checked := &b.Module().base().commonProperties.CheckedMissingDeps
+	*checked = true
+	var missingDeps []string
+	missingDeps = append(missingDeps, b.Module().base().commonProperties.MissingDeps...)
+	missingDeps = append(missingDeps, b.bp.EarlyGetMissingDependencies()...)
+	missingDeps = FirstUniqueStrings(missingDeps)
+	return missingDeps
+}
+
 type AllowDisabledModuleDependency interface {
 	blueprint.DependencyTag
 	AllowDisabledModuleDependency(target Module) bool
@@ -3724,7 +3743,7 @@
 }
 
 func init() {
-	RegisterSingletonType("buildtarget", BuildTargetSingleton)
+	RegisterParallelSingletonType("buildtarget", BuildTargetSingleton)
 }
 
 func BuildTargetSingleton() Singleton {
diff --git a/android/mutator.go b/android/mutator.go
index 0a091eb..4185315 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -67,6 +67,8 @@
 // collateGloballyRegisteredMutators constructs the list of mutators that have been registered
 // with the InitRegistrationContext and will be used at runtime.
 func collateGloballyRegisteredMutators() sortableComponents {
+	// ensure mixed builds mutator is the last mutator
+	finalDeps = append(finalDeps, registerMixedBuildsMutator)
 	return collateRegisteredMutators(preArch, preDeps, postDeps, finalDeps)
 }
 
@@ -885,10 +887,16 @@
 }
 
 func (b *bottomUpMutatorContext) AddDependency(module blueprint.Module, tag blueprint.DependencyTag, name ...string) []blueprint.Module {
+	if b.baseModuleContext.checkedMissingDeps() {
+		panic("Adding deps not allowed after checking for missing deps")
+	}
 	return b.bp.AddDependency(module, tag, name...)
 }
 
 func (b *bottomUpMutatorContext) AddReverseDependency(module blueprint.Module, tag blueprint.DependencyTag, name string) {
+	if b.baseModuleContext.checkedMissingDeps() {
+		panic("Adding deps not allowed after checking for missing deps")
+	}
 	b.bp.AddReverseDependency(module, tag, name)
 }
 
@@ -938,11 +946,17 @@
 
 func (b *bottomUpMutatorContext) AddVariationDependencies(variations []blueprint.Variation, tag blueprint.DependencyTag,
 	names ...string) []blueprint.Module {
+	if b.baseModuleContext.checkedMissingDeps() {
+		panic("Adding deps not allowed after checking for missing deps")
+	}
 	return b.bp.AddVariationDependencies(variations, tag, names...)
 }
 
 func (b *bottomUpMutatorContext) AddFarVariationDependencies(variations []blueprint.Variation,
 	tag blueprint.DependencyTag, names ...string) []blueprint.Module {
+	if b.baseModuleContext.checkedMissingDeps() {
+		panic("Adding deps not allowed after checking for missing deps")
+	}
 
 	return b.bp.AddFarVariationDependencies(variations, tag, names...)
 }
@@ -952,10 +966,16 @@
 }
 
 func (b *bottomUpMutatorContext) ReplaceDependencies(name string) {
+	if b.baseModuleContext.checkedMissingDeps() {
+		panic("Adding deps not allowed after checking for missing deps")
+	}
 	b.bp.ReplaceDependencies(name)
 }
 
 func (b *bottomUpMutatorContext) ReplaceDependenciesIf(name string, predicate blueprint.ReplaceDependencyPredicate) {
+	if b.baseModuleContext.checkedMissingDeps() {
+		panic("Adding deps not allowed after checking for missing deps")
+	}
 	b.bp.ReplaceDependenciesIf(name, predicate)
 }
 
diff --git a/android/plugin.go b/android/plugin.go
new file mode 100644
index 0000000..c9d1338
--- /dev/null
+++ b/android/plugin.go
@@ -0,0 +1,140 @@
+// Copyright 2022 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package android
+
+import (
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"strings"
+
+	"github.com/google/blueprint"
+)
+
+func init() {
+	RegisterPluginSingletonBuildComponents(InitRegistrationContext)
+}
+
+func RegisterPluginSingletonBuildComponents(ctx RegistrationContext) {
+	ctx.RegisterSingletonType("plugins", pluginSingletonFactory)
+}
+
+// pluginSingleton is a singleton to handle allowlisting of the final Android-<product_name>.mk file
+// output.
+func pluginSingletonFactory() Singleton {
+	return &pluginSingleton{}
+}
+
+type pluginSingleton struct{}
+
+var allowedPluginsByName = map[string]bool{
+	"aidl-soong-rules":                       true,
+	"arm_compute_library_nn_driver":          true,
+	"cuttlefish-soong-rules":                 true,
+	"gki-soong-rules":                        true,
+	"hidl-soong-rules":                       true,
+	"kernel-config-soong-rules":              true,
+	"soong-angle-codegen":                    true,
+	"soong-api":                              true,
+	"soong-art":                              true,
+	"soong-ca-certificates":                  true,
+	"soong-ca-certificates-apex":             true,
+	"soong-clang":                            true,
+	"soong-clang-prebuilts":                  true,
+	"soong-csuite":                           true,
+	"soong-fluoride":                         true,
+	"soong-fs_config":                        true,
+	"soong-icu":                              true,
+	"soong-java-config-error_prone":          true,
+	"soong-libchrome":                        true,
+	"soong-llvm":                             true,
+	"soong-robolectric":                      true,
+	"soong-rust-prebuilts":                   true,
+	"soong-selinux":                          true,
+	"soong-wayland-protocol-codegen":         true,
+	"treble_report_app":                      true,
+	"treble_report_local":                    true,
+	"treble_report_module":                   true,
+	"vintf-compatibility-matrix-soong-rules": true,
+	"xsdc-soong-rules":                       true,
+}
+
+const (
+	internalPluginsPath = "vendor/google/build/soong/internal_plugins.json"
+)
+
+type pluginProvider interface {
+	IsPluginFor(string) bool
+}
+
+func maybeAddInternalPluginsToAllowlist(ctx SingletonContext) {
+	if path := ExistentPathForSource(ctx, internalPluginsPath); path.Valid() {
+		ctx.AddNinjaFileDeps(path.String())
+		absPath := absolutePath(path.String())
+		var moreAllowed map[string]bool
+		data, err := ioutil.ReadFile(absPath)
+		if err != nil {
+			ctx.Errorf("Failed to open internal plugins path %q %q", internalPluginsPath, err)
+		}
+		if err := json.Unmarshal(data, &moreAllowed); err != nil {
+			fmt.Fprintf(os.Stderr, "Internal plugins file %q did not parse correctly: %q", data, err)
+		}
+		for k, v := range moreAllowed {
+			allowedPluginsByName[k] = v
+		}
+	}
+}
+
+func (p *pluginSingleton) GenerateBuildActions(ctx SingletonContext) {
+	for _, p := range ctx.DeviceConfig().BuildBrokenPluginValidation() {
+		allowedPluginsByName[p] = true
+	}
+	maybeAddInternalPluginsToAllowlist(ctx)
+
+	disallowedPlugins := map[string]bool{}
+	ctx.VisitAllModulesBlueprint(func(module blueprint.Module) {
+		if ctx.ModuleType(module) != "bootstrap_go_package" {
+			return
+		}
+
+		p, ok := module.(pluginProvider)
+		if !ok || !p.IsPluginFor("soong_build") {
+			return
+		}
+
+		name := ctx.ModuleName(module)
+		if _, ok := allowedPluginsByName[name]; ok {
+			return
+		}
+
+		dir := ctx.ModuleDir(module)
+
+		// allow use of plugins within Soong to not allowlist everything
+		if strings.HasPrefix(dir, "build/soong") {
+			return
+		}
+
+		// allow third party users outside of external to create new plugins, i.e. non-google paths
+		// under vendor or hardware
+		if !strings.HasPrefix(dir, "external/") && IsThirdPartyPath(dir) {
+			return
+		}
+		disallowedPlugins[name] = true
+	})
+	if len(disallowedPlugins) > 0 {
+		ctx.Errorf("New plugins are not supported; however %q were found. Please reach out to the build team or use BUILD_BROKEN_PLUGIN_VALIDATION (see Changes.md for more info).", SortedStringKeys(disallowedPlugins))
+	}
+}
diff --git a/android/register.go b/android/register.go
index 1a3db9d..64b0207 100644
--- a/android/register.go
+++ b/android/register.go
@@ -65,16 +65,19 @@
 	// True if this should be registered as a pre-singleton, false otherwise.
 	pre bool
 
+	// True if this should be registered as a parallel singleton.
+	parallel bool
+
 	name    string
 	factory SingletonFactory
 }
 
-func newSingleton(name string, factory SingletonFactory) singleton {
-	return singleton{false, name, factory}
+func newSingleton(name string, factory SingletonFactory, parallel bool) singleton {
+	return singleton{pre: false, parallel: parallel, name: name, factory: factory}
 }
 
 func newPreSingleton(name string, factory SingletonFactory) singleton {
-	return singleton{true, name, factory}
+	return singleton{pre: true, parallel: false, name: name, factory: factory}
 }
 
 func (s singleton) componentName() string {
@@ -86,7 +89,7 @@
 	if s.pre {
 		ctx.RegisterPreSingletonType(s.name, adaptor)
 	} else {
-		ctx.RegisterSingletonType(s.name, adaptor)
+		ctx.RegisterSingletonType(s.name, adaptor, s.parallel)
 	}
 }
 
@@ -145,8 +148,16 @@
 	moduleTypeByFactory[factory] = name
 }
 
+func registerSingletonType(name string, factory SingletonFactory, parallel bool) {
+	singletons = append(singletons, newSingleton(name, factory, parallel))
+}
+
 func RegisterSingletonType(name string, factory SingletonFactory) {
-	singletons = append(singletons, newSingleton(name, factory))
+	registerSingletonType(name, factory, false)
+}
+
+func RegisterParallelSingletonType(name string, factory SingletonFactory) {
+	registerSingletonType(name, factory, true)
 }
 
 func RegisterPreSingletonType(name string, factory SingletonFactory) {
@@ -220,17 +231,17 @@
 func collateGloballyRegisteredSingletons() sortableComponents {
 	allSingletons := append(sortableComponents(nil), singletons...)
 	allSingletons = append(allSingletons,
-		singleton{false, "bazeldeps", BazelSingleton},
+		singleton{pre: false, parallel: true, name: "bazeldeps", factory: BazelSingleton},
 
 		// Register phony just before makevars so it can write out its phony rules as Make rules
-		singleton{false, "phony", phonySingletonFactory},
+		singleton{pre: false, parallel: false, name: "phony", factory: phonySingletonFactory},
 
 		// Register makevars after other singletons so they can export values through makevars
-		singleton{false, "makevars", makeVarsSingletonFunc},
+		singleton{pre: false, parallel: false, name: "makevars", factory: makeVarsSingletonFunc},
 
 		// Register env and ninjadeps last so that they can track all used environment variables and
 		// Ninja file dependencies stored in the config.
-		singleton{false, "ninjadeps", ninjaDepsSingletonFactory},
+		singleton{pre: false, parallel: false, name: "ninjadeps", factory: ninjaDepsSingletonFactory},
 	)
 
 	return allSingletons
@@ -259,7 +270,9 @@
 type RegistrationContext interface {
 	RegisterModuleType(name string, factory ModuleFactory)
 	RegisterSingletonModuleType(name string, factory SingletonModuleFactory)
+	RegisterParallelSingletonModuleType(name string, factory SingletonModuleFactory)
 	RegisterPreSingletonType(name string, factory SingletonFactory)
+	RegisterParallelSingletonType(name string, factory SingletonFactory)
 	RegisterSingletonType(name string, factory SingletonFactory)
 	PreArchMutators(f RegisterMutatorFunc)
 
@@ -315,8 +328,15 @@
 }
 
 func (ctx *initRegistrationContext) RegisterSingletonModuleType(name string, factory SingletonModuleFactory) {
+	ctx.registerSingletonModuleType(name, factory, false)
+}
+func (ctx *initRegistrationContext) RegisterParallelSingletonModuleType(name string, factory SingletonModuleFactory) {
+	ctx.registerSingletonModuleType(name, factory, true)
+}
+
+func (ctx *initRegistrationContext) registerSingletonModuleType(name string, factory SingletonModuleFactory, parallel bool) {
 	s, m := SingletonModuleFactoryAdaptor(name, factory)
-	ctx.RegisterSingletonType(name, s)
+	ctx.registerSingletonType(name, s, parallel)
 	ctx.RegisterModuleType(name, m)
 	// Overwrite moduleTypesForDocs with the original factory instead of the lambda returned by
 	// SingletonModuleFactoryAdaptor so that docs can find the module type documentation on the
@@ -324,12 +344,20 @@
 	RegisterModuleTypeForDocs(name, reflect.ValueOf(factory))
 }
 
-func (ctx *initRegistrationContext) RegisterSingletonType(name string, factory SingletonFactory) {
+func (ctx *initRegistrationContext) registerSingletonType(name string, factory SingletonFactory, parallel bool) {
 	if _, present := ctx.singletonTypes[name]; present {
 		panic(fmt.Sprintf("singleton type %q is already registered", name))
 	}
 	ctx.singletonTypes[name] = factory
-	RegisterSingletonType(name, factory)
+	registerSingletonType(name, factory, parallel)
+}
+
+func (ctx *initRegistrationContext) RegisterSingletonType(name string, factory SingletonFactory) {
+	ctx.registerSingletonType(name, factory, false)
+}
+
+func (ctx *initRegistrationContext) RegisterParallelSingletonType(name string, factory SingletonFactory) {
+	ctx.registerSingletonType(name, factory, true)
 }
 
 func (ctx *initRegistrationContext) RegisterPreSingletonType(name string, factory SingletonFactory) {
diff --git a/android/test_suites.go b/android/test_suites.go
index b570b23..b48d71a 100644
--- a/android/test_suites.go
+++ b/android/test_suites.go
@@ -15,7 +15,7 @@
 package android
 
 func init() {
-	RegisterSingletonType("testsuites", testSuiteFilesFactory)
+	RegisterParallelSingletonType("testsuites", testSuiteFilesFactory)
 }
 
 func testSuiteFilesFactory() Singleton {
diff --git a/android/testing.go b/android/testing.go
index 2a9c658..5ad7ad0 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -495,8 +495,18 @@
 	ctx.RegisterModuleType(name, m)
 }
 
+func (ctx *TestContext) RegisterParallelSingletonModuleType(name string, factory SingletonModuleFactory) {
+	s, m := SingletonModuleFactoryAdaptor(name, factory)
+	ctx.RegisterParallelSingletonType(name, s)
+	ctx.RegisterModuleType(name, m)
+}
+
 func (ctx *TestContext) RegisterSingletonType(name string, factory SingletonFactory) {
-	ctx.singletons = append(ctx.singletons, newSingleton(name, factory))
+	ctx.singletons = append(ctx.singletons, newSingleton(name, factory, false))
+}
+
+func (ctx *TestContext) RegisterParallelSingletonType(name string, factory SingletonFactory) {
+	ctx.singletons = append(ctx.singletons, newSingleton(name, factory, true))
 }
 
 func (ctx *TestContext) RegisterPreSingletonType(name string, factory SingletonFactory) {
diff --git a/android/variable.go b/android/variable.go
index a6822c6..3832fcf 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -440,6 +440,7 @@
 
 	ShippingApiLevel *string `json:",omitempty"`
 
+	BuildBrokenPluginValidation        []string `json:",omitempty"`
 	BuildBrokenClangAsFlags            bool     `json:",omitempty"`
 	BuildBrokenClangCFlags             bool     `json:",omitempty"`
 	BuildBrokenClangProperty           bool     `json:",omitempty"`
diff --git a/android_sdk/sdk_repo_host.go b/android_sdk/sdk_repo_host.go
index 61058df..9623a8b 100644
--- a/android_sdk/sdk_repo_host.go
+++ b/android_sdk/sdk_repo_host.go
@@ -242,7 +242,7 @@
 			fmt.Fprintln(w, ".PHONY:", name, "sdk_repo", "sdk-repo-"+name)
 			fmt.Fprintln(w, "sdk_repo", "sdk-repo-"+name+":", strings.Join(s.FilesToInstall().Strings(), " "))
 
-			fmt.Fprintf(w, "$(call dist-for-goals,sdk_repo sdk-repo-%s,%s:%s-$(FILE_NAME_TAG).zip)\n\n", s.BaseModuleName(), s.outputFile.String(), s.outputBaseName)
+			fmt.Fprintf(w, "$(call dist-for-goals,sdk_repo sdk-repo-%s,%s:%s-FILE_NAME_TAG_PLACEHOLDER.zip)\n\n", s.BaseModuleName(), s.outputFile.String(), s.outputBaseName)
 		},
 	}
 }
diff --git a/apex/apex_singleton.go b/apex/apex_singleton.go
index ebc35cf..a63344f 100644
--- a/apex/apex_singleton.go
+++ b/apex/apex_singleton.go
@@ -27,7 +27,7 @@
 }
 
 func registerApexDepsInfoComponents(ctx android.RegistrationContext) {
-	ctx.RegisterSingletonType("apex_depsinfo_singleton", apexDepsInfoSingletonFactory)
+	ctx.RegisterParallelSingletonType("apex_depsinfo_singleton", apexDepsInfoSingletonFactory)
 }
 
 type apexDepsInfoSingleton struct {
diff --git a/apex/key.go b/apex/key.go
index 0a7e80f..3010d76 100644
--- a/apex/key.go
+++ b/apex/key.go
@@ -33,7 +33,7 @@
 
 func registerApexKeyBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("apex_key", ApexKeyFactory)
-	ctx.RegisterSingletonType("apex_keys_text", apexKeysTextFactory)
+	ctx.RegisterParallelSingletonType("apex_keys_text", apexKeysTextFactory)
 }
 
 type apexKey struct {
diff --git a/bloaty/bloaty.go b/bloaty/bloaty.go
index 50f241f..3cff60f 100644
--- a/bloaty/bloaty.go
+++ b/bloaty/bloaty.go
@@ -51,7 +51,7 @@
 	pctx.VariableConfigMethod("hostPrebuiltTag", android.Config.PrebuiltOS)
 	pctx.SourcePathVariable("bloaty", "prebuilts/build-tools/${hostPrebuiltTag}/bin/bloaty")
 	pctx.HostBinToolVariable("bloatyMerger", "bloaty_merger")
-	android.RegisterSingletonType("file_metrics", fileSizesSingleton)
+	android.RegisterParallelSingletonType("file_metrics", fileSizesSingleton)
 	fileSizeMeasurerKey = blueprint.NewProvider(measuredFiles{})
 }
 
diff --git a/cc/cc.go b/cc/cc.go
index f4b5655..0e7f6c8 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -83,7 +83,7 @@
 		ctx.TopDown("sabi_deps", sabiDepsMutator)
 	})
 
-	ctx.RegisterSingletonType("kythe_extract_all", kytheExtractAllFactory)
+	ctx.RegisterParallelSingletonType("kythe_extract_all", kytheExtractAllFactory)
 }
 
 // Deps is a struct containing module names of dependencies, separated by the kind of dependency.
diff --git a/cc/ccdeps.go b/cc/ccdeps.go
index 75e1faf..d30abba 100644
--- a/cc/ccdeps.go
+++ b/cc/ccdeps.go
@@ -30,7 +30,7 @@
 // The info file is generated in $OUT/module_bp_cc_depend.json.
 
 func init() {
-	android.RegisterSingletonType("ccdeps_generator", ccDepsGeneratorSingleton)
+	android.RegisterParallelSingletonType("ccdeps_generator", ccDepsGeneratorSingleton)
 }
 
 func ccDepsGeneratorSingleton() android.Singleton {
diff --git a/cc/cmakelists.go b/cc/cmakelists.go
index ad130ba..0f3f02d 100644
--- a/cc/cmakelists.go
+++ b/cc/cmakelists.go
@@ -29,7 +29,7 @@
 // structure (see variable CLionOutputProjectsDirectory for root).
 
 func init() {
-	android.RegisterSingletonType("cmakelists_generator", cMakeListsGeneratorSingleton)
+	android.RegisterParallelSingletonType("cmakelists_generator", cMakeListsGeneratorSingleton)
 }
 
 func cMakeListsGeneratorSingleton() android.Singleton {
diff --git a/cc/compdb.go b/cc/compdb.go
index ea12443..617be1a 100644
--- a/cc/compdb.go
+++ b/cc/compdb.go
@@ -32,7 +32,7 @@
 // make SOONG_GEN_COMPDB=1 nothing to get all targets.
 
 func init() {
-	android.RegisterSingletonType("compdb_generator", compDBGeneratorSingleton)
+	android.RegisterParallelSingletonType("compdb_generator", compDBGeneratorSingleton)
 }
 
 func compDBGeneratorSingleton() android.Singleton {
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 0f709c9..c897501 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -28,7 +28,7 @@
 
 func init() {
 	android.RegisterModuleType("cc_fuzz", LibFuzzFactory)
-	android.RegisterSingletonType("cc_fuzz_packaging", fuzzPackagingFactory)
+	android.RegisterParallelSingletonType("cc_fuzz_packaging", fuzzPackagingFactory)
 }
 
 type FuzzProperties struct {
diff --git a/cc/ndk_abi.go b/cc/ndk_abi.go
index 3456c32..86166dc 100644
--- a/cc/ndk_abi.go
+++ b/cc/ndk_abi.go
@@ -19,8 +19,8 @@
 )
 
 func init() {
-	android.RegisterSingletonType("ndk_abi_dump", NdkAbiDumpSingleton)
-	android.RegisterSingletonType("ndk_abi_diff", NdkAbiDiffSingleton)
+	android.RegisterParallelSingletonType("ndk_abi_dump", NdkAbiDumpSingleton)
+	android.RegisterParallelSingletonType("ndk_abi_diff", NdkAbiDiffSingleton)
 }
 
 func getNdkAbiDumpInstallBase(ctx android.PathContext) android.OutputPath {
diff --git a/cc/ndk_sysroot.go b/cc/ndk_sysroot.go
index dffc6c6..0cf21b6 100644
--- a/cc/ndk_sysroot.go
+++ b/cc/ndk_sysroot.go
@@ -66,7 +66,7 @@
 	ctx.RegisterModuleType("ndk_library", NdkLibraryFactory)
 	ctx.RegisterModuleType("versioned_ndk_headers", versionedNdkHeadersFactory)
 	ctx.RegisterModuleType("preprocessed_ndk_headers", preprocessedNdkHeadersFactory)
-	ctx.RegisterSingletonType("ndk", NdkSingleton)
+	ctx.RegisterParallelSingletonType("ndk", NdkSingleton)
 }
 
 func getNdkInstallBase(ctx android.PathContext) android.InstallPath {
diff --git a/cc/stub_library.go b/cc/stub_library.go
index f324dcc..3a6d0ae 100644
--- a/cc/stub_library.go
+++ b/cc/stub_library.go
@@ -23,7 +23,7 @@
 
 func init() {
 	// Use singleton type to gather all generated soong modules.
-	android.RegisterSingletonType("stublibraries", stubLibrariesSingleton)
+	android.RegisterParallelSingletonType("stublibraries", stubLibrariesSingleton)
 }
 
 type stubLibraries struct {
diff --git a/cc/tidy.go b/cc/tidy.go
index bbcaece..7b123cb 100644
--- a/cc/tidy.go
+++ b/cc/tidy.go
@@ -201,7 +201,7 @@
 }
 
 func init() {
-	android.RegisterSingletonType("tidy_phony_targets", TidyPhonySingleton)
+	android.RegisterParallelSingletonType("tidy_phony_targets", TidyPhonySingleton)
 }
 
 // This TidyPhonySingleton generates both tidy-* and obj-* phony targets for C/C++ files.
diff --git a/cc/vndk.go b/cc/vndk.go
index 9b70004..7a2286e 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -417,16 +417,16 @@
 
 func init() {
 	RegisterVndkLibraryTxtTypes(android.InitRegistrationContext)
-	android.RegisterSingletonType("vndk-snapshot", VndkSnapshotSingleton)
+	android.RegisterParallelSingletonType("vndk-snapshot", VndkSnapshotSingleton)
 }
 
 func RegisterVndkLibraryTxtTypes(ctx android.RegistrationContext) {
-	ctx.RegisterSingletonModuleType("llndk_libraries_txt", llndkLibrariesTxtFactory)
-	ctx.RegisterSingletonModuleType("vndksp_libraries_txt", vndkSPLibrariesTxtFactory)
-	ctx.RegisterSingletonModuleType("vndkcore_libraries_txt", vndkCoreLibrariesTxtFactory)
-	ctx.RegisterSingletonModuleType("vndkprivate_libraries_txt", vndkPrivateLibrariesTxtFactory)
-	ctx.RegisterSingletonModuleType("vndkproduct_libraries_txt", vndkProductLibrariesTxtFactory)
-	ctx.RegisterSingletonModuleType("vndkcorevariant_libraries_txt", vndkUsingCoreVariantLibrariesTxtFactory)
+	ctx.RegisterParallelSingletonModuleType("llndk_libraries_txt", llndkLibrariesTxtFactory)
+	ctx.RegisterParallelSingletonModuleType("vndksp_libraries_txt", vndkSPLibrariesTxtFactory)
+	ctx.RegisterParallelSingletonModuleType("vndkcore_libraries_txt", vndkCoreLibrariesTxtFactory)
+	ctx.RegisterParallelSingletonModuleType("vndkprivate_libraries_txt", vndkPrivateLibrariesTxtFactory)
+	ctx.RegisterParallelSingletonModuleType("vndkproduct_libraries_txt", vndkProductLibrariesTxtFactory)
+	ctx.RegisterParallelSingletonModuleType("vndkcorevariant_libraries_txt", vndkUsingCoreVariantLibrariesTxtFactory)
 }
 
 type vndkLibrariesTxt struct {
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index 0cc3bd6..e61ebe6 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -197,7 +197,7 @@
 
 func init() {
 	pctx.Import("android/soong/android")
-	android.RegisterSingletonType("dexpreopt-soong-config", func() android.Singleton {
+	android.RegisterParallelSingletonType("dexpreopt-soong-config", func() android.Singleton {
 		return &globalSoongConfigSingleton{}
 	})
 }
diff --git a/java/app.go b/java/app.go
index e095092..366005c 100755
--- a/java/app.go
+++ b/java/app.go
@@ -977,6 +977,10 @@
 // For OutputFileProducer interface
 func (a *AndroidApp) OutputFiles(tag string) (android.Paths, error) {
 	switch tag {
+	// In some instances, it can be useful to reference the aapt-generated flags from another
+	// target, e.g., system server implements services declared in the framework-res manifest.
+	case ".aapt.proguardOptionsFile":
+		return []android.Path{a.proguardOptionsFile}, nil
 	case ".aapt.srcjar":
 		return []android.Path{a.aaptSrcJar}, nil
 	case ".export-package.apk":
diff --git a/java/bootclasspath_fragment_test.go b/java/bootclasspath_fragment_test.go
index 2541f14..9bdef74 100644
--- a/java/bootclasspath_fragment_test.go
+++ b/java/bootclasspath_fragment_test.go
@@ -432,3 +432,39 @@
 	fragment = result.Module("a_test_fragment", "android_common").(*BootclasspathFragmentModule)
 	android.AssertBoolEquals(t, "is a test fragment by type", true, fragment.isTestFragment())
 }
+
+func TestBootclassFragment_LinkTextStub(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForJavaTest,
+		prepareForTestWithBootclasspathFragment,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithLastReleaseApis("mysdklibrary"),
+		android.FixtureModifyConfig(func(config android.Config) {
+			config.SetBuildFromTextStub(true)
+		}),
+	).RunTestWithBp(t, `
+        bootclasspath_fragment {
+            name: "myfragment",
+            contents: ["mysdklibrary"],
+            hidden_api: {split_packages: ["*"]},
+            additional_stubs: [
+                "android-non-updatable",
+            ],
+        }
+        java_sdk_library {
+            name: "mysdklibrary",
+            srcs: ["a.java"],
+            shared_library: false,
+            public: {enabled: true},
+            system: {enabled: true},
+        }
+    `)
+
+	fragment := result.ModuleForTests("myfragment", "android_common")
+	ruleCommand := fragment.Rule("modularHiddenAPIStubFlagsFile").RuleParams.Command
+	android.AssertStringDoesContain(t, "Command expected to contain library as dependency stub dex",
+		ruleCommand, "--dependency-stub-dex=out/soong/.intermediates/default/java/android-non-updatable.stubs.module_lib.from-text/android_common/dex/android-non-updatable.stubs.module_lib.from-text.jar")
+	android.AssertStringDoesNotContain(t,
+		"Command not expected to contain multiple api_library as dependency stub dex", ruleCommand,
+		"--dependency-stub-dex=out/soong/.intermediates/default/java/android-non-updatable.stubs.from-text/android_common/dex/android-non-updatable.stubs.from-text.jar")
+}
diff --git a/java/config/config.go b/java/config/config.go
index b82a137..195dae1 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -98,7 +98,7 @@
 		"-JDcom.android.tools.r8.emitRecordAnnotationsExInDex",
 	}, dexerJavaVmFlagsList...))
 	exportedVars.ExportStringListStaticVariable("R8Flags", append([]string{
-		"-JXmx2048M",
+		"-JXmx4096M",
 		"-JDcom.android.tools.r8.emitRecordAnnotationsInDex",
 		"-JDcom.android.tools.r8.emitPermittedSubclassesAnnotationsInDex",
 		"-JDcom.android.tools.r8.emitRecordAnnotationsExInDex",
diff --git a/java/core-libraries/TxtStubLibraries.bp b/java/core-libraries/TxtStubLibraries.bp
index 813187e..0cf0f36 100644
--- a/java/core-libraries/TxtStubLibraries.bp
+++ b/java/core-libraries/TxtStubLibraries.bp
@@ -22,8 +22,6 @@
     libs: [
         "core-current-stubs-for-system-modules-no-annotations.from-text",
     ],
-    // TODO: Enable after stub generation from .txt file is available
-    enabled: false,
 }
 
 java_library {
@@ -36,8 +34,6 @@
         "core.current.stubs.from-text",
         "core-lambda-stubs.from-text",
     ],
-    // TODO: Enable after stub generation from .txt file is available
-    enabled: false,
 }
 
 // Same as core-module-lib-stubs-system-modules, but the stubs are generated from .txt files
@@ -47,8 +43,6 @@
     libs: [
         "core-module-lib-stubs-for-system-modules-no-annotations.from-text",
     ],
-    // TODO: Enable after stub generation from .txt file is available
-    enabled: false,
 }
 
 java_library {
@@ -61,8 +55,6 @@
         "core.module_lib.stubs.from-text",
         "core-lambda-stubs.from-text",
     ],
-    // TODO: Enable after stub generation from .txt file is available
-    enabled: false,
 }
 
 java_library {
@@ -79,8 +71,6 @@
     sdk_version: "none",
     system_modules: "none",
     visibility: ["//visibility:private"],
-    // TODO: Enable after stub generation from .txt file is available
-    enabled: false,
 }
 
 // Same as legacy-core-platform-api-stubs-system-modules, but the stubs are generated from .txt files
@@ -91,8 +81,6 @@
         "legacy.core.platform.api.no.annotations.stubs.from-text",
         "core-lambda-stubs.from-text",
     ],
-    // TODO: Enable after stub generation from .txt file is available
-    enabled: false,
 }
 
 java_library {
@@ -108,8 +96,6 @@
         "legacy.core.platform.api.stubs.from-text",
     ],
     patch_module: "java.base",
-    // TODO: Enable after stub generation from .txt file is available
-    enabled: false,
 }
 
 // Same as stable-core-platform-api-stubs-system-modules, but the stubs are generated from .txt files
@@ -120,8 +106,6 @@
         "stable.core.platform.api.no.annotations.stubs.from-text",
         "core-lambda-stubs.from-text",
     ],
-    // TODO: Enable after stub generation from .txt file is available
-    enabled: false,
 }
 
 java_library {
@@ -137,8 +121,6 @@
         "stable.core.platform.api.stubs.from-text",
     ],
     patch_module: "java.base",
-    // TODO: Enable after stub generation from .txt file is available
-    enabled: false,
 }
 
 java_api_library {
@@ -151,6 +133,4 @@
         // LambdaMetaFactory depends on CallSite etc. which is part of the Core API surface
         "core.current.stubs.from-text",
     ],
-    // TODO: Enable after stub generation from .txt file is available
-    enabled: false,
 }
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index f477f40..116c833 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -465,7 +465,7 @@
 }
 
 func RegisterDexpreoptBootJarsComponents(ctx android.RegistrationContext) {
-	ctx.RegisterSingletonModuleType("dex_bootjars", dexpreoptBootJarsFactory)
+	ctx.RegisterParallelSingletonModuleType("dex_bootjars", dexpreoptBootJarsFactory)
 }
 
 func SkipDexpreoptBootJars(ctx android.PathContext) bool {
diff --git a/java/dexpreopt_check.go b/java/dexpreopt_check.go
index 83c088c..7499481 100644
--- a/java/dexpreopt_check.go
+++ b/java/dexpreopt_check.go
@@ -28,7 +28,7 @@
 }
 
 func RegisterDexpreoptCheckBuildComponents(ctx android.RegistrationContext) {
-	ctx.RegisterSingletonModuleType("dexpreopt_systemserver_check", dexpreoptSystemserverCheckFactory)
+	ctx.RegisterParallelSingletonModuleType("dexpreopt_systemserver_check", dexpreoptSystemserverCheckFactory)
 }
 
 // A build-time check to verify if all compilation artifacts of system server jars are installed
diff --git a/java/fuzz.go b/java/fuzz.go
index e24594a..5c5f769 100644
--- a/java/fuzz.go
+++ b/java/fuzz.go
@@ -38,7 +38,7 @@
 
 func RegisterJavaFuzzBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("java_fuzz", JavaFuzzFactory)
-	ctx.RegisterSingletonType("java_fuzz_packaging", javaFuzzPackagingFactory)
+	ctx.RegisterParallelSingletonType("java_fuzz_packaging", javaFuzzPackagingFactory)
 }
 
 type JavaFuzzTest struct {
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index 96e084a..e54275b 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -647,7 +647,7 @@
 	// public version is provided by the art.module.public.api module. In those cases it is necessary
 	// to treat all those modules as they were the same name, otherwise it will result in multiple
 	// definitions of a single class being passed to hidden API processing which will cause an error.
-	if name == scope.nonUpdatablePrebuiltModule || name == scope.nonUpdatableSourceModule {
+	if name == scope.nonUpdatablePrebuiltModule || name == android.JavaApiLibraryName(ctx.Config(), scope.nonUpdatableSourceModule) {
 		// Treat all *android-non-updatable* modules as if they were part of an android-non-updatable
 		// java_sdk_library.
 		// TODO(b/192067200): Remove once android-non-updatable is a java_sdk_library or equivalent.
diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
index 52934a3..d4ee4fc 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -25,7 +25,7 @@
 }
 
 func RegisterHiddenApiSingletonComponents(ctx android.RegistrationContext) {
-	ctx.RegisterSingletonType("hiddenapi", hiddenAPISingletonFactory)
+	ctx.RegisterParallelSingletonType("hiddenapi", hiddenAPISingletonFactory)
 }
 
 var PrepareForTestWithHiddenApiBuildComponents = android.FixtureRegisterWithContext(RegisterHiddenApiSingletonComponents)
diff --git a/java/java.go b/java/java.go
index bb8fe62..33846be 100644
--- a/java/java.go
+++ b/java/java.go
@@ -73,8 +73,8 @@
 		ctx.BottomUp("jacoco_deps", jacocoDepsMutator).Parallel()
 	})
 
-	ctx.RegisterSingletonType("logtags", LogtagsSingleton)
-	ctx.RegisterSingletonType("kythe_java_extract", kytheExtractJavaFactory)
+	ctx.RegisterParallelSingletonType("logtags", LogtagsSingleton)
+	ctx.RegisterParallelSingletonType("kythe_java_extract", kytheExtractJavaFactory)
 }
 
 func RegisterJavaSdkMemberTypes() {
diff --git a/java/jdeps.go b/java/jdeps.go
index a52b867..4c8c11c 100644
--- a/java/jdeps.go
+++ b/java/jdeps.go
@@ -26,7 +26,7 @@
 // called. Dependency info file is generated in $OUT/module_bp_java_depend.json.
 
 func init() {
-	android.RegisterSingletonType("jdeps_generator", jDepsGeneratorSingleton)
+	android.RegisterParallelSingletonType("jdeps_generator", jDepsGeneratorSingleton)
 }
 
 func jDepsGeneratorSingleton() android.Singleton {
diff --git a/java/lint.go b/java/lint.go
index 40ef484..a0f9970 100644
--- a/java/lint.go
+++ b/java/lint.go
@@ -705,7 +705,7 @@
 var _ android.SingletonMakeVarsProvider = (*lintSingleton)(nil)
 
 func init() {
-	android.RegisterSingletonType("lint",
+	android.RegisterParallelSingletonType("lint",
 		func() android.Singleton { return &lintSingleton{} })
 
 	registerLintBuildComponents(android.InitRegistrationContext)
diff --git a/java/platform_bootclasspath.go b/java/platform_bootclasspath.go
index 07fb92c..0d4db7c 100644
--- a/java/platform_bootclasspath.go
+++ b/java/platform_bootclasspath.go
@@ -26,7 +26,7 @@
 }
 
 func registerPlatformBootclasspathBuildComponents(ctx android.RegistrationContext) {
-	ctx.RegisterSingletonModuleType("platform_bootclasspath", platformBootclasspathFactory)
+	ctx.RegisterParallelSingletonModuleType("platform_bootclasspath", platformBootclasspathFactory)
 }
 
 // The tags used for the dependencies between the platform bootclasspath and any configured boot
diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go
index d417291..2197304 100644
--- a/java/platform_compat_config.go
+++ b/java/platform_compat_config.go
@@ -36,7 +36,7 @@
 }
 
 func registerPlatformCompatConfigBuildComponents(ctx android.RegistrationContext) {
-	ctx.RegisterSingletonType("platform_compat_config_singleton", platformCompatConfigSingletonFactory)
+	ctx.RegisterParallelSingletonType("platform_compat_config_singleton", platformCompatConfigSingletonFactory)
 	ctx.RegisterModuleType("platform_compat_config", PlatformCompatConfigFactory)
 	ctx.RegisterModuleType("prebuilt_platform_compat_config", prebuiltCompatConfigFactory)
 	ctx.RegisterModuleType("global_compat_config", globalCompatConfigFactory)
diff --git a/java/sdk.go b/java/sdk.go
index 8b4918a..7fa604f 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -28,7 +28,7 @@
 
 func init() {
 	android.RegisterPreSingletonType("sdk_versions", sdkPreSingletonFactory)
-	android.RegisterSingletonType("sdk", sdkSingletonFactory)
+	android.RegisterParallelSingletonType("sdk", sdkSingletonFactory)
 	android.RegisterMakeVarsProvider(pctx, sdkMakeVars)
 }
 
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 103f1ac..89da19a 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -156,6 +156,9 @@
 
 	// Whether the api scope can be treated as unstable, and should skip compat checks.
 	unstable bool
+
+	// Represents the SDK kind of this scope.
+	kind android.SdkKind
 }
 
 // Initialize a scope, creating and adding appropriate dependency tags
@@ -229,6 +232,10 @@
 	return ".stubs" + scope.moduleSuffix
 }
 
+func (scope *apiScope) apiLibraryModuleName(baseName string) string {
+	return scope.stubsLibraryModuleName(baseName) + ".from-text"
+}
+
 func (scope *apiScope) stubsLibraryModuleName(baseName string) string {
 	return baseName + scope.stubsLibraryModuleNameSuffix()
 }
@@ -289,6 +296,7 @@
 			return &module.sdkLibraryProperties.Public
 		},
 		sdkVersion: "current",
+		kind:       android.SdkPublic,
 	})
 	apiScopeSystem = initApiScope(&apiScope{
 		name:                "system",
@@ -301,6 +309,7 @@
 		moduleSuffix:  ".system",
 		sdkVersion:    "system_current",
 		annotation:    "android.annotation.SystemApi(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS)",
+		kind:          android.SdkSystem,
 	})
 	apiScopeTest = initApiScope(&apiScope{
 		name:                "test",
@@ -314,6 +323,7 @@
 		sdkVersion:    "test_current",
 		annotation:    "android.annotation.TestApi",
 		unstable:      true,
+		kind:          android.SdkTest,
 	})
 	apiScopeModuleLib = initApiScope(&apiScope{
 		name:    "module-lib",
@@ -331,6 +341,7 @@
 		moduleSuffix:  ".module_lib",
 		sdkVersion:    "module_current",
 		annotation:    "android.annotation.SystemApi(client=android.annotation.SystemApi.Client.MODULE_LIBRARIES)",
+		kind:          android.SdkModule,
 	})
 	apiScopeSystemServer = initApiScope(&apiScope{
 		name:    "system-server",
@@ -361,6 +372,7 @@
 			// com.android.* classes are okay in this interface"
 			"--hide", "InternalClasses",
 		},
+		kind: android.SdkSystemServer,
 	})
 	allApiScopes = apiScopes{
 		apiScopePublic,
@@ -842,6 +854,13 @@
 	return c.namingScheme.stubsSourceModuleName(apiScope, baseName)
 }
 
+// Name of the java_api_library module that generates the from-text stubs source
+// and compiles to a jar file.
+func (c *commonToSdkLibraryAndImport) apiLibraryModuleName(apiScope *apiScope) string {
+	baseName := c.module.BaseModuleName()
+	return c.namingScheme.apiLibraryModuleName(apiScope, baseName)
+}
+
 // The component names for different outputs of the java_sdk_library.
 //
 // They are similar to the names used for the child modules it creates
@@ -1269,7 +1288,9 @@
 		// Add dependencies to the stubs library
 		stubModuleName := module.stubsLibraryModuleName(apiScope)
 		// Use JavaApiLibraryName function to be redirected to stubs generated from .txt if applicable
-		stubModuleName = android.JavaApiLibraryName(ctx.Config(), stubModuleName)
+		if module.contributesToApiSurface(ctx.Config()) {
+			stubModuleName = android.JavaApiLibraryName(ctx.Config(), stubModuleName)
+		}
 		ctx.AddVariationDependencies(nil, apiScope.stubsTag, stubModuleName)
 
 		// Add a dependency on the stubs source in order to access both stubs source and api information.
@@ -1477,6 +1498,11 @@
 	return latestPrebuiltApiModuleName(module.distStem()+"-incompatibilities", apiScope)
 }
 
+func (module *SdkLibrary) contributesToApiSurface(c android.Config) bool {
+	_, exists := c.GetApiLibraries()[module.Name()]
+	return exists
+}
+
 func childModuleVisibility(childVisibility []string) []string {
 	if childVisibility == nil {
 		// No child visibility set. The child will use the visibility of the sdk_library.
@@ -1758,6 +1784,46 @@
 	mctx.CreateModule(DroidstubsFactory, &props).(*Droidstubs).CallHookIfAvailable(mctx)
 }
 
+func (module *SdkLibrary) createApiLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) {
+	props := struct {
+		Name              *string
+		Visibility        []string
+		Api_contributions []string
+		Libs              []string
+		Static_libs       []string
+		Dep_api_srcs      *string
+	}{}
+
+	props.Name = proptools.StringPtr(module.apiLibraryModuleName(apiScope))
+	props.Visibility = childModuleVisibility(module.sdkLibraryProperties.Stubs_library_visibility)
+
+	apiContributions := []string{}
+
+	// Api surfaces are not independent of each other, but have subset relationships,
+	// and so does the api files. To generate from-text stubs for api surfaces other than public,
+	// all subset api domains' api_contriubtions must be added as well.
+	scope := apiScope
+	for scope != nil {
+		apiContributions = append(apiContributions, module.stubsSourceModuleName(scope)+".api.contribution")
+		scope = scope.extends
+	}
+
+	props.Api_contributions = apiContributions
+	props.Libs = module.properties.Libs
+	props.Libs = append(props.Libs, module.sdkLibraryProperties.Stub_only_libs...)
+	props.Libs = append(props.Libs, "stub-annotations")
+	props.Static_libs = module.sdkLibraryProperties.Stub_only_static_libs
+	props.Dep_api_srcs = proptools.StringPtr(apiScope.kind.DefaultJavaLibraryName() + ".from-text")
+
+	// android_module_lib_stubs_current.from-text only comprises api contributions from art, conscrypt and i18n.
+	// Thus, replace with android_module_lib_stubs_current_full.from-text, which comprises every api domains.
+	if apiScope.kind == android.SdkModule {
+		props.Dep_api_srcs = proptools.StringPtr(apiScope.kind.DefaultJavaLibraryName() + "_full.from-text")
+	}
+
+	mctx.CreateModule(ApiLibraryFactory, &props)
+}
+
 func (module *SdkLibrary) compareAgainstLatestApi(apiScope *apiScope) bool {
 	return !(apiScope.unstable || module.sdkLibraryProperties.Unsafe_ignore_missing_latest_api)
 }
@@ -1954,6 +2020,10 @@
 		module.createStubsSourcesAndApi(mctx, scope, module.stubsSourceModuleName(scope), scope.droidstubsArgs)
 
 		module.createStubsLibrary(mctx, scope)
+
+		if module.contributesToApiSurface(mctx.Config()) {
+			module.createApiLibrary(mctx, scope)
+		}
 	}
 
 	if module.requiresRuntimeImplementationLibrary() {
@@ -2006,6 +2076,8 @@
 	stubsLibraryModuleName(scope *apiScope, baseName string) string
 
 	stubsSourceModuleName(scope *apiScope, baseName string) string
+
+	apiLibraryModuleName(scope *apiScope, baseName string) string
 }
 
 type defaultNamingScheme struct {
@@ -2019,6 +2091,10 @@
 	return scope.stubsSourceModuleName(baseName)
 }
 
+func (s *defaultNamingScheme) apiLibraryModuleName(scope *apiScope, baseName string) string {
+	return scope.apiLibraryModuleName(baseName)
+}
+
 var _ sdkLibraryComponentNamingScheme = (*defaultNamingScheme)(nil)
 
 func moduleStubLinkType(name string) (stub bool, ret sdkLinkType) {
diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go
index 1d0c13d..141e16b 100644
--- a/java/sdk_library_test.go
+++ b/java/sdk_library_test.go
@@ -35,6 +35,9 @@
 			"29": {"foo"},
 			"30": {"bar", "barney", "baz", "betty", "foo", "fred", "quuz", "wilma"},
 		}),
+		android.FixtureModifyConfig(func(config android.Config) {
+			config.SetApiLibraries([]string{"foo"})
+		}),
 	).RunTestWithBp(t, `
 		droiddoc_exported_dir {
 			name: "droiddoc-templates-sdk",
@@ -121,6 +124,7 @@
 	result.ModuleForTests(apiScopeSystem.stubsSourceModuleName("foo"), "android_common")
 	result.ModuleForTests(apiScopeTest.stubsSourceModuleName("foo"), "android_common")
 	result.ModuleForTests(apiScopePublic.stubsSourceModuleName("foo")+".api.contribution", "")
+	result.ModuleForTests(apiScopePublic.apiLibraryModuleName("foo"), "android_common")
 	result.ModuleForTests("foo"+sdkXmlFileSuffix, "android_common")
 	result.ModuleForTests("foo.api.public.28", "")
 	result.ModuleForTests("foo.api.system.28", "")
@@ -1412,3 +1416,62 @@
 	fooStubsSources := result.ModuleForTests("foo.stubs.source", "android_common").Module().(*Droidstubs)
 	android.AssertStringListContains(t, "foo stubs should depend on bar-lib", fooStubsSources.Javadoc.properties.Libs, "bar-lib")
 }
+
+func TestJavaSdkLibrary_ApiLibrary(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForJavaTest,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		FixtureWithLastReleaseApis("foo"),
+		android.FixtureModifyConfig(func(config android.Config) {
+			config.SetApiLibraries([]string{"foo"})
+		}),
+	).RunTestWithBp(t, `
+		java_sdk_library {
+			name: "foo",
+			srcs: ["a.java", "b.java"],
+			api_packages: ["foo"],
+			system: {
+				enabled: true,
+			},
+			module_lib: {
+				enabled: true,
+			},
+			test: {
+				enabled: true,
+			},
+		}
+	`)
+
+	testCases := []struct {
+		scope            *apiScope
+		apiContributions []string
+		depApiSrcs       string
+	}{
+		{
+			scope:            apiScopePublic,
+			apiContributions: []string{"foo.stubs.source.api.contribution"},
+			depApiSrcs:       "android_stubs_current.from-text",
+		},
+		{
+			scope:            apiScopeSystem,
+			apiContributions: []string{"foo.stubs.source.system.api.contribution", "foo.stubs.source.api.contribution"},
+			depApiSrcs:       "android_system_stubs_current.from-text",
+		},
+		{
+			scope:            apiScopeTest,
+			apiContributions: []string{"foo.stubs.source.test.api.contribution", "foo.stubs.source.system.api.contribution", "foo.stubs.source.api.contribution"},
+			depApiSrcs:       "android_test_stubs_current.from-text",
+		},
+		{
+			scope:            apiScopeModuleLib,
+			apiContributions: []string{"foo.stubs.source.module_lib.api.contribution", "foo.stubs.source.system.api.contribution", "foo.stubs.source.api.contribution"},
+			depApiSrcs:       "android_module_lib_stubs_current_full.from-text",
+		},
+	}
+
+	for _, c := range testCases {
+		m := result.ModuleForTests(c.scope.apiLibraryModuleName("foo"), "android_common").Module().(*ApiLibrary)
+		android.AssertArrayString(t, "Module expected to contain api contributions", c.apiContributions, m.properties.Api_contributions)
+		android.AssertStringEquals(t, "Module expected to contain full api surface api library", c.depApiSrcs, *m.properties.Dep_api_srcs)
+	}
+}
diff --git a/java/testing.go b/java/testing.go
index ffc3a08..3a238d7 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -400,16 +400,20 @@
 	}
 
 	extraApiLibraryModules := map[string]string{
-		"android_stubs_current.from-text":                 "api/current.txt",
-		"android_system_stubs_current.from-text":          "api/system-current.txt",
-		"android_test_stubs_current.from-text":            "api/test-current.txt",
-		"android_module_lib_stubs_current.from-text":      "api/module-lib-current.txt",
-		"android_module_lib_stubs_current_full.from-text": "api/module-lib-current.txt",
-		"android_system_server_stubs_current.from-text":   "api/system-server-current.txt",
-		"core.current.stubs.from-text":                    "api/current.txt",
-		"legacy.core.platform.api.stubs.from-text":        "api/current.txt",
-		"stable.core.platform.api.stubs.from-text":        "api/current.txt",
-		"core-lambda-stubs.from-text":                     "api/current.txt",
+		"android_stubs_current.from-text":                  "api/current.txt",
+		"android_system_stubs_current.from-text":           "api/system-current.txt",
+		"android_test_stubs_current.from-text":             "api/test-current.txt",
+		"android_module_lib_stubs_current.from-text":       "api/module-lib-current.txt",
+		"android_module_lib_stubs_current_full.from-text":  "api/module-lib-current.txt",
+		"android_system_server_stubs_current.from-text":    "api/system-server-current.txt",
+		"core.current.stubs.from-text":                     "api/current.txt",
+		"legacy.core.platform.api.stubs.from-text":         "api/current.txt",
+		"stable.core.platform.api.stubs.from-text":         "api/current.txt",
+		"core-lambda-stubs.from-text":                      "api/current.txt",
+		"android-non-updatable.stubs.from-text":            "api/current.txt",
+		"android-non-updatable.stubs.system.from-text":     "api/system-current.txt",
+		"android-non-updatable.stubs.test.from-text":       "api/test-current.txt",
+		"android-non-updatable.stubs.module_lib.from-text": "api/module-lib-current.txt",
 	}
 
 	for libName, apiFile := range extraApiLibraryModules {
diff --git a/multitree/metadata.go b/multitree/metadata.go
index 3fd7215..0eb0efc 100644
--- a/multitree/metadata.go
+++ b/multitree/metadata.go
@@ -20,7 +20,7 @@
 )
 
 func init() {
-	android.RegisterSingletonType("update-meta", UpdateMetaSingleton)
+	android.RegisterParallelSingletonType("update-meta", UpdateMetaSingleton)
 }
 
 func UpdateMetaSingleton() android.Singleton {
diff --git a/provenance/provenance_singleton.go b/provenance/provenance_singleton.go
index fbb6212..5d27c0c 100644
--- a/provenance/provenance_singleton.go
+++ b/provenance/provenance_singleton.go
@@ -51,7 +51,7 @@
 }
 
 func RegisterProvenanceSingleton(ctx android.RegistrationContext) {
-	ctx.RegisterSingletonType("provenance_metadata_singleton", provenanceInfoSingletonFactory)
+	ctx.RegisterParallelSingletonType("provenance_metadata_singleton", provenanceInfoSingletonFactory)
 }
 
 var PrepareForTestWithProvenanceSingleton = android.FixtureRegisterWithContext(RegisterProvenanceSingleton)
diff --git a/python/test.go b/python/test.go
index 7de7af2..6e23a44 100644
--- a/python/test.go
+++ b/python/test.go
@@ -66,6 +66,10 @@
 
 	// Test options.
 	Test_options TestOptions
+
+	// list of device binary modules that should be installed alongside the test
+	// This property adds 64bit AND 32bit variants of the dependency
+	Data_device_bins_both []string `android:"arch_variant"`
 }
 
 type TestOptions struct {
@@ -98,12 +102,48 @@
 	android.InitAndroidArchModule(p, p.hod, p.multilib)
 	android.InitDefaultableModule(p)
 	android.InitBazelModule(p)
-	if p.hod == android.HostSupported && p.testProperties.Test_options.Unit_test == nil {
+	if p.isTestHost() && p.testProperties.Test_options.Unit_test == nil {
 		p.testProperties.Test_options.Unit_test = proptools.BoolPtr(true)
 	}
 	return p
 }
 
+func (p *PythonTestModule) isTestHost() bool {
+	return p.hod == android.HostSupported
+}
+
+var dataDeviceBinsTag = dependencyTag{name: "dataDeviceBins"}
+
+// python_test_host DepsMutator uses this method to add multilib dependencies of
+// data_device_bin_both
+func (p *PythonTestModule) addDataDeviceBinsDeps(ctx android.BottomUpMutatorContext, filter string) {
+	if len(p.testProperties.Data_device_bins_both) < 1 {
+		return
+	}
+
+	var maybeAndroidTarget *android.Target
+	androidTargetList := android.FirstTarget(ctx.Config().Targets[android.Android], filter)
+	if len(androidTargetList) > 0 {
+		maybeAndroidTarget = &androidTargetList[0]
+	}
+
+	if maybeAndroidTarget != nil {
+		ctx.AddFarVariationDependencies(
+			maybeAndroidTarget.Variations(),
+			dataDeviceBinsTag,
+			p.testProperties.Data_device_bins_both...,
+		)
+	}
+}
+
+func (p *PythonTestModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+	p.PythonBinaryModule.DepsMutator(ctx)
+	if p.isTestHost() {
+		p.addDataDeviceBinsDeps(ctx, "lib32")
+		p.addDataDeviceBinsDeps(ctx, "lib64")
+	}
+}
+
 func (p *PythonTestModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	// We inherit from only the library's GenerateAndroidBuildActions, and then
 	// just use buildBinary() so that the binary is not installed into the location
@@ -153,6 +193,12 @@
 		p.data = append(p.data, android.DataPath{SrcPath: dataSrcPath})
 	}
 
+	if p.isTestHost() && len(p.testProperties.Data_device_bins_both) > 0 {
+		ctx.VisitDirectDepsWithTag(dataDeviceBinsTag, func(dep android.Module) {
+			p.data = append(p.data, android.DataPath{SrcPath: android.OutputFileForModule(ctx, dep, "")})
+		})
+	}
+
 	// Emulate the data property for java_data dependencies.
 	for _, javaData := range ctx.GetDirectDepsWithTag(javaDataTag) {
 		for _, javaDataSrcPath := range android.OutputFilesForModule(ctx, javaData, "") {
diff --git a/rust/doc.go b/rust/doc.go
index fe3581b..6970d79 100644
--- a/rust/doc.go
+++ b/rust/doc.go
@@ -19,7 +19,7 @@
 )
 
 func init() {
-	android.RegisterSingletonType("rustdoc", RustdocSingleton)
+	android.RegisterParallelSingletonType("rustdoc", RustdocSingleton)
 }
 
 func RustdocSingleton() android.Singleton {
diff --git a/rust/project_json.go b/rust/project_json.go
index fe259d6..40aa7c7 100644
--- a/rust/project_json.go
+++ b/rust/project_json.go
@@ -74,7 +74,7 @@
 }
 
 func init() {
-	android.RegisterSingletonType("rust_project_generator", rustProjectGeneratorSingleton)
+	android.RegisterParallelSingletonType("rust_project_generator", rustProjectGeneratorSingleton)
 }
 
 // sourceProviderVariantSource returns the path to the source file if this
diff --git a/rust/rust.go b/rust/rust.go
index dc53cc0..4324ecb 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -45,7 +45,7 @@
 	})
 	pctx.Import("android/soong/rust/config")
 	pctx.ImportAs("cc_config", "android/soong/cc/config")
-	android.InitRegistrationContext.RegisterSingletonType("kythe_rust_extract", kytheExtractRustFactory)
+	android.InitRegistrationContext.RegisterParallelSingletonType("kythe_rust_extract", kytheExtractRustFactory)
 }
 
 type Flags struct {
diff --git a/rust/testing.go b/rust/testing.go
index 0a6a870..7f30569 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -200,8 +200,8 @@
 		ctx.BottomUp("rust_stdlinkage", LibstdMutator).Parallel()
 		ctx.BottomUp("rust_begin", BeginMutator).Parallel()
 	})
-	ctx.RegisterSingletonType("rust_project_generator", rustProjectGeneratorSingleton)
-	ctx.RegisterSingletonType("kythe_rust_extract", kytheExtractRustFactory)
+	ctx.RegisterParallelSingletonType("rust_project_generator", rustProjectGeneratorSingleton)
+	ctx.RegisterParallelSingletonType("kythe_rust_extract", kytheExtractRustFactory)
 	ctx.PostDepsMutators(func(ctx android.RegisterMutatorsContext) {
 		ctx.BottomUp("rust_sanitizers", rustSanitizerRuntimeMutator).Parallel()
 	})
diff --git a/snapshot/host_fake_snapshot.go b/snapshot/host_fake_snapshot.go
index b04657d..c4cfbb5 100644
--- a/snapshot/host_fake_snapshot.go
+++ b/snapshot/host_fake_snapshot.go
@@ -75,7 +75,7 @@
 }
 
 func registerHostSnapshotComponents(ctx android.RegistrationContext) {
-	ctx.RegisterSingletonType("host-fake-snapshot", HostToolsFakeAndroidSingleton)
+	ctx.RegisterParallelSingletonType("host-fake-snapshot", HostToolsFakeAndroidSingleton)
 }
 
 type hostFakeSingleton struct {
diff --git a/snapshot/recovery_snapshot.go b/snapshot/recovery_snapshot.go
index ac002be..8ff59cb 100644
--- a/snapshot/recovery_snapshot.go
+++ b/snapshot/recovery_snapshot.go
@@ -68,7 +68,7 @@
 type RecoverySnapshotImage struct{}
 
 func (RecoverySnapshotImage) Init(ctx android.RegistrationContext) {
-	ctx.RegisterSingletonType("recovery-snapshot", RecoverySnapshotSingleton)
+	ctx.RegisterParallelSingletonType("recovery-snapshot", RecoverySnapshotSingleton)
 }
 
 func (RecoverySnapshotImage) RegisterAdditionalModule(ctx android.RegistrationContext, name string, factory android.ModuleFactory) {
diff --git a/snapshot/vendor_snapshot.go b/snapshot/vendor_snapshot.go
index 8f7b8c2..4484c85 100644
--- a/snapshot/vendor_snapshot.go
+++ b/snapshot/vendor_snapshot.go
@@ -78,8 +78,8 @@
 type VendorSnapshotImage struct{}
 
 func (VendorSnapshotImage) Init(ctx android.RegistrationContext) {
-	ctx.RegisterSingletonType("vendor-snapshot", VendorSnapshotSingleton)
-	ctx.RegisterSingletonType("vendor-fake-snapshot", VendorFakeSnapshotSingleton)
+	ctx.RegisterParallelSingletonType("vendor-snapshot", VendorSnapshotSingleton)
+	ctx.RegisterParallelSingletonType("vendor-fake-snapshot", VendorFakeSnapshotSingleton)
 }
 
 func (VendorSnapshotImage) RegisterAdditionalModule(ctx android.RegistrationContext, name string, factory android.ModuleFactory) {
diff --git a/tests/bootstrap_test.sh b/tests/bootstrap_test.sh
index 5935247..5fc05f8 100755
--- a/tests/bootstrap_test.sh
+++ b/tests/bootstrap_test.sh
@@ -207,8 +207,8 @@
 function test_soong_build_rerun_iff_environment_changes() {
   setup
 
-  mkdir -p cherry
-  cat > cherry/Android.bp <<'EOF'
+  mkdir -p build/soong/cherry
+  cat > build/soong/cherry/Android.bp <<'EOF'
 bootstrap_go_package {
   name: "cherry",
   pkgPath: "android/soong/cherry",
@@ -224,7 +224,7 @@
 }
 EOF
 
-  cat > cherry/cherry.go <<'EOF'
+  cat > build/soong/cherry/cherry.go <<'EOF'
 package cherry
 
 import (
@@ -317,8 +317,8 @@
   run_soong
   local -r mtime1=$(stat -c "%y" out/soong/build.ninja)
 
-  mkdir -p a
-  cat > a/Android.bp <<'EOF'
+  mkdir -p vendor/foo/picard
+  cat > vendor/foo/picard/Android.bp <<'EOF'
 bootstrap_go_package {
   name: "picard-soong-rules",
   pkgPath: "android/soong/picard",
@@ -334,7 +334,7 @@
 }
 EOF
 
-  cat > a/picard.go <<'EOF'
+  cat > vendor/foo/picard/picard.go <<'EOF'
 package picard
 
 import (
@@ -390,11 +390,11 @@
 function test_glob_during_bootstrapping() {
   setup
 
-  mkdir -p a
-  cat > a/Android.bp <<'EOF'
+  mkdir -p build/soong/picard
+  cat > build/soong/picard/Android.bp <<'EOF'
 build=["foo*.bp"]
 EOF
-  cat > a/fooa.bp <<'EOF'
+  cat > build/soong/picard/fooa.bp <<'EOF'
 bootstrap_go_package {
   name: "picard-soong-rules",
   pkgPath: "android/soong/picard",
@@ -410,7 +410,7 @@
 }
 EOF
 
-  cat > a/picard.go <<'EOF'
+  cat > build/soong/picard/picard.go <<'EOF'
 package picard
 
 import (
@@ -459,7 +459,7 @@
 
   grep -q "Make it so" out/soong/build.ninja || fail "Original action not present"
 
-  cat > a/foob.bp <<'EOF'
+  cat > build/soong/picard/foob.bp <<'EOF'
 bootstrap_go_package {
   name: "worf-soong-rules",
   pkgPath: "android/soong/worf",
@@ -476,7 +476,7 @@
 }
 EOF
 
-  cat > a/worf.go <<'EOF'
+  cat > build/soong/picard/worf.go <<'EOF'
 package worf
 
 import "android/soong/picard"
diff --git a/tests/lib.sh b/tests/lib.sh
index 715eac1..7f3970e 100644
--- a/tests/lib.sh
+++ b/tests/lib.sh
@@ -99,6 +99,7 @@
   symlink_directory external/python
   symlink_directory external/sqlite
   symlink_directory external/spdx-tools
+  symlink_directory libcore
 
   touch "$MOCK_TOP/Android.bp"
 }
diff --git a/ui/build/kati.go b/ui/build/kati.go
index dad68fa..aea56d3 100644
--- a/ui/build/kati.go
+++ b/ui/build/kati.go
@@ -22,6 +22,7 @@
 	"os/user"
 	"path/filepath"
 	"strings"
+	"time"
 
 	"android/soong/ui/metrics"
 	"android/soong/ui/status"
@@ -66,6 +67,21 @@
 	}
 }
 
+func writeValueIfChanged(ctx Context, config Config, dir string, filename string, value string) {
+	filePath := filepath.Join(dir, filename)
+	previousValue := ""
+	rawPreviousValue, err := ioutil.ReadFile(filePath)
+	if err == nil {
+		previousValue = string(rawPreviousValue)
+	}
+
+	if previousValue != value {
+		if err = ioutil.WriteFile(filePath, []byte(value), 0666); err != nil {
+			ctx.Fatalf("Failed to write: %v", err)
+		}
+	}
+}
+
 // Base function to construct and run the Kati command line with additional
 // arguments, and a custom function closure to mutate the environment Kati runs
 // in.
@@ -157,28 +173,60 @@
 	}
 	cmd.Stderr = cmd.Stdout
 
-	// Apply the caller's function closure to mutate the environment variables.
-	envFunc(cmd.Environment)
-
+	var username string
 	// Pass on various build environment metadata to Kati.
-	if _, ok := cmd.Environment.Get("BUILD_USERNAME"); !ok {
-		username := "unknown"
+	if usernameFromEnv, ok := cmd.Environment.Get("BUILD_USERNAME"); !ok {
+		username = "unknown"
 		if u, err := user.Current(); err == nil {
 			username = u.Username
 		} else {
 			ctx.Println("Failed to get current user:", err)
 		}
 		cmd.Environment.Set("BUILD_USERNAME", username)
+	} else {
+		username = usernameFromEnv
 	}
 
-	if _, ok := cmd.Environment.Get("BUILD_HOSTNAME"); !ok {
-		hostname, err := os.Hostname()
+	hostname, ok := cmd.Environment.Get("BUILD_HOSTNAME")
+	// Unset BUILD_HOSTNAME during kati run to avoid kati rerun, kati will use BUILD_HOSTNAME from a file.
+	cmd.Environment.Unset("BUILD_HOSTNAME")
+	if !ok {
+		hostname, err = os.Hostname()
 		if err != nil {
 			ctx.Println("Failed to read hostname:", err)
 			hostname = "unknown"
 		}
-		cmd.Environment.Set("BUILD_HOSTNAME", hostname)
 	}
+	writeValueIfChanged(ctx, config, config.SoongOutDir(), "build_hostname.txt", hostname)
+
+	// BUILD_NUMBER should be set to the source control value that
+	// represents the current state of the source code.  E.g., a
+	// perforce changelist number or a git hash.  Can be an arbitrary string
+	// (to allow for source control that uses something other than numbers),
+	// but must be a single word and a valid file name.
+	//
+	// If no BUILD_NUMBER is set, create a useful "I am an engineering build
+	// from this date/time" value.  Make it start with a non-digit so that
+	// anyone trying to parse it as an integer will probably get "0".
+	cmd.Environment.Unset("HAS_BUILD_NUMBER")
+	buildNumber, ok := cmd.Environment.Get("BUILD_NUMBER")
+	// Unset BUILD_NUMBER during kati run to avoid kati rerun, kati will use BUILD_NUMBER from a file.
+	cmd.Environment.Unset("BUILD_NUMBER")
+	if ok {
+		cmd.Environment.Set("HAS_BUILD_NUMBER", "true")
+		writeValueIfChanged(ctx, config, config.OutDir(), "file_name_tag.txt", buildNumber)
+	} else {
+		buildNumber = fmt.Sprintf("eng.%.6s.%s", username, time.Now().Format("20060102.150405" /* YYYYMMDD.HHMMSS */))
+		cmd.Environment.Set("HAS_BUILD_NUMBER", "false")
+		writeValueIfChanged(ctx, config, config.OutDir(), "file_name_tag.txt", username)
+	}
+	// Write the build number to a file so it can be read back in
+	// without changing the command line every time.  Avoids rebuilds
+	// when using ninja.
+	writeValueIfChanged(ctx, config, config.SoongOutDir(), "build_number.txt", buildNumber)
+
+	// Apply the caller's function closure to mutate the environment variables.
+	envFunc(cmd.Environment)
 
 	cmd.StartOrFatal()
 	// Set up the ToolStatus command line reader for Kati for a consistent UI
@@ -336,6 +384,7 @@
 			"ANDROID_BUILD_SHELL",
 			"DIST_DIR",
 			"OUT_DIR",
+			"FILE_NAME_TAG",
 		}...)
 
 		if config.Dist() {