Merge "Improved formatting of a module list in a panic message."
diff --git a/Android.bp b/Android.bp
index 342ca4c..73fad25 100644
--- a/Android.bp
+++ b/Android.bp
@@ -289,6 +289,7 @@
         "soong-dexpreopt",
         "soong-genrule",
         "soong-java-config",
+        "soong-remoteexec",
         "soong-tradefed",
     ],
     srcs: [
@@ -492,6 +493,7 @@
     srcs: [
         "apex/androidmk.go",
         "apex/apex.go",
+        "apex/apex_singleton.go",
         "apex/builder.go",
         "apex/key.go",
         "apex/prebuilt.go",
diff --git a/README.md b/README.md
index 8b028a8..f1857f8 100644
--- a/README.md
+++ b/README.md
@@ -289,6 +289,9 @@
 * `["//visibility:public"]`: Anyone can use this module.
 * `["//visibility:private"]`: Only rules in the module's package (not its
 subpackages) can use this module.
+* `["//visibility:override"]`: Discards any rules inherited from defaults or a
+creating module. Can only be used at the beginning of a list of visibility
+rules.
 * `["//some/package:__pkg__", "//other/package:__pkg__"]`: Only modules in
 `some/package` and `other/package` (defined in `some/package/*.bp` and
 `other/package/*.bp`) have access to this module. Note that sub-packages do not
diff --git a/android/apex.go b/android/apex.go
index 9056c3d..30152db 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -421,21 +421,16 @@
 type DepNameToDepInfoMap map[string]ApexModuleDepInfo
 
 type ApexBundleDepsInfo struct {
-	minSdkVersion string
-	flatListPath  OutputPath
-	fullListPath  OutputPath
+	flatListPath OutputPath
+	fullListPath OutputPath
 }
 
-type ApexDepsInfoIntf interface {
-	MinSdkVersion() string
+type ApexBundleDepsInfoIntf interface {
+	Updatable() bool
 	FlatListPath() Path
 	FullListPath() Path
 }
 
-func (d *ApexBundleDepsInfo) MinSdkVersion() string {
-	return d.minSdkVersion
-}
-
 func (d *ApexBundleDepsInfo) FlatListPath() Path {
 	return d.flatListPath
 }
@@ -444,14 +439,10 @@
 	return d.fullListPath
 }
 
-var _ ApexDepsInfoIntf = (*ApexBundleDepsInfo)(nil)
-
 // Generate two module out files:
 // 1. FullList with transitive deps and their parents in the dep graph
 // 2. FlatList with a flat list of transitive deps
 func (d *ApexBundleDepsInfo) BuildDepsInfoLists(ctx ModuleContext, minSdkVersion string, depInfos DepNameToDepInfoMap) {
-	d.minSdkVersion = minSdkVersion
-
 	var fullContent strings.Builder
 	var flatContent strings.Builder
 
diff --git a/android/config.go b/android/config.go
index ee31c10..59118ce 100644
--- a/android/config.go
+++ b/android/config.go
@@ -897,27 +897,31 @@
 }
 
 // Expected format for apexJarValue = <apex name>:<jar name>
-func SplitApexJarPair(apexJarValue string) (string, string) {
-	var apexJarPair []string = strings.SplitN(apexJarValue, ":", 2)
-	if apexJarPair == nil || len(apexJarPair) != 2 {
-		panic(fmt.Errorf("malformed apexJarValue: %q, expected format: <apex>:<jar>",
-			apexJarValue))
+func SplitApexJarPair(ctx PathContext, str string) (string, string) {
+	pair := strings.SplitN(str, ":", 2)
+	if len(pair) == 2 {
+		return pair[0], pair[1]
+	} else {
+		reportPathErrorf(ctx, "malformed (apex, jar) pair: '%s', expected format: <apex>:<jar>", str)
+		return "error-apex", "error-jar"
 	}
-	return apexJarPair[0], apexJarPair[1]
 }
 
-func GetJarsFromApexJarPairs(apexJarPairs []string) []string {
+func GetJarsFromApexJarPairs(ctx PathContext, apexJarPairs []string) []string {
 	modules := make([]string, len(apexJarPairs))
 	for i, p := range apexJarPairs {
-		_, jar := SplitApexJarPair(p)
+		_, jar := SplitApexJarPair(ctx, p)
 		modules[i] = jar
 	}
 	return modules
 }
 
 func (c *config) BootJars() []string {
-	return append(GetJarsFromApexJarPairs(c.productVariables.BootJars),
-		GetJarsFromApexJarPairs(c.productVariables.UpdatableBootJars)...)
+	ctx := NullPathContext{Config{
+		config: c,
+	}}
+	return append(GetJarsFromApexJarPairs(ctx, c.productVariables.BootJars),
+		GetJarsFromApexJarPairs(ctx, c.productVariables.UpdatableBootJars)...)
 }
 
 func (c *config) DexpreoptGlobalConfig(ctx PathContext) ([]byte, error) {
diff --git a/android/env.go b/android/env.go
index 46bd3d6..c7c96d5 100644
--- a/android/env.go
+++ b/android/env.go
@@ -15,9 +15,11 @@
 package android
 
 import (
+	"fmt"
 	"os"
 	"os/exec"
 	"strings"
+	"syscall"
 
 	"android/soong/env"
 )
@@ -30,28 +32,59 @@
 // a manifest regeneration.
 
 var originalEnv map[string]string
-var SoongDelveListen string
-var SoongDelvePath string
+var soongDelveListen string
+var soongDelvePath string
+var soongDelveEnv []string
 
 func init() {
 	// Delve support needs to read this environment variable very early, before NewConfig has created a way to
 	// access originalEnv with dependencies.  Store the value where soong_build can find it, it will manually
 	// ensure the dependencies are created.
-	SoongDelveListen = os.Getenv("SOONG_DELVE")
-	SoongDelvePath, _ = exec.LookPath("dlv")
+	soongDelveListen = os.Getenv("SOONG_DELVE")
+	soongDelvePath, _ = exec.LookPath("dlv")
 
 	originalEnv = make(map[string]string)
+	soongDelveEnv = []string{}
 	for _, env := range os.Environ() {
 		idx := strings.IndexRune(env, '=')
 		if idx != -1 {
 			originalEnv[env[:idx]] = env[idx+1:]
+			if env[:idx] != "SOONG_DELVE" {
+				soongDelveEnv = append(soongDelveEnv, env)
+			}
 		}
 	}
+
 	// Clear the environment to prevent use of os.Getenv(), which would not provide dependencies on environment
 	// variable values.  The environment is available through ctx.Config().Getenv, ctx.Config().IsEnvTrue, etc.
 	os.Clearenv()
 }
 
+func ReexecWithDelveMaybe() {
+	if soongDelveListen == "" {
+		return
+	}
+
+	if soongDelvePath == "" {
+		fmt.Fprintln(os.Stderr, "SOONG_DELVE is set but failed to find dlv")
+		os.Exit(1)
+	}
+	dlvArgv := []string{
+		soongDelvePath,
+		"--listen=:" + soongDelveListen,
+		"--headless=true",
+		"--api-version=2",
+		"exec",
+		os.Args[0],
+		"--",
+	}
+	dlvArgv = append(dlvArgv, os.Args[1:]...)
+	os.Chdir(absSrcDir)
+	syscall.Exec(soongDelvePath, dlvArgv, soongDelveEnv)
+	fmt.Fprintln(os.Stderr, "exec() failed while trying to reexec with Delve")
+	os.Exit(1)
+}
+
 // getenv checks either os.Getenv or originalEnv so that it works before or after the init()
 // function above.  It doesn't add any dependencies on the environment variable, so it should
 // only be used for values that won't change.  For values that might change use ctx.Config().Getenv.
diff --git a/android/module.go b/android/module.go
index e431ada..82321f4 100644
--- a/android/module.go
+++ b/android/module.go
@@ -104,11 +104,15 @@
 type BaseModuleContext interface {
 	EarlyModuleContext
 
+	blueprintBaseModuleContext() blueprint.BaseModuleContext
+
 	OtherModuleName(m blueprint.Module) string
 	OtherModuleDir(m blueprint.Module) string
 	OtherModuleErrorf(m blueprint.Module, fmt string, args ...interface{})
 	OtherModuleDependencyTag(m blueprint.Module) blueprint.DependencyTag
 	OtherModuleExists(name string) bool
+	OtherModuleDependencyVariantExists(variations []blueprint.Variation, name string) bool
+	OtherModuleReverseDependencyVariantExists(name string) bool
 	OtherModuleType(m blueprint.Module) string
 
 	GetDirectDepsWithTag(tag blueprint.DependencyTag) []Module
@@ -257,9 +261,6 @@
 	// Get information about the properties that can contain visibility rules.
 	visibilityProperties() []visibilityProperty
 
-	// Get the visibility rules that control the visibility of this module.
-	visibility() []string
-
 	RequiredModuleNames() []string
 	HostRequiredModuleNames() []string
 	TargetRequiredModuleNames() []string
@@ -331,6 +332,8 @@
 	//  ["//visibility:public"]: Anyone can use this module.
 	//  ["//visibility:private"]: Only rules in the module's package (not its subpackages) can use
 	//      this module.
+	//  ["//visibility:override"]: Discards any rules inherited from defaults or a creating module.
+	//      Can only be used at the beginning of a list of visibility rules.
 	//  ["//some/package:__pkg__", "//other/package:__pkg__"]: Only modules in some/package and
 	//      other/package (defined in some/package/*.bp and other/package/*.bp) have access to
 	//      this module. Note that sub-packages do not have access to the rule; for example,
@@ -807,15 +810,6 @@
 	return m.visibilityPropertyInfo
 }
 
-func (m *ModuleBase) visibility() []string {
-	// The soong_namespace module does not initialize the primaryVisibilityProperty.
-	if m.primaryVisibilityProperty != nil {
-		return m.primaryVisibilityProperty.getStrings()
-	} else {
-		return nil
-	}
-}
-
 func (m *ModuleBase) Target() Target {
 	return m.commonProperties.CompileTarget
 }
@@ -908,6 +902,13 @@
 	return Bool(m.commonProperties.System_ext_specific)
 }
 
+// RequiresStableAPIs returns true if the module will be installed to a partition that may
+// be updated separately from the system image.
+func (m *ModuleBase) RequiresStableAPIs(ctx BaseModuleContext) bool {
+	return m.SocSpecific() || m.DeviceSpecific() ||
+		(m.ProductSpecific() && ctx.Config().EnforceProductPartitionInterface())
+}
+
 func (m *ModuleBase) PartitionTag(config DeviceConfig) string {
 	partition := "system"
 	if m.SocSpecific() {
@@ -1434,6 +1435,12 @@
 	return b.bp.OtherModuleDependencyTag(m)
 }
 func (b *baseModuleContext) OtherModuleExists(name string) bool { return b.bp.OtherModuleExists(name) }
+func (b *baseModuleContext) OtherModuleDependencyVariantExists(variations []blueprint.Variation, name string) bool {
+	return b.bp.OtherModuleDependencyVariantExists(variations, name)
+}
+func (b *baseModuleContext) OtherModuleReverseDependencyVariantExists(name string) bool {
+	return b.bp.OtherModuleReverseDependencyVariantExists(name)
+}
 func (b *baseModuleContext) OtherModuleType(m blueprint.Module) string {
 	return b.bp.OtherModuleType(m)
 }
@@ -1442,6 +1449,10 @@
 	return b.bp.GetDirectDepWithTag(name, tag)
 }
 
+func (b *baseModuleContext) blueprintBaseModuleContext() blueprint.BaseModuleContext {
+	return b.bp
+}
+
 type moduleContext struct {
 	bp blueprint.ModuleContext
 	baseModuleContext
@@ -2363,4 +2374,10 @@
 	Classes           []string `json:"class,omitempty"`
 	Installed_paths   []string `json:"installed,omitempty"`
 	SrcJars           []string `json:"srcjars,omitempty"`
+	Paths             []string `json:"path,omitempty"`
+}
+
+func CheckBlueprintSyntax(ctx BaseModuleContext, filename string, contents string) []error {
+	bpctx := ctx.blueprintBaseModuleContext()
+	return blueprint.CheckBlueprintSyntax(bpctx.ModuleFactories(), filename, contents)
 }
diff --git a/android/neverallow.go b/android/neverallow.go
index cf09792..9e075b7 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -55,6 +55,7 @@
 	AddNeverAllowRules(createMediaRules()...)
 	AddNeverAllowRules(createJavaDeviceForHostRules()...)
 	AddNeverAllowRules(createCcSdkVariantRules()...)
+	AddNeverAllowRules(createUncompressDexRules()...)
 }
 
 // Add a NeverAllow rule to the set of rules to apply.
@@ -141,6 +142,7 @@
 		"external/icu",
 		"external/okhttp",
 		"external/wycheproof",
+		"prebuilts",
 	}
 
 	// Core library constraints. The sdk_version: "none" can only be used in core library projects.
@@ -209,6 +211,15 @@
 	}
 }
 
+func createUncompressDexRules() []Rule {
+	return []Rule{
+		NeverAllow().
+			NotIn("art").
+			WithMatcher("uncompress_dex", isSetMatcherInstance).
+			Because("uncompress_dex is only allowed for certain jars for test in art."),
+	}
+}
+
 func neverallowMutator(ctx BottomUpMutatorContext) {
 	m, ok := ctx.Module().(Module)
 	if !ok {
diff --git a/android/neverallow_test.go b/android/neverallow_test.go
index 2fc42e3..85c8c59 100644
--- a/android/neverallow_test.go
+++ b/android/neverallow_test.go
@@ -303,6 +303,29 @@
 			`module "outside_whitelist": violates neverallow`,
 		},
 	},
+	{
+		name: "uncompress_dex inside art",
+		fs: map[string][]byte{
+			"art/Android.bp": []byte(`
+				java_library {
+					name: "inside_art_libraries",
+					uncompress_dex: true,
+				}`),
+		},
+	},
+	{
+		name: "uncompress_dex outside art",
+		fs: map[string][]byte{
+			"other/Android.bp": []byte(`
+				java_library {
+					name: "outside_art_libraries",
+					uncompress_dex: true,
+				}`),
+		},
+		expectedErrors: []string{
+			"module \"outside_art_libraries\": violates neverallow",
+		},
+	},
 }
 
 func TestNeverallow(t *testing.T) {
@@ -396,8 +419,9 @@
 }
 
 type mockJavaLibraryProperties struct {
-	Libs        []string
-	Sdk_version *string
+	Libs           []string
+	Sdk_version    *string
+	Uncompress_dex *bool
 }
 
 type mockJavaLibraryModule struct {
diff --git a/android/paths.go b/android/paths.go
index 8bb9a96..3ad27ac 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -43,6 +43,14 @@
 var _ PathContext = SingletonContext(nil)
 var _ PathContext = ModuleContext(nil)
 
+// "Null" path context is a minimal path context for a given config.
+type NullPathContext struct {
+	config Config
+}
+
+func (NullPathContext) AddNinjaFileDeps(...string) {}
+func (ctx NullPathContext) Config() Config         { return ctx.config }
+
 type ModuleInstallPathContext interface {
 	BaseModuleContext
 
@@ -477,6 +485,15 @@
 	return firstUniquePathsList(list)
 }
 
+// SortedUniquePaths returns what its name says
+func SortedUniquePaths(list Paths) Paths {
+	unique := FirstUniquePaths(list)
+	sort.Slice(unique, func(i, j int) bool {
+		return unique[i].String() < unique[j].String()
+	})
+	return unique
+}
+
 func firstUniquePathsList(list Paths) Paths {
 	k := 0
 outer:
diff --git a/android/prebuilt.go b/android/prebuilt.go
index 82745a4..a29ec91 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -52,6 +52,9 @@
 
 	SourceExists bool `blueprint:"mutated"`
 	UsePrebuilt  bool `blueprint:"mutated"`
+
+	// Set if the module has been renamed to remove the "prebuilt_" prefix.
+	PrebuiltRenamedToSource bool `blueprint:"mutated"`
 }
 
 type Prebuilt struct {
@@ -69,6 +72,10 @@
 	p.properties.Prefer = proptools.BoolPtr(true)
 }
 
+func (p *Prebuilt) Prefer() bool {
+	return proptools.Bool(p.properties.Prefer)
+}
+
 // The below source-related functions and the srcs, src fields are based on an assumption that
 // prebuilt modules have a static source property at the moment. Currently there is only one
 // exception, android_app_import, which chooses a source file depending on the product's DPI
@@ -184,25 +191,38 @@
 }
 
 func RegisterPrebuiltsPreArchMutators(ctx RegisterMutatorsContext) {
-	ctx.BottomUp("prebuilts", PrebuiltMutator).Parallel()
+	ctx.BottomUp("prebuilt_rename", PrebuiltRenameMutator).Parallel()
 }
 
 func RegisterPrebuiltsPostDepsMutators(ctx RegisterMutatorsContext) {
+	ctx.BottomUp("prebuilt_source", PrebuiltSourceDepsMutator).Parallel()
 	ctx.TopDown("prebuilt_select", PrebuiltSelectModuleMutator).Parallel()
 	ctx.BottomUp("prebuilt_postdeps", PrebuiltPostDepsMutator).Parallel()
 }
 
-// PrebuiltMutator ensures that there is always a module with an undecorated name, and marks
-// prebuilt modules that have both a prebuilt and a source module.
-func PrebuiltMutator(ctx BottomUpMutatorContext) {
+// PrebuiltRenameMutator ensures that there always is a module with an
+// undecorated name.
+func PrebuiltRenameMutator(ctx BottomUpMutatorContext) {
+	if m, ok := ctx.Module().(PrebuiltInterface); ok && m.Prebuilt() != nil {
+		name := m.base().BaseModuleName()
+		if !ctx.OtherModuleExists(name) {
+			ctx.Rename(name)
+			m.Prebuilt().properties.PrebuiltRenamedToSource = true
+		}
+	}
+}
+
+// PrebuiltSourceDepsMutator adds dependencies to the prebuilt module from the
+// corresponding source module, if one exists for the same variant.
+func PrebuiltSourceDepsMutator(ctx BottomUpMutatorContext) {
 	if m, ok := ctx.Module().(PrebuiltInterface); ok && m.Prebuilt() != nil {
 		p := m.Prebuilt()
-		name := m.base().BaseModuleName()
-		if ctx.OtherModuleExists(name) {
-			ctx.AddReverseDependency(ctx.Module(), PrebuiltDepTag, name)
-			p.properties.SourceExists = true
-		} else {
-			ctx.Rename(name)
+		if !p.properties.PrebuiltRenamedToSource {
+			name := m.base().BaseModuleName()
+			if ctx.OtherModuleReverseDependencyVariantExists(name) {
+				ctx.AddReverseDependency(ctx.Module(), PrebuiltDepTag, name)
+				p.properties.SourceExists = true
+			}
 		}
 	}
 }
diff --git a/android/prebuilt_etc.go b/android/prebuilt_etc.go
index 3dea6d8..f0c0767 100644
--- a/android/prebuilt_etc.go
+++ b/android/prebuilt_etc.go
@@ -49,6 +49,9 @@
 
 	// Whether this module is directly installable to one of the partitions. Default: true.
 	Installable *bool
+
+	// Install symlinks to the installed file.
+	Symlinks []string `android:"arch_variant"`
 }
 
 type PrebuiltEtcModule interface {
@@ -201,10 +204,13 @@
 				entries.SetString("LOCAL_MODULE_TAGS", "optional")
 				entries.SetString("LOCAL_MODULE_PATH", p.installDirPath.ToMakePath().String())
 				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.outputFilePath.Base())
+				if len(p.properties.Symlinks) > 0 {
+					entries.AddStrings("LOCAL_MODULE_SYMLINKS", p.properties.Symlinks...)
+				}
 				entries.SetString("LOCAL_UNINSTALLABLE_MODULE", strconv.FormatBool(!p.Installable()))
 				if p.additionalDependencies != nil {
 					for _, path := range *p.additionalDependencies {
-						entries.SetString("LOCAL_ADDITIONAL_DEPENDENCIES", path.String())
+						entries.AddStrings("LOCAL_ADDITIONAL_DEPENDENCIES", path.String())
 					}
 				}
 			},
diff --git a/android/prebuilt_test.go b/android/prebuilt_test.go
index 8ff5c40..b568f78 100644
--- a/android/prebuilt_test.go
+++ b/android/prebuilt_test.go
@@ -24,7 +24,7 @@
 var prebuiltsTests = []struct {
 	name     string
 	modules  string
-	prebuilt bool
+	prebuilt []OsClass
 }{
 	{
 		name: "no prebuilt",
@@ -32,7 +32,7 @@
 			source {
 				name: "bar",
 			}`,
-		prebuilt: false,
+		prebuilt: nil,
 	},
 	{
 		name: "no source prebuilt not preferred",
@@ -42,7 +42,7 @@
 				prefer: false,
 				srcs: ["prebuilt_file"],
 			}`,
-		prebuilt: true,
+		prebuilt: []OsClass{Device, Host},
 	},
 	{
 		name: "no source prebuilt preferred",
@@ -52,7 +52,7 @@
 				prefer: true,
 				srcs: ["prebuilt_file"],
 			}`,
-		prebuilt: true,
+		prebuilt: []OsClass{Device, Host},
 	},
 	{
 		name: "prebuilt not preferred",
@@ -60,13 +60,13 @@
 			source {
 				name: "bar",
 			}
-			
+
 			prebuilt {
 				name: "bar",
 				prefer: false,
 				srcs: ["prebuilt_file"],
 			}`,
-		prebuilt: false,
+		prebuilt: nil,
 	},
 	{
 		name: "prebuilt preferred",
@@ -74,13 +74,13 @@
 			source {
 				name: "bar",
 			}
-			
+
 			prebuilt {
 				name: "bar",
 				prefer: true,
 				srcs: ["prebuilt_file"],
 			}`,
-		prebuilt: true,
+		prebuilt: []OsClass{Device, Host},
 	},
 	{
 		name: "prebuilt no file not preferred",
@@ -88,12 +88,12 @@
 			source {
 				name: "bar",
 			}
-			
+
 			prebuilt {
 				name: "bar",
 				prefer: false,
 			}`,
-		prebuilt: false,
+		prebuilt: nil,
 	},
 	{
 		name: "prebuilt no file preferred",
@@ -101,12 +101,12 @@
 			source {
 				name: "bar",
 			}
-			
+
 			prebuilt {
 				name: "bar",
 				prefer: true,
 			}`,
-		prebuilt: false,
+		prebuilt: nil,
 	},
 	{
 		name: "prebuilt file from filegroup preferred",
@@ -120,7 +120,40 @@
 				prefer: true,
 				srcs: [":fg"],
 			}`,
-		prebuilt: true,
+		prebuilt: []OsClass{Device, Host},
+	},
+	{
+		name: "prebuilt module for device only",
+		modules: `
+			source {
+				name: "bar",
+			}
+
+			prebuilt {
+				name: "bar",
+				host_supported: false,
+				prefer: true,
+				srcs: ["prebuilt_file"],
+			}`,
+		prebuilt: []OsClass{Device},
+	},
+	{
+		name: "prebuilt file for host only",
+		modules: `
+			source {
+				name: "bar",
+			}
+
+			prebuilt {
+				name: "bar",
+				prefer: true,
+				target: {
+					host: {
+						srcs: ["prebuilt_file"],
+					},
+				},
+			}`,
+		prebuilt: []OsClass{Host},
 	},
 }
 
@@ -138,9 +171,9 @@
 					deps: [":bar"],
 				}
 				` + test.modules
-			config := TestConfig(buildDir, nil, bp, fs)
+			config := TestArchConfig(buildDir, nil, bp, fs)
 
-			ctx := NewTestContext()
+			ctx := NewTestArchContext()
 			registerTestPrebuiltBuildComponents(ctx)
 			ctx.RegisterModuleType("filegroup", FileGroupFactory)
 			ctx.Register(config)
@@ -150,61 +183,71 @@
 			_, errs = ctx.PrepareBuildActions(config)
 			FailIfErrored(t, errs)
 
-			foo := ctx.ModuleForTests("foo", "")
+			for _, variant := range ctx.ModuleVariantsForTests("foo") {
+				foo := ctx.ModuleForTests("foo", variant)
+				t.Run(foo.Module().Target().Os.Class.String(), func(t *testing.T) {
+					var dependsOnSourceModule, dependsOnPrebuiltModule bool
+					ctx.VisitDirectDeps(foo.Module(), func(m blueprint.Module) {
+						if _, ok := m.(*sourceModule); ok {
+							dependsOnSourceModule = true
+						}
+						if p, ok := m.(*prebuiltModule); ok {
+							dependsOnPrebuiltModule = true
+							if !p.Prebuilt().properties.UsePrebuilt {
+								t.Errorf("dependency on prebuilt module not marked used")
+							}
+						}
+					})
 
-			var dependsOnSourceModule, dependsOnPrebuiltModule bool
-			ctx.VisitDirectDeps(foo.Module(), func(m blueprint.Module) {
-				if _, ok := m.(*sourceModule); ok {
-					dependsOnSourceModule = true
-				}
-				if p, ok := m.(*prebuiltModule); ok {
-					dependsOnPrebuiltModule = true
-					if !p.Prebuilt().properties.UsePrebuilt {
-						t.Errorf("dependency on prebuilt module not marked used")
+					deps := foo.Module().(*sourceModule).deps
+					if deps == nil || len(deps) != 1 {
+						t.Errorf("deps does not have single path, but is %v", deps)
 					}
-				}
-			})
+					var usingSourceFile, usingPrebuiltFile bool
+					if deps[0].String() == "source_file" {
+						usingSourceFile = true
+					}
+					if deps[0].String() == "prebuilt_file" {
+						usingPrebuiltFile = true
+					}
 
-			deps := foo.Module().(*sourceModule).deps
-			if deps == nil || len(deps) != 1 {
-				t.Errorf("deps does not have single path, but is %v", deps)
-			}
-			var usingSourceFile, usingPrebuiltFile bool
-			if deps[0].String() == "source_file" {
-				usingSourceFile = true
-			}
-			if deps[0].String() == "prebuilt_file" {
-				usingPrebuiltFile = true
-			}
+					prebuilt := false
+					for _, os := range test.prebuilt {
+						if os == foo.Module().Target().Os.Class {
+							prebuilt = true
+						}
+					}
 
-			if test.prebuilt {
-				if !dependsOnPrebuiltModule {
-					t.Errorf("doesn't depend on prebuilt module")
-				}
-				if !usingPrebuiltFile {
-					t.Errorf("doesn't use prebuilt_file")
-				}
+					if prebuilt {
+						if !dependsOnPrebuiltModule {
+							t.Errorf("doesn't depend on prebuilt module")
+						}
+						if !usingPrebuiltFile {
+							t.Errorf("doesn't use prebuilt_file")
+						}
 
-				if dependsOnSourceModule {
-					t.Errorf("depends on source module")
-				}
-				if usingSourceFile {
-					t.Errorf("using source_file")
-				}
-			} else {
-				if dependsOnPrebuiltModule {
-					t.Errorf("depends on prebuilt module")
-				}
-				if usingPrebuiltFile {
-					t.Errorf("using prebuilt_file")
-				}
+						if dependsOnSourceModule {
+							t.Errorf("depends on source module")
+						}
+						if usingSourceFile {
+							t.Errorf("using source_file")
+						}
+					} else {
+						if dependsOnPrebuiltModule {
+							t.Errorf("depends on prebuilt module")
+						}
+						if usingPrebuiltFile {
+							t.Errorf("using prebuilt_file")
+						}
 
-				if !dependsOnSourceModule {
-					t.Errorf("doesn't depend on source module")
-				}
-				if !usingSourceFile {
-					t.Errorf("doesn't use source_file")
-				}
+						if !dependsOnSourceModule {
+							t.Errorf("doesn't depend on source module")
+						}
+						if !usingSourceFile {
+							t.Errorf("doesn't use source_file")
+						}
+					}
+				})
 			}
 		})
 	}
@@ -221,7 +264,7 @@
 	ModuleBase
 	prebuilt   Prebuilt
 	properties struct {
-		Srcs []string `android:"path"`
+		Srcs []string `android:"path,arch_variant"`
 	}
 	src Path
 }
@@ -230,7 +273,7 @@
 	m := &prebuiltModule{}
 	m.AddProperties(&m.properties)
 	InitPrebuiltModule(m, &m.properties.Srcs)
-	InitAndroidModule(m)
+	InitAndroidArchModule(m, HostAndDeviceDefault, MultilibCommon)
 	return m
 }
 
@@ -260,7 +303,7 @@
 type sourceModule struct {
 	ModuleBase
 	properties struct {
-		Deps []string `android:"path"`
+		Deps []string `android:"path,arch_variant"`
 	}
 	dependsOnSourceModule, dependsOnPrebuiltModule bool
 	deps                                           Paths
@@ -270,7 +313,7 @@
 func newSourceModule() Module {
 	m := &sourceModule{}
 	m.AddProperties(&m.properties)
-	InitAndroidModule(m)
+	InitAndroidArchModule(m, HostAndDeviceDefault, MultilibCommon)
 	return m
 }
 
diff --git a/android/visibility.go b/android/visibility.go
index 1e3b91d..68da1c4 100644
--- a/android/visibility.go
+++ b/android/visibility.go
@@ -140,7 +140,7 @@
 }
 
 func (r packageRule) String() string {
-	return fmt.Sprintf("//%s:__pkg__", r.pkg)
+	return fmt.Sprintf("//%s", r.pkg) // :__pkg__ is the default, so skip it.
 }
 
 // A subpackagesRule is a visibility rule that matches modules in a specific package (i.e.
@@ -245,7 +245,7 @@
 		return
 	}
 
-	for _, v := range visibility {
+	for i, v := range visibility {
 		ok, pkg, name := splitRule(ctx, v, currentPkg, property)
 		if !ok {
 			continue
@@ -257,11 +257,18 @@
 			case "legacy_public":
 				ctx.PropertyErrorf(property, "//visibility:legacy_public must not be used")
 				continue
+			case "override":
+				// This keyword does not create a rule so pretend it does not exist.
+				ruleCount -= 1
 			default:
 				ctx.PropertyErrorf(property, "unrecognized visibility rule %q", v)
 				continue
 			}
-			if ruleCount != 1 {
+			if name == "override" {
+				if i != 0 {
+					ctx.PropertyErrorf(property, `"%v" may only be used at the start of the visibility rules`, v)
+				}
+			} else if ruleCount != 1 {
 				ctx.PropertyErrorf(property, "cannot mix %q with any other visibility rules", v)
 				continue
 			}
@@ -327,6 +334,14 @@
 			case "public":
 				r = publicRule{}
 				hasPublicRule = true
+			case "override":
+				// Discard all preceding rules and any state based on them.
+				rules = nil
+				hasPrivateRule = false
+				hasPublicRule = false
+				hasNonPrivateRule = false
+				// This does not actually create a rule so continue onto the next rule.
+				continue
 			}
 		} else {
 			switch name {
@@ -481,6 +496,24 @@
 
 	rule := effectiveVisibilityRules(ctx.Config(), qualified)
 
+	// Modules are implicitly visible to other modules in the same package,
+	// without checking the visibility rules. Here we need to add that visibility
+	// explicitly.
+	if rule != nil && !rule.matches(qualified) {
+		if len(rule) == 1 {
+			if _, ok := rule[0].(privateRule); ok {
+				// If the rule is //visibility:private we can't append another
+				// visibility to it. Semantically we need to convert it to a package
+				// visibility rule for the location where the result is used, but since
+				// modules are implicitly visible within the package we get the same
+				// result without any rule at all, so just make it an empty list to be
+				// appended below.
+				rule = compositeRule{}
+			}
+		}
+		rule = append(rule, packageRule{dir})
+	}
+
 	return rule.Strings()
 }
 
diff --git a/android/visibility_test.go b/android/visibility_test.go
index 4cf41a6..ca09345 100644
--- a/android/visibility_test.go
+++ b/android/visibility_test.go
@@ -636,6 +636,177 @@
 		},
 	},
 	{
+		name: "//visibility:private override //visibility:public",
+		fs: map[string][]byte{
+			"top/Blueprints": []byte(`
+				mock_defaults {
+					name: "libexample_defaults",
+					visibility: ["//visibility:public"],
+				}
+				mock_library {
+					name: "libexample",
+					visibility: ["//visibility:private"],
+					defaults: ["libexample_defaults"],
+				}`),
+		},
+		expectedErrors: []string{
+			`module "libexample": visibility: cannot mix "//visibility:private" with any other visibility rules`,
+		},
+	},
+	{
+		name: "//visibility:public override //visibility:private",
+		fs: map[string][]byte{
+			"top/Blueprints": []byte(`
+				mock_defaults {
+					name: "libexample_defaults",
+					visibility: ["//visibility:private"],
+				}
+				mock_library {
+					name: "libexample",
+					visibility: ["//visibility:public"],
+					defaults: ["libexample_defaults"],
+				}`),
+		},
+		expectedErrors: []string{
+			`module "libexample": visibility: cannot mix "//visibility:private" with any other visibility rules`,
+		},
+	},
+	{
+		name: "//visibility:override must be first in the list",
+		fs: map[string][]byte{
+			"top/Blueprints": []byte(`
+				mock_library {
+					name: "libexample",
+					visibility: ["//other", "//visibility:override", "//namespace"],
+				}`),
+		},
+		expectedErrors: []string{
+			`module "libexample": visibility: "//visibility:override" may only be used at the start of the visibility rules`,
+		},
+	},
+	{
+		name: "//visibility:override discards //visibility:private",
+		fs: map[string][]byte{
+			"top/Blueprints": []byte(`
+				mock_defaults {
+					name: "libexample_defaults",
+					visibility: ["//visibility:private"],
+				}
+				mock_library {
+					name: "libexample",
+					// Make this visibility to //other but not //visibility:private
+					visibility: ["//visibility:override", "//other"],
+					defaults: ["libexample_defaults"],
+				}`),
+			"other/Blueprints": []byte(`
+				mock_library {
+					name: "libother",
+					deps: ["libexample"],
+				}`),
+		},
+	},
+	{
+		name: "//visibility:override discards //visibility:public",
+		fs: map[string][]byte{
+			"top/Blueprints": []byte(`
+				mock_defaults {
+					name: "libexample_defaults",
+					visibility: ["//visibility:public"],
+				}
+				mock_library {
+					name: "libexample",
+					// Make this visibility to //other but not //visibility:public
+					visibility: ["//visibility:override", "//other"],
+					defaults: ["libexample_defaults"],
+				}`),
+			"other/Blueprints": []byte(`
+				mock_library {
+					name: "libother",
+					deps: ["libexample"],
+				}`),
+			"namespace/Blueprints": []byte(`
+				mock_library {
+					name: "libnamespace",
+					deps: ["libexample"],
+				}`),
+		},
+		expectedErrors: []string{
+			`module "libnamespace" variant "android_common": depends on //top:libexample which is not visible to this module`,
+		},
+	},
+	{
+		name: "//visibility:override discards defaults supplied rules",
+		fs: map[string][]byte{
+			"top/Blueprints": []byte(`
+				mock_defaults {
+					name: "libexample_defaults",
+					visibility: ["//namespace"],
+				}
+				mock_library {
+					name: "libexample",
+					// Make this visibility to //other but not //namespace
+					visibility: ["//visibility:override", "//other"],
+					defaults: ["libexample_defaults"],
+				}`),
+			"other/Blueprints": []byte(`
+				mock_library {
+					name: "libother",
+					deps: ["libexample"],
+				}`),
+			"namespace/Blueprints": []byte(`
+				mock_library {
+					name: "libnamespace",
+					deps: ["libexample"],
+				}`),
+		},
+		expectedErrors: []string{
+			`module "libnamespace" variant "android_common": depends on //top:libexample which is not visible to this module`,
+		},
+	},
+	{
+		name: "//visibility:override can override //visibility:public with //visibility:private",
+		fs: map[string][]byte{
+			"top/Blueprints": []byte(`
+				mock_defaults {
+					name: "libexample_defaults",
+					visibility: ["//visibility:public"],
+				}
+				mock_library {
+					name: "libexample",
+					visibility: ["//visibility:override", "//visibility:private"],
+					defaults: ["libexample_defaults"],
+				}`),
+			"namespace/Blueprints": []byte(`
+				mock_library {
+					name: "libnamespace",
+					deps: ["libexample"],
+				}`),
+		},
+		expectedErrors: []string{
+			`module "libnamespace" variant "android_common": depends on //top:libexample which is not visible to this module`,
+		},
+	},
+	{
+		name: "//visibility:override can override //visibility:private with //visibility:public",
+		fs: map[string][]byte{
+			"top/Blueprints": []byte(`
+				mock_defaults {
+					name: "libexample_defaults",
+					visibility: ["//visibility:private"],
+				}
+				mock_library {
+					name: "libexample",
+					visibility: ["//visibility:override", "//visibility:public"],
+					defaults: ["libexample_defaults"],
+				}`),
+			"namespace/Blueprints": []byte(`
+				mock_library {
+					name: "libnamespace",
+					deps: ["libexample"],
+				}`),
+		},
+	},
+	{
 		name: "//visibility:private mixed with itself",
 		fs: map[string][]byte{
 			"top/Blueprints": []byte(`
diff --git a/androidmk/androidmk/android.go b/androidmk/androidmk/android.go
index 5a62324..eaf06c5 100644
--- a/androidmk/androidmk/android.go
+++ b/androidmk/androidmk/android.go
@@ -40,26 +40,31 @@
 	append  bool
 }
 
+var trueValue = &bpparser.Bool{
+	Value: true,
+}
+
 var rewriteProperties = map[string](func(variableAssignmentContext) error){
 	// custom functions
-	"LOCAL_32_BIT_ONLY":           local32BitOnly,
-	"LOCAL_AIDL_INCLUDES":         localAidlIncludes,
-	"LOCAL_ASSET_DIR":             localizePathList("asset_dirs"),
-	"LOCAL_C_INCLUDES":            localIncludeDirs,
-	"LOCAL_EXPORT_C_INCLUDE_DIRS": exportIncludeDirs,
-	"LOCAL_JARJAR_RULES":          localizePath("jarjar_rules"),
-	"LOCAL_LDFLAGS":               ldflags,
-	"LOCAL_MODULE_CLASS":          prebuiltClass,
-	"LOCAL_MODULE_STEM":           stem,
-	"LOCAL_MODULE_HOST_OS":        hostOs,
-	"LOCAL_RESOURCE_DIR":          localizePathList("resource_dirs"),
-	"LOCAL_SANITIZE":              sanitize(""),
-	"LOCAL_SANITIZE_DIAG":         sanitize("diag."),
-	"LOCAL_STRIP_MODULE":          strip(),
-	"LOCAL_CFLAGS":                cflags,
-	"LOCAL_UNINSTALLABLE_MODULE":  invert("installable"),
-	"LOCAL_PROGUARD_ENABLED":      proguardEnabled,
-	"LOCAL_MODULE_PATH":           prebuiltModulePath,
+	"LOCAL_32_BIT_ONLY":                    local32BitOnly,
+	"LOCAL_AIDL_INCLUDES":                  localAidlIncludes,
+	"LOCAL_ASSET_DIR":                      localizePathList("asset_dirs"),
+	"LOCAL_C_INCLUDES":                     localIncludeDirs,
+	"LOCAL_EXPORT_C_INCLUDE_DIRS":          exportIncludeDirs,
+	"LOCAL_JARJAR_RULES":                   localizePath("jarjar_rules"),
+	"LOCAL_LDFLAGS":                        ldflags,
+	"LOCAL_MODULE_CLASS":                   prebuiltClass,
+	"LOCAL_MODULE_STEM":                    stem,
+	"LOCAL_MODULE_HOST_OS":                 hostOs,
+	"LOCAL_RESOURCE_DIR":                   localizePathList("resource_dirs"),
+	"LOCAL_SANITIZE":                       sanitize(""),
+	"LOCAL_SANITIZE_DIAG":                  sanitize("diag."),
+	"LOCAL_STRIP_MODULE":                   strip(),
+	"LOCAL_CFLAGS":                         cflags,
+	"LOCAL_UNINSTALLABLE_MODULE":           invert("installable"),
+	"LOCAL_PROGUARD_ENABLED":               proguardEnabled,
+	"LOCAL_MODULE_PATH":                    prebuiltModulePath,
+	"LOCAL_REPLACE_PREBUILT_APK_INSTALLED": prebuiltPreprocessed,
 
 	// composite functions
 	"LOCAL_MODULE_TAGS": includeVariableIf(bpVariable{"tags", bpparser.ListType}, not(valueDumpEquals("optional"))),
@@ -495,10 +500,6 @@
 		Value: false,
 	}
 
-	trueValue := &bpparser.Bool{
-		Value: true,
-	}
-
 	if inList("windows") {
 		err = setVariable(ctx.file, ctx.append, "target.windows", "enabled", trueValue, true)
 	}
@@ -704,6 +705,11 @@
 	return nil
 }
 
+func prebuiltPreprocessed(ctx variableAssignmentContext) error {
+	ctx.mkvalue = ctx.mkvalue.Clone()
+	return setVariable(ctx.file, false, ctx.prefix, "preprocessed", trueValue, true)
+}
+
 func cflags(ctx variableAssignmentContext) error {
 	// The Soong replacement for CFLAGS doesn't need the same extra escaped quotes that were present in Make
 	ctx.mkvalue = ctx.mkvalue.Clone()
diff --git a/androidmk/androidmk/androidmk_test.go b/androidmk/androidmk/androidmk_test.go
index d9bde94..54bd586 100644
--- a/androidmk/androidmk/androidmk_test.go
+++ b/androidmk/androidmk/androidmk_test.go
@@ -1342,6 +1342,31 @@
 `,
 	},
 	{
+		desc: "android_test_import prebuilt",
+		in: `
+		include $(CLEAR_VARS)
+		LOCAL_MODULE := foo
+		LOCAL_SRC_FILES := foo.apk
+		LOCAL_MODULE_CLASS := APPS
+		LOCAL_MODULE_TAGS := tests
+		LOCAL_MODULE_SUFFIX := .apk
+		LOCAL_CERTIFICATE := PRESIGNED
+		LOCAL_REPLACE_PREBUILT_APK_INSTALLED := $(LOCAL_PATH)/foo.apk
+		LOCAL_COMPATIBILITY_SUITE := cts
+		include $(BUILD_PREBUILT)
+		`,
+		expected: `
+android_test_import {
+	name: "foo",
+	srcs: ["foo.apk"],
+
+	certificate: "PRESIGNED",
+	preprocessed: true,
+	test_suites: ["cts"],
+}
+`,
+	},
+	{
 		desc: "undefined_boolean_var",
 		in: `
 include $(CLEAR_VARS)
diff --git a/apex/androidmk.go b/apex/androidmk.go
index 2c5407c..9321ad2 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -78,6 +78,8 @@
 		}
 	}
 
+	seenDataOutPaths := make(map[string]bool)
+
 	for _, fi := range a.filesInfo {
 		if cc, ok := fi.module.(*cc.Module); ok && cc.Properties.HideFromMake {
 			continue
@@ -112,14 +114,25 @@
 		pathWhenActivated := filepath.Join("$(PRODUCT_OUT)", "apex", apexName, fi.installDir)
 		if apexType == flattenedApex {
 			// /system/apex/<name>/{lib|framework|...}
-			fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", filepath.Join(a.installDir.ToMakePath().String(),
-				apexBundleName, fi.installDir))
+			modulePath := filepath.Join(a.installDir.ToMakePath().String(), apexBundleName, fi.installDir)
+			fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", modulePath)
 			if a.primaryApexType && !symbolFilesNotNeeded {
 				fmt.Fprintln(w, "LOCAL_SOONG_SYMBOL_PATH :=", pathWhenActivated)
 			}
 			if len(fi.symlinks) > 0 {
 				fmt.Fprintln(w, "LOCAL_MODULE_SYMLINKS :=", strings.Join(fi.symlinks, " "))
 			}
+			newDataPaths := []android.Path{}
+			for _, path := range fi.dataPaths {
+				dataOutPath := modulePath + ":" + path.Rel()
+				if ok := seenDataOutPaths[dataOutPath]; !ok {
+					newDataPaths = append(newDataPaths, path)
+					seenDataOutPaths[dataOutPath] = true
+				}
+			}
+			if len(newDataPaths) > 0 {
+				fmt.Fprintln(w, "LOCAL_TEST_DATA :=", strings.Join(cc.AndroidMkDataPaths(newDataPaths), " "))
+			}
 
 			if fi.module != nil && len(fi.module.NoticeFiles()) > 0 {
 				fmt.Fprintln(w, "LOCAL_NOTICE_FILE :=", strings.Join(fi.module.NoticeFiles().Strings(), " "))
@@ -166,11 +179,11 @@
 			fmt.Fprintln(w, "LOCAL_SOONG_JACOCO_REPORT_CLASSES_JAR :=", fi.jacocoReportClassesFile.String())
 		}
 		if fi.class == javaSharedLib {
-			javaModule := fi.module.(javaLibrary)
+			javaModule := fi.module.(java.Dependency)
 			// soong_java_prebuilt.mk sets LOCAL_MODULE_SUFFIX := .jar  Therefore
 			// we need to remove the suffix from LOCAL_MODULE_STEM, otherwise
 			// we will have foo.jar.jar
-			fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", strings.TrimSuffix(fi.builtFile.Base(), ".jar"))
+			fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", strings.TrimSuffix(fi.Stem(), ".jar"))
 			fmt.Fprintln(w, "LOCAL_SOONG_CLASSES_JAR :=", javaModule.ImplementationAndResourcesJars()[0].String())
 			fmt.Fprintln(w, "LOCAL_SOONG_HEADER_JAR :=", javaModule.HeaderJars()[0].String())
 			fmt.Fprintln(w, "LOCAL_SOONG_DEX_JAR :=", fi.builtFile.String())
@@ -181,13 +194,13 @@
 			// soong_app_prebuilt.mk sets LOCAL_MODULE_SUFFIX := .apk  Therefore
 			// we need to remove the suffix from LOCAL_MODULE_STEM, otherwise
 			// we will have foo.apk.apk
-			fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", strings.TrimSuffix(fi.builtFile.Base(), ".apk"))
+			fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", strings.TrimSuffix(fi.Stem(), ".apk"))
 			if app, ok := fi.module.(*java.AndroidApp); ok && len(app.JniCoverageOutputs()) > 0 {
 				fmt.Fprintln(w, "LOCAL_PREBUILT_COVERAGE_ARCHIVE :=", strings.Join(app.JniCoverageOutputs().Strings(), " "))
 			}
 			fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_app_prebuilt.mk")
 		} else if fi.class == nativeSharedLib || fi.class == nativeExecutable || fi.class == nativeTest {
-			fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", fi.builtFile.Base())
+			fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", fi.Stem())
 			if cc, ok := fi.module.(*cc.Module); ok {
 				if cc.UnstrippedOutputFile() != nil {
 					fmt.Fprintln(w, "LOCAL_SOONG_UNSTRIPPED_BINARY :=", cc.UnstrippedOutputFile().String())
@@ -199,7 +212,7 @@
 			}
 			fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_cc_prebuilt.mk")
 		} else {
-			fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", fi.builtFile.Base())
+			fmt.Fprintln(w, "LOCAL_MODULE_STEM :=", fi.Stem())
 			if fi.builtFile == a.manifestPbOut && apexType == flattenedApex {
 				if a.primaryApexType {
 					// Make apex_manifest.pb module for this APEX to override all other
diff --git a/apex/apex.go b/apex/apex.go
index fa71ffa..10c16f5 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -93,69 +93,6 @@
 	//
 	// Module separator
 	//
-	artApexContents := []string{
-		"art_cmdlineparser_headers",
-		"art_disassembler_headers",
-		"art_libartbase_headers",
-		"bionic_libc_platform_headers",
-		"core-repackaged-icu4j",
-		"cpp-define-generator-asm-support",
-		"cpp-define-generator-definitions",
-		"crtbegin_dynamic",
-		"crtbegin_dynamic1",
-		"crtbegin_so1",
-		"crtbrand",
-		"dex2oat_headers",
-		"dt_fd_forward_export",
-		"icu4c_extra_headers",
-		"javavm_headers",
-		"jni_platform_headers",
-		"libPlatformProperties",
-		"libadbconnection_client",
-		"libadbconnection_server",
-		"libandroidicuinit",
-		"libart_runtime_headers_ndk",
-		"libartd-disassembler",
-		"libdexfile_all_headers",
-		"libdexfile_external_headers",
-		"libdexfile_support",
-		"libdmabufinfo",
-		"libexpat",
-		"libfdlibm",
-		"libicui18n_headers",
-		"libicuuc",
-		"libicuuc_headers",
-		"libicuuc_stubdata",
-		"libjdwp_headers",
-		"liblz4",
-		"liblzma",
-		"libmeminfo",
-		"libnativebridge-headers",
-		"libnativehelper_header_only",
-		"libnativeloader-headers",
-		"libnpt_headers",
-		"libopenjdkjvmti_headers",
-		"libperfetto_client_experimental",
-		"libprocinfo",
-		"libunwind_llvm",
-		"libunwindstack",
-		"libv8",
-		"libv8base",
-		"libv8gen",
-		"libv8platform",
-		"libv8sampler",
-		"libv8src",
-		"libvixl",
-		"libvixld",
-		"libz",
-		"libziparchive",
-		"perfetto_trace_protos",
-	}
-	m["com.android.art.debug"] = artApexContents
-	m["com.android.art.release"] = artApexContents
-	//
-	// Module separator
-	//
 	m["com.android.bluetooth.updatable"] = []string{
 		"android.hardware.audio.common@5.0",
 		"android.hardware.bluetooth.a2dp@1.0",
@@ -244,19 +181,6 @@
 	//
 	// Module separator
 	//
-	m["com.android.extservices"] = []string{
-		"flatbuffer_headers",
-		"liblua",
-		"libtextclassifier",
-		"libtextclassifier_hash_static",
-		"libtflite_static",
-		"libutf",
-		"libz_current",
-		"tensorflow_headers",
-	}
-	//
-	// Module separator
-	//
 	m["com.android.neuralnetworks"] = []string{
 		"android.hardware.neuralnetworks@1.0",
 		"android.hardware.neuralnetworks@1.1",
@@ -300,7 +224,10 @@
 		"android.hidl.token@1.0",
 		"android.hidl.token@1.0-utils",
 		"bionic_libc_platform_headers",
+		"exoplayer2-extractor",
+		"exoplayer2-extractor-annotation-stubs",
 		"gl_headers",
+		"jsr305",
 		"libEGL",
 		"libEGL_blobCache",
 		"libEGL_getProcAddress",
@@ -555,12 +482,6 @@
 	// Module separator
 	//
 	m["com.android.permission"] = []string{
-		"androidx.annotation_annotation",
-		"androidx.annotation_annotation-nodeps",
-		"androidx.lifecycle_lifecycle-common",
-		"androidx.lifecycle_lifecycle-common-java8",
-		"androidx.lifecycle_lifecycle-common-java8-nodeps",
-		"androidx.lifecycle_lifecycle-common-nodeps",
 		"kotlin-annotations",
 		"kotlin-stdlib",
 		"kotlin-stdlib-jdk7",
@@ -631,13 +552,21 @@
 	// Module separator
 	//
 	m["com.android.tethering"] = []string{
-		"libnativehelper_compat_libc++",
-		"android.hardware.tetheroffload.config@1.0",
+		"android.hardware.tetheroffload.config-V1.0-java",
+		"android.hardware.tetheroffload.control-V1.0-java",
+		"android.hidl.base-V1.0-java",
+		"ipmemorystore-aidl-interfaces-java",
 		"libcgrouprc",
 		"libcgrouprc_format",
+		"libnativehelper_compat_libc++",
 		"libtetherutilsjni",
 		"libvndksupport",
+		"net-utils-framework-common",
+		"netd_aidl_interface-V3-java",
+		"netlink-client",
+		"networkstack-aidl-interfaces-java",
 		"tethering-aidl-interfaces-java",
+		"TetheringApiCurrentLib",
 	}
 	//
 	// Module separator
@@ -661,8 +590,6 @@
 		"android.hidl.manager-V1.0-java",
 		"android.hidl.manager-V1.1-java",
 		"android.hidl.manager-V1.2-java",
-		"androidx.annotation_annotation",
-		"androidx.annotation_annotation-nodeps",
 		"bouncycastle-unbundled",
 		"dnsresolver_aidl_interface-V2-java",
 		"error_prone_annotations",
@@ -684,7 +611,6 @@
 		"wifi-nano-protos",
 		"wifi-service-pre-jarjar",
 		"wifi-service-resources",
-		"prebuilt_androidx.annotation_annotation-nodeps",
 	}
 	//
 	// Module separator
@@ -704,6 +630,15 @@
 	// Module separator
 	//
 	m[android.AvailableToAnyApex] = []string{
+		// TODO(b/156996905) Set apex_available/min_sdk_version for androidx/extras support libraries
+		"androidx",
+		"androidx-constraintlayout_constraintlayout",
+		"androidx-constraintlayout_constraintlayout-nodeps",
+		"androidx-constraintlayout_constraintlayout-solver",
+		"androidx-constraintlayout_constraintlayout-solver-nodeps",
+		"com.google.android.material_material",
+		"com.google.android.material_material-nodeps",
+
 		"libatomic",
 		"libclang_rt",
 		"libgcc_stripped",
@@ -723,6 +658,7 @@
 	android.RegisterModuleType("apex_defaults", defaultsFactory)
 	android.RegisterModuleType("prebuilt_apex", PrebuiltFactory)
 	android.RegisterModuleType("override_apex", overrideApexFactory)
+	android.RegisterModuleType("apex_set", apexSetFactory)
 
 	android.PreDepsMutators(RegisterPreDepsMutators)
 	android.PostDepsMutators(RegisterPostDepsMutators)
@@ -759,7 +695,7 @@
 		apexBundles = []android.ApexInfo{{
 			ApexName:      mctx.ModuleName(),
 			MinSdkVersion: a.minSdkVersion(mctx),
-			Updatable:     proptools.Bool(a.properties.Updatable),
+			Updatable:     a.Updatable(),
 		}}
 		directDep = true
 	} else if am, ok := mctx.Module().(android.ApexModule); ok {
@@ -1208,12 +1144,14 @@
 // apexFile represents a file in an APEX bundle
 type apexFile struct {
 	builtFile  android.Path
+	stem       string
 	moduleName string
 	installDir string
 	class      apexFileClass
 	module     android.Module
 	// list of symlinks that will be created in installDir that point to this apexFile
 	symlinks      []string
+	dataPaths     android.Paths
 	transitiveDep bool
 	moduleDir     string
 
@@ -1249,16 +1187,27 @@
 	return af.builtFile != nil && af.builtFile.String() != ""
 }
 
+func (af *apexFile) apexRelativePath(path string) string {
+	return filepath.Join(af.installDir, path)
+}
+
 // Path() returns path of this apex file relative to the APEX root
 func (af *apexFile) Path() string {
-	return filepath.Join(af.installDir, af.builtFile.Base())
+	return af.apexRelativePath(af.Stem())
+}
+
+func (af *apexFile) Stem() string {
+	if af.stem != "" {
+		return af.stem
+	}
+	return af.builtFile.Base()
 }
 
 // SymlinkPaths() returns paths of the symlinks (if any) relative to the APEX root
 func (af *apexFile) SymlinkPaths() []string {
 	var ret []string
 	for _, symlink := range af.symlinks {
-		ret = append(ret, filepath.Join(af.installDir, symlink))
+		ret = append(ret, af.apexRelativePath(symlink))
 	}
 	return ret
 }
@@ -1458,19 +1407,7 @@
 					target,
 					a.getImageVariation(config))
 			}
-
-			if strings.HasPrefix(ctx.ModuleName(), "com.android.runtime") && target.Os.Class == android.Device {
-				for _, sanitizer := range ctx.Config().SanitizeDevice() {
-					if sanitizer == "hwaddress" {
-						addDependenciesForNativeModules(ctx,
-							ApexNativeDependencies{[]string{"libclang_rt.hwasan-aarch64-android"}, nil, nil, nil},
-							target, a.getImageVariation(config))
-						break
-					}
-				}
-			}
 		}
-
 	}
 
 	// For prebuilt_etc, use the first variant (64 on 64/32bit device,
@@ -1602,6 +1539,21 @@
 	return android.InList(sanitizerName, globalSanitizerNames)
 }
 
+func (a *apexBundle) AddSanitizerDependencies(ctx android.BottomUpMutatorContext, sanitizerName string) {
+	if ctx.Device() && sanitizerName == "hwaddress" && strings.HasPrefix(a.Name(), "com.android.runtime") {
+		for _, target := range ctx.MultiTargets() {
+			if target.Arch.ArchType.Multilib == "lib64" {
+				ctx.AddFarVariationDependencies(append(target.Variations(), []blueprint.Variation{
+					{Mutator: "image", Variation: a.getImageVariation(ctx.DeviceConfig())},
+					{Mutator: "link", Variation: "shared"},
+					{Mutator: "version", Variation: ""}, // "" is the non-stub variant
+				}...), sharedLibTag, "libclang_rt.hwasan-aarch64-android")
+				break
+			}
+		}
+	}
+}
+
 var _ cc.Coverage = (*apexBundle)(nil)
 
 func (a *apexBundle) IsNativeCoverageNeeded(ctx android.BaseModuleContext) bool {
@@ -1664,6 +1616,7 @@
 	fileToCopy := cc.OutputFile().Path()
 	af := newApexFile(ctx, fileToCopy, cc.Name(), dirInApex, nativeExecutable, cc)
 	af.symlinks = cc.Symlinks()
+	af.dataPaths = cc.DataPaths()
 	return af
 }
 
@@ -1694,17 +1647,17 @@
 	return af
 }
 
-// TODO(b/146586360): replace javaLibrary(in apex/apex.go) with java.Dependency
-type javaLibrary interface {
-	android.Module
+type javaDependency interface {
 	java.Dependency
+	Stem() string
 }
 
-func apexFileForJavaLibrary(ctx android.BaseModuleContext, lib javaLibrary) apexFile {
+func apexFileForJavaLibrary(ctx android.BaseModuleContext, lib javaDependency, module android.Module) apexFile {
 	dirInApex := "javalib"
 	fileToCopy := lib.DexJar()
-	af := newApexFile(ctx, fileToCopy, lib.Name(), dirInApex, javaSharedLib, lib)
+	af := newApexFile(ctx, fileToCopy, module.Name(), dirInApex, javaSharedLib, module)
 	af.jacocoReportClassesFile = lib.JacocoReportClassesFile()
+	af.stem = lib.Stem() + ".jar"
 	return af
 }
 
@@ -1810,6 +1763,12 @@
 	return intVer
 }
 
+func (a *apexBundle) Updatable() bool {
+	return proptools.Bool(a.properties.Updatable)
+}
+
+var _ android.ApexBundleDepsInfoIntf = (*apexBundle)(nil)
+
 // Ensures that the dependencies are marked as available for this APEX
 func (a *apexBundle) checkApexAvailability(ctx android.ModuleContext) {
 	// Let's be practical. Availability for test, host, and the VNDK apex isn't important
@@ -1851,7 +1810,7 @@
 }
 
 func (a *apexBundle) checkUpdatable(ctx android.ModuleContext) {
-	if proptools.Bool(a.properties.Updatable) {
+	if a.Updatable() {
 		if String(a.properties.Min_sdk_version) == "" {
 			ctx.PropertyErrorf("updatable", "updatable APEXes should set min_sdk_version as well")
 		}
@@ -1860,6 +1819,51 @@
 	}
 }
 
+// Ensures that a lib providing stub isn't statically linked
+func (a *apexBundle) checkStaticLinkingToStubLibraries(ctx android.ModuleContext) {
+	// Practically, we only care about regular APEXes on the device.
+	if ctx.Host() || a.testApex || a.vndkApex {
+		return
+	}
+
+	a.walkPayloadDeps(ctx, func(ctx android.ModuleContext, from blueprint.Module, to android.ApexModule, externalDep bool) bool {
+		if ccm, ok := to.(*cc.Module); ok {
+			apexName := ctx.ModuleName()
+			fromName := ctx.OtherModuleName(from)
+			toName := ctx.OtherModuleName(to)
+
+			// If `to` is not actually in the same APEX as `from` then it does not need apex_available and neither
+			// do any of its dependencies.
+			if am, ok := from.(android.DepIsInSameApex); ok && !am.DepIsInSameApex(ctx, to) {
+				// As soon as the dependency graph crosses the APEX boundary, don't go further.
+				return false
+			}
+
+			// TODO(jiyong) remove this check when R is published to AOSP. Currently, libstatssocket
+			// is capable of providing a stub variant, but is being statically linked from the bluetooth
+			// APEX.
+			if toName == "libstatssocket" {
+				return false
+			}
+
+			// The dynamic linker and crash_dump tool in the runtime APEX is the only exception to this rule.
+			// It can't make the static dependencies dynamic because it can't
+			// do the dynamic linking for itself.
+			if apexName == "com.android.runtime" && (fromName == "linker" || fromName == "crash_dump") {
+				return false
+			}
+
+			isStubLibraryFromOtherApex := ccm.HasStubsVariants() && !android.DirectlyInApex(apexName, toName)
+			if isStubLibraryFromOtherApex && !externalDep {
+				ctx.ModuleErrorf("%q required by %q is a native library providing stub. "+
+					"It shouldn't be included in this APEX via static linking. Dependency path: %s", to.String(), fromName, ctx.GetPathString(false))
+			}
+
+		}
+		return true
+	})
+}
+
 func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	buildFlattenedAsDefault := ctx.Config().FlattenApex() && !ctx.Config().UnbundledBuild()
 	switch a.properties.ApexType {
@@ -1897,6 +1901,7 @@
 
 	a.checkApexAvailability(ctx)
 	a.checkUpdatable(ctx)
+	a.checkStaticLinkingToStubLibraries(ctx)
 
 	handleSpecialLibs := !android.Bool(a.properties.Ignore_system_library_special_case)
 
@@ -1941,13 +1946,13 @@
 			case sharedLibTag, jniLibTag:
 				isJniLib := depTag == jniLibTag
 				if c, ok := child.(*cc.Module); ok {
-					// bootstrap bionic libs are treated as provided by system
-					if c.HasStubsVariants() && !cc.InstallToBootstrap(c.BaseModuleName(), ctx.Config()) {
-						provideNativeLibs = append(provideNativeLibs, c.OutputFile().Path().Base())
-					}
 					fi := apexFileForNativeLibrary(ctx, c, handleSpecialLibs)
 					fi.isJniLib = isJniLib
 					filesInfo = append(filesInfo, fi)
+					// bootstrap bionic libs are treated as provided by system
+					if c.HasStubsVariants() && !cc.InstallToBootstrap(c.BaseModuleName(), ctx.Config()) {
+						provideNativeLibs = append(provideNativeLibs, fi.Stem())
+					}
 					return true // track transitive dependencies
 				} else {
 					propertyName := "native_shared_libs"
@@ -1971,7 +1976,7 @@
 				}
 			case javaLibTag:
 				if javaLib, ok := child.(*java.Library); ok {
-					af := apexFileForJavaLibrary(ctx, javaLib)
+					af := apexFileForJavaLibrary(ctx, javaLib, javaLib)
 					if !af.Ok() {
 						ctx.PropertyErrorf("java_libs", "%q is not configured to be compiled into dex", depName)
 					} else {
@@ -1979,7 +1984,7 @@
 						return true // track transitive dependencies
 					}
 				} else if sdkLib, ok := child.(*java.SdkLibrary); ok {
-					af := apexFileForJavaLibrary(ctx, sdkLib)
+					af := apexFileForJavaLibrary(ctx, sdkLib, sdkLib)
 					if !af.Ok() {
 						ctx.PropertyErrorf("java_libs", "%q is not configured to be compiled into dex", depName)
 						return false
@@ -2067,6 +2072,8 @@
 							// don't include it in this APEX
 							return false
 						}
+						af := apexFileForNativeLibrary(ctx, cc, handleSpecialLibs)
+						af.transitiveDep = true
 						if !a.Host() && !android.DirectlyInApex(ctx.ModuleName(), ctx.OtherModuleName(cc)) && (cc.IsStubs() || cc.HasStubsVariants()) {
 							// If the dependency is a stubs lib, don't include it in this APEX,
 							// but make sure that the lib is installed on the device.
@@ -2078,12 +2085,10 @@
 							if !android.DirectlyInAnyApex(ctx, cc.Name()) && !android.InList(cc.BaseModuleName(), a.requiredDeps) {
 								a.requiredDeps = append(a.requiredDeps, cc.BaseModuleName())
 							}
-							requireNativeLibs = append(requireNativeLibs, cc.OutputFile().Path().Base())
+							requireNativeLibs = append(requireNativeLibs, af.Stem())
 							// Don't track further
 							return false
 						}
-						af := apexFileForNativeLibrary(ctx, cc, handleSpecialLibs)
-						af.transitiveDep = true
 						filesInfo = append(filesInfo, af)
 						return true // track transitive dependencies
 					}
@@ -2191,7 +2196,7 @@
 
 	// We don't need the optimization for updatable APEXes, as it might give false signal
 	// to the system health when the APEXes are still bundled (b/149805758)
-	if proptools.Bool(a.properties.Updatable) && a.properties.ApexType == imageApex {
+	if a.Updatable() && a.properties.ApexType == imageApex {
 		a.linkToSystemLib = false
 	}
 
@@ -2257,6 +2262,10 @@
 		// We don't want to list them all
 		moduleName = "libclang_rt"
 	}
+	if strings.HasPrefix(moduleName, "androidx.") {
+		// TODO(b/156996905) Set apex_available/min_sdk_version for androidx support libraries
+		moduleName = "androidx"
+	}
 	return moduleName
 }
 
diff --git a/apex/apex_singleton.go b/apex/apex_singleton.go
new file mode 100644
index 0000000..83a56a2
--- /dev/null
+++ b/apex/apex_singleton.go
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package apex
+
+import (
+	"github.com/google/blueprint"
+
+	"android/soong/android"
+)
+
+func init() {
+	android.RegisterSingletonType("apex_depsinfo_singleton", apexDepsInfoSingletonFactory)
+}
+
+type apexDepsInfoSingleton struct {
+	// Output file with all flatlists from updatable modules' deps-info combined
+	updatableFlatListsPath android.OutputPath
+}
+
+func apexDepsInfoSingletonFactory() android.Singleton {
+	return &apexDepsInfoSingleton{}
+}
+
+var combineFilesRule = pctx.AndroidStaticRule("combineFilesRule",
+	blueprint.RuleParams{
+		Command:        "cat $out.rsp | xargs cat > $out",
+		Rspfile:        "$out.rsp",
+		RspfileContent: "$in",
+	},
+)
+
+func (s *apexDepsInfoSingleton) GenerateBuildActions(ctx android.SingletonContext) {
+	updatableFlatLists := android.Paths{}
+	ctx.VisitAllModules(func(module android.Module) {
+		if binaryInfo, ok := module.(android.ApexBundleDepsInfoIntf); ok {
+			if path := binaryInfo.FlatListPath(); path != nil {
+				if binaryInfo.Updatable() {
+					updatableFlatLists = append(updatableFlatLists, path)
+				}
+			}
+		}
+	})
+
+	s.updatableFlatListsPath = android.PathForOutput(ctx, "apex", "depsinfo", "updatable-flatlists.txt")
+	ctx.Build(pctx, android.BuildParams{
+		Rule:        combineFilesRule,
+		Description: "Generate " + s.updatableFlatListsPath.String(),
+		Inputs:      updatableFlatLists,
+		Output:      s.updatableFlatListsPath,
+	})
+}
diff --git a/apex/apex_test.go b/apex/apex_test.go
index dc69862..29bd087 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -19,6 +19,7 @@
 	"os"
 	"path"
 	"reflect"
+	"regexp"
 	"sort"
 	"strings"
 	"testing"
@@ -175,11 +176,15 @@
 		"testkey2.pem":                               nil,
 		"myapex-arm64.apex":                          nil,
 		"myapex-arm.apex":                            nil,
+		"myapex.apks":                                nil,
 		"frameworks/base/api/current.txt":            nil,
 		"framework/aidl/a.aidl":                      nil,
 		"build/make/core/proguard.flags":             nil,
 		"build/make/core/proguard_basic_keeps.flags": nil,
 		"dummy.txt":                                  nil,
+		"baz":                                        nil,
+		"bar/baz":                                    nil,
+		"testdata/baz":                               nil,
 	}
 
 	cc.GatherRequiredFilesForTest(fs)
@@ -216,6 +221,7 @@
 	ctx.RegisterModuleType("apex_defaults", defaultsFactory)
 	ctx.RegisterModuleType("prebuilt_apex", PrebuiltFactory)
 	ctx.RegisterModuleType("override_apex", overrideApexFactory)
+	ctx.RegisterModuleType("apex_set", apexSetFactory)
 
 	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
 	ctx.PostDepsMutators(android.RegisterOverridePostDepsMutators)
@@ -269,6 +275,15 @@
 	}
 }
 
+// ensure that 'result' contains 'expected' exactly one time
+func ensureContainsOnce(t *testing.T, result string, expected string) {
+	t.Helper()
+	count := strings.Count(result, expected)
+	if count != 1 {
+		t.Errorf("%q is found %d times (expected 1 time) in %q", expected, count, result)
+	}
+}
+
 // ensures that 'result' does not contain 'notExpected'
 func ensureNotContains(t *testing.T, result string, notExpected string) {
 	t.Helper()
@@ -409,6 +424,7 @@
 		java_library {
 			name: "myjar",
 			srcs: ["foo/bar/MyClass.java"],
+			stem: "myjar_stem",
 			sdk_version: "none",
 			system_modules: "none",
 			static_libs: ["myotherjar"],
@@ -463,7 +479,7 @@
 	// Ensure that both direct and indirect deps are copied into apex
 	ensureContains(t, copyCmds, "image.apex/lib64/mylib.so")
 	ensureContains(t, copyCmds, "image.apex/lib64/mylib2.so")
-	ensureContains(t, copyCmds, "image.apex/javalib/myjar.jar")
+	ensureContains(t, copyCmds, "image.apex/javalib/myjar_stem.jar")
 	// .. but not for java libs
 	ensureNotContains(t, copyCmds, "image.apex/javalib/myotherjar.jar")
 	ensureNotContains(t, copyCmds, "image.apex/javalib/msharedjar.jar")
@@ -905,6 +921,130 @@
 
 }
 
+func TestRuntimeApexShouldInstallHwasanIfLibcDependsOnIt(t *testing.T) {
+	ctx, _ := testApex(t, "", func(fs map[string][]byte, config android.Config) {
+		bp := `
+		apex {
+			name: "com.android.runtime",
+			key: "com.android.runtime.key",
+			native_shared_libs: ["libc"],
+		}
+
+		apex_key {
+			name: "com.android.runtime.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "libc",
+			no_libcrt: true,
+			nocrt: true,
+			stl: "none",
+			system_shared_libs: [],
+			stubs: { versions: ["1"] },
+			apex_available: ["com.android.runtime"],
+
+			sanitize: {
+				hwaddress: true,
+			}
+		}
+
+		cc_prebuilt_library_shared {
+			name: "libclang_rt.hwasan-aarch64-android",
+			no_libcrt: true,
+			nocrt: true,
+			stl: "none",
+			system_shared_libs: [],
+			srcs: [""],
+			stubs: { versions: ["1"] },
+
+			sanitize: {
+				never: true,
+			},
+		}
+		`
+		// override bp to use hard-coded names: com.android.runtime and libc
+		fs["Android.bp"] = []byte(bp)
+		fs["system/sepolicy/apex/com.android.runtime-file_contexts"] = nil
+	})
+
+	ensureExactContents(t, ctx, "com.android.runtime", "android_common_hwasan_com.android.runtime_image", []string{
+		"lib64/bionic/libc.so",
+		"lib64/bionic/libclang_rt.hwasan-aarch64-android.so",
+	})
+
+	hwasan := ctx.ModuleForTests("libclang_rt.hwasan-aarch64-android", "android_arm64_armv8-a_shared")
+
+	installed := hwasan.Description("install libclang_rt.hwasan")
+	ensureContains(t, installed.Output.String(), "/system/lib64/bootstrap/libclang_rt.hwasan-aarch64-android.so")
+
+	symlink := hwasan.Description("install symlink libclang_rt.hwasan")
+	ensureEquals(t, symlink.Args["fromPath"], "/apex/com.android.runtime/lib64/bionic/libclang_rt.hwasan-aarch64-android.so")
+	ensureContains(t, symlink.Output.String(), "/system/lib64/libclang_rt.hwasan-aarch64-android.so")
+}
+
+func TestRuntimeApexShouldInstallHwasanIfHwaddressSanitized(t *testing.T) {
+	ctx, _ := testApex(t, "", func(fs map[string][]byte, config android.Config) {
+		bp := `
+		apex {
+			name: "com.android.runtime",
+			key: "com.android.runtime.key",
+			native_shared_libs: ["libc"],
+		}
+
+		apex_key {
+			name: "com.android.runtime.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "libc",
+			no_libcrt: true,
+			nocrt: true,
+			stl: "none",
+			system_shared_libs: [],
+			stubs: { versions: ["1"] },
+			apex_available: ["com.android.runtime"],
+		}
+
+		cc_prebuilt_library_shared {
+			name: "libclang_rt.hwasan-aarch64-android",
+			no_libcrt: true,
+			nocrt: true,
+			stl: "none",
+			system_shared_libs: [],
+			srcs: [""],
+			stubs: { versions: ["1"] },
+
+			sanitize: {
+				never: true,
+			},
+		}
+		`
+		// override bp to use hard-coded names: com.android.runtime and libc
+		fs["Android.bp"] = []byte(bp)
+		fs["system/sepolicy/apex/com.android.runtime-file_contexts"] = nil
+
+		config.TestProductVariables.SanitizeDevice = []string{"hwaddress"}
+	})
+
+	ensureExactContents(t, ctx, "com.android.runtime", "android_common_hwasan_com.android.runtime_image", []string{
+		"lib64/bionic/libc.so",
+		"lib64/bionic/libclang_rt.hwasan-aarch64-android.so",
+	})
+
+	hwasan := ctx.ModuleForTests("libclang_rt.hwasan-aarch64-android", "android_arm64_armv8-a_shared")
+
+	installed := hwasan.Description("install libclang_rt.hwasan")
+	ensureContains(t, installed.Output.String(), "/system/lib64/bootstrap/libclang_rt.hwasan-aarch64-android.so")
+
+	symlink := hwasan.Description("install symlink libclang_rt.hwasan")
+	ensureEquals(t, symlink.Args["fromPath"], "/apex/com.android.runtime/lib64/bionic/libclang_rt.hwasan-aarch64-android.so")
+	ensureContains(t, symlink.Output.String(), "/system/lib64/libclang_rt.hwasan-aarch64-android.so")
+}
+
 func TestApexDependsOnLLNDKTransitively(t *testing.T) {
 	testcases := []struct {
 		name          string
@@ -3267,6 +3407,14 @@
 			private_key: "testkey.pem",
 		}
 
+		filegroup {
+			name: "fg",
+			srcs: [
+				"baz",
+				"bar/baz"
+			],
+		}
+
 		cc_test {
 			name: "mytest",
 			gtest: false,
@@ -3276,6 +3424,7 @@
 			system_shared_libs: [],
 			static_executable: true,
 			stl: "none",
+			data: [":fg"],
 		}
 
 		cc_library {
@@ -3285,6 +3434,13 @@
 			stl: "none",
 		}
 
+		filegroup {
+			name: "fg2",
+			srcs: [
+				"testdata/baz"
+			],
+		}
+
 		cc_test {
 			name: "mytests",
 			gtest: false,
@@ -3298,6 +3454,10 @@
 			system_shared_libs: [],
 			static_executable: true,
 			stl: "none",
+			data: [
+				":fg",
+				":fg2",
+			],
 		}
 	`)
 
@@ -3308,15 +3468,19 @@
 	ensureContains(t, copyCmds, "image.apex/bin/test/mytest")
 	ensureContains(t, copyCmds, "image.apex/lib64/mylib.so")
 
+	//Ensure that test data are copied into apex.
+	ensureContains(t, copyCmds, "image.apex/bin/test/baz")
+	ensureContains(t, copyCmds, "image.apex/bin/test/bar/baz")
+
 	// Ensure that test deps built with `test_per_src` are copied into apex.
 	ensureContains(t, copyCmds, "image.apex/bin/test/mytest1")
 	ensureContains(t, copyCmds, "image.apex/bin/test/mytest2")
 	ensureContains(t, copyCmds, "image.apex/bin/test/mytest3")
 
 	// Ensure the module is correctly translated.
-	apexBundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
-	data := android.AndroidMkDataForTest(t, config, "", apexBundle)
-	name := apexBundle.BaseModuleName()
+	bundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
+	data := android.AndroidMkDataForTest(t, config, "", bundle)
+	name := bundle.BaseModuleName()
 	prefix := "TARGET_"
 	var builder strings.Builder
 	data.Custom(&builder, name, prefix, "", data)
@@ -3328,6 +3492,13 @@
 	ensureContains(t, androidMk, "LOCAL_MODULE := apex_manifest.pb.myapex\n")
 	ensureContains(t, androidMk, "LOCAL_MODULE := apex_pubkey.myapex\n")
 	ensureContains(t, androidMk, "LOCAL_MODULE := myapex\n")
+
+	flatBundle := ctx.ModuleForTests("myapex", "android_common_myapex_flattened").Module().(*apexBundle)
+	data = android.AndroidMkDataForTest(t, config, "", flatBundle)
+	data.Custom(&builder, name, prefix, "", data)
+	flatAndroidMk := builder.String()
+	ensureContainsOnce(t, flatAndroidMk, "LOCAL_TEST_DATA := :baz :bar/baz\n")
+	ensureContainsOnce(t, flatAndroidMk, "LOCAL_TEST_DATA := :testdata/baz\n")
 }
 
 func TestInstallExtraFlattenedApexes(t *testing.T) {
@@ -4115,6 +4286,15 @@
 	}
 }
 
+var filesForSdkLibrary = map[string][]byte{
+	"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,
+}
+
 func TestJavaSDKLibrary(t *testing.T) {
 	ctx, _ := testApex(t, `
 		apex {
@@ -4135,14 +4315,7 @@
 			api_packages: ["foo"],
 			apex_available: [ "myapex" ],
 		}
-	`, withFiles(map[string][]byte{
-		"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,
-	}))
+	`, withFiles(filesForSdkLibrary))
 
 	// java_sdk_library installs both impl jar and permission XML
 	ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
@@ -4154,6 +4327,98 @@
 	ensureContains(t, sdkLibrary.RuleParams.Command, `<library name=\"foo\" file=\"/apex/myapex/javalib/foo.jar\"`)
 }
 
+func TestJavaSDKLibrary_WithinApex(t *testing.T) {
+	ctx, _ := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			java_libs: ["foo", "bar"],
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		java_sdk_library {
+			name: "foo",
+			srcs: ["a.java"],
+			api_packages: ["foo"],
+			apex_available: ["myapex"],
+			sdk_version: "none",
+			system_modules: "none",
+		}
+
+		java_library {
+			name: "bar",
+			srcs: ["a.java"],
+			libs: ["foo"],
+			apex_available: ["myapex"],
+			sdk_version: "none",
+			system_modules: "none",
+		}
+	`, withFiles(filesForSdkLibrary))
+
+	// java_sdk_library installs both impl jar and permission XML
+	ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
+		"javalib/bar.jar",
+		"javalib/foo.jar",
+		"etc/permissions/foo.xml",
+	})
+
+	// The bar library should depend on the implementation jar.
+	barLibrary := ctx.ModuleForTests("bar", "android_common_myapex").Rule("javac")
+	if expected, actual := `^-classpath /[^:]*/turbine-combined/foo\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
+		t.Errorf("expected %q, found %#q", expected, actual)
+	}
+}
+
+func TestJavaSDKLibrary_CrossBoundary(t *testing.T) {
+	ctx, _ := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			java_libs: ["foo"],
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		java_sdk_library {
+			name: "foo",
+			srcs: ["a.java"],
+			api_packages: ["foo"],
+			apex_available: ["myapex"],
+			sdk_version: "none",
+			system_modules: "none",
+		}
+
+		java_library {
+			name: "bar",
+			srcs: ["a.java"],
+			libs: ["foo"],
+			sdk_version: "none",
+			system_modules: "none",
+		}
+	`, withFiles(filesForSdkLibrary))
+
+	// java_sdk_library installs both impl jar and permission XML
+	ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
+		"javalib/foo.jar",
+		"etc/permissions/foo.xml",
+	})
+
+	// The bar library should depend on the stubs jar.
+	barLibrary := ctx.ModuleForTests("bar", "android_common").Rule("javac")
+	if expected, actual := `^-classpath /[^:]*/turbine-combined/foo\.stubs\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
+		t.Errorf("expected %q, found %#q", expected, actual)
+	}
+}
+
 func TestCompatConfig(t *testing.T) {
 	ctx, _ := testApex(t, `
 		apex {
@@ -4491,10 +4756,78 @@
 	ensureContains(t, content, `"apex_config":{"apex_embedded_apk_config":[{"package_name":"com.android.foo","path":"app/AppFoo/AppFoo.apk"}]}`)
 }
 
-func testNoUpdatableJarsInBootImage(t *testing.T, errmsg, bp string, transformDexpreoptConfig func(*dexpreopt.GlobalConfig)) {
+func testNoUpdatableJarsInBootImage(t *testing.T, errmsg string, transformDexpreoptConfig func(*dexpreopt.GlobalConfig)) {
 	t.Helper()
 
-	bp = bp + `
+	bp := `
+		java_library {
+			name: "some-updatable-apex-lib",
+			srcs: ["a.java"],
+			sdk_version: "current",
+			apex_available: [
+				"some-updatable-apex",
+			],
+		}
+
+		java_library {
+			name: "some-non-updatable-apex-lib",
+			srcs: ["a.java"],
+			apex_available: [
+				"some-non-updatable-apex",
+			],
+		}
+
+		java_library {
+			name: "some-platform-lib",
+			srcs: ["a.java"],
+			sdk_version: "current",
+			installable: true,
+		}
+
+		java_library {
+			name: "some-art-lib",
+			srcs: ["a.java"],
+			sdk_version: "current",
+			apex_available: [
+				"com.android.art.something",
+			],
+			hostdex: true,
+		}
+
+		apex {
+			name: "some-updatable-apex",
+			key: "some-updatable-apex.key",
+			java_libs: ["some-updatable-apex-lib"],
+			updatable: true,
+			min_sdk_version: "current",
+		}
+
+		apex {
+			name: "some-non-updatable-apex",
+			key: "some-non-updatable-apex.key",
+			java_libs: ["some-non-updatable-apex-lib"],
+		}
+
+		apex_key {
+			name: "some-updatable-apex.key",
+		}
+
+		apex_key {
+			name: "some-non-updatable-apex.key",
+		}
+
+		apex {
+			name: "com.android.art.something",
+			key: "com.android.art.something.key",
+			java_libs: ["some-art-lib"],
+			updatable: true,
+			min_sdk_version: "current",
+		}
+
+		apex_key {
+			name: "com.android.art.something.key",
+		}
+
 		filegroup {
 			name: "some-updatable-apex-file_contexts",
 			srcs: [
@@ -4581,145 +4914,86 @@
 }
 
 func TestNoUpdatableJarsInBootImage(t *testing.T) {
-	bp := `
-		java_library {
-			name: "some-updatable-apex-lib",
-			srcs: ["a.java"],
-			sdk_version: "current",
-			apex_available: [
-				"some-updatable-apex",
-			],
-		}
-
-		java_library {
-			name: "some-non-updatable-apex-lib",
-			srcs: ["a.java"],
-			apex_available: [
-				"some-non-updatable-apex",
-			],
-		}
-
-		java_library {
-			name: "some-platform-lib",
-			srcs: ["a.java"],
-			sdk_version: "current",
-			installable: true,
-		}
-
-		java_library {
-			name: "some-art-lib",
-			srcs: ["a.java"],
-			sdk_version: "current",
-			apex_available: [
-				"com.android.art.something",
-			],
-			hostdex: true,
-		}
-
-		apex {
-			name: "some-updatable-apex",
-			key: "some-updatable-apex.key",
-			java_libs: ["some-updatable-apex-lib"],
-			updatable: true,
-			min_sdk_version: "current",
-		}
-
-		apex {
-			name: "some-non-updatable-apex",
-			key: "some-non-updatable-apex.key",
-			java_libs: ["some-non-updatable-apex-lib"],
-		}
-
-		apex_key {
-			name: "some-updatable-apex.key",
-		}
-
-		apex_key {
-			name: "some-non-updatable-apex.key",
-		}
-
-		apex {
-			name: "com.android.art.something",
-			key: "com.android.art.something.key",
-			java_libs: ["some-art-lib"],
-			updatable: true,
-			min_sdk_version: "current",
-		}
-
-		apex_key {
-			name: "com.android.art.something.key",
-		}
-	`
 
 	var error string
 	var transform func(*dexpreopt.GlobalConfig)
 
-	// updatable jar from ART apex in the ART boot image => ok
-	transform = func(config *dexpreopt.GlobalConfig) {
-		config.ArtApexJars = []string{"com.android.art.something:some-art-lib"}
-	}
-	testNoUpdatableJarsInBootImage(t, "", bp, transform)
+	t.Run("updatable jar from ART apex in the ART boot image => ok", func(t *testing.T) {
+		transform = func(config *dexpreopt.GlobalConfig) {
+			config.ArtApexJars = []string{"com.android.art.something:some-art-lib"}
+		}
+		testNoUpdatableJarsInBootImage(t, "", transform)
+	})
 
-	// updatable jar from ART apex in the framework boot image => error
-	error = "module 'some-art-lib' from updatable apex 'com.android.art.something' is not allowed in the framework boot image"
-	transform = func(config *dexpreopt.GlobalConfig) {
-		config.BootJars = []string{"com.android.art.something:some-art-lib"}
-	}
-	testNoUpdatableJarsInBootImage(t, error, bp, transform)
+	t.Run("updatable jar from ART apex in the framework boot image => error", func(t *testing.T) {
+		error = "module 'some-art-lib' from updatable apex 'com.android.art.something' is not allowed in the framework boot image"
+		transform = func(config *dexpreopt.GlobalConfig) {
+			config.BootJars = []string{"com.android.art.something:some-art-lib"}
+		}
+		testNoUpdatableJarsInBootImage(t, error, transform)
+	})
 
-	// updatable jar from some other apex in the ART boot image => error
-	error = "module 'some-updatable-apex-lib' from updatable apex 'some-updatable-apex' is not allowed in the ART boot image"
-	transform = func(config *dexpreopt.GlobalConfig) {
-		config.ArtApexJars = []string{"some-updatable-apex:some-updatable-apex-lib"}
-	}
-	testNoUpdatableJarsInBootImage(t, error, bp, transform)
+	t.Run("updatable jar from some other apex in the ART boot image => error", func(t *testing.T) {
+		error = "module 'some-updatable-apex-lib' from updatable apex 'some-updatable-apex' is not allowed in the ART boot image"
+		transform = func(config *dexpreopt.GlobalConfig) {
+			config.ArtApexJars = []string{"some-updatable-apex:some-updatable-apex-lib"}
+		}
+		testNoUpdatableJarsInBootImage(t, error, transform)
+	})
 
-	// non-updatable jar from some other apex in the ART boot image => error
-	error = "module 'some-non-updatable-apex-lib' is not allowed in the ART boot image"
-	transform = func(config *dexpreopt.GlobalConfig) {
-		config.ArtApexJars = []string{"some-non-updatable-apex:some-non-updatable-apex-lib"}
-	}
-	testNoUpdatableJarsInBootImage(t, error, bp, transform)
+	t.Run("non-updatable jar from some other apex in the ART boot image => error", func(t *testing.T) {
+		error = "module 'some-non-updatable-apex-lib' is not allowed in the ART boot image"
+		transform = func(config *dexpreopt.GlobalConfig) {
+			config.ArtApexJars = []string{"some-non-updatable-apex:some-non-updatable-apex-lib"}
+		}
+		testNoUpdatableJarsInBootImage(t, error, transform)
+	})
 
-	// updatable jar from some other apex in the framework boot image => error
-	error = "module 'some-updatable-apex-lib' from updatable apex 'some-updatable-apex' is not allowed in the framework boot image"
-	transform = func(config *dexpreopt.GlobalConfig) {
-		config.BootJars = []string{"some-updatable-apex:some-updatable-apex-lib"}
-	}
-	testNoUpdatableJarsInBootImage(t, error, bp, transform)
+	t.Run("updatable jar from some other apex in the framework boot image => error", func(t *testing.T) {
+		error = "module 'some-updatable-apex-lib' from updatable apex 'some-updatable-apex' is not allowed in the framework boot image"
+		transform = func(config *dexpreopt.GlobalConfig) {
+			config.BootJars = []string{"some-updatable-apex:some-updatable-apex-lib"}
+		}
+		testNoUpdatableJarsInBootImage(t, error, transform)
+	})
 
-	// non-updatable jar from some other apex in the framework boot image => ok
-	transform = func(config *dexpreopt.GlobalConfig) {
-		config.BootJars = []string{"some-non-updatable-apex:some-non-updatable-apex-lib"}
-	}
-	testNoUpdatableJarsInBootImage(t, "", bp, transform)
+	t.Run("non-updatable jar from some other apex in the framework boot image => ok", func(t *testing.T) {
+		transform = func(config *dexpreopt.GlobalConfig) {
+			config.BootJars = []string{"some-non-updatable-apex:some-non-updatable-apex-lib"}
+		}
+		testNoUpdatableJarsInBootImage(t, "", transform)
+	})
 
-	// nonexistent jar in the ART boot image => error
-	error = "failed to find a dex jar path for module 'nonexistent'"
-	transform = func(config *dexpreopt.GlobalConfig) {
-		config.ArtApexJars = []string{"platform:nonexistent"}
-	}
-	testNoUpdatableJarsInBootImage(t, error, bp, transform)
+	t.Run("nonexistent jar in the ART boot image => error", func(t *testing.T) {
+		error = "failed to find a dex jar path for module 'nonexistent'"
+		transform = func(config *dexpreopt.GlobalConfig) {
+			config.ArtApexJars = []string{"platform:nonexistent"}
+		}
+		testNoUpdatableJarsInBootImage(t, error, transform)
+	})
 
-	// nonexistent jar in the framework boot image => error
-	error = "failed to find a dex jar path for module 'nonexistent'"
-	transform = func(config *dexpreopt.GlobalConfig) {
-		config.BootJars = []string{"platform:nonexistent"}
-	}
-	testNoUpdatableJarsInBootImage(t, error, bp, transform)
+	t.Run("nonexistent jar in the framework boot image => error", func(t *testing.T) {
+		error = "failed to find a dex jar path for module 'nonexistent'"
+		transform = func(config *dexpreopt.GlobalConfig) {
+			config.BootJars = []string{"platform:nonexistent"}
+		}
+		testNoUpdatableJarsInBootImage(t, error, transform)
+	})
 
-	// platform jar in the ART boot image => error
-	error = "module 'some-platform-lib' is not allowed in the ART boot image"
-	transform = func(config *dexpreopt.GlobalConfig) {
-		config.ArtApexJars = []string{"platform:some-platform-lib"}
-	}
-	testNoUpdatableJarsInBootImage(t, error, bp, transform)
+	t.Run("platform jar in the ART boot image => error", func(t *testing.T) {
+		error = "module 'some-platform-lib' is not allowed in the ART boot image"
+		transform = func(config *dexpreopt.GlobalConfig) {
+			config.ArtApexJars = []string{"platform:some-platform-lib"}
+		}
+		testNoUpdatableJarsInBootImage(t, error, transform)
+	})
 
-	// platform jar in the framework boot image => ok
-	transform = func(config *dexpreopt.GlobalConfig) {
-		config.BootJars = []string{"platform:some-platform-lib"}
-	}
-	testNoUpdatableJarsInBootImage(t, "", bp, transform)
+	t.Run("platform jar in the framework boot image => ok", func(t *testing.T) {
+		transform = func(config *dexpreopt.GlobalConfig) {
+			config.BootJars = []string{"platform:some-platform-lib"}
+		}
+		testNoUpdatableJarsInBootImage(t, "", transform)
+	})
 }
 
 func TestTestFor(t *testing.T) {
@@ -4774,6 +5048,84 @@
 	ensureNotContains(t, ldFlags, "mylib/android_arm64_armv8-a_shared_1/mylib.so")
 }
 
+// TODO(jungjw): Move this to proptools
+func intPtr(i int) *int {
+	return &i
+}
+
+func TestApexSet(t *testing.T) {
+	ctx, config := testApex(t, `
+		apex_set {
+			name: "myapex",
+			set: "myapex.apks",
+			filename: "foo_v2.apex",
+			overrides: ["foo"],
+		}
+	`, func(fs map[string][]byte, config android.Config) {
+		config.TestProductVariables.Platform_sdk_version = intPtr(30)
+		config.TestProductVariables.DeviceArch = proptools.StringPtr("arm")
+		config.TestProductVariables.DeviceSecondaryArch = proptools.StringPtr("arm64")
+	})
+
+	m := ctx.ModuleForTests("myapex", "android_common")
+
+	// Check extract_apks tool parameters.
+	extractedApex := m.Output(buildDir + "/.intermediates/myapex/android_common/foo_v2.apex")
+	actual := extractedApex.Args["abis"]
+	expected := "ARMEABI_V7A,ARM64_V8A"
+	if actual != expected {
+		t.Errorf("Unexpected abis parameter - expected %q vs actual %q", expected, actual)
+	}
+	actual = extractedApex.Args["sdk-version"]
+	expected = "30"
+	if actual != expected {
+		t.Errorf("Unexpected abis parameter - expected %q vs actual %q", expected, actual)
+	}
+
+	a := m.Module().(*ApexSet)
+	expectedOverrides := []string{"foo"}
+	actualOverrides := android.AndroidMkEntriesForTest(t, config, "", a)[0].EntryMap["LOCAL_OVERRIDES_MODULES"]
+	if !reflect.DeepEqual(actualOverrides, expectedOverrides) {
+		t.Errorf("Incorrect LOCAL_OVERRIDES_MODULES - expected %q vs actual %q", expectedOverrides, actualOverrides)
+	}
+}
+
+func TestNoStaticLinkingToStubsLib(t *testing.T) {
+	testApexError(t, `.*required by "mylib" is a native library providing stub.*`, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			native_shared_libs: ["mylib"],
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "mylib",
+			srcs: ["mylib.cpp"],
+			static_libs: ["otherlib"],
+			system_shared_libs: [],
+			stl: "none",
+			apex_available: [ "myapex" ],
+		}
+
+		cc_library {
+			name: "otherlib",
+			srcs: ["mylib.cpp"],
+			system_shared_libs: [],
+			stl: "none",
+			stubs: {
+				versions: ["1", "2", "3"],
+			},
+			apex_available: [ "myapex" ],
+		}
+	`)
+}
+
 func TestMain(m *testing.M) {
 	run := func() int {
 		setUp()
diff --git a/apex/builder.go b/apex/builder.go
index ca24f2c..0108d06 100644
--- a/apex/builder.go
+++ b/apex/builder.go
@@ -61,6 +61,7 @@
 	pctx.HostBinToolVariable("zipalign", "zipalign")
 	pctx.HostBinToolVariable("jsonmodify", "jsonmodify")
 	pctx.HostBinToolVariable("conv_apex_manifest", "conv_apex_manifest")
+	pctx.HostBinToolVariable("extract_apks", "extract_apks")
 }
 
 var (
@@ -189,7 +190,7 @@
 	var jniLibs []string
 	for _, fi := range a.filesInfo {
 		if fi.isJniLib {
-			jniLibs = append(jniLibs, fi.builtFile.Base())
+			jniLibs = append(jniLibs, fi.Stem())
 		}
 	}
 	if len(jniLibs) > 0 {
@@ -246,7 +247,7 @@
 		return android.NoticeOutputs{}
 	}
 
-	return android.BuildNoticeOutput(ctx, a.installDir, apexFileName, android.FirstUniquePaths(noticeFiles))
+	return android.BuildNoticeOutput(ctx, a.installDir, apexFileName, android.SortedUniquePaths(noticeFiles))
 }
 
 func (a *apexBundle) buildInstalledFilesFile(ctx android.ModuleContext, builtApex android.Path, imageDir android.Path) android.OutputPath {
@@ -350,6 +351,19 @@
 			symlinkDest := android.PathForModuleOut(ctx, "image"+suffix, symlinkPath).String()
 			copyCommands = append(copyCommands, "ln -sfn "+filepath.Base(destPath)+" "+symlinkDest)
 		}
+		for _, d := range fi.dataPaths {
+			// TODO(eakammer): This is now the third repetition of ~this logic for test paths, refactoring should be possible
+			relPath := d.Rel()
+			dataPath := d.String()
+			if !strings.HasSuffix(dataPath, relPath) {
+				panic(fmt.Errorf("path %q does not end with %q", dataPath, relPath))
+			}
+
+			dataDest := android.PathForModuleOut(ctx, "image"+suffix, fi.apexRelativePath(relPath)).String()
+
+			copyCommands = append(copyCommands, "cp -f "+d.String()+" "+dataDest)
+			implicitInputs = append(implicitInputs, d)
+		}
 	}
 
 	// TODO(jiyong): use RuleBuilder
@@ -403,9 +417,12 @@
 		var readOnlyPaths = []string{"apex_manifest.json", "apex_manifest.pb"}
 		var executablePaths []string // this also includes dirs
 		for _, f := range a.filesInfo {
-			pathInApex := filepath.Join(f.installDir, f.builtFile.Base())
+			pathInApex := f.Path()
 			if f.installDir == "bin" || strings.HasPrefix(f.installDir, "bin/") {
 				executablePaths = append(executablePaths, pathInApex)
+				for _, d := range f.dataPaths {
+					readOnlyPaths = append(readOnlyPaths, filepath.Join(f.installDir, d.Rel()))
+				}
 				for _, s := range f.symlinks {
 					executablePaths = append(executablePaths, filepath.Join(f.installDir, s))
 				}
@@ -453,11 +470,11 @@
 		}
 
 		targetSdkVersion := ctx.Config().DefaultAppTargetSdk()
+		// TODO(b/157078772): propagate min_sdk_version to apexer.
 		minSdkVersion := ctx.Config().DefaultAppTargetSdk()
 
 		if a.minSdkVersion(ctx) == android.SdkVersion_Android10 {
 			minSdkVersion = strconv.Itoa(a.minSdkVersion(ctx))
-			targetSdkVersion = strconv.Itoa(a.minSdkVersion(ctx))
 		}
 
 		if java.UseApiFingerprint(ctx) {
@@ -565,19 +582,27 @@
 	}
 
 	a.outputFile = android.PathForModuleOut(ctx, a.Name()+suffix)
+	rule := java.Signapk
+	args := map[string]string{
+		"certificates": a.container_certificate_file.String() + " " + a.container_private_key_file.String(),
+		"flags":        "-a 4096", //alignment
+	}
+	implicits := android.Paths{
+		a.container_certificate_file,
+		a.container_private_key_file,
+	}
+	if ctx.Config().IsEnvTrue("RBE_SIGNAPK") {
+		rule = java.SignapkRE
+		args["implicits"] = strings.Join(implicits.Strings(), ",")
+		args["outCommaList"] = a.outputFile.String()
+	}
 	ctx.Build(pctx, android.BuildParams{
-		Rule:        java.Signapk,
+		Rule:        rule,
 		Description: "signapk",
 		Output:      a.outputFile,
 		Input:       unsignedOutputFile,
-		Implicits: []android.Path{
-			a.container_certificate_file,
-			a.container_private_key_file,
-		},
-		Args: map[string]string{
-			"certificates": a.container_certificate_file.String() + " " + a.container_private_key_file.String(),
-			"flags":        "-a 4096", //alignment
-		},
+		Implicits:   implicits,
+		Args:        args,
 	})
 
 	// Install to $OUT/soong/{target,host}/.../apex
@@ -642,7 +667,7 @@
 			apexBundleName := a.Name()
 			for _, fi := range a.filesInfo {
 				dir := filepath.Join("apex", apexBundleName, fi.installDir)
-				target := ctx.InstallFile(android.PathForModuleInstall(ctx, dir), fi.builtFile.Base(), fi.builtFile)
+				target := ctx.InstallFile(android.PathForModuleInstall(ctx, dir), fi.Stem(), fi.builtFile)
 				for _, sym := range fi.symlinks {
 					ctx.InstallSymlink(android.PathForModuleInstall(ctx, dir), sym, target)
 				}
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index d089c28..03266c5 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -16,13 +16,29 @@
 
 import (
 	"fmt"
+	"strconv"
 	"strings"
 
 	"android/soong/android"
+	"android/soong/java"
+	"github.com/google/blueprint"
 
 	"github.com/google/blueprint/proptools"
 )
 
+var (
+	extractMatchingApex = pctx.StaticRule(
+		"extractMatchingApex",
+		blueprint.RuleParams{
+			Command: `rm -rf "$out" && ` +
+				`${extract_apks} -o "${out}" -allow-prereleased=${allow-prereleased} ` +
+				`-sdk-version=${sdk-version} -abis=${abis} -screen-densities=all -extract-single ` +
+				`${in}`,
+			CommandDeps: []string{"${extract_apks}"},
+		},
+		"abis", "allow-prereleased", "sdk-version")
+)
+
 type Prebuilt struct {
 	android.ModuleBase
 	prebuilt android.Prebuilt
@@ -208,3 +224,117 @@
 		},
 	}}
 }
+
+type ApexSet struct {
+	android.ModuleBase
+	prebuilt android.Prebuilt
+
+	properties ApexSetProperties
+
+	installDir      android.InstallPath
+	installFilename string
+	outputApex      android.WritablePath
+
+	// list of commands to create symlinks for backward compatibility.
+	// these commands will be attached as LOCAL_POST_INSTALL_CMD
+	compatSymlinks []string
+}
+
+type ApexSetProperties struct {
+	// the .apks file path that contains prebuilt apex files to be extracted.
+	Set *string
+
+	// whether the extracted apex file installable.
+	Installable *bool
+
+	// optional name for the installed apex. If unspecified, name of the
+	// module is used as the file name
+	Filename *string
+
+	// names of modules to be overridden. Listed modules can only be other binaries
+	// (in Make or Soong).
+	// This does not completely prevent installation of the overridden binaries, but if both
+	// binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will be removed
+	// from PRODUCT_PACKAGES.
+	Overrides []string
+
+	// apexes in this set use prerelease SDK version
+	Prerelease *bool
+}
+
+func (a *ApexSet) installable() bool {
+	return a.properties.Installable == nil || proptools.Bool(a.properties.Installable)
+}
+
+func (a *ApexSet) InstallFilename() string {
+	return proptools.StringDefault(a.properties.Filename, a.BaseModuleName()+imageApexSuffix)
+}
+
+func (a *ApexSet) Prebuilt() *android.Prebuilt {
+	return &a.prebuilt
+}
+
+func (a *ApexSet) Name() string {
+	return a.prebuilt.Name(a.ModuleBase.Name())
+}
+
+// prebuilt_apex imports an `.apex` file into the build graph as if it was built with apex.
+func apexSetFactory() android.Module {
+	module := &ApexSet{}
+	module.AddProperties(&module.properties)
+	android.InitSingleSourcePrebuiltModule(module, &module.properties, "Set")
+	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	return module
+}
+
+func (a *ApexSet) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	a.installFilename = a.InstallFilename()
+	if !strings.HasSuffix(a.installFilename, imageApexSuffix) {
+		ctx.ModuleErrorf("filename should end in %s for apex_set", imageApexSuffix)
+	}
+
+	apexSet := a.prebuilt.SingleSourcePath(ctx)
+	a.outputApex = android.PathForModuleOut(ctx, a.installFilename)
+	ctx.Build(pctx,
+		android.BuildParams{
+			Rule:        extractMatchingApex,
+			Description: "Extract an apex from an apex set",
+			Inputs:      android.Paths{apexSet},
+			Output:      a.outputApex,
+			Args: map[string]string{
+				"abis":              strings.Join(java.SupportedAbis(ctx), ","),
+				"allow-prereleased": strconv.FormatBool(proptools.Bool(a.properties.Prerelease)),
+				"sdk-version":       ctx.Config().PlatformSdkVersion(),
+			},
+		})
+	a.installDir = android.PathForModuleInstall(ctx, "apex")
+	if a.installable() {
+		ctx.InstallFile(a.installDir, a.installFilename, a.outputApex)
+	}
+
+	// in case that apex_set replaces source apex (using prefer: prop)
+	a.compatSymlinks = makeCompatSymlinks(a.BaseModuleName(), ctx)
+	// or that apex_set overrides other apexes (using overrides: prop)
+	for _, overridden := range a.properties.Overrides {
+		a.compatSymlinks = append(a.compatSymlinks, makeCompatSymlinks(overridden, ctx)...)
+	}
+}
+
+func (a *ApexSet) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{android.AndroidMkEntries{
+		Class:      "ETC",
+		OutputFile: android.OptionalPathForPath(a.outputApex),
+		Include:    "$(BUILD_PREBUILT)",
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(entries *android.AndroidMkEntries) {
+				entries.SetString("LOCAL_MODULE_PATH", a.installDir.ToMakePath().String())
+				entries.SetString("LOCAL_MODULE_STEM", a.installFilename)
+				entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !a.installable())
+				entries.AddStrings("LOCAL_OVERRIDES_MODULES", a.properties.Overrides...)
+				if len(a.compatSymlinks) > 0 {
+					entries.SetString("LOCAL_POST_INSTALL_CMD", strings.Join(a.compatSymlinks, " && "))
+				}
+			},
+		},
+	}}
+}
diff --git a/bpfix/bpfix/bpfix.go b/bpfix/bpfix/bpfix.go
index a1c5de1..e731750 100644
--- a/bpfix/bpfix/bpfix.go
+++ b/bpfix/bpfix/bpfix.go
@@ -408,6 +408,8 @@
 			switch mod.Type {
 			case "android_app":
 				mod.Type = "android_test"
+			case "android_app_import":
+				mod.Type = "android_test_import"
 			case "java_library", "java_library_installable":
 				mod.Type = "java_test"
 			case "java_library_host":
@@ -951,7 +953,8 @@
 			case strings.Contains(mod.Type, "cc_test"),
 				strings.Contains(mod.Type, "cc_library_static"),
 				strings.Contains(mod.Type, "java_test"),
-				mod.Type == "android_test":
+				mod.Type == "android_test",
+				mod.Type == "android_test_import":
 				continue
 			case strings.Contains(mod.Type, "cc_lib"):
 				replaceStr += `// WARNING: Module tags are not supported in Soong.
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 671adb5..3ce1a89 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -149,7 +149,7 @@
 	return []android.AndroidMkEntries{entries}
 }
 
-func androidMkWriteTestData(data android.Paths, ctx AndroidMkContext, entries *android.AndroidMkEntries) {
+func AndroidMkDataPaths(data android.Paths) []string {
 	var testFiles []string
 	for _, d := range data {
 		rel := d.Rel()
@@ -160,6 +160,11 @@
 		path = strings.TrimSuffix(path, rel)
 		testFiles = append(testFiles, path+":"+rel)
 	}
+	return testFiles
+}
+
+func androidMkWriteTestData(data android.Paths, ctx AndroidMkContext, entries *android.AndroidMkEntries) {
+	testFiles := AndroidMkDataPaths(data)
 	if len(testFiles) > 0 {
 		entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
 			entries.AddStrings("LOCAL_TEST_DATA", testFiles...)
diff --git a/cc/binary_sdk_member.go b/cc/binary_sdk_member.go
index 88ac513..372a72e 100644
--- a/cc/binary_sdk_member.go
+++ b/cc/binary_sdk_member.go
@@ -20,6 +20,7 @@
 	"android/soong/android"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
 )
 
 func init() {
@@ -65,7 +66,15 @@
 }
 
 func (mt *binarySdkMemberType) AddPrebuiltModule(ctx android.SdkMemberContext, member android.SdkMember) android.BpModule {
-	return ctx.SnapshotBuilder().AddPrebuiltModule(member, "cc_prebuilt_binary")
+	pbm := ctx.SnapshotBuilder().AddPrebuiltModule(member, "cc_prebuilt_binary")
+
+	ccModule := member.Variants()[0].(*Module)
+
+	if stl := ccModule.stl.Properties.Stl; stl != nil {
+		pbm.AddProperty("stl", proptools.String(stl))
+	}
+
+	return pbm
 }
 
 func (mt *binarySdkMemberType) CreateVariantPropertiesStruct() android.SdkMemberProperties {
@@ -105,6 +114,10 @@
 	//
 	// This field is exported as its contents may not be arch specific.
 	SystemSharedLibs []string
+
+	// Arch specific flags.
+	StaticExecutable bool
+	Nocrt            bool
 }
 
 func (p *nativeBinaryInfoProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) {
@@ -113,6 +126,10 @@
 	p.archType = ccModule.Target().Arch.ArchType.String()
 	p.outputFile = getRequiredMemberOutputFile(ctx, ccModule)
 
+	binaryLinker := ccModule.linker.(*binaryDecorator)
+	p.StaticExecutable = binaryLinker.static()
+	p.Nocrt = Bool(binaryLinker.baseLinker.Properties.Nocrt)
+
 	if ccModule.linker != nil {
 		specifiedDeps := specifiedDeps{}
 		specifiedDeps = ccModule.linker.linkerSpecifiedDeps(specifiedDeps)
@@ -143,4 +160,11 @@
 	if p.SystemSharedLibs != nil {
 		propertySet.AddPropertyWithTag("system_shared_libs", p.SystemSharedLibs, builder.SdkMemberReferencePropertyTag(false))
 	}
+
+	if p.StaticExecutable {
+		propertySet.AddProperty("static_executable", p.StaticExecutable)
+	}
+	if p.Nocrt {
+		propertySet.AddProperty("nocrt", p.Nocrt)
+	}
 }
diff --git a/cc/builder.go b/cc/builder.go
index 4e8f1fa..e571e5a 100644
--- a/cc/builder.go
+++ b/cc/builder.go
@@ -99,6 +99,15 @@
 		},
 		"arCmd", "arFlags")
 
+	arWithLibs = pctx.AndroidStaticRule("arWithLibs",
+		blueprint.RuleParams{
+			Command:        "rm -f ${out} && $arCmd $arObjFlags $out @${out}.rsp && $arCmd $arLibFlags $out $arLibs",
+			CommandDeps:    []string{"$arCmd"},
+			Rspfile:        "${out}.rsp",
+			RspfileContent: "${arObjs}",
+		},
+		"arCmd", "arObjFlags", "arObjs", "arLibFlags", "arLibs")
+
 	darwinStrip = pctx.AndroidStaticRule("darwinStrip",
 		blueprint.RuleParams{
 			Command:     "${config.MacStripPath} -u -r -o $out $in",
@@ -207,15 +216,23 @@
 		}, []string{"cFlags", "exportDirs"}, nil)
 
 	_ = pctx.SourcePathVariable("sAbiLinker", "prebuilts/clang-tools/${config.HostPrebuiltTag}/bin/header-abi-linker")
+	_ = pctx.SourcePathVariable("sAbiLinkerLibs", "prebuilts/clang-tools/${config.HostPrebuiltTag}/lib64")
 
-	sAbiLink = pctx.AndroidStaticRule("sAbiLink",
+	sAbiLink, sAbiLinkRE = remoteexec.StaticRules(pctx, "sAbiLink",
 		blueprint.RuleParams{
-			Command:        "$sAbiLinker -o ${out} $symbolFilter -arch $arch  $exportedHeaderFlags @${out}.rsp ",
+			Command:        "$reTemplate$sAbiLinker -o ${out} $symbolFilter -arch $arch  $exportedHeaderFlags @${out}.rsp ",
 			CommandDeps:    []string{"$sAbiLinker"},
 			Rspfile:        "${out}.rsp",
 			RspfileContent: "${in}",
-		},
-		"symbolFilter", "arch", "exportedHeaderFlags")
+		}, &remoteexec.REParams{
+			Labels:          map[string]string{"type": "tool", "name": "abi-linker"},
+			ExecStrategy:    "${config.REAbiLinkerExecStrategy}",
+			Inputs:          []string{"$sAbiLinkerLibs", "${out}.rsp", "$implicits"},
+			RSPFile:         "${out}.rsp",
+			OutputFiles:     []string{"$out"},
+			ToolchainInputs: []string{"$sAbiLinker"},
+			Platform:        map[string]string{remoteexec.PoolKey: "${config.RECXXPool}"},
+		}, []string{"symbolFilter", "arch", "exportedHeaderFlags"}, []string{"implicits"})
 
 	_ = pctx.SourcePathVariable("sAbiDiffer", "prebuilts/clang-tools/${config.HostPrebuiltTag}/bin/header-abi-diff")
 
@@ -601,26 +618,45 @@
 }
 
 // Generate a rule for compiling multiple .o files to a static library (.a)
-func TransformObjToStaticLib(ctx android.ModuleContext, objFiles android.Paths,
+func TransformObjToStaticLib(ctx android.ModuleContext,
+	objFiles android.Paths, wholeStaticLibs android.Paths,
 	flags builderFlags, outputFile android.ModuleOutPath, deps android.Paths) {
 
 	arCmd := "${config.ClangBin}/llvm-ar"
-	arFlags := "crsPD"
+	arFlags := ""
 	if !ctx.Darwin() {
 		arFlags += " -format=gnu"
 	}
 
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        ar,
-		Description: "static link " + outputFile.Base(),
-		Output:      outputFile,
-		Inputs:      objFiles,
-		Implicits:   deps,
-		Args: map[string]string{
-			"arFlags": arFlags,
-			"arCmd":   arCmd,
-		},
-	})
+	if len(wholeStaticLibs) == 0 {
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        ar,
+			Description: "static link " + outputFile.Base(),
+			Output:      outputFile,
+			Inputs:      objFiles,
+			Implicits:   deps,
+			Args: map[string]string{
+				"arFlags": "crsPD" + arFlags,
+				"arCmd":   arCmd,
+			},
+		})
+
+	} else {
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        arWithLibs,
+			Description: "static link " + outputFile.Base(),
+			Output:      outputFile,
+			Inputs:      append(objFiles, wholeStaticLibs...),
+			Implicits:   deps,
+			Args: map[string]string{
+				"arCmd":      arCmd,
+				"arObjFlags": "crsPD" + arFlags,
+				"arObjs":     strings.Join(objFiles.Strings(), " "),
+				"arLibFlags": "cqsL" + arFlags,
+				"arLibs":     strings.Join(wholeStaticLibs.Strings(), " "),
+			},
+		})
+	}
 }
 
 // Generate a rule for compiling multiple .o files, plus static libraries, whole static libraries,
@@ -724,17 +760,30 @@
 	for _, tag := range excludedSymbolTags {
 		symbolFilterStr += " --exclude-symbol-tag " + tag
 	}
+	rule := sAbiLink
+	args := map[string]string{
+		"symbolFilter":        symbolFilterStr,
+		"arch":                ctx.Arch().ArchType.Name,
+		"exportedHeaderFlags": exportedHeaderFlags,
+	}
+	if ctx.Config().IsEnvTrue("RBE_ABI_LINKER") {
+		rule = sAbiLinkRE
+		rbeImplicits := implicits.Strings()
+		for _, p := range strings.Split(exportedHeaderFlags, " ") {
+			if len(p) > 2 {
+				// Exclude the -I prefix.
+				rbeImplicits = append(rbeImplicits, p[2:])
+			}
+		}
+		args["implicits"] = strings.Join(rbeImplicits, ",")
+	}
 	ctx.Build(pctx, android.BuildParams{
-		Rule:        sAbiLink,
+		Rule:        rule,
 		Description: "header-abi-linker " + outputFile.Base(),
 		Output:      outputFile,
 		Inputs:      sAbiDumps,
 		Implicits:   implicits,
-		Args: map[string]string{
-			"symbolFilter":        symbolFilterStr,
-			"arch":                ctx.Arch().ArchType.Name,
-			"exportedHeaderFlags": exportedHeaderFlags,
-		},
+		Args:        args,
 	})
 	return android.OptionalPathForPath(outputFile)
 }
diff --git a/cc/cc.go b/cc/cc.go
index 02c4879..94512d5 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -124,10 +124,16 @@
 	StaticLibs, LateStaticLibs, WholeStaticLibs android.Paths
 
 	// Paths to .o files
-	Objs               Objects
+	Objs Objects
+	// Paths to .o files in dependencies that provide them. Note that these lists
+	// aren't complete since prebuilt modules don't provide the .o files.
 	StaticLibObjs      Objects
 	WholeStaticLibObjs Objects
 
+	// Paths to .a files in prebuilts. Complements WholeStaticLibObjs to contain
+	// the libs from all whole_static_lib dependencies.
+	WholeStaticLibsFromPrebuilts android.Paths
+
 	// Paths to generated source files
 	GeneratedSources android.Paths
 	GeneratedHeaders android.Paths
@@ -1068,7 +1074,7 @@
 
 func InstallToBootstrap(name string, config android.Config) bool {
 	if name == "libclang_rt.hwasan-aarch64-android" {
-		return inList("hwaddress", config.SanitizeDevice())
+		return true
 	}
 	return isBionic(name)
 }
@@ -1395,6 +1401,15 @@
 	return ok && test.isAllTestsVariation()
 }
 
+func (c *Module) DataPaths() android.Paths {
+	if p, ok := c.installer.(interface {
+		dataPaths() android.Paths
+	}); ok {
+		return p.dataPaths()
+	}
+	return nil
+}
+
 func (c *Module) getNameSuffixWithVndkVersion(ctx android.ModuleContext) string {
 	// Returns the name suffix for product and vendor variants. If the VNDK version is not
 	// "current", it will append the VNDK version to the name suffix.
@@ -2473,7 +2488,11 @@
 					}
 					ctx.AddMissingDependencies(missingDeps)
 				}
-				depPaths.WholeStaticLibObjs = depPaths.WholeStaticLibObjs.Append(staticLib.objs())
+				if _, ok := ccWholeStaticLib.linker.(prebuiltLinkerInterface); ok {
+					depPaths.WholeStaticLibsFromPrebuilts = append(depPaths.WholeStaticLibsFromPrebuilts, linkFile.Path())
+				} else {
+					depPaths.WholeStaticLibObjs = depPaths.WholeStaticLibObjs.Append(staticLib.objs())
+				}
 			} else {
 				ctx.ModuleErrorf(
 					"non-cc.Modules cannot be included as whole static libraries.", depName)
@@ -2862,6 +2881,9 @@
 				return false
 			}
 		}
+	} else if ctx.OtherModuleDependencyTag(dep) == llndkImplDep {
+		// We don't track beyond LLNDK
+		return false
 	}
 	return true
 }
diff --git a/cc/config/global.go b/cc/config/global.go
index 4e51ae9..1dd8a2d 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -162,7 +162,7 @@
 
 		// http://b/131390872
 		// Automatically initialize any uninitialized stack variables.
-		// Prefer zero-init if both options are set.
+		// Prefer zero-init if multiple options are set.
 		if ctx.Config().IsEnvTrue("AUTO_ZERO_INITIALIZE") {
 			flags = append(flags, "-ftrivial-auto-var-init=zero -enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang")
 		} else if ctx.Config().IsEnvTrue("AUTO_PATTERN_INITIALIZE") {
@@ -170,8 +170,8 @@
 		} else if ctx.Config().IsEnvTrue("AUTO_UNINITIALIZE") {
 			flags = append(flags, "-ftrivial-auto-var-init=uninitialized")
 		} else {
-			// Default to pattern initialization.
-			flags = append(flags, "-ftrivial-auto-var-init=pattern")
+			// Default to zero initialization.
+			flags = append(flags, "-ftrivial-auto-var-init=zero -enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang")
 		}
 
 		return strings.Join(flags, " ")
@@ -261,6 +261,7 @@
 	pctx.VariableFunc("RECXXLinksPool", remoteexec.EnvOverrideFunc("RBE_CXX_LINKS_POOL", remoteexec.DefaultPool))
 	pctx.VariableFunc("RECXXLinksExecStrategy", remoteexec.EnvOverrideFunc("RBE_CXX_LINKS_EXEC_STRATEGY", remoteexec.LocalExecStrategy))
 	pctx.VariableFunc("REAbiDumperExecStrategy", remoteexec.EnvOverrideFunc("RBE_ABI_DUMPER_EXEC_STRATEGY", remoteexec.LocalExecStrategy))
+	pctx.VariableFunc("REAbiLinkerExecStrategy", remoteexec.EnvOverrideFunc("RBE_ABI_LINKER_EXEC_STRATEGY", remoteexec.LocalExecStrategy))
 }
 
 var HostPrebuiltTag = pctx.VariableConfigMethod("HostPrebuiltTag", android.Config.PrebuiltOS)
diff --git a/cc/coverage.go b/cc/coverage.go
index cc9a1ad..f885fcb 100644
--- a/cc/coverage.go
+++ b/cc/coverage.go
@@ -92,7 +92,7 @@
 			// flags that the module may use.
 			flags.Local.CFlags = append(flags.Local.CFlags, "-Wno-frame-larger-than=", "-O0")
 		} else if clangCoverage {
-			flags.Local.CommonFlags = append(flags.Local.CommonFlags, "-fprofile-instr-generate", "-fcoverage-mapping")
+			flags.Local.CommonFlags = append(flags.Local.CommonFlags, "-fprofile-instr-generate", "-fcoverage-mapping", "-Wno-pass-failed")
 		}
 	}
 
diff --git a/cc/library.go b/cc/library.go
index ecea2ea..ba8b0f4 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -407,6 +407,31 @@
 		if strings.HasPrefix(dir, android.PathForOutput(ctx).String()) {
 			continue
 		}
+		// libeigen wrongly exports the root directory "external/eigen". But only two
+		// subdirectories "Eigen" and "unsupported" contain exported header files. Even worse
+		// some of them have no extension. So we need special treatment for libeigen in order
+		// to glob correctly.
+		if dir == "external/eigen" {
+			// Only these two directories contains exported headers.
+			for _, subdir := range []string{"Eigen", "unsupported/Eigen"} {
+				glob, err := ctx.GlobWithDeps("external/eigen/"+subdir+"/**/*", nil)
+				if err != nil {
+					ctx.ModuleErrorf("glob failed: %#v", err)
+					return
+				}
+				for _, header := range glob {
+					if strings.HasSuffix(header, "/") {
+						continue
+					}
+					ext := filepath.Ext(header)
+					if ext != "" && ext != ".h" {
+						continue
+					}
+					ret = append(ret, android.PathForSource(ctx, header))
+				}
+			}
+			continue
+		}
 		exts := headerExts
 		// Glob all files under this special directory, because of C++ headers.
 		if strings.HasPrefix(dir, "external/libcxx/include") {
@@ -874,7 +899,7 @@
 		}
 	}
 
-	TransformObjToStaticLib(ctx, library.objects.objFiles, builderFlags, outputFile, objs.tidyFiles)
+	TransformObjToStaticLib(ctx, library.objects.objFiles, deps.WholeStaticLibsFromPrebuilts, builderFlags, outputFile, objs.tidyFiles)
 
 	library.coverageOutputFile = TransformCoverageFilesToZip(ctx, library.objects, ctx.ModuleName())
 
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 3412c9b..aaaf694 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -989,6 +989,7 @@
 	android.Module
 	IsSanitizerEnabled(ctx android.BaseModuleContext, sanitizerName string) bool
 	EnableSanitizer(sanitizerName string)
+	AddSanitizerDependencies(ctx android.BottomUpMutatorContext, sanitizerName string)
 }
 
 // Create sanitized variants for modules that need them
@@ -1075,6 +1076,7 @@
 			c.sanitize.Properties.SanitizeDep = false
 		} else if sanitizeable, ok := mctx.Module().(Sanitizeable); ok && sanitizeable.IsSanitizerEnabled(mctx, t.name()) {
 			// APEX modules fall here
+			sanitizeable.AddSanitizerDependencies(mctx, t.name())
 			mctx.CreateVariations(t.variationName())
 		}
 	}
diff --git a/cc/test.go b/cc/test.go
index b1f8eec..2439c94 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -160,6 +160,10 @@
 	return test.baseCompiler.Properties.Srcs
 }
 
+func (test *testBinary) dataPaths() android.Paths {
+	return test.data
+}
+
 func (test *testBinary) isAllTestsVariation() bool {
 	stem := test.binaryDecorator.Properties.Stem
 	return stem != nil && *stem == ""
@@ -345,9 +349,7 @@
 	}
 	if Bool(test.Properties.Disable_framework) {
 		var options []tradefed.Option
-		options = append(options, tradefed.Option{Name: "run-command", Value: "stop"})
-		options = append(options, tradefed.Option{Name: "teardown-command", Value: "start"})
-		configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.RunCommandTargetPreparer", options})
+		configs = append(configs, tradefed.Object{"target_preparer", "com.android.tradefed.targetprep.StopServicesSetup", options})
 	}
 	if Bool(test.testDecorator.Properties.Isolated) {
 		configs = append(configs, tradefed.Option{Name: "not-shardable", Value: "true"})
diff --git a/cc/vndk.go b/cc/vndk.go
index dbe1f3b..e5e4533 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -309,7 +309,13 @@
 		panic(err)
 	}
 
-	if m.HasStubsVariants() {
+	if m.HasStubsVariants() && name != "libz" {
+		// b/155456180 libz is the ONLY exception here. We don't want to make
+		// libz an LLNDK library because we in general can't guarantee that
+		// libz will behave consistently especially about the compression.
+		// i.e. the compressed output might be different across releases.
+		// As the library is an external one, it's risky to keep the compatibility
+		// promise if it becomes an LLNDK.
 		mctx.PropertyErrorf("vndk.enabled", "This library provides stubs. Shouldn't be VNDK. Consider making it as LLNDK")
 	}
 
diff --git a/cmd/extract_apks/Android.bp b/cmd/extract_apks/Android.bp
new file mode 100644
index 0000000..90548cd
--- /dev/null
+++ b/cmd/extract_apks/Android.bp
@@ -0,0 +1,21 @@
+blueprint_go_binary {
+    name: "extract_apks",
+    srcs: ["main.go"],
+    deps: [
+        "android-archive-zip",
+        "golang-protobuf-proto",
+        "soong-cmd-extract_apks-proto",
+    ],
+    testSrcs: ["main_test.go"]
+}
+
+bootstrap_go_package {
+    name: "soong-cmd-extract_apks-proto",
+    pkgPath: "android/soong/cmd/extract_apks/bundle_proto",
+    deps: ["golang-protobuf-proto"],
+    srcs: [
+        "bundle_proto/commands.pb.go",
+        "bundle_proto/config.pb.go",
+        "bundle_proto/targeting.pb.go",
+    ],
+}
diff --git a/cmd/extract_apks/bundle_proto/commands.pb.go b/cmd/extract_apks/bundle_proto/commands.pb.go
new file mode 100644
index 0000000..bbf3314
--- /dev/null
+++ b/cmd/extract_apks/bundle_proto/commands.pb.go
@@ -0,0 +1,1033 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: commands.proto
+
+package android_bundle_proto
+
+import (
+	fmt "fmt"
+	proto "github.com/golang/protobuf/proto"
+	math "math"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+
+type DeliveryType int32
+
+const (
+	DeliveryType_UNKNOWN_DELIVERY_TYPE DeliveryType = 0
+	DeliveryType_INSTALL_TIME          DeliveryType = 1
+	DeliveryType_ON_DEMAND             DeliveryType = 2
+	DeliveryType_FAST_FOLLOW           DeliveryType = 3
+)
+
+var DeliveryType_name = map[int32]string{
+	0: "UNKNOWN_DELIVERY_TYPE",
+	1: "INSTALL_TIME",
+	2: "ON_DEMAND",
+	3: "FAST_FOLLOW",
+}
+
+var DeliveryType_value = map[string]int32{
+	"UNKNOWN_DELIVERY_TYPE": 0,
+	"INSTALL_TIME":          1,
+	"ON_DEMAND":             2,
+	"FAST_FOLLOW":           3,
+}
+
+func (x DeliveryType) String() string {
+	return proto.EnumName(DeliveryType_name, int32(x))
+}
+
+func (DeliveryType) EnumDescriptor() ([]byte, []int) {
+	return fileDescriptor_0dff099eb2e3dfdb, []int{0}
+}
+
+type SystemApkMetadata_SystemApkType int32
+
+const (
+	SystemApkMetadata_UNSPECIFIED_VALUE SystemApkMetadata_SystemApkType = 0
+	// Uncompressed APK for system image.
+	SystemApkMetadata_SYSTEM SystemApkMetadata_SystemApkType = 1
+	// Stub APK for compressed APK in the system image
+	// (contains only android manifest).
+	SystemApkMetadata_SYSTEM_STUB SystemApkMetadata_SystemApkType = 2
+	// Compressed APK for system image.
+	SystemApkMetadata_SYSTEM_COMPRESSED SystemApkMetadata_SystemApkType = 3
+)
+
+var SystemApkMetadata_SystemApkType_name = map[int32]string{
+	0: "UNSPECIFIED_VALUE",
+	1: "SYSTEM",
+	2: "SYSTEM_STUB",
+	3: "SYSTEM_COMPRESSED",
+}
+
+var SystemApkMetadata_SystemApkType_value = map[string]int32{
+	"UNSPECIFIED_VALUE": 0,
+	"SYSTEM":            1,
+	"SYSTEM_STUB":       2,
+	"SYSTEM_COMPRESSED": 3,
+}
+
+func (x SystemApkMetadata_SystemApkType) String() string {
+	return proto.EnumName(SystemApkMetadata_SystemApkType_name, int32(x))
+}
+
+func (SystemApkMetadata_SystemApkType) EnumDescriptor() ([]byte, []int) {
+	return fileDescriptor_0dff099eb2e3dfdb, []int{10, 0}
+}
+
+// Describes the output of the "build-apks" command.
+type BuildApksResult struct {
+	// The package name of this app.
+	PackageName string `protobuf:"bytes,4,opt,name=package_name,json=packageName,proto3" json:"package_name,omitempty"`
+	// List of the created variants.
+	Variant []*Variant `protobuf:"bytes,1,rep,name=variant,proto3" json:"variant,omitempty"`
+	// Metadata about BundleTool used to build the APKs.
+	Bundletool *Bundletool `protobuf:"bytes,2,opt,name=bundletool,proto3" json:"bundletool,omitempty"`
+	// List of the created asset slices.
+	AssetSliceSet []*AssetSliceSet `protobuf:"bytes,3,rep,name=asset_slice_set,json=assetSliceSet,proto3" json:"asset_slice_set,omitempty"`
+	// Information about local testing mode.
+	LocalTestingInfo     *LocalTestingInfo `protobuf:"bytes,5,opt,name=local_testing_info,json=localTestingInfo,proto3" json:"local_testing_info,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}          `json:"-"`
+	XXX_unrecognized     []byte            `json:"-"`
+	XXX_sizecache        int32             `json:"-"`
+}
+
+func (m *BuildApksResult) Reset()         { *m = BuildApksResult{} }
+func (m *BuildApksResult) String() string { return proto.CompactTextString(m) }
+func (*BuildApksResult) ProtoMessage()    {}
+func (*BuildApksResult) Descriptor() ([]byte, []int) {
+	return fileDescriptor_0dff099eb2e3dfdb, []int{0}
+}
+
+func (m *BuildApksResult) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_BuildApksResult.Unmarshal(m, b)
+}
+func (m *BuildApksResult) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_BuildApksResult.Marshal(b, m, deterministic)
+}
+func (m *BuildApksResult) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_BuildApksResult.Merge(m, src)
+}
+func (m *BuildApksResult) XXX_Size() int {
+	return xxx_messageInfo_BuildApksResult.Size(m)
+}
+func (m *BuildApksResult) XXX_DiscardUnknown() {
+	xxx_messageInfo_BuildApksResult.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_BuildApksResult proto.InternalMessageInfo
+
+func (m *BuildApksResult) GetPackageName() string {
+	if m != nil {
+		return m.PackageName
+	}
+	return ""
+}
+
+func (m *BuildApksResult) GetVariant() []*Variant {
+	if m != nil {
+		return m.Variant
+	}
+	return nil
+}
+
+func (m *BuildApksResult) GetBundletool() *Bundletool {
+	if m != nil {
+		return m.Bundletool
+	}
+	return nil
+}
+
+func (m *BuildApksResult) GetAssetSliceSet() []*AssetSliceSet {
+	if m != nil {
+		return m.AssetSliceSet
+	}
+	return nil
+}
+
+func (m *BuildApksResult) GetLocalTestingInfo() *LocalTestingInfo {
+	if m != nil {
+		return m.LocalTestingInfo
+	}
+	return nil
+}
+
+// Variant is a group of APKs that covers a part of the device configuration
+// space. APKs from multiple variants are never combined on one device.
+type Variant struct {
+	// Variant-level targeting.
+	// This targeting is fairly high-level and each APK has its own targeting as
+	// well.
+	Targeting *VariantTargeting `protobuf:"bytes,1,opt,name=targeting,proto3" json:"targeting,omitempty"`
+	// Set of APKs, one set per module.
+	ApkSet []*ApkSet `protobuf:"bytes,2,rep,name=apk_set,json=apkSet,proto3" json:"apk_set,omitempty"`
+	// Number of the variant, starting at 0 (unless overridden).
+	// A device will receive APKs from the first variant that matches the device
+	// configuration, with higher variant numbers having priority over lower
+	// variant numbers.
+	VariantNumber        uint32   `protobuf:"varint,3,opt,name=variant_number,json=variantNumber,proto3" json:"variant_number,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *Variant) Reset()         { *m = Variant{} }
+func (m *Variant) String() string { return proto.CompactTextString(m) }
+func (*Variant) ProtoMessage()    {}
+func (*Variant) Descriptor() ([]byte, []int) {
+	return fileDescriptor_0dff099eb2e3dfdb, []int{1}
+}
+
+func (m *Variant) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_Variant.Unmarshal(m, b)
+}
+func (m *Variant) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_Variant.Marshal(b, m, deterministic)
+}
+func (m *Variant) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Variant.Merge(m, src)
+}
+func (m *Variant) XXX_Size() int {
+	return xxx_messageInfo_Variant.Size(m)
+}
+func (m *Variant) XXX_DiscardUnknown() {
+	xxx_messageInfo_Variant.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Variant proto.InternalMessageInfo
+
+func (m *Variant) GetTargeting() *VariantTargeting {
+	if m != nil {
+		return m.Targeting
+	}
+	return nil
+}
+
+func (m *Variant) GetApkSet() []*ApkSet {
+	if m != nil {
+		return m.ApkSet
+	}
+	return nil
+}
+
+func (m *Variant) GetVariantNumber() uint32 {
+	if m != nil {
+		return m.VariantNumber
+	}
+	return 0
+}
+
+// Represents a module.
+// For pre-L devices multiple modules (possibly all) may be merged into one.
+type ApkSet struct {
+	ModuleMetadata *ModuleMetadata `protobuf:"bytes,1,opt,name=module_metadata,json=moduleMetadata,proto3" json:"module_metadata,omitempty"`
+	// APKs.
+	ApkDescription       []*ApkDescription `protobuf:"bytes,2,rep,name=apk_description,json=apkDescription,proto3" json:"apk_description,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}          `json:"-"`
+	XXX_unrecognized     []byte            `json:"-"`
+	XXX_sizecache        int32             `json:"-"`
+}
+
+func (m *ApkSet) Reset()         { *m = ApkSet{} }
+func (m *ApkSet) String() string { return proto.CompactTextString(m) }
+func (*ApkSet) ProtoMessage()    {}
+func (*ApkSet) Descriptor() ([]byte, []int) {
+	return fileDescriptor_0dff099eb2e3dfdb, []int{2}
+}
+
+func (m *ApkSet) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_ApkSet.Unmarshal(m, b)
+}
+func (m *ApkSet) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_ApkSet.Marshal(b, m, deterministic)
+}
+func (m *ApkSet) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_ApkSet.Merge(m, src)
+}
+func (m *ApkSet) XXX_Size() int {
+	return xxx_messageInfo_ApkSet.Size(m)
+}
+func (m *ApkSet) XXX_DiscardUnknown() {
+	xxx_messageInfo_ApkSet.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ApkSet proto.InternalMessageInfo
+
+func (m *ApkSet) GetModuleMetadata() *ModuleMetadata {
+	if m != nil {
+		return m.ModuleMetadata
+	}
+	return nil
+}
+
+func (m *ApkSet) GetApkDescription() []*ApkDescription {
+	if m != nil {
+		return m.ApkDescription
+	}
+	return nil
+}
+
+type ModuleMetadata struct {
+	// Module name.
+	Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+	// Indicates the delivery type (e.g. on-demand) of the module.
+	DeliveryType DeliveryType `protobuf:"varint,6,opt,name=delivery_type,json=deliveryType,proto3,enum=android.bundle.DeliveryType" json:"delivery_type,omitempty"`
+	// Indicates whether this module is marked "instant".
+	IsInstant bool `protobuf:"varint,3,opt,name=is_instant,json=isInstant,proto3" json:"is_instant,omitempty"`
+	// Names of the modules that this module directly depends on.
+	// Each module implicitly depends on the base module.
+	Dependencies []string `protobuf:"bytes,4,rep,name=dependencies,proto3" json:"dependencies,omitempty"`
+	// The targeting that makes a conditional module installed.
+	// Relevant only for Split APKs.
+	Targeting *ModuleTargeting `protobuf:"bytes,5,opt,name=targeting,proto3" json:"targeting,omitempty"`
+	// Deprecated. Please use delivery_type.
+	OnDemandDeprecated   bool     `protobuf:"varint,2,opt,name=on_demand_deprecated,json=onDemandDeprecated,proto3" json:"on_demand_deprecated,omitempty"` // Deprecated: Do not use.
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *ModuleMetadata) Reset()         { *m = ModuleMetadata{} }
+func (m *ModuleMetadata) String() string { return proto.CompactTextString(m) }
+func (*ModuleMetadata) ProtoMessage()    {}
+func (*ModuleMetadata) Descriptor() ([]byte, []int) {
+	return fileDescriptor_0dff099eb2e3dfdb, []int{3}
+}
+
+func (m *ModuleMetadata) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_ModuleMetadata.Unmarshal(m, b)
+}
+func (m *ModuleMetadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_ModuleMetadata.Marshal(b, m, deterministic)
+}
+func (m *ModuleMetadata) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_ModuleMetadata.Merge(m, src)
+}
+func (m *ModuleMetadata) XXX_Size() int {
+	return xxx_messageInfo_ModuleMetadata.Size(m)
+}
+func (m *ModuleMetadata) XXX_DiscardUnknown() {
+	xxx_messageInfo_ModuleMetadata.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ModuleMetadata proto.InternalMessageInfo
+
+func (m *ModuleMetadata) GetName() string {
+	if m != nil {
+		return m.Name
+	}
+	return ""
+}
+
+func (m *ModuleMetadata) GetDeliveryType() DeliveryType {
+	if m != nil {
+		return m.DeliveryType
+	}
+	return DeliveryType_UNKNOWN_DELIVERY_TYPE
+}
+
+func (m *ModuleMetadata) GetIsInstant() bool {
+	if m != nil {
+		return m.IsInstant
+	}
+	return false
+}
+
+func (m *ModuleMetadata) GetDependencies() []string {
+	if m != nil {
+		return m.Dependencies
+	}
+	return nil
+}
+
+func (m *ModuleMetadata) GetTargeting() *ModuleTargeting {
+	if m != nil {
+		return m.Targeting
+	}
+	return nil
+}
+
+// Deprecated: Do not use.
+func (m *ModuleMetadata) GetOnDemandDeprecated() bool {
+	if m != nil {
+		return m.OnDemandDeprecated
+	}
+	return false
+}
+
+// Set of asset slices belonging to a single asset module.
+type AssetSliceSet struct {
+	// Module level metadata.
+	AssetModuleMetadata *AssetModuleMetadata `protobuf:"bytes,1,opt,name=asset_module_metadata,json=assetModuleMetadata,proto3" json:"asset_module_metadata,omitempty"`
+	// Asset slices.
+	ApkDescription       []*ApkDescription `protobuf:"bytes,2,rep,name=apk_description,json=apkDescription,proto3" json:"apk_description,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}          `json:"-"`
+	XXX_unrecognized     []byte            `json:"-"`
+	XXX_sizecache        int32             `json:"-"`
+}
+
+func (m *AssetSliceSet) Reset()         { *m = AssetSliceSet{} }
+func (m *AssetSliceSet) String() string { return proto.CompactTextString(m) }
+func (*AssetSliceSet) ProtoMessage()    {}
+func (*AssetSliceSet) Descriptor() ([]byte, []int) {
+	return fileDescriptor_0dff099eb2e3dfdb, []int{4}
+}
+
+func (m *AssetSliceSet) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_AssetSliceSet.Unmarshal(m, b)
+}
+func (m *AssetSliceSet) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_AssetSliceSet.Marshal(b, m, deterministic)
+}
+func (m *AssetSliceSet) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_AssetSliceSet.Merge(m, src)
+}
+func (m *AssetSliceSet) XXX_Size() int {
+	return xxx_messageInfo_AssetSliceSet.Size(m)
+}
+func (m *AssetSliceSet) XXX_DiscardUnknown() {
+	xxx_messageInfo_AssetSliceSet.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_AssetSliceSet proto.InternalMessageInfo
+
+func (m *AssetSliceSet) GetAssetModuleMetadata() *AssetModuleMetadata {
+	if m != nil {
+		return m.AssetModuleMetadata
+	}
+	return nil
+}
+
+func (m *AssetSliceSet) GetApkDescription() []*ApkDescription {
+	if m != nil {
+		return m.ApkDescription
+	}
+	return nil
+}
+
+type AssetModuleMetadata struct {
+	// Module name.
+	Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
+	// Indicates the delivery type for persistent install.
+	DeliveryType DeliveryType `protobuf:"varint,4,opt,name=delivery_type,json=deliveryType,proto3,enum=android.bundle.DeliveryType" json:"delivery_type,omitempty"`
+	// Metadata for instant installs.
+	InstantMetadata *InstantMetadata `protobuf:"bytes,3,opt,name=instant_metadata,json=instantMetadata,proto3" json:"instant_metadata,omitempty"`
+	// Deprecated. Use delivery_type.
+	OnDemandDeprecated   bool     `protobuf:"varint,2,opt,name=on_demand_deprecated,json=onDemandDeprecated,proto3" json:"on_demand_deprecated,omitempty"` // Deprecated: Do not use.
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *AssetModuleMetadata) Reset()         { *m = AssetModuleMetadata{} }
+func (m *AssetModuleMetadata) String() string { return proto.CompactTextString(m) }
+func (*AssetModuleMetadata) ProtoMessage()    {}
+func (*AssetModuleMetadata) Descriptor() ([]byte, []int) {
+	return fileDescriptor_0dff099eb2e3dfdb, []int{5}
+}
+
+func (m *AssetModuleMetadata) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_AssetModuleMetadata.Unmarshal(m, b)
+}
+func (m *AssetModuleMetadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_AssetModuleMetadata.Marshal(b, m, deterministic)
+}
+func (m *AssetModuleMetadata) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_AssetModuleMetadata.Merge(m, src)
+}
+func (m *AssetModuleMetadata) XXX_Size() int {
+	return xxx_messageInfo_AssetModuleMetadata.Size(m)
+}
+func (m *AssetModuleMetadata) XXX_DiscardUnknown() {
+	xxx_messageInfo_AssetModuleMetadata.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_AssetModuleMetadata proto.InternalMessageInfo
+
+func (m *AssetModuleMetadata) GetName() string {
+	if m != nil {
+		return m.Name
+	}
+	return ""
+}
+
+func (m *AssetModuleMetadata) GetDeliveryType() DeliveryType {
+	if m != nil {
+		return m.DeliveryType
+	}
+	return DeliveryType_UNKNOWN_DELIVERY_TYPE
+}
+
+func (m *AssetModuleMetadata) GetInstantMetadata() *InstantMetadata {
+	if m != nil {
+		return m.InstantMetadata
+	}
+	return nil
+}
+
+// Deprecated: Do not use.
+func (m *AssetModuleMetadata) GetOnDemandDeprecated() bool {
+	if m != nil {
+		return m.OnDemandDeprecated
+	}
+	return false
+}
+
+type InstantMetadata struct {
+	// Indicates whether this module is marked "instant".
+	IsInstant bool `protobuf:"varint,1,opt,name=is_instant,json=isInstant,proto3" json:"is_instant,omitempty"`
+	// Indicates the delivery type for instant install.
+	DeliveryType DeliveryType `protobuf:"varint,3,opt,name=delivery_type,json=deliveryType,proto3,enum=android.bundle.DeliveryType" json:"delivery_type,omitempty"`
+	// Deprecated. Use delivery_type.
+	OnDemandDeprecated   bool     `protobuf:"varint,2,opt,name=on_demand_deprecated,json=onDemandDeprecated,proto3" json:"on_demand_deprecated,omitempty"` // Deprecated: Do not use.
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *InstantMetadata) Reset()         { *m = InstantMetadata{} }
+func (m *InstantMetadata) String() string { return proto.CompactTextString(m) }
+func (*InstantMetadata) ProtoMessage()    {}
+func (*InstantMetadata) Descriptor() ([]byte, []int) {
+	return fileDescriptor_0dff099eb2e3dfdb, []int{6}
+}
+
+func (m *InstantMetadata) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_InstantMetadata.Unmarshal(m, b)
+}
+func (m *InstantMetadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_InstantMetadata.Marshal(b, m, deterministic)
+}
+func (m *InstantMetadata) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_InstantMetadata.Merge(m, src)
+}
+func (m *InstantMetadata) XXX_Size() int {
+	return xxx_messageInfo_InstantMetadata.Size(m)
+}
+func (m *InstantMetadata) XXX_DiscardUnknown() {
+	xxx_messageInfo_InstantMetadata.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_InstantMetadata proto.InternalMessageInfo
+
+func (m *InstantMetadata) GetIsInstant() bool {
+	if m != nil {
+		return m.IsInstant
+	}
+	return false
+}
+
+func (m *InstantMetadata) GetDeliveryType() DeliveryType {
+	if m != nil {
+		return m.DeliveryType
+	}
+	return DeliveryType_UNKNOWN_DELIVERY_TYPE
+}
+
+// Deprecated: Do not use.
+func (m *InstantMetadata) GetOnDemandDeprecated() bool {
+	if m != nil {
+		return m.OnDemandDeprecated
+	}
+	return false
+}
+
+type ApkDescription struct {
+	Targeting *ApkTargeting `protobuf:"bytes,1,opt,name=targeting,proto3" json:"targeting,omitempty"`
+	// Path to the APK file.
+	// BEGIN-INTERNAL
+	// The path may be a blobkey if the proto is not constructed by bundletool.
+	// END-INTERNAL
+	Path string `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"`
+	// Types that are valid to be assigned to ApkMetadataOneofValue:
+	//	*ApkDescription_SplitApkMetadata
+	//	*ApkDescription_StandaloneApkMetadata
+	//	*ApkDescription_InstantApkMetadata
+	//	*ApkDescription_SystemApkMetadata
+	//	*ApkDescription_AssetSliceMetadata
+	//	*ApkDescription_ApexApkMetadata
+	ApkMetadataOneofValue isApkDescription_ApkMetadataOneofValue `protobuf_oneof:"apk_metadata_oneof_value"`
+	XXX_NoUnkeyedLiteral  struct{}                               `json:"-"`
+	XXX_unrecognized      []byte                                 `json:"-"`
+	XXX_sizecache         int32                                  `json:"-"`
+}
+
+func (m *ApkDescription) Reset()         { *m = ApkDescription{} }
+func (m *ApkDescription) String() string { return proto.CompactTextString(m) }
+func (*ApkDescription) ProtoMessage()    {}
+func (*ApkDescription) Descriptor() ([]byte, []int) {
+	return fileDescriptor_0dff099eb2e3dfdb, []int{7}
+}
+
+func (m *ApkDescription) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_ApkDescription.Unmarshal(m, b)
+}
+func (m *ApkDescription) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_ApkDescription.Marshal(b, m, deterministic)
+}
+func (m *ApkDescription) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_ApkDescription.Merge(m, src)
+}
+func (m *ApkDescription) XXX_Size() int {
+	return xxx_messageInfo_ApkDescription.Size(m)
+}
+func (m *ApkDescription) XXX_DiscardUnknown() {
+	xxx_messageInfo_ApkDescription.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ApkDescription proto.InternalMessageInfo
+
+func (m *ApkDescription) GetTargeting() *ApkTargeting {
+	if m != nil {
+		return m.Targeting
+	}
+	return nil
+}
+
+func (m *ApkDescription) GetPath() string {
+	if m != nil {
+		return m.Path
+	}
+	return ""
+}
+
+type isApkDescription_ApkMetadataOneofValue interface {
+	isApkDescription_ApkMetadataOneofValue()
+}
+
+type ApkDescription_SplitApkMetadata struct {
+	SplitApkMetadata *SplitApkMetadata `protobuf:"bytes,3,opt,name=split_apk_metadata,json=splitApkMetadata,proto3,oneof"`
+}
+
+type ApkDescription_StandaloneApkMetadata struct {
+	StandaloneApkMetadata *StandaloneApkMetadata `protobuf:"bytes,4,opt,name=standalone_apk_metadata,json=standaloneApkMetadata,proto3,oneof"`
+}
+
+type ApkDescription_InstantApkMetadata struct {
+	InstantApkMetadata *SplitApkMetadata `protobuf:"bytes,5,opt,name=instant_apk_metadata,json=instantApkMetadata,proto3,oneof"`
+}
+
+type ApkDescription_SystemApkMetadata struct {
+	SystemApkMetadata *SystemApkMetadata `protobuf:"bytes,6,opt,name=system_apk_metadata,json=systemApkMetadata,proto3,oneof"`
+}
+
+type ApkDescription_AssetSliceMetadata struct {
+	AssetSliceMetadata *SplitApkMetadata `protobuf:"bytes,7,opt,name=asset_slice_metadata,json=assetSliceMetadata,proto3,oneof"`
+}
+
+type ApkDescription_ApexApkMetadata struct {
+	ApexApkMetadata *ApexApkMetadata `protobuf:"bytes,8,opt,name=apex_apk_metadata,json=apexApkMetadata,proto3,oneof"`
+}
+
+func (*ApkDescription_SplitApkMetadata) isApkDescription_ApkMetadataOneofValue() {}
+
+func (*ApkDescription_StandaloneApkMetadata) isApkDescription_ApkMetadataOneofValue() {}
+
+func (*ApkDescription_InstantApkMetadata) isApkDescription_ApkMetadataOneofValue() {}
+
+func (*ApkDescription_SystemApkMetadata) isApkDescription_ApkMetadataOneofValue() {}
+
+func (*ApkDescription_AssetSliceMetadata) isApkDescription_ApkMetadataOneofValue() {}
+
+func (*ApkDescription_ApexApkMetadata) isApkDescription_ApkMetadataOneofValue() {}
+
+func (m *ApkDescription) GetApkMetadataOneofValue() isApkDescription_ApkMetadataOneofValue {
+	if m != nil {
+		return m.ApkMetadataOneofValue
+	}
+	return nil
+}
+
+func (m *ApkDescription) GetSplitApkMetadata() *SplitApkMetadata {
+	if x, ok := m.GetApkMetadataOneofValue().(*ApkDescription_SplitApkMetadata); ok {
+		return x.SplitApkMetadata
+	}
+	return nil
+}
+
+func (m *ApkDescription) GetStandaloneApkMetadata() *StandaloneApkMetadata {
+	if x, ok := m.GetApkMetadataOneofValue().(*ApkDescription_StandaloneApkMetadata); ok {
+		return x.StandaloneApkMetadata
+	}
+	return nil
+}
+
+func (m *ApkDescription) GetInstantApkMetadata() *SplitApkMetadata {
+	if x, ok := m.GetApkMetadataOneofValue().(*ApkDescription_InstantApkMetadata); ok {
+		return x.InstantApkMetadata
+	}
+	return nil
+}
+
+func (m *ApkDescription) GetSystemApkMetadata() *SystemApkMetadata {
+	if x, ok := m.GetApkMetadataOneofValue().(*ApkDescription_SystemApkMetadata); ok {
+		return x.SystemApkMetadata
+	}
+	return nil
+}
+
+func (m *ApkDescription) GetAssetSliceMetadata() *SplitApkMetadata {
+	if x, ok := m.GetApkMetadataOneofValue().(*ApkDescription_AssetSliceMetadata); ok {
+		return x.AssetSliceMetadata
+	}
+	return nil
+}
+
+func (m *ApkDescription) GetApexApkMetadata() *ApexApkMetadata {
+	if x, ok := m.GetApkMetadataOneofValue().(*ApkDescription_ApexApkMetadata); ok {
+		return x.ApexApkMetadata
+	}
+	return nil
+}
+
+// XXX_OneofWrappers is for the internal use of the proto package.
+func (*ApkDescription) XXX_OneofWrappers() []interface{} {
+	return []interface{}{
+		(*ApkDescription_SplitApkMetadata)(nil),
+		(*ApkDescription_StandaloneApkMetadata)(nil),
+		(*ApkDescription_InstantApkMetadata)(nil),
+		(*ApkDescription_SystemApkMetadata)(nil),
+		(*ApkDescription_AssetSliceMetadata)(nil),
+		(*ApkDescription_ApexApkMetadata)(nil),
+	}
+}
+
+// Holds data specific to Split APKs.
+type SplitApkMetadata struct {
+	SplitId string `protobuf:"bytes,1,opt,name=split_id,json=splitId,proto3" json:"split_id,omitempty"`
+	// Indicates whether this APK is the master split of the module.
+	IsMasterSplit        bool     `protobuf:"varint,2,opt,name=is_master_split,json=isMasterSplit,proto3" json:"is_master_split,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *SplitApkMetadata) Reset()         { *m = SplitApkMetadata{} }
+func (m *SplitApkMetadata) String() string { return proto.CompactTextString(m) }
+func (*SplitApkMetadata) ProtoMessage()    {}
+func (*SplitApkMetadata) Descriptor() ([]byte, []int) {
+	return fileDescriptor_0dff099eb2e3dfdb, []int{8}
+}
+
+func (m *SplitApkMetadata) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_SplitApkMetadata.Unmarshal(m, b)
+}
+func (m *SplitApkMetadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_SplitApkMetadata.Marshal(b, m, deterministic)
+}
+func (m *SplitApkMetadata) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_SplitApkMetadata.Merge(m, src)
+}
+func (m *SplitApkMetadata) XXX_Size() int {
+	return xxx_messageInfo_SplitApkMetadata.Size(m)
+}
+func (m *SplitApkMetadata) XXX_DiscardUnknown() {
+	xxx_messageInfo_SplitApkMetadata.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_SplitApkMetadata proto.InternalMessageInfo
+
+func (m *SplitApkMetadata) GetSplitId() string {
+	if m != nil {
+		return m.SplitId
+	}
+	return ""
+}
+
+func (m *SplitApkMetadata) GetIsMasterSplit() bool {
+	if m != nil {
+		return m.IsMasterSplit
+	}
+	return false
+}
+
+// Holds data specific to Standalone APKs.
+type StandaloneApkMetadata struct {
+	// Names of the modules fused in this standalone APK.
+	FusedModuleName      []string `protobuf:"bytes,1,rep,name=fused_module_name,json=fusedModuleName,proto3" json:"fused_module_name,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *StandaloneApkMetadata) Reset()         { *m = StandaloneApkMetadata{} }
+func (m *StandaloneApkMetadata) String() string { return proto.CompactTextString(m) }
+func (*StandaloneApkMetadata) ProtoMessage()    {}
+func (*StandaloneApkMetadata) Descriptor() ([]byte, []int) {
+	return fileDescriptor_0dff099eb2e3dfdb, []int{9}
+}
+
+func (m *StandaloneApkMetadata) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_StandaloneApkMetadata.Unmarshal(m, b)
+}
+func (m *StandaloneApkMetadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_StandaloneApkMetadata.Marshal(b, m, deterministic)
+}
+func (m *StandaloneApkMetadata) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_StandaloneApkMetadata.Merge(m, src)
+}
+func (m *StandaloneApkMetadata) XXX_Size() int {
+	return xxx_messageInfo_StandaloneApkMetadata.Size(m)
+}
+func (m *StandaloneApkMetadata) XXX_DiscardUnknown() {
+	xxx_messageInfo_StandaloneApkMetadata.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_StandaloneApkMetadata proto.InternalMessageInfo
+
+func (m *StandaloneApkMetadata) GetFusedModuleName() []string {
+	if m != nil {
+		return m.FusedModuleName
+	}
+	return nil
+}
+
+// Holds data specific to system APKs.
+type SystemApkMetadata struct {
+	// Names of the modules fused in this system APK.
+	FusedModuleName []string `protobuf:"bytes,1,rep,name=fused_module_name,json=fusedModuleName,proto3" json:"fused_module_name,omitempty"`
+	// Indicates whether the APK is uncompressed system APK, stub APK or
+	// compressed system APK.
+	SystemApkType        SystemApkMetadata_SystemApkType `protobuf:"varint,2,opt,name=system_apk_type,json=systemApkType,proto3,enum=android.bundle.SystemApkMetadata_SystemApkType" json:"system_apk_type,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}                        `json:"-"`
+	XXX_unrecognized     []byte                          `json:"-"`
+	XXX_sizecache        int32                           `json:"-"`
+}
+
+func (m *SystemApkMetadata) Reset()         { *m = SystemApkMetadata{} }
+func (m *SystemApkMetadata) String() string { return proto.CompactTextString(m) }
+func (*SystemApkMetadata) ProtoMessage()    {}
+func (*SystemApkMetadata) Descriptor() ([]byte, []int) {
+	return fileDescriptor_0dff099eb2e3dfdb, []int{10}
+}
+
+func (m *SystemApkMetadata) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_SystemApkMetadata.Unmarshal(m, b)
+}
+func (m *SystemApkMetadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_SystemApkMetadata.Marshal(b, m, deterministic)
+}
+func (m *SystemApkMetadata) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_SystemApkMetadata.Merge(m, src)
+}
+func (m *SystemApkMetadata) XXX_Size() int {
+	return xxx_messageInfo_SystemApkMetadata.Size(m)
+}
+func (m *SystemApkMetadata) XXX_DiscardUnknown() {
+	xxx_messageInfo_SystemApkMetadata.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_SystemApkMetadata proto.InternalMessageInfo
+
+func (m *SystemApkMetadata) GetFusedModuleName() []string {
+	if m != nil {
+		return m.FusedModuleName
+	}
+	return nil
+}
+
+func (m *SystemApkMetadata) GetSystemApkType() SystemApkMetadata_SystemApkType {
+	if m != nil {
+		return m.SystemApkType
+	}
+	return SystemApkMetadata_UNSPECIFIED_VALUE
+}
+
+// Holds data specific to APEX APKs.
+type ApexApkMetadata struct {
+	// Configuration for processing of APKs embedded in an APEX image.
+	ApexEmbeddedApkConfig []*ApexEmbeddedApkConfig `protobuf:"bytes,1,rep,name=apex_embedded_apk_config,json=apexEmbeddedApkConfig,proto3" json:"apex_embedded_apk_config,omitempty"`
+	XXX_NoUnkeyedLiteral  struct{}                 `json:"-"`
+	XXX_unrecognized      []byte                   `json:"-"`
+	XXX_sizecache         int32                    `json:"-"`
+}
+
+func (m *ApexApkMetadata) Reset()         { *m = ApexApkMetadata{} }
+func (m *ApexApkMetadata) String() string { return proto.CompactTextString(m) }
+func (*ApexApkMetadata) ProtoMessage()    {}
+func (*ApexApkMetadata) Descriptor() ([]byte, []int) {
+	return fileDescriptor_0dff099eb2e3dfdb, []int{11}
+}
+
+func (m *ApexApkMetadata) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_ApexApkMetadata.Unmarshal(m, b)
+}
+func (m *ApexApkMetadata) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_ApexApkMetadata.Marshal(b, m, deterministic)
+}
+func (m *ApexApkMetadata) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_ApexApkMetadata.Merge(m, src)
+}
+func (m *ApexApkMetadata) XXX_Size() int {
+	return xxx_messageInfo_ApexApkMetadata.Size(m)
+}
+func (m *ApexApkMetadata) XXX_DiscardUnknown() {
+	xxx_messageInfo_ApexApkMetadata.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ApexApkMetadata proto.InternalMessageInfo
+
+func (m *ApexApkMetadata) GetApexEmbeddedApkConfig() []*ApexEmbeddedApkConfig {
+	if m != nil {
+		return m.ApexEmbeddedApkConfig
+	}
+	return nil
+}
+
+type LocalTestingInfo struct {
+	// Indicates if the bundle is built in local testing mode.
+	Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
+	// The local testing path, as specified in the base manifest.
+	// This refers to the relative path on the external directory of the app where
+	// APKs will be pushed for local testing.
+	// Set only if local testing is enabled.
+	LocalTestingPath     string   `protobuf:"bytes,2,opt,name=local_testing_path,json=localTestingPath,proto3" json:"local_testing_path,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *LocalTestingInfo) Reset()         { *m = LocalTestingInfo{} }
+func (m *LocalTestingInfo) String() string { return proto.CompactTextString(m) }
+func (*LocalTestingInfo) ProtoMessage()    {}
+func (*LocalTestingInfo) Descriptor() ([]byte, []int) {
+	return fileDescriptor_0dff099eb2e3dfdb, []int{12}
+}
+
+func (m *LocalTestingInfo) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_LocalTestingInfo.Unmarshal(m, b)
+}
+func (m *LocalTestingInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_LocalTestingInfo.Marshal(b, m, deterministic)
+}
+func (m *LocalTestingInfo) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_LocalTestingInfo.Merge(m, src)
+}
+func (m *LocalTestingInfo) XXX_Size() int {
+	return xxx_messageInfo_LocalTestingInfo.Size(m)
+}
+func (m *LocalTestingInfo) XXX_DiscardUnknown() {
+	xxx_messageInfo_LocalTestingInfo.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_LocalTestingInfo proto.InternalMessageInfo
+
+func (m *LocalTestingInfo) GetEnabled() bool {
+	if m != nil {
+		return m.Enabled
+	}
+	return false
+}
+
+func (m *LocalTestingInfo) GetLocalTestingPath() string {
+	if m != nil {
+		return m.LocalTestingPath
+	}
+	return ""
+}
+
+func init() {
+	proto.RegisterEnum("android.bundle.DeliveryType", DeliveryType_name, DeliveryType_value)
+	proto.RegisterEnum("android.bundle.SystemApkMetadata_SystemApkType", SystemApkMetadata_SystemApkType_name, SystemApkMetadata_SystemApkType_value)
+	proto.RegisterType((*BuildApksResult)(nil), "android.bundle.BuildApksResult")
+	proto.RegisterType((*Variant)(nil), "android.bundle.Variant")
+	proto.RegisterType((*ApkSet)(nil), "android.bundle.ApkSet")
+	proto.RegisterType((*ModuleMetadata)(nil), "android.bundle.ModuleMetadata")
+	proto.RegisterType((*AssetSliceSet)(nil), "android.bundle.AssetSliceSet")
+	proto.RegisterType((*AssetModuleMetadata)(nil), "android.bundle.AssetModuleMetadata")
+	proto.RegisterType((*InstantMetadata)(nil), "android.bundle.InstantMetadata")
+	proto.RegisterType((*ApkDescription)(nil), "android.bundle.ApkDescription")
+	proto.RegisterType((*SplitApkMetadata)(nil), "android.bundle.SplitApkMetadata")
+	proto.RegisterType((*StandaloneApkMetadata)(nil), "android.bundle.StandaloneApkMetadata")
+	proto.RegisterType((*SystemApkMetadata)(nil), "android.bundle.SystemApkMetadata")
+	proto.RegisterType((*ApexApkMetadata)(nil), "android.bundle.ApexApkMetadata")
+	proto.RegisterType((*LocalTestingInfo)(nil), "android.bundle.LocalTestingInfo")
+}
+
+func init() {
+	proto.RegisterFile("commands.proto", fileDescriptor_0dff099eb2e3dfdb)
+}
+
+var fileDescriptor_0dff099eb2e3dfdb = []byte{
+	// 1104 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0xcf, 0x6f, 0xe2, 0x46,
+	0x14, 0x5e, 0x03, 0x0b, 0xe1, 0x05, 0xb0, 0x33, 0x1b, 0xba, 0xde, 0x68, 0x77, 0xcb, 0xba, 0x4a,
+	0x85, 0xa2, 0x2a, 0xab, 0xa6, 0x3d, 0xad, 0xd4, 0x4a, 0x10, 0x9c, 0x96, 0x2d, 0x90, 0xc8, 0x26,
+	0x89, 0x92, 0x4a, 0x1d, 0x4d, 0x98, 0x49, 0xd6, 0xc2, 0xbf, 0xca, 0x98, 0x28, 0xf9, 0x57, 0x7a,
+	0xa9, 0x7a, 0xec, 0xb1, 0xd7, 0xfe, 0x51, 0x3d, 0xf5, 0xde, 0xca, 0x63, 0x03, 0xb6, 0xb1, 0xd4,
+	0x64, 0xd5, 0x13, 0x7e, 0x6f, 0xbe, 0xf9, 0xe6, 0xbd, 0xf7, 0xbd, 0x79, 0x0c, 0x34, 0x26, 0x9e,
+	0xe3, 0x10, 0x97, 0xf2, 0x7d, 0x7f, 0xe6, 0x05, 0x1e, 0x6a, 0x10, 0x97, 0xce, 0x3c, 0x8b, 0xee,
+	0x5f, 0xcd, 0x5d, 0x6a, 0xb3, 0x9d, 0xda, 0xc4, 0x73, 0xaf, 0xad, 0x9b, 0x68, 0x75, 0x47, 0x0e,
+	0xc8, 0xec, 0x86, 0x05, 0x96, 0x1b, 0x3b, 0xb4, 0x3f, 0x0b, 0x20, 0x77, 0xe7, 0x96, 0x4d, 0x3b,
+	0xfe, 0x94, 0x1b, 0x8c, 0xcf, 0xed, 0x00, 0xbd, 0x81, 0x9a, 0x4f, 0x26, 0x53, 0x72, 0xc3, 0xb0,
+	0x4b, 0x1c, 0xa6, 0x96, 0x5a, 0x52, 0xbb, 0x6a, 0x6c, 0xc6, 0xbe, 0x11, 0x71, 0x18, 0xfa, 0x12,
+	0x2a, 0xb7, 0x64, 0x66, 0x11, 0x37, 0x50, 0xa5, 0x56, 0xb1, 0xbd, 0x79, 0xf0, 0x7c, 0x3f, 0x7d,
+	0xee, 0xfe, 0x59, 0xb4, 0x6c, 0x2c, 0x70, 0xe8, 0x1d, 0x40, 0xb4, 0x14, 0x78, 0x9e, 0xad, 0x16,
+	0x5a, 0x52, 0x7b, 0xf3, 0x60, 0x27, 0xbb, 0xab, 0xbb, 0x44, 0x18, 0x09, 0x34, 0xd2, 0x41, 0x26,
+	0x9c, 0xb3, 0x00, 0x73, 0xdb, 0x9a, 0x30, 0xcc, 0x59, 0xa0, 0x16, 0xc5, 0xb1, 0xaf, 0xb2, 0x04,
+	0x9d, 0x10, 0x66, 0x86, 0x28, 0x93, 0x05, 0x46, 0x9d, 0x24, 0x4d, 0x34, 0x02, 0x64, 0x7b, 0x13,
+	0x62, 0xe3, 0x80, 0xf1, 0xb0, 0x06, 0xd8, 0x72, 0xaf, 0x3d, 0xf5, 0xa9, 0x08, 0xa5, 0x95, 0x65,
+	0x1a, 0x84, 0xc8, 0x71, 0x04, 0xec, 0xbb, 0xd7, 0x9e, 0xa1, 0xd8, 0x19, 0x8f, 0xf6, 0x9b, 0x04,
+	0x95, 0x38, 0x4f, 0xf4, 0x2d, 0x54, 0x97, 0xb5, 0x55, 0xa5, 0x7c, 0xca, 0x18, 0x3b, 0x5e, 0xe0,
+	0x8c, 0xd5, 0x16, 0xf4, 0x16, 0x2a, 0xc4, 0x9f, 0x8a, 0xd4, 0x0a, 0x22, 0xb5, 0x4f, 0xd6, 0x52,
+	0xf3, 0xa7, 0x61, 0x4e, 0x65, 0x22, 0x7e, 0xd1, 0x2e, 0x34, 0xe2, 0xd2, 0x62, 0x77, 0xee, 0x5c,
+	0xb1, 0x99, 0x5a, 0x6c, 0x49, 0xed, 0xba, 0x51, 0x8f, 0xbd, 0x23, 0xe1, 0xd4, 0x7e, 0x91, 0xa0,
+	0x1c, 0xed, 0x44, 0xdf, 0x81, 0xec, 0x78, 0x74, 0x6e, 0x33, 0xec, 0xb0, 0x80, 0x50, 0x12, 0x90,
+	0x38, 0xd0, 0xd7, 0xd9, 0xa3, 0x86, 0x02, 0x36, 0x8c, 0x51, 0x46, 0xc3, 0x49, 0xd9, 0x21, 0x51,
+	0x18, 0x2b, 0x65, 0x7c, 0x32, 0xb3, 0xfc, 0xc0, 0xf2, 0xdc, 0x38, 0xe6, 0xd7, 0x39, 0x31, 0xf7,
+	0x56, 0x28, 0xa3, 0x41, 0x52, 0xb6, 0xf6, 0x6b, 0x01, 0x1a, 0xe9, 0xb3, 0x10, 0x82, 0x92, 0x68,
+	0x3a, 0x49, 0x34, 0x9d, 0xf8, 0x46, 0x1d, 0xa8, 0x53, 0x66, 0x5b, 0xb7, 0x6c, 0x76, 0x8f, 0x83,
+	0x7b, 0x9f, 0xa9, 0xe5, 0x96, 0xd4, 0x6e, 0x1c, 0xbc, 0xcc, 0x9e, 0xd6, 0x8b, 0x41, 0xe3, 0x7b,
+	0x9f, 0x19, 0x35, 0x9a, 0xb0, 0xd0, 0x2b, 0x00, 0x8b, 0x63, 0xcb, 0xe5, 0x41, 0xd8, 0xb3, 0x61,
+	0xa5, 0x36, 0x8c, 0xaa, 0xc5, 0xfb, 0x91, 0x03, 0x69, 0x50, 0xa3, 0xcc, 0x67, 0x2e, 0x65, 0xee,
+	0xc4, 0x62, 0x5c, 0x2d, 0xb5, 0x8a, 0xed, 0xaa, 0x91, 0xf2, 0xa1, 0x6f, 0x92, 0x0a, 0x47, 0x4d,
+	0xf3, 0x69, 0x7e, 0xe1, 0x72, 0x05, 0xfe, 0x1a, 0xb6, 0x3d, 0x17, 0x53, 0x16, 0x5e, 0x56, 0x4c,
+	0x99, 0x3f, 0x63, 0x13, 0x12, 0x30, 0x2a, 0x6e, 0xc2, 0x46, 0xb7, 0xa0, 0x4a, 0x06, 0xf2, 0xdc,
+	0x9e, 0x58, 0xee, 0x2d, 0x57, 0xb5, 0x3f, 0x24, 0xa8, 0xa7, 0x7a, 0x1a, 0x9d, 0x43, 0x33, 0xba,
+	0x0b, 0xf9, 0x5a, 0x7e, 0x96, 0x7b, 0x23, 0x32, 0x82, 0x3e, 0x23, 0xeb, 0xce, 0xff, 0x4f, 0xd5,
+	0xbf, 0x24, 0x78, 0x96, 0x73, 0xea, 0xc3, 0xa4, 0x2d, 0x3d, 0x5a, 0xda, 0xf7, 0xa0, 0xc4, 0xba,
+	0xae, 0x6a, 0x51, 0xcc, 0x97, 0x27, 0x96, 0x7b, 0x59, 0x07, 0xd9, 0x4a, 0x3b, 0x3e, 0x52, 0xa4,
+	0xdf, 0x25, 0x90, 0x33, 0xd4, 0x99, 0x86, 0x93, 0xb2, 0x0d, 0xb7, 0x96, 0x77, 0xf1, 0xd1, 0x79,
+	0x7f, 0x5c, 0xac, 0xff, 0x94, 0xa0, 0x91, 0xd6, 0x0f, 0xbd, 0x5b, 0x1f, 0x5d, 0x2f, 0x73, 0x24,
+	0xcf, 0xed, 0x6a, 0x04, 0x25, 0x9f, 0x04, 0x1f, 0xc4, 0xa1, 0x55, 0x43, 0x7c, 0xa3, 0x13, 0x40,
+	0xdc, 0xb7, 0xad, 0x00, 0x87, 0xed, 0x94, 0x91, 0x64, 0x6d, 0x26, 0x9a, 0x21, 0xb2, 0xe3, 0x4f,
+	0x17, 0x85, 0xfb, 0xfe, 0x89, 0xa1, 0xf0, 0x8c, 0x0f, 0x61, 0x78, 0x1e, 0x96, 0x8d, 0x12, 0xdb,
+	0x73, 0x59, 0x9a, 0xb6, 0x24, 0x68, 0x77, 0xd7, 0x68, 0x97, 0xf0, 0x34, 0x77, 0x93, 0xe7, 0x2d,
+	0xa0, 0x31, 0x6c, 0x2f, 0x7a, 0x28, 0xc5, 0xfe, 0xf4, 0xc1, 0x41, 0xa3, 0x78, 0x7f, 0x92, 0xd5,
+	0x84, 0x67, 0xfc, 0x9e, 0x07, 0xcc, 0x49, 0x93, 0x96, 0x05, 0xe9, 0x9b, 0x35, 0x52, 0x01, 0x4d,
+	0xb3, 0x6e, 0xf1, 0xac, 0x33, 0x0c, 0x35, 0xf9, 0x5f, 0xb8, 0x64, 0xad, 0x3c, 0x3c, 0xd4, 0xd5,
+	0xbf, 0xe2, 0x92, 0x75, 0x08, 0x5b, 0xc4, 0x67, 0x77, 0xe9, 0x40, 0x37, 0xf2, 0x6f, 0x51, 0xc7,
+	0x67, 0x77, 0x69, 0x46, 0x99, 0xa4, 0x5d, 0xdd, 0x1d, 0x50, 0x93, 0x4c, 0xd8, 0x73, 0x99, 0x77,
+	0x8d, 0x6f, 0x89, 0x3d, 0x67, 0xda, 0x29, 0x28, 0xd9, 0xa0, 0xd0, 0x0b, 0xd8, 0x88, 0x5a, 0xc6,
+	0xa2, 0xf1, 0x78, 0xa8, 0x08, 0xbb, 0x4f, 0xd1, 0xe7, 0x20, 0x5b, 0x1c, 0x3b, 0x84, 0x07, 0x6c,
+	0x86, 0x85, 0x33, 0xea, 0x70, 0xa3, 0x6e, 0xf1, 0xa1, 0xf0, 0x0a, 0x36, 0xad, 0x0f, 0xcd, 0x5c,
+	0xd1, 0xd1, 0x1e, 0x6c, 0x5d, 0xcf, 0x39, 0xa3, 0x8b, 0x81, 0x19, 0xcf, 0xa0, 0x70, 0xc0, 0xcb,
+	0x62, 0x21, 0x1a, 0x53, 0xe1, 0xbb, 0xe6, 0x7d, 0x69, 0xa3, 0xa0, 0x14, 0xb5, 0xbf, 0x25, 0xd8,
+	0x5a, 0x53, 0xe3, 0x31, 0x3c, 0xe8, 0x1c, 0xe4, 0x84, 0xf2, 0xe2, 0x82, 0x17, 0xc4, 0x05, 0x7f,
+	0xfb, 0x9f, 0xaa, 0xaf, 0x3c, 0xe2, 0xce, 0xd7, 0x79, 0xd2, 0xd4, 0x2e, 0xa1, 0x9e, 0x5a, 0x47,
+	0x4d, 0xd8, 0x3a, 0x1d, 0x99, 0x27, 0xfa, 0x61, 0xff, 0xa8, 0xaf, 0xf7, 0xf0, 0x59, 0x67, 0x70,
+	0xaa, 0x2b, 0x4f, 0x10, 0x40, 0xd9, 0xbc, 0x30, 0xc7, 0xfa, 0x50, 0x91, 0x90, 0x0c, 0x9b, 0xd1,
+	0x37, 0x36, 0xc7, 0xa7, 0x5d, 0xa5, 0x10, 0xee, 0x89, 0x1d, 0x87, 0xc7, 0xc3, 0x13, 0x43, 0x37,
+	0x4d, 0xbd, 0xa7, 0x14, 0xb5, 0x9f, 0x41, 0xce, 0x48, 0x8b, 0x7e, 0x0a, 0x75, 0x64, 0x77, 0x98,
+	0x39, 0x57, 0x8c, 0x52, 0x46, 0x45, 0x3a, 0xd1, 0x8b, 0x32, 0x7e, 0xf8, 0xed, 0xe6, 0x75, 0x87,
+	0x1e, 0xc3, 0x3b, 0xfe, 0xf4, 0x50, 0x80, 0x8d, 0x26, 0xc9, 0x73, 0x6b, 0x97, 0xa0, 0x64, 0xdf,
+	0x59, 0x48, 0x85, 0x0a, 0x73, 0xc9, 0x95, 0xcd, 0x68, 0x3c, 0x36, 0x17, 0x26, 0xfa, 0x22, 0xfb,
+	0x7e, 0x4b, 0x8c, 0x9e, 0xd4, 0xeb, 0xec, 0x84, 0x04, 0x1f, 0xf6, 0x7e, 0x84, 0x5a, 0x72, 0x7a,
+	0xa2, 0x17, 0xd0, 0x3c, 0x1d, 0xfd, 0x30, 0x3a, 0x3e, 0x1f, 0xe1, 0x9e, 0x3e, 0xe8, 0x9f, 0xe9,
+	0xc6, 0x05, 0x1e, 0x5f, 0x9c, 0x84, 0xd5, 0x52, 0xa0, 0xd6, 0x1f, 0x99, 0xe3, 0xce, 0x60, 0x80,
+	0xc7, 0xfd, 0xa1, 0xae, 0x48, 0xa8, 0x0e, 0xd5, 0xe3, 0x10, 0x37, 0xec, 0x8c, 0x7a, 0x4a, 0x21,
+	0x2c, 0xe1, 0x51, 0xc7, 0x1c, 0xe3, 0xa3, 0xe3, 0xc1, 0xe0, 0xf8, 0x5c, 0x29, 0x76, 0xf7, 0x00,
+	0x4d, 0x3c, 0x27, 0x93, 0xfb, 0xe5, 0x76, 0x6c, 0xe3, 0xc8, 0xc6, 0xe2, 0x8d, 0x7d, 0x55, 0x16,
+	0x3f, 0x5f, 0xfd, 0x1b, 0x00, 0x00, 0xff, 0xff, 0xb1, 0xe5, 0xcb, 0x87, 0xab, 0x0b, 0x00, 0x00,
+}
diff --git a/cmd/extract_apks/bundle_proto/commands.proto b/cmd/extract_apks/bundle_proto/commands.proto
new file mode 100644
index 0000000..b36340b
--- /dev/null
+++ b/cmd/extract_apks/bundle_proto/commands.proto
@@ -0,0 +1,197 @@
+// Messages describing APK Set's table of contents (toc.pb entry).
+// Please be advised that the ultimate source is at
+// https://github.com/google/bundletool/tree/master/src/main/proto
+// so you have been warned.
+syntax = "proto3";
+
+package android.bundle;
+
+import "config.proto";
+import "targeting.proto";
+
+option go_package = "android_bundle_proto";
+option java_package = "com.android.bundle";
+
+// Describes the output of the "build-apks" command.
+message BuildApksResult {
+  // The package name of this app.
+  string package_name = 4;
+
+  // List of the created variants.
+  repeated Variant variant = 1;
+
+  // Metadata about BundleTool used to build the APKs.
+  Bundletool bundletool = 2;
+
+  // List of the created asset slices.
+  repeated AssetSliceSet asset_slice_set = 3;
+
+  // Information about local testing mode.
+  LocalTestingInfo local_testing_info = 5;
+}
+
+// Variant is a group of APKs that covers a part of the device configuration
+// space. APKs from multiple variants are never combined on one device.
+message Variant {
+  // Variant-level targeting.
+  // This targeting is fairly high-level and each APK has its own targeting as
+  // well.
+  VariantTargeting targeting = 1;
+
+  // Set of APKs, one set per module.
+  repeated ApkSet apk_set = 2;
+
+  // Number of the variant, starting at 0 (unless overridden).
+  // A device will receive APKs from the first variant that matches the device
+  // configuration, with higher variant numbers having priority over lower
+  // variant numbers.
+  uint32 variant_number = 3;
+}
+
+// Represents a module.
+// For pre-L devices multiple modules (possibly all) may be merged into one.
+message ApkSet {
+  ModuleMetadata module_metadata = 1;
+
+  // APKs.
+  repeated ApkDescription apk_description = 2;
+}
+
+message ModuleMetadata {
+  // Module name.
+  string name = 1;
+
+  // Indicates the delivery type (e.g. on-demand) of the module.
+  DeliveryType delivery_type = 6;
+
+  // Indicates whether this module is marked "instant".
+  bool is_instant = 3;
+
+  // Names of the modules that this module directly depends on.
+  // Each module implicitly depends on the base module.
+  repeated string dependencies = 4;
+
+  // The targeting that makes a conditional module installed.
+  // Relevant only for Split APKs.
+  ModuleTargeting targeting = 5;
+
+  // Deprecated. Please use delivery_type.
+  bool on_demand_deprecated = 2 [deprecated = true];
+}
+
+// Set of asset slices belonging to a single asset module.
+message AssetSliceSet {
+  // Module level metadata.
+  AssetModuleMetadata asset_module_metadata = 1;
+
+  // Asset slices.
+  repeated ApkDescription apk_description = 2;
+}
+
+message AssetModuleMetadata {
+  // Module name.
+  string name = 1;
+
+  // Indicates the delivery type for persistent install.
+  DeliveryType delivery_type = 4;
+
+  // Metadata for instant installs.
+  InstantMetadata instant_metadata = 3;
+
+  // Deprecated. Use delivery_type.
+  bool on_demand_deprecated = 2 [deprecated = true];
+}
+
+message InstantMetadata {
+  // Indicates whether this module is marked "instant".
+  bool is_instant = 1;
+
+  // Indicates the delivery type for instant install.
+  DeliveryType delivery_type = 3;
+
+  // Deprecated. Use delivery_type.
+  bool on_demand_deprecated = 2 [deprecated = true];
+}
+
+enum DeliveryType {
+  UNKNOWN_DELIVERY_TYPE = 0;
+  INSTALL_TIME = 1;
+  ON_DEMAND = 2;
+  FAST_FOLLOW = 3;
+}
+
+message ApkDescription {
+  ApkTargeting targeting = 1;
+
+  // Path to the APK file.
+  // BEGIN-INTERNAL
+  // The path may be a blobkey if the proto is not constructed by bundletool.
+  // END-INTERNAL
+  string path = 2;
+
+  oneof apk_metadata_oneof_value {
+    // Set only for Split APKs.
+    SplitApkMetadata split_apk_metadata = 3;
+    // Set only for standalone APKs.
+    StandaloneApkMetadata standalone_apk_metadata = 4;
+    // Set only for Instant split APKs.
+    SplitApkMetadata instant_apk_metadata = 5;
+    // Set only for system APKs.
+    SystemApkMetadata system_apk_metadata = 6;
+    // Set only for asset slices.
+    SplitApkMetadata asset_slice_metadata = 7;
+    // Set only for APEX APKs.
+    ApexApkMetadata apex_apk_metadata = 8;
+  }
+}
+
+// Holds data specific to Split APKs.
+message SplitApkMetadata {
+  string split_id = 1;
+
+  // Indicates whether this APK is the master split of the module.
+  bool is_master_split = 2;
+}
+
+// Holds data specific to Standalone APKs.
+message StandaloneApkMetadata {
+  // Names of the modules fused in this standalone APK.
+  repeated string fused_module_name = 1;
+
+  reserved 2;
+}
+
+// Holds data specific to system APKs.
+message SystemApkMetadata {
+  // Names of the modules fused in this system APK.
+  repeated string fused_module_name = 1;
+  enum SystemApkType {
+    UNSPECIFIED_VALUE = 0;
+    // Uncompressed APK for system image.
+    SYSTEM = 1;
+    // Stub APK for compressed APK in the system image
+    // (contains only android manifest).
+    SYSTEM_STUB = 2;
+    // Compressed APK for system image.
+    SYSTEM_COMPRESSED = 3;
+  }
+  // Indicates whether the APK is uncompressed system APK, stub APK or
+  // compressed system APK.
+  SystemApkType system_apk_type = 2;
+}
+
+// Holds data specific to APEX APKs.
+message ApexApkMetadata {
+  // Configuration for processing of APKs embedded in an APEX image.
+  repeated ApexEmbeddedApkConfig apex_embedded_apk_config = 1;
+}
+
+message LocalTestingInfo {
+  // Indicates if the bundle is built in local testing mode.
+  bool enabled = 1;
+  // The local testing path, as specified in the base manifest.
+  // This refers to the relative path on the external directory of the app where
+  // APKs will be pushed for local testing.
+  // Set only if local testing is enabled.
+  string local_testing_path = 2;
+}
diff --git a/cmd/extract_apks/bundle_proto/config.pb.go b/cmd/extract_apks/bundle_proto/config.pb.go
new file mode 100644
index 0000000..a28147a
--- /dev/null
+++ b/cmd/extract_apks/bundle_proto/config.pb.go
@@ -0,0 +1,952 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: config.proto
+
+package android_bundle_proto
+
+import (
+	fmt "fmt"
+	proto "github.com/golang/protobuf/proto"
+	math "math"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+
+type BundleConfig_BundleType int32
+
+const (
+	BundleConfig_REGULAR    BundleConfig_BundleType = 0
+	BundleConfig_APEX       BundleConfig_BundleType = 1
+	BundleConfig_ASSET_ONLY BundleConfig_BundleType = 2
+)
+
+var BundleConfig_BundleType_name = map[int32]string{
+	0: "REGULAR",
+	1: "APEX",
+	2: "ASSET_ONLY",
+}
+
+var BundleConfig_BundleType_value = map[string]int32{
+	"REGULAR":    0,
+	"APEX":       1,
+	"ASSET_ONLY": 2,
+}
+
+func (x BundleConfig_BundleType) String() string {
+	return proto.EnumName(BundleConfig_BundleType_name, int32(x))
+}
+
+func (BundleConfig_BundleType) EnumDescriptor() ([]byte, []int) {
+	return fileDescriptor_3eaf2c85e69e9ea4, []int{0, 0}
+}
+
+type SplitDimension_Value int32
+
+const (
+	SplitDimension_UNSPECIFIED_VALUE          SplitDimension_Value = 0
+	SplitDimension_ABI                        SplitDimension_Value = 1
+	SplitDimension_SCREEN_DENSITY             SplitDimension_Value = 2
+	SplitDimension_LANGUAGE                   SplitDimension_Value = 3
+	SplitDimension_TEXTURE_COMPRESSION_FORMAT SplitDimension_Value = 4
+	// BEGIN-INTERNAL
+	SplitDimension_GRAPHICS_API SplitDimension_Value = 5
+)
+
+var SplitDimension_Value_name = map[int32]string{
+	0: "UNSPECIFIED_VALUE",
+	1: "ABI",
+	2: "SCREEN_DENSITY",
+	3: "LANGUAGE",
+	4: "TEXTURE_COMPRESSION_FORMAT",
+	5: "GRAPHICS_API",
+}
+
+var SplitDimension_Value_value = map[string]int32{
+	"UNSPECIFIED_VALUE":          0,
+	"ABI":                        1,
+	"SCREEN_DENSITY":             2,
+	"LANGUAGE":                   3,
+	"TEXTURE_COMPRESSION_FORMAT": 4,
+	"GRAPHICS_API":               5,
+}
+
+func (x SplitDimension_Value) String() string {
+	return proto.EnumName(SplitDimension_Value_name, int32(x))
+}
+
+func (SplitDimension_Value) EnumDescriptor() ([]byte, []int) {
+	return fileDescriptor_3eaf2c85e69e9ea4, []int{9, 0}
+}
+
+type BundleConfig struct {
+	Bundletool    *Bundletool    `protobuf:"bytes,1,opt,name=bundletool,proto3" json:"bundletool,omitempty"`
+	Optimizations *Optimizations `protobuf:"bytes,2,opt,name=optimizations,proto3" json:"optimizations,omitempty"`
+	Compression   *Compression   `protobuf:"bytes,3,opt,name=compression,proto3" json:"compression,omitempty"`
+	// Resources to be always kept in the master split.
+	MasterResources *MasterResources `protobuf:"bytes,4,opt,name=master_resources,json=masterResources,proto3" json:"master_resources,omitempty"`
+	ApexConfig      *ApexConfig      `protobuf:"bytes,5,opt,name=apex_config,json=apexConfig,proto3" json:"apex_config,omitempty"`
+	// APKs to be signed with the same key as generated APKs.
+	UnsignedEmbeddedApkConfig []*UnsignedEmbeddedApkConfig `protobuf:"bytes,6,rep,name=unsigned_embedded_apk_config,json=unsignedEmbeddedApkConfig,proto3" json:"unsigned_embedded_apk_config,omitempty"`
+	AssetModulesConfig        *AssetModulesConfig          `protobuf:"bytes,7,opt,name=asset_modules_config,json=assetModulesConfig,proto3" json:"asset_modules_config,omitempty"`
+	Type                      BundleConfig_BundleType      `protobuf:"varint,8,opt,name=type,proto3,enum=android.bundle.BundleConfig_BundleType" json:"type,omitempty"`
+	XXX_NoUnkeyedLiteral      struct{}                     `json:"-"`
+	XXX_unrecognized          []byte                       `json:"-"`
+	XXX_sizecache             int32                        `json:"-"`
+}
+
+func (m *BundleConfig) Reset()         { *m = BundleConfig{} }
+func (m *BundleConfig) String() string { return proto.CompactTextString(m) }
+func (*BundleConfig) ProtoMessage()    {}
+func (*BundleConfig) Descriptor() ([]byte, []int) {
+	return fileDescriptor_3eaf2c85e69e9ea4, []int{0}
+}
+
+func (m *BundleConfig) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_BundleConfig.Unmarshal(m, b)
+}
+func (m *BundleConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_BundleConfig.Marshal(b, m, deterministic)
+}
+func (m *BundleConfig) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_BundleConfig.Merge(m, src)
+}
+func (m *BundleConfig) XXX_Size() int {
+	return xxx_messageInfo_BundleConfig.Size(m)
+}
+func (m *BundleConfig) XXX_DiscardUnknown() {
+	xxx_messageInfo_BundleConfig.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_BundleConfig proto.InternalMessageInfo
+
+func (m *BundleConfig) GetBundletool() *Bundletool {
+	if m != nil {
+		return m.Bundletool
+	}
+	return nil
+}
+
+func (m *BundleConfig) GetOptimizations() *Optimizations {
+	if m != nil {
+		return m.Optimizations
+	}
+	return nil
+}
+
+func (m *BundleConfig) GetCompression() *Compression {
+	if m != nil {
+		return m.Compression
+	}
+	return nil
+}
+
+func (m *BundleConfig) GetMasterResources() *MasterResources {
+	if m != nil {
+		return m.MasterResources
+	}
+	return nil
+}
+
+func (m *BundleConfig) GetApexConfig() *ApexConfig {
+	if m != nil {
+		return m.ApexConfig
+	}
+	return nil
+}
+
+func (m *BundleConfig) GetUnsignedEmbeddedApkConfig() []*UnsignedEmbeddedApkConfig {
+	if m != nil {
+		return m.UnsignedEmbeddedApkConfig
+	}
+	return nil
+}
+
+func (m *BundleConfig) GetAssetModulesConfig() *AssetModulesConfig {
+	if m != nil {
+		return m.AssetModulesConfig
+	}
+	return nil
+}
+
+func (m *BundleConfig) GetType() BundleConfig_BundleType {
+	if m != nil {
+		return m.Type
+	}
+	return BundleConfig_REGULAR
+}
+
+type Bundletool struct {
+	// Version of BundleTool used to build the Bundle.
+	Version              string   `protobuf:"bytes,2,opt,name=version,proto3" json:"version,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *Bundletool) Reset()         { *m = Bundletool{} }
+func (m *Bundletool) String() string { return proto.CompactTextString(m) }
+func (*Bundletool) ProtoMessage()    {}
+func (*Bundletool) Descriptor() ([]byte, []int) {
+	return fileDescriptor_3eaf2c85e69e9ea4, []int{1}
+}
+
+func (m *Bundletool) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_Bundletool.Unmarshal(m, b)
+}
+func (m *Bundletool) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_Bundletool.Marshal(b, m, deterministic)
+}
+func (m *Bundletool) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Bundletool.Merge(m, src)
+}
+func (m *Bundletool) XXX_Size() int {
+	return xxx_messageInfo_Bundletool.Size(m)
+}
+func (m *Bundletool) XXX_DiscardUnknown() {
+	xxx_messageInfo_Bundletool.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Bundletool proto.InternalMessageInfo
+
+func (m *Bundletool) GetVersion() string {
+	if m != nil {
+		return m.Version
+	}
+	return ""
+}
+
+type Compression struct {
+	// Glob matching the list of files to leave uncompressed in the APKs.
+	// The matching is done against the path of files in the APK, thus excluding
+	// the name of the modules, and using forward slash ("/") as a name separator.
+	// Examples: "res/raw/**", "assets/**/*.uncompressed", etc.
+	UncompressedGlob     []string `protobuf:"bytes,1,rep,name=uncompressed_glob,json=uncompressedGlob,proto3" json:"uncompressed_glob,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *Compression) Reset()         { *m = Compression{} }
+func (m *Compression) String() string { return proto.CompactTextString(m) }
+func (*Compression) ProtoMessage()    {}
+func (*Compression) Descriptor() ([]byte, []int) {
+	return fileDescriptor_3eaf2c85e69e9ea4, []int{2}
+}
+
+func (m *Compression) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_Compression.Unmarshal(m, b)
+}
+func (m *Compression) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_Compression.Marshal(b, m, deterministic)
+}
+func (m *Compression) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Compression.Merge(m, src)
+}
+func (m *Compression) XXX_Size() int {
+	return xxx_messageInfo_Compression.Size(m)
+}
+func (m *Compression) XXX_DiscardUnknown() {
+	xxx_messageInfo_Compression.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Compression proto.InternalMessageInfo
+
+func (m *Compression) GetUncompressedGlob() []string {
+	if m != nil {
+		return m.UncompressedGlob
+	}
+	return nil
+}
+
+// Resources to keep in the master split.
+type MasterResources struct {
+	// Resource IDs to be kept in master split.
+	ResourceIds []int32 `protobuf:"varint,1,rep,packed,name=resource_ids,json=resourceIds,proto3" json:"resource_ids,omitempty"`
+	// Resource names to be kept in master split.
+	ResourceNames        []string `protobuf:"bytes,2,rep,name=resource_names,json=resourceNames,proto3" json:"resource_names,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *MasterResources) Reset()         { *m = MasterResources{} }
+func (m *MasterResources) String() string { return proto.CompactTextString(m) }
+func (*MasterResources) ProtoMessage()    {}
+func (*MasterResources) Descriptor() ([]byte, []int) {
+	return fileDescriptor_3eaf2c85e69e9ea4, []int{3}
+}
+
+func (m *MasterResources) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_MasterResources.Unmarshal(m, b)
+}
+func (m *MasterResources) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_MasterResources.Marshal(b, m, deterministic)
+}
+func (m *MasterResources) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MasterResources.Merge(m, src)
+}
+func (m *MasterResources) XXX_Size() int {
+	return xxx_messageInfo_MasterResources.Size(m)
+}
+func (m *MasterResources) XXX_DiscardUnknown() {
+	xxx_messageInfo_MasterResources.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MasterResources proto.InternalMessageInfo
+
+func (m *MasterResources) GetResourceIds() []int32 {
+	if m != nil {
+		return m.ResourceIds
+	}
+	return nil
+}
+
+func (m *MasterResources) GetResourceNames() []string {
+	if m != nil {
+		return m.ResourceNames
+	}
+	return nil
+}
+
+type Optimizations struct {
+	SplitsConfig *SplitsConfig `protobuf:"bytes,1,opt,name=splits_config,json=splitsConfig,proto3" json:"splits_config,omitempty"`
+	// This is for uncompressing native libraries on M+ devices (L+ devices on
+	// instant apps).
+	UncompressNativeLibraries *UncompressNativeLibraries `protobuf:"bytes,2,opt,name=uncompress_native_libraries,json=uncompressNativeLibraries,proto3" json:"uncompress_native_libraries,omitempty"`
+	// This is for uncompressing dex files on P+ devices.
+	UncompressDexFiles *UncompressDexFiles `protobuf:"bytes,3,opt,name=uncompress_dex_files,json=uncompressDexFiles,proto3" json:"uncompress_dex_files,omitempty"`
+	// Configuration for the generation of standalone APKs.
+	// If no StandaloneConfig is set, the configuration is inherited from
+	// splits_config.
+	StandaloneConfig     *StandaloneConfig `protobuf:"bytes,4,opt,name=standalone_config,json=standaloneConfig,proto3" json:"standalone_config,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}          `json:"-"`
+	XXX_unrecognized     []byte            `json:"-"`
+	XXX_sizecache        int32             `json:"-"`
+}
+
+func (m *Optimizations) Reset()         { *m = Optimizations{} }
+func (m *Optimizations) String() string { return proto.CompactTextString(m) }
+func (*Optimizations) ProtoMessage()    {}
+func (*Optimizations) Descriptor() ([]byte, []int) {
+	return fileDescriptor_3eaf2c85e69e9ea4, []int{4}
+}
+
+func (m *Optimizations) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_Optimizations.Unmarshal(m, b)
+}
+func (m *Optimizations) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_Optimizations.Marshal(b, m, deterministic)
+}
+func (m *Optimizations) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Optimizations.Merge(m, src)
+}
+func (m *Optimizations) XXX_Size() int {
+	return xxx_messageInfo_Optimizations.Size(m)
+}
+func (m *Optimizations) XXX_DiscardUnknown() {
+	xxx_messageInfo_Optimizations.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Optimizations proto.InternalMessageInfo
+
+func (m *Optimizations) GetSplitsConfig() *SplitsConfig {
+	if m != nil {
+		return m.SplitsConfig
+	}
+	return nil
+}
+
+func (m *Optimizations) GetUncompressNativeLibraries() *UncompressNativeLibraries {
+	if m != nil {
+		return m.UncompressNativeLibraries
+	}
+	return nil
+}
+
+func (m *Optimizations) GetUncompressDexFiles() *UncompressDexFiles {
+	if m != nil {
+		return m.UncompressDexFiles
+	}
+	return nil
+}
+
+func (m *Optimizations) GetStandaloneConfig() *StandaloneConfig {
+	if m != nil {
+		return m.StandaloneConfig
+	}
+	return nil
+}
+
+type UncompressNativeLibraries struct {
+	Enabled              bool     `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *UncompressNativeLibraries) Reset()         { *m = UncompressNativeLibraries{} }
+func (m *UncompressNativeLibraries) String() string { return proto.CompactTextString(m) }
+func (*UncompressNativeLibraries) ProtoMessage()    {}
+func (*UncompressNativeLibraries) Descriptor() ([]byte, []int) {
+	return fileDescriptor_3eaf2c85e69e9ea4, []int{5}
+}
+
+func (m *UncompressNativeLibraries) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_UncompressNativeLibraries.Unmarshal(m, b)
+}
+func (m *UncompressNativeLibraries) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_UncompressNativeLibraries.Marshal(b, m, deterministic)
+}
+func (m *UncompressNativeLibraries) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_UncompressNativeLibraries.Merge(m, src)
+}
+func (m *UncompressNativeLibraries) XXX_Size() int {
+	return xxx_messageInfo_UncompressNativeLibraries.Size(m)
+}
+func (m *UncompressNativeLibraries) XXX_DiscardUnknown() {
+	xxx_messageInfo_UncompressNativeLibraries.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_UncompressNativeLibraries proto.InternalMessageInfo
+
+func (m *UncompressNativeLibraries) GetEnabled() bool {
+	if m != nil {
+		return m.Enabled
+	}
+	return false
+}
+
+type UncompressDexFiles struct {
+	Enabled              bool     `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *UncompressDexFiles) Reset()         { *m = UncompressDexFiles{} }
+func (m *UncompressDexFiles) String() string { return proto.CompactTextString(m) }
+func (*UncompressDexFiles) ProtoMessage()    {}
+func (*UncompressDexFiles) Descriptor() ([]byte, []int) {
+	return fileDescriptor_3eaf2c85e69e9ea4, []int{6}
+}
+
+func (m *UncompressDexFiles) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_UncompressDexFiles.Unmarshal(m, b)
+}
+func (m *UncompressDexFiles) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_UncompressDexFiles.Marshal(b, m, deterministic)
+}
+func (m *UncompressDexFiles) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_UncompressDexFiles.Merge(m, src)
+}
+func (m *UncompressDexFiles) XXX_Size() int {
+	return xxx_messageInfo_UncompressDexFiles.Size(m)
+}
+func (m *UncompressDexFiles) XXX_DiscardUnknown() {
+	xxx_messageInfo_UncompressDexFiles.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_UncompressDexFiles proto.InternalMessageInfo
+
+func (m *UncompressDexFiles) GetEnabled() bool {
+	if m != nil {
+		return m.Enabled
+	}
+	return false
+}
+
+// Optimization configuration used to generate Split APKs.
+type SplitsConfig struct {
+	SplitDimension       []*SplitDimension `protobuf:"bytes,1,rep,name=split_dimension,json=splitDimension,proto3" json:"split_dimension,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}          `json:"-"`
+	XXX_unrecognized     []byte            `json:"-"`
+	XXX_sizecache        int32             `json:"-"`
+}
+
+func (m *SplitsConfig) Reset()         { *m = SplitsConfig{} }
+func (m *SplitsConfig) String() string { return proto.CompactTextString(m) }
+func (*SplitsConfig) ProtoMessage()    {}
+func (*SplitsConfig) Descriptor() ([]byte, []int) {
+	return fileDescriptor_3eaf2c85e69e9ea4, []int{7}
+}
+
+func (m *SplitsConfig) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_SplitsConfig.Unmarshal(m, b)
+}
+func (m *SplitsConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_SplitsConfig.Marshal(b, m, deterministic)
+}
+func (m *SplitsConfig) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_SplitsConfig.Merge(m, src)
+}
+func (m *SplitsConfig) XXX_Size() int {
+	return xxx_messageInfo_SplitsConfig.Size(m)
+}
+func (m *SplitsConfig) XXX_DiscardUnknown() {
+	xxx_messageInfo_SplitsConfig.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_SplitsConfig proto.InternalMessageInfo
+
+func (m *SplitsConfig) GetSplitDimension() []*SplitDimension {
+	if m != nil {
+		return m.SplitDimension
+	}
+	return nil
+}
+
+// Optimization configuration used to generate Standalone APKs.
+type StandaloneConfig struct {
+	// Device targeting dimensions to shard.
+	SplitDimension []*SplitDimension `protobuf:"bytes,1,rep,name=split_dimension,json=splitDimension,proto3" json:"split_dimension,omitempty"`
+	// Whether 64 bit libraries should be stripped from Standalone APKs.
+	Strip_64BitLibraries bool     `protobuf:"varint,2,opt,name=strip_64_bit_libraries,json=strip64BitLibraries,proto3" json:"strip_64_bit_libraries,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *StandaloneConfig) Reset()         { *m = StandaloneConfig{} }
+func (m *StandaloneConfig) String() string { return proto.CompactTextString(m) }
+func (*StandaloneConfig) ProtoMessage()    {}
+func (*StandaloneConfig) Descriptor() ([]byte, []int) {
+	return fileDescriptor_3eaf2c85e69e9ea4, []int{8}
+}
+
+func (m *StandaloneConfig) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_StandaloneConfig.Unmarshal(m, b)
+}
+func (m *StandaloneConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_StandaloneConfig.Marshal(b, m, deterministic)
+}
+func (m *StandaloneConfig) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_StandaloneConfig.Merge(m, src)
+}
+func (m *StandaloneConfig) XXX_Size() int {
+	return xxx_messageInfo_StandaloneConfig.Size(m)
+}
+func (m *StandaloneConfig) XXX_DiscardUnknown() {
+	xxx_messageInfo_StandaloneConfig.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_StandaloneConfig proto.InternalMessageInfo
+
+func (m *StandaloneConfig) GetSplitDimension() []*SplitDimension {
+	if m != nil {
+		return m.SplitDimension
+	}
+	return nil
+}
+
+func (m *StandaloneConfig) GetStrip_64BitLibraries() bool {
+	if m != nil {
+		return m.Strip_64BitLibraries
+	}
+	return false
+}
+
+type SplitDimension struct {
+	Value SplitDimension_Value `protobuf:"varint,1,opt,name=value,proto3,enum=android.bundle.SplitDimension_Value" json:"value,omitempty"`
+	// If set to 'true', indicates that APKs should *not* be split by this
+	// dimension.
+	Negate bool `protobuf:"varint,2,opt,name=negate,proto3" json:"negate,omitempty"`
+	// Optional transformation to be applied to asset directories where
+	// the targeting is encoded in the directory name (e.g: assets/foo#tcf_etc1)
+	SuffixStripping      *SuffixStripping `protobuf:"bytes,3,opt,name=suffix_stripping,json=suffixStripping,proto3" json:"suffix_stripping,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}         `json:"-"`
+	XXX_unrecognized     []byte           `json:"-"`
+	XXX_sizecache        int32            `json:"-"`
+}
+
+func (m *SplitDimension) Reset()         { *m = SplitDimension{} }
+func (m *SplitDimension) String() string { return proto.CompactTextString(m) }
+func (*SplitDimension) ProtoMessage()    {}
+func (*SplitDimension) Descriptor() ([]byte, []int) {
+	return fileDescriptor_3eaf2c85e69e9ea4, []int{9}
+}
+
+func (m *SplitDimension) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_SplitDimension.Unmarshal(m, b)
+}
+func (m *SplitDimension) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_SplitDimension.Marshal(b, m, deterministic)
+}
+func (m *SplitDimension) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_SplitDimension.Merge(m, src)
+}
+func (m *SplitDimension) XXX_Size() int {
+	return xxx_messageInfo_SplitDimension.Size(m)
+}
+func (m *SplitDimension) XXX_DiscardUnknown() {
+	xxx_messageInfo_SplitDimension.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_SplitDimension proto.InternalMessageInfo
+
+func (m *SplitDimension) GetValue() SplitDimension_Value {
+	if m != nil {
+		return m.Value
+	}
+	return SplitDimension_UNSPECIFIED_VALUE
+}
+
+func (m *SplitDimension) GetNegate() bool {
+	if m != nil {
+		return m.Negate
+	}
+	return false
+}
+
+func (m *SplitDimension) GetSuffixStripping() *SuffixStripping {
+	if m != nil {
+		return m.SuffixStripping
+	}
+	return nil
+}
+
+type SuffixStripping struct {
+	// If set to 'true', indicates that the targeting suffix should be removed
+	// from assets paths for this dimension when splits (or asset slices) are
+	// generated.
+	// This only applies to assets.
+	// For example a folder with path "assets/level1_textures#tcf_etc1"
+	// would be outputted to "assets/level1_textures". File contents are
+	// unchanged.
+	Enabled bool `protobuf:"varint,1,opt,name=enabled,proto3" json:"enabled,omitempty"`
+	// The default suffix to be used for the cases where separate slices can't
+	// be generated for this dimension. In the case of standalone/universal APKs
+	// generation, stripping the suffix can lead to file name collisions. This
+	// default suffix defines the directories to retain. The others are
+	// discarded: standalone/universal APKs will contain only directories
+	// targeted at this value for the dimension.
+	//
+	// If not set or empty, the fallback directory in each directory group will be
+	// used (for example, if both "assets/level1_textures#tcf_etc1" and
+	// "assets/level1_textures" are present and the default suffix is empty,
+	// then only "assets/level1_textures" will be used).
+	DefaultSuffix        string   `protobuf:"bytes,2,opt,name=default_suffix,json=defaultSuffix,proto3" json:"default_suffix,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *SuffixStripping) Reset()         { *m = SuffixStripping{} }
+func (m *SuffixStripping) String() string { return proto.CompactTextString(m) }
+func (*SuffixStripping) ProtoMessage()    {}
+func (*SuffixStripping) Descriptor() ([]byte, []int) {
+	return fileDescriptor_3eaf2c85e69e9ea4, []int{10}
+}
+
+func (m *SuffixStripping) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_SuffixStripping.Unmarshal(m, b)
+}
+func (m *SuffixStripping) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_SuffixStripping.Marshal(b, m, deterministic)
+}
+func (m *SuffixStripping) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_SuffixStripping.Merge(m, src)
+}
+func (m *SuffixStripping) XXX_Size() int {
+	return xxx_messageInfo_SuffixStripping.Size(m)
+}
+func (m *SuffixStripping) XXX_DiscardUnknown() {
+	xxx_messageInfo_SuffixStripping.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_SuffixStripping proto.InternalMessageInfo
+
+func (m *SuffixStripping) GetEnabled() bool {
+	if m != nil {
+		return m.Enabled
+	}
+	return false
+}
+
+func (m *SuffixStripping) GetDefaultSuffix() string {
+	if m != nil {
+		return m.DefaultSuffix
+	}
+	return ""
+}
+
+// Configuration for processing APEX bundles.
+// https://source.android.com/devices/tech/ota/apex
+type ApexConfig struct {
+	// Configuration for processing of APKs embedded in an APEX image.
+	ApexEmbeddedApkConfig []*ApexEmbeddedApkConfig `protobuf:"bytes,1,rep,name=apex_embedded_apk_config,json=apexEmbeddedApkConfig,proto3" json:"apex_embedded_apk_config,omitempty"`
+	XXX_NoUnkeyedLiteral  struct{}                 `json:"-"`
+	XXX_unrecognized      []byte                   `json:"-"`
+	XXX_sizecache         int32                    `json:"-"`
+}
+
+func (m *ApexConfig) Reset()         { *m = ApexConfig{} }
+func (m *ApexConfig) String() string { return proto.CompactTextString(m) }
+func (*ApexConfig) ProtoMessage()    {}
+func (*ApexConfig) Descriptor() ([]byte, []int) {
+	return fileDescriptor_3eaf2c85e69e9ea4, []int{11}
+}
+
+func (m *ApexConfig) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_ApexConfig.Unmarshal(m, b)
+}
+func (m *ApexConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_ApexConfig.Marshal(b, m, deterministic)
+}
+func (m *ApexConfig) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_ApexConfig.Merge(m, src)
+}
+func (m *ApexConfig) XXX_Size() int {
+	return xxx_messageInfo_ApexConfig.Size(m)
+}
+func (m *ApexConfig) XXX_DiscardUnknown() {
+	xxx_messageInfo_ApexConfig.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ApexConfig proto.InternalMessageInfo
+
+func (m *ApexConfig) GetApexEmbeddedApkConfig() []*ApexEmbeddedApkConfig {
+	if m != nil {
+		return m.ApexEmbeddedApkConfig
+	}
+	return nil
+}
+
+type ApexEmbeddedApkConfig struct {
+	// Android package name of the APK.
+	PackageName string `protobuf:"bytes,1,opt,name=package_name,json=packageName,proto3" json:"package_name,omitempty"`
+	// Path to the APK within the APEX system image.
+	Path                 string   `protobuf:"bytes,2,opt,name=path,proto3" json:"path,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *ApexEmbeddedApkConfig) Reset()         { *m = ApexEmbeddedApkConfig{} }
+func (m *ApexEmbeddedApkConfig) String() string { return proto.CompactTextString(m) }
+func (*ApexEmbeddedApkConfig) ProtoMessage()    {}
+func (*ApexEmbeddedApkConfig) Descriptor() ([]byte, []int) {
+	return fileDescriptor_3eaf2c85e69e9ea4, []int{12}
+}
+
+func (m *ApexEmbeddedApkConfig) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_ApexEmbeddedApkConfig.Unmarshal(m, b)
+}
+func (m *ApexEmbeddedApkConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_ApexEmbeddedApkConfig.Marshal(b, m, deterministic)
+}
+func (m *ApexEmbeddedApkConfig) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_ApexEmbeddedApkConfig.Merge(m, src)
+}
+func (m *ApexEmbeddedApkConfig) XXX_Size() int {
+	return xxx_messageInfo_ApexEmbeddedApkConfig.Size(m)
+}
+func (m *ApexEmbeddedApkConfig) XXX_DiscardUnknown() {
+	xxx_messageInfo_ApexEmbeddedApkConfig.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ApexEmbeddedApkConfig proto.InternalMessageInfo
+
+func (m *ApexEmbeddedApkConfig) GetPackageName() string {
+	if m != nil {
+		return m.PackageName
+	}
+	return ""
+}
+
+func (m *ApexEmbeddedApkConfig) GetPath() string {
+	if m != nil {
+		return m.Path
+	}
+	return ""
+}
+
+type UnsignedEmbeddedApkConfig struct {
+	// Path to the APK inside the module (e.g. if the path inside the bundle
+	// is split/assets/example.apk, this will be assets/example.apk).
+	Path                 string   `protobuf:"bytes,1,opt,name=path,proto3" json:"path,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *UnsignedEmbeddedApkConfig) Reset()         { *m = UnsignedEmbeddedApkConfig{} }
+func (m *UnsignedEmbeddedApkConfig) String() string { return proto.CompactTextString(m) }
+func (*UnsignedEmbeddedApkConfig) ProtoMessage()    {}
+func (*UnsignedEmbeddedApkConfig) Descriptor() ([]byte, []int) {
+	return fileDescriptor_3eaf2c85e69e9ea4, []int{13}
+}
+
+func (m *UnsignedEmbeddedApkConfig) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_UnsignedEmbeddedApkConfig.Unmarshal(m, b)
+}
+func (m *UnsignedEmbeddedApkConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_UnsignedEmbeddedApkConfig.Marshal(b, m, deterministic)
+}
+func (m *UnsignedEmbeddedApkConfig) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_UnsignedEmbeddedApkConfig.Merge(m, src)
+}
+func (m *UnsignedEmbeddedApkConfig) XXX_Size() int {
+	return xxx_messageInfo_UnsignedEmbeddedApkConfig.Size(m)
+}
+func (m *UnsignedEmbeddedApkConfig) XXX_DiscardUnknown() {
+	xxx_messageInfo_UnsignedEmbeddedApkConfig.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_UnsignedEmbeddedApkConfig proto.InternalMessageInfo
+
+func (m *UnsignedEmbeddedApkConfig) GetPath() string {
+	if m != nil {
+		return m.Path
+	}
+	return ""
+}
+
+type AssetModulesConfig struct {
+	// App versionCodes that will be updated with these asset modules.
+	// Only relevant for asset-only bundles.
+	AppVersion []int64 `protobuf:"varint,1,rep,packed,name=app_version,json=appVersion,proto3" json:"app_version,omitempty"`
+	// Version tag for the asset upload.
+	// Only relevant for asset-only bundles.
+	AssetVersionTag      string   `protobuf:"bytes,2,opt,name=asset_version_tag,json=assetVersionTag,proto3" json:"asset_version_tag,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *AssetModulesConfig) Reset()         { *m = AssetModulesConfig{} }
+func (m *AssetModulesConfig) String() string { return proto.CompactTextString(m) }
+func (*AssetModulesConfig) ProtoMessage()    {}
+func (*AssetModulesConfig) Descriptor() ([]byte, []int) {
+	return fileDescriptor_3eaf2c85e69e9ea4, []int{14}
+}
+
+func (m *AssetModulesConfig) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_AssetModulesConfig.Unmarshal(m, b)
+}
+func (m *AssetModulesConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_AssetModulesConfig.Marshal(b, m, deterministic)
+}
+func (m *AssetModulesConfig) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_AssetModulesConfig.Merge(m, src)
+}
+func (m *AssetModulesConfig) XXX_Size() int {
+	return xxx_messageInfo_AssetModulesConfig.Size(m)
+}
+func (m *AssetModulesConfig) XXX_DiscardUnknown() {
+	xxx_messageInfo_AssetModulesConfig.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_AssetModulesConfig proto.InternalMessageInfo
+
+func (m *AssetModulesConfig) GetAppVersion() []int64 {
+	if m != nil {
+		return m.AppVersion
+	}
+	return nil
+}
+
+func (m *AssetModulesConfig) GetAssetVersionTag() string {
+	if m != nil {
+		return m.AssetVersionTag
+	}
+	return ""
+}
+
+func init() {
+	proto.RegisterEnum("android.bundle.BundleConfig_BundleType", BundleConfig_BundleType_name, BundleConfig_BundleType_value)
+	proto.RegisterEnum("android.bundle.SplitDimension_Value", SplitDimension_Value_name, SplitDimension_Value_value)
+	proto.RegisterType((*BundleConfig)(nil), "android.bundle.BundleConfig")
+	proto.RegisterType((*Bundletool)(nil), "android.bundle.Bundletool")
+	proto.RegisterType((*Compression)(nil), "android.bundle.Compression")
+	proto.RegisterType((*MasterResources)(nil), "android.bundle.MasterResources")
+	proto.RegisterType((*Optimizations)(nil), "android.bundle.Optimizations")
+	proto.RegisterType((*UncompressNativeLibraries)(nil), "android.bundle.UncompressNativeLibraries")
+	proto.RegisterType((*UncompressDexFiles)(nil), "android.bundle.UncompressDexFiles")
+	proto.RegisterType((*SplitsConfig)(nil), "android.bundle.SplitsConfig")
+	proto.RegisterType((*StandaloneConfig)(nil), "android.bundle.StandaloneConfig")
+	proto.RegisterType((*SplitDimension)(nil), "android.bundle.SplitDimension")
+	proto.RegisterType((*SuffixStripping)(nil), "android.bundle.SuffixStripping")
+	proto.RegisterType((*ApexConfig)(nil), "android.bundle.ApexConfig")
+	proto.RegisterType((*ApexEmbeddedApkConfig)(nil), "android.bundle.ApexEmbeddedApkConfig")
+	proto.RegisterType((*UnsignedEmbeddedApkConfig)(nil), "android.bundle.UnsignedEmbeddedApkConfig")
+	proto.RegisterType((*AssetModulesConfig)(nil), "android.bundle.AssetModulesConfig")
+}
+
+func init() {
+	proto.RegisterFile("config.proto", fileDescriptor_3eaf2c85e69e9ea4)
+}
+
+var fileDescriptor_3eaf2c85e69e9ea4 = []byte{
+	// 1001 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0xdb, 0x6e, 0xdb, 0x46,
+	0x10, 0x0d, 0x75, 0xb1, 0xe5, 0x91, 0x2c, 0xd1, 0xdb, 0x38, 0x50, 0x2e, 0x4d, 0x5c, 0xa2, 0x41,
+	0xdd, 0xb4, 0x50, 0x01, 0x3b, 0xcd, 0x83, 0x83, 0x3e, 0xd0, 0x32, 0xad, 0x2a, 0xd0, 0x0d, 0x4b,
+	0xc9, 0x4d, 0x5a, 0xa0, 0x8b, 0x95, 0xb8, 0x52, 0xb7, 0xa6, 0x48, 0x82, 0x4b, 0x1a, 0x4a, 0xfb,
+	0x09, 0x7d, 0xe9, 0x8f, 0xf4, 0xa7, 0xfa, 0x25, 0x05, 0x97, 0xa4, 0x2c, 0x51, 0x52, 0x9e, 0xfa,
+	0x24, 0xce, 0xec, 0x39, 0xb3, 0x3b, 0xb3, 0x67, 0x67, 0x04, 0x95, 0x89, 0xeb, 0x4c, 0xf9, 0xac,
+	0xe1, 0xf9, 0x6e, 0xe0, 0xa2, 0x2a, 0x75, 0x2c, 0xdf, 0xe5, 0x56, 0x63, 0x1c, 0x3a, 0x96, 0xcd,
+	0xb4, 0xbf, 0x8a, 0x50, 0xb9, 0x94, 0x9f, 0x4d, 0x09, 0x43, 0x17, 0x00, 0xf1, 0x52, 0xe0, 0xba,
+	0x76, 0x5d, 0x39, 0x51, 0x4e, 0xcb, 0x67, 0x4f, 0x1a, 0xeb, 0xac, 0xc6, 0xe5, 0x12, 0x81, 0x57,
+	0xd0, 0xa8, 0x09, 0x87, 0xae, 0x17, 0xf0, 0x39, 0xff, 0x83, 0x06, 0xdc, 0x75, 0x44, 0x3d, 0x27,
+	0xe9, 0x9f, 0x67, 0xe9, 0xfd, 0x55, 0x10, 0x5e, 0xe7, 0xa0, 0x1f, 0xa0, 0x3c, 0x71, 0xe7, 0x9e,
+	0xcf, 0x84, 0xe0, 0xae, 0x53, 0xcf, 0xcb, 0x10, 0x4f, 0xb3, 0x21, 0x9a, 0xf7, 0x10, 0xbc, 0x8a,
+	0x47, 0xef, 0x40, 0x9d, 0x53, 0x11, 0x30, 0x9f, 0xf8, 0x4c, 0xb8, 0xa1, 0x3f, 0x61, 0xa2, 0x5e,
+	0x90, 0x31, 0x5e, 0x64, 0x63, 0x74, 0x25, 0x0e, 0xa7, 0x30, 0x5c, 0x9b, 0xaf, 0x3b, 0xd0, 0x5b,
+	0x28, 0x53, 0x8f, 0x2d, 0x48, 0x5c, 0xc1, 0x7a, 0x71, 0x7b, 0x31, 0x74, 0x8f, 0x2d, 0xe2, 0xe2,
+	0x61, 0xa0, 0xcb, 0x6f, 0xf4, 0x3b, 0x3c, 0x0b, 0x1d, 0xc1, 0x67, 0x0e, 0xb3, 0x08, 0x9b, 0x8f,
+	0x99, 0x65, 0x31, 0x8b, 0x50, 0xef, 0x36, 0x8d, 0xb6, 0x77, 0x92, 0x3f, 0x2d, 0x9f, 0x7d, 0x9d,
+	0x8d, 0x36, 0x4a, 0x38, 0x46, 0x42, 0xd1, 0xbd, 0xdb, 0x24, 0xf8, 0xe3, 0x70, 0xd7, 0x12, 0x1a,
+	0xc2, 0x43, 0x2a, 0x04, 0x0b, 0xc8, 0xdc, 0xb5, 0x42, 0x9b, 0x89, 0x74, 0x8f, 0x7d, 0x79, 0x62,
+	0x6d, 0xe3, 0xc4, 0x11, 0xb6, 0x1b, 0x43, 0x93, 0xe0, 0x88, 0x6e, 0xf8, 0xd0, 0x5b, 0x28, 0x04,
+	0x1f, 0x3d, 0x56, 0x2f, 0x9d, 0x28, 0xa7, 0xd5, 0xb3, 0xaf, 0xb6, 0x8b, 0x20, 0xc6, 0x26, 0xc6,
+	0xf0, 0xa3, 0xc7, 0xb0, 0x24, 0x69, 0xe7, 0x00, 0xf7, 0x3e, 0x54, 0x86, 0x7d, 0x6c, 0xb4, 0x46,
+	0x1d, 0x1d, 0xab, 0x0f, 0x50, 0x09, 0x0a, 0xfa, 0xc0, 0x78, 0xaf, 0x2a, 0xa8, 0x0a, 0xa0, 0x9b,
+	0xa6, 0x31, 0x24, 0xfd, 0x5e, 0xe7, 0x83, 0x9a, 0xd3, 0xbe, 0x4d, 0x49, 0x52, 0x4e, 0x75, 0xd8,
+	0xbf, 0x63, 0xbe, 0x54, 0x41, 0x24, 0xa4, 0x03, 0x9c, 0x9a, 0xef, 0x0a, 0x25, 0x45, 0xcd, 0x69,
+	0x17, 0x50, 0x5e, 0x91, 0x01, 0xfa, 0x06, 0x8e, 0x42, 0x27, 0x95, 0x02, 0xb3, 0xc8, 0xcc, 0x76,
+	0xc7, 0x75, 0xe5, 0x24, 0x7f, 0x7a, 0x80, 0xd5, 0xd5, 0x85, 0x96, 0xed, 0x8e, 0xb5, 0x5f, 0xa0,
+	0x96, 0xb9, 0x7e, 0xf4, 0x05, 0x54, 0x52, 0xc9, 0x10, 0x6e, 0x09, 0x49, 0x2d, 0xe2, 0x72, 0xea,
+	0x6b, 0x5b, 0x02, 0xbd, 0x84, 0xea, 0x12, 0xe2, 0xd0, 0x39, 0x8b, 0x14, 0x1e, 0xc5, 0x3f, 0x4c,
+	0xbd, 0xbd, 0xc8, 0xa9, 0xfd, 0x9b, 0x83, 0xc3, 0x35, 0x8d, 0x23, 0x1d, 0x0e, 0x85, 0x67, 0xf3,
+	0x60, 0x79, 0x33, 0xf1, 0xc3, 0x7a, 0x96, 0xad, 0xa9, 0x29, 0x41, 0xc9, 0x9d, 0x54, 0xc4, 0x8a,
+	0x85, 0x38, 0x3c, 0xbd, 0xcf, 0x82, 0x38, 0x34, 0xe0, 0x77, 0x8c, 0xd8, 0x7c, 0xec, 0x53, 0x9f,
+	0xb3, 0xf4, 0xa9, 0x6d, 0x91, 0x53, 0x4a, 0xe9, 0x49, 0x46, 0x27, 0x25, 0x44, 0x72, 0xda, 0xb1,
+	0x14, 0xc9, 0x69, 0x65, 0x2b, 0x8b, 0x2d, 0xc8, 0x94, 0xdb, 0x4c, 0x24, 0x6f, 0x51, 0xdb, 0xbd,
+	0xc7, 0x15, 0x5b, 0x5c, 0x47, 0x48, 0x8c, 0xc2, 0x0d, 0x1f, 0xea, 0xc2, 0x91, 0x08, 0xa8, 0x63,
+	0x51, 0xdb, 0x75, 0x58, 0x5a, 0x87, 0xf8, 0x69, 0x9e, 0x6c, 0xd4, 0x61, 0x09, 0x4c, 0x6a, 0xa1,
+	0x8a, 0x8c, 0x47, 0xfb, 0x1e, 0x1e, 0xef, 0x4c, 0x2e, 0x92, 0x0e, 0x73, 0xe8, 0xd8, 0x66, 0x96,
+	0xac, 0x74, 0x09, 0xa7, 0xa6, 0xd6, 0x00, 0xb4, 0x79, 0xde, 0x4f, 0xe0, 0x7f, 0x82, 0xca, 0xea,
+	0xa5, 0xa0, 0x16, 0xd4, 0xe4, 0xb5, 0x10, 0x8b, 0xcf, 0x99, 0x23, 0xc5, 0xa9, 0xc8, 0x97, 0xfc,
+	0x7c, 0xeb, 0x5d, 0x5e, 0xa5, 0x28, 0x5c, 0x15, 0x6b, 0xb6, 0xf6, 0xb7, 0x02, 0x6a, 0x36, 0xcd,
+	0xff, 0x2d, 0x3a, 0x3a, 0x87, 0x47, 0x22, 0xf0, 0xb9, 0x47, 0xde, 0xbc, 0x26, 0x63, 0x1e, 0x64,
+	0x84, 0x52, 0xc2, 0x9f, 0xc9, 0xd5, 0x37, 0xaf, 0x2f, 0x79, 0xb0, 0xac, 0x9a, 0xf6, 0x4f, 0x0e,
+	0xaa, 0xeb, 0x71, 0xd1, 0x05, 0x14, 0xef, 0xa8, 0x1d, 0x32, 0x59, 0x96, 0xea, 0xd9, 0x97, 0x9f,
+	0x3e, 0x46, 0xe3, 0x26, 0xc2, 0xe2, 0x98, 0x82, 0x1e, 0xc1, 0x9e, 0xc3, 0x66, 0x34, 0x60, 0xc9,
+	0x9e, 0x89, 0x15, 0xb5, 0x68, 0x11, 0x4e, 0xa7, 0x7c, 0x41, 0xe4, 0x21, 0x3c, 0xee, 0xcc, 0x12,
+	0x69, 0x6d, 0xb4, 0x68, 0x53, 0xe2, 0xcc, 0x14, 0x86, 0x6b, 0x62, 0xdd, 0xa1, 0xfd, 0x09, 0x45,
+	0xb9, 0x27, 0x3a, 0x86, 0xa3, 0x51, 0xcf, 0x1c, 0x18, 0xcd, 0xf6, 0x75, 0xdb, 0xb8, 0x22, 0x37,
+	0x7a, 0x67, 0x64, 0xa8, 0x0f, 0xd0, 0x3e, 0xe4, 0xf5, 0xcb, 0xb6, 0xaa, 0x20, 0x04, 0x55, 0xb3,
+	0x89, 0x0d, 0xa3, 0x47, 0xae, 0x8c, 0x9e, 0xd9, 0x1e, 0x7e, 0x50, 0x73, 0xa8, 0x02, 0xa5, 0x8e,
+	0xde, 0x6b, 0x8d, 0xf4, 0x96, 0xa1, 0xe6, 0xd1, 0x73, 0x78, 0x32, 0x34, 0xde, 0x0f, 0x47, 0xd8,
+	0x20, 0xcd, 0x7e, 0x77, 0x80, 0x0d, 0xd3, 0x6c, 0xf7, 0x7b, 0xe4, 0xba, 0x8f, 0xbb, 0xfa, 0x50,
+	0x2d, 0x20, 0x15, 0x2a, 0x2d, 0xac, 0x0f, 0x7e, 0x6c, 0x37, 0x4d, 0xa2, 0x0f, 0xda, 0x6a, 0x51,
+	0xc3, 0x50, 0xcb, 0x1c, 0x70, 0xb7, 0x90, 0xa2, 0xde, 0x61, 0xb1, 0x29, 0x0d, 0xed, 0x80, 0xc4,
+	0x49, 0x24, 0x4d, 0xed, 0x30, 0xf1, 0xc6, 0x91, 0x34, 0x1b, 0xe0, 0x7e, 0xa0, 0xa0, 0x5f, 0xa1,
+	0x2e, 0x27, 0xd0, 0xb6, 0x01, 0x12, 0x0b, 0xe3, 0xe5, 0xb6, 0x71, 0xb4, 0x39, 0x3c, 0x8e, 0xe9,
+	0x36, 0xb7, 0xd6, 0x83, 0xe3, 0xad, 0xf8, 0xa8, 0x19, 0x7a, 0x74, 0x72, 0x4b, 0x67, 0x71, 0xa3,
+	0x93, 0xc9, 0x1c, 0xe0, 0x72, 0xe2, 0x8b, 0xda, 0x1c, 0x42, 0x50, 0xf0, 0x68, 0xf0, 0x5b, 0x92,
+	0x86, 0xfc, 0xd6, 0xbe, 0x8b, 0x1e, 0xe5, 0xae, 0x29, 0x95, 0x12, 0x94, 0x15, 0x02, 0x05, 0xb4,
+	0x39, 0x8d, 0xd0, 0x8b, 0x68, 0xf0, 0x7a, 0x24, 0xed, 0xfe, 0x51, 0xa6, 0xf9, 0x68, 0xb8, 0x7a,
+	0x37, 0xb1, 0x07, 0xbd, 0x82, 0xa3, 0x78, 0xe0, 0x25, 0x10, 0x12, 0xd0, 0x59, 0x72, 0x90, 0x9a,
+	0x5c, 0x48, 0x80, 0x43, 0x3a, 0xbb, 0x7c, 0x05, 0x68, 0xe2, 0xce, 0x33, 0x65, 0xfa, 0xf9, 0x61,
+	0x62, 0x93, 0xd8, 0x26, 0xf2, 0xef, 0xd1, 0x78, 0x4f, 0xfe, 0x9c, 0xff, 0x17, 0x00, 0x00, 0xff,
+	0xff, 0x6b, 0x05, 0xbf, 0x99, 0x35, 0x09, 0x00, 0x00,
+}
diff --git a/cmd/extract_apks/bundle_proto/config.proto b/cmd/extract_apks/bundle_proto/config.proto
new file mode 100644
index 0000000..1c161aa
--- /dev/null
+++ b/cmd/extract_apks/bundle_proto/config.proto
@@ -0,0 +1,162 @@
+// Messages describing APK Set's table of contents (toc.pb entry).
+// Please be advised that the ultimate source is at
+// https://github.com/google/bundletool/tree/master/src/main/proto
+// so you have been warned.
+syntax = "proto3";
+
+package android.bundle;
+
+option go_package = "android_bundle_proto";
+option java_package = "com.android.bundle";
+
+message BundleConfig {
+  Bundletool bundletool = 1;
+  Optimizations optimizations = 2;
+  Compression compression = 3;
+  // Resources to be always kept in the master split.
+  MasterResources master_resources = 4;
+  ApexConfig apex_config = 5;
+  // APKs to be signed with the same key as generated APKs.
+  repeated UnsignedEmbeddedApkConfig unsigned_embedded_apk_config = 6;
+  AssetModulesConfig asset_modules_config = 7;
+
+  enum BundleType {
+    REGULAR = 0;
+    APEX = 1;
+    ASSET_ONLY = 2;
+  }
+  BundleType type = 8;
+}
+
+message Bundletool {
+  reserved 1;
+  // Version of BundleTool used to build the Bundle.
+  string version = 2;
+}
+
+message Compression {
+  // Glob matching the list of files to leave uncompressed in the APKs.
+  // The matching is done against the path of files in the APK, thus excluding
+  // the name of the modules, and using forward slash ("/") as a name separator.
+  // Examples: "res/raw/**", "assets/**/*.uncompressed", etc.
+  repeated string uncompressed_glob = 1;
+}
+
+// Resources to keep in the master split.
+message MasterResources {
+  // Resource IDs to be kept in master split.
+  repeated int32 resource_ids = 1;
+  // Resource names to be kept in master split.
+  repeated string resource_names = 2;
+}
+
+message Optimizations {
+  SplitsConfig splits_config = 1;
+  // This is for uncompressing native libraries on M+ devices (L+ devices on
+  // instant apps).
+  UncompressNativeLibraries uncompress_native_libraries = 2;
+  // This is for uncompressing dex files on P+ devices.
+  UncompressDexFiles uncompress_dex_files = 3;
+  // Configuration for the generation of standalone APKs.
+  // If no StandaloneConfig is set, the configuration is inherited from
+  // splits_config.
+  StandaloneConfig standalone_config = 4;
+}
+
+message UncompressNativeLibraries {
+  bool enabled = 1;
+}
+
+message UncompressDexFiles {
+  bool enabled = 1;
+}
+
+// Optimization configuration used to generate Split APKs.
+message SplitsConfig {
+  repeated SplitDimension split_dimension = 1;
+}
+
+// Optimization configuration used to generate Standalone APKs.
+message StandaloneConfig {
+  // Device targeting dimensions to shard.
+  repeated SplitDimension split_dimension = 1;
+  // Whether 64 bit libraries should be stripped from Standalone APKs.
+  bool strip_64_bit_libraries = 2;
+}
+
+message SplitDimension {
+  enum Value {
+    UNSPECIFIED_VALUE = 0;
+    ABI = 1;
+    SCREEN_DENSITY = 2;
+    LANGUAGE = 3;
+    TEXTURE_COMPRESSION_FORMAT = 4;
+    // BEGIN-INTERNAL
+    GRAPHICS_API = 5;
+    // END-INTERNAL
+  }
+  Value value = 1;
+
+  // If set to 'true', indicates that APKs should *not* be split by this
+  // dimension.
+  bool negate = 2;
+
+  // Optional transformation to be applied to asset directories where
+  // the targeting is encoded in the directory name (e.g: assets/foo#tcf_etc1)
+  SuffixStripping suffix_stripping = 3;
+}
+
+message SuffixStripping {
+  // If set to 'true', indicates that the targeting suffix should be removed
+  // from assets paths for this dimension when splits (or asset slices) are
+  // generated.
+  // This only applies to assets.
+  // For example a folder with path "assets/level1_textures#tcf_etc1"
+  // would be outputted to "assets/level1_textures". File contents are
+  // unchanged.
+  bool enabled = 1;
+
+  // The default suffix to be used for the cases where separate slices can't
+  // be generated for this dimension. In the case of standalone/universal APKs
+  // generation, stripping the suffix can lead to file name collisions. This
+  // default suffix defines the directories to retain. The others are
+  // discarded: standalone/universal APKs will contain only directories
+  // targeted at this value for the dimension.
+  //
+  // If not set or empty, the fallback directory in each directory group will be
+  // used (for example, if both "assets/level1_textures#tcf_etc1" and
+  // "assets/level1_textures" are present and the default suffix is empty,
+  // then only "assets/level1_textures" will be used).
+  string default_suffix = 2;
+}
+
+// Configuration for processing APEX bundles.
+// https://source.android.com/devices/tech/ota/apex
+message ApexConfig {
+  // Configuration for processing of APKs embedded in an APEX image.
+  repeated ApexEmbeddedApkConfig apex_embedded_apk_config = 1;
+}
+
+message ApexEmbeddedApkConfig {
+  // Android package name of the APK.
+  string package_name = 1;
+
+  // Path to the APK within the APEX system image.
+  string path = 2;
+}
+
+message UnsignedEmbeddedApkConfig {
+  // Path to the APK inside the module (e.g. if the path inside the bundle
+  // is split/assets/example.apk, this will be assets/example.apk).
+  string path = 1;
+}
+
+message AssetModulesConfig {
+  // App versionCodes that will be updated with these asset modules.
+  // Only relevant for asset-only bundles.
+  repeated int64 app_version = 1;
+
+  // Version tag for the asset upload.
+  // Only relevant for asset-only bundles.
+  string asset_version_tag = 2;
+}
diff --git a/cmd/extract_apks/bundle_proto/regen.sh b/cmd/extract_apks/bundle_proto/regen.sh
new file mode 100755
index 0000000..89fb655
--- /dev/null
+++ b/cmd/extract_apks/bundle_proto/regen.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+# Generates the golang source file of protos file describing APK set table of
+# contents (toc.pb file).
+
+set -e
+function die() { echo "ERROR: $1" >&2; exit 1; }
+
+readonly error_msg="Maybe you need to run 'lunch aosp_arm-eng && m aprotoc blueprint_tools'?"
+
+hash aprotoc &>/dev/null || die "could not find aprotoc. ${error_msg}"
+# TODO(asmundak): maybe have the paths relative to repo top?
+(cd "${0%/*}" && aprotoc --go_out=paths=source_relative:. commands.proto config.proto targeting.proto ) || die "build failed. ${error_msg}"
diff --git a/cmd/extract_apks/bundle_proto/targeting.pb.go b/cmd/extract_apks/bundle_proto/targeting.pb.go
new file mode 100644
index 0000000..187bc44
--- /dev/null
+++ b/cmd/extract_apks/bundle_proto/targeting.pb.go
@@ -0,0 +1,1734 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: targeting.proto
+
+package android_bundle_proto
+
+import (
+	fmt "fmt"
+	proto "github.com/golang/protobuf/proto"
+	math "math"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+
+type ScreenDensity_DensityAlias int32
+
+const (
+	ScreenDensity_DENSITY_UNSPECIFIED ScreenDensity_DensityAlias = 0
+	ScreenDensity_NODPI               ScreenDensity_DensityAlias = 1
+	ScreenDensity_LDPI                ScreenDensity_DensityAlias = 2
+	ScreenDensity_MDPI                ScreenDensity_DensityAlias = 3
+	ScreenDensity_TVDPI               ScreenDensity_DensityAlias = 4
+	ScreenDensity_HDPI                ScreenDensity_DensityAlias = 5
+	ScreenDensity_XHDPI               ScreenDensity_DensityAlias = 6
+	ScreenDensity_XXHDPI              ScreenDensity_DensityAlias = 7
+	ScreenDensity_XXXHDPI             ScreenDensity_DensityAlias = 8
+)
+
+var ScreenDensity_DensityAlias_name = map[int32]string{
+	0: "DENSITY_UNSPECIFIED",
+	1: "NODPI",
+	2: "LDPI",
+	3: "MDPI",
+	4: "TVDPI",
+	5: "HDPI",
+	6: "XHDPI",
+	7: "XXHDPI",
+	8: "XXXHDPI",
+}
+
+var ScreenDensity_DensityAlias_value = map[string]int32{
+	"DENSITY_UNSPECIFIED": 0,
+	"NODPI":               1,
+	"LDPI":                2,
+	"MDPI":                3,
+	"TVDPI":               4,
+	"HDPI":                5,
+	"XHDPI":               6,
+	"XXHDPI":              7,
+	"XXXHDPI":             8,
+}
+
+func (x ScreenDensity_DensityAlias) String() string {
+	return proto.EnumName(ScreenDensity_DensityAlias_name, int32(x))
+}
+
+func (ScreenDensity_DensityAlias) EnumDescriptor() ([]byte, []int) {
+	return fileDescriptor_df45b505afdf471e, []int{4, 0}
+}
+
+type TextureCompressionFormat_TextureCompressionFormatAlias int32
+
+const (
+	TextureCompressionFormat_UNSPECIFIED_TEXTURE_COMPRESSION_FORMAT TextureCompressionFormat_TextureCompressionFormatAlias = 0
+	TextureCompressionFormat_ETC1_RGB8                              TextureCompressionFormat_TextureCompressionFormatAlias = 1
+	TextureCompressionFormat_PALETTED                               TextureCompressionFormat_TextureCompressionFormatAlias = 2
+	TextureCompressionFormat_THREE_DC                               TextureCompressionFormat_TextureCompressionFormatAlias = 3
+	TextureCompressionFormat_ATC                                    TextureCompressionFormat_TextureCompressionFormatAlias = 4
+	TextureCompressionFormat_LATC                                   TextureCompressionFormat_TextureCompressionFormatAlias = 5
+	TextureCompressionFormat_DXT1                                   TextureCompressionFormat_TextureCompressionFormatAlias = 6
+	TextureCompressionFormat_S3TC                                   TextureCompressionFormat_TextureCompressionFormatAlias = 7
+	TextureCompressionFormat_PVRTC                                  TextureCompressionFormat_TextureCompressionFormatAlias = 8
+	TextureCompressionFormat_ASTC                                   TextureCompressionFormat_TextureCompressionFormatAlias = 9
+	TextureCompressionFormat_ETC2                                   TextureCompressionFormat_TextureCompressionFormatAlias = 10
+)
+
+var TextureCompressionFormat_TextureCompressionFormatAlias_name = map[int32]string{
+	0:  "UNSPECIFIED_TEXTURE_COMPRESSION_FORMAT",
+	1:  "ETC1_RGB8",
+	2:  "PALETTED",
+	3:  "THREE_DC",
+	4:  "ATC",
+	5:  "LATC",
+	6:  "DXT1",
+	7:  "S3TC",
+	8:  "PVRTC",
+	9:  "ASTC",
+	10: "ETC2",
+}
+
+var TextureCompressionFormat_TextureCompressionFormatAlias_value = map[string]int32{
+	"UNSPECIFIED_TEXTURE_COMPRESSION_FORMAT": 0,
+	"ETC1_RGB8":                              1,
+	"PALETTED":                               2,
+	"THREE_DC":                               3,
+	"ATC":                                    4,
+	"LATC":                                   5,
+	"DXT1":                                   6,
+	"S3TC":                                   7,
+	"PVRTC":                                  8,
+	"ASTC":                                   9,
+	"ETC2":                                   10,
+}
+
+func (x TextureCompressionFormat_TextureCompressionFormatAlias) String() string {
+	return proto.EnumName(TextureCompressionFormat_TextureCompressionFormatAlias_name, int32(x))
+}
+
+func (TextureCompressionFormat_TextureCompressionFormatAlias) EnumDescriptor() ([]byte, []int) {
+	return fileDescriptor_df45b505afdf471e, []int{10, 0}
+}
+
+type Abi_AbiAlias int32
+
+const (
+	Abi_UNSPECIFIED_CPU_ARCHITECTURE Abi_AbiAlias = 0
+	Abi_ARMEABI                      Abi_AbiAlias = 1
+	Abi_ARMEABI_V7A                  Abi_AbiAlias = 2
+	Abi_ARM64_V8A                    Abi_AbiAlias = 3
+	Abi_X86                          Abi_AbiAlias = 4
+	Abi_X86_64                       Abi_AbiAlias = 5
+	Abi_MIPS                         Abi_AbiAlias = 6
+	Abi_MIPS64                       Abi_AbiAlias = 7
+)
+
+var Abi_AbiAlias_name = map[int32]string{
+	0: "UNSPECIFIED_CPU_ARCHITECTURE",
+	1: "ARMEABI",
+	2: "ARMEABI_V7A",
+	3: "ARM64_V8A",
+	4: "X86",
+	5: "X86_64",
+	6: "MIPS",
+	7: "MIPS64",
+}
+
+var Abi_AbiAlias_value = map[string]int32{
+	"UNSPECIFIED_CPU_ARCHITECTURE": 0,
+	"ARMEABI":                      1,
+	"ARMEABI_V7A":                  2,
+	"ARM64_V8A":                    3,
+	"X86":                          4,
+	"X86_64":                       5,
+	"MIPS":                         6,
+	"MIPS64":                       7,
+}
+
+func (x Abi_AbiAlias) String() string {
+	return proto.EnumName(Abi_AbiAlias_name, int32(x))
+}
+
+func (Abi_AbiAlias) EnumDescriptor() ([]byte, []int) {
+	return fileDescriptor_df45b505afdf471e, []int{11, 0}
+}
+
+type Sanitizer_SanitizerAlias int32
+
+const (
+	Sanitizer_NONE      Sanitizer_SanitizerAlias = 0
+	Sanitizer_HWADDRESS Sanitizer_SanitizerAlias = 1
+)
+
+var Sanitizer_SanitizerAlias_name = map[int32]string{
+	0: "NONE",
+	1: "HWADDRESS",
+}
+
+var Sanitizer_SanitizerAlias_value = map[string]int32{
+	"NONE":      0,
+	"HWADDRESS": 1,
+}
+
+func (x Sanitizer_SanitizerAlias) String() string {
+	return proto.EnumName(Sanitizer_SanitizerAlias_name, int32(x))
+}
+
+func (Sanitizer_SanitizerAlias) EnumDescriptor() ([]byte, []int) {
+	return fileDescriptor_df45b505afdf471e, []int{13, 0}
+}
+
+// Targeting on the level of variants.
+type VariantTargeting struct {
+	SdkVersionTargeting               *SdkVersionTargeting               `protobuf:"bytes,1,opt,name=sdk_version_targeting,json=sdkVersionTargeting,proto3" json:"sdk_version_targeting,omitempty"`
+	AbiTargeting                      *AbiTargeting                      `protobuf:"bytes,2,opt,name=abi_targeting,json=abiTargeting,proto3" json:"abi_targeting,omitempty"`
+	ScreenDensityTargeting            *ScreenDensityTargeting            `protobuf:"bytes,3,opt,name=screen_density_targeting,json=screenDensityTargeting,proto3" json:"screen_density_targeting,omitempty"`
+	MultiAbiTargeting                 *MultiAbiTargeting                 `protobuf:"bytes,4,opt,name=multi_abi_targeting,json=multiAbiTargeting,proto3" json:"multi_abi_targeting,omitempty"`
+	TextureCompressionFormatTargeting *TextureCompressionFormatTargeting `protobuf:"bytes,5,opt,name=texture_compression_format_targeting,json=textureCompressionFormatTargeting,proto3" json:"texture_compression_format_targeting,omitempty"`
+	XXX_NoUnkeyedLiteral              struct{}                           `json:"-"`
+	XXX_unrecognized                  []byte                             `json:"-"`
+	XXX_sizecache                     int32                              `json:"-"`
+}
+
+func (m *VariantTargeting) Reset()         { *m = VariantTargeting{} }
+func (m *VariantTargeting) String() string { return proto.CompactTextString(m) }
+func (*VariantTargeting) ProtoMessage()    {}
+func (*VariantTargeting) Descriptor() ([]byte, []int) {
+	return fileDescriptor_df45b505afdf471e, []int{0}
+}
+
+func (m *VariantTargeting) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_VariantTargeting.Unmarshal(m, b)
+}
+func (m *VariantTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_VariantTargeting.Marshal(b, m, deterministic)
+}
+func (m *VariantTargeting) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_VariantTargeting.Merge(m, src)
+}
+func (m *VariantTargeting) XXX_Size() int {
+	return xxx_messageInfo_VariantTargeting.Size(m)
+}
+func (m *VariantTargeting) XXX_DiscardUnknown() {
+	xxx_messageInfo_VariantTargeting.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_VariantTargeting proto.InternalMessageInfo
+
+func (m *VariantTargeting) GetSdkVersionTargeting() *SdkVersionTargeting {
+	if m != nil {
+		return m.SdkVersionTargeting
+	}
+	return nil
+}
+
+func (m *VariantTargeting) GetAbiTargeting() *AbiTargeting {
+	if m != nil {
+		return m.AbiTargeting
+	}
+	return nil
+}
+
+func (m *VariantTargeting) GetScreenDensityTargeting() *ScreenDensityTargeting {
+	if m != nil {
+		return m.ScreenDensityTargeting
+	}
+	return nil
+}
+
+func (m *VariantTargeting) GetMultiAbiTargeting() *MultiAbiTargeting {
+	if m != nil {
+		return m.MultiAbiTargeting
+	}
+	return nil
+}
+
+func (m *VariantTargeting) GetTextureCompressionFormatTargeting() *TextureCompressionFormatTargeting {
+	if m != nil {
+		return m.TextureCompressionFormatTargeting
+	}
+	return nil
+}
+
+// Targeting on the level of individual APKs.
+type ApkTargeting struct {
+	AbiTargeting                      *AbiTargeting                      `protobuf:"bytes,1,opt,name=abi_targeting,json=abiTargeting,proto3" json:"abi_targeting,omitempty"`
+	GraphicsApiTargeting              *GraphicsApiTargeting              `protobuf:"bytes,2,opt,name=graphics_api_targeting,json=graphicsApiTargeting,proto3" json:"graphics_api_targeting,omitempty"`
+	LanguageTargeting                 *LanguageTargeting                 `protobuf:"bytes,3,opt,name=language_targeting,json=languageTargeting,proto3" json:"language_targeting,omitempty"`
+	ScreenDensityTargeting            *ScreenDensityTargeting            `protobuf:"bytes,4,opt,name=screen_density_targeting,json=screenDensityTargeting,proto3" json:"screen_density_targeting,omitempty"`
+	SdkVersionTargeting               *SdkVersionTargeting               `protobuf:"bytes,5,opt,name=sdk_version_targeting,json=sdkVersionTargeting,proto3" json:"sdk_version_targeting,omitempty"`
+	TextureCompressionFormatTargeting *TextureCompressionFormatTargeting `protobuf:"bytes,6,opt,name=texture_compression_format_targeting,json=textureCompressionFormatTargeting,proto3" json:"texture_compression_format_targeting,omitempty"`
+	MultiAbiTargeting                 *MultiAbiTargeting                 `protobuf:"bytes,7,opt,name=multi_abi_targeting,json=multiAbiTargeting,proto3" json:"multi_abi_targeting,omitempty"`
+	SanitizerTargeting                *SanitizerTargeting                `protobuf:"bytes,8,opt,name=sanitizer_targeting,json=sanitizerTargeting,proto3" json:"sanitizer_targeting,omitempty"`
+	XXX_NoUnkeyedLiteral              struct{}                           `json:"-"`
+	XXX_unrecognized                  []byte                             `json:"-"`
+	XXX_sizecache                     int32                              `json:"-"`
+}
+
+func (m *ApkTargeting) Reset()         { *m = ApkTargeting{} }
+func (m *ApkTargeting) String() string { return proto.CompactTextString(m) }
+func (*ApkTargeting) ProtoMessage()    {}
+func (*ApkTargeting) Descriptor() ([]byte, []int) {
+	return fileDescriptor_df45b505afdf471e, []int{1}
+}
+
+func (m *ApkTargeting) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_ApkTargeting.Unmarshal(m, b)
+}
+func (m *ApkTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_ApkTargeting.Marshal(b, m, deterministic)
+}
+func (m *ApkTargeting) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_ApkTargeting.Merge(m, src)
+}
+func (m *ApkTargeting) XXX_Size() int {
+	return xxx_messageInfo_ApkTargeting.Size(m)
+}
+func (m *ApkTargeting) XXX_DiscardUnknown() {
+	xxx_messageInfo_ApkTargeting.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ApkTargeting proto.InternalMessageInfo
+
+func (m *ApkTargeting) GetAbiTargeting() *AbiTargeting {
+	if m != nil {
+		return m.AbiTargeting
+	}
+	return nil
+}
+
+func (m *ApkTargeting) GetGraphicsApiTargeting() *GraphicsApiTargeting {
+	if m != nil {
+		return m.GraphicsApiTargeting
+	}
+	return nil
+}
+
+func (m *ApkTargeting) GetLanguageTargeting() *LanguageTargeting {
+	if m != nil {
+		return m.LanguageTargeting
+	}
+	return nil
+}
+
+func (m *ApkTargeting) GetScreenDensityTargeting() *ScreenDensityTargeting {
+	if m != nil {
+		return m.ScreenDensityTargeting
+	}
+	return nil
+}
+
+func (m *ApkTargeting) GetSdkVersionTargeting() *SdkVersionTargeting {
+	if m != nil {
+		return m.SdkVersionTargeting
+	}
+	return nil
+}
+
+func (m *ApkTargeting) GetTextureCompressionFormatTargeting() *TextureCompressionFormatTargeting {
+	if m != nil {
+		return m.TextureCompressionFormatTargeting
+	}
+	return nil
+}
+
+func (m *ApkTargeting) GetMultiAbiTargeting() *MultiAbiTargeting {
+	if m != nil {
+		return m.MultiAbiTargeting
+	}
+	return nil
+}
+
+func (m *ApkTargeting) GetSanitizerTargeting() *SanitizerTargeting {
+	if m != nil {
+		return m.SanitizerTargeting
+	}
+	return nil
+}
+
+// Targeting on the module level.
+// The semantic of the targeting is the "AND" rule on all immediate values.
+type ModuleTargeting struct {
+	SdkVersionTargeting    *SdkVersionTargeting      `protobuf:"bytes,1,opt,name=sdk_version_targeting,json=sdkVersionTargeting,proto3" json:"sdk_version_targeting,omitempty"`
+	DeviceFeatureTargeting []*DeviceFeatureTargeting `protobuf:"bytes,2,rep,name=device_feature_targeting,json=deviceFeatureTargeting,proto3" json:"device_feature_targeting,omitempty"`
+	UserCountriesTargeting *UserCountriesTargeting   `protobuf:"bytes,3,opt,name=user_countries_targeting,json=userCountriesTargeting,proto3" json:"user_countries_targeting,omitempty"`
+	XXX_NoUnkeyedLiteral   struct{}                  `json:"-"`
+	XXX_unrecognized       []byte                    `json:"-"`
+	XXX_sizecache          int32                     `json:"-"`
+}
+
+func (m *ModuleTargeting) Reset()         { *m = ModuleTargeting{} }
+func (m *ModuleTargeting) String() string { return proto.CompactTextString(m) }
+func (*ModuleTargeting) ProtoMessage()    {}
+func (*ModuleTargeting) Descriptor() ([]byte, []int) {
+	return fileDescriptor_df45b505afdf471e, []int{2}
+}
+
+func (m *ModuleTargeting) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_ModuleTargeting.Unmarshal(m, b)
+}
+func (m *ModuleTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_ModuleTargeting.Marshal(b, m, deterministic)
+}
+func (m *ModuleTargeting) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_ModuleTargeting.Merge(m, src)
+}
+func (m *ModuleTargeting) XXX_Size() int {
+	return xxx_messageInfo_ModuleTargeting.Size(m)
+}
+func (m *ModuleTargeting) XXX_DiscardUnknown() {
+	xxx_messageInfo_ModuleTargeting.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ModuleTargeting proto.InternalMessageInfo
+
+func (m *ModuleTargeting) GetSdkVersionTargeting() *SdkVersionTargeting {
+	if m != nil {
+		return m.SdkVersionTargeting
+	}
+	return nil
+}
+
+func (m *ModuleTargeting) GetDeviceFeatureTargeting() []*DeviceFeatureTargeting {
+	if m != nil {
+		return m.DeviceFeatureTargeting
+	}
+	return nil
+}
+
+func (m *ModuleTargeting) GetUserCountriesTargeting() *UserCountriesTargeting {
+	if m != nil {
+		return m.UserCountriesTargeting
+	}
+	return nil
+}
+
+// User Countries targeting describing an inclusive/exclusive list of country
+// codes that module targets.
+type UserCountriesTargeting struct {
+	// List of country codes in the two-letter CLDR territory format.
+	CountryCodes []string `protobuf:"bytes,1,rep,name=country_codes,json=countryCodes,proto3" json:"country_codes,omitempty"`
+	// Indicates if the list above is exclusive.
+	Exclude              bool     `protobuf:"varint,2,opt,name=exclude,proto3" json:"exclude,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *UserCountriesTargeting) Reset()         { *m = UserCountriesTargeting{} }
+func (m *UserCountriesTargeting) String() string { return proto.CompactTextString(m) }
+func (*UserCountriesTargeting) ProtoMessage()    {}
+func (*UserCountriesTargeting) Descriptor() ([]byte, []int) {
+	return fileDescriptor_df45b505afdf471e, []int{3}
+}
+
+func (m *UserCountriesTargeting) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_UserCountriesTargeting.Unmarshal(m, b)
+}
+func (m *UserCountriesTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_UserCountriesTargeting.Marshal(b, m, deterministic)
+}
+func (m *UserCountriesTargeting) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_UserCountriesTargeting.Merge(m, src)
+}
+func (m *UserCountriesTargeting) XXX_Size() int {
+	return xxx_messageInfo_UserCountriesTargeting.Size(m)
+}
+func (m *UserCountriesTargeting) XXX_DiscardUnknown() {
+	xxx_messageInfo_UserCountriesTargeting.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_UserCountriesTargeting proto.InternalMessageInfo
+
+func (m *UserCountriesTargeting) GetCountryCodes() []string {
+	if m != nil {
+		return m.CountryCodes
+	}
+	return nil
+}
+
+func (m *UserCountriesTargeting) GetExclude() bool {
+	if m != nil {
+		return m.Exclude
+	}
+	return false
+}
+
+type ScreenDensity struct {
+	// Types that are valid to be assigned to DensityOneof:
+	//	*ScreenDensity_DensityAlias_
+	//	*ScreenDensity_DensityDpi
+	DensityOneof         isScreenDensity_DensityOneof `protobuf_oneof:"density_oneof"`
+	XXX_NoUnkeyedLiteral struct{}                     `json:"-"`
+	XXX_unrecognized     []byte                       `json:"-"`
+	XXX_sizecache        int32                        `json:"-"`
+}
+
+func (m *ScreenDensity) Reset()         { *m = ScreenDensity{} }
+func (m *ScreenDensity) String() string { return proto.CompactTextString(m) }
+func (*ScreenDensity) ProtoMessage()    {}
+func (*ScreenDensity) Descriptor() ([]byte, []int) {
+	return fileDescriptor_df45b505afdf471e, []int{4}
+}
+
+func (m *ScreenDensity) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_ScreenDensity.Unmarshal(m, b)
+}
+func (m *ScreenDensity) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_ScreenDensity.Marshal(b, m, deterministic)
+}
+func (m *ScreenDensity) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_ScreenDensity.Merge(m, src)
+}
+func (m *ScreenDensity) XXX_Size() int {
+	return xxx_messageInfo_ScreenDensity.Size(m)
+}
+func (m *ScreenDensity) XXX_DiscardUnknown() {
+	xxx_messageInfo_ScreenDensity.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ScreenDensity proto.InternalMessageInfo
+
+type isScreenDensity_DensityOneof interface {
+	isScreenDensity_DensityOneof()
+}
+
+type ScreenDensity_DensityAlias_ struct {
+	DensityAlias ScreenDensity_DensityAlias `protobuf:"varint,1,opt,name=density_alias,json=densityAlias,proto3,enum=android.bundle.ScreenDensity_DensityAlias,oneof"`
+}
+
+type ScreenDensity_DensityDpi struct {
+	DensityDpi int32 `protobuf:"varint,2,opt,name=density_dpi,json=densityDpi,proto3,oneof"`
+}
+
+func (*ScreenDensity_DensityAlias_) isScreenDensity_DensityOneof() {}
+
+func (*ScreenDensity_DensityDpi) isScreenDensity_DensityOneof() {}
+
+func (m *ScreenDensity) GetDensityOneof() isScreenDensity_DensityOneof {
+	if m != nil {
+		return m.DensityOneof
+	}
+	return nil
+}
+
+func (m *ScreenDensity) GetDensityAlias() ScreenDensity_DensityAlias {
+	if x, ok := m.GetDensityOneof().(*ScreenDensity_DensityAlias_); ok {
+		return x.DensityAlias
+	}
+	return ScreenDensity_DENSITY_UNSPECIFIED
+}
+
+func (m *ScreenDensity) GetDensityDpi() int32 {
+	if x, ok := m.GetDensityOneof().(*ScreenDensity_DensityDpi); ok {
+		return x.DensityDpi
+	}
+	return 0
+}
+
+// XXX_OneofWrappers is for the internal use of the proto package.
+func (*ScreenDensity) XXX_OneofWrappers() []interface{} {
+	return []interface{}{
+		(*ScreenDensity_DensityAlias_)(nil),
+		(*ScreenDensity_DensityDpi)(nil),
+	}
+}
+
+// Wrapper message for `int32`.
+//
+// The JSON representation for `Int32Value` is JSON number.
+type Int32Value struct {
+	// The int32 value.
+	Value                int32    `protobuf:"varint,1,opt,name=value,proto3" json:"value,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *Int32Value) Reset()         { *m = Int32Value{} }
+func (m *Int32Value) String() string { return proto.CompactTextString(m) }
+func (*Int32Value) ProtoMessage()    {}
+func (*Int32Value) Descriptor() ([]byte, []int) {
+	return fileDescriptor_df45b505afdf471e, []int{5}
+}
+
+func (m *Int32Value) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_Int32Value.Unmarshal(m, b)
+}
+func (m *Int32Value) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_Int32Value.Marshal(b, m, deterministic)
+}
+func (m *Int32Value) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Int32Value.Merge(m, src)
+}
+func (m *Int32Value) XXX_Size() int {
+	return xxx_messageInfo_Int32Value.Size(m)
+}
+func (m *Int32Value) XXX_DiscardUnknown() {
+	xxx_messageInfo_Int32Value.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Int32Value proto.InternalMessageInfo
+
+func (m *Int32Value) GetValue() int32 {
+	if m != nil {
+		return m.Value
+	}
+	return 0
+}
+
+type SdkVersion struct {
+	// Inclusive.
+	Min                  *Int32Value `protobuf:"bytes,1,opt,name=min,proto3" json:"min,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}    `json:"-"`
+	XXX_unrecognized     []byte      `json:"-"`
+	XXX_sizecache        int32       `json:"-"`
+}
+
+func (m *SdkVersion) Reset()         { *m = SdkVersion{} }
+func (m *SdkVersion) String() string { return proto.CompactTextString(m) }
+func (*SdkVersion) ProtoMessage()    {}
+func (*SdkVersion) Descriptor() ([]byte, []int) {
+	return fileDescriptor_df45b505afdf471e, []int{6}
+}
+
+func (m *SdkVersion) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_SdkVersion.Unmarshal(m, b)
+}
+func (m *SdkVersion) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_SdkVersion.Marshal(b, m, deterministic)
+}
+func (m *SdkVersion) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_SdkVersion.Merge(m, src)
+}
+func (m *SdkVersion) XXX_Size() int {
+	return xxx_messageInfo_SdkVersion.Size(m)
+}
+func (m *SdkVersion) XXX_DiscardUnknown() {
+	xxx_messageInfo_SdkVersion.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_SdkVersion proto.InternalMessageInfo
+
+func (m *SdkVersion) GetMin() *Int32Value {
+	if m != nil {
+		return m.Min
+	}
+	return nil
+}
+
+type GraphicsApi struct {
+	// Types that are valid to be assigned to ApiOneof:
+	//	*GraphicsApi_MinOpenGlVersion
+	//	*GraphicsApi_MinVulkanVersion
+	ApiOneof             isGraphicsApi_ApiOneof `protobuf_oneof:"api_oneof"`
+	XXX_NoUnkeyedLiteral struct{}               `json:"-"`
+	XXX_unrecognized     []byte                 `json:"-"`
+	XXX_sizecache        int32                  `json:"-"`
+}
+
+func (m *GraphicsApi) Reset()         { *m = GraphicsApi{} }
+func (m *GraphicsApi) String() string { return proto.CompactTextString(m) }
+func (*GraphicsApi) ProtoMessage()    {}
+func (*GraphicsApi) Descriptor() ([]byte, []int) {
+	return fileDescriptor_df45b505afdf471e, []int{7}
+}
+
+func (m *GraphicsApi) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_GraphicsApi.Unmarshal(m, b)
+}
+func (m *GraphicsApi) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_GraphicsApi.Marshal(b, m, deterministic)
+}
+func (m *GraphicsApi) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_GraphicsApi.Merge(m, src)
+}
+func (m *GraphicsApi) XXX_Size() int {
+	return xxx_messageInfo_GraphicsApi.Size(m)
+}
+func (m *GraphicsApi) XXX_DiscardUnknown() {
+	xxx_messageInfo_GraphicsApi.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_GraphicsApi proto.InternalMessageInfo
+
+type isGraphicsApi_ApiOneof interface {
+	isGraphicsApi_ApiOneof()
+}
+
+type GraphicsApi_MinOpenGlVersion struct {
+	MinOpenGlVersion *OpenGlVersion `protobuf:"bytes,1,opt,name=min_open_gl_version,json=minOpenGlVersion,proto3,oneof"`
+}
+
+type GraphicsApi_MinVulkanVersion struct {
+	MinVulkanVersion *VulkanVersion `protobuf:"bytes,2,opt,name=min_vulkan_version,json=minVulkanVersion,proto3,oneof"`
+}
+
+func (*GraphicsApi_MinOpenGlVersion) isGraphicsApi_ApiOneof() {}
+
+func (*GraphicsApi_MinVulkanVersion) isGraphicsApi_ApiOneof() {}
+
+func (m *GraphicsApi) GetApiOneof() isGraphicsApi_ApiOneof {
+	if m != nil {
+		return m.ApiOneof
+	}
+	return nil
+}
+
+func (m *GraphicsApi) GetMinOpenGlVersion() *OpenGlVersion {
+	if x, ok := m.GetApiOneof().(*GraphicsApi_MinOpenGlVersion); ok {
+		return x.MinOpenGlVersion
+	}
+	return nil
+}
+
+func (m *GraphicsApi) GetMinVulkanVersion() *VulkanVersion {
+	if x, ok := m.GetApiOneof().(*GraphicsApi_MinVulkanVersion); ok {
+		return x.MinVulkanVersion
+	}
+	return nil
+}
+
+// XXX_OneofWrappers is for the internal use of the proto package.
+func (*GraphicsApi) XXX_OneofWrappers() []interface{} {
+	return []interface{}{
+		(*GraphicsApi_MinOpenGlVersion)(nil),
+		(*GraphicsApi_MinVulkanVersion)(nil),
+	}
+}
+
+type VulkanVersion struct {
+	Major                int32    `protobuf:"varint,1,opt,name=major,proto3" json:"major,omitempty"`
+	Minor                int32    `protobuf:"varint,2,opt,name=minor,proto3" json:"minor,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *VulkanVersion) Reset()         { *m = VulkanVersion{} }
+func (m *VulkanVersion) String() string { return proto.CompactTextString(m) }
+func (*VulkanVersion) ProtoMessage()    {}
+func (*VulkanVersion) Descriptor() ([]byte, []int) {
+	return fileDescriptor_df45b505afdf471e, []int{8}
+}
+
+func (m *VulkanVersion) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_VulkanVersion.Unmarshal(m, b)
+}
+func (m *VulkanVersion) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_VulkanVersion.Marshal(b, m, deterministic)
+}
+func (m *VulkanVersion) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_VulkanVersion.Merge(m, src)
+}
+func (m *VulkanVersion) XXX_Size() int {
+	return xxx_messageInfo_VulkanVersion.Size(m)
+}
+func (m *VulkanVersion) XXX_DiscardUnknown() {
+	xxx_messageInfo_VulkanVersion.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_VulkanVersion proto.InternalMessageInfo
+
+func (m *VulkanVersion) GetMajor() int32 {
+	if m != nil {
+		return m.Major
+	}
+	return 0
+}
+
+func (m *VulkanVersion) GetMinor() int32 {
+	if m != nil {
+		return m.Minor
+	}
+	return 0
+}
+
+type OpenGlVersion struct {
+	// e.g. OpenGL ES 3.2 is represented as { major: 3, minor: 2 }
+	Major                int32    `protobuf:"varint,1,opt,name=major,proto3" json:"major,omitempty"`
+	Minor                int32    `protobuf:"varint,2,opt,name=minor,proto3" json:"minor,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *OpenGlVersion) Reset()         { *m = OpenGlVersion{} }
+func (m *OpenGlVersion) String() string { return proto.CompactTextString(m) }
+func (*OpenGlVersion) ProtoMessage()    {}
+func (*OpenGlVersion) Descriptor() ([]byte, []int) {
+	return fileDescriptor_df45b505afdf471e, []int{9}
+}
+
+func (m *OpenGlVersion) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_OpenGlVersion.Unmarshal(m, b)
+}
+func (m *OpenGlVersion) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_OpenGlVersion.Marshal(b, m, deterministic)
+}
+func (m *OpenGlVersion) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_OpenGlVersion.Merge(m, src)
+}
+func (m *OpenGlVersion) XXX_Size() int {
+	return xxx_messageInfo_OpenGlVersion.Size(m)
+}
+func (m *OpenGlVersion) XXX_DiscardUnknown() {
+	xxx_messageInfo_OpenGlVersion.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_OpenGlVersion proto.InternalMessageInfo
+
+func (m *OpenGlVersion) GetMajor() int32 {
+	if m != nil {
+		return m.Major
+	}
+	return 0
+}
+
+func (m *OpenGlVersion) GetMinor() int32 {
+	if m != nil {
+		return m.Minor
+	}
+	return 0
+}
+
+type TextureCompressionFormat struct {
+	Alias                TextureCompressionFormat_TextureCompressionFormatAlias `protobuf:"varint,1,opt,name=alias,proto3,enum=android.bundle.TextureCompressionFormat_TextureCompressionFormatAlias" json:"alias,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}                                               `json:"-"`
+	XXX_unrecognized     []byte                                                 `json:"-"`
+	XXX_sizecache        int32                                                  `json:"-"`
+}
+
+func (m *TextureCompressionFormat) Reset()         { *m = TextureCompressionFormat{} }
+func (m *TextureCompressionFormat) String() string { return proto.CompactTextString(m) }
+func (*TextureCompressionFormat) ProtoMessage()    {}
+func (*TextureCompressionFormat) Descriptor() ([]byte, []int) {
+	return fileDescriptor_df45b505afdf471e, []int{10}
+}
+
+func (m *TextureCompressionFormat) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_TextureCompressionFormat.Unmarshal(m, b)
+}
+func (m *TextureCompressionFormat) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_TextureCompressionFormat.Marshal(b, m, deterministic)
+}
+func (m *TextureCompressionFormat) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_TextureCompressionFormat.Merge(m, src)
+}
+func (m *TextureCompressionFormat) XXX_Size() int {
+	return xxx_messageInfo_TextureCompressionFormat.Size(m)
+}
+func (m *TextureCompressionFormat) XXX_DiscardUnknown() {
+	xxx_messageInfo_TextureCompressionFormat.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_TextureCompressionFormat proto.InternalMessageInfo
+
+func (m *TextureCompressionFormat) GetAlias() TextureCompressionFormat_TextureCompressionFormatAlias {
+	if m != nil {
+		return m.Alias
+	}
+	return TextureCompressionFormat_UNSPECIFIED_TEXTURE_COMPRESSION_FORMAT
+}
+
+type Abi struct {
+	Alias                Abi_AbiAlias `protobuf:"varint,1,opt,name=alias,proto3,enum=android.bundle.Abi_AbiAlias" json:"alias,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}     `json:"-"`
+	XXX_unrecognized     []byte       `json:"-"`
+	XXX_sizecache        int32        `json:"-"`
+}
+
+func (m *Abi) Reset()         { *m = Abi{} }
+func (m *Abi) String() string { return proto.CompactTextString(m) }
+func (*Abi) ProtoMessage()    {}
+func (*Abi) Descriptor() ([]byte, []int) {
+	return fileDescriptor_df45b505afdf471e, []int{11}
+}
+
+func (m *Abi) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_Abi.Unmarshal(m, b)
+}
+func (m *Abi) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_Abi.Marshal(b, m, deterministic)
+}
+func (m *Abi) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Abi.Merge(m, src)
+}
+func (m *Abi) XXX_Size() int {
+	return xxx_messageInfo_Abi.Size(m)
+}
+func (m *Abi) XXX_DiscardUnknown() {
+	xxx_messageInfo_Abi.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Abi proto.InternalMessageInfo
+
+func (m *Abi) GetAlias() Abi_AbiAlias {
+	if m != nil {
+		return m.Alias
+	}
+	return Abi_UNSPECIFIED_CPU_ARCHITECTURE
+}
+
+type MultiAbi struct {
+	Abi                  []*Abi   `protobuf:"bytes,1,rep,name=abi,proto3" json:"abi,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *MultiAbi) Reset()         { *m = MultiAbi{} }
+func (m *MultiAbi) String() string { return proto.CompactTextString(m) }
+func (*MultiAbi) ProtoMessage()    {}
+func (*MultiAbi) Descriptor() ([]byte, []int) {
+	return fileDescriptor_df45b505afdf471e, []int{12}
+}
+
+func (m *MultiAbi) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_MultiAbi.Unmarshal(m, b)
+}
+func (m *MultiAbi) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_MultiAbi.Marshal(b, m, deterministic)
+}
+func (m *MultiAbi) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MultiAbi.Merge(m, src)
+}
+func (m *MultiAbi) XXX_Size() int {
+	return xxx_messageInfo_MultiAbi.Size(m)
+}
+func (m *MultiAbi) XXX_DiscardUnknown() {
+	xxx_messageInfo_MultiAbi.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MultiAbi proto.InternalMessageInfo
+
+func (m *MultiAbi) GetAbi() []*Abi {
+	if m != nil {
+		return m.Abi
+	}
+	return nil
+}
+
+type Sanitizer struct {
+	Alias                Sanitizer_SanitizerAlias `protobuf:"varint,1,opt,name=alias,proto3,enum=android.bundle.Sanitizer_SanitizerAlias" json:"alias,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}                 `json:"-"`
+	XXX_unrecognized     []byte                   `json:"-"`
+	XXX_sizecache        int32                    `json:"-"`
+}
+
+func (m *Sanitizer) Reset()         { *m = Sanitizer{} }
+func (m *Sanitizer) String() string { return proto.CompactTextString(m) }
+func (*Sanitizer) ProtoMessage()    {}
+func (*Sanitizer) Descriptor() ([]byte, []int) {
+	return fileDescriptor_df45b505afdf471e, []int{13}
+}
+
+func (m *Sanitizer) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_Sanitizer.Unmarshal(m, b)
+}
+func (m *Sanitizer) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_Sanitizer.Marshal(b, m, deterministic)
+}
+func (m *Sanitizer) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Sanitizer.Merge(m, src)
+}
+func (m *Sanitizer) XXX_Size() int {
+	return xxx_messageInfo_Sanitizer.Size(m)
+}
+func (m *Sanitizer) XXX_DiscardUnknown() {
+	xxx_messageInfo_Sanitizer.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Sanitizer proto.InternalMessageInfo
+
+func (m *Sanitizer) GetAlias() Sanitizer_SanitizerAlias {
+	if m != nil {
+		return m.Alias
+	}
+	return Sanitizer_NONE
+}
+
+type DeviceFeature struct {
+	FeatureName string `protobuf:"bytes,1,opt,name=feature_name,json=featureName,proto3" json:"feature_name,omitempty"`
+	// Equivalent of android:glEsVersion or android:version in <uses-feature>.
+	FeatureVersion       int32    `protobuf:"varint,2,opt,name=feature_version,json=featureVersion,proto3" json:"feature_version,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *DeviceFeature) Reset()         { *m = DeviceFeature{} }
+func (m *DeviceFeature) String() string { return proto.CompactTextString(m) }
+func (*DeviceFeature) ProtoMessage()    {}
+func (*DeviceFeature) Descriptor() ([]byte, []int) {
+	return fileDescriptor_df45b505afdf471e, []int{14}
+}
+
+func (m *DeviceFeature) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_DeviceFeature.Unmarshal(m, b)
+}
+func (m *DeviceFeature) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_DeviceFeature.Marshal(b, m, deterministic)
+}
+func (m *DeviceFeature) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_DeviceFeature.Merge(m, src)
+}
+func (m *DeviceFeature) XXX_Size() int {
+	return xxx_messageInfo_DeviceFeature.Size(m)
+}
+func (m *DeviceFeature) XXX_DiscardUnknown() {
+	xxx_messageInfo_DeviceFeature.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_DeviceFeature proto.InternalMessageInfo
+
+func (m *DeviceFeature) GetFeatureName() string {
+	if m != nil {
+		return m.FeatureName
+	}
+	return ""
+}
+
+func (m *DeviceFeature) GetFeatureVersion() int32 {
+	if m != nil {
+		return m.FeatureVersion
+	}
+	return 0
+}
+
+// Targeting specific for directories under assets/.
+type AssetsDirectoryTargeting struct {
+	Abi                      *AbiTargeting                      `protobuf:"bytes,1,opt,name=abi,proto3" json:"abi,omitempty"`
+	GraphicsApi              *GraphicsApiTargeting              `protobuf:"bytes,2,opt,name=graphics_api,json=graphicsApi,proto3" json:"graphics_api,omitempty"`
+	TextureCompressionFormat *TextureCompressionFormatTargeting `protobuf:"bytes,3,opt,name=texture_compression_format,json=textureCompressionFormat,proto3" json:"texture_compression_format,omitempty"`
+	Language                 *LanguageTargeting                 `protobuf:"bytes,4,opt,name=language,proto3" json:"language,omitempty"`
+	XXX_NoUnkeyedLiteral     struct{}                           `json:"-"`
+	XXX_unrecognized         []byte                             `json:"-"`
+	XXX_sizecache            int32                              `json:"-"`
+}
+
+func (m *AssetsDirectoryTargeting) Reset()         { *m = AssetsDirectoryTargeting{} }
+func (m *AssetsDirectoryTargeting) String() string { return proto.CompactTextString(m) }
+func (*AssetsDirectoryTargeting) ProtoMessage()    {}
+func (*AssetsDirectoryTargeting) Descriptor() ([]byte, []int) {
+	return fileDescriptor_df45b505afdf471e, []int{15}
+}
+
+func (m *AssetsDirectoryTargeting) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_AssetsDirectoryTargeting.Unmarshal(m, b)
+}
+func (m *AssetsDirectoryTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_AssetsDirectoryTargeting.Marshal(b, m, deterministic)
+}
+func (m *AssetsDirectoryTargeting) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_AssetsDirectoryTargeting.Merge(m, src)
+}
+func (m *AssetsDirectoryTargeting) XXX_Size() int {
+	return xxx_messageInfo_AssetsDirectoryTargeting.Size(m)
+}
+func (m *AssetsDirectoryTargeting) XXX_DiscardUnknown() {
+	xxx_messageInfo_AssetsDirectoryTargeting.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_AssetsDirectoryTargeting proto.InternalMessageInfo
+
+func (m *AssetsDirectoryTargeting) GetAbi() *AbiTargeting {
+	if m != nil {
+		return m.Abi
+	}
+	return nil
+}
+
+func (m *AssetsDirectoryTargeting) GetGraphicsApi() *GraphicsApiTargeting {
+	if m != nil {
+		return m.GraphicsApi
+	}
+	return nil
+}
+
+func (m *AssetsDirectoryTargeting) GetTextureCompressionFormat() *TextureCompressionFormatTargeting {
+	if m != nil {
+		return m.TextureCompressionFormat
+	}
+	return nil
+}
+
+func (m *AssetsDirectoryTargeting) GetLanguage() *LanguageTargeting {
+	if m != nil {
+		return m.Language
+	}
+	return nil
+}
+
+// Targeting specific for directories under lib/.
+type NativeDirectoryTargeting struct {
+	Abi                      *Abi                      `protobuf:"bytes,1,opt,name=abi,proto3" json:"abi,omitempty"`
+	GraphicsApi              *GraphicsApi              `protobuf:"bytes,2,opt,name=graphics_api,json=graphicsApi,proto3" json:"graphics_api,omitempty"`
+	TextureCompressionFormat *TextureCompressionFormat `protobuf:"bytes,3,opt,name=texture_compression_format,json=textureCompressionFormat,proto3" json:"texture_compression_format,omitempty"`
+	Sanitizer                *Sanitizer                `protobuf:"bytes,4,opt,name=sanitizer,proto3" json:"sanitizer,omitempty"`
+	XXX_NoUnkeyedLiteral     struct{}                  `json:"-"`
+	XXX_unrecognized         []byte                    `json:"-"`
+	XXX_sizecache            int32                     `json:"-"`
+}
+
+func (m *NativeDirectoryTargeting) Reset()         { *m = NativeDirectoryTargeting{} }
+func (m *NativeDirectoryTargeting) String() string { return proto.CompactTextString(m) }
+func (*NativeDirectoryTargeting) ProtoMessage()    {}
+func (*NativeDirectoryTargeting) Descriptor() ([]byte, []int) {
+	return fileDescriptor_df45b505afdf471e, []int{16}
+}
+
+func (m *NativeDirectoryTargeting) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_NativeDirectoryTargeting.Unmarshal(m, b)
+}
+func (m *NativeDirectoryTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_NativeDirectoryTargeting.Marshal(b, m, deterministic)
+}
+func (m *NativeDirectoryTargeting) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_NativeDirectoryTargeting.Merge(m, src)
+}
+func (m *NativeDirectoryTargeting) XXX_Size() int {
+	return xxx_messageInfo_NativeDirectoryTargeting.Size(m)
+}
+func (m *NativeDirectoryTargeting) XXX_DiscardUnknown() {
+	xxx_messageInfo_NativeDirectoryTargeting.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_NativeDirectoryTargeting proto.InternalMessageInfo
+
+func (m *NativeDirectoryTargeting) GetAbi() *Abi {
+	if m != nil {
+		return m.Abi
+	}
+	return nil
+}
+
+func (m *NativeDirectoryTargeting) GetGraphicsApi() *GraphicsApi {
+	if m != nil {
+		return m.GraphicsApi
+	}
+	return nil
+}
+
+func (m *NativeDirectoryTargeting) GetTextureCompressionFormat() *TextureCompressionFormat {
+	if m != nil {
+		return m.TextureCompressionFormat
+	}
+	return nil
+}
+
+func (m *NativeDirectoryTargeting) GetSanitizer() *Sanitizer {
+	if m != nil {
+		return m.Sanitizer
+	}
+	return nil
+}
+
+// Targeting specific for image files under apex/.
+type ApexImageTargeting struct {
+	MultiAbi             *MultiAbiTargeting `protobuf:"bytes,1,opt,name=multi_abi,json=multiAbi,proto3" json:"multi_abi,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}           `json:"-"`
+	XXX_unrecognized     []byte             `json:"-"`
+	XXX_sizecache        int32              `json:"-"`
+}
+
+func (m *ApexImageTargeting) Reset()         { *m = ApexImageTargeting{} }
+func (m *ApexImageTargeting) String() string { return proto.CompactTextString(m) }
+func (*ApexImageTargeting) ProtoMessage()    {}
+func (*ApexImageTargeting) Descriptor() ([]byte, []int) {
+	return fileDescriptor_df45b505afdf471e, []int{17}
+}
+
+func (m *ApexImageTargeting) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_ApexImageTargeting.Unmarshal(m, b)
+}
+func (m *ApexImageTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_ApexImageTargeting.Marshal(b, m, deterministic)
+}
+func (m *ApexImageTargeting) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_ApexImageTargeting.Merge(m, src)
+}
+func (m *ApexImageTargeting) XXX_Size() int {
+	return xxx_messageInfo_ApexImageTargeting.Size(m)
+}
+func (m *ApexImageTargeting) XXX_DiscardUnknown() {
+	xxx_messageInfo_ApexImageTargeting.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ApexImageTargeting proto.InternalMessageInfo
+
+func (m *ApexImageTargeting) GetMultiAbi() *MultiAbiTargeting {
+	if m != nil {
+		return m.MultiAbi
+	}
+	return nil
+}
+
+type AbiTargeting struct {
+	Value []*Abi `protobuf:"bytes,1,rep,name=value,proto3" json:"value,omitempty"`
+	// Targeting of other sibling directories that were in the Bundle.
+	// For master splits this is targeting of other master splits.
+	Alternatives         []*Abi   `protobuf:"bytes,2,rep,name=alternatives,proto3" json:"alternatives,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *AbiTargeting) Reset()         { *m = AbiTargeting{} }
+func (m *AbiTargeting) String() string { return proto.CompactTextString(m) }
+func (*AbiTargeting) ProtoMessage()    {}
+func (*AbiTargeting) Descriptor() ([]byte, []int) {
+	return fileDescriptor_df45b505afdf471e, []int{18}
+}
+
+func (m *AbiTargeting) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_AbiTargeting.Unmarshal(m, b)
+}
+func (m *AbiTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_AbiTargeting.Marshal(b, m, deterministic)
+}
+func (m *AbiTargeting) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_AbiTargeting.Merge(m, src)
+}
+func (m *AbiTargeting) XXX_Size() int {
+	return xxx_messageInfo_AbiTargeting.Size(m)
+}
+func (m *AbiTargeting) XXX_DiscardUnknown() {
+	xxx_messageInfo_AbiTargeting.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_AbiTargeting proto.InternalMessageInfo
+
+func (m *AbiTargeting) GetValue() []*Abi {
+	if m != nil {
+		return m.Value
+	}
+	return nil
+}
+
+func (m *AbiTargeting) GetAlternatives() []*Abi {
+	if m != nil {
+		return m.Alternatives
+	}
+	return nil
+}
+
+type MultiAbiTargeting struct {
+	Value []*MultiAbi `protobuf:"bytes,1,rep,name=value,proto3" json:"value,omitempty"`
+	// Targeting of other sibling directories that were in the Bundle.
+	// For master splits this is targeting of other master splits.
+	Alternatives         []*MultiAbi `protobuf:"bytes,2,rep,name=alternatives,proto3" json:"alternatives,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}    `json:"-"`
+	XXX_unrecognized     []byte      `json:"-"`
+	XXX_sizecache        int32       `json:"-"`
+}
+
+func (m *MultiAbiTargeting) Reset()         { *m = MultiAbiTargeting{} }
+func (m *MultiAbiTargeting) String() string { return proto.CompactTextString(m) }
+func (*MultiAbiTargeting) ProtoMessage()    {}
+func (*MultiAbiTargeting) Descriptor() ([]byte, []int) {
+	return fileDescriptor_df45b505afdf471e, []int{19}
+}
+
+func (m *MultiAbiTargeting) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_MultiAbiTargeting.Unmarshal(m, b)
+}
+func (m *MultiAbiTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_MultiAbiTargeting.Marshal(b, m, deterministic)
+}
+func (m *MultiAbiTargeting) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_MultiAbiTargeting.Merge(m, src)
+}
+func (m *MultiAbiTargeting) XXX_Size() int {
+	return xxx_messageInfo_MultiAbiTargeting.Size(m)
+}
+func (m *MultiAbiTargeting) XXX_DiscardUnknown() {
+	xxx_messageInfo_MultiAbiTargeting.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_MultiAbiTargeting proto.InternalMessageInfo
+
+func (m *MultiAbiTargeting) GetValue() []*MultiAbi {
+	if m != nil {
+		return m.Value
+	}
+	return nil
+}
+
+func (m *MultiAbiTargeting) GetAlternatives() []*MultiAbi {
+	if m != nil {
+		return m.Alternatives
+	}
+	return nil
+}
+
+type ScreenDensityTargeting struct {
+	Value []*ScreenDensity `protobuf:"bytes,1,rep,name=value,proto3" json:"value,omitempty"`
+	// Targeting of other sibling directories that were in the Bundle.
+	// For master splits this is targeting of other master splits.
+	Alternatives         []*ScreenDensity `protobuf:"bytes,2,rep,name=alternatives,proto3" json:"alternatives,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}         `json:"-"`
+	XXX_unrecognized     []byte           `json:"-"`
+	XXX_sizecache        int32            `json:"-"`
+}
+
+func (m *ScreenDensityTargeting) Reset()         { *m = ScreenDensityTargeting{} }
+func (m *ScreenDensityTargeting) String() string { return proto.CompactTextString(m) }
+func (*ScreenDensityTargeting) ProtoMessage()    {}
+func (*ScreenDensityTargeting) Descriptor() ([]byte, []int) {
+	return fileDescriptor_df45b505afdf471e, []int{20}
+}
+
+func (m *ScreenDensityTargeting) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_ScreenDensityTargeting.Unmarshal(m, b)
+}
+func (m *ScreenDensityTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_ScreenDensityTargeting.Marshal(b, m, deterministic)
+}
+func (m *ScreenDensityTargeting) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_ScreenDensityTargeting.Merge(m, src)
+}
+func (m *ScreenDensityTargeting) XXX_Size() int {
+	return xxx_messageInfo_ScreenDensityTargeting.Size(m)
+}
+func (m *ScreenDensityTargeting) XXX_DiscardUnknown() {
+	xxx_messageInfo_ScreenDensityTargeting.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_ScreenDensityTargeting proto.InternalMessageInfo
+
+func (m *ScreenDensityTargeting) GetValue() []*ScreenDensity {
+	if m != nil {
+		return m.Value
+	}
+	return nil
+}
+
+func (m *ScreenDensityTargeting) GetAlternatives() []*ScreenDensity {
+	if m != nil {
+		return m.Alternatives
+	}
+	return nil
+}
+
+type LanguageTargeting struct {
+	// ISO-639: 2 or 3 letter language code.
+	Value []string `protobuf:"bytes,1,rep,name=value,proto3" json:"value,omitempty"`
+	// Targeting of other sibling directories that were in the Bundle.
+	// For master splits this is targeting of other master splits.
+	Alternatives         []string `protobuf:"bytes,2,rep,name=alternatives,proto3" json:"alternatives,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *LanguageTargeting) Reset()         { *m = LanguageTargeting{} }
+func (m *LanguageTargeting) String() string { return proto.CompactTextString(m) }
+func (*LanguageTargeting) ProtoMessage()    {}
+func (*LanguageTargeting) Descriptor() ([]byte, []int) {
+	return fileDescriptor_df45b505afdf471e, []int{21}
+}
+
+func (m *LanguageTargeting) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_LanguageTargeting.Unmarshal(m, b)
+}
+func (m *LanguageTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_LanguageTargeting.Marshal(b, m, deterministic)
+}
+func (m *LanguageTargeting) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_LanguageTargeting.Merge(m, src)
+}
+func (m *LanguageTargeting) XXX_Size() int {
+	return xxx_messageInfo_LanguageTargeting.Size(m)
+}
+func (m *LanguageTargeting) XXX_DiscardUnknown() {
+	xxx_messageInfo_LanguageTargeting.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_LanguageTargeting proto.InternalMessageInfo
+
+func (m *LanguageTargeting) GetValue() []string {
+	if m != nil {
+		return m.Value
+	}
+	return nil
+}
+
+func (m *LanguageTargeting) GetAlternatives() []string {
+	if m != nil {
+		return m.Alternatives
+	}
+	return nil
+}
+
+type GraphicsApiTargeting struct {
+	Value []*GraphicsApi `protobuf:"bytes,1,rep,name=value,proto3" json:"value,omitempty"`
+	// Targeting of other sibling directories that were in the Bundle.
+	// For master splits this is targeting of other master splits.
+	Alternatives         []*GraphicsApi `protobuf:"bytes,2,rep,name=alternatives,proto3" json:"alternatives,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}       `json:"-"`
+	XXX_unrecognized     []byte         `json:"-"`
+	XXX_sizecache        int32          `json:"-"`
+}
+
+func (m *GraphicsApiTargeting) Reset()         { *m = GraphicsApiTargeting{} }
+func (m *GraphicsApiTargeting) String() string { return proto.CompactTextString(m) }
+func (*GraphicsApiTargeting) ProtoMessage()    {}
+func (*GraphicsApiTargeting) Descriptor() ([]byte, []int) {
+	return fileDescriptor_df45b505afdf471e, []int{22}
+}
+
+func (m *GraphicsApiTargeting) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_GraphicsApiTargeting.Unmarshal(m, b)
+}
+func (m *GraphicsApiTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_GraphicsApiTargeting.Marshal(b, m, deterministic)
+}
+func (m *GraphicsApiTargeting) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_GraphicsApiTargeting.Merge(m, src)
+}
+func (m *GraphicsApiTargeting) XXX_Size() int {
+	return xxx_messageInfo_GraphicsApiTargeting.Size(m)
+}
+func (m *GraphicsApiTargeting) XXX_DiscardUnknown() {
+	xxx_messageInfo_GraphicsApiTargeting.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_GraphicsApiTargeting proto.InternalMessageInfo
+
+func (m *GraphicsApiTargeting) GetValue() []*GraphicsApi {
+	if m != nil {
+		return m.Value
+	}
+	return nil
+}
+
+func (m *GraphicsApiTargeting) GetAlternatives() []*GraphicsApi {
+	if m != nil {
+		return m.Alternatives
+	}
+	return nil
+}
+
+type SdkVersionTargeting struct {
+	Value []*SdkVersion `protobuf:"bytes,1,rep,name=value,proto3" json:"value,omitempty"`
+	// Targeting of other sibling directories that were in the Bundle.
+	// For master splits this is targeting of other master splits.
+	Alternatives         []*SdkVersion `protobuf:"bytes,2,rep,name=alternatives,proto3" json:"alternatives,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}      `json:"-"`
+	XXX_unrecognized     []byte        `json:"-"`
+	XXX_sizecache        int32         `json:"-"`
+}
+
+func (m *SdkVersionTargeting) Reset()         { *m = SdkVersionTargeting{} }
+func (m *SdkVersionTargeting) String() string { return proto.CompactTextString(m) }
+func (*SdkVersionTargeting) ProtoMessage()    {}
+func (*SdkVersionTargeting) Descriptor() ([]byte, []int) {
+	return fileDescriptor_df45b505afdf471e, []int{23}
+}
+
+func (m *SdkVersionTargeting) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_SdkVersionTargeting.Unmarshal(m, b)
+}
+func (m *SdkVersionTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_SdkVersionTargeting.Marshal(b, m, deterministic)
+}
+func (m *SdkVersionTargeting) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_SdkVersionTargeting.Merge(m, src)
+}
+func (m *SdkVersionTargeting) XXX_Size() int {
+	return xxx_messageInfo_SdkVersionTargeting.Size(m)
+}
+func (m *SdkVersionTargeting) XXX_DiscardUnknown() {
+	xxx_messageInfo_SdkVersionTargeting.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_SdkVersionTargeting proto.InternalMessageInfo
+
+func (m *SdkVersionTargeting) GetValue() []*SdkVersion {
+	if m != nil {
+		return m.Value
+	}
+	return nil
+}
+
+func (m *SdkVersionTargeting) GetAlternatives() []*SdkVersion {
+	if m != nil {
+		return m.Alternatives
+	}
+	return nil
+}
+
+type TextureCompressionFormatTargeting struct {
+	Value []*TextureCompressionFormat `protobuf:"bytes,1,rep,name=value,proto3" json:"value,omitempty"`
+	// Targeting of other sibling directories that were in the Bundle.
+	// For master splits this is targeting of other master splits.
+	Alternatives         []*TextureCompressionFormat `protobuf:"bytes,2,rep,name=alternatives,proto3" json:"alternatives,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}                    `json:"-"`
+	XXX_unrecognized     []byte                      `json:"-"`
+	XXX_sizecache        int32                       `json:"-"`
+}
+
+func (m *TextureCompressionFormatTargeting) Reset()         { *m = TextureCompressionFormatTargeting{} }
+func (m *TextureCompressionFormatTargeting) String() string { return proto.CompactTextString(m) }
+func (*TextureCompressionFormatTargeting) ProtoMessage()    {}
+func (*TextureCompressionFormatTargeting) Descriptor() ([]byte, []int) {
+	return fileDescriptor_df45b505afdf471e, []int{24}
+}
+
+func (m *TextureCompressionFormatTargeting) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_TextureCompressionFormatTargeting.Unmarshal(m, b)
+}
+func (m *TextureCompressionFormatTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_TextureCompressionFormatTargeting.Marshal(b, m, deterministic)
+}
+func (m *TextureCompressionFormatTargeting) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_TextureCompressionFormatTargeting.Merge(m, src)
+}
+func (m *TextureCompressionFormatTargeting) XXX_Size() int {
+	return xxx_messageInfo_TextureCompressionFormatTargeting.Size(m)
+}
+func (m *TextureCompressionFormatTargeting) XXX_DiscardUnknown() {
+	xxx_messageInfo_TextureCompressionFormatTargeting.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_TextureCompressionFormatTargeting proto.InternalMessageInfo
+
+func (m *TextureCompressionFormatTargeting) GetValue() []*TextureCompressionFormat {
+	if m != nil {
+		return m.Value
+	}
+	return nil
+}
+
+func (m *TextureCompressionFormatTargeting) GetAlternatives() []*TextureCompressionFormat {
+	if m != nil {
+		return m.Alternatives
+	}
+	return nil
+}
+
+type SanitizerTargeting struct {
+	Value                []*Sanitizer `protobuf:"bytes,1,rep,name=value,proto3" json:"value,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}     `json:"-"`
+	XXX_unrecognized     []byte       `json:"-"`
+	XXX_sizecache        int32        `json:"-"`
+}
+
+func (m *SanitizerTargeting) Reset()         { *m = SanitizerTargeting{} }
+func (m *SanitizerTargeting) String() string { return proto.CompactTextString(m) }
+func (*SanitizerTargeting) ProtoMessage()    {}
+func (*SanitizerTargeting) Descriptor() ([]byte, []int) {
+	return fileDescriptor_df45b505afdf471e, []int{25}
+}
+
+func (m *SanitizerTargeting) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_SanitizerTargeting.Unmarshal(m, b)
+}
+func (m *SanitizerTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_SanitizerTargeting.Marshal(b, m, deterministic)
+}
+func (m *SanitizerTargeting) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_SanitizerTargeting.Merge(m, src)
+}
+func (m *SanitizerTargeting) XXX_Size() int {
+	return xxx_messageInfo_SanitizerTargeting.Size(m)
+}
+func (m *SanitizerTargeting) XXX_DiscardUnknown() {
+	xxx_messageInfo_SanitizerTargeting.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_SanitizerTargeting proto.InternalMessageInfo
+
+func (m *SanitizerTargeting) GetValue() []*Sanitizer {
+	if m != nil {
+		return m.Value
+	}
+	return nil
+}
+
+// Since other atom targeting messages have the "OR" semantic on values
+// the DeviceFeatureTargeting represents only one device feature to retain
+// that convention.
+type DeviceFeatureTargeting struct {
+	RequiredFeature      *DeviceFeature `protobuf:"bytes,1,opt,name=required_feature,json=requiredFeature,proto3" json:"required_feature,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}       `json:"-"`
+	XXX_unrecognized     []byte         `json:"-"`
+	XXX_sizecache        int32          `json:"-"`
+}
+
+func (m *DeviceFeatureTargeting) Reset()         { *m = DeviceFeatureTargeting{} }
+func (m *DeviceFeatureTargeting) String() string { return proto.CompactTextString(m) }
+func (*DeviceFeatureTargeting) ProtoMessage()    {}
+func (*DeviceFeatureTargeting) Descriptor() ([]byte, []int) {
+	return fileDescriptor_df45b505afdf471e, []int{26}
+}
+
+func (m *DeviceFeatureTargeting) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_DeviceFeatureTargeting.Unmarshal(m, b)
+}
+func (m *DeviceFeatureTargeting) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_DeviceFeatureTargeting.Marshal(b, m, deterministic)
+}
+func (m *DeviceFeatureTargeting) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_DeviceFeatureTargeting.Merge(m, src)
+}
+func (m *DeviceFeatureTargeting) XXX_Size() int {
+	return xxx_messageInfo_DeviceFeatureTargeting.Size(m)
+}
+func (m *DeviceFeatureTargeting) XXX_DiscardUnknown() {
+	xxx_messageInfo_DeviceFeatureTargeting.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_DeviceFeatureTargeting proto.InternalMessageInfo
+
+func (m *DeviceFeatureTargeting) GetRequiredFeature() *DeviceFeature {
+	if m != nil {
+		return m.RequiredFeature
+	}
+	return nil
+}
+
+func init() {
+	proto.RegisterEnum("android.bundle.ScreenDensity_DensityAlias", ScreenDensity_DensityAlias_name, ScreenDensity_DensityAlias_value)
+	proto.RegisterEnum("android.bundle.TextureCompressionFormat_TextureCompressionFormatAlias", TextureCompressionFormat_TextureCompressionFormatAlias_name, TextureCompressionFormat_TextureCompressionFormatAlias_value)
+	proto.RegisterEnum("android.bundle.Abi_AbiAlias", Abi_AbiAlias_name, Abi_AbiAlias_value)
+	proto.RegisterEnum("android.bundle.Sanitizer_SanitizerAlias", Sanitizer_SanitizerAlias_name, Sanitizer_SanitizerAlias_value)
+	proto.RegisterType((*VariantTargeting)(nil), "android.bundle.VariantTargeting")
+	proto.RegisterType((*ApkTargeting)(nil), "android.bundle.ApkTargeting")
+	proto.RegisterType((*ModuleTargeting)(nil), "android.bundle.ModuleTargeting")
+	proto.RegisterType((*UserCountriesTargeting)(nil), "android.bundle.UserCountriesTargeting")
+	proto.RegisterType((*ScreenDensity)(nil), "android.bundle.ScreenDensity")
+	proto.RegisterType((*Int32Value)(nil), "android.bundle.Int32Value")
+	proto.RegisterType((*SdkVersion)(nil), "android.bundle.SdkVersion")
+	proto.RegisterType((*GraphicsApi)(nil), "android.bundle.GraphicsApi")
+	proto.RegisterType((*VulkanVersion)(nil), "android.bundle.VulkanVersion")
+	proto.RegisterType((*OpenGlVersion)(nil), "android.bundle.OpenGlVersion")
+	proto.RegisterType((*TextureCompressionFormat)(nil), "android.bundle.TextureCompressionFormat")
+	proto.RegisterType((*Abi)(nil), "android.bundle.Abi")
+	proto.RegisterType((*MultiAbi)(nil), "android.bundle.MultiAbi")
+	proto.RegisterType((*Sanitizer)(nil), "android.bundle.Sanitizer")
+	proto.RegisterType((*DeviceFeature)(nil), "android.bundle.DeviceFeature")
+	proto.RegisterType((*AssetsDirectoryTargeting)(nil), "android.bundle.AssetsDirectoryTargeting")
+	proto.RegisterType((*NativeDirectoryTargeting)(nil), "android.bundle.NativeDirectoryTargeting")
+	proto.RegisterType((*ApexImageTargeting)(nil), "android.bundle.ApexImageTargeting")
+	proto.RegisterType((*AbiTargeting)(nil), "android.bundle.AbiTargeting")
+	proto.RegisterType((*MultiAbiTargeting)(nil), "android.bundle.MultiAbiTargeting")
+	proto.RegisterType((*ScreenDensityTargeting)(nil), "android.bundle.ScreenDensityTargeting")
+	proto.RegisterType((*LanguageTargeting)(nil), "android.bundle.LanguageTargeting")
+	proto.RegisterType((*GraphicsApiTargeting)(nil), "android.bundle.GraphicsApiTargeting")
+	proto.RegisterType((*SdkVersionTargeting)(nil), "android.bundle.SdkVersionTargeting")
+	proto.RegisterType((*TextureCompressionFormatTargeting)(nil), "android.bundle.TextureCompressionFormatTargeting")
+	proto.RegisterType((*SanitizerTargeting)(nil), "android.bundle.SanitizerTargeting")
+	proto.RegisterType((*DeviceFeatureTargeting)(nil), "android.bundle.DeviceFeatureTargeting")
+}
+
+func init() {
+	proto.RegisterFile("targeting.proto", fileDescriptor_df45b505afdf471e)
+}
+
+var fileDescriptor_df45b505afdf471e = []byte{
+	// 1504 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x58, 0x5b, 0x6f, 0xe3, 0xc4,
+	0x17, 0xaf, 0x93, 0xa6, 0x4d, 0x4e, 0x92, 0xd6, 0x3b, 0xe9, 0xbf, 0xff, 0xb0, 0xec, 0x4a, 0x5b,
+	0xef, 0x85, 0xee, 0x0a, 0x05, 0xda, 0xae, 0xba, 0x15, 0x97, 0x22, 0xd7, 0x71, 0x9b, 0x48, 0x4d,
+	0x9a, 0x75, 0xdc, 0x6c, 0x59, 0x90, 0xbc, 0x4e, 0x3c, 0x0d, 0xa6, 0x89, 0x1d, 0x6c, 0xa7, 0xda,
+	0xe5, 0x05, 0x81, 0x90, 0x90, 0x78, 0xe2, 0x8d, 0x77, 0x3e, 0x00, 0x12, 0x4f, 0x08, 0x89, 0x07,
+	0x24, 0x3e, 0x0c, 0x7c, 0x0c, 0x34, 0xbe, 0x24, 0x9e, 0xc4, 0x4e, 0xb3, 0x2c, 0xf0, 0x50, 0xe5,
+	0xcc, 0xf1, 0x39, 0xbf, 0x73, 0x99, 0x73, 0x66, 0xce, 0x14, 0x56, 0x1d, 0xd5, 0xea, 0x62, 0x47,
+	0x37, 0xba, 0xa5, 0x81, 0x65, 0x3a, 0x26, 0x5a, 0x51, 0x0d, 0xcd, 0x32, 0x75, 0xad, 0xd4, 0x1e,
+	0x1a, 0x5a, 0x0f, 0x73, 0x7f, 0x26, 0x81, 0x6d, 0xa9, 0x96, 0xae, 0x1a, 0x8e, 0x1c, 0x88, 0xa2,
+	0x27, 0xf0, 0x3f, 0x5b, 0xbb, 0x50, 0x2e, 0xb1, 0x65, 0xeb, 0xa6, 0xa1, 0x8c, 0x30, 0x8a, 0xcc,
+	0x2d, 0x66, 0x33, 0xbb, 0x7d, 0xbb, 0x44, 0x83, 0x94, 0x9a, 0xda, 0x45, 0xcb, 0x93, 0x1d, 0x61,
+	0x48, 0x05, 0x7b, 0x9a, 0x89, 0x78, 0xc8, 0xab, 0x6d, 0x3d, 0x04, 0x98, 0x70, 0x01, 0x6f, 0x4c,
+	0x02, 0xf2, 0x6d, 0x7d, 0x8c, 0x94, 0x53, 0x43, 0x2b, 0xf4, 0x0c, 0x8a, 0x76, 0xc7, 0xc2, 0xd8,
+	0x50, 0x34, 0x6c, 0xd8, 0xba, 0xf3, 0x22, 0x84, 0x96, 0x74, 0xd1, 0xee, 0x4d, 0xb9, 0xe7, 0xca,
+	0x97, 0x3d, 0xf1, 0x31, 0xee, 0xba, 0x1d, 0xc9, 0x47, 0x8f, 0xa1, 0xd0, 0x1f, 0xf6, 0x1c, 0x5d,
+	0xa1, 0x5d, 0x5d, 0x74, 0xc1, 0x37, 0x26, 0xc1, 0x6b, 0x44, 0x94, 0xf2, 0xf7, 0x5a, 0x7f, 0x92,
+	0x85, 0xbe, 0x62, 0xe0, 0x8e, 0x83, 0x9f, 0x3b, 0x43, 0x0b, 0x2b, 0x1d, 0xb3, 0x3f, 0xb0, 0xb0,
+	0xed, 0x66, 0xf6, 0xdc, 0xb4, 0xfa, 0xaa, 0x13, 0x32, 0x92, 0x72, 0x8d, 0x6c, 0x4d, 0x1a, 0x91,
+	0x3d, 0x5d, 0x61, 0xac, 0x7a, 0xe8, 0x6a, 0x8e, 0x8d, 0x6e, 0x38, 0x57, 0x89, 0x70, 0x7f, 0xa4,
+	0x20, 0xc7, 0x0f, 0x2e, 0x66, 0xec, 0x06, 0xf3, 0xd2, 0xbb, 0xf1, 0x14, 0xd6, 0xbb, 0x96, 0x3a,
+	0xf8, 0x44, 0xef, 0xd8, 0x8a, 0x3a, 0x98, 0xde, 0xd9, 0x3b, 0x93, 0x58, 0x47, 0xbe, 0x34, 0x3f,
+	0x08, 0x61, 0xae, 0x75, 0x23, 0xb8, 0xa8, 0x01, 0xa8, 0xa7, 0x1a, 0xdd, 0xa1, 0xda, 0xc5, 0x53,
+	0x7b, 0x3c, 0xb5, 0x0d, 0xc7, 0xbe, 0x64, 0x68, 0x1b, 0x7a, 0x93, 0xac, 0x99, 0xb5, 0xb3, 0xf8,
+	0x8f, 0xd4, 0x4e, 0x6c, 0xe7, 0xa4, 0x5e, 0xb1, 0x73, 0xe6, 0xae, 0xa0, 0xa5, 0x7f, 0xaf, 0x82,
+	0xe2, 0x3a, 0x63, 0xf9, 0x15, 0x3a, 0xa3, 0x09, 0x05, 0x5b, 0x35, 0x74, 0x47, 0xff, 0x1c, 0x5b,
+	0x21, 0xc8, 0xb4, 0x0b, 0xc9, 0x4d, 0xa5, 0x2b, 0x10, 0x1d, 0x63, 0x22, 0x7b, 0x8a, 0xc7, 0xfd,
+	0x98, 0x80, 0xd5, 0x9a, 0xa9, 0x0d, 0x7b, 0xf8, 0x3f, 0x38, 0xd3, 0x9e, 0x41, 0x51, 0xc3, 0x97,
+	0x7a, 0x07, 0x2b, 0xe7, 0x58, 0x75, 0xf7, 0x27, 0xdc, 0x04, 0xc9, 0xa8, 0xa2, 0x2a, 0xbb, 0xf2,
+	0x87, 0x9e, 0x78, 0xa8, 0xa8, 0xb4, 0x48, 0x3e, 0xb1, 0x30, 0xb4, 0xb1, 0xa5, 0x74, 0xcc, 0xa1,
+	0xe1, 0x58, 0x3a, 0xb6, 0xaf, 0x3e, 0xf2, 0x4e, 0x6d, 0x6c, 0x09, 0x81, 0x78, 0xc8, 0xc2, 0x30,
+	0x92, 0xcf, 0x3d, 0x81, 0xf5, 0x68, 0x0d, 0x74, 0x1b, 0xf2, 0x9e, 0xd9, 0x17, 0x4a, 0xc7, 0xd4,
+	0xb0, 0x5d, 0x64, 0x6e, 0x25, 0x37, 0x33, 0x52, 0xce, 0x67, 0x0a, 0x84, 0x87, 0x8a, 0xb0, 0x8c,
+	0x9f, 0x77, 0x7a, 0x43, 0x0d, 0xbb, 0x6d, 0x9f, 0x96, 0x82, 0x25, 0xf7, 0x7d, 0x02, 0xf2, 0x54,
+	0x0b, 0xa1, 0xc7, 0x90, 0x0f, 0x9a, 0x4f, 0xed, 0xe9, 0xaa, 0xed, 0xe6, 0x7f, 0x65, 0xfb, 0xc1,
+	0xcc, 0xc6, 0x2b, 0xf9, 0xbf, 0x3c, 0xd1, 0xa8, 0x2c, 0x48, 0x39, 0x2d, 0xb4, 0x46, 0x1b, 0x90,
+	0x0d, 0x20, 0xb5, 0x81, 0xee, 0xba, 0x90, 0xaa, 0x2c, 0x48, 0xe0, 0x33, 0xcb, 0x03, 0x9d, 0xfb,
+	0x02, 0x72, 0x61, 0x08, 0xf4, 0x7f, 0x28, 0x94, 0xc5, 0x7a, 0xb3, 0x2a, 0x7f, 0xa8, 0x9c, 0xd6,
+	0x9b, 0x0d, 0x51, 0xa8, 0x1e, 0x56, 0xc5, 0x32, 0xbb, 0x80, 0x32, 0x90, 0xaa, 0x9f, 0x94, 0x1b,
+	0x55, 0x96, 0x41, 0x69, 0x58, 0x3c, 0x26, 0x54, 0x82, 0x50, 0x35, 0x42, 0x25, 0xc9, 0x67, 0xb9,
+	0x45, 0xc8, 0x45, 0xc2, 0xac, 0x10, 0x2a, 0x45, 0x98, 0x67, 0x2e, 0xb9, 0x84, 0x00, 0x96, 0xce,
+	0x3c, 0x7a, 0x19, 0x65, 0x61, 0xf9, 0xcc, 0x5f, 0xa4, 0x0f, 0x56, 0xc7, 0x61, 0x9b, 0x06, 0x36,
+	0xcf, 0x39, 0x0e, 0xa0, 0x6a, 0x38, 0x3b, 0xdb, 0x2d, 0xb5, 0x37, 0xc4, 0x68, 0x0d, 0x52, 0x97,
+	0x84, 0x70, 0xb3, 0x91, 0x92, 0xbc, 0x05, 0xf7, 0x0e, 0xc0, 0xb8, 0x0c, 0xd1, 0x9b, 0x90, 0xec,
+	0xeb, 0x86, 0x5f, 0xaf, 0xd7, 0x27, 0xf3, 0x35, 0x06, 0x93, 0x88, 0x18, 0xf7, 0x0b, 0x03, 0xd9,
+	0xd0, 0x61, 0x8b, 0xea, 0x50, 0xe8, 0xeb, 0x86, 0x62, 0x0e, 0xb0, 0xa1, 0x74, 0x7b, 0x41, 0x1f,
+	0xf8, 0x68, 0x37, 0x27, 0xd1, 0x4e, 0x06, 0xd8, 0x38, 0xea, 0xf9, 0x96, 0x2b, 0x0b, 0x12, 0xdb,
+	0xd7, 0x0d, 0x8a, 0x87, 0x6a, 0x80, 0x08, 0xde, 0xe5, 0xb0, 0x77, 0xa1, 0x1a, 0x23, 0xb8, 0x44,
+	0x34, 0x5c, 0xcb, 0x95, 0xa2, 0xe1, 0x28, 0xde, 0x41, 0x16, 0x32, 0xe4, 0xfe, 0xf0, 0x72, 0xf3,
+	0x2e, 0xe4, 0xa9, 0xaf, 0x24, 0x3d, 0x7d, 0xf5, 0x53, 0xd3, 0x0a, 0xd2, 0xe3, 0x2e, 0x5c, 0xae,
+	0x6e, 0x98, 0x96, 0xb7, 0xe3, 0x92, 0xb7, 0x20, 0xca, 0xb4, 0xa7, 0x2f, 0xa3, 0xfc, 0x73, 0x02,
+	0x8a, 0x71, 0x47, 0x25, 0xfa, 0x18, 0x52, 0xe1, 0x92, 0x3d, 0x9c, 0xf7, 0x8c, 0x8d, 0xfd, 0xe0,
+	0xd6, 0xa2, 0xe4, 0x81, 0x72, 0xbf, 0x32, 0x70, 0x73, 0xa6, 0x20, 0x7a, 0x00, 0xf7, 0x42, 0xc5,
+	0xaa, 0xc8, 0xe2, 0x99, 0x7c, 0x2a, 0x89, 0x8a, 0x70, 0x52, 0x6b, 0x48, 0x62, 0xb3, 0x59, 0x3d,
+	0xa9, 0x2b, 0x87, 0x27, 0x52, 0x8d, 0x97, 0xd9, 0x05, 0x94, 0x87, 0x8c, 0x28, 0x0b, 0x5b, 0x8a,
+	0x74, 0x74, 0xb0, 0xc7, 0x32, 0x28, 0x07, 0xe9, 0x06, 0x7f, 0x2c, 0xca, 0xb2, 0x58, 0x66, 0x13,
+	0x64, 0x25, 0x57, 0x24, 0x51, 0x54, 0xca, 0x02, 0x9b, 0x44, 0xcb, 0x90, 0xe4, 0x65, 0xc1, 0xab,
+	0xe8, 0x63, 0x42, 0xa5, 0x08, 0x55, 0x3e, 0x93, 0xb7, 0xd8, 0x25, 0x42, 0x35, 0x77, 0x64, 0x81,
+	0x5d, 0x26, 0x55, 0xde, 0x68, 0x49, 0xb2, 0xc0, 0xa6, 0x09, 0x93, 0x6f, 0xca, 0x02, 0x9b, 0x21,
+	0x94, 0x28, 0x0b, 0xdb, 0x2c, 0x70, 0xbf, 0x31, 0x90, 0xe4, 0xdb, 0x3a, 0xda, 0xa6, 0x93, 0x14,
+	0x35, 0x4c, 0x90, 0x3f, 0x2a, 0xf4, 0xaf, 0x19, 0x48, 0x07, 0x3c, 0x74, 0x0b, 0x6e, 0x84, 0xa3,
+	0x14, 0x1a, 0xa7, 0x0a, 0x2f, 0x09, 0x95, 0xaa, 0x2c, 0x0a, 0x24, 0x5c, 0x76, 0x81, 0x34, 0x16,
+	0x2f, 0xd5, 0x44, 0xfe, 0x80, 0x74, 0xe9, 0x2a, 0x64, 0xfd, 0x85, 0xd2, 0x7a, 0xc4, 0xb3, 0x09,
+	0x12, 0x39, 0x2f, 0xd5, 0x76, 0x1f, 0x2a, 0xad, 0x3d, 0xde, 0x8b, 0xee, 0x6c, 0x6f, 0x97, 0x5d,
+	0x74, 0x5b, 0x73, 0x6f, 0x57, 0xd9, 0x7d, 0xe8, 0xc5, 0x57, 0xab, 0x36, 0x9a, 0x5e, 0xc3, 0x12,
+	0x6a, 0xf7, 0x21, 0xbb, 0xcc, 0x6d, 0x41, 0x3a, 0xb8, 0xb3, 0xd0, 0x5d, 0x48, 0xaa, 0x6d, 0xdd,
+	0x3d, 0xed, 0xb2, 0xdb, 0x85, 0x88, 0x20, 0x24, 0xf2, 0x9d, 0xbb, 0x84, 0xcc, 0xe8, 0x4e, 0x42,
+	0xfb, 0x74, 0xe8, 0x9b, 0xb1, 0xb7, 0xd7, 0x98, 0xa2, 0xd2, 0x70, 0x1f, 0x56, 0xe8, 0x0f, 0xc4,
+	0xcf, 0xfa, 0x49, 0x5d, 0xf4, 0xf6, 0xb3, 0xf2, 0x84, 0x2f, 0x97, 0xc9, 0x46, 0xb3, 0x0c, 0xf7,
+	0x11, 0xe4, 0xa9, 0x4b, 0x04, 0x6d, 0x40, 0x2e, 0xb8, 0x7e, 0x0c, 0xb5, 0xef, 0x9d, 0x23, 0x19,
+	0x29, 0xeb, 0xf3, 0xea, 0x6a, 0x1f, 0xa3, 0x37, 0x60, 0x35, 0x10, 0x09, 0xb7, 0x6b, 0x4a, 0x5a,
+	0xf1, 0xd9, 0x7e, 0xc3, 0x70, 0xbf, 0x27, 0xa0, 0xc8, 0xdb, 0x36, 0x76, 0xec, 0xb2, 0x6e, 0xe1,
+	0x8e, 0x63, 0x5a, 0xa1, 0x09, 0xa7, 0x14, 0x24, 0xe6, 0xea, 0x51, 0x91, 0x08, 0xa2, 0x23, 0xc8,
+	0x85, 0x27, 0xc4, 0x97, 0x9a, 0x0b, 0xb3, 0xa1, 0xb9, 0x10, 0x99, 0x70, 0x3d, 0x7e, 0x00, 0xf2,
+	0xef, 0xc1, 0xbf, 0x31, 0xf6, 0x14, 0xe3, 0xc6, 0x1e, 0xf4, 0x3e, 0xa4, 0x83, 0x11, 0x32, 0x6e,
+	0xf8, 0x9f, 0x9e, 0x3a, 0x47, 0x2a, 0xdc, 0x0f, 0x09, 0x28, 0xd6, 0x55, 0x47, 0xbf, 0xc4, 0x11,
+	0x59, 0xbc, 0x1b, 0xce, 0x62, 0x6c, 0x79, 0xa1, 0xfd, 0xc8, 0xe4, 0xbd, 0x3e, 0x23, 0x79, 0x74,
+	0xce, 0xce, 0xe7, 0xc8, 0xd9, 0xe6, 0xbc, 0x39, 0x9b, 0x91, 0xaa, 0x47, 0x90, 0x19, 0x8d, 0x61,
+	0x7e, 0xae, 0x5e, 0x8b, 0xad, 0x7e, 0x69, 0x2c, 0xcb, 0xc9, 0x80, 0xf8, 0x01, 0x7e, 0x5e, 0xed,
+	0x53, 0x73, 0xfa, 0x3e, 0x64, 0x46, 0x73, 0xa6, 0x9f, 0xa3, 0x39, 0xa6, 0xcb, 0x74, 0x30, 0x5d,
+	0x72, 0x16, 0xe4, 0xa8, 0x21, 0xf3, 0xfe, 0xf8, 0x76, 0x8d, 0x6d, 0x67, 0x4f, 0x02, 0x3d, 0x82,
+	0x9c, 0xda, 0x73, 0xb0, 0x65, 0xb8, 0x3b, 0x67, 0xfb, 0x13, 0x5c, 0xa4, 0x06, 0x25, 0xc8, 0x7d,
+	0xc9, 0xc0, 0xb5, 0x29, 0x9f, 0x50, 0x89, 0xb6, 0x5c, 0x8c, 0x8b, 0x22, 0x30, 0xff, 0x5e, 0xa4,
+	0xf9, 0x78, 0x35, 0xda, 0x87, 0xef, 0x18, 0x58, 0x8f, 0x7e, 0xb0, 0xa0, 0x1d, 0xda, 0x91, 0x9b,
+	0x33, 0xc7, 0xad, 0xc0, 0x1b, 0x3e, 0xd2, 0x9b, 0x2b, 0x74, 0x69, 0x97, 0x6a, 0x70, 0x6d, 0xaa,
+	0x49, 0xc2, 0xd3, 0x0e, 0x19, 0x26, 0x7d, 0x6b, 0x5c, 0x84, 0xb5, 0xcc, 0x04, 0xdc, 0xb7, 0x0c,
+	0xac, 0x45, 0x1d, 0x15, 0x68, 0x8b, 0x8e, 0x6f, 0x66, 0x8b, 0xf8, 0xf6, 0x3e, 0x88, 0x8c, 0x6e,
+	0xa6, 0x26, 0xed, 0xcc, 0x37, 0x0c, 0x14, 0x22, 0x9e, 0x09, 0xe8, 0x6d, 0xda, 0x97, 0xeb, 0xf1,
+	0x4f, 0x8b, 0xc0, 0x95, 0xfd, 0x48, 0x57, 0x66, 0x29, 0xd2, 0x9e, 0xfc, 0xc4, 0xc0, 0xc6, 0x95,
+	0x47, 0x1d, 0xb9, 0x9f, 0xc2, 0x7e, 0xcd, 0xdf, 0xf8, 0xbe, 0x97, 0xc7, 0x91, 0x5e, 0xce, 0x0f,
+	0x43, 0xfb, 0x2c, 0x02, 0x9a, 0x7e, 0xce, 0xa1, 0xb7, 0x68, 0x1f, 0x67, 0x9c, 0x22, 0xfe, 0x8c,
+	0xdc, 0x86, 0xf5, 0xe8, 0xe7, 0x14, 0xaa, 0x00, 0x6b, 0xe1, 0xcf, 0x86, 0xba, 0x85, 0xb5, 0xe0,
+	0x69, 0x16, 0x37, 0xee, 0x52, 0x08, 0xd2, 0x6a, 0xa0, 0xe6, 0x33, 0x0e, 0x1e, 0x00, 0xea, 0x98,
+	0xfd, 0x09, 0xa5, 0xa7, 0x6b, 0xfe, 0x5a, 0xf1, 0xd6, 0x8a, 0xfb, 0x0f, 0xb6, 0xf6, 0x92, 0xfb,
+	0xb3, 0xf3, 0x57, 0x00, 0x00, 0x00, 0xff, 0xff, 0x47, 0xe0, 0x85, 0xa8, 0x7a, 0x13, 0x00, 0x00,
+}
diff --git a/cmd/extract_apks/bundle_proto/targeting.proto b/cmd/extract_apks/bundle_proto/targeting.proto
new file mode 100644
index 0000000..cdc910b
--- /dev/null
+++ b/cmd/extract_apks/bundle_proto/targeting.proto
@@ -0,0 +1,232 @@
+// Messages describing APK Set's table of contents (toc.pb entry).
+// Please be advised that the ultimate source is at
+// https://github.com/google/bundletool/tree/master/src/main/proto
+// so you have been warned.
+syntax = "proto3";
+
+package android.bundle;
+
+option go_package = "android_bundle_proto";
+option java_package = "com.android.bundle";
+
+// Targeting on the level of variants.
+message VariantTargeting {
+  SdkVersionTargeting sdk_version_targeting = 1;
+  AbiTargeting abi_targeting = 2;
+  ScreenDensityTargeting screen_density_targeting = 3;
+  MultiAbiTargeting multi_abi_targeting = 4;
+  TextureCompressionFormatTargeting texture_compression_format_targeting = 5;
+}
+
+// Targeting on the level of individual APKs.
+message ApkTargeting {
+  AbiTargeting abi_targeting = 1;
+  GraphicsApiTargeting graphics_api_targeting = 2;
+  LanguageTargeting language_targeting = 3;
+  ScreenDensityTargeting screen_density_targeting = 4;
+  SdkVersionTargeting sdk_version_targeting = 5;
+  TextureCompressionFormatTargeting texture_compression_format_targeting = 6;
+  MultiAbiTargeting multi_abi_targeting = 7;
+  SanitizerTargeting sanitizer_targeting = 8;
+}
+
+// Targeting on the module level.
+// The semantic of the targeting is the "AND" rule on all immediate values.
+message ModuleTargeting {
+  SdkVersionTargeting sdk_version_targeting = 1;
+  repeated DeviceFeatureTargeting device_feature_targeting = 2;
+  UserCountriesTargeting user_countries_targeting = 3;
+}
+
+// User Countries targeting describing an inclusive/exclusive list of country
+// codes that module targets.
+message UserCountriesTargeting {
+  // List of country codes in the two-letter CLDR territory format.
+  repeated string country_codes = 1;
+
+  // Indicates if the list above is exclusive.
+  bool exclude = 2;
+}
+
+message ScreenDensity {
+  enum DensityAlias {
+    DENSITY_UNSPECIFIED = 0;
+    NODPI = 1;
+    LDPI = 2;
+    MDPI = 3;
+    TVDPI = 4;
+    HDPI = 5;
+    XHDPI = 6;
+    XXHDPI = 7;
+    XXXHDPI = 8;
+  }
+
+  oneof density_oneof {
+    DensityAlias density_alias = 1;
+    int32 density_dpi = 2;
+  }
+}
+
+// Wrapper message for `int32`.
+//
+// The JSON representation for `Int32Value` is JSON number.
+message Int32Value {
+  // The int32 value.
+  int32 value = 1;
+}
+
+message SdkVersion {
+  // Inclusive.
+  Int32Value min = 1;
+}
+
+message GraphicsApi {
+  oneof api_oneof {
+    // Inclusive.
+    OpenGlVersion min_open_gl_version = 1;
+    // Inclusive.
+    VulkanVersion min_vulkan_version = 2;
+  }
+}
+
+message VulkanVersion {
+  int32 major = 1;  // VK_VERSION_MAJOR
+  int32 minor = 2;  // VK_VERSION_MINOR
+}
+
+message OpenGlVersion {
+  // e.g. OpenGL ES 3.2 is represented as { major: 3, minor: 2 }
+  int32 major = 1;  // GL_MAJOR_VERSION
+  int32 minor = 2;  // GL_MINOR_VERSION
+}
+
+message TextureCompressionFormat {
+  enum TextureCompressionFormatAlias {
+    UNSPECIFIED_TEXTURE_COMPRESSION_FORMAT = 0;
+    ETC1_RGB8 = 1;
+    PALETTED = 2;
+    THREE_DC = 3;
+    ATC = 4;
+    LATC = 5;
+    DXT1 = 6;
+    S3TC = 7;
+    PVRTC = 8;
+    ASTC = 9;
+    ETC2 = 10;
+  }
+  TextureCompressionFormatAlias alias = 1;
+}
+
+message Abi {
+  enum AbiAlias {
+    UNSPECIFIED_CPU_ARCHITECTURE = 0;
+    ARMEABI = 1;
+    ARMEABI_V7A = 2;
+    ARM64_V8A = 3;
+    X86 = 4;
+    X86_64 = 5;
+    MIPS = 6;
+    MIPS64 = 7;
+  }
+  AbiAlias alias = 1;
+}
+
+message MultiAbi {
+  repeated Abi abi = 1;
+}
+
+message Sanitizer {
+  enum SanitizerAlias {
+    NONE = 0;
+    HWADDRESS = 1;
+  }
+  SanitizerAlias alias = 1;
+}
+
+message DeviceFeature {
+  string feature_name = 1;
+  // Equivalent of android:glEsVersion or android:version in <uses-feature>.
+  int32 feature_version = 2;
+}
+
+// Targeting specific for directories under assets/.
+message AssetsDirectoryTargeting {
+  AbiTargeting abi = 1;
+  GraphicsApiTargeting graphics_api = 2;
+  TextureCompressionFormatTargeting texture_compression_format = 3;
+  LanguageTargeting language = 4;
+}
+
+// Targeting specific for directories under lib/.
+message NativeDirectoryTargeting {
+  Abi abi = 1;
+  GraphicsApi graphics_api = 2;
+  TextureCompressionFormat texture_compression_format = 3;
+  Sanitizer sanitizer = 4;
+}
+
+// Targeting specific for image files under apex/.
+message ApexImageTargeting {
+  MultiAbiTargeting multi_abi = 1;
+}
+
+message AbiTargeting {
+  repeated Abi value = 1;
+  // Targeting of other sibling directories that were in the Bundle.
+  // For master splits this is targeting of other master splits.
+  repeated Abi alternatives = 2;
+}
+
+message MultiAbiTargeting {
+  repeated MultiAbi value = 1;
+  // Targeting of other sibling directories that were in the Bundle.
+  // For master splits this is targeting of other master splits.
+  repeated MultiAbi alternatives = 2;
+}
+
+message ScreenDensityTargeting {
+  repeated ScreenDensity value = 1;
+  // Targeting of other sibling directories that were in the Bundle.
+  // For master splits this is targeting of other master splits.
+  repeated ScreenDensity alternatives = 2;
+}
+
+message LanguageTargeting {
+  // ISO-639: 2 or 3 letter language code.
+  repeated string value = 1;
+  // Targeting of other sibling directories that were in the Bundle.
+  // For master splits this is targeting of other master splits.
+  repeated string alternatives = 2;
+}
+
+message GraphicsApiTargeting {
+  repeated GraphicsApi value = 1;
+  // Targeting of other sibling directories that were in the Bundle.
+  // For master splits this is targeting of other master splits.
+  repeated GraphicsApi alternatives = 2;
+}
+
+message SdkVersionTargeting {
+  repeated SdkVersion value = 1;
+  // Targeting of other sibling directories that were in the Bundle.
+  // For master splits this is targeting of other master splits.
+  repeated SdkVersion alternatives = 2;
+}
+
+message TextureCompressionFormatTargeting {
+  repeated TextureCompressionFormat value = 1;
+  // Targeting of other sibling directories that were in the Bundle.
+  // For master splits this is targeting of other master splits.
+  repeated TextureCompressionFormat alternatives = 2;
+}
+
+message SanitizerTargeting {
+  repeated Sanitizer value = 1;
+}
+
+// Since other atom targeting messages have the "OR" semantic on values
+// the DeviceFeatureTargeting represents only one device feature to retain
+// that convention.
+message DeviceFeatureTargeting {
+  DeviceFeature required_feature = 1;
+}
diff --git a/cmd/extract_apks/main.go b/cmd/extract_apks/main.go
new file mode 100644
index 0000000..e9a850e
--- /dev/null
+++ b/cmd/extract_apks/main.go
@@ -0,0 +1,544 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Copies all the entries (APKs/APEXes) matching the target configuration from the given
+// APK set into a zip file. Run it without arguments to see usage details.
+package main
+
+import (
+	"flag"
+	"fmt"
+	"io"
+	"log"
+	"math"
+	"os"
+	"regexp"
+	"strings"
+
+	"github.com/golang/protobuf/proto"
+
+	"android/soong/cmd/extract_apks/bundle_proto"
+	"android/soong/third_party/zip"
+)
+
+type TargetConfig struct {
+	sdkVersion int32
+	screenDpi  map[android_bundle_proto.ScreenDensity_DensityAlias]bool
+	// Map holding <ABI alias>:<its sequence number in the flag> info.
+	abis             map[android_bundle_proto.Abi_AbiAlias]int
+	allowPrereleased bool
+	stem             string
+}
+
+// An APK set is a zip archive. An entry 'toc.pb' describes its contents.
+// It is a protobuf message BuildApkResult.
+type Toc *android_bundle_proto.BuildApksResult
+
+type ApkSet struct {
+	path    string
+	reader  *zip.ReadCloser
+	entries map[string]*zip.File
+}
+
+func newApkSet(path string) (*ApkSet, error) {
+	apkSet := &ApkSet{path: path, entries: make(map[string]*zip.File)}
+	var err error
+	if apkSet.reader, err = zip.OpenReader(apkSet.path); err != nil {
+		return nil, err
+	}
+	for _, f := range apkSet.reader.File {
+		apkSet.entries[f.Name] = f
+	}
+	return apkSet, nil
+}
+
+func (apkSet *ApkSet) getToc() (Toc, error) {
+	var err error
+	tocFile, ok := apkSet.entries["toc.pb"]
+	if !ok {
+		return nil, fmt.Errorf("%s: APK set should have toc.pb entry", apkSet.path)
+	}
+	rc, err := tocFile.Open()
+	if err != nil {
+		return nil, err
+	}
+	bytes := make([]byte, tocFile.FileHeader.UncompressedSize64)
+	if _, err := rc.Read(bytes); err != io.EOF {
+		return nil, err
+	}
+	rc.Close()
+	buildApksResult := new(android_bundle_proto.BuildApksResult)
+	if err = proto.Unmarshal(bytes, buildApksResult); err != nil {
+		return nil, err
+	}
+	return buildApksResult, nil
+}
+
+func (apkSet *ApkSet) close() {
+	apkSet.reader.Close()
+}
+
+// Matchers for selection criteria
+
+type abiTargetingMatcher struct {
+	*android_bundle_proto.AbiTargeting
+}
+
+func (m abiTargetingMatcher) matches(config TargetConfig) bool {
+	if m.AbiTargeting == nil {
+		return true
+	}
+	if _, ok := config.abis[android_bundle_proto.Abi_UNSPECIFIED_CPU_ARCHITECTURE]; ok {
+		return true
+	}
+	// Find the one that appears first in the abis flags.
+	abiIdx := math.MaxInt32
+	for _, v := range m.GetValue() {
+		if i, ok := config.abis[v.Alias]; ok {
+			if i < abiIdx {
+				abiIdx = i
+			}
+		}
+	}
+	if abiIdx == math.MaxInt32 {
+		return false
+	}
+	// See if any alternatives appear before the above one.
+	for _, a := range m.GetAlternatives() {
+		if i, ok := config.abis[a.Alias]; ok {
+			if i < abiIdx {
+				// There is a better alternative. Skip this one.
+				return false
+			}
+		}
+	}
+	return true
+}
+
+type apkDescriptionMatcher struct {
+	*android_bundle_proto.ApkDescription
+}
+
+func (m apkDescriptionMatcher) matches(config TargetConfig) bool {
+	return m.ApkDescription == nil || (apkTargetingMatcher{m.Targeting}).matches(config)
+}
+
+type apkTargetingMatcher struct {
+	*android_bundle_proto.ApkTargeting
+}
+
+func (m apkTargetingMatcher) matches(config TargetConfig) bool {
+	return m.ApkTargeting == nil ||
+		(abiTargetingMatcher{m.AbiTargeting}.matches(config) &&
+			languageTargetingMatcher{m.LanguageTargeting}.matches(config) &&
+			screenDensityTargetingMatcher{m.ScreenDensityTargeting}.matches(config) &&
+			sdkVersionTargetingMatcher{m.SdkVersionTargeting}.matches(config) &&
+			multiAbiTargetingMatcher{m.MultiAbiTargeting}.matches(config))
+}
+
+type languageTargetingMatcher struct {
+	*android_bundle_proto.LanguageTargeting
+}
+
+func (m languageTargetingMatcher) matches(_ TargetConfig) bool {
+	if m.LanguageTargeting == nil {
+		return true
+	}
+	log.Fatal("language based entry selection is not implemented")
+	return false
+}
+
+type moduleMetadataMatcher struct {
+	*android_bundle_proto.ModuleMetadata
+}
+
+func (m moduleMetadataMatcher) matches(config TargetConfig) bool {
+	return m.ModuleMetadata == nil ||
+		(m.GetDeliveryType() == android_bundle_proto.DeliveryType_INSTALL_TIME &&
+			moduleTargetingMatcher{m.Targeting}.matches(config) &&
+			!m.IsInstant)
+}
+
+type moduleTargetingMatcher struct {
+	*android_bundle_proto.ModuleTargeting
+}
+
+func (m moduleTargetingMatcher) matches(config TargetConfig) bool {
+	return m.ModuleTargeting == nil ||
+		(sdkVersionTargetingMatcher{m.SdkVersionTargeting}.matches(config) &&
+			userCountriesTargetingMatcher{m.UserCountriesTargeting}.matches(config))
+}
+
+// A higher number means a higher priority.
+// This order must be kept identical to bundletool's.
+var multiAbiPriorities = map[android_bundle_proto.Abi_AbiAlias]int{
+	android_bundle_proto.Abi_ARMEABI:     1,
+	android_bundle_proto.Abi_ARMEABI_V7A: 2,
+	android_bundle_proto.Abi_ARM64_V8A:   3,
+	android_bundle_proto.Abi_X86:         4,
+	android_bundle_proto.Abi_X86_64:      5,
+	android_bundle_proto.Abi_MIPS:        6,
+	android_bundle_proto.Abi_MIPS64:      7,
+}
+
+type multiAbiTargetingMatcher struct {
+	*android_bundle_proto.MultiAbiTargeting
+}
+
+func (t multiAbiTargetingMatcher) matches(config TargetConfig) bool {
+	if t.MultiAbiTargeting == nil {
+		return true
+	}
+	if _, ok := config.abis[android_bundle_proto.Abi_UNSPECIFIED_CPU_ARCHITECTURE]; ok {
+		return true
+	}
+	// Find the one with the highest priority.
+	highestPriority := 0
+	for _, v := range t.GetValue() {
+		for _, a := range v.GetAbi() {
+			if _, ok := config.abis[a.Alias]; ok {
+				if highestPriority < multiAbiPriorities[a.Alias] {
+					highestPriority = multiAbiPriorities[a.Alias]
+				}
+			}
+		}
+	}
+	if highestPriority == 0 {
+		return false
+	}
+	// See if there are any matching alternatives with a higher priority.
+	for _, v := range t.GetAlternatives() {
+		for _, a := range v.GetAbi() {
+			if _, ok := config.abis[a.Alias]; ok {
+				if highestPriority < multiAbiPriorities[a.Alias] {
+					// There's a better one. Skip this one.
+					return false
+				}
+			}
+		}
+	}
+	return true
+}
+
+type screenDensityTargetingMatcher struct {
+	*android_bundle_proto.ScreenDensityTargeting
+}
+
+func (m screenDensityTargetingMatcher) matches(config TargetConfig) bool {
+	if m.ScreenDensityTargeting == nil {
+		return true
+	}
+	if _, ok := config.screenDpi[android_bundle_proto.ScreenDensity_DENSITY_UNSPECIFIED]; ok {
+		return true
+	}
+	for _, v := range m.GetValue() {
+		switch x := v.GetDensityOneof().(type) {
+		case *android_bundle_proto.ScreenDensity_DensityAlias_:
+			if _, ok := config.screenDpi[x.DensityAlias]; ok {
+				return true
+			}
+		default:
+			log.Fatal("For screen density, only DPI name based entry selection (e.g. HDPI, XHDPI) is implemented")
+		}
+	}
+	return false
+}
+
+type sdkVersionTargetingMatcher struct {
+	*android_bundle_proto.SdkVersionTargeting
+}
+
+func (m sdkVersionTargetingMatcher) matches(config TargetConfig) bool {
+	const preReleaseVersion = 10000
+	if m.SdkVersionTargeting == nil {
+		return true
+	}
+	if len(m.Value) > 1 {
+		log.Fatal(fmt.Sprintf("sdk_version_targeting should not have multiple values:%#v", m.Value))
+	}
+	// Inspect only sdkVersionTargeting.Value.
+	// Even though one of the SdkVersionTargeting.Alternatives values may be
+	// better matching, we will select all of them
+	return m.Value[0].Min == nil ||
+		m.Value[0].Min.Value <= config.sdkVersion ||
+		(config.allowPrereleased && m.Value[0].Min.Value == preReleaseVersion)
+}
+
+type textureCompressionFormatTargetingMatcher struct {
+	*android_bundle_proto.TextureCompressionFormatTargeting
+}
+
+func (m textureCompressionFormatTargetingMatcher) matches(_ TargetConfig) bool {
+	if m.TextureCompressionFormatTargeting == nil {
+		return true
+	}
+	log.Fatal("texture based entry selection is not implemented")
+	return false
+}
+
+type userCountriesTargetingMatcher struct {
+	*android_bundle_proto.UserCountriesTargeting
+}
+
+func (m userCountriesTargetingMatcher) matches(_ TargetConfig) bool {
+	if m.UserCountriesTargeting == nil {
+		return true
+	}
+	log.Fatal("country based entry selection is not implemented")
+	return false
+}
+
+type variantTargetingMatcher struct {
+	*android_bundle_proto.VariantTargeting
+}
+
+func (m variantTargetingMatcher) matches(config TargetConfig) bool {
+	if m.VariantTargeting == nil {
+		return true
+	}
+	return sdkVersionTargetingMatcher{m.SdkVersionTargeting}.matches(config) &&
+		abiTargetingMatcher{m.AbiTargeting}.matches(config) &&
+		multiAbiTargetingMatcher{m.MultiAbiTargeting}.matches(config) &&
+		screenDensityTargetingMatcher{m.ScreenDensityTargeting}.matches(config) &&
+		textureCompressionFormatTargetingMatcher{m.TextureCompressionFormatTargeting}.matches(config)
+}
+
+type SelectionResult struct {
+	moduleName string
+	entries    []string
+}
+
+// Return all entries matching target configuration
+func selectApks(toc Toc, targetConfig TargetConfig) SelectionResult {
+	var result SelectionResult
+	for _, variant := range (*toc).GetVariant() {
+		if !(variantTargetingMatcher{variant.GetTargeting()}.matches(targetConfig)) {
+			continue
+		}
+		for _, as := range variant.GetApkSet() {
+			if !(moduleMetadataMatcher{as.ModuleMetadata}.matches(targetConfig)) {
+				continue
+			}
+			for _, apkdesc := range as.GetApkDescription() {
+				if (apkDescriptionMatcher{apkdesc}).matches(targetConfig) {
+					result.entries = append(result.entries, apkdesc.GetPath())
+					// TODO(asmundak): As it turns out, moduleName which we get from
+					// the ModuleMetadata matches the module names of the generated
+					// entry paths just by coincidence, only for the split APKs. We
+					// need to discuss this with bundletool folks.
+					result.moduleName = as.GetModuleMetadata().GetName()
+				}
+			}
+			// we allow only a single module, so bail out here if we found one
+			if result.moduleName != "" {
+				return result
+			}
+		}
+	}
+	return result
+}
+
+type Zip2ZipWriter interface {
+	CopyFrom(file *zip.File, name string) error
+}
+
+// Writes out selected entries, renaming them as needed
+func (apkSet *ApkSet) writeApks(selected SelectionResult, config TargetConfig,
+	writer Zip2ZipWriter) error {
+	// Renaming rules:
+	//  splits/MODULE-master.apk to STEM.apk
+	// else
+	//  splits/MODULE-*.apk to STEM>-$1.apk
+	// TODO(asmundak):
+	//  add more rules, for .apex files
+	renameRules := []struct {
+		rex  *regexp.Regexp
+		repl string
+	}{
+		{
+			regexp.MustCompile(`^.*/` + selected.moduleName + `-master\.apk$`),
+			config.stem + `.apk`,
+		},
+		{
+			regexp.MustCompile(`^.*/` + selected.moduleName + `(-.*\.apk)$`),
+			config.stem + `$1`,
+		},
+		{
+			regexp.MustCompile(`^universal\.apk$`),
+			config.stem + ".apk",
+		},
+	}
+	renamer := func(path string) (string, bool) {
+		for _, rr := range renameRules {
+			if rr.rex.MatchString(path) {
+				return rr.rex.ReplaceAllString(path, rr.repl), true
+			}
+		}
+		return "", false
+	}
+
+	entryOrigin := make(map[string]string) // output entry to input entry
+	for _, apk := range selected.entries {
+		apkFile, ok := apkSet.entries[apk]
+		if !ok {
+			return fmt.Errorf("TOC refers to an entry %s which does not exist", apk)
+		}
+		inName := apkFile.Name
+		outName, ok := renamer(inName)
+		if !ok {
+			log.Fatalf("selected an entry with unexpected name %s", inName)
+		}
+		if origin, ok := entryOrigin[inName]; ok {
+			log.Fatalf("selected entries %s and %s will have the same output name %s",
+				origin, inName, outName)
+		}
+		entryOrigin[outName] = inName
+		if err := writer.CopyFrom(apkFile, outName); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func (apkSet *ApkSet) extractAndCopySingle(selected SelectionResult, outFile *os.File) error {
+	if len(selected.entries) != 1 {
+		return fmt.Errorf("Too many matching entries for extract-single:\n%v", selected.entries)
+	}
+	apk, ok := apkSet.entries[selected.entries[0]]
+	if !ok {
+		return fmt.Errorf("Couldn't find apk path %s", selected.entries[0])
+	}
+	inputReader, _ := apk.Open()
+	_, err := io.Copy(outFile, inputReader)
+	return err
+}
+
+// Arguments parsing
+var (
+	outputFile   = flag.String("o", "", "output file containing extracted entries")
+	targetConfig = TargetConfig{
+		screenDpi: map[android_bundle_proto.ScreenDensity_DensityAlias]bool{},
+		abis:      map[android_bundle_proto.Abi_AbiAlias]int{},
+	}
+	extractSingle = flag.Bool("extract-single", false,
+		"extract a single target and output it uncompressed. only available for standalone apks and apexes.")
+)
+
+// Parse abi values
+type abiFlagValue struct {
+	targetConfig *TargetConfig
+}
+
+func (a abiFlagValue) String() string {
+	return "all"
+}
+
+func (a abiFlagValue) Set(abiList string) error {
+	for i, abi := range strings.Split(abiList, ",") {
+		v, ok := android_bundle_proto.Abi_AbiAlias_value[abi]
+		if !ok {
+			return fmt.Errorf("bad ABI value: %q", abi)
+		}
+		targetConfig.abis[android_bundle_proto.Abi_AbiAlias(v)] = i
+	}
+	return nil
+}
+
+// Parse screen density values
+type screenDensityFlagValue struct {
+	targetConfig *TargetConfig
+}
+
+func (s screenDensityFlagValue) String() string {
+	return "none"
+}
+
+func (s screenDensityFlagValue) Set(densityList string) error {
+	if densityList == "none" {
+		return nil
+	}
+	if densityList == "all" {
+		targetConfig.screenDpi[android_bundle_proto.ScreenDensity_DENSITY_UNSPECIFIED] = true
+		return nil
+	}
+	for _, density := range strings.Split(densityList, ",") {
+		v, found := android_bundle_proto.ScreenDensity_DensityAlias_value[density]
+		if !found {
+			return fmt.Errorf("bad screen density value: %q", density)
+		}
+		targetConfig.screenDpi[android_bundle_proto.ScreenDensity_DensityAlias(v)] = true
+	}
+	return nil
+}
+
+func processArgs() {
+	flag.Usage = func() {
+		fmt.Fprintln(os.Stderr, `usage: extract_apks -o <output-file> -sdk-version value -abis value `+
+			`-screen-densities value {-stem value | -extract-single} [-allow-prereleased] <APK set>`)
+		flag.PrintDefaults()
+		os.Exit(2)
+	}
+	version := flag.Uint("sdk-version", 0, "SDK version")
+	flag.Var(abiFlagValue{&targetConfig}, "abis",
+		"comma-separated ABIs list of ARMEABI ARMEABI_V7A ARM64_V8A X86 X86_64 MIPS MIPS64")
+	flag.Var(screenDensityFlagValue{&targetConfig}, "screen-densities",
+		"'all' or comma-separated list of screen density names (NODPI LDPI MDPI TVDPI HDPI XHDPI XXHDPI XXXHDPI)")
+	flag.BoolVar(&targetConfig.allowPrereleased, "allow-prereleased", false,
+		"allow prereleased")
+	flag.StringVar(&targetConfig.stem, "stem", "", "output entries base name in the output zip file")
+	flag.Parse()
+	if (*outputFile == "") || len(flag.Args()) != 1 || *version == 0 || (targetConfig.stem == "" && !*extractSingle) {
+		flag.Usage()
+	}
+	targetConfig.sdkVersion = int32(*version)
+
+}
+
+func main() {
+	processArgs()
+	var toc Toc
+	apkSet, err := newApkSet(flag.Arg(0))
+	if err == nil {
+		defer apkSet.close()
+		toc, err = apkSet.getToc()
+	}
+	if err != nil {
+		log.Fatal(err)
+	}
+	sel := selectApks(toc, targetConfig)
+	if len(sel.entries) == 0 {
+		log.Fatalf("there are no entries for the target configuration: %#v", targetConfig)
+	}
+
+	outFile, err := os.Create(*outputFile)
+	if err != nil {
+		log.Fatal(err)
+	}
+	defer outFile.Close()
+
+	if *extractSingle {
+		err = apkSet.extractAndCopySingle(sel, outFile)
+	} else {
+		writer := zip.NewWriter(outFile)
+		defer func() {
+			if err := writer.Close(); err != nil {
+				log.Fatal(err)
+			}
+		}()
+		err = apkSet.writeApks(sel, targetConfig, writer)
+	}
+	if err != nil {
+		log.Fatal(err)
+	}
+}
diff --git a/cmd/extract_apks/main_test.go b/cmd/extract_apks/main_test.go
new file mode 100644
index 0000000..bdd4bec
--- /dev/null
+++ b/cmd/extract_apks/main_test.go
@@ -0,0 +1,477 @@
+// Copyright 2020 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+	"fmt"
+	"github.com/golang/protobuf/proto"
+	"reflect"
+	"testing"
+
+	bp "android/soong/cmd/extract_apks/bundle_proto"
+	"android/soong/third_party/zip"
+)
+
+type testConfigDesc struct {
+	name         string
+	targetConfig TargetConfig
+	expected     SelectionResult
+}
+
+type testDesc struct {
+	protoText string
+	configs   []testConfigDesc
+}
+
+func TestSelectApks_ApkSet(t *testing.T) {
+	testCases := []testDesc{
+		{
+			protoText: `
+variant {
+  targeting {
+    sdk_version_targeting {
+      value { min { value: 29 } } } }
+  apk_set {
+    module_metadata {
+      name: "base" targeting {} delivery_type: INSTALL_TIME }
+    apk_description {
+      targeting {
+        screen_density_targeting {
+          value { density_alias: LDPI } }
+        sdk_version_targeting {
+          value { min { value: 21 } } } }
+      path: "splits/base-ldpi.apk"
+      split_apk_metadata { split_id: "config.ldpi" } }
+    apk_description {
+      targeting {
+        screen_density_targeting {
+          value { density_alias: MDPI } }
+        sdk_version_targeting {
+          value { min { value: 21 } } } }
+      path: "splits/base-mdpi.apk"
+      split_apk_metadata { split_id: "config.mdpi" } }
+    apk_description {
+      targeting {
+        sdk_version_targeting {
+          value { min { value: 21 } } } }
+      path: "splits/base-master.apk"
+      split_apk_metadata { is_master_split: true } }
+    apk_description {
+      targeting {
+        abi_targeting {
+          value { alias: ARMEABI_V7A }
+          alternatives { alias: ARM64_V8A }
+          alternatives { alias: X86 }
+          alternatives { alias: X86_64 } }
+        sdk_version_targeting {
+          value { min { value: 21 } } } }
+      path: "splits/base-armeabi_v7a.apk"
+      split_apk_metadata { split_id: "config.armeabi_v7a" } }
+    apk_description {
+      targeting {
+        abi_targeting {
+          value { alias: ARM64_V8A }
+          alternatives { alias: ARMEABI_V7A }
+          alternatives { alias: X86 }
+          alternatives { alias: X86_64 } }
+        sdk_version_targeting {
+          value { min { value: 21 } } } }
+      path: "splits/base-arm64_v8a.apk"
+      split_apk_metadata { split_id: "config.arm64_v8a" } }
+    apk_description {
+      targeting {
+        abi_targeting {
+          value { alias: X86 }
+          alternatives { alias: ARMEABI_V7A }
+          alternatives { alias: ARM64_V8A }
+          alternatives { alias: X86_64 } }
+        sdk_version_targeting {
+          value { min { value: 21 } } } }
+      path: "splits/base-x86.apk"
+      split_apk_metadata { split_id: "config.x86" } }
+    apk_description {
+      targeting {
+        abi_targeting {
+          value { alias: X86_64 }
+          alternatives { alias: ARMEABI_V7A }
+          alternatives { alias: ARM64_V8A }
+          alternatives { alias: X86 } }
+        sdk_version_targeting {
+          value { min { value: 21 } } } }
+      path: "splits/base-x86_64.apk"
+      split_apk_metadata { split_id: "config.x86_64" } } }
+}
+bundletool {
+  version: "0.10.3" }
+
+`,
+			configs: []testConfigDesc{
+				{
+					name: "one",
+					targetConfig: TargetConfig{
+						sdkVersion: 29,
+						screenDpi: map[bp.ScreenDensity_DensityAlias]bool{
+							bp.ScreenDensity_DENSITY_UNSPECIFIED: true,
+						},
+						abis: map[bp.Abi_AbiAlias]int{
+							bp.Abi_ARMEABI_V7A: 0,
+							bp.Abi_ARM64_V8A:   1,
+						},
+					},
+					expected: SelectionResult{
+						"base",
+						[]string{
+							"splits/base-ldpi.apk",
+							"splits/base-mdpi.apk",
+							"splits/base-master.apk",
+							"splits/base-armeabi_v7a.apk",
+						},
+					},
+				},
+				{
+					name: "two",
+					targetConfig: TargetConfig{
+						sdkVersion: 29,
+						screenDpi: map[bp.ScreenDensity_DensityAlias]bool{
+							bp.ScreenDensity_LDPI: true,
+						},
+						abis: map[bp.Abi_AbiAlias]int{},
+					},
+					expected: SelectionResult{
+						"base",
+						[]string{
+							"splits/base-ldpi.apk",
+							"splits/base-master.apk",
+						},
+					},
+				},
+				{
+					name: "three",
+					targetConfig: TargetConfig{
+						sdkVersion: 20,
+						screenDpi: map[bp.ScreenDensity_DensityAlias]bool{
+							bp.ScreenDensity_LDPI: true,
+						},
+						abis: map[bp.Abi_AbiAlias]int{},
+					},
+					expected: SelectionResult{
+						"",
+						nil,
+					},
+				},
+				{
+					name: "four",
+					targetConfig: TargetConfig{
+						sdkVersion: 29,
+						screenDpi: map[bp.ScreenDensity_DensityAlias]bool{
+							bp.ScreenDensity_MDPI: true,
+						},
+						abis: map[bp.Abi_AbiAlias]int{
+							bp.Abi_ARM64_V8A:   0,
+							bp.Abi_ARMEABI_V7A: 1,
+						},
+					},
+					expected: SelectionResult{
+						"base",
+						[]string{
+							"splits/base-mdpi.apk",
+							"splits/base-master.apk",
+							"splits/base-arm64_v8a.apk",
+						},
+					},
+				},
+			},
+		},
+		{
+			protoText: `
+variant {
+  targeting {
+    sdk_version_targeting {
+      value { min { value: 10000 } } } }
+  apk_set {
+    module_metadata {
+      name: "base" targeting {} delivery_type: INSTALL_TIME }
+    apk_description {
+      targeting {
+        sdk_version_targeting {
+          value { min { value: 21 } } } }
+      path: "splits/base-master.apk"
+      split_apk_metadata { is_master_split: true } } } }`,
+			configs: []testConfigDesc{
+				{
+					name: "Prerelease",
+					targetConfig: TargetConfig{
+						sdkVersion:       30,
+						screenDpi:        map[bp.ScreenDensity_DensityAlias]bool{},
+						abis:             map[bp.Abi_AbiAlias]int{},
+						allowPrereleased: true,
+					},
+					expected: SelectionResult{
+						"base",
+						[]string{"splits/base-master.apk"},
+					},
+				},
+			},
+		},
+		{
+			protoText: `
+variant {
+  targeting {
+    sdk_version_targeting {
+      value { min { value: 29 } } } }
+  apk_set {
+    module_metadata {
+      name: "base" targeting {} delivery_type: INSTALL_TIME }
+    apk_description {
+      targeting {}
+      path: "universal.apk"
+      standalone_apk_metadata { fused_module_name: "base" } } } }`,
+			configs: []testConfigDesc{
+				{
+					name:         "Universal",
+					targetConfig: TargetConfig{sdkVersion: 30},
+					expected: SelectionResult{
+						"base",
+						[]string{"universal.apk"},
+					},
+				},
+			},
+		},
+	}
+	for _, testCase := range testCases {
+		var toc bp.BuildApksResult
+		if err := proto.UnmarshalText(testCase.protoText, &toc); err != nil {
+			t.Fatal(err)
+		}
+		for _, config := range testCase.configs {
+			actual := selectApks(&toc, config.targetConfig)
+			if !reflect.DeepEqual(config.expected, actual) {
+				t.Errorf("%s: expected %v, got %v", config.name, config.expected, actual)
+			}
+		}
+	}
+}
+
+func TestSelectApks_ApexSet(t *testing.T) {
+	testCases := []testDesc{
+		{
+			protoText: `
+variant {
+  targeting {
+    sdk_version_targeting {
+      value { min { value: 29 } } } }
+  apk_set {
+    module_metadata {
+      name: "base" targeting {} delivery_type: INSTALL_TIME }
+    apk_description {
+      targeting {
+        multi_abi_targeting {
+          value { abi { alias: ARMEABI_V7A } }
+          alternatives { abi { alias: ARM64_V8A } }
+          alternatives { abi { alias: X86 } }
+          alternatives { abi { alias: X86_64 } } }
+        sdk_version_targeting {
+          value { min { value: 21 } } } }
+      path: "standalones/standalone-armeabi_v7a.apex"
+      apex_apk_metadata { } }
+    apk_description {
+      targeting {
+        multi_abi_targeting {
+          value { abi { alias: ARM64_V8A } }
+          alternatives { abi { alias: ARMEABI_V7A } }
+          alternatives { abi { alias: X86 } }
+          alternatives { abi { alias: X86_64 } } }
+        sdk_version_targeting {
+          value { min { value: 21 } } } }
+      path: "standalones/standalone-arm64_v8a.apex"
+      apex_apk_metadata { } }
+    apk_description {
+      targeting {
+        multi_abi_targeting {
+          value { abi { alias: X86 } }
+          alternatives { abi { alias: ARMEABI_V7A } }
+          alternatives { abi { alias: ARM64_V8A } }
+          alternatives { abi { alias: X86_64 } } }
+        sdk_version_targeting {
+          value { min { value: 21 } } } }
+      path: "standalones/standalone-x86.apex"
+      apex_apk_metadata { } }
+    apk_description {
+      targeting {
+        multi_abi_targeting {
+          value { abi { alias: X86_64 } }
+          alternatives { abi { alias: ARMEABI_V7A } }
+          alternatives { abi { alias: ARM64_V8A } }
+          alternatives { abi { alias: X86 } } }
+        sdk_version_targeting {
+          value { min { value: 21 } } } }
+      path: "standalones/standalone-x86_64.apex"
+      apex_apk_metadata { } } }
+}
+bundletool {
+  version: "0.10.3" }
+
+`,
+			configs: []testConfigDesc{
+				{
+					name: "order matches priorities",
+					targetConfig: TargetConfig{
+						sdkVersion: 29,
+						screenDpi: map[bp.ScreenDensity_DensityAlias]bool{
+							bp.ScreenDensity_DENSITY_UNSPECIFIED: true,
+						},
+						abis: map[bp.Abi_AbiAlias]int{
+							bp.Abi_ARM64_V8A:   0,
+							bp.Abi_ARMEABI_V7A: 1,
+						},
+					},
+					expected: SelectionResult{
+						"base",
+						[]string{
+							"standalones/standalone-arm64_v8a.apex",
+						},
+					},
+				},
+				{
+					name: "order doesn't match priorities",
+					targetConfig: TargetConfig{
+						sdkVersion: 29,
+						screenDpi: map[bp.ScreenDensity_DensityAlias]bool{
+							bp.ScreenDensity_DENSITY_UNSPECIFIED: true,
+						},
+						abis: map[bp.Abi_AbiAlias]int{
+							bp.Abi_ARMEABI_V7A: 0,
+							bp.Abi_ARM64_V8A:   1,
+						},
+					},
+					expected: SelectionResult{
+						"base",
+						[]string{
+							"standalones/standalone-arm64_v8a.apex",
+						},
+					},
+				},
+				{
+					name: "single choice",
+					targetConfig: TargetConfig{
+						sdkVersion: 29,
+						screenDpi: map[bp.ScreenDensity_DensityAlias]bool{
+							bp.ScreenDensity_DENSITY_UNSPECIFIED: true,
+						},
+						abis: map[bp.Abi_AbiAlias]int{
+							bp.Abi_ARMEABI_V7A: 0,
+						},
+					},
+					expected: SelectionResult{
+						"base",
+						[]string{
+							"standalones/standalone-armeabi_v7a.apex",
+						},
+					},
+				},
+				{
+					name: "cross platform",
+					targetConfig: TargetConfig{
+						sdkVersion: 29,
+						screenDpi: map[bp.ScreenDensity_DensityAlias]bool{
+							bp.ScreenDensity_DENSITY_UNSPECIFIED: true,
+						},
+						abis: map[bp.Abi_AbiAlias]int{
+							bp.Abi_ARM64_V8A: 0,
+							bp.Abi_MIPS64:    1,
+							bp.Abi_X86:       2,
+						},
+					},
+					expected: SelectionResult{
+						"base",
+						[]string{
+							"standalones/standalone-x86.apex",
+						},
+					},
+				},
+			},
+		},
+	}
+	for _, testCase := range testCases {
+		var toc bp.BuildApksResult
+		if err := proto.UnmarshalText(testCase.protoText, &toc); err != nil {
+			t.Fatal(err)
+		}
+		for _, config := range testCase.configs {
+			actual := selectApks(&toc, config.targetConfig)
+			if !reflect.DeepEqual(config.expected, actual) {
+				t.Errorf("%s: expected %v, got %v", config.name, config.expected, actual)
+			}
+		}
+	}
+}
+
+type testZip2ZipWriter struct {
+	entries map[string]string
+}
+
+func (w testZip2ZipWriter) CopyFrom(file *zip.File, out string) error {
+	if x, ok := w.entries[out]; ok {
+		return fmt.Errorf("%s and %s both write to %s", x, file.Name, out)
+	}
+	w.entries[out] = file.Name
+	return nil
+}
+
+type testCaseWriteZip struct {
+	name       string
+	moduleName string
+	stem       string
+	// what we write from what
+	expected map[string]string
+}
+
+func TestWriteZip(t *testing.T) {
+	testCases := []testCaseWriteZip{
+		{
+			name:       "splits",
+			moduleName: "mybase",
+			stem:       "Foo",
+			expected: map[string]string{
+				"Foo.apk":       "splits/mybase-master.apk",
+				"Foo-xhdpi.apk": "splits/mybase-xhdpi.apk",
+			},
+		},
+		{
+			name:       "universal",
+			moduleName: "base",
+			stem:       "Bar",
+			expected: map[string]string{
+				"Bar.apk": "universal.apk",
+			},
+		},
+	}
+	for _, testCase := range testCases {
+		apkSet := ApkSet{entries: make(map[string]*zip.File)}
+		sel := SelectionResult{moduleName: testCase.moduleName}
+		for _, in := range testCase.expected {
+			apkSet.entries[in] = &zip.File{FileHeader: zip.FileHeader{Name: in}}
+			sel.entries = append(sel.entries, in)
+		}
+		writer := testZip2ZipWriter{make(map[string]string)}
+		config := TargetConfig{stem: testCase.stem}
+		if err := apkSet.writeApks(sel, config, writer); err != nil {
+			t.Error(err)
+		}
+		if !reflect.DeepEqual(testCase.expected, writer.entries) {
+			t.Errorf("expected %v, got %v", testCase.expected, writer.entries)
+		}
+	}
+}
diff --git a/cmd/host_bionic_inject/host_bionic_inject.go b/cmd/host_bionic_inject/host_bionic_inject.go
index f7163d7..629f6cc 100644
--- a/cmd/host_bionic_inject/host_bionic_inject.go
+++ b/cmd/host_bionic_inject/host_bionic_inject.go
@@ -105,7 +105,9 @@
 
 	err = checkLinker(file, linker, symbols)
 	if err != nil {
-		return 0, err
+		return 0, fmt.Errorf("Linker executable failed verification against app embedded linker: %s\n"+
+			"linker might not be in sync with crtbegin_dynamic.o.",
+			err)
 	}
 
 	start, err := findSymbol(symbols, "_start")
diff --git a/cmd/pom2bp/pom2bp.go b/cmd/pom2bp/pom2bp.go
index 191b919..d341b8c 100644
--- a/cmd/pom2bp/pom2bp.go
+++ b/cmd/pom2bp/pom2bp.go
@@ -213,10 +213,14 @@
 	return hostAndDeviceModuleNames.IsHostAndDeviceModule(p.GroupId, p.ArtifactId)
 }
 
+func (p Pom) IsHostOnly() bool {
+	return p.IsHostModule() && !p.IsHostAndDeviceModule()
+}
+
 func (p Pom) ModuleType() string {
 	if p.IsAar() {
 		return "android_library"
-	} else if p.IsHostModule() && !p.IsHostAndDeviceModule() {
+	} else if p.IsHostOnly() {
 		return "java_library_host"
 	} else {
 		return "java_library_static"
@@ -226,7 +230,7 @@
 func (p Pom) ImportModuleType() string {
 	if p.IsAar() {
 		return "android_library_import"
-	} else if p.IsHostModule() && !p.IsHostAndDeviceModule() {
+	} else if p.IsHostOnly() {
 		return "java_import_host"
 	} else {
 		return "java_import"
@@ -366,6 +370,12 @@
     {{- if .IsHostAndDeviceModule}}
     host_supported: true,
     {{- end}}
+    {{- if not .IsHostOnly}}
+    apex_available: [
+        "//apex_available:platform",
+        "//apex_available:anyapex",
+    ],
+    {{- end}}
     {{- if .IsAar}}
     min_sdk_version: "{{.MinSdkVersion}}",
     static_libs: [
@@ -401,6 +411,12 @@
     {{- if .IsHostAndDeviceModule}}
     host_supported: true,
     {{- end}}
+    {{- if not .IsHostOnly}}
+    apex_available: [
+        "//apex_available:platform",
+        "//apex_available:anyapex",
+    ],
+    {{- end}}
     {{- if .IsAar}}
     min_sdk_version: "{{.MinSdkVersion}}",
     static_libs: [
@@ -431,9 +447,17 @@
     {{- if .IsHostAndDeviceModule}}
     host_supported: true,
     {{- end}}
+    {{- if not .IsHostOnly}}
+    apex_available: [
+        "//apex_available:platform",
+        "//apex_available:anyapex",
+    ],
+    {{- end}}
     {{- if .IsAar}}
     min_sdk_version: "{{.MinSdkVersion}}",
     manifest: "manifests/{{.BpName}}/AndroidManifest.xml",
+    {{- else if not .IsHostOnly}}
+    min_sdk_version: "24",
     {{- end}}
     {{- end}}
     static_libs: [
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index bd1a9ed..905f206 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -18,12 +18,7 @@
 	"flag"
 	"fmt"
 	"os"
-	"os/exec"
 	"path/filepath"
-	"strconv"
-	"strings"
-	"syscall"
-	"time"
 
 	"github.com/google/blueprint/bootstrap"
 
@@ -55,42 +50,7 @@
 }
 
 func main() {
-	if android.SoongDelveListen != "" {
-		if android.SoongDelvePath == "" {
-			fmt.Fprintln(os.Stderr, "SOONG_DELVE is set but failed to find dlv")
-			os.Exit(1)
-		}
-		pid := strconv.Itoa(os.Getpid())
-		cmd := []string{android.SoongDelvePath,
-			"attach", pid,
-			"--headless",
-			"-l", android.SoongDelveListen,
-			"--api-version=2",
-			"--accept-multiclient",
-			"--log",
-		}
-
-		fmt.Println("Starting", strings.Join(cmd, " "))
-		dlv := exec.Command(cmd[0], cmd[1:]...)
-		dlv.Stdout = os.Stdout
-		dlv.Stderr = os.Stderr
-		dlv.Stdin = nil
-
-		// Put dlv into its own process group so we can kill it and the child process it starts.
-		dlv.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
-
-		err := dlv.Start()
-		if err != nil {
-			// Print the error starting dlv and continue.
-			fmt.Println(err)
-		} else {
-			// Kill the process group for dlv when soong_build exits.
-			defer syscall.Kill(-dlv.Process.Pid, syscall.SIGKILL)
-			// Wait to give dlv a chance to connect and pause the process.
-			time.Sleep(time.Second)
-		}
-	}
-
+	android.ReexecWithDelveMaybe()
 	flag.Parse()
 
 	// The top-level Blueprints file is passed as the first argument.
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index f1dde9c..de696da 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -82,7 +82,7 @@
 
 	if !dexpreoptDisabled(ctx, global, module) {
 		// Don't preopt individual boot jars, they will be preopted together.
-		if !contains(android.GetJarsFromApexJarPairs(global.BootJars), module.Name) {
+		if !contains(android.GetJarsFromApexJarPairs(ctx, global.BootJars), module.Name) {
 			appImage := (generateProfile || module.ForceCreateAppImage || global.DefaultAppImages) &&
 				!module.NoCreateAppImage
 
@@ -104,7 +104,7 @@
 
 	// Don't preopt system server jars that are updatable.
 	for _, p := range global.UpdatableSystemServerJars {
-		if _, jar := android.SplitApexJarPair(p); jar == module.Name {
+		if _, jar := android.SplitApexJarPair(ctx, p); jar == module.Name {
 			return true
 		}
 	}
@@ -113,7 +113,7 @@
 	// Also preopt system server jars since selinux prevents system server from loading anything from
 	// /data. If we don't do this they will need to be extracted which is not favorable for RAM usage
 	// or performance. If PreoptExtractedApk is true, we ignore the only preopt boot image options.
-	if global.OnlyPreoptBootImageAndSystemServer && !contains(android.GetJarsFromApexJarPairs(global.BootJars), module.Name) &&
+	if global.OnlyPreoptBootImageAndSystemServer && !contains(android.GetJarsFromApexJarPairs(ctx, global.BootJars), module.Name) &&
 		!contains(global.SystemServerJars, module.Name) && !module.PreoptExtractedApk {
 		return true
 	}
@@ -241,6 +241,10 @@
 	var conditionalClassLoaderContextHost29 android.Paths
 	var conditionalClassLoaderContextTarget29 []string
 
+	// Extra paths that will be appended to the class loader if the APK manifest has targetSdkVersion < 30
+	var conditionalClassLoaderContextHost30 android.Paths
+	var conditionalClassLoaderContextTarget30 []string
+
 	// A flag indicating if the '&' class loader context is used.
 	unknownClassLoaderContext := false
 
@@ -256,28 +260,22 @@
 				filepath.Join("/system/framework", l+".jar"))
 		}
 
+		// org.apache.http.legacy contains classes that were in the default classpath until API 28.
+		// If the targetSdkVersion in the manifest or APK is < 28, and the module does not explicitly
+		// depend on org.apache.http.legacy, then implicitly add it to the classpath for dexpreopt.
 		const httpLegacy = "org.apache.http.legacy"
-		const httpLegacyImpl = "org.apache.http.legacy.impl"
-
-		// org.apache.http.legacy contains classes that were in the default classpath until API 28.  If the
-		// targetSdkVersion in the manifest or APK is < 28, and the module does not explicitly depend on
-		// org.apache.http.legacy, then implicitly add the classes to the classpath for dexpreopt.  One the
-		// device the classes will be in a file called org.apache.http.legacy.impl.jar.
-		module.LibraryPaths[httpLegacyImpl] = module.LibraryPaths[httpLegacy]
-
-		if !contains(module.UsesLibraries, httpLegacy) && !contains(module.PresentOptionalUsesLibraries, httpLegacy) {
+		if !contains(usesLibs, httpLegacy) {
 			conditionalClassLoaderContextHost28 = append(conditionalClassLoaderContextHost28,
-				pathForLibrary(module, httpLegacyImpl))
+				pathForLibrary(module, httpLegacy))
 			conditionalClassLoaderContextTarget28 = append(conditionalClassLoaderContextTarget28,
-				filepath.Join("/system/framework", httpLegacyImpl+".jar"))
+				filepath.Join("/system/framework", httpLegacy+".jar"))
 		}
 
-		const hidlBase = "android.hidl.base-V1.0-java"
-		const hidlManager = "android.hidl.manager-V1.0-java"
-
 		// android.hidl.base-V1.0-java and android.hidl.manager-V1.0 contain classes that were in the default
 		// classpath until API 29.  If the targetSdkVersion in the manifest or APK is < 29 then implicitly add
 		// the classes to the classpath for dexpreopt.
+		const hidlBase = "android.hidl.base-V1.0-java"
+		const hidlManager = "android.hidl.manager-V1.0-java"
 		conditionalClassLoaderContextHost29 = append(conditionalClassLoaderContextHost29,
 			pathForLibrary(module, hidlManager))
 		conditionalClassLoaderContextTarget29 = append(conditionalClassLoaderContextTarget29,
@@ -286,6 +284,17 @@
 			pathForLibrary(module, hidlBase))
 		conditionalClassLoaderContextTarget29 = append(conditionalClassLoaderContextTarget29,
 			filepath.Join("/system/framework", hidlBase+".jar"))
+
+		// android.test.base contains classes that were in the default classpath until API 30.
+		// If the targetSdkVersion in the manifest or APK is < 30 then implicitly add it to the
+		// classpath for dexpreopt.
+		const testBase = "android.test.base"
+		if !contains(usesLibs, testBase) {
+			conditionalClassLoaderContextHost30 = append(conditionalClassLoaderContextHost30,
+				pathForLibrary(module, testBase))
+			conditionalClassLoaderContextTarget30 = append(conditionalClassLoaderContextTarget30,
+				filepath.Join("/system/framework", testBase+".jar"))
+		}
 	} else if jarIndex := android.IndexList(module.Name, systemServerJars); jarIndex >= 0 {
 		// System server jars should be dexpreopted together: class loader context of each jar
 		// should include all preceding jars on the system server classpath.
@@ -351,6 +360,11 @@
 			Implicits(conditionalClassLoaderContextHost29)
 		rule.Command().Textf(`conditional_target_libs_29="%s"`,
 			strings.Join(conditionalClassLoaderContextTarget29, " "))
+		rule.Command().Textf(`conditional_host_libs_30="%s"`,
+			strings.Join(conditionalClassLoaderContextHost30.Strings(), " ")).
+			Implicits(conditionalClassLoaderContextHost30)
+		rule.Command().Textf(`conditional_target_libs_30="%s"`,
+			strings.Join(conditionalClassLoaderContextTarget30, " "))
 		rule.Command().Text("source").Tool(globalSoong.ConstructContext).Input(module.DexPath)
 	}
 
@@ -561,8 +575,8 @@
 }
 
 // Expected format for apexJarValue = <apex name>:<jar name>
-func GetJarLocationFromApexJarPair(apexJarValue string) string {
-	apex, jar := android.SplitApexJarPair(apexJarValue)
+func GetJarLocationFromApexJarPair(ctx android.PathContext, apexJarValue string) string {
+	apex, jar := android.SplitApexJarPair(ctx, apexJarValue)
 	return filepath.Join("/apex", apex, "javalib", jar+".jar")
 }
 
@@ -573,7 +587,7 @@
 func NonUpdatableSystemServerJars(ctx android.PathContext, global *GlobalConfig) []string {
 	return ctx.Config().Once(nonUpdatableSystemServerJarsKey, func() interface{} {
 		return android.RemoveListFromList(global.SystemServerJars,
-			android.GetJarsFromApexJarPairs(global.UpdatableSystemServerJars))
+			android.GetJarsFromApexJarPairs(ctx, global.UpdatableSystemServerJars))
 	}).([]string)
 }
 
diff --git a/genrule/genrule.go b/genrule/genrule.go
index fe877fe..f6904f1 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -144,6 +144,9 @@
 
 	subName string
 	subDir  string
+
+	// Collect the module directory for IDE info in java/jdeps.go.
+	modulePaths []string
 }
 
 type taskFunc func(ctx android.ModuleContext, rawCommand string, srcFiles android.Paths) []generateTask
@@ -190,6 +193,9 @@
 func (g *Module) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	g.subName = ctx.ModuleSubDir()
 
+	// Collect the module directory for IDE info in java/jdeps.go.
+	g.modulePaths = append(g.modulePaths, ctx.ModuleDir())
+
 	if len(g.properties.Export_include_dirs) > 0 {
 		for _, dir := range g.properties.Export_include_dirs {
 			g.exportedIncludeDirs = append(g.exportedIncludeDirs,
@@ -529,6 +535,7 @@
 			dpInfo.Deps = append(dpInfo.Deps, src)
 		}
 	}
+	dpInfo.Paths = append(dpInfo.Paths, g.modulePaths...)
 }
 
 func (g *Module) AndroidMk() android.AndroidMkData {
diff --git a/java/aar.go b/java/aar.go
index 6e3b9e6..c8daf83 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -379,8 +379,11 @@
 				sharedLibs = append(sharedLibs, exportPackage)
 			}
 
-			if _, ok := module.(SdkLibraryDependency); ok {
-				sdkLibraries = append(sdkLibraries, ctx.OtherModuleName(module))
+			// If the module is (or possibly could be) a component of a java_sdk_library
+			// (including the java_sdk_library) itself then append any implicit sdk library
+			// names to the list of sdk libraries to be added to the manifest.
+			if component, ok := module.(SdkLibraryComponentDependency); ok {
+				sdkLibraries = append(sdkLibraries, component.OptionalImplicitSdkLibrary()...)
 			}
 
 		case frameworkResTag:
@@ -513,6 +516,7 @@
 
 	module.androidLibraryProperties.BuildAAR = true
 
+	android.InitApexModule(module)
 	InitJavaModule(module, android.DeviceSupported)
 	return module
 }
@@ -537,8 +541,12 @@
 type AARImport struct {
 	android.ModuleBase
 	android.DefaultableModuleBase
+	android.ApexModuleBase
 	prebuilt android.Prebuilt
 
+	// Functionality common to Module and Import.
+	embeddableInModuleAndImport
+
 	properties AARImportProperties
 
 	classpathFile         android.WritablePath
@@ -742,6 +750,10 @@
 	return nil, nil
 }
 
+func (a *AARImport) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
+	return a.depIsInSameApex(ctx, dep)
+}
+
 var _ android.PrebuiltInterface = (*Import)(nil)
 
 // android_library_import imports an `.aar` file into the build graph as if it was built with android_library.
@@ -754,6 +766,7 @@
 	module.AddProperties(&module.properties)
 
 	android.InitPrebuiltModule(module, &module.properties.Aars)
+	android.InitApexModule(module)
 	InitJavaModule(module, android.DeviceSupported)
 	return module
 }
diff --git a/java/androidmk.go b/java/androidmk.go
index 2c29192..62cf169 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -69,7 +69,26 @@
 	if !library.ApexModuleBase.AvailableFor(android.AvailableToPlatform) {
 		hideFromMake = true
 	}
-	if !hideFromMake {
+	if hideFromMake {
+		// May still need to add some additional dependencies. This will be called
+		// once for the platform variant (even if it is not being used) and once each
+		// for the APEX specific variants. In order to avoid adding the dependency
+		// multiple times only add it for the platform variant.
+		checkedModulePaths := library.additionalCheckedModules
+		if library.IsForPlatform() && len(checkedModulePaths) != 0 {
+			mainEntries = android.AndroidMkEntries{
+				Class: "FAKE",
+				// Need at least one output file in order for this to take effect.
+				OutputFile: android.OptionalPathForPath(checkedModulePaths[0]),
+				Include:    "$(BUILD_PHONY_PACKAGE)",
+				ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+					func(entries *android.AndroidMkEntries) {
+						entries.AddStrings("LOCAL_ADDITIONAL_CHECKED_MODULE", checkedModulePaths.Strings()...)
+					},
+				},
+			}
+		}
+	} else {
 		mainEntries = android.AndroidMkEntries{
 			Class:      "JAVA_LIBRARIES",
 			DistFile:   android.OptionalPathForPath(library.distFile),
@@ -211,6 +230,11 @@
 }
 
 func (prebuilt *AARImport) AndroidMkEntries() []android.AndroidMkEntries {
+	if !prebuilt.IsForPlatform() {
+		return []android.AndroidMkEntries{{
+			Disabled: true,
+		}}
+	}
 	return []android.AndroidMkEntries{android.AndroidMkEntries{
 		Class:      "JAVA_LIBRARIES",
 		OutputFile: android.OptionalPathForPath(prebuilt.classpathFile),
@@ -416,6 +440,11 @@
 }
 
 func (a *AndroidLibrary) AndroidMkEntries() []android.AndroidMkEntries {
+	if !a.IsForPlatform() {
+		return []android.AndroidMkEntries{{
+			Disabled: true,
+		}}
+	}
 	entriesList := a.Library.AndroidMkEntries()
 	entries := &entriesList[0]
 
@@ -502,14 +531,12 @@
 					fmt.Fprintln(w, ddoc.Name()+"-check-last-released-api:",
 						ddoc.checkLastReleasedApiTimestamp.String())
 
-					if ddoc.Name() == "api-stubs-docs" || ddoc.Name() == "system-api-stubs-docs" {
-						fmt.Fprintln(w, ".PHONY: checkapi")
-						fmt.Fprintln(w, "checkapi:",
-							ddoc.checkLastReleasedApiTimestamp.String())
+					fmt.Fprintln(w, ".PHONY: checkapi")
+					fmt.Fprintln(w, "checkapi:",
+						ddoc.checkLastReleasedApiTimestamp.String())
 
-						fmt.Fprintln(w, ".PHONY: droidcore")
-						fmt.Fprintln(w, "droidcore: checkapi")
-					}
+					fmt.Fprintln(w, ".PHONY: droidcore")
+					fmt.Fprintln(w, "droidcore: checkapi")
 				}
 			},
 		},
@@ -681,3 +708,20 @@
 		},
 	}}
 }
+
+func (apkSet *AndroidAppSet) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{
+		android.AndroidMkEntries{
+			Class:      "APPS",
+			OutputFile: android.OptionalPathForPath(apkSet.packedOutput),
+			Include:    "$(BUILD_SYSTEM)/soong_android_app_set.mk",
+			ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+				func(entries *android.AndroidMkEntries) {
+					entries.SetBoolIfTrue("LOCAL_PRIVILEGED_MODULE", apkSet.Privileged())
+					entries.SetString("LOCAL_APK_SET_MASTER_FILE", apkSet.masterFile)
+					entries.AddStrings("LOCAL_OVERRIDES_PACKAGES", apkSet.properties.Overrides...)
+				},
+			},
+		},
+	}
+}
diff --git a/java/app.go b/java/app.go
index f1af2ad..a45ab6f 100755
--- a/java/app.go
+++ b/java/app.go
@@ -20,6 +20,7 @@
 	"path/filepath"
 	"reflect"
 	"sort"
+	"strconv"
 	"strings"
 
 	"github.com/google/blueprint"
@@ -49,6 +50,127 @@
 	ctx.RegisterModuleType("android_app_import", AndroidAppImportFactory)
 	ctx.RegisterModuleType("android_test_import", AndroidTestImportFactory)
 	ctx.RegisterModuleType("runtime_resource_overlay", RuntimeResourceOverlayFactory)
+	ctx.RegisterModuleType("android_app_set", AndroidApkSetFactory)
+}
+
+type AndroidAppSetProperties struct {
+	// APK Set path
+	Set *string
+
+	// Specifies that this app should be installed to the priv-app directory,
+	// where the system will grant it additional privileges not available to
+	// normal apps.
+	Privileged *bool
+
+	// APKs in this set use prerelease SDK version
+	Prerelease *bool
+
+	// Names of modules to be overridden. Listed modules can only be other apps
+	//	(in Make or Soong).
+	Overrides []string
+}
+
+type AndroidAppSet struct {
+	android.ModuleBase
+	android.DefaultableModuleBase
+	prebuilt android.Prebuilt
+
+	properties   AndroidAppSetProperties
+	packedOutput android.WritablePath
+	masterFile   string
+}
+
+func (as *AndroidAppSet) Name() string {
+	return as.prebuilt.Name(as.ModuleBase.Name())
+}
+
+func (as *AndroidAppSet) IsInstallable() bool {
+	return true
+}
+
+func (as *AndroidAppSet) Prebuilt() *android.Prebuilt {
+	return &as.prebuilt
+}
+
+func (as *AndroidAppSet) Privileged() bool {
+	return Bool(as.properties.Privileged)
+}
+
+var TargetCpuAbi = map[string]string{
+	"arm":    "ARMEABI_V7A",
+	"arm64":  "ARM64_V8A",
+	"x86":    "X86",
+	"x86_64": "X86_64",
+}
+
+func SupportedAbis(ctx android.ModuleContext) []string {
+	abiName := func(archVar string, deviceArch string) string {
+		if abi, found := TargetCpuAbi[deviceArch]; found {
+			return abi
+		}
+		ctx.ModuleErrorf("Invalid %s: %s", archVar, deviceArch)
+		return "BAD_ABI"
+	}
+
+	result := []string{abiName("TARGET_ARCH", ctx.DeviceConfig().DeviceArch())}
+	if s := ctx.DeviceConfig().DeviceSecondaryArch(); s != "" {
+		result = append(result, abiName("TARGET_2ND_ARCH", s))
+	}
+	return result
+}
+
+func (as *AndroidAppSet) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	as.packedOutput = android.PathForModuleOut(ctx, "extracted.zip")
+	// We are assuming here that the master file in the APK
+	// set has `.apk` suffix. If it doesn't the build will fail.
+	// APK sets containing APEX files are handled elsewhere.
+	as.masterFile = ctx.ModuleName() + ".apk"
+	screenDensities := "all"
+	if dpis := ctx.Config().ProductAAPTPrebuiltDPI(); len(dpis) > 0 {
+		screenDensities = strings.ToUpper(strings.Join(dpis, ","))
+	}
+	// TODO(asmundak): handle locales.
+	// TODO(asmundak): do we support device features
+	ctx.Build(pctx,
+		android.BuildParams{
+			Rule:        extractMatchingApks,
+			Description: "Extract APKs from APK set",
+			Output:      as.packedOutput,
+			Inputs:      android.Paths{as.prebuilt.SingleSourcePath(ctx)},
+			Args: map[string]string{
+				"abis":              strings.Join(SupportedAbis(ctx), ","),
+				"allow-prereleased": strconv.FormatBool(proptools.Bool(as.properties.Prerelease)),
+				"screen-densities":  screenDensities,
+				"sdk-version":       ctx.Config().PlatformSdkVersion(),
+				"stem":              ctx.ModuleName(),
+			},
+		})
+	// TODO(asmundak): add this (it's wrong now, will cause copying extracted.zip)
+	/*
+		var installDir android.InstallPath
+		if Bool(as.properties.Privileged) {
+			installDir = android.PathForModuleInstall(ctx, "priv-app", as.BaseModuleName())
+		} else if ctx.InstallInTestcases() {
+			installDir = android.PathForModuleInstall(ctx, as.BaseModuleName(), ctx.DeviceConfig().DeviceArch())
+		} else {
+			installDir = android.PathForModuleInstall(ctx, "app", as.BaseModuleName())
+		}
+		ctx.InstallFile(installDir, as.masterFile", as.packedOutput)
+	*/
+}
+
+// android_app_set extracts a set of APKs based on the target device
+// configuration and installs this set as "split APKs".
+// The set will always contain `base-master.apk` and every APK built
+// to the target device. All density-specific APK will be included, too,
+// unless PRODUCT_APPT_PREBUILT_DPI is defined (should contain comma-sepearated
+// list of density names (LDPI, MDPI, HDPI, etc.)
+func AndroidApkSetFactory() android.Module {
+	module := &AndroidAppSet{}
+	module.AddProperties(&module.properties)
+	InitJavaModule(module, android.DeviceSupported)
+	android.InitSingleSourcePrebuiltModule(module, &module.properties, "Set")
+	return module
 }
 
 // AndroidManifest.xml merging
@@ -129,6 +251,9 @@
 	// or an android_app_certificate module name in the form ":module".
 	Certificate *string
 
+	// Name of the signing certificate lineage file.
+	Lineage *string
+
 	// the package name of this app. The package name in the manifest file is used if one was not given.
 	Package_name *string
 
@@ -249,7 +374,10 @@
 
 		// If the app builds against an Android SDK use the SDK variant of JNI dependencies
 		// unless jni_uses_platform_apis is set.
-		if (usesSDK && !Bool(a.appProperties.Jni_uses_platform_apis)) ||
+		// Don't require the SDK variant for apps that are shipped on vendor, etc., as they already
+		// have stable APIs through the VNDK.
+		if (usesSDK && !a.RequiresStableAPIs(ctx) &&
+			!Bool(a.appProperties.Jni_uses_platform_apis)) ||
 			Bool(a.appProperties.Jni_uses_sdk_apis) {
 			variation = append(variation, blueprint.Variation{Mutator: "sdk", Variation: "sdk"})
 		}
@@ -286,19 +414,47 @@
 }
 
 func (a *AndroidApp) checkAppSdkVersions(ctx android.ModuleContext) {
-	if Bool(a.appProperties.Updatable) {
+	if a.Updatable() {
 		if !a.sdkVersion().stable() {
 			ctx.PropertyErrorf("sdk_version", "Updatable apps must use stable SDKs, found %v", a.sdkVersion())
 		}
 		if String(a.deviceProperties.Min_sdk_version) == "" {
 			ctx.PropertyErrorf("updatable", "updatable apps must set min_sdk_version.")
 		}
+		if minSdkVersion, err := a.minSdkVersion().effectiveVersion(ctx); err == nil {
+			a.checkJniLibsSdkVersion(ctx, minSdkVersion)
+		} else {
+			ctx.PropertyErrorf("min_sdk_version", "%s", err.Error())
+		}
 	}
 
 	a.checkPlatformAPI(ctx)
 	a.checkSdkVersions(ctx)
 }
 
+// If an updatable APK sets min_sdk_version, min_sdk_vesion of JNI libs should match with it.
+// This check is enforced for "updatable" APKs (including APK-in-APEX).
+// b/155209650: until min_sdk_version is properly supported, use sdk_version instead.
+// because, sdk_version is overridden by min_sdk_version (if set as smaller)
+// and linkType is checked with dependencies so we can be sure that the whole dependency tree
+// will meet the requirements.
+func (a *AndroidApp) checkJniLibsSdkVersion(ctx android.ModuleContext, minSdkVersion sdkVersion) {
+	// It's enough to check direct JNI deps' sdk_version because all transitive deps from JNI deps are checked in cc.checkLinkType()
+	ctx.VisitDirectDeps(func(m android.Module) {
+		if !IsJniDepTag(ctx.OtherModuleDependencyTag(m)) {
+			return
+		}
+		dep, _ := m.(*cc.Module)
+		jniSdkVersion, err := android.ApiStrToNum(ctx, dep.SdkVersion())
+		if err != nil || int(minSdkVersion) < jniSdkVersion {
+			ctx.OtherModuleErrorf(dep, "sdk_version(%v) is higher than min_sdk_version(%v) of the containing android_app(%v)",
+				dep.SdkVersion(), minSdkVersion, ctx.ModuleName())
+			return
+		}
+
+	})
+}
+
 // Returns true if the native libraries should be stored in the APK uncompressed and the
 // extractNativeLibs application flag should be set to false in the manifest.
 func (a *AndroidApp) useEmbeddedNativeLibs(ctx android.ModuleContext) bool {
@@ -412,16 +568,17 @@
 		installDir = filepath.Join("app", a.installApkName)
 	}
 	a.dexpreopter.installPath = android.PathForModuleInstall(ctx, installDir, a.installApkName+".apk")
-	a.dexpreopter.uncompressedDex = a.shouldUncompressDex(ctx)
-
+	if a.deviceProperties.Uncompress_dex == nil {
+		// If the value was not force-set by the user, use reasonable default based on the module.
+		a.deviceProperties.Uncompress_dex = proptools.BoolPtr(a.shouldUncompressDex(ctx))
+	}
+	a.dexpreopter.uncompressedDex = *a.deviceProperties.Uncompress_dex
 	a.dexpreopter.enforceUsesLibs = a.usesLibrary.enforceUsesLibraries()
 	a.dexpreopter.usesLibs = a.usesLibrary.usesLibraryProperties.Uses_libs
 	a.dexpreopter.optionalUsesLibs = a.usesLibrary.presentOptionalUsesLibs(ctx)
 	a.dexpreopter.libraryPaths = a.usesLibrary.usesLibraryPaths(ctx)
 	a.dexpreopter.manifestFile = a.mergedManifestFile
 
-	a.deviceProperties.UncompressDex = a.dexpreopter.uncompressedDex
-
 	if ctx.ModuleName() != "framework-res" {
 		a.Module.compile(ctx, a.aaptSrcJar)
 	}
@@ -584,7 +741,7 @@
 
 	dexJarFile := a.dexBuildActions(ctx)
 
-	jniLibs, certificateDeps := collectAppDeps(ctx, a.shouldEmbedJnis(ctx), !Bool(a.appProperties.Jni_uses_platform_apis))
+	jniLibs, certificateDeps := collectAppDeps(ctx, a, a.shouldEmbedJnis(ctx), !Bool(a.appProperties.Jni_uses_platform_apis))
 	jniJarFile := a.jniBuildActions(jniLibs, ctx)
 
 	if ctx.Failed() {
@@ -596,13 +753,17 @@
 
 	// Build a final signed app package.
 	packageFile := android.PathForModuleOut(ctx, a.installApkName+".apk")
-	CreateAndSignAppPackage(ctx, packageFile, a.exportPackage, jniJarFile, dexJarFile, certificates, apkDeps)
+	var lineageFile android.Path
+	if lineage := String(a.overridableAppProperties.Lineage); lineage != "" {
+		lineageFile = android.PathForModuleSrc(ctx, lineage)
+	}
+	CreateAndSignAppPackage(ctx, packageFile, a.exportPackage, jniJarFile, dexJarFile, certificates, apkDeps, lineageFile)
 	a.outputFile = packageFile
 
 	for _, split := range a.aapt.splits {
 		// Sign the split APKs
 		packageFile := android.PathForModuleOut(ctx, a.installApkName+"_"+split.suffix+".apk")
-		CreateAndSignAppPackage(ctx, packageFile, split.path, nil, nil, certificates, apkDeps)
+		CreateAndSignAppPackage(ctx, packageFile, split.path, nil, nil, certificates, apkDeps, lineageFile)
 		a.extraOutputFiles = append(a.extraOutputFiles, packageFile)
 	}
 
@@ -622,12 +783,25 @@
 	a.buildAppDependencyInfo(ctx)
 }
 
-func collectAppDeps(ctx android.ModuleContext, shouldCollectRecursiveNativeDeps bool,
+type appDepsInterface interface {
+	sdkVersion() sdkSpec
+	minSdkVersion() sdkSpec
+	RequiresStableAPIs(ctx android.BaseModuleContext) bool
+}
+
+func collectAppDeps(ctx android.ModuleContext, app appDepsInterface,
+	shouldCollectRecursiveNativeDeps bool,
 	checkNativeSdkVersion bool) ([]jniLib, []Certificate) {
+
 	var jniLibs []jniLib
 	var certificates []Certificate
 	seenModulePaths := make(map[string]bool)
 
+	if checkNativeSdkVersion {
+		checkNativeSdkVersion = app.sdkVersion().specified() &&
+			app.sdkVersion().kind != sdkCorePlatform && !app.RequiresStableAPIs(ctx)
+	}
+
 	ctx.WalkDeps(func(module android.Module, parent android.Module) bool {
 		otherName := ctx.OtherModuleName(module)
 		tag := ctx.OtherModuleDependencyTag(module)
@@ -645,16 +819,9 @@
 				}
 				seenModulePaths[path.String()] = true
 
-				if checkNativeSdkVersion {
-					if app, ok := ctx.Module().(interface{ sdkVersion() sdkSpec }); ok {
-						if app.sdkVersion().specified() &&
-							app.sdkVersion().kind != sdkCorePlatform &&
-							dep.SdkVersion() == "" {
-							ctx.PropertyErrorf("jni_libs",
-								"JNI dependency %q uses platform APIs, but this module does not",
-								otherName)
-						}
-					}
+				if checkNativeSdkVersion && dep.SdkVersion() == "" {
+					ctx.PropertyErrorf("jni_libs", "JNI dependency %q uses platform APIs, but this module does not",
+						otherName)
 				}
 
 				if lib.Valid() {
@@ -731,6 +898,10 @@
 	a.ApexBundleDepsInfo.BuildDepsInfoLists(ctx, a.MinSdkVersion(), depsInfo)
 }
 
+func (a *AndroidApp) Updatable() bool {
+	return Bool(a.appProperties.Updatable) || a.ApexModuleBase.Updatable()
+}
+
 func (a *AndroidApp) getCertString(ctx android.BaseModuleContext) string {
 	certificate, overridden := ctx.DeviceConfig().OverrideCertificateFor(ctx.ModuleName())
 	if overridden {
@@ -835,6 +1006,7 @@
 }
 
 func (a *AndroidTest) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	var configs []tradefed.Config
 	if a.appTestProperties.Instrumentation_target_package != nil {
 		a.additionalAaptFlags = append(a.additionalAaptFlags,
 			"--rename-instrumentation-target-package "+*a.appTestProperties.Instrumentation_target_package)
@@ -847,8 +1019,12 @@
 	}
 	a.generateAndroidBuildActions(ctx)
 
+	for _, module := range a.testProperties.Test_mainline_modules {
+		configs = append(configs, tradefed.Option{Name: "config-descriptor:metadata", Key: "mainline-param", Value: module})
+	}
+
 	testConfig := tradefed.AutoGenInstrumentationTestConfig(ctx, a.testProperties.Test_config,
-		a.testProperties.Test_config_template, a.manifestPath, a.testProperties.Test_suites, a.testProperties.Auto_gen_config)
+		a.testProperties.Test_config_template, a.manifestPath, a.testProperties.Test_suites, a.testProperties.Auto_gen_config, configs)
 	a.testConfig = a.FixTestConfig(ctx, testConfig)
 	a.data = android.PathsForModuleSrc(ctx, a.testProperties.Data)
 }
@@ -1086,6 +1262,8 @@
 
 	usesLibrary usesLibrary
 
+	preprocessed bool
+
 	installPath android.InstallPath
 }
 
@@ -1101,6 +1279,9 @@
 	// be set for presigned modules.
 	Presigned *bool
 
+	// Name of the signing certificate lineage file.
+	Lineage *string
+
 	// Sign with the default system dev certificate. Must be used judiciously. Most imported apps
 	// need to either specify a specific certificate or be presigned.
 	Default_dev_cert *bool
@@ -1175,7 +1356,7 @@
 	ctx android.ModuleContext, inputPath android.Path, outputPath android.OutputPath) {
 	// Test apps don't need their JNI libraries stored uncompressed. As a matter of fact, messing
 	// with them may invalidate pre-existing signature data.
-	if ctx.InstallInTestcases() && Bool(a.properties.Presigned) {
+	if ctx.InstallInTestcases() && (Bool(a.properties.Presigned) || a.preprocessed) {
 		ctx.Build(pctx, android.BuildParams{
 			Rule:   android.Cp,
 			Output: outputPath,
@@ -1196,7 +1377,7 @@
 
 // Returns whether this module should have the dex file stored uncompressed in the APK.
 func (a *AndroidAppImport) shouldUncompressDex(ctx android.ModuleContext) bool {
-	if ctx.Config().UnbundledBuild() {
+	if ctx.Config().UnbundledBuild() || a.preprocessed {
 		return false
 	}
 
@@ -1244,7 +1425,7 @@
 		ctx.ModuleErrorf("One and only one of certficate, presigned, and default_dev_cert properties must be set")
 	}
 
-	_, certificates := collectAppDeps(ctx, false, false)
+	_, certificates := collectAppDeps(ctx, a, false, false)
 
 	// TODO: LOCAL_EXTRACT_APK/LOCAL_EXTRACT_DPI_APK
 	// TODO: LOCAL_PACKAGE_SPLITS
@@ -1288,9 +1469,13 @@
 
 	apkFilename := proptools.StringDefault(a.properties.Filename, a.BaseModuleName()+".apk")
 
-	// Sign or align the package
 	// TODO: Handle EXTERNAL
-	if !Bool(a.properties.Presigned) {
+
+	// Sign or align the package if package has not been preprocessed
+	if a.preprocessed {
+		a.outputFile = srcApk
+		a.certificate = presignedCertificate
+	} else if !Bool(a.properties.Presigned) {
 		// If the certificate property is empty at this point, default_dev_cert must be set to true.
 		// Which makes processMainCert's behavior for the empty cert string WAI.
 		certificates = processMainCert(a.ModuleBase, String(a.properties.Certificate), certificates, ctx)
@@ -1299,7 +1484,11 @@
 		}
 		a.certificate = certificates[0]
 		signed := android.PathForModuleOut(ctx, "signed", apkFilename)
-		SignAppPackage(ctx, signed, dexOutput, certificates)
+		var lineageFile android.Path
+		if lineage := String(a.properties.Lineage); lineage != "" {
+			lineageFile = android.PathForModuleSrc(ctx, lineage)
+		}
+		SignAppPackage(ctx, signed, dexOutput, certificates, lineageFile)
 		a.outputFile = signed
 	} else {
 		alignedApk := android.PathForModuleOut(ctx, "zip-aligned", apkFilename)
@@ -1370,6 +1559,14 @@
 	return false
 }
 
+func (a *AndroidAppImport) sdkVersion() sdkSpec {
+	return sdkSpecFrom("")
+}
+
+func (a *AndroidAppImport) minSdkVersion() sdkSpec {
+	return sdkSpecFrom("")
+}
+
 func createVariantGroupType(variants []string, variantGroupName string) reflect.Type {
 	props := reflect.TypeOf((*AndroidAppImportProperties)(nil))
 
@@ -1417,21 +1614,31 @@
 	})
 
 	android.InitApexModule(module)
-	InitJavaModule(module, android.DeviceSupported)
+	android.InitAndroidMultiTargetsArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	android.InitDefaultableModule(module)
 	android.InitSingleSourcePrebuiltModule(module, &module.properties, "Apk")
 
 	return module
 }
 
+type androidTestImportProperties struct {
+	// Whether the prebuilt apk can be installed without additional processing. Default is false.
+	Preprocessed *bool
+}
+
 type AndroidTestImport struct {
 	AndroidAppImport
 
 	testProperties testProperties
 
+	testImportProperties androidTestImportProperties
+
 	data android.Paths
 }
 
 func (a *AndroidTestImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	a.preprocessed = Bool(a.testImportProperties.Preprocessed)
+
 	a.generateAndroidBuildActions(ctx)
 
 	a.data = android.PathsForModuleSrc(ctx, a.testProperties.Data)
@@ -1449,6 +1656,7 @@
 	module.AddProperties(&module.dexpreoptProperties)
 	module.AddProperties(&module.usesLibrary.usesLibraryProperties)
 	module.AddProperties(&module.testProperties)
+	module.AddProperties(&module.testImportProperties)
 	module.populateAllVariantStructs()
 	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
 		module.processVariants(ctx)
@@ -1484,6 +1692,9 @@
 	// module name in the form ":module".
 	Certificate *string
 
+	// Name of the signing certificate lineage file.
+	Lineage *string
+
 	// optional theme name. If specified, the overlay package will be applied
 	// only when the ro.boot.vendor.overlay.theme system property is set to the same value.
 	Theme *string
@@ -1555,10 +1766,14 @@
 	r.aapt.buildActions(ctx, r, aaptLinkFlags...)
 
 	// Sign the built package
-	_, certificates := collectAppDeps(ctx, false, false)
+	_, certificates := collectAppDeps(ctx, r, false, false)
 	certificates = processMainCert(r.ModuleBase, String(r.properties.Certificate), certificates, ctx)
 	signed := android.PathForModuleOut(ctx, "signed", r.Name()+".apk")
-	SignAppPackage(ctx, signed, r.aapt.exportPackage, certificates)
+	var lineageFile android.Path
+	if lineage := String(r.properties.Lineage); lineage != "" {
+		lineageFile = android.PathForModuleSrc(ctx, lineage)
+	}
+	SignAppPackage(ctx, signed, r.aapt.exportPackage, certificates, lineageFile)
 	r.certificate = certificates[0]
 
 	r.outputFile = signed
@@ -1648,6 +1863,7 @@
 				"org.apache.http.legacy",
 				"android.hidl.base-V1.0-java",
 				"android.hidl.manager-V1.0-java")
+			ctx.AddVariationDependencies(nil, usesLibTag, optionalUsesLibs...)
 		}
 	}
 }
diff --git a/java/app_builder.go b/java/app_builder.go
index 5e7fbe6..014bd54 100644
--- a/java/app_builder.go
+++ b/java/app_builder.go
@@ -26,16 +26,23 @@
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
+	"android/soong/remoteexec"
 )
 
 var (
-	Signapk = pctx.AndroidStaticRule("signapk",
+	Signapk, SignapkRE = remoteexec.StaticRules(pctx, "signapk",
 		blueprint.RuleParams{
-			Command: `${config.JavaCmd} ${config.JavaVmFlags} -Djava.library.path=$$(dirname ${config.SignapkJniLibrary}) ` +
+			Command: `$reTemplate${config.JavaCmd} ${config.JavaVmFlags} -Djava.library.path=$$(dirname ${config.SignapkJniLibrary}) ` +
 				`-jar ${config.SignapkCmd} $flags $certificates $in $out`,
 			CommandDeps: []string{"${config.SignapkCmd}", "${config.SignapkJniLibrary}"},
 		},
-		"flags", "certificates")
+		&remoteexec.REParams{Labels: map[string]string{"type": "tool", "name": "signapk"},
+			ExecStrategy:    "${config.RESignApkExecStrategy}",
+			Inputs:          []string{"${config.SignapkCmd}", "$in", "$$(dirname ${config.SignapkJniLibrary})", "$implicits"},
+			OutputFiles:     []string{"$outCommaList"},
+			ToolchainInputs: []string{"${config.JavaCmd}"},
+			Platform:        map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+		}, []string{"flags", "certificates"}, []string{"implicits", "outCommaList"})
 )
 
 var combineApk = pctx.AndroidStaticRule("combineApk",
@@ -45,7 +52,7 @@
 	})
 
 func CreateAndSignAppPackage(ctx android.ModuleContext, outputFile android.WritablePath,
-	packageFile, jniJarFile, dexJarFile android.Path, certificates []Certificate, deps android.Paths) {
+	packageFile, jniJarFile, dexJarFile android.Path, certificates []Certificate, deps android.Paths, lineageFile android.Path) {
 
 	unsignedApkName := strings.TrimSuffix(outputFile.Base(), ".apk") + "-unsigned.apk"
 	unsignedApk := android.PathForModuleOut(ctx, unsignedApkName)
@@ -66,10 +73,10 @@
 		Implicits: deps,
 	})
 
-	SignAppPackage(ctx, outputFile, unsignedApk, certificates)
+	SignAppPackage(ctx, outputFile, unsignedApk, certificates, lineageFile)
 }
 
-func SignAppPackage(ctx android.ModuleContext, signedApk android.WritablePath, unsignedApk android.Path, certificates []Certificate) {
+func SignAppPackage(ctx android.ModuleContext, signedApk android.WritablePath, unsignedApk android.Path, certificates []Certificate, lineageFile android.Path) {
 
 	var certificateArgs []string
 	var deps android.Paths
@@ -78,15 +85,30 @@
 		deps = append(deps, c.Pem, c.Key)
 	}
 
+	outputFiles := android.WritablePaths{signedApk}
+	var flags []string
+	if lineageFile != nil {
+		flags = append(flags, "--lineage", lineageFile.String())
+		deps = append(deps, lineageFile)
+	}
+
+	rule := Signapk
+	args := map[string]string{
+		"certificates": strings.Join(certificateArgs, " "),
+		"flags":        strings.Join(flags, " "),
+	}
+	if ctx.Config().IsEnvTrue("RBE_SIGNAPK") {
+		rule = SignapkRE
+		args["implicits"] = strings.Join(deps.Strings(), ",")
+		args["outCommaList"] = strings.Join(outputFiles.Strings(), ",")
+	}
 	ctx.Build(pctx, android.BuildParams{
-		Rule:        Signapk,
+		Rule:        rule,
 		Description: "signapk",
-		Output:      signedApk,
+		Outputs:     outputFiles,
 		Input:       unsignedApk,
 		Implicits:   deps,
-		Args: map[string]string{
-			"certificates": strings.Join(certificateArgs, " "),
-		},
+		Args:        args,
 	})
 }
 
@@ -210,14 +232,20 @@
 			"-f", j.path.String())
 	}
 
+	rule := zip
+	args := map[string]string{
+		"jarArgs": strings.Join(proptools.NinjaAndShellEscapeList(jarArgs), " "),
+	}
+	if ctx.Config().IsEnvTrue("RBE_ZIP") {
+		rule = zipRE
+		args["implicits"] = strings.Join(deps.Strings(), ",")
+	}
 	ctx.Build(pctx, android.BuildParams{
-		Rule:        zip,
+		Rule:        rule,
 		Description: "zip jni libs",
 		Output:      outputFile,
 		Implicits:   deps,
-		Args: map[string]string{
-			"jarArgs": strings.Join(proptools.NinjaAndShellEscapeList(jarArgs), " "),
-		},
+		Args:        args,
 	})
 }
 
diff --git a/java/app_test.go b/java/app_test.go
index 4bcfa5a..eb583be 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -141,6 +141,94 @@
 	}
 }
 
+func TestAndroidAppSet(t *testing.T) {
+	ctx, config := testJava(t, `
+		android_app_set {
+			name: "foo",
+			set: "prebuilts/apks/app.apks",
+			prerelease: true,
+        }`)
+	module := ctx.ModuleForTests("foo", "android_common")
+	const packedSplitApks = "extracted.zip"
+	params := module.Output(packedSplitApks)
+	if params.Rule == nil {
+		t.Errorf("expected output %s is missing", packedSplitApks)
+	}
+	if s := params.Args["allow-prereleased"]; s != "true" {
+		t.Errorf("wrong allow-prereleased value: '%s', expected 'true'", s)
+	}
+	mkEntries := android.AndroidMkEntriesForTest(t, config, "", module.Module())[0]
+	actualMaster := mkEntries.EntryMap["LOCAL_APK_SET_MASTER_FILE"]
+	expectedMaster := []string{"foo.apk"}
+	if !reflect.DeepEqual(actualMaster, expectedMaster) {
+		t.Errorf("Unexpected LOCAL_APK_SET_MASTER_FILE value: '%s', expected: '%s',",
+			actualMaster, expectedMaster)
+	}
+}
+
+func TestAndroidAppSet_Variants(t *testing.T) {
+	bp := `
+		android_app_set {
+			name: "foo",
+			set: "prebuilts/apks/app.apks",
+		}`
+	testCases := []struct {
+		name                string
+		deviceArch          *string
+		deviceSecondaryArch *string
+		aaptPrebuiltDPI     []string
+		sdkVersion          int
+		expected            map[string]string
+	}{
+		{
+			name:            "One",
+			deviceArch:      proptools.StringPtr("x86"),
+			aaptPrebuiltDPI: []string{"ldpi", "xxhdpi"},
+			sdkVersion:      29,
+			expected: map[string]string{
+				"abis":              "X86",
+				"allow-prereleased": "false",
+				"screen-densities":  "LDPI,XXHDPI",
+				"sdk-version":       "29",
+				"stem":              "foo",
+			},
+		},
+		{
+			name:                "Two",
+			deviceArch:          proptools.StringPtr("x86_64"),
+			deviceSecondaryArch: proptools.StringPtr("x86"),
+			aaptPrebuiltDPI:     nil,
+			sdkVersion:          30,
+			expected: map[string]string{
+				"abis":              "X86_64,X86",
+				"allow-prereleased": "false",
+				"screen-densities":  "all",
+				"sdk-version":       "30",
+				"stem":              "foo",
+			},
+		},
+	}
+
+	for _, test := range testCases {
+		config := testAppConfig(nil, bp, nil)
+		config.TestProductVariables.AAPTPrebuiltDPI = test.aaptPrebuiltDPI
+		config.TestProductVariables.Platform_sdk_version = &test.sdkVersion
+		config.TestProductVariables.DeviceArch = test.deviceArch
+		config.TestProductVariables.DeviceSecondaryArch = test.deviceSecondaryArch
+		ctx := testContext()
+		run(t, ctx, config)
+		module := ctx.ModuleForTests("foo", "android_common")
+		const packedSplitApks = "extracted.zip"
+		params := module.Output(packedSplitApks)
+		for k, v := range test.expected {
+			if actual := params.Args[k]; actual != v {
+				t.Errorf("%s: bad build arg value for '%s': '%s', expected '%s'",
+					test.name, k, actual, v)
+			}
+		}
+	}
+}
+
 func TestPlatformAPIs(t *testing.T) {
 	testJava(t, `
 		android_app {
@@ -385,6 +473,127 @@
 	}
 }
 
+func TestUpdatableApps_JniLibsShouldShouldSupportMinSdkVersion(t *testing.T) {
+	testJava(t, cc.GatherRequiredDepsForTest(android.Android)+`
+		android_app {
+			name: "foo",
+			srcs: ["a.java"],
+			updatable: true,
+			sdk_version: "current",
+			min_sdk_version: "current",
+			jni_libs: ["libjni"],
+		}
+
+		cc_library {
+			name: "libjni",
+			stl: "none",
+			system_shared_libs: [],
+			sdk_version: "current",
+		}
+	`)
+}
+
+func TestUpdatableApps_JniLibShouldBeBuiltAgainstMinSdkVersion(t *testing.T) {
+	bp := cc.GatherRequiredDepsForTest(android.Android) + `
+		android_app {
+			name: "foo",
+			srcs: ["a.java"],
+			updatable: true,
+			sdk_version: "current",
+			min_sdk_version: "29",
+			jni_libs: ["libjni"],
+		}
+
+		cc_library {
+			name: "libjni",
+			stl: "none",
+			system_shared_libs: [],
+			sdk_version: "29",
+		}
+
+		ndk_prebuilt_object {
+			name: "ndk_crtbegin_so.29",
+			sdk_version: "29",
+		}
+
+		ndk_prebuilt_object {
+			name: "ndk_crtend_so.29",
+			sdk_version: "29",
+		}
+	`
+	fs := map[string][]byte{
+		"prebuilts/ndk/current/platforms/android-29/arch-arm64/usr/lib/crtbegin_so.o": nil,
+		"prebuilts/ndk/current/platforms/android-29/arch-arm64/usr/lib/crtend_so.o":   nil,
+		"prebuilts/ndk/current/platforms/android-29/arch-arm/usr/lib/crtbegin_so.o":   nil,
+		"prebuilts/ndk/current/platforms/android-29/arch-arm/usr/lib/crtend_so.o":     nil,
+	}
+
+	ctx, _ := testJavaWithConfig(t, testConfig(nil, bp, fs))
+
+	inputs := ctx.ModuleForTests("libjni", "android_arm64_armv8-a_sdk_shared").Description("link").Implicits
+	var crtbeginFound, crtendFound bool
+	for _, input := range inputs {
+		switch input.String() {
+		case "prebuilts/ndk/current/platforms/android-29/arch-arm64/usr/lib/crtbegin_so.o":
+			crtbeginFound = true
+		case "prebuilts/ndk/current/platforms/android-29/arch-arm64/usr/lib/crtend_so.o":
+			crtendFound = true
+		}
+	}
+	if !crtbeginFound || !crtendFound {
+		t.Error("should link with ndk_crtbegin_so.29 and ndk_crtend_so.29")
+	}
+}
+
+func TestUpdatableApps_ErrorIfJniLibDoesntSupportMinSdkVersion(t *testing.T) {
+	bp := cc.GatherRequiredDepsForTest(android.Android) + `
+		android_app {
+			name: "foo",
+			srcs: ["a.java"],
+			updatable: true,
+			sdk_version: "current",
+			min_sdk_version: "29",  // this APK should support 29
+			jni_libs: ["libjni"],
+		}
+
+		cc_library {
+			name: "libjni",
+			stl: "none",
+			sdk_version: "current",
+		}
+	`
+	testJavaError(t, `"libjni" .*: sdk_version\(current\) is higher than min_sdk_version\(29\)`, bp)
+}
+
+func TestUpdatableApps_ErrorIfDepSdkVersionIsHigher(t *testing.T) {
+	bp := cc.GatherRequiredDepsForTest(android.Android) + `
+		android_app {
+			name: "foo",
+			srcs: ["a.java"],
+			updatable: true,
+			sdk_version: "current",
+			min_sdk_version: "29",  // this APK should support 29
+			jni_libs: ["libjni"],
+		}
+
+		cc_library {
+			name: "libjni",
+			stl: "none",
+			shared_libs: ["libbar"],
+			system_shared_libs: [],
+			sdk_version: "27",
+		}
+
+		cc_library {
+			name: "libbar",
+			stl: "none",
+			system_shared_libs: [],
+			sdk_version: "current",
+		}
+	`
+	testJavaError(t, `"libjni" .*: links "libbar" built against newer API version "current"`, bp)
+}
+
 func TestResourceDirs(t *testing.T) {
 	testCases := []struct {
 		name      string
@@ -1144,25 +1353,44 @@
 			platform_apis: true,
 			jni_uses_sdk_apis: true,
 		}
+
+		cc_library {
+			name: "libvendorjni",
+			system_shared_libs: [],
+			stl: "none",
+			vendor: true,
+		}
+
+		android_test {
+			name: "app_vendor",
+			jni_libs: ["libvendorjni"],
+			sdk_version: "current",
+			vendor: true,
+		}
 	`)
 
 	testCases := []struct {
-		name   string
-		sdkJNI bool
+		name      string
+		sdkJNI    bool
+		vendorJNI bool
 	}{
-		{"app_platform", false},
-		{"app_sdk", true},
-		{"app_force_platform", false},
-		{"app_force_sdk", true},
+		{name: "app_platform"},
+		{name: "app_sdk", sdkJNI: true},
+		{name: "app_force_platform"},
+		{name: "app_force_sdk", sdkJNI: true},
+		{name: "app_vendor", vendorJNI: true},
 	}
 
+	platformJNI := ctx.ModuleForTests("libjni", "android_arm64_armv8-a_shared").
+		Output("libjni.so").Output.String()
+	sdkJNI := ctx.ModuleForTests("libjni", "android_arm64_armv8-a_sdk_shared").
+		Output("libjni.so").Output.String()
+	vendorJNI := ctx.ModuleForTests("libvendorjni", "android_arm64_armv8-a_shared").
+		Output("libvendorjni.so").Output.String()
+
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
 			app := ctx.ModuleForTests(test.name, "android_common")
-			platformJNI := ctx.ModuleForTests("libjni", "android_arm64_armv8-a_shared").
-				Output("libjni.so").Output.String()
-			sdkJNI := ctx.ModuleForTests("libjni", "android_arm64_armv8-a_sdk_shared").
-				Output("libjni.so").Output.String()
 
 			jniLibZip := app.MaybeOutput("jnilibs.zip")
 			if len(jniLibZip.Implicits) != 1 {
@@ -1174,6 +1402,10 @@
 				if gotJNI != sdkJNI {
 					t.Errorf("expected SDK JNI library %q, got %q", sdkJNI, gotJNI)
 				}
+			} else if test.vendorJNI {
+				if gotJNI != vendorJNI {
+					t.Errorf("expected platform JNI library %q, got %q", vendorJNI, gotJNI)
+				}
 			} else {
 				if gotJNI != platformJNI {
 					t.Errorf("expected platform JNI library %q, got %q", platformJNI, gotJNI)
@@ -1209,7 +1441,8 @@
 		name                string
 		bp                  string
 		certificateOverride string
-		expected            string
+		expectedLineage     string
+		expectedCertificate string
 	}{
 		{
 			name: "default",
@@ -1221,7 +1454,8 @@
 				}
 			`,
 			certificateOverride: "",
-			expected:            "build/make/target/product/security/testkey.x509.pem build/make/target/product/security/testkey.pk8",
+			expectedLineage:     "",
+			expectedCertificate: "build/make/target/product/security/testkey.x509.pem build/make/target/product/security/testkey.pk8",
 		},
 		{
 			name: "module certificate property",
@@ -1239,7 +1473,8 @@
 				}
 			`,
 			certificateOverride: "",
-			expected:            "cert/new_cert.x509.pem cert/new_cert.pk8",
+			expectedLineage:     "",
+			expectedCertificate: "cert/new_cert.x509.pem cert/new_cert.pk8",
 		},
 		{
 			name: "path certificate property",
@@ -1252,7 +1487,8 @@
 				}
 			`,
 			certificateOverride: "",
-			expected:            "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
+			expectedLineage:     "",
+			expectedCertificate: "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
 		},
 		{
 			name: "certificate overrides",
@@ -1270,7 +1506,28 @@
 				}
 			`,
 			certificateOverride: "foo:new_certificate",
-			expected:            "cert/new_cert.x509.pem cert/new_cert.pk8",
+			expectedLineage:     "",
+			expectedCertificate: "cert/new_cert.x509.pem cert/new_cert.pk8",
+		},
+		{
+			name: "certificate lineage",
+			bp: `
+				android_app {
+					name: "foo",
+					srcs: ["a.java"],
+					certificate: ":new_certificate",
+					lineage: "lineage.bin",
+					sdk_version: "current",
+				}
+
+				android_app_certificate {
+					name: "new_certificate",
+					certificate: "cert/new_cert",
+				}
+			`,
+			certificateOverride: "",
+			expectedLineage:     "--lineage lineage.bin",
+			expectedCertificate: "cert/new_cert.x509.pem cert/new_cert.pk8",
 		},
 	}
 
@@ -1286,9 +1543,14 @@
 			foo := ctx.ModuleForTests("foo", "android_common")
 
 			signapk := foo.Output("foo.apk")
-			signFlags := signapk.Args["certificates"]
-			if test.expected != signFlags {
-				t.Errorf("Incorrect signing flags, expected: %q, got: %q", test.expected, signFlags)
+			signCertificateFlags := signapk.Args["certificates"]
+			if test.expectedCertificate != signCertificateFlags {
+				t.Errorf("Incorrect signing flags, expected: %q, got: %q", test.expectedCertificate, signCertificateFlags)
+			}
+
+			signFlags := signapk.Args["flags"]
+			if test.expectedLineage != signFlags {
+				t.Errorf("Incorrect signing flags, expected: %q, got: %q", test.expectedLineage, signFlags)
 			}
 		})
 	}
@@ -1402,6 +1664,7 @@
 			name: "bar",
 			base: "foo",
 			certificate: ":new_certificate",
+			lineage: "lineage.bin",
 			logging_parent: "bah",
 		}
 
@@ -1422,7 +1685,8 @@
 		variantName    string
 		apkName        string
 		apkPath        string
-		signFlag       string
+		certFlag       string
+		lineageFlag    string
 		overrides      []string
 		aaptFlag       string
 		logging_parent string
@@ -1431,7 +1695,8 @@
 			moduleName:     "foo",
 			variantName:    "android_common",
 			apkPath:        "/target/product/test_device/system/app/foo/foo.apk",
-			signFlag:       "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
+			certFlag:       "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
+			lineageFlag:    "",
 			overrides:      []string{"qux"},
 			aaptFlag:       "",
 			logging_parent: "",
@@ -1440,7 +1705,8 @@
 			moduleName:     "bar",
 			variantName:    "android_common_bar",
 			apkPath:        "/target/product/test_device/system/app/bar/bar.apk",
-			signFlag:       "cert/new_cert.x509.pem cert/new_cert.pk8",
+			certFlag:       "cert/new_cert.x509.pem cert/new_cert.pk8",
+			lineageFlag:    "--lineage lineage.bin",
 			overrides:      []string{"qux", "foo"},
 			aaptFlag:       "",
 			logging_parent: "bah",
@@ -1449,7 +1715,8 @@
 			moduleName:     "baz",
 			variantName:    "android_common_baz",
 			apkPath:        "/target/product/test_device/system/app/baz/baz.apk",
-			signFlag:       "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
+			certFlag:       "build/make/target/product/security/expiredkey.x509.pem build/make/target/product/security/expiredkey.pk8",
+			lineageFlag:    "",
 			overrides:      []string{"qux", "foo"},
 			aaptFlag:       "--rename-manifest-package org.dandroid.bp",
 			logging_parent: "",
@@ -1474,9 +1741,15 @@
 
 		// Check the certificate paths
 		signapk := variant.Output(expected.moduleName + ".apk")
-		signFlag := signapk.Args["certificates"]
-		if expected.signFlag != signFlag {
-			t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected.signFlag, signFlag)
+		certFlag := signapk.Args["certificates"]
+		if expected.certFlag != certFlag {
+			t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected.certFlag, certFlag)
+		}
+
+		// Check the lineage flags
+		lineageFlag := signapk.Args["flags"]
+		if expected.lineageFlag != lineageFlag {
+			t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected.lineageFlag, lineageFlag)
 		}
 
 		// Check if the overrides field values are correctly aggregated.
@@ -1798,6 +2071,27 @@
 	}
 }
 
+func TestAndroidAppImport_SigningLineage(t *testing.T) {
+	ctx, _ := testJava(t, `
+	  android_app_import {
+			name: "foo",
+			apk: "prebuilts/apk/app.apk",
+			certificate: "platform",
+			lineage: "lineage.bin",
+		}
+	`)
+
+	variant := ctx.ModuleForTests("foo", "android_common")
+
+	// Check cert signing lineage flag.
+	signedApk := variant.Output("signed/foo.apk")
+	signingFlag := signedApk.Args["flags"]
+	expected := "--lineage lineage.bin"
+	if expected != signingFlag {
+		t.Errorf("Incorrect signing flags, expected: %q, got: %q", expected, signingFlag)
+	}
+}
+
 func TestAndroidAppImport_DefaultDevCert(t *testing.T) {
 	ctx, _ := testJava(t, `
 		android_app_import {
@@ -2075,6 +2369,45 @@
 	if jniRule != android.Cp.String() {
 		t.Errorf("Unexpected JNI uncompress rule: " + jniRule)
 	}
+	if variant.MaybeOutput("zip-aligned/foo_presigned.apk").Rule == nil {
+		t.Errorf("Presigned test apk should be aligned")
+	}
+}
+
+func TestAndroidTestImport_Preprocessed(t *testing.T) {
+	ctx, _ := testJava(t, `
+		android_test_import {
+			name: "foo",
+			apk: "prebuilts/apk/app.apk",
+			presigned: true,
+			preprocessed: true,
+		}
+
+		android_test_import {
+			name: "foo_cert",
+			apk: "prebuilts/apk/app.apk",
+			certificate: "cert/new_cert",
+			preprocessed: true,
+		}
+		`)
+
+	testModules := []string{"foo", "foo_cert"}
+	for _, m := range testModules {
+		apkName := m + ".apk"
+		variant := ctx.ModuleForTests(m, "android_common")
+		jniRule := variant.Output("jnis-uncompressed/" + apkName).BuildParams.Rule.String()
+		if jniRule != android.Cp.String() {
+			t.Errorf("Unexpected JNI uncompress rule: " + jniRule)
+		}
+
+		// Make sure signing and aligning were skipped.
+		if variant.MaybeOutput("signed/"+apkName).Rule != nil {
+			t.Errorf("signing rule shouldn't be included for preprocessed.")
+		}
+		if variant.MaybeOutput("zip-aligned/"+apkName).Rule != nil {
+			t.Errorf("aligning rule shouldn't be for preprocessed")
+		}
+	}
 }
 
 func TestStl(t *testing.T) {
@@ -2150,6 +2483,20 @@
 		}
 
 		java_sdk_library {
+			name: "qux",
+			srcs: ["a.java"],
+			api_packages: ["qux"],
+			sdk_version: "current",
+		}
+
+		java_sdk_library {
+			name: "quuz",
+			srcs: ["a.java"],
+			api_packages: ["quuz"],
+			sdk_version: "current",
+		}
+
+		java_sdk_library {
 			name: "bar",
 			srcs: ["a.java"],
 			api_packages: ["bar"],
@@ -2159,6 +2506,7 @@
 		android_app {
 			name: "app",
 			srcs: ["a.java"],
+			libs: ["qux", "quuz.stubs"],
 			uses_libs: ["foo"],
 			sdk_version: "current",
 			optional_uses_libs: [
@@ -2189,6 +2537,15 @@
 	app := ctx.ModuleForTests("app", "android_common")
 	prebuilt := ctx.ModuleForTests("prebuilt", "android_common")
 
+	// Test that implicit dependencies on java_sdk_library instances are passed to the manifest.
+	manifestFixerArgs := app.Output("manifest_fixer/AndroidManifest.xml").Args["args"]
+	if w := "--uses-library qux"; !strings.Contains(manifestFixerArgs, w) {
+		t.Errorf("unexpected manifest_fixer args: wanted %q in %q", w, manifestFixerArgs)
+	}
+	if w := "--uses-library quuz"; !strings.Contains(manifestFixerArgs, w) {
+		t.Errorf("unexpected manifest_fixer args: wanted %q in %q", w, manifestFixerArgs)
+	}
+
 	// Test that all libraries are verified
 	cmd := app.Rule("verify_uses_libraries").RuleParams.Command
 	if w := "--uses-library foo"; !strings.Contains(cmd, w) {
@@ -2449,6 +2806,32 @@
 			uncompressedPlatform:  true,
 			uncompressedUnbundled: true,
 		},
+		{
+			name: "normal_uncompress_dex_true",
+			bp: `
+				android_app {
+					name: "foo",
+					srcs: ["a.java"],
+					sdk_version: "current",
+					uncompress_dex: true,
+				}
+			`,
+			uncompressedPlatform:  true,
+			uncompressedUnbundled: true,
+		},
+		{
+			name: "normal_uncompress_dex_false",
+			bp: `
+				android_app {
+					name: "foo",
+					srcs: ["a.java"],
+					sdk_version: "current",
+					uncompress_dex: false,
+				}
+			`,
+			uncompressedPlatform:  false,
+			uncompressedUnbundled: false,
+		},
 	}
 
 	test := func(t *testing.T, bp string, want bool, unbundled bool) {
@@ -2512,6 +2895,7 @@
 		runtime_resource_overlay {
 			name: "foo",
 			certificate: "platform",
+			lineage: "lineage.bin",
 			product_specific: true,
 			static_libs: ["bar"],
 			resource_libs: ["baz"],
@@ -2566,6 +2950,11 @@
 
 	// Check cert signing flag.
 	signedApk := m.Output("signed/foo.apk")
+	lineageFlag := signedApk.Args["flags"]
+	expectedLineageFlag := "--lineage lineage.bin"
+	if expectedLineageFlag != lineageFlag {
+		t.Errorf("Incorrect signing lineage flags, expected: %q, got: %q", expectedLineageFlag, lineageFlag)
+	}
 	signingFlag := signedApk.Args["certificates"]
 	expected := "build/make/target/product/security/platform.x509.pem build/make/target/product/security/platform.pk8"
 	if expected != signingFlag {
diff --git a/java/builder.go b/java/builder.go
index 3a4a10d..a27e5c3 100644
--- a/java/builder.go
+++ b/java/builder.go
@@ -40,17 +40,17 @@
 	// (if the rule produces .class files) or a .srcjar file (if the rule produces .java files).
 	// .srcjar files are unzipped into a temporary directory when compiled with javac.
 	// TODO(b/143658984): goma can't handle the --system argument to javac.
-	javac, javacRE = remoteexec.StaticRules(pctx, "javac",
+	javac, javacRE = remoteexec.MultiCommandStaticRules(pctx, "javac",
 		blueprint.RuleParams{
 			Command: `rm -rf "$outDir" "$annoDir" "$srcJarDir" && mkdir -p "$outDir" "$annoDir" "$srcJarDir" && ` +
 				`${config.ZipSyncCmd} -d $srcJarDir -l $srcJarDir/list -f "*.java" $srcJars && ` +
 				`(if [ -s $srcJarDir/list ] || [ -s $out.rsp ] ; then ` +
-				`${config.SoongJavacWrapper} $reTemplate${config.JavacCmd} ` +
+				`${config.SoongJavacWrapper} $javaTemplate${config.JavacCmd} ` +
 				`${config.JavacHeapFlags} ${config.JavacVmFlags} ${config.CommonJdkFlags} ` +
 				`$processorpath $processor $javacFlags $bootClasspath $classpath ` +
 				`-source $javaVersion -target $javaVersion ` +
 				`-d $outDir -s $annoDir @$out.rsp @$srcJarDir/list ; fi ) && ` +
-				`${config.SoongZipCmd} -jar -o $out -C $outDir -D $outDir && ` +
+				`$zipTemplate${config.SoongZipCmd} -jar -o $out -C $outDir -D $outDir && ` +
 				`rm -rf "$srcJarDir"`,
 			CommandDeps: []string{
 				"${config.JavacCmd}",
@@ -60,10 +60,19 @@
 			CommandOrderOnly: []string{"${config.SoongJavacWrapper}"},
 			Rspfile:          "$out.rsp",
 			RspfileContent:   "$in",
-		}, &remoteexec.REParams{
-			Labels:       map[string]string{"type": "compile", "lang": "java", "compiler": "javac"},
-			ExecStrategy: "${config.REJavacExecStrategy}",
-			Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+		}, map[string]*remoteexec.REParams{
+			"$javaTemplate": &remoteexec.REParams{
+				Labels:       map[string]string{"type": "compile", "lang": "java", "compiler": "javac"},
+				ExecStrategy: "${config.REJavacExecStrategy}",
+				Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+			},
+			"$zipTemplate": &remoteexec.REParams{
+				Labels:       map[string]string{"type": "tool", "name": "soong_zip"},
+				Inputs:       []string{"${config.SoongZipCmd}", "$outDir"},
+				OutputFiles:  []string{"$out"},
+				ExecStrategy: "${config.REJavacExecStrategy}",
+				Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+			},
 		}, []string{"javacFlags", "bootClasspath", "classpath", "processorpath", "processor", "srcJars", "srcJarDir",
 			"outDir", "annoDir", "javaVersion"}, nil)
 
@@ -104,10 +113,22 @@
 		"javacFlags", "bootClasspath", "classpath", "processorpath", "processor", "srcJars", "srcJarDir",
 		"outDir", "annoDir", "javaVersion")
 
-	turbine = pctx.AndroidStaticRule("turbine",
+	extractMatchingApks = pctx.StaticRule(
+		"extractMatchingApks",
+		blueprint.RuleParams{
+			Command: `rm -rf "$out" && ` +
+				`${config.ExtractApksCmd} -o "${out}" -allow-prereleased=${allow-prereleased} ` +
+				`-sdk-version=${sdk-version} -abis=${abis} ` +
+				`--screen-densities=${screen-densities} --stem=${stem} ` +
+				`${in}`,
+			CommandDeps: []string{"${config.ExtractApksCmd}"},
+		},
+		"abis", "allow-prereleased", "screen-densities", "sdk-version", "stem")
+
+	turbine, turbineRE = remoteexec.StaticRules(pctx, "turbine",
 		blueprint.RuleParams{
 			Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
-				`${config.JavaCmd} ${config.JavaVmFlags} -jar ${config.TurbineJar} --output $out.tmp ` +
+				`$reTemplate${config.JavaCmd} ${config.JavaVmFlags} -jar ${config.TurbineJar} --output $out.tmp ` +
 				`--temp_dir "$outDir" --sources @$out.rsp  --source_jars $srcJars ` +
 				`--javacopts ${config.CommonJdkFlags} ` +
 				`$javacFlags -source $javaVersion -target $javaVersion -- $bootClasspath $classpath && ` +
@@ -122,25 +143,45 @@
 			RspfileContent: "$in",
 			Restat:         true,
 		},
-		"javacFlags", "bootClasspath", "classpath", "srcJars", "outDir", "javaVersion")
+		&remoteexec.REParams{Labels: map[string]string{"type": "tool", "name": "turbine"},
+			ExecStrategy:      "${config.RETurbineExecStrategy}",
+			Inputs:            []string{"${config.TurbineJar}", "${out}.rsp", "$implicits"},
+			RSPFile:           "${out}.rsp",
+			OutputFiles:       []string{"$out.tmp"},
+			OutputDirectories: []string{"$outDir"},
+			ToolchainInputs:   []string{"${config.JavaCmd}"},
+			Platform:          map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+		}, []string{"javacFlags", "bootClasspath", "classpath", "srcJars", "outDir", "javaVersion"}, []string{"implicits"})
 
-	jar = pctx.AndroidStaticRule("jar",
+	jar, jarRE = remoteexec.StaticRules(pctx, "jar",
 		blueprint.RuleParams{
-			Command:        `${config.SoongZipCmd} -jar -o $out @$out.rsp`,
+			Command:        `$reTemplate${config.SoongZipCmd} -jar -o $out @$out.rsp`,
 			CommandDeps:    []string{"${config.SoongZipCmd}"},
 			Rspfile:        "$out.rsp",
 			RspfileContent: "$jarArgs",
 		},
-		"jarArgs")
+		&remoteexec.REParams{
+			ExecStrategy: "${config.REJarExecStrategy}",
+			Inputs:       []string{"${config.SoongZipCmd}", "${out}.rsp"},
+			RSPFile:      "${out}.rsp",
+			OutputFiles:  []string{"$out"},
+			Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+		}, []string{"jarArgs"}, nil)
 
-	zip = pctx.AndroidStaticRule("zip",
+	zip, zipRE = remoteexec.StaticRules(pctx, "zip",
 		blueprint.RuleParams{
 			Command:        `${config.SoongZipCmd} -o $out @$out.rsp`,
 			CommandDeps:    []string{"${config.SoongZipCmd}"},
 			Rspfile:        "$out.rsp",
 			RspfileContent: "$jarArgs",
 		},
-		"jarArgs")
+		&remoteexec.REParams{
+			ExecStrategy: "${config.REZipExecStrategy}",
+			Inputs:       []string{"${config.SoongZipCmd}", "${out}.rsp", "$implicits"},
+			RSPFile:      "${out}.rsp",
+			OutputFiles:  []string{"$out"},
+			Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+		}, []string{"jarArgs"}, []string{"implicits"})
 
 	combineJar = pctx.AndroidStaticRule("combineJar",
 		blueprint.RuleParams{
@@ -334,20 +375,26 @@
 	deps = append(deps, classpath...)
 	deps = append(deps, flags.processorPath...)
 
+	rule := turbine
+	args := map[string]string{
+		"javacFlags":    flags.javacFlags,
+		"bootClasspath": bootClasspath,
+		"srcJars":       strings.Join(srcJars.Strings(), " "),
+		"classpath":     classpath.FormTurbineClassPath("--classpath "),
+		"outDir":        android.PathForModuleOut(ctx, "turbine", "classes").String(),
+		"javaVersion":   flags.javaVersion.String(),
+	}
+	if ctx.Config().IsEnvTrue("RBE_TURBINE") {
+		rule = turbineRE
+		args["implicits"] = strings.Join(deps.Strings(), ",")
+	}
 	ctx.Build(pctx, android.BuildParams{
-		Rule:        turbine,
+		Rule:        rule,
 		Description: "turbine",
 		Output:      outputFile,
 		Inputs:      srcFiles,
 		Implicits:   deps,
-		Args: map[string]string{
-			"javacFlags":    flags.javacFlags,
-			"bootClasspath": bootClasspath,
-			"srcJars":       strings.Join(srcJars.Strings(), " "),
-			"classpath":     classpath.FormTurbineClassPath("--classpath "),
-			"outDir":        android.PathForModuleOut(ctx, "turbine", "classes").String(),
-			"javaVersion":   flags.javaVersion.String(),
-		},
+		Args:        args,
 	})
 }
 
@@ -431,8 +478,12 @@
 func TransformResourcesToJar(ctx android.ModuleContext, outputFile android.WritablePath,
 	jarArgs []string, deps android.Paths) {
 
+	rule := jar
+	if ctx.Config().IsEnvTrue("RBE_JAR") {
+		rule = jarRE
+	}
 	ctx.Build(pctx, android.BuildParams{
-		Rule:        jar,
+		Rule:        rule,
 		Description: "jar",
 		Output:      outputFile,
 		Implicits:   deps,
diff --git a/java/config/config.go b/java/config/config.go
index c4f2363..edaed2a 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -122,7 +122,7 @@
 	pctx.HostBinToolVariable("D8Cmd", "d8")
 	pctx.HostBinToolVariable("R8Cmd", "r8-compat-proguard")
 	pctx.HostBinToolVariable("HiddenAPICmd", "hiddenapi")
-
+	pctx.HostBinToolVariable("ExtractApksCmd", "extract_apks")
 	pctx.VariableFunc("TurbineJar", func(ctx android.PackageVarContext) string {
 		turbine := "turbine.jar"
 		if ctx.Config().UnbundledBuild() {
@@ -148,6 +148,10 @@
 	pctx.VariableFunc("REJavacExecStrategy", remoteexec.EnvOverrideFunc("RBE_JAVAC_EXEC_STRATEGY", remoteexec.LocalExecStrategy))
 	pctx.VariableFunc("RED8ExecStrategy", remoteexec.EnvOverrideFunc("RBE_D8_EXEC_STRATEGY", remoteexec.LocalExecStrategy))
 	pctx.VariableFunc("RER8ExecStrategy", remoteexec.EnvOverrideFunc("RBE_R8_EXEC_STRATEGY", remoteexec.LocalExecStrategy))
+	pctx.VariableFunc("RETurbineExecStrategy", remoteexec.EnvOverrideFunc("RBE_TURBINE_EXEC_STRATEGY", remoteexec.LocalExecStrategy))
+	pctx.VariableFunc("RESignApkExecStrategy", remoteexec.EnvOverrideFunc("RBE_SIGNAPK_EXEC_STRATEGY", remoteexec.LocalExecStrategy))
+	pctx.VariableFunc("REJarExecStrategy", remoteexec.EnvOverrideFunc("RBE_JAR_EXEC_STRATEGY", remoteexec.LocalExecStrategy))
+	pctx.VariableFunc("REZipExecStrategy", remoteexec.EnvOverrideFunc("RBE_ZIP_EXEC_STRATEGY", remoteexec.LocalExecStrategy))
 
 	pctx.HostJavaToolVariable("JacocoCLIJar", "jacoco-cli.jar")
 
diff --git a/java/dex.go b/java/dex.go
index 27ec6ee..9e61e95 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -18,53 +18,72 @@
 	"strings"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
 	"android/soong/remoteexec"
 )
 
-var d8, d8RE = remoteexec.StaticRules(pctx, "d8",
+var d8, d8RE = remoteexec.MultiCommandStaticRules(pctx, "d8",
 	blueprint.RuleParams{
 		Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
-			`$reTemplate${config.D8Cmd} ${config.DexFlags} --output $outDir $d8Flags $in && ` +
-			`${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
+			`$d8Template${config.D8Cmd} ${config.DexFlags} --output $outDir $d8Flags $in && ` +
+			`$zipTemplate${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
 			`${config.MergeZipsCmd} -D -stripFile "**/*.class" $out $outDir/classes.dex.jar $in`,
 		CommandDeps: []string{
 			"${config.D8Cmd}",
 			"${config.SoongZipCmd}",
 			"${config.MergeZipsCmd}",
 		},
-	}, &remoteexec.REParams{
-		Labels:          map[string]string{"type": "compile", "compiler": "d8"},
-		Inputs:          []string{"${config.D8Jar}"},
-		ExecStrategy:    "${config.RED8ExecStrategy}",
-		ToolchainInputs: []string{"${config.JavaCmd}"},
-		Platform:        map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+	}, map[string]*remoteexec.REParams{
+		"$d8Template": &remoteexec.REParams{
+			Labels:          map[string]string{"type": "compile", "compiler": "d8"},
+			Inputs:          []string{"${config.D8Jar}"},
+			ExecStrategy:    "${config.RED8ExecStrategy}",
+			ToolchainInputs: []string{"${config.JavaCmd}"},
+			Platform:        map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+		},
+		"$zipTemplate": &remoteexec.REParams{
+			Labels:       map[string]string{"type": "tool", "name": "soong_zip"},
+			Inputs:       []string{"${config.SoongZipCmd}", "$outDir"},
+			OutputFiles:  []string{"$outDir/classes.dex.jar"},
+			ExecStrategy: "${config.RED8ExecStrategy}",
+			Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+		},
 	}, []string{"outDir", "d8Flags", "zipFlags"}, nil)
 
-var r8, r8RE = remoteexec.StaticRules(pctx, "r8",
+var r8, r8RE = remoteexec.MultiCommandStaticRules(pctx, "r8",
 	blueprint.RuleParams{
 		Command: `rm -rf "$outDir" && mkdir -p "$outDir" && ` +
 			`rm -f "$outDict" && ` +
-			`$reTemplate${config.R8Cmd} ${config.DexFlags} -injars $in --output $outDir ` +
+			`$r8Template${config.R8Cmd} ${config.DexFlags} -injars $in --output $outDir ` +
 			`--force-proguard-compatibility ` +
 			`--no-data-resources ` +
 			`-printmapping $outDict ` +
 			`$r8Flags && ` +
 			`touch "$outDict" && ` +
-			`${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
+			`$zipTemplate${config.SoongZipCmd} $zipFlags -o $outDir/classes.dex.jar -C $outDir -f "$outDir/classes*.dex" && ` +
 			`${config.MergeZipsCmd} -D -stripFile "**/*.class" $out $outDir/classes.dex.jar $in`,
 		CommandDeps: []string{
 			"${config.R8Cmd}",
 			"${config.SoongZipCmd}",
 			"${config.MergeZipsCmd}",
 		},
-	}, &remoteexec.REParams{
-		Labels:          map[string]string{"type": "compile", "compiler": "r8"},
-		Inputs:          []string{"$implicits", "${config.R8Jar}"},
-		ExecStrategy:    "${config.RER8ExecStrategy}",
-		ToolchainInputs: []string{"${config.JavaCmd}"},
-		Platform:        map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+	}, map[string]*remoteexec.REParams{
+		"$r8Template": &remoteexec.REParams{
+			Labels:          map[string]string{"type": "compile", "compiler": "r8"},
+			Inputs:          []string{"$implicits", "${config.R8Jar}"},
+			ExecStrategy:    "${config.RER8ExecStrategy}",
+			ToolchainInputs: []string{"${config.JavaCmd}"},
+			Platform:        map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+		},
+		"$zipTemplate": &remoteexec.REParams{
+			Labels:       map[string]string{"type": "tool", "name": "soong_zip"},
+			Inputs:       []string{"${config.SoongZipCmd}", "$outDir"},
+			OutputFiles:  []string{"$outDir/classes.dex.jar"},
+			ExecStrategy: "${config.RER8ExecStrategy}",
+			Platform:     map[string]string{remoteexec.PoolKey: "${config.REJavaPool}"},
+		},
 	}, []string{"outDir", "outDict", "r8Flags", "zipFlags"}, []string{"implicits"})
 
 func (j *Module) dexCommonFlags(ctx android.ModuleContext) []string {
@@ -188,7 +207,7 @@
 	outDir := android.PathForModuleOut(ctx, "dex")
 
 	zipFlags := "--ignore_missing_files"
-	if j.deviceProperties.UncompressDex {
+	if proptools.Bool(j.deviceProperties.Uncompress_dex) {
 		zipFlags += " -L 0"
 	}
 
@@ -235,7 +254,7 @@
 			},
 		})
 	}
-	if j.deviceProperties.UncompressDex {
+	if proptools.Bool(j.deviceProperties.Uncompress_dex) {
 		alignedJavalibJar := android.PathForModuleOut(ctx, "aligned", jarName)
 		TransformZipAlign(ctx, alignedJavalibJar, javalibJar)
 		javalibJar = alignedJavalibJar
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index dffdc24..ed61d4b 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -109,11 +109,11 @@
 	return nil
 }
 
-func (image bootImageConfig) moduleName(idx int) string {
+func (image bootImageConfig) moduleName(ctx android.PathContext, idx int) string {
 	// Dexpreopt on the boot class path produces multiple files. The first dex file
 	// is converted into 'name'.art (to match the legacy assumption that 'name'.art
 	// exists), and the rest are converted to 'name'-<jar>.art.
-	_, m := android.SplitApexJarPair(image.modules[idx])
+	_, m := android.SplitApexJarPair(ctx, image.modules[idx])
 	name := image.stem
 	if idx != 0 || image.extends != nil {
 		name += "-" + stemOf(m)
@@ -121,9 +121,9 @@
 	return name
 }
 
-func (image bootImageConfig) firstModuleNameOrStem() string {
+func (image bootImageConfig) firstModuleNameOrStem(ctx android.PathContext) string {
 	if len(image.modules) > 0 {
-		return image.moduleName(0)
+		return image.moduleName(ctx, 0)
 	} else {
 		return image.stem
 	}
@@ -132,7 +132,7 @@
 func (image bootImageConfig) moduleFiles(ctx android.PathContext, dir android.OutputPath, exts ...string) android.OutputPaths {
 	ret := make(android.OutputPaths, 0, len(image.modules)*len(exts))
 	for i := range image.modules {
-		name := image.moduleName(i)
+		name := image.moduleName(ctx, i)
 		for _, ext := range exts {
 			ret = append(ret, dir.Join(ctx, name+ext))
 		}
@@ -261,7 +261,7 @@
 	}
 
 	name := ctx.ModuleName(module)
-	index := android.IndexList(name, android.GetJarsFromApexJarPairs(image.modules))
+	index := android.IndexList(name, android.GetJarsFromApexJarPairs(ctx, image.modules))
 	if index == -1 {
 		return -1, nil
 	}
@@ -314,7 +314,7 @@
 	// Ensure all modules were converted to paths
 	for i := range bootDexJars {
 		if bootDexJars[i] == nil {
-			_, m := android.SplitApexJarPair(image.modules[i])
+			_, m := android.SplitApexJarPair(ctx, image.modules[i])
 			if ctx.Config().AllowMissingDependencies() {
 				missingDeps = append(missingDeps, m)
 				bootDexJars[i] = android.PathForOutput(ctx, "missing")
@@ -614,15 +614,15 @@
 
 	return ctx.Config().Once(updatableBcpPackagesRuleKey, func() interface{} {
 		global := dexpreopt.GetGlobalConfig(ctx)
-		updatableModules := android.GetJarsFromApexJarPairs(global.UpdatableBootJars)
+		updatableModules := android.GetJarsFromApexJarPairs(ctx, global.UpdatableBootJars)
 
 		// Collect `permitted_packages` for updatable boot jars.
 		var updatablePackages []string
 		ctx.VisitAllModules(func(module android.Module) {
-			if j, ok := module.(*Library); ok {
+			if j, ok := module.(PermittedPackagesForUpdatableBootJars); ok {
 				name := ctx.ModuleName(module)
 				if i := android.IndexList(name, updatableModules); i != -1 {
-					pp := j.properties.Permitted_packages
+					pp := j.PermittedPackagesForUpdatableBootJars()
 					if len(pp) > 0 {
 						updatablePackages = append(updatablePackages, pp...)
 					} else {
diff --git a/java/dexpreopt_bootjars_test.go b/java/dexpreopt_bootjars_test.go
index 0946bf0..e9704dc 100644
--- a/java/dexpreopt_bootjars_test.go
+++ b/java/dexpreopt_bootjars_test.go
@@ -24,7 +24,7 @@
 	"android/soong/dexpreopt"
 )
 
-func TestDexpreoptBootJars(t *testing.T) {
+func testDexpreoptBoot(t *testing.T, ruleFile string, expectedInputs, expectedOutputs []string) {
 	bp := `
 		java_sdk_library {
 			name: "foo",
@@ -52,14 +52,39 @@
 	dexpreopt.SetTestGlobalConfig(config, dexpreoptConfig)
 
 	ctx := testContext()
-
 	RegisterDexpreoptBootJarsComponents(ctx)
-
 	run(t, ctx, config)
 
 	dexpreoptBootJars := ctx.SingletonForTests("dex_bootjars")
+	rule := dexpreoptBootJars.Output(ruleFile)
 
-	bootArt := dexpreoptBootJars.Output("boot-foo.art")
+	for i := range expectedInputs {
+		expectedInputs[i] = filepath.Join(buildDir, "test_device", expectedInputs[i])
+	}
+
+	for i := range expectedOutputs {
+		expectedOutputs[i] = filepath.Join(buildDir, "test_device", expectedOutputs[i])
+	}
+
+	inputs := rule.Implicits.Strings()
+	sort.Strings(inputs)
+	sort.Strings(expectedInputs)
+
+	outputs := append(android.WritablePaths{rule.Output}, rule.ImplicitOutputs...).Strings()
+	sort.Strings(outputs)
+	sort.Strings(expectedOutputs)
+
+	if !reflect.DeepEqual(inputs, expectedInputs) {
+		t.Errorf("want inputs %q\n got inputs %q", expectedInputs, inputs)
+	}
+
+	if !reflect.DeepEqual(outputs, expectedOutputs) {
+		t.Errorf("want outputs %q\n got outputs %q", expectedOutputs, outputs)
+	}
+}
+
+func TestDexpreoptBootJars(t *testing.T) {
+	ruleFile := "boot-foo.art"
 
 	expectedInputs := []string{
 		"dex_artjars/android/apex/com.android.art/javalib/arm64/boot.art",
@@ -68,47 +93,43 @@
 		"dex_bootjars_input/baz.jar",
 	}
 
-	for i := range expectedInputs {
-		expectedInputs[i] = filepath.Join(buildDir, "test_device", expectedInputs[i])
-	}
-
-	inputs := bootArt.Implicits.Strings()
-	sort.Strings(inputs)
-	sort.Strings(expectedInputs)
-
-	if !reflect.DeepEqual(inputs, expectedInputs) {
-		t.Errorf("want inputs %q\n got inputs %q", expectedInputs, inputs)
-	}
-
 	expectedOutputs := []string{
 		"dex_bootjars/android/system/framework/arm64/boot.invocation",
-
 		"dex_bootjars/android/system/framework/arm64/boot-foo.art",
 		"dex_bootjars/android/system/framework/arm64/boot-bar.art",
 		"dex_bootjars/android/system/framework/arm64/boot-baz.art",
-
 		"dex_bootjars/android/system/framework/arm64/boot-foo.oat",
 		"dex_bootjars/android/system/framework/arm64/boot-bar.oat",
 		"dex_bootjars/android/system/framework/arm64/boot-baz.oat",
-
 		"dex_bootjars/android/system/framework/arm64/boot-foo.vdex",
 		"dex_bootjars/android/system/framework/arm64/boot-bar.vdex",
 		"dex_bootjars/android/system/framework/arm64/boot-baz.vdex",
-
 		"dex_bootjars_unstripped/android/system/framework/arm64/boot-foo.oat",
 		"dex_bootjars_unstripped/android/system/framework/arm64/boot-bar.oat",
 		"dex_bootjars_unstripped/android/system/framework/arm64/boot-baz.oat",
 	}
 
-	for i := range expectedOutputs {
-		expectedOutputs[i] = filepath.Join(buildDir, "test_device", expectedOutputs[i])
+	testDexpreoptBoot(t, ruleFile, expectedInputs, expectedOutputs)
+}
+
+// Changes to the boot.zip structure may break the ART APK scanner.
+func TestDexpreoptBootZip(t *testing.T) {
+	ruleFile := "boot.zip"
+
+	ctx := android.PathContextForTesting(testConfig(nil, "", nil))
+	expectedInputs := []string{}
+	for _, target := range dexpreoptTargets(ctx) {
+		for _, ext := range []string{".art", ".oat", ".vdex"} {
+			for _, jar := range []string{"foo", "bar", "baz"} {
+				expectedInputs = append(expectedInputs,
+					filepath.Join("dex_bootjars", target.Os.String(), "system/framework", target.Arch.ArchType.String(), "boot-"+jar+ext))
+			}
+		}
 	}
 
-	outputs := append(android.WritablePaths{bootArt.Output}, bootArt.ImplicitOutputs...).Strings()
-	sort.Strings(outputs)
-	sort.Strings(expectedOutputs)
-
-	if !reflect.DeepEqual(outputs, expectedOutputs) {
-		t.Errorf("want outputs %q\n got outputs %q", expectedOutputs, outputs)
+	expectedOutputs := []string{
+		"dex_bootjars/boot.zip",
 	}
+
+	testDexpreoptBoot(t, ruleFile, expectedInputs, expectedOutputs)
 }
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index 41a2ca7..f13d9f2 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -39,7 +39,7 @@
 		// 2) The jars that are from an updatable apex.
 		for _, m := range global.UpdatableSystemServerJars {
 			systemServerClasspathLocations = append(systemServerClasspathLocations,
-				dexpreopt.GetJarLocationFromApexJarPair(m))
+				dexpreopt.GetJarLocationFromApexJarPair(ctx, m))
 		}
 		if len(systemServerClasspathLocations) != len(global.SystemServerJars)+len(global.UpdatableSystemServerJars) {
 			panic(fmt.Errorf("Wrong number of system server jars, got %d, expected %d",
@@ -80,7 +80,7 @@
 }
 
 func getDexLocation(ctx android.PathContext, target android.Target, module string) string {
-	apex, jar := android.SplitApexJarPair(module)
+	apex, jar := android.SplitApexJarPair(ctx, module)
 
 	name := stemOf(jar) + ".jar"
 
@@ -156,7 +156,7 @@
 			c.symbolsDir = deviceDir.Join(ctx, "dex_"+c.name+"jars_unstripped")
 
 			// expands to <stem>.art for primary image and <stem>-<1st module>.art for extension
-			imageName := c.firstModuleNameOrStem() + ".art"
+			imageName := c.firstModuleNameOrStem(ctx) + ".art"
 
 			// The path to bootclasspath dex files needs to be known at module
 			// GenerateAndroidBuildAction time, before the bootclasspath modules have been compiled.
@@ -164,7 +164,7 @@
 			// TODO(b/143682396): use module dependencies instead
 			inputDir := deviceDir.Join(ctx, "dex_"+c.name+"jars_input")
 			for _, m := range c.modules {
-				_, jar := android.SplitApexJarPair(m)
+				_, jar := android.SplitApexJarPair(ctx, m)
 				c.dexPaths = append(c.dexPaths, inputDir.Join(ctx, stemOf(jar)+".jar"))
 			}
 			c.dexPathsDeps = c.dexPaths
@@ -215,7 +215,7 @@
 
 		updatableBootclasspath := make([]string, len(global.UpdatableBootJars))
 		for i, p := range global.UpdatableBootJars {
-			updatableBootclasspath[i] = dexpreopt.GetJarLocationFromApexJarPair(p)
+			updatableBootclasspath[i] = dexpreopt.GetJarLocationFromApexJarPair(ctx, p)
 		}
 
 		bootclasspath := append(copyOf(image.getAnyAndroidVariant().dexLocationsDeps), updatableBootclasspath...)
diff --git a/java/droiddoc.go b/java/droiddoc.go
index 78ecb09..b16c9cd 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -222,8 +222,22 @@
 
 		Current ApiToCheck
 
-		// do not perform API check against Last_released, in the case that both two specified API
-		// files by Last_released are modules which don't exist.
+		// The java_sdk_library module generates references to modules (i.e. filegroups)
+		// from which information about the latest API version can be obtained. As those
+		// modules may not exist (e.g. because a previous version has not been released) it
+		// sets ignore_missing_latest_api=true on the droidstubs modules it creates so
+		// that droidstubs can ignore those references if the modules do not yet exist.
+		//
+		// If true then this will ignore module references for modules that do not exist
+		// in properties that supply the previous version of the API.
+		//
+		// There are two sets of those:
+		// * Api_file, Removed_api_file in check_api.last_released
+		// * New_since in check_api.api_lint.new_since
+		//
+		// The first two must be set as a pair, so either they should both exist or neither
+		// should exist - in which case when this property is true they are ignored. If one
+		// exists and the other does not then it is an error.
 		Ignore_missing_latest_api *bool `blueprint:"mutated"`
 
 		Api_lint struct {
@@ -337,11 +351,16 @@
 	ApiFilePath() android.Path
 }
 
+type ApiStubsSrcProvider interface {
+	StubsSrcJar() android.Path
+}
+
 // Provider of information about API stubs, used by java_sdk_library.
 type ApiStubsProvider interface {
 	ApiFilePath
 	RemovedApiFilePath() android.Path
-	StubsSrcJar() android.Path
+
+	ApiStubsSrcProvider
 }
 
 //
@@ -357,6 +376,7 @@
 	srcFiles    android.Paths
 	sourcepaths android.Paths
 	argFiles    android.Paths
+	implicits   android.Paths
 
 	args string
 
@@ -527,7 +547,7 @@
 		case libTag:
 			switch dep := module.(type) {
 			case SdkLibraryDependency:
-				deps.classpath = append(deps.classpath, dep.SdkImplementationJars(ctx, j.sdkVersion())...)
+				deps.classpath = append(deps.classpath, dep.SdkHeaderJars(ctx, j.sdkVersion())...)
 			case Dependency:
 				deps.classpath = append(deps.classpath, dep.HeaderJars()...)
 				deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs()...)
@@ -556,6 +576,7 @@
 	// do not pass exclude_srcs directly when expanding srcFiles since exclude_srcs
 	// may contain filegroup or genrule.
 	srcFiles := android.PathsForModuleSrcExcludes(ctx, j.properties.Srcs, j.properties.Exclude_srcs)
+	j.implicits = append(j.implicits, srcFiles...)
 
 	filterByPackage := func(srcs []android.Path, filterPackages []string) []android.Path {
 		if filterPackages == nil {
@@ -581,6 +602,24 @@
 	}
 	srcFiles = filterByPackage(srcFiles, j.properties.Filter_packages)
 
+	// While metalava needs package html files, it does not need them to be explicit on the command
+	// line. More importantly, the metalava rsp file is also used by the subsequent jdiff action if
+	// jdiff_enabled=true. javadoc complains if it receives html files on the command line. The filter
+	// below excludes html files from the rsp file for both metalava and jdiff. Note that the html
+	// files are still included as implicit inputs for successful remote execution and correct
+	// incremental builds.
+	filterHtml := func(srcs []android.Path) []android.Path {
+		filtered := []android.Path{}
+		for _, src := range srcs {
+			if src.Ext() == ".html" {
+				continue
+			}
+			filtered = append(filtered, src)
+		}
+		return filtered
+	}
+	srcFiles = filterHtml(srcFiles)
+
 	flags := j.collectAidlFlags(ctx, deps)
 	srcFiles = j.genSources(ctx, srcFiles, flags)
 
@@ -1200,8 +1239,18 @@
 func (d *Droidstubs) DepsMutator(ctx android.BottomUpMutatorContext) {
 	d.Javadoc.addDeps(ctx)
 
+	// If requested clear any properties that provide information about the latest version
+	// of an API and which reference non-existent modules.
 	if Bool(d.properties.Check_api.Ignore_missing_latest_api) {
 		ignoreMissingModules(ctx, &d.properties.Check_api.Last_released)
+
+		// If the new_since references a module, e.g. :module-latest-api and the module
+		// does not exist then clear it.
+		newSinceSrc := d.properties.Check_api.Api_lint.New_since
+		newSinceSrcModule := android.SrcIsModule(proptools.String(newSinceSrc))
+		if newSinceSrcModule != "" && !ctx.OtherModuleExists(newSinceSrcModule) {
+			d.properties.Check_api.Api_lint.New_since = nil
+		}
 	}
 
 	if len(d.properties.Merge_annotations_dirs) != 0 {
@@ -1373,10 +1422,26 @@
 }
 
 func metalavaCmd(ctx android.ModuleContext, rule *android.RuleBuilder, javaVersion javaVersion, srcs android.Paths,
-	srcJarList android.Path, bootclasspath, classpath classpath, sourcepaths android.Paths) *android.RuleBuilderCommand {
+	srcJarList android.Path, bootclasspath, classpath classpath, sourcepaths android.Paths, implicits android.Paths) *android.RuleBuilderCommand {
 	// Metalava uses lots of memory, restrict the number of metalava jobs that can run in parallel.
 	rule.HighMem()
 	cmd := rule.Command()
+
+	rspFile := ""
+	if len(implicits) > 0 {
+		implicitsRsp := android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"implicits.rsp")
+		rspFile = implicitsRsp.String()
+		impRule := android.NewRuleBuilder()
+		impCmd := impRule.Command()
+		// A dummy action that copies the ninja generated rsp file to a new location. This allows us to
+		// add a large number of inputs to a file without exceeding bash command length limits (which
+		// would happen if we use the WriteFile rule). The cp is needed because RuleBuilder sets the
+		// rsp file to be ${output}.rsp.
+		impCmd.Text("cp").FlagWithRspFileInputList("", implicits).Output(implicitsRsp)
+		impRule.Build(pctx, ctx, "implicitsGen", "implicits generation")
+		cmd.Implicits(implicits)
+		cmd.Implicit(implicitsRsp)
+	}
 	if ctx.Config().IsEnvTrue("RBE_METALAVA") {
 		rule.Remoteable(android.RemoteRuleSupports{RBE: true})
 		execStrategy := remoteexec.LocalExecStrategy
@@ -1388,7 +1453,6 @@
 			pool = v
 		}
 		inputs := []string{android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "framework", "metalava.jar").String()}
-		inputs = append(inputs, sourcepaths.Strings()...)
 		if v := ctx.Config().Getenv("RBE_METALAVA_INPUTS"); v != "" {
 			inputs = append(inputs, strings.Split(v, ",")...)
 		}
@@ -1396,6 +1460,7 @@
 			Labels:          map[string]string{"type": "compile", "lang": "java", "compiler": "metalava"},
 			ExecStrategy:    execStrategy,
 			Inputs:          inputs,
+			RSPFile:         rspFile,
 			ToolchainInputs: []string{config.JavaCmd(ctx).String()},
 			Platform:        map[string]string{remoteexec.PoolKey: pool},
 		}).NoVarTemplate(ctx.Config()))
@@ -1453,7 +1518,7 @@
 	srcJarList := zipSyncCmd(ctx, rule, srcJarDir, d.Javadoc.srcJars)
 
 	cmd := metalavaCmd(ctx, rule, javaVersion, d.Javadoc.srcFiles, srcJarList,
-		deps.bootClasspath, deps.classpath, d.Javadoc.sourcepaths)
+		deps.bootClasspath, deps.classpath, d.Javadoc.sourcepaths, d.Javadoc.implicits)
 
 	d.stubsFlags(ctx, cmd, stubsDir)
 
@@ -1908,6 +1973,10 @@
 	}
 }
 
+func (d *PrebuiltStubsSources) StubsSrcJar() android.Path {
+	return d.stubsSrcJar
+}
+
 func (p *PrebuiltStubsSources) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	p.stubsSrcJar = android.PathForModuleOut(ctx, ctx.ModuleName()+"-"+"stubs.srcjar")
 
diff --git a/java/hiddenapi.go b/java/hiddenapi.go
index 884a757..ce624bf 100644
--- a/java/hiddenapi.go
+++ b/java/hiddenapi.go
@@ -180,7 +180,9 @@
 	// b/149353192: when a module is instrumented, jacoco adds synthetic members
 	// $jacocoData and $jacocoInit. Since they don't exist when building the hidden API flags,
 	// don't complain when we don't find hidden API flags for the synthetic members.
-	if j, ok := ctx.Module().(*Library); ok && j.shouldInstrument(ctx) {
+	if j, ok := ctx.Module().(interface {
+		shouldInstrument(android.BaseModuleContext) bool
+	}); ok && j.shouldInstrument(ctx) {
 		enforceHiddenApiFlagsToAllMembers = false
 	}
 
diff --git a/java/java.go b/java/java.go
index 9d75c74..76bfa86 100644
--- a/java/java.go
+++ b/java/java.go
@@ -95,8 +95,7 @@
 }
 
 func (j *Module) checkSdkVersions(ctx android.ModuleContext) {
-	if j.SocSpecific() || j.DeviceSpecific() ||
-		(j.ProductSpecific() && ctx.Config().EnforceProductPartitionInterface()) {
+	if j.RequiresStableAPIs(ctx) {
 		if sc, ok := ctx.Module().(sdkContext); ok {
 			if !sc.sdkVersion().specified() {
 				ctx.PropertyErrorf("sdk_version",
@@ -343,14 +342,45 @@
 	// set the name of the output
 	Stem *string
 
-	UncompressDex bool `blueprint:"mutated"`
-	IsSDKLibrary  bool `blueprint:"mutated"`
+	// Keep the data uncompressed. We always need uncompressed dex for execution,
+	// so this might actually save space by avoiding storing the same data twice.
+	// This defaults to reasonable value based on module and should not be set.
+	// It exists only to support ART tests.
+	Uncompress_dex *bool
+
+	IsSDKLibrary bool `blueprint:"mutated"`
 }
 
 func (me *CompilerDeviceProperties) EffectiveOptimizeEnabled() bool {
 	return BoolDefault(me.Optimize.Enabled, me.Optimize.EnabledByDefault)
 }
 
+// Functionality common to Module and Import
+//
+// It is embedded in Module so its functionality can be used by methods in Module
+// but it is currently only initialized by Import and Library.
+type embeddableInModuleAndImport struct {
+
+	// Functionality related to this being used as a component of a java_sdk_library.
+	EmbeddableSdkLibraryComponent
+}
+
+func (e *embeddableInModuleAndImport) initModuleAndImport(moduleBase *android.ModuleBase) {
+	e.initSdkLibraryComponent(moduleBase)
+}
+
+// Module/Import's DepIsInSameApex(...) delegates to this method.
+//
+// This cannot implement DepIsInSameApex(...) directly as that leads to ambiguity with
+// the one provided by ApexModuleBase.
+func (e *embeddableInModuleAndImport) depIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
+	// dependencies other than the static linkage are all considered crossing APEX boundary
+	if staticLibTag == ctx.OtherModuleDependencyTag(dep) {
+		return true
+	}
+	return false
+}
+
 // Module contains the properties and members used by all java module types
 type Module struct {
 	android.ModuleBase
@@ -358,6 +388,9 @@
 	android.ApexModuleBase
 	android.SdkBase
 
+	// Functionality common to Module and Import.
+	embeddableInModuleAndImport
+
 	properties       CompilerProperties
 	protoProperties  android.ProtoProperties
 	deviceProperties CompilerDeviceProperties
@@ -443,6 +476,9 @@
 	kytheFiles android.Paths
 
 	distFile android.Path
+
+	// Collect the module directory for IDE info in java/jdeps.go.
+	modulePaths []string
 }
 
 func (j *Module) OutputFiles(tag string) (android.Paths, error) {
@@ -474,11 +510,6 @@
 	JacocoReportClassesFile() android.Path
 }
 
-type SdkLibraryDependency interface {
-	SdkHeaderJars(ctx android.BaseModuleContext, sdkVersion sdkSpec) android.Paths
-	SdkImplementationJars(ctx android.BaseModuleContext, sdkVersion sdkSpec) android.Paths
-}
-
 type xref interface {
 	XrefJavaFiles() android.Paths
 }
@@ -625,13 +656,15 @@
 			}
 		} else if sdkDep.useModule {
 			ctx.AddVariationDependencies(nil, bootClasspathTag, sdkDep.bootclasspath...)
-			ctx.AddVariationDependencies(nil, systemModulesTag, sdkDep.systemModules)
 			ctx.AddVariationDependencies(nil, java9LibTag, sdkDep.java9Classpath...)
 			if j.deviceProperties.EffectiveOptimizeEnabled() && sdkDep.hasStandardLibs() {
 				ctx.AddVariationDependencies(nil, proguardRaiseTag, config.DefaultBootclasspathLibraries...)
 				ctx.AddVariationDependencies(nil, proguardRaiseTag, config.DefaultLibraries...)
 			}
 		}
+		if sdkDep.systemModules != "" {
+			ctx.AddVariationDependencies(nil, systemModulesTag, sdkDep.systemModules)
+		}
 
 		if ctx.ModuleName() == "android_stubs_current" ||
 			ctx.ModuleName() == "android_system_stubs_current" ||
@@ -906,6 +939,12 @@
 		}
 	}
 
+	// If this is a component library (stubs, etc.) for a java_sdk_library then
+	// add the name of that java_sdk_library to the exported sdk libs to make sure
+	// that, if necessary, a <uses-library> element for that java_sdk_library is
+	// added to the Android manifest.
+	j.exportedSdkLibs = append(j.exportedSdkLibs, j.OptionalImplicitSdkLibrary()...)
+
 	ctx.VisitDirectDeps(func(module android.Module) {
 		otherName := ctx.OtherModuleName(module)
 		tag := ctx.OtherModuleDependencyTag(module)
@@ -925,7 +964,7 @@
 			case libTag:
 				deps.classpath = append(deps.classpath, dep.SdkHeaderJars(ctx, j.sdkVersion())...)
 				// names of sdk libs that are directly depended are exported
-				j.exportedSdkLibs = append(j.exportedSdkLibs, otherName)
+				j.exportedSdkLibs = append(j.exportedSdkLibs, dep.OptionalImplicitSdkLibrary()...)
 			case staticLibTag:
 				ctx.ModuleErrorf("dependency on java_sdk_library %q can only be in libs", otherName)
 			}
@@ -1034,19 +1073,10 @@
 }
 
 func getJavaVersion(ctx android.ModuleContext, javaVersion string, sdkContext sdkContext) javaVersion {
-	sdk, err := sdkContext.sdkVersion().effectiveVersion(ctx)
-	if err != nil {
-		ctx.PropertyErrorf("sdk_version", "%s", err)
-	}
 	if javaVersion != "" {
 		return normalizeJavaVersion(ctx, javaVersion)
-	} else if ctx.Device() && sdk <= 23 {
-		return JAVA_VERSION_7
-	} else if ctx.Device() && sdk <= 29 {
-		return JAVA_VERSION_8
-	} else if ctx.Device() && ctx.Config().UnbundledBuildUsePrebuiltSdks() {
-		// TODO(b/142896162): once we have prebuilt system modules we can use 1.9 for unbundled builds
-		return JAVA_VERSION_8
+	} else if ctx.Device() {
+		return sdkContext.sdkVersion().defaultJavaLanguageVersion(ctx)
 	} else {
 		return JAVA_VERSION_9
 	}
@@ -1426,13 +1456,19 @@
 			serviceFile := file.String()
 			zipargs = append(zipargs, "-C", filepath.Dir(serviceFile), "-f", serviceFile)
 		}
+		rule := zip
+		args := map[string]string{
+			"jarArgs": "-P META-INF/services/ " + strings.Join(proptools.NinjaAndShellEscapeList(zipargs), " "),
+		}
+		if ctx.Config().IsEnvTrue("RBE_ZIP") {
+			rule = zipRE
+			args["implicits"] = strings.Join(services.Strings(), ",")
+		}
 		ctx.Build(pctx, android.BuildParams{
-			Rule:      zip,
+			Rule:      rule,
 			Output:    servicesJar,
 			Implicits: services,
-			Args: map[string]string{
-				"jarArgs": "-P META-INF/services/ " + strings.Join(proptools.NinjaAndShellEscapeList(zipargs), " "),
-			},
+			Args:      args,
 		})
 		jars = append(jars, servicesJar)
 	}
@@ -1545,7 +1581,7 @@
 
 		// Hidden API CSV generation and dex encoding
 		dexOutputFile = j.hiddenAPI.hiddenAPI(ctx, dexOutputFile, j.implementationJarFile,
-			j.deviceProperties.UncompressDex)
+			proptools.Bool(j.deviceProperties.Uncompress_dex))
 
 		// merge dex jar with resources if necessary
 		if j.resourceJar != nil {
@@ -1553,7 +1589,7 @@
 			combinedJar := android.PathForModuleOut(ctx, "dex-withres", jarName)
 			TransformJarsToJar(ctx, combinedJar, "for dex resources", jars, android.OptionalPath{},
 				false, nil, nil)
-			if j.deviceProperties.UncompressDex {
+			if *j.deviceProperties.Uncompress_dex {
 				combinedAlignedJar := android.PathForModuleOut(ctx, "dex-withres-aligned", jarName)
 				TransformZipAlign(ctx, combinedAlignedJar, combinedJar)
 				dexOutputFile = combinedAlignedJar
@@ -1754,6 +1790,7 @@
 	if j.expandJarjarRules != nil {
 		dpInfo.Jarjar_rules = append(dpInfo.Jarjar_rules, j.expandJarjarRules.String())
 	}
+	dpInfo.Paths = append(dpInfo.Paths, j.modulePaths...)
 }
 
 func (j *Module) CompilerDeps() []string {
@@ -1769,11 +1806,7 @@
 }
 
 func (j *Module) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
-	// Dependencies other than the static linkage are all considered crossing APEX boundary
-	if staticLibTag == ctx.OtherModuleDependencyTag(dep) {
-		return true
-	}
-	return false
+	return j.depIsInSameApex(ctx, dep)
 }
 
 func (j *Module) Stem() string {
@@ -1807,6 +1840,17 @@
 	InstallMixin func(ctx android.ModuleContext, installPath android.Path) (extraInstallDeps android.Paths)
 }
 
+// Provides access to the list of permitted packages from updatable boot jars.
+type PermittedPackagesForUpdatableBootJars interface {
+	PermittedPackagesForUpdatableBootJars() []string
+}
+
+var _ PermittedPackagesForUpdatableBootJars = (*Library)(nil)
+
+func (j *Library) PermittedPackagesForUpdatableBootJars() []string {
+	return j.properties.Permitted_packages
+}
+
 func shouldUncompressDex(ctx android.ModuleContext, dexpreopter *dexpreopter) bool {
 	// Store uncompressed (and aligned) any dex files from jars in APEXes.
 	if am, ok := ctx.Module().(android.ApexModule); ok && !am.IsForPlatform() {
@@ -1834,10 +1878,16 @@
 	j.checkSdkVersions(ctx)
 	j.dexpreopter.installPath = android.PathForModuleInstall(ctx, "framework", j.Stem()+".jar")
 	j.dexpreopter.isSDKLibrary = j.deviceProperties.IsSDKLibrary
-	j.dexpreopter.uncompressedDex = shouldUncompressDex(ctx, &j.dexpreopter)
-	j.deviceProperties.UncompressDex = j.dexpreopter.uncompressedDex
+	if j.deviceProperties.Uncompress_dex == nil {
+		// If the value was not force-set by the user, use reasonable default based on the module.
+		j.deviceProperties.Uncompress_dex = proptools.BoolPtr(shouldUncompressDex(ctx, &j.dexpreopter))
+	}
+	j.dexpreopter.uncompressedDex = *j.deviceProperties.Uncompress_dex
 	j.compile(ctx, nil)
 
+	// Collect the module directory for IDE info in java/jdeps.go.
+	j.modulePaths = append(j.modulePaths, ctx.ModuleDir())
+
 	exclusivelyForApex := android.InAnyApex(ctx.ModuleName()) && !j.IsForPlatform()
 	if (Bool(j.properties.Installable) || ctx.Host()) && !exclusivelyForApex {
 		var extraInstallDeps android.Paths
@@ -1845,7 +1895,7 @@
 			extraInstallDeps = j.InstallMixin(ctx, j.outputFile)
 		}
 		j.installFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"),
-			ctx.ModuleName()+".jar", j.outputFile, extraInstallDeps...)
+			j.Stem()+".jar", j.outputFile, extraInstallDeps...)
 	}
 
 	// Verify Dist.Tag is set to a supported output
@@ -1979,6 +2029,8 @@
 		&module.Module.protoProperties,
 		&module.libraryProperties)
 
+	module.initModuleAndImport(&module.ModuleBase)
+
 	android.InitApexModule(module)
 	android.InitSdkAwareModule(module)
 	InitJavaModule(module, android.HostAndDeviceSupported)
@@ -2033,6 +2085,10 @@
 	// doesn't exist next to the Android.bp, this attribute doesn't need to be set to true
 	// explicitly.
 	Auto_gen_config *bool
+
+	// Add parameterized mainline modules to auto generated test config. The options will be
+	// handled by TradeFed to do downloading and installing the specified modules on the device.
+	Test_mainline_modules []string
 }
 
 type testHelperLibraryProperties struct {
@@ -2387,6 +2443,9 @@
 	prebuilt android.Prebuilt
 	android.SdkBase
 
+	// Functionality common to Module and Import.
+	embeddableInModuleAndImport
+
 	properties ImportProperties
 
 	combinedClasspathFile android.Path
@@ -2444,6 +2503,12 @@
 	}
 	j.combinedClasspathFile = outputFile
 
+	// If this is a component library (impl, stubs, etc.) for a java_sdk_library then
+	// add the name of that java_sdk_library to the exported sdk libs to make sure
+	// that, if necessary, a <uses-library> element for that java_sdk_library is
+	// added to the Android manifest.
+	j.exportedSdkLibs = append(j.exportedSdkLibs, j.OptionalImplicitSdkLibrary()...)
+
 	ctx.VisitDirectDeps(func(module android.Module) {
 		otherName := ctx.OtherModuleName(module)
 		tag := ctx.OtherModuleDependencyTag(module)
@@ -2521,11 +2586,7 @@
 }
 
 func (j *Import) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
-	// dependencies other than the static linkage are all considered crossing APEX boundary
-	if staticLibTag == ctx.OtherModuleDependencyTag(dep) {
-		return true
-	}
-	return false
+	return j.depIsInSameApex(ctx, dep)
 }
 
 // Add compile time check for interface implementation
@@ -2566,6 +2627,8 @@
 
 	module.AddProperties(&module.properties)
 
+	module.initModuleAndImport(&module.ModuleBase)
+
 	android.InitPrebuiltModule(module, &module.properties.Jars)
 	android.InitApexModule(module)
 	android.InitSdkAwareModule(module)
@@ -2682,7 +2745,7 @@
 	j.maybeStrippedDexJarFile = dexOutputFile
 
 	ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"),
-		ctx.ModuleName()+".jar", dexOutputFile)
+		j.Stem()+".jar", dexOutputFile)
 }
 
 func (j *DexImport) DexJar() android.Path {
@@ -2764,6 +2827,7 @@
 		&ImportProperties{},
 		&AARImportProperties{},
 		&sdkLibraryProperties{},
+		&commonToSdkLibraryAndImportProperties{},
 		&DexImportProperties{},
 		&android.ApexProperties{},
 		&RuntimeResourceOverlayProperties{},
diff --git a/java/java_test.go b/java/java_test.go
index 0033f31..8ea34d9 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -19,6 +19,8 @@
 	"os"
 	"path/filepath"
 	"reflect"
+	"regexp"
+	"sort"
 	"strconv"
 	"strings"
 	"testing"
@@ -575,6 +577,7 @@
 			},
 			test: {
 				jars: ["c.jar"],
+				stub_srcs: ["c.java"],
 			},
 		}
 		`)
@@ -1035,7 +1038,7 @@
 	for _, i := range metalavaRule.Implicits {
 		systemJars = append(systemJars, i.Base())
 	}
-	if len(systemJars) != 1 || systemJars[0] != systemJar {
+	if len(systemJars) < 1 || systemJars[0] != systemJar {
 		t.Errorf("inputs of %q must be []string{%q}, but was %#v.", moduleName, systemJar, systemJars)
 	}
 }
@@ -1152,13 +1155,42 @@
 		java_library {
 			name: "baz",
 			srcs: ["c.java"],
-			libs: ["foo", "bar"],
+			libs: ["foo", "bar.stubs"],
 			sdk_version: "system_current",
 		}
+		java_sdk_library {
+			name: "barney",
+			srcs: ["c.java"],
+			api_only: true,
+		}
+		java_sdk_library {
+			name: "betty",
+			srcs: ["c.java"],
+			shared_library: false,
+		}
+		java_sdk_library_import {
+		    name: "quuz",
+				public: {
+					jars: ["c.jar"],
+				},
+		}
+		java_sdk_library_import {
+		    name: "fred",
+				public: {
+					jars: ["b.jar"],
+				},
+		}
+		java_sdk_library_import {
+		    name: "wilma",
+				public: {
+					jars: ["b.jar"],
+				},
+				shared_library: false,
+		}
 		java_library {
 		    name: "qux",
 		    srcs: ["c.java"],
-		    libs: ["baz"],
+		    libs: ["baz", "fred", "quuz.stubs", "wilma", "barney", "betty"],
 		    sdk_version: "system_current",
 		}
 		java_library {
@@ -1177,12 +1209,12 @@
 
 	// check the existence of the internal modules
 	ctx.ModuleForTests("foo", "android_common")
-	ctx.ModuleForTests("foo"+sdkStubsLibrarySuffix, "android_common")
-	ctx.ModuleForTests("foo"+sdkStubsLibrarySuffix+sdkSystemApiSuffix, "android_common")
-	ctx.ModuleForTests("foo"+sdkStubsLibrarySuffix+sdkTestApiSuffix, "android_common")
-	ctx.ModuleForTests("foo"+sdkStubsSourceSuffix, "android_common")
-	ctx.ModuleForTests("foo"+sdkStubsSourceSuffix+sdkSystemApiSuffix, "android_common")
-	ctx.ModuleForTests("foo"+sdkStubsSourceSuffix+sdkTestApiSuffix, "android_common")
+	ctx.ModuleForTests(apiScopePublic.stubsLibraryModuleName("foo"), "android_common")
+	ctx.ModuleForTests(apiScopeSystem.stubsLibraryModuleName("foo"), "android_common")
+	ctx.ModuleForTests(apiScopeTest.stubsLibraryModuleName("foo"), "android_common")
+	ctx.ModuleForTests(apiScopePublic.stubsSourceModuleName("foo"), "android_common")
+	ctx.ModuleForTests(apiScopeSystem.stubsSourceModuleName("foo"), "android_common")
+	ctx.ModuleForTests(apiScopeTest.stubsSourceModuleName("foo"), "android_common")
 	ctx.ModuleForTests("foo"+sdkXmlFileSuffix, "android_common")
 	ctx.ModuleForTests("foo.api.public.28", "")
 	ctx.ModuleForTests("foo.api.system.28", "")
@@ -1223,12 +1255,145 @@
 	qux := ctx.ModuleForTests("qux", "android_common")
 	if quxLib, ok := qux.Module().(*Library); ok {
 		sdkLibs := quxLib.ExportedSdkLibs()
-		if len(sdkLibs) != 2 || !android.InList("foo", sdkLibs) || !android.InList("bar", sdkLibs) {
-			t.Errorf("qux should export \"foo\" and \"bar\" but exports %v", sdkLibs)
+		sort.Strings(sdkLibs)
+		if w := []string{"bar", "foo", "fred", "quuz"}; !reflect.DeepEqual(w, sdkLibs) {
+			t.Errorf("qux should export %q but exports %q", w, sdkLibs)
 		}
 	}
 }
 
+func TestJavaSdkLibrary_DoNotAccessImplWhenItIsNotBuilt(t *testing.T) {
+	ctx, _ := testJava(t, `
+		java_sdk_library {
+			name: "foo",
+			srcs: ["a.java"],
+			api_only: true,
+			public: {
+				enabled: true,
+			},
+		}
+
+		java_library {
+			name: "bar",
+			srcs: ["b.java"],
+			libs: ["foo"],
+		}
+		`)
+
+	// The bar library should depend on the stubs jar.
+	barLibrary := ctx.ModuleForTests("bar", "android_common").Rule("javac")
+	if expected, actual := `^-classpath .*:/[^:]*/turbine-combined/foo\.stubs\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
+		t.Errorf("expected %q, found %#q", expected, actual)
+	}
+}
+
+func TestJavaSdkLibrary_UseSourcesFromAnotherSdkLibrary(t *testing.T) {
+	testJava(t, `
+		java_sdk_library {
+			name: "foo",
+			srcs: ["a.java"],
+			api_packages: ["foo"],
+			public: {
+				enabled: true,
+			},
+		}
+
+		java_library {
+			name: "bar",
+			srcs: ["b.java", ":foo{.public.stubs.source}"],
+		}
+		`)
+}
+
+func TestJavaSdkLibrary_AccessOutputFiles_MissingScope(t *testing.T) {
+	testJavaError(t, `"foo" does not provide api scope system`, `
+		java_sdk_library {
+			name: "foo",
+			srcs: ["a.java"],
+			api_packages: ["foo"],
+			public: {
+				enabled: true,
+			},
+		}
+
+		java_library {
+			name: "bar",
+			srcs: ["b.java", ":foo{.system.stubs.source}"],
+		}
+		`)
+}
+
+func TestJavaSdkLibraryImport_AccessOutputFiles(t *testing.T) {
+	testJava(t, `
+		java_sdk_library_import {
+			name: "foo",
+			public: {
+				jars: ["a.jar"],
+				stub_srcs: ["a.java"],
+				current_api: "api/current.txt",
+				removed_api: "api/removed.txt",
+			},
+		}
+
+		java_library {
+			name: "bar",
+			srcs: [":foo{.public.stubs.source}"],
+			java_resources: [
+				":foo{.public.api.txt}",
+				":foo{.public.removed-api.txt}",
+			],
+		}
+		`)
+}
+
+func TestJavaSdkLibraryImport_AccessOutputFiles_Invalid(t *testing.T) {
+	bp := `
+		java_sdk_library_import {
+			name: "foo",
+			public: {
+				jars: ["a.jar"],
+			},
+		}
+		`
+
+	t.Run("stubs.source", func(t *testing.T) {
+		testJavaError(t, `stubs.source not available for api scope public`, bp+`
+		java_library {
+			name: "bar",
+			srcs: [":foo{.public.stubs.source}"],
+			java_resources: [
+				":foo{.public.api.txt}",
+				":foo{.public.removed-api.txt}",
+			],
+		}
+		`)
+	})
+
+	t.Run("api.txt", func(t *testing.T) {
+		testJavaError(t, `api.txt not available for api scope public`, bp+`
+		java_library {
+			name: "bar",
+			srcs: ["a.java"],
+			java_resources: [
+				":foo{.public.api.txt}",
+			],
+		}
+		`)
+	})
+
+	t.Run("removed-api.txt", func(t *testing.T) {
+		testJavaError(t, `removed-api.txt not available for api scope public`, bp+`
+		java_library {
+			name: "bar",
+			srcs: ["a.java"],
+			java_resources: [
+				":foo{.public.removed-api.txt}",
+			],
+		}
+		`)
+	})
+}
+
 func TestJavaSdkLibrary_InvalidScopes(t *testing.T) {
 	testJavaError(t, `module "foo": enabled api scope "system" depends on disabled scope "public"`, `
 		java_sdk_library {
@@ -1247,6 +1412,86 @@
 		`)
 }
 
+func TestJavaSdkLibrary_SdkVersion_ForScope(t *testing.T) {
+	testJava(t, `
+		java_sdk_library {
+			name: "foo",
+			srcs: ["a.java", "b.java"],
+			api_packages: ["foo"],
+			system: {
+				enabled: true,
+				sdk_version: "module_current",
+			},
+		}
+		`)
+}
+
+func TestJavaSdkLibrary_MissingScope(t *testing.T) {
+	testJavaError(t, `requires api scope module-lib from foo but it only has \[\] available`, `
+		java_sdk_library {
+			name: "foo",
+			srcs: ["a.java"],
+			public: {
+				enabled: false,
+			},
+		}
+
+		java_library {
+			name: "baz",
+			srcs: ["a.java"],
+			libs: ["foo"],
+			sdk_version: "module_current",
+		}
+		`)
+}
+
+func TestJavaSdkLibrary_FallbackScope(t *testing.T) {
+	testJava(t, `
+		java_sdk_library {
+			name: "foo",
+			srcs: ["a.java"],
+			system: {
+				enabled: true,
+			},
+		}
+
+		java_library {
+			name: "baz",
+			srcs: ["a.java"],
+			libs: ["foo"],
+			// foo does not have module-lib scope so it should fallback to system
+			sdk_version: "module_current",
+		}
+		`)
+}
+
+func TestJavaSdkLibrary_DefaultToStubs(t *testing.T) {
+	ctx, _ := testJava(t, `
+		java_sdk_library {
+			name: "foo",
+			srcs: ["a.java"],
+			system: {
+				enabled: true,
+			},
+			default_to_stubs: true,
+		}
+
+		java_library {
+			name: "baz",
+			srcs: ["a.java"],
+			libs: ["foo"],
+			// does not have sdk_version set, should fallback to module,
+			// which will then fallback to system because the module scope
+			// is not enabled.
+		}
+		`)
+	// The baz library should depend on the system stubs jar.
+	bazLibrary := ctx.ModuleForTests("baz", "android_common").Rule("javac")
+	if expected, actual := `^-classpath .*:/[^:]*/turbine-combined/foo\.stubs.system\.jar$`, bazLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
+		t.Errorf("expected %q, found %#q", expected, actual)
+	}
+}
+
 var compilerFlagsTestCases = []struct {
 	in  string
 	out bool
diff --git a/java/jdeps.go b/java/jdeps.go
index 49e3de3..9f43887 100644
--- a/java/jdeps.go
+++ b/java/jdeps.go
@@ -72,6 +72,7 @@
 		dpInfo.Jarjar_rules = android.FirstUniqueStrings(dpInfo.Jarjar_rules)
 		dpInfo.Jars = android.FirstUniqueStrings(dpInfo.Jars)
 		dpInfo.SrcJars = android.FirstUniqueStrings(dpInfo.SrcJars)
+		dpInfo.Paths = android.FirstUniqueStrings(dpInfo.Paths)
 		moduleInfos[name] = dpInfo
 
 		mkProvider, ok := module.(android.AndroidMkDataProvider)
diff --git a/java/prebuilt_apis.go b/java/prebuilt_apis.go
index 03bc76b..999c72f 100644
--- a/java/prebuilt_apis.go
+++ b/java/prebuilt_apis.go
@@ -15,11 +15,12 @@
 package java
 
 import (
-	"android/soong/android"
 	"sort"
 	"strings"
 
 	"github.com/google/blueprint/proptools"
+
+	"android/soong/android"
 )
 
 func init() {
@@ -69,6 +70,10 @@
 	return
 }
 
+func prebuiltApiModuleName(mctx android.LoadHookContext, module string, scope string, apiver string) string {
+	return mctx.ModuleName() + "_" + scope + "_" + apiver + "_" + module
+}
+
 func createImport(mctx android.LoadHookContext, module string, scope string, apiver string, path string) {
 	props := struct {
 		Name        *string
@@ -76,7 +81,7 @@
 		Sdk_version *string
 		Installable *bool
 	}{}
-	props.Name = proptools.StringPtr(mctx.ModuleName() + "_" + scope + "_" + apiver + "_" + module)
+	props.Name = proptools.StringPtr(prebuiltApiModuleName(mctx, module, scope, apiver))
 	props.Jars = append(props.Jars, path)
 	// TODO(hansson): change to scope after migration is done.
 	props.Sdk_version = proptools.StringPtr("current")
@@ -124,6 +129,27 @@
 	}
 }
 
+func createSystemModules(mctx android.LoadHookContext, apiver string) {
+	props := struct {
+		Name *string
+		Libs []string
+	}{}
+	props.Name = proptools.StringPtr(prebuiltApiModuleName(mctx, "system_modules", "public", apiver))
+	props.Libs = append(props.Libs, prebuiltApiModuleName(mctx, "core-for-system-modules", "public", apiver))
+
+	mctx.CreateModule(SystemModulesFactory, &props)
+}
+
+func prebuiltSdkSystemModules(mctx android.LoadHookContext) {
+	for _, apiver := range mctx.Module().(*prebuiltApis).properties.Api_dirs {
+		jar := android.ExistentPathForSource(mctx,
+			mctx.ModuleDir(), apiver, "public", "core-for-system-modules.jar")
+		if jar.Valid() {
+			createSystemModules(mctx, apiver)
+		}
+	}
+}
+
 func prebuiltApiFiles(mctx android.LoadHookContext) {
 	mydir := mctx.ModuleDir() + "/"
 	// <apiver>/<scope>/api/<module>.txt
@@ -178,6 +204,7 @@
 	if _, ok := mctx.Module().(*prebuiltApis); ok {
 		prebuiltApiFiles(mctx)
 		prebuiltSdkStubs(mctx)
+		prebuiltSdkSystemModules(mctx)
 	}
 }
 
@@ -191,7 +218,9 @@
 // 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>.
+// <prebuilt-api-module>_<scope>_<ver>_<module>, and for SDK versions >= 30
+// a java_system_modules module named
+// <prebuilt-api-module>_public_<ver>_system_modules
 func PrebuiltApisFactory() android.Module {
 	module := &prebuiltApis{}
 	module.AddProperties(&module.properties)
diff --git a/java/sdk.go b/java/sdk.go
index 690451c..9310f78 100644
--- a/java/sdk.go
+++ b/java/sdk.go
@@ -252,6 +252,20 @@
 	return ver.String(), err
 }
 
+func (s sdkSpec) defaultJavaLanguageVersion(ctx android.EarlyModuleContext) javaVersion {
+	sdk, err := s.effectiveVersion(ctx)
+	if err != nil {
+		ctx.PropertyErrorf("sdk_version", "%s", err)
+	}
+	if sdk <= 23 {
+		return JAVA_VERSION_7
+	} else if sdk <= 29 {
+		return JAVA_VERSION_8
+	} else {
+		return JAVA_VERSION_9
+	}
+}
+
 func sdkSpecFrom(str string) sdkSpec {
 	switch str {
 	// special cases first
@@ -370,10 +384,16 @@
 			return sdkDep{}
 		}
 
+		var systemModules string
+		if sdkVersion.defaultJavaLanguageVersion(ctx).usesJavaModules() {
+			systemModules = "sdk_public_" + sdkVersion.version.String() + "_system_modules"
+		}
+
 		return sdkDep{
-			useFiles: true,
-			jars:     android.Paths{jarPath.Path(), lambdaStubsPath},
-			aidl:     android.OptionalPathForPath(aidlPath.Path()),
+			useFiles:      true,
+			jars:          android.Paths{jarPath.Path(), lambdaStubsPath},
+			aidl:          android.OptionalPathForPath(aidlPath.Path()),
+			systemModules: systemModules,
 		}
 	}
 
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 2c85c8c..c4d257f 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -19,6 +19,7 @@
 	"path"
 	"path/filepath"
 	"reflect"
+	"regexp"
 	"sort"
 	"strings"
 	"sync"
@@ -30,12 +31,8 @@
 )
 
 const (
-	sdkStubsLibrarySuffix = ".stubs"
-	sdkSystemApiSuffix    = ".system"
-	sdkTestApiSuffix      = ".test"
-	sdkStubsSourceSuffix  = ".stubs.source"
-	sdkXmlFileSuffix      = ".xml"
-	permissionsTemplate   = `<?xml version=\"1.0\" encoding=\"utf-8\"?>\n` +
+	sdkXmlFileSuffix    = ".xml"
+	permissionsTemplate = `<?xml version=\"1.0\" encoding=\"utf-8\"?>\n` +
 		`<!-- Copyright (C) 2018 The Android Open Source Project\n` +
 		`\n` +
 		`    Licensed under the Apache License, Version 2.0 (the \"License\");\n` +
@@ -96,9 +93,18 @@
 	// The name of the field in the dynamically created structure.
 	fieldName string
 
+	// The name of the property in the java_sdk_library_import
+	propertyName string
+
 	// The tag to use to depend on the stubs library module.
 	stubsTag scopeDependencyTag
 
+	// The tag to use to depend on the stubs source module (if separate from the API module).
+	stubsSourceTag scopeDependencyTag
+
+	// The tag to use to depend on the API file generating module (if separate from the stubs source module).
+	apiFileTag scopeDependencyTag
+
 	// The tag to use to depend on the stubs source and API module.
 	stubsSourceAndApiTag scopeDependencyTag
 
@@ -117,6 +123,22 @@
 	// Extra arguments to pass to droidstubs for this scope.
 	droidstubsArgs []string
 
+	// The args that must be passed to droidstubs to generate the stubs source
+	// for this scope.
+	//
+	// The stubs source must include the definitions of everything that is in this
+	// api scope and all the scopes that this one extends.
+	droidstubsArgsForGeneratingStubsSource []string
+
+	// The args that must be passed to droidstubs to generate the API for this scope.
+	//
+	// The API only includes the additional members that this scope adds over the scope
+	// that it extends.
+	droidstubsArgsForGeneratingApi []string
+
+	// True if the stubs source and api can be created by the same metalava invocation.
+	createStubsSourceAndApiTogether bool
+
 	// Whether the api scope can be treated as unstable, and should skip compat checks.
 	unstable bool
 }
@@ -124,26 +146,63 @@
 // Initialize a scope, creating and adding appropriate dependency tags
 func initApiScope(scope *apiScope) *apiScope {
 	name := scope.name
-	scope.fieldName = proptools.FieldNameForProperty(name)
+	scopeByName[name] = scope
+	allScopeNames = append(allScopeNames, name)
+	scope.propertyName = strings.ReplaceAll(name, "-", "_")
+	scope.fieldName = proptools.FieldNameForProperty(scope.propertyName)
 	scope.stubsTag = scopeDependencyTag{
 		name:             name + "-stubs",
 		apiScope:         scope,
 		depInfoExtractor: (*scopePaths).extractStubsLibraryInfoFromDependency,
 	}
+	scope.stubsSourceTag = scopeDependencyTag{
+		name:             name + "-stubs-source",
+		apiScope:         scope,
+		depInfoExtractor: (*scopePaths).extractStubsSourceInfoFromDep,
+	}
+	scope.apiFileTag = scopeDependencyTag{
+		name:             name + "-api",
+		apiScope:         scope,
+		depInfoExtractor: (*scopePaths).extractApiInfoFromDep,
+	}
 	scope.stubsSourceAndApiTag = scopeDependencyTag{
 		name:             name + "-stubs-source-and-api",
 		apiScope:         scope,
 		depInfoExtractor: (*scopePaths).extractStubsSourceAndApiInfoFromApiStubsProvider,
 	}
+
+	// To get the args needed to generate the stubs source append all the args from
+	// this scope and all the scopes it extends as each set of args adds additional
+	// members to the stubs.
+	var stubsSourceArgs []string
+	for s := scope; s != nil; s = s.extends {
+		stubsSourceArgs = append(stubsSourceArgs, s.droidstubsArgs...)
+	}
+	scope.droidstubsArgsForGeneratingStubsSource = stubsSourceArgs
+
+	// Currently the args needed to generate the API are the same as the args
+	// needed to add additional members.
+	apiArgs := scope.droidstubsArgs
+	scope.droidstubsArgsForGeneratingApi = apiArgs
+
+	// If the args needed to generate the stubs and API are the same then they
+	// can be generated in a single invocation of metalava, otherwise they will
+	// need separate invocations.
+	scope.createStubsSourceAndApiTogether = reflect.DeepEqual(stubsSourceArgs, apiArgs)
+
 	return scope
 }
 
-func (scope *apiScope) stubsModuleName(baseName string) string {
-	return baseName + sdkStubsLibrarySuffix + scope.moduleSuffix
+func (scope *apiScope) stubsLibraryModuleName(baseName string) string {
+	return baseName + ".stubs" + scope.moduleSuffix
 }
 
 func (scope *apiScope) stubsSourceModuleName(baseName string) string {
-	return baseName + sdkStubsSourceSuffix + scope.moduleSuffix
+	return baseName + ".stubs.source" + scope.moduleSuffix
+}
+
+func (scope *apiScope) apiModuleName(baseName string) string {
+	return baseName + ".api" + scope.moduleSuffix
 }
 
 func (scope *apiScope) String() string {
@@ -161,6 +220,8 @@
 }
 
 var (
+	scopeByName    = make(map[string]*apiScope)
+	allScopeNames  []string
 	apiScopePublic = initApiScope(&apiScope{
 		name: "public",
 
@@ -183,7 +244,7 @@
 			return &module.sdkLibraryProperties.System
 		},
 		apiFilePrefix:  "system-",
-		moduleSuffix:   sdkSystemApiSuffix,
+		moduleSuffix:   ".system",
 		sdkVersion:     "system_current",
 		droidstubsArgs: []string{"-showAnnotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\)"},
 	})
@@ -195,13 +256,13 @@
 			return &module.sdkLibraryProperties.Test
 		},
 		apiFilePrefix:  "test-",
-		moduleSuffix:   sdkTestApiSuffix,
+		moduleSuffix:   ".test",
 		sdkVersion:     "test_current",
 		droidstubsArgs: []string{"-showAnnotation android.annotation.TestApi"},
 		unstable:       true,
 	})
 	apiScopeModuleLib = initApiScope(&apiScope{
-		name:    "module_lib",
+		name:    "module-lib",
 		extends: apiScopeSystem,
 		// Module_lib scope is disabled by default in legacy mode.
 		//
@@ -268,9 +329,29 @@
 	// Otherwise, if this is not set for any scope then the default  behavior is
 	// scope specific so please refer to the scope specific property documentation.
 	Enabled *bool
+
+	// The sdk_version to use for building the stubs.
+	//
+	// If not specified then it will use an sdk_version determined as follows:
+	// 1) If the sdk_version specified on the java_sdk_library is none then this
+	//    will be none. This is used for java_sdk_library instances that are used
+	//    to create stubs that contribute to the core_current sdk version.
+	// 2) Otherwise, it is assumed that this library extends but does not contribute
+	//    directly to a specific sdk_version and so this uses the sdk_version appropriate
+	//    for the api scope. e.g. public will use sdk_version: current, system will use
+	//    sdk_version: system_current, etc.
+	//
+	// This does not affect the sdk_version used for either generating the stubs source
+	// or the API file. They both have to use the same sdk_version as is used for
+	// compiling the implementation library.
+	Sdk_version *string
 }
 
 type sdkLibraryProperties struct {
+	// Visibility for impl library module. If not specified then defaults to the
+	// visibility property.
+	Impl_library_visibility []string
+
 	// Visibility for stubs library modules. If not specified then defaults to the
 	// visibility property.
 	Stubs_library_visibility []string
@@ -295,7 +376,10 @@
 	// Defaults to "api".
 	Api_dir *string
 
-	// If set to true there is no runtime library.
+	// Determines whether a runtime implementation library is built; defaults to false.
+	//
+	// If true then it also prevents the module from being used as a shared module, i.e.
+	// it is as is shared_library: false, was set.
 	Api_only *bool
 
 	// local files that are used within user customized droiddoc options.
@@ -351,16 +435,48 @@
 	// disabled by default.
 	Module_lib ApiScopeProperties
 
+	// Determines if the stubs are preferred over the implementation library
+	// for linking, even when the client doesn't specify sdk_version. When this
+	// is set to true, such clients are provided with the widest API surface that
+	// this lib provides. Note however that this option doesn't affect the clients
+	// that are in the same APEX as this library. In that case, the clients are
+	// always linked with the implementation library. Default is false.
+	Default_to_stubs *bool
+
+	// Properties related to api linting.
+	Api_lint struct {
+		// Enable api linting.
+		Enabled *bool
+	}
+
 	// TODO: determines whether to create HTML doc or not
 	//Html_doc *bool
 }
 
+// Paths to outputs from java_sdk_library and java_sdk_library_import.
+//
+// Fields that are android.Paths are always set (during GenerateAndroidBuildActions).
+// OptionalPaths are always set by java_sdk_library but may not be set by
+// java_sdk_library_import as not all instances provide that information.
 type scopePaths struct {
-	stubsHeaderPath    android.Paths
-	stubsImplPath      android.Paths
-	currentApiFilePath android.Path
-	removedApiFilePath android.Path
-	stubsSrcJar        android.Path
+	// The path (represented as Paths for convenience when returning) to the stubs header jar.
+	//
+	// That is the jar that is created by turbine.
+	stubsHeaderPath android.Paths
+
+	// The path (represented as Paths for convenience when returning) to the stubs implementation jar.
+	//
+	// This is not the implementation jar, it still only contains stubs.
+	stubsImplPath android.Paths
+
+	// The API specification file, e.g. system_current.txt.
+	currentApiFilePath android.OptionalPath
+
+	// The specification of API elements removed since the last release.
+	removedApiFilePath android.OptionalPath
+
+	// The stubs source jar.
+	stubsSrcJar android.OptionalPath
 }
 
 func (paths *scopePaths) extractStubsLibraryInfoFromDependency(dep android.Module) error {
@@ -373,23 +489,211 @@
 	}
 }
 
-func (paths *scopePaths) extractStubsSourceAndApiInfoFromApiStubsProvider(dep android.Module) error {
-	if provider, ok := dep.(ApiStubsProvider); ok {
-		paths.currentApiFilePath = provider.ApiFilePath()
-		paths.removedApiFilePath = provider.RemovedApiFilePath()
-		paths.stubsSrcJar = provider.StubsSrcJar()
+func (paths *scopePaths) treatDepAsApiStubsProvider(dep android.Module, action func(provider ApiStubsProvider)) error {
+	if apiStubsProvider, ok := dep.(ApiStubsProvider); ok {
+		action(apiStubsProvider)
 		return nil
 	} else {
 		return fmt.Errorf("expected module that implements ApiStubsProvider, e.g. droidstubs")
 	}
 }
 
-// Common code between sdk library and sdk library import
-type commonToSdkLibraryAndImport struct {
-	scopePaths map[*apiScope]*scopePaths
+func (paths *scopePaths) treatDepAsApiStubsSrcProvider(dep android.Module, action func(provider ApiStubsSrcProvider)) error {
+	if apiStubsProvider, ok := dep.(ApiStubsSrcProvider); ok {
+		action(apiStubsProvider)
+		return nil
+	} else {
+		return fmt.Errorf("expected module that implements ApiStubsSrcProvider, e.g. droidstubs")
+	}
 }
 
-func (c *commonToSdkLibraryAndImport) getScopePaths(scope *apiScope) *scopePaths {
+func (paths *scopePaths) extractApiInfoFromApiStubsProvider(provider ApiStubsProvider) {
+	paths.currentApiFilePath = android.OptionalPathForPath(provider.ApiFilePath())
+	paths.removedApiFilePath = android.OptionalPathForPath(provider.RemovedApiFilePath())
+}
+
+func (paths *scopePaths) extractApiInfoFromDep(dep android.Module) error {
+	return paths.treatDepAsApiStubsProvider(dep, func(provider ApiStubsProvider) {
+		paths.extractApiInfoFromApiStubsProvider(provider)
+	})
+}
+
+func (paths *scopePaths) extractStubsSourceInfoFromApiStubsProviders(provider ApiStubsSrcProvider) {
+	paths.stubsSrcJar = android.OptionalPathForPath(provider.StubsSrcJar())
+}
+
+func (paths *scopePaths) extractStubsSourceInfoFromDep(dep android.Module) error {
+	return paths.treatDepAsApiStubsSrcProvider(dep, func(provider ApiStubsSrcProvider) {
+		paths.extractStubsSourceInfoFromApiStubsProviders(provider)
+	})
+}
+
+func (paths *scopePaths) extractStubsSourceAndApiInfoFromApiStubsProvider(dep android.Module) error {
+	return paths.treatDepAsApiStubsProvider(dep, func(provider ApiStubsProvider) {
+		paths.extractApiInfoFromApiStubsProvider(provider)
+		paths.extractStubsSourceInfoFromApiStubsProviders(provider)
+	})
+}
+
+type commonToSdkLibraryAndImportProperties struct {
+	// The naming scheme to use for the components that this module creates.
+	//
+	// If not specified then it defaults to "default". The other allowable value is
+	// "framework-modules" which matches the scheme currently used by framework modules
+	// for the equivalent components represented as separate Soong modules.
+	//
+	// This is a temporary mechanism to simplify conversion from separate modules for each
+	// component that follow a different naming pattern to the default one.
+	//
+	// TODO(b/155480189) - Remove once naming inconsistencies have been resolved.
+	Naming_scheme *string
+
+	// Specifies whether this module can be used as an Android shared library; defaults
+	// to true.
+	//
+	// An Android shared library is one that can be referenced in a <uses-library> element
+	// in an AndroidManifest.xml.
+	Shared_library *bool
+}
+
+// Common code between sdk library and sdk library import
+type commonToSdkLibraryAndImport struct {
+	moduleBase *android.ModuleBase
+
+	scopePaths map[*apiScope]*scopePaths
+
+	namingScheme sdkLibraryComponentNamingScheme
+
+	commonSdkLibraryProperties commonToSdkLibraryAndImportProperties
+
+	// Functionality related to this being used as a component of a java_sdk_library.
+	EmbeddableSdkLibraryComponent
+}
+
+func (c *commonToSdkLibraryAndImport) initCommon(moduleBase *android.ModuleBase) {
+	c.moduleBase = moduleBase
+
+	moduleBase.AddProperties(&c.commonSdkLibraryProperties)
+
+	// Initialize this as an sdk library component.
+	c.initSdkLibraryComponent(moduleBase)
+}
+
+func (c *commonToSdkLibraryAndImport) initCommonAfterDefaultsApplied(ctx android.DefaultableHookContext) bool {
+	schemeProperty := proptools.StringDefault(c.commonSdkLibraryProperties.Naming_scheme, "default")
+	switch schemeProperty {
+	case "default":
+		c.namingScheme = &defaultNamingScheme{}
+	case "framework-modules":
+		c.namingScheme = &frameworkModulesNamingScheme{}
+	default:
+		ctx.PropertyErrorf("naming_scheme", "expected 'default' but was %q", schemeProperty)
+		return false
+	}
+
+	// Only track this sdk library if this can be used as a shared library.
+	if c.sharedLibrary() {
+		// Use the name specified in the module definition as the owner.
+		c.sdkLibraryComponentProperties.SdkLibraryToImplicitlyTrack = proptools.StringPtr(c.moduleBase.BaseModuleName())
+	}
+
+	return true
+}
+
+// Name of the java_library module that compiles the stubs source.
+func (c *commonToSdkLibraryAndImport) stubsLibraryModuleName(apiScope *apiScope) string {
+	return c.namingScheme.stubsLibraryModuleName(apiScope, c.moduleBase.BaseModuleName())
+}
+
+// Name of the droidstubs module that generates the stubs source and may also
+// generate/check the API.
+func (c *commonToSdkLibraryAndImport) stubsSourceModuleName(apiScope *apiScope) string {
+	return c.namingScheme.stubsSourceModuleName(apiScope, c.moduleBase.BaseModuleName())
+}
+
+// Name of the droidstubs module that generates/checks the API. Only used if it
+// requires different arts to the stubs source generating module.
+func (c *commonToSdkLibraryAndImport) apiModuleName(apiScope *apiScope) string {
+	return c.namingScheme.apiModuleName(apiScope, c.moduleBase.BaseModuleName())
+}
+
+// The component names for different outputs of the java_sdk_library.
+//
+// They are similar to the names used for the child modules it creates
+const (
+	stubsSourceComponentName = "stubs.source"
+
+	apiTxtComponentName = "api.txt"
+
+	removedApiTxtComponentName = "removed-api.txt"
+)
+
+// A regular expression to match tags that reference a specific stubs component.
+//
+// It will only match if given a valid scope and a valid component. It is verfy strict
+// to ensure it does not accidentally match a similar looking tag that should be processed
+// by the embedded Library.
+var tagSplitter = func() *regexp.Regexp {
+	// Given a list of literal string items returns a regular expression that will
+	// match any one of the items.
+	choice := func(items ...string) string {
+		return `\Q` + strings.Join(items, `\E|\Q`) + `\E`
+	}
+
+	// Regular expression to match one of the scopes.
+	scopesRegexp := choice(allScopeNames...)
+
+	// Regular expression to match one of the components.
+	componentsRegexp := choice(stubsSourceComponentName, apiTxtComponentName, removedApiTxtComponentName)
+
+	// Regular expression to match any combination of one scope and one component.
+	return regexp.MustCompile(fmt.Sprintf(`^\.(%s)\.(%s)$`, scopesRegexp, componentsRegexp))
+}()
+
+// For OutputFileProducer interface
+//
+// .<scope>.stubs.source
+// .<scope>.api.txt
+// .<scope>.removed-api.txt
+func (c *commonToSdkLibraryAndImport) commonOutputFiles(tag string) (android.Paths, error) {
+	if groups := tagSplitter.FindStringSubmatch(tag); groups != nil {
+		scopeName := groups[1]
+		component := groups[2]
+
+		if scope, ok := scopeByName[scopeName]; ok {
+			paths := c.findScopePaths(scope)
+			if paths == nil {
+				return nil, fmt.Errorf("%q does not provide api scope %s", c.moduleBase.BaseModuleName(), scopeName)
+			}
+
+			switch component {
+			case stubsSourceComponentName:
+				if paths.stubsSrcJar.Valid() {
+					return android.Paths{paths.stubsSrcJar.Path()}, nil
+				}
+
+			case apiTxtComponentName:
+				if paths.currentApiFilePath.Valid() {
+					return android.Paths{paths.currentApiFilePath.Path()}, nil
+				}
+
+			case removedApiTxtComponentName:
+				if paths.removedApiFilePath.Valid() {
+					return android.Paths{paths.removedApiFilePath.Path()}, nil
+				}
+			}
+
+			return nil, fmt.Errorf("%s not available for api scope %s", component, scopeName)
+		} else {
+			return nil, fmt.Errorf("unknown scope %s in %s", scope, tag)
+		}
+
+	} else {
+		return nil, nil
+	}
+}
+
+func (c *commonToSdkLibraryAndImport) getScopePathsCreateIfNeeded(scope *apiScope) *scopePaths {
 	if c.scopePaths == nil {
 		c.scopePaths = make(map[*apiScope]*scopePaths)
 	}
@@ -402,6 +706,147 @@
 	return paths
 }
 
+func (c *commonToSdkLibraryAndImport) findScopePaths(scope *apiScope) *scopePaths {
+	if c.scopePaths == nil {
+		return nil
+	}
+
+	return c.scopePaths[scope]
+}
+
+// If this does not support the requested api scope then find the closest available
+// scope it does support. Returns nil if no such scope is available.
+func (c *commonToSdkLibraryAndImport) findClosestScopePath(scope *apiScope) *scopePaths {
+	for s := scope; s != nil; s = s.extends {
+		if paths := c.findScopePaths(s); paths != nil {
+			return paths
+		}
+	}
+
+	// This should never happen outside tests as public should be the base scope for every
+	// scope and is enabled by default.
+	return nil
+}
+
+func (c *commonToSdkLibraryAndImport) selectHeaderJarsForSdkVersion(ctx android.BaseModuleContext, sdkVersion sdkSpec) android.Paths {
+
+	// If a specific numeric version has been requested then use prebuilt versions of the sdk.
+	if sdkVersion.version.isNumbered() {
+		return PrebuiltJars(ctx, c.moduleBase.BaseModuleName(), sdkVersion)
+	}
+
+	var apiScope *apiScope
+	switch sdkVersion.kind {
+	case sdkSystem:
+		apiScope = apiScopeSystem
+	case sdkModule:
+		apiScope = apiScopeModuleLib
+	case sdkTest:
+		apiScope = apiScopeTest
+	default:
+		apiScope = apiScopePublic
+	}
+
+	paths := c.findClosestScopePath(apiScope)
+	if paths == nil {
+		var scopes []string
+		for _, s := range allApiScopes {
+			if c.findScopePaths(s) != nil {
+				scopes = append(scopes, s.name)
+			}
+		}
+		ctx.ModuleErrorf("requires api scope %s from %s but it only has %q available", apiScope.name, c.moduleBase.BaseModuleName(), scopes)
+		return nil
+	}
+
+	return paths.stubsHeaderPath
+}
+
+func (c *commonToSdkLibraryAndImport) sdkComponentPropertiesForChildLibrary() interface{} {
+	componentProps := &struct {
+		SdkLibraryToImplicitlyTrack *string
+	}{}
+
+	if c.sharedLibrary() {
+		// Mark the stubs library as being components of this java_sdk_library so that
+		// any app that includes code which depends (directly or indirectly) on the stubs
+		// library will have the appropriate <uses-library> invocation inserted into its
+		// manifest if necessary.
+		componentProps.SdkLibraryToImplicitlyTrack = proptools.StringPtr(c.moduleBase.BaseModuleName())
+	}
+
+	return componentProps
+}
+
+// Check if this can be used as a shared library.
+func (c *commonToSdkLibraryAndImport) sharedLibrary() bool {
+	return proptools.BoolDefault(c.commonSdkLibraryProperties.Shared_library, true)
+}
+
+// Properties related to the use of a module as an component of a java_sdk_library.
+type SdkLibraryComponentProperties struct {
+
+	// The name of the java_sdk_library/_import to add to a <uses-library> entry
+	// in the AndroidManifest.xml of any Android app that includes code that references
+	// this module. If not set then no java_sdk_library/_import is tracked.
+	SdkLibraryToImplicitlyTrack *string `blueprint:"mutated"`
+}
+
+// Structure to be embedded in a module struct that needs to support the
+// SdkLibraryComponentDependency interface.
+type EmbeddableSdkLibraryComponent struct {
+	sdkLibraryComponentProperties SdkLibraryComponentProperties
+}
+
+func (e *EmbeddableSdkLibraryComponent) initSdkLibraryComponent(moduleBase *android.ModuleBase) {
+	moduleBase.AddProperties(&e.sdkLibraryComponentProperties)
+}
+
+// to satisfy SdkLibraryComponentDependency
+func (e *EmbeddableSdkLibraryComponent) OptionalImplicitSdkLibrary() []string {
+	if e.sdkLibraryComponentProperties.SdkLibraryToImplicitlyTrack != nil {
+		return []string{*e.sdkLibraryComponentProperties.SdkLibraryToImplicitlyTrack}
+	}
+	return nil
+}
+
+// Implemented by modules that are (or possibly could be) a component of a java_sdk_library
+// (including the java_sdk_library) itself.
+type SdkLibraryComponentDependency interface {
+	// The optional name of the sdk library that should be implicitly added to the
+	// AndroidManifest of an app that contains code which references the sdk library.
+	//
+	// Returns an array containing 0 or 1 items rather than a *string to make it easier
+	// to append this to the list of exported sdk libraries.
+	OptionalImplicitSdkLibrary() []string
+}
+
+// Make sure that all the module types that are components of java_sdk_library/_import
+// and which can be referenced (directly or indirectly) from an android app implement
+// the SdkLibraryComponentDependency interface.
+var _ SdkLibraryComponentDependency = (*Library)(nil)
+var _ SdkLibraryComponentDependency = (*Import)(nil)
+var _ SdkLibraryComponentDependency = (*SdkLibrary)(nil)
+var _ SdkLibraryComponentDependency = (*sdkLibraryImport)(nil)
+
+// Provides access to sdk_version related header and implentation jars.
+type SdkLibraryDependency interface {
+	SdkLibraryComponentDependency
+
+	// Get the header jars appropriate for the supplied sdk_version.
+	//
+	// These are turbine generated jars so they only change if the externals of the
+	// class changes but it does not contain and implementation or JavaDoc.
+	SdkHeaderJars(ctx android.BaseModuleContext, sdkVersion sdkSpec) android.Paths
+
+	// Get the implementation jars appropriate for the supplied sdk version.
+	//
+	// These are either the implementation jar for the whole sdk library or the implementation
+	// jars for the stubs. The latter should only be needed when generating JavaDoc as otherwise
+	// they are identical to the corresponding header jars.
+	SdkImplementationJars(ctx android.BaseModuleContext, sdkVersion sdkSpec) android.Paths
+}
+
 type SdkLibrary struct {
 	Library
 
@@ -477,26 +922,51 @@
 	return false
 }
 
+var implLibraryTag = dependencyTag{name: "impl-library"}
+
 func (module *SdkLibrary) DepsMutator(ctx android.BottomUpMutatorContext) {
 	for _, apiScope := range module.getGeneratedApiScopes(ctx) {
 		// Add dependencies to the stubs library
-		ctx.AddVariationDependencies(nil, apiScope.stubsTag, module.stubsName(apiScope))
+		ctx.AddVariationDependencies(nil, apiScope.stubsTag, module.stubsLibraryModuleName(apiScope))
 
-		// And the stubs source and api files
-		ctx.AddVariationDependencies(nil, apiScope.stubsSourceAndApiTag, module.stubsSourceName(apiScope))
+		// If the stubs source and API cannot be generated together then add an additional dependency on
+		// the API module.
+		if apiScope.createStubsSourceAndApiTogether {
+			// Add a dependency on the stubs source in order to access both stubs source and api information.
+			ctx.AddVariationDependencies(nil, apiScope.stubsSourceAndApiTag, module.stubsSourceModuleName(apiScope))
+		} else {
+			// Add separate dependencies on the creators of the stubs source files and the API.
+			ctx.AddVariationDependencies(nil, apiScope.stubsSourceTag, module.stubsSourceModuleName(apiScope))
+			ctx.AddVariationDependencies(nil, apiScope.apiFileTag, module.apiModuleName(apiScope))
+		}
 	}
 
-	if !proptools.Bool(module.sdkLibraryProperties.Api_only) {
-		// Add dependency to the rule for generating the xml permissions file
-		ctx.AddDependency(module, xmlPermissionsFileTag, module.xmlFileName())
-	}
+	if module.requiresRuntimeImplementationLibrary() {
+		// Add dependency to the rule for generating the implementation library.
+		ctx.AddDependency(module, implLibraryTag, module.implLibraryModuleName())
 
-	module.Library.deps(ctx)
+		if module.sharedLibrary() {
+			// Add dependency to the rule for generating the xml permissions file
+			ctx.AddDependency(module, xmlPermissionsFileTag, module.xmlFileName())
+		}
+
+		// Only add the deps for the library if it is actually going to be built.
+		module.Library.deps(ctx)
+	}
+}
+
+func (module *SdkLibrary) OutputFiles(tag string) (android.Paths, error) {
+	paths, err := module.commonOutputFiles(tag)
+	if paths == nil && err == nil {
+		return module.Library.OutputFiles(tag)
+	} else {
+		return paths, err
+	}
 }
 
 func (module *SdkLibrary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	// Don't build an implementation library if this is api only.
-	if !proptools.Bool(module.sdkLibraryProperties.Api_only) {
+	// Only build an implementation library if required.
+	if module.requiresRuntimeImplementationLibrary() {
 		module.Library.GenerateAndroidBuildActions(ctx)
 	}
 
@@ -509,7 +979,7 @@
 		// Extract information from any of the scope specific dependencies.
 		if scopeTag, ok := tag.(scopeDependencyTag); ok {
 			apiScope := scopeTag.apiScope
-			scopePaths := module.getScopePaths(apiScope)
+			scopePaths := module.getScopePathsCreateIfNeeded(apiScope)
 
 			// Extract information from the dependency. The exact information extracted
 			// is determined by the nature of the dependency which is determined by the tag.
@@ -519,7 +989,7 @@
 }
 
 func (module *SdkLibrary) AndroidMkEntries() []android.AndroidMkEntries {
-	if proptools.Bool(module.sdkLibraryProperties.Api_only) {
+	if !module.requiresRuntimeImplementationLibrary() {
 		return nil
 	}
 	entriesList := module.Library.AndroidMkEntries()
@@ -528,20 +998,9 @@
 	return entriesList
 }
 
-// Name of the java_library module that compiles the stubs source.
-func (module *SdkLibrary) stubsName(apiScope *apiScope) string {
-	return apiScope.stubsModuleName(module.BaseModuleName())
-}
-
-// // Name of the droidstubs module that generates the stubs source and
-// generates/checks the API.
-func (module *SdkLibrary) stubsSourceName(apiScope *apiScope) string {
-	return apiScope.stubsSourceModuleName(module.BaseModuleName())
-}
-
 // Module name of the runtime implementation library
-func (module *SdkLibrary) implName() string {
-	return module.BaseModuleName()
+func (module *SdkLibrary) implLibraryModuleName() string {
+	return module.BaseModuleName() + ".impl"
 }
 
 // Module name of the XML file for the lib
@@ -561,7 +1020,12 @@
 }
 
 // Get the sdk version for use when compiling the stubs library.
-func (module *SdkLibrary) sdkVersionForStubsLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) string {
+func (module *SdkLibrary) sdkVersionForStubsLibrary(mctx android.EarlyModuleContext, apiScope *apiScope) string {
+	scopeProperties := module.scopeToProperties[apiScope]
+	if scopeProperties.Sdk_version != nil {
+		return proptools.String(scopeProperties.Sdk_version)
+	}
+
 	sdkDep := decodeSdkDep(mctx, sdkContext(&module.Library))
 	if sdkDep.hasStandardLibs() {
 		// If building against a standard sdk then use the sdk version appropriate for the scope.
@@ -580,24 +1044,41 @@
 	return ":" + module.BaseModuleName() + "-removed.api." + apiScope.name + ".latest"
 }
 
+// Creates the implementation java library
+func (module *SdkLibrary) createImplLibrary(mctx android.DefaultableHookContext) {
+	props := struct {
+		Name       *string
+		Visibility []string
+	}{
+		Name:       proptools.StringPtr(module.implLibraryModuleName()),
+		Visibility: module.sdkLibraryProperties.Impl_library_visibility,
+	}
+
+	properties := []interface{}{
+		&module.properties,
+		&module.protoProperties,
+		&module.deviceProperties,
+		&module.dexpreoptProperties,
+		&props,
+		module.sdkComponentPropertiesForChildLibrary(),
+	}
+	mctx.CreateModule(LibraryFactory, properties...)
+}
+
 // Creates a static java library that has API stubs
 func (module *SdkLibrary) createStubsLibrary(mctx android.DefaultableHookContext, apiScope *apiScope) {
 	props := struct {
-		Name                *string
-		Visibility          []string
-		Srcs                []string
-		Installable         *bool
-		Sdk_version         *string
-		System_modules      *string
-		Patch_module        *string
-		Libs                []string
-		Soc_specific        *bool
-		Device_specific     *bool
-		Product_specific    *bool
-		System_ext_specific *bool
-		Compile_dex         *bool
-		Java_version        *string
-		Product_variables   struct {
+		Name              *string
+		Visibility        []string
+		Srcs              []string
+		Installable       *bool
+		Sdk_version       *string
+		System_modules    *string
+		Patch_module      *string
+		Libs              []string
+		Compile_dex       *bool
+		Java_version      *string
+		Product_variables struct {
 			Pdk struct {
 				Enabled *bool
 			}
@@ -614,7 +1095,7 @@
 		}
 	}{}
 
-	props.Name = proptools.StringPtr(module.stubsName(apiScope))
+	props.Name = proptools.StringPtr(module.stubsLibraryModuleName(apiScope))
 
 	// If stubs_library_visibility is not set then the created module will use the
 	// visibility of this module.
@@ -622,30 +1103,21 @@
 	props.Visibility = visibility
 
 	// sources are generated from the droiddoc
-	props.Srcs = []string{":" + module.stubsSourceName(apiScope)}
+	props.Srcs = []string{":" + module.stubsSourceModuleName(apiScope)}
 	sdkVersion := module.sdkVersionForStubsLibrary(mctx, apiScope)
 	props.Sdk_version = proptools.StringPtr(sdkVersion)
-	props.System_modules = module.Library.Module.deviceProperties.System_modules
-	props.Patch_module = module.Library.Module.properties.Patch_module
+	props.System_modules = module.deviceProperties.System_modules
+	props.Patch_module = module.properties.Patch_module
 	props.Installable = proptools.BoolPtr(false)
 	props.Libs = module.sdkLibraryProperties.Stub_only_libs
 	props.Product_variables.Pdk.Enabled = proptools.BoolPtr(false)
-	props.Openjdk9.Srcs = module.Library.Module.properties.Openjdk9.Srcs
-	props.Openjdk9.Javacflags = module.Library.Module.properties.Openjdk9.Javacflags
-	props.Java_version = module.Library.Module.properties.Java_version
-	if module.Library.Module.deviceProperties.Compile_dex != nil {
-		props.Compile_dex = module.Library.Module.deviceProperties.Compile_dex
+	props.Openjdk9.Srcs = module.properties.Openjdk9.Srcs
+	props.Openjdk9.Javacflags = module.properties.Openjdk9.Javacflags
+	props.Java_version = module.properties.Java_version
+	if module.deviceProperties.Compile_dex != nil {
+		props.Compile_dex = module.deviceProperties.Compile_dex
 	}
 
-	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)
-	}
 	// Dist the class jar artifact for sdk builds.
 	if !Bool(module.sdkLibraryProperties.No_dist) {
 		props.Dist.Targets = []string{"sdk", "win_sdk"}
@@ -654,12 +1126,12 @@
 		props.Dist.Tag = proptools.StringPtr(".jar")
 	}
 
-	mctx.CreateModule(LibraryFactory, &props)
+	mctx.CreateModule(LibraryFactory, &props, module.sdkComponentPropertiesForChildLibrary())
 }
 
 // Creates a droidstubs module that creates stubs source files from the given full source
 // files and also updates and checks the API specification files.
-func (module *SdkLibrary) createStubsSourcesAndApi(mctx android.DefaultableHookContext, apiScope *apiScope) {
+func (module *SdkLibrary) createStubsSourcesAndApi(mctx android.DefaultableHookContext, apiScope *apiScope, name string, createStubSources, createApi bool, scopeSpecificDroidstubsArgs []string) {
 	props := struct {
 		Name                             *string
 		Visibility                       []string
@@ -673,10 +1145,17 @@
 		Java_version                     *string
 		Merge_annotations_dirs           []string
 		Merge_inclusion_annotations_dirs []string
+		Generate_stubs                   *bool
 		Check_api                        struct {
 			Current                   ApiToCheck
 			Last_released             ApiToCheck
 			Ignore_missing_latest_api *bool
+
+			Api_lint struct {
+				Enabled       *bool
+				New_since     *string
+				Baseline_file *string
+			}
 		}
 		Aidl struct {
 			Include_dirs       []string
@@ -695,24 +1174,24 @@
 	// * system_modules
 	// * libs (static_libs/libs)
 
-	props.Name = proptools.StringPtr(module.stubsSourceName(apiScope))
+	props.Name = proptools.StringPtr(name)
 
 	// If stubs_source_visibility is not set then the created module will use the
 	// visibility of this module.
 	visibility := module.sdkLibraryProperties.Stubs_source_visibility
 	props.Visibility = visibility
 
-	props.Srcs = append(props.Srcs, module.Library.Module.properties.Srcs...)
-	props.Sdk_version = module.Library.Module.deviceProperties.Sdk_version
-	props.System_modules = module.Library.Module.deviceProperties.System_modules
+	props.Srcs = append(props.Srcs, module.properties.Srcs...)
+	props.Sdk_version = module.deviceProperties.Sdk_version
+	props.System_modules = module.deviceProperties.System_modules
 	props.Installable = proptools.BoolPtr(false)
 	// A droiddoc module has only one Libs property and doesn't distinguish between
 	// shared libs and static libs. So we need to add both of these libs to Libs property.
-	props.Libs = module.Library.Module.properties.Libs
-	props.Libs = append(props.Libs, module.Library.Module.properties.Static_libs...)
-	props.Aidl.Include_dirs = module.Library.Module.deviceProperties.Aidl.Include_dirs
-	props.Aidl.Local_include_dirs = module.Library.Module.deviceProperties.Aidl.Local_include_dirs
-	props.Java_version = module.Library.Module.properties.Java_version
+	props.Libs = module.properties.Libs
+	props.Libs = append(props.Libs, module.properties.Static_libs...)
+	props.Aidl.Include_dirs = module.deviceProperties.Aidl.Include_dirs
+	props.Aidl.Local_include_dirs = module.deviceProperties.Aidl.Local_include_dirs
+	props.Java_version = module.properties.Java_version
 
 	props.Merge_annotations_dirs = module.sdkLibraryProperties.Merge_annotations_dirs
 	props.Merge_inclusion_annotations_dirs = module.sdkLibraryProperties.Merge_inclusion_annotations_dirs
@@ -739,38 +1218,64 @@
 	}
 	droidstubsArgs = append(droidstubsArgs, android.JoinWithPrefix(disabledWarnings, "--hide "))
 
+	if !createStubSources {
+		// Stubs are not required.
+		props.Generate_stubs = proptools.BoolPtr(false)
+	}
+
 	// Add in scope specific arguments.
-	droidstubsArgs = append(droidstubsArgs, apiScope.droidstubsArgs...)
+	droidstubsArgs = append(droidstubsArgs, scopeSpecificDroidstubsArgs...)
 	props.Arg_files = module.sdkLibraryProperties.Droiddoc_option_files
 	props.Args = proptools.StringPtr(strings.Join(droidstubsArgs, " "))
 
-	// List of APIs identified from the provided source files are created. They are later
-	// compared against to the not-yet-released (a.k.a current) list of APIs and to the
-	// last-released (a.k.a numbered) list of API.
-	currentApiFileName := apiScope.apiFilePrefix + "current.txt"
-	removedApiFileName := apiScope.apiFilePrefix + "removed.txt"
-	apiDir := module.getApiDir()
-	currentApiFileName = path.Join(apiDir, currentApiFileName)
-	removedApiFileName = path.Join(apiDir, removedApiFileName)
+	if createApi {
+		// List of APIs identified from the provided source files are created. They are later
+		// compared against to the not-yet-released (a.k.a current) list of APIs and to the
+		// last-released (a.k.a numbered) list of API.
+		currentApiFileName := apiScope.apiFilePrefix + "current.txt"
+		removedApiFileName := apiScope.apiFilePrefix + "removed.txt"
+		apiDir := module.getApiDir()
+		currentApiFileName = path.Join(apiDir, currentApiFileName)
+		removedApiFileName = path.Join(apiDir, removedApiFileName)
 
-	// check against the not-yet-release API
-	props.Check_api.Current.Api_file = proptools.StringPtr(currentApiFileName)
-	props.Check_api.Current.Removed_api_file = proptools.StringPtr(removedApiFileName)
+		// check against the not-yet-release API
+		props.Check_api.Current.Api_file = proptools.StringPtr(currentApiFileName)
+		props.Check_api.Current.Removed_api_file = proptools.StringPtr(removedApiFileName)
 
-	if !apiScope.unstable {
-		// check against the latest released API
-		props.Check_api.Last_released.Api_file = proptools.StringPtr(
-			module.latestApiFilegroupName(apiScope))
-		props.Check_api.Last_released.Removed_api_file = proptools.StringPtr(
-			module.latestRemovedApiFilegroupName(apiScope))
-		props.Check_api.Ignore_missing_latest_api = proptools.BoolPtr(true)
-	}
+		if !apiScope.unstable {
+			// check against the latest released API
+			latestApiFilegroupName := proptools.StringPtr(module.latestApiFilegroupName(apiScope))
+			props.Check_api.Last_released.Api_file = latestApiFilegroupName
+			props.Check_api.Last_released.Removed_api_file = proptools.StringPtr(
+				module.latestRemovedApiFilegroupName(apiScope))
+			props.Check_api.Ignore_missing_latest_api = proptools.BoolPtr(true)
 
-	// Dist the api txt artifact for sdk builds.
-	if !Bool(module.sdkLibraryProperties.No_dist) {
-		props.Dist.Targets = []string{"sdk", "win_sdk"}
-		props.Dist.Dest = proptools.StringPtr(fmt.Sprintf("%v.txt", module.BaseModuleName()))
-		props.Dist.Dir = proptools.StringPtr(path.Join(module.apiDistPath(apiScope), "api"))
+			if proptools.Bool(module.sdkLibraryProperties.Api_lint.Enabled) {
+				// Enable api lint.
+				props.Check_api.Api_lint.Enabled = proptools.BoolPtr(true)
+				props.Check_api.Api_lint.New_since = latestApiFilegroupName
+
+				// If it exists then pass a lint-baseline.txt through to droidstubs.
+				baselinePath := path.Join(apiDir, apiScope.apiFilePrefix+"lint-baseline.txt")
+				baselinePathRelativeToRoot := path.Join(mctx.ModuleDir(), baselinePath)
+				paths, err := mctx.GlobWithDeps(baselinePathRelativeToRoot, nil)
+				if err != nil {
+					mctx.ModuleErrorf("error checking for presence of %s: %s", baselinePathRelativeToRoot, err)
+				}
+				if len(paths) == 1 {
+					props.Check_api.Api_lint.Baseline_file = proptools.StringPtr(baselinePath)
+				} else if len(paths) != 0 {
+					mctx.ModuleErrorf("error checking for presence of %s: expected one path, found: %v", baselinePathRelativeToRoot, paths)
+				}
+			}
+		}
+
+		// Dist the api txt artifact for sdk builds.
+		if !Bool(module.sdkLibraryProperties.No_dist) {
+			props.Dist.Targets = []string{"sdk", "win_sdk"}
+			props.Dist.Dest = proptools.StringPtr(fmt.Sprintf("%v.txt", module.BaseModuleName()))
+			props.Dist.Dir = proptools.StringPtr(path.Join(module.apiDistPath(apiScope), "api"))
+		}
 	}
 
 	mctx.CreateModule(DroidstubsFactory, &props)
@@ -787,29 +1292,15 @@
 // Creates the xml file that publicizes the runtime library
 func (module *SdkLibrary) createXmlFile(mctx android.DefaultableHookContext) {
 	props := struct {
-		Name                *string
-		Lib_name            *string
-		Soc_specific        *bool
-		Device_specific     *bool
-		Product_specific    *bool
-		System_ext_specific *bool
-		Apex_available      []string
+		Name           *string
+		Lib_name       *string
+		Apex_available []string
 	}{
 		Name:           proptools.StringPtr(module.xmlFileName()),
 		Lib_name:       proptools.StringPtr(module.BaseModuleName()),
 		Apex_available: module.ApexProperties.Apex_available,
 	}
 
-	if module.SocSpecific() {
-		props.Soc_specific = proptools.BoolPtr(true)
-	} else if module.DeviceSpecific() {
-		props.Device_specific = proptools.BoolPtr(true)
-	} else if module.ProductSpecific() {
-		props.Product_specific = proptools.BoolPtr(true)
-	} else if module.SystemExtSpecific() {
-		props.System_ext_specific = proptools.BoolPtr(true)
-	}
-
 	mctx.CreateModule(sdkLibraryXmlFactory, &props)
 }
 
@@ -840,41 +1331,50 @@
 	return android.Paths{jarPath.Path()}
 }
 
-func (module *SdkLibrary) sdkJars(
-	ctx android.BaseModuleContext,
-	sdkVersion sdkSpec,
-	headerJars bool) android.Paths {
+// Get the apex name for module, "" if it is for platform.
+func getApexNameForModule(module android.Module) string {
+	if apex, ok := module.(android.ApexModule); ok {
+		return apex.ApexName()
+	}
 
-	// If a specific numeric version has been requested then use prebuilt versions of the sdk.
-	if sdkVersion.version.isNumbered() {
-		return PrebuiltJars(ctx, module.BaseModuleName(), sdkVersion)
-	} else {
-		if !sdkVersion.specified() {
+	return ""
+}
+
+// Check to see if the other module is within the same named APEX as this module.
+//
+// If either this or the other module are on the platform then this will return
+// false.
+func (module *SdkLibrary) withinSameApexAs(other android.Module) bool {
+	name := module.ApexName()
+	return name != "" && getApexNameForModule(other) == name
+}
+
+func (module *SdkLibrary) sdkJars(ctx android.BaseModuleContext, sdkVersion sdkSpec, headerJars bool) android.Paths {
+	// If the client doesn't set sdk_version, but if this library prefers stubs over
+	// the impl library, let's provide the widest API surface possible. To do so,
+	// force override sdk_version to module_current so that the closest possible API
+	// surface could be found in selectHeaderJarsForSdkVersion
+	if module.defaultsToStubs() && !sdkVersion.specified() {
+		sdkVersion = sdkSpecFrom("module_current")
+	}
+
+	// Only provide access to the implementation library if it is actually built.
+	if module.requiresRuntimeImplementationLibrary() {
+		// Check any special cases for java_sdk_library.
+		//
+		// Only allow access to the implementation library in the following condition:
+		// * No sdk_version specified on the referencing module.
+		// * The referencing module is in the same apex as this.
+		if sdkVersion.kind == sdkPrivate || module.withinSameApexAs(ctx.Module()) {
 			if headerJars {
-				return module.Library.HeaderJars()
+				return module.HeaderJars()
 			} else {
-				return module.Library.ImplementationJars()
+				return module.ImplementationJars()
 			}
 		}
-		var apiScope *apiScope
-		switch sdkVersion.kind {
-		case sdkSystem:
-			apiScope = apiScopeSystem
-		case sdkTest:
-			apiScope = apiScopeTest
-		case sdkPrivate:
-			return module.Library.HeaderJars()
-		default:
-			apiScope = apiScopePublic
-		}
-
-		paths := module.getScopePaths(apiScope)
-		if headerJars {
-			return paths.stubsHeaderPath
-		} else {
-			return paths.stubsImplPath
-		}
 	}
+
+	return module.selectHeaderJarsForSdkVersion(ctx, sdkVersion)
 }
 
 // to satisfy SdkLibraryDependency interface
@@ -912,7 +1412,7 @@
 		return
 	}
 
-	if len(module.Library.Module.properties.Srcs) == 0 {
+	if len(module.properties.Srcs) == 0 {
 		mctx.PropertyErrorf("srcs", "java_sdk_library must specify srcs")
 		return
 	}
@@ -958,13 +1458,40 @@
 	}
 
 	for _, scope := range generatedScopes {
+		stubsSourceArgs := scope.droidstubsArgsForGeneratingStubsSource
+		stubsSourceModuleName := module.stubsSourceModuleName(scope)
+
+		// If the args needed to generate the stubs and API are the same then they
+		// can be generated in a single invocation of metalava, otherwise they will
+		// need separate invocations.
+		if scope.createStubsSourceAndApiTogether {
+			// Use the stubs source name for legacy reasons.
+			module.createStubsSourcesAndApi(mctx, scope, stubsSourceModuleName, true, true, stubsSourceArgs)
+		} else {
+			module.createStubsSourcesAndApi(mctx, scope, stubsSourceModuleName, true, false, stubsSourceArgs)
+
+			apiArgs := scope.droidstubsArgsForGeneratingApi
+			apiName := module.apiModuleName(scope)
+			module.createStubsSourcesAndApi(mctx, scope, apiName, false, true, apiArgs)
+		}
+
 		module.createStubsLibrary(mctx, scope)
-		module.createStubsSourcesAndApi(mctx, scope)
 	}
 
-	if !proptools.Bool(module.sdkLibraryProperties.Api_only) {
-		// for runtime
-		module.createXmlFile(mctx)
+	if module.requiresRuntimeImplementationLibrary() {
+		// Create child module to create an implementation library.
+		//
+		// This temporarily creates a second implementation library that can be explicitly
+		// referenced.
+		//
+		// TODO(b/156618935) - update comment once only one implementation library is created.
+		module.createImplLibrary(mctx)
+
+		// Only create an XML permissions file that declares the library as being usable
+		// as a shared library if required.
+		if module.sharedLibrary() {
+			module.createXmlFile(mctx)
+		}
 
 		// record java_sdk_library modules so that they are exported to make
 		javaSdkLibraries := javaSdkLibraries(mctx.Config())
@@ -977,16 +1504,77 @@
 func (module *SdkLibrary) InitSdkLibraryProperties() {
 	module.AddProperties(
 		&module.sdkLibraryProperties,
-		&module.Library.Module.properties,
-		&module.Library.Module.dexpreoptProperties,
-		&module.Library.Module.deviceProperties,
-		&module.Library.Module.protoProperties,
+		&module.properties,
+		&module.dexpreoptProperties,
+		&module.deviceProperties,
+		&module.protoProperties,
 	)
 
-	module.Library.Module.properties.Installable = proptools.BoolPtr(true)
-	module.Library.Module.deviceProperties.IsSDKLibrary = true
+	module.initSdkLibraryComponent(&module.ModuleBase)
+
+	module.properties.Installable = proptools.BoolPtr(true)
+	module.deviceProperties.IsSDKLibrary = true
 }
 
+func (module *SdkLibrary) requiresRuntimeImplementationLibrary() bool {
+	return !proptools.Bool(module.sdkLibraryProperties.Api_only)
+}
+
+func (module *SdkLibrary) defaultsToStubs() bool {
+	return proptools.Bool(module.sdkLibraryProperties.Default_to_stubs)
+}
+
+// Defines how to name the individual component modules the sdk library creates.
+type sdkLibraryComponentNamingScheme interface {
+	stubsLibraryModuleName(scope *apiScope, baseName string) string
+
+	stubsSourceModuleName(scope *apiScope, baseName string) string
+
+	apiModuleName(scope *apiScope, baseName string) string
+}
+
+type defaultNamingScheme struct {
+}
+
+func (s *defaultNamingScheme) stubsLibraryModuleName(scope *apiScope, baseName string) string {
+	return scope.stubsLibraryModuleName(baseName)
+}
+
+func (s *defaultNamingScheme) stubsSourceModuleName(scope *apiScope, baseName string) string {
+	return scope.stubsSourceModuleName(baseName)
+}
+
+func (s *defaultNamingScheme) apiModuleName(scope *apiScope, baseName string) string {
+	return scope.apiModuleName(baseName)
+}
+
+var _ sdkLibraryComponentNamingScheme = (*defaultNamingScheme)(nil)
+
+type frameworkModulesNamingScheme struct {
+}
+
+func (s *frameworkModulesNamingScheme) moduleSuffix(scope *apiScope) string {
+	suffix := scope.name
+	if scope == apiScopeModuleLib {
+		suffix = "module_libs_"
+	}
+	return suffix
+}
+
+func (s *frameworkModulesNamingScheme) stubsLibraryModuleName(scope *apiScope, baseName string) string {
+	return fmt.Sprintf("%s-stubs-%sapi", baseName, s.moduleSuffix(scope))
+}
+
+func (s *frameworkModulesNamingScheme) stubsSourceModuleName(scope *apiScope, baseName string) string {
+	return fmt.Sprintf("%s-stubs-srcs-%sapi", baseName, s.moduleSuffix(scope))
+}
+
+func (s *frameworkModulesNamingScheme) apiModuleName(scope *apiScope, baseName string) string {
+	return fmt.Sprintf("%s-api-%sapi", baseName, s.moduleSuffix(scope))
+}
+
+var _ sdkLibraryComponentNamingScheme = (*frameworkModulesNamingScheme)(nil)
+
 // java_sdk_library is a special Java library that provides optional platform APIs to apps.
 // In practice, it can be viewed as a combination of several modules: 1) stubs library that clients
 // are linked against to, 2) droiddoc module that internally generates API stubs source files,
@@ -994,6 +1582,10 @@
 // the runtime lib to the classpath at runtime if requested via <uses-library>.
 func SdkLibraryFactory() android.Module {
 	module := &SdkLibrary{}
+
+	// Initialize information common between source and prebuilt.
+	module.initCommon(&module.ModuleBase)
+
 	module.InitSdkLibraryProperties()
 	android.InitApexModule(module)
 	InitJavaModule(module, android.HostAndDeviceSupported)
@@ -1006,10 +1598,27 @@
 	module.scopeToProperties = scopeToProperties
 
 	// Add the properties containing visibility rules so that they are checked.
+	android.AddVisibilityProperty(module, "impl_library_visibility", &module.sdkLibraryProperties.Impl_library_visibility)
 	android.AddVisibilityProperty(module, "stubs_library_visibility", &module.sdkLibraryProperties.Stubs_library_visibility)
 	android.AddVisibilityProperty(module, "stubs_source_visibility", &module.sdkLibraryProperties.Stubs_source_visibility)
 
-	module.SetDefaultableHook(func(ctx android.DefaultableHookContext) { module.CreateInternalModules(ctx) })
+	module.SetDefaultableHook(func(ctx android.DefaultableHookContext) {
+		// If no implementation is required then it cannot be used as a shared library
+		// either.
+		if !module.requiresRuntimeImplementationLibrary() {
+			// If shared_library has been explicitly set to true then it is incompatible
+			// with api_only: true.
+			if proptools.Bool(module.commonSdkLibraryProperties.Shared_library) {
+				ctx.PropertyErrorf("api_only/shared_library", "inconsistent settings, shared_library and api_only cannot both be true")
+			}
+			// Set shared_library: false.
+			module.commonSdkLibraryProperties.Shared_library = proptools.BoolPtr(false)
+		}
+
+		if module.initCommonAfterDefaultsApplied(ctx) {
+			module.CreateInternalModules(ctx)
+		}
+	})
 	return module
 }
 
@@ -1030,10 +1639,10 @@
 	Stub_srcs []string `android:"path"`
 
 	// The current.txt
-	Current_api string `android:"path"`
+	Current_api *string `android:"path"`
 
 	// The removed.txt
-	Removed_api string `android:"path"`
+	Removed_api *string `android:"path"`
 }
 
 type sdkLibraryImportProperties struct {
@@ -1105,12 +1714,19 @@
 	module.scopeProperties = scopeToProperties
 	module.AddProperties(&module.properties, allScopeProperties)
 
+	// Initialize information common between source and prebuilt.
+	module.initCommon(&module.ModuleBase)
+
 	android.InitPrebuiltModule(module, &[]string{""})
 	android.InitApexModule(module)
 	android.InitSdkAwareModule(module)
 	InitJavaModule(module, android.HostAndDeviceSupported)
 
-	module.SetDefaultableHook(func(mctx android.DefaultableHookContext) { module.createInternalModules(mctx) })
+	module.SetDefaultableHook(func(mctx android.DefaultableHookContext) {
+		if module.initCommonAfterDefaultsApplied(mctx) {
+			module.createInternalModules(mctx)
+		}
+	})
 	return module
 }
 
@@ -1136,7 +1752,9 @@
 
 		module.createJavaImportForStubs(mctx, apiScope, scopeProperties)
 
-		module.createPrebuiltStubsSources(mctx, apiScope, scopeProperties)
+		if len(scopeProperties.Stub_srcs) > 0 {
+			module.createPrebuiltStubsSources(mctx, apiScope, scopeProperties)
+		}
 	}
 
 	javaSdkLibraries := javaSdkLibraries(mctx.Config())
@@ -1148,47 +1766,37 @@
 func (module *sdkLibraryImport) createJavaImportForStubs(mctx android.DefaultableHookContext, 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
+		Name        *string
+		Sdk_version *string
+		Libs        []string
+		Jars        []string
+		Prefer      *bool
 	}{}
-	props.Name = proptools.StringPtr(apiScope.stubsModuleName(module.BaseModuleName()))
+	props.Name = proptools.StringPtr(module.stubsLibraryModuleName(apiScope))
 	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)
+
+	// The imports are preferred if the java_sdk_library_import is preferred.
+	props.Prefer = proptools.BoolPtr(module.prebuilt.Prefer())
+
+	mctx.CreateModule(ImportFactory, &props, module.sdkComponentPropertiesForChildLibrary())
 }
 
 func (module *sdkLibraryImport) createPrebuiltStubsSources(mctx android.DefaultableHookContext, apiScope *apiScope, scopeProperties *sdkLibraryScopeProperties) {
 	props := struct {
-		Name *string
-		Srcs []string
+		Name   *string
+		Srcs   []string
+		Prefer *bool
 	}{}
-	props.Name = proptools.StringPtr(apiScope.stubsSourceModuleName(module.BaseModuleName()))
+	props.Name = proptools.StringPtr(module.stubsSourceModuleName(apiScope))
 	props.Srcs = scopeProperties.Stub_srcs
 	mctx.CreateModule(PrebuiltStubsSourcesFactory, &props)
+
+	// The stubs source is preferred if the java_sdk_library_import is preferred.
+	props.Prefer = proptools.BoolPtr(module.prebuilt.Prefer())
 }
 
 func (module *sdkLibraryImport) DepsMutator(ctx android.BottomUpMutatorContext) {
@@ -1198,46 +1806,49 @@
 		}
 
 		// Add dependencies to the prebuilt stubs library
-		ctx.AddVariationDependencies(nil, apiScope.stubsTag, apiScope.stubsModuleName(module.BaseModuleName()))
+		ctx.AddVariationDependencies(nil, apiScope.stubsTag, module.stubsLibraryModuleName(apiScope))
+
+		if len(scopeProperties.Stub_srcs) > 0 {
+			// Add dependencies to the prebuilt stubs source library
+			ctx.AddVariationDependencies(nil, apiScope.stubsSourceTag, module.stubsSourceModuleName(apiScope))
+		}
 	}
 }
 
+func (module *sdkLibraryImport) OutputFiles(tag string) (android.Paths, error) {
+	return module.commonOutputFiles(tag)
+}
+
 func (module *sdkLibraryImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	// Record the paths to the prebuilt stubs library.
+	// Record the paths to the prebuilt stubs library and stubs source.
 	ctx.VisitDirectDeps(func(to android.Module) {
 		tag := ctx.OtherModuleDependencyTag(to)
 
-		if lib, ok := to.(Dependency); ok {
-			if scopeTag, ok := tag.(scopeDependencyTag); ok {
-				apiScope := scopeTag.apiScope
-				scopePaths := module.getScopePaths(apiScope)
-				scopePaths.stubsHeaderPath = lib.HeaderJars()
-			}
+		// Extract information from any of the scope specific dependencies.
+		if scopeTag, ok := tag.(scopeDependencyTag); ok {
+			apiScope := scopeTag.apiScope
+			scopePaths := module.getScopePathsCreateIfNeeded(apiScope)
+
+			// Extract information from the dependency. The exact information extracted
+			// is determined by the nature of the dependency which is determined by the tag.
+			scopeTag.extractDepInfo(ctx, to, scopePaths)
 		}
 	})
+
+	// Populate the scope paths with information from the properties.
+	for apiScope, scopeProperties := range module.scopeProperties {
+		if len(scopeProperties.Jars) == 0 {
+			continue
+		}
+
+		paths := module.getScopePathsCreateIfNeeded(apiScope)
+		paths.currentApiFilePath = android.OptionalPathForModuleSrc(ctx, scopeProperties.Current_api)
+		paths.removedApiFilePath = android.OptionalPathForModuleSrc(ctx, scopeProperties.Removed_api)
+	}
 }
 
-func (module *sdkLibraryImport) sdkJars(
-	ctx android.BaseModuleContext,
-	sdkVersion sdkSpec) android.Paths {
-
-	// If a specific numeric version has been requested then use prebuilt versions of the sdk.
-	if sdkVersion.version.isNumbered() {
-		return PrebuiltJars(ctx, module.BaseModuleName(), sdkVersion)
-	}
-
-	var apiScope *apiScope
-	switch sdkVersion.kind {
-	case sdkSystem:
-		apiScope = apiScopeSystem
-	case sdkTest:
-		apiScope = apiScopeTest
-	default:
-		apiScope = apiScopePublic
-	}
-
-	paths := module.getScopePaths(apiScope)
-	return paths.stubsHeaderPath
+func (module *sdkLibraryImport) sdkJars(ctx android.BaseModuleContext, sdkVersion sdkSpec) android.Paths {
+	return module.selectHeaderJarsForSdkVersion(ctx, sdkVersion)
 }
 
 // to satisfy SdkLibraryDependency interface
@@ -1392,6 +2003,13 @@
 
 	// The Java stubs source files.
 	Stub_srcs []string
+
+	// The naming scheme.
+	Naming_scheme *string
+
+	// True if the java_sdk_library_import is for a shared library, false
+	// otherwise.
+	Shared_library *bool
 }
 
 type scopeProperties struct {
@@ -1407,26 +2025,39 @@
 
 	s.Scopes = make(map[*apiScope]scopeProperties)
 	for _, apiScope := range allApiScopes {
-		paths := sdk.getScopePaths(apiScope)
+		paths := sdk.findScopePaths(apiScope)
+		if paths == nil {
+			continue
+		}
+
 		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
+			properties.SdkVersion = sdk.sdkVersionForStubsLibrary(ctx.SdkModuleContext(), apiScope)
+			properties.StubsSrcJar = paths.stubsSrcJar.Path()
+			properties.CurrentApiFile = paths.currentApiFilePath.Path()
+			properties.RemovedApiFile = paths.removedApiFilePath.Path()
 			s.Scopes[apiScope] = properties
 		}
 	}
 
 	s.Libs = sdk.properties.Libs
+	s.Naming_scheme = sdk.commonSdkLibraryProperties.Naming_scheme
+	s.Shared_library = proptools.BoolPtr(sdk.sharedLibrary())
 }
 
 func (s *sdkLibrarySdkMemberProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) {
+	if s.Naming_scheme != nil {
+		propertySet.AddProperty("naming_scheme", proptools.String(s.Naming_scheme))
+	}
+	if s.Shared_library != nil {
+		propertySet.AddProperty("shared_library", *s.Shared_library)
+	}
+
 	for _, apiScope := range allApiScopes {
 		if properties, ok := s.Scopes[apiScope]; ok {
-			scopeSet := propertySet.AddPropertySet(apiScope.name)
+			scopeSet := propertySet.AddPropertySet(apiScope.propertyName)
 
 			scopeDir := filepath.Join("sdk_library", s.OsPrefix(), apiScope.name)
 
diff --git a/java/sdk_test.go b/java/sdk_test.go
index 088db9e..fb86463 100644
--- a/java/sdk_test.go
+++ b/java/sdk_test.go
@@ -83,6 +83,16 @@
 		},
 		{
 
+			name:           "sdk v30",
+			properties:     `sdk_version: "30",`,
+			bootclasspath:  []string{`""`},
+			system:         "sdk_public_30_system_modules",
+			java8classpath: []string{"prebuilts/sdk/30/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			java9classpath: []string{"prebuilts/sdk/30/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			aidl:           "-pprebuilts/sdk/30/public/framework.aidl",
+		},
+		{
+
 			name:           "current",
 			properties:     `sdk_version: "current",`,
 			bootclasspath:  []string{"android_stubs_current", "core-lambda-stubs"},
@@ -110,6 +120,16 @@
 		},
 		{
 
+			name:           "system_30",
+			properties:     `sdk_version: "system_30",`,
+			bootclasspath:  []string{`""`},
+			system:         "sdk_public_30_system_modules",
+			java8classpath: []string{"prebuilts/sdk/30/system/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			java9classpath: []string{"prebuilts/sdk/30/system/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			aidl:           "-pprebuilts/sdk/30/public/framework.aidl",
+		},
+		{
+
 			name:           "test_current",
 			properties:     `sdk_version: "test_current",`,
 			bootclasspath:  []string{"android_test_stubs_current", "core-lambda-stubs"},
@@ -176,12 +196,24 @@
 		},
 		{
 
+			name:           "unbundled sdk v30",
+			unbundled:      true,
+			properties:     `sdk_version: "30",`,
+			bootclasspath:  []string{`""`},
+			system:         "sdk_public_30_system_modules",
+			java8classpath: []string{"prebuilts/sdk/30/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			java9classpath: []string{"prebuilts/sdk/30/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			aidl:           "-pprebuilts/sdk/30/public/framework.aidl",
+		},
+		{
+
 			name:           "unbundled current",
 			unbundled:      true,
 			properties:     `sdk_version: "current",`,
 			bootclasspath:  []string{`""`},
-			forces8:        true,
+			system:         "sdk_public_current_system_modules",
 			java8classpath: []string{"prebuilts/sdk/current/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			java9classpath: []string{"prebuilts/sdk/current/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
 			aidl:           "-pprebuilts/sdk/current/public/framework.aidl",
 		},
 
@@ -189,27 +221,30 @@
 			name:           "pdk default",
 			pdk:            true,
 			bootclasspath:  []string{`""`},
-			forces8:        true,
-			java8classpath: []string{"prebuilts/sdk/29/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
-			aidl:           "-pprebuilts/sdk/29/public/framework.aidl",
+			system:         "sdk_public_30_system_modules",
+			java8classpath: []string{"prebuilts/sdk/30/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			java9classpath: []string{"prebuilts/sdk/30/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			aidl:           "-pprebuilts/sdk/30/public/framework.aidl",
 		},
 		{
 			name:           "pdk current",
 			pdk:            true,
 			properties:     `sdk_version: "current",`,
 			bootclasspath:  []string{`""`},
-			forces8:        true,
-			java8classpath: []string{"prebuilts/sdk/29/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
-			aidl:           "-pprebuilts/sdk/29/public/framework.aidl",
+			system:         "sdk_public_30_system_modules",
+			java8classpath: []string{"prebuilts/sdk/30/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			java9classpath: []string{"prebuilts/sdk/30/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			aidl:           "-pprebuilts/sdk/30/public/framework.aidl",
 		},
 		{
 			name:           "pdk 29",
 			pdk:            true,
 			properties:     `sdk_version: "29",`,
 			bootclasspath:  []string{`""`},
-			forces8:        true,
-			java8classpath: []string{"prebuilts/sdk/29/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
-			aidl:           "-pprebuilts/sdk/29/public/framework.aidl",
+			system:         "sdk_public_30_system_modules",
+			java8classpath: []string{"prebuilts/sdk/30/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			java9classpath: []string{"prebuilts/sdk/30/public/android.jar", "prebuilts/sdk/tools/core-lambda-stubs.jar"},
+			aidl:           "-pprebuilts/sdk/30/public/framework.aidl",
 		},
 		{
 			name:           "module_current",
@@ -292,12 +327,16 @@
 			if testcase.system == "none" {
 				system = "--system=none"
 			} else if testcase.system != "" {
-				system = "--system=" + filepath.Join(buildDir, ".intermediates", testcase.system, "android_common", "system")
+				dir := ""
+				if strings.HasPrefix(testcase.system, "sdk_public_") {
+					dir = "prebuilts/sdk"
+				}
+				system = "--system=" + filepath.Join(buildDir, ".intermediates", dir, testcase.system, "android_common", "system")
 				// The module-relative parts of these paths are hardcoded in system_modules.go:
 				systemDeps = []string{
-					filepath.Join(buildDir, ".intermediates", testcase.system, "android_common", "system", "lib", "modules"),
-					filepath.Join(buildDir, ".intermediates", testcase.system, "android_common", "system", "lib", "jrt-fs.jar"),
-					filepath.Join(buildDir, ".intermediates", testcase.system, "android_common", "system", "release"),
+					filepath.Join(buildDir, ".intermediates", dir, testcase.system, "android_common", "system", "lib", "modules"),
+					filepath.Join(buildDir, ".intermediates", dir, testcase.system, "android_common", "system", "lib", "jrt-fs.jar"),
+					filepath.Join(buildDir, ".intermediates", dir, testcase.system, "android_common", "system", "release"),
 				}
 			}
 
diff --git a/java/testing.go b/java/testing.go
index 6929bb7..faf4d32 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -55,36 +55,54 @@
 		"assets_a/a":             nil,
 		"assets_b/b":             nil,
 
-		"prebuilts/sdk/14/public/android.jar":         nil,
-		"prebuilts/sdk/14/public/framework.aidl":      nil,
-		"prebuilts/sdk/14/system/android.jar":         nil,
-		"prebuilts/sdk/17/public/android.jar":         nil,
-		"prebuilts/sdk/17/public/framework.aidl":      nil,
-		"prebuilts/sdk/17/system/android.jar":         nil,
-		"prebuilts/sdk/29/public/android.jar":         nil,
-		"prebuilts/sdk/29/public/framework.aidl":      nil,
-		"prebuilts/sdk/29/system/android.jar":         nil,
-		"prebuilts/sdk/29/system/foo.jar":             nil,
-		"prebuilts/sdk/current/core/android.jar":      nil,
-		"prebuilts/sdk/current/public/android.jar":    nil,
-		"prebuilts/sdk/current/public/framework.aidl": nil,
-		"prebuilts/sdk/current/public/core.jar":       nil,
-		"prebuilts/sdk/current/system/android.jar":    nil,
-		"prebuilts/sdk/current/test/android.jar":      nil,
-		"prebuilts/sdk/28/public/api/foo.txt":         nil,
-		"prebuilts/sdk/28/system/api/foo.txt":         nil,
-		"prebuilts/sdk/28/test/api/foo.txt":           nil,
-		"prebuilts/sdk/28/public/api/foo-removed.txt": nil,
-		"prebuilts/sdk/28/system/api/foo-removed.txt": nil,
-		"prebuilts/sdk/28/test/api/foo-removed.txt":   nil,
-		"prebuilts/sdk/28/public/api/bar.txt":         nil,
-		"prebuilts/sdk/28/system/api/bar.txt":         nil,
-		"prebuilts/sdk/28/test/api/bar.txt":           nil,
-		"prebuilts/sdk/28/public/api/bar-removed.txt": nil,
-		"prebuilts/sdk/28/system/api/bar-removed.txt": nil,
-		"prebuilts/sdk/28/test/api/bar-removed.txt":   nil,
-		"prebuilts/sdk/tools/core-lambda-stubs.jar":   nil,
-		"prebuilts/sdk/Android.bp":                    []byte(`prebuilt_apis { name: "sdk", api_dirs: ["14", "28", "current"],}`),
+		"prebuilts/sdk/14/public/android.jar":                      nil,
+		"prebuilts/sdk/14/public/framework.aidl":                   nil,
+		"prebuilts/sdk/14/system/android.jar":                      nil,
+		"prebuilts/sdk/17/public/android.jar":                      nil,
+		"prebuilts/sdk/17/public/framework.aidl":                   nil,
+		"prebuilts/sdk/17/system/android.jar":                      nil,
+		"prebuilts/sdk/29/public/android.jar":                      nil,
+		"prebuilts/sdk/29/public/framework.aidl":                   nil,
+		"prebuilts/sdk/29/system/android.jar":                      nil,
+		"prebuilts/sdk/29/system/foo.jar":                          nil,
+		"prebuilts/sdk/30/public/android.jar":                      nil,
+		"prebuilts/sdk/30/public/framework.aidl":                   nil,
+		"prebuilts/sdk/30/system/android.jar":                      nil,
+		"prebuilts/sdk/30/system/foo.jar":                          nil,
+		"prebuilts/sdk/30/public/core-for-system-modules.jar":      nil,
+		"prebuilts/sdk/current/core/android.jar":                   nil,
+		"prebuilts/sdk/current/public/android.jar":                 nil,
+		"prebuilts/sdk/current/public/framework.aidl":              nil,
+		"prebuilts/sdk/current/public/core.jar":                    nil,
+		"prebuilts/sdk/current/public/core-for-system-modules.jar": nil,
+		"prebuilts/sdk/current/system/android.jar":                 nil,
+		"prebuilts/sdk/current/test/android.jar":                   nil,
+		"prebuilts/sdk/28/public/api/foo.txt":                      nil,
+		"prebuilts/sdk/28/system/api/foo.txt":                      nil,
+		"prebuilts/sdk/28/test/api/foo.txt":                        nil,
+		"prebuilts/sdk/28/public/api/foo-removed.txt":              nil,
+		"prebuilts/sdk/28/system/api/foo-removed.txt":              nil,
+		"prebuilts/sdk/28/test/api/foo-removed.txt":                nil,
+		"prebuilts/sdk/28/public/api/bar.txt":                      nil,
+		"prebuilts/sdk/28/system/api/bar.txt":                      nil,
+		"prebuilts/sdk/28/test/api/bar.txt":                        nil,
+		"prebuilts/sdk/28/public/api/bar-removed.txt":              nil,
+		"prebuilts/sdk/28/system/api/bar-removed.txt":              nil,
+		"prebuilts/sdk/28/test/api/bar-removed.txt":                nil,
+		"prebuilts/sdk/30/public/api/foo.txt":                      nil,
+		"prebuilts/sdk/30/system/api/foo.txt":                      nil,
+		"prebuilts/sdk/30/test/api/foo.txt":                        nil,
+		"prebuilts/sdk/30/public/api/foo-removed.txt":              nil,
+		"prebuilts/sdk/30/system/api/foo-removed.txt":              nil,
+		"prebuilts/sdk/30/test/api/foo-removed.txt":                nil,
+		"prebuilts/sdk/30/public/api/bar.txt":                      nil,
+		"prebuilts/sdk/30/system/api/bar.txt":                      nil,
+		"prebuilts/sdk/30/test/api/bar.txt":                        nil,
+		"prebuilts/sdk/30/public/api/bar-removed.txt":              nil,
+		"prebuilts/sdk/30/system/api/bar-removed.txt":              nil,
+		"prebuilts/sdk/30/test/api/bar-removed.txt":                nil,
+		"prebuilts/sdk/tools/core-lambda-stubs.jar":                nil,
+		"prebuilts/sdk/Android.bp":                                 []byte(`prebuilt_apis { name: "sdk", api_dirs: ["14", "28", "30", "current"],}`),
 
 		"prebuilts/apk/app.apk":        nil,
 		"prebuilts/apk/app_arm.apk":    nil,
@@ -92,6 +110,8 @@
 		"prebuilts/apk/app_xhdpi.apk":  nil,
 		"prebuilts/apk/app_xxhdpi.apk": nil,
 
+		"prebuilts/apks/app.apks": nil,
+
 		// For framework-res, which is an implicit dependency for framework
 		"AndroidManifest.xml":                        nil,
 		"build/make/target/product/security/testkey": nil,
@@ -116,6 +136,7 @@
 
 		"cert/new_cert.x509.pem": nil,
 		"cert/new_cert.pk8":      nil,
+		"lineage.bin":            nil,
 
 		"testdata/data": nil,
 
@@ -209,6 +230,22 @@
 			system_modules: "core-platform-api-stubs-system-modules",
 			installable: true,
 		}
+
+		java_library {
+			name: "android.test.base",
+			srcs: ["a.java"],
+			sdk_version: "none",
+			system_modules: "core-platform-api-stubs-system-modules",
+			installable: true,
+		}
+  
+		java_library {
+			name: "android.test.mock",
+			srcs: ["a.java"],
+			sdk_version: "none",
+			system_modules: "core-platform-api-stubs-system-modules",
+			installable: true,
+		}
 	`
 
 	systemModules := []string{
diff --git a/remoteexec/remoteexec.go b/remoteexec/remoteexec.go
index 99e29dc..d6e2c0a 100644
--- a/remoteexec/remoteexec.go
+++ b/remoteexec/remoteexec.go
@@ -75,6 +75,9 @@
 	// OutputFiles is a list of output file paths or ninja variables as placeholders for rule
 	// outputs.
 	OutputFiles []string
+	// OutputDirectories is a list of output directories or ninja variables as placeholders for
+	// rule output directories.
+	OutputDirectories []string
 	// ToolchainInputs is a list of paths or ninja variables pointing to the location of
 	// toolchain binaries used by the rule.
 	ToolchainInputs []string
@@ -99,7 +102,7 @@
 	return "${remoteexec.Wrapper}" + r.wrapperArgs()
 }
 
-// NoVarTemplate generate the remote execution wrapper template without variables, to be used in
+// NoVarTemplate generates the remote execution wrapper template without variables, to be used in
 // RuleBuilder.
 func (r *REParams) NoVarTemplate(cfg android.Config) string {
 	return wrapper(cfg) + r.wrapperArgs()
@@ -151,6 +154,10 @@
 		args += " --output_files=" + strings.Join(r.OutputFiles, ",")
 	}
 
+	if len(r.OutputDirectories) > 0 {
+		args += " --output_directories=" + strings.Join(r.OutputDirectories, ",")
+	}
+
 	if len(r.ToolchainInputs) > 0 {
 		args += " --toolchain_inputs=" + strings.Join(r.ToolchainInputs, ",")
 	}
@@ -159,7 +166,9 @@
 }
 
 // StaticRules returns a pair of rules based on the given RuleParams, where the first rule is a
-// locally executable rule and the second rule is a remotely executable rule.
+// locally executable rule and the second rule is a remotely executable rule. commonArgs are args
+// used for both the local and remotely executable rules. reArgs are used only for remote
+// execution.
 func StaticRules(ctx android.PackageContext, name string, ruleParams blueprint.RuleParams, reParams *REParams, commonArgs []string, reArgs []string) (blueprint.Rule, blueprint.Rule) {
 	ruleParamsRE := ruleParams
 	ruleParams.Command = strings.ReplaceAll(ruleParams.Command, "$reTemplate", "")
@@ -169,6 +178,22 @@
 		ctx.AndroidRemoteStaticRule(name+"RE", android.RemoteRuleSupports{RBE: true}, ruleParamsRE, append(commonArgs, reArgs...)...)
 }
 
+// MultiCommandStaticRules returns a pair of rules based on the given RuleParams, where the first
+// rule is a locally executable rule and the second rule is a remotely executable rule. This
+// function supports multiple remote execution wrappers placed in the template when commands are
+// chained together with &&. commonArgs are args used for both the local and remotely executable
+// rules. reArgs are args used only for remote execution.
+func MultiCommandStaticRules(ctx android.PackageContext, name string, ruleParams blueprint.RuleParams, reParams map[string]*REParams, commonArgs []string, reArgs []string) (blueprint.Rule, blueprint.Rule) {
+	ruleParamsRE := ruleParams
+	for k, v := range reParams {
+		ruleParams.Command = strings.ReplaceAll(ruleParams.Command, k, "")
+		ruleParamsRE.Command = strings.ReplaceAll(ruleParamsRE.Command, k, v.Template())
+	}
+
+	return ctx.AndroidStaticRule(name, ruleParams, commonArgs...),
+		ctx.AndroidRemoteStaticRule(name+"RE", android.RemoteRuleSupports{RBE: true}, ruleParamsRE, append(commonArgs, reArgs...)...)
+}
+
 // EnvOverrideFunc retrieves a variable func that evaluates to the value of the given environment
 // variable if set, otherwise the given default.
 func EnvOverrideFunc(envVar, defaultVal string) func(ctx android.PackageVarContext) string {
diff --git a/rust/config/x86_darwin_host.go b/rust/config/x86_darwin_host.go
index 7cfc59c..4c16693 100644
--- a/rust/config/x86_darwin_host.go
+++ b/rust/config/x86_darwin_host.go
@@ -62,7 +62,11 @@
 	return "x86_64-apple-darwin"
 }
 
-func (t *toolchainDarwin) ShlibSuffix() string {
+func (t *toolchainDarwin) SharedLibSuffix() string {
+	return ".dylib"
+}
+
+func (t *toolchainDarwin) ProcMacroSuffix() string {
 	return ".dylib"
 }
 
diff --git a/scripts/build-mainline-modules.sh b/scripts/build-mainline-modules.sh
index 770adc9..f836ea9 100755
--- a/scripts/build-mainline-modules.sh
+++ b/scripts/build-mainline-modules.sh
@@ -19,6 +19,7 @@
   conscrypt-module-sdk
   conscrypt-module-test-exports
   conscrypt-module-host-exports
+  runtime-module-sdk
 )
 
 # We want to create apex modules for all supported architectures.
diff --git a/scripts/package-check.sh b/scripts/package-check.sh
index f982e82..d7e602f 100755
--- a/scripts/package-check.sh
+++ b/scripts/package-check.sh
@@ -52,6 +52,7 @@
 # Check all class file names against the expected prefixes.
 old_ifs=${IFS}
 IFS=$'\n'
+failed=false
 for zip_entry in ${zip_contents}; do
   # Check the suffix.
   if [[ "${zip_entry}" = *.class ]]; then
@@ -65,8 +66,11 @@
     done
     if [[ "${found}" == "false" ]]; then
       echo "Class file ${zip_entry} is outside specified packages."
-      exit 1
+      failed=true
     fi
   fi
 done
+if [[ "${failed}" == "true" ]]; then
+  exit 1
+fi
 IFS=${old_ifs}
diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go
index dded153..4a09081 100644
--- a/sdk/cc_sdk_test.go
+++ b/sdk/cc_sdk_test.go
@@ -401,7 +401,6 @@
 				"Test.cpp",
 			],
 			compile_multilib: "both",
-			stl: "none",
 		}
 	`)
 
@@ -494,6 +493,7 @@
     device_supported: false,
     host_supported: true,
     installable: false,
+    stl: "none",
     target: {
         linux_glibc: {
             compile_multilib: "both",
@@ -518,6 +518,7 @@
     prefer: false,
     device_supported: false,
     host_supported: true,
+    stl: "none",
     target: {
         linux_glibc: {
             compile_multilib: "both",
@@ -557,6 +558,90 @@
 	)
 }
 
+// Test that we support the necessary flags for the linker binary, which is
+// special in several ways.
+func TestSnapshotWithCcStaticNocrtBinary(t *testing.T) {
+	// b/145598135 - Generating host snapshots for anything other than linux is not supported.
+	SkipIfNotLinux(t)
+
+	result := testSdkWithCc(t, `
+		module_exports {
+			name: "mymodule_exports",
+			host_supported: true,
+			device_supported: false,
+			native_binaries: ["linker"],
+		}
+
+		cc_binary {
+			name: "linker",
+			host_supported: true,
+			static_executable: true,
+			nocrt: true,
+			stl: "none",
+			srcs: [
+				"Test.cpp",
+			],
+			compile_multilib: "both",
+		}
+	`)
+
+	result.CheckSnapshot("mymodule_exports", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+cc_prebuilt_binary {
+    name: "mymodule_exports_linker@current",
+    sdk_member_name: "linker",
+    device_supported: false,
+    host_supported: true,
+    installable: false,
+    stl: "none",
+    static_executable: true,
+    nocrt: true,
+    compile_multilib: "both",
+    arch: {
+        x86_64: {
+            srcs: ["x86_64/bin/linker"],
+        },
+        x86: {
+            srcs: ["x86/bin/linker"],
+        },
+    },
+}
+
+cc_prebuilt_binary {
+    name: "linker",
+    prefer: false,
+    device_supported: false,
+    host_supported: true,
+    stl: "none",
+    static_executable: true,
+    nocrt: true,
+    compile_multilib: "both",
+    arch: {
+        x86_64: {
+            srcs: ["x86_64/bin/linker"],
+        },
+        x86: {
+            srcs: ["x86/bin/linker"],
+        },
+    },
+}
+
+module_exports_snapshot {
+    name: "mymodule_exports@current",
+    device_supported: false,
+    host_supported: true,
+    native_binaries: ["mymodule_exports_linker@current"],
+}
+`),
+		checkAllCopyRules(`
+.intermediates/linker/linux_glibc_x86_64/linker -> x86_64/bin/linker
+.intermediates/linker/linux_glibc_x86/linker -> x86/bin/linker
+`),
+	)
+}
+
 func TestSnapshotWithCcSharedLibrary(t *testing.T) {
 	result := testSdkWithCc(t, `
 		sdk {
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 560a6b8..f8e9fc1 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -989,6 +989,7 @@
 			apex_available: ["//apex_available:anyapex"],
 			srcs: ["Test.java"],
 			sdk_version: "current",
+			shared_library: false,
 			stubs_library_visibility: ["//other"],
 			stubs_source_visibility: ["//another"],
 		}
@@ -1002,6 +1003,7 @@
     name: "mysdk_myjavalib@current",
     sdk_member_name: "myjavalib",
     apex_available: ["//apex_available:anyapex"],
+    shared_library: false,
     public: {
         jars: ["sdk_library/public/myjavalib-stubs.jar"],
         stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
@@ -1029,6 +1031,7 @@
     name: "myjavalib",
     prefer: false,
     apex_available: ["//apex_available:anyapex"],
+    shared_library: false,
     public: {
         jars: ["sdk_library/public/myjavalib-stubs.jar"],
         stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
@@ -1075,7 +1078,132 @@
 	)
 }
 
-func TestSnapshotWithJavaSdkLibrary_ApiSurfaces(t *testing.T) {
+func TestSnapshotWithJavaSdkLibrary_SdkVersion_None(t *testing.T) {
+	result := testSdkWithJava(t, `
+		sdk {
+			name: "mysdk",
+			java_sdk_libs: ["myjavalib"],
+		}
+
+		java_sdk_library {
+			name: "myjavalib",
+			srcs: ["Test.java"],
+			sdk_version: "none",
+			system_modules: "none",
+		}
+	`)
+
+	result.CheckSnapshot("mysdk", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_sdk_library_import {
+    name: "mysdk_myjavalib@current",
+    sdk_member_name: "myjavalib",
+    shared_library: true,
+    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: "none",
+    },
+}
+
+java_sdk_library_import {
+    name: "myjavalib",
+    prefer: false,
+    shared_library: true,
+    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: "none",
+    },
+}
+
+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
+`),
+		checkMergeZips(
+			".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
+		),
+	)
+}
+
+func TestSnapshotWithJavaSdkLibrary_SdkVersion_ForScope(t *testing.T) {
+	result := testSdkWithJava(t, `
+		sdk {
+			name: "mysdk",
+			java_sdk_libs: ["myjavalib"],
+		}
+
+		java_sdk_library {
+			name: "myjavalib",
+			srcs: ["Test.java"],
+			sdk_version: "module_current",
+			public: {
+				enabled: true,
+				sdk_version: "module_current",
+			},
+		}
+	`)
+
+	result.CheckSnapshot("mysdk", "",
+		checkAndroidBpContents(`
+// This is auto-generated. DO NOT EDIT.
+
+java_sdk_library_import {
+    name: "mysdk_myjavalib@current",
+    sdk_member_name: "myjavalib",
+    shared_library: true,
+    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: "module_current",
+    },
+}
+
+java_sdk_library_import {
+    name: "myjavalib",
+    prefer: false,
+    shared_library: true,
+    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: "module_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
+`),
+		checkMergeZips(
+			".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
+		),
+	)
+}
+
+func TestSnapshotWithJavaSdkLibrary_ApiScopes(t *testing.T) {
 	result := testSdkWithJava(t, `
 		sdk {
 			name: "mysdk",
@@ -1104,6 +1232,7 @@
     name: "mysdk_myjavalib@current",
     sdk_member_name: "myjavalib",
     apex_available: ["//apex_available:anyapex"],
+    shared_library: true,
     public: {
         jars: ["sdk_library/public/myjavalib-stubs.jar"],
         stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
@@ -1124,6 +1253,7 @@
     name: "myjavalib",
     prefer: false,
     apex_available: ["//apex_available:anyapex"],
+    shared_library: true,
     public: {
         jars: ["sdk_library/public/myjavalib-stubs.jar"],
         stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
@@ -1192,6 +1322,7 @@
     name: "mysdk_myjavalib@current",
     sdk_member_name: "myjavalib",
     apex_available: ["//apex_available:anyapex"],
+    shared_library: true,
     public: {
         jars: ["sdk_library/public/myjavalib-stubs.jar"],
         stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
@@ -1207,10 +1338,10 @@
         sdk_version: "system_current",
     },
     module_lib: {
-        jars: ["sdk_library/module_lib/myjavalib-stubs.jar"],
-        stub_srcs: ["sdk_library/module_lib/myjavalib_stub_sources"],
-        current_api: "sdk_library/module_lib/myjavalib.txt",
-        removed_api: "sdk_library/module_lib/myjavalib-removed.txt",
+        jars: ["sdk_library/module-lib/myjavalib-stubs.jar"],
+        stub_srcs: ["sdk_library/module-lib/myjavalib_stub_sources"],
+        current_api: "sdk_library/module-lib/myjavalib.txt",
+        removed_api: "sdk_library/module-lib/myjavalib-removed.txt",
         sdk_version: "module_current",
     },
 }
@@ -1219,6 +1350,7 @@
     name: "myjavalib",
     prefer: false,
     apex_available: ["//apex_available:anyapex"],
+    shared_library: true,
     public: {
         jars: ["sdk_library/public/myjavalib-stubs.jar"],
         stub_srcs: ["sdk_library/public/myjavalib_stub_sources"],
@@ -1234,10 +1366,10 @@
         sdk_version: "system_current",
     },
     module_lib: {
-        jars: ["sdk_library/module_lib/myjavalib-stubs.jar"],
-        stub_srcs: ["sdk_library/module_lib/myjavalib_stub_sources"],
-        current_api: "sdk_library/module_lib/myjavalib.txt",
-        removed_api: "sdk_library/module_lib/myjavalib-removed.txt",
+        jars: ["sdk_library/module-lib/myjavalib-stubs.jar"],
+        stub_srcs: ["sdk_library/module-lib/myjavalib_stub_sources"],
+        current_api: "sdk_library/module-lib/myjavalib.txt",
+        removed_api: "sdk_library/module-lib/myjavalib-removed.txt",
         sdk_version: "module_current",
     },
 }
@@ -1254,14 +1386,83 @@
 .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.module_lib/android_common/javac/myjavalib.stubs.module_lib.jar -> sdk_library/module_lib/myjavalib-stubs.jar
-.intermediates/myjavalib.stubs.source.module_lib/android_common/myjavalib.stubs.source.module_lib_api.txt -> sdk_library/module_lib/myjavalib.txt
-.intermediates/myjavalib.stubs.source.module_lib/android_common/myjavalib.stubs.source.module_lib_api.txt -> sdk_library/module_lib/myjavalib-removed.txt
+.intermediates/myjavalib.stubs.module_lib/android_common/javac/myjavalib.stubs.module_lib.jar -> sdk_library/module-lib/myjavalib-stubs.jar
+.intermediates/myjavalib.api.module_lib/android_common/myjavalib.api.module_lib_api.txt -> sdk_library/module-lib/myjavalib.txt
+.intermediates/myjavalib.api.module_lib/android_common/myjavalib.api.module_lib_api.txt -> sdk_library/module-lib/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/module_lib/myjavalib_stub_sources.zip",
+			".intermediates/mysdk/common_os/tmp/sdk_library/module-lib/myjavalib_stub_sources.zip",
+		),
+	)
+}
+
+func TestSnapshotWithJavaSdkLibrary_NamingScheme(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",
+			naming_scheme: "framework-modules",
+			public: {
+				enabled: true,
+			},
+		}
+	`)
+
+	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"],
+    naming_scheme: "framework-modules",
+    shared_library: true,
+    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",
+    },
+}
+
+java_sdk_library_import {
+    name: "myjavalib",
+    prefer: false,
+    apex_available: ["//apex_available:anyapex"],
+    naming_scheme: "framework-modules",
+    shared_library: true,
+    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",
+    },
+}
+
+sdk_snapshot {
+    name: "mysdk@current",
+    java_sdk_libs: ["mysdk_myjavalib@current"],
+}
+`),
+		checkAllCopyRules(`
+.intermediates/myjavalib-stubs-publicapi/android_common/javac/myjavalib-stubs-publicapi.jar -> sdk_library/public/myjavalib-stubs.jar
+.intermediates/myjavalib-stubs-srcs-publicapi/android_common/myjavalib-stubs-srcs-publicapi_api.txt -> sdk_library/public/myjavalib.txt
+.intermediates/myjavalib-stubs-srcs-publicapi/android_common/myjavalib-stubs-srcs-publicapi_api.txt -> sdk_library/public/myjavalib-removed.txt
+`),
+		checkMergeZips(
+			".intermediates/mysdk/common_os/tmp/sdk_library/public/myjavalib_stub_sources.zip",
 		),
 	)
 }
diff --git a/sdk/sdk_test.go b/sdk/sdk_test.go
index ae1a492..56be741 100644
--- a/sdk/sdk_test.go
+++ b/sdk/sdk_test.go
@@ -103,6 +103,7 @@
 				"myjavalib",
 				"mypublicjavalib",
 				"mydefaultedjavalib",
+				"myprivatejavalib",
 			],
 		}
 
@@ -140,6 +141,14 @@
 			system_modules: "none",
 			sdk_version: "none",
 		}
+
+		java_library {
+			name: "myprivatejavalib",
+			srcs: ["Test.java"],
+			visibility: ["//visibility:private"],
+			system_modules: "none",
+			sdk_version: "none",
+		}
 	`
 
 	result := testSdkWithFs(t, ``,
@@ -155,14 +164,20 @@
 java_import {
     name: "mysdk_myjavalib@current",
     sdk_member_name: "myjavalib",
-    visibility: ["//other/foo:__pkg__"],
+    visibility: [
+        "//other/foo",
+        "//package",
+    ],
     jars: ["java/myjavalib.jar"],
 }
 
 java_import {
     name: "myjavalib",
     prefer: false,
-    visibility: ["//other/foo:__pkg__"],
+    visibility: [
+        "//other/foo",
+        "//package",
+    ],
     jars: ["java/myjavalib.jar"],
 }
 
@@ -183,27 +198,48 @@
 java_import {
     name: "mysdk_mydefaultedjavalib@current",
     sdk_member_name: "mydefaultedjavalib",
-    visibility: ["//other/bar:__pkg__"],
+    visibility: [
+        "//other/bar",
+        "//package",
+    ],
     jars: ["java/mydefaultedjavalib.jar"],
 }
 
 java_import {
     name: "mydefaultedjavalib",
     prefer: false,
-    visibility: ["//other/bar:__pkg__"],
+    visibility: [
+        "//other/bar",
+        "//package",
+    ],
     jars: ["java/mydefaultedjavalib.jar"],
 }
 
+java_import {
+    name: "mysdk_myprivatejavalib@current",
+    sdk_member_name: "myprivatejavalib",
+    visibility: ["//package"],
+    jars: ["java/myprivatejavalib.jar"],
+}
+
+java_import {
+    name: "myprivatejavalib",
+    prefer: false,
+    visibility: ["//package"],
+    jars: ["java/myprivatejavalib.jar"],
+}
+
 sdk_snapshot {
     name: "mysdk@current",
     visibility: [
-        "//other/foo:__pkg__",
+        "//other/foo",
         "//package:__subpackages__",
     ],
     java_header_libs: [
         "mysdk_myjavalib@current",
         "mysdk_mypublicjavalib@current",
         "mysdk_mydefaultedjavalib@current",
+        "mysdk_myprivatejavalib@current",
     ],
 }
 `))
diff --git a/sdk/update.go b/sdk/update.go
index 476a4a5..1ba5806 100644
--- a/sdk/update.go
+++ b/sdk/update.go
@@ -350,6 +350,9 @@
 	bp = newGeneratedFile(ctx, "snapshot", "Android.bp")
 	generateBpContents(&bp.generatedContents, bpFile)
 
+	contents := bp.content.String()
+	syntaxCheckSnapshotBpFile(ctx, contents)
+
 	bp.build(pctx, ctx, nil)
 
 	filesToZip := builder.filesToZip
@@ -394,6 +397,36 @@
 	return outputZipFile
 }
 
+// Check the syntax of the generated Android.bp file contents and if they are
+// invalid then log an error with the contents (tagged with line numbers) and the
+// errors that were found so that it is easy to see where the problem lies.
+func syntaxCheckSnapshotBpFile(ctx android.ModuleContext, contents string) {
+	errs := android.CheckBlueprintSyntax(ctx, "Android.bp", contents)
+	if len(errs) != 0 {
+		message := &strings.Builder{}
+		_, _ = fmt.Fprint(message, `errors in generated Android.bp snapshot:
+
+Generated Android.bp contents
+========================================================================
+`)
+		for i, line := range strings.Split(contents, "\n") {
+			_, _ = fmt.Fprintf(message, "%6d:    %s\n", i+1, line)
+		}
+
+		_, _ = fmt.Fprint(message, `
+========================================================================
+
+Errors found:
+`)
+
+		for _, err := range errs {
+			_, _ = fmt.Fprintf(message, "%s\n", err.Error())
+		}
+
+		ctx.ModuleErrorf("%s", message.String())
+	}
+}
+
 func extractCommonProperties(ctx android.ModuleContext, extractor *commonValueExtractor, commonProperties interface{}, inputPropertiesSlice interface{}) {
 	err := extractor.extractCommonProperties(commonProperties, inputPropertiesSlice)
 	if err != nil {
diff --git a/tradefed/autogen.go b/tradefed/autogen.go
index 596284b..be44cac 100644
--- a/tradefed/autogen.go
+++ b/tradefed/autogen.go
@@ -219,31 +219,39 @@
 }
 
 var autogenInstrumentationTest = pctx.StaticRule("autogenInstrumentationTest", blueprint.RuleParams{
-	Command: "${AutoGenTestConfigScript} $out $in ${EmptyTestConfig} $template",
+	Command: "${AutoGenTestConfigScript} $out $in ${EmptyTestConfig} $template ${extraConfigs}",
 	CommandDeps: []string{
 		"${AutoGenTestConfigScript}",
 		"${EmptyTestConfig}",
 		"$template",
 	},
-}, "name", "template")
+}, "name", "template", "extraConfigs")
 
 func AutoGenInstrumentationTestConfig(ctx android.ModuleContext, testConfigProp *string,
-	testConfigTemplateProp *string, manifest android.Path, testSuites []string, autoGenConfig *bool) android.Path {
+	testConfigTemplateProp *string, manifest android.Path, testSuites []string, autoGenConfig *bool, configs []Config) android.Path {
 	path, autogenPath := testConfigPath(ctx, testConfigProp, testSuites, autoGenConfig, testConfigTemplateProp)
+	var configStrings []string
 	if autogenPath != nil {
 		template := "${InstrumentationTestConfigTemplate}"
 		moduleTemplate := getTestConfigTemplate(ctx, testConfigTemplateProp)
 		if moduleTemplate.Valid() {
 			template = moduleTemplate.String()
 		}
+		for _, config := range configs {
+			configStrings = append(configStrings, config.Config())
+		}
+		extraConfigs := strings.Join(configStrings, fmt.Sprintf("\\n%s", test_xml_indent))
+		extraConfigs = fmt.Sprintf("--extra-configs '%s'", extraConfigs)
+
 		ctx.Build(pctx, android.BuildParams{
 			Rule:        autogenInstrumentationTest,
 			Description: "test config",
 			Input:       manifest,
 			Output:      autogenPath,
 			Args: map[string]string{
-				"name":     ctx.ModuleName(),
-				"template": template,
+				"name":         ctx.ModuleName(),
+				"template":     template,
+				"extraConfigs": extraConfigs,
 			},
 		})
 		return autogenPath
diff --git a/ui/build/build.go b/ui/build/build.go
index 1122733..89c3fad 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -141,7 +141,26 @@
 func Build(ctx Context, config Config, what int) {
 	ctx.Verboseln("Starting build with args:", config.Arguments())
 	ctx.Verboseln("Environment:", config.Environment().Environ())
-	ctx.Verbosef("Total RAM: %dGB", config.TotalRAM()/1024/1024/1024)
+
+	if totalRAM := config.TotalRAM(); totalRAM != 0 {
+		ram := float32(totalRAM) / (1024 * 1024 * 1024)
+		ctx.Verbosef("Total RAM: %.3vGB", ram)
+
+		if ram <= 16 {
+			ctx.Println("************************************************************")
+			ctx.Printf("You are building on a machine with %.3vGB of RAM\n", ram)
+			ctx.Println("")
+			ctx.Println("The minimum required amount of free memory is around 16GB,")
+			ctx.Println("and even with that, some configurations may not work.")
+			ctx.Println("")
+			ctx.Println("If you run into segfaults or other errors, try reducing your")
+			ctx.Println("-j value.")
+			ctx.Println("************************************************************")
+		} else if ram <= float32(config.Parallel()) {
+			ctx.Printf("Warning: high -j%d count compared to %.3vGB of RAM", config.Parallel(), ram)
+			ctx.Println("If you run into segfaults or other errors, try a lower -j value")
+		}
+	}
 
 	ctx.BeginTrace(metrics.Total, "total")
 	defer ctx.EndTrace()
diff --git a/ui/build/config.go b/ui/build/config.go
index 7fcc471..d66a86c 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -737,6 +737,9 @@
 	} else if c.totalRAM == 0 {
 		// Couldn't detect the total RAM, don't restrict highmem processes.
 		return parallel
+	} else if c.totalRAM <= 16*1024*1024*1024 {
+		// Less than 16GB of ram, restrict to 1 highmem processes
+		return 1
 	} else if c.totalRAM <= 32*1024*1024*1024 {
 		// Less than 32GB of ram, restrict to 2 highmem processes
 		return 2
diff --git a/ui/build/ninja.go b/ui/build/ninja.go
index 1b13e5d..fa44cb1 100644
--- a/ui/build/ninja.go
+++ b/ui/build/ninja.go
@@ -39,6 +39,7 @@
 	args := []string{
 		"-d", "keepdepfile",
 		"-d", "keeprsp",
+		"-d", "stats",
 		"--frontend_file", fifo,
 	}
 
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 2fbf381..6a12add 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -119,6 +119,7 @@
 		cmd := Command(ctx, config, "soong "+name,
 			config.PrebuiltBuildTool("ninja"),
 			"-d", "keepdepfile",
+			"-d", "stats",
 			"-o", "usesphonyoutputs=yes",
 			"-o", "preremoveoutputs=yes",
 			"-w", "dupbuild=err",
diff --git a/ui/status/ninja.go b/ui/status/ninja.go
index 9cf2f6a..a11774c 100644
--- a/ui/status/ninja.go
+++ b/ui/status/ninja.go
@@ -174,6 +174,8 @@
 				n.status.Print("warning: " + message)
 			case ninja_frontend.Status_Message_ERROR:
 				n.status.Error(message)
+			case ninja_frontend.Status_Message_DEBUG:
+				n.status.Verbose(message)
 			default:
 				n.status.Print(message)
 			}
diff --git a/ui/status/ninja_frontend/frontend.pb.go b/ui/status/ninja_frontend/frontend.pb.go
index 7c05eed..7ba9de2 100644
--- a/ui/status/ninja_frontend/frontend.pb.go
+++ b/ui/status/ninja_frontend/frontend.pb.go
@@ -3,9 +3,11 @@
 
 package ninja_frontend
 
-import proto "github.com/golang/protobuf/proto"
-import fmt "fmt"
-import math "math"
+import (
+	fmt "fmt"
+	proto "github.com/golang/protobuf/proto"
+	math "math"
+)
 
 // Reference imports to suppress errors if they are not otherwise used.
 var _ = proto.Marshal
@@ -16,7 +18,7 @@
 // is compatible with the proto package it is being compiled against.
 // A compilation error at this line likely means your copy of the
 // proto package needs to be updated.
-const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
 
 type Status_Message_Level int32
 
@@ -24,17 +26,21 @@
 	Status_Message_INFO    Status_Message_Level = 0
 	Status_Message_WARNING Status_Message_Level = 1
 	Status_Message_ERROR   Status_Message_Level = 2
+	Status_Message_DEBUG   Status_Message_Level = 3
 )
 
 var Status_Message_Level_name = map[int32]string{
 	0: "INFO",
 	1: "WARNING",
 	2: "ERROR",
+	3: "DEBUG",
 }
+
 var Status_Message_Level_value = map[string]int32{
 	"INFO":    0,
 	"WARNING": 1,
 	"ERROR":   2,
+	"DEBUG":   3,
 }
 
 func (x Status_Message_Level) Enum() *Status_Message_Level {
@@ -42,9 +48,11 @@
 	*p = x
 	return p
 }
+
 func (x Status_Message_Level) String() string {
 	return proto.EnumName(Status_Message_Level_name, int32(x))
 }
+
 func (x *Status_Message_Level) UnmarshalJSON(data []byte) error {
 	value, err := proto.UnmarshalJSONEnum(Status_Message_Level_value, data, "Status_Message_Level")
 	if err != nil {
@@ -53,8 +61,9 @@
 	*x = Status_Message_Level(value)
 	return nil
 }
+
 func (Status_Message_Level) EnumDescriptor() ([]byte, []int) {
-	return fileDescriptor_frontend_5a49d9b15a642005, []int{0, 5, 0}
+	return fileDescriptor_eca3873955a29cfe, []int{0, 5, 0}
 }
 
 type Status struct {
@@ -73,16 +82,17 @@
 func (m *Status) String() string { return proto.CompactTextString(m) }
 func (*Status) ProtoMessage()    {}
 func (*Status) Descriptor() ([]byte, []int) {
-	return fileDescriptor_frontend_5a49d9b15a642005, []int{0}
+	return fileDescriptor_eca3873955a29cfe, []int{0}
 }
+
 func (m *Status) XXX_Unmarshal(b []byte) error {
 	return xxx_messageInfo_Status.Unmarshal(m, b)
 }
 func (m *Status) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
 	return xxx_messageInfo_Status.Marshal(b, m, deterministic)
 }
-func (dst *Status) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_Status.Merge(dst, src)
+func (m *Status) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Status.Merge(m, src)
 }
 func (m *Status) XXX_Size() int {
 	return xxx_messageInfo_Status.Size(m)
@@ -147,16 +157,17 @@
 func (m *Status_TotalEdges) String() string { return proto.CompactTextString(m) }
 func (*Status_TotalEdges) ProtoMessage()    {}
 func (*Status_TotalEdges) Descriptor() ([]byte, []int) {
-	return fileDescriptor_frontend_5a49d9b15a642005, []int{0, 0}
+	return fileDescriptor_eca3873955a29cfe, []int{0, 0}
 }
+
 func (m *Status_TotalEdges) XXX_Unmarshal(b []byte) error {
 	return xxx_messageInfo_Status_TotalEdges.Unmarshal(m, b)
 }
 func (m *Status_TotalEdges) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
 	return xxx_messageInfo_Status_TotalEdges.Marshal(b, m, deterministic)
 }
-func (dst *Status_TotalEdges) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_Status_TotalEdges.Merge(dst, src)
+func (m *Status_TotalEdges) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Status_TotalEdges.Merge(m, src)
 }
 func (m *Status_TotalEdges) XXX_Size() int {
 	return xxx_messageInfo_Status_TotalEdges.Size(m)
@@ -188,16 +199,17 @@
 func (m *Status_BuildStarted) String() string { return proto.CompactTextString(m) }
 func (*Status_BuildStarted) ProtoMessage()    {}
 func (*Status_BuildStarted) Descriptor() ([]byte, []int) {
-	return fileDescriptor_frontend_5a49d9b15a642005, []int{0, 1}
+	return fileDescriptor_eca3873955a29cfe, []int{0, 1}
 }
+
 func (m *Status_BuildStarted) XXX_Unmarshal(b []byte) error {
 	return xxx_messageInfo_Status_BuildStarted.Unmarshal(m, b)
 }
 func (m *Status_BuildStarted) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
 	return xxx_messageInfo_Status_BuildStarted.Marshal(b, m, deterministic)
 }
-func (dst *Status_BuildStarted) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_Status_BuildStarted.Merge(dst, src)
+func (m *Status_BuildStarted) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Status_BuildStarted.Merge(m, src)
 }
 func (m *Status_BuildStarted) XXX_Size() int {
 	return xxx_messageInfo_Status_BuildStarted.Size(m)
@@ -232,16 +244,17 @@
 func (m *Status_BuildFinished) String() string { return proto.CompactTextString(m) }
 func (*Status_BuildFinished) ProtoMessage()    {}
 func (*Status_BuildFinished) Descriptor() ([]byte, []int) {
-	return fileDescriptor_frontend_5a49d9b15a642005, []int{0, 2}
+	return fileDescriptor_eca3873955a29cfe, []int{0, 2}
 }
+
 func (m *Status_BuildFinished) XXX_Unmarshal(b []byte) error {
 	return xxx_messageInfo_Status_BuildFinished.Unmarshal(m, b)
 }
 func (m *Status_BuildFinished) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
 	return xxx_messageInfo_Status_BuildFinished.Marshal(b, m, deterministic)
 }
-func (dst *Status_BuildFinished) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_Status_BuildFinished.Merge(dst, src)
+func (m *Status_BuildFinished) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Status_BuildFinished.Merge(m, src)
 }
 func (m *Status_BuildFinished) XXX_Size() int {
 	return xxx_messageInfo_Status_BuildFinished.Size(m)
@@ -276,16 +289,17 @@
 func (m *Status_EdgeStarted) String() string { return proto.CompactTextString(m) }
 func (*Status_EdgeStarted) ProtoMessage()    {}
 func (*Status_EdgeStarted) Descriptor() ([]byte, []int) {
-	return fileDescriptor_frontend_5a49d9b15a642005, []int{0, 3}
+	return fileDescriptor_eca3873955a29cfe, []int{0, 3}
 }
+
 func (m *Status_EdgeStarted) XXX_Unmarshal(b []byte) error {
 	return xxx_messageInfo_Status_EdgeStarted.Unmarshal(m, b)
 }
 func (m *Status_EdgeStarted) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
 	return xxx_messageInfo_Status_EdgeStarted.Marshal(b, m, deterministic)
 }
-func (dst *Status_EdgeStarted) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_Status_EdgeStarted.Merge(dst, src)
+func (m *Status_EdgeStarted) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Status_EdgeStarted.Merge(m, src)
 }
 func (m *Status_EdgeStarted) XXX_Size() int {
 	return xxx_messageInfo_Status_EdgeStarted.Size(m)
@@ -353,7 +367,11 @@
 	// Exit status (0 for success).
 	Status *int32 `protobuf:"zigzag32,3,opt,name=status" json:"status,omitempty"`
 	// Edge output, may contain ANSI codes.
-	Output               *string  `protobuf:"bytes,4,opt,name=output" json:"output,omitempty"`
+	Output *string `protobuf:"bytes,4,opt,name=output" json:"output,omitempty"`
+	// Number of milliseconds spent executing in user mode
+	UserTime *uint32 `protobuf:"varint,5,opt,name=user_time,json=userTime" json:"user_time,omitempty"`
+	// Number of milliseconds spent executing in kernel mode
+	SystemTime           *uint32  `protobuf:"varint,6,opt,name=system_time,json=systemTime" json:"system_time,omitempty"`
 	XXX_NoUnkeyedLiteral struct{} `json:"-"`
 	XXX_unrecognized     []byte   `json:"-"`
 	XXX_sizecache        int32    `json:"-"`
@@ -363,16 +381,17 @@
 func (m *Status_EdgeFinished) String() string { return proto.CompactTextString(m) }
 func (*Status_EdgeFinished) ProtoMessage()    {}
 func (*Status_EdgeFinished) Descriptor() ([]byte, []int) {
-	return fileDescriptor_frontend_5a49d9b15a642005, []int{0, 4}
+	return fileDescriptor_eca3873955a29cfe, []int{0, 4}
 }
+
 func (m *Status_EdgeFinished) XXX_Unmarshal(b []byte) error {
 	return xxx_messageInfo_Status_EdgeFinished.Unmarshal(m, b)
 }
 func (m *Status_EdgeFinished) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
 	return xxx_messageInfo_Status_EdgeFinished.Marshal(b, m, deterministic)
 }
-func (dst *Status_EdgeFinished) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_Status_EdgeFinished.Merge(dst, src)
+func (m *Status_EdgeFinished) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Status_EdgeFinished.Merge(m, src)
 }
 func (m *Status_EdgeFinished) XXX_Size() int {
 	return xxx_messageInfo_Status_EdgeFinished.Size(m)
@@ -411,8 +430,22 @@
 	return ""
 }
 
+func (m *Status_EdgeFinished) GetUserTime() uint32 {
+	if m != nil && m.UserTime != nil {
+		return *m.UserTime
+	}
+	return 0
+}
+
+func (m *Status_EdgeFinished) GetSystemTime() uint32 {
+	if m != nil && m.SystemTime != nil {
+		return *m.SystemTime
+	}
+	return 0
+}
+
 type Status_Message struct {
-	// Message priority level (INFO, WARNING, or ERROR).
+	// Message priority level (DEBUG, INFO, WARNING, ERROR).
 	Level *Status_Message_Level `protobuf:"varint,1,opt,name=level,enum=ninja.Status_Message_Level,def=0" json:"level,omitempty"`
 	// Info/warning/error message from Ninja.
 	Message              *string  `protobuf:"bytes,2,opt,name=message" json:"message,omitempty"`
@@ -425,16 +458,17 @@
 func (m *Status_Message) String() string { return proto.CompactTextString(m) }
 func (*Status_Message) ProtoMessage()    {}
 func (*Status_Message) Descriptor() ([]byte, []int) {
-	return fileDescriptor_frontend_5a49d9b15a642005, []int{0, 5}
+	return fileDescriptor_eca3873955a29cfe, []int{0, 5}
 }
+
 func (m *Status_Message) XXX_Unmarshal(b []byte) error {
 	return xxx_messageInfo_Status_Message.Unmarshal(m, b)
 }
 func (m *Status_Message) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
 	return xxx_messageInfo_Status_Message.Marshal(b, m, deterministic)
 }
-func (dst *Status_Message) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_Status_Message.Merge(dst, src)
+func (m *Status_Message) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Status_Message.Merge(m, src)
 }
 func (m *Status_Message) XXX_Size() int {
 	return xxx_messageInfo_Status_Message.Size(m)
@@ -462,6 +496,7 @@
 }
 
 func init() {
+	proto.RegisterEnum("ninja.Status_Message_Level", Status_Message_Level_name, Status_Message_Level_value)
 	proto.RegisterType((*Status)(nil), "ninja.Status")
 	proto.RegisterType((*Status_TotalEdges)(nil), "ninja.Status.TotalEdges")
 	proto.RegisterType((*Status_BuildStarted)(nil), "ninja.Status.BuildStarted")
@@ -469,42 +504,46 @@
 	proto.RegisterType((*Status_EdgeStarted)(nil), "ninja.Status.EdgeStarted")
 	proto.RegisterType((*Status_EdgeFinished)(nil), "ninja.Status.EdgeFinished")
 	proto.RegisterType((*Status_Message)(nil), "ninja.Status.Message")
-	proto.RegisterEnum("ninja.Status_Message_Level", Status_Message_Level_name, Status_Message_Level_value)
 }
 
-func init() { proto.RegisterFile("frontend.proto", fileDescriptor_frontend_5a49d9b15a642005) }
+func init() {
+	proto.RegisterFile("frontend.proto", fileDescriptor_eca3873955a29cfe)
+}
 
-var fileDescriptor_frontend_5a49d9b15a642005 = []byte{
-	// 496 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x53, 0xd1, 0x6e, 0xd3, 0x30,
-	0x14, 0xa5, 0x69, 0xd3, 0x34, 0x37, 0x6d, 0x28, 0x96, 0x40, 0x59, 0x10, 0xa2, 0xda, 0xd3, 0x78,
-	0x20, 0x48, 0xbc, 0x20, 0x10, 0x12, 0xa2, 0xd2, 0x06, 0x43, 0xd0, 0x49, 0xde, 0x24, 0x24, 0x5e,
-	0xaa, 0x74, 0xf6, 0x86, 0x51, 0xe2, 0x54, 0xb1, 0xbb, 0x5f, 0xe0, 0x7f, 0x78, 0xe0, 0xfb, 0x90,
-	0xaf, 0xed, 0x2c, 0x65, 0x7b, 0xcb, 0xf1, 0x3d, 0xe7, 0xde, 0x73, 0x8f, 0x1d, 0x48, 0xaf, 0xda,
-	0x46, 0x6a, 0x2e, 0x59, 0xb1, 0x6d, 0x1b, 0xdd, 0x90, 0x50, 0x0a, 0xf9, 0xab, 0x3c, 0xfc, 0x13,
-	0xc1, 0xf8, 0x5c, 0x97, 0x7a, 0xa7, 0xc8, 0x5b, 0x48, 0x74, 0xa3, 0xcb, 0x6a, 0xcd, 0xd9, 0x35,
-	0x57, 0xd9, 0x60, 0x31, 0x38, 0x4a, 0x5e, 0x67, 0x05, 0xf2, 0x0a, 0xcb, 0x29, 0x2e, 0x0c, 0xe1,
-	0xd8, 0xd4, 0x29, 0xe8, 0xee, 0x9b, 0x7c, 0x80, 0xd9, 0x66, 0x27, 0x2a, 0xb6, 0x56, 0xba, 0x6c,
-	0x35, 0x67, 0x59, 0x80, 0xe2, 0x7c, 0x5f, 0xbc, 0x34, 0x94, 0x73, 0xcb, 0xa0, 0xd3, 0x4d, 0x0f,
-	0x91, 0x25, 0xa4, 0xb6, 0xc1, 0x95, 0x90, 0x42, 0xfd, 0xe4, 0x2c, 0x1b, 0x62, 0x87, 0xa7, 0xf7,
-	0x74, 0x38, 0x71, 0x14, 0x6a, 0x67, 0x7a, 0x48, 0xde, 0xc3, 0xd4, 0x38, 0xef, 0x3c, 0x8c, 0xb0,
-	0xc3, 0xc1, 0x7e, 0x07, 0xe3, 0xd7, 0x5b, 0x48, 0xf8, 0x2d, 0x30, 0x2b, 0xa0, 0xba, 0x33, 0x10,
-	0xde, 0xb7, 0x82, 0x91, 0x77, 0xf3, 0x71, 0x5c, 0x37, 0xfe, 0x15, 0x44, 0x35, 0x57, 0xaa, 0xbc,
-	0xe6, 0xd9, 0x18, 0xa5, 0x8f, 0xf7, 0xa5, 0xdf, 0x6c, 0x91, 0x7a, 0x56, 0xfe, 0x12, 0xe0, 0x36,
-	0x4e, 0xf2, 0xfc, 0x6e, 0xfa, 0xb3, 0x7e, 0xc6, 0xf9, 0x17, 0x98, 0xf6, 0x03, 0x24, 0x0b, 0x48,
-	0xb6, 0x65, 0x5b, 0x56, 0x15, 0xaf, 0x84, 0xaa, 0x9d, 0xa0, 0x7f, 0x44, 0x32, 0x88, 0x6e, 0x78,
-	0xbb, 0x69, 0x14, 0xc7, 0xfb, 0x98, 0x50, 0x0f, 0xf3, 0x87, 0x30, 0xdb, 0x8b, 0x32, 0xff, 0x3b,
-	0x80, 0xa4, 0x17, 0x0d, 0x49, 0x21, 0x10, 0xcc, 0xf5, 0x0c, 0x04, 0x23, 0xcf, 0x00, 0x30, 0xd6,
-	0xb5, 0x16, 0xb5, 0xed, 0x36, 0xa3, 0x31, 0x9e, 0x5c, 0x88, 0x9a, 0x93, 0x27, 0x30, 0x16, 0x72,
-	0xbb, 0xd3, 0x2a, 0x1b, 0x2e, 0x86, 0x47, 0x31, 0x75, 0xc8, 0x38, 0x68, 0x76, 0x1a, 0x0b, 0x23,
-	0x2c, 0x78, 0x48, 0x08, 0x8c, 0x18, 0x57, 0x97, 0x98, 0x72, 0x4c, 0xf1, 0xdb, 0xb0, 0x2f, 0x9b,
-	0xba, 0x2e, 0x25, 0xc3, 0x04, 0x63, 0xea, 0xa1, 0xad, 0x48, 0xd5, 0x54, 0x3c, 0x8b, 0xec, 0x26,
-	0x0e, 0xe6, 0x02, 0xa6, 0xfd, 0x3b, 0xb9, 0x63, 0xfc, 0x00, 0x26, 0x5c, 0xb2, 0xbe, 0xed, 0x88,
-	0x4b, 0xe6, 0x4d, 0x2b, 0xbc, 0x1a, 0x7c, 0x6b, 0x8f, 0xa8, 0x43, 0xe6, 0xdc, 0xba, 0xc4, 0x17,
-	0x14, 0x53, 0x87, 0xf2, 0xdf, 0x03, 0x88, 0xdc, 0x25, 0x92, 0x37, 0x10, 0x56, 0xfc, 0x86, 0x57,
-	0x38, 0x29, 0xfd, 0xff, 0x99, 0x3a, 0x56, 0xf1, 0xd5, 0x50, 0xde, 0x8d, 0x4e, 0x57, 0x27, 0x67,
-	0xd4, 0xf2, 0xcd, 0x26, 0xfe, 0x95, 0x04, 0x76, 0x47, 0x07, 0x0f, 0x5f, 0x40, 0x88, 0x7c, 0x32,
-	0x01, 0x54, 0xcc, 0x1f, 0x90, 0x04, 0xa2, 0xef, 0x1f, 0xe9, 0xea, 0x74, 0xf5, 0x69, 0x3e, 0x20,
-	0x31, 0x84, 0xc7, 0x94, 0x9e, 0xd1, 0x79, 0xb0, 0x24, 0x9f, 0x87, 0x3f, 0x52, 0x9c, 0xb8, 0xf6,
-	0x7f, 0xf5, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0x2e, 0x8c, 0xef, 0xcb, 0xe0, 0x03, 0x00, 0x00,
+var fileDescriptor_eca3873955a29cfe = []byte{
+	// 539 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x54, 0xd1, 0x6e, 0xd3, 0x4a,
+	0x10, 0xbd, 0x4e, 0xe2, 0x38, 0x1e, 0x27, 0xb9, 0x61, 0x25, 0x90, 0xeb, 0x0a, 0x35, 0xea, 0x53,
+	0x5f, 0x08, 0x12, 0x42, 0x42, 0x20, 0x24, 0x44, 0x44, 0x5a, 0x8a, 0x20, 0x95, 0xb6, 0x45, 0x48,
+	0xbc, 0x44, 0x4e, 0x77, 0x5a, 0x8c, 0xec, 0x75, 0xe4, 0xdd, 0x54, 0xe2, 0x37, 0xf8, 0x09, 0xfe,
+	0x80, 0xaf, 0xe3, 0x01, 0xed, 0xec, 0xda, 0x75, 0x68, 0xdf, 0x7c, 0x76, 0xce, 0x9c, 0x39, 0x7b,
+	0x76, 0x64, 0x18, 0x5f, 0x55, 0xa5, 0xd4, 0x28, 0xc5, 0x6c, 0x53, 0x95, 0xba, 0x64, 0xbe, 0xcc,
+	0xe4, 0xf7, 0xf4, 0xf0, 0x4f, 0x00, 0xfd, 0x73, 0x9d, 0xea, 0xad, 0x62, 0x2f, 0x21, 0xd2, 0xa5,
+	0x4e, 0xf3, 0x15, 0x8a, 0x6b, 0x54, 0xb1, 0x37, 0xf5, 0x8e, 0xa2, 0x67, 0xf1, 0x8c, 0x78, 0x33,
+	0xcb, 0x99, 0x5d, 0x18, 0xc2, 0xc2, 0xd4, 0x39, 0xe8, 0xe6, 0x9b, 0xbd, 0x81, 0xd1, 0x7a, 0x9b,
+	0xe5, 0x62, 0xa5, 0x74, 0x5a, 0x69, 0x14, 0x71, 0x87, 0x9a, 0x93, 0xdd, 0xe6, 0xb9, 0xa1, 0x9c,
+	0x5b, 0x06, 0x1f, 0xae, 0x5b, 0x88, 0xcd, 0x61, 0x6c, 0x05, 0xae, 0x32, 0x99, 0xa9, 0x6f, 0x28,
+	0xe2, 0x2e, 0x29, 0xec, 0xdf, 0xa3, 0x70, 0xec, 0x28, 0xdc, 0xce, 0xac, 0x21, 0x7b, 0x0d, 0x43,
+	0xe3, 0xbc, 0xf1, 0xd0, 0x23, 0x85, 0xbd, 0x5d, 0x05, 0xe3, 0xb7, 0xb6, 0x10, 0xe1, 0x2d, 0x30,
+	0x57, 0xa0, 0xee, 0xc6, 0x80, 0x7f, 0xdf, 0x15, 0x4c, 0x7b, 0x33, 0x9f, 0xc6, 0x35, 0xe3, 0x9f,
+	0x42, 0x50, 0xa0, 0x52, 0xe9, 0x35, 0xc6, 0x7d, 0x6a, 0x7d, 0xb8, 0xdb, 0xfa, 0xc9, 0x16, 0x79,
+	0xcd, 0x4a, 0x9e, 0x00, 0xdc, 0xc6, 0xc9, 0x0e, 0xee, 0xa6, 0x3f, 0x6a, 0x67, 0x9c, 0x7c, 0x80,
+	0x61, 0x3b, 0x40, 0x36, 0x85, 0x68, 0x93, 0x56, 0x69, 0x9e, 0x63, 0x9e, 0xa9, 0xc2, 0x35, 0xb4,
+	0x8f, 0x58, 0x0c, 0xc1, 0x0d, 0x56, 0xeb, 0x52, 0x21, 0xbd, 0xc7, 0x80, 0xd7, 0x30, 0xf9, 0x1f,
+	0x46, 0x3b, 0x51, 0x26, 0xbf, 0x3d, 0x88, 0x5a, 0xd1, 0xb0, 0x31, 0x74, 0x32, 0xe1, 0x34, 0x3b,
+	0x99, 0x60, 0x8f, 0x01, 0x28, 0xd6, 0x95, 0xce, 0x0a, 0xab, 0x36, 0xe2, 0x21, 0x9d, 0x5c, 0x64,
+	0x05, 0xb2, 0x47, 0xd0, 0xcf, 0xe4, 0x66, 0xab, 0x55, 0xdc, 0x9d, 0x76, 0x8f, 0x42, 0xee, 0x90,
+	0x71, 0x50, 0x6e, 0x35, 0x15, 0x7a, 0x54, 0xa8, 0x21, 0x63, 0xd0, 0x13, 0xa8, 0x2e, 0x29, 0xe5,
+	0x90, 0xd3, 0xb7, 0x61, 0x5f, 0x96, 0x45, 0x91, 0x4a, 0x41, 0x09, 0x86, 0xbc, 0x86, 0xb6, 0x22,
+	0x55, 0x99, 0x63, 0x1c, 0xd8, 0x9b, 0x38, 0x98, 0xfc, 0xf2, 0x60, 0xd8, 0x7e, 0x94, 0x3b, 0xce,
+	0xf7, 0x60, 0x80, 0x52, 0xb4, 0x7d, 0x07, 0x28, 0x45, 0xed, 0x5a, 0xd1, 0xdb, 0xd0, 0xb2, 0x3d,
+	0xe0, 0x0e, 0x99, 0x73, 0x6b, 0x93, 0x56, 0x28, 0xe4, 0x0e, 0xb1, 0x7d, 0x08, 0xb7, 0x0a, 0x2b,
+	0xab, 0xe5, 0x93, 0xd6, 0xc0, 0x1c, 0x90, 0xd8, 0x01, 0x44, 0xea, 0x87, 0xd2, 0x58, 0xd8, 0x72,
+	0xdf, 0xbe, 0x9f, 0x3d, 0x32, 0x84, 0xe4, 0xa7, 0x07, 0x81, 0xdb, 0x01, 0xf6, 0x02, 0xfc, 0x1c,
+	0x6f, 0x30, 0x27, 0x9f, 0xe3, 0x7f, 0xb7, 0xdc, 0xb1, 0x66, 0x1f, 0x0d, 0xe5, 0x55, 0xef, 0x74,
+	0x79, 0x7c, 0xc6, 0x2d, 0xdf, 0x04, 0x51, 0x2f, 0x59, 0xc7, 0x46, 0xe4, 0xe0, 0xe1, 0x73, 0xf0,
+	0x89, 0xcf, 0x06, 0x40, 0x1d, 0x93, 0xff, 0x58, 0x04, 0xc1, 0x97, 0xb7, 0x7c, 0x79, 0xba, 0x3c,
+	0x99, 0x78, 0x2c, 0x04, 0x7f, 0xc1, 0xf9, 0x19, 0x9f, 0x74, 0xcc, 0xe7, 0xbb, 0xc5, 0xfc, 0xf3,
+	0xc9, 0xa4, 0x3b, 0x67, 0xef, 0xbb, 0x5f, 0xc7, 0x34, 0x7c, 0x55, 0xff, 0x1f, 0xfe, 0x06, 0x00,
+	0x00, 0xff, 0xff, 0xaf, 0x93, 0x48, 0xcf, 0x2a, 0x04, 0x00, 0x00,
 }
diff --git a/ui/status/ninja_frontend/frontend.proto b/ui/status/ninja_frontend/frontend.proto
index 13fd535..baa0046 100644
--- a/ui/status/ninja_frontend/frontend.proto
+++ b/ui/status/ninja_frontend/frontend.proto
@@ -61,6 +61,10 @@
     optional sint32 status = 3;
     // Edge output, may contain ANSI codes.
     optional string output = 4;
+    // Number of milliseconds spent executing in user mode
+    optional uint32 user_time = 5;
+    // Number of milliseconds spent executing in kernel mode
+    optional uint32 system_time = 6;
   }
 
   message Message {
@@ -68,8 +72,9 @@
       INFO = 0;
       WARNING = 1;
       ERROR = 2;
+      DEBUG = 3;
     }
-    // Message priority level (INFO, WARNING, or ERROR).
+    // Message priority level (DEBUG, INFO, WARNING, ERROR).
     optional Level level = 1 [default = INFO];
     // Info/warning/error message from Ninja.
     optional string message = 2;