Merge "Allow crash_dump to read vendor apex dir." into main
diff --git a/Android.bp b/Android.bp
index b0544cf..dedcf63 100644
--- a/Android.bp
+++ b/Android.bp
@@ -913,7 +913,7 @@
 // SEPOLICY_FREEZE_TEST_EXTRA_DIRS and SEPOLICY_FREEZE_TEST_EXTRA_PREBUILT_DIRS.
 //////////////////////////////////
 se_freeze_test {
-    name: "sepolicy_freeze_test",
+    name: "se_freeze_test",
 }
 
 //////////////////////////////////
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 9aa510b..053e36a 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -11,9 +11,6 @@
                 },
                 {
                     "include-filter": "android.security.cts.SELinuxHostTest#testGMSCoreDomain"
-                },
-                {
-                    "include-filter": "android.security.cts.SeamendcHostTest"
                 }
             ]
         }
diff --git a/build/soong/build_files.go b/build/soong/build_files.go
index db028e6..a15c65c 100644
--- a/build/soong/build_files.go
+++ b/build/soong/build_files.go
@@ -16,6 +16,7 @@
 
 import (
 	"fmt"
+	"path"
 	"path/filepath"
 	"strings"
 
@@ -103,8 +104,15 @@
 	b.srcs[".vendor"] = b.findSrcsInDirs(ctx, ctx.DeviceConfig().VendorSepolicyDirs()...)
 	b.srcs[".odm"] = b.findSrcsInDirs(ctx, ctx.DeviceConfig().OdmSepolicyDirs()...)
 
+	prebuilt_directories, err := ctx.GlobWithDeps("system/sepolicy/prebuilts/api/*", nil)
+	if err != nil {
+		ctx.ModuleErrorf("error while globbing: %w", err)
+		return
+	}
+
 	// directories used for compat tests and Treble tests
-	for _, ver := range ctx.DeviceConfig().PlatformSepolicyCompatVersions() {
+	for _, dir := range prebuilt_directories {
+		ver := path.Base(dir)
 		b.srcs[".plat_public_"+ver] = b.findSrcsInDirs(ctx, filepath.Join("system", "sepolicy", "prebuilts", "api", ver, "public"))
 		b.srcs[".plat_private_"+ver] = b.findSrcsInDirs(ctx, filepath.Join("system", "sepolicy", "prebuilts", "api", ver, "private"))
 		b.srcs[".system_ext_public_"+ver] = b.findSrcsInDirs(ctx, filepath.Join(ctx.DeviceConfig().SystemExtSepolicyPrebuiltApiDir(), "prebuilts", "api", ver, "public"))
diff --git a/build/soong/sepolicy_freeze.go b/build/soong/sepolicy_freeze.go
index 9ae7826..385d6af 100644
--- a/build/soong/sepolicy_freeze.go
+++ b/build/soong/sepolicy_freeze.go
@@ -15,12 +15,14 @@
 package selinux
 
 import (
-	"path/filepath"
 	"sort"
 
 	"android/soong/android"
 )
 
+var currentCilTag = dependencyTag{name: "current_cil"}
+var prebuiltCilTag = dependencyTag{name: "prebuilt_cil"}
+
 func init() {
 	ctx := android.InitRegistrationContext
 	ctx.RegisterParallelSingletonModuleType("se_freeze_test", freezeTestFactory)
@@ -32,6 +34,9 @@
 func freezeTestFactory() android.SingletonModule {
 	f := &freezeTestModule{}
 	android.InitAndroidModule(f)
+	android.AddLoadHook(f, func(ctx android.LoadHookContext) {
+		f.loadHook(ctx)
+	})
 	return f
 }
 
@@ -40,26 +45,23 @@
 	freezeTestTimestamp android.ModuleOutPath
 }
 
-func (f *freezeTestModule) GenerateSingletonBuildActions(ctx android.SingletonContext) {
-	// does nothing; se_freeze_test is a singeton because two freeze test modules don't make sense.
-}
-
-func (f *freezeTestModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+func (f *freezeTestModule) shouldSkip(ctx android.EarlyModuleContext) bool {
 	platformVersion := ctx.DeviceConfig().PlatformSepolicyVersion()
 	totVersion := ctx.DeviceConfig().TotSepolicyVersion()
 
+	return platformVersion == totVersion
+}
+
+func (f *freezeTestModule) loadHook(ctx android.LoadHookContext) {
 	extraDirs := ctx.DeviceConfig().SepolicyFreezeTestExtraDirs()
 	extraPrebuiltDirs := ctx.DeviceConfig().SepolicyFreezeTestExtraPrebuiltDirs()
-	f.freezeTestTimestamp = android.PathForModuleOut(ctx, "freeze_test")
 
-	if platformVersion == totVersion {
+	if f.shouldSkip(ctx) {
 		if len(extraDirs) > 0 || len(extraPrebuiltDirs) > 0 {
 			ctx.ModuleErrorf("SEPOLICY_FREEZE_TEST_EXTRA_DIRS or SEPOLICY_FREEZE_TEST_EXTRA_PREBUILT_DIRS cannot be set before system/sepolicy freezes.")
 			return
 		}
 
-		// we still build a rule to prevent possible regression
-		android.WriteFileRule(ctx, f.freezeTestTimestamp, ";; no freeze tests needed before system/sepolicy freezes")
 		return
 	}
 
@@ -67,17 +69,80 @@
 		ctx.ModuleErrorf("SEPOLICY_FREEZE_TEST_EXTRA_DIRS and SEPOLICY_FREEZE_TEST_EXTRA_PREBUILT_DIRS must have the same number of directories.")
 		return
 	}
+}
 
-	platPublic := filepath.Join(ctx.ModuleDir(), "public")
-	platPrivate := filepath.Join(ctx.ModuleDir(), "private")
-	prebuiltPublic := filepath.Join(ctx.ModuleDir(), "prebuilts", "api", platformVersion, "public")
-	prebuiltPrivate := filepath.Join(ctx.ModuleDir(), "prebuilts", "api", platformVersion, "private")
+func (f *freezeTestModule) prebuiltCilModuleName(ctx android.EarlyModuleContext) string {
+	return ctx.DeviceConfig().PlatformSepolicyVersion() + "_plat_pub_policy.cil"
+}
 
-	sourceDirs := append(extraDirs, platPublic, platPrivate)
-	prebuiltDirs := append(extraPrebuiltDirs, prebuiltPublic, prebuiltPrivate)
+func (f *freezeTestModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+	if f.shouldSkip(ctx) {
+		return
+	}
+
+	ctx.AddDependency(f, currentCilTag, "base_plat_pub_policy.cil")
+	ctx.AddDependency(f, prebuiltCilTag, f.prebuiltCilModuleName(ctx))
+}
+
+func (f *freezeTestModule) GenerateSingletonBuildActions(ctx android.SingletonContext) {
+	// does nothing; se_freeze_test is a singeton because two freeze test modules don't make sense.
+}
+
+func (f *freezeTestModule) outputFileOfDep(ctx android.ModuleContext, depTag dependencyTag) android.Path {
+	deps := ctx.GetDirectDepsWithTag(depTag)
+	if len(deps) != 1 {
+		ctx.ModuleErrorf("%d deps having tag %q; expected only one dep", len(deps), depTag)
+		return nil
+	}
+
+	dep := deps[0]
+	outputFileProducer, ok := dep.(android.OutputFileProducer)
+	if !ok {
+		ctx.ModuleErrorf("module %q is not an output file producer", dep.String())
+		return nil
+	}
+
+	output, err := outputFileProducer.OutputFiles("")
+	if err != nil {
+		ctx.ModuleErrorf("module %q failed to produce output: %w", dep.String(), err)
+		return nil
+	}
+	if len(output) != 1 {
+		ctx.ModuleErrorf("module %q produced %d outputs; expected only one output", dep.String(), len(output))
+		return nil
+	}
+
+	return output[0]
+}
+
+func (f *freezeTestModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	f.freezeTestTimestamp = android.PathForModuleOut(ctx, "freeze_test")
+
+	if f.shouldSkip(ctx) {
+		// we still build a rule to prevent possible regression
+		android.WriteFileRule(ctx, f.freezeTestTimestamp, ";; no freeze tests needed before system/sepolicy freezes")
+		return
+	}
+
+	// Freeze test 1: compare ToT sepolicy and prebuilt sepolicy
+	currentCil := f.outputFileOfDep(ctx, currentCilTag)
+	prebuiltCil := f.outputFileOfDep(ctx, prebuiltCilTag)
+	if ctx.Failed() {
+		return
+	}
+
+	rule := android.NewRuleBuilder(pctx, ctx)
+	rule.Command().BuiltTool("sepolicy_freeze_test").
+		FlagWithInput("-c ", currentCil).
+		FlagWithInput("-p ", prebuiltCil)
+
+	// Freeze test 2: compare extra directories
+	// We don't know the exact structure of extra directories, so just directly compare them
+	extraDirs := ctx.DeviceConfig().SepolicyFreezeTestExtraDirs()
+	extraPrebuiltDirs := ctx.DeviceConfig().SepolicyFreezeTestExtraPrebuiltDirs()
 
 	var implicits []string
-	for _, dir := range append(sourceDirs, prebuiltDirs...) {
+	for _, dir := range append(extraDirs, extraPrebuiltDirs...) {
 		glob, err := ctx.GlobWithDeps(dir+"/**/*", []string{"bug_map"} /* exclude */)
 		if err != nil {
 			ctx.ModuleErrorf("failed to glob sepolicy dir %q: %s", dir, err.Error())
@@ -87,15 +152,13 @@
 	}
 	sort.Strings(implicits)
 
-	rule := android.NewRuleBuilder(pctx, ctx)
-
-	for idx, _ := range sourceDirs {
+	for idx, _ := range extraDirs {
 		rule.Command().Text("diff").
 			Flag("-r").
 			Flag("-q").
 			FlagWithArg("-x ", "bug_map"). // exclude
-			Text(sourceDirs[idx]).
-			Text(prebuiltDirs[idx])
+			Text(extraDirs[idx]).
+			Text(extraPrebuiltDirs[idx])
 	}
 
 	rule.Command().Text("touch").
diff --git a/prebuilts/api/29.0/Android.bp b/prebuilts/api/29.0/Android.bp
new file mode 100644
index 0000000..34052a2
--- /dev/null
+++ b/prebuilts/api/29.0/Android.bp
@@ -0,0 +1,28 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+se_policy_conf {
+    name: "29.0_plat_pub_policy.conf",
+    srcs: [":se_build_files{.plat_public_29.0}", ":se_build_files{.reqd_mask}"],
+    installable: false,
+    build_variant: "user",
+}
+
+se_policy_cil {
+    name: "29.0_plat_pub_policy.cil",
+    src: ":29.0_plat_pub_policy.conf",
+    filter_out: [":reqd_policy_mask.cil"],
+    secilc_check: false,
+    installable: false,
+}
diff --git a/prebuilts/api/30.0/Android.bp b/prebuilts/api/30.0/Android.bp
new file mode 100644
index 0000000..0e51bb7
--- /dev/null
+++ b/prebuilts/api/30.0/Android.bp
@@ -0,0 +1,28 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+se_policy_conf {
+    name: "30.0_plat_pub_policy.conf",
+    srcs: [":se_build_files{.plat_public_30.0}", ":se_build_files{.reqd_mask}"],
+    installable: false,
+    build_variant: "user",
+}
+
+se_policy_cil {
+    name: "30.0_plat_pub_policy.cil",
+    src: ":30.0_plat_pub_policy.conf",
+    filter_out: [":reqd_policy_mask.cil"],
+    secilc_check: false,
+    installable: false,
+}
diff --git a/prebuilts/api/31.0/Android.bp b/prebuilts/api/31.0/Android.bp
new file mode 100644
index 0000000..5697173
--- /dev/null
+++ b/prebuilts/api/31.0/Android.bp
@@ -0,0 +1,28 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+se_policy_conf {
+    name: "31.0_plat_pub_policy.conf",
+    srcs: [":se_build_files{.plat_public_31.0}", ":se_build_files{.reqd_mask}"],
+    installable: false,
+    build_variant: "user",
+}
+
+se_policy_cil {
+    name: "31.0_plat_pub_policy.cil",
+    src: ":31.0_plat_pub_policy.conf",
+    filter_out: [":reqd_policy_mask.cil"],
+    secilc_check: false,
+    installable: false,
+}
diff --git a/prebuilts/api/32.0/Android.bp b/prebuilts/api/32.0/Android.bp
new file mode 100644
index 0000000..723f743
--- /dev/null
+++ b/prebuilts/api/32.0/Android.bp
@@ -0,0 +1,28 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+se_policy_conf {
+    name: "32.0_plat_pub_policy.conf",
+    srcs: [":se_build_files{.plat_public_32.0}", ":se_build_files{.reqd_mask}"],
+    installable: false,
+    build_variant: "user",
+}
+
+se_policy_cil {
+    name: "32.0_plat_pub_policy.cil",
+    src: ":32.0_plat_pub_policy.conf",
+    filter_out: [":reqd_policy_mask.cil"],
+    secilc_check: false,
+    installable: false,
+}
diff --git a/prebuilts/api/33.0/Android.bp b/prebuilts/api/33.0/Android.bp
new file mode 100644
index 0000000..df4da89
--- /dev/null
+++ b/prebuilts/api/33.0/Android.bp
@@ -0,0 +1,28 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+se_policy_conf {
+    name: "33.0_plat_pub_policy.conf",
+    srcs: [":se_build_files{.plat_public_33.0}", ":se_build_files{.reqd_mask}"],
+    installable: false,
+    build_variant: "user",
+}
+
+se_policy_cil {
+    name: "33.0_plat_pub_policy.cil",
+    src: ":33.0_plat_pub_policy.conf",
+    filter_out: [":reqd_policy_mask.cil"],
+    secilc_check: false,
+    installable: false,
+}
diff --git a/prebuilts/api/34.0/Android.bp b/prebuilts/api/34.0/Android.bp
new file mode 100644
index 0000000..e962848
--- /dev/null
+++ b/prebuilts/api/34.0/Android.bp
@@ -0,0 +1,28 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+se_policy_conf {
+    name: "34.0_plat_pub_policy.conf",
+    srcs: [":se_build_files{.plat_public_34.0}", ":se_build_files{.reqd_mask}"],
+    installable: false,
+    build_variant: "user",
+}
+
+se_policy_cil {
+    name: "34.0_plat_pub_policy.cil",
+    src: ":34.0_plat_pub_policy.conf",
+    filter_out: [":reqd_policy_mask.cil"],
+    secilc_check: false,
+    installable: false,
+}
diff --git a/tests/Android.bp b/tests/Android.bp
index cc0bdc1..544db37 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -146,3 +146,18 @@
     name: "check_prop_prefix",
     srcs: ["check_prop_prefix.py"],
 }
+
+python_binary_host {
+    name: "sepolicy_freeze_test",
+    srcs: [
+        "sepolicy_freeze_test.py",
+    ],
+    version: {
+        py3: {
+            embedded_launcher: true,
+        },
+    },
+    libs: [
+        "mini_cil_parser",
+    ],
+}
diff --git a/tests/sepolicy_freeze_test.py b/tests/sepolicy_freeze_test.py
new file mode 100644
index 0000000..72c8fde
--- /dev/null
+++ b/tests/sepolicy_freeze_test.py
@@ -0,0 +1,56 @@
+# Copyright 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from optparse import OptionParser
+import mini_parser
+import os
+import sys
+
+def do_main():
+    usage = "sepolicy_freeze_test "
+    usage += "-c current_cil -p prebuilt_cil [--help]"
+    parser = OptionParser(usage=usage)
+    parser.add_option("-c", "--current", dest="current", metavar="FILE")
+    parser.add_option("-p", "--prebuilt", dest="prebuilt", metavar="FILE")
+
+    (options, args) = parser.parse_args()
+
+    if not options.current or not options.prebuilt:
+        sys.exit("Must specify both current and prebuilt\n" + parser.usage)
+    if not os.path.exists(options.current):
+        sys.exit("Current policy " + options.current + " does not exist\n"
+                + parser.usage)
+    if not os.path.exists(options.prebuilt):
+        sys.exit("Prebuilt policy " + options.prebuilt + " does not exist\n"
+                + parser.usage)
+
+    current_policy = mini_parser.MiniCilParser(options.current)
+    prebuilt_policy = mini_parser.MiniCilParser(options.prebuilt)
+
+    results = ""
+    removed_types = prebuilt_policy.types - current_policy.types
+    removed_attributes = prebuilt_policy.typeattributes - current_policy.typeattributes
+    removed_attributes = set(filter(lambda x: "base_typeattr_" not in x, removed_attributes))
+
+    if removed_types:
+        results += "The following public types were removed:\n" + ", ".join(removed_types) + "\n"
+
+    if removed_attributes:
+        results += "The following public attributes were removed:\n" + ", ".join(removed_attributes) + "\n"
+
+    if len(results) > 0:
+        sys.exit(results)
+
+if __name__ == '__main__':
+    do_main()