diff --git a/Android.mk b/Android.mk
index 10b3ca3d924266eee9585078ebe3fd6b5ca83646..80f5ece2e30b8cf446852b1f2c70ccbac3d34f67 100644
--- a/Android.mk
+++ b/Android.mk
@@ -108,6 +108,33 @@ $(LOCAL_BUILT_MODULE) : $(sepolicy_policy.conf) $(HOST_OUT_EXECUTABLES)/checkpol
 built_sepolicy := $(LOCAL_BUILT_MODULE)
 sepolicy_policy.conf :=
 
+##################################
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := sepolicy.recovery
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_TAGS := eng
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+sepolicy_policy_recovery.conf := $(intermediates)/policy_recovery.conf
+$(sepolicy_policy_recovery.conf): PRIVATE_MLS_SENS := $(MLS_SENS)
+$(sepolicy_policy_recovery.conf): PRIVATE_MLS_CATS := $(MLS_CATS)
+$(sepolicy_policy_recovery.conf) : $(call build_policy, security_classes initial_sids access_vectors global_macros mls_macros mls policy_capabilities te_macros attributes *.te roles users initial_sid_contexts fs_use genfs_contexts port_contexts)
+	@mkdir -p $(dir $@)
+	$(hide) m4 -D mls_num_sens=$(PRIVATE_MLS_SENS) -D mls_num_cats=$(PRIVATE_MLS_CATS) \
+		-D target_build_variant=$(TARGET_BUILD_VARIANT) \
+		-D force_permissive_to_unconfined=$(FORCE_PERMISSIVE_TO_UNCONFINED) \
+		-D target_recovery=true \
+		-s $^ > $@
+
+$(LOCAL_BUILT_MODULE) : $(sepolicy_policy_recovery.conf) $(HOST_OUT_EXECUTABLES)/checkpolicy
+	@mkdir -p $(dir $@)
+	$(hide) $(HOST_OUT_EXECUTABLES)/checkpolicy -M -c $(POLICYVERS) -o $@ $<
+
+built_sepolicy_recovery := $(LOCAL_BUILT_MODULE)
+sepolicy_policy_recovery.conf :=
+
 ###################################
 include $(CLEAR_VARS)
 
diff --git a/recovery.te b/recovery.te
index 669c1da372a30d8714f50f395efd5d286924a2b3..41038c8114c82a23c2c5181ba9b6232f145db759 100644
--- a/recovery.te
+++ b/recovery.te
@@ -1,25 +1,36 @@
 # recovery console (used in recovery init.rc for /sbin/recovery)
+
+# Declare the domain unconditionally so we can always reference it
+# in neverallow rules.
 type recovery, domain;
-allow recovery rootfs:file entrypoint;
-unconfined_domain(recovery)
 
-allow recovery self:capability2 mac_admin;
+# But the allow rules are only included in the recovery policy.
+# Otherwise recovery is only allowed the domain rules.
+recovery_only(`
+  allow recovery rootfs:file entrypoint;
+  unconfined_domain(recovery)
+
+  # Set security contexts on files that are not known to the loaded policy.
+  allow recovery self:capability2 mac_admin;
 
-# Mount filesystems.
-allow recovery fs_type:filesystem *;
-allow recovery unlabeled:filesystem *;
+  # Mount filesystems.
+  allow recovery fs_type:filesystem *;
+  allow recovery unlabeled:filesystem *;
 
+  # Create and relabel files under /system.
+  allow recovery exec_type:{ file dir lnk_file } { create write setattr relabelfrom relabelto append unlink link rename };
+  allow recovery system_file:{ file dir lnk_file } { create write setattr relabelfrom relabelto append unlink link rename };
 
-# Create and relabel files under /system.
-allow recovery exec_type:{ file dir lnk_file } { create write setattr relabelfrom relabelto append unlink link rename };
-allow recovery system_file:{ file dir lnk_file } { create write setattr relabelfrom relabelto append unlink link rename };
+  # Required to e.g. wipe userdata/cache.
+  allow recovery dev_type:blk_file rw_file_perms;
 
-# Required to e.g. wipe userdata/cache.
-allow recovery dev_type:blk_file rw_file_perms;
+  # GUI
+  allow recovery self:process execmem;
+  allow recovery ashmem_device:chr_file execute;
 
-allow recovery self:process execmem;
-allow recovery ashmem_device:chr_file execute;
-allow recovery tmpfs:file rx_file_perms;
+  # Execute /tmp/update_binary.
+  allow recovery tmpfs:file rx_file_perms;
 
-# Use setfscreatecon() to label files for OTA updates.
-allow recovery self:process setfscreate;
+  # Use setfscreatecon() to label files for OTA updates.
+  allow recovery self:process setfscreate;
+')
diff --git a/te_macros b/te_macros
index fb6cdae1c00a866f59c4bf3906868828ca772a56..7a6d74ace9d1de7376d26318535b38597a601a76 100644
--- a/te_macros
+++ b/te_macros
@@ -308,6 +308,12 @@ allow $1 $1_devpts:chr_file { open getattr read write ioctl };
 #
 define(`non_system_app_set', `{ appdomain -system_app }')
 
+#####################################
+# Recovery only
+# SELinux rules which apply only to recovery mode
+#
+define(`recovery_only', ifelse(target_recovery, `true', $1, ))
+
 #####################################
 # Userdebug or eng builds
 # SELinux rules which apply only to userdebug or eng builds