Merge "filesystem modules gathers first target only" into main
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 74b2eec..a758caf 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -5908,6 +5908,7 @@
 			srcs: ["foo/bar/MyClass.java"],
 			sdk_version: "current",
 			system_modules: "none",
+			use_embedded_native_libs: true,
 			jni_libs: ["libjni"],
 			stl: "none",
 			apex_available: [ "myapex" ],
diff --git a/apex/platform_bootclasspath_test.go b/apex/platform_bootclasspath_test.go
index 2be9c10..9f1e1e1 100644
--- a/apex/platform_bootclasspath_test.go
+++ b/apex/platform_bootclasspath_test.go
@@ -795,3 +795,127 @@
 			}
 		`)
 }
+
+// Source and prebuilt apex provide different set of boot jars
+func TestNonBootJarMissingInPrebuiltFragment(t *testing.T) {
+	bp := `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			bootclasspath_fragments: ["apex-fragment"],
+			updatable: false,
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		java_library {
+			name: "foo",
+			srcs: ["b.java"],
+			installable: true,
+			apex_available: ["myapex"],
+			permitted_packages: ["foo"],
+		}
+
+		java_library {
+			name: "bar",
+			srcs: ["b.java"],
+			installable: true,
+			apex_available: ["myapex"],
+			permitted_packages: ["bar"],
+		}
+
+		bootclasspath_fragment {
+			name: "apex-fragment",
+			contents: ["foo", "bar"],
+			apex_available:[ "myapex" ],
+			hidden_api: {
+				split_packages: ["*"],
+			},
+		}
+
+		prebuilt_apex {
+			name: "com.google.android.myapex", // mainline prebuilt selection logic in soong relies on the naming convention com.google.android
+			apex_name: "myapex",
+			source_apex_name: "myapex",
+			src: "myapex.apex",
+			exported_bootclasspath_fragments: ["apex-fragment"],
+		}
+
+		java_import {
+			name: "foo",
+			jars: ["foo.jar"],
+			apex_available: ["myapex"],
+			permitted_packages: ["foo"],
+		}
+
+		prebuilt_bootclasspath_fragment {
+			name: "apex-fragment",
+			contents: ["foo"], // Unlike the source fragment, this is missing bar
+			apex_available:[ "myapex" ],
+			hidden_api: {
+				annotation_flags: "my-bootclasspath-fragment/annotation-flags.csv",
+				metadata: "my-bootclasspath-fragment/metadata.csv",
+				index: "my-bootclasspath-fragment/index.csv",
+				stub_flags: "my-bootclasspath-fragment/stub-flags.csv",
+				all_flags: "my-bootclasspath-fragment/all-flags.csv",
+			},
+		}
+
+		apex_contributions {
+			name: "my_apex_contributions",
+			api_domain: "myapex",
+			contents: [%v],
+		}
+	`
+	testCases := []struct {
+		desc                     string
+		configuredBootJars       []string
+		apexContributionContents string
+		errorExpected            bool
+	}{
+		{
+			desc:               "Source apex is selected, and APEX_BOOT_JARS is correctly configured for source apex builds",
+			configuredBootJars: []string{"myapex:foo", "myapex:bar"},
+		},
+		{
+			desc:               "Source apex is selected, and APEX_BOOT_JARS is missing bar",
+			configuredBootJars: []string{"myapex:foo"},
+			errorExpected:      true,
+		},
+		{
+			desc:                     "Prebuilt apex is selected, and APEX_BOOT_JARS is correctly configured for prebuilt apex build",
+			configuredBootJars:       []string{"myapex:foo"},
+			apexContributionContents: `"prebuilt_com.google.android.myapex"`,
+		},
+		{
+			desc:                     "Prebuilt apex is selected, and APEX_BOOT_JARS is missing foo",
+			configuredBootJars:       []string{"myapex:bar"},
+			apexContributionContents: `"prebuilt_com.google.android.myapex"`,
+			errorExpected:            true,
+		},
+	}
+
+	for _, tc := range testCases {
+		fixture := android.GroupFixturePreparers(
+			prepareForTestWithPlatformBootclasspath,
+			PrepareForTestWithApexBuildComponents,
+			prepareForTestWithMyapex,
+			java.FixtureConfigureApexBootJars(tc.configuredBootJars...),
+			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				variables.BuildFlags = map[string]string{
+					"RELEASE_APEX_CONTRIBUTIONS_ART": "my_apex_contributions",
+				}
+			}),
+		)
+		if tc.errorExpected {
+			fixture = fixture.ExtendWithErrorHandler(
+				android.FixtureExpectsAtLeastOneErrorMatchingPattern(`in contents.*must also be declared in PRODUCT_APEX_BOOT_JARS`),
+			)
+		}
+		fixture.RunTestWithBp(t, fmt.Sprintf(bp, tc.apexContributionContents))
+	}
+}
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index 72a9e52..b2afa39 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -835,7 +835,21 @@
 	android.SetProvider(ctx, android.PrebuiltInfoProvider, info)
 }
 
+// Uses an object provided by its deps to validate that the contents of bcpf have been added to the global
+// PRODUCT_APEX_BOOT_JARS
+// This validation will only run on the apex which is active for this product/release_config
+func validateApexClasspathFragments(ctx android.ModuleContext) {
+	ctx.VisitDirectDeps(func(m android.Module) {
+		if info, exists := android.OtherModuleProvider(ctx, m, java.ClasspathFragmentValidationInfoProvider); exists {
+			ctx.ModuleErrorf("%s in contents of %s must also be declared in PRODUCT_APEX_BOOT_JARS", info.UnknownJars, info.ClasspathFragmentModuleName)
+		}
+	})
+}
+
 func (p *Prebuilt) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// Validate contents of classpath fragments
+	validateApexClasspathFragments(ctx)
+
 	p.apexKeysPath = writeApexKeys(ctx, p)
 	// TODO(jungjw): Check the key validity.
 	p.inputApex = android.OptionalPathForModuleSrc(ctx, p.prebuiltCommonProperties.Selected_apex).Path()
@@ -1059,6 +1073,9 @@
 }
 
 func (a *ApexSet) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	// Validate contents of classpath fragments
+	validateApexClasspathFragments(ctx)
+
 	a.apexKeysPath = writeApexKeys(ctx, a)
 	a.installFilename = a.InstallFilename()
 	if !strings.HasSuffix(a.installFilename, imageApexSuffix) && !strings.HasSuffix(a.installFilename, imageCapexSuffix) {
diff --git a/cmd/release_config/crunch_flags/main.go b/cmd/release_config/crunch_flags/main.go
index cd39ffd..8a80a02 100644
--- a/cmd/release_config/crunch_flags/main.go
+++ b/cmd/release_config/crunch_flags/main.go
@@ -16,8 +16,8 @@
 )
 
 var (
-	// When a flag declaration has an initial value that is a string, the default workflow is PREBUILT.
-	// If the flag name starts with any of prefixes in manualFlagNamePrefixes, it is MANUAL.
+	// When a flag declaration has an initial value that is a string, the default workflow is WorkflowPrebuilt.
+	// If the flag name starts with any of prefixes in manualFlagNamePrefixes, it is WorkflowManual.
 	manualFlagNamePrefixes []string = []string{
 		"RELEASE_ACONFIG_",
 		"RELEASE_PLATFORM_",
@@ -133,8 +133,8 @@
 			Containers:  containers,
 		}
 		description = ""
-		// Most build flags are `workflow: PREBUILT`.
-		workflow := rc_proto.Workflow(rc_proto.Workflow_PREBUILT)
+		// Most build flags are `workflow: WorkflowPrebuilt`.
+		workflow := rc_proto.Workflow(rc_proto.Workflow_WorkflowPrebuilt)
 		switch {
 		case declName == "RELEASE_ACONFIG_VALUE_SETS":
 			if strings.HasPrefix(declValue, "\"") {
@@ -142,21 +142,21 @@
 			}
 			continue
 		case strings.HasPrefix(declValue, "\""):
-			// String values mean that the flag workflow is (most likely) either MANUAL or PREBUILT.
+			// String values mean that the flag workflow is (most likely) either WorkflowManual or WorkflowPrebuilt.
 			declValue = declValue[1 : len(declValue)-1]
 			flagDeclaration.Value = &rc_proto.Value{Val: &rc_proto.Value_StringValue{declValue}}
 			for _, prefix := range manualFlagNamePrefixes {
 				if strings.HasPrefix(declName, prefix) {
-					workflow = rc_proto.Workflow(rc_proto.Workflow_MANUAL)
+					workflow = rc_proto.Workflow(rc_proto.Workflow_WorkflowManual)
 					break
 				}
 			}
 		case declValue == "False" || declValue == "True":
-			// Boolean values are LAUNCH flags.
+			// Boolean values are WorkflowLaunch flags.
 			flagDeclaration.Value = &rc_proto.Value{Val: &rc_proto.Value_BoolValue{declValue == "True"}}
-			workflow = rc_proto.Workflow(rc_proto.Workflow_LAUNCH)
+			workflow = rc_proto.Workflow(rc_proto.Workflow_WorkflowLaunch)
 		case declValue == "None":
-			// Use PREBUILT workflow with no initial value.
+			// Use WorkflowPrebuilt workflow with no initial value.
 		default:
 			fmt.Printf("%s: Unexpected value %s=%s\n", path, declName, declValue)
 		}
diff --git a/cmd/release_config/release_config/main.go b/cmd/release_config/release_config/main.go
index a41183f..0617838 100644
--- a/cmd/release_config/release_config/main.go
+++ b/cmd/release_config/release_config/main.go
@@ -77,7 +77,7 @@
 		panic(err)
 	}
 
-	makefilePath := filepath.Join(outputDir, fmt.Sprintf("release_config-%s-%s.mk", product, targetRelease))
+	makefilePath := filepath.Join(outputDir, fmt.Sprintf("release_config-%s-%s.varmk", product, targetRelease))
 	useProto, ok := config.FlagArtifacts["RELEASE_BUILD_FLAGS_IN_PROTOBUF"]
 	if guard && (!ok || rc_lib.MarshalValue(useProto.Value) == "") {
 		// We were told to guard operation and either we have no build flag, or it is False.
@@ -92,10 +92,10 @@
 	}
 	if allMake {
 		// Write one makefile per release config, using the canonical release name.
-		for k, _ := range configs.ReleaseConfigs {
-			if k != targetRelease {
-				makefilePath = filepath.Join(outputDir, fmt.Sprintf("release_config-%s-%s.mk", product, k))
-				err = configs.WriteMakefile(makefilePath, k)
+		for _, c := range configs.GetSortedReleaseConfigs() {
+			if c.Name != targetRelease {
+				makefilePath = filepath.Join(outputDir, fmt.Sprintf("release_config-%s-%s.varmk", product, c.Name))
+				err = configs.WriteMakefile(makefilePath, c.Name)
 				if err != nil {
 					panic(err)
 				}
diff --git a/cmd/release_config/release_config_lib/release_config.go b/cmd/release_config/release_config_lib/release_config.go
index 93410a6..82adc34 100644
--- a/cmd/release_config/release_config_lib/release_config.go
+++ b/cmd/release_config/release_config_lib/release_config.go
@@ -69,6 +69,9 @@
 	// Unmarshalled flag artifacts
 	FlagArtifacts FlagArtifacts
 
+	// The files used by this release config
+	FilesUsedMap map[string]bool
+
 	// Generated release config
 	ReleaseConfigArtifact *rc_proto.ReleaseConfigArtifact
 
@@ -80,10 +83,17 @@
 }
 
 func ReleaseConfigFactory(name string, index int) (c *ReleaseConfig) {
-	return &ReleaseConfig{Name: name, DeclarationIndex: index}
+	return &ReleaseConfig{
+		Name:             name,
+		DeclarationIndex: index,
+		FilesUsedMap:     make(map[string]bool),
+	}
 }
 
 func (config *ReleaseConfig) InheritConfig(iConfig *ReleaseConfig) error {
+	for f := range iConfig.FilesUsedMap {
+		config.FilesUsedMap[f] = true
+	}
 	for _, fa := range iConfig.FlagArtifacts {
 		name := *fa.FlagDeclaration.Name
 		myFa, ok := config.FlagArtifacts[name]
@@ -106,6 +116,17 @@
 	return nil
 }
 
+func (config *ReleaseConfig) GetSortedFileList() []string {
+	ret := []string{}
+	for k := range config.FilesUsedMap {
+		ret = append(ret, k)
+	}
+	slices.SortFunc(ret, func(a, b string) int {
+		return cmp.Compare(a, b)
+	})
+	return ret
+}
+
 func (config *ReleaseConfig) GenerateReleaseConfig(configs *ReleaseConfigs) error {
 	if config.ReleaseConfigArtifact != nil {
 		return nil
@@ -145,9 +166,18 @@
 			return err
 		}
 	}
+
+	// If we inherited nothing, then we need to mark the global files as used for this
+	// config.  If we inherited, then we already marked them as part of inheritance.
+	if len(config.InheritNames) == 0 {
+		for f := range configs.FilesUsedMap {
+			config.FilesUsedMap[f] = true
+		}
+	}
+
 	contributionsToApply = append(contributionsToApply, config.Contributions...)
 
-	workflowManual := rc_proto.Workflow(rc_proto.Workflow_MANUAL)
+	workflowManual := rc_proto.Workflow(rc_proto.Workflow_WorkflowManual)
 	myDirsMap := make(map[int]bool)
 	for _, contrib := range contributionsToApply {
 		contribAconfigValueSets := []string{}
@@ -181,8 +211,8 @@
 				return fmt.Errorf("Setting value for flag %s not allowed in %s\n", name, value.path)
 			}
 			if isRoot && *fa.FlagDeclaration.Workflow != workflowManual {
-				// The "root" release config can only contain workflow: MANUAL flags.
-				return fmt.Errorf("Setting value for non-MANUAL flag %s is not allowed in %s", name, value.path)
+				// The "root" release config can only contain workflow: WorkflowManual flags.
+				return fmt.Errorf("Setting value for non-WorkflowManual flag %s is not allowed in %s", name, value.path)
 			}
 			if err := fa.UpdateValue(*value); err != nil {
 				return err
diff --git a/cmd/release_config/release_config_lib/release_configs.go b/cmd/release_config/release_config_lib/release_configs.go
index 0b65658..65e6d90 100644
--- a/cmd/release_config/release_config_lib/release_configs.go
+++ b/cmd/release_config/release_config_lib/release_configs.go
@@ -67,6 +67,9 @@
 	// Map of directory to *ReleaseConfigMap
 	releaseConfigMapsMap map[string]*ReleaseConfigMap
 
+	// The files used by all release configs
+	FilesUsedMap map[string]bool
+
 	// The list of config directories used.
 	configDirs []string
 
@@ -102,8 +105,9 @@
 		releaseConfigMapsMap: make(map[string]*ReleaseConfigMap),
 		configDirs:           []string{},
 		configDirIndexes:     make(ReleaseConfigDirMap),
+		FilesUsedMap:         make(map[string]bool),
 	}
-	workflowManual := rc_proto.Workflow(rc_proto.Workflow_MANUAL)
+	workflowManual := rc_proto.Workflow(rc_proto.Workflow_WorkflowManual)
 	releaseAconfigValueSets := FlagArtifact{
 		FlagDeclaration: &rc_proto.FlagDeclaration{
 			Name:        proto.String("RELEASE_ACONFIG_VALUE_SETS"),
@@ -120,6 +124,16 @@
 	return &configs
 }
 
+func (configs *ReleaseConfigs) GetSortedReleaseConfigs() (ret []*ReleaseConfig) {
+	for _, config := range configs.ReleaseConfigs {
+		ret = append(ret, config)
+	}
+	slices.SortFunc(ret, func(a, b *ReleaseConfig) int {
+		return cmp.Compare(a.Name, b.Name)
+	})
+	return ret
+}
+
 func ReleaseConfigMapFactory(protoPath string) (m *ReleaseConfigMap) {
 	m = &ReleaseConfigMap{
 		path:                       protoPath,
@@ -170,6 +184,7 @@
 			return fmt.Errorf("Release config map %s has invalid container %s", path, container)
 		}
 	}
+	configs.FilesUsedMap[path] = true
 	dir := filepath.Dir(path)
 	// Record any aliases, checking for duplicates.
 	for _, alias := range m.proto.Aliases {
@@ -216,6 +231,7 @@
 			return fmt.Errorf("Duplicate definition of %s", *flagDeclaration.Name)
 		}
 		// Set the initial value in the flag artifact.
+		configs.FilesUsedMap[path] = true
 		configs.FlagArtifacts[name].UpdateValue(
 			FlagValue{path: path, proto: rc_proto.FlagValue{
 				Name: proto.String(name), Value: flagDeclaration.Value}})
@@ -239,6 +255,7 @@
 			configs.ReleaseConfigs[name] = ReleaseConfigFactory(name, ConfigDirIndex)
 		}
 		config := configs.ReleaseConfigs[name]
+		config.FilesUsedMap[path] = true
 		config.InheritNames = append(config.InheritNames, releaseConfigContribution.proto.Inherits...)
 
 		// Only walk flag_values/{RELEASE} for defined releases.
@@ -250,6 +267,7 @@
 			if *flagValue.proto.Name == "RELEASE_ACONFIG_VALUE_SETS" {
 				return fmt.Errorf("%s: %s is a reserved build flag", path, *flagValue.proto.Name)
 			}
+			config.FilesUsedMap[path] = true
 			releaseConfigContribution.FlagValues = append(releaseConfigContribution.FlagValues, flagValue)
 			return nil
 		})
@@ -283,9 +301,7 @@
 	return nil, fmt.Errorf("Missing config %s.  Trace=%v", name, trace)
 }
 
-// Write the makefile for this targetRelease.
-func (configs *ReleaseConfigs) WriteMakefile(outFile, targetRelease string) error {
-	makeVars := make(map[string]string)
+func (configs *ReleaseConfigs) GetAllReleaseNames() []string {
 	var allReleaseNames []string
 	for _, v := range configs.ReleaseConfigs {
 		allReleaseNames = append(allReleaseNames, v.Name)
@@ -294,6 +310,12 @@
 	slices.SortFunc(allReleaseNames, func(a, b string) int {
 		return cmp.Compare(a, b)
 	})
+	return allReleaseNames
+}
+
+// Write the makefile for this targetRelease.
+func (configs *ReleaseConfigs) WriteMakefile(outFile, targetRelease string) error {
+	makeVars := make(map[string]string)
 	config, err := configs.GetReleaseConfig(targetRelease)
 	if err != nil {
 		return err
@@ -356,7 +378,8 @@
 		data += fmt.Sprintf("# User specified TARGET_RELEASE=%s\n", targetRelease)
 	}
 	// The variable _all_release_configs will get deleted during processing, so do not mark it read-only.
-	data += fmt.Sprintf("_all_release_configs := %s\n", strings.Join(allReleaseNames, " "))
+	data += fmt.Sprintf("_all_release_configs := %s\n", strings.Join(configs.GetAllReleaseNames(), " "))
+	data += fmt.Sprintf("_used_files := %s\n", strings.Join(config.GetSortedFileList(), " "))
 	data += fmt.Sprintf("_ALL_RELEASE_FLAGS :=$= %s\n", strings.Join(names, " "))
 	for _, pName := range pNames {
 		data += fmt.Sprintf("_ALL_RELEASE_FLAGS.PARTITIONS.%s :=$= %s\n", pName, strings.Join(partitions[pName], " "))
@@ -388,8 +411,9 @@
 		configs.ReleaseConfigs[name].OtherNames = aliases
 	}
 
-	for _, config := range configs.ReleaseConfigs {
-		err := config.GenerateReleaseConfig(configs)
+	sortedReleaseConfigs := configs.GetSortedReleaseConfigs()
+	for _, c := range sortedReleaseConfigs {
+		err := c.GenerateReleaseConfig(configs)
 		if err != nil {
 			return err
 		}
@@ -399,17 +423,16 @@
 	if err != nil {
 		return err
 	}
+	orc := []*rc_proto.ReleaseConfigArtifact{}
+	for _, c := range sortedReleaseConfigs {
+		if c.Name != releaseConfig.Name {
+			orc = append(orc, c.ReleaseConfigArtifact)
+		}
+	}
+
 	configs.Artifact = rc_proto.ReleaseConfigsArtifact{
-		ReleaseConfig: releaseConfig.ReleaseConfigArtifact,
-		OtherReleaseConfigs: func() []*rc_proto.ReleaseConfigArtifact {
-			orc := []*rc_proto.ReleaseConfigArtifact{}
-			for name, config := range configs.ReleaseConfigs {
-				if name != releaseConfig.Name {
-					orc = append(orc, config.ReleaseConfigArtifact)
-				}
-			}
-			return orc
-		}(),
+		ReleaseConfig:       releaseConfig.ReleaseConfigArtifact,
+		OtherReleaseConfigs: orc,
 		ReleaseConfigMapsMap: func() map[string]*rc_proto.ReleaseConfigMap {
 			ret := make(map[string]*rc_proto.ReleaseConfigMap)
 			for k, v := range configs.releaseConfigMapsMap {
diff --git a/cmd/release_config/release_config_proto/build_flags_out.pb.go b/cmd/release_config/release_config_proto/build_flags_out.pb.go
index 483cffa..8fa75aa 100644
--- a/cmd/release_config/release_config_proto/build_flags_out.pb.go
+++ b/cmd/release_config/release_config_proto/build_flags_out.pb.go
@@ -11,7 +11,7 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.30.0
+// 	protoc-gen-go v1.33.0
 // 	protoc        v3.21.12
 // source: build_flags_out.proto
 
diff --git a/cmd/release_config/release_config_proto/build_flags_src.pb.go b/cmd/release_config/release_config_proto/build_flags_src.pb.go
index dded975..c52a238 100644
--- a/cmd/release_config/release_config_proto/build_flags_src.pb.go
+++ b/cmd/release_config/release_config_proto/build_flags_src.pb.go
@@ -11,7 +11,7 @@
 
 // Code generated by protoc-gen-go. DO NOT EDIT.
 // versions:
-// 	protoc-gen-go v1.30.0
+// 	protoc-gen-go v1.33.0
 // 	protoc        v3.21.12
 // source: build_flags_src.proto
 
@@ -34,30 +34,30 @@
 type Workflow int32
 
 const (
-	Workflow_UNSPECIFIED_workflow Workflow = 0
+	Workflow_WorkflowUnspecified Workflow = 0
 	// Boolean value flags that progress from false to true.
-	Workflow_LAUNCH Workflow = 1
+	Workflow_WorkflowLaunch Workflow = 1
 	// String value flags that get updated with new version strings to control
 	// prebuilt inclusion.
-	Workflow_PREBUILT Workflow = 2
+	Workflow_WorkflowPrebuilt Workflow = 2
 	// Manually managed outside flags.  These are likely to be found in a
 	// different directory than flags with other workflows.
-	Workflow_MANUAL Workflow = 3
+	Workflow_WorkflowManual Workflow = 3
 )
 
 // Enum value maps for Workflow.
 var (
 	Workflow_name = map[int32]string{
-		0: "UNSPECIFIED_workflow",
-		1: "LAUNCH",
-		2: "PREBUILT",
-		3: "MANUAL",
+		0: "WorkflowUnspecified",
+		1: "WorkflowLaunch",
+		2: "WorkflowPrebuilt",
+		3: "WorkflowManual",
 	}
 	Workflow_value = map[string]int32{
-		"UNSPECIFIED_workflow": 0,
-		"LAUNCH":               1,
-		"PREBUILT":             2,
-		"MANUAL":               3,
+		"WorkflowUnspecified": 0,
+		"WorkflowLaunch":      1,
+		"WorkflowPrebuilt":    2,
+		"WorkflowManual":      3,
 	}
 )
 
@@ -295,7 +295,7 @@
 	if x != nil && x.Workflow != nil {
 		return *x.Workflow
 	}
-	return Workflow_UNSPECIFIED_workflow
+	return Workflow_WorkflowUnspecified
 }
 
 func (x *FlagDeclaration) GetContainers() []string {
@@ -642,15 +642,17 @@
 	0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x2d, 0x0a,
 	0x12, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e,
 	0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x11, 0x64, 0x65, 0x66, 0x61, 0x75,
-	0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x2a, 0x4a, 0x0a, 0x08,
-	0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x18, 0x0a, 0x14, 0x55, 0x4e, 0x53, 0x50,
-	0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x5f, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77,
-	0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x4c, 0x41, 0x55, 0x4e, 0x43, 0x48, 0x10, 0x01, 0x12, 0x0c,
-	0x0a, 0x08, 0x50, 0x52, 0x45, 0x42, 0x55, 0x49, 0x4c, 0x54, 0x10, 0x02, 0x12, 0x0a, 0x0a, 0x06,
-	0x4d, 0x41, 0x4e, 0x55, 0x41, 0x4c, 0x10, 0x03, 0x42, 0x33, 0x5a, 0x31, 0x61, 0x6e, 0x64, 0x72,
-	0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73,
-	0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65,
-	0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
+	0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x73, 0x2a, 0x61, 0x0a, 0x08,
+	0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x12, 0x17, 0x0a, 0x13, 0x57, 0x6f, 0x72, 0x6b,
+	0x66, 0x6c, 0x6f, 0x77, 0x55, 0x6e, 0x73, 0x70, 0x65, 0x63, 0x69, 0x66, 0x69, 0x65, 0x64, 0x10,
+	0x00, 0x12, 0x12, 0x0a, 0x0e, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x4c, 0x61, 0x75,
+	0x6e, 0x63, 0x68, 0x10, 0x01, 0x12, 0x14, 0x0a, 0x10, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f,
+	0x77, 0x50, 0x72, 0x65, 0x62, 0x75, 0x69, 0x6c, 0x74, 0x10, 0x02, 0x12, 0x12, 0x0a, 0x0e, 0x57,
+	0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x4d, 0x61, 0x6e, 0x75, 0x61, 0x6c, 0x10, 0x03, 0x42,
+	0x33, 0x5a, 0x31, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67,
+	0x2f, 0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f,
+	0x72, 0x65, 0x6c, 0x65, 0x61, 0x73, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x70,
+	0x72, 0x6f, 0x74, 0x6f,
 }
 
 var (
diff --git a/cmd/release_config/release_config_proto/build_flags_src.proto b/cmd/release_config/release_config_proto/build_flags_src.proto
index 0ef1a5f..81c6ae3 100644
--- a/cmd/release_config/release_config_proto/build_flags_src.proto
+++ b/cmd/release_config/release_config_proto/build_flags_src.proto
@@ -39,18 +39,18 @@
 //      com.android.1mypackage are invalid
 
 enum workflow {
-  UNSPECIFIED_workflow = 0;
+  WorkflowUnspecified = 0;
 
   // Boolean value flags that progress from false to true.
-  LAUNCH = 1;
+  WorkflowLaunch = 1;
 
   // String value flags that get updated with new version strings to control
   // prebuilt inclusion.
-  PREBUILT = 2;
+  WorkflowPrebuilt = 2;
 
   // Manually managed outside flags.  These are likely to be found in a
   // different directory than flags with other workflows.
-  MANUAL = 3;
+  WorkflowManual = 3;
 }
 
 message value {
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index 6ab3b88..e168edc 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -59,6 +59,7 @@
 	ctx.RegisterModuleType("prebuilt_usr_keychars", PrebuiltUserKeyCharsFactory)
 	ctx.RegisterModuleType("prebuilt_usr_idc", PrebuiltUserIdcFactory)
 	ctx.RegisterModuleType("prebuilt_font", PrebuiltFontFactory)
+	ctx.RegisterModuleType("prebuilt_overlay", PrebuiltOverlayFactory)
 	ctx.RegisterModuleType("prebuilt_firmware", PrebuiltFirmwareFactory)
 	ctx.RegisterModuleType("prebuilt_dsp", PrebuiltDSPFactory)
 	ctx.RegisterModuleType("prebuilt_rfsa", PrebuiltRFSAFactory)
@@ -650,6 +651,15 @@
 	return module
 }
 
+// prebuilt_overlay is for a prebuilt artifact in <partition>/overlay directory.
+func PrebuiltOverlayFactory() android.Module {
+	module := &PrebuiltEtc{}
+	InitPrebuiltEtcModule(module, "overlay")
+	// This module is device-only
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+	return module
+}
+
 // prebuilt_firmware installs a firmware file to <partition>/etc/firmware directory for system
 // image.
 // If soc_specific property is set to true, the firmware file is installed to the
diff --git a/etc/prebuilt_etc_test.go b/etc/prebuilt_etc_test.go
index 3ee2340..c44574a 100644
--- a/etc/prebuilt_etc_test.go
+++ b/etc/prebuilt_etc_test.go
@@ -342,6 +342,19 @@
 	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath)
 }
 
+func TestPrebuiltOverlayInstallDirPath(t *testing.T) {
+	result := prepareForPrebuiltEtcTest.RunTestWithBp(t, `
+		prebuilt_overlay {
+			name: "foo.conf",
+			src: "foo.conf",
+		}
+	`)
+
+	p := result.Module("foo.conf", "android_arm64_armv8-a").(*PrebuiltEtc)
+	expected := "out/soong/target/product/test_device/system/overlay"
+	android.AssertPathRelativeToTopEquals(t, "install dir", expected, p.installDirPath)
+}
+
 func TestPrebuiltFirmwareDirPath(t *testing.T) {
 	targetPath := "out/soong/target/product/test_device"
 	tests := []struct {
diff --git a/java/androidmk.go b/java/androidmk.go
index 4f740b2..4316074 100644
--- a/java/androidmk.go
+++ b/java/androidmk.go
@@ -17,7 +17,6 @@
 import (
 	"fmt"
 	"io"
-	"strings"
 
 	"android/soong/android"
 
@@ -413,23 +412,6 @@
 				if app.embeddedJniLibs {
 					jniSymbols := app.JNISymbolsInstalls(app.installPathForJNISymbols.String())
 					entries.SetString("LOCAL_SOONG_JNI_LIBS_SYMBOLS", jniSymbols.String())
-				} else {
-					for _, jniLib := range app.jniLibs {
-						entries.AddStrings("LOCAL_SOONG_JNI_LIBS_"+jniLib.target.Arch.ArchType.String(), jniLib.name)
-						var partitionTag string
-
-						// Mimic the creation of partition_tag in build/make,
-						// which defaults to an empty string when the partition is system.
-						// Otherwise, capitalize with a leading _
-						if jniLib.partition == "system" {
-							partitionTag = ""
-						} else {
-							split := strings.Split(jniLib.partition, "/")
-							partitionTag = "_" + strings.ToUpper(split[len(split)-1])
-						}
-						entries.AddStrings("LOCAL_SOONG_JNI_LIBS_PARTITION_"+jniLib.target.Arch.ArchType.String(),
-							jniLib.name+":"+partitionTag)
-					}
 				}
 
 				if len(app.jniCoverageOutputs) > 0 {
diff --git a/java/androidmk_test.go b/java/androidmk_test.go
index 2978a40..875e06f 100644
--- a/java/androidmk_test.go
+++ b/java/androidmk_test.go
@@ -19,9 +19,6 @@
 	"testing"
 
 	"android/soong/android"
-	"android/soong/cc"
-
-	"github.com/google/blueprint/proptools"
 )
 
 func TestRequired(t *testing.T) {
@@ -255,149 +252,3 @@
 		android.AssertDeepEquals(t, "overrides property", expected.overrides, actual)
 	}
 }
-
-func TestJniPartition(t *testing.T) {
-	bp := `
-		cc_library {
-			name: "libjni_system",
-			system_shared_libs: [],
-			sdk_version: "current",
-			stl: "none",
-		}
-
-		cc_library {
-			name: "libjni_system_ext",
-			system_shared_libs: [],
-			sdk_version: "current",
-			stl: "none",
-			system_ext_specific: true,
-		}
-
-		cc_library {
-			name: "libjni_odm",
-			system_shared_libs: [],
-			sdk_version: "current",
-			stl: "none",
-			device_specific: true,
-		}
-
-		cc_library {
-			name: "libjni_product",
-			system_shared_libs: [],
-			sdk_version: "current",
-			stl: "none",
-			product_specific: true,
-		}
-
-		cc_library {
-			name: "libjni_vendor",
-			system_shared_libs: [],
-			sdk_version: "current",
-			stl: "none",
-			soc_specific: true,
-		}
-
-		android_app {
-			name: "test_app_system_jni_system",
-			privileged: true,
-			platform_apis: true,
-			certificate: "platform",
-			jni_libs: ["libjni_system"],
-		}
-
-		android_app {
-			name: "test_app_system_jni_system_ext",
-			privileged: true,
-			platform_apis: true,
-			certificate: "platform",
-			jni_libs: ["libjni_system_ext"],
-		}
-
-		android_app {
-			name: "test_app_system_ext_jni_system",
-			privileged: true,
-			platform_apis: true,
-			certificate: "platform",
-			jni_libs: ["libjni_system"],
-			system_ext_specific: true
-		}
-
-		android_app {
-			name: "test_app_system_ext_jni_system_ext",
-			sdk_version: "core_platform",
-			jni_libs: ["libjni_system_ext"],
-			system_ext_specific: true
-		}
-
-		android_app {
-			name: "test_app_product_jni_product",
-			sdk_version: "core_platform",
-			jni_libs: ["libjni_product"],
-			product_specific: true
-		}
-
-		android_app {
-			name: "test_app_vendor_jni_odm",
-			sdk_version: "core_platform",
-			jni_libs: ["libjni_odm"],
-			soc_specific: true
-		}
-
-		android_app {
-			name: "test_app_odm_jni_vendor",
-			sdk_version: "core_platform",
-			jni_libs: ["libjni_vendor"],
-			device_specific: true
-		}
-		android_app {
-			name: "test_app_system_jni_multiple",
-			privileged: true,
-			platform_apis: true,
-			certificate: "platform",
-			jni_libs: ["libjni_system", "libjni_system_ext"],
-		}
-		android_app {
-			name: "test_app_vendor_jni_multiple",
-			sdk_version: "core_platform",
-			jni_libs: ["libjni_odm", "libjni_vendor"],
-			soc_specific: true
-		}
-		`
-	arch := "arm64"
-	ctx := android.GroupFixturePreparers(
-		PrepareForTestWithJavaDefaultModules,
-		cc.PrepareForTestWithCcDefaultModules,
-		android.PrepareForTestWithAndroidMk,
-		android.FixtureModifyConfig(func(config android.Config) {
-			config.TestProductVariables.DeviceArch = proptools.StringPtr(arch)
-		}),
-	).
-		RunTestWithBp(t, bp)
-	testCases := []struct {
-		name           string
-		partitionNames []string
-		partitionTags  []string
-	}{
-		{"test_app_system_jni_system", []string{"libjni_system"}, []string{""}},
-		{"test_app_system_jni_system_ext", []string{"libjni_system_ext"}, []string{"_SYSTEM_EXT"}},
-		{"test_app_system_ext_jni_system", []string{"libjni_system"}, []string{""}},
-		{"test_app_system_ext_jni_system_ext", []string{"libjni_system_ext"}, []string{"_SYSTEM_EXT"}},
-		{"test_app_product_jni_product", []string{"libjni_product"}, []string{"_PRODUCT"}},
-		{"test_app_vendor_jni_odm", []string{"libjni_odm"}, []string{"_ODM"}},
-		{"test_app_odm_jni_vendor", []string{"libjni_vendor"}, []string{"_VENDOR"}},
-		{"test_app_system_jni_multiple", []string{"libjni_system", "libjni_system_ext"}, []string{"", "_SYSTEM_EXT"}},
-		{"test_app_vendor_jni_multiple", []string{"libjni_odm", "libjni_vendor"}, []string{"_ODM", "_VENDOR"}},
-	}
-
-	for _, test := range testCases {
-		t.Run(test.name, func(t *testing.T) {
-			mod := ctx.ModuleForTests(test.name, "android_common").Module()
-			entry := android.AndroidMkEntriesForTest(t, ctx.TestContext, mod)[0]
-			for i := range test.partitionNames {
-				actual := entry.EntryMap["LOCAL_SOONG_JNI_LIBS_PARTITION_"+arch][i]
-				expected := test.partitionNames[i] + ":" + test.partitionTags[i]
-				android.AssertStringEquals(t, "Expected and actual differ", expected, actual)
-			}
-		})
-	}
-}
diff --git a/java/app.go b/java/app.go
index f05b8a7..377851e 100644
--- a/java/app.go
+++ b/java/app.go
@@ -274,16 +274,37 @@
 		variation := append(jniTarget.Variations(),
 			blueprint.Variation{Mutator: "link", Variation: "shared"})
 
-		// If the app builds against an Android SDK use the SDK variant of JNI dependencies
-		// unless jni_uses_platform_apis is set.
-		// Don't require the SDK variant for apps that are shipped on vendor, etc., as they already
-		// have stable APIs through the VNDK.
-		if (usesSDK && !a.RequiresStableAPIs(ctx) &&
-			!Bool(a.appProperties.Jni_uses_platform_apis)) ||
-			Bool(a.appProperties.Jni_uses_sdk_apis) {
+		// Test whether to use the SDK variant or the non-SDK variant of JNI dependencies.
+		// Many factors are considered here.
+		// 1. Basically, the selection follows whether the app has sdk_version set or not.
+		jniUsesSdkVariant := usesSDK
+		// 2. However, jni_uses_platform_apis and jni_uses_sdk_apis can override it
+		if Bool(a.appProperties.Jni_uses_sdk_apis) {
+			jniUsesSdkVariant = true
+		}
+		if Bool(a.appProperties.Jni_uses_platform_apis) {
+			jniUsesSdkVariant = false
+		}
+		// 3. Then the use of SDK variant is again prohibited for the following cases:
+		// 3.1. the app is shipped on unbundled partitions like vendor. Since the entire
+		// partition (not only the app) is considered unbudled, there's no need to use the
+		// SDK variant.
+		// 3.2. the app doesn't support embedding the JNI libs
+		if a.RequiresStableAPIs(ctx) || !a.shouldEmbedJnis(ctx) {
+			jniUsesSdkVariant = false
+		}
+		if jniUsesSdkVariant {
 			variation = append(variation, blueprint.Variation{Mutator: "sdk", Variation: "sdk"})
 		}
-		ctx.AddFarVariationDependencies(variation, jniLibTag, a.appProperties.Jni_libs...)
+
+		// Use the installable dep tag when the JNIs are not embedded
+		var tag dependencyTag
+		if a.shouldEmbedJnis(ctx) {
+			tag = jniLibTag
+		} else {
+			tag = jniInstallTag
+		}
+		ctx.AddFarVariationDependencies(variation, tag, a.appProperties.Jni_libs...)
 	}
 	for _, aconfig_declaration := range a.aaptProperties.Flags_packages {
 		ctx.AddDependency(ctx.Module(), aconfigDeclarationTag, aconfig_declaration)
@@ -334,6 +355,7 @@
 
 func (a *AndroidApp) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	a.checkAppSdkVersions(ctx)
+	a.checkEmbedJnis(ctx)
 	a.generateAndroidBuildActions(ctx)
 	a.generateJavaUsedByApex(ctx)
 }
@@ -378,6 +400,17 @@
 	a.checkSdkVersions(ctx)
 }
 
+// Ensures that use_embedded_native_libs are set for apk-in-apex
+func (a *AndroidApp) checkEmbedJnis(ctx android.BaseModuleContext) {
+	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
+	apkInApex := !apexInfo.IsForPlatform()
+	hasJnis := len(a.appProperties.Jni_libs) > 0
+
+	if apkInApex && hasJnis && !Bool(a.appProperties.Use_embedded_native_libs) {
+		ctx.ModuleErrorf("APK in APEX should have use_embedded_native_libs: true")
+	}
+}
+
 // If an updatable APK sets min_sdk_version, min_sdk_vesion of JNI libs should match with it.
 // This check is enforced for "updatable" APKs (including APK-in-APEX).
 func (a *AndroidApp) checkJniLibsSdkVersion(ctx android.ModuleContext, minSdkVersion android.ApiLevel) {
@@ -433,9 +466,9 @@
 }
 
 func (a *AndroidApp) shouldEmbedJnis(ctx android.BaseModuleContext) bool {
-	apexInfo, _ := android.ModuleProvider(ctx, android.ApexInfoProvider)
 	return ctx.Config().UnbundledBuild() || Bool(a.appProperties.Use_embedded_native_libs) ||
-		!apexInfo.IsForPlatform() || a.appProperties.AlwaysPackageNativeLibs
+		Bool(a.appProperties.Updatable) ||
+		a.appProperties.AlwaysPackageNativeLibs
 }
 
 func generateAaptRenamePackageFlags(packageName string, renameResourcesPackage bool) []string {
@@ -829,7 +862,9 @@
 
 	dexJarFile, packageResources := a.dexBuildActions(ctx)
 
-	jniLibs, prebuiltJniPackages, certificates := collectAppDeps(ctx, a, a.shouldEmbedJnis(ctx), !Bool(a.appProperties.Jni_uses_platform_apis))
+	// No need to check the SDK version of the JNI deps unless we embed them
+	checkNativeSdkVersion := a.shouldEmbedJnis(ctx) && !Bool(a.appProperties.Jni_uses_platform_apis)
+	jniLibs, prebuiltJniPackages, certificates := collectAppDeps(ctx, a, a.shouldEmbedJnis(ctx), checkNativeSdkVersion)
 	jniJarFile := a.jniBuildActions(jniLibs, prebuiltJniPackages, ctx)
 
 	if ctx.Failed() {
@@ -911,6 +946,22 @@
 			installed := ctx.InstallFile(a.installDir, extra.Base(), extra)
 			extraInstalledPaths = append(extraInstalledPaths, installed)
 		}
+		// If we don't embed jni libs, make sure that those are installed along with the
+		// app, and also place symlinks to the installed paths under the lib/<arch>
+		// directory of the app installation directory. ex:
+		// /system/app/MyApp/lib/arm64/libfoo.so -> /system/lib64/libfoo.so
+		if !a.embeddedJniLibs {
+			for _, jniLib := range jniLibs {
+				archStr := jniLib.target.Arch.ArchType.String()
+				symlinkDir := a.installDir.Join(ctx, "lib", archStr)
+				for _, installedLib := range jniLib.installPaths {
+					// install the symlink itself
+					symlinkName := installedLib.Base()
+					symlinkTarget := android.InstallPathToOnDevicePath(ctx, installedLib)
+					ctx.InstallAbsoluteSymlink(symlinkDir, symlinkName, symlinkTarget)
+				}
+			}
+		}
 		ctx.InstallFile(a.installDir, a.outputFile.Base(), a.outputFile, extraInstalledPaths...)
 	}
 
@@ -998,6 +1049,7 @@
 						coverageFile:   dep.CoverageOutputFile(),
 						unstrippedFile: dep.UnstrippedOutputFile(),
 						partition:      dep.Partition(),
+						installPaths:   dep.FilesToInstall(),
 					})
 				} else if ctx.Config().AllowMissingDependencies() {
 					ctx.AddMissingDependencies([]string{otherName})
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index 82a34ca..4d3d794 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -590,13 +590,36 @@
 		// So ignore it even if it is not in PRODUCT_APEX_BOOT_JARS.
 		// TODO(b/202896428): Add better way to handle this.
 		_, unknown = android.RemoveFromList("android.car-module", unknown)
-		if isActiveModule(ctx, ctx.Module()) && len(unknown) > 0 {
-			ctx.ModuleErrorf("%s in contents must also be declared in PRODUCT_APEX_BOOT_JARS", unknown)
+		if isApexVariant(ctx) && len(unknown) > 0 {
+			if android.IsModulePrebuilt(ctx.Module()) {
+				// prebuilt bcpf. the validation of this will be done at the top-level apex
+				providerClasspathFragmentValidationInfoProvider(ctx, unknown)
+			} else if !disableSourceApexVariant(ctx) {
+				// source bcpf, and prebuilt apex are not selected.
+				ctx.ModuleErrorf("%s in contents must also be declared in PRODUCT_APEX_BOOT_JARS", unknown)
+			}
 		}
 	}
 	return jars
 }
 
+var ClasspathFragmentValidationInfoProvider = blueprint.NewProvider[ClasspathFragmentValidationInfo]()
+
+type ClasspathFragmentValidationInfo struct {
+	ClasspathFragmentModuleName string
+	UnknownJars                 []string
+}
+
+// Set a provider with the list of jars that have not been added to PRODUCT_APEX_BOOT_JARS
+// The validation will be done in the ctx of the top-level _selected_ apex
+func providerClasspathFragmentValidationInfoProvider(ctx android.ModuleContext, unknown []string) {
+	info := ClasspathFragmentValidationInfo{
+		ClasspathFragmentModuleName: ctx.ModuleName(),
+		UnknownJars:                 unknown,
+	}
+	android.SetProvider(ctx, ClasspathFragmentValidationInfoProvider, info)
+}
+
 // generateHiddenAPIBuildActions generates all the hidden API related build rules.
 func (b *BootclasspathFragmentModule) generateHiddenAPIBuildActions(ctx android.ModuleContext, contents []android.Module, fragments []android.Module) *HiddenAPIOutput {
 
diff --git a/java/java.go b/java/java.go
index 0df96a3..05ef5d0 100644
--- a/java/java.go
+++ b/java/java.go
@@ -366,14 +366,14 @@
 	toolchain bool
 
 	static bool
+
+	installable bool
 }
 
-// installDependencyTag is a dependency tag that is annotated to cause the installed files of the
-// dependency to be installed when the parent module is installed.
-type installDependencyTag struct {
-	blueprint.BaseDependencyTag
-	android.InstallAlwaysNeededDependencyTag
-	name string
+var _ android.InstallNeededDependencyTag = (*dependencyTag)(nil)
+
+func (d dependencyTag) InstallDepNeeded() bool {
+	return d.installable
 }
 
 func (d dependencyTag) LicenseAnnotations() []android.LicenseAnnotation {
@@ -405,7 +405,7 @@
 }
 
 func IsJniDepTag(depTag blueprint.DependencyTag) bool {
-	return depTag == jniLibTag
+	return depTag == jniLibTag || depTag == jniInstallTag
 }
 
 var (
@@ -434,8 +434,8 @@
 	javaApiContributionTag  = dependencyTag{name: "java-api-contribution"}
 	depApiSrcsTag           = dependencyTag{name: "dep-api-srcs"}
 	aconfigDeclarationTag   = dependencyTag{name: "aconfig-declaration"}
-	jniInstallTag           = installDependencyTag{name: "jni install"}
-	binaryInstallTag        = installDependencyTag{name: "binary install"}
+	jniInstallTag           = dependencyTag{name: "jni install", runtimeLinked: true, installable: true}
+	binaryInstallTag        = dependencyTag{name: "binary install", runtimeLinked: true, installable: true}
 	usesLibReqTag           = makeUsesLibraryDependencyTag(dexpreopt.AnySdkVersion, false)
 	usesLibOptTag           = makeUsesLibraryDependencyTag(dexpreopt.AnySdkVersion, true)
 	usesLibCompat28OptTag   = makeUsesLibraryDependencyTag(28, true)
@@ -491,6 +491,7 @@
 	coverageFile   android.OptionalPath
 	unstrippedFile android.Path
 	partition      string
+	installPaths   android.InstallPaths
 }
 
 func sdkDeps(ctx android.BottomUpMutatorContext, sdkContext android.SdkContext, d dexer) {