Merge "Create variants for each image type"
diff --git a/android/util.go b/android/util.go
index ddbe1cd..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 {
diff --git a/apex/apex.go b/apex/apex.go
index 4cbd762..4e6827f 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -352,6 +352,34 @@
 	}
 }
 
+var (
+	useVendorWhitelistKey = android.NewOnceKey("useVendorWhitelist")
+)
+
+// useVendorWhitelist returns the list of APEXes which are allowed to use_vendor.
+// When use_vendor is used, native modules are built with __ANDROID_VNDK__ and __ANDROID_APEX__,
+// which may cause compatibility issues. (e.g. libbinder)
+// Even though libbinder restricts its availability via 'apex_available' property and relies on
+// yet another macro __ANDROID_APEX_<NAME>__, we restrict usage of "use_vendor:" from other APEX modules
+// to avoid similar problems.
+func useVendorWhitelist(config android.Config) []string {
+	return config.Once(useVendorWhitelistKey, func() interface{} {
+		return []string{
+			// swcodec uses "vendor" variants for smaller size
+			"com.android.media.swcodec",
+			"test_com.android.media.swcodec",
+		}
+	}).([]string)
+}
+
+// setUseVendorWhitelistForTest overrides useVendorWhitelist and must be
+// called before the first call to useVendorWhitelist()
+func setUseVendorWhitelistForTest(config android.Config, whitelist []string) {
+	config.Once(useVendorWhitelistKey, func() interface{} {
+		return whitelist
+	})
+}
+
 type apexNativeDependencies struct {
 	// List of native libraries
 	Native_shared_libs []string
@@ -659,6 +687,10 @@
 }
 
 func (a *apexBundle) DepsMutator(ctx android.BottomUpMutatorContext) {
+	if proptools.Bool(a.properties.Use_vendor) && !android.InList(a.Name(), useVendorWhitelist(ctx.Config())) {
+		ctx.PropertyErrorf("use_vendor", "not allowed to set use_vendor: true")
+	}
+
 	targets := ctx.MultiTargets()
 	config := ctx.DeviceConfig()
 
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 91f664a..77c1fb0 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -818,8 +818,9 @@
 			name: "libbar",
 			symbol_file: "",
 		}
-
-	`)
+	`, func(fs map[string][]byte, config android.Config) {
+		setUseVendorWhitelistForTest(config, []string{"myapex"})
+	})
 
 	apexRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexRule")
 	copyCmds := apexRule.Args["copy_commands"]
@@ -1047,7 +1048,9 @@
 			vendor_available: true,
 			stl: "none",
 		}
-	`)
+	`, func(fs map[string][]byte, config android.Config) {
+		setUseVendorWhitelistForTest(config, []string{"myapex"})
+	})
 
 	inputsList := []string{}
 	for _, i := range ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().BuildParamsForTests() {
@@ -1066,6 +1069,38 @@
 	ensureNotContains(t, inputsString, "android_arm64_armv8-a_core_shared_myapex/mylib2.so")
 }
 
+func TestUseVendorRestriction(t *testing.T) {
+	testApexError(t, `module "myapex" .*: use_vendor: not allowed`, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			use_vendor: true,
+		}
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+	`, func(fs map[string][]byte, config android.Config) {
+		setUseVendorWhitelistForTest(config, []string{""})
+	})
+	// no error with whitelist
+	testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			use_vendor: true,
+		}
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+	`, func(fs map[string][]byte, config android.Config) {
+		setUseVendorWhitelistForTest(config, []string{"myapex"})
+	})
+}
+
 func TestUseVendorFailsIfNotVendorAvailable(t *testing.T) {
 	testApexError(t, `dependency "mylib" of "myapex" missing variant:\n.*image:vendor`, `
 		apex {
@@ -2353,7 +2388,9 @@
 			public_key: "testkey.avbpubkey",
 			private_key: "testkey.pem",
 		}
-	`)
+	`, func(fs map[string][]byte, config android.Config) {
+		setUseVendorWhitelistForTest(config, []string{"myapex"})
+	})
 }
 
 func TestErrorsIfDepsAreNotEnabled(t *testing.T) {
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/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/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/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 ##################################################")
 			},
 		},