Merge "Remove PLATFORM_VERSION_FUTURE_CODENAMES"
diff --git a/OWNERS b/OWNERS
index 4ae045d..e1db459 100644
--- a/OWNERS
+++ b/OWNERS
@@ -4,3 +4,4 @@
 per-file clang.go,global.go = srhines@google.com, chh@google.com, pirama@google.com, yikong@google.com
 per-file tidy.go = srhines@google.com, chh@google.com
 per-file lto.go,pgo.go = srhines@google.com, pirama@google.com, yikong@google.com
+per-file docs/map_files.md = danalbert@google.com, enh@google.com, jiyong@google.com
diff --git a/README.md b/README.md
index 3eac87b..8b028a8 100644
--- a/README.md
+++ b/README.md
@@ -421,6 +421,7 @@
     config_namespace: "acme",
     variables: ["board"],
     bool_variables: ["feature"],
+    value_variables: ["width"],
     properties: ["cflags", "srcs"],
 }
 
@@ -431,8 +432,9 @@
 ```
 
 This example describes a new `acme_cc_defaults` module type that extends the
-`cc_defaults` module type, with two additional conditionals based on variables
-`board` and `feature`, which can affect properties `cflags` and `srcs`.
+`cc_defaults` module type, with three additional conditionals based on
+variables `board`, `feature` and `width`, which can affect properties `cflags`
+and `srcs`.
 
 The values of the variables can be set from a product's `BoardConfig.mk` file:
 ```
@@ -443,6 +445,7 @@
 
 SOONG_CONFIG_acme_board := soc_a
 SOONG_CONFIG_acme_feature := true
+SOONG_CONFIG_acme_width := 200
 ```
 
 The `acme_cc_defaults` module type can be used anywhere after the definition in
@@ -471,6 +474,9 @@
         feature: {
             cflags: ["-DFEATURE"],
         },
+        width: {
+            cflags: ["-DWIDTH=%s"],
+        },
     },
 }
 
@@ -482,7 +488,7 @@
 ```
 
 With the `BoardConfig.mk` snippet above, libacme_foo would build with
-cflags "-DGENERIC -DSOC_A -DFEATURE".
+cflags "-DGENERIC -DSOC_A -DFEATURE -DWIDTH=200".
 
 `soong_config_module_type` modules will work best when used to wrap defaults
 modules (`cc_defaults`, `java_defaults`, etc.), which can then be referenced
diff --git a/android/neverallow.go b/android/neverallow.go
index 547230c..04ec27d 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -17,6 +17,7 @@
 import (
 	"path/filepath"
 	"reflect"
+	"regexp"
 	"strconv"
 	"strings"
 
@@ -147,7 +148,8 @@
 	rules := []Rule{
 		NeverAllow().
 			NotIn(coreLibraryProjects...).
-			With("sdk_version", "none"),
+			With("sdk_version", "none").
+			WithoutMatcher("name", Regexp("^android_.*stubs_current$")),
 	}
 
 	return rules
@@ -286,6 +288,18 @@
 	return ".starts-with(" + m.prefix + ")"
 }
 
+type regexMatcher struct {
+	re *regexp.Regexp
+}
+
+func (m *regexMatcher) test(value string) bool {
+	return m.re.MatchString(value)
+}
+
+func (m *regexMatcher) String() string {
+	return ".regexp(" + m.re.String() + ")"
+}
+
 type isSetMatcher struct{}
 
 func (m *isSetMatcher) test(value string) bool {
@@ -501,6 +515,14 @@
 	return &startsWithMatcher{prefix}
 }
 
+func Regexp(re string) ValueMatcher {
+	r, err := regexp.Compile(re)
+	if err != nil {
+		panic(err)
+	}
+	return &regexMatcher{r}
+}
+
 // assorted utils
 
 func cleanPaths(paths []string) []string {
diff --git a/android/neverallow_test.go b/android/neverallow_test.go
index 41e5c6f..0373b79 100644
--- a/android/neverallow_test.go
+++ b/android/neverallow_test.go
@@ -227,6 +227,16 @@
 		},
 	},
 	{
+		name: "sdk_version: \"none\" on android_*stubs_current stub",
+		fs: map[string][]byte{
+			"frameworks/base/Android.bp": []byte(`
+				java_library {
+					name: "android_stubs_current",
+					sdk_version: "none",
+				}`),
+		},
+	},
+	{
 		name: "sdk_version: \"none\" outside core libraries",
 		fs: map[string][]byte{
 			"Android.bp": []byte(`
diff --git a/android/soong_config_modules.go b/android/soong_config_modules.go
index 6ce609a..619cf86 100644
--- a/android/soong_config_modules.go
+++ b/android/soong_config_modules.go
@@ -73,6 +73,9 @@
 //             feature: {
 //                 cflags: ["-DFEATURE"],
 //             },
+//             width: {
+//                 cflags: ["-DWIDTH=%s"],
+//             },
 //         },
 //     }
 //
@@ -90,6 +93,7 @@
 //         config_namespace: "acme",
 //         variables: ["board"],
 //         bool_variables: ["feature"],
+//         value_variables: ["width"],
 //         properties: ["cflags", "srcs"],
 //     }
 //
@@ -107,8 +111,9 @@
 //
 //     SOONG_CONFIG_acme_board := soc_a
 //     SOONG_CONFIG_acme_feature := true
+//     SOONG_CONFIG_acme_width := 200
 //
-// Then libacme_foo would build with cflags "-DGENERIC -DSOC_A -DFEATURE".
+// Then libacme_foo would build with cflags "-DGENERIC -DSOC_A -DFEATURE -DWIDTH=200".
 func soongConfigModuleTypeImportFactory() Module {
 	module := &soongConfigModuleTypeImport{}
 
@@ -151,6 +156,7 @@
 //         config_namespace: "acme",
 //         variables: ["board"],
 //         bool_variables: ["feature"],
+//         value_variables: ["width"],
 //         properties: ["cflags", "srcs"],
 //     }
 //
@@ -174,6 +180,9 @@
 //             feature: {
 //                 cflags: ["-DFEATURE"],
 //             },
+//             width: {
+//	               cflags: ["-DWIDTH=%s"],
+//             },
 //         },
 //     }
 //
@@ -192,6 +201,7 @@
 //
 //     SOONG_CONFIG_acme_board := soc_a
 //     SOONG_CONFIG_acme_feature := true
+//     SOONG_CONFIG_acme_width := 200
 //
 // Then libacme_foo would build with cflags "-DGENERIC -DSOC_A -DFEATURE".
 func soongConfigModuleTypeFactory() Module {
@@ -352,7 +362,12 @@
 
 			AddLoadHook(module, func(ctx LoadHookContext) {
 				config := ctx.Config().VendorConfig(moduleType.ConfigNamespace)
-				for _, ps := range soongconfig.PropertiesToApply(moduleType, conditionalProps, config) {
+				newProps, err := soongconfig.PropertiesToApply(moduleType, conditionalProps, config)
+				if err != nil {
+					ctx.ModuleErrorf("%s", err)
+					return
+				}
+				for _, ps := range newProps {
 					ctx.AppendProperties(ps)
 				}
 			})
diff --git a/android/soong_config_modules_test.go b/android/soong_config_modules_test.go
index 1cf060d..f905b1a 100644
--- a/android/soong_config_modules_test.go
+++ b/android/soong_config_modules_test.go
@@ -45,6 +45,7 @@
 			config_namespace: "acme",
 			variables: ["board", "feature1", "FEATURE3"],
 			bool_variables: ["feature2"],
+			value_variables: ["size"],
 			properties: ["cflags", "srcs"],
 		}
 
@@ -82,6 +83,9 @@
 						cflags: ["-DSOC_B"],
 					},
 				},
+				size: {
+					cflags: ["-DSIZE=%s"],
+				},
 				feature1: {
 					cflags: ["-DFEATURE1"],
 				},
@@ -101,6 +105,7 @@
 		config.TestProductVariables.VendorVars = map[string]map[string]string{
 			"acme": map[string]string{
 				"board":    "soc_a",
+				"size":     "42",
 				"feature1": "true",
 				"feature2": "false",
 				// FEATURE3 unset
@@ -121,7 +126,7 @@
 		FailIfErrored(t, errs)
 
 		foo := ctx.ModuleForTests("foo", "").Module().(*soongConfigTestModule)
-		if g, w := foo.props.Cflags, []string{"-DGENERIC", "-DSOC_A", "-DFEATURE1"}; !reflect.DeepEqual(g, w) {
+		if g, w := foo.props.Cflags, []string{"-DGENERIC", "-DSIZE=42", "-DSOC_A", "-DFEATURE1"}; !reflect.DeepEqual(g, w) {
 			t.Errorf("wanted foo cflags %q, got %q", w, g)
 		}
 	}
diff --git a/android/soongconfig/modules.go b/android/soongconfig/modules.go
index 2d6063d..142a813 100644
--- a/android/soongconfig/modules.go
+++ b/android/soongconfig/modules.go
@@ -112,6 +112,10 @@
 	// the list of boolean SOONG_CONFIG variables that this module type will read
 	Bool_variables []string
 
+	// the list of SOONG_CONFIG variables that this module type will read. The value will be
+	// inserted into the properties with %s substitution.
+	Value_variables []string
+
 	// the list of properties that this module type will extend.
 	Properties []string
 }
@@ -161,6 +165,18 @@
 		})
 	}
 
+	for _, name := range props.Value_variables {
+		if name == "" {
+			return []error{fmt.Errorf("value_variables entry must not be blank")}
+		}
+
+		mt.Variables = append(mt.Variables, &valueVariable{
+			baseVariable: baseVariable{
+				variable: name,
+			},
+		})
+	}
+
 	return nil
 }
 
@@ -404,15 +420,17 @@
 
 // PropertiesToApply returns the applicable properties from a ModuleType that should be applied
 // based on SoongConfig values.
-func PropertiesToApply(moduleType *ModuleType, props reflect.Value, config SoongConfig) []interface{} {
+func PropertiesToApply(moduleType *ModuleType, props reflect.Value, config SoongConfig) ([]interface{}, error) {
 	var ret []interface{}
 	props = props.Elem().FieldByName(soongConfigProperty)
 	for i, c := range moduleType.Variables {
-		if ps := c.PropertiesToApply(config, props.Field(i)); ps != nil {
+		if ps, err := c.PropertiesToApply(config, props.Field(i)); err != nil {
+			return nil, err
+		} else if ps != nil {
 			ret = append(ret, ps)
 		}
 	}
-	return ret
+	return ret, nil
 }
 
 type ModuleType struct {
@@ -438,7 +456,7 @@
 
 	// PropertiesToApply should return one of the interface{} values set by initializeProperties to be applied
 	// to the module.
-	PropertiesToApply(config SoongConfig, values reflect.Value) interface{}
+	PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error)
 }
 
 type baseVariable struct {
@@ -473,14 +491,14 @@
 	}
 }
 
-func (s *stringVariable) PropertiesToApply(config SoongConfig, values reflect.Value) interface{} {
+func (s *stringVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
 	for j, v := range s.values {
 		if config.String(s.variable) == v {
-			return values.Field(j).Interface()
+			return values.Field(j).Interface(), nil
 		}
 	}
 
-	return nil
+	return nil, nil
 }
 
 type boolVariable struct {
@@ -495,11 +513,83 @@
 	v.Set(reflect.Zero(typ))
 }
 
-func (b boolVariable) PropertiesToApply(config SoongConfig, values reflect.Value) interface{} {
+func (b boolVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
 	if config.Bool(b.variable) {
-		return values.Interface()
+		return values.Interface(), nil
 	}
 
+	return nil, nil
+}
+
+type valueVariable struct {
+	baseVariable
+}
+
+func (s *valueVariable) variableValuesType() reflect.Type {
+	return emptyInterfaceType
+}
+
+func (s *valueVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
+	v.Set(reflect.Zero(typ))
+}
+
+func (s *valueVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
+	if !config.IsSet(s.variable) {
+		return nil, nil
+	}
+	configValue := config.String(s.variable)
+
+	propStruct := values.Elem().Elem()
+	for i := 0; i < propStruct.NumField(); i++ {
+		field := propStruct.Field(i)
+		kind := field.Kind()
+		if kind == reflect.Ptr {
+			if field.IsNil() {
+				continue
+			}
+			field = field.Elem()
+		}
+		switch kind {
+		case reflect.String:
+			err := printfIntoProperty(field, configValue)
+			if err != nil {
+				return nil, fmt.Errorf("soong_config_variables.%s.%s: %s", s.variable, propStruct.Type().Field(i).Name, err)
+			}
+		case reflect.Slice:
+			for j := 0; j < field.Len(); j++ {
+				err := printfIntoProperty(field.Index(j), configValue)
+				if err != nil {
+					return nil, fmt.Errorf("soong_config_variables.%s.%s: %s", s.variable, propStruct.Type().Field(i).Name, err)
+				}
+			}
+		case reflect.Bool:
+			// Nothing to do
+		default:
+			return nil, fmt.Errorf("soong_config_variables.%s.%s: unsupported property type %q", s.variable, propStruct.Type().Field(i).Name, kind)
+		}
+	}
+
+	return values.Interface(), nil
+}
+
+func printfIntoProperty(propertyValue reflect.Value, configValue string) error {
+	s := propertyValue.String()
+
+	count := strings.Count(s, "%")
+	if count == 0 {
+		return nil
+	}
+
+	if count > 1 {
+		return fmt.Errorf("value variable properties only support a single '%%'")
+	}
+
+	if !strings.Contains(s, "%s") {
+		return fmt.Errorf("unsupported %% in value variable property")
+	}
+
+	propertyValue.Set(reflect.ValueOf(fmt.Sprintf(s, configValue)))
+
 	return nil
 }
 
diff --git a/apex/vndk.go b/apex/vndk.go
index f2e913e..2a0d5b0 100644
--- a/apex/vndk.go
+++ b/apex/vndk.go
@@ -95,6 +95,10 @@
 func apexVndkDepsMutator(mctx android.BottomUpMutatorContext) {
 	if m, ok := mctx.Module().(*cc.Module); ok && cc.IsForVndkApex(mctx, m) {
 		vndkVersion := m.VndkVersion()
+		// For VNDK-Lite device, we gather core-variants of VNDK-Sp libraries, which doesn't have VNDK version defined
+		if vndkVersion == "" {
+			vndkVersion = mctx.DeviceConfig().PlatformVndkVersion()
+		}
 		vndkApexList := vndkApexList(mctx.Config())
 		if vndkApex, ok := vndkApexList[vndkVersion]; ok {
 			mctx.AddReverseDependency(mctx.Module(), sharedLibTag, vndkApex)
diff --git a/apex/vndk_test.go b/apex/vndk_test.go
index 863d40d..523ac26 100644
--- a/apex/vndk_test.go
+++ b/apex/vndk_test.go
@@ -8,6 +8,59 @@
 	"android/soong/android"
 )
 
+func TestVndkApexForVndkLite(t *testing.T) {
+	ctx, _ := testApex(t, `
+		apex_vndk {
+			name: "myapex",
+			key: "myapex.key",
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "libvndk",
+			srcs: ["mylib.cpp"],
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+			},
+			system_shared_libs: [],
+			stl: "none",
+			apex_available: [ "myapex" ],
+		}
+
+		cc_library {
+			name: "libvndksp",
+			srcs: ["mylib.cpp"],
+			vendor_available: true,
+			vndk: {
+				enabled: true,
+				support_system_process: true,
+			},
+			system_shared_libs: [],
+			stl: "none",
+			apex_available: [ "myapex" ],
+		}
+	`+vndkLibrariesTxtFiles("current"), func(fs map[string][]byte, config android.Config) {
+		config.TestProductVariables.DeviceVndkVersion = proptools.StringPtr("")
+	})
+	// VNDK-Lite contains only core variants of VNDK-Sp libraries
+	ensureExactContents(t, ctx, "myapex", "android_common_image", []string{
+		"lib/libvndksp.so",
+		"lib/libc++.so",
+		"lib64/libvndksp.so",
+		"lib64/libc++.so",
+		"etc/llndk.libraries.VER.txt",
+		"etc/vndkcore.libraries.VER.txt",
+		"etc/vndksp.libraries.VER.txt",
+		"etc/vndkprivate.libraries.VER.txt",
+	})
+}
+
 func TestVndkApexUsesVendorVariant(t *testing.T) {
 	bp := `
 		apex_vndk {
diff --git a/cc/config/x86_windows_host.go b/cc/config/x86_windows_host.go
index 43e8c85..cd0a508 100644
--- a/cc/config/x86_windows_host.go
+++ b/cc/config/x86_windows_host.go
@@ -58,8 +58,11 @@
 		"-Wl,--dynamicbase",
 		"-Wl,--nxcompat",
 	}
+	windowsLldflags = []string{
+		"-Wl,--Xlink=-Brepro", // Enable deterministic build
+	}
 	windowsClangLdflags  = append(ClangFilterUnknownCflags(windowsLdflags), []string{}...)
-	windowsClangLldflags = ClangFilterUnknownLldflags(windowsClangLdflags)
+	windowsClangLldflags = append(ClangFilterUnknownLldflags(windowsClangLdflags), windowsLldflags...)
 
 	windowsX86Cflags = []string{
 		"-m32",
diff --git a/cc/tidy.go b/cc/tidy.go
index 6a13a78..cfb5b68 100644
--- a/cc/tidy.go
+++ b/cc/tidy.go
@@ -120,7 +120,7 @@
 	// https://b.corp.google.com/issues/153464409
 	// many local projects enable cert-* checks, which
 	// trigger bugprone-reserved-identifier.
-	tidyChecks = tidyChecks + ",-bugprone-reserved-identifier*"
+	tidyChecks = tidyChecks + ",-bugprone-reserved-identifier*,-cert-dcl51-cpp,-cert-dcl37-c"
 	flags.TidyFlags = append(flags.TidyFlags, tidyChecks)
 
 	if len(tidy.Properties.Tidy_checks_as_errors) > 0 {
diff --git a/cc/vndk.go b/cc/vndk.go
index 4888dcf..dbe1f3b 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -309,6 +309,10 @@
 		panic(err)
 	}
 
+	if m.HasStubsVariants() {
+		mctx.PropertyErrorf("vndk.enabled", "This library provides stubs. Shouldn't be VNDK. Consider making it as LLNDK")
+	}
+
 	vndkLibrariesLock.Lock()
 	defer vndkLibrariesLock.Unlock()
 
@@ -350,6 +354,15 @@
 	}
 
 	if lib, ok := m.linker.(libraryInterface); ok {
+		// VNDK APEX for VNDK-Lite devices will have VNDK-SP libraries from core variants
+		if mctx.DeviceConfig().VndkVersion() == "" {
+			// b/73296261: filter out libz.so because it is considered as LLNDK for VNDK-lite devices
+			if mctx.ModuleName() == "libz" {
+				return false
+			}
+			return m.ImageVariation().Variation == android.CoreVariation && lib.shared() && m.isVndkSp()
+		}
+
 		useCoreVariant := m.VndkVersion() == mctx.DeviceConfig().PlatformVndkVersion() &&
 			mctx.DeviceConfig().VndkUseCoreVariant() && !m.MustUseVendorVariant()
 		return lib.shared() && m.inVendor() && m.IsVndk() && !m.isVndkExt() && !useCoreVariant
diff --git a/docs/map_files.md b/docs/map_files.md
new file mode 100644
index 0000000..9fc0d14
--- /dev/null
+++ b/docs/map_files.md
@@ -0,0 +1,174 @@
+# Native API Map Files
+
+Native APIs such as those exposed by the NDK, LL-NDK, or APEX are described by
+map.txt files. These files are [linker version scripts] with comments that are
+semantically meaningful to [gen_stub_libs.py]. For an example of a map file, see
+[libc.map.txt].
+
+[gen_stub_libs.py]: https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/gen_stub_libs.py
+[libc.map.txt]: https://cs.android.com/android/platform/superproject/+/master:bionic/libc/libc.map.txt
+[linker version scripts]: https://www.gnu.org/software/gnulib/manual/html_node/LD-Version-Scripts.html
+
+## Basic format
+
+A linker version script defines at least one alphanumeric "version" definition,
+each of which contain a list of symbols. For example:
+
+```txt
+MY_API_R { # introduced=R
+  global:
+    api_foo;
+    api_bar;
+  local:
+    *;
+};
+
+MY_API_S { # introduced=S
+  global:
+    api_baz;
+} MY_API_R;
+```
+
+Comments on the same line as either a version definition or a symbol name have
+meaning. If you need to add any comments that should not be interpreted by the
+stub generator, keep them on their own line. For a list of supported comments,
+see the "Tags" section.
+
+Here, `api_foo` and `api_bar` are exposed in the generated stubs with the
+`MY_API_R` version and `api_baz` is exposed with the `MY_API_S` version. No
+other symbols are defined as public by this API. `MY_API_S` inherits all symbols
+defined by `MY_API_R`.
+
+When generating NDK API stubs from this version script, the stub library for R
+will define `api_foo` and `api_bar`. The stub library for S will define all
+three APIs.
+
+Note that, with few exceptions (see "Special version names" below), the name of
+the version has no inherent meaning.
+
+These map files can (and should) also be used as version scripts for building
+the implementation library rather than just defining the stub interface by using
+the `version_script` property of `cc_library`. This has the effect of limiting
+symbol visibility of the library to expose only the interface named by the map
+file. Without this, APIs that you have not explicitly exposed will still be
+available to users via `dlsym`. Note: All comments are ignored in this case. Any
+symbol named in any `global:` group will be visible.
+
+## Special version names
+
+Version names that end with `_PRIVATE` or `_PLATFORM` will not be exposed in any
+stubs, but will be exposed in the implementation library. Using either of these
+naming schemes is equivalent to marking the version with the `platform-only`
+tag. See the docs for `platform-only` for more information.
+
+## Tags
+
+Comments on the same line as a version definition or a symbol name are
+interpreted by the stub generator. Multiple space-delimited tags may be used on
+the same line. The supported tags are:
+
+### apex
+
+Indicates that the version or symbol is to be exposed in the APEX stubs rather
+than the NDK. May be used in combination with `llndk` if the symbol is exposed
+to both APEX and the LL-NDK.
+
+### future
+
+Indicates that the version or symbol is first introduced in the "future" API
+level. This is an abitrarily high API level used to define APIs that have not
+yet been added to a specific release.
+
+### introduced
+
+Indicates the version in which an API was first introduced. For example,
+`introduced=21` specifies that the API was first added (or first made public) in
+API level 21. This tag can be applied to either a version definition or an
+individual symbol. If applied to a version, all symbols contained in the version
+will have the tag applied. An `introduced` tag on a symbol overrides the value
+set for the version, if both are defined.
+
+Note: The map file alone does not contain all the information needed to
+determine which API level an API was added in. The `first_version` property of
+`ndk_library` will dictate which API levels stubs are generated for. If the
+module sets `first_version: "21"`, no symbols were introduced before API 21.
+
+Codenames can (and typically should) be used when defining new APIs. This allows
+the actual number of the API level to remain vague during development of that
+release. For example, `introduced=S` can be used to define APIs added in S. Any
+code name known to the build system can be used. For a list of versions known to
+the build system, see `out/soong/api_levels.json` (if not present, run `m
+out/soong/api_levels.json` to generate it).
+
+Architecture-specific variants of this tag exist:
+
+* `introduced-arm=VERSION`
+* `introduced-arm64=VERSION`
+* `introduced-x86=VERSION`
+* `introduced-x86_64=VERSION`
+
+The architecture-specific tag will take precedence over the architecture-generic
+tag when generating stubs for that architecture if both are present. If the
+symbol is defined with only architecture-specific tags, it will not be present
+for architectures that are not named.
+
+Note: The architecture-specific tags should, in general, not be used. These are
+primarily needed for APIs that were wrongly inconsistently exposed by libc/libm
+in old versions of Android before the stubs were well maintained. Think hard
+before using an architecture-specific tag for a new API.
+
+### llndk
+
+Indicates that the version or symbol is to be exposed in the LL-NDK stubs rather
+than the NDK. May be used in combination with `apex` if the symbol is exposed to
+both APEX and the LL-NDK.
+
+### platform-only
+
+Indicates that the version or symbol is public in the implementation library but
+should not be exposed in the stub library. Developers can still access them via
+`dlsym`, but they will not be exposed in the stubs so it should at least be
+clear to the developer that they are up to no good.
+
+The typical use for this tag is for exposing an API to the platform that is not
+for use by the NDK, LL-NDK, or APEX. It is preferable to keep such APIs in an
+entirely separate library to protect them from access via `dlsym`, but this is
+not always possible.
+
+### var
+
+Used to define a public global variable. By default all symbols are exposed as
+functions. In the uncommon situation of exposing a global variable, the `var`
+tag may be used.
+
+### versioned=VERSION
+
+Behaves similarly to `introduced` but defines the first version that the stub
+library should apply symbol versioning. For example:
+
+```txt
+R { # introduced=R
+  global:
+    foo;
+    bar; # versioned=S
+  local:
+    *;
+};
+```
+
+The stub library for R will contain symbols for both `foo` and `bar`, but only
+`foo` will include a versioned symbol `foo@R`. The stub library for S will
+contain both symbols, as well as the versioned symbols `foo@R` and `bar@R`.
+
+This tag is not commonly needed and is only used to hide symbol versioning
+mistakes that shipped as part of the platform.
+
+Note: Like `introduced`, the map file does not tell the whole story. The
+`ndk_library` Soong module may define a `unversioned_until` property that sets
+the default for the entire map file.
+
+### weak
+
+Indicates that the symbol should be [weak] in the stub library.
+
+[weak]: https://gcc.gnu.org/onlinedocs/gcc-4.7.2/gcc/Function-Attributes.html
diff --git a/java/androidmk.go b/java/androidmk.go
index 136bb36..d37eac8 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -545,10 +545,21 @@
 }
 
 func (dstubs *Droidstubs) AndroidMkEntries() []android.AndroidMkEntries {
+	// If the stubsSrcJar is not generated (because generate_stubs is false) then
+	// use the api file as the output file to ensure the relevant phony targets
+	// are created in make if only the api txt file is being generated. This is
+	// needed because an invalid output file would prevent the make entries from
+	// being written.
+	// TODO(b/146727827): Revert when we do not need to generate stubs and API separately.
+	distFile := android.OptionalPathForPath(dstubs.apiFile)
+	outputFile := android.OptionalPathForPath(dstubs.stubsSrcJar)
+	if !outputFile.Valid() {
+		outputFile = distFile
+	}
 	return []android.AndroidMkEntries{android.AndroidMkEntries{
 		Class:      "JAVA_LIBRARIES",
-		DistFile:   android.OptionalPathForPath(dstubs.apiFile),
-		OutputFile: android.OptionalPathForPath(dstubs.stubsSrcJar),
+		DistFile:   distFile,
+		OutputFile: outputFile,
 		Include:    "$(BUILD_SYSTEM)/soong_droiddoc_prebuilt.mk",
 		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
 			func(entries *android.AndroidMkEntries) {
diff --git a/java/app.go b/java/app.go
index 6a0aa8f..ae9cd5f 100755
--- a/java/app.go
+++ b/java/app.go
@@ -392,7 +392,18 @@
 			TransformJniLibsToJar(ctx, jniJarFile, jniLibs, a.useEmbeddedNativeLibs(ctx))
 			for _, jni := range jniLibs {
 				if jni.coverageFile.Valid() {
-					a.jniCoverageOutputs = append(a.jniCoverageOutputs, jni.coverageFile.Path())
+					// Only collect coverage for the first target arch if this is a multilib target.
+					// TODO(jungjw): Ideally, we want to collect both reports, but that would cause coverage
+					// data file path collisions since the current coverage file path format doesn't contain
+					// arch-related strings. This is fine for now though; the code coverage team doesn't use
+					// multi-arch targets such as test_suite_* for coverage collections yet.
+					//
+					// Work with the team to come up with a new format that handles multilib modules properly
+					// and change this.
+					if len(ctx.Config().Targets[android.Android]) == 1 ||
+						ctx.Config().Targets[android.Android][0].Arch.ArchType == jni.target.Arch.ArchType {
+						a.jniCoverageOutputs = append(a.jniCoverageOutputs, jni.coverageFile.Path())
+					}
 				}
 			}
 		} else {
@@ -1358,6 +1369,12 @@
 	// if not blank, set the minimum version of the sdk that the compiled artifacts will run against.
 	// Defaults to sdk_version if not set.
 	Min_sdk_version *string
+
+	// list of android_library modules whose resources are extracted and linked against statically
+	Static_libs []string
+
+	// list of android_app modules whose resources are extracted and linked against
+	Resource_libs []string
 }
 
 func (r *RuntimeResourceOverlay) DepsMutator(ctx android.BottomUpMutatorContext) {
@@ -1370,6 +1387,9 @@
 	if cert != "" {
 		ctx.AddDependency(ctx.Module(), certificateTag, cert)
 	}
+
+	ctx.AddVariationDependencies(nil, staticLibTag, r.properties.Static_libs...)
+	ctx.AddVariationDependencies(nil, libTag, r.properties.Resource_libs...)
 }
 
 func (r *RuntimeResourceOverlay) GenerateAndroidBuildActions(ctx android.ModuleContext) {
diff --git a/java/app_test.go b/java/app_test.go
index f2ec483..43696db 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -2349,11 +2349,17 @@
 }
 
 func TestRuntimeResourceOverlay(t *testing.T) {
-	ctx, config := testJava(t, `
+	fs := map[string][]byte{
+		"baz/res/res/values/strings.xml": nil,
+		"bar/res/res/values/strings.xml": nil,
+	}
+	bp := `
 		runtime_resource_overlay {
 			name: "foo",
 			certificate: "platform",
 			product_specific: true,
+			static_libs: ["bar"],
+			resource_libs: ["baz"],
 			aaptflags: ["--keep-raw-values"],
 		}
 
@@ -2363,7 +2369,21 @@
 			product_specific: true,
 			theme: "faza",
 		}
-		`)
+
+		android_library {
+			name: "bar",
+			resource_dirs: ["bar/res"],
+		}
+
+		android_app {
+			name: "baz",
+			sdk_version: "current",
+			resource_dirs: ["baz/res"],
+		}
+		`
+	config := testAppConfig(nil, bp, fs)
+	ctx := testContext()
+	run(t, ctx, config)
 
 	m := ctx.ModuleForTests("foo", "android_common")
 
@@ -2375,6 +2395,19 @@
 		t.Errorf("expected values, %q are missing in aapt2 link flags, %q", absentFlags, aapt2Flags)
 	}
 
+	// Check overlay.list output for static_libs dependency.
+	overlayList := m.Output("aapt2/overlay.list").Inputs.Strings()
+	staticLibPackage := buildDir + "/.intermediates/bar/android_common/package-res.apk"
+	if !inList(staticLibPackage, overlayList) {
+		t.Errorf("Stactic lib res package %q missing in overlay list: %q", staticLibPackage, overlayList)
+	}
+
+	// Check AAPT2 link flags for resource_libs dependency.
+	resourceLibFlag := "-I " + buildDir + "/.intermediates/baz/android_common/package-res.apk"
+	if !strings.Contains(aapt2Flags, resourceLibFlag) {
+		t.Errorf("Resource lib flag %q missing in aapt2 link flags: %q", resourceLibFlag, aapt2Flags)
+	}
+
 	// Check cert signing flag.
 	signedApk := m.Output("signed/foo.apk")
 	signingFlag := signedApk.Args["certificates"]
diff --git a/java/droiddoc.go b/java/droiddoc.go
index b0efaa5..e4582f6 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -301,6 +301,11 @@
 	// if set to true, allow Metalava to generate doc_stubs source files. Defaults to false.
 	Create_doc_stubs *bool
 
+	// if set to false then do not write out stubs. Defaults to true.
+	//
+	// TODO(b/146727827): Remove capability when we do not need to generate stubs and API separately.
+	Generate_stubs *bool
+
 	// is set to true, Metalava will allow framework SDK to contain API levels annotations.
 	Api_levels_annotations_enabled *bool
 
@@ -370,10 +375,18 @@
 	apiToCheck.Removed_api_file = nil
 }
 
+// Used by xsd_config
 type ApiFilePath interface {
 	ApiFilePath() android.Path
 }
 
+// Provider of information about API stubs, used by java_sdk_library.
+type ApiStubsProvider interface {
+	ApiFilePath
+	RemovedApiFilePath() android.Path
+	StubsSrcJar() android.Path
+}
+
 //
 // Javadoc
 //
@@ -1259,6 +1272,14 @@
 	return d.apiFilePath
 }
 
+func (d *Droidstubs) RemovedApiFilePath() android.Path {
+	return d.removedApiFile
+}
+
+func (d *Droidstubs) StubsSrcJar() android.Path {
+	return d.stubsSrcJar
+}
+
 func (d *Droidstubs) DepsMutator(ctx android.BottomUpMutatorContext) {
 	d.Javadoc.addDeps(ctx)
 
@@ -1285,7 +1306,7 @@
 	}
 }
 
-func (d *Droidstubs) stubsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsDir android.WritablePath) {
+func (d *Droidstubs) stubsFlags(ctx android.ModuleContext, cmd *android.RuleBuilderCommand, stubsDir android.OptionalPath) {
 	if apiCheckEnabled(ctx, d.properties.Check_api.Current, "current") ||
 		apiCheckEnabled(ctx, d.properties.Check_api.Last_released, "last_released") ||
 		String(d.properties.Api_filename) != "" {
@@ -1341,11 +1362,13 @@
 		cmd.FlagWithArg("--sdk-values ", d.metadataDir.String())
 	}
 
-	if Bool(d.properties.Create_doc_stubs) {
-		cmd.FlagWithArg("--doc-stubs ", stubsDir.String())
-	} else {
-		cmd.FlagWithArg("--stubs ", stubsDir.String())
-		cmd.Flag("--exclude-documentation-from-stubs")
+	if stubsDir.Valid() {
+		if Bool(d.properties.Create_doc_stubs) {
+			cmd.FlagWithArg("--doc-stubs ", stubsDir.String())
+		} else {
+			cmd.FlagWithArg("--stubs ", stubsDir.String())
+			cmd.Flag("--exclude-documentation-from-stubs")
+		}
 	}
 }
 
@@ -1502,15 +1525,18 @@
 
 	// Create rule for metalava
 
-	d.Javadoc.stubsSrcJar = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"stubs.srcjar")
-
 	srcJarDir := android.PathForModuleOut(ctx, "srcjars")
-	stubsDir := android.PathForModuleOut(ctx, "stubsDir")
 
 	rule := android.NewRuleBuilder()
 
-	rule.Command().Text("rm -rf").Text(stubsDir.String())
-	rule.Command().Text("mkdir -p").Text(stubsDir.String())
+	generateStubs := BoolDefault(d.properties.Generate_stubs, true)
+	var stubsDir android.OptionalPath
+	if generateStubs {
+		d.Javadoc.stubsSrcJar = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"stubs.srcjar")
+		stubsDir = android.OptionalPathForPath(android.PathForModuleOut(ctx, "stubsDir"))
+		rule.Command().Text("rm -rf").Text(stubsDir.String())
+		rule.Command().Text("mkdir -p").Text(stubsDir.String())
+	}
 
 	srcJarList := zipSyncCmd(ctx, rule, srcJarDir, d.Javadoc.srcJars)
 
@@ -1536,13 +1562,15 @@
 		cmd.ImplicitOutput(android.PathForModuleGen(ctx, o))
 	}
 
-	rule.Command().
-		BuiltTool(ctx, "soong_zip").
-		Flag("-write_if_changed").
-		Flag("-jar").
-		FlagWithOutput("-o ", d.Javadoc.stubsSrcJar).
-		FlagWithArg("-C ", stubsDir.String()).
-		FlagWithArg("-D ", stubsDir.String())
+	if generateStubs {
+		rule.Command().
+			BuiltTool(ctx, "soong_zip").
+			Flag("-write_if_changed").
+			Flag("-jar").
+			FlagWithOutput("-o ", d.Javadoc.stubsSrcJar).
+			FlagWithArg("-C ", stubsDir.String()).
+			FlagWithArg("-D ", stubsDir.String())
+	}
 
 	if Bool(d.properties.Write_sdk_values) {
 		d.metadataZip = android.PathForModuleOut(ctx, ctx.ModuleName()+"-metadata.zip")
diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go
index cb17fee..86eddb1 100644
--- a/java/prebuilt_apis.go
+++ b/java/prebuilt_apis.go
@@ -28,10 +28,6 @@
 
 func RegisterPrebuiltApisBuildComponents(ctx android.RegistrationContext) {
 	ctx.RegisterModuleType("prebuilt_apis", PrebuiltApisFactory)
-
-	ctx.PreArchMutators(func(ctx android.RegisterMutatorsContext) {
-		ctx.TopDown("prebuilt_apis", PrebuiltApisMutator).Parallel()
-	})
 }
 
 type prebuiltApisProperties struct {
@@ -48,7 +44,7 @@
 	// no need to implement
 }
 
-func parseJarPath(ctx android.BaseModuleContext, path string) (module string, apiver string, scope string) {
+func parseJarPath(path string) (module string, apiver string, scope string) {
 	elements := strings.Split(path, "/")
 
 	apiver = elements[0]
@@ -58,7 +54,7 @@
 	return
 }
 
-func parseApiFilePath(ctx android.BaseModuleContext, path string) (module string, apiver string, scope string) {
+func parseApiFilePath(ctx android.LoadHookContext, path string) (module string, apiver string, scope string) {
 	elements := strings.Split(path, "/")
 	apiver = elements[0]
 
@@ -73,7 +69,7 @@
 	return
 }
 
-func createImport(mctx android.TopDownMutatorContext, module string, scope string, apiver string, path string) {
+func createImport(mctx android.LoadHookContext, module string, scope string, apiver string, path string) {
 	props := struct {
 		Name        *string
 		Jars        []string
@@ -89,7 +85,7 @@
 	mctx.CreateModule(ImportFactory, &props)
 }
 
-func createFilegroup(mctx android.TopDownMutatorContext, module string, scope string, apiver string, path string) {
+func createFilegroup(mctx android.LoadHookContext, module string, scope string, apiver string, path string) {
 	fgName := module + ".api." + scope + "." + apiver
 	filegroupProps := struct {
 		Name *string
@@ -100,7 +96,7 @@
 	mctx.CreateModule(android.FileGroupFactory, &filegroupProps)
 }
 
-func getPrebuiltFiles(mctx android.TopDownMutatorContext, name string) []string {
+func getPrebuiltFiles(mctx android.LoadHookContext, name string) []string {
 	mydir := mctx.ModuleDir() + "/"
 	var files []string
 	for _, apiver := range mctx.Module().(*prebuiltApis).properties.Api_dirs {
@@ -115,7 +111,7 @@
 	return files
 }
 
-func prebuiltSdkStubs(mctx android.TopDownMutatorContext) {
+func prebuiltSdkStubs(mctx android.LoadHookContext) {
 	mydir := mctx.ModuleDir() + "/"
 	// <apiver>/<scope>/<module>.jar
 	files := getPrebuiltFiles(mctx, "*.jar")
@@ -123,12 +119,12 @@
 	for _, f := range files {
 		// create a Import module for each jar file
 		localPath := strings.TrimPrefix(f, mydir)
-		module, apiver, scope := parseJarPath(mctx, localPath)
+		module, apiver, scope := parseJarPath(localPath)
 		createImport(mctx, module, scope, apiver, localPath)
 	}
 }
 
-func prebuiltApiFiles(mctx android.TopDownMutatorContext) {
+func prebuiltApiFiles(mctx android.LoadHookContext) {
 	mydir := mctx.ModuleDir() + "/"
 	// <apiver>/<scope>/api/<module>.txt
 	files := getPrebuiltFiles(mctx, "api/*.txt")
@@ -178,7 +174,7 @@
 	}
 }
 
-func PrebuiltApisMutator(mctx android.TopDownMutatorContext) {
+func createPrebuiltApiModules(mctx android.LoadHookContext) {
 	if _, ok := mctx.Module().(*prebuiltApis); ok {
 		prebuiltApiFiles(mctx)
 		prebuiltSdkStubs(mctx)
@@ -191,9 +187,15 @@
 // generates a filegroup module named <module>-api.<scope>.<ver>.
 //
 // It also creates <module>-api.<scope>.latest for the latest <ver>.
+//
+// Similarly, it generates a java_import for all API .jar files found under the
+// directory where the Android.bp is located. Specifically, an API file located
+// at ./<ver>/<scope>/api/<module>.jar generates a java_import module named
+// <prebuilt-api-module>.<scope>.<ver>.<module>.
 func PrebuiltApisFactory() android.Module {
 	module := &prebuiltApis{}
 	module.AddProperties(&module.properties)
 	android.InitAndroidModule(module)
+	android.AddLoadHook(module, createPrebuiltApiModules)
 	return module
 }
diff --git a/java/sdk_library.go b/java/sdk_library.go
index efee7da..ff80d63 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -15,17 +15,18 @@
 package java
 
 import (
-	"android/soong/android"
-
 	"fmt"
 	"path"
 	"path/filepath"
+	"reflect"
 	"sort"
 	"strings"
 	"sync"
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
+
+	"android/soong/android"
 )
 
 const (
@@ -66,6 +67,9 @@
 	// The name of the api scope, e.g. public, system, test
 	name string
 
+	// The name of the field in the dynamically created structure.
+	fieldName string
+
 	// The tag to use to depend on the stubs library module.
 	stubsTag scopeDependencyTag
 
@@ -93,7 +97,7 @@
 
 // Initialize a scope, creating and adding appropriate dependency tags
 func initApiScope(scope *apiScope) *apiScope {
-	//apiScope := &scope
+	scope.fieldName = proptools.FieldNameForProperty(scope.name)
 	scope.stubsTag = scopeDependencyTag{
 		name:     scope.name + "-stubs",
 		apiScope: scope,
@@ -167,6 +171,14 @@
 		sort.Strings(*javaSdkLibraries)
 		ctx.Strict("JAVA_SDK_LIBRARIES", strings.Join(*javaSdkLibraries, " "))
 	})
+
+	// Register sdk member types.
+	android.RegisterSdkMemberType(&sdkLibrarySdkMemberType{
+		android.SdkMemberTypeBase{
+			PropertyName: "java_sdk_libs",
+			SupportsSdk:  true,
+		},
+	})
 }
 
 func RegisterSdkLibraryBuildComponents(ctx android.RegistrationContext) {
@@ -224,9 +236,11 @@
 }
 
 type scopePaths struct {
-	stubsHeaderPath android.Paths
-	stubsImplPath   android.Paths
-	apiFilePath     android.Path
+	stubsHeaderPath    android.Paths
+	stubsImplPath      android.Paths
+	currentApiFilePath android.Path
+	removedApiFilePath android.Path
+	stubsSrcJar        android.Path
 }
 
 // Common code between sdk library and sdk library import
@@ -313,11 +327,13 @@
 				scopePaths.stubsImplPath = lib.ImplementationJars()
 			}
 		}
-		if doc, ok := to.(ApiFilePath); ok {
+		if doc, ok := to.(ApiStubsProvider); ok {
 			if scopeTag, ok := tag.(scopeDependencyTag); ok {
 				apiScope := scopeTag.apiScope
 				scopePaths := module.getScopePaths(apiScope)
-				scopePaths.apiFilePath = doc.ApiFilePath()
+				scopePaths.currentApiFilePath = doc.ApiFilePath()
+				scopePaths.removedApiFilePath = doc.RemovedApiFilePath()
+				scopePaths.stubsSrcJar = doc.StubsSrcJar()
 			} else {
 				ctx.ModuleErrorf("depends on module %q of unknown tag %q", otherName, tag)
 			}
@@ -814,42 +830,89 @@
 
 	// List of shared java libs that this module has dependencies to
 	Libs []string
+
+	// The stub sources.
+	Stub_srcs []string `android:"path"`
+
+	// The current.txt
+	Current_api string `android:"path"`
+
+	// The removed.txt
+	Removed_api string `android:"path"`
 }
 
 type sdkLibraryImportProperties struct {
 	// List of shared java libs, common to all scopes, that this module has
 	// dependencies to
 	Libs []string
-
-	// Properties associated with the public api scope.
-	Public sdkLibraryScopeProperties
-
-	// Properties associated with the system api scope.
-	System sdkLibraryScopeProperties
-
-	// Properties associated with the test api scope.
-	Test sdkLibraryScopeProperties
 }
 
 type sdkLibraryImport struct {
 	android.ModuleBase
 	android.DefaultableModuleBase
 	prebuilt android.Prebuilt
+	android.ApexModuleBase
+	android.SdkBase
 
 	properties sdkLibraryImportProperties
 
+	// Map from api scope to the scope specific property structure.
+	scopeProperties map[*apiScope]*sdkLibraryScopeProperties
+
 	commonToSdkLibraryAndImport
 }
 
 var _ SdkLibraryDependency = (*sdkLibraryImport)(nil)
 
+// The type of a structure that contains a field of type sdkLibraryScopeProperties
+// for each apiscope in allApiScopes, e.g. something like:
+// struct {
+//   Public sdkLibraryScopeProperties
+//   System sdkLibraryScopeProperties
+//   ...
+// }
+var allScopeStructType = createAllScopePropertiesStructType()
+
+// Dynamically create a structure type for each apiscope in allApiScopes.
+func createAllScopePropertiesStructType() reflect.Type {
+	var fields []reflect.StructField
+	for _, apiScope := range allApiScopes {
+		field := reflect.StructField{
+			Name: apiScope.fieldName,
+			Type: reflect.TypeOf(sdkLibraryScopeProperties{}),
+		}
+		fields = append(fields, field)
+	}
+
+	return reflect.StructOf(fields)
+}
+
+// Create an instance of the scope specific structure type and return a map
+// from apiscope to a pointer to each scope specific field.
+func createPropertiesInstance() (interface{}, map[*apiScope]*sdkLibraryScopeProperties) {
+	allScopePropertiesPtr := reflect.New(allScopeStructType)
+	allScopePropertiesStruct := allScopePropertiesPtr.Elem()
+	scopeProperties := make(map[*apiScope]*sdkLibraryScopeProperties)
+
+	for _, apiScope := range allApiScopes {
+		field := allScopePropertiesStruct.FieldByName(apiScope.fieldName)
+		scopeProperties[apiScope] = field.Addr().Interface().(*sdkLibraryScopeProperties)
+	}
+
+	return allScopePropertiesPtr.Interface(), scopeProperties
+}
+
 // java_sdk_library_import imports a prebuilt java_sdk_library.
 func sdkLibraryImportFactory() android.Module {
 	module := &sdkLibraryImport{}
 
-	module.AddProperties(&module.properties)
+	allScopeProperties, scopeToProperties := createPropertiesInstance()
+	module.scopeProperties = scopeToProperties
+	module.AddProperties(&module.properties, allScopeProperties)
 
 	android.InitPrebuiltModule(module, &[]string{""})
+	android.InitApexModule(module)
+	android.InitSdkAwareModule(module)
 	InitJavaModule(module, android.HostAndDeviceSupported)
 
 	android.AddLoadHook(module, func(mctx android.LoadHookContext) { module.createInternalModules(mctx) })
@@ -871,48 +934,14 @@
 		module.prebuilt.ForcePrefer()
 	}
 
-	for apiScope, scopeProperties := range module.scopeProperties() {
+	for apiScope, scopeProperties := range module.scopeProperties {
 		if len(scopeProperties.Jars) == 0 {
 			continue
 		}
 
-		// Creates a java import for the jar with ".stubs" suffix
-		props := struct {
-			Name                *string
-			Soc_specific        *bool
-			Device_specific     *bool
-			Product_specific    *bool
-			System_ext_specific *bool
-			Sdk_version         *string
-			Libs                []string
-			Jars                []string
-			Prefer              *bool
-		}{}
+		module.createJavaImportForStubs(mctx, apiScope, scopeProperties)
 
-		props.Name = proptools.StringPtr(apiScope.stubsModuleName(module.BaseModuleName()))
-		props.Sdk_version = scopeProperties.Sdk_version
-		// Prepend any of the libs from the legacy public properties to the libs for each of the
-		// scopes to avoid having to duplicate them in each scope.
-		props.Libs = append(module.properties.Libs, scopeProperties.Libs...)
-		props.Jars = scopeProperties.Jars
-
-		if module.SocSpecific() {
-			props.Soc_specific = proptools.BoolPtr(true)
-		} else if module.DeviceSpecific() {
-			props.Device_specific = proptools.BoolPtr(true)
-		} else if module.ProductSpecific() {
-			props.Product_specific = proptools.BoolPtr(true)
-		} else if module.SystemExtSpecific() {
-			props.System_ext_specific = proptools.BoolPtr(true)
-		}
-
-		// If the build should use prebuilt sdks then set prefer to true on the stubs library.
-		// That will cause the prebuilt version of the stubs to override the source version.
-		if mctx.Config().UnbundledBuildUsePrebuiltSdks() {
-			props.Prefer = proptools.BoolPtr(true)
-		}
-
-		mctx.CreateModule(ImportFactory, &props)
+		module.createPrebuiltStubsSources(mctx, apiScope, scopeProperties)
 	}
 
 	javaSdkLibraries := javaSdkLibraries(mctx.Config())
@@ -921,16 +950,54 @@
 	*javaSdkLibraries = append(*javaSdkLibraries, module.BaseModuleName())
 }
 
-func (module *sdkLibraryImport) scopeProperties() map[*apiScope]*sdkLibraryScopeProperties {
-	p := make(map[*apiScope]*sdkLibraryScopeProperties)
-	p[apiScopePublic] = &module.properties.Public
-	p[apiScopeSystem] = &module.properties.System
-	p[apiScopeTest] = &module.properties.Test
-	return p
+func (module *sdkLibraryImport) createJavaImportForStubs(mctx android.LoadHookContext, apiScope *apiScope, scopeProperties *sdkLibraryScopeProperties) {
+	// Creates a java import for the jar with ".stubs" suffix
+	props := struct {
+		Name                *string
+		Soc_specific        *bool
+		Device_specific     *bool
+		Product_specific    *bool
+		System_ext_specific *bool
+		Sdk_version         *string
+		Libs                []string
+		Jars                []string
+		Prefer              *bool
+	}{}
+	props.Name = proptools.StringPtr(apiScope.stubsModuleName(module.BaseModuleName()))
+	props.Sdk_version = scopeProperties.Sdk_version
+	// Prepend any of the libs from the legacy public properties to the libs for each of the
+	// scopes to avoid having to duplicate them in each scope.
+	props.Libs = append(module.properties.Libs, scopeProperties.Libs...)
+	props.Jars = scopeProperties.Jars
+	if module.SocSpecific() {
+		props.Soc_specific = proptools.BoolPtr(true)
+	} else if module.DeviceSpecific() {
+		props.Device_specific = proptools.BoolPtr(true)
+	} else if module.ProductSpecific() {
+		props.Product_specific = proptools.BoolPtr(true)
+	} else if module.SystemExtSpecific() {
+		props.System_ext_specific = proptools.BoolPtr(true)
+	}
+	// If the build should use prebuilt sdks then set prefer to true on the stubs library.
+	// That will cause the prebuilt version of the stubs to override the source version.
+	if mctx.Config().UnbundledBuildUsePrebuiltSdks() {
+		props.Prefer = proptools.BoolPtr(true)
+	}
+	mctx.CreateModule(ImportFactory, &props)
+}
+
+func (module *sdkLibraryImport) createPrebuiltStubsSources(mctx android.LoadHookContext, apiScope *apiScope, scopeProperties *sdkLibraryScopeProperties) {
+	props := struct {
+		Name *string
+		Srcs []string
+	}{}
+	props.Name = proptools.StringPtr(apiScope.docsModuleName(module.BaseModuleName()))
+	props.Srcs = scopeProperties.Stub_srcs
+	mctx.CreateModule(PrebuiltStubsSourcesFactory, &props)
 }
 
 func (module *sdkLibraryImport) DepsMutator(ctx android.BottomUpMutatorContext) {
-	for apiScope, scopeProperties := range module.scopeProperties() {
+	for apiScope, scopeProperties := range module.scopeProperties {
 		if len(scopeProperties.Jars) == 0 {
 			continue
 		}
@@ -1097,3 +1164,110 @@
 		},
 	}}
 }
+
+type sdkLibrarySdkMemberType struct {
+	android.SdkMemberTypeBase
+}
+
+func (s *sdkLibrarySdkMemberType) AddDependencies(mctx android.BottomUpMutatorContext, dependencyTag blueprint.DependencyTag, names []string) {
+	mctx.AddVariationDependencies(nil, dependencyTag, names...)
+}
+
+func (s *sdkLibrarySdkMemberType) IsInstance(module android.Module) bool {
+	_, ok := module.(*SdkLibrary)
+	return ok
+}
+
+func (s *sdkLibrarySdkMemberType) AddPrebuiltModule(ctx android.SdkMemberContext, member android.SdkMember) android.BpModule {
+	return ctx.SnapshotBuilder().AddPrebuiltModule(member, "java_sdk_library_import")
+}
+
+func (s *sdkLibrarySdkMemberType) CreateVariantPropertiesStruct() android.SdkMemberProperties {
+	return &sdkLibrarySdkMemberProperties{}
+}
+
+type sdkLibrarySdkMemberProperties struct {
+	android.SdkMemberPropertiesBase
+
+	// Scope to per scope properties.
+	Scopes map[*apiScope]scopeProperties
+
+	// Additional libraries that the exported stubs libraries depend upon.
+	Libs []string
+
+	// The Java stubs source files.
+	Stub_srcs []string
+}
+
+type scopeProperties struct {
+	Jars           android.Paths
+	StubsSrcJar    android.Path
+	CurrentApiFile android.Path
+	RemovedApiFile android.Path
+	SdkVersion     string
+}
+
+func (s *sdkLibrarySdkMemberProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) {
+	sdk := variant.(*SdkLibrary)
+
+	s.Scopes = make(map[*apiScope]scopeProperties)
+	for _, apiScope := range allApiScopes {
+		paths := sdk.getScopePaths(apiScope)
+		jars := paths.stubsImplPath
+		if len(jars) > 0 {
+			properties := scopeProperties{}
+			properties.Jars = jars
+			properties.SdkVersion = apiScope.sdkVersion
+			properties.StubsSrcJar = paths.stubsSrcJar
+			properties.CurrentApiFile = paths.currentApiFilePath
+			properties.RemovedApiFile = paths.removedApiFilePath
+			s.Scopes[apiScope] = properties
+		}
+	}
+
+	s.Libs = sdk.properties.Libs
+}
+
+func (s *sdkLibrarySdkMemberProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) {
+	for _, apiScope := range allApiScopes {
+		if properties, ok := s.Scopes[apiScope]; ok {
+			scopeSet := propertySet.AddPropertySet(apiScope.name)
+
+			scopeDir := filepath.Join("sdk_library", s.OsPrefix(), apiScope.name)
+
+			var jars []string
+			for _, p := range properties.Jars {
+				dest := filepath.Join(scopeDir, ctx.Name()+"-stubs.jar")
+				ctx.SnapshotBuilder().CopyToSnapshot(p, dest)
+				jars = append(jars, dest)
+			}
+			scopeSet.AddProperty("jars", jars)
+
+			// Merge the stubs source jar into the snapshot zip so that when it is unpacked
+			// the source files are also unpacked.
+			snapshotRelativeDir := filepath.Join(scopeDir, ctx.Name()+"_stub_sources")
+			ctx.SnapshotBuilder().UnzipToSnapshot(properties.StubsSrcJar, snapshotRelativeDir)
+			scopeSet.AddProperty("stub_srcs", []string{snapshotRelativeDir})
+
+			if properties.CurrentApiFile != nil {
+				currentApiSnapshotPath := filepath.Join(scopeDir, ctx.Name()+".txt")
+				ctx.SnapshotBuilder().CopyToSnapshot(properties.CurrentApiFile, currentApiSnapshotPath)
+				scopeSet.AddProperty("current_api", currentApiSnapshotPath)
+			}
+
+			if properties.RemovedApiFile != nil {
+				removedApiSnapshotPath := filepath.Join(scopeDir, ctx.Name()+"-removed.txt")
+				ctx.SnapshotBuilder().CopyToSnapshot(properties.CurrentApiFile, removedApiSnapshotPath)
+				scopeSet.AddProperty("removed_api", removedApiSnapshotPath)
+			}
+
+			if properties.SdkVersion != "" {
+				scopeSet.AddProperty("sdk_version", properties.SdkVersion)
+			}
+		}
+	}
+
+	if len(s.Libs) > 0 {
+		propertySet.AddPropertyWithTag("libs", s.Libs, ctx.SnapshotBuilder().SdkMemberReferencePropertyTag(false))
+	}
+}
diff --git a/rust/config/arm64_device.go b/rust/config/arm64_device.go
index 60796d8..180fd8b 100644
--- a/rust/config/arm64_device.go
+++ b/rust/config/arm64_device.go
@@ -27,7 +27,6 @@
 		"-Wl,--icf=safe",
 		"-Wl,-z,max-page-size=4096",
 
-		"-Wl,--execute-only",
 		"-Wl,-z,separate-code",
 	}
 
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 4a2c053..c0ad35c 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -24,7 +24,51 @@
 	fs := map[string][]byte{
 		"Test.java":              nil,
 		"aidl/foo/bar/Test.aidl": nil,
+
+		// For java_sdk_library
+		"api/current.txt":                                   nil,
+		"api/removed.txt":                                   nil,
+		"api/system-current.txt":                            nil,
+		"api/system-removed.txt":                            nil,
+		"api/test-current.txt":                              nil,
+		"api/test-removed.txt":                              nil,
+		"build/soong/scripts/gen-java-current-api-files.sh": nil,
 	}
+
+	// for java_sdk_library tests
+	bp = `
+java_system_modules_import {
+	name: "core-current-stubs-system-modules",
+}
+java_system_modules_import {
+	name: "core-platform-api-stubs-system-modules",
+}
+java_import {
+	name: "core.platform.api.stubs",
+}
+java_sdk_library_import {
+	name: "android_stubs_current",
+}
+java_sdk_library_import {
+	name: "android_system_stubs_current",
+}
+java_sdk_library_import {
+	name: "android_test_stubs_current",
+}
+java_import {
+	name: "core-lambda-stubs", 
+	sdk_version: "none",
+}
+java_import {
+	name: "ext", 
+	sdk_version: "none",
+}
+java_import {
+	name: "framework", 
+	sdk_version: "none",
+}
+` + bp
+
 	return testSdkWithFs(t, bp, fs)
 }
 
@@ -580,7 +624,7 @@
 
 `),
 		checkAllCopyRules(""),
-		checkMergeZip(".intermediates/myexports/common_os/tmp/java/myjavaapistubs_stubs_sources.zip"),
+		checkMergeZips(".intermediates/myexports/common_os/tmp/java/myjavaapistubs_stubs_sources.zip"),
 	)
 }
 
@@ -634,7 +678,7 @@
 }
 `),
 		checkAllCopyRules(""),
-		checkMergeZip(".intermediates/myexports/common_os/tmp/java/myjavaapistubs_stubs_sources.zip"),
+		checkMergeZips(".intermediates/myexports/common_os/tmp/java/myjavaapistubs_stubs_sources.zip"),
 	)
 }
 
@@ -927,3 +971,99 @@
 `),
 	)
 }
+
+func TestSnapshotWithJavaSdkLibrary(t *testing.T) {
+	result := testSdkWithJava(t, `
+		sdk {
+			name: "mysdk",
+			java_sdk_libs: ["myjavalib"],
+		}
+
+		java_sdk_library {
+			name: "myjavalib",
+			apex_available: ["//apex_available:anyapex"],
+			srcs: ["Test.java"],
+			sdk_version: "current",
+		}
+	`)
+
+	result.CheckSnapshot("mysdk", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_sdk_library_import {
+    name: "mysdk_myjavalib@current",
+    sdk_member_name: "myjavalib",
+    apex_available: ["//apex_available:anyapex"],
+    public: {
+        jars: ["sdk_library/public/myjavalib-stubs.jar"],
+        stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
+        current_api: "sdk_library/public/myjavalib.txt",
+        removed_api: "sdk_library/public/myjavalib-removed.txt",
+        sdk_version: "current",
+    },
+    system: {
+        jars: ["sdk_library/system/myjavalib-stubs.jar"],
+        stub_srcs: ["sdk_library/system/myjavalib_stub_sources"],
+        current_api: "sdk_library/system/myjavalib.txt",
+        removed_api: "sdk_library/system/myjavalib-removed.txt",
+        sdk_version: "system_current",
+    },
+    test: {
+        jars: ["sdk_library/test/myjavalib-stubs.jar"],
+        stub_srcs: ["sdk_library/test/myjavalib_stub_sources"],
+        current_api: "sdk_library/test/myjavalib.txt",
+        removed_api: "sdk_library/test/myjavalib-removed.txt",
+        sdk_version: "test_current",
+    },
+}
+
+java_sdk_library_import {
+    name: "myjavalib",
+    prefer: false,
+    apex_available: ["//apex_available:anyapex"],
+    public: {
+        jars: ["sdk_library/public/myjavalib-stubs.jar"],
+        stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
+        current_api: "sdk_library/public/myjavalib.txt",
+        removed_api: "sdk_library/public/myjavalib-removed.txt",
+        sdk_version: "current",
+    },
+    system: {
+        jars: ["sdk_library/system/myjavalib-stubs.jar"],
+        stub_srcs: ["sdk_library/system/myjavalib_stub_sources"],
+        current_api: "sdk_library/system/myjavalib.txt",
+        removed_api: "sdk_library/system/myjavalib-removed.txt",
+        sdk_version: "system_current",
+    },
+    test: {
+        jars: ["sdk_library/test/myjavalib-stubs.jar"],
+        stub_srcs: ["sdk_library/test/myjavalib_stub_sources"],
+        current_api: "sdk_library/test/myjavalib.txt",
+        removed_api: "sdk_library/test/myjavalib-removed.txt",
+        sdk_version: "test_current",
+    },
+}
+
+sdk_snapshot {
+    name: "mysdk@current",
+    java_sdk_libs: ["mysdk_myjavalib@current"],
+}
+`),
+		checkAllCopyRules(`
+.intermediates/myjavalib.stubs/android_common/javac/myjavalib.stubs.jar -> sdk_library/public/myjavalib-stubs.jar
+.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib.stubs.source/android_common/myjavalib.stubs.source_api.txt -> sdk_library/public/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.system/android_common/javac/myjavalib.stubs.system.jar -> sdk_library/system/myjavalib-stubs.jar
+.intermediates/myjavalib.stubs.source.system/android_common/myjavalib.stubs.source.system_api.txt -> sdk_library/system/myjavalib.txt
+.intermediates/myjavalib.stubs.source.system/android_common/myjavalib.stubs.source.system_api.txt -> sdk_library/system/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.test/android_common/javac/myjavalib.stubs.test.jar -> sdk_library/test/myjavalib-stubs.jar
+.intermediates/myjavalib.stubs.source.test/android_common/myjavalib.stubs.source.test_api.txt -> sdk_library/test/myjavalib.txt
+.intermediates/myjavalib.stubs.source.test/android_common/myjavalib.stubs.source.test_api.txt -> sdk_library/test/myjavalib-removed.txt
+`),
+		checkMergeZips(
+			".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
+			".intermediates/mysdk/common_os/tmp/sdk_library/system/myjavalib_stub_sources.zip",
+			".intermediates/mysdk/common_os/tmp/sdk_library/test/myjavalib_stub_sources.zip"),
+	)
+}
diff --git a/sdk/testing.go b/sdk/testing.go
index 00245ce..9e27201 100644
--- a/sdk/testing.go
+++ b/sdk/testing.go
@@ -90,6 +90,7 @@
 	// from java package
 	java.RegisterJavaBuildComponents(ctx)
 	java.RegisterAppBuildComponents(ctx)
+	java.RegisterSdkLibraryBuildComponents(ctx)
 	java.RegisterStubsBuildComponents(ctx)
 	java.RegisterSystemModulesBuildComponents(ctx)
 
@@ -338,14 +339,15 @@
 	}
 }
 
-// Check that the specified path is in the list of zips to merge with the intermediate zip.
-func checkMergeZip(expected string) snapshotBuildInfoChecker {
+// Check that the specified paths match the list of zips to merge with the intermediate zip.
+func checkMergeZips(expected ...string) snapshotBuildInfoChecker {
 	return func(info *snapshotBuildInfo) {
 		info.r.t.Helper()
 		if info.intermediateZip == "" {
 			info.r.t.Errorf("No intermediate zip file was created")
 		}
-		ensureListContains(info.r.t, info.mergeZips, expected)
+
+		info.r.AssertDeepEquals("mismatching merge zip files", expected, info.mergeZips)
 	}
 }