Support systems that use a full copy of recovery image.

If BOARD_USES_FULL_RECOVERY_IMAGE == true, a full copy of recovery
image will be carried as /system/etc/recovery.img instead of a patch
at /system/recovery_from_boot.p.

Bug: 22641135
Change-Id: Ie271d3e2d55d7b003f667ac5b44203d69b23c63b
(cherry picked from commit 8beab69bd5d728810aca55536017912e65777bb8)
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index 0d8ca34..5fcdbe4 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -1395,18 +1395,24 @@
   if info_dict is None:
     info_dict = OPTIONS.info_dict
 
-  diff_program = ["imgdiff"]
-  path = os.path.join(input_dir, "SYSTEM", "etc", "recovery-resource.dat")
-  if os.path.exists(path):
-    diff_program.append("-b")
-    diff_program.append(path)
-    bonus_args = "-b /system/etc/recovery-resource.dat"
-  else:
-    bonus_args = ""
+  full_recovery_image = info_dict.get("full_recovery_image", None) == "true"
 
-  d = Difference(recovery_img, boot_img, diff_program=diff_program)
-  _, _, patch = d.ComputePatch()
-  output_sink("recovery-from-boot.p", patch)
+  if full_recovery_image:
+    output_sink("etc/recovery.img", recovery_img.data)
+
+  else:
+    diff_program = ["imgdiff"]
+    path = os.path.join(input_dir, "SYSTEM", "etc", "recovery-resource.dat")
+    if os.path.exists(path):
+      diff_program.append("-b")
+      diff_program.append(path)
+      bonus_args = "-b /system/etc/recovery-resource.dat"
+    else:
+      bonus_args = ""
+
+    d = Difference(recovery_img, boot_img, diff_program=diff_program)
+    _, _, patch = d.ComputePatch()
+    output_sink("recovery-from-boot.p", patch)
 
   try:
     boot_type, boot_device = GetTypeAndDevice("/boot", info_dict)
@@ -1414,7 +1420,19 @@
   except KeyError:
     return
 
-  sh = """#!/system/bin/sh
+  if full_recovery_image:
+    sh = """#!/system/bin/sh
+if ! applypatch -c %(type)s:%(device)s:%(size)d:%(sha1)s; then
+  applypatch /system/etc/recovery.img %(type)s:%(device)s %(sha1)s %(size)d && log -t recovery "Installing new recovery image: succeeded" || log -t recovery "Installing new recovery image: failed"
+else
+  log -t recovery "Recovery image already installed"
+fi
+""" % {'type': recovery_type,
+       'device': recovery_device,
+       'sha1': recovery_img.sha1,
+       'size': recovery_img.size}
+  else:
+    sh = """#!/system/bin/sh
 if ! applypatch -c %(recovery_type)s:%(recovery_device)s:%(recovery_size)d:%(recovery_sha1)s; then
   applypatch %(bonus_args)s %(boot_type)s:%(boot_device)s:%(boot_size)d:%(boot_sha1)s %(recovery_type)s:%(recovery_device)s %(recovery_sha1)s %(recovery_size)d %(boot_sha1)s:/system/recovery-from-boot.p && log -t recovery "Installing new recovery image: succeeded" || log -t recovery "Installing new recovery image: failed"
 else
diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py
index 531b432..f5b912f 100755
--- a/tools/releasetools/ota_from_target_files.py
+++ b/tools/releasetools/ota_from_target_files.py
@@ -218,7 +218,8 @@
         if i.is_dir:
           i.children.sort(key=lambda i: i.name)
 
-    # set metadata for the files generated by this script.
+    # Set metadata for the files generated by this script. For full recovery
+    # image at system/etc/recovery.img, it will be taken care by fs_config.
     i = self.ITEMS.get("system/recovery-from-boot.p", None)
     if i:
       i.uid, i.gid, i.mode, i.selabel, i.capabilities = 0, 0, 0o644, None, None
@@ -422,11 +423,9 @@
 
 
 def HasRecoveryPatch(target_files_zip):
-  try:
-    target_files_zip.getinfo("SYSTEM/recovery-from-boot.p")
-    return True
-  except KeyError:
-    return False
+  namelist = [name for name in target_files_zip.namelist()]
+  return ("SYSTEM/recovery-from-boot.p" in namelist or
+          "SYSTEM/etc/recovery.img" in namelist)
 
 def HasVendorPartition(target_files_zip):
   try:
@@ -1360,6 +1359,7 @@
       common.MakeRecoveryPatch(OPTIONS.target_tmp, output_sink,
                                target_recovery, target_boot)
       script.DeleteFiles(["/system/recovery-from-boot.p",
+                          "/system/etc/recovery.img",
                           "/system/etc/install-recovery.sh"])
     print "recovery image changed; including as patch from boot."
   else:
diff --git a/tools/releasetools/sign_target_files_apks.py b/tools/releasetools/sign_target_files_apks.py
index ec49112..962d3dd 100755
--- a/tools/releasetools/sign_target_files_apks.py
+++ b/tools/releasetools/sign_target_files_apks.py
@@ -175,6 +175,7 @@
     data = input_tf_zip.read(info.filename)
     out_info = copy.copy(info)
 
+    # Replace keys if requested.
     if (info.filename == "META/misc_info.txt" and
         OPTIONS.replace_verity_private_key):
       ReplaceVerityPrivateKey(input_tf_zip, output_tf_zip, misc_info,
@@ -184,12 +185,14 @@
       new_data = ReplaceVerityPublicKey(output_tf_zip,
                                         OPTIONS.replace_verity_public_key[1])
       write_to_temp(info.filename, info.external_attr, new_data)
+    # Copy BOOT/, RECOVERY/, META/, ROOT/ to rebuild recovery patch.
     elif (info.filename.startswith("BOOT/") or
           info.filename.startswith("RECOVERY/") or
           info.filename.startswith("META/") or
           info.filename == "SYSTEM/etc/recovery-resource.dat"):
       write_to_temp(info.filename, info.external_attr, data)
 
+    # Sign APKs.
     if info.filename.endswith(".apk"):
       name = os.path.basename(info.filename)
       key = apk_key_map[name]
@@ -214,6 +217,7 @@
       new_data = ReplaceCerts(data)
       common.ZipWriteStr(output_tf_zip, out_info, new_data)
     elif info.filename in ("SYSTEM/recovery-from-boot.p",
+                           "SYSTEM/etc/recovery.img",
                            "SYSTEM/bin/install-recovery.sh"):
       rebuild_recovery = True
     elif (OPTIONS.replace_ota_keys and