diff --git a/private/app_neverallows.te b/private/app_neverallows.te
index 46b49c274d426da392f0eaecb9e89be1241499af..ab080c29029f9de861dfe2969c47fc0f7f233188 100644
--- a/private/app_neverallows.te
+++ b/private/app_neverallows.te
@@ -40,6 +40,17 @@ neverallow { all_untrusted_apps -mediaprovider } property_type:property_service
 # but otherwise disallow untrusted apps from reading this property.
 neverallow { all_untrusted_apps -untrusted_app_25 } net_dns_prop:file read;
 
+# Block calling execve() on files in an apps home directory.
+# This is a W^X violation (loading executable code from a writable
+# home directory). For compatibility, allow for targetApi <= 28.
+# b/112357170
+neverallow {
+  all_untrusted_apps
+  -untrusted_app_25
+  -untrusted_app_27
+  -runas_app
+} { app_data_file privapp_data_file }:file execute_no_trans;
+
 # Do not allow untrusted apps to be assigned mlstrustedsubject.
 # This would undermine the per-user isolation model being
 # enforced via levelFrom=user in seapp_contexts and the mls
diff --git a/private/runas_app.te b/private/runas_app.te
new file mode 100644
index 0000000000000000000000000000000000000000..b976b91879ed85a34e98e032c07ce622e1f76537
--- /dev/null
+++ b/private/runas_app.te
@@ -0,0 +1,11 @@
+type runas_app, domain;
+typeattribute runas_app coredomain;
+
+app_domain(runas_app)
+untrusted_app_domain(runas_app)
+net_domain(runas_app)
+bluetooth_domain(runas_app)
+
+# The ability to call exec() on files in the apps home directories
+# when using run-as on a debuggable app. Needed by simpleperf.
+allow runas_app app_data_file:file execute_no_trans;
diff --git a/private/seapp_contexts b/private/seapp_contexts
index d0cf2a569d85430edfb7a66c7f59306465683d52..55391ea36ba96d2e9a695e76825aabf8cfbab374 100644
--- a/private/seapp_contexts
+++ b/private/seapp_contexts
@@ -9,6 +9,7 @@
 #       path (string)
 #       isPrivApp (boolean)
 #       minTargetSdkVersion (unsigned integer)
+#       fromRunAs (boolean)
 # isSystemServer=true can only be used once.
 # An unspecified isSystemServer defaults to false.
 # isEphemeralApp=true will match apps marked by PackageManager as Ephemeral
@@ -25,6 +26,7 @@
 # minTargetSdkVersion will match applications with a targetSdkVersion
 #       greater than or equal to the specified value. If unspecified,
 #       it has a default value of 0.
+# fromRunAs=true means the setcontext request is from run-as. Default is false.
 # All specified input selectors in an entry must match (i.e. logical AND).
 # Matching is case-insensitive.
 #
@@ -43,6 +45,7 @@
 #       (11) Specified isPrivApp= before unspecified isPrivApp= boolean.
 #       (12) Higher value of minTargetSdkVersion= before lower value of minTargetSdkVersion=
 #              integer. Note that minTargetSdkVersion= defaults to 0 if unspecified.
+#       (13) fromRunAs=true before fromRunAs=false.
 #
 # Outputs:
 #       domain (string)
@@ -114,6 +117,8 @@ user=_app seinfo=media domain=mediaprovider name=android.process.media type=app_
 user=_app seinfo=platform domain=platform_app type=app_data_file levelFrom=user
 user=_app isV2App=true isEphemeralApp=true domain=ephemeral_app type=app_data_file levelFrom=all
 user=_app isPrivApp=true domain=priv_app type=privapp_data_file levelFrom=user
-user=_app minTargetSdkVersion=28 domain=untrusted_app type=app_data_file levelFrom=all
+user=_app minTargetSdkVersion=29 domain=untrusted_app type=app_data_file levelFrom=all
+user=_app minTargetSdkVersion=28 domain=untrusted_app_27 type=app_data_file levelFrom=all
 user=_app minTargetSdkVersion=26 domain=untrusted_app_27 type=app_data_file levelFrom=user
 user=_app domain=untrusted_app_25 type=app_data_file levelFrom=user
+user=_app fromRunAs=true domain=runas_app levelFrom=all
diff --git a/private/untrusted_app_25.te b/private/untrusted_app_25.te
index 61c9a817583229acc752e246de7e24bc8ce2543a..d264aaf161a27f044b99d9059d9af57e244437f8 100644
--- a/private/untrusted_app_25.te
+++ b/private/untrusted_app_25.te
@@ -44,3 +44,8 @@ allow untrusted_app_25 proc_tty_drivers:file r_file_perms;
 # Text relocation support for API < 23
 # https://android.googlesource.com/platform/bionic/+/master/android-changes-for-ndk-developers.md#text-relocations-enforced-for-api-level-23
 allow untrusted_app_25 { apk_data_file app_data_file asec_public_file }:file execmod;
+
+# The ability to call exec() on files in the apps home directories
+# for targetApi<=25. This is also allowed for targetAPIs 26, 27,
+# and 28 in untrusted_app_27.te.
+allow untrusted_app_25 app_data_file:file execute_no_trans;
diff --git a/private/untrusted_app_27.te b/private/untrusted_app_27.te
index 79c776287f5f1766d4543d140cf242871a17bbf0..7b9060d639a637910167a82649613139106f9579 100644
--- a/private/untrusted_app_27.te
+++ b/private/untrusted_app_27.te
@@ -2,7 +2,7 @@
 ### Untrusted_27.
 ###
 ### This file defines the rules for untrusted apps running with
-### 25 < targetSdkVersion <= 27.
+### 25 < targetSdkVersion <= 28.
 ###
 ### This file defines the rules for untrusted apps.
 ### Apps are labeled based on mac_permissions.xml (maps signer and
@@ -26,3 +26,7 @@ app_domain(untrusted_app_27)
 untrusted_app_domain(untrusted_app_27)
 net_domain(untrusted_app_27)
 bluetooth_domain(untrusted_app_27)
+
+# The ability to call exec() on files in the apps home directories
+# for targetApi 26, 27, and 28.
+allow untrusted_app_27 app_data_file:file execute_no_trans;
diff --git a/private/untrusted_app_all.te b/private/untrusted_app_all.te
index 54d278e2c961662e3ef21ee3414ceae57fb7679f..527216def248891dd2bab6ac76a21dfe2255f324 100644
--- a/private/untrusted_app_all.te
+++ b/private/untrusted_app_all.te
@@ -22,7 +22,7 @@
 
 # Some apps ship with shared libraries and binaries that they write out
 # to their sandbox directory and then execute.
-allow untrusted_app_all { app_data_file privapp_data_file }:file { rx_file_perms };
+allow untrusted_app_all { app_data_file privapp_data_file }:file { r_file_perms execute };
 
 # ASEC
 allow untrusted_app_all asec_apk_file:file r_file_perms;
diff --git a/tools/check_seapp.c b/tools/check_seapp.c
index c23c1f6c6a58ad3a82fe296a7d25b7320a73102d..1022cbd485ce2353d024d8de9731d1afa372091d 100644
--- a/tools/check_seapp.c
+++ b/tools/check_seapp.c
@@ -212,6 +212,7 @@ key_map rules[] = {
                 { .name = "path",           .dir = dir_in,                              },
                 { .name = "isPrivApp",      .dir = dir_in, .fn_validate = validate_bool },
                 { .name = "minTargetSdkVersion", .dir = dir_in, .fn_validate = validate_uint },
+                { .name = "fromRunAs",       .dir = dir_in, .fn_validate = validate_bool },
                 /*Outputs*/
                 { .name = "domain",         .dir = dir_out, .fn_validate = validate_selinux_type  },
                 { .name = "type",           .dir = dir_out, .fn_validate = validate_selinux_type  },