Merge "Change openjdk9 specific props in bp2build/Soong" into main
diff --git a/bp2build/prebuilt_etc_conversion_test.go b/bp2build/prebuilt_etc_conversion_test.go
index 1f37270..c8cfd87 100644
--- a/bp2build/prebuilt_etc_conversion_test.go
+++ b/bp2build/prebuilt_etc_conversion_test.go
@@ -26,10 +26,17 @@
 	t.Helper()
 	(&tc).ModuleTypeUnderTest = "prebuilt_etc"
 	(&tc).ModuleTypeUnderTestFactory = etc.PrebuiltEtcFactory
-	RunBp2BuildTestCase(t, registerPrebuiltEtcModuleTypes, tc)
+	RunBp2BuildTestCase(t, registerPrebuiltModuleTypes, tc)
 }
 
-func registerPrebuiltEtcModuleTypes(ctx android.RegistrationContext) {
+func runPrebuiltRootHostTestCase(t *testing.T, tc Bp2buildTestCase) {
+	t.Helper()
+	(&tc).ModuleTypeUnderTest = "prebuilt_root_host"
+	(&tc).ModuleTypeUnderTestFactory = etc.PrebuiltRootHostFactory
+	RunBp2BuildTestCase(t, registerPrebuiltModuleTypes, tc)
+}
+
+func registerPrebuiltModuleTypes(ctx android.RegistrationContext) {
 }
 
 func TestPrebuiltEtcSimple(t *testing.T) {
@@ -160,7 +167,7 @@
 	t.Helper()
 	(&tc).ModuleTypeUnderTest = "prebuilt_usr_share"
 	(&tc).ModuleTypeUnderTestFactory = etc.PrebuiltUserShareFactory
-	RunBp2BuildTestCase(t, registerPrebuiltEtcModuleTypes, tc)
+	RunBp2BuildTestCase(t, registerPrebuiltModuleTypes, tc)
 }
 
 func registerPrebuiltUsrShareModuleTypes(ctx android.RegistrationContext) {
@@ -360,3 +367,30 @@
 		ExpectedBazelTargets: []string{},
 	})
 }
+
+func TestPrebuiltRootHostWithWildCardInSrc(t *testing.T) {
+	runPrebuiltRootHostTestCase(t, Bp2buildTestCase{
+		Description: "prebuilt_root_host - src string has wild card",
+		Filesystem: map[string]string{
+			"prh.dat": "",
+		},
+		Blueprint: `
+prebuilt_root_host {
+    name: "prh_test",
+    src: "*.dat",
+    filename_from_src: true,
+    relative_install_path: "test/install/path",
+    bazel_module: { bp2build_available: true },
+}
+`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("prebuilt_file", "prh_test", AttrNameToString{
+				"filename": `"prh.dat"`,
+				"src":      `"prh.dat"`,
+				"dir":      `"./test/install/path"`,
+				"target_compatible_with": `select({
+        "//build/bazel_common_rules/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`,
+			})}})
+}
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index 9423531..ca28442 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -508,6 +508,7 @@
 	// This module is host-only
 	android.InitAndroidArchModule(module, android.HostSupported, android.MultilibCommon)
 	android.InitDefaultableModule(module)
+	android.InitBazelModule(module)
 	return module
 }
 
@@ -759,7 +760,7 @@
 		filename = *moduleProps.Filename
 	} else if moduleProps.Filename_from_src != nil && *moduleProps.Filename_from_src {
 		if moduleProps.Src != nil {
-			filename = *moduleProps.Src
+			filename = android.BazelLabelForModuleSrcSingle(ctx, *moduleProps.Src).Label
 		}
 		filenameFromSrc = true
 	} else {
@@ -767,8 +768,8 @@
 	}
 
 	var dir = module.installDirBase
-	if subDir := module.subdirProperties.Sub_dir; subDir != nil {
-		dir = dir + "/" + *subDir
+	if module.SubDir() != "" {
+		dir = dir + "/" + module.SubDir()
 	}
 
 	var installable bazel.BoolAttribute
@@ -796,8 +797,9 @@
 // which we treat as *PrebuiltFile*
 func (module *PrebuiltEtc) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) {
 	var dir = module.installDirBase
-	// prebuilt_file supports only `etc` or `usr/share`
-	if !(dir == "etc" || dir == "usr/share") {
+	// prebuilt_file only supports "etc" or "usr/share" or "." as module installDirBase
+	if !(dir == "etc" || dir == "usr/share" || dir == ".") {
+		ctx.MarkBp2buildUnconvertible(bp2build_metrics_proto.UnconvertedReasonType_TYPE_UNSUPPORTED, "")
 		return
 	}
 
diff --git a/genrule/allowlists.go b/genrule/allowlists.go
index 8c78528..8552173 100644
--- a/genrule/allowlists.go
+++ b/genrule/allowlists.go
@@ -58,6 +58,7 @@
 		"BlueberryFacadeGeneratedStub_h",
 		"BluetoothGeneratedDumpsysBinarySchema_bfbs",
 		"BluetoothGeneratedDumpsysDataSchema_h",
+		"CompilationTestCases_package-dex-usage",
 		"ControlEnvProxyServerProto_cc",
 		"ControlEnvProxyServerProto_h",
 		"CtsApkVerityTestDebugFiles",
diff --git a/java/bootclasspath_fragment_test.go b/java/bootclasspath_fragment_test.go
index 888caad..828de21 100644
--- a/java/bootclasspath_fragment_test.go
+++ b/java/bootclasspath_fragment_test.go
@@ -316,6 +316,60 @@
 	android.AssertPathsRelativeToTopEquals(t, "widest dex stubs jar", expectedWidestPaths, info.TransitiveStubDexJarsByScope.StubDexJarsForWidestAPIScope())
 }
 
+func TestFromTextWidestApiScope(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForTestWithBootclasspathFragment,
+		PrepareForTestWithJavaSdkLibraryFiles,
+		android.FixtureModifyConfig(func(config android.Config) {
+			config.SetBuildFromTextStub(true)
+		}),
+		FixtureWithLastReleaseApis("mysdklibrary", "android-non-updatable"),
+		FixtureConfigureApexBootJars("someapex:mysdklibrary"),
+	).RunTestWithBp(t, `
+		bootclasspath_fragment {
+			name: "myfragment",
+			contents: ["mysdklibrary"],
+			additional_stubs: [
+				"android-non-updatable",
+			],
+			hidden_api: {
+				split_packages: ["*"],
+			},
+		}
+		java_sdk_library {
+			name: "mysdklibrary",
+			srcs: ["a.java"],
+			shared_library: false,
+			public: {enabled: true},
+			system: {enabled: true},
+		}
+		java_sdk_library {
+			name: "android-non-updatable",
+			srcs: ["b.java"],
+			compile_dex: true,
+			public: {
+				enabled: true,
+			},
+			system: {
+				enabled: true,
+			},
+			test: {
+				enabled: true,
+			},
+			module_lib: {
+				enabled: true,
+			},
+		}
+	`)
+
+	fragment := result.ModuleForTests("myfragment", "android_common")
+	dependencyStubDexFlag := "--dependency-stub-dex=out/soong/.intermediates/default/java/android-non-updatable.stubs.test_module_lib/android_common/dex/android-non-updatable.stubs.test_module_lib.jar"
+	stubFlagsCommand := fragment.Output("modular-hiddenapi/stub-flags.csv").RuleParams.Command
+	android.AssertStringDoesContain(t,
+		"Stub flags generating command does not include the expected dependency stub dex file",
+		stubFlagsCommand, dependencyStubDexFlag)
+}
+
 func TestSnapshotWithBootclasspathFragment_HiddenAPI(t *testing.T) {
 	result := android.GroupFixturePreparers(
 		prepareForTestWithBootclasspathFragment,
diff --git a/java/hiddenapi_modular.go b/java/hiddenapi_modular.go
index f31f5d1..81be33d 100644
--- a/java/hiddenapi_modular.go
+++ b/java/hiddenapi_modular.go
@@ -38,10 +38,14 @@
 	// The option needed to passed to "hiddenapi list".
 	hiddenAPIListOption string
 
-	// The name sof the source stub library modules that contain the API provided by the platform,
+	// The names of the source stub library modules that contain the API provided by the platform,
 	// i.e. by modules that are not in an APEX.
 	nonUpdatableSourceModule string
 
+	// The names of from-text stub library modules that contain the API provided by the platform,
+	// i.e. by modules that are not in an APEX.
+	nonUpdatableFromTextModule string
+
 	// The names of the prebuilt stub library modules that contain the API provided by the platform,
 	// i.e. by modules that are not in an APEX.
 	nonUpdatablePrebuiltModule string
@@ -86,6 +90,9 @@
 		if ctx.Config().AlwaysUsePrebuiltSdks() {
 			return l.nonUpdatablePrebuiltModule
 		} else {
+			if l.nonUpdatableFromTextModule != "" && ctx.Config().BuildFromTextStub() {
+				return l.nonUpdatableFromTextModule
+			}
 			return l.nonUpdatableSourceModule
 		}
 	} else {
@@ -117,8 +124,9 @@
 		hiddenAPIListOption: "--test-stub-classpath",
 	})
 	ModuleLibHiddenAPIScope = initHiddenAPIScope(&HiddenAPIScope{
-		name:    "module-lib",
-		sdkKind: android.SdkModule,
+		name:                       "module-lib",
+		sdkKind:                    android.SdkModule,
+		nonUpdatableFromTextModule: "android-non-updatable.stubs.test_module_lib",
 	})
 	CorePlatformHiddenAPIScope = initHiddenAPIScope(&HiddenAPIScope{
 		name:                "core-platform",
@@ -647,7 +655,7 @@
 	// public version is provided by the art.module.public.api module. In those cases it is necessary
 	// to treat all those modules as they were the same name, otherwise it will result in multiple
 	// definitions of a single class being passed to hidden API processing which will cause an error.
-	if name == scope.nonUpdatablePrebuiltModule || name == scope.nonUpdatableSourceModule {
+	if name == scope.nonUpdatablePrebuiltModule || name == scope.nonUpdatableSourceModule || name == scope.nonUpdatableFromTextModule {
 		// Treat all *android-non-updatable* modules as if they were part of an android-non-updatable
 		// java_sdk_library.
 		// TODO(b/192067200): Remove once android-non-updatable is a java_sdk_library or equivalent.
diff --git a/java/testing.go b/java/testing.go
index 4461351..16bdd80 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -491,6 +491,7 @@
 		"android-non-updatable.stubs.system.from-text":     systemDroidstubs,
 		"android-non-updatable.stubs.test.from-text":       testDroidstubs,
 		"android-non-updatable.stubs.module_lib.from-text": moduleLibDroidstubs,
+		"android-non-updatable.stubs.test_module_lib":      moduleLibDroidstubs,
 	}
 
 	for _, droidstubs := range droidstubsStructs {
diff --git a/ui/build/build.go b/ui/build/build.go
index 9d5c330..15cff5f 100644
--- a/ui/build/build.go
+++ b/ui/build/build.go
@@ -298,6 +298,8 @@
 		runMakeProductConfig(ctx, config)
 	}
 
+	checkForCleanPartitions(ctx, config)
+
 	// Everything below here depends on product config.
 
 	if inList("installclean", config.Arguments()) ||
diff --git a/ui/build/cleanbuild.go b/ui/build/cleanbuild.go
index 41cb5ab..7114963 100644
--- a/ui/build/cleanbuild.go
+++ b/ui/build/cleanbuild.go
@@ -218,6 +218,50 @@
 	writeConfig()
 }
 
+// Writes out/partitions_were_clean_at_start_of_build.txt.
+// This file will contain "true" if there were no partition staging directories at the start of
+// the build (most likely from having just run `m installclean`) and "false" otherwise.
+// It's used to make a test that the staging directories are correct. That test can only be
+// correctly run directly after `m installclean`, and this is how we check for that.
+func checkForCleanPartitions(ctx Context, config Config) {
+	productOutPath := config.ProductOut()
+	productOut := func(path string) string {
+		return filepath.Join(productOutPath, path)
+	}
+
+	notExists := func(path string) bool {
+		_, err := os.Stat(path)
+		return os.IsNotExist(err)
+	}
+
+	clean := notExists(productOut("ramdisk")) &&
+		notExists(productOut("ramdisk_16k")) &&
+		notExists(productOut("debug_ramdisk")) &&
+		notExists(productOut("vendor_ramdisk")) &&
+		notExists(productOut("vendor_debug_ramdisk")) &&
+		notExists(productOut("vendor_kernel_ramdisk")) &&
+		notExists(productOut("test_harness_ramdisk")) &&
+		notExists(productOut("data")) &&
+		notExists(productOut("recovery")) &&
+		notExists(productOut("root")) &&
+		notExists(productOut("system")) &&
+		notExists(productOut("system_dlkm")) &&
+		notExists(productOut("system_other")) &&
+		notExists(productOut("vendor")) &&
+		notExists(productOut("vendor_dlkm")) &&
+		notExists(productOut("product")) &&
+		notExists(productOut("system_ext")) &&
+		notExists(productOut("oem")) &&
+		notExists(productOut("breakpad")) &&
+		notExists(productOut("cache")) &&
+		notExists(productOut("coverage")) &&
+		notExists(productOut("installer")) &&
+		notExists(productOut("odm")) &&
+		notExists(productOut("odm_dlkm"))
+
+	writeValueIfChanged(ctx, config, config.OutDir(), "partitions_were_clean_at_start_of_build.txt", fmt.Sprintf("%t\n", clean))
+}
+
 // cleanOldFiles takes an input file (with all paths relative to basePath), and removes files from
 // the filesystem if they were removed from the input file since the last execution.
 func cleanOldFiles(ctx Context, basePath, newFile string) {
diff --git a/ui/build/config.go b/ui/build/config.go
index 2e5921a..264d83e 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -472,11 +472,6 @@
 		}
 	}
 
-	if ret.BuildFromTextStub() {
-		// TODO(b/271443071): support hidden api check for from-text stub build
-		ret.environ.Set("UNSAFE_DISABLE_HIDDENAPI_FLAGS", "true")
-	}
-
 	bpd := ret.BazelMetricsDir()
 	if err := os.RemoveAll(bpd); err != nil {
 		ctx.Fatalf("Unable to remove bazel profile directory %q: %v", bpd, err)
diff --git a/ui/build/kati.go b/ui/build/kati.go
index 31e7440..7f0ea24 100644
--- a/ui/build/kati.go
+++ b/ui/build/kati.go
@@ -102,6 +102,8 @@
 		"--use_ninja_phony_output",
 		// Support declaring symlink outputs in AOSP Ninja.
 		"--use_ninja_symlink_outputs",
+		// Support ninja validation actions with .KATI_VALIDATIONS: https://ninja-build.org/manual.html#validations
+		"--use_ninja_validations",
 		// Regenerate the Ninja file if environment inputs have changed. e.g.
 		// CLI flags, .mk file timestamps, env vars, $(wildcard ..) and some
 		// $(shell ..) results.
diff --git a/ui/build/test_build.go b/ui/build/test_build.go
index 2efc732..af60e0d 100644
--- a/ui/build/test_build.go
+++ b/ui/build/test_build.go
@@ -103,8 +103,10 @@
 	// treated as an source file.
 	dexpreoptConfigFilePath := filepath.Join(outDir, "soong", "dexpreopt.config")
 
-	// out/build_date.txt is considered a "source file"
+	// These files are written by soong_ui at the beginning of every build.
+	// Ninja considers them "source files"
 	buildDatetimeFilePath := filepath.Join(outDir, "build_date.txt")
+	cleanPartitionsFilePath := filepath.Join(outDir, "partitions_were_clean_at_start_of_build.txt")
 
 	// bpglob is built explicitly using Microfactory
 	bpglob := filepath.Join(config.SoongOutDir(), "bpglob")
@@ -122,6 +124,7 @@
 			line == variablesFilePath ||
 			line == dexpreoptConfigFilePath ||
 			line == buildDatetimeFilePath ||
+			line == cleanPartitionsFilePath ||
 			line == bpglob {
 			// Leaf node is in one of Soong's bootstrap directories, which do not have
 			// full build rules in the primary build.ninja file.