diff --git a/private/app.te b/private/app.te
index d6d407af973c59943de8b3699e43f79cfee6b13b..fbf89e86fa1098415bd971096dc92887e6362a3f 100644
--- a/private/app.te
+++ b/private/app.te
@@ -297,11 +297,14 @@ allow appdomain proc_meminfo:file r_file_perms;
 # For app fuse.
 allow appdomain app_fuse_file:file { getattr read append write };
 
-use_pdx({ appdomain -isolated_app -ephemeral_app }, surfaceflinger)
-use_pdx({ appdomain -isolated_app -ephemeral_app }, sensord)
-use_pdx({ appdomain -isolated_app -ephemeral_app }, performanced)
-# TODO: apps do not directly open the IPC socket for bufferhubd.
-use_pdx({ appdomain -isolated_app -ephemeral_app }, bufferhubd)
+pdx_client({ appdomain -isolated_app -ephemeral_app }, display_client)
+pdx_client({ appdomain -isolated_app -ephemeral_app }, display_manager)
+pdx_client({ appdomain -isolated_app -ephemeral_app }, display_vsync)
+pdx_client({ appdomain -isolated_app -ephemeral_app }, sensors_client)
+pdx_client({ appdomain -isolated_app -ephemeral_app }, pose_client)
+pdx_client({ appdomain -isolated_app -ephemeral_app }, performance_client)
+# Apps do not directly open the IPC socket for bufferhubd.
+pdx_use({ appdomain -isolated_app -ephemeral_app }, bufferhub_client)
 
 ###
 ### CTS-specific rules
diff --git a/private/file_contexts b/private/file_contexts
index 3b5319a8cfaa396c051c52d1c3a3691acee219c5..4d64c6172a34de98f9ddf323092826334b22ca3e 100644
--- a/private/file_contexts
+++ b/private/file_contexts
@@ -130,7 +130,19 @@
 /dev/socket/mdnsd	u:object_r:mdnsd_socket:s0
 /dev/socket/mtpd	u:object_r:mtpd_socket:s0
 /dev/socket/netd	u:object_r:netd_socket:s0
-/dev/socket/pdx(/.*)?	u:object_r:pdx_socket:s0
+/dev/socket/pdx/system/buffer_hub	u:object_r:pdx_bufferhub_dir:s0
+/dev/socket/pdx/system/buffer_hub/client	u:object_r:pdx_bufferhub_client_endpoint_socket:s0
+/dev/socket/pdx/system/performance	u:object_r:pdx_performance_dir:s0
+/dev/socket/pdx/system/performance/client	u:object_r:pdx_performance_client_endpoint_socket:s0
+/dev/socket/pdx/system/vr/sensors	u:object_r:pdx_sensors_dir:s0
+/dev/socket/pdx/system/vr/sensors/client	u:object_r:pdx_sensors_client_endpoint_socket:s0
+/dev/socket/pdx/system/vr/pose	u:object_r:pdx_pose_dir:s0
+/dev/socket/pdx/system/vr/pose/client	u:object_r:pdx_pose_client_endpoint_socket:s0
+/dev/socket/pdx/system/vr/display	u:object_r:pdx_display_dir:s0
+/dev/socket/pdx/system/vr/display/client	u:object_r:pdx_display_client_endpoint_socket:s0
+/dev/socket/pdx/system/vr/display/manager	u:object_r:pdx_display_manager_endpoint_socket:s0
+/dev/socket/pdx/system/vr/display/screenshot	u:object_r:pdx_display_screenshot_endpoint_socket:s0
+/dev/socket/pdx/system/vr/display/vsync	u:object_r:pdx_display_vsync_endpoint_socket:s0
 /dev/socket/property_service	u:object_r:property_socket:s0
 /dev/socket/racoon	u:object_r:racoon_socket:s0
 /dev/socket/rild	u:object_r:rild_socket:s0
diff --git a/private/surfaceflinger.te b/private/surfaceflinger.te
index 8e5892be84d9af06b42bcbd79991d436c73f57d6..1e425ba33b60c25deaf66559ea72480dbb298c8a 100644
--- a/private/surfaceflinger.te
+++ b/private/surfaceflinger.te
@@ -91,11 +91,15 @@ allow surfaceflinger system_server:fd use;
 allow surfaceflinger ion_device:chr_file r_file_perms;
 
 # pdx IPC
-pdx_server(surfaceflinger)
-
-use_pdx(surfaceflinger, bufferhubd)
-use_pdx(surfaceflinger, performanced)
-use_pdx(surfaceflinger, sensord)
+pdx_server(surfaceflinger, display_client)
+pdx_server(surfaceflinger, display_manager)
+pdx_server(surfaceflinger, display_screenshot)
+pdx_server(surfaceflinger, display_vsync)
+
+pdx_client(surfaceflinger, bufferhub_client)
+pdx_client(surfaceflinger, performance_client)
+pdx_client(surfaceflinger, sensors_client)
+pdx_client(surfaceflinger, pose_client)
 
 ###
 ### Neverallow rules
diff --git a/public/attributes b/public/attributes
index f2ae06f98055b638d0eeba79b5315e2245b27175..f8650b7a43b4bafd2a20b7cfdf04037914275a95 100644
--- a/public/attributes
+++ b/public/attributes
@@ -154,6 +154,20 @@ attribute socket_between_core_and_vendor_violators;
 # TODO(b/36463595)
 attribute vendor_executes_system_violators;
 
+# PDX services
+attribute pdx_endpoint_dir_type;
+attribute pdx_endpoint_socket_type;
+attribute pdx_channel_socket_type;
+
+pdx_service_attributes(display_client)
+pdx_service_attributes(display_manager)
+pdx_service_attributes(display_screenshot)
+pdx_service_attributes(display_vsync)
+pdx_service_attributes(performance_client)
+pdx_service_attributes(sensors_client)
+pdx_service_attributes(pose_client);
+pdx_service_attributes(bufferhub_client)
+
 # All HAL servers
 attribute halserverdomain;
 # All HAL clients
diff --git a/public/bufferhubd.te b/public/bufferhubd.te
index 7d5be49fe8f8639707d73cc0969375b8af0eee1b..274c2716b3a69141863a3eed0a11772f37f88ddf 100644
--- a/public/bufferhubd.te
+++ b/public/bufferhubd.te
@@ -4,8 +4,8 @@ type bufferhubd_exec, exec_type, file_type;
 
 hal_client_domain(bufferhubd, hal_graphics_allocator)
 
-pdx_server(bufferhubd)
-use_pdx(bufferhubd, performanced)
+pdx_server(bufferhubd, bufferhub_client)
+pdx_client(bufferhubd, performance_client)
 
 # Access the GPU.
 allow bufferhubd gpu_device:chr_file rw_file_perms;
@@ -16,5 +16,5 @@ allow bufferhubd ion_device:chr_file r_file_perms;
 # Receive sync fence FDs from mediacodec. Note that mediacodec never directly
 # connects to bufferhubd via PDX. Instead, a VR app acts as a bridge between
 # those two: it talks to mediacodec via Binder and talks to bufferhubd via PDX.
-# Thus, there is no need to use use_pdx macro.
+# Thus, there is no need to use pdx_client macro.
 allow bufferhubd mediacodec:fd use;
diff --git a/public/domain.te b/public/domain.te
index c48950db1b64ca8d37ba179076ca6ac3e31fb033..8f63624e811984bec2ae9dcfece5c4036584d1df 100644
--- a/public/domain.te
+++ b/public/domain.te
@@ -640,7 +640,8 @@ full_treble_only(`
     unlabeled # used only by core domains
     core_data_file_type
     -app_data_file
-    -pdx_socket # used by VR layer
+    -pdx_endpoint_socket_type # used by VR layer
+    -pdx_channel_socket_type # used by VR layer
   }:sock_file ~{ append getattr ioctl read write };
 
   # Core domains are not permitted to create/open sockets owned by vendor domains
diff --git a/public/file.te b/public/file.te
index 7cb7c3678d830b2a38444536a1a2c1798d4e3694..b5b6f86cbc64d3ca5509f8d944c66bfbad5ad806 100644
--- a/public/file.te
+++ b/public/file.te
@@ -264,7 +264,6 @@ type mdnsd_socket, file_type, coredomain_socket, mlstrustedobject;
 type misc_logd_file, coredomain_socket, file_type;
 type mtpd_socket, file_type, coredomain_socket;
 type netd_socket, file_type, coredomain_socket;
-type pdx_socket, file_type, coredomain_socket, mlstrustedobject;
 type property_socket, file_type, coredomain_socket, mlstrustedobject;
 type racoon_socket, file_type, coredomain_socket;
 type rild_socket, file_type;
@@ -281,6 +280,22 @@ type zygote_socket, file_type, coredomain_socket;
 # UART (for GPS) control proc file
 type gps_control, file_type;
 
+# PDX endpoint types
+type pdx_display_dir, pdx_endpoint_dir_type, file_type;
+type pdx_performance_dir, pdx_endpoint_dir_type, file_type;
+type pdx_sensors_dir, pdx_endpoint_dir_type, file_type;
+type pdx_pose_dir, pdx_endpoint_dir_type, file_type;
+type pdx_bufferhub_dir, pdx_endpoint_dir_type, file_type;
+
+pdx_service_socket_types(display_client, pdx_display_dir)
+pdx_service_socket_types(display_manager, pdx_display_dir)
+pdx_service_socket_types(display_screenshot, pdx_display_dir)
+pdx_service_socket_types(display_vsync, pdx_display_dir)
+pdx_service_socket_types(performance_client, pdx_performance_dir)
+pdx_service_socket_types(sensors_client, pdx_sensors_dir)
+pdx_service_socket_types(pose_client, pdx_pose_dir)
+pdx_service_socket_types(bufferhub_client, pdx_bufferhub_dir)
+
 # file_contexts files
 type file_contexts_file, file_type;
 
diff --git a/public/mediacodec.te b/public/mediacodec.te
index 771701c6b99e7e545390358bdb63a0aa9c45f3e5..5ca41fcf11719b2812a70d775ce1b7b1cec3547d 100644
--- a/public/mediacodec.te
+++ b/public/mediacodec.te
@@ -43,7 +43,7 @@ hal_client_domain(mediacodec, hal_graphics_allocator)
 # Recieve gralloc buffer FDs from bufferhubd. Note that mediacodec never
 # directly connects to bufferhubd via PDX. Instead, a VR app acts as a bridge
 # between those two: it talks to mediacodec via Binder and talks to bufferhubd
-# via PDX. Thus, there is no need to use use_pdx macro.
+# via PDX. Thus, there is no need to use pdx_client macro.
 allow mediacodec bufferhubd:fd use;
 
 ###
diff --git a/public/performanced.te b/public/performanced.te
index 8f9d16b05757ce313f31f1208117d6b34d42f65d..7f2e13ff7848a9b4ef71d9e57021db0bff919f49 100644
--- a/public/performanced.te
+++ b/public/performanced.te
@@ -2,7 +2,7 @@
 type performanced, domain, mlstrustedsubject;
 type performanced_exec, exec_type, file_type;
 
-pdx_server(performanced)
+pdx_server(performanced, performance_client)
 
 # TODO: use file caps to obtain sys_nice instead of setuid / setgid.
 allow performanced self:capability { setuid setgid sys_nice };
diff --git a/public/sensord.te b/public/sensord.te
index 3211f8103b3cecea9fd813724b3220fd6067cc53..c9749cb1045ef6a2dc08edaab30e675f8c825c63 100644
--- a/public/sensord.te
+++ b/public/sensord.te
@@ -5,9 +5,10 @@ type sensord_exec, exec_type, file_type;
 hal_client_domain(sensord, hal_graphics_allocator)
 allow sensord hal_graphics_allocator:fd use;
 
-pdx_server(sensord)
-use_pdx(sensord, bufferhubd)
-use_pdx(sensord, performanced)
+pdx_server(sensord, sensors_client)
+pdx_server(sensord, pose_client)
+pdx_client(sensord, bufferhub_client)
+pdx_client(sensord, performance_client)
 
 # Access /dev/ion
 allow sensord ion_device:chr_file r_file_perms;
diff --git a/public/te_macros b/public/te_macros
index e1c46c957a7e8d78b02454bdbd350b714dc331e5..80b56964563781ef38fbf3fba92853a1dd785d8b 100644
--- a/public/te_macros
+++ b/public/te_macros
@@ -85,26 +85,73 @@ allow $1 tmpfs:dir { getattr search };
 # rules from underlying transport (e.g. UDS-based implementation).
 
 #####################################
-# pdx_server(domain)
-define(`pdx_server', `
-allow $1 pdx_socket:dir create_dir_perms;
-allow $1 pdx_socket:sock_file create_file_perms;
+# pdx_service_attributes(service)
+# Defines type attribute used to identify various service-related types.
+define(`pdx_service_attributes', `
+attribute pdx_$1_endpoint_dir_type;
+attribute pdx_$1_endpoint_socket_type;
+attribute pdx_$1_channel_socket_type;
+attribute pdx_$1_server_type;
 ')
 
 #####################################
-# use_pdx(clientdomain, serverdomain)
-define(`use_pdx', `
-# Open the socket.
-allow $1 pdx_socket:dir r_dir_perms;
-allow $1 pdx_socket:sock_file rw_file_perms;
-# Use the socket.
-allow $1 $2:unix_stream_socket { connectto read write shutdown };
-# Clients recieve an event fd from the server.
-allow $1 $2:fd use;
+# pdx_service_socket_types(service, endpoint_dir_t)
+# Define types for endpoint and channel sockets.
+define(`pdx_service_socket_types', `
+typeattribute $2 pdx_$1_endpoint_dir_type;
+type pdx_$1_endpoint_socket, pdx_$1_endpoint_socket_type, pdx_endpoint_socket_type, file_type, coredomain_socket, mlstrustedobject, mlstrustedsubject;
+type pdx_$1_channel_socket, pdx_$1_channel_socket_type, pdx_channel_socket_type, coredomain_socket;
+')
+
+#####################################
+# pdx_server(server_domain, service)
+define(`pdx_server', `
+# Mark the server domain as a PDX server.
+typeattribute $1 pdx_$2_server_type;
+# Allow the init process to create the initial endpoint socket.
+allow init pdx_$2_endpoint_socket_type:unix_stream_socket { create bind };
+# Allow the server domain to use the endpoint socket and accept connections on it.
+# Not using macro like "rw_socket_perms_no_ioctl" because it provides more rights
+# than we need (e.g. we don"t need "bind" or "connect").
+allow $1 pdx_$2_endpoint_socket_type:unix_stream_socket { read getattr write setattr lock append getopt setopt shutdown listen accept };
+# Allow the server domain to apply security context label to the channel socket pair (allow process to use setsockcreatecon_raw()).
+allow $1 self:process setsockcreate;
+# Allow the server domain to create a client channel socket.
+allow $1 pdx_$2_channel_socket_type:unix_stream_socket create_stream_socket_perms;
+# Prevent other processes from claiming to be a server for the same service.
+neverallow {domain -$1} pdx_$2_endpoint_socket_type:unix_stream_socket { listen accept };
+')
+
+#####################################
+# pdx_connect(client, service)
+define(`pdx_connect', `
+# Allow client to open the service endpoint file.
+allow $1 pdx_$2_endpoint_dir_type:dir r_dir_perms;
+allow $1 pdx_$2_endpoint_socket_type:sock_file rw_file_perms;
+# Allow the client to connect to endpoint socket.
+allow $1 pdx_$2_endpoint_socket_type:unix_stream_socket { connectto read write shutdown };
+')
+
+#####################################
+# pdx_use(client, service)
+define(`pdx_use', `
+# Allow the client to use the PDX channel socket.
+# Not using macro like "rw_socket_perms_no_ioctl" because it provides more rights
+# than we need (e.g. we don"t need "bind" or "connect").
+allow $1 pdx_$2_channel_socket_type:unix_stream_socket { read getattr write setattr lock append getopt setopt shutdown };
+# Client needs to use an channel event fd from the server.
+allow $1 pdx_$2_server_type:fd use;
 # Servers may receive sync fences, gralloc buffers, etc, from clients.
 # This could be tightened on a per-server basis, but keeping track of service
 # clients is error prone.
-allow $2 $1:fd use;
+allow pdx_$2_server_type $1:fd use;
+')
+
+#####################################
+# pdx_client(client, service)
+define(`pdx_client', `
+pdx_connect($1, $2)
+pdx_use($1, $2)
 ')
 
 #####################################
diff --git a/public/vr_hwc.te b/public/vr_hwc.te
index a33baea53e548db1a6d69472c3278b0d583bbc08..c05dd638ad10c26854a72ef393626a213f1be641 100644
--- a/public/vr_hwc.te
+++ b/public/vr_hwc.te
@@ -24,7 +24,7 @@ allow vr_hwc ion_device:chr_file r_file_perms;
 
 # Allow connection to VR DisplayClient to get the primary display metadata
 # (ie: size).
-use_pdx(vr_hwc, surfaceflinger)
+pdx_client(vr_hwc, display_client)
 
 # Requires access to the permission service to validate that clients have the
 # appropriate VR permissions.