Merge "Start unblocking com.android.runtime deps"
diff --git a/android/arch.go b/android/arch.go
index e08fd5c..96a4cbf 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -993,8 +993,6 @@
 
 	base := m.base()
 
-	// Store the original list of top level property structs
-	base.generalProperties = m.GetProperties()
 	if len(base.archProperties) != 0 {
 		panic(fmt.Errorf("module %s already has archProperties", m.Name()))
 	}
@@ -1015,7 +1013,7 @@
 		return t
 	}
 
-	for _, properties := range base.generalProperties {
+	for _, properties := range m.GetProperties() {
 		t := getStructType(properties)
 		// Get or create the arch-specific property struct types for this property struct type.
 		archPropTypes := archPropTypeMap.Once(NewCustomOnceKey(t), func() interface{} {
@@ -1036,9 +1034,6 @@
 		m.AddProperties(archProperties...)
 	}
 
-	// Update the list of properties that can be set by a defaults module or a call to
-	// AppendMatchingProperties or PrependMatchingProperties.
-	base.customizableProperties = m.GetProperties()
 }
 
 func maybeBlueprintEmbed(src reflect.Value) reflect.Value {
@@ -1111,8 +1106,8 @@
 func (m *ModuleBase) setOSProperties(ctx BottomUpMutatorContext) {
 	os := m.commonProperties.CompileOS
 
-	for i := range m.generalProperties {
-		genProps := m.generalProperties[i]
+	for i := range m.archProperties {
+		genProps := m.GetProperties()[i]
 		if m.archProperties[i] == nil {
 			continue
 		}
@@ -1439,8 +1434,8 @@
 	arch := m.Arch()
 	os := m.Os()
 
-	for i := range m.generalProperties {
-		genProps := m.generalProperties[i]
+	for i := range m.archProperties {
+		genProps := m.GetProperties()[i]
 		if m.archProperties[i] == nil {
 			continue
 		}
@@ -2018,8 +2013,8 @@
 	var archProperties []interface{}
 
 	// First find the property set in the module that corresponds to the requested
-	// one. m.archProperties[i] corresponds to m.generalProperties[i].
-	for i, generalProp := range m.generalProperties {
+	// one. m.archProperties[i] corresponds to m.GetProperties()[i].
+	for i, generalProp := range m.GetProperties() {
 		srcType := reflect.ValueOf(generalProp).Type()
 		if srcType == dstType {
 			archProperties = m.archProperties[i]
diff --git a/android/bazel.go b/android/bazel.go
index 2eb5417..99cc30c 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -452,6 +452,8 @@
 
 		"abb",                     // depends on unconverted modules: libcmd, libbinder
 		"adb",                     // depends on unconverted modules: AdbWinApi, libadb_host, libandroidfw, libapp_processes_protos_full, libfastdeploy_host, libopenscreen-discovery, libopenscreen-platform-impl, libusb, bin2c_fastdeployagent, AdbWinUsbApi
+		"libadb_host",             // depends on unconverted modules: libopenscreen-discovery, libopenscreen-platform-impl, libusb, AdbWinApi
+		"libfastdeploy_host",      // depends on unconverted modules: libandroidfw, libusb, AdbWinApi
 		"linker",                  // depends on unconverted modules: libdebuggerd_handler_fallback
 		"linker_reloc_bench_main", // depends on unconverted modules: liblinker_reloc_bench_*
 		"versioner",               // depends on unconverted modules: libclang_cxx_host, libLLVM_host, of unsupported type llvm_host_prebuilt_library_shared
diff --git a/android/config.go b/android/config.go
index 0187a8a..afc138b 100644
--- a/android/config.go
+++ b/android/config.go
@@ -1469,6 +1469,10 @@
 	return String(c.config.productVariables.TotSepolicyVersion)
 }
 
+func (c *deviceConfig) PlatformSepolicyCompatVersions() []string {
+	return c.config.productVariables.PlatformSepolicyCompatVersions
+}
+
 func (c *deviceConfig) BoardSepolicyVers() string {
 	if ver := String(c.config.productVariables.BoardSepolicyVers); ver != "" {
 		return ver
diff --git a/android/defaults.go b/android/defaults.go
index 5677638..8b121f6 100644
--- a/android/defaults.go
+++ b/android/defaults.go
@@ -96,8 +96,6 @@
 	module.setProperties(module.GetProperties(), module.base().variableProperties)
 
 	module.AddProperties(module.defaults())
-
-	module.base().customizableProperties = module.GetProperties()
 }
 
 // A restricted subset of context methods, similar to LoadHookContext.
diff --git a/android/hooks.go b/android/hooks.go
index 9eaa1ac..bded764 100644
--- a/android/hooks.go
+++ b/android/hooks.go
@@ -68,7 +68,7 @@
 func (l *loadHookContext) appendPrependHelper(props []interface{},
 	extendFn func([]interface{}, interface{}, proptools.ExtendPropertyFilterFunc) error) {
 	for _, p := range props {
-		err := extendFn(l.Module().base().customizableProperties, p, nil)
+		err := extendFn(l.Module().base().GetProperties(), p, nil)
 		if err != nil {
 			if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
 				l.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
diff --git a/android/license.go b/android/license.go
index 8bfd3ba..587cb36 100644
--- a/android/license.go
+++ b/android/license.go
@@ -80,9 +80,6 @@
 	base := module.base()
 	module.AddProperties(&base.nameProperties, &module.properties)
 
-	base.generalProperties = module.GetProperties()
-	base.customizableProperties = module.GetProperties()
-
 	// The visibility property needs to be checked and parsed by the visibility module.
 	setPrimaryVisibilityProperty(module, "visibility", &module.properties.Visibility)
 
diff --git a/android/license_kind.go b/android/license_kind.go
index ddecd77..838dedd 100644
--- a/android/license_kind.go
+++ b/android/license_kind.go
@@ -53,9 +53,6 @@
 	base := module.base()
 	module.AddProperties(&base.nameProperties, &module.properties)
 
-	base.generalProperties = module.GetProperties()
-	base.customizableProperties = module.GetProperties()
-
 	// The visibility property needs to be checked and parsed by the visibility module.
 	setPrimaryVisibilityProperty(module, "visibility", &module.properties.Visibility)
 
diff --git a/android/licenses_test.go b/android/licenses_test.go
index d05b0a3..70160fa 100644
--- a/android/licenses_test.go
+++ b/android/licenses_test.go
@@ -779,9 +779,6 @@
 	base := m.base()
 	m.AddProperties(&base.nameProperties, &m.properties)
 
-	base.generalProperties = m.GetProperties()
-	base.customizableProperties = m.GetProperties()
-
 	// The default_visibility property needs to be checked and parsed by the visibility module during
 	// its checking and parsing phases so make it the primary visibility property.
 	setPrimaryVisibilityProperty(m, "visibility", &m.properties.Visibility)
diff --git a/android/module.go b/android/module.go
index 9a31bf8..4da201c 100644
--- a/android/module.go
+++ b/android/module.go
@@ -19,6 +19,7 @@
 	"os"
 	"path"
 	"path/filepath"
+	"reflect"
 	"regexp"
 	"strings"
 	"text/scanner"
@@ -1039,9 +1040,6 @@
 
 	initProductVariableModule(m)
 
-	base.generalProperties = m.GetProperties()
-	base.customizableProperties = m.GetProperties()
-
 	// The default_visibility property needs to be checked and parsed by the visibility module during
 	// its checking and parsing phases so make it the primary visibility property.
 	setPrimaryVisibilityProperty(m, "visibility", &base.commonProperties.Visibility)
@@ -1149,20 +1147,71 @@
 		}
 	}
 
-	data.Append(required)
+	productConfigEnabledLabels := []bazel.Label{}
+	if !proptools.BoolDefault(enabledProperty.Value, true) {
+		// If the module is not enabled by default, then we can check if a
+		// product variable enables it
+		productConfigEnabledLabels = productVariableConfigEnableLabels(ctx)
 
-	var err error
-	constraints := constraintAttributes{}
-	constraints.Target_compatible_with, err = enabledProperty.ToLabelListAttribute(
+		if len(productConfigEnabledLabels) > 0 {
+			// In this case, an existing product variable configuration overrides any
+			// module-level `enable: false` definition
+			newValue := true
+			enabledProperty.Value = &newValue
+		}
+	}
+
+	productConfigEnabledAttribute := bazel.MakeLabelListAttribute(bazel.LabelList{
+		productConfigEnabledLabels, nil,
+	})
+
+	platformEnabledAttribute, err := enabledProperty.ToLabelListAttribute(
 		bazel.LabelList{[]bazel.Label{bazel.Label{Label: "@platforms//:incompatible"}}, nil},
 		bazel.LabelList{[]bazel.Label{}, nil})
-
 	if err != nil {
-		ctx.ModuleErrorf("Error processing enabled attribute: %s", err)
+		ctx.ModuleErrorf("Error processing platform enabled attribute: %s", err)
 	}
+
+	data.Append(required)
+
+	constraints := constraintAttributes{}
+	moduleEnableConstraints := bazel.LabelListAttribute{}
+	moduleEnableConstraints.Append(platformEnabledAttribute)
+	moduleEnableConstraints.Append(productConfigEnabledAttribute)
+	constraints.Target_compatible_with = moduleEnableConstraints
+
 	return constraints
 }
 
+// Check product variables for `enabled: true` flag override.
+// Returns a list of the constraint_value targets who enable this override.
+func productVariableConfigEnableLabels(ctx *topDownMutatorContext) []bazel.Label {
+	productVariableProps := ProductVariableProperties(ctx)
+	productConfigEnablingTargets := []bazel.Label{}
+	const propName = "Enabled"
+	if productConfigProps, exists := productVariableProps[propName]; exists {
+		for productConfigProp, prop := range productConfigProps {
+			flag, ok := prop.(*bool)
+			if !ok {
+				ctx.ModuleErrorf("Could not convert product variable %s property", proptools.PropertyNameForField(propName))
+			}
+
+			if *flag {
+				axis := productConfigProp.ConfigurationAxis()
+				targetLabel := axis.SelectKey(productConfigProp.SelectKey())
+				productConfigEnablingTargets = append(productConfigEnablingTargets, bazel.Label{
+					Label: targetLabel,
+				})
+			} else {
+				// TODO(b/210546943): handle negative case where `enabled: false`
+				ctx.ModuleErrorf("`enabled: false` is not currently supported for configuration variables. See b/210546943", proptools.PropertyNameForField(propName))
+			}
+		}
+	}
+
+	return productConfigEnablingTargets
+}
+
 // A ModuleBase object contains the properties that are common to all Android
 // modules.  It should be included as an anonymous field in every module
 // struct definition.  InitAndroidModule should then be called from the module's
@@ -1215,17 +1264,14 @@
 	distProperties          distProperties
 	variableProperties      interface{}
 	hostAndDeviceProperties hostAndDeviceProperties
-	generalProperties       []interface{}
 
-	// Arch specific versions of structs in generalProperties. The outer index
-	// has the same order as generalProperties as initialized in
-	// InitAndroidArchModule, and the inner index chooses the props specific to
-	// the architecture. The interface{} value is an archPropRoot that is
-	// filled with arch specific values by the arch mutator.
+	// Arch specific versions of structs in GetProperties() prior to
+	// initialization in InitAndroidArchModule, lets call it `generalProperties`.
+	// The outer index has the same order as generalProperties and the inner index
+	// chooses the props specific to the architecture. The interface{} value is an
+	// archPropRoot that is filled with arch specific values by the arch mutator.
 	archProperties [][]interface{}
 
-	customizableProperties []interface{}
-
 	// Properties specific to the Blueprint to BUILD migration.
 	bazelTargetModuleProperties bazel.BazelTargetModuleProperties
 
@@ -1350,7 +1396,64 @@
 }
 
 func (m *ModuleBase) AddJSONData(d *map[string]interface{}) {
-	(*d)["Android"] = map[string]interface{}{}
+	(*d)["Android"] = map[string]interface{}{
+		// Properties set in Blueprint or in blueprint of a defaults modules
+		"SetProperties": m.propertiesWithValues(),
+	}
+}
+
+type propInfo struct {
+	Name string
+	Type string
+}
+
+func (m *ModuleBase) propertiesWithValues() []propInfo {
+	var info []propInfo
+	props := m.GetProperties()
+
+	var propsWithValues func(name string, v reflect.Value)
+	propsWithValues = func(name string, v reflect.Value) {
+		kind := v.Kind()
+		switch kind {
+		case reflect.Ptr, reflect.Interface:
+			if v.IsNil() {
+				return
+			}
+			propsWithValues(name, v.Elem())
+		case reflect.Struct:
+			if v.IsZero() {
+				return
+			}
+			for i := 0; i < v.NumField(); i++ {
+				namePrefix := name
+				sTyp := v.Type().Field(i)
+				if proptools.ShouldSkipProperty(sTyp) {
+					continue
+				}
+				if name != "" && !strings.HasSuffix(namePrefix, ".") {
+					namePrefix += "."
+				}
+				if !proptools.IsEmbedded(sTyp) {
+					namePrefix += sTyp.Name
+				}
+				sVal := v.Field(i)
+				propsWithValues(namePrefix, sVal)
+			}
+		case reflect.Array, reflect.Slice:
+			if v.IsNil() {
+				return
+			}
+			elKind := v.Type().Elem().Kind()
+			info = append(info, propInfo{name, elKind.String() + " " + kind.String()})
+		default:
+			info = append(info, propInfo{name, kind.String()})
+		}
+	}
+
+	for _, p := range props {
+		propsWithValues("", reflect.ValueOf(p).Elem())
+	}
+	return info
 }
 
 func (m *ModuleBase) ComponentDepsMutator(BottomUpMutatorContext) {}
diff --git a/android/module_test.go b/android/module_test.go
index d9e2c87..c35e66e 100644
--- a/android/module_test.go
+++ b/android/module_test.go
@@ -615,3 +615,204 @@
 
 	return rules
 }
+
+type PropsTestModuleEmbedded struct {
+	Embedded_prop *string
+}
+
+type propsTestModule struct {
+	ModuleBase
+	DefaultableModuleBase
+	props struct {
+		A string `android:"arch_variant"`
+		B *bool
+		C []string
+	}
+	otherProps struct {
+		PropsTestModuleEmbedded
+
+		D      *int64
+		Nested struct {
+			E *string
+		}
+		F *string `blueprint:"mutated"`
+	}
+}
+
+func propsTestModuleFactory() Module {
+	module := &propsTestModule{}
+	module.AddProperties(&module.props, &module.otherProps)
+	InitAndroidArchModule(module, HostAndDeviceSupported, MultilibBoth)
+	InitDefaultableModule(module)
+	return module
+}
+
+type propsTestModuleDefaults struct {
+	ModuleBase
+	DefaultsModuleBase
+}
+
+func propsTestModuleDefaultsFactory() Module {
+	defaults := &propsTestModuleDefaults{}
+	module := propsTestModule{}
+	defaults.AddProperties(&module.props, &module.otherProps)
+	InitDefaultsModule(defaults)
+	return defaults
+}
+
+func (p *propsTestModule) GenerateAndroidBuildActions(ctx ModuleContext) {
+	str := "abc"
+	p.otherProps.F = &str
+}
+
+func TestUsedProperties(t *testing.T) {
+	testCases := []struct {
+		desc          string
+		bp            string
+		expectedProps []propInfo
+	}{
+		{
+			desc: "only name",
+			bp: `test {
+			name: "foo",
+		}
+	`,
+			expectedProps: []propInfo{
+				propInfo{"Name", "string"},
+			},
+		},
+		{
+			desc: "some props",
+			bp: `test {
+			name: "foo",
+			a: "abc",
+			b: true,
+			d: 123,
+		}
+	`,
+			expectedProps: []propInfo{
+				propInfo{"A", "string"},
+				propInfo{"B", "bool"},
+				propInfo{"D", "int64"},
+				propInfo{"Name", "string"},
+			},
+		},
+		{
+			desc: "unused non-pointer prop",
+			bp: `test {
+			name: "foo",
+			b: true,
+			d: 123,
+		}
+	`,
+			expectedProps: []propInfo{
+				// for non-pointer cannot distinguish between unused and intentionally set to empty
+				propInfo{"A", "string"},
+				propInfo{"B", "bool"},
+				propInfo{"D", "int64"},
+				propInfo{"Name", "string"},
+			},
+		},
+		{
+			desc: "nested props",
+			bp: `test {
+			name: "foo",
+			nested: {
+				e: "abc",
+			}
+		}
+	`,
+			expectedProps: []propInfo{
+				propInfo{"Nested.E", "string"},
+				propInfo{"Name", "string"},
+			},
+		},
+		{
+			desc: "arch props",
+			bp: `test {
+			name: "foo",
+			arch: {
+				x86_64: {
+					a: "abc",
+				},
+			}
+		}
+	`,
+			expectedProps: []propInfo{
+				propInfo{"Name", "string"},
+				propInfo{"Arch.X86_64.A", "string"},
+			},
+		},
+		{
+			desc: "embedded props",
+			bp: `test {
+			name: "foo",
+			embedded_prop: "a",
+		}
+	`,
+			expectedProps: []propInfo{
+				propInfo{"Embedded_prop", "string"},
+				propInfo{"Name", "string"},
+			},
+		},
+		{
+			desc: "defaults",
+			bp: `
+test_defaults {
+	name: "foo_defaults",
+	a: "a",
+	b: true,
+	embedded_prop:"a",
+	arch: {
+		x86_64: {
+			a: "a",
+		},
+	},
+}
+test {
+	name: "foo",
+	defaults: ["foo_defaults"],
+	c: ["a"],
+	nested: {
+		e: "d",
+	},
+	target: {
+		linux: {
+			a: "a",
+		},
+	},
+}
+	`,
+			expectedProps: []propInfo{
+				propInfo{"A", "string"},
+				propInfo{"B", "bool"},
+				propInfo{"C", "string slice"},
+				propInfo{"Embedded_prop", "string"},
+				propInfo{"Nested.E", "string"},
+				propInfo{"Name", "string"},
+				propInfo{"Arch.X86_64.A", "string"},
+				propInfo{"Target.Linux.A", "string"},
+				propInfo{"Defaults", "string slice"},
+			},
+		},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.desc, func(t *testing.T) {
+			result := GroupFixturePreparers(
+				PrepareForTestWithAllowMissingDependencies,
+				PrepareForTestWithDefaults,
+				FixtureRegisterWithContext(func(ctx RegistrationContext) {
+					ctx.RegisterModuleType("test", propsTestModuleFactory)
+					ctx.RegisterModuleType("test_defaults", propsTestModuleDefaultsFactory)
+				}),
+				FixtureWithRootAndroidBp(tc.bp),
+			).RunTest(t)
+
+			foo := result.ModuleForTests("foo", "").Module().base()
+
+			AssertDeepEquals(t, "foo ", tc.expectedProps, foo.propertiesWithValues())
+
+		})
+	}
+}
diff --git a/android/neverallow.go b/android/neverallow.go
index 4bb3e57..6f9ae58 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -134,7 +134,6 @@
 		NeverAllow().
 			Without("name", "libhidlbase-combined-impl").
 			Without("name", "libhidlbase").
-			Without("name", "libhidlbase_pgo").
 			With("product_variables.enforce_vintf_manifest.cflags", "*").
 			Because("manifest enforcement should be independent of ."),
 
@@ -215,11 +214,8 @@
 	return []Rule{
 		NeverAllow().
 			ModuleType("makefile_goal").
-			// TODO(b/33691272): remove this after migrating seapp to Soong
-			Without("product_out_path", "obj/ETC/plat_seapp_contexts_intermediates/plat_seapp_contexts").
-			Without("product_out_path", "obj/ETC/plat_seapp_neverallows_intermediates/plat_seapp_neverallows").
 			WithoutMatcher("product_out_path", Regexp("^boot[0-9a-zA-Z.-]*[.]img$")).
-			Because("Only boot images and seapp contexts may be imported as a makefile goal."),
+			Because("Only boot images may be imported as a makefile goal."),
 	}
 }
 
diff --git a/android/neverallow_test.go b/android/neverallow_test.go
index 58a90b3..59016d4 100644
--- a/android/neverallow_test.go
+++ b/android/neverallow_test.go
@@ -324,7 +324,7 @@
 			`),
 		},
 		expectedErrors: []string{
-			"Only boot images and seapp contexts may be imported as a makefile goal.",
+			"Only boot images may be imported as a makefile goal.",
 		},
 	},
 	{
diff --git a/android/path_properties.go b/android/path_properties.go
index 3976880..fdc4d91 100644
--- a/android/path_properties.go
+++ b/android/path_properties.go
@@ -33,7 +33,7 @@
 // The pathDepsMutator automatically adds dependencies on any module that is listed with the
 // ":module" module reference syntax in a property that is tagged with `android:"path"`.
 func pathDepsMutator(ctx BottomUpMutatorContext) {
-	props := ctx.Module().base().generalProperties
+	props := ctx.Module().base().GetProperties()
 	addPathDepsForProps(ctx, props)
 }
 
diff --git a/android/prebuilt.go b/android/prebuilt.go
index b0a4f43..5843487 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -183,7 +183,6 @@
 func initPrebuiltModuleCommon(module PrebuiltInterface) *Prebuilt {
 	p := module.Prebuilt()
 	module.AddProperties(&p.properties)
-	module.base().customizableProperties = module.GetProperties()
 	return p
 }
 
@@ -310,6 +309,54 @@
 	return nil
 }
 
+// PrebuiltGetPreferred returns the module that is preferred for the given
+// module. That is either the module itself or the prebuilt counterpart that has
+// taken its place. The given module must be a direct dependency of the current
+// context module, and it must be the source module if both source and prebuilt
+// exist.
+//
+// This function is for use on dependencies after PrebuiltPostDepsMutator has
+// run - any dependency that is registered before that will already reference
+// the right module. This function is only safe to call after all mutators that
+// may call CreateVariations, e.g. in GenerateAndroidBuildActions.
+func PrebuiltGetPreferred(ctx BaseModuleContext, module Module) Module {
+	if !module.IsReplacedByPrebuilt() {
+		return module
+	}
+	if IsModulePrebuilt(module) {
+		// If we're given a prebuilt then assume there's no source module around.
+		return module
+	}
+
+	sourceModDepFound := false
+	var prebuiltMod Module
+
+	ctx.WalkDeps(func(child, parent Module) bool {
+		if prebuiltMod != nil {
+			return false
+		}
+		if parent == ctx.Module() {
+			// First level: Only recurse if the module is found as a direct dependency.
+			sourceModDepFound = child == module
+			return sourceModDepFound
+		}
+		// Second level: Follow PrebuiltDepTag to the prebuilt.
+		if t := ctx.OtherModuleDependencyTag(child); t == PrebuiltDepTag {
+			prebuiltMod = child
+		}
+		return false
+	})
+
+	if prebuiltMod == nil {
+		if !sourceModDepFound {
+			panic(fmt.Errorf("Failed to find source module as a direct dependency: %s", module))
+		} else {
+			panic(fmt.Errorf("Failed to find prebuilt for source module: %s", module))
+		}
+	}
+	return prebuiltMod
+}
+
 func RegisterPrebuiltsPreArchMutators(ctx RegisterMutatorsContext) {
 	ctx.BottomUp("prebuilt_rename", PrebuiltRenameMutator).Parallel()
 }
diff --git a/android/variable.go b/android/variable.go
index a6156b1..40dd2d8 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -124,6 +124,7 @@
 			Shared_libs       []string
 
 			Cmdline []string
+
 			Srcs         []string
 			Exclude_srcs []string
 		}
@@ -363,6 +364,8 @@
 	PlatformSepolicyVersion *string `json:",omitempty"`
 	TotSepolicyVersion      *string `json:",omitempty"`
 
+	PlatformSepolicyCompatVersions []string `json:",omitempty"`
+
 	VendorVars map[string]map[string]string `json:",omitempty"`
 
 	Ndk_abis *bool `json:",omitempty"`
@@ -598,10 +601,16 @@
 
 	value := p.FullConfig
 	if value == p.Name {
-		value = "enabled"
+		value = ""
 	}
-	// e.g. acme__feature1__enabled, android__board__soc_a
-	return strings.ToLower(strings.Join([]string{p.Namespace, p.Name, value}, "__"))
+
+	// e.g. acme__feature1, android__board__soc_a
+	selectKey := strings.ToLower(strings.Join([]string{p.Namespace, p.Name}, "__"))
+	if value != "" {
+		selectKey = strings.ToLower(strings.Join([]string{selectKey, value}, "__"))
+	}
+
+	return selectKey
 }
 
 // ProductConfigProperties is a map of maps to group property values according
@@ -1028,7 +1037,7 @@
 
 	printfIntoProperties(ctx, prefix, productVariablePropertyValue, variableValue)
 
-	err := proptools.AppendMatchingProperties(m.generalProperties,
+	err := proptools.AppendMatchingProperties(m.GetProperties(),
 		productVariablePropertyValue.Addr().Interface(), nil)
 	if err != nil {
 		if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
diff --git a/bp2build/java_binary_host_conversion_test.go b/bp2build/java_binary_host_conversion_test.go
index 96b8958..c683b25 100644
--- a/bp2build/java_binary_host_conversion_test.go
+++ b/bp2build/java_binary_host_conversion_test.go
@@ -41,7 +41,7 @@
 
 func TestJavaBinaryHost(t *testing.T) {
 	runJavaBinaryHostTestCase(t, bp2buildTestCase{
-		description: "java_binary_host with srcs, exclude_srcs, jni_libs and manifest.",
+		description: "java_binary_host with srcs, exclude_srcs, jni_libs, javacflags, and manifest.",
 		filesystem:  fs,
 		blueprint: `java_binary_host {
     name: "java-binary-host-1",
@@ -49,6 +49,7 @@
     exclude_srcs: ["b.java"],
     manifest: "test.mf",
     jni_libs: ["jni-lib-1"],
+    javacflags: ["-Xdoclint:all/protected"],
     bazel_module: { bp2build_available: true },
 }`,
 		expectedBazelTargets: []string{
@@ -57,6 +58,7 @@
 				"main_class": `"com.android.test.MainClass"`,
 				"deps":       `["//other:jni-lib-1"]`,
 				"jvm_flags":  `["-Djava.library.path=$${RUNPATH}other"]`,
+				"javacopts":  `["-Xdoclint:all/protected"]`,
 			}),
 		},
 	})
diff --git a/bp2build/soong_config_module_type_conversion_test.go b/bp2build/soong_config_module_type_conversion_test.go
index f1489aa..b1e1fb2 100644
--- a/bp2build/soong_config_module_type_conversion_test.go
+++ b/bp2build/soong_config_module_type_conversion_test.go
@@ -68,7 +68,7 @@
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo",
     copts = select({
-        "//build/bazel/product_variables:acme__feature1__enabled": ["-DFEATURE1"],
+        "//build/bazel/product_variables:acme__feature1": ["-DFEATURE1"],
         "//conditions:default": ["-DDEFAULT1"],
     }),
     local_includes = ["."],
@@ -116,7 +116,7 @@
 		expectedBazelTargets: []string{`cc_library_static(
     name = "foo",
     copts = select({
-        "//build/bazel/product_variables:acme__feature1__enabled": ["-DFEATURE1"],
+        "//build/bazel/product_variables:acme__feature1": ["-DFEATURE1"],
         "//conditions:default": ["-DDEFAULT1"],
     }),
     local_includes = ["."],
@@ -240,10 +240,10 @@
         "//build/bazel/product_variables:acme__board__soc_b": ["-DSOC_B"],
         "//conditions:default": ["-DSOC_DEFAULT"],
     }) + select({
-        "//build/bazel/product_variables:acme__feature1__enabled": ["-DFEATURE1"],
+        "//build/bazel/product_variables:acme__feature1": ["-DFEATURE1"],
         "//conditions:default": ["-DDEFAULT1"],
     }) + select({
-        "//build/bazel/product_variables:acme__feature2__enabled": ["-DFEATURE2"],
+        "//build/bazel/product_variables:acme__feature2": ["-DFEATURE2"],
         "//conditions:default": ["-DDEFAULT2"],
     }),
     local_includes = ["."],
@@ -367,7 +367,7 @@
 		expectedBazelTargets: []string{`cc_library_static(
     name = "lib",
     copts = select({
-        "//build/bazel/product_variables:vendor_foo__feature__enabled": [
+        "//build/bazel/product_variables:vendor_foo__feature": [
             "-cflag_feature_2",
             "-cflag_feature_1",
         ],
@@ -446,11 +446,11 @@
 		expectedBazelTargets: []string{`cc_library_static(
     name = "lib",
     asflags = select({
-        "//build/bazel/product_variables:acme__feature__enabled": ["-asflag_bar"],
+        "//build/bazel/product_variables:acme__feature": ["-asflag_bar"],
         "//conditions:default": ["-asflag_default_bar"],
     }),
     copts = select({
-        "//build/bazel/product_variables:acme__feature__enabled": [
+        "//build/bazel/product_variables:acme__feature": [
             "-cflag_foo",
             "-cflag_bar",
         ],
@@ -465,11 +465,11 @@
 			`cc_library_static(
     name = "lib2",
     asflags = select({
-        "//build/bazel/product_variables:acme__feature__enabled": ["-asflag_bar"],
+        "//build/bazel/product_variables:acme__feature": ["-asflag_bar"],
         "//conditions:default": ["-asflag_default_bar"],
     }),
     copts = select({
-        "//build/bazel/product_variables:acme__feature__enabled": [
+        "//build/bazel/product_variables:acme__feature": [
             "-cflag_bar",
             "-cflag_foo",
         ],
@@ -561,13 +561,13 @@
 		expectedBazelTargets: []string{`cc_library_static(
     name = "lib",
     copts = select({
-        "//build/bazel/product_variables:vendor_bar__feature__enabled": ["-DVENDOR_BAR_FEATURE"],
+        "//build/bazel/product_variables:vendor_bar__feature": ["-DVENDOR_BAR_FEATURE"],
         "//conditions:default": ["-DVENDOR_BAR_DEFAULT"],
     }) + select({
-        "//build/bazel/product_variables:vendor_foo__feature__enabled": ["-DVENDOR_FOO_FEATURE"],
+        "//build/bazel/product_variables:vendor_foo__feature": ["-DVENDOR_FOO_FEATURE"],
         "//conditions:default": ["-DVENDOR_FOO_DEFAULT"],
     }) + select({
-        "//build/bazel/product_variables:vendor_qux__feature__enabled": ["-DVENDOR_QUX_FEATURE"],
+        "//build/bazel/product_variables:vendor_qux__feature": ["-DVENDOR_QUX_FEATURE"],
         "//conditions:default": ["-DVENDOR_QUX_DEFAULT"],
     }),
     local_includes = ["."],
@@ -834,3 +834,152 @@
     srcs = ["main.cc"],
 )`}})
 }
+
+func TestSoongConfigModuleType_ProductVariableConfigWithPlatformConfig(t *testing.T) {
+	bp := `
+soong_config_bool_variable {
+    name: "special_build",
+}
+
+soong_config_module_type {
+    name: "alphabet_cc_defaults",
+    module_type: "cc_defaults",
+    config_namespace: "alphabet_module",
+    bool_variables: ["special_build"],
+    properties: ["enabled"],
+}
+
+alphabet_cc_defaults {
+    name: "alphabet_sample_cc_defaults",
+    soong_config_variables: {
+        special_build: {
+            enabled: true,
+        },
+    },
+}
+
+cc_binary {
+    name: "alphabet_binary",
+    srcs: ["main.cc"],
+    defaults: ["alphabet_sample_cc_defaults"],
+    enabled: false,
+    arch: {
+        x86_64: {
+            enabled: false,
+        },
+    },
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
+}`
+
+	runSoongConfigModuleTypeTest(t, bp2buildTestCase{
+		description:                "soong config variables - generates selects for library_linking_strategy",
+		moduleTypeUnderTest:        "cc_binary",
+		moduleTypeUnderTestFactory: cc.BinaryFactory,
+		blueprint:                  bp,
+		filesystem:                 map[string]string{},
+		expectedBazelTargets: []string{`cc_binary(
+    name = "alphabet_binary",
+    local_includes = ["."],
+    srcs = ["main.cc"],
+    target_compatible_with = ["//build/bazel/product_variables:alphabet_module__special_build"] + select({
+        "//build/bazel/platforms/os_arch:android_x86_64": ["@platforms//:incompatible"],
+        "//build/bazel/platforms/os_arch:darwin_arm64": ["@platforms//:incompatible"],
+        "//build/bazel/platforms/os_arch:darwin_x86_64": ["@platforms//:incompatible"],
+        "//build/bazel/platforms/os_arch:linux_bionic_x86_64": ["@platforms//:incompatible"],
+        "//build/bazel/platforms/os_arch:linux_glibc_x86_64": ["@platforms//:incompatible"],
+        "//build/bazel/platforms/os_arch:linux_musl_x86_64": ["@platforms//:incompatible"],
+        "//build/bazel/platforms/os_arch:windows_x86_64": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    }),
+)`}})
+}
+
+func TestSoongConfigModuleType_ProductVariableConfigOverridesEnable(t *testing.T) {
+	bp := `
+soong_config_bool_variable {
+    name: "special_build",
+}
+
+soong_config_module_type {
+    name: "alphabet_cc_defaults",
+    module_type: "cc_defaults",
+    config_namespace: "alphabet_module",
+    bool_variables: ["special_build"],
+    properties: ["enabled"],
+}
+
+alphabet_cc_defaults {
+    name: "alphabet_sample_cc_defaults",
+    soong_config_variables: {
+        special_build: {
+            enabled: true,
+        },
+    },
+}
+
+cc_binary {
+    name: "alphabet_binary",
+    srcs: ["main.cc"],
+    defaults: ["alphabet_sample_cc_defaults"],
+    enabled: false,
+}`
+
+	runSoongConfigModuleTypeTest(t, bp2buildTestCase{
+		description:                "soong config variables - generates selects for library_linking_strategy",
+		moduleTypeUnderTest:        "cc_binary",
+		moduleTypeUnderTestFactory: cc.BinaryFactory,
+		blueprint:                  bp,
+		filesystem:                 map[string]string{},
+		expectedBazelTargets: []string{`cc_binary(
+    name = "alphabet_binary",
+    local_includes = ["."],
+    srcs = ["main.cc"],
+    target_compatible_with = ["//build/bazel/product_variables:alphabet_module__special_build"],
+)`}})
+}
+
+func TestSoongConfigModuleType_ProductVariableIgnoredIfEnabledByDefault(t *testing.T) {
+	bp := `
+soong_config_bool_variable {
+    name: "special_build",
+}
+
+soong_config_module_type {
+    name: "alphabet_cc_defaults",
+    module_type: "cc_defaults",
+    config_namespace: "alphabet_module",
+    bool_variables: ["special_build"],
+    properties: ["enabled"],
+}
+
+alphabet_cc_defaults {
+    name: "alphabet_sample_cc_defaults",
+    soong_config_variables: {
+        special_build: {
+            enabled: true,
+        },
+    },
+}
+
+cc_binary {
+    name: "alphabet_binary",
+    srcs: ["main.cc"],
+    defaults: ["alphabet_sample_cc_defaults"],
+}`
+
+	runSoongConfigModuleTypeTest(t, bp2buildTestCase{
+		description:                "soong config variables - generates selects for library_linking_strategy",
+		moduleTypeUnderTest:        "cc_binary",
+		moduleTypeUnderTestFactory: cc.BinaryFactory,
+		blueprint:                  bp,
+		filesystem:                 map[string]string{},
+		expectedBazelTargets: []string{`cc_binary(
+    name = "alphabet_binary",
+    local_includes = ["."],
+    srcs = ["main.cc"],
+)`}})
+}
diff --git a/cc/binary.go b/cc/binary.go
index b59e762..ee3de3f 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -427,7 +427,7 @@
 		linkerDeps = append(linkerDeps, ndkSharedLibDeps(ctx)...)
 	}
 
-	validations = append(validations, objs.tidyFiles...)
+	validations = append(validations, objs.tidyDepFiles...)
 	linkerDeps = append(linkerDeps, flags.LdFlagsDeps...)
 
 	// Register link action.
diff --git a/cc/builder.go b/cc/builder.go
index fa7f7a3..512f838 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -387,10 +387,11 @@
 	toolchain     config.Toolchain
 
 	// True if these extra features are enabled.
-	tidy         bool
-	gcovCoverage bool
-	sAbiDump     bool
-	emitXrefs    bool
+	tidy          bool
+	needTidyFiles bool
+	gcovCoverage  bool
+	sAbiDump      bool
+	emitXrefs     bool
 
 	assemblerWithCpp bool // True if .s files should be processed with the c preprocessor.
 
@@ -420,6 +421,7 @@
 type Objects struct {
 	objFiles      android.Paths
 	tidyFiles     android.Paths
+	tidyDepFiles  android.Paths // link dependent .tidy files
 	coverageFiles android.Paths
 	sAbiDumpFiles android.Paths
 	kytheFiles    android.Paths
@@ -429,6 +431,7 @@
 	return Objects{
 		objFiles:      append(android.Paths{}, a.objFiles...),
 		tidyFiles:     append(android.Paths{}, a.tidyFiles...),
+		tidyDepFiles:  append(android.Paths{}, a.tidyDepFiles...),
 		coverageFiles: append(android.Paths{}, a.coverageFiles...),
 		sAbiDumpFiles: append(android.Paths{}, a.sAbiDumpFiles...),
 		kytheFiles:    append(android.Paths{}, a.kytheFiles...),
@@ -439,6 +442,7 @@
 	return Objects{
 		objFiles:      append(a.objFiles, b.objFiles...),
 		tidyFiles:     append(a.tidyFiles, b.tidyFiles...),
+		tidyDepFiles:  append(a.tidyDepFiles, b.tidyDepFiles...),
 		coverageFiles: append(a.coverageFiles, b.coverageFiles...),
 		sAbiDumpFiles: append(a.sAbiDumpFiles, b.sAbiDumpFiles...),
 		kytheFiles:    append(a.kytheFiles, b.kytheFiles...),
@@ -452,9 +456,8 @@
 }
 
 // Generate rules for compiling multiple .c, .cpp, or .S files to individual .o files
-func transformSourceToObj(ctx android.ModuleContext, subdir string, srcFiles, noTidySrcs android.Paths,
+func transformSourceToObj(ctx ModuleContext, subdir string, srcFiles, noTidySrcs android.Paths,
 	flags builderFlags, pathDeps android.Paths, cFlagsDeps android.Paths) Objects {
-
 	// Source files are one-to-one with tidy, coverage, or kythe files, if enabled.
 	objFiles := make(android.Paths, len(srcFiles))
 	var tidyFiles android.Paths
@@ -540,8 +543,7 @@
 	// Multiple source files have build rules usually share the same cFlags or tidyFlags.
 	// Define only one version in this module and share it in multiple build rules.
 	// To simplify the code, the shared variables are all named as $flags<nnn>.
-	numSharedFlags := 0
-	flagsMap := make(map[string]string)
+	shared := ctx.getSharedFlags()
 
 	// Share flags only when there are multiple files or tidy rules.
 	var hasMultipleRules = len(srcFiles) > 1 || flags.tidy
@@ -553,11 +555,11 @@
 			return flags
 		}
 		mapKey := kind + flags
-		n, ok := flagsMap[mapKey]
+		n, ok := shared.flagsMap[mapKey]
 		if !ok {
-			numSharedFlags += 1
-			n = strconv.Itoa(numSharedFlags)
-			flagsMap[mapKey] = n
+			shared.numSharedFlags += 1
+			n = strconv.Itoa(shared.numSharedFlags)
+			shared.flagsMap[mapKey] = n
 			ctx.Variable(pctx, kind+n, flags)
 		}
 		return "$" + kind + n
@@ -720,9 +722,14 @@
 
 	}
 
+	var tidyDepFiles android.Paths
+	if flags.needTidyFiles {
+		tidyDepFiles = tidyFiles
+	}
 	return Objects{
 		objFiles:      objFiles,
 		tidyFiles:     tidyFiles,
+		tidyDepFiles:  tidyDepFiles,
 		coverageFiles: coverageFiles,
 		sAbiDumpFiles: sAbiDumpFiles,
 		kytheFiles:    kytheFiles,
diff --git a/cc/cc.go b/cc/cc.go
index a4b7c9c..9c35348 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -210,11 +210,12 @@
 	// These must be after any module include flags, which will be in CommonFlags.
 	SystemIncludeFlags []string
 
-	Toolchain    config.Toolchain
-	Tidy         bool // True if clang-tidy is enabled.
-	GcovCoverage bool // True if coverage files should be generated.
-	SAbiDump     bool // True if header abi dumps should be generated.
-	EmitXrefs    bool // If true, generate Ninja rules to generate emitXrefs input files for Kythe
+	Toolchain     config.Toolchain
+	Tidy          bool // True if ninja .tidy rules should be generated.
+	NeedTidyFiles bool // True if module link should depend on .tidy files
+	GcovCoverage  bool // True if coverage files should be generated.
+	SAbiDump      bool // True if header abi dumps should be generated.
+	EmitXrefs     bool // If true, generate Ninja rules to generate emitXrefs input files for Kythe
 
 	// The instruction set required for clang ("arm" or "thumb").
 	RequiredInstructionSet string
@@ -516,6 +517,12 @@
 	directlyInAnyApex() bool
 	isPreventInstall() bool
 	isCfiAssemblySupportEnabled() bool
+	getSharedFlags() *SharedFlags
+}
+
+type SharedFlags struct {
+	numSharedFlags int
+	flagsMap       map[string]string
 }
 
 type ModuleContext interface {
@@ -827,6 +834,9 @@
 	// Flags used to compile this module
 	flags Flags
 
+	// Shared flags among build rules of this module
+	sharedFlags SharedFlags
+
 	// only non-nil when this is a shared library that reuses the objects of a static library
 	staticAnalogue *StaticLibraryInfo
 
@@ -1605,6 +1615,15 @@
 	return ctx.mod.Properties.PreventInstall
 }
 
+func (ctx *moduleContextImpl) getSharedFlags() *SharedFlags {
+	shared := &ctx.mod.sharedFlags
+	if shared.flagsMap == nil {
+		shared.numSharedFlags = 0
+		shared.flagsMap = make(map[string]string)
+	}
+	return shared
+}
+
 func (ctx *moduleContextImpl) isCfiAssemblySupportEnabled() bool {
 	return ctx.mod.isCfiAssemblySupportEnabled()
 }
@@ -3490,9 +3509,7 @@
 				libraryBp2Build(ctx, c)
 			}
 		} else if !static && !shared {
-			if !prebuilt {
-				libraryHeadersBp2Build(ctx, c)
-			}
+			libraryHeadersBp2Build(ctx, c)
 		} else if static {
 			if prebuilt {
 				prebuiltLibraryStaticBp2Build(ctx, c)
diff --git a/cc/compiler.go b/cc/compiler.go
index 8adc3ab..9dbf2d1 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -675,7 +675,7 @@
 }
 
 // Compile a list of source files into objects a specified subdirectory
-func compileObjs(ctx android.ModuleContext, flags builderFlags, subdir string,
+func compileObjs(ctx ModuleContext, flags builderFlags, subdir string,
 	srcFiles, noTidySrcs, pathDeps android.Paths, cFlagsDeps android.Paths) Objects {
 
 	return transformSourceToObj(ctx, subdir, srcFiles, noTidySrcs, flags, pathDeps, cFlagsDeps)
diff --git a/cc/config/global.go b/cc/config/global.go
index 7f2c23e..e46ac96 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -279,8 +279,8 @@
 
 	// prebuilts/clang default settings.
 	ClangDefaultBase         = "prebuilts/clang/host"
-	ClangDefaultVersion      = "clang-r437112"
-	ClangDefaultShortVersion = "14.0.0"
+	ClangDefaultVersion      = "clang-r437112b"
+	ClangDefaultShortVersion = "14.0.1"
 
 	// Directories with warnings from Android.bp files.
 	WarningAllowedProjects = []string{
diff --git a/cc/coverage.go b/cc/coverage.go
index 8dd2db1..cd7b199 100644
--- a/cc/coverage.go
+++ b/cc/coverage.go
@@ -22,6 +22,7 @@
 	"android/soong/android"
 )
 
+// Add '%c' to default specifier after we resolve http://b/210012154
 const profileInstrFlag = "-fprofile-instr-generate=/data/misc/trace/clang-%p-%m.profraw"
 
 type CoverageProperties struct {
@@ -77,6 +78,11 @@
 	return deps
 }
 
+func EnableContinuousCoverage(ctx android.BaseModuleContext) bool {
+	// http://b/210012154 Disable continuous coverage if we're instrumenting bionic/libc.
+	return !ctx.DeviceConfig().NativeCoverageEnabledForPath("bionic/libc")
+}
+
 func (cov *coverage) flags(ctx ModuleContext, flags Flags, deps PathDeps) (Flags, PathDeps) {
 	clangCoverage := ctx.DeviceConfig().ClangCoverageEnabled()
 	gcovCoverage := ctx.DeviceConfig().GcovCoverageEnabled()
@@ -98,6 +104,11 @@
 		} else if clangCoverage {
 			flags.Local.CommonFlags = append(flags.Local.CommonFlags, profileInstrFlag,
 				"-fcoverage-mapping", "-Wno-pass-failed", "-D__ANDROID_CLANG_COVERAGE__")
+			// Override -Wframe-larger-than that a module may use.
+			flags.Local.CFlags = append(flags.Local.CFlags, "-Wno-frame-larger-than=")
+			if EnableContinuousCoverage(ctx) {
+				flags.Local.CommonFlags = append(flags.Local.CommonFlags, "-mllvm", "-runtime-counter-relocation")
+			}
 		}
 	}
 
@@ -149,6 +160,9 @@
 			flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,--wrap,getenv")
 		} else if clangCoverage {
 			flags.Local.LdFlags = append(flags.Local.LdFlags, profileInstrFlag)
+			if EnableContinuousCoverage(ctx) {
+				flags.Local.LdFlags = append(flags.Local.LdFlags, "-Wl,-mllvm=-runtime-counter-relocation")
+			}
 
 			coverage := ctx.GetDirectDepWithTag(getClangProfileLibraryName(ctx), CoverageDepTag).(*Module)
 			deps.WholeStaticLibs = append(deps.WholeStaticLibs, coverage.OutputFile().Path())
diff --git a/cc/library.go b/cc/library.go
index 5720944..1f9ff7c 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -443,6 +443,8 @@
 	module, library := NewLibrary(android.HostSupported)
 	library.BuildOnlyStatic()
 	module.sdkMemberTypes = []android.SdkMemberType{staticLibrarySdkMemberType}
+	module.bazelable = true
+	module.bazelHandler = &ccLibraryBazelHandler{module: module}
 	return module.Init()
 }
 
@@ -1338,7 +1340,7 @@
 		}
 	}
 
-	transformObjToStaticLib(ctx, library.objects.objFiles, deps.WholeStaticLibsFromPrebuilts, builderFlags, outputFile, nil, objs.tidyFiles)
+	transformObjToStaticLib(ctx, library.objects.objFiles, deps.WholeStaticLibsFromPrebuilts, builderFlags, outputFile, nil, objs.tidyDepFiles)
 
 	library.coverageOutputFile = transformCoverageFilesToZip(ctx, library.objects, ctx.ModuleName())
 
@@ -1485,7 +1487,7 @@
 	linkerDeps = append(linkerDeps, deps.LateSharedLibsDeps...)
 	transformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs,
 		deps.StaticLibs, deps.LateStaticLibs, deps.WholeStaticLibs,
-		linkerDeps, deps.CrtBegin, deps.CrtEnd, false, builderFlags, outputFile, implicitOutputs, objs.tidyFiles)
+		linkerDeps, deps.CrtBegin, deps.CrtEnd, false, builderFlags, outputFile, implicitOutputs, objs.tidyDepFiles)
 
 	objs.coverageFiles = append(objs.coverageFiles, deps.StaticLibObjs.coverageFiles...)
 	objs.coverageFiles = append(objs.coverageFiles, deps.WholeStaticLibObjs.coverageFiles...)
diff --git a/cc/library_headers.go b/cc/library_headers.go
index 70e4715..064e2b8 100644
--- a/cc/library_headers.go
+++ b/cc/library_headers.go
@@ -104,6 +104,8 @@
 func prebuiltLibraryHeaderFactory() android.Module {
 	module, library := NewPrebuiltLibrary(android.HostAndDeviceSupported, "")
 	library.HeaderOnly()
+	module.bazelable = true
+	module.bazelHandler = &ccLibraryBazelHandler{module: module}
 	return module.Init()
 }
 
diff --git a/cc/tidy.go b/cc/tidy.go
index 78a791f..97418fe 100644
--- a/cc/tidy.go
+++ b/cc/tidy.go
@@ -71,13 +71,17 @@
 		return flags
 	}
 
-	// If not explicitly set, check the global tidy flag
-	if tidy.Properties.Tidy == nil && !ctx.Config().ClangTidy() {
-		return flags
-	}
-
+	// If not explicitly disabled, set flags.Tidy to generate .tidy rules.
+	// Note that libraries and binaries will depend on .tidy files ONLY if
+	// the global WITH_TIDY or module 'tidy' property is true.
 	flags.Tidy = true
 
+	// If explicitly enabled, by global default or local tidy property,
+	// set flags.NeedTidyFiles to make this module depend on .tidy files.
+	if ctx.Config().ClangTidy() || Bool(tidy.Properties.Tidy) {
+		flags.NeedTidyFiles = true
+	}
+
 	// Add global WITH_TIDY_FLAGS and local tidy_flags.
 	withTidyFlags := ctx.Config().Getenv("WITH_TIDY_FLAGS")
 	if len(withTidyFlags) > 0 {
diff --git a/cc/util.go b/cc/util.go
index 88b0aba..b256b9a 100644
--- a/cc/util.go
+++ b/cc/util.go
@@ -85,6 +85,7 @@
 		toolchain:     in.Toolchain,
 		gcovCoverage:  in.GcovCoverage,
 		tidy:          in.Tidy,
+		needTidyFiles: in.NeedTidyFiles,
 		sAbiDump:      in.SAbiDump,
 		emitXrefs:     in.EmitXrefs,
 
diff --git a/filesystem/bootimg.go b/filesystem/bootimg.go
index fc973a4..33beb37 100644
--- a/filesystem/bootimg.go
+++ b/filesystem/bootimg.go
@@ -145,12 +145,10 @@
 	}
 
 	dtbName := proptools.String(b.properties.Dtb_prebuilt)
-	if dtbName == "" {
-		ctx.PropertyErrorf("dtb_prebuilt", "must be set")
-		return output
+	if dtbName != "" {
+		dtb := android.PathForModuleSrc(ctx, dtbName)
+		cmd.FlagWithInput("--dtb ", dtb)
 	}
-	dtb := android.PathForModuleSrc(ctx, dtbName)
-	cmd.FlagWithInput("--dtb ", dtb)
 
 	cmdline := strings.Join(b.properties.Cmdline, " ")
 	if cmdline != "" {
@@ -178,20 +176,18 @@
 	cmd.FlagWithArg("--header_version ", headerVersion)
 
 	ramdiskName := proptools.String(b.properties.Ramdisk_module)
-	if ramdiskName == "" {
-		ctx.PropertyErrorf("ramdisk_module", "must be set")
-		return output
-	}
-	ramdisk := ctx.GetDirectDepWithTag(ramdiskName, bootimgRamdiskDep)
-	if filesystem, ok := ramdisk.(*filesystem); ok {
-		flag := "--ramdisk "
-		if vendor {
-			flag = "--vendor_ramdisk "
+	if ramdiskName != "" {
+		ramdisk := ctx.GetDirectDepWithTag(ramdiskName, bootimgRamdiskDep)
+		if filesystem, ok := ramdisk.(*filesystem); ok {
+			flag := "--ramdisk "
+			if vendor {
+				flag = "--vendor_ramdisk "
+			}
+			cmd.FlagWithInput(flag, filesystem.OutputPath())
+		} else {
+			ctx.PropertyErrorf("ramdisk", "%q is not android_filesystem module", ramdisk.Name())
+			return output
 		}
-		cmd.FlagWithInput(flag, filesystem.OutputPath())
-	} else {
-		ctx.PropertyErrorf("ramdisk", "%q is not android_filesystem module", ramdisk.Name())
-		return output
 	}
 
 	bootconfig := proptools.String(b.properties.Bootconfig)
diff --git a/fuzz/fuzz_common.go b/fuzz/fuzz_common.go
index 8861d1b..89f8187 100644
--- a/fuzz/fuzz_common.go
+++ b/fuzz/fuzz_common.go
@@ -31,6 +31,7 @@
 const (
 	Cc   Lang = ""
 	Rust Lang = "rust"
+	Java Lang = "java"
 )
 
 var BoolDefault = proptools.BoolDefault
@@ -220,6 +221,9 @@
 		if lang == Rust {
 			zipFileName = "fuzz-rust-" + hostOrTarget + "-" + arch + ".zip"
 		}
+		if lang == Java {
+			zipFileName = "fuzz-java-" + hostOrTarget + "-" + arch + ".zip"
+		}
 		outputFile := android.PathForOutput(ctx, zipFileName)
 
 		s.Packages = append(s.Packages, outputFile)
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 6a91e01..c3e3ba5 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -106,6 +106,16 @@
 	android.LicenseAnnotationToolchainDependencyTag
 	label string
 }
+
+func (t hostToolDependencyTag) AllowDisabledModuleDependency(target android.Module) bool {
+	// Allow depending on a disabled module if it's replaced by a prebuilt
+	// counterpart. We get the prebuilt through android.PrebuiltGetPreferred in
+	// GenerateAndroidBuildActions.
+	return target.IsReplacedByPrebuilt()
+}
+
+var _ android.AllowDisabledModuleDependency = (*hostToolDependencyTag)(nil)
+
 type generatorProperties struct {
 	// The command to run on one or more input files. Cmd supports substitution of a few variables.
 	//
@@ -298,6 +308,12 @@
 			switch tag := ctx.OtherModuleDependencyTag(module).(type) {
 			case hostToolDependencyTag:
 				tool := ctx.OtherModuleName(module)
+				if m, ok := module.(android.Module); ok {
+					// Necessary to retrieve any prebuilt replacement for the tool, since
+					// toolDepsMutator runs too late for the prebuilt mutators to have
+					// replaced the dependency.
+					module = android.PrebuiltGetPreferred(ctx, m)
+				}
 
 				switch t := module.(type) {
 				case android.HostToolProvider:
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index 714d2f8..04c97fd 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -34,7 +34,9 @@
 	android.PrepareForTestWithFilegroup,
 	PrepareForTestWithGenRuleBuildComponents,
 	android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
+		android.RegisterPrebuiltMutators(ctx)
 		ctx.RegisterModuleType("tool", toolFactory)
+		ctx.RegisterModuleType("prebuilt_tool", prebuiltToolFactory)
 		ctx.RegisterModuleType("output", outputProducerFactory)
 		ctx.RegisterModuleType("use_source", useSourceFactory)
 	}),
@@ -720,6 +722,69 @@
 		result.ModuleForTests("gen_all", "").Module().(*useSource).srcs)
 }
 
+func TestPrebuiltTool(t *testing.T) {
+	testcases := []struct {
+		name             string
+		bp               string
+		expectedToolName string
+	}{
+		{
+			name: "source only",
+			bp: `
+				tool { name: "tool" }
+			`,
+			expectedToolName: "bin/tool",
+		},
+		{
+			name: "prebuilt only",
+			bp: `
+				prebuilt_tool { name: "tool" }
+			`,
+			expectedToolName: "prebuilt_bin/tool",
+		},
+		{
+			name: "source preferred",
+			bp: `
+				tool { name: "tool" }
+				prebuilt_tool { name: "tool" }
+			`,
+			expectedToolName: "bin/tool",
+		},
+		{
+			name: "prebuilt preferred",
+			bp: `
+				tool { name: "tool" }
+				prebuilt_tool { name: "tool", prefer: true }
+			`,
+			expectedToolName: "prebuilt_bin/prebuilt_tool",
+		},
+		{
+			name: "source disabled",
+			bp: `
+				tool { name: "tool", enabled: false }
+				prebuilt_tool { name: "tool" }
+      `,
+			expectedToolName: "prebuilt_bin/prebuilt_tool",
+		},
+	}
+
+	for _, test := range testcases {
+		t.Run(test.name, func(t *testing.T) {
+			result := prepareForGenRuleTest.RunTestWithBp(t, test.bp+`
+				genrule {
+					name: "gen",
+					tools: ["tool"],
+					out: ["foo"],
+					cmd: "$(location tool)",
+				}
+			`)
+			gen := result.Module("gen", "").(*Module)
+			expectedCmd := "__SBOX_SANDBOX_DIR__/tools/out/" + test.expectedToolName
+			android.AssertStringEquals(t, "command", expectedCmd, gen.rawCommands[0])
+		})
+	}
+}
+
 func TestGenruleWithBazel(t *testing.T) {
 	bp := `
 		genrule {
@@ -764,7 +829,33 @@
 	return android.OptionalPathForPath(t.outputFile)
 }
 
+type prebuiltTestTool struct {
+	android.ModuleBase
+	prebuilt android.Prebuilt
+	testTool
+}
+
+func (p *prebuiltTestTool) Name() string {
+	return p.prebuilt.Name(p.ModuleBase.Name())
+}
+
+func (p *prebuiltTestTool) Prebuilt() *android.Prebuilt {
+	return &p.prebuilt
+}
+
+func (t *prebuiltTestTool) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	t.outputFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "prebuilt_bin"), ctx.ModuleName(), android.PathForOutput(ctx, ctx.ModuleName()))
+}
+
+func prebuiltToolFactory() android.Module {
+	module := &prebuiltTestTool{}
+	android.InitPrebuiltModuleWithoutSrcs(module)
+	android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
+	return module
+}
+
 var _ android.HostToolProvider = (*testTool)(nil)
+var _ android.HostToolProvider = (*prebuiltTestTool)(nil)
 
 type testOutputProducer struct {
 	android.ModuleBase
diff --git a/java/Android.bp b/java/Android.bp
index 8835b44..c062941 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -44,6 +44,7 @@
         "dexpreopt_config.go",
         "droiddoc.go",
         "droidstubs.go",
+        "fuzz.go",
         "gen.go",
         "genrule.go",
         "hiddenapi.go",
diff --git a/java/aar.go b/java/aar.go
index 13390db..aabbec6 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -616,6 +616,7 @@
 	exportPackage         android.WritablePath
 	extraAaptPackagesFile android.WritablePath
 	manifest              android.WritablePath
+	assetsPackage         android.WritablePath
 
 	exportedStaticPackages android.Paths
 
@@ -686,9 +687,8 @@
 	return android.Paths{a.manifest}
 }
 
-// TODO(jungjw): Decide whether we want to implement this.
 func (a *AARImport) ExportedAssets() android.OptionalPath {
-	return android.OptionalPath{}
+	return android.OptionalPathForPath(a.assetsPackage)
 }
 
 // RRO enforcement is not available on aar_import since its RRO dirs are not
@@ -732,10 +732,11 @@
 	blueprint.RuleParams{
 		Command: `rm -rf $outDir && mkdir -p $outDir && ` +
 			`unzip -qoDD -d $outDir $in && rm -rf $outDir/res && touch $out && ` +
+			`${config.Zip2ZipCmd} -i $in -o $assetsPackage 'assets/**/*' && ` +
 			`${config.MergeZipsCmd} $combinedClassesJar $$(ls $outDir/classes.jar 2> /dev/null) $$(ls $outDir/libs/*.jar 2> /dev/null)`,
-		CommandDeps: []string{"${config.MergeZipsCmd}"},
+		CommandDeps: []string{"${config.MergeZipsCmd}", "${config.Zip2ZipCmd}"},
 	},
-	"outDir", "combinedClassesJar")
+	"outDir", "combinedClassesJar", "assetsPackage")
 
 func (a *AARImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	if len(a.properties.Aars) != 1 {
@@ -761,15 +762,17 @@
 	a.classpathFile = extractedAARDir.Join(ctx, "classes-combined.jar")
 	a.proguardFlags = extractedAARDir.Join(ctx, "proguard.txt")
 	a.manifest = extractedAARDir.Join(ctx, "AndroidManifest.xml")
+	a.assetsPackage = android.PathForModuleOut(ctx, "assets.zip")
 
 	ctx.Build(pctx, android.BuildParams{
 		Rule:        unzipAAR,
 		Input:       a.aarPath,
-		Outputs:     android.WritablePaths{a.classpathFile, a.proguardFlags, a.manifest},
+		Outputs:     android.WritablePaths{a.classpathFile, a.proguardFlags, a.manifest, a.assetsPackage},
 		Description: "unzip AAR",
 		Args: map[string]string{
 			"outDir":             extractedAARDir.String(),
 			"combinedClassesJar": a.classpathFile.String(),
+			"assetsPackage":      a.assetsPackage.String(),
 		},
 	})
 
@@ -812,6 +815,19 @@
 	aapt2Link(ctx, a.exportPackage, srcJar, proguardOptionsFile, rTxt, a.extraAaptPackagesFile,
 		linkFlags, linkDeps, nil, overlayRes, transitiveAssets, nil)
 
+	// Merge this import's assets with its dependencies' assets (if there are any).
+	if len(transitiveAssets) > 0 {
+		mergedAssets := android.PathForModuleOut(ctx, "merged-assets.zip")
+		inputZips := append(android.Paths{a.assetsPackage}, transitiveAssets...)
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        mergeAssetsRule,
+			Inputs:      inputZips,
+			Output:      mergedAssets,
+			Description: "merge assets from dependencies and self",
+		})
+		a.assetsPackage = mergedAssets
+	}
+
 	ctx.SetProvider(JavaInfoProvider, JavaInfo{
 		HeaderJars:                     android.PathsIfNonNil(a.classpathFile),
 		ImplementationAndResourcesJars: android.PathsIfNonNil(a.classpathFile),
diff --git a/java/androidmk.go b/java/androidmk.go
index 19fe7e2..b930441 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -433,8 +433,10 @@
 	if len(a.appProperties.Overrides) > 0 {
 		overridden = append(overridden, a.appProperties.Overrides...)
 	}
-	if a.Name() != a.installApkName {
-		overridden = append(overridden, a.Name())
+	// When APK name is overridden via PRODUCT_PACKAGE_NAME_OVERRIDES
+	// ensure that the original name is overridden.
+	if a.Stem() != a.installApkName {
+		overridden = append(overridden, a.Stem())
 	}
 	return overridden
 }
diff --git a/java/app.go b/java/app.go
index 1c69aeb..f574599 100755
--- a/java/app.go
+++ b/java/app.go
@@ -621,7 +621,7 @@
 	a.aapt.useEmbeddedDex = Bool(a.appProperties.Use_embedded_dex)
 
 	// Check if the install APK name needs to be overridden.
-	a.installApkName = ctx.DeviceConfig().OverridePackageNameFor(a.Name())
+	a.installApkName = ctx.DeviceConfig().OverridePackageNameFor(a.Stem())
 
 	if ctx.ModuleName() == "framework-res" {
 		// framework-res.apk is installed as system/framework/framework-res.apk
@@ -1006,6 +1006,7 @@
 	command := rule.Command().BuiltTool("test_config_fixer").Input(testConfig).Output(fixedConfig)
 	fixNeeded := false
 
+	// Auto-generated test config uses `ModuleName` as the APK name. So fix it if it is not the case.
 	if ctx.ModuleName() != a.installApkName {
 		fixNeeded = true
 		command.FlagWithArg("--test-file-name ", a.installApkName+".apk")
@@ -1162,7 +1163,10 @@
 // some of its properties.
 func OverrideAndroidAppModuleFactory() android.Module {
 	m := &OverrideAndroidApp{}
-	m.AddProperties(&overridableAppProperties{})
+	m.AddProperties(
+		&OverridableDeviceProperties{},
+		&overridableAppProperties{},
+	)
 
 	android.InitAndroidMultiTargetsArchModule(m, android.DeviceSupported, android.MultilibCommon)
 	android.InitOverrideModule(m)
diff --git a/java/app_test.go b/java/app_test.go
index 4da7c3d..2322ef4 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -1707,7 +1707,7 @@
 			},
 		},
 		{
-			name: "overridden",
+			name: "overridden via PRODUCT_PACKAGE_NAME_OVERRIDES",
 			bp: `
 				android_app {
 					name: "foo",
@@ -1722,6 +1722,22 @@
 				"out/soong/target/product/test_device/system/app/bar/bar.apk",
 			},
 		},
+		{
+			name: "overridden via stem",
+			bp: `
+				android_app {
+					name: "foo",
+					srcs: ["a.java"],
+					sdk_version: "current",
+					stem: "bar",
+				}
+			`,
+			packageNameOverride: "",
+			expected: []string{
+				"out/soong/.intermediates/foo/android_common/bar.apk",
+				"out/soong/target/product/test_device/system/app/bar/bar.apk",
+			},
+		},
 	}
 
 	for _, test := range testCases {
@@ -1965,6 +1981,80 @@
 	}
 }
 
+func TestOverrideAndroidAppStem(t *testing.T) {
+	ctx, _ := testJava(t, `
+		android_app {
+			name: "foo",
+			srcs: ["a.java"],
+			sdk_version: "current",
+		}
+		override_android_app {
+			name: "bar",
+			base: "foo",
+		}
+		override_android_app {
+			name: "baz",
+			base: "foo",
+			stem: "baz_stem",
+		}
+		android_app {
+			name: "foo2",
+			srcs: ["a.java"],
+			sdk_version: "current",
+			stem: "foo2_stem",
+		}
+		override_android_app {
+			name: "bar2",
+			base: "foo2",
+		}
+		override_android_app {
+			name: "baz2",
+			base: "foo2",
+			stem: "baz2_stem",
+		}
+	`)
+	for _, expected := range []struct {
+		moduleName  string
+		variantName string
+		apkPath     string
+	}{
+		{
+			moduleName:  "foo",
+			variantName: "android_common",
+			apkPath:     "out/soong/target/product/test_device/system/app/foo/foo.apk",
+		},
+		{
+			moduleName:  "foo",
+			variantName: "android_common_bar",
+			apkPath:     "out/soong/target/product/test_device/system/app/bar/bar.apk",
+		},
+		{
+			moduleName:  "foo",
+			variantName: "android_common_baz",
+			apkPath:     "out/soong/target/product/test_device/system/app/baz_stem/baz_stem.apk",
+		},
+		{
+			moduleName:  "foo2",
+			variantName: "android_common",
+			apkPath:     "out/soong/target/product/test_device/system/app/foo2_stem/foo2_stem.apk",
+		},
+		{
+			moduleName:  "foo2",
+			variantName: "android_common_bar2",
+			// Note that this may cause the duplicate output error.
+			apkPath: "out/soong/target/product/test_device/system/app/foo2_stem/foo2_stem.apk",
+		},
+		{
+			moduleName:  "foo2",
+			variantName: "android_common_baz2",
+			apkPath:     "out/soong/target/product/test_device/system/app/baz2_stem/baz2_stem.apk",
+		},
+	} {
+		variant := ctx.ModuleForTests(expected.moduleName, expected.variantName)
+		variant.Output(expected.apkPath)
+	}
+}
+
 func TestOverrideAndroidAppDependency(t *testing.T) {
 	ctx, _ := testJava(t, `
 		android_app {
@@ -2168,10 +2258,33 @@
 				t.Errorf("test_config_fixer was not expected to run, but did: %q", params.RuleParams.Command)
 			}
 		}
-
 	}
 }
 
+func TestInstrumentationTargetPrebuilt(t *testing.T) {
+	bp := `
+		android_app_import {
+			name: "foo",
+			apk: "foo.apk",
+			presigned: true,
+		}
+
+		android_test {
+			name: "bar",
+			srcs: ["a.java"],
+			instrumentation_for: "foo",
+			sdk_version: "current",
+		}
+		`
+
+	android.GroupFixturePreparers(
+		PrepareForTestWithJavaDefaultModules,
+	).ExtendWithErrorHandler(
+		android.FixtureExpectsAtLeastOneErrorMatchingPattern(
+			"instrumentation_for: dependency \"foo\" of type \"android_app_import\" does not provide JavaInfo so is unsuitable for use with this property")).
+		RunTestWithBp(t, bp)
+}
+
 func TestStl(t *testing.T) {
 	ctx, _ := testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
 		cc_library {
diff --git a/java/base.go b/java/base.go
index 7cd71a2..63328c8 100644
--- a/java/base.go
+++ b/java/base.go
@@ -253,9 +253,6 @@
 	// otherwise provides defaults libraries to add to the bootclasspath.
 	System_modules *string
 
-	// set the name of the output
-	Stem *string
-
 	IsSDKLibrary bool `blueprint:"mutated"`
 
 	// If true, generate the signature file of APK Signing Scheme V4, along side the signed APK file.
@@ -267,6 +264,15 @@
 	SyspropPublicStub string `blueprint:"mutated"`
 }
 
+// Device properties that can be overridden by overriding module (e.g. override_android_app)
+type OverridableDeviceProperties struct {
+	// set the name of the output. If not set, `name` is used.
+	// To override a module with this property set, overriding module might need to set this as well.
+	// Otherwise, both the overridden and the overriding modules will have the same output name, which
+	// can cause the duplicate output error.
+	Stem *string
+}
+
 // Functionality common to Module and Import
 //
 // It is embedded in Module so its functionality can be used by methods in Module
@@ -389,6 +395,8 @@
 	protoProperties  android.ProtoProperties
 	deviceProperties DeviceProperties
 
+	overridableDeviceProperties OverridableDeviceProperties
+
 	// jar file containing header classes including static library dependencies, suitable for
 	// inserting into the bootclasspath/classpath of another compile
 	headerJarFile android.Path
@@ -544,6 +552,7 @@
 	j.addHostProperties()
 	j.AddProperties(
 		&j.deviceProperties,
+		&j.overridableDeviceProperties,
 		&j.dexer.dexProperties,
 		&j.dexpreoptProperties,
 		&j.linter.properties,
@@ -1671,7 +1680,7 @@
 }
 
 func (j *Module) Stem() string {
-	return proptools.StringDefault(j.deviceProperties.Stem, j.Name())
+	return proptools.StringDefault(j.overridableDeviceProperties.Stem, j.Name())
 }
 
 func (j *Module) JacocoReportClassesFile() android.Path {
@@ -1936,6 +1945,9 @@
 				sm := module.(SystemModulesProvider)
 				outputDir, outputDeps := sm.OutputDirAndDeps()
 				deps.systemModules = &systemModules{outputDir, outputDeps}
+
+			case instrumentationForTag:
+				ctx.PropertyErrorf("instrumentation_for", "dependency %q of type %q does not provide JavaInfo so is unsuitable for use with this property", ctx.OtherModuleName(module), ctx.OtherModuleType(module))
 			}
 		}
 
diff --git a/java/dex.go b/java/dex.go
index 8045b5c..474694a 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -72,6 +72,9 @@
 	// This defaults to reasonable value based on module and should not be set.
 	// It exists only to support ART tests.
 	Uncompress_dex *bool
+
+	// Exclude kotlinc generate files: *.kotlin_module, *.kotlin_builtins. Defaults to false.
+	Exclude_kotlinc_generated_files *bool
 }
 
 type dexer struct {
@@ -94,7 +97,7 @@
 			`${config.Zip2ZipCmd} -i $in -o $tmpJar -x '**/*.dex' && ` +
 			`$d8Template${config.D8Cmd} ${config.DexFlags} --output $outDir $d8Flags $tmpJar && ` +
 			`$zipTemplate${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
-			`${config.MergeZipsCmd} -D -stripFile "**/*.class" $out $outDir/classes.dex.jar $in`,
+			`${config.MergeZipsCmd} -D -stripFile "**/*.class" $mergeZipsFlags $out $outDir/classes.dex.jar $in`,
 		CommandDeps: []string{
 			"${config.D8Cmd}",
 			"${config.Zip2ZipCmd}",
@@ -116,7 +119,7 @@
 			ExecStrategy: "${config.RED8ExecStrategy}",
 			Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
 		},
-	}, []string{"outDir", "d8Flags", "zipFlags", "tmpJar"}, nil)
+	}, []string{"outDir", "d8Flags", "zipFlags", "tmpJar", "mergeZipsFlags"}, nil)
 
 var r8, r8RE = pctx.MultiCommandRemoteStaticRules("r8",
 	blueprint.RuleParams{
@@ -134,7 +137,7 @@
 			`${config.SoongZipCmd} -o ${outUsageZip} -C ${outUsageDir} -f ${outUsage} && ` +
 			`rm -rf ${outUsageDir} && ` +
 			`$zipTemplate${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
-			`${config.MergeZipsCmd} -D -stripFile "**/*.class" $out $outDir/classes.dex.jar $in`,
+			`${config.MergeZipsCmd} -D -stripFile "**/*.class" $mergeZipsFlags $out $outDir/classes.dex.jar $in`,
 		CommandDeps: []string{
 			"${config.R8Cmd}",
 			"${config.Zip2ZipCmd}",
@@ -165,7 +168,7 @@
 			Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
 		},
 	}, []string{"outDir", "outDict", "outUsage", "outUsageZip", "outUsageDir",
-		"r8Flags", "zipFlags", "tmpJar"}, []string{"implicits"})
+		"r8Flags", "zipFlags", "tmpJar", "mergeZipsFlags"}, []string{"implicits"})
 
 func (d *dexer) dexCommonFlags(ctx android.ModuleContext,
 	minSdkVersion android.SdkSpec) (flags []string, deps android.Paths) {
@@ -254,6 +257,15 @@
 
 	if BoolDefault(opt.Proguard_compatibility, true) {
 		r8Flags = append(r8Flags, "--force-proguard-compatibility")
+	} else {
+		// TODO(b/213833843): Allow configuration of the prefix via a build variable.
+		var sourceFilePrefix = "go/retraceme "
+		var sourceFileTemplate = "\"" + sourceFilePrefix + "%MAP_ID\""
+		// TODO(b/200967150): Also tag the source file in compat builds.
+		if Bool(opt.Optimize) || Bool(opt.Obfuscate) {
+			r8Flags = append(r8Flags, "--map-id-template", "%MAP_HASH")
+			r8Flags = append(r8Flags, "--source-file-template", sourceFileTemplate)
+		}
 	}
 
 	// TODO(ccross): Don't shrink app instrumentation tests by default.
@@ -298,6 +310,12 @@
 
 	commonFlags, commonDeps := d.dexCommonFlags(ctx, minSdkVersion)
 
+	// Exclude kotlinc generated files when "exclude_kotlinc_generated_files" is set to true.
+	mergeZipsFlags := ""
+	if proptools.BoolDefault(d.dexProperties.Exclude_kotlinc_generated_files, false) {
+		mergeZipsFlags = "-stripFile META-INF/*.kotlin_module -stripFile **/*.kotlin_builtins"
+	}
+
 	useR8 := d.effectiveOptimizeEnabled()
 	if useR8 {
 		proguardDictionary := android.PathForModuleOut(ctx, "proguard_dictionary")
@@ -311,14 +329,15 @@
 		r8Deps = append(r8Deps, commonDeps...)
 		rule := r8
 		args := map[string]string{
-			"r8Flags":     strings.Join(append(commonFlags, r8Flags...), " "),
-			"zipFlags":    zipFlags,
-			"outDict":     proguardDictionary.String(),
-			"outUsageDir": proguardUsageDir.String(),
-			"outUsage":    proguardUsage.String(),
-			"outUsageZip": proguardUsageZip.String(),
-			"outDir":      outDir.String(),
-			"tmpJar":      tmpJar.String(),
+			"r8Flags":        strings.Join(append(commonFlags, r8Flags...), " "),
+			"zipFlags":       zipFlags,
+			"outDict":        proguardDictionary.String(),
+			"outUsageDir":    proguardUsageDir.String(),
+			"outUsage":       proguardUsage.String(),
+			"outUsageZip":    proguardUsageZip.String(),
+			"outDir":         outDir.String(),
+			"tmpJar":         tmpJar.String(),
+			"mergeZipsFlags": mergeZipsFlags,
 		}
 		if ctx.Config().UseRBE() && ctx.Config().IsEnvTrue("RBE_R8") {
 			rule = r8RE
@@ -347,10 +366,11 @@
 			Input:       classesJar,
 			Implicits:   d8Deps,
 			Args: map[string]string{
-				"d8Flags":  strings.Join(append(commonFlags, d8Flags...), " "),
-				"zipFlags": zipFlags,
-				"outDir":   outDir.String(),
-				"tmpJar":   tmpJar.String(),
+				"d8Flags":        strings.Join(append(commonFlags, d8Flags...), " "),
+				"zipFlags":       zipFlags,
+				"outDir":         outDir.String(),
+				"tmpJar":         tmpJar.String(),
+				"mergeZipsFlags": mergeZipsFlags,
 			},
 		})
 	}
diff --git a/java/droidstubs.go b/java/droidstubs.go
index 7ad316f..5a84e05 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -19,6 +19,7 @@
 	"path/filepath"
 	"strings"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
@@ -806,7 +807,8 @@
 
 	properties PrebuiltStubsSourcesProperties
 
-	stubsSrcJar android.Path
+	stubsSrcJar     android.Path
+	jsonDataActions []blueprint.JSONDataAction
 }
 
 func (p *PrebuiltStubsSources) OutputFiles(tag string) (android.Paths, error) {
@@ -822,6 +824,13 @@
 	return d.stubsSrcJar
 }
 
+// AddJSONData is a temporary solution for droidstubs module to put action
+// related data into the module json graph.
+func (p *PrebuiltStubsSources) AddJSONData(d *map[string]interface{}) {
+	p.ModuleBase.AddJSONData(d)
+	(*d)["Actions"] = blueprint.FormatJSONDataActions(p.jsonDataActions)
+}
+
 func (p *PrebuiltStubsSources) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	if len(p.properties.Srcs) != 1 {
 		ctx.PropertyErrorf("srcs", "must only specify one directory path or srcjar, contains %d paths", len(p.properties.Srcs))
@@ -829,9 +838,12 @@
 	}
 
 	src := p.properties.Srcs[0]
+	var jsonDataAction blueprint.JSONDataAction
 	if filepath.Ext(src) == ".srcjar" {
 		// This is a srcjar. We can use it directly.
 		p.stubsSrcJar = android.PathForModuleSrc(ctx, src)
+		jsonDataAction.Inputs = []string{src}
+		jsonDataAction.Outputs = []string{src}
 	} else {
 		outPath := android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"stubs.srcjar")
 
@@ -855,7 +867,10 @@
 		rule.Restat()
 		rule.Build("zip src", "Create srcjar from prebuilt source")
 		p.stubsSrcJar = outPath
+		jsonDataAction.Inputs = srcPaths.Strings()
+		jsonDataAction.Outputs = []string{outPath.String()}
 	}
+	p.jsonDataActions = []blueprint.JSONDataAction{jsonDataAction}
 }
 
 func (p *PrebuiltStubsSources) Prebuilt() *android.Prebuilt {
diff --git a/java/droidstubs_test.go b/java/droidstubs_test.go
index 10d99f3..82ebba7 100644
--- a/java/droidstubs_test.go
+++ b/java/droidstubs_test.go
@@ -21,6 +21,8 @@
 	"strings"
 	"testing"
 
+	"github.com/google/blueprint"
+
 	"android/soong/android"
 )
 
@@ -232,6 +234,27 @@
 	checkSystemModulesUseByDroidstubs(t, ctx, "stubs-prebuilt-system-modules", "prebuilt-jar.jar")
 }
 
+func TestAddJSONData(t *testing.T) {
+	prebuiltStubsSources := PrebuiltStubsSources{}
+	prebuiltStubsSources.jsonDataActions = []blueprint.JSONDataAction{
+		blueprint.JSONDataAction{
+			Inputs:  []string{},
+			Outputs: []string{},
+		},
+	}
+	jsonData := map[string]interface{}{}
+	prebuiltStubsSources.AddJSONData(&jsonData)
+	expectedOut := []map[string]interface{}{
+		map[string]interface{}{
+			"Inputs":  []string{},
+			"Outputs": []string{},
+		},
+	}
+	if !reflect.DeepEqual(jsonData["Actions"], expectedOut) {
+		t.Errorf("The JSON action data %#v isn't as expected %#v.", jsonData["Actions"], expectedOut)
+	}
+}
+
 func checkSystemModulesUseByDroidstubs(t *testing.T, ctx *android.TestContext, moduleName string, systemJar string) {
 	metalavaRule := ctx.ModuleForTests(moduleName, "android_common").Rule("metalava")
 	var systemJars []string
diff --git a/java/fuzz.go b/java/fuzz.go
new file mode 100644
index 0000000..257f343
--- /dev/null
+++ b/java/fuzz.go
@@ -0,0 +1,159 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+	"github.com/google/blueprint/proptools"
+	"sort"
+	"strings"
+
+	"android/soong/android"
+	"android/soong/fuzz"
+)
+
+func init() {
+	RegisterJavaFuzzBuildComponents(android.InitRegistrationContext)
+}
+
+func RegisterJavaFuzzBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("java_fuzz_host", FuzzFactory)
+	ctx.RegisterSingletonType("java_fuzz_packaging", javaFuzzPackagingFactory)
+}
+
+type JavaFuzzLibrary struct {
+	Library
+	fuzzPackagedModule fuzz.FuzzPackagedModule
+}
+
+func (j *JavaFuzzLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	j.Library.GenerateAndroidBuildActions(ctx)
+
+	if j.fuzzPackagedModule.FuzzProperties.Corpus != nil {
+		j.fuzzPackagedModule.Corpus = android.PathsForModuleSrc(ctx, j.fuzzPackagedModule.FuzzProperties.Corpus)
+	}
+	if j.fuzzPackagedModule.FuzzProperties.Data != nil {
+		j.fuzzPackagedModule.Data = android.PathsForModuleSrc(ctx, j.fuzzPackagedModule.FuzzProperties.Data)
+	}
+	if j.fuzzPackagedModule.FuzzProperties.Dictionary != nil {
+		j.fuzzPackagedModule.Dictionary = android.PathForModuleSrc(ctx, *j.fuzzPackagedModule.FuzzProperties.Dictionary)
+	}
+
+	if j.fuzzPackagedModule.FuzzProperties.Fuzz_config != nil {
+		configPath := android.PathForModuleOut(ctx, "config").Join(ctx, "config.json")
+		android.WriteFileRule(ctx, configPath, j.fuzzPackagedModule.FuzzProperties.Fuzz_config.String())
+		j.fuzzPackagedModule.Config = configPath
+	}
+}
+
+// java_fuzz builds and links sources into a `.jar` file for the host.
+//
+// By default, a java_fuzz produces a `.jar` file containing `.class` files.
+// This jar is not suitable for installing on a device.
+func FuzzFactory() android.Module {
+	module := &JavaFuzzLibrary{}
+
+	module.addHostProperties()
+	module.Module.properties.Installable = proptools.BoolPtr(false)
+	module.AddProperties(&module.fuzzPackagedModule.FuzzProperties)
+
+	// java_fuzz packaging rules collide when both linux_glibc and linux_bionic are enabled, disable the linux_bionic variants.
+	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
+		disableLinuxBionic := struct {
+			Target struct {
+				Linux_bionic struct {
+					Enabled *bool
+				}
+			}
+		}{}
+		disableLinuxBionic.Target.Linux_bionic.Enabled = proptools.BoolPtr(false)
+		ctx.AppendProperties(&disableLinuxBionic)
+	})
+
+	module.initModuleAndImport(module)
+	android.InitSdkAwareModule(module)
+	InitJavaModule(module, android.HostSupported)
+	return module
+}
+
+// Responsible for generating rules that package fuzz targets into
+// their architecture & target/host specific zip file.
+type javaFuzzPackager struct {
+	fuzz.FuzzPackager
+}
+
+func javaFuzzPackagingFactory() android.Singleton {
+	return &javaFuzzPackager{}
+}
+
+func (s *javaFuzzPackager) GenerateBuildActions(ctx android.SingletonContext) {
+	// Map between each architecture + host/device combination.
+	archDirs := make(map[fuzz.ArchOs][]fuzz.FileToZip)
+
+	// List of individual fuzz targets.
+	s.FuzzTargets = make(map[string]bool)
+
+	ctx.VisitAllModules(func(module android.Module) {
+		// Discard non-fuzz targets.
+		javaModule, ok := module.(*JavaFuzzLibrary)
+		if !ok {
+			return
+		}
+
+		fuzzModuleValidator := fuzz.FuzzModule{
+			javaModule.ModuleBase,
+			javaModule.DefaultableModuleBase,
+			javaModule.ApexModuleBase,
+		}
+
+		if ok := fuzz.IsValid(fuzzModuleValidator); !ok || *javaModule.Module.properties.Installable {
+			return
+		}
+
+		hostOrTargetString := "target"
+		if javaModule.Host() {
+			hostOrTargetString = "host"
+		}
+		archString := javaModule.Arch().ArchType.String()
+
+		archDir := android.PathForIntermediates(ctx, "fuzz", hostOrTargetString, archString)
+		archOs := fuzz.ArchOs{HostOrTarget: hostOrTargetString, Arch: archString, Dir: archDir.String()}
+
+		var files []fuzz.FileToZip
+		builder := android.NewRuleBuilder(pctx, ctx)
+
+		// Package the artifacts (data, corpus, config and dictionary into a zipfile.
+		files = s.PackageArtifacts(ctx, module, javaModule.fuzzPackagedModule, archDir, builder)
+
+		// Add .jar
+		files = append(files, fuzz.FileToZip{javaModule.outputFile, ""})
+
+		archDirs[archOs], ok = s.BuildZipFile(ctx, module, javaModule.fuzzPackagedModule, files, builder, archDir, archString, "host", archOs, archDirs)
+		if !ok {
+			return
+		}
+
+	})
+	s.CreateFuzzPackage(ctx, archDirs, fuzz.Java, pctx)
+}
+
+func (s *javaFuzzPackager) MakeVars(ctx android.MakeVarsContext) {
+	packages := s.Packages.Strings()
+	sort.Strings(packages)
+
+	ctx.Strict("SOONG_JAVA_FUZZ_PACKAGING_ARCH_MODULES", strings.Join(packages, " "))
+
+	// Preallocate the slice of fuzz targets to minimize memory allocations.
+	s.PreallocateSlice(ctx, "ALL_JAVA_FUZZ_TARGETS")
+}
diff --git a/java/fuzz_test.go b/java/fuzz_test.go
new file mode 100644
index 0000000..cf063eb
--- /dev/null
+++ b/java/fuzz_test.go
@@ -0,0 +1,65 @@
+// Copyright 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+	"android/soong/android"
+	"path/filepath"
+	"testing"
+)
+
+var prepForJavaFuzzTest = android.GroupFixturePreparers(
+	PrepareForTestWithJavaDefaultModules,
+	android.FixtureRegisterWithContext(RegisterJavaFuzzBuildComponents),
+)
+
+func TestJavaFuzz(t *testing.T) {
+	result := prepForJavaFuzzTest.RunTestWithBp(t, `
+		java_fuzz_host {
+			name: "foo",
+			srcs: ["a.java"],
+			libs: ["bar"],
+			static_libs: ["baz"],
+		}
+
+		java_library_host {
+			name: "bar",
+			srcs: ["b.java"],
+		}
+
+		java_library_host {
+			name: "baz",
+			srcs: ["c.java"],
+		}`)
+
+	osCommonTarget := result.Config.BuildOSCommonTarget.String()
+	javac := result.ModuleForTests("foo", osCommonTarget).Rule("javac")
+	combineJar := result.ModuleForTests("foo", osCommonTarget).Description("for javac")
+
+	if len(javac.Inputs) != 1 || javac.Inputs[0].String() != "a.java" {
+		t.Errorf(`foo inputs %v != ["a.java"]`, javac.Inputs)
+	}
+
+	baz := result.ModuleForTests("baz", osCommonTarget).Rule("javac").Output.String()
+	barOut := filepath.Join("out", "soong", ".intermediates", "bar", osCommonTarget, "javac", "bar.jar")
+	bazOut := filepath.Join("out", "soong", ".intermediates", "baz", osCommonTarget, "javac", "baz.jar")
+
+	android.AssertStringDoesContain(t, "foo classpath", javac.Args["classpath"], barOut)
+	android.AssertStringDoesContain(t, "foo classpath", javac.Args["classpath"], bazOut)
+
+	if len(combineJar.Inputs) != 2 || combineJar.Inputs[1].String() != baz {
+		t.Errorf("foo combineJar inputs %v does not contain %q", combineJar.Inputs, baz)
+	}
+}
diff --git a/java/java.go b/java/java.go
index 9b4a005..bb7c32b 100644
--- a/java/java.go
+++ b/java/java.go
@@ -24,6 +24,7 @@
 	"strings"
 
 	"android/soong/bazel"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 
@@ -1866,6 +1867,7 @@
 	module.AddProperties(
 		&CommonProperties{},
 		&DeviceProperties{},
+		&OverridableDeviceProperties{},
 		&DexProperties{},
 		&DexpreoptProperties{},
 		&android.ProtoProperties{},
@@ -2000,6 +2002,7 @@
 	Deps       bazel.LabelListAttribute
 	Main_class string
 	Jvm_flags  bazel.StringListAttribute
+	Javacopts  bazel.StringListAttribute
 }
 
 // JavaBinaryHostBp2Build is for java_binary_host bp2build.
@@ -2021,6 +2024,10 @@
 		Main_class: mainClass,
 	}
 
+	if m.properties.Javacflags != nil {
+		attrs.Javacopts = bazel.MakeStringListAttribute(m.properties.Javacflags)
+	}
+
 	// Attribute deps
 	deps := []string{}
 	if m.properties.Static_libs != nil {
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 0bc8895..7849f96 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -2579,11 +2579,11 @@
 	implicitUntilAttr := formattedOptionalSdkLevelAttribute(ctx, "on-bootclasspath-before", module.properties.On_bootclasspath_before)
 	minSdkAttr := formattedOptionalSdkLevelAttribute(ctx, "min-device-sdk", module.properties.Min_device_sdk)
 	maxSdkAttr := formattedOptionalSdkLevelAttribute(ctx, "max-device-sdk", module.properties.Max_device_sdk)
-	// <library> is understood in all android versions whereas <updatable-library> is only understood from API T (and ignored before that).
-	// similarly, min_device_sdk is only understood from T. So if a library is using that, we need to use the updatable-library to make sure this library is not loaded before T
+	// <library> is understood in all android versions whereas <apex-library> is only understood from API T (and ignored before that).
+	// similarly, min_device_sdk is only understood from T. So if a library is using that, we need to use the apex-library to make sure this library is not loaded before T
 	var libraryTag string
 	if module.properties.Min_device_sdk != nil {
-		libraryTag = `    <updatable-library\n`
+		libraryTag = `    <apex-library\n`
 	} else {
 		libraryTag = `    <library\n`
 	}
diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go
index f3a19e9..e0e5b56 100644
--- a/java/sdk_library_test.go
+++ b/java/sdk_library_test.go
@@ -48,6 +48,7 @@
 			name: "bar",
 			srcs: ["a.java", "b.java"],
 			api_packages: ["bar"],
+			exclude_kotlinc_generated_files: true,
 		}
 		java_library {
 			name: "baz",
@@ -161,6 +162,14 @@
 		android.AssertDeepEquals(t, "qux exports (required)", []string{"fred", "quuz", "foo", "bar"}, requiredSdkLibs)
 		android.AssertDeepEquals(t, "qux exports (optional)", []string{}, optionalSdkLibs)
 	}
+
+	fooDexJar := result.ModuleForTests("foo", "android_common").Rule("d8")
+	// tests if kotlinc generated files are NOT excluded from output of foo.
+	android.AssertStringDoesNotContain(t, "foo dex", fooDexJar.BuildParams.Args["mergeZipsFlags"], "-stripFile META-INF/*.kotlin_module")
+
+	barDexJar := result.ModuleForTests("bar", "android_common").Rule("d8")
+	// tests if kotlinc generated files are excluded from output of bar.
+	android.AssertStringDoesContain(t, "bar dex", barDexJar.BuildParams.Args["mergeZipsFlags"], "-stripFile META-INF/*.kotlin_module")
 }
 
 func TestJavaSdkLibrary_UpdatableLibrary(t *testing.T) {
@@ -338,7 +347,7 @@
 `)
 	// test that updatability attributes are passed on correctly
 	fooUpdatable := result.ModuleForTests("foo.xml", "android_common").Rule("java_sdk_xml")
-	android.AssertStringDoesContain(t, "foo.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `<updatable-library`)
+	android.AssertStringDoesContain(t, "foo.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `<apex-library`)
 	android.AssertStringDoesNotContain(t, "foo.xml java_sdk_xml command", fooUpdatable.RuleParams.Command, `<library`)
 }
 
diff --git a/mk2rbc/expr.go b/mk2rbc/expr.go
index 7cd4899..3f355ac 100644
--- a/mk2rbc/expr.go
+++ b/mk2rbc/expr.go
@@ -119,6 +119,29 @@
 	}
 }
 
+type globalsExpr struct {
+}
+
+func (g *globalsExpr) emit(gctx *generationContext) {
+	gctx.write("g")
+}
+
+func (g *globalsExpr) typ() starlarkType {
+	return starlarkTypeUnknown
+}
+
+func (g *globalsExpr) emitListVarCopy(gctx *generationContext) {
+	g.emit(gctx)
+}
+
+func (g *globalsExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
+	if replacement := transformer(g); replacement != nil {
+		return replacement
+	} else {
+		return g
+	}
+}
+
 // interpolateExpr represents Starlark's interpolation operator <string> % list
 // we break <string> into a list of chunks, i.e., "first%second%third" % (X, Y)
 // will have chunks = ["first", "second", "third"] and args = [X, Y]
@@ -322,35 +345,6 @@
 }
 
 func (eq *eqExpr) emit(gctx *generationContext) {
-	var stringOperand string
-	var otherOperand starlarkExpr
-	if s, ok := maybeString(eq.left); ok {
-		stringOperand = s
-		otherOperand = eq.right
-	} else if s, ok := maybeString(eq.right); ok {
-		stringOperand = s
-		otherOperand = eq.left
-	}
-
-	// If we've identified one of the operands as being a string literal, check
-	// for some special cases we can do to simplify the resulting expression.
-	if otherOperand != nil {
-		if stringOperand == "" {
-			if eq.isEq {
-				gctx.write("not ")
-			}
-			otherOperand.emit(gctx)
-			return
-		}
-		if stringOperand == "true" && otherOperand.typ() == starlarkTypeBool {
-			if !eq.isEq {
-				gctx.write("not ")
-			}
-			otherOperand.emit(gctx)
-			return
-		}
-	}
-
 	if eq.left.typ() != eq.right.typ() {
 		eq.left = &toStringExpr{expr: eq.left}
 		eq.right = &toStringExpr{expr: eq.right}
@@ -594,29 +588,15 @@
 }
 
 func (cx *callExpr) emit(gctx *generationContext) {
-	sep := ""
 	if cx.object != nil {
 		gctx.write("(")
 		cx.object.emit(gctx)
 		gctx.write(")")
 		gctx.write(".", cx.name, "(")
 	} else {
-		kf, found := knownFunctions[cx.name]
-		if !found {
-			panic(fmt.Errorf("callExpr with unknown function %q", cx.name))
-		}
-		if kf.runtimeName[0] == '!' {
-			panic(fmt.Errorf("callExpr for %q should not be there", cx.name))
-		}
-		gctx.write(kf.runtimeName, "(")
-		if kf.hiddenArg == hiddenArgGlobal {
-			gctx.write("g")
-			sep = ", "
-		} else if kf.hiddenArg == hiddenArgConfig {
-			gctx.write("cfg")
-			sep = ", "
-		}
+		gctx.write(cx.name, "(")
 	}
+	sep := ""
 	for _, arg := range cx.args {
 		gctx.write(sep)
 		arg.emit(gctx)
@@ -748,6 +728,36 @@
 	}
 }
 
+type binaryOpExpr struct {
+	left, right starlarkExpr
+	op          string
+	returnType  starlarkType
+}
+
+func (b *binaryOpExpr) emit(gctx *generationContext) {
+	b.left.emit(gctx)
+	gctx.write(" " + b.op + " ")
+	b.right.emit(gctx)
+}
+
+func (b *binaryOpExpr) typ() starlarkType {
+	return b.returnType
+}
+
+func (b *binaryOpExpr) emitListVarCopy(gctx *generationContext) {
+	b.emit(gctx)
+}
+
+func (b *binaryOpExpr) transform(transformer func(expr starlarkExpr) starlarkExpr) starlarkExpr {
+	b.left = b.left.transform(transformer)
+	b.right = b.right.transform(transformer)
+	if replacement := transformer(b); replacement != nil {
+		return replacement
+	} else {
+		return b
+	}
+}
+
 type badExpr struct {
 	errorLocation ErrorLocation
 	message       string
diff --git a/mk2rbc/mk2rbc.go b/mk2rbc/mk2rbc.go
index 2a80e56..14988e7 100644
--- a/mk2rbc/mk2rbc.go
+++ b/mk2rbc/mk2rbc.go
@@ -54,7 +54,6 @@
 	cfnMain            = baseName + ".product_configuration"
 	cfnBoardMain       = baseName + ".board_configuration"
 	cfnPrintVars       = baseName + ".printvars"
-	cfnPrintGlobals    = baseName + ".printglobals"
 	cfnWarning         = baseName + ".warning"
 	cfnLocalAppend     = baseName + ".local_append"
 	cfnLocalSetDefault = baseName + ".local_set_default"
@@ -63,92 +62,78 @@
 )
 
 const (
-	// Phony makefile functions, they are eventually rewritten
-	// according to knownFunctions map
-	fileExistsPhony = "$file_exists"
-	// The following two macros are obsolete, and will we deleted once
-	// there are deleted from the makefiles:
-	soongConfigNamespaceOld = "add_soong_config_namespace"
-	soongConfigVarSetOld    = "add_soong_config_var_value"
-	soongConfigAppend       = "soong_config_append"
-	soongConfigAssign       = "soong_config_set"
-	soongConfigGet          = "soong_config_get"
-	wildcardExistsPhony     = "$wildcard_exists"
+	soongConfigAppend = "soong_config_append"
+	soongConfigAssign = "soong_config_set"
 )
 
-const (
-	callLoadAlways = "inherit-product"
-	callLoadIf     = "inherit-product-if-exists"
-)
-
-var knownFunctions = map[string]struct {
-	// The name of the runtime function this function call in makefiles maps to.
-	// If it starts with !, then this makefile function call is rewritten to
-	// something else.
-	runtimeName string
-	returnType  starlarkType
-	hiddenArg   hiddenArgType
+var knownFunctions = map[string]interface {
+	parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr
 }{
-	"abspath":                             {baseName + ".abspath", starlarkTypeString, hiddenArgNone},
-	fileExistsPhony:                       {baseName + ".file_exists", starlarkTypeBool, hiddenArgNone},
-	wildcardExistsPhony:                   {baseName + ".file_wildcard_exists", starlarkTypeBool, hiddenArgNone},
-	soongConfigNamespaceOld:               {baseName + ".soong_config_namespace", starlarkTypeVoid, hiddenArgGlobal},
-	soongConfigVarSetOld:                  {baseName + ".soong_config_set", starlarkTypeVoid, hiddenArgGlobal},
-	soongConfigAssign:                     {baseName + ".soong_config_set", starlarkTypeVoid, hiddenArgGlobal},
-	soongConfigAppend:                     {baseName + ".soong_config_append", starlarkTypeVoid, hiddenArgGlobal},
-	soongConfigGet:                        {baseName + ".soong_config_get", starlarkTypeString, hiddenArgGlobal},
-	"add-to-product-copy-files-if-exists": {baseName + ".copy_if_exists", starlarkTypeList, hiddenArgNone},
-	"addprefix":                           {baseName + ".addprefix", starlarkTypeList, hiddenArgNone},
-	"addsuffix":                           {baseName + ".addsuffix", starlarkTypeList, hiddenArgNone},
-	"copy-files":                          {baseName + ".copy_files", starlarkTypeList, hiddenArgNone},
-	"dir":                                 {baseName + ".dir", starlarkTypeList, hiddenArgNone},
-	"dist-for-goals":                      {baseName + ".mkdist_for_goals", starlarkTypeVoid, hiddenArgGlobal},
-	"enforce-product-packages-exist":      {baseName + ".enforce_product_packages_exist", starlarkTypeVoid, hiddenArgNone},
-	"error":                               {baseName + ".mkerror", starlarkTypeVoid, hiddenArgNone},
-	"findstring":                          {baseName + ".findstring", starlarkTypeString, hiddenArgNone},
-	"find-copy-subdir-files":              {baseName + ".find_and_copy", starlarkTypeList, hiddenArgNone},
-	"find-word-in-list":                   {"!find-word-in-list", starlarkTypeUnknown, hiddenArgNone}, // internal macro
-	"filter":                              {baseName + ".filter", starlarkTypeList, hiddenArgNone},
-	"filter-out":                          {baseName + ".filter_out", starlarkTypeList, hiddenArgNone},
-	"firstword":                           {"!firstword", starlarkTypeString, hiddenArgNone},
-	"foreach":                             {"!foreach", starlarkTypeList, hiddenArgNone},
-	"get-vendor-board-platforms":          {"!get-vendor-board-platforms", starlarkTypeList, hiddenArgNone}, // internal macro, used by is-board-platform, etc.
-	"if":                                  {"!if", starlarkTypeUnknown, hiddenArgNone},
-	"info":                                {baseName + ".mkinfo", starlarkTypeVoid, hiddenArgNone},
-	"is-android-codename":                 {"!is-android-codename", starlarkTypeBool, hiddenArgNone},         // unused by product config
-	"is-android-codename-in-list":         {"!is-android-codename-in-list", starlarkTypeBool, hiddenArgNone}, // unused by product config
-	"is-board-platform":                   {"!is-board-platform", starlarkTypeBool, hiddenArgNone},
-	"is-board-platform2":                  {baseName + ".board_platform_is", starlarkTypeBool, hiddenArgGlobal},
-	"is-board-platform-in-list":           {"!is-board-platform-in-list", starlarkTypeBool, hiddenArgNone},
-	"is-board-platform-in-list2":          {baseName + ".board_platform_in", starlarkTypeBool, hiddenArgGlobal},
-	"is-chipset-in-board-platform":        {"!is-chipset-in-board-platform", starlarkTypeUnknown, hiddenArgNone},     // unused by product config
-	"is-chipset-prefix-in-board-platform": {"!is-chipset-prefix-in-board-platform", starlarkTypeBool, hiddenArgNone}, // unused by product config
-	"is-not-board-platform":               {"!is-not-board-platform", starlarkTypeBool, hiddenArgNone},               // defined but never used
-	"is-platform-sdk-version-at-least":    {"!is-platform-sdk-version-at-least", starlarkTypeBool, hiddenArgNone},    // unused by product config
-	"is-product-in-list":                  {"!is-product-in-list", starlarkTypeBool, hiddenArgNone},
-	"is-vendor-board-platform":            {"!is-vendor-board-platform", starlarkTypeBool, hiddenArgNone},
-	"is-vendor-board-qcom":                {"!is-vendor-board-qcom", starlarkTypeBool, hiddenArgNone},
-	callLoadAlways:                        {"!inherit-product", starlarkTypeVoid, hiddenArgNone},
-	callLoadIf:                            {"!inherit-product-if-exists", starlarkTypeVoid, hiddenArgNone},
-	"lastword":                            {"!lastword", starlarkTypeString, hiddenArgNone},
-	"match-prefix":                        {"!match-prefix", starlarkTypeUnknown, hiddenArgNone},       // internal macro
-	"match-word":                          {"!match-word", starlarkTypeUnknown, hiddenArgNone},         // internal macro
-	"match-word-in-list":                  {"!match-word-in-list", starlarkTypeUnknown, hiddenArgNone}, // internal macro
-	"notdir":                              {baseName + ".notdir", starlarkTypeString, hiddenArgNone},
-	"my-dir":                              {"!my-dir", starlarkTypeString, hiddenArgNone},
-	"patsubst":                            {baseName + ".mkpatsubst", starlarkTypeString, hiddenArgNone},
-	"product-copy-files-by-pattern":       {baseName + ".product_copy_files_by_pattern", starlarkTypeList, hiddenArgNone},
-	"require-artifacts-in-path":           {baseName + ".require_artifacts_in_path", starlarkTypeVoid, hiddenArgNone},
-	"require-artifacts-in-path-relaxed":   {baseName + ".require_artifacts_in_path_relaxed", starlarkTypeVoid, hiddenArgNone},
+	"abspath":                             &simpleCallParser{name: baseName + ".abspath", returnType: starlarkTypeString, addGlobals: false},
+	"add_soong_config_namespace":          &simpleCallParser{name: baseName + ".soong_config_namespace", returnType: starlarkTypeVoid, addGlobals: true},
+	"add_soong_config_var_value":          &simpleCallParser{name: baseName + ".soong_config_set", returnType: starlarkTypeVoid, addGlobals: true},
+	soongConfigAssign:                     &simpleCallParser{name: baseName + ".soong_config_set", returnType: starlarkTypeVoid, addGlobals: true},
+	soongConfigAppend:                     &simpleCallParser{name: baseName + ".soong_config_append", returnType: starlarkTypeVoid, addGlobals: true},
+	"soong_config_get":                    &simpleCallParser{name: baseName + ".soong_config_get", returnType: starlarkTypeString, addGlobals: true},
+	"add-to-product-copy-files-if-exists": &simpleCallParser{name: baseName + ".copy_if_exists", returnType: starlarkTypeList, addGlobals: false},
+	"addprefix":                           &simpleCallParser{name: baseName + ".addprefix", returnType: starlarkTypeList, addGlobals: false},
+	"addsuffix":                           &simpleCallParser{name: baseName + ".addsuffix", returnType: starlarkTypeList, addGlobals: false},
+	"copy-files":                          &simpleCallParser{name: baseName + ".copy_files", returnType: starlarkTypeList, addGlobals: false},
+	"dir":                                 &simpleCallParser{name: baseName + ".dir", returnType: starlarkTypeList, addGlobals: false},
+	"dist-for-goals":                      &simpleCallParser{name: baseName + ".mkdist_for_goals", returnType: starlarkTypeVoid, addGlobals: true},
+	"enforce-product-packages-exist":      &simpleCallParser{name: baseName + ".enforce_product_packages_exist", returnType: starlarkTypeVoid, addGlobals: false},
+	"error":                               &makeControlFuncParser{name: baseName + ".mkerror"},
+	"findstring":                          &simpleCallParser{name: baseName + ".findstring", returnType: starlarkTypeInt, addGlobals: false},
+	"find-copy-subdir-files":              &simpleCallParser{name: baseName + ".find_and_copy", returnType: starlarkTypeList, addGlobals: false},
+	"filter":                              &simpleCallParser{name: baseName + ".filter", returnType: starlarkTypeList, addGlobals: false},
+	"filter-out":                          &simpleCallParser{name: baseName + ".filter_out", returnType: starlarkTypeList, addGlobals: false},
+	"firstword":                           &firstOrLastwordCallParser{isLastWord: false},
+	"foreach":                             &foreachCallPaser{},
+	"if":                                  &ifCallParser{},
+	"info":                                &makeControlFuncParser{name: baseName + ".mkinfo"},
+	"is-board-platform":                   &simpleCallParser{name: baseName + ".board_platform_is", returnType: starlarkTypeBool, addGlobals: true},
+	"is-board-platform2":                  &simpleCallParser{name: baseName + ".board_platform_is", returnType: starlarkTypeBool, addGlobals: true},
+	"is-board-platform-in-list":           &simpleCallParser{name: baseName + ".board_platform_in", returnType: starlarkTypeBool, addGlobals: true},
+	"is-board-platform-in-list2":          &simpleCallParser{name: baseName + ".board_platform_in", returnType: starlarkTypeBool, addGlobals: true},
+	"is-product-in-list":                  &isProductInListCallParser{},
+	"is-vendor-board-platform":            &isVendorBoardPlatformCallParser{},
+	"is-vendor-board-qcom":                &isVendorBoardQcomCallParser{},
+	"lastword":                            &firstOrLastwordCallParser{isLastWord: true},
+	"notdir":                              &simpleCallParser{name: baseName + ".notdir", returnType: starlarkTypeString, addGlobals: false},
+	"math_max":                            &mathMaxOrMinCallParser{function: "max"},
+	"math_min":                            &mathMaxOrMinCallParser{function: "min"},
+	"math_gt_or_eq":                       &mathComparisonCallParser{op: ">="},
+	"math_gt":                             &mathComparisonCallParser{op: ">"},
+	"math_lt":                             &mathComparisonCallParser{op: "<"},
+	"my-dir":                              &myDirCallParser{},
+	"patsubst":                            &substCallParser{fname: "patsubst"},
+	"product-copy-files-by-pattern":       &simpleCallParser{name: baseName + ".product_copy_files_by_pattern", returnType: starlarkTypeList, addGlobals: false},
+	"require-artifacts-in-path":           &simpleCallParser{name: baseName + ".require_artifacts_in_path", returnType: starlarkTypeVoid, addGlobals: false},
+	"require-artifacts-in-path-relaxed":   &simpleCallParser{name: baseName + ".require_artifacts_in_path_relaxed", returnType: starlarkTypeVoid, addGlobals: false},
 	// TODO(asmundak): remove it once all calls are removed from configuration makefiles. see b/183161002
-	"shell":      {baseName + ".shell", starlarkTypeString, hiddenArgNone},
-	"strip":      {baseName + ".mkstrip", starlarkTypeString, hiddenArgNone},
-	"tb-modules": {"!tb-modules", starlarkTypeUnknown, hiddenArgNone}, // defined in hardware/amlogic/tb_modules/tb_detect.mk, unused
-	"subst":      {baseName + ".mksubst", starlarkTypeString, hiddenArgNone},
-	"warning":    {baseName + ".mkwarning", starlarkTypeVoid, hiddenArgNone},
-	"word":       {baseName + "!word", starlarkTypeString, hiddenArgNone},
-	"wildcard":   {baseName + ".expand_wildcard", starlarkTypeList, hiddenArgNone},
-	"words":      {baseName + ".words", starlarkTypeList, hiddenArgNone},
+	"shell":    &shellCallParser{},
+	"strip":    &simpleCallParser{name: baseName + ".mkstrip", returnType: starlarkTypeString, addGlobals: false},
+	"subst":    &substCallParser{fname: "subst"},
+	"warning":  &makeControlFuncParser{name: baseName + ".mkwarning"},
+	"word":     &wordCallParser{},
+	"wildcard": &simpleCallParser{name: baseName + ".expand_wildcard", returnType: starlarkTypeList, addGlobals: false},
+}
+
+// These are functions that we don't implement conversions for, but
+// we allow seeing their definitions in the product config files.
+var ignoredDefines = map[string]bool{
+	"find-word-in-list":                   true, // internal macro
+	"get-vendor-board-platforms":          true, // internal macro, used by is-board-platform, etc.
+	"is-android-codename":                 true, // unused by product config
+	"is-android-codename-in-list":         true, // unused by product config
+	"is-chipset-in-board-platform":        true, // unused by product config
+	"is-chipset-prefix-in-board-platform": true, // unused by product config
+	"is-not-board-platform":               true, // defined but never used
+	"is-platform-sdk-version-at-least":    true, // unused by product config
+	"match-prefix":                        true, // internal macro
+	"match-word":                          true, // internal macro
+	"match-word-in-list":                  true, // internal macro
+	"tb-modules":                          true, // defined in hardware/amlogic/tb_modules/tb_detect.mk, unused
 }
 
 var identifierFullMatchRegex = regexp.MustCompile("^[a-zA-Z_][a-zA-Z0-9_]*$")
@@ -466,7 +451,7 @@
 		variables:        make(map[string]variable),
 		dependentModules: make(map[string]*moduleInfo),
 		soongNamespaces:  make(map[string]map[string]bool),
-		includeTops:      []string{"vendor/google-devices"},
+		includeTops:      []string{},
 	}
 	ctx.pushVarAssignments()
 	for _, item := range predefined {
@@ -641,8 +626,8 @@
 		for _, ns := range strings.Fields(s) {
 			ctx.addSoongNamespace(ns)
 			ctx.receiver.newNode(&exprNode{&callExpr{
-				name:       soongConfigNamespaceOld,
-				args:       []starlarkExpr{&stringLiteralExpr{ns}},
+				name:       baseName + ".soong_config_namespace",
+				args:       []starlarkExpr{&globalsExpr{}, &stringLiteralExpr{ns}},
 				returnType: starlarkTypeVoid,
 			}})
 		}
@@ -691,13 +676,13 @@
 			ctx.errorf(asgn, "no %s variable in %s namespace, please use add_soong_config_var_value instead", varName, namespaceName)
 			return
 		}
-		fname := soongConfigAssign
+		fname := baseName + "." + soongConfigAssign
 		if asgn.Type == "+=" {
-			fname = soongConfigAppend
+			fname = baseName + "." + soongConfigAppend
 		}
 		ctx.receiver.newNode(&exprNode{&callExpr{
 			name:       fname,
-			args:       []starlarkExpr{&stringLiteralExpr{namespaceName}, &stringLiteralExpr{varName}, val},
+			args:       []starlarkExpr{&globalsExpr{}, &stringLiteralExpr{namespaceName}, &stringLiteralExpr{varName}, val},
 			returnType: starlarkTypeVoid,
 		}})
 	}
@@ -829,6 +814,10 @@
 		}
 	}
 	if pathPattern[0] == "" {
+		if len(ctx.includeTops) == 0 {
+			ctx.errorf(v, "inherit-product/include statements must not be prefixed with a variable, or must include a #RBC# include_top comment beforehand giving a root directory to search.")
+			return
+		}
 		// If pattern starts from the top. restrict it to the directories where
 		// we know inherit-product uses dynamically calculated path.
 		for _, p := range ctx.includeTops {
@@ -878,7 +867,13 @@
 	return res
 }
 
-func (ctx *parseContext) handleInheritModule(v mkparser.Node, pathExpr starlarkExpr, loadAlways bool) {
+func (ctx *parseContext) handleInheritModule(v mkparser.Node, args *mkparser.MakeString, loadAlways bool) {
+	args.TrimLeftSpaces()
+	args.TrimRightSpaces()
+	pathExpr := ctx.parseMakeString(v, args)
+	if _, ok := pathExpr.(*badExpr); ok {
+		ctx.errorf(v, "Unable to parse argument to inherit")
+	}
 	ctx.handleSubConfig(v, pathExpr, loadAlways, func(im inheritedModule) {
 		ctx.receiver.newNode(&inheritNode{im, loadAlways})
 	})
@@ -897,36 +892,40 @@
 	//   $(info xxx)
 	//   $(warning xxx)
 	//   $(error xxx)
+	//   $(call other-custom-functions,...)
+
+	// inherit-product(-if-exists) gets converted to a series of statements,
+	// not just a single expression like parseReference returns. So handle it
+	// separately at the beginning here.
+	if strings.HasPrefix(v.Name.Dump(), "call inherit-product,") {
+		args := v.Name.Clone()
+		args.ReplaceLiteral("call inherit-product,", "")
+		ctx.handleInheritModule(v, args, true)
+		return
+	}
+	if strings.HasPrefix(v.Name.Dump(), "call inherit-product-if-exists,") {
+		args := v.Name.Clone()
+		args.ReplaceLiteral("call inherit-product-if-exists,", "")
+		ctx.handleInheritModule(v, args, false)
+		return
+	}
 	expr := ctx.parseReference(v, v.Name)
 	switch x := expr.(type) {
 	case *callExpr:
-		if x.name == callLoadAlways || x.name == callLoadIf {
-			ctx.handleInheritModule(v, x.args[0], x.name == callLoadAlways)
-		} else if isMakeControlFunc(x.name) {
-			// File name is the first argument
-			args := []starlarkExpr{
-				&stringLiteralExpr{ctx.script.mkFile},
-				x.args[0],
-			}
-			ctx.receiver.newNode(&exprNode{
-				&callExpr{name: x.name, args: args, returnType: starlarkTypeUnknown},
-			})
-		} else {
-			ctx.receiver.newNode(&exprNode{expr})
-		}
+		ctx.receiver.newNode(&exprNode{expr})
 	case *badExpr:
 		ctx.wrapBadExpr(x)
-		return
 	default:
 		ctx.errorf(v, "cannot handle %s", v.Dump())
-		return
 	}
 }
 
 func (ctx *parseContext) handleDefine(directive *mkparser.Directive) {
 	macro_name := strings.Fields(directive.Args.Strings[0])[0]
 	// Ignore the macros that we handle
-	if _, ok := knownFunctions[macro_name]; !ok {
+	_, ignored := ignoredDefines[macro_name]
+	_, known := knownFunctions[macro_name]
+	if !ignored && !known {
 		ctx.errorf(directive, "define is not supported: %s", macro_name)
 	}
 }
@@ -1056,6 +1055,72 @@
 		return expr
 	}
 
+	var stringOperand string
+	var otherOperand starlarkExpr
+	if s, ok := maybeString(xLeft); ok {
+		stringOperand = s
+		otherOperand = xRight
+	} else if s, ok := maybeString(xRight); ok {
+		stringOperand = s
+		otherOperand = xLeft
+	}
+
+	not := func(expr starlarkExpr) starlarkExpr {
+		switch typedExpr := expr.(type) {
+		case *inExpr:
+			typedExpr.isNot = !typedExpr.isNot
+			return typedExpr
+		case *eqExpr:
+			typedExpr.isEq = !typedExpr.isEq
+			return typedExpr
+		case *binaryOpExpr:
+			switch typedExpr.op {
+			case ">":
+				typedExpr.op = "<="
+				return typedExpr
+			case "<":
+				typedExpr.op = ">="
+				return typedExpr
+			case ">=":
+				typedExpr.op = "<"
+				return typedExpr
+			case "<=":
+				typedExpr.op = ">"
+				return typedExpr
+			default:
+				return &notExpr{expr: expr}
+			}
+		default:
+			return &notExpr{expr: expr}
+		}
+	}
+
+	// If we've identified one of the operands as being a string literal, check
+	// for some special cases we can do to simplify the resulting expression.
+	if otherOperand != nil {
+		if stringOperand == "" {
+			if isEq {
+				return not(otherOperand)
+			} else {
+				return otherOperand
+			}
+		}
+		if stringOperand == "true" && otherOperand.typ() == starlarkTypeBool {
+			if !isEq {
+				return not(otherOperand)
+			} else {
+				return otherOperand
+			}
+		}
+		if intOperand, err := strconv.Atoi(strings.TrimSpace(stringOperand)); err == nil && otherOperand.typ() == starlarkTypeInt {
+			return &eqExpr{
+				left:  otherOperand,
+				right: &intLiteralExpr{literal: intOperand},
+				isEq:  isEq,
+			}
+		}
+	}
+
 	return &eqExpr{left: xLeft, right: xRight, isEq: isEq}
 }
 
@@ -1089,97 +1154,15 @@
 		return nil, false
 	}
 
-	checkIsSomethingFunction := func(xCall *callExpr) starlarkExpr {
-		s, ok := maybeString(value)
-		if !ok || s != "true" {
-			return ctx.newBadExpr(directive,
-				fmt.Sprintf("the result of %s can be compared only to 'true'", xCall.name))
-		}
-		if len(xCall.args) < 1 {
-			return ctx.newBadExpr(directive, "%s requires an argument", xCall.name)
-		}
-		return nil
-	}
-
 	switch call.name {
-	case "filter", "filter-out":
+	case baseName + ".filter", baseName + ".filter-out":
 		return ctx.parseCompareFilterFuncResult(directive, call, value, isEq), true
-	case "wildcard":
+	case baseName + ".expand_wildcard":
 		return ctx.parseCompareWildcardFuncResult(directive, call, value, !isEq), true
-	case "findstring":
+	case baseName + ".findstring":
 		return ctx.parseCheckFindstringFuncResult(directive, call, value, !isEq), true
-	case "strip":
+	case baseName + ".strip":
 		return ctx.parseCompareStripFuncResult(directive, call, value, !isEq), true
-	case "is-board-platform":
-		if xBad := checkIsSomethingFunction(call); xBad != nil {
-			return xBad, true
-		}
-		return &eqExpr{
-			left:  NewVariableRefExpr(ctx.addVariable("TARGET_BOARD_PLATFORM"), false),
-			right: call.args[0],
-			isEq:  isEq,
-		}, true
-	case "is-board-platform-in-list":
-		if xBad := checkIsSomethingFunction(call); xBad != nil {
-			return xBad, true
-		}
-		return &inExpr{
-			expr:  NewVariableRefExpr(ctx.addVariable("TARGET_BOARD_PLATFORM"), false),
-			list:  maybeConvertToStringList(call.args[0]),
-			isNot: !isEq,
-		}, true
-	case "is-product-in-list":
-		if xBad := checkIsSomethingFunction(call); xBad != nil {
-			return xBad, true
-		}
-		return &inExpr{
-			expr:  NewVariableRefExpr(ctx.addVariable("TARGET_PRODUCT"), true),
-			list:  maybeConvertToStringList(call.args[0]),
-			isNot: !isEq,
-		}, true
-	case "is-vendor-board-platform":
-		if xBad := checkIsSomethingFunction(call); xBad != nil {
-			return xBad, true
-		}
-		s, ok := maybeString(call.args[0])
-		if !ok {
-			return ctx.newBadExpr(directive, "cannot handle non-constant argument to is-vendor-board-platform"), true
-		}
-		return &inExpr{
-			expr:  NewVariableRefExpr(ctx.addVariable("TARGET_BOARD_PLATFORM"), false),
-			list:  NewVariableRefExpr(ctx.addVariable(s+"_BOARD_PLATFORMS"), true),
-			isNot: !isEq,
-		}, true
-
-	case "is-board-platform2", "is-board-platform-in-list2":
-		if s, ok := maybeString(value); !ok || s != "" {
-			return ctx.newBadExpr(directive,
-				fmt.Sprintf("the result of %s can be compared only to empty", call.name)), true
-		}
-		if len(call.args) != 1 {
-			return ctx.newBadExpr(directive, "%s requires an argument", call.name), true
-		}
-		cc := &callExpr{
-			name:       call.name,
-			args:       []starlarkExpr{call.args[0]},
-			returnType: starlarkTypeBool,
-		}
-		if isEq {
-			return &notExpr{cc}, true
-		}
-		return cc, true
-	case "is-vendor-board-qcom":
-		if s, ok := maybeString(value); !ok || s != "" {
-			return ctx.newBadExpr(directive,
-				fmt.Sprintf("the result of %s can be compared only to empty", call.name)), true
-		}
-		// if the expression is ifneq (,$(call is-vendor-board-platform,...)), negate==true,
-		// so we should set inExpr.isNot to false
-		return &inExpr{
-			expr:  NewVariableRefExpr(ctx.addVariable("TARGET_BOARD_PLATFORM"), false),
-			list:  NewVariableRefExpr(ctx.addVariable("QCOM_BOARD_PLATFORMS"), true),
-			isNot: isEq,
-		}, true
 	}
 	return nil, false
 }
@@ -1254,9 +1237,9 @@
 	if !isEmptyString(xValue) {
 		return ctx.newBadExpr(directive, "wildcard result can be compared only to empty: %s", xValue)
 	}
-	callFunc := wildcardExistsPhony
+	callFunc := baseName + ".file_wildcard_exists"
 	if s, ok := xCall.args[0].(*stringLiteralExpr); ok && !strings.ContainsAny(s.literal, "*?{[") {
-		callFunc = fileExistsPhony
+		callFunc = baseName + ".file_exists"
 	}
 	var cc starlarkExpr = &callExpr{name: callFunc, args: xCall.args, returnType: starlarkTypeBool}
 	if !negate {
@@ -1323,14 +1306,7 @@
 
 	// If it is a single word, it can be a simple variable
 	// reference or a function call
-	if len(words) == 1 {
-		if isMakeControlFunc(refDump) || refDump == "shell" {
-			return &callExpr{
-				name:       refDump,
-				args:       []starlarkExpr{&stringLiteralExpr{""}},
-				returnType: starlarkTypeUnknown,
-			}
-		}
+	if len(words) == 1 && !isMakeControlFunc(refDump) && refDump != "shell" {
 		if strings.HasPrefix(refDump, soongNsPrefix) {
 			// TODO (asmundak): if we find many, maybe handle them.
 			return ctx.newBadExpr(node, "SOONG_CONFIG_ variables cannot be referenced, use soong_config_get instead: %s", refDump)
@@ -1354,8 +1330,8 @@
 				return ctx.newBadExpr(node, "unknown variable %s", refDump)
 			}
 			return &callExpr{
-				name:       "patsubst",
-				returnType: knownFunctions["patsubst"].returnType,
+				name:       baseName + ".mkpatsubst",
+				returnType: starlarkTypeString,
 				args: []starlarkExpr{
 					&stringLiteralExpr{literal: substParts[0]},
 					&stringLiteralExpr{literal: substParts[1]},
@@ -1370,18 +1346,11 @@
 	}
 
 	expr := &callExpr{name: words[0].Dump(), returnType: starlarkTypeUnknown}
-	args := words[1]
-	args.TrimLeftSpaces()
-	// Make control functions and shell need special treatment as everything
-	// after the name is a single text argument
-	if isMakeControlFunc(expr.name) || expr.name == "shell" {
-		x := ctx.parseMakeString(node, args)
-		if xBad, ok := x.(*badExpr); ok {
-			return xBad
-		}
-		expr.args = []starlarkExpr{x}
-		return expr
+	args := mkparser.SimpleMakeString("", words[0].Pos())
+	if len(words) >= 2 {
+		args = words[1]
 	}
+	args.TrimLeftSpaces()
 	if expr.name == "call" {
 		words = args.SplitN(",", 2)
 		if words[0].Empty() || !words[0].Const() {
@@ -1395,41 +1364,128 @@
 		}
 	}
 	if kf, found := knownFunctions[expr.name]; found {
-		expr.returnType = kf.returnType
+		return kf.parse(ctx, node, args)
 	} else {
 		return ctx.newBadExpr(node, "cannot handle invoking %s", expr.name)
 	}
-	switch expr.name {
-	case "if":
-		return ctx.parseIfFunc(node, args)
-	case "foreach":
-		return ctx.parseForeachFunc(node, args)
-	case "word":
-		return ctx.parseWordFunc(node, args)
-	case "firstword", "lastword":
-		return ctx.parseFirstOrLastwordFunc(node, expr.name, args)
-	case "my-dir":
-		return NewVariableRefExpr(ctx.addVariable("LOCAL_PATH"), true)
-	case "subst", "patsubst":
-		return ctx.parseSubstFunc(node, expr.name, args)
-	default:
-		for _, arg := range args.Split(",") {
-			arg.TrimLeftSpaces()
-			arg.TrimRightSpaces()
-			x := ctx.parseMakeString(node, arg)
-			if xBad, ok := x.(*badExpr); ok {
-				return xBad
-			}
-			expr.args = append(expr.args, x)
+}
+
+type simpleCallParser struct {
+	name       string
+	returnType starlarkType
+	addGlobals bool
+}
+
+func (p *simpleCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
+	expr := &callExpr{name: p.name, returnType: p.returnType}
+	if p.addGlobals {
+		expr.args = append(expr.args, &globalsExpr{})
+	}
+	for _, arg := range args.Split(",") {
+		arg.TrimLeftSpaces()
+		arg.TrimRightSpaces()
+		x := ctx.parseMakeString(node, arg)
+		if xBad, ok := x.(*badExpr); ok {
+			return xBad
 		}
+		expr.args = append(expr.args, x)
 	}
 	return expr
 }
 
-func (ctx *parseContext) parseSubstFunc(node mkparser.Node, fname string, args *mkparser.MakeString) starlarkExpr {
+type makeControlFuncParser struct {
+	name string
+}
+
+func (p *makeControlFuncParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
+	// Make control functions need special treatment as everything
+	// after the name is a single text argument
+	x := ctx.parseMakeString(node, args)
+	if xBad, ok := x.(*badExpr); ok {
+		return xBad
+	}
+	return &callExpr{
+		name: p.name,
+		args: []starlarkExpr{
+			&stringLiteralExpr{ctx.script.mkFile},
+			x,
+		},
+		returnType: starlarkTypeUnknown,
+	}
+}
+
+type shellCallParser struct{}
+
+func (p *shellCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
+	// Shell functions need special treatment as everything
+	// after the name is a single text argument
+	x := ctx.parseMakeString(node, args)
+	if xBad, ok := x.(*badExpr); ok {
+		return xBad
+	}
+	return &callExpr{
+		name:       baseName + ".shell",
+		args:       []starlarkExpr{x},
+		returnType: starlarkTypeUnknown,
+	}
+}
+
+type myDirCallParser struct{}
+
+func (p *myDirCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
+	if !args.Empty() {
+		return ctx.newBadExpr(node, "my-dir function cannot have any arguments passed to it.")
+	}
+	return &variableRefExpr{ctx.addVariable("LOCAL_PATH"), true}
+}
+
+type isProductInListCallParser struct{}
+
+func (p *isProductInListCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
+	if args.Empty() {
+		return ctx.newBadExpr(node, "is-product-in-list requires an argument")
+	}
+	return &inExpr{
+		expr:  &variableRefExpr{ctx.addVariable("TARGET_PRODUCT"), true},
+		list:  maybeConvertToStringList(ctx.parseMakeString(node, args)),
+		isNot: false,
+	}
+}
+
+type isVendorBoardPlatformCallParser struct{}
+
+func (p *isVendorBoardPlatformCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
+	if args.Empty() || !identifierFullMatchRegex.MatchString(args.Dump()) {
+		return ctx.newBadExpr(node, "cannot handle non-constant argument to is-vendor-board-platform")
+	}
+	return &inExpr{
+		expr:  &variableRefExpr{ctx.addVariable("TARGET_BOARD_PLATFORM"), false},
+		list:  &variableRefExpr{ctx.addVariable(args.Dump() + "_BOARD_PLATFORMS"), true},
+		isNot: false,
+	}
+}
+
+type isVendorBoardQcomCallParser struct{}
+
+func (p *isVendorBoardQcomCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
+	if !args.Empty() {
+		return ctx.newBadExpr(node, "is-vendor-board-qcom does not accept any arguments")
+	}
+	return &inExpr{
+		expr:  &variableRefExpr{ctx.addVariable("TARGET_BOARD_PLATFORM"), false},
+		list:  &variableRefExpr{ctx.addVariable("QCOM_BOARD_PLATFORMS"), true},
+		isNot: false,
+	}
+}
+
+type substCallParser struct {
+	fname string
+}
+
+func (p *substCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
 	words := args.Split(",")
 	if len(words) != 3 {
-		return ctx.newBadExpr(node, "%s function should have 3 arguments", fname)
+		return ctx.newBadExpr(node, "%s function should have 3 arguments", p.fname)
 	}
 	from := ctx.parseMakeString(node, words[0])
 	if xBad, ok := from.(*badExpr); ok {
@@ -1443,7 +1499,7 @@
 	words[2].TrimRightSpaces()
 	obj := ctx.parseMakeString(node, words[2])
 	typ := obj.typ()
-	if typ == starlarkTypeString && fname == "subst" {
+	if typ == starlarkTypeString && p.fname == "subst" {
 		// Optimization: if it's $(subst from, to, string), emit string.replace(from, to)
 		return &callExpr{
 			object:     obj,
@@ -1453,13 +1509,15 @@
 		}
 	}
 	return &callExpr{
-		name:       fname,
+		name:       baseName + ".mk" + p.fname,
 		args:       []starlarkExpr{from, to, obj},
 		returnType: obj.typ(),
 	}
 }
 
-func (ctx *parseContext) parseIfFunc(node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
+type ifCallParser struct{}
+
+func (p *ifCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
 	words := args.Split(",")
 	if len(words) != 2 && len(words) != 3 {
 		return ctx.newBadExpr(node, "if function should have 2 or 3 arguments, found "+strconv.Itoa(len(words)))
@@ -1488,7 +1546,9 @@
 	}
 }
 
-func (ctx *parseContext) parseForeachFunc(node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
+type foreachCallPaser struct{}
+
+func (p *foreachCallPaser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
 	words := args.Split(",")
 	if len(words) != 3 {
 		return ctx.newBadExpr(node, "foreach function should have 3 arguments, found "+strconv.Itoa(len(words)))
@@ -1507,8 +1567,8 @@
 
 	if list.typ() != starlarkTypeList {
 		list = &callExpr{
-			name:       "words",
-			returnType: knownFunctions["words"].returnType,
+			name:       baseName + ".words",
+			returnType: starlarkTypeList,
 			args:       []starlarkExpr{list},
 		}
 	}
@@ -1520,7 +1580,9 @@
 	}
 }
 
-func (ctx *parseContext) parseWordFunc(node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
+type wordCallParser struct{}
+
+func (p *wordCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
 	words := args.Split(",")
 	if len(words) != 2 {
 		return ctx.newBadExpr(node, "word function should have 2 arguments")
@@ -1544,13 +1606,17 @@
 	return &indexExpr{array, &intLiteralExpr{int(index - 1)}}
 }
 
-func (ctx *parseContext) parseFirstOrLastwordFunc(node mkparser.Node, name string, args *mkparser.MakeString) starlarkExpr {
+type firstOrLastwordCallParser struct {
+	isLastWord bool
+}
+
+func (p *firstOrLastwordCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
 	arg := ctx.parseMakeString(node, args)
 	if bad, ok := arg.(*badExpr); ok {
 		return bad
 	}
 	index := &intLiteralExpr{0}
-	if name == "lastword" {
+	if p.isLastWord {
 		if v, ok := arg.(*variableRefExpr); ok && v.ref.name() == "MAKEFILE_LIST" {
 			return &stringLiteralExpr{ctx.script.mkFile}
 		}
@@ -1562,6 +1628,68 @@
 	return &indexExpr{&callExpr{object: arg, name: "split", returnType: starlarkTypeList}, index}
 }
 
+func parseIntegerArguments(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString, expectedArgs int) ([]starlarkExpr, error) {
+	parsedArgs := make([]starlarkExpr, 0)
+	for _, arg := range args.Split(",") {
+		expr := ctx.parseMakeString(node, arg)
+		if expr.typ() == starlarkTypeList {
+			return nil, fmt.Errorf("argument to math argument has type list, which cannot be converted to int")
+		}
+		if s, ok := maybeString(expr); ok {
+			intVal, err := strconv.Atoi(strings.TrimSpace(s))
+			if err != nil {
+				return nil, err
+			}
+			expr = &intLiteralExpr{literal: intVal}
+		} else if expr.typ() != starlarkTypeInt {
+			expr = &callExpr{
+				name:       "int",
+				args:       []starlarkExpr{expr},
+				returnType: starlarkTypeInt,
+			}
+		}
+		parsedArgs = append(parsedArgs, expr)
+	}
+	if len(parsedArgs) != expectedArgs {
+		return nil, fmt.Errorf("function should have %d arguments", expectedArgs)
+	}
+	return parsedArgs, nil
+}
+
+type mathComparisonCallParser struct {
+	op string
+}
+
+func (p *mathComparisonCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
+	parsedArgs, err := parseIntegerArguments(ctx, node, args, 2)
+	if err != nil {
+		return ctx.newBadExpr(node, err.Error())
+	}
+	return &binaryOpExpr{
+		left:       parsedArgs[0],
+		right:      parsedArgs[1],
+		op:         p.op,
+		returnType: starlarkTypeBool,
+	}
+}
+
+type mathMaxOrMinCallParser struct {
+	function string
+}
+
+func (p *mathMaxOrMinCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
+	parsedArgs, err := parseIntegerArguments(ctx, node, args, 2)
+	if err != nil {
+		return ctx.newBadExpr(node, err.Error())
+	}
+	return &callExpr{
+		object:     nil,
+		name:       p.function,
+		args:       parsedArgs,
+		returnType: starlarkTypeInt,
+	}
+}
+
 func (ctx *parseContext) parseMakeString(node mkparser.Node, mk *mkparser.MakeString) starlarkExpr {
 	if mk.Const() {
 		return &stringLiteralExpr{mk.Dump()}
@@ -1612,6 +1740,13 @@
 	default:
 		ctx.errorf(x, "unsupported line %s", strings.ReplaceAll(x.Dump(), "\n", "\n#"))
 	}
+
+	// Clear the includeTops after each non-comment statement
+	// so that include annotations placed on certain statements don't apply
+	// globally for the rest of the makefile was well.
+	if _, wasComment := node.(*mkparser.Comment); !wasComment && len(ctx.includeTops) > 0 {
+		ctx.includeTops = []string{}
+	}
 }
 
 // Processes annotation. An annotation is a comment that starts with #RBC# and provides
diff --git a/mk2rbc/mk2rbc_test.go b/mk2rbc/mk2rbc_test.go
index 1ba273b..ec6dfd0 100644
--- a/mk2rbc/mk2rbc_test.go
+++ b/mk2rbc/mk2rbc_test.go
@@ -598,9 +598,9 @@
 
 def init(g, handle):
   cfg = rblf.cfg(handle)
-  if g.get("TARGET_BOARD_PLATFORM", "") in ["msm8998"]:
+  if rblf.board_platform_in(g, "msm8998"):
     pass
-  elif g.get("TARGET_BOARD_PLATFORM", "") != "copper":
+  elif not rblf.board_platform_is(g, "copper"):
     pass
   elif g.get("TARGET_BOARD_PLATFORM", "") not in g["QCOM_BOARD_PLATFORMS"]:
     pass
@@ -1112,6 +1112,46 @@
 `,
 	},
 	{
+		desc:   "Dynamic inherit path that lacks necessary hint",
+		mkname: "product.mk",
+		in: `
+#RBC# include_top foo
+$(call inherit-product,$(MY_VAR)/font.mk)
+
+#RBC# include_top foo
+
+# There's some space and even this comment between the include_top and the inherit-product
+
+$(call inherit-product,$(MY_VAR)/font.mk)
+
+$(call inherit-product,$(MY_VAR)/font.mk)
+`,
+		expected: `#RBC# include_top foo
+load("//build/make/core:product_config.rbc", "rblf")
+load("//foo:font.star|init", _font_init = "init")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  _entry = {
+    "foo/font.mk": ("_font", _font_init),
+  }.get("%s/font.mk" % g.get("MY_VAR", ""))
+  (_varmod, _varmod_init) = _entry if _entry else (None, None)
+  if not _varmod_init:
+    rblf.mkerror("product.mk", "Cannot find %s" % ("%s/font.mk" % g.get("MY_VAR", "")))
+  rblf.inherit(handle, _varmod, _varmod_init)
+  #RBC# include_top foo
+  # There's some space and even this comment between the include_top and the inherit-product
+  _entry = {
+    "foo/font.mk": ("_font", _font_init),
+  }.get("%s/font.mk" % g.get("MY_VAR", ""))
+  (_varmod, _varmod_init) = _entry if _entry else (None, None)
+  if not _varmod_init:
+    rblf.mkerror("product.mk", "Cannot find %s" % ("%s/font.mk" % g.get("MY_VAR", "")))
+  rblf.inherit(handle, _varmod, _varmod_init)
+  rblf.mk2rbc_error("product.mk:11", "inherit-product/include statements must not be prefixed with a variable, or must include a #RBC# include_top comment beforehand giving a root directory to search.")
+`,
+	},
+	{
 		desc:   "Ignore make rules",
 		mkname: "product.mk",
 		in: `
@@ -1241,6 +1281,63 @@
   g["NATIVE_BRIDGE_PRODUCT_PACKAGES"] += " " + " ".join(rblf.addsuffix(".native_bridge", g.get("NATIVE_BRIDGE_ORIG_GUEST_LIBS", "")))
 `,
 	},
+	{
+		desc:   "Math functions",
+		mkname: "product.mk",
+		in: `
+# Test the math functions defined in build/make/common/math.mk
+ifeq ($(call math_max,2,5),5)
+endif
+ifeq ($(call math_min,2,5),2)
+endif
+ifeq ($(call math_gt_or_eq,2,5),true)
+endif
+ifeq ($(call math_gt,2,5),true)
+endif
+ifeq ($(call math_lt,2,5),true)
+endif
+ifeq ($(call math_gt_or_eq,2,5),)
+endif
+ifeq ($(call math_gt,2,5),)
+endif
+ifeq ($(call math_lt,2,5),)
+endif
+ifeq ($(call math_gt_or_eq,$(MY_VAR), 5),true)
+endif
+ifeq ($(call math_gt_or_eq,$(MY_VAR),$(MY_OTHER_VAR)),true)
+endif
+ifeq ($(call math_gt_or_eq,100$(MY_VAR),10),true)
+endif
+`,
+		expected: `# Test the math functions defined in build/make/common/math.mk
+load("//build/make/core:product_config.rbc", "rblf")
+
+def init(g, handle):
+  cfg = rblf.cfg(handle)
+  if max(2, 5) == 5:
+    pass
+  if min(2, 5) == 2:
+    pass
+  if 2 >= 5:
+    pass
+  if 2 > 5:
+    pass
+  if 2 < 5:
+    pass
+  if 2 < 5:
+    pass
+  if 2 <= 5:
+    pass
+  if 2 >= 5:
+    pass
+  if int(g.get("MY_VAR", "")) >= 5:
+    pass
+  if int(g.get("MY_VAR", "")) >= int(g.get("MY_OTHER_VAR", "")):
+    pass
+  if int("100%s" % g.get("MY_VAR", "")) >= 10:
+    pass
+`,
+	},
 }
 
 var known_variables = []struct {
diff --git a/rust/OWNERS b/rust/OWNERS
index b5b795c..d07ef7e 100644
--- a/rust/OWNERS
+++ b/rust/OWNERS
@@ -2,4 +2,4 @@
 per-file * = chh@google.com, ivanlozano@google.com, jeffv@google.com, mmaurer@google.com, srhines@google.com
 
 # Limited owners/reviewers of the allowed list.
-per-file allowed_list.go = chh@google.com, ivanlozano@google.com, jeffv@google.com, jgalenson@google.com, mmaurer@google.com, srhines@google.com
+per-file allowed_list.go = chh@google.com, ivanlozano@google.com, jeffv@google.com, mmaurer@google.com, srhines@google.com
diff --git a/rust/bindgen.go b/rust/bindgen.go
index 5e1b4b7..ef5702b 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -30,7 +30,7 @@
 	defaultBindgenFlags = []string{""}
 
 	// bindgen should specify its own Clang revision so updating Clang isn't potentially blocked on bindgen failures.
-	bindgenClangVersion = "clang-r437112"
+	bindgenClangVersion = "clang-r437112b"
 
 	_ = pctx.VariableFunc("bindgenClangVersion", func(ctx android.PackageVarContext) string {
 		if override := ctx.Config().Getenv("LLVM_BINDGEN_PREBUILTS_VERSION"); override != "" {
diff --git a/rust/coverage.go b/rust/coverage.go
index 8fdfa23..91d34ac 100644
--- a/rust/coverage.go
+++ b/rust/coverage.go
@@ -22,6 +22,7 @@
 
 var CovLibraryName = "libprofile-clang-extras"
 
+// Add '%c' to default specifier after we resolve http://b/210012154
 const profileInstrFlag = "-fprofile-instr-generate=/data/misc/trace/clang-%p-%m.profraw"
 
 type coverage struct {
@@ -70,6 +71,10 @@
 			"-Wl,-z,nostart-stop-gc",
 		)
 		deps.StaticLibs = append(deps.StaticLibs, coverage.OutputFile().Path())
+		if cc.EnableContinuousCoverage(ctx) {
+			flags.RustFlags = append(flags.RustFlags, "-C llvm-args=--runtime-counter-relocation")
+			flags.LinkFlags = append(flags.LinkFlags, "-Wl,-mllvm,-runtime-counter-relocation")
+		}
 	}
 
 	return flags, deps
diff --git a/rust/rust.go b/rust/rust.go
index c2585f2..778000d 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -901,6 +901,9 @@
 		bloaty.MeasureSizeForPaths(ctx, mod.compiler.strippedOutputFilePath(), android.OptionalPathForPath(mod.compiler.unstrippedOutputFilePath()))
 
 		mod.docTimestampFile = mod.compiler.rustdoc(ctx, flags, deps)
+		if mod.docTimestampFile.Valid() {
+			ctx.CheckbuildFile(mod.docTimestampFile.Path())
+		}
 
 		// glob exported headers for snapshot, if BOARD_VNDK_VERSION is current or
 		// RECOVERY_SNAPSHOT_VERSION is current.