Merge "Enable benchmarks on darwin"
diff --git a/android/config.go b/android/config.go
index 695a298..367b42c 100644
--- a/android/config.go
+++ b/android/config.go
@@ -881,6 +881,10 @@
 	return Bool(c.productVariables.Ndk_abis)
 }
 
+func (c *config) FlattenApex() bool {
+	return Bool(c.productVariables.FlattenApex)
+}
+
 func stringSlice(s *[]string) []string {
 	if s != nil {
 		return *s
diff --git a/android/variable.go b/android/variable.go
index 2686049..2763bf2 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -250,6 +250,8 @@
 	VendorVars map[string]map[string]string `json:",omitempty"`
 
 	Ndk_abis *bool `json:",omitempty"`
+
+	FlattenApex *bool `json:",omitempty"`
 }
 
 func boolPtr(v bool) *bool {
diff --git a/apex/apex.go b/apex/apex.go
index 3e7c0a7..177856e 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -171,8 +171,10 @@
 	// "manifest.json"
 	Manifest *string
 
-	// File contexts file for setting security context to each file in this APEX bundle
-	// Default: "file_contexts".
+	// Determines the file contexts file for setting security context to each file in this APEX bundle.
+	// Specifically, when this is set to <value>, /system/sepolicy/apex/<value>_file_contexts file is
+	// used.
+	// Default: <name_of_this_module>
 	File_contexts *string
 
 	// List of native shared libs that are embedded inside this APEX bundle
@@ -228,6 +230,38 @@
 	}
 }
 
+type apexFileClass int
+
+const (
+	etc apexFileClass = iota
+	nativeSharedLib
+	nativeExecutable
+	javaSharedLib
+)
+
+func (class apexFileClass) NameInMake() string {
+	switch class {
+	case etc:
+		return "ETC"
+	case nativeSharedLib:
+		return "SHARED_LIBRARIES"
+	case nativeExecutable:
+		return "EXECUTABLES"
+	case javaSharedLib:
+		return "JAVA_LIBRARIES"
+	default:
+		panic(fmt.Errorf("unkonwn class %d", class))
+	}
+}
+
+type apexFile struct {
+	builtFile  android.Path
+	moduleName string
+	archType   android.ArchType
+	installDir string
+	class      apexFileClass
+}
+
 type apexBundle struct {
 	android.ModuleBase
 	android.DefaultableModuleBase
@@ -236,6 +270,11 @@
 
 	outputFile android.WritablePath
 	installDir android.OutputPath
+
+	// list of files to be included in this apex
+	filesInfo []apexFile
+
+	flattened bool
 }
 
 func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext,
@@ -362,7 +401,7 @@
 
 func getCopyManifestForJavaLibrary(java *java.Library) (fileToCopy android.Path, dirInApex string) {
 	dirInApex = "javalib"
-	fileToCopy = java.Srcs()[0]
+	fileToCopy = java.DexJarFile()
 	return
 }
 
@@ -373,8 +412,7 @@
 }
 
 func (a *apexBundle) GenerateAndroidBuildActions(ctx android.ModuleContext) {
-	// files to copy -> dir in apex
-	copyManifest := make(map[android.Path]string)
+	filesInfo := []apexFile{}
 
 	var keyFile android.Path
 	var certificate java.Certificate
@@ -388,7 +426,7 @@
 			case sharedLibTag:
 				if cc, ok := child.(*cc.Module); ok {
 					fileToCopy, dirInApex := getCopyManifestForNativeLibrary(cc)
-					copyManifest[fileToCopy] = dirInApex
+					filesInfo = append(filesInfo, apexFile{fileToCopy, depName, cc.Arch().ArchType, dirInApex, nativeSharedLib})
 					return true
 				} else {
 					ctx.PropertyErrorf("native_shared_libs", "%q is not a cc_library or cc_library_shared module", depName)
@@ -396,7 +434,7 @@
 			case executableTag:
 				if cc, ok := child.(*cc.Module); ok {
 					fileToCopy, dirInApex := getCopyManifestForExecutable(cc)
-					copyManifest[fileToCopy] = dirInApex
+					filesInfo = append(filesInfo, apexFile{fileToCopy, depName, cc.Arch().ArchType, dirInApex, nativeExecutable})
 					return true
 				} else {
 					ctx.PropertyErrorf("binaries", "%q is not a cc_binary module", depName)
@@ -404,7 +442,11 @@
 			case javaLibTag:
 				if java, ok := child.(*java.Library); ok {
 					fileToCopy, dirInApex := getCopyManifestForJavaLibrary(java)
-					copyManifest[fileToCopy] = dirInApex
+					if fileToCopy == nil {
+						ctx.PropertyErrorf("java_libs", "%q is not configured to be compiled into dex", depName)
+					} else {
+						filesInfo = append(filesInfo, apexFile{fileToCopy, depName, java.Arch().ArchType, dirInApex, javaSharedLib})
+					}
 					return true
 				} else {
 					ctx.PropertyErrorf("java_libs", "%q is not a java_library module", depName)
@@ -412,7 +454,7 @@
 			case prebuiltTag:
 				if prebuilt, ok := child.(*android.PrebuiltEtc); ok {
 					fileToCopy, dirInApex := getCopyManifestForPrebuiltEtc(prebuilt)
-					copyManifest[fileToCopy] = dirInApex
+					filesInfo = append(filesInfo, apexFile{fileToCopy, depName, prebuilt.Arch().ArchType, dirInApex, etc})
 					return true
 				} else {
 					ctx.PropertyErrorf("prebuilts", "%q is not a prebuilt_etc module", depName)
@@ -436,8 +478,9 @@
 			// indirect dependencies
 			if am, ok := child.(android.ApexModule); ok && am.CanHaveApexVariants() && am.IsInstallableToApex() {
 				if cc, ok := child.(*cc.Module); ok {
+					depName := ctx.OtherModuleName(child)
 					fileToCopy, dirInApex := getCopyManifestForNativeLibrary(cc)
-					copyManifest[fileToCopy] = dirInApex
+					filesInfo = append(filesInfo, apexFile{fileToCopy, depName, cc.Arch().ArchType, dirInApex, nativeSharedLib})
 					return true
 				}
 			}
@@ -450,6 +493,42 @@
 		return
 	}
 
+	// remove duplicates in filesInfo
+	removeDup := func(filesInfo []apexFile) []apexFile {
+		encountered := make(map[android.Path]bool)
+		result := []apexFile{}
+		for _, f := range filesInfo {
+			if !encountered[f.builtFile] {
+				encountered[f.builtFile] = true
+				result = append(result, f)
+			}
+		}
+		return result
+	}
+	filesInfo = removeDup(filesInfo)
+
+	// to have consistent build rules
+	sort.Slice(filesInfo, func(i, j int) bool {
+		return filesInfo[i].builtFile.String() < filesInfo[j].builtFile.String()
+	})
+
+	// prepend the name of this APEX to the module names. These names will be the names of
+	// modules that will be defined if the APEX is flattened.
+	for i := range filesInfo {
+		filesInfo[i].moduleName = ctx.ModuleName() + "." + filesInfo[i].moduleName
+	}
+
+	a.flattened = ctx.Config().FlattenApex()
+	a.installDir = android.PathForModuleInstall(ctx, "apex")
+	a.filesInfo = filesInfo
+	if ctx.Config().FlattenApex() {
+		a.buildFlattenedApex(ctx)
+	} else {
+		a.buildUnflattenedApex(ctx, keyFile, certificate)
+	}
+}
+
+func (a *apexBundle) buildUnflattenedApex(ctx android.ModuleContext, keyFile android.Path, certificate java.Certificate) {
 	cert := String(a.properties.Certificate)
 	if cert != "" && android.SrcIsModule(cert) == "" {
 		defaultDir := ctx.Config().DefaultAppCertificateDir(ctx)
@@ -465,15 +544,15 @@
 	// files and dirs that will be created in apex
 	var readOnlyPaths []string
 	var executablePaths []string // this also includes dirs
-	for fileToCopy, dirInApex := range copyManifest {
-		pathInApex := filepath.Join(dirInApex, fileToCopy.Base())
-		if dirInApex == "bin" {
+	for _, f := range a.filesInfo {
+		pathInApex := filepath.Join(f.installDir, f.builtFile.Base())
+		if f.installDir == "bin" {
 			executablePaths = append(executablePaths, pathInApex)
 		} else {
 			readOnlyPaths = append(readOnlyPaths, pathInApex)
 		}
-		if !android.InList(dirInApex, executablePaths) {
-			executablePaths = append(executablePaths, dirInApex)
+		if !android.InList(f.installDir, executablePaths) {
+			executablePaths = append(executablePaths, f.installDir)
 		}
 	}
 	sort.Strings(readOnlyPaths)
@@ -489,21 +568,26 @@
 	})
 
 	manifest := android.PathForModuleSrc(ctx, proptools.StringDefault(a.properties.Manifest, "manifest.json"))
-	fileContexts := android.PathForModuleSrc(ctx, proptools.StringDefault(a.properties.File_contexts, "file_contexts"))
+
+	fcName := proptools.StringDefault(a.properties.File_contexts, a.ModuleBase.Name())
+	fileContextsPath := "system/sepolicy/apex/" + fcName + "_file_contexts"
+	fileContextsOptionalPath := android.ExistentPathForSource(ctx, fileContextsPath)
+	if !fileContextsOptionalPath.Valid() {
+		ctx.ModuleErrorf("Cannot find file_contexts file: %q", fileContextsPath)
+		return
+	}
+	fileContexts := fileContextsOptionalPath.Path()
 
 	unsignedOutputFile := android.PathForModuleOut(ctx, a.ModuleBase.Name()+apexSuffix+".unsigned")
 
 	filesToCopy := []android.Path{}
-	for file := range copyManifest {
-		filesToCopy = append(filesToCopy, file)
+	for _, f := range a.filesInfo {
+		filesToCopy = append(filesToCopy, f.builtFile)
 	}
-	sort.Slice(filesToCopy, func(i, j int) bool {
-		return filesToCopy[i].String() < filesToCopy[j].String()
-	})
 
 	copyCommands := []string{}
-	for _, src := range filesToCopy {
-		dest := filepath.Join(copyManifest[src], src.Base())
+	for i, src := range filesToCopy {
+		dest := filepath.Join(a.filesInfo[i].installDir, src.Base())
 		dest_path := filepath.Join(android.PathForModuleOut(ctx, "image").String(), dest)
 		copyCommands = append(copyCommands, "mkdir -p "+filepath.Dir(dest_path))
 		copyCommands = append(copyCommands, "cp "+src.String()+" "+dest_path)
@@ -537,23 +621,71 @@
 			"certificates": strings.Join([]string{certificate.Pem.String(), certificate.Key.String()}, " "),
 		},
 	})
+}
 
-	a.installDir = android.PathForModuleInstall(ctx, "apex")
+func (a *apexBundle) buildFlattenedApex(ctx android.ModuleContext) {
+	// For flattened APEX, do nothing but make sure that manifest.json file is also copied along
+	// with other ordinary files.
+	manifest := android.PathForModuleSrc(ctx, proptools.StringDefault(a.properties.Manifest, "manifest.json"))
+	a.filesInfo = append(a.filesInfo, apexFile{manifest, a.Name() + ".manifest.json", android.Common, ".", etc})
+
+	for _, fi := range a.filesInfo {
+		dir := filepath.Join("apex", a.Name(), fi.installDir)
+		ctx.InstallFile(android.PathForModuleInstall(ctx, dir), fi.builtFile.Base(), fi.builtFile)
+	}
 }
 
 func (a *apexBundle) AndroidMk() android.AndroidMkData {
-	return android.AndroidMkData{
-		Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
-			fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
-			fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
-			fmt.Fprintln(w, "LOCAL_MODULE :=", name)
-			fmt.Fprintln(w, "LOCAL_MODULE_CLASS := ETC") // do we need a new class?
-			fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", a.outputFile.String())
-			fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", filepath.Join("$(OUT_DIR)", a.installDir.RelPathString()))
-			fmt.Fprintln(w, "LOCAL_INSTALLED_MODULE_STEM :=", name+apexSuffix)
-			fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES :=", String(a.properties.Key))
-			fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
-		}}
+	if a.flattened {
+		return android.AndroidMkData{
+			Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
+				moduleNames := []string{}
+				for _, fi := range a.filesInfo {
+					if !android.InList(fi.moduleName, moduleNames) {
+						moduleNames = append(moduleNames, fi.moduleName)
+					}
+				}
+				fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
+				fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
+				fmt.Fprintln(w, "LOCAL_MODULE :=", name)
+				fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES :=", strings.Join(moduleNames, " "))
+				fmt.Fprintln(w, "include $(BUILD_PHONY_PACKAGE)")
+
+				for _, fi := range a.filesInfo {
+					fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
+					fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
+					fmt.Fprintln(w, "LOCAL_MODULE :=", fi.moduleName)
+					fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", filepath.Join("$(OUT_DIR)", a.installDir.RelPathString(), name, fi.installDir))
+					fmt.Fprintln(w, "LOCAL_INSTALLED_MODULE_STEM :=", fi.builtFile.Base())
+					fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", fi.builtFile.String())
+					fmt.Fprintln(w, "LOCAL_MODULE_CLASS :=", fi.class.NameInMake())
+					archStr := fi.archType.String()
+					if archStr != "common" {
+						fmt.Fprintln(w, "LOCAL_MODULE_TARGET_ARCH :=", archStr)
+					}
+					if fi.class == javaSharedLib {
+						fmt.Fprintln(w, "LOCAL_SOONG_DEX_JAR :=", fi.builtFile.String())
+						fmt.Fprintln(w, "LOCAL_DEX_PREOPT := false")
+						fmt.Fprintln(w, "include $(BUILD_SYSTEM)/soong_java_prebuilt.mk")
+					} else {
+						fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
+					}
+				}
+			}}
+	} else {
+		return android.AndroidMkData{
+			Custom: func(w io.Writer, name, prefix, moduleDir string, data android.AndroidMkData) {
+				fmt.Fprintln(w, "\ninclude $(CLEAR_VARS)")
+				fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
+				fmt.Fprintln(w, "LOCAL_MODULE :=", name)
+				fmt.Fprintln(w, "LOCAL_MODULE_CLASS := ETC") // do we need a new class?
+				fmt.Fprintln(w, "LOCAL_PREBUILT_MODULE_FILE :=", a.outputFile.String())
+				fmt.Fprintln(w, "LOCAL_MODULE_PATH :=", filepath.Join("$(OUT_DIR)", a.installDir.RelPathString()))
+				fmt.Fprintln(w, "LOCAL_INSTALLED_MODULE_STEM :=", name+apexSuffix)
+				fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES :=", String(a.properties.Key))
+				fmt.Fprintln(w, "include $(BUILD_PREBUILT)")
+			}}
+	}
 }
 
 func apexBundleFactory() android.Module {
diff --git a/cc/config/arm_device.go b/cc/config/arm_device.go
index 75f5962..d759125 100644
--- a/cc/config/arm_device.go
+++ b/cc/config/arm_device.go
@@ -66,6 +66,11 @@
 			"-mfloat-abi=softfp",
 			"-mfpu=neon-fp-armv8",
 		},
+		"armv8-2a": []string{
+			"-march=armv8.2-a",
+			"-mfloat-abi=softfp",
+			"-mfpu=neon-fp-armv8",
+		},
 	}
 
 	armClangCpuVariantCflags = map[string][]string{
@@ -161,6 +166,7 @@
 		"armv7-a",
 		"armv7-a-neon",
 		"armv8-a",
+		"armv8-2a",
 		"cortex-a7",
 		"cortex-a8",
 		"cortex-a9",
@@ -180,6 +186,7 @@
 
 	android.RegisterArchVariantFeatures(android.Arm, "armv7-a-neon", "neon")
 	android.RegisterArchVariantFeatures(android.Arm, "armv8-a", "neon")
+	android.RegisterArchVariantFeatures(android.Arm, "armv8-2a", "neon")
 
 	pctx.StaticVariable("armGccVersion", armGccVersion)
 
@@ -208,6 +215,8 @@
 		strings.Join(armClangArchVariantCflags["armv7-a-neon"], " "))
 	pctx.StaticVariable("ArmClangArmv8ACflags",
 		strings.Join(armClangArchVariantCflags["armv8-a"], " "))
+	pctx.StaticVariable("ArmClangArmv82ACflags",
+		strings.Join(armClangArchVariantCflags["armv8-2a"], " "))
 
 	// Clang cpu variant cflags
 	pctx.StaticVariable("ArmClangGenericCflags",
@@ -233,6 +242,7 @@
 		"armv7-a":      "${config.ArmClangArmv7ACflags}",
 		"armv7-a-neon": "${config.ArmClangArmv7ANeonCflags}",
 		"armv8-a":      "${config.ArmClangArmv8ACflags}",
+		"armv8-2a":      "${config.ArmClangArmv82ACflags}",
 	}
 
 	armClangCpuVariantCflagsVar = map[string]string{
@@ -347,8 +357,8 @@
 		}
 	case "armv7-a":
 		fixCortexA8 = "-Wl,--fix-cortex-a8"
-	case "armv8-a":
-		// Nothing extra for armv8-a
+	case "armv8-a", "armv8-2a":
+		// Nothing extra for armv8-a/armv8-2a
 	default:
 		panic(fmt.Sprintf("Unknown ARM architecture version: %q", arch.ArchVariant))
 	}
diff --git a/java/config/kotlin.go b/java/config/kotlin.go
index 35f9e9d..432840e 100644
--- a/java/config/kotlin.go
+++ b/java/config/kotlin.go
@@ -15,7 +15,11 @@
 package config
 
 var (
-	KotlinStdlibJar = "external/kotlinc/lib/kotlin-stdlib.jar"
+	KotlinStdlibJar     = "external/kotlinc/lib/kotlin-stdlib.jar"
+	KotlincIllegalFlags = []string{
+		"-no-jdk",
+		"-no-stdlib",
+	}
 )
 
 func init() {
diff --git a/java/java.go b/java/java.go
index 50c284a..5ed99f7 100644
--- a/java/java.go
+++ b/java/java.go
@@ -89,6 +89,9 @@
 	// list of module-specific flags that will be used for javac compiles
 	Javacflags []string `android:"arch_variant"`
 
+	// list of module-specific flags that will be used for kotlinc compiles
+	Kotlincflags []string `android:"arch_variant"`
+
 	// list of of java libraries that will be in the classpath
 	Libs []string `android:"arch_variant"`
 
@@ -330,6 +333,10 @@
 	return android.Paths{j.outputFile}
 }
 
+func (j *Module) DexJarFile() android.Path {
+	return j.dexJarFile
+}
+
 var _ android.SourceFileProducer = (*Module)(nil)
 
 type Dependency interface {
@@ -1083,13 +1090,21 @@
 	var kotlinJars android.Paths
 
 	if srcFiles.HasExt(".kt") {
+		// user defined kotlin flags.
+		kotlincFlags := j.properties.Kotlincflags
+		CheckKotlincFlags(ctx, kotlincFlags)
+
 		// If there are kotlin files, compile them first but pass all the kotlin and java files
 		// kotlinc will use the java files to resolve types referenced by the kotlin files, but
 		// won't emit any classes for them.
-
-		flags.kotlincFlags = "-no-stdlib"
+		kotlincFlags = append(kotlincFlags, "-no-stdlib")
 		if ctx.Device() {
-			flags.kotlincFlags += " -no-jdk"
+			kotlincFlags = append(kotlincFlags, "-no-jdk")
+		}
+		if len(kotlincFlags) > 0 {
+			// optimization.
+			ctx.Variable(pctx, "kotlincFlags", strings.Join(kotlincFlags, " "))
+			flags.kotlincFlags += "$kotlincFlags"
 		}
 
 		var kotlinSrcFiles android.Paths
@@ -1328,6 +1343,31 @@
 	j.outputFile = outputFile.WithoutRel()
 }
 
+// Check for invalid kotlinc flags. Only use this for flags explicitly passed by the user,
+// since some of these flags may be used internally.
+func CheckKotlincFlags(ctx android.ModuleContext, flags []string) {
+	for _, flag := range flags {
+		flag = strings.TrimSpace(flag)
+
+		if !strings.HasPrefix(flag, "-") {
+			ctx.PropertyErrorf("kotlincflags", "Flag `%s` must start with `-`", flag)
+		} else if strings.HasPrefix(flag, "-Xintellij-plugin-root") {
+			ctx.PropertyErrorf("kotlincflags",
+				"Bad flag: `%s`, only use internal compiler for consistency.", flag)
+		} else if inList(flag, config.KotlincIllegalFlags) {
+			ctx.PropertyErrorf("kotlincflags", "Flag `%s` already used by build system", flag)
+		} else if flag == "-include-runtime" {
+			ctx.PropertyErrorf("kotlincflags", "Bad flag: `%s`, do not include runtime.", flag)
+		} else {
+			args := strings.Split(flag, " ")
+			if args[0] == "-kotlin-home" {
+				ctx.PropertyErrorf("kotlincflags",
+					"Bad flag: `%s`, kotlin home already set to default (path to kotlinc in the repo).", flag)
+			}
+		}
+	}
+}
+
 func (j *Module) compileJavaHeader(ctx android.ModuleContext, srcFiles, srcJars android.Paths,
 	deps deps, flags javaBuilderFlags, jarName string, extraJars android.Paths) android.Path {
 
diff --git a/java/java_test.go b/java/java_test.go
index 86349fe..4d4b836 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -1124,3 +1124,64 @@
 		}
 	}
 }
+
+var compilerFlagsTestCases = []struct {
+	in  string
+	out bool
+}{
+	{
+		in:  "a",
+		out: false,
+	},
+	{
+		in:  "-a",
+		out: true,
+	},
+	{
+		in:  "-no-jdk",
+		out: false,
+	},
+	{
+		in:  "-no-stdlib",
+		out: false,
+	},
+	{
+		in:  "-kotlin-home",
+		out: false,
+	},
+	{
+		in:  "-kotlin-home /some/path",
+		out: false,
+	},
+	{
+		in:  "-include-runtime",
+		out: false,
+	},
+	{
+		in:  "-Xintellij-plugin-root",
+		out: false,
+	},
+}
+
+type mockContext struct {
+	android.ModuleContext
+	result bool
+}
+
+func (ctx *mockContext) PropertyErrorf(property, format string, args ...interface{}) {
+	// CheckBadCompilerFlags calls this function when the flag should be rejected
+	ctx.result = false
+}
+
+func TestCompilerFlags(t *testing.T) {
+	for _, testCase := range compilerFlagsTestCases {
+		ctx := &mockContext{result: true}
+		CheckKotlincFlags(ctx, []string{testCase.in})
+		if ctx.result != testCase.out {
+			t.Errorf("incorrect output:")
+			t.Errorf("     input: %#v", testCase.in)
+			t.Errorf("  expected: %#v", testCase.out)
+			t.Errorf("       got: %#v", ctx.result)
+		}
+	}
+}
diff --git a/ui/build/paths/config.go b/ui/build/paths/config.go
index f3406e5..4034533 100644
--- a/ui/build/paths/config.go
+++ b/ui/build/paths/config.go
@@ -79,7 +79,6 @@
 	"bash":      Allowed,
 	"bc":        Allowed,
 	"bzip2":     Allowed,
-	"cat":       Allowed,
 	"chmod":     Allowed,
 	"cmp":       Allowed,
 	"comm":      Allowed,
@@ -148,7 +147,6 @@
 	"todos":     Allowed,
 	"touch":     Allowed,
 	"tr":        Allowed,
-	"uniq":      Allowed,
 	"unix2dos":  Allowed,
 	"unzip":     Allowed,
 	"wc":        Allowed,
@@ -174,9 +172,11 @@
 	"pkg-config": Forbidden,
 
 	// On linux we'll use the toybox version of these instead
+	"cat":    Toybox,
 	"id":     Toybox,
 	"true":   Toybox,
 	"uname":  Toybox,
+	"uniq":   Toybox,
 	"whoami": Toybox,
 }