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