Merge "apex: Restrict usage of "use_vendor""
diff --git a/Android.bp b/Android.bp
index 05972fd..8d0c1ea 100644
--- a/Android.bp
+++ b/Android.bp
@@ -366,6 +366,7 @@
         "rust/prebuilt.go",
         "rust/proc_macro.go",
         "rust/rust.go",
+        "rust/test.go",
         "rust/testing.go",
     ],
     testSrcs: [
@@ -373,6 +374,7 @@
         "rust/compiler_test.go",
         "rust/library_test.go",
         "rust/rust_test.go",
+        "rust/test_test.go",
     ],
     pluginFor: ["soong_build"],
 }
diff --git a/android/util.go b/android/util.go
index 71ded5e..81f481d 100644
--- a/android/util.go
+++ b/android/util.go
@@ -93,6 +93,20 @@
 	return s
 }
 
+func SortedStringMapValues(m interface{}) []string {
+	v := reflect.ValueOf(m)
+	if v.Kind() != reflect.Map {
+		panic(fmt.Sprintf("%#v is not a map", m))
+	}
+	keys := v.MapKeys()
+	s := make([]string, 0, len(keys))
+	for _, key := range keys {
+		s = append(s, v.MapIndex(key).String())
+	}
+	sort.Strings(s)
+	return s
+}
+
 func IndexList(s string, list []string) int {
 	for i, l := range list {
 		if l == s {
@@ -352,3 +366,14 @@
 	}
 	return ret
 }
+
+func CheckDuplicate(values []string) (duplicate string, found bool) {
+	seen := make(map[string]string)
+	for _, v := range values {
+		if duplicate, found = seen[v]; found {
+			return
+		}
+		seen[v] = v
+	}
+	return
+}
diff --git a/apex/apex_test.go b/apex/apex_test.go
index ea479aa..29ddd4a 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -1247,24 +1247,21 @@
 		}
 	`)
 
-	// non-APEX variant does not have __ANDROID__APEX__ defined
+	// non-APEX variant does not have __ANDROID_APEX(_NAME)__ defined
 	mylibCFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_core_static").Rule("cc").Args["cFlags"]
-	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__=myapex")
-	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__=otherapex")
+	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__")
 	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_MYAPEX__")
 	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_OTHERAPEX__")
 
-	// APEX variant has __ANDROID_APEX__=<apexname> defined
+	// APEX variant has __ANDROID_APEX(_NAME)__ defined
 	mylibCFlags = ctx.ModuleForTests("mylib", "android_arm64_armv8-a_core_static_myapex").Rule("cc").Args["cFlags"]
-	ensureContains(t, mylibCFlags, "-D__ANDROID_APEX__=myapex")
-	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__=otherapex")
+	ensureContains(t, mylibCFlags, "-D__ANDROID_APEX__")
 	ensureContains(t, mylibCFlags, "-D__ANDROID_APEX_MYAPEX__")
 	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_OTHERAPEX__")
 
-	// APEX variant has __ANDROID_APEX__=<apexname> defined
+	// APEX variant has __ANDROID_APEX(_NAME)__ defined
 	mylibCFlags = ctx.ModuleForTests("mylib", "android_arm64_armv8-a_core_static_otherapex").Rule("cc").Args["cFlags"]
-	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX__=myapex")
-	ensureContains(t, mylibCFlags, "-D__ANDROID_APEX__=otherapex")
+	ensureContains(t, mylibCFlags, "-D__ANDROID_APEX__")
 	ensureNotContains(t, mylibCFlags, "-D__ANDROID_APEX_MYAPEX__")
 	ensureContains(t, mylibCFlags, "-D__ANDROID_APEX_OTHERAPEX__")
 }
diff --git a/cc/cc.go b/cc/cc.go
index f90f1e8..a312c49 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -745,17 +745,18 @@
 
 func (c *Module) isLlndk(config android.Config) bool {
 	// Returns true for both LLNDK (public) and LLNDK-private libs.
-	return inList(c.BaseModuleName(), *llndkLibraries(config))
+	return isLlndkLibrary(c.BaseModuleName(), config)
 }
 
 func (c *Module) isLlndkPublic(config android.Config) bool {
 	// Returns true only for LLNDK (public) libs.
-	return c.isLlndk(config) && !c.isVndkPrivate(config)
+	name := c.BaseModuleName()
+	return isLlndkLibrary(name, config) && !isVndkPrivateLibrary(name, config)
 }
 
 func (c *Module) isVndkPrivate(config android.Config) bool {
 	// Returns true for LLNDK-private, VNDK-SP-private, and VNDK-core-private.
-	return inList(c.BaseModuleName(), *vndkPrivateLibraries(config))
+	return isVndkPrivateLibrary(c.BaseModuleName(), config)
 }
 
 func (c *Module) IsVndk() bool {
@@ -853,6 +854,27 @@
 	return c.linker != nil && c.linker.nativeCoverage()
 }
 
+func (c *Module) ExportedIncludeDirs() android.Paths {
+	if flagsProducer, ok := c.linker.(exportedFlagsProducer); ok {
+		return flagsProducer.exportedDirs()
+	}
+	return []android.Path{}
+}
+
+func (c *Module) ExportedSystemIncludeDirs() android.Paths {
+	if flagsProducer, ok := c.linker.(exportedFlagsProducer); ok {
+		return flagsProducer.exportedSystemDirs()
+	}
+	return []android.Path{}
+}
+
+func (c *Module) ExportedFlags() []string {
+	if flagsProducer, ok := c.linker.(exportedFlagsProducer); ok {
+		return flagsProducer.exportedFlags()
+	}
+	return []string{}
+}
+
 func isBionic(name string) bool {
 	switch name {
 	case "libc", "libm", "libdl", "linker":
@@ -1456,7 +1478,7 @@
 }
 
 // Split name#version into name and version
-func stubsLibNameAndVersion(name string) (string, string) {
+func StubsLibNameAndVersion(name string) (string, string) {
 	if sharp := strings.LastIndex(name, "#"); sharp != -1 && sharp != len(name)-1 {
 		version := name[sharp+1:]
 		libname := name[:sharp]
@@ -1496,21 +1518,20 @@
 		// The caller can then know to add the variantLibs dependencies differently from the
 		// nonvariantLibs
 
-		llndkLibraries := llndkLibraries(actx.Config())
 		vendorPublicLibraries := vendorPublicLibraries(actx.Config())
 		rewriteNdkLibs := func(list []string) (nonvariantLibs []string, variantLibs []string) {
 			variantLibs = []string{}
 			nonvariantLibs = []string{}
 			for _, entry := range list {
 				// strip #version suffix out
-				name, _ := stubsLibNameAndVersion(entry)
+				name, _ := StubsLibNameAndVersion(entry)
 				if ctx.useSdk() && inList(name, ndkPrebuiltSharedLibraries) {
 					if !inList(name, ndkMigratedLibs) {
 						nonvariantLibs = append(nonvariantLibs, name+".ndk."+version)
 					} else {
 						variantLibs = append(variantLibs, name+ndkLibrarySuffix)
 					}
-				} else if ctx.useVndk() && inList(name, *llndkLibraries) {
+				} else if ctx.useVndk() && isLlndkLibrary(name, ctx.Config()) {
 					nonvariantLibs = append(nonvariantLibs, name+llndkLibrarySuffix)
 				} else if (ctx.Platform() || ctx.ProductSpecific()) && inList(name, *vendorPublicLibraries) {
 					vendorPublicLib := name + vendorPublicLibrarySuffix
@@ -1609,7 +1630,7 @@
 		// If the version is not specified, add dependency to the latest stubs library.
 		// The stubs library will be used when the depending module is built for APEX and
 		// the dependent module is not in the same APEX.
-		latestVersion := latestStubsVersionFor(actx.Config(), name)
+		latestVersion := LatestStubsVersionFor(actx.Config(), name)
 		if version == "" && latestVersion != "" && versionVariantAvail {
 			actx.AddVariationDependencies([]blueprint.Variation{
 				{Mutator: "link", Variation: "shared"},
@@ -1632,7 +1653,7 @@
 			lib = impl
 		}
 
-		name, version := stubsLibNameAndVersion(lib)
+		name, version := StubsLibNameAndVersion(lib)
 		sharedLibNames = append(sharedLibNames, name)
 
 		addSharedLibDependencies(depTag, name, version)
@@ -1820,7 +1841,6 @@
 // it is subject to be double loaded. Such lib should be explicitly marked as double_loadable: true
 // or as vndk-sp (vndk: { enabled: true, support_system_process: true}).
 func checkDoubleLoadableLibraries(ctx android.TopDownMutatorContext) {
-	llndkLibraries := llndkLibraries(ctx.Config())
 	check := func(child, parent android.Module) bool {
 		to, ok := child.(*Module)
 		if !ok {
@@ -1837,7 +1857,7 @@
 			return true
 		}
 
-		if to.isVndkSp() || inList(child.Name(), *llndkLibraries) || Bool(to.VendorProperties.Double_loadable) {
+		if to.isVndkSp() || to.isLlndk(ctx.Config()) || Bool(to.VendorProperties.Double_loadable) {
 			return false
 		}
 
@@ -1852,7 +1872,7 @@
 	}
 	if module, ok := ctx.Module().(*Module); ok {
 		if lib, ok := module.linker.(*libraryDecorator); ok && lib.shared() {
-			if inList(ctx.ModuleName(), *llndkLibraries) || Bool(module.VendorProperties.Double_loadable) {
+			if module.isLlndk(ctx.Config()) || Bool(module.VendorProperties.Double_loadable) {
 				ctx.WalkDeps(check)
 			}
 		}
@@ -1866,7 +1886,6 @@
 	directStaticDeps := []LinkableInterface{}
 	directSharedDeps := []LinkableInterface{}
 
-	llndkLibraries := llndkLibraries(ctx.Config())
 	vendorPublicLibraries := vendorPublicLibraries(ctx.Config())
 
 	reexportExporter := func(exporter exportedFlagsProducer) {
@@ -2138,7 +2157,7 @@
 			libName := strings.TrimSuffix(depName, llndkLibrarySuffix)
 			libName = strings.TrimSuffix(libName, vendorPublicLibrarySuffix)
 			libName = strings.TrimPrefix(libName, "prebuilt_")
-			isLLndk := inList(libName, *llndkLibraries)
+			isLLndk := isLlndkLibrary(libName, ctx.Config())
 			isVendorPublicLib := inList(libName, *vendorPublicLibraries)
 			bothVendorAndCoreVariantsExist := ccDep.HasVendorVariant() || isLLndk
 
@@ -2329,7 +2348,9 @@
 	if shared, ok := c.linker.(interface {
 		shared() bool
 	}); ok {
-		return shared.shared()
+		// Stub libs and prebuilt libs in a versioned SDK are not
+		// installable to APEX even though they are shared libs.
+		return shared.shared() && !c.IsStubs() && c.ContainingSdk().Unversioned()
 	} else if _, ok := c.linker.(testPerSrc); ok {
 		return true
 	}
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 064b1a2..808968c 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -293,6 +293,7 @@
 				enabled: true,
 			},
 			nocrt: true,
+			stem: "libvndk-private",
 		}
 
 		cc_library {
@@ -303,6 +304,7 @@
 				support_system_process: true,
 			},
 			nocrt: true,
+			suffix: "-x",
 		}
 
 		cc_library {
@@ -313,6 +315,11 @@
 				support_system_process: true,
 			},
 			nocrt: true,
+			target: {
+				vendor: {
+					suffix: "-x",
+				},
+			},
 		}
 	`, config)
 
@@ -345,9 +352,9 @@
 	checkVndkSnapshot(t, ctx, "libvndk_sp", vndkSpLib2ndPath, variant2nd)
 
 	checkVndkOutput(t, ctx, "vndk/llndk.libraries.txt", []string{"libc.so", "libdl.so", "libft2.so", "libm.so"})
-	checkVndkOutput(t, ctx, "vndk/vndkcore.libraries.txt", []string{"libvndk.so", "libvndk_private.so"})
-	checkVndkOutput(t, ctx, "vndk/vndkprivate.libraries.txt", []string{"libft2.so", "libvndk_private.so", "libvndk_sp_private.so"})
-	checkVndkOutput(t, ctx, "vndk/vndksp.libraries.txt", []string{"libc++.so", "libvndk_sp.so", "libvndk_sp_private.so"})
+	checkVndkOutput(t, ctx, "vndk/vndkcore.libraries.txt", []string{"libvndk-private.so", "libvndk.so"})
+	checkVndkOutput(t, ctx, "vndk/vndkprivate.libraries.txt", []string{"libft2.so", "libvndk-private.so", "libvndk_sp_private-x.so"})
+	checkVndkOutput(t, ctx, "vndk/vndksp.libraries.txt", []string{"libc++.so", "libvndk_sp-x.so", "libvndk_sp_private-x.so"})
 	checkVndkOutput(t, ctx, "vndk/vndkcorevariant.libraries.txt", nil)
 	// merged & tagged & filtered-out(libclang_rt)
 	checkVndkOutput(t, ctx, "vndk/vndk.libraries.txt", []string{
@@ -356,14 +363,27 @@
 		"LLNDK: libft2.so",
 		"LLNDK: libm.so",
 		"VNDK-SP: libc++.so",
-		"VNDK-SP: libvndk_sp.so",
-		"VNDK-SP: libvndk_sp_private.so",
+		"VNDK-SP: libvndk_sp-x.so",
+		"VNDK-SP: libvndk_sp_private-x.so",
+		"VNDK-core: libvndk-private.so",
 		"VNDK-core: libvndk.so",
-		"VNDK-core: libvndk_private.so",
 		"VNDK-private: libft2.so",
-		"VNDK-private: libvndk_private.so",
-		"VNDK-private: libvndk_sp_private.so",
+		"VNDK-private: libvndk-private.so",
+		"VNDK-private: libvndk_sp_private-x.so",
 	})
+	checkVndkOutput(t, ctx, "vndk/llndk.libraries.txt", []string{
+		"libc.so", "libdl.so", "libft2.so", "libm.so",
+	})
+	checkVndkOutput(t, ctx, "vndk/vndkcore.libraries.txt", []string{
+		"libvndk-private.so", "libvndk.so",
+	})
+	checkVndkOutput(t, ctx, "vndk/vndksp.libraries.txt", []string{
+		"libc++.so", "libvndk_sp-x.so", "libvndk_sp_private-x.so",
+	})
+	checkVndkOutput(t, ctx, "vndk/vndkprivate.libraries.txt", []string{
+		"libft2.so", "libvndk-private.so", "libvndk_sp_private-x.so",
+	})
+	checkVndkOutput(t, ctx, "vndk/vndkcorevariant.libraries.txt", []string{})
 }
 
 func TestVndkUsingCoreVariant(t *testing.T) {
@@ -405,7 +425,36 @@
 	`, config)
 
 	checkVndkOutput(t, ctx, "vndk/vndkcore.libraries.txt", []string{"libvndk.so", "libvndk2.so"})
-	checkVndkOutput(t, ctx, "vndk/vndkcorevariant.libraries.txt", []string{"libvndk2.so"})
+	checkVndkOutput(t, ctx, "vndk/vndkcorevariant.libraries.txt", []string{
+		"libc++.so", "libvndk2.so", "libvndk_sp.so",
+	})
+}
+
+func TestVndkWhenVndkVersionIsNotSet(t *testing.T) {
+	config := android.TestArchConfig(buildDir, nil)
+	config.TestProductVariables.DeviceVndkVersion = nil
+	config.TestProductVariables.Platform_vndk_version = nil
+
+	ctx := testCcWithConfig(t, `
+		cc_library {
+			name: "libvndk",
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+			},
+			nocrt: true,
+		}
+	`, config)
+
+	checkVndkOutput(t, ctx, "vndk/vndk.libraries.txt", []string{
+		"LLNDK: libc.so",
+		"LLNDK: libdl.so",
+		"LLNDK: libft2.so",
+		"LLNDK: libm.so",
+		"VNDK-SP: libc++.so",
+		"VNDK-core: libvndk.so",
+		"VNDK-private: libft2.so",
+	})
 }
 
 func TestVndkDepError(t *testing.T) {
@@ -1431,13 +1480,13 @@
 		symbol_file: "",
 	}`, config)
 
-	assertArrayString(t, *vndkCoreLibraries(config),
+	assertMapKeys(t, vndkCoreLibraries(config),
 		[]string{"libvndk", "libvndkprivate"})
-	assertArrayString(t, *vndkSpLibraries(config),
+	assertMapKeys(t, vndkSpLibraries(config),
 		[]string{"libc++", "libvndksp"})
-	assertArrayString(t, *llndkLibraries(config),
+	assertMapKeys(t, llndkLibraries(config),
 		[]string{"libc", "libdl", "libft2", "libllndk", "libllndkprivate", "libm"})
-	assertArrayString(t, *vndkPrivateLibraries(config),
+	assertMapKeys(t, vndkPrivateLibraries(config),
 		[]string{"libft2", "libllndkprivate", "libvndkprivate"})
 
 	vendorVariant27 := "android_arm64_armv8-a_vendor.27_shared"
@@ -2419,6 +2468,11 @@
 	}
 }
 
+func assertMapKeys(t *testing.T, m map[string]string, expected []string) {
+	t.Helper()
+	assertArrayString(t, android.SortedStringKeys(m), expected)
+}
+
 func TestDefaults(t *testing.T) {
 	ctx := testCc(t, `
 		cc_defaults {
diff --git a/cc/compiler.go b/cc/compiler.go
index 438dee7..ff68101 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -320,8 +320,7 @@
 	}
 
 	if ctx.apexName() != "" {
-		// TODO(b/142582178): remove the value for __ANDROID_APEX__
-		flags.GlobalFlags = append(flags.GlobalFlags, "-D__ANDROID_APEX__="+ctx.apexName())
+		flags.GlobalFlags = append(flags.GlobalFlags, "-D__ANDROID_APEX__")
 		flags.GlobalFlags = append(flags.GlobalFlags, "-D__ANDROID_APEX_"+makeDefineString(ctx.apexName())+"__")
 	}
 
diff --git a/cc/fuzz.go b/cc/fuzz.go
index 2c3b973..577fa70 100644
--- a/cc/fuzz.go
+++ b/cc/fuzz.go
@@ -149,7 +149,7 @@
 	}
 
 	if fuzz.Properties.Fuzz_config != nil {
-		configPath := android.PathForModuleOut(ctx, "config").Join(ctx, "config.txt")
+		configPath := android.PathForModuleOut(ctx, "config").Join(ctx, "config.json")
 		ctx.Build(pctx, android.BuildParams{
 			Rule:        android.WriteFile,
 			Description: "fuzzer infrastructure configuration",
@@ -259,7 +259,7 @@
 
 		// The executable.
 		archDirs[archDir] = append(archDirs[archDir],
-			fileToZip{ccModule.outputFile.Path(), ccModule.Name()})
+			fileToZip{ccModule.UnstrippedOutputFile(), ccModule.Name()})
 
 		// The corpora.
 		for _, corpusEntry := range fuzzModule.corpus {
diff --git a/cc/library.go b/cc/library.go
index 5a08879..8d90cd8 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -158,6 +158,10 @@
 	// listed in local_include_dirs.
 	Export_include_dirs []string `android:"arch_variant"`
 
+	// list of directories that will be added to the system include path
+	// using -isystem for this module and any module that links against this module.
+	Export_system_include_dirs []string `android:"arch_variant"`
+
 	Target struct {
 		Vendor struct {
 			// list of exported include directories, like
@@ -245,10 +249,13 @@
 
 func (f *flagExporter) exportIncludes(ctx ModuleContext) {
 	f.dirs = append(f.dirs, f.exportedIncludes(ctx)...)
+	f.systemDirs = append(f.systemDirs, android.PathsForModuleSrc(ctx, f.Properties.Export_system_include_dirs)...)
 }
 
 func (f *flagExporter) exportIncludesAsSystem(ctx ModuleContext) {
+	// all dirs are force exported as system
 	f.systemDirs = append(f.systemDirs, f.exportedIncludes(ctx)...)
+	f.systemDirs = append(f.systemDirs, android.PathsForModuleSrc(ctx, f.Properties.Export_system_include_dirs)...)
 }
 
 func (f *flagExporter) reexportDirs(dirs ...android.Path) {
@@ -579,24 +586,28 @@
 	availableFor(string) bool
 }
 
-func (library *libraryDecorator) getLibName(ctx BaseModuleContext) string {
+func (library *libraryDecorator) getLibNameHelper(baseModuleName string, useVndk bool) string {
 	name := library.libName
 	if name == "" {
 		name = String(library.Properties.Stem)
 		if name == "" {
-			name = ctx.baseModuleName()
+			name = baseModuleName
 		}
 	}
 
 	suffix := ""
-	if ctx.useVndk() {
+	if useVndk {
 		suffix = String(library.Properties.Target.Vendor.Suffix)
 	}
 	if suffix == "" {
 		suffix = String(library.Properties.Suffix)
 	}
 
-	name += suffix
+	return name + suffix
+}
+
+func (library *libraryDecorator) getLibName(ctx BaseModuleContext) string {
+	name := library.getLibNameHelper(ctx.baseModuleName(), ctx.useVndk())
 
 	if ctx.isVndkExt() {
 		// vndk-ext lib should have the same name with original lib
@@ -1294,7 +1305,7 @@
 
 var stubsVersionsLock sync.Mutex
 
-func latestStubsVersionFor(config android.Config, name string) string {
+func LatestStubsVersionFor(config android.Config, name string) string {
 	versions, ok := stubsVersionsFor(config)[name]
 	if ok && len(versions) > 0 {
 		// the versions are alreay sorted in ascending order
diff --git a/cc/makevars.go b/cc/makevars.go
index f9c58b9..e8cedf0 100644
--- a/cc/makevars.go
+++ b/cc/makevars.go
@@ -101,35 +101,6 @@
 
 	ctx.Strict("BOARD_VNDK_VERSION", ctx.DeviceConfig().VndkVersion())
 
-	ctx.Strict("VNDK_CORE_LIBRARIES", strings.Join(*vndkCoreLibraries(ctx.Config()), " "))
-	ctx.Strict("VNDK_SAMEPROCESS_LIBRARIES", strings.Join(*vndkSpLibraries(ctx.Config()), " "))
-
-	// Make uses LLNDK_LIBRARIES to determine which libraries to install.
-	// HWASAN is only part of the LL-NDK in builds in which libc depends on HWASAN.
-	// Therefore, by removing the library here, we cause it to only be installed if libc
-	// depends on it.
-	installedLlndkLibraries := []string{}
-
-	// Make uses LLNDK_MOVED_TO_APEX_LIBRARIES to avoid installing libraries on /system if
-	// they been moved to an apex.
-	movedToApexLlndkLibraries := []string{}
-	for _, lib := range *llndkLibraries(ctx.Config()) {
-		if strings.HasPrefix(lib, "libclang_rt.hwasan-") {
-			continue
-		}
-		installedLlndkLibraries = append(installedLlndkLibraries, lib)
-
-		// Skip bionic libs, they are handled in different manner
-		if android.DirectlyInAnyApex(&notOnHostContext{}, lib) && !isBionic(lib) {
-			movedToApexLlndkLibraries = append(movedToApexLlndkLibraries, lib)
-		}
-	}
-	ctx.Strict("LLNDK_LIBRARIES", strings.Join(installedLlndkLibraries, " "))
-	ctx.Strict("LLNDK_MOVED_TO_APEX_LIBRARIES", strings.Join(movedToApexLlndkLibraries, " "))
-
-	ctx.Strict("VNDK_PRIVATE_LIBRARIES", strings.Join(*vndkPrivateLibraries(ctx.Config()), " "))
-	ctx.Strict("VNDK_USING_CORE_VARIANT_LIBRARIES", strings.Join(*vndkUsingCoreVariantLibraries(ctx.Config()), " "))
-
 	// Filter vendor_public_library that are exported to make
 	exportedVendorPublicLibraries := []string{}
 	ctx.VisitAllModules(func(module android.Module) {
diff --git a/cc/sabi.go b/cc/sabi.go
index 8a9eff0..4760313 100644
--- a/cc/sabi.go
+++ b/cc/sabi.go
@@ -78,7 +78,7 @@
 
 func sabiDepsMutator(mctx android.TopDownMutatorContext) {
 	if c, ok := mctx.Module().(*Module); ok &&
-		((c.IsVndk() && c.UseVndk()) || inList(c.Name(), *llndkLibraries(mctx.Config())) ||
+		((c.IsVndk() && c.UseVndk()) || c.isLlndk(mctx.Config()) ||
 			(c.sabi != nil && c.sabi.Properties.CreateSAbiDumps)) {
 		mctx.VisitDirectDeps(func(m android.Module) {
 			tag := mctx.OtherModuleDependencyTag(m)
diff --git a/cc/sanitize.go b/cc/sanitize.go
index e4c6b1c..a3b4e8e 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -873,7 +873,7 @@
 		}
 
 		if mctx.Device() && runtimeLibrary != "" {
-			if inList(runtimeLibrary, *llndkLibraries(mctx.Config())) && !c.static() && c.UseVndk() {
+			if isLlndkLibrary(runtimeLibrary, mctx.Config()) && !c.static() && c.UseVndk() {
 				runtimeLibrary = runtimeLibrary + llndkLibrarySuffix
 			}
 
diff --git a/cc/test.go b/cc/test.go
index 0e66e28..5c49d6e 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -173,7 +173,7 @@
 		if test, ok := m.linker.(testPerSrc); ok {
 			numTests := len(test.srcs())
 			if test.testPerSrc() && numTests > 0 {
-				if duplicate, found := checkDuplicate(test.srcs()); found {
+				if duplicate, found := android.CheckDuplicate(test.srcs()); found {
 					mctx.PropertyErrorf("srcs", "found a duplicate entry %q", duplicate)
 					return
 				}
@@ -206,17 +206,6 @@
 	}
 }
 
-func checkDuplicate(values []string) (duplicate string, found bool) {
-	seen := make(map[string]string)
-	for _, v := range values {
-		if duplicate, found = seen[v]; found {
-			return
-		}
-		seen[v] = v
-	}
-	return
-}
-
 type testDecorator struct {
 	Properties TestProperties
 	linker     *baseLinker
diff --git a/cc/vndk.go b/cc/vndk.go
index 2805e56..f39ee50 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -17,8 +17,8 @@
 import (
 	"encoding/json"
 	"errors"
+	"fmt"
 	"path/filepath"
-	"sort"
 	"strings"
 	"sync"
 
@@ -208,34 +208,44 @@
 	headerExts = []string{".h", ".hh", ".hpp", ".hxx", ".h++", ".inl", ".inc", ".ipp", ".h.generic"}
 )
 
-func vndkCoreLibraries(config android.Config) *[]string {
+func vndkCoreLibraries(config android.Config) map[string]string {
 	return config.Once(vndkCoreLibrariesKey, func() interface{} {
-		return &[]string{}
-	}).(*[]string)
+		return make(map[string]string)
+	}).(map[string]string)
 }
 
-func vndkSpLibraries(config android.Config) *[]string {
+func vndkSpLibraries(config android.Config) map[string]string {
 	return config.Once(vndkSpLibrariesKey, func() interface{} {
-		return &[]string{}
-	}).(*[]string)
+		return make(map[string]string)
+	}).(map[string]string)
 }
 
-func llndkLibraries(config android.Config) *[]string {
+func isLlndkLibrary(baseModuleName string, config android.Config) bool {
+	_, ok := llndkLibraries(config)[baseModuleName]
+	return ok
+}
+
+func llndkLibraries(config android.Config) map[string]string {
 	return config.Once(llndkLibrariesKey, func() interface{} {
-		return &[]string{}
-	}).(*[]string)
+		return make(map[string]string)
+	}).(map[string]string)
 }
 
-func vndkPrivateLibraries(config android.Config) *[]string {
+func isVndkPrivateLibrary(baseModuleName string, config android.Config) bool {
+	_, ok := vndkPrivateLibraries(config)[baseModuleName]
+	return ok
+}
+
+func vndkPrivateLibraries(config android.Config) map[string]string {
 	return config.Once(vndkPrivateLibrariesKey, func() interface{} {
-		return &[]string{}
-	}).(*[]string)
+		return make(map[string]string)
+	}).(map[string]string)
 }
 
-func vndkUsingCoreVariantLibraries(config android.Config) *[]string {
+func vndkUsingCoreVariantLibraries(config android.Config) map[string]string {
 	return config.Once(vndkUsingCoreVariantLibrariesKey, func() interface{} {
-		return &[]string{}
-	}).(*[]string)
+		return make(map[string]string)
+	}).(map[string]string)
 }
 
 func modulePaths(config android.Config) map[string]string {
@@ -272,63 +282,44 @@
 
 func processLlndkLibrary(mctx android.BottomUpMutatorContext, m *Module) {
 	lib := m.linker.(*llndkStubDecorator)
-	name := strings.TrimSuffix(m.Name(), llndkLibrarySuffix)
+	name := m.BaseModuleName()
+	filename := m.BaseModuleName() + ".so"
 
 	vndkLibrariesLock.Lock()
 	defer vndkLibrariesLock.Unlock()
 
-	llndkLibraries := llndkLibraries(mctx.Config())
-	if !inList(name, *llndkLibraries) {
-		*llndkLibraries = append(*llndkLibraries, name)
-		sort.Strings(*llndkLibraries)
-	}
+	llndkLibraries(mctx.Config())[name] = filename
 	if !Bool(lib.Properties.Vendor_available) {
-		vndkPrivateLibraries := vndkPrivateLibraries(mctx.Config())
-		if !inList(name, *vndkPrivateLibraries) {
-			*vndkPrivateLibraries = append(*vndkPrivateLibraries, name)
-			sort.Strings(*vndkPrivateLibraries)
-		}
+		vndkPrivateLibraries(mctx.Config())[name] = filename
 	}
 }
 
 func processVndkLibrary(mctx android.BottomUpMutatorContext, m *Module) {
-	name := strings.TrimPrefix(m.Name(), "prebuilt_")
+	name := m.BaseModuleName()
+	filename, err := getVndkFileName(m)
+	if err != nil {
+		panic(err)
+	}
 
 	vndkLibrariesLock.Lock()
 	defer vndkLibrariesLock.Unlock()
 
-	modulePaths := modulePaths(mctx.Config())
+	modulePaths(mctx.Config())[name] = mctx.ModuleDir()
+
 	if inList(name, vndkMustUseVendorVariantList(mctx.Config())) {
 		m.Properties.MustUseVendorVariant = true
 	}
-	if mctx.DeviceConfig().VndkUseCoreVariant() && !inList(name, vndkMustUseVendorVariantList(mctx.Config())) {
-		vndkUsingCoreVariantLibraries := vndkUsingCoreVariantLibraries(mctx.Config())
-		if !inList(name, *vndkUsingCoreVariantLibraries) {
-			*vndkUsingCoreVariantLibraries = append(*vndkUsingCoreVariantLibraries, name)
-			sort.Strings(*vndkUsingCoreVariantLibraries)
-		}
+	if mctx.DeviceConfig().VndkUseCoreVariant() && !m.Properties.MustUseVendorVariant {
+		vndkUsingCoreVariantLibraries(mctx.Config())[name] = filename
 	}
+
 	if m.vndkdep.isVndkSp() {
-		vndkSpLibraries := vndkSpLibraries(mctx.Config())
-		if !inList(name, *vndkSpLibraries) {
-			*vndkSpLibraries = append(*vndkSpLibraries, name)
-			sort.Strings(*vndkSpLibraries)
-			modulePaths[name] = mctx.ModuleDir()
-		}
+		vndkSpLibraries(mctx.Config())[name] = filename
 	} else {
-		vndkCoreLibraries := vndkCoreLibraries(mctx.Config())
-		if !inList(name, *vndkCoreLibraries) {
-			*vndkCoreLibraries = append(*vndkCoreLibraries, name)
-			sort.Strings(*vndkCoreLibraries)
-			modulePaths[name] = mctx.ModuleDir()
-		}
+		vndkCoreLibraries(mctx.Config())[name] = filename
 	}
 	if !Bool(m.VendorProperties.Vendor_available) {
-		vndkPrivateLibraries := vndkPrivateLibraries(mctx.Config())
-		if !inList(name, *vndkPrivateLibraries) {
-			*vndkPrivateLibraries = append(*vndkPrivateLibraries, name)
-			sort.Strings(*vndkPrivateLibraries)
-		}
+		vndkPrivateLibraries(mctx.Config())[name] = filename
 	}
 }
 
@@ -402,9 +393,20 @@
 	return &vndkSnapshotSingleton{}
 }
 
-type vndkSnapshotSingleton struct{}
+type vndkSnapshotSingleton struct {
+	installedLlndkLibraries      []string
+	llnkdLibrariesFile           android.Path
+	vndkSpLibrariesFile          android.Path
+	vndkCoreLibrariesFile        android.Path
+	vndkPrivateLibrariesFile     android.Path
+	vndkCoreVariantLibrariesFile android.Path
+	vndkLibrariesFile            android.Path
+}
 
 func (c *vndkSnapshotSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+	// build these files even if PlatformVndkVersion or BoardVndkVersion is not set
+	c.buildVndkLibrariesTxtFiles(ctx)
+
 	// BOARD_VNDK_VERSION must be set to 'current' in order to generate a VNDK snapshot.
 	if ctx.DeviceConfig().VndkVersion() != "current" {
 		return
@@ -418,8 +420,6 @@
 		return
 	}
 
-	c.buildVndkLibrariesTxtFiles(ctx)
-
 	outputs := vndkSnapshotOutputs(ctx.Config())
 
 	snapshotDir := "vndk-snapshot"
@@ -488,9 +488,9 @@
 		}
 	}
 
-	vndkCoreLibraries := vndkCoreLibraries(ctx.Config())
-	vndkSpLibraries := vndkSpLibraries(ctx.Config())
-	vndkPrivateLibraries := vndkPrivateLibraries(ctx.Config())
+	vndkCoreLibraries := android.SortedStringKeys(vndkCoreLibraries(ctx.Config()))
+	vndkSpLibraries := android.SortedStringKeys(vndkSpLibraries(ctx.Config()))
+	vndkPrivateLibraries := android.SortedStringKeys(vndkPrivateLibraries(ctx.Config()))
 
 	var generatedHeaders android.Paths
 	includeDirs := make(map[string]bool)
@@ -547,9 +547,9 @@
 			return nil, "", false
 		}
 		name := ctx.ModuleName(m)
-		if inList(name, *vndkCoreLibraries) {
+		if inList(name, vndkCoreLibraries) {
 			return l, filepath.Join("shared", "vndk-core"), true
-		} else if inList(name, *vndkSpLibraries) {
+		} else if inList(name, vndkSpLibraries) {
 			return l, filepath.Join("shared", "vndk-sp"), true
 		} else {
 			return nil, "", false
@@ -635,9 +635,9 @@
 		}
 	}
 
-	installSnapshotFileFromContent(android.JoinWithSuffix(*vndkCoreLibraries, ".so", "\\n"),
+	installSnapshotFileFromContent(android.JoinWithSuffix(vndkCoreLibraries, ".so", "\\n"),
 		filepath.Join(configsDir, "vndkcore.libraries.txt"))
-	installSnapshotFileFromContent(android.JoinWithSuffix(*vndkPrivateLibraries, ".so", "\\n"),
+	installSnapshotFileFromContent(android.JoinWithSuffix(vndkPrivateLibraries, ".so", "\\n"),
 		filepath.Join(configsDir, "vndkprivate.libraries.txt"))
 
 	var modulePathTxtBuilder strings.Builder
@@ -660,75 +660,60 @@
 		filepath.Join(configsDir, "module_paths.txt"))
 }
 
-func installListFile(ctx android.SingletonContext, list []string, pathComponents ...string) android.OutputPath {
-	out := android.PathForOutput(ctx, pathComponents...)
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        android.WriteFile,
-		Output:      out,
-		Description: "Writing " + out.String(),
-		Args: map[string]string{
-			"content": strings.Join(list, "\\n"),
-		},
-	})
-	return out
+func getVndkFileName(m *Module) (string, error) {
+	if library, ok := m.linker.(*libraryDecorator); ok {
+		return library.getLibNameHelper(m.BaseModuleName(), true) + ".so", nil
+	}
+	if prebuilt, ok := m.linker.(*prebuiltLibraryLinker); ok {
+		return prebuilt.libraryDecorator.getLibNameHelper(m.BaseModuleName(), true) + ".so", nil
+	}
+	return "", fmt.Errorf("VNDK library should have libraryDecorator or prebuiltLibraryLinker as linker: %T", m.linker)
 }
 
 func (c *vndkSnapshotSingleton) buildVndkLibrariesTxtFiles(ctx android.SingletonContext) {
-	var (
-		llndk, vndkcore, vndksp, vndkprivate, vndkcorevariant, merged []string
-	)
-	vndkVersion := ctx.DeviceConfig().PlatformVndkVersion()
-	config := ctx.Config()
-	ctx.VisitAllModules(func(m android.Module) {
-		if !m.Enabled() {
-			return
+	// Make uses LLNDK_LIBRARIES to determine which libraries to install.
+	// HWASAN is only part of the LL-NDK in builds in which libc depends on HWASAN.
+	// Therefore, by removing the library here, we cause it to only be installed if libc
+	// depends on it.
+	installedLlndkLibraries := make(map[string]string)
+	for lib, filename := range llndkLibraries(ctx.Config()) {
+		if strings.HasPrefix(lib, "libclang_rt.hwasan-") {
+			continue
 		}
-		c, ok := m.(*Module)
-		if !ok || c.Os().Class != android.Device {
-			return
-		}
-		lib, ok := c.linker.(interface{ shared() bool })
-		if !ok || !lib.shared() {
-			return
-		}
+		installedLlndkLibraries[lib] = filename
+	}
 
-		if !c.OutputFile().Valid() {
-			return
-		}
+	installListFile := func(list []string, fileName string) android.Path {
+		out := android.PathForOutput(ctx, "vndk", fileName)
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        android.WriteFile,
+			Output:      out,
+			Description: "Writing " + out.String(),
+			Args: map[string]string{
+				"content": strings.Join(list, "\\n"),
+			},
+		})
+		return out
+	}
 
-		filename := c.OutputFile().Path().Base()
-		if c.isLlndk(config) {
-			llndk = append(llndk, filename)
-			if c.isVndkPrivate(config) {
-				vndkprivate = append(vndkprivate, filename)
-			}
-		} else if c.vndkVersion() == vndkVersion && c.IsVndk() && !c.isVndkExt() {
-			if c.isVndkSp() {
-				vndksp = append(vndksp, filename)
-			} else {
-				vndkcore = append(vndkcore, filename)
-			}
-			if c.isVndkPrivate(config) {
-				vndkprivate = append(vndkprivate, filename)
-			}
-			if ctx.DeviceConfig().VndkUseCoreVariant() && !c.MustUseVendorVariant() {
-				vndkcorevariant = append(vndkcorevariant, filename)
-			}
-		}
-	})
-	llndk = android.SortedUniqueStrings(llndk)
-	vndkcore = android.SortedUniqueStrings(vndkcore)
-	vndksp = android.SortedUniqueStrings(vndksp)
-	vndkprivate = android.SortedUniqueStrings(vndkprivate)
-	vndkcorevariant = android.SortedUniqueStrings(vndkcorevariant)
+	c.installedLlndkLibraries = android.SortedStringKeys(installedLlndkLibraries)
 
-	installListFile(ctx, llndk, "vndk", "llndk.libraries.txt")
-	installListFile(ctx, vndkcore, "vndk", "vndkcore.libraries.txt")
-	installListFile(ctx, vndksp, "vndk", "vndksp.libraries.txt")
-	installListFile(ctx, vndkprivate, "vndk", "vndkprivate.libraries.txt")
-	installListFile(ctx, vndkcorevariant, "vndk", "vndkcorevariant.libraries.txt")
+	llndk := android.SortedStringMapValues(installedLlndkLibraries)
+	vndkcore := android.SortedStringMapValues(vndkCoreLibraries(ctx.Config()))
+	vndksp := android.SortedStringMapValues(vndkSpLibraries(ctx.Config()))
+	vndkprivate := android.SortedStringMapValues(vndkPrivateLibraries(ctx.Config()))
+	vndkcorevariant := android.SortedStringMapValues(vndkUsingCoreVariantLibraries(ctx.Config()))
+
+	c.llnkdLibrariesFile = installListFile(llndk, "llndk.libraries.txt")
+	c.vndkCoreLibrariesFile = installListFile(vndkcore, "vndkcore.libraries.txt")
+	c.vndkSpLibrariesFile = installListFile(vndksp, "vndksp.libraries.txt")
+	c.vndkPrivateLibrariesFile = installListFile(vndkprivate, "vndkprivate.libraries.txt")
+	c.vndkCoreVariantLibrariesFile = installListFile(vndkcorevariant, "vndkcorevariant.libraries.txt")
 
 	// merged & tagged & filtered-out(libclang_rt)
+	// Since each target have different set of libclang_rt.* files,
+	// keep the common set of files in vndk.libraries.txt
+	var merged []string
 	filterOutLibClangRt := func(libList []string) (filtered []string) {
 		for _, lib := range libList {
 			if !strings.HasPrefix(lib, "libclang_rt.") {
@@ -741,6 +726,31 @@
 	merged = append(merged, addPrefix(vndksp, "VNDK-SP: ")...)
 	merged = append(merged, addPrefix(filterOutLibClangRt(vndkcore), "VNDK-core: ")...)
 	merged = append(merged, addPrefix(vndkprivate, "VNDK-private: ")...)
+	c.vndkLibrariesFile = installListFile(merged, "vndk.libraries.txt")
+}
 
-	installListFile(ctx, merged, "vndk", "vndk.libraries.txt")
+func (c *vndkSnapshotSingleton) MakeVars(ctx android.MakeVarsContext) {
+	// Make uses LLNDK_MOVED_TO_APEX_LIBRARIES to avoid installing libraries on /system if
+	// they been moved to an apex.
+	movedToApexLlndkLibraries := []string{}
+	for _, lib := range c.installedLlndkLibraries {
+		// Skip bionic libs, they are handled in different manner
+		if android.DirectlyInAnyApex(&notOnHostContext{}, lib) && !isBionic(lib) {
+			movedToApexLlndkLibraries = append(movedToApexLlndkLibraries, lib)
+		}
+	}
+	ctx.Strict("LLNDK_MOVED_TO_APEX_LIBRARIES", strings.Join(movedToApexLlndkLibraries, " "))
+	ctx.Strict("LLNDK_LIBRARIES", strings.Join(c.installedLlndkLibraries, " "))
+	ctx.Strict("VNDK_CORE_LIBRARIES", strings.Join(android.SortedStringKeys(vndkCoreLibraries(ctx.Config())), " "))
+	ctx.Strict("VNDK_SAMEPROCESS_LIBRARIES", strings.Join(android.SortedStringKeys(vndkSpLibraries(ctx.Config())), " "))
+	ctx.Strict("VNDK_PRIVATE_LIBRARIES", strings.Join(android.SortedStringKeys(vndkPrivateLibraries(ctx.Config())), " "))
+	ctx.Strict("VNDK_USING_CORE_VARIANT_LIBRARIES", strings.Join(android.SortedStringKeys(vndkUsingCoreVariantLibraries(ctx.Config())), " "))
+
+	ctx.Strict("LLNDK_LIBRARIES_FILE", c.llnkdLibrariesFile.String())
+	ctx.Strict("VNDKCORE_LIBRARIES_FILE", c.vndkCoreLibrariesFile.String())
+	ctx.Strict("VNDKSP_LIBRARIES_FILE", c.vndkSpLibrariesFile.String())
+	ctx.Strict("VNDKPRIVATE_LIBRARIES_FILE", c.vndkPrivateLibrariesFile.String())
+	ctx.Strict("VNDKCOREVARIANT_LIBRARIES_FILE", c.vndkCoreVariantLibrariesFile.String())
+
+	ctx.Strict("VNDK_LIBRARIES_FILE", c.vndkLibrariesFile.String())
 }
diff --git a/rust/androidmk.go b/rust/androidmk.go
index f933cfb..49115f2 100644
--- a/rust/androidmk.go
+++ b/rust/androidmk.go
@@ -72,6 +72,8 @@
 
 	mod.subAndroidMk(&ret, mod.compiler)
 
+	ret.SubName += mod.Properties.SubName
+
 	return ret
 }
 
@@ -85,6 +87,11 @@
 	})
 }
 
+func (test *testBinaryDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
+	test.binaryDecorator.AndroidMk(ctx, ret)
+	ret.SubName = "_" + String(test.baseCompiler.Properties.Stem)
+}
+
 func (library *libraryDecorator) AndroidMk(ctx AndroidMkContext, ret *android.AndroidMkData) {
 	ctx.subAndroidMk(ret, library.baseCompiler)
 
diff --git a/rust/binary_test.go b/rust/binary_test.go
index cd41fcf..ab2dae1 100644
--- a/rust/binary_test.go
+++ b/rust/binary_test.go
@@ -36,11 +36,20 @@
 	fizzBuzz := ctx.ModuleForTests("fizz-buzz", "linux_glibc_x86_64").Output("fizz-buzz")
 	fizzBuzzDynamic := ctx.ModuleForTests("fizz-buzz-dynamic", "linux_glibc_x86_64").Output("fizz-buzz-dynamic")
 
-	if !strings.Contains(fizzBuzzDynamic.Args["rustcFlags"], "prefer-dynamic") {
-		t.Errorf("missing prefer-dynamic flag, rustcFlags: %#v", fizzBuzzDynamic.Args["rustcFlags"])
+	// Do not compile binary modules with the --test flag.
+	flags := fizzBuzzDynamic.Args["rustcFlags"]
+	if strings.Contains(flags, "--test") {
+		t.Errorf("extra --test flag, rustcFlags: %#v", flags)
+	}
+	if !strings.Contains(flags, "prefer-dynamic") {
+		t.Errorf("missing prefer-dynamic flag, rustcFlags: %#v", flags)
 	}
 
-	if strings.Contains(fizzBuzz.Args["rustcFlags"], "prefer-dynamic") {
-		t.Errorf("unexpected prefer-dynamic flag, rustcFlags: %#v", fizzBuzz.Args["rustcFlags"])
+	flags = fizzBuzz.Args["rustcFlags"]
+	if strings.Contains(flags, "--test") {
+		t.Errorf("extra --test flag, rustcFlags: %#v", flags)
+	}
+	if strings.Contains(flags, "prefer-dynamic") {
+		t.Errorf("unexpected prefer-dynamic flag, rustcFlags: %#v", flags)
 	}
 }
diff --git a/rust/rust.go b/rust/rust.go
index ce81b91..ec3b590 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -39,6 +39,7 @@
 	android.RegisterModuleType("rust_defaults", defaultsFactory)
 	android.PreDepsMutators(func(ctx android.RegisterMutatorsContext) {
 		ctx.BottomUp("rust_libraries", LibraryMutator).Parallel()
+		ctx.BottomUp("rust_unit_tests", TestPerSrcMutator).Parallel()
 	})
 	pctx.Import("android/soong/rust/config")
 }
@@ -58,6 +59,7 @@
 	AndroidMkProcMacroLibs []string
 	AndroidMkSharedLibs    []string
 	AndroidMkStaticLibs    []string
+	SubName                string `blueprint:"mutated"`
 }
 
 type Module struct {
@@ -495,9 +497,10 @@
 }
 
 var (
-	rlibDepTag      = dependencyTag{name: "rlibTag", library: true}
-	dylibDepTag     = dependencyTag{name: "dylib", library: true}
-	procMacroDepTag = dependencyTag{name: "procMacro", proc_macro: true}
+	rlibDepTag       = dependencyTag{name: "rlibTag", library: true}
+	dylibDepTag      = dependencyTag{name: "dylib", library: true}
+	procMacroDepTag  = dependencyTag{name: "procMacro", proc_macro: true}
+	testPerSrcDepTag = dependencyTag{name: "rust_unit_tests"}
 )
 
 func (mod *Module) depsToPaths(ctx android.ModuleContext) PathDeps {
diff --git a/rust/test.go b/rust/test.go
new file mode 100644
index 0000000..816e3c7
--- /dev/null
+++ b/rust/test.go
@@ -0,0 +1,116 @@
+// Copyright 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rust
+
+import (
+	"path/filepath"
+	"strings"
+
+	"android/soong/android"
+)
+
+// A test module is a binary module with extra --test compiler flag
+// and different default installation directory.
+// In golang, inheriance is written as a component.
+type testBinaryDecorator struct {
+	*binaryDecorator
+}
+
+func NewRustTest(hod android.HostOrDeviceSupported) (*Module, *testBinaryDecorator) {
+	module := newModule(hod, android.MultilibFirst)
+
+	test := &testBinaryDecorator{
+		binaryDecorator: &binaryDecorator{
+			// TODO(chh): set up dir64?
+			baseCompiler: NewBaseCompiler("testcases", ""),
+		},
+	}
+
+	module.compiler = test
+
+	return module, test
+}
+
+func (test *testBinaryDecorator) compilerFlags(ctx ModuleContext, flags Flags) Flags {
+	flags = test.binaryDecorator.compilerFlags(ctx, flags)
+	flags.RustFlags = append(flags.RustFlags, "--test")
+	return flags
+}
+
+func init() {
+	// Rust tests are binary files built with --test.
+	android.RegisterModuleType("rust_test", RustTestFactory)
+	android.RegisterModuleType("rust_test_host", RustTestHostFactory)
+}
+
+func RustTestFactory() android.Module {
+	module, _ := NewRustTest(android.HostAndDeviceSupported)
+	return module.Init()
+}
+
+func RustTestHostFactory() android.Module {
+	module, _ := NewRustTest(android.HostSupported)
+	return module.Init()
+}
+
+func (test *testBinaryDecorator) testPerSrc() bool {
+	return true
+}
+
+func (test *testBinaryDecorator) srcs() []string {
+	return test.Properties.Srcs
+}
+
+func (test *testBinaryDecorator) setSrc(name, src string) {
+	test.Properties.Srcs = []string{src}
+	test.baseCompiler.Properties.Stem = StringPtr(name)
+}
+
+func (test *testBinaryDecorator) unsetSrc() {
+	test.Properties.Srcs = nil
+	test.baseCompiler.Properties.Stem = StringPtr("")
+}
+
+type testPerSrc interface {
+	testPerSrc() bool
+	srcs() []string
+	setSrc(string, string)
+	unsetSrc()
+}
+
+var _ testPerSrc = (*testBinaryDecorator)(nil)
+
+func TestPerSrcMutator(mctx android.BottomUpMutatorContext) {
+	if m, ok := mctx.Module().(*Module); ok {
+		if test, ok := m.compiler.(testPerSrc); ok {
+			numTests := len(test.srcs())
+			if test.testPerSrc() && numTests > 0 {
+				if duplicate, found := android.CheckDuplicate(test.srcs()); found {
+					mctx.PropertyErrorf("srcs", "found a duplicate entry %q", duplicate)
+					return
+				}
+				testNames := make([]string, numTests)
+				for i, src := range test.srcs() {
+					testNames[i] = strings.TrimSuffix(filepath.Base(src), filepath.Ext(src))
+				}
+				// TODO(chh): Add an "all tests" variation like cc/test.go?
+				tests := mctx.CreateLocalVariations(testNames...)
+				for i, src := range test.srcs() {
+					tests[i].(*Module).compiler.(testPerSrc).setSrc(testNames[i], src)
+				}
+			}
+		}
+	}
+}
diff --git a/rust/test_test.go b/rust/test_test.go
new file mode 100644
index 0000000..aa4c3c8
--- /dev/null
+++ b/rust/test_test.go
@@ -0,0 +1,43 @@
+// Copyright 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package rust
+
+import (
+	"strings"
+	"testing"
+)
+
+// Check if rust_test_host accepts multiple source files and applies --test flag.
+func TestRustTest(t *testing.T) {
+	ctx := testRust(t, `
+		rust_test_host {
+			name: "my_test",
+			srcs: ["foo.rs", "src/bar.rs"],
+			relative_install_path: "rust/my-test",
+		}`)
+
+	for _, name := range []string{"foo", "bar"} {
+		testingModule := ctx.ModuleForTests("my_test", "linux_glibc_x86_64_"+name)
+		testingBuildParams := testingModule.Output(name)
+		rustcFlags := testingBuildParams.Args["rustcFlags"]
+		if !strings.Contains(rustcFlags, "--test") {
+			t.Errorf("%v missing --test flag, rustcFlags: %#v", name, rustcFlags)
+		}
+		outPath := "/my_test/linux_glibc_x86_64_" + name + "/" + name
+		if !strings.Contains(testingBuildParams.Output.String(), outPath) {
+			t.Errorf("wrong output: %v  expect: %v", testingBuildParams.Output, outPath)
+		}
+	}
+}
diff --git a/rust/testing.go b/rust/testing.go
index cd63084..24defa6 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -168,6 +168,8 @@
 	ctx.RegisterModuleType("cc_object", android.ModuleFactoryAdaptor(cc.ObjectFactory))
 	ctx.RegisterModuleType("rust_binary", android.ModuleFactoryAdaptor(RustBinaryFactory))
 	ctx.RegisterModuleType("rust_binary_host", android.ModuleFactoryAdaptor(RustBinaryHostFactory))
+	ctx.RegisterModuleType("rust_test", android.ModuleFactoryAdaptor(RustTestFactory))
+	ctx.RegisterModuleType("rust_test_host", android.ModuleFactoryAdaptor(RustTestHostFactory))
 	ctx.RegisterModuleType("rust_library", android.ModuleFactoryAdaptor(RustLibraryFactory))
 	ctx.RegisterModuleType("rust_library_host", android.ModuleFactoryAdaptor(RustLibraryHostFactory))
 	ctx.RegisterModuleType("rust_library_host_rlib", android.ModuleFactoryAdaptor(RustLibraryRlibHostFactory))
@@ -190,6 +192,7 @@
 
 		// rust mutators
 		ctx.BottomUp("rust_libraries", LibraryMutator).Parallel()
+		ctx.BottomUp("rust_unit_tests", TestPerSrcMutator).Parallel()
 	})
 	bp = bp + GatherRequiredDepsForTest()
 
diff --git a/sdk/sdk.go b/sdk/sdk.go
index d189043..002fb5d 100644
--- a/sdk/sdk.go
+++ b/sdk/sdk.go
@@ -24,6 +24,7 @@
 	// This package doesn't depend on the apex package, but import it to make its mutators to be
 	// registered before mutators in this package. See RegisterPostDepsMutators for more details.
 	_ "android/soong/apex"
+	"android/soong/cc"
 )
 
 func init() {
@@ -148,10 +149,17 @@
 
 		targets := mctx.MultiTargets()
 		for _, target := range targets {
-			mctx.AddFarVariationDependencies(append(target.Variations(), []blueprint.Variation{
-				{Mutator: "image", Variation: "core"},
-				{Mutator: "link", Variation: "shared"},
-			}...), sdkMemberDepTag, m.properties.Native_shared_libs...)
+			for _, lib := range m.properties.Native_shared_libs {
+				name, version := cc.StubsLibNameAndVersion(lib)
+				if version == "" {
+					version = cc.LatestStubsVersionFor(mctx.Config(), name)
+				}
+				mctx.AddFarVariationDependencies(append(target.Variations(), []blueprint.Variation{
+					{Mutator: "image", Variation: "core"},
+					{Mutator: "link", Variation: "shared"},
+					{Mutator: "version", Variation: version},
+				}...), sdkMemberDepTag, name)
+			}
 		}
 	}
 }
diff --git a/sdk/update.go b/sdk/update.go
index 5235c9e..ce60827 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -24,6 +24,7 @@
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
+	"android/soong/cc"
 	"android/soong/java"
 )
 
@@ -32,21 +33,31 @@
 // generatedFile abstracts operations for writing contents into a file and emit a build rule
 // for the file.
 type generatedFile struct {
-	path    android.OutputPath
-	content strings.Builder
+	path        android.OutputPath
+	content     strings.Builder
+	indentLevel int
 }
 
 func newGeneratedFile(ctx android.ModuleContext, name string) *generatedFile {
 	return &generatedFile{
-		path: android.PathForModuleOut(ctx, name).OutputPath,
+		path:        android.PathForModuleOut(ctx, name).OutputPath,
+		indentLevel: 0,
 	}
 }
 
+func (gf *generatedFile) indent() {
+	gf.indentLevel++
+}
+
+func (gf *generatedFile) dedent() {
+	gf.indentLevel--
+}
+
 func (gf *generatedFile) printfln(format string, args ...interface{}) {
 	// ninja consumes newline characters in rspfile_content. Prevent it by
 	// escaping the backslash in the newline character. The extra backshash
 	// is removed when the rspfile is written to the actual script file
-	fmt.Fprintf(&(gf.content), format+"\\n", args...)
+	fmt.Fprintf(&(gf.content), strings.Repeat("    ", gf.indentLevel)+format+"\\n", args...)
 }
 
 func (gf *generatedFile) build(pctx android.PackageContext, ctx android.BuilderContext, implicits android.Paths) {
@@ -61,34 +72,187 @@
 	rb.Build(pctx, ctx, gf.path.Base(), "Build "+gf.path.Base())
 }
 
-func (s *sdk) javaMemberNames(ctx android.ModuleContext) []string {
-	result := []string{}
+func (s *sdk) javaLibs(ctx android.ModuleContext) []*java.Library {
+	result := []*java.Library{}
 	ctx.VisitDirectDeps(func(m android.Module) {
-		if _, ok := m.(*java.Library); ok {
-			result = append(result, m.Name())
+		if j, ok := m.(*java.Library); ok {
+			result = append(result, j)
 		}
 	})
 	return result
 }
 
-// buildAndroidBp creates the blueprint file that defines prebuilt modules for each of
-// the SDK members, and the sdk_snapshot module for the specified version
-func (s *sdk) buildAndroidBp(ctx android.ModuleContext, version string) android.OutputPath {
-	bp := newGeneratedFile(ctx, "blueprint-"+version+".sh")
+// archSpecificNativeLibInfo represents an arch-specific variant of a native lib
+type archSpecificNativeLibInfo struct {
+	name                      string
+	archType                  string
+	exportedIncludeDirs       android.Paths
+	exportedSystemIncludeDirs android.Paths
+	exportedFlags             []string
+	outputFile                android.Path
+}
 
-	makePrebuiltName := func(name string) string {
-		return ctx.ModuleName() + "_" + name + string(android.SdkVersionSeparator) + version
+func (lib *archSpecificNativeLibInfo) signature() string {
+	return fmt.Sprintf("%v %v %v %v",
+		lib.name,
+		lib.exportedIncludeDirs.Strings(),
+		lib.exportedSystemIncludeDirs.Strings(),
+		lib.exportedFlags)
+}
+
+// nativeLibInfo represents a collection of arch-specific modules having the same name
+type nativeLibInfo struct {
+	name         string
+	archVariants []archSpecificNativeLibInfo
+	// hasArchSpecificFlags is set to true if modules for each architecture all have the same
+	// include dirs, flags, etc, in which case only those of the first arch is selected.
+	hasArchSpecificFlags bool
+}
+
+// nativeMemberInfos collects all cc.Modules that are member of an SDK.
+func (s *sdk) nativeMemberInfos(ctx android.ModuleContext) []*nativeLibInfo {
+	infoMap := make(map[string]*nativeLibInfo)
+
+	// Collect cc.Modules
+	ctx.VisitDirectDeps(func(m android.Module) {
+		ccModule, ok := m.(*cc.Module)
+		if !ok {
+			return
+		}
+		depName := ctx.OtherModuleName(m)
+
+		if _, ok := infoMap[depName]; !ok {
+			infoMap[depName] = &nativeLibInfo{name: depName}
+		}
+
+		info := infoMap[depName]
+		info.archVariants = append(info.archVariants, archSpecificNativeLibInfo{
+			name:                      ccModule.BaseModuleName(),
+			archType:                  ccModule.Target().Arch.ArchType.String(),
+			exportedIncludeDirs:       ccModule.ExportedIncludeDirs(),
+			exportedSystemIncludeDirs: ccModule.ExportedSystemIncludeDirs(),
+			exportedFlags:             ccModule.ExportedFlags(),
+			outputFile:                ccModule.OutputFile().Path(),
+		})
+	})
+
+	// Determine if include dirs and flags for each module are different across arch-specific
+	// modules or not. And set hasArchSpecificFlags accordingly
+	for _, info := range infoMap {
+		// by default, include paths and flags are assumed to be the same across arches
+		info.hasArchSpecificFlags = false
+		oldSignature := ""
+		for _, av := range info.archVariants {
+			newSignature := av.signature()
+			if oldSignature == "" {
+				oldSignature = newSignature
+			}
+			if oldSignature != newSignature {
+				info.hasArchSpecificFlags = true
+				break
+			}
+		}
 	}
 
-	javaLibs := s.javaMemberNames(ctx)
-	for _, name := range javaLibs {
-		prebuiltName := makePrebuiltName(name)
-		jar := filepath.Join("java", name, "stub.jar")
+	var list []*nativeLibInfo
+	for _, v := range infoMap {
+		list = append(list, v)
+	}
+	return list
+}
 
+// SDK directory structure
+// <sdk_root>/
+//     Android.bp   : definition of a 'sdk' module is here. This is a hand-made one.
+//     <api_ver>/   : below this directory are all auto-generated
+//         Android.bp   : definition of 'sdk_snapshot' module is here
+//         aidl/
+//            frameworks/base/core/..../IFoo.aidl   : an exported AIDL file
+//         java/
+//            java/<module_name>/stub.jar    : a stub jar for a java library 'module_name'
+//         include/
+//            bionic/libc/include/stdlib.h   : an exported header file
+//         include_gen/
+//            com/android/.../IFoo.h : a generated header file
+//         <arch>/include/   : arch-specific exported headers
+//         <arch>/include_gen/   : arch-specific generated headers
+//         <arch>/lib/
+//            libFoo.so   : a stub library
+
+const (
+	aidlIncludeDir            = "aidl"
+	javaStubDir               = "java"
+	javaStubFile              = "stub.jar"
+	nativeIncludeDir          = "include"
+	nativeGeneratedIncludeDir = "include_gen"
+	nativeStubDir             = "lib"
+	nativeStubFileSuffix      = ".so"
+)
+
+// path to the stub file of a java library. Relative to <sdk_root>/<api_dir>
+func javaStubFilePathFor(javaLib *java.Library) string {
+	return filepath.Join(javaStubDir, javaLib.Name(), javaStubFile)
+}
+
+// path to the stub file of a native shared library. Relative to <sdk_root>/<api_dir>
+func nativeStubFilePathFor(lib archSpecificNativeLibInfo) string {
+	return filepath.Join(lib.archType,
+		nativeStubDir, lib.name+nativeStubFileSuffix)
+}
+
+// paths to the include dirs of a native shared library. Relative to <sdk_root>/<api_dir>
+func nativeIncludeDirPathsFor(ctx android.ModuleContext, lib archSpecificNativeLibInfo,
+	systemInclude bool, archSpecific bool) []string {
+	var result []string
+	buildDir := ctx.Config().BuildDir()
+	var includeDirs []android.Path
+	if !systemInclude {
+		includeDirs = lib.exportedIncludeDirs
+	} else {
+		includeDirs = lib.exportedSystemIncludeDirs
+	}
+	for _, dir := range includeDirs {
+		var path string
+		if gen := strings.HasPrefix(dir.String(), buildDir); gen {
+			path = filepath.Join(nativeGeneratedIncludeDir, dir.Rel())
+		} else {
+			path = filepath.Join(nativeIncludeDir, dir.String())
+		}
+		if archSpecific {
+			path = filepath.Join(lib.archType, path)
+		}
+		result = append(result, path)
+	}
+	return result
+}
+
+// A name that uniquely identifies an prebuilt SDK member for a version of SDK snapshot
+// This isn't visible to users, so could be changed in future.
+func versionedSdkMemberName(ctx android.ModuleContext, memberName string, version string) string {
+	return ctx.ModuleName() + "_" + memberName + string(android.SdkVersionSeparator) + version
+}
+
+// arm64, arm, x86, x86_64, etc.
+func archTypeOf(module android.Module) string {
+	return module.Target().Arch.ArchType.String()
+}
+
+// buildAndroidBp creates the blueprint file that defines prebuilt modules for each of
+// the SDK members, and the entire sdk_snapshot module for the specified version
+func (s *sdk) buildAndroidBp(ctx android.ModuleContext, version string) android.OutputPath {
+	bp := newGeneratedFile(ctx, "blueprint-"+version+".bp")
+	bp.printfln("// This is auto-generated. DO NOT EDIT.")
+	bp.printfln("")
+
+	javaLibModules := s.javaLibs(ctx)
+	for _, m := range javaLibModules {
+		name := m.Name()
 		bp.printfln("java_import {")
-		bp.printfln("    name: %q,", prebuiltName)
-		bp.printfln("    jars: [%q],", jar)
-		bp.printfln("    sdk_member_name: %q,", name)
+		bp.indent()
+		bp.printfln("name: %q,", versionedSdkMemberName(ctx, name, version))
+		bp.printfln("sdk_member_name: %q,", name)
+		bp.printfln("jars: [%q],", javaStubFilePathFor(m))
+		bp.dedent()
 		bp.printfln("}")
 		bp.printfln("")
 
@@ -96,25 +260,92 @@
 		// doesn't exist (i.e. building in an unbundled tree). "prefer:" is set to false
 		// so that this module does not eclipse the unversioned module if it exists.
 		bp.printfln("java_import {")
-		bp.printfln("    name: %q,", name)
-		bp.printfln("    jars: [%q],", jar)
-		bp.printfln("    prefer: false,")
+		bp.indent()
+		bp.printfln("name: %q,", name)
+		bp.printfln("jars: [%q],", javaStubFilePathFor(m))
+		bp.printfln("prefer: false,")
+		bp.dedent()
 		bp.printfln("}")
 		bp.printfln("")
-
 	}
 
-	// TODO(jiyong): emit cc_prebuilt_library_shared for the native libs
+	nativeLibInfos := s.nativeMemberInfos(ctx)
+	for _, info := range nativeLibInfos {
+		bp.printfln("cc_prebuilt_library_shared {")
+		bp.indent()
+		bp.printfln("name: %q,", versionedSdkMemberName(ctx, info.name, version))
+		bp.printfln("sdk_member_name: %q,", info.name)
+
+		// a function for emitting include dirs
+		printExportedDirsForNativeLibs := func(lib archSpecificNativeLibInfo, systemInclude bool) {
+			includeDirs := nativeIncludeDirPathsFor(ctx, lib, systemInclude, info.hasArchSpecificFlags)
+			if len(includeDirs) == 0 {
+				return
+			}
+			if !systemInclude {
+				bp.printfln("export_include_dirs: [")
+			} else {
+				bp.printfln("export_system_include_dirs: [")
+			}
+			bp.indent()
+			for _, dir := range includeDirs {
+				bp.printfln("%q,", dir)
+			}
+			bp.dedent()
+			bp.printfln("],")
+		}
+
+		if !info.hasArchSpecificFlags {
+			printExportedDirsForNativeLibs(info.archVariants[0], false /*systemInclude*/)
+			printExportedDirsForNativeLibs(info.archVariants[0], true /*systemInclude*/)
+		}
+
+		bp.printfln("arch: {")
+		bp.indent()
+		for _, av := range info.archVariants {
+			bp.printfln("%s: {", av.archType)
+			bp.indent()
+			bp.printfln("srcs: [%q],", nativeStubFilePathFor(av))
+			if info.hasArchSpecificFlags {
+				// export_* properties are added inside the arch: {<arch>: {...}} block
+				printExportedDirsForNativeLibs(av, false /*systemInclude*/)
+				printExportedDirsForNativeLibs(av, true /*systemInclude*/)
+			}
+			bp.dedent()
+			bp.printfln("},") // <arch>
+		}
+		bp.dedent()
+		bp.printfln("},") // arch
+		bp.printfln("stl: \"none\",")
+		bp.printfln("system_shared_libs: [],")
+		bp.dedent()
+		bp.printfln("}") // cc_prebuilt_library_shared
+		bp.printfln("")
+	}
 
 	bp.printfln("sdk_snapshot {")
-	bp.printfln("    name: %q,", ctx.ModuleName()+string(android.SdkVersionSeparator)+version)
-	bp.printfln("    java_libs: [")
-	for _, n := range javaLibs {
-		bp.printfln("        %q,", makePrebuiltName(n))
+	bp.indent()
+	bp.printfln("name: %q,", ctx.ModuleName()+string(android.SdkVersionSeparator)+version)
+	if len(javaLibModules) > 0 {
+		bp.printfln("java_libs: [")
+		bp.indent()
+		for _, m := range javaLibModules {
+			bp.printfln("%q,", versionedSdkMemberName(ctx, m.Name(), version))
+		}
+		bp.dedent()
+		bp.printfln("],") // java_libs
 	}
-	bp.printfln("    ],")
-	// TODO(jiyong): emit native_shared_libs
-	bp.printfln("}")
+	if len(nativeLibInfos) > 0 {
+		bp.printfln("native_shared_libs: [")
+		bp.indent()
+		for _, info := range nativeLibInfos {
+			bp.printfln("%q,", versionedSdkMemberName(ctx, info.name, version))
+		}
+		bp.dedent()
+		bp.printfln("],") // native_shared_libs
+	}
+	bp.dedent()
+	bp.printfln("}") // sdk_snapshot
 	bp.printfln("")
 
 	bp.build(pctx, ctx, nil)
@@ -123,46 +354,104 @@
 
 func (s *sdk) buildScript(ctx android.ModuleContext, version string) android.OutputPath {
 	sh := newGeneratedFile(ctx, "update_prebuilt-"+version+".sh")
+	buildDir := ctx.Config().BuildDir()
 
-	snapshotRoot := filepath.Join(ctx.ModuleDir(), version)
-	aidlIncludeDir := filepath.Join(snapshotRoot, "aidl")
-	javaStubsDir := filepath.Join(snapshotRoot, "java")
+	snapshotPath := func(paths ...string) string {
+		return filepath.Join(ctx.ModuleDir(), version, filepath.Join(paths...))
+	}
 
+	// TODO(jiyong) instead of creating script, create a zip file having the Android.bp, the headers,
+	// and the stubs and put it to the dist directory. The dist'ed zip file then would be downloaded,
+	// unzipped and then uploaded to gerrit again.
 	sh.printfln("#!/bin/bash")
-	sh.printfln("echo Updating snapshot of %s in %s", ctx.ModuleName(), snapshotRoot)
+	sh.printfln("echo Updating snapshot of %s in %s", ctx.ModuleName(), snapshotPath())
 	sh.printfln("pushd $ANDROID_BUILD_TOP > /dev/null")
-	sh.printfln("rm -rf %s", snapshotRoot)
-	sh.printfln("mkdir -p %s", aidlIncludeDir)
-	sh.printfln("mkdir -p %s", javaStubsDir)
-	// TODO(jiyong): mkdir the 'native' dir
+	sh.printfln("mkdir -p %s", snapshotPath(aidlIncludeDir))
+	sh.printfln("mkdir -p %s", snapshotPath(javaStubDir))
+	sh.printfln("mkdir -p %s", snapshotPath(nativeIncludeDir))
+	sh.printfln("mkdir -p %s", snapshotPath(nativeGeneratedIncludeDir))
+	for _, target := range ctx.MultiTargets() {
+		arch := target.Arch.ArchType.String()
+		sh.printfln("mkdir -p %s", snapshotPath(arch, nativeStubDir))
+		sh.printfln("mkdir -p %s", snapshotPath(arch, nativeIncludeDir))
+		sh.printfln("mkdir -p %s", snapshotPath(arch, nativeGeneratedIncludeDir))
+	}
 
 	var implicits android.Paths
-	ctx.VisitDirectDeps(func(m android.Module) {
-		if javaLib, ok := m.(*java.Library); ok {
-			headerJars := javaLib.HeaderJars()
-			if len(headerJars) != 1 {
-				panic(fmt.Errorf("there must be only one header jar from %q", m.Name()))
-			}
-			implicits = append(implicits, headerJars...)
-
-			exportedAidlIncludeDirs := javaLib.AidlIncludeDirs()
-			for _, dir := range exportedAidlIncludeDirs {
-				// Using tar to copy with the directory structure
-				// TODO(jiyong): copy parcelable declarations only
-				sh.printfln("find %s -name \"*.aidl\" | tar cf - -T - | (cd %s; tar xf -)",
-					dir.String(), aidlIncludeDir)
-			}
-
-			copiedHeaderJar := filepath.Join(javaStubsDir, m.Name(), "stub.jar")
-			sh.printfln("mkdir -p $(dirname %s) && cp %s %s",
-				copiedHeaderJar, headerJars[0].String(), copiedHeaderJar)
+	for _, m := range s.javaLibs(ctx) {
+		headerJars := m.HeaderJars()
+		if len(headerJars) != 1 {
+			panic(fmt.Errorf("there must be only one header jar from %q", m.Name()))
 		}
-		// TODO(jiyong): emit the commands for copying the headers and stub libraries for native libs
-	})
+		implicits = append(implicits, headerJars...)
+
+		exportedAidlIncludeDirs := m.AidlIncludeDirs()
+		for _, dir := range exportedAidlIncludeDirs {
+			// Using tar to copy with the directory structure
+			// TODO(jiyong): copy parcelable declarations only
+			sh.printfln("find %s -name \"*.aidl\" | tar cf - -T - | (cd %s; tar xf -)",
+				dir.String(), snapshotPath(aidlIncludeDir))
+		}
+
+		copyTarget := snapshotPath(javaStubFilePathFor(m))
+		sh.printfln("mkdir -p %s && cp %s %s",
+			filepath.Dir(copyTarget), headerJars[0].String(), copyTarget)
+	}
+
+	nativeLibInfos := s.nativeMemberInfos(ctx)
+	for _, info := range nativeLibInfos {
+
+		// a function for emitting include dirs
+		printExportedDirCopyCommandsForNativeLibs := func(lib archSpecificNativeLibInfo) {
+			includeDirs := lib.exportedIncludeDirs
+			includeDirs = append(includeDirs, lib.exportedSystemIncludeDirs...)
+			if len(includeDirs) == 0 {
+				return
+			}
+			for _, dir := range includeDirs {
+				gen := strings.HasPrefix(dir.String(), buildDir)
+				targetDir := nativeIncludeDir
+				if gen {
+					targetDir = nativeGeneratedIncludeDir
+				}
+				if info.hasArchSpecificFlags {
+					targetDir = filepath.Join(lib.archType, targetDir)
+				}
+				targetDir = snapshotPath(targetDir)
+
+				sourceDirRoot := "."
+				sourceDirRel := dir.String()
+				if gen {
+					// ex) out/soong/.intermediate/foo/bar/gen/aidl
+					sourceDirRoot = strings.TrimSuffix(dir.String(), dir.Rel())
+					sourceDirRel = dir.Rel()
+				}
+				// TODO(jiyong) copy headers having other suffixes
+				sh.printfln("(cd %s; find %s -name \"*.h\" | tar cf - -T - ) | (cd %s; tar xf -)",
+					sourceDirRoot, sourceDirRel, targetDir)
+			}
+		}
+
+		if !info.hasArchSpecificFlags {
+			printExportedDirCopyCommandsForNativeLibs(info.archVariants[0])
+		}
+
+		// for each architecture
+		for _, av := range info.archVariants {
+			stub := av.outputFile
+			implicits = append(implicits, stub)
+			copiedStub := snapshotPath(nativeStubFilePathFor(av))
+			sh.printfln("cp %s %s", stub.String(), copiedStub)
+
+			if info.hasArchSpecificFlags {
+				printExportedDirCopyCommandsForNativeLibs(av)
+			}
+		}
+	}
 
 	bp := s.buildAndroidBp(ctx, version)
 	implicits = append(implicits, bp)
-	sh.printfln("cp %s %s", bp.String(), filepath.Join(snapshotRoot, "Android.bp"))
+	sh.printfln("cp %s %s", bp.String(), snapshotPath("Android.bp"))
 
 	sh.printfln("popd > /dev/null")
 	sh.printfln("rm -- \"$0\"") // self deleting so that stale script is not used
@@ -218,8 +507,8 @@
 				fmt.Fprintln(w, "$(LOCAL_BUILT_MODULE): $(LOCAL_ADDITIONAL_DEPENDENCIES)")
 				fmt.Fprintln(w, "	touch $@")
 				fmt.Fprintln(w, "	echo ##################################################")
-				fmt.Fprintln(w, "	echo To update current SDK: execute", s.updateScript.String())
-				fmt.Fprintln(w, "	echo To freeze current SDK: execute", s.freezeScript.String())
+				fmt.Fprintln(w, "	echo To update current SDK: execute", filepath.Join("\\$$ANDROID_BUILD_TOP", s.updateScript.String()))
+				fmt.Fprintln(w, "	echo To freeze current SDK: execute", filepath.Join("\\$$ANDROID_BUILD_TOP", s.freezeScript.String()))
 				fmt.Fprintln(w, "	echo ##################################################")
 			},
 		},