Pack file_contexts into target_files zip.

file_contexts (specified by SELINUX_FC) is needed both when building
and (re)packaging. We used to use the copy in out/ when building, and
looked for the copy in BOOT/RAMDISK/ when packaging from target_files
zip. With system_root_image enabled, the file_contexts needed for
building and packaging might be different from the one on device. So
we explicitly pack the file as META/file_contexts in target_files zip.

Also refactor out the overriding of selinux_fc property into
common.LoadInfoDict().

Change-Id: I0781a147319148c76d989d8f29d5ef766a502dbd
(cherry picked from commit aa7318c3849095aeb3bea00efbf303c0c40a089d)
diff --git a/core/Makefile b/core/Makefile
index c9ad03a..8de935c 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -1509,6 +1509,7 @@
 	$(hide) $(ACP) $(APKCERTS_FILE) $(zip_root)/META/apkcerts.txt
 	$(hide) if test -e $(tool_extensions)/releasetools.py; then $(ACP) $(tool_extensions)/releasetools.py $(zip_root)/META/; fi
 	$(hide) echo "$(PRODUCT_OTA_PUBLIC_KEYS)" > $(zip_root)/META/otakeys.txt
+	$(hide) $(ACP) $(SELINUX_FC) $(zip_root)/META/file_contexts
 	$(hide) echo "recovery_api_version=$(PRIVATE_RECOVERY_API_VERSION)" > $(zip_root)/META/misc_info.txt
 	$(hide) echo "fstab_version=$(PRIVATE_RECOVERY_FSTAB_VERSION)" >> $(zip_root)/META/misc_info.txt
 ifdef BOARD_FLASH_BLOCK_SIZE
diff --git a/tools/releasetools/add_img_to_target_files.py b/tools/releasetools/add_img_to_target_files.py
index ec9f75b..5e81ad2 100755
--- a/tools/releasetools/add_img_to_target_files.py
+++ b/tools/releasetools/add_img_to_target_files.py
@@ -139,15 +139,9 @@
   if not os.path.exists(fs_config):
     fs_config = None
 
-  fc_config = os.path.join(input_dir, "BOOT/RAMDISK/file_contexts")
-  if not os.path.exists(fc_config):
-    fc_config = None
-
   # Override values loaded from info_dict.
   if fs_config:
     image_props["fs_config"] = fs_config
-  if fc_config:
-    image_props["selinux_fc"] = fc_config
   if block_list:
     image_props["block_list"] = block_list
   if image_props.get("system_root_image") == "true":
@@ -170,8 +164,7 @@
     print "userdata.img already exists in %s, no need to rebuild..." % (prefix,)
     return
 
-  image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict,
-                                                    "data")
+  image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict, "data")
   # We only allow yaffs to have a 0/missing partition_size.
   # Extfs, f2fs must have a size. Skip userdata.img if no size.
   if (not image_props.get("fs_type", "").startswith("yaffs") and
@@ -215,8 +208,7 @@
     print "cache.img already exists in %s, no need to rebuild..." % (prefix,)
     return
 
-  image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict,
-                                                    "cache")
+  image_props = build_image.ImagePropFromGlobalDict(OPTIONS.info_dict, "cache")
   # The build system has to explicitly request for cache.img.
   if "fs_type" not in image_props:
     return
@@ -265,10 +257,7 @@
   except KeyError:
     has_vendor = False
 
-  OPTIONS.info_dict = common.LoadInfoDict(input_zip)
-  if "selinux_fc" in OPTIONS.info_dict:
-    OPTIONS.info_dict["selinux_fc"] = os.path.join(
-        OPTIONS.input_tmp, "BOOT", "RAMDISK", "file_contexts")
+  OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.input_tmp)
 
   common.ZipClose(input_zip)
   output_zip = zipfile.ZipFile(filename, "a",
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index d5c15f5..844d376 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -102,7 +102,7 @@
       pass
 
 
-def LoadInfoDict(input_file):
+def LoadInfoDict(input_file, input_dir=None):
   """Read and parse the META/misc_info.txt key/value pairs from the
   input target files and return a dict."""
 
@@ -153,6 +153,23 @@
   if "fstab_version" not in d:
     d["fstab_version"] = "1"
 
+  # During building, we use the "file_contexts" in the out/ directory tree.
+  # It is no longer available when (re)generating from target_files zip. So
+  # when generating from target_files zip, we look for a copy under META/
+  # first, if not available search under BOOT/RAMDISK/. Note that we may need
+  # a different file_contexts to build images than the one running on device,
+  # such as when enabling system_root_image. In that case, we must have the
+  # one for building copied to META/.
+  if input_dir is not None:
+    fc_config = os.path.join(input_dir, "META", "file_contexts")
+    if not os.path.exists(fc_config):
+      fc_config = os.path.join(input_dir, "BOOT", "RAMDISK", "file_contexts")
+      if not os.path.exists(fc_config):
+        fc_config = None
+
+    if fc_config:
+      d["selinux_fc"] = fc_config
+
   try:
     data = read_helper("META/imagesizes.txt")
     for line in data.split("\n"):
diff --git a/tools/releasetools/img_from_target_files.py b/tools/releasetools/img_from_target_files.py
index 99c991a..aa21d7e 100755
--- a/tools/releasetools/img_from_target_files.py
+++ b/tools/releasetools/img_from_target_files.py
@@ -95,16 +95,7 @@
       # images, so build them.
       import add_img_to_target_files
 
-      OPTIONS.info_dict = common.LoadInfoDict(input_zip)
-
-      # If this image was originally labelled with SELinux contexts,
-      # make sure we also apply the labels in our new image. During
-      # building, the "file_contexts" is in the out/ directory tree,
-      # but for repacking from target-files.zip it's in the root
-      # directory of the ramdisk.
-      if "selinux_fc" in OPTIONS.info_dict:
-        OPTIONS.info_dict["selinux_fc"] = os.path.join(
-            OPTIONS.input_tmp, "BOOT", "RAMDISK", "file_contexts")
+      OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.input_tmp)
 
       boot_image = common.GetBootableImage(
           "boot.img", "boot.img", OPTIONS.input_tmp, "BOOT")
diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py
index 0380787..e5e4d74 100755
--- a/tools/releasetools/ota_from_target_files.py
+++ b/tools/releasetools/ota_from_target_files.py
@@ -593,6 +593,8 @@
   if HasVendorPartition(input_zip):
     system_progress -= 0.1
 
+  # Place a copy of file_contexts into the OTA package which will be used by
+  # the recovery program.
   if "selinux_fc" in OPTIONS.info_dict:
     WritePolicyConfig(OPTIONS.info_dict["selinux_fc"], output_zip)
 
@@ -1588,15 +1590,7 @@
   OPTIONS.input_tmp, input_zip = common.UnzipTemp(args[0])
 
   OPTIONS.target_tmp = OPTIONS.input_tmp
-  OPTIONS.info_dict = common.LoadInfoDict(input_zip)
-
-  # If this image was originally labelled with SELinux contexts, make sure we
-  # also apply the labels in our new image. During building, the "file_contexts"
-  # is in the out/ directory tree, but for repacking from target-files.zip it's
-  # in the root directory of the ramdisk.
-  if "selinux_fc" in OPTIONS.info_dict:
-    OPTIONS.info_dict["selinux_fc"] = os.path.join(
-        OPTIONS.input_tmp, "BOOT", "RAMDISK", "file_contexts")
+  OPTIONS.info_dict = common.LoadInfoDict(input_zip, OPTIONS.target_tmp)
 
   if OPTIONS.verbose:
     print "--- target info ---"
@@ -1655,10 +1649,8 @@
       OPTIONS.source_tmp, source_zip = common.UnzipTemp(
           OPTIONS.incremental_source)
       OPTIONS.target_info_dict = OPTIONS.info_dict
-      OPTIONS.source_info_dict = common.LoadInfoDict(source_zip)
-      if "selinux_fc" in OPTIONS.source_info_dict:
-        OPTIONS.source_info_dict["selinux_fc"] = os.path.join(
-            OPTIONS.source_tmp, "BOOT", "RAMDISK", "file_contexts")
+      OPTIONS.source_info_dict = common.LoadInfoDict(source_zip,
+                                                     OPTIONS.source_tmp)
       if OPTIONS.package_key is None:
         OPTIONS.package_key = OPTIONS.source_info_dict.get(
             "default_system_dev_certificate",