Merge "Clean com.android.neuralnetworks APEX whitelist"
diff --git a/Android.bp b/Android.bp
index 9c2bb43..fe776df 100644
--- a/Android.bp
+++ b/Android.bp
@@ -186,6 +186,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 cbaf1c7..9bf6fc7 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -19,6 +19,8 @@
 	"sort"
 	"strconv"
 	"sync"
+
+	"github.com/google/blueprint"
 )
 
 const (
@@ -32,6 +34,14 @@
 	MinSdkVersion int
 }
 
+// Extracted from ApexModule to make it easier to define custom subsets of the
+// ApexModule interface and improve code navigation within the IDE.
+type DepIsInSameApex interface {
+	// DepIsInSameApex tests if the other module 'dep' is installed to the same
+	// APEX as this module
+	DepIsInSameApex(ctx BaseModuleContext, dep Module) bool
+}
+
 // ApexModule is the interface that a module type is expected to implement if
 // the module has to be built differently depending on whether the module
 // is destined for an apex or not (installed to one of the regular partitions).
@@ -49,6 +59,8 @@
 // respectively.
 type ApexModule interface {
 	Module
+	DepIsInSameApex
+
 	apexModuleBase() *ApexModuleBase
 
 	// Marks that this module should be built for the specified APEXes.
@@ -88,10 +100,6 @@
 	// Tests if this module is available for the specified APEX or ":platform"
 	AvailableFor(what string) bool
 
-	// DepIsInSameApex tests if the other module 'dep' is installed to the same
-	// APEX as this module
-	DepIsInSameApex(ctx BaseModuleContext, dep Module) bool
-
 	// Returns the highest version which is <= maxSdkVersion.
 	// For example, with maxSdkVersion is 10 and versionList is [9,11]
 	// it returns 9 as string
@@ -111,6 +119,15 @@
 	Info ApexInfo `blueprint:"mutated"`
 }
 
+// Marker interface that identifies dependencies that are excluded from APEX
+// contents.
+type ExcludeFromApexContentsTag interface {
+	blueprint.DependencyTag
+
+	// Method that differentiates this interface from others.
+	ExcludeFromApexContents()
+}
+
 // Provides default implementation for the ApexModule interface. APEX-aware
 // modules are expected to include this struct and call InitApexModule().
 type ApexModuleBase struct {
diff --git a/android/module.go b/android/module.go
index 80f477b..02b2c89 100644
--- a/android/module.go
+++ b/android/module.go
@@ -128,6 +128,13 @@
 	// and returns a top-down dependency path from a start module to current child module.
 	GetWalkPath() []Module
 
+	// GetTagPath is supposed to be called in visit function passed in WalkDeps()
+	// and returns a top-down dependency tags path from a start module to current child module.
+	// It has one less entry than GetWalkPath() as it contains the dependency tags that
+	// exist between each adjacent pair of modules in the GetWalkPath().
+	// GetTagPath()[i] is the tag between GetWalkPath()[i] and GetWalkPath()[i+1]
+	GetTagPath() []blueprint.DependencyTag
+
 	AddMissingDependencies(missingDeps []string)
 
 	Target() Target
@@ -220,6 +227,7 @@
 	InstallBypassMake() bool
 	InstallForceOS() *OsType
 	SkipInstall()
+	IsSkipInstall() bool
 	ExportedToMake() bool
 	InitRc() Paths
 	VintfFragments() Paths
@@ -943,6 +951,10 @@
 	m.commonProperties.SkipInstall = true
 }
 
+func (m *ModuleBase) IsSkipInstall() bool {
+	return m.commonProperties.SkipInstall == true
+}
+
 func (m *ModuleBase) ExportedToMake() bool {
 	return m.commonProperties.NamespaceExportedToMake
 }
@@ -1400,6 +1412,7 @@
 	debug         bool
 
 	walkPath []Module
+	tagPath  []blueprint.DependencyTag
 
 	strictVisitDeps bool // If true, enforce that all dependencies are enabled
 }
@@ -1696,6 +1709,7 @@
 
 func (b *baseModuleContext) WalkDeps(visit func(Module, Module) bool) {
 	b.walkPath = []Module{b.Module()}
+	b.tagPath = []blueprint.DependencyTag{}
 	b.bp.WalkDeps(func(child, parent blueprint.Module) bool {
 		childAndroidModule, _ := child.(Module)
 		parentAndroidModule, _ := parent.(Module)
@@ -1703,8 +1717,10 @@
 			// record walkPath before visit
 			for b.walkPath[len(b.walkPath)-1] != parentAndroidModule {
 				b.walkPath = b.walkPath[0 : len(b.walkPath)-1]
+				b.tagPath = b.tagPath[0 : len(b.tagPath)-1]
 			}
 			b.walkPath = append(b.walkPath, childAndroidModule)
+			b.tagPath = append(b.tagPath, b.OtherModuleDependencyTag(childAndroidModule))
 			return visit(childAndroidModule, parentAndroidModule)
 		} else {
 			return false
@@ -1716,6 +1732,10 @@
 	return b.walkPath
 }
 
+func (b *baseModuleContext) GetTagPath() []blueprint.DependencyTag {
+	return b.tagPath
+}
+
 func (m *moduleContext) VisitAllModuleVariants(visit func(Module)) {
 	m.bp.VisitAllModuleVariants(func(module blueprint.Module) {
 		visit(module.(Module))
diff --git a/android/neverallow.go b/android/neverallow.go
index 8fcfb8a..547230c 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -53,6 +53,7 @@
 	AddNeverAllowRules(createLibcoreRules()...)
 	AddNeverAllowRules(createMediaRules()...)
 	AddNeverAllowRules(createJavaDeviceForHostRules()...)
+	AddNeverAllowRules(createCcSdkVariantRules()...)
 }
 
 // Add a NeverAllow rule to the set of rules to apply.
@@ -175,6 +176,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 {
@@ -254,6 +286,18 @@
 	return ".starts-with(" + m.prefix + ")"
 }
 
+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 6f07a4a..41e5c6f 100644
--- a/android/neverallow_test.go
+++ b/android/neverallow_test.go
@@ -249,6 +249,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) {
@@ -289,6 +333,8 @@
 	Include_dirs     []string
 	Vendor_available *bool
 	Static_libs      []string
+	Sdk_version      *string
+	Sdk_variant_only *bool
 
 	Vndk struct {
 		Enabled                *bool
@@ -305,6 +351,10 @@
 			Cflags []string
 		}
 	}
+
+	Platform struct {
+		Shared_libs []string
+	}
 }
 
 type mockCcLibraryModule struct {
diff --git a/android/prebuilt.go b/android/prebuilt.go
index c902ec8..82745a4 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -39,6 +39,12 @@
 // Mark this tag so dependencies that use it are excluded from visibility enforcement.
 func (t prebuiltDependencyTag) ExcludeFromVisibilityEnforcement() {}
 
+// Mark this tag so dependencies that use it are excluded from APEX contents.
+func (t prebuiltDependencyTag) ExcludeFromApexContents() {}
+
+var _ ExcludeFromVisibilityEnforcementTag = PrebuiltDepTag
+var _ ExcludeFromApexContentsTag = PrebuiltDepTag
+
 type PrebuiltProperties struct {
 	// When prefer is set to true the prebuilt will be used instead of any source module with
 	// a matching name.
diff --git a/android/sdk.go b/android/sdk.go
index 66094cd..6f62f55 100644
--- a/android/sdk.go
+++ b/android/sdk.go
@@ -22,17 +22,30 @@
 	"github.com/google/blueprint/proptools"
 )
 
+// Extracted from SdkAware to make it easier to define custom subsets of the
+// SdkAware interface and improve code navigation within the IDE.
+//
+// In addition to its use in SdkAware this interface must also be implemented by
+// APEX to specify the SDKs required by that module and its contents. e.g. APEX
+// is expected to implement RequiredSdks() by reading its own properties like
+// `uses_sdks`.
+type RequiredSdks interface {
+	// The set of SDKs required by an APEX and its contents.
+	RequiredSdks() SdkRefs
+}
+
 // SdkAware is the interface that must be supported by any module to become a member of SDK or to be
 // built with SDK
 type SdkAware interface {
 	Module
+	RequiredSdks
+
 	sdkBase() *SdkBase
 	MakeMemberOf(sdk SdkRef)
 	IsInAnySdk() bool
 	ContainingSdk() SdkRef
 	MemberName() string
 	BuildWithSdks(sdks SdkRefs)
-	RequiredSdks() SdkRefs
 }
 
 // SdkRef refers to a version of an SDK
diff --git a/android/soong_config_modules.go b/android/soong_config_modules.go
index fa1e204..6ce609a 100644
--- a/android/soong_config_modules.go
+++ b/android/soong_config_modules.go
@@ -122,7 +122,10 @@
 }
 
 func (m *soongConfigModuleTypeImport) Name() string {
-	return "soong_config_module_type_import_" + soongconfig.CanonicalizeToProperty(m.properties.From)
+	// The generated name is non-deterministic, but it does not
+	// matter because this module does not emit any rules.
+	return soongconfig.CanonicalizeToProperty(m.properties.From) +
+		"soong_config_module_type_import_" + fmt.Sprintf("%p", m)
 }
 
 func (*soongConfigModuleTypeImport) Nameless()                                 {}
diff --git a/apex/apex.go b/apex/apex.go
index af8c35d..fd3fb2b 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -18,6 +18,7 @@
 	"fmt"
 	"path"
 	"path/filepath"
+	"regexp"
 	"sort"
 	"strconv"
 	"strings"
@@ -123,7 +124,6 @@
 		"crtbegin_dynamic1",
 		"crtbegin_so1",
 		"crtbrand",
-		"conscrypt.module.intra.core.api.stubs",
 		"dex2oat_headers",
 		"dt_fd_forward_export",
 		"icu4c_extra_headers",
@@ -853,20 +853,28 @@
 		return
 	}
 
-	cur := mctx.Module().(interface {
-		DepIsInSameApex(android.BaseModuleContext, android.Module) bool
-	})
+	cur := mctx.Module().(android.DepIsInSameApex)
 
 	mctx.VisitDirectDeps(func(child android.Module) {
 		depName := mctx.OtherModuleName(child)
 		if am, ok := child.(android.ApexModule); ok && am.CanHaveApexVariants() &&
-			cur.DepIsInSameApex(mctx, child) {
+			(cur.DepIsInSameApex(mctx, child) || inAnySdk(child)) {
 			android.UpdateApexDependency(apexBundles, depName, directDep)
 			am.BuildForApexes(apexBundles)
 		}
 	})
 }
 
+// If a module in an APEX depends on a module from an SDK then it needs an APEX
+// specific variant created for it. Refer to sdk.sdkDepsReplaceMutator.
+func inAnySdk(module android.Module) bool {
+	if sa, ok := module.(android.SdkAware); ok {
+		return sa.IsInAnySdk()
+	}
+
+	return false
+}
+
 // Create apex variations if a module is included in APEX(s).
 func apexMutator(mctx android.BottomUpMutatorContext) {
 	if am, ok := mctx.Module().(android.ApexModule); ok && am.CanHaveApexVariants() {
@@ -1798,6 +1806,24 @@
 	return android.FutureApiLevel
 }
 
+// A regexp for removing boilerplate from BaseDependencyTag from the string representation of
+// a dependency tag.
+var tagCleaner = regexp.MustCompile(`\QBaseDependencyTag:blueprint.BaseDependencyTag{}\E(, )?`)
+
+func PrettyPrintTag(tag blueprint.DependencyTag) string {
+	// Use tag's custom String() method if available.
+	if stringer, ok := tag.(fmt.Stringer); ok {
+		return stringer.String()
+	}
+
+	// Otherwise, get a default string representation of the tag's struct.
+	tagString := fmt.Sprintf("%#v", tag)
+
+	// Remove the boilerplate from BaseDependencyTag as it adds no value.
+	tagString = tagCleaner.ReplaceAllString(tagString, "")
+	return tagString
+}
+
 // Ensures that the dependencies are marked as available for this APEX
 func (a *apexBundle) checkApexAvailability(ctx android.ModuleContext) {
 	// Let's be practical. Availability for test, host, and the VNDK apex isn't important
@@ -1821,12 +1847,23 @@
 		apexName := ctx.ModuleName()
 		fromName := ctx.OtherModuleName(from)
 		toName := ctx.OtherModuleName(to)
+
+		// If `to` is not actually in the same APEX as `from` then it does not need apex_available and neither
+		// do any of its dependencies.
+		if am, ok := from.(android.DepIsInSameApex); ok && !am.DepIsInSameApex(ctx, to) {
+			// As soon as the dependency graph crosses the APEX boundary, don't go further.
+			return false
+		}
+
 		if to.AvailableFor(apexName) || whitelistedApexAvailable(apexName, toName) {
 			return true
 		}
 		message := ""
-		for _, m := range ctx.GetWalkPath()[1:] {
-			message = fmt.Sprintf("%s\n    -> %s", message, m.String())
+		tagPath := ctx.GetTagPath()
+		// Skip the first module as that will be added at the start of the error message by ctx.ModuleErrorf().
+		walkPath := ctx.GetWalkPath()[1:]
+		for i, m := range walkPath {
+			message = fmt.Sprintf("%s\n           via tag %s\n    -> %s", message, PrettyPrintTag(tagPath[i]), m.String())
 		}
 		ctx.ModuleErrorf("%q requires %q that is not available for the APEX. Dependency path:%s", fromName, toName, message)
 		// Visit this module's dependencies to check and report any issues with their availability.
@@ -1936,6 +1973,9 @@
 	// TODO(jiyong) do this using walkPayloadDeps
 	ctx.WalkDepsBlueprint(func(child, parent blueprint.Module) bool {
 		depTag := ctx.OtherModuleDependencyTag(child)
+		if _, ok := depTag.(android.ExcludeFromApexContentsTag); ok {
+			return false
+		}
 		depName := ctx.OtherModuleName(child)
 		if _, isDirectDep := parent.(*apexBundle); isDirectDep {
 			switch depTag {
@@ -2104,7 +2144,7 @@
 						filesInfo = append(filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, depName))
 					}
 				} else if am.CanHaveApexVariants() && am.IsInstallableToApex() {
-					ctx.ModuleErrorf("unexpected tag %q for indirect dependency %q", depTag, depName)
+					ctx.ModuleErrorf("unexpected tag %s for indirect dependency %q", PrettyPrintTag(depTag), depName)
 				}
 			}
 		}
diff --git a/apex/apex_test.go b/apex/apex_test.go
index b97e38d..0c8937e 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -27,6 +27,7 @@
 
 	"android/soong/android"
 	"android/soong/cc"
+	"android/soong/dexpreopt"
 	"android/soong/java"
 )
 
@@ -158,6 +159,7 @@
 		"my_include":                                 nil,
 		"foo/bar/MyClass.java":                       nil,
 		"prebuilt.jar":                               nil,
+		"prebuilt.so":                                nil,
 		"vendor/foo/devkeys/test.x509.pem":           nil,
 		"vendor/foo/devkeys/test.pk8":                nil,
 		"testkey.x509.pem":                           nil,
@@ -367,7 +369,7 @@
 			apex_available: [ "myapex" ],
 		}
 
-		cc_library {
+		cc_library_shared {
 			name: "mylib2",
 			srcs: ["mylib.cpp"],
 			system_shared_libs: [],
@@ -381,6 +383,16 @@
 			],
 		}
 
+		cc_prebuilt_library_shared {
+			name: "mylib2",
+			srcs: ["prebuilt.so"],
+			// TODO: remove //apex_available:platform
+			apex_available: [
+				"//apex_available:platform",
+				"myapex",
+			],
+    }
+
 		cc_library_static {
 			name: "libstatic",
 			srcs: ["mylib.cpp"],
@@ -3384,7 +3396,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
@@ -3540,12 +3552,12 @@
 func TestApexAvailable_IndirectDep(t *testing.T) {
 	// libbbaz is an indirect dep
 	testApexError(t, `requires "libbaz" that is not available for the APEX. Dependency path:
+.*via tag apex\.dependencyTag.*"sharedLib".*
 .*-> libfoo.*link:shared.*
-.*-> libfoo.*link:static.*
+.*via tag cc\.DependencyTag.*"shared".*
 .*-> libbar.*link:shared.*
-.*-> libbar.*link:static.*
-.*-> libbaz.*link:shared.*
-.*-> libbaz.*link:static.*`, `
+.*via tag cc\.DependencyTag.*"shared".*
+.*-> libbaz.*link:shared.*`, `
 	apex {
 		name: "myapex",
 		key: "myapex.key",
@@ -4192,6 +4204,175 @@
 	ensureContains(t, content, `"apex_config":{"apex_embedded_apk_config":[{"package_name":"com.android.foo","path":"app/AppFoo/AppFoo.apk"}]}`)
 }
 
+func testNoUpdatableJarsInBootImage(t *testing.T, errmsg, bp string, transformDexpreoptConfig func(*dexpreopt.GlobalConfig)) {
+	t.Helper()
+
+	bp = bp + `
+		filegroup {
+			name: "some-updatable-apex-file_contexts",
+			srcs: [
+				"system/sepolicy/apex/some-updatable-apex-file_contexts",
+			],
+		}
+	`
+	bp += cc.GatherRequiredDepsForTest(android.Android)
+	bp += java.GatherRequiredDepsForTest()
+	bp += dexpreopt.BpToolModulesForTest()
+
+	fs := map[string][]byte{
+		"a.java":                             nil,
+		"a.jar":                              nil,
+		"build/make/target/product/security": nil,
+		"apex_manifest.json":                 nil,
+		"AndroidManifest.xml":                nil,
+		"system/sepolicy/apex/some-updatable-apex-file_contexts":       nil,
+		"system/sepolicy/apex/com.android.art.something-file_contexts": nil,
+		"framework/aidl/a.aidl": nil,
+	}
+	cc.GatherRequiredFilesForTest(fs)
+
+	ctx := android.NewTestArchContext()
+	ctx.RegisterModuleType("apex", BundleFactory)
+	ctx.RegisterModuleType("apex_key", ApexKeyFactory)
+	ctx.RegisterModuleType("filegroup", android.FileGroupFactory)
+	cc.RegisterRequiredBuildComponentsForTest(ctx)
+	java.RegisterJavaBuildComponents(ctx)
+	java.RegisterSystemModulesBuildComponents(ctx)
+	java.RegisterAppBuildComponents(ctx)
+	java.RegisterDexpreoptBootJarsComponents(ctx)
+	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
+	ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators)
+	ctx.PreDepsMutators(RegisterPreDepsMutators)
+	ctx.PostDepsMutators(RegisterPostDepsMutators)
+
+	config := android.TestArchConfig(buildDir, nil, bp, fs)
+	ctx.Register(config)
+
+	_ = dexpreopt.GlobalSoongConfigForTests(config)
+	dexpreopt.RegisterToolModulesForTest(ctx)
+	pathCtx := android.PathContextForTesting(config)
+	dexpreoptConfig := dexpreopt.GlobalConfigForTests(pathCtx)
+	transformDexpreoptConfig(dexpreoptConfig)
+	dexpreopt.SetTestGlobalConfig(config, dexpreoptConfig)
+
+	_, errs := ctx.ParseBlueprintsFiles("Android.bp")
+	android.FailIfErrored(t, errs)
+
+	_, errs = ctx.PrepareBuildActions(config)
+	if errmsg == "" {
+		android.FailIfErrored(t, errs)
+	} else if len(errs) > 0 {
+		android.FailIfNoMatchingErrors(t, errmsg, errs)
+		return
+	} else {
+		t.Fatalf("missing expected error %q (0 errors are returned)", errmsg)
+	}
+}
+
+func TestNoUpdatableJarsInBootImage(t *testing.T) {
+	bp := `
+		java_library {
+			name: "some-updatable-apex-lib",
+			srcs: ["a.java"],
+			apex_available: [
+				"some-updatable-apex",
+			],
+		}
+
+		java_library {
+			name: "some-platform-lib",
+			srcs: ["a.java"],
+			installable: true,
+		}
+
+		java_library {
+			name: "some-art-lib",
+			srcs: ["a.java"],
+			apex_available: [
+				"com.android.art.something",
+			],
+			hostdex: true,
+		}
+
+		apex {
+			name: "some-updatable-apex",
+			key: "some-updatable-apex.key",
+			java_libs: ["some-updatable-apex-lib"],
+		}
+
+		apex_key {
+			name: "some-updatable-apex.key",
+		}
+
+		apex {
+			name: "com.android.art.something",
+			key: "com.android.art.something.key",
+			java_libs: ["some-art-lib"],
+		}
+
+		apex_key {
+			name: "com.android.art.something.key",
+		}
+	`
+
+	var error string
+	var transform func(*dexpreopt.GlobalConfig)
+
+	// updatable jar from ART apex in the ART boot image => ok
+	transform = func(config *dexpreopt.GlobalConfig) {
+		config.ArtApexJars = []string{"some-art-lib"}
+	}
+	testNoUpdatableJarsInBootImage(t, "", bp, transform)
+
+	// updatable jar from ART apex in the framework boot image => error
+	error = "module 'some-art-lib' from updatable apex 'com.android.art.something' is not allowed in the framework boot image"
+	transform = func(config *dexpreopt.GlobalConfig) {
+		config.BootJars = []string{"some-art-lib"}
+	}
+	testNoUpdatableJarsInBootImage(t, error, bp, transform)
+
+	// updatable jar from some other apex in the ART boot image => error
+	error = "module 'some-updatable-apex-lib' from updatable apex 'some-updatable-apex' is not allowed in the ART boot image"
+	transform = func(config *dexpreopt.GlobalConfig) {
+		config.ArtApexJars = []string{"some-updatable-apex-lib"}
+	}
+	testNoUpdatableJarsInBootImage(t, error, bp, transform)
+
+	// updatable jar from some other apex in the framework boot image => error
+	error = "module 'some-updatable-apex-lib' from updatable apex 'some-updatable-apex' is not allowed in the framework boot image"
+	transform = func(config *dexpreopt.GlobalConfig) {
+		config.BootJars = []string{"some-updatable-apex-lib"}
+	}
+	testNoUpdatableJarsInBootImage(t, error, bp, transform)
+
+	// nonexistent jar in the ART boot image => error
+	error = "failed to find a dex jar path for module 'nonexistent'"
+	transform = func(config *dexpreopt.GlobalConfig) {
+		config.ArtApexJars = []string{"nonexistent"}
+	}
+	testNoUpdatableJarsInBootImage(t, error, bp, transform)
+
+	// nonexistent jar in the framework boot image => error
+	error = "failed to find a dex jar path for module 'nonexistent'"
+	transform = func(config *dexpreopt.GlobalConfig) {
+		config.BootJars = []string{"nonexistent"}
+	}
+	testNoUpdatableJarsInBootImage(t, error, bp, transform)
+
+	// platform jar in the ART boot image => error
+	error = "module 'some-platform-lib' is part of the platform and not allowed in the ART boot image"
+	transform = func(config *dexpreopt.GlobalConfig) {
+		config.ArtApexJars = []string{"some-platform-lib"}
+	}
+	testNoUpdatableJarsInBootImage(t, error, bp, transform)
+
+	// platform jar in the framework boot image => ok
+	transform = func(config *dexpreopt.GlobalConfig) {
+		config.BootJars = []string{"some-platform-lib"}
+	}
+	testNoUpdatableJarsInBootImage(t, "", bp, transform)
+}
+
 func TestMain(m *testing.M) {
 	run := func() int {
 		setUp()
diff --git a/apex/vndk_test.go b/apex/vndk_test.go
index a9e26ad..863d40d 100644
--- a/apex/vndk_test.go
+++ b/apex/vndk_test.go
@@ -90,6 +90,7 @@
 				system_shared_libs: [],
 				stl: "none",
 				notice: "custom_notice",
+				sdk_version: "current",
 			}
 			cc_library {
 				name: "libprofile-clang-extras_ndk",
@@ -98,6 +99,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 ef695b0..19c7182 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))))")
+				}
 			},
 		},
 	}
@@ -393,6 +416,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 c42914b..4667a3d 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,9 +209,13 @@
 	// 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
 
+	// 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"`
@@ -252,6 +257,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 {
@@ -530,7 +545,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 {
@@ -558,6 +576,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 {
@@ -586,13 +608,6 @@
 	return c.depsInLinkOrder
 }
 
-func (c *Module) StubsSymbolFile() android.OptionalPath {
-	if library, ok := c.linker.(*libraryDecorator); ok {
-		return library.stubsSymbolFile
-	}
-	return android.OptionalPath{}
-}
-
 func (c *Module) StubsVersions() []string {
 	if c.linker != nil {
 		if library, ok := c.linker.(*libraryDecorator); ok {
@@ -824,6 +839,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
 }
@@ -1081,14 +1107,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 {
@@ -1407,6 +1430,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/config/global.go b/cc/config/global.go
index 29020ab..5611a96 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -127,8 +127,8 @@
 
 	// prebuilts/clang default settings.
 	ClangDefaultBase         = "prebuilts/clang/host"
-	ClangDefaultVersion      = "clang-r377782c"
-	ClangDefaultShortVersion = "10.0.5"
+	ClangDefaultVersion      = "clang-r377782d"
+	ClangDefaultShortVersion = "10.0.6"
 
 	// Directories with warnings from Android.bp files.
 	WarningAllowedProjects = []string{
diff --git a/cc/genrule.go b/cc/genrule.go
index 155e410..9331448 100644
--- a/cc/genrule.go
+++ b/cc/genrule.go
@@ -27,6 +27,7 @@
 	Vendor_available   *bool
 	Ramdisk_available  *bool
 	Recovery_available *bool
+	Sdk_version        *string
 }
 
 // cc_genrule is a genrule that can depend on other cc_* objects.
diff --git a/cc/library.go b/cc/library.go
index 26e86f9..140f87a 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -367,9 +367,6 @@
 	// Location of the file that should be copied to dist dir when requested
 	distFile android.OptionalPath
 
-	// stubs.symbol_file
-	stubsSymbolFile android.OptionalPath
-
 	versionScriptPath android.ModuleGenPath
 
 	post_install_cmds []string
@@ -606,7 +603,6 @@
 
 func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
 	if library.buildStubs() {
-		library.stubsSymbolFile = android.OptionalPathForModuleSrc(ctx, library.Properties.Stubs.Symbol_file)
 		objs, versionScript := compileStubLibrary(ctx, flags, String(library.Properties.Stubs.Symbol_file), library.MutatedProperties.StubsVersion, "--apex")
 		library.versionScriptPath = versionScript
 		return objs
@@ -1241,7 +1237,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/library_sdk_member.go b/cc/library_sdk_member.go
index 4e4df5b..754b96a 100644
--- a/cc/library_sdk_member.go
+++ b/cc/library_sdk_member.go
@@ -271,10 +271,7 @@
 	}
 
 	if len(libInfo.StubsVersion) > 0 {
-		symbolFilePath := filepath.Join(nativeEtcDir, libInfo.StubsSymbolFile.Path().Base())
-		builder.CopyToSnapshot(libInfo.StubsSymbolFile.Path(), symbolFilePath)
 		stubsSet := outputProperties.AddPropertySet("stubs")
-		stubsSet.AddProperty("symbol_file", symbolFilePath)
 		stubsSet.AddProperty("versions", []string{libInfo.StubsVersion})
 	}
 }
@@ -283,7 +280,6 @@
 	nativeIncludeDir          = "include"
 	nativeGeneratedIncludeDir = "include_gen"
 	nativeStubDir             = "lib"
-	nativeEtcDir              = "etc"
 )
 
 // path to the native library. Relative to <sdk_root>/<api_dir>
@@ -348,9 +344,6 @@
 	// are not in use.
 	StubsVersion string
 
-	// The stubs symbol file.
-	StubsSymbolFile android.OptionalPath
-
 	// outputFile is not exported as it is always arch specific.
 	outputFile android.Path
 }
@@ -389,7 +382,6 @@
 
 	if ccModule.HasStubsVariants() {
 		p.StubsVersion = ccModule.StubsVersion()
-		p.StubsSymbolFile = ccModule.StubsSymbolFile()
 	}
 }
 
diff --git a/cc/linkable.go b/cc/linkable.go
index 80cd6b8..fbe61a4 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -45,12 +45,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 f65d7c8..57a0c01 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 2a86d33..68d4ac0 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -381,6 +381,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/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 8113f72..4e74c7f 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/tidy.go b/cc/tidy.go
index 5455392..6a13a78 100644
--- a/cc/tidy.go
+++ b/cc/tidy.go
@@ -117,6 +117,10 @@
 		// which is used in many Android files.
 		tidyChecks = tidyChecks + ",-cert-dcl16-c"
 	}
+	// https://b.corp.google.com/issues/153464409
+	// many local projects enable cert-* checks, which
+	// trigger bugprone-reserved-identifier.
+	tidyChecks = tidyChecks + ",-bugprone-reserved-identifier*"
 	flags.TidyFlags = append(flags.TidyFlags, tidyChecks)
 
 	if len(tidy.Properties.Tidy_checks_as_errors) > 0 {
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/cmd/zipsync/zipsync.go b/cmd/zipsync/zipsync.go
index a6023d3..294e5ef 100644
--- a/cmd/zipsync/zipsync.go
+++ b/cmd/zipsync/zipsync.go
@@ -115,7 +115,7 @@
 
 			filename := filepath.Join(*outputDir, name)
 			if f.FileInfo().IsDir() {
-				must(os.MkdirAll(filename, f.FileInfo().Mode()))
+				must(os.MkdirAll(filename, 0777))
 			} else {
 				must(os.MkdirAll(filepath.Dir(filename), 0777))
 				in, err := f.Open()
diff --git a/java/app.go b/java/app.go
index 0a4d9e4..6a0aa8f 100755
--- a/java/app.go
+++ b/java/app.go
@@ -214,6 +214,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...)
 	}
 
diff --git a/java/app_test.go b/java/app_test.go
index f3cd070..f2ec483 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -791,6 +791,7 @@
 		cc_library {
 			name: "libjni",
 			system_shared_libs: [],
+			sdk_version: "current",
 			stl: "none",
 		}
 
@@ -928,26 +929,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,
 		}
@@ -979,6 +980,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())
+				}
 			}
 		})
 	}
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index d00864d..543b233 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -26,7 +26,7 @@
 )
 
 func init() {
-	android.RegisterSingletonType("dex_bootjars", dexpreoptBootJarsFactory)
+	RegisterDexpreoptBootJarsComponents(android.InitRegistrationContext)
 }
 
 // Target-independent description of pre-compiled boot image.
@@ -174,6 +174,10 @@
 	return &dexpreoptBootJars{}
 }
 
+func RegisterDexpreoptBootJarsComponents(ctx android.RegistrationContext) {
+	ctx.RegisterSingletonType("dex_bootjars", dexpreoptBootJarsFactory)
+}
+
 func skipDexpreoptBootJars(ctx android.PathContext) bool {
 	if dexpreopt.GetGlobalConfig(ctx).DisablePreopt {
 		return true
@@ -242,16 +246,66 @@
 	dumpOatRules(ctx, d.defaultBootImage)
 }
 
+// Inspect this module to see if it contains a bootclasspath dex jar.
+// Note that the same jar may occur in multiple modules.
+// This logic is tested in the apex package to avoid import cycle apex <-> java.
+func getBootImageJar(ctx android.SingletonContext, image *bootImageConfig, module android.Module) (int, android.Path) {
+	// All apex Java libraries have non-installable platform variants, skip them.
+	if module.IsSkipInstall() {
+		return -1, nil
+	}
+
+	jar, hasJar := module.(interface{ DexJar() android.Path })
+	if !hasJar {
+		return -1, nil
+	}
+
+	name := ctx.ModuleName(module)
+	index := android.IndexList(name, image.modules)
+	if index == -1 {
+		return -1, nil
+	}
+
+	// Check that this module satisfies constraints for a particular boot image.
+	apex, isApexModule := module.(android.ApexModule)
+	if image.name == artBootImageName {
+		if isApexModule && strings.HasPrefix(apex.ApexName(), "com.android.art.") {
+			// ok, found the jar in the ART apex
+		} else if isApexModule && !apex.IsForPlatform() {
+			// this jar is part of an updatable apex other than ART, fail immediately
+			ctx.Errorf("module '%s' from updatable apex '%s' is not allowed in the ART boot image", name, apex.ApexName())
+		} else if isApexModule && apex.IsForPlatform() && Bool(module.(*Library).deviceProperties.Hostdex) {
+			// this is a special "hostdex" variant, skip it and resume search
+			return -1, nil
+		} else if name == "jacocoagent" && ctx.Config().IsEnvTrue("EMMA_INSTRUMENT_FRAMEWORK") {
+			// this is Jacoco platform variant for a coverage build, skip it and resume search
+			return -1, nil
+		} else {
+			// this (installable) jar is part of the platform, fail immediately
+			ctx.Errorf("module '%s' is part of the platform and not allowed in the ART boot image", name)
+		}
+	} else if image.name == frameworkBootImageName {
+		if !isApexModule || apex.IsForPlatform() {
+			// ok, this jar is part of the platform
+		} else {
+			// this jar is part of an updatable apex, fail immediately
+			ctx.Errorf("module '%s' from updatable apex '%s' is not allowed in the framework boot image", name, apex.ApexName())
+		}
+	} else {
+		panic("unknown boot image: " + image.name)
+	}
+
+	return index, jar.DexJar()
+}
+
 // buildBootImage takes a bootImageConfig, creates rules to build it, and returns the image.
 func buildBootImage(ctx android.SingletonContext, image *bootImageConfig) *bootImageConfig {
+	// Collect dex jar paths for the boot image modules.
+	// This logic is tested in the apex package to avoid import cycle apex <-> java.
 	bootDexJars := make(android.Paths, len(image.modules))
 	ctx.VisitAllModules(func(module android.Module) {
-		// Collect dex jar paths for the modules listed above.
-		if j, ok := module.(interface{ DexJar() android.Path }); ok {
-			name := ctx.ModuleName(module)
-			if i := android.IndexList(name, image.modules); i != -1 {
-				bootDexJars[i] = j.DexJar()
-			}
+		if i, j := getBootImageJar(ctx, image, module); i != -1 {
+			bootDexJars[i] = j
 		}
 	})
 
@@ -263,7 +317,8 @@
 				missingDeps = append(missingDeps, image.modules[i])
 				bootDexJars[i] = android.PathForOutput(ctx, "missing")
 			} else {
-				ctx.Errorf("failed to find dex jar path for module %q",
+				ctx.Errorf("failed to find a dex jar path for module '%s'"+
+					", note that some jars may be filtered out by module constraints",
 					image.modules[i])
 			}
 		}
diff --git a/java/dexpreopt_bootjars_test.go b/java/dexpreopt_bootjars_test.go
index 94ca8bb..127c201 100644
--- a/java/dexpreopt_bootjars_test.go
+++ b/java/dexpreopt_bootjars_test.go
@@ -53,7 +53,7 @@
 
 	ctx := testContext()
 
-	ctx.RegisterSingletonType("dex_bootjars", dexpreoptBootJarsFactory)
+	RegisterDexpreoptBootJarsComponents(ctx)
 
 	run(t, ctx, config)
 
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index 1315aba..066694c 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -81,7 +81,7 @@
 
 func getDexLocation(ctx android.PathContext, target android.Target, subdir string, name string) string {
 	if target.Os.Class == android.Host {
-		return filepath.Join("out", "host", ctx.Config().PrebuiltOS(), subdir, name)
+		return filepath.Join(ctx.Config().Getenv("OUT_DIR"), "host", ctx.Config().PrebuiltOS(), subdir, name)
 	} else {
 		return filepath.Join("/", subdir, name)
 	}
diff --git a/java/java.go b/java/java.go
index c81b781..a8ca3ff 100644
--- a/java/java.go
+++ b/java/java.go
@@ -1760,11 +1760,6 @@
 	if staticLibTag == ctx.OtherModuleDependencyTag(dep) {
 		return true
 	}
-	// Also, a dependency to an sdk member is also considered as such. This is required because
-	// sdk members should be mutated into APEXes. Refer to sdk.sdkDepsReplaceMutator.
-	if sa, ok := dep.(android.SdkAware); ok && sa.IsInAnySdk() {
-		return true
-	}
 	return false
 }
 
@@ -2513,11 +2508,6 @@
 	if staticLibTag == ctx.OtherModuleDependencyTag(dep) {
 		return true
 	}
-	// Also, a dependency to an sdk member is also considered as such. This is required because
-	// sdk members should be mutated into APEXes. Refer to sdk.sdkDepsReplaceMutator.
-	if sa, ok := dep.(android.SdkAware); ok && sa.IsInAnySdk() {
-		return true
-	}
 	return false
 }
 
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 52c9004..efee7da 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -86,6 +86,9 @@
 	// *current. Older stubs library built with a numbered SDK version is created from
 	// the prebuilt jar.
 	sdkVersion string
+
+	// Extra arguments to pass to droidstubs for this scope.
+	droidstubsArgs []string
 }
 
 // Initialize a scope, creating and adding appropriate dependency tags
@@ -131,6 +134,7 @@
 		moduleSuffix:              sdkSystemApiSuffix,
 		apiFileMakeVariableSuffix: "_SYSTEM",
 		sdkVersion:                "system_current",
+		droidstubsArgs:            []string{"-showAnnotation android.annotation.SystemApi"},
 	})
 	apiScopeTest = initApiScope(&apiScope{
 		name:                      "test",
@@ -138,6 +142,7 @@
 		moduleSuffix:              sdkTestApiSuffix,
 		apiFileMakeVariableSuffix: "_TEST",
 		sdkVersion:                "test_current",
+		droidstubsArgs:            []string{"-showAnnotation android.annotation.TestApi"},
 	})
 	allApiScopes = apiScopes{
 		apiScopePublic,
@@ -458,7 +463,7 @@
 	mctx.CreateModule(LibraryFactory, &props)
 }
 
-// Creates a droiddoc module that creates stubs source files from the given full source
+// Creates a droidstubs module that creates stubs source files from the given full source
 // files
 func (module *SdkLibrary) createStubsSources(mctx android.LoadHookContext, apiScope *apiScope) {
 	props := struct {
@@ -516,15 +521,15 @@
 	props.Merge_annotations_dirs = module.sdkLibraryProperties.Merge_annotations_dirs
 	props.Merge_inclusion_annotations_dirs = module.sdkLibraryProperties.Merge_inclusion_annotations_dirs
 
-	droiddocArgs := []string{}
+	droidstubsArgs := []string{}
 	if len(module.sdkLibraryProperties.Api_packages) != 0 {
-		droiddocArgs = append(droiddocArgs, "--stub-packages "+strings.Join(module.sdkLibraryProperties.Api_packages, ":"))
+		droidstubsArgs = append(droidstubsArgs, "--stub-packages "+strings.Join(module.sdkLibraryProperties.Api_packages, ":"))
 	}
 	if len(module.sdkLibraryProperties.Hidden_api_packages) != 0 {
-		droiddocArgs = append(droiddocArgs,
+		droidstubsArgs = append(droidstubsArgs,
 			android.JoinWithPrefix(module.sdkLibraryProperties.Hidden_api_packages, " --hide-package "))
 	}
-	droiddocArgs = append(droiddocArgs, module.sdkLibraryProperties.Droiddoc_options...)
+	droidstubsArgs = append(droidstubsArgs, module.sdkLibraryProperties.Droiddoc_options...)
 	disabledWarnings := []string{
 		"MissingPermission",
 		"BroadcastBehavior",
@@ -536,16 +541,12 @@
 		"Todo",
 		"Typo",
 	}
-	droiddocArgs = append(droiddocArgs, android.JoinWithPrefix(disabledWarnings, "--hide "))
+	droidstubsArgs = append(droidstubsArgs, android.JoinWithPrefix(disabledWarnings, "--hide "))
 
-	switch apiScope {
-	case apiScopeSystem:
-		droiddocArgs = append(droiddocArgs, "-showAnnotation android.annotation.SystemApi")
-	case apiScopeTest:
-		droiddocArgs = append(droiddocArgs, " -showAnnotation android.annotation.TestApi")
-	}
+	// Add in scope specific arguments.
+	droidstubsArgs = append(droidstubsArgs, apiScope.droidstubsArgs...)
 	props.Arg_files = module.sdkLibraryProperties.Droiddoc_option_files
-	props.Args = proptools.StringPtr(strings.Join(droiddocArgs, " "))
+	props.Args = proptools.StringPtr(strings.Join(droidstubsArgs, " "))
 
 	// List of APIs identified from the provided source files are created. They are later
 	// compared against to the not-yet-released (a.k.a current) list of APIs and to the
diff --git a/rust/compiler.go b/rust/compiler.go
index 4593165..81b258c 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -222,7 +222,10 @@
 	if ctx.toolchain().Is64Bit() && compiler.dir64 != "" {
 		dir = compiler.dir64
 	}
-	if !ctx.Host() || ctx.Target().NativeBridge == android.NativeBridgeEnabled {
+	if ctx.Target().NativeBridge == android.NativeBridgeEnabled {
+		dir = filepath.Join(dir, ctx.Target().NativeBridgeRelativePath)
+	}
+	if !ctx.Host() && ctx.Config().HasMultilibConflict(ctx.Arch().ArchType) {
 		dir = filepath.Join(dir, ctx.Arch().ArchType.String())
 	}
 	return android.PathForModuleInstall(ctx, dir, compiler.subDir,
diff --git a/rust/library.go b/rust/library.go
index 0cf2dd0..bf863bb 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -318,6 +318,8 @@
 
 	if ctx.toolchain().Bionic() && (library.dylib() || library.shared()) {
 		deps = library.baseCompiler.bionicDeps(ctx, deps)
+		deps.CrtBegin = "crtbegin_so"
+		deps.CrtEnd = "crtend_so"
 	}
 
 	return deps
diff --git a/rust/rust.go b/rust/rust.go
index f446ef0..6fe8871 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
 }
diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go
index 9e6086f..780da9f 100644
--- a/sdk/cc_sdk_test.go
+++ b/sdk/cc_sdk_test.go
@@ -1766,7 +1766,6 @@
     sdk_member_name: "stubslib",
     installable: false,
     stubs: {
-        symbol_file: "etc/stubslib.map.txt",
         versions: ["3"],
     },
     arch: {
@@ -1783,7 +1782,6 @@
     name: "stubslib",
     prefer: false,
     stubs: {
-        symbol_file: "etc/stubslib.map.txt",
         versions: ["3"],
     },
     arch: {
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index cbffb50..4a2c053 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -53,30 +53,18 @@
 			system_modules: "none",
 			sdk_version: "none",
 			host_supported: true,
-			apex_available: [
-				"//apex_available:platform",
-				"//apex_available:anyapex",
-			],
 		}
 
 		java_import {
 			name: "sdkmember_mysdk_1",
 			sdk_member_name: "sdkmember",
 			host_supported: true,
-			apex_available: [
-				"//apex_available:platform",
-				"//apex_available:anyapex",
-			],
 		}
 
 		java_import {
 			name: "sdkmember_mysdk_2",
 			sdk_member_name: "sdkmember",
 			host_supported: true,
-			apex_available: [
-				"//apex_available:platform",
-				"//apex_available:anyapex",
-			],
 		}
 
 		java_library {
diff --git a/sdk/sdk.go b/sdk/sdk.go
index dabdf85..cb5a605 100644
--- a/sdk/sdk.go
+++ b/sdk/sdk.go
@@ -312,7 +312,7 @@
 	ctx.BottomUp("SdkMemberInterVersion", memberInterVersionMutator).Parallel()
 }
 
-// RegisterPostDepshMutators registers post-deps mutators to support modules implementing SdkAware
+// RegisterPostDepsMutators registers post-deps mutators to support modules implementing SdkAware
 // interface and the sdk module type. This function has been made public to be called by tests
 // outside of the sdk package
 func RegisterPostDepsMutators(ctx android.RegisterMutatorsContext) {
@@ -431,23 +431,31 @@
 	}
 }
 
-// Step 6: ensure that the dependencies from outside of the APEX are all from the required SDKs
+// Step 6: ensure that the dependencies outside of the APEX are all from the required SDKs
 func sdkRequirementsMutator(mctx android.TopDownMutatorContext) {
 	if m, ok := mctx.Module().(interface {
-		DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool
-		RequiredSdks() android.SdkRefs
+		android.DepIsInSameApex
+		android.RequiredSdks
 	}); ok {
 		requiredSdks := m.RequiredSdks()
 		if len(requiredSdks) == 0 {
 			return
 		}
 		mctx.VisitDirectDeps(func(dep android.Module) {
-			if mctx.OtherModuleDependencyTag(dep) == android.DefaultsDepTag {
+			tag := mctx.OtherModuleDependencyTag(dep)
+			if tag == android.DefaultsDepTag {
 				// dependency to defaults is always okay
 				return
 			}
 
-			// If the dep is from outside of the APEX, but is not in any of the
+			// Ignore the dependency from the unversioned member to any versioned members as an
+			// apex that depends on the unversioned member will not also be depending on a versioned
+			// member.
+			if _, ok := tag.(sdkMemberVersionedDepTag); ok {
+				return
+			}
+
+			// If the dep is outside of the APEX, but is not in any of the
 			// required SDKs, we know that the dep is a violation.
 			if sa, ok := dep.(android.SdkAware); ok {
 				if !m.DepIsInSameApex(mctx, dep) && !requiredSdks.Contains(sa.ContainingSdk()) {
diff --git a/ui/build/sandbox_linux.go b/ui/build/sandbox_linux.go
index 5ca83cc..4c3bac3 100644
--- a/ui/build/sandbox_linux.go
+++ b/ui/build/sandbox_linux.go
@@ -79,7 +79,7 @@
 		sandboxConfig.outDir = absPath(c.ctx, c.config.OutDir())
 		sandboxConfig.distDir = absPath(c.ctx, c.config.DistDir())
 
-		cmd := exec.CommandContext(c.ctx.Context, nsjailPath,
+		sandboxArgs := []string{
 			"-H", "android-build",
 			"-e",
 			"-u", "nobody",
@@ -88,10 +88,21 @@
 			"-B", sandboxConfig.srcDir,
 			"-B", "/tmp",
 			"-B", sandboxConfig.outDir,
-			"-B", sandboxConfig.distDir,
+		}
+
+		if _, err := os.Stat(sandboxConfig.distDir); !os.IsNotExist(err) {
+			//Mount dist dir as read-write if it already exists
+			sandboxArgs = append(sandboxArgs, "-B",
+				sandboxConfig.distDir)
+		}
+
+		sandboxArgs = append(sandboxArgs,
 			"--disable_clone_newcgroup",
 			"--",
 			"/bin/bash", "-c", `if [ $(hostname) == "android-build" ]; then echo "Android" "Success"; else echo Failure; fi`)
+
+		cmd := exec.CommandContext(c.ctx.Context, nsjailPath, sandboxArgs...)
+
 		cmd.Env = c.config.Environment().Environ()
 
 		c.ctx.Verboseln(cmd.Args)
@@ -164,9 +175,6 @@
 		//Mount out dir as read-write
 		"-B", sandboxConfig.outDir,
 
-		//Mount dist dir as read-write
-		"-B", sandboxConfig.distDir,
-
 		// Mount a writable tmp dir
 		"-B", "/tmp",
 
@@ -178,6 +186,11 @@
 		"-q",
 	}
 
+	if _, err := os.Stat(sandboxConfig.distDir); !os.IsNotExist(err) {
+		//Mount dist dir as read-write if it already exists
+		sandboxArgs = append(sandboxArgs, "-B", sandboxConfig.distDir)
+	}
+
 	if c.Sandbox.AllowBuildBrokenUsesNetwork && c.config.BuildBrokenUsesNetwork() {
 		c.ctx.Printf("AllowBuildBrokenUsesNetwork: %v", c.Sandbox.AllowBuildBrokenUsesNetwork)
 		c.ctx.Printf("BuildBrokenUsesNetwork: %v", c.config.BuildBrokenUsesNetwork())