Merge "Fix diffs in system_other image" into main
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 7240ea5..2c06924 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -2689,7 +2689,7 @@
 	cppOnly := []string{"-fPIC", "${config.CommonGlobalCppflags}", "${config.DeviceGlobalCppflags}", "${config.ArmCppflags}"}
 
 	cflags := []string{"-Werror", "-std=candcpp"}
-	cstd := []string{"-std=gnu23", "-std=conly"}
+	cstd := []string{"-std=gnu17", "-std=conly"}
 	cppstd := []string{"-std=gnu++20", "-std=cpp", "-fno-rtti"}
 
 	lastNDKFlags := []string{
diff --git a/cc/config/global.go b/cc/config/global.go
index 5011acd..7bea124 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -375,7 +375,7 @@
 		"-w",
 	}
 
-	CStdVersion               = "gnu23"
+	CStdVersion               = "gnu17"
 	CppStdVersion             = "gnu++20"
 	ExperimentalCStdVersion   = "gnu2x"
 	ExperimentalCppStdVersion = "gnu++2b"
diff --git a/ci_tests/Android.bp b/ci_tests/Android.bp
new file mode 100644
index 0000000..181ded4
--- /dev/null
+++ b/ci_tests/Android.bp
@@ -0,0 +1,21 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+bootstrap_go_package {
+    name: "soong-ci-tests",
+    pkgPath: "android/soong/ci_tests",
+    deps: [
+        "blueprint",
+        "blueprint-proptools",
+        "soong",
+        "soong-android",
+    ],
+    srcs: [
+        "ci_test_package_zip.go",
+    ],
+    testSrcs: [
+    ],
+    pluginFor: ["soong_build"],
+    visibility: ["//visibility:public"],
+}
diff --git a/ci_tests/ci_test_package_zip.go b/ci_tests/ci_test_package_zip.go
new file mode 100644
index 0000000..d9573a3
--- /dev/null
+++ b/ci_tests/ci_test_package_zip.go
@@ -0,0 +1,237 @@
+// Copyright (C) 2025 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.
+
+package ci_tests
+
+import (
+	"fmt"
+	"path/filepath"
+	"strings"
+
+	"android/soong/android"
+	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
+)
+
+func init() {
+	pctx.Import("android/soong/android")
+	registerTestPackageZipBuildComponents(android.InitRegistrationContext)
+}
+
+func registerTestPackageZipBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("test_package", TestPackageZipFactory)
+}
+
+type testPackageZip struct {
+	android.ModuleBase
+	android.DefaultableModuleBase
+
+	properties CITestPackageProperties
+
+	output android.Path
+}
+
+type CITestPackageProperties struct {
+	// test modules will be added as dependencies using the device os and the common architecture's variant.
+	Tests proptools.Configurable[[]string] `android:"arch_variant"`
+	// test modules that will be added as dependencies based on the first supported arch variant and the device os variant
+	Device_first_tests proptools.Configurable[[]string] `android:"arch_variant"`
+	// test modules that will be added as dependencies based on both 32bit and 64bit arch variant and the device os variant
+	Device_both_tests proptools.Configurable[[]string] `android:"arch_variant"`
+	// test modules that will be added as dependencies based on host
+	Host_tests proptools.Configurable[[]string] `android:"arch_variant"`
+	// git-main only test modules. Will only be added as dependencies using the device os and the common architecture's variant if exists.
+	Tests_if_exist_common proptools.Configurable[[]string] `android:"arch_variant"`
+	// git-main only test modules. Will only be added as dependencies based on both 32bit and 64bit arch variant and the device os variant if exists.
+	Tests_if_exist_device_both proptools.Configurable[[]string] `android:"arch_variant"`
+}
+
+type testPackageZipDepTagType struct {
+	blueprint.BaseDependencyTag
+}
+
+var testPackageZipDepTag testPackageZipDepTagType
+
+var (
+	pctx = android.NewPackageContext("android/soong/ci_tests")
+	// test_package module type should only be used for the following modules.
+	// TODO: remove "_soong" from the module names inside when eliminating the corresponding make modules
+	moduleNamesAllowed = []string{"continuous_native_tests_soong", "continuous_instrumentation_tests_soong", "platform_tests"}
+)
+
+func (p *testPackageZip) DepsMutator(ctx android.BottomUpMutatorContext) {
+	// adding tests property deps
+	for _, t := range p.properties.Tests.GetOrDefault(ctx, nil) {
+		ctx.AddVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(), testPackageZipDepTag, t)
+	}
+
+	// adding device_first_tests property deps
+	for _, t := range p.properties.Device_first_tests.GetOrDefault(ctx, nil) {
+		ctx.AddVariationDependencies(ctx.Config().AndroidFirstDeviceTarget.Variations(), testPackageZipDepTag, t)
+	}
+
+	// adding device_both_tests property deps
+	p.addDeviceBothDeps(ctx, false)
+
+	// adding host_tests property deps
+	for _, t := range p.properties.Host_tests.GetOrDefault(ctx, nil) {
+		ctx.AddVariationDependencies(ctx.Config().BuildOSTarget.Variations(), testPackageZipDepTag, t)
+	}
+
+	// adding Tests_if_exist_* property deps
+	for _, t := range p.properties.Tests_if_exist_common.GetOrDefault(ctx, nil) {
+		if ctx.OtherModuleExists(t) {
+			ctx.AddVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(), testPackageZipDepTag, t)
+		}
+	}
+	p.addDeviceBothDeps(ctx, true)
+}
+
+func (p *testPackageZip) addDeviceBothDeps(ctx android.BottomUpMutatorContext, checkIfExist bool) {
+	android32TargetList := android.FirstTarget(ctx.Config().Targets[android.Android], "lib32")
+	android64TargetList := android.FirstTarget(ctx.Config().Targets[android.Android], "lib64")
+	if len(android32TargetList) > 0 {
+		maybeAndroid32Target := &android32TargetList[0]
+		if checkIfExist {
+			for _, t := range p.properties.Tests_if_exist_device_both.GetOrDefault(ctx, nil) {
+				if ctx.OtherModuleExists(t) {
+					ctx.AddFarVariationDependencies(maybeAndroid32Target.Variations(), testPackageZipDepTag, t)
+				}
+			}
+		} else {
+			ctx.AddFarVariationDependencies(maybeAndroid32Target.Variations(), testPackageZipDepTag, p.properties.Device_both_tests.GetOrDefault(ctx, nil)...)
+		}
+	}
+	if len(android64TargetList) > 0 {
+		maybeAndroid64Target := &android64TargetList[0]
+		if checkIfExist {
+			for _, t := range p.properties.Tests_if_exist_device_both.GetOrDefault(ctx, nil) {
+				if ctx.OtherModuleExists(t) {
+					ctx.AddFarVariationDependencies(maybeAndroid64Target.Variations(), testPackageZipDepTag, t)
+				}
+			}
+		} else {
+			ctx.AddFarVariationDependencies(maybeAndroid64Target.Variations(), testPackageZipDepTag, p.properties.Device_both_tests.GetOrDefault(ctx, nil)...)
+		}
+	}
+}
+
+func TestPackageZipFactory() android.Module {
+	module := &testPackageZip{}
+
+	module.AddProperties(&module.properties)
+
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibCommon)
+	android.InitDefaultableModule(module)
+
+	return module
+}
+
+func (p *testPackageZip) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	if !android.InList(ctx.ModuleName(), moduleNamesAllowed) {
+		ctx.ModuleErrorf("%s is not allowed to use module type test_package")
+	}
+
+	p.output = createOutput(ctx, pctx)
+
+	ctx.SetOutputFiles(android.Paths{p.output}, "")
+
+	// dist the test output
+	if ctx.ModuleName() == "platform_tests_soong" {
+		distedName := ctx.Config().Getenv("TARGET_PRODUCT") + "-tests-" + ctx.Config().BuildId() + ".zip"
+		ctx.DistForGoalsWithFilename([]string{"droid", "platform_tests"}, p.output, distedName)
+	}
+}
+
+func createOutput(ctx android.ModuleContext, pctx android.PackageContext) android.ModuleOutPath {
+	productOut := filepath.Join(ctx.Config().OutDir(), "target", "product", ctx.Config().DeviceName())
+	stagingDir := android.PathForModuleOut(ctx, "STAGING")
+	productVariables := ctx.Config().ProductVariables()
+	arch := proptools.String(productVariables.DeviceArch)
+	secondArch := proptools.String(productVariables.DeviceSecondaryArch)
+
+	builder := android.NewRuleBuilder(pctx, ctx)
+	builder.Command().Text("rm").Flag("-rf").Text(stagingDir.String())
+	builder.Command().Text("mkdir").Flag("-p").Output(stagingDir)
+	builder.Temporary(stagingDir)
+	ctx.VisitDirectDepsWithTag(testPackageZipDepTag, func(m android.Module) {
+		info, ok := android.OtherModuleProvider(ctx, m, android.ModuleInfoJSONProvider)
+		if !ok {
+			ctx.OtherModuleErrorf(m, "doesn't set ModuleInfoJSON provider")
+		} else if len(info) != 1 {
+			ctx.OtherModuleErrorf(m, "doesn't provide exactly one ModuleInfoJSON")
+		}
+
+		classes := info[0].GetClass()
+		if len(info[0].Class) != 1 {
+			ctx.OtherModuleErrorf(m, "doesn't have exactly one class in its ModuleInfoJSON")
+		}
+		class := strings.ToLower(classes[0])
+		if class == "apps" {
+			class = "app"
+		} else if class == "java_libraries" {
+			class = "framework"
+		}
+
+		installedFilesInfo, ok := android.OtherModuleProvider(ctx, m, android.InstallFilesProvider)
+		if !ok {
+			ctx.ModuleErrorf("Module %s doesn't set InstallFilesProvider", m.Name())
+		}
+
+		for _, installedFile := range installedFilesInfo.InstallFiles {
+			name := removeFileExtension(installedFile.Base())
+			f := strings.TrimPrefix(installedFile.String(), productOut+"/")
+			if strings.HasPrefix(f, "out") {
+				continue
+			}
+			f = strings.ReplaceAll(f, "system/", "DATA/")
+			f = strings.ReplaceAll(f, filepath.Join("testcases", name, arch), filepath.Join("DATA", class, name))
+			f = strings.ReplaceAll(f, filepath.Join("testcases", name, secondArch), filepath.Join("DATA", class, name))
+			f = strings.ReplaceAll(f, "testcases", filepath.Join("DATA", class))
+			f = strings.ReplaceAll(f, "data/", "DATA/")
+			f = strings.ReplaceAll(f, "DATA_other", "system_other")
+			f = strings.ReplaceAll(f, "system_other/DATA", "system_other/system")
+			dir := filepath.Dir(f)
+			tempOut := android.PathForModuleOut(ctx, "STAGING", f)
+			builder.Command().Text("mkdir").Flag("-p").Text(filepath.Join(stagingDir.String(), dir))
+			builder.Command().Text("cp").Flag("-Rf").Input(installedFile).Output(tempOut)
+			builder.Temporary(tempOut)
+		}
+	})
+
+	output := android.PathForModuleOut(ctx, ctx.ModuleName()+".zip")
+	builder.Command().
+		BuiltTool("soong_zip").
+		Flag("-o").Output(output).
+		Flag("-C").Text(stagingDir.String()).
+		Flag("-D").Text(stagingDir.String())
+	builder.Command().Text("rm").Flag("-rf").Text(stagingDir.String())
+	builder.Build("test_package", fmt.Sprintf("build test_package for %s", ctx.ModuleName()))
+	return output
+}
+
+func removeFileExtension(filename string) string {
+	return strings.TrimSuffix(filename, filepath.Ext(filename))
+}
+
+// The only purpose of this method is to make sure we can build the module directly
+// without adding suffix "-soong"
+func (p *testPackageZip) AndroidMkEntries() []android.AndroidMkEntries {
+	return []android.AndroidMkEntries{
+		android.AndroidMkEntries{
+			Class:      "ETC",
+			OutputFile: android.OptionalPathForPath(p.output),
+		},
+	}
+}