Merge "Switch SBOM generation of products to Soong." into main
diff --git a/Android.bp b/Android.bp
index c37d777..8d1280c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -104,7 +104,6 @@
 // Instantiate the dex_bootjars singleton module.
 dex_bootjars {
     name: "dex_bootjars",
-    no_full_install: true,
 }
 
 // Pseudo-test that's run on checkbuilds to ensure that get_clang_version can
@@ -141,3 +140,12 @@
         "//packages/modules/Virtualization/build/microdroid",
     ],
 }
+
+build_prop {
+    name: "system_ext-build.prop",
+    stem: "build.prop",
+    system_ext_specific: true,
+    product_config: ":product_config",
+    relative_install_path: "etc", // system_ext/etc/build.prop
+    visibility: ["//visibility:private"],
+}
diff --git a/android/apex_contributions.go b/android/apex_contributions.go
index 8b72f8e..4cd8dda 100644
--- a/android/apex_contributions.go
+++ b/android/apex_contributions.go
@@ -119,7 +119,10 @@
 func (a *allApexContributions) SetPrebuiltSelectionInfoProvider(ctx BaseModuleContext) {
 	addContentsToProvider := func(p *PrebuiltSelectionInfoMap, m *apexContributions) {
 		for _, content := range m.Contents() {
-			if !ctx.OtherModuleExists(content) && !ctx.Config().AllowMissingDependencies() {
+			// Verify that the module listed in contents exists in the tree
+			// Remove the prebuilt_ prefix to account for partner worksapces where the source module does not
+			// exist, and PrebuiltRenameMutator renames `prebuilt_foo` to `foo`
+			if !ctx.OtherModuleExists(content) && !ctx.OtherModuleExists(RemoveOptionalPrebuiltPrefix(content)) && !ctx.Config().AllowMissingDependencies() {
 				ctx.ModuleErrorf("%s listed in apex_contributions %s does not exist\n", content, m.Name())
 			}
 			pi := &PrebuiltSelectionInfo{
diff --git a/android/build_prop.go b/android/build_prop.go
index 14e5e23..d48d94d 100644
--- a/android/build_prop.go
+++ b/android/build_prop.go
@@ -37,6 +37,9 @@
 
 	// Path to a JSON file containing product configs.
 	Product_config *string `android:"path"`
+
+	// Optional subdirectory under which this file is installed into
+	Relative_install_path *string
 }
 
 type buildPropModule struct {
@@ -56,6 +59,8 @@
 	partition := p.PartitionTag(ctx.DeviceConfig())
 	if partition == "system" {
 		return ctx.Config().SystemPropFiles(ctx)
+	} else if partition == "system_ext" {
+		return ctx.Config().SystemExtPropFiles(ctx)
 	}
 	return nil
 }
@@ -84,8 +89,8 @@
 	}
 
 	partition := p.PartitionTag(ctx.DeviceConfig())
-	if partition != "system" {
-		ctx.PropertyErrorf("partition", "unsupported partition %q: only \"system\" is supported", partition)
+	if partition != "system" && partition != "system_ext" {
+		ctx.PropertyErrorf("partition", "unsupported partition %q: only \"system\" and \"system_ext\" are supported", partition)
 		return
 	}
 
@@ -134,7 +139,7 @@
 
 	rule.Build(ctx.ModuleName(), "generating build.prop")
 
-	p.installPath = PathForModuleInstall(ctx)
+	p.installPath = PathForModuleInstall(ctx, proptools.String(p.properties.Relative_install_path))
 	ctx.InstallFile(p.installPath, p.stem(), p.outputFilePath)
 
 	ctx.SetOutputFiles(Paths{p.outputFilePath}, "")
diff --git a/android/config.go b/android/config.go
index 40a73d6..727a27f 100644
--- a/android/config.go
+++ b/android/config.go
@@ -2004,41 +2004,41 @@
 }
 
 var (
-	mainlineApexContributionBuildFlags = []string{
-		"RELEASE_APEX_CONTRIBUTIONS_ADBD",
-		"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES",
-		"RELEASE_APEX_CONTRIBUTIONS_APPSEARCH",
-		"RELEASE_APEX_CONTRIBUTIONS_ART",
-		"RELEASE_APEX_CONTRIBUTIONS_BLUETOOTH",
-		"RELEASE_APEX_CONTRIBUTIONS_CAPTIVEPORTALLOGIN",
-		"RELEASE_APEX_CONTRIBUTIONS_CELLBROADCAST",
-		"RELEASE_APEX_CONTRIBUTIONS_CONFIGINFRASTRUCTURE",
-		"RELEASE_APEX_CONTRIBUTIONS_CONNECTIVITY",
-		"RELEASE_APEX_CONTRIBUTIONS_CONSCRYPT",
-		"RELEASE_APEX_CONTRIBUTIONS_CRASHRECOVERY",
-		"RELEASE_APEX_CONTRIBUTIONS_DEVICELOCK",
-		"RELEASE_APEX_CONTRIBUTIONS_DOCUMENTSUIGOOGLE",
-		"RELEASE_APEX_CONTRIBUTIONS_EXTSERVICES",
-		"RELEASE_APEX_CONTRIBUTIONS_HEALTHFITNESS",
-		"RELEASE_APEX_CONTRIBUTIONS_IPSEC",
-		"RELEASE_APEX_CONTRIBUTIONS_MEDIA",
-		"RELEASE_APEX_CONTRIBUTIONS_MEDIAPROVIDER",
-		"RELEASE_APEX_CONTRIBUTIONS_MODULE_METADATA",
-		"RELEASE_APEX_CONTRIBUTIONS_NETWORKSTACKGOOGLE",
-		"RELEASE_APEX_CONTRIBUTIONS_NEURALNETWORKS",
-		"RELEASE_APEX_CONTRIBUTIONS_ONDEVICEPERSONALIZATION",
-		"RELEASE_APEX_CONTRIBUTIONS_PERMISSION",
-		"RELEASE_APEX_CONTRIBUTIONS_PRIMARY_LIBS",
-		"RELEASE_APEX_CONTRIBUTIONS_REMOTEKEYPROVISIONING",
-		"RELEASE_APEX_CONTRIBUTIONS_RESOLV",
-		"RELEASE_APEX_CONTRIBUTIONS_SCHEDULING",
-		"RELEASE_APEX_CONTRIBUTIONS_SDKEXTENSIONS",
-		"RELEASE_APEX_CONTRIBUTIONS_SWCODEC",
-		"RELEASE_APEX_CONTRIBUTIONS_STATSD",
-		"RELEASE_APEX_CONTRIBUTIONS_TELEMETRY_TVP",
-		"RELEASE_APEX_CONTRIBUTIONS_TZDATA",
-		"RELEASE_APEX_CONTRIBUTIONS_UWB",
-		"RELEASE_APEX_CONTRIBUTIONS_WIFI",
+	mainlineApexContributionBuildFlagsToApexNames = map[string]string{
+		"RELEASE_APEX_CONTRIBUTIONS_ADBD":                    "com.android.adbd",
+		"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES":              "com.android.adservices",
+		"RELEASE_APEX_CONTRIBUTIONS_APPSEARCH":               "com.android.appsearch",
+		"RELEASE_APEX_CONTRIBUTIONS_ART":                     "com.android.art",
+		"RELEASE_APEX_CONTRIBUTIONS_BLUETOOTH":               "com.android.btservices",
+		"RELEASE_APEX_CONTRIBUTIONS_CAPTIVEPORTALLOGIN":      "",
+		"RELEASE_APEX_CONTRIBUTIONS_CELLBROADCAST":           "com.android.cellbroadcast",
+		"RELEASE_APEX_CONTRIBUTIONS_CONFIGINFRASTRUCTURE":    "com.android.configinfrastructure",
+		"RELEASE_APEX_CONTRIBUTIONS_CONNECTIVITY":            "com.android.tethering",
+		"RELEASE_APEX_CONTRIBUTIONS_CONSCRYPT":               "com.android.conscrypt",
+		"RELEASE_APEX_CONTRIBUTIONS_CRASHRECOVERY":           "",
+		"RELEASE_APEX_CONTRIBUTIONS_DEVICELOCK":              "com.android.devicelock",
+		"RELEASE_APEX_CONTRIBUTIONS_DOCUMENTSUIGOOGLE":       "",
+		"RELEASE_APEX_CONTRIBUTIONS_EXTSERVICES":             "com.android.extservices",
+		"RELEASE_APEX_CONTRIBUTIONS_HEALTHFITNESS":           "com.android.healthfitness",
+		"RELEASE_APEX_CONTRIBUTIONS_IPSEC":                   "com.android.ipsec",
+		"RELEASE_APEX_CONTRIBUTIONS_MEDIA":                   "com.android.media",
+		"RELEASE_APEX_CONTRIBUTIONS_MEDIAPROVIDER":           "com.android.mediaprovider",
+		"RELEASE_APEX_CONTRIBUTIONS_MODULE_METADATA":         "",
+		"RELEASE_APEX_CONTRIBUTIONS_NETWORKSTACKGOOGLE":      "",
+		"RELEASE_APEX_CONTRIBUTIONS_NEURALNETWORKS":          "com.android.neuralnetworks",
+		"RELEASE_APEX_CONTRIBUTIONS_ONDEVICEPERSONALIZATION": "com.android.ondevicepersonalization",
+		"RELEASE_APEX_CONTRIBUTIONS_PERMISSION":              "com.android.permission",
+		"RELEASE_APEX_CONTRIBUTIONS_PRIMARY_LIBS":            "",
+		"RELEASE_APEX_CONTRIBUTIONS_REMOTEKEYPROVISIONING":   "com.android.rkpd",
+		"RELEASE_APEX_CONTRIBUTIONS_RESOLV":                  "com.android.resolv",
+		"RELEASE_APEX_CONTRIBUTIONS_SCHEDULING":              "com.android.scheduling",
+		"RELEASE_APEX_CONTRIBUTIONS_SDKEXTENSIONS":           "com.android.sdkext",
+		"RELEASE_APEX_CONTRIBUTIONS_SWCODEC":                 "com.android.media.swcodec",
+		"RELEASE_APEX_CONTRIBUTIONS_STATSD":                  "com.android.os.statsd",
+		"RELEASE_APEX_CONTRIBUTIONS_TELEMETRY_TVP":           "",
+		"RELEASE_APEX_CONTRIBUTIONS_TZDATA":                  "com.android.tzdata",
+		"RELEASE_APEX_CONTRIBUTIONS_UWB":                     "com.android.uwb",
+		"RELEASE_APEX_CONTRIBUTIONS_WIFI":                    "com.android.wifi",
 	}
 )
 
@@ -2046,7 +2046,7 @@
 // Each mainline module will have one entry in the list
 func (c *config) AllApexContributions() []string {
 	ret := []string{}
-	for _, f := range mainlineApexContributionBuildFlags {
+	for _, f := range SortedKeys(mainlineApexContributionBuildFlagsToApexNames) {
 		if val, exists := c.GetBuildFlag(f); exists && val != "" {
 			ret = append(ret, val)
 		}
@@ -2054,6 +2054,10 @@
 	return ret
 }
 
+func (c *config) AllMainlineApexNames() []string {
+	return SortedStringValues(mainlineApexContributionBuildFlagsToApexNames)
+}
+
 func (c *config) BuildIgnoreApexContributionContents() *bool {
 	return c.productVariables.BuildIgnoreApexContributionContents
 }
@@ -2086,6 +2090,10 @@
 	return PathsForSource(ctx, c.productVariables.SystemPropFiles)
 }
 
+func (c *config) SystemExtPropFiles(ctx PathContext) Paths {
+	return PathsForSource(ctx, c.productVariables.SystemExtPropFiles)
+}
+
 func (c *config) EnableUffdGc() string {
 	return String(c.productVariables.EnableUffdGc)
 }
diff --git a/android/prebuilt.go b/android/prebuilt.go
index 8856d26..9c8c130 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -419,15 +419,7 @@
 // The metadata will be used for source vs prebuilts selection
 func PrebuiltSourceDepsMutator(ctx BottomUpMutatorContext) {
 	m := ctx.Module()
-	// If this module is a prebuilt, is enabled and has not been renamed to source then add a
-	// dependency onto the source if it is present.
-	if p := GetEmbeddedPrebuilt(m); p != nil && m.Enabled(ctx) && !p.properties.PrebuiltRenamedToSource {
-		bmn, _ := m.(baseModuleName)
-		name := bmn.BaseModuleName()
-		if ctx.OtherModuleReverseDependencyVariantExists(name) {
-			ctx.AddReverseDependency(ctx.Module(), PrebuiltDepTag, name)
-			p.properties.SourceExists = true
-		}
+	if p := GetEmbeddedPrebuilt(m); p != nil {
 		// Add a dependency from the prebuilt to the `all_apex_contributions`
 		// metadata module
 		// TODO: When all branches contain this singleton module, make this strict
@@ -435,7 +427,16 @@
 		if ctx.OtherModuleExists("all_apex_contributions") {
 			ctx.AddDependency(m, AcDepTag, "all_apex_contributions")
 		}
-
+		if m.Enabled(ctx) && !p.properties.PrebuiltRenamedToSource {
+			// If this module is a prebuilt, is enabled and has not been renamed to source then add a
+			// dependency onto the source if it is present.
+			bmn, _ := m.(baseModuleName)
+			name := bmn.BaseModuleName()
+			if ctx.OtherModuleReverseDependencyVariantExists(name) {
+				ctx.AddReverseDependency(ctx.Module(), PrebuiltDepTag, name)
+				p.properties.SourceExists = true
+			}
+		}
 	}
 }
 
@@ -664,12 +665,37 @@
 	return p.srcsSupplier != nil && len(p.srcsSupplier(ctx, prebuilt)) == 0
 }
 
+type apexVariationName interface {
+	ApexVariationName() string
+}
+
 // usePrebuilt returns true if a prebuilt should be used instead of the source module.  The prebuilt
 // will be used if it is marked "prefer" or if the source module is disabled.
 func (p *Prebuilt) usePrebuilt(ctx BaseMutatorContext, source Module, prebuilt Module) bool {
+	isMainlinePrebuilt := func(prebuilt Module) bool {
+		apex, ok := prebuilt.(apexVariationName)
+		if !ok {
+			return false
+		}
+		// Prebuilts of aosp apexes in prebuilts/runtime
+		// Used in minimal art branches
+		if prebuilt.base().BaseModuleName() == apex.ApexVariationName() {
+			return false
+		}
+		return InList(apex.ApexVariationName(), ctx.Config().AllMainlineApexNames())
+	}
+
 	// Use `all_apex_contributions` for source vs prebuilt selection.
 	psi := PrebuiltSelectionInfoMap{}
-	ctx.VisitDirectDepsWithTag(PrebuiltDepTag, func(am Module) {
+	var psiDepTag blueprint.DependencyTag
+	if p := GetEmbeddedPrebuilt(ctx.Module()); p != nil {
+		// This is a prebuilt module, visit all_apex_contributions to get the info
+		psiDepTag = AcDepTag
+	} else {
+		// This is a source module, visit any of its prebuilts to get the info
+		psiDepTag = PrebuiltDepTag
+	}
+	ctx.VisitDirectDepsWithTag(psiDepTag, func(am Module) {
 		psi, _ = OtherModuleProvider(ctx, am, PrebuiltSelectionInfoProvider)
 	})
 
@@ -682,6 +708,11 @@
 		return true
 	}
 
+	// If this is a mainline prebuilt, but has not been flagged, hide it.
+	if isMainlinePrebuilt(prebuilt) {
+		return false
+	}
+
 	// If the baseModuleName could not be found in the metadata module,
 	// fall back to the existing source vs prebuilt selection.
 	// TODO: Drop the fallback mechanisms
diff --git a/android/variable.go b/android/variable.go
index a715e0e..c9b29f1 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -508,7 +508,8 @@
 
 	ArtTargetIncludeDebugBuild *bool `json:",omitempty"`
 
-	SystemPropFiles []string `json:",omitempty"`
+	SystemPropFiles    []string `json:",omitempty"`
+	SystemExtPropFiles []string `json:",omitempty"`
 
 	EnableUffdGc *string `json:",omitempty"`
 }
diff --git a/apex/apex_test.go b/apex/apex_test.go
index cdf16dd..d200e92 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -11465,6 +11465,118 @@
 	}
 }
 
+// Test that product packaging installs the selected mainline module in workspaces withtout source mainline module
+func TestInstallationRulesForMultipleApexPrebuiltsWithoutSource(t *testing.T) {
+	// for a mainline module family, check that only the flagged soong module is visible to make
+	checkHideFromMake := func(t *testing.T, ctx *android.TestContext, visibleModuleNames []string, hiddenModuleNames []string) {
+		variation := func(moduleName string) string {
+			ret := "android_common_com.android.adservices"
+			if moduleName == "com.google.android.foo" {
+				ret = "android_common_com.google.android.foo_com.google.android.foo"
+			}
+			return ret
+		}
+
+		for _, visibleModuleName := range visibleModuleNames {
+			visibleModule := ctx.ModuleForTests(visibleModuleName, variation(visibleModuleName)).Module()
+			android.AssertBoolEquals(t, "Apex "+visibleModuleName+" selected using apex_contributions should be visible to make", false, visibleModule.IsHideFromMake())
+		}
+
+		for _, hiddenModuleName := range hiddenModuleNames {
+			hiddenModule := ctx.ModuleForTests(hiddenModuleName, variation(hiddenModuleName)).Module()
+			android.AssertBoolEquals(t, "Apex "+hiddenModuleName+" not selected using apex_contributions should be hidden from make", true, hiddenModule.IsHideFromMake())
+
+		}
+	}
+
+	bp := `
+		apex_key {
+			name: "com.android.adservices.key",
+			public_key: "com.android.adservices.avbpubkey",
+			private_key: "com.android.adservices.pem",
+		}
+
+		// AOSP source apex
+		apex {
+			name: "com.android.adservices",
+			key: "com.android.adservices.key",
+			updatable: false,
+		}
+
+		// Prebuilt Google APEX.
+
+		prebuilt_apex {
+			name: "com.google.android.adservices",
+			apex_name: "com.android.adservices",
+			src: "com.android.foo-arm.apex",
+		}
+
+		// Another Prebuilt Google APEX
+		prebuilt_apex {
+			name: "com.google.android.adservices.v2",
+			apex_name: "com.android.adservices",
+			src: "com.android.foo-arm.apex",
+		}
+
+		// APEX contribution modules
+
+
+		apex_contributions {
+			name: "adservices.prebuilt.contributions",
+			api_domain: "com.android.adservices",
+			contents: ["prebuilt_com.google.android.adservices"],
+		}
+
+		apex_contributions {
+			name: "adservices.prebuilt.v2.contributions",
+			api_domain: "com.android.adservices",
+			contents: ["prebuilt_com.google.android.adservices.v2"],
+		}
+	`
+
+	testCases := []struct {
+		desc                       string
+		selectedApexContributions  string
+		expectedVisibleModuleNames []string
+		expectedHiddenModuleNames  []string
+	}{
+		{
+			desc:                       "No apex contributions selected, source aosp apex should be visible, and mainline prebuilts should be hidden",
+			selectedApexContributions:  "",
+			expectedVisibleModuleNames: []string{"com.android.adservices"},
+			expectedHiddenModuleNames:  []string{"com.google.android.adservices", "com.google.android.adservices.v2"},
+		},
+		{
+			desc:                       "Prebuilt apex prebuilt_com.android.foo is selected",
+			selectedApexContributions:  "adservices.prebuilt.contributions",
+			expectedVisibleModuleNames: []string{"com.android.adservices", "com.google.android.adservices"},
+			expectedHiddenModuleNames:  []string{"com.google.android.adservices.v2"},
+		},
+		{
+			desc:                       "Prebuilt apex prebuilt_com.android.foo.v2 is selected",
+			selectedApexContributions:  "adservices.prebuilt.v2.contributions",
+			expectedVisibleModuleNames: []string{"com.android.adservices", "com.google.android.adservices.v2"},
+			expectedHiddenModuleNames:  []string{"com.google.android.adservices"},
+		},
+	}
+
+	for _, tc := range testCases {
+		preparer := android.GroupFixturePreparers(
+			android.FixtureMergeMockFs(map[string][]byte{
+				"system/sepolicy/apex/com.android.adservices-file_contexts": nil,
+			}),
+			android.FixtureModifyProductVariables(func(variables android.FixtureProductVariables) {
+				variables.BuildFlags = map[string]string{
+					"RELEASE_APEX_CONTRIBUTIONS_ADSERVICES": tc.selectedApexContributions,
+				}
+			}),
+		)
+		ctx := testApex(t, bp, preparer)
+
+		checkHideFromMake(t, ctx, tc.expectedVisibleModuleNames, tc.expectedHiddenModuleNames)
+	}
+}
+
 func TestAconfifDeclarationsValidation(t *testing.T) {
 	aconfigDeclarationLibraryString := func(moduleNames []string) (ret string) {
 		for _, moduleName := range moduleNames {
diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go
index 919cb01..533f937 100644
--- a/apex/bootclasspath_fragment_test.go
+++ b/apex/bootclasspath_fragment_test.go
@@ -561,6 +561,7 @@
 		result := preparers.RunTestWithBp(t, fmt.Sprintf(bp, "enabled: false,"))
 
 		java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common_com.android.art", []string{
+			`all_apex_contributions`,
 			`dex2oatd`,
 			`prebuilt_art-bootclasspath-fragment`,
 			`prebuilt_com.android.art.apex.selector`,
@@ -568,6 +569,7 @@
 		})
 
 		java.CheckModuleDependencies(t, result.TestContext, "art-bootclasspath-fragment", "android_common_com.android.art", []string{
+			`all_apex_contributions`,
 			`dex2oatd`,
 			`prebuilt_bar`,
 			`prebuilt_com.android.art.deapexer`,
diff --git a/apex/systemserver_classpath_fragment_test.go b/apex/systemserver_classpath_fragment_test.go
index f6c53b2..452a43e 100644
--- a/apex/systemserver_classpath_fragment_test.go
+++ b/apex/systemserver_classpath_fragment_test.go
@@ -274,6 +274,7 @@
 	ctx := result.TestContext
 
 	java.CheckModuleDependencies(t, ctx, "myapex", "android_common_myapex", []string{
+		`all_apex_contributions`,
 		`dex2oatd`,
 		`prebuilt_myapex.apex.selector`,
 		`prebuilt_myapex.deapexer`,
@@ -281,6 +282,7 @@
 	})
 
 	java.CheckModuleDependencies(t, ctx, "mysystemserverclasspathfragment", "android_common_myapex", []string{
+		`all_apex_contributions`,
 		`prebuilt_bar`,
 		`prebuilt_foo`,
 		`prebuilt_myapex.deapexer`,
@@ -432,6 +434,7 @@
 	ctx := result.TestContext
 
 	java.CheckModuleDependencies(t, ctx, "mysystemserverclasspathfragment", "android_common_myapex", []string{
+		`all_apex_contributions`,
 		`prebuilt_bar`,
 		`prebuilt_foo`,
 		`prebuilt_myapex.deapexer`,
diff --git a/java/aar.go b/java/aar.go
index 186289e..5c82872 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -166,7 +166,11 @@
 func (a *aapt) useResourceProcessorBusyBox(ctx android.BaseModuleContext) bool {
 	return BoolDefault(a.aaptProperties.Use_resource_processor, ctx.Config().UseResourceProcessorByDefault()) &&
 		// TODO(b/331641946): remove this when ResourceProcessorBusyBox supports generating shared libraries.
-		!slices.Contains(a.aaptProperties.Aaptflags, "--shared-lib")
+		!slices.Contains(a.aaptProperties.Aaptflags, "--shared-lib") &&
+		// Use the legacy resource processor in kythe builds.
+		// The legacy resource processor creates an R.srcjar, which kythe can use for generating crossrefs.
+		// TODO(b/354854007): Re-enable BusyBox in kythe builds
+		!ctx.Config().EmitXrefRules()
 }
 
 func (a *aapt) filterProduct() string {
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index 7949244..ddb89e9 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -616,10 +616,8 @@
 	return installPath, relDir, installBase
 }
 
-// RuleBuilder.Install() adds output-to-install copy pairs to a list for Make. To share this
-// information with PackagingSpec in soong, call PackageFile for them.
-// The install path and the target install partition of the module must be the same.
-func packageFile(ctx android.ModuleContext, install android.RuleBuilderInstall) {
+// installFile will install the file if `install` path and the target install partition are the same.
+func installFile(ctx android.ModuleContext, install android.RuleBuilderInstall) {
 	installPath, relDir, name := getModuleInstallPathInfo(ctx, install.To)
 	// Empty name means the install partition is not for the target image.
 	// For the system image, files for "apex" and "system_other" are skipped here.
@@ -628,7 +626,7 @@
 	// TODO(b/320196894): Files for "system_other" are skipped because soong creates the system
 	// image only for now.
 	if name != "" {
-		ctx.PackageFile(installPath.Join(ctx, relDir), name, install.From)
+		ctx.InstallFile(installPath.Join(ctx, relDir), name, install.From)
 	}
 }
 
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index defa82c..56e007b 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -491,6 +491,11 @@
 	// Build path to a config file that Soong writes for Make (to be used in makefiles that install
 	// the default boot image).
 	dexpreoptConfigForMake android.WritablePath
+
+	// Build path to the boot framework profile.
+	// This is used as the `OutputFile` in `AndroidMkEntries`.
+	// A non-nil value ensures that this singleton module does not get skipped in AndroidMkEntries processing.
+	bootFrameworkProfile android.WritablePath
 }
 
 func (dbj *dexpreoptBootJars) DepsMutator(ctx android.BottomUpMutatorContext) {
@@ -603,7 +608,8 @@
 		installs := generateBootImage(ctx, config)
 		profileInstalls = append(profileInstalls, installs...)
 		if config == d.defaultBootImage {
-			_, installs := bootFrameworkProfileRule(ctx, config)
+			bootProfile, installs := bootFrameworkProfileRule(ctx, config)
+			d.bootFrameworkProfile = bootProfile
 			profileInstalls = append(profileInstalls, installs...)
 		}
 	}
@@ -613,7 +619,7 @@
 			profileLicenseMetadataFile: android.OptionalPathForPath(ctx.LicenseMetadataFile()),
 		})
 		for _, install := range profileInstalls {
-			packageFile(ctx, install)
+			installFile(ctx, install)
 		}
 	}
 }
@@ -939,24 +945,21 @@
 	}
 
 	for _, install := range image.installs {
-		packageFile(ctx, install)
+		installFile(ctx, install)
 	}
 
 	for _, install := range image.vdexInstalls {
-		if image.target.Arch.ArchType.Name != ctx.DeviceConfig().DeviceArch() {
-			// Note that the vdex files are identical between architectures. If the target image is
-			// not for the primary architecture create symlinks to share the vdex of the primary
-			// architecture with the other architectures.
-			//
-			// Assuming that the install path has the architecture name with it, replace the
-			// architecture name with the primary architecture name to find the source vdex file.
-			installPath, relDir, name := getModuleInstallPathInfo(ctx, install.To)
-			if name != "" {
-				srcRelDir := strings.Replace(relDir, image.target.Arch.ArchType.Name, ctx.DeviceConfig().DeviceArch(), 1)
-				ctx.InstallSymlink(installPath.Join(ctx, relDir), name, installPath.Join(ctx, srcRelDir, name))
-			}
-		} else {
-			packageFile(ctx, install)
+		installPath, relDir, name := getModuleInstallPathInfo(ctx, install.To)
+		if name == "" {
+			continue
+		}
+		// Note that the vdex files are identical between architectures. Copy the vdex to a no arch directory
+		// and create symlinks for both the primary and secondary arches.
+		ctx.InstallSymlink(installPath.Join(ctx, relDir), name, installPath.Join(ctx, "framework", name))
+		if image.target.Arch.ArchType.Name == ctx.DeviceConfig().DeviceArch() {
+			// Copy the vdex from the primary arch to the no-arch directory
+			// e.g. /system/framework/$bootjar.vdex
+			ctx.InstallFile(installPath.Join(ctx, "framework"), name, install.From)
 		}
 	}
 }
@@ -1234,7 +1237,7 @@
 
 	profile := bootImageProfileRuleCommon(ctx, image.name, image.dexPathsDeps.Paths(), image.getAnyAndroidVariant().dexLocationsDeps)
 
-	if image == defaultBootImageConfig(ctx) {
+	if image == defaultBootImageConfig(ctx) && profile != nil {
 		rule := android.NewRuleBuilder(pctx, ctx)
 		rule.Install(profile, "/system/etc/boot-image.prof")
 		return profile, rule.Installs()
@@ -1380,3 +1383,12 @@
 		ctx.Strict("DEXPREOPT_IMAGE_NAMES", strings.Join(getImageNames(), " "))
 	}
 }
+
+// Add one of the outputs in `OutputFile`
+// This ensures that this singleton module does not get skipped when writing out/soong/Android-*.mk
+func (d *dexpreoptBootJars) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{{
+		Class:      "ETC",
+		OutputFile: android.OptionalPathForPath(d.bootFrameworkProfile),
+	}}
+}
diff --git a/java/sdk_library_test.go b/java/sdk_library_test.go
index a8a1494..ac8bc1a 100644
--- a/java/sdk_library_test.go
+++ b/java/sdk_library_test.go
@@ -923,6 +923,7 @@
 	}
 
 	CheckModuleDependencies(t, result.TestContext, "sdklib", "android_common", []string{
+		`all_apex_contributions`,
 		`dex2oatd`,
 		`prebuilt_sdklib.stubs`,
 		`prebuilt_sdklib.stubs.source.test`,
diff --git a/scripts/gen_build_prop.py b/scripts/gen_build_prop.py
index c0d4735..2bd246d 100644
--- a/scripts/gen_build_prop.py
+++ b/scripts/gen_build_prop.py
@@ -129,16 +129,16 @@
     print(f"ro.product.{partition}.name={config['DeviceProduct']}")
 
   if partition != "system":
-    if config["ModelForAttestation"]:
-        print(f"ro.product.model_for_attestation={config['ModelForAttestation']}")
-    if config["BrandForAttestation"]:
-        print(f"ro.product.brand_for_attestation={config['BrandForAttestation']}")
-    if config["NameForAttestation"]:
-        print(f"ro.product.name_for_attestation={config['NameForAttestation']}")
-    if config["DeviceForAttestation"]:
-        print(f"ro.product.device_for_attestation={config['DeviceForAttestation']}")
-    if config["ManufacturerForAttestation"]:
-        print(f"ro.product.manufacturer_for_attestation={config['ManufacturerForAttestation']}")
+    if config["ProductModelForAttestation"]:
+        print(f"ro.product.model_for_attestation={config['ProductModelForAttestation']}")
+    if config["ProductBrandForAttestation"]:
+        print(f"ro.product.brand_for_attestation={config['ProductBrandForAttestation']}")
+    if config["ProductNameForAttestation"]:
+        print(f"ro.product.name_for_attestation={config['ProductNameForAttestation']}")
+    if config["ProductDeviceForAttestation"]:
+        print(f"ro.product.device_for_attestation={config['ProductDeviceForAttestation']}")
+    if config["ProductManufacturerForAttestation"]:
+        print(f"ro.product.manufacturer_for_attestation={config['ProductManufacturerForAttestation']}")
 
   if config["ZygoteForce64"]:
     if partition == "vendor":
@@ -511,6 +511,15 @@
 
   build_prop(args, gen_build_info=True, gen_common_build_props=True, variables=variables)
 
+def build_system_ext_prop(args):
+  config = args.config
+
+  # Order matters here. When there are duplicates, the last one wins.
+  # TODO(b/117892318): don't allow duplicates so that the ordering doesn't matter
+  variables = ["PRODUCT_SYSTEM_EXT_PROPERTIES"]
+
+  build_prop(args, gen_build_info=False, gen_common_build_props=True, variables=variables)
+
 '''
 def build_vendor_prop(args):
   config = args.config
@@ -563,6 +572,8 @@
   with contextlib.redirect_stdout(args.out):
     if args.partition == "system":
       build_system_prop(args)
+    elif args.partition == "system_ext":
+      build_system_ext_prop(args)
       '''
     elif args.partition == "vendor":
       build_vendor_prop(args)