Merge "rust modules in APEX don't get installed to the system partition"
diff --git a/android/androidmk.go b/android/androidmk.go
index b32048a..062dcb3 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -820,7 +820,7 @@
 	}
 
 	return !module.Enabled() ||
-		module.commonProperties.SkipInstall ||
+		module.commonProperties.HideFromMake ||
 		// Make does not understand LinuxBionic
 		module.Os() == LinuxBionic
 }
diff --git a/android/makevars.go b/android/makevars.go
index 5101436..546abcf 100644
--- a/android/makevars.go
+++ b/android/makevars.go
@@ -90,6 +90,7 @@
 	ModuleDir(module blueprint.Module) string
 	ModuleSubDir(module blueprint.Module) string
 	ModuleType(module blueprint.Module) string
+	ModuleProvider(module blueprint.Module, key blueprint.ProviderKey) interface{}
 	BlueprintFile(module blueprint.Module) string
 
 	ModuleErrorf(module blueprint.Module, format string, args ...interface{})
diff --git a/android/module.go b/android/module.go
index 429f311..cfb32c1 100644
--- a/android/module.go
+++ b/android/module.go
@@ -452,8 +452,8 @@
 	InstallInRoot() bool
 	InstallBypassMake() bool
 	InstallForceOS() (*OsType, *ArchType)
-	SkipInstall()
-	IsSkipInstall() bool
+	HideFromMake()
+	IsHideFromMake() bool
 	MakeUninstallable()
 	ReplacedByPrebuilt()
 	IsReplacedByPrebuilt() bool
@@ -751,6 +751,13 @@
 	// Set by osMutator.
 	CommonOSVariant bool `blueprint:"mutated"`
 
+	// When HideFromMake is set to true, no entry for this variant will be emitted in the
+	// generated Android.mk file.
+	HideFromMake bool `blueprint:"mutated"`
+
+	// When SkipInstall is set to true, calls to ctx.InstallFile, ctx.InstallExecutable,
+	// ctx.InstallSymlink and ctx.InstallAbsoluteSymlink act like calls to ctx.PackageFile
+	// and don't create a rule to install the file.
 	SkipInstall bool `blueprint:"mutated"`
 
 	// Whether the module has been replaced by a prebuilt
@@ -1355,26 +1362,33 @@
 	m.commonProperties.ForcedDisabled = true
 }
 
+// HideFromMake marks this variant so that it is not emitted in the generated Android.mk file.
+func (m *ModuleBase) HideFromMake() {
+	m.commonProperties.HideFromMake = true
+}
+
+// IsHideFromMake returns true if HideFromMake was previously called.
+func (m *ModuleBase) IsHideFromMake() bool {
+	return m.commonProperties.HideFromMake == true
+}
+
+// SkipInstall marks this variant to not create install rules when ctx.Install* are called.
 func (m *ModuleBase) SkipInstall() {
 	m.commonProperties.SkipInstall = true
 }
 
-func (m *ModuleBase) IsSkipInstall() bool {
-	return m.commonProperties.SkipInstall == true
-}
-
-// Similar to SkipInstall, but if the AndroidMk entry would set
+// Similar to HideFromMake, but if the AndroidMk entry would set
 // LOCAL_UNINSTALLABLE_MODULE then this variant may still output that entry
 // rather than leaving it out altogether. That happens in cases where it would
 // have other side effects, in particular when it adds a NOTICE file target,
 // which other install targets might depend on.
 func (m *ModuleBase) MakeUninstallable() {
-	m.SkipInstall()
+	m.HideFromMake()
 }
 
 func (m *ModuleBase) ReplacedByPrebuilt() {
 	m.commonProperties.ReplacedByPrebuilt = true
-	m.SkipInstall()
+	m.HideFromMake()
 }
 
 func (m *ModuleBase) IsReplacedByPrebuilt() bool {
@@ -2440,11 +2454,15 @@
 	return m.module.InstallForceOS()
 }
 
-func (m *moduleContext) skipInstall(fullInstallPath InstallPath) bool {
+func (m *moduleContext) skipInstall() bool {
 	if m.module.base().commonProperties.SkipInstall {
 		return true
 	}
 
+	if m.module.base().commonProperties.HideFromMake {
+		return true
+	}
+
 	// We'll need a solution for choosing which of modules with the same name in different
 	// namespaces to install.  For now, reuse the list of namespaces exported to Make as the
 	// list of namespaces to install in a Soong-only build.
@@ -2492,7 +2510,7 @@
 	fullInstallPath := installPath.Join(m, name)
 	m.module.base().hooks.runInstallHooks(m, srcPath, fullInstallPath, false)
 
-	if !m.skipInstall(fullInstallPath) {
+	if !m.skipInstall() {
 		deps = append(deps, m.module.base().installFilesDepSet.ToList().Paths()...)
 
 		var implicitDeps, orderOnlyDeps Paths
@@ -2526,6 +2544,7 @@
 	m.packageFile(fullInstallPath, srcPath, executable)
 
 	m.checkbuildFiles = append(m.checkbuildFiles, srcPath)
+
 	return fullInstallPath
 }
 
@@ -2537,7 +2556,7 @@
 	if err != nil {
 		panic(fmt.Sprintf("Unable to generate symlink between %q and %q: %s", fullInstallPath.Base(), srcPath.Base(), err))
 	}
-	if !m.skipInstall(fullInstallPath) {
+	if !m.skipInstall() {
 
 		m.Build(pctx, BuildParams{
 			Rule:        Symlink,
@@ -2570,7 +2589,7 @@
 	fullInstallPath := installPath.Join(m, name)
 	m.module.base().hooks.runInstallHooks(m, nil, fullInstallPath, true)
 
-	if !m.skipInstall(fullInstallPath) {
+	if !m.skipInstall() {
 		m.Build(pctx, BuildParams{
 			Rule:        Symlink,
 			Description: "install symlink " + fullInstallPath.Base() + " -> " + absPath,
@@ -2738,6 +2757,7 @@
 // Modules can implement HostToolProvider and return a valid OptionalPath from HostToolPath() to
 // specify that they can be used as a tool by a genrule module.
 type HostToolProvider interface {
+	Module
 	// HostToolPath returns the path to the host tool for the module if it is one, or an invalid
 	// OptionalPath.
 	HostToolPath() OptionalPath
diff --git a/android/namespace.go b/android/namespace.go
index 9d7e8ac..d137636 100644
--- a/android/namespace.go
+++ b/android/namespace.go
@@ -232,13 +232,14 @@
 
 }
 
-func (r *NameResolver) getNamespacesToSearchForModule(sourceNamespace *Namespace) (searchOrder []*Namespace) {
-	if sourceNamespace.visibleNamespaces == nil {
+func (r *NameResolver) getNamespacesToSearchForModule(sourceNamespace blueprint.Namespace) (searchOrder []*Namespace) {
+	ns, ok := sourceNamespace.(*Namespace)
+	if !ok || ns.visibleNamespaces == nil {
 		// When handling dependencies before namespaceMutator, assume they are non-Soong Blueprint modules and give
 		// access to all namespaces.
 		return r.sortedNamespaces.sortedItems()
 	}
-	return sourceNamespace.visibleNamespaces
+	return ns.visibleNamespaces
 }
 
 func (r *NameResolver) ModuleFromName(name string, namespace blueprint.Namespace) (group blueprint.ModuleGroup, found bool) {
@@ -252,7 +253,7 @@
 		container := namespace.moduleContainer
 		return container.ModuleFromName(moduleName, nil)
 	}
-	for _, candidate := range r.getNamespacesToSearchForModule(namespace.(*Namespace)) {
+	for _, candidate := range r.getNamespacesToSearchForModule(namespace) {
 		group, found = candidate.moduleContainer.ModuleFromName(name, nil)
 		if found {
 			return group, true
diff --git a/android/neverallow.go b/android/neverallow.go
index 8b8e1ac..031b3f4 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -51,7 +51,6 @@
 func init() {
 	AddNeverAllowRules(createIncludeDirsRules()...)
 	AddNeverAllowRules(createTrebleRules()...)
-	AddNeverAllowRules(createLibcoreRules()...)
 	AddNeverAllowRules(createMediaRules()...)
 	AddNeverAllowRules(createJavaDeviceForHostRules()...)
 	AddNeverAllowRules(createCcSdkVariantRules()...)
@@ -133,38 +132,6 @@
 	}
 }
 
-func createLibcoreRules() []Rule {
-	var coreLibraryProjects = []string{
-		"libcore",
-		"external/apache-harmony",
-		"external/apache-xml",
-		"external/bouncycastle",
-		"external/conscrypt",
-		"external/icu",
-		"external/okhttp",
-		"external/wycheproof",
-		"prebuilts",
-	}
-
-	// Additional whitelisted path only used for ART testing, which needs access to core library
-	// targets. This does not affect the contents of a device image (system, vendor, etc.).
-	var artTests = []string{
-		"art/test",
-	}
-
-	// Core library constraints. The sdk_version: "none" can only be used in core library projects and ART tests.
-	// Access to core library targets is restricted using visibility rules.
-	rules := []Rule{
-		NeverAllow().
-			NotIn(coreLibraryProjects...).
-			NotIn(artTests...).
-			With("sdk_version", "none").
-			WithoutMatcher("name", Regexp("^android_.*stubs_current$")),
-	}
-
-	return rules
-}
-
 func createMediaRules() []Rule {
 	return []Rule{
 		NeverAllow().
diff --git a/android/neverallow_test.go b/android/neverallow_test.go
index 1d454e5..8c7a538 100644
--- a/android/neverallow_test.go
+++ b/android/neverallow_test.go
@@ -215,50 +215,6 @@
 			"java_device_for_host can only be used in allowed projects",
 		},
 	},
-	// Libcore rule tests
-	{
-		name: "sdk_version: \"none\" inside core libraries",
-		fs: map[string][]byte{
-			"libcore/Android.bp": []byte(`
-				java_library {
-					name: "inside_core_libraries",
-					sdk_version: "none",
-				}`),
-		},
-	},
-	{
-		name: "sdk_version: \"none\" on android_*stubs_current stub",
-		fs: map[string][]byte{
-			"frameworks/base/Android.bp": []byte(`
-				java_library {
-					name: "android_stubs_current",
-					sdk_version: "none",
-				}`),
-		},
-	},
-	{
-		name: "sdk_version: \"none\" outside core libraries",
-		fs: map[string][]byte{
-			"Android.bp": []byte(`
-				java_library {
-					name: "outside_core_libraries",
-					sdk_version: "none",
-				}`),
-		},
-		expectedErrors: []string{
-			"module \"outside_core_libraries\": violates neverallow",
-		},
-	},
-	{
-		name: "sdk_version: \"current\"",
-		fs: map[string][]byte{
-			"Android.bp": []byte(`
-				java_library {
-					name: "outside_core_libraries",
-					sdk_version: "current",
-				}`),
-		},
-	},
 	// CC sdk rule tests
 	{
 		name: `"sdk_variant_only" outside allowed list`,
diff --git a/android/override_module.go b/android/override_module.go
index f8342d5..fa08566 100644
--- a/android/override_module.go
+++ b/android/override_module.go
@@ -235,7 +235,7 @@
 			return
 		}
 		// See if there's a prebuilt module that overrides this override module with prefer flag,
-		// in which case we call SkipInstall on the corresponding variant later.
+		// in which case we call HideFromMake on the corresponding variant later.
 		ctx.VisitDirectDepsWithTag(PrebuiltDepTag, func(dep Module) {
 			prebuilt, ok := dep.(PrebuiltInterface)
 			if !ok {
@@ -284,7 +284,7 @@
 			mods[i+1].(OverridableModule).override(ctx, o)
 			if o.getOverriddenByPrebuilt() {
 				// The overriding module itself, too, is overridden by a prebuilt. Skip its installation.
-				mods[i+1].SkipInstall()
+				mods[i+1].HideFromMake()
 			}
 		}
 	} else if o, ok := ctx.Module().(OverrideModule); ok {
diff --git a/android/prebuilt.go b/android/prebuilt.go
index 8114a65..39d30c5 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -289,7 +289,7 @@
 				})
 			}
 		} else {
-			m.SkipInstall()
+			m.HideFromMake()
 		}
 	}
 }
diff --git a/android/prebuilt_build_tool.go b/android/prebuilt_build_tool.go
index b00dc2f..516d042 100644
--- a/android/prebuilt_build_tool.go
+++ b/android/prebuilt_build_tool.go
@@ -86,6 +86,9 @@
 
 func (t *prebuiltBuildTool) MakeVars(ctx MakeVarsModuleContext) {
 	if makeVar := String(t.properties.Export_to_make_var); makeVar != "" {
+		if t.Target().Os != BuildOs {
+			return
+		}
 		ctx.StrictRaw(makeVar, t.toolPath.String())
 	}
 }
diff --git a/android/rule_builder.go b/android/rule_builder.go
index e2d8187..84501fe 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -32,6 +32,7 @@
 
 const sboxSandboxBaseDir = "__SBOX_SANDBOX_DIR__"
 const sboxOutSubDir = "out"
+const sboxToolsSubDir = "tools"
 const sboxOutDir = sboxSandboxBaseDir + "/" + sboxOutSubDir
 
 // RuleBuilder provides an alternative to ModuleContext.Rule and ModuleContext.Build to add a command line to the build
@@ -48,6 +49,7 @@
 	highmem          bool
 	remoteable       RemoteRuleSupports
 	outDir           WritablePath
+	sboxTools        bool
 	sboxManifestPath WritablePath
 	missingDeps      []string
 }
@@ -140,6 +142,19 @@
 	return r
 }
 
+// SandboxTools enables tool sandboxing for the rule by copying any referenced tools into the
+// sandbox.
+func (r *RuleBuilder) SandboxTools() *RuleBuilder {
+	if !r.sbox {
+		panic("SandboxTools() must be called after Sbox()")
+	}
+	if len(r.commands) > 0 {
+		panic("SandboxTools() may not be called after Command()")
+	}
+	r.sboxTools = true
+	return r
+}
+
 // Install associates an output of the rule with an install location, which can be retrieved later using
 // RuleBuilder.Installs.
 func (r *RuleBuilder) Install(from Path, to string) {
@@ -468,8 +483,29 @@
 			manifest.OutputDepfile = proto.String(depFile.String())
 		}
 
+		// If sandboxing tools is enabled, add copy rules to the manifest to copy each tool
+		// into the sbox directory.
+		if r.sboxTools {
+			for _, tool := range tools {
+				command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
+					From: proto.String(tool.String()),
+					To:   proto.String(sboxPathForToolRel(r.ctx, tool)),
+				})
+			}
+			for _, c := range r.commands {
+				for _, tool := range c.packagedTools {
+					command.CopyBefore = append(command.CopyBefore, &sbox_proto.Copy{
+						From:       proto.String(tool.srcPath.String()),
+						To:         proto.String(sboxPathForPackagedToolRel(tool)),
+						Executable: proto.Bool(tool.executable),
+					})
+					tools = append(tools, tool.srcPath)
+				}
+			}
+		}
+
 		// Add copy rules to the manifest to copy each output file from the sbox directory.
-		// to the output directory.
+		// to the output directory after running the commands.
 		sboxOutputs := make([]string, len(outputs))
 		for i, output := range outputs {
 			rel := Rel(r.ctx, r.outDir.String(), output.String())
@@ -582,6 +618,7 @@
 	symlinkOutputs WritablePaths
 	depFiles       WritablePaths
 	tools          Paths
+	packagedTools  []PackagingSpec
 	rspFileInputs  Paths
 
 	// spans [start,end) of the command that should not be ninja escaped
@@ -625,6 +662,79 @@
 	return path.String()
 }
 
+// SboxPathForTool takes a path to a tool, which may be an output file or a source file, and returns
+// the corresponding path for the tool in the sbox sandbox.  It assumes that sandboxing and tool
+// sandboxing are enabled.
+func SboxPathForTool(ctx BuilderContext, path Path) string {
+	return filepath.Join(sboxSandboxBaseDir, sboxPathForToolRel(ctx, path))
+}
+
+func sboxPathForToolRel(ctx BuilderContext, path Path) string {
+	// Errors will be handled in RuleBuilder.Build where we have a context to report them
+	relOut, isRelOut, _ := maybeRelErr(PathForOutput(ctx, "host", ctx.Config().PrebuiltOS()).String(), path.String())
+	if isRelOut {
+		// The tool is in the output directory, it will be copied to __SBOX_OUT_DIR__/tools/out
+		return filepath.Join(sboxToolsSubDir, "out", relOut)
+	}
+	// The tool is in the source directory, it will be copied to __SBOX_OUT_DIR__/tools/src
+	return filepath.Join(sboxToolsSubDir, "src", path.String())
+}
+
+// SboxPathForPackagedTool takes a PackageSpec for a tool and returns the corresponding path for the
+// tool after copying it into the sandbox.  This can be used  on the RuleBuilder command line to
+// reference the tool.
+func SboxPathForPackagedTool(spec PackagingSpec) string {
+	return filepath.Join(sboxSandboxBaseDir, sboxPathForPackagedToolRel(spec))
+}
+
+func sboxPathForPackagedToolRel(spec PackagingSpec) string {
+	return filepath.Join(sboxToolsSubDir, "out", spec.relPathInPackage)
+}
+
+// PathForTool takes a path to a tool, which may be an output file or a source file, and returns
+// the corresponding path for the tool in the sbox sandbox if sbox is enabled, or the original path
+// if it is not.  This can be used  on the RuleBuilder command line to reference the tool.
+func (c *RuleBuilderCommand) PathForTool(path Path) string {
+	if c.rule.sbox && c.rule.sboxTools {
+		return filepath.Join(sboxSandboxBaseDir, sboxPathForToolRel(c.rule.ctx, path))
+	}
+	return path.String()
+}
+
+// PackagedTool adds the specified tool path to the command line.  It can only be used with tool
+// sandboxing enabled by SandboxTools(), and will copy the tool into the sandbox.
+func (c *RuleBuilderCommand) PackagedTool(spec PackagingSpec) *RuleBuilderCommand {
+	if !c.rule.sboxTools {
+		panic("PackagedTool() requires SandboxTools()")
+	}
+
+	c.packagedTools = append(c.packagedTools, spec)
+	c.Text(sboxPathForPackagedToolRel(spec))
+	return c
+}
+
+// ImplicitPackagedTool copies the specified tool into the sandbox without modifying the command
+// line.  It can only be used with tool sandboxing enabled by SandboxTools().
+func (c *RuleBuilderCommand) ImplicitPackagedTool(spec PackagingSpec) *RuleBuilderCommand {
+	if !c.rule.sboxTools {
+		panic("ImplicitPackagedTool() requires SandboxTools()")
+	}
+
+	c.packagedTools = append(c.packagedTools, spec)
+	return c
+}
+
+// ImplicitPackagedTools copies the specified tools into the sandbox without modifying the command
+// line.  It can only be used with tool sandboxing enabled by SandboxTools().
+func (c *RuleBuilderCommand) ImplicitPackagedTools(specs []PackagingSpec) *RuleBuilderCommand {
+	if !c.rule.sboxTools {
+		panic("ImplicitPackagedTools() requires SandboxTools()")
+	}
+
+	c.packagedTools = append(c.packagedTools, specs...)
+	return c
+}
+
 // Text adds the specified raw text to the command line.  The text should not contain input or output paths or the
 // rule will not have them listed in its dependencies or outputs.
 func (c *RuleBuilderCommand) Text(text string) *RuleBuilderCommand {
@@ -693,7 +803,19 @@
 // RuleBuilder.Tools.
 func (c *RuleBuilderCommand) Tool(path Path) *RuleBuilderCommand {
 	c.tools = append(c.tools, path)
-	return c.Text(path.String())
+	return c.Text(c.PathForTool(path))
+}
+
+// Tool adds the specified tool path to the dependencies returned by RuleBuilder.Tools.
+func (c *RuleBuilderCommand) ImplicitTool(path Path) *RuleBuilderCommand {
+	c.tools = append(c.tools, path)
+	return c
+}
+
+// Tool adds the specified tool path to the dependencies returned by RuleBuilder.Tools.
+func (c *RuleBuilderCommand) ImplicitTools(paths Paths) *RuleBuilderCommand {
+	c.tools = append(c.tools, paths...)
+	return c
 }
 
 // BuiltTool adds the specified tool path that was built using a host Soong module to the command line.  The path will
diff --git a/android/rule_builder_test.go b/android/rule_builder_test.go
index e676e4a..06ea124 100644
--- a/android/rule_builder_test.go
+++ b/android/rule_builder_test.go
@@ -436,6 +436,44 @@
 			t.Errorf("\nwant rule.depFileMergerCmd() = %#v\n                   got %#v", w, g)
 		}
 	})
+
+	t.Run("sbox tools", func(t *testing.T) {
+		rule := NewRuleBuilder(pctx, ctx).Sbox(PathForOutput(ctx, ""),
+			PathForOutput(ctx, "sbox.textproto")).SandboxTools()
+		addCommands(rule)
+
+		wantCommands := []string{
+			"__SBOX_SANDBOX_DIR__/out/DepFile Flag FlagWithArg=arg FlagWithDepFile=__SBOX_SANDBOX_DIR__/out/depfile FlagWithInput=input FlagWithOutput=__SBOX_SANDBOX_DIR__/out/output Input __SBOX_SANDBOX_DIR__/out/Output __SBOX_SANDBOX_DIR__/out/SymlinkOutput Text __SBOX_SANDBOX_DIR__/tools/src/Tool after command2 old cmd",
+			"command2 __SBOX_SANDBOX_DIR__/out/depfile2 input2 __SBOX_SANDBOX_DIR__/out/output2 __SBOX_SANDBOX_DIR__/tools/src/tool2",
+			"command3 input3 __SBOX_SANDBOX_DIR__/out/output2 __SBOX_SANDBOX_DIR__/out/output3",
+		}
+
+		wantDepMergerCommand := "__SBOX_SANDBOX_DIR__/tools/out/bin/dep_fixer __SBOX_SANDBOX_DIR__/out/DepFile __SBOX_SANDBOX_DIR__/out/depfile __SBOX_SANDBOX_DIR__/out/ImplicitDepFile __SBOX_SANDBOX_DIR__/out/depfile2"
+
+		if g, w := rule.Commands(), wantCommands; !reflect.DeepEqual(g, w) {
+			t.Errorf("\nwant rule.Commands() = %#v\n                   got %#v", w, g)
+		}
+
+		if g, w := rule.Inputs(), wantInputs; !reflect.DeepEqual(w, g) {
+			t.Errorf("\nwant rule.Inputs() = %#v\n                 got %#v", w, g)
+		}
+		if g, w := rule.Outputs(), wantOutputs; !reflect.DeepEqual(w, g) {
+			t.Errorf("\nwant rule.Outputs() = %#v\n                  got %#v", w, g)
+		}
+		if g, w := rule.DepFiles(), wantDepFiles; !reflect.DeepEqual(w, g) {
+			t.Errorf("\nwant rule.DepFiles() = %#v\n                  got %#v", w, g)
+		}
+		if g, w := rule.Tools(), wantTools; !reflect.DeepEqual(w, g) {
+			t.Errorf("\nwant rule.Tools() = %#v\n                got %#v", w, g)
+		}
+		if g, w := rule.OrderOnlys(), wantOrderOnlys; !reflect.DeepEqual(w, g) {
+			t.Errorf("\nwant rule.OrderOnlys() = %#v\n                got %#v", w, g)
+		}
+
+		if g, w := rule.depFileMergerCmd(rule.DepFiles()).String(), wantDepMergerCommand; g != w {
+			t.Errorf("\nwant rule.depFileMergerCmd() = %#v\n                   got %#v", w, g)
+		}
+	})
 }
 
 func testRuleBuilderFactory() Module {
diff --git a/android/soongconfig/modules.go b/android/soongconfig/modules.go
index 142a813..5a6917e 100644
--- a/android/soongconfig/modules.go
+++ b/android/soongconfig/modules.go
@@ -158,11 +158,7 @@
 			return []error{fmt.Errorf("bool_variable name must not be blank")}
 		}
 
-		mt.Variables = append(mt.Variables, &boolVariable{
-			baseVariable: baseVariable{
-				variable: name,
-			},
-		})
+		mt.Variables = append(mt.Variables, newBoolVariable(name))
 	}
 
 	for _, name := range props.Value_variables {
@@ -420,6 +416,9 @@
 
 // PropertiesToApply returns the applicable properties from a ModuleType that should be applied
 // based on SoongConfig values.
+// Expects that props contains a struct field with name soong_config_variables. The fields within
+// soong_config_variables are expected to be in the same order as moduleType.Variables. In general,
+// props should be generated via CreateProperties.
 func PropertiesToApply(moduleType *ModuleType, props reflect.Value, config SoongConfig) ([]interface{}, error) {
 	var ret []interface{}
 	props = props.Elem().FieldByName(soongConfigProperty)
@@ -505,6 +504,14 @@
 	baseVariable
 }
 
+func newBoolVariable(name string) *boolVariable {
+	return &boolVariable{
+		baseVariable{
+			variable: name,
+		},
+	}
+}
+
 func (b boolVariable) variableValuesType() reflect.Type {
 	return emptyInterfaceType
 }
diff --git a/android/soongconfig/modules_test.go b/android/soongconfig/modules_test.go
index 4190016..fb0e189 100644
--- a/android/soongconfig/modules_test.go
+++ b/android/soongconfig/modules_test.go
@@ -17,6 +17,8 @@
 import (
 	"reflect"
 	"testing"
+
+	"github.com/google/blueprint/proptools"
 )
 
 func Test_CanonicalizeToProperty(t *testing.T) {
@@ -247,3 +249,72 @@
 		})
 	}
 }
+
+type properties struct {
+	A *string
+	B bool
+}
+type soongConfigVariables struct {
+	Bool_var       properties
+	Other_bool_var properties
+}
+
+type soongConfigProps struct {
+	Soong_config_variables soongConfigVariables
+}
+
+func Test_PropertiesToApply(t *testing.T) {
+
+	mt := &ModuleType{
+		BaseModuleType:  "foo",
+		ConfigNamespace: "bar",
+		Variables: []soongConfigVariable{
+			newBoolVariable("bool_var"),
+			newBoolVariable("other_bool_var"),
+		},
+		affectableProperties: []string{
+			"a",
+			"b",
+		},
+	}
+	props := soongConfigProps{
+		Soong_config_variables: soongConfigVariables{
+			Bool_var: properties{
+				A: proptools.StringPtr("a"),
+				B: true,
+			},
+			Other_bool_var: properties{
+				A: proptools.StringPtr("other"),
+				B: false,
+			},
+		},
+	}
+
+	testCases := []struct {
+		config    SoongConfig
+		wantProps []interface{}
+	}{
+		{
+			config: Config(map[string]string{}),
+		},
+		{
+			config:    Config(map[string]string{"bool_var": "y"}),
+			wantProps: []interface{}{props.Soong_config_variables.Bool_var},
+		},
+		{
+			config:    Config(map[string]string{"other_bool_var": "y"}),
+			wantProps: []interface{}{props.Soong_config_variables.Other_bool_var},
+		},
+	}
+
+	for _, tc := range testCases {
+		gotProps, err := PropertiesToApply(mt, reflect.ValueOf(&props), tc.config)
+		if err != nil {
+			t.Errorf("Unexpected error in PropertiesToApply: %s", err)
+		}
+
+		if !reflect.DeepEqual(gotProps, tc.wantProps) {
+			t.Errorf("Expected %s, got %s", tc.wantProps, gotProps)
+		}
+	}
+}
diff --git a/android/variable.go b/android/variable.go
index 93dac3d..753ddd7 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -126,13 +126,14 @@
 		}
 
 		Arc struct {
-			Cflags       []string
-			Exclude_srcs []string
-			Include_dirs []string
-			Shared_libs  []string
-			Static_libs  []string
-			Srcs         []string
-		}
+			Cflags            []string `android:"arch_variant"`
+			Exclude_srcs      []string `android:"arch_variant"`
+			Include_dirs      []string `android:"arch_variant"`
+			Shared_libs       []string `android:"arch_variant"`
+			Static_libs       []string `android:"arch_variant"`
+			Srcs              []string `android:"arch_variant"`
+			Whole_static_libs []string `android:"arch_variant"`
+		} `android:"arch_variant"`
 
 		Flatten_apex struct {
 			Enabled *bool
diff --git a/apex/apex.go b/apex/apex.go
index 88d93af..9e66254 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -1183,6 +1183,9 @@
 // Implements cc.Coverage
 func (a *apexBundle) HideFromMake() {
 	a.properties.HideFromMake = true
+	// This HideFromMake is shadowing the ModuleBase one, call through to it for now.
+	// TODO(ccross): untangle these
+	a.ModuleBase.HideFromMake()
 }
 
 // Implements cc.Coverage
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 69b1dbb..cb43e24 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -1355,9 +1355,9 @@
 			ensureListContains(t, names(apexManifestRule.Args["requireNativeLibs"]), "libbar.so")
 
 			mylibLdFlags := ctx.ModuleForTests("mylib", "android_vendor.VER_arm64_armv8-a_shared_"+tc.apexVariant).Rule("ld").Args["libFlags"]
-			ensureContains(t, mylibLdFlags, "libbar.llndk/android_vendor.VER_arm64_armv8-a_shared_"+tc.shouldLink+"/libbar.so")
+			ensureContains(t, mylibLdFlags, "libbar/android_vendor.VER_arm64_armv8-a_shared_"+tc.shouldLink+"/libbar.so")
 			for _, ver := range tc.shouldNotLink {
-				ensureNotContains(t, mylibLdFlags, "libbar.llndk/android_vendor.VER_arm64_armv8-a_shared_"+ver+"/libbar.so")
+				ensureNotContains(t, mylibLdFlags, "libbar/android_vendor.VER_arm64_armv8-a_shared_"+ver+"/libbar.so")
 			}
 
 			mylibCFlags := ctx.ModuleForTests("mylib", "android_vendor.VER_arm64_armv8-a_static_"+tc.apexVariant).Rule("cc").Args["cFlags"]
@@ -6578,7 +6578,7 @@
 								continue
 							}
 							mod := ctx.ModuleForTests(modName, variant).Module().(*cc.Module)
-							if !mod.Enabled() || mod.IsSkipInstall() {
+							if !mod.Enabled() || mod.IsHideFromMake() {
 								continue
 							}
 							for _, ent := range android.AndroidMkEntriesForTest(t, config, "", mod) {
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index ce16d73..7931e9e 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -218,7 +218,7 @@
 	})
 
 	if p.prebuiltCommon.checkForceDisable(ctx) {
-		p.SkipInstall()
+		p.HideFromMake()
 		return
 	}
 
@@ -392,7 +392,7 @@
 		})
 
 	if a.prebuiltCommon.checkForceDisable(ctx) {
-		a.SkipInstall()
+		a.HideFromMake()
 		return
 	}
 
diff --git a/build_test.bash b/build_test.bash
index accca0f..3230f2d 100755
--- a/build_test.bash
+++ b/build_test.bash
@@ -23,6 +23,11 @@
 # evolve as we find interesting things to test or track performance for.
 #
 
+# Products that are broken or otherwise don't work with multiproduct_kati
+SKIPPED_PRODUCTS=(
+    mainline_sdk
+)
+
 # To track how long we took to startup. %N isn't supported on Darwin, but
 # that's detected in the Go code, which skips calculating the startup time.
 export TRACE_BEGIN_SOONG=$(date +%s%N)
@@ -50,4 +55,4 @@
 echo
 echo "Running Soong test..."
 soong_build_go multiproduct_kati android/soong/cmd/multiproduct_kati
-exec "$(getoutdir)/multiproduct_kati" "$@"
+exec "$(getoutdir)/multiproduct_kati" --skip-products "$(echo "${SKIPPED_PRODUCTS[@]-}" | tr ' ' ',')" "$@"
diff --git a/cc/androidmk.go b/cc/androidmk.go
index 187a2ff..c4b9cd5 100644
--- a/cc/androidmk.go
+++ b/cc/androidmk.go
@@ -269,11 +269,11 @@
 	if library.shared() && !library.buildStubs() {
 		ctx.subAndroidMk(entries, library.baseInstaller)
 	} else {
-		if library.buildStubs() {
+		if library.buildStubs() && library.stubsVersion() != "" {
 			entries.SubName = "." + library.stubsVersion()
 		}
 		entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
-			// library.makeUninstallable() depends on this to bypass SkipInstall() for
+			// library.makeUninstallable() depends on this to bypass HideFromMake() for
 			// static libraries.
 			entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
 			if library.buildStubs() {
@@ -471,18 +471,9 @@
 }
 
 func (c *llndkStubDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
-	entries.Class = "SHARED_LIBRARIES"
-	entries.OverrideName = c.implementationModuleName(ctx.BaseModuleName())
-
-	entries.ExtraEntries = append(entries.ExtraEntries, func(entries *android.AndroidMkEntries) {
-		c.libraryDecorator.androidMkWriteExportedFlags(entries)
-		_, _, ext := android.SplitFileExt(entries.OutputFile.Path().Base())
-
-		entries.SetString("LOCAL_BUILT_MODULE_STEM", "$(LOCAL_MODULE)"+ext)
-		entries.SetBool("LOCAL_UNINSTALLABLE_MODULE", true)
-		entries.SetBool("LOCAL_NO_NOTICE_FILE", true)
-		entries.SetString("LOCAL_SOONG_TOC", c.toc().String())
-	})
+	// Don't write anything for an llndk_library module, the vendor variant of the cc_library
+	// module will write the Android.mk entries.
+	entries.Disabled = true
 }
 
 func (c *vndkPrebuiltLibraryDecorator) AndroidMkEntries(ctx AndroidMkContext, entries *android.AndroidMkEntries) {
diff --git a/cc/cc.go b/cc/cc.go
index 89f32f1..a023f3f 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -390,6 +390,13 @@
 	// explicitly marked as `double_loadable: true` by the owner, or the dependency
 	// from the LLNDK lib should be cut if the lib is not designed to be double loaded.
 	Double_loadable *bool
+
+	// IsLLNDK is set to true for the vendor variant of a cc_library module that has LLNDK stubs.
+	IsLLNDK bool `blueprint:"mutated"`
+
+	// IsLLNDKPrivate is set to true for the vendor variant of a cc_library module that has LLNDK
+	// stubs and also sets llndk.vendor_available: false.
+	IsLLNDKPrivate bool `blueprint:"mutated"`
 }
 
 // ModuleContextIntf is an interface (on a module context helper) consisting of functions related
@@ -408,9 +415,10 @@
 	sdkVersion() string
 	useVndk() bool
 	isNdk(config android.Config) bool
-	isLlndk(config android.Config) bool
-	isLlndkPublic(config android.Config) bool
-	isVndkPrivate(config android.Config) bool
+	IsLlndk() bool
+	IsLlndkPublic() bool
+	isImplementationForLLNDKPublic() bool
+	IsVndkPrivate() bool
 	isVndk() bool
 	isVndkSp() bool
 	IsVndkExt() bool
@@ -432,6 +440,7 @@
 	mustUseVendorVariant() bool
 	nativeCoverage() bool
 	directlyInAnyApex() bool
+	isPreventInstall() bool
 }
 
 type ModuleContext interface {
@@ -644,6 +653,7 @@
 	runtimeDepTag         = installDependencyTag{name: "runtime lib"}
 	testPerSrcDepTag      = dependencyTag{name: "test_per_src"}
 	stubImplDepTag        = dependencyTag{name: "stub_impl"}
+	llndkStubDepTag       = dependencyTag{name: "llndk stub"}
 )
 
 type copyDirectlyInAnyApexDependencyTag dependencyTag
@@ -1027,20 +1037,34 @@
 	return inList(c.BaseModuleName(), *getNDKKnownLibs(config))
 }
 
-func (c *Module) isLlndk(config android.Config) bool {
-	// Returns true for both LLNDK (public) and LLNDK-private libs.
-	return isLlndkLibrary(c.BaseModuleName(), config)
+// isLLndk returns true for both LLNDK (public) and LLNDK-private libs.
+func (c *Module) IsLlndk() bool {
+	return c.VendorProperties.IsLLNDK
 }
 
-func (c *Module) isLlndkPublic(config android.Config) bool {
-	// Returns true only for LLNDK (public) libs.
-	name := c.BaseModuleName()
-	return isLlndkLibrary(name, config) && !isVndkPrivateLibrary(name, config)
+// IsLlndkPublic returns true only for LLNDK (public) libs.
+func (c *Module) IsLlndkPublic() bool {
+	return c.VendorProperties.IsLLNDK && !c.VendorProperties.IsLLNDKPrivate
 }
 
-func (c *Module) IsVndkPrivate(config android.Config) bool {
+// isImplementationForLLNDKPublic returns true for any variant of a cc_library that has LLNDK stubs
+// and does not set llndk.vendor_available: false.
+func (c *Module) isImplementationForLLNDKPublic() bool {
+	library, _ := c.library.(*libraryDecorator)
+	return library != nil && library.hasLLNDKStubs() &&
+		(Bool(library.Properties.Llndk.Vendor_available) ||
+			// TODO(b/170784825): until the LLNDK properties are moved into the cc_library,
+			// the non-Vendor variants of the cc_library don't know if the corresponding
+			// llndk_library set vendor_available: false.  Since libft2 is the only
+			// private LLNDK library, hardcode it during the transition.
+			c.BaseModuleName() != "libft2")
+}
+
+func (c *Module) IsVndkPrivate() bool {
 	// Returns true for LLNDK-private, VNDK-SP-private, and VNDK-core-private.
-	return isVndkPrivateLibrary(c.BaseModuleName(), config)
+	library, _ := c.library.(*libraryDecorator)
+	return library != nil && !Bool(library.Properties.Llndk.Vendor_available) &&
+		!Bool(c.VendorProperties.Vendor_available) && !c.IsVndkExt()
 }
 
 func (c *Module) IsVndk() bool {
@@ -1246,16 +1270,20 @@
 	return ctx.mod.IsNdk(config)
 }
 
-func (ctx *moduleContextImpl) isLlndk(config android.Config) bool {
-	return ctx.mod.isLlndk(config)
+func (ctx *moduleContextImpl) IsLlndk() bool {
+	return ctx.mod.IsLlndk()
 }
 
-func (ctx *moduleContextImpl) isLlndkPublic(config android.Config) bool {
-	return ctx.mod.isLlndkPublic(config)
+func (ctx *moduleContextImpl) IsLlndkPublic() bool {
+	return ctx.mod.IsLlndkPublic()
 }
 
-func (ctx *moduleContextImpl) isVndkPrivate(config android.Config) bool {
-	return ctx.mod.IsVndkPrivate(config)
+func (ctx *moduleContextImpl) isImplementationForLLNDKPublic() bool {
+	return ctx.mod.isImplementationForLLNDKPublic()
+}
+
+func (ctx *moduleContextImpl) IsVndkPrivate() bool {
+	return ctx.mod.IsVndkPrivate()
 }
 
 func (ctx *moduleContextImpl) isVndk() bool {
@@ -1325,6 +1353,10 @@
 	return ctx.mod.DirectlyInAnyApex()
 }
 
+func (ctx *moduleContextImpl) isPreventInstall() bool {
+	return ctx.mod.Properties.PreventInstall
+}
+
 func newBaseModule(hod android.HostOrDeviceSupported, multilib android.Multilib) *Module {
 	return &Module{
 		hod:      hod,
@@ -1402,7 +1434,7 @@
 	if vndkVersion == "current" {
 		vndkVersion = ctx.DeviceConfig().PlatformVndkVersion()
 	}
-	if c.Properties.VndkVersion != vndkVersion {
+	if c.Properties.VndkVersion != vndkVersion && c.Properties.VndkVersion != "" {
 		// add version suffix only if the module is using different vndk version than the
 		// version in product or vendor partition.
 		nameSuffix += "." + c.Properties.VndkVersion
@@ -1434,7 +1466,7 @@
 		c.Properties.SubName += nativeBridgeSuffix
 	}
 
-	_, llndk := c.linker.(*llndkStubDecorator)
+	llndk := c.IsLlndk()
 	_, llndkHeader := c.linker.(*llndkHeadersDecorator)
 	if llndk || llndkHeader || (c.UseVndk() && c.HasNonSystemVariants()) {
 		// .vendor.{version} suffix is added for vendor variant or .product.{version} suffix is
@@ -1575,18 +1607,24 @@
 		}
 	}
 
-	if c.installable(apexInfo) {
+	if !proptools.BoolDefault(c.Properties.Installable, true) {
+		// If the module has been specifically configure to not be installed then
+		// hide from make as otherwise it will break when running inside make
+		// as the output path to install will not be specified. Not all uninstallable
+		// modules can be hidden from make as some are needed for resolving make side
+		// dependencies.
+		c.HideFromMake()
+	} else if !c.installable(apexInfo) {
+		c.SkipInstall()
+	}
+
+	// Still call c.installer.install though, the installs will be stored as PackageSpecs
+	// to allow using the outputs in a genrule.
+	if c.installer != nil && c.outputFile.Valid() {
 		c.installer.install(ctx, c.outputFile.Path())
 		if ctx.Failed() {
 			return
 		}
-	} else if !proptools.BoolDefault(c.Properties.Installable, true) {
-		// If the module has been specifically configure to not be installed then
-		// skip the installation as otherwise it will break when running inside make
-		// as the output path to install will not be specified. Not all uninstallable
-		// modules can skip installation as some are needed for resolving make side
-		// dependencies.
-		c.SkipInstall()
 	}
 }
 
@@ -1806,10 +1844,6 @@
 		vendorSnapshotSharedLibs := vendorSnapshotSharedLibs(actx.Config())
 
 		rewriteVendorLibs := func(lib string) string {
-			if isLlndkLibrary(lib, ctx.Config()) {
-				return lib + llndkLibrarySuffix
-			}
-
 			// only modules with BOARD_VNDK_VERSION uses snapshot.
 			if c.VndkVersion() != actx.DeviceConfig().VndkVersion() {
 				return lib
@@ -2226,7 +2260,7 @@
 			return true
 		}
 
-		if to.isVndkSp() || to.isLlndk(ctx.Config()) || Bool(to.VendorProperties.Double_loadable) {
+		if to.isVndkSp() || to.IsLlndk() || Bool(to.VendorProperties.Double_loadable) {
 			return false
 		}
 
@@ -2241,7 +2275,7 @@
 	}
 	if module, ok := ctx.Module().(*Module); ok {
 		if lib, ok := module.linker.(*libraryDecorator); ok && lib.shared() {
-			if module.isLlndk(ctx.Config()) || Bool(module.VendorProperties.Double_loadable) {
+			if lib.hasLLNDKStubs() || Bool(module.VendorProperties.Double_loadable) {
 				ctx.WalkDeps(check)
 			}
 		}
@@ -2361,9 +2395,6 @@
 		if depTag == android.ProtoPluginDepTag {
 			return
 		}
-		if depTag == llndkImplDep {
-			return
-		}
 
 		if dep.Target().Os != ctx.Os() {
 			ctx.ModuleErrorf("OS mismatch between %q and %q", ctx.ModuleName(), depName)
@@ -2733,7 +2764,8 @@
 	vendorPublicLibraries := vendorPublicLibraries(ctx.Config())
 
 	libName := baseLibName(depName)
-	isLLndk := isLlndkLibrary(libName, ctx.Config())
+	ccDepModule, _ := ccDep.(*Module)
+	isLLndk := ccDepModule != nil && ccDepModule.IsLlndk()
 	isVendorPublicLib := inList(libName, *vendorPublicLibraries)
 	bothVendorAndCoreVariantsExist := ccDep.HasVendorVariant() || isLLndk
 
@@ -2885,17 +2917,14 @@
 
 func GetMakeLinkType(actx android.ModuleContext, c LinkableInterface) string {
 	if c.UseVndk() {
-		if ccModule, ok := c.Module().(*Module); ok {
-			// Only CC modules provide stubs at the moment.
-			if lib, ok := ccModule.linker.(*llndkStubDecorator); ok {
-				if Bool(lib.Properties.Vendor_available) {
-					return "native:vndk"
-				}
+		if c.IsLlndk() {
+			if !c.IsLlndkPublic() {
 				return "native:vndk_private"
 			}
+			return "native:vndk"
 		}
 		if c.IsVndk() && !c.IsVndkExt() {
-			if c.IsVndkPrivate(actx.Config()) {
+			if c.IsVndkPrivate() {
 				return "native:vndk_private"
 			}
 			return "native:vndk"
@@ -2999,6 +3028,9 @@
 	}
 }
 
+var _ android.ApexModule = (*Module)(nil)
+
+// Implements android.ApexModule
 func (c *Module) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
 	depTag := ctx.OtherModuleDependencyTag(dep)
 	libDepTag, isLibDepTag := depTag.(libraryDependencyTag)
@@ -3025,13 +3057,14 @@
 			return false
 		}
 	}
-	if depTag == stubImplDepTag || depTag == llndkImplDep {
+	if depTag == stubImplDepTag || depTag == llndkStubDepTag {
 		// We don't track beyond LLNDK or from an implementation library to its stubs.
 		return false
 	}
 	return true
 }
 
+// Implements android.ApexModule
 func (c *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
 	sdkVersion android.ApiLevel) error {
 	// We ignore libclang_rt.* prebuilt libs since they declare sdk_version: 14(b/121358700)
diff --git a/cc/cc_test.go b/cc/cc_test.go
index c16cce8..fe9db37 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -1049,6 +1049,16 @@
 		name: "obj",
 		vendor_available: true,
 	}
+
+	cc_library {
+		name: "libllndk",
+		llndk_stubs: "libllndk.llndk",
+	}
+
+	llndk_library {
+		name: "libllndk.llndk",
+		symbol_file: "",
+	}
 `
 	config := TestConfig(buildDir, android.Android, nil, bp, nil)
 	config.TestProductVariables.DeviceVndkVersion = StringPtr("current")
@@ -1080,6 +1090,9 @@
 			filepath.Join(sharedDir, "libvendor.so.json"),
 			filepath.Join(sharedDir, "libvendor_available.so.json"))
 
+		// LLNDK modules are not captured
+		checkSnapshotExclude(t, ctx, snapshotSingleton, "libllndk", "libllndk.so", sharedDir, sharedVariant)
+
 		// For static libraries, all vendor:true and vendor_available modules (including VNDK) are captured.
 		// Also cfi variants are captured, except for prebuilts like toolchain_library
 		staticVariant := fmt.Sprintf("android_vendor.VER_%s_%s_static", archType, archVariant)
@@ -2899,7 +2912,7 @@
 		{vendorVariant, "libvndkprivate", "native:vndk_private"},
 		{vendorVariant, "libvendor", "native:vendor"},
 		{vendorVariant, "libvndkext", "native:vendor"},
-		{vendorVariant, "libllndk.llndk", "native:vndk"},
+		{vendorVariant, "libllndk", "native:vndk"},
 		{vendorVariant27, "prevndk.vndk.27.arm.binder32", "native:vndk"},
 		{coreVariant, "libvndk", "native:platform"},
 		{coreVariant, "libvndkprivate", "native:platform"},
@@ -3178,8 +3191,39 @@
 	llndk_library {
 		name: "libllndk.llndk",
 	}
+
+	cc_prebuilt_library_shared {
+		name: "libllndkprebuilt",
+		stubs: { versions: ["1", "2"] },
+		llndk_stubs: "libllndkprebuilt.llndk",
+	}
+	llndk_library {
+		name: "libllndkprebuilt.llndk",
+	}
+
+	cc_library {
+		name: "libllndk_with_external_headers",
+		stubs: { versions: ["1", "2"] },
+		llndk_stubs: "libllndk_with_external_headers.llndk",
+		header_libs: ["libexternal_headers"],
+		export_header_lib_headers: ["libexternal_headers"],
+	}
+	llndk_library {
+		name: "libllndk_with_external_headers.llndk",
+	}
+	cc_library_headers {
+		name: "libexternal_headers",
+		export_include_dirs: ["include"],
+		vendor_available: true,
+	}
 	`)
-	actual := ctx.ModuleVariantsForTests("libllndk.llndk")
+	actual := ctx.ModuleVariantsForTests("libllndk")
+	for i := 0; i < len(actual); i++ {
+		if !strings.HasPrefix(actual[i], "android_vendor.VER_") {
+			actual = append(actual[:i], actual[i+1:]...)
+			i--
+		}
+	}
 	expected := []string{
 		"android_vendor.VER_arm64_armv8-a_shared_1",
 		"android_vendor.VER_arm64_armv8-a_shared_2",
@@ -3190,10 +3234,10 @@
 	}
 	checkEquals(t, "variants for llndk stubs", expected, actual)
 
-	params := ctx.ModuleForTests("libllndk.llndk", "android_vendor.VER_arm_armv7-a-neon_shared").Description("generate stub")
+	params := ctx.ModuleForTests("libllndk", "android_vendor.VER_arm_armv7-a-neon_shared").Description("generate stub")
 	checkEquals(t, "use VNDK version for default stubs", "current", params.Args["apiLevel"])
 
-	params = ctx.ModuleForTests("libllndk.llndk", "android_vendor.VER_arm_armv7-a-neon_shared_1").Description("generate stub")
+	params = ctx.ModuleForTests("libllndk", "android_vendor.VER_arm_armv7-a-neon_shared_1").Description("generate stub")
 	checkEquals(t, "override apiLevel for versioned stubs", "1", params.Args["apiLevel"])
 }
 
diff --git a/cc/image.go b/cc/image.go
index 32325b9..380c1db 100644
--- a/cc/image.go
+++ b/cc/image.go
@@ -308,6 +308,18 @@
 		} else {
 			vendorVariants = append(vendorVariants, platformVndkVersion)
 		}
+	} else if lib := moduleLibraryInterface(m); lib != nil && lib.hasLLNDKStubs() {
+		// This is an LLNDK library.  The implementation of the library will be on /system,
+		// and vendor and product variants will be created with LLNDK stubs.
+		coreVariantNeeded = true
+		vendorVariants = append(vendorVariants,
+			platformVndkVersion,
+			boardVndkVersion,
+		)
+		productVariants = append(productVariants,
+			platformVndkVersion,
+			productVndkVersion,
+		)
 	} else {
 		// This is either in /system (or similar: /data), or is a
 		// modules built with the NDK. Modules built with the NDK
@@ -454,7 +466,7 @@
 		vndkVersion := ctx.DeviceConfig().VndkVersion()
 		if vndkVersion != "current" && vndkVersion != "" && vndkVersion != m.Properties.VndkVersion {
 			m.Properties.HideFromMake = true
-			m.SkipInstall()
+			m.HideFromMake()
 		}
 	} else if strings.HasPrefix(variant, ProductVariationPrefix) {
 		m.Properties.ImageVariationPrefix = ProductVariationPrefix
diff --git a/cc/library.go b/cc/library.go
index 959d670..1d0c018 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -22,6 +22,7 @@
 	"strings"
 	"sync"
 
+	"github.com/google/blueprint"
 	"github.com/google/blueprint/pathtools"
 
 	"android/soong/android"
@@ -114,6 +115,10 @@
 
 	// If this is an LLNDK library, the name of the equivalent llndk_library module.
 	Llndk_stubs *string
+
+	// If this is an LLNDK library, properties to describe the LLNDK stubs.  Will be copied from
+	// the module pointed to by llndk_stubs if it is set.
+	Llndk llndkLibraryProperties
 }
 
 // StaticProperties is a properties stanza to affect only attributes of the "static" variants of a
@@ -570,6 +575,12 @@
 	}
 
 	flags = library.baseCompiler.compilerFlags(ctx, flags, deps)
+	if ctx.IsLlndk() {
+		// LLNDK libraries ignore most of the properties on the cc_library and use the
+		// LLNDK-specific properties instead.
+		// Wipe all the module-local properties, leaving only the global properties.
+		flags.Local = LocalOrGlobalFlags{}
+	}
 	if library.buildStubs() {
 		// Remove -include <file> when compiling stubs. Otherwise, the force included
 		// headers might cause conflicting types error with the symbols in the
@@ -603,6 +614,22 @@
 }
 
 func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
+	if ctx.IsLlndk() {
+		// This is the vendor variant of an LLNDK library, build the LLNDK stubs.
+		vndkVer := ctx.Module().(*Module).VndkVersion()
+		if !inList(vndkVer, ctx.Config().PlatformVersionActiveCodenames()) || vndkVer == "" {
+			// For non-enforcing devices, vndkVer is empty. Use "current" in that case, too.
+			vndkVer = "current"
+		}
+		if library.stubsVersion() != "" {
+			vndkVer = library.stubsVersion()
+		}
+		objs, versionScript := compileStubLibrary(ctx, flags, String(library.Properties.Llndk.Symbol_file), vndkVer, "--llndk")
+		if !Bool(library.Properties.Llndk.Unversioned) {
+			library.versionScriptPath = android.OptionalPathForPath(versionScript)
+		}
+		return objs
+	}
 	if library.buildStubs() {
 		objs, versionScript := compileStubLibrary(ctx, flags, String(library.Properties.Stubs.Symbol_file), library.MutatedProperties.StubsVersion, "--apex")
 		library.versionScriptPath = android.OptionalPathForPath(versionScript)
@@ -693,6 +720,7 @@
 	allStubsVersions() []string
 
 	implementationModuleName(name string) string
+	hasLLNDKStubs() bool
 }
 
 var _ libraryInterface = (*libraryDecorator)(nil)
@@ -768,12 +796,27 @@
 }
 
 func (library *libraryDecorator) compilerDeps(ctx DepsContext, deps Deps) Deps {
+	if ctx.IsLlndk() {
+		// LLNDK libraries ignore most of the properties on the cc_library and use the
+		// LLNDK-specific properties instead.
+		return deps
+	}
+
 	deps = library.baseCompiler.compilerDeps(ctx, deps)
 
 	return deps
 }
 
 func (library *libraryDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps {
+	if ctx.IsLlndk() {
+		// LLNDK libraries ignore most of the properties on the cc_library and use the
+		// LLNDK-specific properties instead.
+		deps.HeaderLibs = append(deps.HeaderLibs, library.Properties.Llndk.Export_llndk_headers...)
+		deps.ReexportHeaderLibHeaders = append(deps.ReexportHeaderLibHeaders,
+			library.Properties.Llndk.Export_llndk_headers...)
+		return deps
+	}
+
 	if library.static() {
 		// Compare with nil because an empty list needs to be propagated.
 		if library.StaticProperties.Static.System_shared_libs != nil {
@@ -977,7 +1020,12 @@
 	transformSharedObjectToToc(ctx, outputFile, tocFile, builderFlags)
 
 	stripFlags := flagsToStripFlags(flags)
-	if library.stripper.NeedsStrip(ctx) {
+	needsStrip := library.stripper.NeedsStrip(ctx)
+	if library.buildStubs() {
+		// No need to strip stubs libraries
+		needsStrip = false
+	}
+	if needsStrip {
 		if ctx.Darwin() {
 			stripFlags.StripUseGnuStrip = true
 		}
@@ -1017,7 +1065,7 @@
 	linkerDeps = append(linkerDeps, deps.LateSharedLibsDeps...)
 	linkerDeps = append(linkerDeps, objs.tidyFiles...)
 
-	if Bool(library.Properties.Sort_bss_symbols_by_size) {
+	if Bool(library.Properties.Sort_bss_symbols_by_size) && !library.buildStubs() {
 		unsortedOutputFile := android.PathForModuleOut(ctx, "unsorted", fileName)
 		transformObjToDynamicBinary(ctx, objs.objFiles, sharedLibs,
 			deps.StaticLibs, deps.LateStaticLibs, deps.WholeStaticLibs,
@@ -1071,7 +1119,7 @@
 		ctx.SetProvider(SharedLibraryStubsProvider, SharedLibraryStubsInfo{
 			SharedStubLibraries: stubsInfo,
 
-			IsLLNDK: ctx.isLlndk(ctx.Config()),
+			IsLLNDK: ctx.IsLlndk(),
 		})
 	}
 
@@ -1100,7 +1148,7 @@
 func getRefAbiDumpFile(ctx ModuleContext, vndkVersion, fileName string) android.Path {
 	// The logic must be consistent with classifySourceAbiDump.
 	isNdk := ctx.isNdk(ctx.Config())
-	isLlndkOrVndk := ctx.isLlndkPublic(ctx.Config()) || (ctx.useVndk() && ctx.isVndk())
+	isLlndkOrVndk := ctx.IsLlndkPublic() || (ctx.useVndk() && ctx.isVndk())
 
 	refAbiDumpTextFile := android.PathForVndkRefAbiDump(ctx, vndkVersion, fileName, isNdk, isLlndkOrVndk, false)
 	refAbiDumpGzipFile := android.PathForVndkRefAbiDump(ctx, vndkVersion, fileName, isNdk, isLlndkOrVndk, true)
@@ -1153,17 +1201,64 @@
 			library.sAbiDiff = sourceAbiDiff(ctx, library.sAbiOutputFile.Path(),
 				refAbiDumpFile, fileName, exportedHeaderFlags,
 				Bool(library.Properties.Header_abi_checker.Check_all_apis),
-				ctx.isLlndk(ctx.Config()), ctx.isNdk(ctx.Config()), ctx.IsVndkExt())
+				ctx.IsLlndk(), ctx.isNdk(ctx.Config()), ctx.IsVndkExt())
 		}
 	}
 }
 
+func processLLNDKHeaders(ctx ModuleContext, srcHeaderDir string, outDir android.ModuleGenPath) android.Path {
+	srcDir := android.PathForModuleSrc(ctx, srcHeaderDir)
+	srcFiles := ctx.GlobFiles(filepath.Join(srcDir.String(), "**/*.h"), nil)
+
+	var installPaths []android.WritablePath
+	for _, header := range srcFiles {
+		headerDir := filepath.Dir(header.String())
+		relHeaderDir, err := filepath.Rel(srcDir.String(), headerDir)
+		if err != nil {
+			ctx.ModuleErrorf("filepath.Rel(%q, %q) failed: %s",
+				srcDir.String(), headerDir, err)
+			continue
+		}
+
+		installPaths = append(installPaths, outDir.Join(ctx, relHeaderDir, header.Base()))
+	}
+
+	return processHeadersWithVersioner(ctx, srcDir, outDir, srcFiles, installPaths)
+}
+
 // link registers actions to link this library, and sets various fields
 // on this library to reflect information that should be exported up the build
 // tree (for example, exported flags and include paths).
 func (library *libraryDecorator) link(ctx ModuleContext,
 	flags Flags, deps PathDeps, objs Objects) android.Path {
 
+	if ctx.IsLlndk() {
+		if len(library.Properties.Llndk.Export_preprocessed_headers) > 0 {
+			// This is the vendor variant of an LLNDK library with preprocessed headers.
+			genHeaderOutDir := android.PathForModuleGen(ctx, "include")
+
+			var timestampFiles android.Paths
+			for _, dir := range library.Properties.Llndk.Export_preprocessed_headers {
+				timestampFiles = append(timestampFiles, processLLNDKHeaders(ctx, dir, genHeaderOutDir))
+			}
+
+			if Bool(library.Properties.Llndk.Export_headers_as_system) {
+				library.reexportSystemDirs(genHeaderOutDir)
+			} else {
+				library.reexportDirs(genHeaderOutDir)
+			}
+
+			library.reexportDeps(timestampFiles...)
+		}
+
+		if Bool(library.Properties.Llndk.Export_headers_as_system) {
+			library.flagExporter.Properties.Export_system_include_dirs = append(
+				library.flagExporter.Properties.Export_system_include_dirs,
+				library.flagExporter.Properties.Export_include_dirs...)
+			library.flagExporter.Properties.Export_include_dirs = nil
+		}
+	}
+
 	// Linking this library consists of linking `deps.Objs` (.o files in dependencies
 	// of this library), together with `objs` (.o files created by compiling this
 	// library).
@@ -1246,7 +1341,7 @@
 }
 
 func (library *libraryDecorator) exportVersioningMacroIfNeeded(ctx android.BaseModuleContext) {
-	if library.buildStubs() && !library.skipAPIDefine {
+	if library.buildStubs() && library.stubsVersion() != "" && !library.skipAPIDefine {
 		name := versioningMacroName(ctx.Module().(*Module).ImplementationModuleName(ctx))
 		ver := library.stubsVersion()
 		library.reexportFlags("-D" + name + "=" + ver)
@@ -1334,9 +1429,9 @@
 				}
 				library.baseInstaller.subDir = "bootstrap"
 			}
-		} else if ctx.directlyInAnyApex() && ctx.isLlndk(ctx.Config()) && !isBionic(ctx.baseModuleName()) {
+		} else if ctx.directlyInAnyApex() && ctx.IsLlndk() && !isBionic(ctx.baseModuleName()) {
 			// Skip installing LLNDK (non-bionic) libraries moved to APEX.
-			ctx.Module().SkipInstall()
+			ctx.Module().HideFromMake()
 		}
 
 		library.baseInstaller.install(ctx, file)
@@ -1345,7 +1440,7 @@
 	if Bool(library.Properties.Static_ndk_lib) && library.static() &&
 		!ctx.useVndk() && !ctx.inRamdisk() && !ctx.inVendorRamdisk() && !ctx.inRecovery() && ctx.Device() &&
 		library.baseLinker.sanitize.isUnsanitizedVariant() &&
-		!library.buildStubs() && ctx.sdkVersion() == "" {
+		ctx.isForPlatform() && !ctx.isPreventInstall() {
 		installPath := getNdkSysrootBase(ctx).Join(
 			ctx, "usr/lib", config.NDKTriple(ctx.toolchain()), file.Base())
 
@@ -1411,6 +1506,11 @@
 	library.MutatedProperties.BuildStatic = false
 }
 
+// hasLLNDKStubs returns true if this cc_library module has a variant that will build LLNDK stubs.
+func (library *libraryDecorator) hasLLNDKStubs() bool {
+	return String(library.Properties.Llndk_stubs) != ""
+}
+
 func (library *libraryDecorator) implementationModuleName(name string) string {
 	return name
 }
@@ -1423,6 +1523,9 @@
 	if library.Properties.Header_abi_checker.Symbol_file != nil {
 		return library.Properties.Header_abi_checker.Symbol_file
 	}
+	if ctx.Module().(*Module).IsLlndk() {
+		return library.Properties.Llndk.Symbol_file
+	}
 	if library.hasStubsVariants() && library.Properties.Stubs.Symbol_file != nil {
 		return library.Properties.Stubs.Symbol_file
 	}
@@ -1587,7 +1690,9 @@
 		library := mctx.Module().(*Module).linker.(prebuiltLibraryInterface)
 
 		// Differentiate between header only and building an actual static/shared library
-		if library.buildStatic() || library.buildShared() {
+		buildStatic := library.buildStatic()
+		buildShared := library.buildShared()
+		if buildStatic || buildShared {
 			// Always create both the static and shared variants for prebuilt libraries, and then disable the one
 			// that is not being used.  This allows them to share the name of a cc_library module, which requires that
 			// all the variants of the cc_library also exist on the prebuilt.
@@ -1598,16 +1703,16 @@
 			static.linker.(prebuiltLibraryInterface).setStatic()
 			shared.linker.(prebuiltLibraryInterface).setShared()
 
-			if library.buildShared() {
+			if buildShared {
 				mctx.AliasVariation("shared")
-			} else if library.buildStatic() {
+			} else if buildStatic {
 				mctx.AliasVariation("static")
 			}
 
-			if !library.buildStatic() {
+			if !buildStatic {
 				static.linker.(prebuiltLibraryInterface).disablePrebuilt()
 			}
-			if !library.buildShared() {
+			if !buildShared {
 				shared.linker.(prebuiltLibraryInterface).disablePrebuilt()
 			}
 		} else {
@@ -1622,7 +1727,18 @@
 			variations = append(variations, "")
 		}
 
-		if library.BuildStaticVariant() && library.BuildSharedVariant() {
+		isLLNDK := false
+		if m, ok := mctx.Module().(*Module); ok {
+			isLLNDK = m.IsLlndk()
+			// Don't count the vestigial llndk_library module as isLLNDK, it needs a static
+			// variant so that a cc_library_prebuilt can depend on it.
+			if _, ok := m.linker.(*llndkStubDecorator); ok {
+				isLLNDK = false
+			}
+		}
+		buildStatic := library.BuildStaticVariant() && !isLLNDK
+		buildShared := library.BuildSharedVariant()
+		if buildStatic && buildShared {
 			variations := append([]string{"static", "shared"}, variations...)
 
 			modules := mctx.CreateLocalVariations(variations...)
@@ -1636,13 +1752,13 @@
 				reuseStaticLibrary(mctx, static.(*Module), shared.(*Module))
 			}
 			mctx.AliasVariation("shared")
-		} else if library.BuildStaticVariant() {
+		} else if buildStatic {
 			variations := append([]string{"static"}, variations...)
 
 			modules := mctx.CreateLocalVariations(variations...)
 			modules[0].(LinkableInterface).SetStatic()
 			mctx.AliasVariation("static")
-		} else if library.BuildSharedVariant() {
+		} else if buildShared {
 			variations := append([]string{"shared"}, variations...)
 
 			modules := mctx.CreateLocalVariations(variations...)
@@ -1675,22 +1791,32 @@
 }
 
 func createVersionVariations(mctx android.BottomUpMutatorContext, versions []string) {
-	// "" is for the non-stubs (implementation) variant.
+	// "" is for the non-stubs (implementation) variant for system modules, or the LLNDK variant
+	// for LLNDK modules.
 	variants := append(android.CopyOf(versions), "")
 
+	m := mctx.Module().(*Module)
+	isLLNDK := m.IsLlndk()
+
 	modules := mctx.CreateLocalVariations(variants...)
 	for i, m := range modules {
-		if variants[i] != "" {
+
+		if variants[i] != "" || isLLNDK {
+			// A stubs or LLNDK stubs variant.
 			c := m.(*Module)
-			c.Properties.HideFromMake = true
 			c.sanitize = nil
 			c.stl = nil
 			c.Properties.PreventInstall = true
 			lib := moduleLibraryInterface(m)
 			lib.setBuildStubs()
-			lib.setStubsVersion(variants[i])
-			// The implementation depends on the stubs
-			mctx.AddInterVariantDependency(stubImplDepTag, modules[len(modules)-1], modules[i])
+
+			if variants[i] != "" {
+				// A non-LLNDK stubs module is hidden from make and has a dependency from the
+				// implementation module to the stubs module.
+				c.Properties.HideFromMake = true
+				lib.setStubsVersion(variants[i])
+				mctx.AddInterVariantDependency(stubImplDepTag, modules[len(modules)-1], modules[i])
+			}
 		}
 	}
 	mctx.AliasVariation("")
@@ -1737,7 +1863,7 @@
 		module.CcLibraryInterface() && module.Shared()
 }
 
-func moduleLibraryInterface(module android.Module) libraryInterface {
+func moduleLibraryInterface(module blueprint.Module) libraryInterface {
 	if m, ok := module.(*Module); ok {
 		return m.library
 	}
@@ -1758,7 +1884,15 @@
 				// Set the versions on the pre-mutated module so they can be read by any llndk modules that
 				// depend on the implementation library and haven't been mutated yet.
 				library.setAllStubsVersions(versions)
-				return
+			}
+
+			if mctx.Module().(*Module).UseVndk() && library.hasLLNDKStubs() {
+				// Propagate the version to the llndk stubs module.
+				mctx.VisitDirectDepsWithTag(llndkStubDepTag, func(stubs android.Module) {
+					if stubsLib := moduleLibraryInterface(stubs); stubsLib != nil {
+						stubsLib.setAllStubsVersions(library.allStubsVersions())
+					}
+				})
 			}
 		}
 	}
diff --git a/cc/linkable.go b/cc/linkable.go
index d010985..489063f 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -43,9 +43,11 @@
 	UseSdk() bool
 	UseVndk() bool
 	MustUseVendorVariant() bool
+	IsLlndk() bool
+	IsLlndkPublic() bool
 	IsVndk() bool
 	IsVndkExt() bool
-	IsVndkPrivate(config android.Config) bool
+	IsVndkPrivate() bool
 	HasVendorVariant() bool
 	InProduct() bool
 
diff --git a/cc/llndk_library.go b/cc/llndk_library.go
index 212720b..d0fbc48 100644
--- a/cc/llndk_library.go
+++ b/cc/llndk_library.go
@@ -15,21 +15,21 @@
 package cc
 
 import (
-	"fmt"
-	"path/filepath"
 	"strings"
 
 	"android/soong/android"
 )
 
-var llndkImplDep = dependencyTag{name: "llndk impl"}
-
 var (
 	llndkLibrarySuffix = ".llndk"
 	llndkHeadersSuffix = ".llndk"
 )
 
-// Creates a stub shared library based on the provided version file.
+// Holds properties to describe a stub shared library based on the provided version file.
+// The stub library will actually be built by the cc_library module that points to this
+// module with the llndk_stubs property.
+// TODO(ccross): move the properties from llndk_library modules directly into the cc_library
+//  modules and remove the llndk_library modules.
 //
 // Example:
 //
@@ -64,43 +64,32 @@
 
 	// list of llndk headers to re-export include directories from.
 	Export_llndk_headers []string `android:"arch_variant"`
+
+	// whether this module can be directly depended upon by libs that are installed
+	// to /vendor and /product.
+	// When set to true, this module can only be depended on by VNDK libraries, not
+	// vendor nor product libraries. This effectively hides this module from
+	// non-system modules. Default value is false.
+	Private *bool
 }
 
 type llndkStubDecorator struct {
 	*libraryDecorator
 
 	Properties llndkLibraryProperties
-
-	movedToApex bool
 }
 
 var _ versionedInterface = (*llndkStubDecorator)(nil)
 
 func (stub *llndkStubDecorator) compilerFlags(ctx ModuleContext, flags Flags, deps PathDeps) Flags {
-	flags = stub.baseCompiler.compilerFlags(ctx, flags, deps)
-	return addStubLibraryCompilerFlags(flags)
+	return flags
 }
 
 func (stub *llndkStubDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
-	vndkVer := ctx.Module().(*Module).VndkVersion()
-	if !inList(vndkVer, ctx.Config().PlatformVersionActiveCodenames()) || vndkVer == "" {
-		// For non-enforcing devices, vndkVer is empty. Use "current" in that case, too.
-		vndkVer = "current"
-	}
-	if stub.stubsVersion() != "" {
-		vndkVer = stub.stubsVersion()
-	}
-	objs, versionScript := compileStubLibrary(ctx, flags, String(stub.Properties.Symbol_file), vndkVer, "--llndk")
-	if !Bool(stub.Properties.Unversioned) {
-		stub.versionScriptPath = android.OptionalPathForPath(versionScript)
-	}
-	return objs
+	return Objects{}
 }
 
 func (stub *llndkStubDecorator) linkerDeps(ctx DepsContext, deps Deps) Deps {
-	headers := addSuffix(stub.Properties.Export_llndk_headers, llndkHeadersSuffix)
-	deps.HeaderLibs = append(deps.HeaderLibs, headers...)
-	deps.ReexportHeaderLibHeaders = append(deps.ReexportHeaderLibHeaders, headers...)
 	return deps
 }
 
@@ -116,57 +105,9 @@
 	return stub.libraryDecorator.linkerFlags(ctx, flags)
 }
 
-func (stub *llndkStubDecorator) processHeaders(ctx ModuleContext, srcHeaderDir string, outDir android.ModuleGenPath) android.Path {
-	srcDir := android.PathForModuleSrc(ctx, srcHeaderDir)
-	srcFiles := ctx.GlobFiles(filepath.Join(srcDir.String(), "**/*.h"), nil)
-
-	var installPaths []android.WritablePath
-	for _, header := range srcFiles {
-		headerDir := filepath.Dir(header.String())
-		relHeaderDir, err := filepath.Rel(srcDir.String(), headerDir)
-		if err != nil {
-			ctx.ModuleErrorf("filepath.Rel(%q, %q) failed: %s",
-				srcDir.String(), headerDir, err)
-			continue
-		}
-
-		installPaths = append(installPaths, outDir.Join(ctx, relHeaderDir, header.Base()))
-	}
-
-	return processHeadersWithVersioner(ctx, srcDir, outDir, srcFiles, installPaths)
-}
-
 func (stub *llndkStubDecorator) link(ctx ModuleContext, flags Flags, deps PathDeps,
 	objs Objects) android.Path {
-
-	impl := ctx.GetDirectDepWithTag(stub.implementationModuleName(ctx.ModuleName()), llndkImplDep)
-	if implApexModule, ok := impl.(android.ApexModule); ok {
-		stub.movedToApex = implApexModule.DirectlyInAnyApex()
-	}
-
-	if len(stub.Properties.Export_preprocessed_headers) > 0 {
-		genHeaderOutDir := android.PathForModuleGen(ctx, "include")
-
-		var timestampFiles android.Paths
-		for _, dir := range stub.Properties.Export_preprocessed_headers {
-			timestampFiles = append(timestampFiles, stub.processHeaders(ctx, dir, genHeaderOutDir))
-		}
-
-		if Bool(stub.Properties.Export_headers_as_system) {
-			stub.reexportSystemDirs(genHeaderOutDir)
-		} else {
-			stub.reexportDirs(genHeaderOutDir)
-		}
-
-		stub.reexportDeps(timestampFiles...)
-	}
-
-	if Bool(stub.Properties.Export_headers_as_system) {
-		stub.exportIncludesAsSystem(ctx)
-		stub.libraryDecorator.flagExporter.Properties.Export_include_dirs = []string{}
-	}
-
-	return stub.libraryDecorator.link(ctx, flags, deps, objs)
+	return nil
 }
 
 func (stub *llndkStubDecorator) nativeCoverage() bool {
@@ -181,20 +122,8 @@
 	return true
 }
 
-func (stub *llndkStubDecorator) stubsVersions(ctx android.BaseMutatorContext) []string {
-	// Get the versions from the implementation module.
-	impls := ctx.GetDirectDepsWithTag(llndkImplDep)
-	if len(impls) > 1 {
-		panic(fmt.Errorf("Expected single implmenetation library, got %d", len(impls)))
-	} else if len(impls) == 1 {
-		return moduleLibraryInterface(impls[0]).allStubsVersions()
-	}
-	return nil
-}
-
 func NewLLndkStubLibrary() *Module {
 	module, library := NewLibrary(android.DeviceSupported)
-	library.BuildOnlyShared()
 	module.stl = nil
 	module.sanitize = nil
 	library.disableStripping()
@@ -235,10 +164,6 @@
 	*libraryDecorator
 }
 
-func (headers *llndkHeadersDecorator) Name(name string) string {
-	return name + llndkHeadersSuffix
-}
-
 // llndk_headers contains a set of c/c++ llndk headers files which are imported
 // by other soongs cc modules.
 func llndkHeadersFactory() android.Module {
diff --git a/cc/sabi.go b/cc/sabi.go
index 99e718e..4a1ba3c 100644
--- a/cc/sabi.go
+++ b/cc/sabi.go
@@ -80,10 +80,10 @@
 	if m.IsNdk(ctx.Config()) {
 		return "NDK"
 	}
-	if m.isLlndkPublic(ctx.Config()) {
+	if m.isImplementationForLLNDKPublic() {
 		return "LLNDK"
 	}
-	if m.UseVndk() && m.IsVndk() && !m.IsVndkPrivate(ctx.Config()) {
+	if m.UseVndk() && m.IsVndk() && !m.IsVndkPrivate() {
 		if m.isVndkSp() {
 			if m.IsVndkExt() {
 				return "VNDK-SP-ext"
@@ -131,7 +131,7 @@
 	// Module is shared library type.
 
 	// Don't check uninstallable modules.
-	if m.IsSkipInstall() {
+	if m.IsHideFromMake() {
 		return false
 	}
 
@@ -156,7 +156,7 @@
 	}
 
 	// Don't create ABI dump for stubs.
-	if m.isNDKStubLibrary() || m.IsStubs() {
+	if m.isNDKStubLibrary() || m.IsLlndk() || m.IsStubs() {
 		return false
 	}
 
diff --git a/cc/sanitize.go b/cc/sanitize.go
index 22ee25f..d7df5dc 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -1002,9 +1002,6 @@
 
 		if runtimeLibrary != "" && (toolchain.Bionic() || c.sanitize.Properties.UbsanRuntimeDep) {
 			// UBSan is supported on non-bionic linux host builds as well
-			if isLlndkLibrary(runtimeLibrary, mctx.Config()) && !c.static() && c.UseVndk() {
-				runtimeLibrary = runtimeLibrary + llndkLibrarySuffix
-			}
 
 			// Adding dependency to the runtime library. We are using *FarVariation*
 			// because the runtime libraries themselves are not mutated by sanitizer
diff --git a/cc/snapshot_prebuilt.go b/cc/snapshot_prebuilt.go
index b291bd0..419b7cf 100644
--- a/cc/snapshot_prebuilt.go
+++ b/cc/snapshot_prebuilt.go
@@ -805,7 +805,7 @@
 	}
 
 	// .. and also filter out llndk library
-	if module.isLlndk(ctx.Config()) {
+	if module.IsLlndk() {
 		return
 	}
 
@@ -836,7 +836,7 @@
 	// Disables source modules if corresponding snapshot exists.
 	if lib, ok := module.linker.(libraryInterface); ok && lib.buildStatic() && lib.buildShared() {
 		// But do not disable because the shared variant depends on the static variant.
-		module.SkipInstall()
+		module.HideFromMake()
 		module.Properties.HideFromMake = true
 	} else {
 		module.Disable()
diff --git a/cc/vendor_snapshot.go b/cc/vendor_snapshot.go
index da37d0f..d2c29d6 100644
--- a/cc/vendor_snapshot.go
+++ b/cc/vendor_snapshot.go
@@ -181,8 +181,8 @@
 		return false
 	}
 	// When android/prebuilt.go selects between source and prebuilt, it sets
-	// SkipInstall on the other one to avoid duplicate install rules in make.
-	if m.IsSkipInstall() {
+	// HideFromMake on the other one to avoid duplicate install rules in make.
+	if m.IsHideFromMake() {
 		return false
 	}
 	// skip proprietary modules, but (for the vendor snapshot only)
@@ -210,6 +210,9 @@
 		return false
 	}
 	// skip llndk_library and llndk_headers which are backward compatible
+	if m.IsLlndk() {
+		return false
+	}
 	if _, ok := m.linker.(*llndkStubDecorator); ok {
 		return false
 	}
diff --git a/cc/vndk.go b/cc/vndk.go
index 1529ac5..c1bce16 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -156,9 +156,15 @@
 	}
 	if lib, ok := to.linker.(*libraryDecorator); !ok || !lib.shared() {
 		// Check only shared libraries.
-		// Other (static and LL-NDK) libraries are allowed to link.
+		// Other (static) libraries are allowed to link.
 		return
 	}
+
+	if to.IsLlndk() {
+		// LL-NDK libraries are allowed to link
+		return
+	}
+
 	if !to.UseVndk() {
 		ctx.ModuleErrorf("(%s) should not link to %q which is not a vendor-available library",
 			vndk.typeName(), to.Name())
@@ -250,22 +256,12 @@
 	}).(map[string]string)
 }
 
-func isLlndkLibrary(baseModuleName string, config android.Config) bool {
-	_, ok := llndkLibraries(config)[strings.TrimSuffix(baseModuleName, llndkLibrarySuffix)]
-	return ok
-}
-
 func llndkLibraries(config android.Config) map[string]string {
 	return config.Once(llndkLibrariesKey, func() interface{} {
 		return make(map[string]string)
 	}).(map[string]string)
 }
 
-func isVndkPrivateLibrary(baseModuleName string, config android.Config) bool {
-	_, ok := vndkPrivateLibraries(config)[baseModuleName]
-	return ok
-}
-
 func vndkPrivateLibraries(config android.Config) map[string]string {
 	return config.Once(vndkPrivateLibrariesKey, func() interface{} {
 		return make(map[string]string)
@@ -301,12 +297,10 @@
 	defer vndkLibrariesLock.Unlock()
 
 	llndkLibraries(mctx.Config())[name] = filename
+	m.VendorProperties.IsLLNDK = true
 	if !Bool(lib.Properties.Vendor_available) {
 		vndkPrivateLibraries(mctx.Config())[name] = filename
-	}
-
-	if mctx.OtherModuleExists(name) {
-		mctx.AddFarVariationDependencies(m.Target().Variations(), llndkImplDep, name)
+		m.VendorProperties.IsLLNDKPrivate = true
 	}
 }
 
@@ -410,9 +404,31 @@
 		return
 	}
 
+	// This is a temporary measure to copy the properties from an llndk_library into the cc_library
+	// that will actually build the stubs.  It will be removed once the properties are moved into
+	// the cc_library in the Android.bp files.
+	mergeLLNDKToLib := func(llndk *Module, llndkProperties *llndkLibraryProperties, flagExporter *flagExporter) {
+		if llndkLib := moduleLibraryInterface(llndk); llndkLib != nil {
+			*llndkProperties = llndkLib.(*llndkStubDecorator).Properties
+			flagExporter.Properties = llndkLib.(*llndkStubDecorator).flagExporter.Properties
+
+			m.VendorProperties.IsLLNDK = llndk.VendorProperties.IsLLNDK
+			m.VendorProperties.IsLLNDKPrivate = llndk.VendorProperties.IsLLNDKPrivate
+		}
+	}
+
 	lib, is_lib := m.linker.(*libraryDecorator)
 	prebuilt_lib, is_prebuilt_lib := m.linker.(*prebuiltLibraryLinker)
 
+	if m.UseVndk() && is_lib && lib.hasLLNDKStubs() {
+		llndk := mctx.AddVariationDependencies(nil, llndkStubDepTag, String(lib.Properties.Llndk_stubs))
+		mergeLLNDKToLib(llndk[0].(*Module), &lib.Properties.Llndk, &lib.flagExporter)
+	}
+	if m.UseVndk() && is_prebuilt_lib && prebuilt_lib.hasLLNDKStubs() {
+		llndk := mctx.AddVariationDependencies(nil, llndkStubDepTag, String(prebuilt_lib.Properties.Llndk_stubs))
+		mergeLLNDKToLib(llndk[0].(*Module), &prebuilt_lib.Properties.Llndk, &prebuilt_lib.flagExporter)
+	}
+
 	if (is_lib && lib.buildShared()) || (is_prebuilt_lib && prebuilt_lib.buildShared()) {
 		if m.vndkdep != nil && m.vndkdep.isVndk() && !m.vndkdep.isVndkExt() {
 			processVndkLibrary(mctx, m)
@@ -819,13 +835,11 @@
 	// they been moved to an apex.
 	movedToApexLlndkLibraries := make(map[string]bool)
 	ctx.VisitAllModules(func(module android.Module) {
-		if m, ok := module.(*Module); ok {
-			if llndk, ok := m.linker.(*llndkStubDecorator); ok {
-				// Skip bionic libs, they are handled in different manner
-				name := llndk.implementationModuleName(m.BaseModuleName())
-				if llndk.movedToApex && !isBionic(name) {
-					movedToApexLlndkLibraries[name] = true
-				}
+		if library := moduleLibraryInterface(module); library != nil && library.hasLLNDKStubs() {
+			// Skip bionic libs, they are handled in different manner
+			name := library.implementationModuleName(module.(*Module).BaseModuleName())
+			if module.(android.ApexModule).DirectlyInAnyApex() && !isBionic(name) {
+				movedToApexLlndkLibraries[name] = true
 			}
 		}
 	})
diff --git a/cc/vndk_prebuilt.go b/cc/vndk_prebuilt.go
index e6e2ad8..04162cd 100644
--- a/cc/vndk_prebuilt.go
+++ b/cc/vndk_prebuilt.go
@@ -130,7 +130,7 @@
 	flags Flags, deps PathDeps, objs Objects) android.Path {
 
 	if !p.matchesWithDevice(ctx.DeviceConfig()) {
-		ctx.Module().SkipInstall()
+		ctx.Module().HideFromMake()
 		return nil
 	}
 
@@ -175,7 +175,7 @@
 		return in
 	}
 
-	ctx.Module().SkipInstall()
+	ctx.Module().HideFromMake()
 	return nil
 }
 
diff --git a/cmd/multiproduct_kati/main.go b/cmd/multiproduct_kati/main.go
index c079e83..7dd50f6 100644
--- a/cmd/multiproduct_kati/main.go
+++ b/cmd/multiproduct_kati/main.go
@@ -50,12 +50,30 @@
 
 var buildVariant = flag.String("variant", "eng", "build variant to use")
 
-var skipProducts = flag.String("skip-products", "", "comma-separated list of products to skip (known failures, etc)")
-var includeProducts = flag.String("products", "", "comma-separated list of products to build")
-
 var shardCount = flag.Int("shard-count", 1, "split the products into multiple shards (to spread the build onto multiple machines, etc)")
 var shard = flag.Int("shard", 1, "1-indexed shard to execute")
 
+var skipProducts multipleStringArg
+var includeProducts multipleStringArg
+
+func init() {
+	flag.Var(&skipProducts, "skip-products", "comma-separated list of products to skip (known failures, etc)")
+	flag.Var(&includeProducts, "products", "comma-separated list of products to build")
+}
+
+// multipleStringArg is a flag.Value that takes comma separated lists and converts them to a
+// []string.  The argument can be passed multiple times to append more values.
+type multipleStringArg []string
+
+func (m *multipleStringArg) String() string {
+	return strings.Join(*m, `, `)
+}
+
+func (m *multipleStringArg) Set(s string) error {
+	*m = append(*m, strings.Split(s, ",")...)
+	return nil
+}
+
 const errorLeadingLines = 20
 const errorTrailingLines = 20
 
@@ -250,9 +268,9 @@
 	var productsList []string
 	allProducts := strings.Fields(vars["all_named_products"])
 
-	if *includeProducts != "" {
-		missingProducts := []string{}
-		for _, product := range strings.Split(*includeProducts, ",") {
+	if len(includeProducts) > 0 {
+		var missingProducts []string
+		for _, product := range includeProducts {
 			if inList(product, allProducts) {
 				productsList = append(productsList, product)
 			} else {
@@ -267,9 +285,8 @@
 	}
 
 	finalProductsList := make([]string, 0, len(productsList))
-	skipList := strings.Split(*skipProducts, ",")
 	skipProduct := func(p string) bool {
-		for _, s := range skipList {
+		for _, s := range skipProducts {
 			if p == s {
 				return true
 			}
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index bd1d450..74ede68 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -174,7 +174,7 @@
 
 	build.SetupOutDir(buildCtx, config)
 
-	if config.UseBazel() {
+	if config.UseBazel() && config.Dist() {
 		defer populateExternalDistDir(buildCtx, config)
 	}
 
@@ -547,6 +547,12 @@
 		return
 	}
 
+	// Make sure the internal DIST_DIR actually exists before trying to read from it
+	if _, err = os.Stat(internalDistDirPath); os.IsNotExist(err) {
+		ctx.Println("Skipping Bazel dist dir migration - nothing to do!")
+		return
+	}
+
 	// Make sure the external DIST_DIR actually exists before trying to write to it
 	if err = os.MkdirAll(externalDistDirPath, 0755); err != nil {
 		ctx.Fatalf("Unable to make directory %s: %s", externalDistDirPath, err)
diff --git a/dexpreopt/class_loader_context.go b/dexpreopt/class_loader_context.go
index deaf77f..874edca 100644
--- a/dexpreopt/class_loader_context.go
+++ b/dexpreopt/class_loader_context.go
@@ -335,11 +335,14 @@
 	}
 }
 
-// Add class loader context for the given SDK version. Fail on unknown build/install paths.
+// Add class loader context for the given SDK version. Don't fail on unknown build/install paths, as
+// libraries with unknown paths still need to be processed by manifest_fixer (which doesn't care
+// about paths). For the subset of libraries that are used in dexpreopt, their build/install paths
+// are validated later before CLC is used (in validateClassLoaderContext).
 func (clcMap ClassLoaderContextMap) AddContextForSdk(ctx android.ModuleInstallPathContext, sdkVer int,
 	lib string, hostPath, installPath android.Path, nestedClcMap ClassLoaderContextMap) {
 
-	clcMap.addContextOrReportError(ctx, sdkVer, lib, hostPath, installPath, true, nestedClcMap)
+	clcMap.addContextOrReportError(ctx, sdkVer, lib, hostPath, installPath, false, nestedClcMap)
 }
 
 // Merge the other class loader context map into this one, do not override existing entries.
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 9054017..8df32f2 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -139,7 +139,6 @@
 	// number of shards the input files are sharded into.
 	taskGenerator taskFunc
 
-	deps        android.Paths
 	rule        blueprint.Rule
 	rawCommands []string
 
@@ -244,6 +243,8 @@
 		}
 	}
 
+	var tools android.Paths
+	var packagedTools []android.PackagingSpec
 	if len(g.properties.Tools) > 0 {
 		seenTools := make(map[string]bool)
 
@@ -251,37 +252,52 @@
 			switch tag := ctx.OtherModuleDependencyTag(module).(type) {
 			case hostToolDependencyTag:
 				tool := ctx.OtherModuleName(module)
-				var path android.OptionalPath
 
-				if t, ok := module.(android.HostToolProvider); ok {
+				switch t := module.(type) {
+				case android.HostToolProvider:
+					// A HostToolProvider provides the path to a tool, which will be copied
+					// into the sandbox.
 					if !t.(android.Module).Enabled() {
 						if ctx.Config().AllowMissingDependencies() {
 							ctx.AddMissingDependencies([]string{tool})
 						} else {
 							ctx.ModuleErrorf("depends on disabled module %q", tool)
 						}
-						break
+						return
 					}
-					path = t.HostToolPath()
-				} else if t, ok := module.(bootstrap.GoBinaryTool); ok {
+					path := t.HostToolPath()
+					if !path.Valid() {
+						ctx.ModuleErrorf("host tool %q missing output file", tool)
+						return
+					}
+					if specs := t.TransitivePackagingSpecs(); specs != nil {
+						// If the HostToolProvider has PackgingSpecs, which are definitions of the
+						// required relative locations of the tool and its dependencies, use those
+						// instead.  They will be copied to those relative locations in the sbox
+						// sandbox.
+						packagedTools = append(packagedTools, specs...)
+						// Assume that the first PackagingSpec of the module is the tool.
+						addLocationLabel(tag.label, []string{android.SboxPathForPackagedTool(specs[0])})
+					} else {
+						tools = append(tools, path.Path())
+						addLocationLabel(tag.label, []string{android.SboxPathForTool(ctx, path.Path())})
+					}
+				case bootstrap.GoBinaryTool:
+					// A GoBinaryTool provides the install path to a tool, which will be copied.
 					if s, err := filepath.Rel(android.PathForOutput(ctx).String(), t.InstallPath()); err == nil {
-						path = android.OptionalPathForPath(android.PathForOutput(ctx, s))
+						toolPath := android.PathForOutput(ctx, s)
+						tools = append(tools, toolPath)
+						addLocationLabel(tag.label, []string{android.SboxPathForTool(ctx, toolPath)})
 					} else {
 						ctx.ModuleErrorf("cannot find path for %q: %v", tool, err)
-						break
+						return
 					}
-				} else {
+				default:
 					ctx.ModuleErrorf("%q is not a host tool provider", tool)
-					break
+					return
 				}
 
-				if path.Valid() {
-					g.deps = append(g.deps, path.Path())
-					addLocationLabel(tag.label, []string{path.Path().String()})
-					seenTools[tag.label] = true
-				} else {
-					ctx.ModuleErrorf("host tool %q missing output file", tool)
-				}
+				seenTools[tag.label] = true
 			}
 		})
 
@@ -305,8 +321,12 @@
 
 	for _, toolFile := range g.properties.Tool_files {
 		paths := android.PathsForModuleSrc(ctx, []string{toolFile})
-		g.deps = append(g.deps, paths...)
-		addLocationLabel(toolFile, paths.Strings())
+		tools = append(tools, paths...)
+		var sandboxPaths []string
+		for _, path := range paths {
+			sandboxPaths = append(sandboxPaths, android.SboxPathForTool(ctx, path))
+		}
+		addLocationLabel(toolFile, sandboxPaths)
 	}
 
 	var srcFiles android.Paths
@@ -358,7 +378,7 @@
 		manifestPath := android.PathForModuleOut(ctx, manifestName)
 
 		// Use a RuleBuilder to create a rule that runs the command inside an sbox sandbox.
-		rule := android.NewRuleBuilder(pctx, ctx).Sbox(task.genDir, manifestPath)
+		rule := android.NewRuleBuilder(pctx, ctx).Sbox(task.genDir, manifestPath).SandboxTools()
 		cmd := rule.Command()
 
 		for _, out := range task.out {
@@ -448,8 +468,9 @@
 		cmd.Text(rawCommand)
 		cmd.ImplicitOutputs(task.out)
 		cmd.Implicits(task.in)
-		cmd.Implicits(g.deps)
-		cmd.Implicits(task.extraTools)
+		cmd.ImplicitTools(tools)
+		cmd.ImplicitTools(task.extraTools)
+		cmd.ImplicitPackagedTools(packagedTools)
 		if Bool(g.properties.Depfile) {
 			cmd.ImplicitDepFile(task.depFile)
 		}
@@ -547,6 +568,9 @@
 	}
 }
 
+var _ android.ApexModule = (*Module)(nil)
+
+// Implements android.ApexModule
 func (g *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
 	sdkVersion android.ApiLevel) error {
 	// Because generated outputs are checked by client modules(e.g. cc_library, ...)
@@ -616,7 +640,7 @@
 			// TODO(ccross): this RuleBuilder is a hack to be able to call
 			// rule.Command().PathForOutput.  Replace this with passing the rule into the
 			// generator.
-			rule := android.NewRuleBuilder(pctx, ctx).Sbox(genDir, nil)
+			rule := android.NewRuleBuilder(pctx, ctx).Sbox(genDir, nil).SandboxTools()
 
 			for _, in := range shard {
 				outFile := android.GenPathWithExt(ctx, finalSubDir, in, String(properties.Output_extension))
@@ -669,7 +693,8 @@
 				outputDepfile = android.PathForModuleGen(ctx, genSubDir, "gensrcs.d")
 				depFixerTool := ctx.Config().HostToolPath(ctx, "dep_fixer")
 				fullCommand += fmt.Sprintf(" && %s -o $(depfile) %s",
-					depFixerTool.String(), strings.Join(commandDepFiles, " "))
+					android.SboxPathForTool(ctx, depFixerTool),
+					strings.Join(commandDepFiles, " "))
 				extraTools = append(extraTools, depFixerTool)
 			}
 
diff --git a/genrule/genrule_test.go b/genrule/genrule_test.go
index 6ae9a18..2f5605e 100644
--- a/genrule/genrule_test.go
+++ b/genrule/genrule_test.go
@@ -141,7 +141,7 @@
 				out: ["out"],
 				cmd: "$(location) > $(out)",
 			`,
-			expect: "out/tool > __SBOX_SANDBOX_DIR__/out/out",
+			expect: "__SBOX_SANDBOX_DIR__/tools/out/bin/tool > __SBOX_SANDBOX_DIR__/out/out",
 		},
 		{
 			name: "empty location tool2",
@@ -150,7 +150,7 @@
 				out: ["out"],
 				cmd: "$(location) > $(out)",
 			`,
-			expect: "out/tool > __SBOX_SANDBOX_DIR__/out/out",
+			expect: "__SBOX_SANDBOX_DIR__/tools/out/bin/tool > __SBOX_SANDBOX_DIR__/out/out",
 		},
 		{
 			name: "empty location tool file",
@@ -159,7 +159,7 @@
 				out: ["out"],
 				cmd: "$(location) > $(out)",
 			`,
-			expect: "tool_file1 > __SBOX_SANDBOX_DIR__/out/out",
+			expect: "__SBOX_SANDBOX_DIR__/tools/src/tool_file1 > __SBOX_SANDBOX_DIR__/out/out",
 		},
 		{
 			name: "empty location tool file fg",
@@ -168,7 +168,7 @@
 				out: ["out"],
 				cmd: "$(location) > $(out)",
 			`,
-			expect: "tool_file1 > __SBOX_SANDBOX_DIR__/out/out",
+			expect: "__SBOX_SANDBOX_DIR__/tools/src/tool_file1 > __SBOX_SANDBOX_DIR__/out/out",
 		},
 		{
 			name: "empty location tool and tool file",
@@ -178,7 +178,7 @@
 				out: ["out"],
 				cmd: "$(location) > $(out)",
 			`,
-			expect: "out/tool > __SBOX_SANDBOX_DIR__/out/out",
+			expect: "__SBOX_SANDBOX_DIR__/tools/out/bin/tool > __SBOX_SANDBOX_DIR__/out/out",
 		},
 		{
 			name: "tool",
@@ -187,7 +187,7 @@
 				out: ["out"],
 				cmd: "$(location tool) > $(out)",
 			`,
-			expect: "out/tool > __SBOX_SANDBOX_DIR__/out/out",
+			expect: "__SBOX_SANDBOX_DIR__/tools/out/bin/tool > __SBOX_SANDBOX_DIR__/out/out",
 		},
 		{
 			name: "tool2",
@@ -196,7 +196,7 @@
 				out: ["out"],
 				cmd: "$(location :tool) > $(out)",
 			`,
-			expect: "out/tool > __SBOX_SANDBOX_DIR__/out/out",
+			expect: "__SBOX_SANDBOX_DIR__/tools/out/bin/tool > __SBOX_SANDBOX_DIR__/out/out",
 		},
 		{
 			name: "tool file",
@@ -205,7 +205,7 @@
 				out: ["out"],
 				cmd: "$(location tool_file1) > $(out)",
 			`,
-			expect: "tool_file1 > __SBOX_SANDBOX_DIR__/out/out",
+			expect: "__SBOX_SANDBOX_DIR__/tools/src/tool_file1 > __SBOX_SANDBOX_DIR__/out/out",
 		},
 		{
 			name: "tool file fg",
@@ -214,7 +214,7 @@
 				out: ["out"],
 				cmd: "$(location :1tool_file) > $(out)",
 			`,
-			expect: "tool_file1 > __SBOX_SANDBOX_DIR__/out/out",
+			expect: "__SBOX_SANDBOX_DIR__/tools/src/tool_file1 > __SBOX_SANDBOX_DIR__/out/out",
 		},
 		{
 			name: "tool files",
@@ -223,7 +223,7 @@
 				out: ["out"],
 				cmd: "$(locations :tool_files) > $(out)",
 			`,
-			expect: "tool_file1 tool_file2 > __SBOX_SANDBOX_DIR__/out/out",
+			expect: "__SBOX_SANDBOX_DIR__/tools/src/tool_file1 __SBOX_SANDBOX_DIR__/tools/src/tool_file2 > __SBOX_SANDBOX_DIR__/out/out",
 		},
 		{
 			name: "in1",
@@ -600,7 +600,7 @@
 				cmd: "$(location) $(in) > $(out)",
 			`,
 			cmds: []string{
-				"bash -c 'out/tool in1.txt > __SBOX_SANDBOX_DIR__/out/in1.h' && bash -c 'out/tool in2.txt > __SBOX_SANDBOX_DIR__/out/in2.h'",
+				"bash -c '__SBOX_SANDBOX_DIR__/tools/out/bin/tool in1.txt > __SBOX_SANDBOX_DIR__/out/in1.h' && bash -c '__SBOX_SANDBOX_DIR__/tools/out/bin/tool in2.txt > __SBOX_SANDBOX_DIR__/out/in2.h'",
 			},
 			deps:  []string{buildDir + "/.intermediates/gen/gen/gensrcs/in1.h", buildDir + "/.intermediates/gen/gen/gensrcs/in2.h"},
 			files: []string{buildDir + "/.intermediates/gen/gen/gensrcs/in1.h", buildDir + "/.intermediates/gen/gen/gensrcs/in2.h"},
@@ -614,8 +614,8 @@
 				shard_size: 2,
 			`,
 			cmds: []string{
-				"bash -c 'out/tool in1.txt > __SBOX_SANDBOX_DIR__/out/in1.h' && bash -c 'out/tool in2.txt > __SBOX_SANDBOX_DIR__/out/in2.h'",
-				"bash -c 'out/tool in3.txt > __SBOX_SANDBOX_DIR__/out/in3.h'",
+				"bash -c '__SBOX_SANDBOX_DIR__/tools/out/bin/tool in1.txt > __SBOX_SANDBOX_DIR__/out/in1.h' && bash -c '__SBOX_SANDBOX_DIR__/tools/out/bin/tool in2.txt > __SBOX_SANDBOX_DIR__/out/in2.h'",
+				"bash -c '__SBOX_SANDBOX_DIR__/tools/out/bin/tool in3.txt > __SBOX_SANDBOX_DIR__/out/in3.h'",
 			},
 			deps:  []string{buildDir + "/.intermediates/gen/gen/gensrcs/in1.h", buildDir + "/.intermediates/gen/gen/gensrcs/in2.h", buildDir + "/.intermediates/gen/gen/gensrcs/in3.h"},
 			files: []string{buildDir + "/.intermediates/gen/gen/gensrcs/in1.h", buildDir + "/.intermediates/gen/gen/gensrcs/in2.h", buildDir + "/.intermediates/gen/gen/gensrcs/in3.h"},
@@ -758,7 +758,7 @@
 }
 
 func (t *testTool) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	t.outputFile = android.PathForTesting("out", ctx.ModuleName())
+	t.outputFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "bin"), ctx.ModuleName(), android.PathForOutput(ctx, ctx.ModuleName()))
 }
 
 func (t *testTool) HostToolPath() android.OptionalPath {
diff --git a/java/aar.go b/java/aar.go
index 3750f72..e3ad252 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -416,7 +416,6 @@
 	}
 
 	ctx.VisitDirectDeps(func(module android.Module) {
-		depName := ctx.OtherModuleName(module)
 		depTag := ctx.OtherModuleDependencyTag(module)
 
 		var exportPackage android.Path
@@ -432,15 +431,6 @@
 			if exportPackage != nil {
 				sharedLibs = append(sharedLibs, exportPackage)
 			}
-
-			// 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 {
-				classLoaderContexts.MaybeAddContext(ctx, component.OptionalImplicitSdkLibrary(),
-					component.DexJarBuildPath(), component.DexJarInstallPath())
-			}
-
 		case frameworkResTag:
 			if exportPackage != nil {
 				sharedLibs = append(sharedLibs, exportPackage)
@@ -468,8 +458,7 @@
 			}
 		}
 
-		// Merge dep's CLC after processing the dep itself (which may add its own <uses-library>).
-		maybeAddCLCFromDep(module, depTag, depName, classLoaderContexts)
+		addCLCFromDep(ctx, module, classLoaderContexts)
 	})
 
 	deps = append(deps, sharedLibs...)
@@ -851,10 +840,14 @@
 	return nil, nil
 }
 
+var _ android.ApexModule = (*AARImport)(nil)
+
+// Implements android.ApexModule
 func (a *AARImport) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
 	return a.depIsInSameApex(ctx, dep)
 }
 
+// Implements android.ApexModule
 func (g *AARImport) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
 	sdkVersion android.ApiLevel) error {
 	return nil
diff --git a/java/app.go b/java/app.go
index e6d9550..272d836 100755
--- a/java/app.go
+++ b/java/app.go
@@ -1546,11 +1546,11 @@
 	a.dexpreopter.enforceUsesLibs = a.usesLibrary.enforceUsesLibraries()
 	a.dexpreopter.classLoaderContexts = a.usesLibrary.classLoaderContextForUsesLibDeps(ctx)
 
-	dexOutput := a.dexpreopter.dexpreopt(ctx, jnisUncompressed)
+	a.dexpreopter.dexpreopt(ctx, jnisUncompressed)
 	if a.dexpreopter.uncompressedDex {
 		dexUncompressed := android.PathForModuleOut(ctx, "dex-uncompressed", ctx.ModuleName()+".apk")
-		a.uncompressDex(ctx, dexOutput, dexUncompressed.OutputPath)
-		dexOutput = dexUncompressed
+		a.uncompressDex(ctx, jnisUncompressed, dexUncompressed.OutputPath)
+		jnisUncompressed = dexUncompressed
 	}
 
 	apkFilename := proptools.StringDefault(a.properties.Filename, a.BaseModuleName()+".apk")
@@ -1574,11 +1574,11 @@
 		if lineage := String(a.properties.Lineage); lineage != "" {
 			lineageFile = android.PathForModuleSrc(ctx, lineage)
 		}
-		SignAppPackage(ctx, signed, dexOutput, certificates, nil, lineageFile)
+		SignAppPackage(ctx, signed, jnisUncompressed, certificates, nil, lineageFile)
 		a.outputFile = signed
 	} else {
 		alignedApk := android.PathForModuleOut(ctx, "zip-aligned", apkFilename)
-		TransformZipAlign(ctx, alignedApk, dexOutput)
+		TransformZipAlign(ctx, alignedApk, jnisUncompressed)
 		a.outputFile = alignedApk
 		a.certificate = PresignedCertificate
 	}
@@ -1653,6 +1653,9 @@
 	return sdkSpecFrom("")
 }
 
+var _ android.ApexModule = (*AndroidAppImport)(nil)
+
+// Implements android.ApexModule
 func (j *AndroidAppImport) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
 	sdkVersion android.ApiLevel) error {
 	// Do not check for prebuilts against the min_sdk_version of enclosing APEX
@@ -1987,7 +1990,7 @@
 		ctx.VisitDirectDeps(func(m android.Module) {
 			if tag, ok := ctx.OtherModuleDependencyTag(m).(usesLibraryDependencyTag); ok {
 				dep := ctx.OtherModuleName(m)
-				if lib, ok := m.(Dependency); ok {
+				if lib, ok := m.(UsesLibraryDependency); ok {
 					clcMap.AddContextForSdk(ctx, tag.sdkVersion, dep,
 						lib.DexJarBuildPath(), lib.DexJarInstallPath(), lib.ClassLoaderContexts())
 				} else if ctx.Config().AllowMissingDependencies() {
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index 67738d4..b5830c7 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -112,13 +112,13 @@
 	return dexpreopt.OdexOnSystemOtherByName(ctx.ModuleName(), android.InstallPathToOnDevicePath(ctx, installPath), dexpreopt.GetGlobalConfig(ctx))
 }
 
-func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.ModuleOutPath) android.ModuleOutPath {
+func (d *dexpreopter) dexpreopt(ctx android.ModuleContext, dexJarFile android.ModuleOutPath) {
 	// TODO(b/148690468): The check on d.installPath is to bail out in cases where
 	// the dexpreopter struct hasn't been fully initialized before we're called,
 	// e.g. in aar.go. This keeps the behaviour that dexpreopting is effectively
 	// disabled, even if installable is true.
 	if d.dexpreoptDisabled(ctx) || d.installPath.Base() == "." {
-		return dexJarFile
+		return
 	}
 
 	globalSoong := dexpreopt.GetGlobalSoongConfig(ctx)
@@ -213,12 +213,10 @@
 	dexpreoptRule, err := dexpreopt.GenerateDexpreoptRule(ctx, globalSoong, global, dexpreoptConfig)
 	if err != nil {
 		ctx.ModuleErrorf("error generating dexpreopt rule: %s", err.Error())
-		return dexJarFile
+		return
 	}
 
 	dexpreoptRule.Build("dexpreopt", "dexpreopt")
 
 	d.builtInstalled = dexpreoptRule.Installs().String()
-
-	return dexJarFile
 }
diff --git a/java/java.go b/java/java.go
index d44719e..719915e 100644
--- a/java/java.go
+++ b/java/java.go
@@ -510,6 +510,7 @@
 type UsesLibraryDependency interface {
 	DexJarBuildPath() android.Path
 	DexJarInstallPath() android.Path
+	ClassLoaderContexts() dexpreopt.ClassLoaderContextMap
 }
 
 type Dependency interface {
@@ -518,7 +519,6 @@
 	ImplementationJars() android.Paths
 	ResourceJars() android.Paths
 	AidlIncludeDirs() android.Paths
-	ClassLoaderContexts() dexpreopt.ClassLoaderContextMap
 	ExportedPlugins() (android.Paths, []string, bool)
 	SrcJarArgs() ([]string, android.Paths)
 	BaseModuleName() string
@@ -1081,8 +1081,6 @@
 			switch tag {
 			case libTag:
 				deps.classpath = append(deps.classpath, dep.SdkHeaderJars(ctx, j.sdkVersion())...)
-				j.classLoaderContexts.MaybeAddContext(ctx, dep.OptionalImplicitSdkLibrary(),
-					dep.DexJarBuildPath(), dep.DexJarInstallPath())
 			case staticLibTag:
 				ctx.ModuleErrorf("dependency on java_sdk_library %q can only be in libs", otherName)
 			}
@@ -1092,7 +1090,6 @@
 				deps.bootClasspath = append(deps.bootClasspath, dep.HeaderJars()...)
 			case libTag, instrumentationForTag:
 				deps.classpath = append(deps.classpath, dep.HeaderJars()...)
-				j.classLoaderContexts.AddContextMap(dep.ClassLoaderContexts(), otherName)
 				deps.aidlIncludeDirs = append(deps.aidlIncludeDirs, dep.AidlIncludeDirs()...)
 				pluginJars, pluginClasses, disableTurbine := dep.ExportedPlugins()
 				addPlugins(&deps, pluginJars, pluginClasses...)
@@ -1179,8 +1176,7 @@
 			}
 		}
 
-		// Merge dep's CLC after processing the dep itself (which may add its own <uses-library>).
-		maybeAddCLCFromDep(module, tag, otherName, j.classLoaderContexts)
+		addCLCFromDep(ctx, module, j.classLoaderContexts)
 	})
 
 	return deps
@@ -1786,7 +1782,7 @@
 		j.dexJarFile = dexOutputFile
 
 		// Dexpreopting
-		dexOutputFile = j.dexpreopt(ctx, dexOutputFile)
+		j.dexpreopt(ctx, dexOutputFile)
 
 		j.maybeStrippedDexJarFile = dexOutputFile
 
@@ -2021,10 +2017,12 @@
 	return len(srcFiles) > 0 || len(ctx.GetDirectDepsWithTag(staticLibTag)) > 0
 }
 
+// Implements android.ApexModule
 func (j *Module) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
 	return j.depIsInSameApex(ctx, dep)
 }
 
+// Implements android.ApexModule
 func (j *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
 	sdkVersion android.ApiLevel) error {
 	sdkSpec := j.minSdkVersion()
@@ -2070,6 +2068,8 @@
 	InstallMixin func(ctx android.ModuleContext, installPath android.Path) (extraInstallDeps android.Paths)
 }
 
+var _ android.ApexModule = (*Library)(nil)
+
 // Provides access to the list of permitted packages from updatable boot jars.
 type PermittedPackagesForUpdatableBootJars interface {
 	PermittedPackagesForUpdatableBootJars() []string
@@ -2133,18 +2133,6 @@
 		j.installFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"),
 			j.Stem()+".jar", j.outputFile, extraInstallDeps...)
 	}
-
-	// 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.classLoaderContexts.MaybeAddContext(ctx, j.OptionalImplicitSdkLibrary(),
-		j.DexJarBuildPath(), j.DexJarInstallPath())
-
-	// A non-SDK library may provide a <uses-library> (the name may be different from the module name).
-	if lib := proptools.String(j.usesLibraryProperties.Provides_uses_lib); lib != "" {
-		j.classLoaderContexts.AddContext(ctx, lib, j.DexJarBuildPath(), j.DexJarInstallPath())
-	}
 }
 
 func (j *Library) DepsMutator(ctx android.BottomUpMutatorContext) {
@@ -2806,7 +2794,6 @@
 	var flags javaBuilderFlags
 
 	ctx.VisitDirectDeps(func(module android.Module) {
-		otherName := ctx.OtherModuleName(module)
 		tag := ctx.OtherModuleDependencyTag(module)
 
 		switch dep := module.(type) {
@@ -2821,27 +2808,17 @@
 			switch tag {
 			case libTag:
 				flags.classpath = append(flags.classpath, dep.SdkHeaderJars(ctx, j.sdkVersion())...)
-				j.classLoaderContexts.AddContext(ctx, otherName, dep.DexJarBuildPath(), dep.DexJarInstallPath())
 			}
 		}
 
-		// Merge dep's CLC after processing the dep itself (which may add its own <uses-library>).
-		maybeAddCLCFromDep(module, tag, otherName, j.classLoaderContexts)
+		addCLCFromDep(ctx, module, j.classLoaderContexts)
 	})
 
-	var installFile android.Path
 	if Bool(j.properties.Installable) {
-		installFile = ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"),
+		ctx.InstallFile(android.PathForModuleInstall(ctx, "framework"),
 			jarName, 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.classLoaderContexts.MaybeAddContext(ctx, j.OptionalImplicitSdkLibrary(),
-		outputFile, installFile)
-
 	j.exportAidlIncludeDirs = android.PathsForModuleSrc(ctx, j.properties.Aidl.Export_include_dirs)
 
 	if ctx.Device() && Bool(j.dexProperties.Compile_dex) {
@@ -2934,10 +2911,14 @@
 	return nil, nil
 }
 
+var _ android.ApexModule = (*Import)(nil)
+
+// Implements android.ApexModule
 func (j *Import) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
 	return j.depIsInSameApex(ctx, dep)
 }
 
+// Implements android.ApexModule
 func (j *Import) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
 	sdkVersion android.ApiLevel) error {
 	// Do not check for prebuilts against the min_sdk_version of enclosing APEX
@@ -3115,7 +3096,7 @@
 
 	j.dexJarFile = dexOutputFile
 
-	dexOutputFile = j.dexpreopt(ctx, dexOutputFile)
+	j.dexpreopt(ctx, dexOutputFile)
 
 	j.maybeStrippedDexJarFile = dexOutputFile
 
@@ -3129,6 +3110,9 @@
 	return j.dexJarFile
 }
 
+var _ android.ApexModule = (*DexImport)(nil)
+
+// Implements android.ApexModule
 func (j *DexImport) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
 	sdkVersion android.ApiLevel) error {
 	// we don't check prebuilt modules for sdk_version
@@ -3248,30 +3232,63 @@
 var String = proptools.String
 var inList = android.InList
 
-// Add class loader context of a given dependency to the given class loader context, provided that
-// all the necessary conditions are met.
-func maybeAddCLCFromDep(depModule android.Module, depTag blueprint.DependencyTag,
-	depName string, clcMap dexpreopt.ClassLoaderContextMap) {
+// TODO(b/132357300) Generalize SdkLibrarComponentDependency to non-SDK libraries and merge with
+// this interface.
+type ProvidesUsesLib interface {
+	ProvidesUsesLib() *string
+}
 
-	if dep, ok := depModule.(Dependency); ok {
-		if depTag == libTag {
-			// Ok, propagate <uses-library> through non-static library dependencies.
-		} else if depTag == staticLibTag {
-			// Propagate <uses-library> through static library dependencies, unless it is a
-			// component library (such as stubs). Component libraries have a dependency on their
-			// SDK library, which should not be pulled just because of a static component library.
-			if comp, isComp := depModule.(SdkLibraryComponentDependency); isComp {
-				if compName := comp.OptionalImplicitSdkLibrary(); compName != nil {
-					dep = nil
-				}
-			}
-		} else {
-			// Don't propagate <uses-library> for other dependency tags.
-			dep = nil
-		}
+func (j *Module) ProvidesUsesLib() *string {
+	return j.usesLibraryProperties.Provides_uses_lib
+}
 
-		if dep != nil {
-			clcMap.AddContextMap(dep.ClassLoaderContexts(), depName)
+// Add class loader context (CLC) of a given dependency to the current CLC.
+func addCLCFromDep(ctx android.ModuleContext, depModule android.Module,
+	clcMap dexpreopt.ClassLoaderContextMap) {
+
+	dep, ok := depModule.(UsesLibraryDependency)
+	if !ok {
+		return
+	}
+
+	// Find out if the dependency is either an SDK library or an ordinary library that is disguised
+	// as an SDK library by the means of `provides_uses_lib` property. If yes, the library is itself
+	// a <uses-library> and should be added as a node in the CLC tree, and its CLC should be added
+	// as subtree of that node. Otherwise the library is not a <uses_library> and should not be
+	// added to CLC, but the transitive <uses-library> dependencies from its CLC should be added to
+	// the current CLC.
+	var implicitSdkLib *string
+	comp, isComp := depModule.(SdkLibraryComponentDependency)
+	if isComp {
+		implicitSdkLib = comp.OptionalImplicitSdkLibrary()
+		// OptionalImplicitSdkLibrary() may be nil so need to fall through to ProvidesUsesLib().
+	}
+	if implicitSdkLib == nil {
+		if ulib, ok := depModule.(ProvidesUsesLib); ok {
+			implicitSdkLib = ulib.ProvidesUsesLib()
 		}
 	}
+
+	depTag := ctx.OtherModuleDependencyTag(depModule)
+	if depTag == libTag || depTag == usesLibTag {
+		// Ok, propagate <uses-library> through non-static library dependencies.
+	} else if depTag == staticLibTag {
+		// Propagate <uses-library> through static library dependencies, unless it is a component
+		// library (such as stubs). Component libraries have a dependency on their SDK library,
+		// which should not be pulled just because of a static component library.
+		if implicitSdkLib != nil {
+			return
+		}
+	} else {
+		// Don't propagate <uses-library> for other dependency tags.
+		return
+	}
+
+	if implicitSdkLib != nil {
+		clcMap.AddContextForSdk(ctx, dexpreopt.AnySdkVersion, *implicitSdkLib,
+			dep.DexJarBuildPath(), dep.DexJarInstallPath(), dep.ClassLoaderContexts())
+	} else {
+		depName := ctx.OtherModuleName(depModule)
+		clcMap.AddContextMap(dep.ClassLoaderContexts(), depName)
+	}
 }
diff --git a/java/sdk_library.go b/java/sdk_library.go
index 4e33d74..92b554b 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -28,6 +28,7 @@
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
+	"android/soong/dexpreopt"
 )
 
 const (
@@ -1925,6 +1926,9 @@
 	}
 }
 
+var _ android.ApexModule = (*SdkLibraryImport)(nil)
+
+// Implements android.ApexModule
 func (module *SdkLibraryImport) DepIsInSameApex(mctx android.BaseModuleContext, dep android.Module) bool {
 	depTag := mctx.OtherModuleDependencyTag(dep)
 	if depTag == xmlPermissionsFileTag {
@@ -1936,6 +1940,7 @@
 	return false
 }
 
+// Implements android.ApexModule
 func (module *SdkLibraryImport) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
 	sdkVersion android.ApiLevel) error {
 	// we don't check prebuilt modules for sdk_version
@@ -2016,7 +2021,7 @@
 	return module.sdkJars(ctx, sdkVersion, false)
 }
 
-// to satisfy SdkLibraryDependency interface
+// to satisfy UsesLibraryDependency interface
 func (module *SdkLibraryImport) DexJarBuildPath() android.Path {
 	if module.implLibraryModule == nil {
 		return nil
@@ -2025,7 +2030,7 @@
 	}
 }
 
-// to satisfy SdkLibraryDependency interface
+// to satisfy UsesLibraryDependency interface
 func (module *SdkLibraryImport) DexJarInstallPath() android.Path {
 	if module.implLibraryModule == nil {
 		return nil
@@ -2034,6 +2039,11 @@
 	}
 }
 
+// to satisfy UsesLibraryDependency interface
+func (module *SdkLibraryImport) ClassLoaderContexts() dexpreopt.ClassLoaderContextMap {
+	return nil
+}
+
 // to satisfy apex.javaDependency interface
 func (module *SdkLibraryImport) JacocoReportClassesFile() android.Path {
 	if module.implLibraryModule == nil {
@@ -2141,6 +2151,9 @@
 	// do nothing
 }
 
+var _ android.ApexModule = (*sdkLibraryXml)(nil)
+
+// Implements android.ApexModule
 func (module *sdkLibraryXml) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
 	sdkVersion android.ApiLevel) error {
 	// sdkLibraryXml doesn't need to be checked separately because java_sdk_library is checked
diff --git a/rust/image.go b/rust/image.go
index 4951d2b..af8c3b2 100644
--- a/rust/image.go
+++ b/rust/image.go
@@ -90,7 +90,7 @@
 		vndkVersion := ctx.DeviceConfig().VndkVersion()
 		if vndkVersion != "current" && vndkVersion != "" && vndkVersion != m.Properties.VndkVersion {
 			m.Properties.HideFromMake = true
-			m.SkipInstall()
+			m.HideFromMake()
 		}
 	}
 }
diff --git a/rust/rust.go b/rust/rust.go
index cc70a7a..ac17ed2 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -189,7 +189,15 @@
 	return false
 }
 
-func (c *Module) IsVndkPrivate(config android.Config) bool {
+func (c *Module) IsVndkPrivate() bool {
+	return false
+}
+
+func (c *Module) IsLlndk() bool {
+	return false
+}
+
+func (c *Module) IsLlndkPublic() bool {
 	return false
 }
 
@@ -1077,6 +1085,9 @@
 	return String(mod.Properties.Min_sdk_version)
 }
 
+var _ android.ApexModule = (*Module)(nil)
+
+// Implements android.ApexModule
 func (mod *Module) ShouldSupportSdkVersion(ctx android.BaseModuleContext, sdkVersion android.ApiLevel) error {
 	minSdkVersion := mod.minSdkVersion()
 	if minSdkVersion == "apex_inherit" {
@@ -1099,6 +1110,7 @@
 	return nil
 }
 
+// Implements android.ApexModule
 func (mod *Module) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
 	depTag := ctx.OtherModuleDependencyTag(dep)
 
diff --git a/scripts/build-aml-prebuilts.sh b/scripts/build-aml-prebuilts.sh
index 1be3b8a..4b08ac3 100755
--- a/scripts/build-aml-prebuilts.sh
+++ b/scripts/build-aml-prebuilts.sh
@@ -82,6 +82,9 @@
 # CrossHost: linux_bionic
 # CrossHostArch: x86_64
 #   -  Enable Bionic on host as ART needs prebuilts for it.
+# VendorVars.art_mdoule.source_build
+#   -  TODO(b/172480615): Change default to false when platform uses ART Module
+#      prebuilts by default.
 cat > ${SOONG_VARS}.new << EOF
 {
     "BuildNumberFile": "build_number.txt",
@@ -104,7 +107,7 @@
 
     "VendorVars": {
         "art_module": {
-            "source_build": "${ENABLE_ART_SOURCE_BUILD:-false}"
+            "source_build": "${ENABLE_ART_SOURCE_BUILD:-true}"
         }
     },
 
diff --git a/scripts/build-ndk-prebuilts.sh b/scripts/build-ndk-prebuilts.sh
index 1a33219..c27f098 100755
--- a/scripts/build-ndk-prebuilts.sh
+++ b/scripts/build-ndk-prebuilts.sh
@@ -56,6 +56,12 @@
     "Ndk_abis": true,
     "Exclude_draft_ndk_apis": true,
 
+    "VendorVars": {
+        "art_module": {
+            "source_build": "true"
+        }
+    },
+
     "MissingUsesLibraries": ${MISSING_USES_LIBRARIES}
 }
 EOF
diff --git a/sysprop/sysprop_library.go b/sysprop/sysprop_library.go
index 6a53414..93a3fe0 100644
--- a/sysprop/sysprop_library.go
+++ b/sysprop/sysprop_library.go
@@ -322,6 +322,9 @@
 		}}
 }
 
+var _ android.ApexModule = (*syspropLibrary)(nil)
+
+// Implements android.ApexModule
 func (m *syspropLibrary) ShouldSupportSdkVersion(ctx android.BaseModuleContext,
 	sdkVersion android.ApiLevel) error {
 	return fmt.Errorf("sysprop_library is not supposed to be part of apex modules")
diff --git a/third_party/zip/android_test.go b/third_party/zip/android_test.go
index 5154a17..9932c1b 100644
--- a/third_party/zip/android_test.go
+++ b/third_party/zip/android_test.go
@@ -74,3 +74,69 @@
 		}
 	}
 }
+
+func TestCopyFromZip64(t *testing.T) {
+	if testing.Short() {
+		t.Skip("slow test; skipping")
+	}
+
+	const size = uint32max + 1
+	fromZipBytes := &bytes.Buffer{}
+	fromZip := NewWriter(fromZipBytes)
+	w, err := fromZip.CreateHeaderAndroid(&FileHeader{
+		Name:               "large",
+		Method:             Store,
+		UncompressedSize64: size,
+		CompressedSize64:   size,
+	})
+	if err != nil {
+		t.Fatalf("Create: %v", err)
+	}
+	_, err = w.Write(make([]byte, size))
+	if err != nil {
+		t.Fatalf("Write: %v", err)
+	}
+	err = fromZip.Close()
+	if err != nil {
+		t.Fatalf("Close: %v", err)
+	}
+	fromZip = nil
+
+	fromZipReader, err := NewReader(bytes.NewReader(fromZipBytes.Bytes()), int64(fromZipBytes.Len()))
+	if err != nil {
+		t.Fatalf("NewReader: %v", err)
+	}
+
+	toZipBytes := &bytes.Buffer{}
+	toZip := NewWriter(toZipBytes)
+	err = toZip.CopyFrom(fromZipReader.File[0], fromZipReader.File[0].Name)
+	if err != nil {
+		t.Fatalf("CopyFrom: %v", err)
+	}
+
+	err = toZip.Close()
+	if err != nil {
+		t.Fatalf("Close: %v", err)
+	}
+
+	// Save some memory
+	fromZipReader = nil
+	fromZipBytes.Reset()
+
+	toZipReader, err := NewReader(bytes.NewReader(toZipBytes.Bytes()), int64(toZipBytes.Len()))
+	if err != nil {
+		t.Fatalf("NewReader: %v", err)
+	}
+
+	if len(toZipReader.File) != 1 {
+		t.Fatalf("Expected 1 file in toZip, got %d", len(toZipReader.File))
+	}
+
+	if g, w := toZipReader.File[0].CompressedSize64, uint64(size); g != w {
+		t.Errorf("Expected CompressedSize64 %d, got %d", w, g)
+	}
+
+	if g, w := toZipReader.File[0].UncompressedSize64, uint64(size); g != w {
+		t.Errorf("Expected UnompressedSize64 %d, got %d", w, g)
+	}
+}
diff --git a/third_party/zip/writer.go b/third_party/zip/writer.go
index 4c5eb78..8dd986e 100644
--- a/third_party/zip/writer.go
+++ b/third_party/zip/writer.go
@@ -276,9 +276,6 @@
 	} else {
 		b.uint32(h.CRC32)
 
-		if h.CompressedSize64 > uint32max || h.UncompressedSize64 > uint32max {
-			panic("skipping writing the data descriptor for a 64-bit value is not yet supported")
-		}
 		compressedSize := uint32(h.CompressedSize64)
 		if compressedSize == 0 {
 			compressedSize = h.CompressedSize
@@ -289,6 +286,21 @@
 			uncompressedSize = h.UncompressedSize
 		}
 
+		if h.CompressedSize64 > uint32max || h.UncompressedSize64 > uint32max {
+			// Sizes don't fit in a 32-bit field, put them in a zip64 extra instead.
+			compressedSize = uint32max
+			uncompressedSize = uint32max
+
+			// append a zip64 extra block to Extra
+			var buf [20]byte // 2x uint16 + 2x uint64
+			eb := writeBuf(buf[:])
+			eb.uint16(zip64ExtraId)
+			eb.uint16(16) // size = 2x uint64
+			eb.uint64(h.UncompressedSize64)
+			eb.uint64(h.CompressedSize64)
+			h.Extra = append(h.Extra, buf[:]...)
+		}
+
 		b.uint32(compressedSize)
 		b.uint32(uncompressedSize)
 	}
diff --git a/ui/metrics/proc/Android.bp b/ui/metrics/proc/Android.bp
new file mode 100644
index 0000000..32d8217
--- /dev/null
+++ b/ui/metrics/proc/Android.bp
@@ -0,0 +1,37 @@
+// 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.
+
+bootstrap_go_package {
+    name: "soong-ui-metrics-proc",
+    pkgPath: "android/soong/ui/metrics/proc",
+    deps: [
+        "soong-finder-fs",
+    ],
+    srcs: [
+        "status.go",
+    ],
+    linux: {
+        srcs: [
+            "status_linux.go",
+        ],
+        testSrcs: [
+            "status_linux_test.go",
+        ],
+    },
+    darwin: {
+        srcs: [
+            "status_darwin.go",
+        ],
+    },
+}
diff --git a/ui/metrics/proc/status.go b/ui/metrics/proc/status.go
new file mode 100644
index 0000000..f8b734c
--- /dev/null
+++ b/ui/metrics/proc/status.go
@@ -0,0 +1,128 @@
+// package proc contains functionality to read proc status files.
+package proc
+
+import (
+	"strconv"
+	"strings"
+)
+
+// ProcStatus holds information regarding the memory usage of
+// an executing process. The memory sizes in each of the field
+// is in bytes.
+type ProcStatus struct {
+	// Process PID.
+	pid int
+
+	// Peak virtual memory size.
+	VmPeak uint64
+
+	// Virtual memory size.
+	VmSize uint64
+
+	// Locked Memory size.
+	VmLck uint64
+
+	// Pinned memory size.
+	VmPin uint64
+
+	// Peak resident set size.
+	VmHWM uint64
+
+	// Resident set size (sum of RssAnon, RssFile and RssShmem).
+	VmRss uint64
+
+	// Size of resident anonymous memory.
+	RssAnon uint64
+
+	// Size of resident shared memory.
+	RssShmem uint64
+
+	// Size of data segments.
+	VmData uint64
+
+	// Size of stack segments.
+	VmStk uint64
+
+	//Size of text segments.
+	VmExe uint64
+
+	//Shared library code size.
+	VmLib uint64
+
+	// Page table entries size.
+	VmPTE uint64
+
+	// Size of second-level page tables.
+	VmPMD uint64
+
+	// Swapped-out virtual memory size by anonymous private.
+	VmSwap uint64
+
+	// Size of hugetlb memory page size.
+	HugetlbPages uint64
+}
+
+// fillProcStatus takes the key and value, converts the value
+// to the proper size unit and is stored in the ProcStatus.
+func fillProcStatus(s *ProcStatus, key, value string) {
+	v := strToUint64(value)
+	switch key {
+	case "VmPeak":
+		s.VmPeak = v
+	case "VmSize":
+		s.VmSize = v
+	case "VmLck":
+		s.VmLck = v
+	case "VmPin":
+		s.VmPin = v
+	case "VmHWM":
+		s.VmHWM = v
+	case "VmRSS":
+		s.VmRss = v
+	case "RssAnon":
+		s.RssAnon = v
+	case "RssShmem":
+		s.RssShmem = v
+	case "VmData":
+		s.VmData = v
+	case "VmStk":
+		s.VmStk = v
+	case "VmExe":
+		s.VmExe = v
+	case "VmLib":
+		s.VmLib = v
+	case "VmPTE":
+		s.VmPTE = v
+	case "VmPMD":
+		s.VmPMD = v
+	case "VmSwap":
+		s.VmSwap = v
+	case "HugetlbPages":
+		s.HugetlbPages = v
+	}
+}
+
+// strToUint64 takes the string and converts to unsigned 64-bit integer.
+// If the string contains a memory unit such as kB and is converted to
+// bytes.
+func strToUint64(v string) uint64 {
+	// v could be "1024 kB" so scan for the empty space and
+	// split between the value and the unit.
+	var separatorIndex int
+	if separatorIndex = strings.IndexAny(v, " "); separatorIndex < 0 {
+		separatorIndex = len(v)
+	}
+	value, err := strconv.ParseUint(v[:separatorIndex], 10, 64)
+	if err != nil {
+		return 0
+	}
+
+	var scale uint64 = 1
+	switch strings.TrimSpace(v[separatorIndex:]) {
+	case "kB", "KB":
+		scale = 1024
+	case "mB", "MB":
+		scale = 1024 * 1024
+	}
+	return value * scale
+}
diff --git a/ui/metrics/proc/status_darwin.go b/ui/metrics/proc/status_darwin.go
new file mode 100644
index 0000000..5c788a5
--- /dev/null
+++ b/ui/metrics/proc/status_darwin.go
@@ -0,0 +1,11 @@
+package proc
+
+import (
+	"android/soong/finder/fs"
+)
+
+// NewProcStatus returns a zero filled value of ProcStatus as it
+// is not supported for darwin distribution based.
+func NewProcStatus(pid int, _ fs.FileSystem) (*ProcStatus, error) {
+	return &ProcStatus{}, nil
+}
diff --git a/ui/metrics/proc/status_linux.go b/ui/metrics/proc/status_linux.go
new file mode 100644
index 0000000..dc0f943
--- /dev/null
+++ b/ui/metrics/proc/status_linux.go
@@ -0,0 +1,46 @@
+package proc
+
+import (
+	"io/ioutil"
+	"path/filepath"
+	"strconv"
+	"strings"
+
+	"android/soong/finder/fs"
+)
+
+// NewProcStatus returns an instance of the ProcStatus that contains memory
+// information of the process. The memory information is extracted from the
+// "/proc/<pid>/status" text file. This is only available for Linux
+// distribution that supports /proc.
+func NewProcStatus(pid int, fileSystem fs.FileSystem) (*ProcStatus, error) {
+	statusFname := filepath.Join("/proc", strconv.Itoa(pid), "status")
+	r, err := fileSystem.Open(statusFname)
+	if err != nil {
+		return &ProcStatus{}, err
+	}
+	defer r.Close()
+
+	data, err := ioutil.ReadAll(r)
+	if err != nil {
+		return &ProcStatus{}, err
+	}
+
+	s := &ProcStatus{
+		pid: pid,
+	}
+
+	for _, l := range strings.Split(string(data), "\n") {
+		// If the status file does not contain "key: values", just skip the line
+		// as the information we are looking for is not needed.
+		if !strings.Contains(l, ":") {
+			continue
+		}
+
+		// At this point, we're only considering entries that has key, single value pairs.
+		kv := strings.SplitN(l, ":", 2)
+		fillProcStatus(s, strings.TrimSpace(kv[0]), strings.TrimSpace(kv[1]))
+	}
+
+	return s, nil
+}
diff --git a/ui/metrics/proc/status_linux_test.go b/ui/metrics/proc/status_linux_test.go
new file mode 100644
index 0000000..6709850
--- /dev/null
+++ b/ui/metrics/proc/status_linux_test.go
@@ -0,0 +1,112 @@
+package proc
+
+import (
+	"fmt"
+	"path/filepath"
+	"reflect"
+	"strconv"
+	"testing"
+
+	"android/soong/finder/fs"
+)
+
+func TestNewProcStatus(t *testing.T) {
+	fs := fs.NewMockFs(nil)
+
+	pid := 4032827
+	procDir := filepath.Join("/proc", strconv.Itoa(pid))
+	if err := fs.MkDirs(procDir); err != nil {
+		t.Fatalf("failed to create proc pid dir %s: %v", procDir, err)
+	}
+	statusFilename := filepath.Join(procDir, "status")
+
+	if err := fs.WriteFile(statusFilename, statusData, 0644); err != nil {
+		t.Fatalf("failed to write proc file %s: %v", statusFilename, err)
+	}
+
+	status, err := NewProcStatus(pid, fs)
+	if err != nil {
+		t.Fatalf("got %v, want nil for error", err)
+	}
+
+	fmt.Printf("%d %d\b", status.VmPeak, expectedStatus.VmPeak)
+	if !reflect.DeepEqual(status, expectedStatus) {
+		t.Errorf("got %v, expecting %v for ProcStatus", status, expectedStatus)
+	}
+}
+
+var statusData = []byte(`Name:   fake_process
+Umask:  0022
+State:  S (sleeping)
+Tgid:   4032827
+Ngid:   0
+Pid:    4032827
+PPid:   1
+TracerPid:      0
+Uid:    0       0       0       0
+Gid:    0       0       0       0
+FDSize: 512
+Groups:
+NStgid: 4032827
+NSpid:  4032827
+NSpgid: 4032827
+NSsid:  4032827
+VmPeak:   733232 kB
+VmSize:   733232 kB
+VmLck:       132 kB
+VmPin:       130 kB
+VmHWM:     69156 kB
+VmRSS:     69156 kB
+RssAnon:           50896 kB
+RssFile:           18260 kB
+RssShmem:            122 kB
+VmData:   112388 kB
+VmStk:       132 kB
+VmExe:      9304 kB
+VmLib:         8 kB
+VmPTE:       228 kB
+VmSwap:        10 kB
+HugetlbPages:          22 kB
+CoreDumping:    0
+THP_enabled:    1
+Threads:        46
+SigQ:   2/767780
+SigPnd: 0000000000000000
+ShdPnd: 0000000000000000
+SigBlk: fffffffe3bfa3a00
+SigIgn: 0000000000000000
+SigCgt: fffffffe7fc1feff
+CapInh: 0000000000000000
+CapPrm: 0000003fffffffff
+CapEff: 0000003fffffffff
+CapBnd: 0000003fffffffff
+CapAmb: 0000000000000000
+NoNewPrivs:     0
+Seccomp:        0
+Speculation_Store_Bypass:       thread vulnerable
+Cpus_allowed:   ff,ffffffff,ffffffff
+Cpus_allowed_list:      0-71
+Mems_allowed:   00000000,00000003
+Mems_allowed_list:      0-1
+voluntary_ctxt_switches:        1635
+nonvoluntary_ctxt_switches:     32
+`)
+
+var expectedStatus = &ProcStatus{
+	pid:          4032827,
+	VmPeak:       750829568,
+	VmSize:       750829568,
+	VmLck:        135168,
+	VmPin:        133120,
+	VmHWM:        70815744,
+	VmRss:        70815744,
+	RssAnon:      52117504,
+	RssShmem:     124928,
+	VmData:       115085312,
+	VmStk:        135168,
+	VmExe:        9527296,
+	VmLib:        8192,
+	VmPTE:        233472,
+	VmSwap:       10240,
+	HugetlbPages: 22528,
+}