Merge "Switch to clang-r383902 (11.0.1)." into rvc-dev
diff --git a/Android.bp b/Android.bp
index 352cf80..a81676f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -187,6 +187,7 @@
         "cc/rs.go",
         "cc/sanitize.go",
         "cc/sabi.go",
+        "cc/sdk.go",
         "cc/snapshot_utils.go",
         "cc/stl.go",
         "cc/strip.go",
diff --git a/android/apex.go b/android/apex.go
index c7410a1..19f58d3 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -18,6 +18,7 @@
 	"fmt"
 	"sort"
 	"strconv"
+	"strings"
 	"sync"
 
 	"github.com/google/blueprint"
@@ -395,3 +396,52 @@
 
 	m.AddProperties(&base.ApexProperties)
 }
+
+// A dependency info for a single ApexModule, either direct or transitive.
+type ApexModuleDepInfo struct {
+	// Name of the dependency
+	To string
+	// List of dependencies To belongs to. Includes APEX itself, if a direct dependency.
+	From []string
+	// Whether the dependency belongs to the final compiled APEX.
+	IsExternal bool
+}
+
+// A map of a dependency name to its ApexModuleDepInfo
+type DepNameToDepInfoMap map[string]ApexModuleDepInfo
+
+type ApexBundleDepsInfo struct {
+	fullListPath OutputPath
+}
+
+type ApexDepsInfoIntf interface {
+	FullListPath() Path
+}
+
+func (d *ApexBundleDepsInfo) FullListPath() Path {
+	return d.fullListPath
+}
+
+var _ ApexDepsInfoIntf = (*ApexBundleDepsInfo)(nil)
+
+func (d *ApexBundleDepsInfo) BuildDepsInfoLists(ctx ModuleContext, depInfos DepNameToDepInfoMap) {
+	var content strings.Builder
+	for _, key := range FirstUniqueStrings(SortedStringKeys(depInfos)) {
+		info := depInfos[key]
+		toName := info.To
+		if info.IsExternal {
+			toName = toName + " (external)"
+		}
+		fmt.Fprintf(&content, "%s <- %s\\n", toName, strings.Join(SortedUniqueStrings(info.From), ", "))
+	}
+
+	d.fullListPath = PathForModuleOut(ctx, "depsinfo", "fulllist.txt").OutputPath
+	ctx.Build(pctx, BuildParams{
+		Rule:        WriteFile,
+		Description: "Full Dependency Info",
+		Output:      d.fullListPath,
+		Args: map[string]string{
+			"content": content.String(),
+		},
+	})
+}
diff --git a/android/api_levels.go b/android/api_levels.go
index b6296d8..0872066 100644
--- a/android/api_levels.go
+++ b/android/api_levels.go
@@ -73,6 +73,7 @@
 			"O-MR1": 27,
 			"P":     28,
 			"Q":     29,
+			"R":     30,
 		}
 		for i, codename := range config.PlatformVersionActiveCodenames() {
 			apiLevelsMap[codename] = baseApiLevel + i
diff --git a/android/defaults.go b/android/defaults.go
index fd707a4..6a908ea 100644
--- a/android/defaults.go
+++ b/android/defaults.go
@@ -176,18 +176,18 @@
 	defaultsVisibility := module.defaultsVisibility()
 	module.AddProperties(&base.nameProperties, defaultsVisibility)
 
-	// The defaults_visibility property controls the visibility of a defaults module.
-	base.primaryVisibilityProperty =
-		newVisibilityProperty("defaults_visibility", &defaultsVisibility.Defaults_visibility)
-
 	// Unlike non-defaults modules the visibility property is not stored in m.base().commonProperties.
-	// Instead it is stored in a separate instance of commonProperties created above so use that.
+	// Instead it is stored in a separate instance of commonProperties created above so clear the
+	// existing list of properties.
+	clearVisibilityProperties(module)
+
+	// The defaults_visibility property controls the visibility of a defaults module so it must be
+	// set as the primary property, which also adds it to the list.
+	setPrimaryVisibilityProperty(module, "defaults_visibility", &defaultsVisibility.Defaults_visibility)
+
 	// The visibility property needs to be checked (but not parsed) by the visibility module during
-	// its checking phase and parsing phase.
-	base.visibilityPropertyInfo = []visibilityProperty{
-		base.primaryVisibilityProperty,
-		newVisibilityProperty("visibility", &commonProperties.Visibility),
-	}
+	// its checking phase and parsing phase so add it to the list as a normal property.
+	AddVisibilityProperty(module, "visibility", &commonProperties.Visibility)
 
 	base.module = module
 }
diff --git a/android/module.go b/android/module.go
index d9e655a..1f17eda 100644
--- a/android/module.go
+++ b/android/module.go
@@ -607,10 +607,8 @@
 	base.customizableProperties = m.GetProperties()
 
 	// The default_visibility property needs to be checked and parsed by the visibility module during
-	// its checking and parsing phases.
-	base.primaryVisibilityProperty =
-		newVisibilityProperty("visibility", &base.commonProperties.Visibility)
-	base.visibilityPropertyInfo = []visibilityProperty{base.primaryVisibilityProperty}
+	// its checking and parsing phases so make it the primary visibility property.
+	setPrimaryVisibilityProperty(m, "visibility", &base.commonProperties.Visibility)
 }
 
 func InitAndroidArchModule(m Module, hod HostOrDeviceSupported, defaultMultilib Multilib) {
diff --git a/android/neverallow.go b/android/neverallow.go
index 4d3a16f..cf09792 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -54,6 +54,7 @@
 	AddNeverAllowRules(createLibcoreRules()...)
 	AddNeverAllowRules(createMediaRules()...)
 	AddNeverAllowRules(createJavaDeviceForHostRules()...)
+	AddNeverAllowRules(createCcSdkVariantRules()...)
 }
 
 // Add a NeverAllow rule to the set of rules to apply.
@@ -177,6 +178,37 @@
 	}
 }
 
+func createCcSdkVariantRules() []Rule {
+	sdkVersionOnlyWhitelist := []string{
+		// derive_sdk_prefer32 has stem: "derive_sdk" which conflicts with the derive_sdk.
+		// This sometimes works because the APEX modules that contain derive_sdk and
+		// derive_sdk_prefer32 suppress the platform installation rules, but fails when
+		// the APEX modules contain the SDK variant and the platform variant still exists.
+		"frameworks/base/apex/sdkextensions/derive_sdk",
+	}
+
+	platformVariantPropertiesWhitelist := []string{
+		// android_native_app_glue and libRSSupport use native_window.h but target old
+		// sdk versions (minimum and 9 respectively) where libnativewindow didn't exist,
+		// so they can't add libnativewindow to shared_libs to get the header directory
+		// for the platform variant.  Allow them to use the platform variant
+		// property to set shared_libs.
+		"prebuilts/ndk",
+		"frameworks/rs",
+	}
+
+	return []Rule{
+		NeverAllow().
+			NotIn(sdkVersionOnlyWhitelist...).
+			WithMatcher("sdk_variant_only", isSetMatcherInstance).
+			Because("sdk_variant_only can only be used in whitelisted projects"),
+		NeverAllow().
+			NotIn(platformVariantPropertiesWhitelist...).
+			WithMatcher("platform.shared_libs", isSetMatcherInstance).
+			Because("platform variant properties can only be used in whitelisted projects"),
+	}
+}
+
 func neverallowMutator(ctx BottomUpMutatorContext) {
 	m, ok := ctx.Module().(Module)
 	if !ok {
@@ -268,6 +300,18 @@
 	return ".regexp(" + m.re.String() + ")"
 }
 
+type isSetMatcher struct{}
+
+func (m *isSetMatcher) Test(value string) bool {
+	return value != ""
+}
+
+func (m *isSetMatcher) String() string {
+	return ".is-set"
+}
+
+var isSetMatcherInstance = &isSetMatcher{}
+
 type ruleProperty struct {
 	fields  []string // e.x.: Vndk.Enabled
 	matcher ValueMatcher
diff --git a/android/neverallow_test.go b/android/neverallow_test.go
index 83b2250..2fc42e3 100644
--- a/android/neverallow_test.go
+++ b/android/neverallow_test.go
@@ -259,6 +259,50 @@
 				}`),
 		},
 	},
+	// CC sdk rule tests
+	{
+		name: `"sdk_variant_only" outside whitelist`,
+		fs: map[string][]byte{
+			"Android.bp": []byte(`
+				cc_library {
+					name: "outside_whitelist",
+					sdk_version: "current",
+					sdk_variant_only: true,
+				}`),
+		},
+		expectedErrors: []string{
+			`module "outside_whitelist": violates neverallow`,
+		},
+	},
+	{
+		name: `"sdk_variant_only: false" outside whitelist`,
+		fs: map[string][]byte{
+			"Android.bp": []byte(`
+				cc_library {
+					name: "outside_whitelist",
+					sdk_version: "current",
+					sdk_variant_only: false,
+				}`),
+		},
+		expectedErrors: []string{
+			`module "outside_whitelist": violates neverallow`,
+		},
+	},
+	{
+		name: `"platform" outside whitelist`,
+		fs: map[string][]byte{
+			"Android.bp": []byte(`
+				cc_library {
+					name: "outside_whitelist",
+					platform: {
+						shared_libs: ["libfoo"],
+					},
+				}`),
+		},
+		expectedErrors: []string{
+			`module "outside_whitelist": violates neverallow`,
+		},
+	},
 }
 
 func TestNeverallow(t *testing.T) {
@@ -299,6 +343,8 @@
 	Include_dirs     []string
 	Vendor_available *bool
 	Static_libs      []string
+	Sdk_version      *string
+	Sdk_variant_only *bool
 
 	Vndk struct {
 		Enabled                *bool
@@ -315,6 +361,10 @@
 			Cflags []string
 		}
 	}
+
+	Platform struct {
+		Shared_libs []string
+	}
 }
 
 type mockCcLibraryModule struct {
diff --git a/android/package.go b/android/package.go
index 077c4a4..bb5f4e7 100644
--- a/android/package.go
+++ b/android/package.go
@@ -101,10 +101,8 @@
 	module.AddProperties(&module.properties)
 
 	// The default_visibility property needs to be checked and parsed by the visibility module during
-	// its checking and parsing phases.
-	module.primaryVisibilityProperty =
-		newVisibilityProperty("default_visibility", &module.properties.Default_visibility)
-	module.visibilityPropertyInfo = []visibilityProperty{module.primaryVisibilityProperty}
+	// its checking and parsing phases so make it the primary visibility property.
+	setPrimaryVisibilityProperty(module, "default_visibility", &module.properties.Default_visibility)
 
 	return module
 }
diff --git a/android/visibility.go b/android/visibility.go
index 3f04123..1e3b91d 100644
--- a/android/visibility.go
+++ b/android/visibility.go
@@ -246,14 +246,8 @@
 	}
 
 	for _, v := range visibility {
-		ok, pkg, name := splitRule(v, currentPkg)
+		ok, pkg, name := splitRule(ctx, v, currentPkg, property)
 		if !ok {
-			// Visibility rule is invalid so ignore it. Keep going rather than aborting straight away to
-			// ensure all the rules on this module are checked.
-			ctx.PropertyErrorf(property,
-				"invalid visibility pattern %q must match"+
-					" //<package>:<module>, //<package> or :<module>",
-				v)
 			continue
 		}
 
@@ -301,21 +295,24 @@
 
 	// Parse the visibility rules that control access to the module and store them by id
 	// for use when enforcing the rules.
-	if visibility := m.visibility(); visibility != nil {
-		rule := parseRules(ctx, currentPkg, m.visibility())
-		if rule != nil {
-			moduleToVisibilityRuleMap(ctx.Config()).Store(qualifiedModuleId, rule)
+	primaryProperty := m.base().primaryVisibilityProperty
+	if primaryProperty != nil {
+		if visibility := primaryProperty.getStrings(); visibility != nil {
+			rule := parseRules(ctx, currentPkg, primaryProperty.getName(), visibility)
+			if rule != nil {
+				moduleToVisibilityRuleMap(ctx.Config()).Store(qualifiedModuleId, rule)
+			}
 		}
 	}
 }
 
-func parseRules(ctx BaseModuleContext, currentPkg string, visibility []string) compositeRule {
+func parseRules(ctx BaseModuleContext, currentPkg, property string, visibility []string) compositeRule {
 	rules := make(compositeRule, 0, len(visibility))
 	hasPrivateRule := false
 	hasPublicRule := false
 	hasNonPrivateRule := false
 	for _, v := range visibility {
-		ok, pkg, name := splitRule(v, currentPkg)
+		ok, pkg, name := splitRule(ctx, v, currentPkg, property)
 		if !ok {
 			continue
 		}
@@ -376,10 +373,16 @@
 	return !isAncestor("vendor", pkg)
 }
 
-func splitRule(ruleExpression string, currentPkg string) (bool, string, string) {
+func splitRule(ctx BaseModuleContext, ruleExpression string, currentPkg, property string) (bool, string, string) {
 	// Make sure that the rule is of the correct format.
 	matches := visibilityRuleRegexp.FindStringSubmatch(ruleExpression)
 	if ruleExpression == "" || matches == nil {
+		// Visibility rule is invalid so ignore it. Keep going rather than aborting straight away to
+		// ensure all the rules on this module are checked.
+		ctx.PropertyErrorf(property,
+			"invalid visibility pattern %q must match"+
+				" //<package>:<module>, //<package> or :<module>",
+			ruleExpression)
 		return false, "", ""
 	}
 
@@ -480,3 +483,28 @@
 
 	return rule.Strings()
 }
+
+// Clear the default visibility properties so they can be replaced.
+func clearVisibilityProperties(module Module) {
+	module.base().visibilityPropertyInfo = nil
+}
+
+// Add a property that contains visibility rules so that they are checked for
+// correctness.
+func AddVisibilityProperty(module Module, name string, stringsProperty *[]string) {
+	addVisibilityProperty(module, name, stringsProperty)
+}
+
+func addVisibilityProperty(module Module, name string, stringsProperty *[]string) visibilityProperty {
+	base := module.base()
+	property := newVisibilityProperty(name, stringsProperty)
+	base.visibilityPropertyInfo = append(base.visibilityPropertyInfo, property)
+	return property
+}
+
+// Set the primary visibility property.
+//
+// Also adds the property to the list of properties to be validated.
+func setPrimaryVisibilityProperty(module Module, name string, stringsProperty *[]string) {
+	module.base().primaryVisibilityProperty = addVisibilityProperty(module, name, stringsProperty)
+}
diff --git a/apex/apex.go b/apex/apex.go
index 6a0c4c1..45ba908 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -1276,12 +1276,6 @@
 	return false
 }
 
-type depInfo struct {
-	to         string
-	from       []string
-	isExternal bool
-}
-
 type apexBundle struct {
 	android.ModuleBase
 	android.DefaultableModuleBase
@@ -1316,7 +1310,7 @@
 	requiredDeps []string
 
 	// list of module names that this APEX is including (to be shown via *-deps-info target)
-	depInfos map[string]depInfo
+	android.ApexBundleDepsInfo
 
 	testApex        bool
 	vndkApex        bool
@@ -1871,38 +1865,11 @@
 		if String(a.properties.Min_sdk_version) == "" {
 			ctx.PropertyErrorf("updatable", "updatable APEXes should set min_sdk_version as well")
 		}
+
+		a.checkJavaStableSdkVersion(ctx)
 	}
 }
 
-// Collects the list of module names that directly or indirectly contributes to the payload of this APEX
-func (a *apexBundle) collectDepsInfo(ctx android.ModuleContext) {
-	a.depInfos = make(map[string]depInfo)
-	a.walkPayloadDeps(ctx, func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
-		if from.Name() == to.Name() {
-			// This can happen for cc.reuseObjTag. We are not interested in tracking this.
-			// As soon as the dependency graph crosses the APEX boundary, don't go further.
-			return !externalDep
-		}
-
-		if info, exists := a.depInfos[to.Name()]; exists {
-			if !android.InList(from.Name(), info.from) {
-				info.from = append(info.from, from.Name())
-			}
-			info.isExternal = info.isExternal && externalDep
-			a.depInfos[to.Name()] = info
-		} else {
-			a.depInfos[to.Name()] = depInfo{
-				to:         to.Name(),
-				from:       []string{from.Name()},
-				isExternal: externalDep,
-			}
-		}
-
-		// As soon as the dependency graph crosses the APEX boundary, don't go further.
-		return !externalDep
-	})
-}
-
 func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	buildFlattenedAsDefault := ctx.Config().FlattenApex() && !ctx.Config().UnbundledBuild()
 	switch a.properties.ApexType {
@@ -1941,8 +1908,6 @@
 	a.checkApexAvailability(ctx)
 	a.checkUpdatable(ctx)
 
-	a.collectDepsInfo(ctx)
-
 	handleSpecialLibs := !android.Bool(a.properties.Ignore_system_library_special_case)
 
 	// native lib dependencies
@@ -2247,6 +2212,23 @@
 	a.buildApexDependencyInfo(ctx)
 }
 
+// Enforce that Java deps of the apex are using stable SDKs to compile
+func (a *apexBundle) checkJavaStableSdkVersion(ctx android.ModuleContext) {
+	// Visit direct deps only. As long as we guarantee top-level deps are using
+	// stable SDKs, java's checkLinkType guarantees correct usage for transitive deps
+	ctx.VisitDirectDepsBlueprint(func(module blueprint.Module) {
+		tag := ctx.OtherModuleDependencyTag(module)
+		switch tag {
+		case javaLibTag, androidAppTag:
+			if m, ok := module.(interface{ CheckStableSdkVersion() error }); ok {
+				if err := m.CheckStableSdkVersion(); err != nil {
+					ctx.ModuleErrorf("cannot depend on \"%v\": %v", ctx.OtherModuleName(module), err)
+				}
+			}
+		}
+	})
+}
+
 func whitelistedApexAvailable(apex, moduleName string) bool {
 	key := apex
 	moduleName = normalizeModuleName(moduleName)
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 5451fa2..f0e7ff3 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -478,7 +478,7 @@
 	ensureListContains(t, noticeInputs, "custom_notice")
 	ensureListContains(t, noticeInputs, "custom_notice_for_static_lib")
 
-	depsInfo := strings.Split(ctx.ModuleForTests("myapex", "android_common_myapex_image").Output("myapex-deps-info.txt").Args["content"], "\\n")
+	depsInfo := strings.Split(ctx.ModuleForTests("myapex", "android_common_myapex_image").Output("depsinfo/fulllist.txt").Args["content"], "\\n")
 	ensureListContains(t, depsInfo, "myjar <- myapex")
 	ensureListContains(t, depsInfo, "mylib <- myapex")
 	ensureListContains(t, depsInfo, "mylib2 <- mylib")
@@ -784,7 +784,7 @@
 	// Ensure that libfoo stubs is not linking to libbar (since it is a stubs)
 	ensureNotContains(t, libFooStubsLdFlags, "libbar.so")
 
-	depsInfo := strings.Split(ctx.ModuleForTests("myapex2", "android_common_myapex2_image").Output("myapex2-deps-info.txt").Args["content"], "\\n")
+	depsInfo := strings.Split(ctx.ModuleForTests("myapex2", "android_common_myapex2_image").Output("depsinfo/fulllist.txt").Args["content"], "\\n")
 
 	ensureListContains(t, depsInfo, "mylib <- myapex2")
 	ensureListContains(t, depsInfo, "libbaz <- mylib")
@@ -1377,6 +1377,122 @@
 	`)
 }
 
+func TestJavaStableSdkVersion(t *testing.T) {
+	testCases := []struct {
+		name          string
+		expectedError string
+		bp            string
+	}{
+		{
+			name: "Non-updatable apex with non-stable dep",
+			bp: `
+				apex {
+					name: "myapex",
+					java_libs: ["myjar"],
+					key: "myapex.key",
+				}
+				apex_key {
+					name: "myapex.key",
+					public_key: "testkey.avbpubkey",
+					private_key: "testkey.pem",
+				}
+				java_library {
+					name: "myjar",
+					srcs: ["foo/bar/MyClass.java"],
+					sdk_version: "core_platform",
+					apex_available: ["myapex"],
+				}
+			`,
+		},
+		{
+			name: "Updatable apex with stable dep",
+			bp: `
+				apex {
+					name: "myapex",
+					java_libs: ["myjar"],
+					key: "myapex.key",
+					updatable: true,
+					min_sdk_version: "29",
+				}
+				apex_key {
+					name: "myapex.key",
+					public_key: "testkey.avbpubkey",
+					private_key: "testkey.pem",
+				}
+				java_library {
+					name: "myjar",
+					srcs: ["foo/bar/MyClass.java"],
+					sdk_version: "current",
+					apex_available: ["myapex"],
+				}
+			`,
+		},
+		{
+			name:          "Updatable apex with non-stable dep",
+			expectedError: "cannot depend on \"myjar\"",
+			bp: `
+				apex {
+					name: "myapex",
+					java_libs: ["myjar"],
+					key: "myapex.key",
+					updatable: true,
+				}
+				apex_key {
+					name: "myapex.key",
+					public_key: "testkey.avbpubkey",
+					private_key: "testkey.pem",
+				}
+				java_library {
+					name: "myjar",
+					srcs: ["foo/bar/MyClass.java"],
+					sdk_version: "core_platform",
+					apex_available: ["myapex"],
+				}
+			`,
+		},
+		{
+			name:          "Updatable apex with non-stable transitive dep",
+			expectedError: "compiles against Android API, but dependency \"transitive-jar\" is compiling against non-public Android API.",
+			bp: `
+				apex {
+					name: "myapex",
+					java_libs: ["myjar"],
+					key: "myapex.key",
+					updatable: true,
+				}
+				apex_key {
+					name: "myapex.key",
+					public_key: "testkey.avbpubkey",
+					private_key: "testkey.pem",
+				}
+				java_library {
+					name: "myjar",
+					srcs: ["foo/bar/MyClass.java"],
+					sdk_version: "current",
+					apex_available: ["myapex"],
+					static_libs: ["transitive-jar"],
+				}
+				java_library {
+					name: "transitive-jar",
+					srcs: ["foo/bar/MyClass.java"],
+					sdk_version: "core_platform",
+					apex_available: ["myapex"],
+				}
+			`,
+		},
+	}
+
+	for _, test := range testCases {
+		t.Run(test.name, func(t *testing.T) {
+			if test.expectedError == "" {
+				testApex(t, test.bp)
+			} else {
+				testApexError(t, test.expectedError, test.bp)
+			}
+		})
+	}
+}
+
 func TestFilesInSubDir(t *testing.T) {
 	ctx, _ := testApex(t, `
 		apex {
@@ -3323,18 +3439,20 @@
 		android_app {
 			name: "AppFoo",
 			srcs: ["foo/bar/MyClass.java"],
-			sdk_version: "none",
+			sdk_version: "current",
 			system_modules: "none",
 			jni_libs: ["libjni"],
+			stl: "none",
 			apex_available: [ "myapex" ],
 		}
 
 		android_app {
 			name: "AppFooPriv",
 			srcs: ["foo/bar/MyClass.java"],
-			sdk_version: "none",
+			sdk_version: "current",
 			system_modules: "none",
 			privileged: true,
+			stl: "none",
 			apex_available: [ "myapex" ],
 		}
 
@@ -3371,7 +3489,7 @@
 	}
 	// JNI libraries including transitive deps are
 	for _, jni := range []string{"libjni", "libfoo"} {
-		jniOutput := ctx.ModuleForTests(jni, "android_arm64_armv8-a_shared_myapex").Module().(*cc.Module).OutputFile()
+		jniOutput := ctx.ModuleForTests(jni, "android_arm64_armv8-a_sdk_shared_myapex").Module().(*cc.Module).OutputFile()
 		// ... embedded inside APK (jnilibs.zip)
 		ensureListContains(t, appZipRule.Implicits.Strings(), jniOutput.String())
 		// ... and not directly inside the APEX
@@ -4287,6 +4405,7 @@
 		java_library {
 			name: "some-updatable-apex-lib",
 			srcs: ["a.java"],
+			sdk_version: "current",
 			apex_available: [
 				"some-updatable-apex",
 			],
@@ -4295,12 +4414,14 @@
 		java_library {
 			name: "some-platform-lib",
 			srcs: ["a.java"],
+			sdk_version: "current",
 			installable: true,
 		}
 
 		java_library {
 			name: "some-art-lib",
 			srcs: ["a.java"],
+			sdk_version: "current",
 			apex_available: [
 				"com.android.art.something",
 			],
diff --git a/apex/builder.go b/apex/builder.go
index 0d7e801..a3e6929 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -679,29 +679,37 @@
 		return
 	}
 
-	var content strings.Builder
-	for _, key := range android.SortedStringKeys(a.depInfos) {
-		info := a.depInfos[key]
-		toName := info.to
-		if info.isExternal {
-			toName = toName + " (external)"
+	depInfos := android.DepNameToDepInfoMap{}
+	a.walkPayloadDeps(ctx, func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
+		if from.Name() == to.Name() {
+			// This can happen for cc.reuseObjTag. We are not interested in tracking this.
+			// As soon as the dependency graph crosses the APEX boundary, don't go further.
+			return !externalDep
 		}
-		fmt.Fprintf(&content, "%s <- %s\\n", toName, strings.Join(android.SortedUniqueStrings(info.from), ", "))
-	}
 
-	depsInfoFile := android.PathForOutput(ctx, a.Name()+"-deps-info.txt")
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        android.WriteFile,
-		Description: "Dependency Info",
-		Output:      depsInfoFile,
-		Args: map[string]string{
-			"content": content.String(),
-		},
+		if info, exists := depInfos[to.Name()]; exists {
+			if !android.InList(from.Name(), info.From) {
+				info.From = append(info.From, from.Name())
+			}
+			info.IsExternal = info.IsExternal && externalDep
+			depInfos[to.Name()] = info
+		} else {
+			depInfos[to.Name()] = android.ApexModuleDepInfo{
+				To:         to.Name(),
+				From:       []string{from.Name()},
+				IsExternal: externalDep,
+			}
+		}
+
+		// As soon as the dependency graph crosses the APEX boundary, don't go further.
+		return !externalDep
 	})
 
+	a.ApexBundleDepsInfo.BuildDepsInfoLists(ctx, depInfos)
+
 	ctx.Build(pctx, android.BuildParams{
 		Rule:   android.Phony,
 		Output: android.PathForPhony(ctx, a.Name()+"-deps-info"),
-		Inputs: []android.Path{depsInfoFile},
+		Inputs: []android.Path{a.ApexBundleDepsInfo.FullListPath()},
 	})
 }
diff --git a/apex/vndk_test.go b/apex/vndk_test.go
index 54447fa..523ac26 100644
--- a/apex/vndk_test.go
+++ b/apex/vndk_test.go
@@ -143,6 +143,7 @@
 				system_shared_libs: [],
 				stl: "none",
 				notice: "custom_notice",
+				sdk_version: "current",
 			}
 			cc_library {
 				name: "libprofile-clang-extras_ndk",
@@ -151,6 +152,7 @@
 				system_shared_libs: [],
 				stl: "none",
 				notice: "custom_notice",
+				sdk_version: "current",
 			}
 		`, func(fs map[string][]byte, config android.Config) {
 			config.TestProductVariables.Native_coverage = proptools.BoolPtr(true)
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 1c90aaf..5438b14 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -29,6 +29,7 @@
 	vendorSuffix       = ".vendor"
 	ramdiskSuffix      = ".ramdisk"
 	recoverySuffix     = ".recovery"
+	sdkSuffix          = ".sdk"
 )
 
 type AndroidMkContext interface {
@@ -103,6 +104,28 @@
 						}
 					}
 				}
+				if c.Properties.IsSdkVariant && c.Properties.SdkAndPlatformVariantVisibleToMake {
+					// Make the SDK variant uninstallable so that there are not two rules to install
+					// to the same location.
+					entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
+					// Add the unsuffixed name to SOONG_SDK_VARIANT_MODULES so that Make can rewrite
+					// dependencies to the .sdk suffix when building a module that uses the SDK.
+					entries.SetString("SOONG_SDK_VARIANT_MODULES",
+						"$(SOONG_SDK_VARIANT_MODULES) $(patsubst %.sdk,%,$(LOCAL_MODULE))")
+				}
+			},
+		},
+		ExtraFooters: []android.AndroidMkExtraFootersFunc{
+			func(w io.Writer, name, prefix, moduleDir string, entries *android.AndroidMkEntries) {
+				if c.Properties.IsSdkVariant && c.Properties.SdkAndPlatformVariantVisibleToMake &&
+					c.CcLibraryInterface() && c.Shared() {
+					// Using the SDK variant as a JNI library needs a copy of the .so that
+					// is not named .sdk.so so that it can be packaged into the APK with
+					// the right name.
+					fmt.Fprintln(w, "$(eval $(call copy-one-file,",
+						"$(LOCAL_BUILT_MODULE),",
+						"$(patsubst %.sdk.so,%.so,$(LOCAL_BUILT_MODULE))))")
+				}
 			},
 		},
 	}
@@ -397,6 +420,9 @@
 }
 
 func (installer *baseInstaller) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
+	if installer.path == (android.InstallPath{}) {
+		return
+	}
 	// Soong installation is only supported for host modules. Have Make
 	// installation trigger Soong installation.
 	if ctx.Target().Os.Class == android.Host {
diff --git a/cc/cc.go b/cc/cc.go
index a92495c..b237935 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -42,6 +42,7 @@
 	ctx.RegisterModuleType("cc_defaults", defaultsFactory)
 
 	ctx.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
+		ctx.BottomUp("sdk", sdkMutator).Parallel()
 		ctx.BottomUp("vndk", VndkMutator).Parallel()
 		ctx.BottomUp("link", LinkageMutator).Parallel()
 		ctx.BottomUp("ndk_api", NdkApiMutator).Parallel()
@@ -208,12 +209,16 @@
 	// Deprecated. true is the default, false is invalid.
 	Clang *bool `android:"arch_variant"`
 
-	// Minimum sdk version supported when compiling against the ndk
+	// Minimum sdk version supported when compiling against the ndk. Setting this property causes
+	// two variants to be built, one for the platform and one for apps.
 	Sdk_version *string
 
 	// Minimum sdk version that the artifact should support when it runs as part of mainline modules(APEX).
 	Min_sdk_version *string
 
+	// If true, always create an sdk variant and don't create a platform variant.
+	Sdk_variant_only *bool
+
 	AndroidMkSharedLibs       []string `blueprint:"mutated"`
 	AndroidMkStaticLibs       []string `blueprint:"mutated"`
 	AndroidMkRuntimeLibs      []string `blueprint:"mutated"`
@@ -255,6 +260,16 @@
 	SnapshotRuntimeLibs []string `blueprint:"mutated"`
 
 	Installable *bool
+
+	// Set by factories of module types that can only be referenced from variants compiled against
+	// the SDK.
+	AlwaysSdk bool `blueprint:"mutated"`
+
+	// Variant is an SDK variant created by sdkMutator
+	IsSdkVariant bool `blueprint:"mutated"`
+	// Set when both SDK and platform variants are exported to Make to trigger renaming the SDK
+	// variant to have a ".sdk" suffix.
+	SdkAndPlatformVariantVisibleToMake bool `blueprint:"mutated"`
 }
 
 type VendorProperties struct {
@@ -534,7 +549,10 @@
 }
 
 func (c *Module) SelectedStl() string {
-	return c.stl.Properties.SelectedStl
+	if c.stl != nil {
+		return c.stl.Properties.SelectedStl
+	}
+	return ""
 }
 
 func (c *Module) ToolchainLibrary() bool {
@@ -562,6 +580,10 @@
 	return String(c.Properties.Sdk_version)
 }
 
+func (c *Module) AlwaysSdk() bool {
+	return c.Properties.AlwaysSdk || Bool(c.Properties.Sdk_variant_only)
+}
+
 func (c *Module) IncludeDirs() android.Paths {
 	if c.linker != nil {
 		if library, ok := c.linker.(exportedFlagsProducer); ok {
@@ -821,6 +843,17 @@
 	return c.Properties.VndkVersion != ""
 }
 
+func (c *Module) canUseSdk() bool {
+	return c.Os() == android.Android && !c.UseVndk() && !c.InRamdisk() && !c.InRecovery()
+}
+
+func (c *Module) UseSdk() bool {
+	if c.canUseSdk() {
+		return String(c.Properties.Sdk_version) != ""
+	}
+	return false
+}
+
 func (c *Module) isCoverageVariant() bool {
 	return c.coverage.Properties.IsCoverageVariant
 }
@@ -1078,14 +1111,11 @@
 }
 
 func (ctx *moduleContextImpl) canUseSdk() bool {
-	return ctx.ctx.Device() && !ctx.useVndk() && !ctx.inRamdisk() && !ctx.inRecovery() && !ctx.ctx.Fuchsia()
+	return ctx.mod.canUseSdk()
 }
 
 func (ctx *moduleContextImpl) useSdk() bool {
-	if ctx.canUseSdk() {
-		return String(ctx.mod.Properties.Sdk_version) != ""
-	}
-	return false
+	return ctx.mod.UseSdk()
 }
 
 func (ctx *moduleContextImpl) sdkVersion() string {
@@ -1404,6 +1434,8 @@
 		c.Properties.SubName += ramdiskSuffix
 	} else if c.InRecovery() && !c.OnlyInRecovery() {
 		c.Properties.SubName += recoverySuffix
+	} else if c.Properties.IsSdkVariant && c.Properties.SdkAndPlatformVariantVisibleToMake {
+		c.Properties.SubName += sdkSuffix
 	}
 
 	ctx := &moduleContext{
diff --git a/cc/library.go b/cc/library.go
index c3b20b6..ce814b1 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -1224,7 +1224,7 @@
 	if Bool(library.Properties.Static_ndk_lib) && library.static() &&
 		!ctx.useVndk() && !ctx.inRamdisk() && !ctx.inRecovery() && ctx.Device() &&
 		library.baseLinker.sanitize.isUnsanitizedVariant() &&
-		!library.buildStubs() {
+		!library.buildStubs() && ctx.sdkVersion() == "" {
 		installPath := getNdkSysrootBase(ctx).Join(
 			ctx, "usr/lib", config.NDKTriple(ctx.toolchain()), file.Base())
 
diff --git a/cc/linkable.go b/cc/linkable.go
index 9147681..4a70d48 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -47,12 +47,14 @@
 	InRecovery() bool
 	OnlyInRecovery() bool
 
+	UseSdk() bool
 	UseVndk() bool
 	MustUseVendorVariant() bool
 	IsVndk() bool
 	HasVendorVariant() bool
 
 	SdkVersion() string
+	AlwaysSdk() bool
 
 	ToolchainLibrary() bool
 	NdkPrebuiltStl() bool
diff --git a/cc/linker.go b/cc/linker.go
index aa2d0ab..9b2c1e7 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -158,6 +158,13 @@
 			// the ramdisk variant of the C/C++ module.
 			Exclude_static_libs []string
 		}
+		Platform struct {
+			// list of shared libs that should be use to build the platform variant
+			// of a module that sets sdk_version.  This should rarely be necessary,
+			// in most cases the same libraries are available for the SDK and platform
+			// variants.
+			Shared_libs []string
+		}
 	}
 
 	// make android::build:GetBuildNumber() available containing the build ID.
@@ -255,6 +262,10 @@
 		deps.WholeStaticLibs = removeListFromList(deps.WholeStaticLibs, linker.Properties.Target.Recovery.Exclude_static_libs)
 	}
 
+	if !ctx.useSdk() {
+		deps.SharedLibs = append(deps.SharedLibs, linker.Properties.Target.Platform.Shared_libs...)
+	}
+
 	if ctx.toolchain().Bionic() {
 		// libclang_rt.builtins and libatomic have to be last on the command line
 		if !Bool(linker.Properties.No_libcrt) {
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index eb8e9d3..119ca40 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -385,6 +385,9 @@
 	module.linker = stub
 	module.installer = stub
 
+	module.Properties.AlwaysSdk = true
+	module.Properties.Sdk_version = StringPtr("current")
+
 	module.AddProperties(&stub.properties, &library.MutatedProperties)
 
 	return module
diff --git a/cc/ndk_prebuilt.go b/cc/ndk_prebuilt.go
index 3a630d2..c4d7708 100644
--- a/cc/ndk_prebuilt.go
+++ b/cc/ndk_prebuilt.go
@@ -76,6 +76,8 @@
 			baseLinker: NewBaseLinker(nil),
 		},
 	}
+	module.Properties.AlwaysSdk = true
+	module.Properties.Sdk_version = StringPtr("current")
 	module.Properties.HideFromMake = true
 	return module.Init()
 }
@@ -125,10 +127,9 @@
 		libraryDecorator: library,
 	}
 	module.installer = nil
-	minVersionString := "minimum"
-	noStlString := "none"
-	module.Properties.Sdk_version = &minVersionString
-	module.stl.Properties.Stl = &noStlString
+	module.Properties.Sdk_version = StringPtr("minimum")
+	module.Properties.AlwaysSdk = true
+	module.stl.Properties.Stl = StringPtr("none")
 	return module.Init()
 }
 
@@ -145,6 +146,9 @@
 	}
 	module.installer = nil
 	module.Properties.HideFromMake = true
+	module.Properties.AlwaysSdk = true
+	module.Properties.Sdk_version = StringPtr("current")
+	module.stl.Properties.Stl = StringPtr("none")
 	module.ModuleBase.EnableNativeBridgeSupportByDefault()
 	return module.Init()
 }
diff --git a/cc/sanitize.go b/cc/sanitize.go
index f12edc6..371f270 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -1041,7 +1041,7 @@
 					}
 
 					// Export the static lib name to make
-					if c.static() {
+					if c.static() && c.ExportedToMake() {
 						if t == cfi {
 							appendStringSync(c.Name(), cfiStaticLibs(mctx.Config()), &cfiStaticLibsMutex)
 						} else if t == hwasan {
diff --git a/cc/sdk.go b/cc/sdk.go
new file mode 100644
index 0000000..d05a04a
--- /dev/null
+++ b/cc/sdk.go
@@ -0,0 +1,65 @@
+// 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 cc
+
+import (
+	"android/soong/android"
+	"android/soong/genrule"
+)
+
+// sdkMutator sets a creates a platform and an SDK variant for modules
+// that set sdk_version, and ignores sdk_version for the platform
+// variant.  The SDK variant will be used for embedding in APKs
+// that may be installed on older platforms.  Apexes use their own
+// variants that enforce backwards compatibility.
+func sdkMutator(ctx android.BottomUpMutatorContext) {
+	if ctx.Os() != android.Android {
+		return
+	}
+
+	switch m := ctx.Module().(type) {
+	case LinkableInterface:
+		if m.AlwaysSdk() {
+			if !m.UseSdk() {
+				ctx.ModuleErrorf("UseSdk() must return true when AlwaysSdk is set, did the factory forget to set Sdk_version?")
+			}
+			ctx.CreateVariations("sdk")
+		} else if m.UseSdk() {
+			modules := ctx.CreateVariations("", "sdk")
+			modules[0].(*Module).Properties.Sdk_version = nil
+			modules[1].(*Module).Properties.IsSdkVariant = true
+
+			if ctx.Config().UnbundledBuild() {
+				modules[0].(*Module).Properties.HideFromMake = true
+			} else {
+				modules[1].(*Module).Properties.SdkAndPlatformVariantVisibleToMake = true
+				modules[1].(*Module).Properties.PreventInstall = true
+			}
+			ctx.AliasVariation("")
+		} else {
+			ctx.CreateVariations("")
+			ctx.AliasVariation("")
+		}
+	case *genrule.Module:
+		if p, ok := m.Extra.(*GenruleExtraProperties); ok {
+			if String(p.Sdk_version) != "" {
+				ctx.CreateVariations("", "sdk")
+			} else {
+				ctx.CreateVariations("")
+			}
+			ctx.AliasVariation("")
+		}
+	}
+}
diff --git a/cc/sdk_test.go b/cc/sdk_test.go
new file mode 100644
index 0000000..5a3c181
--- /dev/null
+++ b/cc/sdk_test.go
@@ -0,0 +1,102 @@
+// 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 cc
+
+import (
+	"testing"
+
+	"android/soong/android"
+)
+
+func TestSdkMutator(t *testing.T) {
+	bp := `
+		cc_library {
+			name: "libsdk",
+			shared_libs: ["libsdkdep"],
+			sdk_version: "current",
+			stl: "c++_shared",
+		}
+
+		cc_library {
+			name: "libsdkdep",
+			sdk_version: "current",
+			stl: "c++_shared",
+		}
+
+		cc_library {
+			name: "libplatform",
+			shared_libs: ["libsdk"],
+			stl: "libc++",
+		}
+
+		cc_binary {
+			name: "platformbinary",
+			shared_libs: ["libplatform"],
+			stl: "libc++",
+		}
+
+		cc_binary {
+			name: "sdkbinary",
+			shared_libs: ["libsdk"],
+			sdk_version: "current",
+			stl: "libc++",
+		}
+	`
+
+	assertDep := func(t *testing.T, from, to android.TestingModule) {
+		t.Helper()
+		found := false
+
+		var toFile android.Path
+		m := to.Module().(*Module)
+		if toc := m.Toc(); toc.Valid() {
+			toFile = toc.Path()
+		} else {
+			toFile = m.outputFile.Path()
+		}
+
+		rule := from.Description("link")
+		for _, dep := range rule.Implicits {
+			if dep.String() == toFile.String() {
+				found = true
+			}
+		}
+		if !found {
+			t.Errorf("expected %q in %q", toFile.String(), rule.Implicits.Strings())
+		}
+	}
+
+	ctx := testCc(t, bp)
+
+	libsdkNDK := ctx.ModuleForTests("libsdk", "android_arm64_armv8-a_sdk_shared")
+	libsdkPlatform := ctx.ModuleForTests("libsdk", "android_arm64_armv8-a_shared")
+	libsdkdepNDK := ctx.ModuleForTests("libsdkdep", "android_arm64_armv8-a_sdk_shared")
+	libsdkdepPlatform := ctx.ModuleForTests("libsdkdep", "android_arm64_armv8-a_shared")
+	libplatform := ctx.ModuleForTests("libplatform", "android_arm64_armv8-a_shared")
+	platformbinary := ctx.ModuleForTests("platformbinary", "android_arm64_armv8-a")
+	sdkbinary := ctx.ModuleForTests("sdkbinary", "android_arm64_armv8-a_sdk")
+
+	libcxxNDK := ctx.ModuleForTests("ndk_libc++_shared", "android_arm64_armv8-a_sdk_shared")
+	libcxxPlatform := ctx.ModuleForTests("libc++", "android_arm64_armv8-a_shared")
+
+	assertDep(t, libsdkNDK, libsdkdepNDK)
+	assertDep(t, libsdkPlatform, libsdkdepPlatform)
+	assertDep(t, libplatform, libsdkPlatform)
+	assertDep(t, platformbinary, libplatform)
+	assertDep(t, sdkbinary, libsdkNDK)
+
+	assertDep(t, libsdkNDK, libcxxNDK)
+	assertDep(t, libsdkPlatform, libcxxPlatform)
+}
diff --git a/cc/stl.go b/cc/stl.go
index eda8a4f..34ff30c 100644
--- a/cc/stl.go
+++ b/cc/stl.go
@@ -115,9 +115,13 @@
 			switch s {
 			case "libc++", "libc++_static":
 				return s
+			case "c++_shared":
+				return "libc++"
+			case "c++_static":
+				return "libc++_static"
 			case "none":
 				return ""
-			case "":
+			case "", "system":
 				if ctx.static() {
 					return "libc++_static"
 				} else {
diff --git a/cc/testing.go b/cc/testing.go
index f85795b..53f0995 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -152,6 +152,7 @@
 			name: "libgcc_stripped",
 			vendor_available: true,
 			recovery_available: true,
+			sdk_version: "current",
 			src: "",
 		}
 
@@ -169,6 +170,7 @@
 		llndk_library {
 			name: "libc",
 			symbol_file: "",
+			sdk_version: "current",
 		}
 		cc_library {
 			name: "libm",
@@ -188,6 +190,7 @@
 		llndk_library {
 			name: "libm",
 			symbol_file: "",
+			sdk_version: "current",
 		}
 		cc_library {
 			name: "libdl",
@@ -207,6 +210,7 @@
 		llndk_library {
 			name: "libdl",
 			symbol_file: "",
+			sdk_version: "current",
 		}
 		cc_library {
 			name: "libft2",
@@ -219,6 +223,7 @@
 			name: "libft2",
 			symbol_file: "",
 			vendor_available: false,
+			sdk_version: "current",
 		}
 		cc_library {
 			name: "libc++_static",
@@ -375,6 +380,16 @@
 			sdk_version: "27",
 		}
 
+		ndk_prebuilt_object {
+			name: "ndk_crtbegin_dynamic.27",
+			sdk_version: "27",
+		}
+
+		ndk_prebuilt_object {
+			name: "ndk_crtend_android.27",
+			sdk_version: "27",
+		}
+
 		ndk_prebuilt_shared_stl {
 			name: "ndk_libc++_shared",
 		}
diff --git a/cc/toolchain_library.go b/cc/toolchain_library.go
index dfc6f76..042e012 100644
--- a/cc/toolchain_library.go
+++ b/cc/toolchain_library.go
@@ -67,6 +67,7 @@
 	module.stl = nil
 	module.sanitize = nil
 	module.installer = nil
+	module.Properties.Sdk_version = StringPtr("current")
 	return module.Init()
 }
 
diff --git a/java/app.go b/java/app.go
index 951ce16..0c94e23 100755
--- a/java/app.go
+++ b/java/app.go
@@ -230,6 +230,13 @@
 	for _, jniTarget := range ctx.MultiTargets() {
 		variation := append(jniTarget.Variations(),
 			blueprint.Variation{Mutator: "link", Variation: "shared"})
+
+		// If the app builds against an Android SDK use the SDK variant of JNI dependencies
+		// unless jni_uses_platform_apis is set.
+		if a.sdkVersion().specified() && a.sdkVersion().kind != sdkCorePlatform &&
+			!Bool(a.appProperties.Jni_uses_platform_apis) {
+			variation = append(variation, blueprint.Variation{Mutator: "sdk", Variation: "sdk"})
+		}
 		ctx.AddFarVariationDependencies(variation, tag, a.appProperties.Jni_libs...)
 	}
 
@@ -557,7 +564,7 @@
 
 	dexJarFile := a.dexBuildActions(ctx)
 
-	jniLibs, certificateDeps := collectAppDeps(ctx, a.shouldEmbedJnis(ctx))
+	jniLibs, certificateDeps := collectAppDeps(ctx, a.shouldEmbedJnis(ctx), !Bool(a.appProperties.Jni_uses_platform_apis))
 	jniJarFile := a.jniBuildActions(jniLibs, ctx)
 
 	if ctx.Failed() {
@@ -607,7 +614,8 @@
 	}
 }
 
-func collectAppDeps(ctx android.ModuleContext, shouldCollectRecursiveNativeDeps bool) ([]jniLib, []Certificate) {
+func collectAppDeps(ctx android.ModuleContext, shouldCollectRecursiveNativeDeps bool,
+	checkNativeSdkVersion bool) ([]jniLib, []Certificate) {
 	var jniLibs []jniLib
 	var certificates []Certificate
 	seenModulePaths := make(map[string]bool)
@@ -629,6 +637,18 @@
 				}
 				seenModulePaths[path.String()] = true
 
+				if checkNativeSdkVersion {
+					if app, ok := ctx.Module().(interface{ sdkVersion() sdkSpec }); ok {
+						if app.sdkVersion().specified() &&
+							app.sdkVersion().kind != sdkCorePlatform &&
+							dep.SdkVersion() == "" {
+							ctx.PropertyErrorf("jni_libs",
+								"JNI dependency %q uses platform APIs, but this module does not",
+								otherName)
+						}
+					}
+				}
+
 				if lib.Valid() {
 					jniLibs = append(jniLibs, jniLib{
 						name:         ctx.OtherModuleName(module),
@@ -1169,7 +1189,7 @@
 		ctx.ModuleErrorf("One and only one of certficate, presigned, and default_dev_cert properties must be set")
 	}
 
-	_, certificates := collectAppDeps(ctx, false)
+	_, certificates := collectAppDeps(ctx, false, false)
 
 	// TODO: LOCAL_EXTRACT_APK/LOCAL_EXTRACT_DPI_APK
 	// TODO: LOCAL_PACKAGE_SPLITS
@@ -1457,7 +1477,7 @@
 	r.aapt.buildActions(ctx, r, aaptLinkFlags...)
 
 	// Sign the built package
-	_, certificates := collectAppDeps(ctx, false)
+	_, certificates := collectAppDeps(ctx, false, false)
 	certificates = processMainCert(r.ModuleBase, String(r.properties.Certificate), certificates, ctx)
 	signed := android.PathForModuleOut(ctx, "signed", r.Name()+".apk")
 	SignAppPackage(ctx, signed, r.aapt.exportPackage, certificates, nil)
diff --git a/java/app_test.go b/java/app_test.go
index 8ec8a7f..f2cbbfb 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -912,6 +912,7 @@
 		cc_library {
 			name: "libjni",
 			system_shared_libs: [],
+			sdk_version: "current",
 			stl: "none",
 		}
 
@@ -1024,6 +1025,7 @@
 			name: "libjni",
 			system_shared_libs: [],
 			stl: "none",
+			sdk_version: "current",
 		}
 
 		android_app {
@@ -1048,26 +1050,26 @@
 
 		android_test {
 			name: "test",
-			sdk_version: "core_platform",
+			sdk_version: "current",
 			jni_libs: ["libjni"],
 		}
 
 		android_test {
 			name: "test_noembed",
-			sdk_version: "core_platform",
+			sdk_version: "current",
 			jni_libs: ["libjni"],
 			use_embedded_native_libs: false,
 		}
 
 		android_test_helper_app {
 			name: "test_helper",
-			sdk_version: "core_platform",
+			sdk_version: "current",
 			jni_libs: ["libjni"],
 		}
 
 		android_test_helper_app {
 			name: "test_helper_noembed",
-			sdk_version: "core_platform",
+			sdk_version: "current",
 			jni_libs: ["libjni"],
 			use_embedded_native_libs: false,
 		}
@@ -1099,6 +1101,10 @@
 				if g, w := !strings.Contains(jniLibZip.Args["jarArgs"], "-L 0"), test.compressed; g != w {
 					t.Errorf("expected jni compressed %v, got %v", w, g)
 				}
+
+				if !strings.Contains(jniLibZip.Implicits[0].String(), "_sdk_") {
+					t.Errorf("expected input %q to use sdk variant", jniLibZip.Implicits[0].String())
+				}
 			}
 		})
 	}
@@ -2293,6 +2299,7 @@
 			system_shared_libs: [],
 			stl: "none",
 			notice: "LIB_NOTICE",
+			sdk_version: "current",
 		}
 
 		java_library {
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 7ac71a4..62675c4 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -1471,6 +1471,15 @@
 		cmd := metalavaCmd(ctx, rule, javaVersion, d.Javadoc.srcFiles, srcJarList,
 			deps.bootClasspath, deps.classpath, d.Javadoc.sourcepaths)
 
+		// TODO(b/154317059): Clean up this whitelist by baselining and/or checking in last-released.
+		if d.Name() != "android.car-system-stubs-docs" &&
+			d.Name() != "android.car-stubs-docs" &&
+			d.Name() != "system-api-stubs-docs" &&
+			d.Name() != "test-api-stubs-docs" {
+			cmd.Flag("--lints-as-errors")
+			cmd.Flag("--warnings-as-errors") // Most lints are actually warnings.
+		}
+
 		cmd.Flag(d.Javadoc.args).Implicits(d.Javadoc.argFiles)
 
 		newSince := android.OptionalPathForModuleSrc(ctx, d.properties.Check_api.Api_lint.New_since)
@@ -1489,20 +1498,37 @@
 		updatedBaselineOutput := android.PathForModuleOut(ctx, "api_lint_baseline.txt")
 		d.apiLintTimestamp = android.PathForModuleOut(ctx, "api_lint.timestamp")
 
+		msg := `` +
+			`************************************************************\n` +
+			`Your API changes are triggering API Lint warnings or errors.\n` +
+			`To make these errors go away, fix the code according to the\n` +
+			`error and/or warning messages above.\n` +
+			`\n` +
+			`If it's not possible to do so, there are workarounds:\n` +
+			`\n` +
+			`1. You can suppress the errors with @SuppressLint(\"<id>\")\n`
+
 		if baselineFile.Valid() {
 			cmd.FlagWithInput("--baseline ", baselineFile.Path())
 			cmd.FlagWithOutput("--update-baseline ", updatedBaselineOutput)
+
+			msg += fmt.Sprintf(``+
+				`2. You can update the baseline by executing the following\n`+
+				`   command:\n`+
+				`       cp \\ \n`+
+				`       \"$PWD/%s\" \\ \n`+
+				`       \"$PWD/%s\" \n`+
+				`   To submit the revised baseline.txt to the main Android\n`+
+				`   repository, you will need approval.\n`, updatedBaselineOutput, baselineFile.Path())
+		} else {
+			msg += fmt.Sprintf(``+
+				`2. You can add a baseline file of existing lint failures\n`+
+				`   to the build rule of %s.\n`, d.Name())
 		}
+		msg += `************************************************************\n`
 
 		zipSyncCleanupCmd(rule, srcJarDir)
 
-		msg := fmt.Sprintf(`\n******************************\n`+
-			`Your API changes are triggering API Lint warnings or errors.\n\n`+
-			`To make these errors go away, you have two choices:\n`+
-			`   1. You can suppress the errors with @SuppressLint(\"<id>\").\n\n`+
-			`   2. You can update the baseline by executing the following command:\n`+
-			`         cp \"$PWD/%s\" \"$PWD/%s\"\n\n`+
-			`******************************\n`, updatedBaselineOutput, baselineFile.Path())
 		rule.Command().
 			Text("touch").Output(d.apiLintTimestamp).
 			Text(") || (").
diff --git a/java/java.go b/java/java.go
index 5d77807..6589721 100644
--- a/java/java.go
+++ b/java/java.go
@@ -86,6 +86,14 @@
 	ctx.RegisterSingletonType("kythe_java_extract", kytheExtractJavaFactory)
 }
 
+func (j *Module) CheckStableSdkVersion() error {
+	sdkVersion := j.sdkVersion()
+	if sdkVersion.stable() {
+		return nil
+	}
+	return fmt.Errorf("non stable SDK %v", sdkVersion)
+}
+
 func (j *Module) checkSdkVersions(ctx android.ModuleContext) {
 	if j.SocSpecific() || j.DeviceSpecific() ||
 		(j.ProductSpecific() && ctx.Config().EnforceProductPartitionInterface()) {
diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go
index 86eddb1..03bc76b 100644
--- a/java/prebuilt_apis.go
+++ b/java/prebuilt_apis.go
@@ -59,7 +59,7 @@
 	apiver = elements[0]
 
 	scope = elements[1]
-	if scope != "public" && scope != "system" && scope != "test" {
+	if scope != "public" && scope != "system" && scope != "test" && scope != "module-lib" && scope != "system-server" {
 		ctx.ModuleErrorf("invalid scope %q found in path: %q", scope, path)
 		return
 	}
@@ -100,7 +100,7 @@
 	mydir := mctx.ModuleDir() + "/"
 	var files []string
 	for _, apiver := range mctx.Module().(*prebuiltApis).properties.Api_dirs {
-		for _, scope := range []string{"public", "system", "test", "core"} {
+		for _, scope := range []string{"public", "system", "test", "core", "module-lib", "system-server"} {
 			vfiles, err := mctx.GlobWithDeps(mydir+apiver+"/"+scope+"/"+name, nil)
 			if err != nil {
 				mctx.ModuleErrorf("failed to glob %s files under %q: %s", name, mydir+apiver+"/"+scope, err)
diff --git a/java/sdk.go b/java/sdk.go
index be5e512..4414600 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -170,9 +170,12 @@
 		return false
 	}
 	switch s.kind {
+	case sdkNone:
+		// there is nothing to manage and version in this case; de facto stable API.
+		return true
 	case sdkCore, sdkPublic, sdkSystem, sdkModule, sdkSystemServer:
 		return true
-	case sdkNone, sdkCorePlatform, sdkTest, sdkPrivate:
+	case sdkCorePlatform, sdkTest, sdkPrivate:
 		return false
 	default:
 		panic(fmt.Errorf("unknown sdkKind=%v", s.kind))
diff --git a/java/sdk_library.go b/java/sdk_library.go
index fa26795..d9c948f 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -90,6 +90,9 @@
 
 	// Extra arguments to pass to droidstubs for this scope.
 	droidstubsArgs []string
+
+	// Whether the api scope can be treated as unstable, and should skip compat checks.
+	unstable bool
 }
 
 // Initialize a scope, creating and adding appropriate dependency tags
@@ -142,6 +145,7 @@
 		moduleSuffix:   sdkTestApiSuffix,
 		sdkVersion:     "test_current",
 		droidstubsArgs: []string{"-showAnnotation android.annotation.TestApi"},
+		unstable:       true,
 	})
 	allApiScopes = apiScopes{
 		apiScopePublic,
@@ -556,12 +560,14 @@
 	props.Check_api.Current.Api_file = proptools.StringPtr(currentApiFileName)
 	props.Check_api.Current.Removed_api_file = proptools.StringPtr(removedApiFileName)
 
-	// check against the latest released API
-	props.Check_api.Last_released.Api_file = proptools.StringPtr(
-		module.latestApiFilegroupName(apiScope))
-	props.Check_api.Last_released.Removed_api_file = proptools.StringPtr(
-		module.latestRemovedApiFilegroupName(apiScope))
-	props.Check_api.Ignore_missing_latest_api = proptools.BoolPtr(true)
+	if !apiScope.unstable {
+		// check against the latest released API
+		props.Check_api.Last_released.Api_file = proptools.StringPtr(
+			module.latestApiFilegroupName(apiScope))
+		props.Check_api.Last_released.Removed_api_file = proptools.StringPtr(
+			module.latestRemovedApiFilegroupName(apiScope))
+		props.Check_api.Ignore_missing_latest_api = proptools.BoolPtr(true)
+	}
 
 	// Dist the api txt artifact for sdk builds.
 	if !Bool(module.sdkLibraryProperties.No_dist) {
diff --git a/rust/rust.go b/rust/rust.go
index 17734f9..5cc8845 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -164,6 +164,10 @@
 	return false
 }
 
+func (mod *Module) UseSdk() bool {
+	return false
+}
+
 func (mod *Module) UseVndk() bool {
 	return false
 }
@@ -184,6 +188,10 @@
 	return ""
 }
 
+func (mod *Module) AlwaysSdk() bool {
+	return false
+}
+
 func (mod *Module) ToolchainLibrary() bool {
 	return false
 }