Merge "Change how override_modules work with prebuilts."
diff --git a/android/config.go b/android/config.go
index b906108..16f3d73 100644
--- a/android/config.go
+++ b/android/config.go
@@ -406,6 +406,14 @@
 		return Config{}, err
 	}
 
+	if Bool(config.productVariables.GcovCoverage) && Bool(config.productVariables.ClangCoverage) {
+		return Config{}, fmt.Errorf("GcovCoverage and ClangCoverage cannot both be set")
+	}
+
+	config.productVariables.Native_coverage = proptools.BoolPtr(
+		Bool(config.productVariables.GcovCoverage) ||
+			Bool(config.productVariables.ClangCoverage))
+
 	return Config{config}, nil
 }
 
@@ -1057,18 +1065,20 @@
 	return coverage
 }
 
-func (c *config) NativeLineCoverage() bool {
-	return Bool(c.productVariables.NativeLineCoverage)
-}
-
+// Returns true if gcov or clang coverage is enabled.
 func (c *deviceConfig) NativeCoverageEnabled() bool {
-	return Bool(c.config.productVariables.Native_coverage) || Bool(c.config.productVariables.NativeLineCoverage)
+	return Bool(c.config.productVariables.GcovCoverage) ||
+		Bool(c.config.productVariables.ClangCoverage)
 }
 
 func (c *deviceConfig) ClangCoverageEnabled() bool {
 	return Bool(c.config.productVariables.ClangCoverage)
 }
 
+func (c *deviceConfig) GcovCoverageEnabled() bool {
+	return Bool(c.config.productVariables.GcovCoverage)
+}
+
 // NativeCoverageEnabledForPath returns whether (GCOV- or Clang-based) native
 // code coverage is enabled for path. By default, coverage is not enabled for a
 // given path unless it is part of the NativeCoveragePaths product variable (and
diff --git a/android/module.go b/android/module.go
index a488c0d..f24047c 100644
--- a/android/module.go
+++ b/android/module.go
@@ -835,6 +835,10 @@
 	return m.Os().Class == Host || m.Os().Class == HostCross
 }
 
+func (m *ModuleBase) Device() bool {
+	return m.Os().Class == Device
+}
+
 func (m *ModuleBase) Arch() Arch {
 	return m.Target().Arch
 }
diff --git a/android/rule_builder.go b/android/rule_builder.go
index 6226548..afb5f4e 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -172,7 +172,7 @@
 
 	inputs := make(map[string]Path)
 	for _, c := range r.commands {
-		for _, input := range c.inputs {
+		for _, input := range append(c.inputs, c.implicits...) {
 			inputStr := input.String()
 			if _, isOutput := outputs[inputStr]; !isOutput {
 				if _, isDepFile := depFiles[inputStr]; !isDepFile {
@@ -480,6 +480,7 @@
 type RuleBuilderCommand struct {
 	buf           strings.Builder
 	inputs        Paths
+	implicits     Paths
 	orderOnlys    Paths
 	outputs       WritablePaths
 	depFiles      WritablePaths
@@ -503,6 +504,16 @@
 	return path.String()
 }
 
+func (c *RuleBuilderCommand) addImplicit(path Path) string {
+	if c.sbox {
+		if rel, isRel, _ := maybeRelErr(c.sboxOutDir.String(), path.String()); isRel {
+			return "__SBOX_OUT_DIR__/" + rel
+		}
+	}
+	c.implicits = append(c.implicits, path)
+	return path.String()
+}
+
 func (c *RuleBuilderCommand) addOrderOnly(path Path) {
 	c.orderOnlys = append(c.orderOnlys, path)
 }
@@ -623,7 +634,7 @@
 // Implicit adds the specified input path to the dependencies returned by RuleBuilder.Inputs without modifying the
 // command line.
 func (c *RuleBuilderCommand) Implicit(path Path) *RuleBuilderCommand {
-	c.addInput(path)
+	c.addImplicit(path)
 	return c
 }
 
@@ -631,11 +642,16 @@
 // command line.
 func (c *RuleBuilderCommand) Implicits(paths Paths) *RuleBuilderCommand {
 	for _, path := range paths {
-		c.addInput(path)
+		c.addImplicit(path)
 	}
 	return c
 }
 
+// GetImplicits returns the command's implicit inputs.
+func (c *RuleBuilderCommand) GetImplicits() Paths {
+	return c.implicits
+}
+
 // OrderOnly adds the specified input path to the dependencies returned by RuleBuilder.OrderOnlys
 // without modifying the command line.
 func (c *RuleBuilderCommand) OrderOnly(path Path) *RuleBuilderCommand {
diff --git a/android/variable.go b/android/variable.go
index b2149c3..2c8bd07 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -270,12 +270,14 @@
 	JavaCoveragePaths        []string `json:",omitempty"`
 	JavaCoverageExcludePaths []string `json:",omitempty"`
 
-	NativeLineCoverage         *bool    `json:",omitempty"`
-	Native_coverage            *bool    `json:",omitempty"`
+	GcovCoverage               *bool    `json:",omitempty"`
 	ClangCoverage              *bool    `json:",omitempty"`
 	NativeCoveragePaths        []string `json:",omitempty"`
 	NativeCoverageExcludePaths []string `json:",omitempty"`
 
+	// Set by NewConfig
+	Native_coverage *bool
+
 	SanitizeHost       []string `json:",omitempty"`
 	SanitizeDevice     []string `json:",omitempty"`
 	SanitizeDeviceDiag []string `json:",omitempty"`
diff --git a/androidmk/androidmk/androidmk.go b/androidmk/androidmk/androidmk.go
index 4aadbe8..dcd67d8 100644
--- a/androidmk/androidmk/androidmk.go
+++ b/androidmk/androidmk/androidmk.go
@@ -34,6 +34,7 @@
 	defs              []bpparser.Definition
 	localAssignments  map[string]*bpparser.Property
 	globalAssignments map[string]*bpparser.Expression
+	variableRenames   map[string]string
 	scope             mkparser.Scope
 	module            *bpparser.Module
 
@@ -43,6 +44,10 @@
 	inModule bool
 }
 
+var invalidVariableStringToReplacement = map[string]string{
+	"-": "_dash_",
+}
+
 func (f *bpFile) insertComment(s string) {
 	f.comments = append(f.comments, &bpparser.CommentGroup{
 		Comments: []*bpparser.Comment{
@@ -120,6 +125,7 @@
 		scope:             androidScope(),
 		localAssignments:  make(map[string]*bpparser.Property),
 		globalAssignments: make(map[string]*bpparser.Expression),
+		variableRenames:   make(map[string]string),
 	}
 
 	var conds []*conditional
@@ -224,6 +230,25 @@
 	return string(out), errs
 }
 
+func renameVariableWithInvalidCharacters(name string) string {
+	renamed := ""
+	for invalid, replacement := range invalidVariableStringToReplacement {
+		if strings.Contains(name, invalid) {
+			renamed = strings.ReplaceAll(name, invalid, replacement)
+		}
+	}
+
+	return renamed
+}
+
+func invalidVariableStrings() string {
+	invalidStrings := make([]string, 0, len(invalidVariableStringToReplacement))
+	for s := range invalidVariableStringToReplacement {
+		invalidStrings = append(invalidStrings, "\""+s+"\"")
+	}
+	return strings.Join(invalidStrings, ", ")
+}
+
 func handleAssignment(file *bpFile, assignment *mkparser.Assignment, c *conditional) {
 	if !assignment.Name.Const() {
 		file.errorf(assignment, "unsupported non-const variable name")
@@ -238,6 +263,12 @@
 	name := assignment.Name.Value(nil)
 	prefix := ""
 
+	if newName := renameVariableWithInvalidCharacters(name); newName != "" {
+		file.warnf("Variable names cannot contain: %s. Renamed \"%s\" to \"%s\"", invalidVariableStrings(), name, newName)
+		file.variableRenames[name] = newName
+		name = newName
+	}
+
 	if strings.HasPrefix(name, "LOCAL_") {
 		for _, x := range propertyPrefixes {
 			if strings.HasSuffix(name, "_"+x.mk) {
@@ -341,11 +372,11 @@
 	var err error
 	switch typ {
 	case bpparser.ListType:
-		exp, err = makeToListExpression(val, file.scope)
+		exp, err = makeToListExpression(val, file)
 	case bpparser.StringType:
-		exp, err = makeToStringExpression(val, file.scope)
+		exp, err = makeToStringExpression(val, file)
 	case bpparser.BoolType:
-		exp, err = makeToBoolExpression(val)
+		exp, err = makeToBoolExpression(val, file)
 	default:
 		panic("unknown type")
 	}
@@ -358,7 +389,6 @@
 }
 
 func setVariable(file *bpFile, plusequals bool, prefix, name string, value bpparser.Expression, local bool) error {
-
 	if prefix != "" {
 		name = prefix + "." + name
 	}
diff --git a/androidmk/androidmk/androidmk_test.go b/androidmk/androidmk/androidmk_test.go
index 54bd586..4f307c4 100644
--- a/androidmk/androidmk/androidmk_test.go
+++ b/androidmk/androidmk/androidmk_test.go
@@ -1367,6 +1367,28 @@
 `,
 	},
 	{
+		desc: "dashed_variable gets renamed",
+		in: `
+		include $(CLEAR_VARS)
+
+		dashed-variable:= a.cpp
+
+		LOCAL_MODULE:= test
+		LOCAL_SRC_FILES:= $(dashed-variable)
+		include $(BUILD_EXECUTABLE)
+		`,
+		expected: `
+
+// ANDROIDMK TRANSLATION WARNING: Variable names cannot contain: "-". Renamed "dashed-variable" to "dashed_dash_variable"
+dashed_dash_variable = ["a.cpp"]
+cc_binary {
+
+    name: "test",
+    srcs: dashed_dash_variable,
+}
+`,
+	},
+	{
 		desc: "undefined_boolean_var",
 		in: `
 include $(CLEAR_VARS)
diff --git a/androidmk/androidmk/values.go b/androidmk/androidmk/values.go
index 6b18a65..9618142 100644
--- a/androidmk/androidmk/values.go
+++ b/androidmk/androidmk/values.go
@@ -60,8 +60,7 @@
 	}, nil
 }
 
-func makeToStringExpression(ms *mkparser.MakeString, scope mkparser.Scope) (bpparser.Expression, error) {
-
+func makeToStringExpression(ms *mkparser.MakeString, file *bpFile) (bpparser.Expression, error) {
 	var val bpparser.Expression
 	var err error
 
@@ -70,18 +69,18 @@
 	}
 
 	for i, s := range ms.Strings[1:] {
-		if ret, ok := ms.Variables[i].EvalFunction(scope); ok {
+		if ret, ok := ms.Variables[i].EvalFunction(file.scope); ok {
 			if len(ret) > 1 {
 				return nil, fmt.Errorf("Unexpected list value %s", ms.Dump())
 			}
 			val, err = addValues(val, stringToStringValue(ret[0]))
 		} else {
-			name := ms.Variables[i].Name
-			if !name.Const() {
-				return nil, fmt.Errorf("Unsupported non-const variable name %s", name.Dump())
+			name, err := extractVariableName(ms.Variables[i].Name, file)
+			if err != nil {
+				return nil, err
 			}
 			tmp := &bpparser.Variable{
-				Name:  name.Value(nil),
+				Name:  name,
 				Value: &bpparser.String{},
 			}
 
@@ -125,8 +124,7 @@
 
 }
 
-func makeToListExpression(ms *mkparser.MakeString, scope mkparser.Scope) (bpparser.Expression, error) {
-
+func makeToListExpression(ms *mkparser.MakeString, file *bpFile) (bpparser.Expression, error) {
 	fields := ms.Split(" \t")
 
 	var listOfListValues []bpparser.Expression
@@ -135,14 +133,14 @@
 
 	for _, f := range fields {
 		if len(f.Variables) == 1 && f.Strings[0] == "" && f.Strings[1] == "" {
-			if ret, ok := f.Variables[0].EvalFunction(scope); ok {
+			if ret, ok := f.Variables[0].EvalFunction(file.scope); ok {
 				listValue.Values = append(listValue.Values, stringListToStringValueList(ret)...)
 			} else {
-				// Variable by itself, variable is probably a list
-				if !f.Variables[0].Name.Const() {
-					return nil, fmt.Errorf("unsupported non-const variable name")
+				name, err := extractVariableName(f.Variables[0].Name, file)
+				if err != nil {
+					return nil, err
 				}
-				if f.Variables[0].Name.Value(nil) == "TOP" {
+				if name == "TOP" {
 					listValue.Values = append(listValue.Values, &bpparser.String{
 						Value: ".",
 					})
@@ -151,14 +149,14 @@
 						listOfListValues = append(listOfListValues, listValue)
 					}
 					listOfListValues = append(listOfListValues, &bpparser.Variable{
-						Name:  f.Variables[0].Name.Value(nil),
+						Name:  name,
 						Value: &bpparser.List{},
 					})
 					listValue = &bpparser.List{}
 				}
 			}
 		} else {
-			s, err := makeToStringExpression(f, scope)
+			s, err := makeToStringExpression(f, file)
 			if err != nil {
 				return nil, err
 			}
@@ -208,15 +206,15 @@
 	}, nil
 }
 
-func makeToBoolExpression(ms *mkparser.MakeString) (bpparser.Expression, error) {
+func makeToBoolExpression(ms *mkparser.MakeString, file *bpFile) (bpparser.Expression, error) {
 	if !ms.Const() {
 		if len(ms.Variables) == 1 && ms.Strings[0] == "" && ms.Strings[1] == "" {
-			name := ms.Variables[0].Name
-			if !name.Const() {
-				return nil, fmt.Errorf("unsupported non-const variable name")
+			name, err := extractVariableName(ms.Variables[0].Name, file)
+			if err != nil {
+				return nil, err
 			}
 			return &bpparser.Variable{
-				Name:  name.Value(nil),
+				Name:  name,
 				Value: &bpparser.Bool{},
 			}, nil
 		} else {
@@ -226,3 +224,17 @@
 
 	return stringToBoolValue(ms.Value(nil))
 }
+
+func extractVariableName(name *mkparser.MakeString, file *bpFile) (string, error) {
+	if !name.Const() {
+		return "", fmt.Errorf("Unsupported non-const variable name %s", name.Dump())
+	}
+
+	variableName := name.Value(nil)
+
+	if newName, ok := file.variableRenames[variableName]; ok {
+		variableName = newName
+	}
+
+	return variableName, nil
+}
diff --git a/apex/apex.go b/apex/apex.go
index a4af7aa..58cbb13 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -369,7 +369,6 @@
 		"libbinderthreadstateutils",
 		"libbluetooth-types-header",
 		"libbufferhub_headers",
-		"libc_scudo",
 		"libcodec2",
 		"libcodec2_headers",
 		"libcodec2_hidl@1.0",
@@ -985,9 +984,6 @@
 	// List of providing APEXes' names so that this APEX can depend on provided shared libraries.
 	Uses []string
 
-	// A txt file containing list of files that are allowed to be included in this APEX.
-	Allowed_files *string
-
 	// package format of this apex variant; could be non-flattened, flattened, or zip.
 	// imageApex, zipApex or flattened
 	ApexType apexPackaging `blueprint:"mutated"`
@@ -1063,6 +1059,9 @@
 	// Apex Container Package Name.
 	// Override value for attribute package:name in AndroidManifest.xml
 	Package_name string
+
+	// A txt file containing list of files that are allowed to be included in this APEX.
+	Allowed_files *string `android:"path"`
 }
 
 type apexPackaging int
@@ -1454,6 +1453,9 @@
 }
 
 func (a *apexBundle) OverridablePropertiesDepsMutator(ctx android.BottomUpMutatorContext) {
+	if a.overridableProperties.Allowed_files != nil {
+		android.ExtractSourceDeps(ctx, a.overridableProperties.Allowed_files)
+	}
 	ctx.AddFarVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(),
 		androidAppTag, a.overridableProperties.Apps...)
 	ctx.AddFarVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(),
@@ -1553,7 +1555,7 @@
 var _ cc.Coverage = (*apexBundle)(nil)
 
 func (a *apexBundle) IsNativeCoverageNeeded(ctx android.BaseModuleContext) bool {
-	return ctx.Device() && (ctx.DeviceConfig().NativeCoverageEnabled() || ctx.DeviceConfig().ClangCoverageEnabled())
+	return ctx.Device() && ctx.DeviceConfig().NativeCoverageEnabled()
 }
 
 func (a *apexBundle) PreventInstall() {
diff --git a/apex/apex_test.go b/apex/apex_test.go
index a7a7765..38c8489 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -5393,7 +5393,62 @@
 	apexKeysText := ctx.SingletonForTests("apex_keys_text")
 	content := apexKeysText.MaybeDescription("apexkeys.txt").BuildParams.Args["content"]
 	ensureContains(t, content, `name="myapex_set.apex" public_key="PRESIGNED" private_key="PRESIGNED" container_certificate="PRESIGNED" container_private_key="PRESIGNED" partition="system"`)
-	ensureNotContains(t, content, "myapex.apex")
+	ensureContains(t, content, `name="myapex.apex" public_key="PRESIGNED" private_key="PRESIGNED" container_certificate="PRESIGNED" container_private_key="PRESIGNED" partition="system"`)
+}
+
+func TestAllowedFiles(t *testing.T) {
+	ctx, _ := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			apps: ["app"],
+			allowed_files: "allowed.txt",
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		android_app {
+			name: "app",
+			srcs: ["foo/bar/MyClass.java"],
+			package_name: "foo",
+			sdk_version: "none",
+			system_modules: "none",
+			apex_available: [ "myapex" ],
+		}
+	`, withFiles(map[string][]byte{
+		"sub/Android.bp": []byte(`
+			override_apex {
+				name: "override_myapex",
+				base: "myapex",
+				apps: ["override_app"],
+				allowed_files: ":allowed",
+			}
+			// Overridable "path" property should be referenced indirectly
+			filegroup {
+				name: "allowed",
+				srcs: ["allowed.txt"],
+			}
+			override_android_app {
+				name: "override_app",
+				base: "app",
+				package_name: "bar",
+			}
+			`),
+	}))
+
+	rule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("diffApexContentRule")
+	if expected, actual := "allowed.txt", rule.Args["allowed_files_file"]; expected != actual {
+		t.Errorf("allowed_files_file: expected %q but got %q", expected, actual)
+	}
+
+	rule2 := ctx.ModuleForTests("myapex", "android_common_override_myapex_myapex_image").Rule("diffApexContentRule")
+	if expected, actual := "sub/allowed.txt", rule2.Args["allowed_files_file"]; expected != actual {
+		t.Errorf("allowed_files_file: expected %q but got %q", expected, actual)
+	}
 }
 
 func TestMain(m *testing.M) {
diff --git a/apex/builder.go b/apex/builder.go
index 53c1193..af43417 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -391,7 +391,7 @@
 	emitCommands = append(emitCommands, "sort -o "+imageContentFile.String()+" "+imageContentFile.String())
 	implicitInputs = append(implicitInputs, a.manifestPbOut)
 
-	if a.properties.Allowed_files != nil {
+	if a.overridableProperties.Allowed_files != nil {
 		ctx.Build(pctx, android.BuildParams{
 			Rule:        emitApexContentRule,
 			Implicits:   implicitInputs,
@@ -402,7 +402,7 @@
 			},
 		})
 		implicitInputs = append(implicitInputs, imageContentFile)
-		allowedFilesFile := android.PathForModuleSrc(ctx, proptools.String(a.properties.Allowed_files))
+		allowedFilesFile := android.PathForModuleSrc(ctx, proptools.String(a.overridableProperties.Allowed_files))
 
 		phonyOutput := android.PathForModuleOut(ctx, a.Name()+"-diff-phony-output")
 		ctx.Build(pctx, android.BuildParams{
diff --git a/apex/key.go b/apex/key.go
index a68f6e1..d2d5786 100644
--- a/apex/key.go
+++ b/apex/key.go
@@ -160,12 +160,6 @@
 				presigned: true,
 				partition: m.PartitionTag(ctx.DeviceConfig()),
 			}
-
-			for _, om := range m.Overrides() {
-				if _, ok := apexKeyMap[om]; ok {
-					delete(apexKeyMap, om)
-				}
-			}
 			apexKeyMap[m.BaseModuleName()] = entry
 		}
 	})
diff --git a/apex/vndk_test.go b/apex/vndk_test.go
index 05cdfcd..60b6ed5 100644
--- a/apex/vndk_test.go
+++ b/apex/vndk_test.go
@@ -118,6 +118,7 @@
 
 	t.Run("VNDK APEX supports coverage variants", func(t *testing.T) {
 		ctx, _ := testApex(t, bp, func(fs map[string][]byte, config android.Config) {
+			config.TestProductVariables.GcovCoverage = proptools.BoolPtr(true)
 			config.TestProductVariables.Native_coverage = proptools.BoolPtr(true)
 		})
 
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 76b4e38..f73e021 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -430,6 +430,40 @@
 	checkVndkLibrariesOutput(t, ctx, "vndkcorevariant.libraries.txt", nil)
 }
 
+func TestVndkWithHostSupported(t *testing.T) {
+	ctx := testCc(t, `
+		cc_library {
+			name: "libvndk_host_supported",
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+			},
+			host_supported: true,
+		}
+
+		cc_library {
+			name: "libvndk_host_supported_but_disabled_on_device",
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+			},
+			host_supported: true,
+			enabled: false,
+			target: {
+				host: {
+					enabled: true,
+				}
+			}
+		}
+
+		vndk_libraries_txt {
+			name: "vndkcore.libraries.txt",
+		}
+	`)
+
+	checkVndkLibrariesOutput(t, ctx, "vndkcore.libraries.txt", []string{"libvndk_host_supported.so"})
+}
+
 func TestVndkLibrariesTxtAndroidMk(t *testing.T) {
 	bp := `
 		vndk_libraries_txt {
diff --git a/cc/coverage.go b/cc/coverage.go
index 1a559a9..c823324 100644
--- a/cc/coverage.go
+++ b/cc/coverage.go
@@ -74,8 +74,8 @@
 }
 
 func (cov *coverage) flags(ctx ModuleContext, flags Flags, deps PathDeps) (Flags, PathDeps) {
-	gcovCoverage := ctx.DeviceConfig().NativeCoverageEnabled()
 	clangCoverage := ctx.DeviceConfig().ClangCoverageEnabled()
+	gcovCoverage := ctx.DeviceConfig().GcovCoverageEnabled()
 
 	if !gcovCoverage && !clangCoverage {
 		return flags, deps
@@ -161,7 +161,7 @@
 func SetCoverageProperties(ctx android.BaseModuleContext, properties CoverageProperties, moduleTypeHasCoverage bool,
 	useSdk bool, sdkVersion string) CoverageProperties {
 	// Coverage is disabled globally
-	if !ctx.DeviceConfig().NativeCoverageEnabled() && !ctx.DeviceConfig().ClangCoverageEnabled() {
+	if !ctx.DeviceConfig().NativeCoverageEnabled() {
 		return properties
 	}
 
diff --git a/cc/library_sdk_member.go b/cc/library_sdk_member.go
index a7a1de2..4410302 100644
--- a/cc/library_sdk_member.go
+++ b/cc/library_sdk_member.go
@@ -124,6 +124,14 @@
 	if stl != nil {
 		pbm.AddProperty("stl", proptools.String(stl))
 	}
+
+	if lib, ok := ccModule.linker.(*libraryDecorator); ok {
+		uhs := lib.Properties.Unique_host_soname
+		if uhs != nil {
+			pbm.AddProperty("unique_host_soname", proptools.Bool(uhs))
+		}
+	}
+
 	return pbm
 }
 
diff --git a/cc/linker.go b/cc/linker.go
index c9cbd9b..58f8a29 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -298,17 +298,6 @@
 			}
 		}
 
-		if inList("libc_scudo", deps.SharedLibs) {
-			// libc_scudo is an alternate implementation of all
-			// allocation functions (malloc, free), that uses
-			// the scudo allocator instead of the default native
-			// allocator. If this library is in the list, make
-			// sure it's first so it properly overrides the
-			// allocation functions of all other shared libraries.
-			_, deps.SharedLibs = removeFromList("libc_scudo", deps.SharedLibs)
-			deps.SharedLibs = append([]string{"libc_scudo"}, deps.SharedLibs...)
-		}
-
 		// If libc and libdl are both in system_shared_libs make sure libdl comes after libc
 		// to avoid loading libdl before libc.
 		if inList("libdl", systemSharedLibs) && inList("libc", systemSharedLibs) &&
diff --git a/cc/sanitize.go b/cc/sanitize.go
index aaaf694..72ad6d7 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -408,16 +408,6 @@
 		return deps
 	}
 
-	if ctx.Device() {
-		if Bool(sanitize.Properties.Sanitize.Address) {
-			// Compiling asan and having libc_scudo in the same
-			// executable will cause the executable to crash.
-			// Remove libc_scudo since it is only used to override
-			// allocation functions which asan already overrides.
-			_, deps.SharedLibs = removeFromList("libc_scudo", deps.SharedLibs)
-		}
-	}
-
 	return deps
 }
 
diff --git a/cc/test.go b/cc/test.go
index 2439c94..09da976 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -40,6 +40,8 @@
 type TestOptions struct {
 	// The UID that you want to run the test as on a device.
 	Run_test_as *string
+	// A list of free-formed strings without spaces that categorize the test.
+	Test_suite_tag []string
 }
 
 type TestBinaryProperties struct {
@@ -357,6 +359,9 @@
 	if test.Properties.Test_options.Run_test_as != nil {
 		configs = append(configs, tradefed.Option{Name: "run-test-as", Value: String(test.Properties.Test_options.Run_test_as)})
 	}
+	for _, tag := range test.Properties.Test_options.Test_suite_tag {
+		configs = append(configs, tradefed.Option{Name: "test-suite-tag", Value: tag})
+	}
 	if test.Properties.Test_min_api_level != nil && test.Properties.Test_min_sdk_version != nil {
 		ctx.PropertyErrorf("test_min_api_level", "'test_min_api_level' and 'test_min_sdk_version' should not be set at the same time.")
 	} else if test.Properties.Test_min_api_level != nil {
diff --git a/cc/vndk.go b/cc/vndk.go
index 04b865f..4adf9d2 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -340,16 +340,24 @@
 	}
 }
 
-func IsForVndkApex(mctx android.BottomUpMutatorContext, m *Module) bool {
+// Sanity check for modules that mustn't be VNDK
+func shouldSkipVndkMutator(m *Module) bool {
 	if !m.Enabled() {
-		return false
+		return true
 	}
-
-	if !mctx.Device() {
-		return false
+	if !m.Device() {
+		// Skip non-device modules
+		return true
 	}
-
 	if m.Target().NativeBridge == android.NativeBridgeEnabled {
+		// Skip native_bridge modules
+		return true
+	}
+	return false
+}
+
+func IsForVndkApex(mctx android.BottomUpMutatorContext, m *Module) bool {
+	if shouldSkipVndkMutator(m) {
 		return false
 	}
 
@@ -383,11 +391,8 @@
 	if !ok {
 		return
 	}
-	if !m.Enabled() {
-		return
-	}
-	if m.Target().NativeBridge == android.NativeBridgeEnabled {
-		// Skip native_bridge modules
+
+	if shouldSkipVndkMutator(m) {
 		return
 	}
 
diff --git a/java/Android.bp b/java/Android.bp
index 2de1b8e..1fda7f7 100644
--- a/java/Android.bp
+++ b/java/Android.bp
@@ -37,6 +37,7 @@
         "jdeps.go",
         "java_resources.go",
         "kotlin.go",
+        "lint.go",
         "platform_compat_config.go",
         "plugin.go",
         "prebuilt_apis.go",
diff --git a/java/aar.go b/java/aar.go
index 28e388a..074ead4 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -102,6 +102,7 @@
 	sdkLibraries            []string
 	hasNoCode               bool
 	LoggingParent           string
+	resourceFiles           android.Paths
 
 	splitNames []string
 	splits     []split
@@ -275,6 +276,7 @@
 
 	var compiledResDirs []android.Paths
 	for _, dir := range resDirs {
+		a.resourceFiles = append(a.resourceFiles, dir.files...)
 		compiledResDirs = append(compiledResDirs, aapt2Compile(ctx, dir.dir, dir.files, compileFlags).Paths())
 	}
 
@@ -473,6 +475,10 @@
 	// apps manifests are handled by aapt, don't let Module see them
 	a.properties.Manifest = nil
 
+	a.linter.mergedManifest = a.aapt.mergedManifestFile
+	a.linter.manifest = a.aapt.manifestPath
+	a.linter.resources = a.aapt.resourceFiles
+
 	a.Module.extraProguardFlagFiles = append(a.Module.extraProguardFlagFiles,
 		a.proguardOptionsFile)
 
@@ -506,15 +512,13 @@
 func AndroidLibraryFactory() android.Module {
 	module := &AndroidLibrary{}
 
+	module.Module.addHostAndDeviceProperties()
 	module.AddProperties(
-		&module.Module.properties,
-		&module.Module.deviceProperties,
-		&module.Module.dexpreoptProperties,
-		&module.Module.protoProperties,
 		&module.aaptProperties,
 		&module.androidLibraryProperties)
 
 	module.androidLibraryProperties.BuildAAR = true
+	module.Module.linter.library = true
 
 	android.InitApexModule(module)
 	InitJavaModule(module, android.DeviceSupported)
diff --git a/java/androidmk_test.go b/java/androidmk_test.go
index 7daa624..d471fb7 100644
--- a/java/androidmk_test.go
+++ b/java/androidmk_test.go
@@ -169,3 +169,36 @@
 		t.Errorf("did not expect explicit DistFile, got %v", without_tag_entries[0].DistFile)
 	}
 }
+
+func TestJavaSdkLibrary_RequireXmlPermissionFile(t *testing.T) {
+	ctx, config := testJava(t, `
+		java_sdk_library {
+			name: "foo-shared_library",
+			srcs: ["a.java"],
+		}
+		java_sdk_library {
+			name: "foo-no_shared_library",
+			srcs: ["a.java"],
+			shared_library: false,
+		}
+		`)
+
+	// Verify the existence of internal modules
+	ctx.ModuleForTests("foo-shared_library.xml", "android_common")
+
+	testCases := []struct {
+		moduleName string
+		expected   []string
+	}{
+		{"foo-shared_library", []string{"foo-shared_library.xml"}},
+		{"foo-no_shared_library", nil},
+	}
+	for _, tc := range testCases {
+		mod := ctx.ModuleForTests(tc.moduleName, "android_common").Module()
+		entries := android.AndroidMkEntriesForTest(t, config, "", mod)[0]
+		actual := entries.EntryMap["LOCAL_REQUIRED_MODULES"]
+		if !reflect.DeepEqual(tc.expected, actual) {
+			t.Errorf("Unexpected required modules - expected: %q, actual: %q", tc.expected, actual)
+		}
+	}
+}
diff --git a/java/app.go b/java/app.go
index 74c92a6..c568516 100755
--- a/java/app.go
+++ b/java/app.go
@@ -151,7 +151,7 @@
 				"allow-prereleased": strconv.FormatBool(proptools.Bool(as.properties.Prerelease)),
 				"screen-densities":  screenDensities,
 				"sdk-version":       ctx.Config().PlatformSdkVersion(),
-				"stem":              ctx.ModuleName(),
+				"stem":              as.BaseModuleName(),
 			},
 		})
 }
@@ -742,6 +742,10 @@
 
 	a.proguardBuildActions(ctx)
 
+	a.linter.mergedManifest = a.aapt.mergedManifestFile
+	a.linter.manifest = a.aapt.manifestPath
+	a.linter.resources = a.aapt.resourceFiles
+
 	dexJarFile := a.dexBuildActions(ctx)
 
 	jniLibs, certificateDeps := collectAppDeps(ctx, a, a.shouldEmbedJnis(ctx), !Bool(a.appProperties.Jni_uses_platform_apis))
@@ -934,7 +938,7 @@
 }
 
 func (a *AndroidApp) IsNativeCoverageNeeded(ctx android.BaseModuleContext) bool {
-	return ctx.Device() && (ctx.DeviceConfig().NativeCoverageEnabled() || ctx.DeviceConfig().ClangCoverageEnabled())
+	return ctx.Device() && ctx.DeviceConfig().NativeCoverageEnabled()
 }
 
 func (a *AndroidApp) PreventInstall() {
@@ -963,11 +967,8 @@
 	module.Module.properties.Instrument = true
 	module.Module.properties.Installable = proptools.BoolPtr(true)
 
+	module.addHostAndDeviceProperties()
 	module.AddProperties(
-		&module.Module.properties,
-		&module.Module.deviceProperties,
-		&module.Module.dexpreoptProperties,
-		&module.Module.protoProperties,
 		&module.aaptProperties,
 		&module.appProperties,
 		&module.overridableAppProperties,
@@ -1082,12 +1083,10 @@
 	module.appProperties.Use_embedded_native_libs = proptools.BoolPtr(true)
 	module.appProperties.AlwaysPackageNativeLibs = true
 	module.Module.dexpreopter.isTest = true
+	module.Module.linter.test = true
 
+	module.addHostAndDeviceProperties()
 	module.AddProperties(
-		&module.Module.properties,
-		&module.Module.deviceProperties,
-		&module.Module.dexpreoptProperties,
-		&module.Module.protoProperties,
 		&module.aaptProperties,
 		&module.appProperties,
 		&module.appTestProperties,
@@ -1134,12 +1133,10 @@
 	module.appProperties.Use_embedded_native_libs = proptools.BoolPtr(true)
 	module.appProperties.AlwaysPackageNativeLibs = true
 	module.Module.dexpreopter.isTest = true
+	module.Module.linter.test = true
 
+	module.addHostAndDeviceProperties()
 	module.AddProperties(
-		&module.Module.properties,
-		&module.Module.deviceProperties,
-		&module.Module.dexpreoptProperties,
-		&module.Module.protoProperties,
 		&module.aaptProperties,
 		&module.appProperties,
 		&module.appTestHelperAppProperties,
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 68cfe9f..a0b7edf 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -121,6 +121,10 @@
 
 	// names of the output files used in args that will be generated
 	Out []string
+
+	// If set, metalava is sandboxed to only read files explicitly specified on the command
+	// line. Defaults to false.
+	Sandbox *bool
 }
 
 type ApiToCheck struct {
@@ -1224,6 +1228,21 @@
 	return module
 }
 
+func (d *Droidstubs) OutputFiles(tag string) (android.Paths, error) {
+	switch tag {
+	case "":
+		return android.Paths{d.stubsSrcJar}, nil
+	case ".docs.zip":
+		return android.Paths{d.docZip}, nil
+	case ".annotations.zip":
+		return android.Paths{d.annotationsZip}, nil
+	case ".api_versions.xml":
+		return android.Paths{d.apiVersionsXml}, nil
+	default:
+		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+	}
+}
+
 func (d *Droidstubs) ApiFilePath() android.Path {
 	return d.apiFilePath
 }
@@ -1306,6 +1325,7 @@
 			cmd.Flag("--exclude-documentation-from-stubs")
 		}
 	}
+	cmd.FlagWithArg("--hide ", "ShowingMemberInHiddenClass") // b/159121253 -- remove it once all the violations are fixed.
 }
 
 func (d *Droidstubs) annotationsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
@@ -1399,7 +1419,7 @@
 }
 
 func (d *Droidstubs) apiToXmlFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand) {
-	if Bool(d.properties.Jdiff_enabled) && !ctx.Config().IsPdkBuild() {
+	if Bool(d.properties.Jdiff_enabled) && !ctx.Config().IsPdkBuild() && d.apiFile != nil {
 		if d.apiFile.String() == "" {
 			ctx.ModuleErrorf("API signature file has to be specified in Metalava when jdiff is enabled.")
 		}
@@ -1419,41 +1439,25 @@
 }
 
 func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, javaVersion javaVersion, srcs android.Paths,
-	srcJarList android.Path, bootclasspath, classpath classpath, sourcepaths android.Paths, implicits android.Paths) *android.RuleBuilderCommand {
+	srcJarList android.Path, bootclasspath, classpath classpath, sourcepaths android.Paths, implicitsRsp android.WritablePath, sandbox bool) *android.RuleBuilderCommand {
 	// Metalava uses lots of memory, restrict the number of metalava jobs that can run in parallel.
 	rule.HighMem()
 	cmd := rule.Command()
-
-	var implicitsRsp android.WritablePath
-	if len(implicits) > 0 {
-		implicitsRsp = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"implicits.rsp")
-		impRule := android.NewRuleBuilder()
-		impCmd := impRule.Command()
-		// A dummy action that copies the ninja generated rsp file to a new location. This allows us to
-		// add a large number of inputs to a file without exceeding bash command length limits (which
-		// would happen if we use the WriteFile rule). The cp is needed because RuleBuilder sets the
-		// rsp file to be ${output}.rsp.
-		impCmd.Text("cp").FlagWithRspFileInputList("", implicits).Output(implicitsRsp)
-		impRule.Build(pctx, ctx, "implicitsGen", "implicits generation")
-		cmd.Implicits(implicits)
-		cmd.Implicit(implicitsRsp)
-	}
 	if ctx.Config().IsEnvTrue("RBE_METALAVA") {
 		rule.Remoteable(android.RemoteRuleSupports{RBE: true})
-		execStrategy := remoteexec.LocalExecStrategy
-		if v := ctx.Config().Getenv("RBE_METALAVA_EXEC_STRATEGY"); v != "" {
-			execStrategy = v
-		}
-		pool := "metalava"
-		if v := ctx.Config().Getenv("RBE_METALAVA_POOL"); v != "" {
-			pool = v
+		pool := ctx.Config().GetenvWithDefault("RBE_METALAVA_POOL", "metalava")
+		execStrategy := ctx.Config().GetenvWithDefault("RBE_METALAVA_EXEC_STRATEGY", remoteexec.LocalExecStrategy)
+		labels := map[string]string{"type": "compile", "lang": "java", "compiler": "metalava"}
+		if !sandbox {
+			execStrategy = remoteexec.LocalExecStrategy
+			labels["shallow"] = "true"
 		}
 		inputs := []string{android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "framework", "metalava.jar").String()}
 		if v := ctx.Config().Getenv("RBE_METALAVA_INPUTS"); v != "" {
 			inputs = append(inputs, strings.Split(v, ",")...)
 		}
 		cmd.Text((&remoteexec.REParams{
-			Labels:          map[string]string{"type": "compile", "lang": "java", "compiler": "metalava", "shallow": "true"},
+			Labels:          labels,
 			ExecStrategy:    execStrategy,
 			Inputs:          inputs,
 			RSPFile:         implicitsRsp.String(),
@@ -1467,8 +1471,17 @@
 		FlagWithArg("-encoding ", "UTF-8").
 		FlagWithArg("-source ", javaVersion.String()).
 		FlagWithRspFileInputList("@", srcs).
-		FlagWithInput("@", srcJarList).
-		FlagWithOutput("--strict-input-files:warn ", android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"violations.txt"))
+		FlagWithInput("@", srcJarList)
+
+	if javaHome := ctx.Config().Getenv("ANDROID_JAVA_HOME"); javaHome != "" {
+		cmd.Implicit(android.PathForSource(ctx, javaHome))
+	}
+
+	if sandbox {
+		cmd.FlagWithOutput("--strict-input-files ", android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"violations.txt"))
+	} else {
+		cmd.FlagWithOutput("--strict-input-files:warn ", android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"violations.txt"))
+	}
 
 	if implicitsRsp != nil {
 		cmd.FlagWithArg("--strict-input-files-exempt ", "@"+implicitsRsp.String())
@@ -1518,8 +1531,12 @@
 
 	srcJarList := zipSyncCmd(ctx, rule, srcJarDir, d.Javadoc.srcJars)
 
+	implicitsRsp := android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"implicits.rsp")
+
 	cmd := metalavaCmd(ctx, rule, javaVersion, d.Javadoc.srcFiles, srcJarList,
-		deps.bootClasspath, deps.classpath, d.Javadoc.sourcepaths, d.Javadoc.implicits)
+		deps.bootClasspath, deps.classpath, d.Javadoc.sourcepaths, implicitsRsp,
+		Bool(d.Javadoc.properties.Sandbox))
+	cmd.Implicits(d.Javadoc.implicits)
 
 	d.stubsFlags(ctx, cmd, stubsDir)
 
@@ -1638,6 +1655,16 @@
 		cmd.FlagWithArg("--error-message:compatibility:released ", msg)
 	}
 
+	impRule := android.NewRuleBuilder()
+	impCmd := impRule.Command()
+	// A dummy action that copies the ninja generated rsp file to a new location. This allows us to
+	// add a large number of inputs to a file without exceeding bash command length limits (which
+	// would happen if we use the WriteFile rule). The cp is needed because RuleBuilder sets the
+	// rsp file to be ${output}.rsp.
+	impCmd.Text("cp").FlagWithRspFileInputList("", cmd.GetImplicits()).Output(implicitsRsp)
+	impRule.Build(pctx, ctx, "implicitsGen", "implicits generation")
+	cmd.Implicit(implicitsRsp)
+
 	if generateStubs {
 		rule.Command().
 			BuiltTool(ctx, "soong_zip").
@@ -1820,13 +1847,19 @@
 			Flag("-XDignore.symbol.file").
 			FlagWithArg("-doclet ", "jdiff.JDiff").
 			FlagWithInput("-docletpath ", jdiff).
-			Flag("-quiet").
-			FlagWithArg("-newapi ", strings.TrimSuffix(d.apiXmlFile.Base(), d.apiXmlFile.Ext())).
-			FlagWithArg("-newapidir ", filepath.Dir(d.apiXmlFile.String())).
-			Implicit(d.apiXmlFile).
-			FlagWithArg("-oldapi ", strings.TrimSuffix(d.lastReleasedApiXmlFile.Base(), d.lastReleasedApiXmlFile.Ext())).
-			FlagWithArg("-oldapidir ", filepath.Dir(d.lastReleasedApiXmlFile.String())).
-			Implicit(d.lastReleasedApiXmlFile)
+			Flag("-quiet")
+
+		if d.apiXmlFile != nil {
+			cmd.FlagWithArg("-newapi ", strings.TrimSuffix(d.apiXmlFile.Base(), d.apiXmlFile.Ext())).
+				FlagWithArg("-newapidir ", filepath.Dir(d.apiXmlFile.String())).
+				Implicit(d.apiXmlFile)
+		}
+
+		if d.lastReleasedApiXmlFile != nil {
+			cmd.FlagWithArg("-oldapi ", strings.TrimSuffix(d.lastReleasedApiXmlFile.Base(), d.lastReleasedApiXmlFile.Ext())).
+				FlagWithArg("-oldapidir ", filepath.Dir(d.lastReleasedApiXmlFile.String())).
+				Implicit(d.lastReleasedApiXmlFile)
+		}
 
 		rule.Command().
 			BuiltTool(ctx, "soong_zip").
diff --git a/java/java.go b/java/java.go
index d7f09f9..7d32c34 100644
--- a/java/java.go
+++ b/java/java.go
@@ -44,7 +44,7 @@
 			PropertyName: "java_libs",
 		},
 		func(j *Library) android.Path {
-			implementationJars := j.ImplementationJars()
+			implementationJars := j.ImplementationAndResourcesJars()
 			if len(implementationJars) != 1 {
 				panic(fmt.Errorf("there must be only one implementation jar from %q", j.Name()))
 			}
@@ -253,6 +253,9 @@
 
 	// List of files to include in the META-INF/services folder of the resulting jar.
 	Services []string `android:"path,arch_variant"`
+
+	// If true, package the kotlin stdlib into the jar.  Defaults to true.
+	Static_kotlin_stdlib *bool `android:"arch_variant"`
 }
 
 type CompilerDeviceProperties struct {
@@ -471,6 +474,7 @@
 
 	hiddenAPI
 	dexpreopter
+	linter
 
 	// list of the xref extraction files
 	kytheFiles android.Paths
@@ -481,6 +485,22 @@
 	modulePaths []string
 }
 
+func (j *Module) addHostProperties() {
+	j.AddProperties(
+		&j.properties,
+		&j.protoProperties,
+	)
+}
+
+func (j *Module) addHostAndDeviceProperties() {
+	j.addHostProperties()
+	j.AddProperties(
+		&j.deviceProperties,
+		&j.dexpreoptProperties,
+		&j.linter.properties,
+	)
+}
+
 func (j *Module) OutputFiles(tag string) (android.Paths, error) {
 	switch tag {
 	case "":
@@ -559,6 +579,7 @@
 	certificateTag        = dependencyTag{name: "certificate"}
 	instrumentationForTag = dependencyTag{name: "instrumentation_for"}
 	usesLibTag            = dependencyTag{name: "uses-library"}
+	extraLintCheckTag     = dependencyTag{name: "extra-lint-check"}
 )
 
 func IsLibDepTag(depTag blueprint.DependencyTag) bool {
@@ -670,6 +691,8 @@
 
 func (j *Module) deps(ctx android.BottomUpMutatorContext) {
 	if ctx.Device() {
+		j.linter.deps(ctx)
+
 		sdkDep := decodeSdkDep(ctx, sdkContext(j))
 		if sdkDep.useDefaultLibs {
 			ctx.AddVariationDependencies(nil, bootClasspathTag, config.DefaultBootclasspathLibraries...)
@@ -1333,8 +1356,10 @@
 		if len(flags.processorPath) > 0 {
 			// Use kapt for annotation processing
 			kaptSrcJar := android.PathForModuleOut(ctx, "kapt", "kapt-sources.jar")
-			kotlinKapt(ctx, kaptSrcJar, kotlinSrcFiles, srcJars, flags)
+			kaptResJar := android.PathForModuleOut(ctx, "kapt", "kapt-res.jar")
+			kotlinKapt(ctx, kaptSrcJar, kaptResJar, kotlinSrcFiles, srcJars, flags)
 			srcJars = append(srcJars, kaptSrcJar)
+			kotlinJars = append(kotlinJars, kaptResJar)
 			// Disable annotation processing in javac, it's already been handled by kapt
 			flags.processorPath = nil
 			flags.processors = nil
@@ -1349,9 +1374,11 @@
 		// Make javac rule depend on the kotlinc rule
 		flags.classpath = append(flags.classpath, kotlinJar)
 
-		// Jar kotlin classes into the final jar after javac
 		kotlinJars = append(kotlinJars, kotlinJar)
-		kotlinJars = append(kotlinJars, deps.kotlinStdlib...)
+		// Jar kotlin classes into the final jar after javac
+		if BoolDefault(j.properties.Static_kotlin_stdlib, true) {
+			kotlinJars = append(kotlinJars, deps.kotlinStdlib...)
+		}
 	}
 
 	jars := append(android.Paths(nil), kotlinJars...)
@@ -1639,6 +1666,28 @@
 		outputFile = implementationAndResourcesJar
 	}
 
+	if ctx.Device() {
+		lintSDKVersionString := func(sdkSpec sdkSpec) string {
+			if v := sdkSpec.version; v.isNumbered() {
+				return v.String()
+			} else {
+				return ctx.Config().DefaultAppTargetSdk()
+			}
+		}
+
+		j.linter.name = ctx.ModuleName()
+		j.linter.srcs = srcFiles
+		j.linter.srcJars = srcJars
+		j.linter.classpath = append(append(android.Paths(nil), flags.bootClasspath...), flags.classpath...)
+		j.linter.classes = j.implementationJarFile
+		j.linter.minSdkVersion = lintSDKVersionString(j.minSdkVersion())
+		j.linter.targetSdkVersion = lintSDKVersionString(j.targetSdkVersion())
+		j.linter.compileSdkVersion = lintSDKVersionString(j.sdkVersion())
+		j.linter.javaLanguageLevel = flags.javaVersion.String()
+		j.linter.kotlinLanguageLevel = "1.3"
+		j.linter.lint(ctx)
+	}
+
 	ctx.CheckbuildFile(outputFile)
 
 	// Save the output file with no relative path so that it doesn't end up in a subdirectory when used as a resource
@@ -2051,12 +2100,8 @@
 func LibraryFactory() android.Module {
 	module := &Library{}
 
-	module.AddProperties(
-		&module.Module.properties,
-		&module.Module.deviceProperties,
-		&module.Module.dexpreoptProperties,
-		&module.Module.protoProperties,
-		&module.libraryProperties)
+	module.addHostAndDeviceProperties()
+	module.AddProperties(&module.libraryProperties)
 
 	module.initModuleAndImport(&module.ModuleBase)
 
@@ -2078,9 +2123,7 @@
 func LibraryHostFactory() android.Module {
 	module := &Library{}
 
-	module.AddProperties(
-		&module.Module.properties,
-		&module.Module.protoProperties)
+	module.addHostProperties()
 
 	module.Module.properties.Installable = proptools.BoolPtr(true)
 
@@ -2248,15 +2291,12 @@
 func TestFactory() android.Module {
 	module := &Test{}
 
-	module.AddProperties(
-		&module.Module.properties,
-		&module.Module.deviceProperties,
-		&module.Module.dexpreoptProperties,
-		&module.Module.protoProperties,
-		&module.testProperties)
+	module.addHostAndDeviceProperties()
+	module.AddProperties(&module.testProperties)
 
 	module.Module.properties.Installable = proptools.BoolPtr(true)
 	module.Module.dexpreopter.isTest = true
+	module.Module.linter.test = true
 
 	InitJavaModule(module, android.HostAndDeviceSupported)
 	return module
@@ -2266,15 +2306,12 @@
 func TestHelperLibraryFactory() android.Module {
 	module := &TestHelperLibrary{}
 
-	module.AddProperties(
-		&module.Module.properties,
-		&module.Module.deviceProperties,
-		&module.Module.dexpreoptProperties,
-		&module.Module.protoProperties,
-		&module.testHelperLibraryProperties)
+	module.addHostAndDeviceProperties()
+	module.AddProperties(&module.testHelperLibraryProperties)
 
 	module.Module.properties.Installable = proptools.BoolPtr(true)
 	module.Module.dexpreopter.isTest = true
+	module.Module.linter.test = true
 
 	InitJavaModule(module, android.HostAndDeviceSupported)
 	return module
@@ -2312,10 +2349,8 @@
 func TestHostFactory() android.Module {
 	module := &Test{}
 
-	module.AddProperties(
-		&module.Module.properties,
-		&module.Module.protoProperties,
-		&module.testProperties)
+	module.addHostProperties()
+	module.AddProperties(&module.testProperties)
 
 	module.Module.properties.Installable = proptools.BoolPtr(true)
 
@@ -2399,12 +2434,8 @@
 func BinaryFactory() android.Module {
 	module := &Binary{}
 
-	module.AddProperties(
-		&module.Module.properties,
-		&module.Module.deviceProperties,
-		&module.Module.dexpreoptProperties,
-		&module.Module.protoProperties,
-		&module.binaryProperties)
+	module.addHostAndDeviceProperties()
+	module.AddProperties(&module.binaryProperties)
 
 	module.Module.properties.Installable = proptools.BoolPtr(true)
 
@@ -2420,10 +2451,8 @@
 func BinaryHostFactory() android.Module {
 	module := &Binary{}
 
-	module.AddProperties(
-		&module.Module.properties,
-		&module.Module.protoProperties,
-		&module.binaryProperties)
+	module.addHostProperties()
+	module.AddProperties(&module.binaryProperties)
 
 	module.Module.properties.Installable = proptools.BoolPtr(true)
 
@@ -2870,6 +2899,7 @@
 		&DexImportProperties{},
 		&android.ApexProperties{},
 		&RuntimeResourceOverlayProperties{},
+		&LintProperties{},
 	)
 
 	android.InitDefaultsModule(module)
diff --git a/java/kotlin.go b/java/kotlin.go
index 9b160a0..673970b 100644
--- a/java/kotlin.go
+++ b/java/kotlin.go
@@ -90,7 +90,8 @@
 
 var kapt = pctx.AndroidRemoteStaticRule("kapt", android.RemoteRuleSupports{Goma: true},
 	blueprint.RuleParams{
-		Command: `rm -rf "$srcJarDir" "$kotlinBuildFile" "$kaptDir" && mkdir -p "$srcJarDir" "$kaptDir" && ` +
+		Command: `rm -rf "$srcJarDir" "$kotlinBuildFile" "$kaptDir" && ` +
+			`mkdir -p "$srcJarDir" "$kaptDir/sources" "$kaptDir/classes" && ` +
 			`${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
 			`${config.GenKotlinBuildFileCmd} $classpath "$name" "" $out.rsp $srcJarDir/list > $kotlinBuildFile &&` +
 			`${config.KotlincCmd} ${config.KotlincSuppressJDK9Warnings} ${config.JavacHeapFlags} $kotlincFlags ` +
@@ -105,6 +106,7 @@
 			`$kaptProcessor ` +
 			`-Xbuild-file=$kotlinBuildFile && ` +
 			`${config.SoongZipCmd} -jar -o $out -C $kaptDir/sources -D $kaptDir/sources && ` +
+			`${config.SoongZipCmd} -jar -o $classesJarOut -C $kaptDir/classes -D $kaptDir/classes && ` +
 			`rm -rf "$srcJarDir"`,
 		CommandDeps: []string{
 			"${config.KotlincCmd}",
@@ -118,13 +120,14 @@
 		RspfileContent: `$in`,
 	},
 	"kotlincFlags", "encodedJavacFlags", "kaptProcessorPath", "kaptProcessor",
-	"classpath", "srcJars", "srcJarDir", "kaptDir", "kotlinJvmTarget", "kotlinBuildFile", "name")
+	"classpath", "srcJars", "srcJarDir", "kaptDir", "kotlinJvmTarget", "kotlinBuildFile", "name",
+	"classesJarOut")
 
 // kotlinKapt performs Kotlin-compatible annotation processing.  It takes .kt and .java sources and srcjars, and runs
 // annotation processors over all of them, producing a srcjar of generated code in outputFile.  The srcjar should be
 // added as an additional input to kotlinc and javac rules, and the javac rule should have annotation processing
 // disabled.
-func kotlinKapt(ctx android.ModuleContext, outputFile android.WritablePath,
+func kotlinKapt(ctx android.ModuleContext, srcJarOutputFile, resJarOutputFile android.WritablePath,
 	srcFiles, srcJars android.Paths,
 	flags javaBuilderFlags) {
 
@@ -152,11 +155,12 @@
 	kotlinName = strings.ReplaceAll(kotlinName, "/", "__")
 
 	ctx.Build(pctx, android.BuildParams{
-		Rule:        kapt,
-		Description: "kapt",
-		Output:      outputFile,
-		Inputs:      srcFiles,
-		Implicits:   deps,
+		Rule:           kapt,
+		Description:    "kapt",
+		Output:         srcJarOutputFile,
+		ImplicitOutput: resJarOutputFile,
+		Inputs:         srcFiles,
+		Implicits:      deps,
 		Args: map[string]string{
 			"classpath":         flags.kotlincClasspath.FormJavaClassPath("-classpath"),
 			"kotlincFlags":      flags.kotlincFlags,
@@ -168,6 +172,7 @@
 			"kaptDir":           android.PathForModuleOut(ctx, "kapt/gen").String(),
 			"encodedJavacFlags": encodedJavacFlags,
 			"name":              kotlinName,
+			"classesJarOut":     resJarOutputFile.String(),
 		},
 	})
 }
diff --git a/java/lint.go b/java/lint.go
new file mode 100644
index 0000000..b73d6a5
--- /dev/null
+++ b/java/lint.go
@@ -0,0 +1,381 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+	"fmt"
+	"sort"
+
+	"android/soong/android"
+)
+
+type LintProperties struct {
+	// Controls for running Android Lint on the module.
+	Lint struct {
+
+		// If true, run Android Lint on the module.  Defaults to true.
+		Enabled *bool
+
+		// Flags to pass to the Android Lint tool.
+		Flags []string
+
+		// Checks that should be treated as fatal.
+		Fatal_checks []string
+
+		// Checks that should be treated as errors.
+		Error_checks []string
+
+		// Checks that should be treated as warnings.
+		Warning_checks []string
+
+		// Checks that should be skipped.
+		Disabled_checks []string
+
+		// Modules that provide extra lint checks
+		Extra_check_modules []string
+	}
+}
+
+type linter struct {
+	name                string
+	manifest            android.Path
+	mergedManifest      android.Path
+	srcs                android.Paths
+	srcJars             android.Paths
+	resources           android.Paths
+	classpath           android.Paths
+	classes             android.Path
+	extraLintCheckJars  android.Paths
+	test                bool
+	library             bool
+	minSdkVersion       string
+	targetSdkVersion    string
+	compileSdkVersion   string
+	javaLanguageLevel   string
+	kotlinLanguageLevel string
+	outputs             lintOutputs
+	properties          LintProperties
+}
+
+type lintOutputs struct {
+	html android.ModuleOutPath
+	text android.ModuleOutPath
+	xml  android.ModuleOutPath
+}
+
+func (l *linter) enabled() bool {
+	return BoolDefault(l.properties.Lint.Enabled, true)
+}
+
+func (l *linter) deps(ctx android.BottomUpMutatorContext) {
+	if !l.enabled() {
+		return
+	}
+
+	ctx.AddFarVariationDependencies(ctx.Config().BuildOSCommonTarget.Variations(), extraLintCheckTag, l.properties.Lint.Extra_check_modules...)
+}
+
+func (l *linter) writeLintProjectXML(ctx android.ModuleContext,
+	rule *android.RuleBuilder) (projectXMLPath, configXMLPath, cacheDir, homeDir android.WritablePath, deps android.Paths) {
+
+	var resourcesList android.WritablePath
+	if len(l.resources) > 0 {
+		// The list of resources may be too long to put on the command line, but
+		// we can't use the rsp file because it is already being used for srcs.
+		// Insert a second rule to write out the list of resources to a file.
+		resourcesList = android.PathForModuleOut(ctx, "lint", "resources.list")
+		resListRule := android.NewRuleBuilder()
+		resListRule.Command().Text("cp").FlagWithRspFileInputList("", l.resources).Output(resourcesList)
+		resListRule.Build(pctx, ctx, "lint_resources_list", "lint resources list")
+		deps = append(deps, l.resources...)
+	}
+
+	projectXMLPath = android.PathForModuleOut(ctx, "lint", "project.xml")
+	// Lint looks for a lint.xml file next to the project.xml file, give it one.
+	configXMLPath = android.PathForModuleOut(ctx, "lint", "lint.xml")
+	cacheDir = android.PathForModuleOut(ctx, "lint", "cache")
+	homeDir = android.PathForModuleOut(ctx, "lint", "home")
+
+	srcJarDir := android.PathForModuleOut(ctx, "lint-srcjars")
+	srcJarList := zipSyncCmd(ctx, rule, srcJarDir, l.srcJars)
+
+	cmd := rule.Command().
+		BuiltTool(ctx, "lint-project-xml").
+		FlagWithOutput("--project_out ", projectXMLPath).
+		FlagWithOutput("--config_out ", configXMLPath).
+		FlagWithArg("--name ", ctx.ModuleName())
+
+	if l.library {
+		cmd.Flag("--library")
+	}
+	if l.test {
+		cmd.Flag("--test")
+	}
+	if l.manifest != nil {
+		deps = append(deps, l.manifest)
+		cmd.FlagWithArg("--manifest ", l.manifest.String())
+	}
+	if l.mergedManifest != nil {
+		deps = append(deps, l.mergedManifest)
+		cmd.FlagWithArg("--merged_manifest ", l.mergedManifest.String())
+	}
+
+	// TODO(ccross): some of the files in l.srcs are generated sources and should be passed to
+	// lint separately.
+	cmd.FlagWithRspFileInputList("--srcs ", l.srcs)
+	deps = append(deps, l.srcs...)
+
+	cmd.FlagWithInput("--generated_srcs ", srcJarList)
+	deps = append(deps, l.srcJars...)
+
+	if resourcesList != nil {
+		cmd.FlagWithInput("--resources ", resourcesList)
+	}
+
+	if l.classes != nil {
+		deps = append(deps, l.classes)
+		cmd.FlagWithArg("--classes ", l.classes.String())
+	}
+
+	cmd.FlagForEachArg("--classpath ", l.classpath.Strings())
+	deps = append(deps, l.classpath...)
+
+	cmd.FlagForEachArg("--extra_checks_jar ", l.extraLintCheckJars.Strings())
+	deps = append(deps, l.extraLintCheckJars...)
+
+	cmd.FlagWithArg("--root_dir ", "$PWD")
+
+	// The cache tag in project.xml is relative to the root dir, or the project.xml file if
+	// the root dir is not set.
+	cmd.FlagWithArg("--cache_dir ", cacheDir.String())
+
+	cmd.FlagWithInput("@",
+		android.PathForSource(ctx, "build/soong/java/lint_defaults.txt"))
+
+	cmd.FlagForEachArg("--disable_check ", l.properties.Lint.Disabled_checks)
+	cmd.FlagForEachArg("--warning_check ", l.properties.Lint.Warning_checks)
+	cmd.FlagForEachArg("--error_check ", l.properties.Lint.Error_checks)
+	cmd.FlagForEachArg("--fatal_check ", l.properties.Lint.Fatal_checks)
+
+	return projectXMLPath, configXMLPath, cacheDir, homeDir, deps
+}
+
+// generateManifest adds a command to the rule to write a dummy manifest cat contains the
+// minSdkVersion and targetSdkVersion for modules (like java_library) that don't have a manifest.
+func (l *linter) generateManifest(ctx android.ModuleContext, rule *android.RuleBuilder) android.Path {
+	manifestPath := android.PathForModuleOut(ctx, "lint", "AndroidManifest.xml")
+
+	rule.Command().Text("(").
+		Text(`echo "<?xml version='1.0' encoding='utf-8'?>" &&`).
+		Text(`echo "<manifest xmlns:android='http://schemas.android.com/apk/res/android'" &&`).
+		Text(`echo "    android:versionCode='1' android:versionName='1' >" &&`).
+		Textf(`echo "  <uses-sdk android:minSdkVersion='%s' android:targetSdkVersion='%s'/>" &&`,
+			l.minSdkVersion, l.targetSdkVersion).
+		Text(`echo "</manifest>"`).
+		Text(") >").Output(manifestPath)
+
+	return manifestPath
+}
+
+func (l *linter) lint(ctx android.ModuleContext) {
+	if !l.enabled() {
+		return
+	}
+
+	extraLintCheckModules := ctx.GetDirectDepsWithTag(extraLintCheckTag)
+	for _, extraLintCheckModule := range extraLintCheckModules {
+		if dep, ok := extraLintCheckModule.(Dependency); ok {
+			l.extraLintCheckJars = append(l.extraLintCheckJars, dep.ImplementationAndResourcesJars()...)
+		} else {
+			ctx.PropertyErrorf("lint.extra_check_modules",
+				"%s is not a java module", ctx.OtherModuleName(extraLintCheckModule))
+		}
+	}
+
+	rule := android.NewRuleBuilder()
+
+	if l.manifest == nil {
+		manifest := l.generateManifest(ctx, rule)
+		l.manifest = manifest
+	}
+
+	projectXML, lintXML, cacheDir, homeDir, deps := l.writeLintProjectXML(ctx, rule)
+
+	l.outputs.html = android.PathForModuleOut(ctx, "lint-report.html")
+	l.outputs.text = android.PathForModuleOut(ctx, "lint-report.txt")
+	l.outputs.xml = android.PathForModuleOut(ctx, "lint-report.xml")
+
+	rule.Command().Text("rm -rf").Flag(cacheDir.String()).Flag(homeDir.String())
+	rule.Command().Text("mkdir -p").Flag(cacheDir.String()).Flag(homeDir.String())
+
+	rule.Command().
+		Text("(").
+		Flag("JAVA_OPTS=-Xmx2048m").
+		FlagWithArg("ANDROID_SDK_HOME=", homeDir.String()).
+		FlagWithInput("SDK_ANNOTATIONS=", annotationsZipPath(ctx)).
+		FlagWithInput("LINT_OPTS=-DLINT_API_DATABASE=", apiVersionsXmlPath(ctx)).
+		Tool(android.PathForSource(ctx, "prebuilts/cmdline-tools/tools/bin/lint")).
+		Implicit(android.PathForSource(ctx, "prebuilts/cmdline-tools/tools/lib/lint-classpath.jar")).
+		Flag("--quiet").
+		FlagWithInput("--project ", projectXML).
+		FlagWithInput("--config ", lintXML).
+		FlagWithOutput("--html ", l.outputs.html).
+		FlagWithOutput("--text ", l.outputs.text).
+		FlagWithOutput("--xml ", l.outputs.xml).
+		FlagWithArg("--compile-sdk-version ", l.compileSdkVersion).
+		FlagWithArg("--java-language-level ", l.javaLanguageLevel).
+		FlagWithArg("--kotlin-language-level ", l.kotlinLanguageLevel).
+		FlagWithArg("--url ", fmt.Sprintf(".=.,%s=out", android.PathForOutput(ctx).String())).
+		Flag("--exitcode").
+		Flags(l.properties.Lint.Flags).
+		Implicits(deps).
+		Text("|| (").Text("cat").Input(l.outputs.text).Text("; exit 7)").
+		Text(")")
+
+	rule.Command().Text("rm -rf").Flag(cacheDir.String()).Flag(homeDir.String())
+
+	rule.Build(pctx, ctx, "lint", "lint")
+}
+
+func (l *linter) lintOutputs() *lintOutputs {
+	return &l.outputs
+}
+
+type lintOutputIntf interface {
+	lintOutputs() *lintOutputs
+}
+
+var _ lintOutputIntf = (*linter)(nil)
+
+type lintSingleton struct {
+	htmlZip android.WritablePath
+	textZip android.WritablePath
+	xmlZip  android.WritablePath
+}
+
+func (l *lintSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+	l.generateLintReportZips(ctx)
+	l.copyLintDependencies(ctx)
+}
+
+func (l *lintSingleton) copyLintDependencies(ctx android.SingletonContext) {
+	if ctx.Config().UnbundledBuild() {
+		return
+	}
+
+	var frameworkDocStubs android.Module
+	ctx.VisitAllModules(func(m android.Module) {
+		if ctx.ModuleName(m) == "framework-doc-stubs" {
+			if frameworkDocStubs == nil {
+				frameworkDocStubs = m
+			} else {
+				ctx.Errorf("lint: multiple framework-doc-stubs modules found: %s and %s",
+					ctx.ModuleSubDir(m), ctx.ModuleSubDir(frameworkDocStubs))
+			}
+		}
+	})
+
+	if frameworkDocStubs == nil {
+		if !ctx.Config().AllowMissingDependencies() {
+			ctx.Errorf("lint: missing framework-doc-stubs")
+		}
+		return
+	}
+
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   android.Cp,
+		Input:  android.OutputFileForModule(ctx, frameworkDocStubs, ".annotations.zip"),
+		Output: annotationsZipPath(ctx),
+	})
+
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   android.Cp,
+		Input:  android.OutputFileForModule(ctx, frameworkDocStubs, ".api_versions.xml"),
+		Output: apiVersionsXmlPath(ctx),
+	})
+}
+
+func annotationsZipPath(ctx android.PathContext) android.WritablePath {
+	return android.PathForOutput(ctx, "lint", "annotations.zip")
+}
+
+func apiVersionsXmlPath(ctx android.PathContext) android.WritablePath {
+	return android.PathForOutput(ctx, "lint", "api_versions.xml")
+}
+
+func (l *lintSingleton) generateLintReportZips(ctx android.SingletonContext) {
+	var outputs []*lintOutputs
+	var dirs []string
+	ctx.VisitAllModules(func(m android.Module) {
+		if ctx.Config().EmbeddedInMake() && !m.ExportedToMake() {
+			return
+		}
+
+		if apex, ok := m.(android.ApexModule); ok && apex.NotAvailableForPlatform() && apex.IsForPlatform() {
+			// There are stray platform variants of modules in apexes that are not available for
+			// the platform, and they sometimes can't be built.  Don't depend on them.
+			return
+		}
+
+		if l, ok := m.(lintOutputIntf); ok {
+			outputs = append(outputs, l.lintOutputs())
+		}
+	})
+
+	dirs = android.SortedUniqueStrings(dirs)
+
+	zip := func(outputPath android.WritablePath, get func(*lintOutputs) android.Path) {
+		var paths android.Paths
+
+		for _, output := range outputs {
+			paths = append(paths, get(output))
+		}
+
+		sort.Slice(paths, func(i, j int) bool {
+			return paths[i].String() < paths[j].String()
+		})
+
+		rule := android.NewRuleBuilder()
+
+		rule.Command().BuiltTool(ctx, "soong_zip").
+			FlagWithOutput("-o ", outputPath).
+			FlagWithArg("-C ", android.PathForIntermediates(ctx).String()).
+			FlagWithRspFileInputList("-l ", paths)
+
+		rule.Build(pctx, ctx, outputPath.Base(), outputPath.Base())
+	}
+
+	l.htmlZip = android.PathForOutput(ctx, "lint-report-html.zip")
+	zip(l.htmlZip, func(l *lintOutputs) android.Path { return l.html })
+
+	l.textZip = android.PathForOutput(ctx, "lint-report-text.zip")
+	zip(l.textZip, func(l *lintOutputs) android.Path { return l.text })
+
+	l.xmlZip = android.PathForOutput(ctx, "lint-report-xml.zip")
+	zip(l.xmlZip, func(l *lintOutputs) android.Path { return l.xml })
+
+	ctx.Phony("lint-check", l.htmlZip, l.textZip, l.xmlZip)
+}
+
+func (l *lintSingleton) MakeVars(ctx android.MakeVarsContext) {
+	ctx.DistForGoal("lint-check", l.htmlZip, l.textZip, l.xmlZip)
+}
+
+var _ android.SingletonMakeVarsProvider = (*lintSingleton)(nil)
+
+func init() {
+	android.RegisterSingletonType("lint",
+		func() android.Singleton { return &lintSingleton{} })
+}
diff --git a/java/lint_defaults.txt b/java/lint_defaults.txt
new file mode 100644
index 0000000..0786b7c
--- /dev/null
+++ b/java/lint_defaults.txt
@@ -0,0 +1,78 @@
+# Treat LintError as fatal to catch invocation errors
+--fatal_check LintError
+
+# Downgrade existing errors to warnings
+--warning_check AppCompatResource                  # 55 occurences in 10 modules
+--warning_check AppLinkUrlError                    # 111 occurences in 53 modules
+--warning_check BlockedPrivateApi                  # 2 occurences in 2 modules
+--warning_check ByteOrderMark                      # 2 occurences in 2 modules
+--warning_check DuplicateActivity                  # 3 occurences in 3 modules
+--warning_check DuplicateDefinition                # 3623 occurences in 48 modules
+--warning_check DuplicateIds                       # 207 occurences in 22 modules
+--warning_check EllipsizeMaxLines                  # 12 occurences in 7 modules
+--warning_check ExtraTranslation                   # 21276 occurences in 27 modules
+--warning_check FontValidationError                # 4 occurences in 1 modules
+--warning_check FullBackupContent                  # 16 occurences in 1 modules
+--warning_check GetContentDescriptionOverride      # 3 occurences in 2 modules
+--warning_check HalfFloat                          # 31 occurences in 1 modules
+--warning_check HardcodedDebugMode                 # 99 occurences in 95 modules
+--warning_check ImpliedQuantity                    # 703 occurences in 27 modules
+--warning_check ImpliedTouchscreenHardware         # 4 occurences in 4 modules
+--warning_check IncludeLayoutParam                 # 11 occurences in 6 modules
+--warning_check Instantiatable                     # 145 occurences in 19 modules
+--warning_check InvalidPermission                  # 6 occurences in 4 modules
+--warning_check InvalidUsesTagAttribute            # 6 occurences in 2 modules
+--warning_check InvalidWakeLockTag                 # 111 occurences in 37 modules
+--warning_check JavascriptInterface                # 3 occurences in 2 modules
+--warning_check LibraryCustomView                  # 9 occurences in 4 modules
+--warning_check LogTagMismatch                     # 81 occurences in 13 modules
+--warning_check LongLogTag                         # 249 occurences in 12 modules
+--warning_check MenuTitle                          # 5 occurences in 4 modules
+--warning_check MissingClass                       # 537 occurences in 141 modules
+--warning_check MissingConstraints                 # 39 occurences in 10 modules
+--warning_check MissingDefaultResource             # 1257 occurences in 40 modules
+--warning_check MissingIntentFilterForMediaSearch  # 1 occurences in 1 modules
+--warning_check MissingLeanbackLauncher            # 3 occurences in 3 modules
+--warning_check MissingLeanbackSupport             # 2 occurences in 2 modules
+--warning_check MissingOnPlayFromSearch            # 1 occurences in 1 modules
+--warning_check MissingPermission                  # 2071 occurences in 150 modules
+--warning_check MissingPrefix                      # 46 occurences in 41 modules
+--warning_check MissingQuantity                    # 100 occurences in 1 modules
+--warning_check MissingSuperCall                   # 121 occurences in 36 modules
+--warning_check MissingTvBanner                    # 3 occurences in 3 modules
+--warning_check NamespaceTypo                      # 3 occurences in 3 modules
+--warning_check NetworkSecurityConfig              # 46 occurences in 12 modules
+--warning_check NewApi                             # 1996 occurences in 122 modules
+--warning_check NotSibling                         # 15 occurences in 10 modules
+--warning_check ObjectAnimatorBinding              # 14 occurences in 5 modules
+--warning_check OnClick                            # 49 occurences in 21 modules
+--warning_check Orientation                        # 77 occurences in 19 modules
+--warning_check Override                           # 385 occurences in 36 modules
+--warning_check ParcelCreator                      # 23 occurences in 2 modules
+--warning_check ProtectedPermissions               # 2413 occurences in 381 modules
+--warning_check Range                              # 80 occurences in 28 modules
+--warning_check RecyclerView                       # 1 occurences in 1 modules
+--warning_check ReferenceType                      # 4 occurences in 1 modules
+--warning_check ResourceAsColor                    # 19 occurences in 14 modules
+--warning_check RequiredSize                       # 52 occurences in 13 modules
+--warning_check ResAuto                            # 3 occurences in 1 modules
+--warning_check ResourceCycle                      # 37 occurences in 10 modules
+--warning_check ResourceType                       # 137 occurences in 36 modules
+--warning_check RestrictedApi                      # 28 occurences in 5 modules
+--warning_check RtlCompat                          # 9 occurences in 6 modules
+--warning_check ServiceCast                        # 3 occurences in 1 modules
+--warning_check SoonBlockedPrivateApi              # 5 occurences in 3 modules
+--warning_check StringFormatInvalid                # 148 occurences in 11 modules
+--warning_check StringFormatMatches                # 4800 occurences in 30 modules
+--warning_check UnknownId                          # 8 occurences in 7 modules
+--warning_check ValidFragment                      # 12 occurences in 5 modules
+--warning_check ValidRestrictions                  # 5 occurences in 1 modules
+--warning_check WebViewLayout                      # 3 occurences in 1 modules
+--warning_check WrongCall                          # 21 occurences in 3 modules
+--warning_check WrongConstant                      # 894 occurences in 126 modules
+--warning_check WrongManifestParent                # 10 occurences in 4 modules
+--warning_check WrongThread                        # 14 occurences in 6 modules
+--warning_check WrongViewCast                      # 1 occurences in 1 modules
+
+# TODO(b/158390965): remove this when lint doesn't crash
+--disable_check HardcodedDebugMode
diff --git a/java/plugin.go b/java/plugin.go
index a5e8292..947c286 100644
--- a/java/plugin.go
+++ b/java/plugin.go
@@ -24,10 +24,8 @@
 func PluginFactory() android.Module {
 	module := &Plugin{}
 
-	module.AddProperties(
-		&module.Module.properties,
-		&module.Module.protoProperties,
-		&module.pluginProperties)
+	module.addHostProperties()
+	module.AddProperties(&module.pluginProperties)
 
 	InitJavaModule(module, android.HostSupported)
 	return module
diff --git a/java/robolectric.go b/java/robolectric.go
index 3195615..c6b07a1 100644
--- a/java/robolectric.go
+++ b/java/robolectric.go
@@ -215,13 +215,13 @@
 func RobolectricTestFactory() android.Module {
 	module := &robolectricTest{}
 
+	module.addHostProperties()
 	module.AddProperties(
-		&module.Module.properties,
 		&module.Module.deviceProperties,
-		&module.Module.protoProperties,
 		&module.robolectricProperties)
 
 	module.Module.dexpreopter.isTest = true
+	module.Module.linter.test = true
 
 	InitJavaModule(module, android.DeviceSupported)
 	return module
diff --git a/java/sdk_library.go b/java/sdk_library.go
index e83ec0e..676557e 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -1042,8 +1042,10 @@
 		return nil
 	}
 	entriesList := module.Library.AndroidMkEntries()
-	entries := &entriesList[0]
-	entries.Required = append(entries.Required, module.xmlPermissionsModuleName())
+	if module.sharedLibrary() {
+		entries := &entriesList[0]
+		entries.Required = append(entries.Required, module.xmlPermissionsModuleName())
+	}
 	return entriesList
 }
 
@@ -1088,9 +1090,12 @@
 	props := struct {
 		Name       *string
 		Visibility []string
+		Instrument bool
 	}{
 		Name:       proptools.StringPtr(module.implLibraryModuleName()),
 		Visibility: module.sdkLibraryProperties.Impl_library_visibility,
+		// Set the instrument property to ensure it is instrumented when instrumentation is required.
+		Instrument: true,
 	}
 
 	properties := []interface{}{
@@ -1098,6 +1103,7 @@
 		&module.protoProperties,
 		&module.deviceProperties,
 		&module.dexpreoptProperties,
+		&module.linter.properties,
 		&props,
 		module.sdkComponentPropertiesForChildLibrary(),
 	}
@@ -1543,13 +1549,8 @@
 }
 
 func (module *SdkLibrary) InitSdkLibraryProperties() {
-	module.AddProperties(
-		&module.sdkLibraryProperties,
-		&module.properties,
-		&module.dexpreoptProperties,
-		&module.deviceProperties,
-		&module.protoProperties,
-	)
+	module.addHostAndDeviceProperties()
+	module.AddProperties(&module.sdkLibraryProperties)
 
 	module.initSdkLibraryComponent(&module.ModuleBase)
 
@@ -2192,8 +2193,12 @@
 			properties.Jars = jars
 			properties.SdkVersion = sdk.sdkVersionForStubsLibrary(ctx.SdkModuleContext(), apiScope)
 			properties.StubsSrcJar = paths.stubsSrcJar.Path()
-			properties.CurrentApiFile = paths.currentApiFilePath.Path()
-			properties.RemovedApiFile = paths.removedApiFilePath.Path()
+			if paths.currentApiFilePath.Valid() {
+				properties.CurrentApiFile = paths.currentApiFilePath.Path()
+			}
+			if paths.removedApiFilePath.Valid() {
+				properties.RemovedApiFile = paths.removedApiFilePath.Path()
+			}
 			s.Scopes[apiScope] = properties
 		}
 	}
diff --git a/python/python.go b/python/python.go
index 8b912be..a6c9e2a 100644
--- a/python/python.go
+++ b/python/python.go
@@ -251,6 +251,18 @@
 	return android.OptionalPathForPath(p.installer.(*binaryDecorator).path)
 }
 
+func (p *Module) OutputFiles(tag string) (android.Paths, error) {
+	switch tag {
+	case "":
+		if outputFile := p.installSource; outputFile.Valid() {
+			return android.Paths{outputFile.Path()}, nil
+		}
+		return android.Paths{}, nil
+	default:
+		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
+	}
+}
+
 func (p *Module) isEmbeddedLauncherEnabled(actual_version string) bool {
 	switch actual_version {
 	case pyVersion2:
diff --git a/rust/coverage.go b/rust/coverage.go
index 9be57dc..4e3977b 100644
--- a/rust/coverage.go
+++ b/rust/coverage.go
@@ -45,7 +45,7 @@
 
 func (cov *coverage) flags(ctx ModuleContext, flags Flags, deps PathDeps) (Flags, PathDeps) {
 
-	if !ctx.DeviceConfig().NativeCoverageEnabled() && !ctx.DeviceConfig().ClangCoverageEnabled() {
+	if !ctx.DeviceConfig().NativeCoverageEnabled() {
 		return flags, deps
 	}
 
diff --git a/rust/library.go b/rust/library.go
index 704c77b..3c948ea 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -305,8 +305,8 @@
 
 func (library *libraryDecorator) compilerDeps(ctx DepsContext, deps Deps) Deps {
 
-	// TODO(b/144861059) Remove if C libraries support dylib linkage in the future.
-	if !ctx.Host() && (library.static() || library.shared()) {
+	// TODO(b/155498724) Remove if C static libraries no longer require libstd as an rlib dependency.
+	if !ctx.Host() && library.static() {
 		library.setNoStdlibs()
 		for _, stdlib := range config.Stdlibs {
 			deps.Rlibs = append(deps.Rlibs, stdlib+".static")
diff --git a/rust/library_test.go b/rust/library_test.go
index 37dd541..9d2f6c0 100644
--- a/rust/library_test.go
+++ b/rust/library_test.go
@@ -17,6 +17,8 @@
 import (
 	"strings"
 	"testing"
+
+	"android/soong/android"
 )
 
 // Test that variants are being generated correctly, and that crate-types are correct.
@@ -115,16 +117,24 @@
 
 }
 
-func TestSharedLibraryFlags(t *testing.T) {
+func TestSharedLibrary(t *testing.T) {
 	ctx := testRust(t, `
-		rust_library_host {
+		rust_library {
 			name: "libfoo",
 			srcs: ["foo.rs"],
 			crate_name: "foo",
 		}`)
 
-	libfooShared := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_shared").Output("libfoo.so")
-	if !strings.Contains(libfooShared.Args["linkFlags"], "-Wl,-soname=libfoo.so") {
-		t.Errorf("missing expected -Wl,-soname linker flag for libfoo shared lib, linkFlags: %#v", libfooShared.Args["linkFlags"])
+	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared")
+
+	libfooOutput := libfoo.Output("libfoo.so")
+	if !strings.Contains(libfooOutput.Args["linkFlags"], "-Wl,-soname=libfoo.so") {
+		t.Errorf("missing expected -Wl,-soname linker flag for libfoo shared lib, linkFlags: %#v",
+			libfooOutput.Args["linkFlags"])
+	}
+
+	if !android.InList("libstd", libfoo.Module().(*Module).Properties.AndroidMkDylibs) {
+		t.Errorf("Non-static libstd dylib expected to be a dependency of Rust shared libraries. Dylib deps are: %#v",
+			libfoo.Module().(*Module).Properties.AndroidMkDylibs)
 	}
 }
diff --git a/rust/rust_test.go b/rust/rust_test.go
index fe21e3a..703aaed 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -88,6 +88,7 @@
 	config := testConfig(bp)
 
 	if coverage {
+		config.TestProductVariables.GcovCoverage = proptools.BoolPtr(true)
 		config.TestProductVariables.Native_coverage = proptools.BoolPtr(true)
 		config.TestProductVariables.NativeCoveragePaths = []string{"*"}
 	}
diff --git a/rust/test.go b/rust/test.go
index f616c06..416c557 100644
--- a/rust/test.go
+++ b/rust/test.go
@@ -57,7 +57,14 @@
 }
 
 func NewRustTest(hod android.HostOrDeviceSupported) (*Module, *testDecorator) {
-	module := newModule(hod, android.MultilibFirst)
+	// Build both 32 and 64 targets for device tests.
+	// Cannot build both for host tests yet if the test depends on
+	// something like proc-macro2 that cannot be built for both.
+	multilib := android.MultilibBoth
+	if hod != android.DeviceSupported && hod != android.HostAndDeviceSupported {
+		multilib = android.MultilibFirst
+	}
+	module := newModule(hod, multilib)
 
 	test := &testDecorator{
 		binaryDecorator: &binaryDecorator{
diff --git a/scripts/Android.bp b/scripts/Android.bp
index e848b50..1f55030 100644
--- a/scripts/Android.bp
+++ b/scripts/Android.bp
@@ -148,3 +148,9 @@
     ],
     test_suites: ["general-tests"],
 }
+
+python_binary_host {
+    name: "lint-project-xml",
+    main: "lint-project-xml.py",
+    srcs: ["lint-project-xml.py"],
+}
diff --git a/scripts/build-mainline-modules.sh b/scripts/build-mainline-modules.sh
index df763c8..9b68a82 100755
--- a/scripts/build-mainline-modules.sh
+++ b/scripts/build-mainline-modules.sh
@@ -21,6 +21,8 @@
   conscrypt-module-host-exports
   runtime-module-sdk
   runtime-module-host-exports
+  i18n-module-test-exports
+  i18n-module-sdk
 )
 
 # We want to create apex modules for all supported architectures.
diff --git a/scripts/lint-project-xml.py b/scripts/lint-project-xml.py
new file mode 100755
index 0000000..38c57ca
--- /dev/null
+++ b/scripts/lint-project-xml.py
@@ -0,0 +1,217 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2018 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.
+#
+
+"""This file generates project.xml and lint.xml files used to drive the Android Lint CLI tool."""
+
+import argparse
+
+
+def check_action(check_type):
+  """
+  Returns an action that appends a tuple of check_type and the argument to the dest.
+  """
+  class CheckAction(argparse.Action):
+    def __init__(self, option_strings, dest, nargs=None, **kwargs):
+      if nargs is not None:
+        raise ValueError("nargs must be None, was %s" % nargs)
+      super(CheckAction, self).__init__(option_strings, dest, **kwargs)
+    def __call__(self, parser, namespace, values, option_string=None):
+      checks = getattr(namespace, self.dest, [])
+      checks.append((check_type, values))
+      setattr(namespace, self.dest, checks)
+  return CheckAction
+
+
+def parse_args():
+  """Parse commandline arguments."""
+
+  def convert_arg_line_to_args(arg_line):
+    for arg in arg_line.split():
+      if arg.startswith('#'):
+        return
+      if not arg.strip():
+        continue
+      yield arg
+
+  parser = argparse.ArgumentParser(fromfile_prefix_chars='@')
+  parser.convert_arg_line_to_args = convert_arg_line_to_args
+  parser.add_argument('--project_out', dest='project_out',
+                      help='file to which the project.xml contents will be written.')
+  parser.add_argument('--config_out', dest='config_out',
+                      help='file to which the lint.xml contents will be written.')
+  parser.add_argument('--name', dest='name',
+                      help='name of the module.')
+  parser.add_argument('--srcs', dest='srcs', action='append', default=[],
+                      help='file containing whitespace separated list of source files.')
+  parser.add_argument('--generated_srcs', dest='generated_srcs', action='append', default=[],
+                      help='file containing whitespace separated list of generated source files.')
+  parser.add_argument('--resources', dest='resources', action='append', default=[],
+                      help='file containing whitespace separated list of resource files.')
+  parser.add_argument('--classes', dest='classes', action='append', default=[],
+                      help='file containing the module\'s classes.')
+  parser.add_argument('--classpath', dest='classpath', action='append', default=[],
+                      help='file containing classes from dependencies.')
+  parser.add_argument('--extra_checks_jar', dest='extra_checks_jars', action='append', default=[],
+                      help='file containing extra lint checks.')
+  parser.add_argument('--manifest', dest='manifest',
+                      help='file containing the module\'s manifest.')
+  parser.add_argument('--merged_manifest', dest='merged_manifest',
+                      help='file containing merged manifest for the module and its dependencies.')
+  parser.add_argument('--library', dest='library', action='store_true',
+                      help='mark the module as a library.')
+  parser.add_argument('--test', dest='test', action='store_true',
+                      help='mark the module as a test.')
+  parser.add_argument('--cache_dir', dest='cache_dir',
+                      help='directory to use for cached file.')
+  parser.add_argument('--root_dir', dest='root_dir',
+                      help='directory to use for root dir.')
+  group = parser.add_argument_group('check arguments', 'later arguments override earlier ones.')
+  group.add_argument('--fatal_check', dest='checks', action=check_action('fatal'), default=[],
+                     help='treat a lint issue as a fatal error.')
+  group.add_argument('--error_check', dest='checks', action=check_action('error'), default=[],
+                     help='treat a lint issue as an error.')
+  group.add_argument('--warning_check', dest='checks', action=check_action('warning'), default=[],
+                     help='treat a lint issue as a warning.')
+  group.add_argument('--disable_check', dest='checks', action=check_action('ignore'), default=[],
+                     help='disable a lint issue.')
+  return parser.parse_args()
+
+
+class NinjaRspFileReader:
+  """
+  Reads entries from a Ninja rsp file.  Ninja escapes any entries in the file that contain a
+  non-standard character by surrounding the whole entry with single quotes, and then replacing
+  any single quotes in the entry with the escape sequence '\''.
+  """
+
+  def __init__(self, filename):
+    self.f = open(filename, 'r')
+    self.r = self.character_reader(self.f)
+
+  def __iter__(self):
+    return self
+
+  def character_reader(self, f):
+    """Turns a file into a generator that returns one character at a time."""
+    while True:
+      c = f.read(1)
+      if c:
+        yield c
+      else:
+        return
+
+  def __next__(self):
+    entry = self.read_entry()
+    if entry:
+      return entry
+    else:
+      raise StopIteration
+
+  def read_entry(self):
+    c = next(self.r, "")
+    if not c:
+      return ""
+    elif c == "'":
+      return self.read_quoted_entry()
+    else:
+      entry = c
+      for c in self.r:
+        if c == " " or c == "\n":
+          break
+        entry += c
+      return entry
+
+  def read_quoted_entry(self):
+    entry = ""
+    for c in self.r:
+      if c == "'":
+        # Either the end of the quoted entry, or the beginning of an escape sequence, read the next
+        # character to find out.
+        c = next(self.r)
+        if not c or c == " " or c == "\n":
+          # End of the item
+          return entry
+        elif c == "\\":
+          # Escape sequence, expect a '
+          c = next(self.r)
+          if c != "'":
+            # Malformed escape sequence
+            raise "malformed escape sequence %s'\\%s" % (entry, c)
+          entry += "'"
+        else:
+          raise "malformed escape sequence %s'%s" % (entry, c)
+      else:
+        entry += c
+    raise "unterminated quoted entry %s" % entry
+
+
+def write_project_xml(f, args):
+  test_attr = "test='true' " if args.test else ""
+
+  f.write("<?xml version='1.0' encoding='utf-8'?>\n")
+  f.write("<project>\n")
+  if args.root_dir:
+    f.write("  <root dir='%s' />\n" % args.root_dir)
+  f.write("  <module name='%s' android='true' %sdesugar='full' >\n" % (args.name, "library='true' " if args.library else ""))
+  if args.manifest:
+    f.write("    <manifest file='%s' %s/>\n" % (args.manifest, test_attr))
+  if args.merged_manifest:
+    f.write("    <merged-manifest file='%s' %s/>\n" % (args.merged_manifest, test_attr))
+  for src_file in args.srcs:
+    for src in NinjaRspFileReader(src_file):
+      f.write("    <src file='%s' %s/>\n" % (src, test_attr))
+  for src_file in args.generated_srcs:
+    for src in NinjaRspFileReader(src_file):
+      f.write("    <src file='%s' generated='true' %s/>\n" % (src, test_attr))
+  for res_file in args.resources:
+    for res in NinjaRspFileReader(res_file):
+      f.write("    <resource file='%s' %s/>\n" % (res, test_attr))
+  for classes in args.classes:
+    f.write("    <classes jar='%s' />\n" % classes)
+  for classpath in args.classpath:
+    f.write("    <classpath jar='%s' />\n" % classpath)
+  for extra in args.extra_checks_jars:
+    f.write("    <lint-checks jar='%s' />\n" % extra)
+  f.write("  </module>\n")
+  if args.cache_dir:
+    f.write("  <cache dir='%s'/>\n" % args.cache_dir)
+  f.write("</project>\n")
+
+
+def write_config_xml(f, args):
+  f.write("<?xml version='1.0' encoding='utf-8'?>\n")
+  f.write("<lint>\n")
+  for check in args.checks:
+    f.write("  <issue id='%s' severity='%s' />\n" % (check[1], check[0]))
+  f.write("</lint>\n")
+
+
+def main():
+  """Program entry point."""
+  args = parse_args()
+
+  if args.project_out:
+    with open(args.project_out, 'w') as f:
+      write_project_xml(f, args)
+
+  if args.config_out:
+    with open(args.config_out, 'w') as f:
+      write_config_xml(f, args)
+
+
+if __name__ == '__main__':
+  main()
diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go
index 4a09081..123fe70 100644
--- a/sdk/cc_sdk_test.go
+++ b/sdk/cc_sdk_test.go
@@ -1976,3 +1976,83 @@
 }
 `))
 }
+
+func TestUniqueHostSoname(t *testing.T) {
+	// b/145598135 - Generating host snapshots for anything other than linux is not supported.
+	SkipIfNotLinux(t)
+
+	result := testSdkWithCc(t, `
+		sdk {
+			name: "mysdk",
+			host_supported: true,
+			native_shared_libs: ["mylib"],
+		}
+
+		cc_library {
+			name: "mylib",
+			host_supported: true,
+			unique_host_soname: true,
+		}
+	`)
+
+	result.CheckSnapshot("mysdk", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_library_shared {
+    name: "mysdk_mylib@current",
+    sdk_member_name: "mylib",
+    host_supported: true,
+    installable: false,
+    unique_host_soname: true,
+    target: {
+        android_arm64: {
+            srcs: ["android/arm64/lib/mylib.so"],
+        },
+        android_arm: {
+            srcs: ["android/arm/lib/mylib.so"],
+        },
+        linux_glibc_x86_64: {
+            srcs: ["linux_glibc/x86_64/lib/mylib-host.so"],
+        },
+        linux_glibc_x86: {
+            srcs: ["linux_glibc/x86/lib/mylib-host.so"],
+        },
+    },
+}
+
+cc_prebuilt_library_shared {
+    name: "mylib",
+    prefer: false,
+    host_supported: true,
+    unique_host_soname: true,
+    target: {
+        android_arm64: {
+            srcs: ["android/arm64/lib/mylib.so"],
+        },
+        android_arm: {
+            srcs: ["android/arm/lib/mylib.so"],
+        },
+        linux_glibc_x86_64: {
+            srcs: ["linux_glibc/x86_64/lib/mylib-host.so"],
+        },
+        linux_glibc_x86: {
+            srcs: ["linux_glibc/x86/lib/mylib-host.so"],
+        },
+    },
+}
+
+sdk_snapshot {
+    name: "mysdk@current",
+    host_supported: true,
+    native_shared_libs: ["mysdk_mylib@current"],
+}
+`),
+		checkAllCopyRules(`
+.intermediates/mylib/android_arm64_armv8-a_shared/mylib.so -> android/arm64/lib/mylib.so
+.intermediates/mylib/android_arm_armv7-a-neon_shared/mylib.so -> android/arm/lib/mylib.so
+.intermediates/mylib/linux_glibc_x86_64_shared/mylib-host.so -> linux_glibc/x86_64/lib/mylib-host.so
+.intermediates/mylib/linux_glibc_x86_shared/mylib-host.so -> linux_glibc/x86/lib/mylib-host.so
+`),
+	)
+}
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 77a4e94..7496b20 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -23,6 +23,7 @@
 
 	fs := map[string][]byte{
 		"Test.java":              nil,
+		"resource.test":          nil,
 		"aidl/foo/bar/Test.aidl": nil,
 
 		// For java_sdk_library
@@ -348,6 +349,7 @@
 		java_library {
 			name: "myjavalib",
 			srcs: ["Test.java"],
+			java_resources: ["resource.txt"],
 			aidl: {
 				export_include_dirs: ["aidl"],
 			},
@@ -381,7 +383,7 @@
 
 `),
 		checkAllCopyRules(`
-.intermediates/myjavalib/android_common/javac/myjavalib.jar -> java/myjavalib.jar
+.intermediates/myjavalib/android_common/withres/myjavalib.jar -> java/myjavalib.jar
 aidl/foo/bar/Test.aidl -> aidl/aidl/foo/bar/Test.aidl
 `),
 	)
diff --git a/sh/sh_binary.go b/sh/sh_binary.go
index 7bb267d..f28b2b6 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -107,6 +107,8 @@
 
 	testProperties TestProperties
 
+	installDir android.InstallPath
+
 	data       android.Paths
 	testConfig android.Path
 }
@@ -176,13 +178,13 @@
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 			func(entries *android.AndroidMkEntries) {
 				s.customAndroidMkEntries(entries)
+				entries.SetString("LOCAL_MODULE_RELATIVE_PATH", proptools.String(s.properties.Sub_dir))
 			},
 		},
 	}}
 }
 
 func (s *ShBinary) customAndroidMkEntries(entries *android.AndroidMkEntries) {
-	entries.SetString("LOCAL_MODULE_RELATIVE_PATH", proptools.String(s.properties.Sub_dir))
 	entries.SetString("LOCAL_MODULE_SUFFIX", "")
 	entries.SetString("LOCAL_MODULE_STEM", s.outputFilePath.Rel())
 	if len(s.properties.Symlinks) > 0 {
@@ -201,8 +203,14 @@
 	} else if !ctx.Host() && ctx.Config().HasMultilibConflict(ctx.Arch().ArchType) {
 		testDir = filepath.Join(testDir, ctx.Arch().ArchType.String())
 	}
-	installDir := android.PathForModuleInstall(ctx, testDir, proptools.String(s.properties.Sub_dir))
-	s.installedFile = ctx.InstallExecutable(installDir, s.outputFilePath.Base(), s.outputFilePath)
+	if s.SubDir() != "" {
+		// Don't add the module name to the installation path if sub_dir is specified for backward
+		// compatibility.
+		s.installDir = android.PathForModuleInstall(ctx, testDir, s.SubDir())
+	} else {
+		s.installDir = android.PathForModuleInstall(ctx, testDir, s.Name())
+	}
+	s.installedFile = ctx.InstallExecutable(s.installDir, s.outputFilePath.Base(), s.outputFilePath)
 
 	s.data = android.PathsForModuleSrc(ctx, s.testProperties.Data)
 
@@ -229,7 +237,7 @@
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 			func(entries *android.AndroidMkEntries) {
 				s.customAndroidMkEntries(entries)
-
+				entries.SetPath("LOCAL_MODULE_PATH", s.installDir.ToMakePath())
 				entries.AddStrings("LOCAL_COMPATIBILITY_SUITE", s.testProperties.Test_suites...)
 				if s.testConfig != nil {
 					entries.SetPath("LOCAL_FULL_TEST_CONFIG", s.testConfig)
diff --git a/sh/sh_binary_test.go b/sh/sh_binary_test.go
index 6c0d96a..3bfe611 100644
--- a/sh/sh_binary_test.go
+++ b/sh/sh_binary_test.go
@@ -55,7 +55,27 @@
 	return ctx, config
 }
 
-func TestShTestTestData(t *testing.T) {
+func TestShTestSubDir(t *testing.T) {
+	ctx, config := testShBinary(t, `
+		sh_test {
+			name: "foo",
+			src: "test.sh",
+			sub_dir: "foo_test"
+		}
+	`)
+
+	mod := ctx.ModuleForTests("foo", "android_arm64_armv8-a").Module().(*ShTest)
+
+	entries := android.AndroidMkEntriesForTest(t, config, "", mod)[0]
+
+	expectedPath := "/tmp/target/product/test_device/data/nativetest64/foo_test"
+	actualPath := entries.EntryMap["LOCAL_MODULE_PATH"][0]
+	if expectedPath != actualPath {
+		t.Errorf("Unexpected LOCAL_MODULE_PATH expected: %q, actual: %q", expectedPath, actualPath)
+	}
+}
+
+func TestShTest(t *testing.T) {
 	ctx, config := testShBinary(t, `
 		sh_test {
 			name: "foo",
@@ -71,10 +91,17 @@
 	mod := ctx.ModuleForTests("foo", "android_arm64_armv8-a").Module().(*ShTest)
 
 	entries := android.AndroidMkEntriesForTest(t, config, "", mod)[0]
-	expected := []string{":testdata/data1", ":testdata/sub/data2"}
-	actual := entries.EntryMap["LOCAL_TEST_DATA"]
-	if !reflect.DeepEqual(expected, actual) {
-		t.Errorf("Unexpected test data expected: %q, actual: %q", expected, actual)
+
+	expectedPath := "/tmp/target/product/test_device/data/nativetest64/foo"
+	actualPath := entries.EntryMap["LOCAL_MODULE_PATH"][0]
+	if expectedPath != actualPath {
+		t.Errorf("Unexpected LOCAL_MODULE_PATH expected: %q, actual: %q", expectedPath, actualPath)
+	}
+
+	expectedData := []string{":testdata/data1", ":testdata/sub/data2"}
+	actualData := entries.EntryMap["LOCAL_TEST_DATA"]
+	if !reflect.DeepEqual(expectedData, actualData) {
+		t.Errorf("Unexpected test data expected: %q, actual: %q", expectedData, actualData)
 	}
 }