Merge "Use flag names with merge_csv.py."
diff --git a/android/visibility.go b/android/visibility.go
index a597687..3f04123 100644
--- a/android/visibility.go
+++ b/android/visibility.go
@@ -186,8 +186,8 @@
 var visibilityRuleMap = NewOnceKey("visibilityRuleMap")
 
 // The map from qualifiedModuleName to visibilityRule.
-func moduleToVisibilityRuleMap(ctx BaseModuleContext) *sync.Map {
-	return ctx.Config().Once(visibilityRuleMap, func() interface{} {
+func moduleToVisibilityRuleMap(config Config) *sync.Map {
+	return config.Once(visibilityRuleMap, func() interface{} {
 		return &sync.Map{}
 	}).(*sync.Map)
 }
@@ -304,7 +304,7 @@
 	if visibility := m.visibility(); visibility != nil {
 		rule := parseRules(ctx, currentPkg, m.visibility())
 		if rule != nil {
-			moduleToVisibilityRuleMap(ctx).Store(qualifiedModuleId, rule)
+			moduleToVisibilityRuleMap(ctx.Config()).Store(qualifiedModuleId, rule)
 		}
 	}
 }
@@ -312,6 +312,7 @@
 func parseRules(ctx BaseModuleContext, currentPkg 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)
@@ -328,6 +329,7 @@
 				isPrivateRule = true
 			case "public":
 				r = publicRule{}
+				hasPublicRule = true
 			}
 		} else {
 			switch name {
@@ -355,6 +357,11 @@
 		return compositeRule{privateRule{}}
 	}
 
+	if hasPublicRule {
+		// Public overrides all other rules so just return it.
+		return compositeRule{publicRule{}}
+	}
+
 	return rules
 }
 
@@ -415,21 +422,21 @@
 			return
 		}
 
-		rule := effectiveVisibilityRules(ctx, depQualified)
+		rule := effectiveVisibilityRules(ctx.Config(), depQualified)
 		if rule != nil && !rule.matches(qualified) {
 			ctx.ModuleErrorf("depends on %s which is not visible to this module", depQualified)
 		}
 	})
 }
 
-func effectiveVisibilityRules(ctx BaseModuleContext, qualified qualifiedModuleName) compositeRule {
-	moduleToVisibilityRule := moduleToVisibilityRuleMap(ctx)
+func effectiveVisibilityRules(config Config, qualified qualifiedModuleName) compositeRule {
+	moduleToVisibilityRule := moduleToVisibilityRuleMap(config)
 	value, ok := moduleToVisibilityRule.Load(qualified)
 	var rule compositeRule
 	if ok {
 		rule = value.(compositeRule)
 	} else {
-		rule = packageDefaultVisibility(ctx, qualified)
+		rule = packageDefaultVisibility(config, qualified)
 	}
 	return rule
 }
@@ -441,8 +448,8 @@
 	return qualified
 }
 
-func packageDefaultVisibility(ctx BaseModuleContext, moduleId qualifiedModuleName) compositeRule {
-	moduleToVisibilityRule := moduleToVisibilityRuleMap(ctx)
+func packageDefaultVisibility(config Config, moduleId qualifiedModuleName) compositeRule {
+	moduleToVisibilityRule := moduleToVisibilityRuleMap(config)
 	packageQualifiedId := moduleId.getContainingPackageId()
 	for {
 		value, ok := moduleToVisibilityRule.Load(packageQualifiedId)
@@ -469,7 +476,7 @@
 	dir := ctx.OtherModuleDir(module)
 	qualified := qualifiedModuleName{dir, moduleName}
 
-	rule := effectiveVisibilityRules(ctx, qualified)
+	rule := effectiveVisibilityRules(ctx.Config(), qualified)
 
 	return rule.Strings()
 }
diff --git a/android/visibility_test.go b/android/visibility_test.go
index 6006072..8dd6a8f 100644
--- a/android/visibility_test.go
+++ b/android/visibility_test.go
@@ -1,15 +1,17 @@
 package android
 
 import (
+	"reflect"
 	"testing"
 
 	"github.com/google/blueprint"
 )
 
 var visibilityTests = []struct {
-	name           string
-	fs             map[string][]byte
-	expectedErrors []string
+	name                string
+	fs                  map[string][]byte
+	expectedErrors      []string
+	effectiveVisibility map[qualifiedModuleName][]string
 }{
 	{
 		name: "invalid visibility: empty list",
@@ -493,6 +495,9 @@
 					deps: ["libexample"],
 				}`),
 		},
+		effectiveVisibility: map[qualifiedModuleName][]string{
+			qualifiedModuleName{pkg: "top", name: "libexample"}: {"//visibility:public"},
+		},
 	},
 	{
 		name: "//visibility:public mixed with other from different defaults 1",
@@ -903,13 +908,27 @@
 func TestVisibility(t *testing.T) {
 	for _, test := range visibilityTests {
 		t.Run(test.name, func(t *testing.T) {
-			_, errs := testVisibility(buildDir, test.fs)
+			ctx, errs := testVisibility(buildDir, test.fs)
 
 			CheckErrorsAgainstExpectations(t, errs, test.expectedErrors)
+
+			if test.effectiveVisibility != nil {
+				checkEffectiveVisibility(t, ctx, test.effectiveVisibility)
+			}
 		})
 	}
 }
 
+func checkEffectiveVisibility(t *testing.T, ctx *TestContext, effectiveVisibility map[qualifiedModuleName][]string) {
+	for moduleName, expectedRules := range effectiveVisibility {
+		rule := effectiveVisibilityRules(ctx.config, moduleName)
+		stringRules := rule.Strings()
+		if !reflect.DeepEqual(expectedRules, stringRules) {
+			t.Errorf("effective rules mismatch: expected %q, found %q", expectedRules, stringRules)
+		}
+	}
+}
+
 func testVisibility(buildDir string, fs map[string][]byte) (*TestContext, []error) {
 
 	// Create a new config per test as visibility information is stored in the config.
diff --git a/android/vts_config.go b/android/vts_config.go
index 86f6e72..9a1df7c 100644
--- a/android/vts_config.go
+++ b/android/vts_config.go
@@ -17,6 +17,7 @@
 import (
 	"fmt"
 	"io"
+	"strings"
 )
 
 func init() {
@@ -26,6 +27,8 @@
 type vtsConfigProperties struct {
 	// Override the default (AndroidTest.xml) test manifest file name.
 	Test_config *string
+	// Additional test suites to add the test to.
+	Test_suites []string `android:"arch_variant"`
 }
 
 type VtsConfig struct {
@@ -50,7 +53,8 @@
 				fmt.Fprintf(w, "LOCAL_TEST_CONFIG := %s\n",
 					*me.properties.Test_config)
 			}
-			fmt.Fprintln(w, "LOCAL_COMPATIBILITY_SUITE := vts")
+			fmt.Fprintf(w, "LOCAL_COMPATIBILITY_SUITE := vts %s\n",
+				strings.Join(me.properties.Test_suites, " "))
 		},
 	}
 	return androidMkData
diff --git a/apex/apex.go b/apex/apex.go
index 002bf5b..79fdb71 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -1275,6 +1275,11 @@
 	Legacy_android10_support *bool
 
 	IsCoverageVariant bool `blueprint:"mutated"`
+
+	// Whether this APEX is considered updatable or not. When set to true, this will enforce additional
+	// rules for making sure that the APEX is truely updatable. This will also disable the size optimizations
+	// like symlinking to the system libs. Default is false.
+	Updatable *bool
 }
 
 type apexTargetBundleProperties struct {
@@ -2111,13 +2116,6 @@
 						return false
 					}
 					filesInfo = append(filesInfo, af)
-
-					pf := sdkLib.XmlPermissionsFile()
-					if pf == nil {
-						ctx.PropertyErrorf("java_libs", "%q failed to generate permission XML", depName)
-						return false
-					}
-					filesInfo = append(filesInfo, newApexFile(ctx, pf, pf.Base(), "etc/permissions", etc, nil))
 					return true // track transitive dependencies
 				} else {
 					ctx.PropertyErrorf("java_libs", "%q of type %q is not supported", depName, ctx.OtherModuleType(child))
@@ -2230,6 +2228,10 @@
 					}
 				} else if java.IsJniDepTag(depTag) {
 					return true
+				} else if java.IsXmlPermissionsFileDepTag(depTag) {
+					if prebuilt, ok := child.(android.PrebuiltEtcModule); ok {
+						filesInfo = append(filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, depName))
+					}
 				} else if am.CanHaveApexVariants() && am.IsInstallableToApex() {
 					ctx.ModuleErrorf("unexpected tag %q for indirect dependency %q", depTag, depName)
 				}
@@ -2312,6 +2314,12 @@
 		a.installable() &&
 		!proptools.Bool(a.properties.Use_vendor)
 
+	// We don't need the optimization for updatable APEXes, as it might give false signal
+	// to the system health when the APEXes are still bundled (b/149805758)
+	if proptools.Bool(a.properties.Updatable) && a.properties.ApexType == imageApex {
+		a.linkToSystemLib = false
+	}
+
 	// prepare apex_manifest.json
 	a.buildManifest(ctx, provideNativeLibs, requireNativeLibs)
 
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 5000c88..e5847ab 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -223,6 +223,7 @@
 		"apex_manifest.json":                                  nil,
 		"AndroidManifest.xml":                                 nil,
 		"system/sepolicy/apex/myapex-file_contexts":           nil,
+		"system/sepolicy/apex/myapex.updatable-file_contexts": nil,
 		"system/sepolicy/apex/myapex2-file_contexts":          nil,
 		"system/sepolicy/apex/otherapex-file_contexts":        nil,
 		"system/sepolicy/apex/commonapex-file_contexts":       nil,
@@ -3531,9 +3532,8 @@
 		"etc/permissions/foo.xml",
 	})
 	// Permission XML should point to the activated path of impl jar of java_sdk_library
-	sdkLibrary := ctx.ModuleForTests("foo", "android_common_myapex").Module().(*java.SdkLibrary)
-	xml := sdkLibrary.XmlPermissionsFileContent()
-	ensureContains(t, xml, `<library name="foo" file="/apex/myapex/javalib/foo.jar"`)
+	sdkLibrary := ctx.ModuleForTests("foo.xml", "android_common_myapex").Rule("java_sdk_xml")
+	ensureContains(t, sdkLibrary.RuleParams.Command, `<library name=\"foo\" file=\"/apex/myapex/javalib/foo.jar\"`)
 }
 
 func TestCompatConfig(t *testing.T) {
@@ -3641,6 +3641,14 @@
 			java_libs: ["myjar"],
 		}
 
+		apex {
+			name: "myapex.updatable",
+			key: "myapex.key",
+			native_shared_libs: ["mylib"],
+			java_libs: ["myjar"],
+			updatable: true,
+		}
+
 		apex_key {
 			name: "myapex.key",
 			public_key: "testkey.avbpubkey",
@@ -3655,6 +3663,7 @@
 			stl: "none",
 			apex_available: [
 				"myapex",
+				"myapex.updatable",
 				"//apex_available:platform",
 			],
 		}
@@ -3666,6 +3675,7 @@
 			stl: "none",
 			apex_available: [
 				"myapex",
+				"myapex.updatable",
 				"//apex_available:platform",
 			],
 		}
@@ -3678,6 +3688,7 @@
 			libs: ["myotherjar"],
 			apex_available: [
 				"myapex",
+				"myapex.updatable",
 				"//apex_available:platform",
 			],
 		}
@@ -3689,6 +3700,7 @@
 			system_modules: "none",
 			apex_available: [
 				"myapex",
+				"myapex.updatable",
 				"//apex_available:platform",
 			],
 		}
@@ -3718,17 +3730,30 @@
 		t.Errorf("%q is not found", file)
 	}
 
+	// For unbundled build, symlink shouldn't exist regardless of whether an APEX
+	// is updatable or not
 	ctx, _ := testApex(t, bp, withUnbundledBuild)
 	files := getFiles(t, ctx, "myapex", "android_common_myapex_image")
 	ensureRealfileExists(t, files, "javalib/myjar.jar")
 	ensureRealfileExists(t, files, "lib64/mylib.so")
 	ensureRealfileExists(t, files, "lib64/myotherlib.so")
 
+	files = getFiles(t, ctx, "myapex.updatable", "android_common_myapex.updatable_image")
+	ensureRealfileExists(t, files, "javalib/myjar.jar")
+	ensureRealfileExists(t, files, "lib64/mylib.so")
+	ensureRealfileExists(t, files, "lib64/myotherlib.so")
+
+	// For bundled build, symlink to the system for the non-updatable APEXes only
 	ctx, _ = testApex(t, bp)
 	files = getFiles(t, ctx, "myapex", "android_common_myapex_image")
 	ensureRealfileExists(t, files, "javalib/myjar.jar")
 	ensureRealfileExists(t, files, "lib64/mylib.so")
 	ensureSymlinkExists(t, files, "lib64/myotherlib.so") // this is symlink
+
+	files = getFiles(t, ctx, "myapex.updatable", "android_common_myapex.updatable_image")
+	ensureRealfileExists(t, files, "javalib/myjar.jar")
+	ensureRealfileExists(t, files, "lib64/mylib.so")
+	ensureRealfileExists(t, files, "lib64/myotherlib.so") // this is a real file
 }
 
 func TestMain(m *testing.M) {
diff --git a/bpf/bpf.go b/bpf/bpf.go
index 1d792ef..59d1502 100644
--- a/bpf/bpf.go
+++ b/bpf/bpf.go
@@ -66,6 +66,8 @@
 		// The architecture doesn't matter here, but asm/types.h is included by linux/types.h.
 		"-isystem bionic/libc/kernel/uapi/asm-arm64",
 		"-isystem bionic/libc/kernel/android/uapi",
+		// TODO(b/149785767): only give access to specific file with AID_* constants
+		"-I       system/core/libcutils/include",
 		"-I       system/bpf/progs/include",
 		"-I " + ctx.ModuleDir(),
 	}
diff --git a/cc/androidmk.go b/cc/androidmk.go
index d8210fc..a78e455 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -413,12 +413,9 @@
 }
 
 func (c *vndkPrebuiltLibraryDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
-	// Each vndk prebuilt is exported to androidMk only when BOARD_VNDK_VERSION != current
-	// and the version of the prebuilt is same as BOARD_VNDK_VERSION.
 	ret.Class = "SHARED_LIBRARIES"
 
-	// shouldn't add any suffixes due to mk modules
-	ret.SubName = ""
+	ret.SubName = c.androidMkSuffix
 
 	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
 		c.libraryDecorator.androidMkWriteExportedFlags(w)
@@ -455,19 +452,21 @@
 	ret.Extra = append(ret.Extra, func(w io.Writer, outputFile android.Path) {
 		c.libraryDecorator.androidMkWriteExportedFlags(w)
 
-		if c.shared() {
+		if c.shared() || c.static() {
 			path, file := filepath.Split(c.path.ToMakePath().String())
 			stem, suffix, ext := android.SplitFileExt(file)
 			fmt.Fprintln(w, "LOCAL_BUILT_MODULE_STEM := $(LOCAL_MODULE)"+ext)
 			fmt.Fprintln(w, "LOCAL_MODULE_SUFFIX := "+suffix)
+			fmt.Fprintln(w, "LOCAL_MODULE_STEM := "+stem)
 			if c.shared() {
 				fmt.Fprintln(w, "LOCAL_MODULE_PATH := "+path)
-				fmt.Fprintln(w, "LOCAL_MODULE_STEM := "+stem)
 			}
 			if c.tocFile.Valid() {
 				fmt.Fprintln(w, "LOCAL_SOONG_TOC := "+c.tocFile.String())
 			}
-		} else { // static or header
+		}
+
+		if !c.shared() { // static or header
 			fmt.Fprintln(w, "LOCAL_UNINSTALLABLE_MODULE := true")
 		}
 	})
diff --git a/cc/config/vndk.go b/cc/config/vndk.go
index 5cecbd6..54f693e 100644
--- a/cc/config/vndk.go
+++ b/cc/config/vndk.go
@@ -19,6 +19,7 @@
 // has VndkUseCoreVariant set.
 var VndkMustUseVendorVariantList = []string{
 	"android.hardware.light-ndk_platform",
+	"android.hardware.identity-ndk_platform",
 	"android.hardware.nfc@1.2",
 	"android.hardware.power-ndk_platform",
 	"android.hardware.vibrator-ndk_platform",
diff --git a/cc/gen.go b/cc/gen.go
index 17ab45f..b0aadc6 100644
--- a/cc/gen.go
+++ b/cc/gen.go
@@ -162,17 +162,19 @@
 	})
 }
 
-func genSysprop(ctx android.ModuleContext, syspropFile android.Path) (android.Path, android.Path) {
+func genSysprop(ctx android.ModuleContext, syspropFile android.Path) (android.Path, android.Paths) {
 	headerFile := android.PathForModuleGen(ctx, "sysprop", "include", syspropFile.Rel()+".h")
 	publicHeaderFile := android.PathForModuleGen(ctx, "sysprop/public", "include", syspropFile.Rel()+".h")
 	cppFile := android.PathForModuleGen(ctx, "sysprop", syspropFile.Rel()+".cpp")
 
+	headers := android.WritablePaths{headerFile, publicHeaderFile}
+
 	ctx.Build(pctx, android.BuildParams{
-		Rule:           sysprop,
-		Description:    "sysprop " + syspropFile.Rel(),
-		Output:         cppFile,
-		ImplicitOutput: headerFile,
-		Input:          syspropFile,
+		Rule:            sysprop,
+		Description:     "sysprop " + syspropFile.Rel(),
+		Output:          cppFile,
+		ImplicitOutputs: headers,
+		Input:           syspropFile,
 		Args: map[string]string{
 			"headerOutDir": filepath.Dir(headerFile.String()),
 			"publicOutDir": filepath.Dir(publicHeaderFile.String()),
@@ -181,7 +183,7 @@
 		},
 	})
 
-	return cppFile, headerFile
+	return cppFile, headers.Paths()
 }
 
 func genWinMsg(ctx android.ModuleContext, srcFile android.Path, flags builderFlags) (android.Path, android.Path) {
@@ -259,9 +261,9 @@
 			srcFiles[i] = rcFile
 			deps = append(deps, headerFile)
 		case ".sysprop":
-			cppFile, headerFile := genSysprop(ctx, srcFile)
+			cppFile, headerFiles := genSysprop(ctx, srcFile)
 			srcFiles[i] = cppFile
-			deps = append(deps, headerFile)
+			deps = append(deps, headerFiles...)
 		}
 	}
 
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index d92caa1..aed7918 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -41,7 +41,7 @@
 	vendorSnapshotBinariesKey   = android.NewOnceKey("vendorSnapshotBinaries")
 )
 
-// vendor snapshot maps hold names of vendor snapshot modules per arch.
+// vendor snapshot maps hold names of vendor snapshot modules per arch
 func vendorSuffixModules(config android.Config) map[string]bool {
 	return config.Once(vendorSuffixModulesKey, func() interface{} {
 		return make(map[string]bool)
@@ -772,6 +772,10 @@
 
 // Disables source modules which have snapshots
 func VendorSnapshotSourceMutator(ctx android.BottomUpMutatorContext) {
+	if !ctx.Device() {
+		return
+	}
+
 	vndkVersion := ctx.DeviceConfig().VndkVersion()
 	// don't need snapshot if current
 	if vndkVersion == "current" || vndkVersion == "" {
@@ -783,11 +787,19 @@
 		return
 	}
 
-	if module.HasVendorVariant() {
-		vendorSnapshotsLock.Lock()
-		defer vendorSnapshotsLock.Unlock()
+	// vendor suffix should be added to snapshots if the source module isn't vendor: true.
+	if !module.SocSpecific() {
+		// But we can't just check SocSpecific() since we already passed the image mutator.
+		// Check ramdisk and recovery to see if we are real "vendor: true" module.
+		ramdisk_available := module.InRamdisk() && !module.OnlyInRamdisk()
+		recovery_available := module.InRecovery() && !module.OnlyInRecovery()
 
-		vendorSuffixModules(ctx.Config())[ctx.ModuleName()] = true
+		if !ramdisk_available && !recovery_available {
+			vendorSnapshotsLock.Lock()
+			defer vendorSnapshotsLock.Unlock()
+
+			vendorSuffixModules(ctx.Config())[ctx.ModuleName()] = true
+		}
 	}
 
 	if module.isSnapshotPrebuilt() || module.VndkVersion() != ctx.DeviceConfig().VndkVersion() {
diff --git a/cc/vndk_prebuilt.go b/cc/vndk_prebuilt.go
index 50bc325..53b5181 100644
--- a/cc/vndk_prebuilt.go
+++ b/cc/vndk_prebuilt.go
@@ -72,7 +72,8 @@
 
 type vndkPrebuiltLibraryDecorator struct {
 	*libraryDecorator
-	properties vndkPrebuiltProperties
+	properties      vndkPrebuiltProperties
+	androidMkSuffix string
 }
 
 func (p *vndkPrebuiltLibraryDecorator) Name(name string) string {
@@ -153,6 +154,13 @@
 		p.tocFile = android.OptionalPathForPath(tocFile)
 		TransformSharedObjectToToc(ctx, in, tocFile, builderFlags)
 
+		p.androidMkSuffix = p.NameSuffix()
+
+		vndkVersion := ctx.DeviceConfig().VndkVersion()
+		if vndkVersion == p.version() {
+			p.androidMkSuffix = ""
+		}
+
 		return in
 	}
 
@@ -224,15 +232,6 @@
 		&prebuilt.properties,
 	)
 
-	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
-		// Only vndk snapshots of BOARD_VNDK_VERSION will be used when building.
-		if prebuilt.version() != ctx.DeviceConfig().VndkVersion() {
-			module.SkipInstall()
-			module.Properties.HideFromMake = true
-			return
-		}
-	})
-
 	return module
 }
 
diff --git a/cmd/sbox/sbox.go b/cmd/sbox/sbox.go
index 7057b33..65a34fd 100644
--- a/cmd/sbox/sbox.go
+++ b/cmd/sbox/sbox.go
@@ -36,6 +36,7 @@
 	outputRoot    string
 	keepOutDir    bool
 	depfileOut    string
+	inputHash     string
 )
 
 func init() {
@@ -51,6 +52,8 @@
 	flag.StringVar(&depfileOut, "depfile-out", "",
 		"file path of the depfile to generate. This value will replace '__SBOX_DEPFILE__' in the command and will be treated as an output but won't be added to __SBOX_OUT_FILES__")
 
+	flag.StringVar(&inputHash, "input-hash", "",
+		"This option is ignored. Typical usage is to supply a hash of the list of input names so that the module will be rebuilt if the list (and thus the hash) changes.")
 }
 
 func usageViolation(violation string) {
@@ -59,7 +62,7 @@
 	}
 
 	fmt.Fprintf(os.Stderr,
-		"Usage: sbox -c <commandToRun> --sandbox-path <sandboxPath> --output-root <outputRoot> [--depfile-out depFile] <outputFile> [<outputFile>...]\n"+
+		"Usage: sbox -c <commandToRun> --sandbox-path <sandboxPath> --output-root <outputRoot> [--depfile-out depFile] [--input-hash hash] <outputFile> [<outputFile>...]\n"+
 			"\n"+
 			"Deletes <outputRoot>,"+
 			"runs <commandToRun>,"+
diff --git a/genrule/genrule.go b/genrule/genrule.go
index a0008d3..fe877fe 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -26,6 +26,7 @@
 
 	"android/soong/android"
 	"android/soong/shared"
+	"crypto/sha256"
 	"path/filepath"
 )
 
@@ -309,6 +310,7 @@
 			addLocationLabel(out.Rel(), []string{filepath.Join("__SBOX_OUT_DIR__", out.Rel())})
 		}
 
+		referencedIn := false
 		referencedDepfile := false
 
 		rawCommand, err := android.ExpandNinjaEscaped(task.cmd, func(name string) (string, bool, error) {
@@ -333,6 +335,7 @@
 				}
 				return locationLabels[firstLabel][0], false, nil
 			case "in":
+				referencedIn = true
 				return "${in}", true, nil
 			case "out":
 				return "__SBOX_OUT_FILES__", false, nil
@@ -398,8 +401,16 @@
 		// Escape the command for the shell
 		rawCommand = "'" + strings.Replace(rawCommand, "'", `'\''`, -1) + "'"
 		g.rawCommands = append(g.rawCommands, rawCommand)
-		sandboxCommand := fmt.Sprintf("rm -rf %s && $sboxCmd --sandbox-path %s --output-root %s -c %s %s $allouts",
-			task.genDir, sandboxPath, task.genDir, rawCommand, depfilePlaceholder)
+
+		sandboxCommand := fmt.Sprintf("rm -rf %s && $sboxCmd --sandbox-path %s --output-root %s",
+			task.genDir, sandboxPath, task.genDir)
+
+		if !referencedIn {
+			sandboxCommand = sandboxCommand + hashSrcFiles(srcFiles)
+		}
+
+		sandboxCommand = sandboxCommand + fmt.Sprintf(" -c %s %s $allouts",
+			rawCommand, depfilePlaceholder)
 
 		ruleParams := blueprint.RuleParams{
 			Command:     sandboxCommand,
@@ -463,6 +474,14 @@
 
 }
 
+func hashSrcFiles(srcFiles android.Paths) string {
+	h := sha256.New()
+	for _, src := range srcFiles {
+		h.Write([]byte(src.String()))
+	}
+	return fmt.Sprintf(" --input-hash %x", h.Sum(nil))
+}
+
 func (g *Module) generateSourceFile(ctx android.ModuleContext, task generateTask, rule blueprint.Rule) {
 	desc := "generate"
 	if len(task.out) == 0 {
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index 7eb43ac..4b36600 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -502,6 +502,93 @@
 	}
 }
 
+func TestGenruleHashInputs(t *testing.T) {
+
+	// The basic idea here is to verify that the sbox command (which is
+	// in the Command field of the generate rule) contains a hash of the
+	// inputs, but only if $(in) is not referenced in the genrule cmd
+	// property.
+
+	// By including a hash of the inputs, we cause the rule to re-run if
+	// the list of inputs changes (because the sbox command changes).
+
+	// However, if the genrule cmd property already contains $(in), then
+	// the dependency is already expressed, so we don't need to include the
+	// hash in that case.
+
+	bp := `
+			genrule {
+				name: "hash0",
+				srcs: ["in1.txt", "in2.txt"],
+				out: ["out"],
+				cmd: "echo foo > $(out)",
+			}
+			genrule {
+				name: "hash1",
+				srcs: ["*.txt"],
+				out: ["out"],
+				cmd: "echo bar > $(out)",
+			}
+			genrule {
+				name: "hash2",
+				srcs: ["*.txt"],
+				out: ["out"],
+				cmd: "echo $(in) > $(out)",
+			}
+		`
+	testcases := []struct {
+		name         string
+		expectedHash string
+	}{
+		{
+			name: "hash0",
+			// sha256 value obtained from: echo -n 'in1.txtin2.txt' | sha256sum
+			expectedHash: "031097e11e0a8c822c960eb9742474f46336360a515744000d086d94335a9cb9",
+		},
+		{
+			name: "hash1",
+			// sha256 value obtained from: echo -n 'in1.txtin2.txtin3.txt' | sha256sum
+			expectedHash: "de5d22a4a7ab50d250cc59fcdf7a7e0775790d270bfca3a7a9e1f18a70dd996c",
+		},
+		{
+			name: "hash2",
+			// $(in) is present, option should not appear
+			expectedHash: "",
+		},
+	}
+
+	config := testConfig(bp, nil)
+	ctx := testContext(config)
+	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
+	if errs == nil {
+		_, errs = ctx.PrepareBuildActions(config)
+	}
+	if errs != nil {
+		t.Fatal(errs)
+	}
+
+	for _, test := range testcases {
+		t.Run(test.name, func(t *testing.T) {
+			gen := ctx.ModuleForTests(test.name, "")
+			command := gen.Rule("generator").RuleParams.Command
+
+			if len(test.expectedHash) > 0 {
+				// We add spaces before and after to make sure that
+				// this option doesn't abutt another sbox option.
+				expectedInputHashOption := " --input-hash " + test.expectedHash + " "
+
+				if !strings.Contains(command, expectedInputHashOption) {
+					t.Errorf("Expected command \"%s\" to contain \"%s\"", command, expectedInputHashOption)
+				}
+			} else {
+				if strings.Contains(command, "--input-hash") {
+					t.Errorf("Unexpected \"--input-hash\" found in command: \"%s\"", command)
+				}
+			}
+		})
+	}
+}
+
 func TestGenSrcs(t *testing.T) {
 	testcases := []struct {
 		name string
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 6b39314..fd4b90d 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -547,10 +547,10 @@
 		case bootClasspathTag:
 			if dep, ok := module.(Dependency); ok {
 				deps.bootClasspath = append(deps.bootClasspath, dep.ImplementationJars()...)
-			} else if sm, ok := module.(*SystemModules); ok {
+			} else if sm, ok := module.(SystemModulesProvider); ok {
 				// A system modules dependency has been added to the bootclasspath
 				// so add its libs to the bootclasspath.
-				deps.bootClasspath = append(deps.bootClasspath, sm.headerJars...)
+				deps.bootClasspath = append(deps.bootClasspath, sm.HeaderJars()...)
 			} else {
 				panic(fmt.Errorf("unknown dependency %q for %q", otherName, ctx.ModuleName()))
 			}
@@ -578,11 +578,9 @@
 			if deps.systemModules != nil {
 				panic("Found two system module dependencies")
 			}
-			sm := module.(*SystemModules)
-			if sm.outputDir == nil && len(sm.outputDeps) == 0 {
-				panic("Missing directory for system module dependency")
-			}
-			deps.systemModules = &systemModules{sm.outputDir, sm.outputDeps}
+			sm := module.(SystemModulesProvider)
+			outputDir, outputDeps := sm.OutputDirAndDeps()
+			deps.systemModules = &systemModules{outputDir, outputDeps}
 		}
 	})
 	// do not pass exclude_srcs directly when expanding srcFiles since exclude_srcs
diff --git a/java/java.go b/java/java.go
index c3e2c96..462dba8 100644
--- a/java/java.go
+++ b/java/java.go
@@ -1031,18 +1031,16 @@
 			case bootClasspathTag:
 				// If a system modules dependency has been added to the bootclasspath
 				// then add its libs to the bootclasspath.
-				sm := module.(*SystemModules)
-				deps.bootClasspath = append(deps.bootClasspath, sm.headerJars...)
+				sm := module.(SystemModulesProvider)
+				deps.bootClasspath = append(deps.bootClasspath, sm.HeaderJars()...)
 
 			case systemModulesTag:
 				if deps.systemModules != nil {
 					panic("Found two system module dependencies")
 				}
-				sm := module.(*SystemModules)
-				if sm.outputDir == nil || len(sm.outputDeps) == 0 {
-					panic("Missing directory for system module dependency")
-				}
-				deps.systemModules = &systemModules{sm.outputDir, sm.outputDeps}
+				sm := module.(SystemModulesProvider)
+				outputDir, outputDeps := sm.OutputDirAndDeps()
+				deps.systemModules = &systemModules{outputDir, outputDeps}
 			}
 		}
 	})
diff --git a/java/java_test.go b/java/java_test.go
index a2226b5..7c06699 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -982,6 +982,65 @@
 	}
 }
 
+func TestDroidstubsWithSystemModules(t *testing.T) {
+	ctx, _ := testJava(t, `
+		droidstubs {
+		    name: "stubs-source-system-modules",
+		    srcs: [
+		        "bar-doc/*.java",
+		    ],
+				sdk_version: "none",
+				system_modules: "source-system-modules",
+		}
+
+		java_library {
+				name: "source-jar",
+		    srcs: [
+		        "a.java",
+		    ],
+		}
+
+		java_system_modules {
+				name: "source-system-modules",
+				libs: ["source-jar"],
+		}
+
+		droidstubs {
+		    name: "stubs-prebuilt-system-modules",
+		    srcs: [
+		        "bar-doc/*.java",
+		    ],
+				sdk_version: "none",
+				system_modules: "prebuilt-system-modules",
+		}
+
+		java_import {
+				name: "prebuilt-jar",
+				jars: ["a.jar"],
+		}
+
+		java_system_modules_import {
+				name: "prebuilt-system-modules",
+				libs: ["prebuilt-jar"],
+		}
+		`)
+
+	checkSystemModulesUseByDroidstubs(t, ctx, "stubs-source-system-modules", "source-jar.jar")
+
+	checkSystemModulesUseByDroidstubs(t, ctx, "stubs-prebuilt-system-modules", "prebuilt-jar.jar")
+}
+
+func checkSystemModulesUseByDroidstubs(t *testing.T, ctx *android.TestContext, moduleName string, systemJar string) {
+	metalavaRule := ctx.ModuleForTests(moduleName, "android_common").Rule("metalava")
+	var systemJars []string
+	for _, i := range metalavaRule.Implicits {
+		systemJars = append(systemJars, i.Base())
+	}
+	if len(systemJars) != 1 || systemJars[0] != systemJar {
+		t.Errorf("inputs of %q must be []string{%q}, but was %#v.", moduleName, systemJar, systemJars)
+	}
+}
+
 func TestJarGenrules(t *testing.T) {
 	ctx, _ := testJava(t, `
 		java_library {
@@ -1125,7 +1184,7 @@
 	ctx.ModuleForTests("foo"+sdkStubsSourceSuffix, "android_common")
 	ctx.ModuleForTests("foo"+sdkStubsSourceSuffix+sdkSystemApiSuffix, "android_common")
 	ctx.ModuleForTests("foo"+sdkStubsSourceSuffix+sdkTestApiSuffix, "android_common")
-	ctx.ModuleForTests("foo"+sdkXmlFileSuffix, "android_arm64_armv8-a")
+	ctx.ModuleForTests("foo"+sdkXmlFileSuffix, "android_common")
 	ctx.ModuleForTests("foo.api.public.28", "")
 	ctx.ModuleForTests("foo.api.system.28", "")
 	ctx.ModuleForTests("foo.api.test.28", "")
@@ -1377,3 +1436,59 @@
 		}
 	}
 }
+
+func TestJavaLibraryWithSystemModules(t *testing.T) {
+	ctx, _ := testJava(t, `
+		java_library {
+		    name: "lib-with-source-system-modules",
+		    srcs: [
+		        "a.java",
+		    ],
+				sdk_version: "none",
+				system_modules: "source-system-modules",
+		}
+
+		java_library {
+				name: "source-jar",
+		    srcs: [
+		        "a.java",
+		    ],
+		}
+
+		java_system_modules {
+				name: "source-system-modules",
+				libs: ["source-jar"],
+		}
+
+		java_library {
+		    name: "lib-with-prebuilt-system-modules",
+		    srcs: [
+		        "a.java",
+		    ],
+				sdk_version: "none",
+				system_modules: "prebuilt-system-modules",
+		}
+
+		java_import {
+				name: "prebuilt-jar",
+				jars: ["a.jar"],
+		}
+
+		java_system_modules_import {
+				name: "prebuilt-system-modules",
+				libs: ["prebuilt-jar"],
+		}
+		`)
+
+	checkBootClasspathForSystemModule(t, ctx, "lib-with-source-system-modules", "/source-jar.jar")
+
+	checkBootClasspathForSystemModule(t, ctx, "lib-with-prebuilt-system-modules", "/prebuilt-jar.jar")
+}
+
+func checkBootClasspathForSystemModule(t *testing.T, ctx *android.TestContext, moduleName string, expectedSuffix string) {
+	javacRule := ctx.ModuleForTests(moduleName, "android_common").Rule("javac")
+	bootClasspath := javacRule.Args["bootClasspath"]
+	if strings.HasPrefix(bootClasspath, "--system ") && strings.HasSuffix(bootClasspath, expectedSuffix) {
+		t.Errorf("bootclasspath of %q must start with --system and end with %q, but was %#v.", moduleName, expectedSuffix, bootClasspath)
+	}
+}
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 4f4029a..a8edf1d 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -16,7 +16,6 @@
 
 import (
 	"android/soong/android"
-	"android/soong/genrule"
 
 	"fmt"
 	"io"
@@ -36,23 +35,23 @@
 	sdkTestApiSuffix      = ".test"
 	sdkStubsSourceSuffix  = ".stubs.source"
 	sdkXmlFileSuffix      = ".xml"
-	permissionsTemplate   = `<?xml version="1.0" encoding="utf-8"?>\n` +
+	permissionsTemplate   = `<?xml version=\"1.0\" encoding=\"utf-8\"?>\n` +
 		`<!-- Copyright (C) 2018 The Android Open Source Project\n` +
 		`\n` +
-		`    Licensed under the Apache License, Version 2.0 (the "License");\n` +
+		`    Licensed under the Apache License, Version 2.0 (the \"License\");\n` +
 		`    you may not use this file except in compliance with the License.\n` +
 		`    You may obtain a copy of the License at\n` +
 		`\n` +
 		`        http://www.apache.org/licenses/LICENSE-2.0\n` +
 		`\n` +
 		`    Unless required by applicable law or agreed to in writing, software\n` +
-		`    distributed under the License is distributed on an "AS IS" BASIS,\n` +
+		`    distributed under the License is distributed on an \"AS IS\" BASIS,\n` +
 		`    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n` +
 		`    See the License for the specific language governing permissions and\n` +
 		`    limitations under the License.\n` +
 		`-->\n` +
 		`<permissions>\n` +
-		`    <library name="%s" file="%s"/>\n` +
+		`    <library name=\"%s\" file=\"%s\"/>\n` +
 		`</permissions>\n`
 )
 
@@ -250,8 +249,6 @@
 	sdkLibraryProperties sdkLibraryProperties
 
 	commonToSdkLibraryAndImport
-
-	permissionsFile android.Path
 }
 
 var _ Dependency = (*SdkLibrary)(nil)
@@ -267,6 +264,13 @@
 
 var xmlPermissionsFileTag = dependencyTag{name: "xml-permissions-file"}
 
+func IsXmlPermissionsFileDepTag(depTag blueprint.DependencyTag) bool {
+	if dt, ok := depTag.(dependencyTag); ok {
+		return dt == xmlPermissionsFileTag
+	}
+	return false
+}
+
 func (module *SdkLibrary) DepsMutator(ctx android.BottomUpMutatorContext) {
 	for _, apiScope := range module.getActiveApiScopes() {
 		// Add dependencies to the stubs library
@@ -278,7 +282,7 @@
 
 	if !proptools.Bool(module.sdkLibraryProperties.Api_only) {
 		// Add dependency to the rule for generating the xml permissions file
-		ctx.AddDependency(module, xmlPermissionsFileTag, module.genXmlPermissionsFileName())
+		ctx.AddDependency(module, xmlPermissionsFileTag, module.xmlFileName())
 	}
 
 	module.Library.deps(ctx)
@@ -314,18 +318,6 @@
 				ctx.ModuleErrorf("depends on module %q of unknown tag %q", otherName, tag)
 			}
 		}
-		if tag == xmlPermissionsFileTag {
-			if genRule, ok := to.(genrule.SourceFileGenerator); ok {
-				pf := genRule.GeneratedSourceFiles()
-				if len(pf) != 1 {
-					ctx.ModuleErrorf("%q failed to generate permission XML", otherName)
-				} else {
-					module.permissionsFile = pf[0]
-				}
-			} else {
-				ctx.ModuleErrorf("depends on module %q to generate xml permissions file but it does not provide any outputs", otherName)
-			}
-		}
 	})
 }
 
@@ -389,37 +381,11 @@
 	return module.BaseModuleName()
 }
 
-// File path to the runtime implementation library
-func (module *SdkLibrary) implPath() string {
-	if apexName := module.ApexName(); apexName != "" {
-		// TODO(b/146468504): ApexName() is only a soong module name, not apex name.
-		// In most cases, this works fine. But when apex_name is set or override_apex is used
-		// this can be wrong.
-		return fmt.Sprintf("/apex/%s/javalib/%s.jar", apexName, module.implName())
-	}
-	partition := "system"
-	if module.SocSpecific() {
-		partition = "vendor"
-	} else if module.DeviceSpecific() {
-		partition = "odm"
-	} else if module.ProductSpecific() {
-		partition = "product"
-	} else if module.SystemExtSpecific() {
-		partition = "system_ext"
-	}
-	return "/" + partition + "/framework/" + module.implName() + ".jar"
-}
-
 // Module name of the XML file for the lib
 func (module *SdkLibrary) xmlFileName() string {
 	return module.BaseModuleName() + sdkXmlFileSuffix
 }
 
-// Module name of the rule for generating the XML permissions file
-func (module *SdkLibrary) genXmlPermissionsFileName() string {
-	return "gen-" + module.BaseModuleName() + sdkXmlFileSuffix
-}
-
 // Get the sdk version for use when compiling the stubs library.
 func (module *SdkLibrary) sdkVersionForStubsLibrary(mctx android.LoadHookContext, apiScope *apiScope) string {
 	sdkDep := decodeSdkDep(mctx, sdkContext(&module.Library))
@@ -615,58 +581,31 @@
 	mctx.CreateModule(DroidstubsFactory, &props)
 }
 
-func (module *SdkLibrary) XmlPermissionsFile() android.Path {
-	return module.permissionsFile
-}
-
-func (module *SdkLibrary) XmlPermissionsFileContent() string {
-	return fmt.Sprintf(permissionsTemplate, module.BaseModuleName(), module.implPath())
-}
-
 // Creates the xml file that publicizes the runtime library
 func (module *SdkLibrary) createXmlFile(mctx android.LoadHookContext) {
-
-	xmlContent := module.XmlPermissionsFileContent()
-
-	genRuleName := module.genXmlPermissionsFileName()
-
-	// Create a genrule module to create the XML permissions file.
-	genRuleProps := struct {
-		Name *string
-		Cmd  *string
-		Out  []string
-	}{
-		Name: proptools.StringPtr(genRuleName),
-		Cmd:  proptools.StringPtr("echo -e '" + xmlContent + "' > '$(out)'"),
-		Out:  []string{module.xmlFileName()},
-	}
-
-	mctx.CreateModule(genrule.GenRuleFactory, &genRuleProps)
-
-	// creates a prebuilt_etc module to actually place the xml file under
-	// <partition>/etc/permissions
-	etcProps := struct {
+	props := struct {
 		Name                *string
-		Src                 *string
-		Sub_dir             *string
+		Lib_name            *string
 		Soc_specific        *bool
 		Device_specific     *bool
 		Product_specific    *bool
 		System_ext_specific *bool
-	}{}
-	etcProps.Name = proptools.StringPtr(module.xmlFileName())
-	etcProps.Src = proptools.StringPtr(":" + genRuleName)
-	etcProps.Sub_dir = proptools.StringPtr("permissions")
-	if module.SocSpecific() {
-		etcProps.Soc_specific = proptools.BoolPtr(true)
-	} else if module.DeviceSpecific() {
-		etcProps.Device_specific = proptools.BoolPtr(true)
-	} else if module.ProductSpecific() {
-		etcProps.Product_specific = proptools.BoolPtr(true)
-	} else if module.SystemExtSpecific() {
-		etcProps.System_ext_specific = proptools.BoolPtr(true)
+	}{
+		Name:     proptools.StringPtr(module.xmlFileName()),
+		Lib_name: proptools.StringPtr(module.BaseModuleName()),
 	}
-	mctx.CreateModule(android.PrebuiltEtcFactory, &etcProps)
+
+	if module.SocSpecific() {
+		props.Soc_specific = proptools.BoolPtr(true)
+	} else if module.DeviceSpecific() {
+		props.Device_specific = proptools.BoolPtr(true)
+	} else if module.ProductSpecific() {
+		props.Product_specific = proptools.BoolPtr(true)
+	} else if module.SystemExtSpecific() {
+		props.System_ext_specific = proptools.BoolPtr(true)
+	}
+
+	mctx.CreateModule(sdkLibraryXmlFactory, &props)
 }
 
 func PrebuiltJars(ctx android.BaseModuleContext, baseName string, s sdkSpec) android.Paths {
@@ -1039,3 +978,111 @@
 	// This module is just a wrapper for the stubs.
 	return module.sdkJars(ctx, sdkVersion)
 }
+
+//
+// java_sdk_library_xml
+//
+type sdkLibraryXml struct {
+	android.ModuleBase
+	android.DefaultableModuleBase
+	android.ApexModuleBase
+
+	properties sdkLibraryXmlProperties
+
+	outputFilePath android.OutputPath
+	installDirPath android.InstallPath
+}
+
+type sdkLibraryXmlProperties struct {
+	// canonical name of the lib
+	Lib_name *string
+}
+
+// java_sdk_library_xml builds the permission xml file for a java_sdk_library.
+// Not to be used directly by users. java_sdk_library internally uses this.
+func sdkLibraryXmlFactory() android.Module {
+	module := &sdkLibraryXml{}
+
+	module.AddProperties(&module.properties)
+
+	android.InitApexModule(module)
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+
+	return module
+}
+
+// from android.PrebuiltEtcModule
+func (module *sdkLibraryXml) SubDir() string {
+	return "permissions"
+}
+
+// from android.PrebuiltEtcModule
+func (module *sdkLibraryXml) OutputFile() android.OutputPath {
+	return module.outputFilePath
+}
+
+// from android.ApexModule
+func (module *sdkLibraryXml) AvailableFor(what string) bool {
+	return true
+}
+
+func (module *sdkLibraryXml) DepsMutator(ctx android.BottomUpMutatorContext) {
+	// do nothing
+}
+
+// File path to the runtime implementation library
+func (module *sdkLibraryXml) implPath() string {
+	implName := proptools.String(module.properties.Lib_name)
+	if apexName := module.ApexName(); apexName != "" {
+		// TODO(b/146468504): ApexName() is only a soong module name, not apex name.
+		// In most cases, this works fine. But when apex_name is set or override_apex is used
+		// this can be wrong.
+		return fmt.Sprintf("/apex/%s/javalib/%s.jar", apexName, implName)
+	}
+	partition := "system"
+	if module.SocSpecific() {
+		partition = "vendor"
+	} else if module.DeviceSpecific() {
+		partition = "odm"
+	} else if module.ProductSpecific() {
+		partition = "product"
+	} else if module.SystemExtSpecific() {
+		partition = "system_ext"
+	}
+	return "/" + partition + "/framework/" + implName + ".jar"
+}
+
+func (module *sdkLibraryXml) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	libName := proptools.String(module.properties.Lib_name)
+	xmlContent := fmt.Sprintf(permissionsTemplate, libName, module.implPath())
+
+	module.outputFilePath = android.PathForModuleOut(ctx, libName+".xml").OutputPath
+	rule := android.NewRuleBuilder()
+	rule.Command().
+		Text("/bin/bash -c \"echo -e '" + xmlContent + "'\" > ").
+		Output(module.outputFilePath)
+
+	rule.Build(pctx, ctx, "java_sdk_xml", "Permission XML")
+
+	module.installDirPath = android.PathForModuleInstall(ctx, "etc", module.SubDir())
+}
+
+func (module *sdkLibraryXml) AndroidMkEntries() []android.AndroidMkEntries {
+	if !module.IsForPlatform() {
+		return []android.AndroidMkEntries{android.AndroidMkEntries{
+			Disabled: true,
+		}}
+	}
+
+	return []android.AndroidMkEntries{android.AndroidMkEntries{
+		Class:      "ETC",
+		OutputFile: android.OptionalPathForPath(module.outputFilePath),
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(entries *android.AndroidMkEntries) {
+				entries.SetString("LOCAL_MODULE_TAGS", "optional")
+				entries.SetString("LOCAL_MODULE_PATH", module.installDirPath.ToMakePath().String())
+				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", module.outputFilePath.Base())
+			},
+		},
+	}}
+}
diff --git a/java/system_modules.go b/java/system_modules.go
index 731503f..47de6e3 100644
--- a/java/system_modules.go
+++ b/java/system_modules.go
@@ -117,6 +117,15 @@
 	return module
 }
 
+type SystemModulesProvider interface {
+	HeaderJars() android.Paths
+	OutputDirAndDeps() (android.Path, android.Paths)
+}
+
+var _ SystemModulesProvider = (*SystemModules)(nil)
+
+var _ SystemModulesProvider = (*systemModulesImport)(nil)
+
 type SystemModules struct {
 	android.ModuleBase
 	android.DefaultableModuleBase
@@ -136,6 +145,17 @@
 	Libs []string
 }
 
+func (system *SystemModules) HeaderJars() android.Paths {
+	return system.headerJars
+}
+
+func (system *SystemModules) OutputDirAndDeps() (android.Path, android.Paths) {
+	if system.outputDir == nil || len(system.outputDeps) == 0 {
+		panic("Missing directory for system module dependency")
+	}
+	return system.outputDir, system.outputDeps
+}
+
 func (system *SystemModules) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	var jars android.Paths
 
diff --git a/python/tests/py-cmd_test.py b/python/tests/py-cmd_test.py
new file mode 100644
index 0000000..acda2d7
--- /dev/null
+++ b/python/tests/py-cmd_test.py
@@ -0,0 +1,78 @@
+# 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.
+
+import os
+import site
+import sys
+
+# This file checks the visible python state against expected values when run
+# using a prebuilt python.
+
+failed = False
+def assert_equal(what, a, b):
+    global failed
+    if a != b:
+        print("Expected %s('%s') == '%s'" % (what, a, b))
+        failed = True
+
+assert_equal("__name__", __name__, "__main__")
+assert_equal("os.path.basename(__file__)", os.path.basename(__file__), "py-cmd_test.py")
+
+if os.getenv('ARGTEST', False):
+    assert_equal("len(sys.argv)", len(sys.argv), 3)
+    assert_equal("sys.argv[1]", sys.argv[1], "arg1")
+    assert_equal("sys.argv[2]", sys.argv[2], "arg2")
+elif os.getenv('ARGTEST2', False):
+    assert_equal("len(sys.argv)", len(sys.argv), 3)
+    assert_equal("sys.argv[1]", sys.argv[1], "--arg1")
+    assert_equal("sys.argv[2]", sys.argv[2], "arg2")
+else:
+    assert_equal("len(sys.argv)", len(sys.argv), 1)
+
+if os.getenv('ARGTEST_ONLY', False):
+    if failed:
+        sys.exit(1)
+    sys.exit(0)
+
+assert_equal("__package__", __package__, None)
+assert_equal("sys.argv[0]", sys.argv[0], 'py-cmd_test.py')
+if sys.version_info[0] == 2:
+    assert_equal("basename(sys.executable)", os.path.basename(sys.executable), 'py2-cmd')
+else:
+    assert_equal("basename(sys.executable)", os.path.basename(sys.executable), 'py3-cmd')
+assert_equal("sys.exec_prefix", sys.exec_prefix, sys.executable)
+assert_equal("sys.prefix", sys.prefix, sys.executable)
+assert_equal("site.ENABLE_USER_SITE", site.ENABLE_USER_SITE, None)
+
+if sys.version_info[0] == 2:
+    assert_equal("len(sys.path)", len(sys.path), 4)
+    assert_equal("sys.path[0]", sys.path[0], os.path.dirname(__file__))
+    assert_equal("sys.path[1]", sys.path[1], "/extra")
+    assert_equal("sys.path[2]", sys.path[2], os.path.join(sys.executable, "internal"))
+    assert_equal("sys.path[3]", sys.path[3], os.path.join(sys.executable, "internal", "stdlib"))
+else:
+    assert_equal("len(sys.path)", len(sys.path), 8)
+    assert_equal("sys.path[0]", sys.path[0], os.path.abspath(os.path.dirname(__file__)))
+    assert_equal("sys.path[1]", sys.path[1], "/extra")
+    assert_equal("sys.path[2]", sys.path[2], os.path.join(sys.executable, 'lib', 'python' + str(sys.version_info[0]) + str(sys.version_info[1]) + '.zip'))
+    assert_equal("sys.path[3]", sys.path[3], os.path.join(sys.executable, 'lib', 'python' + str(sys.version_info[0]) + '.' + str(sys.version_info[1]), '..'))
+    assert_equal("sys.path[4]", sys.path[4], os.path.join(sys.executable, 'lib', 'python' + str(sys.version_info[0]) + '.' + str(sys.version_info[1])))
+    assert_equal("sys.path[5]", sys.path[5], os.path.join(sys.executable, 'lib', 'python' + str(sys.version_info[0]) + '.' + str(sys.version_info[1]), 'lib-dynload'))
+    assert_equal("sys.path[6]", sys.path[6], os.path.join(sys.executable, "internal"))
+    assert_equal("sys.path[7]", sys.path[7], os.path.join(sys.executable, "internal", "stdlib"))
+
+if failed:
+    sys.exit(1)
+
+import testpkg.pycmd_test
diff --git a/python/tests/runtest.sh b/python/tests/runtest.sh
index 21187ed..35941dc 100755
--- a/python/tests/runtest.sh
+++ b/python/tests/runtest.sh
@@ -23,8 +23,11 @@
   exit 1
 fi
 
-if [[ ( ! -f $ANDROID_HOST_OUT/nativetest64/par_test/par_test ) || ( ! -f $ANDROID_HOST_OUT/nativetest64/par_test3/par_test3 ) ]]; then
-  echo "Run 'm par_test par_test3' first"
+if [[ ( ! -f $ANDROID_HOST_OUT/nativetest64/par_test/par_test ) ||
+      ( ! -f $ANDROID_HOST_OUT/nativetest64/par_test3/par_test3 ) ||
+      ( ! -f $ANDROID_HOST_OUT/bin/py2-cmd ) ||
+      ( ! -f $ANDROID_HOST_OUT/bin/py3-cmd )]]; then
+  echo "Run 'm par_test par_test3 py2-cmd py3-cmd' first"
   exit 1
 fi
 
@@ -44,4 +47,15 @@
 
 ARGTEST=true $ANDROID_HOST_OUT/nativetest64/par_test3/par_test3 --arg1 arg2
 
+cd $(dirname ${BASH_SOURCE[0]})
+
+PYTHONPATH=/extra $ANDROID_HOST_OUT/bin/py2-cmd py-cmd_test.py
+PYTHONPATH=/extra $ANDROID_HOST_OUT/bin/py3-cmd py-cmd_test.py
+
+ARGTEST=true PYTHONPATH=/extra $ANDROID_HOST_OUT/bin/py2-cmd py-cmd_test.py arg1 arg2
+ARGTEST2=true PYTHONPATH=/extra $ANDROID_HOST_OUT/bin/py2-cmd py-cmd_test.py --arg1 arg2
+
+ARGTEST=true PYTHONPATH=/extra $ANDROID_HOST_OUT/bin/py3-cmd py-cmd_test.py arg1 arg2
+ARGTEST2=true PYTHONPATH=/extra $ANDROID_HOST_OUT/bin/py3-cmd py-cmd_test.py --arg1 arg2
+
 echo "Passed!"
diff --git a/python/tests/testpkg/__init__.py b/python/tests/testpkg/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/python/tests/testpkg/__init__.py
diff --git a/python/tests/testpkg/pycmd_test.py b/python/tests/testpkg/pycmd_test.py
new file mode 100644
index 0000000..6b8a263
--- /dev/null
+++ b/python/tests/testpkg/pycmd_test.py
@@ -0,0 +1,33 @@
+# Copyright 2018 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.
+
+import os
+import sys
+
+# This file checks the visible python state against expected values when run
+# via the py*-cmd prebuilts
+
+failed = False
+def assert_equal(what, a, b):
+    global failed
+    if a != b:
+        print("Expected %s('%s') == '%s'" % (what, a, b))
+        failed = True
+
+assert_equal("__name__", __name__, "testpkg.pycmd_test")
+assert_equal("basename(__file__)", os.path.basename(__file__), "pycmd_test.py")
+assert_equal("__package__", __package__, "testpkg")
+
+if failed:
+    sys.exit(1)
diff --git a/scripts/Android.bp b/scripts/Android.bp
index 4aaff9a..e848b50 100644
--- a/scripts/Android.bp
+++ b/scripts/Android.bp
@@ -3,7 +3,6 @@
     main: "manifest_fixer.py",
     srcs: [
         "manifest_fixer.py",
-        "manifest.py",
     ],
     version: {
         py2: {
@@ -13,6 +12,9 @@
             enabled: false,
         },
     },
+    libs: [
+        "manifest_utils",
+    ],
 }
 
 python_test_host {
@@ -21,6 +23,24 @@
     srcs: [
         "manifest_fixer_test.py",
         "manifest_fixer.py",
+    ],
+    version: {
+        py2: {
+            enabled: true,
+        },
+        py3: {
+            enabled: false,
+        },
+    },
+    libs: [
+        "manifest_utils",
+    ],
+    test_suites: ["general-tests"],
+}
+
+python_library_host {
+    name: "manifest_utils",
+    srcs: [
         "manifest.py",
     ],
     version: {
@@ -31,7 +51,6 @@
             enabled: false,
         },
     },
-    test_suites: ["general-tests"],
 }
 
 python_binary_host {
@@ -39,7 +58,6 @@
     main: "manifest_check.py",
     srcs: [
         "manifest_check.py",
-        "manifest.py",
     ],
     version: {
         py2: {
@@ -49,6 +67,9 @@
             enabled: false,
         },
     },
+    libs: [
+        "manifest_utils",
+    ],
 }
 
 python_test_host {
@@ -57,7 +78,6 @@
     srcs: [
         "manifest_check_test.py",
         "manifest_check.py",
-        "manifest.py",
     ],
     version: {
         py2: {
@@ -67,6 +87,9 @@
             enabled: false,
         },
     },
+    libs: [
+        "manifest_utils",
+    ],
     test_suites: ["general-tests"],
 }
 
@@ -91,7 +114,6 @@
     main: "test_config_fixer.py",
     srcs: [
         "test_config_fixer.py",
-        "manifest.py",
     ],
     version: {
         py2: {
@@ -101,6 +123,9 @@
             enabled: false,
         },
     },
+    libs: [
+        "manifest_utils",
+    ],
 }
 
 python_test_host {
@@ -109,7 +134,6 @@
     srcs: [
         "test_config_fixer_test.py",
         "test_config_fixer.py",
-        "manifest.py",
     ],
     version: {
         py2: {
@@ -119,5 +143,8 @@
             enabled: false,
         },
     },
+    libs: [
+        "manifest_utils",
+    ],
     test_suites: ["general-tests"],
-}
\ No newline at end of file
+}
diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go
index d376e59..934bdae 100644
--- a/sdk/sdk_test.go
+++ b/sdk/sdk_test.go
@@ -111,8 +111,14 @@
 			sdk_version: "none",
 		}
 
+		java_defaults {
+			name: "java-defaults",
+			visibility: ["//other/bar"], 
+		}
+
 		java_library {
 			name: "mypublicjavalib",
+			defaults: ["java-defaults"],
       visibility: ["//visibility:public"],
 			srcs: ["Test.java"],
 			system_modules: "none",
diff --git a/ui/build/ninja.go b/ui/build/ninja.go
index 22ec1f1..0749fe3 100644
--- a/ui/build/ninja.go
+++ b/ui/build/ninja.go
@@ -142,6 +142,7 @@
 			"CCACHE_SLOPPINESS",
 			"CCACHE_BASEDIR",
 			"CCACHE_CPP2",
+			"CCACHE_DIR",
 		}, config.BuildBrokenNinjaUsesEnvVars()...)...)
 	}