diff --git a/dex2oat.te b/dex2oat.te
index 42abb55d795d2d2ce79259857db0b896f3951e3a..4252b88faa5ae94d718b1ead961292dd7d8b13dd 100644
--- a/dex2oat.te
+++ b/dex2oat.te
@@ -3,7 +3,8 @@ type dex2oat, domain, mlstrustedsubject, domain_deprecated;
 type dex2oat_exec, exec_type, file_type;
 
 allow dex2oat dalvikcache_data_file:file write;
-# Read symlinks in /data/dalvik-cache
+# Read symlinks in /data/dalvik-cache. This is required for PIC mode boot images, where
+# the oat file is symlinked to the original file in /system.
 allow dex2oat dalvikcache_data_file:lnk_file read;
 allow dex2oat installd:fd use;
 
@@ -16,4 +17,27 @@ allow dex2oat oemfs:file read;
 allow dex2oat apk_tmp_file:file read;
 allow dex2oat app_data_file:file {read write lock};
 
+##################
+# A/B OTA Dexopt #
+##################
+
+# Allow dex2oat to use file descriptors from otapreopt.
+allow dex2oat otapreopt:fd use;
+# Allow dex2oat access to files in /data/ota.
+allow dex2oat ota_data_file:dir ra_dir_perms;
+allow dex2oat ota_data_file:file r_file_perms;
+
+# Read symlinks in /data/ota/dalvik-cache. This is required for PIC mode boot images, where
+# the oat file is symlinked to the original file in /system.
+allow dex2oat ota_data_file:lnk_file read;
+
+# It would be nice to tie this down, but currently, because of how images are written, we can't
+# pass file descriptors for the preopted boot image to dex2oat. So dex2oat needs to be able to
+# create them itself (and make them world-readable).
+allow dex2oat ota_data_file:file { create w_file_perms setattr };
+
+##############
+# Neverallow #
+##############
+
 neverallow dex2oat app_data_file:notdevfile_class_set open;
diff --git a/domain.te b/domain.te
index 70814282331468bf8505903a9679b3c6ab433071..7ad4e464931b72968da92f988bcad169d53ccc87 100644
--- a/domain.te
+++ b/domain.te
@@ -341,6 +341,7 @@ neverallow {
   -init # TODO: limit init to relabelfrom for files
   -zygote
   -installd
+  -otapreopt
   -dex2oat
 } dalvikcache_data_file:file no_w_file_perms;
 
@@ -348,6 +349,7 @@ neverallow {
   domain
   -init
   -installd
+  -otapreopt
   -dex2oat
   -zygote
 } dalvikcache_data_file:dir no_w_dir_perms;
diff --git a/file.te b/file.te
index 25c4c06605332f97ac3f7fed82c0b4fdaaf7af44..88d997c9feb46dac8e0c75e6ed3f3025d439c46b 100644
--- a/file.te
+++ b/file.te
@@ -82,6 +82,8 @@ type apk_private_data_file, file_type, data_file_type;
 type apk_private_tmp_file, file_type, data_file_type, mlstrustedobject;
 # /data/dalvik-cache
 type dalvikcache_data_file, file_type, data_file_type;
+# /data/ota
+type ota_data_file, file_type, data_file_type;
 # /data/resource-cache
 type resourcecache_data_file, file_type, data_file_type;
 # /data/local - writable by shell
diff --git a/file_contexts b/file_contexts
index f2927801a9a98d3d1d4aed4325bb9b638a348cfc..1195ebdd86e14d36c5559cf25c2db273b7571920 100644
--- a/file_contexts
+++ b/file_contexts
@@ -170,6 +170,7 @@
 /system/bin/mediaextractor	u:object_r:mediaextractor_exec:s0
 /system/bin/mdnsd	u:object_r:mdnsd_exec:s0
 /system/bin/installd	u:object_r:installd_exec:s0
+/system/bin/otapreopt   u:object_r:otapreopt_exec:s0
 /system/bin/keystore	u:object_r:keystore_exec:s0
 /system/bin/fingerprintd u:object_r:fingerprintd_exec:s0
 /system/bin/gatekeeperd u:object_r:gatekeeperd_exec:s0
@@ -237,6 +238,7 @@
 /data/gps(/.*)?		u:object_r:gps_data_file:s0
 /data/resource-cache(/.*)? u:object_r:resourcecache_data_file:s0
 /data/dalvik-cache(/.*)? u:object_r:dalvikcache_data_file:s0
+/data/ota(/.*)? u:object_r:ota_data_file:s0
 /data/adb(/.*)?		u:object_r:adb_data_file:s0
 /data/anr(/.*)?		u:object_r:anr_data_file:s0
 /data/app(/.*)?                       u:object_r:apk_data_file:s0
diff --git a/installd.te b/installd.te
index 379e0745c0f71cd87ff853cd1f872f36be10e8e4..f685a4883fa8849976c77f486adbce392380fcb5 100644
--- a/installd.te
+++ b/installd.te
@@ -69,6 +69,9 @@ domain_auto_trans(installd, dex2oat_exec, dex2oat)
 # Run idmap in its own sandbox.
 domain_auto_trans(installd, idmap_exec, idmap)
 
+# Run otapreopt in its own sandbox.
+domain_auto_trans(installd, otapreopt_exec, otapreopt)
+
 # Upgrade from unlabeled userdata.
 # Just need enough to remove and/or relabel it.
 allow installd unlabeled:dir { getattr search relabelfrom rw_dir_perms rmdir };
diff --git a/otapreopt.te b/otapreopt.te
new file mode 100644
index 0000000000000000000000000000000000000000..bb90eafcd1460ec6ba9d8522599f3ad41b56413d
--- /dev/null
+++ b/otapreopt.te
@@ -0,0 +1,31 @@
+# otapreopt executable
+type otapreopt, domain, mlstrustedsubject;
+type otapreopt_exec, exec_type, file_type;
+
+init_daemon_domain(otapreopt)
+allow otapreopt self:capability { chown dac_override fowner fsetid setgid setuid };
+
+# Note: /data/ota is created by init (see system/core/rootdir/init.rc) to avoid giving access
+# here and having to relabel the directory.
+
+# Write to /data/ota(/*). Create symlinks in /data/ota(/*)
+allow otapreopt ota_data_file:dir create_dir_perms;
+allow otapreopt ota_data_file:file create_file_perms;
+allow otapreopt ota_data_file:lnk_file create_file_perms;
+
+# Allow labeling of files under /data/app/com.example/oat/
+# TODO: Restrict to .b suffix?
+allow otapreopt dalvikcache_data_file:dir relabelto;
+allow otapreopt dalvikcache_data_file:file { relabelto link };
+
+allow otapreopt selinuxfs:dir r_dir_perms;
+
+# Check validity of SELinux context before use.
+selinux_check_context(otapreopt)
+selinux_check_access(otapreopt)
+
+# Run dex2oat in its own sandbox.
+domain_auto_trans(otapreopt, dex2oat_exec, dex2oat)
+
+# Allow otapreopt to use file descriptors from installd.
+allow otapreopt installd:fd use;
diff --git a/service.te b/service.te
index 7e004b4207e418e833339df0a311a81f700d3c53..45f1c877c4fc696e91bead3467982e797e7c3a4e 100644
--- a/service.te
+++ b/service.te
@@ -71,6 +71,7 @@ type netstats_service, app_api_service, system_server_service, service_manager_t
 type network_management_service, app_api_service, system_server_service, service_manager_type;
 type network_score_service, system_api_service, system_server_service, service_manager_type;
 type notification_service, app_api_service, system_server_service, service_manager_type;
+type otadexopt_service, system_server_service, service_manager_type;
 type package_service, app_api_service, system_server_service, service_manager_type;
 type permission_service, app_api_service, system_server_service, service_manager_type;
 type persistent_data_block_service, system_api_service, system_server_service, service_manager_type;
diff --git a/service_contexts b/service_contexts
index 1f3e572ecba019b0f456445087570570b5f63407..747369ef7f954ccf5bb9d121baf312d71bcd4103 100644
--- a/service_contexts
+++ b/service_contexts
@@ -84,6 +84,7 @@ network_management                        u:object_r:network_management_service:
 network_score                             u:object_r:network_score_service:s0
 nfc                                       u:object_r:nfc_service:s0
 notification                              u:object_r:notification_service:s0
+otadexopt                                 u:object_r:otadexopt_service:s0
 package                                   u:object_r:package_service:s0
 permission                                u:object_r:permission_service:s0
 persistent_data_block                     u:object_r:persistent_data_block_service:s0