Snap for 13264465 from c2e8b6c102f4b4b44e2568f07d95fc674bbd2ef0 to 25Q2-release

Change-Id: I6875fd165cc96cc880a3ac7842a516a7b9e70978
diff --git a/android/Android.bp b/android/Android.bp
index 71e6747..97d634f 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -12,6 +12,7 @@
         "blueprint-gobtools",
         "blueprint-metrics",
         "blueprint-pool",
+        "blueprint-syncmap",
         "sbox_proto",
         "soong",
         "soong-android_team_proto",
diff --git a/android/androidmk.go b/android/androidmk.go
index 7cc6aef..e328359 100644
--- a/android/androidmk.go
+++ b/android/androidmk.go
@@ -435,13 +435,18 @@
 				suffix = *dist.Suffix
 			}
 
-			productString := ""
-			if dist.Append_artifact_with_product != nil && *dist.Append_artifact_with_product {
-				productString = fmt.Sprintf("_%s", ctx.Config().DeviceProduct())
+			prependProductString := ""
+			if proptools.Bool(dist.Prepend_artifact_with_product) {
+				prependProductString = fmt.Sprintf("%s-", ctx.Config().DeviceProduct())
 			}
 
-			if suffix != "" || productString != "" {
-				dest = strings.TrimSuffix(dest, ext) + suffix + productString + ext
+			appendProductString := ""
+			if proptools.Bool(dist.Append_artifact_with_product) {
+				appendProductString = fmt.Sprintf("_%s", ctx.Config().DeviceProduct())
+			}
+
+			if suffix != "" || appendProductString != "" || prependProductString != "" {
+				dest = prependProductString + strings.TrimSuffix(dest, ext) + suffix + appendProductString + ext
 			}
 
 			if dist.Dir != nil {
diff --git a/android/module.go b/android/module.go
index c0abfd0..1538861 100644
--- a/android/module.go
+++ b/android/module.go
@@ -208,6 +208,12 @@
 	// no change to the artifact file name.
 	Append_artifact_with_product *bool `android:"arch_variant"`
 
+	// If true, then the artifact file will be prepended with <product name>-. For
+	// example, if the product is coral and the module is an android_app module
+	// of name foo, then the artifact would be coral-foo.apk. If false, there is
+	// no change to the artifact file name.
+	Prepend_artifact_with_product *bool `android:"arch_variant"`
+
 	// A string tag to select the OutputFiles associated with the tag.
 	//
 	// If no tag is specified then it will select the default dist paths provided
diff --git a/android/raw_files.go b/android/raw_files.go
index fd37196..ebba4d1 100644
--- a/android/raw_files.go
+++ b/android/raw_files.go
@@ -26,6 +26,7 @@
 	"testing"
 
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/syncmap"
 
 	"github.com/google/blueprint/proptools"
 )
@@ -213,10 +214,10 @@
 
 var rawFileSetKey OnceKey = NewOnceKey("raw file set")
 
-func getRawFileSet(config Config) *SyncMap[string, rawFileInfo] {
+func getRawFileSet(config Config) *syncmap.SyncMap[string, rawFileInfo] {
 	return config.Once(rawFileSetKey, func() any {
-		return &SyncMap[string, rawFileInfo]{}
-	}).(*SyncMap[string, rawFileInfo])
+		return &syncmap.SyncMap[string, rawFileInfo]{}
+	}).(*syncmap.SyncMap[string, rawFileInfo])
 }
 
 // ContentFromFileRuleForTests returns the content that was passed to a WriteFileRule for use
diff --git a/android/test_suites.go b/android/test_suites.go
index 9eaf785..dbcd48c 100644
--- a/android/test_suites.go
+++ b/android/test_suites.go
@@ -42,6 +42,12 @@
 
 var TestSuiteInfoProvider = blueprint.NewProvider[TestSuiteInfo]()
 
+type SupportFilesInfo struct {
+	SupportFiles InstallPaths
+}
+
+var SupportFilesInfoProvider = blueprint.NewProvider[SupportFilesInfo]()
+
 func (t *testSuiteFiles) GenerateBuildActions(ctx SingletonContext) {
 	files := make(map[string]map[string]InstallPaths)
 
diff --git a/android/util.go b/android/util.go
index 7b305b5..4520f40 100644
--- a/android/util.go
+++ b/android/util.go
@@ -23,7 +23,6 @@
 	"runtime"
 	"sort"
 	"strings"
-	"sync"
 
 	"github.com/google/blueprint/proptools"
 )
@@ -646,35 +645,6 @@
 	}
 }
 
-// SyncMap is a wrapper around sync.Map that provides type safety via generics.
-type SyncMap[K comparable, V any] struct {
-	sync.Map
-}
-
-// Load returns the value stored in the map for a key, or the zero value if no
-// value is present.
-// The ok result indicates whether value was found in the map.
-func (m *SyncMap[K, V]) Load(key K) (value V, ok bool) {
-	v, ok := m.Map.Load(key)
-	if !ok {
-		return *new(V), false
-	}
-	return v.(V), true
-}
-
-// Store sets the value for a key.
-func (m *SyncMap[K, V]) Store(key K, value V) {
-	m.Map.Store(key, value)
-}
-
-// LoadOrStore returns the existing value for the key if present.
-// Otherwise, it stores and returns the given value.
-// The loaded result is true if the value was loaded, false if stored.
-func (m *SyncMap[K, V]) LoadOrStore(key K, value V) (actual V, loaded bool) {
-	v, loaded := m.Map.LoadOrStore(key, value)
-	return v.(V), loaded
-}
-
 // AppendIfNotZero append the given value to the slice if it is not the zero value
 // for its type.
 func AppendIfNotZero[T comparable](slice []T, value T) []T {
diff --git a/apex/prebuilt.go b/apex/prebuilt.go
index 89b0091..fdd9a75 100644
--- a/apex/prebuilt.go
+++ b/apex/prebuilt.go
@@ -15,7 +15,9 @@
 package apex
 
 import (
+	"fmt"
 	"slices"
+	"sort"
 	"strconv"
 	"strings"
 
@@ -80,6 +82,10 @@
 	// systemServerDexJars stores the list of dexjars for system server jars in the prebuilt for use when
 	// dexpreopting system server jars that are later in the system server classpath.
 	systemServerDexJars android.Paths
+
+	// Certificate information of any apk packaged inside the prebuilt apex.
+	// This will be nil if the prebuilt apex does not contain any apk.
+	apkCertsFile android.WritablePath
 }
 
 type sanitizedPrebuilt interface {
@@ -273,6 +279,10 @@
 					entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !p.installable())
 					entries.AddStrings("LOCAL_OVERRIDES_MODULES", p.prebuiltCommonProperties.Overrides...)
 					entries.SetString("LOCAL_APEX_KEY_PATH", p.apexKeysPath.String())
+					if p.apkCertsFile != nil {
+						entries.SetString("LOCAL_APKCERTS_FILE", p.apkCertsFile.String())
+					}
+
 				},
 			},
 		},
@@ -286,6 +296,14 @@
 		len(p.prebuiltCommonProperties.Exported_systemserverclasspath_fragments) > 0
 }
 
+type appInPrebuiltApexDepTag struct {
+	blueprint.BaseDependencyTag
+}
+
+func (appInPrebuiltApexDepTag) ExcludeFromVisibilityEnforcement() {}
+
+var appInPrebuiltApexTag = appInPrebuiltApexDepTag{}
+
 // prebuiltApexContentsDeps adds dependencies onto the prebuilt apex module's contents.
 func (p *prebuiltCommon) prebuiltApexContentsDeps(ctx android.BottomUpMutatorContext) {
 	module := ctx.Module()
@@ -432,6 +450,12 @@
 	ApexFileProperties
 
 	PrebuiltCommonProperties
+
+	// List of apps that are bundled inside this prebuilt apex.
+	// This will be used to create the certificate info of those apps for apkcerts.txt
+	// This dependency will only be used for apkcerts.txt processing.
+	// Notably, building the prebuilt apex will not build the source app.
+	Apps []string
 }
 
 func (a *Prebuilt) hasSanitizedSource(sanitizer string) bool {
@@ -545,6 +569,9 @@
 
 func (p *Prebuilt) ComponentDepsMutator(ctx android.BottomUpMutatorContext) {
 	p.prebuiltApexContentsDeps(ctx)
+	for _, app := range p.properties.Apps {
+		ctx.AddDependency(p, appInPrebuiltApexTag, app)
+	}
 }
 
 var _ ApexTransitionMutator = (*Prebuilt)(nil)
@@ -677,11 +704,59 @@
 		p.provenanceMetaDataFile = provenance.GenerateArtifactProvenanceMetaData(ctx, p.inputApex, p.installedFile)
 	}
 
+	p.addApkCertsInfo(ctx)
+
 	ctx.SetOutputFiles(android.Paths{p.outputApex}, "")
 
 	android.SetProvider(ctx, filesystem.ApexKeyPathInfoProvider, filesystem.ApexKeyPathInfo{p.apexKeysPath})
 }
 
+// `addApkCertsInfo` sets a provider that will be used to create apkcerts.txt
+func (p *Prebuilt) addApkCertsInfo(ctx android.ModuleContext) {
+	formatLine := func(cert java.Certificate, name, partition string) string {
+		pem := cert.AndroidMkString()
+		var key string
+		if cert.Key == nil {
+			key = ""
+		} else {
+			key = cert.Key.String()
+		}
+		return fmt.Sprintf(`name="%s" certificate="%s" private_key="%s" partition="%s"`, name, pem, key, partition)
+	}
+
+	// Determine if this prebuilt_apex contains any .apks
+	var appInfos java.AppInfos
+	ctx.VisitDirectDepsProxyWithTag(appInPrebuiltApexTag, func(app android.ModuleProxy) {
+		if appInfo, ok := android.OtherModuleProvider(ctx, app, java.AppInfoProvider); ok {
+			appInfos = append(appInfos, *appInfo)
+		} else {
+			ctx.ModuleErrorf("App %s does not set AppInfoProvider\n", app.Name())
+		}
+	})
+	sort.Slice(appInfos, func(i, j int) bool {
+		return appInfos[i].InstallApkName < appInfos[j].InstallApkName
+	})
+
+	if len(appInfos) == 0 {
+		return
+	}
+
+	// Set a provider for use by `android_device`.
+	// `android_device` will create an apkcerts.txt with the list of installed apps for that device.
+	android.SetProvider(ctx, java.AppInfosProvider, appInfos)
+
+	// Set a Make variable for legacy apkcerts.txt creation
+	// p.apkCertsFile will become `LOCAL_APKCERTS_FILE`
+	var lines []string
+	for _, appInfo := range appInfos {
+		lines = append(lines, formatLine(appInfo.Certificate, appInfo.InstallApkName+".apk", p.PartitionTag(ctx.DeviceConfig())))
+	}
+	if len(lines) > 0 {
+		p.apkCertsFile = android.PathForModuleOut(ctx, "apkcerts.txt")
+		android.WriteFileRule(ctx, p.apkCertsFile, strings.Join(lines, "\n"))
+	}
+}
+
 func (p *Prebuilt) ProvenanceMetaDataFile() android.Path {
 	return p.provenanceMetaDataFile
 }
diff --git a/ci_tests/ci_test_package_zip.go b/ci_tests/ci_test_package_zip.go
index d7aaa66..4cadffd 100644
--- a/ci_tests/ci_test_package_zip.go
+++ b/ci_tests/ci_test_package_zip.go
@@ -68,7 +68,7 @@
 	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"}
+	moduleNamesAllowed = []string{"continuous_instrumentation_tests_soong", "continuous_instrumentation_metric_tests_soong", "continuous_native_tests_soong", "continuous_native_metric_tests_soong", "platform_tests"}
 )
 
 func (p *testPackageZip) DepsMutator(ctx android.BottomUpMutatorContext) {
@@ -150,12 +150,6 @@
 	p.output = createOutput(ctx, pctx)
 
 	ctx.SetOutputFiles(android.Paths{p.output}, "")
-
-	// dist the test output
-	if ctx.ModuleName() == "platform_tests" {
-		distedName := ctx.Config().Getenv("TARGET_PRODUCT") + "-tests-FILE_NAME_TAG_PLACEHOLDER.zip"
-		ctx.DistForGoalWithFilename("platform_tests", p.output, distedName)
-	}
 }
 
 func createOutput(ctx android.ModuleContext, pctx android.PackageContext) android.ModuleOutPath {
diff --git a/filesystem/android_device.go b/filesystem/android_device.go
index b96c5ea..8b6ea49 100644
--- a/filesystem/android_device.go
+++ b/filesystem/android_device.go
@@ -120,6 +120,8 @@
 	rootDirForFsConfig          string
 	rootDirForFsConfigTimestamp android.Path
 	apkCertsInfo                android.Path
+	targetFilesZip              android.Path
+	updatePackage               android.Path
 }
 
 func AndroidDeviceFactory() android.Module {
@@ -199,6 +201,7 @@
 	a.miscInfo = a.addMiscInfo(ctx)
 	a.buildTargetFilesZip(ctx, allInstalledModules)
 	a.buildProguardZips(ctx, allInstalledModules)
+	a.buildUpdatePackage(ctx)
 
 	var deps []android.Path
 	if proptools.String(a.partitionProps.Super_partition_name) != "" {
@@ -401,6 +404,12 @@
 				ctx.DistForGoalWithFilename("dist_files", a.miscInfo, "super_misc_info.txt")
 			}
 		}
+		if a.targetFilesZip != nil {
+			ctx.DistForGoalWithFilename("target-files-package", a.targetFilesZip, namePrefix+insertBeforeExtension(a.targetFilesZip.Base(), "-FILE_NAME_TAG_PLACEHOLDER"))
+		}
+		if a.updatePackage != nil {
+			ctx.DistForGoalWithFilename("updatepackage", a.updatePackage, namePrefix+insertBeforeExtension(a.updatePackage.Base(), "-FILE_NAME_TAG_PLACEHOLDER"))
+		}
 
 	}
 }
@@ -590,6 +599,7 @@
 	a.copyImagesToTargetZip(ctx, builder, targetFilesDir)
 	a.copyMetadataToTargetZip(ctx, builder, targetFilesDir, allInstalledModules)
 
+	a.targetFilesZip = targetFilesZip
 	builder.Command().
 		BuiltTool("soong_zip").
 		Text("-d").
@@ -943,6 +953,38 @@
 		Text(targetFilesDir)
 }
 
+func (a *androidDevice) buildUpdatePackage(ctx android.ModuleContext) {
+	var exclusions []string
+	fsInfos := a.getFsInfos(ctx)
+	// Exclude the partitions that are not supported by flashall
+	for _, partition := range android.SortedKeys(fsInfos) {
+		if fsInfos[partition].NoFlashall {
+			exclusions = append(exclusions, fmt.Sprintf("IMAGES/%s.img", partition))
+			exclusions = append(exclusions, fmt.Sprintf("IMAGES/%s.map", partition))
+		}
+	}
+
+	updatePackage := android.PathForModuleOut(ctx, "img.zip")
+	rule := android.NewRuleBuilder(pctx, ctx)
+
+	buildSuperImage := ctx.Config().HostToolPath(ctx, "build_super_image")
+	zip2zip := ctx.Config().HostToolPath(ctx, "zip2zip")
+
+	rule.Command().
+		BuiltTool("img_from_target_files").
+		Text("--additional IMAGES/VerifiedBootParams.textproto:VerifiedBootParams.textproto").
+		FlagForEachArg("--exclude ", exclusions).
+		FlagWithArg("--build_super_image ", buildSuperImage.String()).
+		Implicit(buildSuperImage).
+		Implicit(zip2zip).
+		Input(a.targetFilesZip).
+		Output(updatePackage)
+
+	rule.Build("updatepackage", "Building updatepackage")
+
+	a.updatePackage = updatePackage
+}
+
 type ApexKeyPathInfo struct {
 	ApexKeyPath android.Path
 }
diff --git a/filesystem/filesystem.go b/filesystem/filesystem.go
index fd1c784..c3c3835 100644
--- a/filesystem/filesystem.go
+++ b/filesystem/filesystem.go
@@ -249,6 +249,10 @@
 
 	// Whether to enable per-file compression in f2fs
 	Enable_compression *bool
+
+	// Whether this partition is not supported by flashall.
+	// If true, this partition will not be included in the `updatedpackage` dist artifact.
+	No_flashall *bool
 }
 
 type AndroidFilesystemDeps struct {
@@ -464,6 +468,7 @@
 	AvbHashAlgorithm string
 	AvbKey           android.Path
 	PartitionName    string
+	NoFlashall       bool
 	// HasOrIsRecovery returns true for recovery and for ramdisks with a recovery partition.
 	HasOrIsRecovery bool
 }
@@ -725,6 +730,7 @@
 		PartitionSize:       f.properties.Partition_size,
 		PartitionName:       f.partitionName(),
 		HasOrIsRecovery:     f.hasOrIsRecovery(ctx),
+		NoFlashall:          proptools.Bool(f.properties.No_flashall),
 	}
 	if proptools.Bool(f.properties.Use_avb) {
 		fsInfo.UseAvb = true
diff --git a/java/dex.go b/java/dex.go
index f2406fb..dd64675 100644
--- a/java/dex.go
+++ b/java/dex.go
@@ -42,9 +42,6 @@
 		// True if the module containing this has it set by default.
 		EnabledByDefault bool `blueprint:"mutated"`
 
-		// If true, then this module will be optimized on eng builds.
-		Enabled_on_eng *bool
-
 		// Whether to allow that library classes inherit from program classes.
 		// Defaults to false.
 		Ignore_library_extends_program *bool
@@ -165,10 +162,7 @@
 }
 
 func (d *dexer) effectiveOptimizeEnabled(ctx android.EarlyModuleContext) bool {
-	if ctx.Config().Eng() {
-		return proptools.Bool(d.dexProperties.Optimize.Enabled_on_eng)
-	}
-	return BoolDefault(d.dexProperties.Optimize.Enabled, d.dexProperties.Optimize.EnabledByDefault)
+	return BoolDefault(d.dexProperties.Optimize.Enabled, d.dexProperties.Optimize.EnabledByDefault && !ctx.Config().Eng())
 }
 
 func (d *DexProperties) resourceShrinkingEnabled(ctx android.ModuleContext) bool {
diff --git a/java/legacy_core_platform_api_usage.go b/java/legacy_core_platform_api_usage.go
index 4be7d04..21895d4 100644
--- a/java/legacy_core_platform_api_usage.go
+++ b/java/legacy_core_platform_api_usage.go
@@ -47,8 +47,8 @@
 	"sam",
 	"saminterfacelibrary",
 	"sammanagerlibrary",
-	"services",
 	"services.core.unboosted",
+	"services.impl",
 	"Settings-core",
 	"SettingsGoogle",
 	"SettingsGoogleOverlayCoral",
diff --git a/scripts/Android.bp b/scripts/Android.bp
index c0e13d5..b6cac32 100644
--- a/scripts/Android.bp
+++ b/scripts/Android.bp
@@ -254,11 +254,6 @@
     ],
 }
 
-sh_binary_host {
-    name: "list_image",
-    src: "list_image.sh",
-}
-
 filegroup {
     name: "rustfmt.toml",
     srcs: ["rustfmt.toml"],
diff --git a/scripts/list_image.sh b/scripts/list_image.sh
deleted file mode 100755
index 0542fa6..0000000
--- a/scripts/list_image.sh
+++ /dev/null
@@ -1,51 +0,0 @@
-#! /bin/bash
-
-# Recursively list Android image directory.
-set -eu
-set -o pipefail
-
-function die() { format=$1; shift; printf "$format\n" "$@"; exit 1; }
-
-# Figure out the filer utility.
-declare filer=
-[[ -z "${ANDROID_HOST_OUT:-}" ]] || filer=${ANDROID_HOST_OUT}/bin/debugfs_static
-if [[ "${1:-}" =~ --debugfs_path=(.*) ]]; then
-  filer=${BASH_REMATCH[1]}
-  shift
-fi
-if [[ -z "${filer:-}" ]]; then
-  maybefiler="$(dirname $0)/debugfs_static"
-  [[ ! -x "$maybefiler" ]] || filer="$maybefiler"
-fi
-
-(( $# >0 )) || die "%s [--debugfs_path=<path>] IMAGE" "$0"
-
-[[ -n "${filer:-}" ]] || die "cannot locate 'debugfs' executable: \
---debugfs_path= is missing, ANDROID_HOST_OUT is not set, \
-and 'debugfs_static' is not colocated with this script"
-declare -r image="$1"
-
-function dolevel() {
-  printf "%s/\n" "$1"
-  # Each line of the file output consists of 6 fields separated with '/'.
-  # The second one contains the file's attributes, and the fifth its name.
-  $filer -R "ls -l -p $1" "$image" 2>/dev/null |\
-    sed -nr 's|^/.*/(.*)/.*/.*/(.+)/.*/$|\2 \1|p' | LANG=C sort | \
-  while read name attr; do
-    [[ "$name" != '.' && "$name" != '..' ]] || continue
-    path="$1/$name"
-    # If the second char of the attributes is '4', it is a directory.
-    if [[ $attr =~ ^.4 ]]; then
-      dolevel "$path"
-    else
-      printf "%s\n" "$path"
-    fi
-  done
-}
-
-# The filer always prints its version on stderr, so we are going
-# to redirect it to the bit bucket. On the other hand, the filer's
-# return code on error is still 0. Let's run it once to without
-# redirecting stderr to see that there is at least one entry.
-$filer -R "ls -l -p" "$image" | grep -q -m1 -P '^/.*/.*/.*/.*/.+/.*/$'
-dolevel .
diff --git a/tradefed_modules/test_module_config.go b/tradefed_modules/test_module_config.go
index 2b34128..e833df2 100644
--- a/tradefed_modules/test_module_config.go
+++ b/tradefed_modules/test_module_config.go
@@ -1,14 +1,15 @@
 package tradefed_modules
 
 import (
-	"android/soong/android"
-	"android/soong/tradefed"
 	"encoding/json"
 	"fmt"
 	"io"
 	"slices"
 	"strings"
 
+	"android/soong/android"
+	"android/soong/tradefed"
+
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
 )
@@ -178,6 +179,10 @@
 	moduleInfoJSON.TestConfig = []string{m.testConfig.String()}
 	moduleInfoJSON.AutoTestConfig = []string{"true"}
 	moduleInfoJSON.TestModuleConfigBase = proptools.String(m.Base)
+
+	android.SetProvider(ctx, android.SupportFilesInfoProvider, android.SupportFilesInfo{
+		SupportFiles: m.supportFiles,
+	})
 }
 
 // Ensure at least one test_suite is listed.  Ideally it should be general-tests
@@ -327,6 +332,9 @@
 func (m *testModuleConfigHostModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	m.validateBase(ctx, &testModuleConfigHostTag, "java_test_host", true)
 	m.generateManifestAndConfig(ctx)
+	android.SetProvider(ctx, android.SupportFilesInfoProvider, android.SupportFilesInfo{
+		SupportFiles: m.supportFiles,
+	})
 }
 
 // Ensure the base listed is the right type by checking that we get the expected provider data.
diff --git a/ui/build/androidmk_denylist.go b/ui/build/androidmk_denylist.go
index 622aadb..8266654 100644
--- a/ui/build/androidmk_denylist.go
+++ b/ui/build/androidmk_denylist.go
@@ -62,6 +62,7 @@
 var androidmk_allowlist []string = []string{
 	"art/Android.mk",
 	"bootable/deprecated-ota/updater/Android.mk",
+	"tools/vendor/google_prebuilts/arc/Android.mk",
 }
 
 func getAllLines(ctx Context, filename string) []string {
@@ -77,8 +78,17 @@
 }
 
 func blockAndroidMks(ctx Context, androidMks []string) {
-	allowlist := getAllLines(ctx, "vendor/google/build/androidmk/allowlist.txt")
-	androidmk_allowlist = append(androidmk_allowlist, allowlist...)
+	allowlist_files := []string{
+		"vendor/google/build/androidmk/allowlist.txt",
+		"device/google/clockwork/build/androidmk/allowlist.txt",
+		"device/google/sdv/androidmk/allowlist.txt",
+	}
+	for _, allowlist_file := range allowlist_files {
+		allowlist := getAllLines(ctx, allowlist_file)
+		androidmk_allowlist = append(androidmk_allowlist, allowlist...)
+	}
+	slices.Sort(androidmk_allowlist)
+	androidmk_allowlist = slices.Compact(androidmk_allowlist)
 
 	denylist := getAllLines(ctx, "vendor/google/build/androidmk/denylist.txt")
 	androidmk_denylist = append(androidmk_denylist, denylist...)