Merge "Clients of PackagingBase can customize dependency tag to use"
diff --git a/Android.bp b/Android.bp
index e7a5de2..866ed25 100644
--- a/Android.bp
+++ b/Android.bp
@@ -75,6 +75,10 @@
     product_available: true,
     recovery_available: true,
     native_bridge_supported: true,
+    apex_available: [
+        "//apex_available:platform",
+        "//apex_available:anyapex",
+    ],
 
     arch: {
         arm: {
diff --git a/android/androidmk.go b/android/androidmk.go
index 7afc9de..063830b 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -222,13 +222,32 @@
 	amod := mod.(Module).base()
 	name := amod.BaseModuleName()
 
+	// Collate the set of associated tag/paths available for copying to the dist.
+	// Start with an empty (nil) set.
 	var availableTaggedDists TaggedDistFiles
 
+	// Then merge in any that are provided explicitly by the module.
 	if a.DistFiles != nil {
-		availableTaggedDists = a.DistFiles
-	} else if a.OutputFile.Valid() {
-		availableTaggedDists = MakeDefaultDistFiles(a.OutputFile.Path())
-	} else {
+		// Merge the DistFiles into the set.
+		availableTaggedDists = availableTaggedDists.merge(a.DistFiles)
+	}
+
+	// If no paths have been provided for the DefaultDistTag and the output file is
+	// valid then add that as the default dist path.
+	if _, ok := availableTaggedDists[DefaultDistTag]; !ok && a.OutputFile.Valid() {
+		availableTaggedDists = availableTaggedDists.addPathsForTag(DefaultDistTag, a.OutputFile.Path())
+	}
+
+	// If the distFiles created by GenerateTaggedDistFiles contains paths for the
+	// DefaultDistTag then that takes priority so delete any existing paths.
+	if _, ok := amod.distFiles[DefaultDistTag]; ok {
+		delete(availableTaggedDists, DefaultDistTag)
+	}
+
+	// Finally, merge the distFiles created by GenerateTaggedDistFiles.
+	availableTaggedDists = availableTaggedDists.merge(amod.distFiles)
+
+	if len(availableTaggedDists) == 0 {
 		// Nothing dist-able for this module.
 		return nil
 	}
@@ -245,7 +264,7 @@
 		var tag string
 		if dist.Tag == nil {
 			// If the dist struct does not specify a tag, use the default output files tag.
-			tag = ""
+			tag = DefaultDistTag
 		} else {
 			tag = *dist.Tag
 		}
@@ -259,10 +278,10 @@
 		}
 
 		if len(tagPaths) > 1 && (dist.Dest != nil || dist.Suffix != nil) {
-			errorMessage := "Cannot apply dest/suffix for more than one dist " +
-				"file for %s goals in module %s. The list of dist files, " +
+			errorMessage := "%s: Cannot apply dest/suffix for more than one dist " +
+				"file for %q goals tag %q in module %s. The list of dist files, " +
 				"which should have a single element, is:\n%s"
-			panic(fmt.Errorf(errorMessage, goals, name, tagPaths))
+			panic(fmt.Errorf(errorMessage, mod, goals, tag, name, tagPaths))
 		}
 
 		copiesForGoals := distContributions.getCopiesForGoals(goals)
diff --git a/android/androidmk_test.go b/android/androidmk_test.go
index 5d05d91..347b92e 100644
--- a/android/androidmk_test.go
+++ b/android/androidmk_test.go
@@ -35,6 +35,10 @@
 	data       AndroidMkData
 	distFiles  TaggedDistFiles
 	outputFile OptionalPath
+
+	// The paths that will be used as the default dist paths if no tag is
+	// specified.
+	defaultDistPaths Paths
 }
 
 const (
@@ -48,7 +52,14 @@
 	// If the dist_output_file: true then create an output file that is stored in
 	// the OutputFile property of the AndroidMkEntry.
 	if proptools.BoolDefault(m.properties.Dist_output_file, true) {
-		m.outputFile = OptionalPathForPath(PathForTesting("dist-output-file.out"))
+		path := PathForTesting("dist-output-file.out")
+		m.outputFile = OptionalPathForPath(path)
+
+		// Previous code would prioritize the DistFiles property over the OutputFile
+		// property in AndroidMkEntry when determining the default dist paths.
+		// Setting this first allows it to be overridden based on the
+		// default_dist_files setting replicating that previous behavior.
+		m.defaultDistPaths = Paths{path}
 	}
 
 	// Based on the setting of the default_dist_files property possibly create a
@@ -60,9 +71,22 @@
 		// Do nothing
 
 	case defaultDistFiles_Default:
-		m.distFiles = MakeDefaultDistFiles(PathForTesting("default-dist.out"))
+		path := PathForTesting("default-dist.out")
+		m.defaultDistPaths = Paths{path}
+		m.distFiles = MakeDefaultDistFiles(path)
 
 	case defaultDistFiles_Tagged:
+		// Module types that set AndroidMkEntry.DistFiles to the result of calling
+		// GenerateTaggedDistFiles(ctx) relied on no tag being treated as "" which
+		// meant that the default dist paths would be whatever was returned by
+		// OutputFiles(""). In order to preserve that behavior when treating no tag
+		// as being equal to DefaultDistTag this ensures that
+		// OutputFiles(DefaultDistTag) will return the same as OutputFiles("").
+		m.defaultDistPaths = PathsForTesting("one.out")
+
+		// This must be called after setting defaultDistPaths/outputFile as
+		// GenerateTaggedDistFiles calls into OutputFiles(tag) which may use those
+		// fields.
 		m.distFiles = m.GenerateTaggedDistFiles(ctx)
 	}
 }
@@ -77,6 +101,12 @@
 
 func (m *customModule) OutputFiles(tag string) (Paths, error) {
 	switch tag {
+	case DefaultDistTag:
+		if m.defaultDistPaths != nil {
+			return m.defaultDistPaths, nil
+		} else {
+			return nil, fmt.Errorf("default dist tag is not available")
+		}
 	case "":
 		return PathsForTesting("one.out"), nil
 	case ".multiple":
@@ -571,8 +601,6 @@
 					{
 						targets: ["my_goal"],
 					},
-					// The following is silently ignored because the dist files do not
-					// contain the tagged files.
 					{
 						targets: ["my_goal"],
 						tag: ".multiple",
@@ -587,6 +615,13 @@
 					distCopyForTest("default-dist.out", "default-dist.out"),
 				},
 			},
+			{
+				goals: "my_goal",
+				copies: []distCopy{
+					distCopyForTest("two.out", "two.out"),
+					distCopyForTest("three/four.out", "four.out"),
+				},
+			},
 		},
 	})
 
@@ -601,15 +636,23 @@
 					{
 						targets: ["my_goal"],
 					},
-					// The following is silently ignored because the dist files do not
-					// contain the tagged files.
 					{
 						targets: ["my_goal"],
 						tag: ".multiple",
 					},
 				],
 			}
-`, nil)
+`, &distContributions{
+		copiesForGoals: []*copiesForGoals{
+			{
+				goals: "my_goal",
+				copies: []distCopy{
+					distCopyForTest("two.out", "two.out"),
+					distCopyForTest("three/four.out", "four.out"),
+				},
+			},
+		},
+	})
 
 	testHelper(t, "tagged-dist-files-default-output", `
 			custom {
@@ -653,8 +696,6 @@
 					{
 						targets: ["my_goal"],
 					},
-					// The following is silently ignored because the dist files do not
-					// contain the tagged files.
 					{
 						targets: ["my_goal"],
 						tag: ".multiple",
@@ -669,6 +710,13 @@
 					distCopyForTest("default-dist.out", "default-dist.out"),
 				},
 			},
+			{
+				goals: "my_goal",
+				copies: []distCopy{
+					distCopyForTest("two.out", "two.out"),
+					distCopyForTest("three/four.out", "four.out"),
+				},
+			},
 		},
 	})
 
@@ -681,8 +729,6 @@
 					{
 						targets: ["my_goal"],
 					},
-					// The following is silently ignored because the dist files do not
-					// contain the tagged files.
 					{
 						targets: ["my_goal"],
 						tag: ".multiple",
@@ -697,6 +743,13 @@
 					distCopyForTest("dist-output-file.out", "dist-output-file.out"),
 				},
 			},
+			{
+				goals: "my_goal",
+				copies: []distCopy{
+					distCopyForTest("two.out", "two.out"),
+					distCopyForTest("three/four.out", "four.out"),
+				},
+			},
 		},
 	})
 }
diff --git a/android/module.go b/android/module.go
index 0716b5d..d680baa 100644
--- a/android/module.go
+++ b/android/module.go
@@ -503,8 +503,12 @@
 	// A suffix to add to the artifact file name (before any extension).
 	Suffix *string `android:"arch_variant"`
 
-	// A string tag to select the OutputFiles associated with the tag. Defaults to the
-	// the empty "" string.
+	// A string tag to select the OutputFiles associated with the tag.
+	//
+	// If no tag is specified then it will select the default dist paths provided
+	// by the module type. If a tag of "" is specified then it will return the
+	// default output files provided by the modules, i.e. the result of calling
+	// OutputFiles("").
 	Tag *string `android:"arch_variant"`
 }
 
@@ -733,9 +737,45 @@
 	Dists []Dist `android:"arch_variant"`
 }
 
+// The key to use in TaggedDistFiles when a Dist structure does not specify a
+// tag property. This intentionally does not use "" as the default because that
+// would mean that an empty tag would have a different meaning when used in a dist
+// structure that when used to reference a specific set of output paths using the
+// :module{tag} syntax, which passes tag to the OutputFiles(tag) method.
+const DefaultDistTag = "<default-dist-tag>"
+
 // A map of OutputFile tag keys to Paths, for disting purposes.
 type TaggedDistFiles map[string]Paths
 
+// addPathsForTag adds a mapping from the tag to the paths. If the map is nil
+// then it will create a map, update it and then return it. If a mapping already
+// exists for the tag then the paths are appended to the end of the current list
+// of paths, ignoring any duplicates.
+func (t TaggedDistFiles) addPathsForTag(tag string, paths ...Path) TaggedDistFiles {
+	if t == nil {
+		t = make(TaggedDistFiles)
+	}
+
+	for _, distFile := range paths {
+		if distFile != nil && !t[tag].containsPath(distFile) {
+			t[tag] = append(t[tag], distFile)
+		}
+	}
+
+	return t
+}
+
+// merge merges the entries from the other TaggedDistFiles object into this one.
+// If the TaggedDistFiles is nil then it will create a new instance, merge the
+// other into it, and then return it.
+func (t TaggedDistFiles) merge(other TaggedDistFiles) TaggedDistFiles {
+	for tag, paths := range other {
+		t = t.addPathsForTag(tag, paths...)
+	}
+
+	return t
+}
+
 func MakeDefaultDistFiles(paths ...Path) TaggedDistFiles {
 	for _, path := range paths {
 		if path == nil {
@@ -744,7 +784,7 @@
 	}
 
 	// The default OutputFile tag is the empty "" string.
-	return TaggedDistFiles{"": paths}
+	return TaggedDistFiles{DefaultDistTag: paths}
 }
 
 type hostAndDeviceProperties struct {
@@ -970,6 +1010,9 @@
 	noticeFiles        Paths
 	phonies            map[string]Paths
 
+	// The files to copy to the dist as explicitly specified in the .bp file.
+	distFiles TaggedDistFiles
+
 	// Used by buildTargetSingleton to create checkbuild and per-directory build targets
 	// Only set on the final variant of each module
 	installTarget    WritablePath
@@ -1071,23 +1114,30 @@
 }
 
 func (m *ModuleBase) GenerateTaggedDistFiles(ctx BaseModuleContext) TaggedDistFiles {
-	distFiles := make(TaggedDistFiles)
+	var distFiles TaggedDistFiles
 	for _, dist := range m.Dists() {
-		var tag string
-		var distFilesForTag Paths
-		if dist.Tag == nil {
-			tag = ""
-		} else {
-			tag = *dist.Tag
-		}
-		distFilesForTag, err := m.base().module.(OutputFileProducer).OutputFiles(tag)
-		if err != nil {
-			ctx.PropertyErrorf("dist.tag", "%s", err.Error())
-		}
-		for _, distFile := range distFilesForTag {
-			if distFile != nil && !distFiles[tag].containsPath(distFile) {
-				distFiles[tag] = append(distFiles[tag], distFile)
+		// If no tag is specified then it means to use the default dist paths so use
+		// the special tag name which represents that.
+		tag := proptools.StringDefault(dist.Tag, DefaultDistTag)
+
+		if outputFileProducer, ok := m.module.(OutputFileProducer); ok {
+			// Call the OutputFiles(tag) method to get the paths associated with the tag.
+			distFilesForTag, err := outputFileProducer.OutputFiles(tag)
+
+			// If the tag was not supported and is not DefaultDistTag then it is an error.
+			// Failing to find paths for DefaultDistTag is not an error. It just means
+			// that the module type requires the legacy behavior.
+			if err != nil && tag != DefaultDistTag {
+				ctx.PropertyErrorf("dist.tag", "%s", err.Error())
 			}
+
+			distFiles = distFiles.addPathsForTag(tag, distFilesForTag...)
+		} else if tag != DefaultDistTag {
+			// If the tag was specified then it is an error if the module does not
+			// implement OutputFileProducer because there is no other way of accessing
+			// the paths for the specified tag.
+			ctx.PropertyErrorf("dist.tag",
+				"tag %s not supported because the module does not implement OutputFileProducer", tag)
 		}
 	}
 
@@ -1618,6 +1668,15 @@
 			return
 		}
 
+		// Create the set of tagged dist files after calling GenerateAndroidBuildActions
+		// as GenerateTaggedDistFiles() calls OutputFiles(tag) and so relies on the
+		// output paths being set which must be done before or during
+		// GenerateAndroidBuildActions.
+		m.distFiles = m.GenerateTaggedDistFiles(ctx)
+		if ctx.Failed() {
+			return
+		}
+
 		m.installFiles = append(m.installFiles, ctx.installFiles...)
 		m.checkbuildFiles = append(m.checkbuildFiles, ctx.checkbuildFiles...)
 		m.packagingSpecs = append(m.packagingSpecs, ctx.packagingSpecs...)
diff --git a/android/paths.go b/android/paths.go
index a62c9e3..b7117a3 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -665,7 +665,7 @@
 	return Paths(ret)
 }
 
-// WritablePaths is a slice of WritablePaths, used for multiple outputs.
+// WritablePaths is a slice of WritablePath, used for multiple outputs.
 type WritablePaths []WritablePath
 
 // Strings returns the string forms of the writable paths.
diff --git a/apex/Android.bp b/apex/Android.bp
index 1a5f683..9e8c30d 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -9,6 +9,7 @@
         "soong-cc",
         "soong-java",
         "soong-python",
+        "soong-rust",
         "soong-sh",
     ],
     srcs: [
diff --git a/apex/androidmk.go b/apex/androidmk.go
index fe89b73..da38c2a 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -334,7 +334,6 @@
 
 func (a *apexBundle) androidMkForType() android.AndroidMkData {
 	return android.AndroidMkData{
-		DistFiles: a.distFiles,
 		Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
 			moduleNames := []string{}
 			apexType := a.properties.ApexType
diff --git a/apex/apex.go b/apex/apex.go
index d0e0156..f127757 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -32,6 +32,7 @@
 	prebuilt_etc "android/soong/etc"
 	"android/soong/java"
 	"android/soong/python"
+	"android/soong/rust"
 	"android/soong/sh"
 )
 
@@ -179,6 +180,9 @@
 	// List of JNI libraries that are embedded inside this APEX.
 	Jni_libs []string
 
+	// List of rust dyn libraries
+	Rust_dyn_libs []string
+
 	// List of native executables that are embedded inside this APEX.
 	Binaries []string
 
@@ -349,8 +353,6 @@
 	lintReports android.Paths
 
 	prebuiltFileToDelete string
-
-	distFiles android.TaggedDistFiles
 }
 
 // apexFileClass represents a type of file that can be included in APEX.
@@ -513,13 +515,15 @@
 func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext, nativeModules ApexNativeDependencies, target android.Target, imageVariation string) {
 	binVariations := target.Variations()
 	libVariations := append(target.Variations(), blueprint.Variation{Mutator: "link", Variation: "shared"})
+	rustLibVariations := append(target.Variations(), blueprint.Variation{Mutator: "rust_libraries", Variation: "dylib"})
 
 	if ctx.Device() {
 		binVariations = append(binVariations, blueprint.Variation{Mutator: "image", Variation: imageVariation})
 		libVariations = append(libVariations,
 			blueprint.Variation{Mutator: "image", Variation: imageVariation},
-			blueprint.Variation{Mutator: "version", Variation: ""}, // "" is the non-stub variant
-		)
+			blueprint.Variation{Mutator: "version", Variation: ""}) // "" is the non-stub variant
+		rustLibVariations = append(rustLibVariations,
+			blueprint.Variation{Mutator: "image", Variation: imageVariation})
 	}
 
 	// Use *FarVariation* to be able to depend on modules having conflicting variations with
@@ -529,6 +533,7 @@
 	ctx.AddFarVariationDependencies(binVariations, testTag, nativeModules.Tests...)
 	ctx.AddFarVariationDependencies(libVariations, jniLibTag, nativeModules.Jni_libs...)
 	ctx.AddFarVariationDependencies(libVariations, sharedLibTag, nativeModules.Native_shared_libs...)
+	ctx.AddFarVariationDependencies(rustLibVariations, sharedLibTag, nativeModules.Rust_dyn_libs...)
 }
 
 func (a *apexBundle) combineProperties(ctx android.BottomUpMutatorContext) {
@@ -1096,7 +1101,8 @@
 // Implements android.OutputFileProducer
 func (a *apexBundle) OutputFiles(tag string) (android.Paths, error) {
 	switch tag {
-	case "":
+	case "", android.DefaultDistTag:
+		// This is the default dist path.
 		return android.Paths{a.outputFile}, nil
 	default:
 		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
@@ -1264,6 +1270,35 @@
 	return af
 }
 
+func apexFileForRustExecutable(ctx android.BaseModuleContext, rustm *rust.Module) apexFile {
+	dirInApex := "bin"
+	if rustm.Target().NativeBridge == android.NativeBridgeEnabled {
+		dirInApex = filepath.Join(dirInApex, rustm.Target().NativeBridgeRelativePath)
+	}
+	fileToCopy := rustm.OutputFile().Path()
+	androidMkModuleName := rustm.BaseModuleName() + rustm.Properties.SubName
+	af := newApexFile(ctx, fileToCopy, androidMkModuleName, dirInApex, nativeExecutable, rustm)
+	return af
+}
+
+func apexFileForRustLibrary(ctx android.BaseModuleContext, rustm *rust.Module) apexFile {
+	// Decide the APEX-local directory by the multilib of the library
+	// In the future, we may query this to the module.
+	var dirInApex string
+	switch rustm.Arch().ArchType.Multilib {
+	case "lib32":
+		dirInApex = "lib"
+	case "lib64":
+		dirInApex = "lib64"
+	}
+	if rustm.Target().NativeBridge == android.NativeBridgeEnabled {
+		dirInApex = filepath.Join(dirInApex, rustm.Target().NativeBridgeRelativePath)
+	}
+	fileToCopy := rustm.OutputFile().Path()
+	androidMkModuleName := rustm.BaseModuleName() + rustm.Properties.SubName
+	return newApexFile(ctx, fileToCopy, androidMkModuleName, dirInApex, nativeSharedLib, rustm)
+}
+
 func apexFileForPyBinary(ctx android.BaseModuleContext, py *python.Module) apexFile {
 	dirInApex := "bin"
 	fileToCopy := py.HostToolPath().Path()
@@ -1501,8 +1536,11 @@
 					filesInfo = append(filesInfo, apexFileForPyBinary(ctx, py))
 				} else if gb, ok := child.(bootstrap.GoBinaryTool); ok && a.Host() {
 					filesInfo = append(filesInfo, apexFileForGoBinary(ctx, depName, gb))
+				} else if rust, ok := child.(*rust.Module); ok {
+					filesInfo = append(filesInfo, apexFileForRustExecutable(ctx, rust))
+					return true // track transitive dependencies
 				} else {
-					ctx.PropertyErrorf("binaries", "%q is neither cc_binary, (embedded) py_binary, (host) blueprint_go_binary, (host) bootstrap_go_binary, nor sh_binary", depName)
+					ctx.PropertyErrorf("binaries", "%q is neither cc_binary, rust_binary, (embedded) py_binary, (host) blueprint_go_binary, (host) bootstrap_go_binary, nor sh_binary", depName)
 				}
 			case javaLibTag:
 				switch child.(type) {
@@ -1663,6 +1701,13 @@
 					if prebuilt, ok := child.(prebuilt_etc.PrebuiltEtcModule); ok {
 						filesInfo = append(filesInfo, apexFileForPrebuiltEtc(ctx, prebuilt, depName))
 					}
+				} else if rust.IsDylibDepTag(depTag) {
+					if rustm, ok := child.(*rust.Module); ok && rustm.IsInstallableToApex() {
+						af := apexFileForRustLibrary(ctx, rustm)
+						af.transitiveDep = true
+						filesInfo = append(filesInfo, af)
+						return true // track transitive dependencies
+					}
 				} else if _, ok := depTag.(android.CopyDirectlyInAnyApexTag); ok {
 					// nothing
 				} else if am.CanHaveApexVariants() && am.IsInstallableToApex() {
@@ -1798,7 +1843,6 @@
 	}
 	a.buildApexDependencyInfo(ctx)
 	a.buildLintReports(ctx)
-	a.distFiles = a.GenerateTaggedDistFiles(ctx)
 
 	// Append meta-files to the filesInfo list so that they are reflected in Android.mk as well.
 	if a.installable() {
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 33e5077..a94e3b4 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -32,6 +32,7 @@
 	"android/soong/dexpreopt"
 	prebuilt_etc "android/soong/etc"
 	"android/soong/java"
+	"android/soong/rust"
 	"android/soong/sh"
 )
 
@@ -136,6 +137,8 @@
 
 	bp = bp + cc.GatherRequiredDepsForTest(android.Android)
 
+	bp = bp + rust.GatherRequiredDepsForTest()
+
 	bp = bp + java.GatherRequiredDepsForTest()
 
 	fs := map[string][]byte{
@@ -185,6 +188,7 @@
 		"bar/baz":                                    nil,
 		"testdata/baz":                               nil,
 		"AppSet.apks":                                nil,
+		"foo.rs":                                     nil,
 	}
 
 	cc.GatherRequiredFilesForTest(fs)
@@ -241,6 +245,7 @@
 	ctx.PostDepsMutators(android.RegisterVisibilityRuleEnforcer)
 
 	cc.RegisterRequiredBuildComponentsForTest(ctx)
+	rust.RegisterRequiredBuildComponentsForTest(ctx)
 
 	ctx.RegisterModuleType("cc_test", cc.TestFactory)
 	ctx.RegisterModuleType("vndk_prebuilt_shared", cc.VndkPrebuiltSharedFactory)
@@ -349,10 +354,12 @@
 			manifest: ":myapex.manifest",
 			androidManifest: ":myapex.androidmanifest",
 			key: "myapex.key",
+			binaries: ["foo.rust"],
 			native_shared_libs: ["mylib"],
+			rust_dyn_libs: ["libfoo.dylib.rust"],
 			multilib: {
 				both: {
-					binaries: ["foo",],
+					binaries: ["foo"],
 				}
 			},
 			java_libs: [
@@ -415,6 +422,28 @@
 			apex_available: [ "myapex", "com.android.gki.*" ],
 		}
 
+		rust_binary {
+		        name: "foo.rust",
+			srcs: ["foo.rs"],
+			rlibs: ["libfoo.rlib.rust"],
+			dylibs: ["libfoo.dylib.rust"],
+			apex_available: ["myapex"],
+		}
+
+		rust_library_rlib {
+		        name: "libfoo.rlib.rust",
+			srcs: ["foo.rs"],
+			crate_name: "foo",
+			apex_available: ["myapex"],
+		}
+
+		rust_library_dylib {
+		        name: "libfoo.dylib.rust",
+			srcs: ["foo.rs"],
+			crate_name: "foo",
+			apex_available: ["myapex"],
+		}
+
 		apex {
 			name: "com.android.gki.fake",
 			binaries: ["foo"],
@@ -529,16 +558,20 @@
 	ensureListContains(t, ctx.ModuleVariantsForTests("mylib"), "android_arm64_armv8-a_shared_apex10000")
 	ensureListContains(t, ctx.ModuleVariantsForTests("myjar"), "android_common_apex10000")
 	ensureListContains(t, ctx.ModuleVariantsForTests("myjar_dex"), "android_common_apex10000")
+	ensureListContains(t, ctx.ModuleVariantsForTests("foo.rust"), "android_arm64_armv8-a_apex10000")
 
 	// Ensure that apex variant is created for the indirect dep
 	ensureListContains(t, ctx.ModuleVariantsForTests("mylib2"), "android_arm64_armv8-a_shared_apex10000")
 	ensureListContains(t, ctx.ModuleVariantsForTests("myotherjar"), "android_common_apex10000")
+	ensureListContains(t, ctx.ModuleVariantsForTests("libfoo.rlib.rust"), "android_arm64_armv8-a_rlib_dylib-std_apex10000")
+	ensureListContains(t, ctx.ModuleVariantsForTests("libfoo.dylib.rust"), "android_arm64_armv8-a_dylib_apex10000")
 
 	// Ensure that both direct and indirect deps are copied into apex
 	ensureContains(t, copyCmds, "image.apex/lib64/mylib.so")
 	ensureContains(t, copyCmds, "image.apex/lib64/mylib2.so")
 	ensureContains(t, copyCmds, "image.apex/javalib/myjar_stem.jar")
 	ensureContains(t, copyCmds, "image.apex/javalib/myjar_dex.jar")
+	ensureContains(t, copyCmds, "image.apex/lib64/libfoo.dylib.rust.dylib.so")
 	// .. but not for java libs
 	ensureNotContains(t, copyCmds, "image.apex/javalib/myotherjar.jar")
 	ensureNotContains(t, copyCmds, "image.apex/javalib/msharedjar.jar")
diff --git a/cc/testing.go b/cc/testing.go
index 198764b..95a93a0 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -166,6 +166,10 @@
 			product_available: true,
 			recovery_available: true,
 			src: "",
+			apex_available: [
+				"//apex_available:platform",
+				"//apex_available:anyapex",
+			],
 		}
 
 		toolchain_library {
diff --git a/cmd/sbox/sbox.go b/cmd/sbox/sbox.go
index 633c6b2..a4f57ea 100644
--- a/cmd/sbox/sbox.go
+++ b/cmd/sbox/sbox.go
@@ -16,6 +16,8 @@
 
 import (
 	"bytes"
+	"crypto/sha1"
+	"encoding/hex"
 	"errors"
 	"flag"
 	"fmt"
@@ -121,7 +123,22 @@
 		return fmt.Errorf("failed to create %q: %w", sandboxesRoot, err)
 	}
 
-	tempDir, err := ioutil.TempDir(sandboxesRoot, "sbox")
+	// This tool assumes that there are no two concurrent runs with the same
+	// manifestFile. It should therefore be safe to use the hash of the
+	// manifestFile as the temporary directory name. We do this because it
+	// makes the temporary directory name deterministic. There are some
+	// tools that embed the name of the temporary output in the output, and
+	// they otherwise cause non-determinism, which then poisons actions
+	// depending on this one.
+	hash := sha1.New()
+	hash.Write([]byte(manifestFile))
+	tempDir := filepath.Join(sandboxesRoot, "sbox", hex.EncodeToString(hash.Sum(nil)))
+
+	err = os.RemoveAll(tempDir)
+	if err != nil {
+		return err
+	}
+	err = os.MkdirAll(tempDir, 0777)
 	if err != nil {
 		return fmt.Errorf("failed to create temporary dir in %q: %w", sandboxesRoot, err)
 	}
diff --git a/cmd/soong_build/queryview_test.go b/cmd/soong_build/queryview_test.go
index 525802a..9471a91 100644
--- a/cmd/soong_build/queryview_test.go
+++ b/cmd/soong_build/queryview_test.go
@@ -53,6 +53,12 @@
 	android.ModuleBase
 }
 
+// OutputFiles is needed because some instances of this module use dist with a
+// tag property which requires the module implements OutputFileProducer.
+func (m *customModule) OutputFiles(tag string) (android.Paths, error) {
+	return android.PathsForTesting("path" + tag), nil
+}
+
 func (m *customModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	// nothing for now.
 }
diff --git a/genrule/genrule.go b/genrule/genrule.go
index d667e94..e5ee3fc 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -12,6 +12,10 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+// A genrule module takes a list of source files ("srcs" property), an optional
+// list of tools ("tools" property), and a command line ("cmd" property), to
+// generate output files ("out" property).
+
 package genrule
 
 import (
@@ -47,6 +51,8 @@
 var (
 	pctx = android.NewPackageContext("android/soong/genrule")
 
+	// Used by gensrcs when there is more than 1 shard to merge the outputs
+	// of each shard into a zip file.
 	gensrcsMerge = pctx.AndroidStaticRule("gensrcsMerge", blueprint.RuleParams{
 		Command:        "${soongZip} -o ${tmpZip} @${tmpZip}.rsp && ${zipSync} -d ${genDir} ${tmpZip}",
 		CommandDeps:    []string{"${soongZip}", "${zipSync}"},
@@ -115,6 +121,7 @@
 	// Properties for Bazel migration purposes.
 	bazel.Properties
 }
+
 type Module struct {
 	android.ModuleBase
 	android.DefaultableModuleBase
@@ -127,6 +134,9 @@
 
 	properties generatorProperties
 
+	// For the different tasks that genrule and gensrc generate. genrule will
+	// generate 1 task, and gensrc will generate 1 or more tasks based on the
+	// number of shards the input files are sharded into.
 	taskGenerator taskFunc
 
 	deps        android.Paths
@@ -151,11 +161,12 @@
 	in         android.Paths
 	out        android.WritablePaths
 	depFile    android.WritablePath
-	copyTo     android.WritablePaths
+	copyTo     android.WritablePaths // For gensrcs to set on gensrcsMerge rule.
 	genDir     android.WritablePath
 	extraTools android.Paths // dependencies on tools used by the generator
 
-	cmd    string
+	cmd string
+	// For gensrsc sharding.
 	shard  int
 	shards int
 }
@@ -324,6 +335,7 @@
 	var outputFiles android.WritablePaths
 	var zipArgs strings.Builder
 
+	// Generate tasks, either from genrule or gensrcs.
 	for _, task := range g.taskGenerator(ctx, String(g.properties.Cmd), srcFiles) {
 		if len(task.out) == 0 {
 			ctx.ModuleErrorf("must have at least one output file")
diff --git a/java/androidmk.go b/java/androidmk.go
index 386a97f..9ad3f9b 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -85,7 +85,6 @@
 	} else {
 		entriesList = append(entriesList, android.AndroidMkEntries{
 			Class:      "JAVA_LIBRARIES",
-			DistFiles:  library.distFiles,
 			OutputFile: android.OptionalPathForPath(library.outputFile),
 			Include:    "$(BUILD_SYSTEM)/soong_java_prebuilt.mk",
 			ExtraEntries: []android.AndroidMkExtraEntriesFunc{
@@ -524,17 +523,12 @@
 	// Note that dstubs.apiFile can be also be nil if WITHOUT_CHECKS_API is true.
 	// TODO(b/146727827): Revert when we do not need to generate stubs and API separately.
 
-	var distFiles android.TaggedDistFiles
-	if dstubs.apiFile != nil {
-		distFiles = android.MakeDefaultDistFiles(dstubs.apiFile)
-	}
 	outputFile := android.OptionalPathForPath(dstubs.stubsSrcJar)
 	if !outputFile.Valid() {
 		outputFile = android.OptionalPathForPath(dstubs.apiFile)
 	}
 	return []android.AndroidMkEntries{android.AndroidMkEntries{
 		Class:      "JAVA_LIBRARIES",
-		DistFiles:  distFiles,
 		OutputFile: outputFile,
 		Include:    "$(BUILD_SYSTEM)/soong_droiddoc_prebuilt.mk",
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
diff --git a/java/androidmk_test.go b/java/androidmk_test.go
index 075b7aa..233e9d5 100644
--- a/java/androidmk_test.go
+++ b/java/androidmk_test.go
@@ -16,7 +16,6 @@
 
 import (
 	"reflect"
-	"strings"
 	"testing"
 
 	"android/soong/android"
@@ -135,182 +134,6 @@
 	}
 }
 
-func TestDistWithTag(t *testing.T) {
-	ctx, config := testJava(t, `
-		java_library {
-			name: "foo_without_tag",
-			srcs: ["a.java"],
-			compile_dex: true,
-			dist: {
-				targets: ["hi"],
-			},
-		}
-		java_library {
-			name: "foo_with_tag",
-			srcs: ["a.java"],
-			compile_dex: true,
-			dist: {
-				targets: ["hi"],
-				tag: ".jar",
-			},
-		}
-	`)
-
-	withoutTagEntries := android.AndroidMkEntriesForTest(t, config, "", ctx.ModuleForTests("foo_without_tag", "android_common").Module())
-	withTagEntries := android.AndroidMkEntriesForTest(t, config, "", ctx.ModuleForTests("foo_with_tag", "android_common").Module())
-
-	if len(withoutTagEntries) != 2 || len(withTagEntries) != 2 {
-		t.Errorf("two mk entries per module expected, got %d and %d", len(withoutTagEntries), len(withTagEntries))
-	}
-	if len(withTagEntries[0].DistFiles[".jar"]) != 1 ||
-		!strings.Contains(withTagEntries[0].DistFiles[".jar"][0].String(), "/javac/foo_with_tag.jar") {
-		t.Errorf("expected DistFiles to contain classes.jar, got %v", withTagEntries[0].DistFiles)
-	}
-	if len(withoutTagEntries[0].DistFiles[".jar"]) > 0 {
-		t.Errorf("did not expect explicit DistFile for .jar tag, got %v", withoutTagEntries[0].DistFiles[".jar"])
-	}
-}
-
-func TestDistWithDest(t *testing.T) {
-	ctx, config := testJava(t, `
-		java_library {
-			name: "foo",
-			srcs: ["a.java"],
-			compile_dex: true,
-			dist: {
-				targets: ["my_goal"],
-				dest: "my/custom/dest/dir",
-			},
-		}
-	`)
-
-	module := ctx.ModuleForTests("foo", "android_common").Module()
-	entries := android.AndroidMkEntriesForTest(t, config, "", module)
-	if len(entries) != 2 {
-		t.Errorf("Expected 2 AndroidMk entries, got %d", len(entries))
-	}
-
-	distStrings := entries[0].GetDistForGoals(module)
-
-	if len(distStrings) != 2 {
-		t.Errorf("Expected 2 entries for dist: PHONY and dist-for-goals, but got %q", distStrings)
-	}
-
-	if distStrings[0] != ".PHONY: my_goal\n" {
-		t.Errorf("Expected .PHONY entry to declare my_goal, but got: %s", distStrings[0])
-	}
-
-	if !strings.Contains(distStrings[1], "$(call dist-for-goals,my_goal") ||
-		!strings.Contains(distStrings[1], ".intermediates/foo/android_common/dex/foo.jar:my/custom/dest/dir") {
-		t.Errorf(
-			"Expected dist-for-goals entry to contain my_goal and new dest dir, but got: %s", distStrings[1])
-	}
-}
-
-func TestDistsWithAllProperties(t *testing.T) {
-	ctx, config := testJava(t, `
-		java_library {
-			name: "foo",
-			srcs: ["a.java"],
-			compile_dex: true,
-			dist: {
-				targets: ["baz"],
-			},
-			dists: [
-				{
-					targets: ["bar"],
-					tag: ".jar",
-					dest: "bar.jar",
-					dir: "bar/dir",
-					suffix: ".qux",
-				},
-			]
-		}
-	`)
-
-	module := ctx.ModuleForTests("foo", "android_common").Module()
-	entries := android.AndroidMkEntriesForTest(t, config, "", module)
-	if len(entries) != 2 {
-		t.Errorf("Expected 2 AndroidMk entries, got %d", len(entries))
-	}
-
-	distStrings := entries[0].GetDistForGoals(module)
-
-	if len(distStrings) != 4 {
-		t.Errorf("Expected 4 entries for dist: PHONY and dist-for-goals, but got %d", len(distStrings))
-	}
-
-	if distStrings[0] != ".PHONY: bar\n" {
-		t.Errorf("Expected .PHONY entry to declare bar, but got: %s", distStrings[0])
-	}
-
-	if !strings.Contains(distStrings[1], "$(call dist-for-goals,bar") ||
-		!strings.Contains(
-			distStrings[1],
-			".intermediates/foo/android_common/javac/foo.jar:bar/dir/bar.qux.jar") {
-		t.Errorf(
-			"Expected dist-for-goals entry to contain bar and new dest dir, but got: %s", distStrings[1])
-	}
-
-	if distStrings[2] != ".PHONY: baz\n" {
-		t.Errorf("Expected .PHONY entry to declare baz, but got: %s", distStrings[2])
-	}
-
-	if !strings.Contains(distStrings[3], "$(call dist-for-goals,baz") ||
-		!strings.Contains(distStrings[3], ".intermediates/foo/android_common/dex/foo.jar:foo.jar") {
-		t.Errorf(
-			"Expected dist-for-goals entry to contain my_other_goal and new dest dir, but got: %s",
-			distStrings[3])
-	}
-}
-
-func TestDistsWithTag(t *testing.T) {
-	ctx, config := testJava(t, `
-		java_library {
-			name: "foo_without_tag",
-			srcs: ["a.java"],
-			compile_dex: true,
-			dists: [
-				{
-					targets: ["hi"],
-				},
-			],
-		}
-		java_library {
-			name: "foo_with_tag",
-			srcs: ["a.java"],
-			compile_dex: true,
-			dists: [
-				{
-					targets: ["hi"],
-					tag: ".jar",
-				},
-			],
-		}
-	`)
-
-	moduleWithoutTag := ctx.ModuleForTests("foo_without_tag", "android_common").Module()
-	moduleWithTag := ctx.ModuleForTests("foo_with_tag", "android_common").Module()
-
-	withoutTagEntries := android.AndroidMkEntriesForTest(t, config, "", moduleWithoutTag)
-	withTagEntries := android.AndroidMkEntriesForTest(t, config, "", moduleWithTag)
-
-	if len(withoutTagEntries) != 2 || len(withTagEntries) != 2 {
-		t.Errorf("two mk entries per module expected, got %d and %d", len(withoutTagEntries), len(withTagEntries))
-	}
-
-	distFilesWithoutTag := withoutTagEntries[0].DistFiles
-	distFilesWithTag := withTagEntries[0].DistFiles
-
-	if len(distFilesWithTag[".jar"]) != 1 ||
-		!strings.Contains(distFilesWithTag[".jar"][0].String(), "/javac/foo_with_tag.jar") {
-		t.Errorf("expected foo_with_tag's .jar-tagged DistFiles to contain classes.jar, got %v", distFilesWithTag[".jar"])
-	}
-	if len(distFilesWithoutTag[".jar"]) > 0 {
-		t.Errorf("did not expect foo_without_tag's .jar-tagged DistFiles to contain files, but got %v", distFilesWithoutTag[".jar"])
-	}
-}
-
 func TestJavaSdkLibrary_RequireXmlPermissionFile(t *testing.T) {
 	ctx, config := testJava(t, `
 		java_sdk_library {
diff --git a/java/droiddoc.go b/java/droiddoc.go
index cf7f4fb..da8489a 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -1050,7 +1050,8 @@
 		return android.Paths{d.stubsSrcJar}, nil
 	case ".docs.zip":
 		return android.Paths{d.docZip}, nil
-	case ".api.txt":
+	case ".api.txt", android.DefaultDistTag:
+		// This is the default dist path for dist properties that have no tag property.
 		return android.Paths{d.apiFilePath}, nil
 	case ".removed-api.txt":
 		return android.Paths{d.removedApiFilePath}, nil
diff --git a/java/hiddenapi_singleton.go b/java/hiddenapi_singleton.go
index e57f323..8f0e09c 100644
--- a/java/hiddenapi_singleton.go
+++ b/java/hiddenapi_singleton.go
@@ -259,7 +259,7 @@
 		FlagWithInput("--blocked ",
 			android.PathForSource(ctx, "frameworks/base/config/hiddenapi-force-blocked.txt")).
 		FlagWithInput("--blocked ",
-			android.PathForSource(ctx, "frameworks/base/config/hiddenapi-temp-blocklist.txt")).
+			android.PathForSource(ctx, "frameworks/base/config/hiddenapi-temp-blocklist.txt")).FlagWithArg("--tag ", "lo-prio").
 		FlagWithInput("--unsupported ", android.PathForSource(
 			ctx, "frameworks/base/config/hiddenapi-unsupported-packages.txt")).Flag("--packages ").
 		FlagWithOutput("--output ", tempPath)
diff --git a/java/java.go b/java/java.go
index bf139a8..8738e00 100644
--- a/java/java.go
+++ b/java/java.go
@@ -456,8 +456,6 @@
 	// list of the xref extraction files
 	kytheFiles android.Paths
 
-	distFiles android.TaggedDistFiles
-
 	// Collect the module directory for IDE info in java/jdeps.go.
 	modulePaths []string
 
@@ -486,6 +484,8 @@
 	switch tag {
 	case "":
 		return append(android.Paths{j.outputFile}, j.extraOutputFiles...), nil
+	case android.DefaultDistTag:
+		return android.Paths{j.outputFile}, nil
 	case ".jar":
 		return android.Paths{j.implementationAndResourcesJar}, nil
 	case ".proguard_map":
@@ -2112,8 +2112,6 @@
 	if lib := proptools.String(j.usesLibraryProperties.Provides_uses_lib); lib != "" {
 		j.classLoaderContexts.AddContext(ctx, lib, j.DexJarBuildPath(), j.DexJarInstallPath())
 	}
-
-	j.distFiles = j.GenerateTaggedDistFiles(ctx)
 }
 
 func (j *Library) DepsMutator(ctx android.BottomUpMutatorContext) {
diff --git a/java/sdk_library.go b/java/sdk_library.go
index b832678..32bc077 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -1281,11 +1281,7 @@
 			Include_dirs       []string
 			Local_include_dirs []string
 		}
-		Dist struct {
-			Targets []string
-			Dest    *string
-			Dir     *string
-		}
+		Dists []android.Dist
 	}{}
 
 	// The stubs source processing uses the same compile time classpath when extracting the
@@ -1385,11 +1381,23 @@
 		}
 	}
 
-	// Dist the api txt artifact for sdk builds.
 	if !Bool(module.sdkLibraryProperties.No_dist) {
-		props.Dist.Targets = []string{"sdk", "win_sdk"}
-		props.Dist.Dest = proptools.StringPtr(fmt.Sprintf("%v.txt", module.distStem()))
-		props.Dist.Dir = proptools.StringPtr(path.Join(module.apiDistPath(apiScope), "api"))
+		// Dist the api txt and removed api txt artifacts for sdk builds.
+		distDir := proptools.StringPtr(path.Join(module.apiDistPath(apiScope), "api"))
+		for _, p := range []struct {
+			tag     string
+			pattern string
+		}{
+			{tag: ".api.txt", pattern: "%s.txt"},
+			{tag: ".removed-api.txt", pattern: "%s-removed.txt"},
+		} {
+			props.Dists = append(props.Dists, android.Dist{
+				Targets: []string{"sdk", "win_sdk"},
+				Dir:     distDir,
+				Dest:    proptools.StringPtr(fmt.Sprintf(p.pattern, module.distStem())),
+				Tag:     proptools.StringPtr(p.tag),
+			})
+		}
 	}
 
 	mctx.CreateModule(DroidstubsFactory, &props)
diff --git a/rust/androidmk.go b/rust/androidmk.go
index 74cd9bf..4e8b14d 100644
--- a/rust/androidmk.go
+++ b/rust/androidmk.go
@@ -43,7 +43,7 @@
 }
 
 func (mod *Module) AndroidMkEntries() []android.AndroidMkEntries {
-	if mod.Properties.HideFromMake {
+	if mod.Properties.HideFromMake || mod.hideApexVariantFromMake {
 
 		return []android.AndroidMkEntries{android.AndroidMkEntries{Disabled: true}}
 	}
diff --git a/rust/clippy_test.go b/rust/clippy_test.go
index 132b7b8..e24f666 100644
--- a/rust/clippy_test.go
+++ b/rust/clippy_test.go
@@ -18,6 +18,7 @@
 	"testing"
 
 	"android/soong/android"
+	"android/soong/cc"
 )
 
 func TestClippy(t *testing.T) {
@@ -45,6 +46,7 @@
 		}`
 
 	bp = bp + GatherRequiredDepsForTest()
+	bp = bp + cc.GatherRequiredDepsForTest(android.NoOsType)
 
 	fs := map[string][]byte{
 		// Reuse the same blueprint file for subdirectories.
diff --git a/rust/compiler_test.go b/rust/compiler_test.go
index a6dba9e..2b40727 100644
--- a/rust/compiler_test.go
+++ b/rust/compiler_test.go
@@ -19,6 +19,7 @@
 	"testing"
 
 	"android/soong/android"
+	"android/soong/cc"
 )
 
 // Test that feature flags are being correctly generated.
@@ -132,6 +133,7 @@
 		}`
 
 	bp = bp + GatherRequiredDepsForTest()
+	bp = bp + cc.GatherRequiredDepsForTest(android.NoOsType)
 
 	fs := map[string][]byte{
 		// Reuse the same blueprint file for subdirectories.
diff --git a/rust/config/allowed_list.go b/rust/config/allowed_list.go
index 3ad32fa..e6643f5 100644
--- a/rust/config/allowed_list.go
+++ b/rust/config/allowed_list.go
@@ -6,6 +6,7 @@
 	// for an example.
 	// TODO(b/160223496): enable rustfmt globally.
 	RustAllowedPaths = []string{
+		"device/google/cuttlefish",
 		"external/adhd",
 		"external/crosvm",
 		"external/minijail",
diff --git a/rust/rust.go b/rust/rust.go
index bd81c17..38caad3 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -74,6 +74,7 @@
 type Module struct {
 	android.ModuleBase
 	android.DefaultableModuleBase
+	android.ApexModuleBase
 
 	Properties BaseProperties
 
@@ -88,6 +89,8 @@
 	subAndroidMkOnce map[SubAndroidMkProvider]bool
 
 	outputFile android.OptionalPath
+
+	hideApexVariantFromMake bool
 }
 
 func (mod *Module) OutputFiles(tag string) (android.Paths, error) {
@@ -508,6 +511,7 @@
 	}
 
 	android.InitAndroidArchModule(mod, mod.hod, mod.multilib)
+	android.InitApexModule(mod)
 
 	android.InitDefaultableModule(mod)
 	return mod
@@ -605,6 +609,11 @@
 		ModuleContext: actx,
 	}
 
+	apexInfo := actx.Provider(android.ApexInfoProvider).(android.ApexInfo)
+	if !apexInfo.IsForPlatform() {
+		mod.hideApexVariantFromMake = true
+	}
+
 	toolchain := mod.toolchain(ctx)
 
 	if !toolchain.Supported() {
@@ -702,6 +711,11 @@
 	sourceDepTag        = dependencyTag{name: "source"}
 )
 
+func IsDylibDepTag(depTag blueprint.DependencyTag) bool {
+	tag, ok := depTag.(dependencyTag)
+	return ok && tag == dylibDepTag
+}
+
 type autoDep struct {
 	variation string
 	depTag    dependencyTag
@@ -1060,6 +1074,58 @@
 	return android.OptionalPath{}
 }
 
+var _ android.ApexModule = (*Module)(nil)
+
+func (mod *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion android.ApiLevel) error {
+	return nil
+}
+
+func (mod *Module) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
+	depTag := ctx.OtherModuleDependencyTag(dep)
+
+	if ccm, ok := dep.(*cc.Module); ok {
+		if ccm.HasStubsVariants() {
+			if cc.IsSharedDepTag(depTag) {
+				// dynamic dep to a stubs lib crosses APEX boundary
+				return false
+			}
+			if cc.IsRuntimeDepTag(depTag) {
+				// runtime dep to a stubs lib also crosses APEX boundary
+				return false
+			}
+
+			if cc.IsHeaderDepTag(depTag) {
+				return false
+			}
+		}
+		if mod.Static() && cc.IsSharedDepTag(depTag) {
+			// shared_lib dependency from a static lib is considered as crossing
+			// the APEX boundary because the dependency doesn't actually is
+			// linked; the dependency is used only during the compilation phase.
+			return false
+		}
+	}
+
+	if depTag == procMacroDepTag {
+		return false
+	}
+
+	return true
+}
+
+// Overrides ApexModule.IsInstallabeToApex()
+func (mod *Module) IsInstallableToApex() bool {
+	if mod.compiler != nil {
+		if lib, ok := mod.compiler.(*libraryDecorator); ok && (lib.shared() || lib.dylib()) {
+			return true
+		}
+		if _, ok := mod.compiler.(*binaryDecorator); ok {
+			return true
+		}
+	}
+	return false
+}
+
 var Bool = proptools.Bool
 var BoolDefault = proptools.BoolDefault
 var String = proptools.String
diff --git a/rust/rust_test.go b/rust/rust_test.go
index 187f0b6..646b252 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -120,6 +120,7 @@
 // attributes of the testRustCtx.
 func (tctx *testRustCtx) generateConfig() {
 	tctx.bp = tctx.bp + GatherRequiredDepsForTest()
+	tctx.bp = tctx.bp + cc.GatherRequiredDepsForTest(android.NoOsType)
 	cc.GatherRequiredFilesForTest(tctx.fs)
 	config := android.TestArchConfig(buildDir, tctx.env, tctx.bp, tctx.fs)
 	tctx.config = &config
diff --git a/rust/testing.go b/rust/testing.go
index 001f322..a8496d9 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -77,6 +77,7 @@
 			no_libcrt: true,
 			nocrt: true,
 			system_shared_libs: [],
+			apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
 		}
 		cc_library {
 			name: "libprotobuf-cpp-full",
@@ -93,6 +94,7 @@
 			host_supported: true,
                         native_coverage: false,
 			sysroot: true,
+			apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
 		}
 		rust_library {
 			name: "libtest",
@@ -102,6 +104,7 @@
 			host_supported: true,
                         native_coverage: false,
 			sysroot: true,
+			apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
 		}
 		rust_library {
 			name: "libprotobuf",
@@ -122,15 +125,11 @@
 			host_supported: true,
 		}
 
-` + cc.GatherRequiredDepsForTest(android.NoOsType)
+`
 	return bp
 }
 
-func CreateTestContext(config android.Config) *android.TestContext {
-	ctx := android.NewTestArchContext(config)
-	android.RegisterPrebuiltMutators(ctx)
-	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
-	cc.RegisterRequiredBuildComponentsForTest(ctx)
+func RegisterRequiredBuildComponentsForTest(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("rust_binary", RustBinaryFactory)
 	ctx.RegisterModuleType("rust_binary_host", RustBinaryHostFactory)
 	ctx.RegisterModuleType("rust_bindgen", RustBindgenFactory)
@@ -164,6 +163,14 @@
 		ctx.BottomUp("rust_begin", BeginMutator).Parallel()
 	})
 	ctx.RegisterSingletonType("rust_project_generator", rustProjectGeneratorSingleton)
+}
+
+func CreateTestContext(config android.Config) *android.TestContext {
+	ctx := android.NewTestArchContext(config)
+	android.RegisterPrebuiltMutators(ctx)
+	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
+	cc.RegisterRequiredBuildComponentsForTest(ctx)
+	RegisterRequiredBuildComponentsForTest(ctx)
 
 	return ctx
 }
diff --git a/ui/build/upload.go b/ui/build/upload.go
index a9346e0..4f30136 100644
--- a/ui/build/upload.go
+++ b/ui/build/upload.go
@@ -30,32 +30,34 @@
 )
 
 const (
+	// Used to generate a raw protobuf file that contains information
+	// of the list of metrics files from host to destination storage.
 	uploadPbFilename = ".uploader.pb"
 )
 
 var (
-	// For testing purpose
-	getTmpDir = ioutil.TempDir
+	// For testing purpose.
+	tmpDir = ioutil.TempDir
 )
 
 // UploadMetrics uploads a set of metrics files to a server for analysis. An
-// uploader full path is required to be specified in order to upload the set
-// of metrics files. This is accomplished by defining the ANDROID_ENABLE_METRICS_UPLOAD
-// environment variable. The metrics files are copied to a temporary directory
-// and the uploader is then executed in the background to allow the user to continue
-// working.
+// uploader full path is specified in ANDROID_ENABLE_METRICS_UPLOAD environment
+// variable in order to upload the set of metrics files. The metrics files are
+// first copied to a temporary directory and the uploader is then executed in
+// the background to allow the user/system to continue working. Soong communicates
+// to the uploader through the upload_proto raw protobuf file.
 func UploadMetrics(ctx Context, config Config, simpleOutput bool, buildStarted time.Time, files ...string) {
 	ctx.BeginTrace(metrics.RunSetupTool, "upload_metrics")
 	defer ctx.EndTrace()
 
 	uploader := config.MetricsUploaderApp()
-	// No metrics to upload if the path to the uploader was not specified.
 	if uploader == "" {
+		// If the uploader path was not specified, no metrics shall be uploaded.
 		return
 	}
 
-	// Some files may not exist. For example, build errors protobuf file
-	// may not exist since the build was successful.
+	// Some files passed in to this function may not exist. For example,
+	// build errors protobuf file may not exist since the build was successful.
 	var metricsFiles []string
 	for _, f := range files {
 		if _, err := os.Stat(f); err == nil {
@@ -70,7 +72,7 @@
 	// The temporary directory cannot be deleted as the metrics uploader is started
 	// in the background and requires to exist until the operation is done. The
 	// uploader can delete the directory as it is specified in the upload proto.
-	tmpDir, err := getTmpDir("", "upload_metrics")
+	tmpDir, err := tmpDir("", "upload_metrics")
 	if err != nil {
 		ctx.Fatalf("failed to create a temporary directory to store the list of metrics files: %v\n", err)
 	}
@@ -103,7 +105,7 @@
 	}
 
 	// Start the uploader in the background as it takes several milliseconds to start the uploader
-	// and prepare the metrics for upload. This affects small commands like "lunch".
+	// and prepare the metrics for upload. This affects small shell commands like "lunch".
 	cmd := Command(ctx, config, "upload metrics", uploader, "--upload-metrics", pbFile)
 	if simpleOutput {
 		cmd.RunOrFatal()
diff --git a/ui/build/upload_test.go b/ui/build/upload_test.go
index dccf156..768b031 100644
--- a/ui/build/upload_test.go
+++ b/ui/build/upload_test.go
@@ -62,16 +62,16 @@
 			}
 			defer os.RemoveAll(outDir)
 
-			// Supply our own getTmpDir to delete the temp dir once the test is done.
-			orgGetTmpDir := getTmpDir
-			getTmpDir = func(string, string) (string, error) {
+			// Supply our own tmpDir to delete the temp dir once the test is done.
+			orgTmpDir := tmpDir
+			tmpDir = func(string, string) (string, error) {
 				retDir := filepath.Join(outDir, "tmp_upload_dir")
 				if err := os.Mkdir(retDir, 0755); err != nil {
 					t.Fatalf("failed to create temporary directory %q: %v", retDir, err)
 				}
 				return retDir, nil
 			}
-			defer func() { getTmpDir = orgGetTmpDir }()
+			defer func() { tmpDir = orgTmpDir }()
 
 			metricsUploadDir := filepath.Join(outDir, ".metrics_uploader")
 			if err := os.Mkdir(metricsUploadDir, 0755); err != nil {
@@ -134,11 +134,11 @@
 			}
 			defer os.RemoveAll(outDir)
 
-			orgGetTmpDir := getTmpDir
-			getTmpDir = func(string, string) (string, error) {
+			orgTmpDir := tmpDir
+			tmpDir = func(string, string) (string, error) {
 				return tt.tmpDir, tt.tmpDirErr
 			}
-			defer func() { getTmpDir = orgGetTmpDir }()
+			defer func() { tmpDir = orgTmpDir }()
 
 			metricsFile := filepath.Join(outDir, "metrics_file_1")
 			if err := ioutil.WriteFile(metricsFile, []byte("test file"), 0644); err != nil {