diff --git a/private/heapprofd.te b/private/heapprofd.te
index ada66d524ca11ba2d843aa53da147c086d8bf7a9..79249b360a0cc07b466530bc932f38c54e980866 100644
--- a/private/heapprofd.te
+++ b/private/heapprofd.te
@@ -1,5 +1,40 @@
 # Android Heap Profiler Daemon go/heapprofd
-type heapprofd, domain, coredomain;
 type heapprofd_exec, exec_type, file_type, system_file_type;
 
 init_daemon_domain(heapprofd)
+
+userdebug_or_eng(`
+  # Allow to send signal to processes.
+  # This excludes SIGKILL, SIGSTOP and SIGCHLD,
+  # which are controlled by separate permissions.
+  allow heapprofd self:capability kill;
+
+  # Executables and libraries.
+  # These are needed to read the ELF binary data needed for unwinding.
+  allow heapprofd system_file:file r_file_perms;
+  allow heapprofd apk_data_file:file r_file_perms;
+  allow heapprofd dalvikcache_data_file:file r_file_perms;
+  allow heapprofd vendor_file_type:file r_file_perms;
+')
+
+# Write trace data to the Perfetto traced damon. This requires connecting to its
+# producer socket and obtaining a (per-process) tmpfs fd.
+allow heapprofd traced:fd use;
+allow heapprofd traced_tmpfs:file { read write getattr map };
+unix_socket_connect(heapprofd, traced_producer, traced)
+
+never_profile_heap(`{
+  bpfloader
+  init
+  kernel
+  keystore
+  llkd
+  logd
+  ueventd
+  vendor_init
+  vold
+}')
+
+full_treble_only(`
+  neverallow heapprofd vendor_file:file { no_w_file_perms no_x_file_perms };
+')
diff --git a/private/system_server.te b/private/system_server.te
index 7c9e2f2c68fb762e8af984de2933a5333eacdb58..01f02119a52aa2f4a595e5b423017f79958ef166 100644
--- a/private/system_server.te
+++ b/private/system_server.te
@@ -5,6 +5,7 @@
 
 typeattribute system_server coredomain;
 typeattribute system_server mlstrustedsubject;
+can_profile_heap(system_server)
 
 # Define a type for tmpfs-backed ashmem regions.
 tmpfs_domain(system_server)
diff --git a/public/domain.te b/public/domain.te
index b17893bdd1396bde0dd5a1f856db1d6b88b0d459..0a47bc6d6eb48f88c7e8bb8364e88099463ed01a 100644
--- a/public/domain.te
+++ b/public/domain.te
@@ -995,6 +995,7 @@ full_treble_only(`
         -init
         -installd
         userdebug_or_eng(`-perfprofd')
+        userdebug_or_eng(`-heapprofd')
         -postinstall_dexopt
         -system_server
     } vendor_app_file:dir { open read getattr search };
@@ -1009,6 +1010,7 @@ full_treble_only(`
         -init
         -installd
         userdebug_or_eng(`-perfprofd')
+        userdebug_or_eng(`-heapprofd')
         -postinstall_dexopt
         -system_server
         -mediaserver
@@ -1026,6 +1028,7 @@ full_treble_only(`
         -system_server
         -webview_zygote
         -zygote
+        userdebug_or_eng(`-heapprofd')
     } vendor_overlay_file:dir { getattr open read search };
 ')
 
@@ -1039,6 +1042,7 @@ full_treble_only(`
         -system_server
         -webview_zygote
         -zygote
+        userdebug_or_eng(`-heapprofd')
     } vendor_overlay_file:file r_file_perms;
 ')
 
@@ -1109,6 +1113,7 @@ full_treble_only(`
     -init # starts vendor executables
     -kernel # loads /vendor/firmware
     userdebug_or_eng(`-perfprofd')
+    userdebug_or_eng(`-heapprofd')
     -shell
     -system_executes_vendor_violators
     -ueventd # reads /vendor/ueventd.rc
@@ -1446,6 +1451,7 @@ full_treble_only(`
     -init
     -kernel
     -perfprofd
+    -heapprofd
     -ueventd
   } vendor_file:file { no_w_file_perms no_x_file_perms open };
 ')
diff --git a/public/heapprofd.te b/public/heapprofd.te
new file mode 100644
index 0000000000000000000000000000000000000000..7ceb23feb457ab6d24d06b346de45010d30adcfb
--- /dev/null
+++ b/public/heapprofd.te
@@ -0,0 +1 @@
+type heapprofd, domain, coredomain;
diff --git a/public/te_macros b/public/te_macros
index e756f36680e26aa8f6a77cbc947e6af43a7ea235..c70e7db38d1a3a27bcbc0776557fba935fce97b8 100644
--- a/public/te_macros
+++ b/public/te_macros
@@ -643,3 +643,35 @@ define(`hal_attribute_hwservice', `
     neverallow { domain -$1_client -$1_server } $2:hwservice_manager find;
   ')
 ')
+
+###################################
+# can_profile_heap(domain)
+# never_profile_heap(domain)
+#
+# Opt in our out of heap profiling.
+# This will allow a heap profiling daemon to read this
+# process' address space in order to support unwinding.
+#
+define(`can_profile_heap', `
+  userdebug_or_eng(`
+    # RT signal for client initialization.
+    allow heapprofd $1:process signal;
+    # Connect to heapprofd service.
+    unix_socket_connect($1, heapprofd, heapprofd)
+    # To receive file descriptor.
+    allow heapprofd $1:fd use;
+
+    # To read from the received file descriptors.
+    # /proc/[pid]/maps and /proc/[pid]/mem have the same SELinux label as the
+    # process they relate to.
+    allow heapprofd $1:file r_file_perms;
+    # This is needed to search the /proc/[pid] directory.
+    allow heapprofd $1:dir r_dir_perms;
+    allow heapprofd $1:process signal;
+  ')
+')
+
+define(`never_profile_heap', `
+  neverallow heapprofd $1:file read;
+  neverallow heapprofd $1:process signal;
+')